JDK-8059378 : EA: eliminate non-rematerializable allocations of non-escaping objects
Type:Enhancement
Component:hotspot
Sub-Component:compiler
Affected Version:8u40,9,10
Priority:P4
Status:Open
Resolution:Unresolved
Submitted:2014-09-29
Updated:2022-01-25
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.
After JDK-8058825, VM doesn't eliminate allocations of non-escaping objects, if they can't be rematerialized.
Escape analysis can be enhanced to do elimination if there's no need to rematerialize the object.
Comments
There is code which checks type of ArrayCopy node to allow eliminate passed allocation:
https://github.com/openjdk/jdk/blob/master/src/hotspot/share/opto/macro.cpp#L606
I thought we may have issue here. But based on `(CopyOfRange, tightly coupled allocation) ` type (in output) it could be `is_copyofrange_validated()` is false because allocation's klass is not exact. So my statement about ArrayCopy node is void.
I also looked on OptoAssembly and class check failed (and code deoptimized) because we compare klass to `java/lang/Object`:
movq RSI, precise [java/lang/Object: 0x00007fae6c2e2178 *: :Constant:exact * # ptr
And it seems we can't avoid this check and deoptimization in this test - unless we add/use ARRAY argument profiling data. I think.
So all my questions are answered.
May be you can think of other test which does non-escaping allocation when klass is not known and see what happens.
25-01-2022
Thank you, Vladimir I / K. Things are much more clear now. However, I'm still confused. @V. Kozlov, what exactly do you mean by "we need to handle ArrayCopy node case"?
Just sharing some things I noticed while debugging:
- If I remove the exactness check added by Vladimir I. then both AllocateArray nodes are removed.
- The two "NotScalar (Object is passed as argument)...)" messages are about the same AllocateArray node.
24-01-2022
So first, we need to handle ArrayCopy node case.
Second, find why we deoptimize on class check.
21-01-2022
Deoptimization also happened in different place - class check from arraycopy intrinsic code, I think:
UNCOMMON TRAP method=ArraysCopyOf.test(Ljava/lang/Class;)V bci=13 pc=0x00007f8aa1686ec8, relative_pc=0x0000000000000148, debug_id=0 compiler=c2 compile_id=1 (@0x00007f8aa1686ec8) thread=1275115 reason=class_check action=make_not_entrant unloaded_class_index=-1 debug_id=0
159 1 ArraysCopyOf::test (69 bytes) made not entrant
DEOPT PACKING thread=0x00007f8aac02aa00 vframeArray=0x00007f8aac317e10
Compiled frame (sp=0x00007f8ab609c8d0 unextended sp=0x00007f8ab609c8d0, fp=0x0000000458819a20, real_fp=0x00007f8ab609c900, pc=0x00007f8aa1686ec8)
nmethod 159 1 ArraysCopyOf::test (69 bytes)
Virtual frames (innermost/newest first):
VFrame 0 (0x00007f8a3c0b1978) - ArraysCopyOf.test(Ljava/lang/Class;)V - invokestatic @ bci=13
DEOPT UNPACKING thread=0x00007f8aac02aa00 vframeArray=0x00007f8aac317e10 mode=2
Virtual frames (outermost/oldest first):
VFrame 0 (0x00007f8aac319318) - ArraysCopyOf.test(Ljava/lang/Class;)V - invokestatic @ bci=13 sp=0x00007f8ab609c880
21-01-2022
There is test ArraysCopyOf.java attached to JDK-8058825. It has call `Inner.test(example)` to which result of arraycopy passed.
In JDK 9 the compiled code deoptimized at that call because `Inner` class was not loaded - it was uncommon trap.
The JDK-8058825 fix added `!t->klass_is_exact()` check before we set `_is_scalar_replaceable = true` for allocation:
https://github.com/openjdk/jdk/blob/master/src/hotspot/share/opto/escape.cpp#L3144
So allocation is not marked as scalarized by EA. The idea I proposed is to allow scalarize non-exact non-escaping allocation if there are no uncommon traps where we should correctly rematerialize object.
Unfortunately with latest JDK the test behave totally different.
You need to pre-load java.util.Arrays before test
public static void main(String[] args) throws Throwable {
+ int[] a = new int[10];
+ java.util.Arrays.fill(a, 1); // Load java.util.Arrays class
test(String[].class);
}
Then allocations are not scalarized because we have new ArrayCopy nodes:
$ ./build/fastdebug/images/jdk/bin/java -Xcomp -XX:CompileCommand=compileonly,ArraysCopyOf.test -XX:-TieredCompilation -XX:+PrintCompilation -XX:+PrintEscapeAnalysis -XX:+PrintEliminateAllocations -XX:+TraceDeoptimization -XX:CICompilerCount=1 -XX:CompileCommand=quiet ArraysCopyOf
CompileCommand: compileonly ArraysCopyOf.test bool compileonly = true
154 1 b ArraysCopyOf::test (69 bytes)
======== Connection graph for ArraysCopyOf::test
invocation #0: 2 iterations and 0.000004 sec to build connection graph with 242 nodes and worklist size 21
JavaObject NoEscape(NoEscape) [ [ 95 100 195cp ]] 83 AllocateArray === 60 54 55 8 1 ( 81 68 44 44 1 57 1 ) [[ 84 85 86 93 94 95 ]] rawptr:NotNull ( int:>=0, java/lang/Object:NotNull *, bool, int ) ArraysCopyOf::test @ bci:8 (line 12) !jvms: ArraysCopyOf::test @ bci:8 (line 12)
LocalVar [ 83P [ 100 ]] 95 Proj === 83 [[ 96 100 ]] #5 !jvms: ArraysCopyOf::test @ bci:8 (line 12)
LocalVar [ 95 83P [ 195cp ]] 100 CheckCastPP === 97 95 [[ 195 115 132 151 166 177 195 ]] #narrowoop: java/lang/Object *[int:0]:NotNull:exact * !jvms: ArraysCopyOf::test @ bci:8 (line 12)
JavaObject NoEscape(NoEscape) [ 195cp [ 189 194 ]] 177 AllocateArray === 235 94 82 8 1 ( 81 147 44 44 1 1 1 100 44 120 ) [[ 178 179 180 187 188 189 ]] rawptr:NotNull ( int:>=0, java/lang/Object:NotNull *, bool, int ) ArraysCopyOf::test @ bci:13 (line 12) reexecute !jvms: ArraysCopyOf::test @ bci:13 (line 12)
LocalVar [ 177P [ 194 ]] 189 Proj === 177 [[ 190 194 ]] #5 !jvms: ArraysCopyOf::test @ bci:13 (line 12)
LocalVar [ 189 195cp 177P [ ]] 194 CheckCastPP === 191 189 [[ 195 225 ]] #narrowoop: java/lang/Object *[int:0]:NotNull * !jvms: ArraysCopyOf::test @ bci:13 (line 12)
NotScalar (Object is passed as argument) 100 CheckCastPP === 97 95 [[ 195 115 132 151 166 177 195 ]] #narrowoop: java/lang/Object *[int:0]:NotNull:exact *,iid=83 !jvms: ArraysCopyOf::test @ bci:8 (line 12)
>>>> 195 ArrayCopy === 191 188 176 8 1 ( 100 44 194 44 44 _ _ 68 147 1 1 1 100 44 120 ) [[ 198 200 201 202 217 ]] void ( java/lang/Object *, int, java/lang/Object *, int, int, int, int, BotPTR *+bot, BotPTR *+bot ) ArraysCopyOf::test @ bci:13 (line 12) reexecute (CopyOfRange, tightly coupled allocation) !jvms: ArraysCopyOf::test @ bci:13 (line 12)
NotScalar (Object is passed as argument) 100 CheckCastPP === 97 95 [[ 195 115 132 151 166 177 195 ]] #narrowoop: java/lang/Object *[int:0]:NotNull:exact *,iid=83 !jvms: ArraysCopyOf::test @ bci:8 (line 12)
>>>> 195 ArrayCopy === 191 188 176 8 1 ( 100 44 194 44 44 _ _ 68 147 1 1 1 100 44 120 ) [[ 198 200 201 202 217 ]] void ( java/lang/Object *, int, java/lang/Object *, int, int, int, int, BotPTR *+bot, BotPTR *+bot ) ArraysCopyOf::test @ bci:13 (line 12) reexecute (CopyOfRange, tightly coupled allocation) !jvms: ArraysCopyOf::test @ bci:13 (line 12)
21-01-2022
It was filed as a follow-up enhancement for JDK-8058825. The bug was related to Arrays.copyOf(). Vladimir K. noted that "we will not eliminate such allocations anymore - if it is not referenced in debug info (no deoptimiztion points) we could still eliminate it."
21-01-2022
@Vladimir Ivanov - Can you please give me an example where this problem can happen? I'll be glad to try and fix this issue while I study the workings of C2 Escape Analysis & Scalar Replacement.