1 /* 2 * Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 package org.graalvm.compiler.nodes; 26 27 import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE; 28 29 import java.util.ArrayList; 30 import java.util.Collections; 31 import java.util.EnumSet; 32 import java.util.Iterator; 33 import java.util.List; 34 import java.util.SortedSet; 35 import java.util.TreeSet; 36 import java.util.concurrent.atomic.AtomicLong; 37 import java.util.function.Consumer; 38 import java.util.stream.Collectors; 39 40 import jdk.internal.vm.compiler.collections.EconomicMap; 41 import jdk.internal.vm.compiler.collections.EconomicSet; 42 import jdk.internal.vm.compiler.collections.Equivalence; 43 import jdk.internal.vm.compiler.collections.UnmodifiableEconomicMap; 44 import org.graalvm.compiler.api.replacements.MethodSubstitution; 45 import org.graalvm.compiler.api.replacements.Snippet; 46 import org.graalvm.compiler.core.common.CancellationBailoutException; 47 import org.graalvm.compiler.core.common.CompilationIdentifier; 48 import org.graalvm.compiler.core.common.GraalOptions; 49 import org.graalvm.compiler.core.common.cfg.BlockMap; 50 import org.graalvm.compiler.core.common.type.Stamp; 51 import org.graalvm.compiler.debug.DebugContext; 52 import org.graalvm.compiler.debug.JavaMethodContext; 53 import org.graalvm.compiler.debug.TTY; 54 import org.graalvm.compiler.graph.Graph; 55 import org.graalvm.compiler.graph.Node; 56 import org.graalvm.compiler.graph.NodeMap; 57 import org.graalvm.compiler.graph.NodeSourcePosition; 58 import org.graalvm.compiler.nodes.calc.FloatingNode; 59 import org.graalvm.compiler.nodes.cfg.Block; 60 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph; 61 import org.graalvm.compiler.nodes.java.MethodCallTargetNode; 62 import org.graalvm.compiler.nodes.spi.VirtualizableAllocation; 63 import org.graalvm.compiler.nodes.util.GraphUtil; 64 import org.graalvm.compiler.options.OptionValues; 65 66 import jdk.vm.ci.code.BytecodeFrame; 67 import jdk.vm.ci.meta.Assumptions; 68 import jdk.vm.ci.meta.Assumptions.Assumption; 69 import jdk.vm.ci.meta.DefaultProfilingInfo; 70 import jdk.vm.ci.meta.JavaMethod; 71 import jdk.vm.ci.meta.ProfilingInfo; 72 import jdk.vm.ci.meta.ResolvedJavaField; 73 import jdk.vm.ci.meta.ResolvedJavaMethod; 74 import jdk.vm.ci.meta.SpeculationLog; 75 import jdk.vm.ci.meta.TriState; 76 import jdk.vm.ci.runtime.JVMCICompiler; 77 78 /** 79 * A graph that contains at least one distinguished node : the {@link #start() start} node. This 80 * node is the start of the control flow of the graph. 81 */ 82 public final class StructuredGraph extends Graph implements JavaMethodContext { 83 84 /** 85 * The different stages of the compilation of a {@link Graph} regarding the status of 86 * {@link GuardNode guards}, {@link DeoptimizingNode deoptimizations} and {@link FrameState 87 * frame states}. The stage of a graph progresses monotonously. 88 */ 89 public enum GuardsStage { 90 /** 91 * During this stage, there can be {@link FloatingNode floating} {@link DeoptimizingNode} 92 * such as {@link GuardNode GuardNodes}. New {@link DeoptimizingNode DeoptimizingNodes} can 93 * be introduced without constraints. {@link FrameState} nodes are associated with 94 * {@link StateSplit} nodes. 95 */ 96 FLOATING_GUARDS, 97 /** 98 * During this stage, all {@link DeoptimizingNode DeoptimizingNodes} must be 99 * {@link FixedNode fixed} but new {@link DeoptimizingNode DeoptimizingNodes} can still be 100 * introduced. {@link FrameState} nodes are still associated with {@link StateSplit} nodes. 101 */ 102 FIXED_DEOPTS, 103 /** 104 * During this stage, all {@link DeoptimizingNode DeoptimizingNodes} must be 105 * {@link FixedNode fixed}. New {@link DeoptimizingNode DeoptimizingNodes} can not be 106 * introduced any more. {@link FrameState} nodes are now associated with 107 * {@link DeoptimizingNode} nodes. 108 */ 109 AFTER_FSA; 110 allowsFloatingGuards()111 public boolean allowsFloatingGuards() { 112 return this == FLOATING_GUARDS; 113 } 114 allowsGuardInsertion()115 public boolean allowsGuardInsertion() { 116 return this.ordinal() <= FIXED_DEOPTS.ordinal(); 117 } 118 areFrameStatesAtDeopts()119 public boolean areFrameStatesAtDeopts() { 120 return this == AFTER_FSA; 121 } 122 areFrameStatesAtSideEffects()123 public boolean areFrameStatesAtSideEffects() { 124 return !this.areFrameStatesAtDeopts(); 125 } 126 areDeoptsFixed()127 public boolean areDeoptsFixed() { 128 return this.ordinal() >= FIXED_DEOPTS.ordinal(); 129 } 130 requiresValueProxies()131 public boolean requiresValueProxies() { 132 return this != AFTER_FSA; 133 } 134 } 135 136 /** 137 * Constants denoting whether or not {@link Assumption}s can be made while processing a graph. 138 */ 139 public enum AllowAssumptions { 140 YES, 141 NO; 142 ifTrue(boolean flag)143 public static AllowAssumptions ifTrue(boolean flag) { 144 return flag ? YES : NO; 145 } 146 ifNonNull(Assumptions assumptions)147 public static AllowAssumptions ifNonNull(Assumptions assumptions) { 148 return assumptions != null ? YES : NO; 149 } 150 } 151 152 public static class ScheduleResult { 153 private final ControlFlowGraph cfg; 154 private final NodeMap<Block> nodeToBlockMap; 155 private final BlockMap<List<Node>> blockToNodesMap; 156 ScheduleResult(ControlFlowGraph cfg, NodeMap<Block> nodeToBlockMap, BlockMap<List<Node>> blockToNodesMap)157 public ScheduleResult(ControlFlowGraph cfg, NodeMap<Block> nodeToBlockMap, BlockMap<List<Node>> blockToNodesMap) { 158 this.cfg = cfg; 159 this.nodeToBlockMap = nodeToBlockMap; 160 this.blockToNodesMap = blockToNodesMap; 161 } 162 getCFG()163 public ControlFlowGraph getCFG() { 164 return cfg; 165 } 166 getNodeToBlockMap()167 public NodeMap<Block> getNodeToBlockMap() { 168 return nodeToBlockMap; 169 } 170 getBlockToNodesMap()171 public BlockMap<List<Node>> getBlockToNodesMap() { 172 return blockToNodesMap; 173 } 174 nodesFor(Block block)175 public List<Node> nodesFor(Block block) { 176 return blockToNodesMap.get(block); 177 } 178 } 179 180 /** 181 * Object used to create a {@link StructuredGraph}. 182 */ 183 public static class Builder { 184 private String name; 185 private final Assumptions assumptions; 186 private SpeculationLog speculationLog; 187 private ResolvedJavaMethod rootMethod; 188 private CompilationIdentifier compilationId = CompilationIdentifier.INVALID_COMPILATION_ID; 189 private int entryBCI = JVMCICompiler.INVOCATION_ENTRY_BCI; 190 private boolean useProfilingInfo = true; 191 private boolean recordInlinedMethods = true; 192 private boolean trackNodeSourcePosition; 193 private final OptionValues options; 194 private Cancellable cancellable = null; 195 private final DebugContext debug; 196 private NodeSourcePosition callerContext; 197 private boolean isSubstitution; 198 199 /** 200 * Creates a builder for a graph. 201 */ Builder(OptionValues options, DebugContext debug, AllowAssumptions allowAssumptions)202 public Builder(OptionValues options, DebugContext debug, AllowAssumptions allowAssumptions) { 203 this.options = options; 204 this.debug = debug; 205 this.assumptions = allowAssumptions == AllowAssumptions.YES ? new Assumptions() : null; 206 this.trackNodeSourcePosition = Graph.trackNodeSourcePositionDefault(options, debug); 207 } 208 209 /** 210 * Creates a builder for a graph that does not support {@link Assumptions}. 211 */ Builder(OptionValues options, DebugContext debug)212 public Builder(OptionValues options, DebugContext debug) { 213 this.options = options; 214 this.debug = debug; 215 this.assumptions = null; 216 this.trackNodeSourcePosition = Graph.trackNodeSourcePositionDefault(options, debug); 217 } 218 getName()219 public String getName() { 220 return name; 221 } 222 name(String s)223 public Builder name(String s) { 224 this.name = s; 225 return this; 226 } 227 228 /** 229 * @see StructuredGraph#isSubstitution 230 */ setIsSubstitution(boolean flag)231 public Builder setIsSubstitution(boolean flag) { 232 this.isSubstitution = flag; 233 return this; 234 } 235 getMethod()236 public ResolvedJavaMethod getMethod() { 237 return rootMethod; 238 } 239 method(ResolvedJavaMethod method)240 public Builder method(ResolvedJavaMethod method) { 241 this.rootMethod = method; 242 return this; 243 } 244 getDebug()245 public DebugContext getDebug() { 246 return debug; 247 } 248 getSpeculationLog()249 public SpeculationLog getSpeculationLog() { 250 return speculationLog; 251 } 252 speculationLog(SpeculationLog log)253 public Builder speculationLog(SpeculationLog log) { 254 this.speculationLog = log; 255 return this; 256 } 257 getCompilationId()258 public CompilationIdentifier getCompilationId() { 259 return compilationId; 260 } 261 compilationId(CompilationIdentifier id)262 public Builder compilationId(CompilationIdentifier id) { 263 this.compilationId = id; 264 return this; 265 } 266 getCancellable()267 public Cancellable getCancellable() { 268 return cancellable; 269 } 270 cancellable(Cancellable cancel)271 public Builder cancellable(Cancellable cancel) { 272 this.cancellable = cancel; 273 return this; 274 } 275 getEntryBCI()276 public int getEntryBCI() { 277 return entryBCI; 278 } 279 entryBCI(int bci)280 public Builder entryBCI(int bci) { 281 this.entryBCI = bci; 282 return this; 283 } 284 getUseProfilingInfo()285 public boolean getUseProfilingInfo() { 286 return useProfilingInfo; 287 } 288 useProfilingInfo(boolean flag)289 public Builder useProfilingInfo(boolean flag) { 290 this.useProfilingInfo = flag; 291 return this; 292 } 293 getRecordInlinedMethods()294 public boolean getRecordInlinedMethods() { 295 return recordInlinedMethods; 296 } 297 recordInlinedMethods(boolean flag)298 public Builder recordInlinedMethods(boolean flag) { 299 this.recordInlinedMethods = flag; 300 return this; 301 } 302 trackNodeSourcePosition(boolean flag)303 public Builder trackNodeSourcePosition(boolean flag) { 304 if (flag) { 305 this.trackNodeSourcePosition = true; 306 } 307 return this; 308 } 309 callerContext(NodeSourcePosition context)310 public Builder callerContext(NodeSourcePosition context) { 311 this.callerContext = context; 312 return this; 313 } 314 build()315 public StructuredGraph build() { 316 List<ResolvedJavaMethod> inlinedMethods = recordInlinedMethods ? new ArrayList<>() : null; 317 // @formatter:off 318 return new StructuredGraph(name, 319 rootMethod, 320 entryBCI, 321 assumptions, 322 speculationLog, 323 useProfilingInfo, 324 isSubstitution, 325 inlinedMethods, 326 trackNodeSourcePosition, 327 compilationId, 328 options, 329 debug, 330 cancellable, 331 callerContext); 332 // @formatter:on 333 } 334 } 335 336 public static final long INVALID_GRAPH_ID = -1; 337 private static final AtomicLong uniqueGraphIds = new AtomicLong(); 338 339 private StartNode start; 340 private ResolvedJavaMethod rootMethod; 341 private final long graphId; 342 private final CompilationIdentifier compilationId; 343 private final int entryBCI; 344 private GuardsStage guardsStage = GuardsStage.FLOATING_GUARDS; 345 private boolean isAfterFloatingReadPhase = false; 346 private boolean isAfterFixedReadPhase = false; 347 private boolean hasValueProxies = true; 348 private boolean isAfterExpandLogic = false; 349 private FrameStateVerification frameStateVerification; 350 351 /** 352 * Different node types verified during {@linkplain FrameStateVerification}. See 353 * {@linkplain FrameStateVerification} for details. 354 */ 355 public enum FrameStateVerificationFeature { 356 STATE_SPLITS, 357 MERGES, 358 LOOP_BEGINS, 359 LOOP_EXITS 360 } 361 362 /** 363 * The different stages of the compilation of a {@link Graph} regarding the status of 364 * {@linkplain FrameState} verification of {@linkplain AbstractStateSplit} state after. 365 * Verification starts with the mode {@linkplain FrameStateVerification#ALL}, i.e., all state 366 * splits with side-effects, merges and loop exits need a proper state after. The verification 367 * mode progresses monotonously until the {@linkplain FrameStateVerification#NONE} mode is 368 * reached. From there on, no further {@linkplain AbstractStateSplit#stateAfter} verification 369 * happens. 370 */ 371 public enum FrameStateVerification { 372 /** 373 * Verify all {@linkplain AbstractStateSplit} nodes that return {@code true} for 374 * {@linkplain AbstractStateSplit#hasSideEffect()} have a 375 * {@linkplain AbstractStateSplit#stateAfter} assigned. Additionally, verify 376 * {@linkplain LoopExitNode} and {@linkplain AbstractMergeNode} have a valid 377 * {@linkplain AbstractStateSplit#stateAfter}. This is necessary to avoid missing 378 * {@linkplain FrameState} after optimizations. See {@link GraphUtil#mayRemoveSplit} for 379 * more details. 380 * 381 * This stage is the initial verification stage for every graph. 382 */ 383 ALL(EnumSet.allOf(FrameStateVerificationFeature.class)), 384 /** 385 * Same as {@linkplain #ALL} except that {@linkplain LoopExitNode} nodes are no longer 386 * verified. 387 */ 388 ALL_EXCEPT_LOOP_EXIT(EnumSet.complementOf(EnumSet.of(FrameStateVerificationFeature.LOOP_EXITS))), 389 /** 390 * Same as {@linkplain #ALL_EXCEPT_LOOP_EXIT} except that {@linkplain LoopBeginNode} are no 391 * longer verified. 392 */ 393 ALL_EXCEPT_LOOPS(EnumSet.complementOf(EnumSet.of(FrameStateVerificationFeature.LOOP_BEGINS, FrameStateVerificationFeature.LOOP_EXITS))), 394 /** 395 * Verification is disabled. Typically used after assigning {@linkplain FrameState} to 396 * {@linkplain DeoptimizeNode} or for {@linkplain Snippet} compilations. 397 */ 398 NONE(EnumSet.noneOf(FrameStateVerificationFeature.class)); 399 400 private EnumSet<FrameStateVerificationFeature> features; 401 FrameStateVerification(EnumSet<FrameStateVerificationFeature> features)402 FrameStateVerification(EnumSet<FrameStateVerificationFeature> features) { 403 this.features = features; 404 } 405 406 /** 407 * Determines if the current verification mode implies this feature. 408 * 409 * @param feature the other verification feature to check 410 * @return {@code true} if this verification mode implies the feature, {@code false} 411 * otherwise 412 */ implies(FrameStateVerificationFeature feature)413 boolean implies(FrameStateVerificationFeature feature) { 414 return this.features.contains(feature); 415 } 416 417 } 418 419 private final boolean useProfilingInfo; 420 private final Cancellable cancellable; 421 private final boolean isSubstitution; 422 423 /** 424 * The assumptions made while constructing and transforming this graph. 425 */ 426 private final Assumptions assumptions; 427 428 private SpeculationLog speculationLog; 429 430 private ScheduleResult lastSchedule; 431 432 private final InliningLog inliningLog; 433 434 /** 435 * Call stack (context) leading to construction of this graph. 436 */ 437 private final NodeSourcePosition callerContext; 438 439 /** 440 * Records the methods that were used while constructing this graph, one entry for each time a 441 * specific method is used. This will be {@code null} if recording of inlined methods is 442 * disabled for the graph. 443 */ 444 private final List<ResolvedJavaMethod> methods; 445 446 /** 447 * Records the fields that were accessed while constructing this graph. 448 */ 449 private EconomicSet<ResolvedJavaField> fields = null; 450 451 private enum UnsafeAccessState { 452 NO_ACCESS, 453 HAS_ACCESS, 454 DISABLED 455 } 456 457 private UnsafeAccessState hasUnsafeAccess = UnsafeAccessState.NO_ACCESS; 458 459 public static final boolean USE_PROFILING_INFO = true; 460 461 public static final boolean NO_PROFILING_INFO = false; 462 StructuredGraph(String name, ResolvedJavaMethod method, int entryBCI, Assumptions assumptions, SpeculationLog speculationLog, boolean useProfilingInfo, boolean isSubstitution, List<ResolvedJavaMethod> methods, boolean trackNodeSourcePosition, CompilationIdentifier compilationId, OptionValues options, DebugContext debug, Cancellable cancellable, NodeSourcePosition context)463 private StructuredGraph(String name, 464 ResolvedJavaMethod method, 465 int entryBCI, 466 Assumptions assumptions, 467 SpeculationLog speculationLog, 468 boolean useProfilingInfo, 469 boolean isSubstitution, 470 List<ResolvedJavaMethod> methods, 471 boolean trackNodeSourcePosition, 472 CompilationIdentifier compilationId, 473 OptionValues options, 474 DebugContext debug, 475 Cancellable cancellable, 476 NodeSourcePosition context) { 477 super(name, options, debug, trackNodeSourcePosition); 478 this.setStart(add(new StartNode())); 479 this.rootMethod = method; 480 this.graphId = uniqueGraphIds.incrementAndGet(); 481 this.compilationId = compilationId; 482 this.entryBCI = entryBCI; 483 this.assumptions = assumptions; 484 this.methods = methods; 485 this.speculationLog = speculationLog; 486 this.useProfilingInfo = useProfilingInfo; 487 this.isSubstitution = isSubstitution; 488 assert checkIsSubstitutionInvariants(method, isSubstitution); 489 this.cancellable = cancellable; 490 this.inliningLog = new InliningLog(rootMethod, GraalOptions.TraceInlining.getValue(options), debug); 491 this.callerContext = context; 492 this.frameStateVerification = isSubstitution ? FrameStateVerification.NONE : FrameStateVerification.ALL; 493 } 494 checkIsSubstitutionInvariants(ResolvedJavaMethod method, boolean isSubstitution)495 private static boolean checkIsSubstitutionInvariants(ResolvedJavaMethod method, boolean isSubstitution) { 496 if (!IS_IN_NATIVE_IMAGE) { 497 if (method != null) { 498 if (method.getAnnotation(Snippet.class) != null || method.getAnnotation(MethodSubstitution.class) != null) { 499 assert isSubstitution : "Graph for method " + method.format("%H.%n(%p)") + 500 " annotated by " + Snippet.class.getName() + " or " + 501 MethodSubstitution.class.getName() + 502 " must have its `isSubstitution` field set to true"; 503 } 504 } 505 } 506 return true; 507 } 508 setLastSchedule(ScheduleResult result)509 public void setLastSchedule(ScheduleResult result) { 510 lastSchedule = result; 511 } 512 getLastSchedule()513 public ScheduleResult getLastSchedule() { 514 return lastSchedule; 515 } 516 clearLastSchedule()517 public void clearLastSchedule() { 518 setLastSchedule(null); 519 } 520 521 @Override maybeCompress()522 public boolean maybeCompress() { 523 if (super.maybeCompress()) { 524 /* 525 * The schedule contains a NodeMap which is unusable after compression. 526 */ 527 clearLastSchedule(); 528 return true; 529 } 530 return false; 531 } 532 getReturnStamp()533 public Stamp getReturnStamp() { 534 Stamp returnStamp = null; 535 for (ReturnNode returnNode : getNodes(ReturnNode.TYPE)) { 536 ValueNode result = returnNode.result(); 537 if (result != null) { 538 if (returnStamp == null) { 539 returnStamp = result.stamp(NodeView.DEFAULT); 540 } else { 541 returnStamp = returnStamp.meet(result.stamp(NodeView.DEFAULT)); 542 } 543 } 544 } 545 return returnStamp; 546 } 547 548 @Override toString()549 public String toString() { 550 StringBuilder buf = new StringBuilder(getClass().getSimpleName() + ":" + graphId); 551 String sep = "{"; 552 if (name != null) { 553 buf.append(sep); 554 buf.append(name); 555 sep = ", "; 556 } 557 if (method() != null) { 558 buf.append(sep); 559 buf.append(method()); 560 sep = ", "; 561 } 562 563 if (!sep.equals("{")) { 564 buf.append("}"); 565 } 566 return buf.toString(); 567 } 568 start()569 public StartNode start() { 570 return start; 571 } 572 573 /** 574 * Gets the root method from which this graph was built. 575 * 576 * @return null if this method was not built from a method or the method is not available 577 */ method()578 public ResolvedJavaMethod method() { 579 return rootMethod; 580 } 581 getEntryBCI()582 public int getEntryBCI() { 583 return entryBCI; 584 } 585 getCancellable()586 public Cancellable getCancellable() { 587 return cancellable; 588 } 589 checkCancellation()590 public void checkCancellation() { 591 if (cancellable != null && cancellable.isCancelled()) { 592 CancellationBailoutException.cancelCompilation(); 593 } 594 } 595 isOSR()596 public boolean isOSR() { 597 return entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI; 598 } 599 graphId()600 public long graphId() { 601 return graphId; 602 } 603 604 /** 605 * @see CompilationIdentifier 606 */ compilationId()607 public CompilationIdentifier compilationId() { 608 return compilationId; 609 } 610 setStart(StartNode start)611 public void setStart(StartNode start) { 612 this.start = start; 613 } 614 getInliningLog()615 public InliningLog getInliningLog() { 616 return inliningLog; 617 } 618 logInliningTree()619 public void logInliningTree() { 620 if (GraalOptions.TraceInlining.getValue(getOptions())) { 621 String formattedTree = getInliningLog().formatAsTree(true); 622 if (formattedTree != null) { 623 TTY.println(formattedTree); 624 } 625 } 626 } 627 628 /** 629 * Creates a copy of this graph. 630 * 631 * If a node contains an array of objects, only shallow copy of the field is applied. 632 * 633 * @param newName the name of the copy, used for debugging purposes (can be null) 634 * @param duplicationMapCallback consumer of the duplication map created during the copying 635 * @param debugForCopy the debug context for the graph copy. This must not be the debug for this 636 * graph if this graph can be accessed from multiple threads (e.g., it's in a cache 637 * accessed by multiple threads). 638 */ 639 @Override copy(String newName, Consumer<UnmodifiableEconomicMap<Node, Node>> duplicationMapCallback, DebugContext debugForCopy)640 protected Graph copy(String newName, Consumer<UnmodifiableEconomicMap<Node, Node>> duplicationMapCallback, DebugContext debugForCopy) { 641 return copy(newName, duplicationMapCallback, compilationId, debugForCopy); 642 } 643 644 @SuppressWarnings("try") copy(String newName, Consumer<UnmodifiableEconomicMap<Node, Node>> duplicationMapCallback, CompilationIdentifier newCompilationId, DebugContext debugForCopy)645 private StructuredGraph copy(String newName, Consumer<UnmodifiableEconomicMap<Node, Node>> duplicationMapCallback, CompilationIdentifier newCompilationId, DebugContext debugForCopy) { 646 AllowAssumptions allowAssumptions = allowAssumptions(); 647 StructuredGraph copy = new StructuredGraph(newName, 648 method(), 649 entryBCI, 650 assumptions == null ? null : new Assumptions(), 651 speculationLog, 652 useProfilingInfo, 653 isSubstitution, 654 methods != null ? new ArrayList<>(methods) : null, 655 trackNodeSourcePosition, 656 newCompilationId, 657 getOptions(), debugForCopy, null, callerContext); 658 if (allowAssumptions == AllowAssumptions.YES && assumptions != null) { 659 copy.assumptions.record(assumptions); 660 } 661 copy.hasUnsafeAccess = hasUnsafeAccess; 662 copy.setGuardsStage(getGuardsStage()); 663 copy.isAfterFloatingReadPhase = isAfterFloatingReadPhase; 664 copy.hasValueProxies = hasValueProxies; 665 copy.isAfterExpandLogic = isAfterExpandLogic; 666 copy.trackNodeSourcePosition = trackNodeSourcePosition; 667 if (fields != null) { 668 copy.fields = createFieldSet(fields); 669 } 670 EconomicMap<Node, Node> replacements = EconomicMap.create(Equivalence.IDENTITY); 671 replacements.put(start, copy.start); 672 UnmodifiableEconomicMap<Node, Node> duplicates; 673 try (InliningLog.UpdateScope scope = copy.getInliningLog().openDefaultUpdateScope()) { 674 duplicates = copy.addDuplicates(getNodes(), this, this.getNodeCount(), replacements); 675 if (scope != null) { 676 copy.getInliningLog().replaceLog(duplicates, this.getInliningLog()); 677 } 678 } 679 if (duplicationMapCallback != null) { 680 duplicationMapCallback.accept(duplicates); 681 } 682 return copy; 683 } 684 685 /** 686 * @param debugForCopy the debug context for the graph copy. This must not be the debug for this 687 * graph if this graph can be accessed from multiple threads (e.g., it's in a cache 688 * accessed by multiple threads). 689 */ copyWithIdentifier(CompilationIdentifier newCompilationId, DebugContext debugForCopy)690 public StructuredGraph copyWithIdentifier(CompilationIdentifier newCompilationId, DebugContext debugForCopy) { 691 return copy(name, null, newCompilationId, debugForCopy); 692 } 693 getParameter(int index)694 public ParameterNode getParameter(int index) { 695 for (ParameterNode param : getNodes(ParameterNode.TYPE)) { 696 if (param.index() == index) { 697 return param; 698 } 699 } 700 return null; 701 } 702 getInvokes()703 public Iterable<Invoke> getInvokes() { 704 final Iterator<MethodCallTargetNode> callTargets = getNodes(MethodCallTargetNode.TYPE).iterator(); 705 return new Iterable<Invoke>() { 706 707 private Invoke next; 708 709 @Override 710 public Iterator<Invoke> iterator() { 711 return new Iterator<Invoke>() { 712 713 @Override 714 public boolean hasNext() { 715 if (next == null) { 716 while (callTargets.hasNext()) { 717 Invoke i = callTargets.next().invoke(); 718 if (i != null) { 719 next = i; 720 return true; 721 } 722 } 723 return false; 724 } else { 725 return true; 726 } 727 } 728 729 @Override 730 public Invoke next() { 731 try { 732 return next; 733 } finally { 734 next = null; 735 } 736 } 737 738 @Override 739 public void remove() { 740 throw new UnsupportedOperationException(); 741 } 742 }; 743 } 744 }; 745 } 746 747 public boolean hasLoops() { 748 return hasNode(LoopBeginNode.TYPE); 749 } 750 751 /** 752 * Unlinks a node from all its control flow neighbors and then removes it from its graph. The 753 * node must have no {@linkplain Node#usages() usages}. 754 * 755 * @param node the node to be unlinked and removed 756 */ 757 @SuppressWarnings("static-method") 758 public void removeFixed(FixedWithNextNode node) { 759 assert node != null; 760 if (node instanceof AbstractBeginNode) { 761 ((AbstractBeginNode) node).prepareDelete(); 762 } 763 assert node.hasNoUsages() : node + " " + node.getUsageCount() + ", " + node.usages().first(); 764 GraphUtil.unlinkFixedNode(node); 765 node.safeDelete(); 766 } 767 768 public void replaceFixed(FixedWithNextNode node, Node replacement) { 769 if (replacement instanceof FixedWithNextNode) { 770 replaceFixedWithFixed(node, (FixedWithNextNode) replacement); 771 } else { 772 assert replacement != null : "cannot replace " + node + " with null"; 773 assert replacement instanceof FloatingNode : "cannot replace " + node + " with " + replacement; 774 replaceFixedWithFloating(node, (FloatingNode) replacement); 775 } 776 } 777 778 public void replaceFixedWithFixed(FixedWithNextNode node, FixedWithNextNode replacement) { 779 assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement; 780 FixedNode next = node.next(); 781 node.setNext(null); 782 replacement.setNext(next); 783 node.replaceAndDelete(replacement); 784 if (node == start) { 785 setStart((StartNode) replacement); 786 } 787 } 788 789 @SuppressWarnings("static-method") 790 public void replaceFixedWithFloating(FixedWithNextNode node, ValueNode replacement) { 791 assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement; 792 GraphUtil.unlinkFixedNode(node); 793 node.replaceAtUsagesAndDelete(replacement); 794 } 795 796 @SuppressWarnings("static-method") 797 public void removeSplit(ControlSplitNode node, AbstractBeginNode survivingSuccessor) { 798 assert node != null; 799 assert node.hasNoUsages(); 800 assert survivingSuccessor != null; 801 node.clearSuccessors(); 802 node.replaceAtPredecessor(survivingSuccessor); 803 node.safeDelete(); 804 } 805 806 @SuppressWarnings("static-method") 807 public void removeSplitPropagate(ControlSplitNode node, AbstractBeginNode survivingSuccessor) { 808 assert node != null; 809 assert node.hasNoUsages(); 810 assert survivingSuccessor != null; 811 List<Node> snapshot = node.successors().snapshot(); 812 node.clearSuccessors(); 813 node.replaceAtPredecessor(survivingSuccessor); 814 node.safeDelete(); 815 for (Node successor : snapshot) { 816 if (successor != null && successor.isAlive()) { 817 if (successor != survivingSuccessor) { 818 GraphUtil.killCFG((FixedNode) successor); 819 } 820 } 821 } 822 } 823 824 public void replaceSplit(ControlSplitNode node, Node replacement, AbstractBeginNode survivingSuccessor) { 825 if (replacement instanceof FixedWithNextNode) { 826 replaceSplitWithFixed(node, (FixedWithNextNode) replacement, survivingSuccessor); 827 } else { 828 assert replacement != null : "cannot replace " + node + " with null"; 829 assert replacement instanceof FloatingNode : "cannot replace " + node + " with " + replacement; 830 replaceSplitWithFloating(node, (FloatingNode) replacement, survivingSuccessor); 831 } 832 } 833 834 @SuppressWarnings("static-method") 835 public void replaceSplitWithFixed(ControlSplitNode node, FixedWithNextNode replacement, AbstractBeginNode survivingSuccessor) { 836 assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement; 837 assert survivingSuccessor != null; 838 node.clearSuccessors(); 839 replacement.setNext(survivingSuccessor); 840 node.replaceAndDelete(replacement); 841 } 842 843 @SuppressWarnings("static-method") 844 public void replaceSplitWithFloating(ControlSplitNode node, FloatingNode replacement, AbstractBeginNode survivingSuccessor) { 845 assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement; 846 assert survivingSuccessor != null; 847 node.clearSuccessors(); 848 node.replaceAtPredecessor(survivingSuccessor); 849 node.replaceAtUsagesAndDelete(replacement); 850 } 851 852 @SuppressWarnings("static-method") 853 public void replaceWithExceptionSplit(WithExceptionNode node, WithExceptionNode replacement) { 854 assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement; 855 node.replaceAtPredecessor(replacement); 856 AbstractBeginNode next = node.next(); 857 AbstractBeginNode exceptionEdge = node.exceptionEdge(); 858 node.replaceAtUsagesAndDelete(replacement); 859 replacement.setNext(next); 860 replacement.setExceptionEdge(exceptionEdge); 861 } 862 863 @SuppressWarnings("static-method") 864 public void addAfterFixed(FixedWithNextNode node, FixedNode newNode) { 865 assert node != null && newNode != null && node.isAlive() && newNode.isAlive() : "cannot add " + newNode + " after " + node; 866 FixedNode next = node.next(); 867 node.setNext(newNode); 868 if (next != null) { 869 assert newNode instanceof FixedWithNextNode; 870 FixedWithNextNode newFixedWithNext = (FixedWithNextNode) newNode; 871 assert newFixedWithNext.next() == null; 872 newFixedWithNext.setNext(next); 873 } 874 } 875 876 @SuppressWarnings("static-method") 877 public void addBeforeFixed(FixedNode node, FixedWithNextNode newNode) { 878 assert node != null && newNode != null && node.isAlive() && newNode.isAlive() : "cannot add " + newNode + " before " + node; 879 assert node.predecessor() != null && node.predecessor() instanceof FixedWithNextNode : "cannot add " + newNode + " before " + node; 880 assert newNode.next() == null : newNode; 881 assert !(node instanceof AbstractMergeNode); 882 FixedWithNextNode pred = (FixedWithNextNode) node.predecessor(); 883 pred.setNext(newNode); 884 newNode.setNext(node); 885 } 886 887 public void reduceDegenerateLoopBegin(LoopBeginNode begin) { 888 assert begin.loopEnds().isEmpty() : "Loop begin still has backedges"; 889 if (begin.forwardEndCount() == 1) { // bypass merge and remove 890 reduceTrivialMerge(begin); 891 } else { // convert to merge 892 AbstractMergeNode merge = this.add(new MergeNode()); 893 for (EndNode end : begin.forwardEnds()) { 894 merge.addForwardEnd(end); 895 } 896 this.replaceFixedWithFixed(begin, merge); 897 } 898 } 899 900 @SuppressWarnings("static-method") 901 public void reduceTrivialMerge(AbstractMergeNode merge) { 902 assert merge.forwardEndCount() == 1; 903 assert !(merge instanceof LoopBeginNode) || ((LoopBeginNode) merge).loopEnds().isEmpty(); 904 for (PhiNode phi : merge.phis().snapshot()) { 905 assert phi.valueCount() == 1; 906 ValueNode singleValue = phi.valueAt(0); 907 if (phi.hasUsages()) { 908 phi.replaceAtUsagesAndDelete(singleValue); 909 } else { 910 phi.safeDelete(); 911 if (singleValue != null) { 912 GraphUtil.tryKillUnused(singleValue); 913 } 914 } 915 } 916 // remove loop exits 917 if (merge instanceof LoopBeginNode) { 918 ((LoopBeginNode) merge).removeExits(); 919 } 920 AbstractEndNode singleEnd = merge.forwardEndAt(0); 921 FixedNode sux = merge.next(); 922 FrameState stateAfter = merge.stateAfter(); 923 // evacuateGuards 924 merge.prepareDelete((FixedNode) singleEnd.predecessor()); 925 merge.safeDelete(); 926 if (stateAfter != null) { 927 GraphUtil.tryKillUnused(stateAfter); 928 } 929 if (sux == null) { 930 singleEnd.replaceAtPredecessor(null); 931 singleEnd.safeDelete(); 932 } else { 933 singleEnd.replaceAndDelete(sux); 934 } 935 } 936 937 public GuardsStage getGuardsStage() { 938 return guardsStage; 939 } 940 941 public void setGuardsStage(GuardsStage guardsStage) { 942 assert guardsStage.ordinal() >= this.guardsStage.ordinal(); 943 this.guardsStage = guardsStage; 944 } 945 946 public boolean isAfterFloatingReadPhase() { 947 return isAfterFloatingReadPhase; 948 } 949 950 public boolean isAfterFixedReadPhase() { 951 return isAfterFixedReadPhase; 952 } 953 954 public void setAfterFloatingReadPhase(boolean state) { 955 assert state : "cannot 'unapply' floating read phase on graph"; 956 isAfterFloatingReadPhase = state; 957 } 958 959 public void setAfterFixReadPhase(boolean state) { 960 assert state : "cannot 'unapply' fix reads phase on graph"; 961 isAfterFixedReadPhase = state; 962 } 963 964 public boolean hasValueProxies() { 965 return hasValueProxies; 966 } 967 968 public void setHasValueProxies(boolean state) { 969 assert !state : "cannot 'unapply' value proxy removal on graph"; 970 hasValueProxies = state; 971 } 972 973 public boolean isAfterExpandLogic() { 974 return isAfterExpandLogic; 975 } 976 977 public void setAfterExpandLogic() { 978 isAfterExpandLogic = true; 979 } 980 981 /** 982 * Determines if {@link ProfilingInfo} is used during construction of this graph. 983 */ 984 public boolean useProfilingInfo() { 985 return useProfilingInfo; 986 } 987 988 /** 989 * Returns true if this graph is built without parsing the {@linkplain #method() root method} or 990 * if the root method is annotated by {@link Snippet} or {@link MethodSubstitution}. This is 991 * preferred over querying annotations directly as querying annotations can cause class loading. 992 */ 993 public boolean isSubstitution() { 994 return isSubstitution; 995 } 996 997 /** 998 * Gets the profiling info for the {@linkplain #method() root method} of this graph. 999 */ 1000 public ProfilingInfo getProfilingInfo() { 1001 return getProfilingInfo(method()); 1002 } 1003 1004 /** 1005 * Gets the profiling info for a given method that is or will be part of this graph, taking into 1006 * account {@link #useProfilingInfo()}. 1007 */ 1008 public ProfilingInfo getProfilingInfo(ResolvedJavaMethod m) { 1009 if (useProfilingInfo && m != null) { 1010 return m.getProfilingInfo(); 1011 } else { 1012 return DefaultProfilingInfo.get(TriState.UNKNOWN); 1013 } 1014 } 1015 1016 /** 1017 * Gets the object for recording assumptions while constructing of this graph. 1018 * 1019 * @return {@code null} if assumptions cannot be made for this graph 1020 */ 1021 public Assumptions getAssumptions() { 1022 return assumptions; 1023 } 1024 1025 /** 1026 * Returns the AllowAssumptions status for this graph. 1027 * 1028 * @return {@code AllowAssumptions.YES} if this graph allows recording assumptions, 1029 * {@code AllowAssumptions.NO} otherwise 1030 */ 1031 public AllowAssumptions allowAssumptions() { 1032 return AllowAssumptions.ifNonNull(assumptions); 1033 } 1034 1035 /** 1036 * Checks that any method referenced from a {@link FrameState} is also in the set of methods 1037 * parsed while building this graph. 1038 */ 1039 private boolean checkFrameStatesAgainstInlinedMethods() { 1040 for (FrameState fs : getNodes(FrameState.TYPE)) { 1041 if (!BytecodeFrame.isPlaceholderBci(fs.bci)) { 1042 ResolvedJavaMethod m = fs.code.getMethod(); 1043 if (!m.equals(rootMethod) && !methods.contains(m)) { 1044 SortedSet<String> haystack = new TreeSet<>(); 1045 if (!methods.contains(rootMethod)) { 1046 haystack.add(rootMethod.format("%H.%n(%p)")); 1047 } 1048 for (ResolvedJavaMethod e : methods) { 1049 haystack.add(e.format("%H.%n(%p)")); 1050 } 1051 throw new AssertionError(String.format("Could not find %s from %s in set(%s)", m.format("%H.%n(%p)"), fs, haystack.stream().collect(Collectors.joining(System.lineSeparator())))); 1052 } 1053 } 1054 } 1055 return true; 1056 } 1057 1058 private static EconomicSet<ResolvedJavaField> createFieldSet(EconomicSet<ResolvedJavaField> init) { 1059 // Multiple ResolvedJavaField objects can represent the same field so they 1060 // need to be compared with equals(). 1061 if (init != null) { 1062 return EconomicSet.create(Equivalence.DEFAULT, init); 1063 } 1064 return EconomicSet.create(Equivalence.DEFAULT); 1065 } 1066 1067 /** 1068 * Gets an unmodifiable view of the methods that were inlined while constructing this graph. 1069 */ 1070 public List<ResolvedJavaMethod> getMethods() { 1071 if (methods != null) { 1072 assert isSubstitution || checkFrameStatesAgainstInlinedMethods(); 1073 return Collections.unmodifiableList(methods); 1074 } 1075 return Collections.emptyList(); 1076 } 1077 1078 /** 1079 * Records that {@code method} was used to build this graph. 1080 */ 1081 public void recordMethod(ResolvedJavaMethod method) { 1082 if (methods != null) { 1083 methods.add(method); 1084 } 1085 } 1086 1087 /** 1088 * Updates the {@linkplain #getMethods() methods} used to build this graph with the methods used 1089 * to build another graph. 1090 */ 1091 public void updateMethods(StructuredGraph other) { 1092 if (methods != null) { 1093 if (other.rootMethod != null) { 1094 methods.add(other.rootMethod); 1095 } 1096 for (ResolvedJavaMethod m : other.methods) { 1097 methods.add(m); 1098 } 1099 } 1100 } 1101 1102 /** 1103 * Gets an unmodifiable view of the fields that were accessed while constructing this graph. 1104 * 1105 * @return {@code null} if no field accesses were recorded 1106 */ 1107 public EconomicSet<ResolvedJavaField> getFields() { 1108 return fields; 1109 } 1110 1111 /** 1112 * Records that {@code field} was accessed in this graph. 1113 */ 1114 public void recordField(ResolvedJavaField field) { 1115 assert GraalOptions.GeneratePIC.getValue(getOptions()); 1116 if (this.fields == null) { 1117 this.fields = createFieldSet(null); 1118 } 1119 fields.add(field); 1120 } 1121 1122 /** 1123 * Updates the {@linkplain #getFields() fields} of this graph with the accessed fields of 1124 * another graph. 1125 */ 1126 public void updateFields(StructuredGraph other) { 1127 assert this != other; 1128 assert GraalOptions.GeneratePIC.getValue(getOptions()); 1129 if (other.fields != null) { 1130 if (this.fields == null) { 1131 this.fields = createFieldSet(null); 1132 } 1133 this.fields.addAll(other.fields); 1134 } 1135 } 1136 1137 /** 1138 * Gets the input bytecode {@linkplain ResolvedJavaMethod#getCodeSize() size} from which this 1139 * graph is constructed. This ignores how many bytecodes in each constituent method are actually 1140 * parsed (which may be none for methods whose IR is retrieved from a cache or less than the 1141 * full amount for any given method due to profile guided branch pruning). 1142 */ 1143 public int getBytecodeSize() { 1144 int res = 0; 1145 if (rootMethod != null) { 1146 res += rootMethod.getCodeSize(); 1147 } 1148 if (methods != null) { 1149 for (ResolvedJavaMethod e : methods) { 1150 res += e.getCodeSize(); 1151 } 1152 } 1153 return res; 1154 } 1155 1156 @Override 1157 public JavaMethod asJavaMethod() { 1158 return method(); 1159 } 1160 1161 public boolean hasUnsafeAccess() { 1162 return hasUnsafeAccess == UnsafeAccessState.HAS_ACCESS; 1163 } 1164 1165 public void markUnsafeAccess() { 1166 if (hasUnsafeAccess == UnsafeAccessState.DISABLED) { 1167 return; 1168 } 1169 hasUnsafeAccess = UnsafeAccessState.HAS_ACCESS; 1170 } 1171 1172 public void disableUnsafeAccessTracking() { 1173 hasUnsafeAccess = UnsafeAccessState.DISABLED; 1174 } 1175 1176 public boolean isUnsafeAccessTrackingEnabled() { 1177 return hasUnsafeAccess != UnsafeAccessState.DISABLED; 1178 } 1179 1180 public SpeculationLog getSpeculationLog() { 1181 return speculationLog; 1182 } 1183 1184 public FrameStateVerification getFrameStateVerification() { 1185 return frameStateVerification; 1186 } 1187 1188 public void weakenFrameStateVerification(FrameStateVerification newFrameStateVerification) { 1189 if (frameStateVerification == FrameStateVerification.NONE) { 1190 return; 1191 } 1192 if (newFrameStateVerification == FrameStateVerification.NONE) { 1193 /* 1194 * Unit tests and substitution graphs can disable verification early on in the 1195 * compilation pipeline. 1196 */ 1197 frameStateVerification = FrameStateVerification.NONE; 1198 return; 1199 } 1200 assert frameStateVerification.ordinal() < newFrameStateVerification.ordinal() : "Old verification " + frameStateVerification + " must imply new verification " + newFrameStateVerification + 1201 ", i.e., verification can only be relaxed over the course of compilation"; 1202 frameStateVerification = newFrameStateVerification; 1203 } 1204 1205 public void disableFrameStateVerification() { 1206 weakenFrameStateVerification(FrameStateVerification.NONE); 1207 } 1208 1209 public void clearAllStateAfter() { 1210 weakenFrameStateVerification(FrameStateVerification.NONE); 1211 for (Node node : getNodes()) { 1212 if (node instanceof StateSplit) { 1213 FrameState stateAfter = ((StateSplit) node).stateAfter(); 1214 if (stateAfter != null) { 1215 ((StateSplit) node).setStateAfter(null); 1216 // 2 nodes referencing the same frame state 1217 if (stateAfter.isAlive()) { 1218 GraphUtil.killWithUnusedFloatingInputs(stateAfter); 1219 } 1220 } 1221 } 1222 } 1223 } 1224 1225 public boolean hasVirtualizableAllocation() { 1226 for (Node n : getNodes()) { 1227 if (n instanceof VirtualizableAllocation) { 1228 return true; 1229 } 1230 } 1231 return false; 1232 } 1233 1234 @Override 1235 protected void afterRegister(Node node) { 1236 assert hasValueProxies() || !(node instanceof ValueProxyNode); 1237 if (GraalOptions.TraceInlining.getValue(getOptions())) { 1238 if (node instanceof Invokable) { 1239 ((Invokable) node).updateInliningLogAfterRegister(this); 1240 } 1241 } 1242 } 1243 1244 public NodeSourcePosition getCallerContext() { 1245 return callerContext; 1246 } 1247 } 1248