Skip to content

Commit f7fc72e

Browse files
authored
[InstCombine] fold (a == c && b != c) || (a != c && b == c)) to (a == c) == (b != c) (#94915)
resolves #92966 alive proof https://alive2.llvm.org/ce/z/bLAQBS
1 parent 3ae6755 commit f7fc72e

File tree

2 files changed

+169
-0
lines changed

2 files changed

+169
-0
lines changed

llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp

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

3424+
static Value *foldOrOfInversions(BinaryOperator &I,
3425+
InstCombiner::BuilderTy &Builder) {
3426+
assert(I.getOpcode() == Instruction::Or &&
3427+
"Simplification only supports or at the moment.");
3428+
3429+
Value *Cmp1, *Cmp2, *Cmp3, *Cmp4;
3430+
if (!match(I.getOperand(0), m_And(m_Value(Cmp1), m_Value(Cmp2))) ||
3431+
!match(I.getOperand(1), m_And(m_Value(Cmp3), m_Value(Cmp4))))
3432+
return nullptr;
3433+
3434+
// Check if any two pairs of the and operations are inversions of each other.
3435+
if (isKnownInversion(Cmp1, Cmp3) && isKnownInversion(Cmp2, Cmp4))
3436+
return Builder.CreateXor(Cmp1, Cmp4);
3437+
if (isKnownInversion(Cmp1, Cmp4) && isKnownInversion(Cmp2, Cmp3))
3438+
return Builder.CreateXor(Cmp1, Cmp3);
3439+
3440+
return nullptr;
3441+
}
3442+
34243443
// FIXME: We use commutative matchers (m_c_*) for some, but not all, matches
34253444
// here. We should standardize that construct where it is needed or choose some
34263445
// other way to ensure that commutated variants of patterns are not missed.
@@ -3450,6 +3469,11 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
34503469
if (Instruction *X = foldComplexAndOrPatterns(I, Builder))
34513470
return X;
34523471

3472+
// (A & B) | (C & D) -> A ^ D where A == ~C && B == ~D
3473+
// (A & B) | (C & D) -> A ^ C where A == ~D && B == ~C
3474+
if (Value *V = foldOrOfInversions(I, Builder))
3475+
return replaceInstUsesWith(I, V);
3476+
34533477
// (A&B)|(A&C) -> A&(B|C) etc
34543478
if (Value *V = foldUsingDistributiveLaws(I))
34553479
return replaceInstUsesWith(I, V);
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
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 i1 @a_or_b(i32 %a, i32 %b) {
7+
; CHECK-LABEL: define i1 @a_or_b(
8+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
9+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[A]], 0
10+
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[B]], 0
11+
; CHECK-NEXT: [[OR:%.*]] = xor i1 [[TMP1]], [[TMP2]]
12+
; CHECK-NEXT: ret i1 [[OR]]
13+
;
14+
%a_eq_zero = icmp eq i32 %a, 0
15+
%b_ne_zero = icmp ne i32 %b, 0
16+
%and.1 = and i1 %a_eq_zero, %b_ne_zero
17+
%a_ne_zero = icmp ne i32 %a, 0
18+
%b_eq_zero = icmp eq i32 %b, 0
19+
%and.2 = and i1 %a_ne_zero, %b_eq_zero
20+
%or = or i1 %and.1, %and.2
21+
ret i1 %or
22+
}
23+
24+
define i1 @a_or_b_not_inv(i32 %a, i32 %b){
25+
; CHECK-LABEL: define i1 @a_or_b_not_inv(
26+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
27+
; CHECK-NEXT: [[A_EQ_ZERO:%.*]] = icmp eq i32 [[A]], 0
28+
; CHECK-NEXT: [[B_NE_ZERO:%.*]] = icmp ne i32 [[B]], 0
29+
; CHECK-NEXT: [[AND_1:%.*]] = and i1 [[A_EQ_ZERO]], [[B_NE_ZERO]]
30+
; CHECK-NEXT: [[A_NE_ZERO:%.*]] = icmp ne i32 [[A]], 0
31+
; CHECK-NEXT: [[B_EQ_1:%.*]] = icmp eq i32 [[B]], 1
32+
; CHECK-NEXT: [[AND_2:%.*]] = and i1 [[A_NE_ZERO]], [[B_EQ_1]]
33+
; CHECK-NEXT: [[OR:%.*]] = or i1 [[AND_1]], [[AND_2]]
34+
; CHECK-NEXT: ret i1 [[OR]]
35+
;
36+
%a_eq_zero = icmp eq i32 %a, 0
37+
%b_ne_zero = icmp ne i32 %b, 0
38+
%and.1 = and i1 %a_eq_zero, %b_ne_zero
39+
%a_ne_zero = icmp ne i32 %a, 0
40+
%b_eq_1 = icmp eq i32 %b, 1
41+
%and.2 = and i1 %a_ne_zero, %b_eq_1
42+
%or = or i1 %and.1, %and.2
43+
ret i1 %or
44+
}
45+
46+
define i1 @a_or_b_const(i32 %a, i32 %b, i32 %c) {
47+
; CHECK-LABEL: define i1 @a_or_b_const(
48+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]], i32 [[C:%.*]]) {
49+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[A]], [[C]]
50+
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[B]], [[C]]
51+
; CHECK-NEXT: [[OR:%.*]] = xor i1 [[TMP1]], [[TMP2]]
52+
; CHECK-NEXT: ret i1 [[OR]]
53+
;
54+
%a_eq_c = icmp eq i32 %a, %c
55+
%b_ne_c = icmp ne i32 %b, %c
56+
%and.1 = and i1 %a_eq_c, %b_ne_c
57+
%a_ne_c = icmp ne i32 %a, %c
58+
%b_eq_c = icmp eq i32 %b, %c
59+
%and.2 = and i1 %a_ne_c, %b_eq_c
60+
%or = or i1 %and.1, %and.2
61+
ret i1 %or
62+
}
63+
64+
define i1 @a_or_b_const2(i32 %a, i32 %b, i32 %c, i32 %d) {
65+
; CHECK-LABEL: define i1 @a_or_b_const2(
66+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]], i32 [[C:%.*]], i32 [[D:%.*]]) {
67+
; CHECK-NEXT: [[A_EQ_C:%.*]] = icmp eq i32 [[A]], [[C]]
68+
; CHECK-NEXT: [[B_EQ_D:%.*]] = icmp eq i32 [[B]], [[D]]
69+
; CHECK-NEXT: [[OR:%.*]] = xor i1 [[A_EQ_C]], [[B_EQ_D]]
70+
; CHECK-NEXT: ret i1 [[OR]]
71+
;
72+
%a_eq_c = icmp eq i32 %a, %c
73+
%b_ne_d = icmp ne i32 %b, %d
74+
%and.1 = and i1 %a_eq_c, %b_ne_d
75+
%a_ne_c = icmp ne i32 %a, %c
76+
%b_eq_d = icmp eq i32 %b, %d
77+
%and.2 = and i1 %a_ne_c, %b_eq_d
78+
%or = or i1 %and.1, %and.2
79+
ret i1 %or
80+
}
81+
define i1 @a_or_b_nullptr(ptr %a, ptr %b) {
82+
; CHECK-LABEL: define i1 @a_or_b_nullptr(
83+
; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]]) {
84+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq ptr [[A]], null
85+
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq ptr [[B]], null
86+
; CHECK-NEXT: [[OR:%.*]] = xor i1 [[TMP1]], [[TMP2]]
87+
; CHECK-NEXT: ret i1 [[OR]]
88+
;
89+
%a_null = icmp eq ptr %a, null
90+
%b_null = icmp eq ptr %b, null
91+
%a_not_null = icmp ne ptr %a, null
92+
%b_not_null = icmp ne ptr %b, null
93+
%and.1 = and i1 %a_null, %b_not_null
94+
%and.2 = and i1 %a_not_null, %b_null
95+
%or = or i1 %and.1, %and.2
96+
ret i1 %or
97+
}
98+
99+
define i1 @a_or_b_multiple_uses(i32 %a, i32 %b) {
100+
; CHECK-LABEL: define i1 @a_or_b_multiple_uses(
101+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
102+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[A]], 0
103+
; CHECK-NEXT: [[A_NE_ZERO:%.*]] = icmp ne i32 [[A]], 0
104+
; CHECK-NEXT: [[B_EQ_ZERO:%.*]] = icmp eq i32 [[B]], 0
105+
; CHECK-NEXT: [[AND_2:%.*]] = and i1 [[A_NE_ZERO]], [[B_EQ_ZERO]]
106+
; CHECK-NEXT: call void @use(i1 [[AND_2]])
107+
; CHECK-NEXT: [[OR:%.*]] = xor i1 [[TMP1]], [[B_EQ_ZERO]]
108+
; CHECK-NEXT: ret i1 [[OR]]
109+
;
110+
%a_eq_zero = icmp eq i32 %a, 0
111+
%b_ne_zero = icmp ne i32 %b, 0
112+
%and.1 = and i1 %a_eq_zero, %b_ne_zero
113+
%a_ne_zero = icmp ne i32 %a, 0
114+
%b_eq_zero = icmp eq i32 %b, 0
115+
%and.2 = and i1 %a_ne_zero, %b_eq_zero
116+
call void @use(i1 %and.2)
117+
%or = or i1 %and.1, %and.2
118+
ret i1 %or
119+
}
120+
121+
define i1 @a_or_b_multiple_uses_2(i32 %a, i32 %b) {
122+
; CHECK-LABEL: define i1 @a_or_b_multiple_uses_2(
123+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
124+
; CHECK-NEXT: [[A_EQ_ZERO:%.*]] = icmp eq i32 [[A]], 0
125+
; CHECK-NEXT: [[B_NE_ZERO:%.*]] = icmp ne i32 [[B]], 0
126+
; CHECK-NEXT: call void @use(i1 [[B_NE_ZERO]])
127+
; CHECK-NEXT: [[AND_1:%.*]] = and i1 [[A_EQ_ZERO]], [[B_NE_ZERO]]
128+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[B]], 0
129+
; CHECK-NEXT: call void @use(i1 [[AND_1]])
130+
; CHECK-NEXT: [[OR:%.*]] = xor i1 [[A_EQ_ZERO]], [[TMP1]]
131+
; CHECK-NEXT: ret i1 [[OR]]
132+
;
133+
%a_eq_zero = icmp eq i32 %a, 0
134+
%b_ne_zero = icmp ne i32 %b, 0
135+
call void @use(i1 %b_ne_zero)
136+
%and.1 = and i1 %a_eq_zero, %b_ne_zero
137+
%a_ne_zero = icmp ne i32 %a, 0
138+
%b_eq_zero = icmp eq i32 %b, 0
139+
%and.2 = and i1 %a_ne_zero, %b_eq_zero
140+
call void @use(i1 %and.1)
141+
%or = or i1 %and.1, %and.2
142+
ret i1 %or
143+
}
144+
145+

0 commit comments

Comments
 (0)