use of org.graalvm.compiler.graph.Node in project graal by oracle.
the class IfNode method removeIntermediateMaterialization.
/**
* Tries to connect code that initializes a variable directly with the successors of an if
* construct that switches on the variable. For example, the pseudo code below:
*
* <pre>
* contains(list, e, yes, no) {
* if (list == null || e == null) {
* condition = false;
* } else {
* condition = false;
* for (i in list) {
* if (i.equals(e)) {
* condition = true;
* break;
* }
* }
* }
* if (condition) {
* return yes;
* } else {
* return no;
* }
* }
* </pre>
*
* will be transformed into:
*
* <pre>
* contains(list, e, yes, no) {
* if (list == null || e == null) {
* return no;
* } else {
* condition = false;
* for (i in list) {
* if (i.equals(e)) {
* return yes;
* }
* }
* return no;
* }
* }
* </pre>
*
* @return true if a transformation was made, false otherwise
*/
private boolean removeIntermediateMaterialization(SimplifierTool tool) {
if (!(predecessor() instanceof AbstractMergeNode) || predecessor() instanceof LoopBeginNode) {
return false;
}
AbstractMergeNode merge = (AbstractMergeNode) predecessor();
if (!(condition() instanceof CompareNode)) {
return false;
}
CompareNode compare = (CompareNode) condition();
if (compare.getUsageCount() != 1) {
return false;
}
// Only consider merges with a single usage that is both a phi and an operand of the
// comparison
NodeIterable<Node> mergeUsages = merge.usages();
if (mergeUsages.count() != 1) {
return false;
}
Node singleUsage = mergeUsages.first();
if (!(singleUsage instanceof ValuePhiNode) || (singleUsage != compare.getX() && singleUsage != compare.getY())) {
return false;
}
// Ensure phi is used by at most the comparison and the merge's frame state (if any)
ValuePhiNode phi = (ValuePhiNode) singleUsage;
NodeIterable<Node> phiUsages = phi.usages();
if (phiUsages.count() > 2) {
return false;
}
for (Node usage : phiUsages) {
if (usage != compare && usage != merge.stateAfter()) {
return false;
}
}
List<EndNode> mergePredecessors = merge.cfgPredecessors().snapshot();
assert phi.valueCount() == merge.forwardEndCount();
Constant[] xs = constantValues(compare.getX(), merge, false);
Constant[] ys = constantValues(compare.getY(), merge, false);
if (xs == null || ys == null) {
return false;
}
// Sanity check that both ends are not followed by a merge without frame state.
if (!checkFrameState(trueSuccessor()) && !checkFrameState(falseSuccessor())) {
return false;
}
List<EndNode> falseEnds = new ArrayList<>(mergePredecessors.size());
List<EndNode> trueEnds = new ArrayList<>(mergePredecessors.size());
EconomicMap<AbstractEndNode, ValueNode> phiValues = EconomicMap.create(Equivalence.IDENTITY, mergePredecessors.size());
AbstractBeginNode oldFalseSuccessor = falseSuccessor();
AbstractBeginNode oldTrueSuccessor = trueSuccessor();
setFalseSuccessor(null);
setTrueSuccessor(null);
Iterator<EndNode> ends = mergePredecessors.iterator();
for (int i = 0; i < xs.length; i++) {
EndNode end = ends.next();
phiValues.put(end, phi.valueAt(end));
if (compare.condition().foldCondition(xs[i], ys[i], tool.getConstantReflection(), compare.unorderedIsTrue())) {
trueEnds.add(end);
} else {
falseEnds.add(end);
}
}
assert !ends.hasNext();
assert falseEnds.size() + trueEnds.size() == xs.length;
connectEnds(falseEnds, phiValues, oldFalseSuccessor, merge, tool);
connectEnds(trueEnds, phiValues, oldTrueSuccessor, merge, tool);
if (this.trueSuccessorProbability == 0.0) {
for (AbstractEndNode endNode : trueEnds) {
propagateZeroProbability(endNode);
}
}
if (this.trueSuccessorProbability == 1.0) {
for (AbstractEndNode endNode : falseEnds) {
propagateZeroProbability(endNode);
}
}
/*
* Remove obsolete ends only after processing all ends, otherwise oldTrueSuccessor or
* oldFalseSuccessor might have been removed if it is a LoopExitNode.
*/
if (falseEnds.isEmpty()) {
GraphUtil.killCFG(oldFalseSuccessor);
}
if (trueEnds.isEmpty()) {
GraphUtil.killCFG(oldTrueSuccessor);
}
GraphUtil.killCFG(merge);
assert !merge.isAlive() : merge;
assert !phi.isAlive() : phi;
assert !compare.isAlive() : compare;
assert !this.isAlive() : this;
return true;
}
use of org.graalvm.compiler.graph.Node in project graal by oracle.
the class IfNode method sameDestination.
/**
* Check it these two blocks end up at the same place. Meeting at the same merge, or
* deoptimizing in the same way.
*/
private static boolean sameDestination(AbstractBeginNode succ1, AbstractBeginNode succ2) {
Node next1 = succ1.next();
Node next2 = succ2.next();
if (next1 instanceof EndNode && next2 instanceof EndNode) {
EndNode end1 = (EndNode) next1;
EndNode end2 = (EndNode) next2;
if (end1.merge() == end2.merge()) {
for (PhiNode phi : end1.merge().phis()) {
if (phi.valueAt(end1) != phi.valueAt(end2)) {
return false;
}
}
// They go to the same MergeNode and merge the same values
return true;
}
} else if (next1 instanceof DeoptimizeNode && next2 instanceof DeoptimizeNode) {
DeoptimizeNode deopt1 = (DeoptimizeNode) next1;
DeoptimizeNode deopt2 = (DeoptimizeNode) next2;
if (deopt1.getReason() == deopt2.getReason() && deopt1.getAction() == deopt2.getAction()) {
// Same deoptimization reason and action.
return true;
}
} else if (next1 instanceof LoopExitNode && next2 instanceof LoopExitNode) {
LoopExitNode exit1 = (LoopExitNode) next1;
LoopExitNode exit2 = (LoopExitNode) next2;
if (exit1.loopBegin() == exit2.loopBegin() && exit1.stateAfter() == exit2.stateAfter() && exit1.stateAfter() == null && sameDestination(exit1, exit2)) {
// Exit the same loop and end up at the same place.
return true;
}
} else if (next1 instanceof ReturnNode && next2 instanceof ReturnNode) {
ReturnNode exit1 = (ReturnNode) next1;
ReturnNode exit2 = (ReturnNode) next2;
if (exit1.result() == exit2.result()) {
// Exit the same loop and end up at the same place.
return true;
}
}
return false;
}
use of org.graalvm.compiler.graph.Node in project graal by oracle.
the class AbstractMergeNode method simplify.
/**
* This simplify method can deal with a null value for tool, so that it can be used outside of
* canonicalization.
*/
@Override
public void simplify(SimplifierTool tool) {
FixedNode currentNext = next();
if (currentNext instanceof AbstractEndNode) {
AbstractEndNode origLoopEnd = (AbstractEndNode) currentNext;
AbstractMergeNode merge = origLoopEnd.merge();
if (merge instanceof LoopBeginNode && !(origLoopEnd instanceof LoopEndNode)) {
return;
}
// anchors are used by phis of the other merge
if (this.anchored().isNotEmpty()) {
return;
}
if (merge.stateAfter() == null && this.stateAfter() != null) {
// We hold a state, but the succeeding merge does not => do not combine.
return;
}
for (PhiNode phi : phis()) {
for (Node usage : phi.usages()) {
if (!(usage instanceof VirtualState) && !merge.isPhiAtMerge(usage)) {
return;
}
}
}
getDebug().log("Split %s into ends for %s.", this, merge);
int numEnds = this.forwardEndCount();
for (int i = 0; i < numEnds - 1; i++) {
AbstractEndNode end = forwardEndAt(numEnds - 1 - i);
if (tool != null) {
tool.addToWorkList(end);
}
AbstractEndNode newEnd;
if (merge instanceof LoopBeginNode) {
newEnd = graph().add(new LoopEndNode((LoopBeginNode) merge));
} else {
EndNode tmpEnd = graph().add(new EndNode());
merge.addForwardEnd(tmpEnd);
newEnd = tmpEnd;
}
for (PhiNode phi : merge.phis()) {
ValueNode v = phi.valueAt(origLoopEnd);
ValueNode newInput;
if (isPhiAtMerge(v)) {
PhiNode endPhi = (PhiNode) v;
newInput = endPhi.valueAt(end);
} else {
newInput = v;
}
phi.addInput(newInput);
}
this.removeEnd(end);
end.replaceAtPredecessor(newEnd);
end.safeDelete();
if (tool != null) {
tool.addToWorkList(newEnd.predecessor());
}
}
graph().reduceTrivialMerge(this);
} else if (currentNext instanceof ReturnNode) {
ReturnNode returnNode = (ReturnNode) currentNext;
if (anchored().isNotEmpty() || returnNode.getMemoryMap() != null) {
return;
}
List<PhiNode> phis = phis().snapshot();
for (PhiNode phi : phis) {
for (Node usage : phi.usages()) {
if (usage != returnNode && !(usage instanceof FrameState)) {
return;
}
}
}
ValuePhiNode returnValuePhi = returnNode.result() == null || !isPhiAtMerge(returnNode.result()) ? null : (ValuePhiNode) returnNode.result();
List<EndNode> endNodes = forwardEnds().snapshot();
for (EndNode end : endNodes) {
ReturnNode newReturn = graph().add(new ReturnNode(returnValuePhi == null ? returnNode.result() : returnValuePhi.valueAt(end)));
if (tool != null) {
tool.addToWorkList(end.predecessor());
}
end.replaceAtPredecessor(newReturn);
}
GraphUtil.killCFG(this);
for (EndNode end : endNodes) {
end.safeDelete();
}
for (PhiNode phi : phis) {
if (tool.allUsagesAvailable() && phi.isAlive() && phi.hasNoUsages()) {
GraphUtil.killWithUnusedFloatingInputs(phi);
}
}
}
}
use of org.graalvm.compiler.graph.Node in project graal by oracle.
the class PartialEscapeBlockState method materializeBefore.
/**
* Materializes the given virtual object and produces the necessary effects in the effects list.
* This transitively also materializes all other virtual objects that are reachable from the
* entries.
*/
public void materializeBefore(FixedNode fixed, VirtualObjectNode virtual, GraphEffectList materializeEffects) {
PartialEscapeClosure.COUNTER_MATERIALIZATIONS.increment(fixed.getDebug());
List<AllocatedObjectNode> objects = new ArrayList<>(2);
List<ValueNode> values = new ArrayList<>(8);
List<List<MonitorIdNode>> locks = new ArrayList<>();
List<ValueNode> otherAllocations = new ArrayList<>(2);
List<Boolean> ensureVirtual = new ArrayList<>(2);
materializeWithCommit(fixed, virtual, objects, locks, values, ensureVirtual, otherAllocations);
materializeEffects.addVirtualizationDelta(-(objects.size() + otherAllocations.size()));
materializeEffects.add("materializeBefore", new Effect() {
@Override
public void apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes) {
for (ValueNode alloc : otherAllocations) {
ValueNode otherAllocation = graph.addOrUniqueWithInputs(alloc);
if (otherAllocation instanceof FixedWithNextNode) {
graph.addBeforeFixed(fixed, (FixedWithNextNode) otherAllocation);
} else {
assert otherAllocation instanceof FloatingNode;
}
}
if (!objects.isEmpty()) {
CommitAllocationNode commit;
if (fixed.predecessor() instanceof CommitAllocationNode) {
commit = (CommitAllocationNode) fixed.predecessor();
} else {
commit = graph.add(new CommitAllocationNode());
graph.addBeforeFixed(fixed, commit);
}
for (AllocatedObjectNode obj : objects) {
graph.addWithoutUnique(obj);
commit.getVirtualObjects().add(obj.getVirtualObject());
obj.setCommit(commit);
}
for (ValueNode value : values) {
commit.getValues().add(graph.addOrUniqueWithInputs(value));
}
for (List<MonitorIdNode> monitorIds : locks) {
commit.addLocks(monitorIds);
}
commit.getEnsureVirtual().addAll(ensureVirtual);
assert commit.usages().filter(AllocatedObjectNode.class).count() == commit.getUsageCount();
List<AllocatedObjectNode> materializedValues = commit.usages().filter(AllocatedObjectNode.class).snapshot();
for (int i = 0; i < commit.getValues().size(); i++) {
if (materializedValues.contains(commit.getValues().get(i))) {
commit.getValues().set(i, ((AllocatedObjectNode) commit.getValues().get(i)).getVirtualObject());
}
}
}
}
});
}
use of org.graalvm.compiler.graph.Node in project graal by oracle.
the class VirtualUtil method assertNonReachable.
public static boolean assertNonReachable(StructuredGraph graph, List<Node> obsoleteNodes) {
// Helper code that determines the paths that keep obsolete nodes alive.
// Nodes with support for GVN can be kept alive by GVN and are therefore not part of the
// assertion.
DebugContext debug = graph.getDebug();
NodeFlood flood = graph.createNodeFlood();
EconomicMap<Node, Node> path = EconomicMap.create(Equivalence.IDENTITY);
flood.add(graph.start());
for (Node current : flood) {
if (current instanceof AbstractEndNode) {
AbstractEndNode end = (AbstractEndNode) current;
flood.add(end.merge());
if (!path.containsKey(end.merge())) {
path.put(end.merge(), end);
}
} else {
for (Node successor : current.successors()) {
flood.add(successor);
if (!path.containsKey(successor)) {
path.put(successor, current);
}
}
}
}
for (Node node : obsoleteNodes) {
if (node instanceof FixedNode && !node.isDeleted()) {
assert !flood.isMarked(node) : node;
}
}
for (Node node : graph.getNodes()) {
if (flood.isMarked(node)) {
for (Node input : node.inputs()) {
flood.add(input);
if (!path.containsKey(input)) {
path.put(input, node);
}
}
}
}
for (Node current : flood) {
for (Node input : current.inputs()) {
flood.add(input);
if (!path.containsKey(input)) {
path.put(input, current);
}
}
}
boolean success = true;
for (Node node : obsoleteNodes) {
if (!node.isDeleted() && flood.isMarked(node) && !node.getNodeClass().valueNumberable()) {
TTY.println("offending node path:");
Node current = node;
TTY.print(current.toString());
while (true) {
current = path.get(current);
if (current != null) {
TTY.print(" -> " + current.toString());
if (current instanceof FixedNode && !obsoleteNodes.contains(current)) {
break;
}
}
}
success = false;
}
}
if (!success) {
TTY.println();
debug.forceDump(graph, "assertNonReachable");
}
return success;
}
Aggregations