blob: e2581b2b418fea2ac6388c5176899bb769d54e56 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
|
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
; RUN: opt -passes=function-attrs -S < %s | FileCheck %s
define i32 @nonleaf() convergent {
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@nonleaf
; CHECK-SAME: () #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: [[A:%.*]] = call i32 @leaf()
; CHECK-NEXT: ret i32 [[A]]
;
%a = call i32 @leaf()
ret i32 %a
}
define i32 @leaf() convergent {
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@leaf
; CHECK-SAME: () #[[ATTR0]] {
; CHECK-NEXT: ret i32 0
;
ret i32 0
}
declare i32 @k() convergent
define i32 @extern() convergent {
; CHECK: Function Attrs: convergent
; CHECK-LABEL: define {{[^@]+}}@extern
; CHECK-SAME: () #[[ATTR1:[0-9]+]] {
; CHECK-NEXT: [[A:%.*]] = call i32 @k() #[[ATTR1]]
; CHECK-NEXT: ret i32 [[A]]
;
%a = call i32 @k() convergent
ret i32 %a
}
; Convergent should not be removed on the function here. Although the call is
; not explicitly convergent, it picks up the convergent attr from the callee.
define i32 @extern_non_convergent_call() convergent {
; CHECK: Function Attrs: convergent
; CHECK-LABEL: define {{[^@]+}}@extern_non_convergent_call
; CHECK-SAME: () #[[ATTR1]] {
; CHECK-NEXT: [[A:%.*]] = call i32 @k()
; CHECK-NEXT: ret i32 [[A]]
;
%a = call i32 @k()
ret i32 %a
}
define i32 @indirect_convergent_call(ptr %f) convergent {
; CHECK: Function Attrs: convergent
; CHECK-LABEL: define {{[^@]+}}@indirect_convergent_call
; CHECK-SAME: (ptr readonly captures(none) [[F:%.*]]) #[[ATTR1]] {
; CHECK-NEXT: [[A:%.*]] = call i32 [[F]]() #[[ATTR1]]
; CHECK-NEXT: ret i32 [[A]]
;
%a = call i32 %f() convergent
ret i32 %a
}
; Give indirect_non_convergent_call the norecurse attribute so we get a
; "Function Attrs" comment in the output.
define i32 @indirect_non_convergent_call(ptr %f) convergent norecurse {
; CHECK: Function Attrs: norecurse
; CHECK-LABEL: define {{[^@]+}}@indirect_non_convergent_call
; CHECK-SAME: (ptr readonly captures(none) [[F:%.*]]) #[[ATTR2:[0-9]+]] {
; CHECK-NEXT: [[A:%.*]] = call i32 [[F]]()
; CHECK-NEXT: ret i32 [[A]]
;
%a = call i32 %f()
ret i32 %a
}
declare void @llvm.nvvm.barrier.cta.sync.aligned.all(i32) convergent
define i32 @intrinsic() convergent {
; Implicitly convergent, because the intrinsic is convergent.
; CHECK: Function Attrs: convergent norecurse nounwind
; CHECK-LABEL: define {{[^@]+}}@intrinsic
; CHECK-SAME: () #[[ATTR4:[0-9]+]] {
; CHECK-NEXT: call void @llvm.nvvm.barrier.cta.sync.aligned.all(i32 0)
; CHECK-NEXT: ret i32 0
;
call void @llvm.nvvm.barrier.cta.sync.aligned.all(i32 0)
ret i32 0
}
define i32 @recursive1() convergent {
; CHECK: Function Attrs: nofree nosync nounwind memory(none)
; CHECK-LABEL: define {{[^@]+}}@recursive1
; CHECK-SAME: () #[[ATTR5:[0-9]+]] {
; CHECK-NEXT: [[A:%.*]] = call i32 @recursive2() #[[ATTR1]]
; CHECK-NEXT: ret i32 [[A]]
;
%a = call i32 @recursive2() convergent
ret i32 %a
}
define i32 @recursive2() convergent {
; CHECK: Function Attrs: nofree nosync nounwind memory(none)
; CHECK-LABEL: define {{[^@]+}}@recursive2
; CHECK-SAME: () #[[ATTR5]] {
; CHECK-NEXT: [[A:%.*]] = call i32 @recursive1() #[[ATTR1]]
; CHECK-NEXT: ret i32 [[A]]
;
%a = call i32 @recursive1() convergent
ret i32 %a
}
define i32 @noopt() convergent optnone noinline {
; CHECK: Function Attrs: convergent noinline optnone
; CHECK-LABEL: define {{[^@]+}}@noopt
; CHECK-SAME: () #[[ATTR6:[0-9]+]] {
; CHECK-NEXT: [[A:%.*]] = call i32 @noopt_friend() #[[ATTR1]]
; CHECK-NEXT: ret i32 0
;
%a = call i32 @noopt_friend() convergent
ret i32 0
}
; A function which is mutually-recursive with a convergent, optnone function
; shouldn't have its convergent attribute stripped.
define i32 @noopt_friend() convergent {
; CHECK: Function Attrs: convergent
; CHECK-LABEL: define {{[^@]+}}@noopt_friend
; CHECK-SAME: () #[[ATTR1]] {
; CHECK-NEXT: [[A:%.*]] = call i32 @noopt()
; CHECK-NEXT: ret i32 0
;
%a = call i32 @noopt()
ret i32 0
}
; A function which is stripped of its convergent attribute, even
; if used in a controlled convergence call.
; This should be OK.
define i32 @leaf_noconvergent_used() convergent {
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@leaf_noconvergent_used
; CHECK-SAME: () #[[ATTR0]] {
; CHECK-NEXT: ret i32 0
;
ret i32 0
}
define i32 @nonleaf_convergent() convergent {
; CHECK: Function Attrs: convergent mustprogress nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@nonleaf_convergent
; CHECK-SAME: () #[[ATTR7:[0-9]+]] {
; CHECK-NEXT: [[TMP1:%.*]] = call token @llvm.experimental.convergence.entry()
; CHECK-NEXT: [[TMP2:%.*]] = call i32 @leaf_noconvergent_used() [ "convergencectrl"(token [[TMP1]]) ]
; CHECK-NEXT: ret i32 0
;
%1 = call token @llvm.experimental.convergence.entry()
%2 = call i32 @leaf_noconvergent_used() [ "convergencectrl"(token %1) ]
ret i32 0
}
declare token @llvm.experimental.convergence.entry() #1
;.
; CHECK: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) }
; CHECK: attributes #[[ATTR1]] = { convergent }
; CHECK: attributes #[[ATTR2]] = { norecurse }
; CHECK: attributes #[[ATTR3:[0-9]+]] = { convergent nocallback nounwind }
; CHECK: attributes #[[ATTR4]] = { convergent norecurse nounwind }
; CHECK: attributes #[[ATTR5]] = { nofree nosync nounwind memory(none) }
; CHECK: attributes #[[ATTR6]] = { convergent noinline optnone }
; CHECK: attributes #[[ATTR7]] = { convergent mustprogress nofree norecurse nosync nounwind willreturn memory(none) }
; CHECK: attributes #[[ATTR8:[0-9]+]] = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
;.
|