[llvm] [ProfileData] Add traits for on-disk function offset hash table (NFC) (PR #202110)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Jun 7 00:50:55 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-pgo
Author: Kazu Hirata (kazutakahirata)
<details>
<summary>Changes</summary>
This patch introduces serialization helper classes (traits) for the
on-disk chained hash table that will be used to index function offsets
in the SecFuncOffsetTable section:
- FuncOffsetHashTableWriterInfo (in SampleProfWriter.h) for writing.
- FuncOffsetHashTableInfo (in SampleProfReader.h) for reading.
These traits map a 64-bit function name GUID to a 32-bit byte offset
pointing into the SecLBRProfile section. This index structure is
intended to replace the flat layout of the SecFuncOffsetTable section
in the upcoming v104 format. This will allow the compiler to query the
offset of a function sample without having to parse the entire
SecFuncOffsetTable section at startup.
While these two trait classes share identical boilerplate for type
definitions and key comparison, they are kept separate to maintain
clean interface separation between the reader and writer headers. We
also choose not to define a shared base class in SampleProf.h to keep
that common header free of serialization-specific details.
An isolated unit test is added to verify serialization and deserialization.
This is Phase 1 of the RFC: Faster Sample Profile Loading.
RFC: https://discourse.llvm.org/t/rfc-faster-sample-profile-loading/90957/4
---
Full diff: https://github.com/llvm/llvm-project/pull/202110.diff
3 Files Affected:
- (modified) llvm/include/llvm/ProfileData/SampleProfReader.h (+43)
- (modified) llvm/include/llvm/ProfileData/SampleProfWriter.h (+48)
- (modified) llvm/unittests/ProfileData/SampleProfTest.cpp (+44)
``````````diff
diff --git a/llvm/include/llvm/ProfileData/SampleProfReader.h b/llvm/include/llvm/ProfileData/SampleProfReader.h
index 71140af3b25e3..08e458479162f 100644
--- a/llvm/include/llvm/ProfileData/SampleProfReader.h
+++ b/llvm/include/llvm/ProfileData/SampleProfReader.h
@@ -238,6 +238,7 @@
#include "llvm/Support/Discriminator.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/OnDiskHashTable.h"
#include <cstdint>
#include <list>
#include <memory>
@@ -783,6 +784,48 @@ class LLVM_ABI SampleProfileReaderRawBinary : public SampleProfileReaderBinary {
static bool hasFormat(const MemoryBuffer &Buffer);
};
+/// Trait class for reading the on-disk function offset hash table mapping
+/// function name GUIDs to their offsets in the SecLBRProfile section.
+class FuncOffsetHashTableInfo {
+public:
+ using key_type = uint64_t;
+ using key_type_ref = uint64_t;
+ using data_type = uint64_t; // Offset
+ using data_type_ref = uint64_t;
+ using hash_value_type = uint32_t;
+ using offset_type = uint32_t;
+ using internal_key_type = uint64_t;
+ using external_key_type = uint64_t;
+
+ static hash_value_type ComputeHash(key_type_ref Key) {
+ return static_cast<hash_value_type>(Key);
+ }
+
+ static bool EqualKey(key_type_ref LHS, key_type_ref RHS) {
+ return LHS == RHS;
+ }
+
+ static key_type GetInternalKey(key_type_ref Key) { return Key; }
+ static external_key_type GetExternalKey(internal_key_type Key) { return Key; }
+
+ static std::pair<offset_type, offset_type>
+ ReadKeyDataLength(const unsigned char *&D) {
+ // Implicit lengths: do NOT read or advance pointer D.
+ return {8, 4};
+ }
+
+ static key_type ReadKey(const unsigned char *D, offset_type Len) {
+ assert(Len == 8 && "Key length must be 8");
+ return support::endian::read64le(D);
+ }
+
+ static data_type ReadData(key_type_ref K, const unsigned char *D,
+ offset_type Len) {
+ assert(Len == 4 && "Data length must be 4");
+ return support::endian::read32le(D);
+ }
+};
+
/// SampleProfileReaderExtBinaryBase/SampleProfileWriterExtBinaryBase defines
/// the basic structure of the extensible binary format.
/// The format is organized in sections except the magic and version number
diff --git a/llvm/include/llvm/ProfileData/SampleProfWriter.h b/llvm/include/llvm/ProfileData/SampleProfWriter.h
index 9dbeaf56509b0..b796de50451e6 100644
--- a/llvm/include/llvm/ProfileData/SampleProfWriter.h
+++ b/llvm/include/llvm/ProfileData/SampleProfWriter.h
@@ -17,6 +17,7 @@
#include "llvm/IR/ProfileSummary.h"
#include "llvm/ProfileData/SampleProf.h"
#include "llvm/Support/Compiler.h"
+#include "llvm/Support/EndianStream.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdint>
@@ -272,6 +273,53 @@ const std::array<SmallVector<SecHdrTableEntry, 8>, NumOfLayout>
{SecFuncMetadata, 0, 0, 0, 0}}),
};
+/// Trait class for writing the on-disk function offset hash table mapping
+/// function name GUIDs to their offsets in the SecLBRProfile section.
+class FuncOffsetHashTableWriterInfo {
+public:
+ using key_type = uint64_t;
+ using key_type_ref = uint64_t;
+ using data_type = uint64_t; // Offset
+ using data_type_ref = uint64_t;
+ using hash_value_type = uint32_t;
+ using offset_type = uint32_t;
+ using internal_key_type = uint64_t;
+ using external_key_type = uint64_t;
+
+ static hash_value_type ComputeHash(key_type_ref Key) {
+ return static_cast<hash_value_type>(Key);
+ }
+
+ static bool EqualKey(key_type_ref LHS, key_type_ref RHS) {
+ return LHS == RHS;
+ }
+
+ static key_type GetInternalKey(key_type_ref Key) { return Key; }
+ static external_key_type GetExternalKey(internal_key_type Key) { return Key; }
+
+ static std::pair<offset_type, offset_type>
+ EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) {
+ // Implicit lengths: do NOT write anything to Out.
+ return {8, 4};
+ }
+
+ static void EmitKey(raw_ostream &Out, key_type_ref K, offset_type Len) {
+ using namespace llvm::support;
+ endian::Writer LE(Out, llvm::endianness::little);
+ assert(Len == 8 && "Key length must be 8");
+ LE.write<uint64_t>(K);
+ }
+
+ static void EmitData(raw_ostream &Out, key_type_ref K, data_type_ref V,
+ offset_type Len) {
+ using namespace llvm::support;
+ endian::Writer LE(Out, llvm::endianness::little);
+ assert(Len == 4 && "Data length must be 4");
+ assert(V <= std::numeric_limits<uint32_t>::max() && "Offset overflow");
+ LE.write<uint32_t>(static_cast<uint32_t>(V));
+ }
+};
+
class LLVM_ABI SampleProfileWriterExtBinaryBase
: public SampleProfileWriterBinary {
using SampleProfileWriterBinary::SampleProfileWriterBinary;
diff --git a/llvm/unittests/ProfileData/SampleProfTest.cpp b/llvm/unittests/ProfileData/SampleProfTest.cpp
index 3abba47430932..00f5627683616 100644
--- a/llvm/unittests/ProfileData/SampleProfTest.cpp
+++ b/llvm/unittests/ProfileData/SampleProfTest.cpp
@@ -19,6 +19,7 @@
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/OnDiskHashTable.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Testing/Support/SupportHelpers.h"
@@ -490,4 +491,47 @@ TEST_F(SampleProfTest, none_suffix_elision_text) {
testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "none", Expected);
}
+TEST_F(SampleProfTest, FuncOffsetHashTable) {
+ std::vector<std::pair<uint64_t, uint64_t>> TestData = {
+ {0x1111111122222222ULL, 100},
+ {0x3333333344444444ULL, 250},
+ {0x5555555566666666ULL, 1000},
+ };
+
+ SmallVector<char, 128> Buffer;
+ raw_svector_ostream OS(Buffer);
+
+ FuncOffsetHashTableWriterInfo WriterInfo;
+ OnDiskChainedHashTableGenerator<FuncOffsetHashTableWriterInfo> Generator;
+
+ for (const auto &[Name, Offset] : TestData)
+ Generator.insert(Name, Offset);
+
+ // Add padding to avoid bucket offset 0.
+ OS.write("PAD ", 4);
+ uint32_t BucketTableOffset = Generator.Emit(OS, WriterInfo);
+
+ const unsigned char *Start =
+ reinterpret_cast<const unsigned char *>(Buffer.data());
+
+ const unsigned char *Buckets = Start + BucketTableOffset;
+ const unsigned char *Payload = Start + 4;
+
+ auto Table =
+ std::unique_ptr<OnDiskIterableChainedHashTable<FuncOffsetHashTableInfo>>(
+ OnDiskIterableChainedHashTable<FuncOffsetHashTableInfo>::Create(
+ Buckets, Payload, Start));
+
+ ASSERT_TRUE(Table);
+
+ for (const auto &[Name, Offset] : TestData) {
+ auto Iter = Table->find(Name);
+ ASSERT_TRUE(Iter != Table->end());
+ ASSERT_EQ(*Iter, Offset);
+ }
+
+ auto Iter = Table->find(0x9999999999999999ULL);
+ ASSERT_TRUE(Iter == Table->end());
+}
+
} // end anonymous namespace
``````````
</details>
https://github.com/llvm/llvm-project/pull/202110
More information about the llvm-commits
mailing list