Skip to content

Commit 260854b

Browse files
author
Justin Lebar
committedFeb 9, 2016
Add convergent-removing bits to FunctionAttrs pass.
Summary: Remove the convergent attribute on any functions which provably do not contain or invoke any convergent functions. After this change, we'll be able to modify clang to conservatively add 'convergent' to all functions when compiling CUDA. Reviewers: jingyue, joker.eph Subscribers: llvm-commits, tra, jhen, hfinkel, resistor, chandlerc, arsenm Differential Revision: http://reviews.llvm.org/D17013 llvm-svn: 260319
1 parent d5fb695 commit 260854b

File tree

3 files changed

+160
-0
lines changed

3 files changed

+160
-0
lines changed
 

‎llvm/include/llvm/IR/Function.h

+3
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,9 @@ class Function : public GlobalObject, public ilist_node<Function> {
339339
void setConvergent() {
340340
addFnAttr(Attribute::Convergent);
341341
}
342+
void setNotConvergent() {
343+
removeFnAttr(Attribute::Convergent);
344+
}
342345

343346
/// Determine if the function is known not to recurse, directly or
344347
/// indirectly.

‎llvm/lib/Transforms/IPO/FunctionAttrs.cpp

+63
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,68 @@ static bool addNonNullAttrs(const SCCNodeSet &SCCNodes,
935935
return MadeChange;
936936
}
937937

938+
/// Removes convergent attributes where we can prove that none of the SCC's
939+
/// callees are themselves convergent. Returns true if successful at removing
940+
/// the attribute.
941+
static bool removeConvergentAttrs(const CallGraphSCC &SCC,
942+
const SCCNodeSet &SCCNodes) {
943+
// Determines whether a function can be made non-convergent, ignoring all
944+
// other functions in SCC. (A function can *actually* be made non-convergent
945+
// only if all functions in its SCC can be made convergent.)
946+
auto CanRemoveConvergent = [&] (CallGraphNode *CGN) {
947+
Function *F = CGN->getFunction();
948+
if (!F) return false;
949+
950+
if (!F->isConvergent()) return true;
951+
952+
// Can't remove convergent from declarations.
953+
if (F->isDeclaration()) return false;
954+
955+
// Don't remove convergent from optnone functions.
956+
if (F->hasFnAttribute(Attribute::OptimizeNone))
957+
return false;
958+
959+
// Can't remove convergent if any of F's callees -- ignoring functions in the
960+
// SCC itself -- are convergent.
961+
if (llvm::any_of(*CGN, [&](const CallGraphNode::CallRecord &CR) {
962+
Function *F = CR.second->getFunction();
963+
return SCCNodes.count(F) == 0 && (!F || F->isConvergent());
964+
}))
965+
return false;
966+
967+
// CGN doesn't contain calls to intrinsics, so iterate over all of F's
968+
// callsites, looking for any calls to convergent intrinsics. If we find one,
969+
// F must remain marked as convergent.
970+
auto IsConvergentIntrinsicCall = [](Instruction &I) {
971+
CallSite CS(cast<Value>(&I));
972+
if (!CS)
973+
return false;
974+
Function *Callee = CS.getCalledFunction();
975+
return Callee && Callee->isIntrinsic() && Callee->isConvergent();
976+
};
977+
return !llvm::any_of(*F, [=](BasicBlock &BB) {
978+
return llvm::any_of(BB, IsConvergentIntrinsicCall);
979+
});
980+
};
981+
982+
// We can remove the convergent attr from functions in the SCC if they all can
983+
// be made non-convergent (because they call only non-convergent functions,
984+
// other than each other).
985+
if (!llvm::all_of(SCC, CanRemoveConvergent)) return false;
986+
987+
// If we got here, all of the SCC's callees are non-convergent, and none of
988+
// the optnone functions in the SCC are marked as convergent. Therefore all
989+
// of the SCC's functions can be marked as non-convergent.
990+
for (CallGraphNode *CGN : SCC)
991+
if (Function *F = CGN->getFunction()) {
992+
if (F->isConvergent())
993+
DEBUG(dbgs() << "Removing convergent attr from " << F->getName()
994+
<< "\n");
995+
F->setNotConvergent();
996+
}
997+
return true;
998+
}
999+
9381000
static bool setDoesNotRecurse(Function &F) {
9391001
if (F.doesNotRecurse())
9401002
return false;
@@ -1011,6 +1073,7 @@ bool PostOrderFunctionAttrs::runOnSCC(CallGraphSCC &SCC) {
10111073
if (!ExternalNode) {
10121074
Changed |= addNoAliasAttrs(SCCNodes);
10131075
Changed |= addNonNullAttrs(SCCNodes, *TLI);
1076+
Changed |= removeConvergentAttrs(SCC, SCCNodes);
10141077
}
10151078

10161079
Changed |= addNoRecurseAttrs(SCC);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
; RUN: opt < %s -basicaa -functionattrs -rpo-functionattrs -S | FileCheck %s
2+
3+
; CHECK: Function Attrs
4+
; CHECK-NOT: convergent
5+
; CHECK-NEXT: define i32 @nonleaf()
6+
define i32 @nonleaf() convergent {
7+
%a = call i32 @leaf()
8+
ret i32 %a
9+
}
10+
11+
; CHECK: Function Attrs
12+
; CHECK-NOT: convergent
13+
; CHECK-NEXT: define i32 @leaf()
14+
define i32 @leaf() convergent {
15+
ret i32 0
16+
}
17+
18+
; CHECK: Function Attrs
19+
; CHECK-SAME: convergent
20+
; CHECK-NEXT: declare i32 @k()
21+
declare i32 @k() convergent
22+
23+
; CHECK: Function Attrs
24+
; CHECK-SAME: convergent
25+
; CHECK-NEXT: define i32 @extern()
26+
define i32 @extern() convergent {
27+
%a = call i32 @k()
28+
ret i32 %a
29+
}
30+
31+
; CHECK: Function Attrs
32+
; CHECK-SAME: convergent
33+
; CHECK-NEXT: define i32 @call_extern()
34+
define i32 @call_extern() convergent {
35+
%a = call i32 @extern()
36+
ret i32 %a
37+
}
38+
39+
; CHECK: Function Attrs
40+
; CHECK-SAME: convergent
41+
; CHECK-NEXT: declare void @llvm.cuda.syncthreads()
42+
declare void @llvm.cuda.syncthreads() convergent
43+
44+
; CHECK: Function Attrs
45+
; CHECK-SAME: convergent
46+
; CHECK-NEXT: define i32 @intrinsic()
47+
define i32 @intrinsic() convergent {
48+
call void @llvm.cuda.syncthreads()
49+
ret i32 0
50+
}
51+
52+
@xyz = global i32 ()* null
53+
; CHECK: Function Attrs
54+
; CHECK-SAME: convergent
55+
; CHECK-NEXT: define i32 @functionptr()
56+
define i32 @functionptr() convergent {
57+
%1 = load i32 ()*, i32 ()** @xyz
58+
%2 = call i32 %1()
59+
ret i32 %2
60+
}
61+
62+
; CHECK: Function Attrs
63+
; CHECK-NOT: convergent
64+
; CHECK-NEXT: define i32 @recursive1()
65+
define i32 @recursive1() convergent {
66+
%a = call i32 @recursive2()
67+
ret i32 %a
68+
}
69+
70+
; CHECK: Function Attrs
71+
; CHECK-NOT: convergent
72+
; CHECK-NEXT: define i32 @recursive2()
73+
define i32 @recursive2() convergent {
74+
%a = call i32 @recursive1()
75+
ret i32 %a
76+
}
77+
78+
; CHECK: Function Attrs
79+
; CHECK-SAME: convergent
80+
; CHECK-NEXT: define i32 @noopt()
81+
define i32 @noopt() convergent optnone noinline {
82+
%a = call i32 @noopt_friend()
83+
ret i32 0
84+
}
85+
86+
; A function which is mutually-recursive with a convergent, optnone function
87+
; shouldn't have its convergent attribute stripped.
88+
; CHECK: Function Attrs
89+
; CHECK-SAME: convergent
90+
; CHECK-NEXT: define i32 @noopt_friend()
91+
define i32 @noopt_friend() convergent {
92+
%a = call i32 @noopt()
93+
ret i32 0
94+
}

0 commit comments

Comments
 (0)
Please sign in to comment.