Skip to content

Commit 7e99bdd

Browse files
committed
[CSSPGO] Support of CS profiles in extended binary format.
This change brings up support of context-sensitive profiles in the format of extended binary. Existing sample profile reader/writer/merger code is being tweaked to reflect the fact of bracketed input contexts, like (`[...]`). The paired brackets are also needed in extbinary profiles because we don't yet have an otherwise good way to tell calling contexts apart from regular function names since the context delimiter `@` can somehow serve as a part of the C++ mangled names. Reviewed By: wmi, wenlei Differential Revision: https://reviews.llvm.org/D95547
1 parent 5d05cdf commit 7e99bdd

File tree

10 files changed

+113
-50
lines changed

10 files changed

+113
-50
lines changed

llvm/include/llvm/ProfileData/SampleProf.h

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -439,16 +439,19 @@ class SampleContext {
439439
void clearState(ContextStateMask S) { State &= (uint32_t)~S; }
440440
bool hasContext() const { return State != UnknownContext; }
441441
bool isBaseContext() const { return CallingContext.empty(); }
442-
StringRef getName() const { return Name; }
442+
StringRef getNameWithoutContext() const { return Name; }
443443
StringRef getCallingContext() const { return CallingContext; }
444-
StringRef getNameWithContext() const { return FullContext; }
444+
StringRef getNameWithContext(bool WithBracket = false) const {
445+
return WithBracket ? InputContext : FullContext;
446+
}
445447

446448
private:
447449
// Give a context string, decode and populate internal states like
448450
// Function name, Calling context and context state. Example of input
449451
// `ContextStr`: `[main:3 @ _Z5funcAi:1 @ _Z8funcLeafi]`
450452
void setContext(StringRef ContextStr, ContextStateMask CState) {
451453
assert(!ContextStr.empty());
454+
InputContext = ContextStr;
452455
// Note that `[]` wrapped input indicates a full context string, otherwise
453456
// it's treated as context-less function name only.
454457
bool HasContext = ContextStr.startswith("[");
@@ -480,6 +483,9 @@ class SampleContext {
480483
}
481484
}
482485

486+
// Input context string including bracketed calling context and leaf function
487+
// name
488+
StringRef InputContext;
483489
// Full context string including calling context and leaf function name
484490
StringRef FullContext;
485491
// Function name for the associated sample profile
@@ -676,7 +682,8 @@ class FunctionSamples {
676682
Name = Other.getName();
677683
if (!GUIDToFuncNameMap)
678684
GUIDToFuncNameMap = Other.GUIDToFuncNameMap;
679-
685+
if (Context.getNameWithContext(true).empty())
686+
Context = Other.getContext();
680687
if (FunctionHash == 0) {
681688
// Set the function hash code for the target profile.
682689
FunctionHash = Other.getFunctionHash();
@@ -743,8 +750,10 @@ class FunctionSamples {
743750
StringRef getName() const { return Name; }
744751

745752
/// Return function name with context.
746-
StringRef getNameWithContext() const {
747-
return FunctionSamples::ProfileIsCS ? Context.getNameWithContext() : Name;
753+
StringRef getNameWithContext(bool WithBracket = false) const {
754+
return FunctionSamples::ProfileIsCS
755+
? Context.getNameWithContext(WithBracket)
756+
: Name;
748757
}
749758

750759
/// Return the original function name.

llvm/include/llvm/ProfileData/SampleProfReader.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,8 +488,12 @@ class SampleProfileReader {
488488
/// \brief Whether samples are collected based on pseudo probes.
489489
bool ProfileIsProbeBased = false;
490490

491+
/// Whether function profiles are context-sensitive.
491492
bool ProfileIsCS = false;
492493

494+
/// Number of context-sensitive profiles.
495+
uint32_t CSProfileCount = 0;
496+
493497
/// \brief The format of sample.
494498
SampleProfileFormat Format = SPF_None;
495499
};

llvm/lib/ProfileData/SampleProfReader.cpp

Lines changed: 46 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,6 @@ std::error_code SampleProfileReaderText::readImpl() {
222222
sampleprof_error Result = sampleprof_error::success;
223223

224224
InlineCallStack InlineStack;
225-
int CSProfileCount = 0;
226-
int RegularProfileCount = 0;
227225
uint32_t ProbeProfileCount = 0;
228226

229227
// SeenMetadata tracks whether we have processed metadata for the current
@@ -257,11 +255,9 @@ std::error_code SampleProfileReaderText::readImpl() {
257255
SampleContext FContext(FName);
258256
if (FContext.hasContext())
259257
++CSProfileCount;
260-
else
261-
++RegularProfileCount;
262258
Profiles[FContext] = FunctionSamples();
263259
FunctionSamples &FProfile = Profiles[FContext];
264-
FProfile.setName(FContext.getName());
260+
FProfile.setName(FContext.getNameWithoutContext());
265261
FProfile.setContext(FContext);
266262
MergeResult(Result, FProfile.addTotalSamples(NumSamples));
267263
MergeResult(Result, FProfile.addHeadSamples(NumHeadSamples));
@@ -324,13 +320,14 @@ std::error_code SampleProfileReaderText::readImpl() {
324320
}
325321
}
326322

327-
assert((RegularProfileCount == 0 || CSProfileCount == 0) &&
323+
assert((CSProfileCount == 0 || CSProfileCount == Profiles.size()) &&
328324
"Cannot have both context-sensitive and regular profile");
329325
ProfileIsCS = (CSProfileCount > 0);
330326
assert((ProbeProfileCount == 0 || ProbeProfileCount == Profiles.size()) &&
331327
"Cannot have both probe-based profiles and regular profiles");
332328
ProfileIsProbeBased = (ProbeProfileCount > 0);
333329
FunctionSamples::ProfileIsProbeBased = ProfileIsProbeBased;
330+
FunctionSamples::ProfileIsCS = ProfileIsCS;
334331

335332
if (Result == sampleprof_error::success)
336333
computeSummary();
@@ -546,12 +543,16 @@ SampleProfileReaderBinary::readFuncProfile(const uint8_t *Start) {
546543
if (std::error_code EC = FName.getError())
547544
return EC;
548545

549-
Profiles[*FName] = FunctionSamples();
550-
FunctionSamples &FProfile = Profiles[*FName];
551-
FProfile.setName(*FName);
552-
546+
SampleContext FContext(*FName);
547+
Profiles[FContext] = FunctionSamples();
548+
FunctionSamples &FProfile = Profiles[FContext];
549+
FProfile.setName(FContext.getNameWithoutContext());
550+
FProfile.setContext(FContext);
553551
FProfile.addHeadSamples(*NumHeadSamples);
554552

553+
if (FContext.hasContext())
554+
CSProfileCount++;
555+
555556
if (std::error_code EC = readProfile(FProfile))
556557
return EC;
557558
return sampleprof_error::success;
@@ -654,40 +655,44 @@ std::error_code SampleProfileReaderExtBinaryBase::readFuncProfiles() {
654655
return EC;
655656
}
656657
assert(Data == End && "More data is read than expected");
657-
return sampleprof_error::success;
658-
}
659-
660-
if (Remapper) {
661-
for (auto Name : FuncsToUse) {
662-
Remapper->insert(Name);
658+
} else {
659+
if (Remapper) {
660+
for (auto Name : FuncsToUse) {
661+
Remapper->insert(Name);
662+
}
663663
}
664-
}
665664

666-
if (useMD5()) {
667-
for (auto Name : FuncsToUse) {
668-
auto GUID = std::to_string(MD5Hash(Name));
669-
auto iter = FuncOffsetTable.find(StringRef(GUID));
670-
if (iter == FuncOffsetTable.end())
671-
continue;
672-
const uint8_t *FuncProfileAddr = Start + iter->second;
673-
assert(FuncProfileAddr < End && "out of LBRProfile section");
674-
if (std::error_code EC = readFuncProfile(FuncProfileAddr))
675-
return EC;
676-
}
677-
} else {
678-
for (auto NameOffset : FuncOffsetTable) {
679-
auto FuncName = NameOffset.first;
680-
if (!FuncsToUse.count(FuncName) &&
681-
(!Remapper || !Remapper->exist(FuncName)))
682-
continue;
683-
const uint8_t *FuncProfileAddr = Start + NameOffset.second;
684-
assert(FuncProfileAddr < End && "out of LBRProfile section");
685-
if (std::error_code EC = readFuncProfile(FuncProfileAddr))
686-
return EC;
665+
if (useMD5()) {
666+
for (auto Name : FuncsToUse) {
667+
auto GUID = std::to_string(MD5Hash(Name));
668+
auto iter = FuncOffsetTable.find(StringRef(GUID));
669+
if (iter == FuncOffsetTable.end())
670+
continue;
671+
const uint8_t *FuncProfileAddr = Start + iter->second;
672+
assert(FuncProfileAddr < End && "out of LBRProfile section");
673+
if (std::error_code EC = readFuncProfile(FuncProfileAddr))
674+
return EC;
675+
}
676+
} else {
677+
for (auto NameOffset : FuncOffsetTable) {
678+
SampleContext FContext(NameOffset.first);
679+
auto FuncName = FContext.getNameWithoutContext();
680+
if (!FuncsToUse.count(FuncName) &&
681+
(!Remapper || !Remapper->exist(FuncName)))
682+
continue;
683+
const uint8_t *FuncProfileAddr = Start + NameOffset.second;
684+
assert(FuncProfileAddr < End && "out of LBRProfile section");
685+
if (std::error_code EC = readFuncProfile(FuncProfileAddr))
686+
return EC;
687+
}
687688
}
689+
Data = End;
688690
}
689691

690-
Data = End;
692+
assert((CSProfileCount == 0 || CSProfileCount == Profiles.size()) &&
693+
"Cannot have both context-sensitive and regular profile");
694+
ProfileIsCS = (CSProfileCount > 0);
695+
FunctionSamples::ProfileIsCS = ProfileIsCS;
691696
return sampleprof_error::success;
692697
}
693698

@@ -887,7 +892,8 @@ std::error_code SampleProfileReaderExtBinaryBase::readFuncMetadata() {
887892
if (std::error_code EC = Checksum.getError())
888893
return EC;
889894

890-
Profiles[*FName].setFunctionHash(*Checksum);
895+
SampleContext FContext(*FName);
896+
Profiles[FContext].setFunctionHash(*Checksum);
891897
}
892898
return sampleprof_error::success;
893899
}

llvm/lib/ProfileData/SampleProfWriter.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ std::error_code SampleProfileWriterExtBinaryBase::write(
147147
std::error_code
148148
SampleProfileWriterExtBinaryBase::writeSample(const FunctionSamples &S) {
149149
uint64_t Offset = OutputStream->tell();
150-
StringRef Name = S.getName();
150+
StringRef Name = S.getNameWithContext(true);
151151
FuncOffsetTable[Name] = Offset - SecLBRProfileStart;
152152
encodeULEB128(S.getHeadSamples(), *OutputStream);
153153
return writeBody(S);
@@ -635,7 +635,7 @@ std::error_code SampleProfileWriterBinary::writeSummary() {
635635
std::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) {
636636
auto &OS = *OutputStream;
637637

638-
if (std::error_code EC = writeNameIdx(S.getName()))
638+
if (std::error_code EC = writeNameIdx(S.getNameWithContext(true)))
639639
return EC;
640640

641641
encodeULEB128(S.getTotalSamples(), OS);

llvm/lib/Transforms/IPO/SampleContextTracker.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ SampleContextTracker::SampleContextTracker(
179179
SampleContext Context(FuncSample.first(), RawContext);
180180
LLVM_DEBUG(dbgs() << "Tracking Context for function: " << Context << "\n");
181181
if (!Context.isBaseContext())
182-
FuncToCtxtProfileSet[Context.getName()].insert(FSamples);
182+
FuncToCtxtProfileSet[Context.getNameWithoutContext()].insert(FSamples);
183183
ContextTrieNode *NewNode = getOrCreateContextPath(Context, true);
184184
assert(!NewNode->getFunctionSamples() &&
185185
"New node can't have sample profile");

llvm/test/Transforms/SampleProfile/profile-context-tracker.ll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
; Test for CSSPGO's SampleContextTracker to make sure context profile tree is promoted and merged properly
22
; based on inline decision, so post inline counts are accurate.
33

4+
; RUN: llvm-profdata merge --sample --extbinary %S/Inputs/profile-context-tracker.prof -o %t
5+
46
; Note that we need new pass manager to enable top-down processing for sample profile loader
57
; Testwe we inlined the following in top-down order and entry counts accurate reflects post-inline base profile
68
; main:3 @ _Z5funcAi
79
; main:3 @ _Z5funcAi:1 @ _Z8funcLeafi
810
; _Z5funcBi:1 @ _Z8funcLeafi
911
; RUN: opt < %s -passes=sample-profile -sample-profile-file=%S/Inputs/profile-context-tracker.prof -sample-profile-inline-size -profile-sample-accurate -S | FileCheck %s --check-prefix=INLINE-ALL
12+
; RUN: opt < %s -passes=sample-profile -sample-profile-file=%t -sample-profile-inline-size -profile-sample-accurate -S | FileCheck %s --check-prefix=INLINE-ALL
1013

1114
; Testwe we inlined the following in top-down order and entry counts accurate reflects post-inline base profile
1215
; main:3 @ _Z5funcAi
1316
; _Z5funcAi:1 @ _Z8funcLeafi
1417
; _Z5funcBi:1 @ _Z8funcLeafi
1518
; RUN: opt < %s -passes=sample-profile -sample-profile-file=%S/Inputs/profile-context-tracker.prof -profile-sample-accurate -S | FileCheck %s --check-prefix=INLINE-HOT
19+
; RUN: opt < %s -passes=sample-profile -sample-profile-file=%t -profile-sample-accurate -S | FileCheck %s --check-prefix=INLINE-HOT
1620

1721

1822
@factor = dso_local global i32 3, align 4, !dbg !0
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
[main:3 @ _Z5funcAi:1 @ _Z8funcLeafi]:1467299:11
2+
0: 6
3+
1: 6
4+
3: 287884
5+
4: 287864 _Z3fibi:315608
6+
15: 23
7+
[main:3.1 @ _Z5funcBi:1 @ _Z8funcLeafi]:500853:20
8+
0: 15
9+
1: 15
10+
3: 74946
11+
4: 74941 _Z3fibi:82359
12+
10: 23324
13+
11: 23327 _Z3fibi:25228
14+
15: 11
15+
[main]:154:0
16+
2: 12
17+
3: 18 _Z5funcAi:11
18+
3.1: 18 _Z5funcBi:19
19+
[external:12 @ main]:154:12
20+
2: 12
21+
3: 10 _Z5funcAi:7
22+
3.1: 10 _Z5funcBi:11
23+
[main:3.1 @ _Z5funcBi]:120:19
24+
0: 19
25+
1: 19 _Z8funcLeafi:20
26+
3: 12
27+
[externalA:17 @ _Z5funcBi]:120:3
28+
0: 3
29+
1: 3
30+
[external:10 @ _Z5funcBi]:120:10
31+
0: 10
32+
1: 10
33+
[main:3 @ _Z5funcAi]:99:11
34+
0: 10
35+
1: 10 _Z8funcLeafi:11
36+
3: 24
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
RUN: llvm-profdata merge --sample --text -output=%t.proftext %S/Inputs/cs-sample.proftext
2+
RUN: diff -b %t.proftext %S/Inputs/cs-sample.proftext
3+
RUN: llvm-profdata merge --sample --extbinary %p/Inputs/cs-sample.proftext -o %t.prof && llvm-profdata merge --sample --text %t.prof -o %t1.proftext
4+
RUN: diff -b %t1.proftext %S/Inputs/cs-sample.proftext

llvm/tools/llvm-profdata/llvm-profdata.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,7 @@ mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper,
696696
Remapper ? remapSamples(I->second, *Remapper, Result)
697697
: FunctionSamples();
698698
FunctionSamples &Samples = Remapper ? Remapped : I->second;
699-
StringRef FName = Samples.getName();
699+
StringRef FName = Samples.getNameWithContext(true);
700700
MergeResult(Result, ProfileMap[FName].merge(Samples, Input.Weight));
701701
if (Result != sampleprof_error::success) {
702702
std::error_code EC = make_error_code(Result);

llvm/tools/llvm-profgen/ProfileGenerator.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ CSProfileGenerator::getFunctionProfileForContext(StringRef ContextStr) {
164164
if (Ret.second) {
165165
SampleContext FContext(Ret.first->first(), RawContext);
166166
FunctionSamples &FProfile = Ret.first->second;
167-
FProfile.setName(FContext.getName());
167+
FProfile.setName(FContext.getNameWithoutContext());
168168
FProfile.setContext(FContext);
169169
}
170170
return Ret.first->second;

0 commit comments

Comments
 (0)