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; });
394 bool hasAnyOutgoingEdges()
const {
return OutgoingEdges.size(); }
395 bool hasAnyOutgoingEdgesOfKind(EdgeKind EK)
const {
396 return any_of(OutgoingEdges, [&](
const auto *
E) {
return E->Kind == EK; });
400 return IncomingEdges;
404 return OutgoingEdges;
407 bool shouldFollowIndirectCalls()
const {
return isEntryFunctionCC(); }
414 void visitAllDependencies(std::function<
void(
const Node &)> Visitor)
const;
423 void getDependencies(BitVector &BV)
const {
424 visitAllDependencies([&](
const Node &
N) { BV.set(
N.getID()); });
428 void markAsGraphEntry() { IsGraphEntry =
true; }
431 const GlobalValue &GV;
432 CostType IndividualCost;
433 bool IsNonCopyable : 1;
434 bool IsEntryFnCC : 1;
435 bool IsGraphEntry : 1;
439 EdgesVec IncomingEdges;
440 EdgesVec OutgoingEdges;
443void SplitGraph::Node::visitAllDependencies(
444 std::function<
void(
const Node &)> Visitor)
const {
445 const bool FollowIndirect = shouldFollowIndirectCalls();
448 DenseSet<const Node *> Seen;
449 SmallVector<const Node *, 8> WorkList({
this});
450 while (!WorkList.empty()) {
451 const Node *CurN = WorkList.pop_back_val();
452 if (
auto [It, Inserted] = Seen.insert(CurN); !Inserted)
457 for (
const Edge *
E : CurN->outgoing_edges()) {
458 if (!FollowIndirect &&
E->Kind == EdgeKind::IndirectCall)
460 WorkList.push_back(
E->Dst);
472static bool handleCalleesMD(
const Instruction &
I,
473 SetVector<Function *> &Callees) {
474 auto *MD =
I.getMetadata(LLVMContext::MD_callees);
478 for (
const auto &Op : MD->operands()) {
479 Function *Callee = mdconst::extract_or_null<Function>(Op);
482 Callees.insert(Callee);
488void SplitGraph::buildGraph(CallGraph &CG) {
489 SplitModuleTimer SMT(
"buildGraph",
"graph construction");
492 <<
"[build graph] constructing graph representation of the input\n");
500 DenseMap<const GlobalValue *, Node *> Cache;
501 SmallVector<const Function *> FnsWithIndirectCalls, IndirectlyCallableFns;
502 for (
const Function &Fn : M) {
503 if (Fn.isDeclaration())
507 SetVector<const Function *> DirectCallees;
508 bool CallsExternal =
false;
509 for (
auto &CGEntry : *CG[&Fn]) {
510 auto *CGNode = CGEntry.second;
511 if (
auto *Callee = CGNode->getFunction()) {
512 if (!Callee->isDeclaration())
513 DirectCallees.insert(Callee);
514 }
else if (CGNode == CG.getCallsExternalNode())
515 CallsExternal =
true;
521 LLVM_DEBUG(dbgs() <<
" [!] callgraph is incomplete for ";
522 Fn.printAsOperand(dbgs());
523 dbgs() <<
" - analyzing function\n");
525 SetVector<Function *> KnownCallees;
526 bool HasUnknownIndirectCall =
false;
530 if (!CB || CB->getCalledFunction())
535 if (CB->isInlineAsm()) {
536 LLVM_DEBUG(dbgs() <<
" found inline assembly\n");
540 if (handleCalleesMD(Inst, KnownCallees))
544 KnownCallees.clear();
548 HasUnknownIndirectCall =
true;
552 if (HasUnknownIndirectCall) {
553 LLVM_DEBUG(dbgs() <<
" indirect call found\n");
554 FnsWithIndirectCalls.push_back(&Fn);
555 }
else if (!KnownCallees.empty())
556 DirectCallees.insert_range(KnownCallees);
560 for (
const auto *Callee : DirectCallees)
561 createEdge(
N,
getNode(Cache, *Callee), EdgeKind::DirectCall);
563 if (canBeIndirectlyCalled(Fn))
564 IndirectlyCallableFns.push_back(&Fn);
568 for (
const Function *Fn : FnsWithIndirectCalls) {
569 for (
const Function *Candidate : IndirectlyCallableFns) {
572 createEdge(Src, Dst, EdgeKind::IndirectCall);
577 SmallVector<Node *, 16> CandidateEntryPoints;
578 BitVector NodesReachableByKernels = createNodesBitVector();
579 for (
Node *
N : Nodes) {
581 if (
N->isEntryFunctionCC()) {
582 N->markAsGraphEntry();
583 N->getDependencies(NodesReachableByKernels);
584 }
else if (!
N->hasAnyIncomingEdgesOfKind(EdgeKind::DirectCall))
585 CandidateEntryPoints.push_back(
N);
588 for (
Node *
N : CandidateEntryPoints) {
594 if (!NodesReachableByKernels.test(
N->getID()))
595 N->markAsGraphEntry();
604bool SplitGraph::verifyGraph()
const {
605 unsigned ExpectedID = 0;
607 DenseSet<const Node *> SeenNodes;
608 DenseSet<const Function *> SeenFunctionNodes;
609 for (
const Node *
N : Nodes) {
610 if (
N->getID() != (ExpectedID++)) {
611 errs() <<
"Node IDs are incorrect!\n";
615 if (!SeenNodes.insert(
N).second) {
616 errs() <<
"Node seen more than once!\n";
621 errs() <<
"getNode doesn't return the right node\n";
625 for (
const Edge *
E :
N->IncomingEdges) {
626 if (!
E->Src || !
E->Dst || (
E->Dst !=
N) ||
627 (
find(
E->Src->OutgoingEdges,
E) ==
E->Src->OutgoingEdges.end())) {
628 errs() <<
"ill-formed incoming edges\n";
633 for (
const Edge *
E :
N->OutgoingEdges) {
634 if (!
E->Src || !
E->Dst || (
E->Src !=
N) ||
635 (
find(
E->Dst->IncomingEdges,
E) ==
E->Dst->IncomingEdges.end())) {
636 errs() <<
"ill-formed outgoing edges\n";
641 const Function &Fn =
N->getFunction();
642 if (AMDGPU::isEntryFunctionCC(Fn.getCallingConv())) {
643 if (
N->hasAnyIncomingEdges()) {
644 errs() <<
"Kernels cannot have incoming edges\n";
649 if (Fn.isDeclaration()) {
650 errs() <<
"declarations shouldn't have nodes!\n";
654 auto [It, Inserted] = SeenFunctionNodes.insert(&Fn);
656 errs() <<
"one function has multiple nodes!\n";
661 if (ExpectedID != Nodes.size()) {
662 errs() <<
"Node IDs out of sync!\n";
666 if (createNodesBitVector().size() != getNumNodes()) {
667 errs() <<
"nodes bit vector doesn't have the right size!\n";
672 BitVector BV = createNodesBitVector();
674 if (
N->isGraphEntryPoint())
675 N->getDependencies(BV);
679 for (
const auto &Fn : M) {
680 if (!Fn.isDeclaration()) {
681 if (!SeenFunctionNodes.contains(&Fn)) {
682 errs() <<
"Fn has no associated node in the graph!\n";
689 errs() <<
"not all nodes are reachable through the graph's entry points!\n";
697CostType SplitGraph::calculateCost(
const BitVector &BV)
const {
699 for (
unsigned NodeID : BV.set_bits())
700 Cost +=
getNode(NodeID).getIndividualCost();
705SplitGraph::getNode(DenseMap<const GlobalValue *, Node *> &Cache,
706 const GlobalValue &GV) {
707 auto &
N = Cache[&GV];
712 bool NonCopyable =
false;
714 NonCopyable = isNonCopyable(*Fn);
715 Cost = CostMap.at(Fn);
717 N =
new (NodesPool.Allocate())
Node(Nodes.size(), GV, Cost, NonCopyable);
723const SplitGraph::Edge &SplitGraph::createEdge(
Node &Src,
Node &Dst,
725 const Edge *
E =
new (EdgesPool.Allocate<Edge>(1))
Edge(&Src, &Dst, EK);
726 Src.OutgoingEdges.push_back(
E);
727 Dst.IncomingEdges.push_back(
E);
746 SplitProposal(
const SplitGraph &SG,
unsigned MaxPartitions) : SG(&SG) {
747 Partitions.resize(MaxPartitions, {0, SG.createNodesBitVector()});
750 void setName(StringRef NewName) { Name = NewName; }
751 StringRef
getName()
const {
return Name; }
753 const BitVector &operator[](
unsigned PID)
const {
754 return Partitions[PID].second;
757 void add(
unsigned PID,
const BitVector &BV) {
758 Partitions[PID].second |= BV;
762 void print(raw_ostream &OS)
const;
767 unsigned findCheapestPartition()
const;
770 void calculateScores();
773 void verifyCompleteness()
const;
785 double getCodeSizeScore()
const {
return CodeSizeScore; }
799 double getBottleneckScore()
const {
return BottleneckScore; }
802 void updateScore(
unsigned PID) {
804 for (
auto &[PCost, Nodes] : Partitions) {
806 PCost = SG->calculateCost(Nodes);
812 double CodeSizeScore = 0.0;
814 double BottleneckScore = 0.0;
816 CostType TotalCost = 0;
818 const SplitGraph *SG =
nullptr;
821 std::vector<std::pair<CostType, BitVector>> Partitions;
824void SplitProposal::print(raw_ostream &OS)
const {
827 OS <<
"[proposal] " << Name <<
", total cost:" << TotalCost
828 <<
", code size score:" << format(
"%0.3f", CodeSizeScore)
829 <<
", bottleneck score:" << format(
"%0.3f", BottleneckScore) <<
'\n';
830 for (
const auto &[PID, Part] : enumerate(Partitions)) {
831 const auto &[Cost, NodeIDs] = Part;
832 OS <<
" - P" << PID <<
" nodes:" << NodeIDs.count() <<
" cost: " << Cost
833 <<
'|' << formatRatioOf(Cost, SG->getModuleCost()) <<
"%\n";
837unsigned SplitProposal::findCheapestPartition()
const {
838 assert(!Partitions.empty());
839 CostType CurCost = std::numeric_limits<CostType>::max();
840 unsigned CurPID = InvalidPID;
841 for (
const auto &[Idx, Part] : enumerate(Partitions)) {
842 if (Part.first <= CurCost) {
844 CurCost = Part.first;
847 assert(CurPID != InvalidPID);
851void SplitProposal::calculateScores() {
852 if (Partitions.empty())
856 CostType LargestPCost = 0;
857 for (
auto &[PCost, Nodes] : Partitions) {
858 if (PCost > LargestPCost)
859 LargestPCost = PCost;
862 CostType ModuleCost = SG->getModuleCost();
863 CodeSizeScore = double(TotalCost) / ModuleCost;
864 assert(CodeSizeScore >= 0.0);
866 BottleneckScore = double(LargestPCost) / ModuleCost;
868 CodeSizeScore = std::ceil(CodeSizeScore * 100.0) / 100.0;
869 BottleneckScore = std::ceil(BottleneckScore * 100.0) / 100.0;
873void SplitProposal::verifyCompleteness()
const {
874 if (Partitions.empty())
877 BitVector Result = Partitions[0].second;
878 for (
const auto &
P : drop_begin(Partitions))
880 assert(Result.all() &&
"some nodes are missing from this proposal!");
899class RecursiveSearchSplitting {
901 using SubmitProposalFn = function_ref<void(SplitProposal)>;
903 RecursiveSearchSplitting(
const SplitGraph &SG,
unsigned NumParts,
904 SubmitProposalFn SubmitProposal);
909 struct WorkListEntry {
910 WorkListEntry(
const BitVector &BV) : Cluster(BV) {}
912 unsigned NumNonEntryNodes = 0;
913 CostType TotalCost = 0;
914 CostType CostExcludingGraphEntryPoints = 0;
921 void setupWorkList();
932 void pickPartition(
unsigned Depth,
unsigned Idx, SplitProposal SP);
939 std::pair<unsigned, CostType>
940 findMostSimilarPartition(
const WorkListEntry &Entry,
const SplitProposal &SP);
942 const SplitGraph &SG;
944 SubmitProposalFn SubmitProposal;
948 CostType LargeClusterThreshold = 0;
949 unsigned NumProposalsSubmitted = 0;
950 SmallVector<WorkListEntry> WorkList;
953RecursiveSearchSplitting::RecursiveSearchSplitting(
954 const SplitGraph &SG,
unsigned NumParts, SubmitProposalFn SubmitProposal)
955 : SG(SG), NumParts(NumParts), SubmitProposal(SubmitProposal) {
960 report_fatal_error(
"[amdgpu-split-module] search depth of " +
961 Twine(MaxDepth) +
" is too high!");
962 LargeClusterThreshold =
963 (LargeFnFactor != 0.0)
964 ? CostType(((SG.getModuleCost() / NumParts) * LargeFnFactor))
965 : std::numeric_limits<CostType>::max();
966 LLVM_DEBUG(dbgs() <<
"[recursive search] large cluster threshold set at "
967 << LargeClusterThreshold <<
"\n");
970void RecursiveSearchSplitting::run() {
972 SplitModuleTimer SMT(
"recursive_search_prepare",
"preparing worklist");
977 SplitModuleTimer SMT(
"recursive_search_pick",
"partitioning");
978 SplitProposal SP(SG, NumParts);
979 pickPartition(0, 0, std::move(SP));
983void RecursiveSearchSplitting::setupWorkList() {
991 EquivalenceClasses<unsigned> NodeEC;
992 for (
const SplitGraph::Node *
N : SG.nodes()) {
993 if (!
N->isGraphEntryPoint())
996 NodeEC.insert(
N->getID());
997 N->visitAllDependencies([&](
const SplitGraph::Node &Dep) {
998 if (&Dep !=
N && Dep.isNonCopyable())
999 NodeEC.unionSets(
N->getID(), Dep.getID());
1003 for (
const auto &
Node : NodeEC) {
1004 if (!
Node->isLeader())
1007 BitVector Cluster = SG.createNodesBitVector();
1008 for (
unsigned M : NodeEC.members(*
Node)) {
1009 const SplitGraph::Node &
N = SG.getNode(M);
1010 if (
N.isGraphEntryPoint())
1011 N.getDependencies(Cluster);
1013 WorkList.emplace_back(std::move(Cluster));
1017 for (WorkListEntry &Entry : WorkList) {
1018 for (
unsigned NodeID : Entry.Cluster.set_bits()) {
1019 const SplitGraph::Node &
N = SG.getNode(NodeID);
1020 const CostType Cost =
N.getIndividualCost();
1022 Entry.TotalCost += Cost;
1023 if (!
N.isGraphEntryPoint()) {
1024 Entry.CostExcludingGraphEntryPoints += Cost;
1025 ++Entry.NumNonEntryNodes;
1030 stable_sort(WorkList, [](
const WorkListEntry &
A,
const WorkListEntry &
B) {
1031 if (
A.TotalCost !=
B.TotalCost)
1032 return A.TotalCost >
B.TotalCost;
1034 if (
A.CostExcludingGraphEntryPoints !=
B.CostExcludingGraphEntryPoints)
1035 return A.CostExcludingGraphEntryPoints >
B.CostExcludingGraphEntryPoints;
1037 if (
A.NumNonEntryNodes !=
B.NumNonEntryNodes)
1038 return A.NumNonEntryNodes >
B.NumNonEntryNodes;
1040 return A.Cluster.count() >
B.Cluster.count();
1044 dbgs() <<
"[recursive search] worklist:\n";
1045 for (
const auto &[Idx, Entry] : enumerate(WorkList)) {
1046 dbgs() <<
" - [" << Idx <<
"]: ";
1047 for (
unsigned NodeID : Entry.Cluster.set_bits())
1048 dbgs() << NodeID <<
" ";
1049 dbgs() <<
"(total_cost:" << Entry.TotalCost
1050 <<
", cost_excl_entries:" << Entry.CostExcludingGraphEntryPoints
1056void RecursiveSearchSplitting::pickPartition(
unsigned Depth,
unsigned Idx,
1058 while (Idx < WorkList.size()) {
1061 const WorkListEntry &Entry = WorkList[Idx];
1062 const BitVector &Cluster = Entry.Cluster;
1066 const unsigned CheapestPID = SP.findCheapestPartition();
1067 assert(CheapestPID != InvalidPID);
1071 const auto [MostSimilarPID, SimilarDepsCost] =
1072 findMostSimilarPartition(Entry, SP);
1076 unsigned SinglePIDToTry = InvalidPID;
1077 if (MostSimilarPID == InvalidPID)
1078 SinglePIDToTry = CheapestPID;
1079 else if (MostSimilarPID == CheapestPID)
1080 SinglePIDToTry = CheapestPID;
1081 else if (Depth >= MaxDepth) {
1084 if (Entry.CostExcludingGraphEntryPoints > LargeClusterThreshold) {
1086 assert(SimilarDepsCost && Entry.CostExcludingGraphEntryPoints);
1087 const double Ratio =
static_cast<double>(SimilarDepsCost) /
1088 Entry.CostExcludingGraphEntryPoints;
1089 assert(Ratio >= 0.0 && Ratio <= 1.0);
1090 if (Ratio > LargeFnOverlapForMerge) {
1095 SinglePIDToTry = MostSimilarPID;
1098 SinglePIDToTry = CheapestPID;
1105 if (SinglePIDToTry != InvalidPID) {
1106 LLVM_DEBUG(dbgs() << Idx <<
"=P" << SinglePIDToTry <<
' ');
1108 SP.add(SinglePIDToTry, Cluster);
1113 assert(MostSimilarPID != InvalidPID);
1122 SplitProposal BranchSP = SP;
1124 <<
" [lb] " << Idx <<
"=P" << CheapestPID <<
"? ");
1125 BranchSP.add(CheapestPID, Cluster);
1126 pickPartition(Depth + 1, Idx + 1, std::move(BranchSP));
1131 SplitProposal BranchSP = SP;
1133 <<
" [ms] " << Idx <<
"=P" << MostSimilarPID <<
"? ");
1134 BranchSP.add(MostSimilarPID, Cluster);
1135 pickPartition(Depth + 1, Idx + 1, std::move(BranchSP));
1143 assert(Idx == WorkList.size());
1144 assert(NumProposalsSubmitted <= (2u << MaxDepth) &&
1145 "Search got out of bounds?");
1146 SP.setName(
"recursive_search (depth=" + std::to_string(Depth) +
") #" +
1147 std::to_string(NumProposalsSubmitted++));
1149 SubmitProposal(std::move(SP));
1152std::pair<unsigned, CostType>
1153RecursiveSearchSplitting::findMostSimilarPartition(
const WorkListEntry &Entry,
1154 const SplitProposal &SP) {
1155 if (!Entry.NumNonEntryNodes)
1156 return {InvalidPID, 0};
1161 unsigned ChosenPID = InvalidPID;
1162 CostType ChosenCost = 0;
1163 for (
unsigned PID = 0; PID < NumParts; ++PID) {
1164 BitVector BV = SP[PID];
1165 BV &= Entry.Cluster;
1170 const CostType Cost = SG.calculateCost(BV);
1172 if (ChosenPID == InvalidPID || ChosenCost < Cost ||
1173 (ChosenCost == Cost && PID > ChosenPID)) {
1179 return {ChosenPID, ChosenCost};
1186const SplitGraph::Node *mapEdgeToDst(
const SplitGraph::Edge *
E) {
1190using SplitGraphEdgeDstIterator =
1191 mapped_iterator<SplitGraph::edges_iterator,
decltype(&mapEdgeToDst)>;
1206 return {
Ref->outgoing_edges().begin(), mapEdgeToDst};
1209 return {
Ref->outgoing_edges().end(), mapEdgeToDst};
1213 return G.nodes().begin();
1216 return G.nodes().end();
1224 return SG.getModule().getName().str();
1228 return N->getName().str();
1232 const SplitGraph &SG) {
1234 if (
N->isEntryFunctionCC())
1235 Result +=
"entry-fn-cc ";
1236 if (
N->isNonCopyable())
1237 Result +=
"non-copyable ";
1238 Result +=
"cost:" + std::to_string(
N->getIndividualCost());
1243 const SplitGraph &SG) {
1244 return N->hasAnyIncomingEdges() ?
"" :
"color=\"red\"";
1248 SplitGraphEdgeDstIterator EI,
1249 const SplitGraph &SG) {
1251 switch ((*EI.getCurrent())->Kind) {
1252 case SplitGraph::EdgeKind::DirectCall:
1254 case SplitGraph::EdgeKind::IndirectCall:
1255 return "style=\"dashed\"";
1270static bool needsConservativeImport(
const GlobalValue *GV) {
1272 return Var->hasLocalLinkage();
1274 return GA->hasLocalLinkage();
1280static void printPartitionSummary(raw_ostream &OS,
unsigned N,
const Module &M,
1281 unsigned PartCost,
unsigned ModuleCost) {
1282 OS <<
"*** Partition P" <<
N <<
" ***\n";
1284 for (
const auto &Fn : M) {
1285 if (!Fn.isDeclaration())
1286 OS <<
" - [function] " << Fn.getName() <<
"\n";
1289 for (
const auto &GV :
M.globals()) {
1290 if (GV.hasInitializer())
1291 OS <<
" - [global] " << GV.getName() <<
"\n";
1294 OS <<
"Partition contains " << formatRatioOf(PartCost, ModuleCost)
1295 <<
"% of the source\n";
1298static void evaluateProposal(SplitProposal &Best, SplitProposal New) {
1299 SplitModuleTimer SMT(
"proposal_evaluation",
"proposal ranking algorithm");
1302 New.verifyCompleteness();
1303 if (DebugProposalSearch)
1307 const double CurBScore = Best.getBottleneckScore();
1308 const double CurCSScore = Best.getCodeSizeScore();
1309 const double NewBScore =
New.getBottleneckScore();
1310 const double NewCSScore =
New.getCodeSizeScore();
1322 bool IsBest =
false;
1323 if (NewBScore < CurBScore)
1325 else if (NewBScore == CurBScore)
1326 IsBest = (NewCSScore < CurCSScore);
1329 Best = std::move(New);
1333 dbgs() <<
"[search] new best proposal!\n";
1335 dbgs() <<
"[search] discarding - not profitable\n";
1340static std::unique_ptr<Module> cloneAll(
const Module &M) {
1342 return CloneModule(M, VMap, [&](
const GlobalValue *GV) {
return true; });
1346static void writeDOTGraph(
const SplitGraph &SG) {
1347 if (ModuleDotCfgOutput.empty())
1351 raw_fd_ostream OS(ModuleDotCfgOutput, EC);
1353 errs() <<
"[" DEBUG_TYPE "]: cannot open '" << ModuleDotCfgOutput
1354 <<
"' - DOTGraph will not be printed\n";
1357 SG.getModule().getName());
1360static void splitAMDGPUModule(
1362 function_ref<
void(std::unique_ptr<Module> MPart)> ModuleCallback) {
1382 if (!NoExternalizeOnAddrTaken) {
1383 for (
auto &Fn : M) {
1384 if (Fn.hasLocalLinkage() && Fn.hasAddressTaken()) {
1386 dbgs() <<
" because its address is taken\n");
1394 if (!NoExternalizeGlobals) {
1395 for (
auto &GV :
M.globals()) {
1396 if (GV.hasLocalLinkage())
1397 LLVM_DEBUG(
dbgs() <<
"[externalize] GV " << GV.getName() <<
'\n');
1402 for (
auto &GA :
M.aliases()) {
1403 if (GA.hasLocalLinkage()) {
1404 LLVM_DEBUG(
dbgs() <<
"[externalize] alias " << GA.getName() <<
'\n');
1411 FunctionsCostMap FnCosts;
1412 const CostType ModuleCost = calculateFunctionCosts(GetTTI, M, FnCosts);
1416 SplitGraph SG(M, FnCosts, ModuleCost);
1422 <<
"[!] no nodes in graph, input is empty - no splitting possible\n");
1423 ModuleCallback(cloneAll(M));
1428 dbgs() <<
"[graph] nodes:\n";
1429 for (
const SplitGraph::Node *
N : SG.nodes()) {
1430 dbgs() <<
" - [" <<
N->getID() <<
"]: " <<
N->getName() <<
" "
1431 << (
N->isGraphEntryPoint() ?
"(entry)" :
"") <<
" "
1432 << (
N->isNonCopyable() ?
"(noncopyable)" :
"") <<
"\n";
1440 std::optional<SplitProposal> Proposal;
1441 const auto EvaluateProposal = [&](SplitProposal
SP) {
1442 SP.calculateScores();
1444 Proposal = std::move(SP);
1446 evaluateProposal(*Proposal, std::move(SP));
1451 RecursiveSearchSplitting(SG, NumParts, EvaluateProposal).run();
1452 LLVM_DEBUG(
if (Proposal)
dbgs() <<
"[search done] selected proposal: "
1453 << Proposal->getName() <<
"\n";);
1456 LLVM_DEBUG(
dbgs() <<
"[!] no proposal made, no splitting possible!\n");
1457 ModuleCallback(cloneAll(M));
1463 std::optional<raw_fd_ostream> SummariesOS;
1464 if (!PartitionSummariesOutput.empty()) {
1466 SummariesOS.emplace(PartitionSummariesOutput, EC);
1468 errs() <<
"[" DEBUG_TYPE "]: cannot open '" << PartitionSummariesOutput
1469 <<
"' - Partition summaries will not be printed\n";
1474 bool ImportAllGVs =
true;
1476 for (
unsigned PID = 0; PID < NumParts; ++PID) {
1477 SplitModuleTimer SMT2(
"modules_creation",
1478 "creating modules for each partition");
1481 DenseSet<const Function *> FnsInPart;
1482 for (
unsigned NodeID : (*Proposal)[PID].set_bits())
1483 FnsInPart.insert(&SG.getNode(NodeID).getFunction());
1486 if (FnsInPart.empty()) {
1488 <<
" is empty, not creating module\n");
1493 CostType PartCost = 0;
1494 std::unique_ptr<Module> MPart(
1498 if (FnsInPart.contains(Fn)) {
1499 PartCost += SG.getCost(*Fn);
1508 return FnsInPart.contains(Fn);
1512 return ImportAllGVs || needsConservativeImport(GV);
1515 ImportAllGVs =
false;
1519 if (needsConservativeImport(&GV) && GV.use_empty())
1520 GV.eraseFromParent();
1524 printPartitionSummary(*SummariesOS, PID, *MPart, PartCost, ModuleCost);
1527 printPartitionSummary(
dbgs(), PID, *MPart, PartCost, ModuleCost));
1529 ModuleCallback(std::move(MPart));
1536 SplitModuleTimer SMT(
1537 "total",
"total pass runtime (incl. potentially waiting for lockfile)");
1560 dbgs() <<
"[amdgpu-split-module] unable to acquire lockfile, debug "
1561 "output may be mangled by other processes\n");
1562 }
else if (!Owned) {
1571 <<
"[amdgpu-split-module] unable to acquire lockfile, debug "
1572 "output may be mangled by other processes\n");
1578 splitAMDGPUModule(TTIGetter, M, N, ModuleCallback);
1586 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.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
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)