1 //===- llvm/CodeGen/AsmPrinter/AccelTable.cpp - Accelerator Tables --------===//
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 // This file contains support for writing accelerator tables.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "llvm/CodeGen/AccelTable.h"
14 #include "DwarfCompileUnit.h"
15 #include "DwarfUnit.h"
16 #include "llvm/ADT/DenseSet.h"
17 #include "llvm/ADT/STLExtras.h"
18 #include "llvm/ADT/Twine.h"
19 #include "llvm/BinaryFormat/Dwarf.h"
20 #include "llvm/CodeGen/AsmPrinter.h"
21 #include "llvm/CodeGen/DIE.h"
22 #include "llvm/MC/MCStreamer.h"
23 #include "llvm/MC/MCSymbol.h"
24 #include "llvm/Support/raw_ostream.h"
25 #include "llvm/Target/TargetLoweringObjectFile.h"
26 #include <algorithm>
27 #include <cstddef>
28 #include <cstdint>
29 #include <limits>
30 #include <vector>
31 
32 using namespace llvm;
33 
computeBucketCount()34 void AccelTableBase::computeBucketCount() {
35   // First get the number of unique hashes.
36   std::vector<uint32_t> Uniques;
37   Uniques.reserve(Entries.size());
38   for (const auto &E : Entries)
39     Uniques.push_back(E.second.HashValue);
40   array_pod_sort(Uniques.begin(), Uniques.end());
41   std::vector<uint32_t>::iterator P =
42       std::unique(Uniques.begin(), Uniques.end());
43 
44   UniqueHashCount = std::distance(Uniques.begin(), P);
45 
46   if (UniqueHashCount > 1024)
47     BucketCount = UniqueHashCount / 4;
48   else if (UniqueHashCount > 16)
49     BucketCount = UniqueHashCount / 2;
50   else
51     BucketCount = std::max<uint32_t>(UniqueHashCount, 1);
52 }
53 
finalize(AsmPrinter * Asm,StringRef Prefix)54 void AccelTableBase::finalize(AsmPrinter *Asm, StringRef Prefix) {
55   // Create the individual hash data outputs.
56   for (auto &E : Entries) {
57     // Unique the entries.
58     llvm::stable_sort(E.second.Values,
59                       [](const AccelTableData *A, const AccelTableData *B) {
60                         return *A < *B;
61                       });
62     E.second.Values.erase(
63         std::unique(E.second.Values.begin(), E.second.Values.end()),
64         E.second.Values.end());
65   }
66 
67   // Figure out how many buckets we need, then compute the bucket contents and
68   // the final ordering. The hashes and offsets can be emitted by walking these
69   // data structures. We add temporary symbols to the data so they can be
70   // referenced when emitting the offsets.
71   computeBucketCount();
72 
73   // Compute bucket contents and final ordering.
74   Buckets.resize(BucketCount);
75   for (auto &E : Entries) {
76     uint32_t Bucket = E.second.HashValue % BucketCount;
77     Buckets[Bucket].push_back(&E.second);
78     E.second.Sym = Asm->createTempSymbol(Prefix);
79   }
80 
81   // Sort the contents of the buckets by hash value so that hash collisions end
82   // up together. Stable sort makes testing easier and doesn't cost much more.
83   for (auto &Bucket : Buckets)
84     llvm::stable_sort(Bucket, [](HashData *LHS, HashData *RHS) {
85       return LHS->HashValue < RHS->HashValue;
86     });
87 }
88 
89 namespace {
90 /// Base class for writing out Accelerator tables. It holds the common
91 /// functionality for the two Accelerator table types.
92 class AccelTableWriter {
93 protected:
94   AsmPrinter *const Asm;          ///< Destination.
95   const AccelTableBase &Contents; ///< Data to emit.
96 
97   /// Controls whether to emit duplicate hash and offset table entries for names
98   /// with identical hashes. Apple tables don't emit duplicate entries, DWARF v5
99   /// tables do.
100   const bool SkipIdenticalHashes;
101 
102   void emitHashes() const;
103 
104   /// Emit offsets to lists of entries with identical names. The offsets are
105   /// relative to the Base argument.
106   void emitOffsets(const MCSymbol *Base) const;
107 
108 public:
AccelTableWriter(AsmPrinter * Asm,const AccelTableBase & Contents,bool SkipIdenticalHashes)109   AccelTableWriter(AsmPrinter *Asm, const AccelTableBase &Contents,
110                    bool SkipIdenticalHashes)
111       : Asm(Asm), Contents(Contents), SkipIdenticalHashes(SkipIdenticalHashes) {
112   }
113 };
114 
115 class AppleAccelTableWriter : public AccelTableWriter {
116   using Atom = AppleAccelTableData::Atom;
117 
118   /// The fixed header of an Apple Accelerator Table.
119   struct Header {
120     uint32_t Magic = MagicHash;
121     uint16_t Version = 1;
122     uint16_t HashFunction = dwarf::DW_hash_function_djb;
123     uint32_t BucketCount;
124     uint32_t HashCount;
125     uint32_t HeaderDataLength;
126 
127     /// 'HASH' magic value to detect endianness.
128     static const uint32_t MagicHash = 0x48415348;
129 
Header__anonb3ca35f00311::AppleAccelTableWriter::Header130     Header(uint32_t BucketCount, uint32_t UniqueHashCount, uint32_t DataLength)
131         : BucketCount(BucketCount), HashCount(UniqueHashCount),
132           HeaderDataLength(DataLength) {}
133 
134     void emit(AsmPrinter *Asm) const;
135 #ifndef NDEBUG
136     void print(raw_ostream &OS) const;
dump__anonb3ca35f00311::AppleAccelTableWriter::Header137     void dump() const { print(dbgs()); }
138 #endif
139   };
140 
141   /// The HeaderData describes the structure of an Apple accelerator table
142   /// through a list of Atoms.
143   struct HeaderData {
144     /// In the case of data that is referenced via DW_FORM_ref_* the offset
145     /// base is used to describe the offset for all forms in the list of atoms.
146     uint32_t DieOffsetBase;
147 
148     const SmallVector<Atom, 4> Atoms;
149 
HeaderData__anonb3ca35f00311::AppleAccelTableWriter::HeaderData150     HeaderData(ArrayRef<Atom> AtomList, uint32_t Offset = 0)
151         : DieOffsetBase(Offset), Atoms(AtomList.begin(), AtomList.end()) {}
152 
153     void emit(AsmPrinter *Asm) const;
154 #ifndef NDEBUG
155     void print(raw_ostream &OS) const;
dump__anonb3ca35f00311::AppleAccelTableWriter::HeaderData156     void dump() const { print(dbgs()); }
157 #endif
158   };
159 
160   Header Header;
161   HeaderData HeaderData;
162   const MCSymbol *SecBegin;
163 
164   void emitBuckets() const;
165   void emitData() const;
166 
167 public:
AppleAccelTableWriter(AsmPrinter * Asm,const AccelTableBase & Contents,ArrayRef<Atom> Atoms,const MCSymbol * SecBegin)168   AppleAccelTableWriter(AsmPrinter *Asm, const AccelTableBase &Contents,
169                         ArrayRef<Atom> Atoms, const MCSymbol *SecBegin)
170       : AccelTableWriter(Asm, Contents, true),
171         Header(Contents.getBucketCount(), Contents.getUniqueHashCount(),
172                8 + (Atoms.size() * 4)),
173         HeaderData(Atoms), SecBegin(SecBegin) {}
174 
175   void emit() const;
176 
177 #ifndef NDEBUG
178   void print(raw_ostream &OS) const;
dump() const179   void dump() const { print(dbgs()); }
180 #endif
181 };
182 
183 /// Class responsible for emitting a DWARF v5 Accelerator Table. The only
184 /// public function is emit(), which performs the actual emission.
185 ///
186 /// A callback abstracts the logic to provide a CU index for a given entry.
187 class Dwarf5AccelTableWriter : public AccelTableWriter {
188   struct Header {
189     uint16_t Version = 5;
190     uint16_t Padding = 0;
191     uint32_t CompUnitCount;
192     uint32_t LocalTypeUnitCount = 0;
193     uint32_t ForeignTypeUnitCount = 0;
194     uint32_t BucketCount = 0;
195     uint32_t NameCount = 0;
196     uint32_t AbbrevTableSize = 0;
197     uint32_t AugmentationStringSize = sizeof(AugmentationString);
198     char AugmentationString[8] = {'L', 'L', 'V', 'M', '0', '7', '0', '0'};
199 
Header__anonb3ca35f00311::Dwarf5AccelTableWriter::Header200     Header(uint32_t CompUnitCount, uint32_t LocalTypeUnitCount,
201            uint32_t ForeignTypeUnitCount, uint32_t BucketCount,
202            uint32_t NameCount)
203         : CompUnitCount(CompUnitCount), LocalTypeUnitCount(LocalTypeUnitCount),
204           ForeignTypeUnitCount(ForeignTypeUnitCount), BucketCount(BucketCount),
205           NameCount(NameCount) {}
206 
207     void emit(Dwarf5AccelTableWriter &Ctx);
208   };
209 
210   Header Header;
211   DenseMap<uint32_t, SmallVector<DWARF5AccelTableData::AttributeEncoding, 3>>
212       Abbreviations;
213   ArrayRef<std::variant<MCSymbol *, uint64_t>> CompUnits;
214   ArrayRef<std::variant<MCSymbol *, uint64_t>> TypeUnits;
215   llvm::function_ref<std::optional<DWARF5AccelTable::UnitIndexAndEncoding>(
216       const DWARF5AccelTableData &)>
217       getIndexForEntry;
218   MCSymbol *ContributionEnd = nullptr;
219   MCSymbol *AbbrevStart = Asm->createTempSymbol("names_abbrev_start");
220   MCSymbol *AbbrevEnd = Asm->createTempSymbol("names_abbrev_end");
221   MCSymbol *EntryPool = Asm->createTempSymbol("names_entries");
222   // Indicates if this module is built with Split Dwarf enabled.
223   bool IsSplitDwarf = false;
224   /// Stores the DIE offsets which are indexed by this table.
225   DenseSet<OffsetAndUnitID> IndexedOffsets;
226 
227   void populateAbbrevsMap();
228 
229   void emitCUList() const;
230   void emitTUList() const;
231   void emitBuckets() const;
232   void emitStringOffsets() const;
233   void emitAbbrevs() const;
234   void emitEntry(
235       const DWARF5AccelTableData &Entry,
236       const DenseMap<OffsetAndUnitID, MCSymbol *> &DIEOffsetToAccelEntryLabel,
237       DenseSet<MCSymbol *> &EmittedAccelEntrySymbols) const;
238   void emitData();
239 
240 public:
241   Dwarf5AccelTableWriter(
242       AsmPrinter *Asm, const AccelTableBase &Contents,
243       ArrayRef<std::variant<MCSymbol *, uint64_t>> CompUnits,
244       ArrayRef<std::variant<MCSymbol *, uint64_t>> TypeUnits,
245       llvm::function_ref<std::optional<DWARF5AccelTable::UnitIndexAndEncoding>(
246           const DWARF5AccelTableData &)>
247           getIndexForEntry,
248       bool IsSplitDwarf);
249 
250   void emit();
251 };
252 } // namespace
253 
emitHashes() const254 void AccelTableWriter::emitHashes() const {
255   uint64_t PrevHash = std::numeric_limits<uint64_t>::max();
256   unsigned BucketIdx = 0;
257   for (const auto &Bucket : Contents.getBuckets()) {
258     for (const auto &Hash : Bucket) {
259       uint32_t HashValue = Hash->HashValue;
260       if (SkipIdenticalHashes && PrevHash == HashValue)
261         continue;
262       Asm->OutStreamer->AddComment("Hash in Bucket " + Twine(BucketIdx));
263       Asm->emitInt32(HashValue);
264       PrevHash = HashValue;
265     }
266     BucketIdx++;
267   }
268 }
269 
emitOffsets(const MCSymbol * Base) const270 void AccelTableWriter::emitOffsets(const MCSymbol *Base) const {
271   const auto &Buckets = Contents.getBuckets();
272   uint64_t PrevHash = std::numeric_limits<uint64_t>::max();
273   for (size_t i = 0, e = Buckets.size(); i < e; ++i) {
274     for (auto *Hash : Buckets[i]) {
275       uint32_t HashValue = Hash->HashValue;
276       if (SkipIdenticalHashes && PrevHash == HashValue)
277         continue;
278       PrevHash = HashValue;
279       Asm->OutStreamer->AddComment("Offset in Bucket " + Twine(i));
280       Asm->emitLabelDifference(Hash->Sym, Base, Asm->getDwarfOffsetByteSize());
281     }
282   }
283 }
284 
emit(AsmPrinter * Asm) const285 void AppleAccelTableWriter::Header::emit(AsmPrinter *Asm) const {
286   Asm->OutStreamer->AddComment("Header Magic");
287   Asm->emitInt32(Magic);
288   Asm->OutStreamer->AddComment("Header Version");
289   Asm->emitInt16(Version);
290   Asm->OutStreamer->AddComment("Header Hash Function");
291   Asm->emitInt16(HashFunction);
292   Asm->OutStreamer->AddComment("Header Bucket Count");
293   Asm->emitInt32(BucketCount);
294   Asm->OutStreamer->AddComment("Header Hash Count");
295   Asm->emitInt32(HashCount);
296   Asm->OutStreamer->AddComment("Header Data Length");
297   Asm->emitInt32(HeaderDataLength);
298 }
299 
emit(AsmPrinter * Asm) const300 void AppleAccelTableWriter::HeaderData::emit(AsmPrinter *Asm) const {
301   Asm->OutStreamer->AddComment("HeaderData Die Offset Base");
302   Asm->emitInt32(DieOffsetBase);
303   Asm->OutStreamer->AddComment("HeaderData Atom Count");
304   Asm->emitInt32(Atoms.size());
305 
306   for (const Atom &A : Atoms) {
307     Asm->OutStreamer->AddComment(dwarf::AtomTypeString(A.Type));
308     Asm->emitInt16(A.Type);
309     Asm->OutStreamer->AddComment(dwarf::FormEncodingString(A.Form));
310     Asm->emitInt16(A.Form);
311   }
312 }
313 
emitBuckets() const314 void AppleAccelTableWriter::emitBuckets() const {
315   const auto &Buckets = Contents.getBuckets();
316   unsigned index = 0;
317   for (size_t i = 0, e = Buckets.size(); i < e; ++i) {
318     Asm->OutStreamer->AddComment("Bucket " + Twine(i));
319     if (!Buckets[i].empty())
320       Asm->emitInt32(index);
321     else
322       Asm->emitInt32(std::numeric_limits<uint32_t>::max());
323     // Buckets point in the list of hashes, not to the data. Do not increment
324     // the index multiple times in case of hash collisions.
325     uint64_t PrevHash = std::numeric_limits<uint64_t>::max();
326     for (auto *HD : Buckets[i]) {
327       uint32_t HashValue = HD->HashValue;
328       if (PrevHash != HashValue)
329         ++index;
330       PrevHash = HashValue;
331     }
332   }
333 }
334 
emitData() const335 void AppleAccelTableWriter::emitData() const {
336   const auto &Buckets = Contents.getBuckets();
337   for (const AccelTableBase::HashList &Bucket : Buckets) {
338     uint64_t PrevHash = std::numeric_limits<uint64_t>::max();
339     for (const auto &Hash : Bucket) {
340       // Terminate the previous entry if there is no hash collision with the
341       // current one.
342       if (PrevHash != std::numeric_limits<uint64_t>::max() &&
343           PrevHash != Hash->HashValue)
344         Asm->emitInt32(0);
345       // Remember to emit the label for our offset.
346       Asm->OutStreamer->emitLabel(Hash->Sym);
347       Asm->OutStreamer->AddComment(Hash->Name.getString());
348       Asm->emitDwarfStringOffset(Hash->Name);
349       Asm->OutStreamer->AddComment("Num DIEs");
350       Asm->emitInt32(Hash->Values.size());
351       for (const auto *V : Hash->getValues<const AppleAccelTableData *>())
352         V->emit(Asm);
353       PrevHash = Hash->HashValue;
354     }
355     // Emit the final end marker for the bucket.
356     if (!Bucket.empty())
357       Asm->emitInt32(0);
358   }
359 }
360 
emit() const361 void AppleAccelTableWriter::emit() const {
362   Header.emit(Asm);
363   HeaderData.emit(Asm);
364   emitBuckets();
365   emitHashes();
366   emitOffsets(SecBegin);
367   emitData();
368 }
369 
DWARF5AccelTableData(const DIE & Die,const uint32_t UnitID,const bool IsTU)370 DWARF5AccelTableData::DWARF5AccelTableData(const DIE &Die,
371                                            const uint32_t UnitID,
372                                            const bool IsTU)
373     : OffsetVal(&Die), DieTag(Die.getTag()), UnitID(UnitID), IsTU(IsTU) {}
374 
emit(Dwarf5AccelTableWriter & Ctx)375 void Dwarf5AccelTableWriter::Header::emit(Dwarf5AccelTableWriter &Ctx) {
376   assert(CompUnitCount > 0 && "Index must have at least one CU.");
377 
378   AsmPrinter *Asm = Ctx.Asm;
379   Ctx.ContributionEnd =
380       Asm->emitDwarfUnitLength("names", "Header: unit length");
381   Asm->OutStreamer->AddComment("Header: version");
382   Asm->emitInt16(Version);
383   Asm->OutStreamer->AddComment("Header: padding");
384   Asm->emitInt16(Padding);
385   Asm->OutStreamer->AddComment("Header: compilation unit count");
386   Asm->emitInt32(CompUnitCount);
387   Asm->OutStreamer->AddComment("Header: local type unit count");
388   Asm->emitInt32(LocalTypeUnitCount);
389   Asm->OutStreamer->AddComment("Header: foreign type unit count");
390   Asm->emitInt32(ForeignTypeUnitCount);
391   Asm->OutStreamer->AddComment("Header: bucket count");
392   Asm->emitInt32(BucketCount);
393   Asm->OutStreamer->AddComment("Header: name count");
394   Asm->emitInt32(NameCount);
395   Asm->OutStreamer->AddComment("Header: abbreviation table size");
396   Asm->emitLabelDifference(Ctx.AbbrevEnd, Ctx.AbbrevStart, sizeof(uint32_t));
397   Asm->OutStreamer->AddComment("Header: augmentation string size");
398   assert(AugmentationStringSize % 4 == 0);
399   Asm->emitInt32(AugmentationStringSize);
400   Asm->OutStreamer->AddComment("Header: augmentation string");
401   Asm->OutStreamer->emitBytes({AugmentationString, AugmentationStringSize});
402 }
403 
404 std::optional<uint64_t>
getDefiningParentDieOffset(const DIE & Die)405 DWARF5AccelTableData::getDefiningParentDieOffset(const DIE &Die) {
406   if (auto *Parent = Die.getParent();
407       Parent && !Parent->findAttribute(dwarf::Attribute::DW_AT_declaration))
408     return Parent->getOffset();
409   return {};
410 }
411 
412 enum IdxParentEncoding : uint8_t {
413   NoIndexedParent = 0, /// Parent information present but parent isn't indexed.
414   Ref4 = 1,            /// Parent information present and parent is indexed.
415   NoParent = 2,        /// Parent information missing.
416 };
417 
418 static uint32_t constexpr NumBitsIdxParent = 2;
419 
encodeIdxParent(const std::optional<dwarf::Form> MaybeParentForm)420 uint8_t encodeIdxParent(const std::optional<dwarf::Form> MaybeParentForm) {
421   if (!MaybeParentForm)
422     return NoParent;
423   switch (*MaybeParentForm) {
424   case dwarf::Form::DW_FORM_flag_present:
425     return NoIndexedParent;
426   case dwarf::Form::DW_FORM_ref4:
427     return Ref4;
428   default:
429     // This is not crashing on bad input: we should only reach this if the
430     // internal compiler logic is faulty; see getFormForIdxParent.
431     llvm_unreachable("Bad form for IDX_parent");
432   }
433 }
434 
435 static uint32_t constexpr ParentBitOffset = dwarf::DW_IDX_type_hash;
436 static uint32_t constexpr TagBitOffset = ParentBitOffset + NumBitsIdxParent;
getTagFromAbbreviationTag(const uint32_t AbbrvTag)437 static uint32_t getTagFromAbbreviationTag(const uint32_t AbbrvTag) {
438   return AbbrvTag >> TagBitOffset;
439 }
440 
441 /// Constructs a unique AbbrevTag that captures what a DIE accesses.
442 /// Using this tag we can emit a unique abbreviation for each DIE.
constructAbbreviationTag(const unsigned Tag,const std::optional<DWARF5AccelTable::UnitIndexAndEncoding> & EntryRet,std::optional<dwarf::Form> MaybeParentForm)443 static uint32_t constructAbbreviationTag(
444     const unsigned Tag,
445     const std::optional<DWARF5AccelTable::UnitIndexAndEncoding> &EntryRet,
446     std::optional<dwarf::Form> MaybeParentForm) {
447   uint32_t AbbrvTag = 0;
448   if (EntryRet)
449     AbbrvTag |= 1 << EntryRet->Encoding.Index;
450   AbbrvTag |= 1 << dwarf::DW_IDX_die_offset;
451   AbbrvTag |= 1 << dwarf::DW_IDX_parent;
452   AbbrvTag |= encodeIdxParent(MaybeParentForm) << ParentBitOffset;
453   AbbrvTag |= Tag << TagBitOffset;
454   return AbbrvTag;
455 }
456 
457 static std::optional<dwarf::Form>
getFormForIdxParent(const DenseSet<OffsetAndUnitID> & IndexedOffsets,std::optional<OffsetAndUnitID> ParentOffset)458 getFormForIdxParent(const DenseSet<OffsetAndUnitID> &IndexedOffsets,
459                     std::optional<OffsetAndUnitID> ParentOffset) {
460   // No parent information
461   if (!ParentOffset)
462     return std::nullopt;
463   // Parent is indexed by this table.
464   if (IndexedOffsets.contains(*ParentOffset))
465     return dwarf::Form::DW_FORM_ref4;
466   // Parent is not indexed by this table.
467   return dwarf::Form::DW_FORM_flag_present;
468 }
469 
populateAbbrevsMap()470 void Dwarf5AccelTableWriter::populateAbbrevsMap() {
471   for (auto &Bucket : Contents.getBuckets()) {
472     for (auto *Hash : Bucket) {
473       for (auto *Value : Hash->getValues<DWARF5AccelTableData *>()) {
474         std::optional<DWARF5AccelTable::UnitIndexAndEncoding> EntryRet =
475             getIndexForEntry(*Value);
476         unsigned Tag = Value->getDieTag();
477         std::optional<dwarf::Form> MaybeParentForm = getFormForIdxParent(
478             IndexedOffsets, Value->getParentDieOffsetAndUnitID());
479         uint32_t AbbrvTag =
480             constructAbbreviationTag(Tag, EntryRet, MaybeParentForm);
481         if (Abbreviations.count(AbbrvTag) == 0) {
482           SmallVector<DWARF5AccelTableData::AttributeEncoding, 3> UA;
483           if (EntryRet)
484             UA.push_back(EntryRet->Encoding);
485           UA.push_back({dwarf::DW_IDX_die_offset, dwarf::DW_FORM_ref4});
486           if (MaybeParentForm)
487             UA.push_back({dwarf::DW_IDX_parent, *MaybeParentForm});
488           Abbreviations.try_emplace(AbbrvTag, UA);
489         }
490       }
491     }
492   }
493 }
494 
emitCUList() const495 void Dwarf5AccelTableWriter::emitCUList() const {
496   for (const auto &CU : enumerate(CompUnits)) {
497     Asm->OutStreamer->AddComment("Compilation unit " + Twine(CU.index()));
498     if (std::holds_alternative<MCSymbol *>(CU.value()))
499       Asm->emitDwarfSymbolReference(std::get<MCSymbol *>(CU.value()));
500     else
501       Asm->emitDwarfLengthOrOffset(std::get<uint64_t>(CU.value()));
502   }
503 }
504 
emitTUList() const505 void Dwarf5AccelTableWriter::emitTUList() const {
506   for (const auto &TU : enumerate(TypeUnits)) {
507     Asm->OutStreamer->AddComment("Type unit " + Twine(TU.index()));
508     if (std::holds_alternative<MCSymbol *>(TU.value()))
509       Asm->emitDwarfSymbolReference(std::get<MCSymbol *>(TU.value()));
510     else if (IsSplitDwarf)
511       Asm->emitInt64(std::get<uint64_t>(TU.value()));
512     else
513       Asm->emitDwarfLengthOrOffset(std::get<uint64_t>(TU.value()));
514   }
515 }
516 
emitBuckets() const517 void Dwarf5AccelTableWriter::emitBuckets() const {
518   uint32_t Index = 1;
519   for (const auto &Bucket : enumerate(Contents.getBuckets())) {
520     Asm->OutStreamer->AddComment("Bucket " + Twine(Bucket.index()));
521     Asm->emitInt32(Bucket.value().empty() ? 0 : Index);
522     Index += Bucket.value().size();
523   }
524 }
525 
emitStringOffsets() const526 void Dwarf5AccelTableWriter::emitStringOffsets() const {
527   for (const auto &Bucket : enumerate(Contents.getBuckets())) {
528     for (auto *Hash : Bucket.value()) {
529       DwarfStringPoolEntryRef String = Hash->Name;
530       Asm->OutStreamer->AddComment("String in Bucket " + Twine(Bucket.index()) +
531                                    ": " + String.getString());
532       Asm->emitDwarfStringOffset(String);
533     }
534   }
535 }
536 
emitAbbrevs() const537 void Dwarf5AccelTableWriter::emitAbbrevs() const {
538   Asm->OutStreamer->emitLabel(AbbrevStart);
539   for (const auto &Abbrev : Abbreviations) {
540     Asm->OutStreamer->AddComment("Abbrev code");
541     uint32_t Tag = getTagFromAbbreviationTag(Abbrev.first);
542     assert(Tag != 0);
543     Asm->emitULEB128(Abbrev.first);
544     Asm->OutStreamer->AddComment(dwarf::TagString(Tag));
545     Asm->emitULEB128(Tag);
546     for (const auto &AttrEnc : Abbrev.second) {
547       Asm->emitULEB128(AttrEnc.Index, dwarf::IndexString(AttrEnc.Index).data());
548       Asm->emitULEB128(AttrEnc.Form,
549                        dwarf::FormEncodingString(AttrEnc.Form).data());
550     }
551     Asm->emitULEB128(0, "End of abbrev");
552     Asm->emitULEB128(0, "End of abbrev");
553   }
554   Asm->emitULEB128(0, "End of abbrev list");
555   Asm->OutStreamer->emitLabel(AbbrevEnd);
556 }
557 
emitEntry(const DWARF5AccelTableData & Entry,const DenseMap<OffsetAndUnitID,MCSymbol * > & DIEOffsetToAccelEntryLabel,DenseSet<MCSymbol * > & EmittedAccelEntrySymbols) const558 void Dwarf5AccelTableWriter::emitEntry(
559     const DWARF5AccelTableData &Entry,
560     const DenseMap<OffsetAndUnitID, MCSymbol *> &DIEOffsetToAccelEntryLabel,
561     DenseSet<MCSymbol *> &EmittedAccelEntrySymbols) const {
562   std::optional<DWARF5AccelTable::UnitIndexAndEncoding> EntryRet =
563       getIndexForEntry(Entry);
564   std::optional<OffsetAndUnitID> MaybeParentOffset =
565       Entry.getParentDieOffsetAndUnitID();
566   std::optional<dwarf::Form> MaybeParentForm =
567       getFormForIdxParent(IndexedOffsets, MaybeParentOffset);
568   uint32_t AbbrvTag =
569       constructAbbreviationTag(Entry.getDieTag(), EntryRet, MaybeParentForm);
570   auto AbbrevIt = Abbreviations.find(AbbrvTag);
571   assert(AbbrevIt != Abbreviations.end() &&
572          "Why wasn't this abbrev generated?");
573   assert(getTagFromAbbreviationTag(AbbrevIt->first) == Entry.getDieTag() &&
574          "Invalid Tag");
575 
576   auto EntrySymbolIt =
577       DIEOffsetToAccelEntryLabel.find(Entry.getDieOffsetAndUnitID());
578   assert(EntrySymbolIt != DIEOffsetToAccelEntryLabel.end());
579   MCSymbol *EntrySymbol = EntrySymbolIt->getSecond();
580 
581   // Emit the label for this Entry, so that IDX_parents may refer to it.
582   // Note: a DIE may have multiple accelerator Entries; this check avoids
583   // creating/emitting multiple labels for the same DIE.
584   if (EmittedAccelEntrySymbols.insert(EntrySymbol).second)
585     Asm->OutStreamer->emitLabel(EntrySymbol);
586 
587   Asm->emitULEB128(AbbrevIt->first, "Abbreviation code");
588 
589   for (const auto &AttrEnc : AbbrevIt->second) {
590     Asm->OutStreamer->AddComment(dwarf::IndexString(AttrEnc.Index));
591     switch (AttrEnc.Index) {
592     case dwarf::DW_IDX_compile_unit:
593     case dwarf::DW_IDX_type_unit: {
594       DIEInteger ID(EntryRet->Index);
595       ID.emitValue(Asm, AttrEnc.Form);
596       break;
597     }
598     case dwarf::DW_IDX_die_offset:
599       assert(AttrEnc.Form == dwarf::DW_FORM_ref4);
600       Asm->emitInt32(Entry.getDieOffset());
601       break;
602     case dwarf::DW_IDX_parent: {
603       if (AttrEnc.Form == dwarf::Form::DW_FORM_flag_present)
604         break;
605       auto ParentSymbolIt = DIEOffsetToAccelEntryLabel.find(*MaybeParentOffset);
606       assert(ParentSymbolIt != DIEOffsetToAccelEntryLabel.end());
607       Asm->emitLabelDifference(ParentSymbolIt->getSecond(), EntryPool, 4);
608       break;
609     }
610     default:
611       llvm_unreachable("Unexpected index attribute!");
612     }
613   }
614 }
615 
emitData()616 void Dwarf5AccelTableWriter::emitData() {
617   DenseMap<OffsetAndUnitID, MCSymbol *> DIEOffsetToAccelEntryLabel;
618 
619   for (OffsetAndUnitID Offset : IndexedOffsets)
620     DIEOffsetToAccelEntryLabel.insert({Offset, Asm->createTempSymbol("")});
621 
622   Asm->OutStreamer->emitLabel(EntryPool);
623   DenseSet<MCSymbol *> EmittedAccelEntrySymbols;
624   for (auto &Bucket : Contents.getBuckets()) {
625     for (auto *Hash : Bucket) {
626       // Remember to emit the label for our offset.
627       Asm->OutStreamer->emitLabel(Hash->Sym);
628       for (const auto *Value : Hash->getValues<DWARF5AccelTableData *>())
629         emitEntry(*Value, DIEOffsetToAccelEntryLabel, EmittedAccelEntrySymbols);
630       Asm->OutStreamer->AddComment("End of list: " + Hash->Name.getString());
631       Asm->emitInt8(0);
632     }
633   }
634 }
635 
Dwarf5AccelTableWriter(AsmPrinter * Asm,const AccelTableBase & Contents,ArrayRef<std::variant<MCSymbol *,uint64_t>> CompUnits,ArrayRef<std::variant<MCSymbol *,uint64_t>> TypeUnits,llvm::function_ref<std::optional<DWARF5AccelTable::UnitIndexAndEncoding> (const DWARF5AccelTableData &)> getIndexForEntry,bool IsSplitDwarf)636 Dwarf5AccelTableWriter::Dwarf5AccelTableWriter(
637     AsmPrinter *Asm, const AccelTableBase &Contents,
638     ArrayRef<std::variant<MCSymbol *, uint64_t>> CompUnits,
639     ArrayRef<std::variant<MCSymbol *, uint64_t>> TypeUnits,
640     llvm::function_ref<std::optional<DWARF5AccelTable::UnitIndexAndEncoding>(
641         const DWARF5AccelTableData &)>
642         getIndexForEntry,
643     bool IsSplitDwarf)
644     : AccelTableWriter(Asm, Contents, false),
645       Header(CompUnits.size(), IsSplitDwarf ? 0 : TypeUnits.size(),
646              IsSplitDwarf ? TypeUnits.size() : 0, Contents.getBucketCount(),
647              Contents.getUniqueNameCount()),
648       CompUnits(CompUnits), TypeUnits(TypeUnits),
649       getIndexForEntry(std::move(getIndexForEntry)),
650       IsSplitDwarf(IsSplitDwarf) {
651 
652   for (auto &Bucket : Contents.getBuckets())
653     for (auto *Hash : Bucket)
654       for (auto *Value : Hash->getValues<DWARF5AccelTableData *>())
655         IndexedOffsets.insert(Value->getDieOffsetAndUnitID());
656 
657   populateAbbrevsMap();
658 }
659 
emit()660 void Dwarf5AccelTableWriter::emit() {
661   Header.emit(*this);
662   emitCUList();
663   emitTUList();
664   emitBuckets();
665   emitHashes();
666   emitStringOffsets();
667   emitOffsets(EntryPool);
668   emitAbbrevs();
669   emitData();
670   Asm->OutStreamer->emitValueToAlignment(Align(4), 0);
671   Asm->OutStreamer->emitLabel(ContributionEnd);
672 }
673 
emitAppleAccelTableImpl(AsmPrinter * Asm,AccelTableBase & Contents,StringRef Prefix,const MCSymbol * SecBegin,ArrayRef<AppleAccelTableData::Atom> Atoms)674 void llvm::emitAppleAccelTableImpl(AsmPrinter *Asm, AccelTableBase &Contents,
675                                    StringRef Prefix, const MCSymbol *SecBegin,
676                                    ArrayRef<AppleAccelTableData::Atom> Atoms) {
677   Contents.finalize(Asm, Prefix);
678   AppleAccelTableWriter(Asm, Contents, Atoms, SecBegin).emit();
679 }
680 
emitDWARF5AccelTable(AsmPrinter * Asm,DWARF5AccelTable & Contents,const DwarfDebug & DD,ArrayRef<std::unique_ptr<DwarfCompileUnit>> CUs)681 void llvm::emitDWARF5AccelTable(
682     AsmPrinter *Asm, DWARF5AccelTable &Contents, const DwarfDebug &DD,
683     ArrayRef<std::unique_ptr<DwarfCompileUnit>> CUs) {
684   TUVectorTy TUSymbols = Contents.getTypeUnitsSymbols();
685   std::vector<std::variant<MCSymbol *, uint64_t>> CompUnits;
686   std::vector<std::variant<MCSymbol *, uint64_t>> TypeUnits;
687   SmallVector<unsigned, 1> CUIndex(CUs.size());
688   DenseMap<unsigned, unsigned> TUIndex(TUSymbols.size());
689   int CUCount = 0;
690   int TUCount = 0;
691   for (const auto &CU : enumerate(CUs)) {
692     switch (CU.value()->getCUNode()->getNameTableKind()) {
693     case DICompileUnit::DebugNameTableKind::Default:
694     case DICompileUnit::DebugNameTableKind::Apple:
695       break;
696     default:
697       continue;
698     }
699     CUIndex[CU.index()] = CUCount++;
700     assert(CU.index() == CU.value()->getUniqueID());
701     const DwarfCompileUnit *MainCU =
702         DD.useSplitDwarf() ? CU.value()->getSkeleton() : CU.value().get();
703     CompUnits.push_back(MainCU->getLabelBegin());
704   }
705 
706   for (const auto &TU : TUSymbols) {
707     TUIndex[TU.UniqueID] = TUCount++;
708     if (DD.useSplitDwarf())
709       TypeUnits.push_back(std::get<uint64_t>(TU.LabelOrSignature));
710     else
711       TypeUnits.push_back(std::get<MCSymbol *>(TU.LabelOrSignature));
712   }
713 
714   if (CompUnits.empty())
715     return;
716 
717   Asm->OutStreamer->switchSection(
718       Asm->getObjFileLowering().getDwarfDebugNamesSection());
719 
720   Contents.finalize(Asm, "names");
721   dwarf::Form CUIndexForm =
722       DIEInteger::BestForm(/*IsSigned*/ false, CompUnits.size() - 1);
723   dwarf::Form TUIndexForm =
724       DIEInteger::BestForm(/*IsSigned*/ false, TypeUnits.size() - 1);
725   Dwarf5AccelTableWriter(
726       Asm, Contents, CompUnits, TypeUnits,
727       [&](const DWARF5AccelTableData &Entry)
728           -> std::optional<DWARF5AccelTable::UnitIndexAndEncoding> {
729         if (Entry.isTU())
730           return {{TUIndex[Entry.getUnitID()],
731                    {dwarf::DW_IDX_type_unit, TUIndexForm}}};
732         if (CUIndex.size() > 1)
733           return {{CUIndex[Entry.getUnitID()],
734                    {dwarf::DW_IDX_compile_unit, CUIndexForm}}};
735         return std::nullopt;
736       },
737       DD.useSplitDwarf())
738       .emit();
739 }
740 
addTypeUnitSymbol(DwarfTypeUnit & U)741 void DWARF5AccelTable::addTypeUnitSymbol(DwarfTypeUnit &U) {
742   TUSymbolsOrHashes.push_back({U.getLabelBegin(), U.getUniqueID()});
743 }
744 
addTypeUnitSignature(DwarfTypeUnit & U)745 void DWARF5AccelTable::addTypeUnitSignature(DwarfTypeUnit &U) {
746   TUSymbolsOrHashes.push_back({U.getTypeSignature(), U.getUniqueID()});
747 }
748 
emitDWARF5AccelTable(AsmPrinter * Asm,DWARF5AccelTable & Contents,ArrayRef<std::variant<MCSymbol *,uint64_t>> CUs,llvm::function_ref<std::optional<DWARF5AccelTable::UnitIndexAndEncoding> (const DWARF5AccelTableData &)> getIndexForEntry)749 void llvm::emitDWARF5AccelTable(
750     AsmPrinter *Asm, DWARF5AccelTable &Contents,
751     ArrayRef<std::variant<MCSymbol *, uint64_t>> CUs,
752     llvm::function_ref<std::optional<DWARF5AccelTable::UnitIndexAndEncoding>(
753         const DWARF5AccelTableData &)>
754         getIndexForEntry) {
755   std::vector<std::variant<MCSymbol *, uint64_t>> TypeUnits;
756   Contents.finalize(Asm, "names");
757   Dwarf5AccelTableWriter(Asm, Contents, CUs, TypeUnits, getIndexForEntry, false)
758       .emit();
759 }
760 
emit(AsmPrinter * Asm) const761 void AppleAccelTableOffsetData::emit(AsmPrinter *Asm) const {
762   assert(Die.getDebugSectionOffset() <= UINT32_MAX &&
763          "The section offset exceeds the limit.");
764   Asm->emitInt32(Die.getDebugSectionOffset());
765 }
766 
emit(AsmPrinter * Asm) const767 void AppleAccelTableTypeData::emit(AsmPrinter *Asm) const {
768   assert(Die.getDebugSectionOffset() <= UINT32_MAX &&
769          "The section offset exceeds the limit.");
770   Asm->emitInt32(Die.getDebugSectionOffset());
771   Asm->emitInt16(Die.getTag());
772   Asm->emitInt8(0);
773 }
774 
emit(AsmPrinter * Asm) const775 void AppleAccelTableStaticOffsetData::emit(AsmPrinter *Asm) const {
776   Asm->emitInt32(Offset);
777 }
778 
emit(AsmPrinter * Asm) const779 void AppleAccelTableStaticTypeData::emit(AsmPrinter *Asm) const {
780   Asm->emitInt32(Offset);
781   Asm->emitInt16(Tag);
782   Asm->emitInt8(ObjCClassIsImplementation ? dwarf::DW_FLAG_type_implementation
783                                           : 0);
784   Asm->emitInt32(QualifiedNameHash);
785 }
786 
787 constexpr AppleAccelTableData::Atom AppleAccelTableTypeData::Atoms[];
788 constexpr AppleAccelTableData::Atom AppleAccelTableOffsetData::Atoms[];
789 constexpr AppleAccelTableData::Atom AppleAccelTableStaticOffsetData::Atoms[];
790 constexpr AppleAccelTableData::Atom AppleAccelTableStaticTypeData::Atoms[];
791 
792 #ifndef NDEBUG
print(raw_ostream & OS) const793 void AppleAccelTableWriter::Header::print(raw_ostream &OS) const {
794   OS << "Magic: " << format("0x%x", Magic) << "\n"
795      << "Version: " << Version << "\n"
796      << "Hash Function: " << HashFunction << "\n"
797      << "Bucket Count: " << BucketCount << "\n"
798      << "Header Data Length: " << HeaderDataLength << "\n";
799 }
800 
print(raw_ostream & OS) const801 void AppleAccelTableData::Atom::print(raw_ostream &OS) const {
802   OS << "Type: " << dwarf::AtomTypeString(Type) << "\n"
803      << "Form: " << dwarf::FormEncodingString(Form) << "\n";
804 }
805 
print(raw_ostream & OS) const806 void AppleAccelTableWriter::HeaderData::print(raw_ostream &OS) const {
807   OS << "DIE Offset Base: " << DieOffsetBase << "\n";
808   for (auto Atom : Atoms)
809     Atom.print(OS);
810 }
811 
print(raw_ostream & OS) const812 void AppleAccelTableWriter::print(raw_ostream &OS) const {
813   Header.print(OS);
814   HeaderData.print(OS);
815   Contents.print(OS);
816   SecBegin->print(OS, nullptr);
817 }
818 
print(raw_ostream & OS) const819 void AccelTableBase::HashData::print(raw_ostream &OS) const {
820   OS << "Name: " << Name.getString() << "\n";
821   OS << "  Hash Value: " << format("0x%x", HashValue) << "\n";
822   OS << "  Symbol: ";
823   if (Sym)
824     OS << *Sym;
825   else
826     OS << "<none>";
827   OS << "\n";
828   for (auto *Value : Values)
829     Value->print(OS);
830 }
831 
print(raw_ostream & OS) const832 void AccelTableBase::print(raw_ostream &OS) const {
833   // Print Content.
834   OS << "Entries: \n";
835   for (const auto &[Name, Data] : Entries) {
836     OS << "Name: " << Name << "\n";
837     for (auto *V : Data.Values)
838       V->print(OS);
839   }
840 
841   OS << "Buckets and Hashes: \n";
842   for (const auto &Bucket : Buckets)
843     for (const auto &Hash : Bucket)
844       Hash->print(OS);
845 
846   OS << "Data: \n";
847   for (const auto &E : Entries)
848     E.second.print(OS);
849 }
850 
print(raw_ostream & OS) const851 void DWARF5AccelTableData::print(raw_ostream &OS) const {
852   OS << "  Offset: " << getDieOffset() << "\n";
853   OS << "  Tag: " << dwarf::TagString(getDieTag()) << "\n";
854 }
855 
print(raw_ostream & OS) const856 void AppleAccelTableOffsetData::print(raw_ostream &OS) const {
857   OS << "  Offset: " << Die.getOffset() << "\n";
858 }
859 
print(raw_ostream & OS) const860 void AppleAccelTableTypeData::print(raw_ostream &OS) const {
861   OS << "  Offset: " << Die.getOffset() << "\n";
862   OS << "  Tag: " << dwarf::TagString(Die.getTag()) << "\n";
863 }
864 
print(raw_ostream & OS) const865 void AppleAccelTableStaticOffsetData::print(raw_ostream &OS) const {
866   OS << "  Static Offset: " << Offset << "\n";
867 }
868 
print(raw_ostream & OS) const869 void AppleAccelTableStaticTypeData::print(raw_ostream &OS) const {
870   OS << "  Static Offset: " << Offset << "\n";
871   OS << "  QualifiedNameHash: " << format("%x\n", QualifiedNameHash) << "\n";
872   OS << "  Tag: " << dwarf::TagString(Tag) << "\n";
873   OS << "  ObjCClassIsImplementation: "
874      << (ObjCClassIsImplementation ? "true" : "false");
875   OS << "\n";
876 }
877 #endif
878