mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-14 01:56:05 +00:00

This patch adds support for generating accelerator tables in dsymutil. This feature was already present in our internal repository but not yet upstreamed because it requires changes to the Apple accelerator table implementation. Differential revision: https://reviews.llvm.org/D42501 llvm-svn: 323655
236 lines
8.3 KiB
C++
236 lines
8.3 KiB
C++
//===- llvm/CodeGen/AsmPrinter/AccelTable.cpp - Accelerator Tables --------===//
|
|
//
|
|
// 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 accelerator tables.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/CodeGen/AccelTable.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/BinaryFormat/Dwarf.h"
|
|
#include "llvm/CodeGen/AsmPrinter.h"
|
|
#include "llvm/CodeGen/DIE.h"
|
|
#include "llvm/MC/MCExpr.h"
|
|
#include "llvm/MC/MCStreamer.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <algorithm>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <limits>
|
|
#include <vector>
|
|
|
|
using namespace llvm;
|
|
|
|
void AppleAccelTableHeader::emit(AsmPrinter *Asm) {
|
|
// Emit Header.
|
|
Asm->OutStreamer->AddComment("Header Magic");
|
|
Asm->EmitInt32(Header.Magic);
|
|
Asm->OutStreamer->AddComment("Header Version");
|
|
Asm->EmitInt16(Header.Version);
|
|
Asm->OutStreamer->AddComment("Header Hash Function");
|
|
Asm->EmitInt16(Header.HashFunction);
|
|
Asm->OutStreamer->AddComment("Header Bucket Count");
|
|
Asm->EmitInt32(Header.BucketCount);
|
|
Asm->OutStreamer->AddComment("Header Hash Count");
|
|
Asm->EmitInt32(Header.HashCount);
|
|
Asm->OutStreamer->AddComment("Header Data Length");
|
|
Asm->EmitInt32(Header.HeaderDataLength);
|
|
|
|
// Emit Header Data
|
|
Asm->OutStreamer->AddComment("HeaderData Die Offset Base");
|
|
Asm->EmitInt32(HeaderData.DieOffsetBase);
|
|
Asm->OutStreamer->AddComment("HeaderData Atom Count");
|
|
Asm->EmitInt32(HeaderData.Atoms.size());
|
|
|
|
for (size_t i = 0; i < HeaderData.Atoms.size(); i++) {
|
|
Atom A = HeaderData.Atoms[i];
|
|
Asm->OutStreamer->AddComment(dwarf::AtomTypeString(A.Type));
|
|
Asm->EmitInt16(A.Type);
|
|
Asm->OutStreamer->AddComment(dwarf::FormEncodingString(A.Form));
|
|
Asm->EmitInt16(A.Form);
|
|
}
|
|
}
|
|
|
|
void AppleAccelTableHeader::setBucketAndHashCount(uint32_t HashCount) {
|
|
if (HashCount > 1024)
|
|
Header.BucketCount = HashCount / 4;
|
|
else if (HashCount > 16)
|
|
Header.BucketCount = HashCount / 2;
|
|
else
|
|
Header.BucketCount = HashCount > 0 ? HashCount : 1;
|
|
|
|
Header.HashCount = HashCount;
|
|
}
|
|
|
|
constexpr AppleAccelTableHeader::Atom AppleAccelTableTypeData::Atoms[];
|
|
constexpr AppleAccelTableHeader::Atom AppleAccelTableOffsetData::Atoms[];
|
|
constexpr AppleAccelTableHeader::Atom AppleAccelTableStaticOffsetData::Atoms[];
|
|
constexpr AppleAccelTableHeader::Atom AppleAccelTableStaticTypeData::Atoms[];
|
|
|
|
void AppleAccelTableBase::emitHeader(AsmPrinter *Asm) { Header.emit(Asm); }
|
|
|
|
void AppleAccelTableBase::emitBuckets(AsmPrinter *Asm) {
|
|
unsigned index = 0;
|
|
for (size_t i = 0, e = Buckets.size(); i < e; ++i) {
|
|
Asm->OutStreamer->AddComment("Bucket " + Twine(i));
|
|
if (!Buckets[i].empty())
|
|
Asm->EmitInt32(index);
|
|
else
|
|
Asm->EmitInt32(std::numeric_limits<uint32_t>::max());
|
|
// Buckets point in the list of hashes, not to the data. Do not increment
|
|
// the index multiple times in case of hash collisions.
|
|
uint64_t PrevHash = std::numeric_limits<uint64_t>::max();
|
|
for (auto *HD : Buckets[i]) {
|
|
uint32_t HashValue = HD->HashValue;
|
|
if (PrevHash != HashValue)
|
|
++index;
|
|
PrevHash = HashValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
void AppleAccelTableBase::emitHashes(AsmPrinter *Asm) {
|
|
uint64_t PrevHash = std::numeric_limits<uint64_t>::max();
|
|
unsigned BucketIdx = 0;
|
|
for (auto &Bucket : Buckets) {
|
|
for (auto &Hash : Bucket) {
|
|
uint32_t HashValue = Hash->HashValue;
|
|
if (PrevHash == HashValue)
|
|
continue;
|
|
Asm->OutStreamer->AddComment("Hash in Bucket " + Twine(BucketIdx));
|
|
Asm->EmitInt32(HashValue);
|
|
PrevHash = HashValue;
|
|
}
|
|
BucketIdx++;
|
|
}
|
|
}
|
|
|
|
void AppleAccelTableBase::emitOffsets(AsmPrinter *Asm,
|
|
const MCSymbol *SecBegin) {
|
|
uint64_t PrevHash = std::numeric_limits<uint64_t>::max();
|
|
for (size_t i = 0, e = Buckets.size(); i < e; ++i) {
|
|
for (auto HI = Buckets[i].begin(), HE = Buckets[i].end(); HI != HE; ++HI) {
|
|
uint32_t HashValue = (*HI)->HashValue;
|
|
if (PrevHash == HashValue)
|
|
continue;
|
|
PrevHash = HashValue;
|
|
Asm->OutStreamer->AddComment("Offset in Bucket " + Twine(i));
|
|
MCContext &Context = Asm->OutStreamer->getContext();
|
|
const MCExpr *Sub = MCBinaryExpr::createSub(
|
|
MCSymbolRefExpr::create((*HI)->Sym, Context),
|
|
MCSymbolRefExpr::create(SecBegin, Context), Context);
|
|
Asm->OutStreamer->EmitValue(Sub, sizeof(uint32_t));
|
|
}
|
|
}
|
|
}
|
|
|
|
void AppleAccelTableBase::emitData(AsmPrinter *Asm) {
|
|
for (size_t i = 0, e = Buckets.size(); i < e; ++i) {
|
|
uint64_t PrevHash = std::numeric_limits<uint64_t>::max();
|
|
for (auto &Hash : Buckets[i]) {
|
|
// Terminate the previous entry if there is no hash collision with the
|
|
// current one.
|
|
if (PrevHash != std::numeric_limits<uint64_t>::max() &&
|
|
PrevHash != Hash->HashValue)
|
|
Asm->EmitInt32(0);
|
|
// Remember to emit the label for our offset.
|
|
Asm->OutStreamer->EmitLabel(Hash->Sym);
|
|
Asm->OutStreamer->AddComment(Hash->Str);
|
|
Asm->emitDwarfStringOffset(Hash->Data.Name);
|
|
Asm->OutStreamer->AddComment("Num DIEs");
|
|
Asm->EmitInt32(Hash->Data.Values.size());
|
|
for (const auto *V : Hash->Data.Values) {
|
|
V->emit(Asm);
|
|
}
|
|
PrevHash = Hash->HashValue;
|
|
}
|
|
// Emit the final end marker for the bucket.
|
|
if (!Buckets[i].empty())
|
|
Asm->EmitInt32(0);
|
|
}
|
|
}
|
|
|
|
void AppleAccelTableBase::computeBucketCount() {
|
|
// First get the number of unique hashes.
|
|
std::vector<uint32_t> uniques(Data.size());
|
|
for (size_t i = 0, e = Data.size(); i < e; ++i)
|
|
uniques[i] = Data[i]->HashValue;
|
|
array_pod_sort(uniques.begin(), uniques.end());
|
|
std::vector<uint32_t>::iterator p =
|
|
std::unique(uniques.begin(), uniques.end());
|
|
|
|
// Compute the hashes count and use it to set that together with the bucket
|
|
// count in the header.
|
|
Header.setBucketAndHashCount(std::distance(uniques.begin(), p));
|
|
}
|
|
|
|
void AppleAccelTableBase::finalizeTable(AsmPrinter *Asm, StringRef Prefix) {
|
|
// Create the individual hash data outputs.
|
|
Data.reserve(Entries.size());
|
|
for (auto &E : Entries) {
|
|
// Unique the entries.
|
|
std::stable_sort(E.second.Values.begin(), E.second.Values.end(),
|
|
[](const AppleAccelTableData *A,
|
|
const AppleAccelTableData *B) { return *A < *B; });
|
|
E.second.Values.erase(
|
|
std::unique(E.second.Values.begin(), E.second.Values.end()),
|
|
E.second.Values.end());
|
|
|
|
HashData *Entry = new (Allocator) HashData(E.first(), E.second);
|
|
Data.push_back(Entry);
|
|
}
|
|
|
|
// Figure out how many buckets we need, then compute the bucket contents and
|
|
// the final ordering. We'll emit the hashes and offsets by doing a walk
|
|
// during the emission phase. We add temporary symbols to the data so that we
|
|
// can reference them during the offset later, we'll emit them when we emit
|
|
// the data.
|
|
computeBucketCount();
|
|
|
|
// Compute bucket contents and final ordering.
|
|
Buckets.resize(Header.getBucketCount());
|
|
for (auto &D : Data) {
|
|
uint32_t bucket = D->HashValue % Header.getBucketCount();
|
|
Buckets[bucket].push_back(D);
|
|
D->Sym = Asm->createTempSymbol(Prefix);
|
|
}
|
|
|
|
// Sort the contents of the buckets by hash value so that hash collisions end
|
|
// up together. Stable sort makes testing easier and doesn't cost much more.
|
|
for (auto &Bucket : Buckets)
|
|
std::stable_sort(Bucket.begin(), Bucket.end(),
|
|
[](HashData *LHS, HashData *RHS) {
|
|
return LHS->HashValue < RHS->HashValue;
|
|
});
|
|
}
|
|
|
|
void AppleAccelTableOffsetData::emit(AsmPrinter *Asm) const {
|
|
Asm->EmitInt32(Die->getDebugSectionOffset());
|
|
}
|
|
|
|
void AppleAccelTableTypeData::emit(AsmPrinter *Asm) const {
|
|
Asm->EmitInt32(Die->getDebugSectionOffset());
|
|
Asm->EmitInt16(Die->getTag());
|
|
Asm->EmitInt8(0);
|
|
}
|
|
|
|
void AppleAccelTableStaticOffsetData::emit(AsmPrinter *Asm) const {
|
|
Asm->EmitInt32(Offset);
|
|
}
|
|
|
|
void AppleAccelTableStaticTypeData::emit(AsmPrinter *Asm) const {
|
|
Asm->EmitInt32(Offset);
|
|
Asm->EmitInt16(Tag);
|
|
Asm->EmitInt8(ObjCClassIsImplementation ? dwarf::DW_FLAG_type_implementation
|
|
: 0);
|
|
Asm->EmitInt32(QualifiedNameHash);
|
|
}
|