/*
 * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */


package org.graalvm.compiler.replacements;

import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import org.graalvm.compiler.core.common.CompilationIdentifier;
import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
import org.graalvm.compiler.core.common.type.StampFactory;
import org.graalvm.compiler.core.common.type.StampPair;
import org.graalvm.compiler.debug.DebugCloseable;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.graph.Graph;
import org.graalvm.compiler.graph.Node.ValueNumberable;
import org.graalvm.compiler.graph.NodeSourcePosition;
import org.graalvm.compiler.java.FrameStateBuilder;
import org.graalvm.compiler.java.GraphBuilderPhase;
import org.graalvm.compiler.nodes.AbstractBeginNode;
import org.graalvm.compiler.nodes.AbstractMergeNode;
import org.graalvm.compiler.nodes.BeginNode;
import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
import org.graalvm.compiler.nodes.EndNode;
import org.graalvm.compiler.nodes.FixedNode;
import org.graalvm.compiler.nodes.FixedWithNextNode;
import org.graalvm.compiler.nodes.IfNode;
import org.graalvm.compiler.nodes.InvokeNode;
import org.graalvm.compiler.nodes.InvokeWithExceptionNode;
import org.graalvm.compiler.nodes.KillingBeginNode;
import org.graalvm.compiler.nodes.LogicNode;
import org.graalvm.compiler.nodes.MergeNode;
import org.graalvm.compiler.nodes.NodeView;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.UnwindNode;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.calc.FloatingNode;
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderTool;
import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext;
import org.graalvm.compiler.nodes.java.ExceptionObjectNode;
import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
import org.graalvm.compiler.nodes.spi.StampProvider;
import org.graalvm.compiler.nodes.type.StampTool;
import org.graalvm.compiler.phases.OptimisticOptimizations;
import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase.Optionality;
import org.graalvm.compiler.phases.common.inlining.InliningUtil;
import org.graalvm.compiler.phases.util.Providers;
import org.graalvm.compiler.word.WordTypes;
import jdk.internal.vm.compiler.word.LocationIdentity;

import jdk.vm.ci.code.BytecodeFrame;
import jdk.vm.ci.meta.ConstantReflectionProvider;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.JavaType;
import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.Signature;

A utility for manually creating a graph. This will be expanded as necessary to support all subsystems that employ manual graph creation (as opposed to bytecode parsing based graph creation).
/** * A utility for manually creating a graph. This will be expanded as necessary to support all * subsystems that employ manual graph creation (as opposed to {@linkplain GraphBuilderPhase * bytecode parsing} based graph creation). */
public class GraphKit implements GraphBuilderTool { protected final Providers providers; protected final StructuredGraph graph; protected final WordTypes wordTypes; protected final GraphBuilderConfiguration.Plugins graphBuilderPlugins; protected FixedWithNextNode lastFixedNode; private final List<Structure> structures; protected abstract static class Structure { } public GraphKit(DebugContext debug, ResolvedJavaMethod stubMethod, Providers providers, WordTypes wordTypes, Plugins graphBuilderPlugins, CompilationIdentifier compilationId, String name) { this.providers = providers; StructuredGraph.Builder builder = new StructuredGraph.Builder(debug.getOptions(), debug).compilationId(compilationId); if (name != null) { builder.name(name); } else { builder.method(stubMethod); } this.graph = builder.build(); graph.disableUnsafeAccessTracking(); if (graph.trackNodeSourcePosition()) { // Set up a default value that everything constructed by GraphKit will use. graph.withNodeSourcePosition(NodeSourcePosition.substitution(stubMethod)); } this.wordTypes = wordTypes; this.graphBuilderPlugins = graphBuilderPlugins; this.lastFixedNode = graph.start(); structures = new ArrayList<>(); /* * Add a dummy element, so that the access of the last element never leads to an exception. */ structures.add(new Structure() { }); } @Override public StructuredGraph getGraph() { return graph; } @Override public ConstantReflectionProvider getConstantReflection() { return providers.getConstantReflection(); } @Override public ConstantFieldProvider getConstantFieldProvider() { return providers.getConstantFieldProvider(); } @Override public MetaAccessProvider getMetaAccess() { return providers.getMetaAccess(); } @Override public StampProvider getStampProvider() { return providers.getStampProvider(); } @Override public boolean parsingIntrinsic() { return true; }
Ensures a floating node is added to or already present in the graph via Graph.unique.
Returns:a node similar to node if one exists, otherwise node
/** * Ensures a floating node is added to or already present in the graph via {@link Graph#unique}. * * @return a node similar to {@code node} if one exists, otherwise {@code node} */
public <T extends FloatingNode & ValueNumberable> T unique(T node) { return graph.unique(changeToWord(node)); } public <T extends ValueNode> T add(T node) { return graph.add(changeToWord(node)); } public <T extends ValueNode> T changeToWord(T node) { if (wordTypes != null && wordTypes.isWord(node)) { node.setStamp(wordTypes.getWordStamp(StampTool.typeOrNull(node))); } return node; } @Override public <T extends ValueNode> T append(T node) { T result = graph.addOrUniqueWithInputs(changeToWord(node)); if (result instanceof FixedNode) { updateLastFixed((FixedNode) result); } return result; } private void updateLastFixed(FixedNode result) { assert lastFixedNode != null; assert result.predecessor() == null; graph.addAfterFixed(lastFixedNode, result); if (result instanceof FixedWithNextNode) { lastFixedNode = (FixedWithNextNode) result; } else { lastFixedNode = null; } } public InvokeNode createInvoke(Class<?> declaringClass, String name, ValueNode... args) { return createInvoke(declaringClass, name, InvokeKind.Static, null, BytecodeFrame.UNKNOWN_BCI, args); }
Creates and appends an InvokeNode for a call to a given method with a given set of arguments. The method is looked up via reflection based on the declaring class and name.
Params:
  • declaringClass – the class declaring the invoked method
  • name – the name of the invoked method
  • args – the arguments to the invocation
/** * Creates and appends an {@link InvokeNode} for a call to a given method with a given set of * arguments. The method is looked up via reflection based on the declaring class and name. * * @param declaringClass the class declaring the invoked method * @param name the name of the invoked method * @param args the arguments to the invocation */
public InvokeNode createInvoke(Class<?> declaringClass, String name, InvokeKind invokeKind, FrameStateBuilder frameStateBuilder, int bci, ValueNode... args) { boolean isStatic = invokeKind == InvokeKind.Static; ResolvedJavaMethod method = findMethod(declaringClass, name, isStatic); return createInvoke(method, invokeKind, frameStateBuilder, bci, args); } public ResolvedJavaMethod findMethod(Class<?> declaringClass, String name, boolean isStatic) { ResolvedJavaMethod method = null; for (Method m : declaringClass.getDeclaredMethods()) { if (Modifier.isStatic(m.getModifiers()) == isStatic && m.getName().equals(name)) { assert method == null : "found more than one method in " + declaringClass + " named " + name; method = providers.getMetaAccess().lookupJavaMethod(m); } } GraalError.guarantee(method != null, "Could not find %s.%s (%s)", declaringClass, name, isStatic ? "static" : "non-static"); return method; } public ResolvedJavaMethod findMethod(Class<?> declaringClass, String name, Class<?>... parameterTypes) { try { Method m = declaringClass.getDeclaredMethod(name, parameterTypes); return providers.getMetaAccess().lookupJavaMethod(m); } catch (NoSuchMethodException | SecurityException e) { throw new AssertionError(e); } }
Creates and appends an InvokeNode for a call to a given method with a given set of arguments.
/** * Creates and appends an {@link InvokeNode} for a call to a given method with a given set of * arguments. */
@SuppressWarnings("try") public InvokeNode createInvoke(ResolvedJavaMethod method, InvokeKind invokeKind, FrameStateBuilder frameStateBuilder, int bci, ValueNode... args) { try (DebugCloseable context = graph.withNodeSourcePosition(NodeSourcePosition.substitution(graph.currentNodeSourcePosition(), method))) { assert method.isStatic() == (invokeKind == InvokeKind.Static); Signature signature = method.getSignature(); JavaType returnType = signature.getReturnType(null); assert checkArgs(method, args); StampPair returnStamp = graphBuilderPlugins.getOverridingStamp(this, returnType, false); if (returnStamp == null) { returnStamp = StampFactory.forDeclaredType(graph.getAssumptions(), returnType, false); } MethodCallTargetNode callTarget = graph.add(createMethodCallTarget(invokeKind, method, args, returnStamp, bci)); InvokeNode invoke = append(new InvokeNode(callTarget, bci)); if (frameStateBuilder != null) { if (invoke.getStackKind() != JavaKind.Void) { frameStateBuilder.push(invoke.getStackKind(), invoke); } invoke.setStateAfter(frameStateBuilder.create(bci, invoke)); if (invoke.getStackKind() != JavaKind.Void) { frameStateBuilder.pop(invoke.getStackKind()); } } return invoke; } } @SuppressWarnings("try") public InvokeWithExceptionNode createInvokeWithExceptionAndUnwind(ResolvedJavaMethod method, InvokeKind invokeKind, FrameStateBuilder frameStateBuilder, int invokeBci, int exceptionEdgeBci, ValueNode... args) { try (DebugCloseable context = graph.withNodeSourcePosition(NodeSourcePosition.substitution(graph.currentNodeSourcePosition(), method))) { InvokeWithExceptionNode result = startInvokeWithException(method, invokeKind, frameStateBuilder, invokeBci, exceptionEdgeBci, args); exceptionPart(); ExceptionObjectNode exception = exceptionObject(); append(new UnwindNode(exception)); endInvokeWithException(); return result; } } @SuppressWarnings("try") public InvokeWithExceptionNode createInvokeWithExceptionAndUnwind(MethodCallTargetNode callTarget, FrameStateBuilder frameStateBuilder, int invokeBci, int exceptionEdgeBci) { try (DebugCloseable context = graph.withNodeSourcePosition(NodeSourcePosition.substitution(graph.currentNodeSourcePosition(), callTarget.targetMethod()))) { InvokeWithExceptionNode result = startInvokeWithException(callTarget, frameStateBuilder, invokeBci, exceptionEdgeBci); exceptionPart(); ExceptionObjectNode exception = exceptionObject(); append(new UnwindNode(exception)); endInvokeWithException(); return result; } } protected MethodCallTargetNode createMethodCallTarget(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args, StampPair returnStamp, @SuppressWarnings("unused") int bci) { return new MethodCallTargetNode(invokeKind, targetMethod, args, returnStamp, null); } protected final JavaKind asKind(JavaType type) { return wordTypes != null ? wordTypes.asKind(type) : type.getJavaKind(); }
Determines if a given set of arguments is compatible with the signature of a given method.
Throws:
  • AssertionError – if args are not compatible with the signature if method
Returns:true if args are compatible with the signature if method
/** * Determines if a given set of arguments is compatible with the signature of a given method. * * @return true if {@code args} are compatible with the signature if {@code method} * @throws AssertionError if {@code args} are not compatible with the signature if * {@code method} */
public boolean checkArgs(ResolvedJavaMethod method, ValueNode... args) { Signature signature = method.getSignature(); boolean isStatic = method.isStatic(); if (signature.getParameterCount(!isStatic) != args.length) { throw new AssertionError(graph + ": wrong number of arguments to " + method); } int argIndex = 0; if (!isStatic) { JavaKind expected = asKind(method.getDeclaringClass()); JavaKind actual = args[argIndex++].stamp(NodeView.DEFAULT).getStackKind(); assert expected == actual : graph + ": wrong kind of value for receiver argument of call to " + method + " [" + actual + " != " + expected + "]"; } for (int i = 0; i != signature.getParameterCount(false); i++) { JavaKind expected = asKind(signature.getParameterType(i, method.getDeclaringClass())).getStackKind(); JavaKind actual = args[argIndex++].stamp(NodeView.DEFAULT).getStackKind(); if (expected != actual) { throw new AssertionError(graph + ": wrong kind of value for argument " + i + " of call to " + method + " [" + actual + " != " + expected + "]"); } } return true; }
Recursively inlines all invocations currently in the graph.
/** * Recursively {@linkplain #inline inlines} all invocations currently in the graph. */
public void inlineInvokes(String reason, String phase) { while (!graph.getNodes().filter(InvokeNode.class).isEmpty()) { for (InvokeNode invoke : graph.getNodes().filter(InvokeNode.class).snapshot()) { inline(invoke, reason, phase); } } // Clean up all code that is now dead after inlining. new DeadCodeEliminationPhase().apply(graph); }
Inlines a given invocation to a method. The graph of the inlined method is processed in the same manner as for snippets and method substitutions.
/** * Inlines a given invocation to a method. The graph of the inlined method is processed in the * same manner as for snippets and method substitutions. */
public void inline(InvokeNode invoke, String reason, String phase) { ResolvedJavaMethod method = ((MethodCallTargetNode) invoke.callTarget()).targetMethod(); MetaAccessProvider metaAccess = providers.getMetaAccess(); Plugins plugins = new Plugins(graphBuilderPlugins); GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(plugins); StructuredGraph calleeGraph = new StructuredGraph.Builder(invoke.getOptions(), invoke.getDebug()).method(method).build(); if (invoke.graph().trackNodeSourcePosition()) { calleeGraph.setTrackNodeSourcePosition(); } IntrinsicContext initialReplacementContext = new IntrinsicContext(method, method, providers.getReplacements().getDefaultReplacementBytecodeProvider(), INLINE_AFTER_PARSING); GraphBuilderPhase.Instance instance = createGraphBuilderInstance(metaAccess, providers.getStampProvider(), providers.getConstantReflection(), providers.getConstantFieldProvider(), config, OptimisticOptimizations.NONE, initialReplacementContext); instance.apply(calleeGraph); // Remove all frame states from inlinee calleeGraph.clearAllStateAfter(); new DeadCodeEliminationPhase(Optionality.Required).apply(calleeGraph); InliningUtil.inline(invoke, calleeGraph, false, method, reason, phase); } protected GraphBuilderPhase.Instance createGraphBuilderInstance(MetaAccessProvider metaAccess, StampProvider stampProvider, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext) { return new GraphBuilderPhase.Instance(metaAccess, stampProvider, constantReflection, constantFieldProvider, graphBuilderConfig, optimisticOpts, initialIntrinsicContext); } protected void pushStructure(Structure structure) { structures.add(structure); } protected <T extends Structure> T getTopStructure(Class<T> expectedClass) { return expectedClass.cast(structures.get(structures.size() - 1)); } protected void popStructure() { structures.remove(structures.size() - 1); } protected enum IfState { CONDITION, THEN_PART, ELSE_PART, FINISHED } static class IfStructure extends Structure { protected IfState state; protected FixedNode thenPart; protected FixedNode elsePart; }
Starts an if-block. This call can be followed by a call to thenPart to start emitting the code executed when the condition hold; and a call to elsePart to start emititng the code when the condition does not hold. It must be followed by a call to endIf to close the if-block.
Params:
  • condition – The condition for the if-block
  • trueProbability – The estimated probability the condition is true
Returns:the created IfNode.
/** * Starts an if-block. This call can be followed by a call to {@link #thenPart} to start * emitting the code executed when the condition hold; and a call to {@link #elsePart} to start * emititng the code when the condition does not hold. It must be followed by a call to * {@link #endIf} to close the if-block. * * @param condition The condition for the if-block * @param trueProbability The estimated probability the condition is true * @return the created {@link IfNode}. */
public IfNode startIf(LogicNode condition, double trueProbability) { AbstractBeginNode thenSuccessor = graph.add(new BeginNode()); AbstractBeginNode elseSuccessor = graph.add(new BeginNode()); IfNode node = append(new IfNode(condition, thenSuccessor, elseSuccessor, trueProbability)); lastFixedNode = null; IfStructure s = new IfStructure(); s.state = IfState.CONDITION; s.thenPart = thenSuccessor; s.elsePart = elseSuccessor; pushStructure(s); return node; } private IfStructure saveLastIfNode() { IfStructure s = getTopStructure(IfStructure.class); switch (s.state) { case CONDITION: assert lastFixedNode == null; break; case THEN_PART: s.thenPart = lastFixedNode; break; case ELSE_PART: s.elsePart = lastFixedNode; break; case FINISHED: assert false; break; } lastFixedNode = null; return s; } public void thenPart() { IfStructure s = saveLastIfNode(); lastFixedNode = (FixedWithNextNode) s.thenPart; s.state = IfState.THEN_PART; } public void elsePart() { IfStructure s = saveLastIfNode(); lastFixedNode = (FixedWithNextNode) s.elsePart; s.state = IfState.ELSE_PART; }
Ends an if block started with startIf(LogicNode, double).
Returns:the created merge node, or null if no merge node was required (for example, when one part ended with a control sink).
/** * Ends an if block started with {@link #startIf(LogicNode, double)}. * * @return the created merge node, or {@code null} if no merge node was required (for example, * when one part ended with a control sink). */
public AbstractMergeNode endIf() { IfStructure s = saveLastIfNode(); FixedWithNextNode thenPart = s.thenPart instanceof FixedWithNextNode ? (FixedWithNextNode) s.thenPart : null; FixedWithNextNode elsePart = s.elsePart instanceof FixedWithNextNode ? (FixedWithNextNode) s.elsePart : null; AbstractMergeNode merge = null; if (thenPart != null && elsePart != null) { /* Both parts are alive, we need a real merge. */ EndNode thenEnd = graph.add(new EndNode()); graph.addAfterFixed(thenPart, thenEnd); EndNode elseEnd = graph.add(new EndNode()); graph.addAfterFixed(elsePart, elseEnd); merge = graph.add(new MergeNode()); merge.addForwardEnd(thenEnd); merge.addForwardEnd(elseEnd); lastFixedNode = merge; } else if (thenPart != null) { /* elsePart ended with a control sink, so we can continue with thenPart. */ lastFixedNode = thenPart; } else if (elsePart != null) { /* thenPart ended with a control sink, so we can continue with elsePart. */ lastFixedNode = elsePart; } else { /* Both parts ended with a control sink, so no nodes can be added after the if. */ assert lastFixedNode == null; } s.state = IfState.FINISHED; popStructure(); return merge; } static class InvokeWithExceptionStructure extends Structure { protected enum State { INVOKE, NO_EXCEPTION_EDGE, EXCEPTION_EDGE, FINISHED } protected State state; protected ExceptionObjectNode exceptionObject; protected FixedNode noExceptionEdge; protected FixedNode exceptionEdge; } public InvokeWithExceptionNode startInvokeWithException(ResolvedJavaMethod method, InvokeKind invokeKind, FrameStateBuilder frameStateBuilder, int invokeBci, int exceptionEdgeBci, ValueNode... args) { assert method.isStatic() == (invokeKind == InvokeKind.Static); Signature signature = method.getSignature(); JavaType returnType = signature.getReturnType(null); assert checkArgs(method, args); StampPair returnStamp = graphBuilderPlugins.getOverridingStamp(this, returnType, false); if (returnStamp == null) { returnStamp = StampFactory.forDeclaredType(graph.getAssumptions(), returnType, false); } MethodCallTargetNode callTarget = graph.add(createMethodCallTarget(invokeKind, method, args, returnStamp, invokeBci)); return startInvokeWithException(callTarget, frameStateBuilder, invokeBci, exceptionEdgeBci); } public InvokeWithExceptionNode startInvokeWithException(MethodCallTargetNode callTarget, FrameStateBuilder frameStateBuilder, int invokeBci, int exceptionEdgeBci) { ExceptionObjectNode exceptionObject = add(new ExceptionObjectNode(getMetaAccess())); if (frameStateBuilder != null) { FrameStateBuilder exceptionState = frameStateBuilder.copy(); exceptionState.clearStack(); exceptionState.push(JavaKind.Object, exceptionObject); exceptionState.setRethrowException(false); exceptionObject.setStateAfter(exceptionState.create(exceptionEdgeBci, exceptionObject)); } InvokeWithExceptionNode invoke = append(new InvokeWithExceptionNode(callTarget, exceptionObject, invokeBci)); AbstractBeginNode noExceptionEdge = graph.add(KillingBeginNode.create(LocationIdentity.any())); invoke.setNext(noExceptionEdge); if (frameStateBuilder != null) { if (invoke.getStackKind() != JavaKind.Void) { frameStateBuilder.push(invoke.getStackKind(), invoke); } invoke.setStateAfter(frameStateBuilder.create(invokeBci, invoke)); if (invoke.getStackKind() != JavaKind.Void) { frameStateBuilder.pop(invoke.getStackKind()); } } lastFixedNode = null; InvokeWithExceptionStructure s = new InvokeWithExceptionStructure(); s.state = InvokeWithExceptionStructure.State.INVOKE; s.noExceptionEdge = noExceptionEdge; s.exceptionEdge = exceptionObject; s.exceptionObject = exceptionObject; pushStructure(s); return invoke; } private InvokeWithExceptionStructure saveLastInvokeWithExceptionNode() { InvokeWithExceptionStructure s = getTopStructure(InvokeWithExceptionStructure.class); switch (s.state) { case INVOKE: assert lastFixedNode == null; break; case NO_EXCEPTION_EDGE: s.noExceptionEdge = lastFixedNode; break; case EXCEPTION_EDGE: s.exceptionEdge = lastFixedNode; break; case FINISHED: assert false; break; } lastFixedNode = null; return s; } public void noExceptionPart() { InvokeWithExceptionStructure s = saveLastInvokeWithExceptionNode(); lastFixedNode = (FixedWithNextNode) s.noExceptionEdge; s.state = InvokeWithExceptionStructure.State.NO_EXCEPTION_EDGE; } public void exceptionPart() { InvokeWithExceptionStructure s = saveLastInvokeWithExceptionNode(); lastFixedNode = (FixedWithNextNode) s.exceptionEdge; s.state = InvokeWithExceptionStructure.State.EXCEPTION_EDGE; } public ExceptionObjectNode exceptionObject() { InvokeWithExceptionStructure s = getTopStructure(InvokeWithExceptionStructure.class); return s.exceptionObject; }
Finishes a control flow started with startInvokeWithException. If necessary, creates a merge of the non-exception and exception edges. The merge node is returned and the non-exception edge is the first forward end of the merge, the exception edge is the second forward end (relevant for phi nodes).
/** * Finishes a control flow started with {@link #startInvokeWithException}. If necessary, creates * a merge of the non-exception and exception edges. The merge node is returned and the * non-exception edge is the first forward end of the merge, the exception edge is the second * forward end (relevant for phi nodes). */
public AbstractMergeNode endInvokeWithException() { InvokeWithExceptionStructure s = saveLastInvokeWithExceptionNode(); FixedWithNextNode noExceptionEdge = s.noExceptionEdge instanceof FixedWithNextNode ? (FixedWithNextNode) s.noExceptionEdge : null; FixedWithNextNode exceptionEdge = s.exceptionEdge instanceof FixedWithNextNode ? (FixedWithNextNode) s.exceptionEdge : null; AbstractMergeNode merge = null; if (noExceptionEdge != null && exceptionEdge != null) { EndNode noExceptionEnd = graph.add(new EndNode()); graph.addAfterFixed(noExceptionEdge, noExceptionEnd); EndNode exceptionEnd = graph.add(new EndNode()); graph.addAfterFixed(exceptionEdge, exceptionEnd); merge = graph.add(new MergeNode()); merge.addForwardEnd(noExceptionEnd); merge.addForwardEnd(exceptionEnd); lastFixedNode = merge; } else if (noExceptionEdge != null) { lastFixedNode = noExceptionEdge; } else if (exceptionEdge != null) { lastFixedNode = exceptionEdge; } else { assert lastFixedNode == null; } s.state = InvokeWithExceptionStructure.State.FINISHED; popStructure(); return merge; } }