//===-- TypeDumpVisitor.cpp - CodeView type info dumper ----------*- C++-*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h"

#include "llvm/ADT/SmallString.h"
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
#include "llvm/DebugInfo/CodeView/Formatters.h"
#include "llvm/DebugInfo/CodeView/TypeCollection.h"
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/ScopedPrinter.h"

using namespace llvm;
using namespace llvm::codeview;

static const EnumEntry<TypeLeafKind> LeafTypeNames[] = {
#define CV_TYPE(enum, val) {#enum, enum},
#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
};

#define ENUM_ENTRY(enum_class, enum)                                           \
  { #enum, std::underlying_type < enum_class > ::type(enum_class::enum) }

static const EnumEntry<uint16_t> ClassOptionNames[] = {
    ENUM_ENTRY(ClassOptions, Packed),
    ENUM_ENTRY(ClassOptions, HasConstructorOrDestructor),
    ENUM_ENTRY(ClassOptions, HasOverloadedOperator),
    ENUM_ENTRY(ClassOptions, Nested),
    ENUM_ENTRY(ClassOptions, ContainsNestedClass),
    ENUM_ENTRY(ClassOptions, HasOverloadedAssignmentOperator),
    ENUM_ENTRY(ClassOptions, HasConversionOperator),
    ENUM_ENTRY(ClassOptions, ForwardReference),
    ENUM_ENTRY(ClassOptions, Scoped),
    ENUM_ENTRY(ClassOptions, HasUniqueName),
    ENUM_ENTRY(ClassOptions, Sealed),
    ENUM_ENTRY(ClassOptions, Intrinsic),
};

static const EnumEntry<uint8_t> MemberAccessNames[] = {
    ENUM_ENTRY(MemberAccess, None), ENUM_ENTRY(MemberAccess, Private),
    ENUM_ENTRY(MemberAccess, Protected), ENUM_ENTRY(MemberAccess, Public),
};

static const EnumEntry<uint16_t> MethodOptionNames[] = {
    ENUM_ENTRY(MethodOptions, Pseudo),
    ENUM_ENTRY(MethodOptions, NoInherit),
    ENUM_ENTRY(MethodOptions, NoConstruct),
    ENUM_ENTRY(MethodOptions, CompilerGenerated),
    ENUM_ENTRY(MethodOptions, Sealed),
};

static const EnumEntry<uint16_t> MemberKindNames[] = {
    ENUM_ENTRY(MethodKind, Vanilla),
    ENUM_ENTRY(MethodKind, Virtual),
    ENUM_ENTRY(MethodKind, Static),
    ENUM_ENTRY(MethodKind, Friend),
    ENUM_ENTRY(MethodKind, IntroducingVirtual),
    ENUM_ENTRY(MethodKind, PureVirtual),
    ENUM_ENTRY(MethodKind, PureIntroducingVirtual),
};

static const EnumEntry<uint8_t> PtrKindNames[] = {
    ENUM_ENTRY(PointerKind, Near16),
    ENUM_ENTRY(PointerKind, Far16),
    ENUM_ENTRY(PointerKind, Huge16),
    ENUM_ENTRY(PointerKind, BasedOnSegment),
    ENUM_ENTRY(PointerKind, BasedOnValue),
    ENUM_ENTRY(PointerKind, BasedOnSegmentValue),
    ENUM_ENTRY(PointerKind, BasedOnAddress),
    ENUM_ENTRY(PointerKind, BasedOnSegmentAddress),
    ENUM_ENTRY(PointerKind, BasedOnType),
    ENUM_ENTRY(PointerKind, BasedOnSelf),
    ENUM_ENTRY(PointerKind, Near32),
    ENUM_ENTRY(PointerKind, Far32),
    ENUM_ENTRY(PointerKind, Near64),
};

static const EnumEntry<uint8_t> PtrModeNames[] = {
    ENUM_ENTRY(PointerMode, Pointer),
    ENUM_ENTRY(PointerMode, LValueReference),
    ENUM_ENTRY(PointerMode, PointerToDataMember),
    ENUM_ENTRY(PointerMode, PointerToMemberFunction),
    ENUM_ENTRY(PointerMode, RValueReference),
};

static const EnumEntry<uint16_t> PtrMemberRepNames[] = {
    ENUM_ENTRY(PointerToMemberRepresentation, Unknown),
    ENUM_ENTRY(PointerToMemberRepresentation, SingleInheritanceData),
    ENUM_ENTRY(PointerToMemberRepresentation, MultipleInheritanceData),
    ENUM_ENTRY(PointerToMemberRepresentation, VirtualInheritanceData),
    ENUM_ENTRY(PointerToMemberRepresentation, GeneralData),
    ENUM_ENTRY(PointerToMemberRepresentation, SingleInheritanceFunction),
    ENUM_ENTRY(PointerToMemberRepresentation, MultipleInheritanceFunction),
    ENUM_ENTRY(PointerToMemberRepresentation, VirtualInheritanceFunction),
    ENUM_ENTRY(PointerToMemberRepresentation, GeneralFunction),
};

static const EnumEntry<uint16_t> TypeModifierNames[] = {
    ENUM_ENTRY(ModifierOptions, Const), ENUM_ENTRY(ModifierOptions, Volatile),
    ENUM_ENTRY(ModifierOptions, Unaligned),
};

static const EnumEntry<uint8_t> CallingConventions[] = {
    ENUM_ENTRY(CallingConvention, NearC),
    ENUM_ENTRY(CallingConvention, FarC),
    ENUM_ENTRY(CallingConvention, NearPascal),
    ENUM_ENTRY(CallingConvention, FarPascal),
    ENUM_ENTRY(CallingConvention, NearFast),
    ENUM_ENTRY(CallingConvention, FarFast),
    ENUM_ENTRY(CallingConvention, NearStdCall),
    ENUM_ENTRY(CallingConvention, FarStdCall),
    ENUM_ENTRY(CallingConvention, NearSysCall),
    ENUM_ENTRY(CallingConvention, FarSysCall),
    ENUM_ENTRY(CallingConvention, ThisCall),
    ENUM_ENTRY(CallingConvention, MipsCall),
    ENUM_ENTRY(CallingConvention, Generic),
    ENUM_ENTRY(CallingConvention, AlphaCall),
    ENUM_ENTRY(CallingConvention, PpcCall),
    ENUM_ENTRY(CallingConvention, SHCall),
    ENUM_ENTRY(CallingConvention, ArmCall),
    ENUM_ENTRY(CallingConvention, AM33Call),
    ENUM_ENTRY(CallingConvention, TriCall),
    ENUM_ENTRY(CallingConvention, SH5Call),
    ENUM_ENTRY(CallingConvention, M32RCall),
    ENUM_ENTRY(CallingConvention, ClrCall),
    ENUM_ENTRY(CallingConvention, Inline),
    ENUM_ENTRY(CallingConvention, NearVector),
};

static const EnumEntry<uint8_t> FunctionOptionEnum[] = {
    ENUM_ENTRY(FunctionOptions, CxxReturnUdt),
    ENUM_ENTRY(FunctionOptions, Constructor),
    ENUM_ENTRY(FunctionOptions, ConstructorWithVirtualBases),
};

static const EnumEntry<uint16_t> LabelTypeEnum[] = {
    ENUM_ENTRY(LabelType, Near), ENUM_ENTRY(LabelType, Far),
};

#undef ENUM_ENTRY

static StringRef getLeafTypeName(TypeLeafKind LT) {
  switch (LT) {
#define TYPE_RECORD(ename, value, name)                                        \
  case ename:                                                                  \
    return #name;
#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
  default:
    break;
  }
  return "UnknownLeaf";
}

void TypeDumpVisitor::printTypeIndex(StringRef FieldName, TypeIndex TI) const {
  codeview::printTypeIndex(*W, FieldName, TI, TpiTypes);
}

void TypeDumpVisitor::printItemIndex(StringRef FieldName, TypeIndex TI) const {
  codeview::printTypeIndex(*W, FieldName, TI, getSourceTypes());
}

Error TypeDumpVisitor::visitTypeBegin(CVType &Record) {
  return visitTypeBegin(Record, TypeIndex::fromArrayIndex(TpiTypes.size()));
}

Error TypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) {
  W->startLine() << getLeafTypeName(Record.Type);
  W->getOStream() << " (" << HexNumber(Index.getIndex()) << ")";
  W->getOStream() << " {\n";
  W->indent();
  W->printEnum("TypeLeafKind", unsigned(Record.Type),
               makeArrayRef(LeafTypeNames));
  return Error::success();
}

Error TypeDumpVisitor::visitTypeEnd(CVType &Record) {
  if (PrintRecordBytes)
    W->printBinaryBlock("LeafData", getBytesAsCharacters(Record.content()));

  W->unindent();
  W->startLine() << "}\n";
  return Error::success();
}

Error TypeDumpVisitor::visitMemberBegin(CVMemberRecord &Record) {
  W->startLine() << getLeafTypeName(Record.Kind);
  W->getOStream() << " {\n";
  W->indent();
  W->printEnum("TypeLeafKind", unsigned(Record.Kind),
               makeArrayRef(LeafTypeNames));
  return Error::success();
}

Error TypeDumpVisitor::visitMemberEnd(CVMemberRecord &Record) {
  if (PrintRecordBytes)
    W->printBinaryBlock("LeafData", getBytesAsCharacters(Record.Data));

  W->unindent();
  W->startLine() << "}\n";
  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR,
                                        FieldListRecord &FieldList) {
  if (auto EC = codeview::visitMemberRecordStream(FieldList.Data, *this))
    return EC;

  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, StringIdRecord &String) {
  printItemIndex("Id", String.getId());
  W->printString("StringData", String.getString());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, ArgListRecord &Args) {
  auto Indices = Args.getIndices();
  uint32_t Size = Indices.size();
  W->printNumber("NumArgs", Size);
  ListScope Arguments(*W, "Arguments");
  for (uint32_t I = 0; I < Size; ++I) {
    printTypeIndex("ArgType", Indices[I]);
  }
  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, StringListRecord &Strs) {
  auto Indices = Strs.getIndices();
  uint32_t Size = Indices.size();
  W->printNumber("NumStrings", Size);
  ListScope Arguments(*W, "Strings");
  for (uint32_t I = 0; I < Size; ++I) {
    printItemIndex("String", Indices[I]);
  }
  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, ClassRecord &Class) {
  uint16_t Props = static_cast<uint16_t>(Class.getOptions());
  W->printNumber("MemberCount", Class.getMemberCount());
  W->printFlags("Properties", Props, makeArrayRef(ClassOptionNames));
  printTypeIndex("FieldList", Class.getFieldList());
  printTypeIndex("DerivedFrom", Class.getDerivationList());
  printTypeIndex("VShape", Class.getVTableShape());
  W->printNumber("SizeOf", Class.getSize());
  W->printString("Name", Class.getName());
  if (Props & uint16_t(ClassOptions::HasUniqueName))
    W->printString("LinkageName", Class.getUniqueName());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, UnionRecord &Union) {
  uint16_t Props = static_cast<uint16_t>(Union.getOptions());
  W->printNumber("MemberCount", Union.getMemberCount());
  W->printFlags("Properties", Props, makeArrayRef(ClassOptionNames));
  printTypeIndex("FieldList", Union.getFieldList());
  W->printNumber("SizeOf", Union.getSize());
  W->printString("Name", Union.getName());
  if (Props & uint16_t(ClassOptions::HasUniqueName))
    W->printString("LinkageName", Union.getUniqueName());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) {
  uint16_t Props = static_cast<uint16_t>(Enum.getOptions());
  W->printNumber("NumEnumerators", Enum.getMemberCount());
  W->printFlags("Properties", uint16_t(Enum.getOptions()),
                makeArrayRef(ClassOptionNames));
  printTypeIndex("UnderlyingType", Enum.getUnderlyingType());
  printTypeIndex("FieldListType", Enum.getFieldList());
  W->printString("Name", Enum.getName());
  if (Props & uint16_t(ClassOptions::HasUniqueName))
    W->printString("LinkageName", Enum.getUniqueName());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, ArrayRecord &AT) {
  printTypeIndex("ElementType", AT.getElementType());
  printTypeIndex("IndexType", AT.getIndexType());
  W->printNumber("SizeOf", AT.getSize());
  W->printString("Name", AT.getName());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, VFTableRecord &VFT) {
  printTypeIndex("CompleteClass", VFT.getCompleteClass());
  printTypeIndex("OverriddenVFTable", VFT.getOverriddenVTable());
  W->printHex("VFPtrOffset", VFT.getVFPtrOffset());
  W->printString("VFTableName", VFT.getName());
  for (auto N : VFT.getMethodNames())
    W->printString("MethodName", N);
  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, MemberFuncIdRecord &Id) {
  printTypeIndex("ClassType", Id.getClassType());
  printTypeIndex("FunctionType", Id.getFunctionType());
  W->printString("Name", Id.getName());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, ProcedureRecord &Proc) {
  printTypeIndex("ReturnType", Proc.getReturnType());
  W->printEnum("CallingConvention", uint8_t(Proc.getCallConv()),
               makeArrayRef(CallingConventions));
  W->printFlags("FunctionOptions", uint8_t(Proc.getOptions()),
                makeArrayRef(FunctionOptionEnum));
  W->printNumber("NumParameters", Proc.getParameterCount());
  printTypeIndex("ArgListType", Proc.getArgumentList());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, MemberFunctionRecord &MF) {
  printTypeIndex("ReturnType", MF.getReturnType());
  printTypeIndex("ClassType", MF.getClassType());
  printTypeIndex("ThisType", MF.getThisType());
  W->printEnum("CallingConvention", uint8_t(MF.getCallConv()),
               makeArrayRef(CallingConventions));
  W->printFlags("FunctionOptions", uint8_t(MF.getOptions()),
                makeArrayRef(FunctionOptionEnum));
  W->printNumber("NumParameters", MF.getParameterCount());
  printTypeIndex("ArgListType", MF.getArgumentList());
  W->printNumber("ThisAdjustment", MF.getThisPointerAdjustment());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR,
                                        MethodOverloadListRecord &MethodList) {
  for (auto &M : MethodList.getMethods()) {
    ListScope S(*W, "Method");
    printMemberAttributes(M.getAccess(), M.getMethodKind(), M.getOptions());
    printTypeIndex("Type", M.getType());
    if (M.isIntroducingVirtual())
      W->printHex("VFTableOffset", M.getVFTableOffset());
  }
  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, FuncIdRecord &Func) {
  printItemIndex("ParentScope", Func.getParentScope());
  printTypeIndex("FunctionType", Func.getFunctionType());
  W->printString("Name", Func.getName());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, TypeServer2Record &TS) {
  W->printString("Guid", formatv("{0}", TS.getGuid()).str());
  W->printNumber("Age", TS.getAge());
  W->printString("Name", TS.getName());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, PointerRecord &Ptr) {
  printTypeIndex("PointeeType", Ptr.getReferentType());
  W->printHex("PointerAttributes", uint32_t(Ptr.getOptions()));
  W->printEnum("PtrType", unsigned(Ptr.getPointerKind()),
               makeArrayRef(PtrKindNames));
  W->printEnum("PtrMode", unsigned(Ptr.getMode()), makeArrayRef(PtrModeNames));

  W->printNumber("IsFlat", Ptr.isFlat());
  W->printNumber("IsConst", Ptr.isConst());
  W->printNumber("IsVolatile", Ptr.isVolatile());
  W->printNumber("IsUnaligned", Ptr.isUnaligned());
  W->printNumber("IsRestrict", Ptr.isRestrict());
  W->printNumber("SizeOf", Ptr.getSize());

  if (Ptr.isPointerToMember()) {
    const MemberPointerInfo &MI = Ptr.getMemberInfo();

    printTypeIndex("ClassType", MI.getContainingType());
    W->printEnum("Representation", uint16_t(MI.getRepresentation()),
                 makeArrayRef(PtrMemberRepNames));
  }

  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, ModifierRecord &Mod) {
  uint16_t Mods = static_cast<uint16_t>(Mod.getModifiers());
  printTypeIndex("ModifiedType", Mod.getModifiedType());
  W->printFlags("Modifiers", Mods, makeArrayRef(TypeModifierNames));

  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, BitFieldRecord &BitField) {
  printTypeIndex("Type", BitField.getType());
  W->printNumber("BitSize", BitField.getBitSize());
  W->printNumber("BitOffset", BitField.getBitOffset());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR,
                                        VFTableShapeRecord &Shape) {
  W->printNumber("VFEntryCount", Shape.getEntryCount());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR,
                                        UdtSourceLineRecord &Line) {
  printTypeIndex("UDT", Line.getUDT());
  printItemIndex("SourceFile", Line.getSourceFile());
  W->printNumber("LineNumber", Line.getLineNumber());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR,
                                        UdtModSourceLineRecord &Line) {
  printTypeIndex("UDT", Line.getUDT());
  printItemIndex("SourceFile", Line.getSourceFile());
  W->printNumber("LineNumber", Line.getLineNumber());
  W->printNumber("Module", Line.getModule());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, BuildInfoRecord &Args) {
  W->printNumber("NumArgs", static_cast<uint32_t>(Args.getArgs().size()));

  ListScope Arguments(*W, "Arguments");
  for (auto Arg : Args.getArgs()) {
    printItemIndex("ArgType", Arg);
  }
  return Error::success();
}

void TypeDumpVisitor::printMemberAttributes(MemberAttributes Attrs) {
  return printMemberAttributes(Attrs.getAccess(), Attrs.getMethodKind(),
                               Attrs.getFlags());
}

void TypeDumpVisitor::printMemberAttributes(MemberAccess Access,
                                            MethodKind Kind,
                                            MethodOptions Options) {
  W->printEnum("AccessSpecifier", uint8_t(Access),
               makeArrayRef(MemberAccessNames));
  // Data members will be vanilla. Don't try to print a method kind for them.
  if (Kind != MethodKind::Vanilla)
    W->printEnum("MethodKind", unsigned(Kind), makeArrayRef(MemberKindNames));
  if (Options != MethodOptions::None) {
    W->printFlags("MethodOptions", unsigned(Options),
                  makeArrayRef(MethodOptionNames));
  }
}

Error TypeDumpVisitor::visitUnknownMember(CVMemberRecord &Record) {
  W->printHex("UnknownMember", unsigned(Record.Kind));
  return Error::success();
}

Error TypeDumpVisitor::visitUnknownType(CVType &Record) {
  W->printEnum("Kind", uint16_t(Record.kind()), makeArrayRef(LeafTypeNames));
  W->printNumber("Length", uint32_t(Record.content().size()));
  return Error::success();
}

Error TypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
                                        NestedTypeRecord &Nested) {
  printTypeIndex("Type", Nested.getNestedType());
  W->printString("Name", Nested.getName());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
                                        OneMethodRecord &Method) {
  MethodKind K = Method.getMethodKind();
  printMemberAttributes(Method.getAccess(), K, Method.getOptions());
  printTypeIndex("Type", Method.getType());
  // If virtual, then read the vftable offset.
  if (Method.isIntroducingVirtual())
    W->printHex("VFTableOffset", Method.getVFTableOffset());
  W->printString("Name", Method.getName());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
                                        OverloadedMethodRecord &Method) {
  W->printHex("MethodCount", Method.getNumOverloads());
  printTypeIndex("MethodListIndex", Method.getMethodList());
  W->printString("Name", Method.getName());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
                                        DataMemberRecord &Field) {
  printMemberAttributes(Field.getAccess(), MethodKind::Vanilla,
                        MethodOptions::None);
  printTypeIndex("Type", Field.getType());
  W->printHex("FieldOffset", Field.getFieldOffset());
  W->printString("Name", Field.getName());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
                                        StaticDataMemberRecord &Field) {
  printMemberAttributes(Field.getAccess(), MethodKind::Vanilla,
                        MethodOptions::None);
  printTypeIndex("Type", Field.getType());
  W->printString("Name", Field.getName());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
                                        VFPtrRecord &VFTable) {
  printTypeIndex("Type", VFTable.getType());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
                                        EnumeratorRecord &Enum) {
  printMemberAttributes(Enum.getAccess(), MethodKind::Vanilla,
                        MethodOptions::None);
  W->printNumber("EnumValue", Enum.getValue());
  W->printString("Name", Enum.getName());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
                                        BaseClassRecord &Base) {
  printMemberAttributes(Base.getAccess(), MethodKind::Vanilla,
                        MethodOptions::None);
  printTypeIndex("BaseType", Base.getBaseType());
  W->printHex("BaseOffset", Base.getBaseOffset());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
                                        VirtualBaseClassRecord &Base) {
  printMemberAttributes(Base.getAccess(), MethodKind::Vanilla,
                        MethodOptions::None);
  printTypeIndex("BaseType", Base.getBaseType());
  printTypeIndex("VBPtrType", Base.getVBPtrType());
  W->printHex("VBPtrOffset", Base.getVBPtrOffset());
  W->printHex("VBTableIndex", Base.getVTableIndex());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
                                        ListContinuationRecord &Cont) {
  printTypeIndex("ContinuationIndex", Cont.getContinuationIndex());
  return Error::success();
}

Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, LabelRecord &LR) {
  W->printEnum("Mode", uint16_t(LR.Mode), makeArrayRef(LabelTypeEnum));
  return Error::success();
}
