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 573f5e5c89cda743de005833d378aca9bb4d742d..b880d98d8649d7eb23de404bc596058810cec6e5 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
@@ -69,7 +69,7 @@ abstract class BaseWriteVariableNode extends WriteVariableNode {
      * replacement forms of vector updates where a vector is assigned to a temporary (visible)
      * variable and then, again, to the original variable (which would cause the vector to be copied
      * each time); (non-Javadoc)
-     * 
+     *
      * @see com.oracle.truffle.r.nodes.access.AbstractWriteVariableNode#shareObjectValue(com.oracle.
      * truffle .api.frame.Frame, com.oracle.truffle.api.frame.FrameSlot, java.lang.Object,
      * com.oracle.truffle.r.nodes.access.AbstractWriteVariableNode.Mode, boolean)
@@ -77,7 +77,6 @@ abstract class BaseWriteVariableNode extends WriteVariableNode {
     protected final Object shareObjectValue(Frame frame, FrameSlot frameSlot, Object value, Mode mode, boolean isSuper) {
         CompilerAsserts.compilationConstant(mode);
         CompilerAsserts.compilationConstant(isSuper);
-        Object newValue = value;
         // for the meaning of INVISIBLE mode see the comment preceding the current method;
         // also change state when assigning to the enclosing frame as there must
         // be a distinction between variables with the same name defined in
@@ -85,11 +84,17 @@ abstract class BaseWriteVariableNode extends WriteVariableNode {
         // x<-1:3; f<-function() { x[2]<-10; x[2]<<-100; x[2]<-1000 }; f()
         // or
         // x<-c(1); f<-function() { x[[1]]<<-x[[1]] + 1; x }; a<-f(); b<-f(); c(a,b)
-        if ((mode != Mode.INVISIBLE || isSuper) && !isCurrentProfile.profile(isCurrentValue(frame, frameSlot, value))) {
+        if ((mode != Mode.INVISIBLE || isSuper)) {
             if (isShareableProfile.profile(value instanceof RShareable)) {
+
+                // this comparison does not work consistently for boxing objects, so it's important
+                // to do the RShareable check first.
+                if (isCurrentProfile.profile(isCurrentValue(frame, frameSlot, value))) {
+                    return value;
+                }
                 RShareable rShareable = (RShareable) shareableProfile.profile(value);
                 if (mode == Mode.COPY) {
-                    newValue = rShareable.copy();
+                    return rShareable.copy();
                 } else {
                     if (isRefCountUpdateable.profile(!rShareable.isSharedPermanent())) {
                         if (isSuper) {
@@ -104,7 +109,7 @@ abstract class BaseWriteVariableNode extends WriteVariableNode {
                 }
             }
         }
-        return newValue;
+        return value;
     }
 
     private static boolean isCurrentValue(Frame frame, FrameSlot frameSlot, Object value) {
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 1e3daa3e1987b05e8fc808460b9c39af3df477f5..831e6a87db91e4521a947a2fd01e0bd54827913b 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
@@ -495,6 +495,7 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
 
         private final LookupResult lookup;
         private final ValueProfile frameProfile = ValueProfile.createClassProfile();
+        private final ConditionProfile nullValueProfile = kind == ReadKind.Silent ? null : ConditionProfile.createBinaryProfile();
 
         private LookupLevel(LookupResult lookup) {
             this.lookup = lookup;
@@ -509,7 +510,7 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
             } else {
                 value = lookup.getValue();
             }
-            if (kind != ReadKind.Silent && value == null) {
+            if (kind != ReadKind.Silent && nullValueProfile.profile(value == null)) {
                 throw RError.error(RError.SHOW_CALLER, mode == RType.Function ? RError.Message.UNKNOWN_FUNCTION : RError.Message.UNKNOWN_OBJECT, identifier);
             }
             return value;
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 f5f15fd1f3875d472357af0b861499d6b405ce52..7d19a9c479e82585435958ebdba4051a6da8f45e 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
@@ -59,7 +59,7 @@ public final class FrameSlotChangeMonitor {
      * result based on the system's knowledge about the hierarchy of environments and the stable
      * values of certain bindings. Most function lookups can be answered based only on this
      * information.
-     * 
+     *
      * These lookups are stored for caching and invalidation, i.e., to save on repeated lookups and
      * to invalidate lookups in case the environment hierarchy changes.
      */
@@ -458,18 +458,44 @@ public final class FrameSlotChangeMonitor {
             return stableValue != null;
         }
 
+        /*
+         * Special cases for primitive types to force value (instead of identity) comparison.
+         */
+
+        public void setValue(byte value, FrameSlot slot) {
+            if (stableValue != null && (!(stableValue.getValue() instanceof Byte) || ((byte) stableValue.getValue()) != value)) {
+                invalidateStableValue(value, slot);
+            }
+        }
+
+        public void setValue(int value, FrameSlot slot) {
+            if (stableValue != null && (!(stableValue.getValue() instanceof Integer) || ((int) stableValue.getValue()) != value)) {
+                invalidateStableValue(value, slot);
+            }
+        }
+
+        public void setValue(double value, FrameSlot slot) {
+            if (stableValue != null && (!(stableValue.getValue() instanceof Double) || ((double) stableValue.getValue()) != value)) {
+                invalidateStableValue(value, slot);
+            }
+        }
+
         public void setValue(Object value, FrameSlot slot) {
             if (stableValue != null && stableValue.getValue() != value) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                stableValue.getAssumption().invalidate();
-                if (invalidationCount > 0) {
-                    invalidationCount--;
-                    out("setting singleton value %s = %s", slot.getIdentifier(), value == null ? "null" : value.getClass());
-                    stableValue = new StableValue<>(value, String.valueOf(slot.getIdentifier()));
-                } else {
-                    out("setting non-singleton value %s", slot.getIdentifier());
-                    stableValue = null;
-                }
+                invalidateStableValue(value, slot);
+            }
+        }
+
+        private void invalidateStableValue(Object value, FrameSlot slot) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            stableValue.getAssumption().invalidate();
+            if (invalidationCount > 0) {
+                invalidationCount--;
+                out("setting singleton value %s = %s", slot.getIdentifier(), value == null ? "null" : value.getClass());
+                stableValue = new StableValue<>(value, String.valueOf(slot.getIdentifier()));
+            } else {
+                out("setting non-singleton value %s", slot.getIdentifier());
+                stableValue = null;
             }
         }
 
@@ -520,12 +546,8 @@ public final class FrameSlotChangeMonitor {
      *            {@link RInternalError} otherwise
      * @param invalidateProfile Used to guard the invalidation code.
      */
-    private static void checkAndInvalidate(Frame curFrame, FrameSlot slot, boolean isNonLocal, Object newValue, BranchProfile invalidateProfile) {
+    private static void checkAndInvalidate(Frame curFrame, FrameSlot slot, boolean isNonLocal, BranchProfile invalidateProfile) {
         assert curFrame.getFrameDescriptor() == slot.getFrameDescriptor();
-        FrameSlotInfoImpl info = getFrameSlotInfo(slot);
-        if (info.needsInvalidation()) {
-            info.setValue(newValue, slot);
-        }
 
         if (getNotChangedNonLocallyAssumption(slot).isValid()) {
             // Check whether current frame is used outside a regular stack
@@ -542,22 +564,38 @@ public final class FrameSlotChangeMonitor {
 
     public static void setByteAndInvalidate(Frame frame, FrameSlot frameSlot, byte newValue, boolean isNonLocal, BranchProfile invalidateProfile) {
         frame.setByte(frameSlot, newValue);
-        checkAndInvalidate(frame, frameSlot, isNonLocal, newValue, invalidateProfile);
+        FrameSlotInfoImpl info = getFrameSlotInfo(frameSlot);
+        if (info.needsInvalidation()) {
+            info.setValue(newValue, frameSlot);
+        }
+        checkAndInvalidate(frame, frameSlot, isNonLocal, invalidateProfile);
     }
 
     public static void setIntAndInvalidate(Frame frame, FrameSlot frameSlot, int newValue, boolean isNonLocal, BranchProfile invalidateProfile) {
         frame.setInt(frameSlot, newValue);
-        checkAndInvalidate(frame, frameSlot, isNonLocal, newValue, invalidateProfile);
+        FrameSlotInfoImpl info = getFrameSlotInfo(frameSlot);
+        if (info.needsInvalidation()) {
+            info.setValue(newValue, frameSlot);
+        }
+        checkAndInvalidate(frame, frameSlot, isNonLocal, invalidateProfile);
     }
 
     public static void setDoubleAndInvalidate(Frame frame, FrameSlot frameSlot, double newValue, boolean isNonLocal, BranchProfile invalidateProfile) {
         frame.setDouble(frameSlot, newValue);
-        checkAndInvalidate(frame, frameSlot, isNonLocal, newValue, invalidateProfile);
+        FrameSlotInfoImpl info = getFrameSlotInfo(frameSlot);
+        if (info.needsInvalidation()) {
+            info.setValue(newValue, frameSlot);
+        }
+        checkAndInvalidate(frame, frameSlot, isNonLocal, invalidateProfile);
     }
 
     public static void setObjectAndInvalidate(Frame frame, FrameSlot frameSlot, Object newValue, boolean isNonLocal, BranchProfile invalidateProfile) {
         frame.setObject(frameSlot, newValue);
-        checkAndInvalidate(frame, frameSlot, isNonLocal, newValue, invalidateProfile);
+        FrameSlotInfoImpl info = getFrameSlotInfo(frameSlot);
+        if (info.needsInvalidation()) {
+            info.setValue(newValue, frameSlot);
+        }
+        checkAndInvalidate(frame, frameSlot, isNonLocal, invalidateProfile);
     }
 
     /**