JDK-8251324 : derive sharper types for getfield values based on immutability and constructor actions
  • Type: Enhancement
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: repo-valhalla
  • Priority: P3
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2020-08-09
  • Updated: 2022-09-05
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
repo-valhallaUnresolved
Related Reports
Relates :  
Description
Normally, the value produced by a getfield instruction cannot be more sharply typed than the type guaranteed by the JVM, i.e., type denoted by the descriptor of the field.

In some cases, however, the action of an object's constructor can modeled in such a way that that type of the value stored in a field can be sharpened.  For example, a reference field can be proven non-null (on immediate exit from a constructor) if the constructor provably stores a non-null value to the field, either by building a non-null value or calling Objects.requireNonNull.  A more subtle example is that an array field value might always be initialized to an array of some definite length.

This RFE proposes that we revive some prototyping work done years ago by C2 engineers to model the type-flow effects of constructors on final fields, and use the resulting information to sharpen the types of getfield values loaded from such fields.

This work does not apply to mutable fields, since a constructor does not have the final say on the value of a mutable field.  It might be extended to mutable fields, if we extended the analysis to all putfield instructions everywhere in a class, but great care would be required to ensure that reflective updates to fields either would not violate the sharpened types, or that code compiled under the assumption of sharpened types could be deoptimized after a violating reflective store.

Even final fields (before Valhalla) can be reflectively modified (although this is rare outside deserialization frameworks), so similar reflective guards are required for both final and non-final fields.

But this work is more profitable for Valhalla than for the standard JDK.  The final field in an inline type is truly immutable, so we can expect that reflective updates (and/or updates via Unsafe mechanisms) will be rarer and (perhaps) easier to exclude or monitor.

Also, this work is easier to accomplish with record types, since (a) their fields are properly immutable, and (b) all object construction goes through a single canonical constructor.

(Earlier prototyping work found that in a class with multiple constructors, there was some benefit to tracking field type-state through each constructor independently, and then keeping track, for each value of a given class, which constructors might have been used to create that value.  The resulting getfield types are the union/join of of getfield types producible by any constructor which might have built the instance in question, for a given getfield.  This analysis becomes simpler when all construction goes through a single canonical constructor, as with records.)

The type sharpening can apply to fields of any completely-constructed object.  It would have to be disabled for incomplete objects.  This means (a) during the optimization of a getfield in an object whose constructor is or might be on the stack, or (b) at any point, if an object's constructor unsafely publishes the object.  Excluding (b) makes it easier to enforce (a), with an observation that, if a constructor call is on the stack, it is in the current JIT compilation unit (given certain rule enforcements).

Suggestion:  The first time a class is reflectively updated, make a crude type-flow pass through its constructor(s) and derive sharper types (if possible) for the fields.  If this produces a win, compile a guard function to dynamically re-validate an instance's sharpened field values (e.g., null checks and array length verification, per examples above).  Install this guard function and run it after every reflective update; it's not needed for regular code.  Make an API for validation for use by Unsafe APIs, as needed.  A failed validation will un-install any code that relies on the sharpened types, and also removes the validation function; falling back to the weakened types.

Comments
Hotspot sharpens types by using both static analysis (C2-time or load-time) and also dynamic measurement (profiles of various sorts). For Valhalla, field sharpening can be done by the profiler as well. Before Valhalla, identity-based are profiled on the `oop::_klass` pointer of receivers, arguments, and return type, and this is critical to performance of polymorphic types (especially bimorphic ones). One of the crucial limitations of Valhalla is no polymorphism for value classes. This means that when a polymorphic identity class is refactored to a Valhalla value class, the polymorphism must be refactored, so that polymorphic behavior is obtained by consulting a (constant) field value in the value object; that field could be an int-code, an enum, or a polymorphic behavior delegate object. In order to preserve performance after such desirable refactorings, we have an additional need for field type sharpening (of those ints, enums, or poly-delegates). Profiling those fields, instead of the `oop::_klass` field (which for value classes is now invariant!) will preserve performance in the face of the desirable refactorings. We have run into this already with experimental rewrites of low-level high-performance Panama types as Valhalla value classes. Viewed another way, field profiling of value classes is simply argument and return profiling under another name, since value classes are advertised to "scalarize", which means to break into coordinated groups of field values on call and return. The net ask here is, our argument and return profiling mechanisms need to apply to those scalar values in order for Panama to integrate with Valhalla.
05-09-2022

The final field in a record type and that in a hidden class are truly immutable since it's specified that they have no write access via reflection [1]. [1] https://download.java.net/java/early_access/jdk16/docs/api/java.base/java/lang/reflect/Field.html#setAccessible(boolean)
10-08-2020