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