67#define DEBUG_TYPE "amdgpu-split-module"
73 "amdgpu-module-splitting-max-depth",
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."),
79static cl::opt<float> LargeFnFactor(
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."));
89static cl::opt<float> LargeFnOverlapForMerge(
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%"));
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"));
102 "amdgpu-module-splitting-no-externalize-address-taken",
cl::Hidden,
104 "disables externalization of functions whose addresses are taken"));
107 ModuleDotCfgOutput(
"amdgpu-module-splitting-print-module-dotcfg",
109 cl::desc(
"output file to write out the dotgraph "
110 "representation of the input module"));
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"));
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."));
125 DebugProposalSearch(
"amdgpu-module-splitting-debug-proposal-search",
127 cl::desc(
"print all proposals received and whether "
128 "they were rejected or accepted"));
131struct SplitModuleTimer : NamedRegionTimer {
132 SplitModuleTimer(StringRef Name, StringRef
Desc)
133 : NamedRegionTimer(Name,
Desc,
DEBUG_TYPE,
"AMDGPU Module Splitting",
142using FunctionsCostMap = DenseMap<const Function *, CostType>;
144static constexpr unsigned InvalidPID = -1;
149static auto formatRatioOf(CostType Num, CostType Dem) {
150 CostType DemOr1 = Dem ? Dem : 1;
151 return format(
"%0.2f", (
static_cast<double>(Num) / DemOr1) * 100);
162static bool isNonCopyable(
const Function &
F) {
163 return F.hasExternalLinkage() || !
F.isDefinitionExact() ||
174 FunctionsCostMap &CostMap) {
175 SplitModuleTimer SMT(
"calculateFunctionCosts",
"cost analysis");
177 LLVM_DEBUG(
dbgs() <<
"[cost analysis] calculating function costs\n");
178 CostType ModuleCost = 0;
179 [[maybe_unused]] CostType KernelCost = 0;
182 if (Fn.isDeclaration())
186 const auto &
TTI = GetTTI(Fn);
187 for (
const auto &BB : Fn) {
188 for (
const auto &
I : BB) {
193 CostType CostVal = Cost.isValid()
196 assert((FnCost + CostVal) >= FnCost &&
"Overflow!");
203 CostMap[&Fn] = FnCost;
204 assert((ModuleCost + FnCost) >= ModuleCost &&
"Overflow!");
205 ModuleCost += FnCost;
208 KernelCost += FnCost;
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";
229static bool canBeIndirectlyCalled(
const Function &
F) {
232 return !
F.hasLocalLinkage() ||
233 F.hasAddressTaken(
nullptr,
261 enum class EdgeKind :
uint8_t {
280 : Src(Src), Dst(Dst), Kind(Kind) {}
287 using EdgesVec = SmallVector<const Edge *, 0>;
289 using nodes_iterator =
const Node *
const *;
291 SplitGraph(
const Module &M,
const FunctionsCostMap &CostMap,
293 : M(M), CostMap(CostMap), ModuleCost(ModuleCost) {}
295 void buildGraph(CallGraph &CG);
298 bool verifyGraph()
const;
301 bool empty()
const {
return Nodes.empty(); }
303 const Node &
getNode(
unsigned ID)
const {
return *Nodes[ID]; }
305 unsigned getNumNodes()
const {
return Nodes.size(); }
306 BitVector createNodesBitVector()
const {
return BitVector(Nodes.size()); }
308 const Module &getModule()
const {
return M; }
310 CostType getModuleCost()
const {
return ModuleCost; }
315 CostType calculateCost(
const BitVector &BV)
const;
320 Node &
getNode(DenseMap<const GlobalValue *, Node *> &Cache,
321 const GlobalValue &GV);
324 const Edge &createEdge(
Node &Src,
Node &Dst, EdgeKind EK);
327 const FunctionsCostMap &CostMap;
333 SpecificBumpPtrAllocator<Node> NodesPool;
339 std::is_trivially_destructible_v<Edge>,
340 "Edge must be trivially destructible to use the BumpPtrAllocator");
356class SplitGraph::Node {
357 friend class SplitGraph;
360 Node(
unsigned ID,
const GlobalValue &GV, CostType IndividualCost,
362 : ID(ID), GV(GV), IndividualCost(IndividualCost),
363 IsNonCopyable(IsNonCopyable), IsEntryFnCC(
false), IsGraphEntry(
false) {
370 unsigned getID()
const {
return ID; }
376 CostType getIndividualCost()
const {
return IndividualCost; }
378 bool isNonCopyable()
const {
return IsNonCopyable; }
379 bool isEntryFunctionCC()
const {
return IsEntryFnCC; }
385 bool isGraphEntryPoint()
const {
return IsGraphEntry; }
387 StringRef
getName()
const {
return GV.getName(); }
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; });
395 return OutgoingEdges;
398 bool shouldFollowIndirectCalls()
const {
return isEntryFunctionCC(); }
405 void visitAllDependencies(std::function<
void(
const Node &)> Visitor)
const;
414 void getDependencies(BitVector &BV)
const {
415 visitAllDependencies([&](
const Node &
N) { BV.set(
N.getID()); });
419 void markAsGraphEntry() { IsGraphEntry =
true; }
422 const GlobalValue &GV;
423 CostType IndividualCost;
424 bool IsNonCopyable : 1;
425 bool IsEntryFnCC : 1;
426 bool IsGraphEntry : 1;
430 EdgesVec IncomingEdges;
431 EdgesVec OutgoingEdges;
434void SplitGraph::Node::visitAllDependencies(
435 std::function<
void(
const Node &)> Visitor)
const {
436 const bool FollowIndirect = shouldFollowIndirectCalls();
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)
448 for (
const Edge *
E : CurN->outgoing_edges()) {
449 if (!FollowIndirect &&
E->Kind == EdgeKind::IndirectCall)
451 WorkList.push_back(
E->Dst);
463static bool handleCalleesMD(
const Instruction &
I,
464 SetVector<Function *> &Callees) {
465 auto *MD =
I.getMetadata(LLVMContext::MD_callees);
469 for (
const auto &Op : MD->operands()) {
470 Function *Callee = mdconst::extract_or_null<Function>(Op);
473 Callees.insert(Callee);
479void SplitGraph::buildGraph(CallGraph &CG) {
480 SplitModuleTimer SMT(
"buildGraph",
"graph construction");
483 <<
"[build graph] constructing graph representation of the input\n");
491 DenseMap<const GlobalValue *, Node *> Cache;
492 SmallVector<const Function *> FnsWithIndirectCalls, IndirectlyCallableFns;
493 for (
const Function &Fn : M) {
494 if (Fn.isDeclaration())
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;
512 LLVM_DEBUG(dbgs() <<
" [!] callgraph is incomplete for ";
513 Fn.printAsOperand(dbgs());
514 dbgs() <<
" - analyzing function\n");
516 SetVector<Function *> KnownCallees;
517 bool HasUnknownIndirectCall =
false;
521 if (!CB || CB->getCalledFunction())
526 if (CB->isInlineAsm()) {
527 LLVM_DEBUG(dbgs() <<
" found inline assembly\n");
531 if (handleCalleesMD(Inst, KnownCallees))
535 KnownCallees.clear();
539 HasUnknownIndirectCall =
true;
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);
551 for (
const auto *Callee : DirectCallees)
552 createEdge(
N,
getNode(Cache, *Callee), EdgeKind::DirectCall);
554 if (canBeIndirectlyCalled(Fn))
555 IndirectlyCallableFns.push_back(&Fn);
559 for (
const Function *Fn : FnsWithIndirectCalls) {
560 for (
const Function *Candidate : IndirectlyCallableFns) {
563 createEdge(Src, Dst, EdgeKind::IndirectCall);
568 SmallVector<Node *, 16> CandidateEntryPoints;
569 BitVector NodesReachableByKernels = createNodesBitVector();
570 for (
Node *
N : Nodes) {
572 if (
N->isEntryFunctionCC()) {
573 N->markAsGraphEntry();
574 N->getDependencies(NodesReachableByKernels);
575 }
else if (!
N->hasAnyIncomingEdgesOfKind(EdgeKind::DirectCall))
576 CandidateEntryPoints.push_back(
N);
579 BitVector ReachableNodes = NodesReachableByKernels;
580 for (
Node *
N : CandidateEntryPoints) {
586 if (!NodesReachableByKernels.test(
N->getID())) {
587 N->markAsGraphEntry();
588 N->getDependencies(ReachableNodes);
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()))
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);
613 const Edge *
E = *It++;
614 if (
E->Kind != EdgeKind::DirectCall || Visited.test(
E->Dst->getID()))
616 Visited.set(
E->Dst->getID());
617 Stack.emplace_back(
E->Dst,
E->Dst->outgoing_edges().begin());
621 for (
Node *
N : reverse(PostOrder)) {
622 if (ReachableNodes.test(
N->getID()))
624 N->markAsGraphEntry();
625 N->getDependencies(ReachableNodes);
634bool SplitGraph::verifyGraph()
const {
635 unsigned ExpectedID = 0;
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";
645 if (!SeenNodes.insert(
N).second) {
646 errs() <<
"Node seen more than once!\n";
651 errs() <<
"getNode doesn't return the right node\n";
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";
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";
671 const Function &Fn =
N->getFunction();
672 if (AMDGPU::isEntryFunctionCC(Fn.getCallingConv())) {
673 if (
N->hasAnyIncomingEdges()) {
674 errs() <<
"Kernels cannot have incoming edges\n";
679 if (Fn.isDeclaration()) {
680 errs() <<
"declarations shouldn't have nodes!\n";
684 auto [It, Inserted] = SeenFunctionNodes.insert(&Fn);
686 errs() <<
"one function has multiple nodes!\n";
691 if (ExpectedID != Nodes.size()) {
692 errs() <<
"Node IDs out of sync!\n";
696 if (createNodesBitVector().size() != getNumNodes()) {
697 errs() <<
"nodes bit vector doesn't have the right size!\n";
702 BitVector BV = createNodesBitVector();
704 if (
N->isGraphEntryPoint())
705 N->getDependencies(BV);
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";
719 errs() <<
"not all nodes are reachable through the graph's entry points!\n";
727CostType SplitGraph::calculateCost(
const BitVector &BV)
const {
729 for (
unsigned NodeID : BV.set_bits())
730 Cost +=
getNode(NodeID).getIndividualCost();
735SplitGraph::getNode(DenseMap<const GlobalValue *, Node *> &Cache,
736 const GlobalValue &GV) {
737 auto &
N = Cache[&GV];
742 bool NonCopyable =
false;
744 NonCopyable = isNonCopyable(*Fn);
745 Cost = CostMap.at(Fn);
747 N =
new (NodesPool.Allocate())
Node(Nodes.size(), GV, Cost, NonCopyable);
753const SplitGraph::Edge &SplitGraph::createEdge(
Node &Src,
Node &Dst,
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);
776 SplitProposal(
const SplitGraph &SG,
unsigned MaxPartitions) : SG(&SG) {
777 Partitions.resize(MaxPartitions, {0, SG.createNodesBitVector()});
780 void setName(StringRef NewName) { Name = NewName; }
781 StringRef
getName()
const {
return Name; }
783 const BitVector &operator[](
unsigned PID)
const {
784 return Partitions[PID].second;
787 void add(
unsigned PID,
const BitVector &BV) {
788 Partitions[PID].second |= BV;
792 void print(raw_ostream &OS)
const;
797 unsigned findCheapestPartition()
const;
800 void calculateScores();
803 void verifyCompleteness()
const;
815 double getCodeSizeScore()
const {
return CodeSizeScore; }
829 double getBottleneckScore()
const {
return BottleneckScore; }
832 void updateScore(
unsigned PID) {
834 for (
auto &[PCost, Nodes] : Partitions) {
836 PCost = SG->calculateCost(Nodes);
842 double CodeSizeScore = 0.0;
844 double BottleneckScore = 0.0;
846 CostType TotalCost = 0;
848 const SplitGraph *SG =
nullptr;
851 std::vector<std::pair<CostType, BitVector>> Partitions;
854void SplitProposal::print(raw_ostream &OS)
const {
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";
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) {
874 CurCost = Part.first;
877 assert(CurPID != InvalidPID);
881void SplitProposal::calculateScores() {
882 if (Partitions.empty())
886 CostType LargestPCost = 0;
887 for (
auto &[PCost, Nodes] : Partitions) {
888 if (PCost > LargestPCost)
889 LargestPCost = PCost;
892 CostType ModuleCost = SG->getModuleCost();
893 CodeSizeScore = double(TotalCost) / ModuleCost;
894 assert(CodeSizeScore >= 0.0);
896 BottleneckScore = double(LargestPCost) / ModuleCost;
898 CodeSizeScore = std::ceil(CodeSizeScore * 100.0) / 100.0;
899 BottleneckScore = std::ceil(BottleneckScore * 100.0) / 100.0;
903void SplitProposal::verifyCompleteness()
const {
904 if (Partitions.empty())
907 BitVector Result = Partitions[0].second;
908 for (
const auto &
P : drop_begin(Partitions))
910 assert(Result.all() &&
"some nodes are missing from this proposal!");
929class RecursiveSearchSplitting {
931 using SubmitProposalFn = function_ref<void(SplitProposal)>;
933 RecursiveSearchSplitting(
const SplitGraph &SG,
unsigned NumParts,
934 SubmitProposalFn SubmitProposal);
939 struct WorkListEntry {
940 WorkListEntry(
const BitVector &BV) : Cluster(BV) {}
942 unsigned NumNonEntryNodes = 0;
943 CostType TotalCost = 0;
944 CostType CostExcludingGraphEntryPoints = 0;
951 void setupWorkList();
962 void pickPartition(
unsigned Depth,
unsigned Idx, SplitProposal SP);
969 std::pair<unsigned, CostType>
970 findMostSimilarPartition(
const WorkListEntry &Entry,
const SplitProposal &SP);
972 const SplitGraph &SG;
974 SubmitProposalFn SubmitProposal;
978 CostType LargeClusterThreshold = 0;
979 unsigned NumProposalsSubmitted = 0;
980 SmallVector<WorkListEntry> WorkList;
983RecursiveSearchSplitting::RecursiveSearchSplitting(
984 const SplitGraph &SG,
unsigned NumParts, SubmitProposalFn SubmitProposal)
985 : SG(SG), NumParts(NumParts), SubmitProposal(SubmitProposal) {
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");
1000void RecursiveSearchSplitting::run() {
1002 SplitModuleTimer SMT(
"recursive_search_prepare",
"preparing worklist");
1007 SplitModuleTimer SMT(
"recursive_search_pick",
"partitioning");
1008 SplitProposal SP(SG, NumParts);
1009 pickPartition(0, 0, std::move(SP));
1013void RecursiveSearchSplitting::setupWorkList() {
1021 EquivalenceClasses<unsigned> NodeEC;
1022 for (
const SplitGraph::Node *
N : SG.nodes()) {
1023 if (!
N->isGraphEntryPoint())
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());
1033 for (
const auto &
Node : NodeEC) {
1034 if (!
Node->isLeader())
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);
1043 WorkList.emplace_back(std::move(Cluster));
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();
1052 Entry.TotalCost += Cost;
1053 if (!
N.isGraphEntryPoint()) {
1054 Entry.CostExcludingGraphEntryPoints += Cost;
1055 ++Entry.NumNonEntryNodes;
1060 stable_sort(WorkList, [](
const WorkListEntry &
A,
const WorkListEntry &
B) {
1061 if (
A.TotalCost !=
B.TotalCost)
1062 return A.TotalCost >
B.TotalCost;
1064 if (
A.CostExcludingGraphEntryPoints !=
B.CostExcludingGraphEntryPoints)
1065 return A.CostExcludingGraphEntryPoints >
B.CostExcludingGraphEntryPoints;
1067 if (
A.NumNonEntryNodes !=
B.NumNonEntryNodes)
1068 return A.NumNonEntryNodes >
B.NumNonEntryNodes;
1070 return A.Cluster.count() >
B.Cluster.count();
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
1086void RecursiveSearchSplitting::pickPartition(
unsigned Depth,
unsigned Idx,
1088 while (Idx < WorkList.size()) {
1091 const WorkListEntry &Entry = WorkList[Idx];
1092 const BitVector &Cluster = Entry.Cluster;
1096 const unsigned CheapestPID = SP.findCheapestPartition();
1097 assert(CheapestPID != InvalidPID);
1101 const auto [MostSimilarPID, SimilarDepsCost] =
1102 findMostSimilarPartition(Entry, SP);
1106 unsigned SinglePIDToTry = InvalidPID;
1107 if (MostSimilarPID == InvalidPID)
1108 SinglePIDToTry = CheapestPID;
1109 else if (MostSimilarPID == CheapestPID)
1110 SinglePIDToTry = CheapestPID;
1111 else if (Depth >= MaxDepth) {
1114 if (Entry.CostExcludingGraphEntryPoints > LargeClusterThreshold) {
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) {
1125 SinglePIDToTry = MostSimilarPID;
1128 SinglePIDToTry = CheapestPID;
1135 if (SinglePIDToTry != InvalidPID) {
1136 LLVM_DEBUG(dbgs() << Idx <<
"=P" << SinglePIDToTry <<
' ');
1138 SP.add(SinglePIDToTry, Cluster);
1143 assert(MostSimilarPID != InvalidPID);
1152 SplitProposal BranchSP = SP;
1154 <<
" [lb] " << Idx <<
"=P" << CheapestPID <<
"? ");
1155 BranchSP.add(CheapestPID, Cluster);
1156 pickPartition(Depth + 1, Idx + 1, std::move(BranchSP));
1161 SplitProposal BranchSP = SP;
1163 <<
" [ms] " << Idx <<
"=P" << MostSimilarPID <<
"? ");
1164 BranchSP.add(MostSimilarPID, Cluster);
1165 pickPartition(Depth + 1, Idx + 1, std::move(BranchSP));
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++));
1179 SubmitProposal(std::move(SP));
1182std::pair<unsigned, CostType>
1183RecursiveSearchSplitting::findMostSimilarPartition(
const WorkListEntry &Entry,
1184 const SplitProposal &SP) {
1185 if (!Entry.NumNonEntryNodes)
1186 return {InvalidPID, 0};
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;
1200 const CostType Cost = SG.calculateCost(BV);
1202 if (ChosenPID == InvalidPID || ChosenCost < Cost ||
1203 (ChosenCost == Cost && PID > ChosenPID)) {
1209 return {ChosenPID, ChosenCost};
1216const SplitGraph::Node *mapEdgeToDst(
const SplitGraph::Edge *
E) {
1220using SplitGraphEdgeDstIterator =
1221 mapped_iterator<SplitGraph::edges_iterator,
decltype(&mapEdgeToDst)>;
1236 return {
Ref->outgoing_edges().begin(), mapEdgeToDst};
1239 return {
Ref->outgoing_edges().end(), mapEdgeToDst};
1243 return G.nodes().begin();
1246 return G.nodes().end();
1254 return SG.getModule().getName().str();
1258 return N->getName().str();
1262 const SplitGraph &SG) {
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());
1273 const SplitGraph &SG) {
1274 return N->hasAnyIncomingEdges() ?
"" :
"color=\"red\"";
1278 SplitGraphEdgeDstIterator EI,
1279 const SplitGraph &SG) {
1281 switch ((*EI.getCurrent())->Kind) {
1282 case SplitGraph::EdgeKind::DirectCall:
1284 case SplitGraph::EdgeKind::IndirectCall:
1285 return "style=\"dashed\"";
1300static bool needsConservativeImport(
const GlobalValue *GV) {
1302 return Var->hasLocalLinkage();
1304 return GA->hasLocalLinkage();
1310static void printPartitionSummary(raw_ostream &OS,
unsigned N,
const Module &M,
1311 unsigned PartCost,
unsigned ModuleCost) {
1312 OS <<
"*** Partition P" <<
N <<
" ***\n";
1314 for (
const auto &Fn : M) {
1315 if (!Fn.isDeclaration())
1316 OS <<
" - [function] " << Fn.getName() <<
"\n";
1319 for (
const auto &GV :
M.globals()) {
1320 if (GV.hasInitializer())
1321 OS <<
" - [global] " << GV.getName() <<
"\n";
1324 OS <<
"Partition contains " << formatRatioOf(PartCost, ModuleCost)
1325 <<
"% of the source\n";
1328static void evaluateProposal(SplitProposal &Best, SplitProposal New) {
1329 SplitModuleTimer SMT(
"proposal_evaluation",
"proposal ranking algorithm");
1332 New.verifyCompleteness();
1333 if (DebugProposalSearch)
1337 const double CurBScore = Best.getBottleneckScore();
1338 const double CurCSScore = Best.getCodeSizeScore();
1339 const double NewBScore =
New.getBottleneckScore();
1340 const double NewCSScore =
New.getCodeSizeScore();
1352 bool IsBest =
false;
1353 if (NewBScore < CurBScore)
1355 else if (NewBScore == CurBScore)
1356 IsBest = (NewCSScore < CurCSScore);
1359 Best = std::move(New);
1363 dbgs() <<
"[search] new best proposal!\n";
1365 dbgs() <<
"[search] discarding - not profitable\n";
1370static std::unique_ptr<Module> cloneAll(
const Module &M) {
1372 return CloneModule(M, VMap, [&](
const GlobalValue *GV) {
return true; });
1376static void writeDOTGraph(
const SplitGraph &SG) {
1377 if (ModuleDotCfgOutput.empty())
1381 raw_fd_ostream OS(ModuleDotCfgOutput, EC);
1383 errs() <<
"[" DEBUG_TYPE "]: cannot open '" << ModuleDotCfgOutput
1384 <<
"' - DOTGraph will not be printed\n";
1387 SG.getModule().getName());
1390static void splitAMDGPUModule(
1392 function_ref<
void(std::unique_ptr<Module> MPart)> ModuleCallback) {
1412 if (!NoExternalizeOnAddrTaken) {
1413 for (
auto &Fn : M) {
1414 if (Fn.hasLocalLinkage() && Fn.hasAddressTaken()) {
1416 dbgs() <<
" because its address is taken\n");
1424 if (!NoExternalizeGlobals) {
1425 for (
auto &GV :
M.globals()) {
1426 if (GV.hasLocalLinkage())
1427 LLVM_DEBUG(
dbgs() <<
"[externalize] GV " << GV.getName() <<
'\n');
1432 for (
auto &GA :
M.aliases()) {
1433 if (GA.hasLocalLinkage()) {
1434 LLVM_DEBUG(
dbgs() <<
"[externalize] alias " << GA.getName() <<
'\n');
1441 FunctionsCostMap FnCosts;
1442 const CostType ModuleCost = calculateFunctionCosts(GetTTI, M, FnCosts);
1446 SplitGraph SG(M, FnCosts, ModuleCost);
1452 <<
"[!] no nodes in graph, input is empty - no splitting possible\n");
1453 ModuleCallback(cloneAll(M));
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";
1470 std::optional<SplitProposal> Proposal;
1471 const auto EvaluateProposal = [&](SplitProposal
SP) {
1472 SP.calculateScores();
1474 Proposal = std::move(SP);
1476 evaluateProposal(*Proposal, std::move(SP));
1481 RecursiveSearchSplitting(SG, NumParts, EvaluateProposal).run();
1482 LLVM_DEBUG(
if (Proposal)
dbgs() <<
"[search done] selected proposal: "
1483 << Proposal->getName() <<
"\n";);
1486 LLVM_DEBUG(
dbgs() <<
"[!] no proposal made, no splitting possible!\n");
1487 ModuleCallback(cloneAll(M));
1493 std::optional<raw_fd_ostream> SummariesOS;
1494 if (!PartitionSummariesOutput.empty()) {
1496 SummariesOS.emplace(PartitionSummariesOutput, EC);
1498 errs() <<
"[" DEBUG_TYPE "]: cannot open '" << PartitionSummariesOutput
1499 <<
"' - Partition summaries will not be printed\n";
1504 bool ImportAllGVs =
true;
1506 for (
unsigned PID = 0; PID < NumParts; ++PID) {
1507 SplitModuleTimer SMT2(
"modules_creation",
1508 "creating modules for each partition");
1511 DenseSet<const Function *> FnsInPart;
1512 for (
unsigned NodeID : (*Proposal)[PID].set_bits())
1513 FnsInPart.insert(&SG.getNode(NodeID).getFunction());
1516 if (FnsInPart.empty()) {
1518 <<
" is empty, not creating module\n");
1523 CostType PartCost = 0;
1524 std::unique_ptr<Module> MPart(
1528 if (FnsInPart.contains(Fn)) {
1529 PartCost += SG.getCost(*Fn);
1538 return FnsInPart.contains(Fn);
1542 return ImportAllGVs || needsConservativeImport(GV);
1545 ImportAllGVs =
false;
1549 if (needsConservativeImport(&GV) && GV.use_empty())
1550 GV.eraseFromParent();
1554 printPartitionSummary(*SummariesOS, PID, *MPart, PartCost, ModuleCost);
1557 printPartitionSummary(
dbgs(), PID, *MPart, PartCost, ModuleCost));
1559 ModuleCallback(std::move(MPart));
1566 SplitModuleTimer SMT(
1567 "total",
"total pass runtime (incl. potentially waiting for lockfile)");
1590 dbgs() <<
"[amdgpu-split-module] unable to acquire lockfile, debug "
1591 "output may be mangled by other processes\n");
1592 }
else if (!Owned) {
1601 <<
"[amdgpu-split-module] unable to acquire lockfile, debug "
1602 "output may be mangled by other processes\n");
1608 splitAMDGPUModule(TTIGetter, M, N, ModuleCallback);
1616 splitAMDGPUModule(TTIGetter, M, N, ModuleCallback);
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.
static InstructionCost getCost(Instruction &Inst, TTI::TargetCostKind CostKind, TargetTransformInfo &TTI)
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Generic implementation of equivalence classes through the use Tarjan's efficient union-find algorithm...
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.
Machine Check Debug Module
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.
static const BasicSubtargetSubTypeKV * find(StringRef S, ArrayRef< BasicSubtargetSubTypeKV > A)
Find KV in array using binary search.
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.
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.
A set of analyses that are preserved following a run of a transformation pass.
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
StringRef str() const
Explicit conversion to StringRef.
Analysis pass providing the TargetTransformInfo.
#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.
This is an optimization pass for GlobalISel generic memory operations.
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...
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="")
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
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.
@ 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.
ValueMap< const Value *, WeakTrackingVH > ValueToValueMapTy
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.
BumpPtrAllocatorImpl<> BumpPtrAllocator
The standard BumpPtrAllocator which just uses the default template parameters.
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.
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
static std::string getEdgeAttributes(const SplitGraph::Node *N, SplitGraphEdgeDstIterator EI, const SplitGraph &SG)
static std::string getGraphName(const SplitGraph &SG)
DOTGraphTraits(bool IsSimple=false)
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)
const SplitGraph::Edge * EdgeRef
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)
const SplitGraph::Node * NodeRef
static ChildIteratorType child_end(NodeRef Ref)