Skip to content

Commit 6db01bc

Browse files
committed
fix test & udpate non-inline call targets & addressing comments
1 parent f1e659d commit 6db01bc

File tree

4 files changed

+181
-121
lines changed

4 files changed

+181
-121
lines changed

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
#define LLVM_TRANSFORMS_IPO_SAMPLEPROFILEMATCHER_H
1616

1717
#include "llvm/ADT/StringSet.h"
18-
#include "llvm/Analysis/ProfileSummaryInfo.h"
1918
#include "llvm/Transforms/Utils/SampleProfileLoaderBaseImpl.h"
2019

2120
namespace llvm {
@@ -187,15 +186,22 @@ class SampleProfileMatcher {
187186
void runStaleProfileMatching(const Function &F, const AnchorMap &IRAnchors,
188187
const AnchorMap &ProfileAnchors,
189188
LocToLocMap &IRToProfileLocationMap);
189+
std::pair<Function *, bool>
190+
findOrMatchFunction(const FunctionId &ProfCallee,
191+
FunctionMap &OldProfToNewSymbolMap,
192+
const std::vector<Function *> &NewIRCallees);
193+
std::vector<FunctionSamples *> sortFuncProfiles(SampleProfileMap &ProfileMap);
190194
void findNewIRCallees(Function &Caller,
191195
const StringMap<Function *> &newIRFunctions,
192196
std::vector<Function *> &NewIRCallees);
197+
bool functionMatchesProfileHelper(const Function &IRFunc,
198+
const FunctionId &ProfFunc);
193199
bool functionMatchesProfile(const Function &IRFunc,
194200
const FunctionId &ProfFunc);
195201
void matchProfileForNewFunctions(const StringMap<Function *> &newIRFunctions,
196202
FunctionSamples &FS,
197203
FunctionMap &OldProfToNewSymbolMap);
198-
void findnewIRFunctions(StringMap<Function *> &newIRFunctions);
204+
void findNewIRFunctions(StringMap<Function *> &newIRFunctions);
199205
void runCallGraphMatching();
200206
void reportOrPersistProfileStats();
201207
};

llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp

Lines changed: 152 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ static cl::opt<unsigned> RenamedFuncSimilarityThreshold(
2929
cl::desc("The profile matches the function if their similarity is above "
3030
"the given number(percentage)."));
3131

32+
static cl::opt<unsigned> MinBBForCGMatching(
33+
"min-bb-for-cg-matching", cl::Hidden, cl::init(5),
34+
cl::desc("The minimum number of basic blocks required for a function to "
35+
"run stale profile call graph matching."));
36+
37+
static cl::opt<unsigned> MinCallAnchorForCGMatching(
38+
"min-call-for-cg-matching", cl::Hidden, cl::init(3),
39+
cl::desc("The minimum number of call anchors required for a function to "
40+
"run stale profile call graph matching."));
41+
3242
extern cl::opt<bool> SalvageStaleProfile;
3343
extern cl::opt<bool> PersistProfileStaleness;
3444
extern cl::opt<bool> ReportProfileStaleness;
@@ -610,7 +620,7 @@ void SampleProfileMatcher::computeAndReportProfileStaleness() {
610620
// Find functions that don't show in the profile or profile symbol list, which
611621
// are supposed to be new functions. We use them as the targets for renaming
612622
// matching.
613-
void SampleProfileMatcher::findnewIRFunctions(
623+
void SampleProfileMatcher::findNewIRFunctions(
614624
StringMap<Function *> &newIRFunctions) {
615625
// TODO: Support MD5 profile.
616626
if (FunctionSamples::UseMD5)
@@ -668,70 +678,99 @@ void SampleProfileMatcher::findNewIRCallees(
668678
}
669679
}
670680

681+
// Find the function using the profile name. If the function is not found but
682+
// the NewIRCallees is provided, try to match the function profile with all
683+
// functions in NewIRCallees and return the matched function.
684+
// The return pair includes the function pointer and a bool value indicating
685+
// whether the function is new(matched).
686+
std::pair<Function *, bool> SampleProfileMatcher::findOrMatchFunction(
687+
const FunctionId &ProfCallee, FunctionMap &OldProfToNewSymbolMap,
688+
const std::vector<Function *> &NewIRCallees = std::vector<Function *>()) {
689+
auto F = SymbolMap->find(ProfCallee);
690+
if (F != SymbolMap->end())
691+
return {F->second, false};
692+
693+
// Existing matched function is found.
694+
auto NewF = OldProfToNewSymbolMap.find(ProfCallee);
695+
if (NewF != OldProfToNewSymbolMap.end())
696+
return {NewF->second, true};
697+
698+
for (auto *IRCallee : NewIRCallees)
699+
if (functionMatchesProfile(*IRCallee, ProfCallee)) {
700+
OldProfToNewSymbolMap[ProfCallee] = IRCallee;
701+
return {IRCallee, true};
702+
}
703+
return {nullptr, false};
704+
}
705+
671706
// Determine if the function matches profile by computing a similarity ratio
672707
// between two callsite anchors sequences extracted from function and profile.
673-
// The returned value is in the range [0, 1]. The bigger the value is, the more
674-
// similar two sequences are.
675-
bool SampleProfileMatcher::functionMatchesProfile(const Function &IRFunc,
676-
const FunctionId &ProfFunc) {
677-
// Check the cache.
678-
auto R = FunctionProfileNameMap.find({&IRFunc, ProfFunc});
679-
if (R != FunctionProfileNameMap.end())
680-
return R->second;
708+
bool SampleProfileMatcher::functionMatchesProfileHelper(
709+
const Function &IRFunc, const FunctionId &ProfFunc) {
681710
// The value is in the range [0, 1]. The bigger the value is, the more similar
682-
// two sequences are. -1.0 means the similarity is not set, and 0.0 means no
683-
// match.
684-
float Similarity = -1.0;
711+
// two sequences are.
712+
float Similarity = 0.0;
713+
714+
const auto *FSFlattened = getFlattenedSamplesFor(ProfFunc);
715+
assert(FSFlattened && "Flattened profile sample is null");
716+
// Similarity check may not be reiable if the function is tiny, we use the
717+
// number of basic block as a proxy for the function complexity and skip the
718+
// matching if it's too small.
719+
if (IRFunc.size() < MinBBForCGMatching ||
720+
FSFlattened->getBodySamples().size() < MinBBForCGMatching)
721+
return false;
722+
723+
// For probe-based function, we first trust the checksum info. If the checksum
724+
// doesn't match, we continue checking for similarity.
725+
if (FunctionSamples::ProfileIsProbeBased) {
726+
const auto *FuncDesc = ProbeManager->getDesc(IRFunc);
727+
if (FuncDesc &&
728+
!ProbeManager->profileIsHashMismatched(*FuncDesc, *FSFlattened)) {
729+
LLVM_DEBUG(dbgs() << "The checksums for " << IRFunc.getName()
730+
<< "(IR) and " << ProfFunc << "(Profile) match.\n");
731+
732+
return true;
733+
}
734+
}
685735

686736
AnchorMap IRAnchors;
687737
findIRAnchors(IRFunc, IRAnchors);
688-
689738
AnchorMap ProfileAnchors;
690-
const auto *FSFlattened = getFlattenedSamplesFor(ProfFunc);
691-
assert(FSFlattened && "Flattened profile sample is null");
692739
findProfileAnchors(*FSFlattened, ProfileAnchors);
693740

694741
AnchorList FilteredIRAnchorsList;
695742
AnchorList FilteredProfileAnchorList;
696743
getFilteredAnchorList(IRAnchors, ProfileAnchors, FilteredIRAnchorsList,
697744
FilteredProfileAnchorList);
698745

699-
// If the function is probe based, we trust the checksum info to check the
700-
// similarity. Otherwise, if the checksum is mismatched, continue computing
701-
// the similarity.
702-
if (FunctionSamples::ProfileIsProbeBased) {
703-
const auto *FuncDesc = ProbeManager->getDesc(IRFunc);
704-
// Probe-based profile checksum is based on the blocks, if the num of
705-
// function block is small, it's more likely to get checksum conflict and
706-
// generate wrong matching.
707-
if (IRAnchors.size() - FilteredIRAnchorsList.size() > 5 && FuncDesc &&
708-
!ProbeManager->profileIsHashMismatched(*FuncDesc, *FSFlattened)) {
709-
Similarity = 1.0;
710-
}
711-
}
712-
713-
// Skip the matching if the function is tiny. Similarity check may not be
714-
// reiable if the num of anchors is small.
715-
if (Similarity == -1.0 && (FilteredIRAnchorsList.size() <= 2 ||
716-
FilteredProfileAnchorList.size() <= 2))
717-
Similarity = 0.0;
746+
// Similarly skip the matching if the num of anchors is not enough.
747+
if (FilteredIRAnchorsList.size() < MinCallAnchorForCGMatching ||
748+
FilteredProfileAnchorList.size() < MinCallAnchorForCGMatching)
749+
return false;
718750

719-
if (Similarity == -1.0) {
720-
// Use the diff algorithm to find the LCS between IR and profile.
721-
LocToLocMap MatchedAnchors =
722-
longestCommonSequence(FilteredIRAnchorsList, FilteredProfileAnchorList);
751+
// Use the diff algorithm to find the LCS between IR and profile.
752+
LocToLocMap MatchedAnchors =
753+
longestCommonSequence(FilteredIRAnchorsList, FilteredProfileAnchorList);
723754

724-
Similarity =
725-
static_cast<float>(MatchedAnchors.size()) * 2 /
726-
(FilteredIRAnchorsList.size() + FilteredProfileAnchorList.size());
727-
}
755+
Similarity =
756+
static_cast<float>(MatchedAnchors.size()) * 2 /
757+
(FilteredIRAnchorsList.size() + FilteredProfileAnchorList.size());
728758

729759
LLVM_DEBUG(dbgs() << "The similarity between " << IRFunc.getName()
730760
<< "(IR) and " << ProfFunc << "(profile) is "
731761
<< format("%.2f", Similarity) << "\n");
732762
assert((Similarity >= 0 && Similarity <= 1.0) &&
733763
"Similarity value should be in [0, 1]");
734-
bool Matched = Similarity * 100 > RenamedFuncSimilarityThreshold;
764+
return Similarity * 100 > RenamedFuncSimilarityThreshold;
765+
}
766+
767+
bool SampleProfileMatcher::functionMatchesProfile(const Function &IRFunc,
768+
const FunctionId &ProfFunc) {
769+
auto R = FunctionProfileNameMap.find({&IRFunc, ProfFunc});
770+
if (R != FunctionProfileNameMap.end())
771+
return R->second;
772+
773+
bool Matched = functionMatchesProfileHelper(IRFunc, ProfFunc);
735774
FunctionProfileNameMap[{&IRFunc, ProfFunc}] = Matched;
736775
return Matched;
737776
}
@@ -741,72 +780,64 @@ bool SampleProfileMatcher::functionMatchesProfile(const Function &IRFunc,
741780
void SampleProfileMatcher::matchProfileForNewFunctions(
742781
const StringMap<Function *> &newIRFunctions, FunctionSamples &CallerFS,
743782
FunctionMap &OldProfToNewSymbolMap) {
744-
auto FindIRFunction = [&](const FunctionId &FName) {
745-
// Function can be null if name has conflict, use optional to store the
746-
// function pointer.
747-
std::optional<Function *> F;
748-
749-
auto R = SymbolMap->find(FName);
750-
if (R != SymbolMap->end())
751-
return std::optional<Function *>(R->second);
752-
753-
auto NewR = OldProfToNewSymbolMap.find(FName);
754-
if (NewR != OldProfToNewSymbolMap.end())
755-
F = NewR->second;
756-
757-
return F;
758-
};
759-
760-
// Find the new callees from IR in the current caller scope.
783+
// Find the new candidate callees from IR in the current caller scope.
761784
std::vector<Function *> NewIRCallees;
762-
auto Caller = FindIRFunction(CallerFS.getFunction());
763-
if (Caller.has_value() && *Caller) {
785+
if (auto *IRCaller =
786+
findOrMatchFunction(CallerFS.getFunction(), OldProfToNewSymbolMap)
787+
.first) {
764788
// No callees for external function, skip the rename matching.
765-
if ((*Caller)->isDeclaration())
789+
if (IRCaller->isDeclaration())
766790
return;
767-
findNewIRCallees(**Caller, newIRFunctions, NewIRCallees);
791+
findNewIRCallees(*IRCaller, newIRFunctions, NewIRCallees);
792+
}
793+
794+
// Match non-inline callees.
795+
for (auto &BS : const_cast<BodySampleMap &>(CallerFS.getBodySamples())) {
796+
// New function to old function pairs used to update the CallTargetMap.
797+
std::vector<std::pair<FunctionId, FunctionId>> CallTargetsToUpdate;
798+
SampleRecord::CallTargetMap &CTM =
799+
const_cast<SampleRecord::CallTargetMap &>(BS.second.getCallTargets());
800+
for (const auto &TS : CTM) {
801+
const FunctionId &ProfCallee = TS.first;
802+
auto MatchRes =
803+
findOrMatchFunction(ProfCallee, OldProfToNewSymbolMap, NewIRCallees);
804+
if (!MatchRes.second)
805+
continue;
806+
FunctionId NewIRCalleeName(MatchRes.first->getName());
807+
assert(NewIRCalleeName != ProfCallee &&
808+
"New callee symbol is not a new function");
809+
LLVM_DEBUG(dbgs() << "In function " << CallerFS.getFunction()
810+
<< ", changing profile name from " << ProfCallee
811+
<< " to " << NewIRCalleeName << "\n");
812+
CallTargetsToUpdate.emplace_back(NewIRCalleeName, ProfCallee);
813+
}
814+
815+
for (const auto &P : CallTargetsToUpdate) {
816+
CTM[P.first] = CTM[P.second];
817+
CTM.erase(P.second);
818+
}
768819
}
769820

770-
// Run function to profile matching on call-graph edge(caller-callee).
821+
// Match inline callees.
771822
for (auto &CM :
772823
const_cast<CallsiteSampleMap &>(CallerFS.getCallsiteSamples())) {
773824
auto &CalleeMap = CM.second;
774-
// Local container used to update the CallsiteSampleMap.
825+
// New function to old FunctionSamples pairs used to update the
826+
// CallsiteSampleMap.
775827
std::vector<std::pair<FunctionId, FunctionSamples *>> FSamplesToUpdate;
776828
for (auto &CS : CalleeMap) {
777829
FunctionSamples &CalleeFS = CS.second;
778830
FunctionId ProfCallee = CalleeFS.getFunction();
779-
std::optional<Function *> ExistingIRCallee = FindIRFunction(ProfCallee);
780-
// The profile callee is new, run function to profile matching.
781-
if (!ExistingIRCallee.has_value()) {
782-
for (auto *IRCallee : NewIRCallees) {
783-
if (functionMatchesProfile(*IRCallee, ProfCallee)) {
784-
FSamplesToUpdate.emplace_back(ProfCallee, &CalleeFS);
785-
OldProfToNewSymbolMap[ProfCallee] = IRCallee;
786-
// Update the profile in place so that the deeper level matching
787-
// will find the IR function.
788-
CalleeFS.setFunction(FunctionId(IRCallee->getName()));
789-
LLVM_DEBUG(dbgs() << "Callee renaming is found in function "
790-
<< CallerFS.getFunction()
791-
<< ", changing profile name from " << ProfCallee
792-
<< " to " << IRCallee->getName() << "\n");
793-
break;
794-
}
795-
}
796-
} else {
797-
// Apply the existing renaming result.
798-
auto R = OldProfToNewSymbolMap.find(CalleeFS.getFunction());
799-
if (R != OldProfToNewSymbolMap.end()) {
800-
FunctionId IRNewCallee(R->second->getName());
801-
assert(IRNewCallee != ProfCallee &&
802-
"New callee symbol is not a new function");
803-
FSamplesToUpdate.emplace_back(ProfCallee, &CalleeFS);
804-
CalleeFS.setFunction(IRNewCallee);
805-
LLVM_DEBUG(dbgs() << "Existing callee renaming is found in function "
806-
<< CallerFS.getFunction()
807-
<< ", changing profile name from " << ProfCallee
808-
<< " to " << IRNewCallee << "\n");
809-
}
831+
auto MatchRes =
832+
findOrMatchFunction(ProfCallee, OldProfToNewSymbolMap, NewIRCallees);
833+
if (MatchRes.second) {
834+
FunctionId NewIRCalleeName(MatchRes.first->getName());
835+
assert(NewIRCalleeName != ProfCallee &&
836+
"New callee symbol is not a new function");
837+
LLVM_DEBUG(dbgs() << "In function " << CallerFS.getFunction()
838+
<< ", changing profile name from " << ProfCallee
839+
<< " to " << NewIRCalleeName << "\n");
840+
FSamplesToUpdate.emplace_back(NewIRCalleeName, &CalleeFS);
810841
}
811842
// Note that even there is no renaming in the current scope, there could
812843
// be renaming in deeper callee scope, we need to traverse all the callee
@@ -817,14 +848,31 @@ void SampleProfileMatcher::matchProfileForNewFunctions(
817848

818849
// Update the CalleeMap using the new name and remove the old entry.
819850
for (auto &P : FSamplesToUpdate) {
820-
assert((P.first != P.second->getFunction()) &&
851+
const FunctionId &OldFunction = P.second->getFunction();
852+
assert(P.first != OldFunction &&
821853
"Renamed function name should be different from the old map key");
822-
CalleeMap[P.second->getFunction()] = *P.second;
823-
CalleeMap.erase(P.first);
854+
P.second->setFunction(P.first);
855+
CalleeMap[P.first] = *P.second;
856+
CalleeMap.erase(OldFunction);
824857
}
825858
}
826859
}
827860

861+
std::vector<FunctionSamples *>
862+
SampleProfileMatcher::sortFuncProfiles(SampleProfileMap &ProfileMap) {
863+
std::vector<FunctionSamples *> SortedProfiles;
864+
for (auto &I : ProfileMap)
865+
SortedProfiles.push_back(&I.second);
866+
867+
llvm::stable_sort(SortedProfiles,
868+
[](const FunctionSamples *A, const FunctionSamples *B) {
869+
if (A->getTotalSamples() == B->getTotalSamples())
870+
return A->getContext() < B->getContext();
871+
return A->getTotalSamples() > B->getTotalSamples();
872+
});
873+
return SortedProfiles;
874+
}
875+
828876
void SampleProfileMatcher::runCallGraphMatching() {
829877
if (!SalvageRenamedProfile)
830878
return;
@@ -833,17 +881,17 @@ void SampleProfileMatcher::runCallGraphMatching() {
833881
"FunctionProfileNameMap is not empty before the call graph matching");
834882

835883
StringMap<Function *> newIRFunctions;
836-
findnewIRFunctions(newIRFunctions);
884+
findNewIRFunctions(newIRFunctions);
837885
if (newIRFunctions.empty())
838886
return;
839887

840888
// The new functions found by the renaming matching. Save them into a map
841889
// whose key is the old(profile) function name and value is the new(renamed)
842890
// function.
843891
FunctionMap OldProfToNewSymbolMap;
844-
for (auto &I : Reader.getProfiles())
845-
matchProfileForNewFunctions(newIRFunctions, I.second,
846-
OldProfToNewSymbolMap);
892+
// Sort the profiles to make the matching order deterministic.
893+
for (auto *P : sortFuncProfiles(Reader.getProfiles()))
894+
matchProfileForNewFunctions(newIRFunctions, *P, OldProfToNewSymbolMap);
847895

848896
// Update all the data generated by the old profile.
849897
if (!OldProfToNewSymbolMap.empty()) {

llvm/test/Transforms/SampleProfile/Inputs/pseudo-probe-stale-profile-renaming.prof

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,13 @@ main:47:0
3232
!CFGChecksum: 281479271677951
3333
10: cold_func:0
3434
1: 0
35-
2: block_only:0
36-
1: 0
37-
3: 0
38-
5: 0
39-
10: 0
40-
!CFGChecksum: 206551239323
35+
2: 0 block_only:0
4136
!CFGChecksum: 281479271677951
4237
!CFGChecksum: 1126003093360596
4338
test_noninline:22:2
4439
1: 2
4540
2: foo:20
46-
1: 2
41+
1: 3
4742
2: 2 bar:3
4843
4: 3 bar:3
4944
3: baz:13

0 commit comments

Comments
 (0)