| //===- llvm/CodeGen/AsmPrinter/AccelTable.cpp - Accelerator Tables --------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file contains support for writing accelerator tables. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/CodeGen/AccelTable.h" | |
| #include "DwarfCompileUnit.h" | |
| #include "DwarfUnit.h" | |
| #include "llvm/ADT/DenseSet.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/ADT/Twine.h" | |
| #include "llvm/BinaryFormat/Dwarf.h" | |
| #include "llvm/CodeGen/AsmPrinter.h" | |
| #include "llvm/CodeGen/DIE.h" | |
| #include "llvm/MC/MCStreamer.h" | |
| #include "llvm/MC/MCSymbol.h" | |
| #include "llvm/Support/LEB128.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "llvm/Target/TargetLoweringObjectFile.h" | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <limits> | |
| #include <vector> | |
| using namespace llvm; | |
| void AccelTableBase::computeBucketCount() { | |
| SmallVector<uint32_t, 0> Uniques; | |
| Uniques.reserve(Entries.size()); | |
| for (const auto &E : Entries) | |
| Uniques.push_back(E.second.HashValue); | |
| llvm::sort(Uniques); | |
| UniqueHashCount = llvm::unique(Uniques) - Uniques.begin(); | |
| BucketCount = dwarf::getDebugNamesBucketCount(UniqueHashCount); | |
| } | |
| void AccelTableBase::finalize(AsmPrinter *Asm, StringRef Prefix) { | |
| // Create the individual hash data outputs. | |
| for (auto &E : Entries) { | |
| // Unique the entries. | |
| llvm::stable_sort(E.second.Values, | |
| [](const AccelTableData *A, const AccelTableData *B) { | |
| return *A < *B; | |
| }); | |
| E.second.Values.erase(llvm::unique(E.second.Values), E.second.Values.end()); | |
| } | |
| // Figure out how many buckets we need, then compute the bucket contents and | |
| // the final ordering. The hashes and offsets can be emitted by walking these | |
| // data structures. We add temporary symbols to the data so they can be | |
| // referenced when emitting the offsets. | |
| computeBucketCount(); | |
| // Compute bucket contents and final ordering. | |
| Buckets.resize(BucketCount); | |
| for (auto &E : Entries) | |
| Buckets[E.second.HashValue % BucketCount].push_back(&E.second); | |
| // Sort the contents of the buckets by hash value so that hash collisions end | |
| // up together. Entries is keyed by name, so breaking ties by name yields a | |
| // total order that does not depend on the order names were added in. | |
| for (HashList &Bucket : Buckets) | |
| llvm::sort(Bucket, [](const HashData *LHS, const HashData *RHS) { | |
| if (LHS->HashValue != RHS->HashValue) | |
| return LHS->HashValue < RHS->HashValue; | |
| return LHS->Name.getString() < RHS->Name.getString(); | |
| }); | |
| // Create the labels in bucket order so that their numbering matches the | |
| // order they are emitted in. | |
| for (HashList &Bucket : Buckets) | |
| for (HashData *Hash : Bucket) | |
| Hash->Sym = Asm->createTempSymbol(Prefix); | |
| } | |
| namespace { | |
| /// Base class for writing out Accelerator tables. It holds the common | |
| /// functionality for the two Accelerator table types. | |
| class AccelTableWriter { | |
| protected: | |
| AsmPrinter *const Asm; ///< Destination. | |
| const AccelTableBase &Contents; ///< Data to emit. | |
| /// Controls whether to emit duplicate hash and offset table entries for names | |
| /// with identical hashes. Apple tables don't emit duplicate entries, DWARF v5 | |
| /// tables do. | |
| const bool SkipIdenticalHashes; | |
| void emitHashes() const; | |
| /// Emit offsets to lists of entries with identical names. The offsets are | |
| /// relative to the Base argument. | |
| void emitOffsets(const MCSymbol *Base) const; | |
| public: | |
| AccelTableWriter(AsmPrinter *Asm, const AccelTableBase &Contents, | |
| bool SkipIdenticalHashes) | |
| : Asm(Asm), Contents(Contents), SkipIdenticalHashes(SkipIdenticalHashes) { | |
| } | |
| }; | |
| class AppleAccelTableWriter : public AccelTableWriter { | |
| using Atom = AppleAccelTableData::Atom; | |
| /// The fixed header of an Apple Accelerator Table. | |
| struct Header { | |
| uint32_t Magic = MagicHash; | |
| uint16_t Version = 1; | |
| uint16_t HashFunction = dwarf::DW_hash_function_djb; | |
| uint32_t BucketCount; | |
| uint32_t HashCount; | |
| uint32_t HeaderDataLength; | |
| /// 'HASH' magic value to detect endianness. | |
| static const uint32_t MagicHash = 0x48415348; | |
| Header(uint32_t BucketCount, uint32_t UniqueHashCount, uint32_t DataLength) | |
| : BucketCount(BucketCount), HashCount(UniqueHashCount), | |
| HeaderDataLength(DataLength) {} | |
| void emit(AsmPrinter *Asm) const; | |
| #ifndef NDEBUG | |
| void print(raw_ostream &OS) const; | |
| void dump() const { print(dbgs()); } | |
| #endif | |
| }; | |
| /// The HeaderData describes the structure of an Apple accelerator table | |
| /// through a list of Atoms. | |
| struct HeaderData { | |
| /// In the case of data that is referenced via DW_FORM_ref_* the offset | |
| /// base is used to describe the offset for all forms in the list of atoms. | |
| uint32_t DieOffsetBase; | |
| const SmallVector<Atom, 4> Atoms; | |
| HeaderData(ArrayRef<Atom> AtomList, uint32_t Offset = 0) | |
| : DieOffsetBase(Offset), Atoms(AtomList) {} | |
| void emit(AsmPrinter *Asm) const; | |
| #ifndef NDEBUG | |
| void print(raw_ostream &OS) const; | |
| void dump() const { print(dbgs()); } | |
| #endif | |
| }; | |
| Header Header; | |
| HeaderData HeaderData; | |
| const MCSymbol *SecBegin; | |
| void emitBuckets() const; | |
| void emitData() const; | |
| public: | |
| AppleAccelTableWriter(AsmPrinter *Asm, const AccelTableBase &Contents, | |
| ArrayRef<Atom> Atoms, const MCSymbol *SecBegin) | |
| : AccelTableWriter(Asm, Contents, true), | |
| Header(Contents.getBucketCount(), Contents.getUniqueHashCount(), | |
| 8 + (Atoms.size() * 4)), | |
| HeaderData(Atoms), SecBegin(SecBegin) {} | |
| void emit() const; | |
| #ifndef NDEBUG | |
| void print(raw_ostream &OS) const; | |
| void dump() const { print(dbgs()); } | |
| #endif | |
| }; | |
| /// Class responsible for emitting a DWARF v5 Accelerator Table. The only | |
| /// public function is emit(), which performs the actual emission. | |
| /// | |
| /// A callback abstracts the logic to provide a CU index for a given entry. | |
| class Dwarf5AccelTableWriter : public AccelTableWriter { | |
| struct Header { | |
| uint16_t Version = 5; | |
| uint16_t Padding = 0; | |
| uint32_t CompUnitCount; | |
| uint32_t LocalTypeUnitCount = 0; | |
| uint32_t ForeignTypeUnitCount = 0; | |
| uint32_t BucketCount = 0; | |
| uint32_t NameCount = 0; | |
| uint32_t AugmentationStringSize = sizeof(AugmentationString); | |
| char AugmentationString[8] = {'L', 'L', 'V', 'M', '0', '7', '0', '0'}; | |
| Header(uint32_t CompUnitCount, uint32_t LocalTypeUnitCount, | |
| uint32_t ForeignTypeUnitCount, uint32_t BucketCount, | |
| uint32_t NameCount) | |
| : CompUnitCount(CompUnitCount), LocalTypeUnitCount(LocalTypeUnitCount), | |
| ForeignTypeUnitCount(ForeignTypeUnitCount), BucketCount(BucketCount), | |
| NameCount(NameCount) {} | |
| void emit(Dwarf5AccelTableWriter &Ctx); | |
| }; | |
| Header Header; | |
| /// FoldingSet that uniques the abbreviations. | |
| FoldingSet<DebugNamesAbbrev> AbbreviationsSet; | |
| /// Vector containing DebugNames abbreviations for iteration in order. | |
| SmallVector<DebugNamesAbbrev *, 5> AbbreviationsVector; | |
| /// The bump allocator to use when creating DIEAbbrev objects in the uniqued | |
| /// storage container. | |
| BumpPtrAllocator Alloc; | |
| ArrayRef<std::variant<MCSymbol *, uint64_t>> CompUnits; | |
| ArrayRef<std::variant<MCSymbol *, uint64_t>> TypeUnits; | |
| llvm::function_ref<std::optional<DWARF5AccelTable::UnitIndexAndEncoding>( | |
| const DWARF5AccelTableData &)> | |
| getIndexForEntry; | |
| MCSymbol *ContributionEnd = nullptr; | |
| MCSymbol *AbbrevStart = Asm->createTempSymbol("names_abbrev_start"); | |
| MCSymbol *AbbrevEnd = Asm->createTempSymbol("names_abbrev_end"); | |
| MCSymbol *EntryPool = Asm->createTempSymbol("names_entries"); | |
| // Indicates if this module is built with Split Dwarf enabled. | |
| bool IsSplitDwarf = false; | |
| /// Stores the DIE offsets which are indexed by this table. | |
| DenseSet<OffsetAndUnitID> IndexedOffsets; | |
| void populateAbbrevsMap(); | |
| void emitCUList() const; | |
| void emitTUList() const; | |
| void emitBuckets() const; | |
| void emitStringOffsets() const; | |
| void emitAbbrevs() const; | |
| void emitEntry( | |
| const DWARF5AccelTableData &Entry, | |
| const DenseMap<OffsetAndUnitID, uint64_t> &DIEOffsetToAccelEntryOffset); | |
| uint64_t getEntrySize(const DWARF5AccelTableData &Entry) const; | |
| void emitData(); | |
| public: | |
| 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); | |
| ~Dwarf5AccelTableWriter() { | |
| for (DebugNamesAbbrev *Abbrev : AbbreviationsVector) | |
| Abbrev->~DebugNamesAbbrev(); | |
| } | |
| void emit(); | |
| }; | |
| } // namespace | |
| void AccelTableWriter::emitHashes() const { | |
| uint64_t PrevHash = std::numeric_limits<uint64_t>::max(); | |
| unsigned BucketIdx = 0; | |
| for (const auto &Bucket : Contents.getBuckets()) { | |
| for (const auto &Hash : Bucket) { | |
| uint32_t HashValue = Hash->HashValue; | |
| if (SkipIdenticalHashes && PrevHash == HashValue) | |
| continue; | |
| Asm->OutStreamer->AddComment("Hash in Bucket " + Twine(BucketIdx)); | |
| Asm->emitInt32(HashValue); | |
| PrevHash = HashValue; | |
| } | |
| BucketIdx++; | |
| } | |
| } | |
| void AccelTableWriter::emitOffsets(const MCSymbol *Base) const { | |
| const auto &Buckets = Contents.getBuckets(); | |
| uint64_t PrevHash = std::numeric_limits<uint64_t>::max(); | |
| for (size_t i = 0, e = Buckets.size(); i < e; ++i) { | |
| for (auto *Hash : Buckets[i]) { | |
| uint32_t HashValue = Hash->HashValue; | |
| if (SkipIdenticalHashes && PrevHash == HashValue) | |
| continue; | |
| PrevHash = HashValue; | |
| Asm->OutStreamer->AddComment("Offset in Bucket " + Twine(i)); | |
| Asm->emitLabelDifference(Hash->Sym, Base, Asm->getDwarfOffsetByteSize()); | |
| } | |
| } | |
| } | |
| void AppleAccelTableWriter::Header::emit(AsmPrinter *Asm) const { | |
| Asm->OutStreamer->AddComment("Header Magic"); | |
| Asm->emitInt32(Magic); | |
| Asm->OutStreamer->AddComment("Header Version"); | |
| Asm->emitInt16(Version); | |
| Asm->OutStreamer->AddComment("Header Hash Function"); | |
| Asm->emitInt16(HashFunction); | |
| Asm->OutStreamer->AddComment("Header Bucket Count"); | |
| Asm->emitInt32(BucketCount); | |
| Asm->OutStreamer->AddComment("Header Hash Count"); | |
| Asm->emitInt32(HashCount); | |
| Asm->OutStreamer->AddComment("Header Data Length"); | |
| Asm->emitInt32(HeaderDataLength); | |
| } | |
| void AppleAccelTableWriter::HeaderData::emit(AsmPrinter *Asm) const { | |
| Asm->OutStreamer->AddComment("HeaderData Die Offset Base"); | |
| Asm->emitInt32(DieOffsetBase); | |
| Asm->OutStreamer->AddComment("HeaderData Atom Count"); | |
| Asm->emitInt32(Atoms.size()); | |
| for (const Atom &A : Atoms) { | |
| Asm->OutStreamer->AddComment(dwarf::AtomTypeString(A.Type)); | |
| Asm->emitInt16(A.Type); | |
| Asm->OutStreamer->AddComment(dwarf::FormEncodingString(A.Form)); | |
| Asm->emitInt16(A.Form); | |
| } | |
| } | |
| void AppleAccelTableWriter::emitBuckets() const { | |
| const auto &Buckets = Contents.getBuckets(); | |
| unsigned index = 0; | |
| for (size_t i = 0, e = Buckets.size(); i < e; ++i) { | |
| Asm->OutStreamer->AddComment("Bucket " + Twine(i)); | |
| if (!Buckets[i].empty()) | |
| Asm->emitInt32(index); | |
| else | |
| Asm->emitInt32(std::numeric_limits<uint32_t>::max()); | |
| // Buckets point in the list of hashes, not to the data. Do not increment | |
| // the index multiple times in case of hash collisions. | |
| uint64_t PrevHash = std::numeric_limits<uint64_t>::max(); | |
| for (auto *HD : Buckets[i]) { | |
| uint32_t HashValue = HD->HashValue; | |
| if (PrevHash != HashValue) | |
| ++index; | |
| PrevHash = HashValue; | |
| } | |
| } | |
| } | |
| void AppleAccelTableWriter::emitData() const { | |
| const auto &Buckets = Contents.getBuckets(); | |
| for (const AccelTableBase::HashList &Bucket : Buckets) { | |
| uint64_t PrevHash = std::numeric_limits<uint64_t>::max(); | |
| for (const auto &Hash : Bucket) { | |
| // Terminate the previous entry if there is no hash collision with the | |
| // current one. | |
| if (PrevHash != std::numeric_limits<uint64_t>::max() && | |
| PrevHash != Hash->HashValue) | |
| Asm->emitInt32(0); | |
| // Remember to emit the label for our offset. | |
| Asm->OutStreamer->emitLabel(Hash->Sym); | |
| Asm->OutStreamer->AddComment(Hash->Name.getString()); | |
| Asm->emitDwarfStringOffset(Hash->Name); | |
| Asm->OutStreamer->AddComment("Num DIEs"); | |
| Asm->emitInt32(Hash->Values.size()); | |
| for (const auto *V : Hash->getValues<const AppleAccelTableData *>()) | |
| V->emit(Asm); | |
| PrevHash = Hash->HashValue; | |
| } | |
| // Emit the final end marker for the bucket. | |
| if (!Bucket.empty()) | |
| Asm->emitInt32(0); | |
| } | |
| } | |
| void AppleAccelTableWriter::emit() const { | |
| Header.emit(Asm); | |
| HeaderData.emit(Asm); | |
| emitBuckets(); | |
| emitHashes(); | |
| emitOffsets(SecBegin); | |
| emitData(); | |
| } | |
| DWARF5AccelTableData::DWARF5AccelTableData(const DIE &Die, | |
| const uint32_t UnitID, | |
| const bool IsTU) | |
| : OffsetVal(&Die), DieTag(Die.getTag()), AbbrevNumber(0), IsTU(IsTU), | |
| UnitID(UnitID) {} | |
| void Dwarf5AccelTableWriter::Header::emit(Dwarf5AccelTableWriter &Ctx) { | |
| assert(CompUnitCount > 0 && "Index must have at least one CU."); | |
| AsmPrinter *Asm = Ctx.Asm; | |
| Ctx.ContributionEnd = | |
| Asm->emitDwarfUnitLength("names", "Header: unit length"); | |
| Asm->OutStreamer->AddComment("Header: version"); | |
| Asm->emitInt16(Version); | |
| Asm->OutStreamer->AddComment("Header: padding"); | |
| Asm->emitInt16(Padding); | |
| Asm->OutStreamer->AddComment("Header: compilation unit count"); | |
| Asm->emitInt32(CompUnitCount); | |
| Asm->OutStreamer->AddComment("Header: local type unit count"); | |
| Asm->emitInt32(LocalTypeUnitCount); | |
| Asm->OutStreamer->AddComment("Header: foreign type unit count"); | |
| Asm->emitInt32(ForeignTypeUnitCount); | |
| Asm->OutStreamer->AddComment("Header: bucket count"); | |
| Asm->emitInt32(BucketCount); | |
| Asm->OutStreamer->AddComment("Header: name count"); | |
| Asm->emitInt32(NameCount); | |
| Asm->OutStreamer->AddComment("Header: abbreviation table size"); | |
| Asm->emitLabelDifference(Ctx.AbbrevEnd, Ctx.AbbrevStart, sizeof(uint32_t)); | |
| Asm->OutStreamer->AddComment("Header: augmentation string size"); | |
| assert(AugmentationStringSize % 4 == 0); | |
| Asm->emitInt32(AugmentationStringSize); | |
| Asm->OutStreamer->AddComment("Header: augmentation string"); | |
| Asm->OutStreamer->emitBytes({AugmentationString, AugmentationStringSize}); | |
| } | |
| std::optional<uint64_t> | |
| DWARF5AccelTableData::getDefiningParentDieOffset(const DIE &Die) { | |
| if (auto *Parent = Die.getParent(); | |
| Parent && !Parent->findAttribute(dwarf::Attribute::DW_AT_declaration)) | |
| return Parent->getOffset(); | |
| return {}; | |
| } | |
| static std::optional<dwarf::Form> | |
| getFormForIdxParent(const DenseSet<OffsetAndUnitID> &IndexedOffsets, | |
| std::optional<OffsetAndUnitID> ParentOffset) { | |
| // No parent information | |
| if (!ParentOffset) | |
| return std::nullopt; | |
| // Parent is indexed by this table. | |
| if (IndexedOffsets.contains(*ParentOffset)) | |
| return dwarf::Form::DW_FORM_ref4; | |
| // Parent is not indexed by this table. | |
| return dwarf::Form::DW_FORM_flag_present; | |
| } | |
| void DebugNamesAbbrev::Profile(FoldingSetNodeID &ID) const { | |
| ID.AddInteger(DieTag); | |
| for (const DebugNamesAbbrev::AttributeEncoding &Enc : AttrVect) { | |
| ID.AddInteger(Enc.Index); | |
| ID.AddInteger(Enc.Form); | |
| } | |
| } | |
| void Dwarf5AccelTableWriter::populateAbbrevsMap() { | |
| for (auto &Bucket : Contents.getBuckets()) { | |
| for (auto *Hash : Bucket) { | |
| for (auto *Value : Hash->getValues<DWARF5AccelTableData *>()) { | |
| std::optional<DWARF5AccelTable::UnitIndexAndEncoding> EntryRet = | |
| getIndexForEntry(*Value); | |
| std::optional<dwarf::Form> MaybeParentForm = getFormForIdxParent( | |
| IndexedOffsets, Value->getParentDieOffsetAndUnitID()); | |
| DebugNamesAbbrev Abbrev(Value->getDieTag()); | |
| if (EntryRet) | |
| Abbrev.addAttribute(EntryRet->Encoding); | |
| Abbrev.addAttribute({dwarf::DW_IDX_die_offset, dwarf::DW_FORM_ref4}); | |
| if (MaybeParentForm) | |
| Abbrev.addAttribute({dwarf::DW_IDX_parent, *MaybeParentForm}); | |
| FoldingSetNodeID ID; | |
| Abbrev.Profile(ID); | |
| FoldingSetInsertToken Token; | |
| if (DebugNamesAbbrev *Existing = AbbreviationsSet.lookup(ID, Token)) { | |
| Value->setAbbrevNumber(Existing->getNumber()); | |
| continue; | |
| } | |
| DebugNamesAbbrev *NewAbbrev = | |
| new (Alloc) DebugNamesAbbrev(std::move(Abbrev)); | |
| AbbreviationsVector.push_back(NewAbbrev); | |
| NewAbbrev->setNumber(AbbreviationsVector.size()); | |
| AbbreviationsSet.insert(NewAbbrev, Token); | |
| Value->setAbbrevNumber(NewAbbrev->getNumber()); | |
| } | |
| } | |
| } | |
| } | |
| void Dwarf5AccelTableWriter::emitCUList() const { | |
| for (const auto &CU : enumerate(CompUnits)) { | |
| Asm->OutStreamer->AddComment("Compilation unit " + Twine(CU.index())); | |
| if (std::holds_alternative<MCSymbol *>(CU.value())) | |
| Asm->emitDwarfSymbolReference(std::get<MCSymbol *>(CU.value())); | |
| else | |
| Asm->emitDwarfLengthOrOffset(std::get<uint64_t>(CU.value())); | |
| } | |
| } | |
| void Dwarf5AccelTableWriter::emitTUList() const { | |
| for (const auto &TU : enumerate(TypeUnits)) { | |
| Asm->OutStreamer->AddComment("Type unit " + Twine(TU.index())); | |
| if (std::holds_alternative<MCSymbol *>(TU.value())) | |
| Asm->emitDwarfSymbolReference(std::get<MCSymbol *>(TU.value())); | |
| else if (IsSplitDwarf) | |
| Asm->emitInt64(std::get<uint64_t>(TU.value())); | |
| else | |
| Asm->emitDwarfLengthOrOffset(std::get<uint64_t>(TU.value())); | |
| } | |
| } | |
| void Dwarf5AccelTableWriter::emitBuckets() const { | |
| uint32_t Index = 1; | |
| for (const auto &Bucket : enumerate(Contents.getBuckets())) { | |
| Asm->OutStreamer->AddComment("Bucket " + Twine(Bucket.index())); | |
| Asm->emitInt32(Bucket.value().empty() ? 0 : Index); | |
| Index += Bucket.value().size(); | |
| } | |
| } | |
| void Dwarf5AccelTableWriter::emitStringOffsets() const { | |
| for (const auto &Bucket : enumerate(Contents.getBuckets())) { | |
| for (auto *Hash : Bucket.value()) { | |
| DwarfStringPoolEntryRef String = Hash->Name; | |
| Asm->OutStreamer->AddComment("String in Bucket " + Twine(Bucket.index()) + | |
| ": " + String.getString()); | |
| Asm->emitDwarfStringOffset(String); | |
| } | |
| } | |
| } | |
| void Dwarf5AccelTableWriter::emitAbbrevs() const { | |
| Asm->OutStreamer->emitLabel(AbbrevStart); | |
| for (const DebugNamesAbbrev *Abbrev : AbbreviationsVector) { | |
| Asm->OutStreamer->AddComment("Abbrev code"); | |
| Asm->emitULEB128(Abbrev->getNumber()); | |
| Asm->OutStreamer->AddComment(dwarf::TagString(Abbrev->getDieTag())); | |
| Asm->emitULEB128(Abbrev->getDieTag()); | |
| for (const DebugNamesAbbrev::AttributeEncoding &AttrEnc : | |
| Abbrev->getAttributes()) { | |
| Asm->emitULEB128(AttrEnc.Index, dwarf::IndexString(AttrEnc.Index).data()); | |
| Asm->emitULEB128(AttrEnc.Form, | |
| dwarf::FormEncodingString(AttrEnc.Form).data()); | |
| } | |
| Asm->emitULEB128(0, "End of abbrev"); | |
| Asm->emitULEB128(0, "End of abbrev"); | |
| } | |
| Asm->emitULEB128(0, "End of abbrev list"); | |
| Asm->OutStreamer->emitLabel(AbbrevEnd); | |
| } | |
| void Dwarf5AccelTableWriter::emitEntry( | |
| const DWARF5AccelTableData &Entry, | |
| const DenseMap<OffsetAndUnitID, uint64_t> &DIEOffsetToAccelEntryOffset) { | |
| unsigned AbbrevIndex = Entry.getAbbrevNumber() - 1; | |
| assert(AbbrevIndex < AbbreviationsVector.size() && | |
| "Entry abbrev index is outside of abbreviations vector range."); | |
| DebugNamesAbbrev *Abbrev = AbbreviationsVector[AbbrevIndex]; | |
| std::optional<DWARF5AccelTable::UnitIndexAndEncoding> EntryRet = | |
| getIndexForEntry(Entry); | |
| std::optional<OffsetAndUnitID> MaybeParentOffset = | |
| Entry.getParentDieOffsetAndUnitID(); | |
| Asm->emitULEB128(Entry.getAbbrevNumber(), "Abbreviation code"); | |
| for (const DebugNamesAbbrev::AttributeEncoding &AttrEnc : | |
| Abbrev->getAttributes()) { | |
| Asm->OutStreamer->AddComment(dwarf::IndexString(AttrEnc.Index)); | |
| switch (AttrEnc.Index) { | |
| case dwarf::DW_IDX_compile_unit: | |
| case dwarf::DW_IDX_type_unit: { | |
| DIEInteger ID(EntryRet->Index); | |
| ID.emitValue(Asm, AttrEnc.Form); | |
| break; | |
| } | |
| case dwarf::DW_IDX_die_offset: | |
| assert(AttrEnc.Form == dwarf::DW_FORM_ref4); | |
| Asm->emitInt32(Entry.getDieOffset()); | |
| break; | |
| case dwarf::DW_IDX_parent: { | |
| if (AttrEnc.Form == dwarf::Form::DW_FORM_flag_present) | |
| break; | |
| auto It = DIEOffsetToAccelEntryOffset.find(*MaybeParentOffset); | |
| assert(It != DIEOffsetToAccelEntryOffset.end()); | |
| Asm->emitInt32(It->second); | |
| break; | |
| } | |
| default: | |
| llvm_unreachable("Unexpected index attribute!"); | |
| } | |
| } | |
| } | |
| uint64_t | |
| Dwarf5AccelTableWriter::getEntrySize(const DWARF5AccelTableData &Entry) const { | |
| unsigned AbbrevIndex = Entry.getAbbrevNumber() - 1; | |
| assert(AbbrevIndex < AbbreviationsVector.size()); | |
| DebugNamesAbbrev *Abbrev = AbbreviationsVector[AbbrevIndex]; | |
| uint64_t Size = getULEB128Size(Entry.getAbbrevNumber()); | |
| std::optional<DWARF5AccelTable::UnitIndexAndEncoding> EntryRet = | |
| getIndexForEntry(Entry); | |
| for (const auto &AttrEnc : Abbrev->getAttributes()) { | |
| switch (AttrEnc.Index) { | |
| case dwarf::DW_IDX_compile_unit: | |
| case dwarf::DW_IDX_type_unit: | |
| Size += DIEInteger(EntryRet->Index) | |
| .sizeOf(Asm->getDwarfFormParams(), AttrEnc.Form); | |
| break; | |
| case dwarf::DW_IDX_die_offset: | |
| Size += 4; | |
| break; | |
| case dwarf::DW_IDX_parent: | |
| if (AttrEnc.Form != dwarf::Form::DW_FORM_flag_present) | |
| Size += 4; | |
| break; | |
| default: | |
| llvm_unreachable("Unexpected index attribute!"); | |
| } | |
| } | |
| return Size; | |
| } | |
| void Dwarf5AccelTableWriter::emitData() { | |
| // Pre-compute entry pool offsets for DW_IDX_parent references. | |
| DenseMap<OffsetAndUnitID, uint64_t> DIEOffsetToAccelEntryOffset; | |
| uint64_t Offset = 0; | |
| for (auto &Bucket : Contents.getBuckets()) { | |
| for (auto *Hash : Bucket) { | |
| for (const auto *Value : Hash->getValues<DWARF5AccelTableData *>()) { | |
| DIEOffsetToAccelEntryOffset.try_emplace(Value->getDieOffsetAndUnitID(), | |
| Offset); | |
| Offset += getEntrySize(*Value); | |
| } | |
| Offset += 1; // End of list | |
| } | |
| } | |
| Asm->OutStreamer->emitLabel(EntryPool); | |
| for (auto &Bucket : Contents.getBuckets()) { | |
| for (auto *Hash : Bucket) { | |
| // Remember to emit the label for our offset. | |
| Asm->OutStreamer->emitLabel(Hash->Sym); | |
| for (const auto *Value : Hash->getValues<DWARF5AccelTableData *>()) | |
| emitEntry(*Value, DIEOffsetToAccelEntryOffset); | |
| Asm->OutStreamer->AddComment("End of list: " + Hash->Name.getString()); | |
| Asm->emitInt8(0); | |
| } | |
| } | |
| } | |
| Dwarf5AccelTableWriter::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) | |
| : AccelTableWriter(Asm, Contents, false), | |
| Header(CompUnits.size(), IsSplitDwarf ? 0 : TypeUnits.size(), | |
| IsSplitDwarf ? TypeUnits.size() : 0, Contents.getBucketCount(), | |
| Contents.getUniqueNameCount()), | |
| CompUnits(CompUnits), TypeUnits(TypeUnits), | |
| getIndexForEntry(std::move(getIndexForEntry)), | |
| IsSplitDwarf(IsSplitDwarf) { | |
| for (auto &Bucket : Contents.getBuckets()) | |
| for (auto *Hash : Bucket) | |
| for (auto *Value : Hash->getValues<DWARF5AccelTableData *>()) | |
| IndexedOffsets.insert(Value->getDieOffsetAndUnitID()); | |
| populateAbbrevsMap(); | |
| } | |
| void Dwarf5AccelTableWriter::emit() { | |
| Header.emit(*this); | |
| emitCUList(); | |
| emitTUList(); | |
| emitBuckets(); | |
| emitHashes(); | |
| emitStringOffsets(); | |
| emitOffsets(EntryPool); | |
| emitAbbrevs(); | |
| emitData(); | |
| Asm->OutStreamer->emitValueToAlignment(Align(4), 0); | |
| Asm->OutStreamer->emitLabel(ContributionEnd); | |
| } | |
| void llvm::emitAppleAccelTableImpl(AsmPrinter *Asm, AccelTableBase &Contents, | |
| StringRef Prefix, const MCSymbol *SecBegin, | |
| ArrayRef<AppleAccelTableData::Atom> Atoms) { | |
| Contents.finalize(Asm, Prefix); | |
| AppleAccelTableWriter(Asm, Contents, Atoms, SecBegin).emit(); | |
| } | |
| void llvm::emitDWARF5AccelTable( | |
| AsmPrinter *Asm, DWARF5AccelTable &Contents, const DwarfDebug &DD, | |
| ArrayRef<std::unique_ptr<DwarfCompileUnit>> CUs) { | |
| TUVectorTy TUSymbols = Contents.getTypeUnitsSymbols(); | |
| std::vector<std::variant<MCSymbol *, uint64_t>> CompUnits; | |
| std::vector<std::variant<MCSymbol *, uint64_t>> TypeUnits; | |
| SmallVector<unsigned, 1> CUIndex(CUs.size()); | |
| DenseMap<unsigned, unsigned> TUIndex(TUSymbols.size()); | |
| int CUCount = 0; | |
| int TUCount = 0; | |
| for (const auto &CU : enumerate(CUs)) { | |
| switch (CU.value()->getCUNode()->getNameTableKind()) { | |
| case DICompileUnit::DebugNameTableKind::Default: | |
| case DICompileUnit::DebugNameTableKind::Apple: | |
| break; | |
| default: | |
| continue; | |
| } | |
| CUIndex[CU.index()] = CUCount++; | |
| assert(CU.index() == CU.value()->getUniqueID()); | |
| const DwarfCompileUnit *MainCU = | |
| DD.useSplitDwarf() ? CU.value()->getSkeleton() : CU.value().get(); | |
| CompUnits.push_back(MainCU->getLabelBegin()); | |
| } | |
| for (const auto &TU : TUSymbols) { | |
| TUIndex[TU.UniqueID] = TUCount++; | |
| if (DD.useSplitDwarf()) | |
| TypeUnits.push_back(std::get<uint64_t>(TU.LabelOrSignature)); | |
| else | |
| TypeUnits.push_back(std::get<MCSymbol *>(TU.LabelOrSignature)); | |
| } | |
| if (CompUnits.empty()) | |
| return; | |
| Asm->OutStreamer->switchSection( | |
| Asm->getObjFileLowering().getDwarfDebugNamesSection()); | |
| Contents.finalize(Asm, "names"); | |
| dwarf::Form CUIndexForm = | |
| DIEInteger::BestForm(/*IsSigned*/ false, CompUnits.size() - 1); | |
| dwarf::Form TUIndexForm = | |
| DIEInteger::BestForm(/*IsSigned*/ false, TypeUnits.size() - 1); | |
| Dwarf5AccelTableWriter( | |
| Asm, Contents, CompUnits, TypeUnits, | |
| [&](const DWARF5AccelTableData &Entry) | |
| -> std::optional<DWARF5AccelTable::UnitIndexAndEncoding> { | |
| if (Entry.isTU()) | |
| return {{TUIndex[Entry.getUnitID()], | |
| {dwarf::DW_IDX_type_unit, TUIndexForm}}}; | |
| if (CUIndex.size() > 1) | |
| return {{CUIndex[Entry.getUnitID()], | |
| {dwarf::DW_IDX_compile_unit, CUIndexForm}}}; | |
| return std::nullopt; | |
| }, | |
| DD.useSplitDwarf()) | |
| .emit(); | |
| } | |
| void DWARF5AccelTable::addTypeUnitSymbol(DwarfTypeUnit &U) { | |
| TUSymbolsOrHashes.push_back({U.getLabelBegin(), U.getUniqueID()}); | |
| } | |
| void DWARF5AccelTable::addTypeUnitSignature(DwarfTypeUnit &U) { | |
| TUSymbolsOrHashes.push_back({U.getTypeSignature(), U.getUniqueID()}); | |
| } | |
| void llvm::emitDWARF5AccelTable( | |
| AsmPrinter *Asm, DWARF5AccelTable &Contents, | |
| ArrayRef<std::variant<MCSymbol *, uint64_t>> CUs, | |
| llvm::function_ref<std::optional<DWARF5AccelTable::UnitIndexAndEncoding>( | |
| const DWARF5AccelTableData &)> | |
| getIndexForEntry) { | |
| std::vector<std::variant<MCSymbol *, uint64_t>> TypeUnits; | |
| Contents.finalize(Asm, "names"); | |
| Dwarf5AccelTableWriter(Asm, Contents, CUs, TypeUnits, getIndexForEntry, false) | |
| .emit(); | |
| } | |
| void AppleAccelTableOffsetData::emit(AsmPrinter *Asm) const { | |
| assert(Die.getDebugSectionOffset() <= UINT32_MAX && | |
| "The section offset exceeds the limit."); | |
| Asm->emitInt32(Die.getDebugSectionOffset()); | |
| } | |
| void AppleAccelTableTypeData::emit(AsmPrinter *Asm) const { | |
| assert(Die.getDebugSectionOffset() <= UINT32_MAX && | |
| "The section offset exceeds the limit."); | |
| Asm->emitInt32(Die.getDebugSectionOffset()); | |
| Asm->emitInt16(Die.getTag()); | |
| Asm->emitInt8(0); | |
| } | |
| void AppleAccelTableStaticOffsetData::emit(AsmPrinter *Asm) const { | |
| Asm->emitInt32(Offset); | |
| } | |
| void AppleAccelTableStaticTypeData::emit(AsmPrinter *Asm) const { | |
| Asm->emitInt32(Offset); | |
| Asm->emitInt16(Tag); | |
| Asm->emitInt8(ObjCClassIsImplementation ? dwarf::DW_FLAG_type_implementation | |
| : 0); | |
| Asm->emitInt32(QualifiedNameHash); | |
| } | |
| #ifndef NDEBUG | |
| void AppleAccelTableWriter::Header::print(raw_ostream &OS) const { | |
| OS << "Magic: " << format("0x%x", Magic) << "\n" | |
| << "Version: " << Version << "\n" | |
| << "Hash Function: " << HashFunction << "\n" | |
| << "Bucket Count: " << BucketCount << "\n" | |
| << "Header Data Length: " << HeaderDataLength << "\n"; | |
| } | |
| void AppleAccelTableData::Atom::print(raw_ostream &OS) const { | |
| OS << "Type: " << dwarf::AtomTypeString(Type) << "\n" | |
| << "Form: " << dwarf::FormEncodingString(Form) << "\n"; | |
| } | |
| void AppleAccelTableWriter::HeaderData::print(raw_ostream &OS) const { | |
| OS << "DIE Offset Base: " << DieOffsetBase << "\n"; | |
| for (auto Atom : Atoms) | |
| Atom.print(OS); | |
| } | |
| void AppleAccelTableWriter::print(raw_ostream &OS) const { | |
| Header.print(OS); | |
| HeaderData.print(OS); | |
| Contents.print(OS); | |
| SecBegin->print(OS, nullptr); | |
| } | |
| void AccelTableBase::HashData::print(raw_ostream &OS) const { | |
| OS << "Name: " << Name.getString() << "\n"; | |
| OS << " Hash Value: " << format("0x%x", HashValue) << "\n"; | |
| OS << " Symbol: "; | |
| if (Sym) | |
| OS << *Sym; | |
| else | |
| OS << "<none>"; | |
| OS << "\n"; | |
| for (auto *Value : Values) | |
| Value->print(OS); | |
| } | |
| void AccelTableBase::print(raw_ostream &OS) const { | |
| // Print Content. | |
| OS << "Entries: \n"; | |
| for (const auto &[Name, Data] : Entries) { | |
| OS << "Name: " << Name << "\n"; | |
| for (auto *V : Data.Values) | |
| V->print(OS); | |
| } | |
| OS << "Buckets and Hashes: \n"; | |
| for (const auto &Bucket : Buckets) | |
| for (const auto &Hash : Bucket) | |
| Hash->print(OS); | |
| OS << "Data: \n"; | |
| for (const auto &E : Entries) | |
| E.second.print(OS); | |
| } | |
| void DWARF5AccelTableData::print(raw_ostream &OS) const { | |
| OS << " Offset: " << getDieOffset() << "\n"; | |
| OS << " Tag: " << dwarf::TagString(getDieTag()) << "\n"; | |
| } | |
| void AppleAccelTableOffsetData::print(raw_ostream &OS) const { | |
| OS << " Offset: " << Die.getOffset() << "\n"; | |
| } | |
| void AppleAccelTableTypeData::print(raw_ostream &OS) const { | |
| OS << " Offset: " << Die.getOffset() << "\n"; | |
| OS << " Tag: " << dwarf::TagString(Die.getTag()) << "\n"; | |
| } | |
| void AppleAccelTableStaticOffsetData::print(raw_ostream &OS) const { | |
| OS << " Static Offset: " << Offset << "\n"; | |
| } | |
| void AppleAccelTableStaticTypeData::print(raw_ostream &OS) const { | |
| OS << " Static Offset: " << Offset << "\n"; | |
| OS << " QualifiedNameHash: " << format("%x\n", QualifiedNameHash) << "\n"; | |
| OS << " Tag: " << dwarf::TagString(Tag) << "\n"; | |
| OS << " ObjCClassIsImplementation: " | |
| << (ObjCClassIsImplementation ? "true" : "false"); | |
| OS << "\n"; | |
| } | |
| #endif |