Index: llvm/include/llvm/DebugInfo/PDB/Native/RawTypes.h =================================================================== --- llvm/include/llvm/DebugInfo/PDB/Native/RawTypes.h +++ llvm/include/llvm/DebugInfo/PDB/Native/RawTypes.h @@ -343,7 +343,6 @@ char Reserved[8]; }; -constexpr int I = sizeof(SrcHeaderBlockEntry); static_assert(sizeof(SrcHeaderBlockEntry) == 40, "Incorrect struct size!"); } // namespace pdb Index: llvm/include/llvm/DebugInfo/PDB/Native/TpiHashing.h =================================================================== --- llvm/include/llvm/DebugInfo/PDB/Native/TpiHashing.h +++ llvm/include/llvm/DebugInfo/PDB/Native/TpiHashing.h @@ -18,6 +18,54 @@ Expected hashTypeRecord(const llvm::codeview::CVType &Type); +struct TagRecordHash { + explicit TagRecordHash(codeview::ClassRecord CR, uint32_t Full, + uint32_t Forward) + : Class(std::move(CR)), FullRecordHash(Full), ForwardDeclHash(Forward) { + State = 0; + } + + explicit TagRecordHash(codeview::EnumRecord ER, uint32_t Full, + uint32_t Forward) + : Enum(std::move(ER)), FullRecordHash(Full), ForwardDeclHash(Forward) { + State = 1; + } + + explicit TagRecordHash(codeview::UnionRecord UR, uint32_t Full, + uint32_t Forward) + : Union(std::move(UR)), FullRecordHash(Full), ForwardDeclHash(Forward) { + State = 2; + } + + uint32_t FullRecordHash; + uint32_t ForwardDeclHash; + + codeview::TagRecord &getRecord() { + switch (State) { + case 0: + return Class; + case 1: + return Enum; + case 2: + return Union; + } + llvm_unreachable("unreachable!"); + } + +private: + union { + codeview::ClassRecord Class; + codeview::EnumRecord Enum; + codeview::UnionRecord Union; + }; + + uint8_t State = 0; +}; + +/// Given a CVType referring to a class, structure, union, or enum, compute +/// the hash of its forward decl and full decl. +Expected hashTagRecord(const codeview::CVType &Type); + } // end namespace pdb } // end namespace llvm Index: llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h =================================================================== --- llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h +++ llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h @@ -58,10 +58,17 @@ codeview::LazyRandomTypeCollection &typeCollection() { return *Types; } + Expected + findFullDeclForForwardRef(codeview::TypeIndex ForwardRefTI) const; + BinarySubstreamRef getTypeRecordsSubstream() const; Error commit(); + void buildHashMap(); + + bool supportsTypeLookup() const; + private: PDBFile &Pdb; std::unique_ptr Stream; @@ -77,6 +84,8 @@ FixedStreamArray TypeIndexOffsets; HashTable HashAdjusters; + std::vector> HashMap; + const TpiStreamHeader *Header; }; } Index: llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp =================================================================== --- llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp +++ llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp @@ -50,6 +50,32 @@ } template +static Expected getTagRecordHashForUdt(const CVType &Rec) { + T Deserialized; + if (auto E = TypeDeserializer::deserializeAs(const_cast(Rec), + Deserialized)) + return std::move(E); + + ClassOptions Opts = Deserialized.getOptions(); + + bool ForwardRef = bool(Opts & ClassOptions::ForwardReference); + + uint32_t ThisRecordHash = getHashForUdt(Deserialized, Rec.data()); + + // If we don't have a forward ref we can't compute the hash of it from the + // full record because it requires hashing the entire buffer. + if (!ForwardRef) + return TagRecordHash{std::move(Deserialized), ThisRecordHash, 0}; + + bool Scoped = bool(Opts & ClassOptions::Scoped); + + StringRef NameToHash = + Scoped ? Deserialized.getUniqueName() : Deserialized.getName(); + uint32_t FullHash = hashStringV1(NameToHash); + return TagRecordHash{std::move(Deserialized), FullHash, ThisRecordHash}; +} + +template static Expected getSourceLineHash(const CVType &Rec) { T Deserialized; if (auto E = TypeDeserializer::deserializeAs(const_cast(Rec), @@ -60,6 +86,23 @@ return hashStringV1(StringRef(Buf, 4)); } +Expected llvm::pdb::hashTagRecord(const codeview::CVType &Type) { + switch (Type.kind()) { + case LF_CLASS: + case LF_STRUCTURE: + case LF_INTERFACE: + return getTagRecordHashForUdt(Type); + case LF_UNION: + return getTagRecordHashForUdt(Type); + case LF_ENUM: + return getTagRecordHashForUdt(Type); + default: + assert(false && "Type is not a tag record!"); + } + return make_error("Invalid record type", + inconvertibleErrorCode()); +} + Expected llvm::pdb::hashTypeRecord(const CVType &Rec) { switch (Rec.kind()) { case LF_CLASS: Index: llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp =================================================================== --- llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp +++ llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp @@ -11,8 +11,11 @@ #include "llvm/ADT/iterator_range.h" #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/RawConstants.h" #include "llvm/DebugInfo/PDB/Native/RawError.h" @@ -140,6 +143,64 @@ uint32_t TpiStream::getNumHashBuckets() const { return Header->NumHashBuckets; } uint32_t TpiStream::getHashKeySize() const { return Header->HashKeySize; } +void TpiStream::buildHashMap() { + if (!HashMap.empty()) + return; + if (HashValues.empty()) + return; + + HashMap.resize(Header->NumHashBuckets); + + TypeIndex TIB{Header->TypeIndexBegin}; + TypeIndex TIE{Header->TypeIndexEnd}; + while (TIB < TIE) { + uint32_t HV = HashValues[TIB.toArrayIndex()]; + HashMap[HV].push_back(TIB++); + } +} + +bool TpiStream::supportsTypeLookup() const { return !HashMap.empty(); } + +Expected +TpiStream::findFullDeclForForwardRef(TypeIndex ForwardRefTI) const { + CVType F = Types->getType(ForwardRefTI); + if (!isForwardRef(F)) + return ForwardRefTI; + + Expected ForwardTRH = hashTagRecord(F); + if (!ForwardTRH) + return ForwardTRH.takeError(); + + TagRecordHash Copy = std::move(*ForwardTRH); + uint32_t BucketIdx = ForwardTRH->FullRecordHash % Header->NumHashBuckets; + + for (TypeIndex TI : HashMap[BucketIdx]) { + CVType CVT = Types->getType(TI); + if (CVT.kind() != F.kind()) + continue; + + Expected FullTRH = hashTagRecord(CVT); + if (!FullTRH) + return FullTRH.takeError(); + if (ForwardTRH->FullRecordHash != FullTRH->FullRecordHash) + continue; + TagRecord &ForwardTR = ForwardTRH->getRecord(); + TagRecord &FullTR = FullTRH->getRecord(); + + if (!ForwardTR.hasUniqueName()) { + if (ForwardTR.getName() == FullTR.getName()) + return TI; + continue; + } + + if (!FullTR.hasUniqueName()) + continue; + if (ForwardTR.getUniqueName() == FullTR.getUniqueName()) + return TI; + } + return ForwardRefTI; +} + BinarySubstreamRef TpiStream::getTypeRecordsSubstream() const { return TypeRecordsSubstream; } Index: llvm/test/DebugInfo/PDB/Inputs/every-class.cpp =================================================================== --- /dev/null +++ llvm/test/DebugInfo/PDB/Inputs/every-class.cpp @@ -0,0 +1,61 @@ +// Build with "cl.exe /Z7 /GR- /GS- /GX- every-class.cpp /link /debug:full /nodefaultlib /incremental:no /entry:main" + +#include + +// clang-format off +void *__purecall = 0; + +void __cdecl operator delete(void *, unsigned int) {} +void __cdecl operator delete(void *, unsigned __int64) {} + +struct Nothing {}; +struct Constructor { Constructor() {} }; +struct Assignment { + Assignment &operator=(Assignment Other) { return *this; } +}; +struct Cast { + operator int() { return 42; } +}; + +struct Nested { + struct F {}; +}; +struct Operator { + int operator+(int X) { return 42; } +}; + +class Class {}; + +union Union {}; + +enum class Enum {A}; + + +template void f(T t) {} + +int main(int argc, char **argv) { + struct Scoped {}; + + struct { } Anonymous; + + f(Nothing{}); + f(Constructor{}); + f(Assignment{}); + f(Cast{}); + f(Nested{}); + f(Operator{}); + f(Nested::F{}); + f(Scoped{}); + f(Class{}); + f(Union{}); + f(Anonymous); + f(Enum::A); + + + f(Nothing{}); + f(Nothing{}); + f(Nothing{}); + f<__unaligned Nothing>(Nothing{}); + + return 0; +} Index: llvm/test/DebugInfo/PDB/every-type.test =================================================================== --- llvm/test/DebugInfo/PDB/every-type.test +++ llvm/test/DebugInfo/PDB/every-type.test @@ -9,12 +9,14 @@ RUN: llvm-pdbutil dump -type-index=0x101A,0x102C,0x103D,0x104D,0x1098,0x10AA,0x10AC \ -RUN: -dependents %p/Inputs/every-type.pdb | FileCheck --check-prefix=TYPES %s +RUN: -dont-resolve-forward-refs -dependents %p/Inputs/every-type.pdb \ +RUN: | FileCheck --check-prefix=TYPES %s RUN: llvm-pdbutil pdb2yaml -tpi-stream -ipi-stream %p/Inputs/every-type.pdb > %t.pdb.yaml RUN: llvm-pdbutil yaml2pdb -pdb=%t.yaml.pdb %t.pdb.yaml RUN: llvm-pdbutil dump -type-index=0x101A,0x102C,0x103D,0x104D,0x1098,0x10AA,0x10AC \ -RUN: -dependents %t.yaml.pdb | FileCheck --check-prefix=TYPES %s +RUN: -dependents -dont-resolve-forward-refs %t.yaml.pdb \ +RUN: | FileCheck --check-prefix=TYPES %s TYPES: Types (TPI Stream) TYPES-NEXT: ============================================================ Index: llvm/test/DebugInfo/PDB/pdb-resolve-forward-refs.test =================================================================== --- /dev/null +++ llvm/test/DebugInfo/PDB/pdb-resolve-forward-refs.test @@ -0,0 +1,98 @@ +; RUN: llvm-pdbutil dump -types %p/Inputs/every-class.pdb \ +; RUN: | FileCheck %s + +; CHECK: Types (TPI Stream) +; CHECK: ============================================================ +; CHECK: Showing 157 records +; CHECK: 0x1008 | LF_STRUCTURE [size = 124] `main::__l2::` +; CHECK: unique name: `.?AU@?1??main@@YAHHPEAPEAD@Z@`aa6523bc` +; CHECK: vtable: , base list: , field list: 0x1007 +; CHECK: options: has unique name | scoped, sizeof 1 +; CHECK: 0x1009 | LF_STRUCTURE [size = 88] `main::__l2::Scoped` +; CHECK: unique name: `.?AUScoped@?1??main@@YAHHPEAPEAD@Z@`aa6523bc` +; CHECK: vtable: , base list: , field list: 0x1007 +; CHECK: options: has unique name | scoped, sizeof 1 +; CHECK: 0x1054 | LF_STRUCTURE [size = 48] `Nested::F` +; CHECK: unique name: `.?AUF@Nested@@` +; CHECK: vtable: , base list: , field list: +; CHECK: options: forward ref (-> 0x1057) | has unique name | is nested, sizeof 0 +; CHECK: 0x1056 | LF_STRUCTURE [size = 44] `Nested` +; CHECK: unique name: `.?AUNested@@` +; CHECK: vtable: , base list: , field list: 0x1055 +; CHECK: options: contains nested class | has unique name, sizeof 1 +; CHECK: 0x1057 | LF_STRUCTURE [size = 48] `Nested::F` +; CHECK: unique name: `.?AUF@Nested@@` +; CHECK: vtable: , base list: , field list: 0x1007 +; CHECK: options: has unique name | is nested, sizeof 1 +; CHECK: 0x1058 | LF_STRUCTURE [size = 52] `Constructor` +; CHECK: unique name: `.?AUConstructor@@` +; CHECK: vtable: , base list: , field list: +; CHECK: options: forward ref (-> 0x105C) | has unique name, sizeof 0 +; CHECK: 0x105C | LF_STRUCTURE [size = 52] `Constructor` +; CHECK: unique name: `.?AUConstructor@@` +; CHECK: vtable: , base list: , field list: 0x105B +; CHECK: options: has ctor / dtor | has unique name, sizeof 1 +; CHECK: 0x105D | LF_CLASS [size = 40] `Class` +; CHECK: unique name: `.?AVClass@@` +; CHECK: vtable: , base list: , field list: 0x1007 +; CHECK: options: has unique name, sizeof 1 +; CHECK: 0x105E | LF_UNION [size = 32] `Union` +; CHECK: unique name: `.?ATUnion@@` +; CHECK: field list: 0x1007 +; CHECK: options: has unique name | sealed, sizeof 1 +; CHECK: 0x105F | LF_STRUCTURE [size = 48] `Operator` +; CHECK: unique name: `.?AUOperator@@` +; CHECK: vtable: , base list: , field list: +; CHECK: options: forward ref (-> 0x1064) | has unique name, sizeof 0 +; CHECK: 0x1064 | LF_STRUCTURE [size = 48] `Operator` +; CHECK: unique name: `.?AUOperator@@` +; CHECK: vtable: , base list: , field list: 0x1063 +; CHECK: options: has unique name | overloaded operator, sizeof 1 +; CHECK: 0x1066 | LF_ENUM [size = 36] `Enum` +; CHECK: unique name: `.?AW4Enum@@` +; CHECK: field list: 0x1065, underlying type: 0x0074 (int) +; CHECK: options: has unique name +; CHECK: 0x1067 | LF_STRUCTURE [size = 40] `Cast` +; CHECK: unique name: `.?AUCast@@` +; CHECK: vtable: , base list: , field list: +; CHECK: options: forward ref (-> 0x106B) | has unique name, sizeof 0 +; CHECK: 0x106B | LF_STRUCTURE [size = 40] `Cast` +; CHECK: unique name: `.?AUCast@@` +; CHECK: vtable: , base list: , field list: 0x106A +; CHECK: options: conversion operator | has unique name | overloaded operator, sizeof 1 +; CHECK: 0x106C | LF_STRUCTURE [size = 44] `Nothing` +; CHECK: unique name: `.?AUNothing@@` +; CHECK: vtable: , base list: , field list: 0x1007 +; CHECK: options: has unique name, sizeof 1 +; CHECK: 0x106D | LF_STRUCTURE [size = 52] `Assignment` +; CHECK: unique name: `.?AUAssignment@@` +; CHECK: vtable: , base list: , field list: +; CHECK: options: forward ref (-> 0x1073) | has unique name, sizeof 0 +; CHECK: 0x1073 | LF_STRUCTURE [size = 52] `Assignment` +; CHECK: unique name: `.?AUAssignment@@` +; CHECK: vtable: , base list: , field list: 0x1072 +; CHECK: options: has unique name | overloaded operator | overloaded operator=, sizeof 1 +; CHECK: 0x1074 | LF_STRUCTURE [size = 44] `Nothing` +; CHECK: unique name: `.?AUNothing@@` +; CHECK: vtable: , base list: , field list: +; CHECK: options: forward ref (<- 0x106C) | has unique name, sizeof 0 +; CHECK: 0x1081 | LF_UNION [size = 32] `Union` +; CHECK: unique name: `.?ATUnion@@` +; CHECK: field list: +; CHECK: options: forward ref (<- 0x105E) | has unique name, sizeof 0 +; CHECK: 0x1084 | LF_STRUCTURE [size = 124] `main::__l2::` +; CHECK: unique name: `.?AU@?1??main@@YAHHPEAPEAD@Z@`aa6523bc` +; CHECK: vtable: , base list: , field list: +; CHECK: options: forward ref (<- 0x1008) | has unique name | scoped, sizeof 0 +; CHECK: 0x108E | LF_STRUCTURE [size = 44] `Nested` +; CHECK: unique name: `.?AUNested@@` +; CHECK: vtable: , base list: , field list: +; CHECK: options: forward ref (<- 0x1056) | has unique name, sizeof 0 +; CHECK: 0x1095 | LF_STRUCTURE [size = 88] `main::__l2::Scoped` +; CHECK: unique name: `.?AUScoped@?1??main@@YAHHPEAPEAD@Z@`aa6523bc` +; CHECK: vtable: , base list: , field list: +; CHECK: options: forward ref (<- 0x1009) | has unique name | scoped, sizeof 0 +; CHECK: 0x1098 | LF_CLASS [size = 40] `Class` +; CHECK: unique name: `.?AVClass@@` +; CHECK: vtable: , base list: , field list: +; CHECK: options: forward ref (<- 0x105D) | has unique name, sizeof 0 Index: llvm/test/DebugInfo/PDB/pdbdump-headers.test =================================================================== --- llvm/test/DebugInfo/PDB/pdbdump-headers.test +++ llvm/test/DebugInfo/PDB/pdbdump-headers.test @@ -1,7 +1,9 @@ -; RUN: llvm-pdbutil dump -all %p/Inputs/empty.pdb | FileCheck -check-prefix=ALL %s +; RUN: llvm-pdbutil dump -all -dont-resolve-forward-refs %p/Inputs/empty.pdb \ +; RUN: | FileCheck -check-prefix=ALL %s ; RUN: llvm-pdbutil dump -summary -modules -files \ ; RUN: %p/Inputs/big-read.pdb | FileCheck -check-prefix=BIG %s -; RUN: not llvm-pdbutil dump -summary %p/Inputs/bad-block-size.pdb 2>&1 | FileCheck -check-prefix=BAD-BLOCK-SIZE %s +; RUN: not llvm-pdbutil dump -summary %p/Inputs/bad-block-size.pdb 2>&1 \ +; RUN: | FileCheck -check-prefix=BAD-BLOCK-SIZE %s ALL: Summary ALL-NEXT: ============================================================ Index: llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp =================================================================== --- llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp +++ llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp @@ -1241,13 +1241,13 @@ dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types, uint32_t NumTypeRecords, uint32_t NumHashBuckets, FixedStreamArray HashValues, - bool Bytes, bool Extras) { + TpiStream *Stream, bool Bytes, bool Extras) { Printer.formatLine("Showing {0:N} records", NumTypeRecords); uint32_t Width = NumDigits(TypeIndex::FirstNonSimpleIndex + NumTypeRecords); MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, - NumHashBuckets, HashValues); + NumHashBuckets, HashValues, Stream); if (auto EC = codeview::visitTypeStream(Types, V)) { Printer.formatLine("An error occurred dumping type records: {0}", @@ -1263,7 +1263,8 @@ NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords()); MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, - Stream.getNumHashBuckets(), Stream.getHashValues()); + Stream.getNumHashBuckets(), Stream.getHashValues(), + &Stream); if (opts::dump::DumpTypeDependents) { // If we need to dump all dependents, then iterate each index and find @@ -1325,7 +1326,8 @@ Types.reset(Reader, 100); if (opts::dump::DumpTypes) { - dumpFullTypeStream(P, Types, 0, 0, {}, opts::dump::DumpTypeData, false); + dumpFullTypeStream(P, Types, 0, 0, {}, nullptr, opts::dump::DumpTypeData, + false); } else if (opts::dump::DumpTypeExtras) { auto LocalHashes = LocallyHashedType::hashTypeCollection(Types); auto GlobalHashes = GloballyHashedType::hashTypeCollection(Types); @@ -1394,11 +1396,14 @@ auto &Types = (StreamIdx == StreamTPI) ? File.types() : File.ids(); + // Enable resolving forward decls. + Stream.buildHashMap(); + if (DumpTypes || !Indices.empty()) { if (Indices.empty()) dumpFullTypeStream(P, Types, Stream.getNumTypeRecords(), Stream.getNumHashBuckets(), Stream.getHashValues(), - DumpBytes, DumpExtras); + &Stream, DumpBytes, DumpExtras); else { std::vector TiList(Indices.begin(), Indices.end()); dumpPartialTypeStream(P, Types, Stream, TiList, DumpBytes, DumpExtras, Index: llvm/tools/llvm-pdbutil/MinimalTypeDumper.h =================================================================== --- llvm/tools/llvm-pdbutil/MinimalTypeDumper.h +++ llvm/tools/llvm-pdbutil/MinimalTypeDumper.h @@ -20,15 +20,18 @@ namespace pdb { class LinePrinter; +class TpiStream; class MinimalTypeDumpVisitor : public codeview::TypeVisitorCallbacks { public: MinimalTypeDumpVisitor(LinePrinter &P, uint32_t Width, bool RecordBytes, bool Hashes, codeview::LazyRandomTypeCollection &Types, uint32_t NumHashBuckets, - FixedStreamArray HashValues) + FixedStreamArray HashValues, + pdb::TpiStream *Stream) : P(P), Width(Width), RecordBytes(RecordBytes), Hashes(Hashes), - Types(Types), NumHashBuckets(NumHashBuckets), HashValues(HashValues) {} + Types(Types), NumHashBuckets(NumHashBuckets), HashValues(HashValues), + Stream(Stream) {} Error visitTypeBegin(codeview::CVType &Record, codeview::TypeIndex Index) override; @@ -55,7 +58,9 @@ bool Hashes = false; codeview::LazyRandomTypeCollection &Types; uint32_t NumHashBuckets; + codeview::TypeIndex CurrentTypeIndex; FixedStreamArray HashValues; + pdb::TpiStream *Stream = nullptr; }; } // namespace pdb } // namespace llvm Index: llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp =================================================================== --- llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp +++ llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp @@ -12,6 +12,7 @@ #include "FormatUtil.h" #include "LinePrinter.h" +#include "llvm-pdbutil.h" #include "llvm/DebugInfo/CodeView/CVRecord.h" #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" #include "llvm/DebugInfo/CodeView/CodeView.h" @@ -19,6 +20,7 @@ #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/PDB/Native/TpiHashing.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MathExtras.h" @@ -27,15 +29,37 @@ using namespace llvm::pdb; static std::string formatClassOptions(uint32_t IndentLevel, - ClassOptions Options) { + ClassOptions Options, TpiStream *Stream, + TypeIndex CurrentTypeIndex) { std::vector Opts; + + if (Stream && Stream->supportsTypeLookup() && + !opts::dump::DontResolveForwardRefs && + ((Options & ClassOptions::ForwardReference) != ClassOptions::None)) { + // If we're able to resolve forward references, do that. + Expected ETI = + Stream->findFullDeclForForwardRef(CurrentTypeIndex); + if (!ETI) { + consumeError(ETI.takeError()); + PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref (???)"); + } else { + const char *Direction = (*ETI == CurrentTypeIndex) + ? "=" + : ((*ETI < CurrentTypeIndex) ? "<-" : "->"); + std::string Formatted = + formatv("forward ref ({0} {1})", Direction, *ETI).str(); + PUSH_FLAG(ClassOptions, ForwardReference, Options, std::move(Formatted)); + } + } else { + PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref"); + } + PUSH_FLAG(ClassOptions, HasConstructorOrDestructor, Options, "has ctor / dtor"); PUSH_FLAG(ClassOptions, ContainsNestedClass, Options, "contains nested class"); PUSH_FLAG(ClassOptions, HasConversionOperator, Options, "conversion operator"); - PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref"); PUSH_FLAG(ClassOptions, HasUniqueName, Options, "has unique name"); PUSH_FLAG(ClassOptions, Intrinsic, Options, "intrin"); PUSH_FLAG(ClassOptions, Nested, Options, "is nested"); @@ -194,6 +218,7 @@ } Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) { + CurrentTypeIndex = Index; // formatLine puts the newline at the beginning, so we use formatLine here // to start a new line, and then individual visit methods use format to // append to the existing line. @@ -304,7 +329,8 @@ P.formatLine("vtable: {0}, base list: {1}, field list: {2}", Class.VTableShape, Class.DerivationList, Class.FieldList); P.formatLine("options: {0}, sizeof {1}", - formatClassOptions(P.getIndentLevel(), Class.Options), + formatClassOptions(P.getIndentLevel(), Class.Options, Stream, + CurrentTypeIndex), Class.Size); return Error::success(); } @@ -316,7 +342,8 @@ P.formatLine("unique name: `{0}`", Union.UniqueName); P.formatLine("field list: {0}", Union.FieldList); P.formatLine("options: {0}, sizeof {1}", - formatClassOptions(P.getIndentLevel(), Union.Options), + formatClassOptions(P.getIndentLevel(), Union.Options, Stream, + CurrentTypeIndex), Union.Size); return Error::success(); } @@ -328,7 +355,8 @@ P.formatLine("field list: {0}, underlying type: {1}", Enum.FieldList, Enum.UnderlyingType); P.formatLine("options: {0}", - formatClassOptions(P.getIndentLevel(), Enum.Options)); + formatClassOptions(P.getIndentLevel(), Enum.Options, Stream, + CurrentTypeIndex)); return Error::success(); } Index: llvm/tools/llvm-pdbutil/llvm-pdbutil.h =================================================================== --- llvm/tools/llvm-pdbutil/llvm-pdbutil.h +++ llvm/tools/llvm-pdbutil/llvm-pdbutil.h @@ -160,6 +160,7 @@ extern llvm::cl::list DumpIdIndex; extern llvm::cl::opt DumpModi; extern llvm::cl::opt JustMyCode; +extern llvm::cl::opt DontResolveForwardRefs; extern llvm::cl::opt DumpSymbols; extern llvm::cl::opt DumpSymRecordBytes; extern llvm::cl::opt DumpGSIRecords; Index: llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp =================================================================== --- llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp @@ -183,6 +183,8 @@ cl::sub(DiaDumpSubcommand)); static cl::opt Pointers("pointers", cl::desc("Dump enum types"), cl::sub(DiaDumpSubcommand)); +static cl::opt UDTs("udts", cl::desc("Dump udt types"), + cl::sub(DiaDumpSubcommand)); static cl::opt Compilands("compilands", cl::desc("Dump compiland information"), cl::sub(DiaDumpSubcommand)); @@ -465,6 +467,12 @@ cl::desc("dump type hashes and index offsets"), cl::cat(TypeOptions), cl::sub(DumpSubcommand)); +cl::opt DontResolveForwardRefs( + "dont-resolve-forward-refs", + cl::desc("When dumping type records for classes, unions, enums, and " + "structs, don't try to resolve forward references"), + cl::cat(TypeOptions), cl::sub(DumpSubcommand)); + cl::list DumpTypeIndex( "type-index", cl::ZeroOrMore, cl::CommaSeparated, cl::desc("only dump types with the specified hexadecimal type index"),