[lworld] RFR: 8361352: [lworld] Weird interaction between OSR and value class instances [v4]

duke duke at openjdk.org
Tue Aug 26 12:00:57 UTC 2025


On Tue, 26 Aug 2025 08:30:23 GMT, Marc Chevalier <mchevalier at openjdk.org> wrote:

>> First, credit to @TobiHartmann for the diagnostic, and a lot of the solution.
>> 
>> # Diagnostic
>> 
>> According to [Strict Field Initialization JEP](https://openjdk.org/jeps/8350458), when a strict field is being initialized, it is not quite immutable, but observally immutable: at first, the field can be only set (during the early larval phase), then it can be only read (late larval or initialized phase), so the last set is the actual value one can ever observe.
>> 
>> The interesting part is that in early larval phase, a field can be subject to some side effects. When applied to a value object, that means that until it reaches the unrestricted state, it is not yet immutable. While being not theoretically necessary, avoiding scalarization and keeping the value object behind a reference is a convenient way to make sure that side effects are correctly applied. This strategy means that we shouldn't scalarized before reaching the unrestricted state. Normally, in C2, finding out what is early larval or not is the job of bytecode parsing, but in OSR compilation, everything about the StartOSR is not parsed, and thus some objects are soundly assumed that they might be larval, when they actually aren't. In the reported example, that leads to drastic performance difference between OSR and non OSR compilation: the second one is able to eliminate allocations since it knows more precisely when the value object can be scalarized.
>> 
>> In the original example:
>> 
>> public value class MyNumber {
>>     private long d0;
>>     private MyNumber(long d0) { this.d0 = d0; }
>>     public MyNumber add(long v) { return new MyNumber(d0 + v); }
>> 
>>     private static void loop() {
>>         MyNumber dec = new MyNumber(123);
>>         for (int i = 0; i < 1_000_000_000; ++i) {
>>             dec = dec.add(i);
>>         }
>>     }
>> 
>>     public static void main(String[] args) {
>>         for (int i = 0; i < 10; ++i) {
>>             loop();
>>         }
>>     }
>> }
>> 
>> OSR happens in the loop in `loop`, but here `dec` is not detected to be unrestricted (so immutable, so scalarizable), so the allocation in inlined `add` still needs to happen because we need the buffer for the new `dec`. The first iteration traps at the exit of the loop (unstable if), OSR happens again, followed by a non-OSR compilation, finding correctly that `dec` can be scalarized in the loop, making the third iteration fast.
>> 
>> # Solution
>> 
>> Overall, the solution requires to improve our detection of early larval va...
>
> Marc Chevalier has updated the pull request incrementally with one additional commit since the last revision:
> 
>   Remove is_abstract condition

@marc-chevalier 
Your change (at version 0a6f0eb2d45e26e58af70e94aaf8189f83bc0de9) is now ready to be sponsored by a Committer.

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

PR Comment: https://git.openjdk.org/valhalla/pull/1531#issuecomment-3223882594


More information about the valhalla-dev mailing list