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