Skip to content

Commit c623675

Browse files
[SampleFDO][TypeProf]Support vtable type profiling for ext-binary and text format
1 parent 32f461b commit c623675

File tree

11 files changed

+412
-25
lines changed

11 files changed

+412
-25
lines changed

llvm/include/llvm/ProfileData/SampleProf.h

Lines changed: 92 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ enum class sampleprof_error {
6161
ostream_seek_unsupported,
6262
uncompress_failed,
6363
zlib_unavailable,
64-
hash_mismatch
64+
hash_mismatch,
65+
illegal_line_offset,
66+
duplicate_vtable_type,
6567
};
6668

6769
inline std::error_code make_error_code(sampleprof_error E) {
@@ -90,6 +92,8 @@ struct is_error_code_enum<llvm::sampleprof_error> : std::true_type {};
9092
namespace llvm {
9193
namespace sampleprof {
9294

95+
constexpr char kVTableProfPrefix[] = "vtables ";
96+
9397
enum SampleProfileFormat {
9498
SPF_None = 0,
9599
SPF_Text = 0x1,
@@ -203,6 +207,9 @@ enum class SecProfSummaryFlags : uint32_t {
203207
/// SecFlagIsPreInlined means this profile contains ShouldBeInlined
204208
/// contexts thus this is CS preinliner computed.
205209
SecFlagIsPreInlined = (1 << 4),
210+
211+
/// SecFlagHasVTableTypeProf means this profile contains vtable type profiles.
212+
SecFlagHasVTableTypeProf = (1 << 5),
206213
};
207214

208215
enum class SecFuncMetadataFlags : uint32_t {
@@ -286,7 +293,7 @@ struct LineLocation {
286293
LLVM_ABI void dump() const;
287294

288295
// Serialize the line location to the output stream using ULEB128 encoding.
289-
LLVM_ABI void serialize(raw_ostream &OS);
296+
LLVM_ABI void serialize(raw_ostream &OS) const;
290297

291298
bool operator<(const LineLocation &O) const {
292299
return std::tie(LineOffset, Discriminator) <
@@ -302,7 +309,7 @@ struct LineLocation {
302309
}
303310

304311
uint64_t getHashCode() const {
305-
return ((uint64_t) Discriminator << 32) | LineOffset;
312+
return ((uint64_t)Discriminator << 32) | LineOffset;
306313
}
307314

308315
uint32_t LineOffset;
@@ -317,16 +324,28 @@ struct LineLocationHash {
317324

318325
LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const LineLocation &Loc);
319326

327+
/// Key represents the id of a vtable and value represents its count.
328+
/// TODO: Rename class FunctionId to SymbolId in a separate PR.
329+
using TypeCountMap = std::map<FunctionId, uint64_t>;
330+
331+
/// Write \p Map to the output stream. Keys are linearized using \p NameTable
332+
/// and written as ULEB128. Values are written as ULEB128 as well.
333+
std::error_code
334+
serializeTypeMap(const TypeCountMap &Map,
335+
const MapVector<FunctionId, uint32_t> &NameTable,
336+
raw_ostream &OS);
337+
320338
/// Representation of a single sample record.
321339
///
322340
/// A sample record is represented by a positive integer value, which
323341
/// indicates how frequently was the associated line location executed.
324342
///
325343
/// Additionally, if the associated location contains a function call,
326-
/// the record will hold a list of all the possible called targets. For
327-
/// direct calls, this will be the exact function being invoked. For
328-
/// indirect calls (function pointers, virtual table dispatch), this
329-
/// will be a list of one or more functions.
344+
/// the record will hold a list of all the possible called targets and the types
345+
/// for virtual table dispatches. For direct calls, this will be the exact
346+
/// function being invoked. For indirect calls (function pointers, virtual table
347+
/// dispatch), this will be a list of one or more functions. For virtual table
348+
/// dispatches, this record will also hold the type of the object.
330349
class SampleRecord {
331350
public:
332351
using CallTarget = std::pair<FunctionId, uint64_t>;
@@ -745,6 +764,7 @@ using BodySampleMap = std::map<LineLocation, SampleRecord>;
745764
// memory, which is *very* significant for large profiles.
746765
using FunctionSamplesMap = std::map<FunctionId, FunctionSamples>;
747766
using CallsiteSampleMap = std::map<LineLocation, FunctionSamplesMap>;
767+
using CallsiteTypeMap = std::map<LineLocation, TypeCountMap>;
748768
using LocToLocMap =
749769
std::unordered_map<LineLocation, LineLocation, LineLocationHash>;
750770

@@ -927,6 +947,14 @@ class FunctionSamples {
927947
return &Iter->second;
928948
}
929949

950+
/// Returns the TypeCountMap for inlined callsites at the given \p Loc.
951+
const TypeCountMap *findCallsiteTypeSamplesAt(const LineLocation &Loc) const {
952+
auto Iter = VirtualCallsiteTypeCounts.find(mapIRLocToProfileLoc(Loc));
953+
if (Iter == VirtualCallsiteTypeCounts.end())
954+
return nullptr;
955+
return &Iter->second;
956+
}
957+
930958
/// Returns a pointer to FunctionSamples at the given callsite location
931959
/// \p Loc with callee \p CalleeName. If no callsite can be found, relax
932960
/// the restriction to return the FunctionSamples at callsite location
@@ -988,6 +1016,42 @@ class FunctionSamples {
9881016
return CallsiteSamples;
9891017
}
9901018

1019+
/// Return all the callsite type samples collected in the body of the
1020+
/// function.
1021+
const CallsiteTypeMap &getCallsiteTypeCounts() const {
1022+
return VirtualCallsiteTypeCounts;
1023+
}
1024+
1025+
/// Returns the type samples for the un-drifted location of \p Loc.
1026+
TypeCountMap &getTypeSamplesAt(const LineLocation &Loc) {
1027+
return VirtualCallsiteTypeCounts[mapIRLocToProfileLoc(Loc)];
1028+
}
1029+
1030+
/// Scale \p Other sample counts by \p Weight and add the scaled result to the
1031+
/// type samples for the undrifted location of \p Loc.
1032+
template <typename T>
1033+
sampleprof_error addCallsiteVTableTypeProfAt(const LineLocation &Loc,
1034+
const T &Other,
1035+
uint64_t Weight = 1) {
1036+
static_assert((std::is_same_v<typename T::key_type, StringRef> ||
1037+
std::is_same_v<typename T::key_type, FunctionId>) &&
1038+
std::is_same_v<typename T::mapped_type, uint64_t>,
1039+
"T must be a map with StringRef or FunctionId as key and "
1040+
"uint64_t as value");
1041+
TypeCountMap &TypeCounts = getTypeSamplesAt(Loc);
1042+
bool Overflowed = false;
1043+
1044+
for (const auto [Type, Count] : Other) {
1045+
FunctionId TypeId(Type);
1046+
bool RowOverflow = false;
1047+
TypeCounts[TypeId] = SaturatingMultiplyAdd(
1048+
Count, Weight, TypeCounts[TypeId], &RowOverflow);
1049+
Overflowed |= RowOverflow;
1050+
}
1051+
return Overflowed ? sampleprof_error::counter_overflow
1052+
: sampleprof_error::success;
1053+
}
1054+
9911055
/// Return the maximum of sample counts in a function body. When SkipCallSite
9921056
/// is false, which is the default, the return count includes samples in the
9931057
/// inlined functions. When SkipCallSite is true, the return count only
@@ -1042,6 +1106,10 @@ class FunctionSamples {
10421106
mergeSampleProfErrors(Result,
10431107
FSMap[Rec.first].merge(Rec.second, Weight));
10441108
}
1109+
for (const auto &[Loc, OtherTypeMap] : Other.getCallsiteTypeCounts())
1110+
mergeSampleProfErrors(
1111+
Result, addCallsiteVTableTypeProfAt(Loc, OtherTypeMap, Weight));
1112+
10451113
return Result;
10461114
}
10471115

@@ -1285,6 +1353,23 @@ class FunctionSamples {
12851353
/// collected in the call to baz() at line offset 8.
12861354
CallsiteSampleMap CallsiteSamples;
12871355

1356+
/// Map inlined virtual callsites to the vtable from which they are loaded.
1357+
///
1358+
/// Each entry is a mapping from the location to the list of vtables and their
1359+
/// sampled counts. For example, given:
1360+
///
1361+
/// void foo() {
1362+
/// ...
1363+
/// 5 inlined_vcall_bar();
1364+
/// ...
1365+
/// 5 inlined_vcall_baz();
1366+
/// ...
1367+
/// 200 inlined_vcall_qux();
1368+
/// }
1369+
/// This map will contain two entries. One with two types for line offset 5
1370+
/// and one with one type for line offset 200.
1371+
CallsiteTypeMap VirtualCallsiteTypeCounts;
1372+
12881373
/// IR to profile location map generated by stale profile matching.
12891374
///
12901375
/// Each entry is a mapping from the location on current build to the matched

llvm/include/llvm/ProfileData/SampleProfReader.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,16 @@ class LLVM_ABI SampleProfileReaderBinary : public SampleProfileReader {
703703
/// otherwise same as readStringFromTable, also return its hash value.
704704
ErrorOr<std::pair<SampleContext, uint64_t>> readSampleContextFromTable();
705705

706+
std::error_code readBodySampleVTableProf(const LineLocation &Loc,
707+
FunctionSamples &FProfile);
708+
/// Read all callsites' vtable access counts for \p FProfile.
709+
std::error_code readCallsiteVTableProf(FunctionSamples &FProfile);
710+
711+
/// Read bytes from the input buffer pointed by `Data` and decode them into
712+
/// \p M. `Data` will be advanced to the end of the read bytes when this
713+
/// function returns. Returns error if any.
714+
std::error_code readVTableTypeCountMap(TypeCountMap &M);
715+
706716
/// Points to the current location in the buffer.
707717
const uint8_t *Data = nullptr;
708718

@@ -727,6 +737,8 @@ class LLVM_ABI SampleProfileReaderBinary : public SampleProfileReader {
727737
/// to the start of MD5SampleContextTable.
728738
const uint64_t *MD5SampleContextStart = nullptr;
729739

740+
bool ReadVTableProf = false;
741+
730742
private:
731743
std::error_code readSummaryEntry(std::vector<ProfileSummaryEntry> &Entries);
732744
virtual std::error_code verifySPMagic(uint64_t Magic) = 0;

llvm/include/llvm/ProfileData/SampleProfWriter.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -217,13 +217,20 @@ class LLVM_ABI SampleProfileWriterBinary : public SampleProfileWriter {
217217
std::error_code writeBody(const FunctionSamples &S);
218218
inline void stablizeNameTable(MapVector<FunctionId, uint32_t> &NameTable,
219219
std::set<FunctionId> &V);
220-
220+
221221
MapVector<FunctionId, uint32_t> NameTable;
222-
222+
223223
void addName(FunctionId FName);
224224
virtual void addContext(const SampleContext &Context);
225225
void addNames(const FunctionSamples &S);
226226

227+
/// Write \p CallsiteTypeMap to the output stream \p OS.
228+
std::error_code
229+
writeCallsiteVTableProf(const CallsiteTypeMap &CallsiteTypeMap,
230+
raw_ostream &OS);
231+
232+
bool WriteVTableProf = false;
233+
227234
private:
228235
LLVM_ABI friend ErrorOr<std::unique_ptr<SampleProfileWriter>>
229236
SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
@@ -412,8 +419,7 @@ class LLVM_ABI SampleProfileWriterExtBinaryBase
412419
class LLVM_ABI SampleProfileWriterExtBinary
413420
: public SampleProfileWriterExtBinaryBase {
414421
public:
415-
SampleProfileWriterExtBinary(std::unique_ptr<raw_ostream> &OS)
416-
: SampleProfileWriterExtBinaryBase(OS) {}
422+
SampleProfileWriterExtBinary(std::unique_ptr<raw_ostream> &OS);
417423

418424
private:
419425
std::error_code writeDefaultLayout(const SampleProfileMap &ProfileMap);

llvm/lib/ProfileData/SampleProf.cpp

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,24 @@ bool FunctionSamples::ProfileIsPreInlined = false;
4747
bool FunctionSamples::UseMD5 = false;
4848
bool FunctionSamples::HasUniqSuffix = true;
4949
bool FunctionSamples::ProfileIsFS = false;
50+
51+
std::error_code
52+
serializeTypeMap(const TypeCountMap &Map,
53+
const MapVector<FunctionId, uint32_t> &NameTable,
54+
raw_ostream &OS) {
55+
encodeULEB128(Map.size(), OS);
56+
for (const auto &[TypeName, SampleCount] : Map) {
57+
if (auto NameIndexIter = NameTable.find(TypeName);
58+
NameIndexIter != NameTable.end()) {
59+
encodeULEB128(NameIndexIter->second, OS);
60+
} else {
61+
// If the type is not in the name table, we cannot serialize it.
62+
return sampleprof_error::truncated_name_table;
63+
}
64+
encodeULEB128(SampleCount, OS);
65+
}
66+
return sampleprof_error::success;
67+
}
5068
} // namespace sampleprof
5169
} // namespace llvm
5270

@@ -91,6 +109,10 @@ class SampleProfErrorCategoryType : public std::error_category {
91109
return "Zlib is unavailable";
92110
case sampleprof_error::hash_mismatch:
93111
return "Function hash mismatch";
112+
case sampleprof_error::illegal_line_offset:
113+
return "Illegal line offset in sample profile data";
114+
case sampleprof_error::duplicate_vtable_type:
115+
return "Duplicate vtable type in one map";
94116
}
95117
llvm_unreachable("A value of sampleprof_error has no message.");
96118
}
@@ -124,6 +146,7 @@ sampleprof_error SampleRecord::merge(const SampleRecord &Other,
124146
for (const auto &I : Other.getCallTargets()) {
125147
mergeSampleProfErrors(Result, addCalledTarget(I.first, I.second, Weight));
126148
}
149+
127150
return Result;
128151
}
129152

@@ -150,7 +173,7 @@ std::error_code SampleRecord::serialize(
150173
LLVM_DUMP_METHOD void LineLocation::dump() const { print(dbgs()); }
151174
#endif
152175

153-
void LineLocation::serialize(raw_ostream &OS) {
176+
void LineLocation::serialize(raw_ostream &OS) const {
154177
encodeULEB128(LineOffset, OS);
155178
encodeULEB128(Discriminator, OS);
156179
}
@@ -176,6 +199,17 @@ raw_ostream &llvm::sampleprof::operator<<(raw_ostream &OS,
176199
return OS;
177200
}
178201

202+
static void printTypeCountMap(raw_ostream &OS, LineLocation Loc,
203+
const TypeCountMap &TypeCountMap) {
204+
if (TypeCountMap.empty()) {
205+
return;
206+
}
207+
OS << Loc << ": vtables: ";
208+
for (const auto &[Type, Count] : TypeCountMap)
209+
OS << Type << ":" << Count << " ";
210+
OS << "\n";
211+
}
212+
179213
/// Print the samples collected for a function on stream \p OS.
180214
void FunctionSamples::print(raw_ostream &OS, unsigned Indent) const {
181215
if (getFunctionHash())
@@ -190,7 +224,13 @@ void FunctionSamples::print(raw_ostream &OS, unsigned Indent) const {
190224
SampleSorter<LineLocation, SampleRecord> SortedBodySamples(BodySamples);
191225
for (const auto &SI : SortedBodySamples.get()) {
192226
OS.indent(Indent + 2);
227+
const auto &Loc = SI->first;
193228
OS << SI->first << ": " << SI->second;
229+
if (const TypeCountMap *TypeCountMap =
230+
this->findCallsiteTypeSamplesAt(Loc)) {
231+
OS.indent(Indent + 2);
232+
printTypeCountMap(OS, Loc, *TypeCountMap);
233+
}
194234
}
195235
OS.indent(Indent);
196236
OS << "}\n";
@@ -204,11 +244,17 @@ void FunctionSamples::print(raw_ostream &OS, unsigned Indent) const {
204244
SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples(
205245
CallsiteSamples);
206246
for (const auto &CS : SortedCallsiteSamples.get()) {
207-
for (const auto &FS : CS->second) {
247+
for (const auto &[FuncId, FuncSample] : CS->second) {
208248
OS.indent(Indent + 2);
209-
OS << CS->first << ": inlined callee: " << FS.second.getFunction()
249+
OS << CS->first << ": inlined callee: " << FuncSample.getFunction()
210250
<< ": ";
211-
FS.second.print(OS, Indent + 4);
251+
FuncSample.print(OS, Indent + 4);
252+
}
253+
const LineLocation &Loc = CS->first;
254+
auto TypeSamplesIter = VirtualCallsiteTypeCounts.find(Loc);
255+
if (TypeSamplesIter != VirtualCallsiteTypeCounts.end()) {
256+
OS.indent(Indent + 2);
257+
printTypeCountMap(OS, Loc, TypeSamplesIter->second);
212258
}
213259
}
214260
OS.indent(Indent);

0 commit comments

Comments
 (0)