280 lines · plain
1; RUN: opt -passes=objc-arc-contract -S < %s | FileCheck %s2 3target datalayout = "e-p:64:64:64"4 5declare ptr @llvm.objc.retain(ptr)6declare void @llvm.objc.release(ptr)7declare void @use_pointer(ptr)8 9@x = external global ptr10 11; CHECK-LABEL: define void @test0(12; CHECK: entry:13; CHECK-NEXT: tail call void @llvm.objc.storeStrong(ptr @x, ptr %p) [[NUW:#[0-9]+]]14; CHECK-NEXT: ret void15; CHECK-NEXT: }16define void @test0(ptr %p) {17entry:18 %0 = tail call ptr @llvm.objc.retain(ptr %p) nounwind19 %tmp = load ptr, ptr @x, align 820 store ptr %0, ptr @x, align 821 tail call void @llvm.objc.release(ptr %tmp) nounwind22 ret void23}24 25; Don't do this if the load is volatile.26 27; CHECK-LABEL: define void @test1(ptr %p) {28; CHECK-NEXT: entry:29; CHECK-NEXT: %0 = tail call ptr @llvm.objc.retain(ptr %p) [[NUW]]30; CHECK-NEXT: %tmp = load volatile ptr, ptr @x, align 831; CHECK-NEXT: store ptr %0, ptr @x, align 832; CHECK-NEXT: tail call void @llvm.objc.release(ptr %tmp) [[NUW]]33; CHECK-NEXT: ret void34; CHECK-NEXT: }35define void @test1(ptr %p) {36entry:37 %0 = tail call ptr @llvm.objc.retain(ptr %p) nounwind38 %tmp = load volatile ptr, ptr @x, align 839 store ptr %0, ptr @x, align 840 tail call void @llvm.objc.release(ptr %tmp) nounwind41 ret void42}43 44; Don't do this if the store is volatile.45 46; CHECK-LABEL: define void @test2(ptr %p) {47; CHECK-NEXT: entry:48; CHECK-NEXT: %0 = tail call ptr @llvm.objc.retain(ptr %p) [[NUW]]49; CHECK-NEXT: %tmp = load ptr, ptr @x, align 850; CHECK-NEXT: store volatile ptr %0, ptr @x, align 851; CHECK-NEXT: tail call void @llvm.objc.release(ptr %tmp) [[NUW]]52; CHECK-NEXT: ret void53; CHECK-NEXT: }54define void @test2(ptr %p) {55entry:56 %0 = tail call ptr @llvm.objc.retain(ptr %p) nounwind57 %tmp = load ptr, ptr @x, align 858 store volatile ptr %0, ptr @x, align 859 tail call void @llvm.objc.release(ptr %tmp) nounwind60 ret void61}62 63; Don't do this if there's a use of the old pointer value between the store64; and the release.65 66; CHECK-LABEL: define void @test3(ptr %newValue) {67; CHECK-NEXT: entry:68; CHECK-NEXT: %x0 = tail call ptr @llvm.objc.retain(ptr %newValue) [[NUW]]69; CHECK-NEXT: %x1 = load ptr, ptr @x, align 870; CHECK-NEXT: store ptr %x0, ptr @x, align 871; CHECK-NEXT: tail call void @use_pointer(ptr %x1), !clang.arc.no_objc_arc_exceptions !072; CHECK-NEXT: tail call void @llvm.objc.release(ptr %x1) [[NUW]], !clang.imprecise_release !073; CHECK-NEXT: ret void74; CHECK-NEXT: }75define void @test3(ptr %newValue) {76entry:77 %x0 = tail call ptr @llvm.objc.retain(ptr %newValue) nounwind78 %x1 = load ptr, ptr @x, align 879 store ptr %newValue, ptr @x, align 880 tail call void @use_pointer(ptr %x1), !clang.arc.no_objc_arc_exceptions !081 tail call void @llvm.objc.release(ptr %x1) nounwind, !clang.imprecise_release !082 ret void83}84 85; Like test3, but with an icmp use instead of a call, for good measure.86 87; CHECK-LABEL: define i1 @test4(ptr %newValue, ptr %foo) {88; CHECK-NEXT: entry:89; CHECK-NEXT: %x0 = tail call ptr @llvm.objc.retain(ptr %newValue) [[NUW]]90; CHECK-NEXT: %x1 = load ptr, ptr @x, align 891; CHECK-NEXT: store ptr %x0, ptr @x, align 892; CHECK-NEXT: %t = icmp eq ptr %x1, %foo93; CHECK-NEXT: tail call void @llvm.objc.release(ptr %x1) [[NUW]], !clang.imprecise_release !094; CHECK-NEXT: ret i1 %t95; CHECK-NEXT: }96define i1 @test4(ptr %newValue, ptr %foo) {97entry:98 %x0 = tail call ptr @llvm.objc.retain(ptr %newValue) nounwind99 %x1 = load ptr, ptr @x, align 8100 store ptr %newValue, ptr @x, align 8101 %t = icmp eq ptr %x1, %foo102 tail call void @llvm.objc.release(ptr %x1) nounwind, !clang.imprecise_release !0103 ret i1 %t104}105 106; Do form an llvm.objc.storeStrong here, because the use is before the store.107 108; CHECK-LABEL: define i1 @test5(ptr %newValue, ptr %foo) {109; CHECK: %t = icmp eq ptr %x1, %foo110; CHECK: tail call void @llvm.objc.storeStrong(ptr @x, ptr %newValue) [[NUW]]111; CHECK: }112define i1 @test5(ptr %newValue, ptr %foo) {113entry:114 %x0 = tail call ptr @llvm.objc.retain(ptr %newValue) nounwind115 %x1 = load ptr, ptr @x, align 8116 %t = icmp eq ptr %x1, %foo117 store ptr %newValue, ptr @x, align 8118 tail call void @llvm.objc.release(ptr %x1) nounwind, !clang.imprecise_release !0119 ret i1 %t120}121 122; Like test5, but the release is before the store.123 124; CHECK-LABEL: define i1 @test6(ptr %newValue, ptr %foo) {125; CHECK: %t = icmp eq ptr %x1, %foo126; CHECK: tail call void @llvm.objc.storeStrong(ptr @x, ptr %newValue) [[NUW]]127; CHECK: }128define i1 @test6(ptr %newValue, ptr %foo) {129entry:130 %x0 = tail call ptr @llvm.objc.retain(ptr %newValue) nounwind131 %x1 = load ptr, ptr @x, align 8132 tail call void @llvm.objc.release(ptr %x1) nounwind, !clang.imprecise_release !0133 %t = icmp eq ptr %x1, %foo134 store ptr %newValue, ptr @x, align 8135 ret i1 %t136}137 138; Like test0, but there's no store, so don't form an llvm.objc.storeStrong.139 140; CHECK-LABEL: define void @test7(141; CHECK-NEXT: entry:142; CHECK-NEXT: %0 = tail call ptr @llvm.objc.retain(ptr %p) [[NUW]]143; CHECK-NEXT: %tmp = load ptr, ptr @x, align 8144; CHECK-NEXT: tail call void @llvm.objc.release(ptr %tmp) [[NUW]]145; CHECK-NEXT: ret void146; CHECK-NEXT: }147define void @test7(ptr %p) {148entry:149 %0 = tail call ptr @llvm.objc.retain(ptr %p) nounwind150 %tmp = load ptr, ptr @x, align 8151 tail call void @llvm.objc.release(ptr %tmp) nounwind152 ret void153}154 155; Like test0, but there's no retain, so don't form an llvm.objc.storeStrong.156 157; CHECK-LABEL: define void @test8(158; CHECK-NEXT: entry:159; CHECK-NEXT: %tmp = load ptr, ptr @x, align 8160; CHECK-NEXT: store ptr %p, ptr @x, align 8161; CHECK-NEXT: tail call void @llvm.objc.release(ptr %tmp) [[NUW]]162; CHECK-NEXT: ret void163; CHECK-NEXT: }164define void @test8(ptr %p) {165entry:166 %tmp = load ptr, ptr @x, align 8167 store ptr %p, ptr @x, align 8168 tail call void @llvm.objc.release(ptr %tmp) nounwind169 ret void170}171 172; Make sure that we properly handle release that *may* release our new173; value in between the retain and the store. We need to be sure that174; this we can safely move the retain to the store. This specific test175; makes sure that we properly handled a release of an unrelated176; pointer.177;178; CHECK-LABEL: define i1 @test9(ptr %newValue, ptr %foo, ptr %unrelated_ptr) {179; CHECK-NOT: llvm.objc.storeStrong180define i1 @test9(ptr %newValue, ptr %foo, ptr %unrelated_ptr) {181entry:182 %x0 = tail call ptr @llvm.objc.retain(ptr %newValue) nounwind183 tail call void @llvm.objc.release(ptr %unrelated_ptr) nounwind, !clang.imprecise_release !0184 %x1 = load ptr, ptr @x, align 8185 tail call void @llvm.objc.release(ptr %x1) nounwind, !clang.imprecise_release !0186 %t = icmp eq ptr %x1, %foo187 store ptr %newValue, ptr @x, align 8188 ret i1 %t189}190 191; Make sure that we don't perform the optimization when we just have a call.192;193; CHECK-LABEL: define i1 @test10(ptr %newValue, ptr %foo, ptr %unrelated_ptr) {194; CHECK-NOT: llvm.objc.storeStrong195define i1 @test10(ptr %newValue, ptr %foo, ptr %unrelated_ptr) {196entry:197 %x0 = tail call ptr @llvm.objc.retain(ptr %newValue) nounwind198 call void @use_pointer(ptr %unrelated_ptr)199 %x1 = load ptr, ptr @x, align 8200 tail call void @llvm.objc.release(ptr %x1) nounwind, !clang.imprecise_release !0201 %t = icmp eq ptr %x1, %foo202 store ptr %newValue, ptr @x, align 8203 ret i1 %t204}205 206; Make sure we form the store strong if the use in between the retain207; and the store does not touch reference counts.208; CHECK-LABEL: define i1 @test11(ptr %newValue, ptr %foo, ptr %unrelated_ptr) {209; CHECK: llvm.objc.storeStrong210define i1 @test11(ptr %newValue, ptr %foo, ptr %unrelated_ptr) {211entry:212 %x0 = tail call ptr @llvm.objc.retain(ptr %newValue) nounwind213 %t = icmp eq ptr %newValue, %foo214 %x1 = load ptr, ptr @x, align 8215 tail call void @llvm.objc.release(ptr %x1) nounwind, !clang.imprecise_release !0216 store ptr %newValue, ptr @x, align 8217 ret i1 %t218}219 220; Make sure that we form the store strong even if there are bitcasts on221; the pointers.222; CHECK-LABEL: define void @test12(223; CHECK: entry:224; CHECK-NEXT: %p16 = bitcast ptr @x to ptr225; CHECK-NEXT: %tmp16 = load ptr, ptr %p16, align 8226; CHECK-NEXT: %tmp8 = bitcast ptr %tmp16 to ptr227; CHECK-NEXT: %p32 = bitcast ptr @x to ptr228; CHECK-NEXT: %v32 = bitcast ptr %p to ptr229; CHECK-NEXT: tail call void @llvm.objc.storeStrong(ptr %p16, ptr %p)230; CHECK-NEXT: ret void231; CHECK-NEXT: }232define void @test12(ptr %p) {233entry:234 %retain = tail call ptr @llvm.objc.retain(ptr %p) nounwind235 %p16 = bitcast ptr @x to ptr236 %tmp16 = load ptr, ptr %p16, align 8237 %tmp8 = bitcast ptr %tmp16 to ptr238 %p32 = bitcast ptr @x to ptr239 %v32 = bitcast ptr %retain to ptr240 store ptr %v32, ptr %p32, align 8241 tail call void @llvm.objc.release(ptr %tmp8) nounwind242 ret void243}244 245; This used to crash.246; CHECK-LABEL: define ptr @test13(247; CHECK: tail call void @llvm.objc.storeStrong(ptr %{{.*}}, ptr %[[NEW:.*]])248; CHECK-NEXT: ret ptr %[[NEW]]249 250define ptr @test13(ptr %a0, ptr %a1, ptr %addr, ptr %new) {251 %old = load ptr, ptr %addr, align 8252 call void @llvm.objc.release(ptr %old)253 %retained = call ptr @llvm.objc.retain(ptr %new)254 store ptr %retained, ptr %addr, align 8255 ret ptr %retained256}257 258; Cannot form a storeStrong call because it's unsafe to move the release call to259; the store.260 261; CHECK-LABEL: define void @test14(262; CHECK: %[[V0:.*]] = load ptr, ptr %a263; CHECK: %[[V1:.*]] = call ptr @llvm.objc.retain(ptr %p)264; CHECK: store ptr %[[V1]], ptr %a265; CHECK: %[[V2:.*]] = call ptr @llvm.objc.retain(ptr %[[V0]])266; CHECK: call void @llvm.objc.release(ptr %[[V2]])267 268define void @test14(ptr %a, ptr %p) {269 %v0 = load ptr, ptr %a, align 8270 %v1 = call ptr @llvm.objc.retain(ptr %p)271 store ptr %p, ptr %a, align 8272 %v2 = call ptr @llvm.objc.retain(ptr %v0)273 call void @llvm.objc.release(ptr %v0)274 ret void275}276 277!0 = !{}278 279; CHECK: attributes [[NUW]] = { nounwind }280