Constant propagation & withfield updation.

Srikanth srikanth.adayapalam at oracle.com
Thu Mar 8 05:07:30 UTC 2018


Thanks for weighing in John. I would say lworld branch tip behavior 
matches what you describe as the required behavior.

Or better phrased, "lworld branch tip behavior matches *what I think* 
you describe as the required behavior."

:)

Three of your assertions below do trigger some uncertainty in my mind, 
so let me expressly re-ask rather than assuming/pretending that I fully 
understand you.

See below:

On Thursday 08 March 2018 02:39 AM, John Rose wrote:
> 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.

(1) So when you say "Object classes allow fields to be updated", are you 
including
mechanisms such as deserialization and reflection ? Using purely 
linguistic means
an Object class cannot update a non-blank final (i.e initialized) 
instance field and hence
my original question as to whether a value class should be allowed to 
update a non-blank final
(i.e initialized final) instance field via withfield.

I read your answer as an unambiguous yes - and that matches branch tip 
behavior -  but the sentence "Object classes allow fields to be 
updated,and so should value classes." reads distracting in the current 
context as an argument in favor.


>    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.

(2) One further distinction is in how javac generates code for reads of 
non blank final instance fields initialized with compile time constant 
expressions. For a blank final or non-final fields, there would always 
be a getfield while for non blank final instance fields initialized with 
compile time constant expressions, the compiler would directly push the 
constant value onto the operand stack.

This was the central point of my original question: that what we really 
want is for the javac compiler to (a) allow updates to initialized final 
fields via __WithField and (b) for the updated value to be observed, 
always issue a getfield and ignore the constant initializer

I hear you saying yes to both (a) and (b)
>
> (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.

(3) But it is unhelpful to consult the ConstantValue attribute for value
class fields - otherwise any updates won't be observed, no ? Hence the
tip behavior to always issue getField for reads.

>
> 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
> place.
>
> 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.

(4) I think you mean, allow updates via withfield, but when reading 
ignore the ConstantValue ?

Thanks!
Srikanth
>
>> 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
> please proceed.
>
> — John
>
> 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 mailing list