Skip to content

Commit 0f02431

Browse files
Kai Luodtcxzyw
andauthored
[InstCombine] Fold (sub (xor X, (sext C)), (sext C)) => (select C (neg X), X) (#79417)
This is useful when computing absdiff. Correctness prove: https://alive2.llvm.org/ce/z/eMbxps, https://alive2.llvm.org/ce/z/SNCWJe. --------- Co-authored-by: Yingwei Zheng <[email protected]>
1 parent c67a4ae commit 0f02431

File tree

2 files changed

+220
-0
lines changed

2 files changed

+220
-0
lines changed

llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2448,6 +2448,21 @@ Instruction *InstCombinerImpl::visitSub(BinaryOperator &I) {
24482448
}
24492449
}
24502450

2451+
{
2452+
// (sub (xor X, (sext C)), (sext C)) => (select C, (neg X), X)
2453+
// (sub (sext C), (xor X, (sext C))) => (select C, X, (neg X))
2454+
Value *C, *X;
2455+
auto m_SubXorCmp = [&C, &X](Value *LHS, Value *RHS) {
2456+
return match(LHS, m_OneUse(m_c_Xor(m_Value(X), m_Specific(RHS)))) &&
2457+
match(RHS, m_SExt(m_Value(C))) &&
2458+
(C->getType()->getScalarSizeInBits() == 1);
2459+
};
2460+
if (m_SubXorCmp(Op0, Op1))
2461+
return SelectInst::Create(C, Builder.CreateNeg(X), X);
2462+
if (m_SubXorCmp(Op1, Op0))
2463+
return SelectInst::Create(C, X, Builder.CreateNeg(X));
2464+
}
2465+
24512466
if (Instruction *R = tryFoldInstWithCtpopWithNot(&I))
24522467
return R;
24532468

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
2+
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
3+
4+
define i64 @sext_xor_sub(i64 %a, i1 %b) {
5+
; CHECK-LABEL: define i64 @sext_xor_sub(
6+
; CHECK-SAME: i64 [[A:%.*]], i1 [[B:%.*]]) {
7+
; CHECK-NEXT: [[TMP1:%.*]] = sub i64 0, [[A]]
8+
; CHECK-NEXT: [[R:%.*]] = select i1 [[B]], i64 [[TMP1]], i64 [[A]]
9+
; CHECK-NEXT: ret i64 [[R]]
10+
;
11+
%c = sext i1 %b to i64
12+
%d = xor i64 %a, %c
13+
%r = sub i64 %d, %c
14+
ret i64 %r
15+
}
16+
17+
define i64 @sext_xor_sub_1(i64 %a, i1 %b) {
18+
; CHECK-LABEL: define i64 @sext_xor_sub_1(
19+
; CHECK-SAME: i64 [[A:%.*]], i1 [[B:%.*]]) {
20+
; CHECK-NEXT: [[TMP1:%.*]] = sub i64 0, [[A]]
21+
; CHECK-NEXT: [[R:%.*]] = select i1 [[B]], i64 [[TMP1]], i64 [[A]]
22+
; CHECK-NEXT: ret i64 [[R]]
23+
;
24+
%c = sext i1 %b to i64
25+
%d = xor i64 %c, %a
26+
%r = sub i64 %d, %c
27+
ret i64 %r
28+
}
29+
30+
define i64 @sext_xor_sub_2(i64 %a, i1 %b) {
31+
; CHECK-LABEL: define i64 @sext_xor_sub_2(
32+
; CHECK-SAME: i64 [[A:%.*]], i1 [[B:%.*]]) {
33+
; CHECK-NEXT: [[TMP1:%.*]] = sub i64 0, [[A]]
34+
; CHECK-NEXT: [[R:%.*]] = select i1 [[B]], i64 [[A]], i64 [[TMP1]]
35+
; CHECK-NEXT: ret i64 [[R]]
36+
;
37+
%c = sext i1 %b to i64
38+
%d = xor i64 %a, %c
39+
%r = sub i64 %c, %d
40+
ret i64 %r
41+
}
42+
43+
define i64 @sext_xor_sub_3(i64 %a, i1 %b) {
44+
; CHECK-LABEL: define i64 @sext_xor_sub_3(
45+
; CHECK-SAME: i64 [[A:%.*]], i1 [[B:%.*]]) {
46+
; CHECK-NEXT: [[TMP1:%.*]] = sub i64 0, [[A]]
47+
; CHECK-NEXT: [[R:%.*]] = select i1 [[B]], i64 [[A]], i64 [[TMP1]]
48+
; CHECK-NEXT: ret i64 [[R]]
49+
;
50+
%c = sext i1 %b to i64
51+
%d = xor i64 %c, %a
52+
%r = sub i64 %c, %d
53+
ret i64 %r
54+
}
55+
56+
; Sext non boolean type.
57+
define i64 @sext_non_bool_xor_sub(i64 %a, i8 %b) {
58+
; CHECK-LABEL: define i64 @sext_non_bool_xor_sub(
59+
; CHECK-SAME: i64 [[A:%.*]], i8 [[B:%.*]]) {
60+
; CHECK-NEXT: [[C:%.*]] = sext i8 [[B]] to i64
61+
; CHECK-NEXT: [[D:%.*]] = xor i64 [[C]], [[A]]
62+
; CHECK-NEXT: [[R:%.*]] = sub i64 [[D]], [[C]]
63+
; CHECK-NEXT: ret i64 [[R]]
64+
;
65+
%c = sext i8 %b to i64
66+
%d = xor i64 %a, %c
67+
%r = sub i64 %d, %c
68+
ret i64 %r
69+
}
70+
71+
define i64 @sext_non_bool_xor_sub_1(i64 %a, i8 %b) {
72+
; CHECK-LABEL: define i64 @sext_non_bool_xor_sub_1(
73+
; CHECK-SAME: i64 [[A:%.*]], i8 [[B:%.*]]) {
74+
; CHECK-NEXT: [[C:%.*]] = sext i8 [[B]] to i64
75+
; CHECK-NEXT: [[D:%.*]] = xor i64 [[C]], [[A]]
76+
; CHECK-NEXT: [[R:%.*]] = sub i64 [[D]], [[C]]
77+
; CHECK-NEXT: ret i64 [[R]]
78+
;
79+
%c = sext i8 %b to i64
80+
%d = xor i64 %c, %a
81+
%r = sub i64 %d, %c
82+
ret i64 %r
83+
}
84+
85+
; Different boolean values.
86+
define i64 @sext_diff_i1_xor_sub(i64 %a, i1 %b, i1 %c) {
87+
; CHECK-LABEL: define i64 @sext_diff_i1_xor_sub(
88+
; CHECK-SAME: i64 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
89+
; CHECK-NEXT: [[D:%.*]] = sext i1 [[B]] to i64
90+
; CHECK-NEXT: [[E_NEG:%.*]] = zext i1 [[C]] to i64
91+
; CHECK-NEXT: [[R:%.*]] = add nsw i64 [[E_NEG]], [[D]]
92+
; CHECK-NEXT: ret i64 [[R]]
93+
;
94+
%d = sext i1 %b to i64
95+
%e = sext i1 %c to i64
96+
%f = xor i64 %a, %d
97+
%r = sub i64 %d, %e
98+
ret i64 %r
99+
}
100+
101+
define i64 @sext_diff_i1_xor_sub_1(i64 %a, i1 %b, i1 %c) {
102+
; CHECK-LABEL: define i64 @sext_diff_i1_xor_sub_1(
103+
; CHECK-SAME: i64 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
104+
; CHECK-NEXT: [[D:%.*]] = sext i1 [[B]] to i64
105+
; CHECK-NEXT: [[E_NEG:%.*]] = zext i1 [[C]] to i64
106+
; CHECK-NEXT: [[R:%.*]] = add nsw i64 [[E_NEG]], [[D]]
107+
; CHECK-NEXT: ret i64 [[R]]
108+
;
109+
%d = sext i1 %b to i64
110+
%e = sext i1 %c to i64
111+
%f = xor i64 %d, %a
112+
%r = sub i64 %d, %e
113+
ret i64 %r
114+
}
115+
116+
; (sext C) has multiple uses.
117+
define i64 @sext_multi_uses(i64 %a, i1 %b, i64 %x) {
118+
; CHECK-LABEL: define i64 @sext_multi_uses(
119+
; CHECK-SAME: i64 [[A:%.*]], i1 [[B:%.*]], i64 [[X:%.*]]) {
120+
; CHECK-NEXT: [[C:%.*]] = sext i1 [[B]] to i64
121+
; CHECK-NEXT: [[TMP1:%.*]] = sub i64 0, [[A]]
122+
; CHECK-NEXT: [[E:%.*]] = select i1 [[B]], i64 [[TMP1]], i64 [[A]]
123+
; CHECK-NEXT: [[F:%.*]] = mul i64 [[C]], [[X]]
124+
; CHECK-NEXT: [[R:%.*]] = add i64 [[F]], [[E]]
125+
; CHECK-NEXT: ret i64 [[R]]
126+
;
127+
%c = sext i1 %b to i64
128+
%d = xor i64 %a, %c
129+
%e = sub i64 %d, %c
130+
%f = mul i64 %x, %c
131+
%r = add i64 %f, %e
132+
ret i64 %r
133+
}
134+
135+
; (xor X, (sext C)) has multiple uses.
136+
define i64 @xor_multi_uses(i64 %a, i1 %b, i64 %x) {
137+
; CHECK-LABEL: define i64 @xor_multi_uses(
138+
; CHECK-SAME: i64 [[A:%.*]], i1 [[B:%.*]], i64 [[X:%.*]]) {
139+
; CHECK-NEXT: [[C:%.*]] = sext i1 [[B]] to i64
140+
; CHECK-NEXT: [[D:%.*]] = xor i64 [[C]], [[A]]
141+
; CHECK-NEXT: [[E:%.*]] = sub i64 [[D]], [[C]]
142+
; CHECK-NEXT: [[F:%.*]] = mul i64 [[D]], [[X]]
143+
; CHECK-NEXT: [[R:%.*]] = add i64 [[F]], [[E]]
144+
; CHECK-NEXT: ret i64 [[R]]
145+
;
146+
%c = sext i1 %b to i64
147+
%d = xor i64 %a, %c
148+
%e = sub i64 %d, %c
149+
%f = mul i64 %x, %d
150+
%r = add i64 %f, %e
151+
ret i64 %r
152+
}
153+
154+
define i64 @absdiff(i64 %a, i64 %b) {
155+
; CHECK-LABEL: define i64 @absdiff(
156+
; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
157+
; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[A]], [[B]]
158+
; CHECK-NEXT: [[D:%.*]] = sub i64 [[A]], [[B]]
159+
; CHECK-NEXT: [[TMP1:%.*]] = sub i64 0, [[D]]
160+
; CHECK-NEXT: [[RES:%.*]] = select i1 [[C]], i64 [[TMP1]], i64 [[D]]
161+
; CHECK-NEXT: ret i64 [[RES]]
162+
;
163+
%c = icmp ult i64 %a, %b
164+
%c.ext = sext i1 %c to i64
165+
%d = sub i64 %a, %b
166+
%may.rev = xor i64 %c.ext, %d
167+
%res = sub i64 %may.rev, %c.ext
168+
ret i64 %res
169+
}
170+
171+
; Commuted xor operands compared to absdiff.
172+
define i64 @absdiff1(i64 %a, i64 %b) {
173+
; CHECK-LABEL: define i64 @absdiff1(
174+
; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
175+
; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[A]], [[B]]
176+
; CHECK-NEXT: [[D:%.*]] = sub i64 [[A]], [[B]]
177+
; CHECK-NEXT: [[TMP1:%.*]] = sub i64 0, [[D]]
178+
; CHECK-NEXT: [[RES:%.*]] = select i1 [[C]], i64 [[TMP1]], i64 [[D]]
179+
; CHECK-NEXT: ret i64 [[RES]]
180+
;
181+
%c = icmp ult i64 %a, %b
182+
%c.ext = sext i1 %c to i64
183+
%d = sub i64 %a, %b
184+
%may.rev = xor i64 %d, %c.ext
185+
%res = sub i64 %may.rev, %c.ext
186+
ret i64 %res
187+
}
188+
189+
; Use ugt as compare cond.
190+
define i64 @absdiff2(i64 %a, i64 %b) {
191+
; CHECK-LABEL: define i64 @absdiff2(
192+
; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
193+
; CHECK-NEXT: [[C:%.*]] = icmp ugt i64 [[A]], [[B]]
194+
; CHECK-NEXT: [[D:%.*]] = sub i64 [[B]], [[A]]
195+
; CHECK-NEXT: [[TMP1:%.*]] = sub i64 0, [[D]]
196+
; CHECK-NEXT: [[RES:%.*]] = select i1 [[C]], i64 [[TMP1]], i64 [[D]]
197+
; CHECK-NEXT: ret i64 [[RES]]
198+
;
199+
%c = icmp ugt i64 %a, %b
200+
%c.ext = sext i1 %c to i64
201+
%d = sub i64 %b, %a
202+
%may.rev = xor i64 %d, %c.ext
203+
%res = sub i64 %may.rev, %c.ext
204+
ret i64 %res
205+
}

0 commit comments

Comments
 (0)