Skip to content

Commit c02e8f7

Browse files
authored
[llvm][transforms] Add a new algorithm to SplitModule (#95941)
The new round-robin algorithm overrides the hash-based distribution of functions to modules. It achieves a more even number of functions per module when the number of functions is close to the number of requested modules. It's not in use by default and is available under a new flag.
1 parent 7aa906d commit c02e8f7

File tree

5 files changed

+125
-15
lines changed

5 files changed

+125
-15
lines changed

llvm/include/llvm/Transforms/Utils/SplitModule.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ class Module;
2424

2525
/// Splits the module M into N linkable partitions. The function ModuleCallback
2626
/// is called N times passing each individual partition as the MPart argument.
27+
/// PreserveLocals: Split without externalizing locals.
28+
/// RoundRobin: Use round-robin distribution of functions to modules instead
29+
/// of the default name-hash-based one.
2730
///
2831
/// FIXME: This function does not deal with the somewhat subtle symbol
2932
/// visibility issues around module splitting, including (but not limited to):
@@ -35,7 +38,7 @@ class Module;
3538
void SplitModule(
3639
Module &M, unsigned N,
3740
function_ref<void(std::unique_ptr<Module> MPart)> ModuleCallback,
38-
bool PreserveLocals = false);
41+
bool PreserveLocals = false, bool RoundRobin = false);
3942

4043
} // end namespace llvm
4144

llvm/lib/Transforms/Utils/SplitModule.cpp

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,18 @@ using ClusterMapType = EquivalenceClasses<const GlobalValue *>;
5555
using ComdatMembersType = DenseMap<const Comdat *, const GlobalValue *>;
5656
using ClusterIDMapType = DenseMap<const GlobalValue *, unsigned>;
5757

58+
bool compareClusters(const std::pair<unsigned, unsigned> &A,
59+
const std::pair<unsigned, unsigned> &B) {
60+
if (A.second || B.second)
61+
return A.second > B.second;
62+
return A.first > B.first;
63+
}
64+
65+
using BalancingQueueType =
66+
std::priority_queue<std::pair<unsigned, unsigned>,
67+
std::vector<std::pair<unsigned, unsigned>>,
68+
decltype(compareClusters) *>;
69+
5870
} // end anonymous namespace
5971

6072
static void addNonConstUser(ClusterMapType &GVtoClusterMap,
@@ -154,18 +166,7 @@ static void findPartitions(Module &M, ClusterIDMapType &ClusterIDMap,
154166

155167
// Assigned all GVs to merged clusters while balancing number of objects in
156168
// each.
157-
auto CompareClusters = [](const std::pair<unsigned, unsigned> &a,
158-
const std::pair<unsigned, unsigned> &b) {
159-
if (a.second || b.second)
160-
return a.second > b.second;
161-
else
162-
return a.first > b.first;
163-
};
164-
165-
std::priority_queue<std::pair<unsigned, unsigned>,
166-
std::vector<std::pair<unsigned, unsigned>>,
167-
decltype(CompareClusters)>
168-
BalancingQueue(CompareClusters);
169+
BalancingQueueType BalancingQueue(compareClusters);
169170
// Pre-populate priority queue with N slot blanks.
170171
for (unsigned i = 0; i < N; ++i)
171172
BalancingQueue.push(std::make_pair(i, 0));
@@ -254,7 +255,7 @@ static bool isInPartition(const GlobalValue *GV, unsigned I, unsigned N) {
254255
void llvm::SplitModule(
255256
Module &M, unsigned N,
256257
function_ref<void(std::unique_ptr<Module> MPart)> ModuleCallback,
257-
bool PreserveLocals) {
258+
bool PreserveLocals, bool RoundRobin) {
258259
if (!PreserveLocals) {
259260
for (Function &F : M)
260261
externalize(&F);
@@ -271,6 +272,41 @@ void llvm::SplitModule(
271272
ClusterIDMapType ClusterIDMap;
272273
findPartitions(M, ClusterIDMap, N);
273274

275+
// Find functions not mapped to modules in ClusterIDMap and count functions
276+
// per module. Map unmapped functions using round-robin so that they skip
277+
// being distributed by isInPartition() based on function name hashes below.
278+
// This provides better uniformity of distribution of functions to modules
279+
// in some cases - for example when the number of functions equals to N.
280+
if (RoundRobin) {
281+
DenseMap<unsigned, unsigned> ModuleFunctionCount;
282+
SmallVector<const GlobalValue *> UnmappedFunctions;
283+
for (const auto &F : M.functions()) {
284+
if (F.isDeclaration() ||
285+
F.getLinkage() != GlobalValue::LinkageTypes::ExternalLinkage)
286+
continue;
287+
auto It = ClusterIDMap.find(&F);
288+
if (It == ClusterIDMap.end())
289+
UnmappedFunctions.push_back(&F);
290+
else
291+
++ModuleFunctionCount[It->second];
292+
}
293+
BalancingQueueType BalancingQueue(compareClusters);
294+
for (unsigned I = 0; I < N; ++I) {
295+
if (auto It = ModuleFunctionCount.find(I);
296+
It != ModuleFunctionCount.end())
297+
BalancingQueue.push(*It);
298+
else
299+
BalancingQueue.push({I, 0});
300+
}
301+
for (const auto *const F : UnmappedFunctions) {
302+
const unsigned I = BalancingQueue.top().first;
303+
const unsigned Count = BalancingQueue.top().second;
304+
BalancingQueue.pop();
305+
ClusterIDMap.insert({F, I});
306+
BalancingQueue.push({I, Count + 1});
307+
}
308+
}
309+
274310
// FIXME: We should be able to reuse M as the last partition instead of
275311
// cloning it. Note that the callers at the moment expect the module to
276312
// be preserved, so will need some adjustments as well.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
; RUN: llvm-split -o %t %s -j 2
2+
; RUN: llvm-dis -o - %t0 | FileCheck --check-prefix=CHECK0 %s
3+
; RUN: llvm-dis -o - %t1 | FileCheck --check-prefix=CHECK1 %s
4+
5+
; CHECK0-NOT: define
6+
; CHECK0: define void @D
7+
; CHECK0-NOT: define
8+
9+
; CHECK1-NOT: define
10+
; CHECK1: define void @A
11+
; CHECK1: define void @B
12+
; CHECK1: define void @C
13+
; CHECK1-NOT: define
14+
15+
define void @A() {
16+
ret void
17+
}
18+
19+
define void @B() {
20+
ret void
21+
}
22+
23+
define void @C() {
24+
ret void
25+
}
26+
27+
define void @D() {
28+
ret void
29+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
; RUN: llvm-split -o %t %s -j 2 -round-robin
2+
; RUN: llvm-dis -o - %t0 | FileCheck --check-prefix=CHECK0 %s
3+
; RUN: llvm-dis -o - %t1 | FileCheck --check-prefix=CHECK1 %s
4+
5+
; CHECK0-NOT: define
6+
; CHECK0: declare extern_weak void @e
7+
; CHECK0: define void @A
8+
; CHECK0: define void @C
9+
; CHECK0-NOT: define
10+
11+
; CHECK1-NOT: define
12+
; CHECK1: declare extern_weak void @e
13+
; CHECK1: define void @B
14+
; CHECK1: define void @D
15+
; CHECK1-NOT: define
16+
17+
declare extern_weak void @e(...)
18+
19+
define void @A() {
20+
ret void
21+
}
22+
23+
define void @B() {
24+
ret void
25+
}
26+
27+
define void @C() {
28+
ret void
29+
}
30+
31+
define void @D() {
32+
ret void
33+
}

llvm/tools/llvm-split/llvm-split.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ static cl::opt<bool>
5353
cl::desc("Split without externalizing locals"),
5454
cl::cat(SplitCategory));
5555

56+
static cl::opt<bool>
57+
RoundRobin("round-robin", cl::Prefix, cl::init(false),
58+
cl::desc("Use round-robin distribution of functions to "
59+
"modules instead of the default name-hash-based one"),
60+
cl::cat(SplitCategory));
61+
5662
static cl::opt<std::string>
5763
MTriple("mtriple",
5864
cl::desc("Target triple. When present, a TargetMachine is created "
@@ -122,6 +128,9 @@ int main(int argc, char **argv) {
122128
errs() << "warning: -preserve-locals has no effect when using "
123129
"TargetMachine::splitModule\n";
124130
}
131+
if (RoundRobin)
132+
errs() << "warning: -round-robin has no effect when using "
133+
"TargetMachine::splitModule\n";
125134

126135
if (TM->splitModule(*M, NumOutputs, HandleModulePart))
127136
return 0;
@@ -131,6 +140,6 @@ int main(int argc, char **argv) {
131140
"splitModule implementation\n";
132141
}
133142

134-
SplitModule(*M, NumOutputs, HandleModulePart, PreserveLocals);
143+
SplitModule(*M, NumOutputs, HandleModulePart, PreserveLocals, RoundRobin);
135144
return 0;
136145
}

0 commit comments

Comments
 (0)