2424 */
2525package org .graalvm .compiler .phases .common ;
2626
27+ import org .graalvm .compiler .core .common .cfg .AbstractControlFlowGraph ;
28+ import org .graalvm .compiler .graph .Node ;
2729import 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 ;
2833import org .graalvm .compiler .nodes .StructuredGraph ;
2934import 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 ;
3039import org .graalvm .compiler .nodes .extended .OSRMonitorEnterNode ;
3140import org .graalvm .compiler .nodes .java .AccessMonitorNode ;
3241import org .graalvm .compiler .nodes .java .MonitorEnterNode ;
3342import org .graalvm .compiler .nodes .java .MonitorExitNode ;
3443import org .graalvm .compiler .nodes .java .MonitorIdNode ;
44+ import org .graalvm .compiler .nodes .spi .ValueProxy ;
3545import org .graalvm .compiler .nodes .util .GraphUtil ;
3646import org .graalvm .compiler .phases .Phase ;
3747
3848public 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