[llvm] [InstCombine] Fold icmp eq/ne (add (select (icmp X, C), TrueC, FalseC), X), 0 (PR #196027)
Muhammed Shiyas N via llvm-commits
llvm-commits at lists.llvm.org
Sun May 10 20:13:01 PDT 2026
https://github.com/Shiyas-N updated https://github.com/llvm/llvm-project/pull/196027
>From d6dafbd18733833f5918bdf03ac7426198c96069 Mon Sep 17 00:00:00 2001
From: Shiyas-N <muhammedshiyasn811 at gmail.com>
Date: Mon, 4 May 2026 17:42:34 +0000
Subject: [PATCH 1/2] [InstCombine] Add baseline test for icmp eq/ne of
add(select, X) with zero
Add test cases covering patterns of the form:
icmp eq/ne (add (select (icmp X, C), TrueC, FalseC), X), 0
and the commuted variant.
These tests capture the current behavior prior to introducing the
fold for the missed optimization described in #167079.
---
llvm/test/Transforms/InstCombine/icmp-add.ll | 347 +++++++++++++++++++
1 file changed, 347 insertions(+)
diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll
index 1b66a50c26e59..1dd172af15898 100644
--- a/llvm/test/Transforms/InstCombine/icmp-add.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-add.ll
@@ -3602,3 +3602,350 @@ entry:
%result = select i1 %eq, i1 %v1_lt_v3, i1 %less_than
ret i1 %result
}
+
+; PR 167079
+
+define i1 @icmp_add_select_slt_eq(i32 %x) {
+; CHECK-LABEL: @icmp_add_select_slt_eq(
+; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 15
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -10, i32 -20
+; CHECK-NEXT: [[ADD:%.*]] = sub i32 0, [[X]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SEL]], [[ADD]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %cond = icmp slt i32 %x, 15
+ %sel = select i1 %cond, i32 -10, i32 -20
+ %add = add i32 %sel, %x
+ %cmp = icmp eq i32 %add, 0
+ ret i1 %cmp
+}
+
+; ne outer predicate produces and instead of or
+define i1 @icmp_add_select_slt_ne(i32 %x) {
+; CHECK-LABEL: @icmp_add_select_slt_ne(
+; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 15
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -10, i32 -20
+; CHECK-NEXT: [[ADD:%.*]] = sub i32 0, [[X]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[SEL]], [[ADD]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %cond = icmp slt i32 %x, 15
+ %sel = select i1 %cond, i32 -10, i32 -20
+ %add = add i32 %sel, %x
+ %cmp = icmp ne i32 %add, 0
+ ret i1 %cmp
+}
+
+; sgt inner predicate
+define i1 @icmp_add_select_sgt_eq(i32 %x) {
+; CHECK-LABEL: @icmp_add_select_sgt_eq(
+; CHECK-NEXT: [[COND:%.*]] = icmp sgt i32 [[X:%.*]], 15
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -20, i32 -10
+; CHECK-NEXT: [[ADD:%.*]] = sub i32 0, [[X]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SEL]], [[ADD]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %cond = icmp sgt i32 %x, 15
+ %sel = select i1 %cond, i32 -20, i32 -10
+ %add = add i32 %sel, %x
+ %cmp = icmp eq i32 %add, 0
+ ret i1 %cmp
+}
+
+; ult inner predicate
+define i1 @icmp_add_select_ult_eq(i32 %x) {
+; CHECK-LABEL: @icmp_add_select_ult_eq(
+; CHECK-NEXT: [[COND:%.*]] = icmp ult i32 [[X:%.*]], 15
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -10, i32 -20
+; CHECK-NEXT: [[ADD:%.*]] = sub i32 0, [[X]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SEL]], [[ADD]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %cond = icmp ult i32 %x, 15
+ %sel = select i1 %cond, i32 -10, i32 -20
+ %add = add i32 %sel, %x
+ %cmp = icmp eq i32 %add, 0
+ ret i1 %cmp
+}
+
+; ugt inner predicate
+define i1 @icmp_add_select_ugt_eq(i32 %x) {
+; CHECK-LABEL: @icmp_add_select_ugt_eq(
+; CHECK-NEXT: [[COND:%.*]] = icmp ugt i32 [[X:%.*]], 15
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -20, i32 -10
+; CHECK-NEXT: [[ADD:%.*]] = sub i32 0, [[X]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SEL]], [[ADD]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %cond = icmp ugt i32 %x, 15
+ %sel = select i1 %cond, i32 -20, i32 -10
+ %add = add i32 %sel, %x
+ %cmp = icmp eq i32 %add, 0
+ ret i1 %cmp
+}
+
+; Commuted add operands (tests m_c_Add)
+
+define i1 @icmp_add_select_slt_eq_commute(i32 %x) {
+; CHECK-LABEL: @icmp_add_select_slt_eq_commute(
+; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 15
+; CHECK-NEXT: [[SEL_NEG:%.*]] = select i1 [[COND]], i32 10, i32 20
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[X]], [[SEL_NEG]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %cond = icmp slt i32 %x, 15
+ %sel = select i1 %cond, i32 -10, i32 -20
+ %add = add i32 %x, %sel
+ %cmp = icmp eq i32 %add, 0
+ ret i1 %cmp
+}
+
+; i8 Type coverage
+
+define i1 @icmp_add_select_slt_eq_i8(i8 %x) {
+; CHECK-LABEL: @icmp_add_select_slt_eq_i8(
+; CHECK-NEXT: [[COND:%.*]] = icmp slt i8 [[X:%.*]], 15
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i8 -10, i8 -20
+; CHECK-NEXT: [[ADD:%.*]] = sub i8 0, [[X]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[SEL]], [[ADD]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %cond = icmp slt i8 %x, 15
+ %sel = select i1 %cond, i8 -10, i8 -20
+ %add = add i8 %sel, %x
+ %cmp = icmp eq i8 %add, 0
+ ret i1 %cmp
+}
+
+; Vector — splat constants fold identically to scalars
+define <2 x i1> @icmp_add_select_slt_eq_vec(<2 x i32> %x) {
+; CHECK-LABEL: @icmp_add_select_slt_eq_vec(
+; CHECK-NEXT: [[COND:%.*]] = icmp slt <2 x i32> [[X:%.*]], splat (i32 15)
+; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[COND]], <2 x i32> splat (i32 -10), <2 x i32> splat (i32 -20)
+; CHECK-NEXT: [[ADD:%.*]] = sub <2 x i32> zeroinitializer, [[X]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i32> [[SEL]], [[ADD]]
+; CHECK-NEXT: ret <2 x i1> [[CMP]]
+;
+ %cond = icmp slt <2 x i32> %x, splat (i32 15)
+ %sel = select <2 x i1> %cond, <2 x i32> splat (i32 -10), <2 x i32> splat (i32 -20)
+ %add = add <2 x i32> %sel, %x
+ %cmp = icmp eq <2 x i32> %add, zeroinitializer
+ ret <2 x i1> %cmp
+}
+
+; Edge case — INT_MIN in true-arm; negation wraps but fold still correct
+define i1 @icmp_add_select_slt_eq_intmin_root(i32 %x) {
+; CHECK-LABEL: @icmp_add_select_slt_eq_intmin_root(
+; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 1
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -2147483648, i32 -20
+; CHECK-NEXT: [[ADD:%.*]] = sub i32 0, [[X]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SEL]], [[ADD]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %cond = icmp slt i32 %x, 1
+ %sel = select i1 %cond, i32 -2147483648, i32 -20
+ %add = add i32 %sel, %x
+ %cmp = icmp eq i32 %add, 0
+ ret i1 %cmp
+}
+
+; Flags — nsw on the add does not inhibit the fold
+; (nuw would make the eq-zero fold to false by a separate rule, not tested here)
+
+define i1 @icmp_add_select_slt_eq_nsw(i32 %x) {
+; CHECK-LABEL: @icmp_add_select_slt_eq_nsw(
+; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 15
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -10, i32 -20
+; CHECK-NEXT: [[ADD:%.*]] = sub i32 0, [[X]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SEL]], [[ADD]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %cond = icmp slt i32 %x, 15
+ %sel = select i1 %cond, i32 -10, i32 -20
+ %add = add nsw i32 %sel, %x
+ %cmp = icmp eq i32 %add, 0
+ ret i1 %cmp
+}
+
+; Sub canonicalization — sub(x, sel) is canonicalized to add before the fold
+
+define i1 @icmp_sub_select_slt_eq(i32 %x) {
+; CHECK-LABEL: @icmp_sub_select_slt_eq(
+; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 1
+; CHECK-NEXT: [[SEL_NEG:%.*]] = select i1 [[COND]], i32 1, i32 -1
+; CHECK-NEXT: [[SUB:%.*]] = sub i32 0, [[X]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SEL_NEG]], [[SUB]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %cond = icmp slt i32 %x, 1
+ %sel = select i1 %cond, i32 -1, i32 1
+ %sub = sub i32 %x, %sel
+ %cmp = icmp eq i32 %sub, 0
+ ret i1 %cmp
+}
+
+; sub(sel, x) is not commutative with sub(x, sel) — no fold
+define i1 @icmp_sub_select_slt_eq_wrong_order_no_fold(i32 %x) {
+; CHECK-LABEL: @icmp_sub_select_slt_eq_wrong_order_no_fold(
+; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 1
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -1, i32 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SEL]], [[X]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %cond = icmp slt i32 %x, 1
+ %sel = select i1 %cond, i32 -1, i32 1
+ %sub = sub i32 %sel, %x
+ %cmp = icmp eq i32 %sub, 0
+ ret i1 %cmp
+}
+
+; Negative tests
+
+; outer icmp predicate is not eq or ne
+define i1 @icmp_add_select_neg_outer_not_equality(i32 %x) {
+; CHECK-LABEL: @icmp_add_select_neg_outer_not_equality(
+; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 15
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -10, i32 -20
+; CHECK-NEXT: [[ADD:%.*]] = add i32 [[SEL]], [[X]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[ADD]], 0
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %cond = icmp slt i32 %x, 15
+ %sel = select i1 %cond, i32 -10, i32 -20
+ %add = add i32 %sel, %x
+ %cmp = icmp slt i32 %add, 0
+ ret i1 %cmp
+}
+
+; rhs of outer icmp is not zero
+define i1 @icmp_add_select_neg_rhs_nonzero(i32 %x) {
+; CHECK-LABEL: @icmp_add_select_neg_rhs_nonzero(
+; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 15
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -10, i32 -20
+; CHECK-NEXT: [[ADD:%.*]] = add i32 [[SEL]], [[X]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[ADD]], 1
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %cond = icmp slt i32 %x, 15
+ %sel = select i1 %cond, i32 -10, i32 -20
+ %add = add i32 %sel, %x
+ %cmp = icmp eq i32 %add, 1
+ ret i1 %cmp
+}
+
+; inner icmp predicate is eq — not a relational comparison, no partition
+define i1 @icmp_add_select_neg_inner_eq(i32 %x) {
+; CHECK-LABEL: @icmp_add_select_neg_inner_eq(
+; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[X:%.*]], 15
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -10, i32 -20
+; CHECK-NEXT: [[ADD:%.*]] = sub i32 0, [[X]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SEL]], [[ADD]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %cond = icmp eq i32 %x, 15
+ %sel = select i1 %cond, i32 -10, i32 -20
+ %add = add i32 %sel, %x
+ %cmp = icmp eq i32 %add, 0
+ ret i1 %cmp
+}
+
+; add and inner icmp use different SSA values — m_Deferred fails
+define i1 @icmp_add_select_neg_different_vars(i32 %x, i32 %y) {
+; CHECK-LABEL: @icmp_add_select_neg_different_vars(
+; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 15
+; CHECK-NEXT: [[SEL_NEG:%.*]] = select i1 [[COND]], i32 10, i32 20
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[Y:%.*]], [[SEL_NEG]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %cond = icmp slt i32 %x, 15
+ %sel = select i1 %cond, i32 -10, i32 -20
+ %add = add i32 %y, %sel
+ %cmp = icmp eq i32 %add, 0
+ ret i1 %cmp
+}
+
+; CondC equals TrueRoot — strict comparison fails to partition
+define i1 @icmp_add_select_neg_slt_cond_eq_trueroot(i32 %x) {
+; CHECK-LABEL: @icmp_add_select_neg_slt_cond_eq_trueroot(
+; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 10
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -10, i32 -20
+; CHECK-NEXT: [[ADD:%.*]] = sub i32 0, [[X]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SEL]], [[ADD]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %cond = icmp slt i32 %x, 10
+ %sel = select i1 %cond, i32 -10, i32 -20
+ %add = add i32 %sel, %x
+ %cmp = icmp eq i32 %add, 0
+ ret i1 %cmp
+}
+
+; CondC is above both roots — both roots satisfy the predicate, partition fails
+define i1 @icmp_add_select_neg_slt_cond_above_roots(i32 %x) {
+; CHECK-LABEL: @icmp_add_select_neg_slt_cond_above_roots(
+; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 21
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -10, i32 -20
+; CHECK-NEXT: [[ADD:%.*]] = sub i32 0, [[X]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SEL]], [[ADD]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %cond = icmp slt i32 %x, 21
+ %sel = select i1 %cond, i32 -10, i32 -20
+ %add = add i32 %sel, %x
+ %cmp = icmp eq i32 %add, 0
+ ret i1 %cmp
+}
+
+; FalseRoot is INT_MIN — its negation wraps, partition check fails
+define i1 @icmp_add_select_neg_slt_intmin_false_root(i32 %x) {
+; CHECK-LABEL: @icmp_add_select_neg_slt_intmin_false_root(
+; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 15
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -10, i32 -2147483648
+; CHECK-NEXT: [[ADD:%.*]] = sub i32 0, [[X]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SEL]], [[ADD]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %cond = icmp slt i32 %x, 15
+ %sel = select i1 %cond, i32 -10, i32 -2147483648
+ %add = add i32 %sel, %x
+ %cmp = icmp eq i32 %add, 0
+ ret i1 %cmp
+}
+
+; Multi-use tests
+
+; add result has an extra use
+define i1 @icmp_add_select_neg_add_multiuse(i32 %x, ptr %p) {
+; CHECK-LABEL: @icmp_add_select_neg_add_multiuse(
+; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 15
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -10, i32 -20
+; CHECK-NEXT: [[ADD:%.*]] = add i32 [[SEL]], [[X]]
+; CHECK-NEXT: store i32 [[ADD]], ptr [[P:%.*]], align 4
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[ADD]], 0
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %cond = icmp slt i32 %x, 15
+ %sel = select i1 %cond, i32 -10, i32 -20
+ %add = add i32 %sel, %x
+ store i32 %add, ptr %p
+ %cmp = icmp eq i32 %add, 0
+ ret i1 %cmp
+}
+
+; select result has an extra use
+define i32 @icmp_add_select_neg_select_multiuse(i32 %x) {
+; CHECK-LABEL: @icmp_add_select_neg_select_multiuse(
+; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 15
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -10, i32 -20
+; CHECK-NEXT: [[ADD:%.*]] = sub i32 0, [[X]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SEL]], [[ADD]]
+; CHECK-NEXT: [[RET:%.*]] = select i1 [[CMP]], i32 [[SEL]], i32 0
+; CHECK-NEXT: ret i32 [[RET]]
+;
+ %cond = icmp slt i32 %x, 15
+ %sel = select i1 %cond, i32 -10, i32 -20
+ %add = add i32 %sel, %x
+ %cmp = icmp eq i32 %add, 0
+ %ret = select i1 %cmp, i32 %sel, i32 0
+ ret i32 %ret
+}
>From e54b93375c6ada058d746bd6e4ab70d530148b85 Mon Sep 17 00:00:00 2001
From: Shiyas-N <muhammedshiyasn811 at gmail.com>
Date: Mon, 4 May 2026 18:23:08 +0000
Subject: [PATCH 2/2] [InstCombine] Fold icmp eq/ne of add(select, X) with zero
Match patterns of the form:
icmp eq/ne (add (select (icmp X, C), TrueC, FalseC), X), 0
and the commuted variant.
When the roots -TrueC and -FalseC are consistent with the select
condition, rewrite as:
eq : (X == -TrueC) || (X == -FalseC)
ne : (X != -TrueC) && (X != -FalseC)
This eliminates the add and select, replacing them with direct
comparisons on X.
The transform is applied only when:
InnerPred(-TrueC, C) == true
InnerPred(-FalseC, C) == false
ensuring each root corresponds to the select arm that would
produce it.
Handles signed and unsigned predicates (slt/sgt/ult/ugt).
---
.../InstCombine/InstCombineCompares.cpp | 82 +++++++++++++++++++
llvm/test/Transforms/InstCombine/icmp-add.ll | 76 ++++++++---------
2 files changed, 115 insertions(+), 43 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 0b1f683e10408..dfe465f944ca4 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1198,6 +1198,85 @@ Instruction *InstCombinerImpl::foldSignBitTest(ICmpInst &I) {
X, ConstantInt::getNullValue(XTy));
}
+/// Handles:
+/// icmp eq/ne (add select((icmp slt/sgt/ult/ugt X, CondC), TrueC, FalseC),
+/// X), 0
+/// icmp eq/ne (add X,
+/// select((icmp slt/sgt/ult/ugt X, CondC), TrueC,FalseC)), 0
+///
+/// Fold into (when roots are consistent with select arms):
+/// eq : (X == -TrueC) || (X == -FalseC)
+/// ne : (X != -TrueC) && (X != -FalseC)
+///
+/// Caller guarantees: RHS of icmp is zero.
+static Instruction *foldICmpAddSelectZero(ICmpInst &Cmp, InstCombinerImpl &IC) {
+ CmpPredicate Pred = Cmp.getPredicate();
+ if (!ICmpInst::isEquality(Pred))
+ return nullptr;
+
+ Value *X;
+ const APInt *CondC, *TrueC, *FalseC;
+ CmpPredicate InnerPred;
+
+ // Match: add (select (icmp X, CondC), TrueC, FalseC), X — or commuted form.
+ // The outer OneUse guards the add (we're replacing it).
+ // The inner OneUse on the select is a profitability guard: this fold adds
+ // 3 instructions and removes 2, so it isn't profitable if the select is
+ // reused elsewhere.
+ if (!match(Cmp.getOperand(0),
+ m_OneUse(m_c_Add(m_OneUse(m_Select(
+ m_ICmp(InnerPred, m_Value(X), m_APInt(CondC)),
+ m_APInt(TrueC), m_APInt(FalseC))),
+ m_Deferred(X)))))
+ return nullptr;
+
+ APInt TrueRoot = -(*TrueC);
+ APInt FalseRoot = -(*FalseC);
+
+ // The fold is valid only if each root matches the select arm that would
+ // produce it:
+ // InnerPred(TrueRoot, CondC) == true
+ // InnerPred(FalseRoot, CondC) == false
+ //
+ // (sle/sge/ule/uge are canonicalized to slt/sgt/ult/ugt before reaching
+ // here.)
+ switch (InnerPred) {
+ case ICmpInst::ICMP_SLT:
+ if (!TrueRoot.slt(*CondC) || FalseRoot.slt(*CondC))
+ return nullptr;
+ break;
+ case ICmpInst::ICMP_SGT:
+ if (!TrueRoot.sgt(*CondC) || FalseRoot.sgt(*CondC))
+ return nullptr;
+ break;
+ case ICmpInst::ICMP_ULT:
+ if (!TrueRoot.ult(*CondC) || FalseRoot.ult(*CondC))
+ return nullptr;
+ break;
+ case ICmpInst::ICMP_UGT:
+ if (!TrueRoot.ugt(*CondC) || FalseRoot.ugt(*CondC))
+ return nullptr;
+ break;
+ default:
+ return nullptr;
+ }
+
+ InstCombiner::BuilderTy &Builder = IC.Builder;
+ Type *Ty = X->getType();
+ Constant *TrueConst = ConstantInt::get(Ty, TrueRoot);
+ Constant *FalseConst = ConstantInt::get(Ty, FalseRoot);
+
+ // eq -> (X == TrueRoot) || (X == FalseRoot)
+ // ne -> (X != TrueRoot) && (X != FalseRoot)
+ bool IsEq = Pred == ICmpInst::ICMP_EQ;
+ Value *CmpA = Builder.CreateICmp(IsEq ? CmpInst::ICMP_EQ : CmpInst::ICMP_NE,
+ X, TrueConst);
+ Value *CmpB = Builder.CreateICmp(IsEq ? CmpInst::ICMP_EQ : CmpInst::ICMP_NE,
+ X, FalseConst);
+ return IsEq ? BinaryOperator::CreateOr(CmpA, CmpB)
+ : BinaryOperator::CreateAnd(CmpA, CmpB);
+}
+
// Handle icmp pred X, 0
Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
CmpInst::Predicate Pred = Cmp.getPredicate();
@@ -1215,6 +1294,9 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
}
}
+ if (Instruction *New = foldICmpAddSelectZero(Cmp, *this))
+ return New;
+
if (Instruction *New = foldIRemByPowerOfTwoToBitTest(Cmp))
return New;
diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll
index 1dd172af15898..c229a1c5856ac 100644
--- a/llvm/test/Transforms/InstCombine/icmp-add.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-add.ll
@@ -3607,10 +3607,9 @@ entry:
define i1 @icmp_add_select_slt_eq(i32 %x) {
; CHECK-LABEL: @icmp_add_select_slt_eq(
-; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 15
-; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -10, i32 -20
-; CHECK-NEXT: [[ADD:%.*]] = sub i32 0, [[X]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SEL]], [[ADD]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[X:%.*]], 10
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[X]], 20
+; CHECK-NEXT: [[CMP:%.*]] = or i1 [[TMP1]], [[TMP2]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%cond = icmp slt i32 %x, 15
@@ -3623,10 +3622,9 @@ define i1 @icmp_add_select_slt_eq(i32 %x) {
; ne outer predicate produces and instead of or
define i1 @icmp_add_select_slt_ne(i32 %x) {
; CHECK-LABEL: @icmp_add_select_slt_ne(
-; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 15
-; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -10, i32 -20
-; CHECK-NEXT: [[ADD:%.*]] = sub i32 0, [[X]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[SEL]], [[ADD]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i32 [[X:%.*]], 10
+; CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[X]], 20
+; CHECK-NEXT: [[CMP:%.*]] = and i1 [[TMP1]], [[TMP2]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%cond = icmp slt i32 %x, 15
@@ -3639,10 +3637,9 @@ define i1 @icmp_add_select_slt_ne(i32 %x) {
; sgt inner predicate
define i1 @icmp_add_select_sgt_eq(i32 %x) {
; CHECK-LABEL: @icmp_add_select_sgt_eq(
-; CHECK-NEXT: [[COND:%.*]] = icmp sgt i32 [[X:%.*]], 15
-; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -20, i32 -10
-; CHECK-NEXT: [[ADD:%.*]] = sub i32 0, [[X]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SEL]], [[ADD]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[X:%.*]], 20
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[X]], 10
+; CHECK-NEXT: [[CMP:%.*]] = or i1 [[TMP1]], [[TMP2]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%cond = icmp sgt i32 %x, 15
@@ -3655,10 +3652,9 @@ define i1 @icmp_add_select_sgt_eq(i32 %x) {
; ult inner predicate
define i1 @icmp_add_select_ult_eq(i32 %x) {
; CHECK-LABEL: @icmp_add_select_ult_eq(
-; CHECK-NEXT: [[COND:%.*]] = icmp ult i32 [[X:%.*]], 15
-; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -10, i32 -20
-; CHECK-NEXT: [[ADD:%.*]] = sub i32 0, [[X]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SEL]], [[ADD]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[X:%.*]], 10
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[X]], 20
+; CHECK-NEXT: [[CMP:%.*]] = or i1 [[TMP1]], [[TMP2]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%cond = icmp ult i32 %x, 15
@@ -3671,10 +3667,9 @@ define i1 @icmp_add_select_ult_eq(i32 %x) {
; ugt inner predicate
define i1 @icmp_add_select_ugt_eq(i32 %x) {
; CHECK-LABEL: @icmp_add_select_ugt_eq(
-; CHECK-NEXT: [[COND:%.*]] = icmp ugt i32 [[X:%.*]], 15
-; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -20, i32 -10
-; CHECK-NEXT: [[ADD:%.*]] = sub i32 0, [[X]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SEL]], [[ADD]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[X:%.*]], 20
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[X]], 10
+; CHECK-NEXT: [[CMP:%.*]] = or i1 [[TMP1]], [[TMP2]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%cond = icmp ugt i32 %x, 15
@@ -3688,9 +3683,9 @@ define i1 @icmp_add_select_ugt_eq(i32 %x) {
define i1 @icmp_add_select_slt_eq_commute(i32 %x) {
; CHECK-LABEL: @icmp_add_select_slt_eq_commute(
-; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 15
-; CHECK-NEXT: [[SEL_NEG:%.*]] = select i1 [[COND]], i32 10, i32 20
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[X]], [[SEL_NEG]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[X:%.*]], 10
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[X]], 20
+; CHECK-NEXT: [[CMP:%.*]] = or i1 [[TMP1]], [[TMP2]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%cond = icmp slt i32 %x, 15
@@ -3704,10 +3699,9 @@ define i1 @icmp_add_select_slt_eq_commute(i32 %x) {
define i1 @icmp_add_select_slt_eq_i8(i8 %x) {
; CHECK-LABEL: @icmp_add_select_slt_eq_i8(
-; CHECK-NEXT: [[COND:%.*]] = icmp slt i8 [[X:%.*]], 15
-; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i8 -10, i8 -20
-; CHECK-NEXT: [[ADD:%.*]] = sub i8 0, [[X]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[SEL]], [[ADD]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i8 [[X:%.*]], 10
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[X]], 20
+; CHECK-NEXT: [[CMP:%.*]] = or i1 [[TMP1]], [[TMP2]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%cond = icmp slt i8 %x, 15
@@ -3720,10 +3714,9 @@ define i1 @icmp_add_select_slt_eq_i8(i8 %x) {
; Vector — splat constants fold identically to scalars
define <2 x i1> @icmp_add_select_slt_eq_vec(<2 x i32> %x) {
; CHECK-LABEL: @icmp_add_select_slt_eq_vec(
-; CHECK-NEXT: [[COND:%.*]] = icmp slt <2 x i32> [[X:%.*]], splat (i32 15)
-; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[COND]], <2 x i32> splat (i32 -10), <2 x i32> splat (i32 -20)
-; CHECK-NEXT: [[ADD:%.*]] = sub <2 x i32> zeroinitializer, [[X]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i32> [[SEL]], [[ADD]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <2 x i32> [[X:%.*]], splat (i32 10)
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq <2 x i32> [[X]], splat (i32 20)
+; CHECK-NEXT: [[CMP:%.*]] = or <2 x i1> [[TMP1]], [[TMP2]]
; CHECK-NEXT: ret <2 x i1> [[CMP]]
;
%cond = icmp slt <2 x i32> %x, splat (i32 15)
@@ -3736,10 +3729,9 @@ define <2 x i1> @icmp_add_select_slt_eq_vec(<2 x i32> %x) {
; Edge case — INT_MIN in true-arm; negation wraps but fold still correct
define i1 @icmp_add_select_slt_eq_intmin_root(i32 %x) {
; CHECK-LABEL: @icmp_add_select_slt_eq_intmin_root(
-; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 1
-; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -2147483648, i32 -20
-; CHECK-NEXT: [[ADD:%.*]] = sub i32 0, [[X]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SEL]], [[ADD]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[X:%.*]], -2147483648
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[X]], 20
+; CHECK-NEXT: [[CMP:%.*]] = or i1 [[TMP1]], [[TMP2]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%cond = icmp slt i32 %x, 1
@@ -3754,10 +3746,9 @@ define i1 @icmp_add_select_slt_eq_intmin_root(i32 %x) {
define i1 @icmp_add_select_slt_eq_nsw(i32 %x) {
; CHECK-LABEL: @icmp_add_select_slt_eq_nsw(
-; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 15
-; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 -10, i32 -20
-; CHECK-NEXT: [[ADD:%.*]] = sub i32 0, [[X]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SEL]], [[ADD]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[X:%.*]], 10
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[X]], 20
+; CHECK-NEXT: [[CMP:%.*]] = or i1 [[TMP1]], [[TMP2]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%cond = icmp slt i32 %x, 15
@@ -3771,10 +3762,9 @@ define i1 @icmp_add_select_slt_eq_nsw(i32 %x) {
define i1 @icmp_sub_select_slt_eq(i32 %x) {
; CHECK-LABEL: @icmp_sub_select_slt_eq(
-; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[X:%.*]], 1
-; CHECK-NEXT: [[SEL_NEG:%.*]] = select i1 [[COND]], i32 1, i32 -1
-; CHECK-NEXT: [[SUB:%.*]] = sub i32 0, [[X]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SEL_NEG]], [[SUB]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[X:%.*]], -1
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[X]], 1
+; CHECK-NEXT: [[CMP:%.*]] = or i1 [[TMP1]], [[TMP2]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%cond = icmp slt i32 %x, 1
More information about the llvm-commits
mailing list