From 6a2ba6873109d6d2ce97816b10b96f9e8870df7f Mon Sep 17 00:00:00 2001 From: Noah Goldstein 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 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 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)