//===- TypeRecordMapping.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 "llvm/DebugInfo/CodeView/TypeRecordMapping.h" #include "llvm/ADT/StringExtras.h" #include "llvm/DebugInfo/CodeView/EnumTables.h" using namespace llvm; using namespace llvm::codeview; namespace { #define error(X) \ if (auto EC = X) \ return EC; static const EnumEntry LeafTypeNames[] = { #define CV_TYPE(enum, val) {#enum, enum}, #include "llvm/DebugInfo/CodeView/CodeViewTypes.def" }; 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"; } template static bool compEnumNames(const EnumEntry &lhs, const EnumEntry &rhs) { return lhs.Name < rhs.Name; } template static std::string getFlagNames(CodeViewRecordIO &IO, T Value, ArrayRef> Flags) { if (!IO.isStreaming()) return std::string(""); typedef EnumEntry FlagEntry; typedef SmallVector FlagVector; FlagVector SetFlags; for (const auto &Flag : Flags) { if (Flag.Value == 0) continue; if ((Value & Flag.Value) == Flag.Value) { SetFlags.push_back(Flag); } } llvm::sort(SetFlags, &compEnumNames); std::string FlagLabel; bool FirstOcc = true; for (const auto &Flag : SetFlags) { if (FirstOcc) FirstOcc = false; else FlagLabel += (" | "); FlagLabel += (Flag.Name.str() + " (0x" + utohexstr(Flag.Value) + ")"); } if (!FlagLabel.empty()) { std::string LabelWithBraces(" ( "); LabelWithBraces += FlagLabel + " )"; return LabelWithBraces; } else return FlagLabel; } template static StringRef getEnumName(CodeViewRecordIO &IO, T Value, ArrayRef> EnumValues) { if (!IO.isStreaming()) return ""; StringRef Name; for (const auto &EnumItem : EnumValues) { if (EnumItem.Value == Value) { Name = EnumItem.Name; break; } } return Name; } static std::string getMemberAttributes(CodeViewRecordIO &IO, MemberAccess Access, MethodKind Kind, MethodOptions Options) { if (!IO.isStreaming()) return ""; std::string AccessSpecifier = getEnumName(IO, uint8_t(Access), makeArrayRef(getMemberAccessNames())); std::string MemberAttrs(AccessSpecifier); if (Kind != MethodKind::Vanilla) { std::string MethodKind = getEnumName(IO, unsigned(Kind), makeArrayRef(getMemberKindNames())); MemberAttrs += ", " + MethodKind; } if (Options != MethodOptions::None) { std::string MethodOptions = getFlagNames( IO, unsigned(Options), makeArrayRef(getMethodOptionNames())); MemberAttrs += ", " + MethodOptions; } return MemberAttrs; } struct MapOneMethodRecord { explicit MapOneMethodRecord(bool IsFromOverloadList) : IsFromOverloadList(IsFromOverloadList) {} Error operator()(CodeViewRecordIO &IO, OneMethodRecord &Method) const { std::string Attrs = getMemberAttributes( IO, Method.getAccess(), Method.getMethodKind(), Method.getOptions()); error(IO.mapInteger(Method.Attrs.Attrs, "Attrs: " + Attrs)); if (IsFromOverloadList) { uint16_t Padding = 0; error(IO.mapInteger(Padding)); } error(IO.mapInteger(Method.Type, "Type")); if (Method.isIntroducingVirtual()) { error(IO.mapInteger(Method.VFTableOffset, "VFTableOffset")); } else if (IO.isReading()) Method.VFTableOffset = -1; if (!IsFromOverloadList) error(IO.mapStringZ(Method.Name, "Name")); return Error::success(); } private: bool IsFromOverloadList; }; } // namespace static Error mapNameAndUniqueName(CodeViewRecordIO &IO, StringRef &Name, StringRef &UniqueName, bool HasUniqueName) { if (IO.isWriting()) { // Try to be smart about what we write here. We can't write anything too // large, so if we're going to go over the limit, truncate both the name // and unique name by the same amount. size_t BytesLeft = IO.maxFieldLength(); if (HasUniqueName) { size_t BytesNeeded = Name.size() + UniqueName.size() + 2; StringRef N = Name; StringRef U = UniqueName; if (BytesNeeded > BytesLeft) { size_t BytesToDrop = (BytesNeeded - BytesLeft); size_t DropN = std::min(N.size(), BytesToDrop / 2); size_t DropU = std::min(U.size(), BytesToDrop - DropN); N = N.drop_back(DropN); U = U.drop_back(DropU); } error(IO.mapStringZ(N)); error(IO.mapStringZ(U)); } else { // Cap the length of the string at however many bytes we have available, // plus one for the required null terminator. auto N = StringRef(Name).take_front(BytesLeft - 1); error(IO.mapStringZ(N)); } } else { // Reading & Streaming mode come after writing mode is executed for each // record. Truncating large names are done during writing, so its not // necessary to do it while reading or streaming. error(IO.mapStringZ(Name, "Name")); if (HasUniqueName) error(IO.mapStringZ(UniqueName, "LinkageName")); } return Error::success(); } Error TypeRecordMapping::visitTypeBegin(CVType &CVR) { assert(!TypeKind.hasValue() && "Already in a type mapping!"); assert(!MemberKind.hasValue() && "Already in a member mapping!"); // FieldList and MethodList records can be any length because they can be // split with continuation records. All other record types cannot be // longer than the maximum record length. Optional MaxLen; if (CVR.kind() != TypeLeafKind::LF_FIELDLIST && CVR.kind() != TypeLeafKind::LF_METHODLIST) MaxLen = MaxRecordLength - sizeof(RecordPrefix); error(IO.beginRecord(MaxLen)); TypeKind = CVR.kind(); if (IO.isStreaming()) { auto RecordKind = CVR.kind(); uint16_t RecordLen = CVR.length() - 2; std::string RecordKindName = getEnumName(IO, unsigned(RecordKind), makeArrayRef(LeafTypeNames)); error(IO.mapInteger(RecordLen, "Record length")); error(IO.mapEnum(RecordKind, "Record kind: " + RecordKindName)); } return Error::success(); } Error TypeRecordMapping::visitTypeBegin(CVType &CVR, TypeIndex Index) { if (IO.isStreaming()) IO.emitRawComment(" " + getLeafTypeName(CVR.kind()) + " (0x" + utohexstr(Index.getIndex()) + ")"); return visitTypeBegin(CVR); } Error TypeRecordMapping::visitTypeEnd(CVType &Record) { assert(TypeKind.hasValue() && "Not in a type mapping!"); assert(!MemberKind.hasValue() && "Still in a member mapping!"); error(IO.endRecord()); TypeKind.reset(); return Error::success(); } Error TypeRecordMapping::visitMemberBegin(CVMemberRecord &Record) { assert(TypeKind.hasValue() && "Not in a type mapping!"); assert(!MemberKind.hasValue() && "Already in a member mapping!"); // The largest possible subrecord is one in which there is a record prefix, // followed by the subrecord, followed by a continuation, and that entire // sequence spaws `MaxRecordLength` bytes. So the record's length is // calculated as follows. constexpr uint32_t ContinuationLength = 8; error(IO.beginRecord(MaxRecordLength - sizeof(RecordPrefix) - ContinuationLength)); MemberKind = Record.Kind; if (IO.isStreaming()) { std::string MemberKindName = getLeafTypeName(Record.Kind); MemberKindName += " ( " + (getEnumName(IO, unsigned(Record.Kind), makeArrayRef(LeafTypeNames))) .str() + " )"; error(IO.mapEnum(Record.Kind, "Member kind: " + MemberKindName)); } return Error::success(); } Error TypeRecordMapping::visitMemberEnd(CVMemberRecord &Record) { assert(TypeKind.hasValue() && "Not in a type mapping!"); assert(MemberKind.hasValue() && "Not in a member mapping!"); if (IO.isReading()) { if (auto EC = IO.skipPadding()) return EC; } MemberKind.reset(); error(IO.endRecord()); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ModifierRecord &Record) { std::string ModifierNames = getFlagNames(IO, static_cast(Record.Modifiers), makeArrayRef(getTypeModifierNames())); error(IO.mapInteger(Record.ModifiedType, "ModifiedType")); error(IO.mapEnum(Record.Modifiers, "Modifiers" + ModifierNames)); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ProcedureRecord &Record) { std::string CallingConvName = getEnumName( IO, uint8_t(Record.CallConv), makeArrayRef(getCallingConventions())); std::string FuncOptionNames = getFlagNames(IO, static_cast(Record.Options), makeArrayRef(getFunctionOptionEnum())); error(IO.mapInteger(Record.ReturnType, "ReturnType")); error(IO.mapEnum(Record.CallConv, "CallingConvention: " + CallingConvName)); error(IO.mapEnum(Record.Options, "FunctionOptions" + FuncOptionNames)); error(IO.mapInteger(Record.ParameterCount, "NumParameters")); error(IO.mapInteger(Record.ArgumentList, "ArgListType")); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, MemberFunctionRecord &Record) { std::string CallingConvName = getEnumName( IO, uint8_t(Record.CallConv), makeArrayRef(getCallingConventions())); std::string FuncOptionNames = getFlagNames(IO, static_cast(Record.Options), makeArrayRef(getFunctionOptionEnum())); error(IO.mapInteger(Record.ReturnType, "ReturnType")); error(IO.mapInteger(Record.ClassType, "ClassType")); error(IO.mapInteger(Record.ThisType, "ThisType")); error(IO.mapEnum(Record.CallConv, "CallingConvention: " + CallingConvName)); error(IO.mapEnum(Record.Options, "FunctionOptions" + FuncOptionNames)); error(IO.mapInteger(Record.ParameterCount, "NumParameters")); error(IO.mapInteger(Record.ArgumentList, "ArgListType")); error(IO.mapInteger(Record.ThisPointerAdjustment, "ThisAdjustment")); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ArgListRecord &Record) { error(IO.mapVectorN( Record.ArgIndices, [](CodeViewRecordIO &IO, TypeIndex &N) { return IO.mapInteger(N, "Argument"); }, "NumArgs")); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, StringListRecord &Record) { error(IO.mapVectorN( Record.StringIndices, [](CodeViewRecordIO &IO, TypeIndex &N) { return IO.mapInteger(N, "Strings"); }, "NumStrings")); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, PointerRecord &Record) { SmallString<128> Attr("Attrs: "); if (IO.isStreaming()) { std::string PtrType = getEnumName(IO, unsigned(Record.getPointerKind()), makeArrayRef(getPtrKindNames())); Attr += "[ Type: " + PtrType; std::string PtrMode = getEnumName(IO, unsigned(Record.getMode()), makeArrayRef(getPtrModeNames())); Attr += ", Mode: " + PtrMode; auto PtrSizeOf = Record.getSize(); Attr += ", SizeOf: " + itostr(PtrSizeOf); if (Record.isFlat()) Attr += ", isFlat"; if (Record.isConst()) Attr += ", isConst"; if (Record.isVolatile()) Attr += ", isVolatile"; if (Record.isUnaligned()) Attr += ", isUnaligned"; if (Record.isRestrict()) Attr += ", isRestricted"; if (Record.isLValueReferenceThisPtr()) Attr += ", isThisPtr&"; if (Record.isRValueReferenceThisPtr()) Attr += ", isThisPtr&&"; Attr += " ]"; } error(IO.mapInteger(Record.ReferentType, "PointeeType")); error(IO.mapInteger(Record.Attrs, Attr)); if (Record.isPointerToMember()) { if (IO.isReading()) Record.MemberInfo.emplace(); MemberPointerInfo &M = *Record.MemberInfo; error(IO.mapInteger(M.ContainingType, "ClassType")); std::string PtrMemberGetRepresentation = getEnumName( IO, uint16_t(M.Representation), makeArrayRef(getPtrMemberRepNames())); error(IO.mapEnum(M.Representation, "Representation: " + PtrMemberGetRepresentation)); } return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ArrayRecord &Record) { error(IO.mapInteger(Record.ElementType, "ElementType")); error(IO.mapInteger(Record.IndexType, "IndexType")); error(IO.mapEncodedInteger(Record.Size, "SizeOf")); error(IO.mapStringZ(Record.Name, "Name")); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ClassRecord &Record) { assert((CVR.kind() == TypeLeafKind::LF_STRUCTURE) || (CVR.kind() == TypeLeafKind::LF_CLASS) || (CVR.kind() == TypeLeafKind::LF_INTERFACE)); std::string PropertiesNames = getFlagNames(IO, static_cast(Record.Options), makeArrayRef(getClassOptionNames())); error(IO.mapInteger(Record.MemberCount, "MemberCount")); error(IO.mapEnum(Record.Options, "Properties" + PropertiesNames)); error(IO.mapInteger(Record.FieldList, "FieldList")); error(IO.mapInteger(Record.DerivationList, "DerivedFrom")); error(IO.mapInteger(Record.VTableShape, "VShape")); error(IO.mapEncodedInteger(Record.Size, "SizeOf")); error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName, Record.hasUniqueName())); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, UnionRecord &Record) { std::string PropertiesNames = getFlagNames(IO, static_cast(Record.Options), makeArrayRef(getClassOptionNames())); error(IO.mapInteger(Record.MemberCount, "MemberCount")); error(IO.mapEnum(Record.Options, "Properties" + PropertiesNames)); error(IO.mapInteger(Record.FieldList, "FieldList")); error(IO.mapEncodedInteger(Record.Size, "SizeOf")); error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName, Record.hasUniqueName())); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, EnumRecord &Record) { std::string PropertiesNames = getFlagNames(IO, static_cast(Record.Options), makeArrayRef(getClassOptionNames())); error(IO.mapInteger(Record.MemberCount, "NumEnumerators")); error(IO.mapEnum(Record.Options, "Properties" + PropertiesNames)); error(IO.mapInteger(Record.UnderlyingType, "UnderlyingType")); error(IO.mapInteger(Record.FieldList, "FieldListType")); error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName, Record.hasUniqueName())); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, BitFieldRecord &Record) { error(IO.mapInteger(Record.Type, "Type")); error(IO.mapInteger(Record.BitSize, "BitSize")); error(IO.mapInteger(Record.BitOffset, "BitOffset")); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, VFTableShapeRecord &Record) { uint16_t Size; if (!IO.isReading()) { ArrayRef Slots = Record.getSlots(); Size = Slots.size(); error(IO.mapInteger(Size, "VFEntryCount")); for (size_t SlotIndex = 0; SlotIndex < Slots.size(); SlotIndex += 2) { uint8_t Byte = static_cast(Slots[SlotIndex]) << 4; if ((SlotIndex + 1) < Slots.size()) { Byte |= static_cast(Slots[SlotIndex + 1]); } error(IO.mapInteger(Byte)); } } else { error(IO.mapInteger(Size)); for (uint16_t I = 0; I < Size; I += 2) { uint8_t Byte; error(IO.mapInteger(Byte)); Record.Slots.push_back(static_cast(Byte & 0xF)); if ((I + 1) < Size) Record.Slots.push_back(static_cast(Byte >> 4)); } } return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, VFTableRecord &Record) { error(IO.mapInteger(Record.CompleteClass, "CompleteClass")); error(IO.mapInteger(Record.OverriddenVFTable, "OverriddenVFTable")); error(IO.mapInteger(Record.VFPtrOffset, "VFPtrOffset")); uint32_t NamesLen = 0; if (!IO.isReading()) { for (auto Name : Record.MethodNames) NamesLen += Name.size() + 1; } error(IO.mapInteger(NamesLen)); error(IO.mapVectorTail( Record.MethodNames, [](CodeViewRecordIO &IO, StringRef &S) { return IO.mapStringZ(S, "MethodName"); }, "VFTableName")); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, StringIdRecord &Record) { error(IO.mapInteger(Record.Id, "Id")); error(IO.mapStringZ(Record.String, "StringData")); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, UdtSourceLineRecord &Record) { error(IO.mapInteger(Record.UDT, "UDT")); error(IO.mapInteger(Record.SourceFile, "SourceFile")); error(IO.mapInteger(Record.LineNumber, "LineNumber")); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, UdtModSourceLineRecord &Record) { error(IO.mapInteger(Record.UDT, "UDT")); error(IO.mapInteger(Record.SourceFile, "SourceFile")); error(IO.mapInteger(Record.LineNumber, "LineNumber")); error(IO.mapInteger(Record.Module, "Module")); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, FuncIdRecord &Record) { error(IO.mapInteger(Record.ParentScope, "ParentScope")); error(IO.mapInteger(Record.FunctionType, "FunctionType")); error(IO.mapStringZ(Record.Name, "Name")); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, MemberFuncIdRecord &Record) { error(IO.mapInteger(Record.ClassType, "ClassType")); error(IO.mapInteger(Record.FunctionType, "FunctionType")); error(IO.mapStringZ(Record.Name, "Name")); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, BuildInfoRecord &Record) { error(IO.mapVectorN( Record.ArgIndices, [](CodeViewRecordIO &IO, TypeIndex &N) { return IO.mapInteger(N, "Argument"); }, "NumArgs")); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, MethodOverloadListRecord &Record) { // TODO: Split the list into multiple records if it's longer than 64KB, using // a subrecord of TypeRecordKind::Index to chain the records together. error(IO.mapVectorTail(Record.Methods, MapOneMethodRecord(true), "Method")); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, FieldListRecord &Record) { if (IO.isStreaming()) { if (auto EC = codeview::visitMemberRecordStream(Record.Data, *this)) return EC; } else error(IO.mapByteVectorTail(Record.Data)); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, TypeServer2Record &Record) { error(IO.mapGuid(Record.Guid, "Guid")); error(IO.mapInteger(Record.Age, "Age")); error(IO.mapStringZ(Record.Name, "Name")); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, LabelRecord &Record) { std::string ModeName = getEnumName(IO, uint16_t(Record.Mode), makeArrayRef(getLabelTypeEnum())); error(IO.mapEnum(Record.Mode, "Mode: " + ModeName)); return Error::success(); } Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, BaseClassRecord &Record) { std::string Attrs = getMemberAttributes( IO, Record.getAccess(), MethodKind::Vanilla, MethodOptions::None); error(IO.mapInteger(Record.Attrs.Attrs, "Attrs: " + Attrs)); error(IO.mapInteger(Record.Type, "BaseType")); error(IO.mapEncodedInteger(Record.Offset, "BaseOffset")); return Error::success(); } Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, EnumeratorRecord &Record) { std::string Attrs = getMemberAttributes( IO, Record.getAccess(), MethodKind::Vanilla, MethodOptions::None); error(IO.mapInteger(Record.Attrs.Attrs, "Attrs: " + Attrs)); // FIXME: Handle full APInt such as __int128. error(IO.mapEncodedInteger(Record.Value, "EnumValue")); error(IO.mapStringZ(Record.Name, "Name")); return Error::success(); } Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, DataMemberRecord &Record) { std::string Attrs = getMemberAttributes( IO, Record.getAccess(), MethodKind::Vanilla, MethodOptions::None); error(IO.mapInteger(Record.Attrs.Attrs, "Attrs: " + Attrs)); error(IO.mapInteger(Record.Type, "Type")); error(IO.mapEncodedInteger(Record.FieldOffset, "FieldOffset")); error(IO.mapStringZ(Record.Name, "Name")); return Error::success(); } Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, OverloadedMethodRecord &Record) { error(IO.mapInteger(Record.NumOverloads, "MethodCount")); error(IO.mapInteger(Record.MethodList, "MethodListIndex")); error(IO.mapStringZ(Record.Name, "Name")); return Error::success(); } Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, OneMethodRecord &Record) { const bool IsFromOverloadList = (TypeKind == LF_METHODLIST); MapOneMethodRecord Mapper(IsFromOverloadList); return Mapper(IO, Record); } Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, NestedTypeRecord &Record) { uint16_t Padding = 0; error(IO.mapInteger(Padding, "Padding")); error(IO.mapInteger(Record.Type, "Type")); error(IO.mapStringZ(Record.Name, "Name")); return Error::success(); } Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, StaticDataMemberRecord &Record) { std::string Attrs = getMemberAttributes( IO, Record.getAccess(), MethodKind::Vanilla, MethodOptions::None); error(IO.mapInteger(Record.Attrs.Attrs, "Attrs: " + Attrs)); error(IO.mapInteger(Record.Type, "Type")); error(IO.mapStringZ(Record.Name, "Name")); return Error::success(); } Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, VirtualBaseClassRecord &Record) { std::string Attrs = getMemberAttributes( IO, Record.getAccess(), MethodKind::Vanilla, MethodOptions::None); error(IO.mapInteger(Record.Attrs.Attrs, "Attrs: " + Attrs)); error(IO.mapInteger(Record.BaseType, "BaseType")); error(IO.mapInteger(Record.VBPtrType, "VBPtrType")); error(IO.mapEncodedInteger(Record.VBPtrOffset, "VBPtrOffset")); error(IO.mapEncodedInteger(Record.VTableIndex, "VBTableIndex")); return Error::success(); } Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, VFPtrRecord &Record) { uint16_t Padding = 0; error(IO.mapInteger(Padding, "Padding")); error(IO.mapInteger(Record.Type, "Type")); return Error::success(); } Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, ListContinuationRecord &Record) { uint16_t Padding = 0; error(IO.mapInteger(Padding, "Padding")); error(IO.mapInteger(Record.ContinuationIndex, "ContinuationIndex")); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, PrecompRecord &Precomp) { error(IO.mapInteger(Precomp.StartTypeIndex, "StartIndex")); error(IO.mapInteger(Precomp.TypesCount, "Count")); error(IO.mapInteger(Precomp.Signature, "Signature")); error(IO.mapStringZ(Precomp.PrecompFilePath, "PrecompFile")); return Error::success(); } Error TypeRecordMapping::visitKnownRecord(CVType &CVR, EndPrecompRecord &EndPrecomp) { error(IO.mapInteger(EndPrecomp.Signature, "Signature")); return Error::success(); }