xref: /freebsd/contrib/llvm-project/llvm/include/llvm/Transforms/IPO/SampleProfileMatcher.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1*0fca6ea1SDimitry Andric //===- Transforms/IPO/SampleProfileMatcher.h ----------*- C++ -*-===//
2*0fca6ea1SDimitry Andric //
3*0fca6ea1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0fca6ea1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0fca6ea1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0fca6ea1SDimitry Andric //
7*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===//
8*0fca6ea1SDimitry Andric //
9*0fca6ea1SDimitry Andric /// \file
10*0fca6ea1SDimitry Andric /// This file provides the interface for SampleProfileMatcher.
11*0fca6ea1SDimitry Andric //
12*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===//
13*0fca6ea1SDimitry Andric 
14*0fca6ea1SDimitry Andric #ifndef LLVM_TRANSFORMS_IPO_SAMPLEPROFILEMATCHER_H
15*0fca6ea1SDimitry Andric #define LLVM_TRANSFORMS_IPO_SAMPLEPROFILEMATCHER_H
16*0fca6ea1SDimitry Andric 
17*0fca6ea1SDimitry Andric #include "llvm/ADT/StringSet.h"
18*0fca6ea1SDimitry Andric #include "llvm/Transforms/Utils/SampleProfileLoaderBaseImpl.h"
19*0fca6ea1SDimitry Andric 
20*0fca6ea1SDimitry Andric namespace llvm {
21*0fca6ea1SDimitry Andric 
22*0fca6ea1SDimitry Andric using AnchorList = std::vector<std::pair<LineLocation, FunctionId>>;
23*0fca6ea1SDimitry Andric using AnchorMap = std::map<LineLocation, FunctionId>;
24*0fca6ea1SDimitry Andric 
25*0fca6ea1SDimitry Andric // Sample profile matching - fuzzy match.
26*0fca6ea1SDimitry Andric class SampleProfileMatcher {
27*0fca6ea1SDimitry Andric   Module &M;
28*0fca6ea1SDimitry Andric   SampleProfileReader &Reader;
29*0fca6ea1SDimitry Andric   LazyCallGraph &CG;
30*0fca6ea1SDimitry Andric   const PseudoProbeManager *ProbeManager;
31*0fca6ea1SDimitry Andric   const ThinOrFullLTOPhase LTOPhase;
32*0fca6ea1SDimitry Andric   SampleProfileMap FlattenedProfiles;
33*0fca6ea1SDimitry Andric   // For each function, the matcher generates a map, of which each entry is a
34*0fca6ea1SDimitry Andric   // mapping from the source location of current build to the source location
35*0fca6ea1SDimitry Andric   // in the profile.
36*0fca6ea1SDimitry Andric   StringMap<LocToLocMap> FuncMappings;
37*0fca6ea1SDimitry Andric 
38*0fca6ea1SDimitry Andric   // Match state for an anchor/callsite.
39*0fca6ea1SDimitry Andric   enum class MatchState {
40*0fca6ea1SDimitry Andric     Unknown = 0,
41*0fca6ea1SDimitry Andric     // Initial match between input profile and current IR.
42*0fca6ea1SDimitry Andric     InitialMatch = 1,
43*0fca6ea1SDimitry Andric     // Initial mismatch between input profile and current IR.
44*0fca6ea1SDimitry Andric     InitialMismatch = 2,
45*0fca6ea1SDimitry Andric     // InitialMatch stays matched after fuzzy profile matching.
46*0fca6ea1SDimitry Andric     UnchangedMatch = 3,
47*0fca6ea1SDimitry Andric     // InitialMismatch stays mismatched after fuzzy profile matching.
48*0fca6ea1SDimitry Andric     UnchangedMismatch = 4,
49*0fca6ea1SDimitry Andric     // InitialMismatch is recovered after fuzzy profile matching.
50*0fca6ea1SDimitry Andric     RecoveredMismatch = 5,
51*0fca6ea1SDimitry Andric     // InitialMatch is removed and becomes mismatched after fuzzy profile
52*0fca6ea1SDimitry Andric     // matching.
53*0fca6ea1SDimitry Andric     RemovedMatch = 6,
54*0fca6ea1SDimitry Andric   };
55*0fca6ea1SDimitry Andric 
56*0fca6ea1SDimitry Andric   // For each function, store every callsite and its matching state into this
57*0fca6ea1SDimitry Andric   // map, of which each entry is a pair of callsite location and MatchState.
58*0fca6ea1SDimitry Andric   // This is used for profile staleness computation and report.
59*0fca6ea1SDimitry Andric   StringMap<std::unordered_map<LineLocation, MatchState, LineLocationHash>>
60*0fca6ea1SDimitry Andric       FuncCallsiteMatchStates;
61*0fca6ea1SDimitry Andric 
62*0fca6ea1SDimitry Andric   struct FuncToProfileNameMapHash {
63*0fca6ea1SDimitry Andric     uint64_t
operatorFuncToProfileNameMapHash64*0fca6ea1SDimitry Andric     operator()(const std::pair<const Function *, FunctionId> &P) const {
65*0fca6ea1SDimitry Andric       return hash_combine(P.first, P.second);
66*0fca6ea1SDimitry Andric     }
67*0fca6ea1SDimitry Andric   };
68*0fca6ea1SDimitry Andric   // A map from a pair of function and profile name to a boolean value
69*0fca6ea1SDimitry Andric   // indicating whether they are matched. This is used as a cache for the
70*0fca6ea1SDimitry Andric   // matching result.
71*0fca6ea1SDimitry Andric   std::unordered_map<std::pair<const Function *, FunctionId>, bool,
72*0fca6ea1SDimitry Andric                      FuncToProfileNameMapHash>
73*0fca6ea1SDimitry Andric       FuncProfileMatchCache;
74*0fca6ea1SDimitry Andric   // The new functions found by the call graph matching. The map's key is the
75*0fca6ea1SDimitry Andric   // the new(renamed) function pointer and the value is old(unused) profile
76*0fca6ea1SDimitry Andric   // name.
77*0fca6ea1SDimitry Andric   std::unordered_map<Function *, FunctionId> FuncToProfileNameMap;
78*0fca6ea1SDimitry Andric 
79*0fca6ea1SDimitry Andric   // A map pointer to the FuncNameToProfNameMap in SampleProfileLoader,
80*0fca6ea1SDimitry Andric   // which maps the function name to the matched profile name. This is used
81*0fca6ea1SDimitry Andric   // for sample loader to look up profile using the new name.
82*0fca6ea1SDimitry Andric   HashKeyMap<std::unordered_map, FunctionId, FunctionId> *FuncNameToProfNameMap;
83*0fca6ea1SDimitry Andric 
84*0fca6ea1SDimitry Andric   // A map pointer to the SymbolMap in SampleProfileLoader, which stores all
85*0fca6ea1SDimitry Andric   // the original matched symbols before the matching. this is to determine if
86*0fca6ea1SDimitry Andric   // the profile is unused(to be matched) or not.
87*0fca6ea1SDimitry Andric   HashKeyMap<std::unordered_map, FunctionId, Function *> *SymbolMap;
88*0fca6ea1SDimitry Andric 
89*0fca6ea1SDimitry Andric   // The new functions from IR.
90*0fca6ea1SDimitry Andric   HashKeyMap<std::unordered_map, FunctionId, Function *>
91*0fca6ea1SDimitry Andric       FunctionsWithoutProfile;
92*0fca6ea1SDimitry Andric 
93*0fca6ea1SDimitry Andric   // Pointer to the Profile Symbol List in the reader.
94*0fca6ea1SDimitry Andric   std::shared_ptr<ProfileSymbolList> PSL;
95*0fca6ea1SDimitry Andric 
96*0fca6ea1SDimitry Andric   // Profile mismatch statstics:
97*0fca6ea1SDimitry Andric   uint64_t TotalProfiledFunc = 0;
98*0fca6ea1SDimitry Andric   // Num of checksum-mismatched function.
99*0fca6ea1SDimitry Andric   uint64_t NumStaleProfileFunc = 0;
100*0fca6ea1SDimitry Andric   uint64_t TotalProfiledCallsites = 0;
101*0fca6ea1SDimitry Andric   uint64_t NumMismatchedCallsites = 0;
102*0fca6ea1SDimitry Andric   uint64_t NumRecoveredCallsites = 0;
103*0fca6ea1SDimitry Andric   // Total samples for all profiled functions.
104*0fca6ea1SDimitry Andric   uint64_t TotalFunctionSamples = 0;
105*0fca6ea1SDimitry Andric   // Total samples for all checksum-mismatched functions.
106*0fca6ea1SDimitry Andric   uint64_t MismatchedFunctionSamples = 0;
107*0fca6ea1SDimitry Andric   uint64_t MismatchedCallsiteSamples = 0;
108*0fca6ea1SDimitry Andric   uint64_t RecoveredCallsiteSamples = 0;
109*0fca6ea1SDimitry Andric 
110*0fca6ea1SDimitry Andric   // Profile call-graph matching statstics:
111*0fca6ea1SDimitry Andric   uint64_t NumCallGraphRecoveredProfiledFunc = 0;
112*0fca6ea1SDimitry Andric   uint64_t NumCallGraphRecoveredFuncSamples = 0;
113*0fca6ea1SDimitry Andric 
114*0fca6ea1SDimitry Andric   // A dummy name for unknown indirect callee, used to differentiate from a
115*0fca6ea1SDimitry Andric   // non-call instruction that also has an empty callee name.
116*0fca6ea1SDimitry Andric   static constexpr const char *UnknownIndirectCallee =
117*0fca6ea1SDimitry Andric       "unknown.indirect.callee";
118*0fca6ea1SDimitry Andric 
119*0fca6ea1SDimitry Andric public:
SampleProfileMatcher(Module & M,SampleProfileReader & Reader,LazyCallGraph & CG,const PseudoProbeManager * ProbeManager,ThinOrFullLTOPhase LTOPhase,HashKeyMap<std::unordered_map,FunctionId,Function * > & SymMap,std::shared_ptr<ProfileSymbolList> PSL,HashKeyMap<std::unordered_map,FunctionId,FunctionId> & FuncNameToProfNameMap)120*0fca6ea1SDimitry Andric   SampleProfileMatcher(
121*0fca6ea1SDimitry Andric       Module &M, SampleProfileReader &Reader, LazyCallGraph &CG,
122*0fca6ea1SDimitry Andric       const PseudoProbeManager *ProbeManager, ThinOrFullLTOPhase LTOPhase,
123*0fca6ea1SDimitry Andric       HashKeyMap<std::unordered_map, FunctionId, Function *> &SymMap,
124*0fca6ea1SDimitry Andric       std::shared_ptr<ProfileSymbolList> PSL,
125*0fca6ea1SDimitry Andric       HashKeyMap<std::unordered_map, FunctionId, FunctionId>
126*0fca6ea1SDimitry Andric           &FuncNameToProfNameMap)
127*0fca6ea1SDimitry Andric       : M(M), Reader(Reader), CG(CG), ProbeManager(ProbeManager),
128*0fca6ea1SDimitry Andric         LTOPhase(LTOPhase), FuncNameToProfNameMap(&FuncNameToProfNameMap),
129*0fca6ea1SDimitry Andric         SymbolMap(&SymMap), PSL(PSL) {};
130*0fca6ea1SDimitry Andric   void runOnModule();
clearMatchingData()131*0fca6ea1SDimitry Andric   void clearMatchingData() {
132*0fca6ea1SDimitry Andric     // Do not clear FuncMappings, it stores IRLoc to ProfLoc remappings which
133*0fca6ea1SDimitry Andric     // will be used for sample loader.
134*0fca6ea1SDimitry Andric     // Do not clear FlattenedProfiles as it contains function names referenced
135*0fca6ea1SDimitry Andric     // by FuncNameToProfNameMap. Clearing this memory could lead to a
136*0fca6ea1SDimitry Andric     // use-after-free error.
137*0fca6ea1SDimitry Andric     freeContainer(FuncCallsiteMatchStates);
138*0fca6ea1SDimitry Andric     freeContainer(FunctionsWithoutProfile);
139*0fca6ea1SDimitry Andric     freeContainer(FuncToProfileNameMap);
140*0fca6ea1SDimitry Andric   }
141*0fca6ea1SDimitry Andric 
142*0fca6ea1SDimitry Andric private:
getFlattenedSamplesFor(const FunctionId & Fname)143*0fca6ea1SDimitry Andric   FunctionSamples *getFlattenedSamplesFor(const FunctionId &Fname) {
144*0fca6ea1SDimitry Andric     auto It = FlattenedProfiles.find(Fname);
145*0fca6ea1SDimitry Andric     if (It != FlattenedProfiles.end())
146*0fca6ea1SDimitry Andric       return &It->second;
147*0fca6ea1SDimitry Andric     return nullptr;
148*0fca6ea1SDimitry Andric   }
getFlattenedSamplesFor(const Function & F)149*0fca6ea1SDimitry Andric   FunctionSamples *getFlattenedSamplesFor(const Function &F) {
150*0fca6ea1SDimitry Andric     StringRef CanonFName = FunctionSamples::getCanonicalFnName(F);
151*0fca6ea1SDimitry Andric     return getFlattenedSamplesFor(FunctionId(CanonFName));
152*0fca6ea1SDimitry Andric   }
freeContainer(T & C)153*0fca6ea1SDimitry Andric   template <typename T> inline void freeContainer(T &C) {
154*0fca6ea1SDimitry Andric     T Empty;
155*0fca6ea1SDimitry Andric     std::swap(C, Empty);
156*0fca6ea1SDimitry Andric   }
157*0fca6ea1SDimitry Andric   void getFilteredAnchorList(const AnchorMap &IRAnchors,
158*0fca6ea1SDimitry Andric                              const AnchorMap &ProfileAnchors,
159*0fca6ea1SDimitry Andric                              AnchorList &FilteredIRAnchorsList,
160*0fca6ea1SDimitry Andric                              AnchorList &FilteredProfileAnchorList);
161*0fca6ea1SDimitry Andric   void runOnFunction(Function &F);
162*0fca6ea1SDimitry Andric   void findIRAnchors(const Function &F, AnchorMap &IRAnchors) const;
163*0fca6ea1SDimitry Andric   void findProfileAnchors(const FunctionSamples &FS,
164*0fca6ea1SDimitry Andric                           AnchorMap &ProfileAnchors) const;
165*0fca6ea1SDimitry Andric   // Record the callsite match states for profile staleness report, the result
166*0fca6ea1SDimitry Andric   // is saved in FuncCallsiteMatchStates.
167*0fca6ea1SDimitry Andric   void recordCallsiteMatchStates(const Function &F, const AnchorMap &IRAnchors,
168*0fca6ea1SDimitry Andric                                  const AnchorMap &ProfileAnchors,
169*0fca6ea1SDimitry Andric                                  const LocToLocMap *IRToProfileLocationMap);
170*0fca6ea1SDimitry Andric 
isMismatchState(const enum MatchState & State)171*0fca6ea1SDimitry Andric   bool isMismatchState(const enum MatchState &State) {
172*0fca6ea1SDimitry Andric     return State == MatchState::InitialMismatch ||
173*0fca6ea1SDimitry Andric            State == MatchState::UnchangedMismatch ||
174*0fca6ea1SDimitry Andric            State == MatchState::RemovedMatch;
175*0fca6ea1SDimitry Andric   };
176*0fca6ea1SDimitry Andric 
isInitialState(const enum MatchState & State)177*0fca6ea1SDimitry Andric   bool isInitialState(const enum MatchState &State) {
178*0fca6ea1SDimitry Andric     return State == MatchState::InitialMatch ||
179*0fca6ea1SDimitry Andric            State == MatchState::InitialMismatch;
180*0fca6ea1SDimitry Andric   };
181*0fca6ea1SDimitry Andric 
isFinalState(const enum MatchState & State)182*0fca6ea1SDimitry Andric   bool isFinalState(const enum MatchState &State) {
183*0fca6ea1SDimitry Andric     return State == MatchState::UnchangedMatch ||
184*0fca6ea1SDimitry Andric            State == MatchState::UnchangedMismatch ||
185*0fca6ea1SDimitry Andric            State == MatchState::RecoveredMismatch ||
186*0fca6ea1SDimitry Andric            State == MatchState::RemovedMatch;
187*0fca6ea1SDimitry Andric   };
188*0fca6ea1SDimitry Andric 
189*0fca6ea1SDimitry Andric   void countCallGraphRecoveredSamples(
190*0fca6ea1SDimitry Andric       const FunctionSamples &FS,
191*0fca6ea1SDimitry Andric       std::unordered_set<FunctionId> &MatchedUnusedProfile);
192*0fca6ea1SDimitry Andric   // Count the samples of checksum mismatched function for the top-level
193*0fca6ea1SDimitry Andric   // function and all inlinees.
194*0fca6ea1SDimitry Andric   void countMismatchedFuncSamples(const FunctionSamples &FS, bool IsTopLevel);
195*0fca6ea1SDimitry Andric   // Count the number of mismatched or recovered callsites.
196*0fca6ea1SDimitry Andric   void countMismatchCallsites(const FunctionSamples &FS);
197*0fca6ea1SDimitry Andric   // Count the samples of mismatched or recovered callsites for top-level
198*0fca6ea1SDimitry Andric   // function and all inlinees.
199*0fca6ea1SDimitry Andric   void countMismatchedCallsiteSamples(const FunctionSamples &FS);
200*0fca6ea1SDimitry Andric   void computeAndReportProfileStaleness();
201*0fca6ea1SDimitry Andric 
getIRToProfileLocationMap(const Function & F)202*0fca6ea1SDimitry Andric   LocToLocMap &getIRToProfileLocationMap(const Function &F) {
203*0fca6ea1SDimitry Andric     auto Ret = FuncMappings.try_emplace(
204*0fca6ea1SDimitry Andric         FunctionSamples::getCanonicalFnName(F.getName()), LocToLocMap());
205*0fca6ea1SDimitry Andric     return Ret.first->second;
206*0fca6ea1SDimitry Andric   }
207*0fca6ea1SDimitry Andric   void distributeIRToProfileLocationMap();
208*0fca6ea1SDimitry Andric   void distributeIRToProfileLocationMap(FunctionSamples &FS);
209*0fca6ea1SDimitry Andric   // This function implements the Myers diff algorithm used for stale profile
210*0fca6ea1SDimitry Andric   // matching. The algorithm provides a simple and efficient way to find the
211*0fca6ea1SDimitry Andric   // Longest Common Subsequence(LCS) or the Shortest Edit Script(SES) of two
212*0fca6ea1SDimitry Andric   // sequences. For more details, refer to the paper 'An O(ND) Difference
213*0fca6ea1SDimitry Andric   // Algorithm and Its Variations' by Eugene W. Myers.
214*0fca6ea1SDimitry Andric   // In the scenario of profile fuzzy matching, the two sequences are the IR
215*0fca6ea1SDimitry Andric   // callsite anchors and profile callsite anchors. The subsequence equivalent
216*0fca6ea1SDimitry Andric   // parts from the resulting SES are used to remap the IR locations to the
217*0fca6ea1SDimitry Andric   // profile locations. As the number of function callsite is usually not big,
218*0fca6ea1SDimitry Andric   // we currently just implements the basic greedy version(page 6 of the paper).
219*0fca6ea1SDimitry Andric   LocToLocMap longestCommonSequence(const AnchorList &IRCallsiteAnchors,
220*0fca6ea1SDimitry Andric                                     const AnchorList &ProfileCallsiteAnchors,
221*0fca6ea1SDimitry Andric                                     bool MatchUnusedFunction);
222*0fca6ea1SDimitry Andric   void matchNonCallsiteLocs(const LocToLocMap &AnchorMatchings,
223*0fca6ea1SDimitry Andric                             const AnchorMap &IRAnchors,
224*0fca6ea1SDimitry Andric                             LocToLocMap &IRToProfileLocationMap);
225*0fca6ea1SDimitry Andric   void runStaleProfileMatching(const Function &F, const AnchorMap &IRAnchors,
226*0fca6ea1SDimitry Andric                                const AnchorMap &ProfileAnchors,
227*0fca6ea1SDimitry Andric                                LocToLocMap &IRToProfileLocationMap,
228*0fca6ea1SDimitry Andric                                bool RunCFGMatching, bool RunCGMatching);
229*0fca6ea1SDimitry Andric   // If the function doesn't have profile, return the pointer to the function.
230*0fca6ea1SDimitry Andric   bool functionHasProfile(const FunctionId &IRFuncName,
231*0fca6ea1SDimitry Andric                           Function *&FuncWithoutProfile);
232*0fca6ea1SDimitry Andric   bool isProfileUnused(const FunctionId &ProfileFuncName);
233*0fca6ea1SDimitry Andric   bool functionMatchesProfileHelper(const Function &IRFunc,
234*0fca6ea1SDimitry Andric                                     const FunctionId &ProfFunc);
235*0fca6ea1SDimitry Andric   // Determine if the function matches profile. If FindMatchedProfileOnly is
236*0fca6ea1SDimitry Andric   // set, only search the existing matched function. Otherwise, try matching the
237*0fca6ea1SDimitry Andric   // two functions.
238*0fca6ea1SDimitry Andric   bool functionMatchesProfile(const FunctionId &IRFuncName,
239*0fca6ea1SDimitry Andric                               const FunctionId &ProfileFuncName,
240*0fca6ea1SDimitry Andric                               bool FindMatchedProfileOnly);
241*0fca6ea1SDimitry Andric   // Determine if the function matches profile by computing a similarity ratio
242*0fca6ea1SDimitry Andric   // between two sequences of callsite anchors extracted from function and
243*0fca6ea1SDimitry Andric   // profile. If it's above the threshold, the function matches the profile.
244*0fca6ea1SDimitry Andric   bool functionMatchesProfile(Function &IRFunc, const FunctionId &ProfFunc,
245*0fca6ea1SDimitry Andric                               bool FindMatchedProfileOnly);
246*0fca6ea1SDimitry Andric   // Find functions that don't show in the profile or profile symbol list,
247*0fca6ea1SDimitry Andric   // which are supposed to be new functions. We use them as the targets for
248*0fca6ea1SDimitry Andric   // call graph matching.
249*0fca6ea1SDimitry Andric   void findFunctionsWithoutProfile();
250*0fca6ea1SDimitry Andric   void reportOrPersistProfileStats();
251*0fca6ea1SDimitry Andric };
252*0fca6ea1SDimitry Andric } // end namespace llvm
253*0fca6ea1SDimitry Andric #endif // LLVM_TRANSFORMS_IPO_SAMPLEPROFILEMATCHER_H
254