[WIP][GR-45250][GR-45734] Reachability proofs for reflective operations #11079
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Currently, the constant reflection analysis used by Native Image is optimization dependent. This can lead to unexpected results during image run-time when using reflection. For example, the
Class.forName
call in the following snippet will be folded by the analysis:However, adding a simple printing statement to
isEven
or toggling different optimizations during build-time can cause the method to be non-inlinable andClass.forName
call won't be folded:In order to prevent this behavior, we can run a constant reflection analysis directly on the bytecode provided by JVMCI objects.
Analysis specification
For each instruction of the method, the analysis will record the state of the operand stack and the local variable table prior to the execution of that instruction. Each value on the operand stack and each local variable value in the local variable table is either marked as not a compile time constant or as a compile time constant, in which case it has an abstract representation in the form of a pair <source BCI, inferred value>. The source BCI represents the BCI of the instruction that pushed that value on the operand stack or stored it in the local variable table, while the inferred value represents the actual value which would be placed on the operand stack or in the local variable table during runtime execution, as inferred by the analysis. Further, we distinguish between non-array type and array type compile time constants.
Each instruction of the method is assigned a changed bit. Initially, the changed bit is set only for the first instruction of the method. The operand stack corresponding to the first instruction is empty and the local variables corresponding to the method's parameters are marked as not a compile time constants.
The following steps are then repeated in a loop:
The invocation of a reflective method (depending on the method, an INVOKESTATIC or INVOKEVIRTUAL instruction) can then be inferred iff all of its operands on the modeled operand stack are compile time constants.
In terms of Java code, the bytecode analysis specification roughly translates to the following rules for what is considered a compile time constant expression:
Preliminary results
Preliminary results we're getting from running our analysis on Spring PetClinic: