1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-25 12:12:47 +01:00

[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
This commit is contained in:
Zachary Turner 2017-05-25 23:36:16 +00:00
parent 11d1430f60
commit 57b40ea3a5
8 changed files with 954 additions and 355 deletions

View File

@ -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<uint8_t> RecordData,
SmallVectorImpl<TiReference> &Refs);
void discoverTypeIndices(const CVType &Type,
SmallVectorImpl<TiReference> &Refs);
}
}
#endif

View File

@ -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; }

View File

@ -22,6 +22,7 @@ add_llvm_library(LLVMDebugInfoCodeView
TypeDatabaseVisitor.cpp
TypeDumpVisitor.cpp
TypeIndex.cpp
TypeIndexDiscovery.cpp
TypeRecordMapping.cpp
TypeSerializer.cpp
TypeStreamMerger.cpp

View File

@ -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<PointerMode>((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<uint8_t> 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<uint8_t> Data) {
const char *S = reinterpret_cast<const char *>(Data.data());
return strlen(S) + 1;
}
static void handleMethodOverloadList(ArrayRef<uint8_t> Content,
SmallVectorImpl<TiReference> &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<uint8_t> Data, uint32_t Offset,
SmallVectorImpl<TiReference> &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<uint8_t> Data, uint32_t Offset,
SmallVectorImpl<TiReference> &Refs) {
// 0: Kind
// 2: Padding
// 4: Encoded Integer
// <next>: Name
uint32_t Size = 4 + getEncodedIntegerLength(Data.drop_front(4));
return Size + getCStringLength(Data.drop_front(Size));
}
static uint32_t handleDataMember(ArrayRef<uint8_t> Data, uint32_t Offset,
SmallVectorImpl<TiReference> &Refs) {
// 0: Kind
// 2: Padding
// 4: TypeIndex
// 8: Encoded Integer
// <next>: 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<uint8_t> Data, uint32_t Offset,
SmallVectorImpl<TiReference> &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<uint8_t> Data, uint32_t Offset,
SmallVectorImpl<TiReference> &Refs) {
// 0: Kind
// 2: Attributes
// 4: Type
// if (isIntroVirtual)
// 8: VFTableOffset
// <next>: 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<uint8_t> Data, uint32_t Offset,
SmallVectorImpl<TiReference> &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<uint8_t> Data, uint32_t Offset,
SmallVectorImpl<TiReference> &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<uint8_t> Data, uint32_t Offset,
bool IsIndirect,
SmallVectorImpl<TiReference> &Refs) {
// 0: Kind
// 2: Attrs
// 4: TypeIndex
// 8: TypeIndex
// 12: Encoded Integer
// <next>: 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<uint8_t> Data, uint32_t Offset,
SmallVectorImpl<TiReference> &Refs) {
// 0: Kind
// 2: Padding
// 4: TypeIndex
Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1});
return 8;
}
static uint32_t handleListContinuation(ArrayRef<uint8_t> Data, uint32_t Offset,
SmallVectorImpl<TiReference> &Refs) {
// 0: Kind
// 2: Padding
// 4: TypeIndex
Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1});
return 8;
}
static void handleFieldList(ArrayRef<uint8_t> Content,
SmallVectorImpl<TiReference> &Refs) {
uint32_t Offset = 0;
uint32_t ThisLen = 0;
while (!Content.empty()) {
TypeLeafKind Kind =
static_cast<TypeLeafKind>(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<uint8_t> Content,
SmallVectorImpl<TiReference> &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<uint8_t> Content, TypeLeafKind Kind,
SmallVectorImpl<TiReference> &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<TiReference> &Refs) {
::discoverTypeIndices(Type.content(), Type.kind(), Refs);
}
void llvm::codeview::discoverTypeIndices(ArrayRef<uint8_t> RecordData,
SmallVectorImpl<TiReference> &Refs) {
const RecordPrefix *P =
reinterpret_cast<const RecordPrefix *>(RecordData.data());
TypeLeafKind K = static_cast<TypeLeafKind>(uint16_t(P->RecordKind));
::discoverTypeIndices(RecordData.drop_front(sizeof(RecordPrefix)), K, Refs);
}

View File

@ -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<uint32_t> TidOffs,
ArrayRef<uint32_t> IidOffs) {
bool remapIndices(RemappedType &Record, ArrayRef<TiReference> Refs) {
auto OriginalData = Record.OriginalRecord.content();
bool Success = true;
for (auto Off : TidOffs) {
ArrayRef<uint8_t> Bytes = OriginalData.slice(Off, sizeof(TypeIndex));
TypeIndex OldTI(
*reinterpret_cast<const support::ulittle32_t *>(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<uint8_t> Bytes = OriginalData.slice(Off, sizeof(TypeIndex));
TypeIndex OldTI(
*reinterpret_cast<const support::ulittle32_t *>(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<uint8_t> Bytes =
OriginalData.slice(Ref.Offset, sizeof(TypeIndex));
ArrayRef<TypeIndex> TIs(reinterpret_cast<const TypeIndex *>(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<CodeViewError>(cv_error_code::corrupt_record);
}
template <typename RecordType>
Error writeKnownRecord(TypeTableBuilder &Dest, RecordType &R,
bool RemapSuccess) {
TypeIndex DestIdx = Untranslated;
if (RemapSuccess)
DestIdx = Dest.writeKnownType(R);
addMapping(DestIdx);
return Error::success();
}
template <typename RecordType>
Error writeKnownTypeRecord(RecordType &R, bool RemapSuccess) {
return writeKnownRecord(*DestTypeStream, R, RemapSuccess);
}
template <typename RecordType>
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 <typename RecordType>
Error writeMember(RecordType &R, bool RemapSuccess) {
if (RemapSuccess)
FieldListBuilder->writeMemberType(R);
else
HadUntranslatedMember = true;
return Error::success();
}
Optional<Error> 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<FieldListRecordBuilder> 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<TiReference, 32> 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<StringListRecord>(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<FieldListRecordBuilder>(*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;

View File

@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
set(DebugInfoCodeViewSources
RandomAccessVisitorTest.cpp
TypeIndexDiscoveryTest.cpp
)
add_llvm_unittest(DebugInfoCodeViewTests

View File

@ -26,6 +26,15 @@
consumeError(std::move(E)); \
}
#define ASSERT_EXPECTED(Exp) \
{ \
auto E = Exp.takeError(); \
bool Success = static_cast<bool>(E); \
if (!Success) \
consumeError(std::move(E)); \
ASSERT_FALSE(Success); \
}
#define EXPECT_EXPECTED(Exp) \
{ \
auto E = Exp.takeError(); \

View File

@ -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<TypeTableBuilder>(Storage);
FLRB = make_unique<FieldListRecordBuilder>(*TTB);
}
void TearDown() override {
FLRB.reset();
TTB.reset();
}
protected:
template <typename... Indices>
bool checkTypeReferences(uint32_t RecordIndex, Indices &&... TIs) const {
EXPECT_EQ(sizeof...(Indices), countRefs(RecordIndex));
return checkTypeReferencesImpl(RecordIndex, std::forward<Indices>(TIs)...);
}
template <typename... T> void writeFieldList(T &&... MemberRecords) {
FLRB->begin();
writeFieldListImpl(std::forward<T>(MemberRecords)...);
FLRB->end(true);
ASSERT_EQ(1u, TTB->records().size());
discoverAllTypeIndices();
}
template <typename... T> void writeTypeRecords(T &&... Records) {
writeTypeRecordsImpl(std::forward<T>(Records)...);
ASSERT_EQ(sizeof...(T), TTB->records().size());
discoverAllTypeIndices();
}
std::unique_ptr<TypeTableBuilder> 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<uint8_t> RecordData,
TypeIndex TI) const {
RecordData = RecordData.drop_front(sizeof(RecordPrefix));
auto &RefList = Refs[RecordIndex];
for (auto &Ref : RefList) {
uint32_t Offset = Ref.Offset;
ArrayRef<uint8_t> Loc = RecordData.drop_front(Offset);
ArrayRef<TypeIndex> Indices(
reinterpret_cast<const TypeIndex *>(Loc.data()), Ref.Count);
if (llvm::any_of(Indices,
[TI](const TypeIndex &Other) { return Other == TI; }))
return true;
}
return false;
}
template <typename... Indices>
bool checkTypeReferencesImpl(uint32_t RecordIndex) const {
return true;
}
template <typename... Indices>
bool checkTypeReferencesImpl(uint32_t RecordIndex, TypeIndex TI,
Indices &&... Rest) const {
ArrayRef<uint8_t> Record = TTB->records()[RecordIndex];
bool Success = checkOneTypeReference(RecordIndex, Record, TI);
EXPECT_TRUE(Success);
return Success &
checkTypeReferencesImpl(RecordIndex, std::forward<Indices>(Rest)...);
}
void discoverAllTypeIndices() {
Refs.resize(TTB->records().size());
for (uint32_t I = 0; I < TTB->records().size(); ++I) {
ArrayRef<uint8_t> 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 <typename RecType, typename... Rest>
void writeFieldListImpl(RecType &&Record, Rest &&... Records) {
FLRB->writeMemberType(Record);
writeFieldListImpl(std::forward<Rest>(Records)...);
}
// Helper function to write out a list of type records.
void writeTypeRecordsImpl() {}
template <typename RecType, typename... Rest>
void writeTypeRecordsImpl(RecType &&Record, Rest &&... Records) {
TTB->writeKnownType(Record);
writeTypeRecordsImpl(std::forward<Rest>(Records)...);
}
std::vector<SmallVector<TiReference, 4>> Refs;
std::unique_ptr<FieldListRecordBuilder> 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<TypeIndex> Ids = {TypeIndex(6), TypeIndex(7), TypeIndex(8)};
StringListRecord Record{TypeRecordKind::StringList, Ids};
} StringList;
static struct {
std::vector<TypeIndex> 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<TypeIndex> 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<OneMethodRecord> 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);
}