use of org.graalvm.compiler.nodes.memory.MemoryKill in project graal by oracle.
the class FloatingReadPhase method mergeMemoryMaps.
public static MemoryMapImpl mergeMemoryMaps(AbstractMergeNode merge, List<? extends MemoryMap> states) {
MemoryMapImpl newState = new MemoryMapImpl();
EconomicSet<LocationIdentity> keys = EconomicSet.create(Equivalence.DEFAULT);
for (MemoryMap other : states) {
keys.addAll(other.getLocations());
}
assert checkNoImmutableLocations(keys);
for (LocationIdentity key : keys) {
int mergedStatesCount = 0;
boolean isPhi = false;
MemoryKill merged = null;
for (MemoryMap state : states) {
MemoryKill last = state.getLastLocationAccess(key);
if (isPhi) {
// Fortify: Suppress Null Deference false positive (`isPhi == true` implies
// `merged != null`)
((MemoryPhiNode) merged).addInput(ValueNodeUtil.asNode(last));
} else {
if (merged == last) {
// nothing to do
} else if (merged == null) {
merged = last;
} else {
MemoryPhiNode phi = merge.graph().addWithoutUnique(new MemoryPhiNode(merge, key));
for (int j = 0; j < mergedStatesCount; j++) {
phi.addInput(ValueNodeUtil.asNode(merged));
}
phi.addInput(ValueNodeUtil.asNode(last));
merged = phi;
isPhi = true;
}
}
mergedStatesCount++;
}
newState.getMap().put(key, merged);
}
return newState;
}
use of org.graalvm.compiler.nodes.memory.MemoryKill in project graal by oracle.
the class ScheduleVerification method processBlock.
@Override
protected EconomicSet<FloatingReadNode> processBlock(Block block, EconomicSet<FloatingReadNode> currentState) {
AbstractBeginNode beginNode = block.getBeginNode();
if (beginNode instanceof AbstractMergeNode) {
AbstractMergeNode abstractMergeNode = (AbstractMergeNode) beginNode;
for (PhiNode phi : abstractMergeNode.phis()) {
if (phi instanceof MemoryPhiNode) {
MemoryPhiNode memoryPhiNode = (MemoryPhiNode) phi;
addFloatingReadUsages(currentState, memoryPhiNode);
}
}
}
if (beginNode instanceof LoopExitNode) {
LoopExitNode loopExitNode = (LoopExitNode) beginNode;
for (ProxyNode proxy : loopExitNode.proxies()) {
if (proxy instanceof MemoryProxyNode) {
MemoryProxyNode memoryProxyNode = (MemoryProxyNode) proxy;
addFloatingReadUsages(currentState, memoryProxyNode);
}
}
}
for (Node n : blockToNodesMap.get(block)) {
if (n instanceof MemoryKill) {
if (n instanceof SingleMemoryKill) {
SingleMemoryKill single = (SingleMemoryKill) n;
processLocation(n, single.getKilledLocationIdentity(), currentState);
} else if (n instanceof MultiMemoryKill) {
MultiMemoryKill multi = (MultiMemoryKill) n;
for (LocationIdentity location : multi.getKilledLocationIdentities()) {
processLocation(n, location, currentState);
}
}
addFloatingReadUsages(currentState, n);
} else if (n instanceof MemoryAccess) {
addFloatingReadUsages(currentState, n);
} else if (n instanceof FloatingReadNode) {
FloatingReadNode floatingReadNode = (FloatingReadNode) n;
if (floatingReadNode.getLastLocationAccess() != null && floatingReadNode.getLocationIdentity().isMutable()) {
if (currentState.contains(floatingReadNode)) {
// Floating read was found in the state.
currentState.remove(floatingReadNode);
} else {
throw new RuntimeException("Floating read node " + n + " was not found in the state, i.e., it was killed by a memory check point before its place in the schedule. Block=" + block + ", block begin: " + block.getBeginNode() + " block loop: " + block.getLoop() + ", " + blockToNodesMap.get(block).get(0));
}
}
}
assert nodeMap.get(n) == block;
if (graph.isBeforeStage(StageFlag.VALUE_PROXY_REMOVAL) && block.getLoop() != null && !(n instanceof VirtualState)) {
for (Node usage : n.usages()) {
Node usageNode = usage;
if (usageNode instanceof PhiNode) {
PhiNode phiNode = (PhiNode) usage;
usageNode = phiNode.merge();
}
if (usageNode instanceof LoopExitNode) {
LoopExitNode loopExitNode = (LoopExitNode) usageNode;
if (loopExitNode.loopBegin() == n || loopExitNode.stateAfter() == n) {
continue;
}
}
Block usageBlock = nodeMap.get(usageNode);
if (usageBlock == null) {
if (usage instanceof FloatingNode || usage instanceof VirtualState || usage instanceof CallTargetNode) {
if (!(usage instanceof GuardNode)) {
/*
* We do not want to run the schedule behind the verification with
* dead code elimination, i.e., floating nodes without usages are
* not removed, thus we must handle the case that a floating node
* without a usage occurs here.
*/
if (nonFixedNodeTreeWithoutUsages(usage)) {
continue;
}
}
}
}
assert usageBlock != null || usage instanceof ProxyNode : "Usage " + usageNode + " of node " + n + " has no block";
Loop<Block> usageLoop = null;
if (usageNode instanceof ProxyNode) {
ProxyNode proxyNode = (ProxyNode) usageNode;
usageLoop = nodeMap.get(proxyNode.proxyPoint().loopBegin()).getLoop();
} else {
if (usageBlock.getBeginNode() instanceof LoopExitNode) {
// For nodes in the loop exit node block, we don't know for sure
// whether they are "in the loop" or not. It depends on whether
// one of their transient usages is a loop proxy node.
// For now, let's just assume those nodes are OK, i.e., "in the loop".
LoopExitNode loopExitNode = (LoopExitNode) usageBlock.getBeginNode();
usageLoop = nodeMap.get(loopExitNode.loopBegin()).getLoop();
} else {
usageLoop = usageBlock.getLoop();
}
}
assert usageLoop != null : n + ", " + nodeMap.get(n) + " / " + usageNode + ", " + nodeMap.get(usageNode);
while (usageLoop != block.getLoop() && usageLoop != null) {
usageLoop = usageLoop.getParent();
}
assert usageLoop != null : n + ", " + usageNode + ", " + usageBlock + ", " + usageBlock.getLoop() + ", " + block + ", " + block.getLoop();
}
}
}
return currentState;
}
use of org.graalvm.compiler.nodes.memory.MemoryKill in project graal by oracle.
the class PlaceholderLogicNode method verifyWithExceptionNode.
/**
* Verifies that a {@link WithExceptionNode} has only memory usages via the
* {@link WithExceptionNode#next()} edge. On the {@link WithExceptionNode#exceptionEdge()} there
* must be a {@link MemoryKill} (or an {@link UnreachableBeginNode}), otherwise we would not
* know from which edge a memory usage is coming from.
*/
private static void verifyWithExceptionNode(ValueNode node) {
if (node instanceof WithExceptionNode && node instanceof MemoryKill) {
WithExceptionNode withExceptionNode = (WithExceptionNode) node;
AbstractBeginNode exceptionEdge = withExceptionNode.exceptionEdge();
if (exceptionEdge instanceof UnreachableBeginNode) {
// exception edge is unreachable - we are good
return;
}
GraalError.guarantee(exceptionEdge instanceof MemoryKill, "The exception edge of %s is not a memory kill %s", node, exceptionEdge);
if (exceptionEdge instanceof SingleMemoryKill) {
SingleMemoryKill exceptionEdgeKill = (SingleMemoryKill) exceptionEdge;
if (exceptionEdgeKill.getKilledLocationIdentity().isAny()) {
// exception edge kills any - we are good
return;
}
// if the exception edge does not kill any, it must kill the same location
GraalError.guarantee(withExceptionNode instanceof SingleMemoryKill, "Not a single memory kill: %s", withExceptionNode);
SingleMemoryKill withExceptionKill = (SingleMemoryKill) withExceptionNode;
GraalError.guarantee(withExceptionKill.getKilledLocationIdentity().equals(exceptionEdgeKill.getKilledLocationIdentity()), "Kill locations do not match: %s (%s) vs %s (%s)", withExceptionKill, withExceptionKill.getKilledLocationIdentity(), exceptionEdgeKill, exceptionEdgeKill.getKilledLocationIdentity());
} else if (exceptionEdge instanceof MultiMemoryKill) {
// for multi memory kills the locations must match
MultiMemoryKill exceptionEdgeKill = (MultiMemoryKill) exceptionEdge;
GraalError.guarantee(withExceptionNode instanceof MultiMemoryKill, "Not a single memory kill: %s", withExceptionNode);
MultiMemoryKill withExceptionKill = (MultiMemoryKill) withExceptionNode;
GraalError.guarantee(Arrays.equals(withExceptionKill.getKilledLocationIdentities(), exceptionEdgeKill.getKilledLocationIdentities()), "Kill locations do not match: %s (%s) vs %s (%s)", withExceptionKill, withExceptionKill.getKilledLocationIdentities(), exceptionEdgeKill, exceptionEdgeKill.getKilledLocationIdentities());
} else {
GraalError.shouldNotReachHere("Unexpected exception edge: " + exceptionEdge);
}
}
}
use of org.graalvm.compiler.nodes.memory.MemoryKill in project graal by oracle.
the class GraphEffectList method deleteNode.
/**
* Removes the given fixed node from the control flow and deletes it.
*
* @param node The fixed node that should be deleted.
*/
public void deleteNode(Node node) {
add("delete fixed node", new Effect() {
@Override
public void apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes) {
if (node instanceof FixedWithNextNode) {
GraphUtil.unlinkFixedNode((FixedWithNextNode) node);
} else if (node instanceof WithExceptionNode && node.isAlive()) {
WithExceptionNode withExceptionNode = (WithExceptionNode) node;
AbstractBeginNode next = withExceptionNode.next();
GraphUtil.unlinkAndKillExceptionEdge(withExceptionNode);
if (next.hasNoUsages() && next instanceof MemoryKill) {
// This is a killing begin which is no longer needed.
graph.replaceFixedWithFixed(next, graph.add(new BeginNode()));
}
obsoleteNodes.add(withExceptionNode);
}
obsoleteNodes.add(node);
}
@Override
public boolean isCfgKill() {
return node instanceof WithExceptionNode;
}
});
}
use of org.graalvm.compiler.nodes.memory.MemoryKill in project graal by oracle.
the class LoweringPhase method checkPostNodeLowering.
/**
* Checks that lowering of a given node did not introduce any new {@link Lowerable} nodes that
* could be lowered in the current {@link LoweringPhase}. Such nodes must be recursively lowered
* as part of lowering {@code node}.
*
* @param node a node that was just lowered
* @param preLoweringMark the graph mark before {@code node} was lowered
* @param unscheduledUsages set of {@code node}'s usages that were unscheduled before it was
* lowered
* @throws AssertionError if the check fails
*/
private static boolean checkPostNodeLowering(Node node, LoweringToolImpl loweringTool, Mark preLoweringMark, Collection<Node> unscheduledUsages) {
StructuredGraph graph = (StructuredGraph) node.graph();
Mark postLoweringMark = graph.getMark();
NodeIterable<Node> newNodesAfterLowering = graph.getNewNodes(preLoweringMark);
if (node instanceof FloatingNode) {
if (!unscheduledUsages.isEmpty()) {
for (Node n : newNodesAfterLowering) {
assert !(n instanceof FixedNode) : node.graph() + ": cannot lower floatable node " + node + " as it introduces fixed node(s) but has the following unscheduled usages: " + unscheduledUsages;
}
}
}
for (Node n : newNodesAfterLowering) {
if (n instanceof Lowerable) {
((Lowerable) n).lower(loweringTool);
Mark mark = graph.getMark();
assert postLoweringMark.equals(mark) : graph + ": lowering of " + node + " produced lowerable " + n + " that should have been recursively lowered as it introduces these new nodes: " + graph.getNewNodes(postLoweringMark).snapshot();
}
if (graph.isAfterStage(StageFlag.FLOATING_READS) && n instanceof MemoryKill && !(node instanceof MemoryKill) && !(node instanceof ControlSinkNode)) {
/*
* The lowering introduced a MemoryCheckpoint but the current node isn't a
* checkpoint. This is only OK if the locations involved don't affect the memory
* graph or if the new kill location doesn't connect into the existing graph.
*/
boolean isAny = false;
if (n instanceof SingleMemoryKill) {
isAny = ((SingleMemoryKill) n).getKilledLocationIdentity().isAny();
} else if (n instanceof MultiMemoryKill) {
for (LocationIdentity ident : ((MultiMemoryKill) n).getKilledLocationIdentities()) {
if (ident.isAny()) {
isAny = true;
}
}
} else {
throw GraalError.shouldNotReachHere("Unknown type of memory kill " + n);
}
if (isAny && n instanceof FixedWithNextNode) {
/*
* Check if the next kill location leads directly to a ControlSinkNode in the
* new part of the graph. This is a fairly conservative test that could be made
* more general if required.
*/
FixedWithNextNode cur = (FixedWithNextNode) n;
while (cur != null && graph.isNew(preLoweringMark, cur)) {
if (cur.next() instanceof ControlSinkNode) {
isAny = false;
break;
}
if (cur.next() instanceof FixedWithNextNode) {
cur = (FixedWithNextNode) cur.next();
} else {
break;
}
}
}
assert !isAny : node + " " + n;
}
}
return true;
}
Aggregations