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