[llvm-pdbutil] Add -type-ref-stats to help find unused type info

Summary:
This considers module symbol streams and the global symbol stream to be
roots. Most types that this considers "unreferenced" are referenced by
LF_UDT_MOD_SRC_LINE id records, which VC seems to always include.
Essentially, they are types that the user can only find in the debugger
if they call them by name, they cannot be found by traversing a symbol.

In practice, around 80% of type information in a PDB is referenced by a
symbol. That seems like a reasonable number.

I don't really plan to do anything with this tool. It mostly just exists
for informational purposes, and to confirm that we probably don't need
to implement type reference tracking in LLD. We can continue to merge
all types as we do today without wasting space.

Reviewers: zturner, aganea

Subscribers: mgorny, hiraditya, arphaman, jdoerfert, llvm-commits

Tags: #llvm

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

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@356692 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp b/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp
index b01511e..e84e1c9 100644
--- a/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp
+++ b/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp
@@ -363,14 +363,16 @@
   // values.  One idea is to define some structures representing these types
   // that would allow the use of offsetof().
   switch (Kind) {
-  case SymbolKind::S_GPROC32:
-  case SymbolKind::S_LPROC32:
   case SymbolKind::S_GPROC32_ID:
   case SymbolKind::S_LPROC32_ID:
   case SymbolKind::S_LPROC32_DPC:
   case SymbolKind::S_LPROC32_DPC_ID:
     Refs.push_back({TiRefKind::IndexRef, 24, 1}); // LF_FUNC_ID
     break;
+  case SymbolKind::S_GPROC32:
+  case SymbolKind::S_LPROC32:
+    Refs.push_back({TiRefKind::TypeRef, 24, 1}); // Type
+    break;
   case SymbolKind::S_UDT:
     Refs.push_back({TiRefKind::TypeRef, 0, 1}); // UDT
     break;
diff --git a/test/DebugInfo/PDB/pdb-type-ref-stats.test b/test/DebugInfo/PDB/pdb-type-ref-stats.test
new file mode 100644
index 0000000..c7e2204
--- /dev/null
+++ b/test/DebugInfo/PDB/pdb-type-ref-stats.test
@@ -0,0 +1,577 @@
+RUN: llvm-pdbutil dump -types -type-ref-stats %p/Inputs/every-class.pdb \
+RUN:   | FileCheck %s
+
+CHECK:                      Types (TPI Stream)                     
+CHECK: ============================================================
+CHECK:   Showing 157 records
+CHECK:   0x1000 | LF_ARGLIST [size = 16, referenced]
+CHECK:            0x0603 (void*): `void*`
+CHECK:            0x0023 (unsigned __int64): `unsigned __int64`
+CHECK:   0x1001 | LF_PROCEDURE [size = 16, referenced]
+CHECK:            return type = 0x0003 (void), # args = 2, param list = 0x1000
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x1002 | LF_ARGLIST [size = 16, referenced]
+CHECK:            0x0603 (void*): `void*`
+CHECK:            0x0075 (unsigned): `unsigned`
+CHECK:   0x1003 | LF_PROCEDURE [size = 16, referenced]
+CHECK:            return type = 0x0003 (void), # args = 2, param list = 0x1002
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x1004 | LF_POINTER [size = 12, referenced]
+CHECK:            referent = 0x0670 (char*), mode = pointer, opts = None, kind = ptr64
+CHECK:   0x1005 | LF_ARGLIST [size = 16, referenced]
+CHECK:            0x0074 (int): `int`
+CHECK:            0x1004: `char**`
+CHECK:   0x1006 | LF_PROCEDURE [size = 16, referenced]
+CHECK:            return type = 0x0074 (int), # args = 2, param list = 0x1005
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x1007 | LF_FIELDLIST [size = 4, referenced]
+CHECK:   0x1008 | LF_STRUCTURE [size = 124, referenced] `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, referenced] `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:   0x100A | LF_FIELDLIST [size = 48, unreferenced]
+CHECK:            - LF_ENUMERATE [native = 0]
+CHECK:            - LF_ENUMERATE [com = 1]
+CHECK:            - LF_ENUMERATE [managed = 2]
+CHECK:   0x100B | LF_ENUM [size = 116, unreferenced] `__vc_attributes::event_sourceAttribute::type_e`
+CHECK:            unique name: `.?AW4type_e@event_sourceAttribute@__vc_attributes@@`
+CHECK:            field list: 0x100A, underlying type: 0x0074 (int)
+CHECK:            options: has unique name | is nested
+CHECK:   0x100C | LF_FIELDLIST [size = 28, unreferenced]
+CHECK:            - LF_ENUMERATE [speed = 0]
+CHECK:            - LF_ENUMERATE [size = 1]
+CHECK:   0x100D | LF_ENUM [size = 124, unreferenced] `__vc_attributes::event_sourceAttribute::optimize_e`
+CHECK:            unique name: `.?AW4optimize_e@event_sourceAttribute@__vc_attributes@@`
+CHECK:            field list: 0x100C, underlying type: 0x0074 (int)
+CHECK:            options: has unique name | is nested
+CHECK:   0x100E | LF_STRUCTURE [size = 108, unreferenced] `__vc_attributes::event_sourceAttribute`
+CHECK:            unique name: `.?AUevent_sourceAttribute@__vc_attributes@@`
+CHECK:            vtable: <no type>, base list: <no type>, field list: <no type>
+CHECK:            options: forward ref (-> 0x1016) | has unique name, sizeof 0
+CHECK:   0x100F | LF_POINTER [size = 12, unreferenced]
+CHECK:            referent = 0x100E, mode = pointer, opts = const, kind = ptr64
+CHECK:   0x1010 | LF_ARGLIST [size = 12, unreferenced]
+CHECK:            0x100B: `__vc_attributes::event_sourceAttribute::type_e`
+CHECK:   0x1011 | LF_MFUNCTION [size = 28, unreferenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x1010
+CHECK:            class type = 0x100E, this type = 0x100F, this adjust = 0
+CHECK:            calling conv = cdecl, options = constructor
+CHECK:   0x1012 | LF_ARGLIST [size = 8, referenced]
+CHECK:   0x1013 | LF_MFUNCTION [size = 28, unreferenced]
+CHECK:            return type = 0x0003 (void), # args = 0, param list = 0x1012
+CHECK:            class type = 0x100E, this type = 0x100F, this adjust = 0
+CHECK:            calling conv = cdecl, options = constructor
+CHECK:   0x1014 | LF_METHODLIST [size = 20, unreferenced]
+CHECK:            - Method [type = 0x1011, vftable offset = -1, attrs = public]
+CHECK:            - Method [type = 0x1013, vftable offset = -1, attrs = public]
+CHECK:   0x1015 | LF_FIELDLIST [size = 128, unreferenced]
+CHECK:            - LF_NESTTYPE [name = `type_e`, parent = 0x100B]
+CHECK:            - LF_NESTTYPE [name = `optimize_e`, parent = 0x100D]
+CHECK:            - LF_METHOD [name = `event_sourceAttribute`, # overloads = 2, overload list = 0x1014]
+CHECK:            - LF_MEMBER [name = `type`, Type = 0x100B, offset = 0, attrs = public]
+CHECK:            - LF_MEMBER [name = `optimize`, Type = 0x100D, offset = 4, attrs = public]
+CHECK:            - LF_MEMBER [name = `decorate`, Type = 0x0030 (bool), offset = 8, attrs = public]
+CHECK:   0x1016 | LF_STRUCTURE [size = 108, unreferenced] `__vc_attributes::event_sourceAttribute`
+CHECK:            unique name: `.?AUevent_sourceAttribute@__vc_attributes@@`
+CHECK:            vtable: <no type>, base list: <no type>, field list: 0x1015
+CHECK:            options: has ctor / dtor | contains nested class | has unique name, sizeof 12
+CHECK:   0x1017 | LF_FIELDLIST [size = 68, unreferenced]
+CHECK:            - LF_ENUMERATE [eBoolean = 0]
+CHECK:            - LF_ENUMERATE [eInteger = 1]
+CHECK:            - LF_ENUMERATE [eFloat = 2]
+CHECK:            - LF_ENUMERATE [eDouble = 3]
+CHECK:   0x1018 | LF_ENUM [size = 148, unreferenced] `__vc_attributes::helper_attributes::v1_alttypeAttribute::type_e`
+CHECK:            unique name: `.?AW4type_e@v1_alttypeAttribute@helper_attributes@__vc_attributes@@`
+CHECK:            field list: 0x1017, underlying type: 0x0074 (int)
+CHECK:            options: has unique name | is nested
+CHECK:   0x1019 | LF_STRUCTURE [size = 140, unreferenced] `__vc_attributes::helper_attributes::v1_alttypeAttribute`
+CHECK:            unique name: `.?AUv1_alttypeAttribute@helper_attributes@__vc_attributes@@`
+CHECK:            vtable: <no type>, base list: <no type>, field list: <no type>
+CHECK:            options: forward ref (-> 0x101E) | has unique name, sizeof 0
+CHECK:   0x101A | LF_POINTER [size = 12, unreferenced]
+CHECK:            referent = 0x1019, mode = pointer, opts = const, kind = ptr64
+CHECK:   0x101B | LF_ARGLIST [size = 12, unreferenced]
+CHECK:            0x1018: `__vc_attributes::helper_attributes::v1_alttypeAttribute::type_e`
+CHECK:   0x101C | LF_MFUNCTION [size = 28, unreferenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x101B
+CHECK:            class type = 0x1019, this type = 0x101A, this adjust = 0
+CHECK:            calling conv = cdecl, options = constructor
+CHECK:   0x101D | LF_FIELDLIST [size = 64, unreferenced]
+CHECK:            - LF_NESTTYPE [name = `type_e`, parent = 0x1018]
+CHECK:            - LF_ONEMETHOD [name = `v1_alttypeAttribute`]
+CHECK:              type = 0x101C, vftable offset = -1, attrs = public
+CHECK:            - LF_MEMBER [name = `type`, Type = 0x1018, offset = 0, attrs = public]
+CHECK:   0x101E | LF_STRUCTURE [size = 140, unreferenced] `__vc_attributes::helper_attributes::v1_alttypeAttribute`
+CHECK:            unique name: `.?AUv1_alttypeAttribute@helper_attributes@__vc_attributes@@`
+CHECK:            vtable: <no type>, base list: <no type>, field list: 0x101D
+CHECK:            options: has ctor / dtor | contains nested class | has unique name, sizeof 4
+CHECK:   0x101F | LF_FIELDLIST [size = 756, unreferenced]
+CHECK:            - LF_ENUMERATE [eAnyUsage = 0]
+CHECK:            - LF_ENUMERATE [eCoClassUsage = 1]
+CHECK:            - LF_ENUMERATE [eCOMInterfaceUsage = 2]
+CHECK:            - LF_ENUMERATE [eInterfaceUsage = 6]
+CHECK:            - LF_ENUMERATE [eMemberUsage = 8]
+CHECK:            - LF_ENUMERATE [eMethodUsage = 16]
+CHECK:            - LF_ENUMERATE [eInterfaceMethodUsage = 32]
+CHECK:            - LF_ENUMERATE [eInterfaceMemberUsage = 64]
+CHECK:            - LF_ENUMERATE [eCoClassMemberUsage = 128]
+CHECK:            - LF_ENUMERATE [eCoClassMethodUsage = 256]
+CHECK:            - LF_ENUMERATE [eGlobalMethodUsage = 768]
+CHECK:            - LF_ENUMERATE [eGlobalDataUsage = 1024]
+CHECK:            - LF_ENUMERATE [eClassUsage = 2048]
+CHECK:            - LF_ENUMERATE [eInterfaceParameterUsage = 4096]
+CHECK:            - LF_ENUMERATE [eMethodParameterUsage = 12288]
+CHECK:            - LF_ENUMERATE [eIDLModuleUsage = 16384]
+CHECK:            - LF_ENUMERATE [eAnonymousUsage = 32768]
+CHECK:            - LF_ENUMERATE [eTypedefUsage = 65536]
+CHECK:            - LF_ENUMERATE [eUnionUsage = 131072]
+CHECK:            - LF_ENUMERATE [eEnumUsage = 262144]
+CHECK:            - LF_ENUMERATE [eDefineTagUsage = 524288]
+CHECK:            - LF_ENUMERATE [eStructUsage = 1048576]
+CHECK:            - LF_ENUMERATE [eLocalUsage = 2097152]
+CHECK:            - LF_ENUMERATE [ePropertyUsage = 4194304]
+CHECK:            - LF_ENUMERATE [eEventUsage = 8388608]
+CHECK:            - LF_ENUMERATE [eTemplateUsage = 16777216]
+CHECK:            - LF_ENUMERATE [eModuleUsage = 16777216]
+CHECK:            - LF_ENUMERATE [eIllegalUsage = 33554432]
+CHECK:            - LF_ENUMERATE [eAsynchronousUsage = 67108864]
+CHECK:            - LF_ENUMERATE [eAnyIDLUsage = 4161535]
+CHECK:   0x1020 | LF_ENUM [size = 140, unreferenced] `__vc_attributes::helper_attributes::usageAttribute::usage_e`
+CHECK:            unique name: `.?AW4usage_e@usageAttribute@helper_attributes@__vc_attributes@@`
+CHECK:            field list: 0x101F, underlying type: 0x0074 (int)
+CHECK:            options: has unique name | is nested
+CHECK:   0x1021 | LF_STRUCTURE [size = 128, unreferenced] `__vc_attributes::helper_attributes::usageAttribute`
+CHECK:            unique name: `.?AUusageAttribute@helper_attributes@__vc_attributes@@`
+CHECK:            vtable: <no type>, base list: <no type>, field list: <no type>
+CHECK:            options: forward ref (-> 0x1026) | has unique name, sizeof 0
+CHECK:   0x1022 | LF_POINTER [size = 12, unreferenced]
+CHECK:            referent = 0x1021, mode = pointer, opts = const, kind = ptr64
+CHECK:   0x1023 | LF_ARGLIST [size = 12, unreferenced]
+CHECK:            0x0075 (unsigned): `unsigned`
+CHECK:   0x1024 | LF_MFUNCTION [size = 28, unreferenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x1023
+CHECK:            class type = 0x1021, this type = 0x1022, this adjust = 0
+CHECK:            calling conv = cdecl, options = constructor
+CHECK:   0x1025 | LF_FIELDLIST [size = 60, unreferenced]
+CHECK:            - LF_NESTTYPE [name = `usage_e`, parent = 0x1020]
+CHECK:            - LF_ONEMETHOD [name = `usageAttribute`]
+CHECK:              type = 0x1024, vftable offset = -1, attrs = public
+CHECK:            - LF_MEMBER [name = `value`, Type = 0x0075 (unsigned), offset = 0, attrs = public]
+CHECK:   0x1026 | LF_STRUCTURE [size = 128, unreferenced] `__vc_attributes::helper_attributes::usageAttribute`
+CHECK:            unique name: `.?AUusageAttribute@helper_attributes@__vc_attributes@@`
+CHECK:            vtable: <no type>, base list: <no type>, field list: 0x1025
+CHECK:            options: has ctor / dtor | contains nested class | has unique name, sizeof 4
+CHECK:   0x1027 | LF_FIELDLIST [size = 76, unreferenced]
+CHECK:            - LF_ENUMERATE [apartment = 1]
+CHECK:            - LF_ENUMERATE [single = 2]
+CHECK:            - LF_ENUMERATE [free = 3]
+CHECK:            - LF_ENUMERATE [neutral = 4]
+CHECK:            - LF_ENUMERATE [both = 5]
+CHECK:   0x1028 | LF_ENUM [size = 120, unreferenced] `__vc_attributes::threadingAttribute::threading_e`
+CHECK:            unique name: `.?AW4threading_e@threadingAttribute@__vc_attributes@@`
+CHECK:            field list: 0x1027, underlying type: 0x0074 (int)
+CHECK:            options: has unique name | is nested
+CHECK:   0x1029 | LF_STRUCTURE [size = 100, unreferenced] `__vc_attributes::threadingAttribute`
+CHECK:            unique name: `.?AUthreadingAttribute@__vc_attributes@@`
+CHECK:            vtable: <no type>, base list: <no type>, field list: <no type>
+CHECK:            options: forward ref (-> 0x1030) | has unique name, sizeof 0
+CHECK:   0x102A | LF_POINTER [size = 12, unreferenced]
+CHECK:            referent = 0x1029, mode = pointer, opts = const, kind = ptr64
+CHECK:   0x102B | LF_ARGLIST [size = 12, unreferenced]
+CHECK:            0x1028: `__vc_attributes::threadingAttribute::threading_e`
+CHECK:   0x102C | LF_MFUNCTION [size = 28, unreferenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x102B
+CHECK:            class type = 0x1029, this type = 0x102A, this adjust = 0
+CHECK:            calling conv = cdecl, options = constructor
+CHECK:   0x102D | LF_MFUNCTION [size = 28, unreferenced]
+CHECK:            return type = 0x0003 (void), # args = 0, param list = 0x1012
+CHECK:            class type = 0x1029, this type = 0x102A, this adjust = 0
+CHECK:            calling conv = cdecl, options = constructor
+CHECK:   0x102E | LF_METHODLIST [size = 20, unreferenced]
+CHECK:            - Method [type = 0x102C, vftable offset = -1, attrs = public]
+CHECK:            - Method [type = 0x102D, vftable offset = -1, attrs = public]
+CHECK:   0x102F | LF_FIELDLIST [size = 68, unreferenced]
+CHECK:            - LF_NESTTYPE [name = `threading_e`, parent = 0x1028]
+CHECK:            - LF_METHOD [name = `threadingAttribute`, # overloads = 2, overload list = 0x102E]
+CHECK:            - LF_MEMBER [name = `value`, Type = 0x1028, offset = 0, attrs = public]
+CHECK:   0x1030 | LF_STRUCTURE [size = 100, unreferenced] `__vc_attributes::threadingAttribute`
+CHECK:            unique name: `.?AUthreadingAttribute@__vc_attributes@@`
+CHECK:            vtable: <no type>, base list: <no type>, field list: 0x102F
+CHECK:            options: has ctor / dtor | contains nested class | has unique name, sizeof 4
+CHECK:   0x1031 | LF_FIELDLIST [size = 48, unreferenced]
+CHECK:            - LF_ENUMERATE [never = 0]
+CHECK:            - LF_ENUMERATE [allowed = 1]
+CHECK:            - LF_ENUMERATE [always = 2]
+CHECK:   0x1032 | LF_ENUM [size = 116, unreferenced] `__vc_attributes::aggregatableAttribute::type_e`
+CHECK:            unique name: `.?AW4type_e@aggregatableAttribute@__vc_attributes@@`
+CHECK:            field list: 0x1031, underlying type: 0x0074 (int)
+CHECK:            options: has unique name | is nested
+CHECK:   0x1033 | LF_STRUCTURE [size = 108, unreferenced] `__vc_attributes::aggregatableAttribute`
+CHECK:            unique name: `.?AUaggregatableAttribute@__vc_attributes@@`
+CHECK:            vtable: <no type>, base list: <no type>, field list: <no type>
+CHECK:            options: forward ref (-> 0x103A) | has unique name, sizeof 0
+CHECK:   0x1034 | LF_POINTER [size = 12, unreferenced]
+CHECK:            referent = 0x1033, mode = pointer, opts = const, kind = ptr64
+CHECK:   0x1035 | LF_ARGLIST [size = 12, unreferenced]
+CHECK:            0x1032: `__vc_attributes::aggregatableAttribute::type_e`
+CHECK:   0x1036 | LF_MFUNCTION [size = 28, unreferenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x1035
+CHECK:            class type = 0x1033, this type = 0x1034, this adjust = 0
+CHECK:            calling conv = cdecl, options = constructor
+CHECK:   0x1037 | LF_MFUNCTION [size = 28, unreferenced]
+CHECK:            return type = 0x0003 (void), # args = 0, param list = 0x1012
+CHECK:            class type = 0x1033, this type = 0x1034, this adjust = 0
+CHECK:            calling conv = cdecl, options = constructor
+CHECK:   0x1038 | LF_METHODLIST [size = 20, unreferenced]
+CHECK:            - Method [type = 0x1036, vftable offset = -1, attrs = public]
+CHECK:            - Method [type = 0x1037, vftable offset = -1, attrs = public]
+CHECK:   0x1039 | LF_FIELDLIST [size = 68, unreferenced]
+CHECK:            - LF_NESTTYPE [name = `type_e`, parent = 0x1032]
+CHECK:            - LF_METHOD [name = `aggregatableAttribute`, # overloads = 2, overload list = 0x1038]
+CHECK:            - LF_MEMBER [name = `type`, Type = 0x1032, offset = 0, attrs = public]
+CHECK:   0x103A | LF_STRUCTURE [size = 108, unreferenced] `__vc_attributes::aggregatableAttribute`
+CHECK:            unique name: `.?AUaggregatableAttribute@__vc_attributes@@`
+CHECK:            vtable: <no type>, base list: <no type>, field list: 0x1039
+CHECK:            options: has ctor / dtor | contains nested class | has unique name, sizeof 4
+CHECK:   0x103B | LF_ENUM [size = 120, unreferenced] `__vc_attributes::event_receiverAttribute::type_e`
+CHECK:            unique name: `.?AW4type_e@event_receiverAttribute@__vc_attributes@@`
+CHECK:            field list: 0x100A, underlying type: 0x0074 (int)
+CHECK:            options: has unique name | is nested
+CHECK:   0x103C | LF_STRUCTURE [size = 112, unreferenced] `__vc_attributes::event_receiverAttribute`
+CHECK:            unique name: `.?AUevent_receiverAttribute@__vc_attributes@@`
+CHECK:            vtable: <no type>, base list: <no type>, field list: <no type>
+CHECK:            options: forward ref (-> 0x1045) | has unique name, sizeof 0
+CHECK:   0x103D | LF_POINTER [size = 12, unreferenced]
+CHECK:            referent = 0x103C, mode = pointer, opts = const, kind = ptr64
+CHECK:   0x103E | LF_ARGLIST [size = 16, unreferenced]
+CHECK:            0x103B: `__vc_attributes::event_receiverAttribute::type_e`
+CHECK:            0x0030 (bool): `bool`
+CHECK:   0x103F | LF_MFUNCTION [size = 28, unreferenced]
+CHECK:            return type = 0x0003 (void), # args = 2, param list = 0x103E
+CHECK:            class type = 0x103C, this type = 0x103D, this adjust = 0
+CHECK:            calling conv = cdecl, options = constructor
+CHECK:   0x1040 | LF_ARGLIST [size = 12, unreferenced]
+CHECK:            0x103B: `__vc_attributes::event_receiverAttribute::type_e`
+CHECK:   0x1041 | LF_MFUNCTION [size = 28, unreferenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x1040
+CHECK:            class type = 0x103C, this type = 0x103D, this adjust = 0
+CHECK:            calling conv = cdecl, options = constructor
+CHECK:   0x1042 | LF_MFUNCTION [size = 28, unreferenced]
+CHECK:            return type = 0x0003 (void), # args = 0, param list = 0x1012
+CHECK:            class type = 0x103C, this type = 0x103D, this adjust = 0
+CHECK:            calling conv = cdecl, options = constructor
+CHECK:   0x1043 | LF_METHODLIST [size = 28, unreferenced]
+CHECK:            - Method [type = 0x103F, vftable offset = -1, attrs = public]
+CHECK:            - Method [type = 0x1041, vftable offset = -1, attrs = public]
+CHECK:            - Method [type = 0x1042, vftable offset = -1, attrs = public]
+CHECK:   0x1044 | LF_FIELDLIST [size = 96, unreferenced]
+CHECK:            - LF_NESTTYPE [name = `type_e`, parent = 0x103B]
+CHECK:            - LF_METHOD [name = `event_receiverAttribute`, # overloads = 3, overload list = 0x1043]
+CHECK:            - LF_MEMBER [name = `type`, Type = 0x103B, offset = 0, attrs = public]
+CHECK:            - LF_MEMBER [name = `layout_dependent`, Type = 0x0030 (bool), offset = 4, attrs = public]
+CHECK:   0x1045 | LF_STRUCTURE [size = 112, unreferenced] `__vc_attributes::event_receiverAttribute`
+CHECK:            unique name: `.?AUevent_receiverAttribute@__vc_attributes@@`
+CHECK:            vtable: <no type>, base list: <no type>, field list: 0x1044
+CHECK:            options: has ctor / dtor | contains nested class | has unique name, sizeof 8
+CHECK:   0x1046 | LF_FIELDLIST [size = 92, unreferenced]
+CHECK:            - LF_ENUMERATE [dll = 1]
+CHECK:            - LF_ENUMERATE [exe = 2]
+CHECK:            - LF_ENUMERATE [service = 3]
+CHECK:            - LF_ENUMERATE [unspecified = 4]
+CHECK:            - LF_ENUMERATE [EXE = 2]
+CHECK:            - LF_ENUMERATE [SERVICE = 3]
+CHECK:   0x1047 | LF_ENUM [size = 104, unreferenced] `__vc_attributes::moduleAttribute::type_e`
+CHECK:            unique name: `.?AW4type_e@moduleAttribute@__vc_attributes@@`
+CHECK:            field list: 0x1046, underlying type: 0x0074 (int)
+CHECK:            options: has unique name | is nested
+CHECK:   0x1048 | LF_STRUCTURE [size = 96, unreferenced] `__vc_attributes::moduleAttribute`
+CHECK:            unique name: `.?AUmoduleAttribute@__vc_attributes@@`
+CHECK:            vtable: <no type>, base list: <no type>, field list: <no type>
+CHECK:            options: forward ref (-> 0x1053) | has unique name, sizeof 0
+CHECK:   0x1049 | LF_POINTER [size = 12, unreferenced]
+CHECK:            referent = 0x1048, mode = pointer, opts = const, kind = ptr64
+CHECK:   0x104A | LF_MODIFIER [size = 12, unreferenced]
+CHECK:            referent = 0x0070 (char), modifiers = const
+CHECK:   0x104B | LF_POINTER [size = 12, unreferenced]
+CHECK:            referent = 0x104A, mode = pointer, opts = None, kind = ptr64
+CHECK:   0x104C | LF_ARGLIST [size = 68, unreferenced]
+CHECK:            0x1047: `__vc_attributes::moduleAttribute::type_e`
+CHECK:            0x104B: `const char*`
+CHECK:            0x104B: `const char*`
+CHECK:            0x104B: `const char*`
+CHECK:            0x0074 (int): `int`
+CHECK:            0x0030 (bool): `bool`
+CHECK:            0x104B: `const char*`
+CHECK:            0x0074 (int): `int`
+CHECK:            0x104B: `const char*`
+CHECK:            0x104B: `const char*`
+CHECK:            0x0074 (int): `int`
+CHECK:            0x0030 (bool): `bool`
+CHECK:            0x0030 (bool): `bool`
+CHECK:            0x104B: `const char*`
+CHECK:            0x104B: `const char*`
+CHECK:   0x104D | LF_MFUNCTION [size = 28, unreferenced]
+CHECK:            return type = 0x0003 (void), # args = 15, param list = 0x104C
+CHECK:            class type = 0x1048, this type = 0x1049, this adjust = 0
+CHECK:            calling conv = cdecl, options = constructor
+CHECK:   0x104E | LF_ARGLIST [size = 12, unreferenced]
+CHECK:            0x1047: `__vc_attributes::moduleAttribute::type_e`
+CHECK:   0x104F | LF_MFUNCTION [size = 28, unreferenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x104E
+CHECK:            class type = 0x1048, this type = 0x1049, this adjust = 0
+CHECK:            calling conv = cdecl, options = constructor
+CHECK:   0x1050 | LF_MFUNCTION [size = 28, unreferenced]
+CHECK:            return type = 0x0003 (void), # args = 0, param list = 0x1012
+CHECK:            class type = 0x1048, this type = 0x1049, this adjust = 0
+CHECK:            calling conv = cdecl, options = constructor
+CHECK:   0x1051 | LF_METHODLIST [size = 28, unreferenced]
+CHECK:            - Method [type = 0x104D, vftable offset = -1, attrs = public]
+CHECK:            - Method [type = 0x104F, vftable offset = -1, attrs = public]
+CHECK:            - Method [type = 0x1050, vftable offset = -1, attrs = public]
+CHECK:   0x1052 | LF_FIELDLIST [size = 356, unreferenced]
+CHECK:            - LF_NESTTYPE [name = `type_e`, parent = 0x1047]
+CHECK:            - LF_METHOD [name = `moduleAttribute`, # overloads = 3, overload list = 0x1051]
+CHECK:            - LF_MEMBER [name = `type`, Type = 0x1047, offset = 0, attrs = public]
+CHECK:            - LF_MEMBER [name = `name`, Type = 0x104B, offset = 8, attrs = public]
+CHECK:            - LF_MEMBER [name = `version`, Type = 0x104B, offset = 16, attrs = public]
+CHECK:            - LF_MEMBER [name = `uuid`, Type = 0x104B, offset = 24, attrs = public]
+CHECK:            - LF_MEMBER [name = `lcid`, Type = 0x0074 (int), offset = 32, attrs = public]
+CHECK:            - LF_MEMBER [name = `control`, Type = 0x0030 (bool), offset = 36, attrs = public]
+CHECK:            - LF_MEMBER [name = `helpstring`, Type = 0x104B, offset = 40, attrs = public]
+CHECK:            - LF_MEMBER [name = `helpstringcontext`, Type = 0x0074 (int), offset = 48, attrs = public]
+CHECK:            - LF_MEMBER [name = `helpstringdll`, Type = 0x104B, offset = 56, attrs = public]
+CHECK:            - LF_MEMBER [name = `helpfile`, Type = 0x104B, offset = 64, attrs = public]
+CHECK:            - LF_MEMBER [name = `helpcontext`, Type = 0x0074 (int), offset = 72, attrs = public]
+CHECK:            - LF_MEMBER [name = `hidden`, Type = 0x0030 (bool), offset = 76, attrs = public]
+CHECK:            - LF_MEMBER [name = `restricted`, Type = 0x0030 (bool), offset = 77, attrs = public]
+CHECK:            - LF_MEMBER [name = `custom`, Type = 0x104B, offset = 80, attrs = public]
+CHECK:            - LF_MEMBER [name = `resource_name`, Type = 0x104B, offset = 88, attrs = public]
+CHECK:   0x1053 | LF_STRUCTURE [size = 96, unreferenced] `__vc_attributes::moduleAttribute`
+CHECK:            unique name: `.?AUmoduleAttribute@__vc_attributes@@`
+CHECK:            vtable: <no type>, base list: <no type>, field list: 0x1052
+CHECK:            options: has ctor / dtor | contains nested class | has unique name, sizeof 96
+CHECK:   0x1054 | LF_STRUCTURE [size = 48, referenced] `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:   0x1055 | LF_FIELDLIST [size = 16, referenced]
+CHECK:            - LF_NESTTYPE [name = `F`, parent = 0x1054]
+CHECK:   0x1056 | LF_STRUCTURE [size = 44, referenced] `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, referenced] `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, referenced] `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:   0x1059 | LF_POINTER [size = 12, referenced]
+CHECK:            referent = 0x1058, mode = pointer, opts = const, kind = ptr64
+CHECK:   0x105A | LF_MFUNCTION [size = 28, referenced]
+CHECK:            return type = 0x0003 (void), # args = 0, param list = 0x1012
+CHECK:            class type = 0x1058, this type = 0x1059, this adjust = 0
+CHECK:            calling conv = cdecl, options = constructor
+CHECK:   0x105B | LF_FIELDLIST [size = 24, referenced]
+CHECK:            - LF_ONEMETHOD [name = `Constructor`]
+CHECK:              type = 0x105A, vftable offset = -1, attrs = public
+CHECK:   0x105C | LF_STRUCTURE [size = 52, referenced] `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, referenced] `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, referenced] `Union`
+CHECK:            unique name: `.?ATUnion@@`
+CHECK:            field list: 0x1007
+CHECK:            options: has unique name | sealed, sizeof 1
+CHECK:   0x105F | LF_STRUCTURE [size = 48, referenced] `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:   0x1060 | LF_POINTER [size = 12, referenced]
+CHECK:            referent = 0x105F, mode = pointer, opts = const, kind = ptr64
+CHECK:   0x1061 | LF_ARGLIST [size = 12, referenced]
+CHECK:            0x0074 (int): `int`
+CHECK:   0x1062 | LF_MFUNCTION [size = 28, referenced]
+CHECK:            return type = 0x0074 (int), # args = 1, param list = 0x1061
+CHECK:            class type = 0x105F, this type = 0x1060, this adjust = 0
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x1063 | LF_FIELDLIST [size = 24, referenced]
+CHECK:            - LF_ONEMETHOD [name = `operator+`]
+CHECK:              type = 0x1062, vftable offset = -1, attrs = public
+CHECK:   0x1064 | LF_STRUCTURE [size = 48, referenced] `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:   0x1065 | LF_FIELDLIST [size = 12, referenced]
+CHECK:            - LF_ENUMERATE [A = 0]
+CHECK:   0x1066 | LF_ENUM [size = 36, referenced] `Enum`
+CHECK:            unique name: `.?AW4Enum@@`
+CHECK:            field list: 0x1065, underlying type: 0x0074 (int)
+CHECK:            options: has unique name
+CHECK:   0x1067 | LF_STRUCTURE [size = 40, referenced] `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:   0x1068 | LF_POINTER [size = 12, referenced]
+CHECK:            referent = 0x1067, mode = pointer, opts = const, kind = ptr64
+CHECK:   0x1069 | LF_MFUNCTION [size = 28, referenced]
+CHECK:            return type = 0x0074 (int), # args = 0, param list = 0x1012
+CHECK:            class type = 0x1067, this type = 0x1068, this adjust = 0
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x106A | LF_FIELDLIST [size = 28, referenced]
+CHECK:            - LF_ONEMETHOD [name = `operator int`]
+CHECK:              type = 0x1069, vftable offset = -1, attrs = public
+CHECK:   0x106B | LF_STRUCTURE [size = 40, referenced] `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, referenced] `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, referenced] `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:   0x106E | LF_POINTER [size = 12, referenced]
+CHECK:            referent = 0x106D, mode = ref, opts = None, kind = ptr64
+CHECK:   0x106F | LF_POINTER [size = 12, referenced]
+CHECK:            referent = 0x106D, mode = pointer, opts = const, kind = ptr64
+CHECK:   0x1070 | LF_ARGLIST [size = 12, referenced]
+CHECK:            0x106D: `Assignment`
+CHECK:   0x1071 | LF_MFUNCTION [size = 28, referenced]
+CHECK:            return type = 0x106E, # args = 1, param list = 0x1070
+CHECK:            class type = 0x106D, this type = 0x106F, this adjust = 0
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x1072 | LF_FIELDLIST [size = 24, referenced]
+CHECK:            - LF_ONEMETHOD [name = `operator=`]
+CHECK:              type = 0x1071, vftable offset = -1, attrs = public
+CHECK:   0x1073 | LF_STRUCTURE [size = 52, referenced] `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, referenced] `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:   0x1075 | LF_MODIFIER [size = 12, referenced]
+CHECK:            referent = 0x1074, modifiers = const
+CHECK:   0x1076 | LF_ARGLIST [size = 12, referenced]
+CHECK:            0x1075: `const Nothing`
+CHECK:   0x1077 | LF_PROCEDURE [size = 16, referenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x1076
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x1078 | LF_MODIFIER [size = 12, referenced]
+CHECK:            referent = 0x1074, modifiers = volatile
+CHECK:   0x1079 | LF_ARGLIST [size = 12, referenced]
+CHECK:            0x1078: `volatile Nothing`
+CHECK:   0x107A | LF_PROCEDURE [size = 16, referenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x1079
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x107B | LF_MODIFIER [size = 12, referenced]
+CHECK:            referent = 0x1074, modifiers = const | volatile
+CHECK:   0x107C | LF_ARGLIST [size = 12, referenced]
+CHECK:            0x107B: `const volatile Nothing`
+CHECK:   0x107D | LF_PROCEDURE [size = 16, referenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x107C
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x107E | LF_MODIFIER [size = 12, referenced]
+CHECK:            referent = 0x1074, modifiers = unaligned
+CHECK:   0x107F | LF_ARGLIST [size = 12, referenced]
+CHECK:            0x107E: `__unaligned Nothing`
+CHECK:   0x1080 | LF_PROCEDURE [size = 16, referenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x107F
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x1081 | LF_UNION [size = 32, referenced] `Union`
+CHECK:            unique name: `.?ATUnion@@`
+CHECK:            field list: <no type>
+CHECK:            options: forward ref (<- 0x105E) | has unique name, sizeof 0
+CHECK:   0x1082 | LF_ARGLIST [size = 12, referenced]
+CHECK:            0x1081: `Union`
+CHECK:   0x1083 | LF_PROCEDURE [size = 16, referenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x1082
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x1084 | LF_STRUCTURE [size = 124, referenced] `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:   0x1085 | LF_ARGLIST [size = 12, referenced]
+CHECK:            0x1084: `main::__l2::<unnamed-type-Anonymous>`
+CHECK:   0x1086 | LF_PROCEDURE [size = 16, referenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x1085
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x1087 | LF_PROCEDURE [size = 16, referenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x1070
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x1088 | LF_ARGLIST [size = 12, referenced]
+CHECK:            0x1067: `Cast`
+CHECK:   0x1089 | LF_PROCEDURE [size = 16, referenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x1088
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x108A | LF_ARGLIST [size = 12, referenced]
+CHECK:            0x1058: `Constructor`
+CHECK:   0x108B | LF_PROCEDURE [size = 16, referenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x108A
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x108C | LF_ARGLIST [size = 12, referenced]
+CHECK:            0x1054: `Nested::F`
+CHECK:   0x108D | LF_PROCEDURE [size = 16, referenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x108C
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x108E | LF_STRUCTURE [size = 44, referenced] `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:   0x108F | LF_ARGLIST [size = 12, referenced]
+CHECK:            0x108E: `Nested`
+CHECK:   0x1090 | LF_PROCEDURE [size = 16, referenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x108F
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x1091 | LF_ARGLIST [size = 12, referenced]
+CHECK:            0x1074: `Nothing`
+CHECK:   0x1092 | LF_PROCEDURE [size = 16, referenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x1091
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x1093 | LF_ARGLIST [size = 12, referenced]
+CHECK:            0x105F: `Operator`
+CHECK:   0x1094 | LF_PROCEDURE [size = 16, referenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x1093
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x1095 | LF_STRUCTURE [size = 88, referenced] `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:   0x1096 | LF_ARGLIST [size = 12, referenced]
+CHECK:            0x1095: `main::__l2::Scoped`
+CHECK:   0x1097 | LF_PROCEDURE [size = 16, referenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x1096
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x1098 | LF_CLASS [size = 40, referenced] `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
+CHECK:   0x1099 | LF_ARGLIST [size = 12, referenced]
+CHECK:            0x1098: `Class`
+CHECK:   0x109A | LF_PROCEDURE [size = 16, referenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x1099
+CHECK:            calling conv = cdecl, options = None
+CHECK:   0x109B | LF_ARGLIST [size = 12, referenced]
+CHECK:            0x1066: `Enum`
+CHECK:   0x109C | LF_PROCEDURE [size = 16, referenced]
+CHECK:            return type = 0x0003 (void), # args = 1, param list = 0x109B
+CHECK:            calling conv = cdecl, options = None
+
+CHECK:                  Type Reference Statistics                  
+CHECK: ============================================================
+CHECK:   Records referenced: 84 / 157 53.50%
+CHECK:   Bytes referenced: 2,188 / 7,500 29.17%
diff --git a/tools/llvm-pdbutil/CMakeLists.txt b/tools/llvm-pdbutil/CMakeLists.txt
index e403d54..56b9e19 100644
--- a/tools/llvm-pdbutil/CMakeLists.txt
+++ b/tools/llvm-pdbutil/CMakeLists.txt
@@ -30,5 +30,6 @@
   PrettyTypedefDumper.cpp
   PrettyVariableDumper.cpp
   StreamUtil.cpp
+  TypeReferenceTracker.cpp
   YAMLOutputStyle.cpp
   )
diff --git a/tools/llvm-pdbutil/DumpOutputStyle.cpp b/tools/llvm-pdbutil/DumpOutputStyle.cpp
index 23f9af6..f3f2566 100644
--- a/tools/llvm-pdbutil/DumpOutputStyle.cpp
+++ b/tools/llvm-pdbutil/DumpOutputStyle.cpp
@@ -13,6 +13,7 @@
 #include "MinimalSymbolDumper.h"
 #include "MinimalTypeDumper.h"
 #include "StreamUtil.h"
+#include "TypeReferenceTracker.h"
 #include "llvm-pdbutil.h"
 
 #include "llvm/ADT/STLExtras.h"
@@ -60,7 +61,12 @@
 using namespace llvm::pdb;
 
 DumpOutputStyle::DumpOutputStyle(InputFile &File)
-    : File(File), P(2, false, outs()) {}
+    : File(File), P(2, false, outs()) {
+  if (opts::dump::DumpTypeRefStats)
+    RefTracker.reset(new TypeReferenceTracker(File));
+}
+
+DumpOutputStyle::~DumpOutputStyle() {}
 
 PDBFile &DumpOutputStyle::getPdb() { return File.pdb(); }
 object::COFFObjectFile &DumpOutputStyle::getObj() { return File.obj(); }
@@ -76,6 +82,10 @@
 }
 
 Error DumpOutputStyle::dump() {
+  // Walk symbols & globals if we are supposed to mark types referenced.
+  if (opts::dump::DumpTypeRefStats)
+    RefTracker->mark();
+
   if (opts::dump::DumpSummary) {
     if (auto EC = dumpFileSummary())
       return EC;
@@ -187,6 +197,11 @@
       return EC;
   }
 
+  if (opts::dump::DumpTypeRefStats) {
+    if (auto EC = dumpTypeRefStats())
+      return EC;
+  }
+
   if (opts::dump::DumpSectionHeaders) {
     if (auto EC = dumpSectionHeaders())
       return EC;
@@ -1227,14 +1242,15 @@
 
 static void
 dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types,
-                   uint32_t NumTypeRecords, uint32_t NumHashBuckets,
+                   TypeReferenceTracker *RefTracker, uint32_t NumTypeRecords,
+                   uint32_t NumHashBuckets,
                    FixedStreamArray<support::ulittle32_t> HashValues,
                    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,
+  MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker,
                            NumHashBuckets, HashValues, Stream);
 
   if (auto EC = codeview::visitTypeStream(Types, V)) {
@@ -1245,12 +1261,13 @@
 
 static void dumpPartialTypeStream(LinePrinter &Printer,
                                   LazyRandomTypeCollection &Types,
+                                  TypeReferenceTracker *RefTracker,
                                   TpiStream &Stream, ArrayRef<TypeIndex> TiList,
                                   bool Bytes, bool Extras, bool Deps) {
   uint32_t Width =
       NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords());
 
-  MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types,
+  MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker,
                            Stream.getNumHashBuckets(), Stream.getHashValues(),
                            &Stream);
 
@@ -1314,8 +1331,8 @@
     Types.reset(Reader, 100);
 
     if (opts::dump::DumpTypes) {
-      dumpFullTypeStream(P, Types, 0, 0, {}, nullptr, opts::dump::DumpTypeData,
-                         false);
+      dumpFullTypeStream(P, Types, RefTracker.get(), 0, 0, {}, nullptr,
+                         opts::dump::DumpTypeData, false);
     } else if (opts::dump::DumpTypeExtras) {
       auto LocalHashes = LocallyHashedType::hashTypeCollection(Types);
       auto GlobalHashes = GloballyHashedType::hashTypeCollection(Types);
@@ -1384,18 +1401,22 @@
 
   auto &Types = (StreamIdx == StreamTPI) ? File.types() : File.ids();
 
+  // Only emit notes about referenced/unreferenced for types.
+  TypeReferenceTracker *MaybeTracker =
+      (StreamIdx == StreamTPI) ? RefTracker.get() : nullptr;
+
   // Enable resolving forward decls.
   Stream.buildHashMap();
 
   if (DumpTypes || !Indices.empty()) {
     if (Indices.empty())
-      dumpFullTypeStream(P, Types, Stream.getNumTypeRecords(),
+      dumpFullTypeStream(P, Types, MaybeTracker, Stream.getNumTypeRecords(),
                          Stream.getNumHashBuckets(), Stream.getHashValues(),
                          &Stream, DumpBytes, DumpExtras);
     else {
       std::vector<TypeIndex> TiList(Indices.begin(), Indices.end());
-      dumpPartialTypeStream(P, Types, Stream, TiList, DumpBytes, DumpExtras,
-                            opts::dump::DumpTypeDependents);
+      dumpPartialTypeStream(P, Types, MaybeTracker, Stream, TiList, DumpBytes,
+                            DumpExtras, opts::dump::DumpTypeDependents);
     }
   }
 
@@ -1520,6 +1541,34 @@
   return Error::success();
 }
 
+Error DumpOutputStyle::dumpTypeRefStats() {
+  printHeader(P, "Type Reference Statistics");
+  AutoIndent Indent(P);
+
+  // Sum the byte size of all type records, and the size and count of all
+  // referenced records.
+  size_t TotalRecs = File.types().size();
+  size_t RefRecs = 0;
+  size_t TotalBytes = 0;
+  size_t RefBytes = 0;
+  auto &Types = File.types();
+  for (Optional<TypeIndex> TI = Types.getFirst(); TI; TI = Types.getNext(*TI)) {
+    CVType Type = File.types().getType(*TI);
+    TotalBytes += Type.length();
+    if (RefTracker->isTypeReferenced(*TI)) {
+      ++RefRecs;
+      RefBytes += Type.length();
+    }
+  }
+
+  P.formatLine("Records referenced: {0:N} / {1:N} {2:P}", RefRecs, TotalRecs,
+               (double)RefRecs / TotalRecs);
+  P.formatLine("Bytes referenced: {0:N} / {1:N} {2:P}", RefBytes, TotalBytes,
+               (double)RefBytes / TotalBytes);
+
+  return Error::success();
+}
+
 Error DumpOutputStyle::dumpGSIRecords() {
   printHeader(P, "GSI Records");
 
diff --git a/tools/llvm-pdbutil/DumpOutputStyle.h b/tools/llvm-pdbutil/DumpOutputStyle.h
index c95490e..a555223 100644
--- a/tools/llvm-pdbutil/DumpOutputStyle.h
+++ b/tools/llvm-pdbutil/DumpOutputStyle.h
@@ -34,6 +34,7 @@
 namespace pdb {
 class GSIHashTable;
 class InputFile;
+class TypeReferenceTracker;
 
 struct StatCollection {
   struct Stat {
@@ -62,6 +63,7 @@
 
 public:
   DumpOutputStyle(InputFile &File);
+  ~DumpOutputStyle() override;
 
   Error dump() override;
 
@@ -89,6 +91,7 @@
   Error dumpNewFpo(PDBFile &File);
   Error dumpTpiStream(uint32_t StreamIdx);
   Error dumpTypesFromObjectFile();
+  Error dumpTypeRefStats();
   Error dumpModules();
   Error dumpModuleFiles();
   Error dumpModuleSymsForPdb();
@@ -104,6 +107,7 @@
   void dumpSectionHeaders(StringRef Label, DbgHeaderType Type);
 
   InputFile &File;
+  std::unique_ptr<TypeReferenceTracker> RefTracker;
   LinePrinter P;
   SmallVector<StreamInfo, 32> StreamPurposes;
 };
diff --git a/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/tools/llvm-pdbutil/MinimalTypeDumper.cpp
index a662248..dff4423 100644
--- a/tools/llvm-pdbutil/MinimalTypeDumper.cpp
+++ b/tools/llvm-pdbutil/MinimalTypeDumper.cpp
@@ -10,6 +10,7 @@
 
 #include "FormatUtil.h"
 #include "LinePrinter.h"
+#include "TypeReferenceTracker.h"
 
 #include "llvm-pdbutil.h"
 #include "llvm/DebugInfo/CodeView/CVRecord.h"
@@ -221,11 +222,10 @@
   // 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.
-  if (!Hashes) {
-    P.formatLine("{0} | {1} [size = {2}]",
-                 fmt_align(Index, AlignStyle::Right, Width),
-                 formatTypeLeafKind(Record.Type), Record.length());
-  } else {
+  P.formatLine("{0} | {1} [size = {2}",
+               fmt_align(Index, AlignStyle::Right, Width),
+               formatTypeLeafKind(Record.Type), Record.length());
+  if (Hashes) {
     std::string H;
     if (Index.toArrayIndex() >= HashValues.size()) {
       H = "(not present)";
@@ -241,13 +241,19 @@
       else
         H = "0x" + utohexstr(Hash) + ", our hash = 0x" + utohexstr(OurHash);
     }
-    P.formatLine("{0} | {1} [size = {2}, hash = {3}]",
-                 fmt_align(Index, AlignStyle::Right, Width),
-                 formatTypeLeafKind(Record.Type), Record.length(), H);
+    P.format(", hash = {0}", H);
   }
+  if (RefTracker) {
+    if (RefTracker->isTypeReferenced(Index))
+      P.format(", referenced");
+    else
+      P.format(", unreferenced");
+  }
+  P.format("]");
   P.Indent(Width + 3);
   return Error::success();
 }
+
 Error MinimalTypeDumpVisitor::visitTypeEnd(CVType &Record) {
   P.Unindent(Width + 3);
   if (RecordBytes) {
diff --git a/tools/llvm-pdbutil/MinimalTypeDumper.h b/tools/llvm-pdbutil/MinimalTypeDumper.h
index 0374e04..6bc456d 100644
--- a/tools/llvm-pdbutil/MinimalTypeDumper.h
+++ b/tools/llvm-pdbutil/MinimalTypeDumper.h
@@ -20,17 +20,19 @@
 namespace pdb {
 class LinePrinter;
 class TpiStream;
+class TypeReferenceTracker;
 
 class MinimalTypeDumpVisitor : public codeview::TypeVisitorCallbacks {
 public:
   MinimalTypeDumpVisitor(LinePrinter &P, uint32_t Width, bool RecordBytes,
                          bool Hashes, codeview::LazyRandomTypeCollection &Types,
+                         TypeReferenceTracker *RefTracker,
                          uint32_t NumHashBuckets,
                          FixedStreamArray<support::ulittle32_t> HashValues,
                          pdb::TpiStream *Stream)
       : P(P), Width(Width), RecordBytes(RecordBytes), Hashes(Hashes),
-        Types(Types), NumHashBuckets(NumHashBuckets), HashValues(HashValues),
-        Stream(Stream) {}
+        Types(Types), RefTracker(RefTracker), NumHashBuckets(NumHashBuckets),
+        HashValues(HashValues), Stream(Stream) {}
 
   Error visitTypeBegin(codeview::CVType &Record,
                        codeview::TypeIndex Index) override;
@@ -56,6 +58,7 @@
   bool RecordBytes = false;
   bool Hashes = false;
   codeview::LazyRandomTypeCollection &Types;
+  pdb::TypeReferenceTracker *RefTracker = nullptr;
   uint32_t NumHashBuckets;
   codeview::TypeIndex CurrentTypeIndex;
   FixedStreamArray<support::ulittle32_t> HashValues;
diff --git a/tools/llvm-pdbutil/TypeReferenceTracker.cpp b/tools/llvm-pdbutil/TypeReferenceTracker.cpp
new file mode 100644
index 0000000..f184f02
--- /dev/null
+++ b/tools/llvm-pdbutil/TypeReferenceTracker.cpp
@@ -0,0 +1,160 @@
+//===- TypeReferenceTracker.cpp ------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "TypeReferenceTracker.h"
+
+#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
+#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
+
+using namespace llvm;
+using namespace llvm::pdb;
+using namespace llvm::codeview;
+
+// LazyRandomTypeCollection doesn't appear to expose the number of records, so
+// just iterate up front to find out.
+static uint32_t getNumRecordsInCollection(LazyRandomTypeCollection &Types) {
+  uint32_t NumTypes = 0;
+  for (Optional<TypeIndex> TI = Types.getFirst(); TI; TI = Types.getNext(*TI))
+    ++NumTypes;
+  return NumTypes;
+}
+
+TypeReferenceTracker::TypeReferenceTracker(InputFile &File)
+    : File(File), Types(File.types()),
+      Ids(File.isPdb() ? &File.ids() : nullptr) {
+  NumTypeRecords = getNumRecordsInCollection(Types);
+  TypeReferenced.resize(NumTypeRecords, false);
+
+  // If this is a PDB, ids are stored separately, so make a separate bit vector.
+  if (Ids) {
+    NumIdRecords = getNumRecordsInCollection(*Ids);
+    IdReferenced.resize(NumIdRecords, false);
+  }
+
+  // Get the TpiStream pointer for forward decl resolution if this is a pdb.
+  // Build the hash map to enable resolving forward decls.
+  if (File.isPdb()) {
+    Tpi = &cantFail(File.pdb().getPDBTpiStream());
+    Tpi->buildHashMap();
+  }
+}
+
+void TypeReferenceTracker::mark() {
+  // Walk type roots:
+  // - globals
+  // - modi symbols
+  // - LF_UDT_MOD_SRC_LINE? VC always links these in.
+  for (SymbolGroup SG : File.symbol_groups()) {
+    if (File.isObj()) {
+      for (const auto &SS : SG.getDebugSubsections()) {
+        // FIXME: Are there other type-referencing subsections? Inlinees?
+        // Probably for IDs.
+        if (SS.kind() != DebugSubsectionKind::Symbols)
+          continue;
+
+        CVSymbolArray Symbols;
+        BinaryStreamReader Reader(SS.getRecordData());
+        cantFail(Reader.readArray(Symbols, Reader.getLength()));
+        for (const CVSymbol &S : Symbols)
+          addTypeRefsFromSymbol(S);
+      }
+    } else if (SG.hasDebugStream()) {
+      for (const CVSymbol &S : SG.getPdbModuleStream().getSymbolArray())
+        addTypeRefsFromSymbol(S);
+    }
+  }
+
+  // Walk globals and mark types referenced from globals.
+  if (File.isPdb() && File.pdb().hasPDBGlobalsStream()) {
+    SymbolStream &SymStream = cantFail(File.pdb().getPDBSymbolStream());
+    GlobalsStream &GS = cantFail(File.pdb().getPDBGlobalsStream());
+    for (uint32_t PubSymOff : GS.getGlobalsTable()) {
+      CVSymbol Sym = SymStream.readRecord(PubSymOff);
+      addTypeRefsFromSymbol(Sym);
+    }
+  }
+
+  // FIXME: Should we walk Ids?
+}
+
+void TypeReferenceTracker::addOneTypeRef(TiRefKind RefKind, TypeIndex RefTI) {
+  // If it's simple or already seen, no need to add to work list.
+  BitVector &TypeOrIdReferenced =
+      (Ids && RefKind == TiRefKind::IndexRef) ? IdReferenced : TypeReferenced;
+  if (RefTI.isSimple() || TypeOrIdReferenced.test(RefTI.toArrayIndex()))
+    return;
+
+  // Otherwise, mark it seen and add it to the work list.
+  TypeOrIdReferenced.set(RefTI.toArrayIndex());
+  RefWorklist.push_back({RefKind, RefTI});
+}
+
+void TypeReferenceTracker::addTypeRefsFromSymbol(const CVSymbol &Sym) {
+  SmallVector<TiReference, 4> DepList;
+  // FIXME: Check for failure.
+  discoverTypeIndicesInSymbol(Sym, DepList);
+  addReferencedTypes(Sym.content(), DepList);
+  markReferencedTypes();
+}
+
+void TypeReferenceTracker::addReferencedTypes(ArrayRef<uint8_t> RecData,
+                                              ArrayRef<TiReference> DepList) {
+  for (const auto &Ref : DepList) {
+    // FIXME: Report OOB slice instead of truncating.
+    ArrayRef<uint8_t> ByteSlice =
+        RecData.drop_front(Ref.Offset).take_front(4 * Ref.Count);
+    ArrayRef<TypeIndex> TIs(
+        reinterpret_cast<const TypeIndex *>(ByteSlice.data()),
+        ByteSlice.size() / 4);
+
+    // If this is a PDB and this is an item reference, track it in the IPI
+    // bitvector. Otherwise, it's a type ref, or there is only one stream.
+    for (TypeIndex RefTI : TIs)
+      addOneTypeRef(Ref.Kind, RefTI);
+  }
+}
+
+void TypeReferenceTracker::markReferencedTypes() {
+  while (!RefWorklist.empty()) {
+    TiRefKind RefKind;
+    TypeIndex RefTI;
+    std::tie(RefKind, RefTI) = RefWorklist.pop_back_val();
+    Optional<CVType> Rec = (Ids && RefKind == TiRefKind::IndexRef)
+                               ? Ids->tryGetType(RefTI)
+                               : Types.tryGetType(RefTI);
+    if (!Rec)
+      continue; // FIXME: Report a reference to a non-existant type.
+
+    SmallVector<TiReference, 4> DepList;
+    // FIXME: Check for failure.
+    discoverTypeIndices(*Rec, DepList);
+    addReferencedTypes(Rec->content(), DepList);
+
+    // If this is a tag kind and this is a PDB input, mark the complete type as
+    // referenced.
+    // FIXME: This limitation makes this feature somewhat useless on object file
+    // inputs.
+    if (Tpi) {
+      switch (Rec->kind()) {
+      default:
+        break;
+      case LF_CLASS:
+      case LF_INTERFACE:
+      case LF_STRUCTURE:
+      case LF_UNION:
+      case LF_ENUM:
+        addOneTypeRef(TiRefKind::TypeRef,
+                      cantFail(Tpi->findFullDeclForForwardRef(RefTI)));
+        break;
+      }
+    }
+  }
+}
diff --git a/tools/llvm-pdbutil/TypeReferenceTracker.h b/tools/llvm-pdbutil/TypeReferenceTracker.h
new file mode 100644
index 0000000..8861731
--- /dev/null
+++ b/tools/llvm-pdbutil/TypeReferenceTracker.h
@@ -0,0 +1,69 @@
+//===- TypeReferenceTracker.h --------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_TYPEREFERENCETRACKER_H
+#define LLVM_TOOLS_LLVMPDBDUMP_TYPEREFERENCETRACKER_H
+
+#include "InputFile.h"
+
+#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/DebugInfo/CodeView/CVRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeIndex.h"
+#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+namespace pdb {
+
+class TpiStream;
+
+/// Maintains bitvector to track whether a type was referenced by a symbol
+/// record.
+class TypeReferenceTracker {
+public:
+  TypeReferenceTracker(InputFile &File);
+
+  // Do the work of marking referenced types.
+  void mark();
+
+  // Return true if a symbol record transitively references this type.
+  bool isTypeReferenced(codeview::TypeIndex TI) {
+    return TI.toArrayIndex() <= NumTypeRecords &&
+           TypeReferenced.test(TI.toArrayIndex());
+  }
+
+private:
+  void addTypeRefsFromSymbol(const codeview::CVSymbol &Sym);
+
+  // Mark types on this list as referenced.
+  void addReferencedTypes(ArrayRef<uint8_t> RecData,
+                          ArrayRef<codeview::TiReference> Refs);
+
+  // Consume all types on the worklist.
+  void markReferencedTypes();
+
+  void addOneTypeRef(codeview::TiRefKind RefKind, codeview::TypeIndex RefTI);
+
+  InputFile &File;
+  codeview::LazyRandomTypeCollection &Types;
+  codeview::LazyRandomTypeCollection *Ids = nullptr;
+  TpiStream *Tpi = nullptr;
+  BitVector TypeReferenced;
+  BitVector IdReferenced;
+  SmallVector<std::pair<codeview::TiRefKind, codeview::TypeIndex>, 10>
+      RefWorklist;
+  uint32_t NumTypeRecords = 0;
+  uint32_t NumIdRecords = 0;
+};
+
+} // namespace pdb
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVMPDBDUMP_TYPEREFERENCETRACKER_H
diff --git a/tools/llvm-pdbutil/llvm-pdbutil.cpp b/tools/llvm-pdbutil/llvm-pdbutil.cpp
index 418ba13..8a2a6f8 100644
--- a/tools/llvm-pdbutil/llvm-pdbutil.cpp
+++ b/tools/llvm-pdbutil/llvm-pdbutil.cpp
@@ -476,6 +476,11 @@
     "type-data",
     cl::desc("dump CodeView type record raw bytes from TPI stream"),
     cl::cat(TypeOptions), cl::sub(DumpSubcommand));
+cl::opt<bool>
+    DumpTypeRefStats("type-ref-stats",
+                     cl::desc("dump statistics on the number and size of types "
+                              "transitively referenced by symbol records"),
+                     cl::cat(TypeOptions), cl::sub(DumpSubcommand));
 
 cl::opt<bool> DumpTypeExtras("type-extras",
                              cl::desc("dump type hashes and index offsets"),
diff --git a/tools/llvm-pdbutil/llvm-pdbutil.h b/tools/llvm-pdbutil/llvm-pdbutil.h
index a54bfcd..1b98d52 100644
--- a/tools/llvm-pdbutil/llvm-pdbutil.h
+++ b/tools/llvm-pdbutil/llvm-pdbutil.h
@@ -155,6 +155,7 @@
 extern llvm::cl::opt<bool> DumpTypeExtras;
 extern llvm::cl::list<uint32_t> DumpTypeIndex;
 extern llvm::cl::opt<bool> DumpTypeDependents;
+extern llvm::cl::opt<bool> DumpTypeRefStats;
 extern llvm::cl::opt<bool> DumpSectionHeaders;
 
 extern llvm::cl::opt<bool> DumpIds;