Skip to content

Commit 2caae23

Browse files
committed
Ensure FrameStates don't violate lock order forced by lightweight locking.
1 parent 95b689f commit 2caae23

File tree

10 files changed

+121
-52
lines changed

10 files changed

+121
-52
lines changed

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

Lines changed: 84 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -33,13 +33,7 @@
3333
import org.junit.Test;
3434

3535
import jdk.graal.compiler.api.directives.GraalDirectives;
36-
import jdk.graal.compiler.graph.Node;
37-
import jdk.graal.compiler.graph.NodeFlood;
3836
import jdk.graal.compiler.hotspot.replacements.HotSpotReplacementsUtil;
39-
import jdk.graal.compiler.nodes.AbstractEndNode;
40-
import jdk.graal.compiler.nodes.StructuredGraph;
41-
import jdk.graal.compiler.nodes.java.MonitorEnterNode;
42-
import jdk.graal.compiler.nodes.java.MonitorExitNode;
4337

4438
/**
4539
* Tests that PEA preserves the monitorenter order. This is essential for lightweight locking.
@@ -164,37 +158,7 @@ public void testSnippet4() {
164158
test("snippet4", new Object(), true, true);
165159
}
166160

167-
@Override
168-
protected void checkMidTierGraph(StructuredGraph graph) {
169-
// The following lock depth check only works with simple control flow.
170-
NodeFlood flood = graph.createNodeFlood();
171-
flood.add(graph.start());
172-
173-
int lockDepth = -1;
174-
175-
for (Node current : flood) {
176-
if (current instanceof AbstractEndNode) {
177-
AbstractEndNode end = (AbstractEndNode) current;
178-
flood.add(end.merge());
179-
} else {
180-
if (current instanceof MonitorEnterNode enter) {
181-
int depth = enter.getMonitorId().getLockDepth();
182-
assertTrue(lockDepth < depth);
183-
lockDepth = depth;
184-
} else if (current instanceof MonitorExitNode exit) {
185-
int depth = exit.getMonitorId().getLockDepth();
186-
assertTrue(lockDepth >= depth);
187-
lockDepth = depth;
188-
}
189-
190-
for (Node successor : current.successors()) {
191-
flood.add(successor);
192-
}
193-
}
194-
}
195-
}
196-
197-
static class B {
161+
public static class B {
198162
Float f = 1.0f;
199163
}
200164

@@ -296,6 +260,88 @@ public void testSnippet9() {
296260
test("snippet9");
297261
}
298262

263+
@BytecodeParserNeverInline
264+
public static void callee0() {
265+
synchronized (B.class) {
266+
}
267+
}
268+
269+
@BytecodeParserNeverInline
270+
public static void callee1() {
271+
staticObj = new B();
272+
callee0();
273+
}
274+
275+
@BytecodeParserNeverInline
276+
public static void callee() {
277+
callee1();
278+
}
279+
280+
public static void snippet10() {
281+
B b = new B();
282+
synchronized (b) {
283+
callee();
284+
}
285+
}
286+
287+
@Test
288+
public void testSnippet10() {
289+
test("snippet10");
290+
}
291+
292+
static RuntimeException cachedException = new RuntimeException();
293+
294+
public static void snippet11(boolean flag) {
295+
B b = new B();
296+
synchronized (b) {
297+
if (flag) {
298+
staticObj = b;
299+
throw cachedException;
300+
}
301+
}
302+
}
303+
304+
@Test
305+
public void testSnippet11() {
306+
test("snippet11", true);
307+
}
308+
309+
public static void snippet12() {
310+
B b = new B();
311+
synchronized (b) {
312+
callee();
313+
staticObj = b;
314+
}
315+
}
316+
317+
@Test
318+
public void testSnippet12() {
319+
test("snippet12");
320+
}
321+
322+
public static void snippet13(boolean flag, boolean escape) {
323+
B b = new B();
324+
325+
synchronized (b) {
326+
synchronized (b) {
327+
if (GraalDirectives.injectBranchProbability(0.9, flag)) {
328+
GraalDirectives.controlFlowAnchor();
329+
if (GraalDirectives.injectBranchProbability(0.1, escape)) {
330+
staticObj = b;
331+
}
332+
} else {
333+
staticObj1 = b;
334+
throw cachedException;
335+
}
336+
}
337+
}
338+
}
339+
340+
@Test
341+
public void testSnippet13() {
342+
test("snippet13", true, true);
343+
}
344+
299345
public static void snippet15(boolean deoptimize) {
300346
synchronized (new B()) {
301347
synchronized (new StringBuilder()) {

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/HotSpotDebugInfoBuilder.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
import jdk.graal.compiler.nodes.extended.ForeignCall;
4646
import jdk.graal.compiler.nodes.spi.NodeValueMap;
4747
import jdk.graal.compiler.nodes.spi.NodeWithState;
48-
4948
import jdk.vm.ci.code.BytecodeFrame;
5049
import jdk.vm.ci.code.StackLockValue;
5150
import jdk.vm.ci.code.VirtualObject;
@@ -102,7 +101,7 @@ protected void verifyFrameState(NodeWithState node, FrameState topState) {
102101

103102
FrameState current = topState;
104103
while (current != null) {
105-
assert current.getMethod() instanceof HotSpotResolvedJavaMethod : current;
104+
GraalError.guarantee(current.getMethod() instanceof HotSpotResolvedJavaMethod, "unsupported method type %s", current.getMethod());
106105
current = current.outerFrameState();
107106
}
108107

@@ -112,7 +111,7 @@ protected void verifyFrameState(NodeWithState node, FrameState topState) {
112111
if (DefaultHotSpotLoweringProvider.RuntimeCalls.runtimeCalls.containsValue(descriptor.getSignature())) {
113112
// This case is special because we can't use rethrowException. This path must be
114113
// marked as reexecutable so it doesn't fail some internal asserts even though the
115-
// actual deopt is done with Unpack_exception which overrides the reexectuable flag
114+
// actual deopt is done with Unpack_exception which overrides the reexecutable flag
116115
GraalError.guarantee(topState.getStackState() == FrameState.StackState.BeforePop && topState.bci >= 0 && topState.stackSize() == 0, "invalid state %s for bytecode exception %s",
117116
topState, descriptor.getSignature());
118117
GraalError.guarantee(!topState.getStackState().duringCall, "must be not duringCall to set reexecute bit");

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replacements/MonitorSnippets.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,8 @@ public static class Templates extends AbstractTemplates {
850850

851851
public final Counters counters;
852852

853+
private final boolean requiresStrictLockOrder;
854+
853855
@SuppressWarnings("this-escape")
854856
public Templates(OptionValues options, SnippetCounter.Group.Factory factory, HotSpotProviders providers, GraalHotSpotVMConfig config) {
855857
super(options, providers);
@@ -899,6 +901,7 @@ public Templates(OptionValues options, SnippetCounter.Group.Factory factory, Hot
899901
this.checkCounter = snippet(providers, MonitorSnippets.class, "checkCounter");
900902

901903
this.counters = new Counters(factory);
904+
this.requiresStrictLockOrder = providers.getPlatformConfigurationProvider().requiresStrictLockOrder();
902905
}
903906

904907
public void lower(CheckFastPathMonitorEnterNode checkFastPathMonitorEnterNode, HotSpotRegistersProvider registers, GraalHotSpotVMConfig config, LoweringTool tool) {
@@ -916,11 +919,31 @@ public void lower(CheckFastPathMonitorEnterNode checkFastPathMonitorEnterNode, H
916919
template(tool, checkFastPathMonitorEnterNode, args).instantiate(tool.getMetaAccess(), checkFastPathMonitorEnterNode, DEFAULT_REPLACER, args);
917920
}
918921

922+
private boolean verifyLockOrder(MonitorEnterNode monitorenterNode) {
923+
if (requiresStrictLockOrder) {
924+
FrameState state = monitorenterNode.stateAfter();
925+
boolean subsequentLocksMustBeEliminated = false;
926+
for (int lockIdx = 0; lockIdx < state.locksSize(); lockIdx++) {
927+
if (subsequentLocksMustBeEliminated) {
928+
if (!state.monitorIdAt(lockIdx).isEliminated()) {
929+
return false;
930+
}
931+
}
932+
if (state.monitorIdAt(lockIdx) == monitorenterNode.getMonitorId()) {
933+
subsequentLocksMustBeEliminated = true;
934+
}
935+
}
936+
}
937+
return true;
938+
}
939+
919940
public void lower(MonitorEnterNode monitorenterNode, HotSpotRegistersProvider registers, LoweringTool tool) {
920941
StructuredGraph graph = monitorenterNode.graph();
921942
checkBalancedMonitors(graph, tool);
922943

923-
assert ((ObjectStamp) monitorenterNode.object().stamp(NodeView.DEFAULT)).nonNull();
944+
GraalError.guarantee(((ObjectStamp) monitorenterNode.object().stamp(NodeView.DEFAULT)).nonNull(), "should have a non-null stamp: %s", monitorenterNode);
945+
GraalError.guarantee(!monitorenterNode.getMonitorId().isEliminated(), "current monitor is eliminated: %s", monitorenterNode);
946+
GraalError.guarantee(verifyLockOrder(monitorenterNode), "locks are disordered: %s", monitorenterNode);
924947

925948
Arguments args = new Arguments(monitorenter, graph.getGuardsStage(), tool.getLoweringStage());
926949
args.add("object", monitorenterNode.object());

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/java/MonitorEnterNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/spi/VirtualizerTool.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/common/LockEliminationPhase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -122,6 +122,7 @@
122122
import jdk.graal.compiler.nodes.gc.BarrierSet;
123123
import jdk.graal.compiler.nodes.java.AbstractNewObjectNode;
124124
import jdk.graal.compiler.nodes.java.AccessIndexedNode;
125+
import jdk.graal.compiler.nodes.java.AccessMonitorNode;
125126
import jdk.graal.compiler.nodes.java.ArrayLengthNode;
126127
import jdk.graal.compiler.nodes.java.AtomicReadAndAddNode;
127128
import jdk.graal.compiler.nodes.java.AtomicReadAndWriteNode;
@@ -133,7 +134,6 @@
133134
import jdk.graal.compiler.nodes.java.LoweredAtomicReadAndAddNode;
134135
import jdk.graal.compiler.nodes.java.LoweredAtomicReadAndWriteNode;
135136
import jdk.graal.compiler.nodes.java.MonitorEnterNode;
136-
import jdk.graal.compiler.nodes.java.MonitorExitNode;
137137
import jdk.graal.compiler.nodes.java.MonitorIdNode;
138138
import jdk.graal.compiler.nodes.java.NewArrayNode;
139139
import jdk.graal.compiler.nodes.java.NewInstanceNode;
@@ -1169,8 +1169,8 @@ public void finishAllocatedObjects(LoweringTool tool, FixedWithNextNode insertAf
11691169
if (!newList.contains(lock)) {
11701170
// lock is nested and eliminated
11711171
for (Node usage : lock.usages().snapshot()) {
1172-
if (usage.isAlive() && usage instanceof MonitorExitNode exit) {
1173-
removeMonitorAccess(exit);
1172+
if (usage.isAlive() && usage instanceof AccessMonitorNode access) {
1173+
removeMonitorAccess(access);
11741174
}
11751175
}
11761176
lock.setEliminated();
@@ -1184,6 +1184,7 @@ public void finishAllocatedObjects(LoweringTool tool, FixedWithNextNode insertAf
11841184
int lastDepth = -1;
11851185
for (MonitorIdNode monitorId : locks) {
11861186
GraalError.guarantee(lastDepth < monitorId.getLockDepth(), Assertions.errorMessage(lastDepth, monitorId, insertAfter, commit, allocations));
1187+
GraalError.guarantee(!monitorId.isEliminated(), Assertions.errorMessage(lastDepth, monitorId, insertAfter, commit, allocations));
11871188
lastDepth = monitorId.getLockDepth();
11881189
MonitorEnterNode enter = graph.add(new MonitorEnterNode(allocations[commit.getObjectIndex(monitorId)], monitorId));
11891190
enter.setSynthetic();

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it

0 commit comments

Comments
 (0)