[llvm] [InstCombine] Fold `(icmp eq/ne (or (select cond, 0/NZ, 0/NZ), X), 0)` (PR #88183)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Aug 3 09:30:33 PDT 2024
https://github.com/goldsteinn updated https://github.com/llvm/llvm-project/pull/88183
>From 6a2ba6873109d6d2ce97816b10b96f9e8870df7f Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Tue, 9 Apr 2024 14:36:08 -0500
Subject: [PATCH 1/3] [InstCombine] Add tests for folding `(icmp eq/ne (or
(select cond, 0/NZ, 0/NZ), X), 0)`; NFC
---
.../icmp-or-of-select-with-zero.ll | 297 ++++++++++++++++++
1 file changed, 297 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/icmp-or-of-select-with-zero.ll
diff --git a/llvm/test/Transforms/InstCombine/icmp-or-of-select-with-zero.ll b/llvm/test/Transforms/InstCombine/icmp-or-of-select-with-zero.ll
new file mode 100644
index 00000000000000..22968bab481c15
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/icmp-or-of-select-with-zero.ll
@@ -0,0 +1,297 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare void @use.i8(i8)
+declare void @use.i1(i1)
+define i1 @src_tv_eq(i1 %c0, i8 %x, i8 %yy) {
+; CHECK-LABEL: @src_tv_eq(
+; CHECK-NEXT: [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C0:%.*]], i8 0, i8 [[Y]]
+; CHECK-NEXT: [[SELX:%.*]] = or i8 [[SEL]], [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[SELX]], 0
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %y = add nuw i8 %yy, 1
+ %sel = select i1 %c0, i8 0, i8 %y
+ %selx = or i8 %sel, %x
+ %r = icmp eq i8 %selx, 0
+ ret i1 %r
+}
+
+define i1 @src_tv_eq_multiuse_or_fail(i1 %c0, i8 %x, i8 %yy) {
+; CHECK-LABEL: @src_tv_eq_multiuse_or_fail(
+; CHECK-NEXT: [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C0:%.*]], i8 0, i8 [[Y]]
+; CHECK-NEXT: [[SELX:%.*]] = or i8 [[SEL]], [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[SELX]], 0
+; CHECK-NEXT: call void @use.i8(i8 [[SELX]])
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %y = add nuw i8 %yy, 1
+ %sel = select i1 %c0, i8 0, i8 %y
+ %selx = or i8 %sel, %x
+ %r = icmp eq i8 %selx, 0
+ call void @use.i8(i8 %selx)
+ ret i1 %r
+}
+
+define i1 @src_tv_eq_fail_tv_nonzero(i1 %c0, i8 %x, i8 %yy) {
+; CHECK-LABEL: @src_tv_eq_fail_tv_nonzero(
+; CHECK-NEXT: [[Y:%.*]] = add nsw i8 [[YY:%.*]], 1
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C0:%.*]], i8 1, i8 [[Y]]
+; CHECK-NEXT: [[SELX:%.*]] = or i8 [[SEL]], [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[SELX]], 0
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %y = add nsw i8 %yy, 1
+ %sel = select i1 %c0, i8 1, i8 %y
+ %selx = or i8 %sel, %x
+ %r = icmp eq i8 %selx, 0
+ ret i1 %r
+}
+
+define i1 @src_fv_ne(i1 %c0, i8 %x, i8 %yy) {
+; CHECK-LABEL: @src_fv_ne(
+; CHECK-NEXT: [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C0:%.*]], i8 [[Y]], i8 0
+; CHECK-NEXT: [[SELX:%.*]] = or i8 [[SEL]], [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[SELX]], 0
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %y = add nuw i8 %yy, 1
+ %sel = select i1 %c0, i8 %y, i8 0
+ %selx = or i8 %sel, %x
+ %r = icmp ne i8 %selx, 0
+ ret i1 %r
+}
+
+define i1 @src_fv_ne_fail_maybe_zero(i1 %c0, i8 %x, i8 %yy) {
+; CHECK-LABEL: @src_fv_ne_fail_maybe_zero(
+; CHECK-NEXT: [[Y:%.*]] = add nsw i8 [[YY:%.*]], 1
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C0:%.*]], i8 [[Y]], i8 0
+; CHECK-NEXT: [[SELX:%.*]] = or i8 [[SEL]], [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[SELX]], 0
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %y = add nsw i8 %yy, 1
+ %sel = select i1 %c0, i8 %y, i8 0
+ %selx = or i8 %sel, %x
+ %r = icmp ne i8 %selx, 0
+ ret i1 %r
+}
+
+define i1 @src_tv_ne(i1 %c0, i8 %x, i8 %yy) {
+; CHECK-LABEL: @src_tv_ne(
+; CHECK-NEXT: [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C0:%.*]], i8 0, i8 [[Y]]
+; CHECK-NEXT: [[SELX:%.*]] = or i8 [[SEL]], [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[SELX]], 0
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %y = add nuw i8 %yy, 1
+ %sel = select i1 %c0, i8 0, i8 %y
+ %selx = or i8 %sel, %x
+ %r = icmp ne i8 %selx, 0
+ ret i1 %r
+}
+
+define i1 @src_tv_ne_fail_cmp_nonzero(i1 %c0, i8 %x, i8 %yy) {
+; CHECK-LABEL: @src_tv_ne_fail_cmp_nonzero(
+; CHECK-NEXT: [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C0:%.*]], i8 0, i8 [[Y]]
+; CHECK-NEXT: [[SELX:%.*]] = or i8 [[SEL]], [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[SELX]], 1
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %y = add nuw i8 %yy, 1
+ %sel = select i1 %c0, i8 0, i8 %y
+ %selx = or i8 %sel, %x
+ %r = icmp ne i8 %selx, 1
+ ret i1 %r
+}
+
+define i1 @src_fv_eq(i1 %c0, i8 %x, i8 %yy) {
+; CHECK-LABEL: @src_fv_eq(
+; CHECK-NEXT: [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C0:%.*]], i8 [[Y]], i8 0
+; CHECK-NEXT: [[SELX:%.*]] = or i8 [[SEL]], [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[SELX]], 0
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %y = add nuw i8 %yy, 1
+ %sel = select i1 %c0, i8 %y, i8 0
+ %selx = or i8 %sel, %x
+ %r = icmp eq i8 %selx, 0
+ ret i1 %r
+}
+
+define i1 @src_fv_eq_fail_cant_invert(i1 %c0, i8 %x, i8 %yy) {
+; CHECK-LABEL: @src_fv_eq_fail_cant_invert(
+; CHECK-NEXT: [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C0:%.*]], i8 [[Y]], i8 0
+; CHECK-NEXT: [[SELX:%.*]] = or i8 [[SEL]], [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[SELX]], 0
+; CHECK-NEXT: call void @use.i8(i8 [[SEL]])
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %y = add nuw i8 %yy, 1
+ %sel = select i1 %c0, i8 %y, i8 0
+ %selx = or i8 %sel, %x
+ %r = icmp eq i8 %selx, 0
+ call void @use.i8(i8 %sel)
+ ret i1 %r
+}
+
+define i1 @src_fv_eq_fail_cant_invert2(i1 %c1, i8 %a, i8 %b, i8 %x, i8 %yy) {
+; CHECK-LABEL: @src_fv_eq_fail_cant_invert2(
+; CHECK-NEXT: [[C0:%.*]] = icmp ugt i8 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C0]], i8 [[Y]], i8 0
+; CHECK-NEXT: [[CC:%.*]] = or i1 [[C0]], [[C1:%.*]]
+; CHECK-NEXT: [[SEL_OTHER:%.*]] = select i1 [[CC]], i8 [[Y]], i8 [[B]]
+; CHECK-NEXT: [[SELX:%.*]] = or i8 [[SEL]], [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[SELX]], 0
+; CHECK-NEXT: call void @use.i8(i8 [[SEL]])
+; CHECK-NEXT: call void @use.i8(i8 [[SEL_OTHER]])
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %c0 = icmp ugt i8 %a, %b
+ %y = add nuw i8 %yy, 1
+ %sel = select i1 %c0, i8 %y, i8 0
+ %cc = or i1 %c0, %c1
+ %sel_other = select i1 %cc, i8 %y, i8 %b
+
+ %selx = or i8 %sel, %x
+ %r = icmp eq i8 %selx, 0
+ call void @use.i8(i8 %sel)
+ call void @use.i8(i8 %sel_other)
+ ret i1 %r
+}
+
+define i1 @src_fv_eq_invert2(i1 %c1, i8 %a, i8 %b, i8 %x, i8 %yy) {
+; CHECK-LABEL: @src_fv_eq_invert2(
+; CHECK-NEXT: [[C0:%.*]] = icmp ugt i8 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C0]], i8 [[Y]], i8 0
+; CHECK-NEXT: [[CC:%.*]] = or i1 [[C0]], [[C1:%.*]]
+; CHECK-NEXT: [[SEL_OTHER:%.*]] = select i1 [[CC]], i8 [[Y]], i8 [[B]]
+; CHECK-NEXT: [[SELX:%.*]] = or i8 [[SEL]], [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[SELX]], 0
+; CHECK-NEXT: call void @use.i8(i8 [[SEL_OTHER]])
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %c0 = icmp ugt i8 %a, %b
+ %y = add nuw i8 %yy, 1
+ %sel = select i1 %c0, i8 %y, i8 0
+ %cc = or i1 %c0, %c1
+ %sel_other = select i1 %cc, i8 %y, i8 %b
+
+ %selx = or i8 %sel, %x
+ %r = icmp eq i8 %selx, 0
+ call void @use.i8(i8 %sel_other)
+ ret i1 %r
+}
+
+define i1 @src_fv_eq_invert2_fail_wrong_binop(i1 %c1, i8 %a, i8 %b, i8 %x, i8 %yy) {
+; CHECK-LABEL: @src_fv_eq_invert2_fail_wrong_binop(
+; CHECK-NEXT: [[C0:%.*]] = icmp ugt i8 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C0]], i8 [[Y]], i8 0
+; CHECK-NEXT: [[CC:%.*]] = or i1 [[C0]], [[C1:%.*]]
+; CHECK-NEXT: [[SEL_OTHER:%.*]] = select i1 [[CC]], i8 [[Y]], i8 [[B]]
+; CHECK-NEXT: [[SELX:%.*]] = sub i8 0, [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[SEL]], [[SELX]]
+; CHECK-NEXT: call void @use.i8(i8 [[SEL_OTHER]])
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %c0 = icmp ugt i8 %a, %b
+ %y = add nuw i8 %yy, 1
+ %sel = select i1 %c0, i8 %y, i8 0
+ %cc = or i1 %c0, %c1
+ %sel_other = select i1 %cc, i8 %y, i8 %b
+
+ %selx = add i8 %sel, %x
+ %r = icmp eq i8 %selx, 0
+ call void @use.i8(i8 %sel_other)
+ ret i1 %r
+}
+
+define i1 @src_fv_eq_invert2_fail_bad_sel(i1 %c1, i8 %a, i8 %b, i8 %x, i8 %yy) {
+; CHECK-LABEL: @src_fv_eq_invert2_fail_bad_sel(
+; CHECK-NEXT: [[C0:%.*]] = icmp ugt i8 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C0]], i8 [[YY]], i8 0
+; CHECK-NEXT: [[CC:%.*]] = or i1 [[C0]], [[C1:%.*]]
+; CHECK-NEXT: [[SEL_OTHER:%.*]] = select i1 [[CC]], i8 [[Y]], i8 [[B]]
+; CHECK-NEXT: [[SELX:%.*]] = or i8 [[SEL]], [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[SELX]], 0
+; CHECK-NEXT: call void @use.i8(i8 [[SEL_OTHER]])
+; CHECK-NEXT: call void @use.i8(i8 [[YY]])
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %c0 = icmp ugt i8 %a, %b
+ %y = add nuw i8 %yy, 1
+ %sel = select i1 %c0, i8 %yy, i8 0
+ %cc = or i1 %c0, %c1
+ %sel_other = select i1 %cc, i8 %y, i8 %b
+
+ %selx = or i8 %sel, %x
+ %r = icmp eq i8 %selx, 0
+ call void @use.i8(i8 %sel_other)
+ call void @use.i8(i8 %yy)
+ ret i1 %r
+}
+
+define i1 @src_fv_eq_invert3(i8 %a, i8 %b, i8 %x, i8 %yy) {
+; CHECK-LABEL: @src_fv_eq_invert3(
+; CHECK-NEXT: [[C0:%.*]] = icmp ugt i8 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C0]], i8 [[Y]], i8 0
+; CHECK-NEXT: [[SEL_OTHER:%.*]] = select i1 [[C0]], i8 [[Y]], i8 [[B]]
+; CHECK-NEXT: [[SELX:%.*]] = or i8 [[SEL]], [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[SELX]], 0
+; CHECK-NEXT: call void @use.i8(i8 [[SEL_OTHER]])
+; CHECK-NEXT: call void @use.i8(i8 [[SEL]])
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %c0 = icmp ugt i8 %a, %b
+ %y = add nuw i8 %yy, 1
+ %sel = select i1 %c0, i8 %y, i8 0
+ %sel_other = select i1 %c0, i8 %y, i8 %b
+
+ %selx = or i8 %sel, %x
+ %r = icmp eq i8 %selx, 0
+ call void @use.i8(i8 %sel_other)
+ call void @use.i8(i8 %sel)
+ ret i1 %r
+}
+
+define i1 @src_tv_ne_invert(i1 %c1, i8 %a, i8 %b, i8 %x, i8 %yy) {
+; CHECK-LABEL: @src_tv_ne_invert(
+; CHECK-NEXT: [[NOT_C0:%.*]] = icmp ugt i8 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: call void @use.i1(i1 [[NOT_C0]])
+; CHECK-NEXT: [[C0:%.*]] = xor i1 [[NOT_C0]], true
+; CHECK-NEXT: [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[NOT_C0]], i8 [[Y]], i8 0
+; CHECK-NEXT: [[CC:%.*]] = or i1 [[C0]], [[C1:%.*]]
+; CHECK-NEXT: [[SEL_OTHER:%.*]] = select i1 [[CC]], i8 [[Y]], i8 [[B]]
+; CHECK-NEXT: [[SELX:%.*]] = or i8 [[SEL]], [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[SELX]], 0
+; CHECK-NEXT: call void @use.i8(i8 [[SEL]])
+; CHECK-NEXT: call void @use.i8(i8 [[SEL_OTHER]])
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %not_c0 = icmp ugt i8 %a, %b
+ call void @use.i1(i1 %not_c0)
+ %c0 = xor i1 %not_c0, true
+ %y = add nuw i8 %yy, 1
+ %sel = select i1 %c0, i8 0, i8 %y
+ %cc = or i1 %c0, %c1
+ %sel_other = select i1 %cc, i8 %y, i8 %b
+
+ %selx = or i8 %sel, %x
+ %r = icmp ne i8 %selx, 0
+ call void @use.i8(i8 %sel)
+ call void @use.i8(i8 %sel_other)
+ ret i1 %r
+}
>From f57977648bc045647fb52d8b7ee3f32876ecf596 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Tue, 9 Apr 2024 14:36:16 -0500
Subject: [PATCH 2/3] [InstCombine] Fold `(icmp eq/ne (or (select cond, 0/NZ,
0/NZ), X), 0)`
Four cases:
`(icmp eq (or (select cond, 0, NonZero), Other))`
-> `(and cond, (icmp eq Other, 0))`
`(icmp ne (or (select cond, NonZero, 0), Other))`
-> `(or cond, (icmp ne Other, 0))`
`(icmp ne (or (select cond, 0, NonZero), Other))`
-> `(or (not cond), (icmp ne Other, 0))`
`(icmp eq (or (select cond, NonZero, 0), Other))`
-> `(and (not cond), (icmp eq Other, 0))`
These cases came up in tests on: #88088
Proofs: https://alive2.llvm.org/ce/z/ojGo_J
---
.../InstCombine/InstCombineCompares.cpp | 51 +++++++++++++++++++
.../icmp-or-of-select-with-zero.ll | 48 ++++++++---------
2 files changed, 72 insertions(+), 27 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 94786f0b9ec54f..409c25c81816ab 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -3529,6 +3529,57 @@ Instruction *InstCombinerImpl::foldICmpBinOpEqualityWithConstant(
Value *And = Builder.CreateAnd(BOp0, NotBOC);
return new ICmpInst(Pred, And, NotBOC);
}
+ // (icmp eq (or (select cond, 0, NonZero), Other), 0)
+ // -> (and cond, (icmp eq Other, 0))
+ // (icmp ne (or (select cond, NonZero, 0), Other), 0)
+ // -> (or cond, (icmp ne Other, 0))
+ Value *Cond, *TV, *FV, *Other;
+ if (C.isZero() &&
+ match(BO,
+ m_OneUse(m_c_Or(m_Select(m_Value(Cond), m_Value(TV), m_Value(FV)),
+ m_Value(Other))))) {
+ const SimplifyQuery Q = SQ.getWithInstruction(&Cmp);
+ // Easy case is if eq/ne matches whether 0 is trueval/falseval.
+ if (Pred == ICmpInst::ICMP_EQ
+ ? (match(TV, m_SpecificInt(C)) && isKnownNonZero(FV, Q))
+ : (match(FV, m_SpecificInt(C)) && isKnownNonZero(TV, Q))) {
+ Value *Cmp = Builder.CreateICmp(
+ Pred, Other, Constant::getNullValue(Other->getType()));
+ return BinaryOperator::Create(
+ Pred == ICmpInst::ICMP_EQ ? Instruction::And : Instruction::Or, Cmp,
+ Cond);
+ }
+ // Harder case is if eq/ne matches whether 0 is falseval/trueval. In this
+ // case we need to invert the select condition so we need to be careful to
+ // avoid creating extra instructions.
+ // (icmp ne (or (select cond, 0, NonZero), Other), 0)
+ // -> (or (not cond), (icmp ne Other, 0))
+ // (icmp eq (or (select cond, NonZero, 0), Other), 0)
+ // -> (and (not cond), (icmp eq Other, 0))
+ if (Pred == ICmpInst::ICMP_EQ
+ ? (match(FV, m_SpecificInt(C)) && isKnownNonZero(TV, Q))
+ : (match(TV, m_SpecificInt(C)) && isKnownNonZero(FV, Q))) {
+ Value *NotCond = nullptr;
+ // If the select is one use, we are essentially replacing select with
+ // `(not Cond)`.
+ auto SelectMatcher =
+ m_Select(m_Specific(Cond), m_Specific(TV), m_Specific(FV));
+ if (match(BO, m_c_Or(m_OneUse(SelectMatcher), m_Value())))
+ NotCond = Builder.CreateNot(Cond);
+ // Otherwise, see if we can get NotCond for free.
+ else if (match(BO, m_c_Or(SelectMatcher, m_Value())))
+ NotCond =
+ getFreelyInverted(Cond, /*WillInvertAllUses=*/false, &Builder);
+
+ if (NotCond) {
+ Value *Cmp = Builder.CreateICmp(
+ Pred, Other, Constant::getNullValue(Other->getType()));
+ return BinaryOperator::Create(
+ Pred == ICmpInst::ICMP_EQ ? Instruction::And : Instruction::Or,
+ Cmp, NotCond);
+ }
+ }
+ }
break;
}
case Instruction::UDiv:
diff --git a/llvm/test/Transforms/InstCombine/icmp-or-of-select-with-zero.ll b/llvm/test/Transforms/InstCombine/icmp-or-of-select-with-zero.ll
index 22968bab481c15..a876e0612a3ce9 100644
--- a/llvm/test/Transforms/InstCombine/icmp-or-of-select-with-zero.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-or-of-select-with-zero.ll
@@ -5,11 +5,9 @@ declare void @use.i8(i8)
declare void @use.i1(i1)
define i1 @src_tv_eq(i1 %c0, i8 %x, i8 %yy) {
; CHECK-LABEL: @src_tv_eq(
-; CHECK-NEXT: [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
-; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C0:%.*]], i8 0, i8 [[Y]]
-; CHECK-NEXT: [[SELX:%.*]] = or i8 [[SEL]], [[X:%.*]]
-; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[SELX]], 0
-; CHECK-NEXT: ret i1 [[R]]
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[SELX:%.*]], 0
+; CHECK-NEXT: [[R1:%.*]] = and i1 [[R]], [[C0:%.*]]
+; CHECK-NEXT: ret i1 [[R1]]
;
%y = add nuw i8 %yy, 1
%sel = select i1 %c0, i8 0, i8 %y
@@ -52,11 +50,9 @@ define i1 @src_tv_eq_fail_tv_nonzero(i1 %c0, i8 %x, i8 %yy) {
define i1 @src_fv_ne(i1 %c0, i8 %x, i8 %yy) {
; CHECK-LABEL: @src_fv_ne(
-; CHECK-NEXT: [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
-; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C0:%.*]], i8 [[Y]], i8 0
-; CHECK-NEXT: [[SELX:%.*]] = or i8 [[SEL]], [[X:%.*]]
-; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[SELX]], 0
-; CHECK-NEXT: ret i1 [[R]]
+; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[SELX:%.*]], 0
+; CHECK-NEXT: [[R1:%.*]] = or i1 [[R]], [[C0:%.*]]
+; CHECK-NEXT: ret i1 [[R1]]
;
%y = add nuw i8 %yy, 1
%sel = select i1 %c0, i8 %y, i8 0
@@ -82,11 +78,10 @@ define i1 @src_fv_ne_fail_maybe_zero(i1 %c0, i8 %x, i8 %yy) {
define i1 @src_tv_ne(i1 %c0, i8 %x, i8 %yy) {
; CHECK-LABEL: @src_tv_ne(
-; CHECK-NEXT: [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
-; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C0:%.*]], i8 0, i8 [[Y]]
-; CHECK-NEXT: [[SELX:%.*]] = or i8 [[SEL]], [[X:%.*]]
-; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[SELX]], 0
-; CHECK-NEXT: ret i1 [[R]]
+; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[C0:%.*]], true
+; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[SELX:%.*]], 0
+; CHECK-NEXT: [[R1:%.*]] = or i1 [[R]], [[TMP1]]
+; CHECK-NEXT: ret i1 [[R1]]
;
%y = add nuw i8 %yy, 1
%sel = select i1 %c0, i8 0, i8 %y
@@ -112,11 +107,10 @@ define i1 @src_tv_ne_fail_cmp_nonzero(i1 %c0, i8 %x, i8 %yy) {
define i1 @src_fv_eq(i1 %c0, i8 %x, i8 %yy) {
; CHECK-LABEL: @src_fv_eq(
-; CHECK-NEXT: [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
-; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C0:%.*]], i8 [[Y]], i8 0
-; CHECK-NEXT: [[SELX:%.*]] = or i8 [[SEL]], [[X:%.*]]
-; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[SELX]], 0
-; CHECK-NEXT: ret i1 [[R]]
+; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[C0:%.*]], true
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[SELX:%.*]], 0
+; CHECK-NEXT: [[R1:%.*]] = and i1 [[R]], [[TMP1]]
+; CHECK-NEXT: ret i1 [[R1]]
;
%y = add nuw i8 %yy, 1
%sel = select i1 %c0, i8 %y, i8 0
@@ -172,13 +166,13 @@ define i1 @src_fv_eq_invert2(i1 %c1, i8 %a, i8 %b, i8 %x, i8 %yy) {
; CHECK-LABEL: @src_fv_eq_invert2(
; CHECK-NEXT: [[C0:%.*]] = icmp ugt i8 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
-; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C0]], i8 [[Y]], i8 0
; CHECK-NEXT: [[CC:%.*]] = or i1 [[C0]], [[C1:%.*]]
; CHECK-NEXT: [[SEL_OTHER:%.*]] = select i1 [[CC]], i8 [[Y]], i8 [[B]]
-; CHECK-NEXT: [[SELX:%.*]] = or i8 [[SEL]], [[X:%.*]]
-; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[SELX]], 0
+; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[C0]], true
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[SELX:%.*]], 0
+; CHECK-NEXT: [[R1:%.*]] = and i1 [[R]], [[TMP1]]
; CHECK-NEXT: call void @use.i8(i8 [[SEL_OTHER]])
-; CHECK-NEXT: ret i1 [[R]]
+; CHECK-NEXT: ret i1 [[R1]]
;
%c0 = icmp ugt i8 %a, %b
%y = add nuw i8 %yy, 1
@@ -275,11 +269,11 @@ define i1 @src_tv_ne_invert(i1 %c1, i8 %a, i8 %b, i8 %x, i8 %yy) {
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[NOT_C0]], i8 [[Y]], i8 0
; CHECK-NEXT: [[CC:%.*]] = or i1 [[C0]], [[C1:%.*]]
; CHECK-NEXT: [[SEL_OTHER:%.*]] = select i1 [[CC]], i8 [[Y]], i8 [[B]]
-; CHECK-NEXT: [[SELX:%.*]] = or i8 [[SEL]], [[X:%.*]]
-; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[SELX]], 0
+; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[SELX:%.*]], 0
+; CHECK-NEXT: [[R1:%.*]] = or i1 [[R]], [[NOT_C0]]
; CHECK-NEXT: call void @use.i8(i8 [[SEL]])
; CHECK-NEXT: call void @use.i8(i8 [[SEL_OTHER]])
-; CHECK-NEXT: ret i1 [[R]]
+; CHECK-NEXT: ret i1 [[R1]]
;
%not_c0 = icmp ugt i8 %a, %b
call void @use.i1(i1 %not_c0)
>From 489e75ba2d61263f6dee89958826e6a568670739 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Sun, 4 Aug 2024 00:29:46 +0800
Subject: [PATCH 3/3] Fixups
---
.../InstCombine/InstCombineCompares.cpp | 47 +++++++++----------
.../icmp-or-of-select-with-zero.ll | 40 +++++++++-------
2 files changed, 43 insertions(+), 44 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 409c25c81816ab..6b0f6e2a82040c 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -3533,16 +3533,18 @@ Instruction *InstCombinerImpl::foldICmpBinOpEqualityWithConstant(
// -> (and cond, (icmp eq Other, 0))
// (icmp ne (or (select cond, NonZero, 0), Other), 0)
// -> (or cond, (icmp ne Other, 0))
- Value *Cond, *TV, *FV, *Other;
+ Value *Cond, *TV, *FV, *Other, *Sel;
if (C.isZero() &&
match(BO,
- m_OneUse(m_c_Or(m_Select(m_Value(Cond), m_Value(TV), m_Value(FV)),
+ m_OneUse(m_c_Or(m_CombineAnd(m_Value(Sel),
+ m_Select(m_Value(Cond), m_Value(TV),
+ m_Value(FV))),
m_Value(Other))))) {
const SimplifyQuery Q = SQ.getWithInstruction(&Cmp);
// Easy case is if eq/ne matches whether 0 is trueval/falseval.
if (Pred == ICmpInst::ICMP_EQ
- ? (match(TV, m_SpecificInt(C)) && isKnownNonZero(FV, Q))
- : (match(FV, m_SpecificInt(C)) && isKnownNonZero(TV, Q))) {
+ ? (match(TV, m_Zero()) && isKnownNonZero(FV, Q))
+ : (match(FV, m_Zero()) && isKnownNonZero(TV, Q))) {
Value *Cmp = Builder.CreateICmp(
Pred, Other, Constant::getNullValue(Other->getType()));
return BinaryOperator::Create(
@@ -3556,28 +3558,21 @@ Instruction *InstCombinerImpl::foldICmpBinOpEqualityWithConstant(
// -> (or (not cond), (icmp ne Other, 0))
// (icmp eq (or (select cond, NonZero, 0), Other), 0)
// -> (and (not cond), (icmp eq Other, 0))
- if (Pred == ICmpInst::ICMP_EQ
- ? (match(FV, m_SpecificInt(C)) && isKnownNonZero(TV, Q))
- : (match(TV, m_SpecificInt(C)) && isKnownNonZero(FV, Q))) {
- Value *NotCond = nullptr;
- // If the select is one use, we are essentially replacing select with
- // `(not Cond)`.
- auto SelectMatcher =
- m_Select(m_Specific(Cond), m_Specific(TV), m_Specific(FV));
- if (match(BO, m_c_Or(m_OneUse(SelectMatcher), m_Value())))
- NotCond = Builder.CreateNot(Cond);
- // Otherwise, see if we can get NotCond for free.
- else if (match(BO, m_c_Or(SelectMatcher, m_Value())))
- NotCond =
- getFreelyInverted(Cond, /*WillInvertAllUses=*/false, &Builder);
-
- if (NotCond) {
- Value *Cmp = Builder.CreateICmp(
- Pred, Other, Constant::getNullValue(Other->getType()));
- return BinaryOperator::Create(
- Pred == ICmpInst::ICMP_EQ ? Instruction::And : Instruction::Or,
- Cmp, NotCond);
- }
+ //
+ // Only do this if the inner select has one use, in which case we are
+ // replacing `select` with `(not cond)`. Otherwise, we will create more
+ // uses. NB: Trying to freely invert cond doesn't make sense here, as if
+ // cond was freely invertable, the select arms would have been inverted.
+ if (Sel->hasOneUse() &&
+ (Pred == ICmpInst::ICMP_EQ
+ ? (match(FV, m_Zero()) && isKnownNonZero(TV, Q))
+ : (match(TV, m_Zero()) && isKnownNonZero(FV, Q)))) {
+ Value *NotCond = Builder.CreateNot(Cond);
+ Value *Cmp = Builder.CreateICmp(
+ Pred, Other, Constant::getNullValue(Other->getType()));
+ return BinaryOperator::Create(
+ Pred == ICmpInst::ICMP_EQ ? Instruction::And : Instruction::Or, Cmp,
+ NotCond);
}
}
break;
diff --git a/llvm/test/Transforms/InstCombine/icmp-or-of-select-with-zero.ll b/llvm/test/Transforms/InstCombine/icmp-or-of-select-with-zero.ll
index a876e0612a3ce9..90e0461f8b789e 100644
--- a/llvm/test/Transforms/InstCombine/icmp-or-of-select-with-zero.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-or-of-select-with-zero.ll
@@ -5,9 +5,9 @@ declare void @use.i8(i8)
declare void @use.i1(i1)
define i1 @src_tv_eq(i1 %c0, i8 %x, i8 %yy) {
; CHECK-LABEL: @src_tv_eq(
-; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[SELX:%.*]], 0
-; CHECK-NEXT: [[R1:%.*]] = and i1 [[R]], [[C0:%.*]]
-; CHECK-NEXT: ret i1 [[R1]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i8 [[X:%.*]], 0
+; CHECK-NEXT: [[R:%.*]] = and i1 [[TMP1]], [[C0:%.*]]
+; CHECK-NEXT: ret i1 [[R]]
;
%y = add nuw i8 %yy, 1
%sel = select i1 %c0, i8 0, i8 %y
@@ -50,9 +50,9 @@ define i1 @src_tv_eq_fail_tv_nonzero(i1 %c0, i8 %x, i8 %yy) {
define i1 @src_fv_ne(i1 %c0, i8 %x, i8 %yy) {
; CHECK-LABEL: @src_fv_ne(
-; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[SELX:%.*]], 0
-; CHECK-NEXT: [[R1:%.*]] = or i1 [[R]], [[C0:%.*]]
-; CHECK-NEXT: ret i1 [[R1]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i8 [[X:%.*]], 0
+; CHECK-NEXT: [[R:%.*]] = or i1 [[TMP1]], [[C0:%.*]]
+; CHECK-NEXT: ret i1 [[R]]
;
%y = add nuw i8 %yy, 1
%sel = select i1 %c0, i8 %y, i8 0
@@ -79,9 +79,9 @@ define i1 @src_fv_ne_fail_maybe_zero(i1 %c0, i8 %x, i8 %yy) {
define i1 @src_tv_ne(i1 %c0, i8 %x, i8 %yy) {
; CHECK-LABEL: @src_tv_ne(
; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[C0:%.*]], true
-; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[SELX:%.*]], 0
-; CHECK-NEXT: [[R1:%.*]] = or i1 [[R]], [[TMP1]]
-; CHECK-NEXT: ret i1 [[R1]]
+; CHECK-NEXT: [[TMP2:%.*]] = icmp ne i8 [[X:%.*]], 0
+; CHECK-NEXT: [[R:%.*]] = or i1 [[TMP2]], [[TMP1]]
+; CHECK-NEXT: ret i1 [[R]]
;
%y = add nuw i8 %yy, 1
%sel = select i1 %c0, i8 0, i8 %y
@@ -108,9 +108,9 @@ define i1 @src_tv_ne_fail_cmp_nonzero(i1 %c0, i8 %x, i8 %yy) {
define i1 @src_fv_eq(i1 %c0, i8 %x, i8 %yy) {
; CHECK-LABEL: @src_fv_eq(
; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[C0:%.*]], true
-; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[SELX:%.*]], 0
-; CHECK-NEXT: [[R1:%.*]] = and i1 [[R]], [[TMP1]]
-; CHECK-NEXT: ret i1 [[R1]]
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[X:%.*]], 0
+; CHECK-NEXT: [[R:%.*]] = and i1 [[TMP2]], [[TMP1]]
+; CHECK-NEXT: ret i1 [[R]]
;
%y = add nuw i8 %yy, 1
%sel = select i1 %c0, i8 %y, i8 0
@@ -169,10 +169,10 @@ define i1 @src_fv_eq_invert2(i1 %c1, i8 %a, i8 %b, i8 %x, i8 %yy) {
; CHECK-NEXT: [[CC:%.*]] = or i1 [[C0]], [[C1:%.*]]
; CHECK-NEXT: [[SEL_OTHER:%.*]] = select i1 [[CC]], i8 [[Y]], i8 [[B]]
; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[C0]], true
-; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[SELX:%.*]], 0
-; CHECK-NEXT: [[R1:%.*]] = and i1 [[R]], [[TMP1]]
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[X:%.*]], 0
+; CHECK-NEXT: [[R:%.*]] = and i1 [[TMP2]], [[TMP1]]
; CHECK-NEXT: call void @use.i8(i8 [[SEL_OTHER]])
-; CHECK-NEXT: ret i1 [[R1]]
+; CHECK-NEXT: ret i1 [[R]]
;
%c0 = icmp ugt i8 %a, %b
%y = add nuw i8 %yy, 1
@@ -186,6 +186,9 @@ define i1 @src_fv_eq_invert2(i1 %c1, i8 %a, i8 %b, i8 %x, i8 %yy) {
ret i1 %r
}
+
+
+
define i1 @src_fv_eq_invert2_fail_wrong_binop(i1 %c1, i8 %a, i8 %b, i8 %x, i8 %yy) {
; CHECK-LABEL: @src_fv_eq_invert2_fail_wrong_binop(
; CHECK-NEXT: [[C0:%.*]] = icmp ugt i8 [[A:%.*]], [[B:%.*]]
@@ -260,6 +263,7 @@ define i1 @src_fv_eq_invert3(i8 %a, i8 %b, i8 %x, i8 %yy) {
ret i1 %r
}
+
define i1 @src_tv_ne_invert(i1 %c1, i8 %a, i8 %b, i8 %x, i8 %yy) {
; CHECK-LABEL: @src_tv_ne_invert(
; CHECK-NEXT: [[NOT_C0:%.*]] = icmp ugt i8 [[A:%.*]], [[B:%.*]]
@@ -269,11 +273,11 @@ define i1 @src_tv_ne_invert(i1 %c1, i8 %a, i8 %b, i8 %x, i8 %yy) {
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[NOT_C0]], i8 [[Y]], i8 0
; CHECK-NEXT: [[CC:%.*]] = or i1 [[C0]], [[C1:%.*]]
; CHECK-NEXT: [[SEL_OTHER:%.*]] = select i1 [[CC]], i8 [[Y]], i8 [[B]]
-; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[SELX:%.*]], 0
-; CHECK-NEXT: [[R1:%.*]] = or i1 [[R]], [[NOT_C0]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i8 [[X:%.*]], 0
+; CHECK-NEXT: [[R:%.*]] = or i1 [[TMP1]], [[NOT_C0]]
; CHECK-NEXT: call void @use.i8(i8 [[SEL]])
; CHECK-NEXT: call void @use.i8(i8 [[SEL_OTHER]])
-; CHECK-NEXT: ret i1 [[R1]]
+; CHECK-NEXT: ret i1 [[R]]
;
%not_c0 = icmp ugt i8 %a, %b
call void @use.i1(i1 %not_c0)
More information about the llvm-commits
mailing list