diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OnExit.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OnExit.java index c1bc7253e3734145ae3e0e57a2c8927f41f6cbe0..4dabafd7787fd836664697b4e600e68c6e29ece2 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OnExit.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OnExit.java @@ -44,6 +44,7 @@ import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.builtins.RBuiltin; import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RPromise; +import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; import com.oracle.truffle.r.runtime.env.frame.RFrameSlot; @RBuiltin(name = "on.exit", visibility = OFF, kind = PRIMITIVE, parameterNames = {"expr", "add"}, nonEvalArgs = 0, behavior = COMPLEX) @@ -71,7 +72,7 @@ public abstract class OnExit extends RBuiltinNode.Arg2 { if (onExitSlot == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); - onExitSlot = frame.getFrameDescriptor().findOrAddFrameSlot(RFrameSlot.OnExit, FrameSlotKind.Object); + onExitSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frame.getFrameDescriptor(), RFrameSlot.OnExit, FrameSlotKind.Object); } // the empty (RNull.instance) expression is used to clear on.exit diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java index 25883797eef9b10da63e2df558e92940ffeafdf3..c59f4dbc43c325a0046106d0e04140d1123b77f4 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java @@ -23,7 +23,6 @@ package com.oracle.truffle.r.nodes.builtin.fastr; import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.equalTo; -import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.gt; import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf; import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.notEmpty; import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement; @@ -42,6 +41,7 @@ import com.oracle.truffle.api.interop.java.JavaInterop; import com.oracle.truffle.api.vm.PolyglotEngine; import com.oracle.truffle.r.nodes.builtin.NodeWithArgumentCasts.Casts; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; +import com.oracle.truffle.r.runtime.FastROptions; import com.oracle.truffle.r.runtime.RChannel; import com.oracle.truffle.r.runtime.RCmdOptions.Client; import com.oracle.truffle.r.runtime.RError; @@ -50,6 +50,8 @@ import com.oracle.truffle.r.runtime.RSource; import com.oracle.truffle.r.runtime.builtins.RBuiltin; import com.oracle.truffle.r.runtime.context.ContextInfo; import com.oracle.truffle.r.runtime.context.RContext; +import com.oracle.truffle.r.runtime.context.RContext.ContextKind; +import com.oracle.truffle.r.runtime.context.RContext.EvalThread; import com.oracle.truffle.r.runtime.data.RDataFactory; import com.oracle.truffle.r.runtime.data.RIntVector; import com.oracle.truffle.r.runtime.data.RList; @@ -57,6 +59,7 @@ import com.oracle.truffle.r.runtime.data.RMissing; import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector; import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; +import com.oracle.truffle.r.runtime.env.REnvironment; /** * The FastR builtins that allow multiple "virtual" R sessions potentially executing in parallel. @@ -70,11 +73,8 @@ public class FastRContext { private static void kind(Casts casts) { casts.arg("kind").mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst().mustNotBeNA().mustBe( - equalTo(RContext.ContextKind.SHARE_NOTHING.name()).or(equalTo(RContext.ContextKind.SHARE_PARENT_RW.name()).or(equalTo(RContext.ContextKind.SHARE_PARENT_RO.name())))); - } - - private static void pc(Casts casts) { - casts.arg("pc").asIntegerVector().findFirst().mustNotBeNA().mustBe(gt(0)); + equalTo(RContext.ContextKind.SHARE_NOTHING.name()).or(equalTo(RContext.ContextKind.SHARE_PARENT_RW.name()).or( + equalTo(RContext.ContextKind.SHARE_PARENT_RO.name()).or(equalTo(RContext.ContextKind.SHARE_ALL.name()))))); } private static void key(Casts casts) { @@ -95,40 +95,62 @@ public class FastRContext { } } + private static void handleSharedContexts(ContextKind contextKind) { + if (contextKind == ContextKind.SHARE_ALL) { + RContext current = RContext.getInstance(); + if (EvalThread.threads.size() == 0 && (current.isInitial() || current.getKind() == ContextKind.SHARE_PARENT_RW)) { + ContextInfo.resetMultiSlotIndexGenerator(); + } else { + throw RError.error(RError.NO_CALLER, RError.Message.GENERIC, "Shared contexts can be created only if no other child contexts exist"); + } + } + } + /** * Similar to {@code .fastr.context.eval} but the invoking thread does not wait for completion, * which is done by {@code .fastr.context.join}. The result is a vector that should be passed to * {@code .fastr.context.join}. * */ - @RBuiltin(name = ".fastr.context.spawn", kind = PRIMITIVE, parameterNames = {"exprs", "pc", "kind"}, behavior = COMPLEX) - public abstract static class Spawn extends RBuiltinNode.Arg3 { + @RBuiltin(name = ".fastr.context.spawn", kind = PRIMITIVE, parameterNames = {"exprs", "kind"}, behavior = COMPLEX) + public abstract static class Spawn extends RBuiltinNode.Arg2 { @Override public Object[] getDefaultParameterValues() { - return new Object[]{RMissing.instance, 1, "SHARE_NOTHING"}; + return new Object[]{RMissing.instance, FastROptions.SharedContexts.getBooleanValue() ? "SHARE_ALL" : "SHARE_NOTHING"}; } static { Casts casts = new Casts(Spawn.class); CastsHelper.exprs(casts); - CastsHelper.pc(casts); CastsHelper.kind(casts); } @Specialization @TruffleBoundary - protected RIntVector spawn(RAbstractStringVector exprs, int pc, String kind) { + protected RIntVector spawn(RAbstractStringVector exprs, String kind) { RContext.ContextKind contextKind = RContext.ContextKind.valueOf(kind); - RContext.EvalThread[] threads = new RContext.EvalThread[pc]; - int[] data = new int[pc]; - for (int i = 0; i < pc; i++) { + if (FastROptions.SharedContexts.getBooleanValue() && contextKind != ContextKind.SHARE_ALL) { + throw RError.error(RError.NO_CALLER, RError.Message.GENERIC, "Only shared contexts are allowed"); + } + handleSharedContexts(contextKind); + + int length = exprs.getLength(); + RContext.EvalThread[] threads = new RContext.EvalThread[length]; + int[] data = new int[length]; + for (int i = 0; i < length; i++) { ContextInfo info = createContextInfo(contextKind); - threads[i] = new RContext.EvalThread(info, RSource.fromTextInternal(exprs.getDataAt(i % exprs.getLength()), RSource.Internal.CONTEXT_EVAL)); + threads[i] = new RContext.EvalThread(info, RSource.fromTextInternalInvisible(exprs.getDataAt(i % exprs.getLength()), RSource.Internal.CONTEXT_EVAL)); data[i] = info.getId(); } - for (int i = 0; i < pc; i++) { + if (contextKind == ContextKind.SHARE_ALL) { + REnvironment.convertSearchpathToMultiSlot(); + } + for (int i = 0; i < length; i++) { threads[i].start(); } + for (int i = 0; i < length; i++) { + threads[i].waitForInit(); + } return RDataFactory.createIntVector(data, RDataFactory.COMPLETE_VECTOR); } } @@ -173,46 +195,56 @@ public class FastRContext { * sublist contains the result of the evaluation with name "result". It may also have an * attribute "error" if the evaluation threw an exception, in which case the result will be NA. */ - @RBuiltin(name = ".fastr.context.eval", kind = PRIMITIVE, parameterNames = {"exprs", "pc", "kind"}, behavior = COMPLEX) - public abstract static class Eval extends RBuiltinNode.Arg3 { + @RBuiltin(name = ".fastr.context.eval", kind = PRIMITIVE, parameterNames = {"exprs", "kind"}, behavior = COMPLEX) + public abstract static class Eval extends RBuiltinNode.Arg2 { @Override public Object[] getDefaultParameterValues() { - return new Object[]{RMissing.instance, 1, "SHARE_NOTHING"}; + return new Object[]{RMissing.instance, FastROptions.SharedContexts.getBooleanValue() ? "SHARE_ALL" : "SHARE_NOTHING"}; } static { Casts casts = new Casts(Eval.class); CastsHelper.exprs(casts); - CastsHelper.pc(casts); CastsHelper.kind(casts); } @Specialization @TruffleBoundary - protected Object eval(RAbstractStringVector exprs, int pc, String kind) { + protected Object eval(RAbstractStringVector exprs, String kind) { RContext.ContextKind contextKind = RContext.ContextKind.valueOf(kind); + if (FastROptions.SharedContexts.getBooleanValue() && contextKind != ContextKind.SHARE_ALL) { + throw RError.error(RError.NO_CALLER, RError.Message.GENERIC, "Only shared contexts are allowed"); + } + handleSharedContexts(contextKind); - Object[] results = new Object[pc]; - if (pc == 1) { + int length = exprs.getLength(); + Object[] results = new Object[length]; + if (length == 1) { ContextInfo info = createContextInfo(contextKind); PolyglotEngine vm = info.createVM(); try { - results[0] = RContext.EvalThread.run(vm, info, RSource.fromTextInternal(exprs.getDataAt(0), RSource.Internal.CONTEXT_EVAL)); + results[0] = RContext.EvalThread.run(vm, info, RSource.fromTextInternalInvisible(exprs.getDataAt(0), RSource.Internal.CONTEXT_EVAL)); } finally { vm.dispose(); } } else { // separate threads that run in parallel; invoking thread waits for completion - RContext.EvalThread[] threads = new RContext.EvalThread[pc]; - for (int i = 0; i < pc; i++) { + RContext.EvalThread[] threads = new RContext.EvalThread[length]; + for (int i = 0; i < length; i++) { ContextInfo info = createContextInfo(contextKind); - threads[i] = new RContext.EvalThread(info, RSource.fromTextInternal(exprs.getDataAt(i % exprs.getLength()), RSource.Internal.CONTEXT_EVAL)); + threads[i] = new RContext.EvalThread(info, RSource.fromTextInternalInvisible(exprs.getDataAt(i % exprs.getLength()), RSource.Internal.CONTEXT_EVAL)); + } + if (contextKind == ContextKind.SHARE_ALL) { + REnvironment.convertSearchpathToMultiSlot(); } - for (int i = 0; i < pc; i++) { + for (int i = 0; i < length; i++) { threads[i].start(); } + for (int i = 0; i < length; i++) { + threads[i].waitForInit(); + } try { - for (int i = 0; i < pc; i++) { + for (int i = 0; i < length; i++) { threads[i].join(); results[i] = threads[i].getEvalResult(); } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/parallel/R/forkcluster_overrides.R b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/parallel/R/forkcluster_overrides.R index 42d5c9ce8b44849166eb70e73832f781475e471e..2d3ebf6b49a58ea052b43953b3c3e1c47aaaad39 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/parallel/R/forkcluster_overrides.R +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/parallel/R/forkcluster_overrides.R @@ -11,22 +11,35 @@ ## Derived from snow and parallel packages -eval(expression( +eval(expression({ + +# overwritten functions: + +makeCluster <- function (spec, type = getClusterOption("type"), ...) { + switch(type, + PSOCK = makePSOCKcluster(spec, ...), + FORK = makeForkCluster(spec, ...), + SOCK = snow::makeSOCKcluster(spec, ...), + MPI = snow::makeMPIcluster(spec, ...), + NWS = snow::makeNWScluster(spec, ...), + SHARED = makeSHAREDcluster(spec, ...), # this line was added + stop("unknown cluster type")) +} + +# added functions: + closeNode.SHAREDnode <- function(node) { .fastr.channel.close(node$channel) -}), asNamespace("parallel")) +} -eval(expression( sendData.SHAREDnode <- function(node, data) { .fastr.channel.send(node$channel, data) -}), asNamespace("parallel")) +} -eval(expression( recvData.SHAREDnode <- function(node) { .fastr.channel.receive(node$channel) -}), asNamespace("parallel")) +} -eval(expression( recvOneData.SHAREDcluster <- function(cl) { channel_ids = lapply(cl, function(l) l[["channel"]]) res <- .fastr.channel.select(channel_ids) @@ -35,57 +48,67 @@ recvOneData.SHAREDcluster <- function(cl) { indexes = lapply(cl, function(l, id) if (identical(l[["channel"]], id)) id else as.integer(NA), id=selected_id) node_ind = which(as.double(indexes)==as.double(selected_id)) list(node = node_ind, value = res[[2]]) -}), asNamespace("parallel")) - -eval(expression( -fastr.newSHAREDnode <- function(rank, options = defaultClusterOptions) -{ - # Add the "debug" option defaulted to FALSE, if the user didn't specify - # If the user gives TRUE, print extra stuff during cluster setup - debug <- FALSE - tryCatch( - debug <- parallel:::getClusterOption("debug", options), - error = function(e) { } - ) - options <- parallel:::addClusterOptions(options, list(debug = debug)) +} - # generate unique values for channel keys (addition factor is chosen based on how snow generates port numbers) - port <- as.integer(parallel:::getClusterOption("port", options) + rank * 1000) - script <- file.path(R.home(), "com.oracle.truffle.r.native", "library", "parallel", "RSHAREDnode.R") +newSHAREDnodes <- function(nnodes, debug, options = defaultClusterOptions) { + context_code <- vector("character", nnodes) + contexts <- vector("integer", nnodes) + channels <- vector("integer", nnodes) + outfile <- getClusterOption("outfile", options) + for (i in 1:nnodes) { + # generate unique values for channel keys (addition factor is chosen based on how snow generates port numbers) + port <- as.integer(parallel:::getClusterOption("port", options) + i * 1000) + + startup <- substitute(local({ + makeSHAREDmaster <- function(key) { + channel <- .fastr.channel.get(as.integer(key)) + structure(list(channel=channel), class = "SHAREDnode") + } + parallel:::sinkWorkerOutput(OUTFILE) + parallel:::slaveLoop(makeSHAREDmaster(PORT)) + }), list(OUTFILE=outfile, PORT=port)) + + context_code[[i]] <- paste0(deparse(startup), collapse="\n") + if (isTRUE(debug)) cat(sprintf("Starting context: %d with code %s\n", i, context_code[[i]])) - context_code <- paste0("commandArgs<-function() c('--args', 'PORT=", port, "'); source('", script, "')") - if (isTRUE(debug)) cat(sprintf("Starting context: %d with code %s\n", rank, context_code)) - - cx <- .fastr.context.spawn(context_code) - - ## Need to return a list here, in the same form as the - ## "cluster" data structure. - channel <- .fastr.channel.create(port) - if (isTRUE(debug)) cat(sprintf("Context %d started!\n", rank)) - structure(list(channel = channel, context=cx, rank = rank), class = "SHAREDnode") -}), asNamespace("parallel")) + ## Need to return a list here, in the same form as the + ## "cluster" data structure. + channels[[i]] <- .fastr.channel.create(port) + if (isTRUE(debug)) cat(sprintf("Context %d started!\n", i)) + } + contexts <- .fastr.context.spawn(context_code) + cl <- vector("list", nnodes) + for (i in 1:nnodes) { + cl[[i]] <- structure(list(channel = channels[[i]], context=contexts[[i]], rank = i), class = "SHAREDnode") + } + cl +} -makeForkClusterExpr <- expression({ -makeForkCluster <- function(nnodes = getOption("mc.cores", 2L), options = defaultClusterOptions, ...) -{ +makeSHAREDcluster <- function(nnodes = getOption("mc.cores", 2L), options = defaultClusterOptions, ...) { nnodes <- as.integer(nnodes) if(is.na(nnodes) || nnodes < 1L) stop("'nnodes' must be >= 1") .check_ncores(nnodes) options <- addClusterOptions(options, list(...)) - cl <- vector("list", nnodes) - for (i in seq_along(cl)) cl[[i]] <- fastr.newSHAREDnode(rank=i, options=options) + + # Add the "debug" option defaulted to FALSE, if the user didn't specify + # If the user gives TRUE, print extra stuff during cluster setup + debug <- FALSE + if (exists("debug", envir=options, inherits=FALSE)) { + debug <- parallel:::getClusterOption("debug", options) + } else { + options <- parallel:::addClusterOptions(options, list(debug = debug)) + } + + cl <- newSHAREDnodes(nnodes, debug = debug, options=options) class(cl) <- c("SHAREDcluster", "cluster") cl -}; environment(makeForkCluster)<-asNamespace("parallel")}) -eval(makeForkClusterExpr, asNamespace("parallel")) -# seems like we don't need these anymore, but let's make sure -#eval(makeForkClusterExpr, as.environment("package:parallel")) - +} -eval(expression( stopCluster.SHAREDcluster <- function(cl) { for (n in cl) { parallel:::postNode(n, "DONE") .fastr.context.join(n$context) } +} + }), asNamespace("parallel")) 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 f9e15b0a6b16368280b78ca0151f666b4f24db0e..5df1356f04147c5cae76348b71b5d1212b09636f 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 @@ -74,6 +74,7 @@ import com.oracle.truffle.r.runtime.env.frame.ActiveBinding; import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor.FrameAndSlotLookupResult; import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor.LookupResult; +import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor.MultiSlotData; import com.oracle.truffle.r.runtime.nodes.RBaseNode; import com.oracle.truffle.r.runtime.nodes.RSourceSectionNode; import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup; @@ -570,8 +571,16 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta */ FrameSlot localSlot = variableFrame.getFrameDescriptor().findFrameSlot(identifier); // non-local reads can only be handled in a simple way if they are successful - if (localSlot != null && checkTypeSlowPath(frame, getValue(seenValueKinds, variableFrame, localSlot))) { - return new Match(localSlot); + if (localSlot != null) { + Object val = getValue(seenValueKinds, variableFrame, localSlot); + if (checkTypeSlowPath(frame, val)) { + if (val instanceof MultiSlotData) { + RError.performanceWarning("polymorphic (slow path) lookup of symbol \"" + identifier + "\" from a multi slot"); + return new Polymorphic(variableFrame); + } else { + return new Match(localSlot); + } + } } /* diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java index 13c607e358851a5b39313b9fc650e64b2d2aa620..2eb77334c9d86cdb3b18af0e955c8b07a448931b 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java @@ -60,6 +60,11 @@ abstract class ReplacementNode extends OperatorNode { this.lhs = lhs; } + @Override + public final Node deepCopy() { + return RContext.getASTBuilder().process(this).asRNode(); + } + public static ReplacementNode create(SourceSection source, RSyntaxLookup operator, RNode target, RSyntaxElement lhs, RNode rhs, List<RSyntaxCall> calls, String targetVarName, boolean isSuper, int tempNamesStartIndex, boolean isVoid) { CompilerAsserts.neverPartOfCompilation(); @@ -145,7 +150,7 @@ abstract class ReplacementNode extends OperatorNode { RSyntaxNode[] newArgs = new RSyntaxNode[2]; newArgs[0] = (RSyntaxNode) oldArgs[0]; newArgs[1] = builder.lookup(oldArgs[1].getLazySourceSection(), ((RSyntaxLookup) oldArgs[1]).getIdentifier() + "<-", true); - newSyntaxLHS = RCallSpecialNode.createCall(callLHS.getLazySourceSection(), ((RSyntaxNode) callLHS.getSyntaxLHS()).asRNode(), callLHS.getSyntaxSignature(), newArgs); + newSyntaxLHS = RCallSpecialNode.createCall(callLHS.getLazySourceSection(), builder.process(callLHS.getSyntaxLHS(), codeBuilderContext).asRNode(), callLHS.getSyntaxSignature(), newArgs); } return RCallSpecialNode.createCallInReplace(source, newSyntaxLHS.asRNode(), ArgumentsSignature.get(names), argNodes, 0, argNodes.length - 1).asRNode(); } @@ -346,7 +351,7 @@ abstract class ReplacementNode extends OperatorNode { GenericReplacementNode(SourceSection source, RSyntaxLookup operator, RNode target, RSyntaxElement lhs, RNode rhs, List<RSyntaxCall> calls, String targetVarName, boolean isSuper, int tempNamesStartIndex) { - super(source, operator, lhs, rhs, tempNamesStartIndex); + super(source, operator, lhs, RContext.getASTBuilder().process(rhs.asRSyntaxNode()).asRNode(), tempNamesStartIndex); /* * When there are more than two function calls in LHS, then we save some function calls * by saving the intermediate results into temporary variables and reusing them. 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 89c214040ceda244a24290cb59deb3440901c544..2c87c80b612bb7698508071dfd5e94d0d103dc68 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 @@ -515,7 +515,7 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo } if (restartStackSlot == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); - restartStackSlot = frame.getFrameDescriptor().findOrAddFrameSlot(RFrameSlot.RestartStack); + restartStackSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frame.getFrameDescriptor(), RFrameSlot.RestartStack, FrameSlotKind.Object); } return restartStackSlot; } @@ -527,7 +527,7 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo } if (handlerStackSlot == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); - handlerStackSlot = frame.getFrameDescriptor().findOrAddFrameSlot(RFrameSlot.HandlerStack); + handlerStackSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frame.getFrameDescriptor(), RFrameSlot.HandlerStack, FrameSlotKind.Object); } return handlerStackSlot; } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseHelperNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseHelperNode.java index f2407db5244cbdc42c9c845c5748c992d1e4b5a4..b635984a2cb511bea0b9716cacf2003f3c1470ea 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseHelperNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseHelperNode.java @@ -96,7 +96,7 @@ public class PromiseHelperNode extends RBaseNode { boolean deoptOne = false; for (FrameSlot slot : frame.getFrameDescriptor().getSlots().toArray(new FrameSlot[0])) { // We're only interested in RPromises - if (slot.getKind() != FrameSlotKind.Object) { + if (slot.getKind() != FrameSlotKind.Object || !(slot.getIdentifier() instanceof String)) { continue; } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallSpecialNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallSpecialNode.java index 5ba452ebe42f9b092bfb9e5b3439861e2691f379..e87a36945f15d09cdabd87d1c33cba3d6a790a3f 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallSpecialNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallSpecialNode.java @@ -24,7 +24,9 @@ package com.oracle.truffle.r.nodes.function; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeCost; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.profiles.ConditionProfile; @@ -165,7 +167,11 @@ public final class RCallSpecialNode extends RCallBaseNode implements RSyntaxNode */ private RCallSpecialNode callSpecialParent; - private RCallSpecialNode(SourceSection sourceSection, RNode functionNode, RFunction expectedFunction, RSyntaxNode[] arguments, ArgumentsSignature signature, RNode special) { + private final boolean inReplace; + private final int[] ignoredArguments; + + private RCallSpecialNode(SourceSection sourceSection, RNode functionNode, RFunction expectedFunction, RSyntaxNode[] arguments, ArgumentsSignature signature, RNode special, boolean inReplace, + int[] ignoredArguments) { this.sourceSection = sourceSection; this.expectedFunction = expectedFunction; this.special = special; @@ -173,6 +179,8 @@ public final class RCallSpecialNode extends RCallBaseNode implements RSyntaxNode this.arguments = arguments; this.signature = signature; this.visible = expectedFunction.getRBuiltin().getVisibility(); + this.inReplace = inReplace; + this.ignoredArguments = ignoredArguments; } /** @@ -264,7 +272,7 @@ public final class RCallSpecialNode extends RCallBaseNode implements RSyntaxNode RFunction expectedFunction = RContext.lookupBuiltin(name); RInternalError.guarantee(expectedFunction != null); - RCallSpecialNode callSpecial = new RCallSpecialNode(sourceSection, functionNode, expectedFunction, arguments, signature, special); + RCallSpecialNode callSpecial = new RCallSpecialNode(sourceSection, functionNode, expectedFunction, arguments, signature, special, inReplace, ignoredArguments); for (int i = 0; i < arguments.length; i++) { if (!inReplace || !contains(ignoredArguments, i)) { if (arguments[i] instanceof RCallSpecialNode) { @@ -316,6 +324,23 @@ public final class RCallSpecialNode extends RCallBaseNode implements RSyntaxNode } } + @TruffleBoundary + private static void log(String format, Object... args) { + System.out.println(String.format(format, args)); + } + + @Override + public Node deepCopy() { + assert !inReplace && callSpecialParent == null && ignoredArguments.length == 0; + RCallSpecialNode node = (RCallSpecialNode) RContext.getASTBuilder().process(this).asRNode(); + node.functionNode = node.insert(node.functionNode); + node.special = node.insert(node.special); + if (node.visibility != null) { + node.visibility = insert(node.visibility); + } + return node; + } + private RCallNode getRCallNode(RSyntaxNode[] newArguments) { return RCallNode.createCall(sourceSection, functionNode, signature, newArguments); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/visibility/GetVisibilityNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/visibility/GetVisibilityNode.java index 61a735b9465ee9af14bdd25090a6f20dcb104ca7..b59180138a7b98913bb93d00fb60b0292b016802 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/visibility/GetVisibilityNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/visibility/GetVisibilityNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -32,6 +32,7 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeCost; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.r.runtime.RInternalError; +import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; import com.oracle.truffle.r.runtime.env.frame.RFrameSlot; /** @@ -52,7 +53,7 @@ public final class GetVisibilityNode extends Node { public boolean execute(Frame frame) { if (frameSlot == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); - frameSlot = frame.getFrameDescriptor().findOrAddFrameSlot(RFrameSlot.Visibility, FrameSlotKind.Boolean); + frameSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frame.getFrameDescriptor(), RFrameSlot.Visibility, FrameSlotKind.Boolean); } try { return frame.getBoolean(frameSlot); diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/visibility/SetVisibilityNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/visibility/SetVisibilityNode.java index a259f498adbd9a3d08e22058bae9dd8d0ecf311c..f3fea2a553b20e487d2da962614b6c4b5738df9d 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/visibility/SetVisibilityNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/visibility/SetVisibilityNode.java @@ -37,6 +37,7 @@ import com.oracle.truffle.r.runtime.RArguments; import com.oracle.truffle.r.runtime.RCaller; import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.RVisibility; +import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; import com.oracle.truffle.r.runtime.env.frame.RFrameSlot; /** @@ -57,7 +58,7 @@ public final class SetVisibilityNode extends Node { private void ensureFrameSlot(Frame frame) { if (frameSlot == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); - frameSlot = frame.getFrameDescriptor().findOrAddFrameSlot(RFrameSlot.Visibility, FrameSlotKind.Boolean); + frameSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frame.getFrameDescriptor(), RFrameSlot.Visibility, FrameSlotKind.Boolean); } } @@ -103,7 +104,7 @@ public final class SetVisibilityNode extends Node { */ public static void executeAfterCallSlowPath(Frame frame, RCaller caller) { CompilerAsserts.neverPartOfCompilation(); - frame.setBoolean(frame.getFrameDescriptor().findOrAddFrameSlot(RFrameSlot.Visibility, FrameSlotKind.Boolean), caller.getVisibility()); + frame.setBoolean(FrameSlotChangeMonitor.findOrAddFrameSlot(frame.getFrameDescriptor(), RFrameSlot.Visibility, FrameSlotKind.Boolean), caller.getVisibility()); } /** @@ -111,6 +112,6 @@ public final class SetVisibilityNode extends Node { */ public static void executeSlowPath(Frame frame, boolean visibility) { CompilerAsserts.neverPartOfCompilation(); - frame.setBoolean(frame.getFrameDescriptor().findOrAddFrameSlot(RFrameSlot.Visibility, FrameSlotKind.Boolean), visibility); + frame.setBoolean(FrameSlotChangeMonitor.findOrAddFrameSlot(frame.getFrameDescriptor(), RFrameSlot.Visibility, FrameSlotKind.Boolean), visibility); } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java index 688fd6c0e25ac23450fb9e1f2c0ff8e80b1be249..ad8d5d62991725bda18dea55e9e646a9abd96384 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java @@ -54,6 +54,8 @@ public enum FastROptions { UseInternalGridGraphics("Whether the internal (Java) grid graphics implementation should be used", true), UseSpecials("Whether the fast-path special call nodes should be created for simple enough arguments.", true), ForceSources("Generate source sections for unserialized code", false), + SharedContexts("Whether all child contexts are to be shared contexts", true), + SearchPathForcePromises("Whether all promises for frames on shared path are forced in presence of shared contexts", false), // Promises optimizations EagerEval("If enabled, overrides all other EagerEval switches (see EagerEvalHelper)", false), diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java index d5cd4a077fcaa35a49f9d1a42c911cfdd00e0ee7..1f41cbf04d1065e45362ffe5744d36e0c2a93e65 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java @@ -293,12 +293,14 @@ public final class RArguments { frame.getArguments()[INDEX_IS_IRREGULAR] = isIrregularFrame; } - public static void setEnclosingFrame(Frame frame, MaterializedFrame newEnclosingFrame) { + public static void setEnclosingFrame(Frame frame, MaterializedFrame newEnclosingFrame, boolean updateDescriptors) { CompilerAsserts.neverPartOfCompilation(); Object[] arguments = frame.getArguments(); MaterializedFrame oldEnclosingFrame = (MaterializedFrame) arguments[INDEX_ENCLOSING_FRAME]; arguments[INDEX_ENCLOSING_FRAME] = newEnclosingFrame; - FrameSlotChangeMonitor.setEnclosingFrame(frame, newEnclosingFrame, oldEnclosingFrame); + if (updateDescriptors) { + FrameSlotChangeMonitor.setEnclosingFrame(frame, newEnclosingFrame, oldEnclosingFrame); + } } public static void initializeEnclosingFrame(Frame frame, MaterializedFrame newEnclosingFrame) { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java index 595a9c69e2ecea41e936c904830f9792b6251517..bc1cb211f156e4c0a9e698fd1cf9b945af97b894 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java @@ -28,8 +28,10 @@ import java.util.concurrent.Semaphore; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.object.DynamicObject; +import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor; import com.oracle.truffle.r.runtime.conn.RConnection; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.data.RAttributable; @@ -242,11 +244,15 @@ public class RChannel { // parent can be SerializedEnv or byte[] private final Object parent; private final DynamicObject attributes; + private final String name; + private final FrameDescriptor desc; - SerializedEnv(Bindings bindings, Object parent, DynamicObject attributes) { + SerializedEnv(Bindings bindings, Object parent, DynamicObject attributes, String name, FrameDescriptor desc) { this.bindings = bindings; this.parent = parent; this.attributes = attributes; + this.name = name; + this.desc = desc; } public String[] getNames() { @@ -264,6 +270,14 @@ public class RChannel { public DynamicObject getAttributes() { return attributes; } + + public String getName() { + return name; + } + + public FrameDescriptor getDesc() { + return desc; + } } protected static class SerializedPromise { @@ -294,12 +308,18 @@ public class RChannel { protected static class SerializedFunction { private final DynamicObject attributes; private final Object env; - private final RFunction serializedDef; + private final String name; + private final String packageName; + private final RBuiltinDescriptor builtinDesc; + private final RootCallTarget callTarget; - public SerializedFunction(DynamicObject attributes, Object env, RFunction serializedDef) { + public SerializedFunction(DynamicObject attributes, Object env, String name, String packageName, RBuiltinDescriptor builtinDesc, RootCallTarget callTarget) { this.attributes = attributes; this.env = env; - this.serializedDef = serializedDef; + this.name = name; + this.packageName = packageName; + this.builtinDesc = builtinDesc; + this.callTarget = callTarget; } public DynamicObject getAttributes() { @@ -310,8 +330,20 @@ public class RChannel { return env; } - public RFunction getSerializedDef() { - return serializedDef; + public String getName() { + return name; + } + + public RBuiltinDescriptor getRBuiltin() { + return builtinDesc; + } + + public RootCallTarget getTarget() { + return callTarget; + } + + public String getPackageName() { + return packageName; } } @@ -394,6 +426,7 @@ public class RChannel { } } + @TruffleBoundary private Object convertPrivateEnv(Object msg) throws IOException { int refInd = getRefIndex(msg); if (refInd != -1) { @@ -408,7 +441,7 @@ public class RChannel { } SerializedEnv.Bindings bindings = createShareable(env); - return new SerializedEnv(bindings, convertPrivateSlow(env.getParent()), attributes); + return new SerializedEnv(bindings, convertPrivateSlow(env.getParent()), attributes, env.getName(), env.getFrame().getFrameDescriptor()); } private SerializedPromise convertPrivatePromise(Object msg) throws IOException { @@ -425,7 +458,7 @@ public class RChannel { RFunction fn = (RFunction) msg; Object env = convertPrivate(REnvironment.frameToEnvironment(fn.getEnclosingFrame())); DynamicObject attributes = fn.getAttributes(); - return new SerializedFunction(attributes == null ? null : createShareableSlow(attributes, true), env, fn); + return new SerializedFunction(attributes == null ? null : createShareableSlow(attributes, true), env, fn.getName(), fn.getPackageName(), fn.getRBuiltin(), fn.getTarget()); } private Object convertPrivateAttributable(Object msg) throws IOException { @@ -603,7 +636,7 @@ public class RChannel { Object[] values = e.getValues(); String[] names = e.getNames(); assert values.length == names.length; - REnvironment.NewEnv env = RDataFactory.createNewEnv(null); + REnvironment.NewEnv env = RDataFactory.createNewEnv(e.getDesc(), e.getName()); addReadRef(env); int ind = 0; for (String n : names) { @@ -611,7 +644,7 @@ public class RChannel { env.safePut(n, newValue); } REnvironment parent = (REnvironment) unserializeObject(e.getParent()); - RArguments.initializeEnclosingFrame(env.getFrame(), parent.getFrame()); + RArguments.setEnclosingFrame(env.getFrame(), parent.getFrame(), false); DynamicObject attributes = e.getAttributes(); if (attributes != null) { env.initAttributes(attributes); @@ -636,13 +669,17 @@ public class RChannel { @TruffleBoundary private RFunction unserializeFunction(SerializedFunction f) throws IOException { - RFunction fun = f.getSerializedDef(); REnvironment env = (REnvironment) unserializeObject(f.getEnv()); MaterializedFrame enclosingFrame = env.getFrame(); - HasSignature root = (HasSignature) fun.getTarget().getRootNode(); - RootCallTarget target = root.duplicateWithNewFrameDescriptor(); - FrameSlotChangeMonitor.initializeEnclosingFrame(target.getRootNode().getFrameDescriptor(), enclosingFrame); - RFunction fn = RDataFactory.createFunction(fun.getName(), fun.getPackageName(), target, null, enclosingFrame); + RFunction fn; + if (FastROptions.SharedContexts.getBooleanValue()) { + fn = RDataFactory.createFunction(f.getName(), f.getPackageName(), f.getTarget(), f.getRBuiltin(), enclosingFrame); + } else { + HasSignature root = (HasSignature) f.getTarget().getRootNode(); + RootCallTarget target = root.duplicateWithNewFrameDescriptor(); + FrameSlotChangeMonitor.initializeEnclosingFrame(target.getRootNode().getFrameDescriptor(), enclosingFrame); + fn = RDataFactory.createFunction(f.getName(), f.getPackageName(), target, null, enclosingFrame); + } DynamicObject attributes = f.getAttributes(); if (attributes != null) { assert fn.getAttributes() == null; diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java index f1086ac6da4bef8bc6af79815e8ea6a5b6e346a7..bfa0177952d504c0fac8610c4a53f56d1bb03489 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java @@ -264,7 +264,13 @@ public final class RError extends RuntimeException { @TruffleBoundary public static void performanceWarning(String string) { if (FastROptions.PerformanceWarnings.getBooleanValue()) { - warning(RError.SHOW_CALLER2, Message.PERFORMANCE, string); + System.out.println("Performance warning: " + string); + StackTraceElement[] trace = new RuntimeException().getStackTrace(); + for (int i = 1; i < trace.length && i < 8; i++) { + StackTraceElement element = trace[i]; + System.out.println(" at " + element.getClassName() + "." + element.getMethodName() + "(" + element.getFileName() + ":" + element.getLineNumber() + ")"); + } + // warning(RError.SHOW_CALLER2, Message.PERFORMANCE, string); } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java index 412cd0b07ee1e7648e6317511761a916755deab7..6a5b182e76243b8c5d1ea97ee819b20d8cf85b68 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java @@ -597,7 +597,10 @@ public class RErrorHandling { */ ContextStateImpl errorHandlingState = getRErrorHandlingState(); RFunction f = errorHandlingState.getDotSignalSimpleWarning(); - RContext.getRRuntimeASTAccess().callback(f, new Object[]{warningMessage, call}); + if (f != null) { + RContext.getRRuntimeASTAccess().callback(f, new Object[]{warningMessage, call}); + } + // otherwise the subsystem is not initialized yet - no warning } private static void warningcallDfltWithCall(Object call, Message msg, Object... args) { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java index 8104d4e5a0a9d9404b9440f1e026900ed2e3d35f..0f5b22a965c0365c8c3b3747b4f1db2ea7354388 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java @@ -193,6 +193,13 @@ public class RRuntime { } } + /** + * Create a {@link MaterializedFrame} for functions shared between different contexts. + */ + public static MaterializedFrame createNewFrame(FrameDescriptor frameDescriptor) { + return Truffle.getRuntime().createMaterializedFrame(RArguments.createUnitialized(), frameDescriptor); + } + /** * Create an {@link VirtualFrame} for a non-function environment, e.g., a package frame or the * global environment. diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSource.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSource.java index 5f6462db6d8b31943883d61dea6676c84a973d53..41ea6dacdaef4fa6b2282d0bc842ecc33a2f4a50 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSource.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSource.java @@ -107,6 +107,13 @@ public class RSource { return fromTextInternal(text, description, RRuntime.R_APP_MIME); } + /** + * Create an {@code internal} source from {@code text} and {@code description}. + */ + public static Source fromTextInternalInvisible(String text, Internal description) { + return fromTextInternalInvisible(text, description, RRuntime.R_APP_MIME); + } + /** * Create an {@code internal} source from {@code text} and {@code description} of given * {@code mimeType}. @@ -116,6 +123,15 @@ public class RSource { return Source.newBuilder(text).name(description.string).mimeType(mimeType).internal().interactive().build(); } + /** + * Create an {@code internal} source from {@code text} and {@code description} of given + * {@code mimeType}. + */ + + public static Source fromTextInternalInvisible(String text, Internal description, String mimeType) { + return Source.newBuilder(text).name(description.string).mimeType(mimeType).internal().build(); + } + /** * Create an {@code internal} source for a deparsed package from {@code text} and * {@code packageName}. diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java index d533301e924c73398f388816cdb280eda7b6e5ce..a5ac484ad14ccb207471af5f54f7a248e96fd720 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java @@ -60,6 +60,7 @@ import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RPairList; import com.oracle.truffle.r.runtime.data.RStringVector; import com.oracle.truffle.r.runtime.data.model.RAbstractContainer; +import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor.MultiSlotData; import com.oracle.truffle.r.runtime.ffi.BaseRFFI; import com.oracle.truffle.r.runtime.ffi.RFFIFactory; import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; @@ -362,6 +363,7 @@ public final class Utils { */ @TruffleBoundary public static Frame getStackFrame(FrameAccess fa, RCaller target) { + RError.performanceWarning("slow frame access - getStackFrame1"); assert target != null; return Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor<Frame>() { boolean first = true; @@ -388,6 +390,7 @@ public final class Utils { */ @TruffleBoundary public static Frame getStackFrame(FrameAccess fa, int depth) { + RError.performanceWarning("slow frame access - getStackFrame2"); return Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor<Frame>() { boolean first = true; @@ -418,6 +421,7 @@ public final class Utils { */ @TruffleBoundary public static <T> T iterateRFrames(FrameAccess fa, Function<Frame, T> func) { + RError.performanceWarning("slow frame access - iterateRFrames"); return Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor<T>() { boolean first = true; @@ -463,6 +467,7 @@ public final class Utils { */ @TruffleBoundary public static Frame getActualCurrentFrame() { + RError.performanceWarning("slow frame access - getActualCurrentFrame"); FrameInstance frameInstance = Truffle.getRuntime().getCurrentFrame(); if (frameInstance == null) { // Might be the case during initialization, when envs are prepared before the actual @@ -528,6 +533,7 @@ public final class Utils { */ @TruffleBoundary public static Object createTraceback(int skip) { + RError.performanceWarning("slow frame access - createTraceback"); FrameInstance current = Truffle.getRuntime().getCurrentFrame(); if (current != null) { TracebackVisitor fiv = new TracebackVisitor(skip); @@ -543,6 +549,7 @@ public final class Utils { */ @TruffleBoundary public static String createStackTrace(boolean printFrameSlots) { + RError.performanceWarning("slow frame access - createStackTrace"); FrameInstance current = Truffle.getRuntime().getCurrentFrame(); if (current == null) { return "no R stack trace available\n"; @@ -594,12 +601,9 @@ public final class Utils { FrameDescriptor frameDescriptor = unwrapped.getFrameDescriptor(); for (FrameSlot s : frameDescriptor.getSlots()) { str.append("\n ").append(s.getIdentifier()).append(" = "); - Object value; - try { - value = unwrapped.getValue(s); - } catch (Throwable t) { - str.append("<exception ").append(t.getClass().getSimpleName()).append(" while acquiring slot ").append(s.getIdentifier()).append(">"); - continue; + Object value = unwrapped.getValue(s); + if (value instanceof MultiSlotData) { + value = ((MultiSlotData) value).get(RContext.getInstance().getMultiSlotInd()); } try { if (value instanceof RAbstractContainer && ((RAbstractContainer) value).getLength() > 32) { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ContextInfo.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ContextInfo.java index c1d39f9b1e21dc4bb76377f176aa8afb31bb60a6..04862083a6b5bc24b883ad9f05cc01b70734ae92 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ContextInfo.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ContextInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -28,6 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger; import com.oracle.truffle.api.vm.PolyglotEngine; import com.oracle.truffle.r.runtime.RCmdOptions; import com.oracle.truffle.r.runtime.RCmdOptions.Client; +import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.RStartParams; import com.oracle.truffle.r.runtime.context.RContext.ContextKind; @@ -41,6 +42,7 @@ public final class ContextInfo { static final String CONFIG_KEY = "fastrContextInfo"; private static final AtomicInteger contextInfoIds = new AtomicInteger(); + private static final AtomicInteger multiSlotInds = new AtomicInteger(-1); private final RStartParams startParams; private final String[] env; @@ -54,18 +56,32 @@ public final class ContextInfo { private final RContext parent; private final ConsoleHandler consoleHandler; private final int id; + private final int multiSlotInd; private PolyglotEngine vm; - private ContextInfo(RStartParams startParams, String[] env, ContextKind kind, RContext parent, ConsoleHandler consoleHandler, TimeZone systemTimeZone, int id) { + private ContextInfo(RStartParams startParams, String[] env, ContextKind kind, RContext parent, ConsoleHandler consoleHandler, TimeZone systemTimeZone, int id, int multiSlotInd) { this.startParams = startParams; this.env = env; this.kind = kind; this.parent = parent; this.consoleHandler = consoleHandler; this.systemTimeZone = systemTimeZone; + this.multiSlotInd = multiSlotInd; this.id = id; } + /** + * Correctness of this method relies on the fact that parallel contexts are started only after + * all of them (and their info) is created (in FastRContext). + */ + public static int contextNum() { + return multiSlotInds.get() + 1; + } + + public static void resetMultiSlotIndexGenerator() { + multiSlotInds.set(0); // to account for primordial context + } + public PolyglotEngine createVM() { PolyglotEngine newVM = PolyglotEngine.newBuilder().config("application/x-r", CONFIG_KEY, this).build(); this.vm = newVM; @@ -91,7 +107,17 @@ public final class ContextInfo { */ public static ContextInfo create(RStartParams startParams, String[] env, ContextKind kind, RContext parent, ConsoleHandler consoleHandler, TimeZone systemTimeZone) { int id = contextInfoIds.incrementAndGet(); - return new ContextInfo(startParams, env, kind, parent, consoleHandler, systemTimeZone, id); + int multiSlotInd = multiSlotInds.get(); + if (kind == ContextKind.SHARE_ALL || kind == ContextKind.SHARE_NOTHING) { + multiSlotInd = multiSlotInds.incrementAndGet(); + } + // no increment for SHARE_PARENT_RW as it accesses the same data as its parent whose + // execution is suspended + if (kind == ContextKind.SHARE_PARENT_RO) { + throw RInternalError.shouldNotReachHere(); + } + assert kind != ContextKind.SHARE_PARENT_RW || (kind == ContextKind.SHARE_PARENT_RW && parent.getKind() == ContextKind.SHARE_NOTHING && parent.getMultiSlotInd() == 0); + return new ContextInfo(startParams, env, kind, parent, consoleHandler, systemTimeZone, id, kind == ContextKind.SHARE_PARENT_RW ? 0 : multiSlotInd); } public static ContextInfo create(RStartParams startParams, String[] env, ContextKind kind, RContext parent, ConsoleHandler consoleHandler) { @@ -141,6 +167,10 @@ public final class ContextInfo { return id; } + public int getMultiSlotInd() { + return multiSlotInd; + } + public PolyglotEngine getVM() { return vm; } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java index 64339a2fc5dac568b8b2845d39e1f517da5c5210..b4a193b8fa0aa2726c82474ad2f867bbcd0ee0f3 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -30,6 +30,7 @@ import java.util.Map; import java.util.TimeZone; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Semaphore; import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; @@ -134,7 +135,12 @@ public final class RContext extends ExecutionContext { * shallow copy of the environments associated with the default packages of the parent * context at the time the context is created. */ - SHARE_PARENT_RO; + SHARE_PARENT_RO, + + /** + * Shares all environments on the search path. + */ + SHARE_ALL; public static final ContextKind[] VALUES = values(); } @@ -217,6 +223,7 @@ public final class RContext extends ExecutionContext { private final Source source; private final ContextInfo info; private RList evalResult; + private Semaphore init = new Semaphore(0); public static final Map<Integer, Thread> threads = new ConcurrentHashMap<>(); @@ -235,6 +242,7 @@ public final class RContext extends ExecutionContext { } catch (Throwable t) { throw new RInternalError(t, "error while initializing eval thread"); } + init.release(); try { evalResult = run(vm, info, source); } finally { @@ -243,6 +251,19 @@ public final class RContext extends ExecutionContext { } } + /* + * Parent context uses this method to wait for initialization of the child to complete to + * prevent potential updates to runtime's meta data from interfering with program's + * execution. + */ + public void waitForInit() { + try { + init.acquire(); + } catch (InterruptedException x) { + throw new RInternalError(x, "error waiting to initialize eval thread"); + } + } + /** * Convenience method for {@code .fastr.context.eval} in same thread. */ @@ -387,6 +408,10 @@ public final class RContext extends ExecutionContext { private static final Assumption singleContextAssumption = Truffle.getRuntime().createAssumption("single RContext"); @CompilationFinal private static RContext singleContext; + // need an additional flag as we don't want multi-slot processing to start until context + // initialization is fully complete - singleContext flag is not good enough for that + private static final Assumption isSingleContextAssumption = Truffle.getRuntime().createAssumption("is single RContext"); + private final Env env; private final HashMap<String, TruffleObject> exportedSymbols = new HashMap<>(); private final boolean initial; @@ -465,7 +490,7 @@ public final class RContext extends ExecutionContext { this.stateROptions = ROptions.ContextStateImpl.newContextState(stateREnvVars); this.stateRProfile = RProfile.newContextState(stateREnvVars); this.stateStdConnections = StdConnections.ContextStateImpl.newContextState(); - this.stateREnvironment = REnvironment.ContextStateImpl.newContextState(); + this.stateREnvironment = REnvironment.ContextStateImpl.newContextState(this); this.stateRErrorHandling = RErrorHandling.ContextStateImpl.newContextState(); this.stateRConnection = ConnectionSupport.ContextStateImpl.newContextState(); this.stateRNG = RRNG.ContextStateImpl.newContextState(); @@ -636,6 +661,14 @@ public final class RContext extends ExecutionContext { return info.getKind(); } + public int getId() { + return info.getId(); + } + + public int getMultiSlotInd() { + return info.getMultiSlotInd(); + } + @TruffleBoundary public static RContext getThreadLocalInstance() { return threadLocalContext.get(); @@ -654,6 +687,14 @@ public final class RContext extends ExecutionContext { return result; } + public static boolean isSingle() { + return isSingleContextAssumption.isValid(); + } + + public static void markNonSingle() { + isSingleContextAssumption.invalidate(); + } + public static RContext getInstance() { RContext context = singleContext; if (singleContextAssumption.isValid() && context != null) { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java index ee8b17a277e84e17a58aac2f08d58f69f0aa3ea3..2192e8f0598c9b472336e793a2cefe92a5593ef8 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java @@ -31,6 +31,7 @@ import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.utilities.CyclicAssumption; import com.oracle.truffle.r.runtime.FastROptions; @@ -499,6 +500,11 @@ public final class RDataFactory { return traceDataCreated(new REnvironment.NewEnv(RRuntime.createNonFunctionFrame("<internal-env-" + environmentCount.incrementAndGet() + ">"), REnvironment.UNNAMED)); } + @TruffleBoundary + public static REnvironment.NewEnv createNewEnv(FrameDescriptor desc, String name) { + return traceDataCreated(new REnvironment.NewEnv(RRuntime.createNewFrame(desc), name)); + } + @TruffleBoundary public static REnvironment.NewEnv createNewEnv(String name) { return traceDataCreated(new REnvironment.NewEnv(RRuntime.createNonFunctionFrame("<new-env-" + environmentCount.incrementAndGet() + ">"), name)); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java index 56100651dd85980d21657b88e045987d2e07fa41..b8ba2fe1c75518f24ba5f714d69ba7077ecbbd99 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java @@ -29,6 +29,7 @@ import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.Frame; +import com.oracle.truffle.api.frame.FrameSlot; import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.profiles.ValueProfile; import com.oracle.truffle.r.runtime.AnonymousFrameVariable; @@ -41,6 +42,7 @@ import com.oracle.truffle.r.runtime.RType; import com.oracle.truffle.r.runtime.Utils; import com.oracle.truffle.r.runtime.VirtualEvalFrame; import com.oracle.truffle.r.runtime.context.RContext; +import com.oracle.truffle.r.runtime.context.RContext.ContextKind; import com.oracle.truffle.r.runtime.data.RAttributeStorage; import com.oracle.truffle.r.runtime.data.RAttributesLayout; import com.oracle.truffle.r.runtime.data.RDataFactory; @@ -48,6 +50,7 @@ import com.oracle.truffle.r.runtime.data.RFunction; import com.oracle.truffle.r.runtime.data.RList; import com.oracle.truffle.r.runtime.data.RPromise; import com.oracle.truffle.r.runtime.data.RStringVector; +import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; import com.oracle.truffle.r.runtime.env.frame.NSBaseMaterializedFrame; import com.oracle.truffle.r.runtime.env.frame.REnvEmptyFrameAccess; import com.oracle.truffle.r.runtime.env.frame.REnvFrameAccess; @@ -152,8 +155,15 @@ public abstract class REnvironment extends RAttributeStorage { beforeDestroyContext(context, this); } - public static ContextStateImpl newContextState() { - return new ContextStateImpl(RRuntime.createNonFunctionFrame("global")); + public static ContextStateImpl newContextState(RContext context) { + MaterializedFrame newGlobalFrame; + if (context.getKind() == ContextKind.SHARE_ALL) { + ContextStateImpl parentState = context.getParent().stateREnvironment; + newGlobalFrame = parentState.getGlobalFrame(); + } else { + newGlobalFrame = RRuntime.createNonFunctionFrame("global"); + } + return new ContextStateImpl(newGlobalFrame); } public void initialize(Base newBaseEnv, REnvironment newNamespaceRegistry, SearchPath newSearchPath) { @@ -329,6 +339,7 @@ public abstract class REnvironment extends RAttributeStorage { /* We make shallow copies of all the default package environments in the parent */ ContextStateImpl parentState = context.getParent().stateREnvironment; SearchPath parentSearchPath = parentState.getSearchPath(); + assert parentSearchPath.size() > 1; // clone all the environments below global from the parent REnvironment e = parentSearchPath.get(1).cloneEnv(globalFrame); // create the new Global with clone top as parent @@ -352,6 +363,15 @@ public abstract class REnvironment extends RAttributeStorage { break; } + case SHARE_ALL: { + ContextStateImpl parentState = context.getParent().stateREnvironment; + // TODO: may be worthwhile to assert for all environments on the search path there + // is 1:1 descriptor:frame mapping (as they are all meant to be shared in we rely on + // the single descriptor information to identify accesses to shared frames) + contextState.initialize(parentState.getBaseEnv(), parentState.getNamespaceRegistry(), parentState.getSearchPath()); + break; + } + case SHARE_NOTHING: { // SHARE_NOTHING: baseInitialize takes care of everything break; @@ -747,7 +767,7 @@ public abstract class REnvironment extends RAttributeStorage { */ public void setParent(REnvironment env) { if (getParent() != env) { - RArguments.setEnclosingFrame(getFrame(), env.getFrame()); + RArguments.setEnclosingFrame(getFrame(), env.getFrame(), true); } } @@ -890,6 +910,32 @@ public abstract class REnvironment extends RAttributeStorage { return getPrintName(); } + public static void convertSearchpathToMultiSlot() { + CompilerAsserts.neverPartOfCompilation(); + RContext.markNonSingle(); + ContextStateImpl parentState = RContext.getInstance().stateREnvironment; + SearchPath searchPath = parentState.getSearchPath(); + assert searchPath.size() > 0 && searchPath.get(0).getSearchName() == Global.SEARCHNAME; + // for global space don't replicate entries as all contexts should see their own values + FrameSlotChangeMonitor.handleAllMultiSlots(searchPath.get(0).getFrame(), false); + for (int i = 1; i < searchPath.size(); i++) { + FrameSlotChangeMonitor.handleAllMultiSlots(searchPath.get(i).getFrame(), true); + } + REnvironment namespaces = parentState.namespaceRegistry; + Frame namespacesFrame = namespaces.getFrame(); + // make a copy avoid potential updates to the array iterated over + FrameSlot[] slots = new FrameSlot[namespacesFrame.getFrameDescriptor().getSlots().size()]; + slots = namespacesFrame.getFrameDescriptor().getSlots().toArray(slots); + for (int i = 0; i < slots.length; i++) { + REnvironment namespaceEnv = (REnvironment) namespacesFrame.getValue(slots[i]); + if (namespaceEnv != Base.baseNamespaceEnv()) { + // base namespace frame redirects all accesses to base frame and this would + // result in processing the slots twice + FrameSlotChangeMonitor.handleAllMultiSlots(namespaceEnv.getFrame(), true); + } + } + } + private static final class BaseNamespace extends REnvironment { private BaseNamespace(String name, MaterializedFrame frame) { super(name, frame); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/FrameSlotChangeMonitor.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/FrameSlotChangeMonitor.java index 323f4d154d2e181b0be9d8d36bd597d5e602221e..cdb9f13b1c530c131cd8606ea5a29f683be5b4d2 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/FrameSlotChangeMonitor.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/FrameSlotChangeMonitor.java @@ -35,6 +35,7 @@ import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.FrameDescriptor; @@ -44,9 +45,12 @@ import com.oracle.truffle.api.frame.FrameSlotTypeException; import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.nodes.InvalidAssumptionException; import com.oracle.truffle.api.profiles.BranchProfile; +import com.oracle.truffle.r.runtime.FastROptions; import com.oracle.truffle.r.runtime.RArguments; import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.StableValue; +import com.oracle.truffle.r.runtime.context.ContextInfo; +import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.data.RPromise; /** @@ -147,7 +151,7 @@ public final class FrameSlotChangeMonitor { public Object getValue() { // fast path execution should use getFrame / getSlot CompilerAsserts.neverPartOfCompilation("FrameAndSlotLookupResult.getValue() should not be used in fast path execution"); - return frame.getValue(slot); + return FrameSlotChangeMonitor.getValue(slot, frame); } public MaterializedFrame getFrame() throws InvalidAssumptionException { @@ -242,6 +246,7 @@ public final class FrameSlotChangeMonitor { } else { FrameDescriptorMetaData currentMetaData = getMetaData(current); if (currentMetaData.singletonFrame == null) { + // no stable value and no singleton frame return null; } else { assert currentMetaData.singletonFrame.get() != null; @@ -312,7 +317,8 @@ public final class FrameSlotChangeMonitor { return frame == null ? null : frame instanceof NSBaseMaterializedFrame ? ((NSBaseMaterializedFrame) frame).getMarkerFrameDescriptor() : frame.getFrameDescriptor(); } - private static FrameDescriptorMetaData getMetaData(FrameDescriptor descriptor) { + private static synchronized FrameDescriptorMetaData getMetaData(FrameDescriptor descriptor) { + CompilerAsserts.neverPartOfCompilation(); FrameDescriptorMetaData result = frameDescriptors.get(descriptor); assert result != null : "null metadata for " + descriptor; return result; @@ -452,6 +458,24 @@ public final class FrameSlotChangeMonitor { // System.out.println(String.format(format, args)); } + public static final class MultiSlotData { + + private final Object[] data = new Object[ContextInfo.contextNum()]; + + public Object get(int ind) { + return data[ind]; + } + + public void set(int ind, Object val) { + data[ind] = val; + } + + public void setAll(Object val) { + Arrays.fill(data, val); + } + + } + private static final class FrameSlotInfoImpl { /** * This is meant to monitor updates performed on {@link FrameSlot}. Each {@link FrameSlot} @@ -464,11 +488,14 @@ public final class FrameSlotChangeMonitor { * comes into play.<br/> */ private final Assumption nonLocalModifiedAssumption = Truffle.getRuntime().createAssumption(); + private final Assumption noMultiSlot = Truffle.getRuntime().createAssumption(); - @CompilationFinal private StableValue<Object> stableValue; + @CompilationFinal private volatile StableValue<Object> stableValue; private int invalidationCount; + private final boolean possibleMultiSlot; - FrameSlotInfoImpl(boolean isSingletonFrame, boolean isGlobalEnv, Object identifier) { + FrameSlotInfoImpl(boolean isSingletonFrame, boolean isGlobalEnv, Object identifier, boolean isNewEnv) { + this.possibleMultiSlot = isSingletonFrame && !isNewEnv; if (isSingletonFrame) { stableValue = new StableValue<>(null, identifier.toString()); invalidationCount = isGlobalEnv ? MAX_GLOBAL_ENV_INVALIDATION_COUNT : MAX_INVALIDATION_COUNT; @@ -481,29 +508,39 @@ public final class FrameSlotChangeMonitor { return stableValue != null; } + public boolean possibleMultiSlot() { + return possibleMultiSlot; + } + /* * Special cases for primitive types to force value (instead of identity) comparison. */ - public void setValue(byte value, FrameSlot slot) { + private void setValue(boolean value, FrameSlot slot) { + if (stableValue != null && (!(stableValue.getValue() instanceof Boolean) || ((boolean) stableValue.getValue()) != value)) { + invalidateStableValue(value, slot); + } + } + + private void setValue(byte value, FrameSlot slot) { if (stableValue != null && (!(stableValue.getValue() instanceof Byte) || ((byte) stableValue.getValue()) != value)) { invalidateStableValue(value, slot); } } - public void setValue(int value, FrameSlot slot) { + private void setValue(int value, FrameSlot slot) { if (stableValue != null && (!(stableValue.getValue() instanceof Integer) || ((int) stableValue.getValue()) != value)) { invalidateStableValue(value, slot); } } - public void setValue(double value, FrameSlot slot) { + private void setValue(double value, FrameSlot slot) { if (stableValue != null && (!(stableValue.getValue() instanceof Double) || ((double) stableValue.getValue()) != value)) { invalidateStableValue(value, slot); } } - public void setValue(Object value, FrameSlot slot) { + private void setValue(Object value, FrameSlot slot) { if (stableValue != null && stableValue.getValue() != value) { invalidateStableValue(value, slot); } @@ -525,6 +562,110 @@ public final class FrameSlotChangeMonitor { public StableValue<Object> getStableValue() { return stableValue; } + + private static void setNewMultiValue(Frame frame, FrameSlot slot, MultiSlotData data, Object newValue) { + int ind = RContext.getInstance().getMultiSlotInd(); + data.set(ind, newValue); + frame.setObject(slot, data); + } + + private static boolean evalAndSetPromise(Frame frame, FrameSlot slot, FrameSlotInfoImpl info) { + Object prevValue = info.stableValue.getValue(); + if (prevValue instanceof RPromise) { + prevValue = RContext.getRRuntimeASTAccess().forcePromise("searchPathPromiseForce", prevValue); + if (prevValue instanceof Boolean) { + frame.setBoolean(slot, (boolean) prevValue); + info.setValue((boolean) prevValue, slot); + } else if (prevValue instanceof Byte) { + frame.setByte(slot, (byte) prevValue); + info.setValue((byte) prevValue, slot); + } else if (prevValue instanceof Integer) { + frame.setInt(slot, (int) prevValue); + info.setValue((int) prevValue, slot); + } else if (prevValue instanceof Double) { + frame.setDouble(slot, (double) prevValue); + info.setValue((double) prevValue, slot); + } else { + frame.setObject(slot, prevValue); + info.setValue(prevValue, slot); + } + return true; + } else { + return false; + } + } + + public static void handleSearchPathMultiSlot(Frame frame, FrameSlot slot, boolean replicate) { + CompilerAsserts.neverPartOfCompilation(); + while (true) { + FrameSlotInfoImpl info = (FrameSlotInfoImpl) slot.getInfo(); + if (info.stableValue == null || !replicate) { + // create a multi slot for slots whose stableValue is null but also for all + // slots of + // the global frame (which are marked as !replicate) + info.stableValue = null; + info.nonLocalModifiedAssumption.invalidate(); + info.noMultiSlot.invalidate(); + info.invalidationCount = 0; + MultiSlotData data = new MultiSlotData(); + Object prevValue = frame.getValue(slot); + // TODO: do we have to worry that prevValue can be invalid? + if (prevValue instanceof MultiSlotData) { + // this handles the case when we create share contexts for the second time - + // existing multi slots are an artifact of the previous executions and must + // be + // discarded + // TOOD: consider re-using multi slots but since we don't expect many of + // them, + // perhaps it's too much work for too little gain + prevValue = ((MultiSlotData) prevValue).get(0); + } else if (FastROptions.SearchPathForcePromises.getBooleanValue()) { + prevValue = RContext.getRRuntimeASTAccess().forcePromise("searchPathPromiseForce", prevValue); + } + if (replicate) { + data.setAll(prevValue); + } else { + data.set(0, prevValue); + } + frame.setObject(slot, data); + break; + } else { + if (!FastROptions.SearchPathForcePromises.getBooleanValue() || !evalAndSetPromise(frame, slot, info)) { + break; + } + // otherwise stable value may get nullified and slot turned into multi slot + } + + } + } + + @TruffleBoundary + public synchronized void setMultiSlot(Frame frame, FrameSlot slot, Object newValue) { + // TODO: perhaps putting the whole thing behind the Truffle boundary an overkill, but on + // the other hand it shouldn't happen often and not on the fast path + MultiSlotData data; + if (stableValue == null) { + // already a multi slot - should be visible to all threads + data = (MultiSlotData) frame.getValue(slot); + int ind = RContext.getInstance().getMultiSlotInd(); + data.set(ind, newValue); + } else { + nonLocalModifiedAssumption.invalidate(); + invalidationCount = 0; + // TODO: is it necessary since we nullify stable value? + stableValue.getAssumption().invalidate(); + noMultiSlot.invalidate(); + data = new MultiSlotData(); + Object prevValue = frame.getValue(slot); + // value was stable so this slot is set by primordial context + data.set(0, prevValue); + setNewMultiValue(frame, slot, data, newValue); + // this should create happens-before with stable value reads during lookup, thus + // making preceding update to the actual frame OK to read without additional + // synchronization + stableValue = null; + } + } } /** @@ -540,22 +681,32 @@ public final class FrameSlotChangeMonitor { Object info = slot.getInfo(); if (!(info instanceof FrameSlotInfoImpl)) { CompilerDirectives.transferToInterpreter(); - throw RInternalError.shouldNotReachHere("Each FrameSlot should hold a FrameSlotInfo in its info field!"); + throw RInternalError.shouldNotReachHere("Each FrameSlot should hold a FrameSlotInfo in its info field! " + slot.getIdentifier().getClass() + " " + slot.getIdentifier()); } return (FrameSlotInfoImpl) info; } // methods for creating new frame slots - public static synchronized FrameSlot findOrAddFrameSlot(FrameDescriptor fd, Object identifier, FrameSlotKind initialKind) { + public static FrameSlot findOrAddFrameSlot(FrameDescriptor fd, String identifier, FrameSlotKind initialKind) { + return findOrAddFrameSlot(fd, (Object) identifier, initialKind); + } + + public static FrameSlot findOrAddFrameSlot(FrameDescriptor fd, RFrameSlot identifier, FrameSlotKind initialKind) { + return findOrAddFrameSlot(fd, (Object) identifier, initialKind); + } + + private static synchronized FrameSlot findOrAddFrameSlot(FrameDescriptor fd, Object identifier, FrameSlotKind initialKind) { CompilerAsserts.neverPartOfCompilation(); + assert identifier instanceof String || identifier instanceof RFrameSlot; FrameSlot frameSlot = fd.findFrameSlot(identifier); if (frameSlot != null) { return frameSlot; } else { FrameDescriptorMetaData metaData = getMetaData(fd); invalidateNames(metaData, Arrays.asList(identifier)); - return fd.addFrameSlot(identifier, new FrameSlotInfoImpl(metaData.singletonFrame != null, "global".equals(metaData.name), identifier), initialKind); + return fd.addFrameSlot(identifier, new FrameSlotInfoImpl(metaData.singletonFrame != null, "global".equals(metaData.name), identifier, metaData.name.startsWith("<new-env-")), + initialKind); } } @@ -585,44 +736,124 @@ public final class FrameSlotChangeMonitor { } } + public static void setBooleanAndInvalidate(Frame frame, FrameSlot frameSlot, boolean newValue, boolean isNonLocal, BranchProfile invalidateProfile) { + FrameSlotInfoImpl info = getFrameSlotInfo(frameSlot); + if (FastROptions.SharedContexts.getBooleanValue() && info.possibleMultiSlot() && !RContext.isSingle()) { + info.setMultiSlot(frame, frameSlot, newValue); + } else { + frame.setBoolean(frameSlot, newValue); + if (info.needsInvalidation()) { + info.setValue(newValue, frameSlot); + } + checkAndInvalidate(frame, frameSlot, isNonLocal, invalidateProfile); + } + } + + public static void setBoolean(Frame frame, FrameSlot frameSlot, boolean newValue) { + if (FastROptions.SharedContexts.getBooleanValue() && !RContext.isSingle()) { + FrameSlotInfoImpl info = getFrameSlotInfo(frameSlot); + if (info.possibleMultiSlot()) { + info.setMultiSlot(frame, frameSlot, newValue); + return; + } + } + frame.setBoolean(frameSlot, newValue); + } + public static void setByteAndInvalidate(Frame frame, FrameSlot frameSlot, byte newValue, boolean isNonLocal, BranchProfile invalidateProfile) { - frame.setByte(frameSlot, newValue); FrameSlotInfoImpl info = getFrameSlotInfo(frameSlot); - if (info.needsInvalidation()) { - info.setValue(newValue, frameSlot); + if (FastROptions.SharedContexts.getBooleanValue() && info.possibleMultiSlot() && !RContext.isSingle()) { + info.setMultiSlot(frame, frameSlot, newValue); + } else { + frame.setByte(frameSlot, newValue); + if (info.needsInvalidation()) { + info.setValue(newValue, frameSlot); + } + checkAndInvalidate(frame, frameSlot, isNonLocal, invalidateProfile); } - checkAndInvalidate(frame, frameSlot, isNonLocal, invalidateProfile); + } + + public static void setByte(Frame frame, FrameSlot frameSlot, byte newValue) { + if (FastROptions.SharedContexts.getBooleanValue() && !RContext.isSingle()) { + FrameSlotInfoImpl info = getFrameSlotInfo(frameSlot); + if (info.possibleMultiSlot()) { + info.setMultiSlot(frame, frameSlot, newValue); + return; + } + } + frame.setByte(frameSlot, newValue); } public static void setIntAndInvalidate(Frame frame, FrameSlot frameSlot, int newValue, boolean isNonLocal, BranchProfile invalidateProfile) { - frame.setInt(frameSlot, newValue); FrameSlotInfoImpl info = getFrameSlotInfo(frameSlot); - if (info.needsInvalidation()) { - info.setValue(newValue, frameSlot); + if (FastROptions.SharedContexts.getBooleanValue() && info.possibleMultiSlot() && !RContext.isSingle()) { + info.setMultiSlot(frame, frameSlot, newValue); + } else { + frame.setInt(frameSlot, newValue); + if (info.needsInvalidation()) { + info.setValue(newValue, frameSlot); + } + checkAndInvalidate(frame, frameSlot, isNonLocal, invalidateProfile); } - checkAndInvalidate(frame, frameSlot, isNonLocal, invalidateProfile); + } + + public static void setInt(Frame frame, FrameSlot frameSlot, int newValue) { + if (FastROptions.SharedContexts.getBooleanValue() && !RContext.isSingle()) { + FrameSlotInfoImpl info = getFrameSlotInfo(frameSlot); + if (info.possibleMultiSlot()) { + info.setMultiSlot(frame, frameSlot, newValue); + return; + } + } + frame.setInt(frameSlot, newValue); } public static void setDoubleAndInvalidate(Frame frame, FrameSlot frameSlot, double newValue, boolean isNonLocal, BranchProfile invalidateProfile) { - frame.setDouble(frameSlot, newValue); FrameSlotInfoImpl info = getFrameSlotInfo(frameSlot); - if (info.needsInvalidation()) { - info.setValue(newValue, frameSlot); + if (FastROptions.SharedContexts.getBooleanValue() && info.possibleMultiSlot() && !RContext.isSingle()) { + info.setMultiSlot(frame, frameSlot, newValue); + } else { + frame.setDouble(frameSlot, newValue); + if (info.needsInvalidation()) { + info.setValue(newValue, frameSlot); + } + checkAndInvalidate(frame, frameSlot, isNonLocal, invalidateProfile); } - checkAndInvalidate(frame, frameSlot, isNonLocal, invalidateProfile); + } + + public static void setDouble(Frame frame, FrameSlot frameSlot, double newValue) { + if (FastROptions.SharedContexts.getBooleanValue() && !RContext.isSingle()) { + FrameSlotInfoImpl info = getFrameSlotInfo(frameSlot); + if (info.possibleMultiSlot()) { + info.setMultiSlot(frame, frameSlot, newValue); + return; + } + } + frame.setDouble(frameSlot, newValue); } public static void setObjectAndInvalidate(Frame frame, FrameSlot frameSlot, Object newValue, boolean isNonLocal, BranchProfile invalidateProfile) { assert !ActiveBinding.isActiveBinding(newValue); - frame.setObject(frameSlot, newValue); FrameSlotInfoImpl info = getFrameSlotInfo(frameSlot); - if (info.needsInvalidation()) { - info.setValue(newValue, frameSlot); + if (FastROptions.SharedContexts.getBooleanValue() && info.possibleMultiSlot() && !RContext.isSingle()) { + info.setMultiSlot(frame, frameSlot, newValue); + } else { + frame.setObject(frameSlot, newValue); + if (info.needsInvalidation()) { + info.setValue(newValue, frameSlot); + } + checkAndInvalidate(frame, frameSlot, isNonLocal, invalidateProfile); } - checkAndInvalidate(frame, frameSlot, isNonLocal, invalidateProfile); } public static void setObject(Frame frame, FrameSlot frameSlot, Object newValue) { + if (FastROptions.SharedContexts.getBooleanValue() && !RContext.isSingle()) { + FrameSlotInfoImpl info = getFrameSlotInfo(frameSlot); + if (info.possibleMultiSlot()) { + info.setMultiSlot(frame, frameSlot, newValue); + return; + } + } frame.setObject(frameSlot, newValue); } @@ -641,6 +872,7 @@ public final class FrameSlotChangeMonitor { * intended to be used for a non-function frame (and thus will only ever be used for one frame). */ public static synchronized void initializeNonFunctionFrameDescriptor(String name, MaterializedFrame frame) { + CompilerAsserts.neverPartOfCompilation(); frameDescriptors.put(handleBaseNamespaceEnv(frame), new FrameDescriptorMetaData(name, frame)); } @@ -680,11 +912,61 @@ public final class FrameSlotChangeMonitor { return getMetaData(frameDesc) != null; } + /* + * This method should be called for frames of all environments on the search path. + */ + public static void handleAllMultiSlots(Frame frame, boolean replicate) { + // make a copy avoid potential updates to the array iterated over + FrameSlot[] slots = new FrameSlot[frame.getFrameDescriptor().getSlots().size()]; + slots = frame.getFrameDescriptor().getSlots().toArray(slots); + for (int i = 0; i < slots.length; i++) { + if (!(slots[i].getIdentifier() instanceof RFrameSlot)) { + FrameSlotInfoImpl.handleSearchPathMultiSlot(frame, slots[i], replicate); + } + } + } + public static Object getObject(FrameSlot slot, Frame frame) throws FrameSlotTypeException { - return frame.getObject(slot); + if (FastROptions.SharedContexts.getBooleanValue() && !RContext.isSingle()) { + FrameSlotInfoImpl info = getFrameSlotInfo(slot); + if (info.noMultiSlot.isValid()) { + return frame.getObject(slot); + } + Object o; + try { + o = frame.getObject(slot); + } catch (FrameSlotTypeException e) { + CompilerDirectives.transferToInterpreter(); + o = null; + } + if (!(o instanceof MultiSlotData)) { + CompilerDirectives.transferToInterpreter(); + synchronized (info) { + o = frame.getObject(slot); + } + } + return ((MultiSlotData) o).get(RContext.getInstance().getMultiSlotInd()); + } else { + return frame.getObject(slot); + } } public static Object getValue(FrameSlot slot, Frame frame) { - return frame.getValue(slot); + if (FastROptions.SharedContexts.getBooleanValue() && !RContext.isSingle()) { + FrameSlotInfoImpl info = getFrameSlotInfo(slot); + if (info.noMultiSlot.isValid()) { + return frame.getValue(slot); + } + Object o = frame.getValue(slot); + if (!(o instanceof MultiSlotData)) { + CompilerDirectives.transferToInterpreter(); + synchronized (info) { + o = frame.getValue(slot); + } + } + return ((MultiSlotData) o).get(RContext.getInstance().getMultiSlotInd()); + } else { + return frame.getValue(slot); + } } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/NSBaseMaterializedFrame.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/NSBaseMaterializedFrame.java index af7d7af6ec7e74c8c527523e9c58216cd6736c29..e7006cbd06fb44eee613d651621de8cd08e5c778 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/NSBaseMaterializedFrame.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/NSBaseMaterializedFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -60,7 +60,7 @@ public final class NSBaseMaterializedFrame implements MaterializedFrame { } public void updateGlobalFrame(MaterializedFrame globalFrame) { - RArguments.setEnclosingFrame(this, globalFrame); + RArguments.setEnclosingFrame(this, globalFrame, true); } public FrameDescriptor getMarkerFrameDescriptor() { 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 b612947eda43f38e2abceae2556c11b0d781adef..492004a017ebcd486b15ecc4a2902cf9172876d3 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 @@ -130402,6 +130402,22 @@ a b c d e #{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/channels/R/channels9.R") } [1] TRUE FALSE +##com.oracle.truffle.r.test.library.fastr.TestChannels.runRSourceTests# +#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/channels/R/sharing1.R") } +[1] 42 7 + +##com.oracle.truffle.r.test.library.fastr.TestChannels.runRSourceTests# +#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/channels/R/sharing2.R") } +[1] "object 'x' not found" + +##com.oracle.truffle.r.test.library.fastr.TestChannels.runRSourceTests# +#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/channels/R/sharing3.R") } +[1] 42 7 + +##com.oracle.truffle.r.test.library.fastr.TestChannels.runRSourceTests# +#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/channels/R/sharing4.R") } +[1] 24 42 + ##com.oracle.truffle.r.test.library.fastr.TestInterop.testInteropEval# #if (length(grep("FastR", R.Version()$version.string)) != 1) { 1 } else { .fastr.interop.eval('application/x-r', '1') } [1] 1 diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/sharing1.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/sharing1.R new file mode 100644 index 0000000000000000000000000000000000000000..5a96c5964206e95944672aad6f548b19288dc56f --- /dev/null +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/sharing1.R @@ -0,0 +1,14 @@ +# test remote update in global space - values should remain distinct + +if (length(grep("FastR", R.Version()$version.string)) == 1) { + ch1 <- .fastr.channel.create(1L) + code <- "ch2 <- .fastr.channel.get(1L); x <- 7; .fastr.channel.send(ch2, x)" + x <- 42 + cx <- .fastr.context.spawn(code) + y <- .fastr.channel.receive(ch1) + .fastr.context.join(cx) + .fastr.channel.close(ch1) + print(c(x, y)) +} else { + print(c(42L, 7L)) +} diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/sharing2.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/sharing2.R new file mode 100644 index 0000000000000000000000000000000000000000..3c02fba5abc04a0d0c663d5c715828f69efde269 --- /dev/null +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/sharing2.R @@ -0,0 +1,18 @@ +# test subsequent remote updates in global space - second time value should have been reset + +if (length(grep("FastR", R.Version()$version.string)) == 1) { + ch1 <- .fastr.channel.create(1L) + code <- "ch2 <- .fastr.channel.get(1L); x <- 7; .fastr.channel.send(ch2, x)" + x <- 42 + cx <- .fastr.context.spawn(code) + y <- .fastr.channel.receive(ch1) + .fastr.context.join(cx) + code <- "ch2 <- .fastr.channel.get(1L); res <- tryCatch(z <- x, error=function(e) e); .fastr.channel.send(ch2, res[[1]])" + cx <- .fastr.context.spawn(code) + y <- .fastr.channel.receive(ch1) + .fastr.context.join(cx) + .fastr.channel.close(ch1) + print(y) +} else { + print("object 'x' not found") +} diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/sharing3.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/sharing3.R new file mode 100644 index 0000000000000000000000000000000000000000..ec10067a70355cc808f00d8dbb2eec40c0b4633e --- /dev/null +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/sharing3.R @@ -0,0 +1,15 @@ +# test remote update in base space - values should remain distinct + +if (length(grep("FastR", R.Version()$version.string)) == 1) { + ch1 <- .fastr.channel.create(1L) + # use an obscure name so it doesn't clash with other tests + code <- "ch2 <- .fastr.channel.get(1L); assign('tmp59857', 7, env=baseenv()); .fastr.channel.send(ch2, get('tmp59857', env=baseenv(), inherits=F))" + assign('tmp59857', 42, env=baseenv()) + cx <- .fastr.context.spawn(code) + y <- .fastr.channel.receive(ch1) + .fastr.context.join(cx) + .fastr.channel.close(ch1) + print(c(get('tmp59857', env=baseenv(), inherits=F), y)) +} else { + print(c(42L, 7L)) +} diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/sharing4.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/sharing4.R new file mode 100644 index 0000000000000000000000000000000000000000..147da5fcf5939daa2e22f3a78696a799cbd6795a --- /dev/null +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/sharing4.R @@ -0,0 +1,23 @@ +# test access to global environment with multiple context instantiations varying context number + +if (length(grep("FastR", R.Version()$version.string)) == 1) { + ch1 <- .fastr.channel.create(1L) + code <- "ch2 <- .fastr.channel.get(1L); x <- .fastr.channel.receive(ch2); .fastr.channel.send(ch2, x)" + x <- 7 + # create one child context + cx <- .fastr.context.spawn(code) + .fastr.channel.send(ch1, 7) + y <- .fastr.channel.receive(ch1) + .fastr.context.join(cx) + # create two child contexts + cx <- .fastr.context.spawn(rep(code, 2)) + .fastr.channel.send(ch1, 42) + .fastr.channel.send(ch1, 24) + y <- .fastr.channel.receive(ch1) + z <- .fastr.channel.receive(ch1) + + .fastr.channel.close(ch1) + print(sort(c(y, z))) +} else { + print(c(24L, 42L)) +}