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 -> 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 -> 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 }