Skip to content

Commit 8ade0f6

Browse files
committed
[InstCombine] fold (a == 1 && b != 0) || (a != 0 && b == 0)) to `(a ==
0) != (b == 0)`
1 parent 47fd32f commit 8ade0f6

File tree

2 files changed

+131
-0
lines changed

2 files changed

+131
-0
lines changed

llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3421,6 +3421,29 @@ Value *InstCombinerImpl::foldAndOrOfICmps(ICmpInst *LHS, ICmpInst *RHS,
34213421
return foldAndOrOfICmpsUsingRanges(LHS, RHS, IsAnd);
34223422
}
34233423

3424+
Value *foldAorBZero(BinaryOperator &I, InstCombiner::BuilderTy &Builder) {
3425+
Value *Op0 = I.getOperand(0);
3426+
Value *Op1 = I.getOperand(1);
3427+
if (!Op0->hasOneUse() || !Op1->hasOneUse())
3428+
return nullptr;
3429+
3430+
// match each operand of I with and
3431+
Value *A, *B;
3432+
CmpInst::Predicate Pred = CmpInst::ICMP_EQ;
3433+
CmpInst::Predicate InPred = CmpInst::ICMP_EQ;
3434+
bool IsOp0 = match(Op0, m_c_And(m_Cmp(Pred, m_Value(A), m_ZeroInt()),
3435+
m_Cmp(InPred, m_Value(B), m_ZeroInt())));
3436+
bool IsOp1 = match(Op1, m_c_And(m_Cmp(InPred, m_Specific(A), m_ZeroInt()),
3437+
m_Cmp(Pred, m_Specific(B), m_ZeroInt())));
3438+
if (!IsOp0 || !IsOp1)
3439+
return nullptr;
3440+
3441+
Constant *Zero = ConstantInt::getNullValue(A->getType());
3442+
auto *LHS = Builder.CreateICmpEQ(A, Zero);
3443+
auto *RHS = Builder.CreateICmpEQ(B, Zero);
3444+
return Builder.CreateICmpNE(LHS, RHS);
3445+
}
3446+
34243447
// FIXME: We use commutative matchers (m_c_*) for some, but not all, matches
34253448
// here. We should standardize that construct where it is needed or choose some
34263449
// other way to ensure that commutated variants of patterns are not missed.
@@ -3450,6 +3473,10 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
34503473
if (Instruction *X = foldComplexAndOrPatterns(I, Builder))
34513474
return X;
34523475

3476+
// (A == 0 & B != 0) | (A != 0 & B == 0)) -> (A == 0) != (B == 0)
3477+
if (Value *V = foldAorBZero(I, Builder))
3478+
return replaceInstUsesWith(I, V);
3479+
34533480
// (A&B)|(A&C) -> A&(B|C) etc
34543481
if (Value *V = foldUsingDistributiveLaws(I))
34553482
return replaceInstUsesWith(I, V);
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt < %s -S -passes=instcombine | FileCheck %s
3+
4+
declare void @use(i1)
5+
6+
define void @a_or_b(i32 %a, i32 %b) {
7+
; CHECK-LABEL: define void @src(
8+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
9+
; CHECK-NEXT: [[A_EQ_ZERO:%.*]] = icmp eq i32 [[A]], 0
10+
; CHECK-NEXT: [[B_NE_ZERO:%.*]] = icmp ne i32 [[B]], 0
11+
; CHECK-NEXT: [[AND_1:%.*]] = and i1 [[A_EQ_ZERO]], [[B_NE_ZERO]]
12+
; CHECK-NEXT: [[A_NE_ZERO:%.*]] = icmp ne i32 [[A]], 0
13+
; CHECK-NEXT: [[B_EQ_ZERO:%.*]] = icmp eq i32 [[B]], 0
14+
; CHECK-NEXT: [[AND_2:%.*]] = and i1 [[A_NE_ZERO]], [[B_EQ_ZERO]]
15+
; CHECK-NEXT: [[OR:%.*]] = or i1 [[AND_1]], [[AND_2]]
16+
; CHECK-NEXT: call void @use(i1 [[OR]])
17+
; CHECK-NEXT: ret void
18+
;
19+
%a_eq_zero = icmp eq i32 %a, 0
20+
%b_ne_zero = icmp ne i32 %b, 0
21+
%and.1 = and i1 %a_eq_zero, %b_ne_zero
22+
%a_ne_zero = icmp ne i32 %a, 0
23+
%b_eq_zero = icmp eq i32 %b, 0
24+
%and.2 = and i1 %a_ne_zero, %b_eq_zero
25+
%or = or i1 %and.1, %and.2
26+
call void @use(i1 %or)
27+
ret void
28+
}
29+
30+
31+
define void @a_or_b_zero(i32 %a, i32 %b) {
32+
; CHECK-LABEL: define void @src(
33+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
34+
; CHECK-NEXT: [[A_EQ_ZERO:%.*]] = icmp eq i32 [[A]], 0
35+
; CHECK-NEXT: [[B_NE_ZERO:%.*]] = icmp ne i32 [[B]], 0
36+
; CHECK-NEXT: [[AND_1:%.*]] = and i1 [[A_EQ_ZERO]], [[B_NE_ZERO]]
37+
; CHECK-NEXT: [[A_NE_ZERO:%.*]] = icmp ne i32 [[A]], 0
38+
; CHECK-NEXT: [[B_EQ_ZERO:%.*]] = icmp eq i32 [[B]], 0
39+
; CHECK-NEXT: [[AND_2:%.*]] = and i1 [[A_NE_ZERO]], [[B_EQ_ZERO]]
40+
; CHECK-NEXT: [[OR:%.*]] = or i1 [[AND_1]], [[AND_2]]
41+
; CHECK-NEXT: call void @use(i1 [[OR]])
42+
; CHECK-NEXT: ret void
43+
;
44+
%a_eq_zero = icmp eq i32 %a, 0
45+
%b_ne_zero = icmp ne i32 %b, 0
46+
%and.1 = and i1 %a_eq_zero, %b_ne_zero
47+
%a_ne_zero = icmp ne i32 %a, 0
48+
%b_eq_zero = icmp eq i32 %b, 0
49+
%and.2 = and i1 %a_ne_zero, %b_eq_zero
50+
%or = or i1 %and.1, %and.2
51+
call void @use(i1 %or)
52+
ret void
53+
}
54+
55+
define void @a_or_b_multiple_uses(i32 %a, i32 %b) {
56+
; CHECK-LABEL: define void @src(
57+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
58+
; CHECK-NEXT: [[A_EQ_ZERO:%.*]] = icmp eq i32 [[A]], 0
59+
; CHECK-NEXT: [[B_NE_ZERO:%.*]] = icmp ne i32 [[B]], 0
60+
; CHECK-NEXT: [[AND_1:%.*]] = and i1 [[A_EQ_ZERO]], [[B_NE_ZERO]]
61+
; CHECK-NEXT: [[A_NE_ZERO:%.*]] = icmp ne i32 [[A]], 0
62+
; CHECK-NEXT: [[B_EQ_ZERO:%.*]] = icmp eq i32 [[B]], 0
63+
; CHECK-NEXT: [[AND_2:%.*]] = and i1 [[A_NE_ZERO]], [[B_EQ_ZERO]]
64+
; CHECK-NEXT: [[OR:%.*]] = or i1 [[AND_1]], [[AND_2]]
65+
; CHECK-NEXT: call void @use(i1 [[OR]])
66+
; CHECK-NEXT: ret void
67+
;
68+
%a_eq_zero = icmp eq i32 %a, 0
69+
%b_ne_zero = icmp ne i32 %b, 0
70+
%and.1 = and i1 %a_eq_zero, %b_ne_zero
71+
%a_ne_zero = icmp ne i32 %a, 0
72+
%b_eq_zero = icmp eq i32 %b, 0
73+
%and.2 = and i1 %a_ne_zero, %b_eq_zero
74+
call void @use(i1 %and.2)
75+
%or = or i1 %and.1, %and.2
76+
ret void
77+
}
78+
79+
define void @a_or_b_multiple_uses_2(i32 %a, i32 %b) {
80+
; CHECK-LABEL: define void @src(
81+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
82+
; CHECK-NEXT: [[A_EQ_ZERO:%.*]] = icmp eq i32 [[A]], 0
83+
; CHECK-NEXT: [[B_NE_ZERO:%.*]] = icmp ne i32 [[B]], 0
84+
; CHECK-NEXT: [[AND_1:%.*]] = and i1 [[A_EQ_ZERO]], [[B_NE_ZERO]]
85+
; CHECK-NEXT: [[A_NE_ZERO:%.*]] = icmp ne i32 [[A]], 0
86+
; CHECK-NEXT: [[B_EQ_ZERO:%.*]] = icmp eq i32 [[B]], 0
87+
; CHECK-NEXT: [[AND_2:%.*]] = and i1 [[A_NE_ZERO]], [[B_EQ_ZERO]]
88+
; CHECK-NEXT: [[OR:%.*]] = or i1 [[AND_1]], [[AND_2]]
89+
; CHECK-NEXT: call void @use(i1 [[OR]])
90+
; CHECK-NEXT: ret void
91+
;
92+
%a_eq_zero = icmp eq i32 %a, 0
93+
%b_ne_zero = icmp ne i32 %b, 0
94+
call void @use(i1 %b_ne_zero)
95+
%and.1 = and i1 %a_eq_zero, %b_ne_zero
96+
%a_ne_zero = icmp ne i32 %a, 0
97+
%b_eq_zero = icmp eq i32 %b, 0
98+
%and.2 = and i1 %a_ne_zero, %b_eq_zero
99+
call void @use(i1 %and.1)
100+
%or = or i1 %and.1, %and.2
101+
ret void
102+
}
103+
104+

0 commit comments

Comments
 (0)