[llvm] [ProfileData] Support format version 104 for extensible binary sample profiles (PR #206297)

via llvm-commits llvm-commits at lists.llvm.org
Sat Jun 27 18:29:54 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 adds initial support for format version 104 in extensible
binary sample profiles to support the upcoming on-disk hash table.
This patch sets up the versioning scheme in the reader and writer as a
preparation step without actually introducing the on-disk hash table.

The reader is updated to support format version 104.  The writer can
now write format version 104 profiles when requested via
-sample-profile-format-version, but continues to default to version
103 as version 104 is a work in progress.  We plan to promote the
default version to 104 once the on-disk hash table support is fully
integrated in a subsequent patch.

RFC: https://discourse.llvm.org/t/rfc-faster-sample-profile-loading/90957/4

Assisted-by: Antigravity


---
Full diff: https://github.com/llvm/llvm-project/pull/206297.diff


6 Files Affected:

- (modified) llvm/include/llvm/ProfileData/SampleProf.h (+17) 
- (modified) llvm/include/llvm/ProfileData/SampleProfReader.h (+6) 
- (modified) llvm/include/llvm/ProfileData/SampleProfWriter.h (+10) 
- (modified) llvm/lib/ProfileData/SampleProfReader.cpp (+2-1) 
- (modified) llvm/lib/ProfileData/SampleProfWriter.cpp (+11-1) 
- (modified) llvm/unittests/ProfileData/SampleProfTest.cpp (+93) 


``````````diff
diff --git a/llvm/include/llvm/ProfileData/SampleProf.h b/llvm/include/llvm/ProfileData/SampleProf.h
index cb0344e848c34..ba4ab289a1838 100644
--- a/llvm/include/llvm/ProfileData/SampleProf.h
+++ b/llvm/include/llvm/ProfileData/SampleProf.h
@@ -116,6 +116,23 @@ static inline uint64_t SPMagic(SampleProfileFormat Format = SPF_Binary) {
          uint64_t('2') << (64 - 56) | uint64_t(Format);
 }
 
+// The oldest version of the extensible binary format we support.
+static constexpr uint64_t MinSupportedVersion = 103;
+
+// The default version of the extensible binary profile format written by the
+// compiler.  We default to v103 as v104 is work in progress.
+static constexpr uint64_t DefaultVersion = 103;
+
+// The latest supported version of the extensible binary profile format.
+static constexpr uint64_t LatestVersion = 104;
+
+// Query if a given format version is supported by this compiler.
+static inline bool formatVersionIsSupported(uint64_t Version) {
+  return Version >= MinSupportedVersion && Version <= LatestVersion;
+}
+
+// Unused.  Retained for downstream uses only.
+LLVM_DEPRECATED("Use DefaultVersion or LatestVersion instead", "DefaultVersion")
 static inline uint64_t SPVersion() { return 103; }
 
 // Section Type used by SampleProfileExtBinaryBaseReader and
diff --git a/llvm/include/llvm/ProfileData/SampleProfReader.h b/llvm/include/llvm/ProfileData/SampleProfReader.h
index 251e6a8f4843a..de85a5aeeaa6e 100644
--- a/llvm/include/llvm/ProfileData/SampleProfReader.h
+++ b/llvm/include/llvm/ProfileData/SampleProfReader.h
@@ -515,6 +515,9 @@ class SampleProfileReader {
   /// Print all the profiles on stream \p OS in the JSON format.
   LLVM_ABI void dumpJson(raw_ostream &OS = dbgs());
 
+  /// Return the format version of the profile. For tests only.
+  uint64_t getFormatVersion() const { return FormatVersion; }
+
   /// Return the samples collected for function \p F.
   FunctionSamples *getSamplesFor(const Function &F) {
     // The function name may have been updated by adding suffix. Call
@@ -697,6 +700,9 @@ class SampleProfileReader {
   /// Whether the function profiles use FS discriminators.
   bool ProfileIsFS = false;
 
+  /// Format version of the profile.
+  uint64_t FormatVersion = 0;
+
   /// If true, the profile has vtable profiles and reader should decode them
   /// to parse profiles correctly.
   bool ReadVTableProf = false;
diff --git a/llvm/include/llvm/ProfileData/SampleProfWriter.h b/llvm/include/llvm/ProfileData/SampleProfWriter.h
index 3b22f113615e1..df077305525c2 100644
--- a/llvm/include/llvm/ProfileData/SampleProfWriter.h
+++ b/llvm/include/llvm/ProfileData/SampleProfWriter.h
@@ -132,6 +132,13 @@ class LLVM_ABI SampleProfileWriter {
   virtual void setPartialProfile() {}
   virtual void setUseCtxSplitLayout() {}
 
+  void setFormatVersion(uint64_t V) {
+    assert(sampleprof::formatVersionIsSupported(V) &&
+           "Unsupported format version");
+    FormatVersion = V;
+  }
+  uint64_t getFormatVersion() const { return FormatVersion; }
+
 protected:
   SampleProfileWriter(std::unique_ptr<raw_ostream> &OS)
       : OutputStream(std::move(OS)) {}
@@ -162,6 +169,9 @@ class LLVM_ABI SampleProfileWriter {
 
   /// Profile format.
   SampleProfileFormat Format = SPF_None;
+
+  /// Format version to write.
+  uint64_t FormatVersion = sampleprof::DefaultVersion;
 };
 
 /// Sample-based profile writer (text format).
diff --git a/llvm/lib/ProfileData/SampleProfReader.cpp b/llvm/lib/ProfileData/SampleProfReader.cpp
index 14fdaf6581efe..98d36bd3eff7a 100644
--- a/llvm/lib/ProfileData/SampleProfReader.cpp
+++ b/llvm/lib/ProfileData/SampleProfReader.cpp
@@ -1636,8 +1636,9 @@ std::error_code SampleProfileReaderBinary::readMagicIdent() {
   auto Version = readNumber<uint64_t>();
   if (std::error_code EC = Version.getError())
     return EC;
-  else if (*Version != SPVersion())
+  else if (!formatVersionIsSupported(*Version))
     return sampleprof_error::unsupported_version;
+  FormatVersion = *Version;
 
   return sampleprof_error::success;
 }
diff --git a/llvm/lib/ProfileData/SampleProfWriter.cpp b/llvm/lib/ProfileData/SampleProfWriter.cpp
index e5f31348578b8..eaeed95f6c585 100644
--- a/llvm/lib/ProfileData/SampleProfWriter.cpp
+++ b/llvm/lib/ProfileData/SampleProfWriter.cpp
@@ -46,6 +46,10 @@ static cl::opt<bool> ExtBinaryWriteVTableTypeProf(
     "extbinary-write-vtable-type-prof", cl::init(false), cl::Hidden,
     cl::desc("Write vtable type profile in ext-binary sample profile writer"));
 
+static cl::opt<uint64_t> RequestedVersion(
+    "sample-profile-format-version", cl::init(DefaultVersion), cl::Hidden,
+    cl::desc("Format version to write for extensible binary profiles"));
+
 namespace llvm {
 namespace support {
 namespace endian {
@@ -757,7 +761,7 @@ SampleProfileWriterBinary::writeMagicIdent(SampleProfileFormat Format) {
   auto &OS = *OutputStream;
   // Write file magic identifier.
   encodeULEB128(SPMagic(Format), OS);
-  encodeULEB128(SPVersion(), OS);
+  encodeULEB128(FormatVersion, OS);
   return sampleprof_error::success;
 }
 
@@ -982,6 +986,12 @@ SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
     return EC;
 
   Writer->Format = Format;
+  if (Format != SPF_Ext_Binary)
+    Writer->setFormatVersion(DefaultVersion);
+  else if (formatVersionIsSupported(RequestedVersion))
+    Writer->setFormatVersion(RequestedVersion);
+  else
+    return sampleprof_error::unsupported_version;
   return std::move(Writer);
 }
 
diff --git a/llvm/unittests/ProfileData/SampleProfTest.cpp b/llvm/unittests/ProfileData/SampleProfTest.cpp
index 0158b584f8504..584d7fbec1e38 100644
--- a/llvm/unittests/ProfileData/SampleProfTest.cpp
+++ b/llvm/unittests/ProfileData/SampleProfTest.cpp
@@ -18,6 +18,7 @@
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/ErrorOr.h"
 #include "llvm/Support/FileSystem.h"
+#include "llvm/Support/LEB128.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/OnDiskHashTable.h"
 #include "llvm/Support/VirtualFileSystem.h"
@@ -138,6 +139,60 @@ struct SampleProfTest : ::testing::Test {
     delete PS;
   }
 
+  // Write a minimal profile with a specific format version to an in-memory
+  // buffer.
+  ErrorOr<SmallVector<char, 128>> writeProfileToBuffer(uint64_t Version) {
+    SmallVector<char, 128> Buffer;
+    std::unique_ptr<raw_ostream> OS =
+        std::make_unique<raw_svector_ostream>(Buffer);
+    auto WriterOrErr =
+        SampleProfileWriter::create(OS, SampleProfileFormat::SPF_Ext_Binary);
+    if (std::error_code EC = WriterOrErr.getError())
+      return EC;
+    auto Writer = std::move(WriterOrErr.get());
+    Writer->setFormatVersion(Version);
+
+    StringRef FooName("_Z3fooi");
+    FunctionSamples FooSamples;
+    FooSamples.setFunction(FunctionId(FooName));
+    FooSamples.addTotalSamples(1);
+
+    SampleProfileMap Profiles;
+    Profiles[FooName] = std::move(FooSamples);
+
+    if (std::error_code EC = Writer->write(Profiles))
+      return EC;
+    Writer->getOutputStream().flush();
+    Writer.reset();
+    return Buffer;
+  }
+
+  // Write a raw profile header (Magic + Version) directly to a buffer.
+  // This bypasses the writer validation and is used to test reader error
+  // handling.
+  SmallVector<char, 128> writeRawHeaderToBuffer(uint64_t Version) {
+    SmallVector<char, 128> Buffer;
+    raw_svector_ostream OS(Buffer);
+    encodeULEB128(SPMagic(SPF_Ext_Binary), OS);
+    encodeULEB128(Version, OS);
+    return Buffer;
+  }
+
+  // Read the format version from a profile stored in an in-memory buffer.
+  ErrorOr<uint64_t> readVersionFromBuffer(ArrayRef<char> Buffer) {
+    std::unique_ptr<MemoryBuffer> MemBuffer = MemoryBuffer::getMemBuffer(
+        StringRef(Buffer.data(), Buffer.size()), "profile",
+        /*RequiresNullTerminator*/ false);
+    auto FS = vfs::getRealFileSystem();
+    auto ReaderOrErr = SampleProfileReader::create(MemBuffer, Context, *FS);
+    if (std::error_code EC = ReaderOrErr.getError())
+      return EC;
+    auto Reader = std::move(ReaderOrErr.get());
+    if (std::error_code EC = Reader->readHeader())
+      return EC;
+    return Reader->getFormatVersion();
+  }
+
   void testRoundTrip(SampleProfileFormat Format, bool Remap, bool UseMD5) {
     TempFile ProfileFile("profile", "", "", /*Unique*/ true);
     createWriter(Format, ProfileFile.path());
@@ -596,4 +651,42 @@ TEST_F(SampleProfTest, SampleProfileFuncOffsetTableOnDisk) {
   EXPECT_EQ(Table.lookup(0x1111111122222222ULL), std::nullopt);
 }
 
+// Verify that requesting format version 103 results in a version 103 profile.
+TEST_F(SampleProfTest, SampleProfileFormatVersion103) {
+  auto BufferOrErr = writeProfileToBuffer(103);
+  ASSERT_TRUE(NoError(BufferOrErr.getError()));
+  auto Buffer = std::move(*BufferOrErr);
+
+  auto ReadVersionOrErr = readVersionFromBuffer(Buffer);
+  ASSERT_TRUE(NoError(ReadVersionOrErr.getError()));
+  EXPECT_EQ(*ReadVersionOrErr, 103u);
+}
+
+// Verify that requesting format version 104 results in a version 104 profile.
+TEST_F(SampleProfTest, SampleProfileFormatVersion104) {
+  auto BufferOrErr = writeProfileToBuffer(104);
+  ASSERT_TRUE(NoError(BufferOrErr.getError()));
+  auto Buffer = std::move(*BufferOrErr);
+
+  auto ReadVersionOrErr = readVersionFromBuffer(Buffer);
+  ASSERT_TRUE(NoError(ReadVersionOrErr.getError()));
+  EXPECT_EQ(*ReadVersionOrErr, 104u);
+}
+
+// Verify that requesting format version 102 (below minimum supported) is
+// rejected by the reader.
+TEST_F(SampleProfTest, SampleProfileFormatVersion102) {
+  auto Buffer = writeRawHeaderToBuffer(102);
+  auto ReadVersionOrErr = readVersionFromBuffer(Buffer);
+  EXPECT_EQ(ReadVersionOrErr.getError(), sampleprof_error::unsupported_version);
+}
+
+// Verify that requesting format version 105 (above latest supported) is
+// rejected by the reader.
+TEST_F(SampleProfTest, SampleProfileFormatVersion105) {
+  auto Buffer = writeRawHeaderToBuffer(105);
+  auto ReadVersionOrErr = readVersionFromBuffer(Buffer);
+  EXPECT_EQ(ReadVersionOrErr.getError(), sampleprof_error::unsupported_version);
+}
+
 } // end anonymous namespace

``````````

</details>


https://github.com/llvm/llvm-project/pull/206297


More information about the llvm-commits mailing list