[llvm] [SampleProfileMatcher] Move fuzzy base name matching to a later stage after more precise CG matching (PR #210798)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 30 21:56:54 PDT 2026
https://github.com/HighW4y2H3ll updated https://github.com/llvm/llvm-project/pull/210798
>From 704547905fadfaf54a595137497c9debae1c32d4 Mon Sep 17 00:00:00 2001
From: h2h <h2h at meta.com>
Date: Fri, 10 Jul 2026 14:48:50 -0700
Subject: [PATCH 1/8] [NFC][SampleProfileMatcher] Fix naming conventions and
comments following PR202460
---
.../Transforms/IPO/SampleProfileMatcher.h | 7 ++-
.../Transforms/IPO/SampleProfileMatcher.cpp | 52 +++++++++----------
2 files changed, 31 insertions(+), 28 deletions(-)
diff --git a/llvm/include/llvm/Transforms/IPO/SampleProfileMatcher.h b/llvm/include/llvm/Transforms/IPO/SampleProfileMatcher.h
index 1cce006e260ab..251d5bc7e479b 100644
--- a/llvm/include/llvm/Transforms/IPO/SampleProfileMatcher.h
+++ b/llvm/include/llvm/Transforms/IPO/SampleProfileMatcher.h
@@ -37,8 +37,6 @@ class SampleProfileMatcher {
// mapping from the source location of current build to the source location
// in the profile.
StringMap<LocToLocMap> FuncMappings;
- // Hash mapping cache for matched anchor pairs in stale profile matching
- DenseMap<FunctionId, const Function *> MatchedAnchorCache;
// Match state for an anchor/callsite.
enum class MatchState {
@@ -71,6 +69,11 @@ class SampleProfileMatcher {
// the new(renamed) function pointer and the value is old(unused) profile
// name.
MapVector<Function *, FunctionId> FuncToProfileNameMap;
+ // Mapping from matched(renamed) profile name to IR function to during call
+ // graph matching. This is a reversed FuncToProfileNameMap to track duplicated
+ // profile matching and resolve multiple IR functions being mapped to a single
+ // profile.
+ DenseMap<FunctionId, const Function *> ProfileNameToFuncMap;
// A map pointer to the FuncNameToProfNameMap in SampleProfileLoader,
// which maps the function name to the matched profile name. This is used
diff --git a/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp b/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
index 2a6c9ee61efd4..5aef593e561d8 100644
--- a/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
+++ b/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
@@ -396,59 +396,59 @@ void SampleProfileMatcher::runStaleProfileMatching(
// Scan through the matched anchors to make sure functions and profiles are
// 1:1 mapped. If the profile has already been mapped to another function
- // during previous fuzzy matching, create a new profile with the same sample
- // counts and assumed to be pre-inlined.
+ // during previous fuzzy matching, create a new profile the same as the
+ // flattened profile as a top-level function.
for (const auto &IR : IRAnchors) {
bool ProfileConflicted = false;
- const auto &Loc = IR.first;
- Function *Callee = M.getFunction(IR.second.stringRef());
- if (!Callee)
+ const auto &IRLoc = IR.first;
+ Function *IRFunc = M.getFunction(IR.second.stringRef());
+ if (!IRFunc)
continue;
- FunctionId ProfAnchor;
- auto AnchorLoc = MatchedAnchors.find(Loc);
- if (AnchorLoc == MatchedAnchors.end()) {
+ FunctionId ProfId;
+ auto MatchedLoc = MatchedAnchors.find(IRLoc);
+ if (MatchedLoc == MatchedAnchors.end()) {
// Search within the module and find if we have conflicts in pre-matched
// profiles for this anchor
- auto PreMatched = FuncToProfileNameMap.find(Callee);
+ auto PreMatched = FuncToProfileNameMap.find(IRFunc);
if (PreMatched == FuncToProfileNameMap.end())
continue;
- ProfAnchor = PreMatched->second;
+ ProfId = PreMatched->second;
} else {
- const auto &Prof = ProfileAnchors.find(AnchorLoc->second);
- if (Prof == ProfileAnchors.end())
+ const auto &ProfAnchor = ProfileAnchors.find(MatchedLoc->second);
+ if (ProfAnchor == ProfileAnchors.end())
continue;
- ProfAnchor = Prof->second;
+ ProfId = ProfAnchor->second;
}
// Conflicting profile previously matched
- auto Cached = MatchedAnchorCache.find(ProfAnchor);
- if (Cached == MatchedAnchorCache.end())
- MatchedAnchorCache[ProfAnchor] = Callee;
- else if (Cached->second != Callee)
+ auto PrevMatched = ProfileNameToFuncMap.find(ProfId);
+ if (PrevMatched == ProfileNameToFuncMap.end())
+ ProfileNameToFuncMap[ProfId] = IRFunc;
+ else if (PrevMatched->second != IRFunc)
ProfileConflicted = true;
if (ProfileConflicted) {
// Create a flattened profile using the IR function name to avoid profile
// name conflicts
- const auto *FSForMatching = getFlattenedSamplesFor(ProfAnchor);
+ const auto *FSForMatching = getFlattenedSamplesFor(ProfId);
if (!FSForMatching)
- FSForMatching = Reader.getSamplesFor(ProfAnchor.stringRef());
+ FSForMatching = Reader.getSamplesFor(ProfId.stringRef());
if (!FSForMatching)
continue;
- FunctionId NewAnchor(
+ FunctionId NewProfId(
FunctionSamples::getCanonicalFnName(IR.second.stringRef()));
- auto R = FuncProfileMatchCache.find({Callee, NewAnchor});
+ auto R = FuncProfileMatchCache.find({IRFunc, NewProfId});
if (R != FuncProfileMatchCache.end() && R->second)
continue;
- FunctionSamples &NewFS = FlattenedProfiles.create(NewAnchor);
+ FunctionSamples &NewFS = FlattenedProfiles.create(NewProfId);
NewFS.merge(*FSForMatching);
- FuncToProfileNameMap[Callee] = NewAnchor;
- FuncProfileMatchCache[{Callee, NewAnchor}] = true;
+ FuncToProfileNameMap[IRFunc] = NewProfId;
+ FuncProfileMatchCache[{IRFunc, NewProfId}] = true;
// Update profile in the sample profile reader
SampleProfileMap &Profiles = Reader.getProfiles();
- SampleContext FContext(NewAnchor);
+ SampleContext FContext(NewProfId);
auto Res = Profiles.try_emplace(FContext.getHashCode(), FContext, NewFS);
FunctionSamples &FProfile = Res.first->second;
FProfile.setContext(FContext);
@@ -928,7 +928,7 @@ void SampleProfileMatcher::matchFunctionsWithoutProfileByBasename() {
continue;
FuncToProfileNameMap[OrphanFunc] = ProfId;
- MatchedAnchorCache[ProfId] = OrphanFunc;
+ ProfileNameToFuncMap[ProfId] = OrphanFunc;
if (const auto *FS = Reader.getSamplesFor(ProfId.stringRef()))
NewlyLoadedProfiles.create(FS->getFunction()).merge(*FS);
MatchCount++;
>From f07485310e7de6ebb7df37d73237455941fe77da Mon Sep 17 00:00:00 2001
From: h2h <h2h at meta.com>
Date: Mon, 29 Jun 2026 23:37:06 -0700
Subject: [PATCH 2/8] Fix inconsistent anchor matching in stale profile
matching
---
.../Transforms/IPO/SampleProfileMatcher.cpp | 2 +-
.../stale-profile-lcs-anchor-overwrite.prof | 7 ++
.../stale-profile-lcs-anchor-overwrite.ll | 118 ++++++++++++++++++
3 files changed, 126 insertions(+), 1 deletion(-)
create mode 100644 llvm/test/Transforms/SampleProfile/Inputs/stale-profile-lcs-anchor-overwrite.prof
create mode 100644 llvm/test/Transforms/SampleProfile/stale-profile-lcs-anchor-overwrite.ll
diff --git a/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp b/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
index 5aef593e561d8..ef9f45552b07f 100644
--- a/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
+++ b/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
@@ -1065,7 +1065,7 @@ bool SampleProfileMatcher::functionMatchesProfile(Function &IRFunc,
bool Matched = functionMatchesProfileHelper(IRFunc, ProfFunc);
FuncProfileMatchCache[{&IRFunc, ProfFunc}] = Matched;
if (Matched) {
- FuncToProfileNameMap[&IRFunc] = ProfFunc;
+ FuncToProfileNameMap.try_emplace(&IRFunc, ProfFunc);
LLVM_DEBUG(dbgs() << "Function:" << IRFunc.getName()
<< " matches profile:" << ProfFunc << "\n");
}
diff --git a/llvm/test/Transforms/SampleProfile/Inputs/stale-profile-lcs-anchor-overwrite.prof b/llvm/test/Transforms/SampleProfile/Inputs/stale-profile-lcs-anchor-overwrite.prof
new file mode 100644
index 0000000000000..5f9bc7be80d51
--- /dev/null
+++ b/llvm/test/Transforms/SampleProfile/Inputs/stale-profile-lcs-anchor-overwrite.prof
@@ -0,0 +1,7 @@
+_Z3fooi:29916:31
+ 349: 2 A:1 B:1
+ 350: 1 C:1
+ 6: _Z3bari:1843
+ 380: _Z3barl:323
+ 3: _Z6calleei:306
+ !CFGChecksum: 1
diff --git a/llvm/test/Transforms/SampleProfile/stale-profile-lcs-anchor-overwrite.ll b/llvm/test/Transforms/SampleProfile/stale-profile-lcs-anchor-overwrite.ll
new file mode 100644
index 0000000000000..e01447c29e3de
--- /dev/null
+++ b/llvm/test/Transforms/SampleProfile/stale-profile-lcs-anchor-overwrite.ll
@@ -0,0 +1,118 @@
+; REQUIRES: x86_64-linux
+; REQUIRES: asserts
+; RUN: llvm-profdata merge --sample --extbinary %S/Inputs/stale-profile-lcs-anchor-overwrite.prof -o %t.prof
+; RUN: opt < %s -passes=sample-profile -sample-profile-file=%t.prof --salvage-stale-profile --salvage-unused-profile -S --debug-only=sample-profile,sample-profile-matcher,sample-profile-impl 2>&1 | FileCheck %s
+
+; CHECK: Function:_Z3barv matches profile:_Z3bari
+; CHECK: The functions _Z3barv(IR) and _Z3barl(Profile) share the same base name: bar.
+; CHECK: Function:_Z3barv matches profile:_Z3barl
+; CHECK: The functions _Z3barPv(IR) and _Z3barl(Profile) share the same base name: bar.
+; CHECK: Function:_Z3barPv matches profile:_Z3barl
+
+; CHECK: Run stale profile matching for _Z3barPv
+; CHECK: The functions _Z6calleePv(IR) and _Z6calleei(Profile) share the same base name: callee.
+; CHECK: Function:_Z6calleePv matches profile:_Z6calleei
+; CHECK: Location is matched from 1 to 1
+; CHECK: Callsite with callee:_Z6calleePv is matched from 2 to 3
+; CHECK: Run stale profile matching for _Z3barv
+; CHECK: Run stale profile matching for _Z6calleePv
+; CHECK: Function processing order:
+; CHECK: _Z3foov
+; CHECK: _Z3barPv
+; CHECK: _Z6calleePv
+; CHECK: _Z3barv
+
+
+target triple = "x86_64-linux-gnu"
+
+define dso_local noundef ptr @_Z6calleePv(ptr noundef %ptr) #0 !dbg !15 {
+entry:
+ %ptr.addr = alloca ptr, align 8
+ store ptr %ptr, ptr %ptr.addr, align 8
+ #dbg_declare(ptr %ptr.addr, !20, !DIExpression(), !21)
+ call void @llvm.pseudoprobe(i64 7108221232740920931, i64 1, i32 0, i64 -1), !dbg !22
+ ret ptr null, !dbg !22
+}
+
+define dso_local void @_Z3barv() #0 !dbg !23 {
+entry:
+ call void @llvm.pseudoprobe(i64 -1069303473483922844, i64 1, i32 0, i64 -1), !dbg !24
+ ret void, !dbg !24
+}
+
+define dso_local noundef ptr @_Z3barPv(ptr noundef %ptr) #0 !dbg !25 {
+entry:
+ %ptr.addr = alloca ptr, align 8
+ store ptr %ptr, ptr %ptr.addr, align 8
+ #dbg_declare(ptr %ptr.addr, !26, !DIExpression(), !27)
+ call void @llvm.pseudoprobe(i64 5678655469166311522, i64 1, i32 0, i64 -1), !dbg !28
+ %call = call noundef ptr @_Z6calleePv(ptr noundef null), !dbg !29
+ ret ptr %call, !dbg !31
+}
+
+define dso_local noundef ptr @_Z3foov() #0 !dbg !32 {
+entry:
+ call void @llvm.pseudoprobe(i64 9191153033785521275, i64 1, i32 0, i64 -1), !dbg !35
+ call void null(), !dbg !36
+ call void @_Z3barv(), !dbg !38
+ call void null(), !dbg !40
+ %call = call noundef ptr @_Z3barPv(ptr noundef null), !dbg !42
+ ret ptr %call, !dbg !44
+}
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite)
+declare void @llvm.pseudoprobe(i64, i64, i32, i64) #1
+
+attributes #0 = { "use-sample-profile" }
+attributes #1 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!6, !7, !8, !9}
+!llvm.ident = !{!10}
+!llvm.pseudo_probe_desc = !{!11, !12, !13, !14}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !2, splitDebugInlining: false, debugInfoForProfiling: true, nameTableKind: None)
+!1 = !DIFile(filename: "test.cc", directory: "/tmp", checksumkind: CSK_MD5, checksum: "e16862f6a655f30cd332532a91f867b6")
+!2 = !{!3}
+!3 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !4, size: 64)
+!4 = !DISubroutineType(types: !5)
+!5 = !{null}
+!6 = !{i32 7, !"Dwarf Version", i32 5}
+!7 = !{i32 2, !"Debug Info Version", i32 3}
+!8 = !{i32 7, !"uwtable", i32 2}
+!9 = !{i32 7, !"frame-pointer", i32 2}
+!10 = !{!"clang"}
+!11 = !{i64 7108221232740920931, i64 4294967295, !"_Z6calleePv"}
+!12 = !{i64 -1069303473483922844, i64 4294967295, !"_Z3barv"}
+!13 = !{i64 5678655469166311522, i64 281479271677951, !"_Z3barPv"}
+!14 = !{i64 9191153033785521275, i64 1125904201809919, !"_Z3foov"}
+!15 = distinct !DISubprogram(name: "callee", linkageName: "_Z6calleePv", scope: !1, file: !1, line: 1, type: !16, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !19)
+!16 = !DISubroutineType(types: !17)
+!17 = !{!18, !18}
+!18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
+!19 = !{}
+!20 = !DILocalVariable(name: "ptr", arg: 1, scope: !15, file: !1, line: 1, type: !18)
+!21 = !DILocation(line: 1, column: 20, scope: !15)
+!22 = !DILocation(line: 2, column: 5, scope: !15)
+!23 = distinct !DISubprogram(name: "bar", linkageName: "_Z3barv", scope: !1, file: !1, line: 5, type: !4, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!24 = !DILocation(line: 5, column: 13, scope: !23)
+!25 = distinct !DISubprogram(name: "bar", linkageName: "_Z3barPv", scope: !1, file: !1, line: 7, type: !16, scopeLine: 7, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !19)
+!26 = !DILocalVariable(name: "ptr", arg: 1, scope: !25, file: !1, line: 7, type: !18)
+!27 = !DILocation(line: 7, column: 17, scope: !25)
+!28 = !DILocation(line: 8, column: 12, scope: !25)
+!29 = !DILocation(line: 8, column: 12, scope: !30)
+!30 = !DILexicalBlockFile(scope: !25, file: !1, discriminator: 455082007)
+!31 = !DILocation(line: 8, column: 5, scope: !25)
+!32 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 11, type: !33, scopeLine: 11, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!33 = !DISubroutineType(types: !34)
+!34 = !{!18}
+!35 = !DILocation(line: 12, column: 5, scope: !32)
+!36 = !DILocation(line: 12, column: 5, scope: !37)
+!37 = !DILexicalBlockFile(scope: !32, file: !1, discriminator: 387973143)
+!38 = !DILocation(line: 13, column: 5, scope: !39)
+!39 = !DILexicalBlockFile(scope: !32, file: !1, discriminator: 455082015)
+!40 = !DILocation(line: 14, column: 5, scope: !41)
+!41 = !DILexicalBlockFile(scope: !32, file: !1, discriminator: 387973159)
+!42 = !DILocation(line: 15, column: 12, scope: !43)
+!43 = !DILexicalBlockFile(scope: !32, file: !1, discriminator: 455082031)
+!44 = !DILocation(line: 15, column: 5, scope: !32)
>From 2a8aa932aa5c1f8c7a898b691743320354b40da9 Mon Sep 17 00:00:00 2001
From: h2h <h2h at meta.com>
Date: Sun, 5 Jul 2026 22:32:39 -0700
Subject: [PATCH 3/8] Move FuncToProfileNameMap outside of LCS matching
---
.../Transforms/IPO/SampleProfileMatcher.cpp | 12 ++++++------
.../stale-profile-lcs-anchor-overwrite.ll | 19 ++++++++++++++++---
2 files changed, 22 insertions(+), 9 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp b/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
index ef9f45552b07f..d5677f1ee1784 100644
--- a/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
+++ b/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
@@ -418,6 +418,10 @@ void SampleProfileMatcher::runStaleProfileMatching(
if (ProfAnchor == ProfileAnchors.end())
continue;
ProfId = ProfAnchor->second;
+ FuncProfileMatchCache[{IRFunc, ProfId}] = true;
+ FuncToProfileNameMap[Callee] = ProfId;
+ LLVM_DEBUG(dbgs() << "Function:" << IRFunc->getName()
+ << " matches profile:" << ProfId << "\n");
}
// Conflicting profile previously matched
@@ -1063,12 +1067,8 @@ bool SampleProfileMatcher::functionMatchesProfile(Function &IRFunc,
return false;
bool Matched = functionMatchesProfileHelper(IRFunc, ProfFunc);
- FuncProfileMatchCache[{&IRFunc, ProfFunc}] = Matched;
- if (Matched) {
- FuncToProfileNameMap.try_emplace(&IRFunc, ProfFunc);
- LLVM_DEBUG(dbgs() << "Function:" << IRFunc.getName()
- << " matches profile:" << ProfFunc << "\n");
- }
+ if (!Matched)
+ FuncProfileMatchCache[{&IRFunc, ProfFunc}] = Matched;
return Matched;
}
diff --git a/llvm/test/Transforms/SampleProfile/stale-profile-lcs-anchor-overwrite.ll b/llvm/test/Transforms/SampleProfile/stale-profile-lcs-anchor-overwrite.ll
index e01447c29e3de..b0d6694312178 100644
--- a/llvm/test/Transforms/SampleProfile/stale-profile-lcs-anchor-overwrite.ll
+++ b/llvm/test/Transforms/SampleProfile/stale-profile-lcs-anchor-overwrite.ll
@@ -3,12 +3,25 @@
; RUN: llvm-profdata merge --sample --extbinary %S/Inputs/stale-profile-lcs-anchor-overwrite.prof -o %t.prof
; RUN: opt < %s -passes=sample-profile -sample-profile-file=%t.prof --salvage-stale-profile --salvage-unused-profile -S --debug-only=sample-profile,sample-profile-matcher,sample-profile-impl 2>&1 | FileCheck %s
-; CHECK: Function:_Z3barv matches profile:_Z3bari
+; CHECK: Function _Z6calleePv is not in profile or profile symbol list.
+; CHECK: Function _Z3barv is not in profile or profile symbol list.
+; CHECK: Function _Z3barPv is not in profile or profile symbol list.
+; CHECK: Function _Z3foov is not in profile or profile symbol list.
+; CHECK: Direct basename match: _Z6calleePv (IR) -> _Z6calleei (Profile) [basename: callee]
+; CHECK: Direct basename match: _Z3foov (IR) -> _Z3fooi (Profile) [basename: foo]
+; CHECK: Direct basename matching found 2 matches
+; CHECK: Run stale profile matching for _Z3foov
+; CHECK: The functions _Z3barv(IR) and _Z3bari(Profile) share the same base name: bar.
; CHECK: The functions _Z3barv(IR) and _Z3barl(Profile) share the same base name: bar.
-; CHECK: Function:_Z3barv matches profile:_Z3barl
; CHECK: The functions _Z3barPv(IR) and _Z3barl(Profile) share the same base name: bar.
+; CHECK: Function:_Z3barv matches profile:_Z3bari
; CHECK: Function:_Z3barPv matches profile:_Z3barl
-
+; CHECK: Location is matched from 1 to 1
+; CHECK: Location is matched from 2 to 2
+; CHECK: Callsite with callee:_Z3barv is matched from 3 to 6
+; CHECK: Location is rematched backwards from 2 to 5
+; CHECK: Callsite with callee:unknown.indirect.callee is matched from 4 to 349
+; CHECK: Callsite with callee:_Z3barPv is matched from 5 to 380
; CHECK: Run stale profile matching for _Z3barPv
; CHECK: The functions _Z6calleePv(IR) and _Z6calleei(Profile) share the same base name: callee.
; CHECK: Function:_Z6calleePv matches profile:_Z6calleei
>From e1261de7f1c21d76b0f045569895fe8400f896c3 Mon Sep 17 00:00:00 2001
From: h2h <h2h at meta.com>
Date: Mon, 6 Jul 2026 16:21:35 -0700
Subject: [PATCH 4/8] Test fixes
---
.../Transforms/IPO/SampleProfileMatcher.cpp | 18 ++++++++---
...o-probe-stale-profile-orphan-conflict.prof | 1 +
...udo-probe-stale-profile-orphan-conflict.ll | 32 ++++++-------------
3 files changed, 23 insertions(+), 28 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp b/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
index d5677f1ee1784..f3c05b1d5acba 100644
--- a/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
+++ b/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
@@ -418,10 +418,12 @@ void SampleProfileMatcher::runStaleProfileMatching(
if (ProfAnchor == ProfileAnchors.end())
continue;
ProfId = ProfAnchor->second;
- FuncProfileMatchCache[{IRFunc, ProfId}] = true;
- FuncToProfileNameMap[Callee] = ProfId;
- LLVM_DEBUG(dbgs() << "Function:" << IRFunc->getName()
- << " matches profile:" << ProfId << "\n");
+ if (IRFunc->getName() != ProfId.stringRef()) {
+ FuncProfileMatchCache[{IRFunc, ProfId}] = true;
+ FuncToProfileNameMap[Callee] = ProfId;
+ LLVM_DEBUG(dbgs() << "Function:" << IRFunc->getName()
+ << " matches profile:" << ProfId << "\n");
+ }
}
// Conflicting profile previously matched
@@ -448,8 +450,14 @@ void SampleProfileMatcher::runStaleProfileMatching(
FunctionSamples &NewFS = FlattenedProfiles.create(NewProfId);
NewFS.merge(*FSForMatching);
FuncToProfileNameMap[IRFunc] = NewProfId;
+ FuncProfileMatchCache[{IRFunc, ProfId}] = false;
FuncProfileMatchCache[{IRFunc, NewProfId}] = true;
+ LLVM_DEBUG(dbgs() << "Function:" << Callee->getName()
+ << " encounters conflicting profile matchings, "
+ "remapping to new profile:"
+ << NewAnchor << "\n");
+
// Update profile in the sample profile reader
SampleProfileMap &Profiles = Reader.getProfiles();
SampleContext FContext(NewProfId);
@@ -717,7 +725,7 @@ void SampleProfileMatcher::computeAndReportProfileStaleness() {
}
// Count profile mismatches for profile staleness report.
- for (const auto &F : M) {
+ for (auto &F : M) {
if (skipProfileForFunction(F))
continue;
// As the stats will be merged by linker, skip reporting the metrics for
diff --git a/llvm/test/Transforms/SampleProfile/Inputs/pseudo-probe-stale-profile-orphan-conflict.prof b/llvm/test/Transforms/SampleProfile/Inputs/pseudo-probe-stale-profile-orphan-conflict.prof
index 4aae1f1f38b3a..bdb08a13db59b 100644
--- a/llvm/test/Transforms/SampleProfile/Inputs/pseudo-probe-stale-profile-orphan-conflict.prof
+++ b/llvm/test/Transforms/SampleProfile/Inputs/pseudo-probe-stale-profile-orphan-conflict.prof
@@ -1,4 +1,5 @@
_Z3fooi:1:0
+ 30: _Z3topi:1
57: _Z3bari:1
72: _Z3topi:1
2: _Z3midi:1
diff --git a/llvm/test/Transforms/SampleProfile/pseudo-probe-stale-profile-orphan-conflict.ll b/llvm/test/Transforms/SampleProfile/pseudo-probe-stale-profile-orphan-conflict.ll
index d7886cc72dc8f..f33eac6249db3 100644
--- a/llvm/test/Transforms/SampleProfile/pseudo-probe-stale-profile-orphan-conflict.ll
+++ b/llvm/test/Transforms/SampleProfile/pseudo-probe-stale-profile-orphan-conflict.ll
@@ -1,6 +1,6 @@
; Test direct basename matching for orphan functions where multiple callee anchors may be
; matched to one same profile during stale profile matching. In this test case, both of the
-; `mid` functions will be matched to the same `_Z3midi` function in the profile during stale
+; `top` functions will be matched to the same `_Z3topi` function in the profile during stale
; profile matching. This ends up causing an assertation error because only one profile
; function is supposed to be matched to an IR function.
;
@@ -10,21 +10,14 @@
; |_ sub
;
; Profile Function:
-; foo: bar ; top ; mid
-; |_ sub
+; foo: top; bar ; top
+; |_ mid
+; |_ sub
;
; Stale profile match order:
; foo: top<ir> ; bar<ir> ; top(2)<ir>
; | | |
; top<prof> ; bar<prof> ; top<prof>
-;
-; top(2)<ir>: mid(2)<ir>
-; |
-; mid<prof>
-;
-; top<ir>: mid<ir>
-; |
-; mid<prof> => (Assertation error)
; REQUIRES: x86_64-linux
@@ -32,23 +25,15 @@
; RUN: llvm-profdata merge --sample --extbinary %S/Inputs/pseudo-probe-stale-profile-orphan-conflict.prof -o %t.prof
; RUN: opt < %s -passes=sample-profile -sample-profile-file=%t.prof --salvage-stale-profile --salvage-unused-profile -S --debug-only=sample-profile,sample-profile-matcher,sample-profile-impl 2>&1 | FileCheck %s
-; CHECK: Function _Z3midl is not in profile or profile symbol list.
-; CHECK: Function _Z3midll is not in profile or profile symbol list.
-; CHECK: Function _Z3topl is not in profile or profile symbol list.
-; CHECK: Function _Z3barl is not in profile or profile symbol list.
-; CHECK: Function _Z3topll is not in profile or profile symbol list.
-; CHECK: Function _Z3fool is not in profile or profile symbol list.
-; CHECK: Direct basename match: _Z3barl (IR) -> _Z3bari (Profile) [basename: bar]
-; CHECK: Direct basename match: _Z3fool (IR) -> _Z3fooi (Profile) [basename: foo]
-; CHECK: Direct basename matching found 2 matches
; CHECK: Run stale profile matching for _Z3fool
; CHECK: The functions _Z3topl(IR) and _Z3topi(Profile) share the same base name: top.
-; CHECK: Function:_Z3topl matches profile:_Z3topi
; CHECK: The functions _Z3barl(IR) and _Z3bari(Profile) share the same base name: bar.
-; CHECK: Function:_Z3barl matches profile:_Z3bari
; CHECK: The functions _Z3topll(IR) and _Z3topi(Profile) share the same base name: top.
+; CHECK: Function:_Z3topl matches profile:_Z3topi
+; CHECK: Function:_Z3barl matches profile:_Z3bari
; CHECK: Function:_Z3topll matches profile:_Z3topi
-; CHECK: Location is matched from 2 to 2
+; CHECK: Function:_Z3topll encounters conflicting profile matchings, remapping to new profile:_Z3topll
+; CHECK: Callsite with callee:_Z3topl is matched from 2 to 30
; CHECK: Callsite with callee:_Z3barl is matched from 3 to 57
; CHECK: Callsite with callee:_Z3topll is matched from 4 to 72
; CHECK: Run stale profile matching for _Z3topll
@@ -59,6 +44,7 @@
; CHECK: Run stale profile matching for _Z3topl
; CHECK: The functions _Z3midl(IR) and _Z3midi(Profile) share the same base name: mid.
; CHECK: Function:_Z3midl matches profile:_Z3midi
+; CHECK: Function:_Z3midl encounters conflicting profile matchings, remapping to new profile:_Z3midl
; CHECK: Callsite with callee:_Z3midl is matched from 1 to 2
; CHECK: Run stale profile matching for _Z3midll
; CHECK: Callsite with callee:_Z3subi is matched from 1 to 11
>From dd52337287ecfd3c17805a1644feb5803d3c5d29 Mon Sep 17 00:00:00 2001
From: h2h <h2h at meta.com>
Date: Mon, 6 Jul 2026 16:46:41 -0700
Subject: [PATCH 5/8] revert unnecessary changes
---
llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp b/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
index f3c05b1d5acba..6d1e3a2aa7074 100644
--- a/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
+++ b/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
@@ -725,7 +725,7 @@ void SampleProfileMatcher::computeAndReportProfileStaleness() {
}
// Count profile mismatches for profile staleness report.
- for (auto &F : M) {
+ for (const auto &F : M) {
if (skipProfileForFunction(F))
continue;
// As the stats will be merged by linker, skip reporting the metrics for
>From da58ad112c6ad4875768cfb179816bd382175966 Mon Sep 17 00:00:00 2001
From: h2h <h2h at meta.com>
Date: Mon, 20 Jul 2026 10:55:17 -0700
Subject: [PATCH 6/8] Move basename matching after CG matching as a fallback
---
.../Transforms/IPO/SampleProfileMatcher.h | 1 +
.../Transforms/IPO/SampleProfileMatcher.cpp | 52 +++++++++++++------
...udo-probe-stale-profile-orphan-conflict.ll | 1 -
.../stale-profile-lcs-anchor-overwrite.ll | 1 -
4 files changed, 36 insertions(+), 19 deletions(-)
diff --git a/llvm/include/llvm/Transforms/IPO/SampleProfileMatcher.h b/llvm/include/llvm/Transforms/IPO/SampleProfileMatcher.h
index 251d5bc7e479b..955495edc5363 100644
--- a/llvm/include/llvm/Transforms/IPO/SampleProfileMatcher.h
+++ b/llvm/include/llvm/Transforms/IPO/SampleProfileMatcher.h
@@ -69,6 +69,7 @@ class SampleProfileMatcher {
// the new(renamed) function pointer and the value is old(unused) profile
// name.
MapVector<Function *, FunctionId> FuncToProfileNameMap;
+ MapVector<Function *, FunctionId> FuncToProfileNameMapByBase;
// Mapping from matched(renamed) profile name to IR function to during call
// graph matching. This is a reversed FuncToProfileNameMap to track duplicated
// profile matching and resolve multiple IR functions being mapped to a single
diff --git a/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp b/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
index 6d1e3a2aa7074..f17960599107e 100644
--- a/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
+++ b/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
@@ -420,7 +420,7 @@ void SampleProfileMatcher::runStaleProfileMatching(
ProfId = ProfAnchor->second;
if (IRFunc->getName() != ProfId.stringRef()) {
FuncProfileMatchCache[{IRFunc, ProfId}] = true;
- FuncToProfileNameMap[Callee] = ProfId;
+ FuncToProfileNameMap[IRFunc] = ProfId;
LLVM_DEBUG(dbgs() << "Function:" << IRFunc->getName()
<< " matches profile:" << ProfId << "\n");
}
@@ -453,10 +453,10 @@ void SampleProfileMatcher::runStaleProfileMatching(
FuncProfileMatchCache[{IRFunc, ProfId}] = false;
FuncProfileMatchCache[{IRFunc, NewProfId}] = true;
- LLVM_DEBUG(dbgs() << "Function:" << Callee->getName()
+ LLVM_DEBUG(dbgs() << "Function:" << IRFunc->getName()
<< " encounters conflicting profile matchings, "
"remapping to new profile:"
- << NewAnchor << "\n");
+ << NewProfId << "\n");
// Update profile in the sample profile reader
SampleProfileMap &Profiles = Reader.getProfiles();
@@ -941,6 +941,8 @@ void SampleProfileMatcher::matchFunctionsWithoutProfileByBasename() {
FuncToProfileNameMap[OrphanFunc] = ProfId;
ProfileNameToFuncMap[ProfId] = OrphanFunc;
+ FuncProfileMatchCache[{OrphanFunc, ProfId}] = true;
+
if (const auto *FS = Reader.getSamplesFor(ProfId.stringRef()))
NewlyLoadedProfiles.create(FS->getFunction()).merge(*FS);
MatchCount++;
@@ -966,18 +968,6 @@ bool SampleProfileMatcher::functionMatchesProfileHelper(
// two sequences are.
float Similarity = 0.0;
- // Match the functions if they have the same base name(after demangling) and
- // skip the similarity check.
- ItaniumPartialDemangler Demangler;
- auto IRBaseName = getDemangledBaseName(Demangler, IRFunc.getName());
- auto ProfBaseName = getDemangledBaseName(Demangler, ProfFunc.stringRef());
- if (!IRBaseName.empty() && IRBaseName == ProfBaseName) {
- LLVM_DEBUG(dbgs() << "The functions " << IRFunc.getName() << "(IR) and "
- << ProfFunc << "(Profile) share the same base name: "
- << IRBaseName << ".\n");
- return true;
- }
-
const auto *FSForMatching = getFlattenedSamplesFor(ProfFunc);
// With extbinary profile format, initial profile loading only reads profile
// based on current function names in the module.
@@ -1075,8 +1065,21 @@ bool SampleProfileMatcher::functionMatchesProfile(Function &IRFunc,
return false;
bool Matched = functionMatchesProfileHelper(IRFunc, ProfFunc);
- if (!Matched)
+ if (!Matched) {
+ // Match the functions if they have the same base name(after demangling) and
+ // skip the similarity check.
+ ItaniumPartialDemangler Demangler;
+ auto IRBaseName = getDemangledBaseName(Demangler, IRFunc.getName());
+ auto ProfBaseName = getDemangledBaseName(Demangler, ProfFunc.stringRef());
+ if (!IRBaseName.empty() && IRBaseName == ProfBaseName) {
+ LLVM_DEBUG(dbgs() << "The functions " << IRFunc.getName() << "(IR) and "
+ << ProfFunc << "(Profile) share the same base name: "
+ << IRBaseName << ".\n");
+ return true;
+ }
+ // Cache IR/Prof function pairs that don't match
FuncProfileMatchCache[{&IRFunc, ProfFunc}] = Matched;
+ }
return Matched;
}
@@ -1119,11 +1122,11 @@ void SampleProfileMatcher::runOnModule() {
if (SalvageUnusedProfile) {
findFunctionsWithoutProfile();
- matchFunctionsWithoutProfileByBasename();
}
// Process the matching in top-down order so that the caller matching result
// can be used to the callee matching.
+ DenseSet<Function *> FunctionProcessedInStage1;
std::vector<Function *> TopDownFunctionList;
TopDownFunctionList.reserve(M.size());
buildTopDownFuncOrder(CG, TopDownFunctionList);
@@ -1131,6 +1134,21 @@ void SampleProfileMatcher::runOnModule() {
if (skipProfileForFunction(*F))
continue;
runOnFunction(*F);
+ if (getFlattenedSamplesFor(*F) || FuncToProfileNameMap.contains(F))
+ FunctionProcessedInStage1.insert(F);
+ }
+
+ // Stage 2 stale profile matching after rematching all the remaining functions
+ // without profile with fuzzy basename matching
+ if (SalvageUnusedProfile) {
+ matchFunctionsWithoutProfileByBasename();
+ for (auto *F : TopDownFunctionList) {
+ if (skipProfileForFunction(*F))
+ continue;
+ if (FunctionProcessedInStage1.contains(F))
+ continue;
+ runOnFunction(*F);
+ }
}
if (SalvageUnusedProfile)
diff --git a/llvm/test/Transforms/SampleProfile/pseudo-probe-stale-profile-orphan-conflict.ll b/llvm/test/Transforms/SampleProfile/pseudo-probe-stale-profile-orphan-conflict.ll
index f33eac6249db3..42036ad2856e9 100644
--- a/llvm/test/Transforms/SampleProfile/pseudo-probe-stale-profile-orphan-conflict.ll
+++ b/llvm/test/Transforms/SampleProfile/pseudo-probe-stale-profile-orphan-conflict.ll
@@ -27,7 +27,6 @@
; CHECK: Run stale profile matching for _Z3fool
; CHECK: The functions _Z3topl(IR) and _Z3topi(Profile) share the same base name: top.
-; CHECK: The functions _Z3barl(IR) and _Z3bari(Profile) share the same base name: bar.
; CHECK: The functions _Z3topll(IR) and _Z3topi(Profile) share the same base name: top.
; CHECK: Function:_Z3topl matches profile:_Z3topi
; CHECK: Function:_Z3barl matches profile:_Z3bari
diff --git a/llvm/test/Transforms/SampleProfile/stale-profile-lcs-anchor-overwrite.ll b/llvm/test/Transforms/SampleProfile/stale-profile-lcs-anchor-overwrite.ll
index b0d6694312178..4316848283d68 100644
--- a/llvm/test/Transforms/SampleProfile/stale-profile-lcs-anchor-overwrite.ll
+++ b/llvm/test/Transforms/SampleProfile/stale-profile-lcs-anchor-overwrite.ll
@@ -23,7 +23,6 @@
; CHECK: Callsite with callee:unknown.indirect.callee is matched from 4 to 349
; CHECK: Callsite with callee:_Z3barPv is matched from 5 to 380
; CHECK: Run stale profile matching for _Z3barPv
-; CHECK: The functions _Z6calleePv(IR) and _Z6calleei(Profile) share the same base name: callee.
; CHECK: Function:_Z6calleePv matches profile:_Z6calleei
; CHECK: Location is matched from 1 to 1
; CHECK: Callsite with callee:_Z6calleePv is matched from 2 to 3
>From 232a1920aed847860bb6155b1856c6bb4b213f00 Mon Sep 17 00:00:00 2001
From: h2h <h2h at meta.com>
Date: Mon, 20 Jul 2026 11:34:50 -0700
Subject: [PATCH 7/8] remove unused member variable
---
llvm/include/llvm/Transforms/IPO/SampleProfileMatcher.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/llvm/include/llvm/Transforms/IPO/SampleProfileMatcher.h b/llvm/include/llvm/Transforms/IPO/SampleProfileMatcher.h
index 955495edc5363..251d5bc7e479b 100644
--- a/llvm/include/llvm/Transforms/IPO/SampleProfileMatcher.h
+++ b/llvm/include/llvm/Transforms/IPO/SampleProfileMatcher.h
@@ -69,7 +69,6 @@ class SampleProfileMatcher {
// the new(renamed) function pointer and the value is old(unused) profile
// name.
MapVector<Function *, FunctionId> FuncToProfileNameMap;
- MapVector<Function *, FunctionId> FuncToProfileNameMapByBase;
// Mapping from matched(renamed) profile name to IR function to during call
// graph matching. This is a reversed FuncToProfileNameMap to track duplicated
// profile matching and resolve multiple IR functions being mapped to a single
>From 3fdea6602a5c96a291c3ad9822a336ea99983553 Mon Sep 17 00:00:00 2001
From: h2h <h2h at meta.com>
Date: Wed, 30 Sep 2026 21:30:42 -0700
Subject: [PATCH 8/8] refactor & fixings
---
.../Transforms/IPO/SampleProfileMatcher.h | 9 ++
.../Transforms/IPO/SampleProfileMatcher.cpp | 141 ++++++++++--------
...e-profile-orphan-conflict-cross-stage.prof | 11 ++
...file-lcs-anchor-overwrite-cross-stage.prof | 11 ++
...udo-probe-stale-profile-orphan-conflict.ll | 38 +++--
.../stale-profile-lcs-anchor-overwrite.ll | 27 +++-
6 files changed, 160 insertions(+), 77 deletions(-)
create mode 100644 llvm/test/Transforms/SampleProfile/Inputs/pseudo-probe-stale-profile-orphan-conflict-cross-stage.prof
create mode 100644 llvm/test/Transforms/SampleProfile/Inputs/stale-profile-lcs-anchor-overwrite-cross-stage.prof
diff --git a/llvm/include/llvm/Transforms/IPO/SampleProfileMatcher.h b/llvm/include/llvm/Transforms/IPO/SampleProfileMatcher.h
index 251d5bc7e479b..1c387135a6668 100644
--- a/llvm/include/llvm/Transforms/IPO/SampleProfileMatcher.h
+++ b/llvm/include/llvm/Transforms/IPO/SampleProfileMatcher.h
@@ -235,6 +235,15 @@ class SampleProfileMatcher {
// which are supposed to be new functions. We use them as the targets for
// call graph matching.
void findFunctionsWithoutProfile();
+ // Setter function for recording stale profile matching results into
+ // `FuncToProfileNameMap` and `ProfileNameToFuncMap`. The IR function and
+ // Profile function are checked and guaranteed to be 1:1 mapping. A new
+ // profile clone will be created in the SampleReader if multiple IR
+ // functions are found to be mapping to one Profile. Return the updated
+ // profile name that is mapped to the IR function. And update
+ // FuncProfileMatchCache correspondingly.
+ FunctionId recordMatchedStaleProfile(Function *IRFunc,
+ FunctionId ProfFuncName);
// Match orphan IR functions to unused top-level profile entries by demangled
// basename, without requiring a matched caller in the call graph.
void matchFunctionsWithoutProfileByBasename();
diff --git a/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp b/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
index f17960599107e..7aa16f3415186 100644
--- a/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
+++ b/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
@@ -406,64 +406,28 @@ void SampleProfileMatcher::runStaleProfileMatching(
continue;
FunctionId ProfId;
auto MatchedLoc = MatchedAnchors.find(IRLoc);
- if (MatchedLoc == MatchedAnchors.end()) {
- // Search within the module and find if we have conflicts in pre-matched
- // profiles for this anchor
- auto PreMatched = FuncToProfileNameMap.find(IRFunc);
- if (PreMatched == FuncToProfileNameMap.end())
- continue;
- ProfId = PreMatched->second;
- } else {
- const auto &ProfAnchor = ProfileAnchors.find(MatchedLoc->second);
- if (ProfAnchor == ProfileAnchors.end())
- continue;
- ProfId = ProfAnchor->second;
- if (IRFunc->getName() != ProfId.stringRef()) {
- FuncProfileMatchCache[{IRFunc, ProfId}] = true;
- FuncToProfileNameMap[IRFunc] = ProfId;
- LLVM_DEBUG(dbgs() << "Function:" << IRFunc->getName()
- << " matches profile:" << ProfId << "\n");
- }
- }
-
- // Conflicting profile previously matched
- auto PrevMatched = ProfileNameToFuncMap.find(ProfId);
- if (PrevMatched == ProfileNameToFuncMap.end())
- ProfileNameToFuncMap[ProfId] = IRFunc;
- else if (PrevMatched->second != IRFunc)
- ProfileConflicted = true;
-
- if (ProfileConflicted) {
- // Create a flattened profile using the IR function name to avoid profile
- // name conflicts
- const auto *FSForMatching = getFlattenedSamplesFor(ProfId);
- if (!FSForMatching)
- FSForMatching = Reader.getSamplesFor(ProfId.stringRef());
- if (!FSForMatching)
- continue;
+ if (MatchedLoc == MatchedAnchors.end())
+ continue;
- FunctionId NewProfId(
- FunctionSamples::getCanonicalFnName(IR.second.stringRef()));
- auto R = FuncProfileMatchCache.find({IRFunc, NewProfId});
- if (R != FuncProfileMatchCache.end() && R->second)
- continue;
- FunctionSamples &NewFS = FlattenedProfiles.create(NewProfId);
- NewFS.merge(*FSForMatching);
- FuncToProfileNameMap[IRFunc] = NewProfId;
- FuncProfileMatchCache[{IRFunc, ProfId}] = false;
- FuncProfileMatchCache[{IRFunc, NewProfId}] = true;
-
- LLVM_DEBUG(dbgs() << "Function:" << IRFunc->getName()
- << " encounters conflicting profile matchings, "
- "remapping to new profile:"
- << NewProfId << "\n");
-
- // Update profile in the sample profile reader
- SampleProfileMap &Profiles = Reader.getProfiles();
- SampleContext FContext(NewProfId);
- auto Res = Profiles.try_emplace(FContext.getHashCode(), FContext, NewFS);
- FunctionSamples &FProfile = Res.first->second;
- FProfile.setContext(FContext);
+ const auto &ProfAnchor = ProfileAnchors.find(MatchedLoc->second);
+ if (ProfAnchor == ProfileAnchors.end())
+ continue;
+ ProfId = recordMatchedStaleProfile(IRFunc, ProfAnchor->second);
+ if (ProfId != ProfAnchor->second) {
+ // Only load the flattened profile if a new profile is created in the
+ // SampleProfReader
+ FunctionSamples &NewFS = FlattenedProfiles.create(ProfId);
+ SampleProfileMap UpdatedProfile;
+ if (const auto *FS = Reader.getSamplesFor(ProfId.stringRef())) {
+ UpdatedProfile.create(ProfId).merge(*FS);
+ ProfileConverter::flattenProfile(UpdatedProfile,
+ FunctionSamples::ProfileIsCS);
+ auto It = UpdatedProfile.find(ProfId);
+ if (It != UpdatedProfile.end()) {
+ FunctionSamples &NewFlatFS = FlattenedProfiles.create(ProfId);
+ NewFlatFS.merge(It->second);
+ }
+ }
}
}
@@ -844,6 +808,62 @@ void SampleProfileMatcher::findFunctionsWithoutProfile() {
}
}
+FunctionId
+SampleProfileMatcher::recordMatchedStaleProfile(Function *IRFunc,
+ FunctionId ProfFuncName) {
+ // Early return if an IR function has already been matched
+ StringRef IRFuncName = FunctionSamples::getCanonicalFnName(IRFunc->getName());
+ if (IRFuncName == ProfFuncName.stringRef())
+ return ProfFuncName;
+ auto PreMatched = FuncToProfileNameMap.find(IRFunc);
+ if (PreMatched != FuncToProfileNameMap.end())
+ return ProfFuncName;
+
+ // Detect conflicting profile previously matched
+ auto PrevMatched = ProfileNameToFuncMap.find(ProfFuncName);
+ if (PrevMatched == ProfileNameToFuncMap.end()) {
+ FuncProfileMatchCache[{IRFunc, ProfFuncName}] = true;
+ FuncToProfileNameMap[IRFunc] = ProfFuncName;
+ ProfileNameToFuncMap[ProfFuncName] = IRFunc;
+
+ LLVM_DEBUG(dbgs() << "Function:" << IRFunc->getName()
+ << " matches profile:" << ProfFuncName << "\n");
+
+ } else if (PrevMatched->second != IRFunc) {
+ // Create a flattened profile using the IR function name to avoid profile
+ // name conflicts
+ const auto *FSForMatching = getFlattenedSamplesFor(ProfFuncName);
+ if (!FSForMatching)
+ FSForMatching = Reader.getSamplesFor(ProfFuncName.stringRef());
+ if (!FSForMatching)
+ return ProfFuncName;
+
+ FunctionId NewProfId(IRFuncName);
+ auto R = FuncProfileMatchCache.find({IRFunc, NewProfId});
+ if (R != FuncProfileMatchCache.end() && R->second)
+ return ProfFuncName;
+
+ // Update profile in the sample profile reader
+ SampleProfileMap &Profiles = Reader.getProfiles();
+ SampleContext FContext(NewProfId);
+ auto Res = Profiles.try_emplace(FContext.getHashCode(), FContext,
+ FunctionSamples());
+ FunctionSamples &NewFS = Res.first->second;
+ NewFS.setContext(FContext);
+ NewFS.merge(*FSForMatching);
+
+ FuncProfileMatchCache[{IRFunc, ProfFuncName}] = false;
+
+ LLVM_DEBUG(dbgs() << "Function:" << IRFunc->getName()
+ << " encounters conflicting profile matchings, "
+ "remapping to new profile:"
+ << NewProfId << "\n");
+
+ return NewProfId;
+ }
+ return ProfFuncName;
+}
+
// Demangle \p FName and return the base function name (stripping namespaces,
// templates, and parameter types). Returns an empty string on failure.
static std::string getDemangledBaseName(ItaniumPartialDemangler &Demangler,
@@ -882,6 +902,9 @@ void SampleProfileMatcher::matchFunctionsWithoutProfileByBasename() {
StringMap<Function *> OrphansByBaseName;
StringSet<> AmbiguousBaseNames;
for (auto &[FuncId, Func] : FunctionsWithoutProfile) {
+ // Skip functions that already been matched through stale profile matching
+ if (FuncToProfileNameMap.contains(Func))
+ continue;
std::string BaseName = getDemangledBaseName(Demangler, Func->getName());
if (BaseName.empty() || AmbiguousBaseNames.count(BaseName))
continue;
@@ -939,9 +962,7 @@ void SampleProfileMatcher::matchFunctionsWithoutProfileByBasename() {
if (!OrphanFunc)
continue;
- FuncToProfileNameMap[OrphanFunc] = ProfId;
- ProfileNameToFuncMap[ProfId] = OrphanFunc;
- FuncProfileMatchCache[{OrphanFunc, ProfId}] = true;
+ ProfId = recordMatchedStaleProfile(OrphanFunc, ProfId);
if (const auto *FS = Reader.getSamplesFor(ProfId.stringRef()))
NewlyLoadedProfiles.create(FS->getFunction()).merge(*FS);
diff --git a/llvm/test/Transforms/SampleProfile/Inputs/pseudo-probe-stale-profile-orphan-conflict-cross-stage.prof b/llvm/test/Transforms/SampleProfile/Inputs/pseudo-probe-stale-profile-orphan-conflict-cross-stage.prof
new file mode 100644
index 0000000000000..5be0cc53774d2
--- /dev/null
+++ b/llvm/test/Transforms/SampleProfile/Inputs/pseudo-probe-stale-profile-orphan-conflict-cross-stage.prof
@@ -0,0 +1,11 @@
+_Z3fool:1:0
+ 30: _Z3bari:1
+ 1: 1
+ !CFGChecksum: 281479271677951
+ 57: _Z3quxi:1
+ 1: 1
+ !CFGChecksum: 4294967295
+ 72: _Z3topi:1
+ 2: _Z3midi:1
+ 11: _Z3subi:1
+ !CFGChecksum: 1
diff --git a/llvm/test/Transforms/SampleProfile/Inputs/stale-profile-lcs-anchor-overwrite-cross-stage.prof b/llvm/test/Transforms/SampleProfile/Inputs/stale-profile-lcs-anchor-overwrite-cross-stage.prof
new file mode 100644
index 0000000000000..d276f2630a43b
--- /dev/null
+++ b/llvm/test/Transforms/SampleProfile/Inputs/stale-profile-lcs-anchor-overwrite-cross-stage.prof
@@ -0,0 +1,11 @@
+_Z3foov:29916:31
+ 1: 31
+ 349: 2 A:1 B:1
+ 350: 1 C:1
+ 6: _Z3bari:1843
+ 1: 1843
+ !CFGChecksum: 1
+ 380: _Z6calleei:323
+ 1: 323
+ !CFGChecksum: 281479271677951
+ !CFGChecksum: 1
diff --git a/llvm/test/Transforms/SampleProfile/pseudo-probe-stale-profile-orphan-conflict.ll b/llvm/test/Transforms/SampleProfile/pseudo-probe-stale-profile-orphan-conflict.ll
index 42036ad2856e9..33ba87c0c082c 100644
--- a/llvm/test/Transforms/SampleProfile/pseudo-probe-stale-profile-orphan-conflict.ll
+++ b/llvm/test/Transforms/SampleProfile/pseudo-probe-stale-profile-orphan-conflict.ll
@@ -24,13 +24,13 @@
; REQUIRES: asserts
; RUN: llvm-profdata merge --sample --extbinary %S/Inputs/pseudo-probe-stale-profile-orphan-conflict.prof -o %t.prof
; RUN: opt < %s -passes=sample-profile -sample-profile-file=%t.prof --salvage-stale-profile --salvage-unused-profile -S --debug-only=sample-profile,sample-profile-matcher,sample-profile-impl 2>&1 | FileCheck %s
+; RUN: llvm-profdata merge --sample --extbinary %S/Inputs/pseudo-probe-stale-profile-orphan-conflict-cross-stage.prof -o %t.cross-stage.prof
+; RUN: opt < %s -passes=sample-profile -sample-profile-file=%t.cross-stage.prof --salvage-stale-profile --salvage-unused-profile --min-func-count-for-cg-matching=0 -S --debug-only=sample-profile,sample-profile-matcher,sample-profile-impl 2>&1 | FileCheck %s --check-prefix=CROSS-STAGE
; CHECK: Run stale profile matching for _Z3fool
; CHECK: The functions _Z3topl(IR) and _Z3topi(Profile) share the same base name: top.
; CHECK: The functions _Z3topll(IR) and _Z3topi(Profile) share the same base name: top.
; CHECK: Function:_Z3topl matches profile:_Z3topi
-; CHECK: Function:_Z3barl matches profile:_Z3bari
-; CHECK: Function:_Z3topll matches profile:_Z3topi
; CHECK: Function:_Z3topll encounters conflicting profile matchings, remapping to new profile:_Z3topll
; CHECK: Callsite with callee:_Z3topl is matched from 2 to 30
; CHECK: Callsite with callee:_Z3barl is matched from 3 to 57
@@ -42,21 +42,37 @@
; CHECK: Run stale profile matching for _Z3barl
; CHECK: Run stale profile matching for _Z3topl
; CHECK: The functions _Z3midl(IR) and _Z3midi(Profile) share the same base name: mid.
-; CHECK: Function:_Z3midl matches profile:_Z3midi
; CHECK: Function:_Z3midl encounters conflicting profile matchings, remapping to new profile:_Z3midl
; CHECK: Callsite with callee:_Z3midl is matched from 1 to 2
; CHECK: Run stale profile matching for _Z3midll
; CHECK: Callsite with callee:_Z3subi is matched from 1 to 11
; CHECK: Run stale profile matching for _Z3subi
; CHECK: Run stale profile matching for _Z3midl
-; CHECK: Function processing order:
-; CHECK: _Z3topll
-; CHECK: _Z3midl
-; CHECK: _Z3fool
-; CHECK: _Z3topl
-; CHECK: _Z3midll
-; CHECK: _Z3subi
-; CHECK: _Z3barl
+
+; Test the conflict between the two stages of stale profile matching.
+; CROSS-STAGE: Run stale profile matching for _Z3fool
+; CROSS-STAGE: The checksums for _Z3topl(IR) and _Z3bari(Profile) match.
+; CROSS-STAGE: The checksums for _Z3barl(IR) and _Z3quxi(Profile) match.
+; CROSS-STAGE: The functions _Z3topll(IR) and _Z3topi(Profile) share the same base name: top.
+; CROSS-STAGE: Function:_Z3topl matches profile:_Z3bari
+; CROSS-STAGE: Function:_Z3barl matches profile:_Z3quxi
+; CROSS-STAGE: Function:_Z3topll matches profile:_Z3topi
+; CROSS-STAGE: Callsite with callee:_Z3topl is matched from 3 to 30
+; CROSS-STAGE: Callsite with callee:_Z3barl is matched from 5 to 57
+; CROSS-STAGE: Callsite with callee:_Z3topll is matched from 6 to 72
+; CROSS-STAGE: Run stale profile matching for _Z3topll
+; CROSS-STAGE: The functions _Z3midll(IR) and _Z3midi(Profile) share the same base name: mid.
+; CROSS-STAGE: Function:_Z3midll matches profile:_Z3midi
+; CROSS-STAGE: Callsite with callee:_Z3midll is matched from 2 to 2
+; CROSS-STAGE: Run stale profile matching for _Z3barl
+; CROSS-STAGE: Run stale profile matching for _Z3topl
+; CROSS-STAGE: Run stale profile matching for _Z3midll
+; CROSS-STAGE: Callsite with callee:_Z3subi is matched from 2 to 11
+; CROSS-STAGE: Run stale profile matching for _Z3subi
+; CROSS-STAGE: Function:_Z3midl encounters conflicting profile matchings, remapping to new profile:_Z3midl
+; CROSS-STAGE: Direct basename match: _Z3midl (IR) -> _Z3midl (Profile) [basename: mid]
+; CROSS-STAGE: Direct basename matching found 1 matches
+; CROSS-STAGE: Run stale profile matching for _Z3midl
target triple = "x86_64-redhat-linux-gnu"
diff --git a/llvm/test/Transforms/SampleProfile/stale-profile-lcs-anchor-overwrite.ll b/llvm/test/Transforms/SampleProfile/stale-profile-lcs-anchor-overwrite.ll
index 4316848283d68..f148028110bd7 100644
--- a/llvm/test/Transforms/SampleProfile/stale-profile-lcs-anchor-overwrite.ll
+++ b/llvm/test/Transforms/SampleProfile/stale-profile-lcs-anchor-overwrite.ll
@@ -2,6 +2,8 @@
; REQUIRES: asserts
; RUN: llvm-profdata merge --sample --extbinary %S/Inputs/stale-profile-lcs-anchor-overwrite.prof -o %t.prof
; RUN: opt < %s -passes=sample-profile -sample-profile-file=%t.prof --salvage-stale-profile --salvage-unused-profile -S --debug-only=sample-profile,sample-profile-matcher,sample-profile-impl 2>&1 | FileCheck %s
+; RUN: llvm-profdata merge --sample --extbinary %S/Inputs/stale-profile-lcs-anchor-overwrite-cross-stage.prof -o %t.cross-stage.prof
+; RUN: opt < %s -passes=sample-profile -sample-profile-file=%t.cross-stage.prof --salvage-stale-profile --salvage-unused-profile --min-func-count-for-cg-matching=0 -S --debug-only=sample-profile,sample-profile-matcher,sample-profile-impl 2>&1 | FileCheck %s --check-prefix=CROSS-STAGE
; CHECK: Function _Z6calleePv is not in profile or profile symbol list.
; CHECK: Function _Z3barv is not in profile or profile symbol list.
@@ -23,16 +25,29 @@
; CHECK: Callsite with callee:unknown.indirect.callee is matched from 4 to 349
; CHECK: Callsite with callee:_Z3barPv is matched from 5 to 380
; CHECK: Run stale profile matching for _Z3barPv
-; CHECK: Function:_Z6calleePv matches profile:_Z6calleei
; CHECK: Location is matched from 1 to 1
; CHECK: Callsite with callee:_Z6calleePv is matched from 2 to 3
; CHECK: Run stale profile matching for _Z3barv
; CHECK: Run stale profile matching for _Z6calleePv
-; CHECK: Function processing order:
-; CHECK: _Z3foov
-; CHECK: _Z3barPv
-; CHECK: _Z6calleePv
-; CHECK: _Z3barv
+
+; Test the conflict between the two stages of stale profile matching.
+; CROSS-STAGE: Function _Z6calleePv is not in profile or profile symbol list.
+; CROSS-STAGE: Function _Z3barv is not in profile or profile symbol list.
+; CROSS-STAGE: Function _Z3barPv is not in profile or profile symbol list.
+; CROSS-STAGE: Run stale profile matching for _Z3foov
+; CROSS-STAGE: The functions _Z3barv(IR) and _Z3bari(Profile) share the same base name: bar.
+; CROSS-STAGE: The checksums for _Z3barPv(IR) and _Z6calleei(Profile) match.
+; CROSS-STAGE: Function:_Z3barv matches profile:_Z3bari
+; CROSS-STAGE: Function:_Z3barPv matches profile:_Z6calleei
+; CROSS-STAGE: Callsite with callee:_Z3barv is matched from 3 to 6
+; CROSS-STAGE: Callsite with callee:unknown.indirect.callee is matched from 4 to 349
+; CROSS-STAGE: Callsite with callee:_Z3barPv is matched from 5 to 380
+; CROSS-STAGE: Run stale profile matching for _Z3barPv
+; CROSS-STAGE: Run stale profile matching for _Z3barv
+; CROSS-STAGE: Function:_Z6calleePv encounters conflicting profile matchings, remapping to new profile:_Z6calleePv
+; CROSS-STAGE: Direct basename match: _Z6calleePv (IR) -> _Z6calleePv (Profile) [basename: callee]
+; CROSS-STAGE: Direct basename matching found 1 matches
+; CROSS-STAGE: Run stale profile matching for _Z6calleePv
target triple = "x86_64-linux-gnu"
More information about the llvm-commits
mailing list