[clang] [compiler-rt] [llvm] Add a field in Coverage Mapping header for tracking coverage capabilities (PR #158059)

Dorian Péron via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 11 05:42:53 PDT 2025


https://github.com/RenjiSann created https://github.com/llvm/llvm-project/pull/158059

Example implementation of #158027 

>From c2d47a4bea312fd0031bfda419dbed7c017b163f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dorian=20P=C3=A9ron?= <peron at adacore.com>
Date: Tue, 19 Aug 2025 14:12:15 +0000
Subject: [PATCH 1/5] [Coverage] Add CoverageCapabilities struct and add it to
 the CovMapHeader struct

---
 compiler-rt/include/profile/InstrProfData.inc |  4 +-
 .../ProfileData/Coverage/CoverageMapping.h    | 50 +++++++++++++++++++
 .../llvm/ProfileData/InstrProfData.inc        |  4 +-
 3 files changed, 56 insertions(+), 2 deletions(-)

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..e6c45cbe562c2 100644
--- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
+++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
@@ -984,6 +984,46 @@ 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;
+  }
+
+  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 +1034,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.
+  CovInstrLevel AvailableInstrLevels = CovInstrLevel::All;
+
   std::optional<bool> SingleByteCoverage;
 
   CoverageMapping() = default;
@@ -1430,6 +1474,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 +1500,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/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

>From c24a7bc24457a6c4a611812ab7d2d1b6d84b4b69 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dorian=20P=C3=A9ron?= <peron at adacore.com>
Date: Tue, 19 Aug 2025 14:21:46 +0000
Subject: [PATCH 2/5] [Coverage] Extract Coverage capabilities from coverage
 mapping header

---
 .../ProfileData/Coverage/CoverageMapping.h    | 15 ++++--
 .../Coverage/CoverageMappingReader.h          | 13 ++++-
 .../ProfileData/Coverage/CoverageMapping.cpp  | 23 +++++++--
 .../Coverage/CoverageMappingReader.cpp        | 51 +++++++++++++++----
 llvm/tools/llvm-cov/CoverageViewOptions.h     |  4 +-
 .../ProfileData/CoverageMappingTest.cpp       |  5 ++
 6 files changed, 92 insertions(+), 19 deletions(-)

diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
index e6c45cbe562c2..0623e16aff173 100644
--- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
+++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
@@ -1014,6 +1014,12 @@ class CoverageCapabilities {
     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;
@@ -1036,7 +1042,7 @@ class CoverageMapping {
 
   /// Keep track of the coverage capabilities of the loaded object file,
   /// which depends on the parameters used to compile it.
-  CovInstrLevel AvailableInstrLevels = CovInstrLevel::All;
+  CoverageCapabilities AvailableInstrLevels = CoverageCapabilities::none();
 
   std::optional<bool> SingleByteCoverage;
 
@@ -1055,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.
@@ -1078,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.
@@ -1088,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.
   ///
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/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 coverage;
 
   // Read the records in the coverage data section.
@@ -871,6 +883,14 @@ static Error readCoverageMappingData(
   Expected<std::unique_ptr<CovMapFuncRecordReader>> ReaderExpected =
       CovMapFuncRecordReader::get<T, Endian>(Version, ProfileNames, Records,
                                              CompilationDir, Filenames);
+
+  // Assume coverage files before Version8 have all coverage capabilities, as
+  // the field didn't exist before.
+  if (Version < CovMapVersion::Version8)
+    AvailableCapabilities = CoverageCapabilities::all();
+  else
+    AvailableCapabilities = CoverageCapabilities(CovHeader->getCovInstrLevels<Endian>());
+
   if (Error E = ReaderExpected.takeError())
     return E;
   auto Reader = std::move(ReaderExpected.get());
@@ -915,22 +935,22 @@ BinaryCoverageReader::createCoverageReaderFromBuffer(
   if (BytesInAddress == 4 && Endian == llvm::endianness::little) {
     if (Error E = readCoverageMappingData<uint32_t, llvm::endianness::little>(
             ProfileNames, Coverage, FuncRecordsRef, Reader->MappingRecords,
-            CompilationDir, Reader->Filenames))
+            CompilationDir, Reader->Filenames, Reader->AvailableCapabilities))
       return std::move(E);
   } else if (BytesInAddress == 4 && Endian == llvm::endianness::big) {
     if (Error E = readCoverageMappingData<uint32_t, llvm::endianness::big>(
             ProfileNames, Coverage, FuncRecordsRef, Reader->MappingRecords,
-            CompilationDir, Reader->Filenames))
+            CompilationDir, Reader->Filenames, Reader->AvailableCapabilities))
       return std::move(E);
   } else if (BytesInAddress == 8 && Endian == llvm::endianness::little) {
     if (Error E = readCoverageMappingData<uint64_t, llvm::endianness::little>(
             ProfileNames, Coverage, FuncRecordsRef, Reader->MappingRecords,
-            CompilationDir, Reader->Filenames))
+            CompilationDir, Reader->Filenames, Reader->AvailableCapabilities))
       return std::move(E);
   } else if (BytesInAddress == 8 && Endian == llvm::endianness::big) {
     if (Error E = readCoverageMappingData<uint64_t, llvm::endianness::big>(
             ProfileNames, Coverage, FuncRecordsRef, Reader->MappingRecords,
-            CompilationDir, Reader->Filenames))
+            CompilationDir, Reader->Filenames, Reader->AvailableCapabilities))
       return std::move(E);
   } else
     return make_error<CoverageMapError>(
@@ -1003,15 +1023,28 @@ loadTestingFormat(StringRef Data, StringRef CompilationDir) {
     return make_error<CoverageMapError>(coveragemap_error::malformed,
                                         "insufficient padding");
   Data = Data.substr(Pad);
-  if (Data.size() < sizeof(CovMapHeader))
+
+  // In Version8, CovMapHeader passed from 4 to 5 uint32 fields,
+  // So we only check the first 4 fields to get the version.
+  if (Data.size() < 4 * sizeof(uint32_t))
     return make_error<CoverageMapError>(
         coveragemap_error::malformed,
         "coverage mapping header section is larger than data size");
+
   auto const *CovHeader = reinterpret_cast<const CovMapHeader *>(
       Data.substr(0, sizeof(CovMapHeader)).data());
   auto Version =
       CovMapVersion(CovHeader->getVersion<llvm::endianness::little>());
 
+  uint16_t CovHeaderSize = (Version >= CovMapVersion::Version8)
+                               ? sizeof(CovMapHeader)
+                               : 4 * sizeof(uint32_t);
+
+  if (Data.size() < CovHeaderSize)
+    return make_error<CoverageMapError>(
+        coveragemap_error::malformed,
+        "coverage mapping header section is larger than data size");
+
   // In Version1, the size of CoverageMapping is calculated.
   if (TestingVersion == uint64_t(TestingFormatVersion::Version1)) {
     if (Version < CovMapVersion::Version4) {
@@ -1019,7 +1052,7 @@ loadTestingFormat(StringRef Data, StringRef CompilationDir) {
     } else {
       auto FilenamesSize =
           CovHeader->getFilenamesSize<llvm::endianness::little>();
-      CoverageMappingSize = sizeof(CovMapHeader) + FilenamesSize;
+      CoverageMappingSize = CovHeaderSize + FilenamesSize;
     }
   }
 
diff --git a/llvm/tools/llvm-cov/CoverageViewOptions.h b/llvm/tools/llvm-cov/CoverageViewOptions.h
index 1f6ad570f86f2..46169ac9a0cfd 100644
--- a/llvm/tools/llvm-cov/CoverageViewOptions.h
+++ b/llvm/tools/llvm-cov/CoverageViewOptions.h
@@ -30,7 +30,7 @@ struct CoverageViewOptions {
   bool ShowLineNumbers;
   bool ShowLineStats;
   bool ShowRegionMarkers;
-  bool ShowMCDC;
+  bool ShowMCDC = false;
   bool ShowBranchCounts;
   bool ShowBranchPercents;
   bool ShowExpandedRegions;
@@ -38,7 +38,7 @@ struct CoverageViewOptions {
   bool UnifyFunctionInstantiations;
   bool ShowFullFilenames;
   bool ShowBranchSummary;
-  bool ShowMCDCSummary;
+  bool ShowMCDCSummary = false;
   bool ShowRegionSummary;
   bool ShowInstantiationSummary;
   bool ShowDirectoryCoverage;
diff --git a/llvm/unittests/ProfileData/CoverageMappingTest.cpp b/llvm/unittests/ProfileData/CoverageMappingTest.cpp
index ec81e5f274efa..d6167db58be16 100644
--- a/llvm/unittests/ProfileData/CoverageMappingTest.cpp
+++ b/llvm/unittests/ProfileData/CoverageMappingTest.cpp
@@ -103,6 +103,11 @@ struct CoverageMappingReaderMock : CoverageMappingReader {
 
     return Error::success();
   }
+
+  CoverageCapabilities coverageCapabilities() const override {
+    // TODO: Handle coverage capabilities in tests.
+    return CoverageCapabilities::all();
+  }
 };
 
 struct InputFunctionCoverageData {

>From dd7765ef87ccdd5d58185ca85d39f05853b58043 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dorian=20P=C3=A9ron?= <peron at adacore.com>
Date: Tue, 19 Aug 2025 14:15:13 +0000
Subject: [PATCH 3/5] [clang][Coverage] Populate the CovInstrLevel field at
 compilation

---
 clang/lib/CodeGen/CoverageMappingGen.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

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"

>From 9c69111d459721d5a285c9b08a43c2d3b15cbc47 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dorian=20P=C3=A9ron?= <peron at adacore.com>
Date: Thu, 28 Aug 2025 18:56:55 +0000
Subject: [PATCH 4/5] [llvm-cov] Set the requested coverage capabilities
 depending on given options

---
 llvm/tools/llvm-cov/CodeCoverage.cpp | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp
index 6c66858c4de8c..5799fd77ed668 100644
--- a/llvm/tools/llvm-cov/CodeCoverage.cpp
+++ b/llvm/tools/llvm-cov/CodeCoverage.cpp
@@ -462,9 +462,18 @@ std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
                 ObjectFilename);
   }
   auto FS = vfs::getRealFileSystem();
-  auto CoverageOrErr = CoverageMapping::load(
-      ObjectFilenames, PGOFilename, *FS, CoverageArches,
-      ViewOpts.CompilationDirectory, BIDFetcher.get(), CheckBinaryIDs);
+
+  CoverageCapabilities RequestCapabilities =
+      CoverageCapabilities(CoverageCapabilities::CovInstrLevel::Statement);
+  if (ViewOpts.ShowBranches != CoverageViewOptions::BranchOutputType::Off)
+    RequestCapabilities |= CoverageCapabilities::CovInstrLevel::Branch;
+  if (ViewOpts.ShowMCDC || ViewOpts.ShowMCDCSummary)
+    RequestCapabilities |= CoverageCapabilities::CovInstrLevel::MCDC;
+
+  auto CoverageOrErr =
+      CoverageMapping::load(ObjectFilenames, PGOFilename, *FS, CoverageArches,
+                            ViewOpts.CompilationDirectory, BIDFetcher.get(),
+                            CheckBinaryIDs, RequestCapabilities);
   if (Error E = CoverageOrErr.takeError()) {
     error("failed to load coverage: " + toString(std::move(E)));
     return nullptr;

>From 0752bdd322f83960cf0f406af2bb3909382b6576 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dorian=20P=C3=A9ron?= <peron at adacore.com>
Date: Thu, 4 Sep 2025 11:00:02 +0000
Subject: [PATCH 5/5] [clang][test] Fix expected coverage mapping format

---
 clang/test/CoverageMapping/ir.c     | 4 ++--
 clang/test/Profile/def-assignop.cpp | 2 +-
 clang/test/Profile/def-ctors.cpp    | 2 +-
 clang/test/Profile/def-dtors.cpp    | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

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() {



More information about the llvm-commits mailing list