67#define DEBUG_TYPE "amdgpu-split-module"
72struct SplitModuleTimer : NamedRegionTimer {
73 SplitModuleTimer(StringRef Name, StringRef
Desc)
74 : NamedRegionTimer(Name,
Desc,
DEBUG_TYPE,
"AMDGPU Module Splitting",
83using FunctionsCostMap = DenseMap<const Function *, CostType>;
85static constexpr unsigned InvalidPID = -1;
90static auto formatRatioOf(CostType Num, CostType Dem) {
91 CostType DemOr1 = Dem ? Dem : 1;
92 return format(
"%0.2f", (
static_cast<double>(Num) / DemOr1) * 100);
103static bool isNonCopyable(
const Function &
F) {
104 return F.hasExternalLinkage() || !
F.isDefinitionExact() ||
115 FunctionsCostMap &CostMap) {
116 SplitModuleTimer SMT(
"calculateFunctionCosts",
"cost analysis");
118 LLVM_DEBUG(
dbgs() <<
"[cost analysis] calculating function costs\n");
119 CostType ModuleCost = 0;
120 [[maybe_unused]] CostType KernelCost = 0;
123 if (Fn.isDeclaration())
127 const auto &
TTI = GetTTI(Fn);
128 for (
const auto &BB : Fn) {
129 for (
const auto &
I : BB) {
134 CostType CostVal = Cost.isValid()
137 assert((FnCost + CostVal) >= FnCost &&
"Overflow!");
144 CostMap[&Fn] = FnCost;
145 assert((ModuleCost + FnCost) >= ModuleCost &&
"Overflow!");
146 ModuleCost += FnCost;
149 KernelCost += FnCost;
157 const CostType FnCost = ModuleCost - KernelCost;
158 dbgs() <<
" - total module cost is " << ModuleCost <<
". kernels cost "
159 <<
"" << KernelCost <<
" ("
160 <<
format(
"%0.2f", (
float(KernelCost) / ModuleCost) * 100)
161 <<
"% of the module), functions cost " << FnCost <<
" ("
162 <<
format(
"%0.2f", (
float(FnCost) / ModuleCost) * 100)
163 <<
"% of the module)\n";
170static bool canBeIndirectlyCalled(
const Function &
F) {
173 return !
F.hasLocalLinkage() ||
174 F.hasAddressTaken(
nullptr,
202 enum class EdgeKind :
uint8_t {
221 : Src(Src), Dst(Dst), Kind(Kind) {}
228 using EdgesVec = SmallVector<const Edge *, 0>;
230 using nodes_iterator =
const Node *
const *;
232 SplitGraph(
const Module &M,
const FunctionsCostMap &CostMap,
234 : M(M), CostMap(CostMap), ModuleCost(ModuleCost) {}
236 void buildGraph(CallGraph &CG);
239 bool verifyGraph()
const;
242 bool empty()
const {
return Nodes.empty(); }
244 const Node &
getNode(
unsigned ID)
const {
return *Nodes[ID]; }
246 unsigned getNumNodes()
const {
return Nodes.size(); }
247 BitVector createNodesBitVector()
const {
return BitVector(Nodes.size()); }
249 const Module &getModule()
const {
return M; }
251 CostType getModuleCost()
const {
return ModuleCost; }
256 CostType calculateCost(
const BitVector &BV)
const;
261 Node &
getNode(DenseMap<const GlobalValue *, Node *> &Cache,
262 const GlobalValue &GV);
265 const Edge &createEdge(
Node &Src,
Node &Dst, EdgeKind EK);
268 const FunctionsCostMap &CostMap;
274 SpecificBumpPtrAllocator<Node> NodesPool;
280 std::is_trivially_destructible_v<Edge>,
281 "Edge must be trivially destructible to use the BumpPtrAllocator");
297class SplitGraph::Node {
298 friend class SplitGraph;
301 Node(
unsigned ID,
const GlobalValue &GV, CostType IndividualCost,
303 : ID(ID), GV(GV), IndividualCost(IndividualCost),
304 IsNonCopyable(IsNonCopyable), IsEntryFnCC(
false), IsGraphEntry(
false) {
311 unsigned getID()
const {
return ID; }
317 CostType getIndividualCost()
const {
return IndividualCost; }
319 bool isNonCopyable()
const {
return IsNonCopyable; }
320 bool isEntryFunctionCC()
const {
return IsEntryFnCC; }
326 bool isGraphEntryPoint()
const {
return IsGraphEntry; }
328 StringRef
getName()
const {
return GV.getName(); }
330 bool hasAnyIncomingEdges()
const {
return IncomingEdges.size(); }
331 bool hasAnyIncomingEdgesOfKind(EdgeKind EK)
const {
332 return any_of(IncomingEdges, [&](
const auto *
E) {
return E->Kind == EK; });
336 return OutgoingEdges;
339 bool shouldFollowIndirectCalls()
const {
return isEntryFunctionCC(); }
346 void visitAllDependencies(std::function<
void(
const Node &)> Visitor)
const;
355 void getDependencies(BitVector &BV)
const {
356 visitAllDependencies([&](
const Node &
N) { BV.set(
N.getID()); });
360 void markAsGraphEntry() { IsGraphEntry =
true; }
363 const GlobalValue &GV;
364 CostType IndividualCost;
365 bool IsNonCopyable : 1;
366 bool IsEntryFnCC : 1;
367 bool IsGraphEntry : 1;
371 EdgesVec IncomingEdges;
372 EdgesVec OutgoingEdges;
375void SplitGraph::Node::visitAllDependencies(
376 std::function<
void(
const Node &)> Visitor)
const {
377 const bool FollowIndirect = shouldFollowIndirectCalls();
380 DenseSet<const Node *> Seen;
381 SmallVector<const Node *, 8> WorkList({
this});
382 while (!WorkList.empty()) {
383 const Node *CurN = WorkList.pop_back_val();
384 if (
auto [It, Inserted] = Seen.insert(CurN); !Inserted)
389 for (
const Edge *
E : CurN->outgoing_edges()) {
390 if (!FollowIndirect &&
E->Kind == EdgeKind::IndirectCall)
392 WorkList.push_back(
E->Dst);
404static bool handleCalleesMD(
const Instruction &
I,
405 SetVector<Function *> &Callees) {
406 auto *MD =
I.getMetadata(LLVMContext::MD_callees);
410 for (
const auto &Op : MD->operands()) {
411 Function *Callee = mdconst::extract_or_null<Function>(Op);
414 Callees.insert(Callee);
420void SplitGraph::buildGraph(CallGraph &CG) {
421 SplitModuleTimer SMT(
"buildGraph",
"graph construction");
424 <<
"[build graph] constructing graph representation of the input\n");
432 DenseMap<const GlobalValue *, Node *> Cache;
433 SmallVector<const Function *> FnsWithIndirectCalls, IndirectlyCallableFns;
434 for (
const Function &Fn : M) {
435 if (Fn.isDeclaration())
439 SetVector<const Function *> DirectCallees;
440 bool CallsExternal =
false;
441 for (
auto &CGEntry : *CG[&Fn]) {
442 auto *CGNode = CGEntry.second;
443 if (
auto *Callee = CGNode->getFunction()) {
444 if (!Callee->isDeclaration())
445 DirectCallees.insert(Callee);
446 }
else if (CGNode == CG.getCallsExternalNode())
447 CallsExternal =
true;
453 LLVM_DEBUG(dbgs() <<
" [!] callgraph is incomplete for ";
454 Fn.printAsOperand(dbgs());
455 dbgs() <<
" - analyzing function\n");
457 SetVector<Function *> KnownCallees;
458 bool HasUnknownIndirectCall =
false;
462 if (!CB || CB->getCalledFunction())
467 if (CB->isInlineAsm()) {
468 LLVM_DEBUG(dbgs() <<
" found inline assembly\n");
472 if (handleCalleesMD(Inst, KnownCallees))
476 KnownCallees.clear();
480 HasUnknownIndirectCall =
true;
484 if (HasUnknownIndirectCall) {
485 LLVM_DEBUG(dbgs() <<
" indirect call found\n");
486 FnsWithIndirectCalls.push_back(&Fn);
487 }
else if (!KnownCallees.empty())
488 DirectCallees.insert_range(KnownCallees);
492 for (
const auto *Callee : DirectCallees)
493 createEdge(
N,
getNode(Cache, *Callee), EdgeKind::DirectCall);
495 if (canBeIndirectlyCalled(Fn))
496 IndirectlyCallableFns.push_back(&Fn);
500 for (
const Function *Fn : FnsWithIndirectCalls) {
501 for (
const Function *Candidate : IndirectlyCallableFns) {
504 createEdge(Src, Dst, EdgeKind::IndirectCall);
509 SmallVector<Node *, 16> CandidateEntryPoints;
510 BitVector NodesReachableByKernels = createNodesBitVector();
511 for (
Node *
N : Nodes) {
513 if (
N->isEntryFunctionCC()) {
514 N->markAsGraphEntry();
515 N->getDependencies(NodesReachableByKernels);
516 }
else if (!
N->hasAnyIncomingEdgesOfKind(EdgeKind::DirectCall))
517 CandidateEntryPoints.push_back(
N);
520 BitVector ReachableNodes = NodesReachableByKernels;
521 for (
Node *
N : CandidateEntryPoints) {
527 if (!NodesReachableByKernels.test(
N->getID())) {
528 N->markAsGraphEntry();
529 N->getDependencies(ReachableNodes);
539 SmallVector<Node *> PostOrder;
540 BitVector Visited = ReachableNodes;
541 SmallVector<std::pair<Node *, edges_iterator>> Stack;
542 for (
Node *Root : Nodes) {
543 if (Visited.test(Root->getID()))
545 Visited.set(Root->getID());
546 Stack.emplace_back(Root, Root->outgoing_edges().begin());
547 while (!Stack.empty()) {
548 auto &[
N, It] = Stack.back();
549 if (It ==
N->outgoing_edges().end()) {
550 PostOrder.push_back(
N);
554 const Edge *
E = *It++;
555 if (
E->Kind != EdgeKind::DirectCall || Visited.test(
E->Dst->getID()))
557 Visited.set(
E->Dst->getID());
558 Stack.emplace_back(
E->Dst,
E->Dst->outgoing_edges().begin());
562 for (
Node *
N : reverse(PostOrder)) {
563 if (ReachableNodes.test(
N->getID()))
565 N->markAsGraphEntry();
566 N->getDependencies(ReachableNodes);
575bool SplitGraph::verifyGraph()
const {
576 unsigned ExpectedID = 0;
578 DenseSet<const Node *> SeenNodes;
579 DenseSet<const Function *> SeenFunctionNodes;
580 for (
const Node *
N : Nodes) {
581 if (
N->getID() != (ExpectedID++)) {
582 errs() <<
"Node IDs are incorrect!\n";
586 if (!SeenNodes.insert(
N).second) {
587 errs() <<
"Node seen more than once!\n";
592 errs() <<
"getNode doesn't return the right node\n";
596 for (
const Edge *
E :
N->IncomingEdges) {
597 if (!
E->Src || !
E->Dst || (
E->Dst !=
N) ||
598 (
find(
E->Src->OutgoingEdges,
E) ==
E->Src->OutgoingEdges.end())) {
599 errs() <<
"ill-formed incoming edges\n";
604 for (
const Edge *
E :
N->OutgoingEdges) {
605 if (!
E->Src || !
E->Dst || (
E->Src !=
N) ||
606 (
find(
E->Dst->IncomingEdges,
E) ==
E->Dst->IncomingEdges.end())) {
607 errs() <<
"ill-formed outgoing edges\n";
612 const Function &Fn =
N->getFunction();
613 if (AMDGPU::isEntryFunctionCC(Fn.getCallingConv())) {
614 if (
N->hasAnyIncomingEdges()) {
615 errs() <<
"Kernels cannot have incoming edges\n";
620 if (Fn.isDeclaration()) {
621 errs() <<
"declarations shouldn't have nodes!\n";
625 auto [It, Inserted] = SeenFunctionNodes.insert(&Fn);
627 errs() <<
"one function has multiple nodes!\n";
632 if (ExpectedID != Nodes.size()) {
633 errs() <<
"Node IDs out of sync!\n";
637 if (createNodesBitVector().size() != getNumNodes()) {
638 errs() <<
"nodes bit vector doesn't have the right size!\n";
643 BitVector BV = createNodesBitVector();
645 if (
N->isGraphEntryPoint())
646 N->getDependencies(BV);
650 for (
const auto &Fn : M) {
651 if (!Fn.isDeclaration()) {
652 if (!SeenFunctionNodes.contains(&Fn)) {
653 errs() <<
"Fn has no associated node in the graph!\n";
660 errs() <<
"not all nodes are reachable through the graph's entry points!\n";
668CostType SplitGraph::calculateCost(
const BitVector &BV)
const {
670 for (
unsigned NodeID : BV.set_bits())
671 Cost +=
getNode(NodeID).getIndividualCost();
676SplitGraph::getNode(DenseMap<const GlobalValue *, Node *> &Cache,
677 const GlobalValue &GV) {
678 auto &
N = Cache[&GV];
683 bool NonCopyable =
false;
685 NonCopyable = isNonCopyable(*Fn);
686 Cost = CostMap.at(Fn);
688 N =
new (NodesPool.Allocate())
Node(Nodes.size(), GV, Cost, NonCopyable);
694const SplitGraph::Edge &SplitGraph::createEdge(
Node &Src,
Node &Dst,
696 const Edge *
E =
new (EdgesPool.Allocate<Edge>(1))
Edge(&Src, &Dst, EK);
697 Src.OutgoingEdges.push_back(
E);
698 Dst.IncomingEdges.push_back(
E);
717 SplitProposal(
const SplitGraph &SG,
unsigned MaxPartitions) : SG(&SG) {
718 Partitions.resize(MaxPartitions, {0, SG.createNodesBitVector()});
721 void setName(StringRef NewName) { Name = NewName; }
722 StringRef
getName()
const {
return Name; }
724 const BitVector &operator[](
unsigned PID)
const {
725 return Partitions[PID].second;
728 void add(
unsigned PID,
const BitVector &BV) {
729 Partitions[PID].second |= BV;
733 void print(raw_ostream &OS)
const;
738 unsigned findCheapestPartition()
const;
741 void calculateScores();
744 void verifyCompleteness()
const;
756 double getCodeSizeScore()
const {
return CodeSizeScore; }
770 double getBottleneckScore()
const {
return BottleneckScore; }
773 void updateScore(
unsigned PID) {
775 for (
auto &[PCost, Nodes] : Partitions) {
777 PCost = SG->calculateCost(Nodes);
783 double CodeSizeScore = 0.0;
785 double BottleneckScore = 0.0;
787 CostType TotalCost = 0;
789 const SplitGraph *SG =
nullptr;
792 std::vector<std::pair<CostType, BitVector>> Partitions;
795void SplitProposal::print(raw_ostream &OS)
const {
798 OS <<
"[proposal] " << Name <<
", total cost:" << TotalCost
799 <<
", code size score:" << format(
"%0.3f", CodeSizeScore)
800 <<
", bottleneck score:" << format(
"%0.3f", BottleneckScore) <<
'\n';
801 for (
const auto &[PID, Part] : enumerate(Partitions)) {
802 const auto &[Cost, NodeIDs] = Part;
803 OS <<
" - P" << PID <<
" nodes:" << NodeIDs.count() <<
" cost: " << Cost
804 <<
'|' << formatRatioOf(Cost, SG->getModuleCost()) <<
"%\n";
808unsigned SplitProposal::findCheapestPartition()
const {
809 assert(!Partitions.empty());
810 CostType CurCost = std::numeric_limits<CostType>::max();
811 unsigned CurPID = InvalidPID;
812 for (
const auto &[Idx, Part] : enumerate(Partitions)) {
813 if (Part.first <= CurCost) {
815 CurCost = Part.first;
818 assert(CurPID != InvalidPID);
822void SplitProposal::calculateScores() {
823 if (Partitions.empty())
827 CostType LargestPCost = 0;
828 for (
auto &[PCost, Nodes] : Partitions) {
829 if (PCost > LargestPCost)
830 LargestPCost = PCost;
833 CostType ModuleCost = SG->getModuleCost();
834 CodeSizeScore = double(TotalCost) / ModuleCost;
835 assert(CodeSizeScore >= 0.0);
837 BottleneckScore = double(LargestPCost) / ModuleCost;
839 CodeSizeScore = std::ceil(CodeSizeScore * 100.0) / 100.0;
840 BottleneckScore = std::ceil(BottleneckScore * 100.0) / 100.0;
844void SplitProposal::verifyCompleteness()
const {
845 if (Partitions.empty())
848 BitVector Result = Partitions[0].second;
849 for (
const auto &
P : drop_begin(Partitions))
851 assert(Result.all() &&
"some nodes are missing from this proposal!");
870class RecursiveSearchSplitting {
872 using SubmitProposalFn = function_ref<void(SplitProposal)>;
874 RecursiveSearchSplitting(
const AMDGPUOptions &CLOpts,
const SplitGraph &SG,
875 unsigned NumParts, SubmitProposalFn SubmitProposal);
880 struct WorkListEntry {
881 WorkListEntry(
const BitVector &BV) : Cluster(BV) {}
883 unsigned NumNonEntryNodes = 0;
884 CostType TotalCost = 0;
885 CostType CostExcludingGraphEntryPoints = 0;
892 void setupWorkList();
903 void pickPartition(
unsigned Depth,
unsigned Idx, SplitProposal SP);
910 std::pair<unsigned, CostType>
911 findMostSimilarPartition(
const WorkListEntry &Entry,
const SplitProposal &SP);
913 const AMDGPUOptions &CLOpts;
914 const SplitGraph &SG;
916 SubmitProposalFn SubmitProposal;
920 CostType LargeClusterThreshold = 0;
921 unsigned NumProposalsSubmitted = 0;
922 SmallVector<WorkListEntry> WorkList;
925RecursiveSearchSplitting::RecursiveSearchSplitting(
926 const AMDGPUOptions &CLOpts,
const SplitGraph &SG,
unsigned NumParts,
927 SubmitProposalFn SubmitProposal)
929 SubmitProposal(SubmitProposal) {
933 if (CLOpts.module_splitting_max_depth > 16)
934 report_fatal_error(
"[amdgpu-split-module] search depth of " +
935 Twine(CLOpts.module_splitting_max_depth) +
937 float LargeFnFactor = CLOpts.module_splitting_large_threshold;
938 LargeClusterThreshold =
939 (LargeFnFactor != 0.0)
940 ? CostType(((SG.getModuleCost() /
NumParts) * LargeFnFactor))
941 : std::numeric_limits<CostType>::max();
942 LLVM_DEBUG(dbgs() <<
"[recursive search] large cluster threshold set at "
943 << LargeClusterThreshold <<
"\n");
946void RecursiveSearchSplitting::run() {
948 SplitModuleTimer SMT(
"recursive_search_prepare",
"preparing worklist");
953 SplitModuleTimer SMT(
"recursive_search_pick",
"partitioning");
955 pickPartition(0, 0, std::move(SP));
959void RecursiveSearchSplitting::setupWorkList() {
967 EquivalenceClasses<unsigned> NodeEC;
968 for (
const SplitGraph::Node *
N : SG.nodes()) {
969 if (!
N->isGraphEntryPoint())
972 NodeEC.insert(
N->getID());
973 N->visitAllDependencies([&](
const SplitGraph::Node &Dep) {
974 if (&Dep !=
N && Dep.isNonCopyable())
975 NodeEC.unionSets(
N->getID(), Dep.getID());
979 for (
const auto &
Node : NodeEC) {
980 if (!
Node->isLeader())
983 BitVector Cluster = SG.createNodesBitVector();
984 for (
unsigned M : NodeEC.members(*
Node)) {
985 const SplitGraph::Node &
N = SG.getNode(M);
986 if (
N.isGraphEntryPoint())
987 N.getDependencies(Cluster);
989 WorkList.emplace_back(std::move(Cluster));
993 for (WorkListEntry &Entry : WorkList) {
994 for (
unsigned NodeID : Entry.Cluster.set_bits()) {
995 const SplitGraph::Node &
N = SG.getNode(NodeID);
996 const CostType Cost =
N.getIndividualCost();
998 Entry.TotalCost += Cost;
999 if (!
N.isGraphEntryPoint()) {
1000 Entry.CostExcludingGraphEntryPoints += Cost;
1001 ++Entry.NumNonEntryNodes;
1006 stable_sort(WorkList, [](
const WorkListEntry &
A,
const WorkListEntry &
B) {
1007 if (
A.TotalCost !=
B.TotalCost)
1008 return A.TotalCost >
B.TotalCost;
1010 if (
A.CostExcludingGraphEntryPoints !=
B.CostExcludingGraphEntryPoints)
1011 return A.CostExcludingGraphEntryPoints >
B.CostExcludingGraphEntryPoints;
1013 if (
A.NumNonEntryNodes !=
B.NumNonEntryNodes)
1014 return A.NumNonEntryNodes >
B.NumNonEntryNodes;
1016 return A.Cluster.count() >
B.Cluster.count();
1020 dbgs() <<
"[recursive search] worklist:\n";
1021 for (
const auto &[Idx, Entry] : enumerate(WorkList)) {
1022 dbgs() <<
" - [" << Idx <<
"]: ";
1023 for (
unsigned NodeID : Entry.Cluster.set_bits())
1024 dbgs() << NodeID <<
" ";
1025 dbgs() <<
"(total_cost:" << Entry.TotalCost
1026 <<
", cost_excl_entries:" << Entry.CostExcludingGraphEntryPoints
1032void RecursiveSearchSplitting::pickPartition(
unsigned Depth,
unsigned Idx,
1034 while (Idx < WorkList.size()) {
1037 const WorkListEntry &Entry = WorkList[Idx];
1038 const BitVector &Cluster = Entry.Cluster;
1042 const unsigned CheapestPID = SP.findCheapestPartition();
1043 assert(CheapestPID != InvalidPID);
1047 const auto [MostSimilarPID, SimilarDepsCost] =
1048 findMostSimilarPartition(Entry, SP);
1052 unsigned SinglePIDToTry = InvalidPID;
1053 if (MostSimilarPID == InvalidPID)
1054 SinglePIDToTry = CheapestPID;
1055 else if (MostSimilarPID == CheapestPID)
1056 SinglePIDToTry = CheapestPID;
1057 else if (Depth >= CLOpts.module_splitting_max_depth) {
1060 if (Entry.CostExcludingGraphEntryPoints > LargeClusterThreshold) {
1062 assert(SimilarDepsCost && Entry.CostExcludingGraphEntryPoints);
1063 const double Ratio =
static_cast<double>(SimilarDepsCost) /
1064 Entry.CostExcludingGraphEntryPoints;
1065 assert(Ratio >= 0.0 && Ratio <= 1.0);
1066 if (Ratio > CLOpts.module_splitting_merge_threshold) {
1071 SinglePIDToTry = MostSimilarPID;
1074 SinglePIDToTry = CheapestPID;
1081 if (SinglePIDToTry != InvalidPID) {
1082 LLVM_DEBUG(dbgs() << Idx <<
"=P" << SinglePIDToTry <<
' ');
1084 SP.add(SinglePIDToTry, Cluster);
1089 assert(MostSimilarPID != InvalidPID);
1098 SplitProposal BranchSP = SP;
1100 <<
" [lb] " << Idx <<
"=P" << CheapestPID <<
"? ");
1101 BranchSP.add(CheapestPID, Cluster);
1102 pickPartition(Depth + 1, Idx + 1, std::move(BranchSP));
1107 SplitProposal BranchSP = SP;
1109 <<
" [ms] " << Idx <<
"=P" << MostSimilarPID <<
"? ");
1110 BranchSP.add(MostSimilarPID, Cluster);
1111 pickPartition(Depth + 1, Idx + 1, std::move(BranchSP));
1119 assert(Idx == WorkList.size());
1120 assert(NumProposalsSubmitted <= (2u << CLOpts.module_splitting_max_depth) &&
1121 "Search got out of bounds?");
1122 SP.setName(
"recursive_search (depth=" + std::to_string(Depth) +
") #" +
1123 std::to_string(NumProposalsSubmitted++));
1125 SubmitProposal(std::move(SP));
1128std::pair<unsigned, CostType>
1129RecursiveSearchSplitting::findMostSimilarPartition(
const WorkListEntry &Entry,
1130 const SplitProposal &SP) {
1131 if (!Entry.NumNonEntryNodes)
1132 return {InvalidPID, 0};
1137 unsigned ChosenPID = InvalidPID;
1138 CostType ChosenCost = 0;
1139 for (
unsigned PID = 0; PID <
NumParts; ++PID) {
1140 BitVector BV = SP[PID];
1141 BV &= Entry.Cluster;
1146 const CostType Cost = SG.calculateCost(BV);
1148 if (ChosenPID == InvalidPID || ChosenCost < Cost ||
1149 (ChosenCost == Cost && PID > ChosenPID)) {
1155 return {ChosenPID, ChosenCost};
1162const SplitGraph::Node *mapEdgeToDst(
const SplitGraph::Edge *
E) {
1166using SplitGraphEdgeDstIterator =
1167 mapped_iterator<SplitGraph::edges_iterator,
decltype(&mapEdgeToDst)>;
1182 return {
Ref->outgoing_edges().begin(), mapEdgeToDst};
1185 return {
Ref->outgoing_edges().end(), mapEdgeToDst};
1189 return G.nodes().begin();
1192 return G.nodes().end();
1200 return SG.getModule().getName().str();
1204 return N->getName().str();
1208 const SplitGraph &SG) {
1210 if (
N->isEntryFunctionCC())
1211 Result +=
"entry-fn-cc ";
1212 if (
N->isNonCopyable())
1213 Result +=
"non-copyable ";
1214 Result +=
"cost:" + std::to_string(
N->getIndividualCost());
1219 const SplitGraph &SG) {
1220 return N->hasAnyIncomingEdges() ?
"" :
"color=\"red\"";
1224 SplitGraphEdgeDstIterator EI,
1225 const SplitGraph &SG) {
1227 switch ((*EI.getCurrent())->Kind) {
1228 case SplitGraph::EdgeKind::DirectCall:
1230 case SplitGraph::EdgeKind::IndirectCall:
1231 return "style=\"dashed\"";
1246static bool needsConservativeImport(
const GlobalValue *GV) {
1248 return Var->hasLocalLinkage();
1250 return GA->hasLocalLinkage();
1256static void printPartitionSummary(raw_ostream &OS,
unsigned N,
const Module &M,
1257 unsigned PartCost,
unsigned ModuleCost) {
1258 OS <<
"*** Partition P" <<
N <<
" ***\n";
1260 for (
const auto &Fn : M) {
1261 if (!Fn.isDeclaration())
1262 OS <<
" - [function] " << Fn.getName() <<
"\n";
1265 for (
const auto &GV :
M.globals()) {
1266 if (GV.hasInitializer())
1267 OS <<
" - [global] " << GV.getName() <<
"\n";
1270 OS <<
"Partition contains " << formatRatioOf(PartCost, ModuleCost)
1271 <<
"% of the source\n";
1274static void evaluateProposal(
const AMDGPUOptions &CLOpts, SplitProposal &Best,
1275 SplitProposal New) {
1276 SplitModuleTimer SMT(
"proposal_evaluation",
"proposal ranking algorithm");
1279 New.verifyCompleteness();
1280 if (CLOpts.module_splitting_debug_proposal_search)
1284 const double CurBScore = Best.getBottleneckScore();
1285 const double CurCSScore = Best.getCodeSizeScore();
1286 const double NewBScore =
New.getBottleneckScore();
1287 const double NewCSScore =
New.getCodeSizeScore();
1299 bool IsBest =
false;
1300 if (NewBScore < CurBScore)
1302 else if (NewBScore == CurBScore)
1303 IsBest = (NewCSScore < CurCSScore);
1306 Best = std::move(New);
1308 LLVM_DEBUG(
if (CLOpts.module_splitting_debug_proposal_search) {
1310 dbgs() <<
"[search] new best proposal!\n";
1312 dbgs() <<
"[search] discarding - not profitable\n";
1317static std::unique_ptr<Module> cloneAll(
const Module &M) {
1319 return CloneModule(M, VMap, [&](
const GlobalValue *GV) {
return true; });
1323static void writeDOTGraph(
const AMDGPUOptions &CLOpts,
const SplitGraph &SG) {
1324 StringRef ModuleDotCfgOutput = CLOpts.module_splitting_print_module_dotcfg;
1325 if (ModuleDotCfgOutput.empty())
1329 raw_fd_ostream OS(ModuleDotCfgOutput, EC);
1331 errs() <<
"[" DEBUG_TYPE "]: cannot open '" << ModuleDotCfgOutput
1332 <<
"' - DOTGraph will not be printed\n";
1335 SG.getModule().getName());
1338static void splitAMDGPUModule(
1340 function_ref<
void(std::unique_ptr<Module> MPart)> ModuleCallback) {
1360 if (CLOpts.module_splitting_externalize_address_taken) {
1361 for (
auto &Fn : M) {
1362 if (Fn.hasLocalLinkage() && Fn.hasAddressTaken()) {
1364 dbgs() <<
" because its address is taken\n");
1372 if (CLOpts.module_splitting_externalize_globals) {
1373 for (
auto &GV :
M.globals()) {
1374 if (GV.hasLocalLinkage())
1375 LLVM_DEBUG(
dbgs() <<
"[externalize] GV " << GV.getName() <<
'\n');
1380 for (
auto &GA :
M.aliases()) {
1381 if (GA.hasLocalLinkage()) {
1382 LLVM_DEBUG(
dbgs() <<
"[externalize] alias " << GA.getName() <<
'\n');
1389 FunctionsCostMap FnCosts;
1390 const CostType ModuleCost = calculateFunctionCosts(GetTTI, M, FnCosts);
1394 SplitGraph SG(M, FnCosts, ModuleCost);
1400 <<
"[!] no nodes in graph, input is empty - no splitting possible\n");
1401 ModuleCallback(cloneAll(M));
1406 dbgs() <<
"[graph] nodes:\n";
1407 for (
const SplitGraph::Node *
N : SG.nodes()) {
1408 dbgs() <<
" - [" <<
N->getID() <<
"]: " <<
N->getName() <<
" "
1409 << (
N->isGraphEntryPoint() ?
"(entry)" :
"") <<
" "
1410 << (
N->isNonCopyable() ?
"(noncopyable)" :
"") <<
"\n";
1414 writeDOTGraph(CLOpts, SG);
1418 std::optional<SplitProposal> Proposal;
1419 const auto EvaluateProposal = [&](SplitProposal
SP) {
1420 SP.calculateScores();
1422 Proposal = std::move(SP);
1424 evaluateProposal(CLOpts, *Proposal, std::move(SP));
1429 RecursiveSearchSplitting(CLOpts, SG,
NumParts, EvaluateProposal).run();
1430 LLVM_DEBUG(
if (Proposal)
dbgs() <<
"[search done] selected proposal: "
1431 << Proposal->getName() <<
"\n";);
1434 LLVM_DEBUG(
dbgs() <<
"[!] no proposal made, no splitting possible!\n");
1435 ModuleCallback(cloneAll(M));
1441 std::optional<raw_fd_ostream> SummariesOS;
1442 StringRef PartitionSummariesOutput =
1443 CLOpts.module_splitting_print_partition_summaries;
1444 if (!PartitionSummariesOutput.empty()) {
1446 SummariesOS.emplace(PartitionSummariesOutput, EC);
1448 errs() <<
"[" DEBUG_TYPE "]: cannot open '" << PartitionSummariesOutput
1449 <<
"' - Partition summaries will not be printed\n";
1454 bool ImportAllGVs =
true;
1456 for (
unsigned PID = 0; PID <
NumParts; ++PID) {
1457 SplitModuleTimer SMT2(
"modules_creation",
1458 "creating modules for each partition");
1461 DenseSet<const Function *> FnsInPart;
1462 for (
unsigned NodeID : (*Proposal)[PID].set_bits())
1463 FnsInPart.insert(&SG.getNode(NodeID).getFunction());
1466 if (FnsInPart.empty()) {
1468 <<
" is empty, not creating module\n");
1473 CostType PartCost = 0;
1474 std::unique_ptr<Module> MPart(
1478 if (FnsInPart.contains(Fn)) {
1479 PartCost += SG.getCost(*Fn);
1488 return FnsInPart.contains(Fn);
1492 return ImportAllGVs || needsConservativeImport(GV);
1495 ImportAllGVs =
false;
1499 if (needsConservativeImport(&GV) && GV.use_empty())
1500 GV.eraseFromParent();
1504 printPartitionSummary(*SummariesOS, PID, *MPart, PartCost, ModuleCost);
1507 printPartitionSummary(
dbgs(), PID, *MPart, PartCost, ModuleCost));
1509 ModuleCallback(std::move(MPart));
1516 SplitModuleTimer SMT(
1517 "total",
"total pass runtime (incl. potentially waiting for lockfile)");
1525 const AMDGPUOptions &CLOpts =
1526 M.getContext().getLibraryOptions().get<AMDGPUOptions>();
1529 if (CLOpts.module_splitting_serial_execution) {
1542 dbgs() <<
"[amdgpu-split-module] unable to acquire lockfile, debug "
1543 "output may be mangled by other processes\n");
1544 }
else if (!Owned) {
1553 <<
"[amdgpu-split-module] unable to acquire lockfile, debug "
1554 "output may be mangled by other processes\n");
1560 splitAMDGPUModule(CLOpts, TTIGetter, M, N, ModuleCallback);
1568 splitAMDGPUModule(CLOpts, 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(const AnalysisOptions &Opts, 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)
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)