@@ -29,6 +29,16 @@ static cl::opt<unsigned> RenamedFuncSimilarityThreshold(
29
29
cl::desc(" The profile matches the function if their similarity is above "
30
30
" the given number(percentage)." ));
31
31
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
+
32
42
extern cl::opt<bool > SalvageStaleProfile;
33
43
extern cl::opt<bool > PersistProfileStaleness;
34
44
extern cl::opt<bool > ReportProfileStaleness;
@@ -610,7 +620,7 @@ void SampleProfileMatcher::computeAndReportProfileStaleness() {
610
620
// Find functions that don't show in the profile or profile symbol list, which
611
621
// are supposed to be new functions. We use them as the targets for renaming
612
622
// matching.
613
- void SampleProfileMatcher::findnewIRFunctions (
623
+ void SampleProfileMatcher::findNewIRFunctions (
614
624
StringMap<Function *> &newIRFunctions) {
615
625
// TODO: Support MD5 profile.
616
626
if (FunctionSamples::UseMD5)
@@ -668,70 +678,99 @@ void SampleProfileMatcher::findNewIRCallees(
668
678
}
669
679
}
670
680
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
+
671
706
// Determine if the function matches profile by computing a similarity ratio
672
707
// 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) {
681
710
// 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
+ }
685
735
686
736
AnchorMap IRAnchors;
687
737
findIRAnchors (IRFunc, IRAnchors);
688
-
689
738
AnchorMap ProfileAnchors;
690
- const auto *FSFlattened = getFlattenedSamplesFor (ProfFunc);
691
- assert (FSFlattened && " Flattened profile sample is null" );
692
739
findProfileAnchors (*FSFlattened, ProfileAnchors);
693
740
694
741
AnchorList FilteredIRAnchorsList;
695
742
AnchorList FilteredProfileAnchorList;
696
743
getFilteredAnchorList (IRAnchors, ProfileAnchors, FilteredIRAnchorsList,
697
744
FilteredProfileAnchorList);
698
745
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 ;
718
750
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);
723
754
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 ());
728
758
729
759
LLVM_DEBUG (dbgs () << " The similarity between " << IRFunc.getName ()
730
760
<< " (IR) and " << ProfFunc << " (profile) is "
731
761
<< format (" %.2f" , Similarity) << " \n " );
732
762
assert ((Similarity >= 0 && Similarity <= 1.0 ) &&
733
763
" 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);
735
774
FunctionProfileNameMap[{&IRFunc, ProfFunc}] = Matched;
736
775
return Matched;
737
776
}
@@ -741,72 +780,64 @@ bool SampleProfileMatcher::functionMatchesProfile(const Function &IRFunc,
741
780
void SampleProfileMatcher::matchProfileForNewFunctions (
742
781
const StringMap<Function *> &newIRFunctions, FunctionSamples &CallerFS,
743
782
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.
761
784
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 ) {
764
788
// No callees for external function, skip the rename matching.
765
- if ((*Caller) ->isDeclaration ())
789
+ if (IRCaller ->isDeclaration ())
766
790
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
+ }
768
819
}
769
820
770
- // Run function to profile matching on call-graph edge(caller-callee) .
821
+ // Match inline callees .
771
822
for (auto &CM :
772
823
const_cast <CallsiteSampleMap &>(CallerFS.getCallsiteSamples ())) {
773
824
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.
775
827
std::vector<std::pair<FunctionId, FunctionSamples *>> FSamplesToUpdate;
776
828
for (auto &CS : CalleeMap) {
777
829
FunctionSamples &CalleeFS = CS.second ;
778
830
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);
810
841
}
811
842
// Note that even there is no renaming in the current scope, there could
812
843
// be renaming in deeper callee scope, we need to traverse all the callee
@@ -817,14 +848,31 @@ void SampleProfileMatcher::matchProfileForNewFunctions(
817
848
818
849
// Update the CalleeMap using the new name and remove the old entry.
819
850
for (auto &P : FSamplesToUpdate) {
820
- assert ((P.first != P.second ->getFunction ()) &&
851
+ const FunctionId &OldFunction = P.second ->getFunction ();
852
+ assert (P.first != OldFunction &&
821
853
" 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);
824
857
}
825
858
}
826
859
}
827
860
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
+
828
876
void SampleProfileMatcher::runCallGraphMatching () {
829
877
if (!SalvageRenamedProfile)
830
878
return ;
@@ -833,17 +881,17 @@ void SampleProfileMatcher::runCallGraphMatching() {
833
881
" FunctionProfileNameMap is not empty before the call graph matching" );
834
882
835
883
StringMap<Function *> newIRFunctions;
836
- findnewIRFunctions (newIRFunctions);
884
+ findNewIRFunctions (newIRFunctions);
837
885
if (newIRFunctions.empty ())
838
886
return ;
839
887
840
888
// The new functions found by the renaming matching. Save them into a map
841
889
// whose key is the old(profile) function name and value is the new(renamed)
842
890
// function.
843
891
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);
847
895
848
896
// Update all the data generated by the old profile.
849
897
if (!OldProfToNewSymbolMap.empty ()) {
0 commit comments