Index: include/llvm/DebugInfo/CodeView/CodeViewRecordIO.h =================================================================== --- include/llvm/DebugInfo/CodeView/CodeViewRecordIO.h +++ include/llvm/DebugInfo/CodeView/CodeViewRecordIO.h @@ -13,15 +13,18 @@ #include "llvm/ADT/APSInt.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" -#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/MSF/StreamReader.h" #include "llvm/DebugInfo/MSF/StreamWriter.h" #include "llvm/Support/Error.h" -#include #include +#include + namespace llvm { namespace msf { class StreamReader; @@ -30,21 +33,24 @@ namespace codeview { class CodeViewRecordIO { - struct ActiveRecord { - uint16_t Kind; - }; + uint32_t getCurrentOffset() const { + return (isWriting()) ? Writer->getOffset() : Reader->getOffset(); + } public: explicit CodeViewRecordIO(msf::StreamReader &Reader) : Reader(&Reader) {} explicit CodeViewRecordIO(msf::StreamWriter &Writer) : Writer(&Writer) {} - Error beginRecord(uint16_t Kind); + Error beginRecord(Optional MaxLength); Error endRecord(); + Error mapInteger(TypeIndex &TypeInd); bool isReading() const { return Reader != nullptr; } bool isWriting() const { return !isReading(); } + uint32_t maxFieldLength() const; + template Error mapInteger(T &Value) { if (isWriting()) return Writer->writeInteger(Value); @@ -53,6 +59,9 @@ } template Error mapEnum(T &Value) { + if (sizeof(Value) > maxFieldLength()) + return make_error(cv_error_code::insufficient_buffer); + using U = typename std::underlying_type::type; U X; if (isWriting()) @@ -124,7 +133,23 @@ Error writeEncodedSignedInteger(const int64_t &Value); Error writeEncodedUnsignedInteger(const uint64_t &Value); - Optional CurrentRecord; + struct RecordLimit { + uint32_t BeginOffset; + Optional MaxLength; + + Optional bytesRemaining(uint32_t CurrentOffset) const { + if (!MaxLength.hasValue()) + return None; + assert(CurrentOffset >= BeginOffset); + + uint32_t BytesUsed = CurrentOffset - BeginOffset; + if (BytesUsed >= *MaxLength) + return 0; + return *MaxLength - BytesUsed; + } + }; + + SmallVector Limits; msf::StreamReader *Reader = nullptr; msf::StreamWriter *Writer = nullptr; Index: include/llvm/DebugInfo/CodeView/FieldListRecordBuilder.h =================================================================== --- include/llvm/DebugInfo/CodeView/FieldListRecordBuilder.h +++ /dev/null @@ -1,67 +0,0 @@ -//===- FieldListRecordBuilder.h ---------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_DEBUGINFO_CODEVIEW_FIELDLISTRECORDBUILDER_H -#define LLVM_DEBUGINFO_CODEVIEW_FIELDLISTRECORDBUILDER_H - -#include "llvm/DebugInfo/CodeView/ListRecordBuilder.h" -#include "llvm/DebugInfo/CodeView/TypeRecord.h" - -namespace llvm { -namespace codeview { - -class MethodInfo { -public: - MethodInfo() : Access(), Kind(), Options(), Type(), VTableSlotOffset(-1) {} - - MethodInfo(MemberAccess Access, MethodKind Kind, MethodOptions Options, - TypeIndex Type, int32_t VTableSlotOffset) - : Access(Access), Kind(Kind), Options(Options), Type(Type), - VTableSlotOffset(VTableSlotOffset) {} - - MemberAccess getAccess() const { return Access; } - MethodKind getKind() const { return Kind; } - MethodOptions getOptions() const { return Options; } - TypeIndex getType() const { return Type; } - int32_t getVTableSlotOffset() const { return VTableSlotOffset; } - -private: - MemberAccess Access; - MethodKind Kind; - MethodOptions Options; - TypeIndex Type; - int32_t VTableSlotOffset; -}; - -class FieldListRecordBuilder : public ListRecordBuilder { -private: - FieldListRecordBuilder(const FieldListRecordBuilder &) = delete; - void operator=(const FieldListRecordBuilder &) = delete; - -public: - FieldListRecordBuilder(); - - void reset() { ListRecordBuilder::reset(); } - - void writeMemberType(const BaseClassRecord &Record); - void writeMemberType(const EnumeratorRecord &Record); - void writeMemberType(const DataMemberRecord &Record); - void writeMemberType(const OneMethodRecord &Record); - void writeMemberType(const OverloadedMethodRecord &Record); - void writeMemberType(const NestedTypeRecord &Record); - void writeMemberType(const StaticDataMemberRecord &Record); - void writeMemberType(const VirtualBaseClassRecord &Record); - void writeMemberType(const VFPtrRecord &Type); - - using ListRecordBuilder::writeMemberType; -}; -} -} - -#endif Index: include/llvm/DebugInfo/CodeView/ListRecordBuilder.h =================================================================== --- include/llvm/DebugInfo/CodeView/ListRecordBuilder.h +++ /dev/null @@ -1,65 +0,0 @@ -//===- ListRecordBuilder.h --------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_DEBUGINFO_CODEVIEW_LISTRECORDBUILDER_H -#define LLVM_DEBUGINFO_CODEVIEW_LISTRECORDBUILDER_H - -#include "llvm/DebugInfo/CodeView/TypeRecordBuilder.h" - -namespace llvm { -namespace codeview { -class TypeTableBuilder; - -class ListRecordBuilder { -private: - ListRecordBuilder(const ListRecordBuilder &) = delete; - ListRecordBuilder &operator=(const ListRecordBuilder &) = delete; - -protected: - const int MethodKindShift = 2; - - explicit ListRecordBuilder(TypeRecordKind Kind); - -public: - llvm::StringRef str() { return Builder.str(); } - - void reset() { - Builder.reset(Kind); - ContinuationOffsets.clear(); - SubrecordStart = 0; - } - - void writeMemberType(const ListContinuationRecord &R); - - /// Writes this list record as a possible sequence of records. - TypeIndex writeListRecord(TypeTableBuilder &Table); - -protected: - void finishSubRecord(); - - TypeRecordBuilder &getBuilder() { return Builder; } - -private: - size_t getLastContinuationStart() const { - return ContinuationOffsets.empty() ? 0 : ContinuationOffsets.back(); - } - size_t getLastContinuationEnd() const { return Builder.size(); } - size_t getLastContinuationSize() const { - return getLastContinuationEnd() - getLastContinuationStart(); - } - - TypeRecordKind Kind; - TypeRecordBuilder Builder; - SmallVector ContinuationOffsets; - size_t SubrecordStart = 0; -}; -} -} - -#endif Index: include/llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h =================================================================== --- include/llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h +++ /dev/null @@ -1,50 +0,0 @@ -//===- MemoryTypeTableBuilder.h ---------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_DEBUGINFO_CODEVIEW_MEMORYTYPETABLEBUILDER_H -#define LLVM_DEBUGINFO_CODEVIEW_MEMORYTYPETABLEBUILDER_H - -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" -#include - -namespace llvm { -namespace codeview { - -class MemoryTypeTableBuilder : public TypeTableBuilder { -public: - explicit MemoryTypeTableBuilder(BumpPtrAllocator &Allocator) - : RecordStorage(Allocator) {} - - bool empty() const { return Records.empty(); } - - template void ForEachRecord(TFunc Func) { - uint32_t Index = TypeIndex::FirstNonSimpleIndex; - - for (StringRef R : Records) { - Func(TypeIndex(Index), R); - ++Index; - } - } - - TypeIndex writeRecord(llvm::StringRef Data) override; - - ArrayRef getRecords() const { return Records; } - -private: - std::vector Records; - BumpPtrAllocator &RecordStorage; - DenseMap HashedRecords; -}; - -} // end namespace codeview -} // end namespace llvm - -#endif // LLVM_DEBUGINFO_CODEVIEW_MEMORYTYPETABLEBUILDER_H Index: include/llvm/DebugInfo/CodeView/MethodListRecordBuilder.h =================================================================== --- include/llvm/DebugInfo/CodeView/MethodListRecordBuilder.h +++ /dev/null @@ -1,35 +0,0 @@ -//===- MethodListRecordBuilder.h --------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_DEBUGINFO_CODEVIEW_METHODLISTRECORDBUILDER_H -#define LLVM_DEBUGINFO_CODEVIEW_METHODLISTRECORDBUILDER_H - -#include "llvm/DebugInfo/CodeView/ListRecordBuilder.h" - -namespace llvm { -namespace codeview { - -class MethodInfo; - -class MethodListRecordBuilder : public ListRecordBuilder { -private: - MethodListRecordBuilder(const MethodListRecordBuilder &) = delete; - MethodListRecordBuilder &operator=(const MethodListRecordBuilder &) = delete; - -public: - MethodListRecordBuilder(); - - void writeMethod(MemberAccess Access, MethodKind Kind, MethodOptions Options, - TypeIndex Type, int32_t VTableSlotOffset); - void writeMethod(const MethodInfo &Method); -}; -} -} - -#endif Index: include/llvm/DebugInfo/CodeView/RecordSerialization.h =================================================================== --- include/llvm/DebugInfo/CodeView/RecordSerialization.h +++ include/llvm/DebugInfo/CodeView/RecordSerialization.h @@ -32,7 +32,7 @@ enum : unsigned { MaxRecordLength = 0xFF00 }; struct RecordPrefix { - ulittle16_t RecordLen; // Record length, starting from &Leaf. + ulittle16_t RecordLen; // Record length, starting from &RecordKind. ulittle16_t RecordKind; // Record kind enum (SymRecordKind or TypeRecordKind) }; Index: include/llvm/DebugInfo/CodeView/TypeDeserializer.h =================================================================== --- include/llvm/DebugInfo/CodeView/TypeDeserializer.h +++ include/llvm/DebugInfo/CodeView/TypeDeserializer.h @@ -76,7 +76,17 @@ }; public: - explicit FieldListDeserializer(msf::StreamReader &Reader) : Mapping(Reader) {} + explicit FieldListDeserializer(msf::StreamReader &Reader) : Mapping(Reader) { + CVType FieldList; + FieldList.Type = TypeLeafKind::LF_FIELDLIST; + consumeError(Mapping.Mapping.visitTypeBegin(FieldList)); + } + + ~FieldListDeserializer() { + CVType FieldList; + FieldList.Type = TypeLeafKind::LF_FIELDLIST; + consumeError(Mapping.Mapping.visitTypeEnd(FieldList)); + } Error visitMemberBegin(CVMemberRecord &Record) override { Mapping.StartOffset = Mapping.Reader.getOffset(); Index: include/llvm/DebugInfo/CodeView/TypeIndex.h =================================================================== --- include/llvm/DebugInfo/CodeView/TypeIndex.h +++ include/llvm/DebugInfo/CodeView/TypeIndex.h @@ -93,7 +93,7 @@ static const uint32_t SimpleModeMask = 0x00000700; public: - TypeIndex() : Index(0) {} + TypeIndex() : Index(static_cast(SimpleTypeKind::None)) {} explicit TypeIndex(uint32_t Index) : Index(Index) {} explicit TypeIndex(SimpleTypeKind Kind) : Index(static_cast(Kind)) {} Index: include/llvm/DebugInfo/CodeView/TypeRecord.h =================================================================== --- include/llvm/DebugInfo/CodeView/TypeRecord.h +++ include/llvm/DebugInfo/CodeView/TypeRecord.h @@ -701,26 +701,25 @@ StringRef Name) : TypeRecord(TypeRecordKind::OneMethod), Type(Type), Attrs(Attrs), VFTableOffset(VFTableOffset), Name(Name) {} - OneMethodRecord(TypeIndex Type, MemberAccess Access, MethodKind Kind, + OneMethodRecord(TypeIndex Type, MemberAccess Access, MethodKind MK, MethodOptions Options, int32_t VFTableOffset, StringRef Name) : TypeRecord(TypeRecordKind::OneMethod), Type(Type), - Attrs(Access, Kind, Options), VFTableOffset(VFTableOffset), Name(Name) { - } + Attrs(Access, MK, Options), VFTableOffset(VFTableOffset), Name(Name) {} /// Rewrite member type indices with IndexMap. Returns false if a type index /// is not in the map. bool remapTypeIndices(ArrayRef IndexMap); TypeIndex getType() const { return Type; } - MethodKind getKind() const { return Attrs.getMethodKind(); } + MethodKind getMethodKind() const { return Attrs.getMethodKind(); } MethodOptions getOptions() const { return Attrs.getFlags(); } MemberAccess getAccess() const { return Attrs.getAccess(); } int32_t getVFTableOffset() const { return VFTableOffset; } StringRef getName() const { return Name; } bool isIntroducingVirtual() const { - return getKind() == MethodKind::IntroducingVirtual || - getKind() == MethodKind::PureIntroducingVirtual; + return getMethodKind() == MethodKind::IntroducingVirtual || + getMethodKind() == MethodKind::PureIntroducingVirtual; } TypeIndex Type; MemberAttributes Attrs; Index: include/llvm/DebugInfo/CodeView/TypeRecordMapping.h =================================================================== --- include/llvm/DebugInfo/CodeView/TypeRecordMapping.h +++ include/llvm/DebugInfo/CodeView/TypeRecordMapping.h @@ -16,10 +16,15 @@ #include "llvm/Support/Error.h" namespace llvm { +namespace msf { +class StreamReader; +class StreamWriter; +} namespace codeview { class TypeRecordMapping : public TypeVisitorCallbacks { public: explicit TypeRecordMapping(msf::StreamReader &Reader) : IO(Reader) {} + explicit TypeRecordMapping(msf::StreamWriter &Writer) : IO(Writer) {} Error visitTypeBegin(CVType &Record) override; Error visitTypeEnd(CVType &Record) override; @@ -37,6 +42,7 @@ private: Optional TypeKind; + Optional MemberKind; CodeViewRecordIO IO; }; Index: include/llvm/DebugInfo/CodeView/TypeSerializationVisitor.h =================================================================== --- include/llvm/DebugInfo/CodeView/TypeSerializationVisitor.h +++ /dev/null @@ -1,85 +0,0 @@ -//===- TypeSerializationVisitor.h -------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_DEBUGINFO_CODEVIEW_TYPESERIALIZATIONVISITOR_H -#define LLVM_DEBUGINFO_CODEVIEW_TYPESERIALIZATIONVISITOR_H - -#include "llvm/DebugInfo/CodeView/FieldListRecordBuilder.h" -#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h" -#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" - -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/Error.h" - -namespace llvm { -namespace codeview { - -class TypeSerializationVisitor : public TypeVisitorCallbacks { -public: - TypeSerializationVisitor(FieldListRecordBuilder &FieldListBuilder, - MemoryTypeTableBuilder &TypeTableBuilder) - : FieldListBuilder(FieldListBuilder), TypeTableBuilder(TypeTableBuilder) { - } - - virtual Error visitTypeBegin(CVType &Record) override { - if (Record.Type == TypeLeafKind::LF_FIELDLIST) - FieldListBuilder.reset(); - return Error::success(); - } - - virtual Error visitTypeEnd(CVType &Record) override { - // Since this visitor's purpose is to serialize the record, fill out the - // fields of `Record` with the bytes of the record. - if (Record.Type == TypeLeafKind::LF_FIELDLIST) { - TypeTableBuilder.writeFieldList(FieldListBuilder); - updateCVRecord(Record); - } - - return Error::success(); - } - -#define TYPE_RECORD(EnumName, EnumVal, Name) \ - virtual Error visitKnownRecord(CVType &CVR, Name##Record &Record) override { \ - visitKnownRecordImpl(CVR, Record); \ - return Error::success(); \ - } -#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) -#define MEMBER_RECORD(EnumName, EnumVal, Name) \ - virtual Error visitKnownMember(CVMemberRecord &CVR, Name##Record &Record) \ - override { \ - visitMemberRecordImpl(Record); \ - return Error::success(); \ - } -#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) -#include "llvm/DebugInfo/CodeView/TypeRecords.def" - -private: - void updateCVRecord(CVType &Record) { - StringRef S = TypeTableBuilder.getRecords().back(); - Record.RecordData = ArrayRef(S.bytes_begin(), S.bytes_end()); - } - template - void visitKnownRecordImpl(CVType &CVR, RecordKind &Record) { - TypeTableBuilder.writeKnownType(Record); - updateCVRecord(CVR); - } - template - void visitMemberRecordImpl(RecordKind &Record) { - FieldListBuilder.writeMemberType(Record); - } - - void visitKnownRecordImpl(CVType &CVR, FieldListRecord &FieldList) {} - - FieldListRecordBuilder &FieldListBuilder; - MemoryTypeTableBuilder &TypeTableBuilder; -}; -} -} - -#endif Index: include/llvm/DebugInfo/CodeView/TypeSerializer.h =================================================================== --- /dev/null +++ include/llvm/DebugInfo/CodeView/TypeSerializer.h @@ -0,0 +1,342 @@ +//===- TypeSerializer.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_TYPESERIALIZER_H +#define LLVM_DEBUGINFO_CODEVIEW_TYPESERIALIZER_H + +#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" +#include "llvm/DebugInfo/MSF/ByteStream.h" +#include "llvm/DebugInfo/MSF/StreamWriter.h" + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace codeview { + +class TypeSerializer : public TypeVisitorCallbacks { + struct SubRecord { + SubRecord(TypeLeafKind K, uint32_t S) : Kind(K), Size(S) {} + + TypeLeafKind Kind; + uint32_t Size = 0; + }; + struct RecordSegment { + SmallVector SubRecords; + + uint32_t length() const { + uint32_t L = sizeof(RecordPrefix); + for (const auto &R : SubRecords) { + L += R.Size; + } + return L; + } + }; + + typedef SmallVector, 2> RecordList; + + static constexpr uint8_t ContinuationLength = 8; + BumpPtrAllocator &RecordStorage; + RecordSegment CurrentSegment; + RecordList FieldListSegments; + + TypeIndex LastTypeIndex; + Optional TypeKind; + Optional MemberKind; + std::vector RecordBuffer; + msf::MutableByteStream Stream; + msf::StreamWriter Writer; + TypeRecordMapping Mapping; + + RecordList SeenRecords; + StringMap HashedRecords; + + bool isInFieldList() const { + return TypeKind.hasValue() && *TypeKind == TypeLeafKind::LF_FIELDLIST; + } + + TypeIndex calcNextTypeIndex() const { + if (LastTypeIndex.isNoneType()) + return TypeIndex(TypeIndex::FirstNonSimpleIndex); + else + return TypeIndex(LastTypeIndex.getIndex() + 1); + } + + TypeIndex incrementTypeIndex() { + TypeIndex Previous = LastTypeIndex; + LastTypeIndex = calcNextTypeIndex(); + return Previous; + } + + MutableArrayRef getCurrentSubRecordData() { + assert(isInFieldList()); + return getCurrentRecordData().drop_front(CurrentSegment.length()); + } + + MutableArrayRef getCurrentRecordData() { + return MutableArrayRef(RecordBuffer) + .take_front(Writer.getOffset()); + } + + Error writeRecordPrefix(TypeLeafKind Kind) { + RecordPrefix Prefix; + Prefix.RecordKind = Kind; + Prefix.RecordLen = 0; + if (auto EC = Writer.writeObject(Prefix)) + return EC; + return Error::success(); + } + + TypeIndex insertRecordBytesPrivate(MutableArrayRef Record) { + assert(Record.size() % 4 == 0 && "Record is not aligned to 4 bytes!"); + + StringRef S(reinterpret_cast(Record.data()), Record.size()); + + TypeIndex NextTypeIndex = calcNextTypeIndex(); + auto Result = HashedRecords.try_emplace(S, NextTypeIndex); + if (Result.second) { + LastTypeIndex = NextTypeIndex; + SeenRecords.push_back(Record); + } + return Result.first->getValue(); + } + + Expected> + addPadding(MutableArrayRef 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(LF_PAD0 + PaddingBytes); + if (auto EC = Writer.writeInteger(Pad)) + return std::move(EC); + --PaddingBytes; + } + return MutableArrayRef(Record.data(), Record.size() + N); + } + +public: + explicit TypeSerializer(BumpPtrAllocator &Storage) + : RecordStorage(Storage), LastTypeIndex(), + RecordBuffer(MaxRecordLength * 2), Stream(RecordBuffer), 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. + } + + ArrayRef> records() const { return SeenRecords; } + + TypeIndex getLastTypeIndex() const { return LastTypeIndex; } + + TypeIndex insertRecordBytes(MutableArrayRef Record) { + assert(!TypeKind.hasValue() && "Already in a type mapping!"); + assert(Writer.getOffset() == 0 && "Stream has data already!"); + + return insertRecordBytesPrivate(Record); + } + + virtual Error visitTypeBegin(CVType &Record) override { + 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 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(ThisRecordData.data()); + Prefix->RecordLen = ThisRecordData.size() - sizeof(uint16_t); + + uint8_t *Copy = RecordStorage.Allocate(ThisRecordData.size()); + ::memcpy(Copy, ThisRecordData.data(), ThisRecordData.size()); + ThisRecordData = MutableArrayRef(Copy, ThisRecordData.size()); + Record = CVType(*TypeKind, ThisRecordData); + TypeIndex InsertedTypeIndex = insertRecordBytesPrivate(ThisRecordData); + + // 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(CIBytes.data()); + assert(*CI == 0xB0C0B0C0 && "Invalid TypeIndex placeholder"); + *CI = InsertedTypeIndex.getIndex(); + InsertedTypeIndex = insertRecordBytesPrivate(X); + } + + TypeKind.reset(); + Writer.setOffset(0); + FieldListSegments.clear(); + CurrentSegment.SubRecords.clear(); + + return InsertedTypeIndex; + } + + virtual Error visitTypeEnd(CVType &Record) override { + auto ExpectedIndex = visitTypeEndGetIndex(Record); + if (!ExpectedIndex) + return ExpectedIndex.takeError(); + return Error::success(); + } + + Error visitMemberBegin(CVMemberRecord &Record) override { + 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 visitMemberEnd(CVMemberRecord &Record) override { + 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 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(CopyData.data()); + Prefix->RecordLen = LengthWithSize - sizeof(uint16_t); + + uint8_t *SegmentBytes = RecordStorage.Allocate(LengthWithSize); + auto SavedSegment = + MutableArrayRef(SegmentBytes, LengthWithSize); + msf::MutableByteStream CS(SavedSegment); + msf::StreamWriter 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(); + } + +#define TYPE_RECORD(EnumName, EnumVal, Name) \ + virtual Error visitKnownRecord(CVType &CVR, Name##Record &Record) override { \ + return visitKnownRecordImpl(CVR, Record); \ + } +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#define MEMBER_RECORD(EnumName, EnumVal, Name) \ + Error visitKnownMember(CVMemberRecord &CVR, Name##Record &Record) override { \ + return visitKnownMemberImpl(CVR, Record); \ + } +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/TypeRecords.def" + +private: + template + Error visitKnownRecordImpl(CVType &CVR, RecordKind &Record) { + return Mapping.visitKnownRecord(CVR, Record); + } + + template + Error visitKnownMemberImpl(CVMemberRecord &CVR, RecordType &Record) { + assert(CVR.Kind == static_cast(Record.getKind())); + + if (auto EC = Writer.writeEnum(CVR.Kind)) + return EC; + + if (auto EC = Mapping.visitKnownMember(CVR, Record)) + return EC; + + // Get all the data that was just written and is yet to be committed to + // the current segment. Then pad it to 4 bytes. + MutableArrayRef ThisRecord = getCurrentSubRecordData(); + auto ExpectedRecord = addPadding(ThisRecord); + if (!ExpectedRecord) + return ExpectedRecord.takeError(); + ThisRecord = *ExpectedRecord; + + CurrentSegment.SubRecords.emplace_back(CVR.Kind, ThisRecord.size()); + CVR.Data = ThisRecord; + + // Both the last subrecord and the total length of this segment should be + // multiples of 4. + assert(ThisRecord.size() % 4 == 0); + assert(CurrentSegment.length() % 4 == 0); + + return Error::success(); + } +}; +} +} + +#endif Index: include/llvm/DebugInfo/CodeView/TypeTableBuilder.h =================================================================== --- include/llvm/DebugInfo/CodeView/TypeTableBuilder.h +++ include/llvm/DebugInfo/CodeView/TypeTableBuilder.h @@ -10,64 +10,111 @@ #ifndef LLVM_DEBUGINFO_CODEVIEW_TYPETABLEBUILDER_H #define LLVM_DEBUGINFO_CODEVIEW_TYPETABLEBUILDER_H -#include "llvm/DebugInfo/CodeView/CodeView.h" -#include "llvm/DebugInfo/CodeView/TypeIndex.h" -#include "llvm/DebugInfo/CodeView/TypeRecord.h" -#include "llvm/Support/Compiler.h" +#include "llvm/DebugInfo/CodeView/TypeSerializer.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Error.h" namespace llvm { -class StringRef; - namespace codeview { -class FieldListRecordBuilder; -class MethodListRecordBuilder; -class TypeRecordBuilder; - class TypeTableBuilder { private: TypeTableBuilder(const TypeTableBuilder &) = delete; TypeTableBuilder &operator=(const TypeTableBuilder &) = delete; -protected: - TypeTableBuilder(); + TypeIndex handleError(llvm::Error EC) const { + assert(false && "Couldn't write Type!"); + llvm::consumeError(std::move(EC)); + return TypeIndex(); + } -public: - virtual ~TypeTableBuilder(); + BumpPtrAllocator &Allocator; + TypeSerializer Serializer; public: - TypeIndex writeKnownType(const ModifierRecord &Record); - TypeIndex writeKnownType(const ProcedureRecord &Record); - TypeIndex writeKnownType(const MemberFunctionRecord &Record); - TypeIndex writeKnownType(const ArgListRecord &Record); - TypeIndex writeKnownType(const PointerRecord &Record); - TypeIndex writeKnownType(const ArrayRecord &Record); - TypeIndex writeKnownType(const ClassRecord &Record); - TypeIndex writeKnownType(const UnionRecord &Record); - TypeIndex writeKnownType(const EnumRecord &Record); - TypeIndex writeKnownType(const BitFieldRecord &Record); - TypeIndex writeKnownType(const VFTableShapeRecord &Record); - TypeIndex writeKnownType(const StringIdRecord &Record); - TypeIndex writeKnownType(const VFTableRecord &Record); - TypeIndex writeKnownType(const UdtSourceLineRecord &Record); - TypeIndex writeKnownType(const UdtModSourceLineRecord &Record); - TypeIndex writeKnownType(const FuncIdRecord &Record); - TypeIndex writeKnownType(const MemberFuncIdRecord &Record); - TypeIndex writeKnownType(const BuildInfoRecord &Record); - TypeIndex writeKnownType(const MethodOverloadListRecord &Record); - TypeIndex writeKnownType(const TypeServer2Record &Record); - - TypeIndex writeFieldList(FieldListRecordBuilder &FieldList); - - TypeIndex writeRecord(TypeRecordBuilder &builder); - - virtual TypeIndex writeRecord(llvm::StringRef record) = 0; - - ArrayRef getRecordKinds() const { return RecordKinds; } + explicit TypeTableBuilder(BumpPtrAllocator &Allocator) + : Allocator(Allocator), Serializer(Allocator) {} -private: - std::vector RecordKinds; + bool empty() const { return Serializer.records().empty(); } + + BumpPtrAllocator &getAllocator() const { return Allocator; } + + template TypeIndex writeKnownType(T &Record) { + static_assert(!std::is_same::value, + "Can't serialize FieldList!"); + + CVType Type; + Type.Type = static_cast(Record.getKind()); + if (auto EC = Serializer.visitTypeBegin(Type)) + return handleError(std::move(EC)); + if (auto EC = Serializer.visitKnownRecord(Type, Record)) + return handleError(std::move(EC)); + + auto ExpectedIndex = Serializer.visitTypeEndGetIndex(Type); + if (!ExpectedIndex) + return handleError(ExpectedIndex.takeError()); + + return *ExpectedIndex; + } + + TypeIndex writeSerializedRecord(MutableArrayRef Record) { + return Serializer.insertRecordBytes(Record); + } + + template void ForEachRecord(TFunc Func) { + uint32_t Index = TypeIndex::FirstNonSimpleIndex; + + for (auto Record : Serializer.records()) { + Func(TypeIndex(Index), Record); + ++Index; + } + } + + ArrayRef> records() const { + return Serializer.records(); + } +}; + +class FieldListRecordBuilder { + TypeTableBuilder &TypeTable; + TypeSerializer TempSerializer; + CVType Type; + +public: + explicit FieldListRecordBuilder(TypeTableBuilder &TypeTable) + : TypeTable(TypeTable), TempSerializer(TypeTable.getAllocator()) { + Type.Type = TypeLeafKind::LF_FIELDLIST; + } + + void begin() { + if (auto EC = TempSerializer.visitTypeBegin(Type)) + consumeError(std::move(EC)); + } + + template void writeMemberType(T &Record) { + CVMemberRecord CVMR; + CVMR.Kind = static_cast(Record.getKind()); + if (auto EC = TempSerializer.visitMemberBegin(CVMR)) + consumeError(std::move(EC)); + if (auto EC = TempSerializer.visitKnownMember(CVMR, Record)) + consumeError(std::move(EC)); + if (auto EC = TempSerializer.visitMemberEnd(CVMR)) + consumeError(std::move(EC)); + } + + TypeIndex end() { + if (auto EC = TempSerializer.visitTypeEnd(Type)) { + consumeError(std::move(EC)); + return TypeIndex(); + } + + TypeIndex Index; + for (auto Record : TempSerializer.records()) { + Index = TypeTable.writeSerializedRecord(Record); + } + return Index; + } }; } } Index: include/llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h =================================================================== --- include/llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h +++ include/llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h @@ -74,26 +74,34 @@ #define TYPE_RECORD(EnumName, EnumVal, Name) \ Error visitKnownRecord(CVType &CVR, Name##Record &Record) override { \ - for (auto Visitor : Pipeline) { \ - if (auto EC = Visitor->visitKnownRecord(CVR, Record)) \ - return EC; \ - } \ - return Error::success(); \ + return visitKnownRecordImpl(CVR, Record); \ } #define MEMBER_RECORD(EnumName, EnumVal, Name) \ Error visitKnownMember(CVMemberRecord &CVMR, Name##Record &Record) \ override { \ - for (auto Visitor : Pipeline) { \ - if (auto EC = Visitor->visitKnownMember(CVMR, Record)) \ - return EC; \ - } \ - return Error::success(); \ + return visitKnownMemberImpl(CVMR, Record); \ } #define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) #define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) #include "llvm/DebugInfo/CodeView/TypeRecords.def" private: + template Error visitKnownRecordImpl(CVType &CVR, T &Record) { + for (auto Visitor : Pipeline) { + if (auto EC = Visitor->visitKnownRecord(CVR, Record)) + return EC; + } + return Error::success(); + } + + template + Error visitKnownMemberImpl(CVMemberRecord &CVMR, T &Record) { + for (auto Visitor : Pipeline) { + if (auto EC = Visitor->visitKnownMember(CVMR, Record)) + return EC; + } + return Error::success(); + } std::vector Pipeline; }; } Index: include/llvm/DebugInfo/MSF/StreamWriter.h =================================================================== --- include/llvm/DebugInfo/MSF/StreamWriter.h +++ include/llvm/DebugInfo/MSF/StreamWriter.h @@ -26,7 +26,7 @@ class StreamWriter { public: StreamWriter() {} - StreamWriter(WritableStreamRef Stream); + explicit StreamWriter(WritableStreamRef Stream); Error writeBytes(ArrayRef Buffer); Error writeInteger(uint8_t Int); Index: lib/CodeGen/AsmPrinter/CodeViewDebug.h =================================================================== --- lib/CodeGen/AsmPrinter/CodeViewDebug.h +++ lib/CodeGen/AsmPrinter/CodeViewDebug.h @@ -20,8 +20,8 @@ #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineModuleInfo.h" -#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/DebugLoc.h" #include "llvm/MC/MCStreamer.h" @@ -37,7 +37,7 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase { MCStreamer &OS; llvm::BumpPtrAllocator Allocator; - codeview::MemoryTypeTableBuilder TypeTable; + codeview::TypeTableBuilder TypeTable; /// Represents the most general definition range. struct LocalVarDefRange { Index: lib/CodeGen/AsmPrinter/CodeViewDebug.cpp =================================================================== --- lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -15,7 +15,6 @@ #include "llvm/ADT/TinyPtrVector.h" #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" #include "llvm/DebugInfo/CodeView/CodeView.h" -#include "llvm/DebugInfo/CodeView/FieldListRecordBuilder.h" #include "llvm/DebugInfo/CodeView/Line.h" #include "llvm/DebugInfo/CodeView/SymbolRecord.h" #include "llvm/DebugInfo/CodeView/TypeDumper.h" @@ -222,8 +221,8 @@ // Build the fully qualified name of the scope. std::string ScopeName = getFullyQualifiedName(Scope); - TypeIndex TI = - TypeTable.writeKnownType(StringIdRecord(TypeIndex(), ScopeName)); + StringIdRecord SID(TypeIndex(), ScopeName); + auto TI = TypeTable.writeKnownType(SID); return recordTypeIndexForDINode(Scope, TI); } @@ -464,47 +463,47 @@ } CVTypeDumper CVTD(nullptr, /*PrintRecordBytes=*/false); - TypeTable.ForEachRecord( - [&](TypeIndex Index, StringRef Record) { - if (OS.isVerboseAsm()) { - // Emit a block comment describing the type record for readability. - SmallString<512> CommentBlock; - raw_svector_ostream CommentOS(CommentBlock); - ScopedPrinter SP(CommentOS); - SP.setPrefix(CommentPrefix); - CVTD.setPrinter(&SP); - Error E = CVTD.dump({Record.bytes_begin(), Record.bytes_end()}); - if (E) { - logAllUnhandledErrors(std::move(E), errs(), "error: "); - llvm_unreachable("produced malformed type record"); - } - // emitRawComment will insert its own tab and comment string before - // the first line, so strip off our first one. It also prints its own - // newline. - OS.emitRawComment( - CommentOS.str().drop_front(CommentPrefix.size() - 1).rtrim()); - } else { + TypeTable.ForEachRecord([&](TypeIndex Index, ArrayRef Record) { + if (OS.isVerboseAsm()) { + // Emit a block comment describing the type record for readability. + SmallString<512> CommentBlock; + raw_svector_ostream CommentOS(CommentBlock); + ScopedPrinter SP(CommentOS); + SP.setPrefix(CommentPrefix); + CVTD.setPrinter(&SP); + Error E = CVTD.dump(Record); + if (E) { + logAllUnhandledErrors(std::move(E), errs(), "error: "); + llvm_unreachable("produced malformed type record"); + } + // emitRawComment will insert its own tab and comment string before + // the first line, so strip off our first one. It also prints its own + // newline. + OS.emitRawComment( + CommentOS.str().drop_front(CommentPrefix.size() - 1).rtrim()); + } else { #ifndef NDEBUG - // Assert that the type data is valid even if we aren't dumping - // comments. The MSVC linker doesn't do much type record validation, - // so the first link of an invalid type record can succeed while - // subsequent links will fail with LNK1285. - ByteStream Stream({Record.bytes_begin(), Record.bytes_end()}); - CVTypeArray Types; - StreamReader Reader(Stream); - Error E = Reader.readArray(Types, Reader.getLength()); - if (!E) { - TypeVisitorCallbacks C; - E = CVTypeVisitor(C).visitTypeStream(Types); - } - if (E) { - logAllUnhandledErrors(std::move(E), errs(), "error: "); - llvm_unreachable("produced malformed type record"); - } + // Assert that the type data is valid even if we aren't dumping + // comments. The MSVC linker doesn't do much type record validation, + // so the first link of an invalid type record can succeed while + // subsequent links will fail with LNK1285. + ByteStream Stream(Record); + CVTypeArray Types; + StreamReader Reader(Stream); + Error E = Reader.readArray(Types, Reader.getLength()); + if (!E) { + TypeVisitorCallbacks C; + E = CVTypeVisitor(C).visitTypeStream(Types); + } + if (E) { + logAllUnhandledErrors(std::move(E), errs(), "error: "); + llvm_unreachable("produced malformed type record"); + } #endif - } - OS.EmitBinaryData(Record); - }); + } + StringRef S(reinterpret_cast(Record.data()), Record.size()); + OS.EmitBinaryData(S); + }); } namespace { @@ -1190,8 +1189,8 @@ (i == 0 && ElementSize == 0) ? Ty->getSizeInBits() / 8 : ElementSize; StringRef Name = (i == 0) ? Ty->getName() : ""; - ElementTypeIndex = TypeTable.writeKnownType( - ArrayRecord(ElementTypeIndex, IndexType, ArraySize, Name)); + ArrayRecord AR(ElementTypeIndex, IndexType, ArraySize, Name); + ElementTypeIndex = TypeTable.writeKnownType(AR); } (void)PartiallyIncomplete; @@ -1416,7 +1415,8 @@ BaseTy = cast(BaseTy)->getBaseType().resolve(); } TypeIndex ModifiedTI = getTypeIndex(BaseTy); - return TypeTable.writeKnownType(ModifierRecord(ModifiedTI, Mods)); + ModifierRecord MR(ModifiedTI, Mods); + return TypeTable.writeKnownType(MR); } TypeIndex CodeViewDebug::lowerTypeFunction(const DISubroutineType *Ty) { @@ -1473,9 +1473,10 @@ // TODO: Need to use the correct values for: // FunctionOptions // ThisPointerAdjustment. - TypeIndex TI = TypeTable.writeKnownType(MemberFunctionRecord( - ReturnTypeIndex, ClassType, ThisTypeIndex, CC, FunctionOptions::None, - ArgTypeIndices.size(), ArgListIndex, ThisAdjustment)); + MemberFunctionRecord MFR(ReturnTypeIndex, ClassType, ThisTypeIndex, CC, + FunctionOptions::None, ArgTypeIndices.size(), + ArgListIndex, ThisAdjustment); + TypeIndex TI = TypeTable.writeKnownType(MFR); return TI; } @@ -1483,7 +1484,9 @@ TypeIndex CodeViewDebug::lowerTypeVFTableShape(const DIDerivedType *Ty) { unsigned VSlotCount = Ty->getSizeInBits() / (8 * Asm->MAI->getPointerSize()); SmallVector Slots(VSlotCount, VFTableSlotKind::Near); - return TypeTable.writeKnownType(VFTableShapeRecord(Slots)); + + VFTableShapeRecord VFTSR(Slots); + return TypeTable.writeKnownType(VFTSR); } static MemberAccess translateAccessFlags(unsigned RecordTag, unsigned Flags) { @@ -1573,25 +1576,28 @@ if (Ty->isForwardDecl()) { CO |= ClassOptions::ForwardReference; } else { - FieldListRecordBuilder Fields; + FieldListRecordBuilder FLRB(TypeTable); + + FLRB.begin(); for (const DINode *Element : Ty->getElements()) { // We assume that the frontend provides all members in source declaration // order, which is what MSVC does. if (auto *Enumerator = dyn_cast_or_null(Element)) { - Fields.writeMemberType(EnumeratorRecord( - MemberAccess::Public, APSInt::getUnsigned(Enumerator->getValue()), - Enumerator->getName())); + EnumeratorRecord ER(MemberAccess::Public, + APSInt::getUnsigned(Enumerator->getValue()), + Enumerator->getName()); + FLRB.writeMemberType(ER); EnumeratorCount++; } } - FTI = TypeTable.writeFieldList(Fields); + FTI = FLRB.end(); } std::string FullName = getFullyQualifiedName(Ty); - return TypeTable.writeKnownType(EnumRecord(EnumeratorCount, CO, FTI, FullName, - Ty->getIdentifier(), - getTypeIndex(Ty->getBaseType()))); + EnumRecord ER(EnumeratorCount, CO, FTI, FullName, Ty->getIdentifier(), + getTypeIndex(Ty->getBaseType())); + return TypeTable.writeKnownType(ER); } //===----------------------------------------------------------------------===// @@ -1690,9 +1696,9 @@ ClassOptions CO = ClassOptions::ForwardReference | getCommonClassOptions(Ty); std::string FullName = getFullyQualifiedName(Ty); - TypeIndex FwdDeclTI = TypeTable.writeKnownType( - ClassRecord(Kind, 0, CO, TypeIndex(), TypeIndex(), TypeIndex(), 0, - FullName, Ty->getIdentifier())); + ClassRecord CR(Kind, 0, CO, TypeIndex(), TypeIndex(), TypeIndex(), 0, + FullName, Ty->getIdentifier()); + TypeIndex FwdDeclTI = TypeTable.writeKnownType(CR); if (!Ty->isForwardDecl()) DeferredCompleteTypes.push_back(Ty); return FwdDeclTI; @@ -1716,14 +1722,14 @@ uint64_t SizeInBytes = Ty->getSizeInBits() / 8; - TypeIndex ClassTI = TypeTable.writeKnownType( - ClassRecord(Kind, FieldCount, CO, FieldTI, TypeIndex(), VShapeTI, - SizeInBytes, FullName, Ty->getIdentifier())); + ClassRecord CR(Kind, FieldCount, CO, FieldTI, TypeIndex(), VShapeTI, + SizeInBytes, FullName, Ty->getIdentifier()); + TypeIndex ClassTI = TypeTable.writeKnownType(CR); - TypeTable.writeKnownType(UdtSourceLineRecord( - ClassTI, TypeTable.writeKnownType(StringIdRecord( - TypeIndex(0x0), getFullFilepath(Ty->getFile()))), - Ty->getLine())); + StringIdRecord SIDR(TypeIndex(0x0), getFullFilepath(Ty->getFile())); + TypeIndex SIDI = TypeTable.writeKnownType(SIDR); + UdtSourceLineRecord USLR(ClassTI, SIDI, Ty->getLine()); + TypeTable.writeKnownType(USLR); addToUDTs(Ty, ClassTI); @@ -1734,8 +1740,8 @@ ClassOptions CO = ClassOptions::ForwardReference | getCommonClassOptions(Ty); std::string FullName = getFullyQualifiedName(Ty); - TypeIndex FwdDeclTI = TypeTable.writeKnownType( - UnionRecord(0, CO, TypeIndex(), 0, FullName, Ty->getIdentifier())); + UnionRecord UR(0, CO, TypeIndex(), 0, FullName, Ty->getIdentifier()); + TypeIndex FwdDeclTI = TypeTable.writeKnownType(UR); if (!Ty->isForwardDecl()) DeferredCompleteTypes.push_back(Ty); return FwdDeclTI; @@ -1755,13 +1761,14 @@ uint64_t SizeInBytes = Ty->getSizeInBits() / 8; std::string FullName = getFullyQualifiedName(Ty); - TypeIndex UnionTI = TypeTable.writeKnownType(UnionRecord( - FieldCount, CO, FieldTI, SizeInBytes, FullName, Ty->getIdentifier())); + UnionRecord UR(FieldCount, CO, FieldTI, SizeInBytes, FullName, + Ty->getIdentifier()); + TypeIndex UnionTI = TypeTable.writeKnownType(UR); - TypeTable.writeKnownType(UdtSourceLineRecord( - UnionTI, TypeTable.writeKnownType(StringIdRecord( - TypeIndex(0x0), getFullFilepath(Ty->getFile()))), - Ty->getLine())); + StringIdRecord SIR(TypeIndex(0x0), getFullFilepath(Ty->getFile())); + TypeIndex SIRI = TypeTable.writeKnownType(SIR); + UdtSourceLineRecord USLR(UnionTI, SIRI, Ty->getLine()); + TypeTable.writeKnownType(USLR); addToUDTs(Ty, UnionTI); @@ -1776,7 +1783,8 @@ // list record. unsigned MemberCount = 0; ClassInfo Info = collectClassInfo(Ty); - FieldListRecordBuilder Fields; + FieldListRecordBuilder FLBR(TypeTable); + FLBR.begin(); // Create base classes. for (const DIDerivedType *I : Info.Inheritance) { @@ -1789,16 +1797,19 @@ auto RecordKind = (I->getFlags() & DINode::FlagIndirectVirtualBase) == DINode::FlagIndirectVirtualBase ? TypeRecordKind::IndirectVirtualBaseClass : TypeRecordKind::VirtualBaseClass; - Fields.writeMemberType(VirtualBaseClassRecord( + VirtualBaseClassRecord VBCR( RecordKind, translateAccessFlags(Ty->getTag(), I->getFlags()), getTypeIndex(I->getBaseType()), getVBPTypeIndex(), VBPtrOffset, - VBTableIndex)); + VBTableIndex); + + FLBR.writeMemberType(VBCR); } else { assert(I->getOffsetInBits() % 8 == 0 && "bases must be on byte boundaries"); - Fields.writeMemberType(BaseClassRecord( - translateAccessFlags(Ty->getTag(), I->getFlags()), - getTypeIndex(I->getBaseType()), I->getOffsetInBits() / 8)); + BaseClassRecord BCR(translateAccessFlags(Ty->getTag(), I->getFlags()), + getTypeIndex(I->getBaseType()), + I->getOffsetInBits() / 8); + FLBR.writeMemberType(BCR); } } @@ -1811,8 +1822,8 @@ translateAccessFlags(Ty->getTag(), Member->getFlags()); if (Member->isStaticMember()) { - Fields.writeMemberType( - StaticDataMemberRecord(Access, MemberBaseType, MemberName)); + StaticDataMemberRecord SDMR(Access, MemberBaseType, MemberName); + FLBR.writeMemberType(SDMR); MemberCount++; continue; } @@ -1820,7 +1831,8 @@ // Virtual function pointer member. if ((Member->getFlags() & DINode::FlagArtificial) && Member->getName().startswith("_vptr$")) { - Fields.writeMemberType(VFPtrRecord(getTypeIndex(Member->getBaseType()))); + VFPtrRecord VFPR(getTypeIndex(Member->getBaseType())); + FLBR.writeMemberType(VFPR); MemberCount++; continue; } @@ -1835,12 +1847,14 @@ MemberOffsetInBits = CI->getZExtValue() + MemberInfo.BaseOffset; } StartBitOffset -= MemberOffsetInBits; - MemberBaseType = TypeTable.writeKnownType(BitFieldRecord( - MemberBaseType, Member->getSizeInBits(), StartBitOffset)); + BitFieldRecord BFR(MemberBaseType, Member->getSizeInBits(), + StartBitOffset); + MemberBaseType = TypeTable.writeKnownType(BFR); } uint64_t MemberOffsetInBytes = MemberOffsetInBits / 8; - Fields.writeMemberType(DataMemberRecord(Access, MemberBaseType, - MemberOffsetInBytes, MemberName)); + DataMemberRecord DMR(Access, MemberBaseType, MemberOffsetInBytes, + MemberName); + FLBR.writeMemberType(DMR); MemberCount++; } @@ -1865,23 +1879,23 @@ } assert(Methods.size() > 0 && "Empty methods map entry"); if (Methods.size() == 1) - Fields.writeMemberType(Methods[0]); + FLBR.writeMemberType(Methods[0]); else { - TypeIndex MethodList = - TypeTable.writeKnownType(MethodOverloadListRecord(Methods)); - Fields.writeMemberType( - OverloadedMethodRecord(Methods.size(), MethodList, Name)); + MethodOverloadListRecord MOLR(Methods); + TypeIndex MethodList = TypeTable.writeKnownType(MOLR); + OverloadedMethodRecord OMR(Methods.size(), MethodList, Name); + FLBR.writeMemberType(OMR); } } // Create nested classes. for (const DICompositeType *Nested : Info.NestedClasses) { NestedTypeRecord R(getTypeIndex(DITypeRef(Nested)), Nested->getName()); - Fields.writeMemberType(R); + FLBR.writeMemberType(R); MemberCount++; } - TypeIndex FieldTI = TypeTable.writeFieldList(Fields); + TypeIndex FieldTI = FLBR.end(); return std::make_tuple(FieldTI, Info.VShapeTI, MemberCount, !Info.NestedClasses.empty()); } Index: lib/DebugInfo/CodeView/CMakeLists.txt =================================================================== --- lib/DebugInfo/CodeView/CMakeLists.txt +++ lib/DebugInfo/CodeView/CMakeLists.txt @@ -4,21 +4,15 @@ CVSymbolVisitor.cpp CVTypeVisitor.cpp EnumTables.cpp - FieldListRecordBuilder.cpp Line.cpp - ListRecordBuilder.cpp - MemoryTypeTableBuilder.cpp - MethodListRecordBuilder.cpp ModuleSubstream.cpp ModuleSubstreamVisitor.cpp RecordSerialization.cpp SymbolDumper.cpp TypeDumper.cpp TypeRecord.cpp - TypeRecordBuilder.cpp TypeRecordMapping.cpp TypeStreamMerger.cpp - TypeTableBuilder.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/CodeView Index: lib/DebugInfo/CodeView/CVTypeVisitor.cpp =================================================================== --- lib/DebugInfo/CodeView/CVTypeVisitor.cpp +++ lib/DebugInfo/CodeView/CVTypeVisitor.cpp @@ -17,24 +17,6 @@ using namespace llvm; using namespace llvm::codeview; -template -static Expected -deserializeMemberRecord(FieldListDeserializer &Deserializer, - msf::StreamReader &Reader, TypeLeafKind Kind) { - T MR(static_cast(Kind)); - CVMemberRecord CVR; - CVR.Kind = Kind; - - if (auto EC = Deserializer.visitMemberBegin(CVR)) - return std::move(EC); - if (auto EC = Deserializer.visitKnownMember(CVR, MR)) - return std::move(EC); - if (auto EC = Deserializer.visitMemberEnd(CVR)) - return std::move(EC); - - return CVR; -} - CVTypeVisitor::CVTypeVisitor(TypeVisitorCallbacks &Callbacks) : Callbacks(Callbacks) {} @@ -85,7 +67,8 @@ return Error::success(); } -Error CVTypeVisitor::visitMemberRecord(CVMemberRecord &Record) { +static Error visitMemberRecord(CVMemberRecord &Record, + TypeVisitorCallbacks &Callbacks) { if (auto EC = Callbacks.visitMemberBegin(Record)) return EC; @@ -113,6 +96,10 @@ return Error::success(); } +Error CVTypeVisitor::visitMemberRecord(CVMemberRecord &Record) { + return ::visitMemberRecord(Record, Callbacks); +} + /// Visits the type records in Data. Sets the error flag on parse failures. Error CVTypeVisitor::visitTypeStream(const CVTypeArray &Types) { for (auto I : Types) { @@ -122,23 +109,6 @@ return Error::success(); } -template -static Error visitKnownMember(FieldListDeserializer &Deserializer, - msf::StreamReader &Reader, TypeLeafKind Leaf, - TypeVisitorCallbacks &Callbacks) { - MR Record(static_cast(Leaf)); - CVMemberRecord CVR; - CVR.Kind = Leaf; - - if (auto EC = Callbacks.visitMemberBegin(CVR)) - return EC; - if (auto EC = Callbacks.visitKnownMember(CVR, Record)) - return EC; - if (auto EC = Callbacks.visitMemberEnd(CVR)) - return EC; - return Error::success(); -} - Error CVTypeVisitor::visitFieldListMemberStream(msf::StreamReader Reader) { FieldListDeserializer Deserializer(Reader); TypeVisitorCallbackPipeline Pipeline; @@ -150,25 +120,12 @@ if (auto EC = Reader.readEnum(Leaf)) return EC; - CVType Record; - switch (Leaf) { - default: - // Field list records do not describe their own length, so we cannot - // continue parsing past a type that we don't know how to deserialize. - return llvm::make_error( - cv_error_code::unknown_member_record); -#define MEMBER_RECORD(EnumName, EnumVal, Name) \ - case EnumName: { \ - if (auto EC = visitKnownMember(Deserializer, Reader, Leaf, \ - Pipeline)) \ - return EC; \ - break; \ - } -#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \ - MEMBER_RECORD(EnumVal, EnumVal, AliasName) -#include "llvm/DebugInfo/CodeView/TypeRecords.def" - } + CVMemberRecord Record; + Record.Kind = Leaf; + if (auto EC = ::visitMemberRecord(Record, Pipeline)) + return EC; } + return Error::success(); } Index: lib/DebugInfo/CodeView/CodeViewRecordIO.cpp =================================================================== --- lib/DebugInfo/CodeView/CodeViewRecordIO.cpp +++ lib/DebugInfo/CodeView/CodeViewRecordIO.cpp @@ -16,20 +16,39 @@ using namespace llvm; using namespace llvm::codeview; -Error CodeViewRecordIO::beginRecord(uint16_t Kind) { - assert(!CurrentRecord.hasValue() && "There is already a record active!"); - CurrentRecord.emplace(); - - CurrentRecord->Kind = Kind; +Error CodeViewRecordIO::beginRecord(Optional MaxLength) { + RecordLimit Limit; + Limit.MaxLength = MaxLength; + Limit.BeginOffset = getCurrentOffset(); + Limits.push_back(Limit); return Error::success(); } Error CodeViewRecordIO::endRecord() { - assert(CurrentRecord.hasValue() && "Not in a record!"); - CurrentRecord.reset(); + assert(!Limits.empty() && "Not in a record!"); + Limits.pop_back(); return Error::success(); } +uint32_t CodeViewRecordIO::maxFieldLength() const { + assert(!Limits.empty() && "Not in a record!"); + + // The max length of the next field is the minimum of all lengths that would + // be allowed by any of the sub-records we're in. In practice, we can only + // ever be at most 1 sub-record deep (in a FieldList), but this works for + // the general case. + uint32_t Offset = getCurrentOffset(); + Optional Min = Limits.front().bytesRemaining(Offset); + for (auto X : makeArrayRef(Limits).drop_front()) { + Optional ThisMin = X.bytesRemaining(Offset); + if (ThisMin.hasValue()) + Min = (Min.hasValue()) ? std::min(*Min, *ThisMin) : *ThisMin; + } + assert(Min.hasValue() && "Every field must have a maximum length!"); + + return *Min; +} + Error CodeViewRecordIO::skipPadding() { assert(!isWriting() && "Cannot skip padding while writing!"); @@ -114,7 +133,9 @@ Error CodeViewRecordIO::mapStringZ(StringRef &Value) { if (isWriting()) { - if (auto EC = Writer->writeZeroString(Value)) + // Truncate if we attempt to write too much. + StringRef S = Value.take_front(maxFieldLength() - 1); + if (auto EC = Writer->writeZeroString(S)) return EC; } else { if (auto EC = Reader->readZeroString(Value)) @@ -124,6 +145,10 @@ } Error CodeViewRecordIO::mapGuid(StringRef &Guid) { + constexpr uint32_t GuidSize = 16; + if (maxFieldLength() < GuidSize) + return make_error(cv_error_code::insufficient_buffer); + if (isWriting()) { assert(Guid.size() == 16 && "Invalid Guid Size!"); if (auto EC = Writer->writeFixedString(Guid)) Index: lib/DebugInfo/CodeView/FieldListRecordBuilder.cpp =================================================================== --- lib/DebugInfo/CodeView/FieldListRecordBuilder.cpp +++ /dev/null @@ -1,132 +0,0 @@ -//===-- FieldListRecordBuilder.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/FieldListRecordBuilder.h" - -using namespace llvm; -using namespace codeview; - -FieldListRecordBuilder::FieldListRecordBuilder() - : ListRecordBuilder(TypeRecordKind::FieldList) {} - -void FieldListRecordBuilder::writeMemberType(const BaseClassRecord &Record) { - TypeRecordBuilder &Builder = getBuilder(); - - Builder.writeTypeRecordKind(TypeRecordKind::BaseClass); - Builder.writeUInt16(static_cast(Record.getAccess())); - Builder.writeTypeIndex(Record.getBaseType()); - Builder.writeEncodedUnsignedInteger(Record.getBaseOffset()); - - finishSubRecord(); -} - -void FieldListRecordBuilder::writeMemberType(const EnumeratorRecord &Record) { - TypeRecordBuilder &Builder = getBuilder(); - - Builder.writeTypeRecordKind(TypeRecordKind::Enumerator); - Builder.writeUInt16(static_cast(Record.getAccess())); - // FIXME: Handle full APInt such as __int128. - Builder.writeEncodedUnsignedInteger(Record.getValue().getZExtValue()); - Builder.writeNullTerminatedString(Record.getName()); - - finishSubRecord(); -} - -void FieldListRecordBuilder::writeMemberType(const DataMemberRecord &Record) { - TypeRecordBuilder &Builder = getBuilder(); - - Builder.writeTypeRecordKind(Record.getKind()); - Builder.writeUInt16(static_cast(Record.getAccess())); - Builder.writeTypeIndex(Record.getType()); - Builder.writeEncodedUnsignedInteger(Record.getFieldOffset()); - Builder.writeNullTerminatedString(Record.getName()); - - finishSubRecord(); -} - -void FieldListRecordBuilder::writeMemberType( - const OverloadedMethodRecord &Record) { - TypeRecordBuilder &Builder = getBuilder(); - - Builder.writeTypeRecordKind(TypeRecordKind::OverloadedMethod); - Builder.writeUInt16(Record.getNumOverloads()); - Builder.writeTypeIndex(Record.getMethodList()); - Builder.writeNullTerminatedString(Record.getName()); - - finishSubRecord(); -} - -void FieldListRecordBuilder::writeMemberType(const OneMethodRecord &Record) { - TypeRecordBuilder &Builder = getBuilder(); - - uint16_t Flags = static_cast(Record.getAccess()); - Flags |= static_cast(Record.getKind()) << MethodKindShift; - Flags |= static_cast(Record.getOptions()); - - Builder.writeTypeRecordKind(TypeRecordKind::OneMethod); - Builder.writeUInt16(Flags); - Builder.writeTypeIndex(Record.getType()); - if (Record.isIntroducingVirtual()) { - assert(Record.getVFTableOffset() >= 0); - Builder.writeInt32(Record.getVFTableOffset()); - } else { - assert(Record.getVFTableOffset() == -1); - } - - Builder.writeNullTerminatedString(Record.getName()); - - finishSubRecord(); -} - -void FieldListRecordBuilder::writeMemberType(const NestedTypeRecord &Record) { - TypeRecordBuilder &Builder = getBuilder(); - - Builder.writeTypeRecordKind(Record.getKind()); - Builder.writeUInt16(0); - Builder.writeTypeIndex(Record.getNestedType()); - Builder.writeNullTerminatedString(Record.getName()); - - finishSubRecord(); -} - -void FieldListRecordBuilder::writeMemberType( - const StaticDataMemberRecord &Record) { - TypeRecordBuilder &Builder = getBuilder(); - - Builder.writeTypeRecordKind(Record.getKind()); - Builder.writeUInt16(static_cast(Record.getAccess())); - Builder.writeTypeIndex(Record.getType()); - Builder.writeNullTerminatedString(Record.getName()); - - finishSubRecord(); -} - -void FieldListRecordBuilder::writeMemberType( - const VirtualBaseClassRecord &Record) { - TypeRecordBuilder &Builder = getBuilder(); - - Builder.writeTypeRecordKind(Record.getKind()); - Builder.writeUInt16(static_cast(Record.getAccess())); - Builder.writeTypeIndex(Record.getBaseType()); - Builder.writeTypeIndex(Record.getVBPtrType()); - Builder.writeEncodedInteger(Record.getVBPtrOffset()); - Builder.writeEncodedUnsignedInteger(Record.getVTableIndex()); - - finishSubRecord(); -} - -void FieldListRecordBuilder::writeMemberType(const VFPtrRecord &Record) { - TypeRecordBuilder &Builder = getBuilder(); - - Builder.writeTypeRecordKind(TypeRecordKind::VFPtr); - Builder.writeUInt16(0); - Builder.writeTypeIndex(Record.getType()); - - finishSubRecord(); -} Index: lib/DebugInfo/CodeView/ListRecordBuilder.cpp =================================================================== --- lib/DebugInfo/CodeView/ListRecordBuilder.cpp +++ /dev/null @@ -1,103 +0,0 @@ -//===-- ListRecordBuilder.cpp ---------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "llvm/ADT/SmallString.h" -#include "llvm/DebugInfo/CodeView/ListRecordBuilder.h" -#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" - -using namespace llvm; -using namespace codeview; - -ListRecordBuilder::ListRecordBuilder(TypeRecordKind Kind) - : Kind(Kind), Builder(Kind) {} - -void ListRecordBuilder::writeMemberType(const ListContinuationRecord &R) { - TypeRecordBuilder &Builder = getBuilder(); - - assert(getLastContinuationSize() < MaxRecordLength - 8 && "continuation won't fit"); - - Builder.writeTypeRecordKind(TypeRecordKind::ListContinuation); - Builder.writeUInt16(0); - Builder.writeTypeIndex(R.getContinuationIndex()); - - // End the current segment manually so that nothing comes after the - // continuation. - ContinuationOffsets.push_back(Builder.size()); - SubrecordStart = Builder.size(); -} - -void ListRecordBuilder::finishSubRecord() { - // The type table inserts a 16 bit size field before each list, so factor that - // into our alignment padding. - uint32_t Remainder = - (Builder.size() + 2 * (ContinuationOffsets.size() + 1)) % 4; - if (Remainder != 0) { - for (int32_t PaddingBytesLeft = 4 - Remainder; PaddingBytesLeft > 0; - --PaddingBytesLeft) { - Builder.writeUInt8(LF_PAD0 + PaddingBytesLeft); - } - } - - // 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, - // back up and insert a continuation record, sliding the current subrecord - // down. - if (getLastContinuationSize() > MaxRecordLength - 8) { - assert(SubrecordStart != 0 && "can't slide from the start!"); - SmallString<128> SubrecordCopy( - Builder.str().slice(SubrecordStart, Builder.size())); - assert(SubrecordCopy.size() < MaxRecordLength - 8 && - "subrecord is too large to slide!"); - Builder.truncate(SubrecordStart); - - // Write a placeholder continuation record. - Builder.writeTypeRecordKind(TypeRecordKind::ListContinuation); - Builder.writeUInt16(0); - Builder.writeUInt32(0); - ContinuationOffsets.push_back(Builder.size()); - assert(Builder.size() == SubrecordStart + 8 && "wrong continuation size"); - assert(getLastContinuationSize() < MaxRecordLength && "segment too big"); - - // Start a new list record of the appropriate kind, and copy the previous - // subrecord into place. - Builder.writeTypeRecordKind(Kind); - Builder.writeBytes(SubrecordCopy); - } - - SubrecordStart = Builder.size(); -} - -TypeIndex ListRecordBuilder::writeListRecord(TypeTableBuilder &Table) { - // Get the continuation segments as a reversed vector of StringRefs for - // convenience. - SmallVector Segments; - StringRef Data = str(); - size_t LastEnd = 0; - for (size_t SegEnd : ContinuationOffsets) { - Segments.push_back(Data.slice(LastEnd, SegEnd)); - LastEnd = SegEnd; - } - Segments.push_back(Data.slice(LastEnd, Builder.size())); - - // Pop the last record off and emit it directly. - StringRef LastRec = Segments.pop_back_val(); - TypeIndex ContinuationIndex = Table.writeRecord(LastRec); - - // Emit each record with a continuation in reverse order, so that each one - // references the previous record. - for (StringRef Rec : reverse(Segments)) { - assert(*reinterpret_cast(Rec.data()) == - unsigned(Kind)); - ulittle32_t *ContinuationPtr = - reinterpret_cast(const_cast(Rec.end())) - 1; - *ContinuationPtr = ContinuationIndex.getIndex(); - ContinuationIndex = Table.writeRecord(Rec); - } - return ContinuationIndex; -} Index: lib/DebugInfo/CodeView/MemoryTypeTableBuilder.cpp =================================================================== --- lib/DebugInfo/CodeView/MemoryTypeTableBuilder.cpp +++ /dev/null @@ -1,46 +0,0 @@ -//===-- MemoryTypeTableBuilder.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/MemoryTypeTableBuilder.h" -#include "llvm/DebugInfo/CodeView/TypeIndex.h" - -using namespace llvm; -using namespace codeview; - -TypeIndex MemoryTypeTableBuilder::writeRecord(StringRef Data) { - assert(Data.size() <= UINT16_MAX); - auto I = HashedRecords.find(Data); - if (I != HashedRecords.end()) { - return I->second; - } - - // The record provided by the user lacks the 2 byte size field prefix and is - // not padded to 4 bytes. Ultimately, that is what gets emitted in the object - // file, so pad it out now. - const int SizeOfRecLen = 2; - const int Align = 4; - int TotalSize = alignTo(Data.size() + SizeOfRecLen, Align); - assert(TotalSize - SizeOfRecLen <= UINT16_MAX); - char *Mem = - reinterpret_cast(RecordStorage.Allocate(TotalSize, Align)); - *reinterpret_cast(Mem) = uint16_t(TotalSize - SizeOfRecLen); - memcpy(Mem + SizeOfRecLen, Data.data(), Data.size()); - for (int I = Data.size() + SizeOfRecLen; I < TotalSize; ++I) - Mem[I] = LF_PAD0 + (TotalSize - I); - - TypeIndex TI(static_cast(Records.size()) + - TypeIndex::FirstNonSimpleIndex); - - // Use only the data supplied by the user as a key to the hash table, so that - // future lookups will succeed. - HashedRecords.insert(std::make_pair(StringRef(Mem + SizeOfRecLen, Data.size()), TI)); - Records.push_back(StringRef(Mem, TotalSize)); - - return TI; -} Index: lib/DebugInfo/CodeView/MethodListRecordBuilder.cpp =================================================================== --- lib/DebugInfo/CodeView/MethodListRecordBuilder.cpp +++ /dev/null @@ -1,49 +0,0 @@ -//===-- MethodListRecordBuilder.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/MethodListRecordBuilder.h" -#include "llvm/DebugInfo/CodeView/FieldListRecordBuilder.h" - -using namespace llvm; -using namespace codeview; - -MethodListRecordBuilder::MethodListRecordBuilder() - : ListRecordBuilder(TypeRecordKind::MethodOverloadList) {} - -void MethodListRecordBuilder::writeMethod(MemberAccess Access, MethodKind Kind, - MethodOptions Options, TypeIndex Type, - int32_t VTableSlotOffset) { - TypeRecordBuilder &Builder = getBuilder(); - - uint16_t Flags = static_cast(Access); - Flags |= static_cast(Kind) << MethodKindShift; - Flags |= static_cast(Options); - - Builder.writeUInt16(Flags); - Builder.writeUInt16(0); - Builder.writeTypeIndex(Type); - switch (Kind) { - case MethodKind::IntroducingVirtual: - case MethodKind::PureIntroducingVirtual: - assert(VTableSlotOffset >= 0); - Builder.writeInt32(VTableSlotOffset); - break; - - default: - assert(VTableSlotOffset == -1); - break; - } - - // TODO: Fail if too big? -} - -void MethodListRecordBuilder::writeMethod(const MethodInfo &Method) { - writeMethod(Method.getAccess(), Method.getKind(), Method.getOptions(), - Method.getType(), Method.getVTableSlotOffset()); -} Index: lib/DebugInfo/CodeView/TypeDumper.cpp =================================================================== --- lib/DebugInfo/CodeView/TypeDumper.cpp +++ lib/DebugInfo/CodeView/TypeDumper.cpp @@ -428,7 +428,7 @@ MethodOverloadListRecord &MethodList) { for (auto &M : MethodList.getMethods()) { ListScope S(*W, "Method"); - printMemberAttributes(M.getAccess(), M.getKind(), M.getOptions()); + printMemberAttributes(M.getAccess(), M.getMethodKind(), M.getOptions()); printTypeIndex("Type", M.getType()); if (M.isIntroducingVirtual()) W->printHex("VFTableOffset", M.getVFTableOffset()); @@ -607,7 +607,7 @@ Error CVTypeDumper::visitKnownMember(CVMemberRecord &CVR, OneMethodRecord &Method) { - MethodKind K = Method.getKind(); + MethodKind K = Method.getMethodKind(); printMemberAttributes(Method.getAccess(), K, Method.getOptions()); printTypeIndex("Type", Method.getType()); // If virtual, then read the vftable offset. Index: lib/DebugInfo/CodeView/TypeRecordBuilder.cpp =================================================================== --- lib/DebugInfo/CodeView/TypeRecordBuilder.cpp +++ /dev/null @@ -1,119 +0,0 @@ -//===-- TypeRecordBuilder.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/TypeRecordBuilder.h" - -using namespace llvm; -using namespace codeview; - -TypeRecordBuilder::TypeRecordBuilder(TypeRecordKind Kind) - : Kind(Kind), Stream(Buffer), Writer(Stream) { - writeTypeRecordKind(Kind); -} - -StringRef TypeRecordBuilder::str() { - return StringRef(Buffer.data(), Buffer.size()); -} - -void TypeRecordBuilder::writeUInt8(uint8_t Value) { - Writer.write(Value); -} - -void TypeRecordBuilder::writeInt16(int16_t Value) { - Writer.write(Value); -} - -void TypeRecordBuilder::writeUInt16(uint16_t Value) { - Writer.write(Value); -} - -void TypeRecordBuilder::writeInt32(int32_t Value) { - Writer.write(Value); -} - -void TypeRecordBuilder::writeUInt32(uint32_t Value) { - Writer.write(Value); -} - -void TypeRecordBuilder::writeInt64(int64_t Value) { - Writer.write(Value); -} - -void TypeRecordBuilder::writeUInt64(uint64_t Value) { - Writer.write(Value); -} - -void TypeRecordBuilder::writeEncodedInteger(int64_t Value) { - if (Value >= 0) { - writeEncodedUnsignedInteger(static_cast(Value)); - } else { - writeEncodedSignedInteger(Value); - } -} - -void TypeRecordBuilder::writeEncodedSignedInteger(int64_t Value) { - if (Value >= std::numeric_limits::min() && - Value <= std::numeric_limits::max()) { - writeUInt16(LF_CHAR); - writeInt16(static_cast(Value)); - } else if (Value >= std::numeric_limits::min() && - Value <= std::numeric_limits::max()) { - writeUInt16(LF_SHORT); - writeInt16(static_cast(Value)); - } else if (Value >= std::numeric_limits::min() && - Value <= std::numeric_limits::max()) { - writeUInt16(LF_LONG); - writeInt32(static_cast(Value)); - } else { - writeUInt16(LF_QUADWORD); - writeInt64(Value); - } -} - -void TypeRecordBuilder::writeEncodedUnsignedInteger(uint64_t Value) { - if (Value < LF_CHAR) { - writeUInt16(static_cast(Value)); - } else if (Value <= std::numeric_limits::max()) { - writeUInt16(LF_USHORT); - writeUInt16(static_cast(Value)); - } else if (Value <= std::numeric_limits::max()) { - writeUInt16(LF_ULONG); - writeUInt32(static_cast(Value)); - } else { - writeUInt16(LF_UQUADWORD); - writeUInt64(Value); - } -} - -void TypeRecordBuilder::writeNullTerminatedString(StringRef Value) { - // Usually the null terminated string comes last, so truncate it to avoid a - // record larger than MaxNameLength. Don't do this if this is a list record. - // Those have special handling to split the record. - unsigned MaxNameLength = MaxRecordLength; - if (Kind != TypeRecordKind::FieldList && - Kind != TypeRecordKind::MethodOverloadList) - MaxNameLength = maxBytesRemaining(); - assert(MaxNameLength > 0 && "need room for null terminator"); - Value = Value.take_front(MaxNameLength - 1); - Stream.write(Value.data(), Value.size()); - writeUInt8(0); -} - -void TypeRecordBuilder::writeGuid(StringRef Guid) { - assert(Guid.size() == 16); - Stream.write(Guid.data(), 16); -} - -void TypeRecordBuilder::writeTypeIndex(TypeIndex TypeInd) { - writeUInt32(TypeInd.getIndex()); -} - -void TypeRecordBuilder::writeTypeRecordKind(TypeRecordKind Kind) { - writeUInt16(static_cast(Kind)); -} Index: lib/DebugInfo/CodeView/TypeRecordMapping.cpp =================================================================== --- lib/DebugInfo/CodeView/TypeRecordMapping.cpp +++ lib/DebugInfo/CodeView/TypeRecordMapping.cpp @@ -17,24 +17,6 @@ return EC; namespace { -struct MapStringZ { - Error operator()(CodeViewRecordIO &IO, StringRef &S) const { - return IO.mapStringZ(S); - } -}; - -struct MapInteger { - template Error operator()(CodeViewRecordIO &IO, T &N) const { - return IO.mapInteger(N); - } -}; - -struct MapEnum { - template Error operator()(CodeViewRecordIO &IO, T &N) const { - return IO.mapEnum(N); - } -}; - struct MapOneMethodRecord { explicit MapOneMethodRecord(bool IsFromOverloadList) : IsFromOverloadList(IsFromOverloadList) {} @@ -64,35 +46,97 @@ static Error mapNameAndUniqueName(CodeViewRecordIO &IO, StringRef &Name, StringRef &UniqueName, bool HasUniqueName) { - error(IO.mapStringZ(Name)); - if (HasUniqueName) - error(IO.mapStringZ(UniqueName)); + if (IO.isWriting()) { + // Try to be smart about what we write here. We can't write anything too + // large, so if we're going to go over the limit, truncate both the name + // and unique name by the same amount. + uint32_t BytesLeft = IO.maxFieldLength(); + if (HasUniqueName) { + uint32_t BytesNeeded = Name.size() + UniqueName.size() + 2; + StringRef N = Name; + StringRef U = UniqueName; + if (BytesNeeded > BytesLeft) { + uint32_t BytesToDrop = (BytesNeeded - BytesLeft); + uint32_t DropN = std::min(N.size(), BytesToDrop / 2); + uint32_t DropU = std::min(U.size(), BytesToDrop - DropN); + + N = N.drop_back(DropN); + U = U.drop_back(DropU); + } + + error(IO.mapStringZ(N)); + error(IO.mapStringZ(U)); + } else { + uint32_t BytesNeeded = Name.size() + 1; + StringRef N = Name; + if (BytesNeeded > BytesLeft) { + uint32_t BytesToDrop = std::min(N.size(), BytesToDrop); + N = N.drop_back(BytesToDrop); + } + error(IO.mapStringZ(N)); + } + } else { + error(IO.mapStringZ(Name)); + if (HasUniqueName) + error(IO.mapStringZ(UniqueName)); + } return Error::success(); } Error TypeRecordMapping::visitTypeBegin(CVType &CVR) { - error(IO.beginRecord(CVR.Type)); + assert(!TypeKind.hasValue() && "Already in a type mapping!"); + assert(!MemberKind.hasValue() && "Already in a member mapping!"); + + // FieldList and MethodList records can be any length because they can be + // split with continuation records. All other record types cannot be + // longer than the maximum record length. + Optional MaxLen; + if (CVR.Type != TypeLeafKind::LF_FIELDLIST && + CVR.Type != TypeLeafKind::LF_METHODLIST) + MaxLen = MaxRecordLength - sizeof(RecordPrefix); + error(IO.beginRecord(MaxLen)); TypeKind = CVR.Type; return Error::success(); } Error TypeRecordMapping::visitTypeEnd(CVType &Record) { + assert(TypeKind.hasValue() && "Not in a type mapping!"); + assert(!MemberKind.hasValue() && "Still in a member mapping!"); + error(IO.endRecord()); + TypeKind.reset(); return Error::success(); } Error TypeRecordMapping::visitMemberBegin(CVMemberRecord &Record) { + assert(TypeKind.hasValue() && "Not in a type mapping!"); + assert(!MemberKind.hasValue() && "Already in a member mapping!"); + + // The largest possible subrecord is one in which there is a record prefix, + // followed by the subrecord, followed by a continuation, and that entire + // sequence spaws `MaxRecordLength` bytes. So the record's length is + // calculated as follows. + constexpr uint32_t ContinuationLength = 8; + error(IO.beginRecord(MaxRecordLength - sizeof(RecordPrefix) - + ContinuationLength)); + + MemberKind = Record.Kind; return Error::success(); } Error TypeRecordMapping::visitMemberEnd(CVMemberRecord &Record) { + assert(TypeKind.hasValue() && "Not in a type mapping!"); + assert(MemberKind.hasValue() && "Not in a member mapping!"); + if (!IO.isWriting()) { if (auto EC = IO.skipPadding()) return EC; } + MemberKind.reset(); + error(IO.endRecord()); return Error::success(); } @@ -129,7 +173,9 @@ } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ArgListRecord &Record) { - error(IO.mapVectorN(Record.StringIndices, MapInteger())); + error(IO.mapVectorN( + Record.StringIndices, + [](CodeViewRecordIO &IO, TypeIndex &N) { return IO.mapInteger(N); })); return Error::success(); } @@ -245,7 +291,9 @@ NamesLen += Name.size() + 1; } error(IO.mapInteger(NamesLen)); - error(IO.mapVectorTail(Record.MethodNames, MapStringZ())); + error(IO.mapVectorTail( + Record.MethodNames, + [](CodeViewRecordIO &IO, StringRef &S) { return IO.mapStringZ(S); })); return Error::success(); } @@ -295,7 +343,9 @@ Error TypeRecordMapping::visitKnownRecord(CVType &CVR, BuildInfoRecord &Record) { - error(IO.mapVectorN(Record.ArgIndices, MapInteger())); + error(IO.mapVectorN( + Record.ArgIndices, + [](CodeViewRecordIO &IO, TypeIndex &N) { return IO.mapInteger(N); })); return Error::success(); } Index: lib/DebugInfo/CodeView/TypeStreamMerger.cpp =================================================================== --- lib/DebugInfo/CodeView/TypeStreamMerger.cpp +++ lib/DebugInfo/CodeView/TypeStreamMerger.cpp @@ -11,10 +11,10 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" -#include "llvm/DebugInfo/CodeView/FieldListRecordBuilder.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" #include "llvm/Support/Error.h" @@ -54,7 +54,8 @@ /// existing destination type index. class TypeStreamMerger : public TypeVisitorCallbacks { public: - TypeStreamMerger(TypeTableBuilder &DestStream) : DestStream(DestStream) { + TypeStreamMerger(TypeTableBuilder &DestStream) + : DestStream(DestStream), FieldListBuilder(DestStream) { assert(!hadError()); } @@ -94,7 +95,7 @@ template Error visitKnownMemberRecordImpl(RecordType &Record) { FoundBadTypeIndex |= !Record.remapTypeIndices(IndexMap); - FieldBuilder.writeMemberType(Record); + FieldListBuilder.writeMemberType(Record); return Error::success(); } @@ -102,9 +103,10 @@ bool FoundBadTypeIndex = false; - FieldListRecordBuilder FieldBuilder; + BumpPtrAllocator Allocator; TypeTableBuilder &DestStream; + FieldListRecordBuilder FieldListBuilder; bool IsInFieldList{false}; size_t BeginIndexMapSize = 0; @@ -120,6 +122,7 @@ if (Rec.Type == TypeLeafKind::LF_FIELDLIST) { assert(!IsInFieldList); IsInFieldList = true; + FieldListBuilder.begin(); } else BeginIndexMapSize = IndexMap.size(); return Error::success(); @@ -127,8 +130,8 @@ Error TypeStreamMerger::visitTypeEnd(CVRecord &Rec) { if (Rec.Type == TypeLeafKind::LF_FIELDLIST) { - IndexMap.push_back(DestStream.writeFieldList(FieldBuilder)); - FieldBuilder.reset(); + TypeIndex Index = FieldListBuilder.end(); + IndexMap.push_back(Index); IsInFieldList = false; } return Error::success(); Index: lib/DebugInfo/CodeView/TypeTableBuilder.cpp =================================================================== --- lib/DebugInfo/CodeView/TypeTableBuilder.cpp +++ /dev/null @@ -1,300 +0,0 @@ -//===-- TypeTableBuilder.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/TypeTableBuilder.h" -#include "llvm/DebugInfo/CodeView/FieldListRecordBuilder.h" -#include "llvm/DebugInfo/CodeView/MethodListRecordBuilder.h" -#include "llvm/DebugInfo/CodeView/TypeIndex.h" -#include "llvm/DebugInfo/CodeView/TypeRecordBuilder.h" -#include "llvm/Support/raw_ostream.h" - -using namespace llvm; -using namespace codeview; - -TypeTableBuilder::TypeTableBuilder() {} - -TypeTableBuilder::~TypeTableBuilder() {} - -TypeIndex TypeTableBuilder::writeKnownType(const ModifierRecord &Record) { - TypeRecordBuilder Builder(Record.getKind()); - - Builder.writeTypeIndex(Record.getModifiedType()); - Builder.writeUInt16(static_cast(Record.getModifiers())); - - return writeRecord(Builder); -} - -TypeIndex TypeTableBuilder::writeKnownType(const ProcedureRecord &Record) { - TypeRecordBuilder Builder(Record.getKind()); - - Builder.writeTypeIndex(Record.getReturnType()); - Builder.writeUInt8(static_cast(Record.getCallConv())); - Builder.writeUInt8(static_cast(Record.getOptions())); - Builder.writeUInt16(Record.getParameterCount()); - Builder.writeTypeIndex(Record.getArgumentList()); - - return writeRecord(Builder); -} - -TypeIndex TypeTableBuilder::writeKnownType(const MemberFunctionRecord &Record) { - TypeRecordBuilder Builder(Record.getKind()); - - Builder.writeTypeIndex(Record.getReturnType()); - Builder.writeTypeIndex(Record.getClassType()); - Builder.writeTypeIndex(Record.getThisType()); - Builder.writeUInt8(static_cast(Record.getCallConv())); - Builder.writeUInt8(static_cast(Record.getOptions())); - Builder.writeUInt16(Record.getParameterCount()); - Builder.writeTypeIndex(Record.getArgumentList()); - Builder.writeInt32(Record.getThisPointerAdjustment()); - - return writeRecord(Builder); -} - -TypeIndex TypeTableBuilder::writeKnownType(const ArgListRecord &Record) { - TypeRecordBuilder Builder(Record.getKind()); - - Builder.writeUInt32(Record.getIndices().size()); - for (TypeIndex TI : Record.getIndices()) { - Builder.writeTypeIndex(TI); - } - - return writeRecord(Builder); -} - -TypeIndex TypeTableBuilder::writeKnownType(const PointerRecord &Record) { - TypeRecordBuilder Builder(Record.getKind()); - - Builder.writeTypeIndex(Record.getReferentType()); - Builder.writeUInt32(Record.Attrs); - - if (Record.isPointerToMember()) { - const MemberPointerInfo &M = Record.getMemberInfo(); - Builder.writeTypeIndex(M.getContainingType()); - Builder.writeUInt16(static_cast(M.getRepresentation())); - } - - return writeRecord(Builder); -} - -static void writeNameAndUniqueName(TypeRecordBuilder &Builder, ClassOptions CO, - StringRef Name, StringRef UniqueName) { - // Truncate the names to half the remaining record length. - unsigned MaxNameLength = Builder.maxBytesRemaining() / 2; - Name = Name.take_front(MaxNameLength - 1); - UniqueName = UniqueName.take_front(MaxNameLength - 1); - - Builder.writeNullTerminatedString(Name); - if ((CO & ClassOptions::HasUniqueName) != ClassOptions::None) { - Builder.writeNullTerminatedString(UniqueName); - } -} - -TypeIndex TypeTableBuilder::writeKnownType(const ArrayRecord &Record) { - TypeRecordBuilder Builder(Record.getKind()); - - Builder.writeTypeIndex(Record.getElementType()); - Builder.writeTypeIndex(Record.getIndexType()); - Builder.writeEncodedUnsignedInteger(Record.getSize()); - Builder.writeNullTerminatedString(Record.getName()); - - return writeRecord(Builder); -} - -TypeIndex TypeTableBuilder::writeKnownType(const ClassRecord &Record) { - assert((Record.getKind() == TypeRecordKind::Struct) || - (Record.getKind() == TypeRecordKind::Class) || - (Record.getKind() == TypeRecordKind::Interface)); - - TypeRecordBuilder Builder(Record.getKind()); - - Builder.writeUInt16(Record.getMemberCount()); - uint16_t Flags = - static_cast(Record.getOptions()) | - (static_cast(Record.getHfa()) << ClassRecord::HfaKindShift) | - (static_cast(Record.getWinRTKind()) - << ClassRecord::WinRTKindShift); - Builder.writeUInt16(Flags); - Builder.writeTypeIndex(Record.getFieldList()); - Builder.writeTypeIndex(Record.getDerivationList()); - Builder.writeTypeIndex(Record.getVTableShape()); - Builder.writeEncodedUnsignedInteger(Record.getSize()); - writeNameAndUniqueName(Builder, Record.getOptions(), Record.getName(), - Record.getUniqueName()); - - return writeRecord(Builder); -} - -TypeIndex TypeTableBuilder::writeKnownType(const UnionRecord &Record) { - TypeRecordBuilder Builder(TypeRecordKind::Union); - Builder.writeUInt16(Record.getMemberCount()); - uint16_t Flags = - static_cast(Record.getOptions()) | - (static_cast(Record.getHfa()) << ClassRecord::HfaKindShift); - Builder.writeUInt16(Flags); - Builder.writeTypeIndex(Record.getFieldList()); - Builder.writeEncodedUnsignedInteger(Record.getSize()); - writeNameAndUniqueName(Builder, Record.getOptions(), Record.getName(), - Record.getUniqueName()); - return writeRecord(Builder); -} - -TypeIndex TypeTableBuilder::writeKnownType(const EnumRecord &Record) { - TypeRecordBuilder Builder(Record.getKind()); - - Builder.writeUInt16(Record.getMemberCount()); - Builder.writeUInt16(static_cast(Record.getOptions())); - Builder.writeTypeIndex(Record.getUnderlyingType()); - Builder.writeTypeIndex(Record.getFieldList()); - writeNameAndUniqueName(Builder, Record.getOptions(), Record.getName(), - Record.getUniqueName()); - - return writeRecord(Builder); -} - -TypeIndex TypeTableBuilder::writeKnownType(const BitFieldRecord &Record) { - TypeRecordBuilder Builder(Record.getKind()); - - Builder.writeTypeIndex(Record.getType()); - Builder.writeUInt8(Record.getBitSize()); - Builder.writeUInt8(Record.getBitOffset()); - - return writeRecord(Builder); -} - -TypeIndex TypeTableBuilder::writeKnownType(const VFTableShapeRecord &Record) { - TypeRecordBuilder Builder(Record.getKind()); - - ArrayRef Slots = Record.getSlots(); - - Builder.writeUInt16(Slots.size()); - for (size_t SlotIndex = 0; SlotIndex < Slots.size(); SlotIndex += 2) { - uint8_t Byte = static_cast(Slots[SlotIndex]) << 4; - if ((SlotIndex + 1) < Slots.size()) { - Byte |= static_cast(Slots[SlotIndex + 1]); - } - Builder.writeUInt8(Byte); - } - - return writeRecord(Builder); -} - -TypeIndex TypeTableBuilder::writeKnownType(const VFTableRecord &Record) { - TypeRecordBuilder Builder(Record.getKind()); - Builder.writeTypeIndex(Record.getCompleteClass()); - Builder.writeTypeIndex(Record.getOverriddenVTable()); - Builder.writeUInt32(Record.getVFPtrOffset()); - - // Sum up the lengths of the null-terminated names. - size_t NamesLen = Record.getName().size() + 1; - for (StringRef MethodName : Record.getMethodNames()) - NamesLen += MethodName.size() + 1; - - // FIXME: Avoid creating a record longer than MaxRecordLength. - Builder.writeUInt32(NamesLen); - Builder.writeNullTerminatedString(Record.getName()); - for (StringRef MethodName : Record.getMethodNames()) - Builder.writeNullTerminatedString(MethodName); - - return writeRecord(Builder); -} - -TypeIndex TypeTableBuilder::writeKnownType(const StringIdRecord &Record) { - TypeRecordBuilder Builder(TypeRecordKind::StringId); - Builder.writeTypeIndex(Record.getId()); - Builder.writeNullTerminatedString(Record.getString()); - return writeRecord(Builder); -} - -TypeIndex TypeTableBuilder::writeKnownType(const UdtSourceLineRecord &Record) { - TypeRecordBuilder Builder(Record.getKind()); - Builder.writeTypeIndex(Record.getUDT()); - Builder.writeTypeIndex(Record.getSourceFile()); - Builder.writeUInt32(Record.getLineNumber()); - return writeRecord(Builder); -} - -TypeIndex -TypeTableBuilder::writeKnownType(const UdtModSourceLineRecord &Record) { - TypeRecordBuilder Builder(Record.getKind()); - Builder.writeTypeIndex(Record.getUDT()); - Builder.writeTypeIndex(Record.getSourceFile()); - Builder.writeUInt32(Record.getLineNumber()); - Builder.writeUInt16(Record.getModule()); - return writeRecord(Builder); -} - -TypeIndex TypeTableBuilder::writeKnownType(const FuncIdRecord &Record) { - TypeRecordBuilder Builder(Record.getKind()); - Builder.writeTypeIndex(Record.getParentScope()); - Builder.writeTypeIndex(Record.getFunctionType()); - Builder.writeNullTerminatedString(Record.getName()); - return writeRecord(Builder); -} - -TypeIndex TypeTableBuilder::writeKnownType(const MemberFuncIdRecord &Record) { - TypeRecordBuilder Builder(Record.getKind()); - Builder.writeTypeIndex(Record.getClassType()); - Builder.writeTypeIndex(Record.getFunctionType()); - Builder.writeNullTerminatedString(Record.getName()); - return writeRecord(Builder); -} - -TypeIndex TypeTableBuilder::writeKnownType(const BuildInfoRecord &Record) { - TypeRecordBuilder Builder(Record.getKind()); - assert(Record.getArgs().size() <= UINT16_MAX); - Builder.writeUInt16(Record.getArgs().size()); - for (TypeIndex Arg : Record.getArgs()) - Builder.writeTypeIndex(Arg); - return writeRecord(Builder); -} - -TypeIndex TypeTableBuilder::writeRecord(TypeRecordBuilder &Builder) { - TypeIndex I = writeRecord(Builder.str()); - RecordKinds.push_back(Builder.kind()); - return I; -} - -TypeIndex TypeTableBuilder::writeFieldList(FieldListRecordBuilder &FieldList) { - TypeIndex I = FieldList.writeListRecord(*this); - RecordKinds.push_back(TypeRecordKind::FieldList); - return I; -} - -TypeIndex -TypeTableBuilder::writeKnownType(const MethodOverloadListRecord &Record) { - TypeRecordBuilder Builder(Record.getKind()); - for (const OneMethodRecord &Method : Record.getMethods()) { - uint16_t Flags = static_cast(Method.getAccess()); - Flags |= static_cast(Method.getKind()) - << MemberAttributes::MethodKindShift; - Flags |= static_cast(Method.getOptions()); - Builder.writeUInt16(Flags); - Builder.writeUInt16(0); // padding - Builder.writeTypeIndex(Method.getType()); - if (Method.isIntroducingVirtual()) { - assert(Method.getVFTableOffset() >= 0); - Builder.writeInt32(Method.getVFTableOffset()); - } else { - assert(Method.getVFTableOffset() == -1); - } - } - - // TODO: Split the list into multiple records if it's longer than 64KB, using - // a subrecord of TypeRecordKind::Index to chain the records together. - return writeRecord(Builder); -} - -TypeIndex TypeTableBuilder::writeKnownType(const TypeServer2Record &Record) { - TypeRecordBuilder Builder(Record.getKind()); - Builder.writeGuid(Record.getGuid()); - Builder.writeUInt32(Record.getAge()); - Builder.writeNullTerminatedString(Record.getName()); - return writeRecord(Builder); -} Index: tools/llvm-pdbdump/PdbYaml.cpp =================================================================== --- tools/llvm-pdbdump/PdbYaml.cpp +++ tools/llvm-pdbdump/PdbYaml.cpp @@ -18,7 +18,7 @@ #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" -#include "llvm/DebugInfo/CodeView/TypeSerializationVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeSerializer.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" #include "llvm/DebugInfo/PDB/PDBExtras.h" #include "llvm/DebugInfo/PDB/PDBTypes.h" @@ -244,8 +244,7 @@ pdb::yaml::SerializationContext &Context) { codeview::TypeVisitorCallbackPipeline Pipeline; codeview::TypeDeserializer Deserializer; - codeview::TypeSerializationVisitor Serializer(Context.FieldListBuilder, - Context.TypeTableBuilder); + codeview::TypeSerializer Serializer(Context.Allocator); pdb::TpiHashUpdater Hasher; if (IO.outputting()) { @@ -255,6 +254,11 @@ } else { // For Yaml to PDB, extract from the high level record type, then write it // to bytes. + + // This might be interpreted as a hack, but serializing FieldList + // sub-records requires having access to the same serializer being used by + // the FieldList itself. + Context.ActiveSerializer = &Serializer; Pipeline.addCallbackToPipeline(Context.Dumper); Pipeline.addCallbackToPipeline(Serializer); Pipeline.addCallbackToPipeline(Hasher); @@ -262,4 +266,5 @@ codeview::CVTypeVisitor Visitor(Pipeline); consumeError(Visitor.visitTypeRecord(Obj.Record)); + Context.ActiveSerializer = nullptr; } Index: tools/llvm-pdbdump/YamlSerializationContext.h =================================================================== --- tools/llvm-pdbdump/YamlSerializationContext.h +++ tools/llvm-pdbdump/YamlSerializationContext.h @@ -12,10 +12,12 @@ #include "PdbYaml.h" #include "YamlTypeDumper.h" -#include "llvm/DebugInfo/CodeView/FieldListRecordBuilder.h" -#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h" +#include "llvm/Support/Allocator.h" namespace llvm { +namespace codeview { +class TypeSerializer; +} namespace yaml { class IO; } @@ -24,10 +26,11 @@ namespace yaml { struct SerializationContext { explicit SerializationContext(llvm::yaml::IO &IO, BumpPtrAllocator &Allocator) - : Dumper(IO, *this), TypeTableBuilder(Allocator) {} + : Dumper(IO, *this), Allocator(Allocator) {} + codeview::yaml::YamlTypeDumperCallbacks Dumper; - codeview::MemoryTypeTableBuilder TypeTableBuilder; - codeview::FieldListRecordBuilder FieldListBuilder; + BumpPtrAllocator &Allocator; + codeview::TypeSerializer *ActiveSerializer = nullptr; }; } } Index: tools/llvm-pdbdump/YamlTypeDumper.h =================================================================== --- tools/llvm-pdbdump/YamlTypeDumper.h +++ tools/llvm-pdbdump/YamlTypeDumper.h @@ -11,7 +11,6 @@ #define LLVM_TOOLS_LLVMPDBDUMP_YAMLTYPEDUMPER_H #include "llvm/DebugInfo/CodeView/CodeView.h" -#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" #include "llvm/Support/YAMLTraits.h" Index: tools/llvm-pdbdump/YamlTypeDumper.cpp =================================================================== --- tools/llvm-pdbdump/YamlTypeDumper.cpp +++ tools/llvm-pdbdump/YamlTypeDumper.cpp @@ -15,7 +15,7 @@ #include "llvm/DebugInfo/CodeView/EnumTables.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" -#include "llvm/DebugInfo/CodeView/TypeSerializationVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeSerializer.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" #include "llvm/DebugInfo/PDB/Raw/TpiHashing.h" @@ -540,15 +540,27 @@ // which will recurse back to the standard handler for top-level fields // (top-level and member fields all have the exact same Yaml syntax so use // the same parser). - // - // If we are not outputting, then the array contains no data starting out, - // and is instead populated from the sequence represented by the yaml -- - // again, using the same logic that we use for top-level records. FieldListRecordSplitter Splitter(FieldListRecords); CVTypeVisitor V(Splitter); consumeError(V.visitFieldListMemberStream(FieldList.Data)); + YamlIO.mapRequired("FieldList", FieldListRecords, Context); + } else { + // If we are not outputting, then the array contains no data starting out, + // and is instead populated from the sequence represented by the yaml -- + // again, using the same logic that we use for top-level records. + assert(Context.ActiveSerializer && "There is no active serializer!"); + codeview::TypeVisitorCallbackPipeline Pipeline; + pdb::TpiHashUpdater Hasher; + + // For Yaml to PDB, dump it (to fill out the record fields from the Yaml) + // then serialize those fields to bytes, then update their hashes. + Pipeline.addCallbackToPipeline(Context.Dumper); + Pipeline.addCallbackToPipeline(*Context.ActiveSerializer); + Pipeline.addCallbackToPipeline(Hasher); + + codeview::CVTypeVisitor Visitor(Pipeline); + YamlIO.mapRequired("FieldList", FieldListRecords, Visitor); } - YamlIO.mapRequired("FieldList", FieldListRecords, Context); } namespace llvm { @@ -558,31 +570,30 @@ pdb::yaml::SerializationContext> { static void mapping(IO &IO, pdb::yaml::PdbTpiFieldListRecord &Obj, pdb::yaml::SerializationContext &Context) { + assert(IO.outputting()); codeview::TypeVisitorCallbackPipeline Pipeline; msf::ByteStream Data(Obj.Record.Data); msf::StreamReader FieldReader(Data); codeview::FieldListDeserializer Deserializer(FieldReader); - codeview::TypeSerializationVisitor Serializer(Context.FieldListBuilder, - Context.TypeTableBuilder); - pdb::TpiHashUpdater Hasher; - if (IO.outputting()) { - // For PDB to Yaml, deserialize into a high level record type, then dump - // it. - Pipeline.addCallbackToPipeline(Deserializer); - Pipeline.addCallbackToPipeline(Context.Dumper); - } else { - // For Yaml to PDB, extract from the high level record type, then write it - // to bytes. - Pipeline.addCallbackToPipeline(Context.Dumper); - Pipeline.addCallbackToPipeline(Serializer); - Pipeline.addCallbackToPipeline(Hasher); - } + // For PDB to Yaml, deserialize into a high level record type, then dump + // it. + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Context.Dumper); codeview::CVTypeVisitor Visitor(Pipeline); consumeError(Visitor.visitMemberRecord(Obj.Record)); } }; + +template <> +struct MappingContextTraits { + static void mapping(IO &IO, pdb::yaml::PdbTpiFieldListRecord &Obj, + codeview::CVTypeVisitor &Visitor) { + consumeError(Visitor.visitMemberRecord(Obj.Record)); + } +}; } } Index: tools/llvm-readobj/COFFDumper.cpp =================================================================== --- tools/llvm-readobj/COFFDumper.cpp +++ tools/llvm-readobj/COFFDumper.cpp @@ -24,7 +24,6 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/DebugInfo/CodeView/CodeView.h" #include "llvm/DebugInfo/CodeView/Line.h" -#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/RecordSerialization.h" #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" #include "llvm/DebugInfo/CodeView/SymbolDumpDelegate.h" @@ -34,6 +33,7 @@ #include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" +#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" #include "llvm/DebugInfo/MSF/ByteStream.h" #include "llvm/Object/COFF.h" #include "llvm/Object/ObjectFile.h" @@ -79,8 +79,7 @@ void printCOFFBaseReloc() override; void printCOFFDebugDirectory() override; void printCodeViewDebugInfo() override; - void - mergeCodeViewTypes(llvm::codeview::MemoryTypeTableBuilder &CVTypes) override; + void mergeCodeViewTypes(llvm::codeview::TypeTableBuilder &CVTypes) override; void printStackMap() const override; private: void printSymbol(const SymbolRef &Sym); @@ -1063,7 +1062,7 @@ W.printHex(Label, getFileNameForFileOffset(FileOffset), FileOffset); } -void COFFDumper::mergeCodeViewTypes(MemoryTypeTableBuilder &CVTypes) { +void COFFDumper::mergeCodeViewTypes(TypeTableBuilder &CVTypes) { for (const SectionRef &S : Obj->sections()) { StringRef SectionName; error(S.getName(SectionName)); @@ -1545,12 +1544,12 @@ StackMapV2Parser(StackMapContentsArray)); } -void llvm::dumpCodeViewMergedTypes( - ScopedPrinter &Writer, llvm::codeview::MemoryTypeTableBuilder &CVTypes) { +void llvm::dumpCodeViewMergedTypes(ScopedPrinter &Writer, + llvm::codeview::TypeTableBuilder &CVTypes) { // Flatten it first, then run our dumper on it. ListScope S(Writer, "MergedTypeStream"); SmallString<0> Buf; - CVTypes.ForEachRecord([&](TypeIndex TI, StringRef Record) { + CVTypes.ForEachRecord([&](TypeIndex TI, ArrayRef Record) { Buf.append(Record.begin(), Record.end()); }); CVTypeDumper CVTD(&Writer, opts::CodeViewSubsectionBytes); Index: tools/llvm-readobj/ObjDumper.h =================================================================== --- tools/llvm-readobj/ObjDumper.h +++ tools/llvm-readobj/ObjDumper.h @@ -19,7 +19,7 @@ class ObjectFile; } namespace codeview { -class MemoryTypeTableBuilder; +class TypeTableBuilder; } class ScopedPrinter; @@ -65,8 +65,7 @@ virtual void printCOFFBaseReloc() { } virtual void printCOFFDebugDirectory() { } virtual void printCodeViewDebugInfo() { } - virtual void - mergeCodeViewTypes(llvm::codeview::MemoryTypeTableBuilder &CVTypes) {} + virtual void mergeCodeViewTypes(llvm::codeview::TypeTableBuilder &CVTypes) {} // Only implemented for MachO. virtual void printMachODataInCode() { } @@ -97,7 +96,7 @@ void dumpCOFFImportFile(const object::COFFImportFile *File); void dumpCodeViewMergedTypes(ScopedPrinter &Writer, - llvm::codeview::MemoryTypeTableBuilder &CVTypes); + llvm::codeview::TypeTableBuilder &CVTypes); } // namespace llvm Index: tools/llvm-readobj/llvm-readobj.cpp =================================================================== --- tools/llvm-readobj/llvm-readobj.cpp +++ tools/llvm-readobj/llvm-readobj.cpp @@ -22,7 +22,7 @@ #include "llvm-readobj.h" #include "Error.h" #include "ObjDumper.h" -#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFFImportFile.h" #include "llvm/Object/ELFObjectFile.h" @@ -332,14 +332,14 @@ } } namespace { -struct TypeTableBuilder { - TypeTableBuilder() : Allocator(), Builder(Allocator) {} +struct ReadObjTypeTableBuilder { + ReadObjTypeTableBuilder() : Allocator(), Builder(Allocator) {} llvm::BumpPtrAllocator Allocator; - llvm::codeview::MemoryTypeTableBuilder Builder; + llvm::codeview::TypeTableBuilder Builder; }; } -static TypeTableBuilder CVTypes; +static ReadObjTypeTableBuilder CVTypes; /// @brief Creates an format-specific object file dumper. static std::error_code createDumper(const ObjectFile *Obj,