[llvm] [AArch64][SelectionDAG] Avoid cross-bank copy for NEON vcvtfp2fx result (PR #210275)
Kieran B via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 29 07:32:30 PDT 2026
https://github.com/kieroxide updated https://github.com/llvm/llvm-project/pull/210275
>From 7c1cc937cb597e12b1ebd90a8e5db4ee950f47b3 Mon Sep 17 00:00:00 2001
From: Kieran Bailey <kieran.bailey at arm.com>
Date: Thu, 16 Jul 2026 15:15:56 +0000
Subject: [PATCH 1/5] [AArch64][SelectionDAG] Avoid cross-bank copies for NEON
vcvtfp2fx results
- Add SelectionDAG-only patterns for NEON vcvtfp2fx so integer result select GPR instead of FPR, avoiding the need for cross-bank copies.
- Bitcast uses still select the FP/SIMD-register forms where available.
- Add complexity used to prioritize the new patterns over the generic FP/SIMD patterns which GlobalIsel still uses
- Add testing for GPR-resulting and FPR-resulting patterns
- Add missing tests in fp16_intrinsic_scalar_2op.ll: test_vcvth_n_u64_f16_1 and test_vcvth_n_u64_f16_16
A follow-up patch will also include the fix for GlobalIsel
---
llvm/lib/Target/AArch64/AArch64InstrInfo.td | 43 ++++
.../AArch64/fp16_intrinsic_scalar_2op.ll | 215 ++++++++++++++----
.../CodeGen/AArch64/neon-scalar-vcvtfp2fx.ll | 78 +++++++
3 files changed, 292 insertions(+), 44 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/neon-scalar-vcvtfp2fx.ll
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index a660a3f11bdb2..e1b11420cb74f 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -9138,6 +9138,49 @@ defm FCVTZS : SIMDFPScalarRShift<0, 0b11111, "fcvtzs">;
defm FCVTZU : SIMDFPScalarRShift<1, 0b11111, "fcvtzu">;
defm SCVTF : SIMDFPScalarRShift<0, 0b11100, "scvtf">;
defm UCVTF : SIMDFPScalarRShift<1, 0b11100, "ucvtf">;
+
+// Transformation for SIMD shift imm to fixed point imm for FPR-to-GPR result
+def fixedpoint_scalar_xform : SDNodeXForm<timm, [{
+ (void)N;
+ return V;
+}]>;
+
+multiclass FPToFixedScalarPats<SDPatternOperator OpN, string INST > {
+// Allow integer result to remain in GPR registers
+// SelectionDAG-only as GIsel doesn't import fixedpoint_scalar_xform
+// Give priority over generic FPR fallback
+let AddedComplexity = 1 in {
+ def : Pat<(i32 (OpN FPR32:$Rn, vecshiftR32:$imm)),
+ (!cast<Instruction>(INST # "SWSri") FPR32:$Rn, (fixedpoint_scalar_xform vecshiftR32:$imm))>;
+ def : Pat<(i64 (OpN (f64 FPR64:$Rn), vecshiftR64:$imm)),
+ (!cast<Instruction>(INST # "SXDri") FPR64:$Rn, (fixedpoint_scalar_xform vecshiftR64:$imm))>;
+
+ def : Pat<(i32 (OpN (f16 FPR16:$Rn), vecshiftR32:$imm)),
+ (!cast<Instruction>(INST # "SWHri") FPR16:$Rn, (fixedpoint_scalar_xform vecshiftR32:$imm))>;
+ def : Pat<(i64 (OpN (f16 FPR16:$Rn), vecshiftR64:$imm)),
+ (!cast<Instruction>(INST # "SXHri") FPR16:$Rn, (fixedpoint_scalar_xform vecshiftR64:$imm))>;
+ }
+
+ // Bitcast results kept in FP/SIMD registers.
+ def : Pat<(f32 (bitconvert(i32 (OpN FPR32:$Rn, vecshiftR32:$imm)))),
+ (!cast<Instruction>(INST # "s") FPR32:$Rn, vecshiftR32:$imm)>;
+ def : Pat<(f64 (bitconvert(i64 (OpN (f64 FPR64:$Rn), vecshiftR64:$imm)))),
+ (!cast<Instruction>(INST # "d") FPR64:$Rn, vecshiftR64:$imm)>;
+
+ def : Pat<(f32(bitconvert (i32 (OpN (f16 FPR16:$Rn), vecshiftR32:$imm)))),
+ (f32 (INSERT_SUBREG
+ (f32 (IMPLICIT_DEF)),
+ (!cast<Instruction>(INST # "h") FPR16:$Rn, vecshiftR32:$imm),
+ hsub))>;
+ def : Pat<(f64 (bitconvert (i64 (OpN (f16 FPR16:$Rn), vecshiftR64:$imm)))),
+ (f64 (INSERT_SUBREG
+ (f64 (IMPLICIT_DEF)),
+ (!cast<Instruction>(INST # "h") FPR16:$Rn, vecshiftR64:$imm),
+ hsub))>;
+}
+defm : FPToFixedScalarPats<int_aarch64_neon_vcvtfp2fxs, "FCVTZS">;
+defm : FPToFixedScalarPats<int_aarch64_neon_vcvtfp2fxu, "FCVTZU">;
+
// Codegen patterns for the above. We don't put these directly on the
// instructions because TableGen's type inference can't handle the truth.
// Having the same base pattern for fp <--> int totally freaks it out.
diff --git a/llvm/test/CodeGen/AArch64/fp16_intrinsic_scalar_2op.ll b/llvm/test/CodeGen/AArch64/fp16_intrinsic_scalar_2op.ll
index da70599483a63..f44e1a6fa9970 100644
--- a/llvm/test/CodeGen/AArch64/fp16_intrinsic_scalar_2op.ll
+++ b/llvm/test/CodeGen/AArch64/fp16_intrinsic_scalar_2op.ll
@@ -185,6 +185,7 @@ declare i32 @llvm.aarch64.neon.vcvtfp2fxs.i32.f16(half, i32) #1
declare i64 @llvm.aarch64.neon.vcvtfp2fxs.i64.f16(half, i32) #1
declare half @llvm.aarch64.neon.vcvtfxu2fp.f16.i32(i32, i32) #1
declare i32 @llvm.aarch64.neon.vcvtfp2fxu.i32.f16(half, i32) #1
+declare i64 @llvm.aarch64.neon.vcvtfp2fxu.i64.f16(half, i32) #1
define dso_local half @test_vcvth_n_f16_s16_1(i16 %a) {
; CHECK-SD-LABEL: test_vcvth_n_f16_s16_1:
@@ -247,11 +248,16 @@ entry:
}
define dso_local i16 @test_vcvth_n_s16_f16_1(half %a) {
-; CHECK-LABEL: test_vcvth_n_s16_f16_1:
-; CHECK: // %bb.0: // %entry
-; CHECK-NEXT: fcvtzs h0, h0, #1
-; CHECK-NEXT: fmov w0, s0
-; CHECK-NEXT: ret
+; CHECK-SD-LABEL: test_vcvth_n_s16_f16_1:
+; CHECK-SD: // %bb.0: // %entry
+; CHECK-SD-NEXT: fcvtzs w0, h0, #1
+; CHECK-SD-NEXT: ret
+;
+; CHECK-GI-LABEL: test_vcvth_n_s16_f16_1:
+; CHECK-GI: // %bb.0: // %entry
+; CHECK-GI-NEXT: fcvtzs h0, h0, #1
+; CHECK-GI-NEXT: fmov w0, s0
+; CHECK-GI-NEXT: ret
entry:
%fcvth_n = tail call i32 @llvm.aarch64.neon.vcvtfp2fxs.i32.f16(half %a, i32 1)
%0 = trunc i32 %fcvth_n to i16
@@ -259,11 +265,16 @@ entry:
}
define dso_local i16 @test_vcvth_n_s16_f16_16(half %a) {
-; CHECK-LABEL: test_vcvth_n_s16_f16_16:
-; CHECK: // %bb.0: // %entry
-; CHECK-NEXT: fcvtzs h0, h0, #16
-; CHECK-NEXT: fmov w0, s0
-; CHECK-NEXT: ret
+; CHECK-SD-LABEL: test_vcvth_n_s16_f16_16:
+; CHECK-SD: // %bb.0: // %entry
+; CHECK-SD-NEXT: fcvtzs w0, h0, #16
+; CHECK-SD-NEXT: ret
+;
+; CHECK-GI-LABEL: test_vcvth_n_s16_f16_16:
+; CHECK-GI: // %bb.0: // %entry
+; CHECK-GI-NEXT: fcvtzs h0, h0, #16
+; CHECK-GI-NEXT: fmov w0, s0
+; CHECK-GI-NEXT: ret
entry:
%fcvth_n = tail call i32 @llvm.aarch64.neon.vcvtfp2fxs.i32.f16(half %a, i32 16)
%0 = trunc i32 %fcvth_n to i16
@@ -271,47 +282,89 @@ entry:
}
define dso_local i32 @test_vcvth_n_s32_f16_1(half %a) {
-; CHECK-LABEL: test_vcvth_n_s32_f16_1:
-; CHECK: // %bb.0: // %entry
-; CHECK-NEXT: fcvtzs h0, h0, #1
-; CHECK-NEXT: fmov w0, s0
-; CHECK-NEXT: ret
+; CHECK-SD-LABEL: test_vcvth_n_s32_f16_1:
+; CHECK-SD: // %bb.0: // %entry
+; CHECK-SD-NEXT: fcvtzs w0, h0, #1
+; CHECK-SD-NEXT: ret
+;
+; CHECK-GI-LABEL: test_vcvth_n_s32_f16_1:
+; CHECK-GI: // %bb.0: // %entry
+; CHECK-GI-NEXT: fcvtzs h0, h0, #1
+; CHECK-GI-NEXT: fmov w0, s0
+; CHECK-GI-NEXT: ret
entry:
%vcvth_n_s32_f16 = tail call i32 @llvm.aarch64.neon.vcvtfp2fxs.i32.f16(half %a, i32 1)
ret i32 %vcvth_n_s32_f16
}
define dso_local i32 @test_vcvth_n_s32_f16_16(half %a) {
-; CHECK-LABEL: test_vcvth_n_s32_f16_16:
+; CHECK-SD-LABEL: test_vcvth_n_s32_f16_16:
+; CHECK-SD: // %bb.0: // %entry
+; CHECK-SD-NEXT: fcvtzs w0, h0, #16
+; CHECK-SD-NEXT: ret
+;
+; CHECK-GI-LABEL: test_vcvth_n_s32_f16_16:
+; CHECK-GI: // %bb.0: // %entry
+; CHECK-GI-NEXT: fcvtzs h0, h0, #16
+; CHECK-GI-NEXT: fmov w0, s0
+; CHECK-GI-NEXT: ret
+entry:
+ %vcvth_n_s32_f16 = tail call i32 @llvm.aarch64.neon.vcvtfp2fxs.i32.f16(half %a, i32 16)
+ ret i32 %vcvth_n_s32_f16
+}
+
+define dso_local float @test_vcvth_n_s32_f16_fpr(half %a) {
+; CHECK-LABEL: test_vcvth_n_s32_f16_fpr:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: fcvtzs h0, h0, #16
-; CHECK-NEXT: fmov w0, s0
; CHECK-NEXT: ret
entry:
%vcvth_n_s32_f16 = tail call i32 @llvm.aarch64.neon.vcvtfp2fxs.i32.f16(half %a, i32 16)
- ret i32 %vcvth_n_s32_f16
+ %bc = bitcast i32 %vcvth_n_s32_f16 to float
+ ret float %bc
}
define dso_local i64 @test_vcvth_n_s64_f16_1(half %a) {
-; CHECK-LABEL: test_vcvth_n_s64_f16_1:
-; CHECK: // %bb.0: // %entry
-; CHECK-NEXT: fcvtzs h0, h0, #1
-; CHECK-NEXT: fmov x0, d0
-; CHECK-NEXT: ret
+; CHECK-SD-LABEL: test_vcvth_n_s64_f16_1:
+; CHECK-SD: // %bb.0: // %entry
+; CHECK-SD-NEXT: fcvtzs x0, h0, #1
+; CHECK-SD-NEXT: ret
+;
+; CHECK-GI-LABEL: test_vcvth_n_s64_f16_1:
+; CHECK-GI: // %bb.0: // %entry
+; CHECK-GI-NEXT: fcvtzs h0, h0, #1
+; CHECK-GI-NEXT: fmov x0, d0
+; CHECK-GI-NEXT: ret
entry:
%vcvth_n_s64_f16 = tail call i64 @llvm.aarch64.neon.vcvtfp2fxs.i64.f16(half %a, i32 1)
ret i64 %vcvth_n_s64_f16
}
define dso_local i64 @test_vcvth_n_s64_f16_32(half %a) {
-; CHECK-LABEL: test_vcvth_n_s64_f16_32:
+; CHECK-SD-LABEL: test_vcvth_n_s64_f16_32:
+; CHECK-SD: // %bb.0: // %entry
+; CHECK-SD-NEXT: fcvtzs x0, h0, #32
+; CHECK-SD-NEXT: ret
+;
+; CHECK-GI-LABEL: test_vcvth_n_s64_f16_32:
+; CHECK-GI: // %bb.0: // %entry
+; CHECK-GI-NEXT: fcvtzs h0, h0, #32
+; CHECK-GI-NEXT: fmov x0, d0
+; CHECK-GI-NEXT: ret
+entry:
+ %vcvth_n_s64_f16 = tail call i64 @llvm.aarch64.neon.vcvtfp2fxs.i64.f16(half %a, i32 32)
+ ret i64 %vcvth_n_s64_f16
+}
+
+define dso_local double @test_vcvth_n_s64_f16_fpr(half %a) {
+; CHECK-LABEL: test_vcvth_n_s64_f16_fpr:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: fcvtzs h0, h0, #32
-; CHECK-NEXT: fmov x0, d0
; CHECK-NEXT: ret
entry:
%vcvth_n_s64_f16 = tail call i64 @llvm.aarch64.neon.vcvtfp2fxs.i64.f16(half %a, i32 32)
- ret i64 %vcvth_n_s64_f16
+ %bc = bitcast i64 %vcvth_n_s64_f16 to double
+ ret double %bc
}
define dso_local half @test_vcvth_n_f16_u16_1(i16 %a) {
@@ -375,11 +428,16 @@ entry:
}
define dso_local i16 @test_vcvth_n_u16_f16_1(half %a) {
-; CHECK-LABEL: test_vcvth_n_u16_f16_1:
-; CHECK: // %bb.0: // %entry
-; CHECK-NEXT: fcvtzu h0, h0, #1
-; CHECK-NEXT: fmov w0, s0
-; CHECK-NEXT: ret
+; CHECK-SD-LABEL: test_vcvth_n_u16_f16_1:
+; CHECK-SD: // %bb.0: // %entry
+; CHECK-SD-NEXT: fcvtzu w0, h0, #1
+; CHECK-SD-NEXT: ret
+;
+; CHECK-GI-LABEL: test_vcvth_n_u16_f16_1:
+; CHECK-GI: // %bb.0: // %entry
+; CHECK-GI-NEXT: fcvtzu h0, h0, #1
+; CHECK-GI-NEXT: fmov w0, s0
+; CHECK-GI-NEXT: ret
entry:
%fcvth_n = tail call i32 @llvm.aarch64.neon.vcvtfp2fxu.i32.f16(half %a, i32 1)
%0 = trunc i32 %fcvth_n to i16
@@ -387,11 +445,16 @@ entry:
}
define dso_local i16 @test_vcvth_n_u16_f16_16(half %a) {
-; CHECK-LABEL: test_vcvth_n_u16_f16_16:
-; CHECK: // %bb.0: // %entry
-; CHECK-NEXT: fcvtzu h0, h0, #16
-; CHECK-NEXT: fmov w0, s0
-; CHECK-NEXT: ret
+; CHECK-SD-LABEL: test_vcvth_n_u16_f16_16:
+; CHECK-SD: // %bb.0: // %entry
+; CHECK-SD-NEXT: fcvtzu w0, h0, #16
+; CHECK-SD-NEXT: ret
+;
+; CHECK-GI-LABEL: test_vcvth_n_u16_f16_16:
+; CHECK-GI: // %bb.0: // %entry
+; CHECK-GI-NEXT: fcvtzu h0, h0, #16
+; CHECK-GI-NEXT: fmov w0, s0
+; CHECK-GI-NEXT: ret
entry:
%fcvth_n = tail call i32 @llvm.aarch64.neon.vcvtfp2fxu.i32.f16(half %a, i32 16)
%0 = trunc i32 %fcvth_n to i16
@@ -399,25 +462,89 @@ entry:
}
define dso_local i32 @test_vcvth_n_u32_f16_1(half %a) {
-; CHECK-LABEL: test_vcvth_n_u32_f16_1:
-; CHECK: // %bb.0: // %entry
-; CHECK-NEXT: fcvtzu h0, h0, #1
-; CHECK-NEXT: fmov w0, s0
-; CHECK-NEXT: ret
+; CHECK-SD-LABEL: test_vcvth_n_u32_f16_1:
+; CHECK-SD: // %bb.0: // %entry
+; CHECK-SD-NEXT: fcvtzu w0, h0, #1
+; CHECK-SD-NEXT: ret
+;
+; CHECK-GI-LABEL: test_vcvth_n_u32_f16_1:
+; CHECK-GI: // %bb.0: // %entry
+; CHECK-GI-NEXT: fcvtzu h0, h0, #1
+; CHECK-GI-NEXT: fmov w0, s0
+; CHECK-GI-NEXT: ret
entry:
%vcvth_n_u32_f16 = tail call i32 @llvm.aarch64.neon.vcvtfp2fxu.i32.f16(half %a, i32 1)
ret i32 %vcvth_n_u32_f16
}
define dso_local i32 @test_vcvth_n_u32_f16_16(half %a) {
-; CHECK-LABEL: test_vcvth_n_u32_f16_16:
+; CHECK-SD-LABEL: test_vcvth_n_u32_f16_16:
+; CHECK-SD: // %bb.0: // %entry
+; CHECK-SD-NEXT: fcvtzu w0, h0, #16
+; CHECK-SD-NEXT: ret
+;
+; CHECK-GI-LABEL: test_vcvth_n_u32_f16_16:
+; CHECK-GI: // %bb.0: // %entry
+; CHECK-GI-NEXT: fcvtzu h0, h0, #16
+; CHECK-GI-NEXT: fmov w0, s0
+; CHECK-GI-NEXT: ret
+entry:
+ %vcvth_n_u32_f16 = tail call i32 @llvm.aarch64.neon.vcvtfp2fxu.i32.f16(half %a, i32 16)
+ ret i32 %vcvth_n_u32_f16
+}
+
+define dso_local float @test_vcvth_n_u32_f16_fpr(half %a) {
+; CHECK-LABEL: test_vcvth_n_u32_f16_fpr:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: fcvtzu h0, h0, #16
-; CHECK-NEXT: fmov w0, s0
; CHECK-NEXT: ret
entry:
%vcvth_n_u32_f16 = tail call i32 @llvm.aarch64.neon.vcvtfp2fxu.i32.f16(half %a, i32 16)
- ret i32 %vcvth_n_u32_f16
+ %bc = bitcast i32 %vcvth_n_u32_f16 to float
+ ret float %bc
+}
+
+define dso_local i64 @test_vcvth_n_u64_f16_1(half %a) {
+; CHECK-SD-LABEL: test_vcvth_n_u64_f16_1:
+; CHECK-SD: // %bb.0: // %entry
+; CHECK-SD-NEXT: fcvtzu x0, h0, #1
+; CHECK-SD-NEXT: ret
+;
+; CHECK-GI-LABEL: test_vcvth_n_u64_f16_1:
+; CHECK-GI: // %bb.0: // %entry
+; CHECK-GI-NEXT: fcvtzu h0, h0, #1
+; CHECK-GI-NEXT: fmov x0, d0
+; CHECK-GI-NEXT: ret
+entry:
+ %vcvth_n_u64_f16 = tail call i64 @llvm.aarch64.neon.vcvtfp2fxu.i64.f16(half %a, i32 1)
+ ret i64 %vcvth_n_u64_f16
+}
+
+define dso_local i64 @test_vcvth_n_u64_f16_16(half %a) {
+; CHECK-SD-LABEL: test_vcvth_n_u64_f16_16:
+; CHECK-SD: // %bb.0: // %entry
+; CHECK-SD-NEXT: fcvtzu x0, h0, #16
+; CHECK-SD-NEXT: ret
+;
+; CHECK-GI-LABEL: test_vcvth_n_u64_f16_16:
+; CHECK-GI: // %bb.0: // %entry
+; CHECK-GI-NEXT: fcvtzu h0, h0, #16
+; CHECK-GI-NEXT: fmov x0, d0
+; CHECK-GI-NEXT: ret
+entry:
+ %vcvth_n_u64_f16 = tail call i64 @llvm.aarch64.neon.vcvtfp2fxu.i64.f16(half %a, i32 16)
+ ret i64 %vcvth_n_u64_f16
+}
+
+define dso_local double @test_vcvth_n_u64_f16_fpr(half %a) {
+; CHECK-LABEL: test_vcvth_n_u64_f16_fpr:
+; CHECK: // %bb.0: // %entry
+; CHECK-NEXT: fcvtzu h0, h0, #16
+; CHECK-NEXT: ret
+entry:
+ %vcvth_n_u64_f16 = tail call i64 @llvm.aarch64.neon.vcvtfp2fxu.i64.f16(half %a, i32 16)
+ %bc = bitcast i64 %vcvth_n_u64_f16 to double
+ ret double %bc
}
define dso_local i16 @vcageh_f16_test(half %a, half %b) {
diff --git a/llvm/test/CodeGen/AArch64/neon-scalar-vcvtfp2fx.ll b/llvm/test/CodeGen/AArch64/neon-scalar-vcvtfp2fx.ll
new file mode 100644
index 0000000000000..5ca1d8c44cf58
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/neon-scalar-vcvtfp2fx.ll
@@ -0,0 +1,78 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=aarch64 -global-isel=0 < %s | FileCheck %s
+
+define i32 @vcvtfp2fxs_f32_i32(float %a) {
+; CHECK-LABEL: vcvtfp2fxs_f32_i32:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzs w0, s0, #16
+; CHECK-NEXT: ret
+ %r = call i32 @llvm.aarch64.neon.vcvtfp2fxs.i32.f32(float %a, i32 16)
+ ret i32 %r
+}
+
+define float @vcvtfp2fxs_f32_i32_bitcast(float %a) {
+; CHECK-LABEL: vcvtfp2fxs_f32_i32_bitcast:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzs s0, s0, #16
+; CHECK-NEXT: ret
+ %r = call i32 @llvm.aarch64.neon.vcvtfp2fxs.i32.f32(float %a, i32 16)
+ %b = bitcast i32 %r to float
+ ret float %b
+}
+
+define i32 @vcvtfp2fxu_f32_i32(float %a) {
+; CHECK-LABEL: vcvtfp2fxu_f32_i32:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzu w0, s0, #16
+; CHECK-NEXT: ret
+ %r = call i32 @llvm.aarch64.neon.vcvtfp2fxu.i32.f32(float %a, i32 16)
+ ret i32 %r
+}
+
+define float @vcvtfp2fxu_f32_i32_bitcast(float %a) {
+; CHECK-LABEL: vcvtfp2fxu_f32_i32_bitcast:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzu s0, s0, #16
+; CHECK-NEXT: ret
+ %r = call i32 @llvm.aarch64.neon.vcvtfp2fxu.i32.f32(float %a, i32 16)
+ %b = bitcast i32 %r to float
+ ret float %b
+}
+
+define i64 @vcvtfp2fxs_f64_i64(double %a) {
+; CHECK-LABEL: vcvtfp2fxs_f64_i64:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzs x0, d0, #16
+; CHECK-NEXT: ret
+ %r = call i64 @llvm.aarch64.neon.vcvtfp2fxs.i64.f64(double %a, i32 16)
+ ret i64 %r
+}
+
+define double @vcvtfp2fxs_f64_i64_bitcast(double %a) {
+; CHECK-LABEL: vcvtfp2fxs_f64_i64_bitcast:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzs d0, d0, #16
+; CHECK-NEXT: ret
+ %r = call i64 @llvm.aarch64.neon.vcvtfp2fxs.i64.f64(double %a, i32 16)
+ %b = bitcast i64 %r to double
+ ret double %b
+}
+
+define i64 @vcvtfp2fxu_f64_i64(double %a) {
+; CHECK-LABEL: vcvtfp2fxu_f64_i64:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzu x0, d0, #16
+; CHECK-NEXT: ret
+ %r = call i64 @llvm.aarch64.neon.vcvtfp2fxu.i64.f64(double %a, i32 16)
+ ret i64 %r
+}
+
+define double @vcvtfp2fxu_f64_i64_bitcast(double %a) {
+; CHECK-LABEL: vcvtfp2fxu_f64_i64_bitcast:
+; CHECK: // %bb.0:
+; CHECK-NEXT: fcvtzu d0, d0, #16
+; CHECK-NEXT: ret
+ %r = call i64 @llvm.aarch64.neon.vcvtfp2fxu.i64.f64(double %a, i32 16)
+ %b = bitcast i64 %r to double
+ ret double %b
+}
>From bf9501ee710f5a504eb9a595577a00cbdcb8476b Mon Sep 17 00:00:00 2001
From: Kieran Bailey <kieran.bailey at arm.com>
Date: Mon, 20 Jul 2026 13:54:46 +0000
Subject: [PATCH 2/5] Addressing Review Comments
- Moved the existing FPR fallbacks into the multiclass
- Removed unnecessary AddedComplexity
- Restructured comments based on review suggestions
---
llvm/lib/Target/AArch64/AArch64InstrInfo.td | 64 +++++++--------------
1 file changed, 21 insertions(+), 43 deletions(-)
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index e1b11420cb74f..4f975385da8d4 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -9146,27 +9146,21 @@ def fixedpoint_scalar_xform : SDNodeXForm<timm, [{
}]>;
multiclass FPToFixedScalarPats<SDPatternOperator OpN, string INST > {
-// Allow integer result to remain in GPR registers
-// SelectionDAG-only as GIsel doesn't import fixedpoint_scalar_xform
-// Give priority over generic FPR fallback
-let AddedComplexity = 1 in {
+ // Allow integer result to remain in GPR register
def : Pat<(i32 (OpN FPR32:$Rn, vecshiftR32:$imm)),
(!cast<Instruction>(INST # "SWSri") FPR32:$Rn, (fixedpoint_scalar_xform vecshiftR32:$imm))>;
def : Pat<(i64 (OpN (f64 FPR64:$Rn), vecshiftR64:$imm)),
(!cast<Instruction>(INST # "SXDri") FPR64:$Rn, (fixedpoint_scalar_xform vecshiftR64:$imm))>;
-
def : Pat<(i32 (OpN (f16 FPR16:$Rn), vecshiftR32:$imm)),
(!cast<Instruction>(INST # "SWHri") FPR16:$Rn, (fixedpoint_scalar_xform vecshiftR32:$imm))>;
def : Pat<(i64 (OpN (f16 FPR16:$Rn), vecshiftR64:$imm)),
(!cast<Instruction>(INST # "SXHri") FPR16:$Rn, (fixedpoint_scalar_xform vecshiftR64:$imm))>;
- }
- // Bitcast results kept in FP/SIMD registers.
+ // Explicit Bitcast results kept in FP/SIMD registers.
def : Pat<(f32 (bitconvert(i32 (OpN FPR32:$Rn, vecshiftR32:$imm)))),
(!cast<Instruction>(INST # "s") FPR32:$Rn, vecshiftR32:$imm)>;
def : Pat<(f64 (bitconvert(i64 (OpN (f64 FPR64:$Rn), vecshiftR64:$imm)))),
(!cast<Instruction>(INST # "d") FPR64:$Rn, vecshiftR64:$imm)>;
-
def : Pat<(f32(bitconvert (i32 (OpN (f16 FPR16:$Rn), vecshiftR32:$imm)))),
(f32 (INSERT_SUBREG
(f32 (IMPLICIT_DEF)),
@@ -9177,27 +9171,31 @@ let AddedComplexity = 1 in {
(f64 (IMPLICIT_DEF)),
(!cast<Instruction>(INST # "h") FPR16:$Rn, vecshiftR64:$imm),
hsub))>;
+
+ // FPR fallback patterns
+ def : Pat<(i32 (OpN FPR32:$Rn, vecshiftR32:$imm)),
+ (!cast<Instruction>(INST # "s") FPR32:$Rn, vecshiftR32:$imm)>;
+ def : Pat<(i64 (OpN (f64 FPR64:$Rn), vecshiftR64:$imm)),
+ (!cast<Instruction>(INST # "d") FPR64:$Rn, vecshiftR64:$imm)>;
+ def : Pat<(v1i64 (OpN (v1f64 FPR64:$Rn), vecshiftR64:$imm)),
+ (!cast<Instruction>(INST # "d") FPR64:$Rn, vecshiftR64:$imm)>;
+ def : Pat<(i32 (OpN (f16 FPR16:$Rn), vecshiftR32:$imm)),
+ (i32 (INSERT_SUBREG
+ (i32 (IMPLICIT_DEF)),
+ (!cast<Instruction>(INST # "h") FPR16:$Rn, vecshiftR32:$imm),
+ hsub))>;
+ def : Pat<(i64 (OpN (f16 FPR16:$Rn), vecshiftR64:$imm)),
+ (i64 (INSERT_SUBREG
+ (i64 (IMPLICIT_DEF)),
+ (!cast<Instruction>(INST # "h") FPR16:$Rn, vecshiftR64:$imm),
+ hsub))>;
}
defm : FPToFixedScalarPats<int_aarch64_neon_vcvtfp2fxs, "FCVTZS">;
defm : FPToFixedScalarPats<int_aarch64_neon_vcvtfp2fxu, "FCVTZU">;
-// Codegen patterns for the above. We don't put these directly on the
+// Codegen patterns for SCVTF and UCVTF. We don't put these directly on the
// instructions because TableGen's type inference can't handle the truth.
// Having the same base pattern for fp <--> int totally freaks it out.
-def : Pat<(int_aarch64_neon_vcvtfp2fxs FPR32:$Rn, vecshiftR32:$imm),
- (FCVTZSs FPR32:$Rn, vecshiftR32:$imm)>;
-def : Pat<(int_aarch64_neon_vcvtfp2fxu FPR32:$Rn, vecshiftR32:$imm),
- (FCVTZUs FPR32:$Rn, vecshiftR32:$imm)>;
-def : Pat<(i64 (int_aarch64_neon_vcvtfp2fxs (f64 FPR64:$Rn), vecshiftR64:$imm)),
- (FCVTZSd FPR64:$Rn, vecshiftR64:$imm)>;
-def : Pat<(i64 (int_aarch64_neon_vcvtfp2fxu (f64 FPR64:$Rn), vecshiftR64:$imm)),
- (FCVTZUd FPR64:$Rn, vecshiftR64:$imm)>;
-def : Pat<(v1i64 (int_aarch64_neon_vcvtfp2fxs (v1f64 FPR64:$Rn),
- vecshiftR64:$imm)),
- (FCVTZSd FPR64:$Rn, vecshiftR64:$imm)>;
-def : Pat<(v1i64 (int_aarch64_neon_vcvtfp2fxu (v1f64 FPR64:$Rn),
- vecshiftR64:$imm)),
- (FCVTZUd FPR64:$Rn, vecshiftR64:$imm)>;
def : Pat<(int_aarch64_neon_vcvtfxu2fp FPR32:$Rn, vecshiftR32:$imm),
(UCVTFs FPR32:$Rn, vecshiftR32:$imm)>;
def : Pat<(f64 (int_aarch64_neon_vcvtfxu2fp (i64 FPR64:$Rn), vecshiftR64:$imm)),
@@ -9229,26 +9227,6 @@ def : Pat<(f16 (int_aarch64_neon_vcvtfxu2fp FPR32:$Rn, vecshiftR16:$imm)),
(UCVTFh (f16 (EXTRACT_SUBREG FPR32:$Rn, hsub)), vecshiftR16:$imm)>;
def : Pat<(f16 (int_aarch64_neon_vcvtfxu2fp (i64 FPR64:$Rn), vecshiftR16:$imm)),
(UCVTFh (f16 (EXTRACT_SUBREG FPR64:$Rn, hsub)), vecshiftR16:$imm)>;
-def : Pat<(i32 (int_aarch64_neon_vcvtfp2fxs (f16 FPR16:$Rn), vecshiftR32:$imm)),
- (i32 (INSERT_SUBREG
- (i32 (IMPLICIT_DEF)),
- (FCVTZSh FPR16:$Rn, vecshiftR32:$imm),
- hsub))>;
-def : Pat<(i64 (int_aarch64_neon_vcvtfp2fxs (f16 FPR16:$Rn), vecshiftR64:$imm)),
- (i64 (INSERT_SUBREG
- (i64 (IMPLICIT_DEF)),
- (FCVTZSh FPR16:$Rn, vecshiftR64:$imm),
- hsub))>;
-def : Pat<(i32 (int_aarch64_neon_vcvtfp2fxu (f16 FPR16:$Rn), vecshiftR32:$imm)),
- (i32 (INSERT_SUBREG
- (i32 (IMPLICIT_DEF)),
- (FCVTZUh FPR16:$Rn, vecshiftR32:$imm),
- hsub))>;
-def : Pat<(i64 (int_aarch64_neon_vcvtfp2fxu (f16 FPR16:$Rn), vecshiftR64:$imm)),
- (i64 (INSERT_SUBREG
- (i64 (IMPLICIT_DEF)),
- (FCVTZUh FPR16:$Rn, vecshiftR64:$imm),
- hsub))>;
def : Pat<(i32 (int_aarch64_neon_facge (f16 FPR16:$Rn), (f16 FPR16:$Rm))),
(i32 (INSERT_SUBREG
(i32 (IMPLICIT_DEF)),
>From e36b3814086742b5e5c16023c9a3a9b6733f26f4 Mon Sep 17 00:00:00 2001
From: Kieran Bailey <kieran.bailey at arm.com>
Date: Mon, 27 Jul 2026 13:52:56 +0000
Subject: [PATCH 3/5] Remove f16 GPR result conversions. - Acle requires f16
conversions to lower to Hd register - Remove f16 GPR conversion patterns for
vcvth_n - Remove the bitconvert test cases for f16
---
llvm/lib/Target/AArch64/AArch64InstrInfo.td | 20 +-
.../AArch64/fp16_intrinsic_scalar_2op.ll | 208 +++++-------------
2 files changed, 55 insertions(+), 173 deletions(-)
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index 4f975385da8d4..2f36cf2ad5494 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -9139,40 +9139,26 @@ defm FCVTZU : SIMDFPScalarRShift<1, 0b11111, "fcvtzu">;
defm SCVTF : SIMDFPScalarRShift<0, 0b11100, "scvtf">;
defm UCVTF : SIMDFPScalarRShift<1, 0b11100, "ucvtf">;
-// Transformation for SIMD shift imm to fixed point imm for FPR-to-GPR result
+// Transformation for SIMD shift imm to fixed point imm for FPR-to-GPR result.
def fixedpoint_scalar_xform : SDNodeXForm<timm, [{
(void)N;
return V;
}]>;
multiclass FPToFixedScalarPats<SDPatternOperator OpN, string INST > {
- // Allow integer result to remain in GPR register
+ // Allow integer result to remain in GPR register.
def : Pat<(i32 (OpN FPR32:$Rn, vecshiftR32:$imm)),
(!cast<Instruction>(INST # "SWSri") FPR32:$Rn, (fixedpoint_scalar_xform vecshiftR32:$imm))>;
def : Pat<(i64 (OpN (f64 FPR64:$Rn), vecshiftR64:$imm)),
(!cast<Instruction>(INST # "SXDri") FPR64:$Rn, (fixedpoint_scalar_xform vecshiftR64:$imm))>;
- def : Pat<(i32 (OpN (f16 FPR16:$Rn), vecshiftR32:$imm)),
- (!cast<Instruction>(INST # "SWHri") FPR16:$Rn, (fixedpoint_scalar_xform vecshiftR32:$imm))>;
- def : Pat<(i64 (OpN (f16 FPR16:$Rn), vecshiftR64:$imm)),
- (!cast<Instruction>(INST # "SXHri") FPR16:$Rn, (fixedpoint_scalar_xform vecshiftR64:$imm))>;
// Explicit Bitcast results kept in FP/SIMD registers.
def : Pat<(f32 (bitconvert(i32 (OpN FPR32:$Rn, vecshiftR32:$imm)))),
(!cast<Instruction>(INST # "s") FPR32:$Rn, vecshiftR32:$imm)>;
def : Pat<(f64 (bitconvert(i64 (OpN (f64 FPR64:$Rn), vecshiftR64:$imm)))),
(!cast<Instruction>(INST # "d") FPR64:$Rn, vecshiftR64:$imm)>;
- def : Pat<(f32(bitconvert (i32 (OpN (f16 FPR16:$Rn), vecshiftR32:$imm)))),
- (f32 (INSERT_SUBREG
- (f32 (IMPLICIT_DEF)),
- (!cast<Instruction>(INST # "h") FPR16:$Rn, vecshiftR32:$imm),
- hsub))>;
- def : Pat<(f64 (bitconvert (i64 (OpN (f16 FPR16:$Rn), vecshiftR64:$imm)))),
- (f64 (INSERT_SUBREG
- (f64 (IMPLICIT_DEF)),
- (!cast<Instruction>(INST # "h") FPR16:$Rn, vecshiftR64:$imm),
- hsub))>;
- // FPR fallback patterns
+ // FPR fallback patterns.
def : Pat<(i32 (OpN FPR32:$Rn, vecshiftR32:$imm)),
(!cast<Instruction>(INST # "s") FPR32:$Rn, vecshiftR32:$imm)>;
def : Pat<(i64 (OpN (f64 FPR64:$Rn), vecshiftR64:$imm)),
diff --git a/llvm/test/CodeGen/AArch64/fp16_intrinsic_scalar_2op.ll b/llvm/test/CodeGen/AArch64/fp16_intrinsic_scalar_2op.ll
index f44e1a6fa9970..af898c5f1d312 100644
--- a/llvm/test/CodeGen/AArch64/fp16_intrinsic_scalar_2op.ll
+++ b/llvm/test/CodeGen/AArch64/fp16_intrinsic_scalar_2op.ll
@@ -248,16 +248,11 @@ entry:
}
define dso_local i16 @test_vcvth_n_s16_f16_1(half %a) {
-; CHECK-SD-LABEL: test_vcvth_n_s16_f16_1:
-; CHECK-SD: // %bb.0: // %entry
-; CHECK-SD-NEXT: fcvtzs w0, h0, #1
-; CHECK-SD-NEXT: ret
-;
-; CHECK-GI-LABEL: test_vcvth_n_s16_f16_1:
-; CHECK-GI: // %bb.0: // %entry
-; CHECK-GI-NEXT: fcvtzs h0, h0, #1
-; CHECK-GI-NEXT: fmov w0, s0
-; CHECK-GI-NEXT: ret
+; CHECK-LABEL: test_vcvth_n_s16_f16_1:
+; CHECK: // %bb.0: // %entry
+; CHECK-NEXT: fcvtzs h0, h0, #1
+; CHECK-NEXT: fmov w0, s0
+; CHECK-NEXT: ret
entry:
%fcvth_n = tail call i32 @llvm.aarch64.neon.vcvtfp2fxs.i32.f16(half %a, i32 1)
%0 = trunc i32 %fcvth_n to i16
@@ -265,16 +260,11 @@ entry:
}
define dso_local i16 @test_vcvth_n_s16_f16_16(half %a) {
-; CHECK-SD-LABEL: test_vcvth_n_s16_f16_16:
-; CHECK-SD: // %bb.0: // %entry
-; CHECK-SD-NEXT: fcvtzs w0, h0, #16
-; CHECK-SD-NEXT: ret
-;
-; CHECK-GI-LABEL: test_vcvth_n_s16_f16_16:
-; CHECK-GI: // %bb.0: // %entry
-; CHECK-GI-NEXT: fcvtzs h0, h0, #16
-; CHECK-GI-NEXT: fmov w0, s0
-; CHECK-GI-NEXT: ret
+; CHECK-LABEL: test_vcvth_n_s16_f16_16:
+; CHECK: // %bb.0: // %entry
+; CHECK-NEXT: fcvtzs h0, h0, #16
+; CHECK-NEXT: fmov w0, s0
+; CHECK-NEXT: ret
entry:
%fcvth_n = tail call i32 @llvm.aarch64.neon.vcvtfp2fxs.i32.f16(half %a, i32 16)
%0 = trunc i32 %fcvth_n to i16
@@ -282,89 +272,47 @@ entry:
}
define dso_local i32 @test_vcvth_n_s32_f16_1(half %a) {
-; CHECK-SD-LABEL: test_vcvth_n_s32_f16_1:
-; CHECK-SD: // %bb.0: // %entry
-; CHECK-SD-NEXT: fcvtzs w0, h0, #1
-; CHECK-SD-NEXT: ret
-;
-; CHECK-GI-LABEL: test_vcvth_n_s32_f16_1:
-; CHECK-GI: // %bb.0: // %entry
-; CHECK-GI-NEXT: fcvtzs h0, h0, #1
-; CHECK-GI-NEXT: fmov w0, s0
-; CHECK-GI-NEXT: ret
+; CHECK-LABEL: test_vcvth_n_s32_f16_1:
+; CHECK: // %bb.0: // %entry
+; CHECK-NEXT: fcvtzs h0, h0, #1
+; CHECK-NEXT: fmov w0, s0
+; CHECK-NEXT: ret
entry:
%vcvth_n_s32_f16 = tail call i32 @llvm.aarch64.neon.vcvtfp2fxs.i32.f16(half %a, i32 1)
ret i32 %vcvth_n_s32_f16
}
define dso_local i32 @test_vcvth_n_s32_f16_16(half %a) {
-; CHECK-SD-LABEL: test_vcvth_n_s32_f16_16:
-; CHECK-SD: // %bb.0: // %entry
-; CHECK-SD-NEXT: fcvtzs w0, h0, #16
-; CHECK-SD-NEXT: ret
-;
-; CHECK-GI-LABEL: test_vcvth_n_s32_f16_16:
-; CHECK-GI: // %bb.0: // %entry
-; CHECK-GI-NEXT: fcvtzs h0, h0, #16
-; CHECK-GI-NEXT: fmov w0, s0
-; CHECK-GI-NEXT: ret
-entry:
- %vcvth_n_s32_f16 = tail call i32 @llvm.aarch64.neon.vcvtfp2fxs.i32.f16(half %a, i32 16)
- ret i32 %vcvth_n_s32_f16
-}
-
-define dso_local float @test_vcvth_n_s32_f16_fpr(half %a) {
-; CHECK-LABEL: test_vcvth_n_s32_f16_fpr:
+; CHECK-LABEL: test_vcvth_n_s32_f16_16:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: fcvtzs h0, h0, #16
+; CHECK-NEXT: fmov w0, s0
; CHECK-NEXT: ret
entry:
%vcvth_n_s32_f16 = tail call i32 @llvm.aarch64.neon.vcvtfp2fxs.i32.f16(half %a, i32 16)
- %bc = bitcast i32 %vcvth_n_s32_f16 to float
- ret float %bc
+ ret i32 %vcvth_n_s32_f16
}
define dso_local i64 @test_vcvth_n_s64_f16_1(half %a) {
-; CHECK-SD-LABEL: test_vcvth_n_s64_f16_1:
-; CHECK-SD: // %bb.0: // %entry
-; CHECK-SD-NEXT: fcvtzs x0, h0, #1
-; CHECK-SD-NEXT: ret
-;
-; CHECK-GI-LABEL: test_vcvth_n_s64_f16_1:
-; CHECK-GI: // %bb.0: // %entry
-; CHECK-GI-NEXT: fcvtzs h0, h0, #1
-; CHECK-GI-NEXT: fmov x0, d0
-; CHECK-GI-NEXT: ret
+; CHECK-LABEL: test_vcvth_n_s64_f16_1:
+; CHECK: // %bb.0: // %entry
+; CHECK-NEXT: fcvtzs h0, h0, #1
+; CHECK-NEXT: fmov x0, d0
+; CHECK-NEXT: ret
entry:
%vcvth_n_s64_f16 = tail call i64 @llvm.aarch64.neon.vcvtfp2fxs.i64.f16(half %a, i32 1)
ret i64 %vcvth_n_s64_f16
}
define dso_local i64 @test_vcvth_n_s64_f16_32(half %a) {
-; CHECK-SD-LABEL: test_vcvth_n_s64_f16_32:
-; CHECK-SD: // %bb.0: // %entry
-; CHECK-SD-NEXT: fcvtzs x0, h0, #32
-; CHECK-SD-NEXT: ret
-;
-; CHECK-GI-LABEL: test_vcvth_n_s64_f16_32:
-; CHECK-GI: // %bb.0: // %entry
-; CHECK-GI-NEXT: fcvtzs h0, h0, #32
-; CHECK-GI-NEXT: fmov x0, d0
-; CHECK-GI-NEXT: ret
-entry:
- %vcvth_n_s64_f16 = tail call i64 @llvm.aarch64.neon.vcvtfp2fxs.i64.f16(half %a, i32 32)
- ret i64 %vcvth_n_s64_f16
-}
-
-define dso_local double @test_vcvth_n_s64_f16_fpr(half %a) {
-; CHECK-LABEL: test_vcvth_n_s64_f16_fpr:
+; CHECK-LABEL: test_vcvth_n_s64_f16_32:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: fcvtzs h0, h0, #32
+; CHECK-NEXT: fmov x0, d0
; CHECK-NEXT: ret
entry:
%vcvth_n_s64_f16 = tail call i64 @llvm.aarch64.neon.vcvtfp2fxs.i64.f16(half %a, i32 32)
- %bc = bitcast i64 %vcvth_n_s64_f16 to double
- ret double %bc
+ ret i64 %vcvth_n_s64_f16
}
define dso_local half @test_vcvth_n_f16_u16_1(i16 %a) {
@@ -428,16 +376,11 @@ entry:
}
define dso_local i16 @test_vcvth_n_u16_f16_1(half %a) {
-; CHECK-SD-LABEL: test_vcvth_n_u16_f16_1:
-; CHECK-SD: // %bb.0: // %entry
-; CHECK-SD-NEXT: fcvtzu w0, h0, #1
-; CHECK-SD-NEXT: ret
-;
-; CHECK-GI-LABEL: test_vcvth_n_u16_f16_1:
-; CHECK-GI: // %bb.0: // %entry
-; CHECK-GI-NEXT: fcvtzu h0, h0, #1
-; CHECK-GI-NEXT: fmov w0, s0
-; CHECK-GI-NEXT: ret
+; CHECK-LABEL: test_vcvth_n_u16_f16_1:
+; CHECK: // %bb.0: // %entry
+; CHECK-NEXT: fcvtzu h0, h0, #1
+; CHECK-NEXT: fmov w0, s0
+; CHECK-NEXT: ret
entry:
%fcvth_n = tail call i32 @llvm.aarch64.neon.vcvtfp2fxu.i32.f16(half %a, i32 1)
%0 = trunc i32 %fcvth_n to i16
@@ -445,16 +388,11 @@ entry:
}
define dso_local i16 @test_vcvth_n_u16_f16_16(half %a) {
-; CHECK-SD-LABEL: test_vcvth_n_u16_f16_16:
-; CHECK-SD: // %bb.0: // %entry
-; CHECK-SD-NEXT: fcvtzu w0, h0, #16
-; CHECK-SD-NEXT: ret
-;
-; CHECK-GI-LABEL: test_vcvth_n_u16_f16_16:
-; CHECK-GI: // %bb.0: // %entry
-; CHECK-GI-NEXT: fcvtzu h0, h0, #16
-; CHECK-GI-NEXT: fmov w0, s0
-; CHECK-GI-NEXT: ret
+; CHECK-LABEL: test_vcvth_n_u16_f16_16:
+; CHECK: // %bb.0: // %entry
+; CHECK-NEXT: fcvtzu h0, h0, #16
+; CHECK-NEXT: fmov w0, s0
+; CHECK-NEXT: ret
entry:
%fcvth_n = tail call i32 @llvm.aarch64.neon.vcvtfp2fxu.i32.f16(half %a, i32 16)
%0 = trunc i32 %fcvth_n to i16
@@ -462,89 +400,47 @@ entry:
}
define dso_local i32 @test_vcvth_n_u32_f16_1(half %a) {
-; CHECK-SD-LABEL: test_vcvth_n_u32_f16_1:
-; CHECK-SD: // %bb.0: // %entry
-; CHECK-SD-NEXT: fcvtzu w0, h0, #1
-; CHECK-SD-NEXT: ret
-;
-; CHECK-GI-LABEL: test_vcvth_n_u32_f16_1:
-; CHECK-GI: // %bb.0: // %entry
-; CHECK-GI-NEXT: fcvtzu h0, h0, #1
-; CHECK-GI-NEXT: fmov w0, s0
-; CHECK-GI-NEXT: ret
+; CHECK-LABEL: test_vcvth_n_u32_f16_1:
+; CHECK: // %bb.0: // %entry
+; CHECK-NEXT: fcvtzu h0, h0, #1
+; CHECK-NEXT: fmov w0, s0
+; CHECK-NEXT: ret
entry:
%vcvth_n_u32_f16 = tail call i32 @llvm.aarch64.neon.vcvtfp2fxu.i32.f16(half %a, i32 1)
ret i32 %vcvth_n_u32_f16
}
define dso_local i32 @test_vcvth_n_u32_f16_16(half %a) {
-; CHECK-SD-LABEL: test_vcvth_n_u32_f16_16:
-; CHECK-SD: // %bb.0: // %entry
-; CHECK-SD-NEXT: fcvtzu w0, h0, #16
-; CHECK-SD-NEXT: ret
-;
-; CHECK-GI-LABEL: test_vcvth_n_u32_f16_16:
-; CHECK-GI: // %bb.0: // %entry
-; CHECK-GI-NEXT: fcvtzu h0, h0, #16
-; CHECK-GI-NEXT: fmov w0, s0
-; CHECK-GI-NEXT: ret
-entry:
- %vcvth_n_u32_f16 = tail call i32 @llvm.aarch64.neon.vcvtfp2fxu.i32.f16(half %a, i32 16)
- ret i32 %vcvth_n_u32_f16
-}
-
-define dso_local float @test_vcvth_n_u32_f16_fpr(half %a) {
-; CHECK-LABEL: test_vcvth_n_u32_f16_fpr:
+; CHECK-LABEL: test_vcvth_n_u32_f16_16:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: fcvtzu h0, h0, #16
+; CHECK-NEXT: fmov w0, s0
; CHECK-NEXT: ret
entry:
%vcvth_n_u32_f16 = tail call i32 @llvm.aarch64.neon.vcvtfp2fxu.i32.f16(half %a, i32 16)
- %bc = bitcast i32 %vcvth_n_u32_f16 to float
- ret float %bc
+ ret i32 %vcvth_n_u32_f16
}
define dso_local i64 @test_vcvth_n_u64_f16_1(half %a) {
-; CHECK-SD-LABEL: test_vcvth_n_u64_f16_1:
-; CHECK-SD: // %bb.0: // %entry
-; CHECK-SD-NEXT: fcvtzu x0, h0, #1
-; CHECK-SD-NEXT: ret
-;
-; CHECK-GI-LABEL: test_vcvth_n_u64_f16_1:
-; CHECK-GI: // %bb.0: // %entry
-; CHECK-GI-NEXT: fcvtzu h0, h0, #1
-; CHECK-GI-NEXT: fmov x0, d0
-; CHECK-GI-NEXT: ret
+; CHECK-LABEL: test_vcvth_n_u64_f16_1:
+; CHECK: // %bb.0: // %entry
+; CHECK-NEXT: fcvtzu h0, h0, #1
+; CHECK-NEXT: fmov x0, d0
+; CHECK-NEXT: ret
entry:
%vcvth_n_u64_f16 = tail call i64 @llvm.aarch64.neon.vcvtfp2fxu.i64.f16(half %a, i32 1)
ret i64 %vcvth_n_u64_f16
}
define dso_local i64 @test_vcvth_n_u64_f16_16(half %a) {
-; CHECK-SD-LABEL: test_vcvth_n_u64_f16_16:
-; CHECK-SD: // %bb.0: // %entry
-; CHECK-SD-NEXT: fcvtzu x0, h0, #16
-; CHECK-SD-NEXT: ret
-;
-; CHECK-GI-LABEL: test_vcvth_n_u64_f16_16:
-; CHECK-GI: // %bb.0: // %entry
-; CHECK-GI-NEXT: fcvtzu h0, h0, #16
-; CHECK-GI-NEXT: fmov x0, d0
-; CHECK-GI-NEXT: ret
-entry:
- %vcvth_n_u64_f16 = tail call i64 @llvm.aarch64.neon.vcvtfp2fxu.i64.f16(half %a, i32 16)
- ret i64 %vcvth_n_u64_f16
-}
-
-define dso_local double @test_vcvth_n_u64_f16_fpr(half %a) {
-; CHECK-LABEL: test_vcvth_n_u64_f16_fpr:
+; CHECK-LABEL: test_vcvth_n_u64_f16_16:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: fcvtzu h0, h0, #16
+; CHECK-NEXT: fmov x0, d0
; CHECK-NEXT: ret
entry:
%vcvth_n_u64_f16 = tail call i64 @llvm.aarch64.neon.vcvtfp2fxu.i64.f16(half %a, i32 16)
- %bc = bitcast i64 %vcvth_n_u64_f16 to double
- ret double %bc
+ ret i64 %vcvth_n_u64_f16
}
define dso_local i16 @vcageh_f16_test(half %a, half %b) {
>From 2437f37c2745254f12df5b8dc52be0b621d34e67 Mon Sep 17 00:00:00 2001
From: Kieran Bailey <kieran.bailey at arm.com>
Date: Tue, 28 Jul 2026 15:31:26 +0000
Subject: [PATCH 4/5] Removed fp16 missing unsigned test case
I will remove the missing test cases for u64_f16 as they will be added when the f16 patterns are fixed and are unrelated to my patch.
---
.../AArch64/fp16_intrinsic_scalar_2op.ll | 23 -------------------
1 file changed, 23 deletions(-)
diff --git a/llvm/test/CodeGen/AArch64/fp16_intrinsic_scalar_2op.ll b/llvm/test/CodeGen/AArch64/fp16_intrinsic_scalar_2op.ll
index af898c5f1d312..da70599483a63 100644
--- a/llvm/test/CodeGen/AArch64/fp16_intrinsic_scalar_2op.ll
+++ b/llvm/test/CodeGen/AArch64/fp16_intrinsic_scalar_2op.ll
@@ -185,7 +185,6 @@ declare i32 @llvm.aarch64.neon.vcvtfp2fxs.i32.f16(half, i32) #1
declare i64 @llvm.aarch64.neon.vcvtfp2fxs.i64.f16(half, i32) #1
declare half @llvm.aarch64.neon.vcvtfxu2fp.f16.i32(i32, i32) #1
declare i32 @llvm.aarch64.neon.vcvtfp2fxu.i32.f16(half, i32) #1
-declare i64 @llvm.aarch64.neon.vcvtfp2fxu.i64.f16(half, i32) #1
define dso_local half @test_vcvth_n_f16_s16_1(i16 %a) {
; CHECK-SD-LABEL: test_vcvth_n_f16_s16_1:
@@ -421,28 +420,6 @@ entry:
ret i32 %vcvth_n_u32_f16
}
-define dso_local i64 @test_vcvth_n_u64_f16_1(half %a) {
-; CHECK-LABEL: test_vcvth_n_u64_f16_1:
-; CHECK: // %bb.0: // %entry
-; CHECK-NEXT: fcvtzu h0, h0, #1
-; CHECK-NEXT: fmov x0, d0
-; CHECK-NEXT: ret
-entry:
- %vcvth_n_u64_f16 = tail call i64 @llvm.aarch64.neon.vcvtfp2fxu.i64.f16(half %a, i32 1)
- ret i64 %vcvth_n_u64_f16
-}
-
-define dso_local i64 @test_vcvth_n_u64_f16_16(half %a) {
-; CHECK-LABEL: test_vcvth_n_u64_f16_16:
-; CHECK: // %bb.0: // %entry
-; CHECK-NEXT: fcvtzu h0, h0, #16
-; CHECK-NEXT: fmov x0, d0
-; CHECK-NEXT: ret
-entry:
- %vcvth_n_u64_f16 = tail call i64 @llvm.aarch64.neon.vcvtfp2fxu.i64.f16(half %a, i32 16)
- ret i64 %vcvth_n_u64_f16
-}
-
define dso_local i16 @vcageh_f16_test(half %a, half %b) {
; CHECK-LABEL: vcageh_f16_test:
; CHECK: // %bb.0: // %entry
>From 254741717f07707b6fddc031b668aef49584395e Mon Sep 17 00:00:00 2001
From: Kieran Bailey <kieran.bailey at arm.com>
Date: Wed, 29 Jul 2026 14:23:13 +0000
Subject: [PATCH 5/5] [AArch64][SelectionDAG] Avoid cross-bank copies for NEON
vcvtfp2fx results
Updated the types in test names to be correct order for llvm standard
---
.../CodeGen/AArch64/neon-scalar-vcvtfp2fx.ll | 32 +++++++++----------
1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/llvm/test/CodeGen/AArch64/neon-scalar-vcvtfp2fx.ll b/llvm/test/CodeGen/AArch64/neon-scalar-vcvtfp2fx.ll
index 5ca1d8c44cf58..219a2950aa35d 100644
--- a/llvm/test/CodeGen/AArch64/neon-scalar-vcvtfp2fx.ll
+++ b/llvm/test/CodeGen/AArch64/neon-scalar-vcvtfp2fx.ll
@@ -1,8 +1,8 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
; RUN: llc -mtriple=aarch64 -global-isel=0 < %s | FileCheck %s
-define i32 @vcvtfp2fxs_f32_i32(float %a) {
-; CHECK-LABEL: vcvtfp2fxs_f32_i32:
+define i32 @vcvtfp2fxs_i32_f32(float %a) {
+; CHECK-LABEL: vcvtfp2fxs_i32_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: fcvtzs w0, s0, #16
; CHECK-NEXT: ret
@@ -10,8 +10,8 @@ define i32 @vcvtfp2fxs_f32_i32(float %a) {
ret i32 %r
}
-define float @vcvtfp2fxs_f32_i32_bitcast(float %a) {
-; CHECK-LABEL: vcvtfp2fxs_f32_i32_bitcast:
+define float @vcvtfp2fxs_i32_f32_bitcast(float %a) {
+; CHECK-LABEL: vcvtfp2fxs_i32_f32_bitcast:
; CHECK: // %bb.0:
; CHECK-NEXT: fcvtzs s0, s0, #16
; CHECK-NEXT: ret
@@ -20,8 +20,8 @@ define float @vcvtfp2fxs_f32_i32_bitcast(float %a) {
ret float %b
}
-define i32 @vcvtfp2fxu_f32_i32(float %a) {
-; CHECK-LABEL: vcvtfp2fxu_f32_i32:
+define i32 @vcvtfp2fxu_i32_f32(float %a) {
+; CHECK-LABEL: vcvtfp2fxu_i32_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: fcvtzu w0, s0, #16
; CHECK-NEXT: ret
@@ -29,8 +29,8 @@ define i32 @vcvtfp2fxu_f32_i32(float %a) {
ret i32 %r
}
-define float @vcvtfp2fxu_f32_i32_bitcast(float %a) {
-; CHECK-LABEL: vcvtfp2fxu_f32_i32_bitcast:
+define float @vcvtfp2fxu_i32_f32_bitcast(float %a) {
+; CHECK-LABEL: vcvtfp2fxu_i32_f32_bitcast:
; CHECK: // %bb.0:
; CHECK-NEXT: fcvtzu s0, s0, #16
; CHECK-NEXT: ret
@@ -39,8 +39,8 @@ define float @vcvtfp2fxu_f32_i32_bitcast(float %a) {
ret float %b
}
-define i64 @vcvtfp2fxs_f64_i64(double %a) {
-; CHECK-LABEL: vcvtfp2fxs_f64_i64:
+define i64 @vcvtfp2fxs_i64_f64(double %a) {
+; CHECK-LABEL: vcvtfp2fxs_i64_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: fcvtzs x0, d0, #16
; CHECK-NEXT: ret
@@ -48,8 +48,8 @@ define i64 @vcvtfp2fxs_f64_i64(double %a) {
ret i64 %r
}
-define double @vcvtfp2fxs_f64_i64_bitcast(double %a) {
-; CHECK-LABEL: vcvtfp2fxs_f64_i64_bitcast:
+define double @vcvtfp2fxs_i64_f64_bitcast(double %a) {
+; CHECK-LABEL: vcvtfp2fxs_i64_f64_bitcast:
; CHECK: // %bb.0:
; CHECK-NEXT: fcvtzs d0, d0, #16
; CHECK-NEXT: ret
@@ -58,8 +58,8 @@ define double @vcvtfp2fxs_f64_i64_bitcast(double %a) {
ret double %b
}
-define i64 @vcvtfp2fxu_f64_i64(double %a) {
-; CHECK-LABEL: vcvtfp2fxu_f64_i64:
+define i64 @vcvtfp2fxu_i64_f64(double %a) {
+; CHECK-LABEL: vcvtfp2fxu_i64_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: fcvtzu x0, d0, #16
; CHECK-NEXT: ret
@@ -67,8 +67,8 @@ define i64 @vcvtfp2fxu_f64_i64(double %a) {
ret i64 %r
}
-define double @vcvtfp2fxu_f64_i64_bitcast(double %a) {
-; CHECK-LABEL: vcvtfp2fxu_f64_i64_bitcast:
+define double @vcvtfp2fxu_i64_f64_bitcast(double %a) {
+; CHECK-LABEL: vcvtfp2fxu_i64_f64_bitcast:
; CHECK: // %bb.0:
; CHECK-NEXT: fcvtzu d0, d0, #16
; CHECK-NEXT: ret
More information about the llvm-commits
mailing list