1; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefixes=FNATTR,EITHER
2; RUN: opt -passes=function-attrs -S < %s | FileCheck %s --check-prefixes=FNATTR,EITHER
3; RUN: opt -attributor -attributor-manifest-internal -attributor-disable=false -S -attributor-annotate-decl-cs < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,EITHER
4; RUN: opt -passes=attributor -attributor-manifest-internal -attributor-disable=false -S -attributor-annotate-decl-cs < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,EITHER
5
6@g = global i32* null		; <i32**> [#uses=1]
7
8; FNATTR: define i32* @c1(i32* readnone returned %q)
9; ATTRIBUTOR: define i32* @c1(i32* nofree readnone returned "no-capture-maybe-returned" %q)
10define i32* @c1(i32* %q) {
11	ret i32* %q
12}
13
14; FNATTR: define void @c2(i32* %q)
15; ATTRIBUTOR: define void @c2(i32* nofree writeonly %q)
16; It would also be acceptable to mark %q as readnone. Update @c3 too.
17define void @c2(i32* %q) {
18	store i32* %q, i32** @g
19	ret void
20}
21
22; FNATTR: define void @c3(i32* %q)
23; ATTRIBUTOR: define void @c3(i32* nofree writeonly %q)
24define void @c3(i32* %q) {
25	call void @c2(i32* %q)
26	ret void
27}
28
29; FNATTR: define i1 @c4(i32* %q, i32 %bitno)
30; ATTRIBUTOR: define i1 @c4(i32* nofree readnone %q, i32 %bitno)
31define i1 @c4(i32* %q, i32 %bitno) {
32	%tmp = ptrtoint i32* %q to i32
33	%tmp2 = lshr i32 %tmp, %bitno
34	%bit = trunc i32 %tmp2 to i1
35	br i1 %bit, label %l1, label %l0
36l0:
37	ret i1 0 ; escaping value not caught by def-use chaining.
38l1:
39	ret i1 1 ; escaping value not caught by def-use chaining.
40}
41
42; c4b is c4 but without the escaping part
43; FNATTR: define i1 @c4b(i32* %q, i32 %bitno)
44; ATTRIBUTOR: define i1 @c4b(i32* nocapture nofree readnone %q, i32 %bitno)
45define i1 @c4b(i32* %q, i32 %bitno) {
46	%tmp = ptrtoint i32* %q to i32
47	%tmp2 = lshr i32 %tmp, %bitno
48	%bit = trunc i32 %tmp2 to i1
49	br i1 %bit, label %l1, label %l0
50l0:
51	ret i1 0 ; not escaping!
52l1:
53	ret i1 0 ; not escaping!
54}
55
56@lookup_table = global [2 x i1] [ i1 0, i1 1 ]
57
58; FNATTR: define i1 @c5(i32* %q, i32 %bitno)
59; ATTRIBUTOR: define i1 @c5(i32* nofree readonly %q, i32 %bitno)
60define i1 @c5(i32* %q, i32 %bitno) {
61	%tmp = ptrtoint i32* %q to i32
62	%tmp2 = lshr i32 %tmp, %bitno
63	%bit = and i32 %tmp2, 1
64        ; subtle escape mechanism follows
65	%lookup = getelementptr [2 x i1], [2 x i1]* @lookup_table, i32 0, i32 %bit
66	%val = load i1, i1* %lookup
67	ret i1 %val
68}
69
70declare void @throw_if_bit_set(i8*, i8) readonly
71
72; EITHER: define i1 @c6(i8* readonly %q, i8 %bit)
73define i1 @c6(i8* %q, i8 %bit) personality i32 (...)* @__gxx_personality_v0 {
74	invoke void @throw_if_bit_set(i8* %q, i8 %bit)
75		to label %ret0 unwind label %ret1
76ret0:
77	ret i1 0
78ret1:
79        %exn = landingpad {i8*, i32}
80                 cleanup
81	ret i1 1
82}
83
84declare i32 @__gxx_personality_v0(...)
85
86define i1* @lookup_bit(i32* %q, i32 %bitno) readnone nounwind {
87	%tmp = ptrtoint i32* %q to i32
88	%tmp2 = lshr i32 %tmp, %bitno
89	%bit = and i32 %tmp2, 1
90	%lookup = getelementptr [2 x i1], [2 x i1]* @lookup_table, i32 0, i32 %bit
91	ret i1* %lookup
92}
93
94; FNATTR: define i1 @c7(i32* readonly %q, i32 %bitno)
95; ATTRIBUTOR: define i1 @c7(i32* nofree readonly %q, i32 %bitno)
96define i1 @c7(i32* %q, i32 %bitno) {
97	%ptr = call i1* @lookup_bit(i32* %q, i32 %bitno)
98	%val = load i1, i1* %ptr
99	ret i1 %val
100}
101
102
103; FNATTR: define i32 @nc1(i32* %q, i32* nocapture %p, i1 %b)
104; ATTRIBUTOR: define i32 @nc1(i32* nofree %q, i32* nocapture nofree %p, i1 %b)
105define i32 @nc1(i32* %q, i32* %p, i1 %b) {
106e:
107	br label %l
108l:
109	%x = phi i32* [ %p, %e ]
110	%y = phi i32* [ %q, %e ]
111	%tmp = bitcast i32* %x to i32*		; <i32*> [#uses=2]
112	%tmp2 = select i1 %b, i32* %tmp, i32* %y
113	%val = load i32, i32* %tmp2		; <i32> [#uses=1]
114	store i32 0, i32* %tmp
115	store i32* %y, i32** @g
116	ret i32 %val
117}
118
119; FNATTR: define i32 @nc1_addrspace(i32* %q, i32 addrspace(1)* nocapture %p, i1 %b)
120; ATTRIBUTOR: define i32 @nc1_addrspace(i32* nofree %q, i32 addrspace(1)* nocapture nofree %p, i1 %b)
121define i32 @nc1_addrspace(i32* %q, i32 addrspace(1)* %p, i1 %b) {
122e:
123	br label %l
124l:
125	%x = phi i32 addrspace(1)* [ %p, %e ]
126	%y = phi i32* [ %q, %e ]
127	%tmp = addrspacecast i32 addrspace(1)* %x to i32*		; <i32*> [#uses=2]
128	%tmp2 = select i1 %b, i32* %tmp, i32* %y
129	%val = load i32, i32* %tmp2		; <i32> [#uses=1]
130	store i32 0, i32* %tmp
131	store i32* %y, i32** @g
132	ret i32 %val
133}
134
135; FNATTR: define void @nc2(i32* nocapture %p, i32* %q)
136; ATTRIBUTOR: define void @nc2(i32* nocapture nofree %p, i32* nofree %q)
137define void @nc2(i32* %p, i32* %q) {
138	%1 = call i32 @nc1(i32* %q, i32* %p, i1 0)		; <i32> [#uses=0]
139	ret void
140}
141
142
143; FNATTR: define void @nc3(void ()* nocapture %p)
144; ATTRIBUTOR: define void @nc3(void ()* nocapture nofree nonnull %p)
145define void @nc3(void ()* %p) {
146	call void %p()
147	ret void
148}
149
150declare void @external(i8*) readonly nounwind
151; EITHER: define void @nc4(i8* nocapture readonly %p)
152define void @nc4(i8* %p) {
153	call void @external(i8* %p)
154	ret void
155}
156
157; FNATTR: define void @nc5(void (i8*)* nocapture %f, i8* nocapture %p)
158; ATTRIBUTOR: define void @nc5(void (i8*)* nocapture nofree nonnull %f, i8* nocapture %p)
159define void @nc5(void (i8*)* %f, i8* %p) {
160	call void %f(i8* %p) readonly nounwind
161	call void %f(i8* nocapture %p)
162	ret void
163}
164
165; FNATTR:     define void @test1_1(i8* nocapture readnone %x1_1, i8* %y1_1, i1 %c)
166; ATTRIBUTOR: define void @test1_1(i8* nocapture nofree readnone %x1_1, i8* nocapture nofree readnone %y1_1, i1 %c)
167; It would be acceptable to add readnone to %y1_1 and %y1_2.
168define void @test1_1(i8* %x1_1, i8* %y1_1, i1 %c) {
169  call i8* @test1_2(i8* %x1_1, i8* %y1_1, i1 %c)
170  store i32* null, i32** @g
171  ret void
172}
173
174; FNATTR: define i8* @test1_2(i8* nocapture readnone %x1_2, i8* returned %y1_2, i1 %c)
175; ATTRIBUTOR: define i8* @test1_2(i8* nocapture nofree readnone %x1_2, i8* nofree readnone returned "no-capture-maybe-returned" %y1_2, i1 %c)
176define i8* @test1_2(i8* %x1_2, i8* %y1_2, i1 %c) {
177  br i1 %c, label %t, label %f
178t:
179  call void @test1_1(i8* %x1_2, i8* %y1_2, i1 %c)
180  store i32* null, i32** @g
181  br label %f
182f:
183  ret i8* %y1_2
184}
185
186; FNATTR: define void @test2(i8* nocapture readnone %x2)
187; ATTRIBUTOR: define void @test2(i8* nocapture nofree readnone %x2)
188define void @test2(i8* %x2) {
189  call void @test2(i8* %x2)
190  store i32* null, i32** @g
191  ret void
192}
193
194; FNATTR: define void @test3(i8* nocapture readnone %x3, i8* nocapture readnone %y3, i8* nocapture readnone %z3)
195; ATTRIBUTOR: define void @test3(i8* nocapture nofree readnone %x3, i8* nocapture nofree readnone %y3, i8* nocapture nofree readnone %z3)
196define void @test3(i8* %x3, i8* %y3, i8* %z3) {
197  call void @test3(i8* %z3, i8* %y3, i8* %x3)
198  store i32* null, i32** @g
199  ret void
200}
201
202; FNATTR: define void @test4_1(i8* %x4_1, i1 %c)
203; ATTRIBUTOR: define void @test4_1(i8* nocapture nofree readnone %x4_1, i1 %c)
204define void @test4_1(i8* %x4_1, i1 %c) {
205  call i8* @test4_2(i8* %x4_1, i8* %x4_1, i8* %x4_1, i1 %c)
206  store i32* null, i32** @g
207  ret void
208}
209
210; FNATTR: define i8* @test4_2(i8* nocapture readnone %x4_2, i8* readnone returned %y4_2, i8* nocapture readnone %z4_2, i1 %c)
211; ATTRIBUTOR: define i8* @test4_2(i8* nocapture nofree readnone %x4_2, i8* nofree readnone returned "no-capture-maybe-returned" %y4_2, i8* nocapture nofree readnone %z4_2, i1 %c)
212define i8* @test4_2(i8* %x4_2, i8* %y4_2, i8* %z4_2, i1 %c) {
213  br i1 %c, label %t, label %f
214t:
215  call void @test4_1(i8* null, i1 %c)
216  store i32* null, i32** @g
217  br label %f
218f:
219  ret i8* %y4_2
220}
221
222declare i8* @test5_1(i8* %x5_1)
223
224; EITHER: define void @test5_2(i8* %x5_2)
225define void @test5_2(i8* %x5_2) {
226  call i8* @test5_1(i8* %x5_2)
227  store i32* null, i32** @g
228  ret void
229}
230
231declare void @test6_1(i8* %x6_1, i8* nocapture %y6_1, ...)
232
233; EITHER: define void @test6_2(i8* %x6_2, i8* nocapture %y6_2, i8* %z6_2)
234define void @test6_2(i8* %x6_2, i8* %y6_2, i8* %z6_2) {
235  call void (i8*, i8*, ...) @test6_1(i8* %x6_2, i8* %y6_2, i8* %z6_2)
236  store i32* null, i32** @g
237  ret void
238}
239
240; FNATTR: define void @test_cmpxchg(i32* nocapture %p)
241; ATTRIBUTOR: define void @test_cmpxchg(i32* nocapture nofree nonnull dereferenceable(4) %p)
242define void @test_cmpxchg(i32* %p) {
243  cmpxchg i32* %p, i32 0, i32 1 acquire monotonic
244  ret void
245}
246
247; FNATTR: define void @test_cmpxchg_ptr(i32** nocapture %p, i32* %q)
248; ATTRIBUTOR: define void @test_cmpxchg_ptr(i32** nocapture nofree nonnull dereferenceable(8) %p, i32* nofree %q)
249define void @test_cmpxchg_ptr(i32** %p, i32* %q) {
250  cmpxchg i32** %p, i32* null, i32* %q acquire monotonic
251  ret void
252}
253
254; FNATTR: define void @test_atomicrmw(i32* nocapture %p)
255; ATTRIBUTOR: define void @test_atomicrmw(i32* nocapture nofree nonnull dereferenceable(4) %p)
256define void @test_atomicrmw(i32* %p) {
257  atomicrmw add i32* %p, i32 1 seq_cst
258  ret void
259}
260
261; FNATTR: define void @test_volatile(i32* %x)
262; ATTRIBUTOR: define void @test_volatile(i32* nofree %x)
263define void @test_volatile(i32* %x) {
264entry:
265  %gep = getelementptr i32, i32* %x, i64 1
266  store volatile i32 0, i32* %gep, align 4
267  ret void
268}
269
270; EITHER: nocaptureLaunder(i8* nocapture %p)
271define void @nocaptureLaunder(i8* %p) {
272entry:
273  %b = call i8* @llvm.launder.invariant.group.p0i8(i8* %p)
274  store i8 42, i8* %b
275  ret void
276}
277
278@g2 = global i8* null
279; EITHER: define void @captureLaunder(i8* %p)
280define void @captureLaunder(i8* %p) {
281  %b = call i8* @llvm.launder.invariant.group.p0i8(i8* %p)
282  store i8* %b, i8** @g2
283  ret void
284}
285
286; FNATTR: @nocaptureStrip(i8* nocapture %p)
287; ATTRIBUTOR: @nocaptureStrip(i8* nocapture writeonly %p)
288define void @nocaptureStrip(i8* %p) {
289entry:
290  %b = call i8* @llvm.strip.invariant.group.p0i8(i8* %p)
291  store i8 42, i8* %b
292  ret void
293}
294
295@g3 = global i8* null
296; FNATTR: define void @captureStrip(i8* %p)
297; ATTRIBUTOR: define void @captureStrip(i8* writeonly %p)
298define void @captureStrip(i8* %p) {
299  %b = call i8* @llvm.strip.invariant.group.p0i8(i8* %p)
300  store i8* %b, i8** @g3
301  ret void
302}
303
304; FNATTR: define i1 @captureICmp(i32* readnone %x)
305; ATTRIBUTOR: define i1 @captureICmp(i32* nofree readnone %x)
306define i1 @captureICmp(i32* %x) {
307  %1 = icmp eq i32* %x, null
308  ret i1 %1
309}
310
311; FNATTR: define i1 @captureICmpRev(i32* readnone %x)
312; ATTRIBUTOR: define i1 @captureICmpRev(i32* nofree readnone %x)
313define i1 @captureICmpRev(i32* %x) {
314  %1 = icmp eq i32* null, %x
315  ret i1 %1
316}
317
318; FNATTR: define i1 @nocaptureInboundsGEPICmp(i32* nocapture readnone %x)
319; ATTRIBUTOR: define i1 @nocaptureInboundsGEPICmp(i32* nocapture nofree nonnull readnone %x)
320define i1 @nocaptureInboundsGEPICmp(i32* %x) {
321  %1 = getelementptr inbounds i32, i32* %x, i32 5
322  %2 = bitcast i32* %1 to i8*
323  %3 = icmp eq i8* %2, null
324  ret i1 %3
325}
326
327; FNATTR: define i1 @nocaptureInboundsGEPICmpRev(i32* nocapture readnone %x)
328; ATTRIBUTOR: define i1 @nocaptureInboundsGEPICmpRev(i32* nocapture nofree nonnull readnone %x)
329define i1 @nocaptureInboundsGEPICmpRev(i32* %x) {
330  %1 = getelementptr inbounds i32, i32* %x, i32 5
331  %2 = bitcast i32* %1 to i8*
332  %3 = icmp eq i8* null, %2
333  ret i1 %3
334}
335
336; FNATTR: define i1 @nocaptureDereferenceableOrNullICmp(i32* nocapture readnone dereferenceable_or_null(4) %x)
337; ATTRIBUTOR: define i1 @nocaptureDereferenceableOrNullICmp(i32* nocapture nofree readnone dereferenceable_or_null(4) %x)
338define i1 @nocaptureDereferenceableOrNullICmp(i32* dereferenceable_or_null(4) %x) {
339  %1 = bitcast i32* %x to i8*
340  %2 = icmp eq i8* %1, null
341  ret i1 %2
342}
343
344; FNATTR: define i1 @captureDereferenceableOrNullICmp(i32* readnone dereferenceable_or_null(4) %x)
345; ATTRIBUTOR: define i1 @captureDereferenceableOrNullICmp(i32* nofree readnone dereferenceable_or_null(4) %x)
346define i1 @captureDereferenceableOrNullICmp(i32* dereferenceable_or_null(4) %x) "null-pointer-is-valid"="true" {
347  %1 = bitcast i32* %x to i8*
348  %2 = icmp eq i8* %1, null
349  ret i1 %2
350}
351
352declare void @unknown(i8*)
353define void @test_callsite() {
354entry:
355; We know that 'null' in AS 0 does not alias anything and cannot be captured. Though the latter is not qurried -> derived atm.
356; ATTRIBUTOR: call void @unknown(i8* noalias null)
357  call void @unknown(i8* null)
358  ret void
359}
360
361declare i8* @unknownpi8pi8(i8*,i8* returned)
362define i8* @test_returned1(i8* %A, i8* returned %B) nounwind readonly {
363; ATTRIBUTOR: define i8* @test_returned1(i8* nocapture readonly %A, i8* readonly returned %B)
364entry:
365  %p = call i8* @unknownpi8pi8(i8* %A, i8* %B)
366  ret i8* %p
367}
368
369define i8* @test_returned2(i8* %A, i8* %B) {
370; ATTRIBUTOR: define i8* @test_returned2(i8* nocapture readonly %A, i8* readonly returned %B)
371entry:
372  %p = call i8* @unknownpi8pi8(i8* %A, i8* %B) nounwind readonly
373  ret i8* %p
374}
375
376declare i8* @llvm.launder.invariant.group.p0i8(i8*)
377declare i8* @llvm.strip.invariant.group.p0i8(i8*)
378