1; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2; RUN: llc < %s -verify-machineinstrs -O3 -mtriple=aarch64-unknown-unknown -enable-implicit-null-checks | FileCheck %s
3
4; Basic test for implicit null check conversion - this is analogous to the
5; file with the same name in the X86 tree, but adjusted to remove patterns
6; related to memory folding of arithmetic (since aarch64 doesn't), and add
7; a couple of aarch64 specific tests.
8
9define i32 @imp_null_check_load_fallthrough(i32* %x) {
10; CHECK-LABEL: imp_null_check_load_fallthrough:
11; CHECK:       // %bb.0: // %entry
12; CHECK-NEXT:  .Ltmp0:
13; CHECK-NEXT:    ldr w0, [x0] // on-fault: .LBB0_2
14; CHECK-NEXT:  // %bb.1: // %not_null
15; CHECK-NEXT:    ret
16; CHECK-NEXT:  .LBB0_2: // %is_null
17; CHECK-NEXT:    mov w0, #42
18; CHECK-NEXT:    ret
19 entry:
20  %c = icmp eq i32* %x, null
21  br i1 %c, label %is_null, label %not_null, !make.implicit !0
22
23 not_null:
24  %t = load i32, i32* %x
25  ret i32 %t
26
27is_null:
28  ret i32 42
29}
30
31
32define i32 @imp_null_check_load_reorder(i32* %x) {
33; CHECK-LABEL: imp_null_check_load_reorder:
34; CHECK:       // %bb.0: // %entry
35; CHECK-NEXT:  .Ltmp1:
36; CHECK-NEXT:    ldr w0, [x0] // on-fault: .LBB1_2
37; CHECK-NEXT:  // %bb.1: // %not_null
38; CHECK-NEXT:    ret
39; CHECK-NEXT:  .LBB1_2: // %is_null
40; CHECK-NEXT:    mov w0, #42
41; CHECK-NEXT:    ret
42 entry:
43  %c = icmp eq i32* %x, null
44  br i1 %c, label %is_null, label %not_null, !make.implicit !0
45
46 is_null:
47  ret i32 42
48
49 not_null:
50  %t = load i32, i32* %x
51  ret i32 %t
52}
53
54define i32 @imp_null_check_unordered_load(i32* %x) {
55; CHECK-LABEL: imp_null_check_unordered_load:
56; CHECK:       // %bb.0: // %entry
57; CHECK-NEXT:  .Ltmp2:
58; CHECK-NEXT:    ldr w0, [x0] // on-fault: .LBB2_2
59; CHECK-NEXT:  // %bb.1: // %not_null
60; CHECK-NEXT:    ret
61; CHECK-NEXT:  .LBB2_2: // %is_null
62; CHECK-NEXT:    mov w0, #42
63; CHECK-NEXT:    ret
64 entry:
65  %c = icmp eq i32* %x, null
66  br i1 %c, label %is_null, label %not_null, !make.implicit !0
67
68 is_null:
69  ret i32 42
70
71 not_null:
72  %t = load atomic i32, i32* %x unordered, align 4
73  ret i32 %t
74}
75
76
77; TODO: Can be converted into implicit check.
78;; Probably could be implicit, but we're conservative for now
79define i32 @imp_null_check_seq_cst_load(i32* %x) {
80; CHECK-LABEL: imp_null_check_seq_cst_load:
81; CHECK:       // %bb.0: // %entry
82; CHECK-NEXT:    cbz x0, .LBB3_2
83; CHECK-NEXT:  // %bb.1: // %not_null
84; CHECK-NEXT:    ldar w0, [x0]
85; CHECK-NEXT:    ret
86; CHECK-NEXT:  .LBB3_2: // %is_null
87; CHECK-NEXT:    mov w0, #42
88; CHECK-NEXT:    ret
89 entry:
90  %c = icmp eq i32* %x, null
91  br i1 %c, label %is_null, label %not_null, !make.implicit !0
92
93 is_null:
94  ret i32 42
95
96 not_null:
97  %t = load atomic i32, i32* %x seq_cst, align 4
98  ret i32 %t
99}
100
101;; Might be memory mapped IO, so can't rely on fault behavior
102define i32 @imp_null_check_volatile_load(i32* %x) {
103; CHECK-LABEL: imp_null_check_volatile_load:
104; CHECK:       // %bb.0: // %entry
105; CHECK-NEXT:    cbz x0, .LBB4_2
106; CHECK-NEXT:  // %bb.1: // %not_null
107; CHECK-NEXT:    ldr w0, [x0]
108; CHECK-NEXT:    ret
109; CHECK-NEXT:  .LBB4_2: // %is_null
110; CHECK-NEXT:    mov w0, #42
111; CHECK-NEXT:    ret
112 entry:
113  %c = icmp eq i32* %x, null
114  br i1 %c, label %is_null, label %not_null, !make.implicit !0
115
116 is_null:
117  ret i32 42
118
119 not_null:
120  %t = load volatile i32, i32* %x, align 4
121  ret i32 %t
122}
123
124
125define i8 @imp_null_check_load_i8(i8* %x) {
126; CHECK-LABEL: imp_null_check_load_i8:
127; CHECK:       // %bb.0: // %entry
128; CHECK-NEXT:  .Ltmp3:
129; CHECK-NEXT:    ldrb w0, [x0] // on-fault: .LBB5_2
130; CHECK-NEXT:  // %bb.1: // %not_null
131; CHECK-NEXT:    ret
132; CHECK-NEXT:  .LBB5_2: // %is_null
133; CHECK-NEXT:    mov w0, #42
134; CHECK-NEXT:    ret
135 entry:
136  %c = icmp eq i8* %x, null
137  br i1 %c, label %is_null, label %not_null, !make.implicit !0
138
139 is_null:
140  ret i8 42
141
142 not_null:
143  %t = load i8, i8* %x
144  ret i8 %t
145}
146
147define i256 @imp_null_check_load_i256(i256* %x) {
148; CHECK-LABEL: imp_null_check_load_i256:
149; CHECK:       // %bb.0: // %entry
150; CHECK-NEXT:    cbz x0, .LBB6_2
151; CHECK-NEXT:  // %bb.1: // %not_null
152; CHECK-NEXT:    ldp x8, x1, [x0]
153; CHECK-NEXT:    ldp x2, x3, [x0, #16]
154; CHECK-NEXT:    mov x0, x8
155; CHECK-NEXT:    ret
156; CHECK-NEXT:  .LBB6_2: // %is_null
157; CHECK-NEXT:    mov w0, #42
158; CHECK-NEXT:    mov x1, xzr
159; CHECK-NEXT:    mov x2, xzr
160; CHECK-NEXT:    mov x3, xzr
161; CHECK-NEXT:    ret
162 entry:
163  %c = icmp eq i256* %x, null
164  br i1 %c, label %is_null, label %not_null, !make.implicit !0
165
166 is_null:
167  ret i256 42
168
169 not_null:
170  %t = load i256, i256* %x
171  ret i256 %t
172}
173
174
175
176define i32 @imp_null_check_gep_load(i32* %x) {
177; CHECK-LABEL: imp_null_check_gep_load:
178; CHECK:       // %bb.0: // %entry
179; CHECK-NEXT:  .Ltmp4:
180; CHECK-NEXT:    ldr w0, [x0, #128] // on-fault: .LBB7_2
181; CHECK-NEXT:  // %bb.1: // %not_null
182; CHECK-NEXT:    ret
183; CHECK-NEXT:  .LBB7_2: // %is_null
184; CHECK-NEXT:    mov w0, #42
185; CHECK-NEXT:    ret
186 entry:
187  %c = icmp eq i32* %x, null
188  br i1 %c, label %is_null, label %not_null, !make.implicit !0
189
190 is_null:
191  ret i32 42
192
193 not_null:
194  %x.gep = getelementptr i32, i32* %x, i32 32
195  %t = load i32, i32* %x.gep
196  ret i32 %t
197}
198
199define i32 @imp_null_check_add_result(i32* %x, i32 %p) {
200; CHECK-LABEL: imp_null_check_add_result:
201; CHECK:       // %bb.0: // %entry
202; CHECK-NEXT:  .Ltmp5:
203; CHECK-NEXT:    ldr w8, [x0] // on-fault: .LBB8_2
204; CHECK-NEXT:  // %bb.1: // %not_null
205; CHECK-NEXT:    add w0, w8, w1
206; CHECK-NEXT:    ret
207; CHECK-NEXT:  .LBB8_2: // %is_null
208; CHECK-NEXT:    mov w0, #42
209; CHECK-NEXT:    ret
210 entry:
211  %c = icmp eq i32* %x, null
212  br i1 %c, label %is_null, label %not_null, !make.implicit !0
213
214 is_null:
215  ret i32 42
216
217 not_null:
218  %t = load i32, i32* %x
219  %p1 = add i32 %t, %p
220  ret i32 %p1
221}
222
223; Can hoist over a potential faulting instruction as long as we don't
224; change the conditions under which the instruction faults.
225define i32 @imp_null_check_hoist_over_udiv(i32* %x, i32 %a, i32 %b) {
226; CHECK-LABEL: imp_null_check_hoist_over_udiv:
227; CHECK:       // %bb.0: // %entry
228; CHECK-NEXT:  .Ltmp6:
229; CHECK-NEXT:    ldr w8, [x0] // on-fault: .LBB9_2
230; CHECK-NEXT:  // %bb.1: // %not_null
231; CHECK-NEXT:    udiv w9, w1, w2
232; CHECK-NEXT:    add w0, w8, w9
233; CHECK-NEXT:    ret
234; CHECK-NEXT:  .LBB9_2: // %is_null
235; CHECK-NEXT:    mov w0, #42
236; CHECK-NEXT:    ret
237 entry:
238  %c = icmp eq i32* %x, null
239  br i1 %c, label %is_null, label %not_null, !make.implicit !0
240
241 is_null:
242  ret i32 42
243
244 not_null:
245  %p1 = udiv i32 %a, %b
246  %t = load i32, i32* %x
247  %res = add i32 %t, %p1
248  ret i32 %res
249}
250
251
252; TODO: We should be able to hoist this - we can on x86, why isn't this
253; working for aarch64?  Aliasing?
254define i32 @imp_null_check_hoist_over_unrelated_load(i32* %x, i32* %y, i32* %z) {
255; CHECK-LABEL: imp_null_check_hoist_over_unrelated_load:
256; CHECK:       // %bb.0: // %entry
257; CHECK-NEXT:    cbz x0, .LBB10_2
258; CHECK-NEXT:  // %bb.1: // %not_null
259; CHECK-NEXT:    ldr w8, [x1]
260; CHECK-NEXT:    ldr w0, [x0]
261; CHECK-NEXT:    str w8, [x2]
262; CHECK-NEXT:    ret
263; CHECK-NEXT:  .LBB10_2: // %is_null
264; CHECK-NEXT:    mov w0, #42
265; CHECK-NEXT:    ret
266 entry:
267  %c = icmp eq i32* %x, null
268  br i1 %c, label %is_null, label %not_null, !make.implicit !0
269
270 is_null:
271  ret i32 42
272
273 not_null:
274  %t0 = load i32, i32* %y
275  %t1 = load i32, i32* %x
276  store i32 %t0, i32* %z
277  ret i32 %t1
278}
279
280define i32 @imp_null_check_gep_load_with_use_dep(i32* %x, i32 %a) {
281; CHECK-LABEL: imp_null_check_gep_load_with_use_dep:
282; CHECK:       // %bb.0: // %entry
283; CHECK-NEXT:  .Ltmp7:
284; CHECK-NEXT:    ldr w8, [x0] // on-fault: .LBB11_2
285; CHECK-NEXT:  // %bb.1: // %not_null
286; CHECK-NEXT:    add w9, w0, w1
287; CHECK-NEXT:    add w8, w9, w8
288; CHECK-NEXT:    add w0, w8, #4 // =4
289; CHECK-NEXT:    ret
290; CHECK-NEXT:  .LBB11_2: // %is_null
291; CHECK-NEXT:    mov w0, #42
292; CHECK-NEXT:    ret
293 entry:
294  %c = icmp eq i32* %x, null
295  br i1 %c, label %is_null, label %not_null, !make.implicit !0
296
297 is_null:
298  ret i32 42
299
300 not_null:
301  %x.loc = getelementptr i32, i32* %x, i32 1
302  %y = ptrtoint i32* %x.loc to i32
303  %b = add i32 %a, %y
304  %t = load i32, i32* %x
305  %z = add i32 %t, %b
306  ret i32 %z
307}
308
309;; TODO: We could handle this case as we can lift the fence into the
310;; previous block before the conditional without changing behavior.
311define i32 @imp_null_check_load_fence1(i32* %x) {
312; CHECK-LABEL: imp_null_check_load_fence1:
313; CHECK:       // %bb.0: // %entry
314; CHECK-NEXT:    cbz x0, .LBB12_2
315; CHECK-NEXT:  // %bb.1: // %not_null
316; CHECK-NEXT:    dmb ishld
317; CHECK-NEXT:    ldr w0, [x0]
318; CHECK-NEXT:    ret
319; CHECK-NEXT:  .LBB12_2: // %is_null
320; CHECK-NEXT:    mov w0, #42
321; CHECK-NEXT:    ret
322entry:
323  %c = icmp eq i32* %x, null
324  br i1 %c, label %is_null, label %not_null, !make.implicit !0
325
326is_null:
327  ret i32 42
328
329not_null:
330  fence acquire
331  %t = load i32, i32* %x
332  ret i32 %t
333}
334
335;; TODO: We could handle this case as we can lift the fence into the
336;; previous block before the conditional without changing behavior.
337define i32 @imp_null_check_load_fence2(i32* %x) {
338; CHECK-LABEL: imp_null_check_load_fence2:
339; CHECK:       // %bb.0: // %entry
340; CHECK-NEXT:    cbz x0, .LBB13_2
341; CHECK-NEXT:  // %bb.1: // %not_null
342; CHECK-NEXT:    dmb ish
343; CHECK-NEXT:    ldr w0, [x0]
344; CHECK-NEXT:    ret
345; CHECK-NEXT:  .LBB13_2: // %is_null
346; CHECK-NEXT:    mov w0, #42
347; CHECK-NEXT:    ret
348entry:
349  %c = icmp eq i32* %x, null
350  br i1 %c, label %is_null, label %not_null, !make.implicit !0
351
352is_null:
353  ret i32 42
354
355not_null:
356  fence seq_cst
357  %t = load i32, i32* %x
358  ret i32 %t
359}
360
361; TODO: We can fold to implicit null here, not sure why this isn't working
362define void @imp_null_check_store(i32* %x) {
363; CHECK-LABEL: imp_null_check_store:
364; CHECK:       // %bb.0: // %entry
365; CHECK-NEXT:    cbz x0, .LBB14_2
366; CHECK-NEXT:  // %bb.1: // %not_null
367; CHECK-NEXT:    mov w8, #1
368; CHECK-NEXT:    str w8, [x0]
369; CHECK-NEXT:    ret
370; CHECK-NEXT:  .LBB14_2: // %is_null
371; CHECK-NEXT:    ret
372 entry:
373  %c = icmp eq i32* %x, null
374  br i1 %c, label %is_null, label %not_null, !make.implicit !0
375
376 is_null:
377  ret void
378
379 not_null:
380  store i32 1, i32* %x
381  ret void
382}
383
384;; TODO: can be implicit
385define void @imp_null_check_unordered_store(i32* %x) {
386; CHECK-LABEL: imp_null_check_unordered_store:
387; CHECK:       // %bb.0: // %entry
388; CHECK-NEXT:    cbz x0, .LBB15_2
389; CHECK-NEXT:  // %bb.1: // %not_null
390; CHECK-NEXT:    mov w8, #1
391; CHECK-NEXT:    str w8, [x0]
392; CHECK-NEXT:    ret
393; CHECK-NEXT:  .LBB15_2: // %is_null
394; CHECK-NEXT:    ret
395 entry:
396  %c = icmp eq i32* %x, null
397  br i1 %c, label %is_null, label %not_null, !make.implicit !0
398
399 is_null:
400  ret void
401
402 not_null:
403  store atomic i32 1, i32* %x unordered, align 4
404  ret void
405}
406
407define i32 @imp_null_check_neg_gep_load(i32* %x) {
408; CHECK-LABEL: imp_null_check_neg_gep_load:
409; CHECK:       // %bb.0: // %entry
410; CHECK-NEXT:  .Ltmp8:
411; CHECK-NEXT:    ldur w0, [x0, #-128] // on-fault: .LBB16_2
412; CHECK-NEXT:  // %bb.1: // %not_null
413; CHECK-NEXT:    ret
414; CHECK-NEXT:  .LBB16_2: // %is_null
415; CHECK-NEXT:    mov w0, #42
416; CHECK-NEXT:    ret
417 entry:
418  %c = icmp eq i32* %x, null
419  br i1 %c, label %is_null, label %not_null, !make.implicit !0
420
421 is_null:
422  ret i32 42
423
424 not_null:
425  %x.gep = getelementptr i32, i32* %x, i32 -32
426  %t = load i32, i32* %x.gep
427  ret i32 %t
428}
429
430!0 = !{}
431