10b57cec5SDimitry Andric //===- MergingTypeTableBuilder.cpp ----------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h"
100b57cec5SDimitry Andric #include "llvm/ADT/ArrayRef.h"
110b57cec5SDimitry Andric #include "llvm/DebugInfo/CodeView/CodeView.h"
120b57cec5SDimitry Andric #include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h"
1381ad6265SDimitry Andric #include "llvm/DebugInfo/CodeView/TypeHashing.h"
140b57cec5SDimitry Andric #include "llvm/DebugInfo/CodeView/TypeIndex.h"
150b57cec5SDimitry Andric #include "llvm/Support/Allocator.h"
1681ad6265SDimitry Andric #include "llvm/Support/ErrorHandling.h"
170b57cec5SDimitry Andric #include <cassert>
180b57cec5SDimitry Andric #include <cstdint>
190b57cec5SDimitry Andric #include <cstring>
200b57cec5SDimitry Andric 
210b57cec5SDimitry Andric using namespace llvm;
220b57cec5SDimitry Andric using namespace llvm::codeview;
230b57cec5SDimitry Andric 
nextTypeIndex() const240b57cec5SDimitry Andric TypeIndex MergingTypeTableBuilder::nextTypeIndex() const {
250b57cec5SDimitry Andric   return TypeIndex::fromArrayIndex(SeenRecords.size());
260b57cec5SDimitry Andric }
270b57cec5SDimitry Andric 
MergingTypeTableBuilder(BumpPtrAllocator & Storage)280b57cec5SDimitry Andric MergingTypeTableBuilder::MergingTypeTableBuilder(BumpPtrAllocator &Storage)
290b57cec5SDimitry Andric     : RecordStorage(Storage) {
300b57cec5SDimitry Andric   SeenRecords.reserve(4096);
310b57cec5SDimitry Andric }
320b57cec5SDimitry Andric 
330b57cec5SDimitry Andric MergingTypeTableBuilder::~MergingTypeTableBuilder() = default;
340b57cec5SDimitry Andric 
getFirst()35bdd1243dSDimitry Andric std::optional<TypeIndex> MergingTypeTableBuilder::getFirst() {
360b57cec5SDimitry Andric   if (empty())
37bdd1243dSDimitry Andric     return std::nullopt;
380b57cec5SDimitry Andric 
390b57cec5SDimitry Andric   return TypeIndex(TypeIndex::FirstNonSimpleIndex);
400b57cec5SDimitry Andric }
410b57cec5SDimitry Andric 
getNext(TypeIndex Prev)42bdd1243dSDimitry Andric std::optional<TypeIndex> MergingTypeTableBuilder::getNext(TypeIndex Prev) {
430b57cec5SDimitry Andric   if (++Prev == nextTypeIndex())
44bdd1243dSDimitry Andric     return std::nullopt;
450b57cec5SDimitry Andric   return Prev;
460b57cec5SDimitry Andric }
470b57cec5SDimitry Andric 
getType(TypeIndex Index)480b57cec5SDimitry Andric CVType MergingTypeTableBuilder::getType(TypeIndex Index) {
490b57cec5SDimitry Andric   CVType Type(SeenRecords[Index.toArrayIndex()]);
500b57cec5SDimitry Andric   return Type;
510b57cec5SDimitry Andric }
520b57cec5SDimitry Andric 
getTypeName(TypeIndex Index)530b57cec5SDimitry Andric StringRef MergingTypeTableBuilder::getTypeName(TypeIndex Index) {
540b57cec5SDimitry Andric   llvm_unreachable("Method not implemented");
550b57cec5SDimitry Andric }
560b57cec5SDimitry Andric 
contains(TypeIndex Index)570b57cec5SDimitry Andric bool MergingTypeTableBuilder::contains(TypeIndex Index) {
580b57cec5SDimitry Andric   if (Index.isSimple() || Index.isNoneType())
590b57cec5SDimitry Andric     return false;
600b57cec5SDimitry Andric 
610b57cec5SDimitry Andric   return Index.toArrayIndex() < SeenRecords.size();
620b57cec5SDimitry Andric }
630b57cec5SDimitry Andric 
size()640b57cec5SDimitry Andric uint32_t MergingTypeTableBuilder::size() { return SeenRecords.size(); }
650b57cec5SDimitry Andric 
capacity()660b57cec5SDimitry Andric uint32_t MergingTypeTableBuilder::capacity() { return SeenRecords.size(); }
670b57cec5SDimitry Andric 
records() const680b57cec5SDimitry Andric ArrayRef<ArrayRef<uint8_t>> MergingTypeTableBuilder::records() const {
690b57cec5SDimitry Andric   return SeenRecords;
700b57cec5SDimitry Andric }
710b57cec5SDimitry Andric 
reset()720b57cec5SDimitry Andric void MergingTypeTableBuilder::reset() {
730b57cec5SDimitry Andric   HashedRecords.clear();
740b57cec5SDimitry Andric   SeenRecords.clear();
750b57cec5SDimitry Andric }
760b57cec5SDimitry Andric 
stabilize(BumpPtrAllocator & Alloc,ArrayRef<uint8_t> Data)770b57cec5SDimitry Andric static inline ArrayRef<uint8_t> stabilize(BumpPtrAllocator &Alloc,
780b57cec5SDimitry Andric                                           ArrayRef<uint8_t> Data) {
790b57cec5SDimitry Andric   uint8_t *Stable = Alloc.Allocate<uint8_t>(Data.size());
800b57cec5SDimitry Andric   memcpy(Stable, Data.data(), Data.size());
81bdd1243dSDimitry Andric   return ArrayRef(Stable, Data.size());
820b57cec5SDimitry Andric }
830b57cec5SDimitry Andric 
insertRecordAs(hash_code Hash,ArrayRef<uint8_t> & Record)840b57cec5SDimitry Andric TypeIndex MergingTypeTableBuilder::insertRecordAs(hash_code Hash,
850b57cec5SDimitry Andric                                                   ArrayRef<uint8_t> &Record) {
860b57cec5SDimitry Andric   assert(Record.size() < UINT32_MAX && "Record too big");
87d65cd7a5SDimitry Andric   assert(Record.size() % 4 == 0 &&
88d65cd7a5SDimitry Andric          "The type record size is not a multiple of 4 bytes which will cause "
89d65cd7a5SDimitry Andric          "misalignment in the output TPI stream!");
900b57cec5SDimitry Andric 
910b57cec5SDimitry Andric   LocallyHashedType WeakHash{Hash, Record};
920b57cec5SDimitry Andric   auto Result = HashedRecords.try_emplace(WeakHash, nextTypeIndex());
930b57cec5SDimitry Andric 
940b57cec5SDimitry Andric   if (Result.second) {
950b57cec5SDimitry Andric     ArrayRef<uint8_t> RecordData = stabilize(RecordStorage, Record);
960b57cec5SDimitry Andric     Result.first->first.RecordData = RecordData;
970b57cec5SDimitry Andric     SeenRecords.push_back(RecordData);
980b57cec5SDimitry Andric   }
990b57cec5SDimitry Andric 
1000b57cec5SDimitry Andric   // Update the caller's copy of Record to point a stable copy.
1010b57cec5SDimitry Andric   TypeIndex ActualTI = Result.first->second;
1020b57cec5SDimitry Andric   Record = SeenRecords[ActualTI.toArrayIndex()];
1030b57cec5SDimitry Andric   return ActualTI;
1040b57cec5SDimitry Andric }
1050b57cec5SDimitry Andric 
1060b57cec5SDimitry Andric TypeIndex
insertRecordBytes(ArrayRef<uint8_t> & Record)1070b57cec5SDimitry Andric MergingTypeTableBuilder::insertRecordBytes(ArrayRef<uint8_t> &Record) {
1080b57cec5SDimitry Andric   return insertRecordAs(hash_value(Record), Record);
1090b57cec5SDimitry Andric }
1100b57cec5SDimitry Andric 
1110b57cec5SDimitry Andric TypeIndex
insertRecord(ContinuationRecordBuilder & Builder)1120b57cec5SDimitry Andric MergingTypeTableBuilder::insertRecord(ContinuationRecordBuilder &Builder) {
1130b57cec5SDimitry Andric   TypeIndex TI;
1140b57cec5SDimitry Andric   auto Fragments = Builder.end(nextTypeIndex());
1150b57cec5SDimitry Andric   assert(!Fragments.empty());
1160b57cec5SDimitry Andric   for (auto C : Fragments)
1170b57cec5SDimitry Andric     TI = insertRecordBytes(C.RecordData);
1180b57cec5SDimitry Andric   return TI;
1190b57cec5SDimitry Andric }
1205ffd83dbSDimitry Andric 
replaceType(TypeIndex & Index,CVType Data,bool Stabilize)1215ffd83dbSDimitry Andric bool MergingTypeTableBuilder::replaceType(TypeIndex &Index, CVType Data,
1225ffd83dbSDimitry Andric                                           bool Stabilize) {
1235ffd83dbSDimitry Andric   assert(Index.toArrayIndex() < SeenRecords.size() &&
1245ffd83dbSDimitry Andric          "This function cannot be used to insert records!");
1255ffd83dbSDimitry Andric 
1265ffd83dbSDimitry Andric   ArrayRef<uint8_t> Record = Data.data();
1275ffd83dbSDimitry Andric   assert(Record.size() < UINT32_MAX && "Record too big");
1285ffd83dbSDimitry Andric   assert(Record.size() % 4 == 0 &&
1295ffd83dbSDimitry Andric          "The type record size is not a multiple of 4 bytes which will cause "
1305ffd83dbSDimitry Andric          "misalignment in the output TPI stream!");
1315ffd83dbSDimitry Andric 
1325ffd83dbSDimitry Andric   LocallyHashedType WeakHash{hash_value(Record), Record};
1335ffd83dbSDimitry Andric   auto Result = HashedRecords.try_emplace(WeakHash, Index.toArrayIndex());
1345ffd83dbSDimitry Andric   if (!Result.second) {
1355ffd83dbSDimitry Andric     Index = Result.first->second;
1365ffd83dbSDimitry Andric     return false; // The record is already there, at a different location
1375ffd83dbSDimitry Andric   }
1385ffd83dbSDimitry Andric 
1395ffd83dbSDimitry Andric   if (Stabilize) {
1405ffd83dbSDimitry Andric     Record = stabilize(RecordStorage, Record);
1415ffd83dbSDimitry Andric     Result.first->first.RecordData = Record;
1425ffd83dbSDimitry Andric   }
1435ffd83dbSDimitry Andric 
1445ffd83dbSDimitry Andric   SeenRecords[Index.toArrayIndex()] = Record;
1455ffd83dbSDimitry Andric   return true;
1465ffd83dbSDimitry Andric }
147