[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