RFR: 8359359: AArch64: share trampolines between static calls to the same method [v3]

Evgeny Astigeevich eastigeevich at openjdk.org
Mon Aug 4 17:14:58 UTC 2025


On Fri, 1 Aug 2025 16:05:42 GMT, Mikhail Ablakatov <mablakatov at openjdk.org> wrote:

>> Modify the C2 compiler to share trampoline stubs between static calls that resolve to the same callee method. Since the relocation target for all static calls is initially set to the static call resolver stub, the call's target alone cannot be used to distinguish between different static method calls. Instead, trampoline stubs should be shared based on the actual callee.
>> 
>> The `SharedTrampolineTest.java` was designed to verify the sharing of trampolines among static calls. However, due to imprecise log analysis, the test currently passes even when trampolines are not shared. Additionally, comments within the test suggest ambiguity regarding whether it was intended to assess trampoline sharing for static calls or runtime calls. To address these issues and eliminate ambiguity, this patch renames and updates the existing test. Furthermore, a new test is introduced, using the existing one as a foundation, to accurately evaluate trampoline sharing for both static and runtime calls.
>> 
>> This has passed tier1-3 and jcstress testing on AArch64.
>
> Mikhail Ablakatov has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains five commits:
> 
>  - Merge branch 'master'
>  - Address review comments from @theRealAph and @eastig
>    - use relocInfo::relocType instead of TrampolineCallKind
>    - move rtype from keys to values; reduce the footprint of the patch
>  - Lift the vm.opt.TieredCompilation == null requirement from the tests
>  - Combine the two shared trampoline request hash tables
>  - 8359359: AArch64: share trampolines between static calls to the same method
>    
>    Modify the C2 compiler to share trampoline stubs between static calls
>    that resolve to the same callee method. Since the relocation target for
>    all static calls is initially set to the static call resolver stub, the
>    call's target alone cannot be used to distinguish between different
>    static method calls. Instead, trampoline stubs should be shared based
>    on the actual callee.
>    
>    The `SharedTrampolineTest.java` was designed to verify the sharing of
>    trampolines among static calls. However, due to imprecise log analysis,
>    the test currently passes even when trampolines are not shared.
>    Additionally, comments within the test suggest ambiguity regarding
>    whether it was intended to assess trampoline sharing for static calls
>    or runtime calls. To address these issues and eliminate ambiguity, this
>    patch renames and updates the existing test. Furthermore, a new test is
>    introduced, using the existing one as a foundation, to accurately
>    evaluate trampoline sharing for both static and runtime calls.

src/hotspot/share/asm/codeBuffer.hpp line 545:

> 543: 
> 544:   typedef Pair<relocInfo::relocType, LinkedListImpl<int>> Offsets;
> 545:   typedef ResizeableResourceHashtable<address, Offsets, AnyObj::C_HEAP, mtCompiler> SharedTrampolineRequests;

I think code can be simplified.
For example, let's a Java method `A` call a static Java method `B` twice and a static Java method `C` three times.
You will be having the following in `SharedTrampilneRequests`:

key -> value
{B} -> {relocInfo::static_call_type, {call_B_offset1, call_B_offset2}}
{C} -> {relocInfo::static_call_type, {call_C_offset1, call_C_offset2, call_C_offset3}}


When you iterate trampoline requests, you don't use the key for a destination if it is for a static call. Instead you use `SharedRuntime::get_resolve_static_call_stub()` as a destination. 

This means your requests don't differ from requests for `runtime_call_type` trampolines. `runtime_call_type` trampolines have final destinations but `static_call_type` have a resolver as a destination.

So you actually have:

key -> value
(trampoline_id1} -> {get_resolve_static_call_stub, {call_B_offset1, call_B_offset2}}
{trampoline_id2} -> {get_resolve_static_call_stub, {call_C_offset1, call_C_offset2, call_C_offset3}}
{trampoilen_id3} -> {some_runtime_call, {call_RT_offset1, call_RT_offset2, call_RT_offset3}}


IMO based on `SharedRuntime::resolve_helper`,  we can have shared trampolines for `relocInfo::opt_virtual_call_type` and `relocInfo::virtual_call_type`. Their trampolines have the corresponding resolver set. I don't see we write anything specific to a call site into a trampoline.

We can have the following:

class SharedTrampolineRequest {
   LinkedListImpl<int> _offsets;
   address _dest;
#ifndef PRODUCT
   relocInfo::relocType; // This can be used in asserts to check a shared trampoline is for the same relocType
#endif
};

// A set of requests for shared trampolines.
// We use a hash table to implement the set.
// A trampoline id is a key in the table.
// We don't store an id in SharedTrampolineRequest because it is only used for the table.
typedef uintptr_t SharedTrampolineId;
typedef ResizeableResourceHashtable<SharedTrampolineId, SharedTrampolineRequest,
     AnyObj::C_HEAP, mtCompiler> SharedTrampolineRequests;


You will need to modify `MacroAssembler::trampoline_call`:


SharedTrampolineId tramp_id = (entry.rspec().type() == relocInfo::runtime_call_type) ? target : callee;
code()->share_trampoline_for(tramp_id, entry.target(), offset());


New code for `share_trampoline_for(NOT_PRODUCT_ARG(relocInfo::relocType rel_type) tramp_id, target, offset )`:

bool created;
SharedTrampolineRequest tramp_req = _shared_trampoline_requests->put_if_absent(tramp_id, &created);
  if (created) {
    _shared_trampoline_requests->maybe_grow();
    tramp_req->set_target(target);
    NOT_PRODUCT(tramp_req->set_reloc_type(rel_type));
  }
  assert(tramp_req->reloc_type() == rel_type);
  tramp_req->offsets()->add(offset);
  _finalize_stubs = true;

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

PR Review Comment: https://git.openjdk.org/jdk/pull/25954#discussion_r2252117186


More information about the hotspot-dev mailing list