Skip to content

Commit 0d44bf3

Browse files
committed
[GR-40383] Infer non-null stamp for mutually recursive non-null phis
PullRequest: graal/12466
2 parents 93e2f48 + 650ed3a commit 0d44bf3

File tree

4 files changed

+144
-1
lines changed

4 files changed

+144
-1
lines changed

compiler/src/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeFlood.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,31 @@
2828
import java.util.Iterator;
2929
import java.util.Queue;
3030

31+
import org.graalvm.compiler.graph.NodeWorkList.SingletonNodeWorkList;
32+
33+
/**
34+
* A data structure for visiting nodes in a graph iteratively. Each node added to the flood will be
35+
* visited exactly once. New nodes to visit can be added during the iteration.
36+
* </p>
37+
*
38+
* A typical use of this class looks like:
39+
*
40+
* <pre>
41+
* NodeFlood flood = graph.createNodeFlood();
42+
* flood.add(initialNodeOfInterest);
43+
* for (Node n : flood) {
44+
* processNode(n);
45+
* if (inputsAreOfInterest(n)) {
46+
* flood.addAll(n.inputs());
47+
* }
48+
* }
49+
* </pre>
50+
*
51+
* This class is equivalent to {@link SingletonNodeWorkList}, except that
52+
* {@link SingletonNodeWorkList} ignores deleted nodes both when adding and enumerating nodes. In
53+
* contrast, it is an error to try to add deleted nodes to a {@link NodeFlood}, but
54+
* {@link NodeFlood} will enumerate nodes that are deleted while they are enqueued for enumeration.
55+
*/
3156
public final class NodeFlood implements Iterable<Node> {
3257

3358
private final NodeBitMap visited;

compiler/src/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeWorkList.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,36 @@ public void remove() {
7373
}
7474
}
7575

76+
/**
77+
* A data structure for visiting nodes in a graph iteratively. Each node added to the worklist
78+
* can be visited multiple times. New nodes to visit can be added during the iteration.
79+
* </p>
80+
*
81+
* Nodes are only re-added to the worklist if they are not already enqueued for a future visit.
82+
* Therefore calling {@link #add} with a node ensures that that node will be visited in the
83+
* future, but the total number of visits may be less than the number of {@link #add} calls.
84+
* </p>
85+
*
86+
* Iteration stops after enumeration of {@code iterationLimitPerNode * graph.getNodeCount()}
87+
* nodes. The limit is thus an average limit over all nodes, and individual nodes may be visited
88+
* more than {@code iterationLimitPerNode} times.
89+
* </p>
90+
*
91+
* A typical use of this class looks like:
92+
*
93+
* <pre>
94+
* NodeWorkList worklist = graph.createIterativeNodeWorkList(fill, iterationLimitPerNode);
95+
* worklist.add(initialNodeOfInterest);
96+
* for (Node n : worklist) {
97+
* processNode(n);
98+
* if (inputsAreOfInterest(n)) {
99+
* worklist.addAll(n.inputs());
100+
* }
101+
* }
102+
* </pre>
103+
*
104+
* Deleted nodes are ignored both when adding and when enumerating nodes.
105+
*/
76106
public static final class IterativeNodeWorkList extends NodeWorkList {
77107
private static final int EXPLICIT_BITMAP_THRESHOLD = 10;
78108
protected NodeBitMap inQueue;
@@ -195,6 +225,29 @@ private void inflateToBitMap(Graph graph) {
195225
}
196226
}
197227

228+
/**
229+
* A data structure for visiting nodes in a graph iteratively. Each node added to the worklist
230+
* will be visited exactly once. New nodes to visit can be added during the iteration.
231+
* </p>
232+
*
233+
* A typical use of this class looks like:
234+
*
235+
* <pre>
236+
* NodeWorkList worklist = graph.createNodeWorkList();
237+
* worklist.add(initialNodeOfInterest);
238+
* for (Node n : worklist) {
239+
* processNode(n);
240+
* if (inputsAreOfInterest(n)) {
241+
* worklist.addAll(n.inputs());
242+
* }
243+
* }
244+
* </pre>
245+
*
246+
* This class is equivalent to {@link NodeFlood}, except that {@link SingletonNodeWorkList}
247+
* ignores deleted nodes both when adding and enumerating nodes. In contrast, it is an error to
248+
* try to add deleted nodes to a {@link NodeFlood}, but {@link NodeFlood} will enumerate nodes
249+
* that are deleted while they are enqueued for enumeration.
250+
*/
198251
public static final class SingletonNodeWorkList extends NodeWorkList {
199252
protected final NodeBitMap visited;
200253

compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValuePhiNode.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,13 @@
2626

2727
import java.util.Map;
2828

29+
import org.graalvm.compiler.core.common.type.AbstractPointerStamp;
2930
import org.graalvm.compiler.core.common.type.IntegerStamp;
3031
import org.graalvm.compiler.core.common.type.Stamp;
3132
import org.graalvm.compiler.core.common.type.StampFactory;
33+
import org.graalvm.compiler.graph.Node;
3234
import org.graalvm.compiler.graph.NodeClass;
35+
import org.graalvm.compiler.graph.NodeFlood;
3336
import org.graalvm.compiler.graph.NodeInputList;
3437
import org.graalvm.compiler.nodeinfo.NodeInfo;
3538
import org.graalvm.compiler.nodes.calc.AddNode;
@@ -80,9 +83,55 @@ public boolean inferStamp() {
8083
} else if (stamp.isCompatible(valuesStamp)) {
8184
valuesStamp = stamp.join(valuesStamp);
8285
}
86+
87+
Stamp maybeNonNullStamp = tryInferNonNullStamp(valuesStamp);
88+
if (maybeNonNullStamp != valuesStamp) {
89+
valuesStamp = maybeNonNullStamp;
90+
}
91+
8392
return updateStamp(valuesStamp);
8493
}
8594

95+
/**
96+
* See if this phi has a pointer stamp and is part of a set of mutually recursive phis that only
97+
* have each other or non-null values as inputs. In this case we can infer a non-null stamp.
98+
*
99+
* @return a non-null version of the original {@code valuesStamp} if it could be improved, the
100+
* unchanged {@code valuesStamp} otherwise
101+
*/
102+
private Stamp tryInferNonNullStamp(Stamp valuesStamp) {
103+
if (isAlive() && isLoopPhi() && valuesStamp instanceof AbstractPointerStamp) {
104+
AbstractPointerStamp pointerStamp = (AbstractPointerStamp) valuesStamp;
105+
if (!pointerStamp.alwaysNull() && !pointerStamp.nonNull() && StampTool.isPointerNonNull(firstValue())) {
106+
// Fail early if this phi already has possibly null non-phi inputs.
107+
for (ValueNode value : values()) {
108+
if (value == this || value instanceof ValuePhiNode) {
109+
continue;
110+
} else if (!StampTool.isPointerNonNull(value)) {
111+
return valuesStamp;
112+
}
113+
}
114+
// Check input phis recursively.
115+
NodeFlood flood = new NodeFlood(graph());
116+
flood.addAll(values().filter(ValuePhiNode.class));
117+
for (Node node : flood) {
118+
if (node instanceof ValuePhiNode) {
119+
for (ValueNode value : ((ValuePhiNode) node).values()) {
120+
if (value == this || value instanceof ValuePhiNode) {
121+
flood.add(value);
122+
} else if (!StampTool.isPointerNonNull(value)) {
123+
return valuesStamp;
124+
}
125+
}
126+
}
127+
}
128+
// All transitive inputs are non-null.
129+
return ((AbstractPointerStamp) valuesStamp).asNonNull();
130+
}
131+
}
132+
return valuesStamp;
133+
}
134+
86135
@Override
87136
public boolean verify() {
88137
Stamp s = null;

compiler/src/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MonitorTest.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
package org.graalvm.compiler.replacements.test;
2626

2727
import org.junit.Test;
28-
28+
import org.graalvm.compiler.api.directives.GraalDirectives;
2929
import org.graalvm.compiler.core.test.GraalCompilerTest;
3030
import org.graalvm.compiler.nodes.ValueNode;
3131
import org.graalvm.compiler.replacements.BoxingSnippets;
@@ -232,4 +232,20 @@ public void test8() {
232232
test("lockBoxedLong", Long.MAX_VALUE - 1);
233233
test("lockBoxedLong", Long.MIN_VALUE + 1);
234234
}
235+
236+
static void loopPhi(boolean f) {
237+
Object j = new Object();
238+
int n = f ? 10 : 11;
239+
for (int i = 0; GraalDirectives.injectIterationCount(11.5, i < n); i++) {
240+
synchronized (j) {
241+
j = f ? new Object() : j;
242+
}
243+
}
244+
}
245+
246+
@Test
247+
public void testLoopPhi() {
248+
test("loopPhi", true);
249+
test("loopPhi", false);
250+
}
235251
}

0 commit comments

Comments
 (0)