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 && AttrInfo.Name != nullptr &&
106 !AttrInfo.Name->getKey().empty()) {
107 uint32_t Hash = hashFullyQualifiedName(InputCU&: InUnit, InputDIE);
108
109 uint64_t RuntimeLang =
110 dwarf::toUnsigned(V: InputDIE.find(Attr: dwarf::DW_AT_APPLE_runtime_class))
111 .value_or(u: 0);
112
113 bool ObjCClassIsImplementation =
114 (RuntimeLang == dwarf::DW_LANG_ObjC ||
115 RuntimeLang == dwarf::DW_LANG_ObjC_plus_plus) &&
116 dwarf::toUnsigned(
117 V: InputDIE.find(Attr: dwarf::DW_AT_APPLE_objc_complete_type))
118 .value_or(u: 0);
119
120 saveTypeRecord(InputDieEntry, Name: AttrInfo.Name, OutDIE,
121 Tag: InputDieEntry->getTag(), QualifiedNameHash: Hash, ObjcClassImplementation: ObjCClassIsImplementation,
122 TypeEntry);
123 }
124 } break;
125 case dwarf::DW_TAG_namespace: {
126 if (AttrInfo.Name == nullptr)
127 AttrInfo.Name =
128 GlobalData.getStringPool().insert(NewValue: "(anonymous namespace)").first;
129
130 saveNamespaceRecord(InputDieEntry, Name: AttrInfo.Name, OutDIE,
131 Tag: InputDieEntry->getTag(), TypeEntry);
132 } break;
133 case dwarf::DW_TAG_imported_declaration: {
134 if (AttrInfo.Name != nullptr)
135 saveNamespaceRecord(InputDieEntry, Name: AttrInfo.Name, OutDIE,
136 Tag: InputDieEntry->getTag(), TypeEntry);
137 } break;
138 case dwarf::DW_TAG_compile_unit:
139 case dwarf::DW_TAG_lexical_block: {
140 // Nothing to do.
141 } break;
142 default:
143 // HasLiveAddress / HasRanges below decides whether a DIE carries enough
144 // information of its own to warrant a name record; the output unit is
145 // incidental and routed by the helpers.
146
147 if (AttrInfo.HasLiveAddress || AttrInfo.HasRanges) {
148 if (AttrInfo.Name)
149 saveNameRecord(
150 InputDieEntry, Name: AttrInfo.Name, OutDIE, Tag: InputDieEntry->getTag(),
151 AvoidForPubSections: InputDieEntry->getTag() == dwarf::DW_TAG_inlined_subroutine,
152 TypeEntry);
153
154 // Look for mangled name recursively if mangled name is not known yet.
155 if (!AttrInfo.MangledName)
156 if (const char *LinkageName = InputDIE.getLinkageName())
157 AttrInfo.MangledName =
158 GlobalData.getStringPool().insert(NewValue: LinkageName).first;
159
160 if (AttrInfo.MangledName && AttrInfo.MangledName != AttrInfo.Name)
161 saveNameRecord(InputDieEntry, Name: AttrInfo.MangledName, OutDIE,
162 Tag: InputDieEntry->getTag(),
163 AvoidForPubSections: InputDieEntry->getTag() ==
164 dwarf::DW_TAG_inlined_subroutine,
165 TypeEntry);
166
167 // Strip template parameters from the short name.
168 if (AttrInfo.Name && AttrInfo.MangledName != AttrInfo.Name &&
169 (InputDieEntry->getTag() != dwarf::DW_TAG_inlined_subroutine)) {
170 if (std::optional<StringRef> Name =
171 StripTemplateParameters(Name: AttrInfo.Name->getKey())) {
172 StringEntry *NameWithoutTemplateParams =
173 GlobalData.getStringPool().insert(NewValue: *Name).first;
174
175 saveNameRecord(InputDieEntry, Name: NameWithoutTemplateParams, OutDIE,
176 Tag: InputDieEntry->getTag(), AvoidForPubSections: true, TypeEntry);
177 }
178 }
179
180 if (AttrInfo.Name)
181 saveObjC(InputDieEntry, OutDIE, AttrInfo, TypeEntry);
182 }
183 break;
184 }
185}
186
187void AcceleratorRecordsSaver::saveObjC(const DWARFDebugInfoEntry *InputDieEntry,
188 DIE *OutDIE, AttributesInfo &AttrInfo,
189 TypeEntry *TypeEntry) {
190 std::optional<ObjCSelectorNames> Names =
191 getObjCNamesIfSelector(Name: AttrInfo.Name->getKey());
192 if (!Names)
193 return;
194
195 StringEntry *Selector =
196 GlobalData.getStringPool().insert(NewValue: Names->Selector).first;
197 saveNameRecord(InputDieEntry, Name: Selector, OutDIE, Tag: InputDieEntry->getTag(), AvoidForPubSections: true,
198 TypeEntry);
199 StringEntry *ClassName =
200 GlobalData.getStringPool().insert(NewValue: Names->ClassName).first;
201 saveObjCNameRecord(InputDieEntry, Name: ClassName, OutDIE, Tag: InputDieEntry->getTag(),
202 TypeEntry);
203 if (Names->ClassNameNoCategory) {
204 StringEntry *ClassNameNoCategory =
205 GlobalData.getStringPool().insert(NewValue: *Names->ClassNameNoCategory).first;
206 saveObjCNameRecord(InputDieEntry, Name: ClassNameNoCategory, OutDIE,
207 Tag: InputDieEntry->getTag(), TypeEntry);
208 }
209 if (Names->MethodNameNoCategory) {
210 StringEntry *MethodNameNoCategory =
211 GlobalData.getStringPool().insert(NewValue: *Names->MethodNameNoCategory).first;
212 saveNameRecord(InputDieEntry, Name: MethodNameNoCategory, OutDIE,
213 Tag: InputDieEntry->getTag(), AvoidForPubSections: true, TypeEntry);
214 }
215}
216
217std::optional<uint64_t> AcceleratorRecordsSaver::getDefiningParentOutOffset(
218 const DWARFDebugInfoEntry *InputDieEntry) {
219 // getDieOutOffset returns this for input DIEs that were not cloned into
220 // this CU's plain DWARF (e.g. routed only into the artificial type unit).
221 // OutDieOffsetArray is zero-initialized and a real DIE never lives at
222 // offset 0 (the CU header occupies the first bytes of the unit), so 0 is
223 // an unambiguous "no plain-DWARF copy" sentinel.
224 constexpr uint64_t NotClonedInPlainDWARF = 0;
225
226 std::optional<uint32_t> ParentIdx = InputDieEntry->getParentIdx();
227 if (!ParentIdx)
228 return std::nullopt;
229 // Skip parents marked as declarations; the name table should only reference
230 // definitions.
231 if (dwarf::toUnsigned(V: InUnit.find(DieIdx: *ParentIdx, Attrs: dwarf::DW_AT_declaration), Default: 0))
232 return std::nullopt;
233 uint64_t ParentOutOffset = InUnit.getDieOutOffset(Idx: *ParentIdx);
234 if (ParentOutOffset == NotClonedInPlainDWARF)
235 return std::nullopt;
236 return ParentOutOffset;
237}
238
239void AcceleratorRecordsSaver::saveNameRecord(
240 const DWARFDebugInfoEntry *InputDieEntry, StringEntry *Name, DIE *OutDIE,
241 dwarf::Tag Tag, bool AvoidForPubSections, TypeEntry *TypeEntry) {
242 if (OutUnit.isCompileUnit()) {
243 assert(TypeEntry == nullptr);
244 DwarfUnit::AccelInfo Info;
245
246 Info.Type = DwarfUnit::AccelType::Name;
247 Info.String = Name;
248 Info.OutOffset = OutDIE->getOffset();
249 Info.ParentOffset = getDefiningParentOutOffset(InputDieEntry);
250 Info.Tag = Tag;
251 Info.AvoidForPubSections = AvoidForPubSections;
252
253 OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
254 return;
255 }
256
257 // TODO: compute DW_IDX_parent for entries emitted into the artificial type
258 // unit (see saveNamespaceRecord).
259
260 assert(TypeEntry != nullptr);
261 TypeUnit::TypeUnitAccelInfo Info;
262 Info.Type = DwarfUnit::AccelType::Name;
263 Info.String = Name;
264 Info.OutOffset = 0xbaddef;
265 Info.Tag = Tag;
266 Info.AvoidForPubSections = AvoidForPubSections;
267 Info.OutDIE = OutDIE;
268 Info.TypeEntryBodyPtr = TypeEntry->getValue().load();
269
270 OutUnit.getAsTypeUnit()->saveAcceleratorInfo(Info);
271}
272void AcceleratorRecordsSaver::saveNamespaceRecord(
273 const DWARFDebugInfoEntry *InputDieEntry, StringEntry *Name, DIE *OutDIE,
274 dwarf::Tag Tag, TypeEntry *TypeEntry) {
275 if (OutUnit.isCompileUnit()) {
276 assert(TypeEntry == nullptr);
277 DwarfUnit::AccelInfo Info;
278
279 Info.Type = DwarfUnit::AccelType::Namespace;
280 Info.String = Name;
281 Info.OutOffset = OutDIE->getOffset();
282 Info.ParentOffset = getDefiningParentOutOffset(InputDieEntry);
283 Info.Tag = Tag;
284
285 OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
286 return;
287 }
288
289 // TODO: compute DW_IDX_parent for entries emitted into the artificial type
290 // unit. The parent lookup via the input-side DIE tree is only valid for
291 // DIEs cloned into this CU's plain DWARF.
292
293 assert(TypeEntry != nullptr);
294 TypeUnit::TypeUnitAccelInfo Info;
295 Info.Type = DwarfUnit::AccelType::Namespace;
296 Info.String = Name;
297 Info.OutOffset = 0xbaddef;
298 Info.Tag = Tag;
299 Info.OutDIE = OutDIE;
300 Info.TypeEntryBodyPtr = TypeEntry->getValue().load();
301
302 OutUnit.getAsTypeUnit()->saveAcceleratorInfo(Info);
303}
304
305void AcceleratorRecordsSaver::saveObjCNameRecord(
306 const DWARFDebugInfoEntry *InputDieEntry, StringEntry *Name, DIE *OutDIE,
307 dwarf::Tag Tag, TypeEntry *TypeEntry) {
308 if (OutUnit.isCompileUnit()) {
309 assert(TypeEntry == nullptr);
310 DwarfUnit::AccelInfo Info;
311
312 Info.Type = DwarfUnit::AccelType::ObjC;
313 Info.String = Name;
314 Info.OutOffset = OutDIE->getOffset();
315 Info.ParentOffset = getDefiningParentOutOffset(InputDieEntry);
316 Info.Tag = Tag;
317 Info.AvoidForPubSections = true;
318
319 OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
320 return;
321 }
322
323 // TODO: compute DW_IDX_parent for entries emitted into the artificial type
324 // unit (see saveNamespaceRecord).
325
326 assert(TypeEntry != nullptr);
327 TypeUnit::TypeUnitAccelInfo Info;
328 Info.Type = DwarfUnit::AccelType::ObjC;
329 Info.String = Name;
330 Info.OutOffset = 0xbaddef;
331 Info.Tag = Tag;
332 Info.AvoidForPubSections = true;
333 Info.OutDIE = OutDIE;
334 Info.TypeEntryBodyPtr = TypeEntry->getValue().load();
335
336 OutUnit.getAsTypeUnit()->saveAcceleratorInfo(Info);
337}
338
339void AcceleratorRecordsSaver::saveTypeRecord(
340 const DWARFDebugInfoEntry *InputDieEntry, StringEntry *Name, DIE *OutDIE,
341 dwarf::Tag Tag, uint32_t QualifiedNameHash, bool ObjcClassImplementation,
342 TypeEntry *TypeEntry) {
343 if (OutUnit.isCompileUnit()) {
344 assert(TypeEntry == nullptr);
345 DwarfUnit::AccelInfo Info;
346
347 Info.Type = DwarfUnit::AccelType::Type;
348 Info.String = Name;
349 Info.OutOffset = OutDIE->getOffset();
350 Info.ParentOffset = getDefiningParentOutOffset(InputDieEntry);
351 Info.Tag = Tag;
352 Info.QualifiedNameHash = QualifiedNameHash;
353 Info.ObjcClassImplementation = ObjcClassImplementation;
354
355 OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
356 return;
357 }
358
359 // TODO: compute DW_IDX_parent for entries emitted into the artificial type
360 // unit (see saveNamespaceRecord).
361
362 assert(TypeEntry != nullptr);
363 TypeUnit::TypeUnitAccelInfo Info;
364
365 Info.Type = DwarfUnit::AccelType::Type;
366 Info.String = Name;
367 Info.OutOffset = 0xbaddef;
368 Info.Tag = Tag;
369 Info.QualifiedNameHash = QualifiedNameHash;
370 Info.ObjcClassImplementation = ObjcClassImplementation;
371 Info.OutDIE = OutDIE;
372 Info.TypeEntryBodyPtr = TypeEntry->getValue().load();
373 OutUnit.getAsTypeUnit()->saveAcceleratorInfo(Info);
374}
375