diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Missing.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Missing.java
index a00d5102dd3a9386c0615a6753b68289a2af50df..bf38632c7ca1036932c027556fdbc3bbcda67bde 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Missing.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Missing.java
@@ -24,9 +24,12 @@ package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.runtime.RBuiltinKind.*;
 
+import java.util.function.*;
+
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.r.nodes.*;
 import com.oracle.truffle.r.nodes.access.*;
 import com.oracle.truffle.r.nodes.builtin.*;
@@ -38,42 +41,104 @@ import com.oracle.truffle.r.runtime.data.RPromise.PromiseProfile;
 @RBuiltin(name = "missing", kind = PRIMITIVE, parameterNames = {"x"}, nonEvalArgs = {0})
 public abstract class Missing extends RBuiltinNode {
 
-    private final PromiseProfile promiseProfile = new PromiseProfile();
+    @Child private InlineCacheNode<Frame, Symbol> repCache;
 
-    @Child private GetMissingValueNode getMissingValue;
+    private final ConditionProfile isSymbolNullProfile = ConditionProfile.createBinaryProfile();
 
-    @Specialization
-    protected byte missing(VirtualFrame frame, RPromise promise) {
-        controlVisibility();
-        // Unwrap current promise, as it's irrelevant for 'missing'
-        RNode argExpr = (RNode) promise.getRep();
-        Symbol symbol = RMissingHelper.unwrapSymbol(argExpr);
+    private static InlineCacheNode<Frame, Symbol> createRepCache(int level) {
+        Function<Symbol, RNode> reify = symbol -> createNodeForRep(symbol, level);
+        BiFunction<Frame, Symbol, Object> generic = (frame, symbol) -> RRuntime.asLogical(RMissingHelper.isMissingArgument(frame, symbol));
+        return InlineCacheNode.create(3, reify, generic);
+    }
+
+    private static RNode createNodeForRep(Symbol symbol, int level) {
         if (symbol == null) {
-            return RRuntime.asLogical(false);
+            return ConstantNode.create(RRuntime.LOGICAL_FALSE);
         }
+        return new MissingCheckLevel(symbol, level);
+    }
 
-        // Read symbols value directly
-        if (getMissingValue == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            getMissingValue = insert(GetMissingValueNode.create(symbol));
+    private static class MissingCheckLevel extends RNode {
+
+        @Child private GetMissingValueNode getMissingValue;
+        @Child private InlineCacheNode<Frame, Symbol> recursive;
+
+        private final ConditionProfile isNullProfile = ConditionProfile.createBinaryProfile();
+        private final ConditionProfile isMissingProfile = ConditionProfile.createBinaryProfile();
+        private final ConditionProfile isPromiseProfile = ConditionProfile.createBinaryProfile();
+        private final ConditionProfile isSymbolNullProfile = ConditionProfile.createBinaryProfile();
+        private final PromiseProfile promiseProfile = new PromiseProfile();
+        private final int level;
+
+        public MissingCheckLevel(Symbol symbol, int level) {
+            this.level = level;
+            this.getMissingValue = GetMissingValueNode.create(symbol);
         }
-        Object obj = getMissingValue.execute(frame);
-        if (obj == null) {
-            // In case we are not able to read the symbol in current frame: This is not an argument
-            // and thus return false
-            return RRuntime.asLogical(false);
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            // Read symbols value directly
+            Object value = getMissingValue.execute(frame);
+            if (isNullProfile.profile(value == null)) {
+                // In case we are not able to read the symbol in current frame: This is not an
+                // argument and thus return false
+                return RRuntime.LOGICAL_FALSE;
+            }
+
+            if (isMissingProfile.profile(value == RMissing.instance)) {
+                return RRuntime.LOGICAL_TRUE;
+            }
+
+            assert level < 30;
+            // This might be a promise...
+            if (isPromiseProfile.profile(value instanceof RPromise)) {
+                RPromise promise = (RPromise) value;
+                if (level == 0 && promise.isDefault(promiseProfile)) {
+                    return RRuntime.LOGICAL_TRUE;
+                }
+                if (level > 0 && promise.isEvaluated(promiseProfile)) {
+                    return RRuntime.LOGICAL_FALSE;
+                }
+                if (recursive == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    recursive = insert(createRepCache(level + 1));
+                }
+                // Check: If there is a cycle, return true. (This is done like in GNU R)
+                if (promise.isUnderEvaluation(promiseProfile)) {
+                    return RRuntime.LOGICAL_TRUE;
+                }
+                try {
+                    promise.setUnderEvaluation(true);
+                    Symbol symbol = RMissingHelper.unwrapSymbol((RNode) promise.getRep());
+                    return isSymbolNullProfile.profile(symbol == null) ? RRuntime.LOGICAL_FALSE : recursive.execute(promise.getFrame(), symbol);
+                } finally {
+                    promise.setUnderEvaluation(false);
+                }
+            }
+            return RRuntime.LOGICAL_FALSE;
         }
+    }
 
-        return RRuntime.asLogical(RMissingHelper.isMissing(obj, promiseProfile));
+    @Specialization
+    protected byte missing(VirtualFrame frame, RPromise promise) {
+        controlVisibility();
+        if (repCache == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            repCache = insert(createRepCache(0));
+        }
+        Symbol symbol = RMissingHelper.unwrapSymbol((RNode) promise.getRep());
+        return isSymbolNullProfile.profile(symbol == null) ? RRuntime.LOGICAL_FALSE : (byte) repCache.execute(frame, symbol);
     }
 
-    @Specialization(guards = "!isPromise")
-    protected byte missing(Object obj) {
+    @Specialization
+    protected byte missing(@SuppressWarnings("unused") RMissing obj) {
         controlVisibility();
-        return RRuntime.asLogical(RMissingHelper.isMissing(obj, promiseProfile));
+        return RRuntime.LOGICAL_TRUE;
     }
 
-    public boolean isPromise(Object obj) {
-        return obj instanceof RPromise;
+    @Fallback
+    protected byte missing(@SuppressWarnings("unused") Object obj) {
+        controlVisibility();
+        return RRuntime.LOGICAL_FALSE;
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UseMethod.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UseMethod.java
index dc2afb7fbf7a5cce07af04b5024a4947ccebcc3e..23fdfd3d6b48b615ceceec9dfd41c45baa0b85c9 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UseMethod.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UseMethod.java
@@ -15,6 +15,7 @@ import static com.oracle.truffle.r.runtime.RBuiltinKind.*;
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.r.nodes.*;
 import com.oracle.truffle.r.nodes.access.*;
 import com.oracle.truffle.r.nodes.builtin.*;
@@ -87,6 +88,7 @@ public abstract class UseMethod extends RBuiltinNode {
         }
     }
 
+    @NodeInfo(cost = NodeCost.UNINITIALIZED)
     private static final class UninitializedUseMethodNode extends UseMethodNode {
 
         protected final int depth;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/CallInlineCacheNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/CallInlineCacheNode.java
index f8f7fff1860d11c0ee70b63ad6234d0ac2e035f2..6ebcf6cbb962f8edf51c26d98b8eb60cf069339d 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/CallInlineCacheNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/CallInlineCacheNode.java
@@ -45,6 +45,7 @@ public abstract class CallInlineCacheNode extends Node {
         return new UninitializedCallInlineCacheNode(maxPicDepth);
     }
 
+    @NodeInfo(cost = NodeCost.UNINITIALIZED)
     private static final class UninitializedCallInlineCacheNode extends CallInlineCacheNode {
 
         private final int maxPicDepth;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/InlineCacheNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/InlineCacheNode.java
index f490925043f3a64b033c7db61d163746d5f9724f..f80ceb6a9f9b8f28018adceabe8ebe8c077e8062 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/InlineCacheNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/InlineCacheNode.java
@@ -74,6 +74,7 @@ public abstract class InlineCacheNode<F extends Frame, T> extends Node {
         return create(maxPicDepth, closure -> (RNode) closure.getExpr(), (frame, closure) -> RContext.getEngine().evalPromise(closure, frame.materialize()));
     }
 
+    @NodeInfo(cost = NodeCost.UNINITIALIZED)
     private static final class UninitializedInlineCacheNode<F extends Frame, T> extends InlineCacheNode<F, T> {
 
         private final int maxPicDepth;
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 0f88a96422ac34b27c4c18862f6d8557869bb061..2b12522378a4dc8363781d2d9e1375fd959629ea 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
@@ -67,6 +67,7 @@ public abstract class FrameSlotNode extends Node {
         return new PresentFrameSlotNode(slot);
     }
 
+    @NodeInfo(cost = NodeCost.UNINITIALIZED)
     private static final class UnresolvedFrameSlotNode extends FrameSlotNode {
 
         private final Object identifier;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariableNode.java
index 411c469e4a26860f6a418d598179fb717b6b8dbd..4f8dce9a8b1775a9f5754680b6e44521a0253c0f 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariableNode.java
@@ -278,6 +278,7 @@ public abstract class ReadVariableNode extends RNode implements VisibilityContro
         RType getMode();
     }
 
+    @NodeInfo(cost = NodeCost.UNINITIALIZED)
     public static final class UnresolvedReadVariableNode extends ReadVariableNode implements HasMode {
 
         // TODO It seems a refactoring would be appropriate to encapsulate all fields (symbol, mode,
@@ -460,6 +461,7 @@ public abstract class ReadVariableNode extends RNode implements VisibilityContro
         }
     }
 
+    @NodeInfo(cost = NodeCost.UNINITIALIZED)
     public static final class UnResolvedReadLocalVariableNode extends ReadVariableNode implements HasMode {
         private final Symbol symbol;
         private final RType mode;
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 b322e5d520d2c0f63d18388948b44e326808d047..b459fcff3dd6929beb88f672908c4a2ff0ed6485 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
@@ -25,6 +25,7 @@ package com.oracle.truffle.r.nodes.access;
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.r.nodes.*;
@@ -152,6 +153,7 @@ public abstract class WriteVariableNode extends RNode implements VisibilityContr
 
     public abstract void execute(VirtualFrame frame, Object value);
 
+    @NodeInfo(cost = NodeCost.UNINITIALIZED)
     @NodeFields({@NodeField(name = "name", type = String.class), @NodeField(name = "mode", type = Mode.class)})
     public abstract static class UnresolvedWriteLocalVariableNode extends WriteVariableNode {
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchedCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchedCallNode.java
index 0607daa5d946784ed1aa1c723dfb0fbd9acc57ee..c3df06bafcf2058493a3bbc81fffd886cabef197 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchedCallNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchedCallNode.java
@@ -13,6 +13,7 @@ package com.oracle.truffle.r.nodes.function;
 
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.r.nodes.*;
 import com.oracle.truffle.r.runtime.*;
@@ -49,6 +50,7 @@ public abstract class DispatchedCallNode extends RNode {
 
     public abstract Object executeInternal(VirtualFrame frame, RStringVector type, Object[] args);
 
+    @NodeInfo(cost = NodeCost.UNINITIALIZED)
     private static final class UninitializedDispatchedCallNode extends DispatchedCallNode {
         private final int depth;
         private final String genericName;
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 276d56ed99d80120a4f0da05b06458cfc92800fd..a4f59b9f05ded0ab468a822ece4460f20445d512 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
@@ -24,19 +24,22 @@ package com.oracle.truffle.r.nodes.function;
 
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.r.nodes.*;
+import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.r.nodes.access.*;
 
 /**
  * This is a node abstraction for the functionality defined in
  * {@link RMissingHelper#getMissingValue(Frame,Symbol)}.
  */
-public abstract class GetMissingValueNode extends RNode {
+public abstract class GetMissingValueNode extends Node {
 
     public static GetMissingValueNode create(Symbol sym) {
         return new UninitializedGetMissingValueNode(sym);
     }
 
+    public abstract Object execute(Frame frame);
+
+    @NodeInfo(cost = NodeCost.UNINITIALIZED)
     private static final class UninitializedGetMissingValueNode extends GetMissingValueNode {
 
         private final Symbol sym;
@@ -46,11 +49,10 @@ public abstract class GetMissingValueNode extends RNode {
         }
 
         @Override
-        public Object execute(VirtualFrame frame) {
+        public Object execute(Frame frame) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             FrameSlot slot = frame.getFrameDescriptor().findFrameSlot(sym.getName());
-            GetMissingValueNode gmvn = new ResolvedGetMissingValueNode(slot);
-            return replace(gmvn).execute(frame);
+            return replace(new ResolvedGetMissingValueNode(slot)).execute(frame);
         }
 
     }
@@ -64,7 +66,7 @@ public abstract class GetMissingValueNode extends RNode {
         }
 
         @Override
-        public Object execute(VirtualFrame frame) {
+        public Object execute(Frame frame) {
             if (slot == null) {
                 return null;
             }
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 766b15a752c1a731dc85e24abf2aee9e48bf6d28..b108aa97e1a4b0408ac871b8da0b761373374038 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
@@ -74,13 +74,13 @@ import com.oracle.truffle.r.runtime.data.*;
  *  U = {@link UninitializedCallNode}: Forms the uninitialized end of the function PIC
  *  D = {@link DispatchedCallNode}: Function fixed, no varargs
  *  G = {@link GenericCallNode}: Function arbitrary, no varargs (generic case)
- * 
+ *
  *  UV = {@link UninitializedCallNode} with varargs,
  *  UVC = {@link UninitializedVarArgsCacheCallNode} with varargs, for varargs cache
  *  DV = {@link DispatchedVarArgsCallNode}: Function fixed, with cached varargs
  *  DGV = {@link DispatchedGenericVarArgsCallNode}: Function fixed, with arbitrary varargs (generic case)
  *  GV = {@link GenericVarArgsCallNode}: Function arbitrary, with arbitrary varargs (generic case)
- * 
+ *
  * (RB = {@link RBuiltinNode}: individual functions that are builtins are represented by this node
  * which is not aware of caching). Due to {@link CachedCallNode} (see below) this is transparent to
  * the cache and just behaves like a D/DGV)
@@ -93,11 +93,11 @@ import com.oracle.truffle.r.runtime.data.*;
  * non varargs, max depth:
  * |
  * D-D-D-U
- * 
+ *
  * no varargs, generic (if max depth is exceeded):
  * |
  * D-D-D-D-G
- * 
+ *
  * varargs:
  * |
  * DV-DV-UV         <- function call target identity level cache
@@ -105,7 +105,7 @@ import com.oracle.truffle.r.runtime.data.*;
  *    DV
  *    |
  *    UVC           <- varargs signature level cache
- * 
+ *
  * varargs, max varargs depth exceeded:
  * |
  * DV-DV-UV
@@ -117,7 +117,7 @@ import com.oracle.truffle.r.runtime.data.*;
  *    DV
  *    |
  *    DGV
- * 
+ *
  * varargs, max function depth exceeded:
  * |
  * DV-DV-DV-DV-GV
@@ -374,6 +374,7 @@ public abstract class RCallNode extends RNode {
      *
      * @see RCallNode
      */
+    @NodeInfo(cost = NodeCost.UNINITIALIZED)
     public static final class UninitializedCallNode extends RootCallNode {
 
         private final int depth;
@@ -544,6 +545,7 @@ public abstract class RCallNode extends RNode {
      *
      * @see RCallNode
      */
+    @NodeInfo(cost = NodeCost.UNINITIALIZED)
     public static final class UninitializedVarArgsCacheCallNode extends VarArgsCacheCallNode {
         @Child private CallArgumentsNode args;
         private int depth = 1;  // varargs cached is started with a [DV] DispatchedVarArgsCallNode
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 655fee04826b8ac6a488464abe2a5b5f652e20ea..84d79f708cc8fa6f78c32fde97ae370ebd6833e7 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
@@ -34,30 +34,6 @@ import com.oracle.truffle.r.runtime.data.RPromise.*;
  * would induce unnecessary dependencies otherwise.
  */
 public class RMissingHelper {
-    /**
-     * This function determines whether an arguments value - given as 'value' - is missing. An
-     * argument is missing when it has not been provided to the current function call (DEFAULTED or
-     * {@code value == RMissing.instance}, if argument has default value), OR if the value that has
-     * been provided once was a missing argument. (cp. R language definition and Internals 1.5.1
-     * Missingness).
-     *
-     * @param value The value that should be examined
-     * @return <code>true</code> iff this value is 'missing' in the definition of R
-     */
-    public static boolean isMissing(Object value, PromiseProfile promiseProfile) {
-        if (value == RMissing.instance) {
-            return true;
-        }
-
-        // This might be a promise...
-        if (value instanceof RPromise) {
-            RPromise promise = (RPromise) value;
-            if (promise.isDefault(promiseProfile) || isMissingSymbol(promise)) {
-                return true;
-            }
-        }
-        return false;
-    }
 
     /**
      * This method determines whether a given {@link Symbol} is missing in the given frame. This is