OSDN Git Service

[PDB] Add the ability to map forward references to full decls.
authorZachary Turner <zturner@google.com>
Thu, 20 Sep 2018 15:50:13 +0000 (15:50 +0000)
committerZachary Turner <zturner@google.com>
Thu, 20 Sep 2018 15:50:13 +0000 (15:50 +0000)
Some records point to an LF_CLASS, LF_UNION, LF_STRUCTURE, or LF_ENUM
which is a forward reference and doesn't contain complete debug
information. In these cases, we'd like to be able to quickly locate the
full record. The TPI stream stores an array of pre-computed record hash
values, one for each type record. If we pre-process this on startup, we
can build a mapping from hash value -> {list of possible matching type
indices}. Since hashes of full records are only based on the name and or
unique name and not the full record contents, we can then use forward
ref record to compute the hash of what *would* be the full record by
just hashing the name, use this to get the list of possible matches, and
iterate those looking for a match on name or unique name.

llvm-pdbutil is updated to resolve forward references for the purposes
of testing (plus it's just useful).

Differential Revision: https://reviews.llvm.org/D52283

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@342656 91177308-0d34-0410-b5e6-96231b3b80d8

15 files changed:
include/llvm/DebugInfo/PDB/Native/RawTypes.h
include/llvm/DebugInfo/PDB/Native/TpiHashing.h
include/llvm/DebugInfo/PDB/Native/TpiStream.h
lib/DebugInfo/PDB/Native/TpiHashing.cpp
lib/DebugInfo/PDB/Native/TpiStream.cpp
test/DebugInfo/PDB/Inputs/every-class.cpp [new file with mode: 0644]
test/DebugInfo/PDB/Inputs/every-class.pdb [new file with mode: 0644]
test/DebugInfo/PDB/every-type.test
test/DebugInfo/PDB/pdb-resolve-forward-refs.test [new file with mode: 0644]
test/DebugInfo/PDB/pdbdump-headers.test
tools/llvm-pdbutil/DumpOutputStyle.cpp
tools/llvm-pdbutil/MinimalTypeDumper.cpp
tools/llvm-pdbutil/MinimalTypeDumper.h
tools/llvm-pdbutil/llvm-pdbutil.cpp
tools/llvm-pdbutil/llvm-pdbutil.h

index 19f592d..8f6d661 100644 (file)
@@ -343,7 +343,6 @@ struct SrcHeaderBlockEntry {
   char Reserved[8];
 };
 
-constexpr int I = sizeof(SrcHeaderBlockEntry);
 static_assert(sizeof(SrcHeaderBlockEntry) == 40, "Incorrect struct size!");
 
 } // namespace pdb
index c1edec7..5c98be1 100644 (file)
@@ -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
 
index b779399..00cc720 100644 (file)
@@ -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;
 };
 }
index 77a2d57..1870882 100644 (file)
@@ -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:
index 0680b67..de0f888 100644 (file)
 
 #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/test/DebugInfo/PDB/Inputs/every-class.cpp b/test/DebugInfo/PDB/Inputs/every-class.cpp
new file mode 100644 (file)
index 0000000..c439bc2
--- /dev/null
@@ -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/test/DebugInfo/PDB/Inputs/every-class.pdb b/test/DebugInfo/PDB/Inputs/every-class.pdb
new file mode 100644 (file)
index 0000000..6462a70
Binary files /dev/null and b/test/DebugInfo/PDB/Inputs/every-class.pdb differ
index f188c74..39772d4 100644 (file)
@@ -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/test/DebugInfo/PDB/pdb-resolve-forward-refs.test b/test/DebugInfo/PDB/pdb-resolve-forward-refs.test
new file mode 100644 (file)
index 0000000..adb1c61
--- /dev/null
@@ -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
index 99c3721..2a6c862 100644 (file)
@@ -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 9869b3a..812aab5 100644 (file)
@@ -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,
index 569bca7..8a5e283 100644 (file)
@@ -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();
 }
 
index 4227688..8f6bdc6 100644 (file)
@@ -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
index 51dcd97..ab9b459 100644 (file)
@@ -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"),
index 1524f83..d25b0b6 100644 (file)
@@ -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;