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