1 /* 2 * Copyright (c) 2011, 2017, 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.graph; 26 27 import static org.graalvm.compiler.core.common.GraalOptions.TrackNodeInsertion; 28 import static org.graalvm.compiler.graph.Graph.SourcePositionTracking.Default; 29 import static org.graalvm.compiler.graph.Graph.SourcePositionTracking.Track; 30 import static org.graalvm.compiler.graph.Graph.SourcePositionTracking.UpdateOnly; 31 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED; 32 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED; 33 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.Iterator; 37 import java.util.function.Consumer; 38 39 import jdk.internal.vm.compiler.collections.EconomicMap; 40 import jdk.internal.vm.compiler.collections.Equivalence; 41 import jdk.internal.vm.compiler.collections.UnmodifiableEconomicMap; 42 import org.graalvm.compiler.core.common.GraalOptions; 43 import org.graalvm.compiler.debug.CounterKey; 44 import org.graalvm.compiler.debug.DebugCloseable; 45 import org.graalvm.compiler.debug.DebugContext; 46 import org.graalvm.compiler.debug.GraalError; 47 import org.graalvm.compiler.debug.TimerKey; 48 import org.graalvm.compiler.graph.Node.NodeInsertionStackTrace; 49 import org.graalvm.compiler.graph.Node.ValueNumberable; 50 import org.graalvm.compiler.graph.iterators.NodeIterable; 51 import org.graalvm.compiler.options.Option; 52 import org.graalvm.compiler.options.OptionKey; 53 import org.graalvm.compiler.options.OptionType; 54 import org.graalvm.compiler.options.OptionValues; 55 56 import jdk.vm.ci.meta.ResolvedJavaMethod; 57 58 /** 59 * This class is a graph container, it contains the set of nodes that belong to this graph. 60 */ 61 public class Graph { 62 63 public static class Options { 64 @Option(help = "Verify graphs often during compilation when assertions are turned on", type = OptionType.Debug)// 65 public static final OptionKey<Boolean> VerifyGraalGraphs = new OptionKey<>(true); 66 @Option(help = "Perform expensive verification of graph inputs, usages, successors and predecessors", type = OptionType.Debug)// 67 public static final OptionKey<Boolean> VerifyGraalGraphEdges = new OptionKey<>(false); 68 @Option(help = "Graal graph compression is performed when percent of live nodes falls below this value", type = OptionType.Debug)// 69 public static final OptionKey<Integer> GraphCompressionThreshold = new OptionKey<>(70); 70 } 71 72 private enum FreezeState { 73 Unfrozen, 74 TemporaryFreeze, 75 DeepFreeze 76 } 77 78 public enum SourcePositionTracking { 79 Default, 80 Ignore, 81 UpdateOnly, 82 Track 83 } 84 85 public final String name; 86 87 /** 88 * The set of nodes in the graph, ordered by {@linkplain #register(Node) registration} time. 89 */ 90 Node[] nodes; 91 92 /** 93 * Source information to associate with newly created nodes. 94 */ 95 NodeSourcePosition currentNodeSourcePosition; 96 97 /** 98 * Records if updating of node source information is required when performing inlining. 99 */ 100 protected SourcePositionTracking trackNodeSourcePosition; 101 102 /** 103 * The number of valid entries in {@link #nodes}. 104 */ 105 int nodesSize; 106 107 /** 108 * Records the modification count for nodes. This is only used in assertions. 109 */ 110 private int[] nodeModCounts; 111 112 /** 113 * Records the modification count for nodes' usage lists. This is only used in assertions. 114 */ 115 private int[] nodeUsageModCounts; 116 117 // these two arrays contain one entry for each NodeClass, indexed by NodeClass.iterableId. 118 // they contain the first and last pointer to a linked list of all nodes with this type. 119 private final ArrayList<Node> iterableNodesFirst; 120 private final ArrayList<Node> iterableNodesLast; 121 122 private int nodesDeletedSinceLastCompression; 123 private int nodesDeletedBeforeLastCompression; 124 125 /** 126 * The number of times this graph has been compressed. 127 */ 128 int compressions; 129 130 NodeEventListener nodeEventListener; 131 132 /** 133 * Used to global value number {@link ValueNumberable} {@linkplain NodeClass#isLeafNode() leaf} 134 * nodes. 135 */ 136 private EconomicMap<Node, Node>[] cachedLeafNodes; 137 138 private static final Equivalence NODE_VALUE_COMPARE = new Equivalence() { 139 140 @Override 141 public boolean equals(Object a, Object b) { 142 if (a == b) { 143 return true; 144 } 145 146 assert a.getClass() == b.getClass(); 147 return ((Node) a).valueEquals((Node) b); 148 } 149 150 @Override 151 public int hashCode(Object k) { 152 return ((Node) k).getNodeClass().valueNumber((Node) k); 153 } 154 }; 155 156 /** 157 * Indicates that the graph should no longer be modified. Frozen graphs can be used by multiple 158 * threads so it's only safe to read them. 159 */ 160 private FreezeState freezeState = FreezeState.Unfrozen; 161 162 /** 163 * The option values used while compiling this graph. 164 */ 165 private final OptionValues options; 166 167 /** 168 * The {@link DebugContext} used while compiling this graph. 169 */ 170 private DebugContext debug; 171 172 private class NodeSourcePositionScope implements DebugCloseable { 173 private final NodeSourcePosition previous; 174 175 NodeSourcePositionScope(NodeSourcePosition sourcePosition) { 176 previous = currentNodeSourcePosition; 177 currentNodeSourcePosition = sourcePosition; 178 } 179 180 @Override 181 public DebugContext getDebug() { 182 return debug; 183 } 184 185 @Override 186 public void close() { 187 currentNodeSourcePosition = previous; 188 } 189 } 190 191 public NodeSourcePosition currentNodeSourcePosition() { 192 return currentNodeSourcePosition; 193 } 194 195 /** 196 * Opens a scope in which the source information from {@code node} is copied into nodes created 197 * within the scope. If {@code node} has no source information information, no scope is opened 198 * and {@code null} is returned. 199 * 200 * @return a {@link DebugCloseable} for managing the opened scope or {@code null} if no scope 201 * was opened 202 */ 203 public DebugCloseable withNodeSourcePosition(Node node) { 204 return withNodeSourcePosition(node.getNodeSourcePosition()); 205 } 206 207 /** 208 * Opens a scope in which {@code sourcePosition} is copied into nodes created within the scope. 209 * If {@code sourcePosition == null}, no scope is opened and {@code null} is returned. 210 * 211 * @return a {@link DebugCloseable} for managing the opened scope or {@code null} if no scope 212 * was opened 213 */ 214 public DebugCloseable withNodeSourcePosition(NodeSourcePosition sourcePosition) { 215 return trackNodeSourcePosition() && sourcePosition != null ? new NodeSourcePositionScope(sourcePosition) : null; 216 } 217 218 /** 219 * Opens a scope in which newly created nodes do not get any source information added. 220 * 221 * @return a {@link DebugCloseable} for managing the opened scope 222 */ 223 public DebugCloseable withoutNodeSourcePosition() { 224 return new NodeSourcePositionScope(null); 225 } 226 227 /** 228 * Determines if this graph might contain nodes with source information. This is mainly useful 229 * to short circuit logic for updating those positions after inlining since that requires 230 * visiting every node in the graph. 231 */ 232 public boolean updateNodeSourcePosition() { 233 return trackNodeSourcePosition == Track || trackNodeSourcePosition == UpdateOnly; 234 } 235 236 public boolean trackNodeSourcePosition() { 237 return trackNodeSourcePosition == Track; 238 } 239 240 public void setTrackNodeSourcePosition() { 241 if (trackNodeSourcePosition != Track) { 242 assert trackNodeSourcePosition == Default : trackNodeSourcePosition; 243 trackNodeSourcePosition = Track; 244 } 245 } 246 247 public static SourcePositionTracking trackNodeSourcePositionDefault(OptionValues options, DebugContext debug) { 248 if (GraalOptions.TrackNodeSourcePosition.getValue(options) || debug.isDumpEnabledForMethod()) { 249 return Track; 250 } 251 return Default; 252 } 253 254 /** 255 * Creates an empty Graph with no name. 256 */ 257 public Graph(OptionValues options, DebugContext debug) { 258 this(null, options, debug); 259 } 260 261 /** 262 * We only want the expensive modification count tracking when assertions are enabled for the 263 * {@link Graph} class. 264 */ 265 @SuppressWarnings("all") 266 public static boolean isModificationCountsEnabled() { 267 boolean enabled = false; 268 assert enabled = true; 269 return enabled; 270 } 271 272 private static final int INITIAL_NODES_SIZE = 32; 273 274 /** 275 * Creates an empty Graph with a given name. 276 * 277 * @param name the name of the graph, used for debugging purposes 278 */ 279 public Graph(String name, OptionValues options, DebugContext debug) { 280 nodes = new Node[INITIAL_NODES_SIZE]; 281 iterableNodesFirst = new ArrayList<>(NodeClass.allocatedNodeIterabledIds()); 282 iterableNodesLast = new ArrayList<>(NodeClass.allocatedNodeIterabledIds()); 283 this.name = name; 284 this.options = options; 285 this.trackNodeSourcePosition = trackNodeSourcePositionDefault(options, debug); 286 assert debug != null; 287 this.debug = debug; 288 289 if (isModificationCountsEnabled()) { 290 nodeModCounts = new int[INITIAL_NODES_SIZE]; 291 nodeUsageModCounts = new int[INITIAL_NODES_SIZE]; 292 } 293 } 294 295 int extractOriginalNodeId(Node node) { 296 int id = node.id; 297 if (id <= Node.DELETED_ID_START) { 298 id = Node.DELETED_ID_START - id; 299 } 300 return id; 301 } 302 303 int modCount(Node node) { 304 int id = extractOriginalNodeId(node); 305 if (id >= 0 && id < nodeModCounts.length) { 306 return nodeModCounts[id]; 307 } 308 return 0; 309 } 310 311 void incModCount(Node node) { 312 int id = extractOriginalNodeId(node); 313 if (id >= 0) { 314 if (id >= nodeModCounts.length) { 315 nodeModCounts = Arrays.copyOf(nodeModCounts, id * 2 + 30); 316 } 317 nodeModCounts[id]++; 318 } else { 319 assert false; 320 } 321 } 322 323 int usageModCount(Node node) { 324 int id = extractOriginalNodeId(node); 325 if (id >= 0 && id < nodeUsageModCounts.length) { 326 return nodeUsageModCounts[id]; 327 } 328 return 0; 329 } 330 331 void incUsageModCount(Node node) { 332 int id = extractOriginalNodeId(node); 333 if (id >= 0) { 334 if (id >= nodeUsageModCounts.length) { 335 nodeUsageModCounts = Arrays.copyOf(nodeUsageModCounts, id * 2 + 30); 336 } 337 nodeUsageModCounts[id]++; 338 } else { 339 assert false; 340 } 341 } 342 343 /** 344 * Creates a copy of this graph. 345 * 346 * @param debugForCopy the debug context for the graph copy. This must not be the debug for this 347 * graph if this graph can be accessed from multiple threads (e.g., it's in a cache 348 * accessed by multiple threads). 349 */ 350 public final Graph copy(DebugContext debugForCopy) { 351 return copy(name, null, debugForCopy); 352 } 353 354 /** 355 * Creates a copy of this graph. 356 * 357 * @param duplicationMapCallback consumer of the duplication map created during the copying 358 * @param debugForCopy the debug context for the graph copy. This must not be the debug for this 359 * graph if this graph can be accessed from multiple threads (e.g., it's in a cache 360 * accessed by multiple threads). 361 */ 362 public final Graph copy(Consumer<UnmodifiableEconomicMap<Node, Node>> duplicationMapCallback, DebugContext debugForCopy) { 363 return copy(name, duplicationMapCallback, debugForCopy); 364 } 365 366 /** 367 * Creates a copy of this graph. 368 * 369 * @param newName the name of the copy, used for debugging purposes (can be null) 370 * @param debugForCopy the debug context for the graph copy. This must not be the debug for this 371 * graph if this graph can be accessed from multiple threads (e.g., it's in a cache 372 * accessed by multiple threads). 373 */ 374 public final Graph copy(String newName, DebugContext debugForCopy) { 375 return copy(newName, null, debugForCopy); 376 } 377 378 /** 379 * Creates a copy of this graph. 380 * 381 * @param newName the name of the copy, used for debugging purposes (can be null) 382 * @param duplicationMapCallback consumer of the duplication map created during the copying 383 * @param debugForCopy the debug context for the graph copy. This must not be the debug for this 384 * graph if this graph can be accessed from multiple threads (e.g., it's in a cache 385 * accessed by multiple threads). 386 */ 387 protected Graph copy(String newName, Consumer<UnmodifiableEconomicMap<Node, Node>> duplicationMapCallback, DebugContext debugForCopy) { 388 Graph copy = new Graph(newName, options, debugForCopy); 389 if (trackNodeSourcePosition()) { 390 copy.setTrackNodeSourcePosition(); 391 } 392 UnmodifiableEconomicMap<Node, Node> duplicates = copy.addDuplicates(getNodes(), this, this.getNodeCount(), (EconomicMap<Node, Node>) null); 393 if (duplicationMapCallback != null) { 394 duplicationMapCallback.accept(duplicates); 395 } 396 return copy; 397 } 398 399 public final OptionValues getOptions() { 400 return options; 401 } 402 403 public DebugContext getDebug() { 404 return debug; 405 } 406 407 /** 408 * Resets the {@link DebugContext} for this graph to a new value. This is useful when a graph is 409 * "handed over" from its creating thread to another thread. 410 * 411 * This must only be done when the current thread is no longer using the graph. This is in 412 * general impossible to test due to races and since metrics can be updated at any time. As 413 * such, this method only performs a weak sanity check that at least the current debug context 414 * does not have a nested scope open (the top level scope will always be open if scopes are 415 * enabled). 416 */ 417 public void resetDebug(DebugContext newDebug) { 418 assert newDebug == debug || !debug.inNestedScope() : String.format("Cannot reset the debug context for %s while it has the nested scope \"%s\" open", this, debug.getCurrentScopeName()); 419 this.debug = newDebug; 420 } 421 422 @Override 423 public String toString() { 424 return name == null ? super.toString() : "Graph " + name; 425 } 426 427 /** 428 * Gets the number of live nodes in this graph. That is the number of nodes which have been 429 * added to the graph minus the number of deleted nodes. 430 * 431 * @return the number of live nodes in this graph 432 */ 433 public int getNodeCount() { 434 return nodesSize - getNodesDeletedSinceLastCompression(); 435 } 436 437 /** 438 * Gets the number of times this graph has been {@linkplain #maybeCompress() compressed}. Node 439 * identifiers are only stable between compressions. To ensure this constraint is observed, any 440 * entity relying upon stable node identifiers should use {@link NodeIdAccessor}. 441 */ 442 public int getCompressions() { 443 return compressions; 444 } 445 446 /** 447 * Gets the number of nodes which have been deleted from this graph since it was last 448 * {@linkplain #maybeCompress() compressed}. 449 */ 450 public int getNodesDeletedSinceLastCompression() { 451 return nodesDeletedSinceLastCompression; 452 } 453 454 /** 455 * Gets the total number of nodes which have been deleted from this graph. 456 */ 457 public int getTotalNodesDeleted() { 458 return nodesDeletedSinceLastCompression + nodesDeletedBeforeLastCompression; 459 } 460 461 /** 462 * Adds a new node to the graph. 463 * 464 * @param node the node to be added 465 * @return the node which was added to the graph 466 */ 467 public <T extends Node> T add(T node) { 468 if (node.getNodeClass().valueNumberable()) { 469 throw new IllegalStateException("Using add for value numberable node. Consider using either unique or addWithoutUnique."); 470 } 471 return addHelper(node); 472 } 473 474 public <T extends Node> T addWithoutUnique(T node) { 475 return addHelper(node); 476 } 477 478 public <T extends Node> T addOrUnique(T node) { 479 if (node.getNodeClass().valueNumberable()) { 480 return uniqueHelper(node); 481 } 482 return add(node); 483 } 484 485 public <T extends Node> T maybeAddOrUnique(T node) { 486 if (node.isAlive()) { 487 return node; 488 } 489 return addOrUnique(node); 490 } 491 492 public <T extends Node> T addOrUniqueWithInputs(T node) { 493 if (node.isAlive()) { 494 assert node.graph() == this; 495 return node; 496 } else { 497 assert node.isUnregistered(); 498 addInputs(node); 499 if (node.getNodeClass().valueNumberable()) { 500 return uniqueHelper(node); 501 } 502 return add(node); 503 } 504 } 505 506 public <T extends Node> T addWithoutUniqueWithInputs(T node) { 507 addInputs(node); 508 return addHelper(node); 509 } 510 511 private final class AddInputsFilter extends Node.EdgeVisitor { 512 513 @Override 514 public Node apply(Node self, Node input) { 515 if (!input.isAlive()) { 516 assert !input.isDeleted(); 517 return addOrUniqueWithInputs(input); 518 } else { 519 return input; 520 } 521 } 522 523 } 524 525 private AddInputsFilter addInputsFilter = new AddInputsFilter(); 526 527 private <T extends Node> void addInputs(T node) { 528 node.applyInputs(addInputsFilter); 529 } 530 531 private <T extends Node> T addHelper(T node) { 532 node.initialize(this); 533 return node; 534 } 535 536 /** 537 * The type of events sent to a {@link NodeEventListener}. 538 */ 539 public enum NodeEvent { 540 /** 541 * A node's input is changed. 542 */ 543 INPUT_CHANGED, 544 545 /** 546 * A node's {@linkplain Node#usages() usages} count dropped to zero. 547 */ 548 ZERO_USAGES, 549 550 /** 551 * A node was added to a graph. 552 */ 553 NODE_ADDED, 554 555 /** 556 * A node was removed from the graph. 557 */ 558 NODE_REMOVED; 559 } 560 561 /** 562 * Client interested in one or more node related events. 563 */ 564 public abstract static class NodeEventListener { 565 566 /** 567 * A method called when a change event occurs. 568 * 569 * This method dispatches the event to user-defined triggers. The methods that change the 570 * graph (typically in Graph and Node) must call this method to dispatch the event. 571 * 572 * @param e an event 573 * @param node the node related to {@code e} 574 */ 575 final void event(NodeEvent e, Node node) { 576 switch (e) { 577 case INPUT_CHANGED: 578 inputChanged(node); 579 break; 580 case ZERO_USAGES: 581 usagesDroppedToZero(node); 582 break; 583 case NODE_ADDED: 584 nodeAdded(node); 585 break; 586 case NODE_REMOVED: 587 nodeRemoved(node); 588 break; 589 } 590 changed(e, node); 591 } 592 593 /** 594 * Notifies this listener about any change event in the graph. 595 * 596 * @param e an event 597 * @param node the node related to {@code e} 598 */ 599 public void changed(NodeEvent e, Node node) { 600 } 601 602 /** 603 * Notifies this listener about a change in a node's inputs. 604 * 605 * @param node a node who has had one of its inputs changed 606 */ 607 public void inputChanged(Node node) { 608 } 609 610 /** 611 * Notifies this listener of a node becoming unused. 612 * 613 * @param node a node whose {@link Node#usages()} just became empty 614 */ 615 public void usagesDroppedToZero(Node node) { 616 } 617 618 /** 619 * Notifies this listener of an added node. 620 * 621 * @param node a node that was just added to the graph 622 */ 623 public void nodeAdded(Node node) { 624 } 625 626 /** 627 * Notifies this listener of a removed node. 628 * 629 * @param node 630 */ 631 public void nodeRemoved(Node node) { 632 } 633 } 634 635 /** 636 * Registers a given {@link NodeEventListener} with the enclosing graph until this object is 637 * {@linkplain #close() closed}. 638 */ 639 public final class NodeEventScope implements AutoCloseable { 640 NodeEventScope(NodeEventListener listener) { 641 if (nodeEventListener == null) { 642 nodeEventListener = listener; 643 } else { 644 nodeEventListener = new ChainedNodeEventListener(listener, nodeEventListener); 645 } 646 } 647 648 @Override 649 public void close() { 650 assert nodeEventListener != null; 651 if (nodeEventListener instanceof ChainedNodeEventListener) { 652 nodeEventListener = ((ChainedNodeEventListener) nodeEventListener).next; 653 } else { 654 nodeEventListener = null; 655 } 656 } 657 } 658 659 private static class ChainedNodeEventListener extends NodeEventListener { 660 661 NodeEventListener head; 662 NodeEventListener next; 663 664 ChainedNodeEventListener(NodeEventListener head, NodeEventListener next) { 665 this.head = head; 666 this.next = next; 667 } 668 669 @Override 670 public void nodeAdded(Node node) { 671 head.event(NodeEvent.NODE_ADDED, node); 672 next.event(NodeEvent.NODE_ADDED, node); 673 } 674 675 @Override 676 public void inputChanged(Node node) { 677 head.event(NodeEvent.INPUT_CHANGED, node); 678 next.event(NodeEvent.INPUT_CHANGED, node); 679 } 680 681 @Override 682 public void usagesDroppedToZero(Node node) { 683 head.event(NodeEvent.ZERO_USAGES, node); 684 next.event(NodeEvent.ZERO_USAGES, node); 685 } 686 687 @Override 688 public void nodeRemoved(Node node) { 689 head.event(NodeEvent.NODE_REMOVED, node); 690 next.event(NodeEvent.NODE_REMOVED, node); 691 } 692 693 @Override 694 public void changed(NodeEvent e, Node node) { 695 head.event(e, node); 696 next.event(e, node); 697 } 698 } 699 700 /** 701 * Registers a given {@link NodeEventListener} with this graph. This should be used in 702 * conjunction with try-with-resources statement as follows: 703 * 704 * <pre> 705 * try (NodeEventScope nes = graph.trackNodeEvents(listener)) { 706 * // make changes to the graph 707 * } 708 * </pre> 709 */ 710 public NodeEventScope trackNodeEvents(NodeEventListener listener) { 711 return new NodeEventScope(listener); 712 } 713 714 /** 715 * Looks for a node <i>similar</i> to {@code node} and returns it if found. Otherwise 716 * {@code node} is added to this graph and returned. 717 * 718 * @return a node similar to {@code node} if one exists, otherwise {@code node} 719 */ 720 public <T extends Node & ValueNumberable> T unique(T node) { 721 return uniqueHelper(node); 722 } 723 724 <T extends Node> T uniqueHelper(T node) { 725 assert node.getNodeClass().valueNumberable(); 726 T other = this.findDuplicate(node); 727 if (other != null) { 728 if (other.getNodeSourcePosition() == null) { 729 other.setNodeSourcePosition(node.getNodeSourcePosition()); 730 } 731 return other; 732 } else { 733 T result = addHelper(node); 734 if (node.getNodeClass().isLeafNode()) { 735 putNodeIntoCache(result); 736 } 737 return result; 738 } 739 } 740 741 void removeNodeFromCache(Node node) { 742 assert node.graph() == this || node.graph() == null; 743 assert node.getNodeClass().valueNumberable(); 744 assert node.getNodeClass().isLeafNode() : node.getClass(); 745 746 int leafId = node.getNodeClass().getLeafId(); 747 if (cachedLeafNodes != null && cachedLeafNodes.length > leafId && cachedLeafNodes[leafId] != null) { 748 cachedLeafNodes[leafId].removeKey(node); 749 } 750 } 751 752 @SuppressWarnings({"unchecked", "rawtypes"}) 753 void putNodeIntoCache(Node node) { 754 assert node.graph() == this || node.graph() == null; 755 assert node.getNodeClass().valueNumberable(); 756 assert node.getNodeClass().isLeafNode() : node.getClass(); 757 758 int leafId = node.getNodeClass().getLeafId(); 759 if (cachedLeafNodes == null || cachedLeafNodes.length <= leafId) { 760 EconomicMap[] newLeafNodes = new EconomicMap[leafId + 1]; 761 if (cachedLeafNodes != null) { 762 System.arraycopy(cachedLeafNodes, 0, newLeafNodes, 0, cachedLeafNodes.length); 763 } 764 cachedLeafNodes = newLeafNodes; 765 } 766 767 if (cachedLeafNodes[leafId] == null) { 768 cachedLeafNodes[leafId] = EconomicMap.create(NODE_VALUE_COMPARE); 769 } 770 771 cachedLeafNodes[leafId].put(node, node); 772 } 773 774 Node findNodeInCache(Node node) { 775 int leafId = node.getNodeClass().getLeafId(); 776 if (cachedLeafNodes == null || cachedLeafNodes.length <= leafId || cachedLeafNodes[leafId] == null) { 777 return null; 778 } 779 780 Node result = cachedLeafNodes[leafId].get(node); 781 assert result == null || result.isAlive() : result; 782 return result; 783 } 784 785 /** 786 * Returns a possible duplicate for the given node in the graph or {@code null} if no such 787 * duplicate exists. 788 */ 789 @SuppressWarnings("unchecked") 790 public <T extends Node> T findDuplicate(T node) { 791 NodeClass<?> nodeClass = node.getNodeClass(); 792 assert nodeClass.valueNumberable(); 793 if (nodeClass.isLeafNode()) { 794 // Leaf node: look up in cache 795 Node cachedNode = findNodeInCache(node); 796 if (cachedNode != null && cachedNode != node) { 797 return (T) cachedNode; 798 } else { 799 return null; 800 } 801 } else { 802 /* 803 * Non-leaf node: look for another usage of the node's inputs that has the same data, 804 * inputs and successors as the node. To reduce the cost of this computation, only the 805 * input with lowest usage count is considered. If this node is the only user of any 806 * input then the search can terminate early. The usage count is only incremented once 807 * the Node is in the Graph, so account for that in the test. 808 */ 809 final int earlyExitUsageCount = node.graph() != null ? 1 : 0; 810 int minCount = Integer.MAX_VALUE; 811 Node minCountNode = null; 812 for (Node input : node.inputs()) { 813 int usageCount = input.getUsageCount(); 814 if (usageCount == earlyExitUsageCount) { 815 return null; 816 } else if (usageCount < minCount) { 817 minCount = usageCount; 818 minCountNode = input; 819 } 820 } 821 if (minCountNode != null) { 822 for (Node usage : minCountNode.usages()) { 823 if (usage != node && nodeClass == usage.getNodeClass() && node.valueEquals(usage) && nodeClass.equalInputs(node, usage) && 824 nodeClass.equalSuccessors(node, usage)) { 825 return (T) usage; 826 } 827 } 828 return null; 829 } 830 return null; 831 } 832 } 833 834 public boolean isNew(Mark mark, Node node) { 835 return node.id >= mark.getValue(); 836 } 837 838 /** 839 * A snapshot of the {@linkplain Graph#getNodeCount() live node count} in a graph. 840 */ 841 public static class Mark extends NodeIdAccessor { 842 private final int value; 843 844 Mark(Graph graph) { 845 super(graph); 846 this.value = graph.nodeIdCount(); 847 } 848 849 @Override 850 public boolean equals(Object obj) { 851 if (obj instanceof Mark) { 852 Mark other = (Mark) obj; 853 return other.getValue() == getValue() && other.getGraph() == getGraph(); 854 } 855 return false; 856 } 857 858 @Override 859 public int hashCode() { 860 return value ^ (epoch + 11); 861 } 862 863 /** 864 * Determines if this mark is positioned at the first live node in the graph. 865 */ 866 public boolean isStart() { 867 return value == 0; 868 } 869 870 /** 871 * Gets the {@linkplain Graph#getNodeCount() live node count} of the associated graph when 872 * this object was created. 873 */ 874 int getValue() { 875 return value; 876 } 877 878 /** 879 * Determines if this mark still represents the {@linkplain Graph#getNodeCount() live node 880 * count} of the graph. 881 */ 882 public boolean isCurrent() { 883 return value == graph.nodeIdCount(); 884 } 885 } 886 887 /** 888 * Gets a mark that can be used with {@link #getNewNodes}. 889 */ 890 public Mark getMark() { 891 return new Mark(this); 892 } 893 894 /** 895 * Returns an {@link Iterable} providing all nodes added since the last {@link Graph#getMark() 896 * mark}. 897 */ 898 public NodeIterable<Node> getNewNodes(Mark mark) { 899 final int index = mark == null ? 0 : mark.getValue(); 900 return new NodeIterable<Node>() { 901 902 @Override 903 public Iterator<Node> iterator() { 904 return new GraphNodeIterator(Graph.this, index); 905 } 906 }; 907 } 908 909 /** 910 * Returns an {@link Iterable} providing all the live nodes. 911 * 912 * @return an {@link Iterable} providing all the live nodes. 913 */ 914 public NodeIterable<Node> getNodes() { 915 return new NodeIterable<Node>() { 916 917 @Override 918 public Iterator<Node> iterator() { 919 return new GraphNodeIterator(Graph.this); 920 } 921 922 @Override 923 public int count() { 924 return getNodeCount(); 925 } 926 }; 927 } 928 929 // Fully qualified annotation name is required to satisfy javac 930 @org.graalvm.compiler.nodeinfo.NodeInfo(cycles = CYCLES_IGNORED, size = SIZE_IGNORED) 931 static final class PlaceHolderNode extends Node { 932 933 public static final NodeClass<PlaceHolderNode> TYPE = NodeClass.create(PlaceHolderNode.class); 934 935 protected PlaceHolderNode() { 936 super(TYPE); 937 } 938 939 } 940 941 private static final CounterKey GraphCompressions = DebugContext.counter("GraphCompressions"); 942 943 /** 944 * If the {@linkplain Options#GraphCompressionThreshold compression threshold} is met, the list 945 * of nodes is compressed such that all non-null entries precede all null entries while 946 * preserving the ordering between the nodes within the list. 947 */ 948 public boolean maybeCompress() { 949 if (debug.isDumpEnabledForMethod() || debug.isLogEnabledForMethod()) { 950 return false; 951 } 952 int liveNodeCount = getNodeCount(); 953 int liveNodePercent = liveNodeCount * 100 / nodesSize; 954 int compressionThreshold = Options.GraphCompressionThreshold.getValue(options); 955 if (compressionThreshold == 0 || liveNodePercent >= compressionThreshold) { 956 return false; 957 } 958 GraphCompressions.increment(debug); 959 int nextId = 0; 960 for (int i = 0; nextId < liveNodeCount; i++) { 961 Node n = nodes[i]; 962 if (n != null) { 963 assert n.id == i; 964 if (i != nextId) { 965 assert n.id > nextId; 966 n.id = nextId; 967 nodes[nextId] = n; 968 nodes[i] = null; 969 } 970 nextId++; 971 } 972 } 973 if (isModificationCountsEnabled()) { 974 // This will cause any current iteration to fail with an assertion 975 Arrays.fill(nodeModCounts, 0); 976 Arrays.fill(nodeUsageModCounts, 0); 977 } 978 nodesSize = nextId; 979 compressions++; 980 nodesDeletedBeforeLastCompression += nodesDeletedSinceLastCompression; 981 nodesDeletedSinceLastCompression = 0; 982 return true; 983 } 984 985 /** 986 * Returns an {@link Iterable} providing all the live nodes whose type is compatible with 987 * {@code type}. 988 * 989 * @param nodeClass the type of node to return 990 * @return an {@link Iterable} providing all the matching nodes 991 */ 992 public <T extends Node & IterableNodeType> NodeIterable<T> getNodes(final NodeClass<T> nodeClass) { 993 return new NodeIterable<T>() { 994 995 @Override 996 public Iterator<T> iterator() { 997 return new TypedGraphNodeIterator<>(nodeClass, Graph.this); 998 } 999 }; 1000 } 1001 1002 /** 1003 * Returns whether the graph contains at least one node of the given type. 1004 * 1005 * @param type the type of node that is checked for occurrence 1006 * @return whether there is at least one such node 1007 */ 1008 public <T extends Node & IterableNodeType> boolean hasNode(final NodeClass<T> type) { 1009 return getNodes(type).iterator().hasNext(); 1010 } 1011 1012 /** 1013 * @param iterableId 1014 * @return the first live Node with a matching iterableId 1015 */ 1016 Node getIterableNodeStart(int iterableId) { 1017 if (iterableNodesFirst.size() <= iterableId) { 1018 return null; 1019 } 1020 Node start = iterableNodesFirst.get(iterableId); 1021 if (start == null || !start.isDeleted()) { 1022 return start; 1023 } 1024 return findFirstLiveIterable(iterableId, start); 1025 } 1026 1027 private Node findFirstLiveIterable(int iterableId, Node node) { 1028 Node start = node; 1029 while (start != null && start.isDeleted()) { 1030 start = start.typeCacheNext; 1031 } 1032 /* 1033 * Multiple threads iterating nodes can update this cache simultaneously. This is a benign 1034 * race, since all threads update it to the same value. 1035 */ 1036 iterableNodesFirst.set(iterableId, start); 1037 if (start == null) { 1038 iterableNodesLast.set(iterableId, start); 1039 } 1040 return start; 1041 } 1042 1043 /** 1044 * @param node 1045 * @return return the first live Node with a matching iterableId starting from {@code node} 1046 */ 1047 Node getIterableNodeNext(Node node) { 1048 if (node == null) { 1049 return null; 1050 } 1051 Node n = node; 1052 if (n == null || !n.isDeleted()) { 1053 return n; 1054 } 1055 1056 return findNextLiveiterable(node); 1057 } 1058 1059 private Node findNextLiveiterable(Node start) { 1060 Node n = start; 1061 while (n != null && n.isDeleted()) { 1062 n = n.typeCacheNext; 1063 } 1064 if (n == null) { 1065 // Only dead nodes after this one 1066 start.typeCacheNext = null; 1067 int nodeClassId = start.getNodeClass().iterableId(); 1068 assert nodeClassId != Node.NOT_ITERABLE; 1069 iterableNodesLast.set(nodeClassId, start); 1070 } else { 1071 // Everything in between is dead 1072 start.typeCacheNext = n; 1073 } 1074 return n; 1075 } 1076 1077 public NodeBitMap createNodeBitMap() { 1078 return new NodeBitMap(this); 1079 } 1080 1081 public <T> NodeMap<T> createNodeMap() { 1082 return new NodeMap<>(this); 1083 } 1084 1085 public NodeFlood createNodeFlood() { 1086 return new NodeFlood(this); 1087 } 1088 1089 public NodeWorkList createNodeWorkList() { 1090 return new NodeWorkList.SingletonNodeWorkList(this); 1091 } 1092 1093 public NodeWorkList createIterativeNodeWorkList(boolean fill, int iterationLimitPerNode) { 1094 return new NodeWorkList.IterativeNodeWorkList(this, fill, iterationLimitPerNode); 1095 } 1096 1097 void register(Node node) { 1098 assert !isFrozen(); 1099 assert node.id() == Node.INITIAL_ID; 1100 if (nodes.length == nodesSize) { 1101 grow(); 1102 } 1103 int id = nodesSize++; 1104 nodes[id] = node; 1105 node.id = id; 1106 if (currentNodeSourcePosition != null && trackNodeSourcePosition()) { 1107 node.setNodeSourcePosition(currentNodeSourcePosition); 1108 } 1109 if (TrackNodeInsertion.getValue(getOptions())) { 1110 node.setInsertionPosition(new NodeInsertionStackTrace()); 1111 } 1112 1113 updateNodeCaches(node); 1114 1115 if (nodeEventListener != null) { 1116 nodeEventListener.event(NodeEvent.NODE_ADDED, node); 1117 } 1118 afterRegister(node); 1119 } 1120 1121 private void grow() { 1122 Node[] newNodes = new Node[(nodesSize * 2) + 1]; 1123 System.arraycopy(nodes, 0, newNodes, 0, nodesSize); 1124 nodes = newNodes; 1125 } 1126 1127 @SuppressWarnings("unused") 1128 protected void afterRegister(Node node) { 1129 1130 } 1131 1132 @SuppressWarnings("unused") 1133 private void postDeserialization() { 1134 recomputeIterableNodeLists(); 1135 } 1136 1137 /** 1138 * Rebuilds the lists used to support {@link #getNodes(NodeClass)}. This is useful for 1139 * serialization where the underlying {@linkplain NodeClass#iterableId() iterable ids} may have 1140 * changed. 1141 */ 1142 private void recomputeIterableNodeLists() { 1143 iterableNodesFirst.clear(); 1144 iterableNodesLast.clear(); 1145 for (Node node : nodes) { 1146 if (node != null && node.isAlive()) { 1147 updateNodeCaches(node); 1148 } 1149 } 1150 } 1151 1152 private void updateNodeCaches(Node node) { 1153 int nodeClassId = node.getNodeClass().iterableId(); 1154 if (nodeClassId != Node.NOT_ITERABLE) { 1155 while (iterableNodesFirst.size() <= nodeClassId) { 1156 iterableNodesFirst.add(null); 1157 iterableNodesLast.add(null); 1158 } 1159 Node prev = iterableNodesLast.get(nodeClassId); 1160 if (prev != null) { 1161 prev.typeCacheNext = node; 1162 } else { 1163 iterableNodesFirst.set(nodeClassId, node); 1164 } 1165 iterableNodesLast.set(nodeClassId, node); 1166 } 1167 } 1168 1169 void unregister(Node node) { 1170 assert !isFrozen(); 1171 assert !node.isDeleted() : node; 1172 if (node.getNodeClass().isLeafNode() && node.getNodeClass().valueNumberable()) { 1173 removeNodeFromCache(node); 1174 } 1175 nodes[node.id] = null; 1176 nodesDeletedSinceLastCompression++; 1177 1178 if (nodeEventListener != null) { 1179 nodeEventListener.event(NodeEvent.NODE_ADDED, node); 1180 } 1181 1182 // nodes aren't removed from the type cache here - they will be removed during iteration 1183 } 1184 1185 public boolean verify() { 1186 if (Options.VerifyGraalGraphs.getValue(options)) { 1187 for (Node node : getNodes()) { 1188 try { 1189 try { 1190 assert node.verify(); 1191 } catch (AssertionError t) { 1192 throw new GraalError(t); 1193 } catch (RuntimeException t) { 1194 throw new GraalError(t); 1195 } 1196 } catch (GraalError e) { 1197 throw GraalGraphError.transformAndAddContext(e, node).addContext(this); 1198 } 1199 } 1200 } 1201 return true; 1202 } 1203 1204 public boolean verifySourcePositions(boolean performConsistencyCheck) { 1205 if (trackNodeSourcePosition()) { 1206 ResolvedJavaMethod root = null; 1207 for (Node node : getNodes()) { 1208 NodeSourcePosition pos = node.getNodeSourcePosition(); 1209 if (pos != null) { 1210 if (root == null) { 1211 root = pos.getRootMethod(); 1212 } else { 1213 assert pos.verifyRootMethod(root) : node; 1214 } 1215 } 1216 1217 // More strict node-type-specific check 1218 if (performConsistencyCheck) { 1219 // Disabled due to GR-10445. 1220 // node.verifySourcePosition(); 1221 } 1222 } 1223 } 1224 return true; 1225 } 1226 1227 public Node getNode(int id) { 1228 return nodes[id]; 1229 } 1230 1231 /** 1232 * Returns the number of node ids generated so far. 1233 * 1234 * @return the number of node ids generated so far 1235 */ 1236 int nodeIdCount() { 1237 return nodesSize; 1238 } 1239 1240 /** 1241 * Adds duplicates of the nodes in {@code newNodes} to this graph. This will recreate any edges 1242 * between the duplicate nodes. The {@code replacement} map can be used to replace a node from 1243 * the source graph by a given node (which must already be in this graph). Edges between 1244 * duplicate and replacement nodes will also be recreated so care should be taken regarding the 1245 * matching of node types in the replacement map. 1246 * 1247 * @param newNodes the nodes to be duplicated 1248 * @param replacementsMap the replacement map (can be null if no replacement is to be performed) 1249 * @return a map which associates the original nodes from {@code nodes} to their duplicates 1250 */ 1251 public UnmodifiableEconomicMap<Node, Node> addDuplicates(Iterable<? extends Node> newNodes, final Graph oldGraph, int estimatedNodeCount, EconomicMap<Node, Node> replacementsMap) { 1252 DuplicationReplacement replacements; 1253 if (replacementsMap == null) { 1254 replacements = null; 1255 } else { 1256 replacements = new MapReplacement(replacementsMap); 1257 } 1258 return addDuplicates(newNodes, oldGraph, estimatedNodeCount, replacements); 1259 } 1260 1261 public interface DuplicationReplacement { 1262 1263 Node replacement(Node original); 1264 } 1265 1266 private static final class MapReplacement implements DuplicationReplacement { 1267 1268 private final EconomicMap<Node, Node> map; 1269 1270 MapReplacement(EconomicMap<Node, Node> map) { 1271 this.map = map; 1272 } 1273 1274 @Override 1275 public Node replacement(Node original) { 1276 Node replacement = map.get(original); 1277 return replacement != null ? replacement : original; 1278 } 1279 1280 } 1281 1282 private static final TimerKey DuplicateGraph = DebugContext.timer("DuplicateGraph"); 1283 1284 @SuppressWarnings({"all", "try"}) 1285 public EconomicMap<Node, Node> addDuplicates(Iterable<? extends Node> newNodes, final Graph oldGraph, int estimatedNodeCount, DuplicationReplacement replacements) { 1286 try (DebugCloseable s = DuplicateGraph.start(getDebug())) { 1287 return NodeClass.addGraphDuplicate(this, oldGraph, estimatedNodeCount, newNodes, replacements); 1288 } 1289 } 1290 1291 public boolean isFrozen() { 1292 return freezeState != FreezeState.Unfrozen; 1293 } 1294 1295 public void freeze() { 1296 this.freezeState = FreezeState.DeepFreeze; 1297 } 1298 1299 public void temporaryFreeze() { 1300 if (this.freezeState == FreezeState.DeepFreeze) { 1301 throw new GraalError("Graph was permanetly frozen."); 1302 } 1303 this.freezeState = FreezeState.TemporaryFreeze; 1304 } 1305 1306 public void unfreeze() { 1307 if (this.freezeState == FreezeState.DeepFreeze) { 1308 throw new GraalError("Graph was permanetly frozen."); 1309 } 1310 this.freezeState = FreezeState.Unfrozen; 1311 } 1312 }