xref: /freebsd/contrib/llvm-project/llvm/lib/CGData/StableFunctionMap.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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