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 for (
Node *
N : CandidateEntryPoints) {
585 if (!NodesReachableByKernels.test(
N->getID()))
586 N->markAsGraphEntry();
595bool SplitGraph::verifyGraph()
const {
596 unsigned ExpectedID = 0;
598 DenseSet<const Node *> SeenNodes;
599 DenseSet<const Function *> SeenFunctionNodes;
600 for (
const Node *
N : Nodes) {
601 if (
N->getID() != (ExpectedID++)) {
602 errs() <<
"Node IDs are incorrect!\n";
606 if (!SeenNodes.insert(
N).second) {
607 errs() <<
"Node seen more than once!\n";
612 errs() <<
"getNode doesn't return the right node\n";
616 for (
const Edge *
E :
N->IncomingEdges) {
617 if (!
E->Src || !
E->Dst || (
E->Dst !=
N) ||
618 (
find(
E->Src->OutgoingEdges,
E) ==
E->Src->OutgoingEdges.end())) {
619 errs() <<
"ill-formed incoming edges\n";
624 for (
const Edge *
E :
N->OutgoingEdges) {
625 if (!
E->Src || !
E->Dst || (
E->Src !=
N) ||
626 (
find(
E->Dst->IncomingEdges,
E) ==
E->Dst->IncomingEdges.end())) {
627 errs() <<
"ill-formed outgoing edges\n";
632 const Function &Fn =
N->getFunction();
633 if (AMDGPU::isEntryFunctionCC(Fn.getCallingConv())) {
634 if (
N->hasAnyIncomingEdges()) {
635 errs() <<
"Kernels cannot have incoming edges\n";
640 if (Fn.isDeclaration()) {
641 errs() <<
"declarations shouldn't have nodes!\n";
645 auto [It, Inserted] = SeenFunctionNodes.insert(&Fn);
647 errs() <<
"one function has multiple nodes!\n";
652 if (ExpectedID != Nodes.size()) {
653 errs() <<
"Node IDs out of sync!\n";
657 if (createNodesBitVector().size() != getNumNodes()) {
658 errs() <<
"nodes bit vector doesn't have the right size!\n";
663 BitVector BV = createNodesBitVector();
665 if (
N->isGraphEntryPoint())
666 N->getDependencies(BV);
670 for (
const auto &Fn : M) {
671 if (!Fn.isDeclaration()) {
672 if (!SeenFunctionNodes.contains(&Fn)) {
673 errs() <<
"Fn has no associated node in the graph!\n";
680 errs() <<
"not all nodes are reachable through the graph's entry points!\n";
688CostType SplitGraph::calculateCost(
const BitVector &BV)
const {
690 for (
unsigned NodeID : BV.set_bits())
691 Cost +=
getNode(NodeID).getIndividualCost();
696SplitGraph::getNode(DenseMap<const GlobalValue *, Node *> &Cache,
697 const GlobalValue &GV) {
698 auto &
N = Cache[&GV];
703 bool NonCopyable =
false;
705 NonCopyable = isNonCopyable(*Fn);
706 Cost = CostMap.at(Fn);
708 N =
new (NodesPool.Allocate())
Node(Nodes.size(), GV, Cost, NonCopyable);
714const SplitGraph::Edge &SplitGraph::createEdge(
Node &Src,
Node &Dst,
716 const Edge *
E =
new (EdgesPool.Allocate<Edge>(1))
Edge(&Src, &Dst, EK);
717 Src.OutgoingEdges.push_back(
E);
718 Dst.IncomingEdges.push_back(
E);
737 SplitProposal(
const SplitGraph &SG,
unsigned MaxPartitions) : SG(&SG) {
738 Partitions.resize(MaxPartitions, {0, SG.createNodesBitVector()});
741 void setName(StringRef NewName) { Name = NewName; }
742 StringRef
getName()
const {
return Name; }
744 const BitVector &operator[](
unsigned PID)
const {
745 return Partitions[PID].second;
748 void add(
unsigned PID,
const BitVector &BV) {
749 Partitions[PID].second |= BV;
753 void print(raw_ostream &OS)
const;
758 unsigned findCheapestPartition()
const;
761 void calculateScores();
764 void verifyCompleteness()
const;
776 double getCodeSizeScore()
const {
return CodeSizeScore; }
790 double getBottleneckScore()
const {
return BottleneckScore; }
793 void updateScore(
unsigned PID) {
795 for (
auto &[PCost, Nodes] : Partitions) {
797 PCost = SG->calculateCost(Nodes);
803 double CodeSizeScore = 0.0;
805 double BottleneckScore = 0.0;
807 CostType TotalCost = 0;
809 const SplitGraph *SG =
nullptr;
812 std::vector<std::pair<CostType, BitVector>> Partitions;
815void SplitProposal::print(raw_ostream &OS)
const {
818 OS <<
"[proposal] " << Name <<
", total cost:" << TotalCost
819 <<
", code size score:" << format(
"%0.3f", CodeSizeScore)
820 <<
", bottleneck score:" << format(
"%0.3f", BottleneckScore) <<
'\n';
821 for (
const auto &[PID, Part] : enumerate(Partitions)) {
822 const auto &[Cost, NodeIDs] = Part;
823 OS <<
" - P" << PID <<
" nodes:" << NodeIDs.count() <<
" cost: " << Cost
824 <<
'|' << formatRatioOf(Cost, SG->getModuleCost()) <<
"%\n";
828unsigned SplitProposal::findCheapestPartition()
const {
829 assert(!Partitions.empty());
830 CostType CurCost = std::numeric_limits<CostType>::max();
831 unsigned CurPID = InvalidPID;
832 for (
const auto &[Idx, Part] : enumerate(Partitions)) {
833 if (Part.first <= CurCost) {
835 CurCost = Part.first;
838 assert(CurPID != InvalidPID);
842void SplitProposal::calculateScores() {
843 if (Partitions.empty())
847 CostType LargestPCost = 0;
848 for (
auto &[PCost, Nodes] : Partitions) {
849 if (PCost > LargestPCost)
850 LargestPCost = PCost;
853 CostType ModuleCost = SG->getModuleCost();
854 CodeSizeScore = double(TotalCost) / ModuleCost;
855 assert(CodeSizeScore >= 0.0);
857 BottleneckScore = double(LargestPCost) / ModuleCost;
859 CodeSizeScore = std::ceil(CodeSizeScore * 100.0) / 100.0;
860 BottleneckScore = std::ceil(BottleneckScore * 100.0) / 100.0;
864void SplitProposal::verifyCompleteness()
const {
865 if (Partitions.empty())
868 BitVector Result = Partitions[0].second;
869 for (
const auto &
P : drop_begin(Partitions))
871 assert(Result.all() &&
"some nodes are missing from this proposal!");
890class RecursiveSearchSplitting {
892 using SubmitProposalFn = function_ref<void(SplitProposal)>;
894 RecursiveSearchSplitting(
const SplitGraph &SG,
unsigned NumParts,
895 SubmitProposalFn SubmitProposal);
900 struct WorkListEntry {
901 WorkListEntry(
const BitVector &BV) : Cluster(BV) {}
903 unsigned NumNonEntryNodes = 0;
904 CostType TotalCost = 0;
905 CostType CostExcludingGraphEntryPoints = 0;
912 void setupWorkList();
923 void pickPartition(
unsigned Depth,
unsigned Idx, SplitProposal SP);
930 std::pair<unsigned, CostType>
931 findMostSimilarPartition(
const WorkListEntry &Entry,
const SplitProposal &SP);
933 const SplitGraph &SG;
935 SubmitProposalFn SubmitProposal;
939 CostType LargeClusterThreshold = 0;
940 unsigned NumProposalsSubmitted = 0;
941 SmallVector<WorkListEntry> WorkList;
944RecursiveSearchSplitting::RecursiveSearchSplitting(
945 const SplitGraph &SG,
unsigned NumParts, SubmitProposalFn SubmitProposal)
946 : SG(SG), NumParts(NumParts), SubmitProposal(SubmitProposal) {
951 report_fatal_error(
"[amdgpu-split-module] search depth of " +
952 Twine(MaxDepth) +
" is too high!");
953 LargeClusterThreshold =
954 (LargeFnFactor != 0.0)
955 ? CostType(((SG.getModuleCost() / NumParts) * LargeFnFactor))
956 : std::numeric_limits<CostType>::max();
957 LLVM_DEBUG(dbgs() <<
"[recursive search] large cluster threshold set at "
958 << LargeClusterThreshold <<
"\n");
961void RecursiveSearchSplitting::run() {
963 SplitModuleTimer SMT(
"recursive_search_prepare",
"preparing worklist");
968 SplitModuleTimer SMT(
"recursive_search_pick",
"partitioning");
969 SplitProposal SP(SG, NumParts);
970 pickPartition(0, 0, std::move(SP));
974void RecursiveSearchSplitting::setupWorkList() {
982 EquivalenceClasses<unsigned> NodeEC;
983 for (
const SplitGraph::Node *
N : SG.nodes()) {
984 if (!
N->isGraphEntryPoint())
987 NodeEC.insert(
N->getID());
988 N->visitAllDependencies([&](
const SplitGraph::Node &Dep) {
989 if (&Dep !=
N && Dep.isNonCopyable())
990 NodeEC.unionSets(
N->getID(), Dep.getID());
994 for (
const auto &
Node : NodeEC) {
995 if (!
Node->isLeader())
998 BitVector Cluster = SG.createNodesBitVector();
999 for (
unsigned M : NodeEC.members(*
Node)) {
1000 const SplitGraph::Node &
N = SG.getNode(M);
1001 if (
N.isGraphEntryPoint())
1002 N.getDependencies(Cluster);
1004 WorkList.emplace_back(std::move(Cluster));
1008 for (WorkListEntry &Entry : WorkList) {
1009 for (
unsigned NodeID : Entry.Cluster.set_bits()) {
1010 const SplitGraph::Node &
N = SG.getNode(NodeID);
1011 const CostType Cost =
N.getIndividualCost();
1013 Entry.TotalCost += Cost;
1014 if (!
N.isGraphEntryPoint()) {
1015 Entry.CostExcludingGraphEntryPoints += Cost;
1016 ++Entry.NumNonEntryNodes;
1021 stable_sort(WorkList, [](
const WorkListEntry &
A,
const WorkListEntry &
B) {
1022 if (
A.TotalCost !=
B.TotalCost)
1023 return A.TotalCost >
B.TotalCost;
1025 if (
A.CostExcludingGraphEntryPoints !=
B.CostExcludingGraphEntryPoints)
1026 return A.CostExcludingGraphEntryPoints >
B.CostExcludingGraphEntryPoints;
1028 if (
A.NumNonEntryNodes !=
B.NumNonEntryNodes)
1029 return A.NumNonEntryNodes >
B.NumNonEntryNodes;
1031 return A.Cluster.count() >
B.Cluster.count();
1035 dbgs() <<
"[recursive search] worklist:\n";
1036 for (
const auto &[Idx, Entry] : enumerate(WorkList)) {
1037 dbgs() <<
" - [" << Idx <<
"]: ";
1038 for (
unsigned NodeID : Entry.Cluster.set_bits())
1039 dbgs() << NodeID <<
" ";
1040 dbgs() <<
"(total_cost:" << Entry.TotalCost
1041 <<
", cost_excl_entries:" << Entry.CostExcludingGraphEntryPoints
1047void RecursiveSearchSplitting::pickPartition(
unsigned Depth,
unsigned Idx,
1049 while (Idx < WorkList.size()) {
1052 const WorkListEntry &Entry = WorkList[Idx];
1053 const BitVector &Cluster = Entry.Cluster;
1057 const unsigned CheapestPID = SP.findCheapestPartition();
1058 assert(CheapestPID != InvalidPID);
1062 const auto [MostSimilarPID, SimilarDepsCost] =
1063 findMostSimilarPartition(Entry, SP);
1067 unsigned SinglePIDToTry = InvalidPID;
1068 if (MostSimilarPID == InvalidPID)
1069 SinglePIDToTry = CheapestPID;
1070 else if (MostSimilarPID == CheapestPID)
1071 SinglePIDToTry = CheapestPID;
1072 else if (Depth >= MaxDepth) {
1075 if (Entry.CostExcludingGraphEntryPoints > LargeClusterThreshold) {
1077 assert(SimilarDepsCost && Entry.CostExcludingGraphEntryPoints);
1078 const double Ratio =
static_cast<double>(SimilarDepsCost) /
1079 Entry.CostExcludingGraphEntryPoints;
1080 assert(Ratio >= 0.0 && Ratio <= 1.0);
1081 if (Ratio > LargeFnOverlapForMerge) {
1086 SinglePIDToTry = MostSimilarPID;
1089 SinglePIDToTry = CheapestPID;
1096 if (SinglePIDToTry != InvalidPID) {
1097 LLVM_DEBUG(dbgs() << Idx <<
"=P" << SinglePIDToTry <<
' ');
1099 SP.add(SinglePIDToTry, Cluster);
1104 assert(MostSimilarPID != InvalidPID);
1113 SplitProposal BranchSP = SP;
1115 <<
" [lb] " << Idx <<
"=P" << CheapestPID <<
"? ");
1116 BranchSP.add(CheapestPID, Cluster);
1117 pickPartition(Depth + 1, Idx + 1, std::move(BranchSP));
1122 SplitProposal BranchSP = SP;
1124 <<
" [ms] " << Idx <<
"=P" << MostSimilarPID <<
"? ");
1125 BranchSP.add(MostSimilarPID, Cluster);
1126 pickPartition(Depth + 1, Idx + 1, std::move(BranchSP));
1134 assert(Idx == WorkList.size());
1135 assert(NumProposalsSubmitted <= (2u << MaxDepth) &&
1136 "Search got out of bounds?");
1137 SP.setName(
"recursive_search (depth=" + std::to_string(Depth) +
") #" +
1138 std::to_string(NumProposalsSubmitted++));
1140 SubmitProposal(std::move(SP));
1143std::pair<unsigned, CostType>
1144RecursiveSearchSplitting::findMostSimilarPartition(
const WorkListEntry &Entry,
1145 const SplitProposal &SP) {
1146 if (!Entry.NumNonEntryNodes)
1147 return {InvalidPID, 0};
1152 unsigned ChosenPID = InvalidPID;
1153 CostType ChosenCost = 0;
1154 for (
unsigned PID = 0; PID < NumParts; ++PID) {
1155 BitVector BV = SP[PID];
1156 BV &= Entry.Cluster;
1161 const CostType Cost = SG.calculateCost(BV);
1163 if (ChosenPID == InvalidPID || ChosenCost < Cost ||
1164 (ChosenCost == Cost && PID > ChosenPID)) {
1170 return {ChosenPID, ChosenCost};
1177const SplitGraph::Node *mapEdgeToDst(
const SplitGraph::Edge *
E) {
1181using SplitGraphEdgeDstIterator =
1182 mapped_iterator<SplitGraph::edges_iterator,
decltype(&mapEdgeToDst)>;
1197 return {
Ref->outgoing_edges().begin(), mapEdgeToDst};
1200 return {
Ref->outgoing_edges().end(), mapEdgeToDst};
1204 return G.nodes().begin();
1207 return G.nodes().end();
1215 return SG.getModule().getName().str();
1219 return N->getName().str();
1223 const SplitGraph &SG) {
1225 if (
N->isEntryFunctionCC())
1226 Result +=
"entry-fn-cc ";
1227 if (
N->isNonCopyable())
1228 Result +=
"non-copyable ";
1229 Result +=
"cost:" + std::to_string(
N->getIndividualCost());
1234 const SplitGraph &SG) {
1235 return N->hasAnyIncomingEdges() ?
"" :
"color=\"red\"";
1239 SplitGraphEdgeDstIterator EI,
1240 const SplitGraph &SG) {
1242 switch ((*EI.getCurrent())->Kind) {
1243 case SplitGraph::EdgeKind::DirectCall:
1245 case SplitGraph::EdgeKind::IndirectCall:
1246 return "style=\"dashed\"";
1261static bool needsConservativeImport(
const GlobalValue *GV) {
1263 return Var->hasLocalLinkage();
1265 return GA->hasLocalLinkage();
1271static void printPartitionSummary(raw_ostream &OS,
unsigned N,
const Module &M,
1272 unsigned PartCost,
unsigned ModuleCost) {
1273 OS <<
"*** Partition P" <<
N <<
" ***\n";
1275 for (
const auto &Fn : M) {
1276 if (!Fn.isDeclaration())
1277 OS <<
" - [function] " << Fn.getName() <<
"\n";
1280 for (
const auto &GV :
M.globals()) {
1281 if (GV.hasInitializer())
1282 OS <<
" - [global] " << GV.getName() <<
"\n";
1285 OS <<
"Partition contains " << formatRatioOf(PartCost, ModuleCost)
1286 <<
"% of the source\n";
1289static void evaluateProposal(SplitProposal &Best, SplitProposal New) {
1290 SplitModuleTimer SMT(
"proposal_evaluation",
"proposal ranking algorithm");
1293 New.verifyCompleteness();
1294 if (DebugProposalSearch)
1298 const double CurBScore = Best.getBottleneckScore();
1299 const double CurCSScore = Best.getCodeSizeScore();
1300 const double NewBScore =
New.getBottleneckScore();
1301 const double NewCSScore =
New.getCodeSizeScore();
1313 bool IsBest =
false;
1314 if (NewBScore < CurBScore)
1316 else if (NewBScore == CurBScore)
1317 IsBest = (NewCSScore < CurCSScore);
1320 Best = std::move(New);
1324 dbgs() <<
"[search] new best proposal!\n";
1326 dbgs() <<
"[search] discarding - not profitable\n";
1331static std::unique_ptr<Module> cloneAll(
const Module &M) {
1333 return CloneModule(M, VMap, [&](
const GlobalValue *GV) {
return true; });
1337static void writeDOTGraph(
const SplitGraph &SG) {
1338 if (ModuleDotCfgOutput.empty())
1342 raw_fd_ostream OS(ModuleDotCfgOutput, EC);
1344 errs() <<
"[" DEBUG_TYPE "]: cannot open '" << ModuleDotCfgOutput
1345 <<
"' - DOTGraph will not be printed\n";
1348 SG.getModule().getName());
1351static void splitAMDGPUModule(
1353 function_ref<
void(std::unique_ptr<Module> MPart)> ModuleCallback) {
1373 if (!NoExternalizeOnAddrTaken) {
1374 for (
auto &Fn : M) {
1375 if (Fn.hasLocalLinkage() && Fn.hasAddressTaken()) {
1377 dbgs() <<
" because its address is taken\n");
1385 if (!NoExternalizeGlobals) {
1386 for (
auto &GV :
M.globals()) {
1387 if (GV.hasLocalLinkage())
1388 LLVM_DEBUG(
dbgs() <<
"[externalize] GV " << GV.getName() <<
'\n');
1393 for (
auto &GA :
M.aliases()) {
1394 if (GA.hasLocalLinkage()) {
1395 LLVM_DEBUG(
dbgs() <<
"[externalize] alias " << GA.getName() <<
'\n');
1402 FunctionsCostMap FnCosts;
1403 const CostType ModuleCost = calculateFunctionCosts(GetTTI, M, FnCosts);
1407 SplitGraph SG(M, FnCosts, ModuleCost);
1413 <<
"[!] no nodes in graph, input is empty - no splitting possible\n");
1414 ModuleCallback(cloneAll(M));
1419 dbgs() <<
"[graph] nodes:\n";
1420 for (
const SplitGraph::Node *
N : SG.nodes()) {
1421 dbgs() <<
" - [" <<
N->getID() <<
"]: " <<
N->getName() <<
" "
1422 << (
N->isGraphEntryPoint() ?
"(entry)" :
"") <<
" "
1423 << (
N->isNonCopyable() ?
"(noncopyable)" :
"") <<
"\n";
1431 std::optional<SplitProposal> Proposal;
1432 const auto EvaluateProposal = [&](SplitProposal
SP) {
1433 SP.calculateScores();
1435 Proposal = std::move(SP);
1437 evaluateProposal(*Proposal, std::move(SP));
1442 RecursiveSearchSplitting(SG, NumParts, EvaluateProposal).run();
1443 LLVM_DEBUG(
if (Proposal)
dbgs() <<
"[search done] selected proposal: "
1444 << Proposal->getName() <<
"\n";);
1447 LLVM_DEBUG(
dbgs() <<
"[!] no proposal made, no splitting possible!\n");
1448 ModuleCallback(cloneAll(M));
1454 std::optional<raw_fd_ostream> SummariesOS;
1455 if (!PartitionSummariesOutput.empty()) {
1457 SummariesOS.emplace(PartitionSummariesOutput, EC);
1459 errs() <<
"[" DEBUG_TYPE "]: cannot open '" << PartitionSummariesOutput
1460 <<
"' - Partition summaries will not be printed\n";
1465 bool ImportAllGVs =
true;
1467 for (
unsigned PID = 0; PID < NumParts; ++PID) {
1468 SplitModuleTimer SMT2(
"modules_creation",
1469 "creating modules for each partition");
1472 DenseSet<const Function *> FnsInPart;
1473 for (
unsigned NodeID : (*Proposal)[PID].set_bits())
1474 FnsInPart.insert(&SG.getNode(NodeID).getFunction());
1477 if (FnsInPart.empty()) {
1479 <<
" is empty, not creating module\n");
1484 CostType PartCost = 0;
1485 std::unique_ptr<Module> MPart(
1489 if (FnsInPart.contains(Fn)) {
1490 PartCost += SG.getCost(*Fn);
1499 return FnsInPart.contains(Fn);
1503 return ImportAllGVs || needsConservativeImport(GV);
1506 ImportAllGVs =
false;
1510 if (needsConservativeImport(&GV) && GV.use_empty())
1511 GV.eraseFromParent();
1515 printPartitionSummary(*SummariesOS, PID, *MPart, PartCost, ModuleCost);
1518 printPartitionSummary(
dbgs(), PID, *MPart, PartCost, ModuleCost));
1520 ModuleCallback(std::move(MPart));
1527 SplitModuleTimer SMT(
1528 "total",
"total pass runtime (incl. potentially waiting for lockfile)");
1551 dbgs() <<
"[amdgpu-split-module] unable to acquire lockfile, debug "
1552 "output may be mangled by other processes\n");
1553 }
else if (!Owned) {
1562 <<
"[amdgpu-split-module] unable to acquire lockfile, debug "
1563 "output may be mangled by other processes\n");
1569 splitAMDGPUModule(TTIGetter, M, N, ModuleCallback);
1577 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)