JDK-8266524 : javac changes for JEP 306
  • Type: CSR
  • Component: tools
  • Sub-Component: javac
  • Priority: P4
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 17
  • Submitted: 2021-05-04
  • Updated: 2023-01-09
  • Resolved: 2021-05-28
Related Reports
CSR :  
CSR :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
Summary
-------

Change the language and virtual machine to only support a single, strict, set of semantics for floating-point expressions.

Problem
-------

Since Java SE 1.2, the language and VM have allowed two variants of floating-point semantics:

 - "strict" semantics: marked with `strictfp` in the language, `ACC_STRICT` in the VM  
 - non-strict semantics: the default

The non-strict semantics allow, in some circumstances, extra exponent range to be used for intermediate `float` and `double` values. This flexibility allowed faster code execution for certain popular processors common circa the Java SE 1.2. However, the processor family in question no longer benefits from this spec laxity and the existence of two subtly different variants of floating-point semantics causes on-going costs in maintenance and in developing new platform features.

Solution
--------

Require all floating-point to be done strictly under a single set of semantics.

Specification
-------------

For `javac`, a new lint option `strictfp` whose usage text is:

`Warn about unnecessary use of the strictfp modifier.`

For the detailed JLS and JVMS changes, see the attached documents. In summary, in the JLS the notions of *value set* and *value set conversion* are removed from the specification. The language-level checks about combinations of other modifiers with `strictfp` remain. Otherwise, for the purposes of floating-point semantics, `strictfp` is a no-op. Many small edits in chapter 15, Expressions, are needed to implement this change. In the JVMS, value set conversion, *FP-strict mode*, and *not FP-strict mode* are removed as concepts. The `ACC_STRICT` method modifier is undefined for class file with major version 61 and above. Therefore the VM checks for `ACC_STRICT` and `ACC_ABSTRACT` methods do not apply for class file with major version 61 and above. The check still occurs for class file major versions 46 through 60. This is a deliberate design difference between the handling of "strict abstract" methods in the language the VM. Chapter 6 of the JVMS, The Java Virtual Machine Instruction Set, has small edits in many floating-point instructions.


Comments
Just for completeness there was much discussion about the form of this change that is not captured in the JEP or this CSR-request, but the end result is as has been attached and approved here.
31-05-2021

Moving to Approved.
28-05-2021

Attached updates JLS and JVMS changes.
28-05-2021

[~jfranck], essentially all floating-point computation on JVMs has been done strictly for many years. Strict computation is a mandatory feature, required with strictfp/ACC_STRICT, but it is an optional feature as well, meaning the default (without any modifier) can be strict on a given implementation. The natural implementation on any platform other than the x87 FPU is strict. This includes x86 systems with SSE2, which support float and double computations in sets of registers separate from the x87. The behavioral difference between strict/non-strict is that (primarily) multiply and divide can overflow/underflow at more extreme thresholds. However, this difference in change of overflow/underflow is unpredictable: one multiply could overflow at the usual point and the next could have extended range.
10-05-2021

Can you elaborate on the potential behavioral incompatibility of switching almost all uses of FP from non-strict to strict? You write that there are subtle differences between the two version, when if ever are users likely to encounter those differences?
10-05-2021

I have also expressed an opinion in the PR that I do not agree with the proposal for ACC_STRICT in the VM. I believe the approach here should be a normal deprecation style process for the VM. Effectively in 17 ACC_STRICT is deprecated: it is implicitly applied to all methods but need not be explicitly applied; if explicitly applied it has the same rules as have existed to date. Then in 17+N ACC_STRICT use is considered an error and is rejected always. Then in 17+N+M we can start reusing the bit value for ACC_MUMBLE.
07-05-2021

I reviewed the proposal from the point of view of javac (Language construct, Class file construct, add/remove/modify command line option) and the proposal looks good to me. Note: I did not study the proposed changes for JLS/JVMS for exhaustiveness/correctness and also is not qualified to add to the discussion on reflection.
07-05-2021

Per a separate conversation, the core reflection modifier enhancement has been agreed to be considered as future work; see JDK-8266670. Work in this vein would need to occur no later than the point at which the bit position was reused for methods.
06-05-2021

Separate from the reflection story, Brian suggested to "keep the error on unexpected use of ACC_STRICTFP in the JVM for longer than one version". AIUI that would mean ACC_STRICT having meaning in a v61 class file, so that "setting ACC_STRICT" has meaning, and can continue to be detected in conjunction with "setting ACC_ABSTRACT" (result: error). That is, setting ACC_STRICT in a v61 class file would mostly be a no-op in 17 (since methods no longer run in FP-strict mode) but would cause errors where class file creators were lax in their ACC flags -- one final chance for users to get their tools fixed. Is my understanding correct?
06-05-2021

For the record, my preferred solution would be: enum Modifiers { PUBLIC, PRIVATE, ...; String name(); // plenty of candidate methods } ... // on Method, Field, Class, etc Set<Modifiers> modifiers(); // name open to bikeshedding The implementation would then be free to filter on classfile version and the kind of thing being reflected on to disambiguate between collisions, obsoleted/changed/reused bits, etc. A simple version of this could easily be done for this JEP.
06-05-2021

The collisions are a different case, because they can be disambiguated by information already present in reflection (e.g., BRIDGE only makes sense on methods.) Whereas the classfile version is not made available, so a reflection client has no idea whether the bit corresponding to STRICT means strictfp (old classfiles), error (17), nothing (18), or something else (future). I also don't find the `isMumble()` solution acceptable unless we're planning to go back and do _all_ the bits this way. The Modifier::toString also continues to report `strictfp`. If we're making strictfp a no-op -- it means nothing in the source, and nothing in the bytecode, and after 17 the bit is considered unused -- then it should be a no-op in the reflection API. I am open to suggestions as to how to handle this, but I do not believe that "do nothing" is good enough here.
06-05-2021

There are already collisions in flag usage in different contexts across methods, fields, and types. For example, the bit position used for bridge methods is the same as the bit position used for volatile; there is also a collision between var-args and transient. A context-free modifier decoding is not sufficient for full accuracy and core reflection was upgraded to filter out misleading results in string output (JDK-6354476). The source of java.lang.reflect.Modifier contains the following comment on that point: // Bits not (yet) exposed in the public API either because they // have different meanings for fields and methods and there is no // way to distinguish between the two in this class, or because // they are not Java programming language keywords static final int BRIDGE = 0x00000040; static final int VARARGS = 0x00000080; static final int SYNTHETIC = 0x00001000; static final int ANNOTATION = 0x00002000; static final int ENUM = 0x00004000; static final int MANDATED = 0x00008000; So I would say any future isMumble method should need to live as an instance method on java.lang.reflect.Method rather than a static method in java.lang.reflect.Modifier. The isMumble implementation would then need to be something logically equivalent to `( (method.flags & ACC_MUMBLE) != 0 && method.classFileVersion() >= FOO)` The method.classFileVersion() might need to be a new capability in the platform implementation if it is exposed to the libraries rather than the VM.
06-05-2021

I don't think this treatment scales to future re-use of the bit. Consider the use case of reproducing class and method declarations (e.g., `public static void main(String[] args)`) based on what's available in core reflection. Suppose Java 19 reuses this for ACC_MUMBLE. Because reflection does not yield the classfile version, a reflection-using API which finds this bit set will not know whether to render this as `strictfp void foo()` or `mumble void foo()`. If the plan is to have always strict semantics, make the source a no-op, and make the bit in the classfile effectively ignored (until repurposed), then reflection need not reflect anything here either.
06-05-2021

Moving to Provisional. Details of how to handle ACC_STRICT in class file version 61 will be determined before the request is Finalized.
06-05-2021

[~briangoetz], I don't think any core reflection update for this work is either necessary or advisable. The ACC_STRICT modifier remains defined for many class file versions so it is reasonable to keep Modifier.STRICT as having its current value and Modifier.isStrict() its current behavior.
06-05-2021

The overall strategy is fine, but I would suggest to keep the error on unexpected use of ACC_STRICTFP in the JVM for longer than one version, and file a separate CSR to reclaim it when an alternate use is proposed. Additionally, the CSR should "reflect" what will happen with the reflective elements associated with strictness -- `Modifier.STRICT` and `Modifier.isStrict(int)`. My preference is that `isStrict()` always returns false and that `STRICT` be defined to be zero, so tests for (x & STRICT) are always zero, and using `STRICT` to set bits similarly has no effect.
06-05-2021