--- /dev/null
+//===- 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 <cassert>
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+namespace llvm {
+namespace codeview {
+enum class ContinuationRecordKind { FieldList, MethodOverloadList };
+
+class ContinuationRecordBuilder {
+ SmallVector<uint32_t, 4> SegmentOffsets;
+ Optional<ContinuationRecordKind> Kind;
+ AppendingBinaryByteStream Buffer;
+ BinaryStreamWriter SegmentWriter;
+ TypeRecordMapping Mapping;
+ ArrayRef<uint8_t> InjectedSegmentBytes;
+
+ uint32_t getCurrentSegmentLength() const;
+
+ void insertSegmentEnd(uint32_t Offset);
+ CVType createSegmentRecord(uint32_t OffBegin, uint32_t OffEnd,
+ Optional<TypeIndex> RefersTo);
+
+public:
+ ContinuationRecordBuilder();
+ ~ContinuationRecordBuilder();
+
+ void begin(ContinuationRecordKind RecordKind);
+
+ // This template is explicitly instantiated in the implementation file for all
+ // supported types. The method itself is ugly, so inlining it into the header
+ // file clutters an otherwise straightforward interface.
+ template <typename RecordType> void writeMemberType(RecordType &Record);
+
+ std::vector<CVType> end(TypeIndex Index);
+};
+} // namespace codeview
+} // namespace llvm
+
+#endif
\ No newline at end of file
--- /dev/null
+//===- 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 <cassert>
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+namespace llvm {
+namespace codeview {
+
+class SimpleTypeSerializer {
+ std::vector<uint8_t> ScratchBuffer;
+
+public:
+ SimpleTypeSerializer();
+ ~SimpleTypeSerializer();
+
+ // This template is explicitly instantiated in the implementation file for all
+ // supported types. The method itself is ugly, so inlining it into the header
+ // file clutters an otherwise straightforward interface.
+ template <typename T> ArrayRef<uint8_t> serialize(T &Record);
+
+ // Don't allow serialization of field list records using this interface.
+ ArrayRef<uint8_t> serialize(const FieldListRecord &Record) = delete;
+};
+
+} // end namespace codeview
+} // end namespace llvm
+
+#endif // LLVM_DEBUGINFO_CODEVIEW_SIMPLETYPESERIALIZER_H
+++ /dev/null
-//===- 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 <cassert>
-#include <cstdint>
-#include <memory>
-#include <vector>
-
-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<SubRecord, 16> SubRecords;
-
- uint32_t length() const {
- uint32_t L = sizeof(RecordPrefix);
- for (const auto &R : SubRecords) {
- L += R.Size;
- }
- return L;
- }
- };
-
- using MutableRecordList = SmallVector<MutableArrayRef<uint8_t>, 2>;
-
- static constexpr uint8_t ContinuationLength = 8;
- BumpPtrAllocator &RecordStorage;
- RecordSegment CurrentSegment;
- MutableRecordList FieldListSegments;
-
- Optional<TypeLeafKind> TypeKind;
- Optional<TypeLeafKind> MemberKind;
- std::vector<uint8_t> RecordBuffer;
- MutableBinaryByteStream Stream;
- BinaryStreamWriter Writer;
- TypeRecordMapping Mapping;
-
- /// Private type record hashing implementation details are handled here.
- std::unique_ptr<TypeHasher> Hasher;
-
- /// Contains a list of all records indexed by TypeIndex.toArrayIndex().
- SmallVector<ArrayRef<uint8_t>, 2> SeenRecords;
-
- /// Temporary storage that we use to copy a record's data while re-writing
- /// its type indices.
- SmallVector<uint8_t, 256> RemapStorage;
-
- TypeIndex nextTypeIndex() const;
-
- bool isInFieldList() const;
- MutableArrayRef<uint8_t> getCurrentSubRecordData();
- MutableArrayRef<uint8_t> getCurrentRecordData();
- Error writeRecordPrefix(TypeLeafKind Kind);
-
- Expected<MutableArrayRef<uint8_t>>
- addPadding(MutableArrayRef<uint8_t> Record);
-
-public:
- explicit TypeSerializer(BumpPtrAllocator &Storage, bool Hash = true);
- ~TypeSerializer() override;
-
- void reset();
-
- BumpPtrAllocator &getAllocator() { return RecordStorage; }
-
- ArrayRef<ArrayRef<uint8_t>> records() const;
- TypeIndex insertRecordBytes(ArrayRef<uint8_t> &Record);
- TypeIndex insertRecord(const RemappedType &Record);
- Expected<TypeIndex> 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<Name##Record>(CVR, Record); \
- }
-#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
-#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
-
-private:
- template <typename RecordKind>
- Error visitKnownRecordImpl(CVType &CVR, RecordKind &Record) {
- return Mapping.visitKnownRecord(CVR, Record);
- }
-
- template <typename RecordType>
- Error visitKnownMemberImpl(CVMemberRecord &CVR, RecordType &Record) {
- assert(CVR.Kind == static_cast<TypeLeafKind>(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<uint8_t> 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
-//===- TypeTableBuilder.h ---------------------------------------*- C++ -*-===//
+//===- TypeTableBuilder.h ----------------------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
#define LLVM_DEBUGINFO_CODEVIEW_TYPETABLEBUILDER_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 <algorithm>
#include <cassert>
#include <cstdint>
-#include <type_traits>
+#include <memory>
+#include <vector>
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<TypeHasher> Hasher;
- bool empty() const { return Serializer.records().empty(); }
+ /// Contains a list of all records indexed by TypeIndex.toArrayIndex().
+ SmallVector<ArrayRef<uint8_t>, 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<uint8_t, 256> RemapStorage;
- template <typename T> TypeIndex writeKnownType(T &Record) {
- static_assert(!std::is_same<T, FieldListRecord>::value,
- "Can't serialize FieldList!");
+public:
+ explicit TypeTableBuilder(BumpPtrAllocator &Storage, bool Hash = true);
+ ~TypeTableBuilder() override;
- CVType Type;
- Type.Type = static_cast<TypeLeafKind>(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<uint8_t> Record) {
- return Serializer.insertRecordBytes(Record);
- }
+ BumpPtrAllocator &getAllocator() { return RecordStorage; }
- TypeIndex writeSerializedRecord(const RemappedType &Record) {
- return Serializer.insertRecord(Record);
+ ArrayRef<ArrayRef<uint8_t>> records() const;
+ TypeIndex insertRecordBytes(ArrayRef<uint8_t> &Record);
+ TypeIndex insertRecord(const RemappedType &Record);
+ TypeIndex insertRecord(ContinuationRecordBuilder &Builder);
+
+ template <typename T> TypeIndex writeLeafType(T &Record) {
+ ArrayRef<uint8_t> Data = SimpleSerializer.serialize(Record);
+ return insertRecordBytes(Data);
}
template <typename TFunc> void ForEachRecord(TFunc Func) {
uint32_t Index = TypeIndex::FirstNonSimpleIndex;
- for (auto Record : Serializer.records()) {
+ for (auto Record : SeenRecords) {
Func(TypeIndex(Index), Record);
++Index;
}
}
-
- ArrayRef<ArrayRef<uint8_t>> 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 <typename T> void writeMemberType(T &Record) {
- CVMemberRecord CVMR;
- CVMR.Kind = static_cast<TypeLeafKind>(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
namespace llvm {
namespace codeview {
-
class TypeTableBuilder;
-
-} // end namespace codeview
+}
namespace CodeViewYAML {
struct LeafRecord {
std::shared_ptr<detail::LeafRecordBase> Leaf;
- codeview::CVType toCodeViewRecord(BumpPtrAllocator &Allocator) const;
- codeview::CVType toCodeViewRecord(codeview::TypeTableBuilder &TS) const;
+ codeview::CVType
+ toCodeViewRecord(codeview::TypeTableBuilder &Serializer) const;
static Expected<LeafRecord> fromCodeViewRecord(codeview::CVType Type);
};
/// causing the underlying data to grow. This class owns the underlying data.
class AppendingBinaryByteStream : public WritableBinaryStream {
std::vector<uint8_t> Data;
- llvm::support::endianness Endian;
+ llvm::support::endianness Endian = llvm::support::little;
public:
AppendingBinaryByteStream() = default;
return Error::success();
}
+ void insert(uint32_t Offset, ArrayRef<uint8_t> Bytes) {
+ Data.insert(Data.begin() + Offset, Bytes.begin(), Bytes.end());
+ }
+
Error readLongestContiguousChunk(uint32_t Offset,
ArrayRef<uint8_t> &Buffer) override {
if (auto EC = checkOffsetForWrite(Offset, 1))
#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"
// 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);
}
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);
StringRef Name = (i == 0) ? Ty->getName() : "";
ArrayRecord AR(ElementTypeIndex, IndexType, ArraySize, Name);
- ElementTypeIndex = TypeTable.writeKnownType(AR);
+ ElementTypeIndex = TypeTable.writeLeafType(AR);
}
return ElementTypeIndex;
// do.
PointerOptions PO = PointerOptions::None;
PointerRecord PR(PointeeTI, PK, PM, PO, Ty->getSizeInBits() / 8);
- return TypeTable.writeKnownType(PR);
+ return TypeTable.writeLeafType(PR);
}
static PointerToMemberRepresentation
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
}
TypeIndex ModifiedTI = getTypeIndex(BaseTy);
ModifierRecord MR(ModifiedTI, Mods);
- return TypeTable.writeKnownType(MR);
+ return TypeTable.writeLeafType(MR);
}
TypeIndex CodeViewDebug::lowerTypeFunction(const DISubroutineType *Ty) {
}
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,
}
ArgListRecord ArgListRec(TypeRecordKind::ArgList, ArgTypeIndices);
- TypeIndex ArgListIndex = TypeTable.writeKnownType(ArgListRec);
+ TypeIndex ArgListIndex = TypeTable.writeLeafType(ArgListRec);
CallingConvention CC = dwarfCCToCodeView(Ty->getCC());
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) {
SmallVector<VFTableSlotKind, 4> Slots(VSlotCount, VFTableSlotKind::Near);
VFTableShapeRecord VFTSR(Slots);
- return TypeTable.writeKnownType(VFTSR);
+ return TypeTable.writeLeafType(VFTSR);
}
static MemberAccess translateAccessFlags(unsigned RecordTag, unsigned Flags) {
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.
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);
}
//===----------------------------------------------------------------------===//
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;
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);
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;
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);
// 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) {
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);
}
}
if (Member->isStaticMember()) {
StaticDataMemberRecord SDMR(Access, MemberBaseType, MemberName);
- FLBR.writeMemberType(SDMR);
+ ContinuationBuilder.writeMemberType(SDMR);
MemberCount++;
continue;
}
if ((Member->getFlags() & DINode::FlagArtificial) &&
Member->getName().startswith("_vptr$")) {
VFPtrRecord VFPR(getTypeIndex(Member->getBaseType()));
- FLBR.writeMemberType(VFPR);
+ ContinuationBuilder.writeMemberType(VFPR);
MemberCount++;
continue;
}
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++;
}
}
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());
}
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;
: PointerKind::Near32,
PointerMode::LValueReference, PointerOptions::None,
Ty->getSizeInBits() / 8);
- return TypeTable.writeKnownType(PR);
+ return TypeTable.writeLeafType(PR);
}
TypeIndex CodeViewDebug::getCompleteTypeIndex(DITypeRef TypeRef) {
add_llvm_library(LLVMDebugInfoCodeView
CodeViewError.cpp
CodeViewRecordIO.cpp
+ ContinuationRecordBuilder.cpp
CVSymbolVisitor.cpp
CVTypeVisitor.cpp
DebugChecksumsSubsection.cpp
Line.cpp
RecordName.cpp
RecordSerialization.cpp
+ SimpleTypeSerializer.cpp
StringsAndChecksums.cpp
SymbolRecordMapping.cpp
SymbolDumper.cpp
TypeIndex.cpp
TypeIndexDiscovery.cpp
TypeRecordMapping.cpp
- TypeSerializer.cpp
+ TypeTableBuilder.cpp
TypeStreamMerger.cpp
TypeTableCollection.cpp
--- /dev/null
+#include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+
+namespace {
+struct ContinuationRecord {
+ ulittle16_t Kind{uint16_t(TypeLeafKind::LF_INDEX)};
+ ulittle16_t Size{0};
+ ulittle32_t IndexRef{0xB0C0B0C0};
+};
+
+struct SegmentInjection {
+ SegmentInjection(TypeLeafKind Kind) { Prefix.RecordKind = Kind; }
+
+ ContinuationRecord Cont;
+ RecordPrefix Prefix;
+};
+} // namespace
+
+static void addPadding(BinaryStreamWriter &Writer) {
+ uint32_t Align = Writer.getOffset() % 4;
+ if (Align == 0)
+ return;
+
+ int PaddingBytes = 4 - Align;
+ while (PaddingBytes > 0) {
+ uint8_t Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes);
+ cantFail(Writer.writeInteger(Pad));
+ --PaddingBytes;
+ }
+}
+
+static SegmentInjection InjectFieldList(TypeLeafKind::LF_FIELDLIST);
+static SegmentInjection InjectMethodOverloadList(TypeLeafKind::LF_METHODLIST);
+
+static constexpr uint32_t ContinuationLength = sizeof(ContinuationRecord);
+static constexpr uint32_t MaxSegmentLength =
+ MaxRecordLength - ContinuationLength;
+
+static inline TypeLeafKind getTypeLeafKind(ContinuationRecordKind CK) {
+ return (CK == ContinuationRecordKind::FieldList) ? LF_FIELDLIST
+ : LF_METHODLIST;
+}
+
+ContinuationRecordBuilder::ContinuationRecordBuilder()
+ : SegmentWriter(Buffer), Mapping(SegmentWriter) {}
+
+ContinuationRecordBuilder::~ContinuationRecordBuilder() {}
+
+void ContinuationRecordBuilder::begin(ContinuationRecordKind RecordKind) {
+ assert(!Kind.hasValue());
+ Kind = RecordKind;
+ Buffer.clear();
+ SegmentWriter.setOffset(0);
+ SegmentOffsets.clear();
+ SegmentOffsets.push_back(0);
+ assert(SegmentWriter.getOffset() == 0);
+ assert(SegmentWriter.getLength() == 0);
+
+ const SegmentInjection *FLI =
+ (RecordKind == ContinuationRecordKind::FieldList)
+ ? &InjectFieldList
+ : &InjectMethodOverloadList;
+ const uint8_t *FLIB = reinterpret_cast<const uint8_t *>(FLI);
+ InjectedSegmentBytes =
+ ArrayRef<uint8_t>(FLIB, FLIB + sizeof(SegmentInjection));
+
+ CVType Type;
+ Type.Type = getTypeLeafKind(RecordKind);
+ cantFail(Mapping.visitTypeBegin(Type));
+
+ // Seed the first trecord with an appropriate record prefix.
+ RecordPrefix Prefix;
+ Prefix.RecordLen = 0;
+ Prefix.RecordKind = Type.Type;
+ cantFail(SegmentWriter.writeObject(Prefix));
+}
+
+template <typename RecordType>
+void ContinuationRecordBuilder::writeMemberType(RecordType &Record) {
+ assert(Kind.hasValue());
+
+ uint32_t OriginalOffset = SegmentWriter.getOffset();
+ CVMemberRecord CVMR;
+ CVMR.Kind = static_cast<TypeLeafKind>(Record.getKind());
+
+ // Member Records aren't length-prefixed, they only have a 2-byte TypeLeafKind
+ // at the beginning.
+ cantFail(SegmentWriter.writeEnum(CVMR.Kind));
+
+ // Let the Mapping handle the rest.
+ cantFail(Mapping.visitMemberBegin(CVMR));
+ cantFail(Mapping.visitKnownMember(CVMR, Record));
+ cantFail(Mapping.visitMemberEnd(CVMR));
+
+ // Make sure it's padded to 4 bytes.
+ addPadding(SegmentWriter);
+ assert(getCurrentSegmentLength() % 4 == 0);
+
+ // The maximum length of a single segment is 64KB minus the size to insert a
+ // continuation. So if we are over that, inject a continuation between the
+ // previous member and the member that was just written, then end the previous
+ // segment after the continuation and begin a new one with the just-written
+ // member.
+ if (getCurrentSegmentLength() > MaxSegmentLength) {
+ // We need to inject some bytes before the member we just wrote but after
+ // the previous member. Save off the length of the member we just wrote so
+ // that we can do some sanity checking on it.
+ uint32_t MemberLength = SegmentWriter.getOffset() - OriginalOffset;
+ 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<TypeIndex> RefersTo) {
+ assert(OffEnd - OffBegin <= USHRT_MAX);
+
+ MutableArrayRef<uint8_t> Data = Buffer.data();
+ Data = Data.slice(OffBegin, OffEnd - OffBegin);
+
+ CVType Type;
+ Type.Type = getTypeLeafKind(*Kind);
+ Type.RecordData = Data;
+
+ // Write the length to the RecordPrefix, making sure it does not include
+ // sizeof(RecordPrefix.Length)
+ RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(Data.data());
+ assert(Prefix->RecordKind == Type.Type);
+ Prefix->RecordLen = Data.size() - sizeof(RecordPrefix::RecordLen);
+
+ if (RefersTo.hasValue()) {
+ auto Continuation = Data.take_back(ContinuationLength);
+ ContinuationRecord *CR =
+ reinterpret_cast<ContinuationRecord *>(Continuation.data());
+ assert(CR->Kind == TypeLeafKind::LF_INDEX);
+ assert(CR->IndexRef == 0xB0C0B0C0);
+ CR->IndexRef = RefersTo->getIndex();
+ }
+
+ return Type;
+}
+
+std::vector<CVType> ContinuationRecordBuilder::end(TypeIndex Index) {
+ CVType Type;
+ Type.Type = getTypeLeafKind(*Kind);
+ cantFail(Mapping.visitTypeEnd(Type));
+
+ // We're now done, and we have a series of segments each beginning at an
+ // offset specified in the SegmentOffsets array. We now need to iterate
+ // over each segment and post-process them in the following two ways:
+ // 1) Each top-level record has a RecordPrefix whose type is either
+ // LF_FIELDLIST or LF_METHODLIST, but the Length field is still 0.
+ // Those should all be set to the correct length now.
+ // 2) Each continuation record has an IndexRef field which we set to the
+ // magic value 0xB0C0B0C0. Now that the caller has told us the TypeIndex
+ // they want this sequence to start from, we can go through and update
+ // each one.
+ //
+ // Logically, the sequence of records we've built up looks like this:
+ //
+ // SegmentOffsets[0]: <Length> (Initially: uninitialized)
+ // SegmentOffsets[0]+2: LF_FIELDLIST
+ // SegmentOffsets[0]+4: Member[0]
+ // SegmentOffsets[0]+?: ...
+ // SegmentOffsets[0]+?: Member[4]
+ // SegmentOffsets[1]-8: LF_INDEX
+ // SegmentOffsets[1]-6: 0
+ // SegmentOffsets[1]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0)
+ //
+ // SegmentOffsets[1]: <Length> (Initially: uninitialized)
+ // SegmentOffsets[1]+2: LF_FIELDLIST
+ // SegmentOffsets[1]+4: Member[0]
+ // SegmentOffsets[1]+?: ...
+ // SegmentOffsets[1]+?: Member[s]
+ // SegmentOffsets[2]-8: LF_INDEX
+ // SegmentOffsets[2]-6: 0
+ // SegmentOffsets[2]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0)
+ //
+ // ...
+ //
+ // SegmentOffsets[N]: <Length> (Initially: uninitialized)
+ // SegmentOffsets[N]+2: LF_FIELDLIST
+ // SegmentOffsets[N]+4: Member[0]
+ // SegmentOffsets[N]+?: ...
+ // SegmentOffsets[N]+?: Member[t]
+ //
+ // And this is the way we have laid them out in the serialization buffer. But
+ // we cannot actually commit them to the underlying stream this way, due to
+ // the topological sorting requirement of a type stream (specifically,
+ // TypeIndex references can only point backwards, not forwards). So the
+ // sequence that we return to the caller contains the records in reverse
+ // order, which is the proper order for committing the serialized records.
+
+ std::vector<CVType> Types;
+ Types.reserve(SegmentOffsets.size());
+
+ auto SO = makeArrayRef(SegmentOffsets);
+
+ uint32_t End = SegmentWriter.getOffset();
+
+ Optional<TypeIndex> RefersTo;
+ for (uint32_t Offset : reverse(SO)) {
+ Types.push_back(createSegmentRecord(Offset, End, RefersTo));
+
+ End = Offset;
+ RefersTo = Index++;
+ }
+
+ Kind.reset();
+ return Types;
+}
+
+// Explicitly instantiate the member function for each known type so that we can
+// implement this in the cpp file.
+#define TYPE_RECORD(EnumName, EnumVal, Name)
+#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
+#define MEMBER_RECORD(EnumName, EnumVal, Name) \
+ template void llvm::codeview::ContinuationRecordBuilder::writeMemberType( \
+ Name##Record &Record);
+#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
+#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
--- /dev/null
+#include "llvm/DebugInfo/CodeView/SimpleTypeSerializer.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+
+static void writeRecordPrefix(BinaryStreamWriter &Writer, TypeLeafKind Kind) {
+ RecordPrefix Prefix;
+ Prefix.RecordKind = Kind;
+ Prefix.RecordLen = 0;
+ cantFail(Writer.writeObject(Prefix));
+}
+
+static void addPadding(BinaryStreamWriter &Writer) {
+ uint32_t Align = Writer.getOffset() % 4;
+ if (Align == 0)
+ return;
+
+ int PaddingBytes = 4 - Align;
+ while (PaddingBytes > 0) {
+ uint8_t Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes);
+ cantFail(Writer.writeInteger(Pad));
+ --PaddingBytes;
+ }
+}
+
+SimpleTypeSerializer::SimpleTypeSerializer() : ScratchBuffer(MaxRecordLength) {}
+
+SimpleTypeSerializer::~SimpleTypeSerializer() {}
+
+template <typename T>
+ArrayRef<uint8_t> SimpleTypeSerializer::serialize(T &Record) {
+ BinaryStreamWriter Writer(ScratchBuffer, support::little);
+ TypeRecordMapping Mapping(Writer);
+
+ CVType CVT;
+ CVT.Type = static_cast<TypeLeafKind>(Record.getKind());
+
+ writeRecordPrefix(Writer, CVT.Type);
+
+ cantFail(Mapping.visitTypeBegin(CVT));
+ cantFail(Mapping.visitKnownRecord(CVT, Record));
+ cantFail(Mapping.visitTypeEnd(CVT));
+
+ addPadding(Writer);
+
+ RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(ScratchBuffer.data());
+
+ Prefix->RecordKind = CVT.kind();
+ Prefix->RecordLen = Writer.getOffset() - sizeof(uint16_t);
+
+ return {ScratchBuffer.data(), Writer.getOffset()};
+}
+
+// Explicitly instantiate the member function for each known type so that we can
+// implement this in the cpp file.
+#define TYPE_RECORD(EnumName, EnumVal, Name) \
+ template ArrayRef<uint8_t> llvm::codeview::SimpleTypeSerializer::serialize( \
+ Name##Record &Record);
+#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
+#define MEMBER_RECORD(EnumName, EnumVal, Name)
+#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
+#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
OneMethodRecord &Record) {
- MapOneMethodRecord Mapper(false);
+ const bool IsFromOverloadList = (TypeKind == LF_METHODLIST);
+ MapOneMethodRecord Mapper(IsFromOverloadList);
return Mapper(IO, Record);
}
+++ /dev/null
-//===- TypeSerialzier.cpp -------------------------------------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "llvm/DebugInfo/CodeView/TypeSerializer.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/DenseSet.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/DebugInfo/CodeView/CodeView.h"
-#include "llvm/DebugInfo/CodeView/RecordSerialization.h"
-#include "llvm/DebugInfo/CodeView/TypeIndex.h"
-#include "llvm/Support/Allocator.h"
-#include "llvm/Support/BinaryByteStream.h"
-#include "llvm/Support/BinaryStreamWriter.h"
-#include "llvm/Support/Endian.h"
-#include "llvm/Support/Error.h"
-#include <algorithm>
-#include <cassert>
-#include <cstdint>
-#include <cstring>
-
-using namespace llvm;
-using namespace llvm::codeview;
-
-namespace {
-
-struct HashedType {
- uint64_t Hash;
- const uint8_t *Data;
- unsigned Size; // FIXME: Go to uint16_t?
- TypeIndex Index;
-};
-
-/// Wrapper around a poitner to a HashedType. Hash and equality operations are
-/// based on data in the pointee.
-struct HashedTypePtr {
- HashedTypePtr() = default;
- HashedTypePtr(HashedType *Ptr) : Ptr(Ptr) {}
-
- HashedType *Ptr = nullptr;
-};
-
-} // end anonymous namespace
-
-namespace llvm {
-
-template <> struct DenseMapInfo<HashedTypePtr> {
- static inline HashedTypePtr getEmptyKey() { return HashedTypePtr(nullptr); }
-
- static inline HashedTypePtr getTombstoneKey() {
- return HashedTypePtr(reinterpret_cast<HashedType *>(1));
- }
-
- static unsigned getHashValue(HashedTypePtr Val) {
- assert(Val.Ptr != getEmptyKey().Ptr && Val.Ptr != getTombstoneKey().Ptr);
- return Val.Ptr->Hash;
- }
-
- static bool isEqual(HashedTypePtr LHSP, HashedTypePtr RHSP) {
- HashedType *LHS = LHSP.Ptr;
- HashedType *RHS = RHSP.Ptr;
- if (RHS == getEmptyKey().Ptr || RHS == getTombstoneKey().Ptr)
- return LHS == RHS;
- if (LHS->Hash != RHS->Hash || LHS->Size != RHS->Size)
- return false;
- return ::memcmp(LHS->Data, RHS->Data, LHS->Size) == 0;
- }
-};
-
-} // end namespace llvm
-
-/// Private implementation so that we don't leak our DenseMap instantiations to
-/// users.
-class llvm::codeview::TypeHasher {
-private:
- /// Storage for type record provided by the caller. Records will outlive the
- /// hasher object, so they should be allocated here.
- BumpPtrAllocator &RecordStorage;
-
- /// Storage for hash keys. These only need to live as long as the hashing
- /// operation.
- BumpPtrAllocator KeyStorage;
-
- /// Hash table. We really want a DenseMap<ArrayRef<uint8_t>, TypeIndex> here,
- /// but DenseMap is inefficient when the keys are long (like type records)
- /// because it recomputes the hash value of every key when it grows. This
- /// value type stores the hash out of line in KeyStorage, so that table
- /// entries are small and easy to rehash.
- DenseSet<HashedTypePtr> HashedRecords;
-
-public:
- TypeHasher(BumpPtrAllocator &RecordStorage) : RecordStorage(RecordStorage) {}
-
- void reset() { HashedRecords.clear(); }
-
- /// Takes the bytes of type record, inserts them into the hash table, saves
- /// them, and returns a pointer to an identical stable type record along with
- /// its type index in the destination stream.
- TypeIndex getOrCreateRecord(ArrayRef<uint8_t> &Record, TypeIndex TI);
-};
-
-TypeIndex TypeHasher::getOrCreateRecord(ArrayRef<uint8_t> &Record,
- TypeIndex TI) {
- assert(Record.size() < UINT32_MAX && "Record too big");
- assert(Record.size() % 4 == 0 && "Record is not aligned to 4 bytes!");
-
- // Compute the hash up front so we can store it in the key.
- HashedType TempHashedType = {hash_value(Record), Record.data(),
- unsigned(Record.size()), TI};
- auto Result = HashedRecords.insert(HashedTypePtr(&TempHashedType));
- HashedType *&Hashed = Result.first->Ptr;
-
- if (Result.second) {
- // This was a new type record. We need stable storage for both the key and
- // the record. The record should outlive the hashing operation.
- Hashed = KeyStorage.Allocate<HashedType>();
- *Hashed = TempHashedType;
-
- uint8_t *Stable = RecordStorage.Allocate<uint8_t>(Record.size());
- memcpy(Stable, Record.data(), Record.size());
- Hashed->Data = Stable;
- assert(Hashed->Size == Record.size());
- }
-
- // Update the caller's copy of Record to point a stable copy.
- Record = ArrayRef<uint8_t>(Hashed->Data, Hashed->Size);
- return Hashed->Index;
-}
-
-TypeIndex TypeSerializer::nextTypeIndex() const {
- return TypeIndex::fromArrayIndex(SeenRecords.size());
-}
-
-bool TypeSerializer::isInFieldList() const {
- return TypeKind.hasValue() && *TypeKind == TypeLeafKind::LF_FIELDLIST;
-}
-
-MutableArrayRef<uint8_t> TypeSerializer::getCurrentSubRecordData() {
- assert(isInFieldList());
- return getCurrentRecordData().drop_front(CurrentSegment.length());
-}
-
-MutableArrayRef<uint8_t> TypeSerializer::getCurrentRecordData() {
- return MutableArrayRef<uint8_t>(RecordBuffer).take_front(Writer.getOffset());
-}
-
-Error TypeSerializer::writeRecordPrefix(TypeLeafKind Kind) {
- RecordPrefix Prefix;
- Prefix.RecordKind = Kind;
- Prefix.RecordLen = 0;
- if (auto EC = Writer.writeObject(Prefix))
- return EC;
- return Error::success();
-}
-
-Expected<MutableArrayRef<uint8_t>>
-TypeSerializer::addPadding(MutableArrayRef<uint8_t> Record) {
- uint32_t Align = Record.size() % 4;
- if (Align == 0)
- return Record;
-
- int PaddingBytes = 4 - Align;
- int N = PaddingBytes;
- while (PaddingBytes > 0) {
- uint8_t Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes);
- if (auto EC = Writer.writeInteger(Pad))
- return std::move(EC);
- --PaddingBytes;
- }
- return MutableArrayRef<uint8_t>(Record.data(), Record.size() + N);
-}
-
-TypeSerializer::TypeSerializer(BumpPtrAllocator &Storage, bool Hash)
- : RecordStorage(Storage), RecordBuffer(MaxRecordLength * 2),
- Stream(RecordBuffer, support::little), Writer(Stream),
- Mapping(Writer) {
- // RecordBuffer needs to be able to hold enough data so that if we are 1
- // byte short of MaxRecordLen, and then we try to write MaxRecordLen bytes,
- // we won't overflow.
- if (Hash)
- Hasher = llvm::make_unique<TypeHasher>(Storage);
-}
-
-TypeSerializer::~TypeSerializer() = default;
-
-ArrayRef<ArrayRef<uint8_t>> TypeSerializer::records() const {
- return SeenRecords;
-}
-
-void TypeSerializer::reset() {
- if (Hasher)
- Hasher->reset();
- Writer.setOffset(0);
- CurrentSegment = RecordSegment();
- FieldListSegments.clear();
- TypeKind.reset();
- MemberKind.reset();
- SeenRecords.clear();
-}
-
-TypeIndex TypeSerializer::insertRecordBytes(ArrayRef<uint8_t> &Record) {
- assert(!TypeKind.hasValue() && "Already in a type mapping!");
- assert(Writer.getOffset() == 0 && "Stream has data already!");
-
- if (Hasher) {
- TypeIndex ActualTI = Hasher->getOrCreateRecord(Record, nextTypeIndex());
- if (nextTypeIndex() == ActualTI)
- SeenRecords.push_back(Record);
- return ActualTI;
- }
-
- TypeIndex NewTI = nextTypeIndex();
- uint8_t *Stable = RecordStorage.Allocate<uint8_t>(Record.size());
- memcpy(Stable, Record.data(), Record.size());
- Record = ArrayRef<uint8_t>(Stable, Record.size());
- SeenRecords.push_back(Record);
- return NewTI;
-}
-
-TypeIndex TypeSerializer::insertRecord(const RemappedType &Record) {
- assert(!TypeKind.hasValue() && "Already in a type mapping!");
- assert(Writer.getOffset() == 0 && "Stream has data already!");
-
- TypeIndex TI;
- ArrayRef<uint8_t> OriginalData = Record.OriginalRecord.RecordData;
- if (Record.Mappings.empty()) {
- // This record did not remap any type indices. Just write it.
- return insertRecordBytes(OriginalData);
- }
-
- // At least one type index was remapped. Before we can hash it we have to
- // copy the full record bytes, re-write each type index, then hash the copy.
- // We do this in temporary storage since only the DenseMap can decide whether
- // this record already exists, and if it does we don't want the memory to
- // stick around.
- RemapStorage.resize(OriginalData.size());
- ::memcpy(&RemapStorage[0], OriginalData.data(), OriginalData.size());
- uint8_t *ContentBegin = RemapStorage.data() + sizeof(RecordPrefix);
- for (const auto &M : Record.Mappings) {
- // First 4 bytes of every record are the record prefix, but the mapping
- // offset is relative to the content which starts after.
- *(TypeIndex *)(ContentBegin + M.first) = M.second;
- }
- auto RemapRef = makeArrayRef(RemapStorage);
- return insertRecordBytes(RemapRef);
-}
-
-Error TypeSerializer::visitTypeBegin(CVType &Record) {
- assert(!TypeKind.hasValue() && "Already in a type mapping!");
- assert(Writer.getOffset() == 0 && "Stream has data already!");
-
- if (auto EC = writeRecordPrefix(Record.kind()))
- return EC;
-
- TypeKind = Record.kind();
- if (auto EC = Mapping.visitTypeBegin(Record))
- return EC;
-
- return Error::success();
-}
-
-Expected<TypeIndex> TypeSerializer::visitTypeEndGetIndex(CVType &Record) {
- assert(TypeKind.hasValue() && "Not in a type mapping!");
- if (auto EC = Mapping.visitTypeEnd(Record))
- return std::move(EC);
-
- // Update the record's length and fill out the CVType members to point to
- // the stable memory holding the record's data.
- auto ThisRecordData = getCurrentRecordData();
- auto ExpectedData = addPadding(ThisRecordData);
- if (!ExpectedData)
- return ExpectedData.takeError();
- ThisRecordData = *ExpectedData;
-
- RecordPrefix *Prefix =
- reinterpret_cast<RecordPrefix *>(ThisRecordData.data());
- Prefix->RecordLen = ThisRecordData.size() - sizeof(uint16_t);
-
- Record.Type = *TypeKind;
- Record.RecordData = ThisRecordData;
-
- // insertRecordBytes assumes we're not in a mapping, so do this first.
- TypeKind.reset();
- Writer.setOffset(0);
-
- TypeIndex InsertedTypeIndex = insertRecordBytes(Record.RecordData);
-
- // Write out each additional segment in reverse order, and update each
- // record's continuation index to point to the previous one.
- for (auto X : reverse(FieldListSegments)) {
- auto CIBytes = X.take_back(sizeof(uint32_t));
- support::ulittle32_t *CI =
- reinterpret_cast<support::ulittle32_t *>(CIBytes.data());
- assert(*CI == 0xB0C0B0C0 && "Invalid TypeIndex placeholder");
- *CI = InsertedTypeIndex.getIndex();
- InsertedTypeIndex = insertRecordBytes(X);
- }
-
- FieldListSegments.clear();
- CurrentSegment.SubRecords.clear();
-
- return InsertedTypeIndex;
-}
-
-Error TypeSerializer::visitTypeEnd(CVType &Record) {
- auto ExpectedIndex = visitTypeEndGetIndex(Record);
- if (!ExpectedIndex)
- return ExpectedIndex.takeError();
- return Error::success();
-}
-
-Error TypeSerializer::visitMemberBegin(CVMemberRecord &Record) {
- assert(isInFieldList() && "Not in a field list!");
- assert(!MemberKind.hasValue() && "Already in a member record!");
- MemberKind = Record.Kind;
-
- if (auto EC = Mapping.visitMemberBegin(Record))
- return EC;
-
- return Error::success();
-}
-
-Error TypeSerializer::visitMemberEnd(CVMemberRecord &Record) {
- if (auto EC = Mapping.visitMemberEnd(Record))
- return EC;
-
- // Check if this subrecord makes the current segment not fit in 64K minus
- // the space for a continuation record (8 bytes). If the segment does not
- // fit, insert a continuation record.
- if (Writer.getOffset() > MaxRecordLength - ContinuationLength) {
- MutableArrayRef<uint8_t> Data = getCurrentRecordData();
- SubRecord LastSubRecord = CurrentSegment.SubRecords.back();
- uint32_t CopySize = CurrentSegment.length() - LastSubRecord.Size;
- auto CopyData = Data.take_front(CopySize);
- auto LeftOverData = Data.drop_front(CopySize);
- assert(LastSubRecord.Size == LeftOverData.size());
-
- // Allocate stable storage for the record and copy the old record plus
- // continuation over.
- uint16_t LengthWithSize = CopySize + ContinuationLength;
- assert(LengthWithSize <= MaxRecordLength);
- RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(CopyData.data());
- Prefix->RecordLen = LengthWithSize - sizeof(uint16_t);
-
- uint8_t *SegmentBytes = RecordStorage.Allocate<uint8_t>(LengthWithSize);
- auto SavedSegment = MutableArrayRef<uint8_t>(SegmentBytes, LengthWithSize);
- MutableBinaryByteStream CS(SavedSegment, support::little);
- BinaryStreamWriter CW(CS);
- if (auto EC = CW.writeBytes(CopyData))
- return EC;
- if (auto EC = CW.writeEnum(TypeLeafKind::LF_INDEX))
- return EC;
- if (auto EC = CW.writeInteger<uint16_t>(0))
- return EC;
- if (auto EC = CW.writeInteger<uint32_t>(0xB0C0B0C0))
- return EC;
- FieldListSegments.push_back(SavedSegment);
-
- // Write a new placeholder record prefix to mark the start of this new
- // top-level record.
- Writer.setOffset(0);
- if (auto EC = writeRecordPrefix(TypeLeafKind::LF_FIELDLIST))
- return EC;
-
- // Then move over the subrecord that overflowed the old segment to the
- // beginning of this segment. Note that we have to use memmove here
- // instead of Writer.writeBytes(), because the new and old locations
- // could overlap.
- ::memmove(Stream.data().data() + sizeof(RecordPrefix), LeftOverData.data(),
- LeftOverData.size());
- // And point the segment writer at the end of that subrecord.
- Writer.setOffset(LeftOverData.size() + sizeof(RecordPrefix));
-
- CurrentSegment.SubRecords.clear();
- CurrentSegment.SubRecords.push_back(LastSubRecord);
- }
-
- // Update the CVMemberRecord since we may have shifted around or gotten
- // padded.
- Record.Data = getCurrentSubRecordData();
-
- MemberKind.reset();
- return Error::success();
-}
bool RemapSuccess) {
TypeIndex DestIdx = Untranslated;
if (RemapSuccess)
- DestIdx = Dest.writeSerializedRecord(Record);
+ DestIdx = Dest.insertRecord(Record);
addMapping(DestIdx);
return Error::success();
}
--- /dev/null
+//===- 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 <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <cstring>
+
+using namespace llvm;
+using namespace llvm::codeview;
+
+namespace {
+
+struct HashedType {
+ uint64_t Hash;
+ const uint8_t *Data;
+ unsigned Size; // FIXME: Go to uint16_t?
+ TypeIndex Index;
+};
+
+/// Wrapper around a poitner to a HashedType. Hash and equality operations are
+/// based on data in the pointee.
+struct HashedTypePtr {
+ HashedTypePtr() = default;
+ HashedTypePtr(HashedType *Ptr) : Ptr(Ptr) {}
+
+ HashedType *Ptr = nullptr;
+};
+
+} // end anonymous namespace
+
+namespace llvm {
+
+template <> struct DenseMapInfo<HashedTypePtr> {
+ static inline HashedTypePtr getEmptyKey() { return HashedTypePtr(nullptr); }
+
+ static inline HashedTypePtr getTombstoneKey() {
+ return HashedTypePtr(reinterpret_cast<HashedType *>(1));
+ }
+
+ static unsigned getHashValue(HashedTypePtr Val) {
+ assert(Val.Ptr != getEmptyKey().Ptr && Val.Ptr != getTombstoneKey().Ptr);
+ return Val.Ptr->Hash;
+ }
+
+ static bool isEqual(HashedTypePtr LHSP, HashedTypePtr RHSP) {
+ HashedType *LHS = LHSP.Ptr;
+ HashedType *RHS = RHSP.Ptr;
+ if (RHS == getEmptyKey().Ptr || RHS == getTombstoneKey().Ptr)
+ return LHS == RHS;
+ if (LHS->Hash != RHS->Hash || LHS->Size != RHS->Size)
+ return false;
+ return ::memcmp(LHS->Data, RHS->Data, LHS->Size) == 0;
+ }
+};
+
+} // end namespace llvm
+
+/// Private implementation so that we don't leak our DenseMap instantiations to
+/// users.
+class llvm::codeview::TypeHasher {
+private:
+ /// Storage for type record provided by the caller. Records will outlive the
+ /// hasher object, so they should be allocated here.
+ BumpPtrAllocator &RecordStorage;
+
+ /// Storage for hash keys. These only need to live as long as the hashing
+ /// operation.
+ BumpPtrAllocator KeyStorage;
+
+ /// Hash table. We really want a DenseMap<ArrayRef<uint8_t>, TypeIndex> here,
+ /// but DenseMap is inefficient when the keys are long (like type records)
+ /// because it recomputes the hash value of every key when it grows. This
+ /// value type stores the hash out of line in KeyStorage, so that table
+ /// entries are small and easy to rehash.
+ DenseSet<HashedTypePtr> HashedRecords;
+
+public:
+ TypeHasher(BumpPtrAllocator &RecordStorage) : RecordStorage(RecordStorage) {}
+
+ void reset() { HashedRecords.clear(); }
+
+ /// Takes the bytes of type record, inserts them into the hash table, saves
+ /// them, and returns a pointer to an identical stable type record along with
+ /// its type index in the destination stream.
+ TypeIndex getOrCreateRecord(ArrayRef<uint8_t> &Record, TypeIndex TI);
+};
+
+TypeIndex TypeHasher::getOrCreateRecord(ArrayRef<uint8_t> &Record,
+ TypeIndex TI) {
+ assert(Record.size() < UINT32_MAX && "Record too big");
+ assert(Record.size() % 4 == 0 && "Record is not aligned to 4 bytes!");
+
+ // Compute the hash up front so we can store it in the key.
+ HashedType TempHashedType = {hash_value(Record), Record.data(),
+ unsigned(Record.size()), TI};
+ auto Result = HashedRecords.insert(HashedTypePtr(&TempHashedType));
+ HashedType *&Hashed = Result.first->Ptr;
+
+ if (Result.second) {
+ // This was a new type record. We need stable storage for both the key and
+ // the record. The record should outlive the hashing operation.
+ Hashed = KeyStorage.Allocate<HashedType>();
+ *Hashed = TempHashedType;
+
+ uint8_t *Stable = RecordStorage.Allocate<uint8_t>(Record.size());
+ memcpy(Stable, Record.data(), Record.size());
+ Hashed->Data = Stable;
+ assert(Hashed->Size == Record.size());
+ }
+
+ // Update the caller's copy of Record to point a stable copy.
+ Record = ArrayRef<uint8_t>(Hashed->Data, Hashed->Size);
+ return Hashed->Index;
+}
+
+TypeIndex TypeTableBuilder::nextTypeIndex() const {
+ return TypeIndex::fromArrayIndex(SeenRecords.size());
+}
+
+TypeTableBuilder::TypeTableBuilder(BumpPtrAllocator &Storage, bool Hash)
+ : RecordStorage(Storage) {
+ if (Hash)
+ Hasher = llvm::make_unique<TypeHasher>(Storage);
+}
+
+TypeTableBuilder::~TypeTableBuilder() = default;
+
+ArrayRef<ArrayRef<uint8_t>> TypeTableBuilder::records() const {
+ return SeenRecords;
+}
+
+void TypeTableBuilder::reset() {
+ if (Hasher)
+ Hasher->reset();
+ SeenRecords.clear();
+}
+
+TypeIndex TypeTableBuilder::insertRecordBytes(ArrayRef<uint8_t> &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<uint8_t>(Record.size());
+ memcpy(Stable, Record.data(), Record.size());
+ Record = ArrayRef<uint8_t>(Stable, Record.size());
+ SeenRecords.push_back(Record);
+ return NewTI;
+}
+
+TypeIndex TypeTableBuilder::insertRecord(const RemappedType &Record) {
+ TypeIndex TI;
+ ArrayRef<uint8_t> OriginalData = Record.OriginalRecord.RecordData;
+ if (Record.Mappings.empty()) {
+ // This record did not remap any type indices. Just write it.
+ return insertRecordBytes(OriginalData);
+ }
+
+ // At least one type index was remapped. Before we can hash it we have to
+ // copy the full record bytes, re-write each type index, then hash the copy.
+ // We do this in temporary storage since only the DenseMap can decide whether
+ // this record already exists, and if it does we don't want the memory to
+ // stick around.
+ RemapStorage.resize(OriginalData.size());
+ ::memcpy(&RemapStorage[0], OriginalData.data(), OriginalData.size());
+ uint8_t *ContentBegin = RemapStorage.data() + sizeof(RecordPrefix);
+ for (const auto &M : Record.Mappings) {
+ // First 4 bytes of every record are the record prefix, but the mapping
+ // offset is relative to the content which starts after.
+ *(TypeIndex *)(ContentBegin + M.first) = M.second;
+ }
+ auto RemapRef = makeArrayRef(RemapStorage);
+ return insertRecordBytes(RemapRef);
+}
+
+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;
+}
#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"
#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"
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;
};
return TypeDeserializer::deserializeAs<T>(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;
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<MemberRecord> Members;
virtual ~MemberRecordBase() = default;
virtual void map(yaml::IO &io) = 0;
- virtual void writeTo(FieldListRecordBuilder &FLRB) = 0;
+ virtual void writeTo(ContinuationRecordBuilder &CRB) = 0;
};
template <typename T> struct MemberRecordImpl : public MemberRecordBase {
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;
}
CVType
-LeafRecordImpl<FieldListRecord>::toCodeViewRecord(TypeTableBuilder &TTB) const {
- FieldListRecordBuilder FLRB(TTB);
- FLRB.begin();
+LeafRecordImpl<FieldListRecord>::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<OneMethodRecord>::mapping(IO &io, OneMethodRecord &Record) {
return make_error<CodeViewError>(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 {
ArrayRef<uint8_t> llvm::CodeViewYAML::toDebugT(ArrayRef<LeafRecord> 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!");
}
BinaryStreamWriter Writer(Output, support::little);
ExitOnError Err("Error writing type record to .debug$T section");
Err(Writer.writeInteger<uint32_t>(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!");
; 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)
; CHECK-NEXT: }
; CHECK: ContinuationIndex: <field list> (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: <field list> (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"
#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"
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);
}
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);
}
#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"
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);
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<CVType> TypeArray;
#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"
void SetUp() override {
Refs.clear();
TTB = make_unique<TypeTableBuilder>(Storage);
- FLRB = make_unique<FieldListRecordBuilder>(*TTB);
+ CRB = make_unique<ContinuationRecordBuilder>();
Symbols.clear();
}
void TearDown() override {
- FLRB.reset();
+ CRB.reset();
TTB.reset();
}
}
template <typename... T> void writeFieldList(T &&... MemberRecords) {
- FLRB->begin();
+ CRB->begin(ContinuationRecordKind::FieldList);
writeFieldListImpl(std::forward<T>(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();
}
template <typename RecType, typename... Rest>
void writeFieldListImpl(RecType &&Record, Rest &&... Records) {
- FLRB->writeMemberType(Record);
+ CRB->writeMemberType(Record);
writeFieldListImpl(std::forward<Rest>(Records)...);
}
template <typename RecType, typename... Rest>
void writeTypeRecordsImpl(RecType &&Record, Rest &&... Records) {
- TTB->writeKnownType(Record);
+ TTB->writeLeafType(Record);
writeTypeRecordsImpl(std::forward<Rest>(Records)...);
}
}
std::vector<SmallVector<TiReference, 4>> Refs;
- std::unique_ptr<FieldListRecordBuilder> FLRB;
+ std::unique_ptr<ContinuationRecordBuilder> CRB;
std::vector<CVSymbol> Symbols;
BumpPtrAllocator Storage;
};