diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
index bf2b03c42fab7464c866090374df12bf96fc89ae..025454ff93fa734c34e557a28831e9277ea022df 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
@@ -56,6 +56,9 @@ import com.oracle.truffle.r.nodes.control.BreakException;
 import com.oracle.truffle.r.nodes.control.NextException;
 import com.oracle.truffle.r.nodes.function.CallMatcherNode.CallMatcherGenericNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
+import com.oracle.truffle.r.nodes.function.call.CallRFunctionNode;
+import com.oracle.truffle.r.nodes.function.visibility.GetVisibilityNode;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.nodes.instrumentation.RInstrumentation;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.ExitException;
@@ -344,6 +347,7 @@ final class REngine implements Engine, Engine.Timings {
     }
 
     @Override
+    @TruffleBoundary
     public Object eval(RExpression exprs, REnvironment envir, RCaller caller) {
         Object result = RNull.instance;
         for (int i = 0; i < exprs.getLength(); i++) {
@@ -358,6 +362,7 @@ final class REngine implements Engine, Engine.Timings {
     }
 
     @Override
+    @TruffleBoundary
     public Object eval(RLanguage expr, REnvironment envir, RCaller caller) {
         return evalNode(expr.getRep().asRSyntaxNode(), envir, caller);
     }
@@ -410,8 +415,7 @@ final class REngine implements Engine, Engine.Timings {
                 newArgs[i] = PromiseHelperNode.evaluateSlowPath(null, (RPromise) arg);
             }
         }
-        Object[] rArgs = RArguments.create(func, caller == null ? RArguments.getCall(actualFrame) : caller, actualFrame, newArgs, null);
-        return func.getTarget().call(rArgs);
+        return CallRFunctionNode.executeSlowpath(func, caller == null ? RArguments.getCall(actualFrame) : caller, actualFrame, newArgs, null);
     }
 
     private Object evalNode(RSyntaxElement exprRep, REnvironment envir, RCaller caller) {
@@ -472,6 +476,8 @@ final class REngine implements Engine, Engine.Timings {
         private final boolean topLevel;
 
         @Child private RNode body;
+        @Child private GetVisibilityNode visibility = GetVisibilityNode.create();
+        @Child private SetVisibilityNode setVisibility = SetVisibilityNode.create();
 
         protected AnonymousRootNode(RNode body, String description, boolean printResult, boolean topLevel) {
             super(TruffleRLanguage.class, null, new FrameDescriptor());
@@ -502,13 +508,14 @@ final class REngine implements Engine, Engine.Timings {
                 assert checkResult(result);
                 if (printResult && result != null) {
                     assert topLevel;
-                    if (context.isVisible()) {
+                    if (visibility.execute(vf, context)) {
                         printResult(result);
                     }
                 }
                 if (topLevel) {
                     RErrorHandling.printWarnings(suppressWarnings);
                 }
+                setVisibility.executeEndOfFunction(vf);
             } catch (RError e) {
                 CompilerDirectives.transferToInterpreter();
                 throw e;
@@ -572,7 +579,7 @@ final class REngine implements Engine, Engine.Timings {
                 ((RShareable) resultValue).incRefCount();
             }
             MaterializedFrame callingFrame = REnvironment.globalEnv().getFrame();
-            function.getTarget().call(RArguments.create(function, RCaller.createInvalid(callingFrame), callingFrame, new Object[]{resultValue, RMissing.instance}, null));
+            CallRFunctionNode.executeSlowpath(function, RCaller.createInvalid(callingFrame), callingFrame, new Object[]{resultValue, RMissing.instance}, null);
             if (resultValue instanceof RShareable && !((RShareable) resultValue).isSharedPermanent()) {
                 ((RShareable) resultValue).decRefCount();
             }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
index eedbc00f40bdc250d7b202b9bd51637dafde34c0..4b958b6752fedded1a7364ead804883088535605 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
@@ -22,6 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import java.util.function.Supplier;
+
 import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.r.nodes.RRootNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
@@ -78,6 +80,7 @@ import com.oracle.truffle.r.nodes.builtin.fastr.FastrDqrls;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastrDqrlsNodeGen;
 import com.oracle.truffle.r.nodes.unary.UnaryNotNode;
 import com.oracle.truffle.r.nodes.unary.UnaryNotNodeGen;
+import com.oracle.truffle.r.runtime.RVisibility;
 import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -640,7 +643,11 @@ public class BasePackage extends RBuiltinPackage {
         ((RRootNode) function.getRootNode()).setFastPath(factory);
     }
 
-    private static void addFastPath(MaterializedFrame baseFrame, String name, java.util.function.Supplier<RFastPathNode> factory, Class<?> builtinNodeClass) {
+    private static void addFastPath(MaterializedFrame baseFrame, String name, Supplier<RFastPathNode> factory, RVisibility visibility) {
+        addFastPath(baseFrame, name, FastPathFactory.fromVisibility(visibility, factory));
+    }
+
+    private static void addFastPath(MaterializedFrame baseFrame, String name, Supplier<RFastPathNode> factory, Class<?> builtinNodeClass) {
         RBuiltin builtin = builtinNodeClass.getAnnotation(RBuiltin.class);
         addFastPath(baseFrame, name, FastPathFactory.fromRBuiltin(builtin, factory));
     }
@@ -648,16 +655,16 @@ public class BasePackage extends RBuiltinPackage {
     @Override
     public void loadOverrides(MaterializedFrame baseFrame) {
         super.loadOverrides(baseFrame);
-        addFastPath(baseFrame, "matrix", () -> MatrixFastPathNodeGen.create(null), Matrix.class);
-        addFastPath(baseFrame, "setdiff", () -> SetDiffFastPathNodeGen.create(null));
-        addFastPath(baseFrame, "get", () -> GetFastPathNodeGen.create(null));
-        addFastPath(baseFrame, "exists", () -> ExistsFastPathNodeGen.create(null), Exists.class);
-        addFastPath(baseFrame, "assign", () -> AssignFastPathNodeGen.create(null), Assign.class);
-        addFastPath(baseFrame, "is.element", () -> IsElementFastPathNodeGen.create(null));
-        addFastPath(baseFrame, "integer", () -> IntegerFastPathNodeGen.create(null));
-        addFastPath(baseFrame, "numeric", () -> DoubleFastPathNodeGen.create(null));
-        addFastPath(baseFrame, "double", () -> DoubleFastPathNodeGen.create(null));
-        addFastPath(baseFrame, "intersect", () -> IntersectFastPathNodeGen.create(null));
+        addFastPath(baseFrame, "matrix", MatrixFastPathNodeGen::create, Matrix.class);
+        addFastPath(baseFrame, "setdiff", SetDiffFastPathNodeGen::create, RVisibility.ON);
+        addFastPath(baseFrame, "get", GetFastPathNodeGen::create, RVisibility.ON);
+        addFastPath(baseFrame, "exists", ExistsFastPathNodeGen::create, Exists.class);
+        addFastPath(baseFrame, "assign", AssignFastPathNodeGen::create, Assign.class);
+        addFastPath(baseFrame, "is.element", IsElementFastPathNodeGen::create, RVisibility.ON);
+        addFastPath(baseFrame, "integer", IntegerFastPathNodeGen::create, RVisibility.ON);
+        addFastPath(baseFrame, "numeric", DoubleFastPathNodeGen::create, RVisibility.ON);
+        addFastPath(baseFrame, "double", DoubleFastPathNodeGen::create, RVisibility.ON);
+        addFastPath(baseFrame, "intersect", IntersectFastPathNodeGen::create, RVisibility.ON);
         addFastPath(baseFrame, "pmax", FastPathFactory.EVALUATE_ARGS);
         addFastPath(baseFrame, "pmin", FastPathFactory.EVALUATE_ARGS);
         addFastPath(baseFrame, "cbind", FastPathFactory.FORCED_EAGER_ARGS);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java
index 8909bff42e888192b915661115b3544a08e45672..5fc17758347b2e04013ac321b5451e25c7bd67ec 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java
@@ -82,7 +82,6 @@ public class BrowserFunctions {
                     browserState.pop();
                 }
             }
-            RContext.getInstance().setVisible(false);
             return RNull.instance;
         }
 
@@ -144,7 +143,6 @@ public class BrowserFunctions {
         @TruffleBoundary
         protected RNull browserSetDebug(@SuppressWarnings("unused") int n) {
             // TODO implement
-            RContext.getInstance().setVisible(false);
             return RNull.instance;
         }
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConditionFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConditionFunctions.java
index 802a42cd7055631bf3f1da0e136bb82228ef228a..9e46e7859168825951c07469d325a4a38143e518 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConditionFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConditionFunctions.java
@@ -11,7 +11,10 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.nullValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.size;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
 import static com.oracle.truffle.r.runtime.RErrorHandling.getHandlerStack;
 import static com.oracle.truffle.r.runtime.RVisibility.OFF;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
@@ -27,7 +30,6 @@ import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RErrorHandling;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
@@ -82,7 +84,6 @@ public class ConditionFunctions {
         @SuppressWarnings("unused")
         @Specialization
         protected RNull resetCondHands(Object stack) {
-            RContext.getInstance().setVisible(false);
             // TODO
             throw RInternalError.unimplemented();
         }
@@ -177,7 +178,6 @@ public class ConditionFunctions {
 
         @Specialization
         protected RNull seterrmessage(String msg) {
-            RContext.getInstance().setVisible(false);
             RErrorHandling.seterrmessage(msg);
             return RNull.instance;
         }
@@ -216,7 +216,6 @@ public class ConditionFunctions {
         @Specialization
         @TruffleBoundary
         protected RNull printDeferredWarnings() {
-            RContext.getInstance().setVisible(false);
             RErrorHandling.printDeferredWarnings();
             return RNull.instance;
         }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java
index f8874611fb802b6b6ff3752e363234c4a9952960..9d17ab954c923876b3a775662c9e13017684985c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java
@@ -22,8 +22,17 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
-import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.equalTo;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.gte;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.lte;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.notEmpty;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.nullValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.scalarStringValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
+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.nodes.builtin.CastBuilder.Predef.trueValue;
 import static com.oracle.truffle.r.runtime.RVisibility.OFF;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE;
@@ -403,7 +412,6 @@ public abstract class ConnectionFunctions {
         @Specialization
         @TruffleBoundary
         protected Object open(RConnection con, String open, @SuppressWarnings("unused") boolean blocking) {
-            RContext.getInstance().setVisible(false);
             try {
                 BaseRConnection baseConn = getBaseConnection(con);
                 if (baseConn.isClosed()) {
@@ -536,7 +544,6 @@ public abstract class ConnectionFunctions {
             } catch (IOException x) {
                 throw RError.error(this, RError.Message.ERROR_WRITING_CONNECTION, x.getMessage());
             }
-            RContext.getInstance().setVisible(false);
             return RNull.instance;
         }
 
@@ -647,7 +654,7 @@ public abstract class ConnectionFunctions {
 
     }
 
-    @RBuiltin(name = "writeChar", visibility = CUSTOM, kind = INTERNAL, parameterNames = {"object", "con", "nchars", "sep", "useBytes"}, behavior = IO)
+    @RBuiltin(name = "writeChar", visibility = OFF, kind = INTERNAL, parameterNames = {"object", "con", "nchars", "sep", "useBytes"}, behavior = IO)
     public abstract static class WriteChar extends InternalCloseHelper {
         @Override
         protected void createCasts(CastBuilder casts) {
@@ -675,7 +682,6 @@ public abstract class ConnectionFunctions {
             } catch (IOException x) {
                 throw RError.error(this, RError.Message.ERROR_WRITING_CONNECTION, x.getMessage());
             }
-            RContext.getInstance().setVisible(false);
             return RNull.instance;
         }
 
@@ -986,7 +992,7 @@ public abstract class ConnectionFunctions {
 
     }
 
-    @RBuiltin(name = "writeBin", visibility = CUSTOM, kind = INTERNAL, parameterNames = {"object", "con", "size", "swap", "useBytes"}, behavior = IO)
+    @RBuiltin(name = "writeBin", visibility = OFF, kind = INTERNAL, parameterNames = {"object", "con", "size", "swap", "useBytes"}, behavior = IO)
     public abstract static class WriteBin extends BinRBuiltinNode {
 
         @Override
@@ -1018,7 +1024,6 @@ public abstract class ConnectionFunctions {
                     throw RError.error(this, RError.Message.ERROR_WRITING_CONNECTION, x.getMessage());
                 }
             }
-            RContext.getInstance().setVisible(false);
             return RNull.instance;
         }
 
@@ -1029,7 +1034,6 @@ public abstract class ConnectionFunctions {
             boolean useBytes = RRuntime.fromLogical(useBytesArg);
             ByteBuffer buffer = writeData.execute(object, size, swap, useBytes);
             buffer.flip();
-            RContext.getInstance().setVisible(false);
             return RDataFactory.createRawVector(buffer.array());
         }
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DebugFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DebugFunctions.java
index 73c025a65417598cd818d7b63951dbffee56b5a3..a4ed591f4a180e85358173bb53b0c57a4346d00d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DebugFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DebugFunctions.java
@@ -36,7 +36,6 @@ import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RNull;
 
@@ -101,7 +100,6 @@ public class DebugFunctions {
         @Specialization
         @TruffleBoundary
         protected byte isDebugged(RFunction func) {
-            RContext.getInstance().setVisible(true);
             return RRuntime.asLogical(DebugHandling.isDebugged(func));
         }
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java
index 2175b5841baeaa0313282dd7634809a4bafb0ff8..d0566c15a27fa135ed742987b7967b291f9e1cc9 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java
@@ -26,6 +26,8 @@ import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
+import java.beans.Visibility;
+
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
@@ -34,6 +36,7 @@ import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.EvalFunctionsFactory.EvalEnvCastNodeGen;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
@@ -119,25 +122,33 @@ public class EvalFunctions {
     @RBuiltin(name = "eval", visibility = CUSTOM, kind = INTERNAL, parameterNames = {"expr", "envir", "enclos"}, behavior = COMPLEX)
     public abstract static class Eval extends RBuiltinNode {
 
-        @TruffleBoundary
-        protected Object doEvalBody(RCaller rCaller, Object exprArg, REnvironment envir) {
-            Object expr = RASTUtils.checkForRSymbol(exprArg);
-
-            if (expr instanceof RExpression) {
-                return RContext.getEngine().eval((RExpression) expr, envir, rCaller);
-            } else if (expr instanceof RLanguage) {
-                return RContext.getEngine().eval((RLanguage) expr, envir, rCaller);
-            } else {
-                // just return value
-                return expr;
-            }
-        }
+        @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
 
         @Specialization
         protected Object doEval(VirtualFrame frame, Object expr, Object envir, Object enclos, //
                         @Cached("createCast()") EvalEnvCast envCast) {
             // Note: fallback for invalid combinations of envir and enclos is in EvalEnvCastNode
-            return doEvalBody(RCaller.create(frame, getOriginalCall()), expr, envCast.execute(envir, enclos));
+            RCaller rCaller = RCaller.create(frame, getOriginalCall());
+            REnvironment envir1 = envCast.execute(envir, enclos);
+            Object expr1 = RASTUtils.checkForRSymbol(expr);
+
+            if (expr1 instanceof RExpression) {
+                try {
+                    return RContext.getEngine().eval((RExpression) expr1, envir1, rCaller);
+                } finally {
+                    visibility.executeAfterCall(frame);
+                }
+            } else if (expr1 instanceof RLanguage) {
+                try {
+                    return RContext.getEngine().eval((RLanguage) expr1, envir1, rCaller);
+                } finally {
+                    visibility.executeAfterCall(frame);
+                }
+            } else {
+                // just return value
+                visibility.execute(frame, true);
+                return expr1;
+            }
         }
 
         protected EvalEnvCast createCast() {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java
index 1898de31ae2fd9d3eda061feeecdc2c30566c106..932d169e1beb658373a7a3e03bf2c4c9ab47b98f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java
@@ -35,8 +35,6 @@ 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.CallInlineCacheNode;
-import com.oracle.truffle.r.nodes.CallInlineCacheNodeGen;
 import com.oracle.truffle.r.nodes.RRootNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.attributes.TypeFromModeNode;
@@ -46,7 +44,8 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.FormalArguments;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.nodes.function.RCallerHelper;
-import com.oracle.truffle.r.nodes.function.signature.RArgumentsNode;
+import com.oracle.truffle.r.nodes.function.call.CallRFunctionCachedNode;
+import com.oracle.truffle.r.nodes.function.call.CallRFunctionCachedNodeGen;
 import com.oracle.truffle.r.nodes.objects.GetS4DataSlot;
 import com.oracle.truffle.r.nodes.objects.GetS4DataSlotNodeGen;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
@@ -224,8 +223,7 @@ public class GetFunctions {
         private final BranchProfile wrongLengthErrorProfile = BranchProfile.create();
 
         @Child private TypeFromModeNode typeFromMode = TypeFromModeNodeGen.create();
-        @Child private CallInlineCacheNode callCache = CallInlineCacheNodeGen.create();
-        @Child private RArgumentsNode argsNode;
+        @Child private CallRFunctionCachedNode callCache = CallRFunctionCachedNodeGen.create(2);
 
         @CompilationFinal private boolean needsCallerFrame;
 
@@ -337,15 +335,11 @@ public class GetFunctions {
                 CompilerDirectives.transferToInterpreterAndInvalidate();
                 needsCallerFrame = true;
             }
-            if (argsNode == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                argsNode = insert(RArgumentsNode.create());
-            }
             MaterializedFrame callerFrame = needsCallerFrame ? frame.materialize() : null;
             FormalArguments formals = ((RRootNode) ifnFunc.getRootNode()).getFormalArguments();
             RArgsValuesAndNames args = new RArgsValuesAndNames(new Object[]{x}, ArgumentsSignature.empty(1));
-            Object[] callArgs = argsNode.execute(ifnFunc, RCaller.create(frame, RCallerHelper.createFromArguments(ifnFunc, args)), callerFrame, new Object[]{x}, formals.getSignature(), null);
-            return callCache.execute(frame, ifnFunc.getTarget(), callArgs);
+            return callCache.execute(frame, ifnFunc, RCaller.create(frame, RCallerHelper.createFromArguments(ifnFunc, args)), callerFrame, new Object[]{x}, formals.getSignature(),
+                            ifnFunc.getEnclosingFrame(), null);
         }
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
index 0de9b7a77ce26cbf755439f550204af27a035556..4359d6faa7feb74f933cf8ebfb413b8c95a84535 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
@@ -11,7 +11,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.nullValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
 import static com.oracle.truffle.r.runtime.RVisibility.OFF;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
@@ -31,8 +33,6 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.LoopNode;
-import com.oracle.truffle.r.nodes.CallInlineCacheNode;
-import com.oracle.truffle.r.nodes.CallInlineCacheNodeGen;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.access.ConstantNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
@@ -41,7 +41,8 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.EvalFunctions.Eval;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
-import com.oracle.truffle.r.runtime.RArguments;
+import com.oracle.truffle.r.nodes.function.call.CallRFunctionCachedNode;
+import com.oracle.truffle.r.nodes.function.call.CallRFunctionCachedNodeGen;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RCompression;
 import com.oracle.truffle.r.runtime.RDeparse;
@@ -187,7 +188,7 @@ public class HiddenInternalFunctions {
     @RBuiltin(name = "lazyLoadDBfetch", kind = PRIMITIVE, parameterNames = {"key", "datafile", "compressed", "envhook"}, behavior = PURE)
     public abstract static class LazyLoadDBFetch extends RBuiltinNode {
 
-        @Child private CallInlineCacheNode callCache = CallInlineCacheNodeGen.create();
+        @Child private CallRFunctionCachedNode callCache = CallRFunctionCachedNodeGen.create(2);
 
         @Override
         protected void createCasts(CastBuilder casts) {
@@ -254,8 +255,7 @@ public class HiddenInternalFunctions {
                 RSerialize.CallHook callHook = new RSerialize.CallHook() {
                     @Override
                     public Object eval(Object arg) {
-                        Object[] callArgs = RArguments.create(envhook, RCaller.create(frame, getOriginalCall()), null, new Object[]{arg}, null);
-                        return callCache.execute(SubstituteVirtualFrame.create(frame), envhook.getTarget(), callArgs);
+                        return callCache.execute(SubstituteVirtualFrame.create(frame), envhook, RCaller.create(frame, getOriginalCall()), null, new Object[]{arg}, null);
                     }
                 };
                 String functionName = ReadVariableNode.getSlowPathEvaluationName();
@@ -356,7 +356,7 @@ public class HiddenInternalFunctions {
     @RBuiltin(name = "lazyLoadDBinsertValue", kind = INTERNAL, parameterNames = {"value", "file", "ascii", "compsxp", "hook"}, behavior = COMPLEX)
     public abstract static class LazyLoadDBinsertValue extends RBuiltinNode {
 
-        @Child private CallInlineCacheNode callCache = CallInlineCacheNodeGen.create();
+        @Child private CallRFunctionCachedNode callCache = CallRFunctionCachedNodeGen.create(2);
 
         @Override
         protected void createCasts(CastBuilder casts) {
@@ -378,8 +378,7 @@ public class HiddenInternalFunctions {
             RSerialize.CallHook callHook = new RSerialize.CallHook() {
                 @Override
                 public Object eval(Object arg) {
-                    Object[] callArgs = RArguments.create(hook, RCaller.create(frame, getOriginalCall()), null, new Object[]{arg}, null);
-                    return callCache.execute(SubstituteVirtualFrame.create(frame), hook.getTarget(), callArgs);
+                    return callCache.execute(SubstituteVirtualFrame.create(frame), hook, RCaller.create(frame, getOriginalCall()), null, new Object[]{arg}, null);
                 }
             };
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OptionsFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OptionsFunctions.java
index 3a6bc80e944456c0f3c0a92a392589bb391cdd28..e07c62fc6864be8d98a9b1f85987947533f8dcfc 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OptionsFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OptionsFunctions.java
@@ -34,9 +34,11 @@ import java.util.Set;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
@@ -62,6 +64,8 @@ public class OptionsFunctions {
     @RBuiltin(name = "options", visibility = CUSTOM, kind = INTERNAL, parameterNames = {"..."}, behavior = MODIFIES_STATE)
     public abstract static class Options extends RBuiltinNode {
 
+        @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
+
         private final ConditionProfile argNameNull = ConditionProfile.createBinaryProfile();
         private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
@@ -85,81 +89,104 @@ public class OptionsFunctions {
             return options(RMissing.instance);
         }
 
+        private static final class InvisibleResult extends Throwable {
+            private static final long serialVersionUID = 5767688593421435507L;
+
+            private final RList value;
+
+            protected InvisibleResult(RList value) {
+                this.value = value;
+            }
+        }
+
         @Specialization(guards = "!isMissing(args)")
-        @TruffleBoundary
-        protected Object options(RArgsValuesAndNames args) {
+        protected Object options(VirtualFrame frame, RArgsValuesAndNames args) {
             try {
-                ROptions.ContextStateImpl options = RContext.getInstance().stateROptions;
-                Object[] values = args.getArguments();
-                ArgumentsSignature signature = args.getSignature();
-                Object[] data = new Object[values.length];
-                String[] names = new String[values.length];
-                for (int i = 0; i < values.length; i++) {
-                    String argName = signature.getName(i);
-                    Object value = values[i];
-                    if (argNameNull.profile(argName == null)) {
-                        // getting
-                        String optionName = null;
-                        if (value instanceof RStringVector) {
-                            // ignore rest (cf GnuR)
-                            optionName = ((RStringVector) value).getDataAt(0);
-                        } else if (value instanceof String) {
-                            optionName = (String) value;
-                        } else if (value instanceof RList) {
-                            // setting
-                            RList list = (RList) value;
-                            RStringVector thisListnames = null;
-                            Object nn = list.getNames(attrProfiles);
-                            if (nn instanceof RStringVector) {
-                                thisListnames = (RStringVector) nn;
-                            } else {
-                                throw RInternalError.shouldNotReachHere();
-                            }
-                            Object[] listData = new Object[list.getLength()];
-                            String[] listNames = new String[listData.length];
-                            for (int j = 0; j < listData.length; j++) {
-                                String name = thisListnames.getDataAt(j);
-                                Object previousVal = options.getValue(name);
-                                listData[j] = previousVal == null ? RNull.instance : previousVal;
-                                listNames[j] = name;
-                                options.setValue(name, list.getDataAtAsObject(j));
-                            }
-                            // if this is the only argument, no need to copy, can just return
-                            if (values.length == 1) {
-                                data = listData;
-                                names = listNames;
-                                break;
-                            } else {
-                                // resize and copy
-                                int newSize = values.length - 1 + listData.length;
-                                Object[] newData = new Object[newSize];
-                                String[] newNames = new String[newSize];
-                                System.arraycopy(data, 0, newData, 0, i);
-                                System.arraycopy(names, 0, newNames, 0, i);
-                                System.arraycopy(listData, 0, newData, i, listData.length);
-                                System.arraycopy(listNames, 0, newNames, i, listNames.length);
-                                data = newData;
-                                names = newNames;
-                            }
+                return optionsInternal(args);
+            } catch (OptionsException ex) {
+                throw RError.error(this, ex);
+            } catch (InvisibleResult ex) {
+                visibility.execute(frame, false);
+                return ex.value;
+            }
+        }
+
+        @TruffleBoundary
+        private Object optionsInternal(RArgsValuesAndNames args) throws OptionsException, InvisibleResult {
+            boolean invisible = false;
+            ROptions.ContextStateImpl options = RContext.getInstance().stateROptions;
+            Object[] values = args.getArguments();
+            ArgumentsSignature signature = args.getSignature();
+            Object[] data = new Object[values.length];
+            String[] names = new String[values.length];
+            for (int i = 0; i < values.length; i++) {
+                String argName = signature.getName(i);
+                Object value = values[i];
+                if (argNameNull.profile(argName == null)) {
+                    // getting
+                    String optionName = null;
+                    if (value instanceof RStringVector) {
+                        // ignore rest (cf GnuR)
+                        optionName = ((RStringVector) value).getDataAt(0);
+                    } else if (value instanceof String) {
+                        optionName = (String) value;
+                    } else if (value instanceof RList) {
+                        // setting
+                        RList list = (RList) value;
+                        RStringVector thisListnames = null;
+                        Object nn = list.getNames(attrProfiles);
+                        if (nn instanceof RStringVector) {
+                            thisListnames = (RStringVector) nn;
                         } else {
-                            throw RError.error(this, Message.INVALID_UNNAMED_ARGUMENT);
+                            throw RInternalError.shouldNotReachHere();
+                        }
+                        Object[] listData = new Object[list.getLength()];
+                        String[] listNames = new String[listData.length];
+                        for (int j = 0; j < listData.length; j++) {
+                            String name = thisListnames.getDataAt(j);
+                            Object previousVal = options.getValue(name);
+                            listData[j] = previousVal == null ? RNull.instance : previousVal;
+                            listNames[j] = name;
+                            options.setValue(name, list.getDataAtAsObject(j));
+                        }
+                        // if this is the only argument, no need to copy, can just return
+                        if (values.length == 1) {
+                            data = listData;
+                            names = listNames;
+                            break;
+                        } else {
+                            // resize and copy
+                            int newSize = values.length - 1 + listData.length;
+                            Object[] newData = new Object[newSize];
+                            String[] newNames = new String[newSize];
+                            System.arraycopy(data, 0, newData, 0, i);
+                            System.arraycopy(names, 0, newNames, 0, i);
+                            System.arraycopy(listData, 0, newData, i, listData.length);
+                            System.arraycopy(listNames, 0, newNames, i, listNames.length);
+                            data = newData;
+                            names = newNames;
                         }
-                        Object optionVal = options.getValue(optionName);
-                        data[i] = optionVal == null ? RNull.instance : optionVal;
-                        names[i] = optionName;
                     } else {
-                        // setting
-                        Object previousVal = options.getValue(argName);
-                        data[i] = previousVal == null ? RNull.instance : previousVal;
-                        names[i] = argName;
-                        options.setValue(argName, value);
-                        // any settings means result is invisible
-                        RContext.getInstance().setVisible(false);
+                        throw RError.error(this, Message.INVALID_UNNAMED_ARGUMENT);
                     }
+                    Object optionVal = options.getValue(optionName);
+                    data[i] = optionVal == null ? RNull.instance : optionVal;
+                    names[i] = optionName;
+                } else {
+                    // setting
+                    Object previousVal = options.getValue(argName);
+                    data[i] = previousVal == null ? RNull.instance : previousVal;
+                    names[i] = argName;
+                    options.setValue(argName, value);
+                    // any settings means result is invisible
+                    invisible = true;
                 }
-                return RDataFactory.createList(data, RDataFactory.createStringVector(names, RDataFactory.COMPLETE_VECTOR));
-            } catch (OptionsException ex) {
-                throw RError.error(this, ex);
+            }
+            RList result = RDataFactory.createList(data, RDataFactory.createStringVector(names, RDataFactory.COMPLETE_VECTOR));
+            if (invisible) {
+                throw new InvisibleResult(result);
+            } else {
+                return result;
             }
         }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Switch.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Switch.java
index a787025f2617d7a6391e07ee7ebdecd662bc5ad3..c41df4be78fcdfafbb4cb032d0a9a36ee8effe05 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Switch.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Switch.java
@@ -23,13 +23,13 @@ import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseCheckHelperNode;
 import com.oracle.truffle.r.nodes.function.RMissingHelper;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNodeGen;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -46,8 +46,10 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
  */
 @RBuiltin(name = "switch", visibility = CUSTOM, kind = PRIMITIVE, parameterNames = {"EXPR", "..."}, nonEvalArgs = 1, behavior = COMPLEX)
 public abstract class Switch extends RBuiltinNode {
+
     @Child private CastIntegerNode castIntNode;
     @Child private PromiseCheckHelperNode promiseHelper = new PromiseCheckHelperNode();
+    @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
 
     private final BranchProfile suppliedArgNameIsEmpty = BranchProfile.create();
     private final BranchProfile suppliedArgNameIsNull = BranchProfile.create();
@@ -61,7 +63,7 @@ public abstract class Switch extends RBuiltinNode {
         if (x.getLength() != 1) {
             throw RError.error(this, RError.Message.EXPR_NOT_LENGTH_ONE);
         }
-        return prepareResult(doSwitchString(frame, x, optionalArgs));
+        return prepareResult(frame, doSwitchString(frame, x, optionalArgs));
     }
 
     private Object doSwitchString(VirtualFrame frame, RAbstractStringVector x, RArgsValuesAndNames optionalArgs) {
@@ -130,7 +132,7 @@ public abstract class Switch extends RBuiltinNode {
 
     @Specialization
     protected Object doSwitch(VirtualFrame frame, int x, RArgsValuesAndNames optionalArgs) {
-        return prepareResult(doSwitchInt(frame, x, optionalArgs));
+        return prepareResult(frame, doSwitchInt(frame, x, optionalArgs));
     }
 
     @Specialization
@@ -144,7 +146,7 @@ public abstract class Switch extends RBuiltinNode {
             notIntType.enter();
             return null;
         }
-        return prepareResult(doSwitchInt(frame, (int) objIndex, optionalArgs));
+        return prepareResult(frame, doSwitchInt(frame, (int) objIndex, optionalArgs));
     }
 
     @SuppressWarnings("unused")
@@ -166,12 +168,12 @@ public abstract class Switch extends RBuiltinNode {
         return null;
     }
 
-    private Object prepareResult(Object value) {
+    private Object prepareResult(VirtualFrame frame, Object value) {
         if (returnValueProfile.profile(value != null)) {
-            RContext.getInstance().setVisible(true);
+            visibility.execute(frame, true);
             return value;
         } else {
-            RContext.getInstance().setVisible(false);
+            visibility.execute(frame, false);
             return RNull.instance;
         }
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TraceFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TraceFunctions.java
index 2e5c2fa33b531583efbed165f3775d95bc0f21ad..3d86ab3e90c3fa99372e58729130d0555f52e82c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TraceFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TraceFunctions.java
@@ -45,6 +45,7 @@ import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.GetFunctionsFactory.GetNodeGen;
 import com.oracle.truffle.r.nodes.builtin.helpers.TraceHandling;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RError;
@@ -227,25 +228,28 @@ public class TraceFunctions {
      */
     @RBuiltin(name = "retracemem", kind = PRIMITIVE, visibility = CUSTOM, parameterNames = {"x", "previous"}, behavior = COMPLEX)
     public abstract static class Retracemem extends TracememBase {
+
+        @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
+
         @Override
         protected void createCasts(CastBuilder casts) {
             casts.arg("previous").defaultError(Message.INVALID_ARGUMENT, "previous").mustBe(stringValue().or(missingValue()));
         }
 
         @Specialization
-        protected Object execute(Object x, @SuppressWarnings("unused") RNull previous) {
-            return getResult(x);
+        protected Object execute(VirtualFrame frame, Object x, @SuppressWarnings("unused") RNull previous) {
+            return getResult(frame, x);
         }
 
         @Specialization
-        protected Object execute(Object x, @SuppressWarnings("unused") RMissing previous) {
-            return getResult(x);
+        protected Object execute(VirtualFrame frame, Object x, @SuppressWarnings("unused") RMissing previous) {
+            return getResult(frame, x);
         }
 
         @Specialization
-        @TruffleBoundary
-        protected Object execute(Object x, String previous) {
-            Object result = getResult(x);
+        protected Object execute(VirtualFrame frame, Object x, String previous) {
+            CompilerDirectives.transferToInterpreter();
+            Object result = getResult(frame, x);
             if (x != null && x != RNull.instance) {
                 startTracing(x);
                 printToStdout(String.format("tracemem[%s -> 0x%x]: %s", previous, x.hashCode(), getStackTrace()));
@@ -253,13 +257,12 @@ public class TraceFunctions {
             return result;
         }
 
-        @TruffleBoundary
-        private static Object getResult(Object x) {
+        private Object getResult(VirtualFrame frame, Object x) {
             if (!isRNull(x) && getTracedObjects().contains(x)) {
-                RContext.getInstance().setVisible(true);
+                visibility.execute(frame, true);
                 return formatHashCode(x);
             } else {
-                RContext.getInstance().setVisible(false);
+                visibility.execute(frame, false);
                 return RNull.instance;
             }
         }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java
index b549fede5a4879ec25b1cc2f1f3c9e40dea1a39e..e6364e915a043a587e286376cb0922cc22152526 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java
@@ -18,11 +18,9 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.nodes.DirectCallNode;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.access.ConstantNode;
 import com.oracle.truffle.r.nodes.access.UpdateSlotNode;
@@ -33,7 +31,7 @@ import com.oracle.truffle.r.nodes.function.ClassHierarchyNode;
 import com.oracle.truffle.r.nodes.function.ClassHierarchyNodeGen;
 import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.nodes.function.WrapArgumentNode;
-import com.oracle.truffle.r.nodes.function.signature.RArgumentsNode;
+import com.oracle.truffle.r.nodes.function.call.CallRFunctionNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RError;
@@ -55,8 +53,7 @@ public abstract class UpdateSlot extends RBuiltinNode {
     @Child private ClassHierarchyNode valClassHierarchy;
     @Child private UpdateSlotNode updateSlotNode = com.oracle.truffle.r.nodes.access.UpdateSlotNodeGen.create(null, null, null);
     @Child private ReadVariableNode checkAtAssignmentFind = ReadVariableNode.createFunctionLookup(RSyntaxNode.INTERNAL, "checkAtAssignment");
-    @Child private DirectCallNode checkAtAssignmentCall;
-    @Child private RArgumentsNode argsNode = RArgumentsNode.create();
+    @Child private CallRFunctionNode checkAtAssignmentCall;
     private final ConditionProfile cached = ConditionProfile.createBinaryProfile();
 
     @Override
@@ -93,7 +90,7 @@ public abstract class UpdateSlot extends RBuiltinNode {
         if (checkSlotAssignFunction == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             checkSlotAssignFunction = (RFunction) checkAtAssignmentFind.execute(frame);
-            checkAtAssignmentCall = insert(Truffle.getRuntime().createDirectCallNode(checkSlotAssignFunction.getTarget()));
+            checkAtAssignmentCall = insert(CallRFunctionNode.create(checkSlotAssignFunction.getTarget()));
             assert objClassHierarchy == null && valClassHierarchy == null;
             objClassHierarchy = insert(ClassHierarchyNodeGen.create(true, false));
             valClassHierarchy = insert(ClassHierarchyNodeGen.create(true, false));
@@ -105,8 +102,8 @@ public abstract class UpdateSlot extends RBuiltinNode {
         if (cached.profile(currentFunction == checkSlotAssignFunction)) {
             // TODO: technically, someone could override checkAtAssignment function and access the
             // caller, but it's rather unlikely
-            Object[] args = argsNode.execute(checkSlotAssignFunction, RCaller.create(frame, getOriginalCall()), null, new Object[]{objClass, name, valClass}, SIGNATURE, null);
-            checkAtAssignmentCall.call(frame, args);
+            checkAtAssignmentCall.execute(frame, checkSlotAssignFunction, RCaller.create(frame, getOriginalCall()), null, new Object[]{objClass, name, valClass}, SIGNATURE,
+                            checkSlotAssignFunction.getEnclosingFrame(), null);
         } else {
             // slow path
             RContext.getEngine().evalFunction(currentFunction, frame.materialize(), RCaller.create(frame, getOriginalCall()), null, objClass, name, valClass);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WithVisible.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WithVisible.java
index ac1c17063144512e320092bfa7a4193bdf381e4f..8146e8d738e69121fe5a4f05d33c88a5bd2534ab 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WithVisible.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WithVisible.java
@@ -22,18 +22,20 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.missingValue;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.function.visibility.GetVisibilityNode;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RStringVector;
@@ -44,7 +46,8 @@ import com.oracle.truffle.r.runtime.data.RStringVector;
 // set noEvalArgs and evaluate the argument here and set the visibility explicitly.
 @RBuiltin(name = "withVisible", kind = PRIMITIVE, parameterNames = "x", behavior = COMPLEX)
 public abstract class WithVisible extends RBuiltinNode {
-    private static final RStringVector LISTNAMES = RDataFactory.createStringVector(new String[]{"value", "visible"}, RDataFactory.COMPLETE_VECTOR);
+
+    private static final RStringVector LISTNAMES = (RStringVector) RDataFactory.createStringVector(new String[]{"value", "visible"}, RDataFactory.COMPLETE_VECTOR).makeSharedPermanent();
 
     @Override
     protected void createCasts(CastBuilder casts) {
@@ -52,13 +55,13 @@ public abstract class WithVisible extends RBuiltinNode {
     }
 
     @Specialization
-    protected RList withVisible(Object x) {
+    protected RList withVisible(VirtualFrame frame, Object x,
+                    @Cached("create()") GetVisibilityNode visibility) {
         if (FastROptions.IgnoreVisibility.getBooleanValue()) {
             RError.warning(this, RError.Message.GENERIC, "using withVisible with IgnoreVisibility");
         }
 
-        Object[] data = new Object[]{x, RRuntime.asLogical(RContext.getInstance().isVisible())};
+        Object[] data = new Object[]{x, RRuntime.asLogical(visibility.execute(frame))};
         return RDataFactory.createList(data, LISTNAMES);
     }
-
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/fastpaths/IntersectFastPath.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/fastpaths/IntersectFastPath.java
index 266a77c09afda2c9ae1f0b2bbc0f60061aec8be8..e0f1161c1d5415edda8156fef193ab9269f7bafa 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/fastpaths/IntersectFastPath.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/fastpaths/IntersectFastPath.java
@@ -32,6 +32,7 @@ import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.nodes.RFastPathNode;
+import com.oracle.truffle.r.runtime.nodes.RNode;
 
 public abstract class IntersectFastPath extends RFastPathNode {
 
@@ -44,7 +45,7 @@ public abstract class IntersectFastPath extends RFastPathNode {
                     @Cached("createBinaryProfile()") ConditionProfile resultLengthMatchProfile) {
         int xLength = x.getLength();
         int yLength = y.getLength();
-        reportWork(xLength + yLength);
+        RNode.reportWork(this, xLength + yLength);
 
         int count = 0;
         int[] result = EMPTY_INT_ARRAY;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/fastpaths/IsElementFastPath.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/fastpaths/IsElementFastPath.java
index 19e6b1b609cf8b61fd8bb25aaa6198178b6a180b..63a1876788b7fe1d3456d3f6d7febe0c2bf550f5 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/fastpaths/IsElementFastPath.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/fastpaths/IsElementFastPath.java
@@ -28,7 +28,6 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RIntSequence;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
@@ -42,7 +41,6 @@ public abstract class IsElementFastPath extends RFastPathNode {
     protected Byte iselementOne(RAbstractStringVector el, RAbstractStringVector set, //
                     @Cached("create()") BranchProfile trueProfile, //
                     @Cached("create()") BranchProfile falseProfile) {
-        RContext.getInstance().setVisible(true);
         String element = el.getDataAt(0);
         int length = set.getLength();
         for (int i = 0; i < length; i++) {
@@ -57,7 +55,6 @@ public abstract class IsElementFastPath extends RFastPathNode {
 
     @Specialization
     protected Byte iselementOne(double el, double set) {
-        RContext.getInstance().setVisible(true);
         return RRuntime.asLogical(el == set);
     }
 
@@ -65,7 +62,6 @@ public abstract class IsElementFastPath extends RFastPathNode {
     protected Byte iselementOne(RAbstractDoubleVector el, RAbstractDoubleVector set, //
                     @Cached("create()") BranchProfile trueProfile, //
                     @Cached("create()") BranchProfile falseProfile) {
-        RContext.getInstance().setVisible(true);
         double element = el.getDataAt(0);
         int length = set.getLength();
         for (int i = 0; i < length; i++) {
@@ -81,7 +77,6 @@ public abstract class IsElementFastPath extends RFastPathNode {
     @Specialization(guards = "el.getLength() == 1")
     protected Byte isElementOneSequence(RAbstractDoubleVector el, RIntSequence set, //
                     @Cached("createBinaryProfile()") ConditionProfile profile) {
-        RContext.getInstance().setVisible(true);
         double element = el.getDataAt(0);
         return RRuntime.asLogical(profile.profile(element >= set.getStart() && element <= set.getEnd()));
     }
@@ -91,7 +86,6 @@ public abstract class IsElementFastPath extends RFastPathNode {
                     @Cached("create()") NACheck na, //
                     @Cached("create()") BranchProfile trueProfile, //
                     @Cached("create()") BranchProfile falseProfile) {
-        RContext.getInstance().setVisible(true);
         double element = el.getDataAt(0);
         int length = set.getLength();
         na.enable(set);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/ProcessSystemFunctionFactory.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/ProcessSystemFunctionFactory.java
index 9be9ca380beaadf2f9c20a535849aadaeeb19a91..d0812bf2d2bd663a8aee3497d2693f17540faeac 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/ProcessSystemFunctionFactory.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/ProcessSystemFunctionFactory.java
@@ -88,7 +88,6 @@ public class ProcessSystemFunctionFactory extends SystemFunctionFactory {
         } catch (InterruptedException | IOException ex) {
             result = 127;
         }
-        RContext.getInstance().setVisible(false);
         return result;
     }
 
@@ -103,5 +102,4 @@ public class ProcessSystemFunctionFactory extends SystemFunctionFactory {
             }
         }
     }
-
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/SystemFunction.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/SystemFunction.java
index 2b4d464807199ebf0eaf6898ce640aea8f900333..237680183ca9549ace6ee66251443453c425c694 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/SystemFunction.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/SystemFunction.java
@@ -24,7 +24,7 @@ package com.oracle.truffle.r.nodes.builtin.base.system;
 
 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.RVisibility.OFF;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
@@ -35,7 +35,7 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 
-@RBuiltin(name = "system", visibility = CUSTOM, kind = INTERNAL, parameterNames = {"command", "intern"}, behavior = COMPLEX)
+@RBuiltin(name = "system", visibility = OFF, kind = INTERNAL, parameterNames = {"command", "intern"}, behavior = COMPLEX)
 public abstract class SystemFunction extends RBuiltinNode {
     @Override
     protected void createCasts(CastBuilder casts) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTrace.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTrace.java
index 470f42fd0914c3dffa37ffd4c107dbf93857b17c..09ce4a2d89bc135a7f791928fabeda2656693483 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTrace.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTrace.java
@@ -43,6 +43,7 @@ import com.oracle.truffle.r.nodes.builtin.base.TraceFunctions;
 import com.oracle.truffle.r.nodes.builtin.base.TraceFunctionsFactory.PrimTraceNodeGen;
 import com.oracle.truffle.r.nodes.builtin.base.TraceFunctionsFactory.PrimUnTraceNodeGen;
 import com.oracle.truffle.r.nodes.builtin.helpers.TraceHandling;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.nodes.unary.CastLogicalNode;
 import com.oracle.truffle.r.nodes.unary.CastLogicalNodeGen;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
@@ -50,7 +51,6 @@ import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RLanguage;
@@ -114,6 +114,7 @@ public class FastRTrace {
 
         @Child private TraceFunctions.PrimTrace primTrace;
         @Child private CastLogicalNode castLogical;
+        @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
 
         @Specialization
         protected Object trace(VirtualFrame frame, Object whatObj, Object tracer, Object exit, Object at, Object printObj, Object signature, Object whereObj) {
@@ -137,7 +138,7 @@ public class FastRTrace {
                 }
 
                 Object result = primTrace.execute(frame, func);
-                RContext.getInstance().setVisible(false);
+                visibility.execute(frame, false);
                 return result;
             }
 
@@ -153,7 +154,7 @@ public class FastRTrace {
                 print = RRuntime.fromLogical((byte) castLogical.execute(printObj));
             }
             complexCase(func, tracer, exit, at, print, signature);
-            RContext.getInstance().setVisible(true);
+            visibility.execute(frame, true);
             return func.toString();
         }
 
@@ -181,7 +182,6 @@ public class FastRTrace {
 
         @Specialization
         protected Object untrace(VirtualFrame frame, Object whatObj, Object signature, Object whereObj) {
-            RContext.getInstance().setVisible(false);
             Object what = whatObj;
             checkWhat(what);
             Object where = whereObj;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/CallInlineCacheNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/CallInlineCacheNode.java
deleted file mode 100644
index 6bffe4ce0c9fc7181d61d8afcdc0ff94efbab026..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/CallInlineCacheNode.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2014, 2016, 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;
-
-import com.oracle.truffle.api.CallTarget;
-import com.oracle.truffle.api.Truffle;
-import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.nodes.DirectCallNode;
-import com.oracle.truffle.api.nodes.IndirectCallNode;
-import com.oracle.truffle.r.runtime.nodes.RBaseNode;
-
-/**
- * This node reifies a runtime object into the AST by creating nodes for frequently encountered
- * values. This can be used to bridge the gap between code as runtime data and executed code.
- */
-public abstract class CallInlineCacheNode extends RBaseNode {
-
-    protected static final int CACHE_LIMIT = 2;
-
-    public abstract Object execute(VirtualFrame frame, CallTarget target, Object[] arguments);
-
-    protected static DirectCallNode createDirectCallNode(CallTarget target) {
-        return Truffle.getRuntime().createDirectCallNode(target);
-    }
-
-    @Specialization(guards = "target == callNode.getCallTarget()", limit = "CACHE_LIMIT")
-    protected Object call(VirtualFrame frame, @SuppressWarnings("unused") CallTarget target, Object[] arguments, @Cached("createDirectCallNode(target)") DirectCallNode callNode) {
-        return callNode.call(frame, arguments);
-    }
-
-    protected static IndirectCallNode createIndirectCallNode() {
-        return Truffle.getRuntime().createIndirectCallNode();
-    }
-
-    @Specialization
-    protected Object call(VirtualFrame frame, CallTarget target, Object[] arguments, @Cached("createIndirectCallNode()") IndirectCallNode callNode) {
-        return callNode.call(frame, target, arguments);
-    }
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.java
index 9c7b5454cdc538f3551c5189d26152b33ec2b7cb..deb90eb6641880a7f0b5834f9f7ebd219453608d 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.java
@@ -50,9 +50,10 @@ public abstract class AccessSlotNode extends RNode {
 
     public abstract Object executeAccess(Object o, String name);
 
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
     @Child private ClassHierarchyNode classHierarchy;
     @Child private TypeofNode typeofNode;
+
+    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
     private final BranchProfile noSlot = BranchProfile.create();
     private final BranchProfile symbolValue = BranchProfile.create();
     private final boolean asOperator;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ConstantNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ConstantNode.java
index e7b17317c98683c229cd58afcb8d281a9b34f8c7..d9d3c1c69ac25b7d6d66ef876a35b4a37152f71c 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ConstantNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ConstantNode.java
@@ -25,9 +25,9 @@ package com.oracle.truffle.r.nodes.access;
 import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSerialize;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RPromise;
@@ -40,6 +40,8 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
 public abstract class ConstantNode extends RSourceSectionNode implements RSyntaxNode, RSyntaxConstant {
 
+    @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
+
     private ConstantNode(SourceSection sourceSection) {
         super(sourceSection);
     }
@@ -59,9 +61,13 @@ public abstract class ConstantNode extends RSourceSectionNode implements RSyntax
     @Override
     public abstract Object getValue();
 
+    protected final void handleVisibility(VirtualFrame frame) {
+        visibility.execute(frame, true);
+    }
+
     @Override
     public final Object execute(VirtualFrame frame) {
-        RContext.getInstance().setVisible(true);
+        handleVisibility(frame);
         return getValue();
     }
 
@@ -113,7 +119,7 @@ public abstract class ConstantNode extends RSourceSectionNode implements RSyntax
 
         @Override
         public double executeDouble(VirtualFrame frame) {
-            RContext.getInstance().setVisible(true);
+            handleVisibility(frame);
             return doubleValue;
         }
     }
@@ -136,7 +142,7 @@ public abstract class ConstantNode extends RSourceSectionNode implements RSyntax
 
         @Override
         public byte executeByte(VirtualFrame frame) {
-            RContext.getInstance().setVisible(true);
+            handleVisibility(frame);
             return logicalValue;
         }
     }
@@ -159,7 +165,7 @@ public abstract class ConstantNode extends RSourceSectionNode implements RSyntax
 
         @Override
         public int executeInteger(VirtualFrame frame) {
-            RContext.getInstance().setVisible(true);
+            handleVisibility(frame);
             return intValue;
         }
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariadicComponentNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariadicComponentNode.java
index 363ba30ac7b19966a77b9d45ae642540e4b737a4..d8702da5ed28f7c372df37b4deae5bf59fd615fe 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariadicComponentNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariadicComponentNode.java
@@ -22,20 +22,20 @@
  */
 package com.oracle.truffle.r.nodes.access;
 
-import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
-import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
+import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseCheckHelperNode;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RSerialize.State;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RMissing;
-import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 import com.oracle.truffle.r.runtime.nodes.RSourceSectionNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
@@ -47,21 +47,23 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 public class ReadVariadicComponentNode extends RSourceSectionNode implements RSyntaxNode, RSyntaxLookup {
 
     @Child private ReadVariableNode lookup = ReadVariableNode.createSilent(ArgumentsSignature.VARARG_NAME, RType.Any);
-    @Child private PromiseHelperNode promiseHelper;
+    @Child private PromiseCheckHelperNode promiseHelper = new PromiseCheckHelperNode();
+    @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
 
     private final int index;
+    private final String name;
 
     private final BranchProfile errorBranch = BranchProfile.create();
-    private final BranchProfile promiseBranch = BranchProfile.create();
 
     public ReadVariadicComponentNode(SourceSection src, int index) {
         super(src);
         this.index = index;
+        this.name = Utils.intern(".." + Integer.toString(index + 1));
     }
 
     @Override
     public Object execute(VirtualFrame frame) {
-        RContext.getInstance().setVisible(true);
+        visibility.execute(frame, true);
 
         Object args = lookup.execute(frame);
         if (args == null) {
@@ -73,37 +75,26 @@ public class ReadVariadicComponentNode extends RSourceSectionNode implements RSy
             errorBranch.enter();
             throw RError.error(this, RError.Message.NO_LIST_FOR_CDR);
         }
-
         if (argsValuesAndNames.getLength() <= index) {
             errorBranch.enter();
             throw RError.error(this, RError.Message.DOT_DOT_SHORT, index + 1);
         }
         Object ret = argsValuesAndNames.getArgument(index);
-        if (ret instanceof RPromise) {
-            promiseBranch.enter();
-            // This might be the case, as lookup only checks for "..." to be a promise and forces it
-            // eventually, NOT (all) of its content
-            if (promiseHelper == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                promiseHelper = insert(new PromiseHelperNode());
-            }
-            ret = promiseHelper.evaluate(frame, (RPromise) ret);
-        }
-        return ret == null ? RMissing.instance : ret;
+        return ret == null ? RMissing.instance : promiseHelper.checkEvaluate(frame, ret);
     }
 
     public String getPrintForm() {
-        return ".." + Integer.toString(index + 1);
+        return name;
     }
 
     @Override
-    public void serializeImpl(com.oracle.truffle.r.runtime.RSerialize.State state) {
+    public void serializeImpl(State state) {
         state.setCarAsSymbol(getPrintForm());
     }
 
     @Override
     public String getIdentifier() {
-        return Utils.intern(getPrintForm());
+        return name;
     }
 
     @Override
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/RemoveAndAnswerNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/RemoveAndAnswerNode.java
index 28c07e510dce1781491d79502e3aefa728faa032..3a66dc1427fa8880bbf7e228ca667b1631edbb14 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/RemoveAndAnswerNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/RemoveAndAnswerNode.java
@@ -31,7 +31,6 @@ import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.nodes.access.RemoveAndAnswerNodeFactory.RemoveAndAnswerResolvedNodeGen;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 
@@ -103,7 +102,6 @@ public abstract class RemoveAndAnswerNode extends RNode {
 
         @Specialization(guards = "isObject(frame)")
         protected Object doObject(VirtualFrame frame) {
-            RContext.getInstance().setVisible(false);
             Object result;
             try {
                 result = frame.getObject(slot);
@@ -118,7 +116,6 @@ public abstract class RemoveAndAnswerNode extends RNode {
 
         @Specialization(guards = "isInt(frame)")
         protected int doInt(VirtualFrame frame) {
-            RContext.getInstance().setVisible(false);
             int result;
             try {
                 result = frame.getInt(slot);
@@ -133,7 +130,6 @@ public abstract class RemoveAndAnswerNode extends RNode {
 
         @Specialization(guards = "isDouble(frame)")
         protected double doDouble(VirtualFrame frame) {
-            RContext.getInstance().setVisible(false);
             double result;
             try {
                 result = frame.getDouble(slot);
@@ -148,7 +144,6 @@ public abstract class RemoveAndAnswerNode extends RNode {
 
         @Specialization(guards = "isByte(frame)")
         protected byte doByte(VirtualFrame frame) {
-            RContext.getInstance().setVisible(false);
             byte result;
             try {
                 result = frame.getByte(slot);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteCurrentVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteCurrentVariableNode.java
index f7dbb258511d99587e6636f7fad2ab9f7a927dc2..f8998f171c9ad1e5399f7365b06e8260ccb8753c 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteCurrentVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteCurrentVariableNode.java
@@ -26,9 +26,9 @@ import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RSerialize;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxCall;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
@@ -40,16 +40,17 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
  */
 @NodeInfo(cost = NodeCost.NONE)
 public class WriteCurrentVariableNode extends WriteVariableNodeSyntaxHelper implements RSyntaxNode, RSyntaxCall {
+
     @Child private WriteLocalFrameVariableNode writeLocalFrameVariableNode;
+    @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
 
-    protected WriteCurrentVariableNode(SourceSection src) {
+    protected WriteCurrentVariableNode(SourceSection src, String name, RNode rhs) {
         super(src);
+        writeLocalFrameVariableNode = WriteLocalFrameVariableNode.create(name, rhs, Mode.REGULAR);
     }
 
     static WriteCurrentVariableNode create(SourceSection src, String name, RNode rhs) {
-        WriteCurrentVariableNode result = new WriteCurrentVariableNode(src);
-        result.writeLocalFrameVariableNode = result.insert(WriteLocalFrameVariableNode.create(name, rhs, Mode.REGULAR));
-        return result;
+        return new WriteCurrentVariableNode(src, name, rhs);
     }
 
     @Override
@@ -65,7 +66,7 @@ public class WriteCurrentVariableNode extends WriteVariableNodeSyntaxHelper impl
     @Override
     public Object execute(VirtualFrame frame) {
         Object result = writeLocalFrameVariableNode.execute(frame);
-        RContext.getInstance().setVisible(false);
+        visibility.execute(frame, false);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteSuperVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteSuperVariableNode.java
index d79875a674117af26fed7c4b062dd65b1bc197bb..a916a6ac23092e1446c26bca9ddf9592ebc02d76 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteSuperVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteSuperVariableNode.java
@@ -26,9 +26,9 @@ import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RSerialize;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 import com.oracle.truffle.r.runtime.nodes.RSourceSectionNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxCall;
@@ -47,15 +47,15 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 public class WriteSuperVariableNode extends WriteVariableNodeSyntaxHelper implements RSyntaxNode, RSyntaxCall {
 
     @Child private WriteVariableNode writeSuperFrameVariableNode;
+    @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
 
-    protected WriteSuperVariableNode(SourceSection src) {
+    protected WriteSuperVariableNode(SourceSection src, String name, RNode rhs) {
         super(src);
+        writeSuperFrameVariableNode = WriteSuperFrameVariableNode.create(name, rhs, Mode.REGULAR);
     }
 
     static WriteSuperVariableNode create(SourceSection src, String name, RNode rhs) {
-        WriteSuperVariableNode result = new WriteSuperVariableNode(src);
-        result.writeSuperFrameVariableNode = result.insert(WriteSuperFrameVariableNode.create(name, rhs, Mode.REGULAR));
-        return result;
+        return new WriteSuperVariableNode(src, name, rhs);
     }
 
     @Override
@@ -71,7 +71,7 @@ public class WriteSuperVariableNode extends WriteVariableNodeSyntaxHelper implem
     @Override
     public Object execute(VirtualFrame frame) {
         Object result = writeSuperFrameVariableNode.execute(frame);
-        RContext.getInstance().setVisible(false);
+        visibility.execute(frame, false);
         return result;
     }
 
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 d13c16f36760f10aa59843d2e091ff4c679dddc8..0482836c0119317f7a47d9ff631e6ffc8a3058d8 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
@@ -48,6 +48,7 @@ import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RArguments;
@@ -57,7 +58,6 @@ import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSerialize;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.StableValue;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RMissing;
@@ -130,6 +130,8 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
 
     @Child private PromiseHelperNode promiseHelper;
     @Child private CheckTypeNode checkTypeNode;
+    @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
+
     @CompilationFinal private FrameLevel read;
     @CompilationFinal private boolean needsCopying;
 
@@ -187,7 +189,7 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
 
     private Object executeInternal(VirtualFrame frame, Frame variableFrame) {
         if (kind != ReadKind.Silent) {
-            RContext.getInstance().setVisible(true);
+            visibility.execute(frame, true);
         }
 
         Object result;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinRootNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinRootNode.java
index 12b61f2bc13a85ee86ec411a2b0316c59d33656b..a4f5ca52693fb66098def049f9e8d548f3c5b092 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinRootNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinRootNode.java
@@ -31,8 +31,9 @@ import com.oracle.truffle.r.nodes.RRootNode;
 import com.oracle.truffle.r.nodes.function.FormalArguments;
 import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RVisibility;
 import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
@@ -40,6 +41,7 @@ import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 public final class RBuiltinRootNode extends RRootNode {
 
     @Child private RBuiltinNode builtin;
+
     private final RBuiltinFactory factory;
 
     RBuiltinRootNode(RBuiltinFactory factory, RBuiltinNode builtin, FormalArguments formalArguments, FrameDescriptor frameDescriptor, FastPathFactory fastPath) {
@@ -74,8 +76,12 @@ public final class RBuiltinRootNode extends RRootNode {
             CompilerDirectives.transferToInterpreter();
             throw new RInternalError(e, "internal error");
         }
+        if (factory.getVisibility() == RVisibility.OFF) {
+            RContext.getInstance().setVisible(false);
+        } else if (factory.getVisibility() == RVisibility.ON) {
+            RContext.getInstance().setVisible(true);
+        }
 
-        RContext.getInstance().setVisible(factory.getVisibility());
         return result;
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BlockNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BlockNode.java
index 0adccc729521bcd0f6769db5810f256da7bc444d..9d9cee87c0faf3c20802d0c936175490f9a4d542 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BlockNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BlockNode.java
@@ -26,9 +26,9 @@ import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.ExplodeLoop;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.nodes.RASTUtils;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RSerialize;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
 import com.oracle.truffle.r.runtime.nodes.RNode;
@@ -46,6 +46,7 @@ public final class BlockNode extends RSourceSectionNode implements RSyntaxNode,
     public static final RNode[] EMPTY_BLOCK = new RNode[0];
 
     @Children protected final RNode[] sequence;
+    @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
 
     public BlockNode(SourceSection src, RNode[] sequence) {
         super(src);
@@ -59,7 +60,7 @@ public final class BlockNode extends RSourceSectionNode implements RSyntaxNode,
     @Override
     @ExplodeLoop
     public Object execute(VirtualFrame frame) {
-        RContext.getInstance().setVisible(true);
+        visibility.execute(frame, true);
         Object lastResult = RNull.instance;
         for (int i = 0; i < sequence.length; i++) {
             lastResult = sequence[i].execute(frame);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BreakNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BreakNode.java
index 3479f87c3a917a82fd38fe3e6c2a241d83e2f004..e7a5887847ac23025ef88700b03bfb62d1fbda2c 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BreakNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BreakNode.java
@@ -24,9 +24,9 @@ package com.oracle.truffle.r.nodes.control;
 
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RSerialize;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.nodes.RSourceSectionNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxCall;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
@@ -35,6 +35,8 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
 public final class BreakNode extends RSourceSectionNode implements RSyntaxNode, RSyntaxCall {
 
+    @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
+
     public BreakNode(SourceSection src) {
         super(src);
     }
@@ -46,7 +48,7 @@ public final class BreakNode extends RSourceSectionNode implements RSyntaxNode,
 
     @Override
     public Object execute(VirtualFrame frame) {
-        RContext.getInstance().setVisible(false);
+        visibility.execute(frame, false);
         throw BreakException.instance;
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java
index 2e29ed124561413cd7eadf5761589219c76e92d5..6f91c950265a89b5b3fbd3c5c5604365e55e8ce3 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java
@@ -36,10 +36,10 @@ import com.oracle.truffle.r.nodes.RRootNode;
 import com.oracle.truffle.r.nodes.access.WriteVariableNode;
 import com.oracle.truffle.r.nodes.access.WriteVariableNode.Mode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.runtime.AnonymousFrameVariable;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RSerialize;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
@@ -55,6 +55,7 @@ public final class ForNode extends AbstractLoopNode implements RSyntaxNode, RSyn
     @Child private WriteVariableNode writeIndexNode;
     @Child private WriteVariableNode writeRangeNode;
     @Child private LoopNode loopNode;
+    @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
 
     private ForNode(SourceSection src, WriteVariableNode cvar, RNode range, RNode body) {
         super(src);
@@ -79,7 +80,7 @@ public final class ForNode extends AbstractLoopNode implements RSyntaxNode, RSyn
         writeRangeNode.execute(frame);
         writeLengthNode.execute(frame);
         loopNode.executeLoop(frame);
-        RContext.getInstance().setVisible(false);
+        visibility.execute(frame, false);
         return RNull.instance;
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/IfNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/IfNode.java
index 25ece0e792df83744f8a0bd50af5943889eb1767..e36b1924649d90ee8945aefa5c7ab144a70f3201 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/IfNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/IfNode.java
@@ -26,12 +26,12 @@ import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.nodes.unary.ConvertBooleanNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSerialize;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
 import com.oracle.truffle.r.runtime.nodes.RNode;
@@ -46,6 +46,7 @@ public final class IfNode extends RSourceSectionNode implements RSyntaxNode, RSy
     @Child private ConvertBooleanNode condition;
     @Child private RNode thenPart;
     @Child private RNode elsePart;
+    @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
 
     private final ConditionProfile conditionProfile = ConditionProfile.createCountingProfile();
 
@@ -72,7 +73,7 @@ public final class IfNode extends RSourceSectionNode implements RSyntaxNode, RSy
     @Override
     public Object execute(VirtualFrame frame) {
         byte cond = condition.executeByte(frame);
-        RContext.getInstance().setVisible(elsePart != null || cond == RRuntime.LOGICAL_TRUE);
+        visibility.execute(frame, elsePart != null || cond == RRuntime.LOGICAL_TRUE);
 
         if (cond == RRuntime.LOGICAL_NA) {
             // NA is the only remaining option
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/NextNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/NextNode.java
index dab24867943b464f7f4603239998327ab405f308..514cd84e6dd25dead708e071771d3a2cfdb1097b 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/NextNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/NextNode.java
@@ -24,9 +24,9 @@ package com.oracle.truffle.r.nodes.control;
 
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RSerialize;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.nodes.RSourceSectionNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxCall;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
@@ -35,13 +35,15 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
 public final class NextNode extends RSourceSectionNode implements RSyntaxNode, RSyntaxCall {
 
+    @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
+
     public NextNode(SourceSection src) {
         super(src);
     }
 
     @Override
     public Object execute(VirtualFrame frame) {
-        RContext.getInstance().setVisible(false);
+        visibility.execute(frame, false);
         throw NextException.instance;
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java
index ab03b7d5e149f22fc2389975ac654331a7eac10e..abc79605a78c6c09ef9077e944af446251f487b7 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java
@@ -29,6 +29,7 @@ import com.oracle.truffle.api.nodes.ExplodeLoop;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.nodes.access.RemoveAndAnswerNode;
 import com.oracle.truffle.r.nodes.access.WriteVariableNode;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RSerialize;
 import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
@@ -61,6 +62,7 @@ public final class ReplacementNode extends RSourceSectionNode implements RSyntax
     @Children private final RNode[] updates;
     @Child private RemoveAndAnswerNode removeTemp;
     @Child private RemoveAndAnswerNode removeRhs;
+    @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
 
     public ReplacementNode(SourceSection src, String operator, RSyntaxNode syntaxLhs, RSyntaxNode rhs, String rhsSymbol, RNode v, String tmpSymbol, List<RNode> updates) {
         super(src);
@@ -98,7 +100,11 @@ public final class ReplacementNode extends RSourceSectionNode implements RSyntax
             update.execute(frame);
         }
         removeTemp.execute(frame);
-        return removeRhs.execute(frame);
+        try {
+            return removeRhs.execute(frame);
+        } finally {
+            visibility.execute(frame, false);
+        }
     }
 
     @Override
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/WhileNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/WhileNode.java
index 170a10ae5d85d3398f91ec1054ba4bc4b2daa325..cc1a415e0193c8e2b7fe627ced2227bd6bbbe37b 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/WhileNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/WhileNode.java
@@ -31,11 +31,11 @@ import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.nodes.RRootNode;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.nodes.unary.ConvertBooleanNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSerialize;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
@@ -48,6 +48,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 public final class WhileNode extends AbstractLoopNode implements RSyntaxNode, RSyntaxCall {
 
     @Child private LoopNode loop;
+    @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
 
     /**
      * Also used for {@code repeat}, with a {@code TRUE} condition.
@@ -68,7 +69,7 @@ public final class WhileNode extends AbstractLoopNode implements RSyntaxNode, RS
     @Override
     public Object execute(VirtualFrame frame) {
         loop.executeLoop(frame);
-        RContext.getInstance().setVisible(false);
+        visibility.execute(frame, false);
         return RNull.instance;
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java
index a28e67e0ffcdb8844f13e333bb4defb6d1bc4c9d..58f248caeb3d1c4aaeb7c70d9492d6b4f16ff260 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java
@@ -16,26 +16,25 @@ import java.util.function.Supplier;
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.nodes.DirectCallNode;
 import com.oracle.truffle.api.nodes.ExplodeLoop;
-import com.oracle.truffle.api.nodes.IndirectCallNode;
 import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.RRootNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.ArgumentMatcher.MatchPermutation;
-import com.oracle.truffle.r.nodes.function.signature.RArgumentsNode;
+import com.oracle.truffle.r.nodes.function.call.CallRFunctionCachedNode;
+import com.oracle.truffle.r.nodes.function.call.CallRFunctionCachedNodeGen;
+import com.oracle.truffle.r.nodes.function.call.CallRFunctionNode;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.nodes.unary.CastNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RArguments.DispatchArgs;
-import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.REmpty;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -50,7 +49,6 @@ public abstract class CallMatcherNode extends RBaseNode {
     protected final boolean argsAreEvaluated;
 
     @Child private PromiseHelperNode promiseHelper;
-    @Child private RArgumentsNode argsNode = RArgumentsNode.create();
 
     protected final ConditionProfile missingArgProfile = ConditionProfile.createBinaryProfile();
     protected final ConditionProfile emptyArgProfile = ConditionProfile.createBinaryProfile();
@@ -128,10 +126,6 @@ public abstract class CallMatcherNode extends RBaseNode {
         return new CallMatcherCachedNode(suppliedSignature, varArgSignatures, function, preparePermutation, permutation, forNextMethod, argsAreEvaluated, next);
     }
 
-    protected Object[] prepareArguments(Object[] reorderedArgs, ArgumentsSignature reorderedSignature, RFunction function, DispatchArgs dispatchArgs, RCaller caller) {
-        return argsNode.execute(function, caller, null, reorderedArgs, reorderedSignature, dispatchArgs);
-    }
-
     protected final void evaluatePromises(VirtualFrame frame, RFunction function, Object[] args, int varArgIndex) {
         if (function.isBuiltin()) {
             if (!argsAreEvaluated) {
@@ -185,7 +179,7 @@ public abstract class CallMatcherNode extends RBaseNode {
                 CallMatcherCachedNode cachedNode = replace(specialize(suppliedSignature, suppliedArguments, function, this));
                 // for splitting if necessary
                 if (cachedNode.call != null && RCallNode.needsSplitting(function.getTarget())) {
-                    cachedNode.call.cloneCallTarget();
+                    cachedNode.call.getCallNode().cloneCallTarget();
                 }
                 return cachedNode.execute(frame, suppliedSignature, suppliedArguments, function, functionName, dispatchArgs);
             }
@@ -200,11 +194,10 @@ public abstract class CallMatcherNode extends RBaseNode {
     private static final class CallMatcherCachedNode extends CallMatcherNode {
 
         @Child private CallMatcherNode next;
-
-        @Child private DirectCallNode call;
-
+        @Child private CallRFunctionNode call;
         @Child private RBuiltinNode builtin;
         @Children private final CastNode[] builtinArgumentCasts;
+        @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
 
         private final RBuiltinDescriptor builtinDescriptor;
         private final ArgumentsSignature cachedSuppliedSignature;
@@ -233,7 +226,7 @@ public abstract class CallMatcherNode extends RBaseNode {
                 this.builtin = RBuiltinNode.inline(builtinDescriptor, null);
                 this.builtinArgumentCasts = builtin.getCasts();
             } else {
-                this.call = Truffle.getRuntime().createDirectCallNode(function.getTarget());
+                this.call = CallRFunctionNode.create(function.getTarget());
                 this.builtinArgumentCasts = null;
                 this.builtinDescriptor = null;
             }
@@ -259,12 +252,11 @@ public abstract class CallMatcherNode extends RBaseNode {
                     String genFunctionName = functionName == null ? function.getName() : functionName;
                     Supplier<RSyntaxNode> argsSupplier = RCallerHelper.createFromArguments(genFunctionName, preparePermutation, suppliedArguments, suppliedSignature);
                     RCaller caller = genFunctionName == null ? RCaller.createInvalid(frame, parent) : RCaller.create(frame, parent, argsSupplier);
-                    Object[] arguments = prepareArguments(reorderedArgs, matchedArgs.getSignature(), cachedFunction, dispatchArgs, caller);
-                    return call.call(frame, arguments);
+                    return call.execute(frame, cachedFunction, caller, null, reorderedArgs, matchedArgs.getSignature(), cachedFunction.getEnclosingFrame(), dispatchArgs);
                 } else {
                     applyCasts(reorderedArgs);
                     Object result = builtin.execute(frame, reorderedArgs);
-                    RContext.getInstance().setVisible(builtinDescriptor.getVisibility());
+                    visibility.execute(frame, builtinDescriptor.getVisibility());
                     return result;
                 }
             } else {
@@ -334,7 +326,7 @@ public abstract class CallMatcherNode extends RBaseNode {
             super(forNextMethod, argsAreEvaluated);
         }
 
-        @Child private IndirectCallNode call = Truffle.getRuntime().createIndirectCallNode();
+        @Child private CallRFunctionCachedNode call = CallRFunctionCachedNodeGen.create(0);
 
         @Override
         public Object execute(VirtualFrame frame, ArgumentsSignature suppliedSignature, Object[] suppliedArguments, RFunction function, String functionName, DispatchArgs dispatchArgs) {
@@ -346,8 +338,7 @@ public abstract class CallMatcherNode extends RBaseNode {
             RCaller caller = genFunctionName == null ? RCaller.createInvalid(frame, parent)
                             : RCaller.create(frame, RCallerHelper.createFromArguments(genFunctionName,
                                             new RArgsValuesAndNames(reorderedArgs.getArguments(), ArgumentsSignature.empty(reorderedArgs.getLength()))));
-            Object[] arguments = prepareArguments(reorderedArgs.getArguments(), reorderedArgs.getSignature(), function, dispatchArgs, caller);
-            return call.call(frame, function.getTarget(), arguments);
+            return call.execute(frame, function, caller, null, reorderedArgs.getArguments(), reorderedArgs.getSignature(), function.getEnclosingFrame(), dispatchArgs);
         }
 
         @Override
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 260ee359b51c6c32724245f659bf10cf8ca94ada..e68431c47f78428f966a191498ac10db4d28b657 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
@@ -47,6 +47,7 @@ import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinFactory;
 import com.oracle.truffle.r.nodes.control.BreakException;
 import com.oracle.truffle.r.nodes.control.NextException;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.JumpToTopLevelException;
 import com.oracle.truffle.r.runtime.ExitException;
@@ -116,6 +117,8 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
     @Child private FrameSlotNode dotTargetSlot;
     @Child private FrameSlotNode dotMethodsSlot;
 
+    @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
+
     @Child private PostProcessArgumentsNode argPostProcess;
 
     private final boolean needsSplitting;
@@ -287,6 +290,7 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
              * has no exit handlers (by fiat), so any exceptions from onExits handlers will be
              * caught above.
              */
+            visibility.executeEndOfFunction(frame);
             if (argPostProcess != null) {
                 resetArgs.enter();
                 argPostProcess.execute(frame);
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 98b20bc722767e8679064d0389a8431800d80ad0..ee4d814f981084f26fe209474fdbb1a721e4da9b 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
@@ -30,7 +30,6 @@ import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.RootCallTarget;
-import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.NodeChild;
@@ -42,7 +41,6 @@ import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.interop.TruffleObject;
-import com.oracle.truffle.api.nodes.DirectCallNode;
 import com.oracle.truffle.api.nodes.ExplodeLoop;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeCost;
@@ -67,7 +65,9 @@ import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseCheckHelperN
 import com.oracle.truffle.r.nodes.function.RCallNodeGen.FunctionDispatchNodeGen;
 import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode.NoGenericMethodException;
 import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode.Result;
+import com.oracle.truffle.r.nodes.function.call.CallRFunctionNode;
 import com.oracle.truffle.r.nodes.function.call.PrepareArguments;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.nodes.profile.TruffleBoundaryNode;
 import com.oracle.truffle.r.nodes.unary.CastNode;
 import com.oracle.truffle.r.runtime.Arguments;
@@ -75,9 +75,6 @@ import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RArguments.S3Args;
 import com.oracle.truffle.r.runtime.RArguments.S3DefaultArguments;
-import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
-import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
-import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RDispatch;
@@ -88,7 +85,9 @@ import com.oracle.truffle.r.runtime.RSerialize;
 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.context.RContext;
+import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.REmpty;
@@ -295,14 +294,15 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
     }
 
     @Specialization(limit = "5", guards = {"isSpecialDispatch(function)", "cachedBuiltin == function.getRBuiltin()"})
-    public Object callSpecial(VirtualFrame frame, @SuppressWarnings("unused") RFunction function, //
-                    @Cached("function.getRBuiltin()") RBuiltinDescriptor cachedBuiltin, //
-                    @Cached("createSpecial(cachedBuiltin)") RBuiltinNode call) {
+    public Object callSpecial(VirtualFrame frame, @SuppressWarnings("unused") RFunction function,
+                    @Cached("function.getRBuiltin()") RBuiltinDescriptor cachedBuiltin,
+                    @Cached("createSpecial(cachedBuiltin)") RBuiltinNode call,
+                    @Cached("create()") SetVisibilityNode visibility) {
         if (explicitArgs != null) {
             CompilerDirectives.transferToInterpreter();
             throw RError.error(this, RError.Message.INVALID_USE, cachedBuiltin.getName());
         }
-        RContext.getInstance().setVisible(cachedBuiltin.getVisibility());
+        visibility.execute(frame, cachedBuiltin.getVisibility());
         return call.execute(frame);
     }
 
@@ -930,6 +930,7 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
         @Child private RBuiltinNode builtin;
         @Child private PromiseCheckHelperNode promiseHelper;
         @Children private final CastNode[] casts;
+        @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
 
         private final BranchProfile emptyProfile = BranchProfile.create();
         private final BranchProfile varArgsProfile = BranchProfile.create();
@@ -1018,15 +1019,16 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
         @Override
         public Object execute(VirtualFrame frame, RFunction currentFunction, RArgsValuesAndNames orderedArguments, S3Args s3Args) {
             Object result = builtin.execute(frame, castArguments(frame, orderedArguments.getArguments()));
-            RContext.getInstance().setVisible(builtinDescriptor.getVisibility());
+            visibility.execute(frame, builtinDescriptor.getVisibility());
             return result;
         }
     }
 
     private static final class DispatchedCallNode extends LeafCallNode {
 
-        @Child private DirectCallNode call;
+        @Child private CallRFunctionNode call;
         @Child private RFastPathNode fastPath;
+        @Child private SetVisibilityNode visibility;
 
         private final RootCallTarget cachedTarget;
         private final FastPathFactory fastPathFactory;
@@ -1039,6 +1041,7 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
             this.fastPathFactory = root.getFastPath();
             this.fastPath = fastPathFactory == null ? null : fastPathFactory.create();
             this.fastPathVisibility = fastPathFactory == null ? null : fastPathFactory.getVisibility();
+            this.visibility = fastPathFactory == null ? null : SetVisibilityNode.create();
             originalCall.needsCallerFrame |= root.containsDispatch();
         }
 
@@ -1047,25 +1050,26 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
             if (fastPath != null) {
                 Object result = fastPath.execute(frame, orderedArguments.getArguments());
                 if (result != null) {
-                    RContext.getInstance().setVisible(this.fastPathVisibility);
+                    assert fastPathVisibility != null;
+                    visibility.execute(frame, fastPathVisibility);
                     return result;
                 }
                 CompilerDirectives.transferToInterpreterAndInvalidate();
                 fastPath = null;
+                visibility = null;
             }
 
             if (call == null) {
                 CompilerDirectives.transferToInterpreterAndInvalidate();
-                call = insert(Truffle.getRuntime().createDirectCallNode(cachedTarget));
+                call = insert(CallRFunctionNode.create(cachedTarget));
                 if (needsSplitting(cachedTarget)) {
-                    call.cloneCallTarget();
+                    call.getCallNode().cloneCallTarget();
                 }
             }
             MaterializedFrame callerFrame = /* CompilerDirectives.inInterpreter() || */originalCall.needsCallerFrame ? frame.materialize() : null;
 
-            Object[] argsObject = RArguments.create(function, originalCall.createCaller(frame, function), callerFrame, orderedArguments.getArguments(), orderedArguments.getSignature(),
+            return call.execute(frame, function, originalCall.createCaller(frame, function), callerFrame, orderedArguments.getArguments(), orderedArguments.getSignature(),
                             function.getEnclosingFrame(), s3Args);
-            return call.call(frame, argsObject);
         }
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java
index da4f5715c1e4add5da9d72ea7b84b4e1766f5275..284b6b0c0825045733416846ed73b1ab1641f761 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java
@@ -17,7 +17,6 @@ import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RArguments.S3Args;
 import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 
@@ -39,7 +38,6 @@ public final class UseMethodInternalNode extends RNode {
     }
 
     public Object execute(VirtualFrame frame, RStringVector type, Object[] arguments) {
-        RContext.getInstance().setVisible(true);
         Result lookupResult = lookup.execute(frame, generic, type, null, frame.materialize(), null);
         if (wrap) {
             assert arguments != null;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionCachedNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionCachedNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c8b0fce0bb4616a854b24566febd93c88265a63
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionCachedNode.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2014, 2016, 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.call;
+
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.DirectCallNode;
+import com.oracle.truffle.api.nodes.IndirectCallNode;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeCost;
+import com.oracle.truffle.api.nodes.NodeInfo;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
+import com.oracle.truffle.r.runtime.ArgumentsSignature;
+import com.oracle.truffle.r.runtime.RArguments;
+import com.oracle.truffle.r.runtime.RArguments.DispatchArgs;
+import com.oracle.truffle.r.runtime.RCaller;
+import com.oracle.truffle.r.runtime.data.RFunction;
+
+@NodeInfo(cost = NodeCost.NONE)
+public abstract class CallRFunctionCachedNode extends Node {
+
+    @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
+
+    protected final int cacheLimit;
+
+    protected CallRFunctionCachedNode(int cacheLimit) {
+        this.cacheLimit = cacheLimit;
+    }
+
+    public final Object execute(VirtualFrame frame, RFunction function, RCaller call, MaterializedFrame callerFrame, Object[] evaluatedArgs, DispatchArgs dispatchArgs) {
+        Object[] callArgs = RArguments.create(function, call, callerFrame, evaluatedArgs, dispatchArgs);
+        return execute(frame, function.getTarget(), callArgs);
+    }
+
+    public final Object execute(VirtualFrame frame, RFunction function, RCaller call, MaterializedFrame callerFrame, Object[] evaluatedArgs,
+                    ArgumentsSignature suppliedSignature, MaterializedFrame enclosingFrame, DispatchArgs dispatchArgs) {
+        Object[] callArgs = RArguments.create(function, call, callerFrame, evaluatedArgs, suppliedSignature, enclosingFrame, dispatchArgs);
+        return execute(frame, function.getTarget(), callArgs);
+    }
+
+    protected abstract Object execute(VirtualFrame frame, CallTarget target, Object[] arguments);
+
+    protected static DirectCallNode createDirectCallNode(CallTarget target) {
+        return Truffle.getRuntime().createDirectCallNode(target);
+    }
+
+    @Specialization(guards = "target == callNode.getCallTarget()", limit = "cacheLimit")
+    protected Object call(VirtualFrame frame, @SuppressWarnings("unused") CallTarget target, Object[] arguments,
+                    @Cached("createDirectCallNode(target)") DirectCallNode callNode) {
+        try {
+            return callNode.call(frame, arguments);
+        } finally {
+            visibility.executeAfterCall(frame);
+        }
+    }
+
+    protected static IndirectCallNode createIndirectCallNode() {
+        return Truffle.getRuntime().createIndirectCallNode();
+    }
+
+    @Specialization
+    protected Object call(VirtualFrame frame, CallTarget target, Object[] arguments,
+                    @Cached("createIndirectCallNode()") IndirectCallNode callNode) {
+        try {
+            return callNode.call(frame, target, arguments);
+        } finally {
+            visibility.executeAfterCall(frame);
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..6cdf8d25967db8c12a98fa7d5df2dd0d25f5420c
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionNode.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2016, 2016, 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.call;
+
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.DirectCallNode;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeCost;
+import com.oracle.truffle.api.nodes.NodeInfo;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
+import com.oracle.truffle.r.runtime.ArgumentsSignature;
+import com.oracle.truffle.r.runtime.RArguments;
+import com.oracle.truffle.r.runtime.RArguments.DispatchArgs;
+import com.oracle.truffle.r.runtime.RCaller;
+import com.oracle.truffle.r.runtime.data.RFunction;
+
+@NodeInfo(cost = NodeCost.NONE)
+public final class CallRFunctionNode extends Node {
+
+    @Child private DirectCallNode callNode;
+    @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
+
+    private CallRFunctionNode(CallTarget callTarget) {
+        this.callNode = Truffle.getRuntime().createDirectCallNode(callTarget);
+    }
+
+    public static CallRFunctionNode create(CallTarget callTarget) {
+        return new CallRFunctionNode(callTarget);
+    }
+
+    public Object execute(VirtualFrame frame, RFunction function, RCaller call, MaterializedFrame callerFrame, Object[] evaluatedArgs, ArgumentsSignature suppliedSignature,
+                    MaterializedFrame enclosingFrame, DispatchArgs dispatchArgs) {
+        Object[] callArgs = RArguments.create(function, call, callerFrame, evaluatedArgs, suppliedSignature, enclosingFrame, dispatchArgs);
+        try {
+            return callNode.call(frame, callArgs);
+        } finally {
+            visibility.executeAfterCall(frame);
+        }
+    }
+
+    public DirectCallNode getCallNode() {
+        return callNode;
+    }
+
+    public static Object executeSlowpath(RFunction function, RCaller call, MaterializedFrame callerFrame, Object[] evaluatedArgs, DispatchArgs dispatchArgs) {
+        Object[] callArgs = RArguments.create(function, call, callerFrame, evaluatedArgs, dispatchArgs);
+        try {
+            return function.getTarget().call(callArgs);
+        } finally {
+            SetVisibilityNode.executeAfterCallSlowPath(callerFrame);
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/RArgumentsNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/RArgumentsNode.java
deleted file mode 100644
index 244c55359e7f6bc046ae731e45ff3fea0e17a9ee..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/RArgumentsNode.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, 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.signature;
-
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.frame.MaterializedFrame;
-import com.oracle.truffle.api.nodes.NodeCost;
-import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RArguments;
-import com.oracle.truffle.r.runtime.RArguments.DispatchArgs;
-import com.oracle.truffle.r.runtime.RCaller;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.nodes.RBaseNode;
-
-public abstract class RArgumentsNode extends RBaseNode {
-
-    /*
-     * This inline cache is not implemented using Truffle DSL because of null values provided for
-     * certain parameters, on which Truffle DSL cannot specialize.
-     */
-
-    public abstract Object[] execute(RFunction function, RCaller caller, MaterializedFrame callerFrame, Object[] evaluatedArgs, ArgumentsSignature signature, DispatchArgs dispatchArgs);
-
-    public static RArgumentsNode create() {
-        return new RArgumentsUninitializedNode();
-    }
-
-    @Override
-    public NodeCost getCost() {
-        return NodeCost.NONE;
-    }
-
-    private static final class RArgumentsUninitializedNode extends RArgumentsNode {
-        @Override
-        public Object[] execute(RFunction function, RCaller caller, MaterializedFrame callerFrame, Object[] evaluatedArgs, ArgumentsSignature signature, DispatchArgs dispatchArgs) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            return replace(new RArgumentsCachedNode(function)).execute(function, caller, callerFrame, evaluatedArgs, signature, dispatchArgs);
-        }
-    }
-
-    private static final class RArgumentsCachedNode extends RArgumentsNode {
-        private final RFunction cachedFunction;
-
-        RArgumentsCachedNode(RFunction cachedFunction) {
-            this.cachedFunction = cachedFunction;
-        }
-
-        @Override
-        public Object[] execute(RFunction function, RCaller caller, MaterializedFrame callerFrame, Object[] evaluatedArgs, ArgumentsSignature signature, DispatchArgs dispatchArgs) {
-            if (function == cachedFunction) {
-                return RArguments.create(cachedFunction, caller, callerFrame, evaluatedArgs, cachedFunction.getEnclosingFrame(), dispatchArgs);
-            } else {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                return replace(new RArgumentsGenericNode()).execute(function, caller, callerFrame, evaluatedArgs, signature, dispatchArgs);
-            }
-        }
-    }
-
-    private static final class RArgumentsGenericNode extends RArgumentsNode {
-        @Override
-        public Object[] execute(RFunction function, RCaller caller, MaterializedFrame callerFrame, Object[] evaluatedArgs, ArgumentsSignature signature, DispatchArgs dispatchArgs) {
-            return RArguments.create(function, caller, callerFrame, evaluatedArgs, function.getEnclosingFrame(), dispatchArgs);
-        }
-    }
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/visibility/GetVisibilityNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/visibility/GetVisibilityNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..4a53a3de85217f25b066dd15d6c15f4bffe71ef5
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/visibility/GetVisibilityNode.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2016, 2016, 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.visibility;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.frame.Frame;
+import com.oracle.truffle.api.frame.FrameSlot;
+import com.oracle.truffle.api.frame.FrameSlotKind;
+import com.oracle.truffle.api.frame.FrameSlotTypeException;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeCost;
+import com.oracle.truffle.api.nodes.NodeInfo;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.runtime.FastROptions;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.env.frame.RFrameSlot;
+
+@NodeInfo(cost = NodeCost.NONE)
+public abstract class GetVisibilityNode extends Node {
+
+    public abstract boolean execute(Frame frame);
+
+    public abstract boolean execute(Frame frame, RContext context);
+
+    private GetVisibilityNode() {
+    }
+
+    public static GetVisibilityNode create() {
+        if (FastROptions.IgnoreVisibility.getBooleanValue()) {
+            return new GetVisibilityNoopNode();
+        } else if (FastROptions.OptimizeVisibility.getBooleanValue()) {
+            return new GetVisibilityOptimizedNode();
+        } else {
+            return new GetVisibilityDefaultNode();
+        }
+    }
+
+    private static final class GetVisibilityNoopNode extends GetVisibilityNode {
+
+        @Override
+        public boolean execute(Frame frame) {
+            return false;
+        }
+
+        @Override
+        public boolean execute(Frame frame, RContext context) {
+            return false;
+        }
+    }
+
+    private static final class GetVisibilityDefaultNode extends GetVisibilityNode {
+
+        @Override
+        public boolean execute(Frame frame) {
+            return execute(frame, RContext.getInstance());
+        }
+
+        @Override
+        public boolean execute(Frame frame, RContext context) {
+            return context.isVisible();
+        }
+    }
+
+    /**
+     * See {@link RFrameSlot#Visibility}.
+     */
+    private static final class GetVisibilityOptimizedNode extends GetVisibilityNode {
+
+        @CompilationFinal private FrameSlot frameSlot;
+        private final ConditionProfile isCustomProfile = ConditionProfile.createBinaryProfile();
+
+        @Override
+        public boolean execute(Frame frame) {
+            if (frameSlot == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                frameSlot = frame.getFrameDescriptor().findOrAddFrameSlot(RFrameSlot.Visibility, FrameSlotKind.Object);
+            }
+            try {
+                Object visibility = frame.getObject(frameSlot);
+                if (isCustomProfile.profile(visibility == null)) {
+                    return RContext.getInstance().isVisible();
+                } else {
+                    return visibility == Boolean.TRUE;
+                }
+            } catch (FrameSlotTypeException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+
+        @Override
+        public boolean execute(Frame frame, RContext context) {
+            return execute(frame);
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/visibility/SetVisibilityNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/visibility/SetVisibilityNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..e6347e24fff03b405cba20e6da3c414638977aaf
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/visibility/SetVisibilityNode.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2016, 2016, 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.visibility;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.frame.Frame;
+import com.oracle.truffle.api.frame.FrameSlot;
+import com.oracle.truffle.api.frame.FrameSlotKind;
+import com.oracle.truffle.api.frame.FrameSlotTypeException;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeCost;
+import com.oracle.truffle.api.nodes.NodeInfo;
+import com.oracle.truffle.r.runtime.FastROptions;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.env.frame.RFrameSlot;
+
+@NodeInfo(cost = NodeCost.NONE)
+public abstract class SetVisibilityNode extends Node {
+
+    public abstract void execute(Frame frame, boolean value);
+
+    public abstract void execute(VirtualFrame frame, RVisibility visibility);
+
+    public abstract void executeAfterCall(VirtualFrame frame);
+
+    public abstract void executeEndOfFunction(VirtualFrame frame);
+
+    private SetVisibilityNode() {
+    }
+
+    public static SetVisibilityNode create() {
+        if (FastROptions.IgnoreVisibility.getBooleanValue()) {
+            return new SetVisibilityNoopNode();
+        } else if (FastROptions.OptimizeVisibility.getBooleanValue()) {
+            return new SetVisibilityOptimizedNode();
+        } else {
+            return new SetVisibilityDefaultNode();
+        }
+    }
+
+    private static final class SetVisibilityNoopNode extends SetVisibilityNode {
+
+        @Override
+        public void execute(Frame frame, boolean value) {
+            // nothing to do
+        }
+
+        @Override
+        public void execute(VirtualFrame frame, RVisibility visibility) {
+            // nothing to do
+        }
+
+        @Override
+        public void executeAfterCall(VirtualFrame frame) {
+            // nothing to do
+        }
+
+        @Override
+        public void executeEndOfFunction(VirtualFrame frame) {
+            // nothing to do
+        }
+    }
+
+    private static final class SetVisibilityDefaultNode extends SetVisibilityNode {
+
+        @Override
+        public void execute(Frame frame, boolean value) {
+            RContext.getInstance().setVisible(value);
+        }
+
+        @Override
+        public void execute(VirtualFrame frame, RVisibility visibility) {
+            if (visibility == RVisibility.ON) {
+                execute(frame, true);
+            } else if (visibility == RVisibility.OFF) {
+                execute(frame, false);
+            }
+        }
+
+        @Override
+        public void executeAfterCall(VirtualFrame frame) {
+            // nothing to do
+        }
+
+        @Override
+        public void executeEndOfFunction(VirtualFrame frame) {
+            // nothing to do
+        }
+    }
+
+    /**
+     * See {@link RFrameSlot#Visibility}.
+     */
+    private static final class SetVisibilityOptimizedNode extends SetVisibilityNode {
+
+        @CompilationFinal private FrameSlot frameSlot;
+
+        @Override
+        public void execute(Frame frame, boolean value) {
+            if (frameSlot == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                frameSlot = frame.getFrameDescriptor().findOrAddFrameSlot(RFrameSlot.Visibility, FrameSlotKind.Object);
+            }
+            frame.setObject(frameSlot, value);
+        }
+
+        @Override
+        public void execute(VirtualFrame frame, RVisibility visibility) {
+            if (visibility == RVisibility.ON) {
+                execute(frame, true);
+            } else if (visibility == RVisibility.OFF) {
+                execute(frame, false);
+            }
+        }
+
+        @Override
+        public void executeAfterCall(VirtualFrame frame) {
+            if (frameSlot == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                frameSlot = frame.getFrameDescriptor().findOrAddFrameSlot(RFrameSlot.Visibility, FrameSlotKind.Object);
+            }
+            frame.setObject(frameSlot, null);
+        }
+
+        @Override
+        public void executeEndOfFunction(VirtualFrame frame) {
+            if (frameSlot == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                frameSlot = frame.getFrameDescriptor().findOrAddFrameSlot(RFrameSlot.Visibility, FrameSlotKind.Object);
+            }
+            try {
+                Object visibility = frame.getObject(frameSlot);
+                if (visibility != null) {
+                    RContext.getInstance().setVisible(visibility == Boolean.TRUE);
+                }
+            } catch (FrameSlotTypeException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    public static void executeAfterCallSlowPath(Frame frame) {
+        frame.setObject(frame.getFrameDescriptor().findOrAddFrameSlot(RFrameSlot.Visibility, FrameSlotKind.Object), null);
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/DispatchGeneric.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/DispatchGeneric.java
index 7863264c41c0e4d51d6e4b53920933e67a4e983e..733648fbaea8f503607b1b735e07275a000315ab 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/DispatchGeneric.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/DispatchGeneric.java
@@ -23,7 +23,6 @@ import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.access.variables.LocalReadVariableNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
-import com.oracle.truffle.r.nodes.function.signature.RArgumentsNode;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -37,7 +36,6 @@ public abstract class DispatchGeneric extends RBaseNode {
 
     private final ConditionProfile singleStringProfile = ConditionProfile.createBinaryProfile();
     private final BranchProfile equalsMethodRequired = BranchProfile.create();
-    @Child private RArgumentsNode argsNode = RArgumentsNode.create();
     @Child private LoadMethod loadMethod = LoadMethodNodeGen.create();
     @Child private ExecuteMethod executeMethod = new ExecuteMethod();
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/LoadMethod.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/LoadMethod.java
index 72a4c84ab2dcfdc2f42b28b3d0a4a3ed09b55855..64bb9faef97b5e45e360939675c1bacd95455be1 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/LoadMethod.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/LoadMethod.java
@@ -14,11 +14,9 @@ package com.oracle.truffle.r.nodes.objects;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.nodes.DirectCallNode;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.ValueProfile;
@@ -29,7 +27,7 @@ import com.oracle.truffle.r.nodes.access.variables.LocalReadVariableNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.attributes.AttributeAccess;
 import com.oracle.truffle.r.nodes.attributes.AttributeAccessNodeGen;
-import com.oracle.truffle.r.nodes.function.signature.RArgumentsNode;
+import com.oracle.truffle.r.nodes.function.call.CallRFunctionNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RError;
@@ -58,9 +56,8 @@ abstract class LoadMethod extends RBaseNode {
     @Child private WriteLocalFrameVariableNode writeRMethod = WriteLocalFrameVariableNode.create(RRuntime.R_DOT_METHOD, null, WriteVariableNode.Mode.REGULAR);
     @Child private LocalReadVariableNode methodsEnvRead = LocalReadVariableNode.create("methods", true);
     @Child private ReadVariableNode loadMethodFind;
-    @Child private DirectCallNode loadMethodCall;
+    @Child private CallRFunctionNode loadMethodCall;
     @CompilationFinal private RFunction loadMethodFunction;
-    @Child private RArgumentsNode argsNode = RArgumentsNode.create();
     private final ConditionProfile cached = ConditionProfile.createBinaryProfile();
     private final ConditionProfile moreAttributes = ConditionProfile.createBinaryProfile();
     private final ConditionProfile noNextMethodAttr = ConditionProfile.createBinaryProfile();
@@ -119,7 +116,7 @@ abstract class LoadMethod extends RBaseNode {
                 loadMethodFind = insert(ReadVariableNode.createFunctionLookup(RSyntaxNode.INTERNAL, RRuntime.R_LOAD_METHOD_NAME));
                 currentFunction = (RFunction) loadMethodFind.execute(frame, methodsEnv.getFrame());
                 loadMethodFunction = currentFunction;
-                loadMethodCall = insert(Truffle.getRuntime().createDirectCallNode(loadMethodFunction.getTarget()));
+                loadMethodCall = insert(CallRFunctionNode.create(loadMethodFunction.getTarget()));
                 RError.performanceWarning("loadMethod executing slow path");
             } else {
                 currentFunction = (RFunction) loadMethodFind.execute(frame, methodsEnv.getFrame(methodsFrameAccessProfile));
@@ -129,10 +126,8 @@ abstract class LoadMethod extends RBaseNode {
             if (cached.profile(currentFunction == loadMethodFunction)) {
                 // TODO: technically, someone could override loadMethod function and access the
                 // caller, but it's rather unlikely
-                Object[] args = argsNode.execute(loadMethodFunction, caller, null,
-                                new Object[]{fdef, fname, REnvironment.frameToEnvironment(frame.materialize())}, SIGNATURE,
-                                null);
-                ret = (RFunction) loadMethodCall.call(frame, args);
+                ret = (RFunction) loadMethodCall.execute(frame, loadMethodFunction, caller, null, new Object[]{fdef, fname, REnvironment.frameToEnvironment(frame.materialize())}, SIGNATURE,
+                                loadMethodFunction.getEnclosingFrame(), null);
             } else {
                 // slow path
                 ret = (RFunction) RContext.getEngine().evalFunction(currentFunction, frame.materialize(), caller, null, fdef, fname, REnvironment.frameToEnvironment(frame.materialize()));
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
index dca4160352aa4e20ce5b12f61385cdae22a23066..ab173296f67a787a4afe0c45d295986c5e14b7e4 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
@@ -27,6 +27,7 @@ import java.util.Map.Entry;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.r.runtime.env.frame.RFrameSlot;
 
 /**
  * Options to control the behavior of the FastR system, that relate to the implementation, i.e., are
@@ -49,6 +50,10 @@ public enum FastROptions {
     LoadBase("Load base package", true),
     PrintComplexLookups("Print a message for each non-trivial variable lookup", false),
     IgnoreVisibility("Ignore setting of the visibility flag", false),
+    /**
+     * See {@link RFrameSlot#Visibility}.
+     */
+    OptimizeVisibility("optimized setting of the visibility flag", true),
     LoadPkgSourcesIndex("Load R package sources index", true),
     InvisibleArgs("Argument writes do not trigger state transitions", true),
     RefCountIncrementOnly("Disable reference count decrements for experimental state transition implementation", false),
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java
index 35ebce4aa43a83817be26942121d23bc3a2fbe36..6fe38d5db1bb7f8b3abb758119d0cefe9e471198 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java
@@ -169,18 +169,10 @@ public final class RArguments {
         return frame.getArguments().length - INDEX_ARGUMENTS;
     }
 
-    public static Object[] create(RFunction functionObj, RCaller call, MaterializedFrame callerFrame, Object[] evaluatedArgs, DispatchArgs dispatchArgs) {
-        ArgumentsSignature formalSignature = ((HasSignature) functionObj.getRootNode()).getSignature();
-        return create(functionObj, call, callerFrame, evaluatedArgs, ArgumentsSignature.empty(formalSignature.getLength()), dispatchArgs);
-    }
-
-    public static Object[] create(RFunction functionObj, RCaller call, MaterializedFrame callerFrame, Object[] evaluatedArgs, ArgumentsSignature suppliedSignature, DispatchArgs dispatchArgs) {
+    public static Object[] create(RFunction function, RCaller call, MaterializedFrame callerFrame, Object[] evaluatedArgs, DispatchArgs dispatchArgs) {
+        ArgumentsSignature formalSignature = ((HasSignature) function.getRootNode()).getSignature();
         CompilerAsserts.neverPartOfCompilation();
-        return create(functionObj, call, callerFrame, evaluatedArgs, suppliedSignature, functionObj.getEnclosingFrame(), dispatchArgs);
-    }
-
-    public static Object[] create(RFunction functionObj, RCaller call, MaterializedFrame callerFrame, Object[] evaluatedArgs, MaterializedFrame enclosingFrame, DispatchArgs dispatchArgs) {
-        return create(functionObj, call, callerFrame, evaluatedArgs, ArgumentsSignature.empty(evaluatedArgs.length), enclosingFrame, dispatchArgs);
+        return create(function, call, callerFrame, evaluatedArgs, ArgumentsSignature.empty(formalSignature.getLength()), function.getEnclosingFrame(), dispatchArgs);
     }
 
     /**
@@ -194,7 +186,7 @@ public final class RArguments {
      *         function as well as additional information like the parent frame or supplied
      *         signature.
      */
-    public static Object[] create(RFunction functionObj, RCaller call, MaterializedFrame callerFrame, Object[] evaluatedArgs,
+    public static Object[] create(RFunction function, RCaller call, MaterializedFrame callerFrame, Object[] evaluatedArgs,
                     ArgumentsSignature suppliedSignature, MaterializedFrame enclosingFrame, DispatchArgs dispatchArgs) {
         assert suppliedSignature.getLength() == evaluatedArgs.length : "suppliedSignature should match the evaluatedArgs (see Java docs).";
         assert evaluatedArgs != null : "RArguments.create evaluatedArgs is null";
@@ -204,7 +196,7 @@ public final class RArguments {
 
         Object[] a = new Object[MINIMAL_ARRAY_LENGTH + evaluatedArgs.length];
         a[INDEX_ENVIRONMENT] = null;
-        a[INDEX_FUNCTION] = functionObj;
+        a[INDEX_FUNCTION] = function;
         a[INDEX_CALL] = call;
         a[INDEX_CALLER_FRAME] = callerFrame;
         a[INDEX_ENCLOSING_FRAME] = enclosingFrame;
@@ -308,11 +300,6 @@ public final class RArguments {
         frame.getArguments()[INDEX_IS_IRREGULAR] = isIrregularFrame;
     }
 
-    public static void setIsIrregular(Object[] arguments, boolean isIrregularFrame) {
-        assert arguments.length >= INDEX_ARGUMENTS;
-        arguments[INDEX_IS_IRREGULAR] = isIrregularFrame;
-    }
-
     public static void setEnclosingFrame(Frame frame, MaterializedFrame newEnclosingFrame) {
         CompilerAsserts.neverPartOfCompilation();
         Object[] arguments = frame.getArguments();
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/FastPathFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/FastPathFactory.java
index 59271ee152afc1d898158e4c11f9195f983d8785..494ef0ba2c953fbfcb6e0b3d757ee2e8f75429b8 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/FastPathFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/FastPathFactory.java
@@ -38,10 +38,30 @@ import com.oracle.truffle.r.runtime.nodes.RFastPathNode;
  * the function is invoked, the fast path is invoked first and only if it returns {@code null}, then
  * the original implementation is invoked.
  */
-@FunctionalInterface
 public interface FastPathFactory {
 
-    FastPathFactory EVALUATE_ARGS = () -> null;
+    FastPathFactory EVALUATE_ARGS = new FastPathFactory() {
+
+        @Override
+        public RFastPathNode create() {
+            return null;
+        }
+
+        @Override
+        public RVisibility getVisibility() {
+            return null;
+        }
+
+        @Override
+        public boolean evaluatesArgument(int index) {
+            return true;
+        }
+
+        @Override
+        public boolean forcedEagerPromise(int index) {
+            return false;
+        }
+    };
 
     FastPathFactory FORCED_EAGER_ARGS = new FastPathFactory() {
 
@@ -50,6 +70,11 @@ public interface FastPathFactory {
             return null;
         }
 
+        @Override
+        public RVisibility getVisibility() {
+            return null;
+        }
+
         @Override
         public boolean evaluatesArgument(int index) {
             return false;
@@ -83,23 +108,46 @@ public interface FastPathFactory {
                 }
                 return true;
             }
+
+            @Override
+            public boolean forcedEagerPromise(int index) {
+                return false;
+            }
         };
     }
 
-    RFastPathNode create();
+    static FastPathFactory fromVisibility(RVisibility visibility, Supplier<RFastPathNode> factory) {
+        return new FastPathFactory() {
+            @Override
+            public RFastPathNode create() {
+                return factory.get();
+            }
 
-    default boolean evaluatesArgument(@SuppressWarnings("unused") int index) {
-        return true;
-    }
+            @Override
+            public RVisibility getVisibility() {
+                return visibility;
+            }
+
+            @Override
+            public boolean evaluatesArgument(int index) {
+                return true;
+            }
 
-    default boolean forcedEagerPromise(@SuppressWarnings("unused") int index) {
-        return false;
+            @Override
+            public boolean forcedEagerPromise(int index) {
+                return false;
+            }
+        };
     }
 
+    RFastPathNode create();
+
+    boolean evaluatesArgument(int index);
+
+    boolean forcedEagerPromise(int index);
+
     /**
      * Visibility of the output. This corresponds to {@link RBuiltin#visibility()}
      */
-    default RVisibility getVisibility() {
-        return RVisibility.ON;
-    }
+    RVisibility getVisibility();
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
index 3c89158ada69e37d6ad29a517fbdf2f73f29581a..1184323bbc096d7d44bf14011f5f44f38d0b75f3 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
@@ -51,19 +51,18 @@ import com.oracle.truffle.r.runtime.REnvVars;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RErrorHandling;
 import com.oracle.truffle.r.runtime.RInternalCode.ContextStateImpl;
-import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
-import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
-import com.oracle.truffle.r.runtime.builtins.RBuiltinLookup;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ROptions;
 import com.oracle.truffle.r.runtime.RProfile;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RRuntimeASTAccess;
 import com.oracle.truffle.r.runtime.RSerialize;
-import com.oracle.truffle.r.runtime.RStartParams;
 import com.oracle.truffle.r.runtime.RSource;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.RStartParams;
 import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinLookup;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport;
 import com.oracle.truffle.r.runtime.conn.StdConnections;
 import com.oracle.truffle.r.runtime.context.Engine.ParseException;
@@ -603,10 +602,18 @@ public final class RContext extends ExecutionContext implements TruffleObject {
         return engine;
     }
 
+    /**
+     * This method should only be used under exceptional circumstances; the visibility can be
+     * derived with {@code GetVisibilityNode}.
+     */
     public boolean isVisible() {
         return resultVisible;
     }
 
+    /**
+     * This method should only be used under exceptional circumstances; the visibility can be
+     * changed with {@code SetVisibilityNode}.
+     */
     public void setVisible(boolean v) {
         if (!FastROptions.IgnoreVisibility.getBooleanValue()) {
             /*
@@ -618,14 +625,6 @@ public final class RContext extends ExecutionContext implements TruffleObject {
         }
     }
 
-    public void setVisible(RVisibility visibility) {
-        if (visibility == RVisibility.ON) {
-            setVisible(true);
-        } else if (visibility == RVisibility.OFF) {
-            setVisible(false);
-        }
-    }
-
     public boolean isMethodTableDispatchOn() {
         return methodTableDispatchOn;
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/RFrameSlot.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/RFrameSlot.java
index af64e0c2b744533d75fc7e44b8116cb713bb40e5..cfec997297a6e845e13761bc762f462d23bf5d22 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/RFrameSlot.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/RFrameSlot.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2016, 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,6 +22,37 @@
  */
 package com.oracle.truffle.r.runtime.env.frame;
 
+import java.util.ArrayList;
+
+import com.oracle.truffle.r.runtime.FastROptions;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+
+/**
+ * Description of different internal frame slots used by FastR. This enum is used as an identifier,
+ * so that these internal frame slots have non-string names.
+ */
 public enum RFrameSlot {
-    OnExit;
+    /**
+     * This frame slot is used to store expressions installed as function exit handlers via on.exit.
+     * It contains an {@link ArrayList} with {@link RNode} elements.
+     */
+    OnExit,
+    /**
+     * This frame slot is used to track result visibility when
+     * {@link FastROptions#OptimizeVisibility} is enabled. It can contain one of three values:
+     * <ul>
+     * <li>{@link Boolean#TRUE} if the result is currently visible</li>
+     * <li>{@link Boolean#FALSE} if the result is currently not visible</li>
+     * <li>{@code null} if the visibility needs to be determined via{@link RContext#isVisible()}
+     * </li>
+     * </ul>
+     *
+     * Whenever an {@RBuiltinNode} is called via {@code RCallNode}, the resulting visibility is
+     * stored in the current frame. At the end of a {@code FunctionDefinitionNode}, the current
+     * state is stored into {@link RContext#setVisible(boolean)} if it is non-{@code null}.
+     * Additionally, the frame-local state stored in this frame slot is set to {@code null} after
+     * each call to a non-builtin function.
+     */
+    Visibility;
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/EvaluatedArgumentsFastPath.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/EvaluatedArgumentsFastPath.java
index 7485df6bc517b694b0b08604f64d936c11506bed..82cbd61526bc10abcd5f907a17fba9583a856e24 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/EvaluatedArgumentsFastPath.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/EvaluatedArgumentsFastPath.java
@@ -26,6 +26,7 @@ import java.util.Arrays;
 
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.FastROptions;
+import com.oracle.truffle.r.runtime.RVisibility;
 import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
 
 final class EvaluatedArgumentsFastPath implements FastPathFactory {
@@ -41,6 +42,11 @@ final class EvaluatedArgumentsFastPath implements FastPathFactory {
         return null;
     }
 
+    @Override
+    public RVisibility getVisibility() {
+        return null;
+    }
+
     @Override
     public boolean evaluatesArgument(int index) {
         return false;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RFastPathNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RFastPathNode.java
index af5a762ce0ded699f73451fffdb63bb5a5084f59..859d2c670d46d5646566adc7fb7c65c2811465cd 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RFastPathNode.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RFastPathNode.java
@@ -22,11 +22,9 @@
  */
 package com.oracle.truffle.r.runtime.nodes;
 
-import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.frame.VirtualFrame;
 
-@NodeChild(value = "arguments", type = RNode[].class)
-public abstract class RFastPathNode extends RNode {
+public abstract class RFastPathNode extends RBaseNode {
 
     public abstract Object execute(VirtualFrame frame, Object... args);
 
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRDebugTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRDebugTest.java
index 318c667e9affa52073115f643799afd861e7c6d8..e86035f0b5d6a2565caa9adb7120f1fd45cae09a 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRDebugTest.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRDebugTest.java
@@ -265,7 +265,7 @@ public class FastRDebugTest {
                 Assert.assertEquals(code, actualCode);
                 final MaterializedFrame frame = suspendedEvent.getFrame();
 
-                Assert.assertEquals(expectedFrame.length / 2, frame.getFrameDescriptor().getSize());
+                Assert.assertEquals(expectedFrame.length / 2, frameSize(frame));
 
                 for (int i = 0; i < expectedFrame.length; i = i + 2) {
                     final String expectedIdentifier = (String) expectedFrame[i];
@@ -277,6 +277,16 @@ public class FastRDebugTest {
                 }
                 run.removeFirst().run();
             }
+
+            private int frameSize(MaterializedFrame frame) {
+                int cnt = 0;
+                for (FrameSlot slot : frame.getFrameDescriptor().getSlots()) {
+                    if (slot.getIdentifier() instanceof String) {
+                        cnt++;
+                    }
+                }
+                return cnt;
+            }
         });
     }
 
diff --git a/mx.fastr/mx_fastr_pkgs.py b/mx.fastr/mx_fastr_pkgs.py
index acde4d86b1b9fd5e027b1502cef15f4d20494bb9..f5771fbc8b29c1146c5725547a3d42d2e71d1415 100644
--- a/mx.fastr/mx_fastr_pkgs.py
+++ b/mx.fastr/mx_fastr_pkgs.py
@@ -77,7 +77,7 @@ def _installpkgs(args, **kwargs):
     '''
     script = _installpkgs_script()
     if _is_graalvm():
-        rscript = join(_graalvm(), 'bin', 'rscript')
+        rscript = join(_graalvm(), 'bin', 'Rscript')
         return mx.run([rscript, script] + args, **kwargs)
     else:
         return mx_fastr.rscript([script] + args, **kwargs)