1 //===-- StableFunctionMap.cpp ---------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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.
14 //===----------------------------------------------------------------------===//
16 #include "llvm/CGData/StableFunctionMap.h"
17 #include "llvm/ADT/SmallSet.h"
18 #include "llvm/Support/CommandLine.h"
19 #include "llvm/Support/Debug.h"
21 #define DEBUG_TYPE "stable-function-map"
25 static cl::opt
<unsigned>
26 GlobalMergingMinMerges("global-merging-min-merges",
27 cl::desc("Minimum number of similar functions with "
28 "the same hash required for merging."),
29 cl::init(2), cl::Hidden
);
30 static cl::opt
<unsigned> GlobalMergingMinInstrs(
31 "global-merging-min-instrs",
32 cl::desc("The minimum instruction count required when merging functions."),
33 cl::init(1), cl::Hidden
);
34 static cl::opt
<unsigned> GlobalMergingMaxParams(
35 "global-merging-max-params",
37 "The maximum number of parameters allowed when merging functions."),
38 cl::init(std::numeric_limits
<unsigned>::max()), cl::Hidden
);
39 static cl::opt
<bool> GlobalMergingSkipNoParams(
40 "global-merging-skip-no-params",
41 cl::desc("Skip merging functions with no parameters."), cl::init(true),
43 static cl::opt
<double> GlobalMergingInstOverhead(
44 "global-merging-inst-overhead",
45 cl::desc("The overhead cost associated with each instruction when lowering "
46 "to machine instruction."),
47 cl::init(1.2), cl::Hidden
);
48 static cl::opt
<double> GlobalMergingParamOverhead(
49 "global-merging-param-overhead",
50 cl::desc("The overhead cost associated with each parameter when merging "
52 cl::init(2.0), cl::Hidden
);
53 static cl::opt
<double>
54 GlobalMergingCallOverhead("global-merging-call-overhead",
55 cl::desc("The overhead cost associated with each "
56 "function call when merging functions."),
57 cl::init(1.0), cl::Hidden
);
58 static cl::opt
<double> GlobalMergingExtraThreshold(
59 "global-merging-extra-threshold",
60 cl::desc("An additional cost threshold that must be exceeded for merging "
61 "to be considered beneficial."),
62 cl::init(0.0), cl::Hidden
);
64 unsigned StableFunctionMap::getIdOrCreateForName(StringRef Name
) {
65 auto It
= NameToId
.find(Name
);
66 if (It
!= NameToId
.end())
68 unsigned Id
= IdToName
.size();
69 assert(Id
== NameToId
.size() && "ID collision");
70 IdToName
.emplace_back(Name
.str());
71 NameToId
[IdToName
.back()] = Id
;
75 std::optional
<std::string
> StableFunctionMap::getNameForId(unsigned Id
) const {
76 if (Id
>= IdToName
.size())
81 void StableFunctionMap::insert(const StableFunction
&Func
) {
82 assert(!Finalized
&& "Cannot insert after finalization");
83 auto FuncNameId
= getIdOrCreateForName(Func
.FunctionName
);
84 auto ModuleNameId
= getIdOrCreateForName(Func
.ModuleName
);
85 auto IndexOperandHashMap
= std::make_unique
<IndexOperandHashMapType
>();
86 for (auto &[Index
, Hash
] : Func
.IndexOperandHashes
)
87 (*IndexOperandHashMap
)[Index
] = Hash
;
88 auto FuncEntry
= std::make_unique
<StableFunctionEntry
>(
89 Func
.Hash
, FuncNameId
, ModuleNameId
, Func
.InstCount
,
90 std::move(IndexOperandHashMap
));
91 insert(std::move(FuncEntry
));
94 void StableFunctionMap::merge(const StableFunctionMap
&OtherMap
) {
95 assert(!Finalized
&& "Cannot merge after finalization");
96 for (auto &[Hash
, Funcs
] : OtherMap
.HashToFuncs
) {
97 auto &ThisFuncs
= HashToFuncs
[Hash
];
98 for (auto &Func
: Funcs
) {
100 getIdOrCreateForName(*OtherMap
.getNameForId(Func
->FunctionNameId
));
102 getIdOrCreateForName(*OtherMap
.getNameForId(Func
->ModuleNameId
));
103 auto ClonedIndexOperandHashMap
=
104 std::make_unique
<IndexOperandHashMapType
>(*Func
->IndexOperandHashMap
);
105 ThisFuncs
.emplace_back(std::make_unique
<StableFunctionEntry
>(
106 Func
->Hash
, FuncNameId
, ModuleNameId
, Func
->InstCount
,
107 std::move(ClonedIndexOperandHashMap
)));
112 size_t StableFunctionMap::size(SizeType Type
) const {
114 case UniqueHashCount
:
115 return HashToFuncs
.size();
116 case TotalFunctionCount
: {
118 for (auto &Funcs
: HashToFuncs
)
119 Count
+= Funcs
.second
.size();
122 case MergeableFunctionCount
: {
124 for (auto &[Hash
, Funcs
] : HashToFuncs
)
125 if (Funcs
.size() >= 2)
126 Count
+= Funcs
.size();
130 llvm_unreachable("Unhandled size type");
133 using ParamLocs
= SmallVector
<IndexPair
>;
134 static void removeIdenticalIndexPair(
135 SmallVector
<std::unique_ptr
<StableFunctionMap::StableFunctionEntry
>> &SFS
) {
137 unsigned StableFunctionCount
= SFS
.size();
139 SmallVector
<IndexPair
> ToDelete
;
140 for (auto &[Pair
, Hash
] : *(RSF
->IndexOperandHashMap
)) {
141 bool Identical
= true;
142 for (unsigned J
= 1; J
< StableFunctionCount
; ++J
) {
144 const auto &SHash
= SF
->IndexOperandHashMap
->at(Pair
);
151 // No need to parameterize them if the hashes are identical across stable
154 ToDelete
.emplace_back(Pair
);
157 for (auto &Pair
: ToDelete
)
159 SF
->IndexOperandHashMap
->erase(Pair
);
162 static bool isProfitable(
163 const SmallVector
<std::unique_ptr
<StableFunctionMap::StableFunctionEntry
>>
165 unsigned StableFunctionCount
= SFS
.size();
166 if (StableFunctionCount
< GlobalMergingMinMerges
)
169 unsigned InstCount
= SFS
[0]->InstCount
;
170 if (InstCount
< GlobalMergingMinInstrs
)
174 SmallSet
<stable_hash
, 8> UniqueHashVals
;
175 for (auto &SF
: SFS
) {
176 UniqueHashVals
.clear();
177 for (auto &[IndexPair
, Hash
] : *SF
->IndexOperandHashMap
)
178 UniqueHashVals
.insert(Hash
);
179 unsigned ParamCount
= UniqueHashVals
.size();
180 if (ParamCount
> GlobalMergingMaxParams
)
182 // Theoretically, if ParamCount is 0, it results in identical code folding
183 // (ICF), which we can skip merging here since the linker already handles
184 // ICF. This pass would otherwise introduce unnecessary thunks that are
185 // merely direct jumps. However, enabling this could be beneficial depending
186 // on downstream passes, so we provide an option for it.
187 if (GlobalMergingSkipNoParams
&& ParamCount
== 0)
189 Cost
+= ParamCount
* GlobalMergingParamOverhead
+ GlobalMergingCallOverhead
;
191 Cost
+= GlobalMergingExtraThreshold
;
194 InstCount
* (StableFunctionCount
- 1) * GlobalMergingInstOverhead
;
195 bool Result
= Benefit
> Cost
;
196 LLVM_DEBUG(dbgs() << "isProfitable: Hash = " << SFS
[0]->Hash
<< ", "
197 << "StableFunctionCount = " << StableFunctionCount
198 << ", InstCount = " << InstCount
199 << ", Benefit = " << Benefit
<< ", Cost = " << Cost
200 << ", Result = " << (Result
? "true" : "false") << "\n");
204 void StableFunctionMap::finalize(bool SkipTrim
) {
205 for (auto It
= HashToFuncs
.begin(); It
!= HashToFuncs
.end(); ++It
) {
206 auto &[StableHash
, SFS
] = *It
;
208 // Group stable functions by ModuleIdentifier.
209 std::stable_sort(SFS
.begin(), SFS
.end(),
210 [&](const std::unique_ptr
<StableFunctionEntry
> &L
,
211 const std::unique_ptr
<StableFunctionEntry
> &R
) {
212 return *getNameForId(L
->ModuleNameId
) <
213 *getNameForId(R
->ModuleNameId
);
216 // Consider the first function as the root function.
219 bool Invalid
= false;
220 unsigned StableFunctionCount
= SFS
.size();
221 for (unsigned I
= 1; I
< StableFunctionCount
; ++I
) {
223 assert(RSF
->Hash
== SF
->Hash
);
224 if (RSF
->InstCount
!= SF
->InstCount
) {
228 if (RSF
->IndexOperandHashMap
->size() != SF
->IndexOperandHashMap
->size()) {
232 for (auto &P
: *RSF
->IndexOperandHashMap
) {
233 auto &InstOpndIndex
= P
.first
;
234 if (!SF
->IndexOperandHashMap
->count(InstOpndIndex
)) {
241 HashToFuncs
.erase(It
);
248 // Trim the index pair that has the same operand hash across
250 removeIdenticalIndexPair(SFS
);
252 if (!isProfitable(SFS
))
253 HashToFuncs
.erase(It
);