use of org.graalvm.compiler.graph.Node in project graal by oracle.
the class LoopDetector method decodeFloatingNode.
/**
* Decodes a non-fixed node, but does not do any post-processing and does not register it.
*/
protected Node decodeFloatingNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId) {
long readerByteIndex = methodScope.reader.getByteIndex();
methodScope.reader.setByteIndex(methodScope.encodedGraph.nodeStartOffsets[nodeOrderId]);
NodeClass<?> nodeClass = methodScope.encodedGraph.getNodeClasses()[methodScope.reader.getUVInt()];
Node node = allocateFloatingNode(nodeClass);
if (node instanceof FixedNode) {
/*
* This is a severe error that will lead to a corrupted graph, so it is better not to
* continue decoding at all.
*/
throw shouldNotReachHere("Not a floating node: " + node.getClass().getName());
}
/* Read the inputs of the node, possibly creating them recursively. */
makeFloatingNodeInputs(methodScope, loopScope, node);
/* Read the properties of the node. */
readProperties(methodScope, node);
/* There must not be any successors to read, since it is a non-fixed node. */
assert node.getNodeClass().getEdges(Edges.Type.Successors).getCount() == 0;
methodScope.reader.setByteIndex(readerByteIndex);
return node;
}
use of org.graalvm.compiler.graph.Node in project graal by oracle.
the class LoopDetector method findLoopExits.
private void findLoopExits(Loop loop) {
/*
* Backward marking of loop nodes: Starting with the known loop ends, we mark all nodes that
* are reachable until we hit the loop begin. All successors of loop nodes that are not
* marked as loop nodes themselves are exits of the loop. We mark all successors, and then
* subtract the loop nodes, to find the exits.
*/
List<Node> possibleExits = new ArrayList<>();
NodeBitMap visited = graph.createNodeBitMap();
Deque<Node> stack = new ArrayDeque<>();
for (EndNode loopEnd : loop.ends) {
stack.push(loopEnd);
visited.mark(loopEnd);
}
while (!stack.isEmpty()) {
Node current = stack.pop();
if (current == loop.header) {
continue;
}
if (!graph.isNew(methodScope.methodStartMark, current)) {
/*
* The current node is before the method that contains the exploded loop. The loop
* must have a second entry point, i.e., it is an irreducible loop.
*/
loop.irreducible = true;
return;
}
for (Node predecessor : current.cfgPredecessors()) {
if (predecessor instanceof LoopExitNode) {
/*
* Inner loop. We do not need to mark every node of it, instead we just continue
* marking at the loop header.
*/
LoopBeginNode innerLoopBegin = ((LoopExitNode) predecessor).loopBegin();
if (!visited.isMarked(innerLoopBegin)) {
stack.push(innerLoopBegin);
visited.mark(innerLoopBegin);
/*
* All loop exits of the inner loop possibly need a LoopExit of our loop.
* Because we are processing inner loops first, we are guaranteed to already
* have all exits of the inner loop.
*/
for (LoopExitNode exit : innerLoopBegin.loopExits()) {
possibleExits.add(exit);
}
}
} else if (!visited.isMarked(predecessor)) {
stack.push(predecessor);
visited.mark(predecessor);
if (predecessor instanceof ControlSplitNode) {
for (Node succ : predecessor.cfgSuccessors()) {
/*
* We would not need to mark the current node, and would not need to
* mark visited nodes. But it is easier to just mark everything, since
* we subtract all visited nodes in the end anyway. Note that at this
* point we do not have the complete visited information, so we would
* always mark too many possible exits.
*/
possibleExits.add(succ);
}
}
}
}
}
/*
* Now we know all the actual loop exits. Ideally, we would insert LoopExit nodes for them.
* However, a LoopExit needs a valid FrameState that captures the state at the point where
* we exit the loop. During graph decoding, we create a FrameState for every exploded loop
* iteration. We need to do a forward marking until we hit the next such point. This puts
* some nodes into the loop that are actually not part of the loop.
*
* In some cases, we did not create a FrameState during graph decoding: when there was no
* LoopExit in the original loop that we exploded. This happens for code paths that lead
* immediately to a DeoptimizeNode.
*
* Both cases mimic the behavior of the BytecodeParser, which also puts more nodes than
* necessary into a loop because it computes loop information based on bytecodes, before the
* actual parsing.
*/
for (Node succ : possibleExits) {
if (!visited.contains(succ)) {
stack.push(succ);
visited.mark(succ);
assert !methodScope.loopExplosionMerges.contains(succ);
}
}
while (!stack.isEmpty()) {
Node current = stack.pop();
assert visited.isMarked(current);
assert current instanceof ControlSinkNode || current instanceof LoopEndNode || current.cfgSuccessors().iterator().hasNext() : "Must not reach a node that has not been decoded yet";
for (Node successor : current.cfgSuccessors()) {
if (visited.isMarked(successor)) {
/* Already processed this successor. */
} else if (methodScope.loopExplosionMerges.contains(successor)) {
/*
* We have a FrameState for the successor. The LoopExit will be inserted between
* the current node and the successor node. Since the successor node is a
* MergeNode, the current node mus be a AbstractEndNode with only that MergeNode
* as the successor.
*/
assert successor instanceof MergeNode;
assert !loop.exits.contains(current);
loop.exits.add((AbstractEndNode) current);
} else {
/* Node we have not seen yet. */
visited.mark(successor);
stack.push(successor);
}
}
}
}
use of org.graalvm.compiler.graph.Node in project graal by oracle.
the class GraphComparison method encode.
/**
* Compresses a graph to a byte array. Multiple graphs can be compressed with the same
* {@link GraphEncoder}.
*
* @param graph The graph to encode
*/
public int encode(StructuredGraph graph) {
assert objectsArray != null && nodeClassesArray != null : "finishPrepare() must be called before encode()";
NodeOrder nodeOrder = new NodeOrder(graph);
int nodeCount = nodeOrder.nextOrderId;
assert nodeOrder.orderIds.get(graph.start()) == START_NODE_ORDER_ID;
assert nodeOrder.orderIds.get(graph.start().next()) == FIRST_NODE_ORDER_ID;
long[] nodeStartOffsets = new long[nodeCount];
UnmodifiableMapCursor<Node, Integer> cursor = nodeOrder.orderIds.getEntries();
while (cursor.advance()) {
Node node = cursor.getKey();
Integer orderId = cursor.getValue();
assert !(node instanceof AbstractBeginNode) || nodeOrder.orderIds.get(((AbstractBeginNode) node).next()) == orderId + BEGIN_NEXT_ORDER_ID_OFFSET;
assert nodeStartOffsets[orderId] == 0;
nodeStartOffsets[orderId] = writer.getBytesWritten();
/* Write out the type, properties, and edges. */
NodeClass<?> nodeClass = node.getNodeClass();
writer.putUV(nodeClasses.getIndex(nodeClass));
writeEdges(node, nodeClass.getEdges(Edges.Type.Inputs), nodeOrder);
writeProperties(node, nodeClass.getData());
writeEdges(node, nodeClass.getEdges(Edges.Type.Successors), nodeOrder);
/* Special handling for some nodes that require additional information for decoding. */
if (node instanceof AbstractEndNode) {
AbstractEndNode end = (AbstractEndNode) node;
AbstractMergeNode merge = end.merge();
/*
* Write the orderId of the merge. The merge is not a successor in the Graal graph
* (only the merge has an input edge to the EndNode).
*/
writeOrderId(merge, nodeOrder);
/*
* Write all phi mappings (the oderId of the phi input for this EndNode, and the
* orderId of the phi node.
*/
writer.putUV(merge.phis().count());
for (PhiNode phi : merge.phis()) {
writeOrderId(phi.valueAt(end), nodeOrder);
writeOrderId(phi, nodeOrder);
}
} else if (node instanceof LoopExitNode) {
LoopExitNode exit = (LoopExitNode) node;
writeOrderId(exit.stateAfter(), nodeOrder);
/* Write all proxy nodes of the LoopExitNode. */
writer.putUV(exit.proxies().count());
for (ProxyNode proxy : exit.proxies()) {
writeOrderId(proxy, nodeOrder);
}
} else if (node instanceof Invoke) {
Invoke invoke = (Invoke) node;
assert invoke.stateDuring() == null : "stateDuring is not used in high-level graphs";
writeObjectId(invoke.getContextType());
writeOrderId(invoke.callTarget(), nodeOrder);
writeOrderId(invoke.stateAfter(), nodeOrder);
writeOrderId(invoke.next(), nodeOrder);
if (invoke instanceof InvokeWithExceptionNode) {
InvokeWithExceptionNode invokeWithExcpetion = (InvokeWithExceptionNode) invoke;
ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithExcpetion.exceptionEdge();
writeOrderId(invokeWithExcpetion.next().next(), nodeOrder);
writeOrderId(invokeWithExcpetion.exceptionEdge(), nodeOrder);
writeOrderId(exceptionEdge.stateAfter(), nodeOrder);
writeOrderId(exceptionEdge.next(), nodeOrder);
}
}
}
/*
* Write out the metadata (maximum fixed node order id and the table of contents with the
* start offset for all nodes).
*/
int metadataStart = TypeConversion.asS4(writer.getBytesWritten());
writer.putUV(nodeOrder.maxFixedNodeOrderId);
writer.putUV(nodeCount);
for (int i = 0; i < nodeCount; i++) {
writer.putUV(metadataStart - nodeStartOffsets[i]);
}
/* Check that the decoding of the encode graph is the same as the input. */
assert verifyEncoding(graph, new EncodedGraph(getEncoding(), metadataStart, getObjects(), getNodeClasses(), graph), architecture);
return metadataStart;
}
use of org.graalvm.compiler.graph.Node in project graal by oracle.
the class GraphComparison method verifyGraphsEqual.
public static boolean verifyGraphsEqual(StructuredGraph expectedGraph, StructuredGraph actualGraph) {
NodeMap<Node> nodeMapping = new NodeMap<>(expectedGraph);
Deque<Pair<Node, Node>> workList = new ArrayDeque<>();
pushToWorklist(expectedGraph.start(), actualGraph.start(), nodeMapping, workList);
while (!workList.isEmpty()) {
Pair<Node, Node> pair = workList.removeFirst();
Node expectedNode = pair.getLeft();
Node actualNode = pair.getRight();
assert expectedNode.getClass() == actualNode.getClass();
NodeClass<?> nodeClass = expectedNode.getNodeClass();
assert nodeClass == actualNode.getNodeClass();
if (expectedNode instanceof MergeNode) {
/* The order of the ends can be different, so ignore them. */
verifyNodesEqual(expectedNode.inputs(), actualNode.inputs(), nodeMapping, workList, true);
} else if (expectedNode instanceof PhiNode) {
verifyPhi((PhiNode) expectedNode, (PhiNode) actualNode, nodeMapping, workList);
} else {
verifyNodesEqual(expectedNode.inputs(), actualNode.inputs(), nodeMapping, workList, false);
}
verifyNodesEqual(expectedNode.successors(), actualNode.successors(), nodeMapping, workList, false);
if (expectedNode instanceof LoopEndNode) {
LoopEndNode actualLoopEnd = (LoopEndNode) actualNode;
assert actualLoopEnd.loopBegin().loopEnds().snapshot().indexOf(actualLoopEnd) == actualLoopEnd.endIndex();
} else {
for (int i = 0; i < nodeClass.getData().getCount(); i++) {
Object expectedProperty = nodeClass.getData().get(expectedNode, i);
Object actualProperty = nodeClass.getData().get(actualNode, i);
assert Objects.equals(expectedProperty, actualProperty);
}
}
if (expectedNode instanceof EndNode) {
/* Visit the merge node, which is the one and only usage of the EndNode. */
assert expectedNode.usages().count() == 1;
assert actualNode.usages().count() == 1;
verifyNodesEqual(expectedNode.usages(), actualNode.usages(), nodeMapping, workList, false);
}
if (expectedNode instanceof AbstractEndNode) {
/* Visit the input values of the merge phi functions for this EndNode. */
verifyPhis((AbstractEndNode) expectedNode, (AbstractEndNode) actualNode, nodeMapping, workList);
}
}
return true;
}
use of org.graalvm.compiler.graph.Node in project graal by oracle.
the class IfNode method splitIfAtPhi.
/**
* Take an if that is immediately dominated by a merge with a single phi and split off any paths
* where the test would be statically decidable creating a new merge below the approriate side
* of the IfNode. Any undecidable tests will continue to use the original IfNode.
*
* @param tool
*/
private boolean splitIfAtPhi(SimplifierTool tool) {
if (graph().getGuardsStage().areFrameStatesAtSideEffects()) {
// Disabled until we make sure we have no FrameState-less merges at this stage
return false;
}
if (!(predecessor() instanceof MergeNode)) {
return false;
}
MergeNode merge = (MergeNode) predecessor();
if (merge.forwardEndCount() == 1) {
// Don't bother.
return false;
}
if (merge.usages().count() != 1 || merge.phis().count() != 1) {
return false;
}
if (merge.stateAfter() != null) {
/* We'll get the chance to simplify this after frame state assignment. */
return false;
}
PhiNode phi = merge.phis().first();
if (phi.usages().count() != 1) {
/*
* For simplicity the below code assumes assumes the phi goes dead at the end so skip
* this case.
*/
return false;
}
/*
* Check that the condition uses the phi and that there is only one user of the condition
* expression.
*/
if (!conditionUses(condition(), phi)) {
return false;
}
/*
* We could additionally filter for the case that at least some of the Phi inputs or one of
* the condition inputs are constants but there are cases where a non-constant is
* simplifiable, usually where the stamp allows the question to be answered.
*/
/* Each successor of the if gets a new merge if needed. */
MergeNode trueMerge = null;
MergeNode falseMerge = null;
assert merge.stateAfter() == null;
for (EndNode end : merge.forwardEnds().snapshot()) {
Node value = phi.valueAt(end);
LogicNode result = computeCondition(tool, condition, phi, value);
if (result instanceof LogicConstantNode) {
merge.removeEnd(end);
if (((LogicConstantNode) result).getValue()) {
if (trueMerge == null) {
trueMerge = insertMerge(trueSuccessor());
}
trueMerge.addForwardEnd(end);
} else {
if (falseMerge == null) {
falseMerge = insertMerge(falseSuccessor());
}
falseMerge.addForwardEnd(end);
}
} else if (result != condition) {
// Build a new IfNode using the new condition
BeginNode trueBegin = graph().add(new BeginNode());
BeginNode falseBegin = graph().add(new BeginNode());
if (result.graph() == null) {
result = graph().addOrUniqueWithInputs(result);
}
IfNode newIfNode = graph().add(new IfNode(result, trueBegin, falseBegin, trueSuccessorProbability));
merge.removeEnd(end);
((FixedWithNextNode) end.predecessor()).setNext(newIfNode);
if (trueMerge == null) {
trueMerge = insertMerge(trueSuccessor());
}
trueBegin.setNext(graph().add(new EndNode()));
trueMerge.addForwardEnd((EndNode) trueBegin.next());
if (falseMerge == null) {
falseMerge = insertMerge(falseSuccessor());
}
falseBegin.setNext(graph().add(new EndNode()));
falseMerge.addForwardEnd((EndNode) falseBegin.next());
end.safeDelete();
}
}
transferProxies(trueSuccessor(), trueMerge);
transferProxies(falseSuccessor(), falseMerge);
cleanupMerge(merge);
cleanupMerge(trueMerge);
cleanupMerge(falseMerge);
return true;
}
Aggregations