[llvm] 8557b57 - [DAG] Fold INT_TO_FP( FP_TO_INT (x) ) to FTRUNC(X) (#198477)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jun 4 02:23:42 PDT 2026
Author: Jacob Crawley
Date: 2026-06-04T09:23:37Z
New Revision: 8557b5714bca181906118a86d8152598f525fcc7
URL: https://github.com/llvm/llvm-project/commit/8557b5714bca181906118a86d8152598f525fcc7
DIFF: https://github.com/llvm/llvm-project/commit/8557b5714bca181906118a86d8152598f525fcc7.diff
LOG: [DAG] Fold INT_TO_FP( FP_TO_INT (x) ) to FTRUNC(X) (#198477)
Extends the `foldFPToIntToFP` DAG Combine so that it can now be applied
when `FTRUNC` has a custom lowering, and given that `INT_TO_FP
(FP_TO_INT (X))` is not already legal.
On AArch64 targets with SVE, this change simplifies the codegen of
`INT_TO_FP (FP_TO_INT (X))` conversions by making use of the `frintz`
instruction.
Added:
llvm/test/CodeGen/AArch64/sve-fixed-length-frintz.ll
llvm/test/CodeGen/AArch64/sve-frintz.ll
Modified:
llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
llvm/test/CodeGen/AMDGPU/fptoui_uitofp.ll
Removed:
################################################################################
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 530ba5f49824e..6cb9ae5fa7803 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -19944,13 +19944,24 @@ static SDValue foldFPToIntToFP(SDNode *N, const SDLoc &DL, SelectionDAG &DAG,
// FIXME: We should be able to use node-level FMF here.
EVT VT = N->getValueType(0);
- if (!TLI.isOperationLegal(ISD::FTRUNC, VT))
+ if (!TLI.isOperationLegalOrCustom(ISD::FTRUNC, VT))
return SDValue();
bool IsUnsigned = N->getOpcode() == ISD::UINT_TO_FP;
bool IsSigned = N->getOpcode() == ISD::SINT_TO_FP;
assert(IsSigned || IsUnsigned);
+ // Don't fold if the individual cast operations are already legal,
+ // as FTRUNC may have a more expensive custom expansion.
+ EVT IntVT = N->getOperand(0).getValueType();
+ EVT LegalIntVT = TLI.getTypeToTransformTo(*DAG.getContext(), IntVT);
+ unsigned FPToIntOp = IsUnsigned ? ISD::FP_TO_UINT : ISD::FP_TO_SINT;
+ unsigned IntToFPOp = N->getOpcode(); // UINT_TO_FP or SINT_TO_FP
+ if (!TLI.isOperationLegal(ISD::FTRUNC, VT) &&
+ TLI.isOperationLegal(FPToIntOp, LegalIntVT) &&
+ TLI.isOperationLegal(IntToFPOp, VT))
+ return SDValue();
+
bool IsSignedZeroSafe = DAG.getTarget().Options.NoSignedZerosFPMath ||
DAG.canIgnoreSignBitOfZero(SDValue(N, 0));
// For signed conversions: The optimization changes signed zero behavior.
@@ -19996,7 +20007,6 @@ static SDValue foldFPToIntToFP(SDNode *N, const SDLoc &DL, SelectionDAG &DAG,
}
// Check that the sequence ends with the correct kind of fpto[us]i.
- unsigned FPToIntOp = IsUnsigned ? ISD::FP_TO_UINT : ISD::FP_TO_SINT;
if (IntVal.getOpcode() != FPToIntOp ||
IntVal.getOperand(0).getValueType() != VT)
return SDValue();
diff --git a/llvm/test/CodeGen/AArch64/sve-fixed-length-frintz.ll b/llvm/test/CodeGen/AArch64/sve-fixed-length-frintz.ll
new file mode 100644
index 0000000000000..26cab6d542c65
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/sve-fixed-length-frintz.ll
@@ -0,0 +1,84 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc --enable-no-signed-zeros-fp-math -mattr=+sve -aarch64-sve-vector-bits-min=256 < %s | FileCheck %s
+
+; Check that fp -> int -> fp conversions can be
+; lowered to the SVE FRINTZ instructions for
+; fixed vectors.
+
+target triple = "aarch64-unknown-linux-gnu"
+
+define <8 x float> @frintz_f32_i32_f32(<8 x float> %in) {
+; CHECK-LABEL: frintz_f32_i32_f32:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ptrue p0.d, vl2
+; CHECK-NEXT: // kill: def $q0 killed $q0 def $z0
+; CHECK-NEXT: // kill: def $q1 killed $q1 def $z1
+; CHECK-NEXT: splice z0.d, p0, z0.d, z1.d
+; CHECK-NEXT: ptrue p0.s, vl8
+; CHECK-NEXT: frintz z0.s, p0/m, z0.s
+; CHECK-NEXT: movprfx z1, z0
+; CHECK-NEXT: ext z1.b, z1.b, z0.b, #16
+; CHECK-NEXT: // kill: def $q0 killed $q0 killed $z0
+; CHECK-NEXT: // kill: def $q1 killed $q1 killed $z1
+; CHECK-NEXT: ret
+ %res = fptosi <8 x float> %in to <8 x i32>
+ %res2 = sitofp <8 x i32> %res to <8 x float>
+ ret <8 x float> %res2
+}
+
+define <4 x double> @frintz_f64_i32_f64(<4 x double> %in) {
+; CHECK-LABEL: frintz_f64_i32_f64:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ptrue p0.d, vl2
+; CHECK-NEXT: // kill: def $q0 killed $q0 def $z0
+; CHECK-NEXT: // kill: def $q1 killed $q1 def $z1
+; CHECK-NEXT: splice z0.d, p0, z0.d, z1.d
+; CHECK-NEXT: ptrue p0.d, vl4
+; CHECK-NEXT: frintz z0.d, p0/m, z0.d
+; CHECK-NEXT: movprfx z1, z0
+; CHECK-NEXT: ext z1.b, z1.b, z0.b, #16
+; CHECK-NEXT: // kill: def $q0 killed $q0 killed $z0
+; CHECK-NEXT: // kill: def $q1 killed $q1 killed $z1
+; CHECK-NEXT: ret
+ %res = fptosi <4 x double> %in to <4 x i32>
+ %res2 = sitofp <4 x i32> %res to <4 x double>
+ ret <4 x double> %res2
+}
+
+define <4 x double> @frintz_f64_i64_f64(<4 x double> %in) {
+; CHECK-LABEL: frintz_f64_i64_f64:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ptrue p0.d, vl2
+; CHECK-NEXT: // kill: def $q0 killed $q0 def $z0
+; CHECK-NEXT: // kill: def $q1 killed $q1 def $z1
+; CHECK-NEXT: splice z0.d, p0, z0.d, z1.d
+; CHECK-NEXT: ptrue p0.d, vl4
+; CHECK-NEXT: frintz z0.d, p0/m, z0.d
+; CHECK-NEXT: movprfx z1, z0
+; CHECK-NEXT: ext z1.b, z1.b, z0.b, #16
+; CHECK-NEXT: // kill: def $q0 killed $q0 killed $z0
+; CHECK-NEXT: // kill: def $q1 killed $q1 killed $z1
+; CHECK-NEXT: ret
+ %res = fptosi <4 x double> %in to <4 x i64>
+ %res2 = sitofp <4 x i64> %res to <4 x double>
+ ret <4 x double> %res2
+}
+
+define <8 x float> @frintz_f32_i64_f32(<8 x float> %in) {
+; CHECK-LABEL: frintz_f32_i64_f32:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ptrue p0.d, vl2
+; CHECK-NEXT: // kill: def $q0 killed $q0 def $z0
+; CHECK-NEXT: // kill: def $q1 killed $q1 def $z1
+; CHECK-NEXT: splice z0.d, p0, z0.d, z1.d
+; CHECK-NEXT: ptrue p0.s, vl8
+; CHECK-NEXT: frintz z0.s, p0/m, z0.s
+; CHECK-NEXT: movprfx z1, z0
+; CHECK-NEXT: ext z1.b, z1.b, z0.b, #16
+; CHECK-NEXT: // kill: def $q0 killed $q0 killed $z0
+; CHECK-NEXT: // kill: def $q1 killed $q1 killed $z1
+; CHECK-NEXT: ret
+ %res = fptosi <8 x float> %in to <8 x i64>
+ %res2 = sitofp <8 x i64> %res to <8 x float>
+ ret <8 x float> %res2
+}
diff --git a/llvm/test/CodeGen/AArch64/sve-frintz.ll b/llvm/test/CodeGen/AArch64/sve-frintz.ll
new file mode 100644
index 0000000000000..f72ee35b0b58a
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/sve-frintz.ll
@@ -0,0 +1,128 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc --enable-no-signed-zeros-fp-math -mattr=+sve < %s | FileCheck %s
+; RUN: llc -mattr=+sve < %s | FileCheck %s --check-prefix=SIGNED-ZEROS
+
+target triple = "aarch64-unknown-linux-gnu"
+
+; Check that fp -> int -> fp conversions can be
+; lowered to the SVE FRINTZ instructions for
+; scalable vectors.
+
+define <vscale x 4 x float> @frintz_f32_i32_f32(<vscale x 4 x float> %in) {
+; CHECK-LABEL: frintz_f32_i32_f32:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ptrue p0.s
+; CHECK-NEXT: frintz z0.s, p0/m, z0.s
+; CHECK-NEXT: ret
+;
+; SIGNED-ZEROS-LABEL: frintz_f32_i32_f32:
+; SIGNED-ZEROS: // %bb.0:
+; SIGNED-ZEROS-NEXT: ptrue p0.s
+; SIGNED-ZEROS-NEXT: fcvtzs z0.s, p0/m, z0.s
+; SIGNED-ZEROS-NEXT: scvtf z0.s, p0/m, z0.s
+; SIGNED-ZEROS-NEXT: ret
+ %res = fptosi <vscale x 4 x float> %in to <vscale x 4 x i32>
+ %res2 = sitofp <vscale x 4 x i32> %res to <vscale x 4 x float>
+ ret <vscale x 4 x float> %res2
+}
+
+define <vscale x 2 x double> @frintz_f64_i32_f64(<vscale x 2 x double> %in) {
+; CHECK-LABEL: frintz_f64_i32_f64:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ptrue p0.d
+; CHECK-NEXT: frintz z0.d, p0/m, z0.d
+; CHECK-NEXT: ret
+;
+; SIGNED-ZEROS-LABEL: frintz_f64_i32_f64:
+; SIGNED-ZEROS: // %bb.0:
+; SIGNED-ZEROS-NEXT: ptrue p0.d
+; SIGNED-ZEROS-NEXT: fcvtzs z0.d, p0/m, z0.d
+; SIGNED-ZEROS-NEXT: scvtf z0.d, p0/m, z0.d
+; SIGNED-ZEROS-NEXT: ret
+ %res = fptosi <vscale x 2 x double> %in to <vscale x 2 x i32>
+ %res2 = sitofp <vscale x 2 x i32> %res to <vscale x 2 x double>
+ ret <vscale x 2 x double> %res2
+}
+
+define <vscale x 8 x float> @frintz_f32_i32_f32_nxv8i32(<vscale x 8 x float> %in) {
+; CHECK-LABEL: frintz_f32_i32_f32_nxv8i32:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ptrue p0.s
+; CHECK-NEXT: frintz z0.s, p0/m, z0.s
+; CHECK-NEXT: frintz z1.s, p0/m, z1.s
+; CHECK-NEXT: ret
+;
+; SIGNED-ZEROS-LABEL: frintz_f32_i32_f32_nxv8i32:
+; SIGNED-ZEROS: // %bb.0:
+; SIGNED-ZEROS-NEXT: ptrue p0.s
+; SIGNED-ZEROS-NEXT: fcvtzs z1.s, p0/m, z1.s
+; SIGNED-ZEROS-NEXT: fcvtzs z0.s, p0/m, z0.s
+; SIGNED-ZEROS-NEXT: scvtf z0.s, p0/m, z0.s
+; SIGNED-ZEROS-NEXT: scvtf z1.s, p0/m, z1.s
+; SIGNED-ZEROS-NEXT: ret
+ %res = fptosi <vscale x 8 x float> %in to <vscale x 8 x i32>
+ %res2 = sitofp <vscale x 8 x i32> %res to <vscale x 8 x float>
+ ret <vscale x 8 x float> %res2
+}
+
+define <vscale x 2 x double> @frintz_f64_i64_f64(<vscale x 2 x double> %in) {
+; CHECK-LABEL: frintz_f64_i64_f64:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ptrue p0.d
+; CHECK-NEXT: frintz z0.d, p0/m, z0.d
+; CHECK-NEXT: ret
+;
+; SIGNED-ZEROS-LABEL: frintz_f64_i64_f64:
+; SIGNED-ZEROS: // %bb.0:
+; SIGNED-ZEROS-NEXT: ptrue p0.d
+; SIGNED-ZEROS-NEXT: fcvtzs z0.d, p0/m, z0.d
+; SIGNED-ZEROS-NEXT: scvtf z0.d, p0/m, z0.d
+; SIGNED-ZEROS-NEXT: ret
+ %res = fptosi <vscale x 2 x double> %in to <vscale x 2 x i64>
+ %res2 = sitofp <vscale x 2 x i64> %res to <vscale x 2 x double>
+ ret <vscale x 2 x double> %res2
+}
+
+define <vscale x 4 x float> @frintz_f32_i64_f32(<vscale x 4 x float> %in) {
+; CHECK-LABEL: frintz_f32_i64_f32:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ptrue p0.s
+; CHECK-NEXT: frintz z0.s, p0/m, z0.s
+; CHECK-NEXT: ret
+;
+; SIGNED-ZEROS-LABEL: frintz_f32_i64_f32:
+; SIGNED-ZEROS: // %bb.0:
+; SIGNED-ZEROS-NEXT: uunpklo z1.d, z0.s
+; SIGNED-ZEROS-NEXT: uunpkhi z0.d, z0.s
+; SIGNED-ZEROS-NEXT: ptrue p0.d
+; SIGNED-ZEROS-NEXT: fcvtzs z1.d, p0/m, z1.s
+; SIGNED-ZEROS-NEXT: fcvtzs z0.d, p0/m, z0.s
+; SIGNED-ZEROS-NEXT: scvtf z0.s, p0/m, z0.d
+; SIGNED-ZEROS-NEXT: scvtf z1.s, p0/m, z1.d
+; SIGNED-ZEROS-NEXT: uzp1 z0.s, z1.s, z0.s
+; SIGNED-ZEROS-NEXT: ret
+ %res = fptosi <vscale x 4 x float> %in to <vscale x 4 x i64>
+ %res2 = sitofp <vscale x 4 x i64> %res to <vscale x 4 x float>
+ ret <vscale x 4 x float> %res2
+}
+
+define <vscale x 4 x double> @frintz_f64_i64_f64_nxv4i64(<vscale x 4 x double> %in) {
+; CHECK-LABEL: frintz_f64_i64_f64_nxv4i64:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ptrue p0.d
+; CHECK-NEXT: frintz z0.d, p0/m, z0.d
+; CHECK-NEXT: frintz z1.d, p0/m, z1.d
+; CHECK-NEXT: ret
+;
+; SIGNED-ZEROS-LABEL: frintz_f64_i64_f64_nxv4i64:
+; SIGNED-ZEROS: // %bb.0:
+; SIGNED-ZEROS-NEXT: ptrue p0.d
+; SIGNED-ZEROS-NEXT: fcvtzs z1.d, p0/m, z1.d
+; SIGNED-ZEROS-NEXT: fcvtzs z0.d, p0/m, z0.d
+; SIGNED-ZEROS-NEXT: scvtf z0.d, p0/m, z0.d
+; SIGNED-ZEROS-NEXT: scvtf z1.d, p0/m, z1.d
+; SIGNED-ZEROS-NEXT: ret
+ %res = fptosi <vscale x 4 x double> %in to <vscale x 4 x i64>
+ %res2 = sitofp <vscale x 4 x i64> %res to <vscale x 4 x double>
+ ret <vscale x 4 x double> %res2
+}
diff --git a/llvm/test/CodeGen/AMDGPU/fptoui_uitofp.ll b/llvm/test/CodeGen/AMDGPU/fptoui_uitofp.ll
index 49204f84acb85..a99cf8ef403d8 100644
--- a/llvm/test/CodeGen/AMDGPU/fptoui_uitofp.ll
+++ b/llvm/test/CodeGen/AMDGPU/fptoui_uitofp.ll
@@ -239,44 +239,25 @@ define amdgpu_kernel void @fptoui_f64_to_i64_to_f64(ptr addrspace(1) %out, doubl
; GFX6: ; %bb.0: ; %entry
; GFX6-NEXT: s_load_dwordx4 s[0:3], s[4:5], 0x9
; GFX6-NEXT: s_mov_b32 s6, -1
-; GFX6-NEXT: s_mov_b32 s5, 0xfffff
-; GFX6-NEXT: s_mov_b32 s4, s6
-; GFX6-NEXT: v_not_b32_e32 v0, 31
-; GFX6-NEXT: s_waitcnt lgkmcnt(0)
-; GFX6-NEXT: s_bfe_u32 s7, s3, 0xb0014
-; GFX6-NEXT: s_addk_i32 s7, 0xfc01
-; GFX6-NEXT: s_lshr_b64 s[4:5], s[4:5], s7
-; GFX6-NEXT: s_and_b32 s8, s3, 0x80000000
-; GFX6-NEXT: s_andn2_b64 s[4:5], s[2:3], s[4:5]
-; GFX6-NEXT: s_cmp_lt_i32 s7, 0
-; GFX6-NEXT: s_cselect_b32 s4, 0, s4
-; GFX6-NEXT: s_cselect_b32 s5, s8, s5
-; GFX6-NEXT: s_cmp_gt_i32 s7, 51
-; GFX6-NEXT: s_cselect_b32 s3, s3, s5
-; GFX6-NEXT: s_cselect_b32 s2, s2, s4
-; GFX6-NEXT: v_ldexp_f64 v[0:1], s[2:3], v0
-; GFX6-NEXT: v_mov_b32_e32 v4, -1
-; GFX6-NEXT: v_fract_f64_e32 v[2:3], v[0:1]
-; GFX6-NEXT: v_mov_b32_e32 v5, 0x3fefffff
-; GFX6-NEXT: v_min_f64 v[2:3], v[2:3], v[4:5]
-; GFX6-NEXT: v_cmp_class_f64_e64 vcc, v[0:1], 3
-; GFX6-NEXT: s_mov_b32 s4, 0
-; GFX6-NEXT: v_cndmask_b32_e32 v2, v2, v0, vcc
-; GFX6-NEXT: v_cndmask_b32_e32 v3, v3, v1, vcc
-; GFX6-NEXT: v_add_f64 v[0:1], v[0:1], -v[2:3]
-; GFX6-NEXT: v_mov_b32_e32 v2, s2
-; GFX6-NEXT: s_mov_b32 s5, 0xc1f00000
-; GFX6-NEXT: v_mov_b32_e32 v3, s3
-; GFX6-NEXT: v_fma_f64 v[2:3], v[0:1], s[4:5], v[2:3]
-; GFX6-NEXT: v_cvt_u32_f64_e32 v0, v[0:1]
-; GFX6-NEXT: v_cvt_u32_f64_e32 v2, v[2:3]
; GFX6-NEXT: s_mov_b32 s7, 0xf000
+; GFX6-NEXT: s_waitcnt lgkmcnt(0)
+; GFX6-NEXT: s_bitset0_b32 s3, 31
; GFX6-NEXT: s_mov_b32 s4, s0
-; GFX6-NEXT: v_cvt_f64_u32_e32 v[0:1], v0
-; GFX6-NEXT: v_cvt_f64_u32_e32 v[2:3], v2
+; GFX6-NEXT: s_bfe_u32 s0, s3, 0xb0014
; GFX6-NEXT: s_mov_b32 s5, s1
-; GFX6-NEXT: v_ldexp_f64 v[0:1], v[0:1], 32
-; GFX6-NEXT: v_add_f64 v[0:1], v[0:1], v[2:3]
+; GFX6-NEXT: s_add_i32 s8, s0, 0xfffffc01
+; GFX6-NEXT: s_mov_b32 s1, 0xfffff
+; GFX6-NEXT: s_mov_b32 s0, s6
+; GFX6-NEXT: s_lshr_b64 s[0:1], s[0:1], s8
+; GFX6-NEXT: s_andn2_b64 s[0:1], s[2:3], s[0:1]
+; GFX6-NEXT: s_cmp_lt_i32 s8, 0
+; GFX6-NEXT: s_cselect_b32 s0, 0, s0
+; GFX6-NEXT: s_cselect_b32 s1, 0, s1
+; GFX6-NEXT: s_cmp_gt_i32 s8, 51
+; GFX6-NEXT: s_cselect_b32 s1, s3, s1
+; GFX6-NEXT: s_cselect_b32 s0, s2, s0
+; GFX6-NEXT: v_mov_b32_e32 v0, s0
+; GFX6-NEXT: v_mov_b32_e32 v1, s1
; GFX6-NEXT: buffer_store_dwordx2 v[0:1], off, s[4:7], 0
; GFX6-NEXT: s_endpgm
;
More information about the llvm-commits
mailing list