Skip to content

Implements PGOBBAddrMap in Object and ObjectYAML with tests [1/5] #71750

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion llvm/include/llvm/Object/ELF.h
Original file line number Diff line number Diff line change
Expand Up @@ -413,8 +413,12 @@ class ELFFile {
/// within the text section that the SHT_LLVM_BB_ADDR_MAP section \p Sec
/// is associated with. If the current ELFFile is relocatable, a corresponding
/// \p RelaSec must be passed in as an argument.
/// Optional out variable to collect all PGO Analyses. New elements are only
/// added if no error occurs. If not provided, the PGO Analyses are decoded
/// then ignored.
Expected<std::vector<BBAddrMap>>
decodeBBAddrMap(const Elf_Shdr &Sec, const Elf_Shdr *RelaSec = nullptr) const;
decodeBBAddrMap(const Elf_Shdr &Sec, const Elf_Shdr *RelaSec = nullptr,
std::vector<PGOAnalysisMap> *PGOAnalyses = nullptr) const;

/// Returns a map from every section matching \p IsMatch to its relocation
/// section, or \p nullptr if it has no relocation section. This function
Expand Down
8 changes: 6 additions & 2 deletions llvm/include/llvm/Object/ELFObjectFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,13 @@ class ELFObjectFileBase : public ObjectFile {

/// Returns a vector of all BB address maps in the object file. When
// `TextSectionIndex` is specified, only returns the BB address maps
// corresponding to the section with that index.
// corresponding to the section with that index. When `PGOAnalyses`is
// specified, the vector is cleared then filled with extra PGO data.
// `PGOAnalyses` will always be the same length as the return value on
// success, otherwise it is empty.
Expected<std::vector<BBAddrMap>>
readBBAddrMap(std::optional<unsigned> TextSectionIndex = std::nullopt) const;
readBBAddrMap(std::optional<unsigned> TextSectionIndex = std::nullopt,
std::vector<PGOAnalysisMap> *PGOAnalyses = nullptr) const;
};

class ELFSectionRef : public SectionRef {
Expand Down
55 changes: 55 additions & 0 deletions llvm/include/llvm/Object/ELFTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Object/Error.h"
#include "llvm/Support/BlockFrequency.h"
#include "llvm/Support/BranchProbability.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/MathExtras.h"
Expand Down Expand Up @@ -875,6 +877,59 @@ struct BBAddrMap {
std::vector<BBEntry> BBEntries; // Basic block entries for this function.
};

/// A feature extension of BBAddrMap that holds information relevant to PGO.
struct PGOAnalysisMap {
/// Bitmask of optional features to include in the PGO extended map.
enum class Features {
FuncEntryCnt = (1 << 0),
BBFreq = (1 << 1),
BrProb = (1 << 2),
};

/// Extra basic block data with fields for block frequency and branch
/// probability.
struct PGOBBEntry {
/// Single successor of a given basic block that contains the tag and branch
/// probability associated with it.
struct SuccessorEntry {
/// Unique ID of this successor basic block.
uint32_t ID;
/// Branch Probability of the edge to this successor taken from MBPI.
BranchProbability Prob;

bool operator==(const SuccessorEntry &Other) const {
return std::tie(ID, Prob) == std::tie(Other.ID, Other.Prob);
}
};

/// Block frequency taken from MBFI
BlockFrequency BlockFreq;
/// List of successors of the current block
llvm::SmallVector<SuccessorEntry, 2> Successors;

bool operator==(const PGOBBEntry &Other) const {
return std::tie(BlockFreq, Successors) ==
std::tie(Other.BlockFreq, Other.Successors);
}
};

uint64_t FuncEntryCount; // Prof count from IR function
std::vector<PGOBBEntry> BBEntries; // Extended basic block entries

// Flags to indicate if each PGO related info was enabled in this function
bool FuncEntryCountEnabled : 1;
bool BBFreqEnabled : 1;
bool BBSuccProbEnabled : 1;

bool operator==(const PGOAnalysisMap &Other) const {
return std::tie(FuncEntryCount, BBEntries, FuncEntryCountEnabled,
BBFreqEnabled, BBSuccProbEnabled) ==
std::tie(Other.FuncEntryCount, Other.BBEntries,
Other.FuncEntryCountEnabled, Other.BBFreqEnabled,
Other.BBSuccProbEnabled);
}
};

} // end namespace object.
} // end namespace llvm.

Expand Down
33 changes: 33 additions & 0 deletions llvm/include/llvm/ObjectYAML/ELFYAML.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,19 @@ struct BBAddrMapEntry {
std::optional<std::vector<BBEntry>> BBEntries;
};

struct PGOAnalysisMapEntry {
struct PGOBBEntry {
struct SuccessorEntry {
uint32_t ID;
llvm::yaml::Hex32 BrProb;
};
std::optional<uint64_t> BBFreq;
std::optional<std::vector<SuccessorEntry>> Successors;
};
std::optional<uint64_t> FuncEntryCount;
std::optional<std::vector<PGOBBEntry>> PGOBBEntries;
};

struct StackSizeEntry {
llvm::yaml::Hex64 Address;
llvm::yaml::Hex64 Size;
Expand Down Expand Up @@ -317,6 +330,7 @@ struct SectionHeaderTable : Chunk {

struct BBAddrMapSection : Section {
std::optional<std::vector<BBAddrMapEntry>> Entries;
std::optional<std::vector<PGOAnalysisMapEntry>> PGOAnalyses;

BBAddrMapSection() : Section(ChunkKind::BBAddrMap) {}

Expand Down Expand Up @@ -737,6 +751,10 @@ bool shouldAllocateFileSpace(ArrayRef<ProgramHeader> Phdrs,
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::StackSizeEntry)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::BBAddrMapEntry)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::BBAddrMapEntry::BBEntry)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::PGOAnalysisMapEntry)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::PGOAnalysisMapEntry::PGOBBEntry)
LLVM_YAML_IS_SEQUENCE_VECTOR(
llvm::ELFYAML::PGOAnalysisMapEntry::PGOBBEntry::SuccessorEntry)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::DynamicEntry)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::LinkerOption)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::CallGraphEntryWeight)
Expand Down Expand Up @@ -905,6 +923,21 @@ template <> struct MappingTraits<ELFYAML::BBAddrMapEntry::BBEntry> {
static void mapping(IO &IO, ELFYAML::BBAddrMapEntry::BBEntry &Rel);
};

template <> struct MappingTraits<ELFYAML::PGOAnalysisMapEntry> {
static void mapping(IO &IO, ELFYAML::PGOAnalysisMapEntry &Rel);
};

template <> struct MappingTraits<ELFYAML::PGOAnalysisMapEntry::PGOBBEntry> {
static void mapping(IO &IO, ELFYAML::PGOAnalysisMapEntry::PGOBBEntry &Rel);
};

template <>
struct MappingTraits<ELFYAML::PGOAnalysisMapEntry::PGOBBEntry::SuccessorEntry> {
static void
mapping(IO &IO,
ELFYAML::PGOAnalysisMapEntry::PGOBBEntry::SuccessorEntry &Rel);
};

template <> struct MappingTraits<ELFYAML::GnuHashHeader> {
static void mapping(IO &IO, ELFYAML::GnuHashHeader &Rel);
};
Expand Down
147 changes: 111 additions & 36 deletions llvm/lib/Object/ELF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -645,11 +645,36 @@ ELFFile<ELFT>::toMappedAddr(uint64_t VAddr, WarningHandler WarnHandler) const {
return base() + Offset;
}

template <class ELFT>
Expected<std::vector<BBAddrMap>>
ELFFile<ELFT>::decodeBBAddrMap(const Elf_Shdr &Sec,
const Elf_Shdr *RelaSec) const {
bool IsRelocatable = getHeader().e_type == ELF::ET_REL;
// Helper to extract and decode the next ULEB128 value as unsigned int.
// Returns zero and sets ULEBSizeErr if the ULEB128 value exceeds the unsigned
// int limit.
// Also returns zero if ULEBSizeErr is already in an error state.
// ULEBSizeErr is an out variable if an error occurs.
template <typename IntTy, std::enable_if_t<std::is_unsigned_v<IntTy>, int> = 0>
static IntTy readULEB128As(DataExtractor &Data, DataExtractor::Cursor &Cur,
Error &ULEBSizeErr) {
// Bail out and do not extract data if ULEBSizeErr is already set.
if (ULEBSizeErr)
return 0;
uint64_t Offset = Cur.tell();
uint64_t Value = Data.getULEB128(Cur);
if (Value > std::numeric_limits<IntTy>::max()) {
ULEBSizeErr = createError("ULEB128 value at offset 0x" +
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there any testing of this error? I couldn't immediately find any in this patch, but I suppose it could exist already.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I this is already tested in the regular BBAddrMap tests. Found at ELFObjectFileTest.cpp:564

Twine::utohexstr(Offset) + " exceeds UINT" +
Twine(std::numeric_limits<IntTy>::digits) +
"_MAX (0x" + Twine::utohexstr(Value) + ")");
return 0;
}
return static_cast<IntTy>(Value);
}

template <typename ELFT>
static Expected<std::vector<BBAddrMap>>
decodeBBAddrMapImpl(const ELFFile<ELFT> &EF,
const typename ELFFile<ELFT>::Elf_Shdr &Sec,
const typename ELFFile<ELFT>::Elf_Shdr *RelaSec,
std::vector<PGOAnalysisMap> *PGOAnalyses) {
bool IsRelocatable = EF.getHeader().e_type == ELF::ET_REL;

// This DenseMap maps the offset of each function (the location of the
// reference to the function in the SHT_LLVM_BB_ADDR_MAP section) to the
Expand All @@ -659,44 +684,30 @@ ELFFile<ELFT>::decodeBBAddrMap(const Elf_Shdr &Sec,
assert(RelaSec &&
"Can't read a SHT_LLVM_BB_ADDR_MAP section in a relocatable "
"object file without providing a relocation section.");
Expected<Elf_Rela_Range> Relas = this->relas(*RelaSec);
Expected<typename ELFFile<ELFT>::Elf_Rela_Range> Relas = EF.relas(*RelaSec);
if (!Relas)
return createError("unable to read relocations for section " +
describe(*this, Sec) + ": " +
describe(EF, Sec) + ": " +
toString(Relas.takeError()));
for (Elf_Rela Rela : *Relas)
for (typename ELFFile<ELFT>::Elf_Rela Rela : *Relas)
FunctionOffsetTranslations[Rela.r_offset] = Rela.r_addend;
}
Expected<ArrayRef<uint8_t>> ContentsOrErr = getSectionContents(Sec);
Expected<ArrayRef<uint8_t>> ContentsOrErr = EF.getSectionContents(Sec);
if (!ContentsOrErr)
return ContentsOrErr.takeError();
ArrayRef<uint8_t> Content = *ContentsOrErr;
DataExtractor Data(Content, isLE(), ELFT::Is64Bits ? 8 : 4);
DataExtractor Data(Content, EF.isLE(), ELFT::Is64Bits ? 8 : 4);
std::vector<BBAddrMap> FunctionEntries;

DataExtractor::Cursor Cur(0);
Error ULEBSizeErr = Error::success();
Error MetadataDecodeErr = Error::success();
// Helper to extract and decode the next ULEB128 value as uint32_t.
// Returns zero and sets ULEBSizeErr if the ULEB128 value exceeds the uint32_t
// limit.
// Also returns zero if ULEBSizeErr is already in an error state.
auto ReadULEB128AsUInt32 = [&Data, &Cur, &ULEBSizeErr]() -> uint32_t {
// Bail out and do not extract data if ULEBSizeErr is already set.
if (ULEBSizeErr)
return 0;
uint64_t Offset = Cur.tell();
uint64_t Value = Data.getULEB128(Cur);
if (Value > UINT32_MAX) {
ULEBSizeErr = createError(
"ULEB128 value at offset 0x" + Twine::utohexstr(Offset) +
" exceeds UINT32_MAX (0x" + Twine::utohexstr(Value) + ")");
return 0;
}
return static_cast<uint32_t>(Value);
};

uint8_t Version = 0;
uint8_t Feature = 0;
bool FuncEntryCountEnabled = false;
bool BBFreqEnabled = false;
bool BrProbEnabled = false;
while (!ULEBSizeErr && !MetadataDecodeErr && Cur &&
Cur.tell() < Content.size()) {
if (Sec.sh_type == ELF::SHT_LLVM_BB_ADDR_MAP) {
Expand All @@ -706,10 +717,21 @@ ELFFile<ELFT>::decodeBBAddrMap(const Elf_Shdr &Sec,
if (Version > 2)
return createError("unsupported SHT_LLVM_BB_ADDR_MAP version: " +
Twine(static_cast<int>(Version)));
Data.getU8(Cur); // Feature byte
Feature = Data.getU8(Cur); // Feature byte
FuncEntryCountEnabled =
Feature & uint8_t(PGOAnalysisMap::Features::FuncEntryCnt);
BBFreqEnabled = Feature & uint8_t(PGOAnalysisMap::Features::BBFreq);
BrProbEnabled = Feature & uint8_t(PGOAnalysisMap::Features::BrProb);
if (Feature != 0 && Version < 2 && Cur)
return createError(
"version should be >= 2 for SHT_LLVM_BB_ADDR_MAP when "
"PGO features are enabled: version = " +
Twine(static_cast<int>(Version)) +
" feature = " + Twine(static_cast<int>(Feature)));
}
uint64_t SectionOffset = Cur.tell();
uintX_t Address = static_cast<uintX_t>(Data.getAddress(Cur));
auto Address =
static_cast<typename ELFFile<ELFT>::uintX_t>(Data.getAddress(Cur));
if (!Cur)
return Cur.takeError();
if (IsRelocatable) {
Expand All @@ -718,20 +740,23 @@ ELFFile<ELFT>::decodeBBAddrMap(const Elf_Shdr &Sec,
if (FOTIterator == FunctionOffsetTranslations.end()) {
return createError("failed to get relocation data for offset: " +
Twine::utohexstr(SectionOffset) + " in section " +
describe(*this, Sec));
describe(EF, Sec));
}
Address = FOTIterator->second;
}
uint32_t NumBlocks = ReadULEB128AsUInt32();
uint32_t NumBlocks = readULEB128As<uint32_t>(Data, Cur, ULEBSizeErr);

std::vector<BBAddrMap::BBEntry> BBEntries;
uint32_t PrevBBEndOffset = 0;
for (uint32_t BlockIndex = 0;
!MetadataDecodeErr && !ULEBSizeErr && Cur && (BlockIndex < NumBlocks);
++BlockIndex) {
uint32_t ID = Version >= 2 ? ReadULEB128AsUInt32() : BlockIndex;
uint32_t Offset = ReadULEB128AsUInt32();
uint32_t Size = ReadULEB128AsUInt32();
uint32_t MD = ReadULEB128AsUInt32();
uint32_t ID = Version >= 2
? readULEB128As<uint32_t>(Data, Cur, ULEBSizeErr)
: BlockIndex;
uint32_t Offset = readULEB128As<uint32_t>(Data, Cur, ULEBSizeErr);
uint32_t Size = readULEB128As<uint32_t>(Data, Cur, ULEBSizeErr);
uint32_t MD = readULEB128As<uint32_t>(Data, Cur, ULEBSizeErr);
if (Version >= 1) {
// Offset is calculated relative to the end of the previous BB.
Offset += PrevBBEndOffset;
Expand All @@ -746,6 +771,44 @@ ELFFile<ELFT>::decodeBBAddrMap(const Elf_Shdr &Sec,
BBEntries.push_back({ID, Offset, Size, *MetadataOrErr});
}
FunctionEntries.emplace_back(Address, std::move(BBEntries));

if (FuncEntryCountEnabled || BBFreqEnabled || BrProbEnabled) {
// Function entry count
uint64_t FuncEntryCount =
FuncEntryCountEnabled
? readULEB128As<uint64_t>(Data, Cur, ULEBSizeErr)
: 0;

std::vector<PGOAnalysisMap::PGOBBEntry> PGOBBEntries;
for (uint32_t BlockIndex = 0; !MetadataDecodeErr && !ULEBSizeErr && Cur &&
(BlockIndex < NumBlocks);
++BlockIndex) {
// Block frequency
uint64_t BBF =
BBFreqEnabled ? readULEB128As<uint64_t>(Data, Cur, ULEBSizeErr) : 0;

// Branch probability
llvm::SmallVector<PGOAnalysisMap::PGOBBEntry::SuccessorEntry, 2>
Successors;
if (BrProbEnabled) {
auto SuccCount = readULEB128As<uint64_t>(Data, Cur, ULEBSizeErr);
for (uint64_t I = 0; I < SuccCount; ++I) {
uint32_t BBID = readULEB128As<uint32_t>(Data, Cur, ULEBSizeErr);
uint32_t BrProb = readULEB128As<uint32_t>(Data, Cur, ULEBSizeErr);
if (PGOAnalyses)
Successors.push_back({BBID, BranchProbability::getRaw(BrProb)});
}
}

if (PGOAnalyses)
PGOBBEntries.push_back({BlockFrequency(BBF), std::move(Successors)});
}

if (PGOAnalyses)
PGOAnalyses->push_back({FuncEntryCount, std::move(PGOBBEntries),
FuncEntryCountEnabled, BBFreqEnabled,
BrProbEnabled});
}
}
// Either Cur is in the error state, or we have an error in ULEBSizeErr or
// MetadataDecodeErr (but not both), but we join all errors here to be safe.
Expand All @@ -755,6 +818,18 @@ ELFFile<ELFT>::decodeBBAddrMap(const Elf_Shdr &Sec,
return FunctionEntries;
}

template <class ELFT>
Expected<std::vector<BBAddrMap>>
ELFFile<ELFT>::decodeBBAddrMap(const Elf_Shdr &Sec, const Elf_Shdr *RelaSec,
std::vector<PGOAnalysisMap> *PGOAnalyses) const {
size_t OriginalPGOSize = PGOAnalyses ? PGOAnalyses->size() : 0;
auto AddrMapsOrErr = decodeBBAddrMapImpl(*this, Sec, RelaSec, PGOAnalyses);
// remove new analyses when an error occurs
if (!AddrMapsOrErr && PGOAnalyses)
PGOAnalyses->resize(OriginalPGOSize);
return std::move(AddrMapsOrErr);
}

template <class ELFT>
Expected<
MapVector<const typename ELFT::Shdr *, const typename ELFT::Shdr *>>
Expand Down
Loading