1//===- TypeIndexDiscovery.cpp -----------------------------------*- C++ -*-===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
9#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
10#include "llvm/DebugInfo/CodeView/TypeRecord.h"
11#include "llvm/ADT/ArrayRef.h"
12#include "llvm/Support/Endian.h"
13
14using namespace llvm;
15using namespace llvm::codeview;
16
17static inline MethodKind getMethodKind(uint16_t Attrs) {
18 Attrs &= uint16_t(MethodOptions::MethodKindMask);
19 Attrs >>= 2;
20 return MethodKind(Attrs);
21}
22
23static inline bool isIntroVirtual(uint16_t Attrs) {
24 MethodKind MK = getMethodKind(Attrs);
25 return MK == MethodKind::IntroducingVirtual ||
26 MK == MethodKind::PureIntroducingVirtual;
27}
28
29static inline PointerMode getPointerMode(uint32_t Attrs) {
30 return static_cast<PointerMode>((Attrs >> PointerRecord::PointerModeShift) &
31 PointerRecord::PointerModeMask);
32}
33
34static inline bool isMemberPointer(uint32_t Attrs) {
35 PointerMode Mode = getPointerMode(Attrs);
36 return Mode == PointerMode::PointerToDataMember ||
37 Mode == PointerMode::PointerToMemberFunction;
38}
39
40static inline uint32_t getEncodedIntegerLength(ArrayRef<uint8_t> Data) {
41 uint16_t N = support::endian::read16le(P: Data.data());
42 if (N < LF_NUMERIC)
43 return 2;
44
45 assert(N <= LF_UQUADWORD);
46
47 constexpr uint32_t Sizes[] = {
48 1, // LF_CHAR
49 2, // LF_SHORT
50 2, // LF_USHORT
51 4, // LF_LONG
52 4, // LF_ULONG
53 4, // LF_REAL32
54 8, // LF_REAL64
55 10, // LF_REAL80
56 16, // LF_REAL128
57 8, // LF_QUADWORD
58 8, // LF_UQUADWORD
59 };
60
61 return 2 + Sizes[N - LF_NUMERIC];
62}
63
64static inline uint32_t getCStringLength(ArrayRef<uint8_t> Data) {
65 const char *S = reinterpret_cast<const char *>(Data.data());
66 return strlen(s: S) + 1;
67}
68
69static void handleMethodOverloadList(ArrayRef<uint8_t> Content,
70 SmallVectorImpl<TiReference> &Refs) {
71 uint32_t Offset = 0;
72
73 while (!Content.empty()) {
74 // Array of:
75 // 0: Attrs
76 // 2: Padding
77 // 4: TypeIndex
78 // if (isIntroVirtual())
79 // 8: VFTableOffset
80
81 // At least 8 bytes are guaranteed. 4 extra bytes come iff function is an
82 // intro virtual.
83 uint32_t Len = 8;
84
85 uint16_t Attrs = support::endian::read16le(P: Content.data());
86 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: Offset + 4, .Count: 1});
87
88 if (LLVM_UNLIKELY(isIntroVirtual(Attrs)))
89 Len += 4;
90 Offset += Len;
91 Content = Content.drop_front(N: Len);
92 }
93}
94
95static uint32_t handleBaseClass(ArrayRef<uint8_t> Data, uint32_t Offset,
96 SmallVectorImpl<TiReference> &Refs) {
97 // 0: Kind
98 // 2: Padding
99 // 4: TypeIndex
100 // 8: Encoded Integer
101 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: Offset + 4, .Count: 1});
102 return 8 + getEncodedIntegerLength(Data: Data.drop_front(N: 8));
103}
104
105static uint32_t handleEnumerator(ArrayRef<uint8_t> Data, uint32_t Offset,
106 SmallVectorImpl<TiReference> &Refs) {
107 // 0: Kind
108 // 2: Padding
109 // 4: Encoded Integer
110 // <next>: Name
111 uint32_t Size = 4 + getEncodedIntegerLength(Data: Data.drop_front(N: 4));
112 return Size + getCStringLength(Data: Data.drop_front(N: Size));
113}
114
115static uint32_t handleDataMember(ArrayRef<uint8_t> Data, uint32_t Offset,
116 SmallVectorImpl<TiReference> &Refs) {
117 // 0: Kind
118 // 2: Padding
119 // 4: TypeIndex
120 // 8: Encoded Integer
121 // <next>: Name
122 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: Offset + 4, .Count: 1});
123 uint32_t Size = 8 + getEncodedIntegerLength(Data: Data.drop_front(N: 8));
124 return Size + getCStringLength(Data: Data.drop_front(N: Size));
125}
126
127static uint32_t handleOverloadedMethod(ArrayRef<uint8_t> Data, uint32_t Offset,
128 SmallVectorImpl<TiReference> &Refs) {
129 // 0: Kind
130 // 2: Padding
131 // 4: TypeIndex
132 // 8: Name
133 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: Offset + 4, .Count: 1});
134 return 8 + getCStringLength(Data: Data.drop_front(N: 8));
135}
136
137static uint32_t handleOneMethod(ArrayRef<uint8_t> Data, uint32_t Offset,
138 SmallVectorImpl<TiReference> &Refs) {
139 // 0: Kind
140 // 2: Attributes
141 // 4: Type
142 // if (isIntroVirtual)
143 // 8: VFTableOffset
144 // <next>: Name
145 uint32_t Size = 8;
146 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: Offset + 4, .Count: 1});
147
148 uint16_t Attrs = support::endian::read16le(P: Data.drop_front(N: 2).data());
149 if (LLVM_UNLIKELY(isIntroVirtual(Attrs)))
150 Size += 4;
151
152 return Size + getCStringLength(Data: Data.drop_front(N: Size));
153}
154
155static uint32_t handleNestedType(ArrayRef<uint8_t> Data, uint32_t Offset,
156 SmallVectorImpl<TiReference> &Refs) {
157 // 0: Kind
158 // 2: Padding
159 // 4: TypeIndex
160 // 8: Name
161 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: Offset + 4, .Count: 1});
162 return 8 + getCStringLength(Data: Data.drop_front(N: 8));
163}
164
165static uint32_t handleStaticDataMember(ArrayRef<uint8_t> Data, uint32_t Offset,
166 SmallVectorImpl<TiReference> &Refs) {
167 // 0: Kind
168 // 2: Padding
169 // 4: TypeIndex
170 // 8: Name
171 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: Offset + 4, .Count: 1});
172 return 8 + getCStringLength(Data: Data.drop_front(N: 8));
173}
174
175static uint32_t handleVirtualBaseClass(ArrayRef<uint8_t> Data, uint32_t Offset,
176 bool IsIndirect,
177 SmallVectorImpl<TiReference> &Refs) {
178 // 0: Kind
179 // 2: Attrs
180 // 4: TypeIndex
181 // 8: TypeIndex
182 // 12: Encoded Integer
183 // <next>: Encoded Integer
184 uint32_t Size = 12;
185 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: Offset + 4, .Count: 2});
186 Size += getEncodedIntegerLength(Data: Data.drop_front(N: Size));
187 Size += getEncodedIntegerLength(Data: Data.drop_front(N: Size));
188 return Size;
189}
190
191static uint32_t handleVFPtr(ArrayRef<uint8_t> Data, uint32_t Offset,
192 SmallVectorImpl<TiReference> &Refs) {
193 // 0: Kind
194 // 2: Padding
195 // 4: TypeIndex
196 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: Offset + 4, .Count: 1});
197 return 8;
198}
199
200static uint32_t handleListContinuation(ArrayRef<uint8_t> Data, uint32_t Offset,
201 SmallVectorImpl<TiReference> &Refs) {
202 // 0: Kind
203 // 2: Padding
204 // 4: TypeIndex
205 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: Offset + 4, .Count: 1});
206 return 8;
207}
208
209static void handleFieldList(ArrayRef<uint8_t> Content,
210 SmallVectorImpl<TiReference> &Refs) {
211 uint32_t Offset = 0;
212 uint32_t ThisLen = 0;
213 while (!Content.empty()) {
214 TypeLeafKind Kind =
215 static_cast<TypeLeafKind>(support::endian::read16le(P: Content.data()));
216 switch (Kind) {
217 case LF_BCLASS:
218 ThisLen = handleBaseClass(Data: Content, Offset, Refs);
219 break;
220 case LF_ENUMERATE:
221 ThisLen = handleEnumerator(Data: Content, Offset, Refs);
222 break;
223 case LF_MEMBER:
224 ThisLen = handleDataMember(Data: Content, Offset, Refs);
225 break;
226 case LF_METHOD:
227 ThisLen = handleOverloadedMethod(Data: Content, Offset, Refs);
228 break;
229 case LF_ONEMETHOD:
230 ThisLen = handleOneMethod(Data: Content, Offset, Refs);
231 break;
232 case LF_NESTTYPE:
233 ThisLen = handleNestedType(Data: Content, Offset, Refs);
234 break;
235 case LF_STMEMBER:
236 ThisLen = handleStaticDataMember(Data: Content, Offset, Refs);
237 break;
238 case LF_VBCLASS:
239 case LF_IVBCLASS:
240 ThisLen =
241 handleVirtualBaseClass(Data: Content, Offset, IsIndirect: Kind == LF_VBCLASS, Refs);
242 break;
243 case LF_VFUNCTAB:
244 ThisLen = handleVFPtr(Data: Content, Offset, Refs);
245 break;
246 case LF_INDEX:
247 ThisLen = handleListContinuation(Data: Content, Offset, Refs);
248 break;
249 default:
250 return;
251 }
252 Content = Content.drop_front(N: ThisLen);
253 Offset += ThisLen;
254 if (!Content.empty()) {
255 uint8_t Pad = Content.front();
256 if (Pad >= LF_PAD0) {
257 uint32_t Skip = Pad & 0x0F;
258 Content = Content.drop_front(N: Skip);
259 Offset += Skip;
260 }
261 }
262 }
263}
264
265static void handlePointer(ArrayRef<uint8_t> Content,
266 SmallVectorImpl<TiReference> &Refs) {
267 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 0, .Count: 1});
268
269 uint32_t Attrs = support::endian::read32le(P: Content.drop_front(N: 4).data());
270 if (isMemberPointer(Attrs))
271 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 8, .Count: 1});
272}
273
274static void discoverTypeIndices(ArrayRef<uint8_t> Content, TypeLeafKind Kind,
275 SmallVectorImpl<TiReference> &Refs) {
276 uint32_t Count;
277 // FIXME: In the future it would be nice if we could avoid hardcoding these
278 // values. One idea is to define some structures representing these types
279 // that would allow the use of offsetof().
280 switch (Kind) {
281 case TypeLeafKind::LF_FUNC_ID:
282 Refs.push_back(Elt: {.Kind: TiRefKind::IndexRef, .Offset: 0, .Count: 1});
283 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 4, .Count: 1});
284 break;
285 case TypeLeafKind::LF_MFUNC_ID:
286 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 0, .Count: 2});
287 break;
288 case TypeLeafKind::LF_STRING_ID:
289 Refs.push_back(Elt: {.Kind: TiRefKind::IndexRef, .Offset: 0, .Count: 1});
290 break;
291 case TypeLeafKind::LF_SUBSTR_LIST:
292 Count = support::endian::read32le(P: Content.data());
293 if (Count > 0)
294 Refs.push_back(Elt: {.Kind: TiRefKind::IndexRef, .Offset: 4, .Count: Count});
295 break;
296 case TypeLeafKind::LF_BUILDINFO:
297 Count = support::endian::read16le(P: Content.data());
298 if (Count > 0)
299 Refs.push_back(Elt: {.Kind: TiRefKind::IndexRef, .Offset: 2, .Count: Count});
300 break;
301 case TypeLeafKind::LF_UDT_SRC_LINE:
302 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 0, .Count: 1});
303 Refs.push_back(Elt: {.Kind: TiRefKind::IndexRef, .Offset: 4, .Count: 1});
304 break;
305 case TypeLeafKind::LF_UDT_MOD_SRC_LINE:
306 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 0, .Count: 1});
307 break;
308 case TypeLeafKind::LF_MODIFIER:
309 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 0, .Count: 1});
310 break;
311 case TypeLeafKind::LF_PROCEDURE:
312 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 0, .Count: 1});
313 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 8, .Count: 1});
314 break;
315 case TypeLeafKind::LF_MFUNCTION:
316 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 0, .Count: 3});
317 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 16, .Count: 1});
318 break;
319 case TypeLeafKind::LF_ARGLIST:
320 Count = support::endian::read32le(P: Content.data());
321 if (Count > 0)
322 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 4, .Count: Count});
323 break;
324 case TypeLeafKind::LF_ARRAY:
325 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 0, .Count: 2});
326 break;
327 case TypeLeafKind::LF_CLASS:
328 case TypeLeafKind::LF_STRUCTURE:
329 case TypeLeafKind::LF_INTERFACE:
330 case TypeLeafKind::LF_CLASS2:
331 case TypeLeafKind::LF_STRUCTURE2:
332 case TypeLeafKind::LF_INTERFACE2:
333 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 4, .Count: 3});
334 break;
335 case TypeLeafKind::LF_UNION:
336 case TypeLeafKind::LF_UNION2:
337 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 4, .Count: 1});
338 break;
339 case TypeLeafKind::LF_ENUM:
340 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 4, .Count: 2});
341 break;
342 case TypeLeafKind::LF_BITFIELD:
343 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 0, .Count: 1});
344 break;
345 case TypeLeafKind::LF_VFTABLE:
346 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 0, .Count: 2});
347 break;
348 case TypeLeafKind::LF_VTSHAPE:
349 break;
350 case TypeLeafKind::LF_METHODLIST:
351 handleMethodOverloadList(Content, Refs);
352 break;
353 case TypeLeafKind::LF_FIELDLIST:
354 handleFieldList(Content, Refs);
355 break;
356 case TypeLeafKind::LF_POINTER:
357 handlePointer(Content, Refs);
358 break;
359 default:
360 break;
361 }
362}
363
364static bool discoverTypeIndices(ArrayRef<uint8_t> Content, SymbolKind Kind,
365 SmallVectorImpl<TiReference> &Refs) {
366 uint32_t Count;
367 // FIXME: In the future it would be nice if we could avoid hardcoding these
368 // values. One idea is to define some structures representing these types
369 // that would allow the use of offsetof().
370 switch (Kind) {
371 case SymbolKind::S_GPROC32_ID:
372 case SymbolKind::S_LPROC32_ID:
373 case SymbolKind::S_LPROC32_DPC:
374 case SymbolKind::S_LPROC32_DPC_ID:
375 Refs.push_back(Elt: {.Kind: TiRefKind::IndexRef, .Offset: 24, .Count: 1}); // LF_FUNC_ID
376 break;
377 case SymbolKind::S_GPROC32:
378 case SymbolKind::S_LPROC32:
379 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 24, .Count: 1}); // Type
380 break;
381 case SymbolKind::S_UDT:
382 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 0, .Count: 1}); // UDT
383 break;
384 case SymbolKind::S_GDATA32:
385 case SymbolKind::S_LDATA32:
386 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 0, .Count: 1}); // Type
387 break;
388 case SymbolKind::S_BUILDINFO:
389 Refs.push_back(Elt: {.Kind: TiRefKind::IndexRef, .Offset: 0, .Count: 1}); // Compile flags
390 break;
391 case SymbolKind::S_LTHREAD32:
392 case SymbolKind::S_GTHREAD32:
393 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 0, .Count: 1}); // Type
394 break;
395 case SymbolKind::S_FILESTATIC:
396 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 0, .Count: 1}); // Type
397 break;
398 case SymbolKind::S_LOCAL:
399 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 0, .Count: 1}); // Type
400 break;
401 case SymbolKind::S_REGISTER:
402 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 0, .Count: 1}); // Type
403 break;
404 case SymbolKind::S_CONSTANT:
405 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 0, .Count: 1}); // Type
406 break;
407 case SymbolKind::S_BPREL32:
408 case SymbolKind::S_REGREL32:
409 case SymbolKind::S_REGREL32_INDIR:
410 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 4, .Count: 1}); // Type
411 break;
412 case SymbolKind::S_CALLSITEINFO:
413 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 8, .Count: 1}); // Call signature
414 break;
415 case SymbolKind::S_CALLERS:
416 case SymbolKind::S_CALLEES:
417 case SymbolKind::S_INLINEES:
418 // The record is a count followed by an array of type indices.
419 Count = *reinterpret_cast<const ulittle32_t *>(Content.data());
420 Refs.push_back(Elt: {.Kind: TiRefKind::IndexRef, .Offset: 4, .Count: Count}); // Callees
421 break;
422 case SymbolKind::S_INLINESITE:
423 Refs.push_back(Elt: {.Kind: TiRefKind::IndexRef, .Offset: 8, .Count: 1}); // ID of inlinee
424 break;
425 case SymbolKind::S_HEAPALLOCSITE:
426 Refs.push_back(Elt: {.Kind: TiRefKind::TypeRef, .Offset: 8, .Count: 1}); // UDT allocated
427 break;
428
429 // Defranges don't have types, just registers and code offsets.
430 case SymbolKind::S_DEFRANGE_REGISTER:
431 case SymbolKind::S_DEFRANGE_REGISTER_REL:
432 case SymbolKind::S_DEFRANGE_REGISTER_REL_INDIR:
433 case SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL:
434 case SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE:
435 case SymbolKind::S_DEFRANGE_SUBFIELD_REGISTER:
436 case SymbolKind::S_DEFRANGE_SUBFIELD:
437 break;
438
439 // No type references.
440 case SymbolKind::S_LABEL32:
441 case SymbolKind::S_OBJNAME:
442 case SymbolKind::S_COMPILE:
443 case SymbolKind::S_COMPILE2:
444 case SymbolKind::S_COMPILE3:
445 case SymbolKind::S_ENVBLOCK:
446 case SymbolKind::S_BLOCK32:
447 case SymbolKind::S_FRAMEPROC:
448 case SymbolKind::S_THUNK32:
449 case SymbolKind::S_FRAMECOOKIE:
450 case SymbolKind::S_UNAMESPACE:
451 case SymbolKind::S_ARMSWITCHTABLE:
452 case SymbolKind::S_ASSOCIATION:
453 break;
454 // Scope ending symbols.
455 case SymbolKind::S_END:
456 case SymbolKind::S_INLINESITE_END:
457 case SymbolKind::S_PROC_ID_END:
458 break;
459 default:
460 return false; // Unknown symbol.
461 }
462 return true;
463}
464
465void llvm::codeview::discoverTypeIndices(const CVType &Type,
466 SmallVectorImpl<TiReference> &Refs) {
467 ::discoverTypeIndices(Content: Type.content(), Kind: Type.kind(), Refs);
468}
469
470static void resolveTypeIndexReferences(ArrayRef<uint8_t> RecordData,
471 ArrayRef<TiReference> Refs,
472 SmallVectorImpl<TypeIndex> &Indices) {
473 Indices.clear();
474
475 if (Refs.empty())
476 return;
477
478 RecordData = RecordData.drop_front(N: sizeof(RecordPrefix));
479
480 BinaryStreamReader Reader(RecordData, llvm::endianness::little);
481 for (const auto &Ref : Refs) {
482 Reader.setOffset(Ref.Offset);
483 FixedStreamArray<TypeIndex> Run;
484 cantFail(Err: Reader.readArray(Array&: Run, NumItems: Ref.Count));
485 Indices.append(in_start: Run.begin(), in_end: Run.end());
486 }
487}
488
489void llvm::codeview::discoverTypeIndices(const CVType &Type,
490 SmallVectorImpl<TypeIndex> &Indices) {
491 return discoverTypeIndices(RecordData: Type.RecordData, Indices);
492}
493
494void llvm::codeview::discoverTypeIndices(ArrayRef<uint8_t> RecordData,
495 SmallVectorImpl<TypeIndex> &Indices) {
496 SmallVector<TiReference, 4> Refs;
497 discoverTypeIndices(RecordData, Refs);
498 resolveTypeIndexReferences(RecordData, Refs, Indices);
499}
500
501void llvm::codeview::discoverTypeIndices(ArrayRef<uint8_t> RecordData,
502 SmallVectorImpl<TiReference> &Refs) {
503 const RecordPrefix *P =
504 reinterpret_cast<const RecordPrefix *>(RecordData.data());
505 TypeLeafKind K = static_cast<TypeLeafKind>(uint16_t(P->RecordKind));
506 ::discoverTypeIndices(Content: RecordData.drop_front(N: sizeof(RecordPrefix)), Kind: K, Refs);
507}
508
509bool llvm::codeview::discoverTypeIndicesInSymbol(
510 const CVSymbol &Sym, SmallVectorImpl<TiReference> &Refs) {
511 SymbolKind K = Sym.kind();
512 return ::discoverTypeIndices(Content: Sym.content(), Kind: K, Refs);
513}
514
515bool llvm::codeview::discoverTypeIndicesInSymbol(
516 ArrayRef<uint8_t> RecordData, SmallVectorImpl<TiReference> &Refs) {
517 const RecordPrefix *P =
518 reinterpret_cast<const RecordPrefix *>(RecordData.data());
519 SymbolKind K = static_cast<SymbolKind>(uint16_t(P->RecordKind));
520 return ::discoverTypeIndices(Content: RecordData.drop_front(N: sizeof(RecordPrefix)), Kind: K,
521 Refs);
522}
523
524bool llvm::codeview::discoverTypeIndicesInSymbol(
525 ArrayRef<uint8_t> RecordData, SmallVectorImpl<TypeIndex> &Indices) {
526 SmallVector<TiReference, 2> Refs;
527 if (!discoverTypeIndicesInSymbol(RecordData, Refs))
528 return false;
529 resolveTypeIndexReferences(RecordData, Refs, Indices);
530 return true;
531}
532