[llvm] [SampleProfile] Support Eytzinger layout in SecFuncOffsetTable (PR #213829)
Kazu Hirata via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 10 00:03:55 PDT 2026
https://github.com/kazutakahirata updated https://github.com/llvm/llvm-project/pull/213829
>From df0b27fa831b4c31ef184815b6b9298ca27e923c Mon Sep 17 00:00:00 2001
From: Kazu Hirata <kazu at google.com>
Date: Fri, 31 Jul 2026 20:46:51 -0700
Subject: [PATCH 1/3] [SampleProfile] Support Eytzinger layout in
SecFuncOffsetTable
This patch supports writing SecFuncOffsetTable as an array of
fixed-length uint32_t file offsets parallel to the Eytzinger spans in
SecNameTable for ExtBinary sample profiles.
Without this patch, SecFuncOffsetTable stores pairs of variable-length
ULEB128 integers for GUIDs and file offsets, requiring us to eagerly
load and decode the entire section into an in-memory map.
This patch implements parallel arrays to avoid eager loading:
- In the writer, when -sample-profile-write-eytzinger-name-tables is set,
SecFuncOffsetTable holds only an array of uint32_t offsets into
SecLBRProfile. The k-th offset entry corresponds to the k-th GUID in the
CSKeys or FlatKeys Eytzinger span of SecNameTable.
- In the reader, when SecFlagEytzinger is set on SecFuncOffsetTable, the
reader accesses the memory-mapped offset array directly without eagerly
decoding ULEB128 pairs or constructing an in-memory map.
This patch does not add a unit test but renames an existing one because
the test that is being renamed already covers the roundtrip test with
-sample-profile-write-eytzinger-name-tables set.
RFC:
https://discourse.llvm.org/t/rfc-faster-sample-profile-loading/90957/8
Assisted-by: Antigravity
---
llvm/include/llvm/ProfileData/SampleProf.h | 3 +
.../llvm/ProfileData/SampleProfReader.h | 52 ++++++++++++-
.../llvm/ProfileData/SampleProfWriter.h | 6 +-
llvm/lib/ProfileData/SampleProfReader.cpp | 62 ++++++++++++++-
llvm/lib/ProfileData/SampleProfWriter.cpp | 75 +++++++++++++++++--
.../eytzinger-split-nametable-partition.test | 4 +-
6 files changed, 189 insertions(+), 13 deletions(-)
diff --git a/llvm/include/llvm/ProfileData/SampleProf.h b/llvm/include/llvm/ProfileData/SampleProf.h
index c409b4293ee8d..eaf347b62b24e 100644
--- a/llvm/include/llvm/ProfileData/SampleProf.h
+++ b/llvm/include/llvm/ProfileData/SampleProf.h
@@ -250,6 +250,9 @@ enum class SecFuncOffsetFlags : uint32_t {
// Store function offsets in an order of contexts. The order ensures that
// callee contexts of a given context laid out next to it.
SecFlagOrdered = (1 << 0),
+ // Store function offsets in a parallel array aligned with Eytzinger NameTable
+ // span.
+ SecFlagEytzinger = (1 << 1),
};
// Verify section specific flag is used for the correct section.
diff --git a/llvm/include/llvm/ProfileData/SampleProfReader.h b/llvm/include/llvm/ProfileData/SampleProfReader.h
index 2ce052d5fb0a5..6eb5760d8e1e6 100644
--- a/llvm/include/llvm/ProfileData/SampleProfReader.h
+++ b/llvm/include/llvm/ProfileData/SampleProfReader.h
@@ -397,6 +397,12 @@ class SampleProfileNameTable {
virtual size_t size() const = 0;
bool empty() const { return size() == 0; }
virtual FunctionId operator[](size_t Idx) const = 0;
+
+ virtual EytzingerTableSpan<support::ulittle64_t>
+ getEytzingerSpan(bool IsCS) const {
+ llvm_unreachable(
+ "getEytzingerSpan is exclusively supported for Eytzinger layout");
+ }
virtual bool contains(StringRef Key) const {
return contains(FunctionId(Key).getHashCode());
}
@@ -514,6 +520,12 @@ class EytzingerSampleProfileNameTable final : public SampleProfileNameTable {
return FunctionId(Array[Idx]);
}
+ EytzingerTableSpan<support::ulittle64_t>
+ getEytzingerSpan(bool IsCS) const override {
+ return Spans[static_cast<size_t>(IsCS ? EytzingerSpan::CS
+ : EytzingerSpan::Flat)];
+ }
+
bool contains(uint64_t GUID) const override {
return llvm::any_of(Spans,
[&](const auto &Span) { return Span.contains(GUID); });
@@ -1004,8 +1016,11 @@ class FuncOffsetHashTableInfo {
/// Tags to select the initialization mode of SampleProfileFuncOffsetTable.
struct InMemoryModeT {};
struct OnDiskModeT {};
+struct EytzingerModeT {};
+
inline constexpr InMemoryModeT InMemoryMode{};
inline constexpr OnDiskModeT OnDiskMode{};
+inline constexpr EytzingerModeT EytzingerMode{};
/// A unified wrapper representing the function offset table.
///
@@ -1019,6 +1034,8 @@ inline constexpr OnDiskModeT OnDiskMode{};
/// - An OnDiskIterableChainedHashTable providing the same mapping directly from
/// the file in (non-context-sensitive) version 104 profiles.
///
+/// - A raw slice of 32-bit relative offsets for Eytzinger parallel lookups.
+///
/// It exposes a single, type-agnostic lookup interface, shielding the reader
/// from the underlying container types. To prevent hybrid-state corruption, the
/// table's mode is locked at construction time, and assertions prevent
@@ -1041,6 +1058,11 @@ class SampleProfileFuncOffsetTable {
InMemoryTable.reserve(InitialCapacity);
}
+ SampleProfileFuncOffsetTable(
+ EytzingerModeT, EytzingerTableSpan<support::ulittle64_t> NameSpan,
+ ArrayRef<support::ulittle32_t> FuncOffsetSpan)
+ : NameSpan(NameSpan), FuncOffsetSpan(FuncOffsetSpan) {}
+
/// Insert a function GUID and its profile offset into the in-memory map.
/// Enforces that the on-disk table must not have been set first.
void insert(uint64_t GUID, uint64_t Offset) {
@@ -1058,6 +1080,14 @@ class SampleProfileFuncOffsetTable {
/// Query the offset table for the profile offset associated with the given
/// GUID. Returns the offset if found, or std::nullopt if the key is missing.
std::optional<uint64_t> lookup(uint64_t GUID) const {
+ if (isEytzinger()) {
+ if (std::optional<size_t> Idx = NameSpan.findIndex(GUID)) {
+ uint32_t RelOffset = FuncOffsetSpan[*Idx];
+ if (RelOffset != UINT32_MAX)
+ return RelOffset;
+ }
+ return std::nullopt;
+ }
if (OnDiskTable) {
auto Iter = OnDiskTable->find(GUID);
if (Iter != OnDiskTable->end())
@@ -1070,9 +1100,27 @@ class SampleProfileFuncOffsetTable {
return std::nullopt;
}
+ /// Direct read-only array (`ArrayRef`) of function offsets aligned parallel
+ /// to the corresponding Eytzinger name span:
+ ArrayRef<support::ulittle32_t> getFuncOffsets() const {
+ assert(isEytzinger() &&
+ "Cannot call getFuncOffsets() on non-Eytzinger table");
+ return FuncOffsetSpan;
+ }
+
+ size_t getExpectedSize() const {
+ assert(isEytzinger() &&
+ "Cannot call getExpectedSize() on non-Eytzinger table");
+ return NameSpan.size();
+ }
+
+ bool isEytzinger() const { return FuncOffsetSpan.data() != nullptr; }
+
private:
llvm::DenseMap<hash_code, uint64_t> InMemoryTable;
std::unique_ptr<OnDiskTableType> OnDiskTable;
+ EytzingerTableSpan<support::ulittle64_t> NameSpan;
+ ArrayRef<support::ulittle32_t> FuncOffsetSpan;
};
/// SampleProfileReaderExtBinaryBase/SampleProfileWriterExtBinaryBase defines
@@ -1112,7 +1160,9 @@ class LLVM_ABI SampleProfileReaderExtBinaryBase
std::error_code readFuncMetadata(DenseSet<FunctionSamples *> &Profiles);
std::error_code readFuncMetadata();
std::error_code readFuncMetadata(FunctionSamples *FProfile);
- std::error_code readFuncOffsetTable();
+ std::error_code readFuncOffsetTable(bool IsEytzinger, bool IsCS);
+ std::error_code readEytzingerFuncOffsetTable(bool IsCS);
+ std::error_code readLegacyFuncOffsetTable();
std::error_code readFuncProfiles();
std::error_code readFuncProfiles(const DenseSet<StringRef> &FuncsToUse,
SampleProfileMap &Profiles);
diff --git a/llvm/include/llvm/ProfileData/SampleProfWriter.h b/llvm/include/llvm/ProfileData/SampleProfWriter.h
index ef368094d5889..b9797c458617a 100644
--- a/llvm/include/llvm/ProfileData/SampleProfWriter.h
+++ b/llvm/include/llvm/ProfileData/SampleProfWriter.h
@@ -417,7 +417,9 @@ class LLVM_ABI SampleProfileWriterExtBinaryBase
std::error_code writeNameTableSection(const SampleProfileMap &ProfileMap);
std::error_code
writeEytzingerNameTableSection(const SampleProfileMap &ProfileMap);
- std::error_code writeFuncOffsetTable();
+ std::error_code writeFuncOffsetTable(bool IsCS);
+ std::error_code writeEytzingerFuncOffsetTable(bool IsCS);
+ std::error_code writeLegacyFuncOffsetTable();
std::error_code writeProfileSymbolListSection();
std::error_code writeStringBasedProfileSymbolListSection();
std::error_code writeMD5ProfileSymbolListSection();
@@ -466,6 +468,8 @@ class LLVM_ABI SampleProfileWriterExtBinaryBase
MapVector<SampleContext, uint64_t> FuncOffsetTable;
// Whether to use MD5 to represent string.
bool UseMD5 = false;
+ size_t NumCS = 0;
+ size_t NumFlat = 0;
/// CSNameTable maps function context to its offset in SecCSNameTable section.
/// The offset will be used everywhere where the context is referenced.
diff --git a/llvm/lib/ProfileData/SampleProfReader.cpp b/llvm/lib/ProfileData/SampleProfReader.cpp
index f7e291729a5d9..eb8e064da198e 100644
--- a/llvm/lib/ProfileData/SampleProfReader.cpp
+++ b/llvm/lib/ProfileData/SampleProfReader.cpp
@@ -895,10 +895,18 @@ std::error_code SampleProfileReaderExtBinaryBase::readOneSection(
if (!M) {
Data = End;
} else {
+ bool IsEytzinger =
+ hasSecFlag(Entry, SecFuncOffsetFlags::SecFlagEytzinger);
+ bool IsFlat = hasSecFlag(Entry, SecCommonFlags::SecFlagFlat);
+ // An unflagged function offset table inherently indexes the primary
+ // context-sensitive symbol span.
+ bool IsCS = !IsFlat;
assert((!ProfileIsCS ||
- hasSecFlag(Entry, SecFuncOffsetFlags::SecFlagOrdered)) &&
- "func offset table should always be sorted in CS profile");
- if (std::error_code EC = readFuncOffsetTable())
+ hasSecFlag(Entry, SecFuncOffsetFlags::SecFlagOrdered) ||
+ IsEytzinger) &&
+ "func offset table should always be sorted or in Eytzinger BFS "
+ "order in CS profile");
+ if (std::error_code EC = readFuncOffsetTable(IsEytzinger, IsCS))
return EC;
}
break;
@@ -985,7 +993,36 @@ bool SampleProfileReaderExtBinaryBase::collectFuncsFromModule() {
return true;
}
-std::error_code SampleProfileReaderExtBinaryBase::readFuncOffsetTable() {
+std::error_code
+SampleProfileReaderExtBinaryBase::readFuncOffsetTable(bool IsEytzinger,
+ bool IsCS) {
+ if (IsEytzinger)
+ return readEytzingerFuncOffsetTable(IsCS);
+ return readLegacyFuncOffsetTable();
+}
+
+std::error_code
+SampleProfileReaderExtBinaryBase::readEytzingerFuncOffsetTable(bool IsCS) {
+ // If there are more than one function offset section, the profile associated
+ // with the previous section has to be done reading before next one is read.
+ FuncOffsetTable.reset();
+
+ size_t Size = End - Data;
+ size_t SpanSize = NameTable->getEytzingerSpan(IsCS).size();
+ if (Size != SpanSize * sizeof(uint32_t))
+ return sampleprof_error::malformed;
+
+ auto *Array = reinterpret_cast<const support::ulittle32_t *>(Data);
+ ArrayRef<support::ulittle32_t> Offsets(Array, SpanSize);
+
+ FuncOffsetTable.emplace(EytzingerMode, NameTable->getEytzingerSpan(IsCS),
+ Offsets);
+
+ Data = End;
+ return sampleprof_error::success;
+}
+
+std::error_code SampleProfileReaderExtBinaryBase::readLegacyFuncOffsetTable() {
// If there are more than one function offset section, the profile associated
// with the previous section has to be done reading before next one is read.
FuncOffsetTable.reset();
@@ -1032,6 +1069,21 @@ std::error_code SampleProfileReaderExtBinaryBase::readFuncProfiles(
}
}
+ if (FuncOffsetTable && FuncOffsetTable->isEytzinger() &&
+ useFuncOffsetList()) {
+ ArrayRef<support::ulittle32_t> Offsets = FuncOffsetTable->getFuncOffsets();
+ if (Offsets.size() != FuncOffsetTable->getExpectedSize())
+ return sampleprof_error::malformed;
+ for (const auto &[LocalIdx, RelOffset] : llvm::enumerate(Offsets)) {
+ if (RelOffset == UINT32_MAX)
+ continue;
+ const uint8_t *FuncProfileAddr = Start + RelOffset;
+ if (std::error_code EC = readFuncProfile(FuncProfileAddr, Profiles))
+ return EC;
+ }
+ return sampleprof_error::success;
+ }
+
if (ProfileIsCS) {
assert(useFuncOffsetList());
DenseSet<uint64_t> FuncGuidsToUse;
@@ -1669,6 +1721,8 @@ static std::string getSecFlagsStr(const SecHdrTableEntry &Entry) {
case SecFuncOffsetTable:
if (hasSecFlag(Entry, SecFuncOffsetFlags::SecFlagOrdered))
Flags.append("ordered,");
+ if (hasSecFlag(Entry, SecFuncOffsetFlags::SecFlagEytzinger))
+ Flags.append("eytzinger,");
break;
case SecFuncMetadata:
if (hasSecFlag(Entry, SecFuncMetadataFlags::SecFlagIsProbeBased))
diff --git a/llvm/lib/ProfileData/SampleProfWriter.cpp b/llvm/lib/ProfileData/SampleProfWriter.cpp
index e17b080659927..4d87f376702ae 100644
--- a/llvm/lib/ProfileData/SampleProfWriter.cpp
+++ b/llvm/lib/ProfileData/SampleProfWriter.cpp
@@ -58,7 +58,8 @@ static cl::opt<bool>
static cl::opt<bool> WriteEytzingerNameTables(
"sample-profile-write-eytzinger-name-tables", cl::init(false), cl::Hidden,
- cl::desc("Write Eytzinger 3-span layout for NameTable"));
+ cl::desc("Write Eytzinger 3-span layout for NameTable and parallel "
+ "FuncOffsetTable"));
namespace llvm {
namespace support {
@@ -277,7 +278,57 @@ SampleProfileWriterExtBinaryBase::writeSample(const FunctionSamples &S) {
return writeBody(S);
}
-std::error_code SampleProfileWriterExtBinaryBase::writeFuncOffsetTable() {
+std::error_code
+SampleProfileWriterExtBinaryBase::writeFuncOffsetTable(bool IsCS) {
+ if (WriteEytzingerNameTables)
+ return writeEytzingerFuncOffsetTable(IsCS);
+ return writeLegacyFuncOffsetTable();
+}
+
+std::error_code
+SampleProfileWriterExtBinaryBase::writeEytzingerFuncOffsetTable(bool IsCS) {
+ assert((NumCS + NumFlat > 0 || FuncOffsetTable.empty()) &&
+ "SecNameTable must be written before SecFuncOffsetTable to establish "
+ "Eytzinger indices!");
+
+ size_t SpanSize = IsCS ? NumCS : NumFlat;
+ size_t BaseIdx = IsCS ? 0 : NumCS;
+
+ std::vector<support::ulittle32_t> FuncOffsets(
+ SpanSize, support::ulittle32_t(UINT32_MAX));
+
+ // Populate the function offset array parallel to the Eytzinger span.
+ for (const auto &[Context, RelativeOffset] : FuncOffsetTable) {
+ if (RelativeOffset >= UINT32_MAX)
+ return sampleprof_error::too_large;
+
+ FunctionId FId = Context.getFunction();
+ auto It = NameTable.find(FId);
+ if (It == NameTable.end())
+ continue;
+
+ size_t GlobalIdx = It->second;
+ if (GlobalIdx < BaseIdx || (GlobalIdx - BaseIdx) >= SpanSize)
+ continue;
+
+ size_t LocalIdx = GlobalIdx - BaseIdx;
+ assert(
+ FuncOffsets[LocalIdx] == UINT32_MAX &&
+ "Function offset slot already populated; duplicate GUID or collision!");
+ FuncOffsets[LocalIdx] = static_cast<uint32_t>(RelativeOffset);
+ }
+
+ assert(!llvm::is_contained(FuncOffsets, support::ulittle32_t(UINT32_MAX)) &&
+ "Unpopulated slot in Eytzinger function offset array!");
+
+ OutputStream->write(reinterpret_cast<const char *>(FuncOffsets.data()),
+ SpanSize * sizeof(support::ulittle32_t));
+ addSectionFlag(SecFuncOffsetTable, SecFuncOffsetFlags::SecFlagEytzinger);
+ FuncOffsetTable.clear();
+ return sampleprof_error::success;
+}
+
+std::error_code SampleProfileWriterExtBinaryBase::writeLegacyFuncOffsetTable() {
auto &OS = *OutputStream;
// Write out the table size.
@@ -464,6 +515,10 @@ class EytzingerNameTable {
OS.write(reinterpret_cast<const char *>(Table.data()),
Table.size() * sizeof(support::ulittle64_t));
}
+
+ size_t size(EytzingerSpan S) const {
+ return Spans[static_cast<size_t>(S)].size();
+ }
};
} // end anonymous namespace
@@ -479,7 +534,9 @@ SampleProfileWriterExtBinaryBase::writeEytzingerNameTableSection(
const SampleContext &Ctx = I.second.getContext();
uint64_t GUID = Ctx.getFunction().getHashCode();
if (TopLevelGUIDs.insert(GUID).second) {
- if (I.second.isContextSensitiveTopLevel())
+ // In single-table default layouts, unify all top-level symbols in the CS
+ // partition so they match the single unflagged function offset table.
+ if (SecLayout != CtxSplitLayout || I.second.isContextSensitiveTopLevel())
CSKeys.emplace_back(GUID);
else
FlatKeys.emplace_back(GUID);
@@ -502,6 +559,8 @@ SampleProfileWriterExtBinaryBase::writeEytzingerNameTableSection(
Idx = Tables.findGlobalIdx(FId.getHashCode());
Tables.write(*OutputStream);
+ NumCS = Tables.size(EytzingerSpan::CS);
+ NumFlat = Tables.size(EytzingerSpan::Flat);
return sampleprof_error::success;
}
@@ -606,10 +665,16 @@ std::error_code SampleProfileWriterExtBinaryBase::writeOneSection(
if (std::error_code EC = writeFuncProfiles(ProfileMap))
return EC;
break;
- case SecFuncOffsetTable:
- if (auto EC = writeFuncOffsetTable())
+ case SecFuncOffsetTable: {
+ bool IsFlat =
+ hasSecFlag(SectionHdrLayout[LayoutIdx], SecCommonFlags::SecFlagFlat);
+ // An unflagged function offset table inherently indexes the primary
+ // context-sensitive symbol span.
+ bool IsCS = !IsFlat;
+ if (auto EC = writeFuncOffsetTable(IsCS))
return EC;
break;
+ }
case SecFuncMetadata:
if (std::error_code EC = writeFuncMetadata(ProfileMap))
return EC;
diff --git a/llvm/test/tools/llvm-profdata/eytzinger-split-nametable-partition.test b/llvm/test/tools/llvm-profdata/eytzinger-split-nametable-partition.test
index f1b40d8c30694..0de8b06b59fb2 100644
--- a/llvm/test/tools/llvm-profdata/eytzinger-split-nametable-partition.test
+++ b/llvm/test/tools/llvm-profdata/eytzinger-split-nametable-partition.test
@@ -9,9 +9,9 @@
# CHECK: ProfileSummarySection - Offset: {{.*}}, Size: {{.*}}, Flags: {}
# CHECK: NameTableSection - Offset: {{.*}}, Size: {{.*}}, Flags: {eytzinger,fixlenmd5}
-# CHECK: FuncOffsetTableSection - Offset: {{.*}}, Size: {{.*}}, Flags: {}
+# CHECK: FuncOffsetTableSection - Offset: {{.*}}, Size: {{.*}}, Flags: {eytzinger}
# CHECK: LBRProfileSection - Offset: {{.*}}, Size: {{.*}}, Flags: {}
-# CHECK: FuncOffsetTableSection - Offset: {{.*}}, Size: {{.*}}, Flags: {flat}
+# CHECK: FuncOffsetTableSection - Offset: {{.*}}, Size: {{.*}}, Flags: {flat,eytzinger}
# CHECK: LBRProfileSection - Offset: {{.*}}, Size: {{.*}}, Flags: {flat}
# COUNTS: [1, 1, 1]
>From 367f5cf7dbcb33cfb6232ec2f24bdabd32cf5b12 Mon Sep 17 00:00:00 2001
From: Kazu Hirata <kazu at google.com>
Date: Fri, 7 Aug 2026 14:21:13 -0700
Subject: [PATCH 2/3] Address comments.
---
llvm/lib/ProfileData/SampleProfWriter.cpp | 10 +++++++++-
llvm/test/tools/llvm-profdata/cs-sample-profile.test | 3 +++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/ProfileData/SampleProfWriter.cpp b/llvm/lib/ProfileData/SampleProfWriter.cpp
index 4d87f376702ae..22c63b660a4ce 100644
--- a/llvm/lib/ProfileData/SampleProfWriter.cpp
+++ b/llvm/lib/ProfileData/SampleProfWriter.cpp
@@ -280,8 +280,13 @@ SampleProfileWriterExtBinaryBase::writeSample(const FunctionSamples &S) {
std::error_code
SampleProfileWriterExtBinaryBase::writeFuncOffsetTable(bool IsCS) {
- if (WriteEytzingerNameTables)
+ if (WriteEytzingerNameTables) {
+ // Eytzinger layout requires MD5 representation and does not support
+ // multi-context Context-Sensitive profiles.
+ if (!UseMD5 || FunctionSamples::ProfileIsCS)
+ return sampleprof_error::unsupported_writing_format;
return writeEytzingerFuncOffsetTable(IsCS);
+ }
return writeLegacyFuncOffsetTable();
}
@@ -460,6 +465,9 @@ std::error_code SampleProfileWriterExtBinaryBase::writeNameTableSection(
}
if (UseMD5 && WriteEytzingerNameTables) {
+ // Eytzinger name tables do not support Context-Sensitive profiles.
+ if (FunctionSamples::ProfileIsCS)
+ return sampleprof_error::unsupported_writing_format;
if (auto EC = writeEytzingerNameTableSection(ProfileMap))
return EC;
return sampleprof_error::success;
diff --git a/llvm/test/tools/llvm-profdata/cs-sample-profile.test b/llvm/test/tools/llvm-profdata/cs-sample-profile.test
index ce69a1ffd61a6..aaafca2bfe542 100644
--- a/llvm/test/tools/llvm-profdata/cs-sample-profile.test
+++ b/llvm/test/tools/llvm-profdata/cs-sample-profile.test
@@ -4,3 +4,6 @@ RUN: llvm-profdata merge --sample --extbinary %p/Inputs/cs-sample.proftext -o %t
RUN: diff -b %t1.proftext %S/Inputs/cs-sample.proftext
RUN: llvm-profdata show --sample -show-sec-info-only %t.prof | FileCheck %s
CHECK: FunctionMetadata {{.*}} Flags: {attr}
+
+RUN: not llvm-profdata merge --sample --extbinary --use-md5 --sample-profile-write-eytzinger-name-tables %p/Inputs/cs-sample.proftext -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR-CS-EYTZ
+ERR-CS-EYTZ: error: {{.*}}Profile encoding format unsupported for writing operations
>From c30b6dbd1edf0b3fefe36d808be1cb9f685ba1e3 Mon Sep 17 00:00:00 2001
From: Kazu Hirata <kazu at google.com>
Date: Sun, 9 Aug 2026 23:44:43 -0700
Subject: [PATCH 3/3] Address comments.
---
llvm/include/llvm/ProfileData/SampleProfReader.h | 16 +++++++++++-----
llvm/lib/ProfileData/SampleProfWriter.cpp | 3 ++-
.../eytzinger-split-nametable-partition.test | 9 +++++++++
3 files changed, 22 insertions(+), 6 deletions(-)
diff --git a/llvm/include/llvm/ProfileData/SampleProfReader.h b/llvm/include/llvm/ProfileData/SampleProfReader.h
index 6eb5760d8e1e6..e0d140c0c8d2c 100644
--- a/llvm/include/llvm/ProfileData/SampleProfReader.h
+++ b/llvm/include/llvm/ProfileData/SampleProfReader.h
@@ -1042,6 +1042,8 @@ inline constexpr EytzingerModeT EytzingerMode{};
/// modification in on-disk mode.
class SampleProfileFuncOffsetTable {
public:
+ enum class TableMode { InMemory, OnDisk, Eytzinger };
+
using OnDiskTableType =
llvm::OnDiskIterableChainedHashTable<FuncOffsetHashTableInfo>;
@@ -1054,14 +1056,16 @@ class SampleProfileFuncOffsetTable {
operator=(SampleProfileFuncOffsetTable &&) = delete;
explicit SampleProfileFuncOffsetTable(InMemoryModeT,
- size_t InitialCapacity = 0) {
+ size_t InitialCapacity = 0)
+ : Mode(TableMode::InMemory) {
InMemoryTable.reserve(InitialCapacity);
}
SampleProfileFuncOffsetTable(
EytzingerModeT, EytzingerTableSpan<support::ulittle64_t> NameSpan,
ArrayRef<support::ulittle32_t> FuncOffsetSpan)
- : NameSpan(NameSpan), FuncOffsetSpan(FuncOffsetSpan) {}
+ : Mode(TableMode::Eytzinger), NameSpan(NameSpan),
+ FuncOffsetSpan(FuncOffsetSpan) {}
/// Insert a function GUID and its profile offset into the in-memory map.
/// Enforces that the on-disk table must not have been set first.
@@ -1073,7 +1077,8 @@ class SampleProfileFuncOffsetTable {
/// Instantiate the on-disk chained hash table using raw stream pointers.
SampleProfileFuncOffsetTable(OnDiskModeT, const uint8_t *Buckets,
- const uint8_t *Payload, const uint8_t *Base) {
+ const uint8_t *Payload, const uint8_t *Base)
+ : Mode(TableMode::OnDisk) {
OnDiskTable.reset(OnDiskTableType::Create(Buckets, Payload, Base));
}
@@ -1101,7 +1106,7 @@ class SampleProfileFuncOffsetTable {
}
/// Direct read-only array (`ArrayRef`) of function offsets aligned parallel
- /// to the corresponding Eytzinger name span:
+ /// to the corresponding Eytzinger name span.
ArrayRef<support::ulittle32_t> getFuncOffsets() const {
assert(isEytzinger() &&
"Cannot call getFuncOffsets() on non-Eytzinger table");
@@ -1114,9 +1119,10 @@ class SampleProfileFuncOffsetTable {
return NameSpan.size();
}
- bool isEytzinger() const { return FuncOffsetSpan.data() != nullptr; }
+ bool isEytzinger() const { return Mode == TableMode::Eytzinger; }
private:
+ TableMode Mode;
llvm::DenseMap<hash_code, uint64_t> InMemoryTable;
std::unique_ptr<OnDiskTableType> OnDiskTable;
EytzingerTableSpan<support::ulittle64_t> NameSpan;
diff --git a/llvm/lib/ProfileData/SampleProfWriter.cpp b/llvm/lib/ProfileData/SampleProfWriter.cpp
index 22c63b660a4ce..1962c83e6e7e6 100644
--- a/llvm/lib/ProfileData/SampleProfWriter.cpp
+++ b/llvm/lib/ProfileData/SampleProfWriter.cpp
@@ -465,7 +465,8 @@ std::error_code SampleProfileWriterExtBinaryBase::writeNameTableSection(
}
if (UseMD5 && WriteEytzingerNameTables) {
- // Eytzinger name tables do not support Context-Sensitive profiles.
+ // Eytzinger name tables do not support CSSPGO profiles
+ // (FunctionSamples::ProfileIsCS).
if (FunctionSamples::ProfileIsCS)
return sampleprof_error::unsupported_writing_format;
if (auto EC = writeEytzingerNameTableSection(ProfileMap))
diff --git a/llvm/test/tools/llvm-profdata/eytzinger-split-nametable-partition.test b/llvm/test/tools/llvm-profdata/eytzinger-split-nametable-partition.test
index 0de8b06b59fb2..e27fd8ed6081c 100644
--- a/llvm/test/tools/llvm-profdata/eytzinger-split-nametable-partition.test
+++ b/llvm/test/tools/llvm-profdata/eytzinger-split-nametable-partition.test
@@ -2,6 +2,8 @@
# RUN: --sample-profile-write-eytzinger-name-tables %s -o %t.xfdo
# RUN: llvm-profdata show --sample --show-sec-info-only %t.xfdo > %t.secinfo
# RUN: FileCheck %s -input-file %t.secinfo
+# RUN: llvm-profdata merge --sample --text %t.xfdo -o %t.text
+# RUN: FileCheck %s --check-prefix=TEXT -input-file %t.text
# Extract and verify the Eytzinger span counts (NumCS, NumFlat,
# NumInlinees) from NameTableSection to ensure correct symbol
# partitioning.
@@ -16,6 +18,13 @@
# COUNTS: [1, 1, 1]
+# TEXT: 15822663052811949562:1000:10
+# TEXT: 1: 1000
+# TEXT: 2: 6699318081062747564:500
+# TEXT: 1: 500
+# TEXT: 110832587303349897:100:5
+# TEXT: 1: 100
+
main:1000:10
1: 1000
2: foo:500
More information about the llvm-commits
mailing list