summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp6
-rw-r--r--llvm/test/DebugInfo/PDB/pdb-type-ref-stats.test577
-rw-r--r--llvm/tools/llvm-pdbutil/CMakeLists.txt1
-rw-r--r--llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp67
-rw-r--r--llvm/tools/llvm-pdbutil/DumpOutputStyle.h4
-rw-r--r--llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp22
-rw-r--r--llvm/tools/llvm-pdbutil/MinimalTypeDumper.h7
-rw-r--r--llvm/tools/llvm-pdbutil/TypeReferenceTracker.cpp160
-rw-r--r--llvm/tools/llvm-pdbutil/TypeReferenceTracker.h69
-rw-r--r--llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp5
-rw-r--r--llvm/tools/llvm-pdbutil/llvm-pdbutil.h1
11 files changed, 898 insertions, 21 deletions
diff --git a/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp b/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp
index b01511e06e7..e84e1c9cea7 100644
--- a/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp
+++ b/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp
@@ -363,14 +363,16 @@ static bool discoverTypeIndices(ArrayRef<uint8_t> Content, SymbolKind Kind,
// 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/llvm/test/DebugInfo/PDB/pdb-type-ref-stats.test b/llvm/test/DebugInfo/PDB/pdb-type-ref-stats.test
new file mode 100644
index 00000000000..c7e220408ee
--- /dev/null
+++ b/llvm/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/llvm/tools/llvm-pdbutil/CMakeLists.txt b/llvm/tools/llvm-pdbutil/CMakeLists.txt
index e403d54eef5..56b9e19926d 100644
--- a/llvm/tools/llvm-pdbutil/CMakeLists.txt
+++ b/llvm/tools/llvm-pdbutil/CMakeLists.txt
@@ -30,5 +30,6 @@ add_llvm_tool(llvm-pdbutil
PrettyTypedefDumper.cpp
PrettyVariableDumper.cpp
StreamUtil.cpp
+ TypeReferenceTracker.cpp
YAMLOutputStyle.cpp
)
diff --git a/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp b/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp
index 23f9af6ab10..f3f25669fc5 100644
--- a/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp
+++ b/llvm/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::msf;
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 @@ void DumpOutputStyle::printStreamNotPresent(StringRef StreamName) {
}
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 @@ Error DumpOutputStyle::dump() {
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 buildDepSet(LazyRandomTypeCollection &Types,
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 @@ dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types,
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 @@ Error DumpOutputStyle::dumpTypesFromObjectFile() {
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 @@ Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
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 @@ Error DumpOutputStyle::dumpModuleSymsForPdb() {
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/llvm/tools/llvm-pdbutil/DumpOutputStyle.h b/llvm/tools/llvm-pdbutil/DumpOutputStyle.h
index c95490e95df..a55522389a7 100644
--- a/llvm/tools/llvm-pdbutil/DumpOutputStyle.h
+++ b/llvm/tools/llvm-pdbutil/DumpOutputStyle.h
@@ -34,6 +34,7 @@ class COFFObjectFile;
namespace pdb {
class GSIHashTable;
class InputFile;
+class TypeReferenceTracker;
struct StatCollection {
struct Stat {
@@ -62,6 +63,7 @@ class DumpOutputStyle : public OutputStyle {
public:
DumpOutputStyle(InputFile &File);
+ ~DumpOutputStyle() override;
Error dump() override;
@@ -89,6 +91,7 @@ private:
Error dumpNewFpo(PDBFile &File);
Error dumpTpiStream(uint32_t StreamIdx);
Error dumpTypesFromObjectFile();
+ Error dumpTypeRefStats();
Error dumpModules();
Error dumpModuleFiles();
Error dumpModuleSymsForPdb();
@@ -104,6 +107,7 @@ private:
void dumpSectionHeaders(StringRef Label, DbgHeaderType Type);
InputFile &File;
+ std::unique_ptr<TypeReferenceTracker> RefTracker;
LinePrinter P;
SmallVector<StreamInfo, 32> StreamPurposes;
};
diff --git a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp
index a6622480c9f..dff4423dc16 100644
--- a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp
+++ b/llvm/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 @@ Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex 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.
- 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 @@ Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) {
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/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h
index 0374e042e42..6bc456d47ac 100644
--- a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h
+++ b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h
@@ -20,17 +20,19 @@ class LazyRandomTypeCollection;
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 @@ private:
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/llvm/tools/llvm-pdbutil/TypeReferenceTracker.cpp b/llvm/tools/llvm-pdbutil/TypeReferenceTracker.cpp
new file mode 100644
index 00000000000..f184f02e01e
--- /dev/null
+++ b/llvm/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/llvm/tools/llvm-pdbutil/TypeReferenceTracker.h b/llvm/tools/llvm-pdbutil/TypeReferenceTracker.h
new file mode 100644
index 00000000000..8861731ab6e
--- /dev/null
+++ b/llvm/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/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp
index 418ba135dc7..8a2a6f8194b 100644
--- a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp
+++ b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp
@@ -476,6 +476,11 @@ cl::opt<bool> DumpTypeData(
"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/llvm/tools/llvm-pdbutil/llvm-pdbutil.h b/llvm/tools/llvm-pdbutil/llvm-pdbutil.h
index a54bfcdb722..1b98d5230e4 100644
--- a/llvm/tools/llvm-pdbutil/llvm-pdbutil.h
+++ b/llvm/tools/llvm-pdbutil/llvm-pdbutil.h
@@ -155,6 +155,7 @@ extern llvm::cl::opt<bool> DumpTypeData;
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;
OpenPOWER on IntegriCloud