diff options
Diffstat (limited to 'lib/DebugInfo/CodeView')
22 files changed, 1115 insertions, 510 deletions
diff --git a/lib/DebugInfo/CodeView/AppendingTypeTableBuilder.cpp b/lib/DebugInfo/CodeView/AppendingTypeTableBuilder.cpp new file mode 100644 index 000000000000..8828671d9be9 --- /dev/null +++ b/lib/DebugInfo/CodeView/AppendingTypeTableBuilder.cpp @@ -0,0 +1,101 @@ +//===- AppendingTypeTableBuilder.cpp --------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/AppendingTypeTableBuilder.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h" +#include "llvm/DebugInfo/CodeView/RecordSerialization.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <cstring> + +using namespace llvm; +using namespace llvm::codeview; + +TypeIndex AppendingTypeTableBuilder::nextTypeIndex() const { + return TypeIndex::fromArrayIndex(SeenRecords.size()); +} + +AppendingTypeTableBuilder::AppendingTypeTableBuilder(BumpPtrAllocator &Storage) + : RecordStorage(Storage) {} + +AppendingTypeTableBuilder::~AppendingTypeTableBuilder() = default; + +Optional<TypeIndex> AppendingTypeTableBuilder::getFirst() { + if (empty()) + return None; + + return TypeIndex(TypeIndex::FirstNonSimpleIndex); +} + +Optional<TypeIndex> AppendingTypeTableBuilder::getNext(TypeIndex Prev) { + if (++Prev == nextTypeIndex()) + return None; + return Prev; +} + +CVType AppendingTypeTableBuilder::getType(TypeIndex Index) { + CVType Type; + Type.RecordData = SeenRecords[Index.toArrayIndex()]; + const RecordPrefix *P = + reinterpret_cast<const RecordPrefix *>(Type.RecordData.data()); + Type.Type = static_cast<TypeLeafKind>(uint16_t(P->RecordKind)); + return Type; +} + +StringRef AppendingTypeTableBuilder::getTypeName(TypeIndex Index) { + llvm_unreachable("Method not implemented"); +} + +bool AppendingTypeTableBuilder::contains(TypeIndex Index) { + if (Index.isSimple() || Index.isNoneType()) + return false; + + return Index.toArrayIndex() < SeenRecords.size(); +} + +uint32_t AppendingTypeTableBuilder::size() { return SeenRecords.size(); } + +uint32_t AppendingTypeTableBuilder::capacity() { return SeenRecords.size(); } + +ArrayRef<ArrayRef<uint8_t>> AppendingTypeTableBuilder::records() const { + return SeenRecords; +} + +void AppendingTypeTableBuilder::reset() { SeenRecords.clear(); } + +TypeIndex +AppendingTypeTableBuilder::insertRecordBytes(ArrayRef<uint8_t> &Record) { + TypeIndex NewTI = nextTypeIndex(); + uint8_t *Stable = RecordStorage.Allocate<uint8_t>(Record.size()); + memcpy(Stable, Record.data(), Record.size()); + Record = ArrayRef<uint8_t>(Stable, Record.size()); + SeenRecords.push_back(Record); + return NewTI; +} + +TypeIndex +AppendingTypeTableBuilder::insertRecord(ContinuationRecordBuilder &Builder) { + TypeIndex TI; + auto Fragments = Builder.end(nextTypeIndex()); + assert(!Fragments.empty()); + for (auto C : Fragments) + TI = insertRecordBytes(C.RecordData); + return TI; +} diff --git a/lib/DebugInfo/CodeView/CMakeLists.txt b/lib/DebugInfo/CodeView/CMakeLists.txt index b94bb0c80c79..0515788d85ef 100644 --- a/lib/DebugInfo/CodeView/CMakeLists.txt +++ b/lib/DebugInfo/CodeView/CMakeLists.txt @@ -1,6 +1,8 @@ add_llvm_library(LLVMDebugInfoCodeView + AppendingTypeTableBuilder.cpp CodeViewError.cpp CodeViewRecordIO.cpp + ContinuationRecordBuilder.cpp CVSymbolVisitor.cpp CVTypeVisitor.cpp DebugChecksumsSubsection.cpp @@ -17,9 +19,13 @@ add_llvm_library(LLVMDebugInfoCodeView DebugSymbolsSubsection.cpp EnumTables.cpp Formatters.cpp + GlobalTypeTableBuilder.cpp LazyRandomTypeCollection.cpp Line.cpp + MergingTypeTableBuilder.cpp + RecordName.cpp RecordSerialization.cpp + SimpleTypeSerializer.cpp StringsAndChecksums.cpp SymbolRecordMapping.cpp SymbolDumper.cpp @@ -27,9 +33,8 @@ add_llvm_library(LLVMDebugInfoCodeView TypeDumpVisitor.cpp TypeIndex.cpp TypeIndexDiscovery.cpp - TypeName.cpp + TypeHashing.cpp TypeRecordMapping.cpp - TypeSerializer.cpp TypeStreamMerger.cpp TypeTableCollection.cpp diff --git a/lib/DebugInfo/CodeView/CVSymbolVisitor.cpp b/lib/DebugInfo/CodeView/CVSymbolVisitor.cpp index e0c7ef58c304..44a67743169e 100644 --- a/lib/DebugInfo/CodeView/CVSymbolVisitor.cpp +++ b/lib/DebugInfo/CodeView/CVSymbolVisitor.cpp @@ -11,7 +11,6 @@ #include "llvm/DebugInfo/CodeView/CodeViewError.h" #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h" -#include "llvm/Support/BinaryByteStream.h" using namespace llvm; using namespace llvm::codeview; diff --git a/lib/DebugInfo/CodeView/CVTypeVisitor.cpp b/lib/DebugInfo/CodeView/CVTypeVisitor.cpp index 79b9fdefd40e..a4182a3b2fa1 100644 --- a/lib/DebugInfo/CodeView/CVTypeVisitor.cpp +++ b/lib/DebugInfo/CodeView/CVTypeVisitor.cpp @@ -9,7 +9,6 @@ #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" -#include "llvm/ADT/TinyPtrVector.h" #include "llvm/DebugInfo/CodeView/CodeViewError.h" #include "llvm/DebugInfo/CodeView/TypeCollection.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" diff --git a/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp b/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp new file mode 100644 index 000000000000..f180fc6990fc --- /dev/null +++ b/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp @@ -0,0 +1,259 @@ +#include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h" + +using namespace llvm; +using namespace llvm::codeview; + +namespace { +struct ContinuationRecord { + ulittle16_t Kind{uint16_t(TypeLeafKind::LF_INDEX)}; + ulittle16_t Size{0}; + ulittle32_t IndexRef{0xB0C0B0C0}; +}; + +struct SegmentInjection { + SegmentInjection(TypeLeafKind Kind) { Prefix.RecordKind = Kind; } + + ContinuationRecord Cont; + RecordPrefix Prefix; +}; +} // namespace + +static void addPadding(BinaryStreamWriter &Writer) { + uint32_t Align = Writer.getOffset() % 4; + if (Align == 0) + return; + + int PaddingBytes = 4 - Align; + while (PaddingBytes > 0) { + uint8_t Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes); + cantFail(Writer.writeInteger(Pad)); + --PaddingBytes; + } +} + +static SegmentInjection InjectFieldList(TypeLeafKind::LF_FIELDLIST); +static SegmentInjection InjectMethodOverloadList(TypeLeafKind::LF_METHODLIST); + +static constexpr uint32_t ContinuationLength = sizeof(ContinuationRecord); +static constexpr uint32_t MaxSegmentLength = + MaxRecordLength - ContinuationLength; + +static inline TypeLeafKind getTypeLeafKind(ContinuationRecordKind CK) { + return (CK == ContinuationRecordKind::FieldList) ? LF_FIELDLIST + : LF_METHODLIST; +} + +ContinuationRecordBuilder::ContinuationRecordBuilder() + : SegmentWriter(Buffer), Mapping(SegmentWriter) {} + +ContinuationRecordBuilder::~ContinuationRecordBuilder() {} + +void ContinuationRecordBuilder::begin(ContinuationRecordKind RecordKind) { + assert(!Kind.hasValue()); + Kind = RecordKind; + Buffer.clear(); + SegmentWriter.setOffset(0); + SegmentOffsets.clear(); + SegmentOffsets.push_back(0); + assert(SegmentWriter.getOffset() == 0); + assert(SegmentWriter.getLength() == 0); + + const SegmentInjection *FLI = + (RecordKind == ContinuationRecordKind::FieldList) + ? &InjectFieldList + : &InjectMethodOverloadList; + const uint8_t *FLIB = reinterpret_cast<const uint8_t *>(FLI); + InjectedSegmentBytes = + ArrayRef<uint8_t>(FLIB, FLIB + sizeof(SegmentInjection)); + + CVType Type; + Type.Type = getTypeLeafKind(RecordKind); + cantFail(Mapping.visitTypeBegin(Type)); + + // Seed the first trecord with an appropriate record prefix. + RecordPrefix Prefix; + Prefix.RecordLen = 0; + Prefix.RecordKind = Type.Type; + cantFail(SegmentWriter.writeObject(Prefix)); +} + +template <typename RecordType> +void ContinuationRecordBuilder::writeMemberType(RecordType &Record) { + assert(Kind.hasValue()); + + uint32_t OriginalOffset = SegmentWriter.getOffset(); + CVMemberRecord CVMR; + CVMR.Kind = static_cast<TypeLeafKind>(Record.getKind()); + + // Member Records aren't length-prefixed, they only have a 2-byte TypeLeafKind + // at the beginning. + cantFail(SegmentWriter.writeEnum(CVMR.Kind)); + + // Let the Mapping handle the rest. + cantFail(Mapping.visitMemberBegin(CVMR)); + cantFail(Mapping.visitKnownMember(CVMR, Record)); + cantFail(Mapping.visitMemberEnd(CVMR)); + + // Make sure it's padded to 4 bytes. + addPadding(SegmentWriter); + assert(getCurrentSegmentLength() % 4 == 0); + + // The maximum length of a single segment is 64KB minus the size to insert a + // continuation. So if we are over that, inject a continuation between the + // previous member and the member that was just written, then end the previous + // segment after the continuation and begin a new one with the just-written + // member. + if (getCurrentSegmentLength() > MaxSegmentLength) { + // We need to inject some bytes before the member we just wrote but after + // the previous member. Save off the length of the member we just wrote so + // that we can do some sanity checking on it. + uint32_t MemberLength = SegmentWriter.getOffset() - OriginalOffset; + (void) MemberLength; + insertSegmentEnd(OriginalOffset); + // Since this member now becomes a new top-level record, it should have + // gotten a RecordPrefix injected, and that RecordPrefix + the member we + // just wrote should now constitute the entirety of the current "new" + // segment. + assert(getCurrentSegmentLength() == MemberLength + sizeof(RecordPrefix)); + } + + assert(getCurrentSegmentLength() % 4 == 0); + assert(getCurrentSegmentLength() <= MaxSegmentLength); +} + +uint32_t ContinuationRecordBuilder::getCurrentSegmentLength() const { + return SegmentWriter.getOffset() - SegmentOffsets.back(); +} + +void ContinuationRecordBuilder::insertSegmentEnd(uint32_t Offset) { + uint32_t SegmentBegin = SegmentOffsets.back(); + (void)SegmentBegin; + assert(Offset > SegmentBegin); + assert(Offset - SegmentBegin <= MaxSegmentLength); + + // We need to make space for the continuation record. For now we can't fill + // out the length or the TypeIndex of the back-reference, but we need the + // space to at least be there. + Buffer.insert(Offset, InjectedSegmentBytes); + + uint32_t NewSegmentBegin = Offset + ContinuationLength; + uint32_t SegmentLength = NewSegmentBegin - SegmentOffsets.back(); + (void) SegmentLength; + + assert(SegmentLength % 4 == 0); + assert(SegmentLength <= MaxRecordLength); + SegmentOffsets.push_back(NewSegmentBegin); + + // Seek to the end so that we can keep writing against the new segment. + SegmentWriter.setOffset(SegmentWriter.getLength()); + assert(SegmentWriter.bytesRemaining() == 0); +} + +CVType ContinuationRecordBuilder::createSegmentRecord( + uint32_t OffBegin, uint32_t OffEnd, Optional<TypeIndex> RefersTo) { + assert(OffEnd - OffBegin <= USHRT_MAX); + + MutableArrayRef<uint8_t> Data = Buffer.data(); + Data = Data.slice(OffBegin, OffEnd - OffBegin); + + CVType Type; + Type.Type = getTypeLeafKind(*Kind); + Type.RecordData = Data; + + // Write the length to the RecordPrefix, making sure it does not include + // sizeof(RecordPrefix.Length) + RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(Data.data()); + assert(Prefix->RecordKind == Type.Type); + Prefix->RecordLen = Data.size() - sizeof(RecordPrefix::RecordLen); + + if (RefersTo.hasValue()) { + auto Continuation = Data.take_back(ContinuationLength); + ContinuationRecord *CR = + reinterpret_cast<ContinuationRecord *>(Continuation.data()); + assert(CR->Kind == TypeLeafKind::LF_INDEX); + assert(CR->IndexRef == 0xB0C0B0C0); + CR->IndexRef = RefersTo->getIndex(); + } + + return Type; +} + +std::vector<CVType> ContinuationRecordBuilder::end(TypeIndex Index) { + CVType Type; + Type.Type = getTypeLeafKind(*Kind); + cantFail(Mapping.visitTypeEnd(Type)); + + // We're now done, and we have a series of segments each beginning at an + // offset specified in the SegmentOffsets array. We now need to iterate + // over each segment and post-process them in the following two ways: + // 1) Each top-level record has a RecordPrefix whose type is either + // LF_FIELDLIST or LF_METHODLIST, but the Length field is still 0. + // Those should all be set to the correct length now. + // 2) Each continuation record has an IndexRef field which we set to the + // magic value 0xB0C0B0C0. Now that the caller has told us the TypeIndex + // they want this sequence to start from, we can go through and update + // each one. + // + // Logically, the sequence of records we've built up looks like this: + // + // SegmentOffsets[0]: <Length> (Initially: uninitialized) + // SegmentOffsets[0]+2: LF_FIELDLIST + // SegmentOffsets[0]+4: Member[0] + // SegmentOffsets[0]+?: ... + // SegmentOffsets[0]+?: Member[4] + // SegmentOffsets[1]-8: LF_INDEX + // SegmentOffsets[1]-6: 0 + // SegmentOffsets[1]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0) + // + // SegmentOffsets[1]: <Length> (Initially: uninitialized) + // SegmentOffsets[1]+2: LF_FIELDLIST + // SegmentOffsets[1]+4: Member[0] + // SegmentOffsets[1]+?: ... + // SegmentOffsets[1]+?: Member[s] + // SegmentOffsets[2]-8: LF_INDEX + // SegmentOffsets[2]-6: 0 + // SegmentOffsets[2]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0) + // + // ... + // + // SegmentOffsets[N]: <Length> (Initially: uninitialized) + // SegmentOffsets[N]+2: LF_FIELDLIST + // SegmentOffsets[N]+4: Member[0] + // SegmentOffsets[N]+?: ... + // SegmentOffsets[N]+?: Member[t] + // + // And this is the way we have laid them out in the serialization buffer. But + // we cannot actually commit them to the underlying stream this way, due to + // the topological sorting requirement of a type stream (specifically, + // TypeIndex references can only point backwards, not forwards). So the + // sequence that we return to the caller contains the records in reverse + // order, which is the proper order for committing the serialized records. + + std::vector<CVType> Types; + Types.reserve(SegmentOffsets.size()); + + auto SO = makeArrayRef(SegmentOffsets); + + uint32_t End = SegmentWriter.getOffset(); + + Optional<TypeIndex> RefersTo; + for (uint32_t Offset : reverse(SO)) { + Types.push_back(createSegmentRecord(Offset, End, RefersTo)); + + End = Offset; + RefersTo = Index++; + } + + Kind.reset(); + return Types; +} + +// Explicitly instantiate the member function for each known type so that we can +// implement this in the cpp file. +#define TYPE_RECORD(EnumName, EnumVal, Name) +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#define MEMBER_RECORD(EnumName, EnumVal, Name) \ + template void llvm::codeview::ContinuationRecordBuilder::writeMemberType( \ + Name##Record &Record); +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" diff --git a/lib/DebugInfo/CodeView/EnumTables.cpp b/lib/DebugInfo/CodeView/EnumTables.cpp index 4cfb55a31b35..d8301cab1657 100644 --- a/lib/DebugInfo/CodeView/EnumTables.cpp +++ b/lib/DebugInfo/CodeView/EnumTables.cpp @@ -33,55 +33,9 @@ static const EnumEntry<TypeLeafKind> TypeLeafNames[] = { }; static const EnumEntry<uint16_t> RegisterNames[] = { - CV_ENUM_CLASS_ENT(RegisterId, Unknown), - CV_ENUM_CLASS_ENT(RegisterId, VFrame), - CV_ENUM_CLASS_ENT(RegisterId, AL), - CV_ENUM_CLASS_ENT(RegisterId, CL), - CV_ENUM_CLASS_ENT(RegisterId, DL), - CV_ENUM_CLASS_ENT(RegisterId, BL), - CV_ENUM_CLASS_ENT(RegisterId, AH), - CV_ENUM_CLASS_ENT(RegisterId, CH), - CV_ENUM_CLASS_ENT(RegisterId, DH), - CV_ENUM_CLASS_ENT(RegisterId, BH), - CV_ENUM_CLASS_ENT(RegisterId, AX), - CV_ENUM_CLASS_ENT(RegisterId, CX), - CV_ENUM_CLASS_ENT(RegisterId, DX), - CV_ENUM_CLASS_ENT(RegisterId, BX), - CV_ENUM_CLASS_ENT(RegisterId, SP), - CV_ENUM_CLASS_ENT(RegisterId, BP), - CV_ENUM_CLASS_ENT(RegisterId, SI), - CV_ENUM_CLASS_ENT(RegisterId, DI), - CV_ENUM_CLASS_ENT(RegisterId, EAX), - CV_ENUM_CLASS_ENT(RegisterId, ECX), - CV_ENUM_CLASS_ENT(RegisterId, EDX), - CV_ENUM_CLASS_ENT(RegisterId, EBX), - CV_ENUM_CLASS_ENT(RegisterId, ESP), - CV_ENUM_CLASS_ENT(RegisterId, EBP), - CV_ENUM_CLASS_ENT(RegisterId, ESI), - CV_ENUM_CLASS_ENT(RegisterId, EDI), - CV_ENUM_CLASS_ENT(RegisterId, ES), - CV_ENUM_CLASS_ENT(RegisterId, CS), - CV_ENUM_CLASS_ENT(RegisterId, SS), - CV_ENUM_CLASS_ENT(RegisterId, DS), - CV_ENUM_CLASS_ENT(RegisterId, FS), - CV_ENUM_CLASS_ENT(RegisterId, GS), - CV_ENUM_CLASS_ENT(RegisterId, IP), - CV_ENUM_CLASS_ENT(RegisterId, RAX), - CV_ENUM_CLASS_ENT(RegisterId, RBX), - CV_ENUM_CLASS_ENT(RegisterId, RCX), - CV_ENUM_CLASS_ENT(RegisterId, RDX), - CV_ENUM_CLASS_ENT(RegisterId, RSI), - CV_ENUM_CLASS_ENT(RegisterId, RDI), - CV_ENUM_CLASS_ENT(RegisterId, RBP), - CV_ENUM_CLASS_ENT(RegisterId, RSP), - CV_ENUM_CLASS_ENT(RegisterId, R8), - CV_ENUM_CLASS_ENT(RegisterId, R9), - CV_ENUM_CLASS_ENT(RegisterId, R10), - CV_ENUM_CLASS_ENT(RegisterId, R11), - CV_ENUM_CLASS_ENT(RegisterId, R12), - CV_ENUM_CLASS_ENT(RegisterId, R13), - CV_ENUM_CLASS_ENT(RegisterId, R14), - CV_ENUM_CLASS_ENT(RegisterId, R15), +#define CV_REGISTER(name, val) CV_ENUM_CLASS_ENT(RegisterId, name), +#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def" +#undef CV_REGISTER }; static const EnumEntry<uint32_t> PublicSymFlagNames[] = { @@ -132,7 +86,7 @@ static const EnumEntry<codeview::SourceLanguage> SourceLanguages[] = { CV_ENUM_ENT(SourceLanguage, CSharp), CV_ENUM_ENT(SourceLanguage, VB), CV_ENUM_ENT(SourceLanguage, ILAsm), CV_ENUM_ENT(SourceLanguage, Java), CV_ENUM_ENT(SourceLanguage, JScript), CV_ENUM_ENT(SourceLanguage, MSIL), - CV_ENUM_ENT(SourceLanguage, HLSL), + CV_ENUM_ENT(SourceLanguage, HLSL), CV_ENUM_ENT(SourceLanguage, D), }; static const EnumEntry<uint32_t> CompileSym2FlagNames[] = { diff --git a/lib/DebugInfo/CodeView/GlobalTypeTableBuilder.cpp b/lib/DebugInfo/CodeView/GlobalTypeTableBuilder.cpp new file mode 100644 index 000000000000..3ecd684c1e39 --- /dev/null +++ b/lib/DebugInfo/CodeView/GlobalTypeTableBuilder.cpp @@ -0,0 +1,127 @@ +//===- GlobalTypeTableBuilder.cpp -----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h" +#include "llvm/DebugInfo/CodeView/RecordSerialization.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <cstring> + +using namespace llvm; +using namespace llvm::codeview; + +TypeIndex GlobalTypeTableBuilder::nextTypeIndex() const { + return TypeIndex::fromArrayIndex(SeenRecords.size()); +} + +GlobalTypeTableBuilder::GlobalTypeTableBuilder(BumpPtrAllocator &Storage) + : RecordStorage(Storage) { + SeenRecords.reserve(4096); +} + +GlobalTypeTableBuilder::~GlobalTypeTableBuilder() = default; + +Optional<TypeIndex> GlobalTypeTableBuilder::getFirst() { + if (empty()) + return None; + + return TypeIndex(TypeIndex::FirstNonSimpleIndex); +} + +Optional<TypeIndex> GlobalTypeTableBuilder::getNext(TypeIndex Prev) { + if (++Prev == nextTypeIndex()) + return None; + return Prev; +} + +CVType GlobalTypeTableBuilder::getType(TypeIndex Index) { + CVType Type; + Type.RecordData = SeenRecords[Index.toArrayIndex()]; + const RecordPrefix *P = + reinterpret_cast<const RecordPrefix *>(Type.RecordData.data()); + Type.Type = static_cast<TypeLeafKind>(uint16_t(P->RecordKind)); + return Type; +} + +StringRef GlobalTypeTableBuilder::getTypeName(TypeIndex Index) { + llvm_unreachable("Method not implemented"); +} + +bool GlobalTypeTableBuilder::contains(TypeIndex Index) { + if (Index.isSimple() || Index.isNoneType()) + return false; + + return Index.toArrayIndex() < SeenRecords.size(); +} + +uint32_t GlobalTypeTableBuilder::size() { return SeenRecords.size(); } + +uint32_t GlobalTypeTableBuilder::capacity() { return SeenRecords.size(); } + +ArrayRef<ArrayRef<uint8_t>> GlobalTypeTableBuilder::records() const { + return SeenRecords; +} + +ArrayRef<GloballyHashedType> GlobalTypeTableBuilder::hashes() const { + return SeenHashes; +} + +void GlobalTypeTableBuilder::reset() { + HashedRecords.clear(); + SeenRecords.clear(); +} + +static inline ArrayRef<uint8_t> stabilize(BumpPtrAllocator &Alloc, + ArrayRef<uint8_t> Data) { + uint8_t *Stable = Alloc.Allocate<uint8_t>(Data.size()); + memcpy(Stable, Data.data(), Data.size()); + return makeArrayRef(Stable, Data.size()); +} + +TypeIndex GlobalTypeTableBuilder::insertRecordAs(GloballyHashedType Hash, + CreateRecord Create) { + auto Result = HashedRecords.try_emplace(Hash, nextTypeIndex()); + + if (Result.second) { + ArrayRef<uint8_t> RecordData = stabilize(RecordStorage, Create()); + SeenRecords.push_back(RecordData); + SeenHashes.push_back(Hash); + } + + // Update the caller's copy of Record to point a stable copy. + return Result.first->second; +} + +TypeIndex GlobalTypeTableBuilder::insertRecordBytes(ArrayRef<uint8_t> Record) { + GloballyHashedType GHT = + GloballyHashedType::hashType(Record, SeenHashes, SeenHashes); + return insertRecordAs(GHT, [Record]() { return Record; }); +} + +TypeIndex +GlobalTypeTableBuilder::insertRecord(ContinuationRecordBuilder &Builder) { + TypeIndex TI; + auto Fragments = Builder.end(nextTypeIndex()); + assert(!Fragments.empty()); + for (auto C : Fragments) + TI = insertRecordBytes(C.RecordData); + return TI; +} diff --git a/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp b/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp index 5aaf3f1453a8..ca8007411cad 100644 --- a/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp +++ b/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp @@ -13,7 +13,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/DebugInfo/CodeView/CodeViewError.h" -#include "llvm/DebugInfo/CodeView/TypeName.h" +#include "llvm/DebugInfo/CodeView/RecordName.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/Endian.h" @@ -58,21 +58,27 @@ LazyRandomTypeCollection::LazyRandomTypeCollection(const CVTypeArray &Types, uint32_t NumRecords) : LazyRandomTypeCollection(Types, NumRecords, PartialOffsetArray()) {} -void LazyRandomTypeCollection::reset(StringRef Data, uint32_t RecordCountHint) { +void LazyRandomTypeCollection::reset(BinaryStreamReader &Reader, + uint32_t RecordCountHint) { Count = 0; PartialOffsets = PartialOffsetArray(); - BinaryStreamReader Reader(Data, support::little); - error(Reader.readArray(Types, Reader.getLength())); + error(Reader.readArray(Types, Reader.bytesRemaining())); // Clear and then resize, to make sure existing data gets destroyed. Records.clear(); Records.resize(RecordCountHint); } +void LazyRandomTypeCollection::reset(StringRef Data, uint32_t RecordCountHint) { + BinaryStreamReader Reader(Data, support::little); + reset(Reader, RecordCountHint); +} + void LazyRandomTypeCollection::reset(ArrayRef<uint8_t> Data, uint32_t RecordCountHint) { - reset(toStringRef(Data), RecordCountHint); + BinaryStreamReader Reader(Data, support::little); + reset(Reader, RecordCountHint); } uint32_t LazyRandomTypeCollection::getOffsetOfType(TypeIndex Index) { @@ -83,12 +89,23 @@ uint32_t LazyRandomTypeCollection::getOffsetOfType(TypeIndex Index) { } CVType LazyRandomTypeCollection::getType(TypeIndex Index) { - error(ensureTypeExists(Index)); + auto EC = ensureTypeExists(Index); + error(std::move(EC)); assert(contains(Index)); return Records[Index.toArrayIndex()].Type; } +Optional<CVType> LazyRandomTypeCollection::tryGetType(TypeIndex Index) { + if (auto EC = ensureTypeExists(Index)) { + consumeError(std::move(EC)); + return None; + } + + assert(contains(Index)); + return Records[Index.toArrayIndex()].Type; +} + StringRef LazyRandomTypeCollection::getTypeName(TypeIndex Index) { if (Index.isNoneType() || Index.isSimple()) return TypeIndex::simpleTypeName(Index); @@ -112,6 +129,9 @@ StringRef LazyRandomTypeCollection::getTypeName(TypeIndex Index) { } bool LazyRandomTypeCollection::contains(TypeIndex Index) { + if (Index.isSimple() || Index.isNoneType()) + return false; + if (Records.size() <= Index.toArrayIndex()) return false; if (!Records[Index.toArrayIndex()].Type.valid()) diff --git a/lib/DebugInfo/CodeView/MergingTypeTableBuilder.cpp b/lib/DebugInfo/CodeView/MergingTypeTableBuilder.cpp new file mode 100644 index 000000000000..8aee4aa2e2ae --- /dev/null +++ b/lib/DebugInfo/CodeView/MergingTypeTableBuilder.cpp @@ -0,0 +1,128 @@ +//===- MergingTypeTableBuilder.cpp ----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h" +#include "llvm/DebugInfo/CodeView/RecordSerialization.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <cstring> + +using namespace llvm; +using namespace llvm::codeview; + +TypeIndex MergingTypeTableBuilder::nextTypeIndex() const { + return TypeIndex::fromArrayIndex(SeenRecords.size()); +} + +MergingTypeTableBuilder::MergingTypeTableBuilder(BumpPtrAllocator &Storage) + : RecordStorage(Storage) { + SeenRecords.reserve(4096); +} + +MergingTypeTableBuilder::~MergingTypeTableBuilder() = default; + +Optional<TypeIndex> MergingTypeTableBuilder::getFirst() { + if (empty()) + return None; + + return TypeIndex(TypeIndex::FirstNonSimpleIndex); +} + +Optional<TypeIndex> MergingTypeTableBuilder::getNext(TypeIndex Prev) { + if (++Prev == nextTypeIndex()) + return None; + return Prev; +} + +CVType MergingTypeTableBuilder::getType(TypeIndex Index) { + CVType Type; + Type.RecordData = SeenRecords[Index.toArrayIndex()]; + const RecordPrefix *P = + reinterpret_cast<const RecordPrefix *>(Type.RecordData.data()); + Type.Type = static_cast<TypeLeafKind>(uint16_t(P->RecordKind)); + return Type; +} + +StringRef MergingTypeTableBuilder::getTypeName(TypeIndex Index) { + llvm_unreachable("Method not implemented"); +} + +bool MergingTypeTableBuilder::contains(TypeIndex Index) { + if (Index.isSimple() || Index.isNoneType()) + return false; + + return Index.toArrayIndex() < SeenRecords.size(); +} + +uint32_t MergingTypeTableBuilder::size() { return SeenRecords.size(); } + +uint32_t MergingTypeTableBuilder::capacity() { return SeenRecords.size(); } + +ArrayRef<ArrayRef<uint8_t>> MergingTypeTableBuilder::records() const { + return SeenRecords; +} + +void MergingTypeTableBuilder::reset() { + HashedRecords.clear(); + SeenRecords.clear(); +} + +static inline ArrayRef<uint8_t> stabilize(BumpPtrAllocator &Alloc, + ArrayRef<uint8_t> Data) { + uint8_t *Stable = Alloc.Allocate<uint8_t>(Data.size()); + memcpy(Stable, Data.data(), Data.size()); + return makeArrayRef(Stable, Data.size()); +} + +TypeIndex MergingTypeTableBuilder::insertRecordAs(hash_code Hash, + ArrayRef<uint8_t> &Record) { + assert(Record.size() < UINT32_MAX && "Record too big"); + assert(Record.size() % 4 == 0 && "Record is not aligned to 4 bytes!"); + + LocallyHashedType WeakHash{Hash, Record}; + auto Result = HashedRecords.try_emplace(WeakHash, nextTypeIndex()); + + if (Result.second) { + ArrayRef<uint8_t> RecordData = stabilize(RecordStorage, Record); + Result.first->first.RecordData = RecordData; + SeenRecords.push_back(RecordData); + } + + // Update the caller's copy of Record to point a stable copy. + TypeIndex ActualTI = Result.first->second; + Record = SeenRecords[ActualTI.toArrayIndex()]; + return ActualTI; +} + +TypeIndex +MergingTypeTableBuilder::insertRecordBytes(ArrayRef<uint8_t> &Record) { + return insertRecordAs(hash_value(Record), Record); +} + +TypeIndex +MergingTypeTableBuilder::insertRecord(ContinuationRecordBuilder &Builder) { + TypeIndex TI; + auto Fragments = Builder.end(nextTypeIndex()); + assert(!Fragments.empty()); + for (auto C : Fragments) + TI = insertRecordBytes(C.RecordData); + return TI; +} diff --git a/lib/DebugInfo/CodeView/TypeName.cpp b/lib/DebugInfo/CodeView/RecordName.cpp index 2eb8b81862f3..15fb1724d23d 100644 --- a/lib/DebugInfo/CodeView/TypeName.cpp +++ b/lib/DebugInfo/CodeView/RecordName.cpp @@ -1,4 +1,4 @@ -//===- TypeName.cpp ------------------------------------------- *- C++ --*-===// +//===- RecordName.cpp ----------------------------------------- *- C++ --*-===// // // The LLVM Compiler Infrastructure // @@ -7,10 +7,12 @@ // //===----------------------------------------------------------------------===// -#include "llvm/DebugInfo/CodeView/TypeName.h" +#include "llvm/DebugInfo/CodeView/RecordName.h" #include "llvm/ADT/SmallString.h" +#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h" #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/SymbolRecordMapping.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" #include "llvm/Support/FormatVariadic.h" @@ -241,3 +243,78 @@ std::string llvm::codeview::computeTypeName(TypeCollection &Types, } return Computer.name(); } + +static int getSymbolNameOffset(CVSymbol Sym) { + switch (Sym.kind()) { + // See ProcSym + case SymbolKind::S_GPROC32: + case SymbolKind::S_LPROC32: + case SymbolKind::S_GPROC32_ID: + case SymbolKind::S_LPROC32_ID: + case SymbolKind::S_LPROC32_DPC: + case SymbolKind::S_LPROC32_DPC_ID: + return 35; + // See Thunk32Sym + case SymbolKind::S_THUNK32: + return 21; + // See SectionSym + case SymbolKind::S_SECTION: + return 16; + // See CoffGroupSym + case SymbolKind::S_COFFGROUP: + return 14; + // See PublicSym32, FileStaticSym, RegRelativeSym, DataSym, ThreadLocalDataSym + case SymbolKind::S_PUB32: + case SymbolKind::S_FILESTATIC: + case SymbolKind::S_REGREL32: + case SymbolKind::S_GDATA32: + case SymbolKind::S_LDATA32: + case SymbolKind::S_LMANDATA: + case SymbolKind::S_GMANDATA: + case SymbolKind::S_LTHREAD32: + case SymbolKind::S_GTHREAD32: + return 10; + // See RegisterSym and LocalSym + case SymbolKind::S_REGISTER: + case SymbolKind::S_LOCAL: + return 6; + // See BlockSym + case SymbolKind::S_BLOCK32: + return 18; + // See LabelSym + case SymbolKind::S_LABEL32: + return 7; + // See ObjNameSym, ExportSym, and UDTSym + case SymbolKind::S_OBJNAME: + case SymbolKind::S_EXPORT: + case SymbolKind::S_UDT: + return 4; + // See BPRelativeSym + case SymbolKind::S_BPREL32: + return 8; + default: + return -1; + } +} + +StringRef llvm::codeview::getSymbolName(CVSymbol Sym) { + if (Sym.kind() == SymbolKind::S_CONSTANT) { + // S_CONSTANT is preceded by an APSInt, which has a variable length. So we + // have to do a full deserialization. + BinaryStreamReader Reader(Sym.content(), llvm::support::little); + // The container doesn't matter for single records. + SymbolRecordMapping Mapping(Reader, CodeViewContainer::ObjectFile); + ConstantSym Const(SymbolKind::S_CONSTANT); + cantFail(Mapping.visitSymbolBegin(Sym)); + cantFail(Mapping.visitKnownRecord(Sym, Const)); + cantFail(Mapping.visitSymbolEnd(Sym)); + return Const.Name; + } + + int Offset = getSymbolNameOffset(Sym); + if (Offset == -1) + return StringRef(); + + StringRef StringData = toStringRef(Sym.content()).drop_front(Offset); + return StringData.split('\0').first; +} diff --git a/lib/DebugInfo/CodeView/RecordSerialization.cpp b/lib/DebugInfo/CodeView/RecordSerialization.cpp index 6446670f60d8..bff9a619a846 100644 --- a/lib/DebugInfo/CodeView/RecordSerialization.cpp +++ b/lib/DebugInfo/CodeView/RecordSerialization.cpp @@ -15,6 +15,7 @@ #include "llvm/ADT/APInt.h" #include "llvm/ADT/APSInt.h" #include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/Support/BinaryByteStream.h" @@ -147,3 +148,8 @@ Error llvm::codeview::consume(BinaryStreamReader &Reader, StringRef &Item) { return Reader.readCString(Item); } + +Expected<CVSymbol> llvm::codeview::readSymbolFromStream(BinaryStreamRef Stream, + uint32_t Offset) { + return readCVRecordFromStream<SymbolKind>(Stream, Offset); +} diff --git a/lib/DebugInfo/CodeView/SimpleTypeSerializer.cpp b/lib/DebugInfo/CodeView/SimpleTypeSerializer.cpp new file mode 100644 index 000000000000..d28b7c3c2d83 --- /dev/null +++ b/lib/DebugInfo/CodeView/SimpleTypeSerializer.cpp @@ -0,0 +1,62 @@ +#include "llvm/DebugInfo/CodeView/SimpleTypeSerializer.h" + +using namespace llvm; +using namespace llvm::codeview; + +static void writeRecordPrefix(BinaryStreamWriter &Writer, TypeLeafKind Kind) { + RecordPrefix Prefix; + Prefix.RecordKind = Kind; + Prefix.RecordLen = 0; + cantFail(Writer.writeObject(Prefix)); +} + +static void addPadding(BinaryStreamWriter &Writer) { + uint32_t Align = Writer.getOffset() % 4; + if (Align == 0) + return; + + int PaddingBytes = 4 - Align; + while (PaddingBytes > 0) { + uint8_t Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes); + cantFail(Writer.writeInteger(Pad)); + --PaddingBytes; + } +} + +SimpleTypeSerializer::SimpleTypeSerializer() : ScratchBuffer(MaxRecordLength) {} + +SimpleTypeSerializer::~SimpleTypeSerializer() {} + +template <typename T> +ArrayRef<uint8_t> SimpleTypeSerializer::serialize(T &Record) { + BinaryStreamWriter Writer(ScratchBuffer, support::little); + TypeRecordMapping Mapping(Writer); + + CVType CVT; + CVT.Type = static_cast<TypeLeafKind>(Record.getKind()); + + writeRecordPrefix(Writer, CVT.Type); + + cantFail(Mapping.visitTypeBegin(CVT)); + cantFail(Mapping.visitKnownRecord(CVT, Record)); + cantFail(Mapping.visitTypeEnd(CVT)); + + addPadding(Writer); + + RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(ScratchBuffer.data()); + + Prefix->RecordKind = CVT.kind(); + Prefix->RecordLen = Writer.getOffset() - sizeof(uint16_t); + + return {ScratchBuffer.data(), Writer.getOffset()}; +} + +// Explicitly instantiate the member function for each known type so that we can +// implement this in the cpp file. +#define TYPE_RECORD(EnumName, EnumVal, Name) \ + template ArrayRef<uint8_t> llvm::codeview::SimpleTypeSerializer::serialize( \ + Name##Record &Record); +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#define MEMBER_RECORD(EnumName, EnumVal, Name) +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" diff --git a/lib/DebugInfo/CodeView/StringsAndChecksums.cpp b/lib/DebugInfo/CodeView/StringsAndChecksums.cpp index 306af1d1ef6b..85d9dbb8c7df 100644 --- a/lib/DebugInfo/CodeView/StringsAndChecksums.cpp +++ b/lib/DebugInfo/CodeView/StringsAndChecksums.cpp @@ -35,14 +35,36 @@ void StringsAndChecksumsRef::initializeStrings( assert(SR.kind() == DebugSubsectionKind::StringTable); assert(!Strings && "Found a string table even though we already have one!"); - OwnedStrings = llvm::make_unique<DebugStringTableSubsectionRef>(); + OwnedStrings = std::make_shared<DebugStringTableSubsectionRef>(); consumeError(OwnedStrings->initialize(SR.getRecordData())); Strings = OwnedStrings.get(); } +void StringsAndChecksumsRef::reset() { + resetStrings(); + resetChecksums(); +} + +void StringsAndChecksumsRef::resetStrings() { + OwnedStrings.reset(); + Strings = nullptr; +} + +void StringsAndChecksumsRef::resetChecksums() { + OwnedChecksums.reset(); + Checksums = nullptr; +} + +void StringsAndChecksumsRef::setStrings( + const DebugStringTableSubsectionRef &StringsRef) { + OwnedStrings = std::make_shared<DebugStringTableSubsectionRef>(); + *OwnedStrings = StringsRef; + Strings = OwnedStrings.get(); +} + void StringsAndChecksumsRef::setChecksums( const DebugChecksumsSubsectionRef &CS) { - OwnedChecksums = llvm::make_unique<DebugChecksumsSubsectionRef>(); + OwnedChecksums = std::make_shared<DebugChecksumsSubsectionRef>(); *OwnedChecksums = CS; Checksums = OwnedChecksums.get(); } @@ -53,7 +75,7 @@ void StringsAndChecksumsRef::initializeChecksums( if (Checksums) return; - OwnedChecksums = llvm::make_unique<DebugChecksumsSubsectionRef>(); + OwnedChecksums = std::make_shared<DebugChecksumsSubsectionRef>(); consumeError(OwnedChecksums->initialize(FCR.getRecordData())); Checksums = OwnedChecksums.get(); } diff --git a/lib/DebugInfo/CodeView/SymbolDumper.cpp b/lib/DebugInfo/CodeView/SymbolDumper.cpp index 62e73acc72d6..df75f52661e1 100644 --- a/lib/DebugInfo/CodeView/SymbolDumper.cpp +++ b/lib/DebugInfo/CodeView/SymbolDumper.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/CodeView/SymbolDumper.h" -#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallString.h" #include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h" #include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" @@ -317,7 +316,8 @@ Error CVSymbolDumperImpl::visitKnownRecord( Error CVSymbolDumperImpl::visitKnownRecord( CVSymbol &CVR, DefRangeRegisterRelSym &DefRangeRegisterRel) { - W.printNumber("BaseRegister", DefRangeRegisterRel.Hdr.Register); + W.printEnum("BaseRegister", uint16_t(DefRangeRegisterRel.Hdr.Register), + getRegisterNames()); W.printBoolean("HasSpilledUDTMember", DefRangeRegisterRel.hasSpilledUDTMember()); W.printNumber("OffsetInParent", DefRangeRegisterRel.offsetInParent()); @@ -330,7 +330,8 @@ Error CVSymbolDumperImpl::visitKnownRecord( Error CVSymbolDumperImpl::visitKnownRecord( CVSymbol &CVR, DefRangeRegisterSym &DefRangeRegister) { - W.printNumber("Register", DefRangeRegister.Hdr.Register); + W.printEnum("Register", uint16_t(DefRangeRegister.Hdr.Register), + getRegisterNames()); W.printNumber("MayHaveNoName", DefRangeRegister.Hdr.MayHaveNoName); printLocalVariableAddrRange(DefRangeRegister.Range, DefRangeRegister.getRelocationOffset()); @@ -340,7 +341,8 @@ Error CVSymbolDumperImpl::visitKnownRecord( Error CVSymbolDumperImpl::visitKnownRecord( CVSymbol &CVR, DefRangeSubfieldRegisterSym &DefRangeSubfieldRegister) { - W.printNumber("Register", DefRangeSubfieldRegister.Hdr.Register); + W.printEnum("Register", uint16_t(DefRangeSubfieldRegister.Hdr.Register), + getRegisterNames()); W.printNumber("MayHaveNoName", DefRangeSubfieldRegister.Hdr.MayHaveNoName); W.printNumber("OffsetInParent", DefRangeSubfieldRegister.Hdr.OffsetInParent); printLocalVariableAddrRange(DefRangeSubfieldRegister.Range, @@ -393,7 +395,7 @@ Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, FrameCookie.getRelocationOffset(), FrameCookie.CodeOffset, &LinkageName); } - W.printHex("Register", FrameCookie.Register); + W.printEnum("Register", uint16_t(FrameCookie.Register), getRegisterNames()); W.printEnum("CookieKind", uint16_t(FrameCookie.CookieKind), getFrameCookieKindNames()); W.printHex("Flags", FrameCookie.Flags); diff --git a/lib/DebugInfo/CodeView/SymbolSerializer.cpp b/lib/DebugInfo/CodeView/SymbolSerializer.cpp index 9a2e776feb75..0071ecc85685 100644 --- a/lib/DebugInfo/CodeView/SymbolSerializer.cpp +++ b/lib/DebugInfo/CodeView/SymbolSerializer.cpp @@ -21,8 +21,7 @@ using namespace llvm::codeview; SymbolSerializer::SymbolSerializer(BumpPtrAllocator &Allocator, CodeViewContainer Container) - : Storage(Allocator), RecordBuffer(MaxRecordLength), - Stream(RecordBuffer, support::little), Writer(Stream), + : Storage(Allocator), Stream(RecordBuffer, support::little), Writer(Stream), Mapping(Writer, Container) {} Error SymbolSerializer::visitSymbolBegin(CVSymbol &Record) { diff --git a/lib/DebugInfo/CodeView/TypeDumpVisitor.cpp b/lib/DebugInfo/CodeView/TypeDumpVisitor.cpp index e18a35ca1f38..e7998b8732fe 100644 --- a/lib/DebugInfo/CodeView/TypeDumpVisitor.cpp +++ b/lib/DebugInfo/CodeView/TypeDumpVisitor.cpp @@ -15,7 +15,6 @@ #include "llvm/DebugInfo/CodeView/TypeCollection.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" -#include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/ScopedPrinter.h" diff --git a/lib/DebugInfo/CodeView/TypeHashing.cpp b/lib/DebugInfo/CodeView/TypeHashing.cpp new file mode 100644 index 000000000000..f5b28b2a2070 --- /dev/null +++ b/lib/DebugInfo/CodeView/TypeHashing.cpp @@ -0,0 +1,74 @@ +//===- TypeHashing.cpp -------------------------------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/TypeHashing.h" + +#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" +#include "llvm/Support/SHA1.h" + +using namespace llvm; +using namespace llvm::codeview; + +LocallyHashedType DenseMapInfo<LocallyHashedType>::Empty{0, {}}; +LocallyHashedType DenseMapInfo<LocallyHashedType>::Tombstone{hash_code(-1), {}}; + +static std::array<uint8_t, 20> EmptyHash; +static std::array<uint8_t, 20> TombstoneHash = { + {0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + +GloballyHashedType DenseMapInfo<GloballyHashedType>::Empty{EmptyHash}; +GloballyHashedType DenseMapInfo<GloballyHashedType>::Tombstone{TombstoneHash}; + +LocallyHashedType LocallyHashedType::hashType(ArrayRef<uint8_t> RecordData) { + return {llvm::hash_value(RecordData), RecordData}; +} + +GloballyHashedType +GloballyHashedType::hashType(ArrayRef<uint8_t> RecordData, + ArrayRef<GloballyHashedType> PreviousTypes, + ArrayRef<GloballyHashedType> PreviousIds) { + SmallVector<TiReference, 4> Refs; + discoverTypeIndices(RecordData, Refs); + SHA1 S; + S.init(); + uint32_t Off = 0; + RecordData = RecordData.drop_front(sizeof(RecordPrefix)); + for (const auto &Ref : Refs) { + // Hash any data that comes before this TiRef. + uint32_t PreLen = Ref.Offset - Off; + ArrayRef<uint8_t> PreData = RecordData.slice(Off, PreLen); + S.update(PreData); + auto Prev = (Ref.Kind == TiRefKind::IndexRef) ? PreviousIds : PreviousTypes; + + auto RefData = RecordData.slice(Ref.Offset, Ref.Count * sizeof(TypeIndex)); + // For each type index referenced, add in the previously computed hash + // value of that type. + ArrayRef<TypeIndex> Indices( + reinterpret_cast<const TypeIndex *>(RefData.data()), Ref.Count); + for (TypeIndex TI : Indices) { + ArrayRef<uint8_t> BytesToHash; + if (TI.isSimple() || TI.isNoneType() || TI.toArrayIndex() >= Prev.size()) { + const uint8_t *IndexBytes = reinterpret_cast<const uint8_t *>(&TI); + BytesToHash = makeArrayRef(IndexBytes, sizeof(TypeIndex)); + } else { + BytesToHash = Prev[TI.toArrayIndex()].Hash; + } + S.update(BytesToHash); + } + + Off = Ref.Offset + Ref.Count * sizeof(TypeIndex); + } + + // Don't forget to add in any trailing bytes. + auto TrailingBytes = RecordData.drop_front(Off); + S.update(TrailingBytes); + + return {S.final()}; +} diff --git a/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp b/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp index 0d935c4472ae..d283e9e6d2f1 100644 --- a/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp +++ b/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp @@ -392,9 +392,13 @@ static bool discoverTypeIndices(ArrayRef<uint8_t> Content, SymbolKind Kind, case SymbolKind::S_LOCAL: Refs.push_back({TiRefKind::TypeRef, 0, 1}); // Type break; + case SymbolKind::S_REGISTER: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); // Type; + break; case SymbolKind::S_CONSTANT: Refs.push_back({TiRefKind::TypeRef, 0, 1}); // Type break; + case SymbolKind::S_BPREL32: case SymbolKind::S_REGREL32: Refs.push_back({TiRefKind::TypeRef, 4, 1}); // Type break; @@ -403,6 +407,7 @@ static bool discoverTypeIndices(ArrayRef<uint8_t> Content, SymbolKind Kind, break; case SymbolKind::S_CALLERS: case SymbolKind::S_CALLEES: + case SymbolKind::S_INLINEES: // The record is a count followed by an array of type indices. Count = *reinterpret_cast<const ulittle32_t *>(Content.data()); Refs.push_back({TiRefKind::IndexRef, 4, Count}); // Callees @@ -411,8 +416,7 @@ static bool discoverTypeIndices(ArrayRef<uint8_t> Content, SymbolKind Kind, Refs.push_back({TiRefKind::IndexRef, 8, 1}); // ID of inlinee break; case SymbolKind::S_HEAPALLOCSITE: - // FIXME: It's not clear if this is a type or item reference. - Refs.push_back({TiRefKind::IndexRef, 8, 1}); // signature + Refs.push_back({TiRefKind::TypeRef, 8, 1}); // UDT allocated break; // Defranges don't have types, just registers and code offsets. @@ -433,6 +437,8 @@ static bool discoverTypeIndices(ArrayRef<uint8_t> Content, SymbolKind Kind, case SymbolKind::S_ENVBLOCK: case SymbolKind::S_BLOCK32: case SymbolKind::S_FRAMEPROC: + case SymbolKind::S_THUNK32: + case SymbolKind::S_FRAMECOOKIE: break; // Scope ending symbols. case SymbolKind::S_END: @@ -450,17 +456,17 @@ void llvm::codeview::discoverTypeIndices(const CVType &Type, ::discoverTypeIndices(Type.content(), Type.kind(), Refs); } -void llvm::codeview::discoverTypeIndices(const CVType &Type, - SmallVectorImpl<TypeIndex> &Indices) { - +static void resolveTypeIndexReferences(ArrayRef<uint8_t> RecordData, + ArrayRef<TiReference> Refs, + SmallVectorImpl<TypeIndex> &Indices) { Indices.clear(); - SmallVector<TiReference, 4> Refs; - discoverTypeIndices(Type, Refs); if (Refs.empty()) return; - BinaryStreamReader Reader(Type.content(), support::little); + RecordData = RecordData.drop_front(sizeof(RecordPrefix)); + + BinaryStreamReader Reader(RecordData, support::little); for (const auto &Ref : Refs) { Reader.setOffset(Ref.Offset); FixedStreamArray<TypeIndex> Run; @@ -469,6 +475,18 @@ void llvm::codeview::discoverTypeIndices(const CVType &Type, } } +void llvm::codeview::discoverTypeIndices(const CVType &Type, + SmallVectorImpl<TypeIndex> &Indices) { + return discoverTypeIndices(Type.RecordData, Indices); +} + +void llvm::codeview::discoverTypeIndices(ArrayRef<uint8_t> RecordData, + SmallVectorImpl<TypeIndex> &Indices) { + SmallVector<TiReference, 4> Refs; + discoverTypeIndices(RecordData, Refs); + resolveTypeIndexReferences(RecordData, Refs, Indices); +} + void llvm::codeview::discoverTypeIndices(ArrayRef<uint8_t> RecordData, SmallVectorImpl<TiReference> &Refs) { const RecordPrefix *P = @@ -477,8 +495,26 @@ void llvm::codeview::discoverTypeIndices(ArrayRef<uint8_t> RecordData, ::discoverTypeIndices(RecordData.drop_front(sizeof(RecordPrefix)), K, Refs); } -bool llvm::codeview::discoverTypeIndices(const CVSymbol &Sym, - SmallVectorImpl<TiReference> &Refs) { +bool llvm::codeview::discoverTypeIndicesInSymbol( + const CVSymbol &Sym, SmallVectorImpl<TiReference> &Refs) { SymbolKind K = Sym.kind(); return ::discoverTypeIndices(Sym.content(), K, Refs); } + +bool llvm::codeview::discoverTypeIndicesInSymbol( + ArrayRef<uint8_t> RecordData, SmallVectorImpl<TiReference> &Refs) { + const RecordPrefix *P = + reinterpret_cast<const RecordPrefix *>(RecordData.data()); + SymbolKind K = static_cast<SymbolKind>(uint16_t(P->RecordKind)); + return ::discoverTypeIndices(RecordData.drop_front(sizeof(RecordPrefix)), K, + Refs); +} + +bool llvm::codeview::discoverTypeIndicesInSymbol( + ArrayRef<uint8_t> RecordData, SmallVectorImpl<TypeIndex> &Indices) { + SmallVector<TiReference, 2> Refs; + if (!discoverTypeIndicesInSymbol(RecordData, Refs)) + return false; + resolveTypeIndexReferences(RecordData, Refs, Indices); + return true; +} diff --git a/lib/DebugInfo/CodeView/TypeRecordMapping.cpp b/lib/DebugInfo/CodeView/TypeRecordMapping.cpp index 114f6fd2897e..9b8a6053da84 100644 --- a/lib/DebugInfo/CodeView/TypeRecordMapping.cpp +++ b/lib/DebugInfo/CodeView/TypeRecordMapping.cpp @@ -426,7 +426,8 @@ Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, OneMethodRecord &Record) { - MapOneMethodRecord Mapper(false); + const bool IsFromOverloadList = (TypeKind == LF_METHODLIST); + MapOneMethodRecord Mapper(IsFromOverloadList); return Mapper(IO, Record); } diff --git a/lib/DebugInfo/CodeView/TypeSerializer.cpp b/lib/DebugInfo/CodeView/TypeSerializer.cpp deleted file mode 100644 index 003c13b4a20d..000000000000 --- a/lib/DebugInfo/CodeView/TypeSerializer.cpp +++ /dev/null @@ -1,389 +0,0 @@ -//===- TypeSerialzier.cpp -------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "llvm/DebugInfo/CodeView/TypeSerializer.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/DebugInfo/CodeView/CodeView.h" -#include "llvm/DebugInfo/CodeView/RecordSerialization.h" -#include "llvm/DebugInfo/CodeView/TypeIndex.h" -#include "llvm/Support/Allocator.h" -#include "llvm/Support/BinaryByteStream.h" -#include "llvm/Support/BinaryStreamWriter.h" -#include "llvm/Support/Endian.h" -#include "llvm/Support/Error.h" -#include <algorithm> -#include <cassert> -#include <cstdint> -#include <cstring> - -using namespace llvm; -using namespace llvm::codeview; - -namespace { - -struct HashedType { - uint64_t Hash; - const uint8_t *Data; - unsigned Size; // FIXME: Go to uint16_t? - TypeIndex Index; -}; - -/// Wrapper around a poitner to a HashedType. Hash and equality operations are -/// based on data in the pointee. -struct HashedTypePtr { - HashedTypePtr() = default; - HashedTypePtr(HashedType *Ptr) : Ptr(Ptr) {} - - HashedType *Ptr = nullptr; -}; - -} // end anonymous namespace - -namespace llvm { - -template <> struct DenseMapInfo<HashedTypePtr> { - static inline HashedTypePtr getEmptyKey() { return HashedTypePtr(nullptr); } - - static inline HashedTypePtr getTombstoneKey() { - return HashedTypePtr(reinterpret_cast<HashedType *>(1)); - } - - static unsigned getHashValue(HashedTypePtr Val) { - assert(Val.Ptr != getEmptyKey().Ptr && Val.Ptr != getTombstoneKey().Ptr); - return Val.Ptr->Hash; - } - - static bool isEqual(HashedTypePtr LHSP, HashedTypePtr RHSP) { - HashedType *LHS = LHSP.Ptr; - HashedType *RHS = RHSP.Ptr; - if (RHS == getEmptyKey().Ptr || RHS == getTombstoneKey().Ptr) - return LHS == RHS; - if (LHS->Hash != RHS->Hash || LHS->Size != RHS->Size) - return false; - return ::memcmp(LHS->Data, RHS->Data, LHS->Size) == 0; - } -}; - -} // end namespace llvm - -/// Private implementation so that we don't leak our DenseMap instantiations to -/// users. -class llvm::codeview::TypeHasher { -private: - /// Storage for type record provided by the caller. Records will outlive the - /// hasher object, so they should be allocated here. - BumpPtrAllocator &RecordStorage; - - /// Storage for hash keys. These only need to live as long as the hashing - /// operation. - BumpPtrAllocator KeyStorage; - - /// Hash table. We really want a DenseMap<ArrayRef<uint8_t>, TypeIndex> here, - /// but DenseMap is inefficient when the keys are long (like type records) - /// because it recomputes the hash value of every key when it grows. This - /// value type stores the hash out of line in KeyStorage, so that table - /// entries are small and easy to rehash. - DenseSet<HashedTypePtr> HashedRecords; - -public: - TypeHasher(BumpPtrAllocator &RecordStorage) : RecordStorage(RecordStorage) {} - - void reset() { HashedRecords.clear(); } - - /// Takes the bytes of type record, inserts them into the hash table, saves - /// them, and returns a pointer to an identical stable type record along with - /// its type index in the destination stream. - TypeIndex getOrCreateRecord(ArrayRef<uint8_t> &Record, TypeIndex TI); -}; - -TypeIndex TypeHasher::getOrCreateRecord(ArrayRef<uint8_t> &Record, - TypeIndex TI) { - assert(Record.size() < UINT32_MAX && "Record too big"); - assert(Record.size() % 4 == 0 && "Record is not aligned to 4 bytes!"); - - // Compute the hash up front so we can store it in the key. - HashedType TempHashedType = {hash_value(Record), Record.data(), - unsigned(Record.size()), TI}; - auto Result = HashedRecords.insert(HashedTypePtr(&TempHashedType)); - HashedType *&Hashed = Result.first->Ptr; - - if (Result.second) { - // This was a new type record. We need stable storage for both the key and - // the record. The record should outlive the hashing operation. - Hashed = KeyStorage.Allocate<HashedType>(); - *Hashed = TempHashedType; - - uint8_t *Stable = RecordStorage.Allocate<uint8_t>(Record.size()); - memcpy(Stable, Record.data(), Record.size()); - Hashed->Data = Stable; - assert(Hashed->Size == Record.size()); - } - - // Update the caller's copy of Record to point a stable copy. - Record = ArrayRef<uint8_t>(Hashed->Data, Hashed->Size); - return Hashed->Index; -} - -TypeIndex TypeSerializer::nextTypeIndex() const { - return TypeIndex::fromArrayIndex(SeenRecords.size()); -} - -bool TypeSerializer::isInFieldList() const { - return TypeKind.hasValue() && *TypeKind == TypeLeafKind::LF_FIELDLIST; -} - -MutableArrayRef<uint8_t> TypeSerializer::getCurrentSubRecordData() { - assert(isInFieldList()); - return getCurrentRecordData().drop_front(CurrentSegment.length()); -} - -MutableArrayRef<uint8_t> TypeSerializer::getCurrentRecordData() { - return MutableArrayRef<uint8_t>(RecordBuffer).take_front(Writer.getOffset()); -} - -Error TypeSerializer::writeRecordPrefix(TypeLeafKind Kind) { - RecordPrefix Prefix; - Prefix.RecordKind = Kind; - Prefix.RecordLen = 0; - if (auto EC = Writer.writeObject(Prefix)) - return EC; - return Error::success(); -} - -Expected<MutableArrayRef<uint8_t>> -TypeSerializer::addPadding(MutableArrayRef<uint8_t> Record) { - uint32_t Align = Record.size() % 4; - if (Align == 0) - return Record; - - int PaddingBytes = 4 - Align; - int N = PaddingBytes; - while (PaddingBytes > 0) { - uint8_t Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes); - if (auto EC = Writer.writeInteger(Pad)) - return std::move(EC); - --PaddingBytes; - } - return MutableArrayRef<uint8_t>(Record.data(), Record.size() + N); -} - -TypeSerializer::TypeSerializer(BumpPtrAllocator &Storage, bool Hash) - : RecordStorage(Storage), RecordBuffer(MaxRecordLength * 2), - Stream(RecordBuffer, support::little), Writer(Stream), - Mapping(Writer) { - // RecordBuffer needs to be able to hold enough data so that if we are 1 - // byte short of MaxRecordLen, and then we try to write MaxRecordLen bytes, - // we won't overflow. - if (Hash) - Hasher = llvm::make_unique<TypeHasher>(Storage); -} - -TypeSerializer::~TypeSerializer() = default; - -ArrayRef<ArrayRef<uint8_t>> TypeSerializer::records() const { - return SeenRecords; -} - -void TypeSerializer::reset() { - if (Hasher) - Hasher->reset(); - Writer.setOffset(0); - CurrentSegment = RecordSegment(); - FieldListSegments.clear(); - TypeKind.reset(); - MemberKind.reset(); - SeenRecords.clear(); -} - -TypeIndex TypeSerializer::insertRecordBytes(ArrayRef<uint8_t> &Record) { - assert(!TypeKind.hasValue() && "Already in a type mapping!"); - assert(Writer.getOffset() == 0 && "Stream has data already!"); - - if (Hasher) { - TypeIndex ActualTI = Hasher->getOrCreateRecord(Record, nextTypeIndex()); - if (nextTypeIndex() == ActualTI) - SeenRecords.push_back(Record); - return ActualTI; - } - - TypeIndex NewTI = nextTypeIndex(); - uint8_t *Stable = RecordStorage.Allocate<uint8_t>(Record.size()); - memcpy(Stable, Record.data(), Record.size()); - Record = ArrayRef<uint8_t>(Stable, Record.size()); - SeenRecords.push_back(Record); - return NewTI; -} - -TypeIndex TypeSerializer::insertRecord(const RemappedType &Record) { - assert(!TypeKind.hasValue() && "Already in a type mapping!"); - assert(Writer.getOffset() == 0 && "Stream has data already!"); - - TypeIndex TI; - ArrayRef<uint8_t> OriginalData = Record.OriginalRecord.RecordData; - if (Record.Mappings.empty()) { - // This record did not remap any type indices. Just write it. - return insertRecordBytes(OriginalData); - } - - // At least one type index was remapped. Before we can hash it we have to - // copy the full record bytes, re-write each type index, then hash the copy. - // We do this in temporary storage since only the DenseMap can decide whether - // this record already exists, and if it does we don't want the memory to - // stick around. - RemapStorage.resize(OriginalData.size()); - ::memcpy(&RemapStorage[0], OriginalData.data(), OriginalData.size()); - uint8_t *ContentBegin = RemapStorage.data() + sizeof(RecordPrefix); - for (const auto &M : Record.Mappings) { - // First 4 bytes of every record are the record prefix, but the mapping - // offset is relative to the content which starts after. - *(TypeIndex *)(ContentBegin + M.first) = M.second; - } - auto RemapRef = makeArrayRef(RemapStorage); - return insertRecordBytes(RemapRef); -} - -Error TypeSerializer::visitTypeBegin(CVType &Record) { - assert(!TypeKind.hasValue() && "Already in a type mapping!"); - assert(Writer.getOffset() == 0 && "Stream has data already!"); - - if (auto EC = writeRecordPrefix(Record.kind())) - return EC; - - TypeKind = Record.kind(); - if (auto EC = Mapping.visitTypeBegin(Record)) - return EC; - - return Error::success(); -} - -Expected<TypeIndex> TypeSerializer::visitTypeEndGetIndex(CVType &Record) { - assert(TypeKind.hasValue() && "Not in a type mapping!"); - if (auto EC = Mapping.visitTypeEnd(Record)) - return std::move(EC); - - // Update the record's length and fill out the CVType members to point to - // the stable memory holding the record's data. - auto ThisRecordData = getCurrentRecordData(); - auto ExpectedData = addPadding(ThisRecordData); - if (!ExpectedData) - return ExpectedData.takeError(); - ThisRecordData = *ExpectedData; - - RecordPrefix *Prefix = - reinterpret_cast<RecordPrefix *>(ThisRecordData.data()); - Prefix->RecordLen = ThisRecordData.size() - sizeof(uint16_t); - - Record.Type = *TypeKind; - Record.RecordData = ThisRecordData; - - // insertRecordBytes assumes we're not in a mapping, so do this first. - TypeKind.reset(); - Writer.setOffset(0); - - TypeIndex InsertedTypeIndex = insertRecordBytes(Record.RecordData); - - // Write out each additional segment in reverse order, and update each - // record's continuation index to point to the previous one. - for (auto X : reverse(FieldListSegments)) { - auto CIBytes = X.take_back(sizeof(uint32_t)); - support::ulittle32_t *CI = - reinterpret_cast<support::ulittle32_t *>(CIBytes.data()); - assert(*CI == 0xB0C0B0C0 && "Invalid TypeIndex placeholder"); - *CI = InsertedTypeIndex.getIndex(); - InsertedTypeIndex = insertRecordBytes(X); - } - - FieldListSegments.clear(); - CurrentSegment.SubRecords.clear(); - - return InsertedTypeIndex; -} - -Error TypeSerializer::visitTypeEnd(CVType &Record) { - auto ExpectedIndex = visitTypeEndGetIndex(Record); - if (!ExpectedIndex) - return ExpectedIndex.takeError(); - return Error::success(); -} - -Error TypeSerializer::visitMemberBegin(CVMemberRecord &Record) { - assert(isInFieldList() && "Not in a field list!"); - assert(!MemberKind.hasValue() && "Already in a member record!"); - MemberKind = Record.Kind; - - if (auto EC = Mapping.visitMemberBegin(Record)) - return EC; - - return Error::success(); -} - -Error TypeSerializer::visitMemberEnd(CVMemberRecord &Record) { - if (auto EC = Mapping.visitMemberEnd(Record)) - return EC; - - // Check if this subrecord makes the current segment not fit in 64K minus - // the space for a continuation record (8 bytes). If the segment does not - // fit, insert a continuation record. - if (Writer.getOffset() > MaxRecordLength - ContinuationLength) { - MutableArrayRef<uint8_t> Data = getCurrentRecordData(); - SubRecord LastSubRecord = CurrentSegment.SubRecords.back(); - uint32_t CopySize = CurrentSegment.length() - LastSubRecord.Size; - auto CopyData = Data.take_front(CopySize); - auto LeftOverData = Data.drop_front(CopySize); - assert(LastSubRecord.Size == LeftOverData.size()); - - // Allocate stable storage for the record and copy the old record plus - // continuation over. - uint16_t LengthWithSize = CopySize + ContinuationLength; - assert(LengthWithSize <= MaxRecordLength); - RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(CopyData.data()); - Prefix->RecordLen = LengthWithSize - sizeof(uint16_t); - - uint8_t *SegmentBytes = RecordStorage.Allocate<uint8_t>(LengthWithSize); - auto SavedSegment = MutableArrayRef<uint8_t>(SegmentBytes, LengthWithSize); - MutableBinaryByteStream CS(SavedSegment, support::little); - BinaryStreamWriter CW(CS); - if (auto EC = CW.writeBytes(CopyData)) - return EC; - if (auto EC = CW.writeEnum(TypeLeafKind::LF_INDEX)) - return EC; - if (auto EC = CW.writeInteger<uint16_t>(0)) - return EC; - if (auto EC = CW.writeInteger<uint32_t>(0xB0C0B0C0)) - return EC; - FieldListSegments.push_back(SavedSegment); - - // Write a new placeholder record prefix to mark the start of this new - // top-level record. - Writer.setOffset(0); - if (auto EC = writeRecordPrefix(TypeLeafKind::LF_FIELDLIST)) - return EC; - - // Then move over the subrecord that overflowed the old segment to the - // beginning of this segment. Note that we have to use memmove here - // instead of Writer.writeBytes(), because the new and old locations - // could overlap. - ::memmove(Stream.data().data() + sizeof(RecordPrefix), LeftOverData.data(), - LeftOverData.size()); - // And point the segment writer at the end of that subrecord. - Writer.setOffset(LeftOverData.size() + sizeof(RecordPrefix)); - - CurrentSegment.SubRecords.clear(); - CurrentSegment.SubRecords.push_back(LastSubRecord); - } - - // Update the CVMemberRecord since we may have shifted around or gotten - // padded. - Record.Data = getCurrentSubRecordData(); - - MemberKind.reset(); - return Error::success(); -} diff --git a/lib/DebugInfo/CodeView/TypeStreamMerger.cpp b/lib/DebugInfo/CodeView/TypeStreamMerger.cpp index bff3516203a0..6a94952c175b 100644 --- a/lib/DebugInfo/CodeView/TypeStreamMerger.cpp +++ b/lib/DebugInfo/CodeView/TypeStreamMerger.cpp @@ -10,13 +10,12 @@ #include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" -#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" #include "llvm/Support/Error.h" -#include "llvm/Support/ScopedPrinter.h" using namespace llvm; using namespace llvm::codeview; @@ -64,12 +63,27 @@ public: static const TypeIndex Untranslated; - Error mergeTypesAndIds(TypeTableBuilder &DestIds, TypeTableBuilder &DestTypes, + // Local hashing entry points + Error mergeTypesAndIds(MergingTypeTableBuilder &DestIds, + MergingTypeTableBuilder &DestTypes, const CVTypeArray &IdsAndTypes); - Error mergeIdRecords(TypeTableBuilder &Dest, + Error mergeIdRecords(MergingTypeTableBuilder &Dest, ArrayRef<TypeIndex> TypeSourceToDest, const CVTypeArray &Ids); - Error mergeTypeRecords(TypeTableBuilder &Dest, const CVTypeArray &Types); + Error mergeTypeRecords(MergingTypeTableBuilder &Dest, + const CVTypeArray &Types); + + // Global hashing entry points + Error mergeTypesAndIds(GlobalTypeTableBuilder &DestIds, + GlobalTypeTableBuilder &DestTypes, + const CVTypeArray &IdsAndTypes, + ArrayRef<GloballyHashedType> Hashes); + Error mergeIdRecords(GlobalTypeTableBuilder &Dest, + ArrayRef<TypeIndex> TypeSourceToDest, + const CVTypeArray &Ids, + ArrayRef<GloballyHashedType> Hashes); + Error mergeTypeRecords(GlobalTypeTableBuilder &Dest, const CVTypeArray &Types, + ArrayRef<GloballyHashedType> Hashes); private: Error doit(const CVTypeArray &Types); @@ -83,6 +97,16 @@ private: bool remapTypeIndex(TypeIndex &Idx); bool remapItemIndex(TypeIndex &Idx); + bool hasTypeStream() const { + return (UseGlobalHashes) ? (!!DestGlobalTypeStream) : (!!DestTypeStream); + } + + bool hasIdStream() const { + return (UseGlobalHashes) ? (!!DestGlobalIdStream) : (!!DestIdStream); + } + + ArrayRef<uint8_t> serializeRemapped(const RemappedType &Record); + bool remapIndices(RemappedType &Record, ArrayRef<TiReference> Refs); bool remapIndex(TypeIndex &Idx, ArrayRef<TypeIndex> Map); @@ -96,25 +120,23 @@ private: return llvm::make_error<CodeViewError>(cv_error_code::corrupt_record); } - Error writeRecord(TypeTableBuilder &Dest, const RemappedType &Record, - bool RemapSuccess) { - TypeIndex DestIdx = Untranslated; - if (RemapSuccess) - DestIdx = Dest.writeSerializedRecord(Record); - addMapping(DestIdx); - return Error::success(); - } - Optional<Error> LastError; + bool UseGlobalHashes = false; + bool IsSecondPass = false; unsigned NumBadIndices = 0; TypeIndex CurIndex{TypeIndex::FirstNonSimpleIndex}; - TypeTableBuilder *DestIdStream = nullptr; - TypeTableBuilder *DestTypeStream = nullptr; + MergingTypeTableBuilder *DestIdStream = nullptr; + MergingTypeTableBuilder *DestTypeStream = nullptr; + + GlobalTypeTableBuilder *DestGlobalIdStream = nullptr; + GlobalTypeTableBuilder *DestGlobalTypeStream = nullptr; + + ArrayRef<GloballyHashedType> GlobalHashes; // If we're only mapping id records, this array contains the mapping for // type records. @@ -123,10 +145,35 @@ private: /// Map from source type index to destination type index. Indexed by source /// type index minus 0x1000. SmallVectorImpl<TypeIndex> &IndexMap; + + /// Temporary storage that we use to copy a record's data while re-writing + /// its type indices. + SmallVector<uint8_t, 256> RemapStorage; }; } // end anonymous namespace +ArrayRef<uint8_t> +TypeStreamMerger::serializeRemapped(const RemappedType &Record) { + TypeIndex TI; + ArrayRef<uint8_t> OriginalData = Record.OriginalRecord.RecordData; + if (Record.Mappings.empty()) + return OriginalData; + + // At least one type index was remapped. We copy the full record bytes, + // re-write each type index, then return that. + RemapStorage.resize(OriginalData.size()); + ::memcpy(&RemapStorage[0], OriginalData.data(), OriginalData.size()); + uint8_t *ContentBegin = RemapStorage.data() + sizeof(RecordPrefix); + for (const auto &M : Record.Mappings) { + // First 4 bytes of every record are the record prefix, but the mapping + // offset is relative to the content which starts after. + *(TypeIndex *)(ContentBegin + M.first) = M.second; + } + auto RemapRef = makeArrayRef(RemapStorage); + return RemapRef; +} + const TypeIndex TypeStreamMerger::Untranslated(SimpleTypeKind::NotTranslated); static bool isIdRecord(TypeLeafKind K) { @@ -191,7 +238,7 @@ bool TypeStreamMerger::remapTypeIndex(TypeIndex &Idx) { // special mapping from OldTypeStream -> NewTypeStream which was computed // externally. Regardless, we use this special map if and only if we are // doing an id-only mapping. - if (DestTypeStream == nullptr) + if (!hasTypeStream()) return remapIndex(Idx, TypeLookup); assert(TypeLookup.empty()); @@ -199,31 +246,69 @@ bool TypeStreamMerger::remapTypeIndex(TypeIndex &Idx) { } bool TypeStreamMerger::remapItemIndex(TypeIndex &Idx) { - assert(DestIdStream); + assert(hasIdStream()); return remapIndex(Idx, IndexMap); } -Error TypeStreamMerger::mergeTypeRecords(TypeTableBuilder &Dest, +// Local hashing entry points +Error TypeStreamMerger::mergeTypeRecords(MergingTypeTableBuilder &Dest, const CVTypeArray &Types) { DestTypeStream = &Dest; + UseGlobalHashes = false; return doit(Types); } -Error TypeStreamMerger::mergeIdRecords(TypeTableBuilder &Dest, +Error TypeStreamMerger::mergeIdRecords(MergingTypeTableBuilder &Dest, ArrayRef<TypeIndex> TypeSourceToDest, const CVTypeArray &Ids) { DestIdStream = &Dest; TypeLookup = TypeSourceToDest; + UseGlobalHashes = false; return doit(Ids); } -Error TypeStreamMerger::mergeTypesAndIds(TypeTableBuilder &DestIds, - TypeTableBuilder &DestTypes, +Error TypeStreamMerger::mergeTypesAndIds(MergingTypeTableBuilder &DestIds, + MergingTypeTableBuilder &DestTypes, const CVTypeArray &IdsAndTypes) { DestIdStream = &DestIds; DestTypeStream = &DestTypes; + UseGlobalHashes = false; + return doit(IdsAndTypes); +} + +// Global hashing entry points +Error TypeStreamMerger::mergeTypeRecords(GlobalTypeTableBuilder &Dest, + const CVTypeArray &Types, + ArrayRef<GloballyHashedType> Hashes) { + DestGlobalTypeStream = &Dest; + UseGlobalHashes = true; + GlobalHashes = Hashes; + + return doit(Types); +} + +Error TypeStreamMerger::mergeIdRecords(GlobalTypeTableBuilder &Dest, + ArrayRef<TypeIndex> TypeSourceToDest, + const CVTypeArray &Ids, + ArrayRef<GloballyHashedType> Hashes) { + DestGlobalIdStream = &Dest; + TypeLookup = TypeSourceToDest; + UseGlobalHashes = true; + GlobalHashes = Hashes; + + return doit(Ids); +} + +Error TypeStreamMerger::mergeTypesAndIds(GlobalTypeTableBuilder &DestIds, + GlobalTypeTableBuilder &DestTypes, + const CVTypeArray &IdsAndTypes, + ArrayRef<GloballyHashedType> Hashes) { + DestGlobalIdStream = &DestIds; + DestGlobalTypeStream = &DestTypes; + UseGlobalHashes = true; + GlobalHashes = Hashes; return doit(IdsAndTypes); } @@ -268,14 +353,30 @@ Error TypeStreamMerger::remapAllTypes(const CVTypeArray &Types) { } Error TypeStreamMerger::remapType(const CVType &Type) { - RemappedType R(Type); - SmallVector<TiReference, 32> Refs; - discoverTypeIndices(Type.RecordData, Refs); - bool MappedAllIndices = remapIndices(R, Refs); - TypeTableBuilder &Dest = - isIdRecord(Type.kind()) ? *DestIdStream : *DestTypeStream; - if (auto EC = writeRecord(Dest, R, MappedAllIndices)) - return EC; + auto DoSerialize = [this, Type]() -> ArrayRef<uint8_t> { + RemappedType R(Type); + SmallVector<TiReference, 32> Refs; + discoverTypeIndices(Type.RecordData, Refs); + if (!remapIndices(R, Refs)) + return {}; + return serializeRemapped(R); + }; + + TypeIndex DestIdx = Untranslated; + if (UseGlobalHashes) { + GlobalTypeTableBuilder &Dest = + isIdRecord(Type.kind()) ? *DestGlobalIdStream : *DestGlobalTypeStream; + GloballyHashedType H = GlobalHashes[CurIndex.toArrayIndex()]; + DestIdx = Dest.insertRecordAs(H, DoSerialize); + } else { + MergingTypeTableBuilder &Dest = + isIdRecord(Type.kind()) ? *DestIdStream : *DestTypeStream; + + auto Data = DoSerialize(); + if (!Data.empty()) + DestIdx = Dest.insertRecordBytes(Data); + } + addMapping(DestIdx); ++CurIndex; assert((IsSecondPass || IndexMap.size() == slotForIndex(CurIndex)) && @@ -306,14 +407,14 @@ bool TypeStreamMerger::remapIndices(RemappedType &Record, return Success; } -Error llvm::codeview::mergeTypeRecords(TypeTableBuilder &Dest, +Error llvm::codeview::mergeTypeRecords(MergingTypeTableBuilder &Dest, SmallVectorImpl<TypeIndex> &SourceToDest, const CVTypeArray &Types) { TypeStreamMerger M(SourceToDest); return M.mergeTypeRecords(Dest, Types); } -Error llvm::codeview::mergeIdRecords(TypeTableBuilder &Dest, +Error llvm::codeview::mergeIdRecords(MergingTypeTableBuilder &Dest, ArrayRef<TypeIndex> TypeSourceToDest, SmallVectorImpl<TypeIndex> &SourceToDest, const CVTypeArray &Ids) { @@ -322,8 +423,33 @@ Error llvm::codeview::mergeIdRecords(TypeTableBuilder &Dest, } Error llvm::codeview::mergeTypeAndIdRecords( - TypeTableBuilder &DestIds, TypeTableBuilder &DestTypes, + MergingTypeTableBuilder &DestIds, MergingTypeTableBuilder &DestTypes, SmallVectorImpl<TypeIndex> &SourceToDest, const CVTypeArray &IdsAndTypes) { TypeStreamMerger M(SourceToDest); return M.mergeTypesAndIds(DestIds, DestTypes, IdsAndTypes); } + +Error llvm::codeview::mergeTypeAndIdRecords( + GlobalTypeTableBuilder &DestIds, GlobalTypeTableBuilder &DestTypes, + SmallVectorImpl<TypeIndex> &SourceToDest, const CVTypeArray &IdsAndTypes, + ArrayRef<GloballyHashedType> Hashes) { + TypeStreamMerger M(SourceToDest); + return M.mergeTypesAndIds(DestIds, DestTypes, IdsAndTypes, Hashes); +} + +Error llvm::codeview::mergeTypeRecords(GlobalTypeTableBuilder &Dest, + SmallVectorImpl<TypeIndex> &SourceToDest, + const CVTypeArray &Types, + ArrayRef<GloballyHashedType> Hashes) { + TypeStreamMerger M(SourceToDest); + return M.mergeTypeRecords(Dest, Types, Hashes); +} + +Error llvm::codeview::mergeIdRecords(GlobalTypeTableBuilder &Dest, + ArrayRef<TypeIndex> Types, + SmallVectorImpl<TypeIndex> &SourceToDest, + const CVTypeArray &Ids, + ArrayRef<GloballyHashedType> Hashes) { + TypeStreamMerger M(SourceToDest); + return M.mergeIdRecords(Dest, Types, Ids, Hashes); +} diff --git a/lib/DebugInfo/CodeView/TypeTableCollection.cpp b/lib/DebugInfo/CodeView/TypeTableCollection.cpp index 4eca5aeaa0ae..cf951baa5111 100644 --- a/lib/DebugInfo/CodeView/TypeTableCollection.cpp +++ b/lib/DebugInfo/CodeView/TypeTableCollection.cpp @@ -10,9 +10,7 @@ #include "llvm/DebugInfo/CodeView/TypeTableCollection.h" #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" -#include "llvm/DebugInfo/CodeView/TypeName.h" -#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" -#include "llvm/Support/BinaryByteStream.h" +#include "llvm/DebugInfo/CodeView/RecordName.h" #include "llvm/Support/BinaryStreamReader.h" using namespace llvm; |