[llvm] [InstCombine] Fold fcmp eq over min/max-like select into compare on input (PR #188226)
Nathiyaa Sengodan via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 1 02:18:10 PDT 2026
https://github.com/Nathiyaa-Sengodan updated https://github.com/llvm/llvm-project/pull/188226
>From e70e9b8d760541d10b5c7a4595623d342f0b774d Mon Sep 17 00:00:00 2001
From: Nathiyaa Sengodan <s.nathiyaa at gmail.com>
Date: Tue, 24 Mar 2026 16:21:36 +0530
Subject: [PATCH 1/2] [InstCombine] Fold fcmp eq over min/max-like select into
compare on input
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Recognize fcmp oeq/ueq where the LHS is a min/max-like select
driven by fcmp ogt/oge, and fold it into a simpler comparison
directly on the input value.
For max-like patterns:
max(X, K) == C:
C > K -> X == C
C == K -> X <= K
For min-like patterns:
min(X, K) == C:
C < K -> X == C
C == K -> X >= K
---
.../InstCombine/InstCombineCompares.cpp | 76 +++++++
.../Transforms/InstCombine/fcmp-select.ll | 198 ++++++++++++++++++
2 files changed, 274 insertions(+)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 7b6d380acffe1..8fadbbc8a7a25 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -8984,6 +8984,80 @@ static Instruction *foldFCmpWithFloorAndCeil(FCmpInst &I,
return nullptr;
}
+/// Fold equality compares against OGT/OGE-driven min/max-like selects:
+/// fcmp oeq/ueq (select (fcmp ogt/oge X, K), X, K), C
+/// fcmp oeq/ueq (select (fcmp ogt/oge X, K), K, X), C
+///
+/// Where one select arm is the compare bound constant K and the other is X.
+/// This performs a local one-step simplification based on C vs K:
+/// max-like select:
+/// C > K -> X == C
+/// C == K -> X <= K
+/// min-like select:
+/// C < K -> X == C
+/// C == K -> X >= K
+///
+static Instruction *
+foldFCmpEqWithMinMaxLikeSelect(FCmpInst &I, Instruction *LHSI, Constant *RHSC) {
+ const FCmpInst::Predicate Pred = I.getPredicate();
+ if (Pred != FCmpInst::FCMP_OEQ && Pred != FCmpInst::FCMP_UEQ)
+ return nullptr;
+
+ auto *SI = dyn_cast<SelectInst>(LHSI);
+ const APFloat *C;
+ if (!SI || !match(RHSC, m_APFloat(C)))
+ return nullptr;
+
+ auto *InnerCmp = dyn_cast<FCmpInst>(SI->getCondition());
+ if (!InnerCmp)
+ return nullptr;
+
+ // Restrict to OGT/OGE-driven min/max-like selects.
+ const FCmpInst::Predicate InnerPred = InnerCmp->getPredicate();
+ if (InnerPred != FCmpInst::FCMP_OGT && InnerPred != FCmpInst::FCMP_OGE)
+ return nullptr;
+
+ auto *CmpBound = dyn_cast<ConstantFP>(InnerCmp->getOperand(1));
+ if (!CmpBound)
+ return nullptr;
+ Value *X = InnerCmp->getOperand(0);
+
+ Value *TV = SI->getTrueValue();
+ Value *FV = SI->getFalseValue();
+
+ // select(cmp X, K), K, X is min-like;
+ // select(cmp X, K), X, K is max-like.
+ const bool isMinPattern = (TV == CmpBound && FV == X);
+ const bool isMaxPattern = (TV == X && FV == CmpBound);
+
+ if (!(isMinPattern || isMaxPattern))
+ return nullptr;
+
+ APFloat::cmpResult CmpVsBound = C->compare(CmpBound->getValueAPF());
+ if (isMaxPattern) {
+ // max(X, K) == C:
+ // C > K -> X == C
+ // C == K -> X <= K
+ if (CmpVsBound == APFloat::cmpGreaterThan)
+ return new FCmpInst(FCmpInst::FCMP_OEQ, X, RHSC, "", &I);
+ if (CmpVsBound == APFloat::cmpEqual)
+ return new FCmpInst(FCmpInst::FCMP_ULE, X, RHSC, "", &I);
+ return nullptr;
+ }
+
+ bool Ordered = FCmpInst::isOrdered(Pred);
+ // min(X, K) == C:
+ // C < K -> X == C
+ // C == K -> X >= K
+ if (CmpVsBound == APFloat::cmpLessThan)
+ return new FCmpInst(Ordered ? FCmpInst::FCMP_OEQ : FCmpInst::FCMP_UEQ, X,
+ RHSC, "", &I);
+ if (CmpVsBound == APFloat::cmpEqual)
+ return new FCmpInst(Ordered ? FCmpInst::FCMP_OGE : FCmpInst::FCMP_UGE, X,
+ RHSC, "", &I);
+ return nullptr;
+}
+
/// Returns true if a select that implements a min/max is redundant and
/// select result can be replaced with its non-constant operand, e.g.,
/// select ( (si/ui-to-fp A) <= C ), C, (si/ui-to-fp A)
@@ -9186,6 +9260,8 @@ Instruction *InstCombinerImpl::visitFCmpInst(FCmpInst &I) {
return replaceOperand(I, 0, X);
if (Instruction *NV = FoldOpIntoSelect(I, cast<SelectInst>(LHSI)))
return NV;
+ if (Instruction *NV = foldFCmpEqWithMinMaxLikeSelect(I, LHSI, RHSC))
+ return NV;
break;
case Instruction::FSub:
if (LHSI->hasOneUse())
diff --git a/llvm/test/Transforms/InstCombine/fcmp-select.ll b/llvm/test/Transforms/InstCombine/fcmp-select.ll
index 2115cd7f232e2..af625526dd8f4 100644
--- a/llvm/test/Transforms/InstCombine/fcmp-select.ll
+++ b/llvm/test/Transforms/InstCombine/fcmp-select.ll
@@ -595,3 +595,201 @@ define float @test_select_fcmp_uitofp_min(i8 %x) {
%sel = select i1 %cmp, float 2.550000e+02, float %f
ret float %sel
}
+
+define i1 @fold_fcmp_min_clamp(double %x) {
+; CHECK-LABEL: @fold_fcmp_min_clamp(
+; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq double [[X:%.*]], 5.000000e+00
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %max_cmp = fcmp ogt double %x, 2.0
+ %clamp = select i1 %max_cmp, double %x, double 2.0
+ %cmp = fcmp oeq double %clamp, 5.0
+ ret i1 %cmp
+}
+
+define i1 @fold_fcmp_min_clamp_same_constant(double %x) {
+; CHECK-LABEL: @fold_fcmp_min_clamp_same_constant(
+; CHECK-NEXT: [[CMP:%.*]] = fcmp ule double [[X:%.*]], 2.000000e+00
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %max_cmp = fcmp ogt double %x, 2.0
+ %clamp = select i1 %max_cmp, double %x, double 2.0
+ %cmp = fcmp oeq double %clamp, 2.0
+ ret i1 %cmp
+}
+
+define i1 @fold_fcmp_max_clamp(double %x) {
+; CHECK-LABEL: @fold_fcmp_max_clamp(
+; CHECK-NEXT: [[CMP:%.*]] = fcmp oge double [[X:%.*]], 5.000000e+00
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %min_cmp = fcmp ogt double %x, 5.0
+ %clamp = select i1 %min_cmp, double 5.0, double %x
+ %cmp = fcmp oeq double %clamp, 5.0
+ ret i1 %cmp
+}
+
+define i1 @fold_fcmp_ueq_max_clamp(double %x) {
+; CHECK-LABEL: @fold_fcmp_ueq_max_clamp(
+; CHECK-NEXT: [[CMP:%.*]] = fcmp uge double [[X:%.*]], 5.000000e+00
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %min_cmp = fcmp ogt double %x, 5.0
+ %clamp = select i1 %min_cmp, double 5.0, double %x
+ %cmp = fcmp ueq double %clamp, 5.0
+ ret i1 %cmp
+}
+
+define i1 @fold_fcmp_max_clamp_small_const(double %x) {
+; CHECK-LABEL: @fold_fcmp_max_clamp_small_const(
+; CHECK-NEXT: [[CMP:%.*]] = fcmp ueq double [[X:%.*]], 3.000000e+00
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %min_cmp = fcmp ogt double %x, 5.0
+ %clamp = select i1 %min_cmp, double 5.0, double %x
+ %cmp = fcmp ueq double %clamp, 3.0
+ ret i1 %cmp
+}
+
+define i1 @fold_fcmp_min_clamp_oge_cond(double %x) {
+; CHECK-LABEL: @fold_fcmp_min_clamp_oge_cond(
+; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq double [[X:%.*]], 7.000000e+00
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %max_cmp = fcmp oge double %x, 2.0
+ %clamp = select i1 %max_cmp, double %x, double 2.0
+ %cmp = fcmp oeq double %clamp, 7.0
+ ret i1 %cmp
+}
+
+; Full clamp (OGT/OGE-driven nested min/max shapes)
+define i1 @fold_fcmp_full_clamp_oeq_hi(double %x) {
+; CHECK-LABEL: @fold_fcmp_full_clamp_oeq_hi(
+; CHECK-NEXT: [[CMP:%.*]] = fcmp oge double [[X:%.*]], 5.000000e+00
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %min_cmp = fcmp ogt double %x, 5.0
+ %min = select i1 %min_cmp, double 5.0, double %x
+ %max_cmp = fcmp ogt double %min, 2.0
+ %clamp = select i1 %max_cmp, double %min, double 2.0
+ %cmp = fcmp oeq double %clamp, 5.0
+ ret i1 %cmp
+}
+
+define i1 @fold_fcmp_full_clamp_oeq_lo(double %x) {
+; CHECK-LABEL: @fold_fcmp_full_clamp_oeq_lo(
+; CHECK-NEXT: [[MIN_CMP:%.*]] = fcmp ogt double [[X:%.*]], 5.000000e+00
+; CHECK-NEXT: [[MIN:%.*]] = select i1 [[MIN_CMP]], double 5.000000e+00, double [[X]]
+; CHECK-NEXT: [[CMP:%.*]] = fcmp ule double [[MIN]], 2.000000e+00
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %min_cmp = fcmp ogt double %x, 5.0
+ %min = select i1 %min_cmp, double 5.0, double %x
+ %max_cmp = fcmp ogt double %min, 2.0
+ %clamp = select i1 %max_cmp, double %min, double 2.0
+ %cmp = fcmp oeq double %clamp, 2.0
+ ret i1 %cmp
+}
+
+define i1 @fold_fcmp_full_clamp_mid_const(double %x) {
+; CHECK-LABEL: @fold_fcmp_full_clamp_mid_const(
+; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq double [[X:%.*]], 4.000000e+00
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %min_cmp = fcmp ogt double %x, 5.0
+ %min = select i1 %min_cmp, double 5.0, double %x
+ %max_cmp = fcmp ogt double %min, 2.0
+ %clamp = select i1 %max_cmp, double %min, double 2.0
+ %cmp = fcmp oeq double %clamp, 4.0
+ ret i1 %cmp
+}
+define i1 @fold_fcmp_clamp_minmax_alt_oeq_hi(double %x) {
+; CHECK-LABEL: @fold_fcmp_clamp_minmax_alt_oeq_hi(
+; CHECK-NEXT: [[MAX_CMP:%.*]] = fcmp ogt double [[X:%.*]], 2.000000e+00
+; CHECK-NEXT: [[MAX:%.*]] = select i1 [[MAX_CMP]], double [[X]], double 2.000000e+00
+; CHECK-NEXT: [[CMP:%.*]] = fcmp oge double [[MAX]], 5.000000e+00
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ ; clamp as min(max(X, 2), 5) instead of max(min(X, 5), 2) pattern
+ %max_cmp = fcmp ogt double %x, 2.0
+ %max = select i1 %max_cmp, double %x, double 2.0
+ %min_cmp = fcmp ogt double %max, 5.0
+ %clamp = select i1 %min_cmp, double 5.0, double %max
+ %cmp = fcmp oeq double %clamp, 5.0
+ ret i1 %cmp
+}
+
+define i1 @fold_two_clamp_hi(float %arg0, float %arg1) {
+; CHECK-LABEL: @fold_two_clamp_hi(
+; CHECK-DAG: [[CMP0:%.*]] = fcmp oge float [[ARG0:%.*]], 1.000000e+00
+; CHECK-DAG: [[CMP1:%.*]] = fcmp oge float [[ARG1:%.*]], 1.000000e+00
+; CHECK: [[RES:%.*]] = and i1
+; CHECK-NEXT: ret i1 [[RES]]
+;
+ %v0 = fcmp ogt float %arg1, 1.000000e+00
+ %v1 = select i1 %v0, float 1.000000e+00, float %arg1
+ %v2 = fcmp ogt float %v1, 0.000000e+00
+ %v3 = select i1 %v2, float %v1, float 0.000000e+00
+ %v4 = fcmp ogt float %arg0, 1.000000e+00
+ %v5 = select i1 %v4, float 1.000000e+00, float %arg0
+ %v6 = fcmp ogt float %v5, 0.000000e+00
+ %v7 = select i1 %v6, float %v5, float 0.000000e+00
+ %v8 = fcmp oeq float %v3, 1.000000e+00
+ %v9 = fcmp oeq float %v7, 1.000000e+00
+ %v10 = and i1 %v8, %v9
+ ret i1 %v10
+}
+
+; Negative test cases (including non-OGT/OGE predicate shapes)
+
+define i1 @fold_fcmp_non_minmax_shape(double %x, double %y) {
+; CHECK-LABEL: @fold_fcmp_non_minmax_shape(
+; CHECK-NEXT: [[CMP:%.*]] = fcmp ogt double [[X:%.*]], 2.000000e+00
+; CHECK-NEXT: [[CLAMP:%.*]] = select i1 [[CMP]], double [[X]], double [[Y:%.*]]
+; CHECK-NEXT: [[RES:%.*]] = fcmp oeq double [[CLAMP]], 5.000000e+00
+; CHECK-NEXT: ret i1 [[RES]]
+;
+ %cmp = fcmp ogt double %x, 2.0
+ %clamp = select i1 %cmp, double %x, double %y
+ %res = fcmp oeq double %clamp, 5.0
+ ret i1 %res
+}
+
+define i1 @fold_fcmp_min_clamp_non_og(double %x) {
+; CHECK-LABEL: @fold_fcmp_min_clamp_non_og(
+; CHECK-NEXT: [[MAX_CMP:%.*]] = fcmp olt double [[X:%.*]], 2.000000e+00
+; CHECK-NEXT: [[CLAMP:%.*]] = select i1 [[MAX_CMP]], double [[X]], double 2.000000e+00
+; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq double [[CLAMP]], 5.000000e+00
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %max_cmp = fcmp olt double %x, 2.0
+ %clamp = select i1 %max_cmp, double %x, double 2.0
+ %cmp = fcmp oeq double %clamp, 5.0
+ ret i1 %cmp
+}
+
+define i1 @fold_fcmp_max_clamp_bigger_const_no_fold(double %x) {
+; CHECK-LABEL: @fold_fcmp_max_clamp_bigger_const_no_fold(
+; CHECK-NEXT: [[MIN_CMP:%.*]] = fcmp ogt double [[X:%.*]], 5.000000e+00
+; CHECK-NEXT: [[CLAMP:%.*]] = select i1 [[MIN_CMP]], double 5.000000e+00, double [[X]]
+; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq double [[CLAMP]], 6.000000e+00
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %min_cmp = fcmp ogt double %x, 5.0
+ %clamp = select i1 %min_cmp, double 5.0, double %x
+ %cmp = fcmp oeq double %clamp, 6.0
+ ret i1 %cmp
+}
+
+define i1 @fold_fcmp_min_clamp_smaller_const_no_fold(double %x) {
+; CHECK-LABEL: @fold_fcmp_min_clamp_smaller_const_no_fold(
+; CHECK-NEXT: [[MAX_CMP:%.*]] = fcmp ogt double [[X:%.*]], 2.000000e+00
+; CHECK-NEXT: [[CLAMP:%.*]] = select i1 [[MAX_CMP]], double [[X]], double 2.000000e+00
+; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq double [[CLAMP]], 1.000000e+00
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %max_cmp = fcmp ogt double %x, 2.0
+ %clamp = select i1 %max_cmp, double %x, double 2.0
+ %cmp = fcmp oeq double %clamp, 1.0
+ ret i1 %cmp
+}
>From 14cba35d593761341399857e9f30982ad7d3dd06 Mon Sep 17 00:00:00 2001
From: Nathiyaa Sengodan <s.nathiyaa at gmail.com>
Date: Fri, 8 May 2026 00:10:01 +0530
Subject: [PATCH 2/2] Address review comments
- Add support for vector inputs
- Expand the inner compare predicate support to both olt and ogt
- Take care of FMF in the target fcmp instruction
- Use matchSelectPattern instead of matching fmin/fmax manually
- Update tests - add new tests along with vector tests
---
.../InstCombine/InstCombineCompares.cpp | 155 +++++++++-----
.../Transforms/InstCombine/fcmp-select.ll | 196 ++++++++++--------
2 files changed, 209 insertions(+), 142 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 8fadbbc8a7a25..69dd072a7a6eb 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -8984,78 +8984,129 @@ static Instruction *foldFCmpWithFloorAndCeil(FCmpInst &I,
return nullptr;
}
-/// Fold equality compares against OGT/OGE-driven min/max-like selects:
-/// fcmp oeq/ueq (select (fcmp ogt/oge X, K), X, K), C
-/// fcmp oeq/ueq (select (fcmp ogt/oge X, K), K, X), C
+/// Fold equality/inequality compares against floating min/max-like select
+/// patterns.
///
-/// Where one select arm is the compare bound constant K and the other is X.
-/// This performs a local one-step simplification based on C vs K:
-/// max-like select:
+/// This handles:
+/// fcmp oeq/ueq/one/une (select (fcmp Pred X, K), X, K), C
+/// fcmp oeq/ueq/one/une (select (fcmp Pred X, K), K, X), C
+///
+/// when the select is recognized as an fmaxnum/fminnum-like operation and one
+/// operand is the constant bound K. The result can be expressed as a
+/// direct comparison against X based on the relation between C and K:
+///
+/// fmaxnum-like:
/// C > K -> X == C
/// C == K -> X <= K
-/// min-like select:
+///
+/// fminnum-like:
/// C < K -> X == C
/// C == K -> X >= K
///
-static Instruction *
-foldFCmpEqWithMinMaxLikeSelect(FCmpInst &I, Instruction *LHSI, Constant *RHSC) {
- const FCmpInst::Predicate Pred = I.getPredicate();
- if (Pred != FCmpInst::FCMP_OEQ && Pred != FCmpInst::FCMP_UEQ)
+static Instruction *foldFCmpEqWithMinMaxLikeSelect(FCmpInst &I,
+ Instruction *LHSI,
+ Constant *RHSC,
+ InstCombinerImpl &CI) {
+ FCmpInst::Predicate Pred = I.getPredicate();
+
+ // Currently handles only equality predicates.
+ if (!I.isEquality())
return nullptr;
- auto *SI = dyn_cast<SelectInst>(LHSI);
+ bool Invert = false;
+ if (Pred != FCmpInst::FCMP_OEQ && Pred != FCmpInst::FCMP_UEQ) {
+ Invert = true;
+ Pred = I.getInversePredicate();
+ }
+
const APFloat *C;
- if (!SI || !match(RHSC, m_APFloat(C)))
+ if (!match(RHSC, m_APFloat(C)))
return nullptr;
- auto *InnerCmp = dyn_cast<FCmpInst>(SI->getCondition());
- if (!InnerCmp)
- return nullptr;
+ Value *X;
+ Constant *CmpBoundC;
+ bool IsMax;
- // Restrict to OGT/OGE-driven min/max-like selects.
- const FCmpInst::Predicate InnerPred = InnerCmp->getPredicate();
- if (InnerPred != FCmpInst::FCMP_OGT && InnerPred != FCmpInst::FCMP_OGE)
+ if (match(LHSI, m_OrdOrUnordFMax(m_Value(X), m_Constant(CmpBoundC))))
+ IsMax = true;
+ else if (match(LHSI, m_OrdOrUnordFMin(m_Value(X), m_Constant(CmpBoundC))))
+ IsMax = false;
+ else
return nullptr;
- auto *CmpBound = dyn_cast<ConstantFP>(InnerCmp->getOperand(1));
- if (!CmpBound)
- return nullptr;
- Value *X = InnerCmp->getOperand(0);
+ auto *SI = cast<SelectInst>(LHSI);
- Value *TV = SI->getTrueValue();
- Value *FV = SI->getFalseValue();
+ // Handling both scalar ConstantFP and splat vector constants.
+ const APFloat *CmpBoundAP;
+ if (!match(CmpBoundC, m_APFloat(CmpBoundAP)))
+ return nullptr;
- // select(cmp X, K), K, X is min-like;
- // select(cmp X, K), X, K is max-like.
- const bool isMinPattern = (TV == CmpBound && FV == X);
- const bool isMaxPattern = (TV == X && FV == CmpBound);
+ // Determine NaN behavior from the IR structure of the select.
+ //
+ // For an ordered inner predicate (NaN -> false -> selects FalseVal):
+ // NaN(X) returns the constant iff the constant is the FalseVal.
+ // For an unordered inner predicate (NaN -> true -> selects TrueVal):
+ // NaN(X) returns the constant iff the constant is the TrueVal.
+ //
+ // In both cases: NaNReturnsConst = (PredIsOrdered != ConstIsTV).
+ auto *FcmpCond = cast<FCmpInst>(SI->getCondition());
+ bool PredIsOrdered = CmpInst::isOrdered(FcmpCond->getPredicate());
+ bool ConstIsTV = SI->getTrueValue() == CmpBoundC;
+ bool NaNReturnsConst = PredIsOrdered != ConstIsTV;
- if (!(isMinPattern || isMaxPattern))
- return nullptr;
+ const SimplifyQuery Q = CI.getSimplifyQuery().getWithInstruction(&I);
+ bool Ordered = FCmpInst::isOrdered(Pred);
+ FCmpInst::Predicate NewPred;
+ APFloat::cmpResult CmpVsBound = C->compare(*CmpBoundAP);
- APFloat::cmpResult CmpVsBound = C->compare(CmpBound->getValueAPF());
- if (isMaxPattern) {
+ if (IsMax) {
// max(X, K) == C:
- // C > K -> X == C
- // C == K -> X <= K
+ //
+ // C > K -> X == C
+ // C == K -> X <= K
+
if (CmpVsBound == APFloat::cmpGreaterThan)
- return new FCmpInst(FCmpInst::FCMP_OEQ, X, RHSC, "", &I);
+ NewPred = Ordered ? FCmpInst::FCMP_OEQ : FCmpInst::FCMP_UEQ;
+ else if (CmpVsBound == APFloat::cmpEqual)
+ NewPred = Ordered ? FCmpInst::FCMP_OLE : FCmpInst::FCMP_ULE;
+ else
+ return nullptr;
+ } else {
+ // min(X, K) == C:
+ //
+ // C < K -> X == C
+ // C == K -> X >= K
+
+ if (CmpVsBound == APFloat::cmpLessThan)
+ NewPred = Ordered ? FCmpInst::FCMP_OEQ : FCmpInst::FCMP_UEQ;
+ else if (CmpVsBound == APFloat::cmpEqual)
+ NewPred = Ordered ? FCmpInst::FCMP_OGE : FCmpInst::FCMP_UGE;
+ else
+ return nullptr;
+ }
+
+ // When the select maps NaN to the bound constant or X is known non-NaN,
+ // choose the appropriate ordered/unordered predicate variant that preserves
+ // the original comparison semantics.
+ if (NaNReturnsConst || SI->getFastMathFlags().noNaNs() ||
+ FcmpCond->hasNoNaNs() || isKnownNeverNaN(X, Q)) {
if (CmpVsBound == APFloat::cmpEqual)
- return new FCmpInst(FCmpInst::FCMP_ULE, X, RHSC, "", &I);
- return nullptr;
+ NewPred = FCmpInst::getUnorderedPredicate(NewPred);
+ else
+ NewPred = FCmpInst::getOrderedPredicate(NewPred);
}
- bool Ordered = FCmpInst::isOrdered(Pred);
- // min(X, K) == C:
- // C < K -> X == C
- // C == K -> X >= K
- if (CmpVsBound == APFloat::cmpLessThan)
- return new FCmpInst(Ordered ? FCmpInst::FCMP_OEQ : FCmpInst::FCMP_UEQ, X,
- RHSC, "", &I);
- if (CmpVsBound == APFloat::cmpEqual)
- return new FCmpInst(Ordered ? FCmpInst::FCMP_OGE : FCmpInst::FCMP_UGE, X,
- RHSC, "", &I);
- return nullptr;
+ if (Invert)
+ NewPred = FCmpInst::getInversePredicate(NewPred);
+
+ auto *NewCmp = new FCmpInst(NewPred, X, RHSC);
+ FastMathFlags FMF = FcmpCond->getFastMathFlags();
+ if (C->isNaN())
+ FMF.setNoNaNs(false);
+ if (C->isInfinity())
+ FMF.setNoInfs(false);
+ NewCmp->setFastMathFlags(FMF);
+ return NewCmp;
}
/// Returns true if a select that implements a min/max is redundant and
@@ -9260,8 +9311,10 @@ Instruction *InstCombinerImpl::visitFCmpInst(FCmpInst &I) {
return replaceOperand(I, 0, X);
if (Instruction *NV = FoldOpIntoSelect(I, cast<SelectInst>(LHSI)))
return NV;
- if (Instruction *NV = foldFCmpEqWithMinMaxLikeSelect(I, LHSI, RHSC))
- return NV;
+ if (LHSI->hasOneUse())
+ if (Instruction *NV =
+ foldFCmpEqWithMinMaxLikeSelect(I, LHSI, RHSC, *this))
+ return NV;
break;
case Instruction::FSub:
if (LHSI->hasOneUse())
diff --git a/llvm/test/Transforms/InstCombine/fcmp-select.ll b/llvm/test/Transforms/InstCombine/fcmp-select.ll
index af625526dd8f4..7978c4b88303a 100644
--- a/llvm/test/Transforms/InstCombine/fcmp-select.ll
+++ b/llvm/test/Transforms/InstCombine/fcmp-select.ll
@@ -596,83 +596,72 @@ define float @test_select_fcmp_uitofp_min(i8 %x) {
ret float %sel
}
-define i1 @fold_fcmp_min_clamp(double %x) {
-; CHECK-LABEL: @fold_fcmp_min_clamp(
-; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq double [[X:%.*]], 5.000000e+00
+define i1 @fold_fcmp_min_clamp_big_const(double %x) {
+; CHECK-LABEL: @fold_fcmp_min_clamp_big_const(
+; CHECK-NEXT: [[CMP:%.*]] = fcmp ninf oeq double [[X:%.*]], 5.000000e+00
; CHECK-NEXT: ret i1 [[CMP]]
;
- %max_cmp = fcmp ogt double %x, 2.0
+ %max_cmp = fcmp ninf ogt double %x, 2.0
%clamp = select i1 %max_cmp, double %x, double 2.0
%cmp = fcmp oeq double %clamp, 5.0
ret i1 %cmp
}
-define i1 @fold_fcmp_min_clamp_same_constant(double %x) {
-; CHECK-LABEL: @fold_fcmp_min_clamp_same_constant(
-; CHECK-NEXT: [[CMP:%.*]] = fcmp ule double [[X:%.*]], 2.000000e+00
+define i1 @fold_fcmp_min_clamp_big_const_inf(double %x) {
+; CHECK-LABEL: @fold_fcmp_min_clamp_big_const_inf(
+; CHECK-NEXT: [[CMP:%.*]] = fcmp une double [[X:%.*]], +inf
; CHECK-NEXT: ret i1 [[CMP]]
;
- %max_cmp = fcmp ogt double %x, 2.0
- %clamp = select i1 %max_cmp, double %x, double 2.0
- %cmp = fcmp oeq double %clamp, 2.0
+ %max_cmp = fcmp ninf ogt double %x, 3.0
+ %clamp = select i1 %max_cmp, double %x, double 3.0
+ %cmp = fcmp one double %clamp, 0x7FF0000000000000
ret i1 %cmp
}
-define i1 @fold_fcmp_max_clamp(double %x) {
-; CHECK-LABEL: @fold_fcmp_max_clamp(
-; CHECK-NEXT: [[CMP:%.*]] = fcmp oge double [[X:%.*]], 5.000000e+00
+define i1 @fold_fcmp_min_clamp_same_constant(double %x) {
+; CHECK-LABEL: @fold_fcmp_min_clamp_same_constant(
+; CHECK-NEXT: [[CMP:%.*]] = fcmp ule double [[X:%.*]], 2.000000e+00
; CHECK-NEXT: ret i1 [[CMP]]
;
- %min_cmp = fcmp ogt double %x, 5.0
- %clamp = select i1 %min_cmp, double 5.0, double %x
- %cmp = fcmp oeq double %clamp, 5.0
+ %max_cmp = fcmp ult double %x, 2.0
+ %clamp = select nnan i1 %max_cmp, double 2.0, double %x
+ %cmp = fcmp ueq double %clamp, 2.0
ret i1 %cmp
}
-define i1 @fold_fcmp_ueq_max_clamp(double %x) {
-; CHECK-LABEL: @fold_fcmp_ueq_max_clamp(
+define i1 @fold_fcmp_max_clamp_same_constant(double %x) {
+; CHECK-LABEL: @fold_fcmp_max_clamp_same_constant(
; CHECK-NEXT: [[CMP:%.*]] = fcmp uge double [[X:%.*]], 5.000000e+00
; CHECK-NEXT: ret i1 [[CMP]]
;
%min_cmp = fcmp ogt double %x, 5.0
- %clamp = select i1 %min_cmp, double 5.0, double %x
- %cmp = fcmp ueq double %clamp, 5.0
+ %clamp = select nnan i1 %min_cmp, double 5.0, double %x
+ %cmp = fcmp nnan ninf oeq double %clamp, 5.0
ret i1 %cmp
}
define i1 @fold_fcmp_max_clamp_small_const(double %x) {
; CHECK-LABEL: @fold_fcmp_max_clamp_small_const(
-; CHECK-NEXT: [[CMP:%.*]] = fcmp ueq double [[X:%.*]], 3.000000e+00
+; CHECK-NEXT: [[CMP:%.*]] = fcmp ninf une double [[X:%.*]], 3.000000e+00
; CHECK-NEXT: ret i1 [[CMP]]
;
- %min_cmp = fcmp ogt double %x, 5.0
- %clamp = select i1 %min_cmp, double 5.0, double %x
- %cmp = fcmp ueq double %clamp, 3.0
+ %min_cmp = fcmp ninf olt double %x, 5.0
+ %clamp = select nnan i1 %min_cmp, double %x, double 5.0
+ %cmp = fcmp une double %clamp, 3.0
ret i1 %cmp
}
-define i1 @fold_fcmp_min_clamp_oge_cond(double %x) {
-; CHECK-LABEL: @fold_fcmp_min_clamp_oge_cond(
-; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq double [[X:%.*]], 7.000000e+00
-; CHECK-NEXT: ret i1 [[CMP]]
-;
- %max_cmp = fcmp oge double %x, 2.0
- %clamp = select i1 %max_cmp, double %x, double 2.0
- %cmp = fcmp oeq double %clamp, 7.0
- ret i1 %cmp
-}
-
-; Full clamp (OGT/OGE-driven nested min/max shapes)
+; Full clamp (nested min/max shapes)
define i1 @fold_fcmp_full_clamp_oeq_hi(double %x) {
; CHECK-LABEL: @fold_fcmp_full_clamp_oeq_hi(
-; CHECK-NEXT: [[CMP:%.*]] = fcmp oge double [[X:%.*]], 5.000000e+00
+; CHECK-NEXT: [[CMP:%.*]] = fcmp uge double [[X:%.*]], 5.000000e+00
; CHECK-NEXT: ret i1 [[CMP]]
;
%min_cmp = fcmp ogt double %x, 5.0
- %min = select i1 %min_cmp, double 5.0, double %x
- %max_cmp = fcmp ogt double %min, 2.0
- %clamp = select i1 %max_cmp, double %min, double 2.0
- %cmp = fcmp oeq double %clamp, 5.0
+ %min = select nnan i1 %min_cmp, double 5.0, double %x
+ %max_cmp = fcmp olt double %min, 2.0
+ %clamp = select nnan i1 %max_cmp, double 2.0, double %min
+ %cmp = fcmp nnan ueq double %clamp, 5.0
ret i1 %cmp
}
@@ -680,104 +669,116 @@ define i1 @fold_fcmp_full_clamp_oeq_lo(double %x) {
; CHECK-LABEL: @fold_fcmp_full_clamp_oeq_lo(
; CHECK-NEXT: [[MIN_CMP:%.*]] = fcmp ogt double [[X:%.*]], 5.000000e+00
; CHECK-NEXT: [[MIN:%.*]] = select i1 [[MIN_CMP]], double 5.000000e+00, double [[X]]
-; CHECK-NEXT: [[CMP:%.*]] = fcmp ule double [[MIN]], 2.000000e+00
+; CHECK-NEXT: [[CMP:%.*]] = fcmp ole double [[MIN]], 2.000000e+00
; CHECK-NEXT: ret i1 [[CMP]]
;
%min_cmp = fcmp ogt double %x, 5.0
%min = select i1 %min_cmp, double 5.0, double %x
- %max_cmp = fcmp ogt double %min, 2.0
- %clamp = select i1 %max_cmp, double %min, double 2.0
+ %max_cmp = fcmp olt double %min, 2.0
+ %clamp = select i1 %max_cmp, double 2.0, double %min
%cmp = fcmp oeq double %clamp, 2.0
ret i1 %cmp
}
define i1 @fold_fcmp_full_clamp_mid_const(double %x) {
; CHECK-LABEL: @fold_fcmp_full_clamp_mid_const(
-; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq double [[X:%.*]], 4.000000e+00
+; CHECK-NEXT: [[CMP:%.*]] = fcmp one double [[X:%.*]], 4.000000e+00
; CHECK-NEXT: ret i1 [[CMP]]
;
%min_cmp = fcmp ogt double %x, 5.0
%min = select i1 %min_cmp, double 5.0, double %x
- %max_cmp = fcmp ogt double %min, 2.0
- %clamp = select i1 %max_cmp, double %min, double 2.0
- %cmp = fcmp oeq double %clamp, 4.0
+ %max_cmp = fcmp olt double %min, 2.0
+ %clamp = select i1 %max_cmp, double 2.0, double %min
+ %cmp = fcmp one double %clamp, 4.0
ret i1 %cmp
}
+
+; clamp as min(max(X, 2), 5) instead of max(min(X, 5), 2) pattern
define i1 @fold_fcmp_clamp_minmax_alt_oeq_hi(double %x) {
; CHECK-LABEL: @fold_fcmp_clamp_minmax_alt_oeq_hi(
; CHECK-NEXT: [[MAX_CMP:%.*]] = fcmp ogt double [[X:%.*]], 2.000000e+00
-; CHECK-NEXT: [[MAX:%.*]] = select i1 [[MAX_CMP]], double [[X]], double 2.000000e+00
-; CHECK-NEXT: [[CMP:%.*]] = fcmp oge double [[MAX]], 5.000000e+00
+; CHECK-NEXT: [[MAX:%.*]] = select nnan i1 [[MAX_CMP]], double [[X]], double 2.000000e+00
+; CHECK-NEXT: [[CMP:%.*]] = fcmp uge double [[MAX]], 5.000000e+00
; CHECK-NEXT: ret i1 [[CMP]]
;
- ; clamp as min(max(X, 2), 5) instead of max(min(X, 5), 2) pattern
%max_cmp = fcmp ogt double %x, 2.0
- %max = select i1 %max_cmp, double %x, double 2.0
+ %max = select nnan i1 %max_cmp, double %x, double 2.0
%min_cmp = fcmp ogt double %max, 5.0
%clamp = select i1 %min_cmp, double 5.0, double %max
- %cmp = fcmp oeq double %clamp, 5.0
+ %cmp = fcmp nnan ueq double %clamp, 5.0
ret i1 %cmp
}
define i1 @fold_two_clamp_hi(float %arg0, float %arg1) {
; CHECK-LABEL: @fold_two_clamp_hi(
-; CHECK-DAG: [[CMP0:%.*]] = fcmp oge float [[ARG0:%.*]], 1.000000e+00
-; CHECK-DAG: [[CMP1:%.*]] = fcmp oge float [[ARG1:%.*]], 1.000000e+00
-; CHECK: [[RES:%.*]] = and i1
-; CHECK-NEXT: ret i1 [[RES]]
-;
- %v0 = fcmp ogt float %arg1, 1.000000e+00
- %v1 = select i1 %v0, float 1.000000e+00, float %arg1
- %v2 = fcmp ogt float %v1, 0.000000e+00
- %v3 = select i1 %v2, float %v1, float 0.000000e+00
- %v4 = fcmp ogt float %arg0, 1.000000e+00
- %v5 = select i1 %v4, float 1.000000e+00, float %arg0
- %v6 = fcmp ogt float %v5, 0.000000e+00
- %v7 = select i1 %v6, float %v5, float 0.000000e+00
- %v8 = fcmp oeq float %v3, 1.000000e+00
- %v9 = fcmp oeq float %v7, 1.000000e+00
+; CHECK-NEXT: [[V8:%.*]] = fcmp uge float [[ARG1:%.*]], 1.000000e+00
+; CHECK-NEXT: [[V9:%.*]] = fcmp ult float [[ARG0:%.*]], 2.000000e+00
+; CHECK-NEXT: [[V10:%.*]] = and i1 [[V8]], [[V9]]
+; CHECK-NEXT: ret i1 [[V10]]
+;
+ %v0 = fcmp olt float %arg1, 1.000000e+00
+ %v1 = select i1 %v0, float %arg1, float 1.000000e+00
+ %v2 = fcmp olt float %v1, 0.000000e+00
+ %v3 = select i1 %v2, float 0.000000e+00, float %v1
+ %v4 = fcmp ogt float %arg0, 2.000000e+00
+ %v5 = select i1 %v4, float 2.000000e+00, float %arg0
+ %v6 = fcmp olt float %v5, 3.000000e+00
+ %v7 = select i1 %v6, float %v5, float 3.000000e+00
+ %v8 = fcmp nnan ueq float %v3, 1.000000e+00
+ %v9 = fcmp nnan une float %v7, 2.000000e+00
%v10 = and i1 %v8, %v9
ret i1 %v10
}
-; Negative test cases (including non-OGT/OGE predicate shapes)
+define <2 x i1> @fold_two_clamp_hi_vec(<2 x float> %arg0, <2 x float> %arg1) {
+; CHECK-LABEL: @fold_two_clamp_hi_vec(
+; CHECK-NEXT: [[V8:%.*]] = fcmp ult <2 x float> [[ARG1:%.*]], splat (float 1.000000e+00)
+; CHECK-NEXT: [[V9:%.*]] = fcmp oeq <2 x float> [[ARG0:%.*]], splat (float 3.000000e+00)
+; CHECK-NEXT: [[V10:%.*]] = and <2 x i1> [[V8]], [[V9]]
+; CHECK-NEXT: ret <2 x i1> [[V10]]
+;
+ %v0 = fcmp ogt <2 x float> %arg1, splat (float 1.000000e+00)
+ %v1 = select <2 x i1> %v0, <2 x float> splat (float 1.000000e+00), <2 x float> %arg1
+ %v2 = fcmp ogt <2 x float> %v1, splat (float 0.000000e+00)
+ %v3 = select <2 x i1> %v2, <2 x float> %v1, <2 x float> splat (float 0.000000e+00)
+
+ %v4 = fcmp olt <2 x float> %arg0, splat (float 5.000000e+00)
+ %v5 = select <2 x i1> %v4, <2 x float> %arg0, <2 x float> splat (float 5.000000e+00)
+ %v6 = fcmp ogt <2 x float> %v5, splat (float 1.000000e+00)
+ %v7 = select <2 x i1> %v6, <2 x float> %v5, <2 x float> splat (float 1.000000e+00)
+
+ %v8 = fcmp one <2 x float> %v3, splat (float 1.000000e+00)
+ %v9 = fcmp ueq <2 x float> %v7, splat (float 3.000000e+00)
+
+ %v10 = and <2 x i1> %v8, %v9
+ ret <2 x i1> %v10
+}
+
+; Negative test cases
define i1 @fold_fcmp_non_minmax_shape(double %x, double %y) {
; CHECK-LABEL: @fold_fcmp_non_minmax_shape(
; CHECK-NEXT: [[CMP:%.*]] = fcmp ogt double [[X:%.*]], 2.000000e+00
; CHECK-NEXT: [[CLAMP:%.*]] = select i1 [[CMP]], double [[X]], double [[Y:%.*]]
-; CHECK-NEXT: [[RES:%.*]] = fcmp oeq double [[CLAMP]], 5.000000e+00
+; CHECK-NEXT: [[RES:%.*]] = fcmp one double [[CLAMP]], 5.000000e+00
; CHECK-NEXT: ret i1 [[RES]]
;
%cmp = fcmp ogt double %x, 2.0
%clamp = select i1 %cmp, double %x, double %y
- %res = fcmp oeq double %clamp, 5.0
+ %res = fcmp one double %clamp, 5.0
ret i1 %res
}
-define i1 @fold_fcmp_min_clamp_non_og(double %x) {
-; CHECK-LABEL: @fold_fcmp_min_clamp_non_og(
-; CHECK-NEXT: [[MAX_CMP:%.*]] = fcmp olt double [[X:%.*]], 2.000000e+00
-; CHECK-NEXT: [[CLAMP:%.*]] = select i1 [[MAX_CMP]], double [[X]], double 2.000000e+00
-; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq double [[CLAMP]], 5.000000e+00
-; CHECK-NEXT: ret i1 [[CMP]]
-;
- %max_cmp = fcmp olt double %x, 2.0
- %clamp = select i1 %max_cmp, double %x, double 2.0
- %cmp = fcmp oeq double %clamp, 5.0
- ret i1 %cmp
-}
-
define i1 @fold_fcmp_max_clamp_bigger_const_no_fold(double %x) {
; CHECK-LABEL: @fold_fcmp_max_clamp_bigger_const_no_fold(
-; CHECK-NEXT: [[MIN_CMP:%.*]] = fcmp ogt double [[X:%.*]], 5.000000e+00
-; CHECK-NEXT: [[CLAMP:%.*]] = select i1 [[MIN_CMP]], double 5.000000e+00, double [[X]]
-; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq double [[CLAMP]], 6.000000e+00
+; CHECK-NEXT: [[MIN_CMP_INV:%.*]] = fcmp ole double [[X:%.*]], 5.000000e+00
+; CHECK-NEXT: [[CLAMP:%.*]] = select i1 [[MIN_CMP_INV]], double [[X]], double 5.000000e+00
+; CHECK-NEXT: [[CMP:%.*]] = fcmp une double [[CLAMP]], 6.000000e+00
; CHECK-NEXT: ret i1 [[CMP]]
;
- %min_cmp = fcmp ogt double %x, 5.0
+ %min_cmp = fcmp ugt double %x, 5.0
%clamp = select i1 %min_cmp, double 5.0, double %x
- %cmp = fcmp oeq double %clamp, 6.0
+ %cmp = fcmp une double %clamp, 6.0
ret i1 %cmp
}
@@ -785,11 +786,24 @@ define i1 @fold_fcmp_min_clamp_smaller_const_no_fold(double %x) {
; CHECK-LABEL: @fold_fcmp_min_clamp_smaller_const_no_fold(
; CHECK-NEXT: [[MAX_CMP:%.*]] = fcmp ogt double [[X:%.*]], 2.000000e+00
; CHECK-NEXT: [[CLAMP:%.*]] = select i1 [[MAX_CMP]], double [[X]], double 2.000000e+00
-; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq double [[CLAMP]], 1.000000e+00
+; CHECK-NEXT: [[CMP:%.*]] = fcmp nnan oeq double [[CLAMP]], 1.000000e+00
; CHECK-NEXT: ret i1 [[CMP]]
;
%max_cmp = fcmp ogt double %x, 2.0
%clamp = select i1 %max_cmp, double %x, double 2.0
- %cmp = fcmp oeq double %clamp, 1.0
+ %cmp = fcmp nnan oeq double %clamp, 1.0
ret i1 %cmp
}
+
+define <2 x i1> @fold_fcmp_max_clamp_neg_test_vect(<2 x float> %x) {
+; CHECK-LABEL: @fold_fcmp_max_clamp_neg_test_vect(
+; CHECK-NEXT: [[MIN_CMP:%.*]] = fcmp olt <2 x float> [[X:%.*]], splat (float 5.000000e+00)
+; CHECK-NEXT: [[CLAMP:%.*]] = select <2 x i1> [[MIN_CMP]], <2 x float> [[X]], <2 x float> splat (float 5.000000e+00)
+; CHECK-NEXT: [[CMP:%.*]] = fcmp nnan ueq <2 x float> [[CLAMP]], <float 3.000000e+00, float 2.000000e+00>
+; CHECK-NEXT: ret <2 x i1> [[CMP]]
+;
+ %min_cmp = fcmp olt <2 x float> %x, <float 5.0, float 5.0>
+ %clamp = select <2 x i1> %min_cmp, <2 x float> %x, <2 x float> <float 5.0, float 5.0>
+ %cmp = fcmp nnan ueq <2 x float> %clamp, <float 3.0, float 2.0>
+ ret <2 x i1> %cmp
+}
More information about the llvm-commits
mailing list