Constant propagation & withfield updation.
john.r.rose at oracle.com
Wed Mar 7 21:09:06 UTC 2018
On Mar 6, 2018, at 10:48 PM, Srikanth <srikanth.adayapalam at oracle.com> wrote:
> At the moment, javac allows updates to both blank final and non-blank final instance
> fields of a value class via the __WithField operator.
> I am trying to confirm that we want to allow updates to initialized non-blank final fields too. (Or not)
You mean should __WithField lower to withfield, regardless of whether
the field was initialized or not?
The short answer is "yes". An initialized field (final or not) is simply the
first version of the field value. Object classes allow fields to be updated,
and so should value classes. The differences in syntax (put vs. with)
are minor. The common thread is that fields are for both initializing
and updating. Value fields are marked final as a reminder that there is
no write-back from simple assignment (except in constructors), but
there is no reason to forbid updates. Updating a value makes a new value.
Regarding the blank vs. non-blank distinction: It is a shallow one, really
just sugar for replicating the non-blank initializer into every constructor.
Taste in sugar at this point doesn't affect updatability.
(A long answer takes us into the complex world of "reconstructors", which
deserves a separate discussion.)
> Such allowance interferes with constant propagation - I was searching for the text in JLS that says what the compiler must do when a final field is initialized with a compile time constant expression - I located only the slightly oblique reference in 17.5.3, which reads:
> "If a final field is initialized to a constant expression (§15.28) in the field declaration, changes to the final
> field may not be observed, since uses of that final field are replaced at compile time with the value of the
> constant expression."
The net effect of that provision is that fields with constant initializers
get ConstantValue attributes. This is true whether the fields are
static or instance. It is IMO an oversight that the ConstantValue
attribute applies to instance fields, since this provides no additional
power over constant static fields, and it tends to bloat the heap
with lots of copies of the same value.
The translation strategy for fields with ConstantValue is specialized,
but *only* for static fields. The language above does not put any
constraint on translation strategy for any instance field.
If it makes it easier, just drop ConstantValue attributes from instance
fields in value types. But I don't think it matters. You can ignore the
issue when you are *initializing* constant fields that are *non-static*.
When reading constant fields, the ConstantValue attribute helpfully
hands you a constant value at compile time. So what you wrote into
the field is irrelevant, except in separate compilation edge cases.
I'm talking about ConstantValue attributes, but there is another
sub-case of initialized final fields (static or instance), when the
initializer expression is not a compile-time constant. In that case,
the translation strategy is exactly the same as for non-final fields,
but the JVM will reject the code if you emit a putfield in the wrong
Does that help?
> This passage which occurs in the context of deserialization and reflection based updates to final fields will also be relevant for WithField updates.
(See P.S. on deserialization, below.)
> ATM, I have disabled such propagation of constants for value class's final instance fields initialized with constant expressions and reads of these fields result in fresh getfield instructions.
The basic rule for instance fields is, when writing ignore the
ConstantValue attribute and when reading ignore the field
value. For statics it's even simpler: The ConstantValue
attribute does everything.
> I don't yet know what all the side effects of disabling this propagation.
They are unimportant for now, but log a bug. The only time
you can observe the side effects is when you do inconsistent
recompilations that shift a ConstantValue attribute in or out
of a classfile.
> FWIW, in the orignal Valhalla prototype, withfield updates were permitted only for blank finals, so this was not an issue.
We were simplifying our life by avoiding the present analysis.
But the present analysis says there is no problem here, so
P.S. Deserialization of values is like deserialization of primitives.
The whole value needs to come in at once.
The hacky stuff that patches deserialized values into final
object fields needs a strong whack in order to make it work
for value fields. If I were working on it today, I might try to
make a temporary one-element array of the value type
to deserialize, and treat it like a tiny object in the deserializer.
Figure out what offsets to patch, patch them with incoming
values, etc. But that's not the whole story, since the deserializer
thinks it can immediately use references to a blank object,
before its fields are initialized, but for value types that's
impossible. The mini-array needs to be associated with
a set of indicators of where to store the value *when it
is complete*. Or the serialization format needs to be
completely changed for value types. Un-lovely problems.
More information about the valhalla-dev