1; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2; RUN: llc -mtriple=aarch64-unknown-linux-gnu < %s | FileCheck %s
3
4; https://bugs.llvm.org/show_bug.cgi?id=38149
5
6; We are truncating from wider width, and then sign-extending
7; back to the original width. Then we inequality-comparing orig and src.
8; If they don't match, then we had signed truncation during truncation.
9
10; This can be expressed in a several ways in IR:
11;   trunc + sext + icmp ne <- not canonical
12;   shl   + ashr + icmp ne
13;   add          + icmp ult/ule
14;   add          + icmp uge/ugt
15; However only the simplest form (with two shifts) gets lowered best.
16
17; ---------------------------------------------------------------------------- ;
18; shl + ashr + icmp ne
19; ---------------------------------------------------------------------------- ;
20
21define i1 @shifts_necmp_i16_i8(i16 %x) nounwind {
22; CHECK-LABEL: shifts_necmp_i16_i8:
23; CHECK:       // %bb.0:
24; CHECK-NEXT:    sxtb w8, w0
25; CHECK-NEXT:    and w8, w8, #0xffff
26; CHECK-NEXT:    cmp w8, w0, uxth
27; CHECK-NEXT:    cset w0, ne
28; CHECK-NEXT:    ret
29  %tmp0 = shl i16 %x, 8 ; 16-8
30  %tmp1 = ashr exact i16 %tmp0, 8 ; 16-8
31  %tmp2 = icmp ne i16 %tmp1, %x
32  ret i1 %tmp2
33}
34
35define i1 @shifts_necmp_i32_i16(i32 %x) nounwind {
36; CHECK-LABEL: shifts_necmp_i32_i16:
37; CHECK:       // %bb.0:
38; CHECK-NEXT:    cmp w0, w0, sxth
39; CHECK-NEXT:    cset w0, ne
40; CHECK-NEXT:    ret
41  %tmp0 = shl i32 %x, 16 ; 32-16
42  %tmp1 = ashr exact i32 %tmp0, 16 ; 32-16
43  %tmp2 = icmp ne i32 %tmp1, %x
44  ret i1 %tmp2
45}
46
47define i1 @shifts_necmp_i32_i8(i32 %x) nounwind {
48; CHECK-LABEL: shifts_necmp_i32_i8:
49; CHECK:       // %bb.0:
50; CHECK-NEXT:    cmp w0, w0, sxtb
51; CHECK-NEXT:    cset w0, ne
52; CHECK-NEXT:    ret
53  %tmp0 = shl i32 %x, 24 ; 32-8
54  %tmp1 = ashr exact i32 %tmp0, 24 ; 32-8
55  %tmp2 = icmp ne i32 %tmp1, %x
56  ret i1 %tmp2
57}
58
59define i1 @shifts_necmp_i64_i32(i64 %x) nounwind {
60; CHECK-LABEL: shifts_necmp_i64_i32:
61; CHECK:       // %bb.0:
62; CHECK-NEXT:    cmp x0, w0, sxtw
63; CHECK-NEXT:    cset w0, ne
64; CHECK-NEXT:    ret
65  %tmp0 = shl i64 %x, 32 ; 64-32
66  %tmp1 = ashr exact i64 %tmp0, 32 ; 64-32
67  %tmp2 = icmp ne i64 %tmp1, %x
68  ret i1 %tmp2
69}
70
71define i1 @shifts_necmp_i64_i16(i64 %x) nounwind {
72; CHECK-LABEL: shifts_necmp_i64_i16:
73; CHECK:       // %bb.0:
74; CHECK-NEXT:    cmp x0, w0, sxth
75; CHECK-NEXT:    cset w0, ne
76; CHECK-NEXT:    ret
77  %tmp0 = shl i64 %x, 48 ; 64-16
78  %tmp1 = ashr exact i64 %tmp0, 48 ; 64-16
79  %tmp2 = icmp ne i64 %tmp1, %x
80  ret i1 %tmp2
81}
82
83define i1 @shifts_necmp_i64_i8(i64 %x) nounwind {
84; CHECK-LABEL: shifts_necmp_i64_i8:
85; CHECK:       // %bb.0:
86; CHECK-NEXT:    cmp x0, w0, sxtb
87; CHECK-NEXT:    cset w0, ne
88; CHECK-NEXT:    ret
89  %tmp0 = shl i64 %x, 56 ; 64-8
90  %tmp1 = ashr exact i64 %tmp0, 56 ; 64-8
91  %tmp2 = icmp ne i64 %tmp1, %x
92  ret i1 %tmp2
93}
94
95; ---------------------------------------------------------------------------- ;
96; add + icmp ult
97; ---------------------------------------------------------------------------- ;
98
99define i1 @add_ultcmp_i16_i8(i16 %x) nounwind {
100; CHECK-LABEL: add_ultcmp_i16_i8:
101; CHECK:       // %bb.0:
102; CHECK-NEXT:    sxtb w8, w0
103; CHECK-NEXT:    and w8, w8, #0xffff
104; CHECK-NEXT:    cmp w8, w0, uxth
105; CHECK-NEXT:    cset w0, ne
106; CHECK-NEXT:    ret
107  %tmp0 = add i16 %x, -128 ; ~0U << (8-1)
108  %tmp1 = icmp ult i16 %tmp0, -256 ; ~0U << 8
109  ret i1 %tmp1
110}
111
112define i1 @add_ultcmp_i32_i16(i32 %x) nounwind {
113; CHECK-LABEL: add_ultcmp_i32_i16:
114; CHECK:       // %bb.0:
115; CHECK-NEXT:    cmp w0, w0, sxth
116; CHECK-NEXT:    cset w0, ne
117; CHECK-NEXT:    ret
118  %tmp0 = add i32 %x, -32768 ; ~0U << (16-1)
119  %tmp1 = icmp ult i32 %tmp0, -65536 ; ~0U << 16
120  ret i1 %tmp1
121}
122
123define i1 @add_ultcmp_i32_i8(i32 %x) nounwind {
124; CHECK-LABEL: add_ultcmp_i32_i8:
125; CHECK:       // %bb.0:
126; CHECK-NEXT:    cmp w0, w0, sxtb
127; CHECK-NEXT:    cset w0, ne
128; CHECK-NEXT:    ret
129  %tmp0 = add i32 %x, -128 ; ~0U << (8-1)
130  %tmp1 = icmp ult i32 %tmp0, -256 ; ~0U << 8
131  ret i1 %tmp1
132}
133
134define i1 @add_ultcmp_i64_i32(i64 %x) nounwind {
135; CHECK-LABEL: add_ultcmp_i64_i32:
136; CHECK:       // %bb.0:
137; CHECK-NEXT:    cmp x0, w0, sxtw
138; CHECK-NEXT:    cset w0, ne
139; CHECK-NEXT:    ret
140  %tmp0 = add i64 %x, -2147483648 ; ~0U << (32-1)
141  %tmp1 = icmp ult i64 %tmp0, -4294967296 ; ~0U << 32
142  ret i1 %tmp1
143}
144
145define i1 @add_ultcmp_i64_i16(i64 %x) nounwind {
146; CHECK-LABEL: add_ultcmp_i64_i16:
147; CHECK:       // %bb.0:
148; CHECK-NEXT:    cmp x0, w0, sxth
149; CHECK-NEXT:    cset w0, ne
150; CHECK-NEXT:    ret
151  %tmp0 = add i64 %x, -32768 ; ~0U << (16-1)
152  %tmp1 = icmp ult i64 %tmp0, -65536 ; ~0U << 16
153  ret i1 %tmp1
154}
155
156define i1 @add_ultcmp_i64_i8(i64 %x) nounwind {
157; CHECK-LABEL: add_ultcmp_i64_i8:
158; CHECK:       // %bb.0:
159; CHECK-NEXT:    cmp x0, w0, sxtb
160; CHECK-NEXT:    cset w0, ne
161; CHECK-NEXT:    ret
162  %tmp0 = add i64 %x, -128 ; ~0U << (8-1)
163  %tmp1 = icmp ult i64 %tmp0, -256 ; ~0U << 8
164  ret i1 %tmp1
165}
166
167; Slightly more canonical variant
168define i1 @add_ulecmp_i16_i8(i16 %x) nounwind {
169; CHECK-LABEL: add_ulecmp_i16_i8:
170; CHECK:       // %bb.0:
171; CHECK-NEXT:    sxtb w8, w0
172; CHECK-NEXT:    and w8, w8, #0xffff
173; CHECK-NEXT:    cmp w8, w0, uxth
174; CHECK-NEXT:    cset w0, ne
175; CHECK-NEXT:    ret
176  %tmp0 = add i16 %x, -128 ; ~0U << (8-1)
177  %tmp1 = icmp ule i16 %tmp0, -257 ; ~0U << 8 - 1
178  ret i1 %tmp1
179}
180
181; ---------------------------------------------------------------------------- ;
182; add + icmp uge
183; ---------------------------------------------------------------------------- ;
184
185define i1 @add_ugecmp_i16_i8(i16 %x) nounwind {
186; CHECK-LABEL: add_ugecmp_i16_i8:
187; CHECK:       // %bb.0:
188; CHECK-NEXT:    sxtb w8, w0
189; CHECK-NEXT:    and w8, w8, #0xffff
190; CHECK-NEXT:    cmp w8, w0, uxth
191; CHECK-NEXT:    cset w0, ne
192; CHECK-NEXT:    ret
193  %tmp0 = add i16 %x, 128 ; 1U << (8-1)
194  %tmp1 = icmp uge i16 %tmp0, 256 ; 1U << 8
195  ret i1 %tmp1
196}
197
198define i1 @add_ugecmp_i32_i16(i32 %x) nounwind {
199; CHECK-LABEL: add_ugecmp_i32_i16:
200; CHECK:       // %bb.0:
201; CHECK-NEXT:    cmp w0, w0, sxth
202; CHECK-NEXT:    cset w0, ne
203; CHECK-NEXT:    ret
204  %tmp0 = add i32 %x, 32768 ; 1U << (16-1)
205  %tmp1 = icmp uge i32 %tmp0, 65536 ; 1U << 16
206  ret i1 %tmp1
207}
208
209define i1 @add_ugecmp_i32_i8(i32 %x) nounwind {
210; CHECK-LABEL: add_ugecmp_i32_i8:
211; CHECK:       // %bb.0:
212; CHECK-NEXT:    cmp w0, w0, sxtb
213; CHECK-NEXT:    cset w0, ne
214; CHECK-NEXT:    ret
215  %tmp0 = add i32 %x, 128 ; 1U << (8-1)
216  %tmp1 = icmp uge i32 %tmp0, 256 ; 1U << 8
217  ret i1 %tmp1
218}
219
220define i1 @add_ugecmp_i64_i32(i64 %x) nounwind {
221; CHECK-LABEL: add_ugecmp_i64_i32:
222; CHECK:       // %bb.0:
223; CHECK-NEXT:    cmp x0, w0, sxtw
224; CHECK-NEXT:    cset w0, ne
225; CHECK-NEXT:    ret
226  %tmp0 = add i64 %x, 2147483648 ; 1U << (32-1)
227  %tmp1 = icmp uge i64 %tmp0, 4294967296 ; 1U << 32
228  ret i1 %tmp1
229}
230
231define i1 @add_ugecmp_i64_i16(i64 %x) nounwind {
232; CHECK-LABEL: add_ugecmp_i64_i16:
233; CHECK:       // %bb.0:
234; CHECK-NEXT:    cmp x0, w0, sxth
235; CHECK-NEXT:    cset w0, ne
236; CHECK-NEXT:    ret
237  %tmp0 = add i64 %x, 32768 ; 1U << (16-1)
238  %tmp1 = icmp uge i64 %tmp0, 65536 ; 1U << 16
239  ret i1 %tmp1
240}
241
242define i1 @add_ugecmp_i64_i8(i64 %x) nounwind {
243; CHECK-LABEL: add_ugecmp_i64_i8:
244; CHECK:       // %bb.0:
245; CHECK-NEXT:    cmp x0, w0, sxtb
246; CHECK-NEXT:    cset w0, ne
247; CHECK-NEXT:    ret
248  %tmp0 = add i64 %x, 128 ; 1U << (8-1)
249  %tmp1 = icmp uge i64 %tmp0, 256 ; 1U << 8
250  ret i1 %tmp1
251}
252
253; Slightly more canonical variant
254define i1 @add_ugtcmp_i16_i8(i16 %x) nounwind {
255; CHECK-LABEL: add_ugtcmp_i16_i8:
256; CHECK:       // %bb.0:
257; CHECK-NEXT:    sxtb w8, w0
258; CHECK-NEXT:    and w8, w8, #0xffff
259; CHECK-NEXT:    cmp w8, w0, uxth
260; CHECK-NEXT:    cset w0, ne
261; CHECK-NEXT:    ret
262  %tmp0 = add i16 %x, 128 ; 1U << (8-1)
263  %tmp1 = icmp ugt i16 %tmp0, 255 ; (1U << 8) - 1
264  ret i1 %tmp1
265}
266
267; Negative tests
268; ---------------------------------------------------------------------------- ;
269
270; Adding not a constant
271define i1 @add_ugecmp_bad_i16_i8_add(i16 %x, i16 %y) nounwind {
272; CHECK-LABEL: add_ugecmp_bad_i16_i8_add:
273; CHECK:       // %bb.0:
274; CHECK-NEXT:    add w8, w0, w1
275; CHECK-NEXT:    and w8, w8, #0xffff
276; CHECK-NEXT:    cmp w8, #255 // =255
277; CHECK-NEXT:    cset w0, hi
278; CHECK-NEXT:    ret
279  %tmp0 = add i16 %x, %y
280  %tmp1 = icmp uge i16 %tmp0, 256 ; 1U << 8
281  ret i1 %tmp1
282}
283
284; Comparing not with a constant
285define i1 @add_ugecmp_bad_i16_i8_cmp(i16 %x, i16 %y) nounwind {
286; CHECK-LABEL: add_ugecmp_bad_i16_i8_cmp:
287; CHECK:       // %bb.0:
288; CHECK-NEXT:    add w8, w0, #128 // =128
289; CHECK-NEXT:    and w8, w8, #0xffff
290; CHECK-NEXT:    cmp w8, w1, uxth
291; CHECK-NEXT:    cset w0, hs
292; CHECK-NEXT:    ret
293  %tmp0 = add i16 %x, 128 ; 1U << (8-1)
294  %tmp1 = icmp uge i16 %tmp0, %y
295  ret i1 %tmp1
296}
297
298; Second constant is not larger than the first one
299define i1 @add_ugecmp_bad_i8_i16(i16 %x) nounwind {
300; CHECK-LABEL: add_ugecmp_bad_i8_i16:
301; CHECK:       // %bb.0:
302; CHECK-NEXT:    add w8, w0, #128 // =128
303; CHECK-NEXT:    and w8, w8, #0xffff
304; CHECK-NEXT:    cmp w8, #127 // =127
305; CHECK-NEXT:    cset w0, hi
306; CHECK-NEXT:    ret
307  %tmp0 = add i16 %x, 128 ; 1U << (8-1)
308  %tmp1 = icmp uge i16 %tmp0, 128 ; 1U << (8-1)
309  ret i1 %tmp1
310}
311
312; First constant is not power of two
313define i1 @add_ugecmp_bad_i16_i8_c0notpoweroftwo(i16 %x) nounwind {
314; CHECK-LABEL: add_ugecmp_bad_i16_i8_c0notpoweroftwo:
315; CHECK:       // %bb.0:
316; CHECK-NEXT:    add w8, w0, #192 // =192
317; CHECK-NEXT:    and w8, w8, #0xffff
318; CHECK-NEXT:    cmp w8, #255 // =255
319; CHECK-NEXT:    cset w0, hi
320; CHECK-NEXT:    ret
321  %tmp0 = add i16 %x, 192 ; (1U << (8-1)) + (1U << (8-1-1))
322  %tmp1 = icmp uge i16 %tmp0, 256 ; 1U << 8
323  ret i1 %tmp1
324}
325
326; Second constant is not power of two
327define i1 @add_ugecmp_bad_i16_i8_c1notpoweroftwo(i16 %x) nounwind {
328; CHECK-LABEL: add_ugecmp_bad_i16_i8_c1notpoweroftwo:
329; CHECK:       // %bb.0:
330; CHECK-NEXT:    add w8, w0, #128 // =128
331; CHECK-NEXT:    and w8, w8, #0xffff
332; CHECK-NEXT:    cmp w8, #767 // =767
333; CHECK-NEXT:    cset w0, hi
334; CHECK-NEXT:    ret
335  %tmp0 = add i16 %x, 128 ; 1U << (8-1)
336  %tmp1 = icmp uge i16 %tmp0, 768 ; (1U << 8)) + (1U << (8+1))
337  ret i1 %tmp1
338}
339
340; Magic check fails, 64 << 1 != 256
341define i1 @add_ugecmp_bad_i16_i8_magic(i16 %x) nounwind {
342; CHECK-LABEL: add_ugecmp_bad_i16_i8_magic:
343; CHECK:       // %bb.0:
344; CHECK-NEXT:    add w8, w0, #64 // =64
345; CHECK-NEXT:    and w8, w8, #0xffff
346; CHECK-NEXT:    cmp w8, #255 // =255
347; CHECK-NEXT:    cset w0, hi
348; CHECK-NEXT:    ret
349  %tmp0 = add i16 %x, 64 ; 1U << (8-1-1)
350  %tmp1 = icmp uge i16 %tmp0, 256 ; 1U << 8
351  ret i1 %tmp1
352}
353
354; Bad 'destination type'
355define i1 @add_ugecmp_bad_i16_i4(i16 %x) nounwind {
356; CHECK-LABEL: add_ugecmp_bad_i16_i4:
357; CHECK:       // %bb.0:
358; CHECK-NEXT:    add w8, w0, #8 // =8
359; CHECK-NEXT:    and w8, w8, #0xffff
360; CHECK-NEXT:    cmp w8, #15 // =15
361; CHECK-NEXT:    cset w0, hi
362; CHECK-NEXT:    ret
363  %tmp0 = add i16 %x, 8 ; 1U << (4-1)
364  %tmp1 = icmp uge i16 %tmp0, 16 ; 1U << 4
365  ret i1 %tmp1
366}
367
368; Bad storage type
369define i1 @add_ugecmp_bad_i24_i8(i24 %x) nounwind {
370; CHECK-LABEL: add_ugecmp_bad_i24_i8:
371; CHECK:       // %bb.0:
372; CHECK-NEXT:    add w8, w0, #128 // =128
373; CHECK-NEXT:    and w8, w8, #0xffffff
374; CHECK-NEXT:    cmp w8, #255 // =255
375; CHECK-NEXT:    cset w0, hi
376; CHECK-NEXT:    ret
377  %tmp0 = add i24 %x, 128 ; 1U << (8-1)
378  %tmp1 = icmp uge i24 %tmp0, 256 ; 1U << 8
379  ret i1 %tmp1
380}
381
382; Slightly more canonical variant
383define i1 @add_ugtcmp_bad_i16_i8(i16 %x) nounwind {
384; CHECK-LABEL: add_ugtcmp_bad_i16_i8:
385; CHECK:       // %bb.0:
386; CHECK-NEXT:    mov w0, wzr
387; CHECK-NEXT:    ret
388  %tmp0 = add i16 %x, 128 ; 1U << (8-1)
389  %tmp1 = icmp ugt i16 %tmp0, -1 ; when we +1 it, it will wrap to 0
390  ret i1 %tmp1
391}
392