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