From 24c3374020390f4851a7f1e6bc54c66b5430e121 Mon Sep 17 00:00:00 2001
From: stepan <stepan.sindelar@oracle.com>
Date: Thu, 1 Feb 2018 14:31:19 +0100
Subject: [PATCH] Rework DoCall builtin do use RExplicitCall and to not create
 new AST nodes every time

---
 .../truffle/r/engine/interop/RFunctionMR.java |   6 +-
 .../truffle/r/library/fastrGrid/ViewPort.java |   2 +-
 .../truffle/r/nodes/builtin/base/Bind.java    |   2 +-
 .../truffle/r/nodes/builtin/base/DoCall.java  | 208 +++++++++---------
 .../r/nodes/builtin/base/ForceAndCall.java    |   4 +-
 .../r/nodes/builtin/base/FrameFunctions.java  |  38 ++--
 .../truffle/r/nodes/builtin/base/Mapply.java  |   4 +-
 .../truffle/r/nodes/builtin/base/Recall.java  |   4 +-
 .../r/nodes/builtin/fastr/FastRInterop.java   |   2 +-
 .../r/nodes/builtin/fastr/FastRTestsTry.java  |   2 +-
 .../r/nodes/access/BaseWriteVariableNode.java |   4 +-
 .../variables/LocalReadVariableNode.java      |   4 +-
 .../access/variables/ReadVariableNode.java    |   4 +-
 .../truffle/r/nodes/function/RCallNode.java   |  20 +-
 .../call/RExplicitBaseEnvCallDispatcher.java  |   4 +-
 .../function/call/RExplicitCallNode.java      |  27 ++-
 .../com/oracle/truffle/r/runtime/RCaller.java |  27 ++-
 .../truffle/r/test/ExpectedTestOutput.test    |  39 ++++
 .../r/test/builtins/TestBuiltin_docall.java   |   2 +-
 .../r/test/builtins/TestBuiltin_sysframe.java |  15 +-
 .../library/utils/TestInteractiveDebug.java   |   5 +
 21 files changed, 266 insertions(+), 157 deletions(-)

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 36d76a99b6..47890c2305 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 b1c90a3a02..6d1772a79b 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 165ad928ea..00894b2b3b 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 fed4396ff0..61ef2ee947 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,27 +45,26 @@ 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;
@@ -79,6 +83,7 @@ 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)
@@ -119,8 +124,9 @@ 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();
 
         public static DoCallInternal create() {
             return DoCallInternalNodeGen.create();
@@ -128,23 +134,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,43 +217,32 @@ 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);
                         }
+                    } else if (arg instanceof RLanguage) {
+                        argValues[i] = RDataFactory.createPromise(PromiseState.Default, Closure.createPromiseClosure(((RLanguage) arg).getRep()), promiseFrame);
                     }
                 }
             }
-            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;
+        private void setVisibility(VirtualFrame frame, boolean value) {
+            if (setVisibilityNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setVisibilityNode = insert(SetVisibilityNode.create());
             }
-            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;
-                }
+            setVisibilityNode.execute(frame, value);
+        }
+
+        private ArgumentsSignature getArgsNames(RList argsAsList) {
+            if (getNamesNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                getNamesNode = insert(GetNamesAttributeNode.create());
             }
-            return true;
+            ArgumentsSignature signature = ArgumentsSignature.fromNamesAttribute(getNamesNode.getNames(argsAsList));
+            return signature == null ? ArgumentsSignature.empty(argsAsList.getLength()) : signature;
         }
 
         @TruffleBoundary
@@ -200,7 +252,7 @@ public abstract class DoCall extends RBuiltinNode.Arg4 implements InternalRSynta
         }
 
         @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 +263,18 @@ 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;
-            }
+        public Object execute(VirtualFrame 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 3cef036d53..0a315155bb 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 e15ff3334c..15afd7016c 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;
             }
@@ -672,9 +673,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 dc3a03b8f5..50a3c846eb 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 5d82e00576..4b9b9eaf9c 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 300ec8e7de..8da59509ff 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 7b7a63ecee..98c3abe8dc 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/BaseWriteVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java
index 6db88a49aa..53db2b3f5b 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 f3e42feefc..f67c254ce6 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 74e6cfc821..7104433d3e 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/RCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java
index 8fd61c7ef5..43e94209b9 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;
@@ -751,8 +758,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 +877,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/call/RExplicitBaseEnvCallDispatcher.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitBaseEnvCallDispatcher.java
index 0fd6147435..2a26de3d44 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 3a4af7be70..d58a059ae2 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 646850b0d7..cf26ddb47f 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 {
 
@@ -66,6 +73,13 @@ public final class RCaller {
         this.parentIsInternal = parentIsInternal;
     }
 
+    private RCaller(int depth, RCaller parent, Object nodeOrSupplier) {
+        this.depth = depth;
+        this.parent = parent;
+        this.payload = nodeOrSupplier;
+        this.parentIsInternal = false;
+    }
+
     private static int depthFromFrame(Frame callingFrame) {
         return callingFrame == null ? 0 : RArguments.getCall(callingFrame).getDepth() + 1;
     }
@@ -142,6 +156,11 @@ public final class RCaller {
         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, false);
+    }
+
     public static RCaller create(Frame callingFrame, RCaller parent, Supplier<RSyntaxElement> supplier) {
         assert supplier != null;
         return new RCaller(depthFromFrame(callingFrame), parent, supplier, false);
@@ -160,4 +179,8 @@ public final class RCaller {
     public void setVisibility(boolean visibility) {
         this.visibility = visibility;
     }
+
+    public Object getPayload() {
+        return payload;
+    }
 }
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 27a1af0f79..7550b1b88e 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 0f49690365..acdd29b2c6 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.
  */
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 a318459bdd..044f680268 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 6f94e4071b..baca92411e 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
-- 
GitLab