From 25c961fe89ed3763042db6488605e60c0660d715 Mon Sep 17 00:00:00 2001 From: Lukas Stadler <lukas.stadler@oracle.com> Date: Mon, 30 Mar 2015 13:51:59 +0200 Subject: [PATCH] encapsulate call argument matching --- .../r/nodes/function/ArgumentMatcher.java | 9 - .../r/nodes/function/CallMatcherNode.java | 289 ++++++++++++++++++ 2 files changed, 289 insertions(+), 9 deletions(-) create mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java 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 6235c2b33b..206dac0841 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 @@ -243,15 +243,6 @@ public class ArgumentMatcher { return new EvaluatedArguments(evaledArgs, formals.getSignature()); } - public static void evaluatePromises(VirtualFrame frame, PromiseHelperNode promiseHelper, Object[] args) { - for (int i = 0; i < args.length; i++) { - Object arg = args[i]; - if (arg instanceof RPromise) { - args[i] = promiseHelper.evaluate(frame, (RPromise) arg); - } - } - } - private static String getErrorForArgument(RNode[] suppliedArgs, int index) { RNode node = suppliedArgs[index]; if (node instanceof VarArgNode) { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java new file mode 100644 index 0000000000..09427823c7 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java @@ -0,0 +1,289 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2014, Purdue University + * Copyright (c) 2014, 2015, Oracle and/or its affiliates + * + * All rights reserved. + */ + +package com.oracle.truffle.r.nodes.function; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.source.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.r.nodes.*; +import com.oracle.truffle.r.nodes.function.ArgumentMatcher.MatchPermutation; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.RArguments.*; +import com.oracle.truffle.r.runtime.data.*; + +public abstract class CallMatcherNode extends Node { + + protected final boolean forNextMethod; + protected final boolean argsAreEvaluated; + + @Child private PromiseHelperNode promiseHelper; + + public CallMatcherNode(boolean forNextMethod, boolean argsAreEvaluated) { + this.forNextMethod = forNextMethod; + this.argsAreEvaluated = argsAreEvaluated; + } + + protected static final int MAX_CACHE_DEPTH = 3; + + public static CallMatcherNode create(boolean forNextMethod, boolean argsAreEvaluated) { + return new CallMatcherUninitializedNode(forNextMethod, argsAreEvaluated); + } + + public abstract Object execute(VirtualFrame frame, ArgumentsSignature suppliedSignature, Object[] suppliedArguments, RFunction function, S3Args s3Args); + + private static CallMatcherCachedNode specialize(ArgumentsSignature suppliedSignature, Object[] suppliedArguments, RFunction function, SourceSection source, boolean forNextMethod, + boolean argsAreEvaluated, CallMatcherNode next) { + + int argCount = suppliedArguments.length; + int argListSize = argCount; + + // extract vararg signatures from the arguments + ArgumentsSignature[] varArgSignatures = null; + for (int i = 0; i < suppliedArguments.length; i++) { + Object arg = suppliedArguments[i]; + if (arg instanceof RArgsValuesAndNames) { + if (varArgSignatures == null) { + varArgSignatures = new ArgumentsSignature[suppliedArguments.length]; + } + varArgSignatures[i] = ((RArgsValuesAndNames) arg).getSignature(); + argListSize += ((RArgsValuesAndNames) arg).length() - 1; + } + } + + long[] preparePermutation; + ArgumentsSignature resultSignature; + if (varArgSignatures != null) { + resultSignature = ArgumentsSignature.flattenNames(suppliedSignature, varArgSignatures, argListSize); + preparePermutation = ArgumentsSignature.flattenIndexes(varArgSignatures, argListSize); + } else { + preparePermutation = new long[argCount]; + for (int i = 0; i < argCount; i++) { + preparePermutation[i] = i; + } + resultSignature = suppliedSignature; + } + + assert resultSignature != null; + ArgumentsSignature formalSignature = ArgumentMatcher.getFunctionSignature(function); + MatchPermutation permutation = ArgumentMatcher.matchArguments(resultSignature, formalSignature, source, forNextMethod); + + CallMatcherCachedNode cachedNode = new CallMatcherCachedNode(suppliedSignature, varArgSignatures, function, preparePermutation, permutation, forNextMethod, argsAreEvaluated, next); + return cachedNode; + } + + protected final Object[] prepareArguments(VirtualFrame frame, Object[] reorderedArgs, ArgumentsSignature reorderedSignature, RFunction function, S3Args s3Args) { + Object[] argObject = RArguments.create(function, getSourceSection(), null, RArguments.getDepth(frame) + 1, reorderedArgs, reorderedSignature); + RArguments.setS3Args(argObject, s3Args); + return argObject; + } + + protected final void evaluatePromises(VirtualFrame frame, RFunction function, Object[] args) { + if (function.isBuiltin()) { + if (!argsAreEvaluated) { + for (int i = 0; i < args.length; i++) { + Object arg = args[i]; + if (arg instanceof RPromise) { + if (promiseHelper == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + promiseHelper = insert(new PromiseHelperNode()); + } + args[i] = promiseHelper.evaluate(frame, (RPromise) arg); + } + } + } + FormalArguments formals = ((RRootNode) function.getRootNode()).getFormalArguments(); + for (int i = 0; i < args.length; i++) { + Object arg = args[i]; + if (arg == RMissing.instance) { + args[i] = formals.getInternalDefaultArgumentAt(i); + } + } + } + } + + @NodeInfo(cost = NodeCost.UNINITIALIZED) + private static final class CallMatcherUninitializedNode extends CallMatcherNode { + public CallMatcherUninitializedNode(boolean forNextMethod, boolean argsAreEvaluated) { + super(forNextMethod, argsAreEvaluated); + } + + private int depth; + + @Override + public Object execute(VirtualFrame frame, ArgumentsSignature suppliedSignature, Object[] suppliedArguments, RFunction function, S3Args s3Args) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + if (++depth > MAX_CACHE_DEPTH) { + return replace(new CallMatcherGenericNode(forNextMethod, argsAreEvaluated)).execute(frame, suppliedSignature, suppliedArguments, function, s3Args); + } else { + CallMatcherCachedNode cachedNode = replace(specialize(suppliedSignature, suppliedArguments, function, getEncapsulatingSourceSection(), forNextMethod, argsAreEvaluated, this)); + return cachedNode.execute(frame, suppliedSignature, suppliedArguments, function, s3Args); + } + } + } + + private static final class CallMatcherCachedNode extends CallMatcherNode { + + @Child private CallMatcherNode next; + + @Child private DirectCallNode call; + + private final ArgumentsSignature cachedSuppliedSignature; + private final ArgumentsSignature[] cachedVarArgSignatures; + private final RFunction cachedFunction; + private final RootCallTarget cachedCallTarget; + @CompilationFinal private final long[] preparePermutation; + private final MatchPermutation permutation; + + public CallMatcherCachedNode(ArgumentsSignature suppliedSignature, ArgumentsSignature[] varArgSignatures, RFunction function, long[] preparePermutation, MatchPermutation permutation, + boolean forNextMethod, boolean argsAreEvaluated, CallMatcherNode next) { + super(forNextMethod, argsAreEvaluated); + this.cachedSuppliedSignature = suppliedSignature; + this.cachedVarArgSignatures = varArgSignatures; + this.cachedFunction = function; + this.cachedCallTarget = function.getTarget(); + this.preparePermutation = preparePermutation; + this.permutation = permutation; + this.next = next; + + this.call = Truffle.getRuntime().createDirectCallNode(cachedCallTarget); + } + + @Override + public Object execute(VirtualFrame frame, ArgumentsSignature suppliedSignature, Object[] suppliedArguments, RFunction function, S3Args s3Args) { + if (suppliedSignature == cachedSuppliedSignature && function == cachedFunction && checkLastArgSignature(cachedSuppliedSignature, suppliedArguments)) { + + Object[] preparedArguments = prepareSuppliedArgument(preparePermutation, suppliedArguments); + + FormalArguments formals = ((RRootNode) function.getRootNode()).getFormalArguments(); + Object[] reorderedArgs = ArgumentMatcher.matchArgumentsEvaluated(permutation, preparedArguments, formals); + evaluatePromises(frame, function, reorderedArgs); + Object[] arguments = prepareArguments(frame, reorderedArgs, formals.getSignature(), function, s3Args); + return call.call(frame, arguments); + } else { + return next.execute(frame, suppliedSignature, suppliedArguments, function, s3Args); + } + } + + @ExplodeLoop + private boolean checkLastArgSignature(ArgumentsSignature cachedSuppliedSignature2, Object[] arguments) { + for (int i = 0; i < cachedSuppliedSignature2.getLength(); i++) { + Object arg = arguments[i]; + if (arg instanceof RArgsValuesAndNames) { + if (cachedVarArgSignatures == null || cachedVarArgSignatures[i] != ((RArgsValuesAndNames) arg).getSignature()) { + return false; + } + } else { + if (cachedVarArgSignatures != null && cachedVarArgSignatures[i] != null) { + return false; + } + } + } + return true; + } + + @ExplodeLoop + private static Object[] prepareSuppliedArgument(long[] preparePermutation, Object[] arguments) { + Object[] result = new Object[preparePermutation.length]; + for (int i = 0; i < result.length; i++) { + long source = preparePermutation[i]; + if (source >= 0) { + result[i] = arguments[(int) source]; + } else { + source = -source; + result[i] = ((RArgsValuesAndNames) arguments[(int) (source >> 32)]).getValues()[(int) source]; + } + } + return result; + } + } + + private static final class CallMatcherGenericNode extends CallMatcherNode { + + public CallMatcherGenericNode(boolean forNextMethod, boolean argsAreEvaluated) { + super(forNextMethod, argsAreEvaluated); + } + + @Child private PromiseHelperNode promiseHelper; + @Child private IndirectCallNode call = Truffle.getRuntime().createIndirectCallNode(); + + private final ConditionProfile hasVarArgsProfile = ConditionProfile.createBinaryProfile(); + + @Override + public Object execute(VirtualFrame frame, ArgumentsSignature suppliedSignature, Object[] suppliedArguments, RFunction function, S3Args s3Args) { + EvaluatedArguments reorderedArgs = reorderArguments(suppliedArguments, function, suppliedSignature, getEncapsulatingSourceSection()); + evaluatePromises(frame, function, reorderedArgs.arguments); + Object[] arguments = prepareArguments(frame, reorderedArgs.arguments, reorderedArgs.signature, function, s3Args); + return call.call(frame, function.getTarget(), arguments); + } + + @TruffleBoundary + protected EvaluatedArguments reorderArguments(Object[] args, RFunction function, ArgumentsSignature paramSignature, SourceSection errorSourceSection) { + assert paramSignature.getLength() == args.length; + + int argCount = args.length; + int argListSize = argCount; + + boolean hasVarArgs = false; + for (int fi = 0; fi < argCount; ++fi) { + Object arg = args[fi]; + if (hasVarArgsProfile.profile(arg instanceof RArgsValuesAndNames)) { + hasVarArgs = true; + argListSize += ((RArgsValuesAndNames) arg).length() - 1; + } + } + Object[] argValues; + ArgumentsSignature signature; + if (hasVarArgs) { + argValues = new Object[argListSize]; + String[] argNames = new String[argListSize]; + int index = 0; + for (int fi = 0; fi < argCount; ++fi) { + Object arg = args[fi]; + if (arg instanceof RArgsValuesAndNames) { + RArgsValuesAndNames varArgs = (RArgsValuesAndNames) arg; + Object[] varArgValues = varArgs.getValues(); + ArgumentsSignature varArgSignature = varArgs.getSignature(); + for (int i = 0; i < varArgs.length(); i++) { + argNames[index] = varArgSignature.getName(i); + argValues[index++] = checkMissing(varArgValues[i]); + } + } else { + argNames[index] = paramSignature.getName(fi); + argValues[index++] = checkMissing(arg); + } + } + signature = ArgumentsSignature.get(argNames); + } else { + argValues = new Object[argCount]; + for (int i = 0; i < argCount; i++) { + argValues[i] = checkMissing(args[i]); + } + signature = paramSignature; + } + + // ...and use them as 'supplied' arguments... + EvaluatedArguments evaledArgs = EvaluatedArguments.create(argValues, signature); + + // ...to match them against the chosen function's formal arguments + EvaluatedArguments evaluated = ArgumentMatcher.matchArgumentsEvaluated(function, evaledArgs, errorSourceSection, forNextMethod); + return evaluated; + } + + protected static Object checkMissing(Object value) { + return RMissingHelper.isMissing(value) || (value instanceof RPromise && RMissingHelper.isMissingName((RPromise) value)) ? null : value; + } + } +} -- GitLab