1 ; RUN: opt -S -objc-arc < %s | FileCheck %s
4 ; Detect loop boundaries and don't move retains and releases
7 declare void @use_pointer(i8*)
8 declare i8* @llvm.objc.retain(i8*)
9 declare void @llvm.objc.release(i8*)
10 declare void @callee()
11 declare void @block_callee(void ()*)
13 ; CHECK-LABEL: define void @test0(
14 ; CHECK: call i8* @llvm.objc.retain
18 ; CHECK: call void @llvm.objc.release
20 define void @test0(i8* %digits) {
22 %tmp1 = call i8* @llvm.objc.retain(i8* %digits) nounwind
23 call void @use_pointer(i8* %digits)
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
33 for.end: ; preds = %for.body
34 call void @llvm.objc.release(i8* %digits) nounwind, !clang.imprecise_release !0
38 ; CHECK-LABEL: define void @test1(
39 ; CHECK: call i8* @llvm.objc.retain
43 ; CHECK: void @llvm.objc.release
45 define void @test1(i8* %digits) {
47 %tmp1 = call i8* @llvm.objc.retain(i8* %digits) nounwind
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
58 for.end: ; preds = %for.body
59 call void @llvm.objc.release(i8* %digits) nounwind, !clang.imprecise_release !0
63 ; CHECK-LABEL: define void @test2(
64 ; CHECK: call i8* @llvm.objc.retain
68 ; CHECK: void @llvm.objc.release
70 define void @test2(i8* %digits) {
72 %tmp1 = call i8* @llvm.objc.retain(i8* %digits) nounwind
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
82 for.end: ; preds = %for.body
83 call void @use_pointer(i8* %digits)
84 call void @llvm.objc.release(i8* %digits) nounwind, !clang.imprecise_release !0
88 ; Delete nested retain+release pairs around loops.
90 ; CHECK: define void @test3(i8* %a) #0 {
92 ; CHECK-NEXT: tail call i8* @llvm.objc.retain(i8* %a) [[NUW:#[0-9]+]]
93 ; CHECK-NEXT: br label %loop
94 ; CHECK-NOT: @llvm.objc.
96 ; CHECK-NEXT: call void @llvm.objc.release(i8* %a)
97 ; CHECK-NEXT: ret void
99 define void @test3(i8* %a) nounwind {
101 %outer = call i8* @llvm.objc.retain(i8* %a) nounwind
102 %inner = call i8* @llvm.objc.retain(i8* %a) nounwind
108 br i1 undef, label %loop, label %exit
111 call void @llvm.objc.release(i8* %a) nounwind
112 call void @llvm.objc.release(i8* %a) nounwind, !clang.imprecise_release !0
116 ; CHECK: define void @test4(i8* %a) #0 {
118 ; CHECK-NEXT: tail call i8* @llvm.objc.retain(i8* %a) [[NUW]]
119 ; CHECK-NEXT: br label %loop
120 ; CHECK-NOT: @llvm.objc.
122 ; CHECK-NEXT: call void @llvm.objc.release(i8* %a)
123 ; CHECK-NEXT: ret void
125 define void @test4(i8* %a) nounwind {
127 %outer = call i8* @llvm.objc.retain(i8* %a) nounwind
128 %inner = call i8* @llvm.objc.retain(i8* %a) nounwind
138 br i1 undef, label %loop, label %exit
141 call void @llvm.objc.release(i8* %a) nounwind
142 call void @llvm.objc.release(i8* %a) nounwind, !clang.imprecise_release !0
146 ; CHECK: define void @test5(i8* %a) #0 {
148 ; CHECK-NEXT: tail call i8* @llvm.objc.retain(i8* %a) [[NUW]]
149 ; CHECK-NEXT: call void @callee()
150 ; CHECK-NEXT: br label %loop
151 ; CHECK-NOT: @llvm.objc.
153 ; CHECK-NEXT: call void @use_pointer(i8* %a)
154 ; CHECK-NEXT: call void @llvm.objc.release(i8* %a)
155 ; CHECK-NEXT: ret void
157 define void @test5(i8* %a) nounwind {
159 %outer = tail call i8* @llvm.objc.retain(i8* %a) nounwind
160 %inner = tail call i8* @llvm.objc.retain(i8* %a) nounwind
165 br i1 undef, label %true, label %more
171 br i1 undef, label %exit, label %loop
174 call void @use_pointer(i8* %a)
175 call void @llvm.objc.release(i8* %a) nounwind
176 call void @llvm.objc.release(i8* %a) nounwind, !clang.imprecise_release !0
180 ; CHECK: define void @test6(i8* %a) #0 {
182 ; CHECK-NEXT: tail call i8* @llvm.objc.retain(i8* %a) [[NUW]]
183 ; CHECK-NEXT: br label %loop
184 ; CHECK-NOT: @llvm.objc.
186 ; CHECK-NEXT: call void @use_pointer(i8* %a)
187 ; CHECK-NEXT: call void @llvm.objc.release(i8* %a)
188 ; CHECK-NEXT: ret void
190 define void @test6(i8* %a) nounwind {
192 %outer = tail call i8* @llvm.objc.retain(i8* %a) nounwind
193 %inner = tail call i8* @llvm.objc.retain(i8* %a) nounwind
197 br i1 undef, label %true, label %more
204 br i1 undef, label %exit, label %loop
207 call void @use_pointer(i8* %a)
208 call void @llvm.objc.release(i8* %a) nounwind
209 call void @llvm.objc.release(i8* %a) nounwind, !clang.imprecise_release !0
213 ; CHECK: define void @test7(i8* %a) #0 {
215 ; CHECK-NEXT: tail call i8* @llvm.objc.retain(i8* %a) [[NUW]]
216 ; CHECK-NEXT: call void @callee()
217 ; CHECK-NEXT: br label %loop
218 ; CHECK-NOT: @llvm.objc.
220 ; CHECK-NEXT: call void @llvm.objc.release(i8* %a)
221 ; CHECK-NEXT: ret void
223 define void @test7(i8* %a) nounwind {
225 %outer = tail call i8* @llvm.objc.retain(i8* %a) nounwind
226 %inner = tail call i8* @llvm.objc.retain(i8* %a) nounwind
231 br i1 undef, label %true, label %more
234 call void @use_pointer(i8* %a)
238 br i1 undef, label %exit, label %loop
241 call void @llvm.objc.release(i8* %a) nounwind
242 call void @llvm.objc.release(i8* %a) nounwind, !clang.imprecise_release !0
246 ; CHECK: define void @test8(i8* %a) #0 {
248 ; CHECK-NEXT: tail call i8* @llvm.objc.retain(i8* %a) [[NUW]]
249 ; CHECK-NEXT: br label %loop
250 ; CHECK-NOT: @llvm.objc.
252 ; CHECK-NEXT: call void @llvm.objc.release(i8* %a)
253 ; CHECK-NEXT: ret void
255 define void @test8(i8* %a) nounwind {
257 %outer = tail call i8* @llvm.objc.retain(i8* %a) nounwind
258 %inner = tail call i8* @llvm.objc.retain(i8* %a) nounwind
262 br i1 undef, label %true, label %more
266 call void @use_pointer(i8* %a)
270 br i1 undef, label %exit, label %loop
273 call void @llvm.objc.release(i8* %a) nounwind
274 call void @llvm.objc.release(i8* %a) nounwind, !clang.imprecise_release !0
278 ; CHECK: define void @test9(i8* %a) #0 {
280 ; CHECK-NEXT: br label %loop
281 ; CHECK-NOT: @llvm.objc.
283 ; CHECK-NEXT: ret void
285 define void @test9(i8* %a) nounwind {
287 %outer = tail call i8* @llvm.objc.retain(i8* %a) nounwind
288 %inner = tail call i8* @llvm.objc.retain(i8* %a) nounwind
292 br i1 undef, label %true, label %more
295 call void @use_pointer(i8* %a)
299 br i1 undef, label %exit, label %loop
302 call void @llvm.objc.release(i8* %a) nounwind
303 call void @llvm.objc.release(i8* %a) nounwind, !clang.imprecise_release !0
307 ; CHECK: define void @test10(i8* %a) #0 {
309 ; CHECK-NEXT: br label %loop
310 ; CHECK-NOT: @llvm.objc.
312 ; CHECK-NEXT: ret void
314 define void @test10(i8* %a) nounwind {
316 %outer = tail call i8* @llvm.objc.retain(i8* %a) nounwind
317 %inner = tail call i8* @llvm.objc.retain(i8* %a) nounwind
321 br i1 undef, label %true, label %more
328 br i1 undef, label %exit, label %loop
331 call void @llvm.objc.release(i8* %a) nounwind
332 call void @llvm.objc.release(i8* %a) nounwind, !clang.imprecise_release !0
336 ; CHECK: define void @test11(i8* %a) #0 {
338 ; CHECK-NEXT: br label %loop
339 ; CHECK-NOT: @llvm.objc.
341 ; CHECK-NEXT: ret void
343 define void @test11(i8* %a) nounwind {
345 %outer = tail call i8* @llvm.objc.retain(i8* %a) nounwind
346 %inner = tail call i8* @llvm.objc.retain(i8* %a) nounwind
350 br i1 undef, label %true, label %more
356 br i1 undef, label %exit, label %loop
359 call void @llvm.objc.release(i8* %a) nounwind
360 call void @llvm.objc.release(i8* %a) nounwind, !clang.imprecise_release !0
364 ; Don't delete anything if they're not balanced.
366 ; CHECK: define void @test12(i8* %a) #0 {
368 ; CHECK-NEXT: %outer = tail call i8* @llvm.objc.retain(i8* %a) [[NUW]]
369 ; CHECK-NEXT: %inner = tail call i8* @llvm.objc.retain(i8* %a) [[NUW]]
370 ; CHECK-NEXT: br label %loop
371 ; CHECK-NOT: @llvm.objc.
373 ; CHECK-NEXT: call void @llvm.objc.release(i8* %a) [[NUW]]
374 ; CHECK-NEXT: call void @llvm.objc.release(i8* %a) [[NUW]], !clang.imprecise_release !0
375 ; CHECK-NEXT: ret void
377 define void @test12(i8* %a) nounwind {
379 %outer = tail call i8* @llvm.objc.retain(i8* %a) nounwind
380 %inner = tail call i8* @llvm.objc.retain(i8* %a) nounwind
384 br i1 undef, label %true, label %more
390 br i1 undef, label %exit, label %loop
393 call void @llvm.objc.release(i8* %a) nounwind
394 call void @llvm.objc.release(i8* %a) nounwind, !clang.imprecise_release !0
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
403 ; CHECK: define void @test13(i8* %a) [[NUW]] {
405 ; CHECK: tail call i8* @llvm.objc.retain(i8* %a) [[NUW]]
407 ; CHECK: tail call i8* @llvm.objc.retain(i8* %a) [[NUW]]
408 ; CHECK: call void @block_callee
409 ; CHECK: call void @llvm.objc.release(i8* %reloaded_a) [[NUW]]
411 ; CHECK: call void @llvm.objc.release(i8* %a) [[NUW]]
413 define void @test13(i8* %a) nounwind {
416 %a1 = tail call i8* @llvm.objc.retain(i8* %a) nounwind
420 %a2 = tail call i8* @llvm.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 @llvm.objc.release(i8* %reloaded_a) nounwind, !clang.imprecise_release !0
426 br i1 undef, label %loop, label %exit
429 call void @llvm.objc.release(i8* %a) nounwind, !clang.imprecise_release !0
433 ; CHECK: attributes [[NUW]] = { nounwind }