1 /*
   2  * Copyright (c) 2015, 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.debug.GraalError.unimplemented;
  26 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED;
  27 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED;
  28 
  29 import java.util.ArrayList;
  30 import java.util.Arrays;
  31 import java.util.HashMap;
  32 import java.util.List;
  33 import java.util.Map;
  34 
  35 import org.graalvm.compiler.bytecode.Bytecode;
  36 import org.graalvm.compiler.bytecode.BytecodeProvider;
  37 import org.graalvm.compiler.core.common.PermanentBailoutException;
  38 import org.graalvm.compiler.core.common.cfg.CFGVerifier;
  39 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
  40 import org.graalvm.compiler.core.common.type.Stamp;
  41 import org.graalvm.compiler.core.common.type.StampFactory;
  42 import org.graalvm.compiler.core.common.type.StampPair;
  43 import org.graalvm.compiler.debug.Debug;
  44 import org.graalvm.compiler.debug.DebugCloseable;
  45 import org.graalvm.compiler.debug.GraalError;
  46 import org.graalvm.compiler.graph.Node;
  47 import org.graalvm.compiler.graph.NodeClass;
  48 import org.graalvm.compiler.graph.NodeSourcePosition;
  49 import org.graalvm.compiler.graph.spi.Canonicalizable;
  50 import org.graalvm.compiler.java.GraphBuilderPhase;
  51 import org.graalvm.compiler.nodeinfo.NodeInfo;
  52 import org.graalvm.compiler.nodes.AbstractBeginNode;
  53 import org.graalvm.compiler.nodes.AbstractMergeNode;
  54 import org.graalvm.compiler.nodes.CallTargetNode;
  55 import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
  56 import org.graalvm.compiler.nodes.ControlSinkNode;
  57 import org.graalvm.compiler.nodes.DeoptimizeNode;
  58 import org.graalvm.compiler.nodes.EncodedGraph;
  59 import org.graalvm.compiler.nodes.FixedNode;
  60 import org.graalvm.compiler.nodes.FixedWithNextNode;
  61 import org.graalvm.compiler.nodes.FrameState;
  62 import org.graalvm.compiler.nodes.IfNode;
  63 import org.graalvm.compiler.nodes.Invoke;
  64 import org.graalvm.compiler.nodes.InvokeWithExceptionNode;
  65 import org.graalvm.compiler.nodes.MergeNode;
  66 import org.graalvm.compiler.nodes.ParameterNode;
  67 import org.graalvm.compiler.nodes.ReturnNode;
  68 import org.graalvm.compiler.nodes.SimplifyingGraphDecoder;
  69 import org.graalvm.compiler.nodes.StateSplit;
  70 import org.graalvm.compiler.nodes.StructuredGraph;
  71 import org.graalvm.compiler.nodes.UnwindNode;
  72 import org.graalvm.compiler.nodes.ValueNode;
  73 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
  74 import org.graalvm.compiler.nodes.extended.ForeignCallNode;
  75 import org.graalvm.compiler.nodes.extended.IntegerSwitchNode;
  76 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
  77 import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin;
  78 import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo;
  79 import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext;
  80 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
  81 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
  82 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.InvocationPluginReceiver;
  83 import org.graalvm.compiler.nodes.graphbuilderconf.LoopExplosionPlugin;
  84 import org.graalvm.compiler.nodes.graphbuilderconf.LoopExplosionPlugin.LoopExplosionKind;
  85 import org.graalvm.compiler.nodes.graphbuilderconf.NodePlugin;
  86 import org.graalvm.compiler.nodes.graphbuilderconf.ParameterPlugin;
  87 import org.graalvm.compiler.nodes.java.LoadFieldNode;
  88 import org.graalvm.compiler.nodes.java.LoadIndexedNode;
  89 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
  90 import org.graalvm.compiler.nodes.java.MonitorIdNode;
  91 import org.graalvm.compiler.nodes.java.NewArrayNode;
  92 import org.graalvm.compiler.nodes.java.NewInstanceNode;
  93 import org.graalvm.compiler.nodes.java.NewMultiArrayNode;
  94 import org.graalvm.compiler.nodes.java.StoreFieldNode;
  95 import org.graalvm.compiler.nodes.java.StoreIndexedNode;
  96 import org.graalvm.compiler.nodes.spi.StampProvider;
  97 import org.graalvm.compiler.nodes.type.StampTool;
  98 import org.graalvm.compiler.nodes.util.GraphUtil;
  99 import org.graalvm.compiler.options.Option;
 100 import org.graalvm.compiler.options.OptionKey;
 101 import org.graalvm.compiler.options.OptionType;
 102 import org.graalvm.compiler.options.OptionValues;
 103 import org.graalvm.compiler.phases.common.inlining.InliningUtil;
 104 import org.graalvm.util.EconomicMap;
 105 import org.graalvm.util.Equivalence;
 106 
 107 import jdk.vm.ci.code.Architecture;
 108 import jdk.vm.ci.code.BailoutException;
 109 import jdk.vm.ci.code.BytecodeFrame;
 110 import jdk.vm.ci.meta.ConstantReflectionProvider;
 111 import jdk.vm.ci.meta.DeoptimizationAction;
 112 import jdk.vm.ci.meta.DeoptimizationReason;
 113 import jdk.vm.ci.meta.JavaConstant;
 114 import jdk.vm.ci.meta.JavaKind;
 115 import jdk.vm.ci.meta.JavaType;
 116 import jdk.vm.ci.meta.MetaAccessProvider;
 117 import jdk.vm.ci.meta.ResolvedJavaField;
 118 import jdk.vm.ci.meta.ResolvedJavaMethod;
 119 import jdk.vm.ci.meta.ResolvedJavaType;
 120 
 121 /**
 122  * A graph decoder that performs partial evaluation, i.e., that performs method inlining and
 123  * canonicalization/simplification of nodes during decoding.
 124  *
 125  * Inlining and loop explosion are configured via the plugin mechanism also used by the
 126  * {@link GraphBuilderPhase}. However, not all callback methods defined in
 127  * {@link GraphBuilderContext} are available since decoding is more limited than graph building.
 128  *
 129  * The standard {@link Canonicalizable#canonical node canonicalization} interface is used to
 130  * canonicalize nodes during decoding. Additionally, {@link IfNode branches} and
 131  * {@link IntegerSwitchNode switches} with constant conditions are simplified.
 132  */
 133 public abstract class PEGraphDecoder extends SimplifyingGraphDecoder {
 134 
 135     private static final Object CACHED_NULL_VALUE = new Object();
 136 
 137     public static class Options {
 138         @Option(help = "Maximum inlining depth during partial evaluation before reporting an infinite recursion")//
 139         public static final OptionKey<Integer> InliningDepthError = new OptionKey<>(1000);
 140 
 141         @Option(help = "Max number of loop explosions per method.", type = OptionType.Debug)//
 142         public static final OptionKey<Integer> MaximumLoopExplosionCount = new OptionKey<>(10000);
 143 
 144         @Option(help = "Do not bail out but throw an exception on failed loop explosion.", type = OptionType.Debug) //
 145         public static final OptionKey<Boolean> FailedLoopExplosionIsFatal = new OptionKey<>(false);
 146     }
 147 
 148     protected class PEMethodScope extends MethodScope {
 149         /** The state of the caller method. Only non-null during method inlining. */
 150         protected final PEMethodScope caller;
 151         protected final ResolvedJavaMethod method;
 152         protected final InvokeData invokeData;
 153         protected final int inliningDepth;
 154 
 155         protected final ValueNode[] arguments;
 156 
 157         protected FrameState outerState;
 158         protected FrameState exceptionState;
 159         protected ExceptionPlaceholderNode exceptionPlaceholderNode;
 160         protected NodeSourcePosition callerBytecodePosition;
 161 
 162         protected PEMethodScope(StructuredGraph targetGraph, PEMethodScope caller, LoopScope callerLoopScope, EncodedGraph encodedGraph, ResolvedJavaMethod method, InvokeData invokeData,
 163                         int inliningDepth, LoopExplosionPlugin loopExplosionPlugin, ValueNode[] arguments) {
 164             super(callerLoopScope, targetGraph, encodedGraph, loopExplosionKind(method, loopExplosionPlugin));
 165 
 166             this.caller = caller;
 167             this.method = method;
 168             this.invokeData = invokeData;
 169             this.inliningDepth = inliningDepth;
 170             this.arguments = arguments;
 171         }
 172 
 173         @Override
 174         public boolean isInlinedMethod() {
 175             return caller != null;
 176         }
 177 
 178         public NodeSourcePosition getCallerBytecodePosition() {
 179             if (caller == null) {
 180                 return null;
 181             }
 182             if (callerBytecodePosition == null) {
 183                 JavaConstant constantReceiver = caller.invokeData == null ? null : caller.invokeData.constantReceiver;
 184                 NodeSourcePosition callerPosition = caller.getCallerBytecodePosition();
 185                 NodeSourcePosition invokePosition = invokeData.invoke.asNode().getNodeSourcePosition();
 186                 callerBytecodePosition = invokePosition != null ? invokePosition.addCaller(constantReceiver, callerPosition) : callerPosition;
 187             }
 188             return callerBytecodePosition;
 189         }
 190     }
 191 
 192     protected class PENonAppendGraphBuilderContext implements GraphBuilderContext {
 193         protected final PEMethodScope methodScope;
 194         protected final Invoke invoke;
 195 
 196         public PENonAppendGraphBuilderContext(PEMethodScope methodScope, Invoke invoke) {
 197             this.methodScope = methodScope;
 198             this.invoke = invoke;
 199         }
 200 
 201         @Override
 202         public BailoutException bailout(String string) {
 203             BailoutException bailout = new PermanentBailoutException(string);
 204             throw GraphUtil.createBailoutException(string, bailout, GraphUtil.approxSourceStackTraceElement(methodScope.getCallerBytecodePosition()));
 205         }
 206 
 207         @Override
 208         public StampProvider getStampProvider() {
 209             return stampProvider;
 210         }
 211 
 212         @Override
 213         public MetaAccessProvider getMetaAccess() {
 214             return metaAccess;
 215         }
 216 
 217         @Override
 218         public ConstantReflectionProvider getConstantReflection() {
 219             return constantReflection;
 220         }
 221 
 222         @Override
 223         public ConstantFieldProvider getConstantFieldProvider() {
 224             return constantFieldProvider;
 225         }
 226 
 227         @Override
 228         public StructuredGraph getGraph() {
 229             return graph;
 230         }
 231 
 232         @Override
 233         public int getDepth() {
 234             return methodScope.inliningDepth;
 235         }
 236 
 237         @Override
 238         public IntrinsicContext getIntrinsic() {
 239             return null;
 240         }
 241 
 242         @Override
 243         public <T extends ValueNode> T append(T value) {
 244             throw unimplemented();
 245         }
 246 
 247         @Override
 248         public void push(JavaKind kind, ValueNode value) {
 249             throw unimplemented();
 250         }
 251 
 252         @Override
 253         public void handleReplacedInvoke(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args, boolean inlineEverything) {
 254             throw unimplemented();
 255         }
 256 
 257         @Override
 258         public void handleReplacedInvoke(CallTargetNode callTarget, JavaKind resultType) {
 259             throw unimplemented();
 260         }
 261 
 262         @Override
 263         public boolean intrinsify(BytecodeProvider bytecodeProvider, ResolvedJavaMethod targetMethod, ResolvedJavaMethod substitute, InvocationPlugin.Receiver receiver, ValueNode[] args) {
 264             return false;
 265         }
 266 
 267         @Override
 268         public void setStateAfter(StateSplit stateSplit) {
 269             throw unimplemented();
 270         }
 271 
 272         @Override
 273         public GraphBuilderContext getParent() {
 274             throw unimplemented();
 275         }
 276 
 277         @Override
 278         public Bytecode getCode() {
 279             throw unimplemented();
 280         }
 281 
 282         @Override
 283         public ResolvedJavaMethod getMethod() {
 284             throw unimplemented();
 285         }
 286 
 287         @Override
 288         public int bci() {
 289             return invoke.bci();
 290         }
 291 
 292         @Override
 293         public InvokeKind getInvokeKind() {
 294             throw unimplemented();
 295         }
 296 
 297         @Override
 298         public JavaType getInvokeReturnType() {
 299             throw unimplemented();
 300         }
 301     }
 302 
 303     protected class PEAppendGraphBuilderContext extends PENonAppendGraphBuilderContext {
 304         protected FixedWithNextNode lastInstr;
 305         protected ValueNode pushedNode;
 306         protected boolean invokeConsumed;
 307 
 308         public PEAppendGraphBuilderContext(PEMethodScope inlineScope, FixedWithNextNode lastInstr) {
 309             super(inlineScope, inlineScope.invokeData != null ? inlineScope.invokeData.invoke : null);
 310             this.lastInstr = lastInstr;
 311         }
 312 
 313         @Override
 314         public void push(JavaKind kind, ValueNode value) {
 315             if (pushedNode != null) {
 316                 throw unimplemented("Only one push is supported");
 317             }
 318             pushedNode = value;
 319         }
 320 
 321         @Override
 322         public void setStateAfter(StateSplit stateSplit) {
 323             Node stateAfter = decodeFloatingNode(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId);
 324             getGraph().add(stateAfter);
 325             FrameState fs = (FrameState) handleFloatingNodeAfterAdd(methodScope.caller, methodScope.callerLoopScope, stateAfter);
 326             stateSplit.setStateAfter(fs);
 327         }
 328 
 329         @SuppressWarnings("try")
 330         @Override
 331         public <T extends ValueNode> T append(T v) {
 332             if (v.graph() != null) {
 333                 return v;
 334             }
 335             try (DebugCloseable position = withNodeSoucePosition()) {
 336                 T added = getGraph().addOrUniqueWithInputs(v);
 337                 if (added == v) {
 338                     updateLastInstruction(v);
 339                 }
 340                 return added;
 341             }
 342         }
 343 
 344         private DebugCloseable withNodeSoucePosition() {
 345             if (getGraph().mayHaveNodeSourcePosition()) {
 346                 return getGraph().withNodeSourcePosition(methodScope.getCallerBytecodePosition());
 347             }
 348             return null;
 349         }
 350 
 351         private <T extends ValueNode> void updateLastInstruction(T v) {
 352             if (v instanceof FixedNode) {
 353                 FixedNode fixedNode = (FixedNode) v;
 354                 if (lastInstr != null) {
 355                     lastInstr.setNext(fixedNode);
 356                 }
 357                 if (fixedNode instanceof FixedWithNextNode) {
 358                     FixedWithNextNode fixedWithNextNode = (FixedWithNextNode) fixedNode;
 359                     assert fixedWithNextNode.next() == null : "cannot append instruction to instruction which isn't end";
 360                     lastInstr = fixedWithNextNode;
 361                 } else {
 362                     lastInstr = null;
 363                 }
 364             }
 365         }
 366 
 367         @Override
 368         public void handleReplacedInvoke(CallTargetNode callTarget, JavaKind resultType) {
 369             if (invokeConsumed) {
 370                 throw unimplemented("handleReplacedInvoke can be called only once");
 371             }
 372             invokeConsumed = true;
 373 
 374             appendInvoke(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData, callTarget);
 375             updateLastInstruction(invoke.asNode());
 376         }
 377     }
 378 
 379     @NodeInfo(cycles = CYCLES_IGNORED, size = SIZE_IGNORED)
 380     static class ExceptionPlaceholderNode extends ValueNode {
 381         public static final NodeClass<ExceptionPlaceholderNode> TYPE = NodeClass.create(ExceptionPlaceholderNode.class);
 382 
 383         protected ExceptionPlaceholderNode() {
 384             super(TYPE, StampFactory.object());
 385         }
 386     }
 387 
 388     protected static class SpecialCallTargetCacheKey {
 389         private final InvokeKind invokeKind;
 390         private final ResolvedJavaMethod targetMethod;
 391         private final ResolvedJavaType contextType;
 392         private final Stamp receiverStamp;
 393 
 394         public SpecialCallTargetCacheKey(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ResolvedJavaType contextType, Stamp receiverStamp) {
 395             this.invokeKind = invokeKind;
 396             this.targetMethod = targetMethod;
 397             this.contextType = contextType;
 398             this.receiverStamp = receiverStamp;
 399         }
 400 
 401         @Override
 402         public int hashCode() {
 403             return invokeKind.hashCode() ^ targetMethod.hashCode() ^ contextType.hashCode() ^ receiverStamp.hashCode();
 404         }
 405 
 406         @Override
 407         public boolean equals(Object obj) {
 408             if (obj instanceof SpecialCallTargetCacheKey) {
 409                 SpecialCallTargetCacheKey key = (SpecialCallTargetCacheKey) obj;
 410                 return key.invokeKind.equals(this.invokeKind) && key.targetMethod.equals(this.targetMethod) && key.contextType.equals(this.contextType) && key.receiverStamp.equals(this.receiverStamp);
 411             }
 412             return false;
 413         }
 414     }
 415 
 416     protected final OptionValues options;
 417     private final LoopExplosionPlugin loopExplosionPlugin;
 418     private final InvocationPlugins invocationPlugins;
 419     private final InlineInvokePlugin[] inlineInvokePlugins;
 420     private final ParameterPlugin parameterPlugin;
 421     private final NodePlugin[] nodePlugins;
 422     private final EconomicMap<SpecialCallTargetCacheKey, Object> specialCallTargetCache;
 423     private final EconomicMap<ResolvedJavaMethod, Object> invocationPluginCache;
 424 
 425     public PEGraphDecoder(Architecture architecture, StructuredGraph graph, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider,
 426                     StampProvider stampProvider, OptionValues options, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin[] inlineInvokePlugins,
 427                     ParameterPlugin parameterPlugin, NodePlugin[] nodePlugins) {
 428         super(architecture, graph, metaAccess, constantReflection, constantFieldProvider, stampProvider, true);
 429         this.loopExplosionPlugin = loopExplosionPlugin;
 430         this.invocationPlugins = invocationPlugins;
 431         this.inlineInvokePlugins = inlineInvokePlugins;
 432         this.parameterPlugin = parameterPlugin;
 433         this.options = options;
 434         this.nodePlugins = nodePlugins;
 435         this.specialCallTargetCache = EconomicMap.create(Equivalence.DEFAULT);
 436         this.invocationPluginCache = EconomicMap.create(Equivalence.DEFAULT);
 437     }
 438 
 439     protected static LoopExplosionKind loopExplosionKind(ResolvedJavaMethod method, LoopExplosionPlugin loopExplosionPlugin) {
 440         if (loopExplosionPlugin == null) {
 441             return LoopExplosionKind.NONE;
 442         } else {
 443             return loopExplosionPlugin.loopExplosionKind(method);
 444         }
 445     }
 446 
 447     public void decode(ResolvedJavaMethod method) {
 448         PEMethodScope methodScope = new PEMethodScope(graph, null, null, lookupEncodedGraph(method, null), method, null, 0, loopExplosionPlugin, null);
 449         decode(createInitialLoopScope(methodScope, null));
 450         cleanupGraph(methodScope);
 451 
 452         Debug.dump(Debug.VERBOSE_LEVEL, graph, "After graph cleanup");
 453         assert graph.verify();
 454 
 455         try {
 456             /* Check that the control flow graph can be computed, to catch problems early. */
 457             assert CFGVerifier.verify(ControlFlowGraph.compute(graph, true, true, true, true));
 458         } catch (Throwable ex) {
 459             throw GraalError.shouldNotReachHere("Control flow graph not valid after partial evaluation");
 460         }
 461     }
 462 
 463     @Override
 464     protected void cleanupGraph(MethodScope methodScope) {
 465         super.cleanupGraph(methodScope);
 466 
 467         for (FrameState frameState : graph.getNodes(FrameState.TYPE)) {
 468             if (frameState.bci == BytecodeFrame.UNWIND_BCI) {
 469                 /*
 470                  * handleMissingAfterExceptionFrameState is called during graph decoding from
 471                  * InliningUtil.processFrameState - but during graph decoding it does not do
 472                  * anything because the usages of the frameState are not available yet. So we need
 473                  * to call it again.
 474                  */
 475                 PEMethodScope peMethodScope = (PEMethodScope) methodScope;
 476                 Invoke invoke = peMethodScope.invokeData != null ? peMethodScope.invokeData.invoke : null;
 477                 InliningUtil.handleMissingAfterExceptionFrameState(frameState, invoke, null, true);
 478 
 479                 /*
 480                  * The frameState must be gone now, because it is not a valid deoptimization point.
 481                  */
 482                 assert frameState.isDeleted();
 483             }
 484         }
 485     }
 486 
 487     @Override
 488     protected void checkLoopExplosionIteration(MethodScope s, LoopScope loopScope) {
 489         PEMethodScope methodScope = (PEMethodScope) s;
 490 
 491         if (loopScope.loopIteration > Options.MaximumLoopExplosionCount.getValue(options)) {
 492             throw tooManyLoopExplosionIterations(methodScope, options);
 493         }
 494     }
 495 
 496     private static RuntimeException tooManyLoopExplosionIterations(PEMethodScope methodScope, OptionValues options) {
 497         String message = "too many loop explosion iterations - does the explosion not terminate for method " + methodScope.method + "?";
 498         RuntimeException bailout = Options.FailedLoopExplosionIsFatal.getValue(options) ? new RuntimeException(message) : new PermanentBailoutException(message);
 499         throw GraphUtil.createBailoutException(message, bailout, GraphUtil.approxSourceStackTraceElement(methodScope.getCallerBytecodePosition()));
 500     }
 501 
 502     @Override
 503     protected LoopScope handleInvoke(MethodScope s, LoopScope loopScope, InvokeData invokeData) {
 504         PEMethodScope methodScope = (PEMethodScope) s;
 505 
 506         /*
 507          * Decode the call target, but do not add it to the graph yet. This avoids adding usages for
 508          * all the arguments, which are expensive to remove again when we can inline the method.
 509          */
 510         assert invokeData.invoke.callTarget() == null : "callTarget edge is ignored during decoding of Invoke";
 511         CallTargetNode callTarget = (CallTargetNode) decodeFloatingNode(methodScope, loopScope, invokeData.callTargetOrderId);
 512         if (callTarget instanceof MethodCallTargetNode) {
 513             MethodCallTargetNode methodCall = (MethodCallTargetNode) callTarget;
 514             if (methodCall.invokeKind().hasReceiver()) {
 515                 invokeData.constantReceiver = methodCall.arguments().get(0).asJavaConstant();
 516             }
 517             LoopScope inlineLoopScope = trySimplifyInvoke(methodScope, loopScope, invokeData, (MethodCallTargetNode) callTarget);
 518             if (inlineLoopScope != null) {
 519                 return inlineLoopScope;
 520             }
 521         }
 522 
 523         /* We know that we need an invoke, so now we can add the call target to the graph. */
 524         graph.add(callTarget);
 525         registerNode(loopScope, invokeData.callTargetOrderId, callTarget, false, false);
 526         return super.handleInvoke(methodScope, loopScope, invokeData);
 527     }
 528 
 529     protected LoopScope trySimplifyInvoke(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) {
 530         // attempt to devirtualize the call
 531         ResolvedJavaMethod specialCallTarget = getSpecialCallTarget(invokeData, callTarget);
 532         if (specialCallTarget != null) {
 533             callTarget.setTargetMethod(specialCallTarget);
 534             callTarget.setInvokeKind(InvokeKind.Special);
 535         }
 536 
 537         if (tryInvocationPlugin(methodScope, loopScope, invokeData, callTarget)) {
 538             /*
 539              * The invocation plugin handled the call, so decoding continues in the calling method.
 540              */
 541             return loopScope;
 542         }
 543         LoopScope inlineLoopScope = tryInline(methodScope, loopScope, invokeData, callTarget);
 544         if (inlineLoopScope != null) {
 545             /*
 546              * We can inline the call, so decoding continues in the inlined method.
 547              */
 548             return inlineLoopScope;
 549         }
 550 
 551         for (InlineInvokePlugin plugin : inlineInvokePlugins) {
 552             plugin.notifyNotInlined(new PENonAppendGraphBuilderContext(methodScope, invokeData.invoke), callTarget.targetMethod(), invokeData.invoke);
 553         }
 554         return null;
 555     }
 556 
 557     private ResolvedJavaMethod getSpecialCallTarget(InvokeData invokeData, MethodCallTargetNode callTarget) {
 558         if (callTarget.invokeKind().isDirect()) {
 559             return null;
 560         }
 561 
 562         // check for trivial cases (e.g. final methods, nonvirtual methods)
 563         if (callTarget.targetMethod().canBeStaticallyBound()) {
 564             return callTarget.targetMethod();
 565         }
 566 
 567         SpecialCallTargetCacheKey key = new SpecialCallTargetCacheKey(callTarget.invokeKind(), callTarget.targetMethod(), invokeData.contextType, callTarget.receiver().stamp());
 568         Object specialCallTarget = specialCallTargetCache.get(key);
 569         if (specialCallTarget == null) {
 570             specialCallTarget = MethodCallTargetNode.devirtualizeCall(key.invokeKind, key.targetMethod, key.contextType, graph.getAssumptions(),
 571                             key.receiverStamp);
 572             if (specialCallTarget == null) {
 573                 specialCallTarget = CACHED_NULL_VALUE;
 574             }
 575             specialCallTargetCache.put(key, specialCallTarget);
 576         }
 577 
 578         return specialCallTarget == CACHED_NULL_VALUE ? null : (ResolvedJavaMethod) specialCallTarget;
 579     }
 580 
 581     protected boolean tryInvocationPlugin(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) {
 582         if (invocationPlugins == null || invocationPlugins.isEmpty()) {
 583             return false;
 584         }
 585 
 586         Invoke invoke = invokeData.invoke;
 587 
 588         ResolvedJavaMethod targetMethod = callTarget.targetMethod();
 589         InvocationPlugin invocationPlugin = getInvocationPlugin(targetMethod);
 590         if (invocationPlugin == null) {
 591             return false;
 592         }
 593 
 594         ValueNode[] arguments = callTarget.arguments().toArray(new ValueNode[0]);
 595         FixedWithNextNode invokePredecessor = (FixedWithNextNode) invoke.asNode().predecessor();
 596 
 597         /*
 598          * Remove invoke from graph so that invocation plugin can append nodes to the predecessor.
 599          */
 600         invoke.asNode().replaceAtPredecessor(null);
 601 
 602         PEMethodScope inlineScope = new PEMethodScope(graph, methodScope, loopScope, null, targetMethod, invokeData, methodScope.inliningDepth + 1, loopExplosionPlugin, arguments);
 603         PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(inlineScope, invokePredecessor);
 604         InvocationPluginReceiver invocationPluginReceiver = new InvocationPluginReceiver(graphBuilderContext);
 605 
 606         if (invocationPlugin.execute(graphBuilderContext, targetMethod, invocationPluginReceiver.init(targetMethod, arguments), arguments)) {
 607 
 608             if (graphBuilderContext.invokeConsumed) {
 609                 /* Nothing to do. */
 610             } else if (graphBuilderContext.lastInstr != null) {
 611                 registerNode(loopScope, invokeData.invokeOrderId, graphBuilderContext.pushedNode, true, true);
 612                 invoke.asNode().replaceAtUsages(graphBuilderContext.pushedNode);
 613                 graphBuilderContext.lastInstr.setNext(nodeAfterInvoke(methodScope, loopScope, invokeData, AbstractBeginNode.prevBegin(graphBuilderContext.lastInstr)));
 614                 deleteInvoke(invoke);
 615             } else {
 616                 assert graphBuilderContext.pushedNode == null : "Why push a node when the invoke does not return anyway?";
 617                 invoke.asNode().replaceAtUsages(null);
 618                 deleteInvoke(invoke);
 619             }
 620             return true;
 621 
 622         } else {
 623             /* Intrinsification failed, restore original state: invoke is in Graph. */
 624             invokePredecessor.setNext(invoke.asNode());
 625             return false;
 626         }
 627     }
 628 
 629     private InvocationPlugin getInvocationPlugin(ResolvedJavaMethod targetMethod) {
 630         Object invocationPlugin = invocationPluginCache.get(targetMethod);
 631         if (invocationPlugin == null) {
 632             invocationPlugin = invocationPlugins.lookupInvocation(targetMethod);
 633             if (invocationPlugin == null) {
 634                 invocationPlugin = CACHED_NULL_VALUE;
 635             }
 636             invocationPluginCache.put(targetMethod, invocationPlugin);
 637         }
 638 
 639         return invocationPlugin == CACHED_NULL_VALUE ? null : (InvocationPlugin) invocationPlugin;
 640     }
 641 
 642     protected LoopScope tryInline(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) {
 643         if (!callTarget.invokeKind().isDirect()) {
 644             return null;
 645         }
 646 
 647         ResolvedJavaMethod targetMethod = callTarget.targetMethod();
 648         if (targetMethod.hasNeverInlineDirective()) {
 649             return null;
 650         }
 651 
 652         ValueNode[] arguments = callTarget.arguments().toArray(new ValueNode[0]);
 653         GraphBuilderContext graphBuilderContext = new PENonAppendGraphBuilderContext(methodScope, invokeData.invoke);
 654 
 655         for (InlineInvokePlugin plugin : inlineInvokePlugins) {
 656             InlineInfo inlineInfo = plugin.shouldInlineInvoke(graphBuilderContext, targetMethod, arguments);
 657             if (inlineInfo != null) {
 658                 if (inlineInfo.getMethodToInline() == null) {
 659                     return null;
 660                 } else {
 661                     return doInline(methodScope, loopScope, invokeData, inlineInfo, arguments);
 662                 }
 663             }
 664         }
 665         return null;
 666     }
 667 
 668     protected LoopScope doInline(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, InlineInfo inlineInfo, ValueNode[] arguments) {
 669         ResolvedJavaMethod inlineMethod = inlineInfo.getMethodToInline();
 670         EncodedGraph graphToInline = lookupEncodedGraph(inlineMethod, inlineInfo.getIntrinsicBytecodeProvider());
 671         if (graphToInline == null) {
 672             return null;
 673         }
 674 
 675         if (methodScope.inliningDepth > Options.InliningDepthError.getValue(options)) {
 676             throw tooDeepInlining(methodScope);
 677         }
 678 
 679         for (InlineInvokePlugin plugin : inlineInvokePlugins) {
 680             plugin.notifyBeforeInline(inlineMethod);
 681         }
 682 
 683         Invoke invoke = invokeData.invoke;
 684         FixedNode invokeNode = invoke.asNode();
 685         FixedWithNextNode predecessor = (FixedWithNextNode) invokeNode.predecessor();
 686         invokeNode.replaceAtPredecessor(null);
 687 
 688         PEMethodScope inlineScope = new PEMethodScope(graph, methodScope, loopScope, graphToInline, inlineMethod, invokeData, methodScope.inliningDepth + 1,
 689                         loopExplosionPlugin, arguments);
 690 
 691         if (!inlineMethod.isStatic()) {
 692             if (StampTool.isPointerAlwaysNull(arguments[0])) {
 693                 /*
 694                  * The receiver is null, so we can unconditionally throw a NullPointerException
 695                  * instead of performing any inlining.
 696                  */
 697                 DeoptimizeNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.NullCheckException));
 698                 predecessor.setNext(deoptimizeNode);
 699                 finishInlining(inlineScope);
 700                 /* Continue decoding in the caller. */
 701                 return loopScope;
 702 
 703             } else if (!StampTool.isPointerNonNull(arguments[0])) {
 704                 /* The receiver might be null, so we need to insert a null check. */
 705                 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(inlineScope, predecessor);
 706                 arguments[0] = graphBuilderContext.nullCheckedValue(arguments[0]);
 707                 predecessor = graphBuilderContext.lastInstr;
 708             }
 709         }
 710 
 711         /*
 712          * Do the actual inlining by returning the initial loop scope for the inlined method scope.
 713          */
 714         return createInitialLoopScope(inlineScope, predecessor);
 715     }
 716 
 717     @Override
 718     protected void finishInlining(MethodScope is) {
 719         PEMethodScope inlineScope = (PEMethodScope) is;
 720         ResolvedJavaMethod inlineMethod = inlineScope.method;
 721         PEMethodScope methodScope = inlineScope.caller;
 722         LoopScope loopScope = inlineScope.callerLoopScope;
 723         InvokeData invokeData = inlineScope.invokeData;
 724         Invoke invoke = invokeData.invoke;
 725         FixedNode invokeNode = invoke.asNode();
 726 
 727         ValueNode exceptionValue = null;
 728         int returnNodeCount = 0;
 729         int unwindNodeCount = 0;
 730         List<ControlSinkNode> returnAndUnwindNodes = inlineScope.returnAndUnwindNodes;
 731         for (int i = 0; i < returnAndUnwindNodes.size(); i++) {
 732             FixedNode fixedNode = returnAndUnwindNodes.get(i);
 733             if (fixedNode instanceof ReturnNode) {
 734                 returnNodeCount++;
 735             } else if (fixedNode.isAlive()) {
 736                 assert fixedNode instanceof UnwindNode;
 737                 unwindNodeCount++;
 738             }
 739         }
 740 
 741         if (unwindNodeCount > 0) {
 742             FixedNode unwindReplacement;
 743             if (invoke instanceof InvokeWithExceptionNode) {
 744                 /* Decoding continues for the exception handler. */
 745                 unwindReplacement = makeStubNode(methodScope, loopScope, invokeData.exceptionNextOrderId);
 746             } else {
 747                 /* No exception handler available, so the only thing we can do is deoptimize. */
 748                 unwindReplacement = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler));
 749             }
 750 
 751             if (unwindNodeCount == 1) {
 752                 /* Only one UnwindNode, we can use the exception directly. */
 753                 UnwindNode unwindNode = getSingleMatchingNode(returnAndUnwindNodes, returnNodeCount > 0, UnwindNode.class);
 754                 exceptionValue = unwindNode.exception();
 755                 unwindNode.replaceAndDelete(unwindReplacement);
 756 
 757             } else {
 758                 /*
 759                  * More than one UnwindNode. This can happen with the loop explosion strategy
 760                  * FULL_EXPLODE_UNTIL_RETURN, where we keep exploding after the loop and therefore
 761                  * also explode exception paths. Merge the exception in a similar way as multiple
 762                  * return values.
 763                  */
 764                 MergeNode unwindMergeNode = graph.add(new MergeNode());
 765                 exceptionValue = InliningUtil.mergeValueProducers(unwindMergeNode, getMatchingNodes(returnAndUnwindNodes, returnNodeCount > 0, UnwindNode.class, unwindNodeCount),
 766                                 null, unwindNode -> unwindNode.exception());
 767                 unwindMergeNode.setNext(unwindReplacement);
 768 
 769                 ensureExceptionStateDecoded(inlineScope);
 770                 unwindMergeNode.setStateAfter(inlineScope.exceptionState.duplicateModified(JavaKind.Object, JavaKind.Object, exceptionValue));
 771             }
 772         }
 773 
 774         assert invoke.next() == null;
 775         assert !(invoke instanceof InvokeWithExceptionNode) || ((InvokeWithExceptionNode) invoke).exceptionEdge() == null;
 776 
 777         ValueNode returnValue;
 778         if (returnNodeCount == 0) {
 779             returnValue = null;
 780         } else if (returnNodeCount == 1) {
 781             ReturnNode returnNode = getSingleMatchingNode(returnAndUnwindNodes, unwindNodeCount > 0, ReturnNode.class);
 782             returnValue = returnNode.result();
 783             FixedNode n = nodeAfterInvoke(methodScope, loopScope, invokeData, AbstractBeginNode.prevBegin(returnNode));
 784             returnNode.replaceAndDelete(n);
 785         } else {
 786             AbstractMergeNode merge = graph.add(new MergeNode());
 787             merge.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope, invokeData.stateAfterOrderId));
 788             returnValue = InliningUtil.mergeReturns(merge, getMatchingNodes(returnAndUnwindNodes, unwindNodeCount > 0, ReturnNode.class, returnNodeCount));
 789             FixedNode n = nodeAfterInvoke(methodScope, loopScope, invokeData, merge);
 790             merge.setNext(n);
 791         }
 792         invokeNode.replaceAtUsages(returnValue);
 793 
 794         /*
 795          * Usage the handles that we have on the return value and the exception to update the
 796          * orderId->Node table.
 797          */
 798         registerNode(loopScope, invokeData.invokeOrderId, returnValue, true, true);
 799         if (invoke instanceof InvokeWithExceptionNode) {
 800             registerNode(loopScope, invokeData.exceptionOrderId, exceptionValue, true, true);
 801         }
 802         if (inlineScope.exceptionPlaceholderNode != null) {
 803             inlineScope.exceptionPlaceholderNode.replaceAtUsagesAndDelete(exceptionValue);
 804         }
 805         deleteInvoke(invoke);
 806 
 807         for (InlineInvokePlugin plugin : inlineInvokePlugins) {
 808             plugin.notifyAfterInline(inlineMethod);
 809         }
 810     }
 811 
 812     @SuppressWarnings("unchecked")
 813     private static <T> T getSingleMatchingNode(List<ControlSinkNode> returnAndUnwindNodes, boolean hasNonMatchingEntries, Class<T> clazz) {
 814         if (!hasNonMatchingEntries) {
 815             assert returnAndUnwindNodes.size() == 1;
 816             return (T) returnAndUnwindNodes.get(0);
 817         }
 818 
 819         for (int i = 0; i < returnAndUnwindNodes.size(); i++) {
 820             ControlSinkNode node = returnAndUnwindNodes.get(i);
 821             if (clazz.isInstance(node)) {
 822                 return (T) node;
 823             }
 824         }
 825         throw GraalError.shouldNotReachHere();
 826     }
 827 
 828     @SuppressWarnings("unchecked")
 829     private static <T> List<T> getMatchingNodes(List<ControlSinkNode> returnAndUnwindNodes, boolean hasNonMatchingEntries, Class<T> clazz, int resultCount) {
 830         if (!hasNonMatchingEntries) {
 831             return (List<T>) returnAndUnwindNodes;
 832         }
 833 
 834         List<T> result = new ArrayList<>(resultCount);
 835         for (int i = 0; i < returnAndUnwindNodes.size(); i++) {
 836             ControlSinkNode node = returnAndUnwindNodes.get(i);
 837             if (clazz.isInstance(node)) {
 838                 result.add((T) node);
 839             }
 840         }
 841         assert result.size() == resultCount;
 842         return result;
 843     }
 844 
 845     private static RuntimeException tooDeepInlining(PEMethodScope methodScope) {
 846         HashMap<ResolvedJavaMethod, Integer> methodCounts = new HashMap<>();
 847         for (PEMethodScope cur = methodScope; cur != null; cur = cur.caller) {
 848             Integer oldCount = methodCounts.get(cur.method);
 849             methodCounts.put(cur.method, oldCount == null ? 1 : oldCount + 1);
 850         }
 851 
 852         List<Map.Entry<ResolvedJavaMethod, Integer>> methods = new ArrayList<>(methodCounts.entrySet());
 853         methods.sort((e1, e2) -> -Integer.compare(e1.getValue(), e2.getValue()));
 854 
 855         StringBuilder msg = new StringBuilder("Too deep inlining, probably caused by recursive inlining.").append(System.lineSeparator()).append("== Inlined methods ordered by inlining frequency:");
 856         for (Map.Entry<ResolvedJavaMethod, Integer> entry : methods) {
 857             msg.append(System.lineSeparator()).append(entry.getKey().format("%H.%n(%p) [")).append(entry.getValue()).append("]");
 858         }
 859         msg.append(System.lineSeparator()).append("== Complete stack trace of inlined methods:");
 860         int lastBci = 0;
 861         for (PEMethodScope cur = methodScope; cur != null; cur = cur.caller) {
 862             msg.append(System.lineSeparator()).append(cur.method.asStackTraceElement(lastBci));
 863             if (cur.invokeData != null) {
 864                 lastBci = cur.invokeData.invoke.bci();
 865             } else {
 866                 lastBci = 0;
 867             }
 868         }
 869 
 870         throw new PermanentBailoutException(msg.toString());
 871     }
 872 
 873     public FixedNode nodeAfterInvoke(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, AbstractBeginNode lastBlock) {
 874         assert lastBlock.isAlive();
 875         FixedNode n;
 876         if (invokeData.invoke instanceof InvokeWithExceptionNode) {
 877             registerNode(loopScope, invokeData.nextOrderId, lastBlock, false, false);
 878             n = makeStubNode(methodScope, loopScope, invokeData.nextNextOrderId);
 879         } else {
 880             n = makeStubNode(methodScope, loopScope, invokeData.nextOrderId);
 881         }
 882         return n;
 883     }
 884 
 885     private static void deleteInvoke(Invoke invoke) {
 886         /*
 887          * Clean up unused nodes. We cannot just call killCFG on the invoke node because that can
 888          * kill too much: nodes that are decoded later can use values that appear unused by now.
 889          */
 890         FrameState frameState = invoke.stateAfter();
 891         invoke.asNode().safeDelete();
 892         assert invoke.callTarget() == null : "must not have been added to the graph yet";
 893         if (frameState != null && frameState.hasNoUsages()) {
 894             frameState.safeDelete();
 895         }
 896     }
 897 
 898     protected abstract EncodedGraph lookupEncodedGraph(ResolvedJavaMethod method, BytecodeProvider intrinsicBytecodeProvider);
 899 
 900     @Override
 901     protected void handleFixedNode(MethodScope s, LoopScope loopScope, int nodeOrderId, FixedNode node) {
 902         PEMethodScope methodScope = (PEMethodScope) s;
 903 
 904         if (node instanceof ForeignCallNode) {
 905             ForeignCallNode foreignCall = (ForeignCallNode) node;
 906             if (foreignCall.getBci() == BytecodeFrame.UNKNOWN_BCI && methodScope.invokeData != null) {
 907                 foreignCall.setBci(methodScope.invokeData.invoke.bci());
 908             }
 909         }
 910 
 911         super.handleFixedNode(methodScope, loopScope, nodeOrderId, node);
 912     }
 913 
 914     @SuppressWarnings("try")
 915     @Override
 916     protected Node canonicalizeFixedNode(MethodScope s, Node node) {
 917         PEMethodScope methodScope = (PEMethodScope) s;
 918 
 919         Node replacedNode = node;
 920         if (nodePlugins != null && nodePlugins.length > 0) {
 921             if (node instanceof LoadFieldNode) {
 922                 LoadFieldNode loadFieldNode = (LoadFieldNode) node;
 923                 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, loadFieldNode);
 924                 ResolvedJavaField field = loadFieldNode.field();
 925                 if (loadFieldNode.isStatic()) {
 926                     for (NodePlugin nodePlugin : nodePlugins) {
 927                         if (nodePlugin.handleLoadStaticField(graphBuilderContext, field)) {
 928                             replacedNode = graphBuilderContext.pushedNode;
 929                             break;
 930                         }
 931                     }
 932                 } else {
 933                     ValueNode object = loadFieldNode.object();
 934                     for (NodePlugin nodePlugin : nodePlugins) {
 935                         if (nodePlugin.handleLoadField(graphBuilderContext, object, field)) {
 936                             replacedNode = graphBuilderContext.pushedNode;
 937                             break;
 938                         }
 939                     }
 940                 }
 941             } else if (node instanceof StoreFieldNode) {
 942                 StoreFieldNode storeFieldNode = (StoreFieldNode) node;
 943                 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, storeFieldNode);
 944                 ResolvedJavaField field = storeFieldNode.field();
 945                 if (storeFieldNode.isStatic()) {
 946                     ValueNode value = storeFieldNode.value();
 947                     for (NodePlugin nodePlugin : nodePlugins) {
 948                         if (nodePlugin.handleStoreStaticField(graphBuilderContext, field, value)) {
 949                             replacedNode = graphBuilderContext.pushedNode;
 950                             break;
 951                         }
 952                     }
 953                 } else {
 954                     ValueNode object = storeFieldNode.object();
 955                     ValueNode value = storeFieldNode.value();
 956                     for (NodePlugin nodePlugin : nodePlugins) {
 957                         if (nodePlugin.handleStoreField(graphBuilderContext, object, field, value)) {
 958                             replacedNode = graphBuilderContext.pushedNode;
 959                             break;
 960                         }
 961                     }
 962                 }
 963             } else if (node instanceof LoadIndexedNode) {
 964                 LoadIndexedNode loadIndexedNode = (LoadIndexedNode) node;
 965                 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, loadIndexedNode);
 966                 ValueNode array = loadIndexedNode.array();
 967                 ValueNode index = loadIndexedNode.index();
 968                 for (NodePlugin nodePlugin : nodePlugins) {
 969                     if (nodePlugin.handleLoadIndexed(graphBuilderContext, array, index, loadIndexedNode.elementKind())) {
 970                         replacedNode = graphBuilderContext.pushedNode;
 971                         break;
 972                     }
 973                 }
 974             } else if (node instanceof StoreIndexedNode) {
 975                 StoreIndexedNode storeIndexedNode = (StoreIndexedNode) node;
 976                 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, storeIndexedNode);
 977                 ValueNode array = storeIndexedNode.array();
 978                 ValueNode index = storeIndexedNode.index();
 979                 ValueNode value = storeIndexedNode.value();
 980                 for (NodePlugin nodePlugin : nodePlugins) {
 981                     if (nodePlugin.handleStoreIndexed(graphBuilderContext, array, index, storeIndexedNode.elementKind(), value)) {
 982                         replacedNode = graphBuilderContext.pushedNode;
 983                         break;
 984                     }
 985                 }
 986             } else if (node instanceof NewInstanceNode) {
 987                 NewInstanceNode newInstanceNode = (NewInstanceNode) node;
 988                 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, newInstanceNode);
 989                 ResolvedJavaType type = newInstanceNode.instanceClass();
 990                 for (NodePlugin nodePlugin : nodePlugins) {
 991                     if (nodePlugin.handleNewInstance(graphBuilderContext, type)) {
 992                         replacedNode = graphBuilderContext.pushedNode;
 993                         break;
 994                     }
 995                 }
 996             } else if (node instanceof NewArrayNode) {
 997                 NewArrayNode newArrayNode = (NewArrayNode) node;
 998                 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, newArrayNode);
 999                 ResolvedJavaType elementType = newArrayNode.elementType();
1000                 ValueNode length = newArrayNode.length();
1001                 for (NodePlugin nodePlugin : nodePlugins) {
1002                     if (nodePlugin.handleNewArray(graphBuilderContext, elementType, length)) {
1003                         replacedNode = graphBuilderContext.pushedNode;
1004                         break;
1005                     }
1006                 }
1007             } else if (node instanceof NewMultiArrayNode) {
1008                 NewMultiArrayNode newArrayNode = (NewMultiArrayNode) node;
1009                 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, newArrayNode);
1010                 ResolvedJavaType elementType = newArrayNode.type();
1011                 ValueNode[] dimensions = newArrayNode.dimensions().toArray(new ValueNode[0]);
1012                 for (NodePlugin nodePlugin : nodePlugins) {
1013                     if (nodePlugin.handleNewMultiArray(graphBuilderContext, elementType, dimensions)) {
1014                         replacedNode = graphBuilderContext.pushedNode;
1015                         break;
1016                     }
1017                 }
1018             }
1019         }
1020 
1021         return super.canonicalizeFixedNode(methodScope, replacedNode);
1022     }
1023 
1024     @Override
1025     protected Node handleFloatingNodeBeforeAdd(MethodScope s, LoopScope loopScope, Node n) {
1026         PEMethodScope methodScope = (PEMethodScope) s;
1027 
1028         Node node = n;
1029         if (node instanceof ParameterNode) {
1030             ParameterNode param = (ParameterNode) node;
1031             if (methodScope.isInlinedMethod()) {
1032                 Node result = methodScope.arguments[param.index()];
1033                 assert result != null;
1034                 return result;
1035 
1036             } else if (parameterPlugin != null) {
1037                 assert !methodScope.isInlinedMethod();
1038                 GraphBuilderContext graphBuilderContext = new PENonAppendGraphBuilderContext(methodScope, null);
1039                 Node result = parameterPlugin.interceptParameter(graphBuilderContext, param.index(),
1040                                 StampPair.create(param.stamp(), param.uncheckedStamp()));
1041                 if (result != null) {
1042                     return result;
1043                 }
1044             }
1045             node = param.copyWithInputs();
1046         }
1047 
1048         return super.handleFloatingNodeBeforeAdd(methodScope, loopScope, node);
1049     }
1050 
1051     protected void ensureOuterStateDecoded(PEMethodScope methodScope) {
1052         if (methodScope.outerState == null && methodScope.caller != null) {
1053             FrameState stateAtReturn = methodScope.invokeData.invoke.stateAfter();
1054             if (stateAtReturn == null) {
1055                 stateAtReturn = (FrameState) decodeFloatingNode(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId);
1056             }
1057 
1058             JavaKind invokeReturnKind = methodScope.invokeData.invoke.asNode().getStackKind();
1059             FrameState outerState = stateAtReturn.duplicateModified(graph, methodScope.invokeData.invoke.bci(), stateAtReturn.rethrowException(), true, invokeReturnKind, null, null);
1060 
1061             /*
1062              * When the encoded graph has methods inlining, we can already have a proper caller
1063              * state. If not, we set the caller state here.
1064              */
1065             if (outerState.outerFrameState() == null && methodScope.caller != null) {
1066                 ensureOuterStateDecoded(methodScope.caller);
1067                 outerState.setOuterFrameState(methodScope.caller.outerState);
1068             }
1069             methodScope.outerState = outerState;
1070         }
1071     }
1072 
1073     protected void ensureStateAfterDecoded(PEMethodScope methodScope) {
1074         if (methodScope.invokeData.invoke.stateAfter() == null) {
1075             methodScope.invokeData.invoke.setStateAfter((FrameState) ensureNodeCreated(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId));
1076         }
1077     }
1078 
1079     protected void ensureExceptionStateDecoded(PEMethodScope methodScope) {
1080         if (methodScope.exceptionState == null && methodScope.caller != null && methodScope.invokeData.invoke instanceof InvokeWithExceptionNode) {
1081             ensureStateAfterDecoded(methodScope);
1082 
1083             assert methodScope.exceptionPlaceholderNode == null;
1084             methodScope.exceptionPlaceholderNode = graph.add(new ExceptionPlaceholderNode());
1085             registerNode(methodScope.callerLoopScope, methodScope.invokeData.exceptionOrderId, methodScope.exceptionPlaceholderNode, false, false);
1086             FrameState exceptionState = (FrameState) ensureNodeCreated(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.exceptionStateOrderId);
1087 
1088             if (exceptionState.outerFrameState() == null && methodScope.caller != null) {
1089                 ensureOuterStateDecoded(methodScope.caller);
1090                 exceptionState.setOuterFrameState(methodScope.caller.outerState);
1091             }
1092             methodScope.exceptionState = exceptionState;
1093         }
1094     }
1095 
1096     @Override
1097     protected Node addFloatingNode(MethodScope s, Node node) {
1098         Node addedNode = super.addFloatingNode(s, node);
1099         PEMethodScope methodScope = (PEMethodScope) s;
1100         NodeSourcePosition pos = node.getNodeSourcePosition();
1101         if (methodScope.isInlinedMethod()) {
1102             if (pos != null) {
1103                 NodeSourcePosition bytecodePosition = methodScope.getCallerBytecodePosition();
1104                 node.setNodeSourcePosition(pos.addCaller(bytecodePosition));
1105             }
1106         }
1107         return addedNode;
1108     }
1109 
1110     @Override
1111     protected Node handleFloatingNodeAfterAdd(MethodScope s, LoopScope loopScope, Node node) {
1112         PEMethodScope methodScope = (PEMethodScope) s;
1113 
1114         if (methodScope.isInlinedMethod()) {
1115             if (node instanceof FrameState) {
1116                 FrameState frameState = (FrameState) node;
1117 
1118                 ensureOuterStateDecoded(methodScope);
1119                 if (frameState.bci < 0) {
1120                     ensureExceptionStateDecoded(methodScope);
1121                 }
1122                 List<ValueNode> invokeArgsList = null;
1123                 if (frameState.bci == BytecodeFrame.BEFORE_BCI) {
1124                     /*
1125                      * We know that the argument list is only used in this case, so avoid the List
1126                      * allocation for "normal" bcis.
1127                      */
1128                     invokeArgsList = Arrays.asList(methodScope.arguments);
1129                 }
1130                 return InliningUtil.processFrameState(frameState, methodScope.invokeData.invoke, null, methodScope.method, methodScope.exceptionState, methodScope.outerState, true,
1131                                 methodScope.method, invokeArgsList);
1132 
1133             } else if (node instanceof MonitorIdNode) {
1134                 ensureOuterStateDecoded(methodScope);
1135                 InliningUtil.processMonitorId(methodScope.outerState, (MonitorIdNode) node);
1136                 return node;
1137             }
1138         }
1139 
1140         return node;
1141     }
1142 }