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 7declare void @use_pointer(i8*) 8declare i8* @objc_retain(i8*) 9declare void @objc_release(i8*) 10declare void @callee() 11declare 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: } 20define void @test0(i8* %digits) { 21entry: 22 %tmp1 = call i8* @objc_retain(i8* %digits) nounwind 23 call void @use_pointer(i8* %digits) 24 br label %for.body 25 26for.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 33for.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: } 45define void @test1(i8* %digits) { 46entry: 47 %tmp1 = call i8* @objc_retain(i8* %digits) nounwind 48 br label %for.body 49 50for.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 58for.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: } 70define void @test2(i8* %digits) { 71entry: 72 %tmp1 = call i8* @objc_retain(i8* %digits) nounwind 73 br label %for.body 74 75for.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 82for.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: } 99define void @test3(i8* %a) nounwind { 100entry: 101 %outer = call i8* @objc_retain(i8* %a) nounwind 102 %inner = call i8* @objc_retain(i8* %a) nounwind 103 br label %loop 104 105loop: 106 call void @callee() 107 store i8 0, i8* %a 108 br i1 undef, label %loop, label %exit 109 110exit: 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: } 125define void @test4(i8* %a) nounwind { 126entry: 127 %outer = call i8* @objc_retain(i8* %a) nounwind 128 %inner = call i8* @objc_retain(i8* %a) nounwind 129 br label %loop 130 131loop: 132 br label %more 133 134more: 135 call void @callee() 136 call void @callee() 137 store i8 0, i8* %a 138 br i1 undef, label %loop, label %exit 139 140exit: 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: } 157define void @test5(i8* %a) nounwind { 158entry: 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 164loop: 165 br i1 undef, label %true, label %more 166 167true: 168 br label %more 169 170more: 171 br i1 undef, label %exit, label %loop 172 173exit: 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: } 190define void @test6(i8* %a) nounwind { 191entry: 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 196loop: 197 br i1 undef, label %true, label %more 198 199true: 200 call void @callee() 201 br label %more 202 203more: 204 br i1 undef, label %exit, label %loop 205 206exit: 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: } 223define void @test7(i8* %a) nounwind { 224entry: 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 230loop: 231 br i1 undef, label %true, label %more 232 233true: 234 call void @use_pointer(i8* %a) 235 br label %more 236 237more: 238 br i1 undef, label %exit, label %loop 239 240exit: 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: } 255define void @test8(i8* %a) nounwind { 256entry: 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 261loop: 262 br i1 undef, label %true, label %more 263 264true: 265 call void @callee() 266 call void @use_pointer(i8* %a) 267 br label %more 268 269more: 270 br i1 undef, label %exit, label %loop 271 272exit: 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: } 285define void @test9(i8* %a) nounwind { 286entry: 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 291loop: 292 br i1 undef, label %true, label %more 293 294true: 295 call void @use_pointer(i8* %a) 296 br label %more 297 298more: 299 br i1 undef, label %exit, label %loop 300 301exit: 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: } 314define void @test10(i8* %a) nounwind { 315entry: 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 320loop: 321 br i1 undef, label %true, label %more 322 323true: 324 call void @callee() 325 br label %more 326 327more: 328 br i1 undef, label %exit, label %loop 329 330exit: 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: } 343define void @test11(i8* %a) nounwind { 344entry: 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 349loop: 350 br i1 undef, label %true, label %more 351 352true: 353 br label %more 354 355more: 356 br i1 undef, label %exit, label %loop 357 358exit: 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: } 377define void @test12(i8* %a) nounwind { 378entry: 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 383loop: 384 br i1 undef, label %true, label %more 385 386true: 387 ret void 388 389more: 390 br i1 undef, label %exit, label %loop 391 392exit: 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: } 413define void @test13(i8* %a) nounwind { 414entry: 415 %block = alloca i8* 416 %a1 = tail call i8* @objc_retain(i8* %a) nounwind 417 br label %loop 418 419loop: 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 428exit: 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