[lworld] RFR: 8352647: [lworld] Remove larval InlineTypeNode in Unsafe intrinsics

Jatin Bhateja jbhateja at openjdk.org
Sat Mar 29 22:45:06 UTC 2025


On Sat, 29 Mar 2025 08:16:25 GMT, Jatin Bhateja <jbhateja at openjdk.org> wrote:

>>> @jatin-bhateja Merging a larval object and a non-larval object is undefined behaviour, and crashing should be an acceptable outcome then. There are issues with merges of larval objects, though, for example:
>>> 
>>> ```
>>> MyValue v = UNSAFE.makePrivateBuffer(v0);
>>> for (int i = 0; i < 100; i++) {}
>>> return UNSAFE.finishPrivateBuffer(v);
>>> ```
>>> 
>>> But generally, the situation of merging larval objects are messy not just with larval objects created by `Unsafe` and I want to defer the fix to after #1405.
>> 
>> Thanks, @merykitty. I think MEET over larval and non-larval should result in a larval value, so InlineTypeNode, which is the other input of the Phi node, should be buffered and treated as a larval object too, until we hit upon a finishPrivate buffer call that brings this merged oop value out of the larval state and re-materialize an InlineTypeNode. 
>> 
>> Kindly include this test with this patch. for now, we can add it to ProblemList.txt, which can be enabled after #1405 .
>
>> @jatin-bhateja A larval object created by `Unsafe` should adhere to all the rules that are put on a larval object created by the `new` bytecode. In the type verification system, each larval object has a distinct type being either `uninitializedThis` or `uninitialized(Offset)`. Meeting of a larval object with another larval object or with a non-larval object, thus, will result in a non-typed `reference`. As a result, the program is ill-formed if the merge is used. Please refer to [the JVMS, section 4.10.1](https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-4.html#jvms-4.10.1).
> 
> Hi @merykitty ,
> Larval transitions are either implicit, i.e. when a new value type instance is created, it begins in larval state and then post initilization transition out of larval state. The second kind of larval transition is explicit when we use UNSAFE APIs.
> 
> While an object is in the larval state, it should not exist in scalarized form, thus, a larval object should be buffered; it still is a legitimate value instance, but because it is assigned a buffer location, we can say for now it has a pseudo-identity. The compiler cannot directly forward the field initialization of larvals to its user; a read of a field value of a larval object is similar to getfield, i.e., the compiler needs to explicitly load the field value at the corresponding field offset from the base.
> 
> Given these semantics, a MEET b/w a larval and non-larval value should result in a laval value when merging the states of two basic blocks. I don't fully follow your comment on non-typed reference, as larval and non-larval instances are of value type.
> I think your approach to buffer the value type instance within the makePrivate buffer looks right to me. Please include the test case that I shared with the patch and add it to ProblemList.txt for now.

> @jatin-bhateja Note that for the verifier, `uninitialized` and `MyValue` are completely different types that have their closest common ancestor being `reference` ([section 4.10.1.2](https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-4.html#jvms-4.10.1.2)).
> 
> When a `new MyValue` instruction is executed, it pushes onto the stack a value of type `uninitialized(Offset)`, not a value of type `MyValue` ([section 4.10.1.9.new](https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-4.html#jvms-4.10.1.9.new)). The only action that can be done with a value of this type is to pass it into a constructor, after which all instances of type `uninitialized(Offset)` on the stack are replaced with the a value of type `MyValue` ([section 4.10.1.9.invokespecial](https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-4.html#jvms-4.10.1.9.invokespecial)).
> 
> Inside the constructor `MyValue::<init>`, the first parameter has its type being `uninitializedThis` ([section 4.10.1.6](https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-4.html#jvms-4.10.1.6)). There is nothing that can be done with a value of this type except passing it to a constructor of the superclass or executing `putfield` instructions. This value will be replaced by a value of type `MyValue` after the execution of the constructor of the superclass.
> 
> `Unsafe` lets you bypass the restriction that final fields can only be set inside the constructor of its holder class. To achieve this, it lies to the bytecode verifier that you are having a value of type `MyValue` when in reality the returned value of `Unsafe::makePrivateBuffer` is a value of type `uninitialized`. However, you still must adhere to the rules that are put on the values of type `uninitialized`, such as:
> 
> * You cannot pass a larval object to any method except to `Unsafe::putXXX` or `Unsafe::finishPrivateBuffer`, which are designed to allow working with larval objects.
> * You cannot load from a field of a larval object.
> * Meeting of an `uninitialized` with a `MyValue` results in a value of type `reference`, which you cannot possibly work with. As a result, you cannot assign to a non-larval local variable with a larval value.

Hi @merykitty ,

>> * Meeting of an `uninitialized` with a `MyValue` results in a value of type `reference`, "

Yes, I agree,  this should be seen  on the lines of a primitive (value) meeting an identity object (boxes),  

https://openjdk.org/projects/valhalla/design-notes/state-of-valhalla/01-background#:~:text=Java%E2%80%99s%20eight%20built%2Din%20primitive%20types%20are%20not%20objects%2C%20but%20they%20have%20boxes.%20When%20primitives%20want%20to%20interact%20in%20the%20world%20of%20objects%2C%20we%20transparently%20convert%20them%20to%20and%20from%20their%20corresponding%20box

https://openjdk.org/projects/valhalla/design-notes/state-of-valhalla/01-background#:~:text=Objects%20have%20identity%2C%20whereas%20primitives%20do%20not%3B%20boxing%20is%20not%20able%20to%20fully%20paper%20over%20this%20gap.%20Each%20time%20we%20convert%20from%20Integer%20to%20int%20the%20identity%20is%20lost%2C%20and%20each%20time%20we%20convert%20from%20int%20to%20an%20Integer%2C%20a%20fresh%20(but%20accidental)%20identity%20is%20created

Laraval value carries an identity while value (MyValue) does not,  larval value still adheres to semantic restrictions of value type, i.e should not be used for synchronization, non-tearable etc.

Thus, here at merge point, MyValue should be buffered, since this is MEETing a larval value so new oop (Phi) should be larval, similar to following
![image](https://github.com/user-attachments/assets/27bce951-353f-4492-8cda-605fa1b0c89c)

>> "Merging a larval object and a non-larval object is undefined behaviour, and crashing should be an acceptable outcome then. "

Do you think we should get the semantics documented in the specification first or let the spec evolve from the implementation you choose?

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

PR Comment: https://git.openjdk.org/valhalla/pull/1406#issuecomment-2764270053


More information about the valhalla-dev mailing list