| # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 |
| # NOTE: Test expansion of PseudoLD_RV32_OPT/PseudoSD_RV32_OPT after register allocation |
| # RUN: llc -mtriple=riscv32 -mattr=+zilsd -run-pass riscv-load-store-opt %s -o - | FileCheck %s |
| --- | |
| define i32 @expand_pseudold_valid(ptr %0) { |
| %2 = load i32, ptr %0, align 4 |
| %3 = getelementptr inbounds i32, ptr %0, i32 1 |
| %4 = load i32, ptr %3, align 4 |
| %5 = add i32 %2, %4 |
| ret i32 %5 |
| } |
| |
| define void @expand_pseudosd_valid(ptr %0, i32 %1, i32 %2) { |
| store i32 %1, ptr %0, align 4 |
| %4 = getelementptr inbounds i32, ptr %0, i32 1 |
| store i32 %2, ptr %4, align 4 |
| ret void |
| } |
| |
| define i32 @expand_pseudold_invalid_pair(ptr %0) { |
| %2 = load i32, ptr %0, align 4 |
| %3 = getelementptr inbounds i32, ptr %0, i32 1 |
| %4 = load i32, ptr %3, align 4 |
| %5 = add i32 %2, %4 |
| ret i32 %5 |
| } |
| |
| define void @expand_pseudosd_invalid_pair(ptr %0, i32 %1, i32 %2) { |
| store i32 %1, ptr %0, align 4 |
| %4 = getelementptr inbounds i32, ptr %0, i32 1 |
| store i32 %2, ptr %4, align 4 |
| ret void |
| } |
| |
| define void @store_zero_combine_valid(ptr %0) { |
| store i32 0, ptr %0, align 8 |
| %2 = getelementptr inbounds i32, ptr %0, i32 1 |
| store i32 0, ptr %2, align 8 |
| ret void |
| } |
| |
| define void @store_zero_combine_invalid(ptr %0, i32 %1) { |
| store i32 %1, ptr %0, align 8 |
| %3 = getelementptr inbounds i32, ptr %0, i32 1 |
| store i32 0, ptr %3, align 8 |
| ret void |
| } |
| |
| @global_array = external global [100 x i32] |
| |
| define i32 @expand_pseudold_invalid_symbolic() { |
| ret i32 0 |
| } |
| |
| define i32 @overlapped_first_reg_base_reg() { |
| ret i32 0 |
| } |
| ... |
| --- |
| # Valid consecutive even/odd register pair - should expand to LD_RV32 |
| name: expand_pseudold_valid |
| tracksRegLiveness: false |
| body: | |
| bb.0: |
| liveins: $x10 |
| |
| ; PseudoLD_RV32_OPT with consecutive even/odd registers (x12, x13) |
| ; CHECK-LABEL: name: expand_pseudold_valid |
| ; CHECK: liveins: $x10 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: $x12_x13 = LD_RV32 killed $x10, 0 |
| ; CHECK-NEXT: $x10 = ADD killed $x12, killed $x13 |
| ; CHECK-NEXT: PseudoRET implicit $x10 |
| $x12, $x13 = PseudoLD_RV32_OPT killed $x10, 0 |
| $x10 = ADD killed $x12, killed $x13 |
| PseudoRET implicit $x10 |
| |
| ... |
| --- |
| # Valid consecutive even/odd register pair - should expand to SD_RV32 |
| name: expand_pseudosd_valid |
| tracksRegLiveness: false |
| body: | |
| bb.0: |
| liveins: $x10, $x12, $x13 |
| |
| ; PseudoSD_RV32_OPT with consecutive even/odd registers (x12, x13) |
| ; CHECK-LABEL: name: expand_pseudosd_valid |
| ; CHECK: liveins: $x10, $x12, $x13 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: SD_RV32 killed $x12_x13, killed $x10, 0 |
| ; CHECK-NEXT: PseudoRET |
| PseudoSD_RV32_OPT killed $x12, killed $x13, killed $x10, 0 |
| PseudoRET |
| |
| ... |
| --- |
| # Invalid register pair (not consecutive) - should decompose back to LW |
| name: expand_pseudold_invalid_pair |
| tracksRegLiveness: false |
| body: | |
| bb.0: |
| liveins: $x10 |
| |
| ; PseudoLD_RV32_OPT with non-consecutive registers (x11, x13) |
| ; Should decompose back to two LW instructions |
| ; CHECK-LABEL: name: expand_pseudold_invalid_pair |
| ; CHECK: liveins: $x10 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: $x11 = LW $x10, 0 |
| ; CHECK-NEXT: $x13 = LW killed $x10, 4 |
| ; CHECK-NEXT: $x10 = ADD killed $x11, killed $x13 |
| ; CHECK-NEXT: PseudoRET implicit $x10 |
| $x11, $x13 = PseudoLD_RV32_OPT killed $x10, 0 |
| $x10 = ADD killed $x11, killed $x13 |
| PseudoRET implicit $x10 |
| |
| ... |
| --- |
| # Invalid register pair (not even/odd) - should decompose back to SW |
| name: expand_pseudosd_invalid_pair |
| tracksRegLiveness: false |
| body: | |
| bb.0: |
| liveins: $x10, $x11, $x14 |
| |
| ; PseudoSD_RV32_OPT with non-consecutive registers (x11, x14) |
| ; Should decompose back to two SW instructions |
| ; CHECK-LABEL: name: expand_pseudosd_invalid_pair |
| ; CHECK: liveins: $x10, $x11, $x14 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: SW killed $x11, $x10, 0 |
| ; CHECK-NEXT: SW killed $x14, killed $x10, 4 |
| ; CHECK-NEXT: PseudoRET |
| PseudoSD_RV32_OPT killed $x11, killed $x14, killed $x10, 0 |
| PseudoRET |
| |
| ... |
| --- |
| # Test store zero combinations - zeros don't need consecutive pairs |
| name: store_zero_combine_valid |
| tracksRegLiveness: false |
| body: | |
| bb.0: |
| liveins: $x10 |
| ; CHECK-LABEL: name: store_zero_combine_valid |
| ; CHECK: liveins: $x10 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: SD_RV32 $x0_pair, killed $x10, 0 |
| ; CHECK-NEXT: PseudoRET |
| PseudoSD_RV32_OPT $x0, $x0, killed $x10, 0 |
| PseudoRET |
| |
| ... |
| --- |
| # Test store zero base combinations - zero can't be first register unless both |
| # are zeros |
| name: store_zero_combine_invalid |
| tracksRegLiveness: false |
| body: | |
| bb.0: |
| liveins: $x10 |
| ; CHECK-LABEL: name: store_zero_combine_invalid |
| ; CHECK: liveins: $x10 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: SW killed $x0, $x10, 0 |
| ; CHECK-NEXT: SW killed $x1, killed $x10, 4 |
| ; CHECK-NEXT: PseudoRET |
| PseudoSD_RV32_OPT killed $x0, killed $x1, killed $x10, 0 |
| PseudoRET |
| |
| ... |
| --- |
| # Test invalid register pair with symbolic operands - should split back to LW |
| name: expand_pseudold_invalid_symbolic |
| tracksRegLiveness: false |
| body: | |
| bb.0: |
| liveins: $x10 |
| |
| ; PseudoLD_RV32_OPT with symbolic operand and non-consecutive registers (x11, x14) |
| ; Should decompose back to two LW instructions preserving symbolic references |
| ; CHECK-LABEL: name: expand_pseudold_invalid_symbolic |
| ; CHECK: liveins: $x10 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: $x11 = LW $x10, target-flags(riscv-lo) @global_array |
| ; CHECK-NEXT: $x14 = LW killed $x10, target-flags(riscv-lo) @global_array + 4 |
| ; CHECK-NEXT: $x10 = ADD killed $x11, killed $x14 |
| ; CHECK-NEXT: PseudoRET implicit $x10 |
| $x11, $x14 = PseudoLD_RV32_OPT killed $x10, target-flags(riscv-lo) @global_array |
| $x10 = ADD killed $x11, killed $x14 |
| PseudoRET implicit $x10 |
| |
| ... |
| --- |
| # Test overlapped first reg and base reg - should split back to LW |
| name: overlapped_first_reg_base_reg |
| tracksRegLiveness: false |
| body: | |
| bb.0: |
| liveins: $x10 |
| |
| ; PseudoLD_RV32_OPT with first destination register overlapping base register ($x11) |
| ; Should decompose back to two LW instructions when first reg overlaps base reg |
| ; CHECK-LABEL: name: overlapped_first_reg_base_reg |
| ; CHECK: liveins: $x10 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: $x14 = LW $x11, 4 |
| ; CHECK-NEXT: $x11 = LW killed $x11, 0 |
| ; CHECK-NEXT: $x10 = ADD killed $x11, killed $x14 |
| ; CHECK-NEXT: PseudoRET implicit $x10 |
| $x11, $x14 = PseudoLD_RV32_OPT killed $x11, 0 |
| $x10 = ADD killed $x11, killed $x14 |
| PseudoRET implicit $x10 |
| |
| ... |