xref: /freebsd/contrib/llvm-project/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 
findIRAnchors(const Function & F,AnchorMap & IRAnchors) const49*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 
findProfileAnchors(const FunctionSamples & FS,AnchorMap & ProfileAnchors) const119*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 
functionHasProfile(const FunctionId & IRFuncName,Function * & FuncWithoutProfile)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 
isProfileUnused(const FunctionId & ProfileFuncName)161*0fca6ea1SDimitry Andric bool SampleProfileMatcher::isProfileUnused(const FunctionId &ProfileFuncName) {
162*0fca6ea1SDimitry Andric   return SymbolMap->find(ProfileFuncName) == SymbolMap->end();
163*0fca6ea1SDimitry Andric }
164*0fca6ea1SDimitry Andric 
functionMatchesProfile(const FunctionId & IRFuncName,const FunctionId & ProfileFuncName,bool FindMatchedProfileOnly)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
longestCommonSequence(const AnchorList & AnchorList1,const AnchorList & AnchorList2,bool MatchUnusedFunction)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 
matchNonCallsiteLocs(const LocToLocMap & MatchedAnchors,const AnchorMap & IRAnchors,LocToLocMap & IRToProfileLocationMap)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.
getFilteredAnchorList(const AnchorMap & IRAnchors,const AnchorMap & ProfileAnchors,AnchorList & FilteredIRAnchorsList,AnchorList & FilteredProfileAnchorList)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].
runStaleProfileMatching(const Function & F,const AnchorMap & IRAnchors,const AnchorMap & ProfileAnchors,LocToLocMap & IRToProfileLocationMap,bool RunCFGMatching,bool RunCGMatching)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 
runOnFunction(Function & F)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 
recordCallsiteMatchStates(const Function & F,const AnchorMap & IRAnchors,const AnchorMap & ProfileAnchors,const LocToLocMap * IRToProfileLocationMap)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 
countMismatchedFuncSamples(const FunctionSamples & FS,bool IsTopLevel)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 
countMismatchedCallsiteSamples(const FunctionSamples & FS)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 
countMismatchCallsites(const FunctionSamples & FS)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 
countCallGraphRecoveredSamples(const FunctionSamples & FS,std::unordered_set<FunctionId> & CallGraphRecoveredProfiles)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 
computeAndReportProfileStaleness()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 
findFunctionsWithoutProfile()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 
functionMatchesProfileHelper(const Function & IRFunc,const FunctionId & ProfFunc)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.
functionMatchesProfile(Function & IRFunc,const FunctionId & ProfFunc,bool FindMatchedProfileOnly)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 
runOnModule()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 
distributeIRToProfileLocationMap(FunctionSamples & FS)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.
distributeIRToProfileLocationMap()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