RFR: 8365047: Remove exception handler stub code in C2 [v7]

Andrew Dinn adinn at openjdk.org
Mon Oct 20 09:27:16 UTC 2025


On Wed, 15 Oct 2025 14:00:32 GMT, Martin Doerr <mdoerr at openjdk.org> wrote:

>> Ruben has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains 10 commits:
>> 
>>  - Merge from the main branch
>>  - Address review comments
>>  - Address review comments
>>  - Address review comments
>>  - The patch is contributed by @TheRealMDoerr
>>  - Offset the deoptimization handler entry point
>>    
>>    Change-Id: I596317ec6a364b341e4642636fa5cf08f87ed722
>>  - Revert "Ensure stub code is not adjacent to a call"
>>  - Ensure stub code is not adjacent to a call
>>  - Address review comments
>>  - 8365047: Remove exception handler stub code in C2
>>    
>>    The C2 exception handler stub code is only a trampoline to the
>>    generated exception handler blob. This change removes the extra
>>    step on the way to the generated blob.
>>    
>>    According to some comments in the source code, the exception handler
>>    stub code used to be patched upon deoptimization, however presumably
>>    these comments are outdated as the patching upon deoptimization happens
>>    for post-call NOPs only.
>
> One probably related issue on linuxaarch64 while testing "gc/epsilon/TestObjects.java":
> SIGSEGV in caller_is_deopted:
> 
> 
> Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
> V  [libjvm.so+0x5531b8]  caller_is_deopted(JavaThread*)+0x2f8  (nativeInst_aarch64.hpp:536)
> V  [libjvm.so+0x555000]  Runtime1::move_mirror_patching(JavaThread*)+0x20  (c1_Runtime1.cpp:1400)
> v  ~RuntimeStub::C1 Runtime load_mirror_patching_blob 0x0000f7cf038f316c
> 
> 
> 
> siginfo: si_signo: 11 (SIGSEGV), si_code: 2 (SEGV_ACCERR), si_addr: 0x0000f7cf03c60000
> 
> 
> That address is at the end of the stub section:
> 
> 
> R0 =0x00000000f001cee8 is an oop: java.util.HashMap 
> {0x00000000f001cee8} - klass: 'java/util/HashMap' - flags: is_cloneable_fast 
>  - ---- fields (total size 5 words):
>  - transient 'keySet' 'Ljava/util/Set;' @8  null (0x00000000)
>  - transient 'values' 'Ljava/util/Collection;' @12  null (0x00000000)
>  - transient 'table' '[Ljava/util/HashMap$Node;' @16  a 'java/util/HashMap$Node'[128] {0x00000000f001cf10} (0xf001cf10)
>  - transient 'entrySet' 'Ljava/util/Set;' @20  null (0x00000000)
>  - transient 'size' 'I' @24  60 (0x0000003c)
>  - transient 'modCount' 'I' @28  60 (0x0000003c)
>  - 'threshold' 'I' @32  96 (0x00000060)
>  - final 'loadFactor' 'F' @36  0.750000 (0x3f400000)
> R1 =0x0000001fd503201f is an unknown value
> R2 =0x0000f7cf03c5fffc is at entry_point+276 in (nmethod*)0x0000f7cf03c5fdc8
> Compiled method (c1) 5966 2946       1       java.util.HashMap::values (25 bytes)
>  total in heap  [0x0000f7cf03c5fdc8,0x0000f7cf03c60000] = 568
>  main code      [0x0000f7cf03c5fec0,0x0000f7cf03c5ffd0] = 272
>  stub code      [0x0000f7cf03c5ffd0,0x0000f7cf03c60000] = 48
>  mutable data [0x0000f7ced888a580,0x0000f7ced888a5c0] = 64
>  relocation     [0x0000f7ced888a580,0x0000f7ced888a5b0] = 48
>  metadata       [0x0000f7ced888a5b0,0x0000f7ced888a5c0] = 16
>  immutable data [0x0000f7ced888a500,0x0000f7ced888a578] = 120
>  dependencies   [0x0000f7ced888a500,0x0000f7ced888a508] = 8
>  scopes pcs     [0x0000f7ced888a508,0x0000f7ced888a558] = 80
>  scopes data    [0x0000f7ced888a558,0x0000f7ced888a570] = 24
>  speculations   [0x0000f7ced888a570,0x0000f7ced888a574] = 4
> 0x0000f7cf03c5fff8:   62 74 ef 17 ff ff ff 17 
> --------------------------------------------------------------------------------
>   0x0000f7cf03c5fff8:   b	0x0000f7cf0383d180
>   0x0000f7cf03c5fffc:   b	0x0000f7cf03c5fff8
> --------------------------------------------------------------------------------
> R3 =0x0000f7cf03817578 points into unknown readable ...

@TheRealMDoerr

I tried reproducing this on linuxaarch64 using my M2 Mac Studio/fedora and could not get it to reproduce with either DEBUG or RELEASE build.

There is something rather weird going here. The code at the offending address looks like this:

    0x0000f7cf03c5fff8:   b	0x0000f7cf0383d180
    0x0000f7cf03c5fffc:   b	0x0000f7cf03c5fff8
    0x0000f7cf03c60000:   <off the end of the nmethod>

That definitely looks like it is the new backward branch code for the deopt stub leading up to the nmethod tail. However, the generator code for the stub in c1_LIRAssembler is this:

    __ bind(start);
    __ far_jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack()));
    int entry_offset = __ offset();
    __ b(start);

Method `far_call` should have planted a `bkr` or `bl` not a `b`:

    void MacroAssembler::far_call(Address entry, Register tmp) {
      ...
      if (target_needs_far_branch(entry.target())) {
        uint64_t offset;
        // We can use ADRP here because we know that the total size of
        // the code cache cannot exceed 2Gb (ADRP limit is 4GB).
        adrp(tmp, entry, offset);
        add(tmp, tmp, offset);
        blr(tmp);
      } else {
        bl(entry);
      }

So, I don't understand how your test ended up with these two successive `b` instructions in the nmethod stub.

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

PR Comment: https://git.openjdk.org/jdk/pull/26678#issuecomment-3421206833


More information about the serviceability-dev mailing list