Home | History | Annotate | Download | only in AsmPrinter
      1 //=-- llvm/CodeGen/DwarfAccelTable.cpp - Dwarf Accelerator Tables -*- C++ -*-=//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This file contains support for writing dwarf accelerator tables.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #include "DwarfAccelTable.h"
     15 #include "DwarfCompileUnit.h"
     16 #include "DwarfDebug.h"
     17 #include "llvm/ADT/STLExtras.h"
     18 #include "llvm/ADT/Twine.h"
     19 #include "llvm/CodeGen/AsmPrinter.h"
     20 #include "llvm/CodeGen/DIE.h"
     21 #include "llvm/MC/MCExpr.h"
     22 #include "llvm/MC/MCStreamer.h"
     23 #include "llvm/MC/MCSymbol.h"
     24 #include "llvm/Support/Debug.h"
     25 
     26 using namespace llvm;
     27 
     28 // The length of the header data is always going to be 4 + 4 + 4*NumAtoms.
     29 DwarfAccelTable::DwarfAccelTable(ArrayRef<DwarfAccelTable::Atom> atomList)
     30     : Header(8 + (atomList.size() * 4)), HeaderData(atomList),
     31       Entries(Allocator) {}
     32 
     33 void DwarfAccelTable::AddName(DwarfStringPoolEntryRef Name, const DIE *die,
     34                               char Flags) {
     35   assert(Data.empty() && "Already finalized!");
     36   // If the string is in the list already then add this die to the list
     37   // otherwise add a new one.
     38   DataArray &DIEs = Entries[Name.getString()];
     39   assert(!DIEs.Name || DIEs.Name == Name);
     40   DIEs.Name = Name;
     41   DIEs.Values.push_back(new (Allocator) HashDataContents(die, Flags));
     42 }
     43 
     44 void DwarfAccelTable::ComputeBucketCount() {
     45   // First get the number of unique hashes.
     46   std::vector<uint32_t> uniques(Data.size());
     47   for (size_t i = 0, e = Data.size(); i < e; ++i)
     48     uniques[i] = Data[i]->HashValue;
     49   array_pod_sort(uniques.begin(), uniques.end());
     50   std::vector<uint32_t>::iterator p =
     51       std::unique(uniques.begin(), uniques.end());
     52   uint32_t num = std::distance(uniques.begin(), p);
     53 
     54   // Then compute the bucket size, minimum of 1 bucket.
     55   if (num > 1024)
     56     Header.bucket_count = num / 4;
     57   else if (num > 16)
     58     Header.bucket_count = num / 2;
     59   else
     60     Header.bucket_count = num > 0 ? num : 1;
     61 
     62   Header.hashes_count = num;
     63 }
     64 
     65 // compareDIEs - comparison predicate that sorts DIEs by their offset.
     66 static bool compareDIEs(const DwarfAccelTable::HashDataContents *A,
     67                         const DwarfAccelTable::HashDataContents *B) {
     68   return A->Die->getOffset() < B->Die->getOffset();
     69 }
     70 
     71 void DwarfAccelTable::FinalizeTable(AsmPrinter *Asm, StringRef Prefix) {
     72   // Create the individual hash data outputs.
     73   Data.reserve(Entries.size());
     74   for (StringMap<DataArray>::iterator EI = Entries.begin(), EE = Entries.end();
     75        EI != EE; ++EI) {
     76 
     77     // Unique the entries.
     78     std::stable_sort(EI->second.Values.begin(), EI->second.Values.end(), compareDIEs);
     79     EI->second.Values.erase(
     80         std::unique(EI->second.Values.begin(), EI->second.Values.end()),
     81         EI->second.Values.end());
     82 
     83     HashData *Entry = new (Allocator) HashData(EI->getKey(), EI->second);
     84     Data.push_back(Entry);
     85   }
     86 
     87   // Figure out how many buckets we need, then compute the bucket
     88   // contents and the final ordering. We'll emit the hashes and offsets
     89   // by doing a walk during the emission phase. We add temporary
     90   // symbols to the data so that we can reference them during the offset
     91   // later, we'll emit them when we emit the data.
     92   ComputeBucketCount();
     93 
     94   // Compute bucket contents and final ordering.
     95   Buckets.resize(Header.bucket_count);
     96   for (size_t i = 0, e = Data.size(); i < e; ++i) {
     97     uint32_t bucket = Data[i]->HashValue % Header.bucket_count;
     98     Buckets[bucket].push_back(Data[i]);
     99     Data[i]->Sym = Asm->createTempSymbol(Prefix);
    100   }
    101 
    102   // Sort the contents of the buckets by hash value so that hash
    103   // collisions end up together. Stable sort makes testing easier and
    104   // doesn't cost much more.
    105   for (size_t i = 0; i < Buckets.size(); ++i)
    106     std::stable_sort(Buckets[i].begin(), Buckets[i].end(),
    107                      [] (HashData *LHS, HashData *RHS) {
    108                        return LHS->HashValue < RHS->HashValue;
    109                      });
    110 }
    111 
    112 // Emits the header for the table via the AsmPrinter.
    113 void DwarfAccelTable::EmitHeader(AsmPrinter *Asm) {
    114   Asm->OutStreamer->AddComment("Header Magic");
    115   Asm->EmitInt32(Header.magic);
    116   Asm->OutStreamer->AddComment("Header Version");
    117   Asm->EmitInt16(Header.version);
    118   Asm->OutStreamer->AddComment("Header Hash Function");
    119   Asm->EmitInt16(Header.hash_function);
    120   Asm->OutStreamer->AddComment("Header Bucket Count");
    121   Asm->EmitInt32(Header.bucket_count);
    122   Asm->OutStreamer->AddComment("Header Hash Count");
    123   Asm->EmitInt32(Header.hashes_count);
    124   Asm->OutStreamer->AddComment("Header Data Length");
    125   Asm->EmitInt32(Header.header_data_len);
    126   Asm->OutStreamer->AddComment("HeaderData Die Offset Base");
    127   Asm->EmitInt32(HeaderData.die_offset_base);
    128   Asm->OutStreamer->AddComment("HeaderData Atom Count");
    129   Asm->EmitInt32(HeaderData.Atoms.size());
    130   for (size_t i = 0; i < HeaderData.Atoms.size(); i++) {
    131     Atom A = HeaderData.Atoms[i];
    132     Asm->OutStreamer->AddComment(dwarf::AtomTypeString(A.type));
    133     Asm->EmitInt16(A.type);
    134     Asm->OutStreamer->AddComment(dwarf::FormEncodingString(A.form));
    135     Asm->EmitInt16(A.form);
    136   }
    137 }
    138 
    139 // Walk through and emit the buckets for the table. Each index is
    140 // an offset into the list of hashes.
    141 void DwarfAccelTable::EmitBuckets(AsmPrinter *Asm) {
    142   unsigned index = 0;
    143   for (size_t i = 0, e = Buckets.size(); i < e; ++i) {
    144     Asm->OutStreamer->AddComment("Bucket " + Twine(i));
    145     if (Buckets[i].size() != 0)
    146       Asm->EmitInt32(index);
    147     else
    148       Asm->EmitInt32(UINT32_MAX);
    149     // Buckets point in the list of hashes, not to the data. Do not
    150     // increment the index multiple times in case of hash collisions.
    151     uint64_t PrevHash = UINT64_MAX;
    152     for (auto *HD : Buckets[i]) {
    153       uint32_t HashValue = HD->HashValue;
    154       if (PrevHash != HashValue)
    155         ++index;
    156       PrevHash = HashValue;
    157     }
    158   }
    159 }
    160 
    161 // Walk through the buckets and emit the individual hashes for each
    162 // bucket.
    163 void DwarfAccelTable::EmitHashes(AsmPrinter *Asm) {
    164   uint64_t PrevHash = UINT64_MAX;
    165   for (size_t i = 0, e = Buckets.size(); i < e; ++i) {
    166     for (HashList::const_iterator HI = Buckets[i].begin(),
    167                                   HE = Buckets[i].end();
    168          HI != HE; ++HI) {
    169       uint32_t HashValue = (*HI)->HashValue;
    170       if (PrevHash == HashValue)
    171         continue;
    172       Asm->OutStreamer->AddComment("Hash in Bucket " + Twine(i));
    173       Asm->EmitInt32(HashValue);
    174       PrevHash = HashValue;
    175     }
    176   }
    177 }
    178 
    179 // Walk through the buckets and emit the individual offsets for each
    180 // element in each bucket. This is done via a symbol subtraction from the
    181 // beginning of the section. The non-section symbol will be output later
    182 // when we emit the actual data.
    183 void DwarfAccelTable::emitOffsets(AsmPrinter *Asm, const MCSymbol *SecBegin) {
    184   uint64_t PrevHash = UINT64_MAX;
    185   for (size_t i = 0, e = Buckets.size(); i < e; ++i) {
    186     for (HashList::const_iterator HI = Buckets[i].begin(),
    187                                   HE = Buckets[i].end();
    188          HI != HE; ++HI) {
    189       uint32_t HashValue = (*HI)->HashValue;
    190       if (PrevHash == HashValue)
    191         continue;
    192       PrevHash = HashValue;
    193       Asm->OutStreamer->AddComment("Offset in Bucket " + Twine(i));
    194       MCContext &Context = Asm->OutStreamer->getContext();
    195       const MCExpr *Sub = MCBinaryExpr::createSub(
    196           MCSymbolRefExpr::create((*HI)->Sym, Context),
    197           MCSymbolRefExpr::create(SecBegin, Context), Context);
    198       Asm->OutStreamer->EmitValue(Sub, sizeof(uint32_t));
    199     }
    200   }
    201 }
    202 
    203 // Walk through the buckets and emit the full data for each element in
    204 // the bucket. For the string case emit the dies and the various offsets.
    205 // Terminate each HashData bucket with 0.
    206 void DwarfAccelTable::EmitData(AsmPrinter *Asm, DwarfDebug *D) {
    207   for (size_t i = 0, e = Buckets.size(); i < e; ++i) {
    208     uint64_t PrevHash = UINT64_MAX;
    209     for (HashList::const_iterator HI = Buckets[i].begin(),
    210                                   HE = Buckets[i].end();
    211          HI != HE; ++HI) {
    212       // Terminate the previous entry if there is no hash collision
    213       // with the current one.
    214       if (PrevHash != UINT64_MAX && PrevHash != (*HI)->HashValue)
    215         Asm->EmitInt32(0);
    216       // Remember to emit the label for our offset.
    217       Asm->OutStreamer->EmitLabel((*HI)->Sym);
    218       Asm->OutStreamer->AddComment((*HI)->Str);
    219       Asm->emitDwarfStringOffset((*HI)->Data.Name);
    220       Asm->OutStreamer->AddComment("Num DIEs");
    221       Asm->EmitInt32((*HI)->Data.Values.size());
    222       for (HashDataContents *HD : (*HI)->Data.Values) {
    223         // Emit the DIE offset
    224         DwarfCompileUnit *CU = D->lookupUnit(HD->Die->getUnit());
    225         assert(CU && "Accelerated DIE should belong to a CU.");
    226         Asm->EmitInt32(HD->Die->getOffset() + CU->getDebugInfoOffset());
    227         // If we have multiple Atoms emit that info too.
    228         // FIXME: A bit of a hack, we either emit only one atom or all info.
    229         if (HeaderData.Atoms.size() > 1) {
    230           Asm->EmitInt16(HD->Die->getTag());
    231           Asm->EmitInt8(HD->Flags);
    232         }
    233       }
    234       PrevHash = (*HI)->HashValue;
    235     }
    236     // Emit the final end marker for the bucket.
    237     if (!Buckets[i].empty())
    238       Asm->EmitInt32(0);
    239   }
    240 }
    241 
    242 // Emit the entire data structure to the output file.
    243 void DwarfAccelTable::emit(AsmPrinter *Asm, const MCSymbol *SecBegin,
    244                            DwarfDebug *D) {
    245   // Emit the header.
    246   EmitHeader(Asm);
    247 
    248   // Emit the buckets.
    249   EmitBuckets(Asm);
    250 
    251   // Emit the hashes.
    252   EmitHashes(Asm);
    253 
    254   // Emit the offsets.
    255   emitOffsets(Asm, SecBegin);
    256 
    257   // Emit the hash data.
    258   EmitData(Asm, D);
    259 }
    260 
    261 #ifndef NDEBUG
    262 void DwarfAccelTable::print(raw_ostream &O) {
    263 
    264   Header.print(O);
    265   HeaderData.print(O);
    266 
    267   O << "Entries: \n";
    268   for (StringMap<DataArray>::const_iterator EI = Entries.begin(),
    269                                             EE = Entries.end();
    270        EI != EE; ++EI) {
    271     O << "Name: " << EI->getKeyData() << "\n";
    272     for (HashDataContents *HD : EI->second.Values)
    273       HD->print(O);
    274   }
    275 
    276   O << "Buckets and Hashes: \n";
    277   for (size_t i = 0, e = Buckets.size(); i < e; ++i)
    278     for (HashList::const_iterator HI = Buckets[i].begin(),
    279                                   HE = Buckets[i].end();
    280          HI != HE; ++HI)
    281       (*HI)->print(O);
    282 
    283   O << "Data: \n";
    284   for (std::vector<HashData *>::const_iterator DI = Data.begin(),
    285                                                DE = Data.end();
    286        DI != DE; ++DI)
    287     (*DI)->print(O);
    288 }
    289 #endif
    290