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