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