[llvm-branch-commits] [llvm] [InstCombine] Fold fpto[su]i.sat of NaN guarded select (PR #230188)

Harrison Hao via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Oct 12 01:56:21 PDT 2026


https://github.com/harrisonGPU updated https://github.com/llvm/llvm-project/pull/230188

>From bd737a9551a1df002b769a0e7cb55d203dd87762 Mon Sep 17 00:00:00 2001
From: Harrison Hao <tsworld1314 at gmail.com>
Date: Thu, 8 Oct 2026 23:08:24 +0800
Subject: [PATCH 1/2] [InstCombine] Fold fpto[su]i.sat of NaN guarded select

fpto[su]i.sat returns 0 for both NaN and $\pm 0.0$, so a select that
replaces a NaN input with zero does not change the result, and the call
can take X directly.

```llvm
%not.nan = fcmp ord float %x, 0.0
%sel = select i1 %not.nan, float %x, float 0.0
%r = call i32 @llvm.fptosi.sat.i32.f32(float %sel)
  -->
%r = call i32 @llvm.fptosi.sat.i32.f32(float %x)
```

The inverted form `select (fcmp uno X, 0.0), 0.0, X` is folded too.
Only the canonical `fcmp ord/uno X, 0.0` is matched, which also covers
`oeq/une X, X` after fcmp canonicalization.
---
 .../Transforms/InstCombine/InstCombineCalls.cpp | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 6fb5f50244a46..8a1b23eb0b5b2 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -4523,10 +4523,25 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
     break;
   }
   case Intrinsic::fptoui_sat:
-  case Intrinsic::fptosi_sat:
+  case Intrinsic::fptosi_sat: {
     if (Instruction *I = foldItoFPtoI(*II))
       return I;
+
+    Value *Src = II->getArgOperand(0);
+    Value *X;
+    // select (fcmp ord X, 0.0), X, +-0.0 --> X
+    if (match(Src, m_Select(m_SpecificFCmp(FCmpInst::FCMP_ORD, m_Value(X),
+                                           m_AnyZeroFP()),
+                            m_Deferred(X), m_AnyZeroFP())))
+      return replaceOperand(*II, 0, X);
+
+    // select (fcmp uno X, 0.0), +-0.0, X --> X
+    if (match(Src, m_Select(m_SpecificFCmp(FCmpInst::FCMP_UNO, m_Value(X),
+                                           m_AnyZeroFP()),
+                            m_AnyZeroFP(), m_Deferred(X))))
+      return replaceOperand(*II, 0, X);
     break;
+  }
   case Intrinsic::frexp: {
     // frexp(frexp(x).fract) -> { frexp(x).fract, 0 }: the fraction operand is
     // already normalized, so the first result is idempotent and the second is

>From 8e02f08f60ee463fa97951143634b473efce1e30 Mon Sep 17 00:00:00 2001
From: Harrison Hao <tsworld1314 at gmail.com>
Date: Mon, 12 Oct 2026 15:53:36 +0800
Subject: [PATCH 2/2] Implement in Simplify

---
 llvm/lib/Analysis/InstructionSimplify.cpp     | 17 +++++++++++
 .../InstCombine/InstCombineCalls.cpp          | 14 ---------
 .../test/Transforms/InstSimplify/fptoi-sat.ll | 30 +++++++------------
 3 files changed, 27 insertions(+), 34 deletions(-)

diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 74a266660b234..d1fb62e77fcba 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -4982,6 +4982,23 @@ static Value *simplifySelectWithFCmp(Value *Cond, Value *T, Value *F,
     return nullptr;
   FCmpInst *I = cast<FCmpInst>(Cond);
 
+  Value *X = nullptr;
+  auto FPToISat =
+      m_AnyIntrinsic<Intrinsic::fptosi_sat, Intrinsic::fptoui_sat>();
+  // select (fcmp ord X, 0.0), fpto[su]i.sat(X), 0 --> fpto[su]i.sat(X)
+  if (match(Cond,
+            m_SpecificFCmp(FCmpInst::FCMP_ORD, m_Value(X), m_AnyZeroFP())) &&
+      match(T, m_CombineAnd(FPToISat, m_Argument<0>(m_Specific(X)))) &&
+      match(F, m_Zero()))
+    return T;
+
+  // select (fcmp uno X, 0.0), 0, fpto[su]i.sat(X) --> fpto[su]i.sat(X)
+  if (match(Cond,
+            m_SpecificFCmp(FCmpInst::FCMP_UNO, m_Value(X), m_AnyZeroFP())) &&
+      match(F, m_CombineAnd(FPToISat, m_Argument<0>(m_Specific(X)))) &&
+      match(T, m_Zero()))
+    return F;
+
   bool IsEquiv = I->isEquivalence();
   if (I->isEquivalence(/*Invert=*/true)) {
     std::swap(T, F);
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 8a1b23eb0b5b2..8cae13805b3b3 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -4526,20 +4526,6 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
   case Intrinsic::fptosi_sat: {
     if (Instruction *I = foldItoFPtoI(*II))
       return I;
-
-    Value *Src = II->getArgOperand(0);
-    Value *X;
-    // select (fcmp ord X, 0.0), X, +-0.0 --> X
-    if (match(Src, m_Select(m_SpecificFCmp(FCmpInst::FCMP_ORD, m_Value(X),
-                                           m_AnyZeroFP()),
-                            m_Deferred(X), m_AnyZeroFP())))
-      return replaceOperand(*II, 0, X);
-
-    // select (fcmp uno X, 0.0), +-0.0, X --> X
-    if (match(Src, m_Select(m_SpecificFCmp(FCmpInst::FCMP_UNO, m_Value(X),
-                                           m_AnyZeroFP()),
-                            m_AnyZeroFP(), m_Deferred(X))))
-      return replaceOperand(*II, 0, X);
     break;
   }
   case Intrinsic::frexp: {
diff --git a/llvm/test/Transforms/InstSimplify/fptoi-sat.ll b/llvm/test/Transforms/InstSimplify/fptoi-sat.ll
index bfd5f083cb2c7..56bcc94010ce5 100644
--- a/llvm/test/Transforms/InstSimplify/fptoi-sat.ll
+++ b/llvm/test/Transforms/InstSimplify/fptoi-sat.ll
@@ -613,10 +613,8 @@ define <2 x i32> @fptoui_f32_to_i32_vec() {
 
 define i32 @fptosi_sat_select_ord(float %a) {
 ; CHECK-LABEL: @fptosi_sat_select_ord(
-; CHECK-NEXT:    [[NOT_NAN:%.*]] = fcmp ord float [[A:%.*]], 0.000000e+00
-; CHECK-NEXT:    [[CVT:%.*]] = call i32 @llvm.fptosi.sat.i32.f32(float [[A]])
-; CHECK-NEXT:    [[R:%.*]] = select i1 [[NOT_NAN]], i32 [[CVT]], i32 0
-; CHECK-NEXT:    ret i32 [[R]]
+; CHECK-NEXT:    [[CVT:%.*]] = call i32 @llvm.fptosi.sat.i32.f32(float [[A:%.*]])
+; CHECK-NEXT:    ret i32 [[CVT]]
 ;
   %not.nan = fcmp ord float %a, 0.000000e+00
   %cvt = call i32 @llvm.fptosi.sat.i32.f32(float %a)
@@ -626,10 +624,8 @@ define i32 @fptosi_sat_select_ord(float %a) {
 
 define i32 @fptoui_sat_select_uno(float %a) {
 ; CHECK-LABEL: @fptoui_sat_select_uno(
-; CHECK-NEXT:    [[IS_NAN:%.*]] = fcmp uno float [[A:%.*]], 0.000000e+00
-; CHECK-NEXT:    [[CVT:%.*]] = call i32 @llvm.fptoui.sat.i32.f32(float [[A]])
-; CHECK-NEXT:    [[R:%.*]] = select i1 [[IS_NAN]], i32 0, i32 [[CVT]]
-; CHECK-NEXT:    ret i32 [[R]]
+; CHECK-NEXT:    [[CVT:%.*]] = call i32 @llvm.fptoui.sat.i32.f32(float [[A:%.*]])
+; CHECK-NEXT:    ret i32 [[CVT]]
 ;
   %is.nan = fcmp uno float %a, 0.000000e+00
   %cvt = call i32 @llvm.fptoui.sat.i32.f32(float %a)
@@ -639,10 +635,8 @@ define i32 @fptoui_sat_select_uno(float %a) {
 
 define i32 @fptoui_sat_select_ord_negzero(float %a) {
 ; CHECK-LABEL: @fptoui_sat_select_ord_negzero(
-; CHECK-NEXT:    [[NOT_NAN:%.*]] = fcmp ord float [[A:%.*]], -0.000000e+00
-; CHECK-NEXT:    [[CVT:%.*]] = call i32 @llvm.fptoui.sat.i32.f32(float [[A]])
-; CHECK-NEXT:    [[R:%.*]] = select i1 [[NOT_NAN]], i32 [[CVT]], i32 0
-; CHECK-NEXT:    ret i32 [[R]]
+; CHECK-NEXT:    [[CVT:%.*]] = call i32 @llvm.fptoui.sat.i32.f32(float [[A:%.*]])
+; CHECK-NEXT:    ret i32 [[CVT]]
 ;
   %not.nan = fcmp ord float %a, -0.000000e+00
   %cvt = call i32 @llvm.fptoui.sat.i32.f32(float %a)
@@ -652,10 +646,8 @@ define i32 @fptoui_sat_select_ord_negzero(float %a) {
 
 define <2 x i32> @fptosi_sat_select_uno_vec(<2 x float> %a) {
 ; CHECK-LABEL: @fptosi_sat_select_uno_vec(
-; CHECK-NEXT:    [[IS_NAN:%.*]] = fcmp uno <2 x float> [[A:%.*]], zeroinitializer
-; CHECK-NEXT:    [[CVT:%.*]] = call <2 x i32> @llvm.fptosi.sat.v2i32.v2f32(<2 x float> [[A]])
-; CHECK-NEXT:    [[R:%.*]] = select <2 x i1> [[IS_NAN]], <2 x i32> zeroinitializer, <2 x i32> [[CVT]]
-; CHECK-NEXT:    ret <2 x i32> [[R]]
+; CHECK-NEXT:    [[CVT:%.*]] = call <2 x i32> @llvm.fptosi.sat.v2i32.v2f32(<2 x float> [[A:%.*]])
+; CHECK-NEXT:    ret <2 x i32> [[CVT]]
 ;
   %is.nan = fcmp uno <2 x float> %a, zeroinitializer
   %cvt = call <2 x i32> @llvm.fptosi.sat.v2i32.v2f32(<2 x float> %a)
@@ -665,10 +657,8 @@ define <2 x i32> @fptosi_sat_select_uno_vec(<2 x float> %a) {
 
 define <2 x i32> @fptoui_sat_select_ord_vec_poison(<2 x float> %a) {
 ; CHECK-LABEL: @fptoui_sat_select_ord_vec_poison(
-; CHECK-NEXT:    [[NOT_NAN:%.*]] = fcmp ord <2 x float> [[A:%.*]], zeroinitializer
-; CHECK-NEXT:    [[CVT:%.*]] = call <2 x i32> @llvm.fptoui.sat.v2i32.v2f32(<2 x float> [[A]])
-; CHECK-NEXT:    [[R:%.*]] = select <2 x i1> [[NOT_NAN]], <2 x i32> [[CVT]], <2 x i32> <i32 0, i32 poison>
-; CHECK-NEXT:    ret <2 x i32> [[R]]
+; CHECK-NEXT:    [[CVT:%.*]] = call <2 x i32> @llvm.fptoui.sat.v2i32.v2f32(<2 x float> [[A:%.*]])
+; CHECK-NEXT:    ret <2 x i32> [[CVT]]
 ;
   %not.nan = fcmp ord <2 x float> %a, zeroinitializer
   %cvt = call <2 x i32> @llvm.fptoui.sat.v2i32.v2f32(<2 x float> %a)



More information about the llvm-branch-commits mailing list