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