Don't implement the gdb hash table as a generic in-memory hash table.

Looks like .gdb.index and its support classes do things that they don't
have to or shouldn't do do. This patch addresses one of these issues.

GdbHashTab class is a hash table class. Just like other in-memory hash
tables, that incrementally updates its internal data and resizes buckets
as new elements are added so that key lookup is always fast.
But that is completely not necessary.

Unlike debuggers, we only produce hash tables for .gdb.index and
never read them. So incrementally updating a hash table in memory is
just a waste of resource and complicates the code. What we should
do is to accumulate symbols and then create the final hash table
at once.

llvm-svn: 296678
This commit is contained in:
Rui Ueyama 2017-03-01 21:08:21 +00:00
parent f78a6f084c
commit d0e07b919c
3 changed files with 25 additions and 43 deletions

View File

@ -59,6 +59,7 @@
//===----------------------------------------------------------------------===//
#include "GdbIndex.h"
#include "Memory.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h"
#include "llvm/Object/ELFObjectFile.h"
@ -106,48 +107,32 @@ GdbIndexBuilder<ELFT>::readPubNamesAndTypes() {
}
std::pair<bool, GdbSymbol *> GdbHashTab::add(uint32_t Hash, size_t Offset) {
if (Size * 4 / 3 >= Table.size())
expand();
GdbSymbol **Slot = findSlot(Hash, Offset);
bool New = false;
if (*Slot == nullptr) {
++Size;
*Slot = new (Alloc) GdbSymbol(Hash, Offset);
New = true;
}
return {New, *Slot};
GdbSymbol *&Sym = Map[{Hash, Offset}];
if (Sym)
return {false, Sym};
++Size;
Sym = make<GdbSymbol>(Hash, Offset);
return {true, Sym};
}
void GdbHashTab::expand() {
if (Table.empty()) {
Table.resize(InitialSize);
return;
}
std::vector<GdbSymbol *> NewTable(Table.size() * 2);
NewTable.swap(Table);
void GdbHashTab::finalizeContents() {
Table.resize(std::max(1024UL, NextPowerOf2(Map.size() * 4 / 3)));
for (GdbSymbol *Sym : NewTable) {
if (!Sym)
continue;
GdbSymbol **Slot = findSlot(Sym->NameHash, Sym->NameOffset);
*Slot = Sym;
}
}
for (auto &P : Map) {
GdbSymbol *Sym = P.second;
// Methods finds a slot for symbol with given hash. The step size used to find
// the next candidate slot when handling a hash collision is specified in
// .gdb_index section format. The hash value for a table entry is computed by
// applying an iterative hash function to the symbol's name.
GdbSymbol **GdbHashTab::findSlot(uint32_t Hash, size_t Offset) {
uint32_t Index = Hash & (Table.size() - 1);
uint32_t Step = ((Hash * 17) & (Table.size() - 1)) | 1;
uint32_t I = Sym->NameHash & (Table.size() - 1);
uint32_t Step = ((Sym->NameHash * 17) & (Table.size() - 1)) | 1;
for (;;) {
GdbSymbol *S = Table[Index];
if (!S || ((S->NameOffset == Offset) && (S->NameHash == Hash)))
return &Table[Index];
Index = (Index + Step) & (Table.size() - 1);
for (;;) {
if (Table[I]) {
I = (I + Step) & (Table.size() - 1);
continue;
}
Table[I] = Sym;
break;
}
}
}

View File

@ -75,22 +75,18 @@ class GdbHashTab final {
public:
std::pair<bool, GdbSymbol *> add(uint32_t Hash, size_t Offset);
void finalizeContents();
size_t getCapacity() { return Table.size(); }
GdbSymbol *getSymbol(size_t I) { return Table[I]; }
private:
void expand();
GdbSymbol **findSlot(uint32_t Hash, size_t Offset);
llvm::BumpPtrAllocator Alloc;
llvm::DenseMap<std::pair<uint32_t, size_t>, GdbSymbol *> Map;
std::vector<GdbSymbol *> Table;
// Size keeps the amount of filled entries in Table.
size_t Size = 0;
// Initial size must be a power of 2.
static const int32_t InitialSize = 1024;
};
} // namespace elf

View File

@ -1749,6 +1749,7 @@ template <class ELFT> void GdbIndexSection<ELFT>::finalizeContents() {
Finalized = true;
parseDebugSections();
SymbolTable.finalizeContents();
// GdbIndex header consist from version fields
// and 5 more fields with different kinds of offsets.