llvm/lib/MC/StringTableBuilder.cpp
Rafael Espindola c55049e9d6 Allow the caller to pass in the hash.
If the caller already has the hash we don't have to compute it. This
will be used in lld.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@283359 91177308-0d34-0410-b5e6-96231b3b80d8
2016-10-05 18:46:21 +00:00

204 lines
5.2 KiB
C++

//===-- StringTableBuilder.cpp - String table building utility ------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/COFF.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/raw_ostream.h"
#include <vector>
using namespace llvm;
namespace llvm {
template <> struct DenseMapInfo<CachedHashString> {
static CachedHashString getEmptyKey() {
StringRef S = DenseMapInfo<StringRef>::getEmptyKey();
return {S, 0};
}
static CachedHashString getTombstoneKey() {
StringRef S = DenseMapInfo<StringRef>::getTombstoneKey();
return {S, 0};
}
static unsigned getHashValue(CachedHashString Val) {
assert(!isEqual(Val, getEmptyKey()) && "Cannot hash the empty key!");
assert(!isEqual(Val, getTombstoneKey()) &&
"Cannot hash the tombstone key!");
return Val.hash();
}
static bool isEqual(CachedHashString A, CachedHashString B) {
return DenseMapInfo<StringRef>::isEqual(A.val(), B.val());
}
};
}
StringTableBuilder::~StringTableBuilder() {}
void StringTableBuilder::initSize() {
// Account for leading bytes in table so that offsets returned from add are
// correct.
switch (K) {
case RAW:
Size = 0;
break;
case MachO:
case ELF:
// Start the table with a NUL byte.
Size = 1;
break;
case WinCOFF:
// Make room to write the table size later.
Size = 4;
break;
}
}
StringTableBuilder::StringTableBuilder(Kind K, unsigned Alignment)
: K(K), Alignment(Alignment) {
initSize();
}
void StringTableBuilder::write(raw_ostream &OS) const {
assert(isFinalized());
SmallString<0> Data;
Data.resize(getSize());
write((uint8_t *)&Data[0]);
OS << Data;
}
typedef std::pair<CachedHashString, size_t> StringPair;
void StringTableBuilder::write(uint8_t *Buf) const {
assert(isFinalized());
for (const StringPair &P : StringIndexMap) {
StringRef Data = P.first.val();
if (!Data.empty())
memcpy(Buf + P.second, Data.data(), Data.size());
}
if (K != WinCOFF)
return;
support::endian::write32le(Buf, Size);
}
// Returns the character at Pos from end of a string.
static int charTailAt(StringPair *P, size_t Pos) {
StringRef S = P->first.val();
if (Pos >= S.size())
return -1;
return (unsigned char)S[S.size() - Pos - 1];
}
// Three-way radix quicksort. This is much faster than std::sort with strcmp
// because it does not compare characters that we already know the same.
static void multikey_qsort(StringPair **Begin, StringPair **End, int Pos) {
tailcall:
if (End - Begin <= 1)
return;
// Partition items. Items in [Begin, P) are greater than the pivot,
// [P, Q) are the same as the pivot, and [Q, End) are less than the pivot.
int Pivot = charTailAt(*Begin, Pos);
StringPair **P = Begin;
StringPair **Q = End;
for (StringPair **R = Begin + 1; R < Q;) {
int C = charTailAt(*R, Pos);
if (C > Pivot)
std::swap(*P++, *R++);
else if (C < Pivot)
std::swap(*--Q, *R);
else
R++;
}
multikey_qsort(Begin, P, Pos);
multikey_qsort(Q, End, Pos);
if (Pivot != -1) {
// qsort(P, Q, Pos + 1), but with tail call optimization.
Begin = P;
End = Q;
++Pos;
goto tailcall;
}
}
void StringTableBuilder::finalize() {
finalizeStringTable(/*Optimize=*/true);
}
void StringTableBuilder::finalizeInOrder() {
finalizeStringTable(/*Optimize=*/false);
}
void StringTableBuilder::finalizeStringTable(bool Optimize) {
Finalized = true;
if (Optimize) {
std::vector<StringPair *> Strings;
Strings.reserve(StringIndexMap.size());
for (StringPair &P : StringIndexMap)
Strings.push_back(&P);
if (!Strings.empty()) {
// If we're optimizing, sort by name. If not, sort by previously assigned
// offset.
multikey_qsort(&Strings[0], &Strings[0] + Strings.size(), 0);
}
initSize();
StringRef Previous;
for (StringPair *P : Strings) {
StringRef S = P->first.val();
if (Previous.endswith(S)) {
size_t Pos = Size - S.size() - (K != RAW);
if (!(Pos & (Alignment - 1))) {
P->second = Pos;
continue;
}
}
Size = alignTo(Size, Alignment);
P->second = Size;
Size += S.size();
if (K != RAW)
++Size;
Previous = S;
}
}
if (K == MachO)
Size = alignTo(Size, 4); // Pad to multiple of 4.
}
void StringTableBuilder::clear() {
Finalized = false;
StringIndexMap.clear();
}
size_t StringTableBuilder::getOffset(CachedHashString S) const {
assert(isFinalized());
auto I = StringIndexMap.find(S);
assert(I != StringIndexMap.end() && "String is not in table!");
return I->second;
}
size_t StringTableBuilder::add(CachedHashString S) {
if (K == WinCOFF)
assert(S.size() > COFF::NameSize && "Short string in COFF string table!");
assert(!isFinalized());
size_t Start = alignTo(Size, Alignment);
auto P = StringIndexMap.insert(std::make_pair(S, Start));
if (P.second)
Size = Start + S.size() + (K != RAW);
return P.first->second;
}