1; RUN: opt -objc-arc-contract -S < %s | FileCheck %s 2; RUN: opt -passes=objc-arc-contract -S < %s | FileCheck %s 3 4target datalayout = "e-p:64:64:64" 5 6declare i8* @llvm.objc.retain(i8*) 7declare void @llvm.objc.release(i8*) 8declare i8* @llvm.objc.autorelease(i8*) 9declare i8* @llvm.objc.autoreleaseReturnValue(i8*) 10declare i8* @llvm.objc.retainAutoreleasedReturnValue(i8*) 11 12declare void @use_pointer(i8*) 13declare i8* @returner() 14declare void @callee() 15 16; CHECK-LABEL: define void @test0( 17; CHECK: call void @use_pointer(i8* %0) 18; CHECK: } 19define void @test0(i8* %x) nounwind { 20entry: 21 %0 = call i8* @llvm.objc.retain(i8* %x) nounwind 22 call void @use_pointer(i8* %x) 23 ret void 24} 25 26; CHECK-LABEL: define void @test1( 27; CHECK: call void @use_pointer(i8* %0) 28; CHECK: } 29define void @test1(i8* %x) nounwind { 30entry: 31 %0 = call i8* @llvm.objc.autorelease(i8* %x) nounwind 32 call void @use_pointer(i8* %x) 33 ret void 34} 35 36; Merge objc_retain and objc_autorelease into objc_retainAutorelease. 37 38; CHECK-LABEL: define void @test2( 39; CHECK: tail call i8* @llvm.objc.retainAutorelease(i8* %x) [[NUW:#[0-9]+]] 40; CHECK: } 41define void @test2(i8* %x) nounwind { 42entry: 43 %0 = tail call i8* @llvm.objc.retain(i8* %x) nounwind 44 call i8* @llvm.objc.autorelease(i8* %0) nounwind 45 call void @use_pointer(i8* %x) 46 ret void 47} 48 49; Same as test2 but the value is returned. Do an RV optimization. 50 51; CHECK-LABEL: define i8* @test2b( 52; CHECK: tail call i8* @llvm.objc.retainAutoreleaseReturnValue(i8* %x) [[NUW]] 53; CHECK: } 54define i8* @test2b(i8* %x) nounwind { 55entry: 56 %0 = tail call i8* @llvm.objc.retain(i8* %x) nounwind 57 tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %0) nounwind 58 ret i8* %x 59} 60 61; Merge a retain,autorelease pair around a call. 62 63; CHECK-LABEL: define void @test3( 64; CHECK: tail call i8* @llvm.objc.retainAutorelease(i8* %x) [[NUW]] 65; CHECK: @use_pointer(i8* %0) 66; CHECK: } 67define void @test3(i8* %x, i64 %n) { 68entry: 69 tail call i8* @llvm.objc.retain(i8* %x) nounwind 70 call void @use_pointer(i8* %x) 71 call i8* @llvm.objc.autorelease(i8* %x) nounwind 72 ret void 73} 74 75; Trivial retain,autorelease pair with intervening call, but it's post-dominated 76; by another release. The retain and autorelease can be merged. 77 78; CHECK-LABEL: define void @test4( 79; CHECK-NEXT: entry: 80; CHECK-NEXT: @llvm.objc.retainAutorelease(i8* %x) [[NUW]] 81; CHECK-NEXT: @use_pointer 82; CHECK-NEXT: @llvm.objc.release 83; CHECK-NEXT: ret void 84; CHECK-NEXT: } 85define void @test4(i8* %x, i64 %n) { 86entry: 87 tail call i8* @llvm.objc.retain(i8* %x) nounwind 88 call void @use_pointer(i8* %x) 89 call i8* @llvm.objc.autorelease(i8* %x) nounwind 90 tail call void @llvm.objc.release(i8* %x) nounwind 91 ret void 92} 93 94; Don't merge retain and autorelease if they're not control-equivalent. 95 96; CHECK-LABEL: define void @test5( 97; CHECK: tail call i8* @llvm.objc.retain(i8* %p) [[NUW]] 98; CHECK: true: 99; CHECK: call i8* @llvm.objc.autorelease(i8* %0) [[NUW]] 100; CHECK: } 101define void @test5(i8* %p, i1 %a) { 102entry: 103 tail call i8* @llvm.objc.retain(i8* %p) nounwind 104 br i1 %a, label %true, label %false 105 106true: 107 call i8* @llvm.objc.autorelease(i8* %p) nounwind 108 call void @use_pointer(i8* %p) 109 ret void 110 111false: 112 ret void 113} 114 115; Don't eliminate objc_retainAutoreleasedReturnValue by merging it into 116; an objc_autorelease. 117; TODO? Merge objc_retainAutoreleasedReturnValue and objc_autorelease into 118; objc_retainAutoreleasedReturnValueAutorelease and merge 119; objc_retainAutoreleasedReturnValue and objc_autoreleaseReturnValue 120; into objc_retainAutoreleasedReturnValueAutoreleaseReturnValue? 121; Those entrypoints don't exist yet though. 122 123; CHECK-LABEL: define i8* @test6( 124; CHECK: call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %p) [[NUW]] 125; CHECK: %t = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %1) [[NUW]] 126; CHECK: } 127define i8* @test6() { 128 %p = call i8* @returner() 129 tail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %p) nounwind 130 %t = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %p) nounwind 131 call void @use_pointer(i8* %t) 132 ret i8* %t 133} 134 135; Don't spoil the RV optimization. 136 137; CHECK: define i8* @test7(i8* %p) 138; CHECK: tail call i8* @llvm.objc.retain(i8* %p) 139; CHECK: call void @use_pointer(i8* %1) 140; CHECK: tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %1) 141; CHECK: ret i8* %2 142; CHECK-NEXT: } 143define i8* @test7(i8* %p) { 144 %1 = tail call i8* @llvm.objc.retain(i8* %p) 145 call void @use_pointer(i8* %p) 146 %2 = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %p) 147 ret i8* %p 148} 149 150; Do the return value substitution for PHI nodes too. 151 152; CHECK-LABEL: define i8* @test8( 153; CHECK: %retval = phi i8* [ %p, %if.then ], [ null, %entry ] 154; CHECK: } 155define i8* @test8(i1 %x, i8* %c) { 156entry: 157 br i1 %x, label %return, label %if.then 158 159if.then: ; preds = %entry 160 %p = call i8* @llvm.objc.retain(i8* %c) nounwind 161 br label %return 162 163return: ; preds = %if.then, %entry 164 %retval = phi i8* [ %c, %if.then ], [ null, %entry ] 165 ret i8* %retval 166} 167 168; Kill calls to @llvm.objc.clang.arc.use(...) 169; CHECK-LABEL: define void @test9( 170; CHECK-NOT: clang.arc.use 171; CHECK: } 172define void @test9(i8* %a, i8* %b) { 173 call void (...) @llvm.objc.clang.arc.use(i8* %a, i8* %b) nounwind 174 ret void 175} 176 177 178; Turn objc_retain into objc_retainAutoreleasedReturnValue if its operand 179; is a return value. 180 181; CHECK: define void @test10() 182; CHECK: tail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %p) 183define void @test10() { 184 %p = call i8* @returner() 185 tail call i8* @llvm.objc.retain(i8* %p) nounwind 186 ret void 187} 188 189; Convert objc_retain to objc_retainAutoreleasedReturnValue if its 190; argument is a return value. 191 192; CHECK-LABEL: define void @test11( 193; CHECK-NEXT: %y = call i8* @returner() 194; CHECK-NEXT: tail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %y) [[NUW]] 195; CHECK-NEXT: ret void 196define void @test11() { 197 %y = call i8* @returner() 198 tail call i8* @llvm.objc.retain(i8* %y) nounwind 199 ret void 200} 201 202; Don't convert objc_retain to objc_retainAutoreleasedReturnValue if its 203; argument is not a return value. 204 205; CHECK-LABEL: define void @test12( 206; CHECK-NEXT: tail call i8* @llvm.objc.retain(i8* %y) [[NUW]] 207; CHECK-NEXT: ret void 208; CHECK-NEXT: } 209define void @test12(i8* %y) { 210 tail call i8* @llvm.objc.retain(i8* %y) nounwind 211 ret void 212} 213 214; Don't Convert objc_retain to objc_retainAutoreleasedReturnValue if it 215; isn't next to the call providing its return value. 216 217; CHECK-LABEL: define void @test13( 218; CHECK-NEXT: %y = call i8* @returner() 219; CHECK-NEXT: call void @callee() 220; CHECK-NEXT: tail call i8* @llvm.objc.retain(i8* %y) [[NUW]] 221; CHECK-NEXT: ret void 222; CHECK-NEXT: } 223define void @test13() { 224 %y = call i8* @returner() 225 call void @callee() 226 tail call i8* @llvm.objc.retain(i8* %y) nounwind 227 ret void 228} 229 230 231declare void @llvm.objc.clang.arc.use(...) nounwind 232 233; CHECK: attributes [[NUW]] = { nounwind } 234