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