1 //===-- StableFunctionMap.cpp ---------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This implements the functionality for the StableFunctionMap class, which 10 // manages the mapping of stable function hashes to their metadata. It includes 11 // methods for inserting, merging, and finalizing function entries, as well as 12 // utilities for handling function names and IDs. 13 // 14 //===----------------------------------------------------------------------===// 15 16 #include "llvm/CGData/StableFunctionMap.h" 17 #include "llvm/Support/CommandLine.h" 18 #include "llvm/Support/Debug.h" 19 20 #define DEBUG_TYPE "stable-function-map" 21 22 using namespace llvm; 23 24 static cl::opt<unsigned> 25 GlobalMergingMinMerges("global-merging-min-merges", 26 cl::desc("Minimum number of similar functions with " 27 "the same hash required for merging."), 28 cl::init(2), cl::Hidden); 29 static cl::opt<unsigned> GlobalMergingMinInstrs( 30 "global-merging-min-instrs", 31 cl::desc("The minimum instruction count required when merging functions."), 32 cl::init(1), cl::Hidden); 33 static cl::opt<unsigned> GlobalMergingMaxParams( 34 "global-merging-max-params", 35 cl::desc( 36 "The maximum number of parameters allowed when merging functions."), 37 cl::init(std::numeric_limits<unsigned>::max()), cl::Hidden); 38 static cl::opt<unsigned> GlobalMergingParamOverhead( 39 "global-merging-param-overhead", 40 cl::desc("The overhead cost associated with each parameter when merging " 41 "functions."), 42 cl::init(2), cl::Hidden); 43 static cl::opt<unsigned> 44 GlobalMergingCallOverhead("global-merging-call-overhead", 45 cl::desc("The overhead cost associated with each " 46 "function call when merging functions."), 47 cl::init(1), cl::Hidden); 48 static cl::opt<unsigned> GlobalMergingExtraThreshold( 49 "global-merging-extra-threshold", 50 cl::desc("An additional cost threshold that must be exceeded for merging " 51 "to be considered beneficial."), 52 cl::init(0), cl::Hidden); 53 54 unsigned StableFunctionMap::getIdOrCreateForName(StringRef Name) { 55 auto It = NameToId.find(Name); 56 if (It != NameToId.end()) 57 return It->second; 58 unsigned Id = IdToName.size(); 59 assert(Id == NameToId.size() && "ID collision"); 60 IdToName.emplace_back(Name.str()); 61 NameToId[IdToName.back()] = Id; 62 return Id; 63 } 64 65 std::optional<std::string> StableFunctionMap::getNameForId(unsigned Id) const { 66 if (Id >= IdToName.size()) 67 return std::nullopt; 68 return IdToName[Id]; 69 } 70 71 void StableFunctionMap::insert(const StableFunction &Func) { 72 assert(!Finalized && "Cannot insert after finalization"); 73 auto FuncNameId = getIdOrCreateForName(Func.FunctionName); 74 auto ModuleNameId = getIdOrCreateForName(Func.ModuleName); 75 auto IndexOperandHashMap = std::make_unique<IndexOperandHashMapType>(); 76 for (auto &[Index, Hash] : Func.IndexOperandHashes) 77 (*IndexOperandHashMap)[Index] = Hash; 78 auto FuncEntry = std::make_unique<StableFunctionEntry>( 79 Func.Hash, FuncNameId, ModuleNameId, Func.InstCount, 80 std::move(IndexOperandHashMap)); 81 insert(std::move(FuncEntry)); 82 } 83 84 void StableFunctionMap::merge(const StableFunctionMap &OtherMap) { 85 assert(!Finalized && "Cannot merge after finalization"); 86 for (auto &[Hash, Funcs] : OtherMap.HashToFuncs) { 87 auto &ThisFuncs = HashToFuncs[Hash]; 88 for (auto &Func : Funcs) { 89 auto FuncNameId = 90 getIdOrCreateForName(*OtherMap.getNameForId(Func->FunctionNameId)); 91 auto ModuleNameId = 92 getIdOrCreateForName(*OtherMap.getNameForId(Func->ModuleNameId)); 93 auto ClonedIndexOperandHashMap = 94 std::make_unique<IndexOperandHashMapType>(*Func->IndexOperandHashMap); 95 ThisFuncs.emplace_back(std::make_unique<StableFunctionEntry>( 96 Func->Hash, FuncNameId, ModuleNameId, Func->InstCount, 97 std::move(ClonedIndexOperandHashMap))); 98 } 99 } 100 } 101 102 size_t StableFunctionMap::size(SizeType Type) const { 103 switch (Type) { 104 case UniqueHashCount: 105 return HashToFuncs.size(); 106 case TotalFunctionCount: { 107 size_t Count = 0; 108 for (auto &Funcs : HashToFuncs) 109 Count += Funcs.second.size(); 110 return Count; 111 } 112 case MergeableFunctionCount: { 113 size_t Count = 0; 114 for (auto &[Hash, Funcs] : HashToFuncs) 115 if (Funcs.size() >= 2) 116 Count += Funcs.size(); 117 return Count; 118 } 119 } 120 llvm_unreachable("Unhandled size type"); 121 } 122 123 using ParamLocs = SmallVector<IndexPair>; 124 static void removeIdenticalIndexPair( 125 SmallVector<std::unique_ptr<StableFunctionMap::StableFunctionEntry>> &SFS) { 126 auto &RSF = SFS[0]; 127 unsigned StableFunctionCount = SFS.size(); 128 129 SmallVector<IndexPair> ToDelete; 130 for (auto &[Pair, Hash] : *(RSF->IndexOperandHashMap)) { 131 bool Identical = true; 132 for (unsigned J = 1; J < StableFunctionCount; ++J) { 133 auto &SF = SFS[J]; 134 const auto &SHash = SF->IndexOperandHashMap->at(Pair); 135 if (Hash != SHash) { 136 Identical = false; 137 break; 138 } 139 } 140 141 // No need to parameterize them if the hashes are identical across stable 142 // functions. 143 if (Identical) 144 ToDelete.emplace_back(Pair); 145 } 146 147 for (auto &Pair : ToDelete) 148 for (auto &SF : SFS) 149 SF->IndexOperandHashMap->erase(Pair); 150 } 151 152 static bool isProfitable( 153 const SmallVector<std::unique_ptr<StableFunctionMap::StableFunctionEntry>> 154 &SFS) { 155 unsigned StableFunctionCount = SFS.size(); 156 if (StableFunctionCount < GlobalMergingMinMerges) 157 return false; 158 159 unsigned InstCount = SFS[0]->InstCount; 160 if (InstCount < GlobalMergingMinInstrs) 161 return false; 162 163 unsigned ParamCount = SFS[0]->IndexOperandHashMap->size(); 164 if (ParamCount > GlobalMergingMaxParams) 165 return false; 166 167 unsigned Benefit = InstCount * (StableFunctionCount - 1); 168 unsigned Cost = 169 (GlobalMergingParamOverhead * ParamCount + GlobalMergingCallOverhead) * 170 StableFunctionCount + 171 GlobalMergingExtraThreshold; 172 173 bool Result = Benefit > Cost; 174 LLVM_DEBUG(dbgs() << "isProfitable: Hash = " << SFS[0]->Hash << ", " 175 << "StableFunctionCount = " << StableFunctionCount 176 << ", InstCount = " << InstCount 177 << ", ParamCount = " << ParamCount 178 << ", Benefit = " << Benefit << ", Cost = " << Cost 179 << ", Result = " << (Result ? "true" : "false") << "\n"); 180 return Result; 181 } 182 183 void StableFunctionMap::finalize(bool SkipTrim) { 184 for (auto It = HashToFuncs.begin(); It != HashToFuncs.end(); ++It) { 185 auto &[StableHash, SFS] = *It; 186 187 // Group stable functions by ModuleIdentifier. 188 std::stable_sort(SFS.begin(), SFS.end(), 189 [&](const std::unique_ptr<StableFunctionEntry> &L, 190 const std::unique_ptr<StableFunctionEntry> &R) { 191 return *getNameForId(L->ModuleNameId) < 192 *getNameForId(R->ModuleNameId); 193 }); 194 195 // Consider the first function as the root function. 196 auto &RSF = SFS[0]; 197 198 bool Invalid = false; 199 unsigned StableFunctionCount = SFS.size(); 200 for (unsigned I = 1; I < StableFunctionCount; ++I) { 201 auto &SF = SFS[I]; 202 assert(RSF->Hash == SF->Hash); 203 if (RSF->InstCount != SF->InstCount) { 204 Invalid = true; 205 break; 206 } 207 if (RSF->IndexOperandHashMap->size() != SF->IndexOperandHashMap->size()) { 208 Invalid = true; 209 break; 210 } 211 for (auto &P : *RSF->IndexOperandHashMap) { 212 auto &InstOpndIndex = P.first; 213 if (!SF->IndexOperandHashMap->count(InstOpndIndex)) { 214 Invalid = true; 215 break; 216 } 217 } 218 } 219 if (Invalid) { 220 HashToFuncs.erase(It); 221 continue; 222 } 223 224 if (SkipTrim) 225 continue; 226 227 // Trim the index pair that has the same operand hash across 228 // stable functions. 229 removeIdenticalIndexPair(SFS); 230 231 if (!isProfitable(SFS)) 232 HashToFuncs.erase(It); 233 } 234 235 Finalized = true; 236 } 237