• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1; RUN: opt -objc-arc -S < %s | FileCheck %s
2
3declare i8* @objc_retain(i8*)
4declare i8* @objc_retainAutoreleasedReturnValue(i8*)
5declare void @objc_release(i8*)
6declare i8* @objc_autorelease(i8*)
7declare i8* @objc_autoreleaseReturnValue(i8*)
8declare void @objc_autoreleasePoolPop(i8*)
9declare i8* @objc_autoreleasePoolPush()
10declare i8* @objc_retainBlock(i8*)
11
12declare i8* @objc_retainedObject(i8*)
13declare i8* @objc_unretainedObject(i8*)
14declare i8* @objc_unretainedPointer(i8*)
15
16declare void @use_pointer(i8*)
17declare void @callee()
18declare void @callee_fnptr(void ()*)
19declare void @invokee()
20declare i8* @returner()
21declare i8* @returner1()
22declare i8* @returner2()
23declare void @bar(i32 ()*)
24declare void @use_alloca(i8**)
25
26declare void @llvm.dbg.value(metadata, i64, metadata)
27
28declare i8* @objc_msgSend(i8*, i8*, ...)
29
30
31; In the presence of allocas, unconditionally remove retain/release pairs only
32; if they are known safe in both directions. This prevents matching up an inner
33; retain with the boundary guarding release in the following situation:
34;
35; %A = alloca
36; retain(%x)
37; retain(%x) <--- Inner Retain
38; store %x, %A
39; %y = load %A
40; ... DO STUFF ...
41; release(%y)
42; release(%x) <--- Guarding Release
43;
44; rdar://13750319
45
46; CHECK: define void @test1a(i8* %x)
47; CHECK: @objc_retain(i8* %x)
48; CHECK: @objc_retain(i8* %x)
49; CHECK: @objc_release(i8* %y)
50; CHECK: @objc_release(i8* %x)
51; CHECK: ret void
52; CHECK: }
53define void @test1a(i8* %x) {
54entry:
55  %A = alloca i8*
56  tail call i8* @objc_retain(i8* %x)
57  tail call i8* @objc_retain(i8* %x)
58  store i8* %x, i8** %A, align 8
59  %y = load i8** %A
60  call void @use_alloca(i8** %A)
61  call void @objc_release(i8* %y), !clang.imprecise_release !0
62  call void @use_pointer(i8* %x)
63  call void @objc_release(i8* %x), !clang.imprecise_release !0
64  ret void
65}
66
67; CHECK: define void @test1b(i8* %x)
68; CHECK: @objc_retain(i8* %x)
69; CHECK: @objc_retain(i8* %x)
70; CHECK: @objc_release(i8* %y)
71; CHECK: @objc_release(i8* %x)
72; CHECK: ret void
73; CHECK: }
74define void @test1b(i8* %x) {
75entry:
76  %A = alloca i8*
77  %gep = getelementptr i8** %A, i32 0
78  tail call i8* @objc_retain(i8* %x)
79  tail call i8* @objc_retain(i8* %x)
80  store i8* %x, i8** %gep, align 8
81  %y = load i8** %A
82  call void @use_alloca(i8** %A)
83  call void @objc_release(i8* %y), !clang.imprecise_release !0
84  call void @use_pointer(i8* %x)
85  call void @objc_release(i8* %x), !clang.imprecise_release !0
86  ret void
87}
88
89
90; CHECK: define void @test1c(i8* %x)
91; CHECK: @objc_retain(i8* %x)
92; CHECK: @objc_retain(i8* %x)
93; CHECK: @objc_release(i8* %y)
94; CHECK: @objc_release(i8* %x)
95; CHECK: ret void
96; CHECK: }
97define void @test1c(i8* %x) {
98entry:
99  %A = alloca i8*, i32 3
100  %gep = getelementptr i8** %A, i32 2
101  tail call i8* @objc_retain(i8* %x)
102  tail call i8* @objc_retain(i8* %x)
103  store i8* %x, i8** %gep, align 8
104  %y = load i8** %gep
105  call void @use_alloca(i8** %A)
106  call void @objc_release(i8* %y), !clang.imprecise_release !0
107  call void @use_pointer(i8* %x)
108  call void @objc_release(i8* %x), !clang.imprecise_release !0
109  ret void
110}
111
112
113; CHECK: define void @test1d(i8* %x)
114; CHECK: @objc_retain(i8* %x)
115; CHECK: @objc_retain(i8* %x)
116; CHECK: @objc_release(i8* %y)
117; CHECK: @objc_release(i8* %x)
118; CHECK: ret void
119; CHECK: }
120define void @test1d(i8* %x) {
121entry:
122  br i1 undef, label %use_allocaA, label %use_allocaB
123
124use_allocaA:
125  %allocaA = alloca i8*
126  br label %exit
127
128use_allocaB:
129  %allocaB = alloca i8*
130  br label %exit
131
132exit:
133  %A = phi i8** [ %allocaA, %use_allocaA ], [ %allocaB, %use_allocaB ]
134  %gep = getelementptr i8** %A, i32 0
135  tail call i8* @objc_retain(i8* %x)
136  tail call i8* @objc_retain(i8* %x)
137  store i8* %x, i8** %gep, align 8
138  %y = load i8** %gep
139  call void @use_alloca(i8** %A)
140  call void @objc_release(i8* %y), !clang.imprecise_release !0
141  call void @use_pointer(i8* %x)
142  call void @objc_release(i8* %x), !clang.imprecise_release !0
143  ret void
144}
145
146; CHECK: define void @test1e(i8* %x)
147; CHECK: @objc_retain(i8* %x)
148; CHECK: @objc_retain(i8* %x)
149; CHECK: @objc_release(i8* %y)
150; CHECK: @objc_release(i8* %x)
151; CHECK: ret void
152; CHECK: }
153define void @test1e(i8* %x) {
154entry:
155  br i1 undef, label %use_allocaA, label %use_allocaB
156
157use_allocaA:
158  %allocaA = alloca i8*, i32 4
159  br label %exit
160
161use_allocaB:
162  %allocaB = alloca i8*, i32 4
163  br label %exit
164
165exit:
166  %A = phi i8** [ %allocaA, %use_allocaA ], [ %allocaB, %use_allocaB ]
167  %gep = getelementptr i8** %A, i32 2
168  tail call i8* @objc_retain(i8* %x)
169  tail call i8* @objc_retain(i8* %x)
170  store i8* %x, i8** %gep, align 8
171  %y = load i8** %gep
172  call void @use_alloca(i8** %A)
173  call void @objc_release(i8* %y), !clang.imprecise_release !0
174  call void @use_pointer(i8* %x)
175  call void @objc_release(i8* %x), !clang.imprecise_release !0
176  ret void
177}
178
179; CHECK: define void @test1f(i8* %x)
180; CHECK: @objc_retain(i8* %x)
181; CHECK: @objc_retain(i8* %x)
182; CHECK: @objc_release(i8* %y)
183; CHECK: @objc_release(i8* %x)
184; CHECK: ret void
185; CHECK: }
186define void @test1f(i8* %x) {
187entry:
188  %allocaOne = alloca i8*
189  %allocaTwo = alloca i8*
190  %A = select i1 undef, i8** %allocaOne, i8** %allocaTwo
191  tail call i8* @objc_retain(i8* %x)
192  tail call i8* @objc_retain(i8* %x)
193  store i8* %x, i8** %A, align 8
194  %y = load i8** %A
195  call void @use_alloca(i8** %A)
196  call void @objc_release(i8* %y), !clang.imprecise_release !0
197  call void @use_pointer(i8* %x)
198  call void @objc_release(i8* %x), !clang.imprecise_release !0
199  ret void
200}
201
202; Make sure that if a store is in a different basic block we handle known safe
203; conservatively.
204
205
206; CHECK: define void @test2a(i8* %x)
207; CHECK: @objc_retain(i8* %x)
208; CHECK: @objc_retain(i8* %x)
209; CHECK: @objc_release(i8* %y)
210; CHECK: @objc_release(i8* %x)
211; CHECK: ret void
212; CHECK: }
213define void @test2a(i8* %x) {
214entry:
215  %A = alloca i8*
216  store i8* %x, i8** %A, align 8
217  %y = load i8** %A
218  br label %bb1
219
220bb1:
221  br label %bb2
222
223bb2:
224  br label %bb3
225
226bb3:
227  tail call i8* @objc_retain(i8* %x)
228  tail call i8* @objc_retain(i8* %x)
229  call void @use_alloca(i8** %A)
230  call void @objc_release(i8* %y), !clang.imprecise_release !0
231  call void @use_pointer(i8* %x)
232  call void @objc_release(i8* %x), !clang.imprecise_release !0
233  ret void
234}
235
236; CHECK: define void @test2b(i8* %x)
237; CHECK: @objc_retain(i8* %x)
238; CHECK: @objc_retain(i8* %x)
239; CHECK: @objc_release(i8* %y)
240; CHECK: @objc_release(i8* %x)
241; CHECK: ret void
242; CHECK: }
243define void @test2b(i8* %x) {
244entry:
245  %A = alloca i8*
246  %gep1 = getelementptr i8** %A, i32 0
247  store i8* %x, i8** %gep1, align 8
248  %gep2 = getelementptr i8** %A, i32 0
249  %y = load i8** %gep2
250  br label %bb1
251
252bb1:
253  br label %bb2
254
255bb2:
256  br label %bb3
257
258bb3:
259  tail call i8* @objc_retain(i8* %x)
260  tail call i8* @objc_retain(i8* %x)
261  call void @use_alloca(i8** %A)
262  call void @objc_release(i8* %y), !clang.imprecise_release !0
263  call void @use_pointer(i8* %x)
264  call void @objc_release(i8* %x), !clang.imprecise_release !0
265  ret void
266}
267
268; CHECK: define void @test2c(i8* %x)
269; CHECK: @objc_retain(i8* %x)
270; CHECK: @objc_retain(i8* %x)
271; CHECK: @objc_release(i8* %y)
272; CHECK: @objc_release(i8* %x)
273; CHECK: ret void
274; CHECK: }
275define void @test2c(i8* %x) {
276entry:
277  %A = alloca i8*, i32 3
278  %gep1 = getelementptr i8** %A, i32 2
279  store i8* %x, i8** %gep1, align 8
280  %gep2 = getelementptr i8** %A, i32 2
281  %y = load i8** %gep2
282  tail call i8* @objc_retain(i8* %x)
283  br label %bb1
284
285bb1:
286  br label %bb2
287
288bb2:
289  br label %bb3
290
291bb3:
292  tail call i8* @objc_retain(i8* %x)
293  call void @use_alloca(i8** %A)
294  call void @objc_release(i8* %y), !clang.imprecise_release !0
295  call void @use_pointer(i8* %x)
296  call void @objc_release(i8* %x), !clang.imprecise_release !0
297  ret void
298}
299
300; CHECK: define void @test2d(i8* %x)
301; CHECK: @objc_retain(i8* %x)
302; CHECK: @objc_retain(i8* %x)
303; CHECK: @objc_release(i8* %y)
304; CHECK: @objc_release(i8* %x)
305; CHECK: ret void
306; CHECK: }
307define void @test2d(i8* %x) {
308entry:
309  tail call i8* @objc_retain(i8* %x)
310  br label %bb1
311
312bb1:
313  %Abb1 = alloca i8*, i32 3
314  %gepbb11 = getelementptr i8** %Abb1, i32 2
315  store i8* %x, i8** %gepbb11, align 8
316  %gepbb12 = getelementptr i8** %Abb1, i32 2
317  %ybb1 = load i8** %gepbb12
318  br label %bb3
319
320bb2:
321  %Abb2 = alloca i8*, i32 4
322  %gepbb21 = getelementptr i8** %Abb2, i32 2
323  store i8* %x, i8** %gepbb21, align 8
324  %gepbb22 = getelementptr i8** %Abb2, i32 2
325  %ybb2 = load i8** %gepbb22
326  br label %bb3
327
328bb3:
329  %A = phi i8** [ %Abb1, %bb1 ], [ %Abb2, %bb2 ]
330  %y = phi i8* [ %ybb1, %bb1 ], [ %ybb2, %bb2 ]
331  tail call i8* @objc_retain(i8* %x)
332  call void @use_alloca(i8** %A)
333  call void @objc_release(i8* %y), !clang.imprecise_release !0
334  call void @use_pointer(i8* %x)
335  call void @objc_release(i8* %x), !clang.imprecise_release !0
336  ret void
337}
338
339; Make sure in the presence of allocas, if we find a cfghazard we do not perform
340; code motion even if we are known safe. These two concepts are separate and
341; should be treated as such.
342;
343; rdar://13949644
344
345; CHECK: define void @test3a() {
346; CHECK: entry:
347; CHECK:   @objc_retainAutoreleasedReturnValue
348; CHECK:   @objc_retain
349; CHECK:   @objc_retain
350; CHECK:   @objc_retain
351; CHECK:   @objc_retain
352; CHECK: arraydestroy.body:
353; CHECK:   @objc_release
354; CHECK-NOT: @objc_release
355; CHECK: arraydestroy.done:
356; CHECK-NOT: @objc_release
357; CHECK: arraydestroy.body1:
358; CHECK:   @objc_release
359; CHECK-NOT: @objc_release
360; CHECK: arraydestroy.done1:
361; CHECK: @objc_release
362; CHECK: ret void
363; CHECK: }
364define void @test3a() {
365entry:
366  %keys = alloca [2 x i8*], align 16
367  %objs = alloca [2 x i8*], align 16
368
369  %call1 = call i8* @returner()
370  %tmp0 = tail call i8* @objc_retainAutoreleasedReturnValue(i8* %call1)
371
372  %objs.begin = getelementptr inbounds [2 x i8*]* %objs, i64 0, i64 0
373  tail call i8* @objc_retain(i8* %call1)
374  store i8* %call1, i8** %objs.begin, align 8
375  %objs.elt = getelementptr inbounds [2 x i8*]* %objs, i64 0, i64 1
376  tail call i8* @objc_retain(i8* %call1)
377  store i8* %call1, i8** %objs.elt
378
379  %call2 = call i8* @returner1()
380  %call3 = call i8* @returner2()
381  %keys.begin = getelementptr inbounds [2 x i8*]* %keys, i64 0, i64 0
382  tail call i8* @objc_retain(i8* %call2)
383  store i8* %call2, i8** %keys.begin, align 8
384  %keys.elt = getelementptr inbounds [2 x i8*]* %keys, i64 0, i64 1
385  tail call i8* @objc_retain(i8* %call3)
386  store i8* %call3, i8** %keys.elt
387
388  %gep = getelementptr inbounds [2 x i8*]* %objs, i64 0, i64 2
389  br label %arraydestroy.body
390
391arraydestroy.body:
392  %arraydestroy.elementPast = phi i8** [ %gep, %entry ], [ %arraydestroy.element, %arraydestroy.body ]
393  %arraydestroy.element = getelementptr inbounds i8** %arraydestroy.elementPast, i64 -1
394  %destroy_tmp = load i8** %arraydestroy.element, align 8
395  call void @objc_release(i8* %destroy_tmp), !clang.imprecise_release !0
396  %objs_ptr = getelementptr inbounds [2 x i8*]* %objs, i64 0, i64 0
397  %arraydestroy.cmp = icmp eq i8** %arraydestroy.element, %objs_ptr
398  br i1 %arraydestroy.cmp, label %arraydestroy.done, label %arraydestroy.body
399
400arraydestroy.done:
401  %gep1 = getelementptr inbounds [2 x i8*]* %keys, i64 0, i64 2
402  br label %arraydestroy.body1
403
404arraydestroy.body1:
405  %arraydestroy.elementPast1 = phi i8** [ %gep1, %arraydestroy.done ], [ %arraydestroy.element1, %arraydestroy.body1 ]
406  %arraydestroy.element1 = getelementptr inbounds i8** %arraydestroy.elementPast1, i64 -1
407  %destroy_tmp1 = load i8** %arraydestroy.element1, align 8
408  call void @objc_release(i8* %destroy_tmp1), !clang.imprecise_release !0
409  %keys_ptr = getelementptr inbounds [2 x i8*]* %keys, i64 0, i64 0
410  %arraydestroy.cmp1 = icmp eq i8** %arraydestroy.element1, %keys_ptr
411  br i1 %arraydestroy.cmp1, label %arraydestroy.done1, label %arraydestroy.body1
412
413arraydestroy.done1:
414  call void @objc_release(i8* %call1), !clang.imprecise_release !0
415  ret void
416}
417
418; Make sure that even though we stop said code motion we still allow for
419; pointers to be removed if we are known safe in both directions.
420;
421; rdar://13949644
422
423; CHECK: define void @test3b() {
424; CHECK: entry:
425; CHECK:   @objc_retainAutoreleasedReturnValue
426; CHECK:   @objc_retain
427; CHECK:   @objc_retain
428; CHECK:   @objc_retain
429; CHECK:   @objc_retain
430; CHECK: arraydestroy.body:
431; CHECK:   @objc_release
432; CHECK-NOT: @objc_release
433; CHECK: arraydestroy.done:
434; CHECK-NOT: @objc_release
435; CHECK: arraydestroy.body1:
436; CHECK:   @objc_release
437; CHECK-NOT: @objc_release
438; CHECK: arraydestroy.done1:
439; CHECK: @objc_release
440; CHECK: ret void
441; CHECK: }
442define void @test3b() {
443entry:
444  %keys = alloca [2 x i8*], align 16
445  %objs = alloca [2 x i8*], align 16
446
447  %call1 = call i8* @returner()
448  %tmp0 = tail call i8* @objc_retainAutoreleasedReturnValue(i8* %call1)
449  %tmp1 = tail call i8* @objc_retain(i8* %call1)
450
451  %objs.begin = getelementptr inbounds [2 x i8*]* %objs, i64 0, i64 0
452  tail call i8* @objc_retain(i8* %call1)
453  store i8* %call1, i8** %objs.begin, align 8
454  %objs.elt = getelementptr inbounds [2 x i8*]* %objs, i64 0, i64 1
455  tail call i8* @objc_retain(i8* %call1)
456  store i8* %call1, i8** %objs.elt
457
458  %call2 = call i8* @returner1()
459  %call3 = call i8* @returner2()
460  %keys.begin = getelementptr inbounds [2 x i8*]* %keys, i64 0, i64 0
461  tail call i8* @objc_retain(i8* %call2)
462  store i8* %call2, i8** %keys.begin, align 8
463  %keys.elt = getelementptr inbounds [2 x i8*]* %keys, i64 0, i64 1
464  tail call i8* @objc_retain(i8* %call3)
465  store i8* %call3, i8** %keys.elt
466
467  %gep = getelementptr inbounds [2 x i8*]* %objs, i64 0, i64 2
468  br label %arraydestroy.body
469
470arraydestroy.body:
471  %arraydestroy.elementPast = phi i8** [ %gep, %entry ], [ %arraydestroy.element, %arraydestroy.body ]
472  %arraydestroy.element = getelementptr inbounds i8** %arraydestroy.elementPast, i64 -1
473  %destroy_tmp = load i8** %arraydestroy.element, align 8
474  call void @objc_release(i8* %destroy_tmp), !clang.imprecise_release !0
475  %objs_ptr = getelementptr inbounds [2 x i8*]* %objs, i64 0, i64 0
476  %arraydestroy.cmp = icmp eq i8** %arraydestroy.element, %objs_ptr
477  br i1 %arraydestroy.cmp, label %arraydestroy.done, label %arraydestroy.body
478
479arraydestroy.done:
480  %gep1 = getelementptr inbounds [2 x i8*]* %keys, i64 0, i64 2
481  br label %arraydestroy.body1
482
483arraydestroy.body1:
484  %arraydestroy.elementPast1 = phi i8** [ %gep1, %arraydestroy.done ], [ %arraydestroy.element1, %arraydestroy.body1 ]
485  %arraydestroy.element1 = getelementptr inbounds i8** %arraydestroy.elementPast1, i64 -1
486  %destroy_tmp1 = load i8** %arraydestroy.element1, align 8
487  call void @objc_release(i8* %destroy_tmp1), !clang.imprecise_release !0
488  %keys_ptr = getelementptr inbounds [2 x i8*]* %keys, i64 0, i64 0
489  %arraydestroy.cmp1 = icmp eq i8** %arraydestroy.element1, %keys_ptr
490  br i1 %arraydestroy.cmp1, label %arraydestroy.done1, label %arraydestroy.body1
491
492arraydestroy.done1:
493  call void @objc_release(i8* %call1), !clang.imprecise_release !0
494  call void @objc_release(i8* %call1), !clang.imprecise_release !0
495  ret void
496}
497
498!0 = metadata !{}
499
500declare i32 @__gxx_personality_v0(...)
501