diff --git a/llvm/include/llvm/ProfileData/InstrProf.h b/llvm/include/llvm/ProfileData/InstrProf.h --- a/llvm/include/llvm/ProfileData/InstrProf.h +++ b/llvm/include/llvm/ProfileData/InstrProf.h @@ -1028,6 +1028,16 @@ 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
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 diff --git a/llvm/lib/ProfileData/InstrProf.cpp b/llvm/lib/ProfileData/InstrProf.cpp --- a/llvm/lib/ProfileData/InstrProf.cpp +++ b/llvm/lib/ProfileData/InstrProf.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -1311,4 +1312,59 @@ } } +namespace IndexedInstrProf { +// A C++14 compatible version of the offsetof macro. +template +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(Buffer + Offset); +} + +Expected
Header::readFromBuffer(const unsigned char *Buffer) { + using namespace support; + static_assert(std::is_standard_layout
::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(H.Magic); + if (Magic != IndexedInstrProf::Magic) + return make_error(instrprof_error::bad_magic); + + // Read the version. + H.Version = read(Buffer, offsetOf(&Header::Version)); + uint64_t FormatVersion = endian::byte_swap(H.Version); + if (GET_VERSION(FormatVersion) > + IndexedInstrProf::ProfVersion::CurrentVersion) + return make_error(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 diff --git a/llvm/lib/ProfileData/InstrProfReader.cpp b/llvm/lib/ProfileData/InstrProfReader.cpp --- a/llvm/lib/ProfileData/InstrProfReader.cpp +++ b/llvm/lib/ProfileData/InstrProfReader.cpp @@ -934,24 +934,17 @@ if ((const unsigned char *)DataBuffer->getBufferEnd() - Cur < 24) return error(instrprof_error::truncated); - auto *Header = reinterpret_cast(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(Header->Magic); - if (Magic != IndexedInstrProf::Magic) - return error(instrprof_error::bad_magic); - - // Read the version. - uint64_t FormatVersion = endian::byte_swap(Header->Version); - if (GET_VERSION(FormatVersion) > - IndexedInstrProf::ProfVersion::CurrentVersion) - return error(instrprof_error::unsupported_version); + const IndexedInstrProf::Header *Header = &HeaderOr.get(); + Cur += Header->size(); - 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 @@ uint64_t HashOffset = endian::byte_swap(Header->HashOffset); // The rest of the file is an on disk hash table. - auto IndexPtr = - std::make_unique>( - Start + HashOffset, Cur, Start, HashType, FormatVersion); + auto IndexPtr = std::make_unique>( + Start + HashOffset, Cur, Start, HashType, Header->Version); // Load the remapping table now if requested. if (RemappingBuffer) {