Index: llvm/include/llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h @@ -0,0 +1,62 @@ +//===- ContinuationRecordBuilder.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_CONTINUATIONRECORDBUILDER_H +#define LLVM_DEBUGINFO_CODEVIEW_CONTINUATIONRECORDBUILDER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/RecordSerialization.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Error.h" +#include +#include +#include +#include + +namespace llvm { +namespace codeview { +enum class ContinuationRecordKind { FieldList, MethodOverloadList }; + +class ContinuationRecordBuilder { + SmallVector SegmentOffsets; + Optional Kind; + AppendingBinaryByteStream Buffer; + BinaryStreamWriter SegmentWriter; + TypeRecordMapping Mapping; + ArrayRef InjectedSegmentBytes; + + uint32_t getCurrentSegmentLength() const; + + void insertSegmentEnd(uint32_t Offset); + CVType createSegmentRecord(uint32_t OffBegin, uint32_t OffEnd, + Optional RefersTo); + +public: + ContinuationRecordBuilder(); + ~ContinuationRecordBuilder(); + + void begin(ContinuationRecordKind RecordKind); + + template void writeMemberType(RecordType &Record); + + std::vector end(TypeIndex Index); +}; +} // namespace codeview +} // namespace llvm + +#endif \ No newline at end of file Index: llvm/include/llvm/DebugInfo/CodeView/SimpleTypeSerializer.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/CodeView/SimpleTypeSerializer.h @@ -0,0 +1,50 @@ +//===- SimpleTypeSerializer.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_SIMPLETYPESERIALIZER_H +#define LLVM_DEBUGINFO_CODEVIEW_SIMPLETYPESERIALIZER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/RecordSerialization.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Error.h" +#include +#include +#include +#include + +namespace llvm { +namespace codeview { + +class SimpleTypeSerializer { + std::vector ScratchBuffer; + +public: + SimpleTypeSerializer(); + ~SimpleTypeSerializer(); + + template ArrayRef serialize(T &Record); + + // Don't allow serialization of field list records using this interface. + ArrayRef serialize(const FieldListRecord &Record) = delete; +}; + +} // end namespace codeview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_CODEVIEW_SIMPLETYPESERIALIZER_H Index: llvm/include/llvm/DebugInfo/CodeView/TypeSerializer.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/TypeSerializer.h +++ /dev/null @@ -1,159 +0,0 @@ -//===- 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/ADT/ArrayRef.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/DebugInfo/CodeView/CodeView.h" -#include "llvm/DebugInfo/CodeView/RecordSerialization.h" -#include "llvm/DebugInfo/CodeView/TypeIndex.h" -#include "llvm/DebugInfo/CodeView/TypeRecord.h" -#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h" -#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" -#include "llvm/Support/Allocator.h" -#include "llvm/Support/BinaryByteStream.h" -#include "llvm/Support/BinaryStreamWriter.h" -#include "llvm/Support/Error.h" -#include -#include -#include -#include - -namespace llvm { -namespace codeview { - -class TypeHasher; - -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; - } - }; - - using MutableRecordList = SmallVector, 2>; - - static constexpr uint8_t ContinuationLength = 8; - BumpPtrAllocator &RecordStorage; - RecordSegment CurrentSegment; - MutableRecordList FieldListSegments; - - Optional TypeKind; - Optional MemberKind; - std::vector RecordBuffer; - MutableBinaryByteStream Stream; - BinaryStreamWriter Writer; - TypeRecordMapping Mapping; - - /// Private type record hashing implementation details are handled here. - std::unique_ptr Hasher; - - /// Contains a list of all records indexed by TypeIndex.toArrayIndex(). - SmallVector, 2> SeenRecords; - - /// Temporary storage that we use to copy a record's data while re-writing - /// its type indices. - SmallVector RemapStorage; - - TypeIndex nextTypeIndex() const; - - bool isInFieldList() const; - MutableArrayRef getCurrentSubRecordData(); - MutableArrayRef getCurrentRecordData(); - Error writeRecordPrefix(TypeLeafKind Kind); - - Expected> - addPadding(MutableArrayRef Record); - -public: - explicit TypeSerializer(BumpPtrAllocator &Storage, bool Hash = true); - ~TypeSerializer() override; - - void reset(); - - BumpPtrAllocator &getAllocator() { return RecordStorage; } - - ArrayRef> records() const; - TypeIndex insertRecordBytes(ArrayRef &Record); - TypeIndex insertRecord(const RemappedType &Record); - Expected visitTypeEndGetIndex(CVType &Record); - - using TypeVisitorCallbacks::visitTypeBegin; - Error visitTypeBegin(CVType &Record) override; - Error visitTypeEnd(CVType &Record) override; - Error visitMemberBegin(CVMemberRecord &Record) override; - Error visitMemberEnd(CVMemberRecord &Record) override; - -#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/CodeViewTypes.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(); - } -}; - -} // end namespace codeview -} // end namespace llvm - -#endif // LLVM_DEBUGINFO_CODEVIEW_TYPESERIALIZER_H Index: llvm/include/llvm/DebugInfo/CodeView/TypeTableBuilder.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/TypeTableBuilder.h +++ llvm/include/llvm/DebugInfo/CodeView/TypeTableBuilder.h @@ -1,4 +1,5 @@ -//===- TypeTableBuilder.h ---------------------------------------*- C++ -*-===// +//===- TypeTableBuilder.h -----------------------------------------*- C++ +//-*-===// // // The LLVM Compiler Infrastructure // @@ -7,131 +8,82 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_DEBUGINFO_CODEVIEW_TYPETABLEBUILDER_H -#define LLVM_DEBUGINFO_CODEVIEW_TYPETABLEBUILDER_H +#ifndef LLVM_DEBUGINFO_CODEVIEW_TYPESERIALIZER_H +#define LLVM_DEBUGINFO_CODEVIEW_TYPESERIALIZER_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/RecordSerialization.h" +#include "llvm/DebugInfo/CodeView/SimpleTypeSerializer.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" -#include "llvm/DebugInfo/CodeView/TypeSerializer.h" +#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" #include "llvm/Support/Allocator.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryStreamWriter.h" #include "llvm/Support/Error.h" -#include #include #include -#include +#include +#include namespace llvm { namespace codeview { -class TypeTableBuilder { -private: - TypeIndex handleError(Error EC) const { - assert(false && "Couldn't write Type!"); - consumeError(std::move(EC)); - return TypeIndex(); - } +class ContinuationRecordBuilder; +class TypeHasher; - BumpPtrAllocator &Allocator; - TypeSerializer Serializer; +class TypeTableBuilder : public TypeVisitorCallbacks { -public: - explicit TypeTableBuilder(BumpPtrAllocator &Allocator, - bool WriteUnique = true) - : Allocator(Allocator), Serializer(Allocator, WriteUnique) {} - TypeTableBuilder(const TypeTableBuilder &) = delete; - TypeTableBuilder &operator=(const TypeTableBuilder &) = delete; + BumpPtrAllocator &RecordStorage; + SimpleTypeSerializer SimpleSerializer; + + /// Private type record hashing implementation details are handled here. + std::unique_ptr Hasher; - bool empty() const { return Serializer.records().empty(); } + /// Contains a list of all records indexed by TypeIndex.toArrayIndex(). + SmallVector, 2> SeenRecords; - BumpPtrAllocator &getAllocator() const { return Allocator; } + /// Temporary storage that we use to copy a record's data while re-writing + /// its type indices. + SmallVector RemapStorage; - template TypeIndex writeKnownType(T &Record) { - static_assert(!std::is_same::value, - "Can't serialize FieldList!"); +public: + explicit TypeTableBuilder(BumpPtrAllocator &Storage, bool Hash = true); + ~TypeTableBuilder() override; - 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)); + void reset(); - auto ExpectedIndex = Serializer.visitTypeEndGetIndex(Type); - if (!ExpectedIndex) - return handleError(ExpectedIndex.takeError()); + bool empty() const { return SeenRecords.empty(); } - return *ExpectedIndex; - } + TypeIndex nextTypeIndex() const; - TypeIndex writeSerializedRecord(ArrayRef Record) { - return Serializer.insertRecordBytes(Record); - } + BumpPtrAllocator &getAllocator() { return RecordStorage; } - TypeIndex writeSerializedRecord(const RemappedType &Record) { - return Serializer.insertRecord(Record); + ArrayRef> records() const; + TypeIndex insertRecordBytes(ArrayRef &Record); + TypeIndex insertRecord(const RemappedType &Record); + TypeIndex insertRecord(ContinuationRecordBuilder &Builder); + + template TypeIndex writeLeafType(T &Record) { + ArrayRef Data = SimpleSerializer.serialize(Record); + return insertRecordBytes(Data); } template void ForEachRecord(TFunc Func) { uint32_t Index = TypeIndex::FirstNonSimpleIndex; - for (auto Record : Serializer.records()) { + for (auto Record : SeenRecords) { Func(TypeIndex(Index), Record); ++Index; } } - - ArrayRef> records() const { return Serializer.records(); } -}; - -class FieldListRecordBuilder { - TypeTableBuilder &TypeTable; - BumpPtrAllocator Allocator; - TypeSerializer TempSerializer; - CVType Type; - -public: - explicit FieldListRecordBuilder(TypeTableBuilder &TypeTable) - : TypeTable(TypeTable), TempSerializer(Allocator, false) { - Type.Type = TypeLeafKind::LF_FIELDLIST; - } - - void begin() { - TempSerializer.reset(); - - 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(bool Write) { - TypeIndex Index; - if (auto EC = TempSerializer.visitTypeEnd(Type)) { - consumeError(std::move(EC)); - return TypeIndex(); - } - - if (Write) { - for (auto Record : TempSerializer.records()) - Index = TypeTable.writeSerializedRecord(Record); - } - - return Index; - } }; } // end namespace codeview } // end namespace llvm -#endif // LLVM_DEBUGINFO_CODEVIEW_TYPETABLEBUILDER_H +#endif // LLVM_DEBUGINFO_CODEVIEW_TYPESERIALIZER_H Index: llvm/include/llvm/ObjectYAML/CodeViewYAMLTypes.h =================================================================== --- llvm/include/llvm/ObjectYAML/CodeViewYAMLTypes.h +++ llvm/include/llvm/ObjectYAML/CodeViewYAMLTypes.h @@ -27,10 +27,8 @@ namespace llvm { namespace codeview { - class TypeTableBuilder; - -} // end namespace codeview +} namespace CodeViewYAML { @@ -48,8 +46,8 @@ struct LeafRecord { std::shared_ptr Leaf; - codeview::CVType toCodeViewRecord(BumpPtrAllocator &Allocator) const; - codeview::CVType toCodeViewRecord(codeview::TypeTableBuilder &TS) const; + codeview::CVType + toCodeViewRecord(codeview::TypeTableBuilder &Serializer) const; static Expected fromCodeViewRecord(codeview::CVType Type); }; Index: llvm/include/llvm/Support/BinaryByteStream.h =================================================================== --- llvm/include/llvm/Support/BinaryByteStream.h +++ llvm/include/llvm/Support/BinaryByteStream.h @@ -135,7 +135,7 @@ /// causing the underlying data to grow. This class owns the underlying data. class AppendingBinaryByteStream : public WritableBinaryStream { std::vector Data; - llvm::support::endianness Endian; + llvm::support::endianness Endian = llvm::support::little; public: AppendingBinaryByteStream() = default; @@ -155,6 +155,10 @@ return Error::success(); } + void insert(uint32_t Offset, ArrayRef Bytes) { + Data.insert(Data.begin() + Offset, Bytes.begin(), Bytes.end()); + } + Error readLongestContiguousChunk(uint32_t Offset, ArrayRef &Buffer) override { if (auto EC = checkOffsetForWrite(Offset, 1)) Index: llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -42,6 +42,7 @@ #include "llvm/Config/llvm-config.h" #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" #include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h" #include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" #include "llvm/DebugInfo/CodeView/Line.h" #include "llvm/DebugInfo/CodeView/SymbolRecord.h" @@ -278,7 +279,7 @@ // Build the fully qualified name of the scope. std::string ScopeName = getFullyQualifiedName(Scope); StringIdRecord SID(TypeIndex(), ScopeName); - auto TI = TypeTable.writeKnownType(SID); + auto TI = TypeTable.writeLeafType(SID); return recordTypeIndexForDINode(Scope, TI); } @@ -303,12 +304,12 @@ TypeIndex ClassType = getTypeIndex(Class); MemberFuncIdRecord MFuncId(ClassType, getMemberFunctionType(SP, Class), DisplayName); - TI = TypeTable.writeKnownType(MFuncId); + TI = TypeTable.writeLeafType(MFuncId); } else { // Otherwise, this must be a free function. TypeIndex ParentScope = getScopeIndex(Scope); FuncIdRecord FuncId(ParentScope, getTypeIndex(SP->getType()), DisplayName); - TI = TypeTable.writeKnownType(FuncId); + TI = TypeTable.writeLeafType(FuncId); } return recordTypeIndexForDINode(SP, TI); @@ -1304,7 +1305,7 @@ StringRef Name = (i == 0) ? Ty->getName() : ""; ArrayRecord AR(ElementTypeIndex, IndexType, ArraySize, Name); - ElementTypeIndex = TypeTable.writeKnownType(AR); + ElementTypeIndex = TypeTable.writeLeafType(AR); } return ElementTypeIndex; @@ -1437,7 +1438,7 @@ // do. PointerOptions PO = PointerOptions::None; PointerRecord PR(PointeeTI, PK, PM, PO, Ty->getSizeInBits() / 8); - return TypeTable.writeKnownType(PR); + return TypeTable.writeLeafType(PR); } static PointerToMemberRepresentation @@ -1488,7 +1489,7 @@ MemberPointerInfo MPI( ClassTI, translatePtrToMemberRep(SizeInBytes, IsPMF, Ty->getFlags())); PointerRecord PR(PointeeTI, PK, PM, PO, SizeInBytes, MPI); - return TypeTable.writeKnownType(PR); + return TypeTable.writeLeafType(PR); } /// Given a DWARF calling convention, get the CodeView equivalent. If we don't @@ -1527,7 +1528,7 @@ } TypeIndex ModifiedTI = getTypeIndex(BaseTy); ModifierRecord MR(ModifiedTI, Mods); - return TypeTable.writeKnownType(MR); + return TypeTable.writeLeafType(MR); } TypeIndex CodeViewDebug::lowerTypeFunction(const DISubroutineType *Ty) { @@ -1544,13 +1545,13 @@ } ArgListRecord ArgListRec(TypeRecordKind::ArgList, ArgTypeIndices); - TypeIndex ArgListIndex = TypeTable.writeKnownType(ArgListRec); + TypeIndex ArgListIndex = TypeTable.writeLeafType(ArgListRec); CallingConvention CC = dwarfCCToCodeView(Ty->getCC()); ProcedureRecord Procedure(ReturnTypeIndex, CC, FunctionOptions::None, ArgTypeIndices.size(), ArgListIndex); - return TypeTable.writeKnownType(Procedure); + return TypeTable.writeLeafType(Procedure); } TypeIndex CodeViewDebug::lowerTypeMemberFunction(const DISubroutineType *Ty, @@ -1578,7 +1579,7 @@ } ArgListRecord ArgListRec(TypeRecordKind::ArgList, ArgTypeIndices); - TypeIndex ArgListIndex = TypeTable.writeKnownType(ArgListRec); + TypeIndex ArgListIndex = TypeTable.writeLeafType(ArgListRec); CallingConvention CC = dwarfCCToCodeView(Ty->getCC()); @@ -1586,9 +1587,7 @@ MemberFunctionRecord MFR(ReturnTypeIndex, ClassType, ThisTypeIndex, CC, FunctionOptions::None, ArgTypeIndices.size(), ArgListIndex, ThisAdjustment); - TypeIndex TI = TypeTable.writeKnownType(MFR); - - return TI; + return TypeTable.writeLeafType(MFR); } TypeIndex CodeViewDebug::lowerTypeVFTableShape(const DIDerivedType *Ty) { @@ -1597,7 +1596,7 @@ SmallVector Slots(VSlotCount, VFTableSlotKind::Near); VFTableShapeRecord VFTSR(Slots); - return TypeTable.writeKnownType(VFTSR); + return TypeTable.writeLeafType(VFTSR); } static MemberAccess translateAccessFlags(unsigned RecordTag, unsigned Flags) { @@ -1688,9 +1687,8 @@ if (Ty->isForwardDecl()) { CO |= ClassOptions::ForwardReference; } else { - FieldListRecordBuilder FLRB(TypeTable); - - FLRB.begin(); + ContinuationRecordBuilder ContinuationBuilder; + ContinuationBuilder.begin(ContinuationRecordKind::FieldList); for (const DINode *Element : Ty->getElements()) { // We assume that the frontend provides all members in source declaration // order, which is what MSVC does. @@ -1698,18 +1696,18 @@ EnumeratorRecord ER(MemberAccess::Public, APSInt::getUnsigned(Enumerator->getValue()), Enumerator->getName()); - FLRB.writeMemberType(ER); + ContinuationBuilder.writeMemberType(ER); EnumeratorCount++; } } - FTI = FLRB.end(true); + FTI = TypeTable.insertRecord(ContinuationBuilder); } std::string FullName = getFullyQualifiedName(Ty); EnumRecord ER(EnumeratorCount, CO, FTI, FullName, Ty->getIdentifier(), getTypeIndex(Ty->getBaseType())); - return TypeTable.writeKnownType(ER); + return TypeTable.writeLeafType(ER); } //===----------------------------------------------------------------------===// @@ -1812,7 +1810,7 @@ std::string FullName = getFullyQualifiedName(Ty); ClassRecord CR(Kind, 0, CO, TypeIndex(), TypeIndex(), TypeIndex(), 0, FullName, Ty->getIdentifier()); - TypeIndex FwdDeclTI = TypeTable.writeKnownType(CR); + TypeIndex FwdDeclTI = TypeTable.writeLeafType(CR); if (!Ty->isForwardDecl()) DeferredCompleteTypes.push_back(Ty); return FwdDeclTI; @@ -1838,13 +1836,14 @@ ClassRecord CR(Kind, FieldCount, CO, FieldTI, TypeIndex(), VShapeTI, SizeInBytes, FullName, Ty->getIdentifier()); - TypeIndex ClassTI = TypeTable.writeKnownType(CR); + TypeIndex ClassTI = TypeTable.writeLeafType(CR); if (const auto *File = Ty->getFile()) { StringIdRecord SIDR(TypeIndex(0x0), getFullFilepath(File)); - TypeIndex SIDI = TypeTable.writeKnownType(SIDR); + TypeIndex SIDI = TypeTable.writeLeafType(SIDR); + UdtSourceLineRecord USLR(ClassTI, SIDI, Ty->getLine()); - TypeTable.writeKnownType(USLR); + TypeTable.writeLeafType(USLR); } addToUDTs(Ty); @@ -1857,7 +1856,7 @@ ClassOptions::ForwardReference | getCommonClassOptions(Ty); std::string FullName = getFullyQualifiedName(Ty); UnionRecord UR(0, CO, TypeIndex(), 0, FullName, Ty->getIdentifier()); - TypeIndex FwdDeclTI = TypeTable.writeKnownType(UR); + TypeIndex FwdDeclTI = TypeTable.writeLeafType(UR); if (!Ty->isForwardDecl()) DeferredCompleteTypes.push_back(Ty); return FwdDeclTI; @@ -1879,12 +1878,13 @@ UnionRecord UR(FieldCount, CO, FieldTI, SizeInBytes, FullName, Ty->getIdentifier()); - TypeIndex UnionTI = TypeTable.writeKnownType(UR); + TypeIndex UnionTI = TypeTable.writeLeafType(UR); StringIdRecord SIR(TypeIndex(0x0), getFullFilepath(Ty->getFile())); - TypeIndex SIRI = TypeTable.writeKnownType(SIR); + TypeIndex SIRI = TypeTable.writeLeafType(SIR); + UdtSourceLineRecord USLR(UnionTI, SIRI, Ty->getLine()); - TypeTable.writeKnownType(USLR); + TypeTable.writeLeafType(USLR); addToUDTs(Ty); @@ -1899,8 +1899,8 @@ // list record. unsigned MemberCount = 0; ClassInfo Info = collectClassInfo(Ty); - FieldListRecordBuilder FLBR(TypeTable); - FLBR.begin(); + ContinuationRecordBuilder ContinuationBuilder; + ContinuationBuilder.begin(ContinuationRecordKind::FieldList); // Create base classes. for (const DIDerivedType *I : Info.Inheritance) { @@ -1918,14 +1918,14 @@ getTypeIndex(I->getBaseType()), getVBPTypeIndex(), VBPtrOffset, VBTableIndex); - FLBR.writeMemberType(VBCR); + ContinuationBuilder.writeMemberType(VBCR); } else { assert(I->getOffsetInBits() % 8 == 0 && "bases must be on byte boundaries"); BaseClassRecord BCR(translateAccessFlags(Ty->getTag(), I->getFlags()), getTypeIndex(I->getBaseType()), I->getOffsetInBits() / 8); - FLBR.writeMemberType(BCR); + ContinuationBuilder.writeMemberType(BCR); } } @@ -1939,7 +1939,7 @@ if (Member->isStaticMember()) { StaticDataMemberRecord SDMR(Access, MemberBaseType, MemberName); - FLBR.writeMemberType(SDMR); + ContinuationBuilder.writeMemberType(SDMR); MemberCount++; continue; } @@ -1948,7 +1948,7 @@ if ((Member->getFlags() & DINode::FlagArtificial) && Member->getName().startswith("_vptr$")) { VFPtrRecord VFPR(getTypeIndex(Member->getBaseType())); - FLBR.writeMemberType(VFPR); + ContinuationBuilder.writeMemberType(VFPR); MemberCount++; continue; } @@ -1965,12 +1965,12 @@ StartBitOffset -= MemberOffsetInBits; BitFieldRecord BFR(MemberBaseType, Member->getSizeInBits(), StartBitOffset); - MemberBaseType = TypeTable.writeKnownType(BFR); + MemberBaseType = TypeTable.writeLeafType(BFR); } uint64_t MemberOffsetInBytes = MemberOffsetInBits / 8; DataMemberRecord DMR(Access, MemberBaseType, MemberOffsetInBytes, MemberName); - FLBR.writeMemberType(DMR); + ContinuationBuilder.writeMemberType(DMR); MemberCount++; } @@ -1995,23 +1995,26 @@ } assert(!Methods.empty() && "Empty methods map entry"); if (Methods.size() == 1) - FLBR.writeMemberType(Methods[0]); + ContinuationBuilder.writeMemberType(Methods[0]); else { + // FIXME: Make this use its own ContinuationBuilder so that + // MethodOverloadList can be split correctly. MethodOverloadListRecord MOLR(Methods); - TypeIndex MethodList = TypeTable.writeKnownType(MOLR); + TypeIndex MethodList = TypeTable.writeLeafType(MOLR); + OverloadedMethodRecord OMR(Methods.size(), MethodList, Name); - FLBR.writeMemberType(OMR); + ContinuationBuilder.writeMemberType(OMR); } } // Create nested classes. for (const DIType *Nested : Info.NestedTypes) { NestedTypeRecord R(getTypeIndex(DITypeRef(Nested)), Nested->getName()); - FLBR.writeMemberType(R); + ContinuationBuilder.writeMemberType(R); MemberCount++; } - TypeIndex FieldTI = FLBR.end(true); + TypeIndex FieldTI = TypeTable.insertRecord(ContinuationBuilder); return std::make_tuple(FieldTI, Info.VShapeTI, MemberCount, !Info.NestedTypes.empty()); } @@ -2020,15 +2023,14 @@ if (!VBPType.getIndex()) { // Make a 'const int *' type. ModifierRecord MR(TypeIndex::Int32(), ModifierOptions::Const); - TypeIndex ModifiedTI = TypeTable.writeKnownType(MR); + TypeIndex ModifiedTI = TypeTable.writeLeafType(MR); PointerKind PK = getPointerSizeInBytes() == 8 ? PointerKind::Near64 : PointerKind::Near32; PointerMode PM = PointerMode::Pointer; PointerOptions PO = PointerOptions::None; PointerRecord PR(ModifiedTI, PK, PM, PO, getPointerSizeInBytes()); - - VBPType = TypeTable.writeKnownType(PR); + VBPType = TypeTable.writeLeafType(PR); } return VBPType; @@ -2061,7 +2063,7 @@ : PointerKind::Near32, PointerMode::LValueReference, PointerOptions::None, Ty->getSizeInBits() / 8); - return TypeTable.writeKnownType(PR); + return TypeTable.writeLeafType(PR); } TypeIndex CodeViewDebug::getCompleteTypeIndex(DITypeRef TypeRef) { Index: llvm/lib/DebugInfo/CodeView/CMakeLists.txt =================================================================== --- llvm/lib/DebugInfo/CodeView/CMakeLists.txt +++ llvm/lib/DebugInfo/CodeView/CMakeLists.txt @@ -1,6 +1,7 @@ add_llvm_library(LLVMDebugInfoCodeView CodeViewError.cpp CodeViewRecordIO.cpp + ContinuationRecordBuilder.cpp CVSymbolVisitor.cpp CVTypeVisitor.cpp DebugChecksumsSubsection.cpp @@ -21,6 +22,7 @@ Line.cpp RecordName.cpp RecordSerialization.cpp + SimpleTypeSerializer.cpp StringsAndChecksums.cpp SymbolRecordMapping.cpp SymbolDumper.cpp @@ -29,7 +31,7 @@ TypeIndex.cpp TypeIndexDiscovery.cpp TypeRecordMapping.cpp - TypeSerializer.cpp + TypeTableBuilder.cpp TypeStreamMerger.cpp TypeTableCollection.cpp Index: llvm/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp @@ -0,0 +1,257 @@ +#include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h" + +using namespace llvm; +using namespace llvm::codeview; + +namespace { +struct ContinuationRecord { + ulittle16_t Kind{uint16_t(TypeLeafKind::LF_INDEX)}; + ulittle16_t Size{0}; + ulittle32_t IndexRef{0xB0C0B0C0}; +}; + +struct SegmentInjection { + SegmentInjection(TypeLeafKind Kind) { Prefix.RecordKind = Kind; } + + ContinuationRecord Cont; + RecordPrefix Prefix; +}; +} // namespace + +static void addPadding(BinaryStreamWriter &Writer) { + uint32_t Align = Writer.getOffset() % 4; + if (Align == 0) + return; + + int PaddingBytes = 4 - Align; + while (PaddingBytes > 0) { + uint8_t Pad = static_cast(LF_PAD0 + PaddingBytes); + cantFail(Writer.writeInteger(Pad)); + --PaddingBytes; + } +} + +static SegmentInjection InjectFieldList(TypeLeafKind::LF_FIELDLIST); +static SegmentInjection InjectMethodOverloadList(TypeLeafKind::LF_METHODLIST); + +static constexpr uint32_t ContinuationLength = sizeof(ContinuationRecord); +static constexpr uint32_t MaxSegmentLength = + MaxRecordLength - ContinuationLength; + +static inline TypeLeafKind getTypeLeafKind(ContinuationRecordKind CK) { + return (CK == ContinuationRecordKind::FieldList) ? LF_FIELDLIST + : LF_METHODLIST; +} + +ContinuationRecordBuilder::ContinuationRecordBuilder() + : SegmentWriter(Buffer), Mapping(SegmentWriter) {} + +ContinuationRecordBuilder::~ContinuationRecordBuilder() {} + +void ContinuationRecordBuilder::begin(ContinuationRecordKind RecordKind) { + assert(!Kind.hasValue()); + Kind = RecordKind; + Buffer.clear(); + SegmentWriter.setOffset(0); + SegmentOffsets.clear(); + SegmentOffsets.push_back(0); + assert(SegmentWriter.getOffset() == 0); + assert(SegmentWriter.getLength() == 0); + + const SegmentInjection *FLI = + (RecordKind == ContinuationRecordKind::FieldList) + ? &InjectFieldList + : &InjectMethodOverloadList; + const uint8_t *FLIB = reinterpret_cast(FLI); + InjectedSegmentBytes = + ArrayRef(FLIB, FLIB + sizeof(SegmentInjection)); + + CVType Type; + Type.Type = getTypeLeafKind(RecordKind); + cantFail(Mapping.visitTypeBegin(Type)); + + // Seed the first trecord with an appropriate record prefix. + RecordPrefix Prefix; + Prefix.RecordLen = 0; + Prefix.RecordKind = Type.Type; + cantFail(SegmentWriter.writeObject(Prefix)); +} + +template +void ContinuationRecordBuilder::writeMemberType(RecordType &Record) { + assert(Kind.hasValue()); + + uint32_t OriginalOffset = SegmentWriter.getOffset(); + CVMemberRecord CVMR; + CVMR.Kind = static_cast(Record.getKind()); + + // Member Records aren't length-prefixed, they only have a 2-byte TypeLeafKind + // at the beginning. + cantFail(SegmentWriter.writeEnum(CVMR.Kind)); + + // Let the Mapping handle the rest. + cantFail(Mapping.visitMemberBegin(CVMR)); + cantFail(Mapping.visitKnownMember(CVMR, Record)); + cantFail(Mapping.visitMemberEnd(CVMR)); + + // Make sure it's padded to 4 bytes. + addPadding(SegmentWriter); + assert(getCurrentSegmentLength() % 4 == 0); + + // The maximum length of a single segment is 64KB minus the size to insert a + // continuation. So if we are over that, inject a continuation between the + // previous member and the member that was just written, then end the previous + // segment after the continuation and begin a new one with the just-written + // member. + if (getCurrentSegmentLength() > MaxSegmentLength) { + // We need to inject some bytes before the member we just wrote but after + // the + // previous member. Save off the length of the member we just wrote so that + // we can do some sanity checking on it. + uint32_t MemberLength = SegmentWriter.getOffset() - OriginalOffset; + insertSegmentEnd(OriginalOffset); + // Since this member now becomes a new top-level record, it should have + // gotten + // a RecordPrefix injected, and that RecordPrefix + the member we just wrote + // should now constitute the entirety of the current "new" segment. + assert(getCurrentSegmentLength() == MemberLength + sizeof(RecordPrefix)); + } + + assert(getCurrentSegmentLength() % 4 == 0); + assert(getCurrentSegmentLength() <= MaxSegmentLength); +} + +uint32_t ContinuationRecordBuilder::getCurrentSegmentLength() const { + return SegmentWriter.getOffset() - SegmentOffsets.back(); +} + +void ContinuationRecordBuilder::insertSegmentEnd(uint32_t Offset) { + uint32_t SegmentBegin = SegmentOffsets.back(); + assert(Offset > SegmentBegin); + assert(Offset - SegmentBegin <= MaxSegmentLength); + + // We need to make space for the continuation record. For now we can't fill + // out the length or the TypeIndex of the back-reference, but we need the + // space to at least be there. + Buffer.insert(Offset, InjectedSegmentBytes); + + uint32_t NewSegmentBegin = Offset + ContinuationLength; + uint32_t SegmentLength = NewSegmentBegin - SegmentOffsets.back(); + + assert(SegmentLength % 4 == 0); + assert(SegmentLength <= MaxRecordLength); + SegmentOffsets.push_back(NewSegmentBegin); + + // Seek to the end so that we can keep writing against the new segment. + SegmentWriter.setOffset(SegmentWriter.getLength()); + assert(SegmentWriter.bytesRemaining() == 0); +} + +CVType ContinuationRecordBuilder::createSegmentRecord( + uint32_t OffBegin, uint32_t OffEnd, Optional RefersTo) { + assert(OffEnd - OffBegin <= USHRT_MAX); + + MutableArrayRef Data = Buffer.data(); + Data = Data.slice(OffBegin, OffEnd - OffBegin); + + CVType Type; + Type.Type = getTypeLeafKind(*Kind); + Type.RecordData = Data; + + // Write the length to the RecordPrefix, making sure it does not include + // sizeof(RecordPrefix.Length) + RecordPrefix *Prefix = reinterpret_cast(Data.data()); + assert(Prefix->RecordKind == Type.Type); + Prefix->RecordLen = Data.size() - sizeof(RecordPrefix::RecordLen); + + if (RefersTo.hasValue()) { + auto Continuation = Data.take_back(ContinuationLength); + ContinuationRecord *CR = + reinterpret_cast(Continuation.data()); + assert(CR->Kind == TypeLeafKind::LF_INDEX); + assert(CR->IndexRef == 0xB0C0B0C0); + CR->IndexRef = RefersTo->getIndex(); + } + + return Type; +} + +std::vector ContinuationRecordBuilder::end(TypeIndex Index) { + CVType Type; + Type.Type = getTypeLeafKind(*Kind); + cantFail(Mapping.visitTypeEnd(Type)); + + // We're now done, and we have a series of segments each beginning at an + // offset specified in the SegmentOffsets array. We now need to iterate + // over each segment and post-process them in the following two ways: + // 1) Each top-level record has a RecordPrefix whose type is either + // LF_FIELDLIST or LF_METHODLIST, but the Length field is still 0. + // Those should all be set to the correct length now. + // 2) Each continuation record has an IndexRef field which we set to the + // magic value 0xB0C0B0C0. Now that the caller has told us the TypeIndex + // they want this sequence to start from, we can go through and update + // each one. + // + // Logically, the sequence of records we've built up looks like this: + // + // SegmentOffsets[0]: (Initially: uninitialized) + // SegmentOffsets[0]+2: LF_FIELDLIST + // SegmentOffsets[0]+4: Member[0] + // SegmentOffsets[0]+?: ... + // SegmentOffsets[0]+?: Member[4] + // SegmentOffsets[1]-8: LF_INDEX + // SegmentOffsets[1]-6: 0 + // SegmentOffsets[1]-4: (Initially: 0xB0C0B0C0) + // + // SegmentOffsets[1]: (Initially: uninitialized) + // SegmentOffsets[1]+2: LF_FIELDLIST + // SegmentOffsets[1]+4: Member[0] + // SegmentOffsets[1]+?: ... + // SegmentOffsets[1]+?: Member[s] + // SegmentOffsets[2]-8: LF_INDEX + // SegmentOffsets[2]-6: 0 + // SegmentOffsets[2]-4: (Initially: 0xB0C0B0C0) + // + // ... + // + // SegmentOffsets[N]: (Initially: uninitialized) + // SegmentOffsets[N]+2: LF_FIELDLIST + // SegmentOffsets[N]+4: Member[0] + // SegmentOffsets[N]+?: ... + // SegmentOffsets[N]+?: Member[t] + // + // And this is the way we have laid them out in the serialization buffer. But + // we cannot actually commit them to the underlying stream this way, due to + // the topological sorting requirement of a type stream (specifically, + // TypeIndex references can only point backwards, not forwards). So the + // sequence that we return to the caller contains the records in reverse + // order, which is the proper order for committing the serialized records. + + std::vector Types; + Types.reserve(SegmentOffsets.size()); + + auto SO = makeArrayRef(SegmentOffsets); + + uint32_t End = SegmentWriter.getOffset(); + + Optional RefersTo; + for (uint32_t Offset : reverse(SO)) { + Types.push_back(createSegmentRecord(Offset, End, RefersTo)); + + End = Offset; + RefersTo = Index++; + } + + Kind.reset(); + return Types; +} + +// Explicitly instantiate the member function for each known type so that we can +// implement this in the cpp file. +#define TYPE_RECORD(EnumName, EnumVal, Name) +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#define MEMBER_RECORD(EnumName, EnumVal, Name) \ + template void llvm::codeview::ContinuationRecordBuilder::writeMemberType( \ + Name##Record &Record); +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" Index: llvm/lib/DebugInfo/CodeView/SimpleTypeSerializer.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/CodeView/SimpleTypeSerializer.cpp @@ -0,0 +1,62 @@ +#include "llvm/DebugInfo/CodeView/SimpleTypeSerializer.h" + +using namespace llvm; +using namespace llvm::codeview; + +static void writeRecordPrefix(BinaryStreamWriter &Writer, TypeLeafKind Kind) { + RecordPrefix Prefix; + Prefix.RecordKind = Kind; + Prefix.RecordLen = 0; + cantFail(Writer.writeObject(Prefix)); +} + +static void addPadding(BinaryStreamWriter &Writer) { + uint32_t Align = Writer.getOffset() % 4; + if (Align == 0) + return; + + int PaddingBytes = 4 - Align; + while (PaddingBytes > 0) { + uint8_t Pad = static_cast(LF_PAD0 + PaddingBytes); + cantFail(Writer.writeInteger(Pad)); + --PaddingBytes; + } +} + +SimpleTypeSerializer::SimpleTypeSerializer() : ScratchBuffer(MaxRecordLength) {} + +SimpleTypeSerializer::~SimpleTypeSerializer() {} + +template +ArrayRef SimpleTypeSerializer::serialize(T &Record) { + BinaryStreamWriter Writer(ScratchBuffer, support::little); + TypeRecordMapping Mapping(Writer); + + CVType CVT; + CVT.Type = static_cast(Record.getKind()); + + writeRecordPrefix(Writer, CVT.Type); + + cantFail(Mapping.visitTypeBegin(CVT)); + cantFail(Mapping.visitKnownRecord(CVT, Record)); + cantFail(Mapping.visitTypeEnd(CVT)); + + addPadding(Writer); + + RecordPrefix *Prefix = reinterpret_cast(ScratchBuffer.data()); + + Prefix->RecordKind = CVT.kind(); + Prefix->RecordLen = Writer.getOffset() - sizeof(uint16_t); + + return {ScratchBuffer.data(), Writer.getOffset()}; +} + +// Explicitly instantiate the member function for each known type so that we can +// implement this in the cpp file. +#define TYPE_RECORD(EnumName, EnumVal, Name) \ + template ArrayRef llvm::codeview::SimpleTypeSerializer::serialize( \ + Name##Record &Record); +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#define MEMBER_RECORD(EnumName, EnumVal, Name) +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" Index: llvm/lib/DebugInfo/CodeView/TypeRecordMapping.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/TypeRecordMapping.cpp +++ llvm/lib/DebugInfo/CodeView/TypeRecordMapping.cpp @@ -426,7 +426,8 @@ Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, OneMethodRecord &Record) { - MapOneMethodRecord Mapper(false); + const bool IsFromOverloadList = (TypeKind == LF_METHODLIST); + MapOneMethodRecord Mapper(IsFromOverloadList); return Mapper(IO, Record); } Index: llvm/lib/DebugInfo/CodeView/TypeSerializer.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/TypeSerializer.cpp +++ /dev/null @@ -1,389 +0,0 @@ -//===- TypeSerialzier.cpp -------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "llvm/DebugInfo/CodeView/TypeSerializer.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/DebugInfo/CodeView/CodeView.h" -#include "llvm/DebugInfo/CodeView/RecordSerialization.h" -#include "llvm/DebugInfo/CodeView/TypeIndex.h" -#include "llvm/Support/Allocator.h" -#include "llvm/Support/BinaryByteStream.h" -#include "llvm/Support/BinaryStreamWriter.h" -#include "llvm/Support/Endian.h" -#include "llvm/Support/Error.h" -#include -#include -#include -#include - -using namespace llvm; -using namespace llvm::codeview; - -namespace { - -struct HashedType { - uint64_t Hash; - const uint8_t *Data; - unsigned Size; // FIXME: Go to uint16_t? - TypeIndex Index; -}; - -/// Wrapper around a poitner to a HashedType. Hash and equality operations are -/// based on data in the pointee. -struct HashedTypePtr { - HashedTypePtr() = default; - HashedTypePtr(HashedType *Ptr) : Ptr(Ptr) {} - - HashedType *Ptr = nullptr; -}; - -} // end anonymous namespace - -namespace llvm { - -template <> struct DenseMapInfo { - static inline HashedTypePtr getEmptyKey() { return HashedTypePtr(nullptr); } - - static inline HashedTypePtr getTombstoneKey() { - return HashedTypePtr(reinterpret_cast(1)); - } - - static unsigned getHashValue(HashedTypePtr Val) { - assert(Val.Ptr != getEmptyKey().Ptr && Val.Ptr != getTombstoneKey().Ptr); - return Val.Ptr->Hash; - } - - static bool isEqual(HashedTypePtr LHSP, HashedTypePtr RHSP) { - HashedType *LHS = LHSP.Ptr; - HashedType *RHS = RHSP.Ptr; - if (RHS == getEmptyKey().Ptr || RHS == getTombstoneKey().Ptr) - return LHS == RHS; - if (LHS->Hash != RHS->Hash || LHS->Size != RHS->Size) - return false; - return ::memcmp(LHS->Data, RHS->Data, LHS->Size) == 0; - } -}; - -} // end namespace llvm - -/// Private implementation so that we don't leak our DenseMap instantiations to -/// users. -class llvm::codeview::TypeHasher { -private: - /// Storage for type record provided by the caller. Records will outlive the - /// hasher object, so they should be allocated here. - BumpPtrAllocator &RecordStorage; - - /// Storage for hash keys. These only need to live as long as the hashing - /// operation. - BumpPtrAllocator KeyStorage; - - /// Hash table. We really want a DenseMap, TypeIndex> here, - /// but DenseMap is inefficient when the keys are long (like type records) - /// because it recomputes the hash value of every key when it grows. This - /// value type stores the hash out of line in KeyStorage, so that table - /// entries are small and easy to rehash. - DenseSet HashedRecords; - -public: - TypeHasher(BumpPtrAllocator &RecordStorage) : RecordStorage(RecordStorage) {} - - void reset() { HashedRecords.clear(); } - - /// Takes the bytes of type record, inserts them into the hash table, saves - /// them, and returns a pointer to an identical stable type record along with - /// its type index in the destination stream. - TypeIndex getOrCreateRecord(ArrayRef &Record, TypeIndex TI); -}; - -TypeIndex TypeHasher::getOrCreateRecord(ArrayRef &Record, - TypeIndex TI) { - assert(Record.size() < UINT32_MAX && "Record too big"); - assert(Record.size() % 4 == 0 && "Record is not aligned to 4 bytes!"); - - // Compute the hash up front so we can store it in the key. - HashedType TempHashedType = {hash_value(Record), Record.data(), - unsigned(Record.size()), TI}; - auto Result = HashedRecords.insert(HashedTypePtr(&TempHashedType)); - HashedType *&Hashed = Result.first->Ptr; - - if (Result.second) { - // This was a new type record. We need stable storage for both the key and - // the record. The record should outlive the hashing operation. - Hashed = KeyStorage.Allocate(); - *Hashed = TempHashedType; - - uint8_t *Stable = RecordStorage.Allocate(Record.size()); - memcpy(Stable, Record.data(), Record.size()); - Hashed->Data = Stable; - assert(Hashed->Size == Record.size()); - } - - // Update the caller's copy of Record to point a stable copy. - Record = ArrayRef(Hashed->Data, Hashed->Size); - return Hashed->Index; -} - -TypeIndex TypeSerializer::nextTypeIndex() const { - return TypeIndex::fromArrayIndex(SeenRecords.size()); -} - -bool TypeSerializer::isInFieldList() const { - return TypeKind.hasValue() && *TypeKind == TypeLeafKind::LF_FIELDLIST; -} - -MutableArrayRef TypeSerializer::getCurrentSubRecordData() { - assert(isInFieldList()); - return getCurrentRecordData().drop_front(CurrentSegment.length()); -} - -MutableArrayRef TypeSerializer::getCurrentRecordData() { - return MutableArrayRef(RecordBuffer).take_front(Writer.getOffset()); -} - -Error TypeSerializer::writeRecordPrefix(TypeLeafKind Kind) { - RecordPrefix Prefix; - Prefix.RecordKind = Kind; - Prefix.RecordLen = 0; - if (auto EC = Writer.writeObject(Prefix)) - return EC; - return Error::success(); -} - -Expected> -TypeSerializer::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); -} - -TypeSerializer::TypeSerializer(BumpPtrAllocator &Storage, bool Hash) - : RecordStorage(Storage), RecordBuffer(MaxRecordLength * 2), - Stream(RecordBuffer, support::little), Writer(Stream), - Mapping(Writer) { - // RecordBuffer needs to be able to hold enough data so that if we are 1 - // byte short of MaxRecordLen, and then we try to write MaxRecordLen bytes, - // we won't overflow. - if (Hash) - Hasher = llvm::make_unique(Storage); -} - -TypeSerializer::~TypeSerializer() = default; - -ArrayRef> TypeSerializer::records() const { - return SeenRecords; -} - -void TypeSerializer::reset() { - if (Hasher) - Hasher->reset(); - Writer.setOffset(0); - CurrentSegment = RecordSegment(); - FieldListSegments.clear(); - TypeKind.reset(); - MemberKind.reset(); - SeenRecords.clear(); -} - -TypeIndex TypeSerializer::insertRecordBytes(ArrayRef &Record) { - assert(!TypeKind.hasValue() && "Already in a type mapping!"); - assert(Writer.getOffset() == 0 && "Stream has data already!"); - - if (Hasher) { - TypeIndex ActualTI = Hasher->getOrCreateRecord(Record, nextTypeIndex()); - if (nextTypeIndex() == ActualTI) - SeenRecords.push_back(Record); - return ActualTI; - } - - TypeIndex NewTI = nextTypeIndex(); - uint8_t *Stable = RecordStorage.Allocate(Record.size()); - memcpy(Stable, Record.data(), Record.size()); - Record = ArrayRef(Stable, Record.size()); - SeenRecords.push_back(Record); - return NewTI; -} - -TypeIndex TypeSerializer::insertRecord(const RemappedType &Record) { - assert(!TypeKind.hasValue() && "Already in a type mapping!"); - assert(Writer.getOffset() == 0 && "Stream has data already!"); - - TypeIndex TI; - ArrayRef OriginalData = Record.OriginalRecord.RecordData; - if (Record.Mappings.empty()) { - // This record did not remap any type indices. Just write it. - return insertRecordBytes(OriginalData); - } - - // At least one type index was remapped. Before we can hash it we have to - // copy the full record bytes, re-write each type index, then hash the copy. - // We do this in temporary storage since only the DenseMap can decide whether - // this record already exists, and if it does we don't want the memory to - // stick around. - RemapStorage.resize(OriginalData.size()); - ::memcpy(&RemapStorage[0], OriginalData.data(), OriginalData.size()); - uint8_t *ContentBegin = RemapStorage.data() + sizeof(RecordPrefix); - for (const auto &M : Record.Mappings) { - // First 4 bytes of every record are the record prefix, but the mapping - // offset is relative to the content which starts after. - *(TypeIndex *)(ContentBegin + M.first) = M.second; - } - auto RemapRef = makeArrayRef(RemapStorage); - return insertRecordBytes(RemapRef); -} - -Error TypeSerializer::visitTypeBegin(CVType &Record) { - assert(!TypeKind.hasValue() && "Already in a type mapping!"); - assert(Writer.getOffset() == 0 && "Stream has data already!"); - - if (auto EC = writeRecordPrefix(Record.kind())) - return EC; - - TypeKind = Record.kind(); - if (auto EC = Mapping.visitTypeBegin(Record)) - return EC; - - return Error::success(); -} - -Expected TypeSerializer::visitTypeEndGetIndex(CVType &Record) { - assert(TypeKind.hasValue() && "Not in a type mapping!"); - if (auto EC = Mapping.visitTypeEnd(Record)) - return std::move(EC); - - // Update the record's length and fill out the CVType members to point to - // the stable memory holding the record's data. - auto ThisRecordData = getCurrentRecordData(); - auto ExpectedData = addPadding(ThisRecordData); - if (!ExpectedData) - return ExpectedData.takeError(); - ThisRecordData = *ExpectedData; - - RecordPrefix *Prefix = - reinterpret_cast(ThisRecordData.data()); - Prefix->RecordLen = ThisRecordData.size() - sizeof(uint16_t); - - Record.Type = *TypeKind; - Record.RecordData = ThisRecordData; - - // insertRecordBytes assumes we're not in a mapping, so do this first. - TypeKind.reset(); - Writer.setOffset(0); - - TypeIndex InsertedTypeIndex = insertRecordBytes(Record.RecordData); - - // Write out each additional segment in reverse order, and update each - // record's continuation index to point to the previous one. - for (auto X : reverse(FieldListSegments)) { - auto CIBytes = X.take_back(sizeof(uint32_t)); - support::ulittle32_t *CI = - reinterpret_cast(CIBytes.data()); - assert(*CI == 0xB0C0B0C0 && "Invalid TypeIndex placeholder"); - *CI = InsertedTypeIndex.getIndex(); - InsertedTypeIndex = insertRecordBytes(X); - } - - FieldListSegments.clear(); - CurrentSegment.SubRecords.clear(); - - return InsertedTypeIndex; -} - -Error TypeSerializer::visitTypeEnd(CVType &Record) { - auto ExpectedIndex = visitTypeEndGetIndex(Record); - if (!ExpectedIndex) - return ExpectedIndex.takeError(); - return Error::success(); -} - -Error TypeSerializer::visitMemberBegin(CVMemberRecord &Record) { - assert(isInFieldList() && "Not in a field list!"); - assert(!MemberKind.hasValue() && "Already in a member record!"); - MemberKind = Record.Kind; - - if (auto EC = Mapping.visitMemberBegin(Record)) - return EC; - - return Error::success(); -} - -Error TypeSerializer::visitMemberEnd(CVMemberRecord &Record) { - if (auto EC = Mapping.visitMemberEnd(Record)) - return EC; - - // Check if this subrecord makes the current segment not fit in 64K minus - // the space for a continuation record (8 bytes). If the segment does not - // fit, insert a continuation record. - if (Writer.getOffset() > MaxRecordLength - ContinuationLength) { - MutableArrayRef 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); - MutableBinaryByteStream CS(SavedSegment, support::little); - BinaryStreamWriter CW(CS); - if (auto EC = CW.writeBytes(CopyData)) - return EC; - if (auto EC = CW.writeEnum(TypeLeafKind::LF_INDEX)) - return EC; - if (auto EC = CW.writeInteger(0)) - return EC; - if (auto EC = CW.writeInteger(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(); -} Index: llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp +++ llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp @@ -100,7 +100,7 @@ bool RemapSuccess) { TypeIndex DestIdx = Untranslated; if (RemapSuccess) - DestIdx = Dest.writeSerializedRecord(Record); + DestIdx = Dest.insertRecord(Record); addMapping(DestIdx); return Error::success(); } Index: llvm/lib/DebugInfo/CodeView/TypeTableBuilder.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/CodeView/TypeTableBuilder.cpp @@ -0,0 +1,206 @@ +//===- TypeSerialzier.cpp -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h" +#include "llvm/DebugInfo/CodeView/RecordSerialization.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include +#include +#include +#include + +using namespace llvm; +using namespace llvm::codeview; + +namespace { + +struct HashedType { + uint64_t Hash; + const uint8_t *Data; + unsigned Size; // FIXME: Go to uint16_t? + TypeIndex Index; +}; + +/// Wrapper around a poitner to a HashedType. Hash and equality operations are +/// based on data in the pointee. +struct HashedTypePtr { + HashedTypePtr() = default; + HashedTypePtr(HashedType *Ptr) : Ptr(Ptr) {} + + HashedType *Ptr = nullptr; +}; + +} // end anonymous namespace + +namespace llvm { + +template <> struct DenseMapInfo { + static inline HashedTypePtr getEmptyKey() { return HashedTypePtr(nullptr); } + + static inline HashedTypePtr getTombstoneKey() { + return HashedTypePtr(reinterpret_cast(1)); + } + + static unsigned getHashValue(HashedTypePtr Val) { + assert(Val.Ptr != getEmptyKey().Ptr && Val.Ptr != getTombstoneKey().Ptr); + return Val.Ptr->Hash; + } + + static bool isEqual(HashedTypePtr LHSP, HashedTypePtr RHSP) { + HashedType *LHS = LHSP.Ptr; + HashedType *RHS = RHSP.Ptr; + if (RHS == getEmptyKey().Ptr || RHS == getTombstoneKey().Ptr) + return LHS == RHS; + if (LHS->Hash != RHS->Hash || LHS->Size != RHS->Size) + return false; + return ::memcmp(LHS->Data, RHS->Data, LHS->Size) == 0; + } +}; + +} // end namespace llvm + +/// Private implementation so that we don't leak our DenseMap instantiations to +/// users. +class llvm::codeview::TypeHasher { +private: + /// Storage for type record provided by the caller. Records will outlive the + /// hasher object, so they should be allocated here. + BumpPtrAllocator &RecordStorage; + + /// Storage for hash keys. These only need to live as long as the hashing + /// operation. + BumpPtrAllocator KeyStorage; + + /// Hash table. We really want a DenseMap, TypeIndex> here, + /// but DenseMap is inefficient when the keys are long (like type records) + /// because it recomputes the hash value of every key when it grows. This + /// value type stores the hash out of line in KeyStorage, so that table + /// entries are small and easy to rehash. + DenseSet HashedRecords; + +public: + TypeHasher(BumpPtrAllocator &RecordStorage) : RecordStorage(RecordStorage) {} + + void reset() { HashedRecords.clear(); } + + /// Takes the bytes of type record, inserts them into the hash table, saves + /// them, and returns a pointer to an identical stable type record along with + /// its type index in the destination stream. + TypeIndex getOrCreateRecord(ArrayRef &Record, TypeIndex TI); +}; + +TypeIndex TypeHasher::getOrCreateRecord(ArrayRef &Record, + TypeIndex TI) { + assert(Record.size() < UINT32_MAX && "Record too big"); + assert(Record.size() % 4 == 0 && "Record is not aligned to 4 bytes!"); + + // Compute the hash up front so we can store it in the key. + HashedType TempHashedType = {hash_value(Record), Record.data(), + unsigned(Record.size()), TI}; + auto Result = HashedRecords.insert(HashedTypePtr(&TempHashedType)); + HashedType *&Hashed = Result.first->Ptr; + + if (Result.second) { + // This was a new type record. We need stable storage for both the key and + // the record. The record should outlive the hashing operation. + Hashed = KeyStorage.Allocate(); + *Hashed = TempHashedType; + + uint8_t *Stable = RecordStorage.Allocate(Record.size()); + memcpy(Stable, Record.data(), Record.size()); + Hashed->Data = Stable; + assert(Hashed->Size == Record.size()); + } + + // Update the caller's copy of Record to point a stable copy. + Record = ArrayRef(Hashed->Data, Hashed->Size); + return Hashed->Index; +} + +TypeIndex TypeTableBuilder::nextTypeIndex() const { + return TypeIndex::fromArrayIndex(SeenRecords.size()); +} + +TypeTableBuilder::TypeTableBuilder(BumpPtrAllocator &Storage, bool Hash) + : RecordStorage(Storage) { + if (Hash) + Hasher = llvm::make_unique(Storage); +} + +TypeTableBuilder::~TypeTableBuilder() = default; + +ArrayRef> TypeTableBuilder::records() const { + return SeenRecords; +} + +void TypeTableBuilder::reset() { + if (Hasher) + Hasher->reset(); + SeenRecords.clear(); +} + +TypeIndex TypeTableBuilder::insertRecordBytes(ArrayRef &Record) { + if (Hasher) { + TypeIndex ActualTI = Hasher->getOrCreateRecord(Record, nextTypeIndex()); + if (nextTypeIndex() == ActualTI) + SeenRecords.push_back(Record); + return ActualTI; + } + + TypeIndex NewTI = nextTypeIndex(); + uint8_t *Stable = RecordStorage.Allocate(Record.size()); + memcpy(Stable, Record.data(), Record.size()); + Record = ArrayRef(Stable, Record.size()); + SeenRecords.push_back(Record); + return NewTI; +} + +TypeIndex TypeTableBuilder::insertRecord(const RemappedType &Record) { + TypeIndex TI; + ArrayRef OriginalData = Record.OriginalRecord.RecordData; + if (Record.Mappings.empty()) { + // This record did not remap any type indices. Just write it. + return insertRecordBytes(OriginalData); + } + + // At least one type index was remapped. Before we can hash it we have to + // copy the full record bytes, re-write each type index, then hash the copy. + // We do this in temporary storage since only the DenseMap can decide whether + // this record already exists, and if it does we don't want the memory to + // stick around. + RemapStorage.resize(OriginalData.size()); + ::memcpy(&RemapStorage[0], OriginalData.data(), OriginalData.size()); + uint8_t *ContentBegin = RemapStorage.data() + sizeof(RecordPrefix); + for (const auto &M : Record.Mappings) { + // First 4 bytes of every record are the record prefix, but the mapping + // offset is relative to the content which starts after. + *(TypeIndex *)(ContentBegin + M.first) = M.second; + } + auto RemapRef = makeArrayRef(RemapStorage); + return insertRecordBytes(RemapRef); +} + +TypeIndex TypeTableBuilder::insertRecord(ContinuationRecordBuilder &Builder) { + TypeIndex TI; + auto Fragments = Builder.end(nextTypeIndex()); + assert(!Fragments.empty()); + for (auto C : Fragments) + TI = insertRecordBytes(C.RecordData); + return TI; +} Index: llvm/lib/DebugInfo/CodeView/TypeTableCollection.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/TypeTableCollection.cpp +++ llvm/lib/DebugInfo/CodeView/TypeTableCollection.cpp @@ -11,7 +11,6 @@ #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" #include "llvm/DebugInfo/CodeView/RecordName.h" -#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" #include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/BinaryStreamReader.h" Index: llvm/lib/ObjectYAML/CodeViewYAMLTypes.cpp =================================================================== --- llvm/lib/ObjectYAML/CodeViewYAMLTypes.cpp +++ llvm/lib/ObjectYAML/CodeViewYAMLTypes.cpp @@ -20,6 +20,7 @@ #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" #include "llvm/DebugInfo/CodeView/CodeView.h" #include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" @@ -82,7 +83,7 @@ virtual ~LeafRecordBase() = default; virtual void map(yaml::IO &io) = 0; - virtual CVType toCodeViewRecord(TypeTableBuilder &TTB) const = 0; + virtual CVType toCodeViewRecord(TypeTableBuilder &TS) const = 0; virtual Error fromCodeViewRecord(CVType Type) = 0; }; @@ -96,9 +97,9 @@ return TypeDeserializer::deserializeAs(Type, Record); } - CVType toCodeViewRecord(TypeTableBuilder &TTB) const override { - TTB.writeKnownType(Record); - return CVType(Kind, TTB.records().back()); + CVType toCodeViewRecord(TypeTableBuilder &TS) const override { + TS.writeLeafType(Record); + return CVType(Kind, TS.records().back()); } mutable T Record; @@ -108,7 +109,7 @@ explicit LeafRecordImpl(TypeLeafKind K) : LeafRecordBase(K) {} void map(yaml::IO &io) override; - CVType toCodeViewRecord(TypeTableBuilder &TTB) const override; + CVType toCodeViewRecord(TypeTableBuilder &TS) const override; Error fromCodeViewRecord(CVType Type) override; std::vector Members; @@ -121,7 +122,7 @@ virtual ~MemberRecordBase() = default; virtual void map(yaml::IO &io) = 0; - virtual void writeTo(FieldListRecordBuilder &FLRB) = 0; + virtual void writeTo(ContinuationRecordBuilder &CRB) = 0; }; template struct MemberRecordImpl : public MemberRecordBase { @@ -130,8 +131,8 @@ void map(yaml::IO &io) override; - void writeTo(FieldListRecordBuilder &FLRB) override { - FLRB.writeMemberType(Record); + void writeTo(ContinuationRecordBuilder &CRB) override { + CRB.writeMemberType(Record); } mutable T Record; @@ -489,14 +490,14 @@ } CVType -LeafRecordImpl::toCodeViewRecord(TypeTableBuilder &TTB) const { - FieldListRecordBuilder FLRB(TTB); - FLRB.begin(); +LeafRecordImpl::toCodeViewRecord(TypeTableBuilder &TS) const { + ContinuationRecordBuilder CRB; + CRB.begin(ContinuationRecordKind::FieldList); for (const auto &Member : Members) { - Member.Member->writeTo(FLRB); + Member.Member->writeTo(CRB); } - FLRB.end(true); - return CVType(Kind, TTB.records().back()); + TS.insertRecord(CRB); + return CVType(Kind, TS.records().back()); } void MappingTraits::mapping(IO &io, OneMethodRecord &Record) { @@ -681,13 +682,8 @@ return make_error(cv_error_code::corrupt_record); } -CVType LeafRecord::toCodeViewRecord(BumpPtrAllocator &Alloc) const { - TypeTableBuilder TTB(Alloc); - return Leaf->toCodeViewRecord(TTB); -} - -CVType LeafRecord::toCodeViewRecord(TypeTableBuilder &TTB) const { - return Leaf->toCodeViewRecord(TTB); +CVType LeafRecord::toCodeViewRecord(TypeTableBuilder &Serializer) const { + return Leaf->toCodeViewRecord(Serializer); } namespace llvm { @@ -786,10 +782,10 @@ ArrayRef llvm::CodeViewYAML::toDebugT(ArrayRef Leafs, BumpPtrAllocator &Alloc) { - TypeTableBuilder TTB(Alloc, false); + TypeTableBuilder TS(Alloc, false); uint32_t Size = sizeof(uint32_t); for (const auto &Leaf : Leafs) { - CVType T = Leaf.toCodeViewRecord(TTB); + CVType T = Leaf.Leaf->toCodeViewRecord(TS); Size += T.length(); assert(T.length() % 4 == 0 && "Improper type record alignment!"); } @@ -798,7 +794,7 @@ BinaryStreamWriter Writer(Output, support::little); ExitOnError Err("Error writing type record to .debug$T section"); Err(Writer.writeInteger(COFF::DEBUG_SECTION_MAGIC)); - for (const auto &R : TTB.records()) { + for (const auto &R : TS.records()) { Err(Writer.writeBytes(R)); } assert(Writer.bytesRemaining() == 0 && "Didn't write all type record bytes!"); Index: llvm/test/DebugInfo/COFF/big-type.ll =================================================================== --- llvm/test/DebugInfo/COFF/big-type.ll +++ llvm/test/DebugInfo/COFF/big-type.ll @@ -10,6 +10,9 @@ ; CHECK-NEXT: EnumValue: 5436 ; CHECK-NEXT: Name: EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE5437 ; CHECK-NEXT: } +; CHECK: EnumValue: 5695 +; CHECK-NEXT: Name: EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE5696 +; CHECK-NEXT: } ; CHECK-NOT: ContinuationIndex ; CHECK-LABEL: FieldList (0x1001) @@ -52,6 +55,18 @@ ; CHECK-NEXT: } ; CHECK: ContinuationIndex: (0x1003) +; CHECK-LABEL: Enum (0x1005) { +; CHECK-NEXT: TypeLeafKind: LF_ENUM (0x1507) +; CHECK-NEXT: NumEnumerators: 5696 +; CHECK-NEXT: Properties [ (0x200) +; CHECK-NEXT: HasUniqueName (0x200) +; CHECK-NEXT: ] +; CHECK-NEXT: UnderlyingType: int (0x74) +; CHECK-NEXT: FieldListType: (0x1004) +; CHECK-NEXT: Name: BigThing +; CHECK-NEXT: LinkageName: .?AW4BigThing@@ +; CHECK-NEXT: } + ; ModuleID = 't.cpp' source_filename = "t.cpp" target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" Index: llvm/tools/llvm-pdbutil/PdbYaml.cpp =================================================================== --- llvm/tools/llvm-pdbutil/PdbYaml.cpp +++ llvm/tools/llvm-pdbutil/PdbYaml.cpp @@ -15,7 +15,6 @@ #include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h" -#include "llvm/DebugInfo/CodeView/TypeSerializer.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/RawTypes.h" Index: llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp =================================================================== --- llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp @@ -727,8 +727,9 @@ auto &TpiBuilder = Builder.getTpiBuilder(); const auto &Tpi = YamlObj.TpiStream.getValueOr(DefaultTpiStream); TpiBuilder.setVersionHeader(Tpi.Version); + TypeTableBuilder TS(Allocator); for (const auto &R : Tpi.Records) { - CVType Type = R.toCodeViewRecord(Allocator); + CVType Type = R.toCodeViewRecord(TS); TpiBuilder.addTypeRecord(Type.RecordData, None); } @@ -736,7 +737,7 @@ auto &IpiBuilder = Builder.getIpiBuilder(); IpiBuilder.setVersionHeader(Ipi.Version); for (const auto &R : Ipi.Records) { - CVType Type = R.toCodeViewRecord(Allocator); + CVType Type = R.toCodeViewRecord(TS); IpiBuilder.addTypeRecord(Type.RecordData, None); } Index: llvm/unittests/DebugInfo/CodeView/RandomAccessVisitorTest.cpp =================================================================== --- llvm/unittests/DebugInfo/CodeView/RandomAccessVisitorTest.cpp +++ llvm/unittests/DebugInfo/CodeView/RandomAccessVisitorTest.cpp @@ -12,7 +12,6 @@ #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/CodeView/TypeRecordMapping.h" -#include "llvm/DebugInfo/CodeView/TypeSerializer.h" #include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" #include "llvm/DebugInfo/PDB/Native/RawTypes.h" @@ -108,7 +107,7 @@ Stream << "Array [" << I << "]"; AR.Name = GlobalState->Strings.save(Stream.str()); GlobalState->Records.push_back(AR); - GlobalState->Indices.push_back(Builder.writeKnownType(AR)); + GlobalState->Indices.push_back(Builder.writeLeafType(AR)); CVType Type(TypeLeafKind::LF_ARRAY, Builder.records().back()); GlobalState->TypeVector.push_back(Type); @@ -363,13 +362,13 @@ Class.DerivationList = TypeIndex::fromArrayIndex(0); Class.FieldList = TypeIndex::fromArrayIndex(0); Class.VTableShape = TypeIndex::fromArrayIndex(0); - TypeIndex IndexZero = Builder.writeKnownType(Class); + TypeIndex IndexZero = Builder.writeLeafType(Class); // TypeIndex 1 refers to type index 0. ModifierRecord Modifier(TypeRecordKind::Modifier); Modifier.ModifiedType = TypeIndex::fromArrayIndex(0); Modifier.Modifiers = ModifierOptions::Const; - TypeIndex IndexOne = Builder.writeKnownType(Modifier); + TypeIndex IndexOne = Builder.writeLeafType(Modifier); // set up a type stream that refers to the above two serialized records. std::vector TypeArray; Index: llvm/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp =================================================================== --- llvm/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp +++ llvm/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp @@ -9,8 +9,9 @@ #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" -#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h" #include "llvm/DebugInfo/CodeView/SymbolSerializer.h" +#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" #include "llvm/Support/Allocator.h" #include "gmock/gmock.h" @@ -26,12 +27,12 @@ void SetUp() override { Refs.clear(); TTB = make_unique(Storage); - FLRB = make_unique(*TTB); + CRB = make_unique(); Symbols.clear(); } void TearDown() override { - FLRB.reset(); + CRB.reset(); TTB.reset(); } @@ -55,10 +56,11 @@ } template void writeFieldList(T &&... MemberRecords) { - FLRB->begin(); + CRB->begin(ContinuationRecordKind::FieldList); writeFieldListImpl(std::forward(MemberRecords)...); - FLRB->end(true); - ASSERT_EQ(1u, TTB->records().size()); + auto Records = CRB->end(TTB->nextTypeIndex()); + ASSERT_EQ(1u, Records.size()); + TTB->insertRecordBytes(Records.front().RecordData); discoverAllTypeIndices(); } @@ -140,7 +142,7 @@ template void writeFieldListImpl(RecType &&Record, Rest &&... Records) { - FLRB->writeMemberType(Record); + CRB->writeMemberType(Record); writeFieldListImpl(std::forward(Records)...); } @@ -149,7 +151,7 @@ template void writeTypeRecordsImpl(RecType &&Record, Rest &&... Records) { - TTB->writeKnownType(Record); + TTB->writeLeafType(Record); writeTypeRecordsImpl(std::forward(Records)...); } @@ -164,7 +166,7 @@ } std::vector> Refs; - std::unique_ptr FLRB; + std::unique_ptr CRB; std::vector Symbols; BumpPtrAllocator Storage; }; Index: llvm/unittests/Support/BinaryStreamTest.cpp =================================================================== --- llvm/unittests/Support/BinaryStreamTest.cpp +++ llvm/unittests/Support/BinaryStreamTest.cpp @@ -279,13 +279,13 @@ // When the stream is empty, it should report a 0 length and we should get an // error trying to read even 1 byte from it. BinaryStreamRef ConstRef(Stream); - EXPECT_EQ(0, ConstRef.getLength()); + EXPECT_EQ(0U, ConstRef.getLength()); EXPECT_THAT_ERROR(Reader.readObject(Byte), Failed()); // But if we write to it, its size should increase and we should be able to // read not just a byte, but the string that was written. EXPECT_THAT_ERROR(Writer.writeCString(Strings[0]), Succeeded()); - EXPECT_EQ(2, ConstRef.getLength()); + EXPECT_EQ(2U, ConstRef.getLength()); EXPECT_THAT_ERROR(Reader.readObject(Byte), Succeeded()); Reader.setOffset(0); @@ -296,25 +296,25 @@ // the // underlying stream grows. BinaryStreamRef Dropped = ConstRef.drop_front(1); - EXPECT_EQ(1, Dropped.getLength()); + EXPECT_EQ(1U, Dropped.getLength()); EXPECT_THAT_ERROR(Writer.writeCString(Strings[1]), Succeeded()); - EXPECT_EQ(4, ConstRef.getLength()); - EXPECT_EQ(3, Dropped.getLength()); + EXPECT_EQ(4U, ConstRef.getLength()); + EXPECT_EQ(3U, Dropped.getLength()); // If we drop zero bytes from the back, we should continue tracking the // length. Dropped = Dropped.drop_back(0); EXPECT_THAT_ERROR(Writer.writeCString(Strings[2]), Succeeded()); - EXPECT_EQ(6, ConstRef.getLength()); - EXPECT_EQ(5, Dropped.getLength()); + EXPECT_EQ(6U, ConstRef.getLength()); + EXPECT_EQ(5U, Dropped.getLength()); // If we drop non-zero bytes from the back, we should stop tracking the // length. Dropped = Dropped.drop_back(1); EXPECT_THAT_ERROR(Writer.writeCString(Strings[3]), Succeeded()); - EXPECT_EQ(8, ConstRef.getLength()); - EXPECT_EQ(4, Dropped.getLength()); + EXPECT_EQ(8U, ConstRef.getLength()); + EXPECT_EQ(4U, Dropped.getLength()); } TEST_F(BinaryStreamTest, DropOperations) { @@ -397,7 +397,7 @@ TEST_F(BinaryStreamTest, AppendingStream) { AppendingBinaryByteStream Stream(llvm::support::little); - EXPECT_EQ(0, Stream.getLength()); + EXPECT_EQ(0U, Stream.getLength()); std::vector InputData = {'T', 'e', 's', 't', 'T', 'e', 's', 't'}; auto Test = makeArrayRef(InputData).take_front(4);