[llvm] [DAGCombiner] Extend FP-to-Int cast without requiring nsz (PR #161093)

Yi-Chi Lee via llvm-commits llvm-commits at lists.llvm.org
Sun Sep 28 13:07:37 PDT 2025


https://github.com/yichi170 created https://github.com/llvm/llvm-project/pull/161093

This patch updates the FP-to-Int conversion handling:
- For signed integers: use `ftrunc` followed by clamping to the target integer range.
- For unsigned integers: apply `fabs` + `ftrunc`, then clamp.

This removes the previous dependence on `nsz` and ensures correct lowering for both signed and unsigned cases.

I've tested the code generation of -mtriple=amdgcn. It seems that the assembly code is expected, but I'm not sure how to write a general testcase for every target.

Fixes #160623.

>From 51bf419075bc7a1e2f8d1e109893c844a03a2ce6 Mon Sep 17 00:00:00 2001
From: Yi-Chi Lee <yichi170 at gmail.com>
Date: Sun, 28 Sep 2025 10:41:29 -0500
Subject: [PATCH 1/2] [DAGCombiner] Extend FP-to-Int cast without requiring nsz

---
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 37 ++++++++++++++++---
 1 file changed, 31 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index a6ba6e518899f..d3798eedc82a4 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -18869,20 +18869,45 @@ static SDValue foldFPToIntToFP(SDNode *N, const SDLoc &DL, SelectionDAG &DAG,
   // FIXME: We should be able to use node-level FMF here.
   // TODO: If strict math, should we use FABS (+ range check for signed cast)?
   EVT VT = N->getValueType(0);
-  if (!TLI.isOperationLegal(ISD::FTRUNC, VT) ||
-      !DAG.getTarget().Options.NoSignedZerosFPMath)
+  if (!TLI.isOperationLegal(ISD::FTRUNC, VT))
     return SDValue();
 
   // fptosi/fptoui round towards zero, so converting from FP to integer and
   // back is the same as an 'ftrunc': [us]itofp (fpto[us]i X) --> ftrunc X
   SDValue N0 = N->getOperand(0);
   if (N->getOpcode() == ISD::SINT_TO_FP && N0.getOpcode() == ISD::FP_TO_SINT &&
-      N0.getOperand(0).getValueType() == VT)
-    return DAG.getNode(ISD::FTRUNC, DL, VT, N0.getOperand(0));
+      N0.getOperand(0).getValueType() == VT) {
+    if (DAG.getTarget().Options.NoSignedZerosFPMath)
+      return DAG.getNode(ISD::FTRUNC, DL, VT, N0.getOperand(0));
+
+    unsigned IntWidth = N0.getValueSizeInBits();
+    APInt APMax = APInt::getSignedMaxValue(IntWidth);
+    APInt APMin = APInt::getSignedMinValue(IntWidth);
+
+    APFloat MaxAPF(VT.getFltSemantics());
+    MaxAPF.convertFromAPInt(APMax, true, APFloat::rmTowardZero);
+    APFloat MinAPF(VT.getFltSemantics());
+    MinAPF.convertFromAPInt(APMin, true, APFloat::rmTowardZero);
+
+    SDValue MaxFP = DAG.getConstantFP(MaxAPF, DL, VT);
+    SDValue MinFP = DAG.getConstantFP(MinAPF, DL, VT);
+
+    SDValue Clamped = DAG.getNode(ISD::FMINNUM, DL, VT,
+                                  DAG.getNode(ISD::FMAXNUM, DL, VT, N0->getOperand(0), MinFP),
+                                  MaxFP);
+    return DAG.getNode(ISD::FTRUNC, DL, VT, Clamped);
+  }
 
   if (N->getOpcode() == ISD::UINT_TO_FP && N0.getOpcode() == ISD::FP_TO_UINT &&
-      N0.getOperand(0).getValueType() == VT)
-    return DAG.getNode(ISD::FTRUNC, DL, VT, N0.getOperand(0));
+      N0.getOperand(0).getValueType() == VT) {
+    if (DAG.getTarget().Options.NoSignedZerosFPMath)
+      return DAG.getNode(ISD::FTRUNC, DL, VT, N0.getOperand(0));
+
+    if (TLI.isFAbsFree(VT)) {
+      SDValue Abs = DAG.getNode(ISD::FABS, DL, VT, N0.getOperand(0));
+      return DAG.getNode(ISD::FTRUNC, DL, VT, Abs);
+    }
+  }
 
   return SDValue();
 }

>From cccc07319d26ed660107eaa53bb363547ef09c43 Mon Sep 17 00:00:00 2001
From: Yi-Chi Lee <yichi170 at gmail.com>
Date: Sun, 28 Sep 2025 14:46:41 -0500
Subject: [PATCH 2/2] [DAGCombiner] Modify the comment to fit the current
 implementation and apply clang-format

---
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 21 ++++++++++++-------
 1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index d3798eedc82a4..65cea64e0982d 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -18862,12 +18862,15 @@ SDValue DAGCombiner::visitFPOW(SDNode *N) {
 
 static SDValue foldFPToIntToFP(SDNode *N, const SDLoc &DL, SelectionDAG &DAG,
                                const TargetLowering &TLI) {
-  // We only do this if the target has legal ftrunc. Otherwise, we'd likely be
-  // replacing casts with a libcall. We also must be allowed to ignore -0.0
-  // because FTRUNC will return -0.0 for (-1.0, -0.0), but using integer
-  // conversions would return +0.0.
+  // We can fold the fpto[us]i -> [us]itofp pattern into a single ftrunc.
+  // If NoSignedZerosFPMath is enabled, this is a direct replacement.
+  // Otherwise, for strict math, we must handle edge cases:
+  // 1. For signed conversions, clamp out-of-range values to the valid
+  //    integer range before the trunc.
+  // 2. For unsigned conversions, use FABS. A negative float becomes integer 0,
+  //    which must convert back to +0.0. FTRUNC on its own could produce -0.0.
+
   // FIXME: We should be able to use node-level FMF here.
-  // TODO: If strict math, should we use FABS (+ range check for signed cast)?
   EVT VT = N->getValueType(0);
   if (!TLI.isOperationLegal(ISD::FTRUNC, VT))
     return SDValue();
@@ -18880,6 +18883,7 @@ static SDValue foldFPToIntToFP(SDNode *N, const SDLoc &DL, SelectionDAG &DAG,
     if (DAG.getTarget().Options.NoSignedZerosFPMath)
       return DAG.getNode(ISD::FTRUNC, DL, VT, N0.getOperand(0));
 
+    // Strict math: clamp to the signed integer range before truncating.
     unsigned IntWidth = N0.getValueSizeInBits();
     APInt APMax = APInt::getSignedMaxValue(IntWidth);
     APInt APMin = APInt::getSignedMinValue(IntWidth);
@@ -18892,9 +18896,9 @@ static SDValue foldFPToIntToFP(SDNode *N, const SDLoc &DL, SelectionDAG &DAG,
     SDValue MaxFP = DAG.getConstantFP(MaxAPF, DL, VT);
     SDValue MinFP = DAG.getConstantFP(MinAPF, DL, VT);
 
-    SDValue Clamped = DAG.getNode(ISD::FMINNUM, DL, VT,
-                                  DAG.getNode(ISD::FMAXNUM, DL, VT, N0->getOperand(0), MinFP),
-                                  MaxFP);
+    SDValue Clamped = DAG.getNode(
+        ISD::FMINNUM, DL, VT,
+        DAG.getNode(ISD::FMAXNUM, DL, VT, N0->getOperand(0), MinFP), MaxFP);
     return DAG.getNode(ISD::FTRUNC, DL, VT, Clamped);
   }
 
@@ -18903,6 +18907,7 @@ static SDValue foldFPToIntToFP(SDNode *N, const SDLoc &DL, SelectionDAG &DAG,
     if (DAG.getTarget().Options.NoSignedZerosFPMath)
       return DAG.getNode(ISD::FTRUNC, DL, VT, N0.getOperand(0));
 
+    // Strict math: use FABS to handle negative inputs correctly.
     if (TLI.isFAbsFree(VT)) {
       SDValue Abs = DAG.getNode(ISD::FABS, DL, VT, N0.getOperand(0));
       return DAG.getNode(ISD::FTRUNC, DL, VT, Abs);



More information about the llvm-commits mailing list