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(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 22 23 not_null: 24 %t = load i32, ptr %x 25 ret i32 %t 26 27is_null: 28 ret i32 42 29} 30 31 32define 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 45 46 is_null: 47 ret i32 42 48 49 not_null: 50 %t = load i32, ptr %x 51 ret i32 %t 52} 53 54define 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 67 68 is_null: 69 ret i32 42 70 71 not_null: 72 %t = load atomic i32, ptr %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(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 92 93 is_null: 94 ret i32 42 95 96 not_null: 97 %t = load atomic i32, ptr %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(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 115 116 is_null: 117 ret i32 42 118 119 not_null: 120 %t = load volatile i32, ptr %x, align 4 121 ret i32 %t 122} 123 124 125define 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 138 139 is_null: 140 ret i8 42 141 142 not_null: 143 %t = load i8, ptr %x 144 ret i8 %t 145} 146 147define 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 164 165 is_null: 166 ret i256 42 167 168 not_null: 169 %t = load i256, ptr %x 170 ret i256 %t 171} 172 173 174 175define 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 188 189 is_null: 190 ret i32 42 191 192 not_null: 193 %x.gep = getelementptr i32, ptr %x, i32 32 194 %t = load i32, ptr %x.gep 195 ret i32 %t 196} 197 198define 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 212 213 is_null: 214 ret i32 42 215 216 not_null: 217 %t = load i32, ptr %x 218 %p1 = add i32 %t, %p 219 ret i32 %p1 220} 221 222; Can hoist over a potential faulting instruction as long as we don't 223; change the conditions under which the instruction faults. 224define 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 239 240 is_null: 241 ret i32 42 242 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 248} 249 250 251; TODO: We should be able to hoist this - we can on x86, why isn't this 252; working for aarch64? Aliasing? 253define 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 268 269 is_null: 270 ret i32 42 271 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 277} 278 279define 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 295 296 is_null: 297 ret i32 42 298 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 306} 307 308;; TODO: We could handle this case as we can lift the fence into the 309;; previous block before the conditional without changing behavior. 310define 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 321entry: 322 %c = icmp eq ptr %x, null 323 br i1 %c, label %is_null, label %not_null, !make.implicit !0 324 325is_null: 326 ret i32 42 327 328not_null: 329 fence acquire 330 %t = load i32, ptr %x 331 ret i32 %t 332} 333 334;; TODO: We could handle this case as we can lift the fence into the 335;; previous block before the conditional without changing behavior. 336define 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 347entry: 348 %c = icmp eq ptr %x, null 349 br i1 %c, label %is_null, label %not_null, !make.implicit !0 350 351is_null: 352 ret i32 42 353 354not_null: 355 fence seq_cst 356 %t = load i32, ptr %x 357 ret i32 %t 358} 359 360; TODO: We can fold to implicit null here, not sure why this isn't working 361define 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 373 374 is_null: 375 ret void 376 377 not_null: 378 store i32 1, ptr %x 379 ret void 380} 381 382;; TODO: can be implicit 383define 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 395 396 is_null: 397 ret void 398 399 not_null: 400 store atomic i32 1, ptr %x unordered, align 4 401 ret void 402} 403 404define 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 417 418 is_null: 419 ret i32 42 420 421 not_null: 422 %x.gep = getelementptr i32, ptr %x, i32 -32 423 %t = load i32, ptr %x.gep 424 ret i32 %t 425} 426 427!0 = !{} 428