Skip to content

[GR-64446] Materializing virtual input with lock now causes materialization of virtual objects with lower-depth locks. #11182

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,12 @@ public void testSnippet3() {
test("snippet3", new Object(), true);
}

static class A {
Object o = new Object();
record A(Object o) {
}

@SuppressWarnings("unused")
public static void snippet4(Object external, boolean flag, boolean flag1) {
A escaped = new A();
A escaped = new A(new Object());

synchronized (escaped) {
synchronized (external) {
Expand Down Expand Up @@ -465,4 +464,39 @@ public static void snippet20(Object o) {
public void testSnippet20() {
test("snippet20", new Object());
}

public static void snippet21() {
Object l1 = new Object();
Object l2 = new Object();
synchronized (l1) {
synchronized (l2) {
staticObj = new Object[]{l2};
synchronized (A.class) {
}
}
}
}

@Test
public void testSnippet21() {
test("snippet21");
}

public static void snippet22() {
Object l2 = new Object();
Object l1 = new A(l2);

synchronized (l1) {
synchronized (l2) {
staticObj = new Object[]{l2};
synchronized (A.class) {
}
}
}
}

@Test
public void testSnippet22() {
test("snippet22");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@
import jdk.graal.compiler.core.common.type.ObjectStamp;
import jdk.graal.compiler.core.common.type.StampFactory;
import jdk.graal.compiler.core.common.type.StampPair;
import jdk.graal.compiler.debug.Assertions;
import jdk.graal.compiler.debug.GraalError;
import jdk.graal.compiler.graph.Node.ConstantNodeParameter;
import jdk.graal.compiler.graph.Node.NodeIntrinsic;
Expand Down Expand Up @@ -887,7 +886,7 @@ private static boolean isVirtualLock(FrameState frameState, int lockIdx) {
}

private boolean verifyLockOrder(MonitorEnterNode monitorenterNode) {
if (Assertions.assertionsEnabled() && requiresStrictLockOrder) {
if (requiresStrictLockOrder) {
FrameState state = monitorenterNode.stateAfter();
boolean subsequentLocksMustBeEliminated = false;
for (int lockIdx = 0; lockIdx < state.locksSize(); lockIdx++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import jdk.graal.compiler.debug.Assertions;
import jdk.graal.compiler.debug.CounterKey;
import jdk.graal.compiler.debug.DebugContext;
import jdk.graal.compiler.debug.GraalError;
import jdk.graal.compiler.nodes.ValueNode;
import jdk.graal.compiler.nodes.java.MonitorIdNode;
import jdk.graal.compiler.nodes.virtual.EscapeObjectState;
Expand Down Expand Up @@ -207,6 +208,8 @@ public void updateMaterializedValue(ValueNode value) {
}

public void addLock(MonitorIdNode monitorId) {
GraalError.guarantee(locks == null || locks.monitorId.getLockDepth() < monitorId.getLockDepth(),
"Adding lock %d to locks %s", monitorId.getLockDepth(), locks);
locks = new LockState(monitorId, locks);
}

Expand All @@ -226,10 +229,25 @@ public boolean hasLocks() {
return locks != null;
}

public int getLockDepth() {
public int getMaximumLockDepth() {
// Assume locks are ordered by their nesting depth in descending order (highest first).
return locks.monitorId.getLockDepth();
}

public int getMinimumLockDepth() {
// Assume locks are ordered by their nesting depth in descending order (highest first).
LockState current = locks;
int currentLockDepth = current.monitorId.getLockDepth();
while (current.next != null) {
int nextLockDepth = current.next.monitorId.getLockDepth();
GraalError.guarantee(currentLockDepth > nextLockDepth,
"Current: %s; Next: %s", current, current.next);
current = current.next;
currentLockDepth = nextLockDepth;
}
return current.monitorId.getLockDepth();
}

public boolean locksEqual(ObjectState other) {
LockState a = locks;
LockState b = other.locks;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,14 +206,14 @@ public void updateMaterializedValue(int object, ValueNode value) {
* entries.
*/
@SuppressWarnings("try")
public void materializeBefore(FixedNode fixed, VirtualObjectNode virtual, GraphEffectList materializeEffects) {
public void materializeBefore(FixedNode fixed, VirtualObjectNode virtual, boolean requiresStrictLockOrder, ArrayList<VirtualObjectNode> virtualObjects, GraphEffectList materializeEffects) {
PartialEscapeClosure.COUNTER_MATERIALIZATIONS.increment(fixed.getDebug());
List<AllocatedObjectNode> objects = new ArrayList<>(2);
List<ValueNode> values = new ArrayList<>(8);
List<List<MonitorIdNode>> locks = new ArrayList<>();
List<ValueNode> otherAllocations = new ArrayList<>(2);
List<Boolean> ensureVirtual = new ArrayList<>(2);
materializeWithCommit(fixed, virtual, objects, locks, values, ensureVirtual, otherAllocations, materializeEffects);
materializeWithCommit(fixed, virtual, objects, locks, values, ensureVirtual, otherAllocations, requiresStrictLockOrder, virtualObjects, materializeEffects);
/*
* because all currently virtualized allocations will be materialized in 1 commit alloc node
* with barriers, we ignore other allocations as we only process new instance and commit
Expand Down Expand Up @@ -290,7 +290,7 @@ void format(StringBuilder str) {
}

private void materializeWithCommit(FixedNode fixed, VirtualObjectNode virtual, List<AllocatedObjectNode> objects, List<List<MonitorIdNode>> locks, List<ValueNode> values,
List<Boolean> ensureVirtual, List<ValueNode> otherAllocations, GraphEffectList materializeEffects) {
List<Boolean> ensureVirtual, List<ValueNode> otherAllocations, boolean requiresStrictLockOrder, ArrayList<VirtualObjectNode> virtualObjects, GraphEffectList materializeEffects) {
ObjectState obj = getObjectState(virtual);

ValueNode[] entries = obj.getEntries();
Expand All @@ -311,14 +311,27 @@ private void materializeWithCommit(FixedNode fixed, VirtualObjectNode virtual, L
VirtualObjectNode entryVirtual = (VirtualObjectNode) entries[i];
ObjectState entryObj = getObjectState(entryVirtual);
if (entryObj.isVirtual()) {
materializeWithCommit(fixed, entryVirtual, objects, locks, values, ensureVirtual, otherAllocations, materializeEffects);
materializeWithCommit(fixed, entryVirtual, objects, locks, values, ensureVirtual, otherAllocations, requiresStrictLockOrder, virtualObjects, materializeEffects);
entryObj = getObjectState(entryVirtual);
}
values.set(pos + i, entryObj.getMaterializedValue());
} else {
values.set(pos + i, entries[i]);
}
}

if (requiresStrictLockOrder && obj.hasLocks()) {
int lockDepth = obj.getMaximumLockDepth();
for (VirtualObjectNode other : virtualObjects) {
if (other != virtual && hasObjectState(other.getObjectId())) {
ObjectState otherState = getObjectState(other);
if (otherState.isVirtual() && otherState.hasLocks() && otherState.getMinimumLockDepth() < lockDepth) {
materializeWithCommit(fixed, other, objects, locks, values, ensureVirtual, otherAllocations, requiresStrictLockOrder, virtualObjects, materializeEffects);
}
}
}
}

objectMaterialized(virtual, (AllocatedObjectNode) representation, values.subList(pos, pos + entries.length));
} else {
VirtualUtil.trace(options, debug, "materialized %s as %s", virtual, representation);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ protected void processStateBeforeLoopOnOverflow(BlockT initialState, FixedNode m
for (int i = 0; i < initialState.getStateCount(); i++) {
if (initialState.hasObjectState(i) && initialState.getObjectState(i).isVirtual()) {
VirtualObjectNode virtual = virtualObjects.get(i);
initialState.materializeBefore(materializeBefore, virtual, effects);
initialState.materializeBefore(materializeBefore, virtual, requiresStrictLockOrder, virtualObjects, effects);
}
}
}
Expand Down Expand Up @@ -559,17 +559,7 @@ protected boolean ensureMaterialized(PartialEscapeBlockState<?> state, int objec
VirtualObjectNode virtual = virtualObjects.get(object);

GraalError.guarantee(objectState.isVirtual(), "%s is not virtual", objectState);
if (requiresStrictLockOrder && objectState.hasLocks()) {
materializeVirtualLocksBefore(state, materializeBefore, effects, counter, objectState.getLockDepth());
}
/*
* At this point, the current object may have been materialized due to materializing
* lower depth locks that have a data dependency to the current object.
*/
objectState = state.getObjectState(object);
if (objectState.isVirtual()) {
state.materializeBefore(materializeBefore, virtual, effects);
}
state.materializeBefore(materializeBefore, virtual, requiresStrictLockOrder, virtualObjects, effects);

assert !updateStatesForMaterialized(state, virtual, state.getObjectState(object).getMaterializedValue()) : "method must already have been called before";
return true;
Expand Down Expand Up @@ -648,7 +638,7 @@ private void materializeVirtualLocksBefore(PartialEscapeBlockState<?> state, Fix
int otherID = other.getObjectId();
if (state.hasObjectState(otherID)) {
ObjectState otherState = state.getObjectState(other);
if (otherState.isVirtual() && otherState.hasLocks() && otherState.getLockDepth() < lockDepth) {
if (otherState.isVirtual() && otherState.hasLocks() && otherState.getMinimumLockDepth() < lockDepth) {
ensureMaterialized(state, other.getObjectId(), materializeBefore, effects, counter);
}
}
Expand Down
Loading