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() ||
169 if (GV.hasLocalLinkage()) {
177 GV.setName(
"__llvmsplit_unnamed");
187 FunctionsCostMap &CostMap) {
188 SplitModuleTimer SMT(
"calculateFunctionCosts",
"cost analysis");
190 LLVM_DEBUG(
dbgs() <<
"[cost analysis] calculating function costs\n");
191 CostType ModuleCost = 0;
192 [[maybe_unused]] CostType KernelCost = 0;
195 if (Fn.isDeclaration())
199 const auto &
TTI = GetTTI(Fn);
200 for (
const auto &BB : Fn) {
201 for (
const auto &
I : BB) {
206 CostType CostVal = Cost.isValid()
209 assert((FnCost + CostVal) >= FnCost &&
"Overflow!");
216 CostMap[&Fn] = FnCost;
217 assert((ModuleCost + FnCost) >= ModuleCost &&
"Overflow!");
218 ModuleCost += FnCost;
221 KernelCost += FnCost;
229 const CostType FnCost = ModuleCost - KernelCost;
230 dbgs() <<
" - total module cost is " << ModuleCost <<
". kernels cost "
231 <<
"" << KernelCost <<
" ("
232 <<
format(
"%0.2f", (
float(KernelCost) / ModuleCost) * 100)
233 <<
"% of the module), functions cost " << FnCost <<
" ("
234 <<
format(
"%0.2f", (
float(FnCost) / ModuleCost) * 100)
235 <<
"% of the module)\n";
242static bool canBeIndirectlyCalled(
const Function &
F) {
245 return !
F.hasLocalLinkage() ||
246 F.hasAddressTaken(
nullptr,
274 enum class EdgeKind :
uint8_t {
293 : Src(Src), Dst(Dst), Kind(Kind) {}
300 using EdgesVec = SmallVector<const Edge *, 0>;
302 using nodes_iterator =
const Node *
const *;
304 SplitGraph(
const Module &M,
const FunctionsCostMap &CostMap,
306 : M(M), CostMap(CostMap), ModuleCost(ModuleCost) {}
308 void buildGraph(CallGraph &CG);
311 bool verifyGraph()
const;
314 bool empty()
const {
return Nodes.empty(); }
316 const Node &
getNode(
unsigned ID)
const {
return *Nodes[ID]; }
318 unsigned getNumNodes()
const {
return Nodes.size(); }
319 BitVector createNodesBitVector()
const {
return BitVector(Nodes.size()); }
321 const Module &getModule()
const {
return M; }
323 CostType getModuleCost()
const {
return ModuleCost; }
328 CostType calculateCost(
const BitVector &BV)
const;
333 Node &
getNode(DenseMap<const GlobalValue *, Node *> &Cache,
334 const GlobalValue &GV);
337 const Edge &createEdge(
Node &Src,
Node &Dst, EdgeKind EK);
340 const FunctionsCostMap &CostMap;
346 SpecificBumpPtrAllocator<Node> NodesPool;
352 std::is_trivially_destructible_v<Edge>,
353 "Edge must be trivially destructible to use the BumpPtrAllocator");
369class SplitGraph::Node {
370 friend class SplitGraph;
373 Node(
unsigned ID,
const GlobalValue &GV, CostType IndividualCost,
375 : ID(ID), GV(GV), IndividualCost(IndividualCost),
376 IsNonCopyable(IsNonCopyable), IsEntryFnCC(
false), IsGraphEntry(
false) {
383 unsigned getID()
const {
return ID; }
389 CostType getIndividualCost()
const {
return IndividualCost; }
391 bool isNonCopyable()
const {
return IsNonCopyable; }
392 bool isEntryFunctionCC()
const {
return IsEntryFnCC; }
398 bool isGraphEntryPoint()
const {
return IsGraphEntry; }
400 StringRef
getName()
const {
return GV.getName(); }
402 bool hasAnyIncomingEdges()
const {
return IncomingEdges.size(); }
403 bool hasAnyIncomingEdgesOfKind(EdgeKind EK)
const {
404 return any_of(IncomingEdges, [&](
const auto *
E) {
return E->Kind == EK; });
407 bool hasAnyOutgoingEdges()
const {
return OutgoingEdges.size(); }
408 bool hasAnyOutgoingEdgesOfKind(EdgeKind EK)
const {
409 return any_of(OutgoingEdges, [&](
const auto *
E) {
return E->Kind == EK; });
413 return IncomingEdges;
417 return OutgoingEdges;
420 bool shouldFollowIndirectCalls()
const {
return isEntryFunctionCC(); }
427 void visitAllDependencies(std::function<
void(
const Node &)> Visitor)
const;
436 void getDependencies(BitVector &BV)
const {
437 visitAllDependencies([&](
const Node &
N) { BV.set(
N.getID()); });
441 void markAsGraphEntry() { IsGraphEntry =
true; }
444 const GlobalValue &GV;
445 CostType IndividualCost;
446 bool IsNonCopyable : 1;
447 bool IsEntryFnCC : 1;
448 bool IsGraphEntry : 1;
452 EdgesVec IncomingEdges;
453 EdgesVec OutgoingEdges;
456void SplitGraph::Node::visitAllDependencies(
457 std::function<
void(
const Node &)> Visitor)
const {
458 const bool FollowIndirect = shouldFollowIndirectCalls();
461 DenseSet<const Node *> Seen;
462 SmallVector<const Node *, 8> WorkList({
this});
463 while (!WorkList.empty()) {
464 const Node *CurN = WorkList.pop_back_val();
465 if (
auto [It, Inserted] = Seen.insert(CurN); !Inserted)
470 for (
const Edge *
E : CurN->outgoing_edges()) {
471 if (!FollowIndirect &&
E->Kind == EdgeKind::IndirectCall)
473 WorkList.push_back(
E->Dst);
485static bool handleCalleesMD(
const Instruction &
I,
486 SetVector<Function *> &Callees) {
487 auto *MD =
I.getMetadata(LLVMContext::MD_callees);
491 for (
const auto &Op : MD->operands()) {
492 Function *Callee = mdconst::extract_or_null<Function>(Op);
495 Callees.insert(Callee);
501void SplitGraph::buildGraph(CallGraph &CG) {
502 SplitModuleTimer SMT(
"buildGraph",
"graph construction");
505 <<
"[build graph] constructing graph representation of the input\n");
513 DenseMap<const GlobalValue *, Node *> Cache;
514 SmallVector<const Function *> FnsWithIndirectCalls, IndirectlyCallableFns;
515 for (
const Function &Fn : M) {
516 if (Fn.isDeclaration())
520 SetVector<const Function *> DirectCallees;
521 bool CallsExternal =
false;
522 for (
auto &CGEntry : *CG[&Fn]) {
523 auto *CGNode = CGEntry.second;
524 if (
auto *Callee = CGNode->getFunction()) {
525 if (!Callee->isDeclaration())
526 DirectCallees.insert(Callee);
527 }
else if (CGNode == CG.getCallsExternalNode())
528 CallsExternal =
true;
534 LLVM_DEBUG(dbgs() <<
" [!] callgraph is incomplete for ";
535 Fn.printAsOperand(dbgs());
536 dbgs() <<
" - analyzing function\n");
538 SetVector<Function *> KnownCallees;
539 bool HasUnknownIndirectCall =
false;
542 const auto *CB = dyn_cast<CallBase>(&Inst);
543 if (!CB || CB->getCalledFunction())
548 if (CB->isInlineAsm()) {
549 LLVM_DEBUG(dbgs() <<
" found inline assembly\n");
553 if (handleCalleesMD(Inst, KnownCallees))
557 KnownCallees.clear();
561 HasUnknownIndirectCall =
true;
565 if (HasUnknownIndirectCall) {
566 LLVM_DEBUG(dbgs() <<
" indirect call found\n");
567 FnsWithIndirectCalls.push_back(&Fn);
568 }
else if (!KnownCallees.empty())
569 DirectCallees.insert_range(KnownCallees);
573 for (
const auto *Callee : DirectCallees)
574 createEdge(
N,
getNode(Cache, *Callee), EdgeKind::DirectCall);
576 if (canBeIndirectlyCalled(Fn))
577 IndirectlyCallableFns.push_back(&Fn);
581 for (
const Function *Fn : FnsWithIndirectCalls) {
582 for (
const Function *Candidate : IndirectlyCallableFns) {
585 createEdge(Src, Dst, EdgeKind::IndirectCall);
590 SmallVector<Node *, 16> CandidateEntryPoints;
591 BitVector NodesReachableByKernels = createNodesBitVector();
592 for (
Node *
N : Nodes) {
594 if (
N->isEntryFunctionCC()) {
595 N->markAsGraphEntry();
596 N->getDependencies(NodesReachableByKernels);
597 }
else if (!
N->hasAnyIncomingEdgesOfKind(EdgeKind::DirectCall))
598 CandidateEntryPoints.push_back(
N);
601 for (
Node *
N : CandidateEntryPoints) {
607 if (!NodesReachableByKernels.test(
N->getID()))
608 N->markAsGraphEntry();
617bool SplitGraph::verifyGraph()
const {
618 unsigned ExpectedID = 0;
620 DenseSet<const Node *> SeenNodes;
621 DenseSet<const Function *> SeenFunctionNodes;
622 for (
const Node *
N : Nodes) {
623 if (
N->getID() != (ExpectedID++)) {
624 errs() <<
"Node IDs are incorrect!\n";
628 if (!SeenNodes.insert(
N).second) {
629 errs() <<
"Node seen more than once!\n";
634 errs() <<
"getNode doesn't return the right node\n";
638 for (
const Edge *
E :
N->IncomingEdges) {
639 if (!
E->Src || !
E->Dst || (
E->Dst !=
N) ||
640 (
find(
E->Src->OutgoingEdges,
E) ==
E->Src->OutgoingEdges.end())) {
641 errs() <<
"ill-formed incoming edges\n";
646 for (
const Edge *
E :
N->OutgoingEdges) {
647 if (!
E->Src || !
E->Dst || (
E->Src !=
N) ||
648 (
find(
E->Dst->IncomingEdges,
E) ==
E->Dst->IncomingEdges.end())) {
649 errs() <<
"ill-formed outgoing edges\n";
654 const Function &Fn =
N->getFunction();
655 if (AMDGPU::isEntryFunctionCC(Fn.getCallingConv())) {
656 if (
N->hasAnyIncomingEdges()) {
657 errs() <<
"Kernels cannot have incoming edges\n";
662 if (Fn.isDeclaration()) {
663 errs() <<
"declarations shouldn't have nodes!\n";
667 auto [It, Inserted] = SeenFunctionNodes.insert(&Fn);
669 errs() <<
"one function has multiple nodes!\n";
674 if (ExpectedID != Nodes.size()) {
675 errs() <<
"Node IDs out of sync!\n";
679 if (createNodesBitVector().size() != getNumNodes()) {
680 errs() <<
"nodes bit vector doesn't have the right size!\n";
685 BitVector BV = createNodesBitVector();
687 if (
N->isGraphEntryPoint())
688 N->getDependencies(BV);
692 for (
const auto &Fn : M) {
693 if (!Fn.isDeclaration()) {
694 if (!SeenFunctionNodes.contains(&Fn)) {
695 errs() <<
"Fn has no associated node in the graph!\n";
702 errs() <<
"not all nodes are reachable through the graph's entry points!\n";
710CostType SplitGraph::calculateCost(
const BitVector &BV)
const {
712 for (
unsigned NodeID : BV.set_bits())
713 Cost +=
getNode(NodeID).getIndividualCost();
718SplitGraph::getNode(DenseMap<const GlobalValue *, Node *> &Cache,
719 const GlobalValue &GV) {
720 auto &
N = Cache[&GV];
725 bool NonCopyable =
false;
726 if (
const Function *Fn = dyn_cast<Function>(&GV)) {
727 NonCopyable = isNonCopyable(*Fn);
728 Cost = CostMap.at(Fn);
730 N =
new (NodesPool.Allocate())
Node(Nodes.size(), GV, Cost, NonCopyable);
736const SplitGraph::Edge &SplitGraph::createEdge(
Node &Src,
Node &Dst,
738 const Edge *
E =
new (EdgesPool.Allocate<Edge>(1))
Edge(&Src, &Dst, EK);
739 Src.OutgoingEdges.push_back(
E);
740 Dst.IncomingEdges.push_back(
E);
759 SplitProposal(
const SplitGraph &SG,
unsigned MaxPartitions) : SG(&SG) {
760 Partitions.resize(MaxPartitions, {0, SG.createNodesBitVector()});
763 void setName(StringRef NewName) { Name = NewName; }
764 StringRef
getName()
const {
return Name; }
766 const BitVector &operator[](
unsigned PID)
const {
767 return Partitions[PID].second;
770 void add(
unsigned PID,
const BitVector &BV) {
771 Partitions[PID].second |= BV;
775 void print(raw_ostream &OS)
const;
780 unsigned findCheapestPartition()
const;
783 void calculateScores();
786 void verifyCompleteness()
const;
798 double getCodeSizeScore()
const {
return CodeSizeScore; }
812 double getBottleneckScore()
const {
return BottleneckScore; }
815 void updateScore(
unsigned PID) {
817 for (
auto &[PCost, Nodes] : Partitions) {
819 PCost = SG->calculateCost(Nodes);
825 double CodeSizeScore = 0.0;
827 double BottleneckScore = 0.0;
829 CostType TotalCost = 0;
831 const SplitGraph *SG =
nullptr;
834 std::vector<std::pair<CostType, BitVector>> Partitions;
837void SplitProposal::print(raw_ostream &OS)
const {
840 OS <<
"[proposal] " << Name <<
", total cost:" << TotalCost
841 <<
", code size score:" << format(
"%0.3f", CodeSizeScore)
842 <<
", bottleneck score:" << format(
"%0.3f", BottleneckScore) <<
'\n';
843 for (
const auto &[PID, Part] : enumerate(Partitions)) {
844 const auto &[Cost, NodeIDs] = Part;
845 OS <<
" - P" << PID <<
" nodes:" << NodeIDs.count() <<
" cost: " << Cost
846 <<
'|' << formatRatioOf(Cost, SG->getModuleCost()) <<
"%\n";
850unsigned SplitProposal::findCheapestPartition()
const {
851 assert(!Partitions.empty());
852 CostType CurCost = std::numeric_limits<CostType>::max();
853 unsigned CurPID = InvalidPID;
854 for (
const auto &[Idx, Part] : enumerate(Partitions)) {
855 if (Part.first <= CurCost) {
857 CurCost = Part.first;
860 assert(CurPID != InvalidPID);
864void SplitProposal::calculateScores() {
865 if (Partitions.empty())
869 CostType LargestPCost = 0;
870 for (
auto &[PCost, Nodes] : Partitions) {
871 if (PCost > LargestPCost)
872 LargestPCost = PCost;
875 CostType ModuleCost = SG->getModuleCost();
876 CodeSizeScore = double(TotalCost) / ModuleCost;
877 assert(CodeSizeScore >= 0.0);
879 BottleneckScore = double(LargestPCost) / ModuleCost;
881 CodeSizeScore = std::ceil(CodeSizeScore * 100.0) / 100.0;
882 BottleneckScore = std::ceil(BottleneckScore * 100.0) / 100.0;
886void SplitProposal::verifyCompleteness()
const {
887 if (Partitions.empty())
890 BitVector Result = Partitions[0].second;
891 for (
const auto &
P : drop_begin(Partitions))
893 assert(Result.all() &&
"some nodes are missing from this proposal!");
912class RecursiveSearchSplitting {
914 using SubmitProposalFn = function_ref<void(SplitProposal)>;
916 RecursiveSearchSplitting(
const SplitGraph &SG,
unsigned NumParts,
917 SubmitProposalFn SubmitProposal);
922 struct WorkListEntry {
923 WorkListEntry(
const BitVector &BV) : Cluster(BV) {}
925 unsigned NumNonEntryNodes = 0;
926 CostType TotalCost = 0;
927 CostType CostExcludingGraphEntryPoints = 0;
934 void setupWorkList();
945 void pickPartition(
unsigned Depth,
unsigned Idx, SplitProposal SP);
952 std::pair<unsigned, CostType>
953 findMostSimilarPartition(
const WorkListEntry &Entry,
const SplitProposal &SP);
955 const SplitGraph &SG;
957 SubmitProposalFn SubmitProposal;
961 CostType LargeClusterThreshold = 0;
962 unsigned NumProposalsSubmitted = 0;
963 SmallVector<WorkListEntry> WorkList;
966RecursiveSearchSplitting::RecursiveSearchSplitting(
967 const SplitGraph &SG,
unsigned NumParts, SubmitProposalFn SubmitProposal)
968 : SG(SG), NumParts(NumParts), SubmitProposal(SubmitProposal) {
973 report_fatal_error(
"[amdgpu-split-module] search depth of " +
974 Twine(MaxDepth) +
" is too high!");
975 LargeClusterThreshold =
976 (LargeFnFactor != 0.0)
977 ? CostType(((SG.getModuleCost() / NumParts) * LargeFnFactor))
978 : std::numeric_limits<CostType>::max();
979 LLVM_DEBUG(dbgs() <<
"[recursive search] large cluster threshold set at "
980 << LargeClusterThreshold <<
"\n");
983void RecursiveSearchSplitting::run() {
985 SplitModuleTimer SMT(
"recursive_search_prepare",
"preparing worklist");
990 SplitModuleTimer SMT(
"recursive_search_pick",
"partitioning");
991 SplitProposal SP(SG, NumParts);
992 pickPartition(0, 0, std::move(SP));
996void RecursiveSearchSplitting::setupWorkList() {
1004 EquivalenceClasses<unsigned> NodeEC;
1005 for (
const SplitGraph::Node *
N : SG.nodes()) {
1006 if (!
N->isGraphEntryPoint())
1009 NodeEC.insert(
N->getID());
1010 N->visitAllDependencies([&](
const SplitGraph::Node &Dep) {
1011 if (&Dep !=
N && Dep.isNonCopyable())
1012 NodeEC.unionSets(
N->getID(), Dep.getID());
1016 for (
const auto &
Node : NodeEC) {
1017 if (!
Node->isLeader())
1020 BitVector Cluster = SG.createNodesBitVector();
1021 for (
unsigned M : NodeEC.members(*
Node)) {
1022 const SplitGraph::Node &
N = SG.getNode(M);
1023 if (
N.isGraphEntryPoint())
1024 N.getDependencies(Cluster);
1026 WorkList.emplace_back(std::move(Cluster));
1030 for (WorkListEntry &Entry : WorkList) {
1031 for (
unsigned NodeID : Entry.Cluster.set_bits()) {
1032 const SplitGraph::Node &
N = SG.getNode(NodeID);
1033 const CostType Cost =
N.getIndividualCost();
1035 Entry.TotalCost += Cost;
1036 if (!
N.isGraphEntryPoint()) {
1037 Entry.CostExcludingGraphEntryPoints += Cost;
1038 ++Entry.NumNonEntryNodes;
1043 stable_sort(WorkList, [](
const WorkListEntry &
A,
const WorkListEntry &
B) {
1044 if (
A.TotalCost !=
B.TotalCost)
1045 return A.TotalCost >
B.TotalCost;
1047 if (
A.CostExcludingGraphEntryPoints !=
B.CostExcludingGraphEntryPoints)
1048 return A.CostExcludingGraphEntryPoints >
B.CostExcludingGraphEntryPoints;
1050 if (
A.NumNonEntryNodes !=
B.NumNonEntryNodes)
1051 return A.NumNonEntryNodes >
B.NumNonEntryNodes;
1053 return A.Cluster.count() >
B.Cluster.count();
1057 dbgs() <<
"[recursive search] worklist:\n";
1058 for (
const auto &[Idx, Entry] : enumerate(WorkList)) {
1059 dbgs() <<
" - [" << Idx <<
"]: ";
1060 for (
unsigned NodeID : Entry.Cluster.set_bits())
1061 dbgs() << NodeID <<
" ";
1062 dbgs() <<
"(total_cost:" << Entry.TotalCost
1063 <<
", cost_excl_entries:" << Entry.CostExcludingGraphEntryPoints
1069void RecursiveSearchSplitting::pickPartition(
unsigned Depth,
unsigned Idx,
1071 while (Idx < WorkList.size()) {
1074 const WorkListEntry &Entry = WorkList[Idx];
1075 const BitVector &Cluster = Entry.Cluster;
1079 const unsigned CheapestPID = SP.findCheapestPartition();
1080 assert(CheapestPID != InvalidPID);
1084 const auto [MostSimilarPID, SimilarDepsCost] =
1085 findMostSimilarPartition(Entry, SP);
1089 unsigned SinglePIDToTry = InvalidPID;
1090 if (MostSimilarPID == InvalidPID)
1091 SinglePIDToTry = CheapestPID;
1092 else if (MostSimilarPID == CheapestPID)
1093 SinglePIDToTry = CheapestPID;
1094 else if (Depth >= MaxDepth) {
1097 if (Entry.CostExcludingGraphEntryPoints > LargeClusterThreshold) {
1099 assert(SimilarDepsCost && Entry.CostExcludingGraphEntryPoints);
1100 const double Ratio =
static_cast<double>(SimilarDepsCost) /
1101 Entry.CostExcludingGraphEntryPoints;
1102 assert(Ratio >= 0.0 && Ratio <= 1.0);
1103 if (Ratio > LargeFnOverlapForMerge) {
1108 SinglePIDToTry = MostSimilarPID;
1111 SinglePIDToTry = CheapestPID;
1118 if (SinglePIDToTry != InvalidPID) {
1119 LLVM_DEBUG(dbgs() << Idx <<
"=P" << SinglePIDToTry <<
' ');
1121 SP.add(SinglePIDToTry, Cluster);
1126 assert(MostSimilarPID != InvalidPID);
1135 SplitProposal BranchSP = SP;
1137 <<
" [lb] " << Idx <<
"=P" << CheapestPID <<
"? ");
1138 BranchSP.add(CheapestPID, Cluster);
1139 pickPartition(Depth + 1, Idx + 1, std::move(BranchSP));
1144 SplitProposal BranchSP = SP;
1146 <<
" [ms] " << Idx <<
"=P" << MostSimilarPID <<
"? ");
1147 BranchSP.add(MostSimilarPID, Cluster);
1148 pickPartition(Depth + 1, Idx + 1, std::move(BranchSP));
1156 assert(Idx == WorkList.size());
1157 assert(NumProposalsSubmitted <= (2u << MaxDepth) &&
1158 "Search got out of bounds?");
1159 SP.setName(
"recursive_search (depth=" + std::to_string(Depth) +
") #" +
1160 std::to_string(NumProposalsSubmitted++));
1162 SubmitProposal(std::move(SP));
1165std::pair<unsigned, CostType>
1166RecursiveSearchSplitting::findMostSimilarPartition(
const WorkListEntry &Entry,
1167 const SplitProposal &SP) {
1168 if (!Entry.NumNonEntryNodes)
1169 return {InvalidPID, 0};
1174 unsigned ChosenPID = InvalidPID;
1175 CostType ChosenCost = 0;
1176 for (
unsigned PID = 0; PID < NumParts; ++PID) {
1177 BitVector BV = SP[PID];
1178 BV &= Entry.Cluster;
1183 const CostType Cost = SG.calculateCost(BV);
1185 if (ChosenPID == InvalidPID || ChosenCost < Cost ||
1186 (ChosenCost == Cost && PID > ChosenPID)) {
1192 return {ChosenPID, ChosenCost};
1199const SplitGraph::Node *mapEdgeToDst(
const SplitGraph::Edge *
E) {
1203using SplitGraphEdgeDstIterator =
1204 mapped_iterator<SplitGraph::edges_iterator,
decltype(&mapEdgeToDst)>;
1219 return {
Ref->outgoing_edges().begin(), mapEdgeToDst};
1222 return {
Ref->outgoing_edges().end(), mapEdgeToDst};
1226 return G.nodes().begin();
1229 return G.nodes().end();
1237 return SG.getModule().getName().str();
1241 return N->getName().str();
1245 const SplitGraph &SG) {
1247 if (
N->isEntryFunctionCC())
1248 Result +=
"entry-fn-cc ";
1249 if (
N->isNonCopyable())
1250 Result +=
"non-copyable ";
1251 Result +=
"cost:" + std::to_string(
N->getIndividualCost());
1256 const SplitGraph &SG) {
1257 return N->hasAnyIncomingEdges() ?
"" :
"color=\"red\"";
1261 SplitGraphEdgeDstIterator EI,
1262 const SplitGraph &SG) {
1264 switch ((*EI.getCurrent())->Kind) {
1265 case SplitGraph::EdgeKind::DirectCall:
1267 case SplitGraph::EdgeKind::IndirectCall:
1268 return "style=\"dashed\"";
1283static bool needsConservativeImport(
const GlobalValue *GV) {
1284 if (
const auto *Var = dyn_cast<GlobalVariable>(GV))
1285 return Var->hasLocalLinkage();
1286 if (
const auto *GA = dyn_cast<GlobalAlias>(GV))
1287 return GA->hasLocalLinkage();
1293static void printPartitionSummary(raw_ostream &OS,
unsigned N,
const Module &M,
1294 unsigned PartCost,
unsigned ModuleCost) {
1295 OS <<
"*** Partition P" <<
N <<
" ***\n";
1297 for (
const auto &Fn : M) {
1298 if (!Fn.isDeclaration())
1299 OS <<
" - [function] " << Fn.getName() <<
"\n";
1302 for (
const auto &GV :
M.globals()) {
1303 if (GV.hasInitializer())
1304 OS <<
" - [global] " << GV.getName() <<
"\n";
1307 OS <<
"Partition contains " << formatRatioOf(PartCost, ModuleCost)
1308 <<
"% of the source\n";
1311static void evaluateProposal(SplitProposal &Best, SplitProposal New) {
1312 SplitModuleTimer SMT(
"proposal_evaluation",
"proposal ranking algorithm");
1315 New.verifyCompleteness();
1316 if (DebugProposalSearch)
1320 const double CurBScore = Best.getBottleneckScore();
1321 const double CurCSScore = Best.getCodeSizeScore();
1322 const double NewBScore =
New.getBottleneckScore();
1323 const double NewCSScore =
New.getCodeSizeScore();
1335 bool IsBest =
false;
1336 if (NewBScore < CurBScore)
1338 else if (NewBScore == CurBScore)
1339 IsBest = (NewCSScore < CurCSScore);
1342 Best = std::move(New);
1346 dbgs() <<
"[search] new best proposal!\n";
1348 dbgs() <<
"[search] discarding - not profitable\n";
1353static std::unique_ptr<Module> cloneAll(
const Module &M) {
1355 return CloneModule(M, VMap, [&](
const GlobalValue *GV) {
return true; });
1359static void writeDOTGraph(
const SplitGraph &SG) {
1360 if (ModuleDotCfgOutput.empty())
1364 raw_fd_ostream OS(ModuleDotCfgOutput, EC);
1366 errs() <<
"[" DEBUG_TYPE "]: cannot open '" << ModuleDotCfgOutput
1367 <<
"' - DOTGraph will not be printed\n";
1370 SG.getModule().getName());
1373static void splitAMDGPUModule(
1375 function_ref<
void(std::unique_ptr<Module> MPart)> ModuleCallback) {
1395 if (!NoExternalizeOnAddrTaken) {
1396 for (
auto &Fn : M) {
1397 if (Fn.hasLocalLinkage() && Fn.hasAddressTaken()) {
1399 dbgs() <<
" because its address is taken\n");
1407 if (!NoExternalizeGlobals) {
1408 for (
auto &GV :
M.globals()) {
1409 if (GV.hasLocalLinkage())
1410 LLVM_DEBUG(
dbgs() <<
"[externalize] GV " << GV.getName() <<
'\n');
1415 for (
auto &GA :
M.aliases()) {
1416 if (GA.hasLocalLinkage()) {
1417 LLVM_DEBUG(
dbgs() <<
"[externalize] alias " << GA.getName() <<
'\n');
1424 FunctionsCostMap FnCosts;
1425 const CostType ModuleCost = calculateFunctionCosts(GetTTI, M, FnCosts);
1429 SplitGraph SG(M, FnCosts, ModuleCost);
1435 <<
"[!] no nodes in graph, input is empty - no splitting possible\n");
1436 ModuleCallback(cloneAll(M));
1441 dbgs() <<
"[graph] nodes:\n";
1442 for (
const SplitGraph::Node *
N : SG.nodes()) {
1443 dbgs() <<
" - [" <<
N->getID() <<
"]: " <<
N->getName() <<
" "
1444 << (
N->isGraphEntryPoint() ?
"(entry)" :
"") <<
" "
1445 << (
N->isNonCopyable() ?
"(noncopyable)" :
"") <<
"\n";
1453 std::optional<SplitProposal> Proposal;
1454 const auto EvaluateProposal = [&](SplitProposal
SP) {
1455 SP.calculateScores();
1457 Proposal = std::move(SP);
1459 evaluateProposal(*Proposal, std::move(SP));
1464 RecursiveSearchSplitting(SG, NumParts, EvaluateProposal).run();
1465 LLVM_DEBUG(
if (Proposal)
dbgs() <<
"[search done] selected proposal: "
1466 << Proposal->getName() <<
"\n";);
1469 LLVM_DEBUG(
dbgs() <<
"[!] no proposal made, no splitting possible!\n");
1470 ModuleCallback(cloneAll(M));
1476 std::optional<raw_fd_ostream> SummariesOS;
1477 if (!PartitionSummariesOutput.empty()) {
1479 SummariesOS.emplace(PartitionSummariesOutput, EC);
1481 errs() <<
"[" DEBUG_TYPE "]: cannot open '" << PartitionSummariesOutput
1482 <<
"' - Partition summaries will not be printed\n";
1487 bool ImportAllGVs =
true;
1489 for (
unsigned PID = 0; PID < NumParts; ++PID) {
1490 SplitModuleTimer SMT2(
"modules_creation",
1491 "creating modules for each partition");
1494 DenseSet<const Function *> FnsInPart;
1495 for (
unsigned NodeID : (*Proposal)[PID].set_bits())
1496 FnsInPart.insert(&SG.getNode(NodeID).getFunction());
1499 if (FnsInPart.empty()) {
1501 <<
" is empty, not creating module\n");
1506 CostType PartCost = 0;
1507 std::unique_ptr<Module> MPart(
1510 if (
const auto *Fn = dyn_cast<Function>(GV)) {
1511 if (FnsInPart.contains(Fn)) {
1512 PartCost += SG.getCost(*Fn);
1519 if (
const auto *GA = dyn_cast<GlobalAlias>(GV)) {
1520 if (
const auto *Fn = dyn_cast<Function>(GA->getAliaseeObject()))
1521 return FnsInPart.contains(Fn);
1525 return ImportAllGVs || needsConservativeImport(GV);
1528 ImportAllGVs =
false;
1532 if (needsConservativeImport(&GV) && GV.use_empty())
1533 GV.eraseFromParent();
1537 printPartitionSummary(*SummariesOS, PID, *MPart, PartCost, ModuleCost);
1540 printPartitionSummary(
dbgs(), PID, *MPart, PartCost, ModuleCost));
1542 ModuleCallback(std::move(MPart));
1549 SplitModuleTimer SMT(
1550 "total",
"total pass runtime (incl. potentially waiting for lockfile)");
1573 dbgs() <<
"[amdgpu-split-module] unable to acquire lockfile, debug "
1574 "output may be mangled by other processes\n");
1575 }
else if (!Owned) {
1584 <<
"[amdgpu-split-module] unable to acquire lockfile, debug "
1585 "output may be mangled by other processes\n");
1591 splitAMDGPUModule(TTIGetter, M, N, ModuleCallback);
1599 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)
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 void externalize(GlobalValue *GV)
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.
@ HiddenVisibility
The GV is hidden.
@ ExternalLinkage
Externally visible function.
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.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
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)