1 /*
   2  * Copyright (c) 2012, 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 java.util.FormattableFlags.ALTERNATE;
  28 import static org.graalvm.compiler.debug.DebugContext.DEFAULT_LOG_STREAM;
  29 import static org.graalvm.compiler.debug.DebugContext.applyFormattingFlagsAndWidth;
  30 import static org.graalvm.compiler.debug.DebugOptions.DebugStubsAndSnippets;
  31 import static org.graalvm.compiler.graph.iterators.NodePredicates.isNotA;
  32 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED;
  33 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED;
  34 import static org.graalvm.compiler.phases.common.DeadCodeEliminationPhase.Optionality.Required;
  35 import static jdk.internal.vm.compiler.word.LocationIdentity.any;
  36 
  37 import java.lang.reflect.Array;
  38 import java.lang.reflect.Method;
  39 import java.util.ArrayList;
  40 import java.util.Arrays;
  41 import java.util.Collection;
  42 import java.util.Collections;
  43 import java.util.Formattable;
  44 import java.util.Formatter;
  45 import java.util.LinkedHashMap;
  46 import java.util.List;
  47 import java.util.Map;
  48 import java.util.concurrent.atomic.AtomicInteger;
  49 import java.util.concurrent.atomic.AtomicReference;
  50 import java.util.function.Predicate;
  51 
  52 import jdk.internal.vm.compiler.collections.EconomicMap;
  53 import jdk.internal.vm.compiler.collections.EconomicSet;
  54 import jdk.internal.vm.compiler.collections.Equivalence;
  55 import jdk.internal.vm.compiler.collections.UnmodifiableEconomicMap;
  56 import org.graalvm.compiler.api.replacements.Fold;
  57 import org.graalvm.compiler.api.replacements.Snippet;
  58 import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter;
  59 import org.graalvm.compiler.api.replacements.Snippet.NonNullParameter;
  60 import org.graalvm.compiler.api.replacements.Snippet.VarargsParameter;
  61 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
  62 import org.graalvm.compiler.core.common.GraalOptions;
  63 import org.graalvm.compiler.core.common.type.Stamp;
  64 import org.graalvm.compiler.core.common.type.StampFactory;
  65 import org.graalvm.compiler.core.common.type.StampPair;
  66 import org.graalvm.compiler.core.common.type.TypeReference;
  67 import org.graalvm.compiler.debug.Assertions;
  68 import org.graalvm.compiler.debug.CounterKey;
  69 import org.graalvm.compiler.debug.DebugCloseable;
  70 import org.graalvm.compiler.debug.DebugContext;
  71 import org.graalvm.compiler.debug.DebugContext.Description;
  72 import org.graalvm.compiler.debug.DebugHandlersFactory;
  73 import org.graalvm.compiler.debug.GraalError;
  74 import org.graalvm.compiler.debug.TimerKey;
  75 import org.graalvm.compiler.graph.Graph.Mark;
  76 import org.graalvm.compiler.graph.Node;
  77 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
  78 import org.graalvm.compiler.graph.NodeClass;
  79 import org.graalvm.compiler.graph.NodeSourcePosition;
  80 import org.graalvm.compiler.graph.Position;
  81 import org.graalvm.compiler.loop.LoopEx;
  82 import org.graalvm.compiler.loop.LoopsData;
  83 import org.graalvm.compiler.loop.phases.LoopTransformations;
  84 import org.graalvm.compiler.nodeinfo.InputType;
  85 import org.graalvm.compiler.nodeinfo.NodeInfo;
  86 import org.graalvm.compiler.nodes.AbstractBeginNode;
  87 import org.graalvm.compiler.nodes.AbstractMergeNode;
  88 import org.graalvm.compiler.nodes.ConstantNode;
  89 import org.graalvm.compiler.nodes.ControlSinkNode;
  90 import org.graalvm.compiler.nodes.DeoptimizingNode;
  91 import org.graalvm.compiler.nodes.FixedNode;
  92 import org.graalvm.compiler.nodes.FixedWithNextNode;
  93 import org.graalvm.compiler.nodes.FrameState;
  94 import org.graalvm.compiler.nodes.InliningLog;
  95 import org.graalvm.compiler.nodes.LoopBeginNode;
  96 import org.graalvm.compiler.nodes.MergeNode;
  97 import org.graalvm.compiler.nodes.NodeView;
  98 import org.graalvm.compiler.nodes.ParameterNode;
  99 import org.graalvm.compiler.nodes.PhiNode;
 100 import org.graalvm.compiler.nodes.PiNode.Placeholder;
 101 import org.graalvm.compiler.nodes.PiNode.PlaceholderStamp;
 102 import org.graalvm.compiler.nodes.ReturnNode;
 103 import org.graalvm.compiler.nodes.StartNode;
 104 import org.graalvm.compiler.nodes.StateSplit;
 105 import org.graalvm.compiler.nodes.StructuredGraph;
 106 import org.graalvm.compiler.nodes.StructuredGraph.GuardsStage;
 107 import org.graalvm.compiler.nodes.ValueNode;
 108 import org.graalvm.compiler.nodes.ValueNodeUtil;
 109 import org.graalvm.compiler.nodes.calc.FloatingNode;
 110 import org.graalvm.compiler.nodes.java.LoadIndexedNode;
 111 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
 112 import org.graalvm.compiler.nodes.java.StoreIndexedNode;
 113 import org.graalvm.compiler.nodes.memory.MemoryAccess;
 114 import org.graalvm.compiler.nodes.memory.MemoryAnchorNode;
 115 import org.graalvm.compiler.nodes.memory.MemoryCheckpoint;
 116 import org.graalvm.compiler.nodes.memory.MemoryMap;
 117 import org.graalvm.compiler.nodes.memory.MemoryMapNode;
 118 import org.graalvm.compiler.nodes.memory.MemoryNode;
 119 import org.graalvm.compiler.nodes.memory.MemoryPhiNode;
 120 import org.graalvm.compiler.nodes.spi.ArrayLengthProvider;
 121 import org.graalvm.compiler.nodes.spi.LoweringTool;
 122 import org.graalvm.compiler.nodes.spi.MemoryProxy;
 123 import org.graalvm.compiler.nodes.util.GraphUtil;
 124 import org.graalvm.compiler.options.Option;
 125 import org.graalvm.compiler.options.OptionKey;
 126 import org.graalvm.compiler.options.OptionValues;
 127 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 128 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
 129 import org.graalvm.compiler.phases.common.FloatingReadPhase;
 130 import org.graalvm.compiler.phases.common.FloatingReadPhase.MemoryMapImpl;
 131 import org.graalvm.compiler.phases.common.GuardLoweringPhase;
 132 import org.graalvm.compiler.phases.common.LoweringPhase;
 133 import org.graalvm.compiler.phases.common.RemoveValueProxyPhase;
 134 import org.graalvm.compiler.phases.common.inlining.InliningUtil;
 135 import org.graalvm.compiler.phases.tiers.PhaseContext;
 136 import org.graalvm.compiler.phases.util.Providers;
 137 import org.graalvm.compiler.replacements.nodes.ExplodeLoopNode;
 138 import org.graalvm.compiler.replacements.nodes.LoadSnippetVarargParameterNode;
 139 import org.graalvm.util.CollectionsUtil;
 140 import jdk.internal.vm.compiler.word.LocationIdentity;
 141 import jdk.internal.vm.compiler.word.WordBase;
 142 
 143 import jdk.vm.ci.code.TargetDescription;
 144 import jdk.vm.ci.meta.Constant;
 145 import jdk.vm.ci.meta.ConstantReflectionProvider;
 146 import jdk.vm.ci.meta.JavaConstant;
 147 import jdk.vm.ci.meta.JavaKind;
 148 import jdk.vm.ci.meta.Local;
 149 import jdk.vm.ci.meta.LocalVariableTable;
 150 import jdk.vm.ci.meta.MetaAccessProvider;
 151 import jdk.vm.ci.meta.ResolvedJavaMethod;
 152 import jdk.vm.ci.meta.ResolvedJavaMethod.Parameter;
 153 import jdk.vm.ci.meta.ResolvedJavaType;
 154 import jdk.vm.ci.meta.Signature;
 155 
 156 /**
 157  * A snippet template is a graph created by parsing a snippet method and then specialized by binding
 158  * constants to the snippet's {@link ConstantParameter} parameters.
 159  *
 160  * Snippet templates can be managed in a cache maintained by {@link AbstractTemplates}.
 161  */
 162 public class SnippetTemplate {
 163 
 164     private boolean mayRemoveLocation = false;
 165 
 166     /**
 167      * Holds the {@link ResolvedJavaMethod} of the snippet, together with some information about the
 168      * method that needs to be computed only once. The {@link SnippetInfo} should be created once
 169      * per snippet and then cached.
 170      */
 171     public abstract static class SnippetInfo {
 172 
 173         protected final ResolvedJavaMethod method;
 174         protected final ResolvedJavaMethod original;
 175         protected final LocationIdentity[] privateLocations;
 176         protected final Object receiver;
 177 
 178         public Object getReceiver() {
 179             return receiver;
 180         }
 181 
 182         boolean hasReceiver() {
 183             assert hasReceiver(method) == (receiver != null) : "Snippet with the receiver must have it set as constant. Snippet: " + this;
 184             return hasReceiver(method);
 185         }
 186 
 187         static boolean hasReceiver(ResolvedJavaMethod method) {
 188             return method.hasReceiver();
 189         }
 190 
 191         /**
 192          * Lazily constructed parts of {@link SnippetInfo}.
 193          */
 194         static class Lazy {
 195             Lazy(ResolvedJavaMethod method) {
 196                 int count = method.getSignature().getParameterCount(hasReceiver(method));
 197                 constantParameters = new boolean[count];
 198                 varargsParameters = new boolean[count];
 199                 nonNullParameters = new boolean[count];
 200                 int offset = hasReceiver(method) ? 1 : 0;
 201                 for (int i = offset; i < count; i++) {
 202                     constantParameters[i] = method.getParameterAnnotation(ConstantParameter.class, i - offset) != null;
 203                     varargsParameters[i] = method.getParameterAnnotation(VarargsParameter.class, i - offset) != null;
 204                     nonNullParameters[i] = method.getParameterAnnotation(NonNullParameter.class, i - offset) != null;
 205 
 206                     assert !constantParameters[i - offset] || !varargsParameters[i - offset] : "Parameter cannot be annotated with both @" + ConstantParameter.class.getSimpleName() + " and @" +
 207                                     VarargsParameter.class.getSimpleName();
 208                 }
 209                 if (method.hasReceiver()) {
 210                     // Receiver must be constant.
 211                     assert !constantParameters[0];
 212                     constantParameters[0] = true;
 213                 }
 214 
 215                 // Retrieve the names only when assertions are turned on.
 216                 assert initNames(method, count);
 217             }
 218 
 219             final boolean[] constantParameters;
 220             final boolean[] varargsParameters;
 221             final boolean[] nonNullParameters;
 222 
 223             /**
 224              * The parameter names, taken from the local variables table. Only used for assertion
 225              * checking, so use only within an assert statement.
 226              */
 227             String[] names;
 228 
 229             private boolean initNames(ResolvedJavaMethod method, int parameterCount) {
 230                 names = new String[parameterCount];
 231                 int offset = 0;
 232                 if (method.hasReceiver()) {
 233                     names[0] = "this";
 234                     offset = 1;
 235                 }
 236                 Parameter[] params = method.getParameters();
 237                 if (params != null) {
 238                     for (int i = offset; i < names.length; i++) {
 239                         if (params[i - offset].isNamePresent()) {
 240                             names[i] = params[i - offset].getName();
 241                         }
 242                     }
 243                 } else {
 244                     int slotIdx = 0;
 245                     LocalVariableTable localVariableTable = method.getLocalVariableTable();
 246                     if (localVariableTable != null) {
 247                         for (int i = 0; i < names.length; i++) {
 248                             Local local = localVariableTable.getLocal(slotIdx, 0);
 249                             if (local != null) {
 250                                 names[i] = local.getName();
 251                             }
 252                             JavaKind kind = method.getSignature().getParameterKind(i);
 253                             slotIdx += kind.getSlotCount();
 254                         }
 255                     }
 256                 }
 257                 return true;
 258             }
 259         }
 260 
 261         /**
 262          * Times instantiations of all templates derived from this snippet.
 263          */
 264         private final TimerKey instantiationTimer;
 265 
 266         /**
 267          * Counts instantiations of all templates derived from this snippet.
 268          */
 269         private final CounterKey instantiationCounter;
 270 
 271         protected abstract Lazy lazy();
 272 
 273         protected SnippetInfo(ResolvedJavaMethod method, ResolvedJavaMethod original, LocationIdentity[] privateLocations, Object receiver) {
 274             this.method = method;
 275             this.original = original;
 276             this.privateLocations = privateLocations;
 277             instantiationCounter = DebugContext.counter("SnippetInstantiationCount[%s]", method.getName());
 278             instantiationTimer = DebugContext.timer("SnippetInstantiationTime[%s]", method.getName());
 279             this.receiver = receiver;
 280         }
 281 
 282         public ResolvedJavaMethod getMethod() {
 283             return method;
 284         }
 285 
 286         public int getParameterCount() {
 287             return lazy().constantParameters.length;
 288         }
 289 
 290         public boolean isConstantParameter(int paramIdx) {
 291             return lazy().constantParameters[paramIdx];
 292         }
 293 
 294         public boolean isVarargsParameter(int paramIdx) {
 295             return lazy().varargsParameters[paramIdx];
 296         }
 297 
 298         public boolean isNonNullParameter(int paramIdx) {
 299             return lazy().nonNullParameters[paramIdx];
 300         }
 301 
 302         public String getParameterName(int paramIdx) {
 303             String[] names = lazy().names;
 304             if (names != null) {
 305                 return names[paramIdx];
 306             }
 307             return null;
 308         }
 309 
 310         @Override
 311         public String toString() {
 312             return getClass().getSimpleName() + ":" + method.format("%h.%n");
 313         }
 314     }
 315 
 316     protected static class LazySnippetInfo extends SnippetInfo {
 317         protected final AtomicReference<Lazy> lazy = new AtomicReference<>(null);
 318 
 319         protected LazySnippetInfo(ResolvedJavaMethod method, ResolvedJavaMethod original, LocationIdentity[] privateLocations, Object receiver) {
 320             super(method, original, privateLocations, receiver);
 321         }
 322 
 323         @Override
 324         protected Lazy lazy() {
 325             if (lazy.get() == null) {
 326                 lazy.compareAndSet(null, new Lazy(method));
 327             }
 328             return lazy.get();
 329         }
 330     }
 331 
 332     protected static class EagerSnippetInfo extends SnippetInfo {
 333         protected final Lazy lazy;
 334 
 335         protected EagerSnippetInfo(ResolvedJavaMethod method, ResolvedJavaMethod original, LocationIdentity[] privateLocations, Object receiver) {
 336             super(method, original, privateLocations, receiver);
 337             lazy = new Lazy(method);
 338         }
 339 
 340         @Override
 341         protected Lazy lazy() {
 342             return lazy;
 343         }
 344     }
 345 
 346     /**
 347      * Values that are bound to the snippet method parameters. The methods {@link #add},
 348      * {@link #addConst}, and {@link #addVarargs} must be called in the same order as in the
 349      * signature of the snippet method. The parameter name is passed to the add methods for
 350      * assertion checking, i.e., to enforce that the order matches. Which method needs to be called
 351      * depends on the annotation of the snippet method parameter:
 352      * <ul>
 353      * <li>Use {@link #add} for a parameter without an annotation. The value is bound when the
 354      * {@link SnippetTemplate} is {@link SnippetTemplate#instantiate instantiated}.
 355      * <li>Use {@link #addConst} for a parameter annotated with {@link ConstantParameter}. The value
 356      * is bound when the {@link SnippetTemplate} is {@link SnippetTemplate#SnippetTemplate created}.
 357      * <li>Use {@link #addVarargs} for an array parameter annotated with {@link VarargsParameter}. A
 358      * separate {@link SnippetTemplate} is {@link SnippetTemplate#SnippetTemplate created} for every
 359      * distinct array length. The actual values are bound when the {@link SnippetTemplate} is
 360      * {@link SnippetTemplate#instantiate instantiated}
 361      * </ul>
 362      */
 363     public static class Arguments implements Formattable {
 364 
 365         protected final SnippetInfo info;
 366         protected final CacheKey cacheKey;
 367         protected final Object[] values;
 368         protected final Stamp[] constStamps;
 369         protected boolean cacheable;
 370 
 371         protected int nextParamIdx;
 372 
 373         public Arguments(SnippetInfo info, GuardsStage guardsStage, LoweringTool.LoweringStage loweringStage) {
 374             this.info = info;
 375             this.cacheKey = new CacheKey(info, guardsStage, loweringStage);
 376             this.values = new Object[info.getParameterCount()];
 377             this.constStamps = new Stamp[info.getParameterCount()];
 378             this.cacheable = true;
 379             if (info.hasReceiver()) {
 380                 addConst("this", info.getReceiver());
 381             }
 382         }
 383 
 384         public Arguments add(String name, Object value) {
 385             assert check(name, false, false);
 386             values[nextParamIdx] = value;
 387             nextParamIdx++;
 388             return this;
 389         }
 390 
 391         public Arguments addConst(String name, Object value) {
 392             assert value != null;
 393             return addConst(name, value, null);
 394         }
 395 
 396         public Arguments addConst(String name, Object value, Stamp stamp) {
 397             assert check(name, true, false);
 398             values[nextParamIdx] = value;
 399             constStamps[nextParamIdx] = stamp;
 400             cacheKey.setParam(nextParamIdx, value);
 401             nextParamIdx++;
 402             return this;
 403         }
 404 
 405         public Arguments addVarargs(String name, Class<?> componentType, Stamp argStamp, Object value) {
 406             assert check(name, false, true);
 407             Varargs varargs = new Varargs(componentType, argStamp, value);
 408             values[nextParamIdx] = varargs;
 409             // A separate template is necessary for every distinct array length
 410             cacheKey.setParam(nextParamIdx, varargs.length);
 411             nextParamIdx++;
 412             return this;
 413         }
 414 
 415         public void setCacheable(boolean cacheable) {
 416             this.cacheable = cacheable;
 417         }
 418 
 419         private boolean check(String name, boolean constParam, boolean varargsParam) {
 420             assert nextParamIdx < info.getParameterCount() : "too many parameters: " + name + "  " + this;
 421             assert info.getParameterName(nextParamIdx) == null || info.getParameterName(nextParamIdx).equals(name) : "wrong parameter name at " + nextParamIdx + " : " + name + "  " + this;
 422             assert constParam == info.isConstantParameter(nextParamIdx) : "Parameter " + (constParam ? "not " : "") + "annotated with @" + ConstantParameter.class.getSimpleName() + ": " + name +
 423                             "  " + this;
 424             assert varargsParam == info.isVarargsParameter(nextParamIdx) : "Parameter " + (varargsParam ? "not " : "") + "annotated with @" + VarargsParameter.class.getSimpleName() + ": " + name +
 425                             "  " + this;
 426             return true;
 427         }
 428 
 429         @Override
 430         public String toString() {
 431             StringBuilder result = new StringBuilder();
 432             result.append("Parameters<").append(info.method.format("%h.%n")).append(" [");
 433             String sep = "";
 434             for (int i = 0; i < info.getParameterCount(); i++) {
 435                 result.append(sep);
 436                 if (info.isConstantParameter(i)) {
 437                     result.append("const ");
 438                 } else if (info.isVarargsParameter(i)) {
 439                     result.append("varargs ");
 440                 }
 441                 result.append(info.getParameterName(i)).append(" = ").append(values[i]);
 442                 sep = ", ";
 443             }
 444             result.append(">");
 445             return result.toString();
 446         }
 447 
 448         @Override
 449         public void formatTo(Formatter formatter, int flags, int width, int precision) {
 450             if ((flags & ALTERNATE) == 0) {
 451                 formatter.format(applyFormattingFlagsAndWidth(toString(), flags, width));
 452             } else {
 453                 StringBuilder sb = new StringBuilder();
 454                 sb.append(info.method.getName()).append('(');
 455                 String sep = "";
 456                 for (int i = 0; i < info.getParameterCount(); i++) {
 457                     if (info.isConstantParameter(i)) {
 458                         sb.append(sep);
 459                         if (info.getParameterName(i) != null) {
 460                             sb.append(info.getParameterName(i));
 461                         } else {
 462                             sb.append(i);
 463                         }
 464                         sb.append('=').append(values[i]);
 465                         sep = ", ";
 466                     }
 467                 }
 468                 sb.append(")");
 469                 String string = sb.toString();
 470                 if (string.indexOf('%') != -1) {
 471                     // Quote any % signs
 472                     string = string.replace("%", "%%");
 473                 }
 474                 formatter.format(applyFormattingFlagsAndWidth(string, flags & ~ALTERNATE, width));
 475             }
 476         }
 477     }
 478 
 479     /**
 480      * Wrapper for the prototype value of a {@linkplain VarargsParameter varargs} parameter.
 481      */
 482     static class Varargs {
 483 
 484         protected final Class<?> componentType;
 485         protected final Stamp stamp;
 486         protected final Object value;
 487         protected final int length;
 488 
 489         protected Varargs(Class<?> componentType, Stamp stamp, Object value) {
 490             this.componentType = componentType;
 491             this.stamp = stamp;
 492             this.value = value;
 493             if (value instanceof List) {
 494                 this.length = ((List<?>) value).size();
 495             } else {
 496                 this.length = Array.getLength(value);
 497             }
 498         }
 499 
 500         @Override
 501         public String toString() {
 502             if (value instanceof boolean[]) {
 503                 return Arrays.toString((boolean[]) value);
 504             }
 505             if (value instanceof byte[]) {
 506                 return Arrays.toString((byte[]) value);
 507             }
 508             if (value instanceof char[]) {
 509                 return Arrays.toString((char[]) value);
 510             }
 511             if (value instanceof short[]) {
 512                 return Arrays.toString((short[]) value);
 513             }
 514             if (value instanceof int[]) {
 515                 return Arrays.toString((int[]) value);
 516             }
 517             if (value instanceof long[]) {
 518                 return Arrays.toString((long[]) value);
 519             }
 520             if (value instanceof float[]) {
 521                 return Arrays.toString((float[]) value);
 522             }
 523             if (value instanceof double[]) {
 524                 return Arrays.toString((double[]) value);
 525             }
 526             if (value instanceof Object[]) {
 527                 return Arrays.toString((Object[]) value);
 528             }
 529             return String.valueOf(value);
 530         }
 531     }
 532 
 533     @NodeInfo(cycles = CYCLES_IGNORED, size = SIZE_IGNORED)
 534     static final class VarargsPlaceholderNode extends FloatingNode implements ArrayLengthProvider {
 535 
 536         public static final NodeClass<VarargsPlaceholderNode> TYPE = NodeClass.create(VarargsPlaceholderNode.class);
 537         protected final Varargs varargs;
 538 
 539         protected VarargsPlaceholderNode(Varargs varargs, MetaAccessProvider metaAccess) {
 540             super(TYPE, StampFactory.objectNonNull(TypeReference.createExactTrusted(metaAccess.lookupJavaType(varargs.componentType).getArrayClass())));
 541             this.varargs = varargs;
 542         }
 543 
 544         @Override
 545         public ValueNode findLength(FindLengthMode mode, ConstantReflectionProvider constantReflection) {
 546             return ConstantNode.forInt(varargs.length);
 547         }
 548     }
 549 
 550     static class CacheKey {
 551 
 552         private final ResolvedJavaMethod method;
 553         private final Object[] values;
 554         private final GuardsStage guardsStage;
 555         private final LoweringTool.LoweringStage loweringStage;
 556         private int hash;
 557 
 558         protected CacheKey(SnippetInfo info, GuardsStage guardsStage, LoweringTool.LoweringStage loweringStage) {
 559             this.method = info.method;
 560             this.guardsStage = guardsStage;
 561             this.loweringStage = loweringStage;
 562             this.values = new Object[info.getParameterCount()];
 563             this.hash = info.method.hashCode() + 31 * guardsStage.ordinal();
 564         }
 565 
 566         protected void setParam(int paramIdx, Object value) {
 567             values[paramIdx] = value;
 568             hash = (hash * 31) ^ (value == null ? 0 : value.hashCode());
 569         }
 570 
 571         @Override
 572         public boolean equals(Object obj) {
 573             if (!(obj instanceof CacheKey)) {
 574                 return false;
 575             }
 576             CacheKey other = (CacheKey) obj;
 577             if (!method.equals(other.method)) {
 578                 return false;
 579             }
 580             if (guardsStage != other.guardsStage || loweringStage != other.loweringStage) {
 581                 return false;
 582             }
 583             for (int i = 0; i < values.length; i++) {
 584                 if (values[i] != null && !values[i].equals(other.values[i])) {
 585                     return false;
 586                 }
 587             }
 588             return true;
 589         }
 590 
 591         @Override
 592         public int hashCode() {
 593             return hash;
 594         }
 595     }
 596 
 597     private static final TimerKey SnippetTemplateCreationTime = DebugContext.timer("SnippetTemplateCreationTime");
 598     private static final CounterKey SnippetTemplates = DebugContext.counter("SnippetTemplateCount");
 599 
 600     static class Options {
 601         @Option(help = "Use a LRU cache for snippet templates.")//
 602         public static final OptionKey<Boolean> UseSnippetTemplateCache = new OptionKey<>(true);
 603 
 604         @Option(help = "")//
 605         static final OptionKey<Integer> MaxTemplatesPerSnippet = new OptionKey<>(50);
 606     }
 607 
 608     /**
 609      * Base class for snippet classes. It provides a cache for {@link SnippetTemplate}s.
 610      */
 611     public abstract static class AbstractTemplates implements org.graalvm.compiler.api.replacements.SnippetTemplateCache {
 612 
 613         protected final OptionValues options;
 614         protected final Providers providers;
 615         protected final SnippetReflectionProvider snippetReflection;
 616         protected final Iterable<DebugHandlersFactory> factories;
 617         protected final TargetDescription target;
 618         private final Map<CacheKey, SnippetTemplate> templates;
 619 
 620         protected AbstractTemplates(OptionValues options, Iterable<DebugHandlersFactory> factories, Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) {
 621             this.options = options;
 622             this.providers = providers;
 623             this.snippetReflection = snippetReflection;
 624             this.target = target;
 625             this.factories = factories;
 626             if (Options.UseSnippetTemplateCache.getValue(options)) {
 627                 int size = Options.MaxTemplatesPerSnippet.getValue(options);
 628                 this.templates = Collections.synchronizedMap(new LRUCache<>(size, size));
 629             } else {
 630                 this.templates = null;
 631             }
 632         }
 633 
 634         public static Method findMethod(Class<? extends Snippets> declaringClass, String methodName, Method except) {
 635             for (Method m : declaringClass.getDeclaredMethods()) {
 636                 if (m.getName().equals(methodName) && !m.equals(except)) {
 637                     return m;
 638                 }
 639             }
 640             return null;
 641         }
 642 
 643         public static ResolvedJavaMethod findMethod(MetaAccessProvider metaAccess, Class<?> declaringClass, String methodName) {
 644             ResolvedJavaType type = metaAccess.lookupJavaType(declaringClass);
 645             ResolvedJavaMethod result = null;
 646             for (ResolvedJavaMethod m : type.getDeclaredMethods()) {
 647                 if (m.getName().equals(methodName)) {
 648                     if (!Assertions.assertionsEnabled()) {
 649                         return m;
 650                     } else {
 651                         assert result == null : "multiple definitions found";
 652                         result = m;
 653                     }
 654                 }
 655             }
 656             if (result == null) {
 657                 throw new GraalError("Could not find method in " + declaringClass + " named " + methodName);
 658             }
 659             return result;
 660         }
 661 
 662         protected SnippetInfo snippet(Class<? extends Snippets> declaringClass, String methodName, LocationIdentity... initialPrivateLocations) {
 663             return snippet(declaringClass, methodName, null, null, initialPrivateLocations);
 664         }
 665 
 666         /**
 667          * Finds the unique method in {@code declaringClass} named {@code methodName} annotated by
 668          * {@link Snippet} and returns a {@link SnippetInfo} value describing it. There must be
 669          * exactly one snippet method in {@code declaringClass} with a given name.
 670          */
 671         protected SnippetInfo snippet(Class<? extends Snippets> declaringClass, String methodName, ResolvedJavaMethod original, Object receiver, LocationIdentity... initialPrivateLocations) {
 672             assert methodName != null;
 673             ResolvedJavaMethod javaMethod = findMethod(providers.getMetaAccess(), declaringClass, methodName);
 674             assert javaMethod != null : "did not find @" + Snippet.class.getSimpleName() + " method in " + declaringClass + " named " + methodName;
 675             assert javaMethod.getAnnotation(Snippet.class) != null : javaMethod + " must be annotated with @" + Snippet.class.getSimpleName();
 676             providers.getReplacements().registerSnippet(javaMethod, original, receiver, GraalOptions.TrackNodeSourcePosition.getValue(options));
 677             LocationIdentity[] privateLocations = GraalOptions.SnippetCounters.getValue(options) ? SnippetCounterNode.addSnippetCounters(initialPrivateLocations) : initialPrivateLocations;
 678             if (GraalOptions.EagerSnippets.getValue(options)) {
 679                 return new EagerSnippetInfo(javaMethod, original, privateLocations, receiver);
 680             } else {
 681                 return new LazySnippetInfo(javaMethod, original, privateLocations, receiver);
 682             }
 683         }
 684 
 685         static final AtomicInteger nextSnippetTemplateId = new AtomicInteger();
 686 
 687         private DebugContext openDebugContext(DebugContext outer, Arguments args) {
 688             if (DebugStubsAndSnippets.getValue(options)) {
 689                 Description description = new Description(args.cacheKey.method, "SnippetTemplate_" + nextSnippetTemplateId.incrementAndGet());
 690                 return DebugContext.create(options, description, outer.getGlobalMetrics(), DEFAULT_LOG_STREAM, factories);
 691             }
 692             return DebugContext.DISABLED;
 693         }
 694 
 695         /**
 696          * Gets a template for a given key, creating it first if necessary.
 697          */
 698         @SuppressWarnings("try")
 699         protected SnippetTemplate template(ValueNode replacee, final Arguments args) {
 700             StructuredGraph graph = replacee.graph();
 701             DebugContext outer = graph.getDebug();
 702             SnippetTemplate template = Options.UseSnippetTemplateCache.getValue(options) && args.cacheable ? templates.get(args.cacheKey) : null;
 703             if (template == null || (graph.trackNodeSourcePosition() && !template.snippet.trackNodeSourcePosition())) {
 704                 try (DebugContext debug = openDebugContext(outer, args)) {
 705                     try (DebugCloseable a = SnippetTemplateCreationTime.start(debug); DebugContext.Scope s = debug.scope("SnippetSpecialization", args.info.method)) {
 706                         SnippetTemplates.increment(debug);
 707                         OptionValues snippetOptions = new OptionValues(options, GraalOptions.TraceInlining, GraalOptions.TraceInliningForStubsAndSnippets.getValue(options));
 708                         template = new SnippetTemplate(snippetOptions, debug, providers, snippetReflection, args, graph.trackNodeSourcePosition(), replacee);
 709                         if (Options.UseSnippetTemplateCache.getValue(snippetOptions) && args.cacheable) {
 710                             templates.put(args.cacheKey, template);
 711                         }
 712                     } catch (Throwable e) {
 713                         throw debug.handle(e);
 714                     }
 715                 }
 716             }
 717             return template;
 718         }
 719     }
 720 
 721     private static final class LRUCache<K, V> extends LinkedHashMap<K, V> {
 722         private static final long serialVersionUID = 1L;
 723         private final int maxCacheSize;
 724 
 725         LRUCache(int initialCapacity, int maxCacheSize) {
 726             super(initialCapacity, 0.75F, true);
 727             this.maxCacheSize = maxCacheSize;
 728         }
 729 
 730         @Override
 731         protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
 732             return size() > maxCacheSize;
 733         }
 734     }
 735 
 736     // These values must be compared with equals() not '==' to support replay compilation.
 737     private static final Object UNUSED_PARAMETER = "UNUSED_PARAMETER";
 738     private static final Object CONSTANT_PARAMETER = "CONSTANT_PARAMETER";
 739 
 740     /**
 741      * Determines if any parameter of a given method is annotated with {@link ConstantParameter}.
 742      */
 743     public static boolean hasConstantParameter(ResolvedJavaMethod method) {
 744         for (ConstantParameter p : method.getParameterAnnotations(ConstantParameter.class)) {
 745             if (p != null) {
 746                 return true;
 747             }
 748         }
 749         return false;
 750     }
 751 
 752     private final SnippetReflectionProvider snippetReflection;
 753 
 754     /**
 755      * Creates a snippet template.
 756      */
 757     @SuppressWarnings("try")
 758     protected SnippetTemplate(OptionValues options, DebugContext debug, final Providers providers, SnippetReflectionProvider snippetReflection, Arguments args, boolean trackNodeSourcePosition,
 759                     Node replacee) {
 760         this.snippetReflection = snippetReflection;
 761         this.info = args.info;
 762 
 763         Object[] constantArgs = getConstantArgs(args);
 764         boolean shouldTrackNodeSourcePosition1 = trackNodeSourcePosition || (providers.getCodeCache() != null && providers.getCodeCache().shouldDebugNonSafepoints());
 765         StructuredGraph snippetGraph = providers.getReplacements().getSnippet(args.info.method, args.info.original, constantArgs, shouldTrackNodeSourcePosition1, replacee.getNodeSourcePosition());
 766 
 767         ResolvedJavaMethod method = snippetGraph.method();
 768         Signature signature = method.getSignature();
 769 
 770         PhaseContext phaseContext = new PhaseContext(providers);
 771 
 772         // Copy snippet graph, replacing constant parameters with given arguments
 773         final StructuredGraph snippetCopy = new StructuredGraph.Builder(options, debug).name(snippetGraph.name).method(snippetGraph.method()).trackNodeSourcePosition(
 774                         snippetGraph.trackNodeSourcePosition()).setIsSubstitution(true).build();
 775         assert !GraalOptions.TrackNodeSourcePosition.getValue(options) || snippetCopy.trackNodeSourcePosition();
 776         try (DebugContext.Scope scope = debug.scope("SpecializeSnippet", snippetCopy)) {
 777             if (!snippetGraph.isUnsafeAccessTrackingEnabled()) {
 778                 snippetCopy.disableUnsafeAccessTracking();
 779             }
 780 
 781             EconomicMap<Node, Node> nodeReplacements = EconomicMap.create(Equivalence.IDENTITY);
 782             nodeReplacements.put(snippetGraph.start(), snippetCopy.start());
 783 
 784             MetaAccessProvider metaAccess = providers.getMetaAccess();
 785             assert checkTemplate(metaAccess, args, method, signature);
 786 
 787             int parameterCount = args.info.getParameterCount();
 788             VarargsPlaceholderNode[] placeholders = new VarargsPlaceholderNode[parameterCount];
 789 
 790             for (int i = 0; i < parameterCount; i++) {
 791                 ParameterNode parameter = snippetGraph.getParameter(i);
 792                 if (parameter != null) {
 793                     if (args.info.isConstantParameter(i)) {
 794                         Object arg = args.values[i];
 795                         JavaKind kind = signature.getParameterKind(i);
 796                         ConstantNode constantNode;
 797                         if (arg instanceof Constant) {
 798                             Stamp stamp = args.constStamps[i];
 799                             if (stamp == null) {
 800                                 assert arg instanceof JavaConstant : "could not determine type of constant " + arg;
 801                                 constantNode = ConstantNode.forConstant((JavaConstant) arg, metaAccess, snippetCopy);
 802                             } else {
 803                                 constantNode = ConstantNode.forConstant(stamp, (Constant) arg, metaAccess, snippetCopy);
 804                             }
 805                         } else {
 806                             constantNode = ConstantNode.forConstant(snippetReflection.forBoxed(kind, arg), metaAccess, snippetCopy);
 807                         }
 808                         nodeReplacements.put(parameter, constantNode);
 809                     } else if (args.info.isVarargsParameter(i)) {
 810                         Varargs varargs = (Varargs) args.values[i];
 811                         VarargsPlaceholderNode placeholder = snippetCopy.unique(new VarargsPlaceholderNode(varargs, providers.getMetaAccess()));
 812                         nodeReplacements.put(parameter, placeholder);
 813                         placeholders[i] = placeholder;
 814                     } else if (args.info.isNonNullParameter(i)) {
 815                         parameter.setStamp(parameter.stamp(NodeView.DEFAULT).join(StampFactory.objectNonNull()));
 816                     }
 817                 }
 818             }
 819             try (InliningLog.UpdateScope updateScope = snippetCopy.getInliningLog().openDefaultUpdateScope()) {
 820                 UnmodifiableEconomicMap<Node, Node> duplicates = snippetCopy.addDuplicates(snippetGraph.getNodes(), snippetGraph, snippetGraph.getNodeCount(), nodeReplacements);
 821                 if (updateScope != null) {
 822                     snippetCopy.getInliningLog().replaceLog(duplicates, snippetGraph.getInliningLog());
 823                 }
 824             }
 825 
 826             debug.dump(DebugContext.INFO_LEVEL, snippetCopy, "Before specialization");
 827 
 828             // Gather the template parameters
 829             parameters = new Object[parameterCount];
 830             for (int i = 0; i < parameterCount; i++) {
 831                 if (args.info.isConstantParameter(i)) {
 832                     parameters[i] = CONSTANT_PARAMETER;
 833                 } else if (args.info.isVarargsParameter(i)) {
 834                     assert snippetCopy.getParameter(i) == null;
 835                     Varargs varargs = (Varargs) args.values[i];
 836                     int length = varargs.length;
 837                     ParameterNode[] params = new ParameterNode[length];
 838                     Stamp stamp = varargs.stamp;
 839                     for (int j = 0; j < length; j++) {
 840                         // Use a decimal friendly numbering make it more obvious how values map
 841                         assert parameterCount < 10000;
 842                         int idx = (i + 1) * 10000 + j;
 843                         assert idx >= parameterCount : "collision in parameter numbering";
 844                         ParameterNode local = snippetCopy.addOrUnique(new ParameterNode(idx, StampPair.createSingle(stamp)));
 845                         params[j] = local;
 846                     }
 847                     parameters[i] = params;
 848 
 849                     VarargsPlaceholderNode placeholder = placeholders[i];
 850                     if (placeholder != null) {
 851                         for (Node usage : placeholder.usages().snapshot()) {
 852                             if (usage instanceof LoadIndexedNode) {
 853                                 LoadIndexedNode loadIndexed = (LoadIndexedNode) usage;
 854                                 debug.dump(DebugContext.INFO_LEVEL, snippetCopy, "Before replacing %s", loadIndexed);
 855                                 LoadSnippetVarargParameterNode loadSnippetParameter = snippetCopy.add(
 856                                                 new LoadSnippetVarargParameterNode(params, loadIndexed.index(), loadIndexed.stamp(NodeView.DEFAULT)));
 857                                 snippetCopy.replaceFixedWithFixed(loadIndexed, loadSnippetParameter);
 858                                 debug.dump(DebugContext.INFO_LEVEL, snippetCopy, "After replacing %s", loadIndexed);
 859                             } else if (usage instanceof StoreIndexedNode) {
 860                                 /*
 861                                  * The template lowering doesn't really treat this as an array so
 862                                  * you can't store back into the varargs. Allocate your own array if
 863                                  * you really need this and EA should eliminate it.
 864                                  */
 865                                 throw new GraalError("Can't store into VarargsParameter array");
 866                             }
 867                         }
 868                     }
 869                 } else {
 870                     ParameterNode local = snippetCopy.getParameter(i);
 871                     if (local == null) {
 872                         // Parameter value was eliminated
 873                         parameters[i] = UNUSED_PARAMETER;
 874                     } else {
 875                         parameters[i] = local;
 876                     }
 877                 }
 878             }
 879 
 880             explodeLoops(snippetCopy, phaseContext);
 881 
 882             GuardsStage guardsStage = args.cacheKey.guardsStage;
 883             // Perform lowering on the snippet
 884             if (!guardsStage.allowsFloatingGuards()) {
 885                 new GuardLoweringPhase().apply(snippetCopy, null);
 886             }
 887             snippetCopy.setGuardsStage(guardsStage);
 888             try (DebugContext.Scope s = debug.scope("LoweringSnippetTemplate", snippetCopy)) {
 889                 new LoweringPhase(new CanonicalizerPhase(), args.cacheKey.loweringStage).apply(snippetCopy, phaseContext);
 890             } catch (Throwable e) {
 891                 throw debug.handle(e);
 892             }
 893 
 894             ArrayList<StateSplit> curSideEffectNodes = new ArrayList<>();
 895             ArrayList<DeoptimizingNode> curDeoptNodes = new ArrayList<>();
 896             ArrayList<ValueNode> curPlaceholderStampedNodes = new ArrayList<>();
 897             for (Node node : snippetCopy.getNodes()) {
 898                 if (node instanceof ValueNode) {
 899                     ValueNode valueNode = (ValueNode) node;
 900                     if (valueNode.stamp(NodeView.DEFAULT) == PlaceholderStamp.singleton()) {
 901                         curPlaceholderStampedNodes.add(valueNode);
 902                     }
 903                 }
 904 
 905                 if (node instanceof StateSplit) {
 906                     StateSplit stateSplit = (StateSplit) node;
 907                     FrameState frameState = stateSplit.stateAfter();
 908                     if (stateSplit.hasSideEffect()) {
 909                         curSideEffectNodes.add((StateSplit) node);
 910                     }
 911                     if (frameState != null) {
 912                         stateSplit.setStateAfter(null);
 913                     }
 914                 }
 915                 if (node instanceof DeoptimizingNode) {
 916                     DeoptimizingNode deoptNode = (DeoptimizingNode) node;
 917                     if (deoptNode.canDeoptimize()) {
 918                         curDeoptNodes.add(deoptNode);
 919                     }
 920                 }
 921             }
 922 
 923             new DeadCodeEliminationPhase(Required).apply(snippetCopy);
 924 
 925             assert checkAllVarargPlaceholdersAreDeleted(parameterCount, placeholders);
 926 
 927             new FloatingReadPhase(true, true).apply(snippetCopy);
 928             new RemoveValueProxyPhase().apply(snippetCopy);
 929 
 930             MemoryAnchorNode anchor = snippetCopy.add(new MemoryAnchorNode());
 931             snippetCopy.start().replaceAtUsages(InputType.Memory, anchor);
 932 
 933             this.snippet = snippetCopy;
 934 
 935             StartNode entryPointNode = snippet.start();
 936             if (anchor.hasNoUsages()) {
 937                 anchor.safeDelete();
 938                 this.memoryAnchor = null;
 939             } else {
 940                 // Find out if all the return memory maps point to the anchor (i.e., there's no kill
 941                 // anywhere)
 942                 boolean needsMemoryMaps = false;
 943                 for (ReturnNode retNode : snippet.getNodes(ReturnNode.TYPE)) {
 944                     MemoryMapNode memoryMap = retNode.getMemoryMap();
 945                     if (memoryMap.getLocations().size() > 1 || memoryMap.getLastLocationAccess(LocationIdentity.any()) != anchor) {
 946                         needsMemoryMaps = true;
 947                         break;
 948                     }
 949                 }
 950                 boolean needsAnchor;
 951                 if (needsMemoryMaps) {
 952                     needsAnchor = true;
 953                 } else {
 954                     // Check that all those memory maps where the only usages of the anchor
 955                     needsAnchor = anchor.usages().filter(isNotA(MemoryMapNode.class)).isNotEmpty();
 956                     // Remove the useless memory map
 957                     MemoryMapNode memoryMap = null;
 958                     for (ReturnNode retNode : snippet.getNodes(ReturnNode.TYPE)) {
 959                         if (memoryMap == null) {
 960                             memoryMap = retNode.getMemoryMap();
 961                         } else {
 962                             assert memoryMap == retNode.getMemoryMap();
 963                         }
 964                         retNode.setMemoryMap(null);
 965                     }
 966                     memoryMap.safeDelete();
 967                 }
 968                 if (needsAnchor) {
 969                     snippetCopy.addAfterFixed(snippetCopy.start(), anchor);
 970                     this.memoryAnchor = anchor;
 971                 } else {
 972                     anchor.safeDelete();
 973                     this.memoryAnchor = null;
 974                 }
 975             }
 976             debug.dump(DebugContext.INFO_LEVEL, snippet, "SnippetTemplate after fixing memory anchoring");
 977 
 978             List<ReturnNode> returnNodes = snippet.getNodes(ReturnNode.TYPE).snapshot();
 979             if (returnNodes.isEmpty()) {
 980                 this.returnNode = null;
 981             } else if (returnNodes.size() == 1) {
 982                 this.returnNode = returnNodes.get(0);
 983             } else {
 984                 AbstractMergeNode merge = snippet.add(new MergeNode());
 985                 List<MemoryMapNode> memMaps = new ArrayList<>();
 986                 for (ReturnNode retNode : returnNodes) {
 987                     MemoryMapNode memoryMapNode = retNode.getMemoryMap();
 988                     if (memoryMapNode != null) {
 989                         memMaps.add(memoryMapNode);
 990                     }
 991                 }
 992 
 993                 ValueNode returnValue = InliningUtil.mergeReturns(merge, returnNodes);
 994                 this.returnNode = snippet.add(new ReturnNode(returnValue));
 995                 if (!memMaps.isEmpty()) {
 996                     MemoryMapImpl mmap = FloatingReadPhase.mergeMemoryMaps(merge, memMaps);
 997                     MemoryMapNode memoryMap = snippet.unique(new MemoryMapNode(mmap.getMap()));
 998                     this.returnNode.setMemoryMap(memoryMap);
 999                     for (MemoryMapNode mm : memMaps) {
1000                         if (mm != memoryMap && mm.isAlive()) {
1001                             assert mm.hasNoUsages();
1002                             GraphUtil.killWithUnusedFloatingInputs(mm);
1003                         }
1004                     }
1005                 }
1006                 merge.setNext(this.returnNode);
1007             }
1008 
1009             assert verifyIntrinsicsProcessed(snippetCopy);
1010 
1011             this.sideEffectNodes = curSideEffectNodes;
1012             this.deoptNodes = curDeoptNodes;
1013             this.placeholderStampedNodes = curPlaceholderStampedNodes;
1014 
1015             nodes = new ArrayList<>(snippet.getNodeCount());
1016             for (Node node : snippet.getNodes()) {
1017                 if (node != entryPointNode && node != entryPointNode.stateAfter()) {
1018                     nodes.add(node);
1019                 }
1020             }
1021 
1022             if (debug.areMetricsEnabled()) {
1023                 DebugContext.counter("SnippetTemplateNodeCount[%#s]", args).add(debug, nodes.size());
1024             }
1025             debug.dump(DebugContext.INFO_LEVEL, snippet, "SnippetTemplate final state");
1026             this.snippet.freeze();
1027 
1028         } catch (Throwable ex) {
1029             throw debug.handle(ex);
1030         }
1031     }
1032 
1033     private static boolean verifyIntrinsicsProcessed(StructuredGraph snippetCopy) {
1034         for (MethodCallTargetNode target : snippetCopy.getNodes(MethodCallTargetNode.TYPE)) {
1035             ResolvedJavaMethod targetMethod = target.targetMethod();
1036             if (targetMethod != null) {
1037                 assert targetMethod.getAnnotation(Fold.class) == null && targetMethod.getAnnotation(NodeIntrinsic.class) == null : "plugin should have been processed";
1038             }
1039         }
1040         return true;
1041     }
1042 
1043     public static void explodeLoops(final StructuredGraph snippetCopy, PhaseContext phaseContext) {
1044         // Do any required loop explosion
1045         boolean exploded = false;
1046         do {
1047             exploded = false;
1048             ExplodeLoopNode explodeLoop = snippetCopy.getNodes().filter(ExplodeLoopNode.class).first();
1049             if (explodeLoop != null) { // Earlier canonicalization may have removed the loop
1050                 // altogether
1051                 LoopBeginNode loopBegin = explodeLoop.findLoopBegin();
1052                 if (loopBegin != null) {
1053                     LoopEx loop = new LoopsData(snippetCopy).loop(loopBegin);
1054                     Mark mark = snippetCopy.getMark();
1055                     LoopTransformations.fullUnroll(loop, phaseContext, new CanonicalizerPhase());
1056                     new CanonicalizerPhase().applyIncremental(snippetCopy, phaseContext, mark, false);
1057                     loop.deleteUnusedNodes();
1058                 }
1059                 GraphUtil.removeFixedWithUnusedInputs(explodeLoop);
1060                 exploded = true;
1061             }
1062         } while (exploded);
1063     }
1064 
1065     protected Object[] getConstantArgs(Arguments args) {
1066         Object[] constantArgs = args.values.clone();
1067         for (int i = 0; i < args.info.getParameterCount(); i++) {
1068             if (!args.info.isConstantParameter(i)) {
1069                 constantArgs[i] = null;
1070             } else {
1071                 assert constantArgs[i] != null : "Can't pass raw null through as argument";
1072             }
1073         }
1074         return constantArgs;
1075     }
1076 
1077     private static boolean checkAllVarargPlaceholdersAreDeleted(int parameterCount, VarargsPlaceholderNode[] placeholders) {
1078         for (int i = 0; i < parameterCount; i++) {
1079             if (placeholders[i] != null) {
1080                 assert placeholders[i].isDeleted() : placeholders[i];
1081             }
1082         }
1083         return true;
1084     }
1085 
1086     private static boolean checkConstantArgument(MetaAccessProvider metaAccess, final ResolvedJavaMethod method, Signature signature, int paramIndex, String name, Object arg, JavaKind kind) {
1087         ResolvedJavaType type = signature.getParameterType(paramIndex, method.getDeclaringClass()).resolve(method.getDeclaringClass());
1088         if (metaAccess.lookupJavaType(WordBase.class).isAssignableFrom(type)) {
1089             assert arg instanceof JavaConstant : method + ": word constant parameters must be passed boxed in a Constant value: " + arg;
1090             return true;
1091         }
1092         if (kind != JavaKind.Object) {
1093             assert arg != null && kind.toBoxedJavaClass() == arg.getClass() : method + ": wrong value kind for " + name + ": expected " + kind + ", got " +
1094                             (arg == null ? "null" : arg.getClass().getSimpleName());
1095         }
1096         return true;
1097     }
1098 
1099     private static boolean checkVarargs(MetaAccessProvider metaAccess, final ResolvedJavaMethod method, Signature signature, int i, String name, Varargs varargs) {
1100         ResolvedJavaType type = (ResolvedJavaType) signature.getParameterType(i, method.getDeclaringClass());
1101         assert type.isArray() : "varargs parameter must be an array type";
1102         assert type.getComponentType().isAssignableFrom(metaAccess.lookupJavaType(varargs.componentType)) : "componentType for " + name + " not matching " + type.toJavaName() + " instance: " +
1103                         varargs.componentType;
1104         return true;
1105     }
1106 
1107     /**
1108      * The graph built from the snippet method.
1109      */
1110     private final StructuredGraph snippet;
1111 
1112     private final SnippetInfo info;
1113 
1114     /**
1115      * The named parameters of this template that must be bound to values during instantiation. For
1116      * a parameter that is still live after specialization, the value in this map is either a
1117      * {@link ParameterNode} instance or a {@link ParameterNode} array. For an eliminated parameter,
1118      * the value is identical to the key.
1119      */
1120     private final Object[] parameters;
1121 
1122     /**
1123      * The return node (if any) of the snippet.
1124      */
1125     private final ReturnNode returnNode;
1126 
1127     /**
1128      * The memory anchor (if any) of the snippet.
1129      */
1130     private final MemoryAnchorNode memoryAnchor;
1131 
1132     /**
1133      * Nodes that inherit the {@link StateSplit#stateAfter()} from the replacee during
1134      * instantiation.
1135      */
1136     private final ArrayList<StateSplit> sideEffectNodes;
1137 
1138     /**
1139      * Nodes that inherit a deoptimization {@link FrameState} from the replacee during
1140      * instantiation.
1141      */
1142     private final ArrayList<DeoptimizingNode> deoptNodes;
1143 
1144     /**
1145      * Nodes that have a stamp originating from a {@link Placeholder}.
1146      */
1147     private final ArrayList<ValueNode> placeholderStampedNodes;
1148 
1149     /**
1150      * The nodes to be inlined when this specialization is instantiated.
1151      */
1152     private final ArrayList<Node> nodes;
1153 
1154     /**
1155      * Gets the instantiation-time bindings to this template's parameters.
1156      *
1157      * @return the map that will be used to bind arguments to parameters when inlining this template
1158      */
1159     private EconomicMap<Node, Node> bind(StructuredGraph replaceeGraph, MetaAccessProvider metaAccess, Arguments args) {
1160         EconomicMap<Node, Node> replacements = EconomicMap.create(Equivalence.IDENTITY);
1161         assert args.info.getParameterCount() == parameters.length : "number of args (" + args.info.getParameterCount() + ") != number of parameters (" + parameters.length + ")";
1162         for (int i = 0; i < parameters.length; i++) {
1163             Object parameter = parameters[i];
1164             assert parameter != null : this + " has no parameter named " + args.info.getParameterName(i);
1165             Object argument = args.values[i];
1166             if (parameter instanceof ParameterNode) {
1167                 if (argument instanceof ValueNode) {
1168                     replacements.put((ParameterNode) parameter, (ValueNode) argument);
1169                 } else {
1170                     JavaKind kind = ((ParameterNode) parameter).getStackKind();
1171                     assert argument != null || kind == JavaKind.Object : this + " cannot accept null for non-object parameter named " + args.info.getParameterName(i);
1172                     JavaConstant constant = forBoxed(argument, kind);
1173                     replacements.put((ParameterNode) parameter, ConstantNode.forConstant(constant, metaAccess, replaceeGraph));
1174                 }
1175             } else if (parameter instanceof ParameterNode[]) {
1176                 ParameterNode[] params = (ParameterNode[]) parameter;
1177                 Varargs varargs = (Varargs) argument;
1178                 int length = params.length;
1179                 List<?> list = null;
1180                 Object array = null;
1181                 if (varargs.value instanceof List) {
1182                     list = (List<?>) varargs.value;
1183                     assert list.size() == length : length + " != " + list.size();
1184                 } else {
1185                     array = varargs.value;
1186                     assert array != null && array.getClass().isArray();
1187                     assert Array.getLength(array) == length : length + " != " + Array.getLength(array);
1188                 }
1189 
1190                 for (int j = 0; j < length; j++) {
1191                     ParameterNode param = params[j];
1192                     assert param != null;
1193                     Object value = list != null ? list.get(j) : Array.get(array, j);
1194                     if (value instanceof ValueNode) {
1195                         replacements.put(param, (ValueNode) value);
1196                     } else {
1197                         JavaConstant constant = forBoxed(value, param.getStackKind());
1198                         ConstantNode element = ConstantNode.forConstant(constant, metaAccess, replaceeGraph);
1199                         replacements.put(param, element);
1200                     }
1201                 }
1202             } else {
1203                 assert parameter.equals(CONSTANT_PARAMETER) || parameter.equals(UNUSED_PARAMETER) : "unexpected entry for parameter: " + args.info.getParameterName(i) + " -> " + parameter;
1204             }
1205         }
1206         return replacements;
1207     }
1208 
1209     /**
1210      * Converts a Java boxed value to a {@link JavaConstant} of the right kind. This adjusts for the
1211      * limitation that a {@link Local}'s kind is a {@linkplain JavaKind#getStackKind() stack kind}
1212      * and so cannot be used for re-boxing primitives smaller than an int.
1213      *
1214      * @param argument a Java boxed value
1215      * @param localKind the kind of the {@link Local} to which {@code argument} will be bound
1216      */
1217     protected JavaConstant forBoxed(Object argument, JavaKind localKind) {
1218         assert localKind == localKind.getStackKind();
1219         if (localKind == JavaKind.Int) {
1220             return JavaConstant.forBoxedPrimitive(argument);
1221         }
1222         return snippetReflection.forBoxed(localKind, argument);
1223     }
1224 
1225     /**
1226      * Logic for replacing a snippet-lowered node at its usages with the return value of the
1227      * snippet. An alternative to the {@linkplain SnippetTemplate#DEFAULT_REPLACER default}
1228      * replacement logic can be used to handle mismatches between the stamp of the node being
1229      * lowered and the stamp of the snippet's return value.
1230      */
1231     public interface UsageReplacer {
1232         /**
1233          * Replaces all usages of {@code oldNode} with direct or indirect usages of {@code newNode}.
1234          */
1235         void replace(ValueNode oldNode, ValueNode newNode);
1236     }
1237 
1238     /**
1239      * Represents the default {@link UsageReplacer usage replacer} logic which simply delegates to
1240      * {@link Node#replaceAtUsages(Node)}.
1241      */
1242     public static final UsageReplacer DEFAULT_REPLACER = new UsageReplacer() {
1243 
1244         @Override
1245         public void replace(ValueNode oldNode, ValueNode newNode) {
1246             if (newNode == null) {
1247                 assert oldNode.hasNoUsages();
1248             } else {
1249                 oldNode.replaceAtUsages(newNode);
1250             }
1251         }
1252     };
1253 
1254     private boolean assertSnippetKills(ValueNode replacee) {
1255         if (!replacee.graph().isAfterFloatingReadPhase()) {
1256             // no floating reads yet, ignore locations created while lowering
1257             return true;
1258         }
1259         if (returnNode == null) {
1260             // The snippet terminates control flow
1261             return true;
1262         }
1263         MemoryMapNode memoryMap = returnNode.getMemoryMap();
1264         if (memoryMap == null || memoryMap.isEmpty()) {
1265             // there are no kills in the snippet graph
1266             return true;
1267         }
1268 
1269         EconomicSet<LocationIdentity> kills = EconomicSet.create(Equivalence.DEFAULT);
1270         kills.addAll(memoryMap.getLocations());
1271 
1272         if (replacee instanceof MemoryCheckpoint.Single) {
1273             // check if some node in snippet graph also kills the same location
1274             LocationIdentity locationIdentity = ((MemoryCheckpoint.Single) replacee).getLocationIdentity();
1275             if (locationIdentity.isAny()) {
1276                 assert !(memoryMap.getLastLocationAccess(any()) instanceof MemoryAnchorNode) : replacee + " kills ANY_LOCATION, but snippet does not";
1277                 // if the replacee kills ANY_LOCATION, the snippet can kill arbitrary locations
1278                 return true;
1279             }
1280             assert kills.contains(locationIdentity) : replacee + " kills " + locationIdentity + ", but snippet doesn't contain a kill to this location";
1281             kills.remove(locationIdentity);
1282         }
1283         assert !(replacee instanceof MemoryCheckpoint.Multi) : replacee + " multi not supported (yet)";
1284 
1285         // remove ANY_LOCATION if it's just a kill by the start node
1286         if (memoryMap.getLastLocationAccess(any()) instanceof MemoryAnchorNode) {
1287             kills.remove(any());
1288         }
1289 
1290         // node can only lower to a ANY_LOCATION kill if the replacee also kills ANY_LOCATION
1291         assert !kills.contains(any()) : "snippet graph contains a kill to ANY_LOCATION, but replacee (" + replacee + ") doesn't kill ANY_LOCATION.  kills: " + kills;
1292 
1293         /*
1294          * Kills to private locations are safe, since there can be no floating read to these
1295          * locations except reads that are introduced by the snippet itself or related snippets in
1296          * the same lowering round. These reads are anchored to a MemoryAnchor at the beginning of
1297          * their snippet, so they can not float above a kill in another instance of the same
1298          * snippet.
1299          */
1300         for (LocationIdentity p : this.info.privateLocations) {
1301             kills.remove(p);
1302         }
1303 
1304         assert kills.isEmpty() : "snippet graph kills non-private locations " + kills + " that replacee (" + replacee + ") doesn't kill";
1305         return true;
1306     }
1307 
1308     private static class MemoryInputMap implements MemoryMap {
1309 
1310         private final LocationIdentity locationIdentity;
1311         private final MemoryNode lastLocationAccess;
1312 
1313         MemoryInputMap(ValueNode replacee) {
1314             if (replacee instanceof MemoryAccess) {
1315                 MemoryAccess access = (MemoryAccess) replacee;
1316                 locationIdentity = access.getLocationIdentity();
1317                 lastLocationAccess = access.getLastLocationAccess();
1318             } else {
1319                 locationIdentity = null;
1320                 lastLocationAccess = null;
1321             }
1322         }
1323 
1324         @Override
1325         public MemoryNode getLastLocationAccess(LocationIdentity location) {
1326             if (locationIdentity != null && locationIdentity.equals(location)) {
1327                 return lastLocationAccess;
1328             } else {
1329                 return null;
1330             }
1331         }
1332 
1333         @Override
1334         public Collection<LocationIdentity> getLocations() {
1335             if (locationIdentity == null) {
1336                 return Collections.emptySet();
1337             } else {
1338                 return Collections.singleton(locationIdentity);
1339             }
1340         }
1341     }
1342 
1343     private class MemoryOutputMap extends MemoryInputMap {
1344 
1345         private final UnmodifiableEconomicMap<Node, Node> duplicates;
1346 
1347         MemoryOutputMap(ValueNode replacee, UnmodifiableEconomicMap<Node, Node> duplicates) {
1348             super(replacee);
1349             this.duplicates = duplicates;
1350         }
1351 
1352         @Override
1353         public MemoryNode getLastLocationAccess(LocationIdentity locationIdentity) {
1354             MemoryMapNode memoryMap = returnNode.getMemoryMap();
1355             assert memoryMap != null : "no memory map stored for this snippet graph (snippet doesn't have a ReturnNode?)";
1356             MemoryNode lastLocationAccess = memoryMap.getLastLocationAccess(locationIdentity);
1357             assert lastLocationAccess != null : locationIdentity;
1358             if (lastLocationAccess == memoryAnchor) {
1359                 return super.getLastLocationAccess(locationIdentity);
1360             } else {
1361                 return (MemoryNode) duplicates.get(ValueNodeUtil.asNode(lastLocationAccess));
1362             }
1363         }
1364 
1365         @Override
1366         public Collection<LocationIdentity> getLocations() {
1367             return returnNode.getMemoryMap().getLocations();
1368         }
1369     }
1370 
1371     private void rewireMemoryGraph(ValueNode replacee, UnmodifiableEconomicMap<Node, Node> duplicates) {
1372         if (replacee.graph().isAfterFloatingReadPhase()) {
1373             // rewire outgoing memory edges
1374             replaceMemoryUsages(replacee, new MemoryOutputMap(replacee, duplicates));
1375 
1376             if (returnNode != null) {
1377                 ReturnNode ret = (ReturnNode) duplicates.get(returnNode);
1378                 if (ret != null) {
1379                     MemoryMapNode memoryMap = ret.getMemoryMap();
1380                     if (memoryMap != null) {
1381                         ret.setMemoryMap(null);
1382                         memoryMap.safeDelete();
1383                     }
1384                 }
1385             }
1386             if (memoryAnchor != null) {
1387                 // rewire incoming memory edges
1388                 MemoryAnchorNode memoryDuplicate = (MemoryAnchorNode) duplicates.get(memoryAnchor);
1389                 replaceMemoryUsages(memoryDuplicate, new MemoryInputMap(replacee));
1390 
1391                 if (memoryDuplicate.hasNoUsages()) {
1392                     if (memoryDuplicate.next() != null) {
1393                         memoryDuplicate.graph().removeFixed(memoryDuplicate);
1394                     } else {
1395                         // this was a dummy memory node used when instantiating pure data-flow
1396                         // snippets: it was not attached to the control flow.
1397                         memoryDuplicate.safeDelete();
1398                     }
1399                 }
1400             }
1401         }
1402     }
1403 
1404     private static LocationIdentity getLocationIdentity(Node node) {
1405         if (node instanceof MemoryAccess) {
1406             return ((MemoryAccess) node).getLocationIdentity();
1407         } else if (node instanceof MemoryProxy) {
1408             return ((MemoryProxy) node).getLocationIdentity();
1409         } else if (node instanceof MemoryPhiNode) {
1410             return ((MemoryPhiNode) node).getLocationIdentity();
1411         } else {
1412             return null;
1413         }
1414     }
1415 
1416     private void replaceMemoryUsages(ValueNode node, MemoryMap map) {
1417         for (Node usage : node.usages().snapshot()) {
1418             if (usage instanceof MemoryMapNode) {
1419                 continue;
1420             }
1421 
1422             LocationIdentity location = getLocationIdentity(usage);
1423             if (location != null) {
1424                 for (Position pos : usage.inputPositions()) {
1425                     if (pos.getInputType() == InputType.Memory && pos.get(usage) == node) {
1426                         MemoryNode replacement = map.getLastLocationAccess(location);
1427                         if (replacement == null) {
1428                             assert mayRemoveLocation || LocationIdentity.any().equals(location) ||
1429                                             CollectionsUtil.anyMatch(info.privateLocations, Predicate.isEqual(location)) : "Snippet " +
1430                                                             info.method.format("%h.%n") + " contains access to the non-private location " +
1431                                                             location + ", but replacee doesn't access this location." + map.getLocations();
1432                         } else {
1433                             pos.set(usage, replacement.asNode());
1434                         }
1435                     }
1436                 }
1437             }
1438         }
1439     }
1440 
1441     /**
1442      * Replaces a given fixed node with this specialized snippet.
1443      *
1444      * @param metaAccess
1445      * @param replacee the node that will be replaced
1446      * @param replacer object that replaces the usages of {@code replacee}
1447      * @param args the arguments to be bound to the flattened positional parameters of the snippet
1448      * @return the map of duplicated nodes (original -&gt; duplicate)
1449      */
1450     @SuppressWarnings("try")
1451     public UnmodifiableEconomicMap<Node, Node> instantiate(MetaAccessProvider metaAccess, FixedNode replacee, UsageReplacer replacer, Arguments args) {
1452         return instantiate(metaAccess, replacee, replacer, args, true);
1453     }
1454 
1455     /**
1456      * Replaces a given fixed node with this specialized snippet.
1457      *
1458      * @param metaAccess
1459      * @param replacee the node that will be replaced
1460      * @param replacer object that replaces the usages of {@code replacee}
1461      * @param args the arguments to be bound to the flattened positional parameters of the snippet
1462      * @param killReplacee is true, the replacee node is deleted
1463      * @return the map of duplicated nodes (original -&gt; duplicate)
1464      */
1465     @SuppressWarnings("try")
1466     public UnmodifiableEconomicMap<Node, Node> instantiate(MetaAccessProvider metaAccess, FixedNode replacee, UsageReplacer replacer, Arguments args, boolean killReplacee) {
1467         DebugContext debug = replacee.getDebug();
1468         assert assertSnippetKills(replacee);
1469         try (DebugCloseable a = args.info.instantiationTimer.start(debug)) {
1470             args.info.instantiationCounter.increment(debug);
1471             // Inline the snippet nodes, replacing parameters with the given args in the process
1472             StartNode entryPointNode = snippet.start();
1473             FixedNode firstCFGNode = entryPointNode.next();
1474             StructuredGraph replaceeGraph = replacee.graph();
1475             EconomicMap<Node, Node> replacements = bind(replaceeGraph, metaAccess, args);
1476             replacements.put(entryPointNode, AbstractBeginNode.prevBegin(replacee));
1477             UnmodifiableEconomicMap<Node, Node> duplicates = inlineSnippet(replacee, debug, replaceeGraph, replacements);
1478 
1479             // Re-wire the control flow graph around the replacee
1480             FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode);
1481             replacee.replaceAtPredecessor(firstCFGNodeDuplicate);
1482 
1483             rewireFrameStates(replacee, duplicates);
1484 
1485             if (replacee instanceof DeoptimizingNode) {
1486                 DeoptimizingNode replaceeDeopt = (DeoptimizingNode) replacee;
1487 
1488                 FrameState stateBefore = null;
1489                 FrameState stateDuring = null;
1490                 FrameState stateAfter = null;
1491                 if (replaceeDeopt.canDeoptimize()) {
1492                     if (replaceeDeopt instanceof DeoptimizingNode.DeoptBefore) {
1493                         stateBefore = ((DeoptimizingNode.DeoptBefore) replaceeDeopt).stateBefore();
1494                     }
1495                     if (replaceeDeopt instanceof DeoptimizingNode.DeoptDuring) {
1496                         stateDuring = ((DeoptimizingNode.DeoptDuring) replaceeDeopt).stateDuring();
1497                     }
1498                     if (replaceeDeopt instanceof DeoptimizingNode.DeoptAfter) {
1499                         stateAfter = ((DeoptimizingNode.DeoptAfter) replaceeDeopt).stateAfter();
1500                     }
1501                 }
1502 
1503                 for (DeoptimizingNode deoptNode : deoptNodes) {
1504                     DeoptimizingNode deoptDup = (DeoptimizingNode) duplicates.get(deoptNode.asNode());
1505                     if (deoptDup.canDeoptimize()) {
1506                         if (deoptDup instanceof DeoptimizingNode.DeoptBefore) {
1507                             ((DeoptimizingNode.DeoptBefore) deoptDup).setStateBefore(stateBefore);
1508                         }
1509                         if (deoptDup instanceof DeoptimizingNode.DeoptDuring) {
1510                             DeoptimizingNode.DeoptDuring deoptDupDuring = (DeoptimizingNode.DeoptDuring) deoptDup;
1511                             if (stateDuring != null) {
1512                                 deoptDupDuring.setStateDuring(stateDuring);
1513                             } else if (stateAfter != null) {
1514                                 deoptDupDuring.computeStateDuring(stateAfter);
1515                             } else if (stateBefore != null) {
1516                                 assert !deoptDupDuring.hasSideEffect() : "can't use stateBefore as stateDuring for state split " + deoptDupDuring;
1517                                 deoptDupDuring.setStateDuring(stateBefore);
1518                             }
1519                         }
1520                         if (deoptDup instanceof DeoptimizingNode.DeoptAfter) {
1521                             DeoptimizingNode.DeoptAfter deoptDupAfter = (DeoptimizingNode.DeoptAfter) deoptDup;
1522                             if (stateAfter != null) {
1523                                 deoptDupAfter.setStateAfter(stateAfter);
1524                             } else {
1525                                 assert !deoptDupAfter.hasSideEffect() : "can't use stateBefore as stateAfter for state split " + deoptDupAfter;
1526                                 deoptDupAfter.setStateAfter(stateBefore);
1527                             }
1528 
1529                         }
1530                     }
1531                 }
1532             }
1533 
1534             updateStamps(replacee, duplicates);
1535 
1536             rewireMemoryGraph(replacee, duplicates);
1537 
1538             // Replace all usages of the replacee with the value returned by the snippet
1539             ValueNode returnValue = null;
1540             if (returnNode != null && !(replacee instanceof ControlSinkNode)) {
1541                 ReturnNode returnDuplicate = (ReturnNode) duplicates.get(returnNode);
1542                 returnValue = returnDuplicate.result();
1543                 if (returnValue == null && replacee.usages().isNotEmpty() && replacee instanceof MemoryCheckpoint) {
1544                     replacer.replace(replacee, null);
1545                 } else {
1546                     assert returnValue != null || replacee.hasNoUsages();
1547                     replacer.replace(replacee, returnValue);
1548                 }
1549                 if (returnDuplicate.isAlive()) {
1550                     FixedNode next = null;
1551                     if (replacee instanceof FixedWithNextNode) {
1552                         FixedWithNextNode fwn = (FixedWithNextNode) replacee;
1553                         next = fwn.next();
1554                         fwn.setNext(null);
1555                     }
1556                     returnDuplicate.replaceAndDelete(next);
1557                 }
1558             }
1559 
1560             if (killReplacee) {
1561                 // Remove the replacee from its graph
1562                 GraphUtil.killCFG(replacee);
1563             }
1564 
1565             debug.dump(DebugContext.DETAILED_LEVEL, replaceeGraph, "After lowering %s with %s", replacee, this);
1566             return duplicates;
1567         }
1568     }
1569 
1570     private UnmodifiableEconomicMap<Node, Node> inlineSnippet(Node replacee, DebugContext debug, StructuredGraph replaceeGraph, EconomicMap<Node, Node> replacements) {
1571         Mark mark = replaceeGraph.getMark();
1572         try (InliningLog.UpdateScope scope = replaceeGraph.getInliningLog().openUpdateScope((oldNode, newNode) -> {
1573             InliningLog log = replaceeGraph.getInliningLog();
1574             if (oldNode == null) {
1575                 log.trackNewCallsite(newNode);
1576             }
1577         })) {
1578             UnmodifiableEconomicMap<Node, Node> duplicates = replaceeGraph.addDuplicates(nodes, snippet, snippet.getNodeCount(), replacements);
1579             if (scope != null) {
1580                 replaceeGraph.getInliningLog().addLog(duplicates, snippet.getInliningLog());
1581             }
1582             NodeSourcePosition position = replacee.getNodeSourcePosition();
1583             InliningUtil.updateSourcePosition(replaceeGraph, duplicates, mark, position, true);
1584             debug.dump(DebugContext.DETAILED_LEVEL, replaceeGraph, "After inlining snippet %s", snippet.method());
1585             return duplicates;
1586         }
1587     }
1588 
1589     private void propagateStamp(Node node) {
1590         if (node instanceof PhiNode) {
1591             PhiNode phi = (PhiNode) node;
1592             if (phi.inferStamp()) {
1593                 for (Node usage : node.usages()) {
1594                     propagateStamp(usage);
1595                 }
1596             }
1597         }
1598     }
1599 
1600     private void updateStamps(ValueNode replacee, UnmodifiableEconomicMap<Node, Node> duplicates) {
1601         for (ValueNode node : placeholderStampedNodes) {
1602             ValueNode dup = (ValueNode) duplicates.get(node);
1603             Stamp replaceeStamp = replacee.stamp(NodeView.DEFAULT);
1604             if (node instanceof Placeholder) {
1605                 Placeholder placeholderDup = (Placeholder) dup;
1606                 placeholderDup.makeReplacement(replaceeStamp);
1607             } else {
1608                 dup.setStamp(replaceeStamp);
1609             }
1610         }
1611         for (ParameterNode paramNode : snippet.getNodes(ParameterNode.TYPE)) {
1612             for (Node usage : paramNode.usages()) {
1613                 Node usageDup = duplicates.get(usage);
1614                 propagateStamp(usageDup);
1615             }
1616         }
1617     }
1618 
1619     /**
1620      * Gets a copy of the specialized graph.
1621      */
1622     public StructuredGraph copySpecializedGraph(DebugContext debugForCopy) {
1623         return (StructuredGraph) snippet.copy(debugForCopy);
1624     }
1625 
1626     /**
1627      * Replaces a given floating node with this specialized snippet.
1628      *
1629      * @param metaAccess
1630      * @param replacee the node that will be replaced
1631      * @param replacer object that replaces the usages of {@code replacee}
1632      * @param tool lowering tool used to insert the snippet into the control-flow
1633      * @param args the arguments to be bound to the flattened positional parameters of the snippet
1634      */
1635     @SuppressWarnings("try")
1636     public void instantiate(MetaAccessProvider metaAccess, FloatingNode replacee, UsageReplacer replacer, LoweringTool tool, Arguments args) {
1637         DebugContext debug = replacee.getDebug();
1638         assert assertSnippetKills(replacee);
1639         try (DebugCloseable a = args.info.instantiationTimer.start(debug)) {
1640             args.info.instantiationCounter.increment(debug);
1641 
1642             // Inline the snippet nodes, replacing parameters with the given args in the process
1643             StartNode entryPointNode = snippet.start();
1644             FixedNode firstCFGNode = entryPointNode.next();
1645             StructuredGraph replaceeGraph = replacee.graph();
1646             EconomicMap<Node, Node> replacements = bind(replaceeGraph, metaAccess, args);
1647             replacements.put(entryPointNode, tool.getCurrentGuardAnchor().asNode());
1648             UnmodifiableEconomicMap<Node, Node> duplicates = inlineSnippet(replacee, debug, replaceeGraph, replacements);
1649 
1650             FixedWithNextNode lastFixedNode = tool.lastFixedNode();
1651             assert lastFixedNode != null && lastFixedNode.isAlive() : replaceeGraph + " lastFixed=" + lastFixedNode;
1652             FixedNode next = lastFixedNode.next();
1653             lastFixedNode.setNext(null);
1654             FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode);
1655             replaceeGraph.addAfterFixed(lastFixedNode, firstCFGNodeDuplicate);
1656 
1657             rewireFrameStates(replacee, duplicates);
1658             updateStamps(replacee, duplicates);
1659 
1660             rewireMemoryGraph(replacee, duplicates);
1661 
1662             // Replace all usages of the replacee with the value returned by the snippet
1663             ReturnNode returnDuplicate = (ReturnNode) duplicates.get(returnNode);
1664             ValueNode returnValue = returnDuplicate.result();
1665             assert returnValue != null || replacee.hasNoUsages();
1666             replacer.replace(replacee, returnValue);
1667 
1668             if (returnDuplicate.isAlive()) {
1669                 returnDuplicate.replaceAndDelete(next);
1670             }
1671 
1672             debug.dump(DebugContext.DETAILED_LEVEL, replaceeGraph, "After lowering %s with %s", replacee, this);
1673         }
1674     }
1675 
1676     /**
1677      * Replaces a given floating node with this specialized snippet.
1678      *
1679      * This snippet must be pure data-flow
1680      *
1681      * @param metaAccess
1682      * @param replacee the node that will be replaced
1683      * @param replacer object that replaces the usages of {@code replacee}
1684      * @param args the arguments to be bound to the flattened positional parameters of the snippet
1685      */
1686     @SuppressWarnings("try")
1687     public void instantiate(MetaAccessProvider metaAccess, FloatingNode replacee, UsageReplacer replacer, Arguments args) {
1688         DebugContext debug = replacee.getDebug();
1689         assert assertSnippetKills(replacee);
1690         try (DebugCloseable a = args.info.instantiationTimer.start(debug)) {
1691             args.info.instantiationCounter.increment(debug);
1692 
1693             // Inline the snippet nodes, replacing parameters with the given args in the process
1694             StartNode entryPointNode = snippet.start();
1695             assert entryPointNode.next() == (memoryAnchor == null ? returnNode : memoryAnchor) : entryPointNode.next();
1696             StructuredGraph replaceeGraph = replacee.graph();
1697             EconomicMap<Node, Node> replacements = bind(replaceeGraph, metaAccess, args);
1698             MemoryAnchorNode anchorDuplicate = null;
1699             if (memoryAnchor != null) {
1700                 anchorDuplicate = replaceeGraph.add(new MemoryAnchorNode());
1701                 replacements.put(memoryAnchor, anchorDuplicate);
1702             }
1703             List<Node> floatingNodes = new ArrayList<>(nodes.size() - 2);
1704             for (Node n : nodes) {
1705                 if (n != entryPointNode && n != returnNode) {
1706                     floatingNodes.add(n);
1707                 }
1708             }
1709             UnmodifiableEconomicMap<Node, Node> duplicates = inlineSnippet(replacee, debug, replaceeGraph, replacements);
1710 
1711             rewireFrameStates(replacee, duplicates);
1712             updateStamps(replacee, duplicates);
1713 
1714             rewireMemoryGraph(replacee, duplicates);
1715             assert anchorDuplicate == null || anchorDuplicate.isDeleted();
1716 
1717             // Replace all usages of the replacee with the value returned by the snippet
1718             ValueNode returnValue = (ValueNode) duplicates.get(returnNode.result());
1719             replacer.replace(replacee, returnValue);
1720 
1721             debug.dump(DebugContext.DETAILED_LEVEL, replaceeGraph, "After lowering %s with %s", replacee, this);
1722         }
1723     }
1724 
1725     protected void rewireFrameStates(ValueNode replacee, UnmodifiableEconomicMap<Node, Node> duplicates) {
1726         if (replacee instanceof StateSplit) {
1727             for (StateSplit sideEffectNode : sideEffectNodes) {
1728                 assert ((StateSplit) replacee).hasSideEffect();
1729                 Node sideEffectDup = duplicates.get(sideEffectNode.asNode());
1730                 ((StateSplit) sideEffectDup).setStateAfter(((StateSplit) replacee).stateAfter());
1731             }
1732         }
1733     }
1734 
1735     @Override
1736     public String toString() {
1737         StringBuilder buf = new StringBuilder(snippet.toString()).append('(');
1738         String sep = "";
1739         for (int i = 0; i < parameters.length; i++) {
1740             String name = "[" + i + "]";
1741             Object value = parameters[i];
1742             buf.append(sep);
1743             sep = ", ";
1744             if (value == null) {
1745                 buf.append("<null> ").append(name);
1746             } else if (value.equals(UNUSED_PARAMETER)) {
1747                 buf.append("<unused> ").append(name);
1748             } else if (value.equals(CONSTANT_PARAMETER)) {
1749                 buf.append("<constant> ").append(name);
1750             } else if (value instanceof ParameterNode) {
1751                 ParameterNode param = (ParameterNode) value;
1752                 buf.append(param.getStackKind().getJavaName()).append(' ').append(name);
1753             } else {
1754                 ParameterNode[] params = (ParameterNode[]) value;
1755                 String kind = params.length == 0 ? "?" : params[0].getStackKind().getJavaName();
1756                 buf.append(kind).append('[').append(params.length).append("] ").append(name);
1757             }
1758         }
1759         return buf.append(')').toString();
1760     }
1761 
1762     private static boolean checkTemplate(MetaAccessProvider metaAccess, Arguments args, ResolvedJavaMethod method, Signature signature) {
1763         int offset = args.info.hasReceiver() ? 1 : 0;
1764         for (int i = offset; i < args.info.getParameterCount(); i++) {
1765             if (args.info.isConstantParameter(i)) {
1766                 JavaKind kind = signature.getParameterKind(i - offset);
1767                 assert checkConstantArgument(metaAccess, method, signature, i - offset, args.info.getParameterName(i), args.values[i], kind);
1768 
1769             } else if (args.info.isVarargsParameter(i)) {
1770                 assert args.values[i] instanceof Varargs;
1771                 Varargs varargs = (Varargs) args.values[i];
1772                 assert checkVarargs(metaAccess, method, signature, i, args.info.getParameterName(i), varargs);
1773             }
1774         }
1775         return true;
1776     }
1777 
1778     public void setMayRemoveLocation(boolean mayRemoveLocation) {
1779         this.mayRemoveLocation = mayRemoveLocation;
1780     }
1781 }