[llvm-branch-commits] [llvm] obj2yaml: Introduce CovMap dump (PR #127432)
NAKAMURA Takumi via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Mar 14 21:06:55 PDT 2025
https://github.com/chapuni updated https://github.com/llvm/llvm-project/pull/127432
>From 7e29d6ace39058b631dcfff5533d8aee055de6dd Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Mon, 3 Mar 2025 12:25:13 +0900
Subject: [PATCH 1/3] obj2yaml
---
llvm/include/llvm/ObjectYAML/CovMap.h | 47 ++-
llvm/include/llvm/ProfileData/InstrProf.h | 6 +
llvm/lib/ObjectYAML/CovMap.cpp | 377 +++++++++++++++++++-
llvm/lib/ProfileData/InstrProf.cpp | 23 +-
llvm/test/tools/obj2yaml/ELF/covmap-be.yaml | 2 +
llvm/test/tools/obj2yaml/ELF/covmap.yaml | 2 +
llvm/tools/obj2yaml/elf2yaml.cpp | 59 ++-
llvm/tools/obj2yaml/obj2yaml.cpp | 2 +-
llvm/tools/obj2yaml/obj2yaml.h | 4 +
9 files changed, 512 insertions(+), 10 deletions(-)
diff --git a/llvm/include/llvm/ObjectYAML/CovMap.h b/llvm/include/llvm/ObjectYAML/CovMap.h
index 3a0b86435d490..406204ee024fb 100644
--- a/llvm/include/llvm/ObjectYAML/CovMap.h
+++ b/llvm/include/llvm/ObjectYAML/CovMap.h
@@ -16,7 +16,7 @@
//
// - llvm::covmap
//
-// Provides YAML encoder for coverage map.
+// Provides YAML encoder and decoder for coverage map.
//
//===----------------------------------------------------------------------===//
@@ -27,6 +27,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/ObjectYAML/ELFYAML.h"
#include "llvm/Support/Endian.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/YAMLTraits.h"
#include <array>
#include <cstdint>
@@ -41,6 +42,8 @@ class raw_ostream;
namespace llvm::coverage::yaml {
+struct DecoderContext;
+
/// Base Counter, corresponding to coverage::Counter.
struct CounterTy {
enum TagTy : uint8_t {
@@ -65,6 +68,12 @@ struct CounterTy {
virtual void mapping(llvm::yaml::IO &IO);
+ /// Holds Val for extensions.
+ Error decodeOrTag(DecoderContext &Data);
+
+ /// Raise Error if Val isn't empty.
+ Error decode(DecoderContext &Data);
+
void encode(raw_ostream &OS) const;
};
@@ -85,6 +94,8 @@ struct DecisionTy {
void mapping(llvm::yaml::IO &IO);
+ Error decode(DecoderContext &Data);
+
void encode(raw_ostream &OS) const;
};
@@ -118,6 +129,8 @@ struct RecTy : CounterTy {
void mapping(llvm::yaml::IO &IO) override;
+ Error decode(DecoderContext &Data);
+
void encode(uint64_t &StartLoc, raw_ostream &OS) const;
};
@@ -142,6 +155,10 @@ struct CovFunTy {
void mapping(llvm::yaml::IO &IO);
+ /// Depends on CovMap and SymTab(IPSK_names)
+ Expected<uint64_t> decode(const ArrayRef<uint8_t> Content, uint64_t Offset,
+ endianness Endianness);
+
void encode(raw_ostream &OS, endianness Endianness) const;
};
@@ -170,6 +187,9 @@ struct CovMapTy {
bool useWD() const { return (!Version || *Version >= 4); }
StringRef getWD() const { return (WD ? *WD : StringRef()); }
+ Expected<uint64_t> decode(const ArrayRef<uint8_t> Content, uint64_t Offset,
+ endianness Endianness);
+
/// Generate Accumulated list with WD.
/// Returns a single element {WD} if AccFiles is not given.
std::vector<std::string>
@@ -236,6 +256,31 @@ LLVM_COVERAGE_YAML_ELEM_MAPPING(CovMapTy)
namespace llvm::covmap {
+class Decoder {
+protected:
+ endianness Endianness;
+
+public:
+ Decoder(endianness Endianness) : Endianness(Endianness) {}
+ virtual ~Decoder() {}
+
+ /// Returns DecoderImpl.
+ static std::unique_ptr<Decoder> get(endianness Endianness,
+ bool CovMapEnabled);
+
+ /// Called from the Sections loop in advance of the final dump.
+ /// Decoder predecodes CovMap for Version info.
+ virtual Error acquire(unsigned AddressAlign, StringRef Name,
+ ArrayRef<uint8_t> Content) = 0;
+
+ /// Make contents on ELFYAML object. CovMap is predecoded.
+ virtual Error make(ELFYAML::CovMapSectionBase *Base,
+ ArrayRef<uint8_t> Content) = 0;
+
+ /// Suppress emission of CovMap unless enabled.
+ static bool enabled;
+};
+
class Encoder {
protected:
endianness Endianness;
diff --git a/llvm/include/llvm/ProfileData/InstrProf.h b/llvm/include/llvm/ProfileData/InstrProf.h
index 7133c0c6a302c..e20424da3cac2 100644
--- a/llvm/include/llvm/ProfileData/InstrProf.h
+++ b/llvm/include/llvm/ProfileData/InstrProf.h
@@ -545,6 +545,12 @@ class InstrProfSymtab {
/// This method is a wrapper to \c readAndDecodeStrings method.
Error create(StringRef NameStrings);
+ // PrfNames is nested array.
+ using PrfNamesTy = SmallVector<std::string>;
+ using PrfNamesChunksTy = SmallVector<PrfNamesTy, 1>;
+
+ Expected<PrfNamesChunksTy> createAndGetList(ArrayRef<uint8_t> Content);
+
/// Initialize symtab states with function names and vtable names. \c
/// FuncNameStrings is a string composed of one or more encoded function name
/// strings, and \c VTableNameStrings composes of one or more encoded vtable
diff --git a/llvm/lib/ObjectYAML/CovMap.cpp b/llvm/lib/ObjectYAML/CovMap.cpp
index 7662284caee76..dcf90f7b109cb 100644
--- a/llvm/lib/ObjectYAML/CovMap.cpp
+++ b/llvm/lib/ObjectYAML/CovMap.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
//
-// Implementations of CovMap and encoder.
+// Implementations of CovMap, encoder, decoder.
//
//===----------------------------------------------------------------------===//
@@ -15,9 +15,11 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ObjectYAML/ELFYAML.h"
+#include "llvm/ProfileData/Coverage/CoverageMappingReader.h"
#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/Alignment.h"
+#include "llvm/Support/DataExtractor.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/MD5.h"
@@ -37,6 +39,28 @@ using namespace llvm;
using namespace llvm::coverage::yaml;
using namespace llvm::covmap;
+bool Decoder::enabled;
+
+// DataExtractor w/ single Cursor
+struct coverage::yaml::DecoderContext : DataExtractor, DataExtractor::Cursor {
+ uint64_t LineStart = 0;
+
+ DecoderContext(const ArrayRef<uint8_t> Content, bool IsLE)
+ : DataExtractor(Content, IsLE, /*AddressSize=*/0),
+ DataExtractor::Cursor(0) {}
+
+ bool eof() { return DataExtractor::eof(*this); }
+ uint32_t getU32() { return DataExtractor::getU32(*this); }
+ uint64_t getU64() { return DataExtractor::getU64(*this); }
+ Expected<uint64_t> getULEB128() {
+ uint64_t Result = DataExtractor::getULEB128(*this);
+ if (!*this)
+ return takeError();
+ return Result;
+ }
+ StringRef getBytes(size_t sz) { return DataExtractor::getBytes(*this, sz); }
+};
+
void CounterTy::encode(raw_ostream &OS) const {
std::pair<unsigned, uint64_t> C;
if (RefOpt)
@@ -55,11 +79,55 @@ void CounterTy::encode(raw_ostream &OS) const {
encodeULEB128(C.first | (C.second << 2), OS);
}
+Error CounterTy::decodeOrTag(DecoderContext &Data) {
+ auto COrErr = Data.getULEB128();
+ if (!COrErr)
+ return COrErr.takeError();
+ auto T = static_cast<TagTy>(*COrErr & 0x03);
+ auto V = (*COrErr >> 2);
+ if (T == Zero) {
+ if (V == 0)
+ Tag = Zero; // w/o Val
+ else
+ Val = V; // w/o Tag
+ } else {
+ Tag = T;
+ Val = V;
+ }
+
+ return Error::success();
+}
+
+Error CounterTy::decode(DecoderContext &Data) {
+ if (auto E = decodeOrTag(Data))
+ return E;
+ if (!this->Tag && this->Val)
+ return make_error<CoverageMapError>(
+ coveragemap_error::malformed,
+ "Counter::Zero shouldn't have the Val: 0x" +
+ Twine::utohexstr(*this->Val));
+ return Error::success();
+}
+
void DecisionTy::encode(raw_ostream &OS) const {
encodeULEB128(BIdx, OS);
encodeULEB128(NC, OS);
}
+Error DecisionTy::decode(DecoderContext &Data) {
+ auto BIdxOrErr = Data.getULEB128();
+ if (!BIdxOrErr)
+ return BIdxOrErr.takeError();
+ BIdx = *BIdxOrErr;
+
+ auto NCOrErr = Data.getULEB128();
+ if (!NCOrErr)
+ return NCOrErr.takeError();
+ NC = *NCOrErr;
+
+ return Error::success();
+}
+
void RecTy::encode(uint64_t &StartLoc, raw_ostream &OS) const {
if (Expansion) {
encodeULEB128(4 + (*Expansion << 3), OS);
@@ -104,6 +172,106 @@ void RecTy::encode(uint64_t &StartLoc, raw_ostream &OS) const {
}
}
+Error RecTy::decode(DecoderContext &Data) {
+ auto getU16 = [&]() -> Expected<uint16_t> {
+ auto ValOrErr = Data.getULEB128();
+ if (!ValOrErr)
+ return ValOrErr.takeError();
+ if (*ValOrErr > 0x7FFF + 1)
+ return make_error<CoverageMapError>(coveragemap_error::malformed,
+ "MC/DC index is out of range: 0x" +
+ Twine::utohexstr(*ValOrErr));
+ return static_cast<uint16_t>(*ValOrErr);
+ };
+
+ auto decodeBranch = [&]() -> Error {
+ auto &B = BranchOpt.emplace();
+ if (auto E = B[0].decode(Data))
+ return E;
+ if (auto E = B[1].decode(Data))
+ return E;
+ return Error::success();
+ };
+
+ // Decode tagged CounterTy
+ if (auto E = CounterTy::decodeOrTag(Data))
+ return E;
+ if (!this->Val || this->Tag) {
+ // Compatible to CounterTy
+ } else if (*this->Val & 1u) {
+ Expansion = (*this->Val >> 1);
+ this->Val.reset();
+ } else {
+ auto Tag = *this->Val >> 1;
+ this->Val.reset();
+ switch (Tag) {
+ case Skip:
+ ExtTag = Skip; // w/o Val
+ break;
+ case Decision:
+ if (auto E = DecisionOpt.emplace().decode(Data))
+ return E;
+ ExtTag = Decision;
+ break;
+ case Branch:
+ if (auto E = decodeBranch())
+ return E;
+ ExtTag = Branch;
+ break;
+ case MCDCBranch: {
+ if (auto E = decodeBranch())
+ return E;
+ auto I0OrErr = getU16();
+ if (!I0OrErr)
+ return I0OrErr.takeError();
+ auto I1OrErr = getU16();
+ if (!I1OrErr)
+ return I1OrErr.takeError();
+ auto I2OrErr = getU16();
+ if (!I2OrErr)
+ return I2OrErr.takeError();
+ MCDC = {*I0OrErr, *I1OrErr, *I2OrErr};
+ ExtTag = MCDCBranch;
+ break;
+ }
+ default:
+ return make_error<CoverageMapError>(
+ coveragemap_error::malformed,
+ "Record doesn't have a valid Tag: 0x" + Twine::utohexstr(Tag));
+ }
+ }
+
+ // Decode Loc
+ auto LSDeltaOrErr = Data.getULEB128();
+ if (!LSDeltaOrErr)
+ return LSDeltaOrErr.takeError();
+ Data.LineStart += *LSDeltaOrErr;
+
+ auto CSOrErr = Data.getULEB128();
+ if (!CSOrErr)
+ return CSOrErr.takeError();
+
+ auto NLOrErr = Data.getULEB128();
+ if (!NLOrErr)
+ return NLOrErr.takeError();
+ auto LineEnd = Data.LineStart + *NLOrErr;
+
+ auto CEOrErr = Data.getULEB128();
+ if (!CEOrErr)
+ return CEOrErr.takeError();
+ auto ColumnEnd = *CEOrErr;
+
+ // Gap is set in ColumnEnd:31
+ if (ColumnEnd & (1u << 31))
+ isGap = true;
+ ColumnEnd &= ((1u << 31) - 1);
+
+ dLoc = {*LSDeltaOrErr, *CSOrErr, *NLOrErr, ColumnEnd};
+ Loc = {Data.LineStart, *CSOrErr, LineEnd, ColumnEnd};
+
+ return Error::success();
+}
+
void CovFunTy::encode(raw_ostream &OS, endianness Endianness) const {
// Encode Body in advance since DataSize should be known.
std::string Body;
@@ -194,6 +362,72 @@ CovMapTy::encodeFilenames(const std::optional<ArrayRef<StringRef>> &AccFilesOpt,
return {llvm::IndexedInstrProf::ComputeHash(FilenamesBlob), FilenamesBlob};
}
+Expected<uint64_t> CovFunTy::decode(const ArrayRef<uint8_t> Content,
+ uint64_t Offset, endianness Endianness) {
+ DecoderContext Data(Content, (Endianness == endianness::little));
+ Data.seek(Offset);
+
+ uint32_t DataSize;
+ [[maybe_unused]] char CoverageMapping; // Ignored
+
+#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Initializer) \
+ if (sizeof(Type) == sizeof(uint64_t)) \
+ Name = Data.getU64(); \
+ else if (sizeof(Type) == sizeof(uint32_t)) \
+ Name = Data.getU32(); \
+ else \
+ assert(sizeof(Type) == sizeof(CoverageMapping) && "Unknown type");
+
+#include "llvm/ProfileData/InstrProfData.inc"
+
+ if (!Data)
+ return Data.takeError();
+
+ [[maybe_unused]] auto ExpectedEndOffset = Data.tell() + DataSize;
+
+ // Decode body.
+ FileIDs.emplace();
+
+ auto NumFilesOrErr = Data.getULEB128();
+ if (!NumFilesOrErr)
+ return NumFilesOrErr.takeError();
+ for (unsigned I = 0, E = *NumFilesOrErr; I != E; ++I) {
+ if (auto IDOrErr = Data.getULEB128())
+ FileIDs->push_back(*IDOrErr);
+ else
+ return IDOrErr.takeError();
+ }
+
+ auto NumExprOrErr = Data.getULEB128();
+ if (!NumExprOrErr)
+ return NumExprOrErr.takeError();
+ Expressions.resize(*NumExprOrErr);
+ for (auto &[LHS, RHS] : Expressions) {
+ if (auto E = LHS.decode(Data))
+ return std::move(E);
+ if (auto E = RHS.decode(Data))
+ return std::move(E);
+ }
+
+ for (unsigned FileIdx = 0; FileIdx != *NumFilesOrErr; ++FileIdx) {
+ auto NumRegionsOrErr = Data.getULEB128();
+ if (!NumRegionsOrErr)
+ return NumRegionsOrErr.takeError();
+ auto &File = Files.emplace_back();
+
+ // Decode subarray.
+ Data.LineStart = 0;
+ for (unsigned I = 0; I != *NumRegionsOrErr; ++I) {
+ auto &Rec = File.Recs.emplace_back();
+ if (auto E = Rec.decode(Data))
+ return std::move(E);
+ }
+ }
+
+ assert(Data.tell() == ExpectedEndOffset);
+ return Data.tell();
+}
+
void CovMapTy::encode(raw_ostream &OS, endianness Endianness) const {
auto [FilenamesRef, FilenamesBlob] = encodeFilenames();
@@ -219,6 +453,36 @@ void CovMapTy::encode(raw_ostream &OS, endianness Endianness) const {
OS << FilenamesBlob;
}
+Expected<uint64_t> CovMapTy::decode(const ArrayRef<uint8_t> Content,
+ uint64_t Offset, endianness Endianness) {
+ DecoderContext Data(Content, (Endianness == endianness::little));
+ Data.seek(Offset);
+
+#define COVMAP_HEADER(Type, LLVMType, Name, Initializer) \
+ static_assert(sizeof(Type) == sizeof(uint32_t)); \
+ Type Name = Data.getU32();
+#include "llvm/ProfileData/InstrProfData.inc"
+ if (!Data)
+ return Data.takeError();
+ assert(NRecords == 0);
+ // +1: uint32_t FilenamesSize;
+ assert(CoverageSize == 0);
+ this->Version = Version;
+
+ // Decode Body -- Filenames.
+ StringRef FnBlob = Data.getBytes(FilenamesSize);
+ if (!Data)
+ return Data.takeError();
+ this->FilenamesRef = MD5Hash(FnBlob);
+ this->Filenames.emplace();
+ if (auto E = RawCoverageFilenamesReader(FnBlob, *this->Filenames)
+ .read(static_cast<CovMapVersion>(Version)))
+ return E;
+
+ Offset = Data.tell();
+ return Offset;
+}
+
void CounterTy::mapping(llvm::yaml::IO &IO) {
IO.mapOptional("Tag", Tag);
IO.mapOptional("Val", Val);
@@ -289,8 +553,7 @@ void llvm::yaml::ScalarEnumerationTraits<RecTy::ExtTagTy>::enumeration(
namespace {
struct PrfNamesSection : ELFYAML::CovMapSectionBase {
- using PrfNamesTy = SmallVector<std::string>;
- SmallVector<PrfNamesTy, 1> PrfNames;
+ InstrProfSymtab::PrfNamesChunksTy PrfNames;
PrfNamesSection() { Name = "__llvm_prf_names"; }
static bool nameMatches(StringRef Name) { return Name == "__llvm_prf_names"; }
@@ -328,6 +591,26 @@ struct CovMapSection : ELFYAML::CovMapSectionBase {
IO.mapOptional("CovMap", CovMaps);
}
+ Error decode(ArrayRef<uint8_t> Blob, unsigned AddressAlign,
+ endianness Endianness) {
+ uint64_t Offset = 0;
+
+ while (true) {
+ Offset = llvm::alignTo(Offset, AddressAlign);
+ if (Offset >= Blob.size()) {
+ break;
+ }
+ auto &CovMap = CovMaps.emplace_back();
+ auto Result = CovMap.decode(Blob, Offset, Endianness);
+ if (!Result) {
+ return Result.takeError();
+ }
+ Offset = *Result;
+ }
+
+ return Error::success();
+ }
+
Error encode(raw_ostream &OS, endianness Endianness) const override {
auto BaseOffset = OS.tell();
for (const auto &CovMap : CovMaps) {
@@ -354,6 +637,28 @@ struct CovFunSection : ELFYAML::CovMapSectionBase {
IO.mapOptional("CovFun", CovFuns);
}
+ static Expected<std::vector<CovFunTy>> decode(ArrayRef<uint8_t> CovFunA,
+ unsigned AddressAlign,
+ endianness Endianness) {
+ std::vector<CovFunTy> CovFuns;
+ uint64_t Offset = 0;
+
+ while (true) {
+ Offset = llvm::alignTo(Offset, AddressAlign);
+ if (Offset >= CovFunA.size())
+ break;
+
+ auto &CovFun = CovFuns.emplace_back();
+ auto Result = CovFun.decode(CovFunA, Offset, Endianness);
+ if (!Result)
+ return Result.takeError();
+
+ Offset = *Result;
+ }
+
+ return std::move(CovFuns);
+ }
+
Error encode(raw_ostream &OS, endianness Endianness) const override {
auto BaseOffset = OS.tell();
for (auto [I, CovFun] : enumerate(CovFuns)) {
@@ -371,6 +676,7 @@ class CovMapFilenamesResolver {
protected:
DenseMap<uint64_t, struct CovMapTy *> CovMapByRef;
+ std::vector<CovMapTy> TempCovMaps; // For Decoder
public:
void collectCovMap(std::vector<CovMapTy> &CovMaps) {
@@ -378,6 +684,11 @@ class CovMapFilenamesResolver {
CovMapByRef[CovMap.FilenamesRef] = &CovMap;
}
+ void moveAndCollectCovMap(std::vector<CovMapTy> &&CovMaps) {
+ TempCovMaps = std::move(CovMaps);
+ collectCovMap(TempCovMaps);
+ }
+
void collectCovFunFilenames(std::vector<CovFunTy> &CovFuns) {
for (auto &CovFun : CovFuns) {
auto &Filenames = FilenamesByCovMap[CovFun.FilenamesRef];
@@ -445,6 +756,59 @@ class CovMapFilenamesResolver {
}
};
+class DecoderImpl : public Decoder, CovMapFilenamesResolver {
+ std::unique_ptr<InstrProfSymtab> ProfileNames;
+
+public:
+ DecoderImpl(endianness Endianness, bool CovMapEnabled)
+ : Decoder(Endianness), ProfileNames(std::make_unique<InstrProfSymtab>()) {
+ enabled = CovMapEnabled;
+ }
+
+ Error acquire(unsigned AddressAlign, StringRef Name,
+ ArrayRef<uint8_t> Content) override {
+ // Don't register anything.
+ if (!enabled)
+ return Error::success();
+
+ if (CovMapSection::nameMatches(Name)) {
+ // Decode CovMaps in advance, since only CovMap knows its Version.
+ // CovMaps is restored (into CovMapSection) later.
+ auto TempCovMap = std::make_unique<CovMapSection>();
+ if (auto E = TempCovMap->decode(Content, AddressAlign, Endianness))
+ return E;
+ moveAndCollectCovMap(std::move(TempCovMap->CovMaps));
+ }
+
+ return Error::success();
+ }
+
+ Error make(ELFYAML::CovMapSectionBase *Base,
+ ArrayRef<uint8_t> Content) override {
+ if (auto *S = dyn_cast<CovMapSection>(Base)) {
+ // Store predecoded CovMaps.
+ S->CovMaps = std::move(TempCovMaps);
+ return Error::success();
+ } else if (auto *S = dyn_cast<PrfNamesSection>(Base)) {
+ // Decode PrfNames in advance since CovFun depends on it.
+ auto PrfNamesOrErr = ProfileNames->createAndGetList(Content);
+ if (!PrfNamesOrErr)
+ return PrfNamesOrErr.takeError();
+ S->PrfNames = std::move(*PrfNamesOrErr);
+ return Error::success();
+ } else if (auto *S = dyn_cast<CovFunSection>(Base)) {
+ auto CovFunsOrErr =
+ CovFunSection::decode(Content, S->AddressAlign, Endianness);
+ if (!CovFunsOrErr)
+ return CovFunsOrErr.takeError();
+ S->CovFuns = std::move(*CovFunsOrErr);
+ return Error::success();
+ }
+
+ llvm_unreachable("Unknown Section");
+ }
+};
+
class EncoderImpl : public Encoder, CovMapFilenamesResolver {
public:
EncoderImpl(endianness Endianness) : Encoder(Endianness) {}
@@ -461,6 +825,11 @@ class EncoderImpl : public Encoder, CovMapFilenamesResolver {
};
} // namespace
+std::unique_ptr<Decoder> Decoder::get(endianness Endianness,
+ bool CovMapEnabled) {
+ return std::make_unique<DecoderImpl>(Endianness, CovMapEnabled);
+}
+
std::unique_ptr<Encoder> Encoder::get(endianness Endianness) {
return std::make_unique<EncoderImpl>(Endianness);
}
@@ -482,4 +851,4 @@ covmap::make_unique(StringRef Name) {
return nullptr;
}
-LLVM_YAML_IS_SEQUENCE_VECTOR(PrfNamesSection::PrfNamesTy)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::InstrProfSymtab::PrfNamesTy)
diff --git a/llvm/lib/ProfileData/InstrProf.cpp b/llvm/lib/ProfileData/InstrProf.cpp
index 819ddd02a24ce..93714a5a05a0d 100644
--- a/llvm/lib/ProfileData/InstrProf.cpp
+++ b/llvm/lib/ProfileData/InstrProf.cpp
@@ -535,9 +535,9 @@ Error InstrProfSymtab::addVTableWithName(GlobalVariable &VTable,
/// \c NameStrings is a string composed of one of more possibly encoded
/// sub-strings. The substrings are separated by 0 or more zero bytes. This
/// method decodes the string and calls `NameCallback` for each substring.
-static Error
-readAndDecodeStrings(StringRef NameStrings,
- std::function<Error(StringRef)> NameCallback) {
+static Error readAndDecodeStrings(
+ StringRef NameStrings, std::function<Error(StringRef)> NameCallback,
+ std::function<void(bool)> ChunkCallback = [](bool) {}) {
const uint8_t *P = NameStrings.bytes_begin();
const uint8_t *EndP = NameStrings.bytes_end();
while (P < EndP) {
@@ -567,6 +567,7 @@ readAndDecodeStrings(StringRef NameStrings,
P += UncompressedSize;
}
// Now parse the name strings.
+ ChunkCallback(IsCompressed);
SmallVector<StringRef, 0> Names;
NameStrings.split(Names, getInstrProfNameSeparator());
for (StringRef &Name : Names)
@@ -585,6 +586,22 @@ Error InstrProfSymtab::create(StringRef NameStrings) {
std::bind(&InstrProfSymtab::addFuncName, this, std::placeholders::_1));
}
+Expected<InstrProfSymtab::PrfNamesChunksTy>
+InstrProfSymtab::createAndGetList(ArrayRef<uint8_t> Content) {
+ PrfNamesChunksTy Result;
+ PrfNamesTy *ArrayP = nullptr;
+ if (auto E = readAndDecodeStrings(
+ StringRef(reinterpret_cast<const char *>(Content.data()),
+ Content.size()),
+ [&](StringRef Name) {
+ ArrayP->emplace_back(Name.str());
+ return addFuncName(Name);
+ },
+ [&](bool IsCompressed) { ArrayP = &Result.emplace_back(); }))
+ return E;
+ return Result;
+}
+
Error InstrProfSymtab::create(StringRef FuncNameStrings,
StringRef VTableNameStrings) {
if (Error E = readAndDecodeStrings(FuncNameStrings,
diff --git a/llvm/test/tools/obj2yaml/ELF/covmap-be.yaml b/llvm/test/tools/obj2yaml/ELF/covmap-be.yaml
index b97782153192b..8423f1ad5f765 100644
--- a/llvm/test/tools/obj2yaml/ELF/covmap-be.yaml
+++ b/llvm/test/tools/obj2yaml/ELF/covmap-be.yaml
@@ -1,6 +1,8 @@
# RUN: yaml2obj %s -o %t.o
# RUN: obj2yaml %t.o > %t.plain.yaml
+# RUN: obj2yaml --covmap-raw %t.o > %t.raw.yaml
# RUN: yaml2obj %t.plain.yaml -o - | cmp %t.o -
+# RUN: yaml2obj %t.raw.yaml -o - | cmp %t.o -
# FIXME: This is synthetically created. s/ELFDATA2LSB/ELF2DATAMSB/ s/EM_X86_64/EM_PPC64/
--- !ELF
diff --git a/llvm/test/tools/obj2yaml/ELF/covmap.yaml b/llvm/test/tools/obj2yaml/ELF/covmap.yaml
index 9ec8987c6f93d..db30d373d5be1 100644
--- a/llvm/test/tools/obj2yaml/ELF/covmap.yaml
+++ b/llvm/test/tools/obj2yaml/ELF/covmap.yaml
@@ -1,6 +1,8 @@
# RUN: yaml2obj %s -o %t.o
# RUN: obj2yaml %t.o | tee %t.plain.yaml | FileCheck %s --check-prefixes=CHECK,PLAIN
+# RUN: obj2yaml --covmap-raw %t.o | tee %t.raw.yaml | FileCheck %s --check-prefixes=CHECK,COVMAP,RAWONLY,RAW,DLOC
# RUN: yaml2obj %t.plain.yaml -o - | cmp %t.o -
+# RUN: yaml2obj %t.raw.yaml -o - | cmp %t.o -
--- !ELF
FileHeader:
diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp
index b1c8032ea2192..50fd92e24aa3d 100644
--- a/llvm/tools/obj2yaml/elf2yaml.cpp
+++ b/llvm/tools/obj2yaml/elf2yaml.cpp
@@ -11,8 +11,10 @@
#include "llvm/ADT/Twine.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/ObjectYAML/CovMap.h"
#include "llvm/ObjectYAML/DWARFYAML.h"
#include "llvm/ObjectYAML/ELFYAML.h"
+#include "llvm/Support/CommandLine.h"
#include "llvm/Support/DataExtractor.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorHandling.h"
@@ -21,6 +23,10 @@
using namespace llvm;
+static cl::opt<bool> CovMapRaw("covmap-raw",
+ cl::desc("Dump raw YAML in Coverage Map."),
+ cl::cat(Cat));
+
namespace {
template <class ELFT>
@@ -97,6 +103,8 @@ class ELFDumper {
dumpStackSizesSection(const Elf_Shdr *Shdr);
Expected<ELFYAML::BBAddrMapSection *>
dumpBBAddrMapSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::Section *> dumpCovMap(const Elf_Shdr *Shdr, StringRef Name,
+ covmap::Decoder *CovMapDecoder);
Expected<ELFYAML::RawContentSection *>
dumpPlaceholderSection(const Elf_Shdr *Shdr);
@@ -580,6 +588,30 @@ ELFDumper<ELFT>::dumpSections() {
return Error::success();
};
+ auto CovMapDecoder = covmap::Decoder::get(ELFT::Endianness, CovMapRaw);
+ if (covmap::Decoder::enabled) {
+ // Look up covmap-related sections in advance.
+ for (const auto &Sec : Sections) {
+ if (Sec.sh_type != ELF::SHT_PROGBITS)
+ continue;
+
+ auto NameOrErr = Obj.getSectionName(Sec);
+ if (!NameOrErr)
+ return NameOrErr.takeError();
+
+ if (!covmap::nameMatches(*NameOrErr))
+ continue;
+
+ auto ContentOrErr = Obj.getSectionContents(Sec);
+ if (!ContentOrErr)
+ return ContentOrErr.takeError();
+
+ if (auto E = CovMapDecoder->acquire(Sec.sh_addralign, *NameOrErr,
+ *ContentOrErr))
+ return std::move(E);
+ }
+ }
+
auto GetDumper = [this](unsigned Type)
-> std::function<Expected<ELFYAML::Chunk *>(const Elf_Shdr *)> {
if (Obj.getHeader().e_machine == ELF::EM_ARM && Type == ELF::SHT_ARM_EXIDX)
@@ -661,6 +693,10 @@ ELFDumper<ELFT>::dumpSections() {
if (Error E = Add(dumpStackSizesSection(&Sec)))
return std::move(E);
continue;
+ } else if (covmap::Decoder::enabled && covmap::nameMatches(*NameOrErr)) {
+ if (Error E = Add(dumpCovMap(&Sec, *NameOrErr, CovMapDecoder.get())))
+ return std::move(E);
+ continue;
}
}
@@ -1662,6 +1698,26 @@ ELFDumper<ELFT>::dumpMipsABIFlags(const Elf_Shdr *Shdr) {
return S.release();
}
+template <class ELFT>
+Expected<ELFYAML::Section *>
+ELFDumper<ELFT>::dumpCovMap(const Elf_Shdr *Shdr, StringRef Name,
+ covmap::Decoder *CovMapDecoder) {
+ auto S = covmap::make_unique(Name);
+ assert(S);
+
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ auto ContentOrErr = Obj.getSectionContents(*Shdr);
+ if (!ContentOrErr)
+ return ContentOrErr.takeError();
+
+ if (auto E = CovMapDecoder->make(S.get(), *ContentOrErr))
+ return std::move(E);
+
+ return S.release();
+}
+
template <class ELFT>
static Error elf2yaml(raw_ostream &Out, const object::ELFFile<ELFT> &Obj,
std::unique_ptr<DWARFContext> DWARFCtx) {
@@ -1671,7 +1727,8 @@ static Error elf2yaml(raw_ostream &Out, const object::ELFFile<ELFT> &Obj,
return YAMLOrErr.takeError();
std::unique_ptr<ELFYAML::Object> YAML(YAMLOrErr.get());
- yaml::Output Yout(Out);
+ yaml::Output Yout(Out, nullptr, /*WrapColumn*/
+ (covmap::Decoder::enabled ? 160 : 70));
Yout << *YAML;
return Error::success();
diff --git a/llvm/tools/obj2yaml/obj2yaml.cpp b/llvm/tools/obj2yaml/obj2yaml.cpp
index 63c8cc55ee2d4..31018161af531 100644
--- a/llvm/tools/obj2yaml/obj2yaml.cpp
+++ b/llvm/tools/obj2yaml/obj2yaml.cpp
@@ -20,7 +20,7 @@
using namespace llvm;
using namespace llvm::object;
-static cl::OptionCategory Cat("obj2yaml Options");
+cl::OptionCategory Cat("obj2yaml Options");
static cl::opt<std::string>
InputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-"));
diff --git a/llvm/tools/obj2yaml/obj2yaml.h b/llvm/tools/obj2yaml/obj2yaml.h
index 03d7db5317acd..ee34919962575 100644
--- a/llvm/tools/obj2yaml/obj2yaml.h
+++ b/llvm/tools/obj2yaml/obj2yaml.h
@@ -16,11 +16,15 @@
#include "llvm/Object/Minidump.h"
#include "llvm/Object/Wasm.h"
#include "llvm/Object/XCOFFObjectFile.h"
+#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBufferRef.h"
#include "llvm/Support/raw_ostream.h"
#include <system_error>
enum RawSegments : unsigned { none = 0, data = 1, linkedit = 1 << 1 };
+
+extern llvm::cl::OptionCategory Cat;
+
std::error_code coff2yaml(llvm::raw_ostream &Out,
const llvm::object::COFFObjectFile &Obj);
llvm::Error elf2yaml(llvm::raw_ostream &Out,
>From daad30aaceec1334771121a08eac647f9f26789c Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Sat, 15 Mar 2025 13:03:08 +0900
Subject: [PATCH 2/3] fix test
---
llvm/test/tools/obj2yaml/ELF/covmap.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/test/tools/obj2yaml/ELF/covmap.yaml b/llvm/test/tools/obj2yaml/ELF/covmap.yaml
index fceb566ae04ce..09bcf86b44f47 100644
--- a/llvm/test/tools/obj2yaml/ELF/covmap.yaml
+++ b/llvm/test/tools/obj2yaml/ELF/covmap.yaml
@@ -48,7 +48,7 @@ Sections:
# DLOC: dLoc: [ 3, 17, 11, 2 ]
# RAW: Tag: Ref, Val: 0
- { dLoc: [ 3, 17, 11, 2 ], Tag: Ref, Val: 0 }
-# DLOC dLoc: [ 3, 6, 5, 4 ]
+# DLOC: dLoc: [ 3, 6, 5, 4 ]
# RAW: Tag: Add, Val: 0
- { dLoc: [ 3, 6, 5, 4 ], Tag: Add, Val: 0 }
# RAW: Tag: Zero, Val: 3,
>From 3ced33cdbc3814bfdf20592659039b9a87554394 Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Sat, 15 Mar 2025 13:03:30 +0900
Subject: [PATCH 3/3] covmap maybe_unused
---
llvm/lib/ObjectYAML/CovMap.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/ObjectYAML/CovMap.cpp b/llvm/lib/ObjectYAML/CovMap.cpp
index 5227e7423b982..267ea0223659a 100644
--- a/llvm/lib/ObjectYAML/CovMap.cpp
+++ b/llvm/lib/ObjectYAML/CovMap.cpp
@@ -374,7 +374,7 @@ Expected<uint64_t> CovMapTy::decode(const ArrayRef<uint8_t> Content,
#define COVMAP_HEADER(Type, LLVMType, Name, Initializer) \
static_assert(sizeof(Type) == sizeof(uint32_t)); \
- Type Name = Data.getU32();
+ [[maybe_unused]] Type Name = Data.getU32();
#include "llvm/ProfileData/InstrProfData.inc"
if (!Data)
return Data.takeError();
More information about the llvm-branch-commits
mailing list