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