/*
 * 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 Graph.

Once a node has been added to a graph, it has a graph-unique id(). Edges in the subclasses are represented with annotated fields. There are two kind of edges : Input and Successor. If a field, of a type compatible with Node, annotated with either Input and 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 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 assertTrue(boolean, String, Object...) and 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.
/** * This class is the base class for all nodes. It represents a node that can be inserted in a * {@link Graph}. * <p> * 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. * <p> * Nodes which are be value numberable should implement the {@link ValueNumberable} interface. * * <h1>Replay Compilation</h1> * * To enable deterministic replay compilation, node sets and node maps should be instantiated with * the following methods: * <ul> * <li>{@link #newSet()}</li> * <li>{@link #newSet(Collection)}</li> * <li>{@link #newMap()}</li> * <li>{@link #newMap(int)}</li> * <li>{@link #newMap(Map)}</li> * <li>{@link #newIdentityMap()}</li> * <li>{@link #newIdentityMap(int)}</li> * <li>{@link #newIdentityMap(Map)}</li> * </ul> * * <h1>Assertions and Verification</h1> * * 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 Node or NodeInputList. Nodes that update fields of type Node outside of their constructor should call Node.updateUsages(Node, Node) just prior to doing the update of the input.
/** * 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 Node or NodeInputList. Nodes that update fields of type Node outside of their constructor should call Node.updateUsages(Node, Node) just prior to doing the update of the input.
/** * 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 intrinsic method must be a compile time constant at all call sites to the intrinsic method.
/** * 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 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.
/** * 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 value(). For this reason, the signature of the annotated method must match the signature (excluding a prefix of injected parameters) of a constructor in the node class.

If the node class has a static method intrinsify with a matching signature plus a GraphBuilderContext as first argument, this method is called instead of creating the node.

/** * 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. * <p> * 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 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 Node subclass).
/** * 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 null.

/** * Determines if the stamp of the instantiated intrinsic node has its stamp set from the * return type of the annotated method. * <p> * 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 setStampFromReturnType().
/** * 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 leaf node can be replaced by another node of the same type that has exactly the same 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 inputs and successors.
/** * 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.
/** * 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 usage0, usage1 and extraUsages. The first null entry terminates the list.
/** * 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<? extends Node> nodeClass; public static final int NODE_LIST = -2; public static final int NOT_ITERABLE = -1; public Node(NodeClass<? extends Node> c) { init(c); } final void init(NodeClass<? extends Node> 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 Also:
  • newSet.newSet()
/** * @see CollectionsFactory#newSet() */
public static <E extends Node> Set<E> newSet() { return CollectionsFactory.newSet(); }
See Also:
  • newSet()
/** * @see #newSet() */
public static <E extends Node> Set<E> newSet(Collection<? extends E> c) { return CollectionsFactory.newSet(c); } public static <K extends Node, V> Map<K, V> 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 <K extends Node, V> Map<K, V> newMap(Map<K, V> 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 <K extends Node, V> Map<K, V> 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 <K extends Node, V> Map<K, V> newIdentityMap() { return CollectionsFactory.newIdentityMap(); } public static <K extends Node, V> Map<K, V> newIdentityMap(Map<K, V> m) { return CollectionsFactory.newIdentityMap(m); } public static <K extends Node, V> Map<K, V> newIdentityMap(int expectedMaxSize) { return CollectionsFactory.newIdentityMap(expectedMaxSize); }
Gets the graph context of this node.
/** * Gets the graph context of this node. */
public Graph graph() { return graph; }
Returns an iterable which can be used to traverse all non-null input edges of this node.
Returns:an iterable for all non-null input edges.
/** * 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<Node> inputs() { return nodeClass.getInputIterable(this); }
Returns an iterable which can be used to traverse all non-null input edges of this node.
Returns:an iterable for all non-null input edges.
/** * 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<Position> 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.
Params:
  • visitor – the visitor to be applied to the inputs
/** * 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.
Params:
  • visitor – the visitor to be applied to the successors
/** * 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 iterable which can be used to traverse all non-null successor edges of this node.
Returns:an iterable for all non-null successor edges.
/** * 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<Node> successors() { assert !this.isDeleted(); return nodeClass.getSuccessorIterable(this); }
Returns an iterable which can be used to traverse all successor edge positions of this node.
Returns:an iterable for all successor edge positoins.
/** * 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<Position> successorPositions() { return nodeClass.getSuccessorEdges().getPositionsIterable(this); }
Gets the maximum number of usages this node has had at any point in time.
/** * 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).
/** * Gets the list of nodes that use this node (i.e., as an input). */
public final NodeIterable<Node> usages() { return new NodeUsageIterable(this); }
Checks whether this node has no usages.
/** * Checks whether this node has no usages. */
public final boolean hasNoUsages() { return this.usage0 == null; }
Checks whether this node has usages.
/** * Checks whether this node has usages. */
public final boolean hasUsages() { return this.usage0 != null; } void reverseUsageOrder() { List<Node> 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 usages.
Params:
  • node – the node to add
/** * 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 usages.
Params:
  • node – the node to remove
Returns:whether or not usage was in the usage list
/** * 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 oldInput to newInput by removing this node from oldInput's usages and adds this node to newInput's usages.
/** * 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.
/** * 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.
/** * 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.
/** * 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 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<? extends Node> 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<Node> filter) { replaceAtUsages(other, filter, null); } public final void replaceAtUsagesAndDelete(Node other) { replaceAtUsages(other, null, this); safeDelete(); } public final void replaceAtUsagesAndDelete(Node other, Predicate<Node> filter) { replaceAtUsages(other, filter, this); safeDelete(); } protected void replaceAtUsages(Node other, Predicate<Node> 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 usages and no predecessor.
/** * 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 Simplifiable. The implementation in Node exists to obviate the need to cast a node before invoking Simplifiable.simplify(SimplifierTool).
Params:
  • tool –
/** * 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(); }
Params:
  • newNode – the result of cloning this node or raw allocating a copy of this node
  • type – the type of edges to process
  • edgesToCopy – if type is in this set, the edges are copied otherwise they are cleared
/** * @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<Edges.Type> 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<Edges.Type> WithNoEdges = EnumSet.noneOf(Edges.Type.class); public static final EnumSet<Edges.Type> WithAllEdges = EnumSet.allOf(Edges.Type.class); public static final EnumSet<Edges.Type> WithOnlyInputEdges = EnumSet.of(Inputs); public static final EnumSet<Edges.Type> WithOnlySucessorEdges = EnumSet.of(Successors);
Makes a copy of this node in(to) a given graph.
Params:
  • 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
  • edgesToCopy – specifies the edges to be copied. The edges not specified in this set are initialized to their default value (i.e., null for a direct edge, an empty list for an edge list)
Returns:the copy of this node
/** * 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<Edges.Type> edgesToCopy) { final NodeClass<? extends Node> 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.
Returns: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<? extends Node> 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 Successor, but some node classes (like EndNode) may return different nodes.
/** * 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<? extends Node> cfgSuccessors() { return successors(); }
Nodes always use an identity hash code.
/** * 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.
/** * Equality tests must rely solely on identity. */
@Override public final boolean equals(Object obj) { return super.equals(obj); }
Provides a Map of properties of this node for use in debugging (e.g., to view in the ideal graph visualizer).
/** * 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<Object, Object> getDebugProperties() { return getDebugProperties(new HashMap<>()); }
Fills a 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.
Params:
  • map –
/** * 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<Object, Object> getDebugProperties(Map<Object, Object> 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 toString(Verbosity) with Verbosity.Short.
/** * 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 Verbosity.
/** * 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<Object, Object> 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 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 Objects.equals(Object, Object). The result of this method undefined if other.getClass() != this.getClass().
Params:
  • other – a node of exactly the same type as this node
Returns:true if the data fields of this object and other are equal
/** * 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); } }