diff --git a/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp b/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp --- a/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp +++ b/llvm/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/llvm/test/DebugInfo/PDB/pdb-type-ref-stats.test b/llvm/test/DebugInfo/PDB/pdb-type-ref-stats.test new file mode 100644 --- /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::` +CHECK: unique name: `.?AU@?1??main@@YAHHPEAPEAD@Z@`aa6523bc` +CHECK: vtable: , base list: , field list: 0x1007 +CHECK: options: has unique name | scoped, sizeof 1 +CHECK: 0x1009 | LF_STRUCTURE [size = 88, referenced] `main::__l2::Scoped` +CHECK: unique name: `.?AUScoped@?1??main@@YAHHPEAPEAD@Z@`aa6523bc` +CHECK: vtable: , base list: , field list: 0x1007 +CHECK: options: has unique name | scoped, sizeof 1 +CHECK: 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: , base list: , field list: +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: , base list: , 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: , base list: , field list: +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: , base list: , 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: , base list: , field list: +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: , base list: , 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: , base list: , field list: +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: , base list: , 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: , base list: , field list: +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: , base list: , 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: , base list: , field list: +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: , base list: , 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: , base list: , field list: +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: , base list: , 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: , base list: , field list: +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: , base list: , 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: , base list: , 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: , base list: , field list: +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: , base list: , 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: , base list: , 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: , base list: , field list: +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: , base list: , 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: , base list: , field list: +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: , base list: , 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: , base list: , field list: 0x1007 +CHECK: options: has unique name, sizeof 1 +CHECK: 0x106D | LF_STRUCTURE [size = 52, referenced] `Assignment` +CHECK: unique name: `.?AUAssignment@@` +CHECK: vtable: , base list: , field list: +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: , base list: , 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: , base list: , field list: +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: +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::` +CHECK: unique name: `.?AU@?1??main@@YAHHPEAPEAD@Z@`aa6523bc` +CHECK: vtable: , base list: , field list: +CHECK: options: forward ref (<- 0x1008) | has unique name | scoped, sizeof 0 +CHECK: 0x1085 | LF_ARGLIST [size = 12, referenced] +CHECK: 0x1084: `main::__l2::` +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: , base list: , field list: +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: , base list: , field list: +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: , base list: , field list: +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 --- a/llvm/tools/llvm-pdbutil/CMakeLists.txt +++ b/llvm/tools/llvm-pdbutil/CMakeLists.txt @@ -30,5 +30,6 @@ PrettyTypedefDumper.cpp PrettyVariableDumper.cpp StreamUtil.cpp + TypeReferenceTracker.cpp YAMLOutputStyle.cpp ) diff --git a/llvm/tools/llvm-pdbutil/DumpOutputStyle.h b/llvm/tools/llvm-pdbutil/DumpOutputStyle.h --- a/llvm/tools/llvm-pdbutil/DumpOutputStyle.h +++ b/llvm/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 RefTracker; LinePrinter P; SmallVector StreamPurposes; }; diff --git a/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp b/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp --- 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::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 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 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 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 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/MinimalTypeDumper.h b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h --- a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h +++ b/llvm/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 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 HashValues; diff --git a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp --- 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 @@ // 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/llvm/tools/llvm-pdbutil/TypeReferenceTracker.h b/llvm/tools/llvm-pdbutil/TypeReferenceTracker.h new file mode 100644 --- /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 RecData, + ArrayRef 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, 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/TypeReferenceTracker.cpp b/llvm/tools/llvm-pdbutil/TypeReferenceTracker.cpp new file mode 100644 --- /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 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 DepList; + // FIXME: Check for failure. + discoverTypeIndicesInSymbol(Sym, DepList); + addReferencedTypes(Sym.content(), DepList); + markReferencedTypes(); +} + +void TypeReferenceTracker::addReferencedTypes(ArrayRef RecData, + ArrayRef DepList) { + for (const auto &Ref : DepList) { + // FIXME: Report OOB slice instead of truncating. + ArrayRef ByteSlice = + RecData.drop_front(Ref.Offset).take_front(4 * Ref.Count); + ArrayRef TIs( + reinterpret_cast(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 Rec = (Ids && RefKind == TiRefKind::IndexRef) + ? Ids->tryGetType(RefTI) + : Types.tryGetType(RefTI); + if (!Rec) + continue; // FIXME: Report a reference to a non-existant type. + + SmallVector 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/llvm-pdbutil.h b/llvm/tools/llvm-pdbutil/llvm-pdbutil.h --- a/llvm/tools/llvm-pdbutil/llvm-pdbutil.h +++ b/llvm/tools/llvm-pdbutil/llvm-pdbutil.h @@ -155,6 +155,7 @@ extern llvm::cl::opt DumpTypeExtras; extern llvm::cl::list DumpTypeIndex; extern llvm::cl::opt DumpTypeDependents; +extern llvm::cl::opt DumpTypeRefStats; extern llvm::cl::opt DumpSectionHeaders; extern llvm::cl::opt DumpIds; diff --git a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp --- a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ b/llvm/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 + 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 DumpTypeExtras("type-extras", cl::desc("dump type hashes and index offsets"),