diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionMR.java index 36d76a99b6ab6f3d8863990a4bbf7d7e9b4c3ebd..47890c2305ed1e5f3afdb0f7fe2fb78f864304d1 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionMR.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionMR.java @@ -39,6 +39,7 @@ import com.oracle.truffle.r.runtime.RArguments; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RFunction; import com.oracle.truffle.r.runtime.data.RObject; +import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; import com.oracle.truffle.r.runtime.env.frame.RFrameSlot; import com.oracle.truffle.r.runtime.interop.Foreign2R; @@ -71,13 +72,15 @@ public class RFunctionMR { private static final FrameDescriptor emptyFrameDescriptor = FrameSlotChangeMonitor.initializeFunctionFrameDescriptor("<interop>", new FrameDescriptor("R interop frame")); private static final RFrameSlot argsIdentifier = RFrameSlot.createTemp(false); + private static final RFrameSlot explicitCallerId = RFrameSlot.createTemp(false); private static final FrameSlot slot = FrameSlotChangeMonitor.findOrAddFrameSlot(emptyFrameDescriptor, argsIdentifier, FrameSlotKind.Object); + private static final FrameSlot slotCaller = FrameSlotChangeMonitor.findOrAddFrameSlot(emptyFrameDescriptor, explicitCallerId, FrameSlotKind.Object); static { FrameSlotChangeMonitor.initializeFunctionFrameDescriptor("<function>", emptyFrameDescriptor); } - @Child private RCallBaseNode call = RCallNode.createExplicitCall(argsIdentifier); + @Child private RCallBaseNode call = RCallNode.createExplicitCall(argsIdentifier, explicitCallerId); protected Object access(RFunction receiver, Object[] arguments) { Object[] dummyFrameArgs = RArguments.createUnitialized(); @@ -90,6 +93,7 @@ public class RFunctionMR { RArgsValuesAndNames actualArgs = new RArgsValuesAndNames(convertedArguments, ArgumentsSignature.empty(arguments.length)); try { FrameSlotChangeMonitor.setObject(dummyFrame, slot, actualArgs); + FrameSlotChangeMonitor.setObject(dummyFrame, slotCaller, RNull.instance); Object value = call.execute(dummyFrame, receiver); return r2Foreign.execute(value); } finally { diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort.java index b1c90a3a02bf7da523010ff7e3deab63d0d31557..6d1772a79b3df52de9a18cc420750c026d7bbe16 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort.java @@ -151,7 +151,7 @@ public final class ViewPort { public RList execute(VirtualFrame frame) { RFunction gridTopLevel = (RFunction) readGridTopLevel.execute(frame); - RList topVP = (RList) callNode.execute(frame, gridTopLevel, RArgsValuesAndNames.EMPTY); + RList topVP = (RList) callNode.call(frame, gridTopLevel, RArgsValuesAndNames.EMPTY); topVP.makeSharedPermanent(); GridDevice device = GridContext.getContext().getCurrentDevice(); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java index 165ad928eaa36e1efebf1232f3bd27e18f9670b2..00894b2b3b2a29c27e2283783b9344330ff407c8 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java @@ -585,7 +585,7 @@ public abstract class Bind extends RBaseNode { CompilerDirectives.transferToInterpreterAndInvalidate(); dispatchCallNode = insert(RExplicitCallNode.create()); } - return dispatchCallNode.execute(frame, dispatchFunction, (RArgsValuesAndNames) RArguments.getArgument(frame, 0)); + return dispatchCallNode.call(frame, dispatchFunction, (RArgsValuesAndNames) RArguments.getArgument(frame, 0)); } else { if (bind == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java index fed4396ff0c3aa815406b06cde8861c76429cd50..626d44088d275eb332c74c7329d32e68948a630a 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,14 +23,19 @@ package com.oracle.truffle.r.nodes.builtin.base; import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.returnIf; import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue; import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean; import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM; import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX; +import java.util.function.Supplier; + +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.FrameSlot; import com.oracle.truffle.api.frame.FrameSlotKind; import com.oracle.truffle.api.frame.FrameSlotTypeException; @@ -40,30 +45,32 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.api.profiles.ValueProfile; -import com.oracle.truffle.r.nodes.RASTUtils; -import com.oracle.truffle.r.nodes.access.ConstantNode; import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.nodes.builtin.base.DoCallNodeGen.DoCallInternalNodeGen; import com.oracle.truffle.r.nodes.builtin.base.GetFunctions.Get; import com.oracle.truffle.r.nodes.builtin.base.GetFunctionsFactory.GetNodeGen; import com.oracle.truffle.r.nodes.function.RCallerHelper; -import com.oracle.truffle.r.nodes.function.opt.ShareObjectNode; +import com.oracle.truffle.r.nodes.function.call.RExplicitCallNode; +import com.oracle.truffle.r.nodes.function.visibility.GetVisibilityNode; import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode; +import com.oracle.truffle.r.nodes.profile.TruffleBoundaryNode; import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.RArguments; import com.oracle.truffle.r.runtime.RCaller; -import com.oracle.truffle.r.runtime.RDispatch; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RError.Message; import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.RType; +import com.oracle.truffle.r.runtime.VirtualEvalFrame; import com.oracle.truffle.r.runtime.builtins.RBuiltin; -import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor; import com.oracle.truffle.r.runtime.builtins.RBuiltinKind; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.data.Closure; +import com.oracle.truffle.r.runtime.data.ClosureCache; +import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache; +import com.oracle.truffle.r.runtime.data.ClosureCache.SymbolClosureCache; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RDataFactory; import com.oracle.truffle.r.runtime.data.REmpty; @@ -79,9 +86,10 @@ import com.oracle.truffle.r.runtime.env.REnvironment; import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; import com.oracle.truffle.r.runtime.env.frame.RFrameSlot; import com.oracle.truffle.r.runtime.nodes.InternalRSyntaxNodeChildren; +import com.oracle.truffle.r.runtime.nodes.RSyntaxElement; import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; -@RBuiltin(name = ".fastr.do.call", visibility = CUSTOM, kind = RBuiltinKind.INTERNAL, parameterNames = {"what", "args", "quote", "envir"}, behavior = COMPLEX) +@RBuiltin(name = ".fastr.do.call", visibility = CUSTOM, kind = RBuiltinKind.INTERNAL, parameterNames = {"what", "args", "quote", "envir"}, behavior = COMPLEX, splitCaller = true) public abstract class DoCall extends RBuiltinNode.Arg4 implements InternalRSyntaxNodeChildren { static { @@ -119,8 +127,11 @@ public abstract class DoCall extends RBuiltinNode.Arg4 implements InternalRSynta } protected abstract static class DoCallInternal extends Node { - @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create(); - @Child private SetVisibilityNode setVisibilityNode = SetVisibilityNode.create(); + @Child private GetNamesAttributeNode getNamesNode; + @Child private SetVisibilityNode setVisibilityNode; + private final ValueProfile frameAccessProfile = ValueProfile.createClassProfile(); + private final RNodeClosureCache languagesClosureCache = new RNodeClosureCache(); + private final SymbolClosureCache symbolsClosureCache = new SymbolClosureCache(); public static DoCallInternal create() { return DoCallInternalNodeGen.create(); @@ -128,23 +139,80 @@ public abstract class DoCall extends RBuiltinNode.Arg4 implements InternalRSynta public abstract Object execute(VirtualFrame virtualFrame, RFunction func, RList argsAsList, boolean quote, REnvironment env); - protected final ArgumentsSignature getArgsNames(RList argsAsList) { - ArgumentsSignature signature = ArgumentsSignature.fromNamesAttribute(getNamesNode.getNames(argsAsList)); - return signature == null ? ArgumentsSignature.empty(argsAsList.getLength()) : signature; + protected FrameDescriptor getFrameDescriptor(REnvironment env) { + return env.getFrame(frameAccessProfile).getFrameDescriptor(); } /** - * Fast version that works only for simple cases. It does not explicitly create the AST and - * evaluate it, but instead it directly implements what the execution of such AST would do. + * Because the underlying AST in {@link RExplicitCallNode} may cache frame slots, i.e. + * expect the {@link FrameDescriptor} to never change, we're caching this AST and also + * {@link GetVisibilityNode} for each {@link FrameDescriptor} we encounter. */ - @Specialization(guards = "isSimple(func, argsAsList)") - public Object doSimple(VirtualFrame virtualFrame, RFunction func, RList argsAsList, boolean quote, REnvironment env, - @Cached("create()") ShareObjectNode shareObjectNode, + @Specialization(guards = {"getFrameDescriptor(env) == fd"}, limit = "20") + public Object doFastPath(VirtualFrame virtualFrame, RFunction func, RList argsAsList, boolean quote, REnvironment env, + @Cached("getFrameDescriptor(env)") @SuppressWarnings("unused") FrameDescriptor fd, + @Cached("create()") RExplicitCallNode explicitCallNode, + @Cached("create()") GetVisibilityNode getVisibilityNode, @Cached("createBinaryProfile()") ConditionProfile quoteProfile, - @Cached("create()") BranchProfile containsRSymbolProfile, - @Cached("createClassProfile()") ValueProfile frameAccessProfile) { + @Cached("create()") BranchProfile containsRSymbolProfile) { + MaterializedFrame promiseFrame = env.getFrame(frameAccessProfile).materialize(); + RArgsValuesAndNames args = getArguments(promiseFrame, quote, quoteProfile, containsRSymbolProfile, argsAsList); + RCaller caller = getExplicitCaller(virtualFrame, promiseFrame, func, args); + MaterializedFrame evalFrame = getEvalFrame(virtualFrame, promiseFrame); + + Object resultValue = explicitCallNode.execute(evalFrame, func, args, caller); + setVisibility(virtualFrame, getVisibilityNode.execute(evalFrame)); + return resultValue; + } + + /** + * Slow-path version avoids the problem by creating {@link RExplicitCallNode} for every call + * again and again and putting it behind truffle boundary to avoid deoptimization. + */ + @Specialization(replaces = "doFastPath") + public Object doSlowPath(VirtualFrame virtualFrame, RFunction func, RList argsAsList, boolean quote, REnvironment env, + @Cached("create()") SlowPathExplicitCall slowPathExplicitCall, + @Cached("createBinaryProfile()") ConditionProfile quoteProfile, + @Cached("create()") BranchProfile containsRSymbolProfile) { + MaterializedFrame promiseFrame = env.getFrame(frameAccessProfile).materialize(); + RArgsValuesAndNames args = getArguments(promiseFrame, quote, quoteProfile, containsRSymbolProfile, argsAsList); + RCaller caller = getExplicitCaller(virtualFrame, promiseFrame, func, args); + MaterializedFrame evalFrame = getEvalFrame(virtualFrame, promiseFrame); + + Object resultValue = slowPathExplicitCall.execute(evalFrame, caller, func, args); + setVisibility(virtualFrame, getVisibilitySlowPath(evalFrame)); + return resultValue; + } + + /** + * The contract is that the function call will be evaluated in the given environment, but at + * the same time some primitives expect to see {@code do.call(foo, ...)} as the caller, so + * we create a frame the fakes caller, but otherwise delegates to the frame backing the + * explicitly given environment. + */ + private MaterializedFrame getEvalFrame(VirtualFrame virtualFrame, MaterializedFrame envFrame) { + return VirtualEvalFrame.create(envFrame, RArguments.getFunction(virtualFrame), RArguments.getCall(virtualFrame)); + } + + /** + * If the call leads to actual call via + * {@link com.oracle.truffle.r.nodes.function.call.CallRFunctionNode}, which creates new + * frame and new set of arguments for it, then for this new arguments we explicitly provide + * a caller that looks like the function was called from the explicitly given environment + * (it will be its parent call), but at the same time its depth is one above the do.call + * function that actually invoked it. + * + * @see RCaller + * @see RArguments + */ + private RCaller getExplicitCaller(VirtualFrame virtualFrame, MaterializedFrame envFrame, RFunction func, RArgsValuesAndNames args) { + Supplier<RSyntaxElement> callerSyntax = RCallerHelper.createFromArguments(func, args); + return RCaller.create(RArguments.getDepth(virtualFrame) + 1, RArguments.getCall(envFrame), callerSyntax); + } + + private RArgsValuesAndNames getArguments(MaterializedFrame promiseFrame, boolean quote, ConditionProfile quoteProfile, + BranchProfile containsRSymbolProfile, RList argsAsList) { Object[] argValues = argsAsList.getDataCopy(); - MaterializedFrame envFrame = env.getFrame(frameAccessProfile).materialize(); if (quoteProfile.profile(!quote)) { for (int i = 0; i < argValues.length; i++) { Object arg = argValues[i]; @@ -154,53 +222,48 @@ public abstract class DoCall extends RBuiltinNode.Arg4 implements InternalRSynta if (symbol.getName().isEmpty()) { argValues[i] = REmpty.instance; } else { - argValues[i] = createLookupPromise(envFrame, symbol); + argValues[i] = createLookupPromise(promiseFrame, symbol.getName()); } + } else if (arg instanceof RLanguage) { + argValues[i] = createLanguagePromise(promiseFrame, (RLanguage) arg); } } } - for (int i = 0; i < argValues.length; i++) { - shareObjectNode.execute(argValues[i]); - } ArgumentsSignature signature = getArgsNames(argsAsList); - RCaller caller = RCaller.createWithInternalParent(virtualFrame, RCallerHelper.createFromArguments(func, new RArgsValuesAndNames(argValues, signature))); - try { - Object resultValue = RContext.getEngine().evalFunction(func, envFrame, caller, false, signature, argValues); - setVisibilityNode.execute(virtualFrame, getVisibility(envFrame)); - return resultValue; - } finally { - for (int i = 0; i < argValues.length; i++) { - ShareObjectNode.unshare(argValues[i]); - } - } + return new RArgsValuesAndNames(argValues, signature); } - protected static boolean isSimple(RFunction function, RList args) { - RBuiltinDescriptor builtin = function.getRBuiltin(); - if (builtin != null && builtin.getDispatch() != RDispatch.DEFAULT) { - return false; - } - for (int i = 0; i < args.getLength(); i++) { - if (args.getDataAt(i) instanceof RLanguage) { - // Note: language is tricky because of formulae, which are language that is - // really not meant to be evaluated again in a different frame than the one were - // the were evaluated for the first time. The solution should be to clone the - // language's rep and get its nodes in uninitilized state, but that does not - // work for some reason. - return false; - } - } - return true; + @TruffleBoundary + private RPromise createLanguagePromise(MaterializedFrame promiseFrame, RLanguage arg) { + Closure closure = languagesClosureCache.getOrCreatePromiseClosure(arg.getRep()); + return RDataFactory.createPromise(PromiseState.Supplied, closure, promiseFrame); } @TruffleBoundary - private static RPromise createLookupPromise(MaterializedFrame callerFrame, RSymbol symbol) { - Closure closure = Closure.createPromiseClosure(RContext.getASTBuilder().lookup(RSyntaxNode.SOURCE_UNAVAILABLE, symbol.getName(), false).asRNode()); + private RPromise createLookupPromise(MaterializedFrame callerFrame, String name) { + Closure closure = symbolsClosureCache.getOrCreatePromiseClosure(name); return RDataFactory.createPromise(PromiseState.Supplied, closure, callerFrame); } + private void setVisibility(VirtualFrame frame, boolean value) { + if (setVisibilityNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + setVisibilityNode = insert(SetVisibilityNode.create()); + } + setVisibilityNode.execute(frame, value); + } + + private ArgumentsSignature getArgsNames(RList argsAsList) { + if (getNamesNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + getNamesNode = insert(GetNamesAttributeNode.create()); + } + ArgumentsSignature signature = ArgumentsSignature.fromNamesAttribute(getNamesNode.getNames(argsAsList)); + return signature == null ? ArgumentsSignature.empty(argsAsList.getLength()) : signature; + } + @TruffleBoundary - private static boolean getVisibility(MaterializedFrame envFrame) { + private static boolean getVisibilitySlowPath(MaterializedFrame envFrame) { FrameSlot envVisibilitySlot = FrameSlotChangeMonitor.findOrAddFrameSlot(envFrame.getFrameDescriptor(), RFrameSlot.Visibility, FrameSlotKind.Boolean); if (envVisibilitySlot != null) { try { @@ -211,62 +274,19 @@ public abstract class DoCall extends RBuiltinNode.Arg4 implements InternalRSynta } return false; } + } - @Specialization(guards = "!isSimple(func, argsAsList)") - public Object doGeneric(VirtualFrame virtualFrame, RFunction func, RList argsAsList, boolean quote, REnvironment env) { - CallResult result = doCallGeneric(func, argsAsList.getDataWithoutCopying(), getArgsNames(argsAsList), quote, RArguments.getCall(virtualFrame), env); - setVisibilityNode.execute(virtualFrame, result.visibility); - return result.value; - } + static class SlowPathExplicitCall extends TruffleBoundaryNode { + @Child private RExplicitCallNode slowPathCallNode; - @TruffleBoundary - private static CallResult doCallGeneric(RFunction function, Object[] argValues, ArgumentsSignature argsSignature, boolean quote, RCaller call, REnvironment env) { - RSyntaxNode[] argsConstants = new RSyntaxNode[argValues.length]; - for (int i = 0; i < argValues.length; i++) { - if (!quote && argValues[i] instanceof RLanguage) { - argsConstants[i] = ((RLanguage) argValues[i]).getRep().asRSyntaxNode(); - } else if (!quote && argValues[i] instanceof RSymbol) { - RSymbol symbol = (RSymbol) argValues[i]; - if (symbol.isMissing()) { - argsConstants[i] = ConstantNode.create(REmpty.instance); - } else { - argsConstants[i] = RContext.getASTBuilder().lookup(RSyntaxNode.LAZY_DEPARSE, ((RSymbol) argValues[i]).getName(), false); - } - } else { - argsConstants[i] = ConstantNode.create(argValues[i]); - } - } - for (int i = 0; i < argValues.length; i++) { - ShareObjectNode.share(argValues[i]); - } - Closure closure = Closure.createLanguageClosure(RASTUtils.createCall(ConstantNode.create(function), true, argsSignature, argsConstants).asRNode()); - RLanguage lang = RDataFactory.createLanguage(closure); - try { - Object resultValue = RContext.getEngine().eval(lang, env, call.withInternalParent()); - MaterializedFrame envFrame = env.getFrame(); - FrameSlot envVisibilitySlot = FrameSlotChangeMonitor.findOrAddFrameSlot(envFrame.getFrameDescriptor(), RFrameSlot.Visibility, FrameSlotKind.Boolean); - boolean resultVisibility = false; - if (envVisibilitySlot != null) { - resultVisibility = envFrame.getBoolean(envVisibilitySlot); - } - return new CallResult(resultValue, resultVisibility); - } catch (FrameSlotTypeException e) { - throw RInternalError.shouldNotReachHere(); - } finally { - for (int i = 0; i < argValues.length; i++) { - ShareObjectNode.unshare(argValues[i]); - } - } + public static SlowPathExplicitCall create() { + return new SlowPathExplicitCall(); } - private static final class CallResult { - public final Object value; - public final boolean visibility; - - CallResult(Object value, boolean visibility) { - this.value = value; - this.visibility = visibility; - } + @TruffleBoundary + public Object execute(MaterializedFrame evalFrame, RCaller caller, RFunction func, RArgsValuesAndNames args) { + slowPathCallNode = insert(RExplicitCallNode.create()); + return slowPathCallNode.execute(evalFrame, func, args, caller); } } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForceAndCall.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForceAndCall.java index 3cef036d53cbbb707ffd9819a0bcb2f02abd496c..0a315155bb6f03b191bd8fc9760717c72544b954 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForceAndCall.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForceAndCall.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,7 +60,7 @@ public abstract class ForceAndCall extends RBuiltinNode.Arg3 { if (!fun.isBuiltin()) { flattenFirstArgs(frame, cachedN, args); } - return call.execute(frame, fun, args); + return call.call(frame, fun, args); } @ExplodeLoop diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java index e15ff3334c5df7e2013c38214709e9a084e83b12..6e2f7dda034d4056c45c345461a5f00d5ab29686 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -89,6 +89,16 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; * The frame for the sys functions themselves is not counted in the R spec. Frames are numbered 0, * 1, .. starting from .GlobalEnv. Non-negative arguments are frame numbers, negative arguments are * relative to the current frame. + * + * The main thing to note is distinction between {@code sys.parent} and {@code sys.frame}. + * {@code sys.parent} gives you "logical" parent, but not necessarily the frame preceding the + * current frame on the call stack. In FastR this is captured by {@link RCaller#getParent()}. + * {@code sys.frame(n)} gives you frame with number {@code n} and traverses the stack without taking + * the parent relation into account. In FastR the frame number is captured in + * {@link RCaller#getDepth()}. See also builtins in {@code FrameFunctions} for more details. + * + * @see RArguments + * @see RCaller */ public class FrameFunctions { @@ -112,19 +122,11 @@ public class FrameFunctions { return RInternalError.guaranteeNonNull(getNumberedFrame(frame, actualFrame)); } - protected RCaller getCall(RCaller currentCall, int n) { + protected RCaller getCall(VirtualFrame frame, int n) { + RCaller currentCall = RArguments.getCall(frame); int actualFrame = decodeFrameNumber(currentCall, n); - RCaller call = currentCall; - while (call != null) { - while (call.isPromise()) { - call = call.getPromiseOriginalCall(); - } - if (call.getDepth() == actualFrame) { - return call; - } - call = call.getParent(); - } - throw RInternalError.shouldNotReachHere(); + Frame targetFrame = getNumberedFrame(frame, actualFrame); + return RArguments.getCall(targetFrame); } /** @@ -197,13 +199,12 @@ public class FrameFunctions { /* * sys.call preserves provided names but does not create them, unlike match.call. */ - return createCall(RArguments.getCall(frame), which); + return createCall(helper.getCall(frame, which)); } @TruffleBoundary - private Object createCall(RCaller currentCall, int which) { - RCaller call = helper.getCall(currentCall, which); - assert !call.isPromise(); + private Object createCall(RCaller call) { + assert call == null || !call.isPromise(); if (call == null || !call.isValidCaller()) { return RNull.instance; } @@ -543,9 +544,6 @@ public class FrameFunctions { RCaller call = RArguments.getCall(frame); int i = 0; while (i < n + 1) { - if (call.hasInternalParent()) { - i--; // in this loop iteration, we deal with the parent, but do not count it - } call = call.getParent(); if (call == null) { nullCallerProfile.enter(); @@ -615,7 +613,7 @@ public class FrameFunctions { RCaller currentCall = RArguments.getCall(f); if (!currentCall.isPromise() && currentCall.getDepth() <= depth) { int currentCallIdx = currentCall.getDepth() - 1; - while (currentCall != null && (currentCall.hasInternalParent() || currentCall.isPromise())) { + while (currentCall != null && (currentCall.isPromise())) { currentCall = currentCall.getParent(); } result[currentCallIdx] = currentCall == null ? 0 : currentCall.getParent().getDepth(); @@ -672,9 +670,6 @@ public class FrameFunctions { } int i = 0; while (i < n) { - if (call.hasInternalParent()) { - i--; // in this loop iteration, we deal with the parent, but do not count it - } call = call.getParent(); if (call == null) { nullCallerProfile.enter(); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mapply.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mapply.java index dc3a03b8f5389ea8e3ec3e811839654de10582ae..50a3c846eb828d74c4fbffed87e08e2407af468d 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mapply.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mapply.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -227,7 +227,7 @@ public abstract class Mapply extends RBuiltinNode.Arg3 { values[listIndex] = vecElement; } /* Now call the function */ - result[i] = callNode.execute(frame, function, new RArgsValuesAndNames(values, signature)); + result[i] = callNode.call(frame, function, new RArgsValuesAndNames(values, signature)); } return result; } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Recall.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Recall.java index 5d82e00576a4c32e52906c736c729abb2a467f56..4b9b9eaf9cd30d10d55ce0790014608cdce99e27 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Recall.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Recall.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -68,6 +68,6 @@ public abstract class Recall extends RBuiltinNode.Arg1 { * builtin looks at the arguments passed to the surrounding function. */ RArgsValuesAndNames actualArgs = (RArgsValuesAndNames) readArgs.execute(frame); - return call.execute(frame, function, actualArgs); + return call.call(frame, function, actualArgs); } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java index 300ec8e7def72083fc8cb0987d789d0e4fde64f4..8da59509fff8f7523de4d3ad3a3ff33a0de82738 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java @@ -973,7 +973,7 @@ public class FastRInterop { boolean isCheck = RRuntime.fromLogical(check); getInteropTryState().stepIn(); try { - return call.execute(frame, function, RArgsValuesAndNames.EMPTY); + return call.call(frame, function, RArgsValuesAndNames.EMPTY); } catch (FastRInteropTryException e) { Throwable cause = e.getCause(); CompilerDirectives.transferToInterpreter(); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTestsTry.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTestsTry.java index 7b7a63ecee5326d0496f6f62a05963efa935be69..98c3abe8dcefc817c91057623908ec12caf7aa8b 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTestsTry.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTestsTry.java @@ -55,7 +55,7 @@ public abstract class FastRTestsTry extends RBuiltinNode.Arg1 { @Specialization public Object tryFunc(VirtualFrame frame, RFunction func) { try { - call.execute(frame, func, RArgsValuesAndNames.EMPTY); + call.call(frame, func, RArgsValuesAndNames.EMPTY); } catch (Throwable ex) { // try to recover from a possibly incosistent state when running tests: // some handlers might still be lying around and interfere with subsequent calls diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java index 990cfce958303755520c57f071ad0a844f7b1ec2..edba93cf227be044180e76fbb148bbc26b1ff9d8 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -142,7 +142,7 @@ public final class AccessArgumentNode extends RNode { private void checkPromiseFactory() { if (factory == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); - Closure defaultClosure = formals.getOrCreatePromiseClosure(formals.getDefaultArgument(index)); + Closure defaultClosure = formals.getClosureCache().getOrCreatePromiseClosure(formals.getDefaultArgument(index)); factory = RPromiseFactory.create(PromiseState.Default, defaultClosure); } } 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 6db88a49aa04b81d8f6feadfa53be6346c4dfe2e..53db2b3f5bc23ee9936ee96a74bb4c8f87e8ac40 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -190,7 +190,7 @@ abstract class BaseWriteVariableNode extends WriteVariableNode { } ActiveBinding binding = (ActiveBinding) object; try { - return writeActiveBinding.execute(execFrame, binding.getFunction(), new RArgsValuesAndNames(new Object[]{value}, ArgumentsSignature.empty(1))); + return writeActiveBinding.call(execFrame, binding.getFunction(), new RArgsValuesAndNames(new Object[]{value}, ArgumentsSignature.empty(1))); } finally { binding.setInitialized(true); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LocalReadVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LocalReadVariableNode.java index f3e42feefcbbb14c573b303dd54de1c038a5905e..f67c254ce6760e5525ff1ef194c5455be9f1c515 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LocalReadVariableNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LocalReadVariableNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -131,7 +131,7 @@ public final class LocalReadVariableNode extends Node { if (binding.isHidden() && !binding.isInitialized()) { return null; } - Object readValue = readActiveBinding.execute(frame, binding.getFunction(), RArgsValuesAndNames.EMPTY); + Object readValue = readActiveBinding.call(frame, binding.getFunction(), RArgsValuesAndNames.EMPTY); if (readValue == RMissing.instance) { return null; } 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 74e6cfc821ebe3c9daa8dfdd52cc6a52651d2bba..7104433d3e0e28128ec9a299b078f73820d058ae 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -275,7 +275,7 @@ public final class ReadVariableNode extends RBaseNode { if (binding.isHidden() && !binding.isInitialized()) { throw error(mode == RType.Function ? RError.Message.UNKNOWN_FUNCTION : RError.Message.UNKNOWN_OBJECT, identifier); } - Object readValue = readActiveBinding.execute(frame, binding.getFunction(), RArgsValuesAndNames.EMPTY); + Object readValue = readActiveBinding.call(frame, binding.getFunction(), RArgsValuesAndNames.EMPTY); if (readValue == RMissing.instance) { throw error(mode == RType.Function ? RError.Message.UNKNOWN_FUNCTION : RError.Message.UNKNOWN_OBJECT, identifier); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java index c9baefdb00e4c3eaa15eb028458d5ee2468b3bd7..b4093331fb475e1187560f1609c512fa197ced46 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,6 +55,7 @@ import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor; import com.oracle.truffle.r.runtime.builtins.RBuiltinKind; import com.oracle.truffle.r.runtime.data.Closure; import com.oracle.truffle.r.runtime.data.ClosureCache; +import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.REmpty; import com.oracle.truffle.r.runtime.data.RFunction; @@ -163,7 +164,7 @@ public class ArgumentMatcher { argNodes = suppliedArgs.getArguments(); signature = suppliedArgs.getSignature(); } - return ArgumentMatcher.matchNodes(target, argNodes, signature, s3DefaultArguments, callingNode, arguments, noOpt); + return ArgumentMatcher.matchNodes(target, argNodes, signature, s3DefaultArguments, callingNode, arguments.getClosureCache(), noOpt); } public static MatchPermutation matchArguments(ArgumentsSignature supplied, ArgumentsSignature formal, RBaseNode callingNode, RBuiltinDescriptor builtin) { @@ -327,7 +328,7 @@ public class ArgumentMatcher { * accordingly. */ private static Arguments<RNode> matchNodes(RRootNode target, RNode[] suppliedArgs, ArgumentsSignature suppliedSignature, S3DefaultArguments s3DefaultArguments, RBaseNode callingNode, - ClosureCache closureCache, boolean noOpt) { + RNodeClosureCache closureCache, boolean noOpt) { CompilerAsserts.neverPartOfCompilation(); assert suppliedArgs.length == suppliedSignature.getLength(); @@ -494,13 +495,13 @@ public class ArgumentMatcher { * InfixEmulationFunctions.tilde). */ RNode defaultArg = formals.getDefaultArgument(formalIndex); - Closure defaultClosure = formals.getOrCreatePromiseClosure(defaultArg); + Closure defaultClosure = formals.getClosureCache().getOrCreatePromiseClosure(defaultArg); return PromiseNode.create(RPromiseFactory.create(PromiseState.Default, defaultClosure), noOpt, false); } return ConstantNode.create(formals.getInternalDefaultArgumentAt(formalIndex)); } - private static RNode wrapMatched(FormalArguments formals, RBuiltinDescriptor builtin, ClosureCache closureCache, RNode suppliedArg, int formalIndex, boolean noOpt, FastPathFactory fastPath) { + private static RNode wrapMatched(FormalArguments formals, RBuiltinDescriptor builtin, RNodeClosureCache closureCache, RNode suppliedArg, int formalIndex, boolean noOpt, FastPathFactory fastPath) { // Create promise, unless it's the empty value if (suppliedArg instanceof ConstantNode) { ConstantNode a = (ConstantNode) suppliedArg; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java index f5f5cf0093a4ce1a615879d794cbe79ecb83dad9..aa7f399b5cd68256b509ad420d882144e43b01d9 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,6 @@ package com.oracle.truffle.r.nodes.function; import java.util.Arrays; -import java.util.IdentityHashMap; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; @@ -34,15 +33,14 @@ import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.profiles.BranchProfile; -import com.oracle.truffle.r.nodes.access.FrameSlotNode; import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode; import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseCheckHelperNode; import com.oracle.truffle.r.runtime.Arguments; import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RInternalError; -import com.oracle.truffle.r.runtime.data.Closure; import com.oracle.truffle.r.runtime.data.ClosureCache; +import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RMissing; import com.oracle.truffle.r.runtime.nodes.RBaseNode; @@ -57,7 +55,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; * {@link RootCallTarget} for every argument. * </p> */ -public final class CallArgumentsNode extends RBaseNode implements UnmatchedArguments { +public final class CallArgumentsNode extends RBaseNode { /** * A list of arguments. Single arguments may be <code>null</code>; semantics have to be * specified by implementing classes @@ -66,24 +64,22 @@ public final class CallArgumentsNode extends RBaseNode implements UnmatchedArgum protected final ArgumentsSignature signature; - @Child private FrameSlotNode varArgsSlotNode; @Child private PromiseCheckHelperNode promiseHelper; + private final RNodeClosureCache closureCache = new RNodeClosureCache(); + /** * If a supplied argument is a {@link ReadVariableNode} whose name is "...", this field contains * the index of the name. Otherwise it is an empty list. */ @CompilationFinal(dimensions = 1) private final int[] varArgsSymbolIndices; - private final IdentityHashMap<RNode, Closure> closureCache = new IdentityHashMap<>(); - private CallArgumentsNode(RNode[] arguments, ArgumentsSignature signature, int[] varArgsSymbolIndices) { assert signature != null && signature.getLength() == arguments.length : Arrays.toString(arguments) + " " + signature; this.arguments = arguments; this.signature = signature; assert signature != null; this.varArgsSymbolIndices = varArgsSymbolIndices; - this.varArgsSlotNode = !containsVarArgsSymbol() ? null : FrameSlotNode.create(ArgumentsSignature.VARARG_NAME); } /** @@ -126,6 +122,10 @@ public final class CallArgumentsNode extends RBaseNode implements UnmatchedArgum return varArgs; } + public RNodeClosureCache getClosureCache() { + return closureCache; + } + /** * This methods unrolls all "..." in the argument list. The result varies if the number of * arguments in the varargs or their names change. @@ -262,25 +262,14 @@ public final class CallArgumentsNode extends RBaseNode implements UnmatchedArgum return varArgsSymbolIndices.length > 0; } - public int[] getVarArgsSymbolIndices() { - return varArgsSymbolIndices; - } - - @Override - public IdentityHashMap<RNode, Closure> getContent() { - return closureCache; - } - /** * @return The {@link RNode}s of the arguments given to a function call, in the same order. A * single argument being <code>null</code> means 'argument not provided'. */ - @Override public RNode[] getArguments() { return arguments; } - @Override public ArgumentsSignature getSignature() { return signature; } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FormalArguments.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FormalArguments.java index 6e6db26cb0fccd899fb10c9f7fd4c50d8b6b4f7e..bd6d0baf292d733a7ecd2c8e449d959acfa2d817 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FormalArguments.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FormalArguments.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,6 @@ package com.oracle.truffle.r.nodes.function; import java.util.Arrays; -import java.util.IdentityHashMap; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.RootCallTarget; @@ -31,8 +30,8 @@ import com.oracle.truffle.r.nodes.access.ConstantNode; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.runtime.Arguments; import com.oracle.truffle.r.runtime.ArgumentsSignature; -import com.oracle.truffle.r.runtime.data.Closure; import com.oracle.truffle.r.runtime.data.ClosureCache; +import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RMissing; import com.oracle.truffle.r.runtime.data.RNull; @@ -50,12 +49,10 @@ import com.oracle.truffle.r.runtime.nodes.RNode; * ever one {@link RootCallTarget} for every default argument. * </p> */ -public final class FormalArguments extends Arguments<RNode> implements ClosureCache { +public final class FormalArguments extends Arguments<RNode> { public static final FormalArguments NO_ARGS = new FormalArguments(new RNode[0], new Object[0], ArgumentsSignature.empty(0)); - private final IdentityHashMap<RNode, Closure> closureCache = new IdentityHashMap<>(); - /** * These argument constants define what will be passed along in case there is no supplied * argument for the given argument slot. In the case of normal functions (as opposed to @@ -64,6 +61,8 @@ public final class FormalArguments extends Arguments<RNode> implements ClosureCa */ @CompilationFinal(dimensions = 1) private final Object[] internalDefaultArguments; + private final RNodeClosureCache closureCache = new RNodeClosureCache(); + private FormalArguments(RNode[] defaultArguments, Object[] internalDefaultArguments, ArgumentsSignature signature) { super(defaultArguments, signature); this.internalDefaultArguments = internalDefaultArguments; @@ -117,8 +116,7 @@ public final class FormalArguments extends Arguments<RNode> implements ClosureCa value == RArgsValuesAndNames.EMPTY; } - @Override - public IdentityHashMap<RNode, Closure> getContent() { + public RNodeClosureCache getClosureCache() { return closureCache; } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java index 8001fc01002329f63db99348295bd5e6c34fd54c..e0dbba8ff097a1738b959f3d45146a6656cc1aee 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java @@ -72,6 +72,7 @@ import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.context.TruffleRLanguage; import com.oracle.truffle.r.runtime.data.Closure; import com.oracle.truffle.r.runtime.data.ClosureCache; +import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache; import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RPairList; import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; @@ -87,7 +88,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup; import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; import com.oracle.truffle.r.runtime.nodes.RSyntaxVisitor; -public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNode, RSyntaxFunction, ClosureCache { +public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNode, RSyntaxFunction { private final FormalArguments formalArguments; @@ -108,6 +109,8 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo private SourceSection sourceSectionR; private final SourceSection[] argSourceSections; + private final RNodeClosureCache closureCache = new RNodeClosureCache(); + @Child private RNode saveArguments; @Child private FrameSlotNode onExitSlot; @Child private InlineCacheNode onExitExpressionCache; @@ -380,7 +383,7 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo CompilerDirectives.transferToInterpreter(); RInternalError.shouldNotReachHere("unexpected type for on.exit entry: " + expr.car()); } - onExitExpressionCache.execute(frame, getOrCreateLanguageClosure((RNode) expr.car())); + onExitExpressionCache.execute(frame, closureCache.getOrCreateLanguageClosure((RNode) expr.car())); } } } catch (ReturnException ex) { @@ -565,13 +568,4 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo } return handlerStackSlot; } - - @Override - public IdentityHashMap<RNode, Closure> getContent() { - if (languageClosureCache == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - languageClosureCache = new IdentityHashMap<>(); - } - return languageClosureCache; - } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java index 8167d69768ac27941ebe9caae88aff77e3cb21f6..adb31fe1b3da2c65c83c82b723655ac7d6fd04ea 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,7 +48,7 @@ import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.RType; import com.oracle.truffle.r.runtime.Utils; import com.oracle.truffle.r.runtime.data.Closure; -import com.oracle.truffle.r.runtime.data.ClosureCache; +import com.oracle.truffle.r.runtime.data.ClosureCache.RNodeClosureCache; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RMissing; import com.oracle.truffle.r.runtime.data.RPromise; @@ -356,7 +356,7 @@ public abstract class PromiseNode extends RNode { } @TruffleBoundary - static RNode createVarArgs(RNode[] nodes, ArgumentsSignature signature, ClosureCache closureCache, boolean forcedEager) { + static RNode createVarArgs(RNode[] nodes, ArgumentsSignature signature, RNodeClosureCache closureCache, boolean forcedEager) { return new VarArgsPromiseNode(nodes, signature, closureCache, forcedEager); } @@ -368,7 +368,7 @@ public abstract class PromiseNode extends RNode { private final Closure[] closures; private final ArgumentsSignature signature; - private VarArgsPromiseNode(RNode[] nodes, ArgumentsSignature signature, ClosureCache closureCache, boolean forcedEager) { + private VarArgsPromiseNode(RNode[] nodes, ArgumentsSignature signature, RNodeClosureCache closureCache, boolean forcedEager) { this.promised = new RNode[nodes.length]; this.closures = new Closure[nodes.length]; boolean noOpt = false; 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 8fd61c7ef545fcff84bbf52286b55dff23baa7c4..26612698417fded8af940440169b54085947ab03 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 @@ -85,7 +85,6 @@ import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.RType; import com.oracle.truffle.r.runtime.RVisibility; -import com.oracle.truffle.r.runtime.SubstituteVirtualFrame; import com.oracle.truffle.r.runtime.builtins.FastPathFactory; import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor; import com.oracle.truffle.r.runtime.conn.RConnection; @@ -98,6 +97,7 @@ import com.oracle.truffle.r.runtime.data.REmpty; import com.oracle.truffle.r.runtime.data.RFunction; import com.oracle.truffle.r.runtime.data.RList; import com.oracle.truffle.r.runtime.data.RMissing; +import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RPromise; import com.oracle.truffle.r.runtime.data.RStringVector; import com.oracle.truffle.r.runtime.env.REnvironment; @@ -150,6 +150,8 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS @Child private ReadVariableNode lookupVarArgs; protected final LocalReadVariableNode explicitArgs; + @Child public LocalReadVariableNode explicitCaller; + private final ConditionProfile nullBuiltinProfile = ConditionProfile.createBinaryProfile(); // needed for INTERNAL_GENERIC calls: @@ -160,6 +162,10 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS if (explicitArgs == null) { return RCaller.create(frame, this); } else { + Object explicitCallerValue = explicitCaller.execute(frame); + if (explicitCallerValue != RNull.instance) { + return (RCaller) explicitCallerValue; + } return RCaller.create(frame, RCallerHelper.createFromArguments(function, (RArgsValuesAndNames) explicitArgs.execute(frame))); } } @@ -174,11 +180,12 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS this.signature = signature; } - protected RCallNode(SourceSection sourceSection, Object explicitArgsIdentifier) { + protected RCallNode(SourceSection sourceSection, Object explicitArgsIdentifier, Object explicitCallerIdentifier) { assert sourceSection != null; this.sourceSection = sourceSection; this.arguments = null; this.explicitArgs = LocalReadVariableNode.create(explicitArgsIdentifier, false); + this.explicitCaller = LocalReadVariableNode.create(explicitCallerIdentifier, false); this.varArgIndexes = null; this.lookupVarArgs = null; this.signature = null; @@ -352,22 +359,24 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS RBuiltinDescriptor builtin = builtinProfile.profile(function.getRBuiltin()); RArgsValuesAndNames argAndNames = (RArgsValuesAndNames) explicitArgs.execute(frame); - Object dispatchObject = argAndNames.getArgument(0); - - if (isAttributableProfile.profile(dispatchObject instanceof RAttributeStorage) && isS4Profile.profile(((RAttributeStorage) dispatchObject).isS4())) { - RList list = (RList) promiseHelperNode.checkEvaluate(frame, REnvironment.getRegisteredNamespace("methods").get(".BasicFunsList")); - // TODO create a node that looks up the name in the names attribute - int index = list.getElementIndexByName(builtin.getName()); - if (index != -1) { - RFunction basicFun = (RFunction) list.getDataAt(index); - Object result = call.execute(frame, basicFun, argAndNames, null, null); - if (result != RRuntime.DEFERRED_DEFAULT_MARKER) { - return result; + RStringVector type = null; + if (!argAndNames.isEmpty()) { + Object dispatchObject = argAndNames.getArgument(0); + if (isAttributableProfile.profile(dispatchObject instanceof RAttributeStorage) && isS4Profile.profile(((RAttributeStorage) dispatchObject).isS4())) { + RList list = (RList) promiseHelperNode.checkEvaluate(frame, REnvironment.getRegisteredNamespace("methods").get(".BasicFunsList")); + // TODO create a node that looks up the name in the names attribute + int index = list.getElementIndexByName(builtin.getName()); + if (index != -1) { + RFunction basicFun = (RFunction) list.getDataAt(index); + Object result = call.execute(frame, basicFun, argAndNames, null, null); + if (result != RRuntime.DEFERRED_DEFAULT_MARKER) { + return result; + } } } + type = classHierarchyNode.execute(promiseHelperNode.checkEvaluate(frame, dispatchObject)); } - RStringVector type = argAndNames.isEmpty() ? null : classHierarchyNode.execute(promiseHelperNode.checkEvaluate(frame, dispatchObject)); S3Args s3Args; RFunction resultFunction; if (implicitTypeProfile.profile(type != null)) { @@ -751,8 +760,8 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS * allows to invoke a function with argument(s) supplied by hand. Consider using * {@link com.oracle.truffle.r.nodes.function.call.RExplicitCallNode} instead. */ - public static RCallNode createExplicitCall(Object explicitArgsIdentifier) { - return RCallNodeGen.create(RSyntaxNode.INTERNAL, explicitArgsIdentifier, null); + public static RCallNode createExplicitCall(Object explicitArgsIdentifier, Object explicitCallerIdentifier) { + return RCallNodeGen.create(RSyntaxNode.INTERNAL, explicitArgsIdentifier, explicitCallerIdentifier, null); } static boolean needsSplitting(RootCallTarget target) { @@ -870,9 +879,8 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS if (e == null || e.cachedTarget != cachedTarget) { entry = e = insert(new GenericCallEntry(cachedTarget, createCacheNode(cachedTarget), createArguments(cachedTarget))); } - VirtualFrame frame = SubstituteVirtualFrame.create(materializedFrame); - RArgsValuesAndNames orderedArguments = e.prepareArguments.execute(frame, (RArgsValuesAndNames) varArgs, (S3DefaultArguments) s3DefaultArguments, originalCall); - return e.leafCall.execute(frame, function, orderedArguments, (S3Args) s3Args); + RArgsValuesAndNames orderedArguments = e.prepareArguments.execute(materializedFrame, (RArgsValuesAndNames) varArgs, (S3DefaultArguments) s3DefaultArguments, originalCall); + return e.leafCall.execute(materializedFrame, function, orderedArguments, (S3Args) s3Args); } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnmatchedArguments.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnmatchedArguments.java deleted file mode 100644 index fd0461f0dc9fac10eeb915e7d01ccc980dd7bfb0..0000000000000000000000000000000000000000 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnmatchedArguments.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.r.nodes.function; - -import com.oracle.truffle.r.runtime.ArgumentsSignature; -import com.oracle.truffle.r.runtime.data.ClosureCache; -import com.oracle.truffle.r.runtime.nodes.RNode; - -/** - * An interface all arguments that are going to be matched need to implement. - */ -public interface UnmatchedArguments extends ClosureCache { - /** - * @return The arguments to be matched. Individual {@link RNode}s may be <code>null</code> to - * denote "missingness"! - */ - RNode[] getArguments(); - - ArgumentsSignature getSignature(); -} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitBaseEnvCallDispatcher.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitBaseEnvCallDispatcher.java index 0fd6147435e0e4362e448097d9dd2a8b3dfab2f8..2a26de3d44ad602db2f991a6231ab2317a882dc1 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitBaseEnvCallDispatcher.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitBaseEnvCallDispatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -63,7 +63,7 @@ public abstract class RExplicitBaseEnvCallDispatcher extends Node { @Specialization public Object doCached(VirtualFrame frame, RArgsValuesAndNames arguments, @Cached("getFunction(frame)") RFunction function) { - return callNode.execute(frame, function, arguments); + return callNode.call(frame, function, arguments); } RFunction getFunction(VirtualFrame frame) { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitCallNode.java index 3a4af7be70543373b97334da11591ad97ba1b7ec..d58a059ae2b47b59c9d1211d09d2a955a625fcc5 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitCallNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitCallNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,10 +30,11 @@ import com.oracle.truffle.api.frame.FrameSlot; import com.oracle.truffle.api.frame.FrameSlotKind; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.r.nodes.function.RCallBaseNode; import com.oracle.truffle.r.nodes.function.RCallNode; +import com.oracle.truffle.r.runtime.RCaller; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RFunction; +import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; import com.oracle.truffle.r.runtime.env.frame.RFrameSlot; @@ -46,27 +47,39 @@ public abstract class RExplicitCallNode extends Node { return RExplicitCallNodeGen.create(); } - public abstract Object execute(VirtualFrame frame, RFunction function, RArgsValuesAndNames args); + public final Object call(VirtualFrame frame, RFunction function, RArgsValuesAndNames args) { + return execute(frame, function, args, null); + } + + public abstract Object execute(VirtualFrame frame, RFunction function, RArgsValuesAndNames args, RCaller explicitCaller); private final RFrameSlot argsIdentifier = RFrameSlot.createTemp(true); + private final RFrameSlot callerIdentifier = RFrameSlot.createTemp(true); @CompilationFinal private FrameSlot argsFrameSlot; + @CompilationFinal private FrameSlot callerFrameSlot; @Specialization - protected Object doCall(VirtualFrame frame, RFunction function, RArgsValuesAndNames args, - @Cached("createExplicitCall()") RCallBaseNode call) { + protected Object doCall(VirtualFrame frame, RFunction function, RArgsValuesAndNames args, RCaller caller, + @Cached("createExplicitCall()") RCallNode call) { if (argsFrameSlot == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); argsFrameSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frame.getFrameDescriptor(), argsIdentifier, FrameSlotKind.Object); } + if (callerFrameSlot == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + callerFrameSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frame.getFrameDescriptor(), callerIdentifier, FrameSlotKind.Object); + } try { FrameSlotChangeMonitor.setObject(frame, argsFrameSlot, args); + FrameSlotChangeMonitor.setObject(frame, callerFrameSlot, caller == null ? RNull.instance : caller); return call.execute(frame, function); } finally { FrameSlotChangeMonitor.setObject(frame, argsFrameSlot, null); + FrameSlotChangeMonitor.setObject(frame, callerFrameSlot, null); } } - protected RCallBaseNode createExplicitCall() { - return RCallNode.createExplicitCall(argsIdentifier); + protected RCallNode createExplicitCall() { + return RCallNode.createExplicitCall(argsIdentifier, callerIdentifier); } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCaller.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCaller.java index 646850b0d719b8394561ccbe10b773bab4f374b1..68acee4338a7f57060cf575d9039893c72a1e456 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCaller.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCaller.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,14 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxElement; /** * Represents the caller of a function and stored in {@link RArguments}. A value of this type never - * appears in a Truffle execution. + * appears in a Truffle execution. Caller remembers its parent caller and frame number as described + * in {@code sys.parent} R function documentation: frames are numbered from 0 (global environment), + * parent does not have to have the frame with number one less, e.g. with do.call(fun, args, envir) + * when fun asks for parent, it should get 'envir', moreover, when evaluating promises parent frame + * and frame with number one less are typically also not the same frames. See also builtins in + * {@code FrameFunctions} for more details. + * + * @see RArguments */ public final class RCaller { @@ -47,23 +54,24 @@ public final class RCaller { private final RCaller parent; /** * The payload can be an RSyntaxNode, a {@link Supplier}, or an {@link RCaller} (which marks - * promise evaluation frames). + * promise evaluation frames). Payload represents the syntax (AST) of how the function was + * invoked. If the function was invoked via regular call node, then the syntax can be that call + * node (RSyntaxNode case), if the function was invoked by other means and we do not have the + * actual syntax for the invocation, we only provide it lazily via Supplier, so that we do not + * have to always construct the AST nodes. */ private final Object payload; - /** - * Marks those callers whose parent should not be taken into account when iterating R level - * frames using e.g. {@code parent.frame()}. This is the case for function invoked through - * {@code do.call} -- R pretends that they were called by the caller of {@code do.call} so that - * code like {@code eval(formula, parent.frame(2))} gives the same results regardless of whether - * the function was invoked directly or through {@code do.call}. - */ - private final boolean parentIsInternal; - private RCaller(Frame callingFrame, Object nodeOrSupplier, boolean parentIsInternal) { + private RCaller(Frame callingFrame, Object nodeOrSupplier) { this.depth = depthFromFrame(callingFrame); this.parent = parentFromFrame(callingFrame); this.payload = nodeOrSupplier; - this.parentIsInternal = parentIsInternal; + } + + private RCaller(int depth, RCaller parent, Object nodeOrSupplier) { + this.depth = depth; + this.parent = parent; + this.payload = nodeOrSupplier; } private static int depthFromFrame(Frame callingFrame) { @@ -74,17 +82,6 @@ public final class RCaller { return callingFrame == null ? null : RArguments.getCall(callingFrame); } - private RCaller(int depth, RCaller parent, Object nodeOrSupplier, boolean parentIsInternal) { - this.depth = depth; - this.parent = parent; - this.payload = nodeOrSupplier; - this.parentIsInternal = parentIsInternal; - } - - public RCaller withInternalParent() { - return new RCaller(depth, parent, payload, true); - } - public int getDepth() { return depth; } @@ -93,10 +90,6 @@ public final class RCaller { return parent; } - public boolean hasInternalParent() { - return parentIsInternal; - } - public RSyntaxElement getSyntaxNode() { assert payload != null && !(payload instanceof RCaller) : payload == null ? "null RCaller" : "promise RCaller"; return payload instanceof RSyntaxElement ? (RSyntaxElement) payload : (RSyntaxElement) ((Supplier<?>) payload).get(); @@ -115,42 +108,42 @@ public final class RCaller { } public static RCaller createInvalid(Frame callingFrame) { - return new RCaller(callingFrame, null, false); + return new RCaller(callingFrame, null); } public static RCaller createInvalid(Frame callingFrame, RCaller parent) { - return new RCaller(depthFromFrame(callingFrame), parent, null, false); + return new RCaller(depthFromFrame(callingFrame), parent, null); } public static RCaller create(Frame callingFrame, RSyntaxElement node) { assert node != null; - return new RCaller(callingFrame, node, false); + return new RCaller(callingFrame, node); } public static RCaller create(Frame callingFrame, RCaller parent, RSyntaxElement node) { assert node != null; - return new RCaller(depthFromFrame(callingFrame), parent, node, false); + return new RCaller(depthFromFrame(callingFrame), parent, node); } - public static RCaller createWithInternalParent(Frame callingFrame, Supplier<RSyntaxElement> supplier) { + public static RCaller create(Frame callingFrame, Supplier<RSyntaxElement> supplier) { assert supplier != null; - return new RCaller(callingFrame, supplier, true); + return new RCaller(callingFrame, supplier); } - public static RCaller create(Frame callingFrame, Supplier<RSyntaxElement> supplier) { - assert supplier != null; - return new RCaller(callingFrame, supplier, false); + public static RCaller create(int depth, RCaller parent, Object payload) { + assert payload != null; + return new RCaller(depth, parent, payload); } public static RCaller create(Frame callingFrame, RCaller parent, Supplier<RSyntaxElement> supplier) { assert supplier != null; - return new RCaller(depthFromFrame(callingFrame), parent, supplier, false); + return new RCaller(depthFromFrame(callingFrame), parent, supplier); } public static RCaller createForPromise(RCaller originalCaller, Frame frame) { int newDepth = frame == null ? 0 : RArguments.getDepth(frame); RCaller originalCall = frame == null ? null : RArguments.getCall(frame); - return new RCaller(newDepth, originalCaller, originalCall, false); + return new RCaller(newDepth, originalCaller, originalCall); } public boolean getVisibility() { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ClosureCache.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ClosureCache.java index a8976382cd81765d33f10da5b45468cc6983709f..f84c8201bb212ca26897996401038fb899630543 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ClosureCache.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ClosureCache.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,49 +22,65 @@ */ package com.oracle.truffle.r.runtime.data; -import java.util.IdentityHashMap; -import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.r.runtime.context.RContext; +import com.oracle.truffle.r.runtime.nodes.RBaseNode; import com.oracle.truffle.r.runtime.nodes.RNode; +import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; /** - * A trait that enables the caching of {@link Closure}s for certain expressions ({@link RNode}s). + * Class that enables the caching of {@link Closure}s for certain expressions ({@link RNode}s). + * Instance of this class is supposed to be a field in AST node, which is using the cache. The field + * must be initialized in the constructor, but the underlying data structure (ConcurrentHashMap) is + * initialized lazily. All methods are thread safe. + * + * Closures need to be cached so that we cache the corresponding call-targets and if the expression + * is evaluated again, we invoke it through the same call-target, which may be compiled by Truffle. */ -public interface ClosureCache { +public abstract class ClosureCache<K> { - default Closure getOrCreatePromiseClosure(RNode expr) { - return getOrCreateClosure(Closure.PROMISE_CLOSURE_WRAPPER_NAME, expr); + private ConcurrentHashMap<K, Closure> cache; + + public Closure getOrCreatePromiseClosure(K key) { + return getOrCreateClosure(Closure.PROMISE_CLOSURE_WRAPPER_NAME, key); } - default Closure getOrCreateLanguageClosure(RNode expr) { - return getOrCreateClosure(Closure.LANGUAGE_CLOSURE_WRAPPER_NAME, expr); + public Closure getOrCreateLanguageClosure(K key) { + return getOrCreateClosure(Closure.LANGUAGE_CLOSURE_WRAPPER_NAME, key); } - /** - * @param expr - * @return A {@link Closure} representing the given {@link RNode}. If expr is <code>null</code> - * <code>null</code> is returned. - */ + protected abstract RBaseNode keyToNode(K key); + @TruffleBoundary - default Closure getOrCreateClosure(String name, RNode expr) { - if (expr == null) { + private Closure getOrCreateClosure(String name, K key) { + if (key == null) { return null; } + if (cache == null) { + initMap(); + } + return cache.computeIfAbsent(key, k -> Closure.create(name, keyToNode(k))); + } - IdentityHashMap<RNode, Closure> cache = getContent(); - Closure result = cache.get(expr); - if (result == null) { - result = Closure.create(name, expr); - cache.put(expr, result); + private synchronized void initMap() { + if (cache == null) { + cache = new ConcurrentHashMap<>(); } - return result; } - /** - * Access to the raw content. - * - * @return The {@link Map} containing the cached values - */ - IdentityHashMap<RNode, Closure> getContent(); + public static final class RNodeClosureCache extends ClosureCache<RBaseNode> { + @Override + protected RBaseNode keyToNode(RBaseNode key) { + return key; + } + } + + public static final class SymbolClosureCache extends ClosureCache<String> { + @Override + protected RNode keyToNode(String key) { + return RContext.getASTBuilder().lookup(RSyntaxNode.SOURCE_UNAVAILABLE, key, false).asRNode(); + } + } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test index 27a1af0f7972a7bf2d73405f23b0549990c138a4..7550b1b88e80478fb75c145a6122116e3c67ef70 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test @@ -22889,6 +22889,10 @@ NULL #argv <- structure(list(expr = expression(quote(x <- c(1, x)))), .Names = 'expr');do.call('.doTrace', argv) NULL +##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall# +#do.call('c', list()) +NULL + ##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall#Output.IgnoreErrorContext# #typeof(do.call(function(x) x, list(as.symbol('foo')))) Error in (function (x) : object 'foo' not found @@ -73322,6 +73326,36 @@ v() [1] "x" +##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameWithPromises# +#{ top <- function(vtop) vtop;foo <- function(vfoo) top(vfoo);boo <- function(vboo) foo(sys.frame(vboo));bar <- function(vbar) do.call(boo, list(vbar), envir = parent.frame(2));baz <- function(vbaz) bar(vbaz);start <- function(vstart) baz(vstart);lapply(lapply(0:8, function(i) start(i)), function(env) sort(tolower(ls(env)))); } +[[1]] +[1] "bar" "baz" "boo" "foo" "start" "top" + +[[2]] +[1] "fun" "i" "x" + +[[3]] +[1] "fun" "i" "x" + +[[4]] +[1] "i" + +[[5]] +[1] "vstart" + +[[6]] +[1] "vbaz" + +[[7]] +[1] "vbar" + +[[8]] +[1] "args" "envir" "quote" "what" + +[[9]] +[1] "vboo" + + ##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframes.frameAccessCommonTest#Ignored.ImplementationError# #{ foo <- function(x) lapply(sys.frames(), function(x) ls(x));bar <- function(ba) do.call(foo, list(ba));boo <- function(bo) bar(bo);callboo <- function(cb) do.call('boo', list(cb));fun <- function(f) callboo(f);fun(42);} [[1]] @@ -164585,6 +164619,11 @@ Error in prod("a") : invalid 'type' (character) of argument Called from: top level +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testBrowser# +#{ bar <- function(vbar) do.call(browser, list(), envir = parent.frame(2));baz <- function(vbaz) bar(vbaz);start <- function(vstart) baz(vstart);start(42); }<<<NEWLINE>>>ls()<<<NEWLINE>>>c +Called from: do.call(browser, list(), envir = parent.frame(2)) +[1] "vstart" + ##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testConditionalBreakpoint# #fun <- function(x) { cat('x='); cat(x); cat('\n') }; trace(fun, quote(if (x > 10) browser())); fun(10)<<<NEWLINE>>>; fun(11)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>> [1] "fun" diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_docall.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_docall.java index 0f496903658c8919cac514ad4ff868f32aaf52e6..85d9ed9b4db6ea6b7c5de1ecba7517ee1cf65f12 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_docall.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_docall.java @@ -4,7 +4,7 @@ * http://www.gnu.org/licenses/gpl-2.0.html * * Copyright (c) 2012-2014, Purdue University - * Copyright (c) 2013, 2017, Oracle and/or its affiliates + * Copyright (c) 2013, 2018, Oracle and/or its affiliates * * All rights reserved. */ @@ -48,5 +48,7 @@ public class TestBuiltin_docall extends TestBase { assertEval("{ boo <- function(c) ls(parent.frame(2)); foo <- function(a,b) boo(a); bar <- function(x,z) do.call('foo', list(parse(text='goo()'),2)); bar() }"); assertEval("{ boo <- function(c) ls(parent.frame(3)); foo <- function(a,b) boo(a); bar <- function(x,z) do.call('foo', list(parse(text='goo()'),2)); baz <- function(bazX) bar(bazX,1); baz(); }"); assertEval("{ f1 <- function(a) ls(parent.frame(2)); f2 <- function(b) f1(b); f3 <- function(c) f2(c); f4 <- function(d) do.call('f3', list(d)); f4(42); }"); + + assertEval("do.call('c', list())"); } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_sysframe.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_sysframe.java index a318459bdd25082bc2360589ba779b3f89e4aede..044f6802683a1ce19d8050800e34b8ac1fc8dd42 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_sysframe.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_sysframe.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,4 +33,17 @@ public class TestBuiltin_sysframe extends TestBase { public void frameAccessCommonTest() { assertEval("{ foo <- function(x) lapply(1:7, function(x) ls(sys.frame(x)));" + SYS_PARENT_SETUP + "}"); } + + @Test + public void sysFrameWithPromises() { + // Note: the parameter names help to identify the corresponding environments in the output + assertEval( + "{ top <- function(vtop) vtop;" + + "foo <- function(vfoo) top(vfoo);" + + "boo <- function(vboo) foo(sys.frame(vboo));" + + "bar <- function(vbar) do.call(boo, list(vbar), envir = parent.frame(2));" + + "baz <- function(vbaz) bar(vbaz);" + + "start <- function(vstart) baz(vstart);" + + "lapply(lapply(0:8, function(i) start(i)), function(env) sort(tolower(ls(env)))); }"); + } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java index 6f94e4071b72db88881ce0c0c731b3c001b5396a..baca92411e714b465d28f5a3496ea4905f2ea219 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java @@ -130,6 +130,11 @@ public class TestInteractiveDebug extends TestBase { assertEval("do.call('browser', list())\nc\n"); assertEval("browser()\nwhere\nc\n"); assertEval("options(error=browser); prod('a')\nwhere\nc\n"); + assertEval( + "{ bar <- function(vbar) do.call(browser, list(), envir = parent.frame(2));" + + "baz <- function(vbaz) bar(vbaz);" + + "start <- function(vstart) baz(vstart);" + + "start(42); }\nls()\nc"); } @Test