diff options
-rw-r--r-- | llvm/include/llvm/DebugInfo/PDB/Native/RawTypes.h | 1 | ||||
-rw-r--r-- | llvm/include/llvm/DebugInfo/PDB/Native/TpiHashing.h | 48 | ||||
-rw-r--r-- | llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h | 9 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp | 43 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp | 90 | ||||
-rw-r--r-- | llvm/test/DebugInfo/PDB/Inputs/every-class.cpp | 61 | ||||
-rw-r--r-- | llvm/test/DebugInfo/PDB/Inputs/every-class.pdb | bin | 0 -> 102400 bytes | |||
-rw-r--r-- | llvm/test/DebugInfo/PDB/every-type.test | 6 | ||||
-rw-r--r-- | llvm/test/DebugInfo/PDB/pdb-resolve-forward-refs.test | 98 | ||||
-rw-r--r-- | llvm/test/DebugInfo/PDB/pdbdump-headers.test | 6 | ||||
-rw-r--r-- | llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp | 15 | ||||
-rw-r--r-- | llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp | 38 | ||||
-rw-r--r-- | llvm/tools/llvm-pdbutil/MinimalTypeDumper.h | 9 | ||||
-rw-r--r-- | llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp | 8 | ||||
-rw-r--r-- | llvm/tools/llvm-pdbutil/llvm-pdbutil.h | 1 |
15 files changed, 416 insertions, 17 deletions
diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/RawTypes.h b/llvm/include/llvm/DebugInfo/PDB/Native/RawTypes.h index 19f592d562e..8f6d6611c03 100644 --- a/llvm/include/llvm/DebugInfo/PDB/Native/RawTypes.h +++ b/llvm/include/llvm/DebugInfo/PDB/Native/RawTypes.h @@ -343,7 +343,6 @@ struct SrcHeaderBlockEntry { char Reserved[8]; }; -constexpr int I = sizeof(SrcHeaderBlockEntry); static_assert(sizeof(SrcHeaderBlockEntry) == 40, "Incorrect struct size!"); } // namespace pdb diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/TpiHashing.h b/llvm/include/llvm/DebugInfo/PDB/Native/TpiHashing.h index c1edec7a26f..5c98be1bb93 100644 --- a/llvm/include/llvm/DebugInfo/PDB/Native/TpiHashing.h +++ b/llvm/include/llvm/DebugInfo/PDB/Native/TpiHashing.h @@ -18,6 +18,54 @@ namespace pdb { Expected<uint32_t> 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<TagRecordHash> hashTagRecord(const codeview::CVType &Type); + } // end namespace pdb } // end namespace llvm diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h b/llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h index b77939929ec..00cc720336c 100644 --- a/llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h +++ b/llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h @@ -58,10 +58,17 @@ public: codeview::LazyRandomTypeCollection &typeCollection() { return *Types; } + Expected<codeview::TypeIndex> + findFullDeclForForwardRef(codeview::TypeIndex ForwardRefTI) const; + BinarySubstreamRef getTypeRecordsSubstream() const; Error commit(); + void buildHashMap(); + + bool supportsTypeLookup() const; + private: PDBFile &Pdb; std::unique_ptr<msf::MappedBlockStream> Stream; @@ -77,6 +84,8 @@ private: FixedStreamArray<codeview::TypeIndexOffset> TypeIndexOffsets; HashTable<support::ulittle32_t> HashAdjusters; + std::vector<std::vector<codeview::TypeIndex>> HashMap; + const TpiStreamHeader *Header; }; } diff --git a/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp b/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp index 77a2d57a836..18708826ffc 100644 --- a/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp @@ -50,6 +50,32 @@ static Expected<uint32_t> getHashForUdt(const CVType &Rec) { } template <typename T> +static Expected<TagRecordHash> getTagRecordHashForUdt(const CVType &Rec) { + T Deserialized; + if (auto E = TypeDeserializer::deserializeAs(const_cast<CVType &>(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 <typename T> static Expected<uint32_t> getSourceLineHash(const CVType &Rec) { T Deserialized; if (auto E = TypeDeserializer::deserializeAs(const_cast<CVType &>(Rec), @@ -60,6 +86,23 @@ static Expected<uint32_t> getSourceLineHash(const CVType &Rec) { return hashStringV1(StringRef(Buf, 4)); } +Expected<TagRecordHash> llvm::pdb::hashTagRecord(const codeview::CVType &Type) { + switch (Type.kind()) { + case LF_CLASS: + case LF_STRUCTURE: + case LF_INTERFACE: + return getTagRecordHashForUdt<ClassRecord>(Type); + case LF_UNION: + return getTagRecordHashForUdt<UnionRecord>(Type); + case LF_ENUM: + return getTagRecordHashForUdt<EnumRecord>(Type); + default: + assert(false && "Type is not a tag record!"); + } + return make_error<StringError>("Invalid record type", + inconvertibleErrorCode()); +} + Expected<uint32_t> llvm::pdb::hashTypeRecord(const CVType &Rec) { switch (Rec.kind()) { case LF_CLASS: diff --git a/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp b/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp index 0680b673380..de0f888137f 100644 --- a/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp +++ b/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,93 @@ uint16_t TpiStream::getTypeHashStreamAuxIndex() const { 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(); } + +template <typename RecordT> static ClassOptions getUdtOptions(CVType CVT) { + RecordT Record; + if (auto EC = TypeDeserializer::deserializeAs<RecordT>(CVT, Record)) { + consumeError(std::move(EC)); + return ClassOptions::None; + } + return Record.getOptions(); +} + +static bool isUdtForwardRef(CVType CVT) { + ClassOptions UdtOptions = ClassOptions::None; + switch (CVT.kind()) { + case LF_STRUCTURE: + case LF_CLASS: + case LF_INTERFACE: + UdtOptions = getUdtOptions<ClassRecord>(std::move(CVT)); + break; + case LF_ENUM: + UdtOptions = getUdtOptions<EnumRecord>(std::move(CVT)); + break; + case LF_UNION: + UdtOptions = getUdtOptions<UnionRecord>(std::move(CVT)); + break; + default: + return false; + } + return (UdtOptions & ClassOptions::ForwardReference) != ClassOptions::None; +} + +Expected<TypeIndex> +TpiStream::findFullDeclForForwardRef(TypeIndex ForwardRefTI) const { + CVType F = Types->getType(ForwardRefTI); + if (!isUdtForwardRef(F)) + return ForwardRefTI; + + Expected<TagRecordHash> 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<TagRecordHash> 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; } diff --git a/llvm/test/DebugInfo/PDB/Inputs/every-class.cpp b/llvm/test/DebugInfo/PDB/Inputs/every-class.cpp new file mode 100644 index 00000000000..c439bc2f365 --- /dev/null +++ b/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 <stdint.h> + +// 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<typename T> 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<const Nothing>(Nothing{}); + f<volatile Nothing>(Nothing{}); + f<const volatile Nothing>(Nothing{}); + f<__unaligned Nothing>(Nothing{}); + + return 0; +} diff --git a/llvm/test/DebugInfo/PDB/Inputs/every-class.pdb b/llvm/test/DebugInfo/PDB/Inputs/every-class.pdb Binary files differnew file mode 100644 index 00000000000..6462a705c86 --- /dev/null +++ b/llvm/test/DebugInfo/PDB/Inputs/every-class.pdb diff --git a/llvm/test/DebugInfo/PDB/every-type.test b/llvm/test/DebugInfo/PDB/every-type.test index f188c746e60..39772d4161f 100644 --- a/llvm/test/DebugInfo/PDB/every-type.test +++ b/llvm/test/DebugInfo/PDB/every-type.test @@ -9,12 +9,14 @@ we claim to understand. We then test this in two ways: 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: ============================================================ diff --git a/llvm/test/DebugInfo/PDB/pdb-resolve-forward-refs.test b/llvm/test/DebugInfo/PDB/pdb-resolve-forward-refs.test new file mode 100644 index 00000000000..adb1c61d862 --- /dev/null +++ b/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::<unnamed-type-Anonymous>` +; CHECK: unique name: `.?AU<unnamed-type-Anonymous>@?1??main@@YAHHPEAPEAD@Z@`aa6523bc` +; CHECK: vtable: <no type>, base list: <no type>, 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: <no type>, base list: <no type>, 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: <no type>, base list: <no type>, field list: <no type> +; 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: <no type>, base list: <no type>, 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: <no type>, base list: <no type>, 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: <no type>, base list: <no type>, field list: <no type> +; CHECK: options: forward ref (-> 0x105C) | has unique name, sizeof 0 +; CHECK: 0x105C | LF_STRUCTURE [size = 52] `Constructor` +; CHECK: unique name: `.?AUConstructor@@` +; CHECK: vtable: <no type>, base list: <no type>, 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: <no type>, base list: <no type>, 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: <no type>, base list: <no type>, field list: <no type> +; CHECK: options: forward ref (-> 0x1064) | has unique name, sizeof 0 +; CHECK: 0x1064 | LF_STRUCTURE [size = 48] `Operator` +; CHECK: unique name: `.?AUOperator@@` +; CHECK: vtable: <no type>, base list: <no type>, 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: <no type>, base list: <no type>, field list: <no type> +; CHECK: options: forward ref (-> 0x106B) | has unique name, sizeof 0 +; CHECK: 0x106B | LF_STRUCTURE [size = 40] `Cast` +; CHECK: unique name: `.?AUCast@@` +; CHECK: vtable: <no type>, base list: <no type>, 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: <no type>, base list: <no type>, field list: 0x1007 +; CHECK: options: has unique name, sizeof 1 +; CHECK: 0x106D | LF_STRUCTURE [size = 52] `Assignment` +; CHECK: unique name: `.?AUAssignment@@` +; CHECK: vtable: <no type>, base list: <no type>, field list: <no type> +; CHECK: options: forward ref (-> 0x1073) | has unique name, sizeof 0 +; CHECK: 0x1073 | LF_STRUCTURE [size = 52] `Assignment` +; CHECK: unique name: `.?AUAssignment@@` +; CHECK: vtable: <no type>, base list: <no type>, 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: <no type>, base list: <no type>, field list: <no type> +; CHECK: options: forward ref (<- 0x106C) | has unique name, sizeof 0 +; CHECK: 0x1081 | LF_UNION [size = 32] `Union` +; CHECK: unique name: `.?ATUnion@@` +; CHECK: field list: <no type> +; CHECK: options: forward ref (<- 0x105E) | has unique name, sizeof 0 +; CHECK: 0x1084 | LF_STRUCTURE [size = 124] `main::__l2::<unnamed-type-Anonymous>` +; CHECK: unique name: `.?AU<unnamed-type-Anonymous>@?1??main@@YAHHPEAPEAD@Z@`aa6523bc` +; CHECK: vtable: <no type>, base list: <no type>, field list: <no type> +; CHECK: options: forward ref (<- 0x1008) | has unique name | scoped, sizeof 0 +; CHECK: 0x108E | LF_STRUCTURE [size = 44] `Nested` +; CHECK: unique name: `.?AUNested@@` +; CHECK: vtable: <no type>, base list: <no type>, field list: <no type> +; 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: <no type>, base list: <no type>, field list: <no type> +; CHECK: options: forward ref (<- 0x1009) | has unique name | scoped, sizeof 0 +; CHECK: 0x1098 | LF_CLASS [size = 40] `Class` +; CHECK: unique name: `.?AVClass@@` +; CHECK: vtable: <no type>, base list: <no type>, field list: <no type> +; CHECK: options: forward ref (<- 0x105D) | has unique name, sizeof 0 diff --git a/llvm/test/DebugInfo/PDB/pdbdump-headers.test b/llvm/test/DebugInfo/PDB/pdbdump-headers.test index 99c37218e92..2a6c862ac2a 100644 --- a/llvm/test/DebugInfo/PDB/pdbdump-headers.test +++ b/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: ============================================================ diff --git a/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp b/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp index 9869b3ae4d2..812aab5bbc2 100644 --- a/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp +++ b/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp @@ -1241,13 +1241,13 @@ static void dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types, uint32_t NumTypeRecords, uint32_t NumHashBuckets, FixedStreamArray<support::ulittle32_t> 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 @@ static void dumpPartialTypeStream(LinePrinter &Printer, 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 @@ Error DumpOutputStyle::dumpTypesFromObjectFile() { 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 @@ Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { 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<TypeIndex> TiList(Indices.begin(), Indices.end()); dumpPartialTypeStream(P, Types, Stream, TiList, DumpBytes, DumpExtras, diff --git a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp index 569bca7490f..8a5e283dfad 100644 --- a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp +++ b/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::codeview; using namespace llvm::pdb; static std::string formatClassOptions(uint32_t IndentLevel, - ClassOptions Options) { + ClassOptions Options, TpiStream *Stream, + TypeIndex CurrentTypeIndex) { std::vector<std::string> Opts; + + if (Stream && Stream->supportsTypeLookup() && + !opts::dump::DontResolveForwardRefs && + ((Options & ClassOptions::ForwardReference) != ClassOptions::None)) { + // If we're able to resolve forward references, do that. + Expected<TypeIndex> 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 @@ static std::string formatFunctionOptions(FunctionOptions Options) { } 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 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 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 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 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 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) { 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(); } diff --git a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h index 4227688f0f7..8f6bdc6110a 100644 --- a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h +++ b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h @@ -20,15 +20,18 @@ class LazyRandomTypeCollection; 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<support::ulittle32_t> HashValues) + FixedStreamArray<support::ulittle32_t> 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 @@ private: bool Hashes = false; codeview::LazyRandomTypeCollection &Types; uint32_t NumHashBuckets; + codeview::TypeIndex CurrentTypeIndex; FixedStreamArray<support::ulittle32_t> HashValues; + pdb::TpiStream *Stream = nullptr; }; } // namespace pdb } // namespace llvm diff --git a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp index 51dcd977a32..ab9b4593c2b 100644 --- a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp @@ -183,6 +183,8 @@ static cl::opt<bool> Enums("enums", cl::desc("Dump enum types"), cl::sub(DiaDumpSubcommand)); static cl::opt<bool> Pointers("pointers", cl::desc("Dump enum types"), cl::sub(DiaDumpSubcommand)); +static cl::opt<bool> UDTs("udts", cl::desc("Dump udt types"), + cl::sub(DiaDumpSubcommand)); static cl::opt<bool> Compilands("compilands", cl::desc("Dump compiland information"), cl::sub(DiaDumpSubcommand)); @@ -465,6 +467,12 @@ cl::opt<bool> DumpTypeExtras("type-extras", cl::desc("dump type hashes and index offsets"), cl::cat(TypeOptions), cl::sub(DumpSubcommand)); +cl::opt<bool> 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<uint32_t> DumpTypeIndex( "type-index", cl::ZeroOrMore, cl::CommaSeparated, cl::desc("only dump types with the specified hexadecimal type index"), diff --git a/llvm/tools/llvm-pdbutil/llvm-pdbutil.h b/llvm/tools/llvm-pdbutil/llvm-pdbutil.h index 1524f83560c..d25b0b6e8eb 100644 --- a/llvm/tools/llvm-pdbutil/llvm-pdbutil.h +++ b/llvm/tools/llvm-pdbutil/llvm-pdbutil.h @@ -160,6 +160,7 @@ extern llvm::cl::opt<bool> DumpIdExtras; extern llvm::cl::list<uint32_t> DumpIdIndex; extern llvm::cl::opt<uint32_t> DumpModi; extern llvm::cl::opt<bool> JustMyCode; +extern llvm::cl::opt<bool> DontResolveForwardRefs; extern llvm::cl::opt<bool> DumpSymbols; extern llvm::cl::opt<bool> DumpSymRecordBytes; extern llvm::cl::opt<bool> DumpGSIRecords; |