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