257 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 ptr @llvm.objc.autorelease(ptr)8declare ptr @llvm.objc.autoreleaseReturnValue(ptr)9declare ptr @llvm.objc.retainAutoreleasedReturnValue(ptr)10 11declare void @use_pointer(ptr)12declare ptr @returner()13declare void @callee()14 15; CHECK-LABEL: define void @test0(16; CHECK: call void @use_pointer(ptr %0)17; CHECK: }18define void @test0(ptr %x) nounwind {19entry:20 %0 = call ptr @llvm.objc.retain(ptr %x) nounwind21 call void @use_pointer(ptr %x)22 ret void23}24 25; CHECK-LABEL: define void @test1(26; CHECK: call void @use_pointer(ptr %0)27; CHECK: }28define void @test1(ptr %x) nounwind {29entry:30 %0 = call ptr @llvm.objc.autorelease(ptr %x) nounwind31 call void @use_pointer(ptr %x)32 ret void33}34 35; Merge objc_retain and objc_autorelease into objc_retainAutorelease.36 37; CHECK-LABEL: define void @test2(38; CHECK: tail call ptr @llvm.objc.retainAutorelease(ptr %x) [[NUW:#[0-9]+]]39; CHECK: }40define void @test2(ptr %x) nounwind {41entry:42 %0 = tail call ptr @llvm.objc.retain(ptr %x) nounwind43 call ptr @llvm.objc.autorelease(ptr %0) nounwind44 call void @use_pointer(ptr %x)45 ret void46}47 48; Same as test2 but the value is returned. Do an RV optimization.49 50; CHECK-LABEL: define ptr @test2b(51; CHECK: tail call ptr @llvm.objc.retainAutoreleaseReturnValue(ptr %x) [[NUW]]52; CHECK: }53define ptr @test2b(ptr %x) nounwind {54entry:55 %0 = tail call ptr @llvm.objc.retain(ptr %x) nounwind56 tail call ptr @llvm.objc.autoreleaseReturnValue(ptr %0) nounwind57 ret ptr %x58}59 60; Merge a retain,autorelease pair around a call.61 62; CHECK-LABEL: define void @test3(63; CHECK: tail call ptr @llvm.objc.retainAutorelease(ptr %x) [[NUW]]64; CHECK: @use_pointer(ptr %0)65; CHECK: }66define void @test3(ptr %x, i64 %n) {67entry:68 tail call ptr @llvm.objc.retain(ptr %x) nounwind69 call void @use_pointer(ptr %x)70 call ptr @llvm.objc.autorelease(ptr %x) nounwind71 ret void72}73 74; Trivial retain,autorelease pair with intervening call, but it's post-dominated75; by another release. The retain and autorelease can be merged.76 77; CHECK-LABEL: define void @test4(78; CHECK-NEXT: entry:79; CHECK-NEXT: @llvm.objc.retainAutorelease(ptr %x) [[NUW]]80; CHECK-NEXT: @use_pointer81; CHECK-NEXT: @llvm.objc.release82; CHECK-NEXT: ret void83; CHECK-NEXT: }84define void @test4(ptr %x, i64 %n) {85entry:86 tail call ptr @llvm.objc.retain(ptr %x) nounwind87 call void @use_pointer(ptr %x)88 call ptr @llvm.objc.autorelease(ptr %x) nounwind89 tail call void @llvm.objc.release(ptr %x) nounwind90 ret void91}92 93; Don't merge retain and autorelease if they're not control-equivalent.94 95; CHECK-LABEL: define void @test5(96; CHECK: tail call ptr @llvm.objc.retain(ptr %p) [[NUW]]97; CHECK: true:98; CHECK: call ptr @llvm.objc.autorelease(ptr %0) [[NUW]]99; CHECK: }100define void @test5(ptr %p, i1 %a) {101entry:102 tail call ptr @llvm.objc.retain(ptr %p) nounwind103 br i1 %a, label %true, label %false104 105true:106 call ptr @llvm.objc.autorelease(ptr %p) nounwind107 call void @use_pointer(ptr %p)108 ret void109 110false:111 ret void112}113 114; Don't eliminate objc_retainAutoreleasedReturnValue by merging it into115; an objc_autorelease.116; TODO? Merge objc_retainAutoreleasedReturnValue and objc_autorelease into117; objc_retainAutoreleasedReturnValueAutorelease and merge118; objc_retainAutoreleasedReturnValue and objc_autoreleaseReturnValue119; into objc_retainAutoreleasedReturnValueAutoreleaseReturnValue?120; Those entrypoints don't exist yet though.121 122; CHECK-LABEL: define ptr @test6(123; CHECK: call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %p) [[NUW]]124; CHECK: %t = tail call ptr @llvm.objc.autoreleaseReturnValue(ptr %1) [[NUW]]125; CHECK: }126define ptr @test6() {127 %p = call ptr @returner()128 tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %p) nounwind129 %t = tail call ptr @llvm.objc.autoreleaseReturnValue(ptr %p) nounwind130 call void @use_pointer(ptr %t)131 ret ptr %t132}133 134; Don't spoil the RV optimization.135 136; CHECK: define ptr @test7(ptr %p)137; CHECK: tail call ptr @llvm.objc.retain(ptr %p)138; CHECK: call void @use_pointer(ptr %1)139; CHECK: tail call ptr @llvm.objc.autoreleaseReturnValue(ptr %1)140; CHECK: ret ptr %2141; CHECK-NEXT: }142define ptr @test7(ptr %p) {143 %1 = tail call ptr @llvm.objc.retain(ptr %p)144 call void @use_pointer(ptr %p)145 %2 = tail call ptr @llvm.objc.autoreleaseReturnValue(ptr %p)146 ret ptr %p147}148 149; Do the return value substitution for PHI nodes too.150 151; CHECK-LABEL: define ptr @test8(152; CHECK: %retval = phi ptr [ %p, %if.then ], [ null, %entry ]153; CHECK: }154define ptr @test8(i1 %x, ptr %c) {155entry:156 br i1 %x, label %return, label %if.then157 158if.then: ; preds = %entry159 %p = call ptr @llvm.objc.retain(ptr %c) nounwind160 br label %return161 162return: ; preds = %if.then, %entry163 %retval = phi ptr [ %c, %if.then ], [ null, %entry ]164 ret ptr %retval165}166 167; Kill calls to @llvm.objc.clang.arc.use(...)168; CHECK-LABEL: define void @test9(169; CHECK-NOT: clang.arc.use170; CHECK: }171define void @test9(ptr %a, ptr %b) {172 call void (...) @llvm.objc.clang.arc.use(ptr %a, ptr %b) nounwind173 ret void174}175 176 177; Turn objc_retain into objc_retainAutoreleasedReturnValue if its operand178; is a return value.179 180; CHECK: define void @test10()181; CHECK: tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %p)182define void @test10() {183 %p = call ptr @returner()184 tail call ptr @llvm.objc.retain(ptr %p) nounwind185 ret void186}187 188; Convert objc_retain to objc_retainAutoreleasedReturnValue if its189; argument is a return value.190 191; CHECK-LABEL: define void @test11(192; CHECK-NEXT: %y = call ptr @returner()193; CHECK-NEXT: tail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %y) [[NUW]]194; CHECK-NEXT: ret void195define void @test11() {196 %y = call ptr @returner()197 tail call ptr @llvm.objc.retain(ptr %y) nounwind198 ret void199}200 201; Don't convert objc_retain to objc_retainAutoreleasedReturnValue if its202; argument is not a return value.203 204; CHECK-LABEL: define void @test12(205; CHECK-NEXT: tail call ptr @llvm.objc.retain(ptr %y) [[NUW]]206; CHECK-NEXT: ret void207; CHECK-NEXT: }208define void @test12(ptr %y) {209 tail call ptr @llvm.objc.retain(ptr %y) nounwind210 ret void211}212 213; Don't Convert objc_retain to objc_retainAutoreleasedReturnValue if it214; isn't next to the call providing its return value.215 216; CHECK-LABEL: define void @test13(217; CHECK-NEXT: %y = call ptr @returner()218; CHECK-NEXT: call void @callee()219; CHECK-NEXT: tail call ptr @llvm.objc.retain(ptr %y) [[NUW]]220; CHECK-NEXT: ret void221; CHECK-NEXT: }222define void @test13() {223 %y = call ptr @returner()224 call void @callee()225 tail call ptr @llvm.objc.retain(ptr %y) nounwind226 ret void227}228 229; CHECK-LABEL: define void @test14(230; CHECK-NOT: clang.arc.noop.use231; CHECK: ret void232define void @test14(ptr %a, ptr %b) {233 call void (...) @llvm.objc.clang.arc.noop.use(ptr %a, ptr %b) nounwind234 ret void235}236 237define void @test15(ptr %x) {238; CHECK-LABEL: define void @test15(239; CHECK-SAME: ptr [[X:%.*]]) {240; CHECK-NEXT: [[Y:%.*]] = getelementptr inbounds ptr, ptr [[X]], i32 0241; CHECK-NEXT: [[V0:%.*]] = call ptr @llvm.objc.retain(ptr [[Y]]) #[[ATTR0:[0-9]+]]242; CHECK-NEXT: call void @use_pointer(ptr [[V0]])243; CHECK-NEXT: call void @use_pointer(ptr [[V0]])244; CHECK-NEXT: ret void245;246 %y = getelementptr inbounds ptr, ptr %x, i32 0247 %v0 = call ptr @llvm.objc.retain(ptr %y) nounwind248 call void @use_pointer(ptr %x)249 call void @use_pointer(ptr %y)250 ret void251}252 253declare void @llvm.objc.clang.arc.use(...) nounwind254declare void @llvm.objc.clang.arc.noop.use(...) nounwind255 256; CHECK: attributes [[NUW]] = { nounwind }257