• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1; RUN: opt -objc-arc-contract -S < %s | FileCheck %s
2; RUN: opt -passes=objc-arc-contract -S < %s | FileCheck %s
3
4target datalayout = "e-p:64:64:64"
5
6declare i8* @llvm.objc.retain(i8*)
7declare void @llvm.objc.release(i8*)
8declare i8* @llvm.objc.autorelease(i8*)
9declare i8* @llvm.objc.autoreleaseReturnValue(i8*)
10declare i8* @llvm.objc.retainAutoreleasedReturnValue(i8*)
11
12declare void @use_pointer(i8*)
13declare i8* @returner()
14declare void @callee()
15
16; CHECK-LABEL: define void @test0(
17; CHECK: call void @use_pointer(i8* %0)
18; CHECK: }
19define void @test0(i8* %x) nounwind {
20entry:
21  %0 = call i8* @llvm.objc.retain(i8* %x) nounwind
22  call void @use_pointer(i8* %x)
23  ret void
24}
25
26; CHECK-LABEL: define void @test1(
27; CHECK: call void @use_pointer(i8* %0)
28; CHECK: }
29define void @test1(i8* %x) nounwind {
30entry:
31  %0 = call i8* @llvm.objc.autorelease(i8* %x) nounwind
32  call void @use_pointer(i8* %x)
33  ret void
34}
35
36; Merge objc_retain and objc_autorelease into objc_retainAutorelease.
37
38; CHECK-LABEL: define void @test2(
39; CHECK: tail call i8* @llvm.objc.retainAutorelease(i8* %x) [[NUW:#[0-9]+]]
40; CHECK: }
41define void @test2(i8* %x) nounwind {
42entry:
43  %0 = tail call i8* @llvm.objc.retain(i8* %x) nounwind
44  call i8* @llvm.objc.autorelease(i8* %0) nounwind
45  call void @use_pointer(i8* %x)
46  ret void
47}
48
49; Same as test2 but the value is returned. Do an RV optimization.
50
51; CHECK-LABEL: define i8* @test2b(
52; CHECK: tail call i8* @llvm.objc.retainAutoreleaseReturnValue(i8* %x) [[NUW]]
53; CHECK: }
54define i8* @test2b(i8* %x) nounwind {
55entry:
56  %0 = tail call i8* @llvm.objc.retain(i8* %x) nounwind
57  tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %0) nounwind
58  ret i8* %x
59}
60
61; Merge a retain,autorelease pair around a call.
62
63; CHECK-LABEL: define void @test3(
64; CHECK: tail call i8* @llvm.objc.retainAutorelease(i8* %x) [[NUW]]
65; CHECK: @use_pointer(i8* %0)
66; CHECK: }
67define void @test3(i8* %x, i64 %n) {
68entry:
69  tail call i8* @llvm.objc.retain(i8* %x) nounwind
70  call void @use_pointer(i8* %x)
71  call i8* @llvm.objc.autorelease(i8* %x) nounwind
72  ret void
73}
74
75; Trivial retain,autorelease pair with intervening call, but it's post-dominated
76; by another release. The retain and autorelease can be merged.
77
78; CHECK-LABEL: define void @test4(
79; CHECK-NEXT: entry:
80; CHECK-NEXT: @llvm.objc.retainAutorelease(i8* %x) [[NUW]]
81; CHECK-NEXT: @use_pointer
82; CHECK-NEXT: @llvm.objc.release
83; CHECK-NEXT: ret void
84; CHECK-NEXT: }
85define void @test4(i8* %x, i64 %n) {
86entry:
87  tail call i8* @llvm.objc.retain(i8* %x) nounwind
88  call void @use_pointer(i8* %x)
89  call i8* @llvm.objc.autorelease(i8* %x) nounwind
90  tail call void @llvm.objc.release(i8* %x) nounwind
91  ret void
92}
93
94; Don't merge retain and autorelease if they're not control-equivalent.
95
96; CHECK-LABEL: define void @test5(
97; CHECK: tail call i8* @llvm.objc.retain(i8* %p) [[NUW]]
98; CHECK: true:
99; CHECK: call i8* @llvm.objc.autorelease(i8* %0) [[NUW]]
100; CHECK: }
101define void @test5(i8* %p, i1 %a) {
102entry:
103  tail call i8* @llvm.objc.retain(i8* %p) nounwind
104  br i1 %a, label %true, label %false
105
106true:
107  call i8* @llvm.objc.autorelease(i8* %p) nounwind
108  call void @use_pointer(i8* %p)
109  ret void
110
111false:
112  ret void
113}
114
115; Don't eliminate objc_retainAutoreleasedReturnValue by merging it into
116; an objc_autorelease.
117; TODO? Merge objc_retainAutoreleasedReturnValue and objc_autorelease into
118; objc_retainAutoreleasedReturnValueAutorelease and merge
119; objc_retainAutoreleasedReturnValue and objc_autoreleaseReturnValue
120; into objc_retainAutoreleasedReturnValueAutoreleaseReturnValue?
121; Those entrypoints don't exist yet though.
122
123; CHECK-LABEL: define i8* @test6(
124; CHECK: call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %p) [[NUW]]
125; CHECK: %t = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %1) [[NUW]]
126; CHECK: }
127define i8* @test6() {
128  %p = call i8* @returner()
129  tail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %p) nounwind
130  %t = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %p) nounwind
131  call void @use_pointer(i8* %t)
132  ret i8* %t
133}
134
135; Don't spoil the RV optimization.
136
137; CHECK: define i8* @test7(i8* %p)
138; CHECK: tail call i8* @llvm.objc.retain(i8* %p)
139; CHECK: call void @use_pointer(i8* %1)
140; CHECK: tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %1)
141; CHECK: ret i8* %2
142; CHECK-NEXT: }
143define i8* @test7(i8* %p) {
144  %1 = tail call i8* @llvm.objc.retain(i8* %p)
145  call void @use_pointer(i8* %p)
146  %2 = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %p)
147  ret i8* %p
148}
149
150; Do the return value substitution for PHI nodes too.
151
152; CHECK-LABEL: define i8* @test8(
153; CHECK: %retval = phi i8* [ %p, %if.then ], [ null, %entry ]
154; CHECK: }
155define i8* @test8(i1 %x, i8* %c) {
156entry:
157  br i1 %x, label %return, label %if.then
158
159if.then:                                          ; preds = %entry
160  %p = call i8* @llvm.objc.retain(i8* %c) nounwind
161  br label %return
162
163return:                                           ; preds = %if.then, %entry
164  %retval = phi i8* [ %c, %if.then ], [ null, %entry ]
165  ret i8* %retval
166}
167
168; Kill calls to @llvm.objc.clang.arc.use(...)
169; CHECK-LABEL: define void @test9(
170; CHECK-NOT: clang.arc.use
171; CHECK: }
172define void @test9(i8* %a, i8* %b) {
173  call void (...) @llvm.objc.clang.arc.use(i8* %a, i8* %b) nounwind
174  ret void
175}
176
177
178; Turn objc_retain into objc_retainAutoreleasedReturnValue if its operand
179; is a return value.
180
181; CHECK: define void @test10()
182; CHECK: tail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %p)
183define void @test10() {
184  %p = call i8* @returner()
185  tail call i8* @llvm.objc.retain(i8* %p) nounwind
186  ret void
187}
188
189; Convert objc_retain to objc_retainAutoreleasedReturnValue if its
190; argument is a return value.
191
192; CHECK-LABEL: define void @test11(
193; CHECK-NEXT: %y = call i8* @returner()
194; CHECK-NEXT: tail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %y) [[NUW]]
195; CHECK-NEXT: ret void
196define void @test11() {
197  %y = call i8* @returner()
198  tail call i8* @llvm.objc.retain(i8* %y) nounwind
199  ret void
200}
201
202; Don't convert objc_retain to objc_retainAutoreleasedReturnValue if its
203; argument is not a return value.
204
205; CHECK-LABEL: define void @test12(
206; CHECK-NEXT: tail call i8* @llvm.objc.retain(i8* %y) [[NUW]]
207; CHECK-NEXT: ret void
208; CHECK-NEXT: }
209define void @test12(i8* %y) {
210  tail call i8* @llvm.objc.retain(i8* %y) nounwind
211  ret void
212}
213
214; Don't Convert objc_retain to objc_retainAutoreleasedReturnValue if it
215; isn't next to the call providing its return value.
216
217; CHECK-LABEL: define void @test13(
218; CHECK-NEXT: %y = call i8* @returner()
219; CHECK-NEXT: call void @callee()
220; CHECK-NEXT: tail call i8* @llvm.objc.retain(i8* %y) [[NUW]]
221; CHECK-NEXT: ret void
222; CHECK-NEXT: }
223define void @test13() {
224  %y = call i8* @returner()
225  call void @callee()
226  tail call i8* @llvm.objc.retain(i8* %y) nounwind
227  ret void
228}
229
230
231declare void @llvm.objc.clang.arc.use(...) nounwind
232
233; CHECK: attributes [[NUW]] = { nounwind }
234