--- /dev/null 2017-01-22 10:16:57.869617664 -0800 +++ new/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Node.java 2017-02-15 16:58:35.462571767 -0800 @@ -0,0 +1,1190 @@ +/* + * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.graph; + +import static org.graalvm.compiler.graph.Edges.Type.Inputs; +import static org.graalvm.compiler.graph.Edges.Type.Successors; +import static org.graalvm.compiler.graph.Graph.isModificationCountsEnabled; +import static org.graalvm.compiler.graph.UnsafeAccess.UNSAFE; + +import java.lang.annotation.ElementType; +import java.lang.annotation.RetentionPolicy; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Formattable; +import java.util.FormattableFlags; +import java.util.Formatter; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Predicate; + +import org.graalvm.compiler.core.common.CollectionsFactory; +import org.graalvm.compiler.core.common.Fields; +import org.graalvm.compiler.debug.DebugCloseable; +import org.graalvm.compiler.debug.Fingerprint; +import org.graalvm.compiler.graph.Graph.NodeEvent; +import org.graalvm.compiler.graph.Graph.NodeEventListener; +import org.graalvm.compiler.graph.Graph.Options; +import org.graalvm.compiler.graph.iterators.NodeIterable; +import org.graalvm.compiler.graph.iterators.NodePredicate; +import org.graalvm.compiler.graph.spi.Simplifiable; +import org.graalvm.compiler.graph.spi.SimplifierTool; +import org.graalvm.compiler.nodeinfo.InputType; +import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodeinfo.Verbosity; + +import sun.misc.Unsafe; + +/** + * This class is the base class for all nodes. It represents a node that can be inserted in a + * {@link Graph}. + *

+ * Once a node has been added to a graph, it has a graph-unique {@link #id()}. Edges in the + * subclasses are represented with annotated fields. There are two kind of edges : {@link Input} and + * {@link Successor}. If a field, of a type compatible with {@link Node}, annotated with either + * {@link Input} and {@link Successor} is not null, then there is an edge from this node to the node + * this field points to. + *

+ * Nodes which are be value numberable should implement the {@link ValueNumberable} interface. + * + *

Replay Compilation

+ * + * To enable deterministic replay compilation, node sets and node maps should be instantiated with + * the following methods: + * + * + *

Assertions and Verification

+ * + * The Node class supplies the {@link #assertTrue(boolean, String, Object...)} and + * {@link #assertFalse(boolean, String, Object...)} methods, which will check the supplied boolean + * and throw a VerificationError if it has the wrong value. Both methods will always either throw an + * exception or return true. They can thus be used within an assert statement, so that the check is + * only performed if assertions are enabled. + */ +@NodeInfo +public abstract class Node implements Cloneable, Formattable, NodeInterface { + + public static final NodeClass TYPE = null; + public static final boolean USE_UNSAFE_TO_CLONE = Graph.Options.CloneNodesWithUnsafe.getValue(); + + static final int DELETED_ID_START = -1000000000; + static final int INITIAL_ID = -1; + static final int ALIVE_ID_START = 0; + + // The use of fully qualified class names here and in the rest + // of this file works around a problem javac has resolving symbols + + /** + * Denotes a non-optional (non-null) node input. This should be applied to exactly the fields of + * a node that are of type {@link Node} or {@link NodeInputList}. Nodes that update fields of + * type {@link Node} outside of their constructor should call + * {@link Node#updateUsages(Node, Node)} just prior to doing the update of the input. + */ + @java.lang.annotation.Retention(RetentionPolicy.RUNTIME) + @java.lang.annotation.Target(ElementType.FIELD) + public static @interface Input { + InputType value() default InputType.Value; + } + + /** + * Denotes an optional (nullable) node input. This should be applied to exactly the fields of a + * node that are of type {@link Node} or {@link NodeInputList}. Nodes that update fields of type + * {@link Node} outside of their constructor should call {@link Node#updateUsages(Node, Node)} + * just prior to doing the update of the input. + */ + @java.lang.annotation.Retention(RetentionPolicy.RUNTIME) + @java.lang.annotation.Target(ElementType.FIELD) + public static @interface OptionalInput { + InputType value() default InputType.Value; + } + + @java.lang.annotation.Retention(RetentionPolicy.RUNTIME) + @java.lang.annotation.Target(ElementType.FIELD) + public static @interface Successor { + } + + /** + * Denotes that a parameter of an {@linkplain NodeIntrinsic intrinsic} method must be a compile + * time constant at all call sites to the intrinsic method. + */ + @java.lang.annotation.Retention(RetentionPolicy.RUNTIME) + @java.lang.annotation.Target(ElementType.PARAMETER) + public static @interface ConstantNodeParameter { + } + + /** + * Denotes an injected parameter in a {@linkplain NodeIntrinsic node intrinsic} constructor. If + * the constructor is called as part of node intrinsification, the node intrinsifier will inject + * an argument for the annotated parameter. Injected parameters must precede all non-injected + * parameters in a constructor. + */ + @java.lang.annotation.Retention(RetentionPolicy.RUNTIME) + @java.lang.annotation.Target(ElementType.PARAMETER) + public static @interface InjectedNodeParameter { + } + + /** + * Annotates a method that can be replaced by a compiler intrinsic. A (resolved) call to the + * annotated method can be replaced with an instance of the node class denoted by + * {@link #value()}. For this reason, the signature of the annotated method must match the + * signature (excluding a prefix of {@linkplain InjectedNodeParameter injected} parameters) of a + * constructor in the node class. + *

+ * If the node class has a static method {@code intrinsify} with a matching signature plus a + * {@code GraphBuilderContext} as first argument, this method is called instead of creating the + * node. + */ + @java.lang.annotation.Retention(RetentionPolicy.RUNTIME) + @java.lang.annotation.Target(ElementType.METHOD) + public static @interface NodeIntrinsic { + + /** + * Gets the {@link Node} subclass instantiated when intrinsifying a call to the annotated + * method. If not specified, then the class in which the annotated method is declared is + * used (and is assumed to be a {@link Node} subclass). + */ + Class value() default NodeIntrinsic.class; + + /** + * Determines if the stamp of the instantiated intrinsic node has its stamp set from the + * return type of the annotated method. + *

+ * When it is set to true, the stamp that is passed in to the constructor of ValueNode is + * ignored and can therefore safely be {@code null}. + */ + boolean setStampFromReturnType() default false; + + /** + * Determines if the stamp of the instantiated intrinsic node is guaranteed to be non-null. + * Generally used in conjunction with {@link #setStampFromReturnType()}. + */ + boolean returnStampIsNonNull() default false; + } + + /** + * Marker for a node that can be replaced by another node via global value numbering. A + * {@linkplain NodeClass#isLeafNode() leaf} node can be replaced by another node of the same + * type that has exactly the same {@linkplain NodeClass#getData() data} values. A non-leaf node + * can be replaced by another node of the same type that has exactly the same data values as + * well as the same {@linkplain Node#inputs() inputs} and {@linkplain Node#successors() + * successors}. + */ + public interface ValueNumberable { + } + + /** + * Marker interface for nodes that contains other nodes. When the inputs to this node changes, + * users of this node should also be placed on the work list for canonicalization. + */ + public interface IndirectCanonicalization { + } + + private Graph graph; + int id; + + // this next pointer is used in Graph to implement fast iteration over NodeClass types, it + // therefore points to the next Node of the same type. + Node typeCacheNext; + + static final int INLINE_USAGE_COUNT = 2; + private static final Node[] NO_NODES = {}; + + /** + * Head of usage list. The elements of the usage list in order are {@link #usage0}, + * {@link #usage1} and {@link #extraUsages}. The first null entry terminates the list. + */ + Node usage0; + Node usage1; + Node[] extraUsages; + int extraUsagesCount; + + private Node predecessor; + private NodeClass nodeClass; + + public static final int NODE_LIST = -2; + public static final int NOT_ITERABLE = -1; + + public Node(NodeClass c) { + init(c); + } + + final void init(NodeClass c) { + assert c.getJavaClass() == this.getClass(); + this.nodeClass = c; + id = INITIAL_ID; + extraUsages = NO_NODES; + } + + final int id() { + return id; + } + + @Override + public Node asNode() { + return this; + } + + /** + * @see CollectionsFactory#newSet() + */ + public static Set newSet() { + return CollectionsFactory.newSet(); + } + + /** + * @see #newSet() + */ + public static Set newSet(Collection c) { + return CollectionsFactory.newSet(c); + } + + public static Map newMap() { + // Node.equals() and Node.hashCode() are final and are implemented + // purely in terms of identity so HashMap and IdentityHashMap with + // Node's as keys will behave the same. We choose to use the latter + // due to its lighter memory footprint. + return newIdentityMap(); + } + + public static Map newMap(Map m) { + // Node.equals() and Node.hashCode() are final and are implemented + // purely in terms of identity so HashMap and IdentityHashMap with + // Node's as keys will behave the same. We choose to use the latter + // due to its lighter memory footprint. + return newIdentityMap(m); + } + + public static Map newMap(int expectedMaxSize) { + // Node.equals() and Node.hashCode() are final and are implemented + // purely in terms of identity so HashMap and IdentityHashMap with + // Node's as keys will behave the same. We choose to use the latter + // due to its lighter memory footprint. + return newIdentityMap(expectedMaxSize); + } + + public static Map newIdentityMap() { + return CollectionsFactory.newIdentityMap(); + } + + public static Map newIdentityMap(Map m) { + return CollectionsFactory.newIdentityMap(m); + } + + public static Map newIdentityMap(int expectedMaxSize) { + return CollectionsFactory.newIdentityMap(expectedMaxSize); + } + + /** + * Gets the graph context of this node. + */ + public Graph graph() { + return graph; + } + + /** + * Returns an {@link NodeIterable iterable} which can be used to traverse all non-null input + * edges of this node. + * + * @return an {@link NodeIterable iterable} for all non-null input edges. + */ + public NodeIterable inputs() { + return nodeClass.getInputIterable(this); + } + + /** + * Returns an {@link Iterable iterable} which can be used to traverse all non-null input edges + * of this node. + * + * @return an {@link Iterable iterable} for all non-null input edges. + */ + public Iterable inputPositions() { + return nodeClass.getInputEdges().getPositionsIterable(this); + } + + public abstract static class EdgeVisitor { + + public abstract Node apply(Node source, Node target); + + } + + /** + * Applies the given visitor to all inputs of this node. + * + * @param visitor the visitor to be applied to the inputs + */ + public void applyInputs(EdgeVisitor visitor) { + nodeClass.applyInputs(this, visitor); + } + + /** + * Applies the given visitor to all successors of this node. + * + * @param visitor the visitor to be applied to the successors + */ + public void applySuccessors(EdgeVisitor visitor) { + nodeClass.applySuccessors(this, visitor); + } + + /** + * Returns an {@link NodeIterable iterable} which can be used to traverse all non-null successor + * edges of this node. + * + * @return an {@link NodeIterable iterable} for all non-null successor edges. + */ + public NodeIterable successors() { + assert !this.isDeleted(); + return nodeClass.getSuccessorIterable(this); + } + + /** + * Returns an {@link Iterable iterable} which can be used to traverse all successor edge + * positions of this node. + * + * @return an {@link Iterable iterable} for all successor edge positoins. + */ + public Iterable successorPositions() { + return nodeClass.getSuccessorEdges().getPositionsIterable(this); + } + + /** + * Gets the maximum number of usages this node has had at any point in time. + */ + public int getUsageCount() { + if (usage0 == null) { + return 0; + } + if (usage1 == null) { + return 1; + } + return 2 + extraUsagesCount; + } + + /** + * Gets the list of nodes that use this node (i.e., as an input). + */ + public final NodeIterable usages() { + return new NodeUsageIterable(this); + } + + /** + * Checks whether this node has no usages. + */ + public final boolean hasNoUsages() { + return this.usage0 == null; + } + + /** + * Checks whether this node has usages. + */ + public final boolean hasUsages() { + return this.usage0 != null; + } + + void reverseUsageOrder() { + List snapshot = this.usages().snapshot(); + for (Node n : snapshot) { + this.removeUsage(n); + } + Collections.reverse(snapshot); + for (Node n : snapshot) { + this.addUsage(n); + } + } + + /** + * Adds a given node to this node's {@linkplain #usages() usages}. + * + * @param node the node to add + */ + void addUsage(Node node) { + incUsageModCount(); + if (usage0 == null) { + usage0 = node; + } else if (usage1 == null) { + usage1 = node; + } else { + int length = extraUsages.length; + if (length == 0) { + extraUsages = new Node[4]; + } else if (extraUsagesCount == length) { + Node[] newExtraUsages = new Node[length * 2 + 1]; + System.arraycopy(extraUsages, 0, newExtraUsages, 0, length); + extraUsages = newExtraUsages; + } + extraUsages[extraUsagesCount++] = node; + } + } + + private void movUsageFromEndTo(int destIndex) { + int lastIndex = this.getUsageCount() - 1; + if (destIndex == 0) { + if (lastIndex == 0) { + usage0 = null; + return; + } else if (lastIndex == 1) { + usage0 = usage1; + usage1 = null; + return; + } else { + usage0 = extraUsages[lastIndex - INLINE_USAGE_COUNT]; + } + } else if (destIndex == 1) { + if (lastIndex == 1) { + usage1 = null; + return; + } + usage1 = extraUsages[lastIndex - INLINE_USAGE_COUNT]; + } else { + Node n = extraUsages[lastIndex - INLINE_USAGE_COUNT]; + extraUsages[destIndex - INLINE_USAGE_COUNT] = n; + } + extraUsages[lastIndex - INLINE_USAGE_COUNT] = null; + this.extraUsagesCount--; + } + + /** + * Removes a given node from this node's {@linkplain #usages() usages}. + * + * @param node the node to remove + * @return whether or not {@code usage} was in the usage list + */ + public boolean removeUsage(Node node) { + assert node != null; + // It is critical that this method maintains the invariant that + // the usage list has no null element preceding a non-null element + incUsageModCount(); + if (usage0 == node) { + this.movUsageFromEndTo(0); + return true; + } + if (usage1 == node) { + this.movUsageFromEndTo(1); + return true; + } + for (int i = this.extraUsagesCount - 1; i >= 0; i--) { + if (extraUsages[i] == node) { + this.movUsageFromEndTo(i + INLINE_USAGE_COUNT); + return true; + } + } + return false; + } + + public final Node predecessor() { + return predecessor; + } + + public final int modCount() { + if (isModificationCountsEnabled() && graph != null) { + return graph.modCount(this); + } + return 0; + } + + final void incModCount() { + if (isModificationCountsEnabled() && graph != null) { + graph.incModCount(this); + } + } + + final int usageModCount() { + if (isModificationCountsEnabled() && graph != null) { + return graph.usageModCount(this); + } + return 0; + } + + final void incUsageModCount() { + if (isModificationCountsEnabled() && graph != null) { + graph.incUsageModCount(this); + } + } + + public boolean isDeleted() { + return id <= DELETED_ID_START; + } + + public boolean isAlive() { + return id >= ALIVE_ID_START; + } + + /** + * Updates the usages sets of the given nodes after an input slot is changed from + * {@code oldInput} to {@code newInput} by removing this node from {@code oldInput}'s usages and + * adds this node to {@code newInput}'s usages. + */ + protected void updateUsages(Node oldInput, Node newInput) { + assert isAlive() && (newInput == null || newInput.isAlive()) : "adding " + newInput + " to " + this + " instead of " + oldInput; + if (oldInput != newInput) { + if (oldInput != null) { + boolean result = removeThisFromUsages(oldInput); + assert assertTrue(result, "not found in usages, old input: %s", oldInput); + } + maybeNotifyInputChanged(this); + if (newInput != null) { + newInput.addUsage(this); + } + if (oldInput != null && oldInput.hasNoUsages()) { + maybeNotifyZeroUsages(oldInput); + } + } + } + + protected void updateUsagesInterface(NodeInterface oldInput, NodeInterface newInput) { + updateUsages(oldInput == null ? null : oldInput.asNode(), newInput == null ? null : newInput.asNode()); + } + + /** + * Updates the predecessor of the given nodes after a successor slot is changed from + * oldSuccessor to newSuccessor: removes this node from oldSuccessor's predecessors and adds + * this node to newSuccessor's predecessors. + */ + protected void updatePredecessor(Node oldSuccessor, Node newSuccessor) { + assert isAlive() && (newSuccessor == null || newSuccessor.isAlive()) || newSuccessor == null && !isAlive() : "adding " + newSuccessor + " to " + this + " instead of " + oldSuccessor; + assert graph == null || !graph.isFrozen(); + if (oldSuccessor != newSuccessor) { + if (oldSuccessor != null) { + assert assertTrue(newSuccessor == null || oldSuccessor.predecessor == this, "wrong predecessor in old successor (%s): %s, should be %s", oldSuccessor, oldSuccessor.predecessor, this); + oldSuccessor.predecessor = null; + } + if (newSuccessor != null) { + assert assertTrue(newSuccessor.predecessor == null, "unexpected non-null predecessor in new successor (%s): %s, this=%s", newSuccessor, newSuccessor.predecessor, this); + newSuccessor.predecessor = this; + } + } + } + + void initialize(Graph newGraph) { + assert assertTrue(id == INITIAL_ID, "unexpected id: %d", id); + this.graph = newGraph; + newGraph.register(this); + this.getNodeClass().registerAtInputsAsUsage(this); + this.getNodeClass().registerAtSuccessorsAsPredecessor(this); + } + + /** + * The position of the bytecode that generated this node. + */ + NodeSourcePosition sourcePosition; + + /** + * Gets the source position information for this node or null if it doesn't exist. + */ + + public NodeSourcePosition getNodeSourcePosition() { + return sourcePosition; + } + + /** + * Set the source position to {@code sourcePosition}. + */ + public void setNodeSourcePosition(NodeSourcePosition sourcePosition) { + this.sourcePosition = sourcePosition; + if (sourcePosition != null && graph != null && !graph.seenNodeSourcePosition) { + graph.seenNodeSourcePosition = true; + } + } + + public DebugCloseable withNodeSourcePosition() { + return graph.withNodeSourcePosition(this); + } + + public final NodeClass getNodeClass() { + return nodeClass; + } + + public boolean isAllowedUsageType(InputType type) { + if (type == InputType.Value) { + return false; + } + return getNodeClass().getAllowedUsageTypes().contains(type); + } + + private boolean checkReplaceWith(Node other) { + assert assertTrue(graph == null || !graph.isFrozen(), "cannot modify frozen graph"); + assert assertFalse(other == this, "cannot replace a node with itself"); + assert assertFalse(isDeleted(), "cannot replace deleted node"); + assert assertTrue(other == null || !other.isDeleted(), "cannot replace with deleted node %s", other); + return true; + } + + public final void replaceAtUsages(Node other) { + replaceAtUsages(other, null, null); + } + + public final void replaceAtUsages(Node other, Predicate filter) { + replaceAtUsages(other, filter, null); + } + + public final void replaceAtUsagesAndDelete(Node other) { + replaceAtUsages(other, null, this); + safeDelete(); + } + + public final void replaceAtUsagesAndDelete(Node other, Predicate filter) { + replaceAtUsages(other, filter, this); + safeDelete(); + } + + protected void replaceAtUsages(Node other, Predicate filter, Node toBeDeleted) { + assert checkReplaceWith(other); + int i = 0; + while (i < this.getUsageCount()) { + Node usage = this.getUsageAt(i); + if (filter == null || filter.test(usage)) { + boolean result = usage.getNodeClass().replaceFirstInput(usage, this, other); + assert assertTrue(result, "not found in inputs, usage: %s", usage); + /* + * Don't notify for nodes which are about to be deleted. + */ + if (toBeDeleted == null || usage != toBeDeleted) { + maybeNotifyInputChanged(usage); + } + if (other != null) { + other.addUsage(usage); + } + this.movUsageFromEndTo(i); + } else { + ++i; + } + } + } + + public Node getUsageAt(int index) { + if (index == 0) { + return this.usage0; + } else if (index == 1) { + return this.usage1; + } else { + return this.extraUsages[index - INLINE_USAGE_COUNT]; + } + } + + public void replaceAtMatchingUsages(Node other, NodePredicate usagePredicate) { + assert checkReplaceWith(other); + int index = 0; + while (index < this.getUsageCount()) { + Node usage = getUsageAt(index); + if (usagePredicate.apply(usage)) { + boolean result = usage.getNodeClass().replaceFirstInput(usage, this, other); + assert assertTrue(result, "not found in inputs, usage: %s", usage); + if (other != null) { + maybeNotifyInputChanged(usage); + other.addUsage(usage); + } + this.movUsageFromEndTo(index); + } else { + index++; + } + } + } + + public void replaceAtUsages(InputType type, Node other) { + assert checkReplaceWith(other); + for (Node usage : usages().snapshot()) { + for (Position pos : usage.inputPositions()) { + if (pos.getInputType() == type && pos.get(usage) == this) { + pos.set(usage, other); + } + } + } + } + + private void maybeNotifyInputChanged(Node node) { + if (graph != null) { + assert !graph.isFrozen(); + NodeEventListener listener = graph.nodeEventListener; + if (listener != null) { + listener.inputChanged(node); + } + if (Fingerprint.ENABLED) { + Fingerprint.submit("%s: %s", NodeEvent.INPUT_CHANGED, node); + } + } + } + + public void maybeNotifyZeroUsages(Node node) { + if (graph != null) { + assert !graph.isFrozen(); + NodeEventListener listener = graph.nodeEventListener; + if (listener != null && node.isAlive()) { + listener.usagesDroppedToZero(node); + } + if (Fingerprint.ENABLED) { + Fingerprint.submit("%s: %s", NodeEvent.ZERO_USAGES, node); + } + } + } + + public void replaceAtPredecessor(Node other) { + assert checkReplaceWith(other); + if (predecessor != null) { + boolean result = predecessor.getNodeClass().replaceFirstSuccessor(predecessor, this, other); + assert assertTrue(result, "not found in successors, predecessor: %s", predecessor); + predecessor.updatePredecessor(this, other); + } + } + + public void replaceAndDelete(Node other) { + assert checkReplaceWith(other); + assert other != null; + replaceAtUsages(other); + replaceAtPredecessor(other); + this.safeDelete(); + } + + public void replaceFirstSuccessor(Node oldSuccessor, Node newSuccessor) { + if (nodeClass.replaceFirstSuccessor(this, oldSuccessor, newSuccessor)) { + updatePredecessor(oldSuccessor, newSuccessor); + } + } + + public void replaceFirstInput(Node oldInput, Node newInput) { + if (nodeClass.replaceFirstInput(this, oldInput, newInput)) { + updateUsages(oldInput, newInput); + } + } + + public void clearInputs() { + assert assertFalse(isDeleted(), "cannot clear inputs of deleted node"); + getNodeClass().unregisterAtInputsAsUsage(this); + } + + boolean removeThisFromUsages(Node n) { + return n.removeUsage(this); + } + + public void clearSuccessors() { + assert assertFalse(isDeleted(), "cannot clear successors of deleted node"); + getNodeClass().unregisterAtSuccessorsAsPredecessor(this); + } + + private boolean checkDeletion() { + assertTrue(isAlive(), "must be alive"); + assertTrue(hasNoUsages(), "cannot delete node %s because of usages: %s", this, usages()); + assertTrue(predecessor == null, "cannot delete node %s because of predecessor: %s", this, predecessor); + return true; + } + + /** + * Removes this node from its graph. This node must have no {@linkplain Node#usages() usages} + * and no {@linkplain #predecessor() predecessor}. + */ + public void safeDelete() { + assert checkDeletion(); + this.clearInputs(); + this.clearSuccessors(); + markDeleted(); + } + + public void markDeleted() { + graph.unregister(this); + id = DELETED_ID_START - id; + assert isDeleted(); + } + + public final Node copyWithInputs() { + return copyWithInputs(true); + } + + public final Node copyWithInputs(boolean insertIntoGraph) { + Node newNode = clone(insertIntoGraph ? graph : null, WithOnlyInputEdges); + if (insertIntoGraph) { + for (Node input : inputs()) { + input.addUsage(newNode); + } + } + return newNode; + } + + /** + * Must be overridden by subclasses that implement {@link Simplifiable}. The implementation in + * {@link Node} exists to obviate the need to cast a node before invoking + * {@link Simplifiable#simplify(SimplifierTool)}. + * + * @param tool + */ + public void simplify(SimplifierTool tool) { + throw new UnsupportedOperationException(); + } + + /** + * @param newNode the result of cloning this node or {@link Unsafe#allocateInstance(Class) raw + * allocating} a copy of this node + * @param type the type of edges to process + * @param edgesToCopy if {@code type} is in this set, the edges are copied otherwise they are + * cleared + */ + private void copyOrClearEdgesForClone(Node newNode, Edges.Type type, EnumSet edgesToCopy) { + if (edgesToCopy.contains(type)) { + getNodeClass().getEdges(type).copy(this, newNode); + } else { + if (USE_UNSAFE_TO_CLONE) { + // The direct edges are already null + getNodeClass().getEdges(type).initializeLists(newNode, this); + } else { + getNodeClass().getEdges(type).clear(newNode); + } + } + } + + public static final EnumSet WithNoEdges = EnumSet.noneOf(Edges.Type.class); + public static final EnumSet WithAllEdges = EnumSet.allOf(Edges.Type.class); + public static final EnumSet WithOnlyInputEdges = EnumSet.of(Inputs); + public static final EnumSet WithOnlySucessorEdges = EnumSet.of(Successors); + + /** + * Makes a copy of this node in(to) a given graph. + * + * @param into the graph in which the copy will be registered (which may be this node's graph) + * or null if the copy should not be registered in a graph + * @param edgesToCopy specifies the edges to be copied. The edges not specified in this set are + * initialized to their default value (i.e., {@code null} for a direct edge, an empty + * list for an edge list) + * @return the copy of this node + */ + final Node clone(Graph into, EnumSet edgesToCopy) { + final NodeClass nodeClassTmp = getNodeClass(); + boolean useIntoLeafNodeCache = false; + if (into != null) { + if (nodeClassTmp.valueNumberable() && nodeClassTmp.isLeafNode()) { + useIntoLeafNodeCache = true; + Node otherNode = into.findNodeInCache(this); + if (otherNode != null) { + return otherNode; + } + } + } + + Node newNode = null; + try { + if (USE_UNSAFE_TO_CLONE) { + newNode = (Node) UNSAFE.allocateInstance(getClass()); + newNode.nodeClass = nodeClassTmp; + nodeClassTmp.getData().copy(this, newNode); + copyOrClearEdgesForClone(newNode, Inputs, edgesToCopy); + copyOrClearEdgesForClone(newNode, Successors, edgesToCopy); + } else { + newNode = (Node) this.clone(); + newNode.typeCacheNext = null; + newNode.usage0 = null; + newNode.usage1 = null; + newNode.predecessor = null; + newNode.extraUsagesCount = 0; + copyOrClearEdgesForClone(newNode, Inputs, edgesToCopy); + copyOrClearEdgesForClone(newNode, Successors, edgesToCopy); + } + } catch (Exception e) { + throw new GraalGraphError(e).addContext(this); + } + newNode.graph = into; + newNode.id = INITIAL_ID; + if (into != null) { + into.register(newNode); + } + newNode.extraUsages = NO_NODES; + + if (into != null && useIntoLeafNodeCache) { + into.putNodeIntoCache(newNode); + } + if (graph != null && into != null && sourcePosition != null) { + newNode.setNodeSourcePosition(sourcePosition); + } + newNode.afterClone(this); + return newNode; + } + + protected void afterClone(@SuppressWarnings("unused") Node other) { + } + + protected boolean verifyInputs() { + for (Position pos : inputPositions()) { + Node input = pos.get(this); + if (input == null) { + assertTrue(pos.isInputOptional(), "non-optional input %s cannot be null in %s (fix nullness or use @OptionalInput)", pos, this); + } else { + assertFalse(input.isDeleted(), "input was deleted"); + assertTrue(input.isAlive(), "input is not alive yet, i.e., it was not yet added to the graph"); + assertTrue(pos.getInputType() == InputType.Unchecked || input.isAllowedUsageType(pos.getInputType()), "invalid usage type %s %s", input, pos.getInputType()); + } + } + return true; + } + + public boolean verify() { + assertTrue(isAlive(), "cannot verify inactive nodes (id=%d)", id); + assertTrue(graph() != null, "null graph"); + verifyInputs(); + if (Options.VerifyGraalGraphEdges.getValue()) { + verifyEdges(); + } + return true; + } + + /** + * Perform expensive verification of inputs, usages, predecessors and successors. + * + * @return true + */ + public boolean verifyEdges() { + for (Node input : inputs()) { + assertTrue(input == null || input.usages().contains(this), "missing usage of %s in input %s", this, input); + } + + for (Node successor : successors()) { + assertTrue(successor.predecessor() == this, "missing predecessor in %s (actual: %s)", successor, successor.predecessor()); + assertTrue(successor.graph() == graph(), "mismatching graph in successor %s", successor); + } + for (Node usage : usages()) { + assertFalse(usage.isDeleted(), "usage %s must never be deleted", usage); + assertTrue(usage.inputs().contains(this), "missing input in usage %s", usage); + boolean foundThis = false; + for (Position pos : usage.inputPositions()) { + if (pos.get(usage) == this) { + foundThis = true; + if (pos.getInputType() != InputType.Unchecked) { + assertTrue(isAllowedUsageType(pos.getInputType()), "invalid input of type %s from %s to %s (%s)", pos.getInputType(), usage, this, pos.getName()); + } + } + } + assertTrue(foundThis, "missing input in usage %s", usage); + } + + if (predecessor != null) { + assertFalse(predecessor.isDeleted(), "predecessor %s must never be deleted", predecessor); + assertTrue(predecessor.successors().contains(this), "missing successor in predecessor %s", predecessor); + } + return true; + } + + public boolean assertTrue(boolean condition, String message, Object... args) { + if (condition) { + return true; + } else { + throw fail(message, args); + } + } + + public boolean assertFalse(boolean condition, String message, Object... args) { + if (condition) { + throw fail(message, args); + } else { + return true; + } + } + + protected VerificationError fail(String message, Object... args) throws GraalGraphError { + throw new VerificationError(message, args).addContext(this); + } + + public Iterable cfgPredecessors() { + if (predecessor == null) { + return Collections.emptySet(); + } else { + return Collections.singleton(predecessor); + } + } + + /** + * Returns an iterator that will provide all control-flow successors of this node. Normally this + * will be the contents of all fields annotated with {@link Successor}, but some node classes + * (like EndNode) may return different nodes. + */ + public Iterable cfgSuccessors() { + return successors(); + } + + /** + * Nodes always use an {@linkplain System#identityHashCode(Object) identity} hash code. + */ + @Override + public final int hashCode() { + return System.identityHashCode(this); + } + + /** + * Equality tests must rely solely on identity. + */ + @Override + public final boolean equals(Object obj) { + return super.equals(obj); + } + + /** + * Provides a {@link Map} of properties of this node for use in debugging (e.g., to view in the + * ideal graph visualizer). + */ + public final Map getDebugProperties() { + return getDebugProperties(new HashMap<>()); + } + + /** + * Fills a {@link Map} with properties of this node for use in debugging (e.g., to view in the + * ideal graph visualizer). Subclasses overriding this method should also fill the map using + * their superclass. + * + * @param map + */ + public Map getDebugProperties(Map map) { + Fields properties = getNodeClass().getData(); + for (int i = 0; i < properties.getCount(); i++) { + map.put(properties.getName(i), properties.get(this, i)); + } + NodeSourcePosition pos = getNodeSourcePosition(); + if (pos != null) { + map.put("nodeSourcePosition", pos); + } + return map; + } + + /** + * This method is a shortcut for {@link #toString(Verbosity)} with {@link Verbosity#Short}. + */ + @Override + public final String toString() { + return toString(Verbosity.Short); + } + + /** + * Creates a String representation for this node with a given {@link Verbosity}. + */ + public String toString(Verbosity verbosity) { + switch (verbosity) { + case Id: + return Integer.toString(id); + case Name: + return getNodeClass().shortName(); + case Short: + return toString(Verbosity.Id) + "|" + toString(Verbosity.Name); + case Long: + return toString(Verbosity.Short); + case Debugger: + case All: { + StringBuilder str = new StringBuilder(); + str.append(toString(Verbosity.Short)).append(" { "); + for (Map.Entry entry : getDebugProperties().entrySet()) { + str.append(entry.getKey()).append("=").append(entry.getValue()).append(", "); + } + str.append(" }"); + return str.toString(); + } + default: + throw new RuntimeException("unknown verbosity: " + verbosity); + } + } + + @Deprecated + public int getId() { + return id; + } + + @Override + public void formatTo(Formatter formatter, int flags, int width, int precision) { + if ((flags & FormattableFlags.ALTERNATE) == FormattableFlags.ALTERNATE) { + formatter.format("%s", toString(Verbosity.Id)); + } else if ((flags & FormattableFlags.UPPERCASE) == FormattableFlags.UPPERCASE) { + // Use All here since Long is only slightly longer than Short. + formatter.format("%s", toString(Verbosity.All)); + } else { + formatter.format("%s", toString(Verbosity.Short)); + } + + boolean neighborsAlternate = ((flags & FormattableFlags.LEFT_JUSTIFY) == FormattableFlags.LEFT_JUSTIFY); + int neighborsFlags = (neighborsAlternate ? FormattableFlags.ALTERNATE | FormattableFlags.LEFT_JUSTIFY : 0); + if (width > 0) { + if (this.predecessor != null) { + formatter.format(" pred={"); + this.predecessor.formatTo(formatter, neighborsFlags, width - 1, 0); + formatter.format("}"); + } + + for (Position position : this.inputPositions()) { + Node input = position.get(this); + if (input != null) { + formatter.format(" "); + formatter.format(position.getName()); + formatter.format("={"); + input.formatTo(formatter, neighborsFlags, width - 1, 0); + formatter.format("}"); + } + } + } + + if (precision > 0) { + if (!hasNoUsages()) { + formatter.format(" usages={"); + int z = 0; + for (Node usage : usages()) { + if (z != 0) { + formatter.format(", "); + } + usage.formatTo(formatter, neighborsFlags, 0, precision - 1); + ++z; + } + formatter.format("}"); + } + + for (Position position : this.successorPositions()) { + Node successor = position.get(this); + if (successor != null) { + formatter.format(" "); + formatter.format(position.getName()); + formatter.format("={"); + successor.formatTo(formatter, neighborsFlags, 0, precision - 1); + formatter.format("}"); + } + } + } + } + + /** + * Determines if this node's {@link NodeClass#getData() data} fields are equal to the data + * fields of another node of the same type. Primitive fields are compared by value and + * non-primitive fields are compared by {@link Objects#equals(Object, Object)}. + * + * The result of this method undefined if {@code other.getClass() != this.getClass()}. + * + * @param other a node of exactly the same type as this node + * @return true if the data fields of this object and {@code other} are equal + */ + public boolean valueEquals(Node other) { + return getNodeClass().dataEquals(this, other); + } + + public final void pushInputs(NodeStack stack) { + getNodeClass().pushInputs(this, stack); + } +}