1//=== AcceleratorRecordsSaver.cpp -----------------------------------------===//
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 "AcceleratorRecordsSaver.h"
10#include "llvm/DWARFLinker/Utils.h"
11#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"
12#include "llvm/Support/DJB.h"
13
14using namespace llvm;
15using namespace dwarf_linker;
16using namespace dwarf_linker::parallel;
17
18static uint32_t hashFullyQualifiedName(CompileUnit &InputCU, DWARFDie &InputDIE,
19 int ChildRecurseDepth = 0) {
20 const char *Name = nullptr;
21 CompileUnit *CU = &InputCU;
22 std::optional<DWARFFormValue> RefVal;
23
24 if (Error Err = finiteLoop(Iteration: [&]() -> Expected<bool> {
25 if (const char *CurrentName = InputDIE.getName(Kind: DINameKind::ShortName))
26 Name = CurrentName;
27
28 if (!(RefVal = InputDIE.find(Attr: dwarf::DW_AT_specification)) &&
29 !(RefVal = InputDIE.find(Attr: dwarf::DW_AT_abstract_origin)))
30 return false;
31
32 if (!RefVal->isFormClass(FC: DWARFFormValue::FC_Reference))
33 return false;
34
35 std::optional<UnitEntryPairTy> RefDie = CU->resolveDIEReference(
36 RefValue: *RefVal, CanResolveInterCUReferences: ResolveInterCUReferencesMode::Resolve);
37 if (!RefDie)
38 return false;
39
40 if (!RefDie->DieEntry)
41 return false;
42
43 CU = RefDie->CU;
44 InputDIE = RefDie->CU->getDIE(Die: RefDie->DieEntry);
45 return true;
46 })) {
47 consumeError(Err: std::move(Err));
48 }
49
50 if (!Name && InputDIE.getTag() == dwarf::DW_TAG_namespace)
51 Name = "(anonymous namespace)";
52
53 DWARFDie ParentDie = InputDIE.getParent();
54 if (!ParentDie.isValid() || ParentDie.getTag() == dwarf::DW_TAG_compile_unit)
55 return djbHash(Buffer: Name ? Name : "", H: djbHash(Buffer: ChildRecurseDepth ? "" : "::"));
56
57 return djbHash(
58 Buffer: (Name ? Name : ""),
59 H: djbHash(Buffer: (Name ? "::" : ""),
60 H: hashFullyQualifiedName(InputCU&: *CU, InputDIE&: ParentDie, ChildRecurseDepth: ++ChildRecurseDepth)));
61}
62
63void AcceleratorRecordsSaver::save(const DWARFDebugInfoEntry *InputDieEntry,
64 DIE *OutDIE, AttributesInfo &AttrInfo,
65 TypeEntry *TypeEntry) {
66 if (GlobalData.getOptions().AccelTables.empty())
67 return;
68
69 DWARFDie InputDIE = InUnit.getDIE(Die: InputDieEntry);
70
71 // Look for short name recursively if short name is not known yet.
72 if (AttrInfo.Name == nullptr)
73 if (const char *ShortName = InputDIE.getShortName())
74 AttrInfo.Name = GlobalData.getStringPool().insert(NewValue: ShortName).first;
75
76 switch (InputDieEntry->getTag()) {
77 case dwarf::DW_TAG_array_type:
78 case dwarf::DW_TAG_class_type:
79 case dwarf::DW_TAG_enumeration_type:
80 case dwarf::DW_TAG_pointer_type:
81 case dwarf::DW_TAG_reference_type:
82 case dwarf::DW_TAG_string_type:
83 case dwarf::DW_TAG_structure_type:
84 case dwarf::DW_TAG_subroutine_type:
85 case dwarf::DW_TAG_template_alias:
86 case dwarf::DW_TAG_typedef:
87 case dwarf::DW_TAG_union_type:
88 case dwarf::DW_TAG_ptr_to_member_type:
89 case dwarf::DW_TAG_set_type:
90 case dwarf::DW_TAG_subrange_type:
91 case dwarf::DW_TAG_base_type:
92 case dwarf::DW_TAG_const_type:
93 case dwarf::DW_TAG_constant:
94 case dwarf::DW_TAG_file_type:
95 case dwarf::DW_TAG_namelist:
96 case dwarf::DW_TAG_packed_type:
97 case dwarf::DW_TAG_volatile_type:
98 case dwarf::DW_TAG_restrict_type:
99 case dwarf::DW_TAG_atomic_type:
100 case dwarf::DW_TAG_interface_type:
101 case dwarf::DW_TAG_unspecified_type:
102 case dwarf::DW_TAG_shared_type:
103 case dwarf::DW_TAG_immutable_type:
104 case dwarf::DW_TAG_rvalue_reference_type: {
105 if (AttrInfo.IsDeclaration)
106 break;
107
108 // hashFullyQualifiedName() advances InputDIE along the specification and
109 // abstract origin chain, so the DIE's own attributes have to be read first.
110
111 // The linkage name may be attached to the declaration rather than to the
112 // definition.
113 if (!AttrInfo.MangledName)
114 if (const char *LinkageName = InputDIE.getLinkageName())
115 AttrInfo.MangledName =
116 GlobalData.getStringPool().insert(NewValue: LinkageName).first;
117
118 uint64_t RuntimeLang =
119 dwarf::toUnsigned(V: InputDIE.find(Attr: dwarf::DW_AT_APPLE_runtime_class))
120 .value_or(u: 0);
121
122 bool ObjCClassIsImplementation =
123 (RuntimeLang == dwarf::DW_LANG_ObjC ||
124 RuntimeLang == dwarf::DW_LANG_ObjC_plus_plus) &&
125 dwarf::toUnsigned(V: InputDIE.find(Attr: dwarf::DW_AT_APPLE_objc_complete_type))
126 .value_or(u: 0);
127
128 if (AttrInfo.Name != nullptr && !AttrInfo.Name->getKey().empty()) {
129 uint32_t Hash = hashFullyQualifiedName(InputCU&: InUnit, InputDIE);
130 saveTypeRecord(InputDieEntry, Name: AttrInfo.Name, OutDIE,
131 Tag: InputDieEntry->getTag(), QualifiedNameHash: Hash, ObjcClassImplementation: ObjCClassIsImplementation,
132 TypeEntry);
133 }
134
135 // Swift carries a type's mangled name in DW_AT_linkage_name, which a
136 // consumer may look up on its own.
137 if (AttrInfo.MangledName != nullptr &&
138 RuntimeLang == dwarf::DW_LANG_Swift &&
139 !AttrInfo.MangledName->getKey().empty() &&
140 AttrInfo.MangledName != AttrInfo.Name)
141 saveTypeRecord(InputDieEntry, Name: AttrInfo.MangledName, OutDIE,
142 Tag: InputDieEntry->getTag(),
143 QualifiedNameHash: djbHash(Buffer: AttrInfo.MangledName->getKey()),
144 ObjcClassImplementation: ObjCClassIsImplementation, TypeEntry);
145 } break;
146 case dwarf::DW_TAG_namespace: {
147 if (AttrInfo.Name == nullptr)
148 AttrInfo.Name =
149 GlobalData.getStringPool().insert(NewValue: "(anonymous namespace)").first;
150
151 saveNamespaceRecord(InputDieEntry, Name: AttrInfo.Name, OutDIE,
152 Tag: InputDieEntry->getTag(), TypeEntry);
153 } break;
154 case dwarf::DW_TAG_imported_declaration: {
155 if (AttrInfo.Name != nullptr)
156 saveNamespaceRecord(InputDieEntry, Name: AttrInfo.Name, OutDIE,
157 Tag: InputDieEntry->getTag(), TypeEntry);
158 } break;
159 case dwarf::DW_TAG_compile_unit:
160 case dwarf::DW_TAG_lexical_block: {
161 // Nothing to do.
162 } break;
163 default:
164 // HasLiveAddress / HasRanges below decides whether a DIE carries enough
165 // information of its own to warrant a name record; the output unit is
166 // incidental and routed by the helpers.
167
168 if ((AttrInfo.HasLiveAddress || AttrInfo.HasRanges) &&
169 !(InputDieEntry->getTag() == dwarf::DW_TAG_variable &&
170 hasImplicitAddressLocation(Die: InputDIE))) {
171 if (AttrInfo.Name)
172 saveNameRecord(
173 InputDieEntry, Name: AttrInfo.Name, OutDIE, Tag: InputDieEntry->getTag(),
174 AvoidForPubSections: InputDieEntry->getTag() == dwarf::DW_TAG_inlined_subroutine,
175 TypeEntry);
176
177 // Look for mangled name recursively if mangled name is not known yet.
178 if (!AttrInfo.MangledName)
179 if (const char *LinkageName = InputDIE.getLinkageName())
180 AttrInfo.MangledName =
181 GlobalData.getStringPool().insert(NewValue: LinkageName).first;
182
183 if (AttrInfo.MangledName && AttrInfo.MangledName != AttrInfo.Name)
184 saveNameRecord(InputDieEntry, Name: AttrInfo.MangledName, OutDIE,
185 Tag: InputDieEntry->getTag(),
186 AvoidForPubSections: InputDieEntry->getTag() ==
187 dwarf::DW_TAG_inlined_subroutine,
188 TypeEntry);
189
190 // Strip template parameters from the short name.
191 if (AttrInfo.Name && AttrInfo.MangledName != AttrInfo.Name &&
192 (InputDieEntry->getTag() != dwarf::DW_TAG_inlined_subroutine)) {
193 if (std::optional<StringRef> Name =
194 StripTemplateParameters(Name: AttrInfo.Name->getKey())) {
195 StringEntry *NameWithoutTemplateParams =
196 GlobalData.getStringPool().insert(NewValue: *Name).first;
197
198 saveNameRecord(InputDieEntry, Name: NameWithoutTemplateParams, OutDIE,
199 Tag: InputDieEntry->getTag(), AvoidForPubSections: true, TypeEntry);
200 }
201 }
202
203 if (AttrInfo.Name)
204 saveObjC(InputDieEntry, OutDIE, AttrInfo, TypeEntry);
205 }
206 break;
207 }
208}
209
210void AcceleratorRecordsSaver::saveObjC(const DWARFDebugInfoEntry *InputDieEntry,
211 DIE *OutDIE, AttributesInfo &AttrInfo,
212 TypeEntry *TypeEntry) {
213 std::optional<ObjCSelectorNames> Names =
214 getObjCNamesIfSelector(Name: AttrInfo.Name->getKey());
215 if (!Names)
216 return;
217
218 StringEntry *Selector =
219 GlobalData.getStringPool().insert(NewValue: Names->Selector).first;
220 saveNameRecord(InputDieEntry, Name: Selector, OutDIE, Tag: InputDieEntry->getTag(), AvoidForPubSections: true,
221 TypeEntry);
222 StringEntry *ClassName =
223 GlobalData.getStringPool().insert(NewValue: Names->ClassName).first;
224 saveObjCNameRecord(InputDieEntry, Name: ClassName, OutDIE, Tag: InputDieEntry->getTag(),
225 TypeEntry);
226 if (Names->ClassNameNoCategory) {
227 StringEntry *ClassNameNoCategory =
228 GlobalData.getStringPool().insert(NewValue: *Names->ClassNameNoCategory).first;
229 saveObjCNameRecord(InputDieEntry, Name: ClassNameNoCategory, OutDIE,
230 Tag: InputDieEntry->getTag(), TypeEntry);
231 }
232 if (Names->MethodNameNoCategory) {
233 StringEntry *MethodNameNoCategory =
234 GlobalData.getStringPool().insert(NewValue: *Names->MethodNameNoCategory).first;
235 saveNameRecord(InputDieEntry, Name: MethodNameNoCategory, OutDIE,
236 Tag: InputDieEntry->getTag(), AvoidForPubSections: true, TypeEntry);
237 }
238}
239
240std::optional<uint64_t> AcceleratorRecordsSaver::getDefiningParentOutOffset(
241 const DWARFDebugInfoEntry *InputDieEntry) {
242 // getDieOutOffset returns this for input DIEs that were not cloned into
243 // this CU's plain DWARF (e.g. routed only into the artificial type unit).
244 // OutDieOffsetArray is zero-initialized and a real DIE never lives at
245 // offset 0 (the CU header occupies the first bytes of the unit), so 0 is
246 // an unambiguous "no plain-DWARF copy" sentinel.
247 constexpr uint64_t NotClonedInPlainDWARF = 0;
248
249 std::optional<uint32_t> ParentIdx = InputDieEntry->getParentIdx();
250 if (!ParentIdx)
251 return std::nullopt;
252 // Skip parents marked as declarations; the name table should only reference
253 // definitions.
254 if (dwarf::toUnsigned(V: InUnit.find(DieIdx: *ParentIdx, Attrs: dwarf::DW_AT_declaration), Default: 0))
255 return std::nullopt;
256 uint64_t ParentOutOffset = InUnit.getDieOutOffset(Idx: *ParentIdx);
257 if (ParentOutOffset == NotClonedInPlainDWARF)
258 return std::nullopt;
259 return ParentOutOffset;
260}
261
262void AcceleratorRecordsSaver::saveNameRecord(
263 const DWARFDebugInfoEntry *InputDieEntry, StringEntry *Name, DIE *OutDIE,
264 dwarf::Tag Tag, bool AvoidForPubSections, TypeEntry *TypeEntry) {
265 if (OutUnit.isCompileUnit()) {
266 assert(TypeEntry == nullptr);
267 DwarfUnit::AccelInfo Info;
268
269 Info.Type = DwarfUnit::AccelType::Name;
270 Info.String = Name;
271 Info.OutOffset = OutDIE->getOffset();
272 Info.ParentOffset = getDefiningParentOutOffset(InputDieEntry);
273 Info.Tag = Tag;
274 Info.AvoidForPubSections = AvoidForPubSections;
275
276 OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
277 return;
278 }
279
280 // TODO: compute DW_IDX_parent for entries emitted into the artificial type
281 // unit (see saveNamespaceRecord).
282
283 assert(TypeEntry != nullptr);
284 TypeUnit::TypeUnitAccelInfo Info;
285 Info.Type = DwarfUnit::AccelType::Name;
286 Info.String = Name;
287 Info.OutOffset = 0xbaddef;
288 Info.Tag = Tag;
289 Info.AvoidForPubSections = AvoidForPubSections;
290 Info.OutDIE = OutDIE;
291 Info.TypeEntryBodyPtr = TypeEntry->getValue().load();
292
293 OutUnit.getAsTypeUnit()->saveAcceleratorInfo(Info);
294}
295void AcceleratorRecordsSaver::saveNamespaceRecord(
296 const DWARFDebugInfoEntry *InputDieEntry, StringEntry *Name, DIE *OutDIE,
297 dwarf::Tag Tag, TypeEntry *TypeEntry) {
298 if (OutUnit.isCompileUnit()) {
299 assert(TypeEntry == nullptr);
300 DwarfUnit::AccelInfo Info;
301
302 Info.Type = DwarfUnit::AccelType::Namespace;
303 Info.String = Name;
304 Info.OutOffset = OutDIE->getOffset();
305 Info.ParentOffset = getDefiningParentOutOffset(InputDieEntry);
306 Info.Tag = Tag;
307
308 OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
309 return;
310 }
311
312 // TODO: compute DW_IDX_parent for entries emitted into the artificial type
313 // unit. The parent lookup via the input-side DIE tree is only valid for
314 // DIEs cloned into this CU's plain DWARF.
315
316 assert(TypeEntry != nullptr);
317 TypeUnit::TypeUnitAccelInfo Info;
318 Info.Type = DwarfUnit::AccelType::Namespace;
319 Info.String = Name;
320 Info.OutOffset = 0xbaddef;
321 Info.Tag = Tag;
322 Info.OutDIE = OutDIE;
323 Info.TypeEntryBodyPtr = TypeEntry->getValue().load();
324
325 OutUnit.getAsTypeUnit()->saveAcceleratorInfo(Info);
326}
327
328void AcceleratorRecordsSaver::saveObjCNameRecord(
329 const DWARFDebugInfoEntry *InputDieEntry, StringEntry *Name, DIE *OutDIE,
330 dwarf::Tag Tag, TypeEntry *TypeEntry) {
331 if (OutUnit.isCompileUnit()) {
332 assert(TypeEntry == nullptr);
333 DwarfUnit::AccelInfo Info;
334
335 Info.Type = DwarfUnit::AccelType::ObjC;
336 Info.String = Name;
337 Info.OutOffset = OutDIE->getOffset();
338 Info.ParentOffset = getDefiningParentOutOffset(InputDieEntry);
339 Info.Tag = Tag;
340 Info.AvoidForPubSections = true;
341
342 OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
343 return;
344 }
345
346 // TODO: compute DW_IDX_parent for entries emitted into the artificial type
347 // unit (see saveNamespaceRecord).
348
349 assert(TypeEntry != nullptr);
350 TypeUnit::TypeUnitAccelInfo Info;
351 Info.Type = DwarfUnit::AccelType::ObjC;
352 Info.String = Name;
353 Info.OutOffset = 0xbaddef;
354 Info.Tag = Tag;
355 Info.AvoidForPubSections = true;
356 Info.OutDIE = OutDIE;
357 Info.TypeEntryBodyPtr = TypeEntry->getValue().load();
358
359 OutUnit.getAsTypeUnit()->saveAcceleratorInfo(Info);
360}
361
362void AcceleratorRecordsSaver::saveTypeRecord(
363 const DWARFDebugInfoEntry *InputDieEntry, StringEntry *Name, DIE *OutDIE,
364 dwarf::Tag Tag, uint32_t QualifiedNameHash, bool ObjcClassImplementation,
365 TypeEntry *TypeEntry) {
366 if (OutUnit.isCompileUnit()) {
367 assert(TypeEntry == nullptr);
368 DwarfUnit::AccelInfo Info;
369
370 Info.Type = DwarfUnit::AccelType::Type;
371 Info.String = Name;
372 Info.OutOffset = OutDIE->getOffset();
373 Info.ParentOffset = getDefiningParentOutOffset(InputDieEntry);
374 Info.Tag = Tag;
375 Info.QualifiedNameHash = QualifiedNameHash;
376 Info.ObjcClassImplementation = ObjcClassImplementation;
377
378 OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
379 return;
380 }
381
382 // TODO: compute DW_IDX_parent for entries emitted into the artificial type
383 // unit (see saveNamespaceRecord).
384
385 assert(TypeEntry != nullptr);
386 TypeUnit::TypeUnitAccelInfo Info;
387
388 Info.Type = DwarfUnit::AccelType::Type;
389 Info.String = Name;
390 Info.OutOffset = 0xbaddef;
391 Info.Tag = Tag;
392 Info.QualifiedNameHash = QualifiedNameHash;
393 Info.ObjcClassImplementation = ObjcClassImplementation;
394 Info.OutDIE = OutDIE;
395 Info.TypeEntryBodyPtr = TypeEntry->getValue().load();
396 OutUnit.getAsTypeUnit()->saveAcceleratorInfo(Info);
397}
398