xref: /llvm-project/clang/test/CodeGenObjC/arc-precise-lifetime.m (revision 0f1c1be1968076d6f96f8a7bcc4a15cf195ecd97)
1// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-passes -o - %s | FileCheck %s
2
3#define PRECISE_LIFETIME __attribute__((objc_precise_lifetime))
4
5id test0_helper(void) __attribute__((ns_returns_retained));
6void test0(void) {
7  PRECISE_LIFETIME id x = test0_helper();
8  x = 0;
9  // CHECK:      [[X:%.*]] = alloca ptr
10  // CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[X]])
11  // CHECK-NEXT: [[CALL:%.*]] = call ptr @test0_helper()
12  // CHECK-NEXT: store ptr [[CALL]], ptr [[X]]
13
14  // CHECK-NEXT: [[T1:%.*]] = load ptr, ptr [[X]]
15  // CHECK-NEXT: store ptr null, ptr [[X]]
16  // CHECK-NEXT: call void @llvm.objc.release(ptr [[T1]]) [[NUW:#[0-9]+]]
17  // CHECK-NOT:  clang.imprecise_release
18
19  // CHECK-NEXT: [[T1:%.*]] = load ptr, ptr [[X]]
20  // CHECK-NEXT: call void @llvm.objc.release(ptr [[T1]]) [[NUW:#[0-9]+]]
21  // CHECK-NOT:  clang.imprecise_release
22
23  // CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[X]])
24  // CHECK-NEXT: ret void
25}
26
27// precise lifetime should suppress extension
28// should work for calls via property syntax, too
29@interface Test1
30- (char*) interior __attribute__((objc_returns_inner_pointer));
31// Should we allow this on properties? Yes!
32@property (nonatomic, readonly) char * PropertyReturnsInnerPointer __attribute__((objc_returns_inner_pointer));
33@end
34extern Test1 *test1_helper(void);
35
36// CHECK-LABEL: define{{.*}} void @test1a_message()
37void test1a_message(void) {
38  // CHECK:      [[PTR:%.*]] = alloca ptr, align 8
39  // CHECK:      [[C:%.*]] = alloca ptr, align 8
40  // CHECK:      call void @llvm.lifetime.start.p0(i64 8, ptr [[PTR]])
41  // CHECK:      [[T0:%.*]] = call ptr @test1_helper() [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
42  // CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T0]])
43  // CHECK-NEXT: store ptr [[T0]]
44  // CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[C]])
45  // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr
46  // CHECK-NEXT: [[T2:%.*]] = call ptr @llvm.objc.retainAutorelease(ptr [[T0]])
47  // CHECK-NEXT: [[T4:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_
48  // CHECK-NEXT: [[T6:%.*]] = call ptr
49  // CHECK-NEXT: store ptr [[T6]], ptr
50  // CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[C]])
51  // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr
52  // CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]]) [[NUW]], !clang.imprecise_release
53  // CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[PTR]])
54  // CHECK-NEXT: ret void
55  Test1 *ptr = test1_helper();
56  char *c = [(ptr) interior];
57}
58
59
60// CHECK-LABEL: define{{.*}} void @test1a_property()
61void test1a_property(void) {
62  // CHECK:      [[PTR:%.*]] = alloca ptr, align 8
63  // CHECK:      [[C:%.*]] = alloca ptr, align 8
64  // CHECK:      call void @llvm.lifetime.start.p0(i64 8, ptr [[PTR]])
65  // CHECK:      [[T0:%.*]] = call ptr @test1_helper() [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
66  // CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T0]])
67  // CHECK-NEXT: store ptr [[T0]]
68  // CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[C]])
69  // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr
70  // CHECK-NEXT: [[T2:%.*]] = call ptr @llvm.objc.retainAutorelease(ptr [[T0]])
71  // CHECK-NEXT: [[T4:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_
72  // CHECK-NEXT: [[T6:%.*]] = call ptr
73  // CHECK-NEXT: store ptr [[T6]], ptr
74  // CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[C]])
75  // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr
76  // CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]]) [[NUW]], !clang.imprecise_release
77  // CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[PTR]])
78  // CHECK-NEXT: ret void
79  Test1 *ptr = test1_helper();
80  char *c = ptr.interior;
81}
82
83
84// CHECK-LABEL: define{{.*}} void @test1b_message()
85void test1b_message(void) {
86  // CHECK:      [[PTR:%.*]] = alloca ptr, align 8
87  // CHECK:      [[C:%.*]] = alloca ptr, align 8
88  // CHECK:      call void @llvm.lifetime.start.p0(i64 8, ptr [[PTR]])
89  // CHECK:      [[T0:%.*]] = call ptr @test1_helper() [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
90  // CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T0]])
91  // CHECK-NEXT: store ptr [[T0]]
92  // CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[C]])
93  // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr
94  // CHECK-NEXT: [[T1:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_
95  // CHECK-NEXT: [[T3:%.*]] = call ptr
96  // CHECK-NEXT: store ptr [[T3]], ptr
97  // CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[C]])
98  // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr
99  // CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]]) [[NUW]]
100  // CHECK-NOT:  clang.imprecise_release
101  // CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[PTR]])
102  // CHECK-NEXT: ret void
103  PRECISE_LIFETIME Test1 *ptr = test1_helper();
104  char *c = [ptr interior];
105}
106
107// CHECK-LABEL: define{{.*}} void @test1b_property()
108void test1b_property(void) {
109  // CHECK:      [[PTR:%.*]] = alloca ptr, align 8
110  // CHECK:      [[C:%.*]] = alloca ptr, align 8
111  // CHECK:      call void @llvm.lifetime.start.p0(i64 8, ptr [[PTR]])
112  // CHECK:      [[T0:%.*]] = call ptr @test1_helper() [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
113  // CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T0]])
114  // CHECK-NEXT: store ptr [[T0]]
115  // CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[C]])
116  // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr
117  // CHECK-NEXT: [[T1:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_
118  // CHECK-NEXT: [[T3:%.*]] = call ptr
119  // CHECK-NEXT: store ptr [[T3]], ptr
120  // CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[C]])
121  // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr
122  // CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]]) [[NUW]]
123  // CHECK-NOT:  clang.imprecise_release
124  // CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[PTR]])
125  // CHECK-NEXT: ret void
126  PRECISE_LIFETIME Test1 *ptr = test1_helper();
127  char *c = ptr.interior;
128}
129
130// CHECK-LABEL: define{{.*}} void @test1c_message()
131void test1c_message(void) {
132  // CHECK:      [[PTR:%.*]] = alloca ptr, align 8
133  // CHECK:      [[PC:%.*]] = alloca ptr, align 8
134  // CHECK:      call void @llvm.lifetime.start.p0(i64 8, ptr [[PTR]])
135  // CHECK:      [[T0:%.*]] = call ptr @test1_helper() [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
136  // CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T0]])
137  // CHECK-NEXT: store ptr [[T0]]
138  // CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[PC]])
139  // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr
140  // CHECK-NEXT: [[T2:%.*]] = call ptr @llvm.objc.retainAutorelease(ptr [[T0]])
141  // CHECK-NEXT: [[T4:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_
142  // CHECK-NEXT: [[T6:%.*]] = call ptr
143  // CHECK-NEXT: store ptr [[T6]], ptr
144  // CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[PC]])
145  // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr
146  // CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]]) [[NUW]], !clang.imprecise_release
147  // CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[PTR]])
148  // CHECK-NEXT: ret void
149  Test1 *ptr = test1_helper();
150  char *pc = [ptr PropertyReturnsInnerPointer];
151}
152
153// CHECK-LABEL: define{{.*}} void @test1c_property()
154void test1c_property(void) {
155  // CHECK:      [[PTR:%.*]] = alloca ptr, align 8
156  // CHECK:      [[PC:%.*]] = alloca ptr, align 8
157  // CHECK:      call void @llvm.lifetime.start.p0(i64 8, ptr [[PTR]])
158  // CHECK:      [[T0:%.*]] = call ptr @test1_helper() [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
159  // CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T0]])
160  // CHECK-NEXT: store ptr [[T0]]
161  // CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[PC]])
162  // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr
163  // CHECK-NEXT: [[T2:%.*]] = call ptr @llvm.objc.retainAutorelease(ptr [[T0]])
164  // CHECK-NEXT: [[T4:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_
165  // CHECK-NEXT: [[T6:%.*]] = call ptr
166  // CHECK-NEXT: store ptr [[T6]], ptr
167  // CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[PC]])
168  // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr
169  // CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]]) [[NUW]], !clang.imprecise_release
170  // CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[PTR]])
171  // CHECK-NEXT: ret void
172  Test1 *ptr = test1_helper();
173  char *pc = ptr.PropertyReturnsInnerPointer;
174}
175
176// CHECK-LABEL: define{{.*}} void @test1d_message()
177void test1d_message(void) {
178  // CHECK:      [[PTR:%.*]] = alloca ptr, align 8
179  // CHECK:      [[PC:%.*]] = alloca ptr, align 8
180  // CHECK:      call void @llvm.lifetime.start.p0(i64 8, ptr [[PTR]])
181  // CHECK:      [[T0:%.*]] = call ptr @test1_helper() [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
182  // CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T0]])
183  // CHECK-NEXT: store ptr [[T0]]
184  // CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[PC]])
185  // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr
186  // CHECK-NEXT: [[SEL:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_
187  // CHECK-NEXT: [[CALL1:%.*]] = call ptr @objc_msgSend(ptr noundef [[T0]], ptr noundef [[SEL]])
188  // CHECK-NEXT: store ptr [[CALL1]], ptr
189  // CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[PC]])
190  // CHECK-NEXT: [[NINE:%.*]] = load ptr, ptr
191  // CHECK-NEXT: call void @llvm.objc.release(ptr [[NINE]])
192  // CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[PTR]])
193  // CHECK-NEXT: ret void
194  PRECISE_LIFETIME Test1 *ptr = test1_helper();
195  char *pc = [ptr PropertyReturnsInnerPointer];
196}
197
198// CHECK-LABEL: define{{.*}} void @test1d_property()
199void test1d_property(void) {
200  // CHECK:      [[PTR:%.*]] = alloca ptr, align 8
201  // CHECK:      [[PC:%.*]] = alloca ptr, align 8
202  // CHECK:      call void @llvm.lifetime.start.p0(i64 8, ptr [[PTR]])
203  // CHECK:      [[T0:%.*]] = call ptr @test1_helper() [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
204  // CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T0]])
205  // CHECK-NEXT: store ptr [[T0]]
206  // CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[PC]])
207  // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr
208  // CHECK-NEXT: [[SEL:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_
209  // CHECK-NEXT: [[CALL1:%.*]] = call ptr @objc_msgSend(ptr noundef [[T0]], ptr noundef [[SEL]])
210  // CHECK-NEXT: store ptr [[CALL1]], ptr
211  // CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[PC]])
212  // CHECK-NEXT: [[NINE:%.*]] = load ptr, ptr
213  // CHECK-NEXT: call void @llvm.objc.release(ptr [[NINE]])
214  // CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[PTR]])
215  // CHECK-NEXT: ret void
216  PRECISE_LIFETIME Test1 *ptr = test1_helper();
217  char *pc = ptr.PropertyReturnsInnerPointer;
218}
219
220@interface Test2 {
221@public
222  id ivar;
223}
224@end
225// CHECK-LABEL:      define{{.*}} void @test2(
226void test2(Test2 *x) {
227  x->ivar = 0;
228  // CHECK:      [[X:%.*]] = alloca ptr
229  // CHECK-NEXT: [[T1:%.*]] = call ptr @llvm.objc.retain(ptr {{%.*}}) [[NUW]]
230  // CHECK-NEXT: store ptr [[T1]], ptr [[X]],
231
232  // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[X]],
233  // CHECK-NEXT: [[OFFSET:%.*]] = load i64, ptr @"OBJC_IVAR_$_Test2.ivar"
234  // CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds i8, ptr [[T0]], i64 [[OFFSET]]
235  // CHECK-NEXT: [[T4:%.*]] = load ptr, ptr [[T2]],
236  // CHECK-NEXT: store ptr null, ptr [[T2]],
237  // CHECK-NEXT: call void @llvm.objc.release(ptr [[T4]]) [[NUW]]
238  // CHECK-NOT:  imprecise
239
240  // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[X]]
241  // CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]]) [[NUW]], !clang.imprecise_release
242
243  // CHECK-NEXT: ret void
244}
245
246// CHECK-LABEL:      define{{.*}} void @test3(ptr
247void test3(PRECISE_LIFETIME id x) {
248  // CHECK:      [[X:%.*]] = alloca ptr,
249  // CHECK-NEXT: [[T0:%.*]] = call ptr @llvm.objc.retain(ptr {{%.*}}) [[NUW]]
250  // CHECK-NEXT: store ptr [[T0]], ptr [[X]],
251
252  // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[X]]
253  // CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]]) [[NUW]]
254  // CHECK-NOT:  imprecise_release
255
256  // CHECK-NEXT: ret void
257}
258
259// CHECK: attributes [[NUW]] = { nounwind }
260