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