On Tue, 28 Apr 2026 06:56:15 GMT, Marc Chevalier <[email protected]> wrote:

>> `acmp` for value object might be expensive: it may need substitutability 
>> test, which is done through a Java call. That is the last resort when 
>> everything failed, that is: we are in the presence of two value objects of 
>> the same class, not statically known or speculated (profile absent or 
>> polluted). While a lot of the acmp logic is trying to improve acmp at 
>> compile-time, this change proposes a runtime check to take a fast path.
>> 
>> The idea is that if the object is small enough, we can read all the fields 
>> at once with a single 8-byte load, we exclude the non-interesting bits with 
>> a mask, and we can simply compare the masked value from both objects.
>> 
>> The first step happens at class loading. When the class is loaded, we check 
>> whether all the fields can be read at once, from which offset, and the mask 
>> to filter out useless bits. Useless bits may have two sources:
>> - padding between fields
>> - header: this happens because we cannot necessarily start reading at 
>> `payload_offset`, the payload might be smaller than a long, and we could 
>> overread. On the other hand, the header is always long enough (never less 
>> than 8 bytes), so we may read from somewhere in the header to make sure we 
>> don't go further than the payload. This piece of header must be filtered out.
>> 
>> This strategy may not apply if the object is too big, or if it contains an 
>> oop. Loading oop without precaution, concurrently with the GC is not a good 
>> idea. This limitation is rather harmless: this fast path is mostly made not 
>> to pay the price of a complicated `acmp` for magically migrated classes 
>> (such as `Integer`), which are small and don't contain oops.
>> 
>> There is an interesting corner case: the mask can be 0 for an empty value 
>> class. We use a negative offset to signal that the fast path doesn't apply, 
>> since it is not a valid value for the offset: we shouldn't load before the 
>> object.
>> 
>> The second step is to prepare the runtime check. In `do_acmp`, when we used 
>> to prepare the Java call, now we have a parallel fast path. The condition 
>> uses the fact that we already have the class node available (used for 
>> checking operands have the same types). We can get the fast acmp offset from 
>> the class, check if it looks valid (that is non-negative), and if so, take 
>> the fast path that will load the mask, do the two loads and masking, and 
>> compare the given values. If the class of the operand is known precisely at 
>> parsing, we don't emit the fast path since the call will be nicely 
>> intrinsified.
>> 
>> The thir...
>
> Marc Chevalier has updated the pull request with a new target base due to a 
> merge or a rebase. The pull request now contains 31 commits:
> 
>  - Move the flag around
>  - Merge remote-tracking branch lworld into JDK-8381472
>  - Printing is gone again
>  - Remove the old way
>  - Fix asserts
>  - Let new version do the job
>  - Flag + using _layout_info
>  - Printing is back
>  - cosmetics
>  - Typo in acmp
>  - ... and 21 more: 
> https://git.openjdk.org/valhalla/compare/cfda30f3...88740943

src/hotspot/share/classfile/classFileParser.cpp line 5384:

> 5382:     int64_t mask_piece = make_mask_piece(field_start, field_size);
> 5383:     mask |= mask_piece;
> 5384:   }

A small description of the algorithm used to compute the mask would be welcome 
(may be with a little example). Some details, like the fact it is filled 
starting from the least significant bit is not obvious at first, and explains 
the need for the shift below.
I've checked it on paper with a few examples, but review would have been faster 
with a description.

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

PR Review Comment: 
https://git.openjdk.org/valhalla/pull/2353#discussion_r3156307635

Reply via email to