Skip to content

Commit 6e60330

Browse files
authored
[SampleFDO] Read call-graph matching recovered top-level function profile (#101053)
With extbinary profile format, initial profile loading only reads profile based on current function names in the module. However, if a function is renamed, sample loader skips to load its original profile(which has a different name), we will miss this case. To address this, we load the top-level profile candidate explicitly for the matching. If a match is found later, the function profile will be further preserved for use by the sample loader.
1 parent 6175951 commit 6e60330

File tree

5 files changed

+278
-26
lines changed

5 files changed

+278
-26
lines changed

llvm/include/llvm/ProfileData/SampleProfReader.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,16 @@ class SampleProfileReader {
424424
if (It != Profiles.end())
425425
return &It->second;
426426

427+
if (FuncNameToProfNameMap && !FuncNameToProfNameMap->empty()) {
428+
auto R = FuncNameToProfNameMap->find(FunctionId(Fname));
429+
if (R != FuncNameToProfNameMap->end()) {
430+
Fname = R->second.stringRef();
431+
auto It = Profiles.find(FunctionId(Fname));
432+
if (It != Profiles.end())
433+
return &It->second;
434+
}
435+
}
436+
427437
if (Remapper) {
428438
if (auto NameInProfile = Remapper->lookUpNameInProfile(Fname)) {
429439
auto It = Profiles.find(FunctionId(*NameInProfile));
@@ -505,6 +515,11 @@ class SampleProfileReader {
505515

506516
void setModule(const Module *Mod) { M = Mod; }
507517

518+
void setFuncNameToProfNameMap(
519+
const HashKeyMap<std::unordered_map, FunctionId, FunctionId> &FPMap) {
520+
FuncNameToProfNameMap = &FPMap;
521+
}
522+
508523
protected:
509524
/// Map every function to its associated profile.
510525
///
@@ -541,6 +556,12 @@ class SampleProfileReader {
541556

542557
std::unique_ptr<SampleProfileReaderItaniumRemapper> Remapper;
543558

559+
// A map pointer to the FuncNameToProfNameMap in SampleProfileLoader,
560+
// which maps the function name to the matched profile name. This is used
561+
// for sample loader to look up profile using the new name.
562+
const HashKeyMap<std::unordered_map, FunctionId, FunctionId>
563+
*FuncNameToProfNameMap = nullptr;
564+
544565
// A map from a function's context hash to its meta data section range, used
545566
// for on-demand read function profile metadata.
546567
std::unordered_map<uint64_t, std::pair<const uint8_t *, const uint8_t *>>

llvm/include/llvm/Transforms/IPO/SampleProfileMatcher.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ class SampleProfileMatcher {
198198
// function and all inlinees.
199199
void countMismatchedCallsiteSamples(const FunctionSamples &FS);
200200
void computeAndReportProfileStaleness();
201+
void UpdateWithSalvagedProfiles();
201202

202203
LocToLocMap &getIRToProfileLocationMap(const Function &F) {
203204
auto Ret = FuncMappings.try_emplace(

llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ static cl::opt<unsigned> MinCallCountForCGMatching(
3636
cl::desc("The minimum number of call anchors required for a function to "
3737
"run stale profile call graph matching."));
3838

39+
static cl::opt<bool> LoadFuncProfileforCGMatching(
40+
"load-func-profile-for-cg-matching", cl::Hidden, cl::init(false),
41+
cl::desc(
42+
"Load top-level profiles that the sample reader initially skipped for "
43+
"the call-graph matching (only meaningful for extended binary "
44+
"format)"));
45+
3946
extern cl::opt<bool> SalvageStaleProfile;
4047
extern cl::opt<bool> SalvageUnusedProfile;
4148
extern cl::opt<bool> PersistProfileStaleness;
@@ -410,18 +417,19 @@ void SampleProfileMatcher::runOnFunction(Function &F) {
410417
// callsites in one context may differ from those in another context. To get
411418
// the maximum number of callsites, we merge the function profiles from all
412419
// contexts, aka, the flattened profile to find profile anchors.
413-
const auto *FSFlattened = getFlattenedSamplesFor(F);
414-
if (SalvageUnusedProfile && !FSFlattened) {
420+
const auto *FSForMatching = getFlattenedSamplesFor(F);
421+
if (SalvageUnusedProfile && !FSForMatching) {
415422
// Apply the matching in place to find the new function's matched profile.
416-
// TODO: For extended profile format, if a function profile is unused and
417-
// it's top-level, even if the profile is matched, it's not found in the
418-
// profile. This is because sample reader only read the used profile at the
419-
// beginning, we need to support loading the profile on-demand in future.
420423
auto R = FuncToProfileNameMap.find(&F);
421-
if (R != FuncToProfileNameMap.end())
422-
FSFlattened = getFlattenedSamplesFor(R->second);
424+
if (R != FuncToProfileNameMap.end()) {
425+
FSForMatching = getFlattenedSamplesFor(R->second);
426+
// Try to find the salvaged top-level profiles that are explicitly loaded
427+
// for the matching, see "functionMatchesProfileHelper" for the details.
428+
if (!FSForMatching && LoadFuncProfileforCGMatching)
429+
FSForMatching = Reader.getSamplesFor(R->second.stringRef());
430+
}
423431
}
424-
if (!FSFlattened)
432+
if (!FSForMatching)
425433
return;
426434

427435
// Anchors for IR. It's a map from IR location to callee name, callee name is
@@ -432,7 +440,7 @@ void SampleProfileMatcher::runOnFunction(Function &F) {
432440
// Anchors for profile. It's a map from callsite location to a set of callee
433441
// name.
434442
AnchorMap ProfileAnchors;
435-
findProfileAnchors(*FSFlattened, ProfileAnchors);
443+
findProfileAnchors(*FSForMatching, ProfileAnchors);
436444

437445
// Compute the callsite match states for profile staleness report.
438446
if (ReportProfileStaleness || PersistProfileStaleness)
@@ -443,7 +451,7 @@ void SampleProfileMatcher::runOnFunction(Function &F) {
443451
// For probe-based profiles, run matching only when profile checksum is
444452
// mismatched.
445453
bool ChecksumMismatch = FunctionSamples::ProfileIsProbeBased &&
446-
!ProbeManager->profileIsValid(F, *FSFlattened);
454+
!ProbeManager->profileIsValid(F, *FSForMatching);
447455
bool RunCFGMatching =
448456
!FunctionSamples::ProfileIsProbeBased || ChecksumMismatch;
449457
bool RunCGMatching = SalvageUnusedProfile;
@@ -781,22 +789,38 @@ bool SampleProfileMatcher::functionMatchesProfileHelper(
781789
// two sequences are.
782790
float Similarity = 0.0;
783791

784-
const auto *FSFlattened = getFlattenedSamplesFor(ProfFunc);
785-
if (!FSFlattened)
792+
const auto *FSForMatching = getFlattenedSamplesFor(ProfFunc);
793+
// With extbinary profile format, initial profile loading only reads profile
794+
// based on current function names in the module.
795+
// However, if a function is renamed, sample loader skips to load its original
796+
// profile(which has a different name), we will miss this case. To address
797+
// this, we load the top-level profile candidate explicitly for the matching.
798+
if (!FSForMatching && LoadFuncProfileforCGMatching) {
799+
DenseSet<StringRef> TopLevelFunc({ProfFunc.stringRef()});
800+
if (std::error_code EC = Reader.read(TopLevelFunc))
801+
return false;
802+
FSForMatching = Reader.getSamplesFor(ProfFunc.stringRef());
803+
LLVM_DEBUG({
804+
if (FSForMatching)
805+
dbgs() << "Read top-level function " << ProfFunc
806+
<< " for call-graph matching\n";
807+
});
808+
}
809+
if (!FSForMatching)
786810
return false;
787811
// The check for similarity or checksum may not be reliable if the function is
788812
// tiny, we use the number of basic block as a proxy for the function
789813
// complexity and skip the matching if it's too small.
790814
if (IRFunc.size() < MinFuncCountForCGMatching ||
791-
FSFlattened->getBodySamples().size() < MinFuncCountForCGMatching)
815+
FSForMatching->getBodySamples().size() < MinFuncCountForCGMatching)
792816
return false;
793817

794818
// For probe-based function, we first trust the checksum info. If the checksum
795819
// doesn't match, we continue checking for similarity.
796820
if (FunctionSamples::ProfileIsProbeBased) {
797821
const auto *FuncDesc = ProbeManager->getDesc(IRFunc);
798822
if (FuncDesc &&
799-
!ProbeManager->profileIsHashMismatched(*FuncDesc, *FSFlattened)) {
823+
!ProbeManager->profileIsHashMismatched(*FuncDesc, *FSForMatching)) {
800824
LLVM_DEBUG(dbgs() << "The checksums for " << IRFunc.getName()
801825
<< "(IR) and " << ProfFunc << "(Profile) match.\n");
802826

@@ -807,7 +831,7 @@ bool SampleProfileMatcher::functionMatchesProfileHelper(
807831
AnchorMap IRAnchors;
808832
findIRAnchors(IRFunc, IRAnchors);
809833
AnchorMap ProfileAnchors;
810-
findProfileAnchors(*FSFlattened, ProfileAnchors);
834+
findProfileAnchors(*FSForMatching, ProfileAnchors);
811835

812836
AnchorList FilteredIRAnchorsList;
813837
AnchorList FilteredProfileAnchorList;
@@ -863,6 +887,29 @@ bool SampleProfileMatcher::functionMatchesProfile(Function &IRFunc,
863887
return Matched;
864888
}
865889

890+
void SampleProfileMatcher::UpdateWithSalvagedProfiles() {
891+
DenseSet<StringRef> ProfileSalvagedFuncs;
892+
// Update FuncNameToProfNameMap and SymbolMap.
893+
for (auto &I : FuncToProfileNameMap) {
894+
assert(I.first && "New function is null");
895+
FunctionId FuncName(I.first->getName());
896+
ProfileSalvagedFuncs.insert(I.second.stringRef());
897+
FuncNameToProfNameMap->emplace(FuncName, I.second);
898+
899+
// We need to remove the old entry to avoid duplicating the function
900+
// processing.
901+
SymbolMap->erase(FuncName);
902+
SymbolMap->emplace(I.second, I.first);
903+
}
904+
905+
// With extbinary profile format, initial profile loading only reads profile
906+
// based on current function names in the module, so we need to load top-level
907+
// profiles for functions with different profile name explicitly after
908+
// function-profile name map is established with stale profile matching.
909+
Reader.read(ProfileSalvagedFuncs);
910+
Reader.setFuncNameToProfNameMap(*FuncNameToProfNameMap);
911+
}
912+
866913
void SampleProfileMatcher::runOnModule() {
867914
ProfileConverter::flattenProfile(Reader.getProfiles(), FlattenedProfiles,
868915
FunctionSamples::ProfileIsCS);
@@ -880,17 +927,8 @@ void SampleProfileMatcher::runOnModule() {
880927
runOnFunction(*F);
881928
}
882929

883-
// Update the data in SampleLoader.
884930
if (SalvageUnusedProfile)
885-
for (auto &I : FuncToProfileNameMap) {
886-
assert(I.first && "New function is null");
887-
FunctionId FuncName(I.first->getName());
888-
FuncNameToProfNameMap->emplace(FuncName, I.second);
889-
// We need to remove the old entry to avoid duplicating the function
890-
// processing.
891-
SymbolMap->erase(FuncName);
892-
SymbolMap->emplace(I.second, I.first);
893-
}
931+
UpdateWithSalvagedProfiles();
894932

895933
if (SalvageStaleProfile)
896934
distributeIRToProfileLocationMap();
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
foo:2724522:51
2+
1: 51
3+
2: 452674
4+
3: 47
5+
4: 497875
6+
6: 415959
7+
10: 452623
8+
11: 452687 bar:452687
9+
12: 452623
10+
13: 47
11+
!CFGChecksum: 281479271677951
12+
bar:452687:452687
13+
1: 452687
14+
!CFGChecksum: 4294967295
15+
main:204:0
16+
1: 0
17+
2: 51
18+
3: 0
19+
4: 51
20+
5: 51 foo:51
21+
6: 51
22+
7: 0
23+
!CFGChecksum: 281582264815352

0 commit comments

Comments
 (0)