-
Enhancement
-
Resolution: Unresolved
-
P4
-
repo-valhalla
TL;DR: Add `java.lang.runtime.Checks` and `void Checks::nullCheck(Object)`, making it be a specially optimized intrinsic as necessary to make such null checks efficient.
Currently, runtime checks are mandated by the Java language at points where an erased generic type must be converted to a type argument, known at a generic use-site. These checks are handled by `checkcast` instructions, which are a good fit for the job.
Some runtime conversions, such as from `int` to `float` (or vice versa) are handled by bytecodes (`i2f`, `f2i`, etc.).
Currently implicit boxing and unboxing conversions to and from primitives are handled by calls to the same routines used by programmers, such as has `Integer::valueOf`. This works fine, except perhaps for a small (probably harmless) erasure of programmer intent: The VM and JIT can't distinguish an implicit call to `valueOf` (invisible in the source code) from one written consciously by the programmer.
Null checks are handled implicitly by bytecodes (like `getfield`) that require a non-null reference. Such checks are likely to increase over time, for example, in `aastore` instructions.
In general, new language features, such as those in Valhalla, are likely to add more kinds of checks and/or casts. Do we enhance the bytecode set to make them work? Probably not. Do we spin bytecodes that look as if the programmer were manually making the checks and conversions? Maybe, but sometimes it is not convenient.
In the case of new null checks, due to implicit or casting conversion from (say) `String` to `String!`, a good bytecode sequence is needed. If javac chooses to emit an idiom the programmer might have used explicitly, it might be found emitting `invokestatic Objects::requireNonNull`.
However, the `Objects` method, while good for programmers, is not so good for bytecode generators like javac. The generator will also have to emit a `checkcast` to recover the original type, or else emit a `dup` before the call to `requireNonNull` and a `pop` to get rid of the return value,
For bytecode generators (which have different requirements than programmers), it seems useful to add at least one method in `java.lang.runtime`, something like `Checks::nullCheck` or `Checks::requireNonNull`.
This method can be defined in such a way as to minimize bytecode complexity, by returning void, and (perhaps) taking multiple arguments. It would do the job of `java.util.Objects::requireNonNull`, but would not return an inconvenient `(T)` value which needs a cleanup `checkcast`. Such a `void` return is inconvenient for programmers, but convenient for a bytecode generator.
Another alternative is to call `Object::getClass` on the reference to be checked, and rely on the implicit null check for `invokevirtual` to do the work. The JITs are able to remove the class fetch and keep the null check, so code is good. But there are two problems here: First, the bytecode looks like the programmer was oddly fond of `getClass`. Second, the interpreter processing of `getClass`, which is a native method, is very slow (unless it were made into an interpreter intrinsic).
Before a method call, if one or more parameters needed a null check (because of a `C!` parameter), a call to `nullCheck` would exclude null for each affected parameter. (Maybe a higher-arity method would handle several at once, amortizing the cost at the bytecode level.) The specific sequence looks like `dup; invokestatic nullCheck`, which is just 3 bytes.
Specialized optimization and diagnostic properties can be given to `nulCheck` to make it cheaper than `requireNonNull` or `getClass`, by making it be a VM intrinsic, in the interpreter and the JITs. If we are lucky, it might turn out that the intrinsic treatment might not need to be deep.
For batching multiple null checks, `invokedynamic` could supply a signature-polymorphic API where any number of null checks would be handled in one bytecode (of 5 bytes plus push or dup instructions).
Those VM components would have to be modified anyway (and with more difficulty) if we introduced new bytecode syntaxes, as proposed in JDK-8297236. So it seems a net win to use intrinsic methods instead of new bytecode syntaxes, or even old methods like `Objects::requireNonNull`..
If this pans out for checkNull, we may wish to intrinsify other checking or conversion operations as well, although it seems that null checks have a special need, when considering translation strategies.
Currently, runtime checks are mandated by the Java language at points where an erased generic type must be converted to a type argument, known at a generic use-site. These checks are handled by `checkcast` instructions, which are a good fit for the job.
Some runtime conversions, such as from `int` to `float` (or vice versa) are handled by bytecodes (`i2f`, `f2i`, etc.).
Currently implicit boxing and unboxing conversions to and from primitives are handled by calls to the same routines used by programmers, such as has `Integer::valueOf`. This works fine, except perhaps for a small (probably harmless) erasure of programmer intent: The VM and JIT can't distinguish an implicit call to `valueOf` (invisible in the source code) from one written consciously by the programmer.
Null checks are handled implicitly by bytecodes (like `getfield`) that require a non-null reference. Such checks are likely to increase over time, for example, in `aastore` instructions.
In general, new language features, such as those in Valhalla, are likely to add more kinds of checks and/or casts. Do we enhance the bytecode set to make them work? Probably not. Do we spin bytecodes that look as if the programmer were manually making the checks and conversions? Maybe, but sometimes it is not convenient.
In the case of new null checks, due to implicit or casting conversion from (say) `String` to `String!`, a good bytecode sequence is needed. If javac chooses to emit an idiom the programmer might have used explicitly, it might be found emitting `invokestatic Objects::requireNonNull`.
However, the `Objects` method, while good for programmers, is not so good for bytecode generators like javac. The generator will also have to emit a `checkcast` to recover the original type, or else emit a `dup` before the call to `requireNonNull` and a `pop` to get rid of the return value,
For bytecode generators (which have different requirements than programmers), it seems useful to add at least one method in `java.lang.runtime`, something like `Checks::nullCheck` or `Checks::requireNonNull`.
This method can be defined in such a way as to minimize bytecode complexity, by returning void, and (perhaps) taking multiple arguments. It would do the job of `java.util.Objects::requireNonNull`, but would not return an inconvenient `(T)` value which needs a cleanup `checkcast`. Such a `void` return is inconvenient for programmers, but convenient for a bytecode generator.
Another alternative is to call `Object::getClass` on the reference to be checked, and rely on the implicit null check for `invokevirtual` to do the work. The JITs are able to remove the class fetch and keep the null check, so code is good. But there are two problems here: First, the bytecode looks like the programmer was oddly fond of `getClass`. Second, the interpreter processing of `getClass`, which is a native method, is very slow (unless it were made into an interpreter intrinsic).
Before a method call, if one or more parameters needed a null check (because of a `C!` parameter), a call to `nullCheck` would exclude null for each affected parameter. (Maybe a higher-arity method would handle several at once, amortizing the cost at the bytecode level.) The specific sequence looks like `dup; invokestatic nullCheck`, which is just 3 bytes.
Specialized optimization and diagnostic properties can be given to `nulCheck` to make it cheaper than `requireNonNull` or `getClass`, by making it be a VM intrinsic, in the interpreter and the JITs. If we are lucky, it might turn out that the intrinsic treatment might not need to be deep.
For batching multiple null checks, `invokedynamic` could supply a signature-polymorphic API where any number of null checks would be handled in one bytecode (of 5 bytes plus push or dup instructions).
Those VM components would have to be modified anyway (and with more difficulty) if we introduced new bytecode syntaxes, as proposed in JDK-8297236. So it seems a net win to use intrinsic methods instead of new bytecode syntaxes, or even old methods like `Objects::requireNonNull`..
If this pans out for checkNull, we may wish to intrinsify other checking or conversion operations as well, although it seems that null checks have a special need, when considering translation strategies.
- relates to
-
JDK-8297236 enhanced checkcast for Valhalla type unification
-
- Draft
-