1 ; RUN: opt -S -objc-arc < %s | FileCheck %s 2 ; rdar://9503416 3 4 ; Detect loop boundaries and don't move retains and releases 5 ; across them. 6 7 declare void @use_pointer(i8*) 8 declare i8* @objc_retain(i8*) 9 declare void @objc_release(i8*) 10 declare void @callee() 11 declare void @block_callee(void ()*) 12 13 ; CHECK-LABEL: define void @test0( 14 ; CHECK: call i8* @objc_retain( 15 ; CHECK: for.body: 16 ; CHECK-NOT: @objc 17 ; CHECK: for.end: 18 ; CHECK: call void @objc_release( 19 ; CHECK: } 20 define void @test0(i8* %digits) { 21 entry: 22 %tmp1 = call i8* @objc_retain(i8* %digits) nounwind 23 call void @use_pointer(i8* %digits) 24 br label %for.body 25 26 for.body: ; preds = %for.body, %entry 27 %upcDigitIndex.01 = phi i64 [ 2, %entry ], [ %inc, %for.body ] 28 call void @use_pointer(i8* %digits) 29 %inc = add i64 %upcDigitIndex.01, 1 30 %cmp = icmp ult i64 %inc, 12 31 br i1 %cmp, label %for.body, label %for.end 32 33 for.end: ; preds = %for.body 34 call void @objc_release(i8* %digits) nounwind, !clang.imprecise_release !0 35 ret void 36 } 37 38 ; CHECK-LABEL: define void @test1( 39 ; CHECK: call i8* @objc_retain( 40 ; CHECK: for.body: 41 ; CHECK-NOT: @objc 42 ; CHECK: for.end: 43 ; CHECK: void @objc_release( 44 ; CHECK: } 45 define void @test1(i8* %digits) { 46 entry: 47 %tmp1 = call i8* @objc_retain(i8* %digits) nounwind 48 br label %for.body 49 50 for.body: ; preds = %for.body, %entry 51 %upcDigitIndex.01 = phi i64 [ 2, %entry ], [ %inc, %for.body ] 52 call void @use_pointer(i8* %digits) 53 call void @use_pointer(i8* %digits) 54 %inc = add i64 %upcDigitIndex.01, 1 55 %cmp = icmp ult i64 %inc, 12 56 br i1 %cmp, label %for.body, label %for.end 57 58 for.end: ; preds = %for.body 59 call void @objc_release(i8* %digits) nounwind, !clang.imprecise_release !0 60 ret void 61 } 62 63 ; CHECK-LABEL: define void @test2( 64 ; CHECK: call i8* @objc_retain( 65 ; CHECK: for.body: 66 ; CHECK-NOT: @objc 67 ; CHECK: for.end: 68 ; CHECK: void @objc_release( 69 ; CHECK: } 70 define void @test2(i8* %digits) { 71 entry: 72 %tmp1 = call i8* @objc_retain(i8* %digits) nounwind 73 br label %for.body 74 75 for.body: ; preds = %for.body, %entry 76 %upcDigitIndex.01 = phi i64 [ 2, %entry ], [ %inc, %for.body ] 77 call void @use_pointer(i8* %digits) 78 %inc = add i64 %upcDigitIndex.01, 1 79 %cmp = icmp ult i64 %inc, 12 80 br i1 %cmp, label %for.body, label %for.end 81 82 for.end: ; preds = %for.body 83 call void @use_pointer(i8* %digits) 84 call void @objc_release(i8* %digits) nounwind, !clang.imprecise_release !0 85 ret void 86 } 87 88 ; Delete nested retain+release pairs around loops. 89 90 ; CHECK: define void @test3(i8* %a) #0 { 91 ; CHECK-NEXT: entry: 92 ; CHECK-NEXT: tail call i8* @objc_retain(i8* %a) [[NUW:#[0-9]+]] 93 ; CHECK-NEXT: br label %loop 94 ; CHECK-NOT: @objc_ 95 ; CHECK: exit: 96 ; CHECK-NEXT: call void @objc_release(i8* %a) 97 ; CHECK-NEXT: ret void 98 ; CHECK-NEXT: } 99 define void @test3(i8* %a) nounwind { 100 entry: 101 %outer = call i8* @objc_retain(i8* %a) nounwind 102 %inner = call i8* @objc_retain(i8* %a) nounwind 103 br label %loop 104 105 loop: 106 call void @callee() 107 store i8 0, i8* %a 108 br i1 undef, label %loop, label %exit 109 110 exit: 111 call void @objc_release(i8* %a) nounwind 112 call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0 113 ret void 114 } 115 116 ; CHECK: define void @test4(i8* %a) #0 { 117 ; CHECK-NEXT: entry: 118 ; CHECK-NEXT: tail call i8* @objc_retain(i8* %a) [[NUW]] 119 ; CHECK-NEXT: br label %loop 120 ; CHECK-NOT: @objc_ 121 ; CHECK: exit: 122 ; CHECK-NEXT: call void @objc_release(i8* %a) 123 ; CHECK-NEXT: ret void 124 ; CHECK-NEXT: } 125 define void @test4(i8* %a) nounwind { 126 entry: 127 %outer = call i8* @objc_retain(i8* %a) nounwind 128 %inner = call i8* @objc_retain(i8* %a) nounwind 129 br label %loop 130 131 loop: 132 br label %more 133 134 more: 135 call void @callee() 136 call void @callee() 137 store i8 0, i8* %a 138 br i1 undef, label %loop, label %exit 139 140 exit: 141 call void @objc_release(i8* %a) nounwind 142 call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0 143 ret void 144 } 145 146 ; CHECK: define void @test5(i8* %a) #0 { 147 ; CHECK-NEXT: entry: 148 ; CHECK-NEXT: tail call i8* @objc_retain(i8* %a) [[NUW]] 149 ; CHECK-NEXT: call void @callee() 150 ; CHECK-NEXT: br label %loop 151 ; CHECK-NOT: @objc_ 152 ; CHECK: exit: 153 ; CHECK-NEXT: call void @use_pointer(i8* %a) 154 ; CHECK-NEXT: call void @objc_release(i8* %a) 155 ; CHECK-NEXT: ret void 156 ; CHECK-NEXT: } 157 define void @test5(i8* %a) nounwind { 158 entry: 159 %outer = tail call i8* @objc_retain(i8* %a) nounwind 160 %inner = tail call i8* @objc_retain(i8* %a) nounwind 161 call void @callee() 162 br label %loop 163 164 loop: 165 br i1 undef, label %true, label %more 166 167 true: 168 br label %more 169 170 more: 171 br i1 undef, label %exit, label %loop 172 173 exit: 174 call void @use_pointer(i8* %a) 175 call void @objc_release(i8* %a) nounwind 176 call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0 177 ret void 178 } 179 180 ; CHECK: define void @test6(i8* %a) #0 { 181 ; CHECK-NEXT: entry: 182 ; CHECK-NEXT: tail call i8* @objc_retain(i8* %a) [[NUW]] 183 ; CHECK-NEXT: br label %loop 184 ; CHECK-NOT: @objc_ 185 ; CHECK: exit: 186 ; CHECK-NEXT: call void @use_pointer(i8* %a) 187 ; CHECK-NEXT: call void @objc_release(i8* %a) 188 ; CHECK-NEXT: ret void 189 ; CHECK-NEXT: } 190 define void @test6(i8* %a) nounwind { 191 entry: 192 %outer = tail call i8* @objc_retain(i8* %a) nounwind 193 %inner = tail call i8* @objc_retain(i8* %a) nounwind 194 br label %loop 195 196 loop: 197 br i1 undef, label %true, label %more 198 199 true: 200 call void @callee() 201 br label %more 202 203 more: 204 br i1 undef, label %exit, label %loop 205 206 exit: 207 call void @use_pointer(i8* %a) 208 call void @objc_release(i8* %a) nounwind 209 call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0 210 ret void 211 } 212 213 ; CHECK: define void @test7(i8* %a) #0 { 214 ; CHECK-NEXT: entry: 215 ; CHECK-NEXT: tail call i8* @objc_retain(i8* %a) [[NUW]] 216 ; CHECK-NEXT: call void @callee() 217 ; CHECK-NEXT: br label %loop 218 ; CHECK-NOT: @objc_ 219 ; CHECK: exit: 220 ; CHECK-NEXT: call void @objc_release(i8* %a) 221 ; CHECK-NEXT: ret void 222 ; CHECK-NEXT: } 223 define void @test7(i8* %a) nounwind { 224 entry: 225 %outer = tail call i8* @objc_retain(i8* %a) nounwind 226 %inner = tail call i8* @objc_retain(i8* %a) nounwind 227 call void @callee() 228 br label %loop 229 230 loop: 231 br i1 undef, label %true, label %more 232 233 true: 234 call void @use_pointer(i8* %a) 235 br label %more 236 237 more: 238 br i1 undef, label %exit, label %loop 239 240 exit: 241 call void @objc_release(i8* %a) nounwind 242 call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0 243 ret void 244 } 245 246 ; CHECK: define void @test8(i8* %a) #0 { 247 ; CHECK-NEXT: entry: 248 ; CHECK-NEXT: tail call i8* @objc_retain(i8* %a) [[NUW]] 249 ; CHECK-NEXT: br label %loop 250 ; CHECK-NOT: @objc_ 251 ; CHECK: exit: 252 ; CHECK-NEXT: call void @objc_release(i8* %a) 253 ; CHECK-NEXT: ret void 254 ; CHECK-NEXT: } 255 define void @test8(i8* %a) nounwind { 256 entry: 257 %outer = tail call i8* @objc_retain(i8* %a) nounwind 258 %inner = tail call i8* @objc_retain(i8* %a) nounwind 259 br label %loop 260 261 loop: 262 br i1 undef, label %true, label %more 263 264 true: 265 call void @callee() 266 call void @use_pointer(i8* %a) 267 br label %more 268 269 more: 270 br i1 undef, label %exit, label %loop 271 272 exit: 273 call void @objc_release(i8* %a) nounwind 274 call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0 275 ret void 276 } 277 278 ; CHECK: define void @test9(i8* %a) #0 { 279 ; CHECK-NEXT: entry: 280 ; CHECK-NEXT: br label %loop 281 ; CHECK-NOT: @objc_ 282 ; CHECK: exit: 283 ; CHECK-NEXT: ret void 284 ; CHECK-NEXT: } 285 define void @test9(i8* %a) nounwind { 286 entry: 287 %outer = tail call i8* @objc_retain(i8* %a) nounwind 288 %inner = tail call i8* @objc_retain(i8* %a) nounwind 289 br label %loop 290 291 loop: 292 br i1 undef, label %true, label %more 293 294 true: 295 call void @use_pointer(i8* %a) 296 br label %more 297 298 more: 299 br i1 undef, label %exit, label %loop 300 301 exit: 302 call void @objc_release(i8* %a) nounwind 303 call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0 304 ret void 305 } 306 307 ; CHECK: define void @test10(i8* %a) #0 { 308 ; CHECK-NEXT: entry: 309 ; CHECK-NEXT: br label %loop 310 ; CHECK-NOT: @objc_ 311 ; CHECK: exit: 312 ; CHECK-NEXT: ret void 313 ; CHECK-NEXT: } 314 define void @test10(i8* %a) nounwind { 315 entry: 316 %outer = tail call i8* @objc_retain(i8* %a) nounwind 317 %inner = tail call i8* @objc_retain(i8* %a) nounwind 318 br label %loop 319 320 loop: 321 br i1 undef, label %true, label %more 322 323 true: 324 call void @callee() 325 br label %more 326 327 more: 328 br i1 undef, label %exit, label %loop 329 330 exit: 331 call void @objc_release(i8* %a) nounwind 332 call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0 333 ret void 334 } 335 336 ; CHECK: define void @test11(i8* %a) #0 { 337 ; CHECK-NEXT: entry: 338 ; CHECK-NEXT: br label %loop 339 ; CHECK-NOT: @objc_ 340 ; CHECK: exit: 341 ; CHECK-NEXT: ret void 342 ; CHECK-NEXT: } 343 define void @test11(i8* %a) nounwind { 344 entry: 345 %outer = tail call i8* @objc_retain(i8* %a) nounwind 346 %inner = tail call i8* @objc_retain(i8* %a) nounwind 347 br label %loop 348 349 loop: 350 br i1 undef, label %true, label %more 351 352 true: 353 br label %more 354 355 more: 356 br i1 undef, label %exit, label %loop 357 358 exit: 359 call void @objc_release(i8* %a) nounwind 360 call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0 361 ret void 362 } 363 364 ; Don't delete anything if they're not balanced. 365 366 ; CHECK: define void @test12(i8* %a) #0 { 367 ; CHECK-NEXT: entry: 368 ; CHECK-NEXT: %outer = tail call i8* @objc_retain(i8* %a) [[NUW]] 369 ; CHECK-NEXT: %inner = tail call i8* @objc_retain(i8* %a) [[NUW]] 370 ; CHECK-NEXT: br label %loop 371 ; CHECK-NOT: @objc_ 372 ; CHECK: exit: 373 ; CHECK-NEXT: call void @objc_release(i8* %a) [[NUW]] 374 ; CHECK-NEXT: call void @objc_release(i8* %a) [[NUW]], !clang.imprecise_release !0 375 ; CHECK-NEXT: ret void 376 ; CHECK-NEXT: } 377 define void @test12(i8* %a) nounwind { 378 entry: 379 %outer = tail call i8* @objc_retain(i8* %a) nounwind 380 %inner = tail call i8* @objc_retain(i8* %a) nounwind 381 br label %loop 382 383 loop: 384 br i1 undef, label %true, label %more 385 386 true: 387 ret void 388 389 more: 390 br i1 undef, label %exit, label %loop 391 392 exit: 393 call void @objc_release(i8* %a) nounwind 394 call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0 395 ret void 396 } 397 398 ; Do not improperly pair retains in a for loop with releases outside of a for 399 ; loop when the proper pairing is disguised by a separate provenance represented 400 ; by an alloca. 401 ; rdar://12969722 402 403 ; CHECK: define void @test13(i8* %a) [[NUW]] { 404 ; CHECK: entry: 405 ; CHECK: tail call i8* @objc_retain(i8* %a) [[NUW]] 406 ; CHECK: loop: 407 ; CHECK: tail call i8* @objc_retain(i8* %a) [[NUW]] 408 ; CHECK: call void @block_callee 409 ; CHECK: call void @objc_release(i8* %reloaded_a) [[NUW]] 410 ; CHECK: exit: 411 ; CHECK: call void @objc_release(i8* %a) [[NUW]] 412 ; CHECK: } 413 define void @test13(i8* %a) nounwind { 414 entry: 415 %block = alloca i8* 416 %a1 = tail call i8* @objc_retain(i8* %a) nounwind 417 br label %loop 418 419 loop: 420 %a2 = tail call i8* @objc_retain(i8* %a) nounwind 421 store i8* %a, i8** %block, align 8 422 %casted_block = bitcast i8** %block to void ()* 423 call void @block_callee(void ()* %casted_block) 424 %reloaded_a = load i8*, i8** %block, align 8 425 call void @objc_release(i8* %reloaded_a) nounwind, !clang.imprecise_release !0 426 br i1 undef, label %loop, label %exit 427 428 exit: 429 call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0 430 ret void 431 } 432 433 ; CHECK: attributes [[NUW]] = { nounwind } 434 435 !0 = !{} 436