RFR: 8320649: C2: Optimize scoped values [v7]

Emanuel Peter epeter at openjdk.org
Tue Feb 13 17:51:59 UTC 2024


On Wed, 7 Feb 2024 08:06:09 GMT, Roland Westrelin <roland at openjdk.org> wrote:

>> This change implements C2 optimizations for calls to
>> ScopedValue.get(). Indeed, in:
>> 
>> 
>> v1 = scopedValue.get();
>> ...
>> v2 = scopedValue.get();
>> 
>> 
>> `v2` can be replaced by `v1` and the second call to `get()` can be
>> optimized out. That's true whatever is between the 2 calls unless a
>> new mapping for `scopedValue` is created in between (when that happens
>> no optimizations is performed for the method being compiled). Hoisting
>> a `get()` call out of loop for a loop invariant `scopedValue` should
>> also be legal in most cases.
>> 
>> `ScopedValue.get()` is implemented in java code as a 2 step process. A
>> cache is attached to the current thread object. If the `ScopedValue`
>> object is in the cache then the result from `get()` is read from
>> there. Otherwise a slow call is performed that also inserts the
>> mapping in the cache. The cache itself is lazily allocated. One
>> `ScopedValue` can be hashed to 2 different indexes in the cache. On a
>> cache probe, both indexes are checked. As a consequence, the process
>> of probing the cache is a multi step process (check if the cache is
>> present, check first index, check second index if first index
>> failed). If the cache is populated early on, then when the method that
>> calls `ScopedValue.get()` is compiled, profile reports the slow path
>> as never taken and only the read from the cache is compiled.
>> 
>> To perform the optimizations, I added 3 new node types to C2:
>> 
>> - the pair
>>   ScopedValueGetHitsInCacheNode/ScopedValueGetLoadFromCacheNode for
>>   the cache probe
>>   
>> - a cfg node ScopedValueGetResultNode to help locate the result of the
>>   `get()` call in the IR graph.
>> 
>> In pseudo code, once the nodes are inserted, the code of a `get()` is:
>> 
>> 
>> hits_in_the_cache = ScopedValueGetHitsInCache(scopedValue)
>> if (hits_in_the_cache) {
>>   res = ScopedValueGetLoadFromCache(hits_in_the_cache);
>> } else {
>>   res = ..; //slow call possibly inlined. Subgraph can be arbitray complex
>> }
>> res = ScopedValueGetResult(res)
>> 
>> 
>> In the snippet:
>> 
>> 
>> v1 = scopedValue.get();
>> ...
>> v2 = scopedValue.get();
>> 
>> 
>> Replacing `v2` by `v1` is then done by starting from the
>> `ScopedValueGetResult` node for the second `get()` and looking for a
>> dominating `ScopedValueGetResult` for the same `ScopedValue`
>> object. When one is found, it is used as a replacement. Eliminating
>> the second `get()` call is achieved by making
>> `ScopedValueGetHitsInCache` always successful if there's a dominating
>> `Scoped...
>
> Roland Westrelin has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains ten commits:
> 
>  - review comment
>  - Merge branch 'master' into JDK-8320649
>  - Update src/hotspot/share/opto/callGenerator.cpp
>    
>    Co-authored-by: Emanuel Peter <emanuel.peter at oracle.com>
>  - Update test/hotspot/jtreg/compiler/c2/irTests/TestScopedValue.java
>    
>    Co-authored-by: Andrey Turbanov <turbanoff at gmail.com>
>  - merge fix
>  - Merge branch 'master' into JDK-8320649
>  - test failures
>  - white spaces + bug id in test
>  - test & fix

For `java/lang/ScopedValue/StressStackOverflow.java#no-TieredCompilation`, no extra flags on `linux-aarch64` (product build):
[test_jdk_tier1_part1_java_lang_ScopedValue_StressStackOverflow_no-TieredCompilation_replay_pid1260224.log](https://github.com/openjdk/jdk/files/14270266/test_jdk_tier1_part1_java_lang_ScopedValue_StressStackOverflow_no-TieredCompilation_replay_pid1260224.log)

And a second failure:
`serviceability/jvmti/vthread/SuspendWithInterruptLock/SuspendWithInterruptLock.java#default`, with flags:
`-Xcomp -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation` on `	linux-aarch64-debug`. No replay since it does not seem to happen during compilation.


#  Internal Error (/opt/mach5/mesos/work_dir/slaves/0db9c48f-6638-40d0-9a4b-bd9cc7533eb8-S9925/frameworks/1735e8a2-a1db-478c-8104-60c8b0af87dd-0196/executors/88e53a9e-90ec-4ff7-a48b-f6334fa445de/runs/40b98fc8-a415-4425-860b-fbe1fb83e170/workspace/open/src/hotspot/share/prims/jvmtiEnvBase.cpp:1731), pid=1617205, tid=1617312
#  assert(!java_thread->is_in_VTMS_transition()) failed: sanity check

Current thread (0x0000ffff4c08ff10):  JavaThread "Thread-1"         [_thread_in_vm, id=1617312, stack(0x0000ffff809c5000,0x0000ffff80bc3000) (2040K)] _threads_hazard_ptr=0x0000ffff4c091310, _nested_threads_hazard_ptr_cnt=0

Stack: [0x0000ffff809c5000,0x0000ffff80bc3000],  sp=0x0000ffff80bc1020,  free space=2032k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [libjvm.so+0x1038de8]  JvmtiEnvBase::suspend_thread(oop, JavaThread*, bool, int*)+0x528  (jvmtiEnvBase.cpp:1731)
V  [libjvm.so+0x10227f8]  JvmtiEnv::SuspendThread(_jobject*)+0xb8  (jvmtiEnv.cpp:948)
V  [libjvm.so+0xfe0504]  jvmti_SuspendThread+0x194  (jvmtiEnter.cpp:534)
J 2631  jvmti.JVMTIUtils.suspendThread0(Ljava/lang/Thread;)I (0 bytes) @ 0x0000ffff953e4078 [0x0000ffff953e3fc0+0x00000000000000b8]
J 2627 c2 jvmti.JVMTIUtils.suspendThread(Ljava/lang/Thread;)V (22 bytes) @ 0x0000ffff953e8310 [0x0000ffff953e82c0+0x0000000000000050]
j  SuspendWithInterruptLock.suspendThread(Ljava/lang/Thread;)V+1
j  SuspendWithInterruptLock.suspender(Ljava/lang/Thread;)V+7
j  SuspendWithInterruptLock.lambda$main$2(Ljava/lang/Thread;)V+1
J 2582 c2 SuspendWithInterruptLock$$Lambda+0x0000000201003458.run()V (8 bytes) @ 0x0000ffff953d2dac [0x0000ffff953d2d40+0x000000000000006c]
J 1763 c2 java.lang.Thread.run()V java.base at 23-internal (23 bytes) @ 0x0000ffff951461e8 [0x0000ffff95146140+0x00000000000000a8]
v  ~StubRoutines::call_stub 0x0000ffff94c1c190
V  [libjvm.so+0xd49374]  JavaCalls::call_helper(JavaValue*, methodHandle const&, JavaCallArguments*, JavaThread*)+0x464  (javaCalls.cpp:415)
V  [libjvm.so+0xd4993c]  JavaCalls::call_virtual(JavaValue*, Klass*, Symbol*, Symbol*, JavaCallArguments*, JavaThread*)+0x29c  (javaCalls.cpp:329)
V  [libjvm.so+0xd49b3c]  JavaCalls::call_virtual(JavaValue*, Handle, Klass*, Symbol*, Symbol*, JavaThread*)+0x6c  (javaCalls.cpp:191)
V  [libjvm.so+0xea4990]  thread_entry(JavaThread*, JavaThread*)+0xa0  (jvm.cpp:2937)
V  [libjvm.so+0xd7d71c]  JavaThread::thread_main_inner()+0xcc  (javaThread.cpp:721)
V  [libjvm.so+0x15beec0]  Thread::call_run()+0xac  (thread.cpp:221)
V  [libjvm.so+0x1333bcc]  thread_native_entry(Thread*)+0x12c  (os_linux.cpp:817)
C  [libpthread.so.0+0x7928]  start_thread+0x188
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
J 2631  jvmti.JVMTIUtils.suspendThread0(Ljava/lang/Thread;)I (0 bytes) @ 0x0000ffff953e4078 [0x0000ffff953e3fc0+0x00000000000000b8]
J 2627 c2 jvmti.JVMTIUtils.suspendThread(Ljava/lang/Thread;)V (22 bytes) @ 0x0000ffff953e8310 [0x0000ffff953e82c0+0x0000000000000050]
j  SuspendWithInterruptLock.suspendThread(Ljava/lang/Thread;)V+1
j  SuspendWithInterruptLock.suspender(Ljava/lang/Thread;)V+7
j  SuspendWithInterruptLock.lambda$main$2(Ljava/lang/Thread;)V+1
J 2582 c2 SuspendWithInterruptLock$$Lambda+0x0000000201003458.run()V (8 bytes) @ 0x0000ffff953d2dac [0x0000ffff953d2d40+0x000000000000006c]
J 1763 c2 java.lang.Thread.run()V java.base at 23-internal (23 bytes) @ 0x0000ffff951461e8 [0x0000ffff95146140+0x00000000000000a8]
v  ~StubRoutines::call_stub 0x0000ffff94c1c190

-------------

PR Comment: https://git.openjdk.org/jdk/pull/16966#issuecomment-1942095739


More information about the hotspot-compiler-dev mailing list