xref: /llvm-project/llvm/test/Transforms/InstCombine/umul-sign-check.ll (revision 4ab40eca080965c65802710e39adbb78c4ce7bde)
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2; RUN: opt -passes=instcombine -S %s | FileCheck %s
3
4; Check that we simplify llvm.umul.with.overflow, if the overflow check is
5; weakened by or (icmp ne %res, 0) %overflow. This is generated by code using
6; __builtin_mul_overflow with negative integer constants, e.g.
7
8;   bool test(unsigned long long v, unsigned long long *res) {
9;     return __builtin_mul_overflow(v, -4775807LL, res);
10;   }
11
12declare { i64, i1 } @llvm.umul.with.overflow.i64(i64, i64) #0
13
14define i1 @test1(i64 %a, i64 %b, ptr %ptr) {
15; CHECK-LABEL: @test1(
16; CHECK-NEXT:    [[MUL:%.*]] = mul i64 [[A:%.*]], [[B:%.*]]
17; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne i64 [[A]], 0
18; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i64 [[B]], 0
19; CHECK-NEXT:    [[OVERFLOW_1:%.*]] = and i1 [[TMP1]], [[TMP2]]
20; CHECK-NEXT:    store i64 [[MUL]], ptr [[PTR:%.*]], align 8
21; CHECK-NEXT:    ret i1 [[OVERFLOW_1]]
22;
23
24  %res = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %a, i64 %b)
25  %overflow = extractvalue { i64, i1 } %res, 1
26  %mul = extractvalue { i64, i1 } %res, 0
27  %cmp  = icmp ne i64 %mul, 0
28  %overflow.1 = or i1 %overflow, %cmp
29  store i64 %mul, ptr %ptr, align 8
30  ret i1 %overflow.1
31}
32
33define i1 @test1_logical(i64 %a, i64 %b, ptr %ptr) {
34; CHECK-LABEL: @test1_logical(
35; CHECK-NEXT:    [[MUL:%.*]] = mul i64 [[A:%.*]], [[B:%.*]]
36; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne i64 [[A]], 0
37; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i64 [[B]], 0
38; CHECK-NEXT:    [[OVERFLOW_1:%.*]] = and i1 [[TMP1]], [[TMP2]]
39; CHECK-NEXT:    store i64 [[MUL]], ptr [[PTR:%.*]], align 8
40; CHECK-NEXT:    ret i1 [[OVERFLOW_1]]
41;
42
43  %res = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %a, i64 %b)
44  %overflow = extractvalue { i64, i1 } %res, 1
45  %mul = extractvalue { i64, i1 } %res, 0
46  %cmp  = icmp ne i64 %mul, 0
47  %overflow.1 = select i1 %overflow, i1 true, i1 %cmp
48  store i64 %mul, ptr %ptr, align 8
49  ret i1 %overflow.1
50}
51
52define i1 @test1_or_ops_swapped(i64 %a, i64 %b, ptr %ptr) {
53; CHECK-LABEL: @test1_or_ops_swapped(
54; CHECK-NEXT:    [[MUL:%.*]] = mul i64 [[A:%.*]], [[B:%.*]]
55; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne i64 [[A]], 0
56; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i64 [[B]], 0
57; CHECK-NEXT:    [[OVERFLOW_1:%.*]] = and i1 [[TMP1]], [[TMP2]]
58; CHECK-NEXT:    store i64 [[MUL]], ptr [[PTR:%.*]], align 8
59; CHECK-NEXT:    ret i1 [[OVERFLOW_1]]
60;
61
62
63  %res = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %a, i64 %b)
64  %overflow = extractvalue { i64, i1 } %res, 1
65  %mul = extractvalue { i64, i1 } %res, 0
66  %cmp  = icmp ne i64 %mul, 0
67  %overflow.1 = or i1 %cmp, %overflow
68  store i64 %mul, ptr %ptr, align 8
69  ret i1 %overflow.1
70}
71
72define i1 @test1_or_ops_swapped_logical(i64 %a, i64 %b, ptr %ptr) {
73; CHECK-LABEL: @test1_or_ops_swapped_logical(
74; CHECK-NEXT:    [[MUL:%.*]] = mul i64 [[A:%.*]], [[B:%.*]]
75; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne i64 [[A]], 0
76; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i64 [[B]], 0
77; CHECK-NEXT:    [[OVERFLOW_1:%.*]] = and i1 [[TMP1]], [[TMP2]]
78; CHECK-NEXT:    store i64 [[MUL]], ptr [[PTR:%.*]], align 8
79; CHECK-NEXT:    ret i1 [[OVERFLOW_1]]
80;
81
82
83  %res = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %a, i64 %b)
84  %overflow = extractvalue { i64, i1 } %res, 1
85  %mul = extractvalue { i64, i1 } %res, 0
86  %cmp  = icmp ne i64 %mul, 0
87  %overflow.1 = select i1 %cmp, i1 true, i1 %overflow
88  store i64 %mul, ptr %ptr, align 8
89  ret i1 %overflow.1
90}
91
92define i1 @test2(i64 %a, i64 %b, ptr %ptr) {
93; CHECK-LABEL: @test2(
94; CHECK-NEXT:    [[MUL:%.*]] = mul i64 [[A:%.*]], [[B:%.*]]
95; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne i64 [[A]], 0
96; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i64 [[B]], 0
97; CHECK-NEXT:    [[OVERFLOW_1:%.*]] = and i1 [[TMP1]], [[TMP2]]
98; CHECK-NEXT:    [[NEG:%.*]] = sub i64 0, [[MUL]]
99; CHECK-NEXT:    store i64 [[NEG]], ptr [[PTR:%.*]], align 8
100; CHECK-NEXT:    ret i1 [[OVERFLOW_1]]
101;
102
103  %res = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %a, i64 %b)
104  %overflow = extractvalue { i64, i1 } %res, 1
105  %mul = extractvalue { i64, i1 } %res, 0
106  %cmp = icmp ne i64 %mul, 0
107  %overflow.1 = or i1 %overflow, %cmp
108  %neg = sub i64 0, %mul
109  store i64 %neg, ptr %ptr, align 8
110  ret i1 %overflow.1
111}
112
113define i1 @test2_logical(i64 %a, i64 %b, ptr %ptr) {
114; CHECK-LABEL: @test2_logical(
115; CHECK-NEXT:    [[MUL:%.*]] = mul i64 [[A:%.*]], [[B:%.*]]
116; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne i64 [[A]], 0
117; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i64 [[B]], 0
118; CHECK-NEXT:    [[OVERFLOW_1:%.*]] = and i1 [[TMP1]], [[TMP2]]
119; CHECK-NEXT:    [[NEG:%.*]] = sub i64 0, [[MUL]]
120; CHECK-NEXT:    store i64 [[NEG]], ptr [[PTR:%.*]], align 8
121; CHECK-NEXT:    ret i1 [[OVERFLOW_1]]
122;
123
124  %res = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %a, i64 %b)
125  %overflow = extractvalue { i64, i1 } %res, 1
126  %mul = extractvalue { i64, i1 } %res, 0
127  %cmp = icmp ne i64 %mul, 0
128  %overflow.1 = select i1 %overflow, i1 true, i1 %cmp
129  %neg = sub i64 0, %mul
130  store i64 %neg, ptr %ptr, align 8
131  ret i1 %overflow.1
132}
133
134declare void @use(i1)
135
136define i1 @test3_multiple_overflow_users(i64 %a, i64 %b, ptr %ptr) {
137; CHECK-LABEL: @test3_multiple_overflow_users(
138; CHECK-NEXT:    [[RES:%.*]] = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 [[A:%.*]], i64 [[B:%.*]])
139; CHECK-NEXT:    [[OVERFLOW:%.*]] = extractvalue { i64, i1 } [[RES]], 1
140; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne i64 [[A]], 0
141; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i64 [[B]], 0
142; CHECK-NEXT:    [[OVERFLOW_1:%.*]] = and i1 [[TMP1]], [[TMP2]]
143; CHECK-NEXT:    call void @use(i1 [[OVERFLOW]])
144; CHECK-NEXT:    ret i1 [[OVERFLOW_1]]
145;
146  %res = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %a, i64 %b)
147  %overflow = extractvalue { i64, i1 } %res, 1
148  %mul = extractvalue { i64, i1 } %res, 0
149  %cmp = icmp ne i64 %mul, 0
150  %overflow.1 = or i1 %overflow, %cmp
151  call void @use(i1 %overflow)
152  ret i1 %overflow.1
153}
154
155define i1 @test3_multiple_overflow_users_logical(i64 %a, i64 %b, ptr %ptr) {
156; CHECK-LABEL: @test3_multiple_overflow_users_logical(
157; CHECK-NEXT:    [[RES:%.*]] = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 [[A:%.*]], i64 [[B:%.*]])
158; CHECK-NEXT:    [[OVERFLOW:%.*]] = extractvalue { i64, i1 } [[RES]], 1
159; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne i64 [[A]], 0
160; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i64 [[B]], 0
161; CHECK-NEXT:    [[OVERFLOW_1:%.*]] = and i1 [[TMP1]], [[TMP2]]
162; CHECK-NEXT:    call void @use(i1 [[OVERFLOW]])
163; CHECK-NEXT:    ret i1 [[OVERFLOW_1]]
164;
165  %res = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %a, i64 %b)
166  %overflow = extractvalue { i64, i1 } %res, 1
167  %mul = extractvalue { i64, i1 } %res, 0
168  %cmp = icmp ne i64 %mul, 0
169  %overflow.1 = select i1 %overflow, i1 true, i1 %cmp
170  call void @use(i1 %overflow)
171  ret i1 %overflow.1
172}
173
174; Do not simplify if %overflow and %mul have multiple uses.
175define i1 @test3_multiple_overflow_and_mul_users(i64 %a, i64 %b, ptr %ptr) {
176; CHECK-LABEL: @test3_multiple_overflow_and_mul_users(
177; CHECK-NEXT:    [[RES:%.*]] = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 [[A:%.*]], i64 [[B:%.*]])
178; CHECK-NEXT:    [[OVERFLOW:%.*]] = extractvalue { i64, i1 } [[RES]], 1
179; CHECK-NEXT:    [[MUL:%.*]] = extractvalue { i64, i1 } [[RES]], 0
180; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i64 [[MUL]], 0
181; CHECK-NEXT:    [[OVERFLOW_1:%.*]] = or i1 [[OVERFLOW]], [[CMP]]
182; CHECK-NEXT:    [[NEG:%.*]] = sub i64 0, [[MUL]]
183; CHECK-NEXT:    store i64 [[NEG]], ptr [[PTR:%.*]], align 8
184; CHECK-NEXT:    call void @use(i1 [[OVERFLOW]])
185; CHECK-NEXT:    ret i1 [[OVERFLOW_1]]
186;
187  %res = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %a, i64 %b)
188  %overflow = extractvalue { i64, i1 } %res, 1
189  %mul = extractvalue { i64, i1 } %res, 0
190  %cmp = icmp ne i64 %mul, 0
191  %overflow.1 = or i1 %overflow, %cmp
192  %neg = sub i64 0, %mul
193  store i64 %neg, ptr %ptr, align 8
194  call void @use(i1 %overflow)
195  ret i1 %overflow.1
196}
197
198define i1 @test3_multiple_overflow_and_mul_users_logical(i64 %a, i64 %b, ptr %ptr) {
199; CHECK-LABEL: @test3_multiple_overflow_and_mul_users_logical(
200; CHECK-NEXT:    [[RES:%.*]] = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 [[A:%.*]], i64 [[B:%.*]])
201; CHECK-NEXT:    [[OVERFLOW:%.*]] = extractvalue { i64, i1 } [[RES]], 1
202; CHECK-NEXT:    [[MUL:%.*]] = extractvalue { i64, i1 } [[RES]], 0
203; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i64 [[MUL]], 0
204; CHECK-NEXT:    [[OVERFLOW_1:%.*]] = or i1 [[OVERFLOW]], [[CMP]]
205; CHECK-NEXT:    [[NEG:%.*]] = sub i64 0, [[MUL]]
206; CHECK-NEXT:    store i64 [[NEG]], ptr [[PTR:%.*]], align 8
207; CHECK-NEXT:    call void @use(i1 [[OVERFLOW]])
208; CHECK-NEXT:    ret i1 [[OVERFLOW_1]]
209;
210  %res = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %a, i64 %b)
211  %overflow = extractvalue { i64, i1 } %res, 1
212  %mul = extractvalue { i64, i1 } %res, 0
213  %cmp = icmp ne i64 %mul, 0
214  %overflow.1 = select i1 %overflow, i1 true, i1 %cmp
215  %neg = sub i64 0, %mul
216  store i64 %neg, ptr %ptr, align 8
217  call void @use(i1 %overflow)
218  ret i1 %overflow.1
219}
220
221
222declare void @use.2({ i64, i1 })
223define i1 @test3_multiple_res_users(i64 %a, i64 %b, ptr %ptr) {
224; CHECK-LABEL: @test3_multiple_res_users(
225; CHECK-NEXT:    [[RES:%.*]] = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 [[A:%.*]], i64 [[B:%.*]])
226; CHECK-NEXT:    [[MUL:%.*]] = extractvalue { i64, i1 } [[RES]], 0
227; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne i64 [[A]], 0
228; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i64 [[B]], 0
229; CHECK-NEXT:    [[OVERFLOW_1:%.*]] = and i1 [[TMP1]], [[TMP2]]
230; CHECK-NEXT:    [[NEG:%.*]] = sub i64 0, [[MUL]]
231; CHECK-NEXT:    store i64 [[NEG]], ptr [[PTR:%.*]], align 8
232; CHECK-NEXT:    call void @use.2({ i64, i1 } [[RES]])
233; CHECK-NEXT:    ret i1 [[OVERFLOW_1]]
234;
235  %res = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %a, i64 %b)
236  %overflow = extractvalue { i64, i1 } %res, 1
237  %mul = extractvalue { i64, i1 } %res, 0
238  %cmp = icmp ne i64 %mul, 0
239  %overflow.1 = or i1 %overflow, %cmp
240  %neg = sub i64 0, %mul
241  store i64 %neg, ptr %ptr, align 8
242  call void @use.2({ i64, i1 } %res)
243  ret i1 %overflow.1
244}
245
246define i1 @test3_multiple_res_users_logical(i64 %a, i64 %b, ptr %ptr) {
247; CHECK-LABEL: @test3_multiple_res_users_logical(
248; CHECK-NEXT:    [[RES:%.*]] = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 [[A:%.*]], i64 [[B:%.*]])
249; CHECK-NEXT:    [[MUL:%.*]] = extractvalue { i64, i1 } [[RES]], 0
250; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne i64 [[A]], 0
251; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i64 [[B]], 0
252; CHECK-NEXT:    [[OVERFLOW_1:%.*]] = and i1 [[TMP1]], [[TMP2]]
253; CHECK-NEXT:    [[NEG:%.*]] = sub i64 0, [[MUL]]
254; CHECK-NEXT:    store i64 [[NEG]], ptr [[PTR:%.*]], align 8
255; CHECK-NEXT:    call void @use.2({ i64, i1 } [[RES]])
256; CHECK-NEXT:    ret i1 [[OVERFLOW_1]]
257;
258  %res = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %a, i64 %b)
259  %overflow = extractvalue { i64, i1 } %res, 1
260  %mul = extractvalue { i64, i1 } %res, 0
261  %cmp = icmp ne i64 %mul, 0
262  %overflow.1 = select i1 %overflow, i1 true, i1 %cmp
263  %neg = sub i64 0, %mul
264  store i64 %neg, ptr %ptr, align 8
265  call void @use.2({ i64, i1 } %res)
266  ret i1 %overflow.1
267}
268
269declare void @use.3(i64)
270
271; Simplify if %mul has multiple uses.
272define i1 @test3_multiple_mul_users(i64 %a, i64 %b, ptr %ptr) {
273; CHECK-LABEL: @test3_multiple_mul_users(
274; CHECK-NEXT:    [[MUL:%.*]] = mul i64 [[A:%.*]], [[B:%.*]]
275; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne i64 [[A]], 0
276; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i64 [[B]], 0
277; CHECK-NEXT:    [[OVERFLOW_1:%.*]] = and i1 [[TMP1]], [[TMP2]]
278; CHECK-NEXT:    [[NEG:%.*]] = sub i64 0, [[MUL]]
279; CHECK-NEXT:    store i64 [[NEG]], ptr [[PTR:%.*]], align 8
280; CHECK-NEXT:    call void @use.3(i64 [[MUL]])
281; CHECK-NEXT:    ret i1 [[OVERFLOW_1]]
282;
283
284  %res = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %a, i64 %b)
285  %overflow = extractvalue { i64, i1 } %res, 1
286  %mul = extractvalue { i64, i1 } %res, 0
287  %cmp = icmp ne i64 %mul, 0
288  %overflow.1 = or i1 %overflow, %cmp
289  %neg = sub i64 0, %mul
290  store i64 %neg, ptr %ptr, align 8
291  call void @use.3(i64 %mul)
292  ret i1 %overflow.1
293}
294
295define i1 @test3_multiple_mul_users_logical(i64 %a, i64 %b, ptr %ptr) {
296; CHECK-LABEL: @test3_multiple_mul_users_logical(
297; CHECK-NEXT:    [[MUL:%.*]] = mul i64 [[A:%.*]], [[B:%.*]]
298; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne i64 [[A]], 0
299; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i64 [[B]], 0
300; CHECK-NEXT:    [[OVERFLOW_1:%.*]] = and i1 [[TMP1]], [[TMP2]]
301; CHECK-NEXT:    [[NEG:%.*]] = sub i64 0, [[MUL]]
302; CHECK-NEXT:    store i64 [[NEG]], ptr [[PTR:%.*]], align 8
303; CHECK-NEXT:    call void @use.3(i64 [[MUL]])
304; CHECK-NEXT:    ret i1 [[OVERFLOW_1]]
305;
306
307  %res = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %a, i64 %b)
308  %overflow = extractvalue { i64, i1 } %res, 1
309  %mul = extractvalue { i64, i1 } %res, 0
310  %cmp = icmp ne i64 %mul, 0
311  %overflow.1 = select i1 %overflow, i1 true, i1 %cmp
312  %neg = sub i64 0, %mul
313  store i64 %neg, ptr %ptr, align 8
314  call void @use.3(i64 %mul)
315  ret i1 %overflow.1
316}
317
318
319
320define i1 @test4_no_icmp_ne(i64 %a, i64 %b, ptr %ptr) {
321; CHECK-LABEL: @test4_no_icmp_ne(
322; CHECK-NEXT:    [[RES:%.*]] = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 [[A:%.*]], i64 [[B:%.*]])
323; CHECK-NEXT:    [[OVERFLOW:%.*]] = extractvalue { i64, i1 } [[RES]], 1
324; CHECK-NEXT:    [[MUL:%.*]] = extractvalue { i64, i1 } [[RES]], 0
325; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i64 [[MUL]], 0
326; CHECK-NEXT:    [[OVERFLOW_1:%.*]] = or i1 [[OVERFLOW]], [[CMP]]
327; CHECK-NEXT:    [[NEG:%.*]] = sub i64 0, [[MUL]]
328; CHECK-NEXT:    store i64 [[NEG]], ptr [[PTR:%.*]], align 8
329; CHECK-NEXT:    ret i1 [[OVERFLOW_1]]
330;
331  %res = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %a, i64 %b)
332  %overflow = extractvalue { i64, i1 } %res, 1
333  %mul = extractvalue { i64, i1 } %res, 0
334  %cmp = icmp sgt i64 %mul, 0
335  %overflow.1 = or i1 %overflow, %cmp
336  %neg = sub i64 0, %mul
337  store i64 %neg, ptr %ptr, align 8
338  ret i1 %overflow.1
339}
340
341define i1 @test4_no_icmp_ne_logical(i64 %a, i64 %b, ptr %ptr) {
342; CHECK-LABEL: @test4_no_icmp_ne_logical(
343; CHECK-NEXT:    [[RES:%.*]] = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 [[A:%.*]], i64 [[B:%.*]])
344; CHECK-NEXT:    [[OVERFLOW:%.*]] = extractvalue { i64, i1 } [[RES]], 1
345; CHECK-NEXT:    [[MUL:%.*]] = extractvalue { i64, i1 } [[RES]], 0
346; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i64 [[MUL]], 0
347; CHECK-NEXT:    [[OVERFLOW_1:%.*]] = or i1 [[OVERFLOW]], [[CMP]]
348; CHECK-NEXT:    [[NEG:%.*]] = sub i64 0, [[MUL]]
349; CHECK-NEXT:    store i64 [[NEG]], ptr [[PTR:%.*]], align 8
350; CHECK-NEXT:    ret i1 [[OVERFLOW_1]]
351;
352  %res = tail call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %a, i64 %b)
353  %overflow = extractvalue { i64, i1 } %res, 1
354  %mul = extractvalue { i64, i1 } %res, 0
355  %cmp = icmp sgt i64 %mul, 0
356  %overflow.1 = select i1 %overflow, i1 true, i1 %cmp
357  %neg = sub i64 0, %mul
358  store i64 %neg, ptr %ptr, align 8
359  ret i1 %overflow.1
360}
361
362attributes #0 = { nounwind readnone speculatable willreturn }
363