[llvm] [InstCombine] Treat `uitofp nneg` as non-negative signed range for fcmp clamp folds (PR #208234)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 9 23:47:45 PDT 2026


https://github.com/imkiva updated https://github.com/llvm/llvm-project/pull/208234

>From 190b2494425c4f2c9f3938f94d535c9617b44411 Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Wed, 8 Jul 2026 21:45:01 +0800
Subject: [PATCH 1/4] [InstCombine] Treat uitofp nneg as non-negative signed
 range for fcmp clamp folds

---
 .../InstCombine/InstCombineCompares.cpp       |  58 +++++++-
 .../InstCombine/uitofp-nneg-fcmp-select.ll    | 140 ++++++++++++++++++
 2 files changed, 194 insertions(+), 4 deletions(-)
 create mode 100644 llvm/test/Transforms/InstCombine/uitofp-nneg-fcmp-select.ll

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index e6deb548819e8..7861745cfe35f 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -8408,6 +8408,48 @@ Instruction *InstCombinerImpl::foldFCmpIntToFPConst(FCmpInst &I,
     }
   }
 
+  Value *X = LHSI->getOperand(0);
+  bool IsNonNegUIToFP =
+      LHSUnsigned && cast<PossiblyNonNegInst>(LHSI)->hasNonNeg();
+  if (IsNonNegUIToFP && IsExact) {
+    APSInt ZeroInt(APInt::getZero(IntWidth), /*isUnsigned=*/true);
+    Constant *ZeroC = ConstantInt::get(IntTy, APInt::getZero(IntWidth));
+    if (RHSInt == ZeroInt) {
+      switch (Pred) {
+      default:
+        break;
+      case ICmpInst::ICMP_UGE:
+        // The defined range of (uitofp nneg x) starts at zero.
+        return replaceInstUsesWith(I, ConstantInt::getTrue(I.getType()));
+      case ICmpInst::ICMP_UGT:
+        return new ICmpInst(ICmpInst::ICMP_NE, X, ZeroC);
+      case ICmpInst::ICMP_ULE:
+        return new ICmpInst(ICmpInst::ICMP_EQ, X, ZeroC);
+      case ICmpInst::ICMP_ULT:
+        return replaceInstUsesWith(I, ConstantInt::getFalse(I.getType()));
+      }
+    }
+
+    APInt SignedMax = APInt::getSignedMaxValue(IntWidth);
+    APSInt SignedMaxInt(SignedMax, /*isUnsigned=*/true);
+    if (RHSInt == SignedMaxInt) {
+      Constant *SignedMaxC =
+          ConstantInt::get(IntTy, APInt::getSignedMaxValue(IntWidth));
+      switch (Pred) {
+      default:
+        break;
+      case ICmpInst::ICMP_UGE:
+        return new ICmpInst(ICmpInst::ICMP_EQ, X, SignedMaxC);
+      case ICmpInst::ICMP_UGT:
+        return replaceInstUsesWith(I, ConstantInt::getFalse(I.getType()));
+      case ICmpInst::ICMP_ULE:
+        return replaceInstUsesWith(I, ConstantInt::getTrue(I.getType()));
+      case ICmpInst::ICMP_ULT:
+        return new ICmpInst(ICmpInst::ICMP_NE, X, SignedMaxC);
+      }
+    }
+  }
+
   // Lower this FP comparison into an appropriate integer version of the
   // comparison.
   return new ICmpInst(Pred, LHSI->getOperand(0),
@@ -9037,11 +9079,19 @@ static bool isMinMaxCmpSelectEliminable(SelectPatternFlavor Flavor, Value *A,
     return false;
 
   bool IsUnsigned = I->getOpcode() == Instruction::UIToFP;
+  bool IsNonNegUIToFP =
+      IsUnsigned && cast<PossiblyNonNegInst>(I)->hasNonNeg();
   unsigned BitWidth = I->getOperand(0)->getType()->getScalarSizeInBits();
-  APSInt IntBoundary = (Flavor == SPF_FMAXNUM)
-                           ? APSInt::getMinValue(BitWidth, IsUnsigned)
-                           : APSInt::getMaxValue(BitWidth, IsUnsigned);
-  APSInt ConvertedInt(BitWidth, IsUnsigned);
+  APSInt LowerBoundary = APSInt::getMinValue(BitWidth, IsUnsigned);
+  // For uitofp nneg, negative signed inputs are poison, so the defined upper
+  // bound is signed max rather than unsigned max.
+  APSInt UpperBoundary =
+      IsNonNegUIToFP
+          ? APSInt(APInt::getSignedMaxValue(BitWidth), /*isUnsigned=*/true)
+          : APSInt::getMaxValue(BitWidth, IsUnsigned);
+  APSInt IntBoundary =
+      Flavor == SPF_FMAXNUM ? LowerBoundary : UpperBoundary;
+  APSInt ConvertedInt(BitWidth, IntBoundary.isUnsigned());
   bool IsExact;
   APFloat::opStatus Status =
       APF->convertToInteger(ConvertedInt, APFloat::rmTowardZero, &IsExact);
diff --git a/llvm/test/Transforms/InstCombine/uitofp-nneg-fcmp-select.ll b/llvm/test/Transforms/InstCombine/uitofp-nneg-fcmp-select.ll
new file mode 100644
index 0000000000000..7019a2e6e0e7c
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/uitofp-nneg-fcmp-select.ll
@@ -0,0 +1,140 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define float @uitofp_nneg_i8_fmin_smax(i8 %x) {
+; CHECK-LABEL: define float @uitofp_nneg_i8_fmin_smax(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[F:%.*]] = uitofp i8 [[X]] to float
+; CHECK-NEXT:    ret float [[F]]
+;
+  %f = uitofp nneg i8 %x to float
+  %cmp = fcmp oge float %f, 1.270000e+02
+  %sel = select i1 %cmp, float 1.270000e+02, float %f
+  ret float %sel
+}
+
+define float @uitofp_nneg_i8_fmin_smax_inverted(i8 %x) {
+; CHECK-LABEL: define float @uitofp_nneg_i8_fmin_smax_inverted(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[F:%.*]] = uitofp i8 [[X]] to float
+; CHECK-NEXT:    ret float [[F]]
+;
+  %f = uitofp nneg i8 %x to float
+  %cmp = fcmp olt float %f, 1.270000e+02
+  %sel = select i1 %cmp, float %f, float 1.270000e+02
+  ret float %sel
+}
+
+define float @uitofp_nneg_i8_fmax_zero(i8 %x) {
+; CHECK-LABEL: define float @uitofp_nneg_i8_fmax_zero(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[F:%.*]] = uitofp i8 [[X]] to float
+; CHECK-NEXT:    ret float [[F]]
+;
+  %f = uitofp nneg i8 %x to float
+  %cmp = fcmp ole float %f, 0.000000e+00
+  %sel = select i1 %cmp, float 0.000000e+00, float %f
+  ret float %sel
+}
+
+define float @uitofp_i8_no_nneg_fmin_127_not_identity(i8 %x) {
+; CHECK-LABEL: define float @uitofp_i8_no_nneg_fmin_127_not_identity(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[F:%.*]] = uitofp i8 [[X]] to float
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp oge float [[F]], 1.270000e+02
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float 1.270000e+02, float [[F]]
+; CHECK-NEXT:    ret float [[SEL]]
+;
+  %f = uitofp i8 %x to float
+  %cmp = fcmp oge float %f, 1.270000e+02
+  %sel = select i1 %cmp, float 1.270000e+02, float %f
+  ret float %sel
+}
+
+define float @uitofp_nneg_i8_fmin_126_not_identity(i8 %x) {
+; CHECK-LABEL: define float @uitofp_nneg_i8_fmin_126_not_identity(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[F:%.*]] = uitofp nneg i8 [[X]] to float
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp oge float [[F]], 1.260000e+02
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float 1.260000e+02, float [[F]]
+; CHECK-NEXT:    ret float [[SEL]]
+;
+  %f = uitofp nneg i8 %x to float
+  %cmp = fcmp oge float %f, 1.260000e+02
+  %sel = select i1 %cmp, float 1.260000e+02, float %f
+  ret float %sel
+}
+
+define i1 @uitofp_nneg_i8_cmp_oge_smax(i8 %x) {
+; CHECK-LABEL: define i1 @uitofp_nneg_i8_cmp_oge_smax(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X]], 127
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %f = uitofp nneg i8 %x to float
+  %cmp = fcmp oge float %f, 1.270000e+02
+  ret i1 %cmp
+}
+
+define i1 @uitofp_nneg_i8_cmp_olt_smax(i8 %x) {
+; CHECK-LABEL: define i1 @uitofp_nneg_i8_cmp_olt_smax(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[X]], 127
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %f = uitofp nneg i8 %x to float
+  %cmp = fcmp olt float %f, 1.270000e+02
+  ret i1 %cmp
+}
+
+define i1 @uitofp_nneg_i8_cmp_ogt_smax(i8 %x) {
+; CHECK-LABEL: define i1 @uitofp_nneg_i8_cmp_ogt_smax(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %f = uitofp nneg i8 %x to float
+  %cmp = fcmp ogt float %f, 1.270000e+02
+  ret i1 %cmp
+}
+
+define i1 @uitofp_nneg_i8_cmp_ole_smax(i8 %x) {
+; CHECK-LABEL: define i1 @uitofp_nneg_i8_cmp_ole_smax(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 true
+;
+  %f = uitofp nneg i8 %x to float
+  %cmp = fcmp ole float %f, 1.270000e+02
+  ret i1 %cmp
+}
+
+define i1 @uitofp_nneg_i8_cmp_ogt_zero(i8 %x) {
+; CHECK-LABEL: define i1 @uitofp_nneg_i8_cmp_ogt_zero(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[X]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %f = uitofp nneg i8 %x to float
+  %cmp = fcmp ogt float %f, 0.000000e+00
+  ret i1 %cmp
+}
+
+define i1 @uitofp_nneg_i8_cmp_olt_zero(i8 %x) {
+; CHECK-LABEL: define i1 @uitofp_nneg_i8_cmp_olt_zero(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %f = uitofp nneg i8 %x to float
+  %cmp = fcmp olt float %f, 0.000000e+00
+  ret i1 %cmp
+}
+
+define <2 x i1> @uitofp_nneg_v2i8_cmp_ogt_zero(<2 x i8> %x) {
+; CHECK-LABEL: define <2 x i1> @uitofp_nneg_v2i8_cmp_ogt_zero(
+; CHECK-SAME: <2 x i8> [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne <2 x i8> [[X]], zeroinitializer
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %f = uitofp nneg <2 x i8> %x to <2 x float>
+  %cmp = fcmp ogt <2 x float> %f, zeroinitializer
+  ret <2 x i1> %cmp
+}

>From 29114edd8cbbbd1104284bd207c3f725b028a21f Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Wed, 8 Jul 2026 22:40:12 +0800
Subject: [PATCH 2/4] [InstCombine] Also handle out-of-range path

---
 .../InstCombine/InstCombineCompares.cpp       | 12 +++--
 .../InstCombine/uitofp-nneg-fcmp-select.ll    | 51 +++++++++++++++++++
 2 files changed, 59 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 7861745cfe35f..8146784bc5a0e 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -8300,10 +8300,14 @@ Instruction *InstCombinerImpl::foldFCmpIntToFPConst(FCmpInst &I,
   } else {
     // If the RHS value is > UnsignedMax, fold the comparison. This handles
     // +INF and large values.
-    APFloat UMax(RHS->getSemantics());
-    UMax.convertFromAPInt(APInt::getMaxValue(IntWidth), false,
-                          APFloat::rmNearestTiesToEven);
-    if (UMax < *RHS) { // umax < 13123.0
+    // For uitofp nneg, negative signed inputs are poison, so the defined upper
+    // bound is signed max rather than unsigned max.
+    APFloat Max(RHS->getSemantics());
+    bool IsNonNegUIToFP = cast<PossiblyNonNegInst>(LHSI)->hasNonNeg();
+    APInt MaxInt = IsNonNegUIToFP ? APInt::getSignedMaxValue(IntWidth)
+                                  : APInt::getMaxValue(IntWidth);
+    Max.convertFromAPInt(MaxInt, false, APFloat::rmNearestTiesToEven);
+    if (Max < *RHS) { // max < 13123.0
       if (Pred == ICmpInst::ICMP_NE || Pred == ICmpInst::ICMP_ULT ||
           Pred == ICmpInst::ICMP_ULE)
         return replaceInstUsesWith(I, ConstantInt::getTrue(I.getType()));
diff --git a/llvm/test/Transforms/InstCombine/uitofp-nneg-fcmp-select.ll b/llvm/test/Transforms/InstCombine/uitofp-nneg-fcmp-select.ll
index 7019a2e6e0e7c..1484664e87ddd 100644
--- a/llvm/test/Transforms/InstCombine/uitofp-nneg-fcmp-select.ll
+++ b/llvm/test/Transforms/InstCombine/uitofp-nneg-fcmp-select.ll
@@ -107,6 +107,57 @@ define i1 @uitofp_nneg_i8_cmp_ole_smax(i8 %x) {
   ret i1 %cmp
 }
 
+define i1 @uitofp_nneg_i8_cmp_olt_above_smax(i8 %x) {
+; CHECK-LABEL: define i1 @uitofp_nneg_i8_cmp_olt_above_smax(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 true
+;
+  %f = uitofp nneg i8 %x to float
+  %cmp = fcmp olt float %f, 1.280000e+02
+  ret i1 %cmp
+}
+
+define i1 @uitofp_nneg_i8_cmp_oge_above_smax(i8 %x) {
+; CHECK-LABEL: define i1 @uitofp_nneg_i8_cmp_oge_above_smax(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %f = uitofp nneg i8 %x to float
+  %cmp = fcmp oge float %f, 1.280000e+02
+  ret i1 %cmp
+}
+
+define i1 @uitofp_nneg_i8_cmp_oeq_above_smax(i8 %x) {
+; CHECK-LABEL: define i1 @uitofp_nneg_i8_cmp_oeq_above_smax(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %f = uitofp nneg i8 %x to float
+  %cmp = fcmp oeq float %f, 1.280000e+02
+  ret i1 %cmp
+}
+
+define i1 @uitofp_nneg_i8_cmp_one_above_smax(i8 %x) {
+; CHECK-LABEL: define i1 @uitofp_nneg_i8_cmp_one_above_smax(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 true
+;
+  %f = uitofp nneg i8 %x to float
+  %cmp = fcmp one float %f, 1.280000e+02
+  ret i1 %cmp
+}
+
+define i1 @uitofp_i8_no_nneg_cmp_olt_128_not_true(i8 %x) {
+; CHECK-LABEL: define i1 @uitofp_i8_no_nneg_cmp_olt_128_not_true(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[X]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %f = uitofp i8 %x to float
+  %cmp = fcmp olt float %f, 1.280000e+02
+  ret i1 %cmp
+}
+
 define i1 @uitofp_nneg_i8_cmp_ogt_zero(i8 %x) {
 ; CHECK-LABEL: define i1 @uitofp_nneg_i8_cmp_ogt_zero(
 ; CHECK-SAME: i8 [[X:%.*]]) {

>From 1c824c7711ed6231570180ecfd9e421adfd5dcfd Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Wed, 8 Jul 2026 22:52:28 +0800
Subject: [PATCH 3/4] format code

---
 llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 8146784bc5a0e..3a48d57b88005 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -9083,8 +9083,7 @@ static bool isMinMaxCmpSelectEliminable(SelectPatternFlavor Flavor, Value *A,
     return false;
 
   bool IsUnsigned = I->getOpcode() == Instruction::UIToFP;
-  bool IsNonNegUIToFP =
-      IsUnsigned && cast<PossiblyNonNegInst>(I)->hasNonNeg();
+  bool IsNonNegUIToFP = IsUnsigned && cast<PossiblyNonNegInst>(I)->hasNonNeg();
   unsigned BitWidth = I->getOperand(0)->getType()->getScalarSizeInBits();
   APSInt LowerBoundary = APSInt::getMinValue(BitWidth, IsUnsigned);
   // For uitofp nneg, negative signed inputs are poison, so the defined upper
@@ -9093,8 +9092,7 @@ static bool isMinMaxCmpSelectEliminable(SelectPatternFlavor Flavor, Value *A,
       IsNonNegUIToFP
           ? APSInt(APInt::getSignedMaxValue(BitWidth), /*isUnsigned=*/true)
           : APSInt::getMaxValue(BitWidth, IsUnsigned);
-  APSInt IntBoundary =
-      Flavor == SPF_FMAXNUM ? LowerBoundary : UpperBoundary;
+  APSInt IntBoundary = Flavor == SPF_FMAXNUM ? LowerBoundary : UpperBoundary;
   APSInt ConvertedInt(BitWidth, IntBoundary.isUnsigned());
   bool IsExact;
   APFloat::opStatus Status =

>From 1a6e4ce426552f3e2830fd5472cab96e21edd3d3 Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Fri, 10 Jul 2026 14:47:29 +0800
Subject: [PATCH 4/4] more tests

---
 .../InstCombine/uitofp-nneg-fcmp-select.ll          | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/uitofp-nneg-fcmp-select.ll b/llvm/test/Transforms/InstCombine/uitofp-nneg-fcmp-select.ll
index 1484664e87ddd..6813c3d1cc310 100644
--- a/llvm/test/Transforms/InstCombine/uitofp-nneg-fcmp-select.ll
+++ b/llvm/test/Transforms/InstCombine/uitofp-nneg-fcmp-select.ll
@@ -147,6 +147,19 @@ define i1 @uitofp_nneg_i8_cmp_one_above_smax(i8 %x) {
   ret i1 %cmp
 }
 
+; INT_MAX rounds to 2^31 in float, so this comparison is not always true.
+define i1 @uitofp_nneg_i32_cmp_olt_smax_plus_one_not_true(i32 %x) {
+; CHECK-LABEL: define i1 @uitofp_nneg_i32_cmp_olt_smax_plus_one_not_true(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[F:%.*]] = uitofp nneg i32 [[X]] to float
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp olt float [[F]], f0x4F000000
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %f = uitofp nneg i32 %x to float
+  %cmp = fcmp olt float %f, 0x41E0000000000000
+  ret i1 %cmp
+}
+
 define i1 @uitofp_i8_no_nneg_cmp_olt_128_not_true(i8 %x) {
 ; CHECK-LABEL: define i1 @uitofp_i8_no_nneg_cmp_olt_128_not_true(
 ; CHECK-SAME: i8 [[X:%.*]]) {



More information about the llvm-commits mailing list