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*d23c5c2dSKyungwoo Lee #include "llvm/Support/CommandLine.h" 18*d23c5c2dSKyungwoo Lee #include "llvm/Support/Debug.h" 197ec26b23SKyungwoo Lee 207ec26b23SKyungwoo Lee #define DEBUG_TYPE "stable-function-map" 217ec26b23SKyungwoo Lee 227ec26b23SKyungwoo Lee using namespace llvm; 237ec26b23SKyungwoo Lee 24*d23c5c2dSKyungwoo Lee static cl::opt<unsigned> 25*d23c5c2dSKyungwoo Lee GlobalMergingMinMerges("global-merging-min-merges", 26*d23c5c2dSKyungwoo Lee cl::desc("Minimum number of similar functions with " 27*d23c5c2dSKyungwoo Lee "the same hash required for merging."), 28*d23c5c2dSKyungwoo Lee cl::init(2), cl::Hidden); 29*d23c5c2dSKyungwoo Lee static cl::opt<unsigned> GlobalMergingMinInstrs( 30*d23c5c2dSKyungwoo Lee "global-merging-min-instrs", 31*d23c5c2dSKyungwoo Lee cl::desc("The minimum instruction count required when merging functions."), 32*d23c5c2dSKyungwoo Lee cl::init(1), cl::Hidden); 33*d23c5c2dSKyungwoo Lee static cl::opt<unsigned> GlobalMergingMaxParams( 34*d23c5c2dSKyungwoo Lee "global-merging-max-params", 35*d23c5c2dSKyungwoo Lee cl::desc( 36*d23c5c2dSKyungwoo Lee "The maximum number of parameters allowed when merging functions."), 37*d23c5c2dSKyungwoo Lee cl::init(std::numeric_limits<unsigned>::max()), cl::Hidden); 38*d23c5c2dSKyungwoo Lee static cl::opt<unsigned> GlobalMergingParamOverhead( 39*d23c5c2dSKyungwoo Lee "global-merging-param-overhead", 40*d23c5c2dSKyungwoo Lee cl::desc("The overhead cost associated with each parameter when merging " 41*d23c5c2dSKyungwoo Lee "functions."), 42*d23c5c2dSKyungwoo Lee cl::init(2), cl::Hidden); 43*d23c5c2dSKyungwoo Lee static cl::opt<unsigned> 44*d23c5c2dSKyungwoo Lee GlobalMergingCallOverhead("global-merging-call-overhead", 45*d23c5c2dSKyungwoo Lee cl::desc("The overhead cost associated with each " 46*d23c5c2dSKyungwoo Lee "function call when merging functions."), 47*d23c5c2dSKyungwoo Lee cl::init(1), cl::Hidden); 48*d23c5c2dSKyungwoo Lee static cl::opt<unsigned> GlobalMergingExtraThreshold( 49*d23c5c2dSKyungwoo Lee "global-merging-extra-threshold", 50*d23c5c2dSKyungwoo Lee cl::desc("An additional cost threshold that must be exceeded for merging " 51*d23c5c2dSKyungwoo Lee "to be considered beneficial."), 52*d23c5c2dSKyungwoo Lee cl::init(0), cl::Hidden); 53*d23c5c2dSKyungwoo Lee 547ec26b23SKyungwoo Lee unsigned StableFunctionMap::getIdOrCreateForName(StringRef Name) { 557ec26b23SKyungwoo Lee auto It = NameToId.find(Name); 567ec26b23SKyungwoo Lee if (It != NameToId.end()) 577ec26b23SKyungwoo Lee return It->second; 587ec26b23SKyungwoo Lee unsigned Id = IdToName.size(); 597ec26b23SKyungwoo Lee assert(Id == NameToId.size() && "ID collision"); 607ec26b23SKyungwoo Lee IdToName.emplace_back(Name.str()); 617ec26b23SKyungwoo Lee NameToId[IdToName.back()] = Id; 627ec26b23SKyungwoo Lee return Id; 637ec26b23SKyungwoo Lee } 647ec26b23SKyungwoo Lee 657ec26b23SKyungwoo Lee std::optional<std::string> StableFunctionMap::getNameForId(unsigned Id) const { 667ec26b23SKyungwoo Lee if (Id >= IdToName.size()) 677ec26b23SKyungwoo Lee return std::nullopt; 687ec26b23SKyungwoo Lee return IdToName[Id]; 697ec26b23SKyungwoo Lee } 707ec26b23SKyungwoo Lee 717ec26b23SKyungwoo Lee void StableFunctionMap::insert(const StableFunction &Func) { 727ec26b23SKyungwoo Lee assert(!Finalized && "Cannot insert after finalization"); 737ec26b23SKyungwoo Lee auto FuncNameId = getIdOrCreateForName(Func.FunctionName); 747ec26b23SKyungwoo Lee auto ModuleNameId = getIdOrCreateForName(Func.ModuleName); 757ec26b23SKyungwoo Lee auto IndexOperandHashMap = std::make_unique<IndexOperandHashMapType>(); 767ec26b23SKyungwoo Lee for (auto &[Index, Hash] : Func.IndexOperandHashes) 777ec26b23SKyungwoo Lee (*IndexOperandHashMap)[Index] = Hash; 787ec26b23SKyungwoo Lee auto FuncEntry = std::make_unique<StableFunctionEntry>( 797ec26b23SKyungwoo Lee Func.Hash, FuncNameId, ModuleNameId, Func.InstCount, 807ec26b23SKyungwoo Lee std::move(IndexOperandHashMap)); 817ec26b23SKyungwoo Lee insert(std::move(FuncEntry)); 827ec26b23SKyungwoo Lee } 837ec26b23SKyungwoo Lee 847ec26b23SKyungwoo Lee void StableFunctionMap::merge(const StableFunctionMap &OtherMap) { 857ec26b23SKyungwoo Lee assert(!Finalized && "Cannot merge after finalization"); 867ec26b23SKyungwoo Lee for (auto &[Hash, Funcs] : OtherMap.HashToFuncs) { 877ec26b23SKyungwoo Lee auto &ThisFuncs = HashToFuncs[Hash]; 887ec26b23SKyungwoo Lee for (auto &Func : Funcs) { 897ec26b23SKyungwoo Lee auto FuncNameId = 907ec26b23SKyungwoo Lee getIdOrCreateForName(*OtherMap.getNameForId(Func->FunctionNameId)); 917ec26b23SKyungwoo Lee auto ModuleNameId = 927ec26b23SKyungwoo Lee getIdOrCreateForName(*OtherMap.getNameForId(Func->ModuleNameId)); 937ec26b23SKyungwoo Lee auto ClonedIndexOperandHashMap = 947ec26b23SKyungwoo Lee std::make_unique<IndexOperandHashMapType>(*Func->IndexOperandHashMap); 957ec26b23SKyungwoo Lee ThisFuncs.emplace_back(std::make_unique<StableFunctionEntry>( 967ec26b23SKyungwoo Lee Func->Hash, FuncNameId, ModuleNameId, Func->InstCount, 977ec26b23SKyungwoo Lee std::move(ClonedIndexOperandHashMap))); 987ec26b23SKyungwoo Lee } 997ec26b23SKyungwoo Lee } 1007ec26b23SKyungwoo Lee } 1017ec26b23SKyungwoo Lee 1027ec26b23SKyungwoo Lee size_t StableFunctionMap::size(SizeType Type) const { 1037ec26b23SKyungwoo Lee switch (Type) { 1047ec26b23SKyungwoo Lee case UniqueHashCount: 1057ec26b23SKyungwoo Lee return HashToFuncs.size(); 1067ec26b23SKyungwoo Lee case TotalFunctionCount: { 1077ec26b23SKyungwoo Lee size_t Count = 0; 1087ec26b23SKyungwoo Lee for (auto &Funcs : HashToFuncs) 1097ec26b23SKyungwoo Lee Count += Funcs.second.size(); 1107ec26b23SKyungwoo Lee return Count; 1117ec26b23SKyungwoo Lee } 1127ec26b23SKyungwoo Lee case MergeableFunctionCount: { 1137ec26b23SKyungwoo Lee size_t Count = 0; 1147ec26b23SKyungwoo Lee for (auto &[Hash, Funcs] : HashToFuncs) 1157ec26b23SKyungwoo Lee if (Funcs.size() >= 2) 1167ec26b23SKyungwoo Lee Count += Funcs.size(); 1177ec26b23SKyungwoo Lee return Count; 1187ec26b23SKyungwoo Lee } 1197ec26b23SKyungwoo Lee } 1207ec26b23SKyungwoo Lee llvm_unreachable("Unhandled size type"); 1217ec26b23SKyungwoo Lee } 1227ec26b23SKyungwoo Lee 1237ec26b23SKyungwoo Lee using ParamLocs = SmallVector<IndexPair>; 1247ec26b23SKyungwoo Lee static void removeIdenticalIndexPair( 1257ec26b23SKyungwoo Lee SmallVector<std::unique_ptr<StableFunctionMap::StableFunctionEntry>> &SFS) { 1267ec26b23SKyungwoo Lee auto &RSF = SFS[0]; 1277ec26b23SKyungwoo Lee unsigned StableFunctionCount = SFS.size(); 1287ec26b23SKyungwoo Lee 1297ec26b23SKyungwoo Lee SmallVector<IndexPair> ToDelete; 1307ec26b23SKyungwoo Lee for (auto &[Pair, Hash] : *(RSF->IndexOperandHashMap)) { 1317ec26b23SKyungwoo Lee bool Identical = true; 1327ec26b23SKyungwoo Lee for (unsigned J = 1; J < StableFunctionCount; ++J) { 1337ec26b23SKyungwoo Lee auto &SF = SFS[J]; 1347ec26b23SKyungwoo Lee const auto &SHash = SF->IndexOperandHashMap->at(Pair); 1357ec26b23SKyungwoo Lee if (Hash != SHash) { 1367ec26b23SKyungwoo Lee Identical = false; 1377ec26b23SKyungwoo Lee break; 1387ec26b23SKyungwoo Lee } 1397ec26b23SKyungwoo Lee } 1407ec26b23SKyungwoo Lee 1417ec26b23SKyungwoo Lee // No need to parameterize them if the hashes are identical across stable 1427ec26b23SKyungwoo Lee // functions. 1437ec26b23SKyungwoo Lee if (Identical) 1447ec26b23SKyungwoo Lee ToDelete.emplace_back(Pair); 1457ec26b23SKyungwoo Lee } 1467ec26b23SKyungwoo Lee 1477ec26b23SKyungwoo Lee for (auto &Pair : ToDelete) 1487ec26b23SKyungwoo Lee for (auto &SF : SFS) 1497ec26b23SKyungwoo Lee SF->IndexOperandHashMap->erase(Pair); 1507ec26b23SKyungwoo Lee } 1517ec26b23SKyungwoo Lee 152*d23c5c2dSKyungwoo Lee static bool isProfitable( 153*d23c5c2dSKyungwoo Lee const SmallVector<std::unique_ptr<StableFunctionMap::StableFunctionEntry>> 154*d23c5c2dSKyungwoo Lee &SFS) { 155*d23c5c2dSKyungwoo Lee unsigned StableFunctionCount = SFS.size(); 156*d23c5c2dSKyungwoo Lee if (StableFunctionCount < GlobalMergingMinMerges) 157*d23c5c2dSKyungwoo Lee return false; 158*d23c5c2dSKyungwoo Lee 159*d23c5c2dSKyungwoo Lee unsigned InstCount = SFS[0]->InstCount; 160*d23c5c2dSKyungwoo Lee if (InstCount < GlobalMergingMinInstrs) 161*d23c5c2dSKyungwoo Lee return false; 162*d23c5c2dSKyungwoo Lee 163*d23c5c2dSKyungwoo Lee unsigned ParamCount = SFS[0]->IndexOperandHashMap->size(); 164*d23c5c2dSKyungwoo Lee if (ParamCount > GlobalMergingMaxParams) 165*d23c5c2dSKyungwoo Lee return false; 166*d23c5c2dSKyungwoo Lee 167*d23c5c2dSKyungwoo Lee unsigned Benefit = InstCount * (StableFunctionCount - 1); 168*d23c5c2dSKyungwoo Lee unsigned Cost = 169*d23c5c2dSKyungwoo Lee (GlobalMergingParamOverhead * ParamCount + GlobalMergingCallOverhead) * 170*d23c5c2dSKyungwoo Lee StableFunctionCount + 171*d23c5c2dSKyungwoo Lee GlobalMergingExtraThreshold; 172*d23c5c2dSKyungwoo Lee 173*d23c5c2dSKyungwoo Lee bool Result = Benefit > Cost; 174*d23c5c2dSKyungwoo Lee LLVM_DEBUG(dbgs() << "isProfitable: Hash = " << SFS[0]->Hash << ", " 175*d23c5c2dSKyungwoo Lee << "StableFunctionCount = " << StableFunctionCount 176*d23c5c2dSKyungwoo Lee << ", InstCount = " << InstCount 177*d23c5c2dSKyungwoo Lee << ", ParamCount = " << ParamCount 178*d23c5c2dSKyungwoo Lee << ", Benefit = " << Benefit << ", Cost = " << Cost 179*d23c5c2dSKyungwoo Lee << ", Result = " << (Result ? "true" : "false") << "\n"); 180*d23c5c2dSKyungwoo Lee return Result; 181*d23c5c2dSKyungwoo Lee } 182*d23c5c2dSKyungwoo Lee 183*d23c5c2dSKyungwoo Lee void StableFunctionMap::finalize(bool SkipTrim) { 1847ec26b23SKyungwoo Lee for (auto It = HashToFuncs.begin(); It != HashToFuncs.end(); ++It) { 1857ec26b23SKyungwoo Lee auto &[StableHash, SFS] = *It; 1867ec26b23SKyungwoo Lee 1877ec26b23SKyungwoo Lee // Group stable functions by ModuleIdentifier. 1887ec26b23SKyungwoo Lee std::stable_sort(SFS.begin(), SFS.end(), 1897ec26b23SKyungwoo Lee [&](const std::unique_ptr<StableFunctionEntry> &L, 1907ec26b23SKyungwoo Lee const std::unique_ptr<StableFunctionEntry> &R) { 1917ec26b23SKyungwoo Lee return *getNameForId(L->ModuleNameId) < 1927ec26b23SKyungwoo Lee *getNameForId(R->ModuleNameId); 1937ec26b23SKyungwoo Lee }); 1947ec26b23SKyungwoo Lee 1957ec26b23SKyungwoo Lee // Consider the first function as the root function. 1967ec26b23SKyungwoo Lee auto &RSF = SFS[0]; 1977ec26b23SKyungwoo Lee 1987ec26b23SKyungwoo Lee bool Invalid = false; 1997ec26b23SKyungwoo Lee unsigned StableFunctionCount = SFS.size(); 2007ec26b23SKyungwoo Lee for (unsigned I = 1; I < StableFunctionCount; ++I) { 2017ec26b23SKyungwoo Lee auto &SF = SFS[I]; 2027ec26b23SKyungwoo Lee assert(RSF->Hash == SF->Hash); 2037ec26b23SKyungwoo Lee if (RSF->InstCount != SF->InstCount) { 2047ec26b23SKyungwoo Lee Invalid = true; 2057ec26b23SKyungwoo Lee break; 2067ec26b23SKyungwoo Lee } 2077ec26b23SKyungwoo Lee if (RSF->IndexOperandHashMap->size() != SF->IndexOperandHashMap->size()) { 2087ec26b23SKyungwoo Lee Invalid = true; 2097ec26b23SKyungwoo Lee break; 2107ec26b23SKyungwoo Lee } 2117ec26b23SKyungwoo Lee for (auto &P : *RSF->IndexOperandHashMap) { 2127ec26b23SKyungwoo Lee auto &InstOpndIndex = P.first; 2137ec26b23SKyungwoo Lee if (!SF->IndexOperandHashMap->count(InstOpndIndex)) { 2147ec26b23SKyungwoo Lee Invalid = true; 2157ec26b23SKyungwoo Lee break; 2167ec26b23SKyungwoo Lee } 2177ec26b23SKyungwoo Lee } 2187ec26b23SKyungwoo Lee } 2197ec26b23SKyungwoo Lee if (Invalid) { 2207ec26b23SKyungwoo Lee HashToFuncs.erase(It); 2217ec26b23SKyungwoo Lee continue; 2227ec26b23SKyungwoo Lee } 2237ec26b23SKyungwoo Lee 224*d23c5c2dSKyungwoo Lee if (SkipTrim) 225*d23c5c2dSKyungwoo Lee continue; 226*d23c5c2dSKyungwoo Lee 2277ec26b23SKyungwoo Lee // Trim the index pair that has the same operand hash across 2287ec26b23SKyungwoo Lee // stable functions. 2297ec26b23SKyungwoo Lee removeIdenticalIndexPair(SFS); 230*d23c5c2dSKyungwoo Lee 231*d23c5c2dSKyungwoo Lee if (!isProfitable(SFS)) 232*d23c5c2dSKyungwoo Lee HashToFuncs.erase(It); 2337ec26b23SKyungwoo Lee } 2347ec26b23SKyungwoo Lee 2357ec26b23SKyungwoo Lee Finalized = true; 2367ec26b23SKyungwoo Lee } 237