Target of opportunity: remove method <vnew> and aconst_init / withfield opcodes

forax at univ-mlv.fr forax at univ-mlv.fr
Mon Aug 14 14:54:10 UTC 2023


> From: "Dan Heidinga" <heidinga at redhat.com>
> To: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "John Rose" <john.r.rose at oracle.com>, "valhalla-spec-experts"
> <valhalla-spec-experts at openjdk.java.net>
> Sent: Monday, August 14, 2023 4:35:31 PM
> Subject: Re: Target of opportunity: remove method <vnew> and aconst_init /
> withfield opcodes

> On Mon, Aug 14, 2023 at 9:55 AM < [ mailto:forax at univ-mlv.fr | forax at univ-mlv.fr
> ] > wrote:

>>> From: "Dan Heidinga" < [ mailto:heidinga at redhat.com | heidinga at redhat.com ] >
>>> To: "John Rose" < [ mailto:john.r.rose at oracle.com | john.r.rose at oracle.com ] >
>>> Cc: "Remi Forax" < [ mailto:forax at univ-mlv.fr | forax at univ-mlv.fr ] >,
>>> "valhalla-spec-experts" < [ mailto:valhalla-spec-experts at openjdk.java.net |
>>> valhalla-spec-experts at openjdk.java.net ] >
>>> Sent: Monday, August 14, 2023 2:53:31 PM
>>> Subject: Re: Target of opportunity: remove method <vnew> and aconst_init /
>>> withfield opcodes

>>> Can one of you sketch out the bytecode sequences being proposed? Being able to
>>> look at some concrete "before we used <vnew>/aconst_init/withfield" and now "we
>>> use ....." comparisons would make the proposal clearer.
>>> For a value class like Point, with a "Point changeX(int x)" method, we used
>>> withfield to pop an instance off the stack, create a new instance with the
>>> updated single field, and put that instance back on the stack which allows us
>>> to preserve our immutability without needing a constructor per field we want to
>>> change.

>>> Old bytecode:
>>> Point changeX(int x) {
>>> aload0
>>> iload_1
>>> withfield "Point:x"
>>> areturn
>>> }

>>> What would the new bytecode do?

>> Sure, for the content of the constructor of a value class the bytecode is almost
>> the same as for an identity class, the only difference is that the call to
>> super() is not emitted.

>> The constructor of Point
>> <init>(int x, int y) {
>> aload 0
>> iload 1
>> putfield Field:x
>> aload 0
>> iload 2
>> putfield Field:y
>> return
>> } // here, 'this' is not larval anymore

>> For all the other methods, a value class or an identity class use the same
>> bytecode, for the method Point:changeX
>> Point changeX(int x) {
>> new. // in larval state
>> dup
>> iload 1
>> aload 0
>> getfield Field:y
>> invokespecial Method Point:<init>(II)V
>> areturn // not larval anymore
>> }

> If I'm following correctly, changing one field requires stacking all the fields
> and calling the constructor?

yes, 

> From a VM perspective withfield is nice but as I dig through my notes, I don't
> see any notes on how we'd expose it in the language. All the examples I'm
> finding are either directly generating the withfield bytecode or java source
> that shows the fields passing through a constructor. Do we have a model on how
> javac would generate withfield or have we already defaulted in the ctor model?

Brian has proposed a syntax for records (as part of Amber) in the past 
[ https://github.com/openjdk/amber-docs/blob/master/eg-drafts/reconstruction-records-and-classes.md | https://github.com/openjdk/amber-docs/blob/master/eg-drafts/reconstruction-records-and-classes.md ] 
but there were no concensus about it. 

And the mapping for the compiler is not obvious given that withfield is limited to the nestmates of a value type. 

Currently the compiler only emits withfield (or aconst_init) inside a <vnew> method. 

> --Dan

Rémi 

>>> --Dan

>> Rémi

>>> On Sat, Aug 12, 2023 at 6:49 PM John Rose < [ mailto:john.r.rose at oracle.com |
>>> john.r.rose at oracle.com ] > wrote:

>>>> The header bit is not a problem because value object headers have no object
>>>> monitor. Thus, their headers are relatively empty OC state. Lots of slack.

>>>> In the spirit of minimizing JVMS changes I think this approa), might make sense.
>>>> The new byte codes paired well with new Q-descriptors and new verifier rules.
>>>> Not so much now.

>>>> Also, we already built the necessary states for serialization. The verifier will
>>>> also hide them for us in bytecode. That was a new discovery yesterday over
>>>> burgers.

>>>>> On Aug 12, 2023, at 1:40 PM, Remi Forax < [ mailto:forax at univ-mlv.fr |
>>>> > forax at univ-mlv.fr ] > wrote:

>>>>> John and I and several others had several interresting discussions yesterday
>>>> > about Valhalla,
>>>>> one of them about the removal of <vnew> and aconst_init/withfield, something
>>>> > i've called in the past, solving the last mile issue.

>>>>> Currently, refactoring from/to a value class/identity class is not a backward
>>>>> compatible move because of the way value classes are initialized using the
>>>>> <vnew> factory method. The reason is that during the initialization, a class
>>>>> instance is mutable but the VM considers that all value class instances are
>>>> > non-mutable.

>>>>> But at the same time, in order to implement Serialization of value classes
>>>>> (exactly de-serialization), there is a need for a mechanism to tag value class
>>>>> instances as "not yet finished to be initialized", something John refers has
>>>>> the object being in larval state because the de-serialization first create the
>>>>> instance and then populate its fields. In the lw prototypes, this is currently
>>>> > implemented using Unsafe.

>>>>> I think we have the opportunity now to use the same larval protocol for all
>>>>> values classes, making them binary backward compatible with identity classes.
>>>>> With both of them using the initialization protocol, the same
>>>> > new/dup/invokespecial dance.

>>>>> In term of specification, the idea is that "new" on a value class creates a
>>>>> larval instance and the end of the constructor mark the instance as
>>>> > non-larval/true-value-instance.

>>>>> I think that using the same initialization protocol at callsite is a good idea.
>>>>> We are re-aligning the bytecode with the Java code, One thing wichh is
>>>>> currently hard to understand for our users is that currently the Java code is
>>>>> the same for a value class and an identity class but the generated bytecode is
>>>>> not binary compatible. This also go well with the recent move to remove the
>>>>> Q-descriptor, we are moving toward the goal of being fully binary backward
>>>> > compatible.

>>>>> We may want to modify the verifier to verify that "this" does not escape the
>>>>> constructor in case of a value class, but I do not thing it's a requirement, so
>>>> > it's maybe better to not do that :)

>>>>> The end of constructor is already a point where all VMs/JITs are able to emit
>>>>> codes (for store/store barrier or finalizer registration), so we are piggy
>>>>> backing on an existing concept. The only main drawback I see is that the header
>>>>> of a buffered value type has to have one bit to indicate the larval state and
>>>> > those header bits are really precious.

>>>> > regards,
>>>> > Rémi
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/valhalla-spec-experts/attachments/20230814/6bf79809/attachment-0001.htm>


More information about the valhalla-spec-experts mailing list