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