Shrink SectionChunk by combining Relocs and SectionName sizes

SectionChunk is one of the most frequently allocated data structures in
LLD, since there are about four per function when optimizations and
debug info are enabled (.text, .pdata, .xdata, .debug$S).

A PE COFF file cannot be larger than 2GB, so there is an inherent limit
on the length of the section name and the number of relocations.
Decompose the ArrayRef and StringRef into pointer and size, and put them
back together in the accessors for section name and relocation list.

I plan to gather complete performance numbers later by padding
SectionChunk with dead data and measuring performance after all the size
optimizations are done.

llvm-svn: 359923
This commit is contained in:
Reid Kleckner 2019-05-03 20:17:14 +00:00
parent a857e31011
commit 0a1b1d6e62
6 changed files with 60 additions and 34 deletions

View File

@ -30,11 +30,16 @@ namespace lld {
namespace coff {
SectionChunk::SectionChunk(ObjFile *F, const coff_section *H)
: Chunk(SectionKind), File(F), Header(H),
Relocs(File->getCOFFObj()->getRelocations(Header)), Repl(this) {
: Chunk(SectionKind), File(F), Header(H), Repl(this) {
// Initialize Relocs.
setRelocs(File->getCOFFObj()->getRelocations(Header));
// Initialize SectionName.
StringRef SectionName;
if (Expected<StringRef> E = File->getCOFFObj()->getSectionName(Header))
SectionName = *E;
SectionNameData = SectionName.data();
SectionNameSize = SectionName.size();
Alignment = Header->getAlignment();
@ -48,7 +53,7 @@ SectionChunk::SectionChunk(ObjFile *F, const coff_section *H)
// SectionChunk is one of the most frequently allocated classes, so it is
// important to keep it as compact as possible. As of this writing, the number
// below is the size of this class on x64 platforms.
static_assert(sizeof(SectionChunk) <= 128, "SectionChunk grew unexpectedly");
static_assert(sizeof(SectionChunk) <= 120, "SectionChunk grew unexpectedly");
static void add16(uint8_t *P, int16_t V) { write16le(P, read16le(P) + V); }
static void add32(uint8_t *P, int32_t V) { write32le(P, read32le(P) + V); }
@ -343,8 +348,8 @@ void SectionChunk::writeTo(uint8_t *Buf) const {
// Apply relocations.
size_t InputSize = getSize();
for (size_t I = 0, E = Relocs.size(); I < E; I++) {
const coff_relocation &Rel = Relocs[I];
for (size_t I = 0, E = RelocsSize; I < E; I++) {
const coff_relocation &Rel = RelocsData[I];
// Check for an invalid relocation offset. This check isn't perfect, because
// we don't have the relocation size, which is only known after checking the
@ -437,8 +442,8 @@ static uint8_t getBaserelType(const coff_relocation &Rel) {
// fixed by the loader if load-time relocation is needed.
// Only called when base relocation is enabled.
void SectionChunk::getBaserels(std::vector<Baserel> *Res) {
for (size_t I = 0, E = Relocs.size(); I < E; I++) {
const coff_relocation &Rel = Relocs[I];
for (size_t I = 0, E = RelocsSize; I < E; I++) {
const coff_relocation &Rel = RelocsData[I];
uint8_t Ty = getBaserelType(Rel);
if (Ty == IMAGE_REL_BASED_ABSOLUTE)
continue;
@ -534,7 +539,7 @@ static int getRuntimePseudoRelocSize(uint16_t Type) {
// imported from another DLL).
void SectionChunk::getRuntimePseudoRelocs(
std::vector<RuntimePseudoReloc> &Res) {
for (const coff_relocation &Rel : Relocs) {
for (const coff_relocation &Rel : getRelocs()) {
auto *Target =
dyn_cast_or_null<Defined>(File->getSymbol(Rel.SymbolTableIndex));
if (!Target || !Target->IsRuntimePseudoReloc)
@ -587,7 +592,7 @@ ArrayRef<uint8_t> SectionChunk::getContents() const {
ArrayRef<uint8_t> SectionChunk::consumeDebugMagic() {
assert(isCodeView());
return consumeDebugMagic(getContents(), SectionName);
return consumeDebugMagic(getContents(), getSectionName());
}
ArrayRef<uint8_t> SectionChunk::consumeDebugMagic(ArrayRef<uint8_t> Data,

View File

@ -153,7 +153,9 @@ public:
void writeTo(uint8_t *Buf) const override;
bool hasData() const override;
uint32_t getOutputCharacteristics() const override;
StringRef getSectionName() const override { return SectionName; }
StringRef getSectionName() const override {
return StringRef(SectionNameData, SectionNameSize);
}
void getBaserels(std::vector<Baserel> *Res) override;
bool isCOMDAT() const;
void applyRelX64(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S,
@ -180,18 +182,29 @@ public:
// True if this is a codeview debug info chunk. These will not be laid out in
// the image. Instead they will end up in the PDB, if one is requested.
bool isCodeView() const {
return SectionName == ".debug" || SectionName.startswith(".debug$");
return getSectionName() == ".debug" || getSectionName().startswith(".debug$");
}
// True if this is a DWARF debug info or exception handling chunk.
bool isDWARF() const {
return SectionName.startswith(".debug_") || SectionName == ".eh_frame";
return getSectionName().startswith(".debug_") || getSectionName() == ".eh_frame";
}
// Allow iteration over the bodies of this chunk's relocated symbols.
llvm::iterator_range<symbol_iterator> symbols() const {
return llvm::make_range(symbol_iterator(File, Relocs.begin()),
symbol_iterator(File, Relocs.end()));
return llvm::make_range(symbol_iterator(File, RelocsData),
symbol_iterator(File, RelocsData + RelocsSize));
}
ArrayRef<coff_relocation> getRelocs() const {
return llvm::makeArrayRef(RelocsData, RelocsSize);
}
// Reloc setter used by ARM range extension thunk insertion.
void setRelocs(ArrayRef<coff_relocation> NewRelocs) {
RelocsData = NewRelocs.data();
RelocsSize = NewRelocs.size();
assert(RelocsSize == NewRelocs.size() && "reloc size truncation");
}
// Single linked list iterator for associated comdat children.
@ -245,9 +258,6 @@ public:
// The COMDAT leader symbol if this is a COMDAT chunk.
DefinedRegular *Sym = nullptr;
// Relocations for this section.
ArrayRef<coff_relocation> Relocs;
// The CRC of the contents as described in the COFF spec 4.5.5.
// Auxiliary Format 5: Section Definitions. Used for ICF.
uint32_t Checksum = 0;
@ -265,12 +275,20 @@ public:
SectionChunk *Repl;
private:
StringRef SectionName;
SectionChunk *AssocChildren = nullptr;
// Used for ICF (Identical COMDAT Folding)
void replace(SectionChunk *Other);
uint32_t Class[2] = {0, 0};
// Relocations for this section. Size is stored below.
const coff_relocation *RelocsData;
// Section name string. Size is stored below.
const char *SectionNameData;
uint32_t RelocsSize = 0;
uint32_t SectionNameSize = 0;
};
// This class is used to implement an lld-specific feature (not implemented in

View File

@ -130,8 +130,8 @@ bool ICF::assocEquals(const SectionChunk *A, const SectionChunk *B) {
auto ChildClasses = [&](const SectionChunk *SC) {
std::vector<uint32_t> Classes;
for (const SectionChunk &C : SC->children())
if (!C.SectionName.startswith(".debug") &&
C.SectionName != ".gfids$y" && C.SectionName != ".gljmp$y")
if (!C.getSectionName().startswith(".debug") &&
C.getSectionName() != ".gfids$y" && C.getSectionName() != ".gljmp$y")
Classes.push_back(C.Class[Cnt % 2]);
return Classes;
};
@ -141,7 +141,7 @@ bool ICF::assocEquals(const SectionChunk *A, const SectionChunk *B) {
// Compare "non-moving" part of two sections, namely everything
// except relocation targets.
bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
if (A->Relocs.size() != B->Relocs.size())
if (A->RelocsSize != B->RelocsSize)
return false;
// Compare relocations.
@ -160,12 +160,13 @@ bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
D1->getChunk()->Class[Cnt % 2] == D2->getChunk()->Class[Cnt % 2];
return false;
};
if (!std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq))
if (!std::equal(A->getRelocs().begin(), A->getRelocs().end(),
B->getRelocs().begin(), Eq))
return false;
// Compare section attributes and contents.
return A->getOutputCharacteristics() == B->getOutputCharacteristics() &&
A->SectionName == B->SectionName &&
A->getSectionName() == B->getSectionName() &&
A->Header->SizeOfRawData == B->Header->SizeOfRawData &&
A->Checksum == B->Checksum && A->getContents() == B->getContents() &&
assocEquals(A, B);
@ -184,8 +185,8 @@ bool ICF::equalsVariable(const SectionChunk *A, const SectionChunk *B) {
return D1->getChunk()->Class[Cnt % 2] == D2->getChunk()->Class[Cnt % 2];
return false;
};
return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(),
Eq) &&
return std::equal(A->getRelocs().begin(), A->getRelocs().end(),
B->getRelocs().begin(), Eq) &&
assocEquals(A, B);
}

View File

@ -1744,7 +1744,7 @@ static bool findLineTable(const SectionChunk *C, uint32_t Addr,
// Build a mapping of SECREL relocations in DbgC that refer to C.
DenseMap<uint32_t, uint32_t> Secrels;
for (const coff_relocation &R : DbgC->Relocs) {
for (const coff_relocation &R : DbgC->getRelocs()) {
if (R.Type != SecrelReloc)
continue;

View File

@ -89,7 +89,7 @@ std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex) {
auto *SC = dyn_cast<SectionChunk>(C);
if (!SC)
continue;
for (const coff_relocation &R : SC->Relocs) {
for (const coff_relocation &R : SC->getRelocs()) {
if (R.SymbolTableIndex != SymIndex)
continue;
std::pair<StringRef, uint32_t> FileLine =
@ -183,7 +183,7 @@ bool SymbolTable::handleMinGWAutomaticImport(Symbol *Sym, StringRef Name) {
dyn_cast_or_null<DefinedRegular>(find((".refptr." + Name).str()));
if (Refptr && Refptr->getChunk()->getSize() == Config->Wordsize) {
SectionChunk *SC = dyn_cast_or_null<SectionChunk>(Refptr->getChunk());
if (SC && SC->Relocs.size() == 1 && *SC->symbols().begin() == Sym) {
if (SC && SC->getRelocs().size() == 1 && *SC->symbols().begin() == Sym) {
log("Replacing .refptr." + Name + " with " + Imp->getName());
Refptr->getChunk()->Live = false;
Refptr->replaceKeepingName(Imp, ImpSize);

View File

@ -467,14 +467,15 @@ static bool createThunks(OutputSection *OS, int Margin) {
// modified. If the relocations point into the object file, allocate new
// memory. Otherwise, this must be previously allocated memory that can be
// modified in place.
ArrayRef<coff_relocation> CurRelocs = SC->getRelocs();
MutableArrayRef<coff_relocation> NewRelocs;
if (OriginalRelocs.data() == SC->Relocs.data()) {
if (OriginalRelocs.data() == CurRelocs.data()) {
NewRelocs = makeMutableArrayRef(
BAlloc.Allocate<coff_relocation>(OriginalRelocs.size()),
OriginalRelocs.size());
} else {
NewRelocs = makeMutableArrayRef(
const_cast<coff_relocation *>(SC->Relocs.data()), SC->Relocs.size());
const_cast<coff_relocation *>(CurRelocs.data()), CurRelocs.size());
}
// Copy each relocation, but replace the symbol table indices which need
@ -489,7 +490,7 @@ static bool createThunks(OutputSection *OS, int Margin) {
}
}
SC->Relocs = makeArrayRef(NewRelocs.data(), NewRelocs.size());
SC->setRelocs(NewRelocs);
}
return AddressesChanged;
}
@ -501,8 +502,9 @@ static bool verifyRanges(const std::vector<Chunk *> Chunks) {
if (!SC)
continue;
for (size_t J = 0, E = SC->Relocs.size(); J < E; ++J) {
const coff_relocation &Rel = SC->Relocs[J];
ArrayRef<coff_relocation> Relocs = SC->getRelocs();
for (size_t J = 0, E = Relocs.size(); J < E; ++J) {
const coff_relocation &Rel = Relocs[J];
Symbol *RelocTarget = SC->File->getSymbol(Rel.SymbolTableIndex);
Defined *Sym = dyn_cast_or_null<Defined>(RelocTarget);
@ -1476,7 +1478,7 @@ static void markSymbolsWithRelocations(ObjFile *File,
if (!SC || !SC->Live)
continue;
for (const coff_relocation &Reloc : SC->Relocs) {
for (const coff_relocation &Reloc : SC->getRelocs()) {
if (Config->Machine == I386 && Reloc.Type == COFF::IMAGE_REL_I386_REL32)
// Ignore relative relocations on x86. On x86_64 they can't be ignored
// since they're also used to compute absolute addresses.