1; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefix=FNATTR
2; RUN: opt -attributor -attributor-disable=false -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* %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 readonly %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 %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 readonly %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 %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 %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* %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* %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 uwtable
189; ATTRIBUTOR-NEXT: define i32 @scc1(i32* %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 uwtable
199; ATTRIBUTOR-NEXT: define void @scc2(i32* %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* %0, %"struct.std::atomic"* %1)
228define void @foo1(i32* %0, %"struct.std::atomic"* %1) {
229  store i32 100, i32* %0, align 4
230  fence release
231  %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
232  store atomic i8 1, i8* %3 monotonic, align 1
233  ret void
234}
235
236; FNATTR: Function Attrs: nofree norecurse nounwind
237; FNATTR-NEXT: define void @bar(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1)
238; ATTRIBUTOR-NOT: nosync
239; ATTRIBUTOR: define void @bar(i32* %0, %"struct.std::atomic"* %1)
240define void @bar(i32* %0, %"struct.std::atomic"* %1) {
241  %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
242  br label %4
243
2444:                                                ; preds = %4, %2
245  %5 = load atomic i8, i8* %3  monotonic, align 1
246  %6 = and i8 %5, 1
247  %7 = icmp eq i8 %6, 0
248  br i1 %7, label %4, label %8
249
2508:                                                ; preds = %4
251  fence acquire
252  ret void
253}
254
255; TEST 13 - Fence syncscope("singlethread") seq_cst
256; FNATTR: Function Attrs: nofree norecurse nounwind
257; FNATTR-NEXT: define void @foo1_singlethread(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
258; ATTRIBUTOR: Function Attrs: nofree nosync
259; ATTRIBUTOR: define void @foo1_singlethread(i32* %0, %"struct.std::atomic"* %1)
260define void @foo1_singlethread(i32* %0, %"struct.std::atomic"* %1) {
261  store i32 100, i32* %0, align 4
262  fence syncscope("singlethread") release
263  %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
264  store atomic i8 1, i8* %3 monotonic, align 1
265  ret void
266}
267
268; FNATTR: Function Attrs: nofree norecurse nounwind
269; FNATTR-NEXT: define void @bar_singlethread(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1)
270; ATTRIBUTOR: Function Attrs: nofree nosync
271; ATTRIBUTOR: define void @bar_singlethread(i32* %0, %"struct.std::atomic"* %1)
272define void @bar_singlethread(i32* %0, %"struct.std::atomic"* %1) {
273  %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
274  br label %4
275
2764:                                                ; preds = %4, %2
277  %5 = load atomic i8, i8* %3  monotonic, align 1
278  %6 = and i8 %5, 1
279  %7 = icmp eq i8 %6, 0
280  br i1 %7, label %4, label %8
281
2828:                                                ; preds = %4
283  fence syncscope("singlethread") acquire
284  ret void
285}
286
287declare void @llvm.memcpy(i8* %dest, i8* %src, i32 %len, i1 %isvolatile)
288declare void @llvm.memset(i8* %dest, i8 %val, i32 %len, i1 %isvolatile)
289
290; TEST 14 - negative, checking volatile intrinsics.
291
292; ATTRIBUTOR: Function Attrs: nounwind
293; ATTRIBUTOR-NOT: nosync
294; ATTRIBUTOR-NEXT: define i32 @memcpy_volatile(i8* %ptr1, i8* %ptr2)
295define i32 @memcpy_volatile(i8* %ptr1, i8* %ptr2) {
296  call void @llvm.memcpy(i8* %ptr1, i8* %ptr2, i32 8, i1 1)
297  ret i32 4
298}
299
300; TEST 15 - positive, non-volatile intrinsic.
301
302; ATTRIBUTOR: Function Attrs: nosync
303; ATTRIBUTOR-NEXT: define i32 @memset_non_volatile(i8* %ptr1, i8 %val)
304define i32 @memset_non_volatile(i8* %ptr1, i8 %val) {
305  call void @llvm.memset(i8* %ptr1, i8 %val, i32 8, i1 0)
306  ret i32 4
307}
308
309; TEST 16 - negative, inline assembly.
310
311; ATTRIBUTOR: define i32 @inline_asm_test(i32 %x)
312define i32 @inline_asm_test(i32 %x) {
313  call i32 asm "bswap $0", "=r,r"(i32 %x)
314  ret i32 4
315}
316
317declare void @readnone_test() convergent readnone
318
319; ATTRIBUTOR: define void @convergent_readnone()
320; TEST 17 - negative. Convergent
321define void @convergent_readnone(){
322    call void @readnone_test()
323    ret void
324}
325
326; ATTRIBUTOR: Function Attrs: nounwind
327; ATTRIBUTOR-NEXT: declare void @llvm.x86.sse2.clflush(i8*)
328declare void @llvm.x86.sse2.clflush(i8*)
329@a = common global i32 0, align 4
330
331; TEST 18 - negative. Synchronizing intrinsic
332
333; ATTRIBUTOR: Function Attrs: nounwind
334; ATTRIBUTOR-NOT: nosync
335; ATTRIBUTOR-NEXT: define void @i_totally_sync()
336define void @i_totally_sync() {
337  tail call void @llvm.x86.sse2.clflush(i8* bitcast (i32* @a to i8*))
338  ret void
339}
340
341declare float @llvm.cos(float %val) readnone
342
343; TEST 19 - positive, readnone & non-convergent intrinsic.
344
345; ATTRIBUTOR: Function Attrs: nosync nounwind
346; ATTRIBUTOR-NEXT: define i32 @cos_test(float %x)
347define i32 @cos_test(float %x) {
348  call float @llvm.cos(float %x)
349  ret i32 4
350}
351