Skip to content

Commit df34428

Browse files
authored
[IR] Relax convergence requirements on call (#135794)
Before this commit, having a convergence token on a non-convergent call was considered to be an error. This commit relaxes this requirement and allows convergence tokens to be present on non-convergent calls. When such token is present, they have no effect as the underlying call is non-convergent. This allows passes like DCE to strip `convergent` attribute from functions for which all convergent operations have been stripped. When this happens, a convergence token can still exist in the call-site, causing the verifier to complain. Alternatives have been considered in #134863 and #134844.
1 parent abe93fe commit df34428

File tree

6 files changed

+203
-12
lines changed

6 files changed

+203
-12
lines changed

llvm/include/llvm/IR/GenericConvergenceVerifierImpl.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,6 @@ void GenericConvergenceVerifier<ContextT>::visit(const InstructionT &I) {
102102
SeenFirstConvOp = true;
103103

104104
if (TokenDef || ConvOp != CONV_NONE) {
105-
Check(isConvergent(I),
106-
"Convergence control token can only be used in a convergent call.",
107-
{Context.print(&I)});
108105
Check(ConvergenceKind != UncontrolledConvergence,
109106
"Cannot mix controlled and uncontrolled convergence in the same "
110107
"function.",

llvm/test/Assembler/convergence-control.ll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ define void @mixed2() {
2424
ret void
2525
}
2626

27+
; convergence control token can be used on non-convergent calls,
28+
; but it has no effect.
29+
define void @mixed3() {
30+
%t05_tok1 = call token @llvm.experimental.convergence.anchor()
31+
call void @g() [ "convergencectrl"(token %t05_tok1) ]
32+
ret void
33+
}
2734

2835
define void @region_nesting1(i1 %arg) convergent {
2936
A:
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 5
2+
; RUN: opt %s -passes=adce -S | FileCheck %s
3+
4+
define i32 @foo(i32 %a) #0 {
5+
; CHECK-LABEL: define i32 @foo(
6+
; CHECK-SAME: i32 [[A:%.*]]) #[[ATTR0:[0-9]+]] {
7+
; CHECK-NEXT: [[ENTRY:.*:]]
8+
; CHECK-NEXT: ret i32 [[A]]
9+
;
10+
entry:
11+
%tk = call token @llvm.experimental.convergence.entry()
12+
ret i32 %a
13+
}
14+
15+
define void @bar() #0 {
16+
; CHECK-LABEL: define void @bar(
17+
; CHECK-SAME: ) #[[ATTR0]] {
18+
; CHECK-NEXT: [[ENTRY:.*:]]
19+
; CHECK-NEXT: ret void
20+
;
21+
entry:
22+
%tk = call token @llvm.experimental.convergence.anchor()
23+
ret void
24+
}
25+
26+
define void @baz() #0 {
27+
; CHECK-LABEL: define void @baz(
28+
; CHECK-SAME: ) #[[ATTR0]] {
29+
; CHECK-NEXT: [[ENTRY:.*:]]
30+
; CHECK-NEXT: br label %[[HEADER:.*]]
31+
; CHECK: [[HEADER]]:
32+
; CHECK-NEXT: br i1 true, label %[[BODY:.*]], label %[[EXIT:.*]]
33+
; CHECK: [[BODY]]:
34+
; CHECK-NEXT: br label %[[HEADER]]
35+
; CHECK: [[EXIT]]:
36+
; CHECK-NEXT: ret void
37+
;
38+
entry:
39+
%tk0 = call token @llvm.experimental.convergence.entry()
40+
br label %header
41+
42+
header:
43+
%tk1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %tk0) ]
44+
br i1 true, label %body, label %exit
45+
46+
body:
47+
br label %header
48+
49+
exit:
50+
ret void
51+
}
52+
53+
define void @indirect_inner() #0 {
54+
; CHECK-LABEL: define void @indirect_inner(
55+
; CHECK-SAME: ) #[[ATTR0]] {
56+
; CHECK-NEXT: [[ENTRY:.*:]]
57+
; CHECK-NEXT: ret void
58+
;
59+
entry:
60+
%tk0 = call token @llvm.experimental.convergence.entry()
61+
ret void
62+
}
63+
64+
define void @indirect() #0 {
65+
; CHECK-LABEL: define void @indirect(
66+
; CHECK-SAME: ) #[[ATTR0]] {
67+
; CHECK-NEXT: [[ENTRY:.*:]]
68+
; CHECK-NEXT: [[TK0:%.*]] = call token @llvm.experimental.convergence.entry()
69+
; CHECK-NEXT: [[VAR:%.*]] = alloca ptr, align 8
70+
; CHECK-NEXT: store ptr @indirect_inner, ptr [[VAR]], align 8
71+
; CHECK-NEXT: [[PTR:%.*]] = load ptr, ptr [[VAR]], align 8
72+
; CHECK-NEXT: call void [[PTR]]() #[[ATTR0]] [ "convergencectrl"(token [[TK0]]) ]
73+
; CHECK-NEXT: ret void
74+
;
75+
entry:
76+
%tk0 = call token @llvm.experimental.convergence.entry()
77+
%var = alloca ptr, align 8
78+
store ptr @indirect_inner, ptr %var, align 8
79+
%ptr = load ptr, ptr %var, align 8
80+
call void %ptr() convergent [ "convergencectrl"(token %tk0) ]
81+
ret void
82+
}
83+
84+
declare token @llvm.experimental.convergence.entry() #1
85+
declare token @llvm.experimental.convergence.anchor() #1
86+
declare token @llvm.experimental.convergence.loop() #1
87+
88+
attributes #0 = { convergent }
89+
attributes #1 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
90+
;.
91+
; CHECK: attributes #[[ATTR0]] = { convergent }
92+
; CHECK: attributes #[[ATTR1:[0-9]+]] = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
93+
;.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 5
2+
; RUN: opt %s -passes=bdce -S | FileCheck %s
3+
4+
define i32 @foo(i32 %a) #0 {
5+
; CHECK-LABEL: define i32 @foo(
6+
; CHECK-SAME: i32 [[A:%.*]]) #[[ATTR0:[0-9]+]] {
7+
; CHECK-NEXT: [[ENTRY:.*:]]
8+
; CHECK-NEXT: ret i32 [[A]]
9+
;
10+
entry:
11+
%tk0 = call token @llvm.experimental.convergence.entry()
12+
ret i32 %a
13+
}
14+
15+
define void @bar() #0 {
16+
; CHECK-LABEL: define void @bar(
17+
; CHECK-SAME: ) #[[ATTR0]] {
18+
; CHECK-NEXT: [[ENTRY:.*:]]
19+
; CHECK-NEXT: ret void
20+
;
21+
entry:
22+
%tk0 = call token @llvm.experimental.convergence.anchor()
23+
ret void
24+
}
25+
26+
define void @baz() #0 {
27+
; CHECK-LABEL: define void @baz(
28+
; CHECK-SAME: ) #[[ATTR0]] {
29+
; CHECK-NEXT: [[ENTRY:.*:]]
30+
; CHECK-NEXT: br label %[[HEADER:.*]]
31+
; CHECK: [[HEADER]]:
32+
; CHECK-NEXT: br i1 true, label %[[BODY:.*]], label %[[EXIT:.*]]
33+
; CHECK: [[BODY]]:
34+
; CHECK-NEXT: br label %[[HEADER]]
35+
; CHECK: [[EXIT]]:
36+
; CHECK-NEXT: ret void
37+
;
38+
entry:
39+
%tk0 = call token @llvm.experimental.convergence.entry()
40+
br label %header
41+
42+
header:
43+
%tk1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %tk0) ]
44+
br i1 true, label %body, label %exit
45+
46+
body:
47+
br label %header
48+
49+
exit:
50+
ret void
51+
}
52+
53+
declare token @llvm.experimental.convergence.entry() #1
54+
declare token @llvm.experimental.convergence.anchor() #1
55+
declare token @llvm.experimental.convergence.loop() #1
56+
57+
attributes #0 = { convergent }
58+
attributes #1 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
59+
;.
60+
; CHECK: attributes #[[ATTR0]] = { convergent }
61+
; CHECK: attributes #[[ATTR1:[0-9]+]] = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
62+
;.

llvm/test/Transforms/FunctionAttrs/convergent.ll

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
22
; RUN: opt -passes=function-attrs -S < %s | FileCheck %s
33

44
define i32 @nonleaf() convergent {
@@ -129,3 +129,43 @@ define i32 @noopt_friend() convergent {
129129
%a = call i32 @noopt()
130130
ret i32 0
131131
}
132+
133+
134+
; A function which is stripped of its convergent attribute, even
135+
; if used in a controlled convergence call.
136+
; This should be OK.
137+
define i32 @leaf_noconvergent_used() convergent {
138+
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
139+
; CHECK-LABEL: define {{[^@]+}}@leaf_noconvergent_used
140+
; CHECK-SAME: () #[[ATTR0]] {
141+
; CHECK-NEXT: ret i32 0
142+
;
143+
ret i32 0
144+
}
145+
146+
define i32 @nonleaf_convergent() convergent {
147+
; CHECK: Function Attrs: convergent mustprogress nofree norecurse nosync nounwind willreturn memory(none)
148+
; CHECK-LABEL: define {{[^@]+}}@nonleaf_convergent
149+
; CHECK-SAME: () #[[ATTR7:[0-9]+]] {
150+
; CHECK-NEXT: [[TMP1:%.*]] = call token @llvm.experimental.convergence.entry()
151+
; CHECK-NEXT: [[TMP2:%.*]] = call i32 @leaf_noconvergent_used() [ "convergencectrl"(token [[TMP1]]) ]
152+
; CHECK-NEXT: ret i32 0
153+
;
154+
%1 = call token @llvm.experimental.convergence.entry()
155+
%2 = call i32 @leaf_noconvergent_used() [ "convergencectrl"(token %1) ]
156+
ret i32 0
157+
}
158+
159+
160+
declare token @llvm.experimental.convergence.entry() #1
161+
;.
162+
; CHECK: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) }
163+
; CHECK: attributes #[[ATTR1]] = { convergent }
164+
; CHECK: attributes #[[ATTR2]] = { norecurse }
165+
; CHECK: attributes #[[ATTR3:[0-9]+]] = { convergent nocallback nounwind }
166+
; CHECK: attributes #[[ATTR4]] = { convergent norecurse nounwind }
167+
; CHECK: attributes #[[ATTR5]] = { nofree nosync nounwind memory(none) }
168+
; CHECK: attributes #[[ATTR6]] = { convergent noinline optnone }
169+
; CHECK: attributes #[[ATTR7]] = { convergent mustprogress nofree norecurse nosync nounwind willreturn memory(none) }
170+
; CHECK: attributes #[[ATTR8:[0-9]+]] = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
171+
;.

llvm/test/Verifier/convergencectrl-invalid.ll

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,6 @@ define void @wrong_token() {
2020
ret void
2121
}
2222

23-
; CHECK: Convergence control token can only be used in a convergent call.
24-
; CHECK-NEXT call void @g(){{.*}}%t05_tok1
25-
define void @missing.attribute() {
26-
%t05_tok1 = call token @llvm.experimental.convergence.anchor()
27-
call void @g() [ "convergencectrl"(token %t05_tok1) ]
28-
ret void
29-
}
30-
3123
; CHECK: The 'convergencectrl' bundle requires exactly one token use.
3224
; CHECK-NEXT: call void @g()
3325
define void @multiple_tokens() {

0 commit comments

Comments
 (0)