LLVM 24.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 "CGDataOptions.h"
18#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 auto It = NameToId.find(Name);
28 if (It != NameToId.end())
29 return It->second;
30 unsigned Id = IdToName.size();
31 assert(Id == NameToId.size() && "ID collision");
32 IdToName.emplace_back(Name.str());
33 NameToId[IdToName.back()] = Id;
34 return Id;
35}
36
37std::optional<std::string> StableFunctionMap::getNameForId(unsigned Id) const {
38 if (Id >= IdToName.size())
39 return std::nullopt;
40 return IdToName[Id];
41}
42
44 assert(!Finalized && "Cannot insert after finalization");
45 auto FuncNameId = getIdOrCreateForName(Func.FunctionName);
46 auto ModuleNameId = getIdOrCreateForName(Func.ModuleName);
47 auto IndexOperandHashMap = std::make_unique<IndexOperandHashMapType>();
48 for (auto &[Index, Hash] : Func.IndexOperandHashes)
49 (*IndexOperandHashMap)[Index] = Hash;
50 auto FuncEntry = std::make_unique<StableFunctionEntry>(
51 Func.Hash, FuncNameId, ModuleNameId, Func.InstCount,
52 std::move(IndexOperandHashMap));
53 insert(std::move(FuncEntry));
54}
55
57 assert(!Finalized && "Cannot merge after finalization");
58 deserializeLazyLoadingEntries();
59 for (auto &[Hash, Funcs] : OtherMap.HashToFuncs) {
60 auto &ThisFuncs = HashToFuncs[Hash].Entries;
61 for (auto &Func : Funcs.Entries) {
62 auto FuncNameId =
63 getIdOrCreateForName(*OtherMap.getNameForId(Func->FunctionNameId));
64 auto ModuleNameId =
65 getIdOrCreateForName(*OtherMap.getNameForId(Func->ModuleNameId));
66 auto ClonedIndexOperandHashMap =
67 std::make_unique<IndexOperandHashMapType>(*Func->IndexOperandHashMap);
68 ThisFuncs.emplace_back(std::make_unique<StableFunctionEntry>(
69 Func->Hash, FuncNameId, ModuleNameId, Func->InstCount,
70 std::move(ClonedIndexOperandHashMap)));
71 }
72 }
73}
74
76 switch (Type) {
77 case UniqueHashCount:
78 return HashToFuncs.size();
79 case TotalFunctionCount: {
80 deserializeLazyLoadingEntries();
81 size_t Count = 0;
82 for (auto &Funcs : HashToFuncs)
83 Count += Funcs.second.Entries.size();
84 return Count;
85 }
87 deserializeLazyLoadingEntries();
88 size_t Count = 0;
89 for (auto &[Hash, Funcs] : HashToFuncs)
90 if (Funcs.Entries.size() >= 2)
91 Count += Funcs.Entries.size();
92 return Count;
93 }
94 }
95 llvm_unreachable("Unhandled size type");
96}
97
99StableFunctionMap::at(HashFuncsMapType::key_type FunctionHash) const {
100 auto It = HashToFuncs.find(FunctionHash);
101 assert(It != HashToFuncs.end() && "FunctionHash not found!");
102 if (isLazilyLoaded())
103 deserializeLazyLoadingEntry(It);
104 return It->second.Entries;
105}
106
107void StableFunctionMap::deserializeLazyLoadingEntry(
108 HashFuncsMapType::iterator It) const {
109 assert(isLazilyLoaded() && "Cannot deserialize non-lazily-loaded map");
110 auto &[Hash, Storage] = *It;
111 std::call_once(Storage.LazyLoadFlag,
112 [this, HashArg = Hash, &StorageArg = Storage]() {
113 for (auto Offset : StorageArg.Offsets)
114 StableFunctionMapRecord::deserializeEntry(
115 reinterpret_cast<const unsigned char *>(Offset),
116 HashArg, const_cast<StableFunctionMap *>(this));
117 });
118}
119
120void StableFunctionMap::deserializeLazyLoadingEntries() const {
121 if (!isLazilyLoaded())
122 return;
123 for (auto It = HashToFuncs.begin(); It != HashToFuncs.end(); ++It)
124 deserializeLazyLoadingEntry(It);
125}
126
129 // Ensure all entries are deserialized before returning the raw map.
130 if (isLazilyLoaded())
131 deserializeLazyLoadingEntries();
132 return HashToFuncs;
133}
134
136static void
138 auto &RSF = SFS[0];
139 unsigned StableFunctionCount = SFS.size();
140
141 SmallVector<IndexPair> ToDelete;
142 for (auto &[Pair, Hash] : *(RSF->IndexOperandHashMap)) {
143 bool Identical = true;
144 for (unsigned J = 1; J < StableFunctionCount; ++J) {
145 auto &SF = SFS[J];
146 const auto &SHash = SF->IndexOperandHashMap->at(Pair);
147 if (Hash != SHash) {
148 Identical = false;
149 break;
150 }
151 }
152
153 // No need to parameterize them if the hashes are identical across stable
154 // functions.
155 if (Identical)
156 ToDelete.emplace_back(Pair);
157 }
158
159 for (auto &Pair : ToDelete)
160 for (auto &SF : SFS)
161 SF->IndexOperandHashMap->erase(Pair);
162}
163
165 const CGDataOptions &Opts = CGDataOptions::Global;
166 unsigned StableFunctionCount = SFS.size();
167 if (StableFunctionCount < Opts.global_merging_min_merges)
168 return false;
169
170 unsigned InstCount = SFS[0]->InstCount;
171 if (InstCount < Opts.global_merging_min_instrs)
172 return false;
173
174 double Cost = 0.0;
175 SmallSet<stable_hash, 8> UniqueHashVals;
176 for (auto &SF : SFS) {
177 UniqueHashVals.clear();
178 for (auto &[IndexPair, Hash] : *SF->IndexOperandHashMap)
179 UniqueHashVals.insert(Hash);
180 unsigned ParamCount = UniqueHashVals.size();
181 if (ParamCount > Opts.global_merging_max_params)
182 return false;
183 // Theoretically, if ParamCount is 0, it results in identical code folding
184 // (ICF), which we can skip merging here since the linker already handles
185 // ICF. This pass would otherwise introduce unnecessary thunks that are
186 // merely direct jumps. However, enabling this could be beneficial depending
187 // on downstream passes, so we provide an option for it.
188 if (Opts.global_merging_skip_no_params && ParamCount == 0)
189 return false;
190 Cost += ParamCount * Opts.global_merging_param_overhead +
191 Opts.global_merging_call_overhead;
192 }
193 Cost += Opts.global_merging_extra_threshold;
194
195 double Benefit =
196 InstCount * (StableFunctionCount - 1) * Opts.global_merging_inst_overhead;
197 bool Result = Benefit > Cost;
198 LLVM_DEBUG(dbgs() << "isProfitable: Hash = " << SFS[0]->Hash << ", "
199 << "StableFunctionCount = " << StableFunctionCount
200 << ", InstCount = " << InstCount
201 << ", Benefit = " << Benefit << ", Cost = " << Cost
202 << ", Result = " << (Result ? "true" : "false") << "\n");
203 return Result;
204}
205
206void StableFunctionMap::finalize(bool SkipTrim) {
207 deserializeLazyLoadingEntries();
209 for (auto It = HashToFuncs.begin(); It != HashToFuncs.end(); ++It) {
210 auto &[StableHash, Storage] = *It;
211 auto &SFS = Storage.Entries;
212
213 // Group stable functions by ModuleIdentifier.
214 llvm::stable_sort(SFS, [&](const std::unique_ptr<StableFunctionEntry> &L,
215 const std::unique_ptr<StableFunctionEntry> &R) {
216 return *getNameForId(L->ModuleNameId) < *getNameForId(R->ModuleNameId);
217 });
218
219 // Consider the first function as the root function.
220 auto &RSF = SFS[0];
221
222 bool Invalid = false;
223 unsigned StableFunctionCount = SFS.size();
224 for (unsigned I = 1; I < StableFunctionCount; ++I) {
225 auto &SF = SFS[I];
226 assert(RSF->Hash == SF->Hash);
227 if (RSF->InstCount != SF->InstCount) {
228 Invalid = true;
229 break;
230 }
231 if (RSF->IndexOperandHashMap->size() != SF->IndexOperandHashMap->size()) {
232 Invalid = true;
233 break;
234 }
235 for (auto &P : *RSF->IndexOperandHashMap) {
236 auto &InstOpndIndex = P.first;
237 if (!SF->IndexOperandHashMap->count(InstOpndIndex)) {
238 Invalid = true;
239 break;
240 }
241 }
242 }
243 if (Invalid) {
244 ToDelete.push_back(It);
245 continue;
246 }
247
248 if (SkipTrim)
249 continue;
250
251 // Trim the index pair that has the same operand hash across
252 // stable functions.
254
255 if (!isProfitable(SFS))
256 ToDelete.push_back(It);
257 }
258 for (auto It : ToDelete)
259 HashToFuncs.erase(It);
260
261 Finalized = true;
262}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
#define I(x, y, z)
Definition MD5.cpp:57
#define P(N)
This file defines the SmallSet class.
static bool isProfitable(const StableFunctionMap::StableFunctionEntries &SFS)
static void removeIdenticalIndexPair(StableFunctionMap::StableFunctionEntries &SFS)
#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
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:184
size_type size() const
Definition SmallSet.h:171
reference emplace_back(ArgTypes &&... Args)
iterator erase(const_iterator CI)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
This is an optimization pass for GlobalISel generic memory operations.
void stable_sort(R &&Range)
Definition STLExtras.h:2132
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
SmallVector< IndexPair, 4 > ParamLocs
std::pair< unsigned, unsigned > IndexPair
The pair of an instruction index and a operand index.
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Count
Definition InstrProf.h:145
std::unordered_map< stable_hash, EntryStorage > HashFuncsMapType
SmallVector< std::unique_ptr< StableFunctionEntry > > StableFunctionEntries
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.
LLVM_ABI 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.
LLVM_ABI 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...