From fed467eefb75e4af9ca1b534019eb5780aaf0984 Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Thu, 25 May 2017 23:36:16 +0000 Subject: [PATCH] [CV Type Merging] Find nested type indices faster. Merging two type streams is one of the most time consuming parts of generating a PDB, and as such it needs to be as fast as possible. The visitor abstractions used for interoperating nicely with many different types of inputs and outputs have been used widely and help greatly for testability and implementing tools, but the abstractions build up and get in the way of performance. This patch removes all of the visitation stuff from the type stream merger, essentially re-inventing the leaf / member switch and loop, but at a very low level. This allows us many other optimizations, such as not actually deserializing *any* records (even member records which don't describe their own length), as the operation of "figure out how long this record is" is somewhat faster than "figure out how long this record *and* get all its fields out". Furthermore, whereas before we had to deserialize, re-write type indices, then re-serialize, now we don't have to do any of those 3 steps. We just find out where the type indices are and pull them directly out of the byte stream and re-write them. This is worth a 50-60% performance increase. On top of all other optimizations that have been applied this week, I now get the following numbers when linking lld.exe and lld.pdb MSVC: 25.67s Before This Patch: 18.59s After This Patch: 8.92s So this is a huge performance win. Differential Revision: https://reviews.llvm.org/D33564 llvm-svn: 303935 --- .../DebugInfo/CodeView/TypeIndexDiscovery.h | 33 ++ .../llvm/DebugInfo/CodeView/TypeRecord.h | 10 +- llvm/lib/DebugInfo/CodeView/CMakeLists.txt | 1 + .../DebugInfo/CodeView/TypeIndexDiscovery.cpp | 371 +++++++++++++ .../DebugInfo/CodeView/TypeStreamMerger.cpp | 388 ++------------ .../DebugInfo/CodeView/CMakeLists.txt | 1 + .../DebugInfo/CodeView/ErrorChecking.h | 9 + .../CodeView/TypeIndexDiscoveryTest.cpp | 496 ++++++++++++++++++ 8 files changed, 954 insertions(+), 355 deletions(-) create mode 100644 llvm/include/llvm/DebugInfo/CodeView/TypeIndexDiscovery.h create mode 100644 llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp create mode 100644 llvm/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp diff --git a/llvm/include/llvm/DebugInfo/CodeView/TypeIndexDiscovery.h b/llvm/include/llvm/DebugInfo/CodeView/TypeIndexDiscovery.h new file mode 100644 index 000000000000..82ceb5038316 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/CodeView/TypeIndexDiscovery.h @@ -0,0 +1,33 @@ +//===- TypeIndexDiscovery.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_TYPEINDEXDISCOVERY_H +#define LLVM_DEBUGINFO_CODEVIEW_TYPEINDEXDISCOVERY_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace codeview { +enum class TiRefKind { TypeRef, IndexRef }; +struct TiReference { + TiRefKind Kind; + uint32_t Offset; + uint32_t Count; +}; + +void discoverTypeIndices(ArrayRef RecordData, + SmallVectorImpl &Refs); +void discoverTypeIndices(const CVType &Type, + SmallVectorImpl &Refs); +} +} + +#endif diff --git a/llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h b/llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h index fb71c72c80e1..92745ebfcded 100644 --- a/llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h +++ b/llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h @@ -279,15 +279,9 @@ public: Attrs(calcAttrs(PK, PM, PO, Size)) {} PointerRecord(TypeIndex ReferentType, PointerKind PK, PointerMode PM, - PointerOptions PO, uint8_t Size, - const MemberPointerInfo &Member) + PointerOptions PO, uint8_t Size, const MemberPointerInfo &MPI) : TypeRecord(TypeRecordKind::Pointer), ReferentType(ReferentType), - Attrs(calcAttrs(PK, PM, PO, Size)), MemberInfo(Member) {} - - PointerRecord(TypeIndex ReferentType, uint32_t Attrs, - const MemberPointerInfo &Member) - : TypeRecord(TypeRecordKind::Pointer), ReferentType(ReferentType), - Attrs(Attrs), MemberInfo(Member) {} + Attrs(calcAttrs(PK, PM, PO, Size)), MemberInfo(MPI) {} TypeIndex getReferentType() const { return ReferentType; } diff --git a/llvm/lib/DebugInfo/CodeView/CMakeLists.txt b/llvm/lib/DebugInfo/CodeView/CMakeLists.txt index 556ebf78622f..90193d07b95d 100644 --- a/llvm/lib/DebugInfo/CodeView/CMakeLists.txt +++ b/llvm/lib/DebugInfo/CodeView/CMakeLists.txt @@ -22,6 +22,7 @@ add_llvm_library(LLVMDebugInfoCodeView TypeDatabaseVisitor.cpp TypeDumpVisitor.cpp TypeIndex.cpp + TypeIndexDiscovery.cpp TypeRecordMapping.cpp TypeSerializer.cpp TypeStreamMerger.cpp diff --git a/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp b/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp new file mode 100644 index 000000000000..11e2e215303c --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp @@ -0,0 +1,371 @@ +//===- TypeIndexDiscovery.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::codeview; + +static inline MethodKind getMethodKind(uint16_t Attrs) { + Attrs &= uint16_t(MethodOptions::MethodKindMask); + Attrs >>= 2; + return MethodKind(Attrs); +} + +static inline bool isIntroVirtual(uint16_t Attrs) { + MethodKind MK = getMethodKind(Attrs); + return MK == MethodKind::IntroducingVirtual || + MK == MethodKind::PureIntroducingVirtual; +} + +static inline PointerMode getPointerMode(uint32_t Attrs) { + return static_cast((Attrs >> PointerRecord::PointerModeShift) & + PointerRecord::PointerModeMask); +} + +static inline bool isMemberPointer(uint32_t Attrs) { + PointerMode Mode = getPointerMode(Attrs); + return Mode == PointerMode::PointerToDataMember || + Mode == PointerMode::PointerToDataMember; +} + +static inline uint32_t getEncodedIntegerLength(ArrayRef Data) { + uint16_t N = support::endian::read16le(Data.data()); + if (N < LF_NUMERIC) + return 2; + + assert(N <= LF_UQUADWORD); + + constexpr uint32_t Sizes[] = { + 1, // LF_CHAR + 2, // LF_SHORT + 2, // LF_USHORT + 4, // LF_LONG + 4, // LF_ULONG + 4, // LF_REAL32 + 8, // LF_REAL64 + 10, // LF_REAL80 + 16, // LF_REAL128 + 8, // LF_QUADWORD + 8, // LF_UQUADWORD + }; + + return Sizes[N - LF_NUMERIC]; +} + +static inline uint32_t getCStringLength(ArrayRef Data) { + const char *S = reinterpret_cast(Data.data()); + return strlen(S) + 1; +} + +static void handleMethodOverloadList(ArrayRef Content, + SmallVectorImpl &Refs) { + uint32_t Offset = 0; + + while (!Content.empty()) { + // Array of: + // 0: Attrs + // 2: Padding + // 4: TypeIndex + // if (isIntroVirtual()) + // 8: VFTableOffset + + // At least 8 bytes are guaranteed. 4 extra bytes come iff function is an + // intro virtual. + uint32_t Len = 8; + + uint16_t Attrs = support::endian::read16le(Content.data()); + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + + if (LLVM_UNLIKELY(isIntroVirtual(Attrs))) + Len += 4; + Offset += Len; + Content = Content.drop_front(Len); + } +} + +static uint32_t handleBaseClass(ArrayRef Data, uint32_t Offset, + SmallVectorImpl &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + // 8: Encoded Integer + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + return 8 + getEncodedIntegerLength(Data.drop_front(8)); +} + +static uint32_t handleEnumerator(ArrayRef Data, uint32_t Offset, + SmallVectorImpl &Refs) { + // 0: Kind + // 2: Padding + // 4: Encoded Integer + // : Name + uint32_t Size = 4 + getEncodedIntegerLength(Data.drop_front(4)); + return Size + getCStringLength(Data.drop_front(Size)); +} + +static uint32_t handleDataMember(ArrayRef Data, uint32_t Offset, + SmallVectorImpl &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + // 8: Encoded Integer + // : Name + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + uint32_t Size = 8 + getEncodedIntegerLength(Data.drop_front(8)); + return Size + getCStringLength(Data.drop_front(Size)); +} + +static uint32_t handleOverloadedMethod(ArrayRef Data, uint32_t Offset, + SmallVectorImpl &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + // 8: Name + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + return 8 + getCStringLength(Data.drop_front(8)); +} + +static uint32_t handleOneMethod(ArrayRef Data, uint32_t Offset, + SmallVectorImpl &Refs) { + // 0: Kind + // 2: Attributes + // 4: Type + // if (isIntroVirtual) + // 8: VFTableOffset + // : Name + uint32_t Size = 8; + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + + uint16_t Attrs = support::endian::read16le(Data.drop_front(2).data()); + if (LLVM_UNLIKELY(isIntroVirtual(Attrs))) + Size += 4; + + return Size + getCStringLength(Data.drop_front(Size)); +} + +static uint32_t handleNestedType(ArrayRef Data, uint32_t Offset, + SmallVectorImpl &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + // 8: Name + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + return 8 + getCStringLength(Data.drop_front(8)); +} + +static uint32_t handleStaticDataMember(ArrayRef Data, uint32_t Offset, + SmallVectorImpl &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + // 8: Name + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + return 8 + getCStringLength(Data.drop_front(8)); +} + +static uint32_t handleVirtualBaseClass(ArrayRef Data, uint32_t Offset, + bool IsIndirect, + SmallVectorImpl &Refs) { + // 0: Kind + // 2: Attrs + // 4: TypeIndex + // 8: TypeIndex + // 12: Encoded Integer + // : Encoded Integer + uint32_t Size = 12; + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 2}); + Size += getEncodedIntegerLength(Data.drop_front(Size)); + Size += getEncodedIntegerLength(Data.drop_front(Size)); + return Size; +} + +static uint32_t handleVFPtr(ArrayRef Data, uint32_t Offset, + SmallVectorImpl &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + return 8; +} + +static uint32_t handleListContinuation(ArrayRef Data, uint32_t Offset, + SmallVectorImpl &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + return 8; +} + +static void handleFieldList(ArrayRef Content, + SmallVectorImpl &Refs) { + uint32_t Offset = 0; + uint32_t ThisLen = 0; + while (!Content.empty()) { + TypeLeafKind Kind = + static_cast(support::endian::read16le(Content.data())); + switch (Kind) { + case LF_BCLASS: + ThisLen = handleBaseClass(Content, Offset, Refs); + break; + case LF_ENUMERATE: + ThisLen = handleEnumerator(Content, Offset, Refs); + break; + case LF_MEMBER: + ThisLen = handleDataMember(Content, Offset, Refs); + break; + case LF_METHOD: + ThisLen = handleOverloadedMethod(Content, Offset, Refs); + break; + case LF_ONEMETHOD: + ThisLen = handleOneMethod(Content, Offset, Refs); + break; + case LF_NESTTYPE: + ThisLen = handleNestedType(Content, Offset, Refs); + break; + case LF_STMEMBER: + ThisLen = handleStaticDataMember(Content, Offset, Refs); + break; + case LF_VBCLASS: + case LF_IVBCLASS: + ThisLen = + handleVirtualBaseClass(Content, Offset, Kind == LF_VBCLASS, Refs); + break; + case LF_VFUNCTAB: + ThisLen = handleVFPtr(Content, Offset, Refs); + break; + case LF_INDEX: + ThisLen = handleListContinuation(Content, Offset, Refs); + break; + default: + return; + } + Content = Content.drop_front(ThisLen); + Offset += ThisLen; + if (!Content.empty()) { + uint8_t Pad = Content.front(); + if (Pad >= LF_PAD0) { + uint32_t Skip = Pad & 0x0F; + Content = Content.drop_front(Skip); + Offset += Skip; + } + } + } +} + +static void handlePointer(ArrayRef Content, + SmallVectorImpl &Refs) { + Refs.push_back({TiRefKind::TypeRef, 0, 1}); + + uint32_t Attrs = support::endian::read32le(Content.drop_front(4).data()); + if (isMemberPointer(Attrs)) + Refs.push_back({TiRefKind::TypeRef, 8, 1}); +} + +static void discoverTypeIndices(ArrayRef Content, TypeLeafKind Kind, + SmallVectorImpl &Refs) { + uint32_t Count; + // FIXME: In the future it would be nice if we could avoid hardcoding these + // values. One idea is to define some structures representing these types + // that would allow the use of offsetof(). + switch (Kind) { + case TypeLeafKind::LF_FUNC_ID: + Refs.push_back({TiRefKind::IndexRef, 0, 1}); + Refs.push_back({TiRefKind::TypeRef, 4, 1}); + break; + case TypeLeafKind::LF_MFUNC_ID: + Refs.push_back({TiRefKind::TypeRef, 0, 2}); + break; + case TypeLeafKind::LF_STRING_ID: + Refs.push_back({TiRefKind::IndexRef, 0, 1}); + break; + case TypeLeafKind::LF_SUBSTR_LIST: + Count = support::endian::read32le(Content.data()); + if (Count > 0) + Refs.push_back({TiRefKind::IndexRef, 4, Count}); + break; + case TypeLeafKind::LF_BUILDINFO: + Count = support::endian::read16le(Content.data()); + if (Count > 0) + Refs.push_back({TiRefKind::IndexRef, 2, Count}); + break; + case TypeLeafKind::LF_UDT_SRC_LINE: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); + Refs.push_back({TiRefKind::IndexRef, 4, 1}); + break; + case TypeLeafKind::LF_UDT_MOD_SRC_LINE: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); + break; + case TypeLeafKind::LF_MODIFIER: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); + break; + case TypeLeafKind::LF_PROCEDURE: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); + Refs.push_back({TiRefKind::TypeRef, 8, 1}); + break; + case TypeLeafKind::LF_MFUNCTION: + Refs.push_back({TiRefKind::TypeRef, 0, 3}); + Refs.push_back({TiRefKind::TypeRef, 16, 1}); + break; + case TypeLeafKind::LF_ARGLIST: + Count = support::endian::read32le(Content.data()); + if (Count > 0) + Refs.push_back({TiRefKind::TypeRef, 4, Count}); + break; + case TypeLeafKind::LF_ARRAY: + Refs.push_back({TiRefKind::TypeRef, 0, 2}); + break; + case TypeLeafKind::LF_CLASS: + case TypeLeafKind::LF_STRUCTURE: + case TypeLeafKind::LF_INTERFACE: + Refs.push_back({TiRefKind::TypeRef, 4, 3}); + break; + case TypeLeafKind::LF_UNION: + Refs.push_back({TiRefKind::TypeRef, 4, 1}); + break; + case TypeLeafKind::LF_ENUM: + Refs.push_back({TiRefKind::TypeRef, 4, 2}); + break; + case TypeLeafKind::LF_BITFIELD: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); + break; + case TypeLeafKind::LF_VFTABLE: + Refs.push_back({TiRefKind::TypeRef, 0, 2}); + break; + case TypeLeafKind::LF_VTSHAPE: + break; + case TypeLeafKind::LF_METHODLIST: + handleMethodOverloadList(Content, Refs); + break; + case TypeLeafKind::LF_FIELDLIST: + handleFieldList(Content, Refs); + break; + case TypeLeafKind::LF_POINTER: + handlePointer(Content, Refs); + break; + default: + break; + } +} + +void llvm::codeview::discoverTypeIndices(const CVType &Type, + SmallVectorImpl &Refs) { + ::discoverTypeIndices(Type.content(), Type.kind(), Refs); +} + +void llvm::codeview::discoverTypeIndices(ArrayRef RecordData, + SmallVectorImpl &Refs) { + const RecordPrefix *P = + reinterpret_cast(RecordData.data()); + TypeLeafKind K = static_cast(uint16_t(P->RecordKind)); + ::discoverTypeIndices(RecordData.drop_front(sizeof(RecordPrefix)), K, Refs); +} diff --git a/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp b/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp index c7f256b838a2..261e89e1d3a7 100644 --- a/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp +++ b/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp @@ -13,6 +13,7 @@ #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" @@ -66,20 +67,8 @@ public: static const TypeIndex Untranslated; -/// TypeVisitorCallbacks overrides. -#define TYPE_RECORD(EnumName, EnumVal, Name) \ - Error visitKnownRecord(CVType &CVR, Name##Record &Record) override; -#define MEMBER_RECORD(EnumName, EnumVal, Name) \ - Error visitKnownMember(CVMemberRecord &CVR, Name##Record &Record) override; -#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) -#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) -#include "llvm/DebugInfo/CodeView/TypeRecords.def" - - Error visitUnknownType(CVType &Record) override; - Error visitTypeBegin(CVType &Record) override; Error visitTypeEnd(CVType &Record) override; - Error visitMemberEnd(CVMemberRecord &Record) override; Error mergeTypesAndIds(TypeTableBuilder &DestIds, TypeTableBuilder &DestTypes, const CVTypeArray &IdsAndTypes); @@ -96,29 +85,25 @@ private: bool remapTypeIndex(TypeIndex &Idx); bool remapItemIndex(TypeIndex &Idx); - bool remapIndices(RemappedType &Record, ArrayRef TidOffs, - ArrayRef IidOffs) { + bool remapIndices(RemappedType &Record, ArrayRef Refs) { auto OriginalData = Record.OriginalRecord.content(); bool Success = true; - for (auto Off : TidOffs) { - ArrayRef Bytes = OriginalData.slice(Off, sizeof(TypeIndex)); - TypeIndex OldTI( - *reinterpret_cast(Bytes.data())); - TypeIndex NewTI = OldTI; - bool ThisSuccess = remapTypeIndex(NewTI); - if (ThisSuccess && NewTI != OldTI) - Record.Mappings.emplace_back(Off, NewTI); - Success &= ThisSuccess; - } - for (auto Off : IidOffs) { - ArrayRef Bytes = OriginalData.slice(Off, sizeof(TypeIndex)); - TypeIndex OldTI( - *reinterpret_cast(Bytes.data())); - TypeIndex NewTI = OldTI; - bool ThisSuccess = remapItemIndex(NewTI); - if (ThisSuccess && NewTI != OldTI) - Record.Mappings.emplace_back(Off, NewTI); - Success &= ThisSuccess; + for (auto &Ref : Refs) { + uint32_t Offset = Ref.Offset; + ArrayRef Bytes = + OriginalData.slice(Ref.Offset, sizeof(TypeIndex)); + ArrayRef TIs(reinterpret_cast(Bytes.data()), + Ref.Count); + for (auto TI : TIs) { + TypeIndex NewTI = TI; + bool ThisSuccess = (Ref.Kind == TiRefKind::IndexRef) + ? remapItemIndex(NewTI) + : remapTypeIndex(NewTI); + if (ThisSuccess && NewTI != TI) + Record.Mappings.emplace_back(Offset, NewTI); + Offset += sizeof(TypeIndex); + Success &= ThisSuccess; + } } return Success; } @@ -134,26 +119,6 @@ private: return llvm::make_error(cv_error_code::corrupt_record); } - template - Error writeKnownRecord(TypeTableBuilder &Dest, RecordType &R, - bool RemapSuccess) { - TypeIndex DestIdx = Untranslated; - if (RemapSuccess) - DestIdx = Dest.writeKnownType(R); - addMapping(DestIdx); - return Error::success(); - } - - template - Error writeKnownTypeRecord(RecordType &R, bool RemapSuccess) { - return writeKnownRecord(*DestTypeStream, R, RemapSuccess); - } - - template - Error writeKnownIdRecord(RecordType &R, bool RemapSuccess) { - return writeKnownRecord(*DestIdStream, R, RemapSuccess); - } - Error writeRecord(TypeTableBuilder &Dest, const RemappedType &Record, bool RemapSuccess) { TypeIndex DestIdx = Untranslated; @@ -178,15 +143,6 @@ private: return writeRecord(*DestIdStream, Record, RemapSuccess); } - template - Error writeMember(RecordType &R, bool RemapSuccess) { - if (RemapSuccess) - FieldListBuilder->writeMemberType(R); - else - HadUntranslatedMember = true; - return Error::success(); - } - Optional LastError; bool IsSecondPass = false; @@ -195,13 +151,10 @@ private: unsigned NumBadIndices = 0; - BumpPtrAllocator Allocator; - TypeIndex CurIndex{TypeIndex::FirstNonSimpleIndex}; TypeTableBuilder *DestIdStream = nullptr; TypeTableBuilder *DestTypeStream = nullptr; - std::unique_ptr FieldListBuilder; TypeServerHandler *Handler = nullptr; // If we're only mapping id records, this array contains the mapping for @@ -217,7 +170,25 @@ private: const TypeIndex TypeStreamMerger::Untranslated(SimpleTypeKind::NotTranslated); -Error TypeStreamMerger::visitTypeBegin(CVType &Rec) { return Error::success(); } +Error TypeStreamMerger::visitTypeBegin(CVType &Rec) { + RemappedType R(Rec); + SmallVector Refs; + discoverTypeIndices(Rec.RecordData, Refs); + bool Success = remapIndices(R, Refs); + switch (Rec.kind()) { + case TypeLeafKind::LF_FUNC_ID: + case TypeLeafKind::LF_MFUNC_ID: + case TypeLeafKind::LF_STRING_ID: + case TypeLeafKind::LF_SUBSTR_LIST: + case TypeLeafKind::LF_BUILDINFO: + case TypeLeafKind::LF_UDT_SRC_LINE: + case TypeLeafKind::LF_UDT_MOD_SRC_LINE: + return writeIdRecord(R, Success); + default: + return writeTypeRecord(R, Success); + } + return Error::success(); +} Error TypeStreamMerger::visitTypeEnd(CVType &Rec) { ++CurIndex; @@ -227,10 +198,6 @@ Error TypeStreamMerger::visitTypeEnd(CVType &Rec) { return Error::success(); } -Error TypeStreamMerger::visitMemberEnd(CVMemberRecord &Rec) { - return Error::success(); -} - void TypeStreamMerger::addMapping(TypeIndex Idx) { if (!IsSecondPass) { assert(IndexMap.size() == slotForIndex(CurIndex) && @@ -290,283 +257,6 @@ bool TypeStreamMerger::remapItemIndex(TypeIndex &Idx) { return remapIndex(Idx, IndexMap); } -//----------------------------------------------------------------------------// -// Item records -//----------------------------------------------------------------------------// - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, FuncIdRecord &R) { - assert(DestIdStream); - - RemappedType RR(CVR); - return writeIdRecord(RR, remapIndices(RR, {4}, {0})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, MemberFuncIdRecord &R) { - assert(DestIdStream); - - RemappedType RR(CVR); - return writeIdRecord(RR, remapIndices(RR, {0, 4}, {})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, StringIdRecord &R) { - assert(DestIdStream); - - RemappedType RR(CVR); - return writeIdRecord(RR, remapIndices(RR, {}, {0})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, StringListRecord &R) { - assert(DestIdStream); - - if (auto EC = TypeDeserializer::deserializeAs(CVR, R)) - return EC; - bool Success = true; - - for (TypeIndex &Id : R.StringIndices) - Success &= remapItemIndex(Id); - return writeKnownIdRecord(R, Success); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, BuildInfoRecord &R) { - assert(DestIdStream); - - if (auto EC = TypeDeserializer::deserializeAs(CVR, R)) - return EC; - - bool Success = true; - for (TypeIndex &Str : R.ArgIndices) - Success &= remapItemIndex(Str); - return writeKnownIdRecord(R, Success); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, UdtSourceLineRecord &R) { - assert(DestIdStream); - - RemappedType RR(CVR); - - // FIXME: Translate UdtSourceLineRecord into UdtModSourceLineRecords in the - // IPI stream. - return writeIdRecord(RR, remapIndices(RR, {0}, {4})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, - UdtModSourceLineRecord &R) { - assert(DestIdStream); - - RemappedType RR(CVR); - - // UdtModSourceLine Source File Ids are offsets into the global string table, - // not type indices. - // FIXME: We need to merge string table records for this to be valid. - return writeIdRecord(RR, remapIndices(RR, {0}, {})); -} - -//----------------------------------------------------------------------------// -// Type records -//----------------------------------------------------------------------------// - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, ModifierRecord &R) { - assert(DestTypeStream); - - RemappedType RR(CVR); - return writeTypeRecord(RR, remapIndices(RR, {0}, {})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, ProcedureRecord &R) { - assert(DestTypeStream); - - RemappedType RR(CVR); - return writeTypeRecord(RR, remapIndices(RR, {0, 8}, {})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, MemberFunctionRecord &R) { - assert(DestTypeStream); - - RemappedType RR(CVR); - return writeTypeRecord(RR, remapIndices(RR, {0, 4, 8, 16}, {})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, ArgListRecord &R) { - assert(DestTypeStream); - - if (auto EC = TypeDeserializer::deserializeAs(CVR, R)) - return EC; - - bool Success = true; - for (TypeIndex &Arg : R.ArgIndices) - Success &= remapTypeIndex(Arg); - - return writeKnownTypeRecord(R, Success); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, PointerRecord &R) { - assert(DestTypeStream); - - // Pointer records have a different number of TypeIndex mappings depending - // on whether or not it is a pointer to member. - if (auto EC = TypeDeserializer::deserializeAs(CVR, R)) - return EC; - - bool Success = remapTypeIndex(R.ReferentType); - if (R.isPointerToMember()) - Success &= remapTypeIndex(R.MemberInfo->ContainingType); - return writeKnownTypeRecord(R, Success); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, ArrayRecord &R) { - assert(DestTypeStream); - - RemappedType RR(CVR); - return writeTypeRecord(RR, remapIndices(RR, {0, 4}, {})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, ClassRecord &R) { - assert(DestTypeStream); - - RemappedType RR(CVR); - return writeTypeRecord(RR, remapIndices(RR, {4, 8, 12}, {})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, UnionRecord &R) { - assert(DestTypeStream); - - RemappedType RR(CVR); - return writeTypeRecord(RR, remapIndices(RR, {4}, {})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, EnumRecord &R) { - assert(DestTypeStream); - - RemappedType RR(CVR); - return writeTypeRecord(RR, remapIndices(RR, {4, 8}, {})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, BitFieldRecord &R) { - assert(DestTypeStream); - - RemappedType RR(CVR); - return writeTypeRecord(RR, remapIndices(RR, {0}, {})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, VFTableShapeRecord &R) { - assert(DestTypeStream); - - return writeTypeRecord(CVR); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, TypeServer2Record &R) { - assert(DestTypeStream); - - return writeTypeRecord(CVR); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, LabelRecord &R) { - assert(DestTypeStream); - - return writeTypeRecord(CVR); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, VFTableRecord &R) { - assert(DestTypeStream); - - RemappedType RR(CVR); - return writeTypeRecord(RR, remapIndices(RR, {0, 4}, {})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, - MethodOverloadListRecord &R) { - assert(DestTypeStream); - - if (auto EC = TypeDeserializer::deserializeAs(CVR, R)) - return EC; - - bool Success = true; - for (OneMethodRecord &Meth : R.Methods) - Success &= remapTypeIndex(Meth.Type); - return writeKnownTypeRecord(R, Success); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, FieldListRecord &R) { - assert(DestTypeStream); - // Visit the members inside the field list. - HadUntranslatedMember = false; - if (!FieldListBuilder) - FieldListBuilder = - llvm::make_unique(*DestTypeStream); - - FieldListBuilder->begin(); - if (auto EC = codeview::visitMemberRecordStream(CVR.content(), *this)) - return EC; - - // Write the record if we translated all field list members. - TypeIndex DestIdx = FieldListBuilder->end(!HadUntranslatedMember); - addMapping(HadUntranslatedMember ? Untranslated : DestIdx); - - return Error::success(); -} - -//----------------------------------------------------------------------------// -// Member records -//----------------------------------------------------------------------------// - -Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, - NestedTypeRecord &R) { - return writeMember(R, remapTypeIndex(R.Type)); -} - -Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, OneMethodRecord &R) { - bool Success = true; - Success &= remapTypeIndex(R.Type); - return writeMember(R, Success); -} - -Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, - OverloadedMethodRecord &R) { - return writeMember(R, remapTypeIndex(R.MethodList)); -} - -Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, - DataMemberRecord &R) { - return writeMember(R, remapTypeIndex(R.Type)); -} - -Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, - StaticDataMemberRecord &R) { - return writeMember(R, remapTypeIndex(R.Type)); -} - -Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, - EnumeratorRecord &R) { - return writeMember(R, true); -} - -Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, VFPtrRecord &R) { - return writeMember(R, remapTypeIndex(R.Type)); -} - -Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, BaseClassRecord &R) { - return writeMember(R, remapTypeIndex(R.Type)); -} - -Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, - VirtualBaseClassRecord &R) { - bool Success = true; - Success &= remapTypeIndex(R.BaseType); - Success &= remapTypeIndex(R.VBPtrType); - return writeMember(R, Success); -} - -Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, - ListContinuationRecord &R) { - return writeMember(R, remapTypeIndex(R.ContinuationIndex)); -} - -Error TypeStreamMerger::visitUnknownType(CVType &Rec) { - // We failed to translate a type. Translate this index as "not translated". - addMapping(TypeIndex(SimpleTypeKind::NotTranslated)); - return errorCorruptRecord(); -} - Error TypeStreamMerger::mergeTypeRecords(TypeTableBuilder &Dest, const CVTypeArray &Types) { DestTypeStream = &Dest; @@ -598,6 +288,10 @@ Error TypeStreamMerger::doit(const CVTypeArray &Types) { // We don't want to deserialize records. I guess this flag is poorly named, // but it really means "Don't deserialize records before switching on the // concrete type. + // FIXME: We can probably get even more speed here if we don't use the visitor + // pipeline here, but instead write the switch ourselves. I don't think it + // would buy us much since it's already pretty fast, but it's probably worth + // a few cycles. if (auto EC = codeview::visitTypeStream(Types, *this, VDS_BytesExternal, Handler)) return EC; diff --git a/llvm/unittests/DebugInfo/CodeView/CMakeLists.txt b/llvm/unittests/DebugInfo/CodeView/CMakeLists.txt index 8accd16acce9..aff3b6d09aab 100644 --- a/llvm/unittests/DebugInfo/CodeView/CMakeLists.txt +++ b/llvm/unittests/DebugInfo/CodeView/CMakeLists.txt @@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS set(DebugInfoCodeViewSources RandomAccessVisitorTest.cpp + TypeIndexDiscoveryTest.cpp ) add_llvm_unittest(DebugInfoCodeViewTests diff --git a/llvm/unittests/DebugInfo/CodeView/ErrorChecking.h b/llvm/unittests/DebugInfo/CodeView/ErrorChecking.h index 09310883bf58..4ca74c487b3e 100644 --- a/llvm/unittests/DebugInfo/CodeView/ErrorChecking.h +++ b/llvm/unittests/DebugInfo/CodeView/ErrorChecking.h @@ -26,6 +26,15 @@ consumeError(std::move(E)); \ } +#define ASSERT_EXPECTED(Exp) \ + { \ + auto E = Exp.takeError(); \ + bool Success = static_cast(E); \ + if (!Success) \ + consumeError(std::move(E)); \ + ASSERT_FALSE(Success); \ + } + #define EXPECT_EXPECTED(Exp) \ { \ auto E = Exp.takeError(); \ diff --git a/llvm/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp b/llvm/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp new file mode 100644 index 000000000000..4eca7777a1e2 --- /dev/null +++ b/llvm/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp @@ -0,0 +1,496 @@ +//===- llvm/unittest/DebugInfo/CodeView/TypeIndexDiscoveryTest.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/TypeIndexDiscovery.h" + +#include "ErrorChecking.h" +#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" +#include "llvm/Support/Allocator.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::codeview; + +class TypeIndexIteratorTest : public testing::Test { +public: + TypeIndexIteratorTest() {} + + void SetUp() override { + Refs.clear(); + TTB = make_unique(Storage); + FLRB = make_unique(*TTB); + } + + void TearDown() override { + FLRB.reset(); + TTB.reset(); + } + +protected: + template + bool checkTypeReferences(uint32_t RecordIndex, Indices &&... TIs) const { + EXPECT_EQ(sizeof...(Indices), countRefs(RecordIndex)); + return checkTypeReferencesImpl(RecordIndex, std::forward(TIs)...); + } + + template void writeFieldList(T &&... MemberRecords) { + FLRB->begin(); + writeFieldListImpl(std::forward(MemberRecords)...); + FLRB->end(true); + ASSERT_EQ(1u, TTB->records().size()); + discoverAllTypeIndices(); + } + + template void writeTypeRecords(T &&... Records) { + writeTypeRecordsImpl(std::forward(Records)...); + ASSERT_EQ(sizeof...(T), TTB->records().size()); + discoverAllTypeIndices(); + } + + std::unique_ptr TTB; + +private: + uint32_t countRefs(uint32_t RecordIndex) const { + auto &R = Refs[RecordIndex]; + uint32_t Count = 0; + for (auto &Ref : R) { + Count += Ref.Count; + } + return Count; + } + + bool checkOneTypeReference(uint32_t RecordIndex, ArrayRef RecordData, + TypeIndex TI) const { + RecordData = RecordData.drop_front(sizeof(RecordPrefix)); + auto &RefList = Refs[RecordIndex]; + for (auto &Ref : RefList) { + uint32_t Offset = Ref.Offset; + ArrayRef Loc = RecordData.drop_front(Offset); + ArrayRef Indices( + reinterpret_cast(Loc.data()), Ref.Count); + if (llvm::any_of(Indices, + [TI](const TypeIndex &Other) { return Other == TI; })) + return true; + } + return false; + } + + template + bool checkTypeReferencesImpl(uint32_t RecordIndex) const { + return true; + } + + template + bool checkTypeReferencesImpl(uint32_t RecordIndex, TypeIndex TI, + Indices &&... Rest) const { + ArrayRef Record = TTB->records()[RecordIndex]; + bool Success = checkOneTypeReference(RecordIndex, Record, TI); + EXPECT_TRUE(Success); + return Success & + checkTypeReferencesImpl(RecordIndex, std::forward(Rest)...); + } + + void discoverAllTypeIndices() { + Refs.resize(TTB->records().size()); + for (uint32_t I = 0; I < TTB->records().size(); ++I) { + ArrayRef Data = TTB->records()[I]; + discoverTypeIndices(Data, Refs[I]); + } + } + + // Helper function to write out a field list record with the given list + // of member records. + void writeFieldListImpl() {} + + template + void writeFieldListImpl(RecType &&Record, Rest &&... Records) { + FLRB->writeMemberType(Record); + writeFieldListImpl(std::forward(Records)...); + } + + // Helper function to write out a list of type records. + void writeTypeRecordsImpl() {} + + template + void writeTypeRecordsImpl(RecType &&Record, Rest &&... Records) { + TTB->writeKnownType(Record); + writeTypeRecordsImpl(std::forward(Records)...); + } + + std::vector> Refs; + std::unique_ptr FLRB; + BumpPtrAllocator Storage; +}; + +namespace leafs { +static FuncIdRecord FuncId(TypeIndex(1), TypeIndex(2), "FuncId"); +static MemberFuncIdRecord MemFuncId(TypeIndex(3), TypeIndex(4), "FuncId"); +static StringIdRecord StringId(TypeIndex(5), "TheString"); +static struct { + std::vector Ids = {TypeIndex(6), TypeIndex(7), TypeIndex(8)}; + StringListRecord Record{TypeRecordKind::StringList, Ids}; +} StringList; +static struct { + std::vector Ids = {TypeIndex(9), TypeIndex(10), TypeIndex(11)}; + BuildInfoRecord Record{Ids}; +} BuildInfo; +static UdtSourceLineRecord UdtSourceLine(TypeIndex(12), TypeIndex(13), 0); +static UdtModSourceLineRecord UdtModSourceLine(TypeIndex(14), TypeIndex(15), 0, + 0); +static ModifierRecord Modifier(TypeIndex(16), ModifierOptions::None); +static ProcedureRecord Procedure(TypeIndex(17), CallingConvention::PpcCall, + FunctionOptions::None, 0, TypeIndex(18)); +static MemberFunctionRecord MemberFunction(TypeIndex(19), TypeIndex(20), + TypeIndex(21), + CallingConvention::ThisCall, + FunctionOptions::None, 2, + TypeIndex(22), 0); +static struct { + std::vector Ids = {TypeIndex(23), TypeIndex(24), TypeIndex(25)}; + ArgListRecord Record{TypeRecordKind::ArgList, Ids}; +} ArgList; +static ArrayRecord Array(TypeIndex(26), TypeIndex(27), 10, "MyArray"); +static ClassRecord Class(TypeRecordKind::Class, 3, ClassOptions::None, + TypeIndex(28), TypeIndex(29), TypeIndex(30), 10, + "MyClass", "MyClassUniqueName"); +static ClassRecord Struct(TypeRecordKind::Struct, 3, ClassOptions::None, + TypeIndex(31), TypeIndex(32), TypeIndex(33), 10, + "MyClass", "MyClassUniqueName"); +static UnionRecord Union(1, ClassOptions::None, TypeIndex(34), 10, "MyUnion", + "MyUnionUniqueName"); +static EnumRecord Enum(1, ClassOptions::None, TypeIndex(35), "MyEnum", + "EnumUniqueName", TypeIndex(36)); +static BitFieldRecord BitField(TypeIndex(37), 1, 0); +static VFTableRecord VFTable(TypeIndex(38), TypeIndex(39), 1, "VFT", {}); +static VFTableShapeRecord VTableShape({}); +static struct { + const TypeIndex T1{40}; + const TypeIndex T2{41}; + const TypeIndex T3{42}; + const TypeIndex T4{43}; + + std::vector Methods{ + {T1, MemberAccess::Public, MethodKind::IntroducingVirtual, + MethodOptions::None, 0, "Method1"}, + {T2, MemberAccess::Public, MethodKind::PureVirtual, MethodOptions::None, + 0, "Method1"}, + {T3, MemberAccess::Public, MethodKind::PureIntroducingVirtual, + MethodOptions::None, 0, "Method1"}, + {T4, MemberAccess::Public, MethodKind::Static, MethodOptions::None, 0, + "Method1"}}; + + MethodOverloadListRecord Record{Methods}; +} MethodOverloadList; +static PointerRecord Pointer(TypeIndex(44), PointerKind::Near32, + PointerMode::Pointer, PointerOptions::Const, 3); +static PointerRecord MemberPointer( + TypeIndex(45), PointerKind::Near32, PointerMode::PointerToDataMember, + PointerOptions::Const, 3, + MemberPointerInfo(TypeIndex(46), + PointerToMemberRepresentation::GeneralData)); +} + +namespace members { +static BaseClassRecord BaseClass(MemberAccess::Public, TypeIndex(47), 0); +static EnumeratorRecord Enumerator(MemberAccess::Public, + APSInt(APInt(8, 3, false)), "Test"); +DataMemberRecord DataMember(MemberAccess::Public, TypeIndex(48), 0, "Test"); +OverloadedMethodRecord OverloadedMethod(3, TypeIndex(49), "MethodList"); +static struct { + const TypeIndex T1{50}; + const TypeIndex T2{51}; + const TypeIndex T3{52}; + const TypeIndex T4{53}; + OneMethodRecord R1{T1, + MemberAccess::Public, + MethodKind::IntroducingVirtual, + MethodOptions::None, + 0, + "M1"}; + OneMethodRecord R2{T2, + MemberAccess::Public, + MethodKind::PureVirtual, + MethodOptions::None, + 0, + "M2"}; + OneMethodRecord R3{T3, + MemberAccess::Public, + MethodKind::PureIntroducingVirtual, + MethodOptions::None, + 0, + "M3"}; + OneMethodRecord R4{T4, + MemberAccess::Protected, + MethodKind::Vanilla, + MethodOptions::CompilerGenerated, + 0, + "M4"}; +} OneMethod; +static NestedTypeRecord NestedType(TypeIndex(54), "MyClass"); +static StaticDataMemberRecord StaticDataMember(MemberAccess::Public, + TypeIndex(55), "Foo"); +static VirtualBaseClassRecord VirtualBaseClass(TypeRecordKind::VirtualBaseClass, + MemberAccess::Public, + TypeIndex(56), TypeIndex(57), 0, + 0); +static VFPtrRecord VFPtr(TypeIndex(58)); +static ListContinuationRecord Continuation(TypeIndex(59)); +} + +TEST_F(TypeIndexIteratorTest, FuncId) { + using namespace leafs; + writeTypeRecords(FuncId); + checkTypeReferences(0, FuncId.FunctionType, FuncId.ParentScope); +} + +TEST_F(TypeIndexIteratorTest, MemFuncId) { + using namespace leafs; + writeTypeRecords(MemFuncId); + checkTypeReferences(0, MemFuncId.ClassType, MemFuncId.FunctionType); +} + +TEST_F(TypeIndexIteratorTest, StringId) { + using namespace leafs; + writeTypeRecords(StringId); + checkTypeReferences(0, StringId.Id); +} + +TEST_F(TypeIndexIteratorTest, SubstrList) { + using namespace leafs; + writeTypeRecords(StringList.Record); + checkTypeReferences(0, StringList.Ids[0], StringList.Ids[1], + StringList.Ids[2]); +} + +TEST_F(TypeIndexIteratorTest, BuildInfo) { + using namespace leafs; + writeTypeRecords(BuildInfo.Record); + checkTypeReferences(0, BuildInfo.Ids[0], BuildInfo.Ids[1], BuildInfo.Ids[2]); +} + +TEST_F(TypeIndexIteratorTest, UdtSrcLine) { + using namespace leafs; + writeTypeRecords(UdtSourceLine); + checkTypeReferences(0, UdtSourceLine.UDT, UdtSourceLine.SourceFile); +} + +TEST_F(TypeIndexIteratorTest, UdtModSrcLine) { + using namespace leafs; + writeTypeRecords(UdtModSourceLine); + checkTypeReferences(0, UdtModSourceLine.UDT, UdtModSourceLine.SourceFile); +} + +TEST_F(TypeIndexIteratorTest, Modifier) { + using namespace leafs; + writeTypeRecords(Modifier); + checkTypeReferences(0, Modifier.ModifiedType); +} + +TEST_F(TypeIndexIteratorTest, Procedure) { + using namespace leafs; + writeTypeRecords(Procedure); + checkTypeReferences(0, Procedure.ReturnType, Procedure.ArgumentList); +} + +TEST_F(TypeIndexIteratorTest, MemFunc) { + using namespace leafs; + writeTypeRecords(MemberFunction); + checkTypeReferences(0, MemberFunction.ReturnType, MemberFunction.ClassType, + MemberFunction.ThisType, MemberFunction.ArgumentList); +} + +TEST_F(TypeIndexIteratorTest, ArgList) { + using namespace leafs; + writeTypeRecords(ArgList.Record); + checkTypeReferences(0, ArgList.Ids[0], ArgList.Ids[1], ArgList.Ids[2]); +} + +TEST_F(TypeIndexIteratorTest, Array) { + using namespace leafs; + writeTypeRecords(Array); + checkTypeReferences(0, Array.ElementType, Array.IndexType); +} + +TEST_F(TypeIndexIteratorTest, Class) { + using namespace leafs; + writeTypeRecords(Class); + checkTypeReferences(0, Class.FieldList, Class.DerivationList, + Class.VTableShape); +} + +TEST_F(TypeIndexIteratorTest, Struct) { + using namespace leafs; + writeTypeRecords(Struct); + checkTypeReferences(0, Struct.FieldList, Struct.DerivationList, + Struct.VTableShape); +} + +TEST_F(TypeIndexIteratorTest, Union) { + using namespace leafs; + writeTypeRecords(Union); + checkTypeReferences(0, Union.FieldList); +} + +TEST_F(TypeIndexIteratorTest, Enum) { + using namespace leafs; + writeTypeRecords(Enum); + checkTypeReferences(0, Enum.FieldList, Enum.UnderlyingType); +} + +TEST_F(TypeIndexIteratorTest, Bitfield) { + using namespace leafs; + writeTypeRecords(BitField); + checkTypeReferences(0, BitField.Type); +} + +TEST_F(TypeIndexIteratorTest, VTable) { + using namespace leafs; + writeTypeRecords(VFTable); + checkTypeReferences(0, VFTable.CompleteClass, VFTable.OverriddenVFTable); +} + +TEST_F(TypeIndexIteratorTest, VTShape) { + using namespace leafs; + writeTypeRecords(VTableShape); + checkTypeReferences(0); +} + +TEST_F(TypeIndexIteratorTest, OverloadList) { + using namespace leafs; + writeTypeRecords(MethodOverloadList.Record); + checkTypeReferences(0, MethodOverloadList.T1, MethodOverloadList.T2, + MethodOverloadList.T3, MethodOverloadList.T4); +} + +TEST_F(TypeIndexIteratorTest, Pointer) { + using namespace leafs; + writeTypeRecords(Pointer); + checkTypeReferences(0, Pointer.ReferentType); +} + +TEST_F(TypeIndexIteratorTest, MemberPointer) { + using namespace leafs; + writeTypeRecords(MemberPointer); + checkTypeReferences(0, MemberPointer.ReferentType, + MemberPointer.MemberInfo->ContainingType); +} + +TEST_F(TypeIndexIteratorTest, ManyTypes) { + + using namespace leafs; + writeTypeRecords(FuncId, MemFuncId, StringId, StringList.Record, + BuildInfo.Record, UdtSourceLine, UdtModSourceLine, Modifier, + Procedure, MemberFunction, ArgList.Record, Array, Class, + Union, Enum, BitField, VFTable, VTableShape, + MethodOverloadList.Record, Pointer, MemberPointer); + + checkTypeReferences(0, FuncId.FunctionType, FuncId.ParentScope); + checkTypeReferences(1, MemFuncId.ClassType, MemFuncId.FunctionType); + checkTypeReferences(2, StringId.Id); + checkTypeReferences(3, StringList.Ids[0], StringList.Ids[1], + StringList.Ids[2]); + checkTypeReferences(4, BuildInfo.Ids[0], BuildInfo.Ids[1], BuildInfo.Ids[2]); + checkTypeReferences(5, UdtSourceLine.UDT, UdtSourceLine.SourceFile); + checkTypeReferences(6, UdtModSourceLine.UDT, UdtModSourceLine.SourceFile); + checkTypeReferences(7, Modifier.ModifiedType); + checkTypeReferences(8, Procedure.ReturnType, Procedure.ArgumentList); + checkTypeReferences(9, MemberFunction.ReturnType, MemberFunction.ClassType, + MemberFunction.ThisType, MemberFunction.ArgumentList); + checkTypeReferences(10, ArgList.Ids[0], ArgList.Ids[1], ArgList.Ids[2]); + checkTypeReferences(11, Array.ElementType, Array.IndexType); + checkTypeReferences(12, Class.FieldList, Class.DerivationList, + Class.VTableShape); + checkTypeReferences(13, Union.FieldList); + checkTypeReferences(14, Enum.FieldList, Enum.UnderlyingType); + checkTypeReferences(15, BitField.Type); + checkTypeReferences(16, VFTable.CompleteClass, VFTable.OverriddenVFTable); + checkTypeReferences(17); + checkTypeReferences(18, MethodOverloadList.T1, MethodOverloadList.T2, + MethodOverloadList.T3, MethodOverloadList.T4); + checkTypeReferences(19, Pointer.ReferentType); + checkTypeReferences(20, MemberPointer.ReferentType, + MemberPointer.MemberInfo->ContainingType); +} + +TEST_F(TypeIndexIteratorTest, FieldListBaseClass) { + using namespace members; + writeFieldList(BaseClass); + checkTypeReferences(0, BaseClass.Type); +} + +TEST_F(TypeIndexIteratorTest, FieldListEnumerator) { + using namespace members; + writeFieldList(Enumerator); + checkTypeReferences(0); +} + +TEST_F(TypeIndexIteratorTest, FieldListMember) { + using namespace members; + writeFieldList(DataMember); + checkTypeReferences(0, DataMember.Type); +} + +TEST_F(TypeIndexIteratorTest, FieldListMethod) { + using namespace members; + writeFieldList(OverloadedMethod); + checkTypeReferences(0, OverloadedMethod.MethodList); +} + +TEST_F(TypeIndexIteratorTest, FieldListOneMethod) { + using namespace members; + writeFieldList(OneMethod.R1, OneMethod.R2, OneMethod.R3, OneMethod.R4); + checkTypeReferences(0, OneMethod.T1, OneMethod.T2, OneMethod.T3, + OneMethod.T4); +} + +TEST_F(TypeIndexIteratorTest, FieldListNestedType) { + using namespace members; + writeFieldList(NestedType); + checkTypeReferences(0, NestedType.Type); +} + +TEST_F(TypeIndexIteratorTest, FieldListStaticMember) { + using namespace members; + writeFieldList(StaticDataMember); + checkTypeReferences(0, StaticDataMember.Type); +} + +TEST_F(TypeIndexIteratorTest, FieldListVirtualBase) { + using namespace members; + writeFieldList(VirtualBaseClass); + checkTypeReferences(0, VirtualBaseClass.BaseType, VirtualBaseClass.VBPtrType); +} + +TEST_F(TypeIndexIteratorTest, FieldListVFTable) { + using namespace members; + writeFieldList(VFPtr); + checkTypeReferences(0, VFPtr.Type); +} + +TEST_F(TypeIndexIteratorTest, FieldListContinuation) { + using namespace members; + writeFieldList(Continuation); + checkTypeReferences(0, Continuation.ContinuationIndex); +} + +TEST_F(TypeIndexIteratorTest, ManyMembers) { + using namespace members; + writeFieldList(BaseClass, Enumerator, DataMember, OverloadedMethod, + OneMethod.R1, OneMethod.R2, OneMethod.R3, OneMethod.R4, + NestedType, StaticDataMember, VirtualBaseClass, VFPtr, + Continuation); + + checkTypeReferences( + 0, BaseClass.Type, DataMember.Type, OverloadedMethod.MethodList, + OneMethod.T1, OneMethod.T2, OneMethod.T3, OneMethod.T4, NestedType.Type, + StaticDataMember.Type, VirtualBaseClass.BaseType, + VirtualBaseClass.VBPtrType, VFPtr.Type, Continuation.ContinuationIndex); +} \ No newline at end of file