Skip to content

Commit 9c7e02d

Browse files
authored
[InstCombine] Fold umax(nuw_mul(x, C0), x + 1) into (x == 0 ? 1 : nuw_mul(x, C0)) (#123468)
This PR introduces the following transformations: - If C0 is not 0: umax(nuw_shl(x, C0), x + 1) -> x == 0 ? 1 : nuw_shl(x, C0) - If C0 is not 0 or 1: umax(nuw_mul(x, C0), x + 1) -> x == 0 ? 1 : nuw_mul(x, C0) Fixes #122388. Alive2 proof: https://alive2.llvm.org/ce/z/rkp_8U
1 parent 8552c49 commit 9c7e02d

File tree

2 files changed

+380
-0
lines changed

2 files changed

+380
-0
lines changed

llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1860,6 +1860,33 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
18601860
return CastInst::Create(Instruction::ZExt, NarrowMaxMin, II->getType());
18611861
}
18621862
}
1863+
// If C is not 0:
1864+
// umax(nuw_shl(x, C), x + 1) -> x == 0 ? 1 : nuw_shl(x, C)
1865+
// If C is not 0 or 1:
1866+
// umax(nuw_mul(x, C), x + 1) -> x == 0 ? 1 : nuw_mul(x, C)
1867+
auto foldMaxMulShift = [&](Value *A, Value *B) -> Instruction * {
1868+
const APInt *C;
1869+
Value *X;
1870+
if (!match(A, m_NUWShl(m_Value(X), m_APInt(C))) &&
1871+
!(match(A, m_NUWMul(m_Value(X), m_APInt(C))) && !C->isOne()))
1872+
return nullptr;
1873+
if (C->isZero())
1874+
return nullptr;
1875+
if (!match(B, m_OneUse(m_Add(m_Specific(X), m_One()))))
1876+
return nullptr;
1877+
1878+
Value *Cmp = Builder.CreateICmpEQ(X, ConstantInt::get(X->getType(), 0));
1879+
Value *NewSelect =
1880+
Builder.CreateSelect(Cmp, ConstantInt::get(X->getType(), 1), A);
1881+
return replaceInstUsesWith(*II, NewSelect);
1882+
};
1883+
1884+
if (IID == Intrinsic::umax) {
1885+
if (Instruction *I = foldMaxMulShift(I0, I1))
1886+
return I;
1887+
if (Instruction *I = foldMaxMulShift(I1, I0))
1888+
return I;
1889+
}
18631890
// If both operands of unsigned min/max are sign-extended, it is still ok
18641891
// to narrow the operation.
18651892
[[fallthrough]];
Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2+
3+
; RUN: opt -S -passes=instcombine < %s | FileCheck %s
4+
5+
; When C0 is neither 0 nor 1:
6+
; umax(nuw_mul(x, C0), x + 1) is optimized to:
7+
; x == 0 ? 1 : nuw_mul(x, C0)
8+
; When C0 is not 0:
9+
; umax(nuw_shl(x, C0), x + 1) is optimized to:
10+
; x == 0 ? 1 : nuw_shl(x, C0)
11+
12+
; Positive Test Cases for `shl`
13+
14+
define i64 @test_shl_by_2(i64 %x) {
15+
; CHECK-LABEL: define i64 @test_shl_by_2(
16+
; CHECK-SAME: i64 [[X:%.*]]) {
17+
; CHECK-NEXT: [[TMP2:%.*]] = shl nuw i64 [[X]], 2
18+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[X]], 0
19+
; CHECK-NEXT: [[MAX:%.*]] = select i1 [[TMP1]], i64 1, i64 [[TMP2]]
20+
; CHECK-NEXT: ret i64 [[MAX]]
21+
;
22+
%x1 = add i64 %x, 1
23+
%shl = shl nuw i64 %x, 2
24+
%max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
25+
ret i64 %max
26+
}
27+
28+
define i64 @test_shl_by_5(i64 %x) {
29+
; CHECK-LABEL: define i64 @test_shl_by_5(
30+
; CHECK-SAME: i64 [[X:%.*]]) {
31+
; CHECK-NEXT: [[TMP2:%.*]] = shl nuw i64 [[X]], 5
32+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[X]], 0
33+
; CHECK-NEXT: [[MAX:%.*]] = select i1 [[TMP1]], i64 1, i64 [[TMP2]]
34+
; CHECK-NEXT: ret i64 [[MAX]]
35+
;
36+
%x1 = add i64 %x, 1
37+
%shl = shl nuw i64 %x, 5
38+
%max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
39+
ret i64 %max
40+
}
41+
42+
define i64 @test_shl_with_nsw(i64 %x) {
43+
; CHECK-LABEL: define i64 @test_shl_with_nsw(
44+
; CHECK-SAME: i64 [[X:%.*]]) {
45+
; CHECK-NEXT: [[SHL:%.*]] = shl nuw nsw i64 [[X]], 2
46+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[X]], 0
47+
; CHECK-NEXT: [[MAX:%.*]] = select i1 [[TMP1]], i64 1, i64 [[SHL]]
48+
; CHECK-NEXT: ret i64 [[MAX]]
49+
;
50+
%x1 = add i64 %x, 1
51+
%shl = shl nuw nsw i64 %x, 2
52+
%max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
53+
ret i64 %max
54+
}
55+
56+
define <2 x i64> @test_shl_vector_by_2(<2 x i64> %x) {
57+
; CHECK-LABEL: define <2 x i64> @test_shl_vector_by_2(
58+
; CHECK-SAME: <2 x i64> [[X:%.*]]) {
59+
; CHECK-NEXT: [[SHL:%.*]] = shl nuw <2 x i64> [[X]], splat (i64 2)
60+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <2 x i64> [[X]], zeroinitializer
61+
; CHECK-NEXT: [[MAX:%.*]] = select <2 x i1> [[TMP1]], <2 x i64> splat (i64 1), <2 x i64> [[SHL]]
62+
; CHECK-NEXT: ret <2 x i64> [[MAX]]
63+
;
64+
%x1 = add <2 x i64> %x, <i64 1, i64 1>
65+
%shl = shl nuw <2 x i64> %x, <i64 2, i64 2>
66+
%max = call <2 x i64> @llvm.umax.v2i64(<2 x i64> %shl, <2 x i64> %x1)
67+
ret <2 x i64> %max
68+
}
69+
70+
; Commuted Test Cases for `shl`
71+
72+
define i64 @test_shl_umax_commuted(i64 %x) {
73+
; CHECK-LABEL: define i64 @test_shl_umax_commuted(
74+
; CHECK-SAME: i64 [[X:%.*]]) {
75+
; CHECK-NEXT: [[SHL:%.*]] = shl nuw i64 [[X]], 2
76+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[X]], 0
77+
; CHECK-NEXT: [[MAX:%.*]] = select i1 [[TMP1]], i64 1, i64 [[SHL]]
78+
; CHECK-NEXT: ret i64 [[MAX]]
79+
;
80+
%x1 = add i64 %x, 1
81+
%shl = shl nuw i64 %x, 2
82+
%max = call i64 @llvm.umax.i64(i64 %x1, i64 %shl)
83+
ret i64 %max
84+
}
85+
86+
; Negative Test Cases for `shl`
87+
88+
define i64 @test_shl_by_zero(i64 %x) {
89+
; CHECK-LABEL: define i64 @test_shl_by_zero(
90+
; CHECK-SAME: i64 [[X:%.*]]) {
91+
; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 1
92+
; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[X]], i64 [[X1]])
93+
; CHECK-NEXT: ret i64 [[MAX]]
94+
;
95+
%x1 = add i64 %x, 1
96+
%shl = shl nuw i64 %x, 0
97+
%max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
98+
ret i64 %max
99+
}
100+
101+
define i64 @test_shl_add_by_2(i64 %x) {
102+
; CHECK-LABEL: define i64 @test_shl_add_by_2(
103+
; CHECK-SAME: i64 [[X:%.*]]) {
104+
; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 2
105+
; CHECK-NEXT: [[SHL:%.*]] = shl nuw i64 [[X]], 2
106+
; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[SHL]], i64 [[X1]])
107+
; CHECK-NEXT: ret i64 [[MAX]]
108+
;
109+
%x1 = add i64 %x, 2
110+
%shl = shl nuw i64 %x, 2
111+
%max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
112+
ret i64 %max
113+
}
114+
115+
define i64 @test_shl_without_nuw(i64 %x) {
116+
; CHECK-LABEL: define i64 @test_shl_without_nuw(
117+
; CHECK-SAME: i64 [[X:%.*]]) {
118+
; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 1
119+
; CHECK-NEXT: [[SHL:%.*]] = shl i64 [[X]], 2
120+
; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[SHL]], i64 [[X1]])
121+
; CHECK-NEXT: ret i64 [[MAX]]
122+
;
123+
%x1 = add i64 %x, 1
124+
%shl = shl i64 %x, 2
125+
%max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
126+
ret i64 %max
127+
}
128+
129+
define i64 @test_shl_umin(i64 %x) {
130+
; CHECK-LABEL: define i64 @test_shl_umin(
131+
; CHECK-SAME: i64 [[X:%.*]]) {
132+
; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 1
133+
; CHECK-NEXT: [[SHL:%.*]] = shl nuw i64 [[X]], 2
134+
; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umin.i64(i64 [[SHL]], i64 [[X1]])
135+
; CHECK-NEXT: ret i64 [[MAX]]
136+
;
137+
%x1 = add i64 %x, 1
138+
%shl = shl nuw i64 %x, 2
139+
%max = call i64 @llvm.umin.i64(i64 %shl, i64 %x1)
140+
ret i64 %max
141+
}
142+
143+
; Multi-use Test Cases for `shl`
144+
declare void @use(i64)
145+
146+
define i64 @test_shl_multi_use_add(i64 %x) {
147+
; CHECK-LABEL: define i64 @test_shl_multi_use_add(
148+
; CHECK-SAME: i64 [[X:%.*]]) {
149+
; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 1
150+
; CHECK-NEXT: call void @use(i64 [[X1]])
151+
; CHECK-NEXT: [[TMP2:%.*]] = shl nuw i64 [[X]], 3
152+
; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[TMP2]], i64 [[X1]])
153+
; CHECK-NEXT: ret i64 [[MAX]]
154+
;
155+
%x1 = add i64 %x, 1
156+
call void @use(i64 %x1)
157+
%shl = shl nuw i64 %x, 3
158+
%max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
159+
ret i64 %max
160+
}
161+
162+
define i64 @test_shl_multi_use_shl(i64 %x) {
163+
; CHECK-LABEL: define i64 @test_shl_multi_use_shl(
164+
; CHECK-SAME: i64 [[X:%.*]]) {
165+
; CHECK-NEXT: [[SHL:%.*]] = shl nuw i64 [[X]], 2
166+
; CHECK-NEXT: call void @use(i64 [[SHL]])
167+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[X]], 0
168+
; CHECK-NEXT: [[MAX:%.*]] = select i1 [[TMP1]], i64 1, i64 [[SHL]]
169+
; CHECK-NEXT: ret i64 [[MAX]]
170+
;
171+
%x1 = add i64 %x, 1
172+
%shl = shl nuw i64 %x, 2
173+
call void @use(i64 %shl)
174+
%max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
175+
ret i64 %max
176+
}
177+
178+
; Positive Test Cases for `mul`
179+
180+
define i64 @test_mul_by_3(i64 %x) {
181+
; CHECK-LABEL: define i64 @test_mul_by_3(
182+
; CHECK-SAME: i64 [[X:%.*]]) {
183+
; CHECK-NEXT: [[MUL:%.*]] = mul nuw i64 [[X]], 3
184+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[X]], 0
185+
; CHECK-NEXT: [[MAX:%.*]] = select i1 [[TMP1]], i64 1, i64 [[MUL]]
186+
; CHECK-NEXT: ret i64 [[MAX]]
187+
;
188+
%x1 = add i64 %x, 1
189+
%mul = mul nuw i64 %x, 3
190+
%max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
191+
ret i64 %max
192+
}
193+
194+
define i64 @test_mul_by_5(i64 %x) {
195+
; CHECK-LABEL: define i64 @test_mul_by_5(
196+
; CHECK-SAME: i64 [[X:%.*]]) {
197+
; CHECK-NEXT: [[MUL:%.*]] = mul nuw i64 [[X]], 5
198+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[X]], 0
199+
; CHECK-NEXT: [[MAX:%.*]] = select i1 [[TMP1]], i64 1, i64 [[MUL]]
200+
; CHECK-NEXT: ret i64 [[MAX]]
201+
;
202+
%x1 = add i64 %x, 1
203+
%mul = mul nuw i64 %x, 5
204+
%max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
205+
ret i64 %max
206+
}
207+
208+
define i64 @test_mul_with_nsw(i64 %x) {
209+
; CHECK-LABEL: define i64 @test_mul_with_nsw(
210+
; CHECK-SAME: i64 [[X:%.*]]) {
211+
; CHECK-NEXT: [[MUL:%.*]] = mul nuw nsw i64 [[X]], 3
212+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[X]], 0
213+
; CHECK-NEXT: [[MAX:%.*]] = select i1 [[TMP1]], i64 1, i64 [[MUL]]
214+
; CHECK-NEXT: ret i64 [[MAX]]
215+
;
216+
%x1 = add i64 %x, 1
217+
%mul = mul nuw nsw i64 %x, 3
218+
%max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
219+
ret i64 %max
220+
}
221+
222+
define <2 x i64> @test_mul_vector_by_3(<2 x i64> %x) {
223+
; CHECK-LABEL: define <2 x i64> @test_mul_vector_by_3(
224+
; CHECK-SAME: <2 x i64> [[X:%.*]]) {
225+
; CHECK-NEXT: [[MUL:%.*]] = mul nuw <2 x i64> [[X]], splat (i64 3)
226+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <2 x i64> [[X]], zeroinitializer
227+
; CHECK-NEXT: [[MAX:%.*]] = select <2 x i1> [[TMP1]], <2 x i64> splat (i64 1), <2 x i64> [[MUL]]
228+
; CHECK-NEXT: ret <2 x i64> [[MAX]]
229+
;
230+
%x1 = add <2 x i64> %x, <i64 1, i64 1>
231+
%mul = mul nuw <2 x i64> %x, <i64 3, i64 3>
232+
%max = call <2 x i64> @llvm.umax.v2i64(<2 x i64> %mul, <2 x i64> %x1)
233+
ret <2 x i64> %max
234+
}
235+
236+
; Commuted Test Cases for `mul`
237+
238+
define i64 @test_mul_max_commuted(i64 %x) {
239+
; CHECK-LABEL: define i64 @test_mul_max_commuted(
240+
; CHECK-SAME: i64 [[X:%.*]]) {
241+
; CHECK-NEXT: [[MUL:%.*]] = mul nuw i64 [[X]], 3
242+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[X]], 0
243+
; CHECK-NEXT: [[MAX:%.*]] = select i1 [[TMP1]], i64 1, i64 [[MUL]]
244+
; CHECK-NEXT: ret i64 [[MAX]]
245+
;
246+
%x1 = add i64 %x, 1
247+
%mul = mul nuw i64 %x, 3
248+
%max = call i64 @llvm.umax.i64(i64 %x1, i64 %mul)
249+
ret i64 %max
250+
}
251+
252+
; Negative Test Cases for `mul`
253+
254+
define i64 @test_mul_by_zero(i64 %x) {
255+
; CHECK-LABEL: define i64 @test_mul_by_zero(
256+
; CHECK-SAME: i64 [[X:%.*]]) {
257+
; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 1
258+
; CHECK-NEXT: ret i64 [[X1]]
259+
;
260+
%x1 = add i64 %x, 1
261+
%mul = mul nuw i64 %x, 0
262+
%max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
263+
ret i64 %max
264+
}
265+
266+
define i64 @test_mul_by_1(i64 %x) {
267+
; CHECK-LABEL: define i64 @test_mul_by_1(
268+
; CHECK-SAME: i64 [[X:%.*]]) {
269+
; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 1
270+
; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[X]], i64 [[X1]])
271+
; CHECK-NEXT: ret i64 [[MAX]]
272+
;
273+
%x1 = add i64 %x, 1
274+
%mul = mul nuw i64 %x, 1
275+
%max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
276+
ret i64 %max
277+
}
278+
279+
define i64 @test_mul_add_by_2(i64 %x) {
280+
; CHECK-LABEL: define i64 @test_mul_add_by_2(
281+
; CHECK-SAME: i64 [[X:%.*]]) {
282+
; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 2
283+
; CHECK-NEXT: [[MUL:%.*]] = mul nuw i64 [[X]], 3
284+
; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[MUL]], i64 [[X1]])
285+
; CHECK-NEXT: ret i64 [[MAX]]
286+
;
287+
%x1 = add i64 %x, 2
288+
%mul = mul nuw i64 %x, 3
289+
%max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
290+
ret i64 %max
291+
}
292+
293+
define i64 @test_mul_without_nuw(i64 %x) {
294+
; CHECK-LABEL: define i64 @test_mul_without_nuw(
295+
; CHECK-SAME: i64 [[X:%.*]]) {
296+
; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 1
297+
; CHECK-NEXT: [[MUL:%.*]] = mul i64 [[X]], 3
298+
; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[MUL]], i64 [[X1]])
299+
; CHECK-NEXT: ret i64 [[MAX]]
300+
;
301+
%x1 = add i64 %x, 1
302+
%mul = mul i64 %x, 3
303+
%max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
304+
ret i64 %max
305+
}
306+
307+
define i64 @test_mul_umin(i64 %x) {
308+
; CHECK-LABEL: define i64 @test_mul_umin(
309+
; CHECK-SAME: i64 [[X:%.*]]) {
310+
; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 1
311+
; CHECK-NEXT: [[MUL:%.*]] = mul nuw i64 [[X]], 3
312+
; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umin.i64(i64 [[MUL]], i64 [[X1]])
313+
; CHECK-NEXT: ret i64 [[MAX]]
314+
;
315+
%x1 = add i64 %x, 1
316+
%mul = mul nuw i64 %x, 3
317+
%max = call i64 @llvm.umin.i64(i64 %mul, i64 %x1)
318+
ret i64 %max
319+
}
320+
321+
; Multi-use Test Cases for `mul`
322+
323+
define i64 @test_mul_multi_use_add(i64 %x) {
324+
; CHECK-LABEL: define i64 @test_mul_multi_use_add(
325+
; CHECK-SAME: i64 [[X:%.*]]) {
326+
; CHECK-NEXT: [[X1:%.*]] = add i64 [[X]], 1
327+
; CHECK-NEXT: call void @use(i64 [[X1]])
328+
; CHECK-NEXT: [[TMP2:%.*]] = mul nuw i64 [[X]], 3
329+
; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[TMP2]], i64 [[X1]])
330+
; CHECK-NEXT: ret i64 [[MAX]]
331+
;
332+
%x1 = add i64 %x, 1
333+
call void @use(i64 %x1)
334+
%mul = mul nuw i64 %x, 3
335+
%max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
336+
ret i64 %max
337+
}
338+
339+
define i64 @test_mul_multi_use_mul(i64 %x) {
340+
; CHECK-LABEL: define i64 @test_mul_multi_use_mul(
341+
; CHECK-SAME: i64 [[X:%.*]]) {
342+
; CHECK-NEXT: [[MUL:%.*]] = mul nuw i64 [[X]], 3
343+
; CHECK-NEXT: call void @use(i64 [[MUL]])
344+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[X]], 0
345+
; CHECK-NEXT: [[MAX:%.*]] = select i1 [[TMP1]], i64 1, i64 [[MUL]]
346+
; CHECK-NEXT: ret i64 [[MAX]]
347+
;
348+
%x1 = add i64 %x, 1
349+
%mul = mul nuw i64 %x, 3
350+
call void @use(i64 %mul)
351+
%max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
352+
ret i64 %max
353+
}

0 commit comments

Comments
 (0)