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