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 private final class AddInputsFilter extends Node.EdgeVisitor { 476 477 @Override 478 public Node apply(Node self, Node input) { 479 if (!input.isAlive()) { 480 assert !input.isDeleted(); 481 return addOrUniqueWithInputs(input); 482 } else { 483 return input; 484 } 485 } 486 487 } 488 489 private AddInputsFilter addInputsFilter = new AddInputsFilter(); 490 491 private <T extends Node> void addInputs(T node) { 492 node.applyInputs(addInputsFilter); 493 } 494 495 private <T extends Node> T addHelper(T node) { 496 node.initialize(this); 497 return node; 498 } 499 500 /** 501 * The type of events sent to a {@link NodeEventListener}. 502 */ 503 public enum NodeEvent { 504 /** 505 * A node's input is changed. 506 */ 507 INPUT_CHANGED, 508 509 /** 510 * A node's {@linkplain Node#usages() usages} count dropped to zero. 511 */ 512 ZERO_USAGES, 513 514 /** 515 * A node was added to a graph. 516 */ 517 NODE_ADDED, 518 519 /** 520 * A node was removed from the graph. 521 */ 522 NODE_REMOVED; 523 } 524 525 /** 526 * Client interested in one or more node related events. 527 */ 528 public abstract static class NodeEventListener { 529 530 /** 531 * A method called when a change event occurs. 532 * 533 * This method dispatches the event to user-defined triggers. The methods that change the 534 * graph (typically in Graph and Node) must call this method to dispatch the event. 535 * 536 * @param e an event 537 * @param node the node related to {@code e} 538 */ 539 final void event(NodeEvent e, Node node) { 540 switch (e) { 541 case INPUT_CHANGED: 542 inputChanged(node); 543 break; 544 case ZERO_USAGES: 545 usagesDroppedToZero(node); 546 break; 547 case NODE_ADDED: 548 nodeAdded(node); 549 break; 550 case NODE_REMOVED: 551 nodeRemoved(node); 552 break; 553 } 554 changed(e, node); 555 } 556 557 /** 558 * Notifies this listener about any change event in the graph. 559 * 560 * @param e an event 561 * @param node the node related to {@code e} 562 */ 563 public void changed(NodeEvent e, Node node) { 564 } 565 566 /** 567 * Notifies this listener about a change in a node's inputs. 568 * 569 * @param node a node who has had one of its inputs changed 570 */ 571 public void inputChanged(Node node) { 572 } 573 574 /** 575 * Notifies this listener of a node becoming unused. 576 * 577 * @param node a node whose {@link Node#usages()} just became empty 578 */ 579 public void usagesDroppedToZero(Node node) { 580 } 581 582 /** 583 * Notifies this listener of an added node. 584 * 585 * @param node a node that was just added to the graph 586 */ 587 public void nodeAdded(Node node) { 588 } 589 590 /** 591 * Notifies this listener of a removed node. 592 * 593 * @param node 594 */ 595 public void nodeRemoved(Node node) { 596 } 597 } 598 599 /** 600 * Registers a given {@link NodeEventListener} with the enclosing graph until this object is 601 * {@linkplain #close() closed}. 602 */ 603 public final class NodeEventScope implements AutoCloseable { 604 NodeEventScope(NodeEventListener listener) { 605 if (nodeEventListener == null) { 606 nodeEventListener = listener; 607 } else { 608 nodeEventListener = new ChainedNodeEventListener(listener, nodeEventListener); 609 } 610 } 611 612 @Override 613 public void close() { 614 assert nodeEventListener != null; 615 if (nodeEventListener instanceof ChainedNodeEventListener) { 616 nodeEventListener = ((ChainedNodeEventListener) nodeEventListener).next; 617 } else { 618 nodeEventListener = null; 619 } 620 } 621 } 622 623 private static class ChainedNodeEventListener extends NodeEventListener { 624 625 NodeEventListener head; 626 NodeEventListener next; 627 628 ChainedNodeEventListener(NodeEventListener head, NodeEventListener next) { 629 this.head = head; 630 this.next = next; 631 } 632 633 @Override 634 public void nodeAdded(Node node) { 635 head.event(NodeEvent.NODE_ADDED, node); 636 next.event(NodeEvent.NODE_ADDED, node); 637 } 638 639 @Override 640 public void inputChanged(Node node) { 641 head.event(NodeEvent.INPUT_CHANGED, node); 642 next.event(NodeEvent.INPUT_CHANGED, node); 643 } 644 645 @Override 646 public void usagesDroppedToZero(Node node) { 647 head.event(NodeEvent.ZERO_USAGES, node); 648 next.event(NodeEvent.ZERO_USAGES, node); 649 } 650 651 @Override 652 public void nodeRemoved(Node node) { 653 head.event(NodeEvent.NODE_REMOVED, node); 654 next.event(NodeEvent.NODE_REMOVED, node); 655 } 656 657 @Override 658 public void changed(NodeEvent e, Node node) { 659 head.event(e, node); 660 next.event(e, node); 661 } 662 } 663 664 /** 665 * Registers a given {@link NodeEventListener} with this graph. This should be used in 666 * conjunction with try-with-resources statement as follows: 667 * 668 * <pre> 669 * try (NodeEventScope nes = graph.trackNodeEvents(listener)) { 670 * // make changes to the graph 671 * } 672 * </pre> 673 */ 674 public NodeEventScope trackNodeEvents(NodeEventListener listener) { 675 return new NodeEventScope(listener); 676 } 677 678 /** 679 * Looks for a node <i>similar</i> to {@code node} and returns it if found. Otherwise 680 * {@code node} is added to this graph and returned. 681 * 682 * @return a node similar to {@code node} if one exists, otherwise {@code node} 683 */ 684 public <T extends Node & ValueNumberable> T unique(T node) { 685 return uniqueHelper(node); 686 } 687 688 <T extends Node> T uniqueHelper(T node) { 689 assert node.getNodeClass().valueNumberable(); 690 T other = this.findDuplicate(node); 691 if (other != null) { 692 return other; 693 } else { 694 T result = addHelper(node); 695 if (node.getNodeClass().isLeafNode()) { 696 putNodeIntoCache(result); 697 } 698 return result; 699 } 700 } 701 702 void removeNodeFromCache(Node node) { 703 assert node.graph() == this || node.graph() == null; 704 assert node.getNodeClass().valueNumberable(); 705 assert node.getNodeClass().isLeafNode() : node.getClass(); 706 707 int leafId = node.getNodeClass().getLeafId(); 708 if (cachedLeafNodes != null && cachedLeafNodes.length > leafId && cachedLeafNodes[leafId] != null) { 709 cachedLeafNodes[leafId].removeKey(node); 710 } 711 } 712 713 @SuppressWarnings({"unchecked", "rawtypes"}) 714 void putNodeIntoCache(Node node) { 715 assert node.graph() == this || node.graph() == null; 716 assert node.getNodeClass().valueNumberable(); 717 assert node.getNodeClass().isLeafNode() : node.getClass(); 718 719 int leafId = node.getNodeClass().getLeafId(); 720 if (cachedLeafNodes == null || cachedLeafNodes.length <= leafId) { 721 EconomicMap[] newLeafNodes = new EconomicMap[leafId + 1]; 722 if (cachedLeafNodes != null) { 723 System.arraycopy(cachedLeafNodes, 0, newLeafNodes, 0, cachedLeafNodes.length); 724 } 725 cachedLeafNodes = newLeafNodes; 726 } 727 728 if (cachedLeafNodes[leafId] == null) { 729 cachedLeafNodes[leafId] = EconomicMap.create(NODE_VALUE_COMPARE); 730 } 731 732 cachedLeafNodes[leafId].put(node, node); 733 } 734 735 Node findNodeInCache(Node node) { 736 int leafId = node.getNodeClass().getLeafId(); 737 if (cachedLeafNodes == null || cachedLeafNodes.length <= leafId || cachedLeafNodes[leafId] == null) { 738 return null; 739 } 740 741 Node result = cachedLeafNodes[leafId].get(node); 742 assert result == null || result.isAlive() : result; 743 return result; 744 } 745 746 /** 747 * Returns a possible duplicate for the given node in the graph or {@code null} if no such 748 * duplicate exists. 749 */ 750 @SuppressWarnings("unchecked") 751 public <T extends Node> T findDuplicate(T node) { 752 NodeClass<?> nodeClass = node.getNodeClass(); 753 assert nodeClass.valueNumberable(); 754 if (nodeClass.isLeafNode()) { 755 // Leaf node: look up in cache 756 Node cachedNode = findNodeInCache(node); 757 if (cachedNode != null && cachedNode != node) { 758 return (T) cachedNode; 759 } else { 760 return null; 761 } 762 } else { 763 /* 764 * Non-leaf node: look for another usage of the node's inputs that has the same data, 765 * inputs and successors as the node. To reduce the cost of this computation, only the 766 * input with lowest usage count is considered. If this node is the only user of any 767 * input then the search can terminate early. The usage count is only incremented once 768 * the Node is in the Graph, so account for that in the test. 769 */ 770 final int earlyExitUsageCount = node.graph() != null ? 1 : 0; 771 int minCount = Integer.MAX_VALUE; 772 Node minCountNode = null; 773 for (Node input : node.inputs()) { 774 int usageCount = input.getUsageCount(); 775 if (usageCount == earlyExitUsageCount) { 776 return null; 777 } else if (usageCount < minCount) { 778 minCount = usageCount; 779 minCountNode = input; 780 } 781 } 782 if (minCountNode != null) { 783 for (Node usage : minCountNode.usages()) { 784 if (usage != node && nodeClass == usage.getNodeClass() && node.valueEquals(usage) && nodeClass.equalInputs(node, usage) && 785 nodeClass.equalSuccessors(node, usage)) { 786 return (T) usage; 787 } 788 } 789 return null; 790 } 791 return null; 792 } 793 } 794 795 public boolean isNew(Mark mark, Node node) { 796 return node.id >= mark.getValue(); 797 } 798 799 /** 800 * A snapshot of the {@linkplain Graph#getNodeCount() live node count} in a graph. 801 */ 802 public static class Mark extends NodeIdAccessor { 803 private final int value; 804 805 Mark(Graph graph) { 806 super(graph); 807 this.value = graph.nodeIdCount(); 808 } 809 810 @Override 811 public boolean equals(Object obj) { 812 if (obj instanceof Mark) { 813 Mark other = (Mark) obj; 814 return other.getValue() == getValue() && other.getGraph() == getGraph(); 815 } 816 return false; 817 } 818 819 @Override 820 public int hashCode() { 821 return value ^ (epoch + 11); 822 } 823 824 /** 825 * Determines if this mark is positioned at the first live node in the graph. 826 */ 827 public boolean isStart() { 828 return value == 0; 829 } 830 831 /** 832 * Gets the {@linkplain Graph#getNodeCount() live node count} of the associated graph when 833 * this object was created. 834 */ 835 int getValue() { 836 return value; 837 } 838 839 /** 840 * Determines if this mark still represents the {@linkplain Graph#getNodeCount() live node 841 * count} of the graph. 842 */ 843 public boolean isCurrent() { 844 return value == graph.nodeIdCount(); 845 } 846 } 847 848 /** 849 * Gets a mark that can be used with {@link #getNewNodes}. 850 */ 851 public Mark getMark() { 852 return new Mark(this); 853 } 854 855 /** 856 * Returns an {@link Iterable} providing all nodes added since the last {@link Graph#getMark() 857 * mark}. 858 */ 859 public NodeIterable<Node> getNewNodes(Mark mark) { 860 final int index = mark == null ? 0 : mark.getValue(); 861 return new NodeIterable<Node>() { 862 863 @Override 864 public Iterator<Node> iterator() { 865 return new GraphNodeIterator(Graph.this, index); 866 } 867 }; 868 } 869 870 /** 871 * Returns an {@link Iterable} providing all the live nodes. 872 * 873 * @return an {@link Iterable} providing all the live nodes. 874 */ 875 public NodeIterable<Node> getNodes() { 876 return new NodeIterable<Node>() { 877 878 @Override 879 public Iterator<Node> iterator() { 880 return new GraphNodeIterator(Graph.this); 881 } 882 883 @Override 884 public int count() { 885 return getNodeCount(); 886 } 887 }; 888 } 889 890 // Fully qualified annotation name is required to satisfy javac 891 @org.graalvm.compiler.nodeinfo.NodeInfo(cycles = CYCLES_IGNORED, size = SIZE_IGNORED) 892 static final class PlaceHolderNode extends Node { 893 894 public static final NodeClass<PlaceHolderNode> TYPE = NodeClass.create(PlaceHolderNode.class); 895 896 protected PlaceHolderNode() { 897 super(TYPE); 898 } 899 900 } 901 902 private static final CounterKey GraphCompressions = DebugContext.counter("GraphCompressions"); 903 904 /** 905 * If the {@linkplain Options#GraphCompressionThreshold compression threshold} is met, the list 906 * of nodes is compressed such that all non-null entries precede all null entries while 907 * preserving the ordering between the nodes within the list. 908 */ 909 public boolean maybeCompress() { 910 if (debug.isDumpEnabledForMethod() || debug.isLogEnabledForMethod()) { 911 return false; 912 } 913 int liveNodeCount = getNodeCount(); 914 int liveNodePercent = liveNodeCount * 100 / nodesSize; 915 int compressionThreshold = Options.GraphCompressionThreshold.getValue(options); 916 if (compressionThreshold == 0 || liveNodePercent >= compressionThreshold) { 917 return false; 918 } 919 GraphCompressions.increment(debug); 920 int nextId = 0; 921 for (int i = 0; nextId < liveNodeCount; i++) { 922 Node n = nodes[i]; 923 if (n != null) { 924 assert n.id == i; 925 if (i != nextId) { 926 assert n.id > nextId; 927 n.id = nextId; 928 nodes[nextId] = n; 929 nodes[i] = null; 930 } 931 nextId++; 932 } 933 } 934 if (isModificationCountsEnabled()) { 935 // This will cause any current iteration to fail with an assertion 936 Arrays.fill(nodeModCounts, 0); 937 Arrays.fill(nodeUsageModCounts, 0); 938 } 939 nodesSize = nextId; 940 compressions++; 941 nodesDeletedBeforeLastCompression += nodesDeletedSinceLastCompression; 942 nodesDeletedSinceLastCompression = 0; 943 return true; 944 } 945 946 /** 947 * Returns an {@link Iterable} providing all the live nodes whose type is compatible with 948 * {@code type}. 949 * 950 * @param nodeClass the type of node to return 951 * @return an {@link Iterable} providing all the matching nodes 952 */ 953 public <T extends Node & IterableNodeType> NodeIterable<T> getNodes(final NodeClass<T> nodeClass) { 954 return new NodeIterable<T>() { 955 956 @Override 957 public Iterator<T> iterator() { 958 return new TypedGraphNodeIterator<>(nodeClass, Graph.this); 959 } 960 }; 961 } 962 963 /** 964 * Returns whether the graph contains at least one node of the given type. 965 * 966 * @param type the type of node that is checked for occurrence 967 * @return whether there is at least one such node 968 */ 969 public <T extends Node & IterableNodeType> boolean hasNode(final NodeClass<T> type) { 970 return getNodes(type).iterator().hasNext(); 971 } 972 973 /** 974 * @param iterableId 975 * @return the first live Node with a matching iterableId 976 */ 977 Node getIterableNodeStart(int iterableId) { 978 if (iterableNodesFirst.size() <= iterableId) { 979 return null; 980 } 981 Node start = iterableNodesFirst.get(iterableId); 982 if (start == null || !start.isDeleted()) { 983 return start; 984 } 985 return findFirstLiveIterable(iterableId, start); 986 } 987 988 private Node findFirstLiveIterable(int iterableId, Node node) { 989 Node start = node; 990 while (start != null && start.isDeleted()) { 991 start = start.typeCacheNext; 992 } 993 /* 994 * Multiple threads iterating nodes can update this cache simultaneously. This is a benign 995 * race, since all threads update it to the same value. 996 */ 997 iterableNodesFirst.set(iterableId, start); 998 if (start == null) { 999 iterableNodesLast.set(iterableId, start); 1000 } 1001 return start; 1002 } 1003 1004 /** 1005 * @param node 1006 * @return return the first live Node with a matching iterableId starting from {@code node} 1007 */ 1008 Node getIterableNodeNext(Node node) { 1009 if (node == null) { 1010 return null; 1011 } 1012 Node n = node; 1013 if (n == null || !n.isDeleted()) { 1014 return n; 1015 } 1016 1017 return findNextLiveiterable(node); 1018 } 1019 1020 private Node findNextLiveiterable(Node start) { 1021 Node n = start; 1022 while (n != null && n.isDeleted()) { 1023 n = n.typeCacheNext; 1024 } 1025 if (n == null) { 1026 // Only dead nodes after this one 1027 start.typeCacheNext = null; 1028 int nodeClassId = start.getNodeClass().iterableId(); 1029 assert nodeClassId != Node.NOT_ITERABLE; 1030 iterableNodesLast.set(nodeClassId, start); 1031 } else { 1032 // Everything in between is dead 1033 start.typeCacheNext = n; 1034 } 1035 return n; 1036 } 1037 1038 public NodeBitMap createNodeBitMap() { 1039 return new NodeBitMap(this); 1040 } 1041 1042 public <T> NodeMap<T> createNodeMap() { 1043 return new NodeMap<>(this); 1044 } 1045 1046 public NodeFlood createNodeFlood() { 1047 return new NodeFlood(this); 1048 } 1049 1050 public NodeWorkList createNodeWorkList() { 1051 return new NodeWorkList.SingletonNodeWorkList(this); 1052 } 1053 1054 public NodeWorkList createIterativeNodeWorkList(boolean fill, int iterationLimitPerNode) { 1055 return new NodeWorkList.IterativeNodeWorkList(this, fill, iterationLimitPerNode); 1056 } 1057 1058 void register(Node node) { 1059 assert !isFrozen(); 1060 assert node.id() == Node.INITIAL_ID; 1061 if (nodes.length == nodesSize) { 1062 grow(); 1063 } 1064 int id = nodesSize++; 1065 nodes[id] = node; 1066 node.id = id; 1067 if (currentNodeSourcePosition != null) { 1068 node.setNodeSourcePosition(currentNodeSourcePosition); 1069 } 1070 seenNodeSourcePosition = seenNodeSourcePosition || node.getNodeSourcePosition() != null; 1071 1072 updateNodeCaches(node); 1073 1074 if (nodeEventListener != null) { 1075 nodeEventListener.event(NodeEvent.NODE_ADDED, node); 1076 } 1077 afterRegister(node); 1078 } 1079 1080 private void grow() { 1081 Node[] newNodes = new Node[(nodesSize * 2) + 1]; 1082 System.arraycopy(nodes, 0, newNodes, 0, nodesSize); 1083 nodes = newNodes; 1084 } 1085 1086 @SuppressWarnings("unused") 1087 protected void afterRegister(Node node) { 1088 1089 } 1090 1091 @SuppressWarnings("unused") 1092 private void postDeserialization() { 1093 recomputeIterableNodeLists(); 1094 } 1095 1096 /** 1097 * Rebuilds the lists used to support {@link #getNodes(NodeClass)}. This is useful for 1098 * serialization where the underlying {@linkplain NodeClass#iterableId() iterable ids} may have 1099 * changed. 1100 */ 1101 private void recomputeIterableNodeLists() { 1102 iterableNodesFirst.clear(); 1103 iterableNodesLast.clear(); 1104 for (Node node : nodes) { 1105 if (node != null && node.isAlive()) { 1106 updateNodeCaches(node); 1107 } 1108 } 1109 } 1110 1111 private void updateNodeCaches(Node node) { 1112 int nodeClassId = node.getNodeClass().iterableId(); 1113 if (nodeClassId != Node.NOT_ITERABLE) { 1114 while (iterableNodesFirst.size() <= nodeClassId) { 1115 iterableNodesFirst.add(null); 1116 iterableNodesLast.add(null); 1117 } 1118 Node prev = iterableNodesLast.get(nodeClassId); 1119 if (prev != null) { 1120 prev.typeCacheNext = node; 1121 } else { 1122 iterableNodesFirst.set(nodeClassId, node); 1123 } 1124 iterableNodesLast.set(nodeClassId, node); 1125 } 1126 } 1127 1128 void unregister(Node node) { 1129 assert !isFrozen(); 1130 assert !node.isDeleted() : node; 1131 if (node.getNodeClass().isLeafNode() && node.getNodeClass().valueNumberable()) { 1132 removeNodeFromCache(node); 1133 } 1134 nodes[node.id] = null; 1135 nodesDeletedSinceLastCompression++; 1136 1137 if (nodeEventListener != null) { 1138 nodeEventListener.event(NodeEvent.NODE_ADDED, node); 1139 } 1140 1141 // nodes aren't removed from the type cache here - they will be removed during iteration 1142 } 1143 1144 public boolean verify() { 1145 if (Options.VerifyGraalGraphs.getValue(options)) { 1146 for (Node node : getNodes()) { 1147 try { 1148 try { 1149 assert node.verify(); 1150 } catch (AssertionError t) { 1151 throw new GraalError(t); 1152 } catch (RuntimeException t) { 1153 throw new GraalError(t); 1154 } 1155 } catch (GraalError e) { 1156 throw GraalGraphError.transformAndAddContext(e, node).addContext(this); 1157 } 1158 } 1159 } 1160 return true; 1161 } 1162 1163 public Node getNode(int id) { 1164 return nodes[id]; 1165 } 1166 1167 /** 1168 * Returns the number of node ids generated so far. 1169 * 1170 * @return the number of node ids generated so far 1171 */ 1172 int nodeIdCount() { 1173 return nodesSize; 1174 } 1175 1176 /** 1177 * Adds duplicates of the nodes in {@code newNodes} to this graph. This will recreate any edges 1178 * between the duplicate nodes. The {@code replacement} map can be used to replace a node from 1179 * the source graph by a given node (which must already be in this graph). Edges between 1180 * duplicate and replacement nodes will also be recreated so care should be taken regarding the 1181 * matching of node types in the replacement map. 1182 * 1183 * @param newNodes the nodes to be duplicated 1184 * @param replacementsMap the replacement map (can be null if no replacement is to be performed) 1185 * @return a map which associates the original nodes from {@code nodes} to their duplicates 1186 */ 1187 public UnmodifiableEconomicMap<Node, Node> addDuplicates(Iterable<? extends Node> newNodes, final Graph oldGraph, int estimatedNodeCount, EconomicMap<Node, Node> replacementsMap) { 1188 DuplicationReplacement replacements; 1189 if (replacementsMap == null) { 1190 replacements = null; 1191 } else { 1192 replacements = new MapReplacement(replacementsMap); 1193 } 1194 return addDuplicates(newNodes, oldGraph, estimatedNodeCount, replacements); 1195 } 1196 1197 public interface DuplicationReplacement { 1198 1199 Node replacement(Node original); 1200 } 1201 1202 private static final class MapReplacement implements DuplicationReplacement { 1203 1204 private final EconomicMap<Node, Node> map; 1205 1206 MapReplacement(EconomicMap<Node, Node> map) { 1207 this.map = map; 1208 } 1209 1210 @Override 1211 public Node replacement(Node original) { 1212 Node replacement = map.get(original); 1213 return replacement != null ? replacement : original; 1214 } 1215 1216 } 1217 1218 private static final TimerKey DuplicateGraph = DebugContext.timer("DuplicateGraph"); 1219 1220 @SuppressWarnings({"all", "try"}) 1221 public EconomicMap<Node, Node> addDuplicates(Iterable<? extends Node> newNodes, final Graph oldGraph, int estimatedNodeCount, DuplicationReplacement replacements) { 1222 try (DebugCloseable s = DuplicateGraph.start(getDebug())) { 1223 return NodeClass.addGraphDuplicate(this, oldGraph, estimatedNodeCount, newNodes, replacements); 1224 } 1225 } 1226 1227 public boolean isFrozen() { 1228 return freezeState != FreezeState.Unfrozen; 1229 } 1230 1231 public void freeze() { 1232 this.freezeState = FreezeState.DeepFreeze; 1233 } 1234 1235 public void temporaryFreeze() { 1236 if (this.freezeState == FreezeState.DeepFreeze) { 1237 throw new GraalError("Graph was permanetly frozen."); 1238 } 1239 this.freezeState = FreezeState.TemporaryFreeze; 1240 } 1241 1242 public void unfreeze() { 1243 if (this.freezeState == FreezeState.DeepFreeze) { 1244 throw new GraalError("Graph was permanetly frozen."); 1245 } 1246 this.freezeState = FreezeState.Unfrozen; 1247 } 1248 }