RFR: 8358821: patch_verified_entry causes problems, use nmethod entry barriers instead [v4]

Erik Österlund eosterlund at openjdk.org
Mon Jun 16 00:59:31 UTC 2025


On Fri, 13 Jun 2025 19:18:19 GMT, Dean Long <dlong at openjdk.org> wrote:

>> This PR removes patching of the verified entry point and related code, and replaces it by refactoring the existing nmethod entry barrier.
>> 
>> We used to patch the verified entry point to make sure it was not_entrant.  The patched entry point then redirected to SharedRuntime::handle_wrong_method(), either directly with a jump to a stub, or indirectly with an illegal instruction and the help of the signal handler.  The not_entrant state is a final state, so once an nmethod becomes not_entrant, it stays not_entrant.  We can do the same thing with a permanently armed nmethod entry barrier.
>> 
>> The solution I went with reserves one bit of the entry barrier guard value.  This bit must remain set, so I call it a "sticky" bit.  Setting the guard value now is effectively like setting a bitfield, so I needed to add a lock around it.  The alternative would be to change the platform-specific code to do compare-and-swap.
>> 
>> For the lock, I introduced a new NMethodEntryBarrier_lock, whose only purpose is to make the update to the guard value atomic.  For ZGC, I decided to use the existing per-nmethod lock ZNMethod::lock_for_nmethod().  I suspect we could do the same for Shenandoah, if needed for performance.
>> 
>> This change also makes it a bit clearer that  the nmethod entry barrier effectively has two levels.  Level 0 is the outer level or layer controlled by BarrierSetNMethod::nmethod_stub_entry_barrier(), and the inner layer controlled by BarrierSetNMethod::nmethod_entry_barrier().  This could be generalized if we decide we need more flavors of entry barriers.  The inner barrier is mostly ignorant of the fact that the outer guard is multiplexing for both levels.
>
> Dean Long has updated the pull request incrementally with one additional commit since the last revision:
> 
>   remove is_sigill_not_entrant

Thanks for doing this! I have been wanting something like this for a while too and it looks great. I have some comments though...

src/hotspot/share/gc/z/zBarrierSetNMethod.cpp line 109:

> 107: }
> 108: 
> 109: void ZBarrierSetNMethod::arm_with(nmethod* nm, int value) {

I don't usually comment on names, but could we call this guard_with instead? We tried to stop saying "arm" about things used also for disarming and we have (hopefully) been consistent about calling that "guard" instead.

src/hotspot/share/gc/z/zBarrierSetNMethod.cpp line 114:

> 112:   // Preserve the sticky bit
> 113:   if (is_not_entrant(nm)) {
> 114:     value |= not_entrant;

Is it possible to have a race where another thread sets an nmethod to not entrant and the thread calling this making the nmethod entry barrier not entrant?

If this was called to disarm a method and then enter it, it seems a bit sneaky in that case that we pass the nmethod entry barrier even though we under the lock see that it is not entrant. Probably okay but still feels like it might be more robust if the thread setting an nmethod to not entrant is always the one that arms the nmethod entry barrier.

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

PR Review: https://git.openjdk.org/jdk/pull/25764#pullrequestreview-2930412450
PR Review Comment: https://git.openjdk.org/jdk/pull/25764#discussion_r2148888954
PR Review Comment: https://git.openjdk.org/jdk/pull/25764#discussion_r2148891702


More information about the shenandoah-dev mailing list