1 //===- TypeRecordMapping.cpp ------------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "llvm/DebugInfo/CodeView/TypeRecordMapping.h"
12 using namespace llvm::codeview;
19 struct MapOneMethodRecord {
20 explicit MapOneMethodRecord(bool IsFromOverloadList)
21 : IsFromOverloadList(IsFromOverloadList) {}
23 Error operator()(CodeViewRecordIO &IO, OneMethodRecord &Method) const {
24 error(IO.mapInteger(Method.Attrs.Attrs, "AccessSpecifier"));
25 if (IsFromOverloadList) {
27 error(IO.mapInteger(Padding, "Padding"));
29 error(IO.mapInteger(Method.Type, "Type"));
30 if (Method.isIntroducingVirtual()) {
31 error(IO.mapInteger(Method.VFTableOffset, "VFTableOffset"));
32 } else if (IO.isReading())
33 Method.VFTableOffset = -1;
35 if (!IsFromOverloadList)
36 error(IO.mapStringZ(Method.Name, "Name"));
38 return Error::success();
42 bool IsFromOverloadList;
46 static Error mapNameAndUniqueName(CodeViewRecordIO &IO, StringRef &Name,
47 StringRef &UniqueName, bool HasUniqueName) {
49 // Try to be smart about what we write here. We can't write anything too
50 // large, so if we're going to go over the limit, truncate both the name
51 // and unique name by the same amount.
52 size_t BytesLeft = IO.maxFieldLength();
54 size_t BytesNeeded = Name.size() + UniqueName.size() + 2;
56 StringRef U = UniqueName;
57 if (BytesNeeded > BytesLeft) {
58 size_t BytesToDrop = (BytesNeeded - BytesLeft);
59 size_t DropN = std::min(N.size(), BytesToDrop / 2);
60 size_t DropU = std::min(U.size(), BytesToDrop - DropN);
62 N = N.drop_back(DropN);
63 U = U.drop_back(DropU);
66 error(IO.mapStringZ(N));
67 error(IO.mapStringZ(U));
69 // Cap the length of the string at however many bytes we have available,
70 // plus one for the required null terminator.
71 auto N = StringRef(Name).take_front(BytesLeft - 1);
72 error(IO.mapStringZ(N));
75 // Reading & Streaming mode come after writing mode is executed for each
76 // record. Truncating large names are done during writing, so its not
77 // necessary to do it while reading or streaming.
78 error(IO.mapStringZ(Name, "Name"));
80 error(IO.mapStringZ(UniqueName, "LinkageName"));
83 return Error::success();
86 Error TypeRecordMapping::visitTypeBegin(CVType &CVR) {
87 assert(!TypeKind.hasValue() && "Already in a type mapping!");
88 assert(!MemberKind.hasValue() && "Already in a member mapping!");
90 // FieldList and MethodList records can be any length because they can be
91 // split with continuation records. All other record types cannot be
92 // longer than the maximum record length.
93 Optional<uint32_t> MaxLen;
94 if (CVR.kind() != TypeLeafKind::LF_FIELDLIST &&
95 CVR.kind() != TypeLeafKind::LF_METHODLIST)
96 MaxLen = MaxRecordLength - sizeof(RecordPrefix);
97 error(IO.beginRecord(MaxLen));
98 TypeKind = CVR.kind();
99 return Error::success();
102 Error TypeRecordMapping::visitTypeBegin(CVType &CVR, TypeIndex Index) {
103 return visitTypeBegin(CVR);
106 Error TypeRecordMapping::visitTypeEnd(CVType &Record) {
107 assert(TypeKind.hasValue() && "Not in a type mapping!");
108 assert(!MemberKind.hasValue() && "Still in a member mapping!");
110 error(IO.endRecord());
113 return Error::success();
116 Error TypeRecordMapping::visitMemberBegin(CVMemberRecord &Record) {
117 assert(TypeKind.hasValue() && "Not in a type mapping!");
118 assert(!MemberKind.hasValue() && "Already in a member mapping!");
120 // The largest possible subrecord is one in which there is a record prefix,
121 // followed by the subrecord, followed by a continuation, and that entire
122 // sequence spaws `MaxRecordLength` bytes. So the record's length is
123 // calculated as follows.
124 constexpr uint32_t ContinuationLength = 8;
125 error(IO.beginRecord(MaxRecordLength - sizeof(RecordPrefix) -
126 ContinuationLength));
128 MemberKind = Record.Kind;
129 return Error::success();
132 Error TypeRecordMapping::visitMemberEnd(CVMemberRecord &Record) {
133 assert(TypeKind.hasValue() && "Not in a type mapping!");
134 assert(MemberKind.hasValue() && "Not in a member mapping!");
136 if (IO.isReading()) {
137 if (auto EC = IO.skipPadding())
142 error(IO.endRecord());
143 return Error::success();
146 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ModifierRecord &Record) {
147 error(IO.mapInteger(Record.ModifiedType, "ModifiedType"));
148 error(IO.mapEnum(Record.Modifiers, "Modifiers"));
149 return Error::success();
152 Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
153 ProcedureRecord &Record) {
154 error(IO.mapInteger(Record.ReturnType, "ReturnType"));
155 error(IO.mapEnum(Record.CallConv, "CallingConvention"));
156 error(IO.mapEnum(Record.Options, "FunctionOptions"));
157 error(IO.mapInteger(Record.ParameterCount, "NumParameters"));
158 error(IO.mapInteger(Record.ArgumentList, "ArgListType"));
160 return Error::success();
163 Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
164 MemberFunctionRecord &Record) {
165 error(IO.mapInteger(Record.ReturnType, "ReturnType"));
166 error(IO.mapInteger(Record.ClassType, "ClassType"));
167 error(IO.mapInteger(Record.ThisType, "ThisType"));
168 error(IO.mapEnum(Record.CallConv, "CallingConvention"));
169 error(IO.mapEnum(Record.Options, "FunctionOptions"));
170 error(IO.mapInteger(Record.ParameterCount, "NumParameters"));
171 error(IO.mapInteger(Record.ArgumentList, "ArgListType"));
172 error(IO.mapInteger(Record.ThisPointerAdjustment, "ThisAdjustment"));
174 return Error::success();
177 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ArgListRecord &Record) {
178 error(IO.mapVectorN<uint32_t>(
180 [](CodeViewRecordIO &IO, TypeIndex &N) {
181 return IO.mapInteger(N, "Argument");
184 return Error::success();
187 Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
188 StringListRecord &Record) {
189 error(IO.mapVectorN<uint32_t>(
190 Record.StringIndices,
191 [](CodeViewRecordIO &IO, TypeIndex &N) {
192 return IO.mapInteger(N, "Strings");
196 return Error::success();
199 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, PointerRecord &Record) {
200 error(IO.mapInteger(Record.ReferentType, "PointeeType"));
201 error(IO.mapInteger(Record.Attrs, "Attributes"));
203 if (Record.isPointerToMember()) {
205 Record.MemberInfo.emplace();
207 MemberPointerInfo &M = *Record.MemberInfo;
208 error(IO.mapInteger(M.ContainingType, "ClassType"));
209 error(IO.mapEnum(M.Representation, "Representation"));
212 return Error::success();
215 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ArrayRecord &Record) {
216 error(IO.mapInteger(Record.ElementType, "ElementType"));
217 error(IO.mapInteger(Record.IndexType, "IndexType"));
218 error(IO.mapEncodedInteger(Record.Size, "SizeOf"));
219 error(IO.mapStringZ(Record.Name, "Name"));
221 return Error::success();
224 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ClassRecord &Record) {
225 assert((CVR.kind() == TypeLeafKind::LF_STRUCTURE) ||
226 (CVR.kind() == TypeLeafKind::LF_CLASS) ||
227 (CVR.kind() == TypeLeafKind::LF_INTERFACE));
229 error(IO.mapInteger(Record.MemberCount, "MemberCount"));
230 error(IO.mapEnum(Record.Options, "Properties"));
231 error(IO.mapInteger(Record.FieldList, "FieldList"));
232 error(IO.mapInteger(Record.DerivationList, "DerivedFrom"));
233 error(IO.mapInteger(Record.VTableShape, "VShape"));
234 error(IO.mapEncodedInteger(Record.Size, "SizeOf"));
235 error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName,
236 Record.hasUniqueName()));
238 return Error::success();
241 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, UnionRecord &Record) {
242 error(IO.mapInteger(Record.MemberCount, "MemberCount"));
243 error(IO.mapEnum(Record.Options, "Properties"));
244 error(IO.mapInteger(Record.FieldList, "FieldList"));
245 error(IO.mapEncodedInteger(Record.Size, "SizeOf"));
246 error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName,
247 Record.hasUniqueName()));
249 return Error::success();
252 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, EnumRecord &Record) {
253 error(IO.mapInteger(Record.MemberCount, "NumEnumerators"));
254 error(IO.mapEnum(Record.Options, "Properties"));
255 error(IO.mapInteger(Record.UnderlyingType, "UnderlyingType"));
256 error(IO.mapInteger(Record.FieldList, "FieldListType"));
257 error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName,
258 Record.hasUniqueName()));
260 return Error::success();
263 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, BitFieldRecord &Record) {
264 error(IO.mapInteger(Record.Type, "Type"));
265 error(IO.mapInteger(Record.BitSize, "BitSize"));
266 error(IO.mapInteger(Record.BitOffset, "BitOffset"));
268 return Error::success();
271 Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
272 VFTableShapeRecord &Record) {
274 if (!IO.isReading()) {
275 ArrayRef<VFTableSlotKind> Slots = Record.getSlots();
277 error(IO.mapInteger(Size, "VFEntryCount"));
279 for (size_t SlotIndex = 0; SlotIndex < Slots.size(); SlotIndex += 2) {
280 uint8_t Byte = static_cast<uint8_t>(Slots[SlotIndex]) << 4;
281 if ((SlotIndex + 1) < Slots.size()) {
282 Byte |= static_cast<uint8_t>(Slots[SlotIndex + 1]);
284 error(IO.mapInteger(Byte));
287 error(IO.mapInteger(Size));
288 for (uint16_t I = 0; I < Size; I += 2) {
290 error(IO.mapInteger(Byte));
291 Record.Slots.push_back(static_cast<VFTableSlotKind>(Byte & 0xF));
293 Record.Slots.push_back(static_cast<VFTableSlotKind>(Byte >> 4));
297 return Error::success();
300 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, VFTableRecord &Record) {
301 error(IO.mapInteger(Record.CompleteClass, "CompleteClass"));
302 error(IO.mapInteger(Record.OverriddenVFTable, "OverriddenVFTable"));
303 error(IO.mapInteger(Record.VFPtrOffset, "VFPtrOffset"));
304 uint32_t NamesLen = 0;
305 if (!IO.isReading()) {
306 for (auto Name : Record.MethodNames)
307 NamesLen += Name.size() + 1;
309 error(IO.mapInteger(NamesLen));
310 error(IO.mapVectorTail(
312 [](CodeViewRecordIO &IO, StringRef &S) {
313 return IO.mapStringZ(S, "MethodName");
317 return Error::success();
320 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, StringIdRecord &Record) {
321 error(IO.mapInteger(Record.Id, "Id"));
322 error(IO.mapStringZ(Record.String, "StringData"));
324 return Error::success();
327 Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
328 UdtSourceLineRecord &Record) {
329 error(IO.mapInteger(Record.UDT, "UDT"));
330 error(IO.mapInteger(Record.SourceFile, "SourceFile"));
331 error(IO.mapInteger(Record.LineNumber, "LineNumber"));
333 return Error::success();
336 Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
337 UdtModSourceLineRecord &Record) {
338 error(IO.mapInteger(Record.UDT, "UDT"));
339 error(IO.mapInteger(Record.SourceFile, "SourceFile"));
340 error(IO.mapInteger(Record.LineNumber, "LineNumber"));
341 error(IO.mapInteger(Record.Module, "Module"));
343 return Error::success();
346 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, FuncIdRecord &Record) {
347 error(IO.mapInteger(Record.ParentScope, "ParentScope"));
348 error(IO.mapInteger(Record.FunctionType, "FunctionType"));
349 error(IO.mapStringZ(Record.Name, "Name"));
351 return Error::success();
354 Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
355 MemberFuncIdRecord &Record) {
356 error(IO.mapInteger(Record.ClassType, "ClassType"));
357 error(IO.mapInteger(Record.FunctionType, "FunctionType"));
358 error(IO.mapStringZ(Record.Name, "Name"));
360 return Error::success();
363 Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
364 BuildInfoRecord &Record) {
365 error(IO.mapVectorN<uint16_t>(
367 [](CodeViewRecordIO &IO, TypeIndex &N) {
368 return IO.mapInteger(N, "Argument");
372 return Error::success();
375 Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
376 MethodOverloadListRecord &Record) {
377 // TODO: Split the list into multiple records if it's longer than 64KB, using
378 // a subrecord of TypeRecordKind::Index to chain the records together.
379 error(IO.mapVectorTail(Record.Methods, MapOneMethodRecord(true), "Method"));
381 return Error::success();
384 Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
385 FieldListRecord &Record) {
386 error(IO.mapByteVectorTail(Record.Data));
388 return Error::success();
391 Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
392 TypeServer2Record &Record) {
393 error(IO.mapGuid(Record.Guid, "Guid"));
394 error(IO.mapInteger(Record.Age, "Age"));
395 error(IO.mapStringZ(Record.Name, "Name"));
396 return Error::success();
399 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, LabelRecord &Record) {
400 error(IO.mapEnum(Record.Mode, "Mode"));
401 return Error::success();
404 Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
405 BaseClassRecord &Record) {
406 error(IO.mapInteger(Record.Attrs.Attrs, "AccessSpecifier"));
407 error(IO.mapInteger(Record.Type, "BaseType"));
408 error(IO.mapEncodedInteger(Record.Offset, "BaseOffset"));
410 return Error::success();
413 Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
414 EnumeratorRecord &Record) {
415 error(IO.mapInteger(Record.Attrs.Attrs));
417 // FIXME: Handle full APInt such as __int128.
418 error(IO.mapEncodedInteger(Record.Value, "EnumValue"));
419 error(IO.mapStringZ(Record.Name, "Name"));
421 return Error::success();
424 Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
425 DataMemberRecord &Record) {
426 error(IO.mapInteger(Record.Attrs.Attrs, "AccessSpecifier"));
427 error(IO.mapInteger(Record.Type, "Type"));
428 error(IO.mapEncodedInteger(Record.FieldOffset, "FieldOffset"));
429 error(IO.mapStringZ(Record.Name, "Name"));
431 return Error::success();
434 Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
435 OverloadedMethodRecord &Record) {
436 error(IO.mapInteger(Record.NumOverloads, "MethodCount"));
437 error(IO.mapInteger(Record.MethodList, "MethodListIndex"));
438 error(IO.mapStringZ(Record.Name, "Name"));
440 return Error::success();
443 Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
444 OneMethodRecord &Record) {
445 const bool IsFromOverloadList = (TypeKind == LF_METHODLIST);
446 MapOneMethodRecord Mapper(IsFromOverloadList);
447 return Mapper(IO, Record);
450 Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
451 NestedTypeRecord &Record) {
452 uint16_t Padding = 0;
453 error(IO.mapInteger(Padding, "Padding"));
454 error(IO.mapInteger(Record.Type, "Type"));
455 error(IO.mapStringZ(Record.Name, "Name"));
457 return Error::success();
460 Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
461 StaticDataMemberRecord &Record) {
463 error(IO.mapInteger(Record.Attrs.Attrs, "AccessSpecifier"));
464 error(IO.mapInteger(Record.Type, "Type"));
465 error(IO.mapStringZ(Record.Name, "Name"));
467 return Error::success();
470 Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
471 VirtualBaseClassRecord &Record) {
473 error(IO.mapInteger(Record.Attrs.Attrs, "AccessSpecifier"));
474 error(IO.mapInteger(Record.BaseType, "BaseType"));
475 error(IO.mapInteger(Record.VBPtrType, "VBPtrType"));
476 error(IO.mapEncodedInteger(Record.VBPtrOffset, "VBPtrOffset"));
477 error(IO.mapEncodedInteger(Record.VTableIndex, "VBTableIndex"));
479 return Error::success();
482 Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
483 VFPtrRecord &Record) {
484 uint16_t Padding = 0;
485 error(IO.mapInteger(Padding, "Padding"));
486 error(IO.mapInteger(Record.Type, "Type"));
488 return Error::success();
491 Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
492 ListContinuationRecord &Record) {
493 uint16_t Padding = 0;
494 error(IO.mapInteger(Padding, "Padding"));
495 error(IO.mapInteger(Record.ContinuationIndex, "ContinuationIndex"));
497 return Error::success();
500 Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
501 PrecompRecord &Precomp) {
502 error(IO.mapInteger(Precomp.StartTypeIndex, "StartIndex"));
503 error(IO.mapInteger(Precomp.TypesCount, "Count"));
504 error(IO.mapInteger(Precomp.Signature, "Signature"));
505 error(IO.mapStringZ(Precomp.PrecompFilePath, "PrecompFile"));
506 return Error::success();
509 Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
510 EndPrecompRecord &EndPrecomp) {
511 error(IO.mapInteger(EndPrecomp.Signature, "Signature"));
512 return Error::success();