RFR: 8359270: C2: alignment check should consider base offset when emitting arraycopy runtime call [v2]
Tobias Hartmann
thartmann at openjdk.org
Thu Jun 19 07:18:50 UTC 2025
On Mon, 16 Jun 2025 03:36:33 GMT, Fei Yang <fyang at openjdk.org> wrote:
>> Hi, please consider this change fixing alignment check when emitting arraycopy runtime call.
>>
>> There are currently four callsites of `StubRoutines::select_arraycopy_function` in hotspot C2 shared code where we emit arraycopy runtime calls [1-4]. Three of them [2-4] missed base offset when calculation alignment for both source and destination array addresses. Seems they assume a base offset of 8 bytes, which is not always true. Base offset becomes 4 bytes under either `-XX:+UseCompactObjectHeaders` or `-XX:-UseCompressedClassPointers`.
>>
>> And `StubRoutines::select_arraycopy_function` selects the right arraycopy runtime call based on this alignment. As a result, it could see an incorrect `aligned` param about the array addresses and thus a wrong arraycopy runtime call is selected. This is causing performance regressions (like Dacapo Spring) on some linux-riscv64 platforms like Sifive Unmatched or Premier P550 SBCs where misaligned memory access is very slow.
>>
>> Proposed change fixes this issue by taking base offset into account when checking the alignment, which is very similar to [1].
>>
>> Testing:
>> - [x] Tier1-3 on linux-x64 (release & fastdebug)
>> - [x] Tier1-3 on linux-aarch64 (release & fastdebug)
>> - [x] Tier1-3 on linux-riscv64 (release)
>> - [x] Dacapo spring performance test on linux-riscv64 (w/wo `-XX:+UseCompactObjectHeaders` / `-XX:-UseCompressedClassPointers`)
>>
>> [1] https://github.com/openjdk/jdk/blob/master/src/hotspot/share/opto/macroArrayCopy.cpp#L341
>> [2] https://github.com/openjdk/jdk/blob/master/src/hotspot/share/opto/library_call.cpp#L1584
>> [3] https://github.com/openjdk/jdk/blob/master/src/hotspot/share/opto/library_call.cpp#L1666
>> [4] https://github.com/openjdk/jdk/blob/master/src/hotspot/share/opto/stringopts.cpp#L1481
>
> Fei Yang has updated the pull request with a new target base due to a merge or a rebase. The incremental webrev excludes the unrelated changes brought in by the merge/rebase. The pull request contains three additional commits since the last revision:
>
> - add test
> - Merge remote-tracking branch 'upstream/master' into JDK-8359270
> - 8359270: C2: alignment check should consider base offset when emitting arraycopy runtime call
`compiler/c2/irTests/stringopts/TestArrayCopySelect.java` fails on various platforms with `-ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation`:
Scenario #0
Compilations (2) of Failed Methods (2)
--------------------------------------
1) Compilation of "static char[] compiler.c2.irTests.stringopts.TestArrayCopySelect.testStrUGetCharsAligned(java.lang.String)":
> Phase "PrintIdeal":
AFTER: print_ideal
0 Root === 0 35 65 66 [[ 0 1 3 22 30 ]] inner
1 Con === 0 [[ ]] #top
3 Start === 3 0 [[ 3 5 6 7 8 9 10 ]] #{0:control, 1:abIO, 2:memory, 3:rawptr:BotPTR, 4:return_address, 5:java/lang/String (java/io/Serializable,java/lang/Comparable,java/lang/CharSequence,java/lang/constant/Constable,java/lang/constant/ConstantDesc):exact *}
5 Parm === 3 [[ 27 ]] Control !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:-1 (line 71)
6 Parm === 3 [[ 31 47 ]] I_O !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:-1 (line 71)
7 Parm === 3 [[ 47 31 ]] Memory Memory: @BotPTR *+bot, idx=Bot; !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:-1 (line 71)
8 Parm === 3 [[ 66 65 47 31 35 ]] FramePtr !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:-1 (line 71)
9 Parm === 3 [[ 66 65 31 ]] ReturnAdr !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:-1 (line 71)
10 Parm === 3 [[ 23 47 ]] Parm0: java/lang/String (java/io/Serializable,java/lang/Comparable,java/lang/CharSequence,java/lang/constant/Constable,java/lang/constant/ConstantDesc):exact * Oop:java/lang/String (java/io/Serializable,java/lang/Comparable,java/lang/CharSequence,java/lang/constant/Constable,java/lang/constant/ConstantDesc):exact * !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:-1 (line 71)
22 ConP === 0 [[ 23 31 ]] #null
23 CmpP === _ 10 22 [[ 24 ]] !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:1 (line 71)
24 Bool === _ 23 [[ 27 ]] [ne] !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:1 (line 71)
27 If === 5 24 [[ 28 29 ]] P=0.999999, C=-1.000000 !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:1 (line 71)
28 IfTrue === 27 [[ 47 ]] #1 !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:1 (line 71)
29 IfFalse === 27 [[ 31 ]] #0 !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:1 (line 71)
30 ConI === 0 [[ 31 ]] #int:-10
31 CallStaticJava === 29 6 7 8 9 (30 1 22 ) [[ 32 ]] # Static uncommon_trap(reason='null_check' action='maybe_recompile' debug_id='0') void ( int ) C=0.000100 TestArrayCopySelect::testStrUGetCharsAligned @ bci:1 (line 71) !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:1 (line 71)
32 Proj === 31 [[ 35 ]] #0 !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:1 (line 71)
35 Halt === 32 1 1 8 1 [[ 0 ]] !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:1 (line 71)
47 CallStaticJava === 28 6 7 8 1 (10 1 ) [[ 48 49 50 52 ]] # Static java.lang.String::toCharArray char[int:>=0] (java/lang/Cloneable,java/io/Serializable):exact * ( java/lang/String (java/io/Serializable,java/lang/Comparable,java/lang/CharSequence,java/lang/constant/Constable,java/lang/constant/ConstantDesc):NotNull:exact * ) TestArrayCopySelect::testStrUGetCharsAligned @ bci:1 (line 71) !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:1 (line 71)
48 Proj === 47 [[ 54 ]] #0 !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:1 (line 71)
49 Proj === 47 [[ 66 54 65 59 ]] #1 !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:1 (line 71)
50 Proj === 47 [[ 66 65 ]] #2 Memory: @BotPTR *+bot, idx=Bot; !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:1 (line 71)
52 Proj === 47 [[ 65 ]] #5 !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:1 (line 71)
54 Catch === 48 49 [[ 55 56 ]] !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:1 (line 71)
55 CatchProj === 54 [[ 65 ]] #0 at bci -1 !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:1 (line 71)
56 CatchProj === 54 [[ 66 59 ]] #1 at bci -1 !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:1 (line 71)
59 CreateEx === 56 49 [[ 66 ]] #java/lang/Throwable (java/io/Serializable):NotNull * Oop:java/lang/Throwable (java/io/Serializable):NotNull * !jvms: TestArrayCopySelect::testStrUGetCharsAligned @ bci:1 (line 71)
65 Return === 55 49 50 8 9 returns 52 [[ 0 ]]
66 Rethrow === 56 49 50 8 9 exception 59 [[ 0 ]]
2) Compilation of "static java.lang.String compiler.c2.irTests.stringopts.TestArrayCopySelect.testStrUtoBytesAligned(char[])":
> Phase "PrintIdeal":
AFTER: print_ideal
0 Root === 0 37 38 [[ 0 1 3 ]] inner
1 Con === 0 [[ ]] #top
3 Start === 3 0 [[ 3 5 6 7 8 9 10 ]] #{0:control, 1:abIO, 2:memory, 3:rawptr:BotPTR, 4:return_address, 5:char[int:>=0] (java/lang/Cloneable,java/io/Serializable):exact *}
5 Parm === 3 [[ 22 ]] Control !jvms: TestArrayCopySelect::testStrUtoBytesAligned @ bci:-1 (line 87)
6 Parm === 3 [[ 22 ]] I_O !jvms: TestArrayCopySelect::testStrUtoBytesAligned @ bci:-1 (line 87)
7 Parm === 3 [[ 22 ]] Memory Memory: @BotPTR *+bot, idx=Bot; !jvms: TestArrayCopySelect::testStrUtoBytesAligned @ bci:-1 (line 87)
8 Parm === 3 [[ 38 37 22 ]] FramePtr !jvms: TestArrayCopySelect::testStrUtoBytesAligned @ bci:-1 (line 87)
9 Parm === 3 [[ 38 37 ]] ReturnAdr !jvms: TestArrayCopySelect::testStrUtoBytesAligned @ bci:-1 (line 87)
10 Parm === 3 [[ 22 ]] Parm0: char[int:>=0] (java/lang/Cloneable,java/io/Serializable):exact * !jvms: TestArrayCopySelect::testStrUtoBytesAligned @ bci:-1 (line 87)
22 CallStaticJava === 5 6 7 8 1 (10 1 ) [[ 23 24 25 27 ]] # Static java.lang.String::valueOf java/lang/String (java/io/Serializable,java/lang/Comparable,java/lang/CharSequence,java/lang/constant/Constable,java/lang/constant/ConstantDesc):exact * ( char[int:>=0] (java/lang/Cloneable,java/io/Serializable):exact * ) TestArrayCopySelect::testStrUtoBytesAligned @ bci:1 (line 87) !jvms: TestArrayCopySelect::testStrUtoBytesAligned @ bci:1 (line 87)
23 Proj === 22 [[ 29 ]] #0 !jvms: TestArrayCopySelect::testStrUtoBytesAligned @ bci:1 (line 87)
24 Proj === 22 [[ 38 29 37 34 ]] #1 !jvms: TestArrayCopySelect::testStrUtoBytesAligned @ bci:1 (line 87)
25 Proj === 22 [[ 38 37 ]] #2 Memory: @BotPTR *+bot, idx=Bot; !jvms: TestArrayCopySelect::testStrUtoBytesAligned @ bci:1 (line 87)
27 Proj === 22 [[ 37 ]] #5 Oop:java/lang/String (java/io/Serializable,java/lang/Comparable,java/lang/CharSequence,java/lang/constant/Constable,java/lang/constant/ConstantDesc):exact * !jvms: TestArrayCopySelect::testStrUtoBytesAligned @ bci:1 (line 87)
29 Catch === 23 24 [[ 30 31 ]] !jvms: TestArrayCopySelect::testStrUtoBytesAligned @ bci:1 (line 87)
30 CatchProj === 29 [[ 37 ]] #0 at bci -1 !jvms: TestArrayCopySelect::testStrUtoBytesAligned @ bci:1 (line 87)
31 CatchProj === 29 [[ 38 34 ]] #1 at bci -1 !jvms: TestArrayCopySelect::testStrUtoBytesAligned @ bci:1 (line 87)
34 CreateEx === 31 24 [[ 38 ]] #java/lang/Throwable (java/io/Serializable):NotNull * Oop:java/lang/Throwable (java/io/Serializable):NotNull * !jvms: TestArrayCopySelect::testStrUtoBytesAligned @ bci:1 (line 87)
37 Return === 30 24 25 8 9 returns 27 [[ 0 ]]
38 Rethrow === 31 24 25 8 9 exception 34 [[ 0 ]]
Failed IR Rules (2) of Methods (2)
----------------------------------
1) Method "static char[] compiler.c2.irTests.stringopts.TestArrayCopySelect.testStrUGetCharsAligned(java.lang.String)" - [Failed IR rules: 1]:
* @IR rule 1: "@compiler.lib.ir_framework.IR(phase={DEFAULT}, applyIfPlatformAnd={}, applyIfCPUFeatureOr={}, counts={"_#C#CALL_OF#_", "arrayof_jshort_disjoint_arraycopy", ">0"}, applyIfPlatform={}, applyIfPlatformOr={}, failOn={}, applyIfOr={}, applyIfCPUFeatureAnd={}, applyIf={"UseCompactObjectHeaders", "false"}, applyIfCPUFeature={}, applyIfAnd={}, applyIfNot={})"
> Phase "PrintIdeal":
- counts: Graph contains wrong number of nodes:
* Constraint 1: "(\\d+(\\s){2}(Call.*.*)+(\\s){2}===.*arrayof_jshort_disjoint_arraycopy )"
- Failed comparison: [found] 0 > 0 [given]
- No nodes matched!
2) Method "static java.lang.String compiler.c2.irTests.stringopts.TestArrayCopySelect.testStrUtoBytesAligned(char[])" - [Failed IR rules: 1]:
* @IR rule 1: "@compiler.lib.ir_framework.IR(phase={DEFAULT}, applyIfPlatformAnd={}, applyIfCPUFeatureOr={}, counts={"_#C#CALL_OF#_", "arrayof_jshort_disjoint_arraycopy", ">0"}, applyIfPlatform={}, applyIfPlatformOr={}, failOn={}, applyIfOr={}, applyIfCPUFeatureAnd={}, applyIf={"UseCompactObjectHeaders", "false"}, applyIfCPUFeature={}, applyIfAnd={}, applyIfNot={})"
> Phase "PrintIdeal":
- counts: Graph contains wrong number of nodes:
* Constraint 1: "(\\d+(\\s){2}(Call.*.*)+(\\s){2}===.*arrayof_jshort_disjoint_arraycopy )"
- Failed comparison: [found] 0 > 0 [given]
- No nodes matched!
-------------
PR Comment: https://git.openjdk.org/jdk/pull/25765#issuecomment-2983742441
More information about the hotspot-compiler-dev
mailing list