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 < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,EITHER
4; RUN: opt -passes=attributor -attributor-manifest-internal -attributor-disable=false -S < %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* 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* 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* 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* 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 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* 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; EITHER: define i1 @c7(i32* readonly %q, i32 %bitno)
95define i1 @c7(i32* %q, i32 %bitno) {
96	%ptr = call i1* @lookup_bit(i32* %q, i32 %bitno)
97	%val = load i1, i1* %ptr
98	ret i1 %val
99}
100
101
102; EITHER: define i32 @nc1(i32* %q, i32* nocapture %p, i1 %b)
103define i32 @nc1(i32* %q, i32* %p, i1 %b) {
104e:
105	br label %l
106l:
107	%x = phi i32* [ %p, %e ]
108	%y = phi i32* [ %q, %e ]
109	%tmp = bitcast i32* %x to i32*		; <i32*> [#uses=2]
110	%tmp2 = select i1 %b, i32* %tmp, i32* %y
111	%val = load i32, i32* %tmp2		; <i32> [#uses=1]
112	store i32 0, i32* %tmp
113	store i32* %y, i32** @g
114	ret i32 %val
115}
116
117; EITHER: define i32 @nc1_addrspace(i32* %q, i32 addrspace(1)* nocapture %p, i1 %b)
118define i32 @nc1_addrspace(i32* %q, i32 addrspace(1)* %p, i1 %b) {
119e:
120	br label %l
121l:
122	%x = phi i32 addrspace(1)* [ %p, %e ]
123	%y = phi i32* [ %q, %e ]
124	%tmp = addrspacecast i32 addrspace(1)* %x to i32*		; <i32*> [#uses=2]
125	%tmp2 = select i1 %b, i32* %tmp, i32* %y
126	%val = load i32, i32* %tmp2		; <i32> [#uses=1]
127	store i32 0, i32* %tmp
128	store i32* %y, i32** @g
129	ret i32 %val
130}
131
132; EITHER: define void @nc2(i32* nocapture %p, i32* %q)
133define void @nc2(i32* %p, i32* %q) {
134	%1 = call i32 @nc1(i32* %q, i32* %p, i1 0)		; <i32> [#uses=0]
135	ret void
136}
137
138
139; FNATTR: define void @nc3(void ()* nocapture %p)
140; ATTRIBUTOR: define void @nc3(void ()* nocapture nonnull %p)
141define void @nc3(void ()* %p) {
142	call void %p()
143	ret void
144}
145
146declare void @external(i8*) readonly nounwind
147; EITHER: define void @nc4(i8* nocapture readonly %p)
148define void @nc4(i8* %p) {
149	call void @external(i8* %p)
150	ret void
151}
152
153; FNATTR: define void @nc5(void (i8*)* nocapture %f, i8* nocapture %p)
154; ATTRIBUTOR: define void @nc5(void (i8*)* nocapture nonnull %f, i8* nocapture %p)
155define void @nc5(void (i8*)* %f, i8* %p) {
156	call void %f(i8* %p) readonly nounwind
157	call void %f(i8* nocapture %p)
158	ret void
159}
160
161; FNATTR:     define void @test1_1(i8* nocapture readnone %x1_1, i8* %y1_1, i1 %c)
162; ATTRIBUTOR: define void @test1_1(i8* nocapture readnone %x1_1, i8* nocapture readnone %y1_1, i1 %c)
163; It would be acceptable to add readnone to %y1_1 and %y1_2.
164define void @test1_1(i8* %x1_1, i8* %y1_1, i1 %c) {
165  call i8* @test1_2(i8* %x1_1, i8* %y1_1, i1 %c)
166  store i32* null, i32** @g
167  ret void
168}
169
170; FNATTR: define i8* @test1_2(i8* nocapture readnone %x1_2, i8* returned %y1_2, i1 %c)
171; ATTRIBUTOR: define i8* @test1_2(i8* nocapture readnone %x1_2, i8* readnone returned "no-capture-maybe-returned" %y1_2, i1 %c)
172define i8* @test1_2(i8* %x1_2, i8* %y1_2, i1 %c) {
173  br i1 %c, label %t, label %f
174t:
175  call void @test1_1(i8* %x1_2, i8* %y1_2, i1 %c)
176  store i32* null, i32** @g
177  br label %f
178f:
179  ret i8* %y1_2
180}
181
182; EITHER: define void @test2(i8* nocapture readnone %x2)
183define void @test2(i8* %x2) {
184  call void @test2(i8* %x2)
185  store i32* null, i32** @g
186  ret void
187}
188
189; EITHER: define void @test3(i8* nocapture readnone %x3, i8* nocapture readnone %y3, i8* nocapture readnone %z3)
190define void @test3(i8* %x3, i8* %y3, i8* %z3) {
191  call void @test3(i8* %z3, i8* %y3, i8* %x3)
192  store i32* null, i32** @g
193  ret void
194}
195
196; FNATTR: define void @test4_1(i8* %x4_1, i1 %c)
197; ATTRIBUTOR: define void @test4_1(i8* nocapture readnone %x4_1, i1 %c)
198define void @test4_1(i8* %x4_1, i1 %c) {
199  call i8* @test4_2(i8* %x4_1, i8* %x4_1, i8* %x4_1, i1 %c)
200  store i32* null, i32** @g
201  ret void
202}
203
204; FNATTR: define i8* @test4_2(i8* nocapture readnone %x4_2, i8* readnone returned %y4_2, i8* nocapture readnone %z4_2, i1 %c)
205; ATTRIBUTOR: define i8* @test4_2(i8* nocapture readnone %x4_2, i8* readnone returned "no-capture-maybe-returned" %y4_2, i8* nocapture readnone %z4_2, i1 %c)
206define i8* @test4_2(i8* %x4_2, i8* %y4_2, i8* %z4_2, i1 %c) {
207  br i1 %c, label %t, label %f
208t:
209  call void @test4_1(i8* null, i1 %c)
210  store i32* null, i32** @g
211  br label %f
212f:
213  ret i8* %y4_2
214}
215
216declare i8* @test5_1(i8* %x5_1)
217
218; EITHER: define void @test5_2(i8* %x5_2)
219define void @test5_2(i8* %x5_2) {
220  call i8* @test5_1(i8* %x5_2)
221  store i32* null, i32** @g
222  ret void
223}
224
225declare void @test6_1(i8* %x6_1, i8* nocapture %y6_1, ...)
226
227; EITHER: define void @test6_2(i8* %x6_2, i8* nocapture %y6_2, i8* %z6_2)
228define void @test6_2(i8* %x6_2, i8* %y6_2, i8* %z6_2) {
229  call void (i8*, i8*, ...) @test6_1(i8* %x6_2, i8* %y6_2, i8* %z6_2)
230  store i32* null, i32** @g
231  ret void
232}
233
234; FNATTR: define void @test_cmpxchg(i32* nocapture %p)
235; ATTRIBUTOR: define void @test_cmpxchg(i32* nocapture nonnull dereferenceable(4) %p)
236define void @test_cmpxchg(i32* %p) {
237  cmpxchg i32* %p, i32 0, i32 1 acquire monotonic
238  ret void
239}
240
241; FNATTR: define void @test_cmpxchg_ptr(i32** nocapture %p, i32* %q)
242; ATTRIBUTOR: define void @test_cmpxchg_ptr(i32** nocapture nonnull dereferenceable(8) %p, i32* %q)
243define void @test_cmpxchg_ptr(i32** %p, i32* %q) {
244  cmpxchg i32** %p, i32* null, i32* %q acquire monotonic
245  ret void
246}
247
248; FNATTR: define void @test_atomicrmw(i32* nocapture %p)
249; ATTRIBUTOR: define void @test_atomicrmw(i32* nocapture nonnull dereferenceable(4) %p)
250define void @test_atomicrmw(i32* %p) {
251  atomicrmw add i32* %p, i32 1 seq_cst
252  ret void
253}
254
255; EITHER: define void @test_volatile(i32* %x)
256define void @test_volatile(i32* %x) {
257entry:
258  %gep = getelementptr i32, i32* %x, i64 1
259  store volatile i32 0, i32* %gep, align 4
260  ret void
261}
262
263; EITHER: nocaptureLaunder(i8* nocapture %p)
264define void @nocaptureLaunder(i8* %p) {
265entry:
266  %b = call i8* @llvm.launder.invariant.group.p0i8(i8* %p)
267  store i8 42, i8* %b
268  ret void
269}
270
271@g2 = global i8* null
272; EITHER: define void @captureLaunder(i8* %p)
273define void @captureLaunder(i8* %p) {
274  %b = call i8* @llvm.launder.invariant.group.p0i8(i8* %p)
275  store i8* %b, i8** @g2
276  ret void
277}
278
279; FNATTR: @nocaptureStrip(i8* nocapture %p)
280; ATTRIBUTOR: @nocaptureStrip(i8* nocapture writeonly %p)
281define void @nocaptureStrip(i8* %p) {
282entry:
283  %b = call i8* @llvm.strip.invariant.group.p0i8(i8* %p)
284  store i8 42, i8* %b
285  ret void
286}
287
288@g3 = global i8* null
289; FNATTR: define void @captureStrip(i8* %p)
290; ATTRIBUTOR: define void @captureStrip(i8* writeonly %p)
291define void @captureStrip(i8* %p) {
292  %b = call i8* @llvm.strip.invariant.group.p0i8(i8* %p)
293  store i8* %b, i8** @g3
294  ret void
295}
296
297; EITHER: define i1 @captureICmp(i32* readnone %x)
298define i1 @captureICmp(i32* %x) {
299  %1 = icmp eq i32* %x, null
300  ret i1 %1
301}
302
303; EITHER: define i1 @captureICmpRev(i32* readnone %x)
304define i1 @captureICmpRev(i32* %x) {
305  %1 = icmp eq i32* null, %x
306  ret i1 %1
307}
308
309; FNATTR: define i1 @nocaptureInboundsGEPICmp(i32* nocapture readnone %x)
310; ATTRIBUTOR: define i1 @nocaptureInboundsGEPICmp(i32* nocapture nonnull readnone %x)
311define i1 @nocaptureInboundsGEPICmp(i32* %x) {
312  %1 = getelementptr inbounds i32, i32* %x, i32 5
313  %2 = bitcast i32* %1 to i8*
314  %3 = icmp eq i8* %2, null
315  ret i1 %3
316}
317
318; FNATTR: define i1 @nocaptureInboundsGEPICmpRev(i32* nocapture readnone %x)
319; ATTRIBUTOR: define i1 @nocaptureInboundsGEPICmpRev(i32* nocapture nonnull readnone %x)
320define i1 @nocaptureInboundsGEPICmpRev(i32* %x) {
321  %1 = getelementptr inbounds i32, i32* %x, i32 5
322  %2 = bitcast i32* %1 to i8*
323  %3 = icmp eq i8* null, %2
324  ret i1 %3
325}
326
327; EITHER: define i1 @nocaptureDereferenceableOrNullICmp(i32* nocapture readnone dereferenceable_or_null(4) %x)
328define i1 @nocaptureDereferenceableOrNullICmp(i32* dereferenceable_or_null(4) %x) {
329  %1 = bitcast i32* %x to i8*
330  %2 = icmp eq i8* %1, null
331  ret i1 %2
332}
333
334; EITHER: define i1 @captureDereferenceableOrNullICmp(i32* readnone dereferenceable_or_null(4) %x)
335define i1 @captureDereferenceableOrNullICmp(i32* dereferenceable_or_null(4) %x) "null-pointer-is-valid"="true" {
336  %1 = bitcast i32* %x to i8*
337  %2 = icmp eq i8* %1, null
338  ret i1 %2
339}
340
341declare void @unknown(i8*)
342define void @test_callsite() {
343entry:
344; We know that 'null' in AS 0 does not alias anything and cannot be captured. Though the latter is not qurried -> derived atm.
345; ATTRIBUTOR: call void @unknown(i8* noalias null)
346  call void @unknown(i8* null)
347  ret void
348}
349
350declare i8* @unknownpi8pi8(i8*,i8* returned)
351define i8* @test_returned1(i8* %A, i8* returned %B) nounwind readonly {
352; ATTRIBUTOR: define i8* @test_returned1(i8* nocapture readonly %A, i8* readonly returned %B)
353entry:
354  %p = call i8* @unknownpi8pi8(i8* %A, i8* %B)
355  ret i8* %p
356}
357
358define i8* @test_returned2(i8* %A, i8* %B) {
359; ATTRIBUTOR: define i8* @test_returned2(i8* nocapture readonly %A, i8* readonly returned %B)
360entry:
361  %p = call i8* @unknownpi8pi8(i8* %A, i8* %B) nounwind readonly
362  ret i8* %p
363}
364
365declare i8* @llvm.launder.invariant.group.p0i8(i8*)
366declare i8* @llvm.strip.invariant.group.p0i8(i8*)
367