| # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6 |
| # RUN: llc -mtriple=aarch64-linux-gnu -run-pass=aarch64-ldst-opt -o - %s | FileCheck %s |
| |
| # Test that UMOV (lane 0) + GPR store is folded into a direct FPR store |
| # when the UMOV result has no other uses. |
| |
| --- |
| # UMOVvi16_idx0 + STRHHui → STRHui, with intervening ST1i8 that kills the |
| # vector register. |
| name: umov_i16_to_fpr_store |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $q0, $x0, $x1 |
| |
| ; CHECK-LABEL: name: umov_i16_to_fpr_store |
| ; CHECK: liveins: $q0, $x0, $x1 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: ST1i8 renamable $q0, 8, killed renamable $x1 :: (store (s8)) |
| ; CHECK-NEXT: STRHui killed $h0, killed renamable $x0, 0 :: (store (s16)) |
| ; CHECK-NEXT: RET undef $lr |
| renamable $w8 = UMOVvi16_idx0 renamable $q0, 0 |
| ST1i8 killed renamable $q0, 8, killed renamable $x1 :: (store (s8)) |
| STRHHui killed renamable $w8, killed renamable $x0, 0 :: (store (s16)) |
| RET undef $lr |
| ... |
| --- |
| # UMOVvi8_idx0 + STRBBui → STRBui |
| name: umov_i8_to_fpr_store |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $q0, $x0 |
| |
| ; CHECK-LABEL: name: umov_i8_to_fpr_store |
| ; CHECK: liveins: $q0, $x0 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: STRBui killed $b0, killed renamable $x0, 0 :: (store (s8)) |
| ; CHECK-NEXT: RET undef $lr |
| renamable $w8 = UMOVvi8_idx0 killed renamable $q0, 0 |
| STRBBui killed renamable $w8, killed renamable $x0, 0 :: (store (s8)) |
| RET undef $lr |
| ... |
| --- |
| # UMOVvi32_idx0 + STRWui → STRSui |
| name: umov_i32_to_fpr_store |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $q0, $x0 |
| |
| ; CHECK-LABEL: name: umov_i32_to_fpr_store |
| ; CHECK: liveins: $q0, $x0 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: STRSui killed $s0, killed renamable $x0, 0 :: (store (s32)) |
| ; CHECK-NEXT: RET undef $lr |
| renamable $w8 = UMOVvi32_idx0 killed renamable $q0, 0 |
| STRWui killed renamable $w8, killed renamable $x0, 0 :: (store (s32)) |
| RET undef $lr |
| ... |
| --- |
| # UMOVvi64_idx0 + STRXui → STRDui |
| name: umov_i64_to_fpr_store |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $q0, $x0 |
| |
| ; CHECK-LABEL: name: umov_i64_to_fpr_store |
| ; CHECK: liveins: $q0, $x0 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: STRDui killed $d0, killed renamable $x0, 0 :: (store (s64)) |
| ; CHECK-NEXT: RET undef $lr |
| renamable $x8 = UMOVvi64_idx0 killed renamable $q0, 0 |
| STRXui killed renamable $x8, killed renamable $x0, 0 :: (store (s64)) |
| RET undef $lr |
| ... |
| --- |
| # Negative: UMOV result is modified before the store. |
| name: umov_i16_used_before_store |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $q0, $x0, $w9 |
| |
| ; CHECK-LABEL: name: umov_i16_used_before_store |
| ; CHECK: liveins: $q0, $x0, $w9 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: renamable $w8 = UMOVvi16_idx0 killed renamable $q0, 0 |
| ; CHECK-NEXT: renamable $w8 = EORWrr killed renamable $w8, killed renamable $w9 |
| ; CHECK-NEXT: STRHHui killed renamable $w8, killed renamable $x0, 0 :: (store (s16)) |
| ; CHECK-NEXT: RET undef $lr |
| renamable $w8 = UMOVvi16_idx0 killed renamable $q0, 0 |
| renamable $w8 = EORWrr killed renamable $w8, killed renamable $w9 |
| STRHHui killed renamable $w8, killed renamable $x0, 0 :: (store (s16)) |
| RET undef $lr |
| ... |
| --- |
| # Negative: The vector register is clobbered between UMOV and store. |
| name: umov_i16_vec_clobbered |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $q0, $q1, $x0 |
| |
| ; CHECK-LABEL: name: umov_i16_vec_clobbered |
| ; CHECK: liveins: $q0, $q1, $x0 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: renamable $w8 = UMOVvi16_idx0 renamable $q0, 0 |
| ; CHECK-NEXT: renamable $q0 = ORRv16i8 killed renamable $q0, killed renamable $q1 |
| ; CHECK-NEXT: STRHHui killed renamable $w8, killed renamable $x0, 0 :: (store (s16)) |
| ; CHECK-NEXT: RET undef $lr |
| renamable $w8 = UMOVvi16_idx0 renamable $q0, 0 |
| renamable $q0 = ORRv16i8 killed renamable $q0, killed renamable $q1 |
| STRHHui killed renamable $w8, killed renamable $x0, 0 :: (store (s16)) |
| RET undef $lr |
| ... |
| --- |
| # Negative: volatile store must not be folded. |
| name: umov_i16_volatile_store |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $q0, $x0 |
| |
| ; CHECK-LABEL: name: umov_i16_volatile_store |
| ; CHECK: liveins: $q0, $x0 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: renamable $w8 = UMOVvi16_idx0 killed renamable $q0, 0 |
| ; CHECK-NEXT: STRHHui killed renamable $w8, killed renamable $x0, 0 :: (volatile store (s16)) |
| ; CHECK-NEXT: RET undef $lr |
| renamable $w8 = UMOVvi16_idx0 killed renamable $q0, 0 |
| STRHHui killed renamable $w8, killed renamable $x0, 0 :: (volatile store (s16)) |
| RET undef $lr |
| ... |
| --- |
| # Negative: A sub-register of the value register ($w8) is redefined between |
| # UMOV and store, clobbering $x8 via zero-extension. |
| name: umov_i64_subreg_clobber |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $q0, $x0, $w9 |
| |
| ; CHECK-LABEL: name: umov_i64_subreg_clobber |
| ; CHECK: liveins: $q0, $x0, $w9 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: renamable $x8 = UMOVvi64_idx0 killed renamable $q0, 0 |
| ; CHECK-NEXT: renamable $w8 = ORRWrr renamable $w8, killed renamable $w9 |
| ; CHECK-NEXT: STRXui killed renamable $x8, killed renamable $x0, 0 :: (store (s64)) |
| ; CHECK-NEXT: RET undef $lr |
| renamable $x8 = UMOVvi64_idx0 killed renamable $q0, 0 |
| renamable $w8 = ORRWrr renamable $w8, killed renamable $w9 |
| STRXui killed renamable $x8, killed renamable $x0, 0 :: (store (s64)) |
| RET undef $lr |
| ... |
| --- |
| # Negative: The GPR result is not killed at the store (has a later use). |
| name: umov_i16_not_killed |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $q0, $x0, $x1 |
| |
| ; CHECK-LABEL: name: umov_i16_not_killed |
| ; CHECK: liveins: $q0, $x0, $x1 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: renamable $w8 = UMOVvi16_idx0 killed renamable $q0, 0 |
| ; CHECK-NEXT: STRHHui renamable $w8, renamable $x0, 0 :: (store (s16)) |
| ; CHECK-NEXT: STRHHui killed renamable $w8, killed renamable $x1, 0 :: (store (s16)) |
| ; CHECK-NEXT: RET undef $lr |
| renamable $w8 = UMOVvi16_idx0 killed renamable $q0, 0 |
| STRHHui renamable $w8, renamable $x0, 0 :: (store (s16)) |
| STRHHui killed renamable $w8, killed renamable $x1, 0 :: (store (s16)) |
| RET undef $lr |
| ... |
| --- |
| # Multiple folds in one block: two independent UMOV+store pairs using |
| # different vector registers and different element sizes. |
| name: umov_multiple_folds |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $q0, $q1, $x0, $x1, $x2 |
| |
| ; CHECK-LABEL: name: umov_multiple_folds |
| ; CHECK: liveins: $q0, $q1, $x0, $x1, $x2 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: renamable $q0 = ORRv16i8 killed renamable $q0, renamable $q1 |
| ; CHECK-NEXT: ST1i8 renamable $q0, 8, killed renamable $x2 :: (store (s8)) |
| ; CHECK-NEXT: STRHui killed $h0, killed renamable $x0, 0 :: (store (s16)) |
| ; CHECK-NEXT: STRSui killed $s1, killed renamable $x1, 0 :: (store (s32)) |
| ; CHECK-NEXT: RET undef $lr |
| renamable $q0 = ORRv16i8 killed renamable $q0, renamable $q1 |
| renamable $w8 = UMOVvi16_idx0 renamable $q0, 0 |
| ST1i8 killed renamable $q0, 8, killed renamable $x2 :: (store (s8)) |
| STRHHui killed renamable $w8, killed renamable $x0, 0 :: (store (s16)) |
| renamable $w9 = UMOVvi32_idx0 killed renamable $q1, 0 |
| STRWui killed renamable $w9, killed renamable $x1, 0 :: (store (s32)) |
| RET undef $lr |
| ... |
| --- |
| # Vector register still live after the store (used via $s0 alias). |
| # The fold should happen but the FPR sub-register must NOT be killed. |
| name: umov_i16_vec_live_after_store |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $q0, $x0, $x1 |
| |
| ; CHECK-LABEL: name: umov_i16_vec_live_after_store |
| ; CHECK: liveins: $q0, $x0, $x1 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: STRHui $h0, killed renamable $x0, 0 :: (store (s16)) |
| ; CHECK-NEXT: renamable $w9 = FMOVSWr renamable $s0 |
| ; CHECK-NEXT: STRWui killed renamable $w9, killed renamable $x1, 0 :: (store (s32)) |
| ; CHECK-NEXT: RET undef $lr |
| renamable $w8 = UMOVvi16_idx0 renamable $q0, 0 |
| STRHHui killed renamable $w8, killed renamable $x0, 0 :: (store (s16)) |
| renamable $w9 = FMOVSWr renamable $s0 |
| STRWui killed renamable $w9, killed renamable $x1, 0 :: (store (s32)) |
| RET undef $lr |
| ... |
| --- |
| # Unscaled immediate: STURWi → STURSi |
| name: umov_i32_unscaled |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $q0, $x0 |
| |
| ; CHECK-LABEL: name: umov_i32_unscaled |
| ; CHECK: liveins: $q0, $x0 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: STURSi killed $s0, killed renamable $x0, -4 :: (store (s32)) |
| ; CHECK-NEXT: RET undef $lr |
| renamable $w8 = UMOVvi32_idx0 killed renamable $q0, 0 |
| STURWi killed renamable $w8, killed renamable $x0, -4 :: (store (s32)) |
| RET undef $lr |
| ... |
| --- |
| # Unscaled immediate: STURHHi → STURHi |
| name: umov_i16_unscaled |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $q0, $x0 |
| |
| ; CHECK-LABEL: name: umov_i16_unscaled |
| ; CHECK: liveins: $q0, $x0 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: STURHi killed $h0, killed renamable $x0, 2 :: (store (s16)) |
| ; CHECK-NEXT: RET undef $lr |
| renamable $w8 = UMOVvi16_idx0 killed renamable $q0, 0 |
| STURHHi killed renamable $w8, killed renamable $x0, 2 :: (store (s16)) |
| RET undef $lr |
| ... |
| --- |
| # Register offset (X): STRWroX → STRSroX |
| name: umov_i32_reg_offset |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $q0, $x0, $x1 |
| |
| ; CHECK-LABEL: name: umov_i32_reg_offset |
| ; CHECK: liveins: $q0, $x0, $x1 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: STRSroX killed $s0, killed renamable $x0, killed renamable $x1, 0, 1 :: (store (s32)) |
| ; CHECK-NEXT: RET undef $lr |
| renamable $w8 = UMOVvi32_idx0 killed renamable $q0, 0 |
| STRWroX killed renamable $w8, killed renamable $x0, killed renamable $x1, 0, 1 :: (store (s32)) |
| RET undef $lr |
| ... |
| --- |
| # Negative: the store's base register overlaps with the value register |
| # (str w8, [x8, #0]). The fold must not fire because the GPR is also used |
| # as the base address. |
| name: umov_i32_val_is_base |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $q0, $x0 |
| |
| ; CHECK-LABEL: name: umov_i32_val_is_base |
| ; CHECK: liveins: $q0, $x0 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: $x8 = COPY killed renamable $x0 |
| ; CHECK-NEXT: renamable $w8 = UMOVvi32_idx0 killed renamable $q0, 0 |
| ; CHECK-NEXT: STRWui killed renamable $w8, renamable $x8, 0 :: (store (s32)) |
| ; CHECK-NEXT: RET undef $lr |
| $x8 = COPY killed renamable $x0 |
| renamable $w8 = UMOVvi32_idx0 killed renamable $q0, 0 |
| STRWui killed renamable $w8, renamable $x8, 0 :: (store (s32)) |
| RET undef $lr |
| ... |
| --- |
| # Negative: the store's index register overlaps with the value register |
| # (strh w8, [x0, w8, sxtw]). |
| name: umov_i16_val_is_index |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $q0, $x0 |
| |
| ; CHECK-LABEL: name: umov_i16_val_is_index |
| ; CHECK: liveins: $q0, $x0 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: renamable $w8 = UMOVvi16_idx0 killed renamable $q0, 0 |
| ; CHECK-NEXT: STRHHroW killed renamable $w8, killed renamable $x0, renamable $w8, 0, 0 :: (store (s16)) |
| ; CHECK-NEXT: RET undef $lr |
| renamable $w8 = UMOVvi16_idx0 killed renamable $q0, 0 |
| STRHHroW killed renamable $w8, killed renamable $x0, renamable $w8, 0, 0 :: (store (s16)) |
| RET undef $lr |
| ... |
| --- |
| # Register offset (W): STRHHroW → STRHroW |
| name: umov_i16_reg_offset_w |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $q0, $x0, $w1 |
| |
| ; CHECK-LABEL: name: umov_i16_reg_offset_w |
| ; CHECK: liveins: $q0, $x0, $w1 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: STRHroW killed $h0, killed renamable $x0, killed renamable $w1, 0, 0 :: (store (s16)) |
| ; CHECK-NEXT: RET undef $lr |
| renamable $w8 = UMOVvi16_idx0 killed renamable $q0, 0 |
| STRHHroW killed renamable $w8, killed renamable $x0, killed renamable $w1, 0, 0 :: (store (s16)) |
| RET undef $lr |
| ... |
| --- |
| # UMOVvi8_idx0 + STRWui != STRBui |
| name: umov_i8_to_i32_fpr_store |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $q0, $x0 |
| |
| ; CHECK-LABEL: name: umov_i8_to_i32_fpr_store |
| ; CHECK: liveins: $q0, $x0 |
| ; CHECK-NEXT: {{ $}} |
| ; CHECK-NEXT: renamable $w8 = UMOVvi8_idx0 killed renamable $q0, 0 |
| ; CHECK-NEXT: STRWui killed renamable $w8, killed renamable $x0, 0 :: (store (s32)) |
| ; CHECK-NEXT: RET undef $lr |
| renamable $w8 = UMOVvi8_idx0 killed renamable $q0, 0 |
| STRWui killed renamable $w8, killed renamable $x0, 0 :: (store (s32)) |
| RET undef $lr |
| ... |