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