428 lines · plain
1; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py2; RUN: llc < %s -verify-machineinstrs -O3 -mtriple=aarch64-unknown-unknown -enable-implicit-null-checks | FileCheck %s3 4; Basic test for implicit null check conversion - this is analogous to the5; file with the same name in the X86 tree, but adjusted to remove patterns6; related to memory folding of arithmetic (since aarch64 doesn't), and add7; 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: // %entry12; CHECK-NEXT: .Ltmp0:13; CHECK-NEXT: ldr w0, [x0] // on-fault: .LBB0_214; CHECK-NEXT: // %bb.1: // %not_null15; CHECK-NEXT: ret16; CHECK-NEXT: .LBB0_2:17; CHECK-NEXT: mov w0, #4218; CHECK-NEXT: ret19 entry:20 %c = icmp eq ptr %x, null21 br i1 %c, label %is_null, label %not_null, !make.implicit !022 23 not_null:24 %t = load i32, ptr %x25 ret i32 %t26 27is_null:28 ret i32 4229}30 31 32define i32 @imp_null_check_load_reorder(ptr %x) {33; CHECK-LABEL: imp_null_check_load_reorder:34; CHECK: // %bb.0: // %entry35; CHECK-NEXT: .Ltmp1:36; CHECK-NEXT: ldr w0, [x0] // on-fault: .LBB1_237; CHECK-NEXT: // %bb.1: // %not_null38; CHECK-NEXT: ret39; CHECK-NEXT: .LBB1_2:40; CHECK-NEXT: mov w0, #4241; CHECK-NEXT: ret42 entry:43 %c = icmp eq ptr %x, null44 br i1 %c, label %is_null, label %not_null, !make.implicit !045 46 is_null:47 ret i32 4248 49 not_null:50 %t = load i32, ptr %x51 ret i32 %t52}53 54define i32 @imp_null_check_unordered_load(ptr %x) {55; CHECK-LABEL: imp_null_check_unordered_load:56; CHECK: // %bb.0: // %entry57; CHECK-NEXT: .Ltmp2:58; CHECK-NEXT: ldr w0, [x0] // on-fault: .LBB2_259; CHECK-NEXT: // %bb.1: // %not_null60; CHECK-NEXT: ret61; CHECK-NEXT: .LBB2_2:62; CHECK-NEXT: mov w0, #4263; CHECK-NEXT: ret64 entry:65 %c = icmp eq ptr %x, null66 br i1 %c, label %is_null, label %not_null, !make.implicit !067 68 is_null:69 ret i32 4270 71 not_null:72 %t = load atomic i32, ptr %x unordered, align 473 ret i32 %t74}75 76 77; TODO: Can be converted into implicit check.78;; Probably could be implicit, but we're conservative for now79define i32 @imp_null_check_seq_cst_load(ptr %x) {80; CHECK-LABEL: imp_null_check_seq_cst_load:81; CHECK: // %bb.0: // %entry82; CHECK-NEXT: cbz x0, .LBB3_283; CHECK-NEXT: // %bb.1: // %not_null84; CHECK-NEXT: ldar w0, [x0]85; CHECK-NEXT: ret86; CHECK-NEXT: .LBB3_2:87; CHECK-NEXT: mov w0, #4288; CHECK-NEXT: ret89 entry:90 %c = icmp eq ptr %x, null91 br i1 %c, label %is_null, label %not_null, !make.implicit !092 93 is_null:94 ret i32 4295 96 not_null:97 %t = load atomic i32, ptr %x seq_cst, align 498 ret i32 %t99}100 101;; Might be memory mapped IO, so can't rely on fault behavior102define i32 @imp_null_check_volatile_load(ptr %x) {103; CHECK-LABEL: imp_null_check_volatile_load:104; CHECK: // %bb.0: // %entry105; CHECK-NEXT: cbz x0, .LBB4_2106; CHECK-NEXT: // %bb.1: // %not_null107; CHECK-NEXT: ldr w0, [x0]108; CHECK-NEXT: ret109; CHECK-NEXT: .LBB4_2:110; CHECK-NEXT: mov w0, #42111; CHECK-NEXT: ret112 entry:113 %c = icmp eq ptr %x, null114 br i1 %c, label %is_null, label %not_null, !make.implicit !0115 116 is_null:117 ret i32 42118 119 not_null:120 %t = load volatile i32, ptr %x, align 4121 ret i32 %t122}123 124 125define i8 @imp_null_check_load_i8(ptr %x) {126; CHECK-LABEL: imp_null_check_load_i8:127; CHECK: // %bb.0: // %entry128; CHECK-NEXT: .Ltmp3:129; CHECK-NEXT: ldrb w0, [x0] // on-fault: .LBB5_2130; CHECK-NEXT: // %bb.1: // %not_null131; CHECK-NEXT: ret132; CHECK-NEXT: .LBB5_2:133; CHECK-NEXT: mov w0, #42134; CHECK-NEXT: ret135 entry:136 %c = icmp eq ptr %x, null137 br i1 %c, label %is_null, label %not_null, !make.implicit !0138 139 is_null:140 ret i8 42141 142 not_null:143 %t = load i8, ptr %x144 ret i8 %t145}146 147define i256 @imp_null_check_load_i256(ptr %x) {148; CHECK-LABEL: imp_null_check_load_i256:149; CHECK: // %bb.0: // %entry150; CHECK-NEXT: cbz x0, .LBB6_2151; CHECK-NEXT: // %bb.1: // %not_null152; CHECK-NEXT: ldp x2, x3, [x0, #16]153; CHECK-NEXT: ldp x0, x1, [x0]154; CHECK-NEXT: ret155; CHECK-NEXT: .LBB6_2:156; CHECK-NEXT: mov x1, xzr157; CHECK-NEXT: mov x2, xzr158; CHECK-NEXT: mov x3, xzr159; CHECK-NEXT: mov w0, #42160; CHECK-NEXT: ret161 entry:162 %c = icmp eq ptr %x, null163 br i1 %c, label %is_null, label %not_null, !make.implicit !0164 165 is_null:166 ret i256 42167 168 not_null:169 %t = load i256, ptr %x170 ret i256 %t171}172 173 174 175define i32 @imp_null_check_gep_load(ptr %x) {176; CHECK-LABEL: imp_null_check_gep_load:177; CHECK: // %bb.0: // %entry178; CHECK-NEXT: .Ltmp4:179; CHECK-NEXT: ldr w0, [x0, #128] // on-fault: .LBB7_2180; CHECK-NEXT: // %bb.1: // %not_null181; CHECK-NEXT: ret182; CHECK-NEXT: .LBB7_2:183; CHECK-NEXT: mov w0, #42184; CHECK-NEXT: ret185 entry:186 %c = icmp eq ptr %x, null187 br i1 %c, label %is_null, label %not_null, !make.implicit !0188 189 is_null:190 ret i32 42191 192 not_null:193 %x.gep = getelementptr i32, ptr %x, i32 32194 %t = load i32, ptr %x.gep195 ret i32 %t196}197 198define i32 @imp_null_check_add_result(ptr %x, i32 %p) {199; CHECK-LABEL: imp_null_check_add_result:200; CHECK: // %bb.0: // %entry201; CHECK-NEXT: .Ltmp5:202; CHECK-NEXT: ldr w8, [x0] // on-fault: .LBB8_2203; CHECK-NEXT: // %bb.1: // %not_null204; CHECK-NEXT: add w0, w8, w1205; CHECK-NEXT: ret206; CHECK-NEXT: .LBB8_2:207; CHECK-NEXT: mov w0, #42208; CHECK-NEXT: ret209 entry:210 %c = icmp eq ptr %x, null211 br i1 %c, label %is_null, label %not_null, !make.implicit !0212 213 is_null:214 ret i32 42215 216 not_null:217 %t = load i32, ptr %x218 %p1 = add i32 %t, %p219 ret i32 %p1220}221 222; Can hoist over a potential faulting instruction as long as we don't223; 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: // %entry227; CHECK-NEXT: cbz x0, .LBB9_2228; CHECK-NEXT: // %bb.1: // %not_null229; CHECK-NEXT: udiv w8, w1, w2230; CHECK-NEXT: ldr w9, [x0]231; CHECK-NEXT: add w0, w9, w8232; CHECK-NEXT: ret233; CHECK-NEXT: .LBB9_2:234; CHECK-NEXT: mov w0, #42235; CHECK-NEXT: ret236 entry:237 %c = icmp eq ptr %x, null238 br i1 %c, label %is_null, label %not_null, !make.implicit !0239 240 is_null:241 ret i32 42242 243 not_null:244 %p1 = udiv i32 %a, %b245 %t = load i32, ptr %x246 %res = add i32 %t, %p1247 ret i32 %res248}249 250 251; TODO: We should be able to hoist this - we can on x86, why isn't this252; 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: // %entry256; CHECK-NEXT: cbz x0, .LBB10_2257; CHECK-NEXT: // %bb.1: // %not_null258; CHECK-NEXT: ldr w8, [x1]259; CHECK-NEXT: ldr w0, [x0]260; CHECK-NEXT: str w8, [x2]261; CHECK-NEXT: ret262; CHECK-NEXT: .LBB10_2:263; CHECK-NEXT: mov w0, #42264; CHECK-NEXT: ret265 entry:266 %c = icmp eq ptr %x, null267 br i1 %c, label %is_null, label %not_null, !make.implicit !0268 269 is_null:270 ret i32 42271 272 not_null:273 %t0 = load i32, ptr %y274 %t1 = load i32, ptr %x275 store i32 %t0, ptr %z276 ret i32 %t1277}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: // %entry282; CHECK-NEXT: .Ltmp6:283; CHECK-NEXT: ldr w8, [x0] // on-fault: .LBB11_2284; CHECK-NEXT: // %bb.1: // %not_null285; CHECK-NEXT: add w9, w0, w1286; CHECK-NEXT: add w8, w9, w8287; CHECK-NEXT: add w0, w8, #4288; CHECK-NEXT: ret289; CHECK-NEXT: .LBB11_2:290; CHECK-NEXT: mov w0, #42291; CHECK-NEXT: ret292 entry:293 %c = icmp eq ptr %x, null294 br i1 %c, label %is_null, label %not_null, !make.implicit !0295 296 is_null:297 ret i32 42298 299 not_null:300 %x.loc = getelementptr i32, ptr %x, i32 1301 %y = ptrtoint ptr %x.loc to i32302 %b = add i32 %a, %y303 %t = load i32, ptr %x304 %z = add i32 %t, %b305 ret i32 %z306}307 308;; TODO: We could handle this case as we can lift the fence into the309;; 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: // %entry313; CHECK-NEXT: cbz x0, .LBB12_2314; CHECK-NEXT: // %bb.1: // %not_null315; CHECK-NEXT: dmb ishld316; CHECK-NEXT: ldr w0, [x0]317; CHECK-NEXT: ret318; CHECK-NEXT: .LBB12_2:319; CHECK-NEXT: mov w0, #42320; CHECK-NEXT: ret321entry:322 %c = icmp eq ptr %x, null323 br i1 %c, label %is_null, label %not_null, !make.implicit !0324 325is_null:326 ret i32 42327 328not_null:329 fence acquire330 %t = load i32, ptr %x331 ret i32 %t332}333 334;; TODO: We could handle this case as we can lift the fence into the335;; 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: // %entry339; CHECK-NEXT: cbz x0, .LBB13_2340; CHECK-NEXT: // %bb.1: // %not_null341; CHECK-NEXT: dmb ish342; CHECK-NEXT: ldr w0, [x0]343; CHECK-NEXT: ret344; CHECK-NEXT: .LBB13_2:345; CHECK-NEXT: mov w0, #42346; CHECK-NEXT: ret347entry:348 %c = icmp eq ptr %x, null349 br i1 %c, label %is_null, label %not_null, !make.implicit !0350 351is_null:352 ret i32 42353 354not_null:355 fence seq_cst356 %t = load i32, ptr %x357 ret i32 %t358}359 360; TODO: We can fold to implicit null here, not sure why this isn't working361define void @imp_null_check_store(ptr %x) {362; CHECK-LABEL: imp_null_check_store:363; CHECK: // %bb.0: // %entry364; CHECK-NEXT: cbz x0, .LBB14_2365; CHECK-NEXT: // %bb.1: // %not_null366; CHECK-NEXT: mov w8, #1367; CHECK-NEXT: str w8, [x0]368; CHECK-NEXT: .LBB14_2: // %common.ret369; CHECK-NEXT: ret370 entry:371 %c = icmp eq ptr %x, null372 br i1 %c, label %is_null, label %not_null, !make.implicit !0373 374 is_null:375 ret void376 377 not_null:378 store i32 1, ptr %x379 ret void380}381 382;; TODO: can be implicit383define void @imp_null_check_unordered_store(ptr %x) {384; CHECK-LABEL: imp_null_check_unordered_store:385; CHECK: // %bb.0: // %entry386; CHECK-NEXT: cbz x0, .LBB15_2387; CHECK-NEXT: // %bb.1: // %not_null388; CHECK-NEXT: mov w8, #1389; CHECK-NEXT: str w8, [x0]390; CHECK-NEXT: .LBB15_2: // %common.ret391; CHECK-NEXT: ret392 entry:393 %c = icmp eq ptr %x, null394 br i1 %c, label %is_null, label %not_null, !make.implicit !0395 396 is_null:397 ret void398 399 not_null:400 store atomic i32 1, ptr %x unordered, align 4401 ret void402}403 404define i32 @imp_null_check_neg_gep_load(ptr %x) {405; CHECK-LABEL: imp_null_check_neg_gep_load:406; CHECK: // %bb.0: // %entry407; CHECK-NEXT: .Ltmp7:408; CHECK-NEXT: ldur w0, [x0, #-128] // on-fault: .LBB16_2409; CHECK-NEXT: // %bb.1: // %not_null410; CHECK-NEXT: ret411; CHECK-NEXT: .LBB16_2:412; CHECK-NEXT: mov w0, #42413; CHECK-NEXT: ret414 entry:415 %c = icmp eq ptr %x, null416 br i1 %c, label %is_null, label %not_null, !make.implicit !0417 418 is_null:419 ret i32 42420 421 not_null:422 %x.gep = getelementptr i32, ptr %x, i32 -32423 %t = load i32, ptr %x.gep424 ret i32 %t425}426 427!0 = !{}428