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 if (AttrInfo.Name)
170 saveNameRecord(
171 InputDieEntry, Name: AttrInfo.Name, OutDIE, Tag: InputDieEntry->getTag(),
172 AvoidForPubSections: InputDieEntry->getTag() == dwarf::DW_TAG_inlined_subroutine,
173 TypeEntry);
174
175 // Look for mangled name recursively if mangled name is not known yet.
176 if (!AttrInfo.MangledName)
177 if (const char *LinkageName = InputDIE.getLinkageName())
178 AttrInfo.MangledName =
179 GlobalData.getStringPool().insert(NewValue: LinkageName).first;
180
181 if (AttrInfo.MangledName && AttrInfo.MangledName != AttrInfo.Name)
182 saveNameRecord(InputDieEntry, Name: AttrInfo.MangledName, OutDIE,
183 Tag: InputDieEntry->getTag(),
184 AvoidForPubSections: InputDieEntry->getTag() ==
185 dwarf::DW_TAG_inlined_subroutine,
186 TypeEntry);
187
188 // Strip template parameters from the short name.
189 if (AttrInfo.Name && AttrInfo.MangledName != AttrInfo.Name &&
190 (InputDieEntry->getTag() != dwarf::DW_TAG_inlined_subroutine)) {
191 if (std::optional<StringRef> Name =
192 StripTemplateParameters(Name: AttrInfo.Name->getKey())) {
193 StringEntry *NameWithoutTemplateParams =
194 GlobalData.getStringPool().insert(NewValue: *Name).first;
195
196 saveNameRecord(InputDieEntry, Name: NameWithoutTemplateParams, OutDIE,
197 Tag: InputDieEntry->getTag(), AvoidForPubSections: true, TypeEntry);
198 }
199 }
200
201 if (AttrInfo.Name)
202 saveObjC(InputDieEntry, OutDIE, AttrInfo, TypeEntry);
203 }
204 break;
205 }
206}
207
208void AcceleratorRecordsSaver::saveObjC(const DWARFDebugInfoEntry *InputDieEntry,
209 DIE *OutDIE, AttributesInfo &AttrInfo,
210 TypeEntry *TypeEntry) {
211 std::optional<ObjCSelectorNames> Names =
212 getObjCNamesIfSelector(Name: AttrInfo.Name->getKey());
213 if (!Names)
214 return;
215
216 StringEntry *Selector =
217 GlobalData.getStringPool().insert(NewValue: Names->Selector).first;
218 saveNameRecord(InputDieEntry, Name: Selector, OutDIE, Tag: InputDieEntry->getTag(), AvoidForPubSections: true,
219 TypeEntry);
220 StringEntry *ClassName =
221 GlobalData.getStringPool().insert(NewValue: Names->ClassName).first;
222 saveObjCNameRecord(InputDieEntry, Name: ClassName, OutDIE, Tag: InputDieEntry->getTag(),
223 TypeEntry);
224 if (Names->ClassNameNoCategory) {
225 StringEntry *ClassNameNoCategory =
226 GlobalData.getStringPool().insert(NewValue: *Names->ClassNameNoCategory).first;
227 saveObjCNameRecord(InputDieEntry, Name: ClassNameNoCategory, OutDIE,
228 Tag: InputDieEntry->getTag(), TypeEntry);
229 }
230 if (Names->MethodNameNoCategory) {
231 StringEntry *MethodNameNoCategory =
232 GlobalData.getStringPool().insert(NewValue: *Names->MethodNameNoCategory).first;
233 saveNameRecord(InputDieEntry, Name: MethodNameNoCategory, OutDIE,
234 Tag: InputDieEntry->getTag(), AvoidForPubSections: true, TypeEntry);
235 }
236}
237
238std::optional<uint64_t> AcceleratorRecordsSaver::getDefiningParentOutOffset(
239 const DWARFDebugInfoEntry *InputDieEntry) {
240 // getDieOutOffset returns this for input DIEs that were not cloned into
241 // this CU's plain DWARF (e.g. routed only into the artificial type unit).
242 // OutDieOffsetArray is zero-initialized and a real DIE never lives at
243 // offset 0 (the CU header occupies the first bytes of the unit), so 0 is
244 // an unambiguous "no plain-DWARF copy" sentinel.
245 constexpr uint64_t NotClonedInPlainDWARF = 0;
246
247 std::optional<uint32_t> ParentIdx = InputDieEntry->getParentIdx();
248 if (!ParentIdx)
249 return std::nullopt;
250 // Skip parents marked as declarations; the name table should only reference
251 // definitions.
252 if (dwarf::toUnsigned(V: InUnit.find(DieIdx: *ParentIdx, Attrs: dwarf::DW_AT_declaration), Default: 0))
253 return std::nullopt;
254 uint64_t ParentOutOffset = InUnit.getDieOutOffset(Idx: *ParentIdx);
255 if (ParentOutOffset == NotClonedInPlainDWARF)
256 return std::nullopt;
257 return ParentOutOffset;
258}
259
260void AcceleratorRecordsSaver::saveNameRecord(
261 const DWARFDebugInfoEntry *InputDieEntry, StringEntry *Name, DIE *OutDIE,
262 dwarf::Tag Tag, bool AvoidForPubSections, TypeEntry *TypeEntry) {
263 if (OutUnit.isCompileUnit()) {
264 assert(TypeEntry == nullptr);
265 DwarfUnit::AccelInfo Info;
266
267 Info.Type = DwarfUnit::AccelType::Name;
268 Info.String = Name;
269 Info.OutOffset = OutDIE->getOffset();
270 Info.ParentOffset = getDefiningParentOutOffset(InputDieEntry);
271 Info.Tag = Tag;
272 Info.AvoidForPubSections = AvoidForPubSections;
273
274 OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
275 return;
276 }
277
278 // TODO: compute DW_IDX_parent for entries emitted into the artificial type
279 // unit (see saveNamespaceRecord).
280
281 assert(TypeEntry != nullptr);
282 TypeUnit::TypeUnitAccelInfo Info;
283 Info.Type = DwarfUnit::AccelType::Name;
284 Info.String = Name;
285 Info.OutOffset = 0xbaddef;
286 Info.Tag = Tag;
287 Info.AvoidForPubSections = AvoidForPubSections;
288 Info.OutDIE = OutDIE;
289 Info.TypeEntryBodyPtr = TypeEntry->getValue().load();
290
291 OutUnit.getAsTypeUnit()->saveAcceleratorInfo(Info);
292}
293void AcceleratorRecordsSaver::saveNamespaceRecord(
294 const DWARFDebugInfoEntry *InputDieEntry, StringEntry *Name, DIE *OutDIE,
295 dwarf::Tag Tag, TypeEntry *TypeEntry) {
296 if (OutUnit.isCompileUnit()) {
297 assert(TypeEntry == nullptr);
298 DwarfUnit::AccelInfo Info;
299
300 Info.Type = DwarfUnit::AccelType::Namespace;
301 Info.String = Name;
302 Info.OutOffset = OutDIE->getOffset();
303 Info.ParentOffset = getDefiningParentOutOffset(InputDieEntry);
304 Info.Tag = Tag;
305
306 OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
307 return;
308 }
309
310 // TODO: compute DW_IDX_parent for entries emitted into the artificial type
311 // unit. The parent lookup via the input-side DIE tree is only valid for
312 // DIEs cloned into this CU's plain DWARF.
313
314 assert(TypeEntry != nullptr);
315 TypeUnit::TypeUnitAccelInfo Info;
316 Info.Type = DwarfUnit::AccelType::Namespace;
317 Info.String = Name;
318 Info.OutOffset = 0xbaddef;
319 Info.Tag = Tag;
320 Info.OutDIE = OutDIE;
321 Info.TypeEntryBodyPtr = TypeEntry->getValue().load();
322
323 OutUnit.getAsTypeUnit()->saveAcceleratorInfo(Info);
324}
325
326void AcceleratorRecordsSaver::saveObjCNameRecord(
327 const DWARFDebugInfoEntry *InputDieEntry, StringEntry *Name, DIE *OutDIE,
328 dwarf::Tag Tag, TypeEntry *TypeEntry) {
329 if (OutUnit.isCompileUnit()) {
330 assert(TypeEntry == nullptr);
331 DwarfUnit::AccelInfo Info;
332
333 Info.Type = DwarfUnit::AccelType::ObjC;
334 Info.String = Name;
335 Info.OutOffset = OutDIE->getOffset();
336 Info.ParentOffset = getDefiningParentOutOffset(InputDieEntry);
337 Info.Tag = Tag;
338 Info.AvoidForPubSections = true;
339
340 OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
341 return;
342 }
343
344 // TODO: compute DW_IDX_parent for entries emitted into the artificial type
345 // unit (see saveNamespaceRecord).
346
347 assert(TypeEntry != nullptr);
348 TypeUnit::TypeUnitAccelInfo Info;
349 Info.Type = DwarfUnit::AccelType::ObjC;
350 Info.String = Name;
351 Info.OutOffset = 0xbaddef;
352 Info.Tag = Tag;
353 Info.AvoidForPubSections = true;
354 Info.OutDIE = OutDIE;
355 Info.TypeEntryBodyPtr = TypeEntry->getValue().load();
356
357 OutUnit.getAsTypeUnit()->saveAcceleratorInfo(Info);
358}
359
360void AcceleratorRecordsSaver::saveTypeRecord(
361 const DWARFDebugInfoEntry *InputDieEntry, StringEntry *Name, DIE *OutDIE,
362 dwarf::Tag Tag, uint32_t QualifiedNameHash, bool ObjcClassImplementation,
363 TypeEntry *TypeEntry) {
364 if (OutUnit.isCompileUnit()) {
365 assert(TypeEntry == nullptr);
366 DwarfUnit::AccelInfo Info;
367
368 Info.Type = DwarfUnit::AccelType::Type;
369 Info.String = Name;
370 Info.OutOffset = OutDIE->getOffset();
371 Info.ParentOffset = getDefiningParentOutOffset(InputDieEntry);
372 Info.Tag = Tag;
373 Info.QualifiedNameHash = QualifiedNameHash;
374 Info.ObjcClassImplementation = ObjcClassImplementation;
375
376 OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
377 return;
378 }
379
380 // TODO: compute DW_IDX_parent for entries emitted into the artificial type
381 // unit (see saveNamespaceRecord).
382
383 assert(TypeEntry != nullptr);
384 TypeUnit::TypeUnitAccelInfo Info;
385
386 Info.Type = DwarfUnit::AccelType::Type;
387 Info.String = Name;
388 Info.OutOffset = 0xbaddef;
389 Info.Tag = Tag;
390 Info.QualifiedNameHash = QualifiedNameHash;
391 Info.ObjcClassImplementation = ObjcClassImplementation;
392 Info.OutDIE = OutDIE;
393 Info.TypeEntryBodyPtr = TypeEntry->getValue().load();
394 OutUnit.getAsTypeUnit()->saveAcceleratorInfo(Info);
395}
396