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