[InstrProf] Make the IndexedInstrProf header backwards compatible.

While the contents of the profile are backwards compatible the header
itself is not. For example, when adding new fields to the header results
in significant issues. This change adds allows for portable
instantiation of the header across indexed format versions.

Differential Revision: https://reviews.llvm.org/D118390
This commit is contained in:
Snehasish Kumar 2022-01-26 19:35:17 -08:00
parent 9b67165285
commit 14cc41a020
3 changed files with 76 additions and 18 deletions

View File

@ -1028,6 +1028,16 @@ struct Header {
uint64_t Unused; // Becomes unused since version 4
uint64_t HashType;
uint64_t HashOffset;
// New fields should only be added at the end to ensure that the size
// computation is correct. The methods below need to be updated to ensure that
// the new field is read correctly.
// Reads a header struct from the buffer.
static Expected<Header> readFromBuffer(const unsigned char *Buffer);
// Returns the size of the header in bytes for all valid fields based on the
// version. I.e a older version header will return a smaller size.
size_t size() const;
};
// Profile summary data recorded in the profile data file in indexed

View File

@ -51,6 +51,7 @@
#include <memory>
#include <string>
#include <system_error>
#include <type_traits>
#include <utility>
#include <vector>
@ -1311,4 +1312,59 @@ void OverlapStats::dump(raw_fd_ostream &OS) const {
}
}
namespace IndexedInstrProf {
// A C++14 compatible version of the offsetof macro.
template <typename T1, typename T2>
inline size_t constexpr offsetOf(T1 T2::*Member) {
constexpr T2 Object{};
return size_t(&(Object.*Member)) - size_t(&Object);
}
static inline uint64_t read(const unsigned char *Buffer, size_t Offset) {
return *reinterpret_cast<const uint64_t *>(Buffer + Offset);
}
Expected<Header> Header::readFromBuffer(const unsigned char *Buffer) {
using namespace support;
static_assert(std::is_standard_layout<Header>::value,
"The header should be standard layout type since we use offset "
"of fields to read.");
Header H;
H.Magic = read(Buffer, offsetOf(&Header::Magic));
// Check the magic number.
uint64_t Magic = endian::byte_swap<uint64_t, little>(H.Magic);
if (Magic != IndexedInstrProf::Magic)
return make_error<InstrProfError>(instrprof_error::bad_magic);
// Read the version.
H.Version = read(Buffer, offsetOf(&Header::Version));
uint64_t FormatVersion = endian::byte_swap<uint64_t, little>(H.Version);
if (GET_VERSION(FormatVersion) >
IndexedInstrProf::ProfVersion::CurrentVersion)
return make_error<InstrProfError>(instrprof_error::unsupported_version);
switch (GET_VERSION(FormatVersion)) {
// When a new field is added in the header add a case statement here to
// populate it.
default:
H.HashType = read(Buffer, offsetOf(&Header::HashType));
H.HashOffset = read(Buffer, offsetOf(&Header::HashOffset));
}
return H;
}
size_t Header::size() const {
switch (GET_VERSION(Version)) {
// When a new field is added to the header add a case statement here to
// compute the size as offset of the new field + size of the new field. This
// relies on the field being added to the end of the list.
default:
return offsetOf(&Header::HashOffset) + sizeof(Header::HashOffset);
}
}
} // namespace IndexedInstrProf
} // end namespace llvm

View File

@ -934,24 +934,17 @@ Error IndexedInstrProfReader::readHeader() {
if ((const unsigned char *)DataBuffer->getBufferEnd() - Cur < 24)
return error(instrprof_error::truncated);
auto *Header = reinterpret_cast<const IndexedInstrProf::Header *>(Cur);
Cur += sizeof(IndexedInstrProf::Header);
auto HeaderOr = IndexedInstrProf::Header::readFromBuffer(Start);
if (!HeaderOr)
return HeaderOr.takeError();
// Check the magic number.
uint64_t Magic = endian::byte_swap<uint64_t, little>(Header->Magic);
if (Magic != IndexedInstrProf::Magic)
return error(instrprof_error::bad_magic);
const IndexedInstrProf::Header *Header = &HeaderOr.get();
Cur += Header->size();
// Read the version.
uint64_t FormatVersion = endian::byte_swap<uint64_t, little>(Header->Version);
if (GET_VERSION(FormatVersion) >
IndexedInstrProf::ProfVersion::CurrentVersion)
return error(instrprof_error::unsupported_version);
Cur = readSummary((IndexedInstrProf::ProfVersion)FormatVersion, Cur,
Cur = readSummary((IndexedInstrProf::ProfVersion)Header->Version, Cur,
/* UseCS */ false);
if (FormatVersion & VARIANT_MASK_CSIR_PROF)
Cur = readSummary((IndexedInstrProf::ProfVersion)FormatVersion, Cur,
if (Header->Version & VARIANT_MASK_CSIR_PROF)
Cur = readSummary((IndexedInstrProf::ProfVersion)Header->Version, Cur,
/* UseCS */ true);
// Read the hash type and start offset.
@ -963,9 +956,8 @@ Error IndexedInstrProfReader::readHeader() {
uint64_t HashOffset = endian::byte_swap<uint64_t, little>(Header->HashOffset);
// The rest of the file is an on disk hash table.
auto IndexPtr =
std::make_unique<InstrProfReaderIndex<OnDiskHashTableImplV3>>(
Start + HashOffset, Cur, Start, HashType, FormatVersion);
auto IndexPtr = std::make_unique<InstrProfReaderIndex<OnDiskHashTableImplV3>>(
Start + HashOffset, Cur, Start, HashType, Header->Version);
// Load the remapping table now if requested.
if (RemappingBuffer) {