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
13 ; CHECK-NEXT: ldr w0, [x0] // on-fault: .LBB0_2
14 ; CHECK-NEXT: // %bb.1: // %not_null
16 ; CHECK-NEXT: .LBB0_2:
17 ; CHECK-NEXT: mov w0, #42
20 %c = icmp eq ptr %x, null
21 br i1 %c, label %is_null, label %not_null, !make.implicit !0
32 define i32 @imp_null_check_load_reorder(ptr %x) {
33 ; CHECK-LABEL: imp_null_check_load_reorder:
34 ; CHECK: // %bb.0: // %entry
36 ; CHECK-NEXT: ldr w0, [x0] // on-fault: .LBB1_2
37 ; CHECK-NEXT: // %bb.1: // %not_null
39 ; CHECK-NEXT: .LBB1_2:
40 ; CHECK-NEXT: mov w0, #42
43 %c = icmp eq ptr %x, null
44 br i1 %c, label %is_null, label %not_null, !make.implicit !0
54 define i32 @imp_null_check_unordered_load(ptr %x) {
55 ; CHECK-LABEL: imp_null_check_unordered_load:
56 ; CHECK: // %bb.0: // %entry
58 ; CHECK-NEXT: ldr w0, [x0] // on-fault: .LBB2_2
59 ; CHECK-NEXT: // %bb.1: // %not_null
61 ; CHECK-NEXT: .LBB2_2:
62 ; CHECK-NEXT: mov w0, #42
65 %c = icmp eq ptr %x, null
66 br i1 %c, label %is_null, label %not_null, !make.implicit !0
72 %t = load atomic i32, ptr %x unordered, align 4
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]
86 ; CHECK-NEXT: .LBB3_2:
87 ; CHECK-NEXT: mov w0, #42
90 %c = icmp eq ptr %x, null
91 br i1 %c, label %is_null, label %not_null, !make.implicit !0
97 %t = load atomic i32, ptr %x seq_cst, align 4
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]
109 ; CHECK-NEXT: .LBB4_2:
110 ; CHECK-NEXT: mov w0, #42
113 %c = icmp eq ptr %x, null
114 br i1 %c, label %is_null, label %not_null, !make.implicit !0
120 %t = load volatile i32, ptr %x, align 4
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
132 ; CHECK-NEXT: .LBB5_2:
133 ; CHECK-NEXT: mov w0, #42
136 %c = icmp eq ptr %x, null
137 br i1 %c, label %is_null, label %not_null, !make.implicit !0
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]
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
162 %c = icmp eq ptr %x, null
163 br i1 %c, label %is_null, label %not_null, !make.implicit !0
169 %t = load i256, ptr %x
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
182 ; CHECK-NEXT: .LBB7_2:
183 ; CHECK-NEXT: mov w0, #42
186 %c = icmp eq ptr %x, null
187 br i1 %c, label %is_null, label %not_null, !make.implicit !0
193 %x.gep = getelementptr i32, ptr %x, i32 32
194 %t = load i32, ptr %x.gep
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
206 ; CHECK-NEXT: .LBB8_2:
207 ; CHECK-NEXT: mov w0, #42
210 %c = icmp eq ptr %x, null
211 br i1 %c, label %is_null, label %not_null, !make.implicit !0
217 %t = load i32, ptr %x
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
233 ; CHECK-NEXT: .LBB9_2:
234 ; CHECK-NEXT: mov w0, #42
237 %c = icmp eq ptr %x, null
238 br i1 %c, label %is_null, label %not_null, !make.implicit !0
244 %p1 = udiv i32 %a, %b
245 %t = load i32, ptr %x
246 %res = add i32 %t, %p1
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]
262 ; CHECK-NEXT: .LBB10_2:
263 ; CHECK-NEXT: mov w0, #42
266 %c = icmp eq ptr %x, null
267 br i1 %c, label %is_null, label %not_null, !make.implicit !0
273 %t0 = load i32, ptr %y
274 %t1 = load i32, ptr %x
275 store i32 %t0, ptr %z
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
289 ; CHECK-NEXT: .LBB11_2:
290 ; CHECK-NEXT: mov w0, #42
293 %c = icmp eq ptr %x, null
294 br i1 %c, label %is_null, label %not_null, !make.implicit !0
300 %x.loc = getelementptr i32, ptr %x, i32 1
301 %y = ptrtoint ptr %x.loc to i32
303 %t = load i32, ptr %x
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]
318 ; CHECK-NEXT: .LBB12_2:
319 ; CHECK-NEXT: mov w0, #42
322 %c = icmp eq ptr %x, null
323 br i1 %c, label %is_null, label %not_null, !make.implicit !0
330 %t = load i32, ptr %x
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]
344 ; CHECK-NEXT: .LBB13_2:
345 ; CHECK-NEXT: mov w0, #42
348 %c = icmp eq ptr %x, null
349 br i1 %c, label %is_null, label %not_null, !make.implicit !0
356 %t = load i32, ptr %x
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
371 %c = icmp eq ptr %x, null
372 br i1 %c, label %is_null, label %not_null, !make.implicit !0
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
393 %c = icmp eq ptr %x, null
394 br i1 %c, label %is_null, label %not_null, !make.implicit !0
400 store atomic i32 1, ptr %x unordered, align 4
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
411 ; CHECK-NEXT: .LBB16_2:
412 ; CHECK-NEXT: mov w0, #42
415 %c = icmp eq ptr %x, null
416 br i1 %c, label %is_null, label %not_null, !make.implicit !0
422 %x.gep = getelementptr i32, ptr %x, i32 -32
423 %t = load i32, ptr %x.gep