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