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