1*0fca6ea1SDimitry Andric //===- SampleProfileMatcher.cpp - Sampling-based Stale Profile Matcher ----===// 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 // This file implements the SampleProfileMatcher used for stale 10*0fca6ea1SDimitry Andric // profile matching. 11*0fca6ea1SDimitry Andric // 12*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 13*0fca6ea1SDimitry Andric 14*0fca6ea1SDimitry Andric #include "llvm/Transforms/IPO/SampleProfileMatcher.h" 15*0fca6ea1SDimitry Andric #include "llvm/IR/IntrinsicInst.h" 16*0fca6ea1SDimitry Andric #include "llvm/IR/MDBuilder.h" 17*0fca6ea1SDimitry Andric #include "llvm/Support/CommandLine.h" 18*0fca6ea1SDimitry Andric 19*0fca6ea1SDimitry Andric using namespace llvm; 20*0fca6ea1SDimitry Andric using namespace sampleprof; 21*0fca6ea1SDimitry Andric 22*0fca6ea1SDimitry Andric #define DEBUG_TYPE "sample-profile-matcher" 23*0fca6ea1SDimitry Andric 24*0fca6ea1SDimitry Andric static cl::opt<unsigned> FuncProfileSimilarityThreshold( 25*0fca6ea1SDimitry Andric "func-profile-similarity-threshold", cl::Hidden, cl::init(80), 26*0fca6ea1SDimitry Andric cl::desc("Consider a profile matches a function if the similarity of their " 27*0fca6ea1SDimitry Andric "callee sequences is above the specified percentile.")); 28*0fca6ea1SDimitry Andric 29*0fca6ea1SDimitry Andric static cl::opt<unsigned> MinFuncCountForCGMatching( 30*0fca6ea1SDimitry Andric "min-func-count-for-cg-matching", cl::Hidden, cl::init(5), 31*0fca6ea1SDimitry Andric cl::desc("The minimum number of basic blocks required for a function to " 32*0fca6ea1SDimitry Andric "run stale profile call graph matching.")); 33*0fca6ea1SDimitry Andric 34*0fca6ea1SDimitry Andric static cl::opt<unsigned> MinCallCountForCGMatching( 35*0fca6ea1SDimitry Andric "min-call-count-for-cg-matching", cl::Hidden, cl::init(3), 36*0fca6ea1SDimitry Andric cl::desc("The minimum number of call anchors required for a function to " 37*0fca6ea1SDimitry Andric "run stale profile call graph matching.")); 38*0fca6ea1SDimitry Andric 39*0fca6ea1SDimitry Andric extern cl::opt<bool> SalvageStaleProfile; 40*0fca6ea1SDimitry Andric extern cl::opt<bool> SalvageUnusedProfile; 41*0fca6ea1SDimitry Andric extern cl::opt<bool> PersistProfileStaleness; 42*0fca6ea1SDimitry Andric extern cl::opt<bool> ReportProfileStaleness; 43*0fca6ea1SDimitry Andric 44*0fca6ea1SDimitry Andric static cl::opt<unsigned> SalvageStaleProfileMaxCallsites( 45*0fca6ea1SDimitry Andric "salvage-stale-profile-max-callsites", cl::Hidden, cl::init(UINT_MAX), 46*0fca6ea1SDimitry Andric cl::desc("The maximum number of callsites in a function, above which stale " 47*0fca6ea1SDimitry Andric "profile matching will be skipped.")); 48*0fca6ea1SDimitry Andric 49*0fca6ea1SDimitry Andric void SampleProfileMatcher::findIRAnchors(const Function &F, 50*0fca6ea1SDimitry Andric AnchorMap &IRAnchors) const { 51*0fca6ea1SDimitry Andric // For inlined code, recover the original callsite and callee by finding the 52*0fca6ea1SDimitry Andric // top-level inline frame. e.g. For frame stack "main:1 @ foo:2 @ bar:3", the 53*0fca6ea1SDimitry Andric // top-level frame is "main:1", the callsite is "1" and the callee is "foo". 54*0fca6ea1SDimitry Andric auto FindTopLevelInlinedCallsite = [](const DILocation *DIL) { 55*0fca6ea1SDimitry Andric assert((DIL && DIL->getInlinedAt()) && "No inlined callsite"); 56*0fca6ea1SDimitry Andric const DILocation *PrevDIL = nullptr; 57*0fca6ea1SDimitry Andric do { 58*0fca6ea1SDimitry Andric PrevDIL = DIL; 59*0fca6ea1SDimitry Andric DIL = DIL->getInlinedAt(); 60*0fca6ea1SDimitry Andric } while (DIL->getInlinedAt()); 61*0fca6ea1SDimitry Andric 62*0fca6ea1SDimitry Andric LineLocation Callsite = FunctionSamples::getCallSiteIdentifier( 63*0fca6ea1SDimitry Andric DIL, FunctionSamples::ProfileIsFS); 64*0fca6ea1SDimitry Andric StringRef CalleeName = PrevDIL->getSubprogramLinkageName(); 65*0fca6ea1SDimitry Andric return std::make_pair(Callsite, FunctionId(CalleeName)); 66*0fca6ea1SDimitry Andric }; 67*0fca6ea1SDimitry Andric 68*0fca6ea1SDimitry Andric auto GetCanonicalCalleeName = [](const CallBase *CB) { 69*0fca6ea1SDimitry Andric StringRef CalleeName = UnknownIndirectCallee; 70*0fca6ea1SDimitry Andric if (Function *Callee = CB->getCalledFunction()) 71*0fca6ea1SDimitry Andric CalleeName = FunctionSamples::getCanonicalFnName(Callee->getName()); 72*0fca6ea1SDimitry Andric return CalleeName; 73*0fca6ea1SDimitry Andric }; 74*0fca6ea1SDimitry Andric 75*0fca6ea1SDimitry Andric // Extract profile matching anchors in the IR. 76*0fca6ea1SDimitry Andric for (auto &BB : F) { 77*0fca6ea1SDimitry Andric for (auto &I : BB) { 78*0fca6ea1SDimitry Andric DILocation *DIL = I.getDebugLoc(); 79*0fca6ea1SDimitry Andric if (!DIL) 80*0fca6ea1SDimitry Andric continue; 81*0fca6ea1SDimitry Andric 82*0fca6ea1SDimitry Andric if (FunctionSamples::ProfileIsProbeBased) { 83*0fca6ea1SDimitry Andric if (auto Probe = extractProbe(I)) { 84*0fca6ea1SDimitry Andric // Flatten inlined IR for the matching. 85*0fca6ea1SDimitry Andric if (DIL->getInlinedAt()) { 86*0fca6ea1SDimitry Andric IRAnchors.emplace(FindTopLevelInlinedCallsite(DIL)); 87*0fca6ea1SDimitry Andric } else { 88*0fca6ea1SDimitry Andric // Use empty StringRef for basic block probe. 89*0fca6ea1SDimitry Andric StringRef CalleeName; 90*0fca6ea1SDimitry Andric if (const auto *CB = dyn_cast<CallBase>(&I)) { 91*0fca6ea1SDimitry Andric // Skip the probe inst whose callee name is "llvm.pseudoprobe". 92*0fca6ea1SDimitry Andric if (!isa<IntrinsicInst>(&I)) 93*0fca6ea1SDimitry Andric CalleeName = GetCanonicalCalleeName(CB); 94*0fca6ea1SDimitry Andric } 95*0fca6ea1SDimitry Andric LineLocation Loc = LineLocation(Probe->Id, 0); 96*0fca6ea1SDimitry Andric IRAnchors.emplace(Loc, FunctionId(CalleeName)); 97*0fca6ea1SDimitry Andric } 98*0fca6ea1SDimitry Andric } 99*0fca6ea1SDimitry Andric } else { 100*0fca6ea1SDimitry Andric // TODO: For line-number based profile(AutoFDO), currently only support 101*0fca6ea1SDimitry Andric // find callsite anchors. In future, we need to parse all the non-call 102*0fca6ea1SDimitry Andric // instructions to extract the line locations for profile matching. 103*0fca6ea1SDimitry Andric if (!isa<CallBase>(&I) || isa<IntrinsicInst>(&I)) 104*0fca6ea1SDimitry Andric continue; 105*0fca6ea1SDimitry Andric 106*0fca6ea1SDimitry Andric if (DIL->getInlinedAt()) { 107*0fca6ea1SDimitry Andric IRAnchors.emplace(FindTopLevelInlinedCallsite(DIL)); 108*0fca6ea1SDimitry Andric } else { 109*0fca6ea1SDimitry Andric LineLocation Callsite = FunctionSamples::getCallSiteIdentifier( 110*0fca6ea1SDimitry Andric DIL, FunctionSamples::ProfileIsFS); 111*0fca6ea1SDimitry Andric StringRef CalleeName = GetCanonicalCalleeName(dyn_cast<CallBase>(&I)); 112*0fca6ea1SDimitry Andric IRAnchors.emplace(Callsite, FunctionId(CalleeName)); 113*0fca6ea1SDimitry Andric } 114*0fca6ea1SDimitry Andric } 115*0fca6ea1SDimitry Andric } 116*0fca6ea1SDimitry Andric } 117*0fca6ea1SDimitry Andric } 118*0fca6ea1SDimitry Andric 119*0fca6ea1SDimitry Andric void SampleProfileMatcher::findProfileAnchors(const FunctionSamples &FS, 120*0fca6ea1SDimitry Andric AnchorMap &ProfileAnchors) const { 121*0fca6ea1SDimitry Andric auto isInvalidLineOffset = [](uint32_t LineOffset) { 122*0fca6ea1SDimitry Andric return LineOffset & 0x8000; 123*0fca6ea1SDimitry Andric }; 124*0fca6ea1SDimitry Andric 125*0fca6ea1SDimitry Andric auto InsertAnchor = [](const LineLocation &Loc, const FunctionId &CalleeName, 126*0fca6ea1SDimitry Andric AnchorMap &ProfileAnchors) { 127*0fca6ea1SDimitry Andric auto Ret = ProfileAnchors.try_emplace(Loc, CalleeName); 128*0fca6ea1SDimitry Andric if (!Ret.second) { 129*0fca6ea1SDimitry Andric // For multiple callees, which indicates it's an indirect call, we use a 130*0fca6ea1SDimitry Andric // dummy name(UnknownIndirectCallee) as the indrect callee name. 131*0fca6ea1SDimitry Andric Ret.first->second = FunctionId(UnknownIndirectCallee); 132*0fca6ea1SDimitry Andric } 133*0fca6ea1SDimitry Andric }; 134*0fca6ea1SDimitry Andric 135*0fca6ea1SDimitry Andric for (const auto &I : FS.getBodySamples()) { 136*0fca6ea1SDimitry Andric const LineLocation &Loc = I.first; 137*0fca6ea1SDimitry Andric if (isInvalidLineOffset(Loc.LineOffset)) 138*0fca6ea1SDimitry Andric continue; 139*0fca6ea1SDimitry Andric for (const auto &C : I.second.getCallTargets()) 140*0fca6ea1SDimitry Andric InsertAnchor(Loc, C.first, ProfileAnchors); 141*0fca6ea1SDimitry Andric } 142*0fca6ea1SDimitry Andric 143*0fca6ea1SDimitry Andric for (const auto &I : FS.getCallsiteSamples()) { 144*0fca6ea1SDimitry Andric const LineLocation &Loc = I.first; 145*0fca6ea1SDimitry Andric if (isInvalidLineOffset(Loc.LineOffset)) 146*0fca6ea1SDimitry Andric continue; 147*0fca6ea1SDimitry Andric for (const auto &C : I.second) 148*0fca6ea1SDimitry Andric InsertAnchor(Loc, C.first, ProfileAnchors); 149*0fca6ea1SDimitry Andric } 150*0fca6ea1SDimitry Andric } 151*0fca6ea1SDimitry Andric 152*0fca6ea1SDimitry Andric bool SampleProfileMatcher::functionHasProfile(const FunctionId &IRFuncName, 153*0fca6ea1SDimitry Andric Function *&FuncWithoutProfile) { 154*0fca6ea1SDimitry Andric FuncWithoutProfile = nullptr; 155*0fca6ea1SDimitry Andric auto R = FunctionsWithoutProfile.find(IRFuncName); 156*0fca6ea1SDimitry Andric if (R != FunctionsWithoutProfile.end()) 157*0fca6ea1SDimitry Andric FuncWithoutProfile = R->second; 158*0fca6ea1SDimitry Andric return !FuncWithoutProfile; 159*0fca6ea1SDimitry Andric } 160*0fca6ea1SDimitry Andric 161*0fca6ea1SDimitry Andric bool SampleProfileMatcher::isProfileUnused(const FunctionId &ProfileFuncName) { 162*0fca6ea1SDimitry Andric return SymbolMap->find(ProfileFuncName) == SymbolMap->end(); 163*0fca6ea1SDimitry Andric } 164*0fca6ea1SDimitry Andric 165*0fca6ea1SDimitry Andric bool SampleProfileMatcher::functionMatchesProfile( 166*0fca6ea1SDimitry Andric const FunctionId &IRFuncName, const FunctionId &ProfileFuncName, 167*0fca6ea1SDimitry Andric bool FindMatchedProfileOnly) { 168*0fca6ea1SDimitry Andric if (IRFuncName == ProfileFuncName) 169*0fca6ea1SDimitry Andric return true; 170*0fca6ea1SDimitry Andric if (!SalvageUnusedProfile) 171*0fca6ea1SDimitry Andric return false; 172*0fca6ea1SDimitry Andric 173*0fca6ea1SDimitry Andric // If IR function doesn't have profile and the profile is unused, try 174*0fca6ea1SDimitry Andric // matching them. 175*0fca6ea1SDimitry Andric Function *IRFunc = nullptr; 176*0fca6ea1SDimitry Andric if (functionHasProfile(IRFuncName, IRFunc) || 177*0fca6ea1SDimitry Andric !isProfileUnused(ProfileFuncName)) 178*0fca6ea1SDimitry Andric return false; 179*0fca6ea1SDimitry Andric 180*0fca6ea1SDimitry Andric assert(FunctionId(IRFunc->getName()) != ProfileFuncName && 181*0fca6ea1SDimitry Andric "IR function should be different from profile function to match"); 182*0fca6ea1SDimitry Andric return functionMatchesProfile(*IRFunc, ProfileFuncName, 183*0fca6ea1SDimitry Andric FindMatchedProfileOnly); 184*0fca6ea1SDimitry Andric } 185*0fca6ea1SDimitry Andric 186*0fca6ea1SDimitry Andric LocToLocMap 187*0fca6ea1SDimitry Andric SampleProfileMatcher::longestCommonSequence(const AnchorList &AnchorList1, 188*0fca6ea1SDimitry Andric const AnchorList &AnchorList2, 189*0fca6ea1SDimitry Andric bool MatchUnusedFunction) { 190*0fca6ea1SDimitry Andric int32_t Size1 = AnchorList1.size(), Size2 = AnchorList2.size(), 191*0fca6ea1SDimitry Andric MaxDepth = Size1 + Size2; 192*0fca6ea1SDimitry Andric auto Index = [&](int32_t I) { return I + MaxDepth; }; 193*0fca6ea1SDimitry Andric 194*0fca6ea1SDimitry Andric LocToLocMap EqualLocations; 195*0fca6ea1SDimitry Andric if (MaxDepth == 0) 196*0fca6ea1SDimitry Andric return EqualLocations; 197*0fca6ea1SDimitry Andric 198*0fca6ea1SDimitry Andric // Backtrack the SES result. 199*0fca6ea1SDimitry Andric auto Backtrack = [&](const std::vector<std::vector<int32_t>> &Trace, 200*0fca6ea1SDimitry Andric const AnchorList &AnchorList1, 201*0fca6ea1SDimitry Andric const AnchorList &AnchorList2, 202*0fca6ea1SDimitry Andric LocToLocMap &EqualLocations) { 203*0fca6ea1SDimitry Andric int32_t X = Size1, Y = Size2; 204*0fca6ea1SDimitry Andric for (int32_t Depth = Trace.size() - 1; X > 0 || Y > 0; Depth--) { 205*0fca6ea1SDimitry Andric const auto &P = Trace[Depth]; 206*0fca6ea1SDimitry Andric int32_t K = X - Y; 207*0fca6ea1SDimitry Andric int32_t PrevK = K; 208*0fca6ea1SDimitry Andric if (K == -Depth || (K != Depth && P[Index(K - 1)] < P[Index(K + 1)])) 209*0fca6ea1SDimitry Andric PrevK = K + 1; 210*0fca6ea1SDimitry Andric else 211*0fca6ea1SDimitry Andric PrevK = K - 1; 212*0fca6ea1SDimitry Andric 213*0fca6ea1SDimitry Andric int32_t PrevX = P[Index(PrevK)]; 214*0fca6ea1SDimitry Andric int32_t PrevY = PrevX - PrevK; 215*0fca6ea1SDimitry Andric while (X > PrevX && Y > PrevY) { 216*0fca6ea1SDimitry Andric X--; 217*0fca6ea1SDimitry Andric Y--; 218*0fca6ea1SDimitry Andric EqualLocations.insert({AnchorList1[X].first, AnchorList2[Y].first}); 219*0fca6ea1SDimitry Andric } 220*0fca6ea1SDimitry Andric 221*0fca6ea1SDimitry Andric if (Depth == 0) 222*0fca6ea1SDimitry Andric break; 223*0fca6ea1SDimitry Andric 224*0fca6ea1SDimitry Andric if (Y == PrevY) 225*0fca6ea1SDimitry Andric X--; 226*0fca6ea1SDimitry Andric else if (X == PrevX) 227*0fca6ea1SDimitry Andric Y--; 228*0fca6ea1SDimitry Andric X = PrevX; 229*0fca6ea1SDimitry Andric Y = PrevY; 230*0fca6ea1SDimitry Andric } 231*0fca6ea1SDimitry Andric }; 232*0fca6ea1SDimitry Andric 233*0fca6ea1SDimitry Andric // The greedy LCS/SES algorithm. 234*0fca6ea1SDimitry Andric 235*0fca6ea1SDimitry Andric // An array contains the endpoints of the furthest reaching D-paths. 236*0fca6ea1SDimitry Andric std::vector<int32_t> V(2 * MaxDepth + 1, -1); 237*0fca6ea1SDimitry Andric V[Index(1)] = 0; 238*0fca6ea1SDimitry Andric // Trace is used to backtrack the SES result. 239*0fca6ea1SDimitry Andric std::vector<std::vector<int32_t>> Trace; 240*0fca6ea1SDimitry Andric for (int32_t Depth = 0; Depth <= MaxDepth; Depth++) { 241*0fca6ea1SDimitry Andric Trace.push_back(V); 242*0fca6ea1SDimitry Andric for (int32_t K = -Depth; K <= Depth; K += 2) { 243*0fca6ea1SDimitry Andric int32_t X = 0, Y = 0; 244*0fca6ea1SDimitry Andric if (K == -Depth || (K != Depth && V[Index(K - 1)] < V[Index(K + 1)])) 245*0fca6ea1SDimitry Andric X = V[Index(K + 1)]; 246*0fca6ea1SDimitry Andric else 247*0fca6ea1SDimitry Andric X = V[Index(K - 1)] + 1; 248*0fca6ea1SDimitry Andric Y = X - K; 249*0fca6ea1SDimitry Andric while (X < Size1 && Y < Size2 && 250*0fca6ea1SDimitry Andric functionMatchesProfile( 251*0fca6ea1SDimitry Andric AnchorList1[X].second, AnchorList2[Y].second, 252*0fca6ea1SDimitry Andric !MatchUnusedFunction /* Find matched function only */)) 253*0fca6ea1SDimitry Andric X++, Y++; 254*0fca6ea1SDimitry Andric 255*0fca6ea1SDimitry Andric V[Index(K)] = X; 256*0fca6ea1SDimitry Andric 257*0fca6ea1SDimitry Andric if (X >= Size1 && Y >= Size2) { 258*0fca6ea1SDimitry Andric // Length of an SES is D. 259*0fca6ea1SDimitry Andric Backtrack(Trace, AnchorList1, AnchorList2, EqualLocations); 260*0fca6ea1SDimitry Andric return EqualLocations; 261*0fca6ea1SDimitry Andric } 262*0fca6ea1SDimitry Andric } 263*0fca6ea1SDimitry Andric } 264*0fca6ea1SDimitry Andric // Length of an SES is greater than MaxDepth. 265*0fca6ea1SDimitry Andric return EqualLocations; 266*0fca6ea1SDimitry Andric } 267*0fca6ea1SDimitry Andric 268*0fca6ea1SDimitry Andric void SampleProfileMatcher::matchNonCallsiteLocs( 269*0fca6ea1SDimitry Andric const LocToLocMap &MatchedAnchors, const AnchorMap &IRAnchors, 270*0fca6ea1SDimitry Andric LocToLocMap &IRToProfileLocationMap) { 271*0fca6ea1SDimitry Andric auto InsertMatching = [&](const LineLocation &From, const LineLocation &To) { 272*0fca6ea1SDimitry Andric // Skip the unchanged location mapping to save memory. 273*0fca6ea1SDimitry Andric if (From != To) 274*0fca6ea1SDimitry Andric IRToProfileLocationMap.insert({From, To}); 275*0fca6ea1SDimitry Andric }; 276*0fca6ea1SDimitry Andric 277*0fca6ea1SDimitry Andric // Use function's beginning location as the initial anchor. 278*0fca6ea1SDimitry Andric int32_t LocationDelta = 0; 279*0fca6ea1SDimitry Andric SmallVector<LineLocation> LastMatchedNonAnchors; 280*0fca6ea1SDimitry Andric for (const auto &IR : IRAnchors) { 281*0fca6ea1SDimitry Andric const auto &Loc = IR.first; 282*0fca6ea1SDimitry Andric bool IsMatchedAnchor = false; 283*0fca6ea1SDimitry Andric // Match the anchor location in lexical order. 284*0fca6ea1SDimitry Andric auto R = MatchedAnchors.find(Loc); 285*0fca6ea1SDimitry Andric if (R != MatchedAnchors.end()) { 286*0fca6ea1SDimitry Andric const auto &Candidate = R->second; 287*0fca6ea1SDimitry Andric InsertMatching(Loc, Candidate); 288*0fca6ea1SDimitry Andric LLVM_DEBUG(dbgs() << "Callsite with callee:" << IR.second.stringRef() 289*0fca6ea1SDimitry Andric << " is matched from " << Loc << " to " << Candidate 290*0fca6ea1SDimitry Andric << "\n"); 291*0fca6ea1SDimitry Andric LocationDelta = Candidate.LineOffset - Loc.LineOffset; 292*0fca6ea1SDimitry Andric 293*0fca6ea1SDimitry Andric // Match backwards for non-anchor locations. 294*0fca6ea1SDimitry Andric // The locations in LastMatchedNonAnchors have been matched forwards 295*0fca6ea1SDimitry Andric // based on the previous anchor, spilt it evenly and overwrite the 296*0fca6ea1SDimitry Andric // second half based on the current anchor. 297*0fca6ea1SDimitry Andric for (size_t I = (LastMatchedNonAnchors.size() + 1) / 2; 298*0fca6ea1SDimitry Andric I < LastMatchedNonAnchors.size(); I++) { 299*0fca6ea1SDimitry Andric const auto &L = LastMatchedNonAnchors[I]; 300*0fca6ea1SDimitry Andric uint32_t CandidateLineOffset = L.LineOffset + LocationDelta; 301*0fca6ea1SDimitry Andric LineLocation Candidate(CandidateLineOffset, L.Discriminator); 302*0fca6ea1SDimitry Andric InsertMatching(L, Candidate); 303*0fca6ea1SDimitry Andric LLVM_DEBUG(dbgs() << "Location is rematched backwards from " << L 304*0fca6ea1SDimitry Andric << " to " << Candidate << "\n"); 305*0fca6ea1SDimitry Andric } 306*0fca6ea1SDimitry Andric 307*0fca6ea1SDimitry Andric IsMatchedAnchor = true; 308*0fca6ea1SDimitry Andric LastMatchedNonAnchors.clear(); 309*0fca6ea1SDimitry Andric } 310*0fca6ea1SDimitry Andric 311*0fca6ea1SDimitry Andric // Match forwards for non-anchor locations. 312*0fca6ea1SDimitry Andric if (!IsMatchedAnchor) { 313*0fca6ea1SDimitry Andric uint32_t CandidateLineOffset = Loc.LineOffset + LocationDelta; 314*0fca6ea1SDimitry Andric LineLocation Candidate(CandidateLineOffset, Loc.Discriminator); 315*0fca6ea1SDimitry Andric InsertMatching(Loc, Candidate); 316*0fca6ea1SDimitry Andric LLVM_DEBUG(dbgs() << "Location is matched from " << Loc << " to " 317*0fca6ea1SDimitry Andric << Candidate << "\n"); 318*0fca6ea1SDimitry Andric LastMatchedNonAnchors.emplace_back(Loc); 319*0fca6ea1SDimitry Andric } 320*0fca6ea1SDimitry Andric } 321*0fca6ea1SDimitry Andric } 322*0fca6ea1SDimitry Andric 323*0fca6ea1SDimitry Andric // Filter the non-call locations from IRAnchors and ProfileAnchors and write 324*0fca6ea1SDimitry Andric // them into a list for random access later. 325*0fca6ea1SDimitry Andric void SampleProfileMatcher::getFilteredAnchorList( 326*0fca6ea1SDimitry Andric const AnchorMap &IRAnchors, const AnchorMap &ProfileAnchors, 327*0fca6ea1SDimitry Andric AnchorList &FilteredIRAnchorsList, AnchorList &FilteredProfileAnchorList) { 328*0fca6ea1SDimitry Andric for (const auto &I : IRAnchors) { 329*0fca6ea1SDimitry Andric if (I.second.stringRef().empty()) 330*0fca6ea1SDimitry Andric continue; 331*0fca6ea1SDimitry Andric FilteredIRAnchorsList.emplace_back(I); 332*0fca6ea1SDimitry Andric } 333*0fca6ea1SDimitry Andric 334*0fca6ea1SDimitry Andric for (const auto &I : ProfileAnchors) 335*0fca6ea1SDimitry Andric FilteredProfileAnchorList.emplace_back(I); 336*0fca6ea1SDimitry Andric } 337*0fca6ea1SDimitry Andric 338*0fca6ea1SDimitry Andric // Call target name anchor based profile fuzzy matching. 339*0fca6ea1SDimitry Andric // Input: 340*0fca6ea1SDimitry Andric // For IR locations, the anchor is the callee name of direct callsite; For 341*0fca6ea1SDimitry Andric // profile locations, it's the call target name for BodySamples or inlinee's 342*0fca6ea1SDimitry Andric // profile name for CallsiteSamples. 343*0fca6ea1SDimitry Andric // Matching heuristic: 344*0fca6ea1SDimitry Andric // First match all the anchors using the diff algorithm, then split the 345*0fca6ea1SDimitry Andric // non-anchor locations between the two anchors evenly, first half are matched 346*0fca6ea1SDimitry Andric // based on the start anchor, second half are matched based on the end anchor. 347*0fca6ea1SDimitry Andric // For example, given: 348*0fca6ea1SDimitry Andric // IR locations: [1, 2(foo), 3, 5, 6(bar), 7] 349*0fca6ea1SDimitry Andric // Profile locations: [1, 2, 3(foo), 4, 7, 8(bar), 9] 350*0fca6ea1SDimitry Andric // The matching gives: 351*0fca6ea1SDimitry Andric // [1, 2(foo), 3, 5, 6(bar), 7] 352*0fca6ea1SDimitry Andric // | | | | | | 353*0fca6ea1SDimitry Andric // [1, 2, 3(foo), 4, 7, 8(bar), 9] 354*0fca6ea1SDimitry Andric // The output mapping: [2->3, 3->4, 5->7, 6->8, 7->9]. 355*0fca6ea1SDimitry Andric void SampleProfileMatcher::runStaleProfileMatching( 356*0fca6ea1SDimitry Andric const Function &F, const AnchorMap &IRAnchors, 357*0fca6ea1SDimitry Andric const AnchorMap &ProfileAnchors, LocToLocMap &IRToProfileLocationMap, 358*0fca6ea1SDimitry Andric bool RunCFGMatching, bool RunCGMatching) { 359*0fca6ea1SDimitry Andric if (!RunCFGMatching && !RunCGMatching) 360*0fca6ea1SDimitry Andric return; 361*0fca6ea1SDimitry Andric LLVM_DEBUG(dbgs() << "Run stale profile matching for " << F.getName() 362*0fca6ea1SDimitry Andric << "\n"); 363*0fca6ea1SDimitry Andric assert(IRToProfileLocationMap.empty() && 364*0fca6ea1SDimitry Andric "Run stale profile matching only once per function"); 365*0fca6ea1SDimitry Andric 366*0fca6ea1SDimitry Andric AnchorList FilteredProfileAnchorList; 367*0fca6ea1SDimitry Andric AnchorList FilteredIRAnchorsList; 368*0fca6ea1SDimitry Andric getFilteredAnchorList(IRAnchors, ProfileAnchors, FilteredIRAnchorsList, 369*0fca6ea1SDimitry Andric FilteredProfileAnchorList); 370*0fca6ea1SDimitry Andric 371*0fca6ea1SDimitry Andric if (FilteredIRAnchorsList.empty() || FilteredProfileAnchorList.empty()) 372*0fca6ea1SDimitry Andric return; 373*0fca6ea1SDimitry Andric 374*0fca6ea1SDimitry Andric if (FilteredIRAnchorsList.size() > SalvageStaleProfileMaxCallsites || 375*0fca6ea1SDimitry Andric FilteredProfileAnchorList.size() > SalvageStaleProfileMaxCallsites) { 376*0fca6ea1SDimitry Andric LLVM_DEBUG(dbgs() << "Skip stale profile matching for " << F.getName() 377*0fca6ea1SDimitry Andric << " because the number of callsites in the IR is " 378*0fca6ea1SDimitry Andric << FilteredIRAnchorsList.size() 379*0fca6ea1SDimitry Andric << " and in the profile is " 380*0fca6ea1SDimitry Andric << FilteredProfileAnchorList.size() << "\n"); 381*0fca6ea1SDimitry Andric return; 382*0fca6ea1SDimitry Andric } 383*0fca6ea1SDimitry Andric 384*0fca6ea1SDimitry Andric // Match the callsite anchors by finding the longest common subsequence 385*0fca6ea1SDimitry Andric // between IR and profile. 386*0fca6ea1SDimitry Andric // Define a match between two anchors as follows: 387*0fca6ea1SDimitry Andric // 1) The function names of anchors are the same. 388*0fca6ea1SDimitry Andric // 2) The similarity between the anchor functions is above a threshold if 389*0fca6ea1SDimitry Andric // RunCGMatching is set. 390*0fca6ea1SDimitry Andric // For 2), we only consider the anchor functions from IR and profile don't 391*0fca6ea1SDimitry Andric // appear on either side to reduce the matching scope. Note that we need to 392*0fca6ea1SDimitry Andric // use IR anchor as base(A side) to align with the order of 393*0fca6ea1SDimitry Andric // IRToProfileLocationMap. 394*0fca6ea1SDimitry Andric LocToLocMap MatchedAnchors = 395*0fca6ea1SDimitry Andric longestCommonSequence(FilteredIRAnchorsList, FilteredProfileAnchorList, 396*0fca6ea1SDimitry Andric RunCGMatching /* Match unused functions */); 397*0fca6ea1SDimitry Andric 398*0fca6ea1SDimitry Andric // CFG level matching: 399*0fca6ea1SDimitry Andric // Apply the callsite matchings to infer matching for the basic 400*0fca6ea1SDimitry Andric // block(non-callsite) locations and write the result to 401*0fca6ea1SDimitry Andric // IRToProfileLocationMap. 402*0fca6ea1SDimitry Andric if (RunCFGMatching) 403*0fca6ea1SDimitry Andric matchNonCallsiteLocs(MatchedAnchors, IRAnchors, IRToProfileLocationMap); 404*0fca6ea1SDimitry Andric } 405*0fca6ea1SDimitry Andric 406*0fca6ea1SDimitry Andric void SampleProfileMatcher::runOnFunction(Function &F) { 407*0fca6ea1SDimitry Andric // We need to use flattened function samples for matching. 408*0fca6ea1SDimitry Andric // Unlike IR, which includes all callsites from the source code, the callsites 409*0fca6ea1SDimitry Andric // in profile only show up when they are hit by samples, i,e. the profile 410*0fca6ea1SDimitry Andric // callsites in one context may differ from those in another context. To get 411*0fca6ea1SDimitry Andric // the maximum number of callsites, we merge the function profiles from all 412*0fca6ea1SDimitry Andric // contexts, aka, the flattened profile to find profile anchors. 413*0fca6ea1SDimitry Andric const auto *FSFlattened = getFlattenedSamplesFor(F); 414*0fca6ea1SDimitry Andric if (SalvageUnusedProfile && !FSFlattened) { 415*0fca6ea1SDimitry Andric // Apply the matching in place to find the new function's matched profile. 416*0fca6ea1SDimitry Andric // TODO: For extended profile format, if a function profile is unused and 417*0fca6ea1SDimitry Andric // it's top-level, even if the profile is matched, it's not found in the 418*0fca6ea1SDimitry Andric // profile. This is because sample reader only read the used profile at the 419*0fca6ea1SDimitry Andric // beginning, we need to support loading the profile on-demand in future. 420*0fca6ea1SDimitry Andric auto R = FuncToProfileNameMap.find(&F); 421*0fca6ea1SDimitry Andric if (R != FuncToProfileNameMap.end()) 422*0fca6ea1SDimitry Andric FSFlattened = getFlattenedSamplesFor(R->second); 423*0fca6ea1SDimitry Andric } 424*0fca6ea1SDimitry Andric if (!FSFlattened) 425*0fca6ea1SDimitry Andric return; 426*0fca6ea1SDimitry Andric 427*0fca6ea1SDimitry Andric // Anchors for IR. It's a map from IR location to callee name, callee name is 428*0fca6ea1SDimitry Andric // empty for non-call instruction and use a dummy name(UnknownIndirectCallee) 429*0fca6ea1SDimitry Andric // for unknown indrect callee name. 430*0fca6ea1SDimitry Andric AnchorMap IRAnchors; 431*0fca6ea1SDimitry Andric findIRAnchors(F, IRAnchors); 432*0fca6ea1SDimitry Andric // Anchors for profile. It's a map from callsite location to a set of callee 433*0fca6ea1SDimitry Andric // name. 434*0fca6ea1SDimitry Andric AnchorMap ProfileAnchors; 435*0fca6ea1SDimitry Andric findProfileAnchors(*FSFlattened, ProfileAnchors); 436*0fca6ea1SDimitry Andric 437*0fca6ea1SDimitry Andric // Compute the callsite match states for profile staleness report. 438*0fca6ea1SDimitry Andric if (ReportProfileStaleness || PersistProfileStaleness) 439*0fca6ea1SDimitry Andric recordCallsiteMatchStates(F, IRAnchors, ProfileAnchors, nullptr); 440*0fca6ea1SDimitry Andric 441*0fca6ea1SDimitry Andric if (!SalvageStaleProfile) 442*0fca6ea1SDimitry Andric return; 443*0fca6ea1SDimitry Andric // For probe-based profiles, run matching only when profile checksum is 444*0fca6ea1SDimitry Andric // mismatched. 445*0fca6ea1SDimitry Andric bool ChecksumMismatch = FunctionSamples::ProfileIsProbeBased && 446*0fca6ea1SDimitry Andric !ProbeManager->profileIsValid(F, *FSFlattened); 447*0fca6ea1SDimitry Andric bool RunCFGMatching = 448*0fca6ea1SDimitry Andric !FunctionSamples::ProfileIsProbeBased || ChecksumMismatch; 449*0fca6ea1SDimitry Andric bool RunCGMatching = SalvageUnusedProfile; 450*0fca6ea1SDimitry Andric // For imported functions, the checksum metadata(pseudo_probe_desc) are 451*0fca6ea1SDimitry Andric // dropped, so we leverage function attribute(profile-checksum-mismatch) to 452*0fca6ea1SDimitry Andric // transfer the info: add the attribute during pre-link phase and check it 453*0fca6ea1SDimitry Andric // during post-link phase(see "profileIsValid"). 454*0fca6ea1SDimitry Andric if (ChecksumMismatch && LTOPhase == ThinOrFullLTOPhase::ThinLTOPreLink) 455*0fca6ea1SDimitry Andric F.addFnAttr("profile-checksum-mismatch"); 456*0fca6ea1SDimitry Andric 457*0fca6ea1SDimitry Andric // The matching result will be saved to IRToProfileLocationMap, create a 458*0fca6ea1SDimitry Andric // new map for each function. 459*0fca6ea1SDimitry Andric auto &IRToProfileLocationMap = getIRToProfileLocationMap(F); 460*0fca6ea1SDimitry Andric runStaleProfileMatching(F, IRAnchors, ProfileAnchors, IRToProfileLocationMap, 461*0fca6ea1SDimitry Andric RunCFGMatching, RunCGMatching); 462*0fca6ea1SDimitry Andric // Find and update callsite match states after matching. 463*0fca6ea1SDimitry Andric if (RunCFGMatching && (ReportProfileStaleness || PersistProfileStaleness)) 464*0fca6ea1SDimitry Andric recordCallsiteMatchStates(F, IRAnchors, ProfileAnchors, 465*0fca6ea1SDimitry Andric &IRToProfileLocationMap); 466*0fca6ea1SDimitry Andric } 467*0fca6ea1SDimitry Andric 468*0fca6ea1SDimitry Andric void SampleProfileMatcher::recordCallsiteMatchStates( 469*0fca6ea1SDimitry Andric const Function &F, const AnchorMap &IRAnchors, 470*0fca6ea1SDimitry Andric const AnchorMap &ProfileAnchors, 471*0fca6ea1SDimitry Andric const LocToLocMap *IRToProfileLocationMap) { 472*0fca6ea1SDimitry Andric bool IsPostMatch = IRToProfileLocationMap != nullptr; 473*0fca6ea1SDimitry Andric auto &CallsiteMatchStates = 474*0fca6ea1SDimitry Andric FuncCallsiteMatchStates[FunctionSamples::getCanonicalFnName(F.getName())]; 475*0fca6ea1SDimitry Andric 476*0fca6ea1SDimitry Andric auto MapIRLocToProfileLoc = [&](const LineLocation &IRLoc) { 477*0fca6ea1SDimitry Andric // IRToProfileLocationMap is null in pre-match phrase. 478*0fca6ea1SDimitry Andric if (!IRToProfileLocationMap) 479*0fca6ea1SDimitry Andric return IRLoc; 480*0fca6ea1SDimitry Andric const auto &ProfileLoc = IRToProfileLocationMap->find(IRLoc); 481*0fca6ea1SDimitry Andric if (ProfileLoc != IRToProfileLocationMap->end()) 482*0fca6ea1SDimitry Andric return ProfileLoc->second; 483*0fca6ea1SDimitry Andric else 484*0fca6ea1SDimitry Andric return IRLoc; 485*0fca6ea1SDimitry Andric }; 486*0fca6ea1SDimitry Andric 487*0fca6ea1SDimitry Andric for (const auto &I : IRAnchors) { 488*0fca6ea1SDimitry Andric // After fuzzy profile matching, use the matching result to remap the 489*0fca6ea1SDimitry Andric // current IR callsite. 490*0fca6ea1SDimitry Andric const auto &ProfileLoc = MapIRLocToProfileLoc(I.first); 491*0fca6ea1SDimitry Andric const auto &IRCalleeId = I.second; 492*0fca6ea1SDimitry Andric const auto &It = ProfileAnchors.find(ProfileLoc); 493*0fca6ea1SDimitry Andric if (It == ProfileAnchors.end()) 494*0fca6ea1SDimitry Andric continue; 495*0fca6ea1SDimitry Andric const auto &ProfCalleeId = It->second; 496*0fca6ea1SDimitry Andric if (IRCalleeId == ProfCalleeId) { 497*0fca6ea1SDimitry Andric auto It = CallsiteMatchStates.find(ProfileLoc); 498*0fca6ea1SDimitry Andric if (It == CallsiteMatchStates.end()) 499*0fca6ea1SDimitry Andric CallsiteMatchStates.emplace(ProfileLoc, MatchState::InitialMatch); 500*0fca6ea1SDimitry Andric else if (IsPostMatch) { 501*0fca6ea1SDimitry Andric if (It->second == MatchState::InitialMatch) 502*0fca6ea1SDimitry Andric It->second = MatchState::UnchangedMatch; 503*0fca6ea1SDimitry Andric else if (It->second == MatchState::InitialMismatch) 504*0fca6ea1SDimitry Andric It->second = MatchState::RecoveredMismatch; 505*0fca6ea1SDimitry Andric } 506*0fca6ea1SDimitry Andric } 507*0fca6ea1SDimitry Andric } 508*0fca6ea1SDimitry Andric 509*0fca6ea1SDimitry Andric // Check if there are any callsites in the profile that does not match to any 510*0fca6ea1SDimitry Andric // IR callsites. 511*0fca6ea1SDimitry Andric for (const auto &I : ProfileAnchors) { 512*0fca6ea1SDimitry Andric const auto &Loc = I.first; 513*0fca6ea1SDimitry Andric assert(!I.second.stringRef().empty() && "Callees should not be empty"); 514*0fca6ea1SDimitry Andric auto It = CallsiteMatchStates.find(Loc); 515*0fca6ea1SDimitry Andric if (It == CallsiteMatchStates.end()) 516*0fca6ea1SDimitry Andric CallsiteMatchStates.emplace(Loc, MatchState::InitialMismatch); 517*0fca6ea1SDimitry Andric else if (IsPostMatch) { 518*0fca6ea1SDimitry Andric // Update the state if it's not matched(UnchangedMatch or 519*0fca6ea1SDimitry Andric // RecoveredMismatch). 520*0fca6ea1SDimitry Andric if (It->second == MatchState::InitialMismatch) 521*0fca6ea1SDimitry Andric It->second = MatchState::UnchangedMismatch; 522*0fca6ea1SDimitry Andric else if (It->second == MatchState::InitialMatch) 523*0fca6ea1SDimitry Andric It->second = MatchState::RemovedMatch; 524*0fca6ea1SDimitry Andric } 525*0fca6ea1SDimitry Andric } 526*0fca6ea1SDimitry Andric } 527*0fca6ea1SDimitry Andric 528*0fca6ea1SDimitry Andric void SampleProfileMatcher::countMismatchedFuncSamples(const FunctionSamples &FS, 529*0fca6ea1SDimitry Andric bool IsTopLevel) { 530*0fca6ea1SDimitry Andric const auto *FuncDesc = ProbeManager->getDesc(FS.getGUID()); 531*0fca6ea1SDimitry Andric // Skip the function that is external or renamed. 532*0fca6ea1SDimitry Andric if (!FuncDesc) 533*0fca6ea1SDimitry Andric return; 534*0fca6ea1SDimitry Andric 535*0fca6ea1SDimitry Andric if (ProbeManager->profileIsHashMismatched(*FuncDesc, FS)) { 536*0fca6ea1SDimitry Andric if (IsTopLevel) 537*0fca6ea1SDimitry Andric NumStaleProfileFunc++; 538*0fca6ea1SDimitry Andric // Given currently all probe ids are after block probe ids, once the 539*0fca6ea1SDimitry Andric // checksum is mismatched, it's likely all the callites are mismatched and 540*0fca6ea1SDimitry Andric // dropped. We conservatively count all the samples as mismatched and stop 541*0fca6ea1SDimitry Andric // counting the inlinees' profiles. 542*0fca6ea1SDimitry Andric MismatchedFunctionSamples += FS.getTotalSamples(); 543*0fca6ea1SDimitry Andric return; 544*0fca6ea1SDimitry Andric } 545*0fca6ea1SDimitry Andric 546*0fca6ea1SDimitry Andric // Even the current-level function checksum is matched, it's possible that the 547*0fca6ea1SDimitry Andric // nested inlinees' checksums are mismatched that affect the inlinee's sample 548*0fca6ea1SDimitry Andric // loading, we need to go deeper to check the inlinees' function samples. 549*0fca6ea1SDimitry Andric // Similarly, count all the samples as mismatched if the inlinee's checksum is 550*0fca6ea1SDimitry Andric // mismatched using this recursive function. 551*0fca6ea1SDimitry Andric for (const auto &I : FS.getCallsiteSamples()) 552*0fca6ea1SDimitry Andric for (const auto &CS : I.second) 553*0fca6ea1SDimitry Andric countMismatchedFuncSamples(CS.second, false); 554*0fca6ea1SDimitry Andric } 555*0fca6ea1SDimitry Andric 556*0fca6ea1SDimitry Andric void SampleProfileMatcher::countMismatchedCallsiteSamples( 557*0fca6ea1SDimitry Andric const FunctionSamples &FS) { 558*0fca6ea1SDimitry Andric auto It = FuncCallsiteMatchStates.find(FS.getFuncName()); 559*0fca6ea1SDimitry Andric // Skip it if no mismatched callsite or this is an external function. 560*0fca6ea1SDimitry Andric if (It == FuncCallsiteMatchStates.end() || It->second.empty()) 561*0fca6ea1SDimitry Andric return; 562*0fca6ea1SDimitry Andric const auto &CallsiteMatchStates = It->second; 563*0fca6ea1SDimitry Andric 564*0fca6ea1SDimitry Andric auto findMatchState = [&](const LineLocation &Loc) { 565*0fca6ea1SDimitry Andric auto It = CallsiteMatchStates.find(Loc); 566*0fca6ea1SDimitry Andric if (It == CallsiteMatchStates.end()) 567*0fca6ea1SDimitry Andric return MatchState::Unknown; 568*0fca6ea1SDimitry Andric return It->second; 569*0fca6ea1SDimitry Andric }; 570*0fca6ea1SDimitry Andric 571*0fca6ea1SDimitry Andric auto AttributeMismatchedSamples = [&](const enum MatchState &State, 572*0fca6ea1SDimitry Andric uint64_t Samples) { 573*0fca6ea1SDimitry Andric if (isMismatchState(State)) 574*0fca6ea1SDimitry Andric MismatchedCallsiteSamples += Samples; 575*0fca6ea1SDimitry Andric else if (State == MatchState::RecoveredMismatch) 576*0fca6ea1SDimitry Andric RecoveredCallsiteSamples += Samples; 577*0fca6ea1SDimitry Andric }; 578*0fca6ea1SDimitry Andric 579*0fca6ea1SDimitry Andric // The non-inlined callsites are saved in the body samples of function 580*0fca6ea1SDimitry Andric // profile, go through it to count the non-inlined callsite samples. 581*0fca6ea1SDimitry Andric for (const auto &I : FS.getBodySamples()) 582*0fca6ea1SDimitry Andric AttributeMismatchedSamples(findMatchState(I.first), I.second.getSamples()); 583*0fca6ea1SDimitry Andric 584*0fca6ea1SDimitry Andric // Count the inlined callsite samples. 585*0fca6ea1SDimitry Andric for (const auto &I : FS.getCallsiteSamples()) { 586*0fca6ea1SDimitry Andric auto State = findMatchState(I.first); 587*0fca6ea1SDimitry Andric uint64_t CallsiteSamples = 0; 588*0fca6ea1SDimitry Andric for (const auto &CS : I.second) 589*0fca6ea1SDimitry Andric CallsiteSamples += CS.second.getTotalSamples(); 590*0fca6ea1SDimitry Andric AttributeMismatchedSamples(State, CallsiteSamples); 591*0fca6ea1SDimitry Andric 592*0fca6ea1SDimitry Andric if (isMismatchState(State)) 593*0fca6ea1SDimitry Andric continue; 594*0fca6ea1SDimitry Andric 595*0fca6ea1SDimitry Andric // When the current level of inlined call site matches the profiled call 596*0fca6ea1SDimitry Andric // site, we need to go deeper along the inline tree to count mismatches from 597*0fca6ea1SDimitry Andric // lower level inlinees. 598*0fca6ea1SDimitry Andric for (const auto &CS : I.second) 599*0fca6ea1SDimitry Andric countMismatchedCallsiteSamples(CS.second); 600*0fca6ea1SDimitry Andric } 601*0fca6ea1SDimitry Andric } 602*0fca6ea1SDimitry Andric 603*0fca6ea1SDimitry Andric void SampleProfileMatcher::countMismatchCallsites(const FunctionSamples &FS) { 604*0fca6ea1SDimitry Andric auto It = FuncCallsiteMatchStates.find(FS.getFuncName()); 605*0fca6ea1SDimitry Andric // Skip it if no mismatched callsite or this is an external function. 606*0fca6ea1SDimitry Andric if (It == FuncCallsiteMatchStates.end() || It->second.empty()) 607*0fca6ea1SDimitry Andric return; 608*0fca6ea1SDimitry Andric const auto &MatchStates = It->second; 609*0fca6ea1SDimitry Andric [[maybe_unused]] bool OnInitialState = 610*0fca6ea1SDimitry Andric isInitialState(MatchStates.begin()->second); 611*0fca6ea1SDimitry Andric for (const auto &I : MatchStates) { 612*0fca6ea1SDimitry Andric TotalProfiledCallsites++; 613*0fca6ea1SDimitry Andric assert( 614*0fca6ea1SDimitry Andric (OnInitialState ? isInitialState(I.second) : isFinalState(I.second)) && 615*0fca6ea1SDimitry Andric "Profile matching state is inconsistent"); 616*0fca6ea1SDimitry Andric 617*0fca6ea1SDimitry Andric if (isMismatchState(I.second)) 618*0fca6ea1SDimitry Andric NumMismatchedCallsites++; 619*0fca6ea1SDimitry Andric else if (I.second == MatchState::RecoveredMismatch) 620*0fca6ea1SDimitry Andric NumRecoveredCallsites++; 621*0fca6ea1SDimitry Andric } 622*0fca6ea1SDimitry Andric } 623*0fca6ea1SDimitry Andric 624*0fca6ea1SDimitry Andric void SampleProfileMatcher::countCallGraphRecoveredSamples( 625*0fca6ea1SDimitry Andric const FunctionSamples &FS, 626*0fca6ea1SDimitry Andric std::unordered_set<FunctionId> &CallGraphRecoveredProfiles) { 627*0fca6ea1SDimitry Andric if (CallGraphRecoveredProfiles.count(FS.getFunction())) { 628*0fca6ea1SDimitry Andric NumCallGraphRecoveredFuncSamples += FS.getTotalSamples(); 629*0fca6ea1SDimitry Andric return; 630*0fca6ea1SDimitry Andric } 631*0fca6ea1SDimitry Andric 632*0fca6ea1SDimitry Andric for (const auto &CM : FS.getCallsiteSamples()) { 633*0fca6ea1SDimitry Andric for (const auto &CS : CM.second) { 634*0fca6ea1SDimitry Andric countCallGraphRecoveredSamples(CS.second, CallGraphRecoveredProfiles); 635*0fca6ea1SDimitry Andric } 636*0fca6ea1SDimitry Andric } 637*0fca6ea1SDimitry Andric } 638*0fca6ea1SDimitry Andric 639*0fca6ea1SDimitry Andric void SampleProfileMatcher::computeAndReportProfileStaleness() { 640*0fca6ea1SDimitry Andric if (!ReportProfileStaleness && !PersistProfileStaleness) 641*0fca6ea1SDimitry Andric return; 642*0fca6ea1SDimitry Andric 643*0fca6ea1SDimitry Andric std::unordered_set<FunctionId> CallGraphRecoveredProfiles; 644*0fca6ea1SDimitry Andric if (SalvageUnusedProfile) { 645*0fca6ea1SDimitry Andric for (const auto &I : FuncToProfileNameMap) { 646*0fca6ea1SDimitry Andric CallGraphRecoveredProfiles.insert(I.second); 647*0fca6ea1SDimitry Andric if (GlobalValue::isAvailableExternallyLinkage(I.first->getLinkage())) 648*0fca6ea1SDimitry Andric continue; 649*0fca6ea1SDimitry Andric NumCallGraphRecoveredProfiledFunc++; 650*0fca6ea1SDimitry Andric } 651*0fca6ea1SDimitry Andric } 652*0fca6ea1SDimitry Andric 653*0fca6ea1SDimitry Andric // Count profile mismatches for profile staleness report. 654*0fca6ea1SDimitry Andric for (const auto &F : M) { 655*0fca6ea1SDimitry Andric if (skipProfileForFunction(F)) 656*0fca6ea1SDimitry Andric continue; 657*0fca6ea1SDimitry Andric // As the stats will be merged by linker, skip reporting the metrics for 658*0fca6ea1SDimitry Andric // imported functions to avoid repeated counting. 659*0fca6ea1SDimitry Andric if (GlobalValue::isAvailableExternallyLinkage(F.getLinkage())) 660*0fca6ea1SDimitry Andric continue; 661*0fca6ea1SDimitry Andric const auto *FS = Reader.getSamplesFor(F); 662*0fca6ea1SDimitry Andric if (!FS) 663*0fca6ea1SDimitry Andric continue; 664*0fca6ea1SDimitry Andric TotalProfiledFunc++; 665*0fca6ea1SDimitry Andric TotalFunctionSamples += FS->getTotalSamples(); 666*0fca6ea1SDimitry Andric 667*0fca6ea1SDimitry Andric if (SalvageUnusedProfile && !CallGraphRecoveredProfiles.empty()) 668*0fca6ea1SDimitry Andric countCallGraphRecoveredSamples(*FS, CallGraphRecoveredProfiles); 669*0fca6ea1SDimitry Andric 670*0fca6ea1SDimitry Andric // Checksum mismatch is only used in pseudo-probe mode. 671*0fca6ea1SDimitry Andric if (FunctionSamples::ProfileIsProbeBased) 672*0fca6ea1SDimitry Andric countMismatchedFuncSamples(*FS, true); 673*0fca6ea1SDimitry Andric 674*0fca6ea1SDimitry Andric // Count mismatches and samples for calliste. 675*0fca6ea1SDimitry Andric countMismatchCallsites(*FS); 676*0fca6ea1SDimitry Andric countMismatchedCallsiteSamples(*FS); 677*0fca6ea1SDimitry Andric } 678*0fca6ea1SDimitry Andric 679*0fca6ea1SDimitry Andric if (ReportProfileStaleness) { 680*0fca6ea1SDimitry Andric if (FunctionSamples::ProfileIsProbeBased) { 681*0fca6ea1SDimitry Andric errs() << "(" << NumStaleProfileFunc << "/" << TotalProfiledFunc 682*0fca6ea1SDimitry Andric << ") of functions' profile are invalid and (" 683*0fca6ea1SDimitry Andric << MismatchedFunctionSamples << "/" << TotalFunctionSamples 684*0fca6ea1SDimitry Andric << ") of samples are discarded due to function hash mismatch.\n"; 685*0fca6ea1SDimitry Andric } 686*0fca6ea1SDimitry Andric if (SalvageUnusedProfile) { 687*0fca6ea1SDimitry Andric errs() << "(" << NumCallGraphRecoveredProfiledFunc << "/" 688*0fca6ea1SDimitry Andric << TotalProfiledFunc << ") of functions' profile are matched and (" 689*0fca6ea1SDimitry Andric << NumCallGraphRecoveredFuncSamples << "/" << TotalFunctionSamples 690*0fca6ea1SDimitry Andric << ") of samples are reused by call graph matching.\n"; 691*0fca6ea1SDimitry Andric } 692*0fca6ea1SDimitry Andric 693*0fca6ea1SDimitry Andric errs() << "(" << (NumMismatchedCallsites + NumRecoveredCallsites) << "/" 694*0fca6ea1SDimitry Andric << TotalProfiledCallsites 695*0fca6ea1SDimitry Andric << ") of callsites' profile are invalid and (" 696*0fca6ea1SDimitry Andric << (MismatchedCallsiteSamples + RecoveredCallsiteSamples) << "/" 697*0fca6ea1SDimitry Andric << TotalFunctionSamples 698*0fca6ea1SDimitry Andric << ") of samples are discarded due to callsite location mismatch.\n"; 699*0fca6ea1SDimitry Andric errs() << "(" << NumRecoveredCallsites << "/" 700*0fca6ea1SDimitry Andric << (NumRecoveredCallsites + NumMismatchedCallsites) 701*0fca6ea1SDimitry Andric << ") of callsites and (" << RecoveredCallsiteSamples << "/" 702*0fca6ea1SDimitry Andric << (RecoveredCallsiteSamples + MismatchedCallsiteSamples) 703*0fca6ea1SDimitry Andric << ") of samples are recovered by stale profile matching.\n"; 704*0fca6ea1SDimitry Andric } 705*0fca6ea1SDimitry Andric 706*0fca6ea1SDimitry Andric if (PersistProfileStaleness) { 707*0fca6ea1SDimitry Andric LLVMContext &Ctx = M.getContext(); 708*0fca6ea1SDimitry Andric MDBuilder MDB(Ctx); 709*0fca6ea1SDimitry Andric 710*0fca6ea1SDimitry Andric SmallVector<std::pair<StringRef, uint64_t>> ProfStatsVec; 711*0fca6ea1SDimitry Andric if (FunctionSamples::ProfileIsProbeBased) { 712*0fca6ea1SDimitry Andric ProfStatsVec.emplace_back("NumStaleProfileFunc", NumStaleProfileFunc); 713*0fca6ea1SDimitry Andric ProfStatsVec.emplace_back("TotalProfiledFunc", TotalProfiledFunc); 714*0fca6ea1SDimitry Andric ProfStatsVec.emplace_back("MismatchedFunctionSamples", 715*0fca6ea1SDimitry Andric MismatchedFunctionSamples); 716*0fca6ea1SDimitry Andric ProfStatsVec.emplace_back("TotalFunctionSamples", TotalFunctionSamples); 717*0fca6ea1SDimitry Andric } 718*0fca6ea1SDimitry Andric 719*0fca6ea1SDimitry Andric if (SalvageUnusedProfile) { 720*0fca6ea1SDimitry Andric ProfStatsVec.emplace_back("NumCallGraphRecoveredProfiledFunc", 721*0fca6ea1SDimitry Andric NumCallGraphRecoveredProfiledFunc); 722*0fca6ea1SDimitry Andric ProfStatsVec.emplace_back("NumCallGraphRecoveredFuncSamples", 723*0fca6ea1SDimitry Andric NumCallGraphRecoveredFuncSamples); 724*0fca6ea1SDimitry Andric } 725*0fca6ea1SDimitry Andric 726*0fca6ea1SDimitry Andric ProfStatsVec.emplace_back("NumMismatchedCallsites", NumMismatchedCallsites); 727*0fca6ea1SDimitry Andric ProfStatsVec.emplace_back("NumRecoveredCallsites", NumRecoveredCallsites); 728*0fca6ea1SDimitry Andric ProfStatsVec.emplace_back("TotalProfiledCallsites", TotalProfiledCallsites); 729*0fca6ea1SDimitry Andric ProfStatsVec.emplace_back("MismatchedCallsiteSamples", 730*0fca6ea1SDimitry Andric MismatchedCallsiteSamples); 731*0fca6ea1SDimitry Andric ProfStatsVec.emplace_back("RecoveredCallsiteSamples", 732*0fca6ea1SDimitry Andric RecoveredCallsiteSamples); 733*0fca6ea1SDimitry Andric 734*0fca6ea1SDimitry Andric auto *MD = MDB.createLLVMStats(ProfStatsVec); 735*0fca6ea1SDimitry Andric auto *NMD = M.getOrInsertNamedMetadata("llvm.stats"); 736*0fca6ea1SDimitry Andric NMD->addOperand(MD); 737*0fca6ea1SDimitry Andric } 738*0fca6ea1SDimitry Andric } 739*0fca6ea1SDimitry Andric 740*0fca6ea1SDimitry Andric void SampleProfileMatcher::findFunctionsWithoutProfile() { 741*0fca6ea1SDimitry Andric // TODO: Support MD5 profile. 742*0fca6ea1SDimitry Andric if (FunctionSamples::UseMD5) 743*0fca6ea1SDimitry Andric return; 744*0fca6ea1SDimitry Andric StringSet<> NamesInProfile; 745*0fca6ea1SDimitry Andric if (auto NameTable = Reader.getNameTable()) { 746*0fca6ea1SDimitry Andric for (auto Name : *NameTable) 747*0fca6ea1SDimitry Andric NamesInProfile.insert(Name.stringRef()); 748*0fca6ea1SDimitry Andric } 749*0fca6ea1SDimitry Andric 750*0fca6ea1SDimitry Andric for (auto &F : M) { 751*0fca6ea1SDimitry Andric // Skip declarations, as even if the function can be matched, we have 752*0fca6ea1SDimitry Andric // nothing to do with it. 753*0fca6ea1SDimitry Andric if (F.isDeclaration()) 754*0fca6ea1SDimitry Andric continue; 755*0fca6ea1SDimitry Andric 756*0fca6ea1SDimitry Andric StringRef CanonFName = FunctionSamples::getCanonicalFnName(F.getName()); 757*0fca6ea1SDimitry Andric const auto *FS = getFlattenedSamplesFor(F); 758*0fca6ea1SDimitry Andric if (FS) 759*0fca6ea1SDimitry Andric continue; 760*0fca6ea1SDimitry Andric 761*0fca6ea1SDimitry Andric // For extended binary, functions fully inlined may not be loaded in the 762*0fca6ea1SDimitry Andric // top-level profile, so check the NameTable which has the all symbol names 763*0fca6ea1SDimitry Andric // in profile. 764*0fca6ea1SDimitry Andric if (NamesInProfile.count(CanonFName)) 765*0fca6ea1SDimitry Andric continue; 766*0fca6ea1SDimitry Andric 767*0fca6ea1SDimitry Andric // For extended binary, non-profiled function symbols are in the profile 768*0fca6ea1SDimitry Andric // symbol list table. 769*0fca6ea1SDimitry Andric if (PSL && PSL->contains(CanonFName)) 770*0fca6ea1SDimitry Andric continue; 771*0fca6ea1SDimitry Andric 772*0fca6ea1SDimitry Andric LLVM_DEBUG(dbgs() << "Function " << CanonFName 773*0fca6ea1SDimitry Andric << " is not in profile or profile symbol list.\n"); 774*0fca6ea1SDimitry Andric FunctionsWithoutProfile[FunctionId(CanonFName)] = &F; 775*0fca6ea1SDimitry Andric } 776*0fca6ea1SDimitry Andric } 777*0fca6ea1SDimitry Andric 778*0fca6ea1SDimitry Andric bool SampleProfileMatcher::functionMatchesProfileHelper( 779*0fca6ea1SDimitry Andric const Function &IRFunc, const FunctionId &ProfFunc) { 780*0fca6ea1SDimitry Andric // The value is in the range [0, 1]. The bigger the value is, the more similar 781*0fca6ea1SDimitry Andric // two sequences are. 782*0fca6ea1SDimitry Andric float Similarity = 0.0; 783*0fca6ea1SDimitry Andric 784*0fca6ea1SDimitry Andric const auto *FSFlattened = getFlattenedSamplesFor(ProfFunc); 785*0fca6ea1SDimitry Andric if (!FSFlattened) 786*0fca6ea1SDimitry Andric return false; 787*0fca6ea1SDimitry Andric // The check for similarity or checksum may not be reliable if the function is 788*0fca6ea1SDimitry Andric // tiny, we use the number of basic block as a proxy for the function 789*0fca6ea1SDimitry Andric // complexity and skip the matching if it's too small. 790*0fca6ea1SDimitry Andric if (IRFunc.size() < MinFuncCountForCGMatching || 791*0fca6ea1SDimitry Andric FSFlattened->getBodySamples().size() < MinFuncCountForCGMatching) 792*0fca6ea1SDimitry Andric return false; 793*0fca6ea1SDimitry Andric 794*0fca6ea1SDimitry Andric // For probe-based function, we first trust the checksum info. If the checksum 795*0fca6ea1SDimitry Andric // doesn't match, we continue checking for similarity. 796*0fca6ea1SDimitry Andric if (FunctionSamples::ProfileIsProbeBased) { 797*0fca6ea1SDimitry Andric const auto *FuncDesc = ProbeManager->getDesc(IRFunc); 798*0fca6ea1SDimitry Andric if (FuncDesc && 799*0fca6ea1SDimitry Andric !ProbeManager->profileIsHashMismatched(*FuncDesc, *FSFlattened)) { 800*0fca6ea1SDimitry Andric LLVM_DEBUG(dbgs() << "The checksums for " << IRFunc.getName() 801*0fca6ea1SDimitry Andric << "(IR) and " << ProfFunc << "(Profile) match.\n"); 802*0fca6ea1SDimitry Andric 803*0fca6ea1SDimitry Andric return true; 804*0fca6ea1SDimitry Andric } 805*0fca6ea1SDimitry Andric } 806*0fca6ea1SDimitry Andric 807*0fca6ea1SDimitry Andric AnchorMap IRAnchors; 808*0fca6ea1SDimitry Andric findIRAnchors(IRFunc, IRAnchors); 809*0fca6ea1SDimitry Andric AnchorMap ProfileAnchors; 810*0fca6ea1SDimitry Andric findProfileAnchors(*FSFlattened, ProfileAnchors); 811*0fca6ea1SDimitry Andric 812*0fca6ea1SDimitry Andric AnchorList FilteredIRAnchorsList; 813*0fca6ea1SDimitry Andric AnchorList FilteredProfileAnchorList; 814*0fca6ea1SDimitry Andric getFilteredAnchorList(IRAnchors, ProfileAnchors, FilteredIRAnchorsList, 815*0fca6ea1SDimitry Andric FilteredProfileAnchorList); 816*0fca6ea1SDimitry Andric 817*0fca6ea1SDimitry Andric // Similarly skip the matching if the num of anchors is not enough. 818*0fca6ea1SDimitry Andric if (FilteredIRAnchorsList.size() < MinCallCountForCGMatching || 819*0fca6ea1SDimitry Andric FilteredProfileAnchorList.size() < MinCallCountForCGMatching) 820*0fca6ea1SDimitry Andric return false; 821*0fca6ea1SDimitry Andric 822*0fca6ea1SDimitry Andric // Use the diff algorithm to find the LCS between IR and profile. 823*0fca6ea1SDimitry Andric 824*0fca6ea1SDimitry Andric // Don't recursively match the callee function to avoid infinite matching, 825*0fca6ea1SDimitry Andric // callee functions will be handled later since it's processed in top-down 826*0fca6ea1SDimitry Andric // order . 827*0fca6ea1SDimitry Andric LocToLocMap MatchedAnchors = 828*0fca6ea1SDimitry Andric longestCommonSequence(FilteredIRAnchorsList, FilteredProfileAnchorList, 829*0fca6ea1SDimitry Andric false /* Match unused functions */); 830*0fca6ea1SDimitry Andric 831*0fca6ea1SDimitry Andric Similarity = 832*0fca6ea1SDimitry Andric static_cast<float>(MatchedAnchors.size()) * 2 / 833*0fca6ea1SDimitry Andric (FilteredIRAnchorsList.size() + FilteredProfileAnchorList.size()); 834*0fca6ea1SDimitry Andric 835*0fca6ea1SDimitry Andric LLVM_DEBUG(dbgs() << "The similarity between " << IRFunc.getName() 836*0fca6ea1SDimitry Andric << "(IR) and " << ProfFunc << "(profile) is " 837*0fca6ea1SDimitry Andric << format("%.2f", Similarity) << "\n"); 838*0fca6ea1SDimitry Andric assert((Similarity >= 0 && Similarity <= 1.0) && 839*0fca6ea1SDimitry Andric "Similarity value should be in [0, 1]"); 840*0fca6ea1SDimitry Andric return Similarity * 100 > FuncProfileSimilarityThreshold; 841*0fca6ea1SDimitry Andric } 842*0fca6ea1SDimitry Andric 843*0fca6ea1SDimitry Andric // If FindMatchedProfileOnly is set to true, only use the processed function 844*0fca6ea1SDimitry Andric // results. This is used for skipping the repeated recursive matching. 845*0fca6ea1SDimitry Andric bool SampleProfileMatcher::functionMatchesProfile(Function &IRFunc, 846*0fca6ea1SDimitry Andric const FunctionId &ProfFunc, 847*0fca6ea1SDimitry Andric bool FindMatchedProfileOnly) { 848*0fca6ea1SDimitry Andric auto R = FuncProfileMatchCache.find({&IRFunc, ProfFunc}); 849*0fca6ea1SDimitry Andric if (R != FuncProfileMatchCache.end()) 850*0fca6ea1SDimitry Andric return R->second; 851*0fca6ea1SDimitry Andric 852*0fca6ea1SDimitry Andric if (FindMatchedProfileOnly) 853*0fca6ea1SDimitry Andric return false; 854*0fca6ea1SDimitry Andric 855*0fca6ea1SDimitry Andric bool Matched = functionMatchesProfileHelper(IRFunc, ProfFunc); 856*0fca6ea1SDimitry Andric FuncProfileMatchCache[{&IRFunc, ProfFunc}] = Matched; 857*0fca6ea1SDimitry Andric if (Matched) { 858*0fca6ea1SDimitry Andric FuncToProfileNameMap[&IRFunc] = ProfFunc; 859*0fca6ea1SDimitry Andric LLVM_DEBUG(dbgs() << "Function:" << IRFunc.getName() 860*0fca6ea1SDimitry Andric << " matches profile:" << ProfFunc << "\n"); 861*0fca6ea1SDimitry Andric } 862*0fca6ea1SDimitry Andric 863*0fca6ea1SDimitry Andric return Matched; 864*0fca6ea1SDimitry Andric } 865*0fca6ea1SDimitry Andric 866*0fca6ea1SDimitry Andric void SampleProfileMatcher::runOnModule() { 867*0fca6ea1SDimitry Andric ProfileConverter::flattenProfile(Reader.getProfiles(), FlattenedProfiles, 868*0fca6ea1SDimitry Andric FunctionSamples::ProfileIsCS); 869*0fca6ea1SDimitry Andric if (SalvageUnusedProfile) 870*0fca6ea1SDimitry Andric findFunctionsWithoutProfile(); 871*0fca6ea1SDimitry Andric 872*0fca6ea1SDimitry Andric // Process the matching in top-down order so that the caller matching result 873*0fca6ea1SDimitry Andric // can be used to the callee matching. 874*0fca6ea1SDimitry Andric std::vector<Function *> TopDownFunctionList; 875*0fca6ea1SDimitry Andric TopDownFunctionList.reserve(M.size()); 876*0fca6ea1SDimitry Andric buildTopDownFuncOrder(CG, TopDownFunctionList); 877*0fca6ea1SDimitry Andric for (auto *F : TopDownFunctionList) { 878*0fca6ea1SDimitry Andric if (skipProfileForFunction(*F)) 879*0fca6ea1SDimitry Andric continue; 880*0fca6ea1SDimitry Andric runOnFunction(*F); 881*0fca6ea1SDimitry Andric } 882*0fca6ea1SDimitry Andric 883*0fca6ea1SDimitry Andric // Update the data in SampleLoader. 884*0fca6ea1SDimitry Andric if (SalvageUnusedProfile) 885*0fca6ea1SDimitry Andric for (auto &I : FuncToProfileNameMap) { 886*0fca6ea1SDimitry Andric assert(I.first && "New function is null"); 887*0fca6ea1SDimitry Andric FunctionId FuncName(I.first->getName()); 888*0fca6ea1SDimitry Andric FuncNameToProfNameMap->emplace(FuncName, I.second); 889*0fca6ea1SDimitry Andric // We need to remove the old entry to avoid duplicating the function 890*0fca6ea1SDimitry Andric // processing. 891*0fca6ea1SDimitry Andric SymbolMap->erase(FuncName); 892*0fca6ea1SDimitry Andric SymbolMap->emplace(I.second, I.first); 893*0fca6ea1SDimitry Andric } 894*0fca6ea1SDimitry Andric 895*0fca6ea1SDimitry Andric if (SalvageStaleProfile) 896*0fca6ea1SDimitry Andric distributeIRToProfileLocationMap(); 897*0fca6ea1SDimitry Andric 898*0fca6ea1SDimitry Andric computeAndReportProfileStaleness(); 899*0fca6ea1SDimitry Andric } 900*0fca6ea1SDimitry Andric 901*0fca6ea1SDimitry Andric void SampleProfileMatcher::distributeIRToProfileLocationMap( 902*0fca6ea1SDimitry Andric FunctionSamples &FS) { 903*0fca6ea1SDimitry Andric const auto ProfileMappings = FuncMappings.find(FS.getFuncName()); 904*0fca6ea1SDimitry Andric if (ProfileMappings != FuncMappings.end()) { 905*0fca6ea1SDimitry Andric FS.setIRToProfileLocationMap(&(ProfileMappings->second)); 906*0fca6ea1SDimitry Andric } 907*0fca6ea1SDimitry Andric 908*0fca6ea1SDimitry Andric for (auto &Callees : 909*0fca6ea1SDimitry Andric const_cast<CallsiteSampleMap &>(FS.getCallsiteSamples())) { 910*0fca6ea1SDimitry Andric for (auto &FS : Callees.second) { 911*0fca6ea1SDimitry Andric distributeIRToProfileLocationMap(FS.second); 912*0fca6ea1SDimitry Andric } 913*0fca6ea1SDimitry Andric } 914*0fca6ea1SDimitry Andric } 915*0fca6ea1SDimitry Andric 916*0fca6ea1SDimitry Andric // Use a central place to distribute the matching results. Outlined and inlined 917*0fca6ea1SDimitry Andric // profile with the function name will be set to the same pointer. 918*0fca6ea1SDimitry Andric void SampleProfileMatcher::distributeIRToProfileLocationMap() { 919*0fca6ea1SDimitry Andric for (auto &I : Reader.getProfiles()) { 920*0fca6ea1SDimitry Andric distributeIRToProfileLocationMap(I.second); 921*0fca6ea1SDimitry Andric } 922*0fca6ea1SDimitry Andric } 923