diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionMR.java
index a7210d6a776f9841b8b66126ec13170f33db5eff..c0e648cc3a7eab50e1c93e2f4f8584c45976d514 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionMR.java
@@ -42,6 +42,7 @@ import com.oracle.truffle.r.runtime.context.RContext.RCloseable;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
+import com.oracle.truffle.r.runtime.env.frame.RFrameSlot;
 
 @MessageResolution(receiverType = RFunction.class, language = TruffleRLanguage.class)
 public class RFunctionMR {
@@ -54,9 +55,9 @@ public class RFunctionMR {
 
     @Resolve(message = "EXECUTE")
     public abstract static class RFunctionExecuteNode extends Node {
-        private static final FrameDescriptor emptyFrameDescriptor = new FrameDescriptor("R interop frame");
-        private static final Object argsIdentifier = new Object();
-        private static final FrameSlot slot = emptyFrameDescriptor.addFrameSlot(argsIdentifier, FrameSlotKind.Object);
+        private static final FrameDescriptor emptyFrameDescriptor = FrameSlotChangeMonitor.initializeFunctionFrameDescriptor("<interop>", new FrameDescriptor("R interop frame"));
+        private static final RFrameSlot argsIdentifier = RFrameSlot.createTemp(false);
+        private static final FrameSlot slot = FrameSlotChangeMonitor.findOrAddFrameSlot(emptyFrameDescriptor, argsIdentifier, FrameSlotKind.Object);
 
         static {
             FrameSlotChangeMonitor.initializeFunctionFrameDescriptor("<function>", emptyFrameDescriptor);
@@ -72,10 +73,10 @@ public class RFunctionMR {
 
             RArgsValuesAndNames actualArgs = new RArgsValuesAndNames(arguments, ArgumentsSignature.empty(arguments.length));
             try (RCloseable c = RContext.withinContext(TruffleRLanguage.INSTANCE.actuallyFindContext0(findContext))) {
-                dummyFrame.setObject(slot, actualArgs);
+                FrameSlotChangeMonitor.setObject(dummyFrame, slot, actualArgs);
                 return call.execute(dummyFrame, receiver);
             } finally {
-                dummyFrame.setObject(slot, null);
+                FrameSlotChangeMonitor.setObject(dummyFrame, slot, null);
             }
         }
     }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_NativeArray.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_NativeArray.java
index 39eeb2a3ea5c06b60d4b9735534f82008180a3cf..8a69a8d47b5accd9cc8c0fd8e45bfaa5538a28ff 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_NativeArray.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_NativeArray.java
@@ -28,7 +28,6 @@ import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.RLogicalVector;
-import com.oracle.truffle.r.runtime.ffi.UpCallsRFFI;
 import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
 
 /**
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_PkgInit.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_PkgInit.java
index 0ec6f4d2efe3a57eedb40e45d53483bb330ba2ce..2621894a9023bdad32f2c628be578698b03a4c4f 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_PkgInit.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_PkgInit.java
@@ -23,7 +23,6 @@
 package com.oracle.truffle.r.engine.interop.ffi.nfi;
 
 import com.oracle.truffle.api.interop.ForeignAccess;
-import com.oracle.truffle.api.interop.InteropException;
 import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java
index 461f4feb45291c91738f7a0b4a37d3966035ab99..29e34173b7b8c6d5f738cc223713f3d0e3e5b7fe 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java
@@ -59,6 +59,7 @@ import com.oracle.truffle.r.runtime.data.RS4Object;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
+import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.ffi.DLL;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 import com.oracle.truffle.r.runtime.nodes.RNode;
@@ -419,7 +420,7 @@ public class MethodsListDispatch {
         private static Object slotRead(MaterializedFrame currentFrame, FrameDescriptor desc, String name) {
             FrameSlot slot = desc.findFrameSlot(name);
             if (slot != null) {
-                Object res = currentFrame.getValue(slot);
+                Object res = FrameSlotChangeMonitor.getValue(slot, currentFrame);
                 if (res != null) {
                     if (res instanceof RPromise) {
                         res = PromiseHelperNode.evaluateSlowPath(null, (RPromise) res);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java
index 19e877dcd4d68fe519dc5343e2730009c81b3b5c..5902eeab4d5f65472b1a3fb9be257f286895a15b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java
@@ -77,6 +77,7 @@ import com.oracle.truffle.r.runtime.data.RPairList;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RPromise.Closure;
 import com.oracle.truffle.r.runtime.env.REnvironment;
+import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
@@ -274,7 +275,7 @@ public class FrameFunctions {
             ArrayList<String> names = new ArrayList<>();
 
             FrameSlot varArgSlot = cframe.getFrameDescriptor().findFrameSlot(ArgumentsSignature.VARARG_NAME);
-            RArgsValuesAndNames varArgParameter = varArgSlot == null ? null : (RArgsValuesAndNames) cframe.getValue(varArgSlot);
+            RArgsValuesAndNames varArgParameter = varArgSlot == null ? null : (RArgsValuesAndNames) FrameSlotChangeMonitor.getValue(varArgSlot, cframe);
 
             for (int i = 0; i < sig.getLength(); i++) {
                 RNode arg = matchedArgNodes[i];
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java
index f9e0d535c66c00b48c701e0bddf5bab7091b6604..c03cc95d031d4f9231600cb604d032d27eb35bf1 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java
@@ -47,6 +47,7 @@ import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.nodes.InternalRSyntaxNodeChildren;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 import com.oracle.truffle.r.runtime.nodes.RSourceSectionNode;
@@ -107,7 +108,7 @@ public abstract class Lapply extends RBuiltinNode {
         @Override
         public Object execute(VirtualFrame frame) {
             try {
-                return extractElementNode.apply(frame, frame.getObject(vectorSlot), new Object[]{frame.getInt(indexSlot)}, RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_TRUE);
+                return extractElementNode.apply(frame, FrameSlotChangeMonitor.getObject(vectorSlot, frame), new Object[]{frame.getInt(indexSlot)}, RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_TRUE);
             } catch (FrameSlotTypeException e) {
                 CompilerDirectives.transferToInterpreter();
                 throw RInternalError.shouldNotReachHere("frame type mismatch in lapply");
@@ -138,11 +139,11 @@ public abstract class Lapply extends RBuiltinNode {
         public abstract Object[] execute(VirtualFrame frame, Object vector, RFunction function);
 
         protected static FrameSlot createIndexSlot(Frame frame) {
-            return frame.getFrameDescriptor().findOrAddFrameSlot(INDEX_NAME, FrameSlotKind.Int);
+            return FrameSlotChangeMonitor.findOrAddFrameSlot(frame.getFrameDescriptor(), INDEX_NAME, FrameSlotKind.Int);
         }
 
         protected static FrameSlot createVectorSlot(Frame frame) {
-            return frame.getFrameDescriptor().findOrAddFrameSlot(VECTOR_NAME, FrameSlotKind.Object);
+            return FrameSlotChangeMonitor.findOrAddFrameSlot(frame.getFrameDescriptor(), VECTOR_NAME, FrameSlotKind.Object);
         }
 
         @Specialization
@@ -154,7 +155,7 @@ public abstract class Lapply extends RBuiltinNode {
                         @Cached("createCallNode(vectorSlot, indexSlot)") RCallNode firstCallNode,
                         @Cached("createCallNode(vectorSlot, indexSlot)") RCallNode callNode) {
             // TODO: R switches to double if x.getLength() is greater than 2^31-1
-            frame.setObject(vectorSlot, vector);
+            FrameSlotChangeMonitor.setObject(frame, vectorSlot, vector);
             int length = lengthNode.executeInteger(frame, vector);
             Object[] result = new Object[length];
             if (length > 0) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MatchFun.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MatchFun.java
index 552a480a2cffd9747309bc42d9127a6066d96b23..5c0d083ede6ab13641b96de7e274e3825bc40883 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MatchFun.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MatchFun.java
@@ -51,6 +51,7 @@ import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.RTypes;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
@@ -194,7 +195,7 @@ public abstract class MatchFun extends RBuiltinNode {
             if (slot == null) {
                 return null;
             } else {
-                return frame.getValue(slot);
+                return FrameSlotChangeMonitor.getValue(slot, frame);
             }
         }
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OnExit.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OnExit.java
index 993863cf59b97d6bc4b9c35b2577dce370ce5c00..2eb897b4f8c21c4ec258d4a7ed501fec7358428e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OnExit.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OnExit.java
@@ -22,52 +22,42 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
 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.PRIMITIVE;
 
 import java.util.ArrayList;
 
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.dsl.Specialization;
 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.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.access.ConstantNode;
-import com.oracle.truffle.r.nodes.access.FrameSlotNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RArguments;
-import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPromise;
-import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.env.frame.RFrameSlot;
-import com.oracle.truffle.r.runtime.ops.na.NAProfile;
 
-/**
- * Placeholder. {@code on.exit} is special (cf {@code .Internal} in that {@code expr} is not
- * evaluated, but {@code add} is. TODO arrange for the {@code expr} be stored with the currently
- * evaluating function using a new slot in {@link RArguments} and run it on function exit.
- */
 @RBuiltin(name = "on.exit", visibility = OFF, kind = PRIMITIVE, parameterNames = {"expr", "add"}, nonEvalArgs = 0, behavior = COMPLEX)
 public abstract class OnExit extends RBuiltinNode {
 
-    @Child private FrameSlotNode onExitSlot = FrameSlotNode.create(RFrameSlot.OnExit, true);
+    @CompilationFinal private FrameSlot onExitSlot;
 
     private final ConditionProfile addProfile = ConditionProfile.createBinaryProfile();
-    private final ConditionProfile existingProfile = ConditionProfile.createBinaryProfile();
+    private final ConditionProfile newProfile = ConditionProfile.createBinaryProfile();
     private final ConditionProfile emptyPromiseProfile = ConditionProfile.createBinaryProfile();
-    private final NAProfile na = NAProfile.create();
-
-    private final BranchProfile invalidateProfile = BranchProfile.create();
 
     static {
         Casts casts = new Casts(OnExit.class);
-        casts.arg("add").asLogicalVector().findFirst(RRuntime.LOGICAL_FALSE);
+        casts.arg("add").asLogicalVector().findFirst(RRuntime.LOGICAL_FALSE).mustNotBeNA().map(toBoolean());
     }
 
     @Override
@@ -75,43 +65,40 @@ public abstract class OnExit extends RBuiltinNode {
         return new Object[]{RNull.instance, RRuntime.LOGICAL_FALSE};
     }
 
+    @SuppressWarnings("unchecked")
     @Specialization
-    protected Object onExit(VirtualFrame frame, RPromise expr, byte add) {
+    protected Object onExit(VirtualFrame frame, RPromise expr, boolean add) {
 
-        if (na.isNA(add)) {
-            throw error(RError.Message.INVALID_ARGUMENT, "add");
+        if (onExitSlot == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            onExitSlot = frame.getFrameDescriptor().findOrAddFrameSlot(RFrameSlot.OnExit, FrameSlotKind.Object);
         }
 
         // the empty (RNull.instance) expression is used to clear on.exit
-        boolean empty = emptyPromiseProfile.profile(expr.isDefaultArgument());
-
-        assert !empty || expr.getRep() instanceof ConstantNode : "only ConstantNode expected for defaulted promise";
-        assert empty || !expr.isEvaluated() : "promise cannot already be evaluated";
-
-        ArrayList<Object> current;
-        FrameSlot slot = onExitSlot.executeFrameSlot(frame);
-        if (existingProfile.profile(onExitSlot.hasValue(frame))) {
-            current = getCurrentList(frame, slot);
-            if (addProfile.profile(!RRuntime.fromLogical(add))) {
-                // add is false, so clear the existing
-                current.clear();
-            }
+        if (emptyPromiseProfile.profile(expr.isDefaultArgument())) {
+            assert expr.getRep() instanceof ConstantNode : "only ConstantNode expected for defaulted promise";
+            frame.setObject(onExitSlot, new ArrayList<>());
         } else {
-            // initialize the list of exit handlers
-            FrameSlotChangeMonitor.setObjectAndInvalidate(frame, slot, current = new ArrayList<>(), false, invalidateProfile);
-        }
-        if (!empty) {
-            current.add(expr.getRep());
+            assert !expr.isEvaluated() : "promise cannot already be evaluated";
+            Object value;
+            try {
+                value = frame.getObject(onExitSlot);
+            } catch (FrameSlotTypeException e) {
+                throw RInternalError.shouldNotReachHere();
+            }
+            ArrayList<Object> list;
+            if (newProfile.profile(value == null)) {
+                // initialize the list of exit handlers
+                frame.setObject(onExitSlot, list = new ArrayList<>());
+            } else {
+                list = (ArrayList<Object>) value;
+                if (addProfile.profile(!add)) {
+                    // add is false, so clear the existing list
+                    list.clear();
+                }
+            }
+            list.add(expr.getRep());
         }
         return RNull.instance;
     }
-
-    @SuppressWarnings("unchecked")
-    private static ArrayList<Object> getCurrentList(VirtualFrame frame, FrameSlot slot) {
-        try {
-            return (ArrayList<Object>) frame.getObject(slot);
-        } catch (FrameSlotTypeException e) {
-            throw RInternalError.shouldNotReachHere();
-        }
-    }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java
index 2cb9365d10f83e6c89a11fba7878740f17dd0b86..e699b28a722a88eee6ce7197b16fa856e64056bb 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java
@@ -47,7 +47,7 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
  */
 abstract class BaseWriteVariableNode extends WriteVariableNode {
 
-    protected BaseWriteVariableNode(Object name) {
+    protected BaseWriteVariableNode(String name) {
         super(name);
     }
 
@@ -118,7 +118,7 @@ abstract class BaseWriteVariableNode extends WriteVariableNode {
 
     private boolean isCurrentValue(Frame frame, FrameSlot frameSlot, Object value) {
         try {
-            return isObjectProfile.profile(frame.isObject(frameSlot)) && isCurrentProfile.profile(frame.getObject(frameSlot) == value);
+            return isObjectProfile.profile(frame.isObject(frameSlot)) && isCurrentProfile.profile(FrameSlotChangeMonitor.getObject(frameSlot, frame) == value);
         } catch (FrameSlotTypeException ex) {
             throw RInternalError.shouldNotReachHere();
         }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/FrameSlotNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/FrameSlotNode.java
index e9ec3546f278c771b5a9b168ec333ea14c271290..c54c9f419a7de9cd3e6165f0fc9c817f1ff3fffe 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/FrameSlotNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/FrameSlotNode.java
@@ -22,8 +22,6 @@
  */
 package com.oracle.truffle.r.nodes.access;
 
-import static com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor.findOrAddFrameSlot;
-
 import com.oracle.truffle.api.Assumption;
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
@@ -37,6 +35,7 @@ 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.api.profiles.ValueProfile;
+import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.env.frame.RFrameSlot;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
@@ -58,19 +57,23 @@ public abstract class FrameSlotNode extends RBaseNode {
         return new UnresolvedFrameSlotNode(name, createIfAbsent);
     }
 
-    public static FrameSlotNode createTemp(Object name, boolean createIfAbsent) {
-        return new UnresolvedFrameSlotNode(name, createIfAbsent);
-    }
-
     public static FrameSlotNode create(RFrameSlot slot, boolean createIfAbsent) {
         return new UnresolvedFrameSlotNode(slot, createIfAbsent);
     }
 
-    public static FrameSlotNode createInitialized(FrameDescriptor frameDescriptor, Object identifier, boolean createIfAbsent) {
+    public static FrameSlotNode createInitialized(FrameDescriptor frameDescriptor, RFrameSlot identifier, boolean createIfAbsent) {
+        return createInitializedInternal(frameDescriptor, identifier, createIfAbsent);
+    }
+
+    private static FrameSlotNode createInitializedInternal(FrameDescriptor frameDescriptor, Object identifier, boolean createIfAbsent) {
         FrameSlotNode newNode;
         FrameSlot frameSlot;
         if (createIfAbsent) {
-            frameSlot = findOrAddFrameSlot(frameDescriptor, identifier, FrameSlotKind.Illegal);
+            if (identifier instanceof String) {
+                frameSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, (String) identifier, FrameSlotKind.Illegal);
+            } else {
+                frameSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, (RFrameSlot) identifier, FrameSlotKind.Illegal);
+            }
         } else {
             frameSlot = frameDescriptor.findFrameSlot(identifier);
         }
@@ -112,7 +115,7 @@ public abstract class FrameSlotNode extends RBaseNode {
         private FrameSlotNode resolveFrameSlot(Frame frame) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             FrameDescriptor frameDescriptor = frame.getFrameDescriptor();
-            FrameSlotNode newNode = createInitialized(frameDescriptor, identifier, createIfAbsent);
+            FrameSlotNode newNode = createInitializedInternal(frameDescriptor, identifier, createIfAbsent);
             return replace(newNode);
         }
     }
@@ -174,7 +177,8 @@ public abstract class FrameSlotNode extends RBaseNode {
         public boolean hasValue(Frame frame) {
             try {
                 Frame typedFrame = frameTypeProfile.profile(frame);
-                return !isObjectProfile.profile(typedFrame.isObject(frameSlot)) || isNullProfile.profile(typedFrame.getObject(frameSlot) != null);
+                return !isObjectProfile.profile(typedFrame.isObject(frameSlot)) ||
+                                isNullProfile.profile(FrameSlotChangeMonitor.getObject(frameSlot, typedFrame) != null);
             } catch (FrameSlotTypeException e) {
                 throw new IllegalStateException();
             }
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 d019c7e235a1d308b441933d9d1a60e923e96583..de421491f537708d3c5aa7632cd4721bfa9e5482 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
@@ -104,7 +104,7 @@ public abstract class RemoveAndAnswerNode extends RNode {
         protected Object doObject(VirtualFrame frame) {
             Object result;
             try {
-                result = frame.getObject(slot);
+                result = FrameSlotChangeMonitor.getObject(slot, frame);
             } catch (FrameSlotTypeException e) {
                 throw RInternalError.shouldNotReachHere();
             }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteLocalFrameVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteLocalFrameVariableNode.java
index 8e953b84205f56107b7a2953ecfc76e6ec387434..56acae1d73b302d92d856f5ddc54366fcce291d5 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteLocalFrameVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteLocalFrameVariableNode.java
@@ -45,11 +45,11 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
 @ImportStatic(FrameSlotKind.class)
 public abstract class WriteLocalFrameVariableNode extends BaseWriteVariableNode {
 
-    public static WriteLocalFrameVariableNode create(Object name, Mode mode, RNode rhs) {
+    public static WriteLocalFrameVariableNode create(String name, Mode mode, RNode rhs) {
         return WriteLocalFrameVariableNodeGen.create(name, mode, rhs);
     }
 
-    public static WriteLocalFrameVariableNode createForRefCount(Object name) {
+    public static WriteLocalFrameVariableNode createForRefCount(String name) {
         return WriteLocalFrameVariableNodeGen.create(name, Mode.INVISIBLE, null);
     }
 
@@ -60,7 +60,7 @@ public abstract class WriteLocalFrameVariableNode extends BaseWriteVariableNode
     private final ConditionProfile isActiveBindingProfile = ConditionProfile.createBinaryProfile();
     @CompilationFinal private Assumption containsNoActiveBinding;
 
-    protected WriteLocalFrameVariableNode(Object name, Mode mode) {
+    protected WriteLocalFrameVariableNode(String name, Mode mode) {
         super(name);
         this.mode = mode;
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteSuperFrameVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteSuperFrameVariableNode.java
index bbc7d82a5b0ba896a54ac9f00fe83370596eb4a5..a389e83a421b23979dea86644709a63e9e5aa1a1 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteSuperFrameVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteSuperFrameVariableNode.java
@@ -49,7 +49,7 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
  */
 abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode {
 
-    protected WriteSuperFrameVariableNode(Object name) {
+    protected WriteSuperFrameVariableNode(String name) {
         super(name);
     }
 
@@ -78,7 +78,7 @@ abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode {
         private final Mode mode;
         @CompilationFinal private Assumption containsNoActiveBinding;
 
-        public ResolvedWriteSuperFrameVariableNode(Object name, Mode mode) {
+        public ResolvedWriteSuperFrameVariableNode(String name, Mode mode) {
             super(name);
             this.mode = mode;
         }
@@ -122,7 +122,7 @@ abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode {
 
         private final Mode mode;
 
-        UnresolvedWriteSuperFrameVariableNode(Object name, Mode mode, RNode rhs) {
+        UnresolvedWriteSuperFrameVariableNode(String name, Mode mode, RNode rhs) {
             super(name);
             this.mode = mode;
             this.rhs = rhs;
@@ -146,7 +146,7 @@ abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode {
                 writeNode = ResolvedWriteSuperFrameVariableNodeGen.create(getName(), mode, rhs, enclosingFrameNode,
                                 FrameSlotNode.create(findOrAddFrameSlot(enclosingFrame.getFrameDescriptor(), getName(), FrameSlotKind.Illegal)));
             } else {
-                ResolvedWriteSuperFrameVariableNode actualWriteNode = ResolvedWriteSuperFrameVariableNodeGen.create(getName(), mode, null, null, FrameSlotNode.createTemp(getName(), false));
+                ResolvedWriteSuperFrameVariableNode actualWriteNode = ResolvedWriteSuperFrameVariableNodeGen.create(getName(), mode, null, null, FrameSlotNode.create(getName(), false));
                 writeNode = new WriteSuperFrameVariableConditionalNode(getName(), actualWriteNode, new UnresolvedWriteSuperFrameVariableNode(getName(), mode, null), rhs);
             }
             replace(writeNode).execute(frame, value, enclosingFrame);
@@ -175,7 +175,7 @@ abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode {
         private final ConditionProfile hasValueProfile = ConditionProfile.createBinaryProfile();
         private final ConditionProfile nullSuperFrameProfile = ConditionProfile.createBinaryProfile();
 
-        WriteSuperFrameVariableConditionalNode(Object name, ResolvedWriteSuperFrameVariableNode writeNode, WriteSuperFrameVariableNode nextNode, RNode rhs) {
+        WriteSuperFrameVariableConditionalNode(String name, ResolvedWriteSuperFrameVariableNode writeNode, WriteSuperFrameVariableNode nextNode, RNode rhs) {
             super(name);
             this.writeNode = writeNode;
             this.nextNode = nextNode;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNode.java
index 9416b60d27eb77218c865980578d564b9467816f..2490221a608110e962270bc69ebbabbdaefc7797 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNode.java
@@ -43,13 +43,13 @@ public abstract class WriteVariableNode extends RNode {
         INVISIBLE
     }
 
-    private final Object name;
+    private final String name;
 
-    protected WriteVariableNode(Object name) {
+    protected WriteVariableNode(String name) {
         this.name = name;
     }
 
-    public final Object getName() {
+    public final String getName() {
         return name;
     }
 
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 2820805cbdebbfd5c7ad3930985b509f600fab85..f9e15b0a6b16368280b78ca0151f666b4f24db0e 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
@@ -466,7 +466,7 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
                 }
             }
             if (frameSlot != null) {
-                Object value = variableFrame.getValue(frameSlot);
+                Object value = FrameSlotChangeMonitor.getObject(frameSlot, variableFrame);
                 if (checkType(frame, value, isNullProfile)) {
                     return value;
                 }
@@ -490,7 +490,7 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
         @TruffleBoundary
         private Object getValue(MaterializedFrame current) {
             FrameSlot slot = current.getFrameDescriptor().findFrameSlot(identifier);
-            return slot == null ? null : current.getValue(slot);
+            return slot == null ? null : FrameSlotChangeMonitor.getValue(slot, current);
         }
 
         @Override
@@ -700,7 +700,7 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
             // see if the current frame has a value of the given name
             FrameSlot frameSlot = current.getFrameDescriptor().findFrameSlot(identifier);
             if (frameSlot != null) {
-                Object value = current.getValue(frameSlot);
+                Object value = FrameSlotChangeMonitor.getValue(frameSlot, current);
 
                 if (value != null) {
                     if (value == RMissing.instance) {
@@ -739,7 +739,7 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
             // see if the current frame has a value of the given name
             FrameSlot frameSlot = current.getFrameDescriptor().findFrameSlot(identifier);
             if (frameSlot != null) {
-                Object value = current.getValue(frameSlot);
+                Object value = FrameSlotChangeMonitor.getValue(frameSlot, current);
 
                 if (value != null) {
                     if (value == RMissing.instance) {
@@ -766,7 +766,7 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
             // see if the current frame has a value of the given name
             FrameSlot frameSlot = current.getFrameDescriptor().findFrameSlot(ArgumentsSignature.VARARG_NAME);
             if (frameSlot != null) {
-                Object value = current.getValue(frameSlot);
+                Object value = FrameSlotChangeMonitor.getValue(frameSlot, current);
 
                 if (value != null) {
                     if (value == RNull.instance) {
@@ -787,6 +787,7 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
         assert variableFrame.getFrameDescriptor() == frameSlot.getFrameDescriptor();
         Object value = variableFrame.getValue(frameSlot);
         if (variableFrame.isObject(frameSlot)) {
+            value = FrameSlotChangeMonitor.getValue(frameSlot, variableFrame);
             seenValueKinds[FrameSlotKind.Object.ordinal()] = true;
         } else if (variableFrame.isByte(frameSlot)) {
             seenValueKinds[FrameSlotKind.Byte.ordinal()] = true;
@@ -802,7 +803,7 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
         assert variableFrame.getFrameDescriptor() == frameSlot.getFrameDescriptor();
         try {
             if (seenValueKinds[FrameSlotKind.Object.ordinal()] && variableFrame.isObject(frameSlot)) {
-                return variableFrame.getObject(frameSlot);
+                return FrameSlotChangeMonitor.getObject(frameSlot, variableFrame);
             } else if (seenValueKinds[FrameSlotKind.Byte.ordinal()] && variableFrame.isByte(frameSlot)) {
                 return variableFrame.getByte(frameSlot);
             } else if (seenValueKinds[FrameSlotKind.Int.ordinal()] && variableFrame.isInt(frameSlot)) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java
index f03c7f17e1a0228857099a11223acf346aa208ef..67abd02ebdf039d2a80818603a91657141c353dd 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java
@@ -31,6 +31,7 @@ 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.frame.Frame;
+import com.oracle.truffle.api.frame.FrameSlot;
 import com.oracle.truffle.api.frame.FrameSlotTypeException;
 import com.oracle.truffle.api.nodes.ExplodeLoop;
 import com.oracle.truffle.api.nodes.Node;
@@ -58,6 +59,7 @@ import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RPromise.Closure;
 import com.oracle.truffle.r.runtime.data.RPromise.PromiseState;
 import com.oracle.truffle.r.runtime.data.RPromise.RPromiseFactory;
+import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 
@@ -285,7 +287,8 @@ public class ArgumentMatcher {
             Frame frame = Utils.getActualCurrentFrame();
             try {
                 // TODO: this error handling code takes many assumptions about the argument types
-                RArgsValuesAndNames varArg = (RArgsValuesAndNames) frame.getObject(frame.getFrameDescriptor().findFrameSlot(ArgumentsSignature.VARARG_NAME));
+                FrameSlot frameSlot = frame.getFrameDescriptor().findFrameSlot(ArgumentsSignature.VARARG_NAME);
+                RArgsValuesAndNames varArg = (RArgsValuesAndNames) FrameSlotChangeMonitor.getObject(frameSlot, frame);
                 RPromise promise = (RPromise) varArg.getArguments()[((VarArgNode) node).getIndex()];
                 return RDeparse.deparseSyntaxElement(promise.getRep().asRSyntaxNode());
             } catch (FrameSlotTypeException | ClassCastException e) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentStatePush.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentStatePush.java
index fe657055d9e020f9955cfe0124f14273b8cafc99..605d628d4f1bc6efcb43f34e55046c5113b593d1 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentStatePush.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentStatePush.java
@@ -23,12 +23,14 @@
 package com.oracle.truffle.r.nodes.function;
 
 import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.FrameSlot;
+import com.oracle.truffle.api.frame.FrameSlotKind;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.profiles.ConditionProfile;
-import com.oracle.truffle.r.nodes.access.WriteLocalFrameVariableNode;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -44,7 +46,7 @@ public abstract class ArgumentStatePush extends Node {
 
     public abstract void executeObject(VirtualFrame frame, Object shareable);
 
-    @Child private WriteLocalFrameVariableNode write;
+    @CompilationFinal private FrameSlot frameSlot;
 
     private final ConditionProfile isRefCountUpdateable = ConditionProfile.createBinaryProfile();
 
@@ -94,11 +96,11 @@ public abstract class ArgumentStatePush extends Node {
         if (isRefCountUpdateable.profile(!shareable.isSharedPermanent())) {
             shareable.incRefCount();
             if (writeArgMask != -1 && !FastROptions.RefCountIncrementOnly.getBooleanValue()) {
-                if (write == null) {
+                if (frameSlot == null) {
                     CompilerDirectives.transferToInterpreterAndInvalidate();
-                    write = insert(WriteLocalFrameVariableNode.createForRefCount(Integer.valueOf(writeArgMask)));
+                    frameSlot = frame.getFrameDescriptor().findOrAddFrameSlot(writeArgMask, FrameSlotKind.Object);
                 }
-                write.execute(frame, shareable);
+                frame.setObject(frameSlot, shareable);
             }
         }
     }
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 984dce3f21d439482c651bf89d9bc433f510eaff..37ff3d56af59c962099bc4223e5b1ca7758c3972 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
@@ -299,18 +299,24 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
             }
             if (runOnExitHandlers) {
                 visibility.executeEndOfFunction(frame);
-                if (!noHandlerStackSlot.isValid() && frame.isObject(handlerStackSlot)) {
-                    try {
-                        RErrorHandling.restoreHandlerStack(frame.getObject(handlerStackSlot));
-                    } catch (FrameSlotTypeException e) {
-                        throw RInternalError.shouldNotReachHere();
+                if (!noHandlerStackSlot.isValid()) {
+                    FrameSlot slot = getHandlerFrameSlot(frame);
+                    if (frame.isObject(slot)) {
+                        try {
+                            RErrorHandling.restoreHandlerStack(frame.getObject(slot));
+                        } catch (FrameSlotTypeException e) {
+                            throw RInternalError.shouldNotReachHere();
+                        }
                     }
                 }
-                if (!noRestartStackSlot.isValid() && frame.isObject(restartStackSlot)) {
-                    try {
-                        RErrorHandling.restoreRestartStack(frame.getObject(restartStackSlot));
-                    } catch (FrameSlotTypeException e) {
-                        throw RInternalError.shouldNotReachHere();
+                if (!noRestartStackSlot.isValid()) {
+                    FrameSlot slot = getRestartFrameSlot(frame);
+                    if (frame.isObject(slot)) {
+                        try {
+                            RErrorHandling.restoreRestartStack(frame.getObject(slot));
+                        } catch (FrameSlotTypeException e) {
+                            throw RInternalError.shouldNotReachHere();
+                        }
                     }
                 }
 
@@ -431,7 +437,7 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
     @SuppressWarnings("unchecked")
     private static ArrayList<Object> getCurrentOnExitList(VirtualFrame frame, FrameSlot slot) {
         try {
-            return (ArrayList<Object>) frame.getObject(slot);
+            return (ArrayList<Object>) FrameSlotChangeMonitor.getObject(slot, frame);
         } catch (FrameSlotTypeException e) {
             throw RInternalError.shouldNotReachHere();
         }
@@ -491,20 +497,24 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
     public FrameSlot getRestartFrameSlot(VirtualFrame frame) {
         if (noRestartStackSlot.isValid()) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            restartStackSlot = frame.getFrameDescriptor().findOrAddFrameSlot(RFrameSlot.RestartStack);
             noRestartStackSlot.invalidate();
         }
-        assert restartStackSlot != null;
+        if (restartStackSlot == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            restartStackSlot = frame.getFrameDescriptor().findOrAddFrameSlot(RFrameSlot.RestartStack);
+        }
         return restartStackSlot;
     }
 
     public FrameSlot getHandlerFrameSlot(VirtualFrame frame) {
         if (noHandlerStackSlot.isValid()) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            handlerStackSlot = frame.getFrameDescriptor().findOrAddFrameSlot(RFrameSlot.HandlerStack);
             noHandlerStackSlot.invalidate();
         }
-        assert handlerStackSlot != null;
+        if (handlerStackSlot == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            handlerStackSlot = frame.getFrameDescriptor().findOrAddFrameSlot(RFrameSlot.HandlerStack);
+        }
         return handlerStackSlot;
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GetMissingValueNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GetMissingValueNode.java
index 96c960da3eccb9c1917ceaf9452a349446b1c4de..d8cffeb8f5b1752ec8bb3c8018fcf76aa99b9353 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GetMissingValueNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GetMissingValueNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -29,6 +29,7 @@ import com.oracle.truffle.api.frame.FrameSlotTypeException;
 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.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 /**
@@ -78,7 +79,7 @@ public abstract class GetMissingValueNode extends RBaseNode {
             }
             if (isObjectProfile.profile(frame.isObject(slot))) {
                 try {
-                    return frame.getObject(slot);
+                    return FrameSlotChangeMonitor.getObject(slot, frame);
                 } catch (FrameSlotTypeException e) {
                     return null;
                 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PostProcessArgumentsNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PostProcessArgumentsNode.java
index e808377fefc5bedf97ad147c5d5950c6cfbd272a..bb1d668a601feb489a4796447bbc39a31ca67dd3 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PostProcessArgumentsNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PostProcessArgumentsNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,11 +23,15 @@
 package com.oracle.truffle.r.nodes.function;
 
 import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+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.ExplodeLoop;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.utilities.AssumedValue;
-import com.oracle.truffle.r.nodes.access.variables.LocalReadVariableNode;
+import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RShareable;
 import com.oracle.truffle.r.runtime.nodes.RNode;
@@ -38,7 +42,7 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
  */
 public final class PostProcessArgumentsNode extends RNode {
 
-    @Children private final LocalReadVariableNode[] sequence;
+    @CompilationFinal(dimensions = 1) private final FrameSlot[] frameSlots;
 
     // stays the same during cloning
     private final AssumedValue<Integer> transArgsBitSet;
@@ -47,7 +51,7 @@ public final class PostProcessArgumentsNode extends RNode {
     private final ConditionProfile isRefCountUpdateable = ConditionProfile.createBinaryProfile();
 
     private PostProcessArgumentsNode(int length) {
-        this.sequence = new LocalReadVariableNode[Math.min(length, ArgumentStatePush.MAX_COUNTED_ARGS)];
+        this.frameSlots = new FrameSlot[Math.min(length, ArgumentStatePush.MAX_COUNTED_ARGS)];
         this.transArgsBitSet = new AssumedValue<>("PostProcessArgumentsNode.transArgsBitSet", 0);
     }
 
@@ -56,7 +60,7 @@ public final class PostProcessArgumentsNode extends RNode {
     }
 
     public int getLength() {
-        return sequence.length;
+        return frameSlots.length;
     }
 
     @Override
@@ -64,14 +68,19 @@ public final class PostProcessArgumentsNode extends RNode {
     public Object execute(VirtualFrame frame) {
         int bits = transArgsBitSet.get();
         if (bits != 0) {
-            for (int i = 0; i < sequence.length; i++) {
+            for (int i = 0; i < frameSlots.length; i++) {
                 int mask = 1 << i;
                 if ((bits & mask) != 0) {
-                    if (sequence[i] == null) {
+                    if (frameSlots[i] == null) {
                         CompilerDirectives.transferToInterpreterAndInvalidate();
-                        sequence[i] = insert(LocalReadVariableNode.create(Integer.valueOf(mask), false));
+                        frameSlots[i] = frame.getFrameDescriptor().findOrAddFrameSlot(mask, FrameSlotKind.Object);
+                    }
+                    RShareable s;
+                    try {
+                        s = (RShareable) frame.getObject(frameSlots[i]);
+                    } catch (FrameSlotTypeException e) {
+                        throw RInternalError.shouldNotReachHere();
                     }
-                    RShareable s = (RShareable) sequence[i].execute(frame);
                     if (isNonNull.profile(s != null)) {
                         if (isRefCountUpdateable.profile(!s.isSharedPermanent())) {
                             s.decRefCount();
@@ -84,7 +93,7 @@ public final class PostProcessArgumentsNode extends RNode {
     }
 
     public boolean updateBits(int index) {
-        if (index < sequence.length) {
+        if (index < frameSlots.length) {
             int bits = transArgsBitSet.get();
             int newBits = bits | (1 << index);
             if (newBits != bits) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseHelperNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseHelperNode.java
index 303eef216502ce3d97149df0dee01020a522787c..f2407db5244cbdc42c9c845c5748c992d1e4b5a4 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseHelperNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseHelperNode.java
@@ -51,6 +51,7 @@ import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RPromise.EagerPromise;
 import com.oracle.truffle.r.runtime.data.RPromise.PromiseState;
 import com.oracle.truffle.r.runtime.data.RShareable;
+import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 /**
@@ -101,7 +102,7 @@ public class PromiseHelperNode extends RBaseNode {
 
                 // Try to read it...
                 try {
-                    Object value = frame.getObject(slot);
+                    Object value = FrameSlotChangeMonitor.getObject(slot, frame);
 
                     // If it's a promise, deoptimize it!
                     if (value instanceof RPromise) {
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 a921d98f18b85fe3ff40d16aa6b8bcaf3e726c4b..1350b4bd2ad4649fb009afc620f639126176aef5 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
@@ -96,6 +96,7 @@ import com.oracle.truffle.r.runtime.data.RPromise.Closure;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
 import com.oracle.truffle.r.runtime.env.REnvironment;
+import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 import com.oracle.truffle.r.runtime.nodes.RFastPathNode;
 import com.oracle.truffle.r.runtime.nodes.RNode;
@@ -640,7 +641,7 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
         @Override
         public Object execute(VirtualFrame frame) {
             try {
-                return frame.getObject(slot);
+                return FrameSlotChangeMonitor.getObject(slot, frame);
             } catch (FrameSlotTypeException e) {
                 throw RInternalError.shouldNotReachHere();
             }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RMissingHelper.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RMissingHelper.java
index 5a6a72b6b0e0fdf8223da3dc5d8055ff95f7feda..9516238bf64adca43dc6a9c552d5c9ce08ae750f 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RMissingHelper.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RMissingHelper.java
@@ -32,6 +32,7 @@ import com.oracle.truffle.r.runtime.data.REmpty;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RPromise.EagerPromise;
+import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 
 /**
  * This class implements the behavior for {@link RMissing} which is needed inside this module, as it
@@ -91,7 +92,7 @@ public class RMissingHelper {
 
         // Read name's value
         try {
-            return frame.getObject(frameSlot);
+            return FrameSlotChangeMonitor.getObject(frameSlot, frame);
         } catch (FrameSlotTypeException e) {
             return null;
         }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java
index bbce4bf49e0fc2c3d86a9cc6fc882f2d635a9e4e..e7921cf9d346bc1fe281a94b8b7c96ec6d2e4837 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java
@@ -45,6 +45,7 @@ import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
+import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 public abstract class S3FunctionLookupNode extends RBaseNode {
@@ -437,7 +438,7 @@ public abstract class S3FunctionLookupNode extends RBaseNode {
                     return null;
                 }
                 try {
-                    return genericDefFrame.getObject(slot);
+                    return FrameSlotChangeMonitor.getObject(slot, genericDefFrame);
                 } catch (FrameSlotTypeException e) {
                     throw RInternalError.shouldNotReachHere();
                 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/TemporarySlotNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/TemporarySlotNode.java
index 03e13b0d068b6a277a8511029d130b4bab6ba20b..1d7589976a4b39ca52e90a91ab45547ccd373d79 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/TemporarySlotNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/TemporarySlotNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -30,10 +30,12 @@ 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.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
+import com.oracle.truffle.r.runtime.env.frame.RFrameSlot;
 
 public final class TemporarySlotNode extends Node {
 
-    private static final Object[] defaultTempIdentifiers = new Object[]{new Object(), new Object(), new Object(), new Object(), new Object(), new Object(), new Object(), new Object()};
+    private static final RFrameSlot[] defaultTempIdentifiers = new RFrameSlot[]{RFrameSlot.createTemp(true), RFrameSlot.createTemp(true), RFrameSlot.createTemp(true), RFrameSlot.createTemp(true)};
 
     @CompilationFinal private FrameSlot tempSlot;
     private int tempIdentifier;
@@ -41,7 +43,7 @@ public final class TemporarySlotNode extends Node {
     public FrameSlot initialize(VirtualFrame frame, Object value) {
         if (tempSlot == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            tempSlot = frame.getFrameDescriptor().findOrAddFrameSlot(defaultTempIdentifiers[0], FrameSlotKind.Object);
+            tempSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frame.getFrameDescriptor(), defaultTempIdentifiers[0], FrameSlotKind.Object);
         }
         FrameSlot slot = tempSlot;
         try {
@@ -50,8 +52,8 @@ public final class TemporarySlotNode extends Node {
                 // keep the complete loop in the slow path
                 do {
                     tempIdentifier++;
-                    Object identifier = tempIdentifier < defaultTempIdentifiers.length ? defaultTempIdentifiers[tempIdentifier] : new Object();
-                    tempSlot = slot = frame.getFrameDescriptor().findOrAddFrameSlot(identifier, FrameSlotKind.Object);
+                    RFrameSlot identifier = tempIdentifier < defaultTempIdentifiers.length ? defaultTempIdentifiers[tempIdentifier] : RFrameSlot.createTemp(true);
+                    tempSlot = slot = FrameSlotChangeMonitor.findOrAddFrameSlot(frame.getFrameDescriptor(), identifier, FrameSlotKind.Object);
                     if (frame.getObject(slot) == null) {
                         break;
                     }
@@ -61,16 +63,16 @@ public final class TemporarySlotNode extends Node {
             CompilerDirectives.transferToInterpreter();
             throw RInternalError.shouldNotReachHere();
         }
-        frame.setObject(slot, value);
+        FrameSlotChangeMonitor.setObject(frame, slot, value);
         return slot;
     }
 
     public static void cleanup(VirtualFrame frame, Object object, FrameSlot tempSlot) {
         try {
-            assert frame.getObject(tempSlot) == object;
+            assert FrameSlotChangeMonitor.getObject(tempSlot, frame) == object;
         } catch (FrameSlotTypeException e) {
             throw RInternalError.shouldNotReachHere();
         }
-        frame.setObject(tempSlot, null);
+        FrameSlotChangeMonitor.setObject(frame, tempSlot, null);
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitCallNode.java
index 7f6354db2fe120fa7eb56e0ebabe4607b0d7475f..b856c58620a7b6e111ee35ca431ca0c57779f4a5 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitCallNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitCallNode.java
@@ -22,50 +22,52 @@
  */
 package com.oracle.truffle.r.nodes.function.call;
 
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.FrameSlot;
+import com.oracle.truffle.api.frame.FrameSlotKind;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.nodes.access.FrameSlotNode;
 import com.oracle.truffle.r.nodes.function.RCallBaseNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
+import com.oracle.truffle.r.runtime.env.frame.RFrameSlot;
 
 /**
  * Helper node that allows to call a given function with explicit arguments.
  */
 public abstract class RExplicitCallNode extends Node {
+
     public static RExplicitCallNode create() {
         return RExplicitCallNodeGen.create();
     }
 
     public abstract Object execute(VirtualFrame frame, RFunction function, RArgsValuesAndNames args);
 
+    private final RFrameSlot argsIdentifier = RFrameSlot.createTemp(true);
+    @CompilationFinal private FrameSlot argsFrameSlot;
+
     @Specialization
-    Object doCall(VirtualFrame frame, RFunction function, RArgsValuesAndNames args,
-                    @SuppressWarnings("unused") @Cached("createArgsIdentifier()") Object argsIdentifier,
-                    @Cached("createExplicitCall(argsIdentifier)") RCallBaseNode call,
-                    @Cached("createFrameSlotNode(argsIdentifier)") FrameSlotNode argumentsSlot) {
-        FrameSlot argsFrameSlot = argumentsSlot.executeFrameSlot(frame);
+    protected Object doCall(VirtualFrame frame, RFunction function, RArgsValuesAndNames args,
+                    @Cached("createExplicitCall()") RCallBaseNode call) {
+        if (argsFrameSlot == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            argsFrameSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frame.getFrameDescriptor(), argsIdentifier, FrameSlotKind.Object);
+        }
         try {
             frame.setObject(argsFrameSlot, args);
+            FrameSlotChangeMonitor.setObject(frame, argsFrameSlot, args);
             return call.execute(frame, function);
         } finally {
-            frame.setObject(argsFrameSlot, null);
+            FrameSlotChangeMonitor.setObject(frame, argsFrameSlot, null);
         }
     }
 
-    static Object createArgsIdentifier() {
-        return new Object();
-    }
-
-    static RCallBaseNode createExplicitCall(Object argsIdentifier) {
+    protected RCallBaseNode createExplicitCall() {
         return RCallNode.createExplicitCall(argsIdentifier);
     }
-
-    static FrameSlotNode createFrameSlotNode(Object argsIdentifier) {
-        return FrameSlotNode.createTemp(argsIdentifier, true);
-    }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/CollectArgumentsNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/CollectArgumentsNode.java
index 0be21e8e3d00446e09e77aa542f44d923380e5a2..9b7f4252103eae8aecd36871d8aa1461121922be 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/CollectArgumentsNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/CollectArgumentsNode.java
@@ -38,6 +38,7 @@ import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RPromise;
+import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 public abstract class CollectArgumentsNode extends RBaseNode {
@@ -88,7 +89,7 @@ public abstract class CollectArgumentsNode extends RBaseNode {
             if (slot == null) {
                 result[i] = RMissing.instance;
             } else {
-                result[i] = frame.getValue(slot);
+                result[i] = FrameSlotChangeMonitor.getValue(slot, frame);
             }
         }
         return result;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/CollectGenericArgumentsNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/CollectGenericArgumentsNode.java
index 6b57fd298bb1dd64f6d4abcabd77b55620bf80cc..f8b51386ac4fa75a5a54bd348c0889c2dae88ed1 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/CollectGenericArgumentsNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/CollectGenericArgumentsNode.java
@@ -45,6 +45,7 @@ import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RSymbol;
+import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 // transcribed from /src/library/methods/src/methods_list_dispatch.c (R_dispatch_generic function)
@@ -117,7 +118,7 @@ public abstract class CollectGenericArgumentsNode extends RBaseNode {
             if (slot == null) {
                 result[i] = "missing";
             } else {
-                Object value = frame.getValue(slot);
+                Object value = FrameSlotChangeMonitor.getValue(slot, frame);
                 if (value instanceof RPromise) {
                     value = PromiseHelperNode.evaluateSlowPath(null, (RPromise) value);
                 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
index 19bb59be1350811d3a3a9589b789120af257b0a2..d533301e924c73398f388816cdb280eda7b6e5ce 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
@@ -565,6 +565,7 @@ public final class Utils {
     }
 
     private static void dumpFrame(StringBuilder str, CallTarget callTarget, Frame frame, boolean printFrameSlots, boolean isVirtual) {
+        CompilerAsserts.neverPartOfCompilation();
         try {
             CompilerAsserts.neverPartOfCompilation();
             if (str.length() > 0) {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/FrameSlotChangeMonitor.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/FrameSlotChangeMonitor.java
index 0f4f3a09ad999efe14c1b044aad6d5cfe733f300..323f4d154d2e181b0be9d8d36bd597d5e602221e 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/FrameSlotChangeMonitor.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/FrameSlotChangeMonitor.java
@@ -40,6 +40,7 @@ import com.oracle.truffle.api.frame.Frame;
 import com.oracle.truffle.api.frame.FrameDescriptor;
 import com.oracle.truffle.api.frame.FrameSlot;
 import com.oracle.truffle.api.frame.FrameSlotKind;
+import com.oracle.truffle.api.frame.FrameSlotTypeException;
 import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.nodes.InvalidAssumptionException;
 import com.oracle.truffle.api.profiles.BranchProfile;
@@ -621,6 +622,10 @@ public final class FrameSlotChangeMonitor {
         checkAndInvalidate(frame, frameSlot, isNonLocal, invalidateProfile);
     }
 
+    public static void setObject(Frame frame, FrameSlot frameSlot, Object newValue) {
+        frame.setObject(frameSlot, newValue);
+    }
+
     public static void setActiveBinding(Frame frame, FrameSlot frameSlot, ActiveBinding newValue, boolean isNonLocal, BranchProfile invalidateProfile) {
         frame.setObject(frameSlot, newValue);
         FrameSlotInfoImpl info = getFrameSlotInfo(frameSlot);
@@ -639,8 +644,10 @@ public final class FrameSlotChangeMonitor {
         frameDescriptors.put(handleBaseNamespaceEnv(frame), new FrameDescriptorMetaData(name, frame));
     }
 
-    public static synchronized void initializeFunctionFrameDescriptor(String name, FrameDescriptor frameDescriptor) {
+    public static synchronized FrameDescriptor initializeFunctionFrameDescriptor(String name, FrameDescriptor frameDescriptor) {
+        CompilerAsserts.neverPartOfCompilation();
         frameDescriptors.put(frameDescriptor, new FrameDescriptorMetaData(name, null));
+        return frameDescriptor;
     }
 
     public static synchronized Assumption getEnclosingFrameDescriptorAssumption(FrameDescriptor descriptor) {
@@ -672,4 +679,12 @@ public final class FrameSlotChangeMonitor {
     public static boolean isValidFrameDescriptor(FrameDescriptor frameDesc) {
         return getMetaData(frameDesc) != null;
     }
+
+    public static Object getObject(FrameSlot slot, Frame frame) throws FrameSlotTypeException {
+        return frame.getObject(slot);
+    }
+
+    public static Object getValue(FrameSlot slot, Frame frame) {
+        return frame.getValue(slot);
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/REnvTruffleFrameAccess.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/REnvTruffleFrameAccess.java
index 2c62896ff5aa889a89b1eccd9b9dd37f237debb6..cd23e990be0ad12876dec80923cdcf24c0e3f095 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/REnvTruffleFrameAccess.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/REnvTruffleFrameAccess.java
@@ -71,7 +71,7 @@ public final class REnvTruffleFrameAccess extends REnvFrameAccess {
         if (slot == null) {
             return null;
         } else {
-            Object value = frame.getValue(slot);
+            Object value = FrameSlotChangeMonitor.getValue(slot, frame);
             // special treatment for active binding: call bound function
             if (ActiveBinding.isActiveBinding(value)) {
                 return ((ActiveBinding) value).readValue();
@@ -159,7 +159,8 @@ public final class REnvTruffleFrameAccess extends REnvFrameAccess {
         ArrayList<String> matchedNamesList = new ArrayList<>(names.length);
         for (int i = 0; i < names.length; i++) {
             String name = names[i];
-            if (frame.getValue(fd.findFrameSlot(name)) == null) {
+            FrameSlot frameSlot = fd.findFrameSlot(name);
+            if (FrameSlotChangeMonitor.getValue(frameSlot, frame) == null) {
                 continue;
             }
             if (REnvironment.includeName(name, allNames, pattern)) {
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 e303fac170654063244a601a7b1891c87fb7e9cb..9b407b153627a1b7e4a5f18813f3589f0ccbe64f 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, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -31,12 +31,37 @@ 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 {
+public final class RFrameSlot {
+    private final String name;
+    private final boolean multiSlot;
+
+    private RFrameSlot(String name, boolean multiSlot) {
+        this.name = name;
+        this.multiSlot = multiSlot;
+    }
+
+    @Override
+    public String toString() {
+        return name == null ? "TempFrameSlot" : name;
+    }
+
+    public boolean isTemp() {
+        return name == null;
+    }
+
+    public boolean isMultiSlot() {
+        return multiSlot;
+    }
+
+    public static RFrameSlot createTemp(boolean multiSlot) {
+        return new RFrameSlot(null, multiSlot);
+    }
+
     /**
      * 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,
+    public static final RFrameSlot OnExit = new RFrameSlot("OnExit", false);
     /**
      * This frame slot is used to track result visibility. It can contain one of three values:
      * <ul>
@@ -51,13 +76,17 @@ public enum RFrameSlot {
      * each call site, the value of {@link RCaller#getVisibility()} is extracted and stored into the
      * frame slot.
      */
-    Visibility,
+    public static final RFrameSlot Visibility = new RFrameSlot("Visibility", false);
     /**
      * Used to save the handler stack in frames that modify it.
      */
-    HandlerStack,
+    public static final RFrameSlot HandlerStack = new RFrameSlot("HandlerStack", false);
     /**
      * Used to save the restart stack in frames that modify it.
      */
-    RestartStack
+    public static final RFrameSlot RestartStack = new RFrameSlot("RestartStack", false);
+
+    public static RFrameSlot[] values() {
+        return new RFrameSlot[]{OnExit, Visibility, HandlerStack, RestartStack};
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StdUpCallsRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StdUpCallsRFFI.java
index 75132c6fe4a6423d490a0403792608614481cffd..6fe58f99a7fd50204785575b447168668c865317 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StdUpCallsRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StdUpCallsRFFI.java
@@ -22,7 +22,6 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BaseRConnection;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RExternalPtr;
 import com.oracle.truffle.r.runtime.data.RIntVector;
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 02ccdb2128867d6684c14a333352a3e2f13a0ebe..131551f152d6aef9454bfa7a171310b432ee6791 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
@@ -270,7 +270,7 @@ public class FastRDebugTest {
                 frame.forEach(var -> {
                     // skip synthetic slots
                     for (RFrameSlot slot : RFrameSlot.values()) {
-                        if (slot.name().equals(var.getName())) {
+                        if (slot.toString().equals(var.getName())) {
                             return;
                         }
                     }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/RFFIUpCallMethodGenerate.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/RFFIUpCallMethodGenerate.java
index 03d88811d97877fd946187de084c38fd6bc1dc72..425f866eadf01fc9692976e6c412df6dfdca39c2 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/RFFIUpCallMethodGenerate.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/RFFIUpCallMethodGenerate.java
@@ -30,7 +30,6 @@ import java.util.Comparator;
 
 import com.oracle.truffle.r.nodes.ffi.RFFIUpCallMethod;
 import com.oracle.truffle.r.runtime.ffi.RFFICstring;
-import com.oracle.truffle.r.runtime.ffi.UpCallsRFFI;
 
 /**
  * Generates the entries for {@link RFFIUpCallMethod}.