[llvm] [InstCombine] Fold fcmp of two int-to-fp conversions to an integer compare (PR #210521)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Jul 18 09:01:48 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Lucas Ly Ba (lucasly-ba)
<details>
<summary>Changes</summary>
When both integer operands are exactly representable in the float type, comparing their int-to-fp conversions is the same as comparing the integers directly -- int->fp never yields NaN and keeps their ordering:
fcmp pred (uitofp X), (uitofp Y) --> icmp upred X, Y
fcmp pred (sitofp X), (sitofp Y) --> icmp spred X, Y
Fixes #<!-- -->206329
---
Full diff: https://github.com/llvm/llvm-project/pull/210521.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp (+66)
- (added) llvm/test/Transforms/InstCombine/fcmp-int-to-fp.ll (+129)
``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 42c2983034e22..dffb0ad611066 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -8942,6 +8942,69 @@ static Instruction *foldFCmpFAbsFSubIntToFP(FCmpInst &I, InstCombinerImpl &IC) {
return new ICmpInst(ResultPred, A, B);
}
+// fcmp pred (uitofp X), (uitofp Y) --> icmp upred X, Y
+// fcmp pred (sitofp X), (sitofp Y) --> icmp spred X, Y
+// Valid when both integers are exactly representable in the float: int->fp is
+// never NaN and keeps their ordering. canBeCastedExactlyIntToFP() uses range
+// info, so this also fires for range-limited operands (e.g. masked values).
+static Instruction *foldFCmpTwoIntToFP(FCmpInst &I, InstCombinerImpl &IC) {
+ Value *X, *Y;
+ bool IsSigned;
+ if (match(I.getOperand(0), m_UIToFP(m_Value(X))) &&
+ match(I.getOperand(1), m_UIToFP(m_Value(Y))))
+ IsSigned = false;
+ else if (match(I.getOperand(0), m_SIToFP(m_Value(X))) &&
+ match(I.getOperand(1), m_SIToFP(m_Value(Y))))
+ IsSigned = true;
+ else
+ return nullptr;
+
+ // Only handle a matching integer type on both sides.
+ if (X->getType() != Y->getType())
+ return nullptr;
+
+ Type *FPTy = I.getOperand(0)->getType();
+ if (!IC.canBeCastedExactlyIntToFP(X, FPTy, IsSigned, &I) ||
+ !IC.canBeCastedExactlyIntToFP(Y, FPTy, IsSigned, &I))
+ return nullptr;
+
+ // Neither operand can be NaN, so ordered and unordered predicates match.
+ ICmpInst::Predicate Pred;
+ switch (I.getPredicate()) {
+ case FCmpInst::FCMP_OEQ:
+ case FCmpInst::FCMP_UEQ:
+ Pred = ICmpInst::ICMP_EQ;
+ break;
+ case FCmpInst::FCMP_ONE:
+ case FCmpInst::FCMP_UNE:
+ Pred = ICmpInst::ICMP_NE;
+ break;
+ case FCmpInst::FCMP_OGT:
+ case FCmpInst::FCMP_UGT:
+ Pred = IsSigned ? ICmpInst::ICMP_SGT : ICmpInst::ICMP_UGT;
+ break;
+ case FCmpInst::FCMP_OGE:
+ case FCmpInst::FCMP_UGE:
+ Pred = IsSigned ? ICmpInst::ICMP_SGE : ICmpInst::ICMP_UGE;
+ break;
+ case FCmpInst::FCMP_OLT:
+ case FCmpInst::FCMP_ULT:
+ Pred = IsSigned ? ICmpInst::ICMP_SLT : ICmpInst::ICMP_ULT;
+ break;
+ case FCmpInst::FCMP_OLE:
+ case FCmpInst::FCMP_ULE:
+ Pred = IsSigned ? ICmpInst::ICMP_SLE : ICmpInst::ICMP_ULE;
+ break;
+ case FCmpInst::FCMP_ORD:
+ return IC.replaceInstUsesWith(I, ConstantInt::getTrue(I.getType()));
+ case FCmpInst::FCMP_UNO:
+ return IC.replaceInstUsesWith(I, ConstantInt::getFalse(I.getType()));
+ default:
+ return nullptr;
+ }
+ return new ICmpInst(Pred, X, Y);
+}
+
static Instruction *foldFCmpWithFloorAndCeil(FCmpInst &I,
InstCombinerImpl &IC) {
Value *LHS = I.getOperand(0), *RHS = I.getOperand(1);
@@ -9259,6 +9322,9 @@ Instruction *InstCombinerImpl::visitFCmpInst(FCmpInst &I) {
if (Instruction *R = foldFCmpFAbsFSubIntToFP(I, *this))
return R;
+ if (Instruction *R = foldFCmpTwoIntToFP(I, *this))
+ return R;
+
if (Instruction *R = foldSqrtWithFcmpZero(I, *this))
return R;
diff --git a/llvm/test/Transforms/InstCombine/fcmp-int-to-fp.ll b/llvm/test/Transforms/InstCombine/fcmp-int-to-fp.ll
new file mode 100644
index 0000000000000..efb0daea4ad90
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/fcmp-int-to-fp.ll
@@ -0,0 +1,129 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+; A comparison of two integers converted to floating-point can be performed as
+; an integer comparison when both operands are exactly representable in the
+; float type. Range information lets this fire even when the whole integer type
+; is not exactly representable.
+
+define i1 @uitofp_olt(i32 %a, i32 %b) {
+; CHECK-LABEL: @uitofp_olt(
+; CHECK-NEXT: [[MA:%.*]] = and i32 [[A:%.*]], 255
+; CHECK-NEXT: [[MB:%.*]] = and i32 [[B:%.*]], 255
+; CHECK-NEXT: [[C:%.*]] = icmp samesign ult i32 [[MA]], [[MB]]
+; CHECK-NEXT: ret i1 [[C]]
+;
+ %ma = and i32 %a, 255
+ %mb = and i32 %b, 255
+ %fa = uitofp i32 %ma to float
+ %fb = uitofp i32 %mb to float
+ %c = fcmp olt float %fa, %fb
+ ret i1 %c
+}
+
+define i1 @uitofp_uge(i32 %a, i32 %b) {
+; CHECK-LABEL: @uitofp_uge(
+; CHECK-NEXT: [[MA:%.*]] = and i32 [[A:%.*]], 65535
+; CHECK-NEXT: [[MB:%.*]] = and i32 [[B:%.*]], 65535
+; CHECK-NEXT: [[C:%.*]] = icmp samesign uge i32 [[MA]], [[MB]]
+; CHECK-NEXT: ret i1 [[C]]
+;
+ %ma = and i32 %a, 65535
+ %mb = and i32 %b, 65535
+ %fa = uitofp i32 %ma to float
+ %fb = uitofp i32 %mb to float
+ %c = fcmp uge float %fa, %fb
+ ret i1 %c
+}
+
+define i1 @sitofp_ole(i32 %a, i32 %b) {
+; CHECK-LABEL: @sitofp_ole(
+; CHECK-NEXT: [[MA:%.*]] = and i32 [[A:%.*]], 255
+; CHECK-NEXT: [[MB:%.*]] = and i32 [[B:%.*]], 255
+; CHECK-NEXT: [[C:%.*]] = icmp samesign ule i32 [[MA]], [[MB]]
+; CHECK-NEXT: ret i1 [[C]]
+;
+ %ma = and i32 %a, 255
+ %mb = and i32 %b, 255
+ %fa = sitofp i32 %ma to float
+ %fb = sitofp i32 %mb to float
+ %c = fcmp ole float %fa, %fb
+ ret i1 %c
+}
+
+define i1 @sitofp_one(i32 %a, i32 %b) {
+; CHECK-LABEL: @sitofp_one(
+; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 255
+; CHECK-NEXT: [[C:%.*]] = icmp ne i32 [[TMP2]], 0
+; CHECK-NEXT: ret i1 [[C]]
+;
+ %ma = and i32 %a, 255
+ %mb = and i32 %b, 255
+ %fa = sitofp i32 %ma to float
+ %fb = sitofp i32 %mb to float
+ %c = fcmp one float %fa, %fb
+ ret i1 %c
+}
+
+; i32 always fits exactly in a double, so no range limit is needed.
+define i1 @sitofp_double_ogt(i32 %a, i32 %b) {
+; CHECK-LABEL: @sitofp_double_ogt(
+; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: ret i1 [[C]]
+;
+ %fa = sitofp i32 %a to double
+ %fb = sitofp i32 %b to double
+ %c = fcmp ogt double %fa, %fb
+ ret i1 %c
+}
+
+; int->fp is never NaN, so ord is always true.
+define i1 @uitofp_ord(i8 %a, i8 %b) {
+; CHECK-LABEL: @uitofp_ord(
+; CHECK-NEXT: ret i1 true
+;
+ %fa = uitofp i8 %a to float
+ %fb = uitofp i8 %b to float
+ %c = fcmp ord float %fa, %fb
+ ret i1 %c
+}
+
+define <2 x i1> @uitofp_vec_olt(<2 x i8> %a, <2 x i8> %b) {
+; CHECK-LABEL: @uitofp_vec_olt(
+; CHECK-NEXT: [[C:%.*]] = icmp ult <2 x i8> [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: ret <2 x i1> [[C]]
+;
+ %fa = uitofp <2 x i8> %a to <2 x float>
+ %fb = uitofp <2 x i8> %b to <2 x float>
+ %c = fcmp olt <2 x float> %fa, %fb
+ ret <2 x i1> %c
+}
+
+; Negative: full i32 is not exactly representable in a float.
+define i1 @negative_full_i32(i32 %a, i32 %b) {
+; CHECK-LABEL: @negative_full_i32(
+; CHECK-NEXT: [[FA:%.*]] = uitofp i32 [[A:%.*]] to float
+; CHECK-NEXT: [[FB:%.*]] = uitofp i32 [[B:%.*]] to float
+; CHECK-NEXT: [[C:%.*]] = fcmp olt float [[FA]], [[FB]]
+; CHECK-NEXT: ret i1 [[C]]
+;
+ %fa = uitofp i32 %a to float
+ %fb = uitofp i32 %b to float
+ %c = fcmp olt float %fa, %fb
+ ret i1 %c
+}
+
+; Negative: the two casts have different signedness.
+define i1 @negative_mixed_signedness(i8 %a, i8 %b) {
+; CHECK-LABEL: @negative_mixed_signedness(
+; CHECK-NEXT: [[FA:%.*]] = uitofp i8 [[A:%.*]] to float
+; CHECK-NEXT: [[FB:%.*]] = sitofp i8 [[B:%.*]] to float
+; CHECK-NEXT: [[C:%.*]] = fcmp olt float [[FA]], [[FB]]
+; CHECK-NEXT: ret i1 [[C]]
+;
+ %fa = uitofp i8 %a to float
+ %fb = sitofp i8 %b to float
+ %c = fcmp olt float %fa, %fb
+ ret i1 %c
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/210521
More information about the llvm-commits
mailing list