[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