RFR: 8363620: AArch64: reimplement emit_static_call_stub() [v6]
Fei Gao
fgao at openjdk.org
Mon Dec 22 18:02:59 UTC 2025
On Wed, 17 Dec 2025 18:17:57 GMT, Andrew Haley <aph at openjdk.org> wrote:
>>>
>>> That visibility is not guaranteed. A `B .+4` already exists because the static stub has already been patched. We then rewrite the static stub because the destination method moved.
>>
>> I see. Thanks for your explanation!
>>
>>> What about a 3rd thread that sees the call site already resolved so never calls the resolve helper? It won't execute an ISB either. I thought the ISB was only needed to see changes to the stub, not the call site.
>>
>> Assume the following race:
>> - One thread has already entered the resolver and started patching, but has not yet completed.
>> - Another thread has **not** observed the updated call site, but **has** observed the patched trampoline stub and therefore reaches the static call stub directly via the trampoline stub.
>>
>> In this case, the second thread will not execute the resolver. An ISB in `CompiledICLocker::~CompiledICLocker()` would not address this scenario, because the second thread never goes through the resolver path and therefore never executes that ISB. As a result, the second thread may execute stale MOV instructions in the static call stub. Would that be a problem? I’m not sure whether this is what @dean-long had in mind.
>
>> Assume the following race:
>>
>> * One thread has already entered the resolver and started patching, but has not yet completed.
>>
>> * Another thread has **not** observed the updated call site, but **has** observed the patched trampoline stub and therefore reaches the static call stub directly via the trampoline stub.
>
> OK.
>
>> In this case, the second thread will not execute the resolver. An ISB in `CompiledICLocker::~CompiledICLocker()` would not address this scenario, because the second thread never goes through the resolver path and therefore never executes that ISB. As a result, the second thread may execute stale MOV instructions in the static call stub. Would that be a problem?
>
> I don't think that's possible, because rewriting when a class gets redefined only happens at a safepoint. So I think we're OK, probably.
Thanks for your answers! @theRealAph @dean-long
> Actually, that scenario does seem possible. Consider:
>
> 1. Call site points to trampoline
> 2. Thread 1 hits call site and enters trampoline, then gets rescheduled
> 3. Something causes a re-resolve and the call site to be reset to the resolve stub
> 4. Thread 2 hits the call site and eventually calls set_to_interpreted, updating stub, trampoline, and call site
>
> Depending on when Thread 1 resumes, I believe Thread 1 can see any of the following:
>
> 1. old destination
> 2. stub destination with ISB + MOVs
> 3. stub destination with B + MOVs
For the case **without** class redefinition, patching performed by Thread 2 are limited to the following:
1. ISB + initial MOVs → B + patched MOVs
2. B + patched MOVs → B + patched MOVs (rewriting with **the same** values)
In either case, Thread 1 should always observe a valid set of MOV instructions, so these transitions appear safe.
**With** class redefinition, however, an additional patching becomes possible:
3. B + patched MOVs → B + patched MOVs (rewriting with **different** values)
At that point, such patching by Thread 2 could potentially expose stale or partially updated MOV instructions to Thread 1. The key question, then, is **whether the race between Thread 1 and Thread 2 can coincide with class redefinition**.
> I don't think that's possible, because rewriting when a class gets redefined only happens at a safepoint. So I think we're OK, probably.
If patching the static call stub after class redefinition can only occur at a safepoint, then I would expect this race not to be possible, since there is no safepoint between executing the call site and entering the trampoline.
AFAIU, when a class is redefined, affected compiled callers are [marked not entrant](https://github.com/openjdk/jdk/blob/9715e6da8355a103d9066bd15ce68b4773cbadcb/src/hotspot/share/prims/jvmtiRedefineClasses.cpp#L278), and call sites are effectively [reset to go through the resolve stub](https://github.com/openjdk/jdk/blob/9715e6da8355a103d9066bd15ce68b4773cbadcb/src/hotspot/share/prims/jvmtiRedefineClasses.cpp#L218). What I have not been able to identify is **where the static call stub itself is patched as part of class redefinition**. I would really appreciate it if someone could point me to the relevant code path.
If the static stub is **not** patched during class redefinition itself, then:
- after class redefinition completes and safepoints are exited,
- new threads are prevented from entering the caller because it is not entrant,
- but threads already executing inside the caller continue running, those threads may reach the resolve path and patch the static call stub outside of a safepoint.
If this understanding is incorrect, please let me know—I’d be happy to be corrected. Thanks!
-------------
PR Comment: https://git.openjdk.org/jdk/pull/26638#issuecomment-3683214894
More information about the hotspot-dev
mailing list