Constant propagation & withfield updation.

Srikanth srikanth.adayapalam at oracle.com
Fri Mar 9 01:18:13 UTC 2018


All my questions stand addressed. Thanks John.

Srikanth

On Thursday 08 March 2018 11:27 AM, John Rose wrote:
> On Mar 7, 2018, at 9:07 PM, Srikanth <srikanth.adayapalam at oracle.com 
> <mailto:srikanth.adayapalam at oracle.com>> wrote:
>>
>>
>> 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."
>
> Good!
>
>> 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 
>>> <mailto: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.
>
> There's a twisty idea in there, which you caught:  Many objects have 
> non-final
> fields, and those can be updated.  The same design patterns sometimes 
> apply
> to values, despite the lack of identity:  You make a new value when 
> you need
> to update the field.  But often there's still the strong notion of the 
> new value being
> the "new version" of the previous value, as with the "i++" in a 
> for-loop:  The value
> of i is different in each iteration but the successive versions of i 
> are derived incrementally
> from the previous versions.  If "i" were an object it might retain its 
> identity and
> update its state, while if it is a value it must make a new version of 
> itself.
> For composite value types, "i++" patterns often update just part of 
> the value
> keeping other parts the same.  For example, if we were to value-ize the
> Iterator type, its "next" method would return two results, the next 
> collection
> item, and the new value of itself.  (With i++ and iterators, the second
> result is implicitly written back to a variable, either i or an 
> iterator state
> variable.  A value-ized iterator probably holds the present result and
> is ready to return its next version as the only result of next, but that's
> a detail.)
>
> Everything I just said about stateful object types can also be modeled
> with read-only value-based objects, which is yet another connection
> between values and objects.
>
> In short, both values and object are used to model incrementally
> updating state, in many of the same ways, although the details
> differ.  The root of the difference is the distinction between putfield
> and 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.
>
> It's a tricky argument, because it appeals to deep similarities between
> values and objects which sometimes don't look similar on the surface.
>
>>
>>
>>>   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.
>
> Yes.  I guess there are really six cases for finals, with different 
> compiler
> behaviors for init and read:
>
> static constant final  I=ConstantValue, R=ConstantValue
> static blank final  I=putstatic, R=getstatic
> other static final  I=putstatic, R=getstatic
>
> instance constant final  I=ConstantValue+putfield, R=ConstantValue
> instance blank final  I=putfield, R=getfield
> other instance final  I=putfield, R=getfield
>
> So the non-constant cases differ only in the surface syntax.
> Blank-ness is not so special, but constant-ness is special.
>
>> 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 see; I missed the point of your question; I thought you were asking 
> about
> non-blank finals in general, and I answered both
>
> Although I don't like the rules for ConstantValue on instance finals, 
> I suspect we
> can't change that in the long run, even for value types (though they 
> are new).
> OTOH we could decide to deprecate this behavior, if we wanted. I'm on the
> fence about that.
>
> In an object class, you almost always don't use "final" with a field 
> initializer,
> so you don't run into this corner case.  I've seen programmers 
> accidentally
> leave out the "static" modifier on a constant declaration, and it takes a
> while before they notice that the fields are taking up space in every 
> instance.
>
> In a value class, since we mandate "final" on everything, you can 
> easily fall
> into the same trap, of accidentally defining a ConstantValue attribute 
> on an
> instance field.
>
> The way out of the trap, of course, is to wean ourselves off of the 
> explicit
> "final" modifiers for value types; make the mandated behavior the default
> without the extra reinforcement of the "final" modifiers.  The same point
> goes for __Flattenable, of course, and for the "final" on the whole class.
> The ACC_FINAL bits on fields and class, and the ACC_FLATTENABLE
> bits on fields, should be automatically set under the hood, in the right
> places.
>
> For now it's less confusing to mandate the modifiers, and that makes
> us fall into the trap.  So here's my advice:  Don't respect ConstantValue
> attributes when reading value type fields.  That's your (b) proposal
> above.  At some point in the future we will decide whether to start
> respecting them again and back of from explicit "final", or take some
> other path.
>
> One reason to retract the explicit "final" modifier on fields is so we
> can assign a useful meaning to "final" if it really does occur explicitly,
> which would be rare.  It would mean something like "this value is set only
> in the constructor, and no other places (ignoring deserialization)".
> Fields not marked with explicit "final" would be settable in more places,
> just like in object classes, and just like we are experimenting with now.
>
> This is just an option; there might be other moves we'd prefer in order
> to tweak the access behavior of value type fields.  Here are the access
> behavior classes I'm thinking of:
>
> field is private-open:  nobody can touch it except the nest, which can 
> use getfield/withfield at will
> field is public-read:  anybody can use getfield; the nest can use 
> withfield/putfield anywhere
> field is public-open:  anybody can use a getfield or 
> withfield/putfield instruction on it
> field is private-constructed:  only official constructors can 
> withfield/putfield, only nest can getfield it
> field is public-constructed:  only official constructors can 
> withfield/putfield, anybody can getfield it
>
> In an object class today:
> public non-final => public-open
> public final => public-constructed
> private non-final => private-open
> private final => private-constructed
>
> In a value class today:
> public final => public-read
> private final => private-open
> public non-final => illegal or same as public final
> private non-final => illegal or same as private final
>
> In a value class tomorrow:
> public non-final => public-read
> private non-final => private-open
> public final => public-constructed
> private final => private-constructed
>
> This will align the behavior of value classes more closely with
> object classes, at the cost of removing the "training wheels"
> of all the extra final modifiers.
>
> The only non-alignment is that we have no proposal
> on the table for public-read in object classes and
> public-open in value classes.  The modifier combination
> "public non-final" describes the missing state for
> the opposite kind of class:
>
> class ObjClass {
>   public /*non-final*/ int x;  // public-open, not secure
>   public __PrivateWrite int x;  // public-read, more secure
> }
> __ByValue class ValClass {
>   public /*non-final*/ int x;  // public-read, very secure
>   public __PublicWrite int x;  // public-open, less secure
> }
>
> I don't know how to spell __PrivateWrite for objects
> and __PublicWrite for values.
>
>>
>> I hear you saying yes to both (a) and (b)
>
> Correct.
>
>>>
>>> (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.
>
> Correct.  Let's keep it that way, but reconsider when we refactor
> the default modifiers on fields.  If final fields become a special
> thing (say, for constructor-only initialization as with objects)
> then we can align the ConstantValue behavior between values
> and objects.
>
>>
>>>
>>> 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 ?
>
> Yes.
>
> — John



More information about the valhalla-dev mailing list