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