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