use of org.graalvm.compiler.nodes.ReturnNode in project graal by oracle.
the class ClassSubstitutionsTests method testConstantReturn.
private void testConstantReturn(String name, Object value) {
StructuredGraph result = test(name);
ReturnNode ret = result.getNodes(ReturnNode.TYPE).first();
assertDeepEquals(1, result.getNodes(ReturnNode.TYPE).count());
assertDeepEquals(true, ret.result().isConstant());
assertDeepEquals(value, ret.result().asJavaConstant().asBoxedPrimitive());
}
use of org.graalvm.compiler.nodes.ReturnNode in project graal by oracle.
the class IntrinsifyMethodHandlesInvocationPlugin method processInvokeWithMethodHandle.
@SuppressWarnings("try")
private void processInvokeWithMethodHandle(GraphBuilderContext b, BytecodeProvider bytecodeProvider, ResolvedJavaMethod methodHandleMethod, ValueNode[] methodHandleArguments) {
Plugins graphBuilderPlugins = new Plugins(((ReplacementsImpl) originalProviders.getReplacements()).getGraphBuilderPlugins());
registerInvocationPlugins(graphBuilderPlugins.getInvocationPlugins(), bytecodeProvider);
graphBuilderPlugins.prependParameterPlugin(new MethodHandlesParameterPlugin(methodHandleArguments));
graphBuilderPlugins.clearInlineInvokePlugins();
graphBuilderPlugins.prependInlineInvokePlugin(new MethodHandlesInlineInvokePlugin());
graphBuilderPlugins.prependNodePlugin(new MethodHandlePlugin(originalProviders.getConstantReflection().getMethodHandleAccess(), false));
/* We do all the word type rewriting because parameters to the lambda can be word types. */
SnippetReflectionProvider originalSnippetReflection = GraalAccess.getOriginalSnippetReflection();
WordOperationPlugin wordOperationPlugin = new WordOperationPlugin(originalSnippetReflection, new WordTypes(originalProviders.getMetaAccess(), FrameAccess.getWordKind()));
graphBuilderPlugins.appendInlineInvokePlugin(wordOperationPlugin);
graphBuilderPlugins.appendTypePlugin(wordOperationPlugin);
graphBuilderPlugins.appendTypePlugin(new TrustedInterfaceTypePlugin());
graphBuilderPlugins.appendNodePlugin(wordOperationPlugin);
GraphBuilderConfiguration graphBuilderConfig = GraphBuilderConfiguration.getSnippetDefault(graphBuilderPlugins);
GraphBuilderPhase.Instance graphBuilder = new GraphBuilderPhase.Instance(originalProviders.getMetaAccess(), originalProviders.getStampProvider(), originalProviders.getConstantReflection(), originalProviders.getConstantFieldProvider(), graphBuilderConfig, OptimisticOptimizations.NONE, null);
DebugContext debug = b.getDebug();
StructuredGraph graph = new StructuredGraph.Builder(b.getOptions(), debug).method(toOriginal(methodHandleMethod)).build();
try (DebugContext.Scope s = debug.scope("IntrinsifyMethodHandles", graph)) {
graphBuilder.apply(graph);
/*
* We do not care about the improved type information from Pi nodes, so we just delete
* them to simplify our graph.
*/
for (PiNode pi : graph.getNodes(PiNode.TYPE)) {
pi.replaceAndDelete(pi.object());
}
/*
* Support for MethodHandle that adapt the input type to a more generic type, i.e., a
* MethodHandle that does a dynamic type check on a parameter.
*/
for (UnaryOpLogicNode node : graph.getNodes().filter(UnaryOpLogicNode.class).filter(v -> v instanceof IsNullNode || v instanceof InstanceOfNode)) {
ValueNode value = node.getValue();
if (value instanceof ParameterNode) {
/*
* We just assume that the InstanceOfNode or IsNullNode are used in an If and
* the true-successor is actually the branch we want. If that assumption is
* wrong, nothing bad happens - we will just continue to report the invocation
* as unsupported because the updated stamp for the parameter will not simplify
* the graph.
*/
if (node instanceof InstanceOfNode) {
InstanceOfNode inst = (InstanceOfNode) node;
TypeReference typeRef = inst.type();
value.setStamp(new ObjectStamp(typeRef.getType(), typeRef.isExact(), !inst.allowsNull(), false));
} else {
assert node instanceof IsNullNode;
ResolvedJavaType type = value.stamp(NodeView.DEFAULT).javaType(originalProviders.getMetaAccess());
value.setStamp(new ObjectStamp(type, false, /* non-null */
true, false));
}
}
}
/*
* The canonicalizer converts unsafe field accesses for get/set method handles back to
* high-level field load and store nodes.
*/
new CanonicalizerPhase().apply(graph, new PhaseContext(originalProviders));
for (FixedGuardNode guard : graph.getNodes(FixedGuardNode.TYPE)) {
if (guard.next() instanceof AccessFieldNode && guard.condition() instanceof IsNullNode && guard.isNegated() && ((IsNullNode) guard.condition()).getValue() == ((AccessFieldNode) guard.next()).object()) {
/*
* Method handles to load and stores fields have null checks. Remove them, since
* the null check is implicitly done by the field access.
*/
GraphUtil.removeFixedWithUnusedInputs(guard);
}
}
debug.dump(DebugContext.VERY_DETAILED_LEVEL, graph, "Final intrinisfication graph");
/*
* After parsing (and recursive inlining during parsing), the graph must contain only
* one invocation (and therefore only one MethodCallTargetNode), plus the parameters,
* constants, start, and return nodes.
*/
Node singleFunctionality = null;
ReturnNode singleReturn = null;
for (Node node : graph.getNodes()) {
if (node == graph.start() || node instanceof ParameterNode || node instanceof ConstantNode || node instanceof FrameState) {
/* Ignore the allowed framework around the nodes we care about. */
continue;
} else if (node instanceof Invoke) {
/* We check the MethodCallTargetNode, so we can ignore the invoke. */
continue;
} else if ((node instanceof MethodCallTargetNode || node instanceof LoadFieldNode || node instanceof StoreFieldNode) && singleFunctionality == null) {
singleFunctionality = node;
continue;
} else if (node instanceof ReturnNode && singleReturn == null) {
singleReturn = (ReturnNode) node;
continue;
}
throw new UnsupportedFeatureException("Invoke with MethodHandle argument could not be reduced to at most a single call: " + methodHandleMethod.format("%H.%n(%p)"));
}
if (singleFunctionality instanceof MethodCallTargetNode) {
MethodCallTargetNode singleCallTarget = (MethodCallTargetNode) singleFunctionality;
assert singleReturn.result() == null || singleReturn.result() == singleCallTarget.invoke();
/*
* Replace the originalTarget with the replacementTarget. Note that the
* replacementTarget node belongs to a different graph than originalTarget, so we
* need to match parameter back to the original graph and allocate a new
* MethodCallTargetNode for the original graph.
*/
ValueNode[] replacedArguments = new ValueNode[singleCallTarget.arguments().size()];
for (int i = 0; i < replacedArguments.length; i++) {
replacedArguments[i] = lookup(b, methodHandleArguments, singleCallTarget.arguments().get(i));
}
b.handleReplacedInvoke(singleCallTarget.invokeKind(), lookup(singleCallTarget.targetMethod()), replacedArguments, false);
} else if (singleFunctionality instanceof LoadFieldNode) {
LoadFieldNode fieldLoad = (LoadFieldNode) singleFunctionality;
b.addPush(b.getInvokeReturnType().getJavaKind(), LoadFieldNode.create(null, lookup(b, methodHandleArguments, fieldLoad.object()), lookup(fieldLoad.field())));
} else if (singleFunctionality instanceof StoreFieldNode) {
StoreFieldNode fieldStore = (StoreFieldNode) singleFunctionality;
b.add(new StoreFieldNode(lookup(b, methodHandleArguments, fieldStore.object()), lookup(fieldStore.field()), lookup(b, methodHandleArguments, fieldStore.value())));
} else if (singleReturn.result() != null) {
/* Replace the invocation with he constant result. */
JavaConstant constantResult = singleReturn.result().asJavaConstant();
assert b.getInvokeReturnType().getJavaKind() == constantResult.getJavaKind();
b.addPush(constantResult.getJavaKind(), ConstantNode.forConstant(lookup(constantResult), universeProviders.getMetaAccess()));
} else {
/* No invoke and no return value, so nothing to do. */
assert b.getInvokeReturnType().getJavaKind() == JavaKind.Void;
}
} catch (Throwable ex) {
throw debug.handle(ex);
}
}
use of org.graalvm.compiler.nodes.ReturnNode in project graal by oracle.
the class SnippetTemplate method rewireMemoryGraph.
private void rewireMemoryGraph(ValueNode replacee, UnmodifiableEconomicMap<Node, Node> duplicates) {
if (replacee.graph().isAfterFloatingReadPhase()) {
// rewire outgoing memory edges
replaceMemoryUsages(replacee, new MemoryOutputMap(replacee, duplicates));
if (returnNode != null) {
ReturnNode ret = (ReturnNode) duplicates.get(returnNode);
if (ret != null) {
MemoryMapNode memoryMap = ret.getMemoryMap();
if (memoryMap != null) {
ret.setMemoryMap(null);
memoryMap.safeDelete();
}
}
}
if (memoryAnchor != null) {
// rewire incoming memory edges
MemoryAnchorNode memoryDuplicate = (MemoryAnchorNode) duplicates.get(memoryAnchor);
replaceMemoryUsages(memoryDuplicate, new MemoryInputMap(replacee));
if (memoryDuplicate.hasNoUsages()) {
if (memoryDuplicate.next() != null) {
memoryDuplicate.graph().removeFixed(memoryDuplicate);
} else {
// this was a dummy memory node used when instantiating pure data-flow
// snippets: it was not attached to the control flow.
memoryDuplicate.safeDelete();
}
}
}
}
}
use of org.graalvm.compiler.nodes.ReturnNode in project graal by oracle.
the class SnippetTemplate method instantiate.
/**
* Replaces a given floating node with this specialized snippet.
*
* @param metaAccess
* @param replacee the node that will be replaced
* @param replacer object that replaces the usages of {@code replacee}
* @param tool lowering tool used to insert the snippet into the control-flow
* @param args the arguments to be bound to the flattened positional parameters of the snippet
*/
@SuppressWarnings("try")
public void instantiate(MetaAccessProvider metaAccess, FloatingNode replacee, UsageReplacer replacer, LoweringTool tool, Arguments args) {
DebugContext debug = replacee.getDebug();
assert assertSnippetKills(replacee);
try (DebugCloseable a = args.info.instantiationTimer.start(debug)) {
args.info.instantiationCounter.increment(debug);
// Inline the snippet nodes, replacing parameters with the given args in the process
StartNode entryPointNode = snippet.start();
FixedNode firstCFGNode = entryPointNode.next();
StructuredGraph replaceeGraph = replacee.graph();
EconomicMap<Node, Node> replacements = bind(replaceeGraph, metaAccess, args);
replacements.put(entryPointNode, tool.getCurrentGuardAnchor().asNode());
UnmodifiableEconomicMap<Node, Node> duplicates = inlineSnippet(replacee, debug, replaceeGraph, replacements);
FixedWithNextNode lastFixedNode = tool.lastFixedNode();
assert lastFixedNode != null && lastFixedNode.isAlive() : replaceeGraph + " lastFixed=" + lastFixedNode;
FixedNode next = lastFixedNode.next();
lastFixedNode.setNext(null);
FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode);
replaceeGraph.addAfterFixed(lastFixedNode, firstCFGNodeDuplicate);
rewireFrameStates(replacee, duplicates);
updateStamps(replacee, duplicates);
rewireMemoryGraph(replacee, duplicates);
// Replace all usages of the replacee with the value returned by the snippet
ReturnNode returnDuplicate = (ReturnNode) duplicates.get(returnNode);
ValueNode returnValue = returnDuplicate.result();
assert returnValue != null || replacee.hasNoUsages();
replacer.replace(replacee, returnValue);
if (returnDuplicate.isAlive()) {
returnDuplicate.replaceAndDelete(next);
}
debug.dump(DebugContext.DETAILED_LEVEL, replaceeGraph, "After lowering %s with %s", replacee, this);
}
}
use of org.graalvm.compiler.nodes.ReturnNode in project graal by oracle.
the class PEGraphDecoder method finishInlining.
@Override
protected void finishInlining(MethodScope is) {
PEMethodScope inlineScope = (PEMethodScope) is;
ResolvedJavaMethod inlineMethod = inlineScope.method;
PEMethodScope methodScope = inlineScope.caller;
LoopScope loopScope = inlineScope.callerLoopScope;
InvokeData invokeData = inlineScope.invokeData;
Invoke invoke = invokeData.invoke;
FixedNode invokeNode = invoke.asNode();
ValueNode exceptionValue = null;
int returnNodeCount = 0;
int unwindNodeCount = 0;
List<ControlSinkNode> returnAndUnwindNodes = inlineScope.returnAndUnwindNodes;
for (int i = 0; i < returnAndUnwindNodes.size(); i++) {
FixedNode fixedNode = returnAndUnwindNodes.get(i);
if (fixedNode instanceof ReturnNode) {
returnNodeCount++;
} else if (fixedNode.isAlive()) {
assert fixedNode instanceof UnwindNode;
unwindNodeCount++;
}
}
if (unwindNodeCount > 0) {
FixedNode unwindReplacement;
if (invoke instanceof InvokeWithExceptionNode) {
/* Decoding continues for the exception handler. */
unwindReplacement = makeStubNode(methodScope, loopScope, invokeData.exceptionNextOrderId);
} else {
/* No exception handler available, so the only thing we can do is deoptimize. */
unwindReplacement = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler));
}
if (unwindNodeCount == 1) {
/* Only one UnwindNode, we can use the exception directly. */
UnwindNode unwindNode = getSingleMatchingNode(returnAndUnwindNodes, returnNodeCount > 0, UnwindNode.class);
exceptionValue = unwindNode.exception();
unwindNode.replaceAndDelete(unwindReplacement);
} else {
/*
* More than one UnwindNode. This can happen with the loop explosion strategy
* FULL_EXPLODE_UNTIL_RETURN, where we keep exploding after the loop and therefore
* also explode exception paths. Merge the exception in a similar way as multiple
* return values.
*/
MergeNode unwindMergeNode = graph.add(new MergeNode());
exceptionValue = InliningUtil.mergeValueProducers(unwindMergeNode, getMatchingNodes(returnAndUnwindNodes, returnNodeCount > 0, UnwindNode.class, unwindNodeCount), null, unwindNode -> unwindNode.exception());
unwindMergeNode.setNext(unwindReplacement);
ensureExceptionStateDecoded(inlineScope);
unwindMergeNode.setStateAfter(inlineScope.exceptionState.duplicateModified(JavaKind.Object, JavaKind.Object, exceptionValue));
}
}
assert invoke.next() == null;
assert !(invoke instanceof InvokeWithExceptionNode) || ((InvokeWithExceptionNode) invoke).exceptionEdge() == null;
ValueNode returnValue;
if (returnNodeCount == 0) {
returnValue = null;
} else if (returnNodeCount == 1) {
ReturnNode returnNode = getSingleMatchingNode(returnAndUnwindNodes, unwindNodeCount > 0, ReturnNode.class);
returnValue = returnNode.result();
FixedNode n = nodeAfterInvoke(methodScope, loopScope, invokeData, AbstractBeginNode.prevBegin(returnNode));
returnNode.replaceAndDelete(n);
} else {
AbstractMergeNode merge = graph.add(new MergeNode());
merge.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope, invokeData.stateAfterOrderId));
returnValue = InliningUtil.mergeReturns(merge, getMatchingNodes(returnAndUnwindNodes, unwindNodeCount > 0, ReturnNode.class, returnNodeCount));
FixedNode n = nodeAfterInvoke(methodScope, loopScope, invokeData, merge);
merge.setNext(n);
}
invokeNode.replaceAtUsages(returnValue);
/*
* Usage the handles that we have on the return value and the exception to update the
* orderId->Node table.
*/
registerNode(loopScope, invokeData.invokeOrderId, returnValue, true, true);
if (invoke instanceof InvokeWithExceptionNode) {
registerNode(loopScope, invokeData.exceptionOrderId, exceptionValue, true, true);
}
if (inlineScope.exceptionPlaceholderNode != null) {
inlineScope.exceptionPlaceholderNode.replaceAtUsagesAndDelete(exceptionValue);
}
deleteInvoke(invoke);
for (InlineInvokePlugin plugin : inlineInvokePlugins) {
plugin.notifyAfterInline(inlineMethod);
}
}
Aggregations