1; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefix=FNATTR
2; RUN: opt -attributor -attributor-manifest-internal -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR
3target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
4
5; Test cases designed for the nosync function attribute.
6; FIXME's are used to indicate problems and missing attributes.
7
8; struct RT {
9;   char A;
10;   int B[10][20];
11;   char C;
12; };
13; struct ST {
14;   int X;
15;   double Y;
16;   struct RT Z;
17; };
18;
19; int *foo(struct ST *s) {
20;   return &s[1].Z.B[5][13];
21; }
22
23; TEST 1
24; non-convergent and readnone implies nosync
25%struct.RT = type { i8, [10 x [20 x i32]], i8 }
26%struct.ST = type { i32, double, %struct.RT }
27
28; FNATTR: Function Attrs: norecurse nounwind optsize readnone ssp uwtable
29; FNATTR-NEXT: define nonnull i32* @foo(%struct.ST* readnone %s)
30; ATTRIBUTOR: Function Attrs: nofree nosync nounwind optsize readnone ssp uwtable
31; ATTRIBUTOR-NEXT: define nonnull i32* @foo(%struct.ST* nofree nonnull readnone "no-capture-maybe-returned" %s)
32define i32* @foo(%struct.ST* %s) nounwind uwtable readnone optsize ssp {
33entry:
34  %arrayidx = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 1, i32 2, i32 1, i64 5, i64 13
35  ret i32* %arrayidx
36}
37
38; TEST 2
39; atomic load with monotonic ordering
40; int load_monotonic(_Atomic int *num) {
41;   int n = atomic_load_explicit(num, memory_order_relaxed);
42;   return n;
43; }
44
45; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
46; FNATTR-NEXT: define i32 @load_monotonic(i32* nocapture readonly %0)
47; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind uwtable
48; ATTRIBUTOR-NEXT: define i32 @load_monotonic(i32* nocapture nofree nonnull readonly dereferenceable(4) %0)
49define i32 @load_monotonic(i32* nocapture readonly %0) norecurse nounwind uwtable {
50  %2 = load atomic i32, i32* %0 monotonic, align 4
51  ret i32 %2
52}
53
54
55; TEST 3
56; atomic store with monotonic ordering.
57; void store_monotonic(_Atomic int *num) {
58;   atomic_load_explicit(num, memory_order_relaxed);
59; }
60
61; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
62; FNATTR-NEXT: define void @store_monotonic(i32* nocapture %0)
63; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind uwtable
64; ATTRIBUTOR-NEXT: define void @store_monotonic(i32* nocapture nofree nonnull writeonly dereferenceable(4) %0)
65define void @store_monotonic(i32* nocapture %0) norecurse nounwind uwtable {
66  store atomic i32 10, i32* %0 monotonic, align 4
67  ret void
68}
69
70; TEST 4 - negative, should not deduce nosync
71; atomic load with acquire ordering.
72; int load_acquire(_Atomic int *num) {
73;   int n = atomic_load_explicit(num, memory_order_acquire);
74;   return n;
75; }
76
77; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
78; FNATTR-NEXT: define i32 @load_acquire(i32* nocapture readonly %0)
79; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable
80; ATTRIBUTOR-NOT: nosync
81; ATTRIBUTOR-NEXT: define i32 @load_acquire(i32* nocapture nofree nonnull readonly dereferenceable(4) %0)
82define i32 @load_acquire(i32* nocapture readonly %0) norecurse nounwind uwtable {
83  %2 = load atomic i32, i32* %0 acquire, align 4
84  ret i32 %2
85}
86
87; TEST 5 - negative, should not deduce nosync
88; atomic load with release ordering
89; void load_release(_Atomic int *num) {
90;   atomic_store_explicit(num, 10, memory_order_release);
91; }
92
93; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
94; FNATTR-NEXT: define void @load_release(i32* nocapture %0)
95; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable
96; ATTRIBUTOR-NOT: nosync
97; ATTRIBUTOR-NEXT: define void @load_release(i32* nocapture nofree writeonly %0)
98define void @load_release(i32* nocapture %0) norecurse nounwind uwtable {
99  store atomic volatile i32 10, i32* %0 release, align 4
100  ret void
101}
102
103; TEST 6 - negative volatile, relaxed atomic
104
105; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
106; FNATTR-NEXT: define void @load_volatile_release(i32* nocapture %0)
107; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable
108; ATTRIBUTOR-NOT: nosync
109; ATTRIBUTOR-NEXT: define void @load_volatile_release(i32* nocapture nofree writeonly %0)
110define void @load_volatile_release(i32* nocapture %0) norecurse nounwind uwtable {
111  store atomic volatile i32 10, i32* %0 release, align 4
112  ret void
113}
114
115; TEST 7 - negative, should not deduce nosync
116; volatile store.
117; void volatile_store(volatile int *num) {
118;   *num = 14;
119; }
120
121; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
122; FNATTR-NEXT: define void @volatile_store(i32* %0)
123; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable
124; ATTRIBUTOR-NOT: nosync
125; ATTRIBUTOR-NEXT: define void @volatile_store(i32* nofree %0)
126define void @volatile_store(i32* %0) norecurse nounwind uwtable {
127  store volatile i32 14, i32* %0, align 4
128  ret void
129}
130
131; TEST 8 - negative, should not deduce nosync
132; volatile load.
133; int volatile_load(volatile int *num) {
134;   int n = *num;
135;   return n;
136; }
137
138; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
139; FNATTR-NEXT: define i32 @volatile_load(i32* %0)
140; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable
141; ATTRIBUTOR-NOT: nosync
142; ATTRIBUTOR-NEXT: define i32 @volatile_load(i32* nofree %0)
143define i32 @volatile_load(i32* %0) norecurse nounwind uwtable {
144  %2 = load volatile i32, i32* %0, align 4
145  ret i32 %2
146}
147
148; TEST 9
149
150; FNATTR: Function Attrs: noinline nosync nounwind uwtable
151; FNATTR-NEXT: declare void @nosync_function()
152; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
153; ATTRIBUTOR-NEXT: declare void @nosync_function()
154declare void @nosync_function() noinline nounwind uwtable nosync
155
156; FNATTR: Function Attrs: noinline nounwind uwtable
157; FNATTR-NEXT: define void @call_nosync_function()
158; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
159; ATTRIBUTOR-next: define void @call_nosync_function()
160define void @call_nosync_function() nounwind uwtable noinline {
161  tail call void @nosync_function() noinline nounwind uwtable
162  ret void
163}
164
165; TEST 10 - negative, should not deduce nosync
166
167; FNATTR: Function Attrs: noinline nounwind uwtable
168; FNATTR-NEXT: declare void @might_sync()
169; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
170; ATTRIBUTOR-NEXT: declare void @might_sync()
171declare void @might_sync() noinline nounwind uwtable
172
173; FNATTR: Function Attrs: noinline nounwind uwtable
174; FNATTR-NEXT: define void @call_might_sync()
175; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
176; ATTRIBUTOR-NOT: nosync
177; ATTRIBUTOR-NEXT: define void @call_might_sync()
178define void @call_might_sync() nounwind uwtable noinline {
179  tail call void @might_sync() noinline nounwind uwtable
180  ret void
181}
182
183; TEST 11 - positive, should deduce nosync
184; volatile operation in same scc but dead. Call volatile_load defined in TEST 8.
185
186; FNATTR: Function Attrs: nofree noinline nounwind uwtable
187; FNATTR-NEXT: define i32 @scc1(i32* %0)
188; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind readnone uwtable
189; ATTRIBUTOR-NEXT: define i32 @scc1(i32* nocapture nofree readnone %0)
190define i32 @scc1(i32* %0) noinline nounwind uwtable {
191  tail call void @scc2(i32* %0);
192  %val = tail call i32 @volatile_load(i32* %0);
193  ret i32 %val;
194}
195
196; FNATTR: Function Attrs: nofree noinline nounwind uwtable
197; FNATTR-NEXT: define void @scc2(i32* %0)
198; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind readnone uwtable
199; ATTRIBUTOR-NEXT: define void @scc2(i32* nocapture nofree readnone %0)
200define void @scc2(i32* %0) noinline nounwind uwtable {
201  tail call i32 @scc1(i32* %0);
202  ret void;
203}
204
205; TEST 12 - fences, negative
206;
207; void foo1(int *a, std::atomic<bool> flag){
208;   *a = 100;
209;   atomic_thread_fence(std::memory_order_release);
210;   flag.store(true, std::memory_order_relaxed);
211; }
212;
213; void bar(int *a, std::atomic<bool> flag){
214;   while(!flag.load(std::memory_order_relaxed))
215;     ;
216;
217;   atomic_thread_fence(std::memory_order_acquire);
218;   int b = *a;
219; }
220
221%"struct.std::atomic" = type { %"struct.std::__atomic_base" }
222%"struct.std::__atomic_base" = type { i8 }
223
224; FNATTR: Function Attrs: nofree norecurse nounwind
225; FNATTR-NEXT: define void @foo1(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
226; ATTRIBUTOR-NOT: nosync
227; ATTRIBUTOR: define void @foo1(i32* nocapture nofree nonnull writeonly dereferenceable(4) %0, %"struct.std::atomic"* nocapture nofree nonnull writeonly dereferenceable(1) %1)
228
229define void @foo1(i32* %0, %"struct.std::atomic"* %1) {
230  store i32 100, i32* %0, align 4
231  fence release
232  %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
233  store atomic i8 1, i8* %3 monotonic, align 1
234  ret void
235}
236
237; FNATTR: Function Attrs: nofree norecurse nounwind
238; FNATTR-NEXT: define void @bar(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1)
239; ATTRIBUTOR-NOT: nosync
240; ATTRIBUTOR: define void @bar(i32* nocapture nofree readnone %0, %"struct.std::atomic"* nocapture nofree nonnull readonly dereferenceable(1) %1)
241define void @bar(i32* %0, %"struct.std::atomic"* %1) {
242  %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
243  br label %4
244
2454:                                                ; preds = %4, %2
246  %5 = load atomic i8, i8* %3  monotonic, align 1
247  %6 = and i8 %5, 1
248  %7 = icmp eq i8 %6, 0
249  br i1 %7, label %4, label %8
250
2518:                                                ; preds = %4
252  fence acquire
253  ret void
254}
255
256; TEST 13 - Fence syncscope("singlethread") seq_cst
257; FNATTR: Function Attrs: nofree norecurse nounwind
258; FNATTR-NEXT: define void @foo1_singlethread(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
259; ATTRIBUTOR: Function Attrs: nofree nosync nounwind willreturn
260; ATTRIBUTOR: define void @foo1_singlethread(i32* nocapture nofree nonnull writeonly dereferenceable(4) %0, %"struct.std::atomic"* nocapture nofree nonnull writeonly dereferenceable(1) %1)
261
262define void @foo1_singlethread(i32* %0, %"struct.std::atomic"* %1) {
263  store i32 100, i32* %0, align 4
264  fence syncscope("singlethread") release
265  %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
266  store atomic i8 1, i8* %3 monotonic, align 1
267  ret void
268}
269
270; FNATTR: Function Attrs: nofree norecurse nounwind
271; FNATTR-NEXT: define void @bar_singlethread(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1)
272; ATTRIBUTOR: Function Attrs: nofree nosync nounwind
273; ATTRIBUTOR: define void @bar_singlethread(i32* nocapture nofree readnone %0, %"struct.std::atomic"* nocapture nofree nonnull readonly dereferenceable(1) %1)
274define void @bar_singlethread(i32* %0, %"struct.std::atomic"* %1) {
275  %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
276  br label %4
277
2784:                                                ; preds = %4, %2
279  %5 = load atomic i8, i8* %3  monotonic, align 1
280  %6 = and i8 %5, 1
281  %7 = icmp eq i8 %6, 0
282  br i1 %7, label %4, label %8
283
2848:                                                ; preds = %4
285  fence syncscope("singlethread") acquire
286  ret void
287}
288
289declare void @llvm.memcpy(i8* %dest, i8* %src, i32 %len, i1 %isvolatile)
290declare void @llvm.memset(i8* %dest, i8 %val, i32 %len, i1 %isvolatile)
291
292; TEST 14 - negative, checking volatile intrinsics.
293
294; It is odd to add nocapture but a result of the llvm.memcpy nocapture.
295;
296; ATTRIBUTOR: Function Attrs: nounwind
297; ATTRIBUTOR-NOT: nosync
298; ATTRIBUTOR-NEXT: define i32 @memcpy_volatile(i8* nocapture writeonly %ptr1, i8* nocapture readonly %ptr2)
299define i32 @memcpy_volatile(i8* %ptr1, i8* %ptr2) {
300  call void @llvm.memcpy(i8* %ptr1, i8* %ptr2, i32 8, i1 1)
301  ret i32 4
302}
303
304; TEST 15 - positive, non-volatile intrinsic.
305
306; It is odd to add nocapture but a result of the llvm.memset nocapture.
307;
308; ATTRIBUTOR: Function Attrs: nosync
309; ATTRIBUTOR-NEXT: define i32 @memset_non_volatile(i8* nocapture writeonly %ptr1, i8 %val)
310define i32 @memset_non_volatile(i8* %ptr1, i8 %val) {
311  call void @llvm.memset(i8* %ptr1, i8 %val, i32 8, i1 0)
312  ret i32 4
313}
314
315; TEST 16 - negative, inline assembly.
316
317; ATTRIBUTOR: define i32 @inline_asm_test(i32 %x)
318define i32 @inline_asm_test(i32 %x) {
319  call i32 asm "bswap $0", "=r,r"(i32 %x)
320  ret i32 4
321}
322
323declare void @readnone_test() convergent readnone
324
325; ATTRIBUTOR: define void @convergent_readnone()
326; TEST 17 - negative. Convergent
327define void @convergent_readnone(){
328    call void @readnone_test()
329    ret void
330}
331
332; ATTRIBUTOR: Function Attrs: nounwind
333; ATTRIBUTOR-NEXT: declare void @llvm.x86.sse2.clflush(i8*)
334declare void @llvm.x86.sse2.clflush(i8*)
335@a = common global i32 0, align 4
336
337; TEST 18 - negative. Synchronizing intrinsic
338
339; ATTRIBUTOR: Function Attrs: nounwind
340; ATTRIBUTOR-NOT: nosync
341; ATTRIBUTOR-NEXT: define void @i_totally_sync()
342define void @i_totally_sync() {
343  tail call void @llvm.x86.sse2.clflush(i8* bitcast (i32* @a to i8*))
344  ret void
345}
346
347declare float @llvm.cos(float %val) readnone
348
349; TEST 19 - positive, readnone & non-convergent intrinsic.
350
351; ATTRIBUTOR: Function Attrs: nosync nounwind
352; ATTRIBUTOR-NEXT: define i32 @cos_test(float %x)
353define i32 @cos_test(float %x) {
354  call float @llvm.cos(float %x)
355  ret i32 4
356}
357