mirror of
https://github.com/RPCS3/llvm.git
synced 2025-02-07 19:19:26 +00:00
ae734ae0ea
This adds a new command line option, -udt-stats, which breaks down the stats of S_UDT records. These are one of the biggest contributors to the size of /DEBUG:FASTLINK PDBs, so they need some additional tools to be able to analyze their usage. This option will dig into each S_UDT record and determine what kind of record it points to, and then break down the statistics by the target type. The goal here is to identify how our object files differ from MSVC object files in S_UDT records, so that we can output fewer of them and reach size parity. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@312276 91177308-0d34-0410-b5e6-96231b3b80d8
542 lines
21 KiB
C++
542 lines
21 KiB
C++
//===- MinimalTypeDumper.cpp ---------------------------------- *- C++ --*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "MinimalTypeDumper.h"
|
|
|
|
#include "FormatUtil.h"
|
|
#include "LinePrinter.h"
|
|
|
|
#include "llvm/DebugInfo/CodeView/CVRecord.h"
|
|
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
|
#include "llvm/DebugInfo/CodeView/CodeView.h"
|
|
#include "llvm/DebugInfo/CodeView/Formatters.h"
|
|
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
|
|
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
|
#include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
|
|
#include "llvm/Support/FormatVariadic.h"
|
|
#include "llvm/Support/MathExtras.h"
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::codeview;
|
|
using namespace llvm::pdb;
|
|
|
|
static std::string formatClassOptions(uint32_t IndentLevel,
|
|
ClassOptions Options) {
|
|
std::vector<std::string> Opts;
|
|
PUSH_FLAG(ClassOptions, HasConstructorOrDestructor, Options,
|
|
"has ctor / dtor");
|
|
PUSH_FLAG(ClassOptions, ContainsNestedClass, Options,
|
|
"contains nested class");
|
|
PUSH_FLAG(ClassOptions, HasConversionOperator, Options,
|
|
"conversion operator");
|
|
PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref");
|
|
PUSH_FLAG(ClassOptions, HasUniqueName, Options, "has unique name");
|
|
PUSH_FLAG(ClassOptions, Intrinsic, Options, "intrin");
|
|
PUSH_FLAG(ClassOptions, Nested, Options, "is nested");
|
|
PUSH_FLAG(ClassOptions, HasOverloadedOperator, Options,
|
|
"overloaded operator");
|
|
PUSH_FLAG(ClassOptions, HasOverloadedAssignmentOperator, Options,
|
|
"overloaded operator=");
|
|
PUSH_FLAG(ClassOptions, Packed, Options, "packed");
|
|
PUSH_FLAG(ClassOptions, Scoped, Options, "scoped");
|
|
PUSH_FLAG(ClassOptions, Sealed, Options, "sealed");
|
|
|
|
return typesetItemList(Opts, 4, IndentLevel, " | ");
|
|
}
|
|
|
|
static std::string pointerOptions(PointerOptions Options) {
|
|
std::vector<std::string> Opts;
|
|
PUSH_FLAG(PointerOptions, Flat32, Options, "flat32");
|
|
PUSH_FLAG(PointerOptions, Volatile, Options, "volatile");
|
|
PUSH_FLAG(PointerOptions, Const, Options, "const");
|
|
PUSH_FLAG(PointerOptions, Unaligned, Options, "unaligned");
|
|
PUSH_FLAG(PointerOptions, Restrict, Options, "restrict");
|
|
PUSH_FLAG(PointerOptions, WinRTSmartPointer, Options, "winrt");
|
|
if (Opts.empty())
|
|
return "None";
|
|
return join(Opts, " | ");
|
|
}
|
|
|
|
static std::string modifierOptions(ModifierOptions Options) {
|
|
std::vector<std::string> Opts;
|
|
PUSH_FLAG(ModifierOptions, Const, Options, "const");
|
|
PUSH_FLAG(ModifierOptions, Volatile, Options, "volatile");
|
|
PUSH_FLAG(ModifierOptions, Unaligned, Options, "unaligned");
|
|
if (Opts.empty())
|
|
return "None";
|
|
return join(Opts, " | ");
|
|
}
|
|
|
|
static std::string formatCallingConvention(CallingConvention Convention) {
|
|
switch (Convention) {
|
|
RETURN_CASE(CallingConvention, AlphaCall, "alphacall");
|
|
RETURN_CASE(CallingConvention, AM33Call, "am33call");
|
|
RETURN_CASE(CallingConvention, ArmCall, "armcall");
|
|
RETURN_CASE(CallingConvention, ClrCall, "clrcall");
|
|
RETURN_CASE(CallingConvention, FarC, "far cdecl");
|
|
RETURN_CASE(CallingConvention, FarFast, "far fastcall");
|
|
RETURN_CASE(CallingConvention, FarPascal, "far pascal");
|
|
RETURN_CASE(CallingConvention, FarStdCall, "far stdcall");
|
|
RETURN_CASE(CallingConvention, FarSysCall, "far syscall");
|
|
RETURN_CASE(CallingConvention, Generic, "generic");
|
|
RETURN_CASE(CallingConvention, Inline, "inline");
|
|
RETURN_CASE(CallingConvention, M32RCall, "m32rcall");
|
|
RETURN_CASE(CallingConvention, MipsCall, "mipscall");
|
|
RETURN_CASE(CallingConvention, NearC, "cdecl");
|
|
RETURN_CASE(CallingConvention, NearFast, "fastcall");
|
|
RETURN_CASE(CallingConvention, NearPascal, "pascal");
|
|
RETURN_CASE(CallingConvention, NearStdCall, "stdcall");
|
|
RETURN_CASE(CallingConvention, NearSysCall, "near syscall");
|
|
RETURN_CASE(CallingConvention, NearVector, "vectorcall");
|
|
RETURN_CASE(CallingConvention, PpcCall, "ppccall");
|
|
RETURN_CASE(CallingConvention, SHCall, "shcall");
|
|
RETURN_CASE(CallingConvention, SH5Call, "sh5call");
|
|
RETURN_CASE(CallingConvention, ThisCall, "thiscall");
|
|
RETURN_CASE(CallingConvention, TriCall, "tricall");
|
|
}
|
|
return formatUnknownEnum(Convention);
|
|
}
|
|
|
|
static std::string formatPointerMode(PointerMode Mode) {
|
|
switch (Mode) {
|
|
RETURN_CASE(PointerMode, LValueReference, "ref");
|
|
RETURN_CASE(PointerMode, Pointer, "pointer");
|
|
RETURN_CASE(PointerMode, PointerToDataMember, "data member pointer");
|
|
RETURN_CASE(PointerMode, PointerToMemberFunction, "member fn pointer");
|
|
RETURN_CASE(PointerMode, RValueReference, "rvalue ref");
|
|
}
|
|
return formatUnknownEnum(Mode);
|
|
}
|
|
|
|
static std::string memberAccess(MemberAccess Access) {
|
|
switch (Access) {
|
|
RETURN_CASE(MemberAccess, None, "");
|
|
RETURN_CASE(MemberAccess, Private, "private");
|
|
RETURN_CASE(MemberAccess, Protected, "protected");
|
|
RETURN_CASE(MemberAccess, Public, "public");
|
|
}
|
|
return formatUnknownEnum(Access);
|
|
}
|
|
|
|
static std::string methodKind(MethodKind Kind) {
|
|
switch (Kind) {
|
|
RETURN_CASE(MethodKind, Vanilla, "");
|
|
RETURN_CASE(MethodKind, Virtual, "virtual");
|
|
RETURN_CASE(MethodKind, Static, "static");
|
|
RETURN_CASE(MethodKind, Friend, "friend");
|
|
RETURN_CASE(MethodKind, IntroducingVirtual, "intro virtual");
|
|
RETURN_CASE(MethodKind, PureVirtual, "pure virtual");
|
|
RETURN_CASE(MethodKind, PureIntroducingVirtual, "pure intro virtual");
|
|
}
|
|
return formatUnknownEnum(Kind);
|
|
}
|
|
|
|
static std::string pointerKind(PointerKind Kind) {
|
|
switch (Kind) {
|
|
RETURN_CASE(PointerKind, Near16, "ptr16");
|
|
RETURN_CASE(PointerKind, Far16, "far ptr16");
|
|
RETURN_CASE(PointerKind, Huge16, "huge ptr16");
|
|
RETURN_CASE(PointerKind, BasedOnSegment, "segment based");
|
|
RETURN_CASE(PointerKind, BasedOnValue, "value based");
|
|
RETURN_CASE(PointerKind, BasedOnSegmentValue, "segment value based");
|
|
RETURN_CASE(PointerKind, BasedOnAddress, "address based");
|
|
RETURN_CASE(PointerKind, BasedOnSegmentAddress, "segment address based");
|
|
RETURN_CASE(PointerKind, BasedOnType, "type based");
|
|
RETURN_CASE(PointerKind, BasedOnSelf, "self based");
|
|
RETURN_CASE(PointerKind, Near32, "ptr32");
|
|
RETURN_CASE(PointerKind, Far32, "far ptr32");
|
|
RETURN_CASE(PointerKind, Near64, "ptr64");
|
|
}
|
|
return formatUnknownEnum(Kind);
|
|
}
|
|
|
|
static std::string memberAttributes(const MemberAttributes &Attrs) {
|
|
std::vector<std::string> Opts;
|
|
std::string Access = memberAccess(Attrs.getAccess());
|
|
std::string Kind = methodKind(Attrs.getMethodKind());
|
|
if (!Access.empty())
|
|
Opts.push_back(Access);
|
|
if (!Kind.empty())
|
|
Opts.push_back(Kind);
|
|
MethodOptions Flags = Attrs.getFlags();
|
|
PUSH_FLAG(MethodOptions, Pseudo, Flags, "pseudo");
|
|
PUSH_FLAG(MethodOptions, NoInherit, Flags, "noinherit");
|
|
PUSH_FLAG(MethodOptions, NoConstruct, Flags, "noconstruct");
|
|
PUSH_FLAG(MethodOptions, CompilerGenerated, Flags, "compiler-generated");
|
|
PUSH_FLAG(MethodOptions, Sealed, Flags, "sealed");
|
|
return join(Opts, " ");
|
|
}
|
|
|
|
static std::string formatPointerAttrs(const PointerRecord &Record) {
|
|
PointerMode Mode = Record.getMode();
|
|
PointerOptions Opts = Record.getOptions();
|
|
PointerKind Kind = Record.getPointerKind();
|
|
return formatv("mode = {0}, opts = {1}, kind = {2}", formatPointerMode(Mode),
|
|
pointerOptions(Opts), pointerKind(Kind));
|
|
}
|
|
|
|
static std::string formatFunctionOptions(FunctionOptions Options) {
|
|
std::vector<std::string> Opts;
|
|
|
|
PUSH_FLAG(FunctionOptions, CxxReturnUdt, Options, "returns cxx udt");
|
|
PUSH_FLAG(FunctionOptions, ConstructorWithVirtualBases, Options,
|
|
"constructor with virtual bases");
|
|
PUSH_FLAG(FunctionOptions, Constructor, Options, "constructor");
|
|
if (Opts.empty())
|
|
return "None";
|
|
return join(Opts, " | ");
|
|
}
|
|
|
|
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 {
|
|
std::string H;
|
|
if (Index.toArrayIndex() >= HashValues.size()) {
|
|
H = "(not present)";
|
|
} else {
|
|
uint32_t Hash = HashValues[Index.toArrayIndex()];
|
|
Expected<uint32_t> MaybeHash = hashTypeRecord(Record);
|
|
if (!MaybeHash)
|
|
return MaybeHash.takeError();
|
|
uint32_t OurHash = *MaybeHash;
|
|
OurHash %= NumHashBuckets;
|
|
if (Hash == OurHash)
|
|
H = "0x" + utohexstr(Hash);
|
|
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.Indent(Width + 3);
|
|
return Error::success();
|
|
}
|
|
Error MinimalTypeDumpVisitor::visitTypeEnd(CVType &Record) {
|
|
P.Unindent(Width + 3);
|
|
if (RecordBytes) {
|
|
AutoIndent Indent(P, 9);
|
|
P.formatBinary("Bytes", Record.RecordData, 0);
|
|
}
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitMemberBegin(CVMemberRecord &Record) {
|
|
P.formatLine("- {0}", formatTypeLeafKind(Record.Kind));
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitMemberEnd(CVMemberRecord &Record) {
|
|
if (RecordBytes) {
|
|
AutoIndent Indent(P, 2);
|
|
P.formatBinary("Bytes", Record.Data, 0);
|
|
}
|
|
return Error::success();
|
|
}
|
|
|
|
StringRef MinimalTypeDumpVisitor::getTypeName(TypeIndex TI) const {
|
|
if (TI.isNoneType())
|
|
return "";
|
|
return Types.getTypeName(TI);
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
|
|
FieldListRecord &FieldList) {
|
|
if (auto EC = codeview::visitMemberRecordStream(FieldList.Data, *this))
|
|
return EC;
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
|
|
StringIdRecord &String) {
|
|
P.format(" ID: {0}, String: {1}", String.getId(), String.getString());
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
|
|
ArgListRecord &Args) {
|
|
auto Indices = Args.getIndices();
|
|
if (Indices.empty())
|
|
return Error::success();
|
|
|
|
auto Max = std::max_element(Indices.begin(), Indices.end());
|
|
uint32_t W = NumDigits(Max->getIndex()) + 2;
|
|
|
|
for (auto I : Indices)
|
|
P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
|
|
getTypeName(I));
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
|
|
StringListRecord &Strings) {
|
|
auto Indices = Strings.getIndices();
|
|
if (Indices.empty())
|
|
return Error::success();
|
|
|
|
auto Max = std::max_element(Indices.begin(), Indices.end());
|
|
uint32_t W = NumDigits(Max->getIndex()) + 2;
|
|
|
|
for (auto I : Indices)
|
|
P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
|
|
getTypeName(I));
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
|
|
ClassRecord &Class) {
|
|
P.format(" `{0}`", Class.Name);
|
|
if (Class.hasUniqueName())
|
|
P.formatLine("unique name: `{0}`", Class.UniqueName);
|
|
P.formatLine("vtable: {0}, base list: {1}, field list: {2}",
|
|
Class.VTableShape, Class.DerivationList, Class.FieldList);
|
|
P.formatLine("options: {0}",
|
|
formatClassOptions(P.getIndentLevel(), Class.Options));
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
|
|
UnionRecord &Union) {
|
|
P.format(" `{0}`", Union.Name);
|
|
if (Union.hasUniqueName())
|
|
P.formatLine("unique name: `{0}`", Union.UniqueName);
|
|
P.formatLine("field list: {0}", Union.FieldList);
|
|
P.formatLine("options: {0}",
|
|
formatClassOptions(P.getIndentLevel(), Union.Options));
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) {
|
|
P.format(" `{0}`", Enum.Name);
|
|
if (Enum.hasUniqueName())
|
|
P.formatLine("unique name: `{0}`", Enum.UniqueName);
|
|
P.formatLine("field list: {0}, underlying type: {1}", Enum.FieldList,
|
|
Enum.UnderlyingType);
|
|
P.formatLine("options: {0}",
|
|
formatClassOptions(P.getIndentLevel(), Enum.Options));
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, ArrayRecord &AT) {
|
|
if (AT.Name.empty()) {
|
|
P.formatLine("size: {0}, index type: {1}, element type: {2}", AT.Size,
|
|
AT.IndexType, AT.ElementType);
|
|
} else {
|
|
P.formatLine("name: {0}, size: {1}, index type: {2}, element type: {3}",
|
|
AT.Name, AT.Size, AT.IndexType, AT.ElementType);
|
|
}
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
|
|
VFTableRecord &VFT) {
|
|
P.formatLine("offset: {0}, complete class: {1}, overridden vftable: {2}",
|
|
VFT.VFPtrOffset, VFT.CompleteClass, VFT.OverriddenVFTable);
|
|
P.formatLine("method names: ");
|
|
if (!VFT.MethodNames.empty()) {
|
|
std::string Sep =
|
|
formatv("\n{0}",
|
|
fmt_repeat(' ', P.getIndentLevel() + strlen("method names: ")))
|
|
.str();
|
|
P.print(join(VFT.MethodNames, Sep));
|
|
}
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
|
|
MemberFuncIdRecord &Id) {
|
|
P.formatLine("name = {0}, type = {1}, class type = {2}", Id.Name,
|
|
Id.FunctionType, Id.ClassType);
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
|
|
ProcedureRecord &Proc) {
|
|
P.formatLine("return type = {0}, # args = {1}, param list = {2}",
|
|
Proc.ReturnType, Proc.ParameterCount, Proc.ArgumentList);
|
|
P.formatLine("calling conv = {0}, options = {1}",
|
|
formatCallingConvention(Proc.CallConv),
|
|
formatFunctionOptions(Proc.Options));
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
|
|
MemberFunctionRecord &MF) {
|
|
P.formatLine("return type = {0}, # args = {1}, param list = {2}",
|
|
MF.ReturnType, MF.ParameterCount, MF.ArgumentList);
|
|
P.formatLine("class type = {0}, this type = {1}, this adjust = {2}",
|
|
MF.ClassType, MF.ThisType, MF.ThisPointerAdjustment);
|
|
P.formatLine("calling conv = {0}, options = {1}",
|
|
formatCallingConvention(MF.CallConv),
|
|
formatFunctionOptions(MF.Options));
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
|
|
FuncIdRecord &Func) {
|
|
P.formatLine("name = {0}, type = {1}, parent scope = {2}", Func.Name,
|
|
Func.FunctionType, Func.ParentScope);
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
|
|
TypeServer2Record &TS) {
|
|
P.formatLine("name = {0}, age = {1}, guid = {2}", TS.Name, TS.Age, TS.Guid);
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
|
|
PointerRecord &Ptr) {
|
|
P.formatLine("referent = {0}, {1}", Ptr.ReferentType,
|
|
formatPointerAttrs(Ptr));
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
|
|
ModifierRecord &Mod) {
|
|
P.formatLine("referent = {0}, modifiers = {1}", Mod.ModifiedType,
|
|
modifierOptions(Mod.Modifiers));
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
|
|
VFTableShapeRecord &Shape) {
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
|
|
UdtModSourceLineRecord &U) {
|
|
P.formatLine("udt = {0}, mod = {1}, file = {2}, line = {3}", U.UDT, U.Module,
|
|
U.SourceFile.getIndex(), U.LineNumber);
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
|
|
UdtSourceLineRecord &U) {
|
|
P.formatLine("udt = {0}, file = {1}, line = {2}", U.UDT,
|
|
U.SourceFile.getIndex(), U.LineNumber);
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
|
|
BitFieldRecord &BF) {
|
|
P.formatLine("type = {0}, bit offset = {1}, # bits = {2}", BF.Type,
|
|
BF.BitOffset, BF.BitSize);
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(
|
|
CVType &CVR, MethodOverloadListRecord &Overloads) {
|
|
for (auto &M : Overloads.Methods)
|
|
P.formatLine("- Method [type = {0}, vftable offset = {1}, attrs = {2}]",
|
|
M.Type, M.VFTableOffset, memberAttributes(M.Attrs));
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
|
|
BuildInfoRecord &BI) {
|
|
auto Indices = BI.ArgIndices;
|
|
if (Indices.empty())
|
|
return Error::success();
|
|
|
|
auto Max = std::max_element(Indices.begin(), Indices.end());
|
|
uint32_t W = NumDigits(Max->getIndex()) + 2;
|
|
|
|
for (auto I : Indices)
|
|
P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
|
|
getTypeName(I));
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, LabelRecord &R) {
|
|
std::string Type = (R.Mode == LabelType::Far) ? "far" : "near";
|
|
P.format(" type = {0}", Type);
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
|
|
NestedTypeRecord &Nested) {
|
|
P.format(" [name = `{0}`, parent = {1}]", Nested.Name, Nested.Type);
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
|
|
OneMethodRecord &Method) {
|
|
P.format(" [name = `{0}`]", Method.Name);
|
|
AutoIndent Indent(P);
|
|
P.formatLine("type = {0}, vftable offset = {1}, attrs = {2}", Method.Type,
|
|
Method.VFTableOffset, memberAttributes(Method.Attrs));
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
|
|
OverloadedMethodRecord &Method) {
|
|
P.format(" [name = `{0}`, # overloads = {1}, overload list = {2}]",
|
|
Method.Name, Method.NumOverloads, Method.MethodList);
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
|
|
DataMemberRecord &Field) {
|
|
P.format(" [name = `{0}`, Type = {1}, offset = {2}, attrs = {3}]", Field.Name,
|
|
Field.Type, Field.FieldOffset, memberAttributes(Field.Attrs));
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
|
|
StaticDataMemberRecord &Field) {
|
|
P.format(" [name = `{0}`, type = {1}, attrs = {2}]", Field.Name, Field.Type,
|
|
memberAttributes(Field.Attrs));
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
|
|
EnumeratorRecord &Enum) {
|
|
P.format(" [{0} = {1}]", Enum.Name,
|
|
Enum.Value.toString(10, Enum.Value.isSigned()));
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
|
|
BaseClassRecord &Base) {
|
|
AutoIndent Indent(P);
|
|
P.formatLine("type = {0}, offset = {1}, attrs = {2}", Base.Type, Base.Offset,
|
|
memberAttributes(Base.Attrs));
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
|
|
VirtualBaseClassRecord &Base) {
|
|
AutoIndent Indent(P);
|
|
P.formatLine(
|
|
"base = {0}, vbptr = {1}, vbptr offset = {2}, vtable index = {3}",
|
|
Base.BaseType, Base.VBPtrType, Base.VBPtrOffset, Base.VTableIndex);
|
|
P.formatLine("attrs = {0}", memberAttributes(Base.Attrs));
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
|
|
ListContinuationRecord &Cont) {
|
|
P.format(" continuation = {0}", Cont.ContinuationIndex);
|
|
return Error::success();
|
|
}
|
|
|
|
Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
|
|
VFPtrRecord &VFP) {
|
|
P.format(" type = {0}", VFP.Type);
|
|
return Error::success();
|
|
}
|