[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