192 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			192 lines
		
	
	
		
			5.0 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 <vector>
 | |
| 
 | |
| using namespace llvm;
 | |
| 
 | |
| StringTableBuilder::StringTableBuilder(Kind K, unsigned Alignment)
 | |
|     : K(K), Alignment(Alignment) {
 | |
|   // 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:
 | |
|     Size = 1;
 | |
|     break;
 | |
|   case WinCOFF:
 | |
|     Size = 4;
 | |
|     break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| typedef std::pair<CachedHash<StringRef>, size_t> StringPair;
 | |
| 
 | |
| // 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) {
 | |
|   typedef std::pair<CachedHash<StringRef>, size_t> StringOffsetPair;
 | |
|   std::vector<StringOffsetPair *> Strings;
 | |
|   Strings.reserve(StringIndexMap.size());
 | |
|   for (StringOffsetPair &P : StringIndexMap)
 | |
|     Strings.push_back(&P);
 | |
| 
 | |
|   if (!Strings.empty()) {
 | |
|     // If we're optimizing, sort by name. If not, sort by previously assigned
 | |
|     // offset.
 | |
|     if (Optimize) {
 | |
|       multikey_qsort(&Strings[0], &Strings[0] + Strings.size(), 0);
 | |
|     } else {
 | |
|       std::sort(Strings.begin(), Strings.end(),
 | |
|                 [](const StringOffsetPair *LHS, const StringOffsetPair *RHS) {
 | |
|                   return LHS->second < RHS->second;
 | |
|                 });
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   switch (K) {
 | |
|   case RAW:
 | |
|     break;
 | |
|   case ELF:
 | |
|   case MachO:
 | |
|     // Start the table with a NUL byte.
 | |
|     StringTable += '\x00';
 | |
|     break;
 | |
|   case WinCOFF:
 | |
|     // Make room to write the table size later.
 | |
|     StringTable.append(4, '\x00');
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   StringRef Previous;
 | |
|   for (StringOffsetPair *P : Strings) {
 | |
|     StringRef S = P->first.Val;
 | |
|     if (K == WinCOFF)
 | |
|       assert(S.size() > COFF::NameSize && "Short string in COFF string table!");
 | |
| 
 | |
|     if (Optimize && Previous.endswith(S)) {
 | |
|       size_t Pos = StringTable.size() - S.size() - (K != RAW);
 | |
|       if (!(Pos & (Alignment - 1))) {
 | |
|         P->second = Pos;
 | |
|         continue;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (Optimize) {
 | |
|       size_t Start = alignTo(StringTable.size(), Alignment);
 | |
|       P->second = Start;
 | |
|       StringTable.append(Start - StringTable.size(), '\0');
 | |
|     } else {
 | |
|       assert(P->second == StringTable.size() &&
 | |
|              "different strtab offset after finalization");
 | |
|     }
 | |
| 
 | |
|     StringTable += S;
 | |
|     if (K != RAW)
 | |
|       StringTable += '\x00';
 | |
|     Previous = S;
 | |
|   }
 | |
| 
 | |
|   switch (K) {
 | |
|   case RAW:
 | |
|   case ELF:
 | |
|     break;
 | |
|   case MachO:
 | |
|     // Pad to multiple of 4.
 | |
|     while (StringTable.size() % 4)
 | |
|       StringTable += '\x00';
 | |
|     break;
 | |
|   case WinCOFF:
 | |
|     // Write the table size in the first word.
 | |
|     assert(StringTable.size() <= std::numeric_limits<uint32_t>::max());
 | |
|     uint32_t Size = static_cast<uint32_t>(StringTable.size());
 | |
|     support::endian::write<uint32_t, support::little, support::unaligned>(
 | |
|         StringTable.data(), Size);
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   Size = StringTable.size();
 | |
| }
 | |
| 
 | |
| void StringTableBuilder::clear() {
 | |
|   StringTable.clear();
 | |
|   StringIndexMap.clear();
 | |
| }
 | |
| 
 | |
| size_t StringTableBuilder::getOffset(StringRef 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(StringRef S) {
 | |
|   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;
 | |
| }
 |