LLVM 24.0.0git
AMDGPUSplitModule.cpp
Go to the documentation of this file.
1//===- AMDGPUSplitModule.cpp ----------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9/// \file Implements a module splitting algorithm designed to support the
10/// FullLTO --lto-partitions option for parallel codegen.
11///
12/// The role of this module splitting pass is the same as
13/// lib/Transforms/Utils/SplitModule.cpp: load-balance the module's functions
14/// across a set of N partitions to allow for parallel codegen.
15///
16/// The similarities mostly end here, as this pass achieves load-balancing in a
17/// more elaborate fashion which is targeted towards AMDGPU modules. It can take
18/// advantage of the structure of AMDGPU modules (which are mostly
19/// self-contained) to allow for more efficient splitting without affecting
20/// codegen negatively, or causing innaccurate resource usage analysis.
21///
22/// High-level pass overview:
23/// - SplitGraph & associated classes
24/// - Graph representation of the module and of the dependencies that
25/// matter for splitting.
26/// - RecursiveSearchSplitting
27/// - Core splitting algorithm.
28/// - SplitProposal
29/// - Represents a suggested solution for splitting the input module. These
30/// solutions can be scored to determine the best one when multiple
31/// solutions are available.
32/// - Driver/pass "run" function glues everything together.
33
34#include "AMDGPUSplitModule.h"
40#include "llvm/ADT/StringRef.h"
43#include "llvm/IR/Function.h"
44#include "llvm/IR/GlobalAlias.h"
46#include "llvm/IR/Instruction.h"
47#include "llvm/IR/Module.h"
49#include "llvm/IR/Value.h"
53#include "llvm/Support/Debug.h"
55#include "llvm/Support/Path.h"
56#include "llvm/Support/Timer.h"
59#include <cassert>
60#include <cmath>
61#include <utility>
62
63#ifndef NDEBUG
65#endif
66
67#define DEBUG_TYPE "amdgpu-split-module"
68
69namespace llvm {
70namespace {
71
72static cl::opt<unsigned> MaxDepth(
73 "amdgpu-module-splitting-max-depth",
74 cl::desc(
75 "maximum search depth. 0 forces a greedy approach. "
76 "warning: the algorithm is up to O(2^N), where N is the max depth."),
77 cl::init(8));
78
79static cl::opt<float> LargeFnFactor(
80 "amdgpu-module-splitting-large-threshold", cl::init(2.0f), cl::Hidden,
81 cl::desc(
82 "when max depth is reached and we can no longer branch out, this "
83 "value determines if a function is worth merging into an already "
84 "existing partition to reduce code duplication. This is a factor "
85 "of the ideal partition size, e.g. 2.0 means we consider the "
86 "function for merging if its cost (including its callees) is 2x the "
87 "size of an ideal partition."));
88
89static cl::opt<float> LargeFnOverlapForMerge(
90 "amdgpu-module-splitting-merge-threshold", cl::init(0.7f), cl::Hidden,
91 cl::desc("when a function is considered for merging into a partition that "
92 "already contains some of its callees, do the merge if at least "
93 "n% of the code it can reach is already present inside the "
94 "partition; e.g. 0.7 means only merge >70%"));
95
96static cl::opt<bool> NoExternalizeGlobals(
97 "amdgpu-module-splitting-no-externalize-globals", cl::Hidden,
98 cl::desc("disables externalization of global variable with local linkage; "
99 "may cause globals to be duplicated which increases binary size"));
100
101static cl::opt<bool> NoExternalizeOnAddrTaken(
102 "amdgpu-module-splitting-no-externalize-address-taken", cl::Hidden,
103 cl::desc(
104 "disables externalization of functions whose addresses are taken"));
105
107 ModuleDotCfgOutput("amdgpu-module-splitting-print-module-dotcfg",
109 cl::desc("output file to write out the dotgraph "
110 "representation of the input module"));
111
112static cl::opt<std::string> PartitionSummariesOutput(
113 "amdgpu-module-splitting-print-partition-summaries", cl::Hidden,
114 cl::desc("output file to write out a summary of "
115 "the partitions created for each module"));
116
117#ifndef NDEBUG
118static cl::opt<bool>
119 UseLockFile("amdgpu-module-splitting-serial-execution", cl::Hidden,
120 cl::desc("use a lock file so only one process in the system "
121 "can run this pass at once. useful to avoid mangled "
122 "debug output in multithreaded environments."));
123
124static cl::opt<bool>
125 DebugProposalSearch("amdgpu-module-splitting-debug-proposal-search",
127 cl::desc("print all proposals received and whether "
128 "they were rejected or accepted"));
129#endif
130
131struct SplitModuleTimer : NamedRegionTimer {
132 SplitModuleTimer(StringRef Name, StringRef Desc)
133 : NamedRegionTimer(Name, Desc, DEBUG_TYPE, "AMDGPU Module Splitting",
135};
136
137//===----------------------------------------------------------------------===//
138// Utils
139//===----------------------------------------------------------------------===//
140
141using CostType = InstructionCost::CostType;
142using FunctionsCostMap = DenseMap<const Function *, CostType>;
143using GetTTIFn = function_ref<const TargetTransformInfo &(Function &)>;
144static constexpr unsigned InvalidPID = -1;
145
146/// \param Num numerator
147/// \param Dem denominator
148/// \returns a printable object to print (Num/Dem) using "%0.2f".
149static auto formatRatioOf(CostType Num, CostType Dem) {
150 CostType DemOr1 = Dem ? Dem : 1;
151 return format("%0.2f", (static_cast<double>(Num) / DemOr1) * 100);
152}
153
154/// Checks whether a given function is non-copyable.
155///
156/// Non-copyable functions cannot be cloned into multiple partitions, and only
157/// one copy of the function can be present across all partitions.
158///
159/// Kernel functions and external functions fall into this category. If we were
160/// to clone them, we would end up with multiple symbol definitions and a very
161/// unhappy linker.
162static bool isNonCopyable(const Function &F) {
163 return F.hasExternalLinkage() || !F.isDefinitionExact() ||
164 AMDGPU::isEntryFunctionCC(F.getCallingConv());
165}
166
167/// Cost analysis function. Calculates the cost of each function in \p M
168///
169/// \param GetTTI Abstract getter for TargetTransformInfo.
170/// \param M Module to analyze.
171/// \param CostMap[out] Resulting Function -> Cost map.
172/// \return The module's total cost.
173static CostType calculateFunctionCosts(GetTTIFn GetTTI, Module &M,
174 FunctionsCostMap &CostMap) {
175 SplitModuleTimer SMT("calculateFunctionCosts", "cost analysis");
176
177 LLVM_DEBUG(dbgs() << "[cost analysis] calculating function costs\n");
178 CostType ModuleCost = 0;
179 [[maybe_unused]] CostType KernelCost = 0;
180
181 for (auto &Fn : M) {
182 if (Fn.isDeclaration())
183 continue;
184
185 CostType FnCost = 0;
186 const auto &TTI = GetTTI(Fn);
187 for (const auto &BB : Fn) {
188 for (const auto &I : BB) {
189 auto Cost =
192 // Assume expensive if we can't tell the cost of an instruction.
193 CostType CostVal = Cost.isValid()
194 ? Cost.getValue()
196 assert((FnCost + CostVal) >= FnCost && "Overflow!");
197 FnCost += CostVal;
198 }
199 }
200
201 assert(FnCost != 0);
202
203 CostMap[&Fn] = FnCost;
204 assert((ModuleCost + FnCost) >= ModuleCost && "Overflow!");
205 ModuleCost += FnCost;
206
207 if (AMDGPU::isEntryFunctionCC(Fn.getCallingConv()))
208 KernelCost += FnCost;
209 }
210
211 if (CostMap.empty())
212 return 0;
213
214 assert(ModuleCost);
215 LLVM_DEBUG({
216 const CostType FnCost = ModuleCost - KernelCost;
217 dbgs() << " - total module cost is " << ModuleCost << ". kernels cost "
218 << "" << KernelCost << " ("
219 << format("%0.2f", (float(KernelCost) / ModuleCost) * 100)
220 << "% of the module), functions cost " << FnCost << " ("
221 << format("%0.2f", (float(FnCost) / ModuleCost) * 100)
222 << "% of the module)\n";
223 });
224
225 return ModuleCost;
226}
227
228/// \return true if \p F can be indirectly called
229static bool canBeIndirectlyCalled(const Function &F) {
230 if (F.isDeclaration() || AMDGPU::isEntryFunctionCC(F.getCallingConv()))
231 return false;
232 return !F.hasLocalLinkage() ||
233 F.hasAddressTaken(/*PutOffender=*/nullptr,
234 /*IgnoreCallbackUses=*/false,
235 /*IgnoreAssumeLikeCalls=*/true,
236 /*IgnoreLLVMUsed=*/true,
237 /*IgnoreARCAttachedCall=*/false,
238 /*IgnoreCastedDirectCall=*/true);
239}
240
241//===----------------------------------------------------------------------===//
242// Graph-based Module Representation
243//===----------------------------------------------------------------------===//
244
245/// AMDGPUSplitModule's view of the source Module, as a graph of all components
246/// that can be split into different modules.
247///
248/// The most trivial instance of this graph is just the CallGraph of the module,
249/// but it is not guaranteed that the graph is strictly equal to the CG. It
250/// currently always is but it's designed in a way that would eventually allow
251/// us to create abstract nodes, or nodes for different entities such as global
252/// variables or any other meaningful constraint we must consider.
253///
254/// The graph is only mutable by this class, and is generally not modified
255/// after \ref SplitGraph::buildGraph runs. No consumers of the graph can
256/// mutate it.
257class SplitGraph {
258public:
259 class Node;
260
261 enum class EdgeKind : uint8_t {
262 /// The nodes are related through a direct call. This is a "strong" edge as
263 /// it means the Src will directly reference the Dst.
265 /// The nodes are related through an indirect call.
266 /// This is a "weaker" edge and is only considered when traversing the graph
267 /// starting from a kernel. We need this edge for resource usage analysis.
268 ///
269 /// The reason why we have this edge in the first place is due to how
270 /// AMDGPUResourceUsageAnalysis works. In the presence of an indirect call,
271 /// the resource usage of the kernel containing the indirect call is the
272 /// max resource usage of all functions that can be indirectly called.
274 };
275
276 /// An edge between two nodes. Edges are directional, and tagged with a
277 /// "kind".
278 struct Edge {
279 Edge(Node *Src, Node *Dst, EdgeKind Kind)
280 : Src(Src), Dst(Dst), Kind(Kind) {}
281
282 Node *Src; ///< Source
283 Node *Dst; ///< Destination
284 EdgeKind Kind;
285 };
286
287 using EdgesVec = SmallVector<const Edge *, 0>;
288 using edges_iterator = EdgesVec::const_iterator;
289 using nodes_iterator = const Node *const *;
290
291 SplitGraph(const Module &M, const FunctionsCostMap &CostMap,
292 CostType ModuleCost)
293 : M(M), CostMap(CostMap), ModuleCost(ModuleCost) {}
294
295 void buildGraph(CallGraph &CG);
296
297#ifndef NDEBUG
298 bool verifyGraph() const;
299#endif
300
301 bool empty() const { return Nodes.empty(); }
302 iterator_range<nodes_iterator> nodes() const { return Nodes; }
303 const Node &getNode(unsigned ID) const { return *Nodes[ID]; }
304
305 unsigned getNumNodes() const { return Nodes.size(); }
306 BitVector createNodesBitVector() const { return BitVector(Nodes.size()); }
307
308 const Module &getModule() const { return M; }
309
310 CostType getModuleCost() const { return ModuleCost; }
311 CostType getCost(const Function &F) const { return CostMap.at(&F); }
312
313 /// \returns the aggregated cost of all nodes in \p BV (bits set to 1 = node
314 /// IDs).
315 CostType calculateCost(const BitVector &BV) const;
316
317private:
318 /// Retrieves the node for \p GV in \p Cache, or creates a new node for it and
319 /// updates \p Cache.
320 Node &getNode(DenseMap<const GlobalValue *, Node *> &Cache,
321 const GlobalValue &GV);
322
323 // Create a new edge between two nodes and add it to both nodes.
324 const Edge &createEdge(Node &Src, Node &Dst, EdgeKind EK);
325
326 const Module &M;
327 const FunctionsCostMap &CostMap;
328 CostType ModuleCost;
329
330 // Final list of nodes with stable ordering.
332
333 SpecificBumpPtrAllocator<Node> NodesPool;
334
335 // Edges are trivially destructible objects, so as a small optimization we
336 // use a BumpPtrAllocator which avoids destructor calls but also makes
337 // allocation faster.
338 static_assert(
339 std::is_trivially_destructible_v<Edge>,
340 "Edge must be trivially destructible to use the BumpPtrAllocator");
341 BumpPtrAllocator EdgesPool;
342};
343
344/// Nodes in the SplitGraph contain both incoming, and outgoing edges.
345/// Incoming edges have this node as their Dst, and Outgoing ones have this node
346/// as their Src.
347///
348/// Edge objects are shared by both nodes in Src/Dst. They provide immediate
349/// feedback on how two nodes are related, and in which direction they are
350/// related, which is valuable information to make splitting decisions.
351///
352/// Nodes are fundamentally abstract, and any consumers of the graph should
353/// treat them as such. While a node will be a function most of the time, we
354/// could also create nodes for any other reason. In the future, we could have
355/// single nodes for multiple functions, or nodes for GVs, etc.
356class SplitGraph::Node {
357 friend class SplitGraph;
358
359public:
360 Node(unsigned ID, const GlobalValue &GV, CostType IndividualCost,
361 bool IsNonCopyable)
362 : ID(ID), GV(GV), IndividualCost(IndividualCost),
363 IsNonCopyable(IsNonCopyable), IsEntryFnCC(false), IsGraphEntry(false) {
364 if (auto *Fn = dyn_cast<Function>(&GV))
365 IsEntryFnCC = AMDGPU::isEntryFunctionCC(Fn->getCallingConv());
366 }
367
368 /// An 0-indexed ID for the node. The maximum ID (exclusive) is the number of
369 /// nodes in the graph. This ID can be used as an index in a BitVector.
370 unsigned getID() const { return ID; }
371
372 const Function &getFunction() const { return cast<Function>(GV); }
373
374 /// \returns the cost to import this component into a given module, not
375 /// accounting for any dependencies that may need to be imported as well.
376 CostType getIndividualCost() const { return IndividualCost; }
377
378 bool isNonCopyable() const { return IsNonCopyable; }
379 bool isEntryFunctionCC() const { return IsEntryFnCC; }
380
381 /// \returns whether this is an entry point in the graph. Entry points are
382 /// defined as follows: if you take all entry points in the graph, and iterate
383 /// their dependencies, you are guaranteed to visit all nodes in the graph at
384 /// least once.
385 bool isGraphEntryPoint() const { return IsGraphEntry; }
386
387 StringRef getName() const { return GV.getName(); }
388
389 bool hasAnyIncomingEdges() const { return IncomingEdges.size(); }
390 bool hasAnyIncomingEdgesOfKind(EdgeKind EK) const {
391 return any_of(IncomingEdges, [&](const auto *E) { return E->Kind == EK; });
392 }
393
394 iterator_range<edges_iterator> outgoing_edges() const {
395 return OutgoingEdges;
396 }
397
398 bool shouldFollowIndirectCalls() const { return isEntryFunctionCC(); }
399
400 /// Visit all children of this node in a recursive fashion. Also visits Self.
401 /// If \ref shouldFollowIndirectCalls returns false, then this only follows
402 /// DirectCall edges.
403 ///
404 /// \param Visitor Visitor Function.
405 void visitAllDependencies(std::function<void(const Node &)> Visitor) const;
406
407 /// Adds the depedencies of this node in \p BV by setting the bit
408 /// corresponding to each node.
409 ///
410 /// Implemented using \ref visitAllDependencies, hence it follows the same
411 /// rules regarding dependencies traversal.
412 ///
413 /// \param[out] BV The bitvector where the bits should be set.
414 void getDependencies(BitVector &BV) const {
415 visitAllDependencies([&](const Node &N) { BV.set(N.getID()); });
416 }
417
418private:
419 void markAsGraphEntry() { IsGraphEntry = true; }
420
421 unsigned ID;
422 const GlobalValue &GV;
423 CostType IndividualCost;
424 bool IsNonCopyable : 1;
425 bool IsEntryFnCC : 1;
426 bool IsGraphEntry : 1;
427
428 // TODO: Use a single sorted vector (with all incoming/outgoing edges grouped
429 // together)
430 EdgesVec IncomingEdges;
431 EdgesVec OutgoingEdges;
432};
433
434void SplitGraph::Node::visitAllDependencies(
435 std::function<void(const Node &)> Visitor) const {
436 const bool FollowIndirect = shouldFollowIndirectCalls();
437 // FIXME: If this can access SplitGraph in the future, use a BitVector
438 // instead.
439 DenseSet<const Node *> Seen;
440 SmallVector<const Node *, 8> WorkList({this});
441 while (!WorkList.empty()) {
442 const Node *CurN = WorkList.pop_back_val();
443 if (auto [It, Inserted] = Seen.insert(CurN); !Inserted)
444 continue;
445
446 Visitor(*CurN);
447
448 for (const Edge *E : CurN->outgoing_edges()) {
449 if (!FollowIndirect && E->Kind == EdgeKind::IndirectCall)
450 continue;
451 WorkList.push_back(E->Dst);
452 }
453 }
454}
455
456/// Checks if \p I has MD_callees and if it does, parse it and put the function
457/// in \p Callees.
458///
459/// \returns true if there was metadata and it was parsed correctly. false if
460/// there was no MD or if it contained unknown entries and parsing failed.
461/// If this returns false, \p Callees will contain incomplete information
462/// and must not be used.
463static bool handleCalleesMD(const Instruction &I,
464 SetVector<Function *> &Callees) {
465 auto *MD = I.getMetadata(LLVMContext::MD_callees);
466 if (!MD)
467 return false;
468
469 for (const auto &Op : MD->operands()) {
470 Function *Callee = mdconst::extract_or_null<Function>(Op);
471 if (!Callee)
472 return false;
473 Callees.insert(Callee);
474 }
475
476 return true;
477}
478
479void SplitGraph::buildGraph(CallGraph &CG) {
480 SplitModuleTimer SMT("buildGraph", "graph construction");
482 dbgs()
483 << "[build graph] constructing graph representation of the input\n");
484
485 // FIXME(?): Is the callgraph really worth using if we have to iterate the
486 // function again whenever it fails to give us enough information?
487
488 // We build the graph by just iterating all functions in the module and
489 // working on their direct callees. At the end, all nodes should be linked
490 // together as expected.
491 DenseMap<const GlobalValue *, Node *> Cache;
492 SmallVector<const Function *> FnsWithIndirectCalls, IndirectlyCallableFns;
493 for (const Function &Fn : M) {
494 if (Fn.isDeclaration())
495 continue;
496
497 // Look at direct callees and create the necessary edges in the graph.
498 SetVector<const Function *> DirectCallees;
499 bool CallsExternal = false;
500 for (auto &CGEntry : *CG[&Fn]) {
501 auto *CGNode = CGEntry.second;
502 if (auto *Callee = CGNode->getFunction()) {
503 if (!Callee->isDeclaration())
504 DirectCallees.insert(Callee);
505 } else if (CGNode == CG.getCallsExternalNode())
506 CallsExternal = true;
507 }
508
509 // Keep track of this function if it contains an indirect call and/or if it
510 // can be indirectly called.
511 if (CallsExternal) {
512 LLVM_DEBUG(dbgs() << " [!] callgraph is incomplete for ";
513 Fn.printAsOperand(dbgs());
514 dbgs() << " - analyzing function\n");
515
516 SetVector<Function *> KnownCallees;
517 bool HasUnknownIndirectCall = false;
518 for (const auto &Inst : instructions(Fn)) {
519 // look at all calls without a direct callee.
520 const auto *CB = dyn_cast<CallBase>(&Inst);
521 if (!CB || CB->getCalledFunction())
522 continue;
523
524 // inline assembly can be ignored, unless InlineAsmIsIndirectCall is
525 // true.
526 if (CB->isInlineAsm()) {
527 LLVM_DEBUG(dbgs() << " found inline assembly\n");
528 continue;
529 }
530
531 if (handleCalleesMD(Inst, KnownCallees))
532 continue;
533 // If we failed to parse any !callees MD, or some was missing,
534 // the entire KnownCallees list is now unreliable.
535 KnownCallees.clear();
536
537 // Everything else is handled conservatively. If we fall into the
538 // conservative case don't bother analyzing further.
539 HasUnknownIndirectCall = true;
540 break;
541 }
542
543 if (HasUnknownIndirectCall) {
544 LLVM_DEBUG(dbgs() << " indirect call found\n");
545 FnsWithIndirectCalls.push_back(&Fn);
546 } else if (!KnownCallees.empty())
547 DirectCallees.insert_range(KnownCallees);
548 }
549
550 Node &N = getNode(Cache, Fn);
551 for (const auto *Callee : DirectCallees)
552 createEdge(N, getNode(Cache, *Callee), EdgeKind::DirectCall);
553
554 if (canBeIndirectlyCalled(Fn))
555 IndirectlyCallableFns.push_back(&Fn);
556 }
557
558 // Post-process functions with indirect calls.
559 for (const Function *Fn : FnsWithIndirectCalls) {
560 for (const Function *Candidate : IndirectlyCallableFns) {
561 Node &Src = getNode(Cache, *Fn);
562 Node &Dst = getNode(Cache, *Candidate);
563 createEdge(Src, Dst, EdgeKind::IndirectCall);
564 }
565 }
566
567 // Now, find all entry points.
568 SmallVector<Node *, 16> CandidateEntryPoints;
569 BitVector NodesReachableByKernels = createNodesBitVector();
570 for (Node *N : Nodes) {
571 // Functions with an Entry CC are always graph entry points too.
572 if (N->isEntryFunctionCC()) {
573 N->markAsGraphEntry();
574 N->getDependencies(NodesReachableByKernels);
575 } else if (!N->hasAnyIncomingEdgesOfKind(EdgeKind::DirectCall))
576 CandidateEntryPoints.push_back(N);
577 }
578
579 BitVector ReachableNodes = NodesReachableByKernels;
580 for (Node *N : CandidateEntryPoints) {
581 // This can be another entry point if it's not reachable by a kernel
582 // TODO: We could sort all of the possible new entries in a stable order
583 // (e.g. by cost), then consume them one by one until
584 // NodesReachableByKernels is all 1s. It'd allow us to avoid
585 // considering some nodes as non-entries in some specific cases.
586 if (!NodesReachableByKernels.test(N->getID())) {
587 N->markAsGraphEntry();
588 N->getDependencies(ReachableNodes);
589 }
590 }
591
592 // A function in a call cycle always has an incoming DirectCall edge, so it
593 // is never a candidate above. If no entry point reaches a cycle, one of its
594 // functions must become an entry point. Visit the unreached nodes in reverse
595 // post-order of the DirectCall edges, so that callers come before callees.
596 // This way, a function that an unreached cycle calls does not become an
597 // entry point of its own.
598 SmallVector<Node *> PostOrder;
599 BitVector Visited = ReachableNodes;
600 SmallVector<std::pair<Node *, edges_iterator>> Stack;
601 for (Node *Root : Nodes) {
602 if (Visited.test(Root->getID()))
603 continue;
604 Visited.set(Root->getID());
605 Stack.emplace_back(Root, Root->outgoing_edges().begin());
606 while (!Stack.empty()) {
607 auto &[N, It] = Stack.back();
608 if (It == N->outgoing_edges().end()) {
609 PostOrder.push_back(N);
610 Stack.pop_back();
611 continue;
612 }
613 const Edge *E = *It++;
614 if (E->Kind != EdgeKind::DirectCall || Visited.test(E->Dst->getID()))
615 continue;
616 Visited.set(E->Dst->getID());
617 Stack.emplace_back(E->Dst, E->Dst->outgoing_edges().begin());
618 }
619 }
620
621 for (Node *N : reverse(PostOrder)) {
622 if (ReachableNodes.test(N->getID()))
623 continue;
624 N->markAsGraphEntry();
625 N->getDependencies(ReachableNodes);
626 }
627
628#ifndef NDEBUG
629 assert(verifyGraph());
630#endif
631}
632
633#ifndef NDEBUG
634bool SplitGraph::verifyGraph() const {
635 unsigned ExpectedID = 0;
636 // Exceptionally using a set here in case IDs are messed up.
637 DenseSet<const Node *> SeenNodes;
638 DenseSet<const Function *> SeenFunctionNodes;
639 for (const Node *N : Nodes) {
640 if (N->getID() != (ExpectedID++)) {
641 errs() << "Node IDs are incorrect!\n";
642 return false;
643 }
644
645 if (!SeenNodes.insert(N).second) {
646 errs() << "Node seen more than once!\n";
647 return false;
648 }
649
650 if (&getNode(N->getID()) != N) {
651 errs() << "getNode doesn't return the right node\n";
652 return false;
653 }
654
655 for (const Edge *E : N->IncomingEdges) {
656 if (!E->Src || !E->Dst || (E->Dst != N) ||
657 (find(E->Src->OutgoingEdges, E) == E->Src->OutgoingEdges.end())) {
658 errs() << "ill-formed incoming edges\n";
659 return false;
660 }
661 }
662
663 for (const Edge *E : N->OutgoingEdges) {
664 if (!E->Src || !E->Dst || (E->Src != N) ||
665 (find(E->Dst->IncomingEdges, E) == E->Dst->IncomingEdges.end())) {
666 errs() << "ill-formed outgoing edges\n";
667 return false;
668 }
669 }
670
671 const Function &Fn = N->getFunction();
672 if (AMDGPU::isEntryFunctionCC(Fn.getCallingConv())) {
673 if (N->hasAnyIncomingEdges()) {
674 errs() << "Kernels cannot have incoming edges\n";
675 return false;
676 }
677 }
678
679 if (Fn.isDeclaration()) {
680 errs() << "declarations shouldn't have nodes!\n";
681 return false;
682 }
683
684 auto [It, Inserted] = SeenFunctionNodes.insert(&Fn);
685 if (!Inserted) {
686 errs() << "one function has multiple nodes!\n";
687 return false;
688 }
689 }
690
691 if (ExpectedID != Nodes.size()) {
692 errs() << "Node IDs out of sync!\n";
693 return false;
694 }
695
696 if (createNodesBitVector().size() != getNumNodes()) {
697 errs() << "nodes bit vector doesn't have the right size!\n";
698 return false;
699 }
700
701 // Check we respect the promise of Node::isKernel
702 BitVector BV = createNodesBitVector();
703 for (const Node *N : nodes()) {
704 if (N->isGraphEntryPoint())
705 N->getDependencies(BV);
706 }
707
708 // Ensure each function in the module has an associated node.
709 for (const auto &Fn : M) {
710 if (!Fn.isDeclaration()) {
711 if (!SeenFunctionNodes.contains(&Fn)) {
712 errs() << "Fn has no associated node in the graph!\n";
713 return false;
714 }
715 }
716 }
717
718 if (!BV.all()) {
719 errs() << "not all nodes are reachable through the graph's entry points!\n";
720 return false;
721 }
722
723 return true;
724}
725#endif
726
727CostType SplitGraph::calculateCost(const BitVector &BV) const {
728 CostType Cost = 0;
729 for (unsigned NodeID : BV.set_bits())
730 Cost += getNode(NodeID).getIndividualCost();
731 return Cost;
732}
733
734SplitGraph::Node &
735SplitGraph::getNode(DenseMap<const GlobalValue *, Node *> &Cache,
736 const GlobalValue &GV) {
737 auto &N = Cache[&GV];
738 if (N)
739 return *N;
740
741 CostType Cost = 0;
742 bool NonCopyable = false;
743 if (const Function *Fn = dyn_cast<Function>(&GV)) {
744 NonCopyable = isNonCopyable(*Fn);
745 Cost = CostMap.at(Fn);
746 }
747 N = new (NodesPool.Allocate()) Node(Nodes.size(), GV, Cost, NonCopyable);
748 Nodes.push_back(N);
749 assert(&getNode(N->getID()) == N);
750 return *N;
751}
752
753const SplitGraph::Edge &SplitGraph::createEdge(Node &Src, Node &Dst,
754 EdgeKind EK) {
755 const Edge *E = new (EdgesPool.Allocate<Edge>(1)) Edge(&Src, &Dst, EK);
756 Src.OutgoingEdges.push_back(E);
757 Dst.IncomingEdges.push_back(E);
758 return *E;
759}
760
761//===----------------------------------------------------------------------===//
762// Split Proposals
763//===----------------------------------------------------------------------===//
764
765/// Represents a module splitting proposal.
766///
767/// Proposals are made of N BitVectors, one for each partition, where each bit
768/// set indicates that the node is present and should be copied inside that
769/// partition.
770///
771/// Proposals have several metrics attached so they can be compared/sorted,
772/// which the driver to try multiple strategies resultings in multiple proposals
773/// and choose the best one out of them.
774class SplitProposal {
775public:
776 SplitProposal(const SplitGraph &SG, unsigned MaxPartitions) : SG(&SG) {
777 Partitions.resize(MaxPartitions, {0, SG.createNodesBitVector()});
778 }
779
780 void setName(StringRef NewName) { Name = NewName; }
781 StringRef getName() const { return Name; }
782
783 const BitVector &operator[](unsigned PID) const {
784 return Partitions[PID].second;
785 }
786
787 void add(unsigned PID, const BitVector &BV) {
788 Partitions[PID].second |= BV;
789 updateScore(PID);
790 }
791
792 void print(raw_ostream &OS) const;
793 LLVM_DUMP_METHOD void dump() const { print(dbgs()); }
794
795 // Find the cheapest partition (lowest cost). In case of ties, always returns
796 // the highest partition number.
797 unsigned findCheapestPartition() const;
798
799 /// Calculate the CodeSize and Bottleneck scores.
800 void calculateScores();
801
802#ifndef NDEBUG
803 void verifyCompleteness() const;
804#endif
805
806 /// Only available after \ref calculateScores is called.
807 ///
808 /// A positive number indicating the % of code duplication that this proposal
809 /// creates. e.g. 0.2 means this proposal adds roughly 20% code size by
810 /// duplicating some functions across partitions.
811 ///
812 /// Value is always rounded up to 3 decimal places.
813 ///
814 /// A perfect score would be 0.0, and anything approaching 1.0 is very bad.
815 double getCodeSizeScore() const { return CodeSizeScore; }
816
817 /// Only available after \ref calculateScores is called.
818 ///
819 /// A number between [0, 1] which indicates how big of a bottleneck is
820 /// expected from the largest partition.
821 ///
822 /// A score of 1.0 means the biggest partition is as big as the source module,
823 /// so build time will be equal to or greater than the build time of the
824 /// initial input.
825 ///
826 /// Value is always rounded up to 3 decimal places.
827 ///
828 /// This is one of the metrics used to estimate this proposal's build time.
829 double getBottleneckScore() const { return BottleneckScore; }
830
831private:
832 void updateScore(unsigned PID) {
833 assert(SG);
834 for (auto &[PCost, Nodes] : Partitions) {
835 TotalCost -= PCost;
836 PCost = SG->calculateCost(Nodes);
837 TotalCost += PCost;
838 }
839 }
840
841 /// \see getCodeSizeScore
842 double CodeSizeScore = 0.0;
843 /// \see getBottleneckScore
844 double BottleneckScore = 0.0;
845 /// Aggregated cost of all partitions
846 CostType TotalCost = 0;
847
848 const SplitGraph *SG = nullptr;
849 std::string Name;
850
851 std::vector<std::pair<CostType, BitVector>> Partitions;
852};
853
854void SplitProposal::print(raw_ostream &OS) const {
855 assert(SG);
856
857 OS << "[proposal] " << Name << ", total cost:" << TotalCost
858 << ", code size score:" << format("%0.3f", CodeSizeScore)
859 << ", bottleneck score:" << format("%0.3f", BottleneckScore) << '\n';
860 for (const auto &[PID, Part] : enumerate(Partitions)) {
861 const auto &[Cost, NodeIDs] = Part;
862 OS << " - P" << PID << " nodes:" << NodeIDs.count() << " cost: " << Cost
863 << '|' << formatRatioOf(Cost, SG->getModuleCost()) << "%\n";
864 }
865}
866
867unsigned SplitProposal::findCheapestPartition() const {
868 assert(!Partitions.empty());
869 CostType CurCost = std::numeric_limits<CostType>::max();
870 unsigned CurPID = InvalidPID;
871 for (const auto &[Idx, Part] : enumerate(Partitions)) {
872 if (Part.first <= CurCost) {
873 CurPID = Idx;
874 CurCost = Part.first;
875 }
876 }
877 assert(CurPID != InvalidPID);
878 return CurPID;
879}
880
881void SplitProposal::calculateScores() {
882 if (Partitions.empty())
883 return;
884
885 assert(SG);
886 CostType LargestPCost = 0;
887 for (auto &[PCost, Nodes] : Partitions) {
888 if (PCost > LargestPCost)
889 LargestPCost = PCost;
890 }
891
892 CostType ModuleCost = SG->getModuleCost();
893 CodeSizeScore = double(TotalCost) / ModuleCost;
894 assert(CodeSizeScore >= 0.0);
895
896 BottleneckScore = double(LargestPCost) / ModuleCost;
897
898 CodeSizeScore = std::ceil(CodeSizeScore * 100.0) / 100.0;
899 BottleneckScore = std::ceil(BottleneckScore * 100.0) / 100.0;
900}
901
902#ifndef NDEBUG
903void SplitProposal::verifyCompleteness() const {
904 if (Partitions.empty())
905 return;
906
907 BitVector Result = Partitions[0].second;
908 for (const auto &P : drop_begin(Partitions))
909 Result |= P.second;
910 assert(Result.all() && "some nodes are missing from this proposal!");
911}
912#endif
913
914//===-- RecursiveSearchStrategy -------------------------------------------===//
915
916/// Partitioning algorithm.
917///
918/// This is a recursive search algorithm that can explore multiple possiblities.
919///
920/// When a cluster of nodes can go into more than one partition, and we haven't
921/// reached maximum search depth, we recurse and explore both options and their
922/// consequences. Both branches will yield a proposal, and the driver will grade
923/// both and choose the best one.
924///
925/// If max depth is reached, we will use some heuristics to make a choice. Most
926/// of the time we will just use the least-pressured (cheapest) partition, but
927/// if a cluster is particularly big and there is a good amount of overlap with
928/// an existing partition, we will choose that partition instead.
929class RecursiveSearchSplitting {
930public:
931 using SubmitProposalFn = function_ref<void(SplitProposal)>;
932
933 RecursiveSearchSplitting(const SplitGraph &SG, unsigned NumParts,
934 SubmitProposalFn SubmitProposal);
935
936 void run();
937
938private:
939 struct WorkListEntry {
940 WorkListEntry(const BitVector &BV) : Cluster(BV) {}
941
942 unsigned NumNonEntryNodes = 0;
943 CostType TotalCost = 0;
944 CostType CostExcludingGraphEntryPoints = 0;
945 BitVector Cluster;
946 };
947
948 /// Collects all graph entry points's clusters and sort them so the most
949 /// expensive clusters are viewed first. This will merge clusters together if
950 /// they share a non-copyable dependency.
951 void setupWorkList();
952
953 /// Recursive function that assigns the worklist item at \p Idx into a
954 /// partition of \p SP.
955 ///
956 /// \p Depth is the current search depth. When this value is equal to
957 /// \ref MaxDepth, we can no longer recurse.
958 ///
959 /// This function only recurses if there is more than one possible assignment,
960 /// otherwise it is iterative to avoid creating a call stack that is as big as
961 /// \ref WorkList.
962 void pickPartition(unsigned Depth, unsigned Idx, SplitProposal SP);
963
964 /// \return A pair: first element is the PID of the partition that has the
965 /// most similarities with \p Entry, or \ref InvalidPID if no partition was
966 /// found with at least one element in common. The second element is the
967 /// aggregated cost of all dependencies in common between \p Entry and that
968 /// partition.
969 std::pair<unsigned, CostType>
970 findMostSimilarPartition(const WorkListEntry &Entry, const SplitProposal &SP);
971
972 const SplitGraph &SG;
973 unsigned NumParts;
974 SubmitProposalFn SubmitProposal;
975
976 // A Cluster is considered large when its cost, excluding entry points,
977 // exceeds this value.
978 CostType LargeClusterThreshold = 0;
979 unsigned NumProposalsSubmitted = 0;
980 SmallVector<WorkListEntry> WorkList;
981};
982
983RecursiveSearchSplitting::RecursiveSearchSplitting(
984 const SplitGraph &SG, unsigned NumParts, SubmitProposalFn SubmitProposal)
985 : SG(SG), NumParts(NumParts), SubmitProposal(SubmitProposal) {
986 // arbitrary max value as a safeguard. Anything above 10 will already be
987 // slow, this is just a max value to prevent extreme resource exhaustion or
988 // unbounded run time.
989 if (MaxDepth > 16)
990 report_fatal_error("[amdgpu-split-module] search depth of " +
991 Twine(MaxDepth) + " is too high!");
992 LargeClusterThreshold =
993 (LargeFnFactor != 0.0)
994 ? CostType(((SG.getModuleCost() / NumParts) * LargeFnFactor))
995 : std::numeric_limits<CostType>::max();
996 LLVM_DEBUG(dbgs() << "[recursive search] large cluster threshold set at "
997 << LargeClusterThreshold << "\n");
998}
999
1000void RecursiveSearchSplitting::run() {
1001 {
1002 SplitModuleTimer SMT("recursive_search_prepare", "preparing worklist");
1003 setupWorkList();
1004 }
1005
1006 {
1007 SplitModuleTimer SMT("recursive_search_pick", "partitioning");
1008 SplitProposal SP(SG, NumParts);
1009 pickPartition(/*BranchDepth=*/0, /*Idx=*/0, std::move(SP));
1010 }
1011}
1012
1013void RecursiveSearchSplitting::setupWorkList() {
1014 // e.g. if A and B are two worklist item, and they both call a non copyable
1015 // dependency C, this does:
1016 // A=C
1017 // B=C
1018 // => NodeEC will create a single group (A, B, C) and we create a new
1019 // WorkList entry for that group.
1020
1021 EquivalenceClasses<unsigned> NodeEC;
1022 for (const SplitGraph::Node *N : SG.nodes()) {
1023 if (!N->isGraphEntryPoint())
1024 continue;
1025
1026 NodeEC.insert(N->getID());
1027 N->visitAllDependencies([&](const SplitGraph::Node &Dep) {
1028 if (&Dep != N && Dep.isNonCopyable())
1029 NodeEC.unionSets(N->getID(), Dep.getID());
1030 });
1031 }
1032
1033 for (const auto &Node : NodeEC) {
1034 if (!Node->isLeader())
1035 continue;
1036
1037 BitVector Cluster = SG.createNodesBitVector();
1038 for (unsigned M : NodeEC.members(*Node)) {
1039 const SplitGraph::Node &N = SG.getNode(M);
1040 if (N.isGraphEntryPoint())
1041 N.getDependencies(Cluster);
1042 }
1043 WorkList.emplace_back(std::move(Cluster));
1044 }
1045
1046 // Calculate costs and other useful information.
1047 for (WorkListEntry &Entry : WorkList) {
1048 for (unsigned NodeID : Entry.Cluster.set_bits()) {
1049 const SplitGraph::Node &N = SG.getNode(NodeID);
1050 const CostType Cost = N.getIndividualCost();
1051
1052 Entry.TotalCost += Cost;
1053 if (!N.isGraphEntryPoint()) {
1054 Entry.CostExcludingGraphEntryPoints += Cost;
1055 ++Entry.NumNonEntryNodes;
1056 }
1057 }
1058 }
1059
1060 stable_sort(WorkList, [](const WorkListEntry &A, const WorkListEntry &B) {
1061 if (A.TotalCost != B.TotalCost)
1062 return A.TotalCost > B.TotalCost;
1063
1064 if (A.CostExcludingGraphEntryPoints != B.CostExcludingGraphEntryPoints)
1065 return A.CostExcludingGraphEntryPoints > B.CostExcludingGraphEntryPoints;
1066
1067 if (A.NumNonEntryNodes != B.NumNonEntryNodes)
1068 return A.NumNonEntryNodes > B.NumNonEntryNodes;
1069
1070 return A.Cluster.count() > B.Cluster.count();
1071 });
1072
1073 LLVM_DEBUG({
1074 dbgs() << "[recursive search] worklist:\n";
1075 for (const auto &[Idx, Entry] : enumerate(WorkList)) {
1076 dbgs() << " - [" << Idx << "]: ";
1077 for (unsigned NodeID : Entry.Cluster.set_bits())
1078 dbgs() << NodeID << " ";
1079 dbgs() << "(total_cost:" << Entry.TotalCost
1080 << ", cost_excl_entries:" << Entry.CostExcludingGraphEntryPoints
1081 << ")\n";
1082 }
1083 });
1084}
1085
1086void RecursiveSearchSplitting::pickPartition(unsigned Depth, unsigned Idx,
1087 SplitProposal SP) {
1088 while (Idx < WorkList.size()) {
1089 // Step 1: Determine candidate PIDs.
1090 //
1091 const WorkListEntry &Entry = WorkList[Idx];
1092 const BitVector &Cluster = Entry.Cluster;
1093
1094 // Default option is to do load-balancing, AKA assign to least pressured
1095 // partition.
1096 const unsigned CheapestPID = SP.findCheapestPartition();
1097 assert(CheapestPID != InvalidPID);
1098
1099 // Explore assigning to the kernel that contains the most dependencies in
1100 // common.
1101 const auto [MostSimilarPID, SimilarDepsCost] =
1102 findMostSimilarPartition(Entry, SP);
1103
1104 // We can chose to explore only one path if we only have one valid path, or
1105 // if we reached maximum search depth and can no longer branch out.
1106 unsigned SinglePIDToTry = InvalidPID;
1107 if (MostSimilarPID == InvalidPID) // no similar PID found
1108 SinglePIDToTry = CheapestPID;
1109 else if (MostSimilarPID == CheapestPID) // both landed on the same PID
1110 SinglePIDToTry = CheapestPID;
1111 else if (Depth >= MaxDepth) {
1112 // We have to choose one path. Use a heuristic to guess which one will be
1113 // more appropriate.
1114 if (Entry.CostExcludingGraphEntryPoints > LargeClusterThreshold) {
1115 // Check if the amount of code in common makes it worth it.
1116 assert(SimilarDepsCost && Entry.CostExcludingGraphEntryPoints);
1117 const double Ratio = static_cast<double>(SimilarDepsCost) /
1118 Entry.CostExcludingGraphEntryPoints;
1119 assert(Ratio >= 0.0 && Ratio <= 1.0);
1120 if (Ratio > LargeFnOverlapForMerge) {
1121 // For debug, just print "L", so we'll see "L3=P3" for instance, which
1122 // will mean we reached max depth and chose P3 based on this
1123 // heuristic.
1124 LLVM_DEBUG(dbgs() << 'L');
1125 SinglePIDToTry = MostSimilarPID;
1126 }
1127 } else
1128 SinglePIDToTry = CheapestPID;
1129 }
1130
1131 // Step 2: Explore candidates.
1132
1133 // When we only explore one possible path, and thus branch depth doesn't
1134 // increase, do not recurse, iterate instead.
1135 if (SinglePIDToTry != InvalidPID) {
1136 LLVM_DEBUG(dbgs() << Idx << "=P" << SinglePIDToTry << ' ');
1137 // Only one path to explore, don't clone SP, don't increase depth.
1138 SP.add(SinglePIDToTry, Cluster);
1139 ++Idx;
1140 continue;
1141 }
1142
1143 assert(MostSimilarPID != InvalidPID);
1144
1145 // We explore multiple paths: recurse at increased depth, then stop this
1146 // function.
1147
1148 LLVM_DEBUG(dbgs() << '\n');
1149
1150 // lb = load balancing = put in cheapest partition
1151 {
1152 SplitProposal BranchSP = SP;
1153 LLVM_DEBUG(dbgs().indent(Depth)
1154 << " [lb] " << Idx << "=P" << CheapestPID << "? ");
1155 BranchSP.add(CheapestPID, Cluster);
1156 pickPartition(Depth + 1, Idx + 1, std::move(BranchSP));
1157 }
1158
1159 // ms = most similar = put in partition with the most in common
1160 {
1161 SplitProposal BranchSP = SP;
1162 LLVM_DEBUG(dbgs().indent(Depth)
1163 << " [ms] " << Idx << "=P" << MostSimilarPID << "? ");
1164 BranchSP.add(MostSimilarPID, Cluster);
1165 pickPartition(Depth + 1, Idx + 1, std::move(BranchSP));
1166 }
1167
1168 return;
1169 }
1170
1171 // Step 3: If we assigned all WorkList items, submit the proposal.
1172
1173 assert(Idx == WorkList.size());
1174 assert(NumProposalsSubmitted <= (2u << MaxDepth) &&
1175 "Search got out of bounds?");
1176 SP.setName("recursive_search (depth=" + std::to_string(Depth) + ") #" +
1177 std::to_string(NumProposalsSubmitted++));
1178 LLVM_DEBUG(dbgs() << '\n');
1179 SubmitProposal(std::move(SP));
1180}
1181
1182std::pair<unsigned, CostType>
1183RecursiveSearchSplitting::findMostSimilarPartition(const WorkListEntry &Entry,
1184 const SplitProposal &SP) {
1185 if (!Entry.NumNonEntryNodes)
1186 return {InvalidPID, 0};
1187
1188 // We take the partition that is the most similar using Cost as a metric.
1189 // So we take the set of nodes in common, compute their aggregated cost, and
1190 // pick the partition with the highest cost in common.
1191 unsigned ChosenPID = InvalidPID;
1192 CostType ChosenCost = 0;
1193 for (unsigned PID = 0; PID < NumParts; ++PID) {
1194 BitVector BV = SP[PID];
1195 BV &= Entry.Cluster; // FIXME: & doesn't work between BVs?!
1196
1197 if (BV.none())
1198 continue;
1199
1200 const CostType Cost = SG.calculateCost(BV);
1201
1202 if (ChosenPID == InvalidPID || ChosenCost < Cost ||
1203 (ChosenCost == Cost && PID > ChosenPID)) {
1204 ChosenPID = PID;
1205 ChosenCost = Cost;
1206 }
1207 }
1208
1209 return {ChosenPID, ChosenCost};
1210}
1211
1212//===----------------------------------------------------------------------===//
1213// DOTGraph Printing Support
1214//===----------------------------------------------------------------------===//
1215
1216const SplitGraph::Node *mapEdgeToDst(const SplitGraph::Edge *E) {
1217 return E->Dst;
1218}
1219
1220using SplitGraphEdgeDstIterator =
1221 mapped_iterator<SplitGraph::edges_iterator, decltype(&mapEdgeToDst)>;
1222
1223} // namespace
1224
1225template <> struct GraphTraits<SplitGraph> {
1226 using NodeRef = const SplitGraph::Node *;
1227 using nodes_iterator = SplitGraph::nodes_iterator;
1228 using ChildIteratorType = SplitGraphEdgeDstIterator;
1229
1230 using EdgeRef = const SplitGraph::Edge *;
1231 using ChildEdgeIteratorType = SplitGraph::edges_iterator;
1232
1233 static NodeRef getEntryNode(NodeRef N) { return N; }
1234
1236 return {Ref->outgoing_edges().begin(), mapEdgeToDst};
1237 }
1239 return {Ref->outgoing_edges().end(), mapEdgeToDst};
1240 }
1241
1242 static nodes_iterator nodes_begin(const SplitGraph &G) {
1243 return G.nodes().begin();
1244 }
1245 static nodes_iterator nodes_end(const SplitGraph &G) {
1246 return G.nodes().end();
1247 }
1248};
1249
1250template <> struct DOTGraphTraits<SplitGraph> : public DefaultDOTGraphTraits {
1251 DOTGraphTraits(bool IsSimple = false) : DefaultDOTGraphTraits(IsSimple) {}
1252
1253 static std::string getGraphName(const SplitGraph &SG) {
1254 return SG.getModule().getName().str();
1255 }
1256
1257 std::string getNodeLabel(const SplitGraph::Node *N, const SplitGraph &SG) {
1258 return N->getName().str();
1259 }
1260
1261 static std::string getNodeDescription(const SplitGraph::Node *N,
1262 const SplitGraph &SG) {
1263 std::string Result;
1264 if (N->isEntryFunctionCC())
1265 Result += "entry-fn-cc ";
1266 if (N->isNonCopyable())
1267 Result += "non-copyable ";
1268 Result += "cost:" + std::to_string(N->getIndividualCost());
1269 return Result;
1270 }
1271
1272 static std::string getNodeAttributes(const SplitGraph::Node *N,
1273 const SplitGraph &SG) {
1274 return N->hasAnyIncomingEdges() ? "" : "color=\"red\"";
1275 }
1276
1277 static std::string getEdgeAttributes(const SplitGraph::Node *N,
1278 SplitGraphEdgeDstIterator EI,
1279 const SplitGraph &SG) {
1280
1281 switch ((*EI.getCurrent())->Kind) {
1282 case SplitGraph::EdgeKind::DirectCall:
1283 return "";
1284 case SplitGraph::EdgeKind::IndirectCall:
1285 return "style=\"dashed\"";
1286 }
1287 llvm_unreachable("Unknown SplitGraph::EdgeKind enum");
1288 }
1289};
1290
1291//===----------------------------------------------------------------------===//
1292// Driver
1293//===----------------------------------------------------------------------===//
1294
1295namespace {
1296
1297// If we didn't externalize GVs, then local GVs need to be conservatively
1298// imported into every module (including their initializers), and then cleaned
1299// up afterwards.
1300static bool needsConservativeImport(const GlobalValue *GV) {
1301 if (const auto *Var = dyn_cast<GlobalVariable>(GV))
1302 return Var->hasLocalLinkage();
1303 if (const auto *GA = dyn_cast<GlobalAlias>(GV))
1304 return GA->hasLocalLinkage();
1305 return false;
1306}
1307
1308/// Prints a summary of the partition \p N, represented by module \p M, to \p
1309/// OS.
1310static void printPartitionSummary(raw_ostream &OS, unsigned N, const Module &M,
1311 unsigned PartCost, unsigned ModuleCost) {
1312 OS << "*** Partition P" << N << " ***\n";
1313
1314 for (const auto &Fn : M) {
1315 if (!Fn.isDeclaration())
1316 OS << " - [function] " << Fn.getName() << "\n";
1317 }
1318
1319 for (const auto &GV : M.globals()) {
1320 if (GV.hasInitializer())
1321 OS << " - [global] " << GV.getName() << "\n";
1322 }
1323
1324 OS << "Partition contains " << formatRatioOf(PartCost, ModuleCost)
1325 << "% of the source\n";
1326}
1327
1328static void evaluateProposal(SplitProposal &Best, SplitProposal New) {
1329 SplitModuleTimer SMT("proposal_evaluation", "proposal ranking algorithm");
1330
1331 LLVM_DEBUG({
1332 New.verifyCompleteness();
1333 if (DebugProposalSearch)
1334 New.print(dbgs());
1335 });
1336
1337 const double CurBScore = Best.getBottleneckScore();
1338 const double CurCSScore = Best.getCodeSizeScore();
1339 const double NewBScore = New.getBottleneckScore();
1340 const double NewCSScore = New.getCodeSizeScore();
1341
1342 // TODO: Improve this
1343 // We can probably lower the precision of the comparison at first
1344 // e.g. if we have
1345 // - (Current): BScore: 0.489 CSCore 1.105
1346 // - (New): BScore: 0.475 CSCore 1.305
1347 // Currently we'd choose the new one because the bottleneck score is
1348 // lower, but the new one duplicates more code. It may be worth it to
1349 // discard the new proposal as the impact on build time is negligible.
1350
1351 // Compare them
1352 bool IsBest = false;
1353 if (NewBScore < CurBScore)
1354 IsBest = true;
1355 else if (NewBScore == CurBScore)
1356 IsBest = (NewCSScore < CurCSScore); // Use code size as tie breaker.
1357
1358 if (IsBest)
1359 Best = std::move(New);
1360
1361 LLVM_DEBUG(if (DebugProposalSearch) {
1362 if (IsBest)
1363 dbgs() << "[search] new best proposal!\n";
1364 else
1365 dbgs() << "[search] discarding - not profitable\n";
1366 });
1367}
1368
1369/// Trivial helper to create an identical copy of \p M.
1370static std::unique_ptr<Module> cloneAll(const Module &M) {
1371 ValueToValueMapTy VMap;
1372 return CloneModule(M, VMap, [&](const GlobalValue *GV) { return true; });
1373}
1374
1375/// Writes \p SG as a DOTGraph to \ref ModuleDotCfgDir if requested.
1376static void writeDOTGraph(const SplitGraph &SG) {
1377 if (ModuleDotCfgOutput.empty())
1378 return;
1379
1380 std::error_code EC;
1381 raw_fd_ostream OS(ModuleDotCfgOutput, EC);
1382 if (EC) {
1383 errs() << "[" DEBUG_TYPE "]: cannot open '" << ModuleDotCfgOutput
1384 << "' - DOTGraph will not be printed\n";
1385 }
1386 WriteGraph(OS, SG, /*ShortName=*/false,
1387 /*Title=*/SG.getModule().getName());
1388}
1389
1390static void splitAMDGPUModule(
1391 GetTTIFn GetTTI, Module &M, unsigned NumParts,
1392 function_ref<void(std::unique_ptr<Module> MPart)> ModuleCallback) {
1393 CallGraph CG(M);
1394
1395 // Externalize functions whose address are taken.
1396 //
1397 // This is needed because partitioning is purely based on calls, but sometimes
1398 // a kernel/function may just look at the address of another local function
1399 // and not do anything (no calls). After partitioning, that local function may
1400 // end up in a different module (so it's just a declaration in the module
1401 // where its address is taken), which emits a "undefined hidden symbol" linker
1402 // error.
1403 //
1404 // Additionally, it guides partitioning to not duplicate this function if it's
1405 // called directly at some point.
1406 //
1407 // TODO: Could we be smarter about this ? This makes all functions whose
1408 // addresses are taken non-copyable. We should probably model this type of
1409 // constraint in the graph and use it to guide splitting, instead of
1410 // externalizing like this. Maybe non-copyable should really mean "keep one
1411 // visible copy, then internalize all other copies" for some functions?
1412 if (!NoExternalizeOnAddrTaken) {
1413 for (auto &Fn : M) {
1414 if (Fn.hasLocalLinkage() && Fn.hasAddressTaken()) {
1415 LLVM_DEBUG(dbgs() << "[externalize] "; Fn.printAsOperand(dbgs());
1416 dbgs() << " because its address is taken\n");
1417 Fn.externalize();
1418 }
1419 }
1420 }
1421
1422 // Externalize local GVs, which avoids duplicating their initializers, which
1423 // in turns helps keep code size in check.
1424 if (!NoExternalizeGlobals) {
1425 for (auto &GV : M.globals()) {
1426 if (GV.hasLocalLinkage())
1427 LLVM_DEBUG(dbgs() << "[externalize] GV " << GV.getName() << '\n');
1428 GV.externalize();
1429 }
1430 }
1431
1432 for (auto &GA : M.aliases()) {
1433 if (GA.hasLocalLinkage()) {
1434 LLVM_DEBUG(dbgs() << "[externalize] alias " << GA.getName() << '\n');
1435 GA.externalize();
1436 }
1437 }
1438
1439 // Start by calculating the cost of every function in the module, as well as
1440 // the module's overall cost.
1441 FunctionsCostMap FnCosts;
1442 const CostType ModuleCost = calculateFunctionCosts(GetTTI, M, FnCosts);
1443
1444 // Build the SplitGraph, which represents the module's functions and models
1445 // their dependencies accurately.
1446 SplitGraph SG(M, FnCosts, ModuleCost);
1447 SG.buildGraph(CG);
1448
1449 if (SG.empty()) {
1450 LLVM_DEBUG(
1451 dbgs()
1452 << "[!] no nodes in graph, input is empty - no splitting possible\n");
1453 ModuleCallback(cloneAll(M));
1454 return;
1455 }
1456
1457 LLVM_DEBUG({
1458 dbgs() << "[graph] nodes:\n";
1459 for (const SplitGraph::Node *N : SG.nodes()) {
1460 dbgs() << " - [" << N->getID() << "]: " << N->getName() << " "
1461 << (N->isGraphEntryPoint() ? "(entry)" : "") << " "
1462 << (N->isNonCopyable() ? "(noncopyable)" : "") << "\n";
1463 }
1464 });
1465
1466 writeDOTGraph(SG);
1467
1468 LLVM_DEBUG(dbgs() << "[search] testing splitting strategies\n");
1469
1470 std::optional<SplitProposal> Proposal;
1471 const auto EvaluateProposal = [&](SplitProposal SP) {
1472 SP.calculateScores();
1473 if (!Proposal)
1474 Proposal = std::move(SP);
1475 else
1476 evaluateProposal(*Proposal, std::move(SP));
1477 };
1478
1479 // TODO: It would be very easy to create new strategies by just adding a base
1480 // class to RecursiveSearchSplitting and abstracting it away.
1481 RecursiveSearchSplitting(SG, NumParts, EvaluateProposal).run();
1482 LLVM_DEBUG(if (Proposal) dbgs() << "[search done] selected proposal: "
1483 << Proposal->getName() << "\n";);
1484
1485 if (!Proposal) {
1486 LLVM_DEBUG(dbgs() << "[!] no proposal made, no splitting possible!\n");
1487 ModuleCallback(cloneAll(M));
1488 return;
1489 }
1490
1491 LLVM_DEBUG(Proposal->print(dbgs()););
1492
1493 std::optional<raw_fd_ostream> SummariesOS;
1494 if (!PartitionSummariesOutput.empty()) {
1495 std::error_code EC;
1496 SummariesOS.emplace(PartitionSummariesOutput, EC);
1497 if (EC)
1498 errs() << "[" DEBUG_TYPE "]: cannot open '" << PartitionSummariesOutput
1499 << "' - Partition summaries will not be printed\n";
1500 }
1501
1502 // One module will import all GlobalValues that are not Functions
1503 // and are not subject to conservative import.
1504 bool ImportAllGVs = true;
1505
1506 for (unsigned PID = 0; PID < NumParts; ++PID) {
1507 SplitModuleTimer SMT2("modules_creation",
1508 "creating modules for each partition");
1509 LLVM_DEBUG(dbgs() << "[split] creating new modules\n");
1510
1511 DenseSet<const Function *> FnsInPart;
1512 for (unsigned NodeID : (*Proposal)[PID].set_bits())
1513 FnsInPart.insert(&SG.getNode(NodeID).getFunction());
1514
1515 // Don't create empty modules.
1516 if (FnsInPart.empty()) {
1517 LLVM_DEBUG(dbgs() << "[split] P" << PID
1518 << " is empty, not creating module\n");
1519 continue;
1520 }
1521
1522 ValueToValueMapTy VMap;
1523 CostType PartCost = 0;
1524 std::unique_ptr<Module> MPart(
1525 CloneModule(M, VMap, [&](const GlobalValue *GV) {
1526 // Functions go in their assigned partition.
1527 if (const auto *Fn = dyn_cast<Function>(GV)) {
1528 if (FnsInPart.contains(Fn)) {
1529 PartCost += SG.getCost(*Fn);
1530 return true;
1531 }
1532 return false;
1533 }
1534
1535 // Aliases should not be separated from their underlying object.
1536 if (const auto *GA = dyn_cast<GlobalAlias>(GV)) {
1537 if (const auto *Fn = dyn_cast<Function>(GA->getAliaseeObject()))
1538 return FnsInPart.contains(Fn);
1539 }
1540
1541 // Everything else goes in the first non-empty module we create.
1542 return ImportAllGVs || needsConservativeImport(GV);
1543 }));
1544
1545 ImportAllGVs = false;
1546
1547 // Clean-up conservatively imported GVs without any users.
1548 for (auto &GV : make_early_inc_range(MPart->global_values())) {
1549 if (needsConservativeImport(&GV) && GV.use_empty())
1550 GV.eraseFromParent();
1551 }
1552
1553 if (SummariesOS)
1554 printPartitionSummary(*SummariesOS, PID, *MPart, PartCost, ModuleCost);
1555
1556 LLVM_DEBUG(
1557 printPartitionSummary(dbgs(), PID, *MPart, PartCost, ModuleCost));
1558
1559 ModuleCallback(std::move(MPart));
1560 }
1561}
1562} // namespace
1563
1566 SplitModuleTimer SMT(
1567 "total", "total pass runtime (incl. potentially waiting for lockfile)");
1568
1570 MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
1571 const auto TTIGetter = [&FAM](Function &F) -> const TargetTransformInfo & {
1572 return FAM.getResult<TargetIRAnalysis>(F);
1573 };
1574
1575 bool Done = false;
1576#ifndef NDEBUG
1577 if (UseLockFile) {
1578 SmallString<128> LockFilePath;
1579 sys::path::system_temp_directory(/*ErasedOnReboot=*/true, LockFilePath);
1580 sys::path::append(LockFilePath, "amdgpu-split-module-debug");
1581 LLVM_DEBUG(dbgs() << DEBUG_TYPE " using lockfile '" << LockFilePath
1582 << "'\n");
1583
1584 while (true) {
1585 llvm::LockFileManager Lock(LockFilePath.str());
1586 bool Owned;
1587 if (Error Err = Lock.tryLock().moveInto(Owned)) {
1588 consumeError(std::move(Err));
1589 LLVM_DEBUG(
1590 dbgs() << "[amdgpu-split-module] unable to acquire lockfile, debug "
1591 "output may be mangled by other processes\n");
1592 } else if (!Owned) {
1593 switch (Lock.waitForUnlockFor(std::chrono::seconds(90))) {
1595 break;
1597 continue; // try again to get the lock.
1599 LLVM_DEBUG(
1600 dbgs()
1601 << "[amdgpu-split-module] unable to acquire lockfile, debug "
1602 "output may be mangled by other processes\n");
1603 Lock.unsafeUnlock();
1604 break; // give up
1605 }
1606 }
1607
1608 splitAMDGPUModule(TTIGetter, M, N, ModuleCallback);
1609 Done = true;
1610 break;
1611 }
1612 }
1613#endif
1614
1615 if (!Done)
1616 splitAMDGPUModule(TTIGetter, M, N, ModuleCallback);
1617
1618 // We can change linkage/visibilities in the input, consider that nothing is
1619 // preserved just to be safe. This pass runs last anyway.
1620 return PreservedAnalyses::none();
1621}
1622} // namespace llvm
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static msgpack::DocNode getNode(msgpack::DocNode DN, msgpack::Type Type, MCValue Val)
function_ref< const TargetTransformInfo *(Function &)> GetTTIFn
Unify divergent function exit nodes
This file defines the BumpPtrAllocator interface.
static void print(raw_ostream &Out, object::Archive::Kind Kind, T Val)
Expand Atomic instructions
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
This file provides interfaces used to build and manipulate a call graph, which is a very useful tool ...
#define LLVM_DUMP_METHOD
Mark debug helper function definitions like dump() that should not be stripped from debug builds.
Definition Compiler.h:686
static InstructionCost getCost(Instruction &Inst, TTI::TargetCostKind CostKind, TargetTransformInfo &TTI)
Definition CostModel.cpp:73
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
Generic implementation of equivalence classes through the use Tarjan's efficient union-find algorithm...
#define DEBUG_TYPE
This file defines the little GraphTraits<X> template class that should be specialized by classes that...
Module.h This file contains the declarations for the Module class.
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
#define G(x, y, z)
Definition MD5.cpp:55
Machine Check Debug Module
#define P(N)
FunctionAnalysisManager FAM
ModuleAnalysisManager MAM
This header defines classes/functions to handle pass execution timing information with interfaces for...
static StringRef getName(Value *V)
std::pair< BasicBlock *, BasicBlock * > Edge
This file defines the SmallVector class.
This file contains some functions that are useful when dealing with strings.
#define LLVM_DEBUG(...)
Definition Debug.h:119
static const BasicSubtargetSubTypeKV * find(StringRef S, ArrayRef< BasicSubtargetSubTypeKV > A)
Find KV in array using binary search.
This pass exposes codegen information to IR-level passes.
static Function * getFunction(FunctionType *Ty, const Twine &Name, Module *M)
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM)
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static InstructionCost getMax()
Class that manages the creation of a lock file to aid implicit coordination between different process...
std::error_code unsafeUnlock() override
Remove the lock file.
WaitForUnlockResult waitForUnlockFor(std::chrono::seconds MaxSeconds) override
For a shared lock, wait until the owner releases the lock.
Expected< bool > tryLock() override
Tries to acquire the lock without blocking.
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:68
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
Definition Analysis.h:115
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:26
StringRef str() const
Explicit conversion to StringRef.
Analysis pass providing the TargetTransformInfo.
This pass provides access to the codegen interfaces that are needed for IR-level transformations.
@ TCK_CodeSize
Instruction code size.
@ TCC_Expensive
The cost of a 'div' instruction on x86.
LLVM_ABI InstructionCost getInstructionCost(const User *U, ArrayRef< const Value * > Operands, TargetCostKind CostKind) const
Estimate the cost of a given IR user when lowered.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
LLVM_READNONE constexpr bool isEntryFunctionCC(CallingConv::ID CC)
template class LLVM_TEMPLATE_ABI opt< bool >
template class LLVM_TEMPLATE_ABI opt< unsigned >
initializer< Ty > init(const Ty &Val)
template class LLVM_TEMPLATE_ABI opt< std::string >
LLVM_ABI void system_temp_directory(bool erasedOnReboot, SmallVectorImpl< char > &result)
Get the typical temporary directory for the system, e.g., "/var/tmp" or "C:/TEMP".
LLVM_ABI void append(SmallVectorImpl< char > &path, const Twine &a, const Twine &b="", const Twine &c="", const Twine &d="")
Append to path.
Definition Path.cpp:467
This is an optimization pass for GlobalISel generic memory operations.
@ Done
Definition Threading.h:60
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
Definition STLExtras.h:649
InnerAnalysisManagerProxy< FunctionAnalysisManager, Module > FunctionAnalysisManagerModuleProxy
Provide the FunctionAnalysisManager to Module proxy.
LLVM_ABI bool TimePassesIsEnabled
If the user specifies the -time-passes argument on an LLVM tool command line then the value of this b...
raw_ostream & WriteGraph(raw_ostream &O, const GraphType &G, bool ShortNames=false, const Twine &Title="")
Op::Description Desc
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1762
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
format_object< Ts... > format(const char *Fmt, const Ts &... Vals)
These are helper functions used to produce formatted output.
Definition Format.h:102
@ Success
The lock was released successfully.
@ OwnerDied
Owner died while holding the lock.
@ Timeout
Reached timeout while waiting for the owner to release the lock.
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
iterator_range(Container &&) -> iterator_range< llvm::detail::IterOfRange< Container > >
@ Ref
The access may reference the value stored in memory.
Definition ModRef.h:32
TargetTransformInfo TTI
ValueMap< const Value *, WeakTrackingVH > ValueToValueMapTy
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.
BumpPtrAllocatorImpl<> BumpPtrAllocator
The standard BumpPtrAllocator which just uses the default template parameters.
Definition Allocator.h:391
LLVM_ABI std::unique_ptr< Module > CloneModule(const Module &M)
Return an exact copy of the specified module.
void consumeError(Error Err)
Consume a Error without doing anything.
Definition Error.h:1106
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
Definition MIRParser.h:39
#define N
static std::string getEdgeAttributes(const SplitGraph::Node *N, SplitGraphEdgeDstIterator EI, const SplitGraph &SG)
static std::string getGraphName(const SplitGraph &SG)
static std::string getNodeAttributes(const SplitGraph::Node *N, const SplitGraph &SG)
static std::string getNodeDescription(const SplitGraph::Node *N, const SplitGraph &SG)
std::string getNodeLabel(const SplitGraph::Node *N, const SplitGraph &SG)
DefaultDOTGraphTraits(bool simple=false)
static NodeRef getEntryNode(NodeRef N)
SplitGraph::nodes_iterator nodes_iterator
SplitGraph::edges_iterator ChildEdgeIteratorType
SplitGraphEdgeDstIterator ChildIteratorType
static nodes_iterator nodes_end(const SplitGraph &G)
static ChildIteratorType child_begin(NodeRef Ref)
static nodes_iterator nodes_begin(const SplitGraph &G)
static ChildIteratorType child_end(NodeRef Ref)