1 /*
2  * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 
25 package org.graalvm.compiler.replacements;
26 
27 import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE;
28 import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING;
29 
30 import java.lang.reflect.Method;
31 import java.lang.reflect.Modifier;
32 import java.util.ArrayList;
33 import java.util.List;
34 
35 import org.graalvm.compiler.core.common.CompilationIdentifier;
36 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
37 import org.graalvm.compiler.core.common.type.StampFactory;
38 import org.graalvm.compiler.core.common.type.StampPair;
39 import org.graalvm.compiler.debug.DebugCloseable;
40 import org.graalvm.compiler.debug.DebugContext;
41 import org.graalvm.compiler.debug.GraalError;
42 import org.graalvm.compiler.graph.Graph;
43 import org.graalvm.compiler.graph.Node.ValueNumberable;
44 import org.graalvm.compiler.graph.NodeSourcePosition;
45 import org.graalvm.compiler.java.FrameStateBuilder;
46 import org.graalvm.compiler.java.GraphBuilderPhase;
47 import org.graalvm.compiler.nodes.AbstractBeginNode;
48 import org.graalvm.compiler.nodes.AbstractMergeNode;
49 import org.graalvm.compiler.nodes.BeginNode;
50 import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
51 import org.graalvm.compiler.nodes.EndNode;
52 import org.graalvm.compiler.nodes.FixedNode;
53 import org.graalvm.compiler.nodes.FixedWithNextNode;
54 import org.graalvm.compiler.nodes.IfNode;
55 import org.graalvm.compiler.nodes.InvokeNode;
56 import org.graalvm.compiler.nodes.InvokeWithExceptionNode;
57 import org.graalvm.compiler.nodes.KillingBeginNode;
58 import org.graalvm.compiler.nodes.LogicNode;
59 import org.graalvm.compiler.nodes.MergeNode;
60 import org.graalvm.compiler.nodes.NodeView;
61 import org.graalvm.compiler.nodes.StructuredGraph;
62 import org.graalvm.compiler.nodes.UnwindNode;
63 import org.graalvm.compiler.nodes.ValueNode;
64 import org.graalvm.compiler.nodes.calc.FloatingNode;
65 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
66 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
67 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderTool;
68 import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext;
69 import org.graalvm.compiler.nodes.java.ExceptionObjectNode;
70 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
71 import org.graalvm.compiler.nodes.spi.Replacements;
72 import org.graalvm.compiler.nodes.spi.StampProvider;
73 import org.graalvm.compiler.nodes.type.StampTool;
74 import org.graalvm.compiler.phases.OptimisticOptimizations;
75 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
76 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase.Optionality;
77 import org.graalvm.compiler.phases.common.inlining.InliningUtil;
78 import org.graalvm.compiler.phases.util.Providers;
79 import org.graalvm.compiler.word.WordTypes;
80 import jdk.internal.vm.compiler.word.LocationIdentity;
81 
82 import jdk.vm.ci.code.BytecodeFrame;
83 import jdk.vm.ci.meta.ConstantReflectionProvider;
84 import jdk.vm.ci.meta.JavaKind;
85 import jdk.vm.ci.meta.JavaType;
86 import jdk.vm.ci.meta.MetaAccessProvider;
87 import jdk.vm.ci.meta.ResolvedJavaMethod;
88 import jdk.vm.ci.meta.ResolvedJavaType;
89 import jdk.vm.ci.meta.Signature;
90 
91 /**
92  * A utility for manually creating a graph. This will be expanded as necessary to support all
93  * subsystems that employ manual graph creation (as opposed to {@linkplain GraphBuilderPhase
94  * bytecode parsing} based graph creation).
95  */
96 public class GraphKit implements GraphBuilderTool {
97 
98     protected final Providers providers;
99     protected final StructuredGraph graph;
100     protected final WordTypes wordTypes;
101     protected final GraphBuilderConfiguration.Plugins graphBuilderPlugins;
102     protected FixedWithNextNode lastFixedNode;
103 
104     private final List<Structure> structures;
105 
106     protected abstract static class Structure {
107     }
108 
GraphKit(DebugContext debug, ResolvedJavaMethod stubMethod, Providers providers, WordTypes wordTypes, Plugins graphBuilderPlugins, CompilationIdentifier compilationId, String name)109     public GraphKit(DebugContext debug, ResolvedJavaMethod stubMethod, Providers providers, WordTypes wordTypes, Plugins graphBuilderPlugins, CompilationIdentifier compilationId, String name) {
110         this.providers = providers;
111         StructuredGraph.Builder builder = new StructuredGraph.Builder(debug.getOptions(), debug).compilationId(compilationId);
112         if (name != null) {
113             builder.name(name);
114         } else {
115             builder.method(stubMethod);
116         }
117         this.graph = builder.build();
118         graph.disableUnsafeAccessTracking();
119         if (graph.trackNodeSourcePosition()) {
120             // Set up a default value that everything constructed by GraphKit will use.
121             graph.withNodeSourcePosition(NodeSourcePosition.substitution(stubMethod));
122         }
123         this.wordTypes = wordTypes;
124         this.graphBuilderPlugins = graphBuilderPlugins;
125         this.lastFixedNode = graph.start();
126 
127         structures = new ArrayList<>();
128         /*
129          * Add a dummy element, so that the access of the last element never leads to an exception.
130          */
131         structures.add(new Structure() {
132         });
133     }
134 
135     @Override
getGraph()136     public StructuredGraph getGraph() {
137         return graph;
138     }
139 
140     @Override
getConstantReflection()141     public ConstantReflectionProvider getConstantReflection() {
142         return providers.getConstantReflection();
143     }
144 
145     @Override
getConstantFieldProvider()146     public ConstantFieldProvider getConstantFieldProvider() {
147         return providers.getConstantFieldProvider();
148     }
149 
150     @Override
getMetaAccess()151     public MetaAccessProvider getMetaAccess() {
152         return providers.getMetaAccess();
153     }
154 
155     @Override
getReplacements()156     public Replacements getReplacements() {
157         return providers.getReplacements();
158     }
159 
160     @Override
getStampProvider()161     public StampProvider getStampProvider() {
162         return providers.getStampProvider();
163     }
164 
165     @Override
parsingIntrinsic()166     public boolean parsingIntrinsic() {
167         return true;
168     }
169 
170     /**
171      * Ensures a floating node is added to or already present in the graph via {@link Graph#unique}.
172      *
173      * @return a node similar to {@code node} if one exists, otherwise {@code node}
174      */
unique(T node)175     public <T extends FloatingNode & ValueNumberable> T unique(T node) {
176         return graph.unique(changeToWord(node));
177     }
178 
add(T node)179     public <T extends ValueNode> T add(T node) {
180         return graph.add(changeToWord(node));
181     }
182 
changeToWord(T node)183     public <T extends ValueNode> T changeToWord(T node) {
184         if (wordTypes != null && wordTypes.isWord(node)) {
185             node.setStamp(wordTypes.getWordStamp(StampTool.typeOrNull(node)));
186         }
187         return node;
188     }
189 
190     @Override
append(T node)191     public <T extends ValueNode> T append(T node) {
192         T result = graph.addOrUniqueWithInputs(changeToWord(node));
193         if (result instanceof FixedNode) {
194             updateLastFixed((FixedNode) result);
195         }
196         return result;
197     }
198 
updateLastFixed(FixedNode result)199     private void updateLastFixed(FixedNode result) {
200         assert lastFixedNode != null;
201         assert result.predecessor() == null;
202         graph.addAfterFixed(lastFixedNode, result);
203         if (result instanceof FixedWithNextNode) {
204             lastFixedNode = (FixedWithNextNode) result;
205         } else {
206             lastFixedNode = null;
207         }
208     }
209 
createInvoke(Class<?> declaringClass, String name, ValueNode... args)210     public InvokeNode createInvoke(Class<?> declaringClass, String name, ValueNode... args) {
211         return createInvoke(declaringClass, name, InvokeKind.Static, null, BytecodeFrame.UNKNOWN_BCI, args);
212     }
213 
214     /**
215      * Creates and appends an {@link InvokeNode} for a call to a given method with a given set of
216      * arguments. The method is looked up via reflection based on the declaring class and name.
217      *
218      * @param declaringClass the class declaring the invoked method
219      * @param name the name of the invoked method
220      * @param args the arguments to the invocation
221      */
createInvoke(Class<?> declaringClass, String name, InvokeKind invokeKind, FrameStateBuilder frameStateBuilder, int bci, ValueNode... args)222     public InvokeNode createInvoke(Class<?> declaringClass, String name, InvokeKind invokeKind, FrameStateBuilder frameStateBuilder, int bci, ValueNode... args) {
223         boolean isStatic = invokeKind == InvokeKind.Static;
224         ResolvedJavaMethod method = findMethod(declaringClass, name, isStatic);
225         return createInvoke(method, invokeKind, frameStateBuilder, bci, args);
226     }
227 
findMethod(Class<?> declaringClass, String name, boolean isStatic)228     public ResolvedJavaMethod findMethod(Class<?> declaringClass, String name, boolean isStatic) {
229         ResolvedJavaType type = providers.getMetaAccess().lookupJavaType(declaringClass);
230         ResolvedJavaMethod method = null;
231         for (ResolvedJavaMethod m : type.getDeclaredMethods()) {
232             if (Modifier.isStatic(m.getModifiers()) == isStatic && m.getName().equals(name)) {
233                 assert method == null : "found more than one method in " + declaringClass + " named " + name;
234                 method = m;
235             }
236         }
237         GraalError.guarantee(method != null, "Could not find %s.%s (%s)", declaringClass, name, isStatic ? "static" : "non-static");
238         return method;
239     }
240 
findMethod(Class<?> declaringClass, String name, Class<?>... parameterTypes)241     public ResolvedJavaMethod findMethod(Class<?> declaringClass, String name, Class<?>... parameterTypes) {
242         try {
243             Method m = declaringClass.getDeclaredMethod(name, parameterTypes);
244             return providers.getMetaAccess().lookupJavaMethod(m);
245         } catch (NoSuchMethodException | SecurityException e) {
246             throw new AssertionError(e);
247         }
248     }
249 
250     /**
251      * Creates and appends an {@link InvokeNode} for a call to a given method with a given set of
252      * arguments.
253      */
254     @SuppressWarnings("try")
createInvoke(ResolvedJavaMethod method, InvokeKind invokeKind, FrameStateBuilder frameStateBuilder, int bci, ValueNode... args)255     public InvokeNode createInvoke(ResolvedJavaMethod method, InvokeKind invokeKind, FrameStateBuilder frameStateBuilder, int bci, ValueNode... args) {
256         try (DebugCloseable context = graph.withNodeSourcePosition(NodeSourcePosition.substitution(graph.currentNodeSourcePosition(), method))) {
257             assert method.isStatic() == (invokeKind == InvokeKind.Static);
258             Signature signature = method.getSignature();
259             JavaType returnType = signature.getReturnType(null);
260             assert checkArgs(method, args);
261             StampPair returnStamp = graphBuilderPlugins.getOverridingStamp(this, returnType, false);
262             if (returnStamp == null) {
263                 returnStamp = StampFactory.forDeclaredType(graph.getAssumptions(), returnType, false);
264             }
265             MethodCallTargetNode callTarget = graph.add(createMethodCallTarget(invokeKind, method, args, returnStamp, bci));
266             InvokeNode invoke = append(new InvokeNode(callTarget, bci));
267 
268             if (frameStateBuilder != null) {
269                 if (invoke.getStackKind() != JavaKind.Void) {
270                     frameStateBuilder.push(invoke.getStackKind(), invoke);
271                 }
272                 invoke.setStateAfter(frameStateBuilder.create(bci, invoke));
273                 if (invoke.getStackKind() != JavaKind.Void) {
274                     frameStateBuilder.pop(invoke.getStackKind());
275                 }
276             }
277             return invoke;
278         }
279     }
280 
281     @SuppressWarnings("try")
createInvokeWithExceptionAndUnwind(ResolvedJavaMethod method, InvokeKind invokeKind, FrameStateBuilder frameStateBuilder, int invokeBci, int exceptionEdgeBci, ValueNode... args)282     public InvokeWithExceptionNode createInvokeWithExceptionAndUnwind(ResolvedJavaMethod method, InvokeKind invokeKind,
283                     FrameStateBuilder frameStateBuilder, int invokeBci, int exceptionEdgeBci, ValueNode... args) {
284         try (DebugCloseable context = graph.withNodeSourcePosition(NodeSourcePosition.substitution(graph.currentNodeSourcePosition(), method))) {
285             InvokeWithExceptionNode result = startInvokeWithException(method, invokeKind, frameStateBuilder, invokeBci, exceptionEdgeBci, args);
286             exceptionPart();
287             ExceptionObjectNode exception = exceptionObject();
288             append(new UnwindNode(exception));
289             endInvokeWithException();
290             return result;
291         }
292     }
293 
294     @SuppressWarnings("try")
createInvokeWithExceptionAndUnwind(MethodCallTargetNode callTarget, FrameStateBuilder frameStateBuilder, int invokeBci, int exceptionEdgeBci)295     public InvokeWithExceptionNode createInvokeWithExceptionAndUnwind(MethodCallTargetNode callTarget, FrameStateBuilder frameStateBuilder, int invokeBci, int exceptionEdgeBci) {
296         try (DebugCloseable context = graph.withNodeSourcePosition(NodeSourcePosition.substitution(graph.currentNodeSourcePosition(), callTarget.targetMethod()))) {
297             InvokeWithExceptionNode result = startInvokeWithException(callTarget, frameStateBuilder, invokeBci, exceptionEdgeBci);
298             exceptionPart();
299             ExceptionObjectNode exception = exceptionObject();
300             append(new UnwindNode(exception));
301             endInvokeWithException();
302             return result;
303         }
304     }
305 
createMethodCallTarget(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args, StampPair returnStamp, @SuppressWarnings(R) int bci)306     protected MethodCallTargetNode createMethodCallTarget(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args, StampPair returnStamp, @SuppressWarnings("unused") int bci) {
307         return new MethodCallTargetNode(invokeKind, targetMethod, args, returnStamp, null);
308     }
309 
asKind(JavaType type)310     protected final JavaKind asKind(JavaType type) {
311         return wordTypes != null ? wordTypes.asKind(type) : type.getJavaKind();
312     }
313 
314     /**
315      * Determines if a given set of arguments is compatible with the signature of a given method.
316      *
317      * @return true if {@code args} are compatible with the signature if {@code method}
318      * @throws AssertionError if {@code args} are not compatible with the signature if
319      *             {@code method}
320      */
checkArgs(ResolvedJavaMethod method, ValueNode... args)321     public boolean checkArgs(ResolvedJavaMethod method, ValueNode... args) {
322         Signature signature = method.getSignature();
323         boolean isStatic = method.isStatic();
324         if (signature.getParameterCount(!isStatic) != args.length) {
325             throw new AssertionError(graph + ": wrong number of arguments to " + method);
326         }
327         int argIndex = 0;
328         if (!isStatic) {
329             JavaKind expected = asKind(method.getDeclaringClass());
330             JavaKind actual = args[argIndex++].stamp(NodeView.DEFAULT).getStackKind();
331             assert expected == actual : graph + ": wrong kind of value for receiver argument of call to " + method + " [" + actual + " != " + expected + "]";
332         }
333         for (int i = 0; i != signature.getParameterCount(false); i++) {
334             JavaKind expected = asKind(signature.getParameterType(i, method.getDeclaringClass())).getStackKind();
335             JavaKind actual = args[argIndex++].stamp(NodeView.DEFAULT).getStackKind();
336             if (expected != actual) {
337                 throw new AssertionError(graph + ": wrong kind of value for argument " + i + " of call to " + method + " [" + actual + " != " + expected + "]");
338             }
339         }
340         return true;
341     }
342 
343     /**
344      * Recursively {@linkplain #inline inlines} all invocations currently in the graph.
345      */
inlineInvokes(String reason, String phase)346     public void inlineInvokes(String reason, String phase) {
347         while (!graph.getNodes().filter(InvokeNode.class).isEmpty()) {
348             for (InvokeNode invoke : graph.getNodes().filter(InvokeNode.class).snapshot()) {
349                 inline(invoke, reason, phase);
350             }
351         }
352 
353         // Clean up all code that is now dead after inlining.
354         new DeadCodeEliminationPhase().apply(graph);
355     }
356 
357     /**
358      * Inlines a given invocation to a method. The graph of the inlined method is processed in the
359      * same manner as for snippets and method substitutions.
360      */
inline(InvokeNode invoke, String reason, String phase)361     public void inline(InvokeNode invoke, String reason, String phase) {
362         ResolvedJavaMethod method = ((MethodCallTargetNode) invoke.callTarget()).targetMethod();
363 
364         Plugins plugins = new Plugins(graphBuilderPlugins);
365         GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(plugins);
366 
367         StructuredGraph calleeGraph;
368         if (IS_IN_NATIVE_IMAGE) {
369             calleeGraph = providers.getReplacements().getSnippet(method, null, null, false, null, invoke.getOptions());
370         } else {
371             calleeGraph = new StructuredGraph.Builder(invoke.getOptions(), invoke.getDebug()).method(method).trackNodeSourcePosition(invoke.graph().trackNodeSourcePosition()).setIsSubstitution(
372                             true).build();
373             IntrinsicContext initialReplacementContext = new IntrinsicContext(method, method, providers.getReplacements().getDefaultReplacementBytecodeProvider(), INLINE_AFTER_PARSING);
374             GraphBuilderPhase.Instance instance = createGraphBuilderInstance(providers, config, OptimisticOptimizations.NONE, initialReplacementContext);
375             instance.apply(calleeGraph);
376         }
377 
378         // Remove all frame states from inlinee
379         calleeGraph.clearAllStateAfter();
380         new DeadCodeEliminationPhase(Optionality.Required).apply(calleeGraph);
381 
382         InliningUtil.inline(invoke, calleeGraph, false, method, reason, phase);
383     }
384 
createGraphBuilderInstance(Providers theProviders, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext)385     protected GraphBuilderPhase.Instance createGraphBuilderInstance(Providers theProviders, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts,
386                     IntrinsicContext initialIntrinsicContext) {
387         return new GraphBuilderPhase.Instance(theProviders, graphBuilderConfig, optimisticOpts, initialIntrinsicContext);
388     }
389 
pushStructure(Structure structure)390     protected void pushStructure(Structure structure) {
391         structures.add(structure);
392     }
393 
getTopStructure(Class<T> expectedClass)394     protected <T extends Structure> T getTopStructure(Class<T> expectedClass) {
395         return expectedClass.cast(structures.get(structures.size() - 1));
396     }
397 
popStructure()398     protected void popStructure() {
399         structures.remove(structures.size() - 1);
400     }
401 
402     protected enum IfState {
403         CONDITION,
404         THEN_PART,
405         ELSE_PART,
406         FINISHED
407     }
408 
409     static class IfStructure extends Structure {
410         protected IfState state;
411         protected FixedNode thenPart;
412         protected FixedNode elsePart;
413     }
414 
415     /**
416      * Starts an if-block. This call can be followed by a call to {@link #thenPart} to start
417      * emitting the code executed when the condition hold; and a call to {@link #elsePart} to start
418      * emititng the code when the condition does not hold. It must be followed by a call to
419      * {@link #endIf} to close the if-block.
420      *
421      * @param condition The condition for the if-block
422      * @param trueProbability The estimated probability the condition is true
423      * @return the created {@link IfNode}.
424      */
startIf(LogicNode condition, double trueProbability)425     public IfNode startIf(LogicNode condition, double trueProbability) {
426         AbstractBeginNode thenSuccessor = graph.add(new BeginNode());
427         AbstractBeginNode elseSuccessor = graph.add(new BeginNode());
428         IfNode node = append(new IfNode(condition, thenSuccessor, elseSuccessor, trueProbability));
429         lastFixedNode = null;
430 
431         IfStructure s = new IfStructure();
432         s.state = IfState.CONDITION;
433         s.thenPart = thenSuccessor;
434         s.elsePart = elseSuccessor;
435         pushStructure(s);
436         return node;
437     }
438 
saveLastIfNode()439     private IfStructure saveLastIfNode() {
440         IfStructure s = getTopStructure(IfStructure.class);
441         switch (s.state) {
442             case CONDITION:
443                 assert lastFixedNode == null;
444                 break;
445             case THEN_PART:
446                 s.thenPart = lastFixedNode;
447                 break;
448             case ELSE_PART:
449                 s.elsePart = lastFixedNode;
450                 break;
451             case FINISHED:
452                 assert false;
453                 break;
454         }
455         lastFixedNode = null;
456         return s;
457     }
458 
thenPart()459     public void thenPart() {
460         IfStructure s = saveLastIfNode();
461         lastFixedNode = (FixedWithNextNode) s.thenPart;
462         s.state = IfState.THEN_PART;
463     }
464 
elsePart()465     public void elsePart() {
466         IfStructure s = saveLastIfNode();
467         lastFixedNode = (FixedWithNextNode) s.elsePart;
468         s.state = IfState.ELSE_PART;
469     }
470 
471     /**
472      * Ends an if block started with {@link #startIf(LogicNode, double)}.
473      *
474      * @return the created merge node, or {@code null} if no merge node was required (for example,
475      *         when one part ended with a control sink).
476      */
endIf()477     public AbstractMergeNode endIf() {
478         IfStructure s = saveLastIfNode();
479 
480         FixedWithNextNode thenPart = s.thenPart instanceof FixedWithNextNode ? (FixedWithNextNode) s.thenPart : null;
481         FixedWithNextNode elsePart = s.elsePart instanceof FixedWithNextNode ? (FixedWithNextNode) s.elsePart : null;
482         AbstractMergeNode merge = null;
483 
484         if (thenPart != null && elsePart != null) {
485             /* Both parts are alive, we need a real merge. */
486             EndNode thenEnd = graph.add(new EndNode());
487             graph.addAfterFixed(thenPart, thenEnd);
488             EndNode elseEnd = graph.add(new EndNode());
489             graph.addAfterFixed(elsePart, elseEnd);
490 
491             merge = graph.add(new MergeNode());
492             merge.addForwardEnd(thenEnd);
493             merge.addForwardEnd(elseEnd);
494 
495             lastFixedNode = merge;
496 
497         } else if (thenPart != null) {
498             /* elsePart ended with a control sink, so we can continue with thenPart. */
499             lastFixedNode = thenPart;
500 
501         } else if (elsePart != null) {
502             /* thenPart ended with a control sink, so we can continue with elsePart. */
503             lastFixedNode = elsePart;
504 
505         } else {
506             /* Both parts ended with a control sink, so no nodes can be added after the if. */
507             assert lastFixedNode == null;
508         }
509         s.state = IfState.FINISHED;
510         popStructure();
511         return merge;
512     }
513 
514     static class InvokeWithExceptionStructure extends Structure {
515         protected enum State {
516             INVOKE,
517             NO_EXCEPTION_EDGE,
518             EXCEPTION_EDGE,
519             FINISHED
520         }
521 
522         protected State state;
523         protected ExceptionObjectNode exceptionObject;
524         protected FixedNode noExceptionEdge;
525         protected FixedNode exceptionEdge;
526     }
527 
startInvokeWithException(ResolvedJavaMethod method, InvokeKind invokeKind, FrameStateBuilder frameStateBuilder, int invokeBci, int exceptionEdgeBci, ValueNode... args)528     public InvokeWithExceptionNode startInvokeWithException(ResolvedJavaMethod method, InvokeKind invokeKind,
529                     FrameStateBuilder frameStateBuilder, int invokeBci, int exceptionEdgeBci, ValueNode... args) {
530 
531         assert method.isStatic() == (invokeKind == InvokeKind.Static);
532         Signature signature = method.getSignature();
533         JavaType returnType = signature.getReturnType(null);
534         assert checkArgs(method, args);
535         StampPair returnStamp = graphBuilderPlugins.getOverridingStamp(this, returnType, false);
536         if (returnStamp == null) {
537             returnStamp = StampFactory.forDeclaredType(graph.getAssumptions(), returnType, false);
538         }
539         MethodCallTargetNode callTarget = graph.add(createMethodCallTarget(invokeKind, method, args, returnStamp, invokeBci));
540         return startInvokeWithException(callTarget, frameStateBuilder, invokeBci, exceptionEdgeBci);
541     }
542 
startInvokeWithException(MethodCallTargetNode callTarget, FrameStateBuilder frameStateBuilder, int invokeBci, int exceptionEdgeBci)543     public InvokeWithExceptionNode startInvokeWithException(MethodCallTargetNode callTarget, FrameStateBuilder frameStateBuilder, int invokeBci, int exceptionEdgeBci) {
544         ExceptionObjectNode exceptionObject = add(new ExceptionObjectNode(getMetaAccess()));
545         if (frameStateBuilder != null) {
546             FrameStateBuilder exceptionState = frameStateBuilder.copy();
547             exceptionState.clearStack();
548             exceptionState.push(JavaKind.Object, exceptionObject);
549             exceptionState.setRethrowException(false);
550             exceptionObject.setStateAfter(exceptionState.create(exceptionEdgeBci, exceptionObject));
551         }
552         InvokeWithExceptionNode invoke = append(new InvokeWithExceptionNode(callTarget, exceptionObject, invokeBci));
553         AbstractBeginNode noExceptionEdge = graph.add(KillingBeginNode.create(LocationIdentity.any()));
554         invoke.setNext(noExceptionEdge);
555         if (frameStateBuilder != null) {
556             if (invoke.getStackKind() != JavaKind.Void) {
557                 frameStateBuilder.push(invoke.getStackKind(), invoke);
558             }
559             invoke.setStateAfter(frameStateBuilder.create(invokeBci, invoke));
560             if (invoke.getStackKind() != JavaKind.Void) {
561                 frameStateBuilder.pop(invoke.getStackKind());
562             }
563         }
564         lastFixedNode = null;
565 
566         InvokeWithExceptionStructure s = new InvokeWithExceptionStructure();
567         s.state = InvokeWithExceptionStructure.State.INVOKE;
568         s.noExceptionEdge = noExceptionEdge;
569         s.exceptionEdge = exceptionObject;
570         s.exceptionObject = exceptionObject;
571         pushStructure(s);
572 
573         return invoke;
574     }
575 
saveLastInvokeWithExceptionNode()576     private InvokeWithExceptionStructure saveLastInvokeWithExceptionNode() {
577         InvokeWithExceptionStructure s = getTopStructure(InvokeWithExceptionStructure.class);
578         switch (s.state) {
579             case INVOKE:
580                 assert lastFixedNode == null;
581                 break;
582             case NO_EXCEPTION_EDGE:
583                 s.noExceptionEdge = lastFixedNode;
584                 break;
585             case EXCEPTION_EDGE:
586                 s.exceptionEdge = lastFixedNode;
587                 break;
588             case FINISHED:
589                 assert false;
590                 break;
591         }
592         lastFixedNode = null;
593         return s;
594     }
595 
noExceptionPart()596     public void noExceptionPart() {
597         InvokeWithExceptionStructure s = saveLastInvokeWithExceptionNode();
598         lastFixedNode = (FixedWithNextNode) s.noExceptionEdge;
599         s.state = InvokeWithExceptionStructure.State.NO_EXCEPTION_EDGE;
600     }
601 
exceptionPart()602     public void exceptionPart() {
603         InvokeWithExceptionStructure s = saveLastInvokeWithExceptionNode();
604         lastFixedNode = (FixedWithNextNode) s.exceptionEdge;
605         s.state = InvokeWithExceptionStructure.State.EXCEPTION_EDGE;
606     }
607 
exceptionObject()608     public ExceptionObjectNode exceptionObject() {
609         InvokeWithExceptionStructure s = getTopStructure(InvokeWithExceptionStructure.class);
610         return s.exceptionObject;
611     }
612 
613     /**
614      * Finishes a control flow started with {@link #startInvokeWithException}. If necessary, creates
615      * a merge of the non-exception and exception edges. The merge node is returned and the
616      * non-exception edge is the first forward end of the merge, the exception edge is the second
617      * forward end (relevant for phi nodes).
618      */
endInvokeWithException()619     public AbstractMergeNode endInvokeWithException() {
620         InvokeWithExceptionStructure s = saveLastInvokeWithExceptionNode();
621         FixedWithNextNode noExceptionEdge = s.noExceptionEdge instanceof FixedWithNextNode ? (FixedWithNextNode) s.noExceptionEdge : null;
622         FixedWithNextNode exceptionEdge = s.exceptionEdge instanceof FixedWithNextNode ? (FixedWithNextNode) s.exceptionEdge : null;
623         AbstractMergeNode merge = null;
624         if (noExceptionEdge != null && exceptionEdge != null) {
625             EndNode noExceptionEnd = graph.add(new EndNode());
626             graph.addAfterFixed(noExceptionEdge, noExceptionEnd);
627             EndNode exceptionEnd = graph.add(new EndNode());
628             graph.addAfterFixed(exceptionEdge, exceptionEnd);
629             merge = graph.add(new MergeNode());
630             merge.addForwardEnd(noExceptionEnd);
631             merge.addForwardEnd(exceptionEnd);
632             lastFixedNode = merge;
633         } else if (noExceptionEdge != null) {
634             lastFixedNode = noExceptionEdge;
635         } else if (exceptionEdge != null) {
636             lastFixedNode = exceptionEdge;
637         } else {
638             assert lastFixedNode == null;
639         }
640         s.state = InvokeWithExceptionStructure.State.FINISHED;
641         popStructure();
642         return merge;
643     }
644 }
645