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