Skip to content

Commit d0d12fc

Browse files
authored
[InstCombine] Fold (X==Z) ? (Y==Z) : (!(Y==Z) && X==Y) --> X==Y (#108619)
This corresponds to the canonicalized form of some logic that was seen in Swift-generated code for comparing optional pointers: `(X==Z || Y==Z) ? (X==Z && Y==Z) : X==Y --> X==Y` where `Z` was the constant `0`. https://alive2.llvm.org/ce/z/J_3aa9
1 parent ee4dd14 commit d0d12fc

File tree

3 files changed

+271
-0
lines changed

3 files changed

+271
-0
lines changed

llvm/lib/Transforms/InstCombine/InstCombineInternal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
739739
Instruction *foldSelectOfBools(SelectInst &SI);
740740
Instruction *foldSelectToCmp(SelectInst &SI);
741741
Instruction *foldSelectExtConst(SelectInst &Sel);
742+
Instruction *foldSelectEqualityTest(SelectInst &SI);
742743
Instruction *foldSelectOpOp(SelectInst &SI, Instruction *TI, Instruction *FI);
743744
Instruction *foldSelectIntoOp(SelectInst &SI, Value *, Value *);
744745
Instruction *foldSPFofSPF(Instruction *Inner, SelectPatternFlavor SPF1,

llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,6 +1410,44 @@ Instruction *InstCombinerImpl::foldSelectValueEquivalence(SelectInst &Sel,
14101410
return nullptr;
14111411
}
14121412

1413+
/// Fold the following code sequence:
1414+
/// \code
1415+
/// %XeqZ = icmp eq i64 %X, %Z
1416+
/// %YeqZ = icmp eq i64 %Y, %Z
1417+
/// %XeqY = icmp eq i64 %X, %Y
1418+
/// %not.YeqZ = xor i1 %YeqZ, true
1419+
/// %and = select i1 %not.YeqZ, i1 %XeqY, i1 false
1420+
/// %equal = select i1 %XeqZ, i1 %YeqZ, i1 %and
1421+
/// \code
1422+
///
1423+
/// into:
1424+
/// %equal = icmp eq i64 %X, %Y
1425+
Instruction *InstCombinerImpl::foldSelectEqualityTest(SelectInst &Sel) {
1426+
Value *X, *Y, *Z;
1427+
Value *XeqY, *XeqZ = Sel.getCondition(), *YeqZ = Sel.getTrueValue();
1428+
1429+
if (!match(XeqZ, m_SpecificICmp(ICmpInst::ICMP_EQ, m_Value(X), m_Value(Z))))
1430+
return nullptr;
1431+
1432+
if (!match(YeqZ,
1433+
m_c_SpecificICmp(ICmpInst::ICMP_EQ, m_Value(Y), m_Specific(Z))))
1434+
std::swap(X, Z);
1435+
1436+
if (!match(YeqZ,
1437+
m_c_SpecificICmp(ICmpInst::ICMP_EQ, m_Value(Y), m_Specific(Z))))
1438+
return nullptr;
1439+
1440+
if (!match(Sel.getFalseValue(),
1441+
m_c_LogicalAnd(m_Not(m_Specific(YeqZ)), m_Value(XeqY))))
1442+
return nullptr;
1443+
1444+
if (!match(XeqY,
1445+
m_c_SpecificICmp(ICmpInst::ICMP_EQ, m_Specific(X), m_Specific(Y))))
1446+
return nullptr;
1447+
1448+
return replaceInstUsesWith(Sel, XeqY);
1449+
}
1450+
14131451
// See if this is a pattern like:
14141452
// %old_cmp1 = icmp slt i32 %x, C2
14151453
// %old_replacement = select i1 %old_cmp1, i32 %target_low, i32 %target_high
@@ -4112,6 +4150,9 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
41124150
if (Instruction *I = foldSelectToCmp(SI))
41134151
return I;
41144152

4153+
if (Instruction *I = foldSelectEqualityTest(SI))
4154+
return I;
4155+
41154156
// Fold:
41164157
// (select A && B, T, F) -> (select A, (select B, T, F), F)
41174158
// (select A || B, T, F) -> (select A, T, (select B, T, F))
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2+
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
3+
4+
define i1 @icmp_equality_test(i64 %X, i64 %Y, i64 %Z) {
5+
; CHECK-LABEL: @icmp_equality_test(
6+
; CHECK-NEXT: entry:
7+
; CHECK-NEXT: [[XEQY:%.*]] = icmp eq i64 [[X:%.*]], [[Y:%.*]]
8+
; CHECK-NEXT: ret i1 [[XEQY]]
9+
;
10+
entry:
11+
%XeqZ = icmp eq i64 %X, %Z
12+
%YeqZ = icmp eq i64 %Y, %Z
13+
%XeqY = icmp eq i64 %X, %Y
14+
%not.YeqZ = xor i1 %YeqZ, true
15+
%and = select i1 %not.YeqZ, i1 %XeqY, i1 false
16+
%equal = select i1 %XeqZ, i1 %YeqZ, i1 %and
17+
ret i1 %equal
18+
}
19+
20+
define i1 @icmp_equality_test_constant(i42 %X, i42 %Y) {
21+
; CHECK-LABEL: @icmp_equality_test_constant(
22+
; CHECK-NEXT: entry:
23+
; CHECK-NEXT: [[XEQY:%.*]] = icmp eq i42 [[X:%.*]], [[Y:%.*]]
24+
; CHECK-NEXT: ret i1 [[XEQY]]
25+
;
26+
entry:
27+
%XeqC = icmp eq i42 %X, -42
28+
%YeqC = icmp eq i42 %Y, -42
29+
%XeqY = icmp eq i42 %X, %Y
30+
%not.YeqC = xor i1 %YeqC, true
31+
%and = select i1 %not.YeqC, i1 %XeqY, i1 false
32+
%equal = select i1 %XeqC, i1 %YeqC, i1 %and
33+
ret i1 %equal
34+
}
35+
36+
define i1 @icmp_equality_test_swift_optional_pointers(i64 %X, i64 %Y) {
37+
; CHECK-LABEL: @icmp_equality_test_swift_optional_pointers(
38+
; CHECK-NEXT: entry:
39+
; CHECK-NEXT: [[XEQY:%.*]] = icmp eq i64 [[X:%.*]], [[Y:%.*]]
40+
; CHECK-NEXT: ret i1 [[XEQY]]
41+
;
42+
entry:
43+
%XeqC = icmp eq i64 %X, 0
44+
%YeqC = icmp eq i64 %Y, 0
45+
%either = select i1 %XeqC, i1 true, i1 %YeqC
46+
%both = select i1 %XeqC, i1 %YeqC, i1 false
47+
%XeqY = icmp eq i64 %X, %Y
48+
%equal = select i1 %either, i1 %both, i1 %XeqY
49+
ret i1 %equal
50+
}
51+
52+
define <2 x i1> @icmp_equality_test_vector(<2 x i64> %X, <2 x i64> %Y) {
53+
; CHECK-LABEL: @icmp_equality_test_vector(
54+
; CHECK-NEXT: entry:
55+
; CHECK-NEXT: [[XEQY:%.*]] = icmp eq <2 x i64> [[X:%.*]], [[Y:%.*]]
56+
; CHECK-NEXT: ret <2 x i1> [[XEQY]]
57+
;
58+
entry:
59+
%XeqC = icmp eq <2 x i64> %X, <i64 123, i64 456>
60+
%YeqC = icmp eq <2 x i64> %Y, <i64 123, i64 456>
61+
%XeqY = icmp eq <2 x i64> %X, %Y
62+
%not.YeqC = xor <2 x i1> %YeqC, <i1 true, i1 true>
63+
%and = select <2 x i1> %not.YeqC, <2 x i1> %XeqY, <2 x i1> <i1 false, i1 false>
64+
%equal = select <2 x i1> %XeqC, <2 x i1> %YeqC, <2 x i1> %and
65+
ret <2 x i1> %equal
66+
}
67+
68+
define i1 @icmp_equality_test_commute_icmp1(i64 %X, i64 %Y, i64 %Z) {
69+
; CHECK-LABEL: @icmp_equality_test_commute_icmp1(
70+
; CHECK-NEXT: entry:
71+
; CHECK-NEXT: [[XEQY:%.*]] = icmp eq i64 [[Y:%.*]], [[X:%.*]]
72+
; CHECK-NEXT: ret i1 [[XEQY]]
73+
;
74+
entry:
75+
%XeqZ = icmp eq i64 %Z, %X
76+
%YeqZ = icmp eq i64 %Z, %Y
77+
%XeqY = icmp eq i64 %Y, %X
78+
%not.YeqZ = xor i1 %YeqZ, true
79+
%and = select i1 %not.YeqZ, i1 %XeqY, i1 false
80+
%equal = select i1 %XeqZ, i1 %YeqZ, i1 %and
81+
ret i1 %equal
82+
}
83+
84+
define i1 @icmp_equality_test_commute_icmp2(i64 %X, i64 %Y, i64 %Z) {
85+
; CHECK-LABEL: @icmp_equality_test_commute_icmp2(
86+
; CHECK-NEXT: [[XEQY:%.*]] = icmp eq i64 [[Y:%.*]], [[X:%.*]]
87+
; CHECK-NEXT: ret i1 [[XEQY]]
88+
;
89+
%XeqZ = icmp eq i64 %Z, %X
90+
%YeqZ = icmp eq i64 %Y, %Z
91+
%XeqY = icmp eq i64 %Y, %X
92+
%not.YeqZ = xor i1 %YeqZ, true
93+
%and = select i1 %not.YeqZ, i1 %XeqY, i1 false
94+
%equal = select i1 %XeqZ, i1 %YeqZ, i1 %and
95+
ret i1 %equal
96+
}
97+
98+
define i1 @icmp_equality_test_commute_select1(i64 %X, i64 %Y, i64 %Z) {
99+
; CHECK-LABEL: @icmp_equality_test_commute_select1(
100+
; CHECK-NEXT: entry:
101+
; CHECK-NEXT: [[XEQY:%.*]] = icmp eq i64 [[X:%.*]], [[Y:%.*]]
102+
; CHECK-NEXT: ret i1 [[XEQY]]
103+
;
104+
entry:
105+
%XeqZ = icmp eq i64 %X, %Z
106+
%YeqZ = icmp eq i64 %Y, %Z
107+
%XeqY = icmp eq i64 %X, %Y
108+
%and = select i1 %YeqZ, i1 false, i1 %XeqY
109+
%equal = select i1 %XeqZ, i1 %YeqZ, i1 %and
110+
ret i1 %equal
111+
}
112+
113+
define i1 @icmp_equality_test_commute_select2(i64 %X, i64 %Y, i64 %Z) {
114+
; CHECK-LABEL: @icmp_equality_test_commute_select2(
115+
; CHECK-NEXT: entry:
116+
; CHECK-NEXT: [[XEQY:%.*]] = icmp eq i64 [[X:%.*]], [[Y:%.*]]
117+
; CHECK-NEXT: ret i1 [[XEQY]]
118+
;
119+
entry:
120+
%XeqZ = icmp eq i64 %X, %Z
121+
%YeqZ = icmp eq i64 %Y, %Z
122+
%XeqY = icmp eq i64 %X, %Y
123+
%not.XeqZ = xor i1 %XeqZ, true
124+
%and = select i1 %YeqZ, i1 false, i1 %XeqY
125+
%equal = select i1 %not.XeqZ, i1 %and, i1 %YeqZ
126+
ret i1 %equal
127+
}
128+
129+
; Negative tests below
130+
131+
define i1 @icmp_equality_test_wrong_constant(i64 %X, i64 %Y) {
132+
; CHECK-LABEL: @icmp_equality_test_wrong_constant(
133+
; CHECK-NEXT: entry:
134+
; CHECK-NEXT: [[XEQC:%.*]] = icmp eq i64 [[X:%.*]], 0
135+
; CHECK-NEXT: [[YEQC:%.*]] = icmp eq i64 [[Y:%.*]], 999
136+
; CHECK-NEXT: [[XEQY:%.*]] = icmp eq i64 [[X]], [[Y]]
137+
; CHECK-NEXT: [[NOT_YEQC:%.*]] = xor i1 [[YEQC]], true
138+
; CHECK-NEXT: [[AND:%.*]] = select i1 [[NOT_YEQC]], i1 [[XEQY]], i1 false
139+
; CHECK-NEXT: [[EQUAL:%.*]] = select i1 [[XEQC]], i1 [[YEQC]], i1 [[AND]]
140+
; CHECK-NEXT: ret i1 [[EQUAL]]
141+
;
142+
entry:
143+
%XeqC = icmp eq i64 %X, 0
144+
%YeqC = icmp eq i64 %Y, 999
145+
%XeqY = icmp eq i64 %X, %Y
146+
%not.YeqC = xor i1 %YeqC, true
147+
%and = select i1 %not.YeqC, i1 %XeqY, i1 false
148+
%equal = select i1 %XeqC, i1 %YeqC, i1 %and
149+
ret i1 %equal
150+
}
151+
152+
define i1 @icmp_equality_test_missing_not(i64 %X, i64 %Y, i64 %Z) {
153+
; CHECK-LABEL: @icmp_equality_test_missing_not(
154+
; CHECK-NEXT: [[XEQZ:%.*]] = icmp eq i64 [[X:%.*]], [[Z:%.*]]
155+
; CHECK-NEXT: [[YEQZ:%.*]] = icmp eq i64 [[Y:%.*]], [[Z]]
156+
; CHECK-NEXT: [[XEQY:%.*]] = icmp eq i64 [[X]], [[Y]]
157+
; CHECK-NEXT: [[AND:%.*]] = select i1 [[YEQZ]], i1 [[XEQY]], i1 false
158+
; CHECK-NEXT: [[EQUAL:%.*]] = select i1 [[XEQZ]], i1 [[YEQZ]], i1 [[AND]]
159+
; CHECK-NEXT: ret i1 [[EQUAL]]
160+
;
161+
%XeqZ = icmp eq i64 %X, %Z
162+
%YeqZ = icmp eq i64 %Y, %Z
163+
%XeqY = icmp eq i64 %X, %Y
164+
%and = select i1 %YeqZ, i1 %XeqY, i1 false
165+
%equal = select i1 %XeqZ, i1 %YeqZ, i1 %and
166+
ret i1 %equal
167+
}
168+
169+
define i1 @icmp_equality_test_wrong_and(i64 %X, i64 %Y, i64 %Z) {
170+
; CHECK-LABEL: @icmp_equality_test_wrong_and(
171+
; CHECK-NEXT: entry:
172+
; CHECK-NEXT: [[XEQZ:%.*]] = icmp eq i64 [[X:%.*]], [[Z:%.*]]
173+
; CHECK-NEXT: [[YEQZ:%.*]] = icmp eq i64 [[Y:%.*]], [[Z]]
174+
; CHECK-NEXT: [[XEQY:%.*]] = icmp eq i64 [[X]], [[Y]]
175+
; CHECK-NEXT: [[AND:%.*]] = select i1 [[YEQZ]], i1 [[XEQY]], i1 false
176+
; CHECK-NEXT: [[EQUAL:%.*]] = select i1 [[XEQZ]], i1 [[YEQZ]], i1 [[AND]]
177+
; CHECK-NEXT: ret i1 [[EQUAL]]
178+
;
179+
entry:
180+
%XeqZ = icmp eq i64 %X, %Z
181+
%YeqZ = icmp eq i64 %Y, %Z
182+
%XeqY = icmp eq i64 %X, %Y
183+
%not.YeqZ = xor i1 %YeqZ, true
184+
%and = select i1 %not.YeqZ, i1 false, i1 %XeqY
185+
%equal = select i1 %XeqZ, i1 %YeqZ, i1 %and
186+
ret i1 %equal
187+
}
188+
189+
define i1 @icmp_equality_test_wrong_cmp(i64 %X, i64 %Y, i64 %Z) {
190+
; CHECK-LABEL: @icmp_equality_test_wrong_cmp(
191+
; CHECK-NEXT: entry:
192+
; CHECK-NEXT: [[XEQZ:%.*]] = icmp eq i64 [[X:%.*]], [[Z:%.*]]
193+
; CHECK-NEXT: [[YEQZ:%.*]] = icmp eq i64 [[Y:%.*]], [[Z]]
194+
; CHECK-NEXT: [[XEQY:%.*]] = icmp eq i64 [[X]], 999
195+
; CHECK-NEXT: [[NOT_YEQZ:%.*]] = xor i1 [[YEQZ]], true
196+
; CHECK-NEXT: [[AND:%.*]] = select i1 [[NOT_YEQZ]], i1 [[XEQY]], i1 false
197+
; CHECK-NEXT: [[EQUAL:%.*]] = select i1 [[XEQZ]], i1 [[YEQZ]], i1 [[AND]]
198+
; CHECK-NEXT: ret i1 [[EQUAL]]
199+
;
200+
entry:
201+
%XeqZ = icmp eq i64 %X, %Z
202+
%YeqZ = icmp eq i64 %Y, %Z
203+
%XeqY = icmp eq i64 %X, 999
204+
%not.YeqZ = xor i1 %YeqZ, true
205+
%and = select i1 %not.YeqZ, i1 %XeqY, i1 false
206+
%equal = select i1 %XeqZ, i1 %YeqZ, i1 %and
207+
ret i1 %equal
208+
}
209+
210+
define i1 @icmp_equality_test_wrong_equal(i64 %X, i64 %Y, i64 %Z) {
211+
; CHECK-LABEL: @icmp_equality_test_wrong_equal(
212+
; CHECK-NEXT: entry:
213+
; CHECK-NEXT: [[XEQZ:%.*]] = icmp eq i64 [[X:%.*]], [[Z:%.*]]
214+
; CHECK-NEXT: [[YEQZ:%.*]] = icmp eq i64 [[Y:%.*]], [[Z]]
215+
; CHECK-NEXT: [[XEQY:%.*]] = icmp eq i64 [[X]], [[Y]]
216+
; CHECK-NEXT: [[NOT_YEQZ:%.*]] = xor i1 [[YEQZ]], true
217+
; CHECK-NEXT: [[AND:%.*]] = select i1 [[NOT_YEQZ]], i1 [[XEQY]], i1 false
218+
; CHECK-NEXT: [[EQUAL:%.*]] = select i1 [[XEQZ]], i1 [[AND]], i1 [[YEQZ]]
219+
; CHECK-NEXT: ret i1 [[EQUAL]]
220+
;
221+
entry:
222+
%XeqZ = icmp eq i64 %X, %Z
223+
%YeqZ = icmp eq i64 %Y, %Z
224+
%XeqY = icmp eq i64 %X, %Y
225+
%not.YeqZ = xor i1 %YeqZ, true
226+
%and = select i1 %not.YeqZ, i1 %XeqY, i1 false
227+
%equal = select i1 %XeqZ, i1 %and, i1 %YeqZ
228+
ret i1 %equal
229+
}

0 commit comments

Comments
 (0)