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
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:    sxth w8, w0
39; CHECK-NEXT:    cmp w8, w0
40; CHECK-NEXT:    cset w0, ne
41; CHECK-NEXT:    ret
42  %tmp0 = shl i32 %x, 16 ; 32-16
43  %tmp1 = ashr exact i32 %tmp0, 16 ; 32-16
44  %tmp2 = icmp ne i32 %tmp1, %x
45  ret i1 %tmp2
46}
47
48define i1 @shifts_necmp_i32_i8(i32 %x) nounwind {
49; CHECK-LABEL: shifts_necmp_i32_i8:
50; CHECK:       // %bb.0:
51; CHECK-NEXT:    sxtb w8, w0
52; CHECK-NEXT:    cmp w8, w0
53; CHECK-NEXT:    cset w0, ne
54; CHECK-NEXT:    ret
55  %tmp0 = shl i32 %x, 24 ; 32-8
56  %tmp1 = ashr exact i32 %tmp0, 24 ; 32-8
57  %tmp2 = icmp ne i32 %tmp1, %x
58  ret i1 %tmp2
59}
60
61define i1 @shifts_necmp_i64_i32(i64 %x) nounwind {
62; CHECK-LABEL: shifts_necmp_i64_i32:
63; CHECK:       // %bb.0:
64; CHECK-NEXT:    sxtw x8, w0
65; CHECK-NEXT:    cmp x8, x0
66; CHECK-NEXT:    cset w0, ne
67; CHECK-NEXT:    ret
68  %tmp0 = shl i64 %x, 32 ; 64-32
69  %tmp1 = ashr exact i64 %tmp0, 32 ; 64-32
70  %tmp2 = icmp ne i64 %tmp1, %x
71  ret i1 %tmp2
72}
73
74define i1 @shifts_necmp_i64_i16(i64 %x) nounwind {
75; CHECK-LABEL: shifts_necmp_i64_i16:
76; CHECK:       // %bb.0:
77; CHECK-NEXT:    sxth x8, w0
78; CHECK-NEXT:    cmp x8, x0
79; CHECK-NEXT:    cset w0, ne
80; CHECK-NEXT:    ret
81  %tmp0 = shl i64 %x, 48 ; 64-16
82  %tmp1 = ashr exact i64 %tmp0, 48 ; 64-16
83  %tmp2 = icmp ne i64 %tmp1, %x
84  ret i1 %tmp2
85}
86
87define i1 @shifts_necmp_i64_i8(i64 %x) nounwind {
88; CHECK-LABEL: shifts_necmp_i64_i8:
89; CHECK:       // %bb.0:
90; CHECK-NEXT:    sxtb x8, w0
91; CHECK-NEXT:    cmp x8, x0
92; CHECK-NEXT:    cset w0, ne
93; CHECK-NEXT:    ret
94  %tmp0 = shl i64 %x, 56 ; 64-8
95  %tmp1 = ashr exact i64 %tmp0, 56 ; 64-8
96  %tmp2 = icmp ne i64 %tmp1, %x
97  ret i1 %tmp2
98}
99
100; ---------------------------------------------------------------------------- ;
101; add + icmp ult
102; ---------------------------------------------------------------------------- ;
103
104define i1 @add_ultcmp_i16_i8(i16 %x) nounwind {
105; CHECK-LABEL: add_ultcmp_i16_i8:
106; CHECK:       // %bb.0:
107; CHECK-NEXT:    sub w8, w0, #128 // =128
108; CHECK-NEXT:    ubfx w8, w8, #8, #8
109; CHECK-NEXT:    cmp w8, #255 // =255
110; CHECK-NEXT:    cset w0, lo
111; CHECK-NEXT:    ret
112  %tmp0 = add i16 %x, -128 ; ~0U << (8-1)
113  %tmp1 = icmp ult i16 %tmp0, -256 ; ~0U << 8
114  ret i1 %tmp1
115}
116
117define i1 @add_ultcmp_i32_i16(i32 %x) nounwind {
118; CHECK-LABEL: add_ultcmp_i32_i16:
119; CHECK:       // %bb.0:
120; CHECK-NEXT:    sub w8, w0, #8, lsl #12 // =32768
121; CHECK-NEXT:    cmn w8, #16, lsl #12 // =65536
122; CHECK-NEXT:    cset w0, lo
123; CHECK-NEXT:    ret
124  %tmp0 = add i32 %x, -32768 ; ~0U << (16-1)
125  %tmp1 = icmp ult i32 %tmp0, -65536 ; ~0U << 16
126  ret i1 %tmp1
127}
128
129define i1 @add_ultcmp_i32_i8(i32 %x) nounwind {
130; CHECK-LABEL: add_ultcmp_i32_i8:
131; CHECK:       // %bb.0:
132; CHECK-NEXT:    sub w8, w0, #128 // =128
133; CHECK-NEXT:    cmn w8, #256 // =256
134; CHECK-NEXT:    cset w0, lo
135; CHECK-NEXT:    ret
136  %tmp0 = add i32 %x, -128 ; ~0U << (8-1)
137  %tmp1 = icmp ult i32 %tmp0, -256 ; ~0U << 8
138  ret i1 %tmp1
139}
140
141define i1 @add_ultcmp_i64_i32(i64 %x) nounwind {
142; CHECK-LABEL: add_ultcmp_i64_i32:
143; CHECK:       // %bb.0:
144; CHECK-NEXT:    mov x8, #-2147483648
145; CHECK-NEXT:    add x8, x0, x8
146; CHECK-NEXT:    mov x9, #-4294967296
147; CHECK-NEXT:    cmp x8, x9
148; CHECK-NEXT:    cset w0, lo
149; CHECK-NEXT:    ret
150  %tmp0 = add i64 %x, -2147483648 ; ~0U << (32-1)
151  %tmp1 = icmp ult i64 %tmp0, -4294967296 ; ~0U << 32
152  ret i1 %tmp1
153}
154
155define i1 @add_ultcmp_i64_i16(i64 %x) nounwind {
156; CHECK-LABEL: add_ultcmp_i64_i16:
157; CHECK:       // %bb.0:
158; CHECK-NEXT:    sub x8, x0, #8, lsl #12 // =32768
159; CHECK-NEXT:    cmn x8, #16, lsl #12 // =65536
160; CHECK-NEXT:    cset w0, lo
161; CHECK-NEXT:    ret
162  %tmp0 = add i64 %x, -32768 ; ~0U << (16-1)
163  %tmp1 = icmp ult i64 %tmp0, -65536 ; ~0U << 16
164  ret i1 %tmp1
165}
166
167define i1 @add_ultcmp_i64_i8(i64 %x) nounwind {
168; CHECK-LABEL: add_ultcmp_i64_i8:
169; CHECK:       // %bb.0:
170; CHECK-NEXT:    sub x8, x0, #128 // =128
171; CHECK-NEXT:    cmn x8, #256 // =256
172; CHECK-NEXT:    cset w0, lo
173; CHECK-NEXT:    ret
174  %tmp0 = add i64 %x, -128 ; ~0U << (8-1)
175  %tmp1 = icmp ult i64 %tmp0, -256 ; ~0U << 8
176  ret i1 %tmp1
177}
178
179; ---------------------------------------------------------------------------- ;
180; add + icmp uge
181; ---------------------------------------------------------------------------- ;
182
183define i1 @add_ugecmp_i16_i8(i16 %x) nounwind {
184; CHECK-LABEL: add_ugecmp_i16_i8:
185; CHECK:       // %bb.0:
186; CHECK-NEXT:    sxtb w8, w0
187; CHECK-NEXT:    and w8, w8, #0xffff
188; CHECK-NEXT:    cmp w8, w0, uxth
189; CHECK-NEXT:    cset w0, ne
190; CHECK-NEXT:    ret
191  %tmp0 = add i16 %x, 128 ; 1U << (8-1)
192  %tmp1 = icmp uge i16 %tmp0, 256 ; 1U << 8
193  ret i1 %tmp1
194}
195
196define i1 @add_ugecmp_i32_i16(i32 %x) nounwind {
197; CHECK-LABEL: add_ugecmp_i32_i16:
198; CHECK:       // %bb.0:
199; CHECK-NEXT:    sxth w8, w0
200; CHECK-NEXT:    cmp w8, w0
201; CHECK-NEXT:    cset w0, ne
202; CHECK-NEXT:    ret
203  %tmp0 = add i32 %x, 32768 ; 1U << (16-1)
204  %tmp1 = icmp uge i32 %tmp0, 65536 ; 1U << 16
205  ret i1 %tmp1
206}
207
208define i1 @add_ugecmp_i32_i8(i32 %x) nounwind {
209; CHECK-LABEL: add_ugecmp_i32_i8:
210; CHECK:       // %bb.0:
211; CHECK-NEXT:    sxtb w8, w0
212; CHECK-NEXT:    cmp w8, w0
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:    sxtw x8, w0
224; CHECK-NEXT:    cmp x8, x0
225; CHECK-NEXT:    cset w0, ne
226; CHECK-NEXT:    ret
227  %tmp0 = add i64 %x, 2147483648 ; 1U << (32-1)
228  %tmp1 = icmp uge i64 %tmp0, 4294967296 ; 1U << 32
229  ret i1 %tmp1
230}
231
232define i1 @add_ugecmp_i64_i16(i64 %x) nounwind {
233; CHECK-LABEL: add_ugecmp_i64_i16:
234; CHECK:       // %bb.0:
235; CHECK-NEXT:    sxth x8, w0
236; CHECK-NEXT:    cmp x8, x0
237; CHECK-NEXT:    cset w0, ne
238; CHECK-NEXT:    ret
239  %tmp0 = add i64 %x, 32768 ; 1U << (16-1)
240  %tmp1 = icmp uge i64 %tmp0, 65536 ; 1U << 16
241  ret i1 %tmp1
242}
243
244define i1 @add_ugecmp_i64_i8(i64 %x) nounwind {
245; CHECK-LABEL: add_ugecmp_i64_i8:
246; CHECK:       // %bb.0:
247; CHECK-NEXT:    sxtb x8, w0
248; CHECK-NEXT:    cmp x8, x0
249; CHECK-NEXT:    cset w0, ne
250; CHECK-NEXT:    ret
251  %tmp0 = add i64 %x, 128 ; 1U << (8-1)
252  %tmp1 = icmp uge i64 %tmp0, 256 ; 1U << 8
253  ret i1 %tmp1
254}
255
256; Slightly more canonical variant
257define i1 @add_ugtcmp_i16_i8(i16 %x) nounwind {
258; CHECK-LABEL: add_ugtcmp_i16_i8:
259; CHECK:       // %bb.0:
260; CHECK-NEXT:    sxtb w8, w0
261; CHECK-NEXT:    and w8, w8, #0xffff
262; CHECK-NEXT:    cmp w8, w0, uxth
263; CHECK-NEXT:    cset w0, ne
264; CHECK-NEXT:    ret
265  %tmp0 = add i16 %x, 128 ; 1U << (8-1)
266  %tmp1 = icmp ugt i16 %tmp0, 255 ; (1U << 8) - 1
267  ret i1 %tmp1
268}
269
270; Negative tests
271; ---------------------------------------------------------------------------- ;
272
273; Adding not a constant
274define i1 @add_ugecmp_bad_i16_i8_add(i16 %x, i16 %y) nounwind {
275; CHECK-LABEL: add_ugecmp_bad_i16_i8_add:
276; CHECK:       // %bb.0:
277; CHECK-NEXT:    add w8, w0, w1
278; CHECK-NEXT:    and w8, w8, #0xffff
279; CHECK-NEXT:    cmp w8, #255 // =255
280; CHECK-NEXT:    cset w0, hi
281; CHECK-NEXT:    ret
282  %tmp0 = add i16 %x, %y
283  %tmp1 = icmp uge i16 %tmp0, 256 ; 1U << 8
284  ret i1 %tmp1
285}
286
287; Comparing not with a constant
288define i1 @add_ugecmp_bad_i16_i8_cmp(i16 %x, i16 %y) nounwind {
289; CHECK-LABEL: add_ugecmp_bad_i16_i8_cmp:
290; CHECK:       // %bb.0:
291; CHECK-NEXT:    add w8, w0, #128 // =128
292; CHECK-NEXT:    and w8, w8, #0xffff
293; CHECK-NEXT:    cmp w8, w1, uxth
294; CHECK-NEXT:    cset w0, hs
295; CHECK-NEXT:    ret
296  %tmp0 = add i16 %x, 128 ; 1U << (8-1)
297  %tmp1 = icmp uge i16 %tmp0, %y
298  ret i1 %tmp1
299}
300
301; Second constant is not larger than the first one
302define i1 @add_ugecmp_bad_i8_i16(i16 %x) nounwind {
303; CHECK-LABEL: add_ugecmp_bad_i8_i16:
304; CHECK:       // %bb.0:
305; CHECK-NEXT:    add w8, w0, #128 // =128
306; CHECK-NEXT:    and w8, w8, #0xffff
307; CHECK-NEXT:    cmp w8, #127 // =127
308; CHECK-NEXT:    cset w0, hi
309; CHECK-NEXT:    ret
310  %tmp0 = add i16 %x, 128 ; 1U << (8-1)
311  %tmp1 = icmp uge i16 %tmp0, 128 ; 1U << (8-1)
312  ret i1 %tmp1
313}
314
315; First constant is not power of two
316define i1 @add_ugecmp_bad_i16_i8_c0notpoweroftwo(i16 %x) nounwind {
317; CHECK-LABEL: add_ugecmp_bad_i16_i8_c0notpoweroftwo:
318; CHECK:       // %bb.0:
319; CHECK-NEXT:    add w8, w0, #192 // =192
320; CHECK-NEXT:    and w8, w8, #0xffff
321; CHECK-NEXT:    cmp w8, #255 // =255
322; CHECK-NEXT:    cset w0, hi
323; CHECK-NEXT:    ret
324  %tmp0 = add i16 %x, 192 ; (1U << (8-1)) + (1U << (8-1-1))
325  %tmp1 = icmp uge i16 %tmp0, 256 ; 1U << 8
326  ret i1 %tmp1
327}
328
329; Second constant is not power of two
330define i1 @add_ugecmp_bad_i16_i8_c1notpoweroftwo(i16 %x) nounwind {
331; CHECK-LABEL: add_ugecmp_bad_i16_i8_c1notpoweroftwo:
332; CHECK:       // %bb.0:
333; CHECK-NEXT:    add w8, w0, #128 // =128
334; CHECK-NEXT:    and w8, w8, #0xffff
335; CHECK-NEXT:    cmp w8, #767 // =767
336; CHECK-NEXT:    cset w0, hi
337; CHECK-NEXT:    ret
338  %tmp0 = add i16 %x, 128 ; 1U << (8-1)
339  %tmp1 = icmp uge i16 %tmp0, 768 ; (1U << 8)) + (1U << (8+1))
340  ret i1 %tmp1
341}
342
343; Magic check fails, 64 << 1 != 256
344define i1 @add_ugecmp_bad_i16_i8_magic(i16 %x) nounwind {
345; CHECK-LABEL: add_ugecmp_bad_i16_i8_magic:
346; CHECK:       // %bb.0:
347; CHECK-NEXT:    add w8, w0, #64 // =64
348; CHECK-NEXT:    and w8, w8, #0xffff
349; CHECK-NEXT:    cmp w8, #255 // =255
350; CHECK-NEXT:    cset w0, hi
351; CHECK-NEXT:    ret
352  %tmp0 = add i16 %x, 64 ; 1U << (8-1-1)
353  %tmp1 = icmp uge i16 %tmp0, 256 ; 1U << 8
354  ret i1 %tmp1
355}
356
357; Bad 'destination type'
358define i1 @add_ugecmp_bad_i16_i4(i16 %x) nounwind {
359; CHECK-LABEL: add_ugecmp_bad_i16_i4:
360; CHECK:       // %bb.0:
361; CHECK-NEXT:    add w8, w0, #8 // =8
362; CHECK-NEXT:    and w8, w8, #0xffff
363; CHECK-NEXT:    cmp w8, #15 // =15
364; CHECK-NEXT:    cset w0, hi
365; CHECK-NEXT:    ret
366  %tmp0 = add i16 %x, 8 ; 1U << (4-1)
367  %tmp1 = icmp uge i16 %tmp0, 16 ; 1U << 4
368  ret i1 %tmp1
369}
370
371; Bad storage type
372define i1 @add_ugecmp_bad_i24_i8(i24 %x) nounwind {
373; CHECK-LABEL: add_ugecmp_bad_i24_i8:
374; CHECK:       // %bb.0:
375; CHECK-NEXT:    add w8, w0, #128 // =128
376; CHECK-NEXT:    and w8, w8, #0xffffff
377; CHECK-NEXT:    cmp w8, #255 // =255
378; CHECK-NEXT:    cset w0, hi
379; CHECK-NEXT:    ret
380  %tmp0 = add i24 %x, 128 ; 1U << (8-1)
381  %tmp1 = icmp uge i24 %tmp0, 256 ; 1U << 8
382  ret i1 %tmp1
383}
384
385; Slightly more canonical variant
386define i1 @add_ugtcmp_bad_i16_i8(i16 %x) nounwind {
387; CHECK-LABEL: add_ugtcmp_bad_i16_i8:
388; CHECK:       // %bb.0:
389; CHECK-NEXT:    mov w0, wzr
390; CHECK-NEXT:    ret
391  %tmp0 = add i16 %x, 128 ; 1U << (8-1)
392  %tmp1 = icmp ugt i16 %tmp0, -1 ; when we +1 it, it will wrap to 0
393  ret i1 %tmp1
394}
395