[llvm] [SampleFDO] Stale profile call-graph matching (PR #92151)
Lei Wang via llvm-commits
llvm-commits at lists.llvm.org
Wed May 29 22:38:27 PDT 2024
================
@@ -590,14 +617,304 @@ void SampleProfileMatcher::computeAndReportProfileStaleness() {
}
}
-void SampleProfileMatcher::runOnModule() {
- ProfileConverter::flattenProfile(Reader.getProfiles(), FlattenedProfiles,
- FunctionSamples::ProfileIsCS);
+void SampleProfileMatcher::findNewIRFunctions(
+ StringMap<Function *> &NewIRFunctions) {
+ // TODO: Support MD5 profile.
+ if (FunctionSamples::UseMD5)
+ return;
+ StringSet<> NamesInProfile;
+ if (auto NameTable = Reader.getNameTable()) {
+ for (auto Name : *NameTable)
+ NamesInProfile.insert(Name.stringRef());
+ }
+
for (auto &F : M) {
- if (skipProfileForFunction(F))
+ // Skip declarations, as even if the function can be recognized renamed, we
+ // have nothing to do with it.
+ if (F.isDeclaration())
continue;
- runOnFunction(F);
+
+ StringRef CanonFName = FunctionSamples::getCanonicalFnName(F.getName());
+ const auto *FS = getFlattenedSamplesFor(F);
+ if (FS)
+ continue;
+
+ // For extended binary, functions are fully inlined may not be loaded in the
+ // top-level profile, so check the NameTable which has the all symbol names
+ // in profile.
+ if (NamesInProfile.count(CanonFName))
+ continue;
+
+ // For extended binary, non-profiled function symbols are in the profile
+ // symbol list table.
+ if (PSL && PSL->contains(CanonFName))
+ continue;
+
+ LLVM_DEBUG(dbgs() << "Function " << CanonFName
+ << " is not in profile or symbol list table.\n");
+ NewIRFunctions[CanonFName] = &F;
}
+}
+
+void SampleProfileMatcher::findNewIRCallees(
+ Function &Caller, const StringMap<Function *> &NewIRFunctions,
+ std::vector<Function *> &NewIRCallees) {
+ for (auto &BB : Caller) {
+ for (auto &I : BB) {
+ const auto *CB = dyn_cast<CallBase>(&I);
+ if (!CB || isa<IntrinsicInst>(&I))
+ continue;
+ Function *Callee = CB->getCalledFunction();
+ if (!Callee || Callee->isDeclaration())
+ continue;
+ StringRef CalleeName =
+ FunctionSamples::getCanonicalFnName(Callee->getName());
+ if (NewIRFunctions.count(CalleeName))
+ NewIRCallees.push_back(Callee);
+ }
+ }
+}
+
+std::pair<Function *, bool> SampleProfileMatcher::findOrMatchFunction(
+ const FunctionId &ProfCallee, FunctionMap &OldProfToNewSymbolMap,
+ const std::vector<Function *> &NewIRCallees = std::vector<Function *>()) {
+ auto F = SymbolMap->find(ProfCallee);
+ if (F != SymbolMap->end())
+ return {F->second, false};
+
+ // Existing matched function is found.
+ auto NewF = OldProfToNewSymbolMap.find(ProfCallee);
+ if (NewF != OldProfToNewSymbolMap.end())
+ return {NewF->second, true};
+
+ for (auto *IRCallee : NewIRCallees)
+ if (functionMatchesProfile(*IRCallee, ProfCallee)) {
+ OldProfToNewSymbolMap[ProfCallee] = IRCallee;
+ return {IRCallee, true};
+ }
+ return {nullptr, false};
+}
+
+bool SampleProfileMatcher::functionMatchesProfileHelper(
+ const Function &IRFunc, const FunctionId &ProfFunc) {
+ // The value is in the range [0, 1]. The bigger the value is, the more similar
+ // two sequences are.
+ float Similarity = 0.0;
+
+ const auto *FSFlattened = getFlattenedSamplesFor(ProfFunc);
+ assert(FSFlattened && "Flattened profile sample is null");
+ // Similarity check may not be reliable if the function is tiny, we use the
+ // number of basic block as a proxy for the function complexity and skip the
+ // matching if it's too small.
+ if (IRFunc.size() < MinBBForCGMatching ||
+ FSFlattened->getBodySamples().size() < MinBBForCGMatching)
+ return false;
+
+ // For probe-based function, we first trust the checksum info. If the checksum
+ // doesn't match, we continue checking for similarity.
+ if (FunctionSamples::ProfileIsProbeBased) {
+ const auto *FuncDesc = ProbeManager->getDesc(IRFunc);
+ if (FuncDesc &&
+ !ProbeManager->profileIsHashMismatched(*FuncDesc, *FSFlattened)) {
+ LLVM_DEBUG(dbgs() << "The checksums for " << IRFunc.getName()
+ << "(IR) and " << ProfFunc << "(Profile) match.\n");
+
+ return true;
+ }
+ }
+
+ AnchorMap IRAnchors;
+ findIRAnchors(IRFunc, IRAnchors);
+ AnchorMap ProfileAnchors;
+ findProfileAnchors(*FSFlattened, ProfileAnchors);
+
+ AnchorList FilteredIRAnchorsList;
+ AnchorList FilteredProfileAnchorList;
+ getFilteredAnchorList(IRAnchors, ProfileAnchors, FilteredIRAnchorsList,
+ FilteredProfileAnchorList);
+
+ // Similarly skip the matching if the num of anchors is not enough.
+ if (FilteredIRAnchorsList.size() < MinCallAnchorForCGMatching ||
+ FilteredProfileAnchorList.size() < MinCallAnchorForCGMatching)
+ return false;
+
+ // Use the diff algorithm to find the LCS between IR and profile.
+ LocToLocMap MatchedAnchors =
+ longestCommonSequence(FilteredIRAnchorsList, FilteredProfileAnchorList);
+
+ Similarity =
+ static_cast<float>(MatchedAnchors.size()) * 2 /
+ (FilteredIRAnchorsList.size() + FilteredProfileAnchorList.size());
+
+ LLVM_DEBUG(dbgs() << "The similarity between " << IRFunc.getName()
+ << "(IR) and " << ProfFunc << "(profile) is "
+ << format("%.2f", Similarity) << "\n");
+ assert((Similarity >= 0 && Similarity <= 1.0) &&
+ "Similarity value should be in [0, 1]");
+ return Similarity * 100 > RenamedFuncSimilarityThreshold;
+}
+
+bool SampleProfileMatcher::functionMatchesProfile(const Function &IRFunc,
+ const FunctionId &ProfFunc) {
+ auto R = FunctionProfileNameMap.find({&IRFunc, ProfFunc});
+ if (R != FunctionProfileNameMap.end())
+ return R->second;
+
+ bool Matched = functionMatchesProfileHelper(IRFunc, ProfFunc);
+ FunctionProfileNameMap[{&IRFunc, ProfFunc}] = Matched;
+ return Matched;
+}
+
+// Match profile for new function on the profiled call-graph edge to limit the
+// matching scope.
+void SampleProfileMatcher::matchProfileForNewFunctions(
+ const StringMap<Function *> &NewIRFunctions, FunctionSamples &CallerFS,
+ FunctionMap &OldProfToNewSymbolMap) {
+ // Find the new candidate callees from IR in the current caller scope.
+ std::vector<Function *> NewIRCallees;
+ if (auto *IRCaller =
+ findOrMatchFunction(CallerFS.getFunction(), OldProfToNewSymbolMap)
+ .first) {
----------------
wlei-llvm wrote:
Trying to clarify: this is not to limit to new IRCaller, it is for all callers. Note that here there is no 3rd parameter for function `findOrMatchFunction`, which has a default argument for the `NewIRCallees`(`const std::vector<Function *> &NewIRCallees = std::vector<Function *>()`), in this case, it's just an empty vector which means it doesn't do any new function matching, so it's only for lookup for existing functions.
The `NewIRCallees` in 774 is used in `findNewIRCallees`.
https://github.com/llvm/llvm-project/pull/92151
More information about the llvm-commits
mailing list