Skip to content

Commit ce41046

Browse files
committed
[GR-32668] Lock elimination: respect scheduling requirements.
PullRequest: graal/9444
2 parents 35fe46e + ede47d4 commit ce41046

File tree

2 files changed

+117
-8
lines changed

2 files changed

+117
-8
lines changed

compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/cfg/AbstractControlFlowGraph.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ static boolean strictlyDominates(AbstractBlockBase<?> a, AbstractBlockBase<?> b)
6666
* True if block {@code a} dominates block {@code b}.
6767
*/
6868
static boolean dominates(AbstractBlockBase<?> a, AbstractBlockBase<?> b) {
69-
assert a != null && b != null;
69+
assert a != null;
70+
assert b != null;
7071
return isDominatedBy(b, a);
7172
}
7273

compiler/src/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/LockEliminationPhase.java

Lines changed: 115 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,29 +24,40 @@
2424
*/
2525
package org.graalvm.compiler.phases.common;
2626

27+
import org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph;
28+
import org.graalvm.compiler.graph.Node;
2729
import org.graalvm.compiler.nodes.FixedNode;
30+
import org.graalvm.compiler.nodes.GuardNode;
31+
import org.graalvm.compiler.nodes.PiNode;
32+
import org.graalvm.compiler.nodes.ProxyNode;
2833
import org.graalvm.compiler.nodes.StructuredGraph;
2934
import org.graalvm.compiler.nodes.ValueNode;
35+
import org.graalvm.compiler.nodes.cfg.Block;
36+
import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
37+
import org.graalvm.compiler.nodes.extended.AnchoringNode;
38+
import org.graalvm.compiler.nodes.extended.GuardingNode;
3039
import org.graalvm.compiler.nodes.extended.OSRMonitorEnterNode;
3140
import org.graalvm.compiler.nodes.java.AccessMonitorNode;
3241
import org.graalvm.compiler.nodes.java.MonitorEnterNode;
3342
import org.graalvm.compiler.nodes.java.MonitorExitNode;
3443
import org.graalvm.compiler.nodes.java.MonitorIdNode;
44+
import org.graalvm.compiler.nodes.spi.ValueProxy;
3545
import org.graalvm.compiler.nodes.util.GraphUtil;
3646
import org.graalvm.compiler.phases.Phase;
3747

3848
public class LockEliminationPhase extends Phase {
3949

4050
@Override
4151
protected void run(StructuredGraph graph) {
52+
ControlFlowGraph cfg = ControlFlowGraph.compute(graph, true, true, true, false);
4253
for (MonitorExitNode monitorExitNode : graph.getNodes(MonitorExitNode.TYPE)) {
4354
FixedNode next = monitorExitNode.next();
4455
if ((next instanceof MonitorEnterNode)) {
4556
// should never happen, osr monitor enters are always direct successors of the graph
4657
// start
4758
assert !(next instanceof OSRMonitorEnterNode);
4859
AccessMonitorNode monitorEnterNode = (AccessMonitorNode) next;
49-
if (isCompatibleLock(monitorEnterNode, monitorExitNode)) {
60+
if (isCompatibleLock(monitorEnterNode, monitorExitNode, true, cfg)) {
5061
/*
5162
* We've coarsened the lock so use the same monitor id for the whole region,
5263
* otherwise the monitor operations appear to be unrelated.
@@ -64,17 +75,114 @@ protected void run(StructuredGraph graph) {
6475
}
6576

6677
/**
67-
* Check that the paired operations operate on the same object at the same lock depth.
78+
* Check that the paired monitor operations operate on the same object at the same lock depth.
79+
* Additionally, ensure that any {@link PiNode} in between respect a dominance relation between
80+
* a and b. This is necessary to ensure any monitor rewiring respects a proper schedule.
81+
*
82+
* @param a The first {@link AccessMonitorNode}
83+
* @param b The first {@link AccessMonitorNode}
84+
* @param aDominatesB if {@code true} determine if a must dominate b (including any guarded
85+
* {@link PiNode} in between to determine if a and b are compatible, else if
86+
* {@code false} determine if b must dominate a
87+
*
6888
*/
69-
public static boolean isCompatibleLock(AccessMonitorNode lock1, AccessMonitorNode lock2) {
89+
public static boolean isCompatibleLock(AccessMonitorNode a, AccessMonitorNode b, boolean aDominatesB, ControlFlowGraph cfg) {
7090
/*
7191
* It is not always the case that sequential monitor operations on the same object have the
7292
* same lock depth: Escape analysis can have removed a lock operation that was in between,
7393
* leading to a mismatch in lock depth.
7494
*/
75-
ValueNode object1 = GraphUtil.unproxify(lock1.object());
76-
ValueNode object2 = GraphUtil.unproxify(lock2.object());
77-
return object1 == object2 && lock1.getMonitorId().getLockDepth() == lock2.getMonitorId().getLockDepth() &&
78-
lock1.getMonitorId().isMultipleEntry() == lock2.getMonitorId().isMultipleEntry();
95+
ValueNode objectA = GraphUtil.unproxify(a.object());
96+
ValueNode objectB = GraphUtil.unproxify(b.object());
97+
if (objectA == objectB && a.getMonitorId().getLockDepth() == b.getMonitorId().getLockDepth() &&
98+
a.getMonitorId().isMultipleEntry() == b.getMonitorId().isMultipleEntry()) {
99+
/*
100+
* If the monitor operations operate on the same unproxified object, ensure any pi nodes
101+
* in the proxy chain are safe to re-order when moving monitor operations.
102+
*/
103+
Block lowestBlockA = lowestGuardedInputBlock(b, cfg);
104+
Block lowestBlockB = null;
105+
/*
106+
* If the object nodes are the same and there is no object or data guard for one of the
107+
* monitor operations it can only mean that one of them did not have to skip any pi
108+
* nodes while the other did. We are safe then because the object node is the same (by
109+
* identity) and it has to dominate both monitor operations.
110+
*/
111+
if (lowestBlockA != null) {
112+
lowestBlockB = lowestGuardedInputBlock(b, cfg);
113+
}
114+
if (lowestBlockA == null || lowestBlockB == null) {
115+
return true;
116+
}
117+
if (aDominatesB) {
118+
return AbstractControlFlowGraph.dominates(lowestBlockA, lowestBlockB);
119+
} else {
120+
return AbstractControlFlowGraph.dominates(lowestBlockB, lowestBlockA);
121+
}
122+
}
123+
return false;
124+
}
125+
126+
/**
127+
* Get the lowest (by dominance relation) {@link Block} for the (potentially hidden behind
128+
* {@link ProxyNode}s) inputs of the {@link AccessMonitorNode}.
129+
*/
130+
public static Block lowestGuardedInputBlock(AccessMonitorNode monitorNode, ControlFlowGraph cfg) {
131+
return lowestGuardedInputBlock(unproxifyHighestGuard(monitorNode.object()), unproxifyHighestGuard(monitorNode.getObjectData()), cfg);
132+
}
133+
134+
public static Block lowestGuardedInputBlock(GuardingNode g1, GuardingNode g2, ControlFlowGraph cfg) {
135+
Block b1 = getGuardingBlock(g1, cfg);
136+
Block b2 = getGuardingBlock(g2, cfg);
137+
if (b1 == null) {
138+
return b2;
139+
}
140+
if (b2 == null) {
141+
return b1;
142+
}
143+
if (AbstractControlFlowGraph.dominates(b1, b2)) {
144+
return b2;
145+
}
146+
return b1;
147+
}
148+
149+
/**
150+
* Get the basic block of the {@link GuardingNode}. Handles fixed and floating guarded nodes.
151+
*/
152+
public static Block getGuardingBlock(GuardingNode g1, ControlFlowGraph cfg) {
153+
Block b1 = null;
154+
if (g1 != null) {
155+
if (g1 instanceof FixedNode) {
156+
b1 = cfg.blockFor((Node) g1);
157+
} else if (g1 instanceof GuardNode) {
158+
AnchoringNode a = ((GuardNode) g1).getAnchor();
159+
if (a instanceof FixedNode) {
160+
b1 = cfg.blockFor((FixedNode) a);
161+
}
162+
}
163+
}
164+
return b1;
165+
}
166+
167+
/**
168+
* Get the highest (by input traversal) {@link GuardingNode} attached to any {@link ProxyNode}
169+
* visited in {@link GraphUtil#unproxify(ValueNode)}.
170+
*/
171+
public static GuardingNode unproxifyHighestGuard(ValueNode value) {
172+
if (value != null) {
173+
ValueNode result = value;
174+
GuardingNode highestGuard = null;
175+
while (result instanceof ValueProxy) {
176+
GuardingNode curGuard = ((ValueProxy) result).getGuard();
177+
if (curGuard != null) {
178+
highestGuard = curGuard;
179+
}
180+
result = ((ValueProxy) result).getOriginalNode();
181+
}
182+
return highestGuard;
183+
} else {
184+
return null;
185+
}
79186
}
187+
80188
}

0 commit comments

Comments
 (0)