1 | #include "UdtRecordCompleter.h" |
2 | |
3 | #include "PdbAstBuilder.h" |
4 | #include "PdbIndex.h" |
5 | #include "PdbSymUid.h" |
6 | #include "PdbUtil.h" |
7 | |
8 | #include "Plugins/ExpressionParser/Clang/ClangASTImporter.h" |
9 | #include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h" |
10 | #include "Plugins/ExpressionParser/Clang/ClangUtil.h" |
11 | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
12 | #include "SymbolFileNativePDB.h" |
13 | #include "lldb/Core/Address.h" |
14 | #include "lldb/Symbol/Type.h" |
15 | #include "lldb/Utility/LLDBAssert.h" |
16 | #include "lldb/Utility/LLDBLog.h" |
17 | #include "lldb/lldb-enumerations.h" |
18 | #include "lldb/lldb-forward.h" |
19 | |
20 | #include "llvm/ADT/STLExtras.h" |
21 | #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" |
22 | #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" |
23 | #include "llvm/DebugInfo/CodeView/TypeIndex.h" |
24 | #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" |
25 | #include "llvm/DebugInfo/PDB/Native/TpiStream.h" |
26 | #include "llvm/DebugInfo/PDB/PDBTypes.h" |
27 | #include <optional> |
28 | |
29 | using namespace llvm::codeview; |
30 | using namespace llvm::pdb; |
31 | using namespace lldb; |
32 | using namespace lldb_private; |
33 | using namespace lldb_private::npdb; |
34 | |
35 | using Error = llvm::Error; |
36 | |
37 | UdtRecordCompleter::UdtRecordCompleter( |
38 | PdbTypeSymId id, CompilerType &derived_ct, clang::TagDecl &tag_decl, |
39 | PdbAstBuilder &ast_builder, PdbIndex &index, |
40 | llvm::DenseMap<clang::Decl *, DeclStatus> &decl_to_status, |
41 | llvm::DenseMap<lldb::opaque_compiler_type_t, |
42 | llvm::SmallSet<std::pair<llvm::StringRef, CompilerType>, 8>> |
43 | &cxx_record_map) |
44 | : m_id(id), m_derived_ct(derived_ct), m_tag_decl(tag_decl), |
45 | m_ast_builder(ast_builder), m_index(index), |
46 | m_decl_to_status(decl_to_status), m_cxx_record_map(cxx_record_map) { |
47 | CVType cvt = m_index.tpi().getType(Index: m_id.index); |
48 | switch (cvt.kind()) { |
49 | case LF_ENUM: |
50 | llvm::cantFail(Err: TypeDeserializer::deserializeAs<EnumRecord>(CVT&: cvt, Record&: m_cvr.er)); |
51 | break; |
52 | case LF_UNION: |
53 | llvm::cantFail(Err: TypeDeserializer::deserializeAs<UnionRecord>(CVT&: cvt, Record&: m_cvr.ur)); |
54 | m_layout.bit_size = m_cvr.ur.getSize() * 8; |
55 | m_record.record.kind = Member::Union; |
56 | break; |
57 | case LF_CLASS: |
58 | case LF_STRUCTURE: |
59 | llvm::cantFail(Err: TypeDeserializer::deserializeAs<ClassRecord>(CVT&: cvt, Record&: m_cvr.cr)); |
60 | m_layout.bit_size = m_cvr.cr.getSize() * 8; |
61 | m_record.record.kind = Member::Struct; |
62 | break; |
63 | default: |
64 | llvm_unreachable("unreachable!" ); |
65 | } |
66 | } |
67 | |
68 | clang::QualType UdtRecordCompleter::AddBaseClassForTypeIndex( |
69 | llvm::codeview::TypeIndex ti, llvm::codeview::MemberAccess access, |
70 | std::optional<uint64_t> vtable_idx) { |
71 | PdbTypeSymId type_id(ti); |
72 | clang::QualType qt = m_ast_builder.GetOrCreateType(type: type_id); |
73 | |
74 | CVType udt_cvt = m_index.tpi().getType(Index: ti); |
75 | |
76 | std::unique_ptr<clang::CXXBaseSpecifier> base_spec = |
77 | m_ast_builder.clang().CreateBaseClassSpecifier( |
78 | type: qt.getAsOpaquePtr(), access: TranslateMemberAccess(access), |
79 | is_virtual: vtable_idx.has_value(), base_of_class: udt_cvt.kind() == LF_CLASS); |
80 | if (!base_spec) |
81 | return {}; |
82 | |
83 | m_bases.push_back( |
84 | x: std::make_pair(x: vtable_idx.value_or(u: 0), y: std::move(base_spec))); |
85 | |
86 | return qt; |
87 | } |
88 | |
89 | void UdtRecordCompleter::AddMethod(llvm::StringRef name, TypeIndex type_idx, |
90 | MemberAccess access, MethodOptions options, |
91 | MemberAttributes attrs) { |
92 | clang::QualType method_qt = |
93 | m_ast_builder.GetOrCreateType(type: PdbTypeSymId(type_idx)); |
94 | if (method_qt.isNull()) |
95 | return; |
96 | CompilerType method_ct = m_ast_builder.ToCompilerType(qt: method_qt); |
97 | TypeSystemClang::RequireCompleteType(type: method_ct); |
98 | lldb::opaque_compiler_type_t derived_opaque_ty = |
99 | m_derived_ct.GetOpaqueQualType(); |
100 | auto iter = m_cxx_record_map.find(Val: derived_opaque_ty); |
101 | if (iter != m_cxx_record_map.end()) { |
102 | if (iter->getSecond().contains(V: {name, method_ct})) { |
103 | return; |
104 | } |
105 | } |
106 | |
107 | lldb::AccessType access_type = TranslateMemberAccess(access); |
108 | bool is_artificial = (options & MethodOptions::CompilerGenerated) == |
109 | MethodOptions::CompilerGenerated; |
110 | m_ast_builder.clang().AddMethodToCXXRecordType( |
111 | type: derived_opaque_ty, name: name.data(), mangled_name: nullptr, method_type: method_ct, |
112 | access: access_type, is_virtual: attrs.isVirtual(), is_static: attrs.isStatic(), is_inline: false, is_explicit: false, is_attr_used: false, |
113 | is_artificial); |
114 | |
115 | m_cxx_record_map[derived_opaque_ty].insert(V: {name, method_ct}); |
116 | } |
117 | |
118 | Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, |
119 | BaseClassRecord &base) { |
120 | clang::QualType base_qt = |
121 | AddBaseClassForTypeIndex(ti: base.Type, access: base.getAccess()); |
122 | |
123 | if (base_qt.isNull()) |
124 | return llvm::Error::success(); |
125 | auto decl = |
126 | m_ast_builder.clang().GetAsCXXRecordDecl(type: base_qt.getAsOpaquePtr()); |
127 | lldbassert(decl); |
128 | |
129 | auto offset = clang::CharUnits::fromQuantity(Quantity: base.getBaseOffset()); |
130 | m_layout.base_offsets.insert(KV: std::make_pair(x&: decl, y&: offset)); |
131 | |
132 | return llvm::Error::success(); |
133 | } |
134 | |
135 | Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, |
136 | VirtualBaseClassRecord &base) { |
137 | AddBaseClassForTypeIndex(ti: base.BaseType, access: base.getAccess(), vtable_idx: base.VTableIndex); |
138 | |
139 | return Error::success(); |
140 | } |
141 | |
142 | Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, |
143 | ListContinuationRecord &cont) { |
144 | return Error::success(); |
145 | } |
146 | |
147 | Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, |
148 | VFPtrRecord &vfptr) { |
149 | return Error::success(); |
150 | } |
151 | |
152 | Error UdtRecordCompleter::visitKnownMember( |
153 | CVMemberRecord &cvr, StaticDataMemberRecord &static_data_member) { |
154 | clang::QualType member_type = |
155 | m_ast_builder.GetOrCreateType(type: PdbTypeSymId(static_data_member.Type)); |
156 | if (member_type.isNull()) |
157 | return llvm::Error::success(); |
158 | |
159 | CompilerType member_ct = m_ast_builder.ToCompilerType(qt: member_type); |
160 | |
161 | lldb::AccessType access = |
162 | TranslateMemberAccess(access: static_data_member.getAccess()); |
163 | auto decl = TypeSystemClang::AddVariableToRecordType( |
164 | type: m_derived_ct, name: static_data_member.Name, var_type: member_ct, access); |
165 | |
166 | // Static constant members may be a const[expr] declaration. |
167 | // Query the symbol's value as the variable initializer if valid. |
168 | if (member_ct.IsConst() && member_ct.IsCompleteType()) { |
169 | std::string qual_name = decl->getQualifiedNameAsString(); |
170 | |
171 | auto results = |
172 | m_index.globals().findRecordsByName(Name: qual_name, Symbols: m_index.symrecords()); |
173 | |
174 | for (const auto &result : results) { |
175 | if (result.second.kind() == SymbolKind::S_CONSTANT) { |
176 | ConstantSym constant(SymbolRecordKind::ConstantSym); |
177 | cantFail(SymbolDeserializer::deserializeAs<ConstantSym>(result.second, |
178 | constant)); |
179 | |
180 | clang::QualType qual_type = decl->getType(); |
181 | unsigned type_width = decl->getASTContext().getIntWidth(qual_type); |
182 | unsigned constant_width = constant.Value.getBitWidth(); |
183 | |
184 | if (qual_type->isIntegralOrEnumerationType()) { |
185 | if (type_width >= constant_width) { |
186 | TypeSystemClang::SetIntegerInitializerForVariable( |
187 | decl, constant.Value.extOrTrunc(type_width)); |
188 | } else { |
189 | LLDB_LOG(GetLog(LLDBLog::AST), |
190 | "Class '{0}' has a member '{1}' of type '{2}' ({3} bits) " |
191 | "which resolves to a wider constant value ({4} bits). " |
192 | "Ignoring constant." , |
193 | m_derived_ct.GetTypeName(), static_data_member.Name, |
194 | member_ct.GetTypeName(), type_width, constant_width); |
195 | } |
196 | } else { |
197 | lldb::BasicType basic_type_enum = member_ct.GetBasicTypeEnumeration(); |
198 | switch (basic_type_enum) { |
199 | case lldb::eBasicTypeFloat: |
200 | case lldb::eBasicTypeDouble: |
201 | case lldb::eBasicTypeLongDouble: |
202 | if (type_width == constant_width) { |
203 | TypeSystemClang::SetFloatingInitializerForVariable( |
204 | decl, basic_type_enum == lldb::eBasicTypeFloat |
205 | ? llvm::APFloat(constant.Value.bitsToFloat()) |
206 | : llvm::APFloat(constant.Value.bitsToDouble())); |
207 | decl->setConstexpr(true); |
208 | } else { |
209 | LLDB_LOG( |
210 | GetLog(LLDBLog::AST), |
211 | "Class '{0}' has a member '{1}' of type '{2}' ({3} bits) " |
212 | "which resolves to a constant value of mismatched width " |
213 | "({4} bits). Ignoring constant." , |
214 | m_derived_ct.GetTypeName(), static_data_member.Name, |
215 | member_ct.GetTypeName(), type_width, constant_width); |
216 | } |
217 | break; |
218 | default: |
219 | break; |
220 | } |
221 | } |
222 | break; |
223 | } |
224 | } |
225 | } |
226 | |
227 | // FIXME: Add a PdbSymUid namespace for field list members and update |
228 | // the m_uid_to_decl map with this decl. |
229 | return Error::success(); |
230 | } |
231 | |
232 | Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, |
233 | NestedTypeRecord &nested) { |
234 | return Error::success(); |
235 | } |
236 | |
237 | Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, |
238 | DataMemberRecord &data_member) { |
239 | |
240 | uint64_t offset = data_member.FieldOffset * 8; |
241 | uint32_t bitfield_width = 0; |
242 | |
243 | TypeIndex ti(data_member.Type); |
244 | if (!ti.isSimple()) { |
245 | CVType cvt = m_index.tpi().getType(Index: ti); |
246 | if (cvt.kind() == LF_BITFIELD) { |
247 | BitFieldRecord bfr; |
248 | llvm::cantFail(Err: TypeDeserializer::deserializeAs<BitFieldRecord>(CVT&: cvt, Record&: bfr)); |
249 | offset += bfr.BitOffset; |
250 | bitfield_width = bfr.BitSize; |
251 | ti = bfr.Type; |
252 | } |
253 | } |
254 | |
255 | clang::QualType member_qt = m_ast_builder.GetOrCreateType(type: PdbTypeSymId(ti)); |
256 | if (member_qt.isNull()) |
257 | return Error::success(); |
258 | TypeSystemClang::RequireCompleteType(type: m_ast_builder.ToCompilerType(qt: member_qt)); |
259 | lldb::AccessType access = TranslateMemberAccess(access: data_member.getAccess()); |
260 | size_t field_size = |
261 | bitfield_width ? bitfield_width : GetSizeOfType(id: ti, tpi&: m_index.tpi()) * 8; |
262 | if (field_size == 0) |
263 | return Error::success(); |
264 | m_record.CollectMember(data_member.Name, offset, field_size, member_qt, access, |
265 | bitfield_width); |
266 | return Error::success(); |
267 | } |
268 | |
269 | Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, |
270 | OneMethodRecord &one_method) { |
271 | AddMethod(name: one_method.Name, type_idx: one_method.Type, access: one_method.getAccess(), |
272 | options: one_method.getOptions(), attrs: one_method.Attrs); |
273 | |
274 | return Error::success(); |
275 | } |
276 | |
277 | Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, |
278 | OverloadedMethodRecord &overloaded) { |
279 | TypeIndex method_list_idx = overloaded.MethodList; |
280 | |
281 | CVType method_list_type = m_index.tpi().getType(Index: method_list_idx); |
282 | assert(method_list_type.kind() == LF_METHODLIST); |
283 | |
284 | MethodOverloadListRecord method_list; |
285 | llvm::cantFail(Err: TypeDeserializer::deserializeAs<MethodOverloadListRecord>( |
286 | CVT&: method_list_type, Record&: method_list)); |
287 | |
288 | for (const OneMethodRecord &method : method_list.Methods) |
289 | AddMethod(name: overloaded.Name, type_idx: method.Type, access: method.getAccess(), |
290 | options: method.getOptions(), attrs: method.Attrs); |
291 | |
292 | return Error::success(); |
293 | } |
294 | |
295 | Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, |
296 | EnumeratorRecord &enumerator) { |
297 | Declaration decl; |
298 | llvm::StringRef name = DropNameScope(name: enumerator.getName()); |
299 | |
300 | m_ast_builder.clang().AddEnumerationValueToEnumerationType( |
301 | enum_type: m_derived_ct, decl, name: name.str().c_str(), value: enumerator.Value); |
302 | return Error::success(); |
303 | } |
304 | |
305 | void UdtRecordCompleter::complete() { |
306 | // Ensure the correct order for virtual bases. |
307 | llvm::stable_sort(Range&: m_bases, C: llvm::less_first()); |
308 | |
309 | std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> bases; |
310 | bases.reserve(n: m_bases.size()); |
311 | for (auto &ib : m_bases) |
312 | bases.push_back(x: std::move(ib.second)); |
313 | |
314 | TypeSystemClang &clang = m_ast_builder.clang(); |
315 | // Make sure all base classes refer to complete types and not forward |
316 | // declarations. If we don't do this, clang will crash with an |
317 | // assertion in the call to clang_type.TransferBaseClasses() |
318 | for (const auto &base_class : bases) { |
319 | clang::TypeSourceInfo *type_source_info = |
320 | base_class->getTypeSourceInfo(); |
321 | if (type_source_info) { |
322 | TypeSystemClang::RequireCompleteType( |
323 | type: clang.GetType(qt: type_source_info->getType())); |
324 | } |
325 | } |
326 | |
327 | clang.TransferBaseClasses(type: m_derived_ct.GetOpaqueQualType(), bases: std::move(bases)); |
328 | |
329 | clang.AddMethodOverridesForCXXRecordType(type: m_derived_ct.GetOpaqueQualType()); |
330 | FinishRecord(); |
331 | TypeSystemClang::BuildIndirectFields(type: m_derived_ct); |
332 | TypeSystemClang::CompleteTagDeclarationDefinition(type: m_derived_ct); |
333 | |
334 | if (auto *record_decl = llvm::dyn_cast<clang::CXXRecordDecl>(Val: &m_tag_decl)) { |
335 | m_ast_builder.GetClangASTImporter().SetRecordLayout(record_decl, m_layout); |
336 | } |
337 | } |
338 | |
339 | uint64_t |
340 | UdtRecordCompleter::AddMember(TypeSystemClang &clang, Member *field, |
341 | uint64_t bit_offset, CompilerType parent_ct, |
342 | ClangASTImporter::LayoutInfo &parent_layout, |
343 | clang::DeclContext *parent_decl_ctx) { |
344 | SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( |
345 | clang.GetSymbolFile()->GetBackingSymbolFile()); |
346 | clang::FieldDecl *field_decl = nullptr; |
347 | uint64_t bit_size = 0; |
348 | switch (field->kind) { |
349 | case Member::Field: { |
350 | field_decl = TypeSystemClang::AddFieldToRecordType( |
351 | type: parent_ct, name: field->name, field_type: m_ast_builder.ToCompilerType(qt: field->qt), |
352 | access: field->access, bitfield_bit_size: field->bitfield_width); |
353 | bit_size = field->bit_size; |
354 | break; |
355 | }; |
356 | case Member::Struct: |
357 | case Member::Union: { |
358 | clang::TagTypeKind kind = field->kind == Member::Struct |
359 | ? clang::TagTypeKind::Struct |
360 | : clang::TagTypeKind::Union; |
361 | ClangASTMetadata metadata; |
362 | metadata.SetUserID(pdb->anonymous_id); |
363 | metadata.SetIsDynamicCXXType(false); |
364 | CompilerType record_ct = clang.CreateRecordType( |
365 | decl_ctx: parent_decl_ctx, owning_module: OptionalClangModuleID(), access_type: lldb::eAccessPublic, name: "" , |
366 | kind: llvm::to_underlying(E: kind), language: lldb::eLanguageTypeC_plus_plus, metadata: &metadata); |
367 | TypeSystemClang::StartTagDeclarationDefinition(type: record_ct); |
368 | ClangASTImporter::LayoutInfo layout; |
369 | clang::DeclContext *decl_ctx = clang.GetDeclContextForType(type: record_ct); |
370 | for (const auto &member : field->fields) { |
371 | uint64_t member_offset = field->kind == Member::Struct |
372 | ? member->bit_offset - field->base_offset |
373 | : 0; |
374 | uint64_t member_bit_size = AddMember(clang, field: member.get(), bit_offset: member_offset, |
375 | parent_ct: record_ct, parent_layout&: layout, parent_decl_ctx: decl_ctx); |
376 | if (field->kind == Member::Struct) |
377 | bit_size = std::max(a: bit_size, b: member_offset + member_bit_size); |
378 | else |
379 | bit_size = std::max(a: bit_size, b: member_bit_size); |
380 | } |
381 | layout.bit_size = bit_size; |
382 | TypeSystemClang::CompleteTagDeclarationDefinition(type: record_ct); |
383 | clang::RecordDecl *record_decl = clang.GetAsRecordDecl(type: record_ct); |
384 | m_ast_builder.GetClangASTImporter().SetRecordLayout(decl: record_decl, layout); |
385 | field_decl = TypeSystemClang::AddFieldToRecordType( |
386 | type: parent_ct, name: "" , field_type: record_ct, access: lldb::eAccessPublic, bitfield_bit_size: 0); |
387 | // Mark this record decl as completed. |
388 | DeclStatus status; |
389 | status.resolved = true; |
390 | status.uid = pdb->anonymous_id--; |
391 | m_decl_to_status.insert({record_decl, status}); |
392 | break; |
393 | }; |
394 | } |
395 | // FIXME: Add a PdbSymUid namespace for field list members and update |
396 | // the m_uid_to_decl map with this decl. |
397 | parent_layout.field_offsets.insert(KV: {field_decl, bit_offset}); |
398 | return bit_size; |
399 | } |
400 | |
401 | void UdtRecordCompleter::FinishRecord() { |
402 | TypeSystemClang &clang = m_ast_builder.clang(); |
403 | clang::DeclContext *decl_ctx = |
404 | m_ast_builder.GetOrCreateDeclContextForUid(uid: m_id); |
405 | m_record.ConstructRecord(); |
406 | // Maybe we should check the construsted record size with the size in pdb. If |
407 | // they mismatch, it might be pdb has fields info missing. |
408 | for (const auto &field : m_record.record.fields) { |
409 | AddMember(clang, field.get(), field->bit_offset, m_derived_ct, m_layout, |
410 | decl_ctx); |
411 | } |
412 | } |
413 | |
414 | void UdtRecordCompleter::Record::CollectMember( |
415 | llvm::StringRef name, uint64_t offset, uint64_t field_size, |
416 | clang::QualType qt, lldb::AccessType access, uint64_t bitfield_width) { |
417 | fields_map[offset].push_back(Elt: std::make_unique<Member>( |
418 | args&: name, args&: offset, args&: field_size, args&: qt, args&: access, args&: bitfield_width)); |
419 | if (start_offset > offset) |
420 | start_offset = offset; |
421 | } |
422 | |
423 | void UdtRecordCompleter::Record::ConstructRecord() { |
424 | // For anonymous unions in a struct, msvc generated pdb doesn't have the |
425 | // entity for that union. So, we need to construct anonymous union and struct |
426 | // based on field offsets. The final AST is likely not matching the exact |
427 | // original AST, but the memory layout is preseved. |
428 | // After we collecting all fields in visitKnownMember, we have all fields in |
429 | // increasing offset order in m_fields. Since we are iterating in increase |
430 | // offset order, if the current offset is equal to m_start_offset, we insert |
431 | // it as direct field of top level record. If the current offset is greater |
432 | // than m_start_offset, we should be able to find a field in end_offset_map |
433 | // whose end offset is less than or equal to current offset. (if not, it might |
434 | // be missing field info. We will ignore the field in this case. e.g. Field A |
435 | // starts at 0 with size 4 bytes, and Field B starts at 2 with size 4 bytes. |
436 | // Normally, there must be something which ends at/before 2.) Then we will |
437 | // append current field to the end of parent record. If parent is struct, we |
438 | // can just grow it. If parent is a field, it's a field inside an union. We |
439 | // convert it into an anonymous struct containing old field and new field. |
440 | |
441 | // The end offset to a vector of field/struct that ends at the offset. |
442 | std::map<uint64_t, std::vector<Member *>> end_offset_map; |
443 | for (auto &pair : fields_map) { |
444 | uint64_t offset = pair.first; |
445 | auto &fields = pair.second; |
446 | lldbassert(offset >= start_offset); |
447 | Member *parent = &record; |
448 | if (offset > start_offset) { |
449 | // Find the field with largest end offset that is <= offset. If it's less |
450 | // than offset, it indicates there are padding bytes between end offset |
451 | // and offset. |
452 | lldbassert(!end_offset_map.empty()); |
453 | auto iter = end_offset_map.lower_bound(x: offset); |
454 | if (iter == end_offset_map.end()) |
455 | --iter; |
456 | else if (iter->first > offset) { |
457 | if (iter == end_offset_map.begin()) |
458 | continue; |
459 | --iter; |
460 | } |
461 | if (iter->second.empty()) |
462 | continue; |
463 | parent = iter->second.back(); |
464 | iter->second.pop_back(); |
465 | } |
466 | // If it's a field, then the field is inside a union, so we can safely |
467 | // increase its size by converting it to a struct to hold multiple fields. |
468 | if (parent->kind == Member::Field) |
469 | parent->ConvertToStruct(); |
470 | |
471 | if (fields.size() == 1) { |
472 | uint64_t end_offset = offset + fields.back()->bit_size; |
473 | parent->fields.push_back(Elt: std::move(fields.back())); |
474 | if (parent->kind == Member::Struct) { |
475 | end_offset_map[end_offset].push_back(x: parent); |
476 | } else { |
477 | lldbassert(parent == &record && |
478 | "If parent is union, it must be the top level record." ); |
479 | end_offset_map[end_offset].push_back(x: parent->fields.back().get()); |
480 | } |
481 | } else { |
482 | if (parent->kind == Member::Struct) { |
483 | parent->fields.push_back(Elt: std::make_unique<Member>(args: Member::Union)); |
484 | parent = parent->fields.back().get(); |
485 | parent->bit_offset = offset; |
486 | } else { |
487 | lldbassert(parent == &record && |
488 | "If parent is union, it must be the top level record." ); |
489 | } |
490 | for (auto &field : fields) { |
491 | int64_t bit_size = field->bit_size; |
492 | parent->fields.push_back(Elt: std::move(field)); |
493 | end_offset_map[offset + bit_size].push_back( |
494 | x: parent->fields.back().get()); |
495 | } |
496 | } |
497 | } |
498 | } |
499 | |