[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