The last miles
John Rose
john.r.rose at oracle.com
Tue Aug 22 19:29:21 UTC 2023
On 22 Aug 2023, at 11:50, Dan Heidinga wrote:
…
>>>> 4) I'm not sure the prohibition on 'super' calls is actually necessary.
>>>
>>> No, but it’s a move of economy. Defining the meaning of super for
>>> values would be extra work. We could do that; I’d prefer not to.
>>> Remember that super-constructors for values are already very special
>>> animals: They must be empty in a special sense. Forbidding calls to
>>> them seems like the clean move.
>>
>> The rule is that super constructors must be empty because we had no
>> concept of mutable state to communicate changes from parent to child. But
>> now that we have larval objects...
>>
>> Concretely, what if:
>>
>> - putfield is a verifier error on non-identity class types, it only works
>> on uninitializedThis
>> - as usual, every <init> method (for all kinds of classes) must do a
>> super-<init> invokespecial (or this-<init>? still thinking about that)
>>
>> Then:
>>
>> - value objects get built bottom-to-top, with fields set before a super()
>> call, and freedom to use 'this' afterwards
>> - abstract classes can participate too, following the same code shape
>> - identity classes (abstract and concrete) have a little more freedom,
>> because they can follow the same pattern *or* set their fields after the
>> super() call
>>
>
> Flattened values can't support layout polymorphism which means all uses of
> a common abstract class will need to rely on pointer polymorphism (this was
> already true before this change).
That’s true, except in the case of inlining, which often removes the
need for pointer polymorphism. In this case, the interpreter needs a
real physical buffer on the heap (or stack, maybe) for the larval value
so that it can be passed to both Bottom.<init> and then Top.<init>.
(Here final Bottom <: abstract Top.) But the JIT can inline
Top.<init> and Bottom.<init> into the same place, and then dissolve
the buffer into scalar components.
I am very confident that inlining policy tweaks can fix this in
nearly all cases, reducing the problem of heap-based larvae,
down into the noise. And/or adjusting the calling sequence
of <init> can do so as well. The constructor Top.<init> could
be adjusted to return the state for just the super fields.
…This stuff about inlining or special handling of Top.<init>
is assuming we adopt a new feature, not planned for Valhalla,
which is non-empty super constructors for values, as required
by abstract supers of values which would define their own fields.
I have always thought fields in abstract supers of values is a
very reasonable ask, as a future feature, but I also think we
should not delay Valhalla to do it.
> If abstract classes which are super
> classes of value classes can now have fields, does that encourage
> developers to adopt patterns which rely on pointer polymorphism and
> forfeit flattening? The concern here is less about can we do this but more
> about should we do it?
I think we should consider this feature for a future release of Valhalla.
There is nothing special about this feature that would make it defeat
the VM in its usual job of optimizing whatever the user throws at it.
As usual inlining will probably be sufficient, and if not there are
other tricks we can play.
> Does this hold together with our story on implicit constructors (generate
> both an implicit_creation attribute and a method in the class)?
Probably, but that’s a question Dan can answer more surely than me.
Again, the interpreter is not my biggest performance concern, but it
is worth noting that the current <vnew>-based design has the interpreter
creating one new buffered value per withfield operation (which is
per non-static field in the class), while the proposed <init>-based
design requires one new (larval) buffered value per instance. That
means the interpreter (which I don’t really care about) will create
no fewer buffered values in the old scheme, as long as there is at
least one field in the value class. That’s a (small) win for the
<init>-based scheme. And for the more important case of the JIT,
which has its own IR for value types unrelated to the interpreter,
a calling sequence adjustment (which we already do in other cases!)
will make the buffer disappear completely.
More information about the valhalla-spec-observers
mailing list