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