Skip to content

Conversation

RenjiSann
Copy link

Example implementation of #158027

Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added clang Clang issues not falling into any other category compiler-rt clang:codegen IR generation bugs: mangling, exceptions, etc. PGO Profile Guided Optimizations labels Sep 11, 2025
@llvmbot
Copy link
Member

llvmbot commented Sep 11, 2025

@llvm/pr-subscribers-pgo
@llvm/pr-subscribers-clang-codegen

@llvm/pr-subscribers-clang

Author: Dorian Péron (RenjiSann)

Changes

Example implementation of #158027


Patch is 26.70 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/158059.diff

14 Files Affected:

  • (modified) clang/lib/CodeGen/CoverageMappingGen.cpp (+9)
  • (modified) clang/test/CoverageMapping/ir.c (+2-2)
  • (modified) clang/test/Profile/def-assignop.cpp (+1-1)
  • (modified) clang/test/Profile/def-ctors.cpp (+1-1)
  • (modified) clang/test/Profile/def-dtors.cpp (+1-1)
  • (modified) compiler-rt/include/profile/InstrProfData.inc (+3-1)
  • (modified) llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h (+61-2)
  • (modified) llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h (+12-1)
  • (modified) llvm/include/llvm/ProfileData/InstrProfData.inc (+3-1)
  • (modified) llvm/lib/ProfileData/Coverage/CoverageMapping.cpp (+19-4)
  • (modified) llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp (+42-9)
  • (modified) llvm/tools/llvm-cov/CodeCoverage.cpp (+12-3)
  • (modified) llvm/tools/llvm-cov/CoverageViewOptions.h (+2-2)
  • (modified) llvm/unittests/ProfileData/CoverageMappingTest.cpp (+5)
diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp
index 05fb137ca0575..441d537e1f33f 100644
--- a/clang/lib/CodeGen/CoverageMappingGen.cpp
+++ b/clang/lib/CodeGen/CoverageMappingGen.cpp
@@ -2604,6 +2604,15 @@ void CoverageMappingModuleGen::emit() {
   };
   auto CovDataHeaderTy =
       llvm::StructType::get(Ctx, ArrayRef(CovDataHeaderTypes));
+
+  // By default, clang instruments the code for "statement" and "branch"
+  // coverage, and can instrument for MCDC when `-fcoverage-mcdc` is passed.
+  uint32_t CovInstrLevels = CoverageCapabilities::CovInstrLevel::Statement |
+                            CoverageCapabilities::CovInstrLevel::Branch;
+  if (CGM.getCodeGenOpts().hasProfileClangInstr() &&
+      CGM.getCodeGenOpts().MCDCCoverage)
+    CovInstrLevels |= CoverageCapabilities::CovInstrLevel::MCDC;
+
   llvm::Constant *CovDataHeaderVals[] = {
 #define COVMAP_HEADER(Type, LLVMType, Name, Init) Init,
 #include "llvm/ProfileData/InstrProfData.inc"
diff --git a/clang/test/CoverageMapping/ir.c b/clang/test/CoverageMapping/ir.c
index b08734cc35113..9f5111afef695 100644
--- a/clang/test/CoverageMapping/ir.c
+++ b/clang/test/CoverageMapping/ir.c
@@ -17,12 +17,12 @@ int main(void) {
 // DARWIN: [[FuncRecord1:@__covrec_[0-9A-F]+u]] = linkonce_odr hidden constant <{ i64, i32, i64, i64, [{{.*}} x i8] }> <{ {{.*}} }>, section "__LLVM_COV,__llvm_covfun", align 8
 // DARWIN: [[FuncRecord2:@__covrec_[0-9A-F]+u]] = linkonce_odr hidden constant <{ i64, i32, i64, i64, [{{.*}} x i8] }> <{ {{.*}} }>, section "__LLVM_COV,__llvm_covfun", align 8
 // DARWIN: [[FuncRecord3:@__covrec_[0-9A-F]+]] = linkonce_odr hidden constant <{ i64, i32, i64, i64, [{{.*}} x i8] }> <{ {{.*}} }>, section "__LLVM_COV,__llvm_covfun", align 8
-// DARWIN: @__llvm_coverage_mapping = private constant { { i32, i32, i32, i32 }, [{{.*}} x i8] } { {{.*}} }, section "__LLVM_COV,__llvm_covmap", align 8
+// DARWIN: @__llvm_coverage_mapping = private constant { { i32, i32, i32, i32, i32 }, [{{.*}} x i8] } { {{.*}} }, section "__LLVM_COV,__llvm_covmap", align 8
 
 // WINDOWS: [[FuncRecord1:@__covrec_[0-9A-F]+u]] = linkonce_odr hidden constant <{ i64, i32, i64, i64, [{{.*}} x i8] }> <{ {{.*}} }>, section ".lcovfun$M", comdat, align 8
 // WINDOWS: [[FuncRecord2:@__covrec_[0-9A-F]+u]] = linkonce_odr hidden constant <{ i64, i32, i64, i64, [{{.*}} x i8] }> <{ {{.*}} }>, section ".lcovfun$M", comdat, align 8
 // WINDOWS: [[FuncRecord3:@__covrec_[0-9A-F]+]] = linkonce_odr hidden constant <{ i64, i32, i64, i64, [{{.*}} x i8] }> <{ {{.*}} }>, section ".lcovfun$M", comdat, align 8
-// WINDOWS: @__llvm_coverage_mapping = private constant { { i32, i32, i32, i32 }, [{{.*}} x i8] } { {{.*}} }, section ".lcovmap$M", align 8
+// WINDOWS: @__llvm_coverage_mapping = private constant { { i32, i32, i32, i32, i32 }, [{{.*}} x i8] } { {{.*}} }, section ".lcovmap$M", align 8
 
 // COMMON: @llvm.used = appending global [{{.*}}] [
 // COMMON-SAME: [[FuncRecord1]]
diff --git a/clang/test/Profile/def-assignop.cpp b/clang/test/Profile/def-assignop.cpp
index d17654f31428b..332ea9f1046e9 100644
--- a/clang/test/Profile/def-assignop.cpp
+++ b/clang/test/Profile/def-assignop.cpp
@@ -23,7 +23,7 @@ struct A {
   // COVMAP: section "__llvm_covfun", comdat
   // COVMAP: section "__llvm_covfun", comdat
   // COVMAP: section "__llvm_covfun", comdat
-  // COVMAP: @__llvm_coverage_mapping = {{.*}} { { i32, i32, i32, i32 }
+  // COVMAP: @__llvm_coverage_mapping = {{.*}} { { i32, i32, i32, i32, i32 }
   B b;
 };
 
diff --git a/clang/test/Profile/def-ctors.cpp b/clang/test/Profile/def-ctors.cpp
index 0179bc92dbb86..8457c13fe2f9e 100644
--- a/clang/test/Profile/def-ctors.cpp
+++ b/clang/test/Profile/def-ctors.cpp
@@ -28,7 +28,7 @@ struct Derived : public Base {
   // COVMAP: section "__llvm_covfun", comdat
   // COVMAP: section "__llvm_covfun", comdat
   // COVMAP: section "__llvm_covfun", comdat
-  // COVMAP: @__llvm_coverage_mapping = {{.*}} { { i32, i32, i32, i32 }
+  // COVMAP: @__llvm_coverage_mapping = {{.*}} { { i32, i32, i32, i32, i32 }
 };
 
 Derived dd;
diff --git a/clang/test/Profile/def-dtors.cpp b/clang/test/Profile/def-dtors.cpp
index dc87508fe7c28..a4d6a38e9e422 100644
--- a/clang/test/Profile/def-dtors.cpp
+++ b/clang/test/Profile/def-dtors.cpp
@@ -23,7 +23,7 @@ struct Derived : public Base {
   // COVMAP: section "__llvm_covfun", comdat
   // COVMAP: section "__llvm_covfun", comdat
   // COVMAP: section "__llvm_covfun", comdat
-  // COVMAP: @__llvm_coverage_mapping = {{.*}} { { i32, i32, i32, i32 }
+  // COVMAP: @__llvm_coverage_mapping = {{.*}} { { i32, i32, i32, i32, i32 }
 };
 
 int main() {
diff --git a/compiler-rt/include/profile/InstrProfData.inc b/compiler-rt/include/profile/InstrProfData.inc
index 0496f240dc823..a2af7cfa8cbe1 100644
--- a/compiler-rt/include/profile/InstrProfData.inc
+++ b/compiler-rt/include/profile/InstrProfData.inc
@@ -300,6 +300,8 @@ COVMAP_HEADER(uint32_t, Int32Ty, CoverageSize, \
               llvm::ConstantInt::get(Int32Ty, CoverageMappingSize))
 COVMAP_HEADER(uint32_t, Int32Ty, Version, \
               llvm::ConstantInt::get(Int32Ty, CovMapVersion::CurrentVersion))
+COVMAP_HEADER(uint32_t, Int32Ty, CovInstrLevels, \
+              llvm::ConstantInt::get(Int32Ty, CovInstrLevels))
 #undef COVMAP_HEADER
 /* COVMAP_HEADER end.  */
 
@@ -724,7 +726,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
 /* Indexed profile format version (start from 1). */
 #define INSTR_PROF_INDEX_VERSION 12
 /* Coverage mapping format version (start from 0). */
-#define INSTR_PROF_COVMAP_VERSION 6
+#define INSTR_PROF_COVMAP_VERSION 7
 
 /* Profile version is always of type uint64_t. Reserve the upper 32 bits in the
  * version for other variants of profile. We set the 8th most significant bit
diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
index 7d1a85ba528fc..0623e16aff173 100644
--- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
+++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
@@ -984,6 +984,52 @@ class CoverageData {
   ArrayRef<MCDCRecord> getMCDCRecords() const { return MCDCRecords; }
 };
 
+/// Represents a set of available capabilities
+class CoverageCapabilities {
+  uint32_t Capabilities;
+
+public:
+  enum CovInstrLevel {
+    Statement = (1 << 0),
+    Branch = (1 << 1),
+    MCDC = (1 << 2),
+  };
+
+  CoverageCapabilities(uint32_t Capabilities) : Capabilities(Capabilities){};
+
+  static CoverageCapabilities all() {
+    return CoverageCapabilities(Statement | Branch | MCDC);
+  }
+
+  static CoverageCapabilities none() {
+    return CoverageCapabilities(0);
+  }
+
+  bool hasCapability(CovInstrLevel Lvl) const {
+    return (this->Capabilities & Lvl) != 0;
+  }
+
+  /// Returns true if this includes all the capabilities of Other.
+  bool includes(const CoverageCapabilities &Other) const {
+    return (this->Capabilities & Other.Capabilities) == Other.Capabilities;
+  }
+
+  std::string toString() const {
+    std::stringstream SS;
+    SS << std::oct << this->Capabilities;
+    return SS.str();
+  }
+
+  CoverageCapabilities& operator |= (const CoverageCapabilities &Rhs) {
+    this->Capabilities |= Rhs.Capabilities;
+    return *this;
+  }
+
+  CoverageCapabilities operator | (const CoverageCapabilities &Rhs) const {
+    return CoverageCapabilities(this->Capabilities | Rhs.Capabilities);
+  }
+};
+
 /// The mapping of profile information to coverage data.
 ///
 /// This is the main interface to get coverage information, using a profile to
@@ -994,6 +1040,10 @@ class CoverageMapping {
   DenseMap<size_t, SmallVector<unsigned, 0>> FilenameHash2RecordIndices;
   std::vector<std::pair<std::string, uint64_t>> FuncHashMismatches;
 
+  /// Keep track of the coverage capabilities of the loaded object file,
+  /// which depends on the parameters used to compile it.
+  CoverageCapabilities AvailableInstrLevels = CoverageCapabilities::none();
+
   std::optional<bool> SingleByteCoverage;
 
   CoverageMapping() = default;
@@ -1011,6 +1061,7 @@ class CoverageMapping {
                std::optional<std::reference_wrapper<IndexedInstrProfReader>>
                    &ProfileReader,
                CoverageMapping &Coverage, bool &DataFound,
+               CoverageCapabilities RequestedCapabilities,
                SmallVectorImpl<object::BuildID> *FoundBinaryIDs = nullptr);
 
   /// Add a function record corresponding to \p Record.
@@ -1034,7 +1085,8 @@ class CoverageMapping {
   LLVM_ABI static Expected<std::unique_ptr<CoverageMapping>>
   load(ArrayRef<std::unique_ptr<CoverageMappingReader>> CoverageReaders,
        std::optional<std::reference_wrapper<IndexedInstrProfReader>>
-           &ProfileReader);
+           &ProfileReader,
+       CoverageCapabilities RequestCapabilities = CoverageCapabilities::none());
 
   /// Load the coverage mapping from the given object files and profile. If
   /// \p Arches is non-empty, it must specify an architecture for each object.
@@ -1044,7 +1096,8 @@ class CoverageMapping {
        std::optional<StringRef> ProfileFilename, vfs::FileSystem &FS,
        ArrayRef<StringRef> Arches = {}, StringRef CompilationDir = "",
        const object::BuildIDFetcher *BIDFetcher = nullptr,
-       bool CheckBinaryIDs = false);
+       bool CheckBinaryIDs = false,
+       CoverageCapabilities RequestCapabilities = CoverageCapabilities::none());
 
   /// The number of functions that couldn't have their profiles mapped.
   ///
@@ -1430,6 +1483,10 @@ struct CovMapHeader {
   template <llvm::endianness Endian> uint32_t getVersion() const {
     return support::endian::byte_swap<uint32_t, Endian>(Version);
   }
+
+  template <llvm::endianness Endian> uint32_t getCovInstrLevels() const {
+    return support::endian::byte_swap<uint32_t, Endian>(CovInstrLevels);
+  }
 };
 
 LLVM_PACKED_END
@@ -1452,6 +1509,8 @@ enum CovMapVersion {
   Version6 = 5,
   // Branch regions extended and Decision Regions added for MC/DC.
   Version7 = 6,
+  // Covmap header tracks the instrumentation level
+  Version8 = 7,
   // The current version is Version7.
   CurrentVersion = INSTR_PROF_COVMAP_VERSION
 };
diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h
index e91ba9147a745..b6e68bf13c10d 100644
--- a/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h
+++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h
@@ -104,6 +104,10 @@ class CoverageMappingReader {
   virtual Error readNextRecord(CoverageMappingRecord &Record) = 0;
   CoverageMappingIterator begin() { return CoverageMappingIterator(this); }
   CoverageMappingIterator end() { return CoverageMappingIterator(); }
+
+  /// Returns the instrumentation levels for which the code was originally
+  /// instrumented.
+  virtual CoverageCapabilities coverageCapabilities() const = 0;
 };
 
 /// Base class for the raw coverage mapping and filenames data readers.
@@ -188,6 +192,7 @@ class LLVM_ABI BinaryCoverageReader : public CoverageMappingReader {
   std::vector<ProfileMappingRecord> MappingRecords;
   std::unique_ptr<InstrProfSymtab> ProfileNames;
   size_t CurrentRecord = 0;
+  CoverageCapabilities AvailableCapabilities;
   std::vector<StringRef> FunctionsFilenames;
   std::vector<CounterExpression> Expressions;
   std::vector<CounterMappingRegion> MappingRegions;
@@ -205,7 +210,9 @@ class LLVM_ABI BinaryCoverageReader : public CoverageMappingReader {
   BinaryCoverageReader(std::unique_ptr<InstrProfSymtab> Symtab,
                        FuncRecordsStorage &&FuncRecords,
                        CoverageMapCopyStorage &&CoverageMapCopy)
-      : ProfileNames(std::move(Symtab)), FuncRecords(std::move(FuncRecords)),
+      : ProfileNames(std::move(Symtab)),
+        AvailableCapabilities(CoverageCapabilities::all()),
+        FuncRecords(std::move(FuncRecords)),
         CoverageMapCopy(std::move(CoverageMapCopy)) {}
 
 public:
@@ -226,6 +233,10 @@ class LLVM_ABI BinaryCoverageReader : public CoverageMappingReader {
       llvm::endianness Endian, StringRef CompilationDir = "");
 
   Error readNextRecord(CoverageMappingRecord &Record) override;
+
+  CoverageCapabilities coverageCapabilities() const override {
+    return this->AvailableCapabilities;
+  };
 };
 
 /// Reader for the raw coverage filenames.
diff --git a/llvm/include/llvm/ProfileData/InstrProfData.inc b/llvm/include/llvm/ProfileData/InstrProfData.inc
index 0496f240dc823..a2af7cfa8cbe1 100644
--- a/llvm/include/llvm/ProfileData/InstrProfData.inc
+++ b/llvm/include/llvm/ProfileData/InstrProfData.inc
@@ -300,6 +300,8 @@ COVMAP_HEADER(uint32_t, Int32Ty, CoverageSize, \
               llvm::ConstantInt::get(Int32Ty, CoverageMappingSize))
 COVMAP_HEADER(uint32_t, Int32Ty, Version, \
               llvm::ConstantInt::get(Int32Ty, CovMapVersion::CurrentVersion))
+COVMAP_HEADER(uint32_t, Int32Ty, CovInstrLevels, \
+              llvm::ConstantInt::get(Int32Ty, CovInstrLevels))
 #undef COVMAP_HEADER
 /* COVMAP_HEADER end.  */
 
@@ -724,7 +726,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
 /* Indexed profile format version (start from 1). */
 #define INSTR_PROF_INDEX_VERSION 12
 /* Coverage mapping format version (start from 0). */
-#define INSTR_PROF_COVMAP_VERSION 6
+#define INSTR_PROF_COVMAP_VERSION 7
 
 /* Profile version is always of type uint64_t. Reserve the upper 32 bits in the
  * version for other variants of profile. We set the 8th most significant bit
diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index 429ec5c19f1f8..a7e8fb8030c04 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -978,6 +978,7 @@ Error CoverageMapping::loadFromReaders(
   Coverage.SingleByteCoverage =
       !ProfileReader || ProfileReader.value().get().hasSingleByteCoverage();
   for (const auto &CoverageReader : CoverageReaders) {
+    Coverage.AvailableInstrLevels |= CoverageReader->coverageCapabilities();
     for (auto RecordOrErr : *CoverageReader) {
       if (Error E = RecordOrErr.takeError())
         return E;
@@ -992,7 +993,7 @@ Error CoverageMapping::loadFromReaders(
 Expected<std::unique_ptr<CoverageMapping>> CoverageMapping::load(
     ArrayRef<std::unique_ptr<CoverageMappingReader>> CoverageReaders,
     std::optional<std::reference_wrapper<IndexedInstrProfReader>>
-        &ProfileReader) {
+        &ProfileReader, CoverageCapabilities RequestedCapabilities) {
   auto Coverage = std::unique_ptr<CoverageMapping>(new CoverageMapping());
   if (Error E = loadFromReaders(CoverageReaders, ProfileReader, *Coverage))
     return std::move(E);
@@ -1013,6 +1014,7 @@ Error CoverageMapping::loadFromFile(
     std::optional<std::reference_wrapper<IndexedInstrProfReader>>
         &ProfileReader,
     CoverageMapping &Coverage, bool &DataFound,
+    CoverageCapabilities RequestedCapabilities,
     SmallVectorImpl<object::BuildID> *FoundBinaryIDs) {
   auto CovMappingBufOrErr = MemoryBuffer::getFileOrSTDIN(
       Filename, /*IsText=*/false, /*RequiresNullTerminator=*/false);
@@ -1045,6 +1047,16 @@ Error CoverageMapping::loadFromFile(
   DataFound |= !Readers.empty();
   if (Error E = loadFromReaders(Readers, ProfileReader, Coverage))
     return createFileError(Filename, std::move(E));
+
+  // Check that the requested coverage capabilities are available.
+  if (!Coverage.AvailableInstrLevels.includes(RequestedCapabilities))
+    return createFileError(
+        Filename, createStringError(
+                      "lacking coverage capabilities (requested %s, got %s)",
+                      RequestedCapabilities.toString().c_str(),
+                      Coverage.AvailableInstrLevels.toString().c_str()
+                    ));
+
   return Error::success();
 }
 
@@ -1052,7 +1064,8 @@ Expected<std::unique_ptr<CoverageMapping>> CoverageMapping::load(
     ArrayRef<StringRef> ObjectFilenames,
     std::optional<StringRef> ProfileFilename, vfs::FileSystem &FS,
     ArrayRef<StringRef> Arches, StringRef CompilationDir,
-    const object::BuildIDFetcher *BIDFetcher, bool CheckBinaryIDs) {
+    const object::BuildIDFetcher *BIDFetcher, bool CheckBinaryIDs,
+    CoverageCapabilities RequestedCapabilities) {
   std::unique_ptr<IndexedInstrProfReader> ProfileReader;
   if (ProfileFilename) {
     auto ProfileReaderOrErr =
@@ -1081,7 +1094,8 @@ Expected<std::unique_ptr<CoverageMapping>> CoverageMapping::load(
   for (const auto &File : llvm::enumerate(ObjectFilenames)) {
     if (Error E = loadFromFile(File.value(), GetArch(File.index()),
                                CompilationDir, ProfileReaderRef, *Coverage,
-                               DataFound, &FoundBinaryIDs))
+                               DataFound, RequestedCapabilities,
+                               &FoundBinaryIDs))
       return std::move(E);
   }
 
@@ -1110,7 +1124,8 @@ Expected<std::unique_ptr<CoverageMapping>> CoverageMapping::load(
         std::string Path = std::move(*PathOpt);
         StringRef Arch = Arches.size() == 1 ? Arches.front() : StringRef();
         if (Error E = loadFromFile(Path, Arch, CompilationDir, ProfileReaderRef,
-                                   *Coverage, DataFound))
+                                   *Coverage, DataFound,
+                                   RequestedCapabilities))
           return std::move(E);
       } else if (CheckBinaryIDs) {
         return createFileError(
diff --git a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp
index fc2577e6ada5d..8ae7a26bc1b50 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp
@@ -670,7 +670,13 @@ class VersionedCovMapFuncRecordReader : public CovMapFuncRecordReader {
                                             const char *CovBufEnd) override {
     using namespace support;
 
-    if (CovBuf + sizeof(CovMapHeader) > CovBufEnd)
+    // In versions before Version8, the Covmap header only has 4 uint32_t
+    // fields.
+    uint32_t HeaderSize = (Version >= CovMapVersion::Version8)
+                              ? sizeof(CovMapHeader)
+                              : 4 * sizeof(uint32_t);
+
+    if (CovBuf + HeaderSize > CovBufEnd)
       return make_error<CoverageMapError>(
           coveragemap_error::malformed,
           "coverage mapping header section is larger than buffer size");
@@ -679,7 +685,8 @@ class VersionedCovMapFuncRecordReader : public CovMapFuncRecordReader {
     uint32_t FilenamesSize = CovHeader->getFilenamesSize<Endian>();
     uint32_t CoverageSize = CovHeader->getCoverageSize<Endian>();
     assert((CovMapVersion)CovHeader->getVersion<Endian>() == Version);
-    CovBuf = reinterpret_cast<const char *>(CovHeader + 1);
+
+    CovBuf = reinterpret_cast<const char *>(CovHeader) + HeaderSize;
 
     // Skip past the function records, saving the start and end for later.
     // This is a no-op in Version4 (function records are read after all headers
@@ -830,6 +837,7 @@ Expected<std::unique_ptr<CovMapFuncRecordReader>> CovMapFuncRecordReader::get(
   case CovMapVersion::Version5:
   case CovMapVersion::Version6:
   case CovMapVersion::Version7:
+  case CovMapVersion::Version8:
     // Decompress the name data.
     if (Error E = P.create(P.getNameData()))
       return std::move(E);
@@ -851,6 +859,9 @@ Expected<std::unique_ptr<CovMapFuncRecordReader>> CovMapFuncRecordReader::get(
     else if (Version == CovMapVersion::Version7)
       return std::make_unique<VersionedCovMapFuncRecordReader<
           CovMapVersion::Version7, IntPtrT, Endian>>(P, R, D, F);
+    else if (Version == CovMapVersion::Version8)
+      return std::make_unique<VersionedCovMapFuncRecordReader<
+          CovMapVersion::Version8, IntPtrT, Endian>>(P, R, D, F);
   }
   llvm_unreachable("Unsupported version");
 }
@@ -859,7 +870,8 @@ template <typename T, llvm::endianness Endian>
 static Error readCoverageMappingData(
     InstrProfSymtab &ProfileNames, StringRef CovMap, StringRef FuncRecords,
     std::vector<BinaryCoverageReader::ProfileMappingRecord> &Records,
-    StringRef CompilationDir, std::vector<std::string> &Filenames) {
+    StringRef CompilationDir, std::vector<std::string> &Filenames,
+    CoverageCapabilities &AvailableCapabilities) {
   using namespace ...
[truncated]

@RenjiSann
Copy link
Author

@evodius96 You might want to take a look at it

@evodius96
Copy link
Contributor

Thanks for submitting this! What you have here looks pretty straightforward. Unless I've totally missed it, I don't see the llvm-profdata merging capability handled or tested as we discussed on the discourse thread. I'd like to see that in as well so that merging is handled in a sound way.

@@ -2604,6 +2604,15 @@ void CoverageMappingModuleGen::emit() {
};
auto CovDataHeaderTy =
llvm::StructType::get(Ctx, ArrayRef(CovDataHeaderTypes));

// By default, clang instruments the code for "statement" and "branch"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know what you're getting at with "Statement", but LLVM uses "Region Coverage", which is more-or-less the same thing (as regions are created according to statement scopes, etc). I think this extension ought to follow that convention by also referring to "Region Coverage".

Of course, "regions" also facilitates other metrics: line coverage, function coverage, and branch coverage, all together (with MC/DC with another option). I'm OK with limiting your extension to "Region", "Branch", and "MCDC".

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Sorry for the delay, I overlooked your answer)

I see "region" is more generic and can fit several purposes, I'll update the code 👍

@RenjiSann RenjiSann force-pushed the cov-instr-level-tracking branch from 0752bdd to 0a94ab7 Compare September 24, 2025 13:26
@RenjiSann RenjiSann force-pushed the cov-instr-level-tracking branch from 0a94ab7 to 487352f Compare September 24, 2025 13:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:codegen IR generation bugs: mangling, exceptions, etc. clang Clang issues not falling into any other category compiler-rt PGO Profile Guided Optimizations

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants