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