diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionMR.java
index 36d76a99b6ab6f3d8863990a4bbf7d7e9b4c3ebd..47890c2305ed1e5f3afdb0f7fe2fb78f864304d1 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionMR.java
@@ -39,6 +39,7 @@ import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RObject;
+import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.env.frame.RFrameSlot;
 import com.oracle.truffle.r.runtime.interop.Foreign2R;
@@ -71,13 +72,15 @@ public class RFunctionMR {
 
         private static final FrameDescriptor emptyFrameDescriptor = FrameSlotChangeMonitor.initializeFunctionFrameDescriptor("<interop>", new FrameDescriptor("R interop frame"));
         private static final RFrameSlot argsIdentifier = RFrameSlot.createTemp(false);
+        private static final RFrameSlot explicitCallerId = RFrameSlot.createTemp(false);
         private static final FrameSlot slot = FrameSlotChangeMonitor.findOrAddFrameSlot(emptyFrameDescriptor, argsIdentifier, FrameSlotKind.Object);
+        private static final FrameSlot slotCaller = FrameSlotChangeMonitor.findOrAddFrameSlot(emptyFrameDescriptor, explicitCallerId, FrameSlotKind.Object);
 
         static {
             FrameSlotChangeMonitor.initializeFunctionFrameDescriptor("<function>", emptyFrameDescriptor);
         }
 
-        @Child private RCallBaseNode call = RCallNode.createExplicitCall(argsIdentifier);
+        @Child private RCallBaseNode call = RCallNode.createExplicitCall(argsIdentifier, explicitCallerId);
 
         protected Object access(RFunction receiver, Object[] arguments) {
             Object[] dummyFrameArgs = RArguments.createUnitialized();
@@ -90,6 +93,7 @@ public class RFunctionMR {
             RArgsValuesAndNames actualArgs = new RArgsValuesAndNames(convertedArguments, ArgumentsSignature.empty(arguments.length));
             try {
                 FrameSlotChangeMonitor.setObject(dummyFrame, slot, actualArgs);
+                FrameSlotChangeMonitor.setObject(dummyFrame, slotCaller, RNull.instance);
                 Object value = call.execute(dummyFrame, receiver);
                 return r2Foreign.execute(value);
             } finally {
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort.java
index b1c90a3a02bf7da523010ff7e3deab63d0d31557..6d1772a79b3df52de9a18cc420750c026d7bbe16 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort.java
@@ -151,7 +151,7 @@ public final class ViewPort {
 
         public RList execute(VirtualFrame frame) {
             RFunction gridTopLevel = (RFunction) readGridTopLevel.execute(frame);
-            RList topVP = (RList) callNode.execute(frame, gridTopLevel, RArgsValuesAndNames.EMPTY);
+            RList topVP = (RList) callNode.call(frame, gridTopLevel, RArgsValuesAndNames.EMPTY);
             topVP.makeSharedPermanent();
 
             GridDevice device = GridContext.getContext().getCurrentDevice();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java
index 165ad928eaa36e1efebf1232f3bd27e18f9670b2..00894b2b3b2a29c27e2283783b9344330ff407c8 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java
@@ -585,7 +585,7 @@ public abstract class Bind extends RBaseNode {
                     CompilerDirectives.transferToInterpreterAndInvalidate();
                     dispatchCallNode = insert(RExplicitCallNode.create());
                 }
-                return dispatchCallNode.execute(frame, dispatchFunction, (RArgsValuesAndNames) RArguments.getArgument(frame, 0));
+                return dispatchCallNode.call(frame, dispatchFunction, (RArgsValuesAndNames) RArguments.getArgument(frame, 0));
             } else {
                 if (bind == null) {
                     CompilerDirectives.transferToInterpreterAndInvalidate();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java
index fed4396ff0c3aa815406b06cde8861c76429cd50..626d44088d275eb332c74c7329d32e68948a630a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,14 +23,19 @@
 package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.returnIf;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
 import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
 
+import java.util.function.Supplier;
+
+import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.FrameDescriptor;
 import com.oracle.truffle.api.frame.FrameSlot;
 import com.oracle.truffle.api.frame.FrameSlotKind;
 import com.oracle.truffle.api.frame.FrameSlotTypeException;
@@ -40,30 +45,32 @@ import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.ValueProfile;
-import com.oracle.truffle.r.nodes.RASTUtils;
-import com.oracle.truffle.r.nodes.access.ConstantNode;
 import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.DoCallNodeGen.DoCallInternalNodeGen;
 import com.oracle.truffle.r.nodes.builtin.base.GetFunctions.Get;
 import com.oracle.truffle.r.nodes.builtin.base.GetFunctionsFactory.GetNodeGen;
 import com.oracle.truffle.r.nodes.function.RCallerHelper;
-import com.oracle.truffle.r.nodes.function.opt.ShareObjectNode;
+import com.oracle.truffle.r.nodes.function.call.RExplicitCallNode;
+import com.oracle.truffle.r.nodes.function.visibility.GetVisibilityNode;
 import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
+import com.oracle.truffle.r.nodes.profile.TruffleBoundaryNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RCaller;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.VirtualEvalFrame;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.Closure;
+import com.oracle.truffle.r.runtime.data.ClosureCache;
+import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache;
+import com.oracle.truffle.r.runtime.data.ClosureCache.SymbolClosureCache;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.REmpty;
@@ -79,9 +86,10 @@ import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.env.frame.RFrameSlot;
 import com.oracle.truffle.r.runtime.nodes.InternalRSyntaxNodeChildren;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
-@RBuiltin(name = ".fastr.do.call", visibility = CUSTOM, kind = RBuiltinKind.INTERNAL, parameterNames = {"what", "args", "quote", "envir"}, behavior = COMPLEX)
+@RBuiltin(name = ".fastr.do.call", visibility = CUSTOM, kind = RBuiltinKind.INTERNAL, parameterNames = {"what", "args", "quote", "envir"}, behavior = COMPLEX, splitCaller = true)
 public abstract class DoCall extends RBuiltinNode.Arg4 implements InternalRSyntaxNodeChildren {
 
     static {
@@ -119,8 +127,11 @@ public abstract class DoCall extends RBuiltinNode.Arg4 implements InternalRSynta
     }
 
     protected abstract static class DoCallInternal extends Node {
-        @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
-        @Child private SetVisibilityNode setVisibilityNode = SetVisibilityNode.create();
+        @Child private GetNamesAttributeNode getNamesNode;
+        @Child private SetVisibilityNode setVisibilityNode;
+        private final ValueProfile frameAccessProfile = ValueProfile.createClassProfile();
+        private final RNodeClosureCache languagesClosureCache = new RNodeClosureCache();
+        private final SymbolClosureCache symbolsClosureCache = new SymbolClosureCache();
 
         public static DoCallInternal create() {
             return DoCallInternalNodeGen.create();
@@ -128,23 +139,80 @@ public abstract class DoCall extends RBuiltinNode.Arg4 implements InternalRSynta
 
         public abstract Object execute(VirtualFrame virtualFrame, RFunction func, RList argsAsList, boolean quote, REnvironment env);
 
-        protected final ArgumentsSignature getArgsNames(RList argsAsList) {
-            ArgumentsSignature signature = ArgumentsSignature.fromNamesAttribute(getNamesNode.getNames(argsAsList));
-            return signature == null ? ArgumentsSignature.empty(argsAsList.getLength()) : signature;
+        protected FrameDescriptor getFrameDescriptor(REnvironment env) {
+            return env.getFrame(frameAccessProfile).getFrameDescriptor();
         }
 
         /**
-         * Fast version that works only for simple cases. It does not explicitly create the AST and
-         * evaluate it, but instead it directly implements what the execution of such AST would do.
+         * Because the underlying AST in {@link RExplicitCallNode} may cache frame slots, i.e.
+         * expect the {@link FrameDescriptor} to never change, we're caching this AST and also
+         * {@link GetVisibilityNode} for each {@link FrameDescriptor} we encounter.
          */
-        @Specialization(guards = "isSimple(func, argsAsList)")
-        public Object doSimple(VirtualFrame virtualFrame, RFunction func, RList argsAsList, boolean quote, REnvironment env,
-                        @Cached("create()") ShareObjectNode shareObjectNode,
+        @Specialization(guards = {"getFrameDescriptor(env) == fd"}, limit = "20")
+        public Object doFastPath(VirtualFrame virtualFrame, RFunction func, RList argsAsList, boolean quote, REnvironment env,
+                        @Cached("getFrameDescriptor(env)") @SuppressWarnings("unused") FrameDescriptor fd,
+                        @Cached("create()") RExplicitCallNode explicitCallNode,
+                        @Cached("create()") GetVisibilityNode getVisibilityNode,
                         @Cached("createBinaryProfile()") ConditionProfile quoteProfile,
-                        @Cached("create()") BranchProfile containsRSymbolProfile,
-                        @Cached("createClassProfile()") ValueProfile frameAccessProfile) {
+                        @Cached("create()") BranchProfile containsRSymbolProfile) {
+            MaterializedFrame promiseFrame = env.getFrame(frameAccessProfile).materialize();
+            RArgsValuesAndNames args = getArguments(promiseFrame, quote, quoteProfile, containsRSymbolProfile, argsAsList);
+            RCaller caller = getExplicitCaller(virtualFrame, promiseFrame, func, args);
+            MaterializedFrame evalFrame = getEvalFrame(virtualFrame, promiseFrame);
+
+            Object resultValue = explicitCallNode.execute(evalFrame, func, args, caller);
+            setVisibility(virtualFrame, getVisibilityNode.execute(evalFrame));
+            return resultValue;
+        }
+
+        /**
+         * Slow-path version avoids the problem by creating {@link RExplicitCallNode} for every call
+         * again and again and putting it behind truffle boundary to avoid deoptimization.
+         */
+        @Specialization(replaces = "doFastPath")
+        public Object doSlowPath(VirtualFrame virtualFrame, RFunction func, RList argsAsList, boolean quote, REnvironment env,
+                        @Cached("create()") SlowPathExplicitCall slowPathExplicitCall,
+                        @Cached("createBinaryProfile()") ConditionProfile quoteProfile,
+                        @Cached("create()") BranchProfile containsRSymbolProfile) {
+            MaterializedFrame promiseFrame = env.getFrame(frameAccessProfile).materialize();
+            RArgsValuesAndNames args = getArguments(promiseFrame, quote, quoteProfile, containsRSymbolProfile, argsAsList);
+            RCaller caller = getExplicitCaller(virtualFrame, promiseFrame, func, args);
+            MaterializedFrame evalFrame = getEvalFrame(virtualFrame, promiseFrame);
+
+            Object resultValue = slowPathExplicitCall.execute(evalFrame, caller, func, args);
+            setVisibility(virtualFrame, getVisibilitySlowPath(evalFrame));
+            return resultValue;
+        }
+
+        /**
+         * The contract is that the function call will be evaluated in the given environment, but at
+         * the same time some primitives expect to see {@code do.call(foo, ...)} as the caller, so
+         * we create a frame the fakes caller, but otherwise delegates to the frame backing the
+         * explicitly given environment.
+         */
+        private MaterializedFrame getEvalFrame(VirtualFrame virtualFrame, MaterializedFrame envFrame) {
+            return VirtualEvalFrame.create(envFrame, RArguments.getFunction(virtualFrame), RArguments.getCall(virtualFrame));
+        }
+
+        /**
+         * If the call leads to actual call via
+         * {@link com.oracle.truffle.r.nodes.function.call.CallRFunctionNode}, which creates new
+         * frame and new set of arguments for it, then for this new arguments we explicitly provide
+         * a caller that looks like the function was called from the explicitly given environment
+         * (it will be its parent call), but at the same time its depth is one above the do.call
+         * function that actually invoked it.
+         *
+         * @see RCaller
+         * @see RArguments
+         */
+        private RCaller getExplicitCaller(VirtualFrame virtualFrame, MaterializedFrame envFrame, RFunction func, RArgsValuesAndNames args) {
+            Supplier<RSyntaxElement> callerSyntax = RCallerHelper.createFromArguments(func, args);
+            return RCaller.create(RArguments.getDepth(virtualFrame) + 1, RArguments.getCall(envFrame), callerSyntax);
+        }
+
+        private RArgsValuesAndNames getArguments(MaterializedFrame promiseFrame, boolean quote, ConditionProfile quoteProfile,
+                        BranchProfile containsRSymbolProfile, RList argsAsList) {
             Object[] argValues = argsAsList.getDataCopy();
-            MaterializedFrame envFrame = env.getFrame(frameAccessProfile).materialize();
             if (quoteProfile.profile(!quote)) {
                 for (int i = 0; i < argValues.length; i++) {
                     Object arg = argValues[i];
@@ -154,53 +222,48 @@ public abstract class DoCall extends RBuiltinNode.Arg4 implements InternalRSynta
                         if (symbol.getName().isEmpty()) {
                             argValues[i] = REmpty.instance;
                         } else {
-                            argValues[i] = createLookupPromise(envFrame, symbol);
+                            argValues[i] = createLookupPromise(promiseFrame, symbol.getName());
                         }
+                    } else if (arg instanceof RLanguage) {
+                        argValues[i] = createLanguagePromise(promiseFrame, (RLanguage) arg);
                     }
                 }
             }
-            for (int i = 0; i < argValues.length; i++) {
-                shareObjectNode.execute(argValues[i]);
-            }
             ArgumentsSignature signature = getArgsNames(argsAsList);
-            RCaller caller = RCaller.createWithInternalParent(virtualFrame, RCallerHelper.createFromArguments(func, new RArgsValuesAndNames(argValues, signature)));
-            try {
-                Object resultValue = RContext.getEngine().evalFunction(func, envFrame, caller, false, signature, argValues);
-                setVisibilityNode.execute(virtualFrame, getVisibility(envFrame));
-                return resultValue;
-            } finally {
-                for (int i = 0; i < argValues.length; i++) {
-                    ShareObjectNode.unshare(argValues[i]);
-                }
-            }
+            return new RArgsValuesAndNames(argValues, signature);
         }
 
-        protected static boolean isSimple(RFunction function, RList args) {
-            RBuiltinDescriptor builtin = function.getRBuiltin();
-            if (builtin != null && builtin.getDispatch() != RDispatch.DEFAULT) {
-                return false;
-            }
-            for (int i = 0; i < args.getLength(); i++) {
-                if (args.getDataAt(i) instanceof RLanguage) {
-                    // Note: language is tricky because of formulae, which are language that is
-                    // really not meant to be evaluated again in a different frame than the one were
-                    // the were evaluated for the first time. The solution should be to clone the
-                    // language's rep and get its nodes in uninitilized state, but that does not
-                    // work for some reason.
-                    return false;
-                }
-            }
-            return true;
+        @TruffleBoundary
+        private RPromise createLanguagePromise(MaterializedFrame promiseFrame, RLanguage arg) {
+            Closure closure = languagesClosureCache.getOrCreatePromiseClosure(arg.getRep());
+            return RDataFactory.createPromise(PromiseState.Supplied, closure, promiseFrame);
         }
 
         @TruffleBoundary
-        private static RPromise createLookupPromise(MaterializedFrame callerFrame, RSymbol symbol) {
-            Closure closure = Closure.createPromiseClosure(RContext.getASTBuilder().lookup(RSyntaxNode.SOURCE_UNAVAILABLE, symbol.getName(), false).asRNode());
+        private RPromise createLookupPromise(MaterializedFrame callerFrame, String name) {
+            Closure closure = symbolsClosureCache.getOrCreatePromiseClosure(name);
             return RDataFactory.createPromise(PromiseState.Supplied, closure, callerFrame);
         }
 
+        private void setVisibility(VirtualFrame frame, boolean value) {
+            if (setVisibilityNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setVisibilityNode = insert(SetVisibilityNode.create());
+            }
+            setVisibilityNode.execute(frame, value);
+        }
+
+        private ArgumentsSignature getArgsNames(RList argsAsList) {
+            if (getNamesNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                getNamesNode = insert(GetNamesAttributeNode.create());
+            }
+            ArgumentsSignature signature = ArgumentsSignature.fromNamesAttribute(getNamesNode.getNames(argsAsList));
+            return signature == null ? ArgumentsSignature.empty(argsAsList.getLength()) : signature;
+        }
+
         @TruffleBoundary
-        private static boolean getVisibility(MaterializedFrame envFrame) {
+        private static boolean getVisibilitySlowPath(MaterializedFrame envFrame) {
             FrameSlot envVisibilitySlot = FrameSlotChangeMonitor.findOrAddFrameSlot(envFrame.getFrameDescriptor(), RFrameSlot.Visibility, FrameSlotKind.Boolean);
             if (envVisibilitySlot != null) {
                 try {
@@ -211,62 +274,19 @@ public abstract class DoCall extends RBuiltinNode.Arg4 implements InternalRSynta
             }
             return false;
         }
+    }
 
-        @Specialization(guards = "!isSimple(func, argsAsList)")
-        public Object doGeneric(VirtualFrame virtualFrame, RFunction func, RList argsAsList, boolean quote, REnvironment env) {
-            CallResult result = doCallGeneric(func, argsAsList.getDataWithoutCopying(), getArgsNames(argsAsList), quote, RArguments.getCall(virtualFrame), env);
-            setVisibilityNode.execute(virtualFrame, result.visibility);
-            return result.value;
-        }
+    static class SlowPathExplicitCall extends TruffleBoundaryNode {
+        @Child private RExplicitCallNode slowPathCallNode;
 
-        @TruffleBoundary
-        private static CallResult doCallGeneric(RFunction function, Object[] argValues, ArgumentsSignature argsSignature, boolean quote, RCaller call, REnvironment env) {
-            RSyntaxNode[] argsConstants = new RSyntaxNode[argValues.length];
-            for (int i = 0; i < argValues.length; i++) {
-                if (!quote && argValues[i] instanceof RLanguage) {
-                    argsConstants[i] = ((RLanguage) argValues[i]).getRep().asRSyntaxNode();
-                } else if (!quote && argValues[i] instanceof RSymbol) {
-                    RSymbol symbol = (RSymbol) argValues[i];
-                    if (symbol.isMissing()) {
-                        argsConstants[i] = ConstantNode.create(REmpty.instance);
-                    } else {
-                        argsConstants[i] = RContext.getASTBuilder().lookup(RSyntaxNode.LAZY_DEPARSE, ((RSymbol) argValues[i]).getName(), false);
-                    }
-                } else {
-                    argsConstants[i] = ConstantNode.create(argValues[i]);
-                }
-            }
-            for (int i = 0; i < argValues.length; i++) {
-                ShareObjectNode.share(argValues[i]);
-            }
-            Closure closure = Closure.createLanguageClosure(RASTUtils.createCall(ConstantNode.create(function), true, argsSignature, argsConstants).asRNode());
-            RLanguage lang = RDataFactory.createLanguage(closure);
-            try {
-                Object resultValue = RContext.getEngine().eval(lang, env, call.withInternalParent());
-                MaterializedFrame envFrame = env.getFrame();
-                FrameSlot envVisibilitySlot = FrameSlotChangeMonitor.findOrAddFrameSlot(envFrame.getFrameDescriptor(), RFrameSlot.Visibility, FrameSlotKind.Boolean);
-                boolean resultVisibility = false;
-                if (envVisibilitySlot != null) {
-                    resultVisibility = envFrame.getBoolean(envVisibilitySlot);
-                }
-                return new CallResult(resultValue, resultVisibility);
-            } catch (FrameSlotTypeException e) {
-                throw RInternalError.shouldNotReachHere();
-            } finally {
-                for (int i = 0; i < argValues.length; i++) {
-                    ShareObjectNode.unshare(argValues[i]);
-                }
-            }
+        public static SlowPathExplicitCall create() {
+            return new SlowPathExplicitCall();
         }
 
-        private static final class CallResult {
-            public final Object value;
-            public final boolean visibility;
-
-            CallResult(Object value, boolean visibility) {
-                this.value = value;
-                this.visibility = visibility;
-            }
+        @TruffleBoundary
+        public Object execute(MaterializedFrame evalFrame, RCaller caller, RFunction func, RArgsValuesAndNames args) {
+            slowPathCallNode = insert(RExplicitCallNode.create());
+            return slowPathCallNode.execute(evalFrame, func, args, caller);
         }
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForceAndCall.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForceAndCall.java
index 3cef036d53cbbb707ffd9819a0bcb2f02abd496c..0a315155bb6f03b191bd8fc9760717c72544b954 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForceAndCall.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForceAndCall.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -60,7 +60,7 @@ public abstract class ForceAndCall extends RBuiltinNode.Arg3 {
         if (!fun.isBuiltin()) {
             flattenFirstArgs(frame, cachedN, args);
         }
-        return call.execute(frame, fun, args);
+        return call.call(frame, fun, args);
     }
 
     @ExplodeLoop
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java
index e15ff3334c5df7e2013c38214709e9a084e83b12..6e2f7dda034d4056c45c345461a5f00d5ab29686 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -89,6 +89,16 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
  * The frame for the sys functions themselves is not counted in the R spec. Frames are numbered 0,
  * 1, .. starting from .GlobalEnv. Non-negative arguments are frame numbers, negative arguments are
  * relative to the current frame.
+ *
+ * The main thing to note is distinction between {@code sys.parent} and {@code sys.frame}.
+ * {@code sys.parent} gives you "logical" parent, but not necessarily the frame preceding the
+ * current frame on the call stack. In FastR this is captured by {@link RCaller#getParent()}.
+ * {@code sys.frame(n)} gives you frame with number {@code n} and traverses the stack without taking
+ * the parent relation into account. In FastR the frame number is captured in
+ * {@link RCaller#getDepth()}. See also builtins in {@code FrameFunctions} for more details.
+ *
+ * @see RArguments
+ * @see RCaller
  */
 public class FrameFunctions {
 
@@ -112,19 +122,11 @@ public class FrameFunctions {
             return RInternalError.guaranteeNonNull(getNumberedFrame(frame, actualFrame));
         }
 
-        protected RCaller getCall(RCaller currentCall, int n) {
+        protected RCaller getCall(VirtualFrame frame, int n) {
+            RCaller currentCall = RArguments.getCall(frame);
             int actualFrame = decodeFrameNumber(currentCall, n);
-            RCaller call = currentCall;
-            while (call != null) {
-                while (call.isPromise()) {
-                    call = call.getPromiseOriginalCall();
-                }
-                if (call.getDepth() == actualFrame) {
-                    return call;
-                }
-                call = call.getParent();
-            }
-            throw RInternalError.shouldNotReachHere();
+            Frame targetFrame = getNumberedFrame(frame, actualFrame);
+            return RArguments.getCall(targetFrame);
         }
 
         /**
@@ -197,13 +199,12 @@ public class FrameFunctions {
             /*
              * sys.call preserves provided names but does not create them, unlike match.call.
              */
-            return createCall(RArguments.getCall(frame), which);
+            return createCall(helper.getCall(frame, which));
         }
 
         @TruffleBoundary
-        private Object createCall(RCaller currentCall, int which) {
-            RCaller call = helper.getCall(currentCall, which);
-            assert !call.isPromise();
+        private Object createCall(RCaller call) {
+            assert call == null || !call.isPromise();
             if (call == null || !call.isValidCaller()) {
                 return RNull.instance;
             }
@@ -543,9 +544,6 @@ public class FrameFunctions {
             RCaller call = RArguments.getCall(frame);
             int i = 0;
             while (i < n + 1) {
-                if (call.hasInternalParent()) {
-                    i--;    // in this loop iteration, we deal with the parent, but do not count it
-                }
                 call = call.getParent();
                 if (call == null) {
                     nullCallerProfile.enter();
@@ -615,7 +613,7 @@ public class FrameFunctions {
                         RCaller currentCall = RArguments.getCall(f);
                         if (!currentCall.isPromise() && currentCall.getDepth() <= depth) {
                             int currentCallIdx = currentCall.getDepth() - 1;
-                            while (currentCall != null && (currentCall.hasInternalParent() || currentCall.isPromise())) {
+                            while (currentCall != null && (currentCall.isPromise())) {
                                 currentCall = currentCall.getParent();
                             }
                             result[currentCallIdx] = currentCall == null ? 0 : currentCall.getParent().getDepth();
@@ -672,9 +670,6 @@ public class FrameFunctions {
             }
             int i = 0;
             while (i < n) {
-                if (call.hasInternalParent()) {
-                    i--;    // in this loop iteration, we deal with the parent, but do not count it
-                }
                 call = call.getParent();
                 if (call == null) {
                     nullCallerProfile.enter();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mapply.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mapply.java
index dc3a03b8f5389ea8e3ec3e811839654de10582ae..50a3c846eb828d74c4fbffed87e08e2407af468d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mapply.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mapply.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -227,7 +227,7 @@ public abstract class Mapply extends RBuiltinNode.Arg3 {
                     values[listIndex] = vecElement;
                 }
                 /* Now call the function */
-                result[i] = callNode.execute(frame, function, new RArgsValuesAndNames(values, signature));
+                result[i] = callNode.call(frame, function, new RArgsValuesAndNames(values, signature));
             }
             return result;
         }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Recall.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Recall.java
index 5d82e00576a4c32e52906c736c729abb2a467f56..4b9b9eaf9cd30d10d55ce0790014608cdce99e27 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Recall.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Recall.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -68,6 +68,6 @@ public abstract class Recall extends RBuiltinNode.Arg1 {
          * builtin looks at the arguments passed to the surrounding function.
          */
         RArgsValuesAndNames actualArgs = (RArgsValuesAndNames) readArgs.execute(frame);
-        return call.execute(frame, function, actualArgs);
+        return call.call(frame, function, actualArgs);
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java
index 300ec8e7def72083fc8cb0987d789d0e4fde64f4..8da59509fff8f7523de4d3ad3a3ff33a0de82738 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java
@@ -973,7 +973,7 @@ public class FastRInterop {
             boolean isCheck = RRuntime.fromLogical(check);
             getInteropTryState().stepIn();
             try {
-                return call.execute(frame, function, RArgsValuesAndNames.EMPTY);
+                return call.call(frame, function, RArgsValuesAndNames.EMPTY);
             } catch (FastRInteropTryException e) {
                 Throwable cause = e.getCause();
                 CompilerDirectives.transferToInterpreter();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTestsTry.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTestsTry.java
index 7b7a63ecee5326d0496f6f62a05963efa935be69..98c3abe8dcefc817c91057623908ec12caf7aa8b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTestsTry.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTestsTry.java
@@ -55,7 +55,7 @@ public abstract class FastRTestsTry extends RBuiltinNode.Arg1 {
     @Specialization
     public Object tryFunc(VirtualFrame frame, RFunction func) {
         try {
-            call.execute(frame, func, RArgsValuesAndNames.EMPTY);
+            call.call(frame, func, RArgsValuesAndNames.EMPTY);
         } catch (Throwable ex) {
             // try to recover from a possibly incosistent state when running tests:
             // some handlers might still be lying around and interfere with subsequent calls
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java
index 990cfce958303755520c57f071ad0a844f7b1ec2..edba93cf227be044180e76fbb148bbc26b1ff9d8 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -142,7 +142,7 @@ public final class AccessArgumentNode extends RNode {
     private void checkPromiseFactory() {
         if (factory == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            Closure defaultClosure = formals.getOrCreatePromiseClosure(formals.getDefaultArgument(index));
+            Closure defaultClosure = formals.getClosureCache().getOrCreatePromiseClosure(formals.getDefaultArgument(index));
             factory = RPromiseFactory.create(PromiseState.Default, defaultClosure);
         }
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java
index 6db88a49aa04b81d8f6feadfa53be6346c4dfe2e..53db2b3f5bc23ee9936ee96a74bb4c8f87e8ac40 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -190,7 +190,7 @@ abstract class BaseWriteVariableNode extends WriteVariableNode {
             }
             ActiveBinding binding = (ActiveBinding) object;
             try {
-                return writeActiveBinding.execute(execFrame, binding.getFunction(), new RArgsValuesAndNames(new Object[]{value}, ArgumentsSignature.empty(1)));
+                return writeActiveBinding.call(execFrame, binding.getFunction(), new RArgsValuesAndNames(new Object[]{value}, ArgumentsSignature.empty(1)));
             } finally {
                 binding.setInitialized(true);
             }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LocalReadVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LocalReadVariableNode.java
index f3e42feefcbbb14c573b303dd54de1c038a5905e..f67c254ce6760e5525ff1ef194c5455be9f1c515 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LocalReadVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LocalReadVariableNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -131,7 +131,7 @@ public final class LocalReadVariableNode extends Node {
             if (binding.isHidden() && !binding.isInitialized()) {
                 return null;
             }
-            Object readValue = readActiveBinding.execute(frame, binding.getFunction(), RArgsValuesAndNames.EMPTY);
+            Object readValue = readActiveBinding.call(frame, binding.getFunction(), RArgsValuesAndNames.EMPTY);
             if (readValue == RMissing.instance) {
                 return null;
             }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
index 74e6cfc821ebe3c9daa8dfdd52cc6a52651d2bba..7104433d3e0e28128ec9a299b078f73820d058ae 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -275,7 +275,7 @@ public final class ReadVariableNode extends RBaseNode {
             if (binding.isHidden() && !binding.isInitialized()) {
                 throw error(mode == RType.Function ? RError.Message.UNKNOWN_FUNCTION : RError.Message.UNKNOWN_OBJECT, identifier);
             }
-            Object readValue = readActiveBinding.execute(frame, binding.getFunction(), RArgsValuesAndNames.EMPTY);
+            Object readValue = readActiveBinding.call(frame, binding.getFunction(), RArgsValuesAndNames.EMPTY);
             if (readValue == RMissing.instance) {
                 throw error(mode == RType.Function ? RError.Message.UNKNOWN_FUNCTION : RError.Message.UNKNOWN_OBJECT, identifier);
             }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java
index c9baefdb00e4c3eaa15eb028458d5ee2468b3bd7..b4093331fb475e1187560f1609c512fa197ced46 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -55,6 +55,7 @@ import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
 import com.oracle.truffle.r.runtime.data.Closure;
 import com.oracle.truffle.r.runtime.data.ClosureCache;
+import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.REmpty;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -163,7 +164,7 @@ public class ArgumentMatcher {
             argNodes = suppliedArgs.getArguments();
             signature = suppliedArgs.getSignature();
         }
-        return ArgumentMatcher.matchNodes(target, argNodes, signature, s3DefaultArguments, callingNode, arguments, noOpt);
+        return ArgumentMatcher.matchNodes(target, argNodes, signature, s3DefaultArguments, callingNode, arguments.getClosureCache(), noOpt);
     }
 
     public static MatchPermutation matchArguments(ArgumentsSignature supplied, ArgumentsSignature formal, RBaseNode callingNode, RBuiltinDescriptor builtin) {
@@ -327,7 +328,7 @@ public class ArgumentMatcher {
      *         accordingly.
      */
     private static Arguments<RNode> matchNodes(RRootNode target, RNode[] suppliedArgs, ArgumentsSignature suppliedSignature, S3DefaultArguments s3DefaultArguments, RBaseNode callingNode,
-                    ClosureCache closureCache, boolean noOpt) {
+                    RNodeClosureCache closureCache, boolean noOpt) {
         CompilerAsserts.neverPartOfCompilation();
         assert suppliedArgs.length == suppliedSignature.getLength();
 
@@ -494,13 +495,13 @@ public class ArgumentMatcher {
              * InfixEmulationFunctions.tilde).
              */
             RNode defaultArg = formals.getDefaultArgument(formalIndex);
-            Closure defaultClosure = formals.getOrCreatePromiseClosure(defaultArg);
+            Closure defaultClosure = formals.getClosureCache().getOrCreatePromiseClosure(defaultArg);
             return PromiseNode.create(RPromiseFactory.create(PromiseState.Default, defaultClosure), noOpt, false);
         }
         return ConstantNode.create(formals.getInternalDefaultArgumentAt(formalIndex));
     }
 
-    private static RNode wrapMatched(FormalArguments formals, RBuiltinDescriptor builtin, ClosureCache closureCache, RNode suppliedArg, int formalIndex, boolean noOpt, FastPathFactory fastPath) {
+    private static RNode wrapMatched(FormalArguments formals, RBuiltinDescriptor builtin, RNodeClosureCache closureCache, RNode suppliedArg, int formalIndex, boolean noOpt, FastPathFactory fastPath) {
         // Create promise, unless it's the empty value
         if (suppliedArg instanceof ConstantNode) {
             ConstantNode a = (ConstantNode) suppliedArg;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java
index f5f5cf0093a4ce1a615879d794cbe79ecb83dad9..aa7f399b5cd68256b509ad420d882144e43b01d9 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,7 +23,6 @@
 package com.oracle.truffle.r.nodes.function;
 
 import java.util.Arrays;
-import java.util.IdentityHashMap;
 
 import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives;
@@ -34,15 +33,14 @@ import com.oracle.truffle.api.frame.Frame;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.ExplodeLoop;
 import com.oracle.truffle.api.profiles.BranchProfile;
-import com.oracle.truffle.r.nodes.access.FrameSlotNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseCheckHelperNode;
 import com.oracle.truffle.r.runtime.Arguments;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.data.Closure;
 import com.oracle.truffle.r.runtime.data.ClosureCache;
+import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
@@ -57,7 +55,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
  * {@link RootCallTarget} for every argument.
  * </p>
  */
-public final class CallArgumentsNode extends RBaseNode implements UnmatchedArguments {
+public final class CallArgumentsNode extends RBaseNode {
     /**
      * A list of arguments. Single arguments may be <code>null</code>; semantics have to be
      * specified by implementing classes
@@ -66,24 +64,22 @@ public final class CallArgumentsNode extends RBaseNode implements UnmatchedArgum
 
     protected final ArgumentsSignature signature;
 
-    @Child private FrameSlotNode varArgsSlotNode;
     @Child private PromiseCheckHelperNode promiseHelper;
 
+    private final RNodeClosureCache closureCache = new RNodeClosureCache();
+
     /**
      * If a supplied argument is a {@link ReadVariableNode} whose name is "...", this field contains
      * the index of the name. Otherwise it is an empty list.
      */
     @CompilationFinal(dimensions = 1) private final int[] varArgsSymbolIndices;
 
-    private final IdentityHashMap<RNode, Closure> closureCache = new IdentityHashMap<>();
-
     private CallArgumentsNode(RNode[] arguments, ArgumentsSignature signature, int[] varArgsSymbolIndices) {
         assert signature != null && signature.getLength() == arguments.length : Arrays.toString(arguments) + " " + signature;
         this.arguments = arguments;
         this.signature = signature;
         assert signature != null;
         this.varArgsSymbolIndices = varArgsSymbolIndices;
-        this.varArgsSlotNode = !containsVarArgsSymbol() ? null : FrameSlotNode.create(ArgumentsSignature.VARARG_NAME);
     }
 
     /**
@@ -126,6 +122,10 @@ public final class CallArgumentsNode extends RBaseNode implements UnmatchedArgum
         return varArgs;
     }
 
+    public RNodeClosureCache getClosureCache() {
+        return closureCache;
+    }
+
     /**
      * This methods unrolls all "..." in the argument list. The result varies if the number of
      * arguments in the varargs or their names change.
@@ -262,25 +262,14 @@ public final class CallArgumentsNode extends RBaseNode implements UnmatchedArgum
         return varArgsSymbolIndices.length > 0;
     }
 
-    public int[] getVarArgsSymbolIndices() {
-        return varArgsSymbolIndices;
-    }
-
-    @Override
-    public IdentityHashMap<RNode, Closure> getContent() {
-        return closureCache;
-    }
-
     /**
      * @return The {@link RNode}s of the arguments given to a function call, in the same order. A
      *         single argument being <code>null</code> means 'argument not provided'.
      */
-    @Override
     public RNode[] getArguments() {
         return arguments;
     }
 
-    @Override
     public ArgumentsSignature getSignature() {
         return signature;
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FormalArguments.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FormalArguments.java
index 6e6db26cb0fccd899fb10c9f7fd4c50d8b6b4f7e..bd6d0baf292d733a7ecd2c8e449d959acfa2d817 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FormalArguments.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FormalArguments.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,7 +23,6 @@
 package com.oracle.truffle.r.nodes.function;
 
 import java.util.Arrays;
-import java.util.IdentityHashMap;
 
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.RootCallTarget;
@@ -31,8 +30,8 @@ import com.oracle.truffle.r.nodes.access.ConstantNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.Arguments;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.data.Closure;
 import com.oracle.truffle.r.runtime.data.ClosureCache;
+import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -50,12 +49,10 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
  * ever one {@link RootCallTarget} for every default argument.
  * </p>
  */
-public final class FormalArguments extends Arguments<RNode> implements ClosureCache {
+public final class FormalArguments extends Arguments<RNode> {
 
     public static final FormalArguments NO_ARGS = new FormalArguments(new RNode[0], new Object[0], ArgumentsSignature.empty(0));
 
-    private final IdentityHashMap<RNode, Closure> closureCache = new IdentityHashMap<>();
-
     /**
      * These argument constants define what will be passed along in case there is no supplied
      * argument for the given argument slot. In the case of normal functions (as opposed to
@@ -64,6 +61,8 @@ public final class FormalArguments extends Arguments<RNode> implements ClosureCa
      */
     @CompilationFinal(dimensions = 1) private final Object[] internalDefaultArguments;
 
+    private final RNodeClosureCache closureCache = new RNodeClosureCache();
+
     private FormalArguments(RNode[] defaultArguments, Object[] internalDefaultArguments, ArgumentsSignature signature) {
         super(defaultArguments, signature);
         this.internalDefaultArguments = internalDefaultArguments;
@@ -117,8 +116,7 @@ public final class FormalArguments extends Arguments<RNode> implements ClosureCa
                         value == RArgsValuesAndNames.EMPTY;
     }
 
-    @Override
-    public IdentityHashMap<RNode, Closure> getContent() {
+    public RNodeClosureCache getClosureCache() {
         return closureCache;
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
index 8001fc01002329f63db99348295bd5e6c34fd54c..e0dbba8ff097a1738b959f3d45146a6656cc1aee 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
@@ -72,6 +72,7 @@ import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.context.TruffleRLanguage;
 import com.oracle.truffle.r.runtime.data.Closure;
 import com.oracle.truffle.r.runtime.data.ClosureCache;
+import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPairList;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
@@ -87,7 +88,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxVisitor;
 
-public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNode, RSyntaxFunction, ClosureCache {
+public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNode, RSyntaxFunction {
 
     private final FormalArguments formalArguments;
 
@@ -108,6 +109,8 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
     private SourceSection sourceSectionR;
     private final SourceSection[] argSourceSections;
 
+    private final RNodeClosureCache closureCache = new RNodeClosureCache();
+
     @Child private RNode saveArguments;
     @Child private FrameSlotNode onExitSlot;
     @Child private InlineCacheNode onExitExpressionCache;
@@ -380,7 +383,7 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
                                     CompilerDirectives.transferToInterpreter();
                                     RInternalError.shouldNotReachHere("unexpected type for on.exit entry: " + expr.car());
                                 }
-                                onExitExpressionCache.execute(frame, getOrCreateLanguageClosure((RNode) expr.car()));
+                                onExitExpressionCache.execute(frame, closureCache.getOrCreateLanguageClosure((RNode) expr.car()));
                             }
                         }
                     } catch (ReturnException ex) {
@@ -565,13 +568,4 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
         }
         return handlerStackSlot;
     }
-
-    @Override
-    public IdentityHashMap<RNode, Closure> getContent() {
-        if (languageClosureCache == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            languageClosureCache = new IdentityHashMap<>();
-        }
-        return languageClosureCache;
-    }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java
index 8167d69768ac27941ebe9caae88aff77e3cb21f6..adb31fe1b3da2c65c83c82b723655ac7d6fd04ea 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -48,7 +48,7 @@ import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.data.Closure;
-import com.oracle.truffle.r.runtime.data.ClosureCache;
+import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RPromise;
@@ -356,7 +356,7 @@ public abstract class PromiseNode extends RNode {
     }
 
     @TruffleBoundary
-    static RNode createVarArgs(RNode[] nodes, ArgumentsSignature signature, ClosureCache closureCache, boolean forcedEager) {
+    static RNode createVarArgs(RNode[] nodes, ArgumentsSignature signature, RNodeClosureCache closureCache, boolean forcedEager) {
         return new VarArgsPromiseNode(nodes, signature, closureCache, forcedEager);
     }
 
@@ -368,7 +368,7 @@ public abstract class PromiseNode extends RNode {
         private final Closure[] closures;
         private final ArgumentsSignature signature;
 
-        private VarArgsPromiseNode(RNode[] nodes, ArgumentsSignature signature, ClosureCache closureCache, boolean forcedEager) {
+        private VarArgsPromiseNode(RNode[] nodes, ArgumentsSignature signature, RNodeClosureCache closureCache, boolean forcedEager) {
             this.promised = new RNode[nodes.length];
             this.closures = new Closure[nodes.length];
             boolean noOpt = false;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java
index 8fd61c7ef545fcff84bbf52286b55dff23baa7c4..26612698417fded8af940440169b54085947ab03 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java
@@ -85,7 +85,6 @@ import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.RVisibility;
-import com.oracle.truffle.r.runtime.SubstituteVirtualFrame;
 import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
 import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.conn.RConnection;
@@ -98,6 +97,7 @@ import com.oracle.truffle.r.runtime.data.REmpty;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RMissing;
+import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
@@ -150,6 +150,8 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
     @Child private ReadVariableNode lookupVarArgs;
     protected final LocalReadVariableNode explicitArgs;
 
+    @Child public LocalReadVariableNode explicitCaller;
+
     private final ConditionProfile nullBuiltinProfile = ConditionProfile.createBinaryProfile();
 
     // needed for INTERNAL_GENERIC calls:
@@ -160,6 +162,10 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
         if (explicitArgs == null) {
             return RCaller.create(frame, this);
         } else {
+            Object explicitCallerValue = explicitCaller.execute(frame);
+            if (explicitCallerValue != RNull.instance) {
+                return (RCaller) explicitCallerValue;
+            }
             return RCaller.create(frame, RCallerHelper.createFromArguments(function, (RArgsValuesAndNames) explicitArgs.execute(frame)));
         }
     }
@@ -174,11 +180,12 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
         this.signature = signature;
     }
 
-    protected RCallNode(SourceSection sourceSection, Object explicitArgsIdentifier) {
+    protected RCallNode(SourceSection sourceSection, Object explicitArgsIdentifier, Object explicitCallerIdentifier) {
         assert sourceSection != null;
         this.sourceSection = sourceSection;
         this.arguments = null;
         this.explicitArgs = LocalReadVariableNode.create(explicitArgsIdentifier, false);
+        this.explicitCaller = LocalReadVariableNode.create(explicitCallerIdentifier, false);
         this.varArgIndexes = null;
         this.lookupVarArgs = null;
         this.signature = null;
@@ -352,22 +359,24 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
         RBuiltinDescriptor builtin = builtinProfile.profile(function.getRBuiltin());
         RArgsValuesAndNames argAndNames = (RArgsValuesAndNames) explicitArgs.execute(frame);
 
-        Object dispatchObject = argAndNames.getArgument(0);
-
-        if (isAttributableProfile.profile(dispatchObject instanceof RAttributeStorage) && isS4Profile.profile(((RAttributeStorage) dispatchObject).isS4())) {
-            RList list = (RList) promiseHelperNode.checkEvaluate(frame, REnvironment.getRegisteredNamespace("methods").get(".BasicFunsList"));
-            // TODO create a node that looks up the name in the names attribute
-            int index = list.getElementIndexByName(builtin.getName());
-            if (index != -1) {
-                RFunction basicFun = (RFunction) list.getDataAt(index);
-                Object result = call.execute(frame, basicFun, argAndNames, null, null);
-                if (result != RRuntime.DEFERRED_DEFAULT_MARKER) {
-                    return result;
+        RStringVector type = null;
+        if (!argAndNames.isEmpty()) {
+            Object dispatchObject = argAndNames.getArgument(0);
+            if (isAttributableProfile.profile(dispatchObject instanceof RAttributeStorage) && isS4Profile.profile(((RAttributeStorage) dispatchObject).isS4())) {
+                RList list = (RList) promiseHelperNode.checkEvaluate(frame, REnvironment.getRegisteredNamespace("methods").get(".BasicFunsList"));
+                // TODO create a node that looks up the name in the names attribute
+                int index = list.getElementIndexByName(builtin.getName());
+                if (index != -1) {
+                    RFunction basicFun = (RFunction) list.getDataAt(index);
+                    Object result = call.execute(frame, basicFun, argAndNames, null, null);
+                    if (result != RRuntime.DEFERRED_DEFAULT_MARKER) {
+                        return result;
+                    }
                 }
             }
+            type = classHierarchyNode.execute(promiseHelperNode.checkEvaluate(frame, dispatchObject));
         }
 
-        RStringVector type = argAndNames.isEmpty() ? null : classHierarchyNode.execute(promiseHelperNode.checkEvaluate(frame, dispatchObject));
         S3Args s3Args;
         RFunction resultFunction;
         if (implicitTypeProfile.profile(type != null)) {
@@ -751,8 +760,8 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
      * allows to invoke a function with argument(s) supplied by hand. Consider using
      * {@link com.oracle.truffle.r.nodes.function.call.RExplicitCallNode} instead.
      */
-    public static RCallNode createExplicitCall(Object explicitArgsIdentifier) {
-        return RCallNodeGen.create(RSyntaxNode.INTERNAL, explicitArgsIdentifier, null);
+    public static RCallNode createExplicitCall(Object explicitArgsIdentifier, Object explicitCallerIdentifier) {
+        return RCallNodeGen.create(RSyntaxNode.INTERNAL, explicitArgsIdentifier, explicitCallerIdentifier, null);
     }
 
     static boolean needsSplitting(RootCallTarget target) {
@@ -870,9 +879,8 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
                 if (e == null || e.cachedTarget != cachedTarget) {
                     entry = e = insert(new GenericCallEntry(cachedTarget, createCacheNode(cachedTarget), createArguments(cachedTarget)));
                 }
-                VirtualFrame frame = SubstituteVirtualFrame.create(materializedFrame);
-                RArgsValuesAndNames orderedArguments = e.prepareArguments.execute(frame, (RArgsValuesAndNames) varArgs, (S3DefaultArguments) s3DefaultArguments, originalCall);
-                return e.leafCall.execute(frame, function, orderedArguments, (S3Args) s3Args);
+                RArgsValuesAndNames orderedArguments = e.prepareArguments.execute(materializedFrame, (RArgsValuesAndNames) varArgs, (S3DefaultArguments) s3DefaultArguments, originalCall);
+                return e.leafCall.execute(materializedFrame, function, orderedArguments, (S3Args) s3Args);
             }
         }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnmatchedArguments.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnmatchedArguments.java
deleted file mode 100644
index fd0461f0dc9fac10eeb915e7d01ccc980dd7bfb0..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnmatchedArguments.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.r.nodes.function;
-
-import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.data.ClosureCache;
-import com.oracle.truffle.r.runtime.nodes.RNode;
-
-/**
- * An interface all arguments that are going to be matched need to implement.
- */
-public interface UnmatchedArguments extends ClosureCache {
-    /**
-     * @return The arguments to be matched. Individual {@link RNode}s may be <code>null</code> to
-     *         denote "missingness"!
-     */
-    RNode[] getArguments();
-
-    ArgumentsSignature getSignature();
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitBaseEnvCallDispatcher.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitBaseEnvCallDispatcher.java
index 0fd6147435e0e4362e448097d9dd2a8b3dfab2f8..2a26de3d44ad602db2f991a6231ab2317a882dc1 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitBaseEnvCallDispatcher.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitBaseEnvCallDispatcher.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -63,7 +63,7 @@ public abstract class RExplicitBaseEnvCallDispatcher extends Node {
     @Specialization
     public Object doCached(VirtualFrame frame, RArgsValuesAndNames arguments,
                     @Cached("getFunction(frame)") RFunction function) {
-        return callNode.execute(frame, function, arguments);
+        return callNode.call(frame, function, arguments);
     }
 
     RFunction getFunction(VirtualFrame frame) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitCallNode.java
index 3a4af7be70543373b97334da11591ad97ba1b7ec..d58a059ae2b47b59c9d1211d09d2a955a625fcc5 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitCallNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitCallNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -30,10 +30,11 @@ import com.oracle.truffle.api.frame.FrameSlot;
 import com.oracle.truffle.api.frame.FrameSlotKind;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.nodes.function.RCallBaseNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
+import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.env.frame.RFrameSlot;
 
@@ -46,27 +47,39 @@ public abstract class RExplicitCallNode extends Node {
         return RExplicitCallNodeGen.create();
     }
 
-    public abstract Object execute(VirtualFrame frame, RFunction function, RArgsValuesAndNames args);
+    public final Object call(VirtualFrame frame, RFunction function, RArgsValuesAndNames args) {
+        return execute(frame, function, args, null);
+    }
+
+    public abstract Object execute(VirtualFrame frame, RFunction function, RArgsValuesAndNames args, RCaller explicitCaller);
 
     private final RFrameSlot argsIdentifier = RFrameSlot.createTemp(true);
+    private final RFrameSlot callerIdentifier = RFrameSlot.createTemp(true);
     @CompilationFinal private FrameSlot argsFrameSlot;
+    @CompilationFinal private FrameSlot callerFrameSlot;
 
     @Specialization
-    protected Object doCall(VirtualFrame frame, RFunction function, RArgsValuesAndNames args,
-                    @Cached("createExplicitCall()") RCallBaseNode call) {
+    protected Object doCall(VirtualFrame frame, RFunction function, RArgsValuesAndNames args, RCaller caller,
+                    @Cached("createExplicitCall()") RCallNode call) {
         if (argsFrameSlot == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             argsFrameSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frame.getFrameDescriptor(), argsIdentifier, FrameSlotKind.Object);
         }
+        if (callerFrameSlot == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            callerFrameSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frame.getFrameDescriptor(), callerIdentifier, FrameSlotKind.Object);
+        }
         try {
             FrameSlotChangeMonitor.setObject(frame, argsFrameSlot, args);
+            FrameSlotChangeMonitor.setObject(frame, callerFrameSlot, caller == null ? RNull.instance : caller);
             return call.execute(frame, function);
         } finally {
             FrameSlotChangeMonitor.setObject(frame, argsFrameSlot, null);
+            FrameSlotChangeMonitor.setObject(frame, callerFrameSlot, null);
         }
     }
 
-    protected RCallBaseNode createExplicitCall() {
-        return RCallNode.createExplicitCall(argsIdentifier);
+    protected RCallNode createExplicitCall() {
+        return RCallNode.createExplicitCall(argsIdentifier, callerIdentifier);
     }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCaller.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCaller.java
index 646850b0d719b8394561ccbe10b773bab4f374b1..68acee4338a7f57060cf575d9039893c72a1e456 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCaller.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCaller.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -29,7 +29,14 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
 
 /**
  * Represents the caller of a function and stored in {@link RArguments}. A value of this type never
- * appears in a Truffle execution.
+ * appears in a Truffle execution. Caller remembers its parent caller and frame number as described
+ * in {@code sys.parent} R function documentation: frames are numbered from 0 (global environment),
+ * parent does not have to have the frame with number one less, e.g. with do.call(fun, args, envir)
+ * when fun asks for parent, it should get 'envir', moreover, when evaluating promises parent frame
+ * and frame with number one less are typically also not the same frames. See also builtins in
+ * {@code FrameFunctions} for more details.
+ * 
+ * @see RArguments
  */
 public final class RCaller {
 
@@ -47,23 +54,24 @@ public final class RCaller {
     private final RCaller parent;
     /**
      * The payload can be an RSyntaxNode, a {@link Supplier}, or an {@link RCaller} (which marks
-     * promise evaluation frames).
+     * promise evaluation frames). Payload represents the syntax (AST) of how the function was
+     * invoked. If the function was invoked via regular call node, then the syntax can be that call
+     * node (RSyntaxNode case), if the function was invoked by other means and we do not have the
+     * actual syntax for the invocation, we only provide it lazily via Supplier, so that we do not
+     * have to always construct the AST nodes.
      */
     private final Object payload;
-    /**
-     * Marks those callers whose parent should not be taken into account when iterating R level
-     * frames using e.g. {@code parent.frame()}. This is the case for function invoked through
-     * {@code do.call} -- R pretends that they were called by the caller of {@code do.call} so that
-     * code like {@code eval(formula, parent.frame(2))} gives the same results regardless of whether
-     * the function was invoked directly or through {@code do.call}.
-     */
-    private final boolean parentIsInternal;
 
-    private RCaller(Frame callingFrame, Object nodeOrSupplier, boolean parentIsInternal) {
+    private RCaller(Frame callingFrame, Object nodeOrSupplier) {
         this.depth = depthFromFrame(callingFrame);
         this.parent = parentFromFrame(callingFrame);
         this.payload = nodeOrSupplier;
-        this.parentIsInternal = parentIsInternal;
+    }
+
+    private RCaller(int depth, RCaller parent, Object nodeOrSupplier) {
+        this.depth = depth;
+        this.parent = parent;
+        this.payload = nodeOrSupplier;
     }
 
     private static int depthFromFrame(Frame callingFrame) {
@@ -74,17 +82,6 @@ public final class RCaller {
         return callingFrame == null ? null : RArguments.getCall(callingFrame);
     }
 
-    private RCaller(int depth, RCaller parent, Object nodeOrSupplier, boolean parentIsInternal) {
-        this.depth = depth;
-        this.parent = parent;
-        this.payload = nodeOrSupplier;
-        this.parentIsInternal = parentIsInternal;
-    }
-
-    public RCaller withInternalParent() {
-        return new RCaller(depth, parent, payload, true);
-    }
-
     public int getDepth() {
         return depth;
     }
@@ -93,10 +90,6 @@ public final class RCaller {
         return parent;
     }
 
-    public boolean hasInternalParent() {
-        return parentIsInternal;
-    }
-
     public RSyntaxElement getSyntaxNode() {
         assert payload != null && !(payload instanceof RCaller) : payload == null ? "null RCaller" : "promise RCaller";
         return payload instanceof RSyntaxElement ? (RSyntaxElement) payload : (RSyntaxElement) ((Supplier<?>) payload).get();
@@ -115,42 +108,42 @@ public final class RCaller {
     }
 
     public static RCaller createInvalid(Frame callingFrame) {
-        return new RCaller(callingFrame, null, false);
+        return new RCaller(callingFrame, null);
     }
 
     public static RCaller createInvalid(Frame callingFrame, RCaller parent) {
-        return new RCaller(depthFromFrame(callingFrame), parent, null, false);
+        return new RCaller(depthFromFrame(callingFrame), parent, null);
     }
 
     public static RCaller create(Frame callingFrame, RSyntaxElement node) {
         assert node != null;
-        return new RCaller(callingFrame, node, false);
+        return new RCaller(callingFrame, node);
     }
 
     public static RCaller create(Frame callingFrame, RCaller parent, RSyntaxElement node) {
         assert node != null;
-        return new RCaller(depthFromFrame(callingFrame), parent, node, false);
+        return new RCaller(depthFromFrame(callingFrame), parent, node);
     }
 
-    public static RCaller createWithInternalParent(Frame callingFrame, Supplier<RSyntaxElement> supplier) {
+    public static RCaller create(Frame callingFrame, Supplier<RSyntaxElement> supplier) {
         assert supplier != null;
-        return new RCaller(callingFrame, supplier, true);
+        return new RCaller(callingFrame, supplier);
     }
 
-    public static RCaller create(Frame callingFrame, Supplier<RSyntaxElement> supplier) {
-        assert supplier != null;
-        return new RCaller(callingFrame, supplier, false);
+    public static RCaller create(int depth, RCaller parent, Object payload) {
+        assert payload != null;
+        return new RCaller(depth, parent, payload);
     }
 
     public static RCaller create(Frame callingFrame, RCaller parent, Supplier<RSyntaxElement> supplier) {
         assert supplier != null;
-        return new RCaller(depthFromFrame(callingFrame), parent, supplier, false);
+        return new RCaller(depthFromFrame(callingFrame), parent, supplier);
     }
 
     public static RCaller createForPromise(RCaller originalCaller, Frame frame) {
         int newDepth = frame == null ? 0 : RArguments.getDepth(frame);
         RCaller originalCall = frame == null ? null : RArguments.getCall(frame);
-        return new RCaller(newDepth, originalCaller, originalCall, false);
+        return new RCaller(newDepth, originalCaller, originalCall);
     }
 
     public boolean getVisibility() {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ClosureCache.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ClosureCache.java
index a8976382cd81765d33f10da5b45468cc6983709f..f84c8201bb212ca26897996401038fb899630543 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ClosureCache.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ClosureCache.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -22,49 +22,65 @@
  */
 package com.oracle.truffle.r.runtime.data;
 
-import java.util.IdentityHashMap;
-import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 import com.oracle.truffle.r.runtime.nodes.RNode;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
 /**
- * A trait that enables the caching of {@link Closure}s for certain expressions ({@link RNode}s).
+ * Class that enables the caching of {@link Closure}s for certain expressions ({@link RNode}s).
+ * Instance of this class is supposed to be a field in AST node, which is using the cache. The field
+ * must be initialized in the constructor, but the underlying data structure (ConcurrentHashMap) is
+ * initialized lazily. All methods are thread safe.
+ * 
+ * Closures need to be cached so that we cache the corresponding call-targets and if the expression
+ * is evaluated again, we invoke it through the same call-target, which may be compiled by Truffle.
  */
-public interface ClosureCache {
+public abstract class ClosureCache<K> {
 
-    default Closure getOrCreatePromiseClosure(RNode expr) {
-        return getOrCreateClosure(Closure.PROMISE_CLOSURE_WRAPPER_NAME, expr);
+    private ConcurrentHashMap<K, Closure> cache;
+
+    public Closure getOrCreatePromiseClosure(K key) {
+        return getOrCreateClosure(Closure.PROMISE_CLOSURE_WRAPPER_NAME, key);
     }
 
-    default Closure getOrCreateLanguageClosure(RNode expr) {
-        return getOrCreateClosure(Closure.LANGUAGE_CLOSURE_WRAPPER_NAME, expr);
+    public Closure getOrCreateLanguageClosure(K key) {
+        return getOrCreateClosure(Closure.LANGUAGE_CLOSURE_WRAPPER_NAME, key);
     }
 
-    /**
-     * @param expr
-     * @return A {@link Closure} representing the given {@link RNode}. If expr is <code>null</code>
-     *         <code>null</code> is returned.
-     */
+    protected abstract RBaseNode keyToNode(K key);
+
     @TruffleBoundary
-    default Closure getOrCreateClosure(String name, RNode expr) {
-        if (expr == null) {
+    private Closure getOrCreateClosure(String name, K key) {
+        if (key == null) {
             return null;
         }
+        if (cache == null) {
+            initMap();
+        }
+        return cache.computeIfAbsent(key, k -> Closure.create(name, keyToNode(k)));
+    }
 
-        IdentityHashMap<RNode, Closure> cache = getContent();
-        Closure result = cache.get(expr);
-        if (result == null) {
-            result = Closure.create(name, expr);
-            cache.put(expr, result);
+    private synchronized void initMap() {
+        if (cache == null) {
+            cache = new ConcurrentHashMap<>();
         }
-        return result;
     }
 
-    /**
-     * Access to the raw content.
-     *
-     * @return The {@link Map} containing the cached values
-     */
-    IdentityHashMap<RNode, Closure> getContent();
+    public static final class RNodeClosureCache extends ClosureCache<RBaseNode> {
+        @Override
+        protected RBaseNode keyToNode(RBaseNode key) {
+            return key;
+        }
+    }
+
+    public static final class SymbolClosureCache extends ClosureCache<String> {
+        @Override
+        protected RNode keyToNode(String key) {
+            return RContext.getASTBuilder().lookup(RSyntaxNode.SOURCE_UNAVAILABLE, key, false).asRNode();
+        }
+    }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
index 27a1af0f7972a7bf2d73405f23b0549990c138a4..7550b1b88e80478fb75c145a6122116e3c67ef70 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
@@ -22889,6 +22889,10 @@ NULL
 #argv <- structure(list(expr = expression(quote(x <- c(1, x)))),     .Names = 'expr');do.call('.doTrace', argv)
 NULL
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall#
+#do.call('c', list())
+NULL
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall#Output.IgnoreErrorContext#
 #typeof(do.call(function(x) x, list(as.symbol('foo'))))
 Error in (function (x)  : object 'foo' not found
@@ -73322,6 +73326,36 @@ v()
 [1] "x"
 
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameWithPromises#
+#{ top <- function(vtop) vtop;foo <- function(vfoo) top(vfoo);boo <- function(vboo) foo(sys.frame(vboo));bar <- function(vbar) do.call(boo, list(vbar), envir = parent.frame(2));baz <- function(vbaz) bar(vbaz);start <- function(vstart) baz(vstart);lapply(lapply(0:8, function(i) start(i)), function(env) sort(tolower(ls(env)))); }
+[[1]]
+[1] "bar"   "baz"   "boo"   "foo"   "start" "top"
+
+[[2]]
+[1] "fun" "i"   "x"
+
+[[3]]
+[1] "fun" "i"   "x"
+
+[[4]]
+[1] "i"
+
+[[5]]
+[1] "vstart"
+
+[[6]]
+[1] "vbaz"
+
+[[7]]
+[1] "vbar"
+
+[[8]]
+[1] "args"  "envir" "quote" "what"
+
+[[9]]
+[1] "vboo"
+
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframes.frameAccessCommonTest#Ignored.ImplementationError#
 #{ foo <- function(x) lapply(sys.frames(), function(x) ls(x));bar <- function(ba) do.call(foo, list(ba));boo <- function(bo) bar(bo);callboo <- function(cb) do.call('boo', list(cb));fun <- function(f) callboo(f);fun(42);}
 [[1]]
@@ -164585,6 +164619,11 @@ Error in prod("a") : invalid 'type' (character) of argument
 Called from: top level
 
 
+##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testBrowser#
+#{ bar <- function(vbar) do.call(browser, list(), envir = parent.frame(2));baz <- function(vbaz) bar(vbaz);start <- function(vstart) baz(vstart);start(42); }<<<NEWLINE>>>ls()<<<NEWLINE>>>c
+Called from: do.call(browser, list(), envir = parent.frame(2))
+[1] "vstart"
+
 ##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testConditionalBreakpoint#
 #fun <- function(x) { cat('x='); cat(x); cat('\n') }; trace(fun, quote(if (x > 10) browser())); fun(10)<<<NEWLINE>>>; fun(11)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>
 [1] "fun"
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_docall.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_docall.java
index 0f496903658c8919cac514ad4ff868f32aaf52e6..85d9ed9b4db6ea6b7c5de1ecba7517ee1cf65f12 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_docall.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_docall.java
@@ -4,7 +4,7 @@
  * http://www.gnu.org/licenses/gpl-2.0.html
  *
  * Copyright (c) 2012-2014, Purdue University
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates
  *
  * All rights reserved.
  */
@@ -48,5 +48,7 @@ public class TestBuiltin_docall extends TestBase {
         assertEval("{ boo <- function(c) ls(parent.frame(2)); foo <- function(a,b) boo(a); bar <- function(x,z) do.call('foo', list(parse(text='goo()'),2)); bar() }");
         assertEval("{ boo <- function(c) ls(parent.frame(3)); foo <- function(a,b) boo(a); bar <- function(x,z) do.call('foo', list(parse(text='goo()'),2)); baz <- function(bazX) bar(bazX,1); baz(); }");
         assertEval("{ f1 <- function(a) ls(parent.frame(2)); f2 <- function(b) f1(b); f3 <- function(c) f2(c); f4 <- function(d) do.call('f3', list(d)); f4(42); }");
+
+        assertEval("do.call('c', list())");
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_sysframe.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_sysframe.java
index a318459bdd25082bc2360589ba779b3f89e4aede..044f6802683a1ce19d8050800e34b8ac1fc8dd42 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_sysframe.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_sysframe.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -33,4 +33,17 @@ public class TestBuiltin_sysframe extends TestBase {
     public void frameAccessCommonTest() {
         assertEval("{ foo <- function(x) lapply(1:7, function(x) ls(sys.frame(x)));" + SYS_PARENT_SETUP + "}");
     }
+
+    @Test
+    public void sysFrameWithPromises() {
+        // Note: the parameter names help to identify the corresponding environments in the output
+        assertEval(
+                        "{ top <- function(vtop) vtop;" +
+                                        "foo <- function(vfoo) top(vfoo);" +
+                                        "boo <- function(vboo) foo(sys.frame(vboo));" +
+                                        "bar <- function(vbar) do.call(boo, list(vbar), envir = parent.frame(2));" +
+                                        "baz <- function(vbaz) bar(vbaz);" +
+                                        "start <- function(vstart) baz(vstart);" +
+                                        "lapply(lapply(0:8, function(i) start(i)), function(env) sort(tolower(ls(env)))); }");
+    }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java
index 6f94e4071b72db88881ce0c0c731b3c001b5396a..baca92411e714b465d28f5a3496ea4905f2ea219 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java
@@ -130,6 +130,11 @@ public class TestInteractiveDebug extends TestBase {
         assertEval("do.call('browser', list())\nc\n");
         assertEval("browser()\nwhere\nc\n");
         assertEval("options(error=browser); prod('a')\nwhere\nc\n");
+        assertEval(
+                        "{ bar <- function(vbar) do.call(browser, list(), envir = parent.frame(2));" +
+                                        "baz <- function(vbaz) bar(vbaz);" +
+                                        "start <- function(vstart) baz(vstart);" +
+                                        "start(42); }\nls()\nc");
     }
 
     @Test