[llvm] [InstCombine] Generalise optimisation of redundant floating point comparisons with `ConstantFPRange` (PR #159315)
Rajveer Singh Bharadwaj via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 17 03:29:04 PDT 2025
https://github.com/Rajveer100 created https://github.com/llvm/llvm-project/pull/159315
Follow up of #158097
Similar to `simplifyAndOrOfICmpsWithConstants`, we can do so for floating point comparisons.
>From 66ced86de2dd69fb2392c5aecb49cdfaecb7b7ed Mon Sep 17 00:00:00 2001
From: Rajveer <rajveer.developer at icloud.com>
Date: Wed, 17 Sep 2025 15:54:27 +0530
Subject: [PATCH] [InstCombine] Generalise optimisation of redundant floating
point comparisons with `ConstantFPRange`
Follow up of #158097
Similar to `simplifyAndOrOfICmpsWithConstants`, we can do so for floating point
comparisons.
---
llvm/lib/Analysis/InstructionSimplify.cpp | 74 ++++++++++++-------
.../Transforms/InstCombine/redundant-fcmp.ll | 21 +++---
2 files changed, 55 insertions(+), 40 deletions(-)
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 100fa428cb842..ffe0b2dd6f6ff 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -35,6 +35,7 @@
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/Analysis/VectorUtils.h"
+#include "llvm/IR/ConstantFPRange.h"
#include "llvm/IR/ConstantRange.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Dominators.h"
@@ -1812,6 +1813,49 @@ static Value *simplifyOrOfICmps(ICmpInst *Op0, ICmpInst *Op1,
return nullptr;
}
+/// Test if a pair of compares with a shared operand and 2 constants has an
+/// empty set intersection, full set union, or if one compare is a superset of
+/// the other.
+static Value *simplifyAndOrOfFCmpsWithConstants(FCmpInst *Cmp0, FCmpInst *Cmp1,
+ bool IsAnd) {
+ // Look for this pattern: {and/or} (fcmp X, C0), (fcmp X, C1)).
+ if (Cmp0->getOperand(0) != Cmp1->getOperand(0))
+ return nullptr;
+
+ const APFloat *C0, *C1;
+ if (!match(Cmp0->getOperand(1), m_APFloat(C0)) ||
+ !match(Cmp1->getOperand(1), m_APFloat(C1)))
+ return nullptr;
+
+ auto Range0 = ConstantFPRange::makeExactFCmpRegion(Cmp0->getPredicate(), *C0);
+ auto Range1 = ConstantFPRange::makeExactFCmpRegion(Cmp1->getPredicate(), *C1);
+
+ if (!Range0 || !Range1)
+ return nullptr;
+
+ // For and-of-compares, check if the intersection is empty:
+ // (fcmp X, C0) && (fcmp X, C1) --> empty set --> false
+ if (IsAnd && (*Range0).intersectWith(*Range1).isEmptySet())
+ return getFalse(Cmp0->getType());
+
+ // For or-of-compares, check if the union is full:
+ // (fcmp X, C0) || (fcmp X, C1) --> full set --> true
+ if (!IsAnd && (*Range0).unionWith(*Range1).isFullSet())
+ return getTrue(Cmp0->getType());
+
+ // Is one range a superset of the other?
+ // If this is and-of-compares, take the smaller set:
+ // (fcmp sgt X, 4) && (fcmp sgt X, 42) --> fcmp sgt X, 42
+ // If this is or-of-compares, take the larger set:
+ // (fcmp sgt X, 4) || (fcmp sgt X, 42) --> fcmp sgt X, 4
+ if ((*Range0).contains(*Range1))
+ return IsAnd ? Cmp1 : Cmp0;
+ if ((*Range1).contains(*Range0))
+ return IsAnd ? Cmp0 : Cmp1;
+
+ return nullptr;
+}
+
static Value *simplifyAndOrOfFCmps(const SimplifyQuery &Q, FCmpInst *LHS,
FCmpInst *RHS, bool IsAnd) {
Value *LHS0 = LHS->getOperand(0), *LHS1 = LHS->getOperand(1);
@@ -1850,34 +1894,8 @@ static Value *simplifyAndOrOfFCmps(const SimplifyQuery &Q, FCmpInst *LHS,
: ConstantInt::getBool(LHS->getType(), !IsAnd);
}
- Value *V0;
- const APFloat *V0Op1, *V1Op1;
- // (fcmp olt V0, V0Op1) || (fcmp olt V0, V1Op1)
- // --> fcmp olt V0, max(V0Op1, V1Op1)
- // (fcmp ogt V0, V0Op1) || (fcmp ogt V0, V1Op1)
- // --> fcmp ogt V0, max(V0Op1, V1Op1)
- //
- // (fcmp olt V0, V0Op1) && (fcmp olt V0, V1Op1)
- // --> fcmp olt V0, min(V0Op1, V1Op1)
- // (fcmp ogt V0, V0Op1) && (fcmp ogt V0, V1Op1)
- // --> fcmp ogt V0, min(V0Op1, V1Op1)
- if (match(LHS, m_SpecificFCmp(FCmpInst::FCMP_OLT, m_Value(V0),
- m_APFloat(V0Op1))) &&
- match(RHS, m_SpecificFCmp(FCmpInst::FCMP_OLT, m_Specific(V0),
- m_APFloat(V1Op1)))) {
- if (*V0Op1 > *V1Op1)
- return IsAnd ? RHS : LHS;
- if (*V1Op1 > *V0Op1)
- return IsAnd ? LHS : RHS;
- } else if (match(LHS, m_SpecificFCmp(FCmpInst::FCMP_OGT, m_Value(V0),
- m_APFloat(V0Op1))) &&
- match(RHS, m_SpecificFCmp(FCmpInst::FCMP_OGT, m_Specific(V0),
- m_APFloat(V1Op1)))) {
- if (*V0Op1 < *V1Op1)
- return IsAnd ? RHS : LHS;
- if (*V1Op1 < *V0Op1)
- return IsAnd ? LHS : RHS;
- }
+ if (auto *V = simplifyAndOrOfFCmpsWithConstants(LHS, RHS, IsAnd))
+ return V;
return nullptr;
}
diff --git a/llvm/test/Transforms/InstCombine/redundant-fcmp.ll b/llvm/test/Transforms/InstCombine/redundant-fcmp.ll
index 0f5fe9fb9a1b2..5d1529ef0b214 100644
--- a/llvm/test/Transforms/InstCombine/redundant-fcmp.ll
+++ b/llvm/test/Transforms/InstCombine/redundant-fcmp.ll
@@ -45,8 +45,8 @@ define i1 @or_fcmp_redundant_or4(double %v0) {
ret i1 %v3
}
-define i1 @or_fcmp_redundant_or_neg1(double %v0) {
-; CHECK-LABEL: @or_fcmp_redundant_or_neg1(
+define i1 @or_fcmp_redundant_or_5(double %v0) {
+; CHECK-LABEL: @or_fcmp_redundant_or_5(
; CHECK-NEXT: [[V1:%.*]] = fcmp olt double [[V0:%.*]], 1.000000e-02
; CHECK-NEXT: [[V2:%.*]] = fcmp ogt double [[V0]], 1.990000e+00
; CHECK-NEXT: [[V3:%.*]] = or i1 [[V1]], [[V2]]
@@ -58,8 +58,8 @@ define i1 @or_fcmp_redundant_or_neg1(double %v0) {
ret i1 %v3
}
-define i1 @or_fcmp_redundant_or_neg2(double %v0) {
-; CHECK-LABEL: @or_fcmp_redundant_or_neg2(
+define i1 @or_fcmp_redundant_or_6(double %v0) {
+; CHECK-LABEL: @or_fcmp_redundant_or_6(
; CHECK-NEXT: [[V1:%.*]] = fcmp ogt double [[V0:%.*]], 1.000000e-02
; CHECK-NEXT: [[V2:%.*]] = fcmp olt double [[V0]], 1.990000e+00
; CHECK-NEXT: [[V3:%.*]] = or i1 [[V1]], [[V2]]
@@ -115,12 +115,9 @@ define i1 @or_fcmp_redundant_and4(double %v0) {
ret i1 %v3
}
-define i1 @or_fcmp_redundant_and_neg1(double %v0) {
-; CHECK-LABEL: @or_fcmp_redundant_and_neg1(
-; CHECK-NEXT: [[V1:%.*]] = fcmp olt double [[V0:%.*]], 1.000000e-02
-; CHECK-NEXT: [[V2:%.*]] = fcmp ogt double [[V0]], 1.990000e+00
-; CHECK-NEXT: [[V3:%.*]] = and i1 [[V1]], [[V2]]
-; CHECK-NEXT: ret i1 [[V3]]
+define i1 @or_fcmp_redundant_and_5(double %v0) {
+; CHECK-LABEL: @or_fcmp_redundant_and_5(
+; CHECK-NEXT: ret i1 false
;
%v1 = fcmp olt double %v0, 1.000000e-02
%v2 = fcmp ogt double %v0, 1.990000e+00
@@ -128,8 +125,8 @@ define i1 @or_fcmp_redundant_and_neg1(double %v0) {
ret i1 %v3
}
-define i1 @or_fcmp_redundant_and_neg2(double %v0) {
-; CHECK-LABEL: @or_fcmp_redundant_and_neg2(
+define i1 @or_fcmp_redundant_and_6(double %v0) {
+; CHECK-LABEL: @or_fcmp_redundant_and_6(
; CHECK-NEXT: [[V1:%.*]] = fcmp ogt double [[V0:%.*]], 1.000000e-02
; CHECK-NEXT: [[V2:%.*]] = fcmp olt double [[V0]], 1.990000e+00
; CHECK-NEXT: [[V3:%.*]] = and i1 [[V1]], [[V2]]
More information about the llvm-commits
mailing list