[AArch64] Remove redundant fmovs when bitcasting from ext/trunc of bitcast (#175983)
The following can be used to reinterpret & extend a float, returning the
result as a double with the upper bits cleared:
return bit_cast<double>((uint64_t)bit_cast<uint32_t>(x))
this currently results in two fmovs being generated, i.e.
fmov w8, s0
fmov d0, x8
ret
This patch adds a pattern to improve this by using a single `fmov s0, s0`
instead, and also for the case where the integer value is truncated.
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index 013bc11..5c64d14 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -7806,6 +7806,12 @@
def : Pat<(v8i8 (bitconvert (i64 (zext GPR32:$Rn)))),
(SUBREG_TO_REG (i32 0), (f32 (FMOVWSr GPR32:$Rn)), ssub)>;
+def: Pat<(f64 (bitconvert (i64 (zext (i32 (bitconvert (f32 FPR32:$Rn))))))),
+ (SUBREG_TO_REG (i32 0), (f32 (FMOVSr FPR32:$Rn)), ssub)>;
+
+def: Pat<(f32 (bitconvert (i32 (trunc (i64 (bitconvert (f64 FPR64:$Rn))))))),
+ (f32 (EXTRACT_SUBREG FPR64:$Rn, ssub))>;
+
def : Pat<(v8i16 (vec_ins_or_scal_vec GPR32:$Rn)),
(SUBREG_TO_REG (i32 0),
(f32 (COPY_TO_REGCLASS GPR32:$Rn, FPR32)), ssub)>;
diff --git a/llvm/test/CodeGen/AArch64/arm64-cvt-bitcast.ll b/llvm/test/CodeGen/AArch64/arm64-cvt-bitcast.ll
new file mode 100644
index 0000000..f698449
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/arm64-cvt-bitcast.ll
@@ -0,0 +1,26 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple aarch64-unknown-unknown | FileCheck %s --check-prefixes=CHECK
+
+define double @extend(float %x) {
+; CHECK-LABEL: extend:
+; CHECK: // %bb.0: // %entry
+; CHECK-NEXT: fmov s0, s0
+; CHECK-NEXT: ret
+entry:
+ %0 = bitcast float %x to i32
+ %conv = zext i32 %0 to i64
+ %1 = bitcast i64 %conv to double
+ ret double %1
+}
+
+define float @truncate(double %x) {
+; CHECK-LABEL: truncate:
+; CHECK: // %bb.0: // %entry
+; CHECK-NEXT: // kill: def $s0 killed $s0 killed $d0
+; CHECK-NEXT: ret
+entry:
+ %0 = bitcast double %x to i64
+ %conv = trunc i64 %0 to i32
+ %1 = bitcast i32 %conv to float
+ ret float %1
+}