diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Assign.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Assign.java
index 316c04031c5cfebdcd71e84146bfc4551d2f11aa..ca7212220000d495c34e3fb5da200a13d575d4a5 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Assign.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Assign.java
@@ -26,14 +26,24 @@ import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElemen
 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;
+import static com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor.findOrAddFrameSlot;
 
 import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.FrameDescriptor;
+import com.oracle.truffle.api.frame.FrameSlotKind;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.LoopNode;
 import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.api.profiles.ValueProfile;
+import com.oracle.truffle.r.nodes.access.FrameSlotNode;
+import com.oracle.truffle.r.nodes.access.WriteSuperFrameVariableNode.ResolvedWriteSuperFrameVariableNode;
+import com.oracle.truffle.r.nodes.access.WriteSuperFrameVariableNodeFactory.ResolvedWriteSuperFrameVariableNodeGen;
+import com.oracle.truffle.r.nodes.access.WriteVariableNode.Mode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.builtin.base.AssignNodeGen.AssignInternalNodeGen;
 import com.oracle.truffle.r.nodes.function.opt.ShareObjectNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -104,9 +114,10 @@ public abstract class Assign extends RBuiltinNode.Arg4 {
      * The general case that requires searching the environment hierarchy.
      */
     @Specialization
-    protected Object assign(RAbstractStringVector xVec, Object value, REnvironment envir, byte inherits,
+    protected Object assign(VirtualFrame frame, RAbstractStringVector xVec, Object value, REnvironment envir, byte inherits,
                     @Cached("createBinaryProfile()") ConditionProfile inheritsProfile,
-                    @Cached("create()") ShareObjectNode share) {
+                    @Cached("create()") ShareObjectNode share,
+                    @Cached("create()") AssignInternalNode assign) {
         String x = checkVariable(xVec);
         REnvironment env = envir;
         if (inheritsProfile.profile(RRuntime.fromLogical(inherits))) {
@@ -127,11 +138,39 @@ public abstract class Assign extends RBuiltinNode.Arg4 {
                 throw error(RError.Message.CANNOT_ASSIGN_IN_EMPTY_ENV);
             }
         }
-        try {
-            env.put(x, share.execute(value));
-        } catch (PutException ex) {
-            throw error(ex);
-        }
+        assign.execute(frame, env, x, share.execute(value));
         return value;
     }
+
+    protected abstract static class AssignInternalNode extends RBaseNode {
+
+        public static AssignInternalNode create() {
+            return AssignInternalNodeGen.create();
+        }
+
+        protected final ValueProfile frameAccessProfile = ValueProfile.createClassProfile();
+
+        public abstract void execute(VirtualFrame frame, REnvironment env, String name, Object value);
+
+        protected static ResolvedWriteSuperFrameVariableNode createWrite(String name, FrameDescriptor envDesc) {
+            return ResolvedWriteSuperFrameVariableNodeGen.create(name, Mode.REGULAR, null, null, FrameSlotNode.create(findOrAddFrameSlot(envDesc, name, FrameSlotKind.Illegal)));
+        }
+
+        @Specialization(guards = {"env.getFrame(frameAccessProfile).getFrameDescriptor() == envDesc", "write.getName().equals(name)"})
+        protected void assignCached(VirtualFrame frame, REnvironment env, @SuppressWarnings("unused") String name, Object value,
+                        @Cached("env.getFrame().getFrameDescriptor()") @SuppressWarnings("unused") FrameDescriptor envDesc,
+                        @Cached("createWrite(name, envDesc)") ResolvedWriteSuperFrameVariableNode write) {
+            write.execute(frame, value, env.getFrame(frameAccessProfile));
+        }
+
+        @Specialization(replaces = "assignCached")
+        @TruffleBoundary
+        protected void assign(REnvironment env, String name, Object value) {
+            try {
+                env.put(name, value);
+            } catch (PutException ex) {
+                throw error(ex);
+            }
+        }
+    }
 }
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 90f278cc2dd0b4fed1799c3b25bbf719138274c3..128c9012ec34d7580901d09ac8c6dd61c7b29dc4 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
@@ -47,7 +47,7 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
  *
  * The state starts out a "unresolved" and transforms to "resolved".
  */
-abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode {
+public abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode {
 
     protected WriteSuperFrameVariableNode(String name) {
         super(name);
@@ -57,7 +57,7 @@ abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode {
         return new UnresolvedWriteSuperFrameVariableNode(name, mode, rhs);
     }
 
-    protected abstract void execute(VirtualFrame frame, Object value, MaterializedFrame enclosingFrame);
+    public abstract void execute(VirtualFrame frame, Object value, MaterializedFrame enclosingFrame);
 
     @Override
     public final Object execute(VirtualFrame frame) {
@@ -68,7 +68,7 @@ abstract class WriteSuperFrameVariableNode extends BaseWriteVariableNode {
 
     @NodeChild(value = "enclosingFrame", type = AccessEnclosingFrameNode.class)
     @NodeChild(value = "frameSlotNode", type = FrameSlotNode.class)
-    protected abstract static class ResolvedWriteSuperFrameVariableNode extends WriteSuperFrameVariableNode {
+    public abstract static class ResolvedWriteSuperFrameVariableNode extends WriteSuperFrameVariableNode {
 
         private final ValueProfile storedObjectProfile = ValueProfile.createClassProfile();
         private final BranchProfile invalidateProfile = BranchProfile.create();