Bump version to 19.1.0-rc3
[llvm-project.git] / llvm / test / CodeGen / AArch64 / implicit-null-check.ll
blob052ff7f0fe5d094022c667502a482efffb372aa0
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
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.
9 define i32 @imp_null_check_load_fallthrough(ptr %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:
17 ; CHECK-NEXT:    mov w0, #42
18 ; CHECK-NEXT:    ret
19  entry:
20   %c = icmp eq ptr %x, null
21   br i1 %c, label %is_null, label %not_null, !make.implicit !0
23  not_null:
24   %t = load i32, ptr %x
25   ret i32 %t
27 is_null:
28   ret i32 42
32 define i32 @imp_null_check_load_reorder(ptr %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:
40 ; CHECK-NEXT:    mov w0, #42
41 ; CHECK-NEXT:    ret
42  entry:
43   %c = icmp eq ptr %x, null
44   br i1 %c, label %is_null, label %not_null, !make.implicit !0
46  is_null:
47   ret i32 42
49  not_null:
50   %t = load i32, ptr %x
51   ret i32 %t
54 define i32 @imp_null_check_unordered_load(ptr %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:
62 ; CHECK-NEXT:    mov w0, #42
63 ; CHECK-NEXT:    ret
64  entry:
65   %c = icmp eq ptr %x, null
66   br i1 %c, label %is_null, label %not_null, !make.implicit !0
68  is_null:
69   ret i32 42
71  not_null:
72   %t = load atomic i32, ptr %x unordered, align 4
73   ret i32 %t
77 ; TODO: Can be converted into implicit check.
78 ;; Probably could be implicit, but we're conservative for now
79 define i32 @imp_null_check_seq_cst_load(ptr %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:
87 ; CHECK-NEXT:    mov w0, #42
88 ; CHECK-NEXT:    ret
89  entry:
90   %c = icmp eq ptr %x, null
91   br i1 %c, label %is_null, label %not_null, !make.implicit !0
93  is_null:
94   ret i32 42
96  not_null:
97   %t = load atomic i32, ptr %x seq_cst, align 4
98   ret i32 %t
101 ;; Might be memory mapped IO, so can't rely on fault behavior
102 define i32 @imp_null_check_volatile_load(ptr %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:
110 ; CHECK-NEXT:    mov w0, #42
111 ; CHECK-NEXT:    ret
112  entry:
113   %c = icmp eq ptr %x, null
114   br i1 %c, label %is_null, label %not_null, !make.implicit !0
116  is_null:
117   ret i32 42
119  not_null:
120   %t = load volatile i32, ptr %x, align 4
121   ret i32 %t
125 define i8 @imp_null_check_load_i8(ptr %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:
133 ; CHECK-NEXT:    mov w0, #42
134 ; CHECK-NEXT:    ret
135  entry:
136   %c = icmp eq ptr %x, null
137   br i1 %c, label %is_null, label %not_null, !make.implicit !0
139  is_null:
140   ret i8 42
142  not_null:
143   %t = load i8, ptr %x
144   ret i8 %t
147 define i256 @imp_null_check_load_i256(ptr %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 x2, x3, [x0, #16]
153 ; CHECK-NEXT:    ldp x0, x1, [x0]
154 ; CHECK-NEXT:    ret
155 ; CHECK-NEXT:  .LBB6_2:
156 ; CHECK-NEXT:    mov x1, xzr
157 ; CHECK-NEXT:    mov x2, xzr
158 ; CHECK-NEXT:    mov x3, xzr
159 ; CHECK-NEXT:    mov w0, #42
160 ; CHECK-NEXT:    ret
161  entry:
162   %c = icmp eq ptr %x, null
163   br i1 %c, label %is_null, label %not_null, !make.implicit !0
165  is_null:
166   ret i256 42
168  not_null:
169   %t = load i256, ptr %x
170   ret i256 %t
175 define i32 @imp_null_check_gep_load(ptr %x) {
176 ; CHECK-LABEL: imp_null_check_gep_load:
177 ; CHECK:       // %bb.0: // %entry
178 ; CHECK-NEXT:  .Ltmp4:
179 ; CHECK-NEXT:    ldr w0, [x0, #128] // on-fault: .LBB7_2
180 ; CHECK-NEXT:  // %bb.1: // %not_null
181 ; CHECK-NEXT:    ret
182 ; CHECK-NEXT:  .LBB7_2:
183 ; CHECK-NEXT:    mov w0, #42
184 ; CHECK-NEXT:    ret
185  entry:
186   %c = icmp eq ptr %x, null
187   br i1 %c, label %is_null, label %not_null, !make.implicit !0
189  is_null:
190   ret i32 42
192  not_null:
193   %x.gep = getelementptr i32, ptr %x, i32 32
194   %t = load i32, ptr %x.gep
195   ret i32 %t
198 define i32 @imp_null_check_add_result(ptr %x, i32 %p) {
199 ; CHECK-LABEL: imp_null_check_add_result:
200 ; CHECK:       // %bb.0: // %entry
201 ; CHECK-NEXT:  .Ltmp5:
202 ; CHECK-NEXT:    ldr w8, [x0] // on-fault: .LBB8_2
203 ; CHECK-NEXT:  // %bb.1: // %not_null
204 ; CHECK-NEXT:    add w0, w8, w1
205 ; CHECK-NEXT:    ret
206 ; CHECK-NEXT:  .LBB8_2:
207 ; CHECK-NEXT:    mov w0, #42
208 ; CHECK-NEXT:    ret
209  entry:
210   %c = icmp eq ptr %x, null
211   br i1 %c, label %is_null, label %not_null, !make.implicit !0
213  is_null:
214   ret i32 42
216  not_null:
217   %t = load i32, ptr %x
218   %p1 = add i32 %t, %p
219   ret i32 %p1
222 ; Can hoist over a potential faulting instruction as long as we don't
223 ; change the conditions under which the instruction faults.
224 define i32 @imp_null_check_hoist_over_udiv(ptr %x, i32 %a, i32 %b) {
225 ; CHECK-LABEL: imp_null_check_hoist_over_udiv:
226 ; CHECK:       // %bb.0: // %entry
227 ; CHECK-NEXT:    cbz x0, .LBB9_2
228 ; CHECK-NEXT:  // %bb.1: // %not_null
229 ; CHECK-NEXT:    udiv w8, w1, w2
230 ; CHECK-NEXT:    ldr w9, [x0]
231 ; CHECK-NEXT:    add w0, w9, w8
232 ; CHECK-NEXT:    ret
233 ; CHECK-NEXT:  .LBB9_2:
234 ; CHECK-NEXT:    mov w0, #42
235 ; CHECK-NEXT:    ret
236  entry:
237   %c = icmp eq ptr %x, null
238   br i1 %c, label %is_null, label %not_null, !make.implicit !0
240  is_null:
241   ret i32 42
243  not_null:
244   %p1 = udiv i32 %a, %b
245   %t = load i32, ptr %x
246   %res = add i32 %t, %p1
247   ret i32 %res
251 ; TODO: We should be able to hoist this - we can on x86, why isn't this
252 ; working for aarch64?  Aliasing?
253 define i32 @imp_null_check_hoist_over_unrelated_load(ptr %x, ptr %y, ptr %z) {
254 ; CHECK-LABEL: imp_null_check_hoist_over_unrelated_load:
255 ; CHECK:       // %bb.0: // %entry
256 ; CHECK-NEXT:    cbz x0, .LBB10_2
257 ; CHECK-NEXT:  // %bb.1: // %not_null
258 ; CHECK-NEXT:    ldr w8, [x1]
259 ; CHECK-NEXT:    ldr w0, [x0]
260 ; CHECK-NEXT:    str w8, [x2]
261 ; CHECK-NEXT:    ret
262 ; CHECK-NEXT:  .LBB10_2:
263 ; CHECK-NEXT:    mov w0, #42
264 ; CHECK-NEXT:    ret
265  entry:
266   %c = icmp eq ptr %x, null
267   br i1 %c, label %is_null, label %not_null, !make.implicit !0
269  is_null:
270   ret i32 42
272  not_null:
273   %t0 = load i32, ptr %y
274   %t1 = load i32, ptr %x
275   store i32 %t0, ptr %z
276   ret i32 %t1
279 define i32 @imp_null_check_gep_load_with_use_dep(ptr %x, i32 %a) {
280 ; CHECK-LABEL: imp_null_check_gep_load_with_use_dep:
281 ; CHECK:       // %bb.0: // %entry
282 ; CHECK-NEXT:  .Ltmp6:
283 ; CHECK-NEXT:    ldr w8, [x0] // on-fault: .LBB11_2
284 ; CHECK-NEXT:  // %bb.1: // %not_null
285 ; CHECK-NEXT:    add w9, w0, w1
286 ; CHECK-NEXT:    add w8, w9, w8
287 ; CHECK-NEXT:    add w0, w8, #4
288 ; CHECK-NEXT:    ret
289 ; CHECK-NEXT:  .LBB11_2:
290 ; CHECK-NEXT:    mov w0, #42
291 ; CHECK-NEXT:    ret
292  entry:
293   %c = icmp eq ptr %x, null
294   br i1 %c, label %is_null, label %not_null, !make.implicit !0
296  is_null:
297   ret i32 42
299  not_null:
300   %x.loc = getelementptr i32, ptr %x, i32 1
301   %y = ptrtoint ptr %x.loc to i32
302   %b = add i32 %a, %y
303   %t = load i32, ptr %x
304   %z = add i32 %t, %b
305   ret i32 %z
308 ;; TODO: We could handle this case as we can lift the fence into the
309 ;; previous block before the conditional without changing behavior.
310 define i32 @imp_null_check_load_fence1(ptr %x) {
311 ; CHECK-LABEL: imp_null_check_load_fence1:
312 ; CHECK:       // %bb.0: // %entry
313 ; CHECK-NEXT:    cbz x0, .LBB12_2
314 ; CHECK-NEXT:  // %bb.1: // %not_null
315 ; CHECK-NEXT:    dmb ishld
316 ; CHECK-NEXT:    ldr w0, [x0]
317 ; CHECK-NEXT:    ret
318 ; CHECK-NEXT:  .LBB12_2:
319 ; CHECK-NEXT:    mov w0, #42
320 ; CHECK-NEXT:    ret
321 entry:
322   %c = icmp eq ptr %x, null
323   br i1 %c, label %is_null, label %not_null, !make.implicit !0
325 is_null:
326   ret i32 42
328 not_null:
329   fence acquire
330   %t = load i32, ptr %x
331   ret i32 %t
334 ;; TODO: We could handle this case as we can lift the fence into the
335 ;; previous block before the conditional without changing behavior.
336 define i32 @imp_null_check_load_fence2(ptr %x) {
337 ; CHECK-LABEL: imp_null_check_load_fence2:
338 ; CHECK:       // %bb.0: // %entry
339 ; CHECK-NEXT:    cbz x0, .LBB13_2
340 ; CHECK-NEXT:  // %bb.1: // %not_null
341 ; CHECK-NEXT:    dmb ish
342 ; CHECK-NEXT:    ldr w0, [x0]
343 ; CHECK-NEXT:    ret
344 ; CHECK-NEXT:  .LBB13_2:
345 ; CHECK-NEXT:    mov w0, #42
346 ; CHECK-NEXT:    ret
347 entry:
348   %c = icmp eq ptr %x, null
349   br i1 %c, label %is_null, label %not_null, !make.implicit !0
351 is_null:
352   ret i32 42
354 not_null:
355   fence seq_cst
356   %t = load i32, ptr %x
357   ret i32 %t
360 ; TODO: We can fold to implicit null here, not sure why this isn't working
361 define void @imp_null_check_store(ptr %x) {
362 ; CHECK-LABEL: imp_null_check_store:
363 ; CHECK:       // %bb.0: // %entry
364 ; CHECK-NEXT:    cbz x0, .LBB14_2
365 ; CHECK-NEXT:  // %bb.1: // %not_null
366 ; CHECK-NEXT:    mov w8, #1
367 ; CHECK-NEXT:    str w8, [x0]
368 ; CHECK-NEXT:  .LBB14_2: // %common.ret
369 ; CHECK-NEXT:    ret
370  entry:
371   %c = icmp eq ptr %x, null
372   br i1 %c, label %is_null, label %not_null, !make.implicit !0
374  is_null:
375   ret void
377  not_null:
378   store i32 1, ptr %x
379   ret void
382 ;; TODO: can be implicit
383 define void @imp_null_check_unordered_store(ptr %x) {
384 ; CHECK-LABEL: imp_null_check_unordered_store:
385 ; CHECK:       // %bb.0: // %entry
386 ; CHECK-NEXT:    cbz x0, .LBB15_2
387 ; CHECK-NEXT:  // %bb.1: // %not_null
388 ; CHECK-NEXT:    mov w8, #1
389 ; CHECK-NEXT:    str w8, [x0]
390 ; CHECK-NEXT:  .LBB15_2: // %common.ret
391 ; CHECK-NEXT:    ret
392  entry:
393   %c = icmp eq ptr %x, null
394   br i1 %c, label %is_null, label %not_null, !make.implicit !0
396  is_null:
397   ret void
399  not_null:
400   store atomic i32 1, ptr %x unordered, align 4
401   ret void
404 define i32 @imp_null_check_neg_gep_load(ptr %x) {
405 ; CHECK-LABEL: imp_null_check_neg_gep_load:
406 ; CHECK:       // %bb.0: // %entry
407 ; CHECK-NEXT:  .Ltmp7:
408 ; CHECK-NEXT:    ldur w0, [x0, #-128] // on-fault: .LBB16_2
409 ; CHECK-NEXT:  // %bb.1: // %not_null
410 ; CHECK-NEXT:    ret
411 ; CHECK-NEXT:  .LBB16_2:
412 ; CHECK-NEXT:    mov w0, #42
413 ; CHECK-NEXT:    ret
414  entry:
415   %c = icmp eq ptr %x, null
416   br i1 %c, label %is_null, label %not_null, !make.implicit !0
418  is_null:
419   ret i32 42
421  not_null:
422   %x.gep = getelementptr i32, ptr %x, i32 -32
423   %t = load i32, ptr %x.gep
424   ret i32 %t
427 !0 = !{}