[clang] [compiler-rt] [llvm] coverage-capabilities (PR #156307)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 1 03:39:42 PDT 2025
Dorian =?utf-8?q?Péron?= <peron at adacore.com>,
Dorian =?utf-8?q?Péron?= <peron at adacore.com>,
Dorian =?utf-8?q?Péron?= <peron at adacore.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/156307 at github.com>
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Dorian Péron (RenjiSann)
<details>
<summary>Changes</summary>
---
Full diff: https://github.com/llvm/llvm-project/pull/156307.diff
8 Files Affected:
- (modified) clang/lib/CodeGen/CoverageMappingGen.cpp (+9)
- (modified) compiler-rt/include/profile/InstrProfData.inc (+3-1)
- (modified) llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h (+56-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 (+15-4)
- (modified) llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp (+26-6)
- (modified) llvm/tools/llvm-cov/CodeCoverage.cpp (+12-3)
``````````diff
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/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..64e9a138815f5 100644
--- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
+++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
@@ -984,6 +984,47 @@ class CoverageData {
ArrayRef<MCDCRecord> getMCDCRecords() const { return MCDCRecords; }
};
+/// TODO: Document
+/// 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 +1035,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 +1056,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 +1080,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 +1091,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::all());
/// The number of functions that couldn't have their profiles mapped.
///
@@ -1430,6 +1478,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 +1504,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..892bba1c2fb04 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,12 @@ Error CoverageMapping::loadFromFile(
DataFound |= !Readers.empty();
if (Error E = loadFromReaders(Readers, ProfileReader, Coverage))
return createFileError(Filename, std::move(E));
+
+ // Check that all the requested coverage capabilities are available.
+ if (!Coverage.AvailableInstrLevels.includes(RequestedCapabilities))
+ return createFileError(
+ Filename, createStringError("is missing instrumentation levels"));
+
return Error::success();
}
@@ -1052,7 +1060,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 +1090,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 +1120,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..982eeec4997f0 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp
@@ -25,6 +25,7 @@
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/Wasm.h"
+#include "llvm/ProfileData/Coverage/CoverageMapping.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Compression.h"
@@ -679,7 +680,16 @@ 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);
+
+ // Assume that older coverage files have data for each level.
+ if (Version >= CovMapVersion::Version8) {
+ CovBuf = reinterpret_cast<const char *>(CovHeader + 1);
+ } else {
+ // In versions before Version8, the Covmap header only has 4 uint32_t
+ // fields.
+ CovBuf = reinterpret_cast<const char *>(
+ reinterpret_cast<const uint32_t *>(CovHeader) + 4);
+ }
// 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 +840,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 +862,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 +873,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 +886,11 @@ static Error readCoverageMappingData(
Expected<std::unique_ptr<CovMapFuncRecordReader>> ReaderExpected =
CovMapFuncRecordReader::get<T, Endian>(Version, ProfileNames, Records,
CompilationDir, Filenames);
+ 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>(
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;
``````````
</details>
https://github.com/llvm/llvm-project/pull/156307
More information about the llvm-commits
mailing list