12f09f445SMaksim Panchenko //===- bolt/Passes/ReorderFunctions.cpp - Function reordering pass --------===// 2a34c753fSRafael Auler // 3a34c753fSRafael Auler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4a34c753fSRafael Auler // See https://llvm.org/LICENSE.txt for license information. 5a34c753fSRafael Auler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6a34c753fSRafael Auler // 7a34c753fSRafael Auler //===----------------------------------------------------------------------===// 8a34c753fSRafael Auler // 92f09f445SMaksim Panchenko // This file implements ReorderFunctions class. 102f09f445SMaksim Panchenko // 11a34c753fSRafael Auler //===----------------------------------------------------------------------===// 12a34c753fSRafael Auler 13a34c753fSRafael Auler #include "bolt/Passes/ReorderFunctions.h" 14a34c753fSRafael Auler #include "bolt/Passes/HFSort.h" 15287508cdSAmir Ayupov #include "bolt/Utils/Utils.h" 16287508cdSAmir Ayupov #include "llvm/ADT/STLExtras.h" 17a34c753fSRafael Auler #include "llvm/Support/CommandLine.h" 18b402487bSspupyrev #include "llvm/Transforms/Utils/CodeLayout.h" 19a34c753fSRafael Auler #include <fstream> 20a34c753fSRafael Auler 21a34c753fSRafael Auler #define DEBUG_TYPE "hfsort" 22a34c753fSRafael Auler 23a34c753fSRafael Auler using namespace llvm; 24a34c753fSRafael Auler 25a34c753fSRafael Auler namespace opts { 26a34c753fSRafael Auler 27a34c753fSRafael Auler extern cl::OptionCategory BoltOptCategory; 28a34c753fSRafael Auler extern cl::opt<unsigned> Verbosity; 29a34c753fSRafael Auler extern cl::opt<uint32_t> RandomSeed; 30a34c753fSRafael Auler 31*aa9cc721SPeter Waller extern size_t padFunctionBefore(const bolt::BinaryFunction &Function); 32*aa9cc721SPeter Waller extern size_t padFunctionAfter(const bolt::BinaryFunction &Function); 33a34c753fSRafael Auler 349058503dSspupyrev extern cl::opt<bolt::ReorderFunctions::ReorderType> ReorderFunctions; 352b642926SAmir Ayupov cl::opt<bolt::ReorderFunctions::ReorderType> ReorderFunctions( 362b642926SAmir Ayupov "reorder-functions", 37a34c753fSRafael Auler cl::desc("reorder and cluster functions (works only with relocations)"), 38a34c753fSRafael Auler cl::init(bolt::ReorderFunctions::RT_NONE), 392b642926SAmir Ayupov cl::values(clEnumValN(bolt::ReorderFunctions::RT_NONE, "none", 40a34c753fSRafael Auler "do not reorder functions"), 412b642926SAmir Ayupov clEnumValN(bolt::ReorderFunctions::RT_EXEC_COUNT, "exec-count", 42a34c753fSRafael Auler "order by execution count"), 432b642926SAmir Ayupov clEnumValN(bolt::ReorderFunctions::RT_HFSORT, "hfsort", 44a34c753fSRafael Auler "use hfsort algorithm"), 452b642926SAmir Ayupov clEnumValN(bolt::ReorderFunctions::RT_HFSORT_PLUS, "hfsort+", 469058503dSspupyrev "use cache-directed sort"), 47287fcd38Sspupyrev clEnumValN(bolt::ReorderFunctions::RT_CDSORT, "cdsort", 48b402487bSspupyrev "use cache-directed sort"), 49a34c753fSRafael Auler clEnumValN(bolt::ReorderFunctions::RT_PETTIS_HANSEN, 502b642926SAmir Ayupov "pettis-hansen", "use Pettis-Hansen algorithm"), 512b642926SAmir Ayupov clEnumValN(bolt::ReorderFunctions::RT_RANDOM, "random", 52a34c753fSRafael Auler "reorder functions randomly"), 532b642926SAmir Ayupov clEnumValN(bolt::ReorderFunctions::RT_USER, "user", 54a34c753fSRafael Auler "use function order specified by -function-order")), 559058503dSspupyrev cl::ZeroOrMore, cl::cat(BoltOptCategory), 569058503dSspupyrev cl::callback([](const bolt::ReorderFunctions::ReorderType &option) { 579058503dSspupyrev if (option == bolt::ReorderFunctions::RT_HFSORT_PLUS) { 589058503dSspupyrev errs() << "BOLT-WARNING: '-reorder-functions=hfsort+' is deprecated," 599058503dSspupyrev << " please use '-reorder-functions=cdsort' instead\n"; 609058503dSspupyrev ReorderFunctions = bolt::ReorderFunctions::RT_CDSORT; 619058503dSspupyrev } 629058503dSspupyrev })); 63a34c753fSRafael Auler 64b92436efSFangrui Song static cl::opt<bool> ReorderFunctionsUseHotSize( 65b92436efSFangrui Song "reorder-functions-use-hot-size", 66b92436efSFangrui Song cl::desc("use a function's hot size when doing clustering"), cl::init(true), 67a34c753fSRafael Auler cl::cat(BoltOptCategory)); 68a34c753fSRafael Auler 692b642926SAmir Ayupov static cl::opt<std::string> FunctionOrderFile( 702b642926SAmir Ayupov "function-order", 71a34c753fSRafael Auler cl::desc("file containing an ordered list of functions to use for function " 72a34c753fSRafael Auler "reordering"), 73a34c753fSRafael Auler cl::cat(BoltOptCategory)); 74a34c753fSRafael Auler 752b642926SAmir Ayupov static cl::opt<std::string> GenerateFunctionOrderFile( 762b642926SAmir Ayupov "generate-function-order", 77a34c753fSRafael Auler cl::desc("file to dump the ordered list of functions to use for function " 78a34c753fSRafael Auler "reordering"), 79a34c753fSRafael Auler cl::cat(BoltOptCategory)); 80a34c753fSRafael Auler 812b642926SAmir Ayupov static cl::opt<std::string> LinkSectionsFile( 822b642926SAmir Ayupov "generate-link-sections", 83a34c753fSRafael Auler cl::desc("generate a list of function sections in a format suitable for " 84a34c753fSRafael Auler "inclusion in a linker script"), 85a34c753fSRafael Auler cl::cat(BoltOptCategory)); 86a34c753fSRafael Auler 87a34c753fSRafael Auler static cl::opt<bool> 88a34c753fSRafael Auler UseEdgeCounts("use-edge-counts", 89a34c753fSRafael Auler cl::desc("use edge count data when doing clustering"), 90b92436efSFangrui Song cl::init(true), cl::cat(BoltOptCategory)); 91a34c753fSRafael Auler 922b642926SAmir Ayupov static cl::opt<bool> CgFromPerfData( 932b642926SAmir Ayupov "cg-from-perf-data", 94a34c753fSRafael Auler cl::desc("use perf data directly when constructing the call graph" 95a34c753fSRafael Auler " for stale functions"), 962b642926SAmir Ayupov cl::init(true), cl::ZeroOrMore, cl::cat(BoltOptCategory)); 97a34c753fSRafael Auler 98b92436efSFangrui Song static cl::opt<bool> CgIgnoreRecursiveCalls( 99b92436efSFangrui Song "cg-ignore-recursive-calls", 100a34c753fSRafael Auler cl::desc("ignore recursive calls when constructing the call graph"), 101b92436efSFangrui Song cl::init(true), cl::cat(BoltOptCategory)); 102a34c753fSRafael Auler 1032b642926SAmir Ayupov static cl::opt<bool> CgUseSplitHotSize( 1042b642926SAmir Ayupov "cg-use-split-hot-size", 105a34c753fSRafael Auler cl::desc("use hot/cold data on basic blocks to determine hot sizes for " 106a34c753fSRafael Auler "call graph functions"), 1072b642926SAmir Ayupov cl::init(false), cl::ZeroOrMore, cl::cat(BoltOptCategory)); 108a34c753fSRafael Auler 109a34c753fSRafael Auler } // namespace opts 110a34c753fSRafael Auler 111a34c753fSRafael Auler namespace llvm { 112a34c753fSRafael Auler namespace bolt { 113a34c753fSRafael Auler 114a34c753fSRafael Auler using NodeId = CallGraph::NodeId; 115a34c753fSRafael Auler using Arc = CallGraph::Arc; 116a34c753fSRafael Auler using Node = CallGraph::Node; 117a34c753fSRafael Auler 11852cf0711SAmir Ayupov void ReorderFunctions::reorder(BinaryContext &BC, 11952cf0711SAmir Ayupov std::vector<Cluster> &&Clusters, 120a34c753fSRafael Auler std::map<uint64_t, BinaryFunction> &BFs) { 121a34c753fSRafael Auler std::vector<uint64_t> FuncAddr(Cg.numNodes()); // Just for computing stats 122a34c753fSRafael Auler uint64_t TotalSize = 0; 123a34c753fSRafael Auler uint32_t Index = 0; 124a34c753fSRafael Auler 125a34c753fSRafael Auler // Set order of hot functions based on clusters. 126a34c753fSRafael Auler for (const Cluster &Cluster : Clusters) { 127a34c753fSRafael Auler for (const NodeId FuncId : Cluster.targets()) { 128a34c753fSRafael Auler Cg.nodeIdToFunc(FuncId)->setIndex(Index++); 129a34c753fSRafael Auler FuncAddr[FuncId] = TotalSize; 130a34c753fSRafael Auler TotalSize += Cg.size(FuncId); 131a34c753fSRafael Auler } 132a34c753fSRafael Auler } 133a34c753fSRafael Auler 134fd960495SVladislav Khmelevsky // Assign valid index for functions with valid profile. 135fd960495SVladislav Khmelevsky for (auto &It : BFs) { 136fd960495SVladislav Khmelevsky BinaryFunction &BF = It.second; 137fd960495SVladislav Khmelevsky if (!BF.hasValidIndex() && BF.hasValidProfile()) 138fd960495SVladislav Khmelevsky BF.setIndex(Index++); 139fd960495SVladislav Khmelevsky } 140fd960495SVladislav Khmelevsky 141a34c753fSRafael Auler if (opts::ReorderFunctions == RT_NONE) 142a34c753fSRafael Auler return; 143a34c753fSRafael Auler 14452cf0711SAmir Ayupov printStats(BC, Clusters, FuncAddr); 14586b47f14SAmir Ayupov } 14686b47f14SAmir Ayupov 14752cf0711SAmir Ayupov void ReorderFunctions::printStats(BinaryContext &BC, 14852cf0711SAmir Ayupov const std::vector<Cluster> &Clusters, 14986b47f14SAmir Ayupov const std::vector<uint64_t> &FuncAddr) { 150a34c753fSRafael Auler if (opts::Verbosity == 0) { 151a34c753fSRafael Auler #ifndef NDEBUG 152a34c753fSRafael Auler if (!DebugFlag || !isCurrentDebugType("hfsort")) 153a34c753fSRafael Auler return; 154a34c753fSRafael Auler #else 155a34c753fSRafael Auler return; 156a34c753fSRafael Auler #endif 157a34c753fSRafael Auler } 158a34c753fSRafael Auler 159a34c753fSRafael Auler bool PrintDetailed = opts::Verbosity > 1; 160a34c753fSRafael Auler #ifndef NDEBUG 161a34c753fSRafael Auler PrintDetailed |= 162a34c753fSRafael Auler (DebugFlag && isCurrentDebugType("hfsort") && opts::Verbosity > 0); 163a34c753fSRafael Auler #endif 16486b47f14SAmir Ayupov uint64_t TotalSize = 0; 165a34c753fSRafael Auler uint64_t CurPage = 0; 166a34c753fSRafael Auler uint64_t Hotfuncs = 0; 167a34c753fSRafael Auler double TotalDistance = 0; 168a34c753fSRafael Auler double TotalCalls = 0; 169a34c753fSRafael Auler double TotalCalls64B = 0; 170a34c753fSRafael Auler double TotalCalls4KB = 0; 171a34c753fSRafael Auler double TotalCalls2MB = 0; 172f92ab6afSAmir Ayupov if (PrintDetailed) 17352cf0711SAmir Ayupov BC.outs() << "BOLT-INFO: Function reordering page layout\n" 174a34c753fSRafael Auler << "BOLT-INFO: ============== page 0 ==============\n"; 17586b47f14SAmir Ayupov for (const Cluster &Cluster : Clusters) { 176f92ab6afSAmir Ayupov if (PrintDetailed) 17752cf0711SAmir Ayupov BC.outs() << format( 17840c2e0faSMaksim Panchenko "BOLT-INFO: -------- density = %.3lf (%u / %u) --------\n", 179a34c753fSRafael Auler Cluster.density(), Cluster.samples(), Cluster.size()); 180a34c753fSRafael Auler 181a34c753fSRafael Auler for (NodeId FuncId : Cluster.targets()) { 182a34c753fSRafael Auler if (Cg.samples(FuncId) > 0) { 183a34c753fSRafael Auler Hotfuncs++; 184a34c753fSRafael Auler 185f92ab6afSAmir Ayupov if (PrintDetailed) 18652cf0711SAmir Ayupov BC.outs() << "BOLT-INFO: hot func " << *Cg.nodeIdToFunc(FuncId) 18752cf0711SAmir Ayupov << " (" << Cg.size(FuncId) << ")\n"; 188a34c753fSRafael Auler 189a34c753fSRafael Auler uint64_t Dist = 0; 190a34c753fSRafael Auler uint64_t Calls = 0; 191a34c753fSRafael Auler for (NodeId Dst : Cg.successors(FuncId)) { 192a34c753fSRafael Auler if (FuncId == Dst) // ignore recursive calls in stats 193a34c753fSRafael Auler continue; 194a34c753fSRafael Auler const Arc &Arc = *Cg.findArc(FuncId, Dst); 195a34c753fSRafael Auler const auto D = std::abs(FuncAddr[Arc.dst()] - 196a34c753fSRafael Auler (FuncAddr[FuncId] + Arc.avgCallOffset())); 197a34c753fSRafael Auler const double W = Arc.weight(); 198f92ab6afSAmir Ayupov if (D < 64 && PrintDetailed && opts::Verbosity > 2) 19952cf0711SAmir Ayupov BC.outs() << "BOLT-INFO: short (" << D << "B) call:\n" 20052cf0711SAmir Ayupov << "BOLT-INFO: Src: " << *Cg.nodeIdToFunc(FuncId) 20152cf0711SAmir Ayupov << "\n" 202a34c753fSRafael Auler << "BOLT-INFO: Dst: " << *Cg.nodeIdToFunc(Dst) << "\n" 203a34c753fSRafael Auler << "BOLT-INFO: Weight = " << W << "\n" 2042b642926SAmir Ayupov << "BOLT-INFO: AvgOffset = " << Arc.avgCallOffset() 2052b642926SAmir Ayupov << "\n"; 206a34c753fSRafael Auler Calls += W; 2072b642926SAmir Ayupov if (D < 64) 2082b642926SAmir Ayupov TotalCalls64B += W; 2092b642926SAmir Ayupov if (D < 4096) 2102b642926SAmir Ayupov TotalCalls4KB += W; 2112b642926SAmir Ayupov if (D < (2 << 20)) 2122b642926SAmir Ayupov TotalCalls2MB += W; 213a34c753fSRafael Auler Dist += Arc.weight() * D; 214f92ab6afSAmir Ayupov if (PrintDetailed) 21552cf0711SAmir Ayupov BC.outs() << format("BOLT-INFO: arc: %u [@%lu+%.1lf] -> %u [@%lu]: " 216a34c753fSRafael Auler "weight = %.0lf, callDist = %f\n", 2172b642926SAmir Ayupov Arc.src(), FuncAddr[Arc.src()], 2182b642926SAmir Ayupov Arc.avgCallOffset(), Arc.dst(), 2192b642926SAmir Ayupov FuncAddr[Arc.dst()], Arc.weight(), D); 220a34c753fSRafael Auler } 221a34c753fSRafael Auler TotalCalls += Calls; 222a34c753fSRafael Auler TotalDistance += Dist; 223a34c753fSRafael Auler TotalSize += Cg.size(FuncId); 224a34c753fSRafael Auler 225a34c753fSRafael Auler if (PrintDetailed) { 22652cf0711SAmir Ayupov BC.outs() << format("BOLT-INFO: start = %6u : avgCallDist = %lu : ", 22740c2e0faSMaksim Panchenko TotalSize, Calls ? Dist / Calls : 0) 228a34c753fSRafael Auler << Cg.nodeIdToFunc(FuncId)->getPrintName() << '\n'; 229a34c753fSRafael Auler const uint64_t NewPage = TotalSize / HugePageSize; 230a34c753fSRafael Auler if (NewPage != CurPage) { 231a34c753fSRafael Auler CurPage = NewPage; 23252cf0711SAmir Ayupov BC.outs() << format( 23340c2e0faSMaksim Panchenko "BOLT-INFO: ============== page %u ==============\n", CurPage); 234a34c753fSRafael Auler } 235a34c753fSRafael Auler } 236a34c753fSRafael Auler } 237a34c753fSRafael Auler } 238a34c753fSRafael Auler } 23952cf0711SAmir Ayupov BC.outs() << "BOLT-INFO: Function reordering stats\n" 240a34c753fSRafael Auler << format("BOLT-INFO: Number of hot functions: %u\n" 241a34c753fSRafael Auler "BOLT-INFO: Number of clusters: %lu\n", 242a34c753fSRafael Auler Hotfuncs, Clusters.size()) 243a34c753fSRafael Auler << format("BOLT-INFO: Final average call distance = %.1lf " 244a34c753fSRafael Auler "(%.0lf / %.0lf)\n", 24552cf0711SAmir Ayupov TotalCalls ? TotalDistance / TotalCalls : 0, 24652cf0711SAmir Ayupov TotalDistance, TotalCalls) 247a34c753fSRafael Auler << format("BOLT-INFO: Total Calls = %.0lf\n", TotalCalls); 248f92ab6afSAmir Ayupov if (TotalCalls) 24952cf0711SAmir Ayupov BC.outs() 25052cf0711SAmir Ayupov << format("BOLT-INFO: Total Calls within 64B = %.0lf (%.2lf%%)\n", 251a34c753fSRafael Auler TotalCalls64B, 100 * TotalCalls64B / TotalCalls) 252a34c753fSRafael Auler << format("BOLT-INFO: Total Calls within 4KB = %.0lf (%.2lf%%)\n", 253a34c753fSRafael Auler TotalCalls4KB, 100 * TotalCalls4KB / TotalCalls) 254a34c753fSRafael Auler << format("BOLT-INFO: Total Calls within 2MB = %.0lf (%.2lf%%)\n", 255a34c753fSRafael Auler TotalCalls2MB, 100 * TotalCalls2MB / TotalCalls); 256a34c753fSRafael Auler } 257a34c753fSRafael Auler 25813d60ce2SAmir Ayupov Error ReorderFunctions::readFunctionOrderFile( 25913d60ce2SAmir Ayupov std::vector<std::string> &FunctionNames) { 260a34c753fSRafael Auler std::ifstream FuncsFile(opts::FunctionOrderFile, std::ios::in); 26113d60ce2SAmir Ayupov if (!FuncsFile) 26213d60ce2SAmir Ayupov return createFatalBOLTError(Twine("Ordered functions file \"") + 26313d60ce2SAmir Ayupov Twine(opts::FunctionOrderFile) + 26413d60ce2SAmir Ayupov Twine("\" can't be opened.")); 26513d60ce2SAmir Ayupov 266a34c753fSRafael Auler std::string FuncName; 267f92ab6afSAmir Ayupov while (std::getline(FuncsFile, FuncName)) 268a34c753fSRafael Auler FunctionNames.push_back(FuncName); 26913d60ce2SAmir Ayupov return Error::success(); 270a34c753fSRafael Auler } 271a34c753fSRafael Auler 272a5f3d1a8SAmir Ayupov Error ReorderFunctions::runOnFunctions(BinaryContext &BC) { 273a34c753fSRafael Auler auto &BFs = BC.getBinaryFunctions(); 274a34c753fSRafael Auler if (opts::ReorderFunctions != RT_NONE && 275a34c753fSRafael Auler opts::ReorderFunctions != RT_EXEC_COUNT && 276a34c753fSRafael Auler opts::ReorderFunctions != RT_USER) { 277ac830664SFabian Parzefall Cg = buildCallGraph( 278ac830664SFabian Parzefall BC, 279a34c753fSRafael Auler [](const BinaryFunction &BF) { 280a34c753fSRafael Auler if (!BF.hasProfile()) 281a34c753fSRafael Auler return true; 282a34c753fSRafael Auler if (BF.getState() != BinaryFunction::State::CFG) 283a34c753fSRafael Auler return true; 284a34c753fSRafael Auler return false; 285a34c753fSRafael Auler }, 286a34c753fSRafael Auler opts::CgFromPerfData, 287ac830664SFabian Parzefall /*IncludeSplitCalls=*/false, opts::ReorderFunctionsUseHotSize, 288ac830664SFabian Parzefall opts::CgUseSplitHotSize, opts::UseEdgeCounts, 289a34c753fSRafael Auler opts::CgIgnoreRecursiveCalls); 290a34c753fSRafael Auler Cg.normalizeArcWeights(); 291a34c753fSRafael Auler } 292a34c753fSRafael Auler 293a34c753fSRafael Auler std::vector<Cluster> Clusters; 294a34c753fSRafael Auler 295a34c753fSRafael Auler switch (opts::ReorderFunctions) { 296a34c753fSRafael Auler case RT_NONE: 297a34c753fSRafael Auler break; 2982b642926SAmir Ayupov case RT_EXEC_COUNT: { 299a34c753fSRafael Auler std::vector<BinaryFunction *> SortedFunctions(BFs.size()); 30072e5b14fSAmir Ayupov llvm::transform(llvm::make_second_range(BFs), SortedFunctions.begin(), 30172e5b14fSAmir Ayupov [](BinaryFunction &BF) { return &BF; }); 3029460ebd1Sspupyrev llvm::stable_sort(SortedFunctions, 3039460ebd1Sspupyrev [&](const BinaryFunction *A, const BinaryFunction *B) { 304a34c753fSRafael Auler if (A->isIgnored()) 305a34c753fSRafael Auler return false; 3069460ebd1Sspupyrev if (B->isIgnored()) 3079460ebd1Sspupyrev return true; 308*aa9cc721SPeter Waller const size_t PadA = opts::padFunctionBefore(*A) + 309*aa9cc721SPeter Waller opts::padFunctionAfter(*A); 310*aa9cc721SPeter Waller const size_t PadB = opts::padFunctionBefore(*B) + 311*aa9cc721SPeter Waller opts::padFunctionAfter(*B); 312a34c753fSRafael Auler if (!PadA || !PadB) { 313a34c753fSRafael Auler if (PadA) 314a34c753fSRafael Auler return true; 315a34c753fSRafael Auler if (PadB) 316a34c753fSRafael Auler return false; 317a34c753fSRafael Auler } 3189460ebd1Sspupyrev if (!A->hasProfile()) 3199460ebd1Sspupyrev return false; 3209460ebd1Sspupyrev if (!B->hasProfile()) 3219460ebd1Sspupyrev return true; 3229460ebd1Sspupyrev return A->getExecutionCount() > B->getExecutionCount(); 323a34c753fSRafael Auler }); 3249460ebd1Sspupyrev uint32_t Index = 0; 325f92ab6afSAmir Ayupov for (BinaryFunction *BF : SortedFunctions) 3269460ebd1Sspupyrev if (BF->hasProfile()) { 327a34c753fSRafael Auler BF->setIndex(Index++); 3289460ebd1Sspupyrev LLVM_DEBUG(if (opts::Verbosity > 1) { 3299460ebd1Sspupyrev dbgs() << "BOLT-INFO: hot func " << BF->getPrintName() << " (" 3309460ebd1Sspupyrev << BF->getExecutionCount() << ")\n"; 3319460ebd1Sspupyrev }); 3329460ebd1Sspupyrev } 3332b642926SAmir Ayupov } break; 334a34c753fSRafael Auler case RT_HFSORT: 335a34c753fSRafael Auler Clusters = clusterize(Cg); 336a34c753fSRafael Auler break; 337287fcd38Sspupyrev case RT_CDSORT: { 338b402487bSspupyrev // It is required that the sum of incoming arc weights is not greater 339b402487bSspupyrev // than the number of samples for every function. Ensuring the call graph 340b402487bSspupyrev // obeys the property before running the algorithm. 341b402487bSspupyrev Cg.adjustArcWeights(); 342b402487bSspupyrev 343b402487bSspupyrev // Initialize CFG nodes and their data 344b402487bSspupyrev std::vector<uint64_t> FuncSizes; 345b402487bSspupyrev std::vector<uint64_t> FuncCounts; 3466b8d04c2SFangrui Song std::vector<codelayout::EdgeCount> CallCounts; 347b402487bSspupyrev std::vector<uint64_t> CallOffsets; 348b402487bSspupyrev for (NodeId F = 0; F < Cg.numNodes(); ++F) { 349b402487bSspupyrev FuncSizes.push_back(Cg.size(F)); 350b402487bSspupyrev FuncCounts.push_back(Cg.samples(F)); 351b402487bSspupyrev for (NodeId Succ : Cg.successors(F)) { 352b402487bSspupyrev const Arc &Arc = *Cg.findArc(F, Succ); 3536b8d04c2SFangrui Song CallCounts.push_back({F, Succ, uint64_t(Arc.weight())}); 354b402487bSspupyrev CallOffsets.push_back(uint64_t(Arc.avgCallOffset())); 355b402487bSspupyrev } 356b402487bSspupyrev } 357b402487bSspupyrev 358b402487bSspupyrev // Run the layout algorithm. 3596b8d04c2SFangrui Song std::vector<uint64_t> Result = codelayout::computeCacheDirectedLayout( 3606b8d04c2SFangrui Song FuncSizes, FuncCounts, CallCounts, CallOffsets); 361b402487bSspupyrev 362b402487bSspupyrev // Create a single cluster from the computed order of hot functions. 363299ec3c2Sspupyrev std::vector<CallGraph::NodeId> NodeOrder(Result.begin(), Result.end()); 364299ec3c2Sspupyrev Clusters.emplace_back(Cluster(NodeOrder, Cg)); 365b402487bSspupyrev } break; 366a34c753fSRafael Auler case RT_PETTIS_HANSEN: 367a34c753fSRafael Auler Clusters = pettisAndHansen(Cg); 368a34c753fSRafael Auler break; 369a34c753fSRafael Auler case RT_RANDOM: 370a34c753fSRafael Auler std::srand(opts::RandomSeed); 371a34c753fSRafael Auler Clusters = randomClusters(Cg); 372a34c753fSRafael Auler break; 3732b642926SAmir Ayupov case RT_USER: { 374287508cdSAmir Ayupov // Build LTOCommonNameMap 375287508cdSAmir Ayupov StringMap<std::vector<uint64_t>> LTOCommonNameMap; 376287508cdSAmir Ayupov for (const BinaryFunction &BF : llvm::make_second_range(BFs)) 377287508cdSAmir Ayupov for (StringRef Name : BF.getNames()) 378287508cdSAmir Ayupov if (std::optional<StringRef> LTOCommonName = getLTOCommonName(Name)) 379287508cdSAmir Ayupov LTOCommonNameMap[*LTOCommonName].push_back(BF.getAddress()); 380287508cdSAmir Ayupov 381a34c753fSRafael Auler uint32_t Index = 0; 382e2007405SAmir Ayupov uint32_t InvalidEntries = 0; 38313d60ce2SAmir Ayupov std::vector<std::string> FunctionNames; 38413d60ce2SAmir Ayupov if (Error E = readFunctionOrderFile(FunctionNames)) 38513d60ce2SAmir Ayupov return Error(std::move(E)); 38613d60ce2SAmir Ayupov 38713d60ce2SAmir Ayupov for (const std::string &Function : FunctionNames) { 388a34c753fSRafael Auler std::vector<uint64_t> FuncAddrs; 389a34c753fSRafael Auler 390a34c753fSRafael Auler BinaryData *BD = BC.getBinaryDataByName(Function); 391a34c753fSRafael Auler if (!BD) { 392287508cdSAmir Ayupov // If we can't find the main symbol name, look for alternates. 393a34c753fSRafael Auler uint32_t LocalID = 1; 3941bf531a5SKazu Hirata while (true) { 3952b642926SAmir Ayupov const std::string FuncName = Function + "/" + std::to_string(LocalID); 396a34c753fSRafael Auler BD = BC.getBinaryDataByName(FuncName); 397a34c753fSRafael Auler if (BD) 398a34c753fSRafael Auler FuncAddrs.push_back(BD->getAddress()); 399a34c753fSRafael Auler else 400a34c753fSRafael Auler break; 401a34c753fSRafael Auler LocalID++; 402a34c753fSRafael Auler } 403287508cdSAmir Ayupov // Strip LTO suffixes 404287508cdSAmir Ayupov if (std::optional<StringRef> CommonName = getLTOCommonName(Function)) 4054e585e51SKazu Hirata if (LTOCommonNameMap.contains(*CommonName)) 406287508cdSAmir Ayupov llvm::append_range(FuncAddrs, LTOCommonNameMap[*CommonName]); 407a34c753fSRafael Auler } else { 408a34c753fSRafael Auler FuncAddrs.push_back(BD->getAddress()); 409a34c753fSRafael Auler } 410a34c753fSRafael Auler 411a34c753fSRafael Auler if (FuncAddrs.empty()) { 412e2007405SAmir Ayupov if (opts::Verbosity >= 1) 41352cf0711SAmir Ayupov BC.errs() << "BOLT-WARNING: Reorder functions: can't find function " 414e2007405SAmir Ayupov << "for " << Function << "\n"; 415e2007405SAmir Ayupov ++InvalidEntries; 416a34c753fSRafael Auler continue; 417a34c753fSRafael Auler } 418a34c753fSRafael Auler 419a34c753fSRafael Auler for (const uint64_t FuncAddr : FuncAddrs) { 420a34c753fSRafael Auler const BinaryData *FuncBD = BC.getBinaryDataAtAddress(FuncAddr); 421a34c753fSRafael Auler assert(FuncBD); 422a34c753fSRafael Auler 423a34c753fSRafael Auler BinaryFunction *BF = BC.getFunctionForSymbol(FuncBD->getSymbol()); 424a34c753fSRafael Auler if (!BF) { 425e2007405SAmir Ayupov if (opts::Verbosity >= 1) 42652cf0711SAmir Ayupov BC.errs() << "BOLT-WARNING: Reorder functions: can't find function " 427e2007405SAmir Ayupov << "for " << Function << "\n"; 428e2007405SAmir Ayupov ++InvalidEntries; 429a34c753fSRafael Auler break; 430a34c753fSRafael Auler } 431f92ab6afSAmir Ayupov if (!BF->hasValidIndex()) 432a34c753fSRafael Auler BF->setIndex(Index++); 433f92ab6afSAmir Ayupov else if (opts::Verbosity > 0) 43452cf0711SAmir Ayupov BC.errs() << "BOLT-WARNING: Duplicate reorder entry for " << Function 435e2007405SAmir Ayupov << "\n"; 436a34c753fSRafael Auler } 437a34c753fSRafael Auler } 438e2007405SAmir Ayupov if (InvalidEntries) 43952cf0711SAmir Ayupov BC.errs() << "BOLT-WARNING: Reorder functions: can't find functions for " 440e2007405SAmir Ayupov << InvalidEntries << " entries in -function-order list\n"; 4412b642926SAmir Ayupov } break; 4429058503dSspupyrev 4439058503dSspupyrev default: 4449058503dSspupyrev llvm_unreachable("unexpected layout type"); 445a34c753fSRafael Auler } 446a34c753fSRafael Auler 44752cf0711SAmir Ayupov reorder(BC, std::move(Clusters), BFs); 448a34c753fSRafael Auler 449076bd22fSShatianWang BC.HasFinalizedFunctionOrder = true; 450076bd22fSShatianWang 451a34c753fSRafael Auler std::unique_ptr<std::ofstream> FuncsFile; 452a34c753fSRafael Auler if (!opts::GenerateFunctionOrderFile.empty()) { 45340c2e0faSMaksim Panchenko FuncsFile = std::make_unique<std::ofstream>(opts::GenerateFunctionOrderFile, 454a34c753fSRafael Auler std::ios::out); 455a34c753fSRafael Auler if (!FuncsFile) { 45652cf0711SAmir Ayupov BC.errs() << "BOLT-ERROR: ordered functions file " 457a34c753fSRafael Auler << opts::GenerateFunctionOrderFile << " cannot be opened\n"; 458fa7dd491SAmir Ayupov return createFatalBOLTError(""); 459a34c753fSRafael Auler } 460a34c753fSRafael Auler } 461a34c753fSRafael Auler 462a34c753fSRafael Auler std::unique_ptr<std::ofstream> LinkSectionsFile; 463a34c753fSRafael Auler if (!opts::LinkSectionsFile.empty()) { 464a34c753fSRafael Auler LinkSectionsFile = 46540c2e0faSMaksim Panchenko std::make_unique<std::ofstream>(opts::LinkSectionsFile, std::ios::out); 466a34c753fSRafael Auler if (!LinkSectionsFile) { 46752cf0711SAmir Ayupov BC.errs() << "BOLT-ERROR: link sections file " << opts::LinkSectionsFile 46840c2e0faSMaksim Panchenko << " cannot be opened\n"; 469fa7dd491SAmir Ayupov return createFatalBOLTError(""); 470a34c753fSRafael Auler } 471a34c753fSRafael Auler } 472a34c753fSRafael Auler 473a34c753fSRafael Auler if (FuncsFile || LinkSectionsFile) { 474a34c753fSRafael Auler std::vector<BinaryFunction *> SortedFunctions(BFs.size()); 47572e5b14fSAmir Ayupov llvm::transform(llvm::make_second_range(BFs), SortedFunctions.begin(), 47672e5b14fSAmir Ayupov [](BinaryFunction &BF) { return &BF; }); 477a34c753fSRafael Auler 478a34c753fSRafael Auler // Sort functions by index. 4794d2bc0adSEnna1 llvm::stable_sort(SortedFunctions, compareBinaryFunctionByIndex); 480a34c753fSRafael Auler 481a34c753fSRafael Auler for (const BinaryFunction *Func : SortedFunctions) { 482a34c753fSRafael Auler if (!Func->hasValidIndex()) 483a34c753fSRafael Auler break; 484a34c753fSRafael Auler if (Func->isPLTFunction()) 485a34c753fSRafael Auler continue; 486a34c753fSRafael Auler 487a34c753fSRafael Auler if (FuncsFile) 488a34c753fSRafael Auler *FuncsFile << Func->getOneName().str() << '\n'; 489a34c753fSRafael Auler 490a34c753fSRafael Auler if (LinkSectionsFile) { 491a34c753fSRafael Auler const char *Indent = ""; 492a34c753fSRafael Auler std::vector<StringRef> AllNames = Func->getNames(); 493d2c87699SAmir Ayupov llvm::sort(AllNames); 494a34c753fSRafael Auler for (StringRef Name : AllNames) { 495a34c753fSRafael Auler const size_t SlashPos = Name.find('/'); 496a34c753fSRafael Auler if (SlashPos != std::string::npos) { 497a34c753fSRafael Auler // Avoid duplicates for local functions. 498a34c753fSRafael Auler if (Name.find('/', SlashPos + 1) != std::string::npos) 499a34c753fSRafael Auler continue; 500a34c753fSRafael Auler Name = Name.substr(0, SlashPos); 501a34c753fSRafael Auler } 502a34c753fSRafael Auler *LinkSectionsFile << Indent << ".text." << Name.str() << '\n'; 503a34c753fSRafael Auler Indent = " "; 504a34c753fSRafael Auler } 505a34c753fSRafael Auler } 506a34c753fSRafael Auler } 507a34c753fSRafael Auler 508a34c753fSRafael Auler if (FuncsFile) { 509a34c753fSRafael Auler FuncsFile->close(); 51052cf0711SAmir Ayupov BC.outs() << "BOLT-INFO: dumped function order to " 511a34c753fSRafael Auler << opts::GenerateFunctionOrderFile << '\n'; 512a34c753fSRafael Auler } 513a34c753fSRafael Auler 514a34c753fSRafael Auler if (LinkSectionsFile) { 515a34c753fSRafael Auler LinkSectionsFile->close(); 51652cf0711SAmir Ayupov BC.outs() << "BOLT-INFO: dumped linker section order to " 517a34c753fSRafael Auler << opts::LinkSectionsFile << '\n'; 518a34c753fSRafael Auler } 519a34c753fSRafael Auler } 520a5f3d1a8SAmir Ayupov return Error::success(); 521a34c753fSRafael Auler } 522a34c753fSRafael Auler 523a34c753fSRafael Auler } // namespace bolt 524a34c753fSRafael Auler } // namespace llvm 525