xref: /llvm-project/llvm/lib/CGData/StableFunctionMap.cpp (revision fe69a20cc1e46bf8473aaef1be8a1805c80fc9d4)
17ec26b23SKyungwoo Lee //===-- StableFunctionMap.cpp ---------------------------------------------===//
27ec26b23SKyungwoo Lee //
37ec26b23SKyungwoo Lee // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47ec26b23SKyungwoo Lee // See https://llvm.org/LICENSE.txt for license information.
57ec26b23SKyungwoo Lee // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67ec26b23SKyungwoo Lee //
77ec26b23SKyungwoo Lee //===----------------------------------------------------------------------===//
87ec26b23SKyungwoo Lee //
97ec26b23SKyungwoo Lee // This implements the functionality for the StableFunctionMap class, which
107ec26b23SKyungwoo Lee // manages the mapping of stable function hashes to their metadata. It includes
117ec26b23SKyungwoo Lee // methods for inserting, merging, and finalizing function entries, as well as
127ec26b23SKyungwoo Lee // utilities for handling function names and IDs.
137ec26b23SKyungwoo Lee //
147ec26b23SKyungwoo Lee //===----------------------------------------------------------------------===//
157ec26b23SKyungwoo Lee 
167ec26b23SKyungwoo Lee #include "llvm/CGData/StableFunctionMap.h"
17*fe69a20cSKyungwoo Lee #include "llvm/ADT/SmallSet.h"
18d23c5c2dSKyungwoo Lee #include "llvm/Support/CommandLine.h"
19d23c5c2dSKyungwoo Lee #include "llvm/Support/Debug.h"
207ec26b23SKyungwoo Lee 
217ec26b23SKyungwoo Lee #define DEBUG_TYPE "stable-function-map"
227ec26b23SKyungwoo Lee 
237ec26b23SKyungwoo Lee using namespace llvm;
247ec26b23SKyungwoo Lee 
25d23c5c2dSKyungwoo Lee static cl::opt<unsigned>
26d23c5c2dSKyungwoo Lee     GlobalMergingMinMerges("global-merging-min-merges",
27d23c5c2dSKyungwoo Lee                            cl::desc("Minimum number of similar functions with "
28d23c5c2dSKyungwoo Lee                                     "the same hash required for merging."),
29d23c5c2dSKyungwoo Lee                            cl::init(2), cl::Hidden);
30d23c5c2dSKyungwoo Lee static cl::opt<unsigned> GlobalMergingMinInstrs(
31d23c5c2dSKyungwoo Lee     "global-merging-min-instrs",
32d23c5c2dSKyungwoo Lee     cl::desc("The minimum instruction count required when merging functions."),
33d23c5c2dSKyungwoo Lee     cl::init(1), cl::Hidden);
34d23c5c2dSKyungwoo Lee static cl::opt<unsigned> GlobalMergingMaxParams(
35d23c5c2dSKyungwoo Lee     "global-merging-max-params",
36d23c5c2dSKyungwoo Lee     cl::desc(
37d23c5c2dSKyungwoo Lee         "The maximum number of parameters allowed when merging functions."),
38d23c5c2dSKyungwoo Lee     cl::init(std::numeric_limits<unsigned>::max()), cl::Hidden);
39*fe69a20cSKyungwoo Lee static cl::opt<bool> GlobalMergingSkipNoParams(
40*fe69a20cSKyungwoo Lee     "global-merging-skip-no-params",
41*fe69a20cSKyungwoo Lee     cl::desc("Skip merging functions with no parameters."), cl::init(true),
42*fe69a20cSKyungwoo Lee     cl::Hidden);
43*fe69a20cSKyungwoo Lee static cl::opt<double> GlobalMergingInstOverhead(
44*fe69a20cSKyungwoo Lee     "global-merging-inst-overhead",
45*fe69a20cSKyungwoo Lee     cl::desc("The overhead cost associated with each instruction when lowering "
46*fe69a20cSKyungwoo Lee              "to machine instruction."),
47*fe69a20cSKyungwoo Lee     cl::init(1.2), cl::Hidden);
48*fe69a20cSKyungwoo Lee static cl::opt<double> GlobalMergingParamOverhead(
49d23c5c2dSKyungwoo Lee     "global-merging-param-overhead",
50d23c5c2dSKyungwoo Lee     cl::desc("The overhead cost associated with each parameter when merging "
51d23c5c2dSKyungwoo Lee              "functions."),
52*fe69a20cSKyungwoo Lee     cl::init(2.0), cl::Hidden);
53*fe69a20cSKyungwoo Lee static cl::opt<double>
54d23c5c2dSKyungwoo Lee     GlobalMergingCallOverhead("global-merging-call-overhead",
55d23c5c2dSKyungwoo Lee                               cl::desc("The overhead cost associated with each "
56d23c5c2dSKyungwoo Lee                                        "function call when merging functions."),
57*fe69a20cSKyungwoo Lee                               cl::init(1.0), cl::Hidden);
58*fe69a20cSKyungwoo Lee static cl::opt<double> GlobalMergingExtraThreshold(
59d23c5c2dSKyungwoo Lee     "global-merging-extra-threshold",
60d23c5c2dSKyungwoo Lee     cl::desc("An additional cost threshold that must be exceeded for merging "
61d23c5c2dSKyungwoo Lee              "to be considered beneficial."),
62*fe69a20cSKyungwoo Lee     cl::init(0.0), cl::Hidden);
63d23c5c2dSKyungwoo Lee 
647ec26b23SKyungwoo Lee unsigned StableFunctionMap::getIdOrCreateForName(StringRef Name) {
657ec26b23SKyungwoo Lee   auto It = NameToId.find(Name);
667ec26b23SKyungwoo Lee   if (It != NameToId.end())
677ec26b23SKyungwoo Lee     return It->second;
687ec26b23SKyungwoo Lee   unsigned Id = IdToName.size();
697ec26b23SKyungwoo Lee   assert(Id == NameToId.size() && "ID collision");
707ec26b23SKyungwoo Lee   IdToName.emplace_back(Name.str());
717ec26b23SKyungwoo Lee   NameToId[IdToName.back()] = Id;
727ec26b23SKyungwoo Lee   return Id;
737ec26b23SKyungwoo Lee }
747ec26b23SKyungwoo Lee 
757ec26b23SKyungwoo Lee std::optional<std::string> StableFunctionMap::getNameForId(unsigned Id) const {
767ec26b23SKyungwoo Lee   if (Id >= IdToName.size())
777ec26b23SKyungwoo Lee     return std::nullopt;
787ec26b23SKyungwoo Lee   return IdToName[Id];
797ec26b23SKyungwoo Lee }
807ec26b23SKyungwoo Lee 
817ec26b23SKyungwoo Lee void StableFunctionMap::insert(const StableFunction &Func) {
827ec26b23SKyungwoo Lee   assert(!Finalized && "Cannot insert after finalization");
837ec26b23SKyungwoo Lee   auto FuncNameId = getIdOrCreateForName(Func.FunctionName);
847ec26b23SKyungwoo Lee   auto ModuleNameId = getIdOrCreateForName(Func.ModuleName);
857ec26b23SKyungwoo Lee   auto IndexOperandHashMap = std::make_unique<IndexOperandHashMapType>();
867ec26b23SKyungwoo Lee   for (auto &[Index, Hash] : Func.IndexOperandHashes)
877ec26b23SKyungwoo Lee     (*IndexOperandHashMap)[Index] = Hash;
887ec26b23SKyungwoo Lee   auto FuncEntry = std::make_unique<StableFunctionEntry>(
897ec26b23SKyungwoo Lee       Func.Hash, FuncNameId, ModuleNameId, Func.InstCount,
907ec26b23SKyungwoo Lee       std::move(IndexOperandHashMap));
917ec26b23SKyungwoo Lee   insert(std::move(FuncEntry));
927ec26b23SKyungwoo Lee }
937ec26b23SKyungwoo Lee 
947ec26b23SKyungwoo Lee void StableFunctionMap::merge(const StableFunctionMap &OtherMap) {
957ec26b23SKyungwoo Lee   assert(!Finalized && "Cannot merge after finalization");
967ec26b23SKyungwoo Lee   for (auto &[Hash, Funcs] : OtherMap.HashToFuncs) {
977ec26b23SKyungwoo Lee     auto &ThisFuncs = HashToFuncs[Hash];
987ec26b23SKyungwoo Lee     for (auto &Func : Funcs) {
997ec26b23SKyungwoo Lee       auto FuncNameId =
1007ec26b23SKyungwoo Lee           getIdOrCreateForName(*OtherMap.getNameForId(Func->FunctionNameId));
1017ec26b23SKyungwoo Lee       auto ModuleNameId =
1027ec26b23SKyungwoo Lee           getIdOrCreateForName(*OtherMap.getNameForId(Func->ModuleNameId));
1037ec26b23SKyungwoo Lee       auto ClonedIndexOperandHashMap =
1047ec26b23SKyungwoo Lee           std::make_unique<IndexOperandHashMapType>(*Func->IndexOperandHashMap);
1057ec26b23SKyungwoo Lee       ThisFuncs.emplace_back(std::make_unique<StableFunctionEntry>(
1067ec26b23SKyungwoo Lee           Func->Hash, FuncNameId, ModuleNameId, Func->InstCount,
1077ec26b23SKyungwoo Lee           std::move(ClonedIndexOperandHashMap)));
1087ec26b23SKyungwoo Lee     }
1097ec26b23SKyungwoo Lee   }
1107ec26b23SKyungwoo Lee }
1117ec26b23SKyungwoo Lee 
1127ec26b23SKyungwoo Lee size_t StableFunctionMap::size(SizeType Type) const {
1137ec26b23SKyungwoo Lee   switch (Type) {
1147ec26b23SKyungwoo Lee   case UniqueHashCount:
1157ec26b23SKyungwoo Lee     return HashToFuncs.size();
1167ec26b23SKyungwoo Lee   case TotalFunctionCount: {
1177ec26b23SKyungwoo Lee     size_t Count = 0;
1187ec26b23SKyungwoo Lee     for (auto &Funcs : HashToFuncs)
1197ec26b23SKyungwoo Lee       Count += Funcs.second.size();
1207ec26b23SKyungwoo Lee     return Count;
1217ec26b23SKyungwoo Lee   }
1227ec26b23SKyungwoo Lee   case MergeableFunctionCount: {
1237ec26b23SKyungwoo Lee     size_t Count = 0;
1247ec26b23SKyungwoo Lee     for (auto &[Hash, Funcs] : HashToFuncs)
1257ec26b23SKyungwoo Lee       if (Funcs.size() >= 2)
1267ec26b23SKyungwoo Lee         Count += Funcs.size();
1277ec26b23SKyungwoo Lee     return Count;
1287ec26b23SKyungwoo Lee   }
1297ec26b23SKyungwoo Lee   }
1307ec26b23SKyungwoo Lee   llvm_unreachable("Unhandled size type");
1317ec26b23SKyungwoo Lee }
1327ec26b23SKyungwoo Lee 
1337ec26b23SKyungwoo Lee using ParamLocs = SmallVector<IndexPair>;
1347ec26b23SKyungwoo Lee static void removeIdenticalIndexPair(
1357ec26b23SKyungwoo Lee     SmallVector<std::unique_ptr<StableFunctionMap::StableFunctionEntry>> &SFS) {
1367ec26b23SKyungwoo Lee   auto &RSF = SFS[0];
1377ec26b23SKyungwoo Lee   unsigned StableFunctionCount = SFS.size();
1387ec26b23SKyungwoo Lee 
1397ec26b23SKyungwoo Lee   SmallVector<IndexPair> ToDelete;
1407ec26b23SKyungwoo Lee   for (auto &[Pair, Hash] : *(RSF->IndexOperandHashMap)) {
1417ec26b23SKyungwoo Lee     bool Identical = true;
1427ec26b23SKyungwoo Lee     for (unsigned J = 1; J < StableFunctionCount; ++J) {
1437ec26b23SKyungwoo Lee       auto &SF = SFS[J];
1447ec26b23SKyungwoo Lee       const auto &SHash = SF->IndexOperandHashMap->at(Pair);
1457ec26b23SKyungwoo Lee       if (Hash != SHash) {
1467ec26b23SKyungwoo Lee         Identical = false;
1477ec26b23SKyungwoo Lee         break;
1487ec26b23SKyungwoo Lee       }
1497ec26b23SKyungwoo Lee     }
1507ec26b23SKyungwoo Lee 
1517ec26b23SKyungwoo Lee     // No need to parameterize them if the hashes are identical across stable
1527ec26b23SKyungwoo Lee     // functions.
1537ec26b23SKyungwoo Lee     if (Identical)
1547ec26b23SKyungwoo Lee       ToDelete.emplace_back(Pair);
1557ec26b23SKyungwoo Lee   }
1567ec26b23SKyungwoo Lee 
1577ec26b23SKyungwoo Lee   for (auto &Pair : ToDelete)
1587ec26b23SKyungwoo Lee     for (auto &SF : SFS)
1597ec26b23SKyungwoo Lee       SF->IndexOperandHashMap->erase(Pair);
1607ec26b23SKyungwoo Lee }
1617ec26b23SKyungwoo Lee 
162d23c5c2dSKyungwoo Lee static bool isProfitable(
163d23c5c2dSKyungwoo Lee     const SmallVector<std::unique_ptr<StableFunctionMap::StableFunctionEntry>>
164d23c5c2dSKyungwoo Lee         &SFS) {
165d23c5c2dSKyungwoo Lee   unsigned StableFunctionCount = SFS.size();
166d23c5c2dSKyungwoo Lee   if (StableFunctionCount < GlobalMergingMinMerges)
167d23c5c2dSKyungwoo Lee     return false;
168d23c5c2dSKyungwoo Lee 
169d23c5c2dSKyungwoo Lee   unsigned InstCount = SFS[0]->InstCount;
170d23c5c2dSKyungwoo Lee   if (InstCount < GlobalMergingMinInstrs)
171d23c5c2dSKyungwoo Lee     return false;
172d23c5c2dSKyungwoo Lee 
173*fe69a20cSKyungwoo Lee   double Cost = 0.0;
174*fe69a20cSKyungwoo Lee   SmallSet<stable_hash, 8> UniqueHashVals;
175*fe69a20cSKyungwoo Lee   for (auto &SF : SFS) {
176*fe69a20cSKyungwoo Lee     UniqueHashVals.clear();
177*fe69a20cSKyungwoo Lee     for (auto &[IndexPair, Hash] : *SF->IndexOperandHashMap)
178*fe69a20cSKyungwoo Lee       UniqueHashVals.insert(Hash);
179*fe69a20cSKyungwoo Lee     unsigned ParamCount = UniqueHashVals.size();
180d23c5c2dSKyungwoo Lee     if (ParamCount > GlobalMergingMaxParams)
181d23c5c2dSKyungwoo Lee       return false;
182*fe69a20cSKyungwoo Lee     // Theoretically, if ParamCount is 0, it results in identical code folding
183*fe69a20cSKyungwoo Lee     // (ICF), which we can skip merging here since the linker already handles
184*fe69a20cSKyungwoo Lee     // ICF. This pass would otherwise introduce unnecessary thunks that are
185*fe69a20cSKyungwoo Lee     // merely direct jumps. However, enabling this could be beneficial depending
186*fe69a20cSKyungwoo Lee     // on downstream passes, so we provide an option for it.
187*fe69a20cSKyungwoo Lee     if (GlobalMergingSkipNoParams && ParamCount == 0)
188*fe69a20cSKyungwoo Lee       return false;
189*fe69a20cSKyungwoo Lee     Cost += ParamCount * GlobalMergingParamOverhead + GlobalMergingCallOverhead;
190*fe69a20cSKyungwoo Lee   }
191*fe69a20cSKyungwoo Lee   Cost += GlobalMergingExtraThreshold;
192d23c5c2dSKyungwoo Lee 
193*fe69a20cSKyungwoo Lee   double Benefit =
194*fe69a20cSKyungwoo Lee       InstCount * (StableFunctionCount - 1) * GlobalMergingInstOverhead;
195d23c5c2dSKyungwoo Lee   bool Result = Benefit > Cost;
196d23c5c2dSKyungwoo Lee   LLVM_DEBUG(dbgs() << "isProfitable: Hash = " << SFS[0]->Hash << ", "
197d23c5c2dSKyungwoo Lee                     << "StableFunctionCount = " << StableFunctionCount
198d23c5c2dSKyungwoo Lee                     << ", InstCount = " << InstCount
199d23c5c2dSKyungwoo Lee                     << ", Benefit = " << Benefit << ", Cost = " << Cost
200d23c5c2dSKyungwoo Lee                     << ", Result = " << (Result ? "true" : "false") << "\n");
201d23c5c2dSKyungwoo Lee   return Result;
202d23c5c2dSKyungwoo Lee }
203d23c5c2dSKyungwoo Lee 
204d23c5c2dSKyungwoo Lee void StableFunctionMap::finalize(bool SkipTrim) {
2057ec26b23SKyungwoo Lee   for (auto It = HashToFuncs.begin(); It != HashToFuncs.end(); ++It) {
2067ec26b23SKyungwoo Lee     auto &[StableHash, SFS] = *It;
2077ec26b23SKyungwoo Lee 
2087ec26b23SKyungwoo Lee     // Group stable functions by ModuleIdentifier.
2097ec26b23SKyungwoo Lee     std::stable_sort(SFS.begin(), SFS.end(),
2107ec26b23SKyungwoo Lee                      [&](const std::unique_ptr<StableFunctionEntry> &L,
2117ec26b23SKyungwoo Lee                          const std::unique_ptr<StableFunctionEntry> &R) {
2127ec26b23SKyungwoo Lee                        return *getNameForId(L->ModuleNameId) <
2137ec26b23SKyungwoo Lee                               *getNameForId(R->ModuleNameId);
2147ec26b23SKyungwoo Lee                      });
2157ec26b23SKyungwoo Lee 
2167ec26b23SKyungwoo Lee     // Consider the first function as the root function.
2177ec26b23SKyungwoo Lee     auto &RSF = SFS[0];
2187ec26b23SKyungwoo Lee 
2197ec26b23SKyungwoo Lee     bool Invalid = false;
2207ec26b23SKyungwoo Lee     unsigned StableFunctionCount = SFS.size();
2217ec26b23SKyungwoo Lee     for (unsigned I = 1; I < StableFunctionCount; ++I) {
2227ec26b23SKyungwoo Lee       auto &SF = SFS[I];
2237ec26b23SKyungwoo Lee       assert(RSF->Hash == SF->Hash);
2247ec26b23SKyungwoo Lee       if (RSF->InstCount != SF->InstCount) {
2257ec26b23SKyungwoo Lee         Invalid = true;
2267ec26b23SKyungwoo Lee         break;
2277ec26b23SKyungwoo Lee       }
2287ec26b23SKyungwoo Lee       if (RSF->IndexOperandHashMap->size() != SF->IndexOperandHashMap->size()) {
2297ec26b23SKyungwoo Lee         Invalid = true;
2307ec26b23SKyungwoo Lee         break;
2317ec26b23SKyungwoo Lee       }
2327ec26b23SKyungwoo Lee       for (auto &P : *RSF->IndexOperandHashMap) {
2337ec26b23SKyungwoo Lee         auto &InstOpndIndex = P.first;
2347ec26b23SKyungwoo Lee         if (!SF->IndexOperandHashMap->count(InstOpndIndex)) {
2357ec26b23SKyungwoo Lee           Invalid = true;
2367ec26b23SKyungwoo Lee           break;
2377ec26b23SKyungwoo Lee         }
2387ec26b23SKyungwoo Lee       }
2397ec26b23SKyungwoo Lee     }
2407ec26b23SKyungwoo Lee     if (Invalid) {
2417ec26b23SKyungwoo Lee       HashToFuncs.erase(It);
2427ec26b23SKyungwoo Lee       continue;
2437ec26b23SKyungwoo Lee     }
2447ec26b23SKyungwoo Lee 
245d23c5c2dSKyungwoo Lee     if (SkipTrim)
246d23c5c2dSKyungwoo Lee       continue;
247d23c5c2dSKyungwoo Lee 
2487ec26b23SKyungwoo Lee     // Trim the index pair that has the same operand hash across
2497ec26b23SKyungwoo Lee     // stable functions.
2507ec26b23SKyungwoo Lee     removeIdenticalIndexPair(SFS);
251d23c5c2dSKyungwoo Lee 
252d23c5c2dSKyungwoo Lee     if (!isProfitable(SFS))
253d23c5c2dSKyungwoo Lee       HashToFuncs.erase(It);
2547ec26b23SKyungwoo Lee   }
2557ec26b23SKyungwoo Lee 
2567ec26b23SKyungwoo Lee   Finalized = true;
2577ec26b23SKyungwoo Lee }
258