LLVM 22.0.0git
StableFunctionMap.cpp
Go to the documentation of this file.
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
17#include "llvm/ADT/SmallSet.h"
20#include "llvm/Support/Debug.h"
21
22#define DEBUG_TYPE "stable-function-map"
23
24using namespace llvm;
25
27 GlobalMergingMinMerges("global-merging-min-merges",
28 cl::desc("Minimum number of similar functions with "
29 "the same hash required for merging."),
32 "global-merging-min-instrs",
33 cl::desc("The minimum instruction count required when merging functions."),
36 "global-merging-max-params",
38 "The maximum number of parameters allowed when merging functions."),
39 cl::init(std::numeric_limits<unsigned>::max()), cl::Hidden);
41 "global-merging-skip-no-params",
42 cl::desc("Skip merging functions with no parameters."), cl::init(true),
45 "global-merging-inst-overhead",
46 cl::desc("The overhead cost associated with each instruction when lowering "
47 "to machine instruction."),
48 cl::init(1.2), cl::Hidden);
50 "global-merging-param-overhead",
51 cl::desc("The overhead cost associated with each parameter when merging "
52 "functions."),
53 cl::init(2.0), cl::Hidden);
54static cl::opt<double>
55 GlobalMergingCallOverhead("global-merging-call-overhead",
56 cl::desc("The overhead cost associated with each "
57 "function call when merging functions."),
58 cl::init(1.0), cl::Hidden);
60 "global-merging-extra-threshold",
61 cl::desc("An additional cost threshold that must be exceeded for merging "
62 "to be considered beneficial."),
63 cl::init(0.0), cl::Hidden);
64
66 auto It = NameToId.find(Name);
67 if (It != NameToId.end())
68 return It->second;
69 unsigned Id = IdToName.size();
70 assert(Id == NameToId.size() && "ID collision");
71 IdToName.emplace_back(Name.str());
72 NameToId[IdToName.back()] = Id;
73 return Id;
74}
75
76std::optional<std::string> StableFunctionMap::getNameForId(unsigned Id) const {
77 if (Id >= IdToName.size())
78 return std::nullopt;
79 return IdToName[Id];
80}
81
83 assert(!Finalized && "Cannot insert after finalization");
84 auto FuncNameId = getIdOrCreateForName(Func.FunctionName);
85 auto ModuleNameId = getIdOrCreateForName(Func.ModuleName);
86 auto IndexOperandHashMap = std::make_unique<IndexOperandHashMapType>();
87 for (auto &[Index, Hash] : Func.IndexOperandHashes)
88 (*IndexOperandHashMap)[Index] = Hash;
89 auto FuncEntry = std::make_unique<StableFunctionEntry>(
90 Func.Hash, FuncNameId, ModuleNameId, Func.InstCount,
91 std::move(IndexOperandHashMap));
92 insert(std::move(FuncEntry));
93}
94
96 assert(!Finalized && "Cannot merge after finalization");
97 deserializeLazyLoadingEntries();
98 for (auto &[Hash, Funcs] : OtherMap.HashToFuncs) {
99 auto &ThisFuncs = HashToFuncs[Hash].Entries;
100 for (auto &Func : Funcs.Entries) {
101 auto FuncNameId =
102 getIdOrCreateForName(*OtherMap.getNameForId(Func->FunctionNameId));
103 auto ModuleNameId =
104 getIdOrCreateForName(*OtherMap.getNameForId(Func->ModuleNameId));
105 auto ClonedIndexOperandHashMap =
106 std::make_unique<IndexOperandHashMapType>(*Func->IndexOperandHashMap);
107 ThisFuncs.emplace_back(std::make_unique<StableFunctionEntry>(
108 Func->Hash, FuncNameId, ModuleNameId, Func->InstCount,
109 std::move(ClonedIndexOperandHashMap)));
110 }
111 }
112}
113
115 switch (Type) {
116 case UniqueHashCount:
117 return HashToFuncs.size();
118 case TotalFunctionCount: {
119 deserializeLazyLoadingEntries();
120 size_t Count = 0;
121 for (auto &Funcs : HashToFuncs)
122 Count += Funcs.second.Entries.size();
123 return Count;
124 }
126 deserializeLazyLoadingEntries();
127 size_t Count = 0;
128 for (auto &[Hash, Funcs] : HashToFuncs)
129 if (Funcs.Entries.size() >= 2)
130 Count += Funcs.Entries.size();
131 return Count;
132 }
133 }
134 llvm_unreachable("Unhandled size type");
135}
136
138StableFunctionMap::at(HashFuncsMapType::key_type FunctionHash) const {
139 auto It = HashToFuncs.find(FunctionHash);
140 if (isLazilyLoaded())
141 deserializeLazyLoadingEntry(It);
142 return It->second.Entries;
143}
144
145void StableFunctionMap::deserializeLazyLoadingEntry(
146 HashFuncsMapType::iterator It) const {
147 assert(isLazilyLoaded() && "Cannot deserialize non-lazily-loaded map");
148 auto &[Hash, Storage] = *It;
149 std::call_once(Storage.LazyLoadFlag,
150 [this, HashArg = Hash, &StorageArg = Storage]() {
151 for (auto Offset : StorageArg.Offsets)
152 StableFunctionMapRecord::deserializeEntry(
153 reinterpret_cast<const unsigned char *>(Offset),
154 HashArg, const_cast<StableFunctionMap *>(this));
155 });
156}
157
158void StableFunctionMap::deserializeLazyLoadingEntries() const {
159 if (!isLazilyLoaded())
160 return;
161 for (auto It = HashToFuncs.begin(); It != HashToFuncs.end(); ++It)
162 deserializeLazyLoadingEntry(It);
163}
164
167 // Ensure all entries are deserialized before returning the raw map.
168 if (isLazilyLoaded())
169 deserializeLazyLoadingEntries();
170 return HashToFuncs;
171}
172
174static void
176 auto &RSF = SFS[0];
177 unsigned StableFunctionCount = SFS.size();
178
179 SmallVector<IndexPair> ToDelete;
180 for (auto &[Pair, Hash] : *(RSF->IndexOperandHashMap)) {
181 bool Identical = true;
182 for (unsigned J = 1; J < StableFunctionCount; ++J) {
183 auto &SF = SFS[J];
184 const auto &SHash = SF->IndexOperandHashMap->at(Pair);
185 if (Hash != SHash) {
186 Identical = false;
187 break;
188 }
189 }
190
191 // No need to parameterize them if the hashes are identical across stable
192 // functions.
193 if (Identical)
194 ToDelete.emplace_back(Pair);
195 }
196
197 for (auto &Pair : ToDelete)
198 for (auto &SF : SFS)
199 SF->IndexOperandHashMap->erase(Pair);
200}
201
203 unsigned StableFunctionCount = SFS.size();
204 if (StableFunctionCount < GlobalMergingMinMerges)
205 return false;
206
207 unsigned InstCount = SFS[0]->InstCount;
208 if (InstCount < GlobalMergingMinInstrs)
209 return false;
210
211 double Cost = 0.0;
212 SmallSet<stable_hash, 8> UniqueHashVals;
213 for (auto &SF : SFS) {
214 UniqueHashVals.clear();
215 for (auto &[IndexPair, Hash] : *SF->IndexOperandHashMap)
216 UniqueHashVals.insert(Hash);
217 unsigned ParamCount = UniqueHashVals.size();
218 if (ParamCount > GlobalMergingMaxParams)
219 return false;
220 // Theoretically, if ParamCount is 0, it results in identical code folding
221 // (ICF), which we can skip merging here since the linker already handles
222 // ICF. This pass would otherwise introduce unnecessary thunks that are
223 // merely direct jumps. However, enabling this could be beneficial depending
224 // on downstream passes, so we provide an option for it.
225 if (GlobalMergingSkipNoParams && ParamCount == 0)
226 return false;
228 }
230
231 double Benefit =
232 InstCount * (StableFunctionCount - 1) * GlobalMergingInstOverhead;
233 bool Result = Benefit > Cost;
234 LLVM_DEBUG(dbgs() << "isProfitable: Hash = " << SFS[0]->Hash << ", "
235 << "StableFunctionCount = " << StableFunctionCount
236 << ", InstCount = " << InstCount
237 << ", Benefit = " << Benefit << ", Cost = " << Cost
238 << ", Result = " << (Result ? "true" : "false") << "\n");
239 return Result;
240}
241
242void StableFunctionMap::finalize(bool SkipTrim) {
243 deserializeLazyLoadingEntries();
245 for (auto It = HashToFuncs.begin(); It != HashToFuncs.end(); ++It) {
246 auto &[StableHash, Storage] = *It;
247 auto &SFS = Storage.Entries;
248
249 // Group stable functions by ModuleIdentifier.
250 llvm::stable_sort(SFS, [&](const std::unique_ptr<StableFunctionEntry> &L,
251 const std::unique_ptr<StableFunctionEntry> &R) {
252 return *getNameForId(L->ModuleNameId) < *getNameForId(R->ModuleNameId);
253 });
254
255 // Consider the first function as the root function.
256 auto &RSF = SFS[0];
257
258 bool Invalid = false;
259 unsigned StableFunctionCount = SFS.size();
260 for (unsigned I = 1; I < StableFunctionCount; ++I) {
261 auto &SF = SFS[I];
262 assert(RSF->Hash == SF->Hash);
263 if (RSF->InstCount != SF->InstCount) {
264 Invalid = true;
265 break;
266 }
267 if (RSF->IndexOperandHashMap->size() != SF->IndexOperandHashMap->size()) {
268 Invalid = true;
269 break;
270 }
271 for (auto &P : *RSF->IndexOperandHashMap) {
272 auto &InstOpndIndex = P.first;
273 if (!SF->IndexOperandHashMap->count(InstOpndIndex)) {
274 Invalid = true;
275 break;
276 }
277 }
278 }
279 if (Invalid) {
280 ToDelete.push_back(It);
281 continue;
282 }
283
284 if (SkipTrim)
285 continue;
286
287 // Trim the index pair that has the same operand hash across
288 // stable functions.
290
291 if (!isProfitable(SFS))
292 ToDelete.push_back(It);
293 }
294 for (auto It : ToDelete)
295 HashToFuncs.erase(It);
296
297 Finalized = true;
298}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
std::string Name
#define I(x, y, z)
Definition: MD5.cpp:58
#define P(N)
This file defines the SmallSet class.
static bool isProfitable(const StableFunctionMap::StableFunctionEntries &SFS)
static cl::opt< bool > GlobalMergingSkipNoParams("global-merging-skip-no-params", cl::desc("Skip merging functions with no parameters."), cl::init(true), cl::Hidden)
static cl::opt< double > GlobalMergingParamOverhead("global-merging-param-overhead", cl::desc("The overhead cost associated with each parameter when merging " "functions."), cl::init(2.0), cl::Hidden)
static cl::opt< unsigned > GlobalMergingMinMerges("global-merging-min-merges", cl::desc("Minimum number of similar functions with " "the same hash required for merging."), cl::init(2), cl::Hidden)
static void removeIdenticalIndexPair(StableFunctionMap::StableFunctionEntries &SFS)
static cl::opt< double > GlobalMergingInstOverhead("global-merging-inst-overhead", cl::desc("The overhead cost associated with each instruction when lowering " "to machine instruction."), cl::init(1.2), cl::Hidden)
static cl::opt< double > GlobalMergingCallOverhead("global-merging-call-overhead", cl::desc("The overhead cost associated with each " "function call when merging functions."), cl::init(1.0), cl::Hidden)
static cl::opt< unsigned > GlobalMergingMaxParams("global-merging-max-params", cl::desc("The maximum number of parameters allowed when merging functions."), cl::init(std::numeric_limits< unsigned >::max()), cl::Hidden)
static cl::opt< unsigned > GlobalMergingMinInstrs("global-merging-min-instrs", cl::desc("The minimum instruction count required when merging functions."), cl::init(1), cl::Hidden)
static cl::opt< double > GlobalMergingExtraThreshold("global-merging-extra-threshold", cl::desc("An additional cost threshold that must be exceeded for merging " "to be considered beneficial."), cl::init(0.0), cl::Hidden)
#define LLVM_DEBUG(...)
Definition: Debug.h:119
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
Definition: SmallSet.h:134
void clear()
Definition: SmallSet.h:209
std::pair< const_iterator, bool > insert(const T &V)
insert - Insert an element into the set if it isn't already there.
Definition: SmallSet.h:182
size_type size() const
Definition: SmallSet.h:171
size_t size() const
Definition: SmallVector.h:79
reference emplace_back(ArgTypes &&... Args)
Definition: SmallVector.h:938
void push_back(const T &Elt)
Definition: SmallVector.h:414
unsigned size() const
Definition: StringMap.h:109
iterator end()
Definition: StringMap.h:224
iterator find(StringRef Key)
Definition: StringMap.h:237
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:55
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
initializer< Ty > init(const Ty &Val)
Definition: CommandLine.h:444
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
void stable_sort(R &&Range)
Definition: STLExtras.h:2077
std::pair< unsigned, unsigned > IndexPair
The pair of an instruction index and a operand index.
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:207
InstructionCost Cost
std::unordered_map< stable_hash, EntryStorage > HashFuncsMapType
LLVM_ABI void finalize(bool SkipTrim=false)
Finalize the stable function map by trimming content.
LLVM_ABI size_t size(SizeType Type=UniqueHashCount) const
LLVM_ABI void insert(const StableFunction &Func)
Insert a StableFunction object into the function map.
const StableFunctionEntries & at(HashFuncsMapType::key_type FunctionHash) const
LLVM_ABI void merge(const StableFunctionMap &OtherMap)
Merge a OtherMap into this function map.
LLVM_ABI std::optional< std::string > getNameForId(unsigned Id) const
Get the name associated with a given ID.
const HashFuncsMapType & getFunctionMap() const
Get the HashToFuncs map for serialization.
LLVM_ABI unsigned getIdOrCreateForName(StringRef Name)
Get an existing ID associated with the given name or create a new ID if it doesn't exist.
A stable function is a function with a stable hash while tracking the locations of ignored operands a...