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