1 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2 ; RUN: opt -S -passes=objc-arc < %s | FileCheck %s
5 ; Detect loop boundaries and don't move retains and releases
8 declare void @use_pointer(ptr)
9 declare ptr @llvm.objc.retain(ptr)
10 declare void @llvm.objc.release(ptr)
11 declare void @callee()
12 declare void @block_callee(ptr)
14 define void @test0(ptr %digits) {
15 ; CHECK-LABEL: @test0(
17 ; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[DIGITS:%.*]]) #[[ATTR0:[0-9]+]]
18 ; CHECK-NEXT: call void @use_pointer(ptr [[DIGITS]])
19 ; CHECK-NEXT: br label [[FOR_BODY:%.*]]
21 ; CHECK-NEXT: [[UPCDIGITINDEX_01:%.*]] = phi i64 [ 2, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY]] ]
22 ; CHECK-NEXT: call void @use_pointer(ptr [[DIGITS]])
23 ; CHECK-NEXT: [[INC]] = add i64 [[UPCDIGITINDEX_01]], 1
24 ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[INC]], 12
25 ; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_END:%.*]]
27 ; CHECK-NEXT: call void @llvm.objc.release(ptr [[DIGITS]]) #[[ATTR0]], !clang.imprecise_release !0
28 ; CHECK-NEXT: ret void
31 %tmp1 = call ptr @llvm.objc.retain(ptr %digits) nounwind
32 call void @use_pointer(ptr %digits)
35 for.body: ; preds = %for.body, %entry
36 %upcDigitIndex.01 = phi i64 [ 2, %entry ], [ %inc, %for.body ]
37 call void @use_pointer(ptr %digits)
38 %inc = add i64 %upcDigitIndex.01, 1
39 %cmp = icmp ult i64 %inc, 12
40 br i1 %cmp, label %for.body, label %for.end
42 for.end: ; preds = %for.body
43 call void @llvm.objc.release(ptr %digits) nounwind, !clang.imprecise_release !0
47 define void @test1(ptr %digits) {
48 ; CHECK-LABEL: @test1(
50 ; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[DIGITS:%.*]]) #[[ATTR0]]
51 ; CHECK-NEXT: br label [[FOR_BODY:%.*]]
53 ; CHECK-NEXT: [[UPCDIGITINDEX_01:%.*]] = phi i64 [ 2, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY]] ]
54 ; CHECK-NEXT: call void @use_pointer(ptr [[DIGITS]])
55 ; CHECK-NEXT: call void @use_pointer(ptr [[DIGITS]])
56 ; CHECK-NEXT: [[INC]] = add i64 [[UPCDIGITINDEX_01]], 1
57 ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[INC]], 12
58 ; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_END:%.*]]
60 ; CHECK-NEXT: call void @llvm.objc.release(ptr [[DIGITS]]) #[[ATTR0]], !clang.imprecise_release !0
61 ; CHECK-NEXT: ret void
64 %tmp1 = call ptr @llvm.objc.retain(ptr %digits) nounwind
67 for.body: ; preds = %for.body, %entry
68 %upcDigitIndex.01 = phi i64 [ 2, %entry ], [ %inc, %for.body ]
69 call void @use_pointer(ptr %digits)
70 call void @use_pointer(ptr %digits)
71 %inc = add i64 %upcDigitIndex.01, 1
72 %cmp = icmp ult i64 %inc, 12
73 br i1 %cmp, label %for.body, label %for.end
75 for.end: ; preds = %for.body
76 call void @llvm.objc.release(ptr %digits) nounwind, !clang.imprecise_release !0
80 define void @test2(ptr %digits) {
81 ; CHECK-LABEL: @test2(
83 ; CHECK-NEXT: [[TMP1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[DIGITS:%.*]]) #[[ATTR0]]
84 ; CHECK-NEXT: br label [[FOR_BODY:%.*]]
86 ; CHECK-NEXT: [[UPCDIGITINDEX_01:%.*]] = phi i64 [ 2, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY]] ]
87 ; CHECK-NEXT: call void @use_pointer(ptr [[DIGITS]])
88 ; CHECK-NEXT: [[INC]] = add i64 [[UPCDIGITINDEX_01]], 1
89 ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[INC]], 12
90 ; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_END:%.*]]
92 ; CHECK-NEXT: call void @use_pointer(ptr [[DIGITS]])
93 ; CHECK-NEXT: call void @llvm.objc.release(ptr [[DIGITS]]) #[[ATTR0]], !clang.imprecise_release !0
94 ; CHECK-NEXT: ret void
97 %tmp1 = call ptr @llvm.objc.retain(ptr %digits) nounwind
100 for.body: ; preds = %for.body, %entry
101 %upcDigitIndex.01 = phi i64 [ 2, %entry ], [ %inc, %for.body ]
102 call void @use_pointer(ptr %digits)
103 %inc = add i64 %upcDigitIndex.01, 1
104 %cmp = icmp ult i64 %inc, 12
105 br i1 %cmp, label %for.body, label %for.end
107 for.end: ; preds = %for.body
108 call void @use_pointer(ptr %digits)
109 call void @llvm.objc.release(ptr %digits) nounwind, !clang.imprecise_release !0
113 ; Delete nested retain+release pairs around loops.
114 define void @test3(ptr %a) nounwind {
115 ; CHECK-LABEL: @test3(
117 ; CHECK-NEXT: [[OUTER:%.*]] = tail call ptr @llvm.objc.retain(ptr [[A:%.*]]) #[[ATTR0]]
118 ; CHECK-NEXT: br label [[LOOP:%.*]]
120 ; CHECK-NEXT: call void @callee()
121 ; CHECK-NEXT: store i8 0, ptr [[A]], align 1
122 ; CHECK-NEXT: br i1 undef, label [[LOOP]], label [[EXIT:%.*]]
124 ; CHECK-NEXT: call void @llvm.objc.release(ptr [[A]]) #[[ATTR0]], !clang.imprecise_release !0
125 ; CHECK-NEXT: ret void
128 %outer = call ptr @llvm.objc.retain(ptr %a) nounwind
129 %inner = call ptr @llvm.objc.retain(ptr %a) nounwind
135 br i1 undef, label %loop, label %exit
138 call void @llvm.objc.release(ptr %a) nounwind
139 call void @llvm.objc.release(ptr %a) nounwind, !clang.imprecise_release !0
143 define void @test4(ptr %a) nounwind {
144 ; CHECK-LABEL: @test4(
146 ; CHECK-NEXT: [[OUTER:%.*]] = tail call ptr @llvm.objc.retain(ptr [[A:%.*]]) #[[ATTR0]]
147 ; CHECK-NEXT: br label [[LOOP:%.*]]
149 ; CHECK-NEXT: br label [[MORE:%.*]]
151 ; CHECK-NEXT: call void @callee()
152 ; CHECK-NEXT: call void @callee()
153 ; CHECK-NEXT: store i8 0, ptr [[A]], align 1
154 ; CHECK-NEXT: br i1 undef, label [[LOOP]], label [[EXIT:%.*]]
156 ; CHECK-NEXT: call void @llvm.objc.release(ptr [[A]]) #[[ATTR0]], !clang.imprecise_release !0
157 ; CHECK-NEXT: ret void
160 %outer = call ptr @llvm.objc.retain(ptr %a) nounwind
161 %inner = call ptr @llvm.objc.retain(ptr %a) nounwind
171 br i1 undef, label %loop, label %exit
174 call void @llvm.objc.release(ptr %a) nounwind
175 call void @llvm.objc.release(ptr %a) nounwind, !clang.imprecise_release !0
179 define void @test5(ptr %a) nounwind {
180 ; CHECK-LABEL: @test5(
182 ; CHECK-NEXT: [[OUTER:%.*]] = tail call ptr @llvm.objc.retain(ptr [[A:%.*]]) #[[ATTR0]]
183 ; CHECK-NEXT: call void @callee()
184 ; CHECK-NEXT: br label [[LOOP:%.*]]
186 ; CHECK-NEXT: br i1 undef, label [[TRUE:%.*]], label [[MORE:%.*]]
188 ; CHECK-NEXT: br label [[MORE]]
190 ; CHECK-NEXT: br i1 undef, label [[EXIT:%.*]], label [[LOOP]]
192 ; CHECK-NEXT: call void @use_pointer(ptr [[A]])
193 ; CHECK-NEXT: call void @llvm.objc.release(ptr [[A]]) #[[ATTR0]], !clang.imprecise_release !0
194 ; CHECK-NEXT: ret void
197 %outer = tail call ptr @llvm.objc.retain(ptr %a) nounwind
198 %inner = tail call ptr @llvm.objc.retain(ptr %a) nounwind
203 br i1 undef, label %true, label %more
209 br i1 undef, label %exit, label %loop
212 call void @use_pointer(ptr %a)
213 call void @llvm.objc.release(ptr %a) nounwind
214 call void @llvm.objc.release(ptr %a) nounwind, !clang.imprecise_release !0
218 define void @test6(ptr %a) nounwind {
219 ; CHECK-LABEL: @test6(
221 ; CHECK-NEXT: [[OUTER:%.*]] = tail call ptr @llvm.objc.retain(ptr [[A:%.*]]) #[[ATTR0]]
222 ; CHECK-NEXT: br label [[LOOP:%.*]]
224 ; CHECK-NEXT: br i1 undef, label [[TRUE:%.*]], label [[MORE:%.*]]
226 ; CHECK-NEXT: call void @callee()
227 ; CHECK-NEXT: br label [[MORE]]
229 ; CHECK-NEXT: br i1 undef, label [[EXIT:%.*]], label [[LOOP]]
231 ; CHECK-NEXT: call void @use_pointer(ptr [[A]])
232 ; CHECK-NEXT: call void @llvm.objc.release(ptr [[A]]) #[[ATTR0]], !clang.imprecise_release !0
233 ; CHECK-NEXT: ret void
236 %outer = tail call ptr @llvm.objc.retain(ptr %a) nounwind
237 %inner = tail call ptr @llvm.objc.retain(ptr %a) nounwind
241 br i1 undef, label %true, label %more
248 br i1 undef, label %exit, label %loop
251 call void @use_pointer(ptr %a)
252 call void @llvm.objc.release(ptr %a) nounwind
253 call void @llvm.objc.release(ptr %a) nounwind, !clang.imprecise_release !0
257 define void @test7(ptr %a) nounwind {
258 ; CHECK-LABEL: @test7(
260 ; CHECK-NEXT: [[OUTER:%.*]] = tail call ptr @llvm.objc.retain(ptr [[A:%.*]]) #[[ATTR0]]
261 ; CHECK-NEXT: call void @callee()
262 ; CHECK-NEXT: br label [[LOOP:%.*]]
264 ; CHECK-NEXT: br i1 undef, label [[TRUE:%.*]], label [[MORE:%.*]]
266 ; CHECK-NEXT: call void @use_pointer(ptr [[A]])
267 ; CHECK-NEXT: br label [[MORE]]
269 ; CHECK-NEXT: br i1 undef, label [[EXIT:%.*]], label [[LOOP]]
271 ; CHECK-NEXT: call void @llvm.objc.release(ptr [[A]]) #[[ATTR0]], !clang.imprecise_release !0
272 ; CHECK-NEXT: ret void
275 %outer = tail call ptr @llvm.objc.retain(ptr %a) nounwind
276 %inner = tail call ptr @llvm.objc.retain(ptr %a) nounwind
281 br i1 undef, label %true, label %more
284 call void @use_pointer(ptr %a)
288 br i1 undef, label %exit, label %loop
291 call void @llvm.objc.release(ptr %a) nounwind
292 call void @llvm.objc.release(ptr %a) nounwind, !clang.imprecise_release !0
296 define void @test8(ptr %a) nounwind {
297 ; CHECK-LABEL: @test8(
299 ; CHECK-NEXT: [[OUTER:%.*]] = tail call ptr @llvm.objc.retain(ptr [[A:%.*]]) #[[ATTR0]]
300 ; CHECK-NEXT: br label [[LOOP:%.*]]
302 ; CHECK-NEXT: br i1 undef, label [[TRUE:%.*]], label [[MORE:%.*]]
304 ; CHECK-NEXT: call void @callee()
305 ; CHECK-NEXT: call void @use_pointer(ptr [[A]])
306 ; CHECK-NEXT: br label [[MORE]]
308 ; CHECK-NEXT: br i1 undef, label [[EXIT:%.*]], label [[LOOP]]
310 ; CHECK-NEXT: call void @llvm.objc.release(ptr [[A]]) #[[ATTR0]], !clang.imprecise_release !0
311 ; CHECK-NEXT: ret void
314 %outer = tail call ptr @llvm.objc.retain(ptr %a) nounwind
315 %inner = tail call ptr @llvm.objc.retain(ptr %a) nounwind
319 br i1 undef, label %true, label %more
323 call void @use_pointer(ptr %a)
327 br i1 undef, label %exit, label %loop
330 call void @llvm.objc.release(ptr %a) nounwind
331 call void @llvm.objc.release(ptr %a) nounwind, !clang.imprecise_release !0
335 define void @test9(ptr %a) nounwind {
336 ; CHECK-LABEL: @test9(
338 ; CHECK-NEXT: br label [[LOOP:%.*]]
340 ; CHECK-NEXT: br i1 undef, label [[TRUE:%.*]], label [[MORE:%.*]]
342 ; CHECK-NEXT: call void @use_pointer(ptr [[A:%.*]])
343 ; CHECK-NEXT: br label [[MORE]]
345 ; CHECK-NEXT: br i1 undef, label [[EXIT:%.*]], label [[LOOP]]
347 ; CHECK-NEXT: ret void
350 %outer = tail call ptr @llvm.objc.retain(ptr %a) nounwind
351 %inner = tail call ptr @llvm.objc.retain(ptr %a) nounwind
355 br i1 undef, label %true, label %more
358 call void @use_pointer(ptr %a)
362 br i1 undef, label %exit, label %loop
365 call void @llvm.objc.release(ptr %a) nounwind
366 call void @llvm.objc.release(ptr %a) nounwind, !clang.imprecise_release !0
370 define void @test10(ptr %a) nounwind {
371 ; CHECK-LABEL: @test10(
373 ; CHECK-NEXT: br label [[LOOP:%.*]]
375 ; CHECK-NEXT: br i1 undef, label [[TRUE:%.*]], label [[MORE:%.*]]
377 ; CHECK-NEXT: call void @callee()
378 ; CHECK-NEXT: br label [[MORE]]
380 ; CHECK-NEXT: br i1 undef, label [[EXIT:%.*]], label [[LOOP]]
382 ; CHECK-NEXT: ret void
385 %outer = tail call ptr @llvm.objc.retain(ptr %a) nounwind
386 %inner = tail call ptr @llvm.objc.retain(ptr %a) nounwind
390 br i1 undef, label %true, label %more
397 br i1 undef, label %exit, label %loop
400 call void @llvm.objc.release(ptr %a) nounwind
401 call void @llvm.objc.release(ptr %a) nounwind, !clang.imprecise_release !0
405 define void @test11(ptr %a) nounwind {
406 ; CHECK-LABEL: @test11(
408 ; CHECK-NEXT: br label [[LOOP:%.*]]
410 ; CHECK-NEXT: br i1 undef, label [[TRUE:%.*]], label [[MORE:%.*]]
412 ; CHECK-NEXT: br label [[MORE]]
414 ; CHECK-NEXT: br i1 undef, label [[EXIT:%.*]], label [[LOOP]]
416 ; CHECK-NEXT: ret void
419 %outer = tail call ptr @llvm.objc.retain(ptr %a) nounwind
420 %inner = tail call ptr @llvm.objc.retain(ptr %a) nounwind
424 br i1 undef, label %true, label %more
430 br i1 undef, label %exit, label %loop
433 call void @llvm.objc.release(ptr %a) nounwind
434 call void @llvm.objc.release(ptr %a) nounwind, !clang.imprecise_release !0
438 ; Don't delete anything if they're not balanced.
440 define void @test12(ptr %a) nounwind {
441 ; CHECK-LABEL: @test12(
443 ; CHECK-NEXT: [[OUTER:%.*]] = tail call ptr @llvm.objc.retain(ptr [[A:%.*]]) #[[ATTR0]]
444 ; CHECK-NEXT: [[INNER:%.*]] = tail call ptr @llvm.objc.retain(ptr [[A]]) #[[ATTR0]]
445 ; CHECK-NEXT: br label [[LOOP:%.*]]
447 ; CHECK-NEXT: br i1 undef, label [[TRUE:%.*]], label [[MORE:%.*]]
449 ; CHECK-NEXT: ret void
451 ; CHECK-NEXT: br i1 undef, label [[EXIT:%.*]], label [[LOOP]]
453 ; CHECK-NEXT: call void @llvm.objc.release(ptr [[A]]) #[[ATTR0]]
454 ; CHECK-NEXT: call void @llvm.objc.release(ptr [[A]]) #[[ATTR0]], !clang.imprecise_release !0
455 ; CHECK-NEXT: ret void
458 %outer = tail call ptr @llvm.objc.retain(ptr %a) nounwind
459 %inner = tail call ptr @llvm.objc.retain(ptr %a) nounwind
463 br i1 undef, label %true, label %more
469 br i1 undef, label %exit, label %loop
472 call void @llvm.objc.release(ptr %a) nounwind
473 call void @llvm.objc.release(ptr %a) nounwind, !clang.imprecise_release !0
477 ; Do not improperly pair retains in a for loop with releases outside of a for
478 ; loop when the proper pairing is disguised by a separate provenance represented
482 define void @test13(ptr %a) nounwind {
483 ; CHECK-LABEL: @test13(
485 ; CHECK-NEXT: [[BLOCK:%.*]] = alloca ptr, align 8
486 ; CHECK-NEXT: [[A1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[A:%.*]]) #[[ATTR0]]
487 ; CHECK-NEXT: br label [[LOOP:%.*]]
489 ; CHECK-NEXT: [[A2:%.*]] = tail call ptr @llvm.objc.retain(ptr [[A]]) #[[ATTR0]]
490 ; CHECK-NEXT: store ptr [[A]], ptr [[BLOCK]], align 8
491 ; CHECK-NEXT: call void @block_callee(ptr [[BLOCK]])
492 ; CHECK-NEXT: [[RELOADED_A:%.*]] = load ptr, ptr [[BLOCK]], align 8
493 ; CHECK-NEXT: call void @llvm.objc.release(ptr [[RELOADED_A]]) #[[ATTR0]], !clang.imprecise_release !0
494 ; CHECK-NEXT: br i1 undef, label [[LOOP]], label [[EXIT:%.*]]
496 ; CHECK-NEXT: call void @llvm.objc.release(ptr [[A]]) #[[ATTR0]], !clang.imprecise_release !0
497 ; CHECK-NEXT: ret void
501 %a1 = tail call ptr @llvm.objc.retain(ptr %a) nounwind
505 %a2 = tail call ptr @llvm.objc.retain(ptr %a) nounwind
506 store ptr %a, ptr %block, align 8
507 call void @block_callee(ptr %block)
508 %reloaded_a = load ptr, ptr %block, align 8
509 call void @llvm.objc.release(ptr %reloaded_a) nounwind, !clang.imprecise_release !0
510 br i1 undef, label %loop, label %exit
513 call void @llvm.objc.release(ptr %a) nounwind, !clang.imprecise_release !0
517 ; The retain call in the entry block shouldn't be moved to the loop body.
519 define void @test14(ptr %val0, i8 %val1) {
520 ; CHECK-LABEL: @test14(
522 ; CHECK-NEXT: [[V1:%.*]] = tail call ptr @llvm.objc.retain(ptr [[VAL0:%.*]]) #[[ATTR0]]
523 ; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[VAL0]], null
524 ; CHECK-NEXT: br i1 [[CMP]], label [[IF_END27:%.*]], label [[IF_THEN:%.*]]
526 ; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i8 [[VAL1:%.*]], 1
527 ; CHECK-NEXT: br label [[FOR_BODY:%.*]]
529 ; CHECK-NEXT: [[CMP6:%.*]] = icmp eq i8 [[VAL1]], 2
530 ; CHECK-NEXT: br i1 [[CMP6]], label [[FOR_BODY]], label [[FOR_END_LOOPEXIT:%.*]]
532 ; CHECK-NEXT: call void @callee()
533 ; CHECK-NEXT: [[TOBOOL9:%.*]] = icmp eq i8 [[VAL1]], 0
534 ; CHECK-NEXT: br i1 [[TOBOOL9]], label [[FOR_COND:%.*]], label [[IF_THEN10:%.*]]
536 ; CHECK-NEXT: br label [[FOR_END:%.*]]
537 ; CHECK: for.end.loopexit:
538 ; CHECK-NEXT: br label [[FOR_END]]
540 ; CHECK-NEXT: call void @callee()
541 ; CHECK-NEXT: call void @use_pointer(ptr [[V1]])
542 ; CHECK-NEXT: br label [[IF_END27]]
544 ; CHECK-NEXT: call void @llvm.objc.release(ptr [[V1]]) #[[ATTR0]], !clang.imprecise_release !0
545 ; CHECK-NEXT: ret void
548 %v1 = tail call ptr @llvm.objc.retain(ptr %val0)
549 %cmp = icmp eq ptr %val0, null
550 br i1 %cmp, label %if.end27, label %if.then
553 %tobool = icmp eq i8 %val1, 1
557 %cmp6 = icmp eq i8 %val1, 2
558 br i1 %cmp6, label %for.body, label %for.end.loopexit
562 %tobool9 = icmp eq i8 %val1, 0
563 br i1 %tobool9, label %for.cond, label %if.then10
573 call void @use_pointer(ptr %v1)
577 call void @llvm.objc.release(ptr %v1) #0, !clang.imprecise_release !0