| //==- llvm/CodeGen/DwarfAccelTable.h - Dwarf Accelerator Tables --*- C++ -*-==// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file contains support for writing dwarf accelerator tables. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_LIB_CODEGEN_ASMPRINTER_DWARFACCELTABLE_H |
| #define LLVM_LIB_CODEGEN_ASMPRINTER_DWARFACCELTABLE_H |
| |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/ADT/StringMap.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/BinaryFormat/Dwarf.h" |
| #include "llvm/CodeGen/DIE.h" |
| #include "llvm/CodeGen/DwarfStringPoolEntry.h" |
| #include "llvm/MC/MCSymbol.h" |
| #include "llvm/Support/Allocator.h" |
| #include "llvm/Support/DJB.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/Format.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include <cstddef> |
| #include <cstdint> |
| #include <vector> |
| |
| // The dwarf accelerator tables are an indirect hash table optimized |
| // for null lookup rather than access to known data. They are output into |
| // an on-disk format that looks like this: |
| // |
| // .-------------. |
| // | HEADER | |
| // |-------------| |
| // | BUCKETS | |
| // |-------------| |
| // | HASHES | |
| // |-------------| |
| // | OFFSETS | |
| // |-------------| |
| // | DATA | |
| // `-------------' |
| // |
| // where the header contains a magic number, version, type of hash function, |
| // the number of buckets, total number of hashes, and room for a special |
| // struct of data and the length of that struct. |
| // |
| // The buckets contain an index (e.g. 6) into the hashes array. The hashes |
| // section contains all of the 32-bit hash values in contiguous memory, and |
| // the offsets contain the offset into the data area for the particular |
| // hash. |
| // |
| // For a lookup example, we could hash a function name and take it modulo the |
| // number of buckets giving us our bucket. From there we take the bucket value |
| // as an index into the hashes table and look at each successive hash as long |
| // as the hash value is still the same modulo result (bucket value) as earlier. |
| // If we have a match we look at that same entry in the offsets table and |
| // grab the offset in the data for our final match. |
| |
| namespace llvm { |
| |
| class AsmPrinter; |
| |
| /// Representation of the header of an Apple accelerator table. This consists |
| /// of the fixed header and the header data. The latter contains the atoms |
| /// which define the columns of the table. |
| class AppleAccelTableHeader { |
| struct Header { |
| uint32_t Magic = MagicHash; |
| uint16_t Version = 1; |
| uint16_t HashFunction = dwarf::DW_hash_function_djb; |
| uint32_t BucketCount = 0; |
| uint32_t HashCount = 0; |
| uint32_t HeaderDataLength; |
| |
| /// 'HASH' magic value to detect endianness. |
| static const uint32_t MagicHash = 0x48415348; |
| |
| Header(uint32_t DataLength) : HeaderDataLength(DataLength) {} |
| |
| #ifndef NDEBUG |
| void 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 dump() const { print(dbgs()); } |
| #endif |
| }; |
| |
| public: |
| /// An Atom defines the form of the data in the accelerator table. |
| /// Conceptually it is a column in the accelerator consisting of a type and a |
| /// specification of the form of its data. |
| struct Atom { |
| /// Atom Type. |
| const uint16_t Type; |
| /// DWARF Form. |
| const uint16_t Form; |
| |
| constexpr Atom(uint16_t Type, uint16_t Form) : Type(Type), Form(Form) {} |
| |
| #ifndef NDEBUG |
| void print(raw_ostream &OS) const { |
| OS << "Type: " << dwarf::AtomTypeString(Type) << "\n" |
| << "Form: " << dwarf::FormEncodingString(Form) << "\n"; |
| } |
| |
| void dump() const { print(dbgs()); } |
| #endif |
| }; |
| |
| private: |
| /// The HeaderData describes the structure of the 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; |
| |
| SmallVector<Atom, 3> Atoms; |
| |
| HeaderData(ArrayRef<Atom> AtomList, uint32_t Offset = 0) |
| : DieOffsetBase(Offset), Atoms(AtomList.begin(), AtomList.end()) {} |
| |
| #ifndef NDEBUG |
| void print(raw_ostream &OS) const { |
| OS << "DIE Offset Base: " << DieOffsetBase << "\n"; |
| for (auto Atom : Atoms) |
| Atom.print(OS); |
| } |
| |
| void dump() const { print(dbgs()); } |
| #endif |
| }; |
| |
| Header Header; |
| HeaderData HeaderData; |
| |
| public: |
| /// The length of the header data is always going to be 4 + 4 + 4*NumAtoms. |
| AppleAccelTableHeader(ArrayRef<AppleAccelTableHeader::Atom> Atoms) |
| : Header(8 + (Atoms.size() * 4)), HeaderData(Atoms) {} |
| |
| /// Update header with hash and bucket count. |
| void setBucketAndHashCount(uint32_t HashCount); |
| |
| uint32_t getHashCount() const { return Header.HashCount; } |
| uint32_t getBucketCount() const { return Header.BucketCount; } |
| |
| /// Emits the header via the AsmPrinter. |
| void emit(AsmPrinter *); |
| |
| #ifndef NDEBUG |
| void print(raw_ostream &OS) const { |
| Header.print(OS); |
| HeaderData.print(OS); |
| } |
| |
| void dump() const { print(dbgs()); } |
| #endif |
| }; |
| |
| /// Interface which the different types of accelerator table data have to |
| /// conform. |
| class AppleAccelTableData { |
| public: |
| virtual ~AppleAccelTableData() = default; |
| |
| virtual void emit(AsmPrinter *Asm) const = 0; |
| |
| bool operator<(const AppleAccelTableData &Other) const { |
| return order() < Other.order(); |
| } |
| |
| #ifndef NDEBUG |
| virtual void print(raw_ostream &OS) const = 0; |
| #endif |
| protected: |
| virtual uint64_t order() const; |
| }; |
| |
| /// Apple-style accelerator table base class. |
| class AppleAccelTableBase { |
| protected: |
| struct DataArray { |
| DwarfStringPoolEntryRef Name; |
| std::vector<AppleAccelTableData *> Values; |
| }; |
| |
| friend struct HashData; |
| |
| struct HashData { |
| StringRef Str; |
| uint32_t HashValue; |
| MCSymbol *Sym; |
| DataArray &Data; |
| |
| HashData(StringRef S, DataArray &Data) : Str(S), Data(Data) { |
| HashValue = djbHash(S); |
| } |
| |
| #ifndef NDEBUG |
| void print(raw_ostream &OS) { |
| OS << "Name: " << Str << "\n"; |
| OS << " Hash Value: " << format("0x%x", HashValue) << "\n"; |
| OS << " Symbol: "; |
| if (Sym) |
| OS << *Sym; |
| else |
| OS << "<none>"; |
| OS << "\n"; |
| for (auto *Value : Data.Values) |
| Value->print(OS); |
| } |
| |
| void dump() { print(dbgs()); } |
| #endif |
| }; |
| |
| /// Allocator for HashData and Values. |
| BumpPtrAllocator Allocator; |
| |
| /// Header containing both the header and header data. |
| AppleAccelTableHeader Header; |
| |
| std::vector<HashData *> Data; |
| |
| using StringEntries = StringMap<DataArray, BumpPtrAllocator &>; |
| StringEntries Entries; |
| |
| using HashList = std::vector<HashData *>; |
| HashList Hashes; |
| |
| using BucketList = std::vector<HashList>; |
| BucketList Buckets; |
| |
| AppleAccelTableBase(ArrayRef<AppleAccelTableHeader::Atom> Atoms) |
| : Header(Atoms), Entries(Allocator) {} |
| |
| private: |
| /// Emits the header for the table via the AsmPrinter. |
| void emitHeader(AsmPrinter *Asm); |
| |
| /// Helper function to compute the number of buckets needed based on the |
| /// number of unique hashes. |
| void computeBucketCount(); |
| |
| /// Walk through and emit the buckets for the table. Each index is an offset |
| /// into the list of hashes. |
| void emitBuckets(AsmPrinter *); |
| |
| /// Walk through the buckets and emit the individual hashes for each bucket. |
| void emitHashes(AsmPrinter *); |
| |
| /// Walk through the buckets and emit the individual offsets for each element |
| /// in each bucket. This is done via a symbol subtraction from the beginning |
| /// of the section. The non-section symbol will be output later when we emit |
| /// the actual data. |
| void emitOffsets(AsmPrinter *, const MCSymbol *); |
| |
| /// Walk through the buckets and emit the full data for each element in the |
| /// bucket. For the string case emit the dies and the various offsets. |
| /// Terminate each HashData bucket with 0. |
| void emitData(AsmPrinter *); |
| |
| public: |
| void finalizeTable(AsmPrinter *, StringRef); |
| |
| void emit(AsmPrinter *Asm, const MCSymbol *SecBegin) { |
| emitHeader(Asm); |
| emitBuckets(Asm); |
| emitHashes(Asm); |
| emitOffsets(Asm, SecBegin); |
| emitData(Asm); |
| } |
| |
| #ifndef NDEBUG |
| void print(raw_ostream &OS) const { |
| // Print Header. |
| Header.print(OS); |
| |
| // Print Content. |
| OS << "Entries: \n"; |
| for (const auto &Entry : Entries) { |
| OS << "Name: " << Entry.first() << "\n"; |
| for (auto *V : Entry.second.Values) |
| V->print(OS); |
| } |
| |
| OS << "Buckets and Hashes: \n"; |
| for (auto &Bucket : Buckets) |
| for (auto &Hash : Bucket) |
| Hash->print(OS); |
| |
| OS << "Data: \n"; |
| for (auto &D : Data) |
| D->print(OS); |
| } |
| void dump() const { print(dbgs()); } |
| #endif |
| }; |
| |
| template <typename AppleAccelTableDataT> |
| class AppleAccelTable : public AppleAccelTableBase { |
| public: |
| AppleAccelTable() : AppleAccelTableBase(AppleAccelTableDataT::Atoms) {} |
| AppleAccelTable(const AppleAccelTable &) = delete; |
| AppleAccelTable &operator=(const AppleAccelTable &) = delete; |
| |
| template <class... Types> |
| void addName(DwarfStringPoolEntryRef Name, Types... Args); |
| }; |
| |
| template <typename AppleAccelTableDataT> |
| template <class... Types> |
| void AppleAccelTable<AppleAccelTableDataT>::addName( |
| DwarfStringPoolEntryRef Name, Types... Args) { |
| assert(Data.empty() && "Already finalized!"); |
| // If the string is in the list already then add this die to the list |
| // otherwise add a new one. |
| DataArray &DA = Entries[Name.getString()]; |
| assert(!DA.Name || DA.Name == Name); |
| DA.Name = Name; |
| DA.Values.push_back(new (Allocator) AppleAccelTableDataT(Args...)); |
| } |
| |
| /// Accelerator table data implementation for simple accelerator tables with |
| /// just a DIE reference. |
| class AppleAccelTableOffsetData : public AppleAccelTableData { |
| public: |
| AppleAccelTableOffsetData(const DIE *D) : Die(D) {} |
| |
| void emit(AsmPrinter *Asm) const override; |
| |
| static constexpr AppleAccelTableHeader::Atom Atoms[] = { |
| AppleAccelTableHeader::Atom(dwarf::DW_ATOM_die_offset, |
| dwarf::DW_FORM_data4)}; |
| |
| #ifndef NDEBUG |
| void print(raw_ostream &OS) const override { |
| OS << " Offset: " << Die->getOffset() << "\n"; |
| } |
| |
| #endif |
| protected: |
| uint64_t order() const override { return Die->getOffset(); } |
| |
| const DIE *Die; |
| }; |
| |
| /// Accelerator table data implementation for type accelerator tables. |
| class AppleAccelTableTypeData : public AppleAccelTableOffsetData { |
| public: |
| AppleAccelTableTypeData(const DIE *D) : AppleAccelTableOffsetData(D) {} |
| |
| void emit(AsmPrinter *Asm) const override; |
| |
| static constexpr AppleAccelTableHeader::Atom Atoms[] = { |
| AppleAccelTableHeader::Atom(dwarf::DW_ATOM_die_offset, |
| dwarf::DW_FORM_data4), |
| AppleAccelTableHeader::Atom(dwarf::DW_ATOM_die_tag, dwarf::DW_FORM_data2), |
| AppleAccelTableHeader::Atom(dwarf::DW_ATOM_type_flags, |
| dwarf::DW_FORM_data1)}; |
| |
| #ifndef NDEBUG |
| void print(raw_ostream &OS) const override { |
| OS << " Offset: " << Die->getOffset() << "\n"; |
| OS << " Tag: " << dwarf::TagString(Die->getTag()) << "\n"; |
| } |
| #endif |
| }; |
| |
| } // end namespace llvm |
| |
| #endif // LLVM_LIB_CODEGEN_ASMPRINTER_DWARFACCELTABLE_H |