Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[InstCombine] Fold (icmp eq/ne (or (select cond, 0/NZ, 0/NZ), X), 0) #88183

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3529,6 +3529,52 @@ 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, *Sel;
if (C.isZero() &&
match(BO,
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_Zero()) && isKnownNonZero(FV, Q))
: (match(FV, m_Zero()) && 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.
dtcxzyw marked this conversation as resolved.
Show resolved Hide resolved
dtcxzyw marked this conversation as resolved.
Show resolved Hide resolved
// (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))
//
// 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;
}
case Instruction::UDiv:
Expand Down
295 changes: 295 additions & 0 deletions llvm/test/Transforms/InstCombine/icmp-or-of-select-with-zero.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
; 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: [[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
%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: [[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
%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: [[TMP1:%.*]] = xor i1 [[C0:%.*]], true
; 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
%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: [[TMP1:%.*]] = xor i1 [[C0:%.*]], true
; 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
%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: [[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: [[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 [[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
}




Comment on lines +189 to +191
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

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: [[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 [[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
}
Loading