1; RUN: opt -function-attrs -S < %s | FileCheck %s
2; RUN: opt -passes=function-attrs -S < %s | FileCheck %s
3
4; CHECK: Function Attrs
5; CHECK-NOT: convergent
6; CHECK-NEXT: define i32 @nonleaf()
7define i32 @nonleaf() convergent {
8  %a = call i32 @leaf()
9  ret i32 %a
10}
11
12; CHECK: Function Attrs
13; CHECK-NOT: convergent
14; CHECK-NEXT: define i32 @leaf()
15define i32 @leaf() convergent {
16  ret i32 0
17}
18
19; CHECK: Function Attrs
20; CHECK-SAME: convergent
21; CHECK-NEXT: declare i32 @k()
22declare i32 @k() convergent
23
24; CHECK: Function Attrs
25; CHECK-SAME: convergent
26; CHECK-NEXT: define i32 @extern()
27define i32 @extern() convergent {
28  %a = call i32 @k() convergent
29  ret i32 %a
30}
31
32; Convergent should not be removed on the function here.  Although the call is
33; not explicitly convergent, it picks up the convergent attr from the callee.
34;
35; CHECK: Function Attrs
36; CHECK-SAME: convergent
37; CHECK-NEXT: define i32 @extern_non_convergent_call()
38define i32 @extern_non_convergent_call() convergent {
39  %a = call i32 @k()
40  ret i32 %a
41}
42
43; CHECK: Function Attrs
44; CHECK-SAME: convergent
45; CHECK-NEXT: define i32 @indirect_convergent_call(
46define i32 @indirect_convergent_call(i32 ()* %f) convergent {
47   %a = call i32 %f() convergent
48   ret i32 %a
49}
50; Give indirect_non_convergent_call the norecurse attribute so we get a
51; "Function Attrs" comment in the output.
52;
53; CHECK: Function Attrs
54; CHECK-NOT: convergent
55; CHECK-NEXT: define i32 @indirect_non_convergent_call(
56define i32 @indirect_non_convergent_call(i32 ()* %f) convergent norecurse {
57   %a = call i32 %f()
58   ret i32 %a
59}
60
61; CHECK: Function Attrs
62; CHECK-SAME: convergent
63; CHECK-NEXT: declare void @llvm.nvvm.barrier0()
64declare void @llvm.nvvm.barrier0() convergent
65
66; CHECK: Function Attrs
67; CHECK-SAME: convergent
68; CHECK-NEXT: define i32 @intrinsic()
69define i32 @intrinsic() convergent {
70  ; Implicitly convergent, because the intrinsic is convergent.
71  call void @llvm.nvvm.barrier0()
72  ret i32 0
73}
74
75; CHECK: Function Attrs
76; CHECK-NOT: convergent
77; CHECK-NEXT: define i32 @recursive1()
78define i32 @recursive1() convergent {
79  %a = call i32 @recursive2() convergent
80  ret i32 %a
81}
82
83; CHECK: Function Attrs
84; CHECK-NOT: convergent
85; CHECK-NEXT: define i32 @recursive2()
86define i32 @recursive2() convergent {
87  %a = call i32 @recursive1() convergent
88  ret i32 %a
89}
90
91; CHECK: Function Attrs
92; CHECK-SAME: convergent
93; CHECK-NEXT: define i32 @noopt()
94define i32 @noopt() convergent optnone noinline {
95  %a = call i32 @noopt_friend() convergent
96  ret i32 0
97}
98
99; A function which is mutually-recursive with a convergent, optnone function
100; shouldn't have its convergent attribute stripped.
101; CHECK: Function Attrs
102; CHECK-SAME: convergent
103; CHECK-NEXT: define i32 @noopt_friend()
104define i32 @noopt_friend() convergent {
105  %a = call i32 @noopt()
106  ret i32 0
107}
108