17ec26b23SKyungwoo Lee //===-- StableFunctionMap.cpp ---------------------------------------------===// 27ec26b23SKyungwoo Lee // 37ec26b23SKyungwoo Lee // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 47ec26b23SKyungwoo Lee // See https://llvm.org/LICENSE.txt for license information. 57ec26b23SKyungwoo Lee // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 67ec26b23SKyungwoo Lee // 77ec26b23SKyungwoo Lee //===----------------------------------------------------------------------===// 87ec26b23SKyungwoo Lee // 97ec26b23SKyungwoo Lee // This implements the functionality for the StableFunctionMap class, which 107ec26b23SKyungwoo Lee // manages the mapping of stable function hashes to their metadata. It includes 117ec26b23SKyungwoo Lee // methods for inserting, merging, and finalizing function entries, as well as 127ec26b23SKyungwoo Lee // utilities for handling function names and IDs. 137ec26b23SKyungwoo Lee // 147ec26b23SKyungwoo Lee //===----------------------------------------------------------------------===// 157ec26b23SKyungwoo Lee 167ec26b23SKyungwoo Lee #include "llvm/CGData/StableFunctionMap.h" 17*fe69a20cSKyungwoo Lee #include "llvm/ADT/SmallSet.h" 18d23c5c2dSKyungwoo Lee #include "llvm/Support/CommandLine.h" 19d23c5c2dSKyungwoo Lee #include "llvm/Support/Debug.h" 207ec26b23SKyungwoo Lee 217ec26b23SKyungwoo Lee #define DEBUG_TYPE "stable-function-map" 227ec26b23SKyungwoo Lee 237ec26b23SKyungwoo Lee using namespace llvm; 247ec26b23SKyungwoo Lee 25d23c5c2dSKyungwoo Lee static cl::opt<unsigned> 26d23c5c2dSKyungwoo Lee GlobalMergingMinMerges("global-merging-min-merges", 27d23c5c2dSKyungwoo Lee cl::desc("Minimum number of similar functions with " 28d23c5c2dSKyungwoo Lee "the same hash required for merging."), 29d23c5c2dSKyungwoo Lee cl::init(2), cl::Hidden); 30d23c5c2dSKyungwoo Lee static cl::opt<unsigned> GlobalMergingMinInstrs( 31d23c5c2dSKyungwoo Lee "global-merging-min-instrs", 32d23c5c2dSKyungwoo Lee cl::desc("The minimum instruction count required when merging functions."), 33d23c5c2dSKyungwoo Lee cl::init(1), cl::Hidden); 34d23c5c2dSKyungwoo Lee static cl::opt<unsigned> GlobalMergingMaxParams( 35d23c5c2dSKyungwoo Lee "global-merging-max-params", 36d23c5c2dSKyungwoo Lee cl::desc( 37d23c5c2dSKyungwoo Lee "The maximum number of parameters allowed when merging functions."), 38d23c5c2dSKyungwoo Lee cl::init(std::numeric_limits<unsigned>::max()), cl::Hidden); 39*fe69a20cSKyungwoo Lee static cl::opt<bool> GlobalMergingSkipNoParams( 40*fe69a20cSKyungwoo Lee "global-merging-skip-no-params", 41*fe69a20cSKyungwoo Lee cl::desc("Skip merging functions with no parameters."), cl::init(true), 42*fe69a20cSKyungwoo Lee cl::Hidden); 43*fe69a20cSKyungwoo Lee static cl::opt<double> GlobalMergingInstOverhead( 44*fe69a20cSKyungwoo Lee "global-merging-inst-overhead", 45*fe69a20cSKyungwoo Lee cl::desc("The overhead cost associated with each instruction when lowering " 46*fe69a20cSKyungwoo Lee "to machine instruction."), 47*fe69a20cSKyungwoo Lee cl::init(1.2), cl::Hidden); 48*fe69a20cSKyungwoo Lee static cl::opt<double> GlobalMergingParamOverhead( 49d23c5c2dSKyungwoo Lee "global-merging-param-overhead", 50d23c5c2dSKyungwoo Lee cl::desc("The overhead cost associated with each parameter when merging " 51d23c5c2dSKyungwoo Lee "functions."), 52*fe69a20cSKyungwoo Lee cl::init(2.0), cl::Hidden); 53*fe69a20cSKyungwoo Lee static cl::opt<double> 54d23c5c2dSKyungwoo Lee GlobalMergingCallOverhead("global-merging-call-overhead", 55d23c5c2dSKyungwoo Lee cl::desc("The overhead cost associated with each " 56d23c5c2dSKyungwoo Lee "function call when merging functions."), 57*fe69a20cSKyungwoo Lee cl::init(1.0), cl::Hidden); 58*fe69a20cSKyungwoo Lee static cl::opt<double> GlobalMergingExtraThreshold( 59d23c5c2dSKyungwoo Lee "global-merging-extra-threshold", 60d23c5c2dSKyungwoo Lee cl::desc("An additional cost threshold that must be exceeded for merging " 61d23c5c2dSKyungwoo Lee "to be considered beneficial."), 62*fe69a20cSKyungwoo Lee cl::init(0.0), cl::Hidden); 63d23c5c2dSKyungwoo Lee 647ec26b23SKyungwoo Lee unsigned StableFunctionMap::getIdOrCreateForName(StringRef Name) { 657ec26b23SKyungwoo Lee auto It = NameToId.find(Name); 667ec26b23SKyungwoo Lee if (It != NameToId.end()) 677ec26b23SKyungwoo Lee return It->second; 687ec26b23SKyungwoo Lee unsigned Id = IdToName.size(); 697ec26b23SKyungwoo Lee assert(Id == NameToId.size() && "ID collision"); 707ec26b23SKyungwoo Lee IdToName.emplace_back(Name.str()); 717ec26b23SKyungwoo Lee NameToId[IdToName.back()] = Id; 727ec26b23SKyungwoo Lee return Id; 737ec26b23SKyungwoo Lee } 747ec26b23SKyungwoo Lee 757ec26b23SKyungwoo Lee std::optional<std::string> StableFunctionMap::getNameForId(unsigned Id) const { 767ec26b23SKyungwoo Lee if (Id >= IdToName.size()) 777ec26b23SKyungwoo Lee return std::nullopt; 787ec26b23SKyungwoo Lee return IdToName[Id]; 797ec26b23SKyungwoo Lee } 807ec26b23SKyungwoo Lee 817ec26b23SKyungwoo Lee void StableFunctionMap::insert(const StableFunction &Func) { 827ec26b23SKyungwoo Lee assert(!Finalized && "Cannot insert after finalization"); 837ec26b23SKyungwoo Lee auto FuncNameId = getIdOrCreateForName(Func.FunctionName); 847ec26b23SKyungwoo Lee auto ModuleNameId = getIdOrCreateForName(Func.ModuleName); 857ec26b23SKyungwoo Lee auto IndexOperandHashMap = std::make_unique<IndexOperandHashMapType>(); 867ec26b23SKyungwoo Lee for (auto &[Index, Hash] : Func.IndexOperandHashes) 877ec26b23SKyungwoo Lee (*IndexOperandHashMap)[Index] = Hash; 887ec26b23SKyungwoo Lee auto FuncEntry = std::make_unique<StableFunctionEntry>( 897ec26b23SKyungwoo Lee Func.Hash, FuncNameId, ModuleNameId, Func.InstCount, 907ec26b23SKyungwoo Lee std::move(IndexOperandHashMap)); 917ec26b23SKyungwoo Lee insert(std::move(FuncEntry)); 927ec26b23SKyungwoo Lee } 937ec26b23SKyungwoo Lee 947ec26b23SKyungwoo Lee void StableFunctionMap::merge(const StableFunctionMap &OtherMap) { 957ec26b23SKyungwoo Lee assert(!Finalized && "Cannot merge after finalization"); 967ec26b23SKyungwoo Lee for (auto &[Hash, Funcs] : OtherMap.HashToFuncs) { 977ec26b23SKyungwoo Lee auto &ThisFuncs = HashToFuncs[Hash]; 987ec26b23SKyungwoo Lee for (auto &Func : Funcs) { 997ec26b23SKyungwoo Lee auto FuncNameId = 1007ec26b23SKyungwoo Lee getIdOrCreateForName(*OtherMap.getNameForId(Func->FunctionNameId)); 1017ec26b23SKyungwoo Lee auto ModuleNameId = 1027ec26b23SKyungwoo Lee getIdOrCreateForName(*OtherMap.getNameForId(Func->ModuleNameId)); 1037ec26b23SKyungwoo Lee auto ClonedIndexOperandHashMap = 1047ec26b23SKyungwoo Lee std::make_unique<IndexOperandHashMapType>(*Func->IndexOperandHashMap); 1057ec26b23SKyungwoo Lee ThisFuncs.emplace_back(std::make_unique<StableFunctionEntry>( 1067ec26b23SKyungwoo Lee Func->Hash, FuncNameId, ModuleNameId, Func->InstCount, 1077ec26b23SKyungwoo Lee std::move(ClonedIndexOperandHashMap))); 1087ec26b23SKyungwoo Lee } 1097ec26b23SKyungwoo Lee } 1107ec26b23SKyungwoo Lee } 1117ec26b23SKyungwoo Lee 1127ec26b23SKyungwoo Lee size_t StableFunctionMap::size(SizeType Type) const { 1137ec26b23SKyungwoo Lee switch (Type) { 1147ec26b23SKyungwoo Lee case UniqueHashCount: 1157ec26b23SKyungwoo Lee return HashToFuncs.size(); 1167ec26b23SKyungwoo Lee case TotalFunctionCount: { 1177ec26b23SKyungwoo Lee size_t Count = 0; 1187ec26b23SKyungwoo Lee for (auto &Funcs : HashToFuncs) 1197ec26b23SKyungwoo Lee Count += Funcs.second.size(); 1207ec26b23SKyungwoo Lee return Count; 1217ec26b23SKyungwoo Lee } 1227ec26b23SKyungwoo Lee case MergeableFunctionCount: { 1237ec26b23SKyungwoo Lee size_t Count = 0; 1247ec26b23SKyungwoo Lee for (auto &[Hash, Funcs] : HashToFuncs) 1257ec26b23SKyungwoo Lee if (Funcs.size() >= 2) 1267ec26b23SKyungwoo Lee Count += Funcs.size(); 1277ec26b23SKyungwoo Lee return Count; 1287ec26b23SKyungwoo Lee } 1297ec26b23SKyungwoo Lee } 1307ec26b23SKyungwoo Lee llvm_unreachable("Unhandled size type"); 1317ec26b23SKyungwoo Lee } 1327ec26b23SKyungwoo Lee 1337ec26b23SKyungwoo Lee using ParamLocs = SmallVector<IndexPair>; 1347ec26b23SKyungwoo Lee static void removeIdenticalIndexPair( 1357ec26b23SKyungwoo Lee SmallVector<std::unique_ptr<StableFunctionMap::StableFunctionEntry>> &SFS) { 1367ec26b23SKyungwoo Lee auto &RSF = SFS[0]; 1377ec26b23SKyungwoo Lee unsigned StableFunctionCount = SFS.size(); 1387ec26b23SKyungwoo Lee 1397ec26b23SKyungwoo Lee SmallVector<IndexPair> ToDelete; 1407ec26b23SKyungwoo Lee for (auto &[Pair, Hash] : *(RSF->IndexOperandHashMap)) { 1417ec26b23SKyungwoo Lee bool Identical = true; 1427ec26b23SKyungwoo Lee for (unsigned J = 1; J < StableFunctionCount; ++J) { 1437ec26b23SKyungwoo Lee auto &SF = SFS[J]; 1447ec26b23SKyungwoo Lee const auto &SHash = SF->IndexOperandHashMap->at(Pair); 1457ec26b23SKyungwoo Lee if (Hash != SHash) { 1467ec26b23SKyungwoo Lee Identical = false; 1477ec26b23SKyungwoo Lee break; 1487ec26b23SKyungwoo Lee } 1497ec26b23SKyungwoo Lee } 1507ec26b23SKyungwoo Lee 1517ec26b23SKyungwoo Lee // No need to parameterize them if the hashes are identical across stable 1527ec26b23SKyungwoo Lee // functions. 1537ec26b23SKyungwoo Lee if (Identical) 1547ec26b23SKyungwoo Lee ToDelete.emplace_back(Pair); 1557ec26b23SKyungwoo Lee } 1567ec26b23SKyungwoo Lee 1577ec26b23SKyungwoo Lee for (auto &Pair : ToDelete) 1587ec26b23SKyungwoo Lee for (auto &SF : SFS) 1597ec26b23SKyungwoo Lee SF->IndexOperandHashMap->erase(Pair); 1607ec26b23SKyungwoo Lee } 1617ec26b23SKyungwoo Lee 162d23c5c2dSKyungwoo Lee static bool isProfitable( 163d23c5c2dSKyungwoo Lee const SmallVector<std::unique_ptr<StableFunctionMap::StableFunctionEntry>> 164d23c5c2dSKyungwoo Lee &SFS) { 165d23c5c2dSKyungwoo Lee unsigned StableFunctionCount = SFS.size(); 166d23c5c2dSKyungwoo Lee if (StableFunctionCount < GlobalMergingMinMerges) 167d23c5c2dSKyungwoo Lee return false; 168d23c5c2dSKyungwoo Lee 169d23c5c2dSKyungwoo Lee unsigned InstCount = SFS[0]->InstCount; 170d23c5c2dSKyungwoo Lee if (InstCount < GlobalMergingMinInstrs) 171d23c5c2dSKyungwoo Lee return false; 172d23c5c2dSKyungwoo Lee 173*fe69a20cSKyungwoo Lee double Cost = 0.0; 174*fe69a20cSKyungwoo Lee SmallSet<stable_hash, 8> UniqueHashVals; 175*fe69a20cSKyungwoo Lee for (auto &SF : SFS) { 176*fe69a20cSKyungwoo Lee UniqueHashVals.clear(); 177*fe69a20cSKyungwoo Lee for (auto &[IndexPair, Hash] : *SF->IndexOperandHashMap) 178*fe69a20cSKyungwoo Lee UniqueHashVals.insert(Hash); 179*fe69a20cSKyungwoo Lee unsigned ParamCount = UniqueHashVals.size(); 180d23c5c2dSKyungwoo Lee if (ParamCount > GlobalMergingMaxParams) 181d23c5c2dSKyungwoo Lee return false; 182*fe69a20cSKyungwoo Lee // Theoretically, if ParamCount is 0, it results in identical code folding 183*fe69a20cSKyungwoo Lee // (ICF), which we can skip merging here since the linker already handles 184*fe69a20cSKyungwoo Lee // ICF. This pass would otherwise introduce unnecessary thunks that are 185*fe69a20cSKyungwoo Lee // merely direct jumps. However, enabling this could be beneficial depending 186*fe69a20cSKyungwoo Lee // on downstream passes, so we provide an option for it. 187*fe69a20cSKyungwoo Lee if (GlobalMergingSkipNoParams && ParamCount == 0) 188*fe69a20cSKyungwoo Lee return false; 189*fe69a20cSKyungwoo Lee Cost += ParamCount * GlobalMergingParamOverhead + GlobalMergingCallOverhead; 190*fe69a20cSKyungwoo Lee } 191*fe69a20cSKyungwoo Lee Cost += GlobalMergingExtraThreshold; 192d23c5c2dSKyungwoo Lee 193*fe69a20cSKyungwoo Lee double Benefit = 194*fe69a20cSKyungwoo Lee InstCount * (StableFunctionCount - 1) * GlobalMergingInstOverhead; 195d23c5c2dSKyungwoo Lee bool Result = Benefit > Cost; 196d23c5c2dSKyungwoo Lee LLVM_DEBUG(dbgs() << "isProfitable: Hash = " << SFS[0]->Hash << ", " 197d23c5c2dSKyungwoo Lee << "StableFunctionCount = " << StableFunctionCount 198d23c5c2dSKyungwoo Lee << ", InstCount = " << InstCount 199d23c5c2dSKyungwoo Lee << ", Benefit = " << Benefit << ", Cost = " << Cost 200d23c5c2dSKyungwoo Lee << ", Result = " << (Result ? "true" : "false") << "\n"); 201d23c5c2dSKyungwoo Lee return Result; 202d23c5c2dSKyungwoo Lee } 203d23c5c2dSKyungwoo Lee 204d23c5c2dSKyungwoo Lee void StableFunctionMap::finalize(bool SkipTrim) { 2057ec26b23SKyungwoo Lee for (auto It = HashToFuncs.begin(); It != HashToFuncs.end(); ++It) { 2067ec26b23SKyungwoo Lee auto &[StableHash, SFS] = *It; 2077ec26b23SKyungwoo Lee 2087ec26b23SKyungwoo Lee // Group stable functions by ModuleIdentifier. 2097ec26b23SKyungwoo Lee std::stable_sort(SFS.begin(), SFS.end(), 2107ec26b23SKyungwoo Lee [&](const std::unique_ptr<StableFunctionEntry> &L, 2117ec26b23SKyungwoo Lee const std::unique_ptr<StableFunctionEntry> &R) { 2127ec26b23SKyungwoo Lee return *getNameForId(L->ModuleNameId) < 2137ec26b23SKyungwoo Lee *getNameForId(R->ModuleNameId); 2147ec26b23SKyungwoo Lee }); 2157ec26b23SKyungwoo Lee 2167ec26b23SKyungwoo Lee // Consider the first function as the root function. 2177ec26b23SKyungwoo Lee auto &RSF = SFS[0]; 2187ec26b23SKyungwoo Lee 2197ec26b23SKyungwoo Lee bool Invalid = false; 2207ec26b23SKyungwoo Lee unsigned StableFunctionCount = SFS.size(); 2217ec26b23SKyungwoo Lee for (unsigned I = 1; I < StableFunctionCount; ++I) { 2227ec26b23SKyungwoo Lee auto &SF = SFS[I]; 2237ec26b23SKyungwoo Lee assert(RSF->Hash == SF->Hash); 2247ec26b23SKyungwoo Lee if (RSF->InstCount != SF->InstCount) { 2257ec26b23SKyungwoo Lee Invalid = true; 2267ec26b23SKyungwoo Lee break; 2277ec26b23SKyungwoo Lee } 2287ec26b23SKyungwoo Lee if (RSF->IndexOperandHashMap->size() != SF->IndexOperandHashMap->size()) { 2297ec26b23SKyungwoo Lee Invalid = true; 2307ec26b23SKyungwoo Lee break; 2317ec26b23SKyungwoo Lee } 2327ec26b23SKyungwoo Lee for (auto &P : *RSF->IndexOperandHashMap) { 2337ec26b23SKyungwoo Lee auto &InstOpndIndex = P.first; 2347ec26b23SKyungwoo Lee if (!SF->IndexOperandHashMap->count(InstOpndIndex)) { 2357ec26b23SKyungwoo Lee Invalid = true; 2367ec26b23SKyungwoo Lee break; 2377ec26b23SKyungwoo Lee } 2387ec26b23SKyungwoo Lee } 2397ec26b23SKyungwoo Lee } 2407ec26b23SKyungwoo Lee if (Invalid) { 2417ec26b23SKyungwoo Lee HashToFuncs.erase(It); 2427ec26b23SKyungwoo Lee continue; 2437ec26b23SKyungwoo Lee } 2447ec26b23SKyungwoo Lee 245d23c5c2dSKyungwoo Lee if (SkipTrim) 246d23c5c2dSKyungwoo Lee continue; 247d23c5c2dSKyungwoo Lee 2487ec26b23SKyungwoo Lee // Trim the index pair that has the same operand hash across 2497ec26b23SKyungwoo Lee // stable functions. 2507ec26b23SKyungwoo Lee removeIdenticalIndexPair(SFS); 251d23c5c2dSKyungwoo Lee 252d23c5c2dSKyungwoo Lee if (!isProfitable(SFS)) 253d23c5c2dSKyungwoo Lee HashToFuncs.erase(It); 2547ec26b23SKyungwoo Lee } 2557ec26b23SKyungwoo Lee 2567ec26b23SKyungwoo Lee Finalized = true; 2577ec26b23SKyungwoo Lee } 258