Skip to content

Commit 51994b1

Browse files
committed
Materialize virtual objects that have lower lock depth
1 parent f8fbbb6 commit 51994b1

File tree

3 files changed

+62
-40
lines changed

3 files changed

+62
-40
lines changed

compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/hotspot/test/MonitorPEATest.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,4 +192,46 @@ protected void checkMidTierGraph(StructuredGraph graph) {
192192
}
193193
}
194194
}
195+
196+
static class B {
197+
Float f = 1.0f;
198+
}
199+
200+
public static void snippet5() {
201+
synchronized (new B()) {
202+
synchronized (new StringBuilder()) {
203+
synchronized (B.class) {
204+
}
205+
}
206+
}
207+
}
208+
209+
@Test
210+
public void testSnippet5() {
211+
test("snippet5");
212+
}
213+
214+
static class C {
215+
B b;
216+
217+
C(B b) {
218+
this.b = b;
219+
}
220+
}
221+
222+
public static void snippet6() {
223+
B b = new B();
224+
C c = new C(b);
225+
synchronized (c) {
226+
synchronized (b) {
227+
synchronized (B.class) {
228+
}
229+
}
230+
}
231+
}
232+
233+
@Test
234+
public void testSnippet6() {
235+
test("snippet6");
236+
}
195237
}

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/DefaultJavaLoweringProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1140,7 +1140,7 @@ public void finishAllocatedObjects(LoweringTool tool, FixedWithNextNode insertAf
11401140
* these objects. This means PEA must treat MonitorEnterNodes as having a side effect even
11411141
* after being virtualized to ensure that the lock is released after being acquired..
11421142
* Additionally we must ensure that the MonitorEnterNodes can't deoptimize as it will use
1143-
* the FrqmeState where the locks are still virtual and the lock acquired by the
1143+
* the FrameState where the locks are still virtual and the lock acquired by the
11441144
* MonitorEnterNode won't be released.
11451145
*/
11461146
ArrayList<MonitorEnterNode> enters = null;

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/virtual/phases/ea/PartialEscapeClosure.java

Lines changed: 19 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import org.graalvm.collections.EconomicMap;
3434
import org.graalvm.collections.EconomicSet;
3535
import org.graalvm.collections.Equivalence;
36-
import org.graalvm.collections.MapCursor;
3736

3837
import jdk.graal.compiler.core.common.GraalOptions;
3938
import jdk.graal.compiler.core.common.RetryableBailoutException;
@@ -72,7 +71,6 @@
7271
import jdk.graal.compiler.nodes.WithExceptionNode;
7372
import jdk.graal.compiler.nodes.cfg.HIRBlock;
7473
import jdk.graal.compiler.nodes.java.AbstractNewObjectNode;
75-
import jdk.graal.compiler.nodes.java.AccessMonitorNode;
7674
import jdk.graal.compiler.nodes.java.MonitorEnterNode;
7775
import jdk.graal.compiler.nodes.spi.Canonicalizable;
7876
import jdk.graal.compiler.nodes.spi.CoreProviders;
@@ -531,14 +529,7 @@ private void collectLockedVirtualObjects(PartialEscapeBlockState<?> state, Econo
531529
}
532530
}
533531

534-
/**
535-
* @return true if materialization happened, false if not.
536-
*/
537532
protected boolean ensureMaterialized(PartialEscapeBlockState<?> state, int object, FixedNode materializeBefore, GraphEffectList effects, CounterKey counter) {
538-
return ensureMaterialized(state, object, materializeBefore, effects, counter, true);
539-
}
540-
541-
private boolean ensureMaterialized(PartialEscapeBlockState<?> state, int object, FixedNode materializeBefore, GraphEffectList effects, CounterKey counter, boolean materializedAcquiredLocks) {
542533
ObjectState objectState = state.getObjectState(object);
543534
if (objectState.isVirtual()) {
544535
if (currentMode == EffectsClosureMode.STOP_NEW_VIRTUALIZATIONS_LOOP_NEST) {
@@ -560,17 +551,25 @@ private boolean ensureMaterialized(PartialEscapeBlockState<?> state, int object,
560551
* it means we try to materialize an allocation from an outer loop, this causes
561552
* multiple iterations of the PEA algorithm for iterative loop processing and the
562553
* algorithm becomes exponential over the loop depth, thus we leave this loop and do
563-
* not virtualize anything
554+
* not virtualize anything.
564555
*/
565556
throw new EffectsClosure.EffecsClosureOverflowException();
566557
}
567558
counter.increment(debug);
568559
VirtualObjectNode virtual = virtualObjects.get(object);
569-
state.materializeBefore(materializeBefore, virtual, effects);
570560

571-
if (requiresStrictLockOrder && materializedAcquiredLocks && objectState.hasLocks()) {
561+
GraalError.guarantee(objectState.isVirtual(), "%s is not virtual", objectState);
562+
if (requiresStrictLockOrder && objectState.hasLocks()) {
572563
materializeVirtualLocksBefore(state, materializeBefore, effects, counter, objectState.getLockDepth());
573564
}
565+
/*
566+
* At this point, the current object may have been materialized due to materializing
567+
* lower depth locks that have a data dependency to the current object.
568+
*/
569+
objectState = state.getObjectState(object);
570+
if (objectState.isVirtual()) {
571+
state.materializeBefore(materializeBefore, virtual, effects);
572+
}
574573

575574
assert !updateStatesForMaterialized(state, virtual, state.getObjectState(object).getMaterializedValue()) : "method must already have been called before";
576575
return true;
@@ -593,7 +592,7 @@ private boolean ensureMaterialized(PartialEscapeBlockState<?> state, int object,
593592
* }
594593
* }
595594
* </pre>
596-
*
595+
* <p>
597596
* PEA may emit:
598597
*
599598
* <pre>
@@ -605,14 +604,14 @@ private boolean ensureMaterialized(PartialEscapeBlockState<?> state, int object,
605604
* monitorexit otherObj
606605
* monitorexit obj
607606
* </pre>
608-
*
607+
* <p>
609608
* On HotSpot, unstructured locking is acceptable for stack locking (LM_LEGACY) and heavy
610609
* monitor (LM_MONITOR). This is because locks under these locking modes are referenced by
611610
* pointers stored in the object mark word, and are not necessary contiguous in memory. There is
612611
* no way to observe a lock disorder from outside, as long as PEA guarantee that a virtual lock
613612
* is materialized and held before it escapes or before the runtime deoptimizes and transfers to
614613
* interpreter.
615-
*
614+
* <p>
616615
* Lightweight locking (LM_LIGHTWEIGHT), however, maintains locks in a thread-local lock stack.
617616
* The more inner lock occupies the closer slot to the lock stack top. Unstructured locking code
618617
* will disrupt the lock stack and result in an inconsistent state. For instance, the lock stack
@@ -625,56 +624,37 @@ private boolean ensureMaterialized(PartialEscapeBlockState<?> state, int object,
625624
* | otherObj |
626625
* ------------
627626
* </pre>
628-
*
627+
* <p>
629628
* and is
630629
*
631630
* <pre>
632631
* ------------
633632
* | otherObj | <-- stack top
634633
* ------------
635634
* </pre>
636-
*
635+
* <p>
637636
* after the first {code monitorexit} instruction. At this point, the still-locked object
638637
* {@code obj} is not maintained in the lock stack, while the lock stack top points to
639638
* {@code otherObj}, which is with an unlocked mark word. Such inconsistent state can be
640639
* observed from outside by scanning a thread's lock stack.
641-
*
640+
* <p>
642641
* To avoid such scenario, we disallow PEA to emit unstructured locking code when using
643642
* lightweight locking. We materialize all virtual objects that potentially get materialized in
644643
* subsequent control flow point and hold locks with lock depth smaller than {@code lockDepth}.
645-
* For those will not get materialized (see {@link #mayBeMaterialized}), we keep them virtual
646-
* and effectively apply lock elimination. The runtime deoptimization code will take care of
647-
* rematerialization of these virtual locks (see JDK-8318895).
648644
*/
649645
private void materializeVirtualLocksBefore(PartialEscapeBlockState<?> state, FixedNode materializeBefore,
650646
GraphEffectList effects, CounterKey counter, int lockDepth) {
651647
for (VirtualObjectNode other : virtualObjects) {
652648
int otherID = other.getObjectId();
653649
if (state.hasObjectState(otherID)) {
654650
ObjectState otherState = state.getObjectState(other);
655-
if (otherState.isVirtual() && otherState.hasLocks() && otherState.getLockDepth() < lockDepth && mayBeMaterialized(other)) {
656-
ensureMaterialized(state, other.getObjectId(), materializeBefore, effects, counter, false);
651+
if (otherState.isVirtual() && otherState.hasLocks() && otherState.getLockDepth() < lockDepth) {
652+
ensureMaterialized(state, other.getObjectId(), materializeBefore, effects, counter);
657653
}
658654
}
659655
}
660656
}
661657

662-
/**
663-
* @return true if this virtual object may be materialized.
664-
*/
665-
private boolean mayBeMaterialized(VirtualObjectNode virtualObjectNode) {
666-
MapCursor<Node, ValueNode> cursor = aliases.getEntries();
667-
while (cursor.advance()) {
668-
if (virtualObjectNode == cursor.getValue()) {
669-
Node allocation = cursor.getKey();
670-
// This is conservative. In practice, PEA may scalar-replace field accesses and
671-
// prevent this virtual object from escaping.
672-
return allocation.usages().filter(n -> n instanceof ValueNode && !(n instanceof AccessMonitorNode)).isNotEmpty();
673-
}
674-
}
675-
return true;
676-
}
677-
678658
public static boolean updateStatesForMaterialized(PartialEscapeBlockState<?> state, VirtualObjectNode virtual, ValueNode materializedValue) {
679659
// update all existing states with the newly materialized object
680660
boolean change = false;

0 commit comments

Comments
 (0)