xref: /freebsd/contrib/llvm-project/llvm/lib/DWARFLinker/Parallel/DWARFLinkerTypeUnit.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===- DWARFLinkerTypeUnit.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 "DWARFLinkerTypeUnit.h"
10 #include "DIEGenerator.h"
11 #include "llvm/Support/LEB128.h"
12 
13 using namespace llvm;
14 using namespace dwarf_linker;
15 using namespace dwarf_linker::parallel;
16 
TypeUnit(LinkingGlobalData & GlobalData,unsigned ID,std::optional<uint16_t> Language,dwarf::FormParams Format,endianness Endianess)17 TypeUnit::TypeUnit(LinkingGlobalData &GlobalData, unsigned ID,
18                    std::optional<uint16_t> Language, dwarf::FormParams Format,
19                    endianness Endianess)
20     : DwarfUnit(GlobalData, ID, ""), Language(Language),
21       AcceleratorRecords(&GlobalData.getAllocator()) {
22 
23   UnitName = "__artificial_type_unit";
24 
25   setOutputFormat(Format, Endianess);
26 
27   // Create line table prologue.
28   LineTable.Prologue.FormParams = getFormParams();
29   LineTable.Prologue.MinInstLength = 1;
30   LineTable.Prologue.MaxOpsPerInst = 1;
31   LineTable.Prologue.DefaultIsStmt = 1;
32   LineTable.Prologue.LineBase = -5;
33   LineTable.Prologue.LineRange = 14;
34   LineTable.Prologue.OpcodeBase = 13;
35   LineTable.Prologue.StandardOpcodeLengths = {0, 1, 1, 1, 1, 0,
36                                               0, 0, 1, 0, 0, 1};
37 
38   getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo);
39 }
40 
createDIETree(BumpPtrAllocator & Allocator)41 void TypeUnit::createDIETree(BumpPtrAllocator &Allocator) {
42   prepareDataForTreeCreation();
43 
44   // TaskGroup is created here as internal code has calls to
45   // PerThreadBumpPtrAllocator which should be called from the task group task.
46   llvm::parallel::TaskGroup TG;
47   TG.spawn([&]() {
48     SectionDescriptor &DebugInfoSection =
49         getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo);
50     SectionDescriptor &DebugLineSection =
51         getOrCreateSectionDescriptor(DebugSectionKind::DebugLine);
52 
53     DIEGenerator DIETreeGenerator(Allocator, *this);
54     OffsetsPtrVector PatchesOffsets;
55 
56     // Create a Die for artificial compilation unit for types.
57     DIE *UnitDIE = DIETreeGenerator.createDIE(dwarf::DW_TAG_compile_unit, 0);
58     uint64_t OutOffset = getDebugInfoHeaderSize();
59     UnitDIE->setOffset(OutOffset);
60 
61     SmallString<200> ProducerString;
62     ProducerString += "llvm DWARFLinkerParallel library version ";
63     DebugInfoSection.notePatchWithOffsetUpdate(
64         DebugStrPatch{
65             {OutOffset},
66             GlobalData.getStringPool().insert(ProducerString.str()).first},
67         PatchesOffsets);
68     OutOffset += DIETreeGenerator
69                      .addStringPlaceholderAttribute(dwarf::DW_AT_producer,
70                                                     dwarf::DW_FORM_strp)
71                      .second;
72 
73     if (Language) {
74       OutOffset += DIETreeGenerator
75                        .addScalarAttribute(dwarf::DW_AT_language,
76                                            dwarf::DW_FORM_data2, *Language)
77                        .second;
78     }
79 
80     DebugInfoSection.notePatchWithOffsetUpdate(
81         DebugStrPatch{{OutOffset},
82                       GlobalData.getStringPool().insert(getUnitName()).first},
83         PatchesOffsets);
84     OutOffset += DIETreeGenerator
85                      .addStringPlaceholderAttribute(dwarf::DW_AT_name,
86                                                     dwarf::DW_FORM_strp)
87                      .second;
88 
89     if (!LineTable.Prologue.FileNames.empty()) {
90       DebugInfoSection.notePatchWithOffsetUpdate(
91           DebugOffsetPatch{OutOffset, &DebugLineSection}, PatchesOffsets);
92 
93       OutOffset += DIETreeGenerator
94                        .addScalarAttribute(dwarf::DW_AT_stmt_list,
95                                            dwarf::DW_FORM_sec_offset, 0xbaddef)
96                        .second;
97     }
98 
99     DebugInfoSection.notePatchWithOffsetUpdate(
100         DebugStrPatch{{OutOffset}, GlobalData.getStringPool().insert("").first},
101         PatchesOffsets);
102     OutOffset += DIETreeGenerator
103                      .addStringPlaceholderAttribute(dwarf::DW_AT_comp_dir,
104                                                     dwarf::DW_FORM_strp)
105                      .second;
106 
107     if (!DebugStringIndexMap.empty()) {
108       // Type unit is assumed to be emitted first. Thus we can use direct value
109       // for DW_AT_str_offsets_base attribute(No need to fix it up with unit
110       // offset value).
111       OutOffset += DIETreeGenerator
112                        .addScalarAttribute(dwarf::DW_AT_str_offsets_base,
113                                            dwarf::DW_FORM_sec_offset,
114                                            getDebugStrOffsetsHeaderSize())
115                        .second;
116     }
117 
118     UnitDIE->setSize(OutOffset - UnitDIE->getOffset() + 1);
119     OutOffset =
120         finalizeTypeEntryRec(UnitDIE->getOffset(), UnitDIE, Types.getRoot());
121 
122     // Update patch offsets.
123     for (uint64_t *OffsetPtr : PatchesOffsets)
124       *OffsetPtr += getULEB128Size(UnitDIE->getAbbrevNumber());
125 
126     setOutUnitDIE(UnitDIE);
127   });
128 }
129 
prepareDataForTreeCreation()130 void TypeUnit::prepareDataForTreeCreation() {
131   SectionDescriptor &DebugInfoSection =
132       getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo);
133 
134   // Type unit data created parallelly. So the order of data is not
135   // deterministic. Order data here if we need deterministic output.
136 
137   llvm::parallel::TaskGroup TG;
138 
139   if (!GlobalData.getOptions().AllowNonDeterministicOutput) {
140     TG.spawn([&]() {
141       // Sort types to have a deterministic output.
142       Types.sortTypes();
143     });
144   }
145 
146   TG.spawn([&]() {
147     if (!GlobalData.getOptions().AllowNonDeterministicOutput) {
148       // Sort decl type patches to have a deterministic output.
149       std::function<bool(const DebugTypeDeclFilePatch &LHS,
150                          const DebugTypeDeclFilePatch &RHS)>
151           PatchesComparator = [&](const DebugTypeDeclFilePatch &LHS,
152                                   const DebugTypeDeclFilePatch &RHS) {
153             return LHS.Directory->first() < RHS.Directory->first() ||
154                    (!(RHS.Directory->first() < LHS.Directory->first()) &&
155                     LHS.FilePath->first() < RHS.FilePath->first());
156           };
157       // Sort patches to have a deterministic output.
158       DebugInfoSection.ListDebugTypeDeclFilePatch.sort(PatchesComparator);
159     }
160 
161     // Update DW_AT_decl_file attribute
162     dwarf::Form DeclFileForm =
163         getScalarFormForValue(
164             DebugInfoSection.ListDebugTypeDeclFilePatch.size())
165             .first;
166 
167     DebugInfoSection.ListDebugTypeDeclFilePatch.forEach(
168         [&](DebugTypeDeclFilePatch &Patch) {
169           TypeEntryBody *TypeEntry = Patch.TypeName->getValue().load();
170           assert(TypeEntry &&
171                  formatv("No data for type {0}", Patch.TypeName->getKey())
172                      .str()
173                      .c_str());
174           if (&TypeEntry->getFinalDie() != Patch.Die)
175             return;
176 
177           uint32_t FileIdx =
178               addFileNameIntoLinetable(Patch.Directory, Patch.FilePath);
179 
180           unsigned DIESize = Patch.Die->getSize();
181           DIEGenerator DIEGen(Patch.Die, Types.getThreadLocalAllocator(),
182                               *this);
183 
184           DIESize += DIEGen
185                          .addScalarAttribute(dwarf::DW_AT_decl_file,
186                                              DeclFileForm, FileIdx)
187                          .second;
188           Patch.Die->setSize(DIESize);
189         });
190   });
191 
192   if (!GlobalData.getOptions().AllowNonDeterministicOutput) {
193     // Sort patches to have a deterministic output.
194     TG.spawn([&]() {
195       forEach([&](SectionDescriptor &OutSection) {
196         std::function<bool(const DebugStrPatch &LHS, const DebugStrPatch &RHS)>
197             StrPatchesComparator =
198                 [&](const DebugStrPatch &LHS, const DebugStrPatch &RHS) {
199                   return LHS.String->getKey() < RHS.String->getKey();
200                 };
201         OutSection.ListDebugStrPatch.sort(StrPatchesComparator);
202 
203         std::function<bool(const DebugTypeStrPatch &LHS,
204                            const DebugTypeStrPatch &RHS)>
205             TypeStrPatchesComparator = [&](const DebugTypeStrPatch &LHS,
206                                            const DebugTypeStrPatch &RHS) {
207               return LHS.String->getKey() < RHS.String->getKey();
208             };
209         OutSection.ListDebugTypeStrPatch.sort(TypeStrPatchesComparator);
210       });
211     });
212   }
213 
214   if (!GlobalData.getOptions().AllowNonDeterministicOutput) {
215     // Sort patches to have a deterministic output.
216     TG.spawn([&]() {
217       forEach([&](SectionDescriptor &OutSection) {
218         std::function<bool(const DebugLineStrPatch &LHS,
219                            const DebugLineStrPatch &RHS)>
220             LineStrPatchesComparator = [&](const DebugLineStrPatch &LHS,
221                                            const DebugLineStrPatch &RHS) {
222               return LHS.String->getKey() < RHS.String->getKey();
223             };
224         OutSection.ListDebugLineStrPatch.sort(LineStrPatchesComparator);
225 
226         std::function<bool(const DebugTypeLineStrPatch &LHS,
227                            const DebugTypeLineStrPatch &RHS)>
228             TypeLineStrPatchesComparator =
229                 [&](const DebugTypeLineStrPatch &LHS,
230                     const DebugTypeLineStrPatch &RHS) {
231                   return LHS.String->getKey() < RHS.String->getKey();
232                 };
233         OutSection.ListDebugTypeLineStrPatch.sort(TypeLineStrPatchesComparator);
234       });
235     });
236   }
237 }
238 
finalizeTypeEntryRec(uint64_t OutOffset,DIE * OutDIE,TypeEntry * Entry)239 uint64_t TypeUnit::finalizeTypeEntryRec(uint64_t OutOffset, DIE *OutDIE,
240                                         TypeEntry *Entry) {
241   bool HasChildren = !Entry->getValue().load()->Children.empty();
242   DIEGenerator DIEGen(OutDIE, Types.getThreadLocalAllocator(), *this);
243   OutOffset += DIEGen.finalizeAbbreviations(HasChildren, nullptr);
244   OutOffset += OutDIE->getSize() - 1;
245 
246   if (HasChildren) {
247     Entry->getValue().load()->Children.forEach([&](TypeEntry *ChildEntry) {
248       DIE *ChildDIE = &ChildEntry->getValue().load()->getFinalDie();
249       DIEGen.addChild(ChildDIE);
250 
251       ChildDIE->setOffset(OutOffset);
252 
253       OutOffset = finalizeTypeEntryRec(OutOffset, ChildDIE, ChildEntry);
254     });
255 
256     // End of children marker.
257     OutOffset += sizeof(int8_t);
258   }
259 
260   OutDIE->setSize(OutOffset - OutDIE->getOffset());
261   return OutOffset;
262 }
263 
addFileNameIntoLinetable(StringEntry * Dir,StringEntry * FileName)264 uint32_t TypeUnit::addFileNameIntoLinetable(StringEntry *Dir,
265                                             StringEntry *FileName) {
266   uint32_t DirIdx = 0;
267 
268   if (Dir->first() == "") {
269     DirIdx = 0;
270   } else {
271     DirectoriesMapTy::iterator DirEntry = DirectoriesMap.find(Dir);
272     if (DirEntry == DirectoriesMap.end()) {
273       // We currently do not support more than UINT32_MAX directories.
274       assert(LineTable.Prologue.IncludeDirectories.size() < UINT32_MAX);
275       DirIdx = LineTable.Prologue.IncludeDirectories.size();
276       DirectoriesMap.insert({Dir, DirIdx});
277       LineTable.Prologue.IncludeDirectories.push_back(
278           DWARFFormValue::createFromPValue(dwarf::DW_FORM_string,
279                                            Dir->getKeyData()));
280     } else {
281       DirIdx = DirEntry->second;
282     }
283 
284     if (getVersion() < 5)
285       DirIdx++;
286   }
287 
288   auto [FileEntry, Inserted] = FileNamesMap.try_emplace(
289       {FileName, DirIdx}, LineTable.Prologue.FileNames.size());
290   if (Inserted) {
291     // We currently do not support more than UINT32_MAX files.
292     assert(LineTable.Prologue.FileNames.size() < UINT32_MAX);
293     LineTable.Prologue.FileNames.push_back(DWARFDebugLine::FileNameEntry());
294     LineTable.Prologue.FileNames.back().Name = DWARFFormValue::createFromPValue(
295         dwarf::DW_FORM_string, FileName->getKeyData());
296     LineTable.Prologue.FileNames.back().DirIdx = DirIdx;
297   }
298 
299   uint32_t FileIdx = FileEntry->second;
300   return getVersion() < 5 ? FileIdx + 1 : FileIdx;
301 }
302 
303 std::pair<dwarf::Form, uint8_t>
getScalarFormForValue(uint64_t Value) const304 TypeUnit::getScalarFormForValue(uint64_t Value) const {
305   if (Value > 0xFFFFFFFF)
306     return std::make_pair(dwarf::DW_FORM_data8, 8);
307 
308   if (Value > 0xFFFF)
309     return std::make_pair(dwarf::DW_FORM_data4, 4);
310 
311   if (Value > 0xFF)
312     return std::make_pair(dwarf::DW_FORM_data2, 2);
313 
314   return std::make_pair(dwarf::DW_FORM_data1, 1);
315 }
316 
getSizeByAttrForm(dwarf::Form Form) const317 uint8_t TypeUnit::getSizeByAttrForm(dwarf::Form Form) const {
318   if (Form == dwarf::DW_FORM_data1)
319     return 1;
320 
321   if (Form == dwarf::DW_FORM_data2)
322     return 2;
323 
324   if (Form == dwarf::DW_FORM_data4)
325     return 4;
326 
327   if (Form == dwarf::DW_FORM_data8)
328     return 8;
329 
330   if (Form == dwarf::DW_FORM_data16)
331     return 16;
332 
333   llvm_unreachable("Unsupported Attr Form");
334 }
335 
finishCloningAndEmit(const Triple & TargetTriple)336 Error TypeUnit::finishCloningAndEmit(const Triple &TargetTriple) {
337   BumpPtrAllocator Allocator;
338   createDIETree(Allocator);
339 
340   if (getOutUnitDIE() == nullptr)
341     return Error::success();
342 
343   // Create sections ahead so that they should not be created asynchronously
344   // later.
345   getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo);
346   getOrCreateSectionDescriptor(DebugSectionKind::DebugLine);
347   getOrCreateSectionDescriptor(DebugSectionKind::DebugStrOffsets);
348   getOrCreateSectionDescriptor(DebugSectionKind::DebugAbbrev);
349   if (llvm::is_contained(GlobalData.getOptions().AccelTables,
350                          DWARFLinker::AccelTableKind::Pub)) {
351     getOrCreateSectionDescriptor(DebugSectionKind::DebugPubNames);
352     getOrCreateSectionDescriptor(DebugSectionKind::DebugPubTypes);
353   }
354 
355   SmallVector<std::function<Error(void)>> Tasks;
356 
357   // Add task for emitting .debug_line section.
358   if (!LineTable.Prologue.FileNames.empty()) {
359     Tasks.push_back(
360         [&]() -> Error { return emitDebugLine(TargetTriple, LineTable); });
361   }
362 
363   // Add task for emitting .debug_info section.
364   Tasks.push_back([&]() -> Error { return emitDebugInfo(TargetTriple); });
365 
366   // Add task for emitting Pub accelerator sections.
367   if (llvm::is_contained(GlobalData.getOptions().AccelTables,
368                          DWARFLinker::AccelTableKind::Pub)) {
369     Tasks.push_back([&]() -> Error {
370       emitPubAccelerators();
371       return Error::success();
372     });
373   }
374 
375   // Add task for emitting .debug_str_offsets section.
376   Tasks.push_back([&]() -> Error { return emitDebugStringOffsetSection(); });
377 
378   // Add task for emitting .debug_abbr section.
379   Tasks.push_back([&]() -> Error { return emitAbbreviations(); });
380 
381   if (auto Err = parallelForEachError(
382           Tasks, [&](std::function<Error(void)> F) { return F(); }))
383     return Err;
384 
385   return Error::success();
386 }
387