From d2e5ef8945e4d6c10ece10994ad2ccc635f9efbd Mon Sep 17 00:00:00 2001
From: Lukas Stadler <lukas.stadler@oracle.com>
Date: Mon, 10 Nov 2014 14:15:37 +0100
Subject: [PATCH] missing

---
 .../truffle/r/nodes/builtin/base/Missing.java | 113 ++++++++++++++----
 .../r/nodes/function/GetMissingValueNode.java |   9 +-
 .../r/nodes/function/RMissingHelper.java      |  24 ----
 3 files changed, 94 insertions(+), 52 deletions(-)

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 a00d5102dd..bf38632c7c 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/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 21aa11cb73..a4f59b9f05 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
@@ -25,19 +25,20 @@ 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.r.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 {
 
@@ -48,7 +49,7 @@ 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());
             return replace(new ResolvedGetMissingValueNode(slot)).execute(frame);
@@ -65,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/RMissingHelper.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RMissingHelper.java
index 655fee0482..84d79f708c 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
-- 
GitLab