16b6b8c4fSAdrian McCarthy //===- TpiStreamBuilder.cpp -   -------------------------------------------===//
26b6b8c4fSAdrian McCarthy //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66b6b8c4fSAdrian McCarthy //
76b6b8c4fSAdrian McCarthy //===----------------------------------------------------------------------===//
86b6b8c4fSAdrian McCarthy 
96b6b8c4fSAdrian McCarthy #include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"
106b6b8c4fSAdrian McCarthy #include "llvm/ADT/ArrayRef.h"
116b6b8c4fSAdrian McCarthy #include "llvm/ADT/STLExtras.h"
12*eb4c8608Sserge-sans-paille #include "llvm/DebugInfo/CodeView/RecordSerialization.h"
136b6b8c4fSAdrian McCarthy #include "llvm/DebugInfo/CodeView/TypeIndex.h"
146b6b8c4fSAdrian McCarthy #include "llvm/DebugInfo/MSF/MSFBuilder.h"
156b6b8c4fSAdrian McCarthy #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
166b6b8c4fSAdrian McCarthy #include "llvm/DebugInfo/PDB/Native/RawTypes.h"
176b6b8c4fSAdrian McCarthy #include "llvm/Support/Allocator.h"
18d9dc2829SZachary Turner #include "llvm/Support/BinaryByteStream.h"
19d9dc2829SZachary Turner #include "llvm/Support/BinaryStreamWriter.h"
206b6b8c4fSAdrian McCarthy #include "llvm/Support/Endian.h"
216b6b8c4fSAdrian McCarthy #include "llvm/Support/Error.h"
226b6b8c4fSAdrian McCarthy #include <algorithm>
236b6b8c4fSAdrian McCarthy #include <cstdint>
245519e4daSReid Kleckner #include <numeric>
256b6b8c4fSAdrian McCarthy 
266b6b8c4fSAdrian McCarthy using namespace llvm;
276b6b8c4fSAdrian McCarthy using namespace llvm::msf;
286b6b8c4fSAdrian McCarthy using namespace llvm::pdb;
296b6b8c4fSAdrian McCarthy using namespace llvm::support;
306b6b8c4fSAdrian McCarthy 
TpiStreamBuilder(MSFBuilder & Msf,uint32_t StreamIdx)316b6b8c4fSAdrian McCarthy TpiStreamBuilder::TpiStreamBuilder(MSFBuilder &Msf, uint32_t StreamIdx)
3213fc411eSReid Kleckner     : Msf(Msf), Allocator(Msf.getAllocator()), Header(nullptr), Idx(StreamIdx) {
336b6b8c4fSAdrian McCarthy }
346b6b8c4fSAdrian McCarthy 
356b6b8c4fSAdrian McCarthy TpiStreamBuilder::~TpiStreamBuilder() = default;
366b6b8c4fSAdrian McCarthy 
setVersionHeader(PdbRaw_TpiVer Version)376b6b8c4fSAdrian McCarthy void TpiStreamBuilder::setVersionHeader(PdbRaw_TpiVer Version) {
386b6b8c4fSAdrian McCarthy   VerHeader = Version;
396b6b8c4fSAdrian McCarthy }
406b6b8c4fSAdrian McCarthy 
updateTypeIndexOffsets(ArrayRef<uint16_t> Sizes)415519e4daSReid Kleckner void TpiStreamBuilder::updateTypeIndexOffsets(ArrayRef<uint16_t> Sizes) {
425519e4daSReid Kleckner   // If we just crossed an 8KB threshold, add a type index offset.
435519e4daSReid Kleckner   for (uint16_t Size : Sizes) {
445519e4daSReid Kleckner     size_t NewSize = TypeRecordBytes + Size;
455519e4daSReid Kleckner     constexpr size_t EightKB = 8 * 1024;
465519e4daSReid Kleckner     if (NewSize / EightKB > TypeRecordBytes / EightKB || TypeRecordCount == 0) {
475519e4daSReid Kleckner       TypeIndexOffsets.push_back(
485519e4daSReid Kleckner           {codeview::TypeIndex(codeview::TypeIndex::FirstNonSimpleIndex +
495519e4daSReid Kleckner                                TypeRecordCount),
505519e4daSReid Kleckner            ulittle32_t(TypeRecordBytes)});
515519e4daSReid Kleckner     }
525519e4daSReid Kleckner     ++TypeRecordCount;
535519e4daSReid Kleckner     TypeRecordBytes = NewSize;
545519e4daSReid Kleckner   }
555519e4daSReid Kleckner }
565519e4daSReid Kleckner 
addTypeRecord(ArrayRef<uint8_t> Record,Optional<uint32_t> Hash)5713fc411eSReid Kleckner void TpiStreamBuilder::addTypeRecord(ArrayRef<uint8_t> Record,
5813fc411eSReid Kleckner                                      Optional<uint32_t> Hash) {
59a7325298SAlexandre Ganea   assert(((Record.size() & 3) == 0) &&
60a7325298SAlexandre Ganea          "The type record's size is not a multiple of 4 bytes which will "
61a7325298SAlexandre Ganea          "cause misalignment in the output TPI stream!");
625519e4daSReid Kleckner   assert(Record.size() <= codeview::MaxRecordLength);
635519e4daSReid Kleckner   uint16_t OneSize = (uint16_t)Record.size();
645519e4daSReid Kleckner   updateTypeIndexOffsets(makeArrayRef(&OneSize, 1));
656e545ffcSReid Kleckner 
665519e4daSReid Kleckner   TypeRecBuffers.push_back(Record);
675519e4daSReid Kleckner   // FIXME: Require it.
6813fc411eSReid Kleckner   if (Hash)
6913fc411eSReid Kleckner     TypeHashes.push_back(*Hash);
706b6b8c4fSAdrian McCarthy }
716b6b8c4fSAdrian McCarthy 
addTypeRecords(ArrayRef<uint8_t> Types,ArrayRef<uint16_t> Sizes,ArrayRef<uint32_t> Hashes)725519e4daSReid Kleckner void TpiStreamBuilder::addTypeRecords(ArrayRef<uint8_t> Types,
735519e4daSReid Kleckner                                       ArrayRef<uint16_t> Sizes,
745519e4daSReid Kleckner                                       ArrayRef<uint32_t> Hashes) {
755519e4daSReid Kleckner   // Ignore empty type buffers. There should be no hashes or sizes in this case.
765519e4daSReid Kleckner   if (Types.empty()) {
775519e4daSReid Kleckner     assert(Sizes.empty() && Hashes.empty());
785519e4daSReid Kleckner     return;
795519e4daSReid Kleckner   }
805519e4daSReid Kleckner 
815519e4daSReid Kleckner   assert(((Types.size() & 3) == 0) &&
825519e4daSReid Kleckner          "The type record's size is not a multiple of 4 bytes which will "
835519e4daSReid Kleckner          "cause misalignment in the output TPI stream!");
845519e4daSReid Kleckner   assert(Sizes.size() == Hashes.size() && "sizes and hashes should be in sync");
855519e4daSReid Kleckner   assert(std::accumulate(Sizes.begin(), Sizes.end(), 0U) == Types.size() &&
865519e4daSReid Kleckner          "sizes of type records should sum to the size of the types");
875519e4daSReid Kleckner   updateTypeIndexOffsets(Sizes);
885519e4daSReid Kleckner 
895519e4daSReid Kleckner   TypeRecBuffers.push_back(Types);
900edbc90eSKazu Hirata   llvm::append_range(TypeHashes, Hashes);
915519e4daSReid Kleckner }
925519e4daSReid Kleckner 
finalize()936b6b8c4fSAdrian McCarthy Error TpiStreamBuilder::finalize() {
946b6b8c4fSAdrian McCarthy   if (Header)
956b6b8c4fSAdrian McCarthy     return Error::success();
966b6b8c4fSAdrian McCarthy 
976b6b8c4fSAdrian McCarthy   TpiStreamHeader *H = Allocator.Allocate<TpiStreamHeader>();
986b6b8c4fSAdrian McCarthy 
998fb441abSZachary Turner   H->Version = VerHeader;
1006b6b8c4fSAdrian McCarthy   H->HeaderSize = sizeof(TpiStreamHeader);
1016b6b8c4fSAdrian McCarthy   H->TypeIndexBegin = codeview::TypeIndex::FirstNonSimpleIndex;
1025519e4daSReid Kleckner   H->TypeIndexEnd = H->TypeIndexBegin + TypeRecordCount;
10313fc411eSReid Kleckner   H->TypeRecordBytes = TypeRecordBytes;
1046b6b8c4fSAdrian McCarthy 
1056b6b8c4fSAdrian McCarthy   H->HashStreamIndex = HashStreamIndex;
1066b6b8c4fSAdrian McCarthy   H->HashAuxStreamIndex = kInvalidStreamIndex;
1076b6b8c4fSAdrian McCarthy   H->HashKeySize = sizeof(ulittle32_t);
1088371da38SZachary Turner   H->NumHashBuckets = MaxTpiHashBuckets - 1;
1096b6b8c4fSAdrian McCarthy 
1106b6b8c4fSAdrian McCarthy   // Recall that hash values go into a completely different stream identified by
1116b6b8c4fSAdrian McCarthy   // the `HashStreamIndex` field of the `TpiStreamHeader`.  Therefore, the data
1126b6b8c4fSAdrian McCarthy   // begins at offset 0 of this independent stream.
1136b6b8c4fSAdrian McCarthy   H->HashValueBuffer.Off = 0;
1146e545ffcSReid Kleckner   H->HashValueBuffer.Length = calculateHashBufferSize();
1156e545ffcSReid Kleckner 
1166e545ffcSReid Kleckner   // We never write any adjustments into our PDBs, so this is usually some
1176e545ffcSReid Kleckner   // offset with zero length.
1186b6b8c4fSAdrian McCarthy   H->HashAdjBuffer.Off = H->HashValueBuffer.Off + H->HashValueBuffer.Length;
1196b6b8c4fSAdrian McCarthy   H->HashAdjBuffer.Length = 0;
1206e545ffcSReid Kleckner 
1216b6b8c4fSAdrian McCarthy   H->IndexOffsetBuffer.Off = H->HashAdjBuffer.Off + H->HashAdjBuffer.Length;
1226e545ffcSReid Kleckner   H->IndexOffsetBuffer.Length = calculateIndexOffsetSize();
1236b6b8c4fSAdrian McCarthy 
1246b6b8c4fSAdrian McCarthy   Header = H;
1256b6b8c4fSAdrian McCarthy   return Error::success();
1266b6b8c4fSAdrian McCarthy }
1276b6b8c4fSAdrian McCarthy 
calculateSerializedLength()128120faca4SZachary Turner uint32_t TpiStreamBuilder::calculateSerializedLength() {
12913fc411eSReid Kleckner   return sizeof(TpiStreamHeader) + TypeRecordBytes;
1306b6b8c4fSAdrian McCarthy }
1316b6b8c4fSAdrian McCarthy 
calculateHashBufferSize() const1326b6b8c4fSAdrian McCarthy uint32_t TpiStreamBuilder::calculateHashBufferSize() const {
1335519e4daSReid Kleckner   assert((TypeRecordCount == TypeHashes.size() || TypeHashes.empty()) &&
13413fc411eSReid Kleckner          "either all or no type records should have hashes");
13513fc411eSReid Kleckner   return TypeHashes.size() * sizeof(ulittle32_t);
1366b6b8c4fSAdrian McCarthy }
1376b6b8c4fSAdrian McCarthy 
calculateIndexOffsetSize() const1386e545ffcSReid Kleckner uint32_t TpiStreamBuilder::calculateIndexOffsetSize() const {
139dd3a739dSZachary Turner   return TypeIndexOffsets.size() * sizeof(codeview::TypeIndexOffset);
1406e545ffcSReid Kleckner }
1416e545ffcSReid Kleckner 
finalizeMsfLayout()1426b6b8c4fSAdrian McCarthy Error TpiStreamBuilder::finalizeMsfLayout() {
1436b6b8c4fSAdrian McCarthy   uint32_t Length = calculateSerializedLength();
1446b6b8c4fSAdrian McCarthy   if (auto EC = Msf.setStreamSize(Idx, Length))
1456b6b8c4fSAdrian McCarthy     return EC;
1466b6b8c4fSAdrian McCarthy 
1476e545ffcSReid Kleckner   uint32_t HashStreamSize =
1486e545ffcSReid Kleckner       calculateHashBufferSize() + calculateIndexOffsetSize();
1496b6b8c4fSAdrian McCarthy 
1506e545ffcSReid Kleckner   if (HashStreamSize == 0)
1516b6b8c4fSAdrian McCarthy     return Error::success();
1526b6b8c4fSAdrian McCarthy 
1536e545ffcSReid Kleckner   auto ExpectedIndex = Msf.addStream(HashStreamSize);
1546b6b8c4fSAdrian McCarthy   if (!ExpectedIndex)
1556b6b8c4fSAdrian McCarthy     return ExpectedIndex.takeError();
1566b6b8c4fSAdrian McCarthy   HashStreamIndex = *ExpectedIndex;
1576e545ffcSReid Kleckner   if (!TypeHashes.empty()) {
15813fc411eSReid Kleckner     ulittle32_t *H = Allocator.Allocate<ulittle32_t>(TypeHashes.size());
15913fc411eSReid Kleckner     MutableArrayRef<ulittle32_t> HashBuffer(H, TypeHashes.size());
16013fc411eSReid Kleckner     for (uint32_t I = 0; I < TypeHashes.size(); ++I) {
1618371da38SZachary Turner       HashBuffer[I] = TypeHashes[I] % (MaxTpiHashBuckets - 1);
1626b6b8c4fSAdrian McCarthy     }
1636e545ffcSReid Kleckner     ArrayRef<uint8_t> Bytes(
1646e545ffcSReid Kleckner         reinterpret_cast<const uint8_t *>(HashBuffer.data()),
1656e545ffcSReid Kleckner         calculateHashBufferSize());
166695ed56bSZachary Turner     HashValueStream =
1670eaee545SJonas Devlieghere         std::make_unique<BinaryByteStream>(Bytes, llvm::support::little);
1686e545ffcSReid Kleckner   }
1696b6b8c4fSAdrian McCarthy   return Error::success();
1706b6b8c4fSAdrian McCarthy }
1716b6b8c4fSAdrian McCarthy 
commit(const msf::MSFLayout & Layout,WritableBinaryStreamRef Buffer)1726b6b8c4fSAdrian McCarthy Error TpiStreamBuilder::commit(const msf::MSFLayout &Layout,
173120faca4SZachary Turner                                WritableBinaryStreamRef Buffer) {
1746b6b8c4fSAdrian McCarthy   if (auto EC = finalize())
1756b6b8c4fSAdrian McCarthy     return EC;
1766b6b8c4fSAdrian McCarthy 
1775b74ff33SZachary Turner   auto InfoS = WritableMappedBlockStream::createIndexedStream(Layout, Buffer,
1785b74ff33SZachary Turner                                                               Idx, Allocator);
1796b6b8c4fSAdrian McCarthy 
180120faca4SZachary Turner   BinaryStreamWriter Writer(*InfoS);
1816b6b8c4fSAdrian McCarthy   if (auto EC = Writer.writeObject(*Header))
1826b6b8c4fSAdrian McCarthy     return EC;
1836b6b8c4fSAdrian McCarthy 
1845519e4daSReid Kleckner   for (auto Rec : TypeRecBuffers) {
185a7325298SAlexandre Ganea     assert(!Rec.empty() && "Attempting to write an empty type record shifts "
186a7325298SAlexandre Ganea                            "all offsets in the TPI stream!");
187a7325298SAlexandre Ganea     assert(((Rec.size() & 3) == 0) &&
188a7325298SAlexandre Ganea            "The type record's size is not a multiple of 4 bytes which will "
189a7325298SAlexandre Ganea            "cause misalignment in the output TPI stream!");
19013fc411eSReid Kleckner     if (auto EC = Writer.writeBytes(Rec))
1916b6b8c4fSAdrian McCarthy       return EC;
192120366edSAlexandre Ganea   }
1936b6b8c4fSAdrian McCarthy 
1946b6b8c4fSAdrian McCarthy   if (HashStreamIndex != kInvalidStreamIndex) {
1955b74ff33SZachary Turner     auto HVS = WritableMappedBlockStream::createIndexedStream(
1965b74ff33SZachary Turner         Layout, Buffer, HashStreamIndex, Allocator);
197120faca4SZachary Turner     BinaryStreamWriter HW(*HVS);
1986e545ffcSReid Kleckner     if (HashValueStream) {
1996b6b8c4fSAdrian McCarthy       if (auto EC = HW.writeStreamRef(*HashValueStream))
2006b6b8c4fSAdrian McCarthy         return EC;
2016b6b8c4fSAdrian McCarthy     }
2026b6b8c4fSAdrian McCarthy 
2036e545ffcSReid Kleckner     for (auto &IndexOffset : TypeIndexOffsets) {
2046e545ffcSReid Kleckner       if (auto EC = HW.writeObject(IndexOffset))
2056e545ffcSReid Kleckner         return EC;
2066e545ffcSReid Kleckner     }
2076e545ffcSReid Kleckner   }
2086e545ffcSReid Kleckner 
2096b6b8c4fSAdrian McCarthy   return Error::success();
2106b6b8c4fSAdrian McCarthy }
211