blob: 0cd7ee88d83a6bdac8661a0001c41e917415576b [file]
# 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
...