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