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 
 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
 136     public StructuredGraph getGraph() {
 137         return graph;
 138     }
 139 
 140     @Override
 141     public ConstantReflectionProvider getConstantReflection() {
 142         return providers.getConstantReflection();
 143     }
 144 
 145     @Override
 146     public ConstantFieldProvider getConstantFieldProvider() {
 147         return providers.getConstantFieldProvider();
 148     }
 149 
 150     @Override
 151     public MetaAccessProvider getMetaAccess() {
 152         return providers.getMetaAccess();
 153     }
 154 
 155     @Override
 156     public Replacements getReplacements() {
 157         return providers.getReplacements();
 158     }
 159 
 160     @Override
 161     public StampProvider getStampProvider() {
 162         return providers.getStampProvider();
 163     }
 164 
 165     @Override
 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      */
 175     public <T extends FloatingNode & ValueNumberable> T unique(T node) {
 176         return graph.unique(changeToWord(node));
 177     }
 178 
 179     public <T extends ValueNode> T add(T node) {
 180         return graph.add(changeToWord(node));
 181     }
 182 
 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
 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 
 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 
 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      */
 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 
 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 
 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")
 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")
 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")
 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 
 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 
 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      */
 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      */
 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      */
 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 
 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 
 390     protected void pushStructure(Structure structure) {
 391         structures.add(structure);
 392     }
 393 
 394     protected <T extends Structure> T getTopStructure(Class<T> expectedClass) {
 395         return expectedClass.cast(structures.get(structures.size() - 1));
 396     }
 397 
 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      */
 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 
 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 
 459     public void thenPart() {
 460         IfStructure s = saveLastIfNode();
 461         lastFixedNode = (FixedWithNextNode) s.thenPart;
 462         s.state = IfState.THEN_PART;
 463     }
 464 
 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      */
 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 
 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 
 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 
 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 
 596     public void noExceptionPart() {
 597         InvokeWithExceptionStructure s = saveLastInvokeWithExceptionNode();
 598         lastFixedNode = (FixedWithNextNode) s.noExceptionEdge;
 599         s.state = InvokeWithExceptionStructure.State.NO_EXCEPTION_EDGE;
 600     }
 601 
 602     public void exceptionPart() {
 603         InvokeWithExceptionStructure s = saveLastInvokeWithExceptionNode();
 604         lastFixedNode = (FixedWithNextNode) s.exceptionEdge;
 605         s.state = InvokeWithExceptionStructure.State.EXCEPTION_EDGE;
 606     }
 607 
 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      */
 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 }