summaryrefslogtreecommitdiff
path: root/lib/CodeGen/AsmPrinter/DwarfAccelTable.h
blob: 18d1138228a8c7893d04a5421ddfc50168778723 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
//==-- 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 CODEGEN_ASMPRINTER_DWARFACCELTABLE_H__
#define CODEGEN_ASMPRINTER_DWARFACCELTABLE_H__

#include "DIE.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Dwarf.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/FormattedStream.h"
#include <map>
#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;
class DIE;
class DwarfFile;

class DwarfAccelTable {

  static uint32_t HashDJB(StringRef Str) {
    uint32_t h = 5381;
    for (unsigned i = 0, e = Str.size(); i != e; ++i)
      h = ((h << 5) + h) + Str[i];
    return h;
  }

  // Helper function to compute the number of buckets needed based on
  // the number of unique hashes.
  void ComputeBucketCount(void);

  struct TableHeader {
    uint32_t magic;           // 'HASH' magic value to allow endian detection
    uint16_t version;         // Version number.
    uint16_t hash_function;   // The hash function enumeration that was used.
    uint32_t bucket_count;    // The number of buckets in this hash table.
    uint32_t hashes_count;    // The total number of unique hash values
                              // and hash data offsets in this table.
    uint32_t header_data_len; // The bytes to skip to get to the hash
                              // indexes (buckets) for correct alignment.
    // Also written to disk is the implementation specific header data.

    static const uint32_t MagicHash = 0x48415348;

    TableHeader(uint32_t data_len)
        : magic(MagicHash), version(1),
          hash_function(dwarf::DW_hash_function_djb), bucket_count(0),
          hashes_count(0), header_data_len(data_len) {}

#ifndef NDEBUG
    void print(raw_ostream &O) {
      O << "Magic: " << format("0x%x", magic) << "\n"
        << "Version: " << version << "\n"
        << "Hash Function: " << hash_function << "\n"
        << "Bucket Count: " << bucket_count << "\n"
        << "Header Data Length: " << header_data_len << "\n";
    }
    void dump() { print(dbgs()); }
#endif
  };

public:
  // The HeaderData describes the form of each set of data. In general this
  // is as a list of atoms (atom_count) where each atom contains a type
  // (AtomType type) of data, and an encoding form (form). In the case of
  // data that is referenced via DW_FORM_ref_* the die_offset_base is
  // used to describe the offset for all forms in the list of atoms.
  // This also serves as a public interface of sorts.
  // When written to disk this will have the form:
  //
  // uint32_t die_offset_base
  // uint32_t atom_count
  // atom_count Atoms

  // Make these public so that they can be used as a general interface to
  // the class.
  struct Atom {
    uint16_t type; // enum AtomType
    uint16_t form; // DWARF DW_FORM_ defines

    Atom(uint16_t type, uint16_t form) : type(type), form(form) {}
#ifndef NDEBUG
    void print(raw_ostream &O) {
      O << "Type: " << dwarf::AtomTypeString(type) << "\n"
        << "Form: " << dwarf::FormEncodingString(form) << "\n";
    }
    void dump() { print(dbgs()); }
#endif
  };

private:
  struct TableHeaderData {
    uint32_t die_offset_base;
    SmallVector<Atom, 1> Atoms;

    TableHeaderData(ArrayRef<Atom> AtomList, uint32_t offset = 0)
        : die_offset_base(offset), Atoms(AtomList.begin(), AtomList.end()) {}

#ifndef NDEBUG
    void print(raw_ostream &O) {
      O << "die_offset_base: " << die_offset_base << "\n";
      for (size_t i = 0; i < Atoms.size(); i++)
        Atoms[i].print(O);
    }
    void dump() { print(dbgs()); }
#endif
  };

  // The data itself consists of a str_offset, a count of the DIEs in the
  // hash and the offsets to the DIEs themselves.
  // On disk each data section is ended with a 0 KeyType as the end of the
  // hash chain.
  // On output this looks like:
  // uint32_t str_offset
  // uint32_t hash_data_count
  // HashData[hash_data_count]
public:
  struct HashDataContents {
    const DIE *Die;   // Offsets
    char Flags; // Specific flags to output

    HashDataContents(const DIE *D, char Flags) : Die(D), Flags(Flags) {}
#ifndef NDEBUG
    void print(raw_ostream &O) const {
      O << "  Offset: " << Die->getOffset() << "\n";
      O << "  Tag: " << dwarf::TagString(Die->getTag()) << "\n";
      O << "  Flags: " << Flags << "\n";
    }
#endif
  };

private:
  struct HashData {
    StringRef Str;
    uint32_t HashValue;
    MCSymbol *Sym;
    ArrayRef<HashDataContents *> Data; // offsets
    HashData(StringRef S, ArrayRef<HashDataContents *> Data)
        : Str(S), Data(Data) {
      HashValue = DwarfAccelTable::HashDJB(S);
    }
#ifndef NDEBUG
    void print(raw_ostream &O) {
      O << "Name: " << Str << "\n";
      O << "  Hash Value: " << format("0x%x", HashValue) << "\n";
      O << "  Symbol: ";
      if (Sym)
        Sym->print(O);
      else
        O << "<none>";
      O << "\n";
      for (size_t i = 0; i < Data.size(); i++) {
        O << "  Offset: " << Data[i]->Die->getOffset() << "\n";
        O << "  Tag: " << dwarf::TagString(Data[i]->Die->getTag()) << "\n";
        O << "  Flags: " << Data[i]->Flags << "\n";
      }
    }
    void dump() { print(dbgs()); }
#endif
  };

  DwarfAccelTable(const DwarfAccelTable &) LLVM_DELETED_FUNCTION;
  void operator=(const DwarfAccelTable &) LLVM_DELETED_FUNCTION;

  // Internal Functions
  void EmitHeader(AsmPrinter *);
  void EmitBuckets(AsmPrinter *);
  void EmitHashes(AsmPrinter *);
  void EmitOffsets(AsmPrinter *, MCSymbol *);
  void EmitData(AsmPrinter *, DwarfFile *D);

  // Allocator for HashData and HashDataContents.
  BumpPtrAllocator Allocator;

  // Output Variables
  TableHeader Header;
  TableHeaderData HeaderData;
  std::vector<HashData *> Data;

  // String Data
  typedef std::vector<HashDataContents *> DataArray;
  typedef StringMap<DataArray, BumpPtrAllocator &> StringEntries;
  StringEntries Entries;

  // Buckets/Hashes/Offsets
  typedef std::vector<HashData *> HashList;
  typedef std::vector<HashList> BucketList;
  BucketList Buckets;
  HashList Hashes;

  // Public Implementation
public:
  DwarfAccelTable(ArrayRef<DwarfAccelTable::Atom>);
  ~DwarfAccelTable();
  void AddName(StringRef, const DIE *, char = 0);
  void FinalizeTable(AsmPrinter *, StringRef);
  void Emit(AsmPrinter *, MCSymbol *, DwarfFile *);
#ifndef NDEBUG
  void print(raw_ostream &O);
  void dump() { print(dbgs()); }
#endif
};
}
#endif