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