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