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