diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index fcc24060a3a..b3161fe2208 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -3427,6 +3427,7 @@ encode %{ call->_oop_map = _oop_map; call->_jvms = _jvms; call->_jvmadj = _jvmadj; + call->_has_ea_local_in_scope = _has_ea_local_in_scope; call->_in_rms = _in_rms; call->_nesting = _nesting; call->_override_symbolic_info = _override_symbolic_info; diff --git a/test/jdk/com/sun/jdi/EATests.java b/test/jdk/com/sun/jdi/EATests.java index cd80d01a07f..72859481659 100644 --- a/test/jdk/com/sun/jdi/EATests.java +++ b/test/jdk/com/sun/jdi/EATests.java @@ -289,6 +289,7 @@ class EATestsTarget { // Relocking test cases new EARelockingSimpleTarget() .run(); new EARelockingSimpleWithAccessInOtherThreadTarget() .run(); + new EARelockingSimpleWithAccessInOtherThread_02_DynamicCall_Target() .run(); new EARelockingRecursiveTarget() .run(); new EARelockingNestedInflatedTarget() .run(); new EARelockingNestedInflated_02Target() .run(); @@ -413,6 +414,7 @@ public class EATests extends TestScaffold { // Relocking test cases new EARelockingSimple() .run(this); new EARelockingSimpleWithAccessInOtherThread() .run(this); + new EARelockingSimpleWithAccessInOtherThread_02_DynamicCall() .run(this); new EARelockingRecursive() .run(this); new EARelockingNestedInflated() .run(this); new EARelockingNestedInflated_02() .run(this); @@ -1851,6 +1853,95 @@ class EARelockingSimpleWithAccessInOtherThreadTarget extends EATestCaseBaseTarge ///////////////////////////////////////////////////////////////////////////// +// The debugger reads and publishes an object with eliminated locking to an instance field. +// A 2nd thread in the debuggee finds it there and changes its state using a synchronized method. +// Without eager relocking the accesses are unsynchronized which can be observed. +// This is a variant of EARelockingSimpleWithAccessInOtherThread with a dynamic call (not devirtualized). +class EARelockingSimpleWithAccessInOtherThread_02_DynamicCall extends EATestCaseBaseDebugger { + + public void runTestCase() throws Exception { + BreakpointEvent bpe = resumeTo(TARGET_TESTCASE_BASE_NAME, "dontinline_brkpt", "()V"); + printStack(bpe.thread()); + String l1ClassName = EARelockingSimpleWithAccessInOtherThread_02_DynamicCall_Target.SyncCounter.class.getName(); + ObjectReference ctr = getLocalRef(bpe.thread().frame(2), l1ClassName, "l1"); + setField(testCase, "sharedCounter", ctr); + terminateEndlessLoop(); + } +} + +class EARelockingSimpleWithAccessInOtherThread_02_DynamicCall_Target extends EATestCaseBaseTarget { + + public static final BrkPtDispatchA[] disp = + {new BrkPtDispatchA(), new BrkPtDispatchB(), new BrkPtDispatchC(), new BrkPtDispatchD()}; + + public static class BrkPtDispatchA { + public EATestCaseBaseTarget testCase; + public void dontinline_brkpt() { testCase.dontinline_brkpt(); } + } + + public static class BrkPtDispatchB extends BrkPtDispatchA { + @Override + public void dontinline_brkpt() { testCase.dontinline_brkpt(); } + } + + public static class BrkPtDispatchC extends BrkPtDispatchA { + @Override + public void dontinline_brkpt() { testCase.dontinline_brkpt(); } + } + + public static class BrkPtDispatchD extends BrkPtDispatchA { + @Override + public void dontinline_brkpt() { + testCase.dontinline_brkpt(); + } + } + + public static class SyncCounter { + private int val; + public synchronized int inc() { return val++; } + } + + public volatile SyncCounter sharedCounter; + + @Override + public void setUp() { + super.setUp(); + testMethodDepth = 2; + for (BrkPtDispatchA d : disp) { + d.testCase = this; + } + doLoop = true; + new Thread(() -> { + while (doLoop) { + SyncCounter ctr = sharedCounter; + if (ctr != null) { + ctr.inc(); + } + } + }).start(); + } + + public int dispCount; + public void dontinline_testMethod() { + SyncCounter l1 = new SyncCounter(); + synchronized (l1) { // Eliminated locking + l1.inc(); + // Use different types for the subsequent call to prevent devirtualization. + BrkPtDispatchA d = disp[(dispCount++) & 3]; + d.dontinline_brkpt(); // Dynamic call. Debugger publishes l1 to sharedCounter. + iResult = l1.inc(); // Changes by the 2nd thread will be observed if l1 + // was not relocked before passing it to the debugger. + } + } + + @Override + public int getExpectedIResult() { + return 1; + } +} + +///////////////////////////////////////////////////////////////////////////// + // Test recursive locking class EARelockingRecursiveTarget extends EATestCaseBaseTarget {