[COFF] De-virtualize Chunk and SectionChunk

Shaves another pointer off of SectionChunk, reducing the size from 96 to
88 bytes, down from 144 before I started working on this. Combined with
D62356, this reduced peak memory usage when linking chrome_child.dll
from 713MB to 675MB, or 5%.

Create NonSectionChunk to provide virtual dispatch to the rest of the
chunk types.

Reviewers: ruiu, aganea

Differential Revision: https://reviews.llvm.org/D62362

llvm-svn: 361667
This commit is contained in:
Reid Kleckner 2019-05-24 20:25:40 +00:00
parent 96f02a8db8
commit a431dd7ae7
4 changed files with 169 additions and 71 deletions

View File

@ -43,6 +43,8 @@ SectionChunk::SectionChunk(ObjFile *F, const coff_section *H)
setAlignment(Header->getAlignment());
HasData = !(Header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA);
// If linker GC is disabled, every chunk starts out alive. If linker GC is
// enabled, treat non-comdat sections as roots. Generally optimized object
// files will be built with -ffunction-sections or /Gy, so most things worth
@ -53,7 +55,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) <= 96, "SectionChunk grew unexpectedly");
static_assert(sizeof(SectionChunk) <= 88, "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); }
@ -559,14 +561,6 @@ void SectionChunk::getRuntimePseudoRelocs(
}
}
bool SectionChunk::hasData() const {
return !(Header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA);
}
uint32_t SectionChunk::getOutputCharacteristics() const {
return Header->Characteristics & (PermMask | TypeMask);
}
bool SectionChunk::isCOMDAT() const {
return Header->Characteristics & IMAGE_SCN_LNK_COMDAT;
}
@ -578,7 +572,7 @@ void SectionChunk::printDiscardedMessage() const {
message("Discarded " + Sym->getName());
}
StringRef SectionChunk::getDebugName() {
StringRef SectionChunk::getDebugName() const {
if (Sym)
return Sym->getName();
return "";
@ -642,6 +636,7 @@ CommonChunk::CommonChunk(const COFFSymbolRef S) : Sym(S) {
// than 32 bytes naturally, i.e. round the size up to the next power of two.
// This is what MSVC link.exe does.
setAlignment(std::min(32U, uint32_t(PowerOf2Ceil(Sym.getValue()))));
HasData = false;
}
uint32_t CommonChunk::getOutputCharacteristics() const {
@ -858,7 +853,7 @@ uint8_t Baserel::getDefaultType() {
MergeChunk *MergeChunk::Instances[Log2MaxSectionAlignment + 1] = {};
MergeChunk::MergeChunk(uint32_t Alignment)
: Chunk(OtherKind), Builder(StringTableBuilder::RAW, Alignment) {
: Builder(StringTableBuilder::RAW, Alignment) {
setAlignment(Alignment);
}

View File

@ -13,6 +13,7 @@
#include "InputFiles.h"
#include "lld/Common/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/MC/StringTableBuilder.h"
@ -55,14 +56,16 @@ class Chunk {
public:
enum Kind : uint8_t { SectionKind, OtherKind };
Kind kind() const { return ChunkKind; }
virtual ~Chunk() = default;
// Returns the size of this chunk (even if this is a common or BSS.)
virtual size_t getSize() const = 0;
size_t getSize() const;
// Returns chunk alignment in power of two form. Value values are powers of
// two from 1 to 8192.
uint32_t getAlignment() const { return 1U << P2Align; }
// Update the chunk section alignment measured in bytes. Internally alignment
// is stored in log2.
void setAlignment(uint32_t Align) {
// Treat zero byte alignment as 1 byte alignment.
Align = Align ? Align : 1;
@ -76,7 +79,7 @@ public:
// beginning of the file. Because this function may use RVA values
// of other chunks for relocations, you need to set them properly
// before calling this function.
virtual void writeTo(uint8_t *Buf) const {}
void writeTo(uint8_t *Buf) const;
// The writer sets and uses the addresses. In practice, PE images cannot be
// larger than 2GB. Chunks are always laid as part of the image, so Chunk RVAs
@ -90,16 +93,14 @@ public:
// Returns true if this has non-zero data. BSS chunks return
// false. If false is returned, the space occupied by this chunk
// will be filled with zeros.
virtual bool hasData() const { return true; }
bool hasData() const { return HasData; }
// Returns readable/writable/executable bits.
virtual uint32_t getOutputCharacteristics() const { return 0; }
uint32_t getOutputCharacteristics() const;
// Returns the section name if this is a section chunk.
// It is illegal to call this function on non-section chunks.
virtual StringRef getSectionName() const {
llvm_unreachable("unimplemented getSectionName");
}
StringRef getSectionName() const;
// An output section has pointers to chunks in the section, and each
// chunk has a back pointer to an output section.
@ -109,22 +110,29 @@ public:
// Windows-specific.
// Collect all locations that contain absolute addresses for base relocations.
virtual void getBaserels(std::vector<Baserel> *Res) {}
void getBaserels(std::vector<Baserel> *Res);
// Returns a human-readable name of this chunk. Chunks are unnamed chunks of
// bytes, so this is used only for logging or debugging.
virtual StringRef getDebugName() { return ""; }
StringRef getDebugName() const;
virtual bool isHotPatchable() const { return false; }
// Return true if this file has the hotpatch flag set to true in the
// S_COMPILE3 record in codeview debug info. Also returns true for some thunks
// synthesized by the linker.
bool isHotPatchable() const;
protected:
Chunk(Kind K = OtherKind) : ChunkKind(K) {}
Chunk(Kind K = OtherKind) : ChunkKind(K), HasData(true), P2Align(0) {}
const Kind ChunkKind;
// True if the section has data. Corresponds to the
// IMAGE_SCN_CNT_UNINITIALIZED_DATA section characteristic bit.
uint8_t HasData : 1;
// The alignment of this chunk, stored in log2 form. The writer uses the
// value.
uint8_t P2Align = 0;
uint8_t P2Align : 7;
// The output section index for this chunk. The first valid section number is
// one.
@ -134,6 +142,46 @@ protected:
uint32_t RVA = 0;
};
class NonSectionChunk : public Chunk {
public:
virtual ~NonSectionChunk() = default;
// Returns the size of this chunk (even if this is a common or BSS.)
virtual size_t getSize() const = 0;
virtual uint32_t getOutputCharacteristics() const { return 0; }
// Write this chunk to a mmap'ed file, assuming Buf is pointing to
// beginning of the file. Because this function may use RVA values
// of other chunks for relocations, you need to set them properly
// before calling this function.
virtual void writeTo(uint8_t *Buf) const {}
// Returns the section name if this is a section chunk.
// It is illegal to call this function on non-section chunks.
virtual StringRef getSectionName() const {
llvm_unreachable("unimplemented getSectionName");
}
// Windows-specific.
// Collect all locations that contain absolute addresses for base relocations.
virtual void getBaserels(std::vector<Baserel> *Res) {}
// Return true if this file has the hotpatch flag set to true in the
// S_COMPILE3 record in codeview debug info. Also returns true for some thunks
// synthesized by the linker.
virtual bool isHotPatchable() const { return false; }
// Returns a human-readable name of this chunk. Chunks are unnamed chunks of
// bytes, so this is used only for logging or debugging.
virtual StringRef getDebugName() const { return ""; }
static bool classof(const Chunk *C) { return C->kind() == OtherKind; }
protected:
NonSectionChunk() : Chunk(OtherKind) {}
};
// A chunk corresponding a section of an input file.
class SectionChunk final : public Chunk {
// Identical COMDAT Folding feature accesses section internal data.
@ -158,15 +206,17 @@ public:
SectionChunk(ObjFile *File, const coff_section *Header);
static bool classof(const Chunk *C) { return C->kind() == SectionKind; }
size_t getSize() const override { return Header->SizeOfRawData; }
size_t getSize() const { return Header->SizeOfRawData; }
ArrayRef<uint8_t> getContents() const;
void writeTo(uint8_t *Buf) const override;
bool hasData() const override;
uint32_t getOutputCharacteristics() const override;
StringRef getSectionName() const override {
void writeTo(uint8_t *Buf) const;
uint32_t getOutputCharacteristics() const {
return Header->Characteristics & (PermMask | TypeMask);
}
StringRef getSectionName() const {
return StringRef(SectionNameData, SectionNameSize);
}
void getBaserels(std::vector<Baserel> *Res) override;
void getBaserels(std::vector<Baserel> *Res);
bool isCOMDAT() const;
void applyRelX64(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S,
uint64_t P) const;
@ -187,7 +237,7 @@ public:
// and its children are treated as a group by the garbage collector.
void addAssociative(SectionChunk *Child);
StringRef getDebugName() override;
StringRef getDebugName() const;
// 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.
@ -200,6 +250,8 @@ public:
return getSectionName().startswith(".debug_") || getSectionName() == ".eh_frame";
}
bool isHotPatchable() const { return File->HotPatchable; }
// 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, RelocsData),
@ -257,8 +309,6 @@ public:
static SectionChunk *findByName(ArrayRef<SectionChunk *> Sections,
StringRef Name);
bool isHotPatchable() const override { return File->HotPatchable; }
// The file that this chunk was created from.
ObjFile *File;
@ -305,6 +355,58 @@ private:
uint32_t SectionNameSize = 0;
};
// Inline methods to implement faux-virtual dispatch for SectionChunk.
inline size_t Chunk::getSize() const {
if (isa<SectionChunk>(this))
return static_cast<const SectionChunk *>(this)->getSize();
else
return static_cast<const NonSectionChunk *>(this)->getSize();
}
inline uint32_t Chunk::getOutputCharacteristics() const {
if (isa<SectionChunk>(this))
return static_cast<const SectionChunk *>(this)->getOutputCharacteristics();
else
return static_cast<const NonSectionChunk *>(this)
->getOutputCharacteristics();
}
inline void Chunk::writeTo(uint8_t *Buf) const {
if (isa<SectionChunk>(this))
static_cast<const SectionChunk *>(this)->writeTo(Buf);
else
static_cast<const NonSectionChunk *>(this)->writeTo(Buf);
}
inline bool Chunk::isHotPatchable() const {
if (isa<SectionChunk>(this))
return static_cast<const SectionChunk *>(this)->isHotPatchable();
else
return static_cast<const NonSectionChunk *>(this)->isHotPatchable();
}
inline StringRef Chunk::getSectionName() const {
if (isa<SectionChunk>(this))
return static_cast<const SectionChunk *>(this)->getSectionName();
else
return static_cast<const NonSectionChunk *>(this)->getSectionName();
}
inline void Chunk::getBaserels(std::vector<Baserel> *Res) {
if (isa<SectionChunk>(this))
static_cast<SectionChunk *>(this)->getBaserels(Res);
else
static_cast<NonSectionChunk *>(this)->getBaserels(Res);
}
inline StringRef Chunk::getDebugName() const {
if (isa<SectionChunk>(this))
return static_cast<const SectionChunk *>(this)->getDebugName();
else
return static_cast<const NonSectionChunk *>(this)->getDebugName();
}
// This class is used to implement an lld-specific feature (not implemented in
// MSVC) that minimizes the output size by finding string literals sharing tail
// parts and merging them.
@ -314,7 +416,7 @@ private:
// The MergeChunk then tail merges the strings using the StringTableBuilder
// class and assigns RVAs and section offsets to each of the member chunks based
// on the offsets assigned by the StringTableBuilder.
class MergeChunk : public Chunk {
class MergeChunk : public NonSectionChunk {
public:
MergeChunk(uint32_t Alignment);
static void addSection(SectionChunk *C);
@ -335,11 +437,10 @@ private:
};
// A chunk for common symbols. Common chunks don't have actual data.
class CommonChunk : public Chunk {
class CommonChunk : public NonSectionChunk {
public:
CommonChunk(const COFFSymbolRef Sym);
size_t getSize() const override { return Sym.getValue(); }
bool hasData() const override { return false; }
uint32_t getOutputCharacteristics() const override;
StringRef getSectionName() const override { return ".bss"; }
@ -348,7 +449,7 @@ private:
};
// A chunk for linker-created strings.
class StringChunk : public Chunk {
class StringChunk : public NonSectionChunk {
public:
explicit StringChunk(StringRef S) : Str(S) {}
size_t getSize() const override { return Str.size() + 1; }
@ -377,7 +478,7 @@ static const uint8_t ImportThunkARM64[] = {
// Windows-specific.
// A chunk for DLL import jump table entry. In a final output, its
// contents will be a JMP instruction to some __imp_ symbol.
class ImportThunkChunkX64 : public Chunk {
class ImportThunkChunkX64 : public NonSectionChunk {
public:
explicit ImportThunkChunkX64(Defined *S);
size_t getSize() const override { return sizeof(ImportThunkX86); }
@ -389,9 +490,10 @@ private:
Defined *ImpSymbol;
};
class ImportThunkChunkX86 : public Chunk {
class ImportThunkChunkX86 : public NonSectionChunk {
public:
explicit ImportThunkChunkX86(Defined *S) : ImpSymbol(S) {}
explicit ImportThunkChunkX86(Defined *S) : ImpSymbol(S) {
}
size_t getSize() const override { return sizeof(ImportThunkX86); }
void getBaserels(std::vector<Baserel> *Res) override;
void writeTo(uint8_t *Buf) const override;
@ -402,9 +504,10 @@ private:
Defined *ImpSymbol;
};
class ImportThunkChunkARM : public Chunk {
class ImportThunkChunkARM : public NonSectionChunk {
public:
explicit ImportThunkChunkARM(Defined *S) : ImpSymbol(S) {}
explicit ImportThunkChunkARM(Defined *S) : ImpSymbol(S) {
}
size_t getSize() const override { return sizeof(ImportThunkARM); }
void getBaserels(std::vector<Baserel> *Res) override;
void writeTo(uint8_t *Buf) const override;
@ -415,9 +518,10 @@ private:
Defined *ImpSymbol;
};
class ImportThunkChunkARM64 : public Chunk {
class ImportThunkChunkARM64 : public NonSectionChunk {
public:
explicit ImportThunkChunkARM64(Defined *S) : ImpSymbol(S) {}
explicit ImportThunkChunkARM64(Defined *S) : ImpSymbol(S) {
}
size_t getSize() const override { return sizeof(ImportThunkARM64); }
void writeTo(uint8_t *Buf) const override;
@ -427,7 +531,7 @@ private:
Defined *ImpSymbol;
};
class RangeExtensionThunkARM : public Chunk {
class RangeExtensionThunkARM : public NonSectionChunk {
public:
explicit RangeExtensionThunkARM(Defined *T) : Target(T) {}
size_t getSize() const override;
@ -436,7 +540,7 @@ public:
Defined *Target;
};
class RangeExtensionThunkARM64 : public Chunk {
class RangeExtensionThunkARM64 : public NonSectionChunk {
public:
explicit RangeExtensionThunkARM64(Defined *T) : Target(T) {}
size_t getSize() const override;
@ -447,7 +551,7 @@ public:
// Windows-specific.
// See comments for DefinedLocalImport class.
class LocalImportChunk : public Chunk {
class LocalImportChunk : public NonSectionChunk {
public:
explicit LocalImportChunk(Defined *S) : Sym(S) {
setAlignment(Config->Wordsize);
@ -487,7 +591,7 @@ struct ChunkAndOffset {
using SymbolRVASet = llvm::DenseSet<ChunkAndOffset>;
// Table which contains symbol RVAs. Used for /safeseh and /guard:cf.
class RVATableChunk : public Chunk {
class RVATableChunk : public NonSectionChunk {
public:
explicit RVATableChunk(SymbolRVASet S) : Syms(std::move(S)) {}
size_t getSize() const override { return Syms.size() * 4; }
@ -500,7 +604,7 @@ private:
// Windows-specific.
// This class represents a block in .reloc section.
// See the PE/COFF spec 5.6 for details.
class BaserelChunk : public Chunk {
class BaserelChunk : public NonSectionChunk {
public:
BaserelChunk(uint32_t Page, Baserel *Begin, Baserel *End);
size_t getSize() const override { return Data.size(); }
@ -524,7 +628,7 @@ public:
// specific place in a section, without any data. This is used for the MinGW
// specific symbol __RUNTIME_PSEUDO_RELOC_LIST_END__, even though the concept
// of an empty chunk isn't MinGW specific.
class EmptyChunk : public Chunk {
class EmptyChunk : public NonSectionChunk {
public:
EmptyChunk() {}
size_t getSize() const override { return 0; }
@ -537,7 +641,7 @@ public:
// the reference didn't use the dllimport attribute. The MinGW runtime will
// process this table after loading, before handling control over to user
// code.
class PseudoRelocTableChunk : public Chunk {
class PseudoRelocTableChunk : public NonSectionChunk {
public:
PseudoRelocTableChunk(std::vector<RuntimePseudoReloc> &Relocs)
: Relocs(std::move(Relocs)) {
@ -568,7 +672,7 @@ public:
};
// MinGW specific. A Chunk that contains one pointer-sized absolute value.
class AbsolutePointerChunk : public Chunk {
class AbsolutePointerChunk : public NonSectionChunk {
public:
AbsolutePointerChunk(uint64_t Value) : Value(Value) {
setAlignment(getSize());

View File

@ -35,7 +35,7 @@ namespace {
// Import table
// A chunk for the import descriptor table.
class HintNameChunk : public Chunk {
class HintNameChunk : public NonSectionChunk {
public:
HintNameChunk(StringRef N, uint16_t H) : Name(N), Hint(H) {}
@ -57,7 +57,7 @@ private:
};
// A chunk for the import descriptor table.
class LookupChunk : public Chunk {
class LookupChunk : public NonSectionChunk {
public:
explicit LookupChunk(Chunk *C) : HintName(C) {
setAlignment(Config->Wordsize);
@ -77,7 +77,7 @@ public:
// A chunk for the import descriptor table.
// This chunk represent import-by-ordinal symbols.
// See Microsoft PE/COFF spec 7.1. Import Header for details.
class OrdinalOnlyChunk : public Chunk {
class OrdinalOnlyChunk : public NonSectionChunk {
public:
explicit OrdinalOnlyChunk(uint16_t V) : Ordinal(V) {
setAlignment(Config->Wordsize);
@ -98,7 +98,7 @@ public:
};
// A chunk for the import descriptor table.
class ImportDirectoryChunk : public Chunk {
class ImportDirectoryChunk : public NonSectionChunk {
public:
explicit ImportDirectoryChunk(Chunk *N) : DLLName(N) {}
size_t getSize() const override { return sizeof(ImportDirectoryTableEntry); }
@ -119,10 +119,9 @@ public:
// A chunk representing null terminator in the import table.
// Contents of this chunk is always null bytes.
class NullChunk : public Chunk {
class NullChunk : public NonSectionChunk {
public:
explicit NullChunk(size_t N) : Size(N) {}
bool hasData() const override { return false; }
explicit NullChunk(size_t N) : Size(N) { HasData = false; }
size_t getSize() const override { return Size; }
void writeTo(uint8_t *Buf) const override {
@ -162,7 +161,7 @@ binImports(const std::vector<DefinedImportData *> &Imports) {
// See Microsoft PE/COFF spec 4.3 for details.
// A chunk for the delay import descriptor table etnry.
class DelayDirectoryChunk : public Chunk {
class DelayDirectoryChunk : public NonSectionChunk {
public:
explicit DelayDirectoryChunk(Chunk *N) : DLLName(N) {}
@ -274,7 +273,7 @@ static const uint8_t ThunkARM64[] = {
};
// A chunk for the delay import thunk.
class ThunkChunkX64 : public Chunk {
class ThunkChunkX64 : public NonSectionChunk {
public:
ThunkChunkX64(Defined *I, Chunk *D, Defined *H)
: Imp(I), Desc(D), Helper(H) {}
@ -293,7 +292,7 @@ public:
Defined *Helper = nullptr;
};
class ThunkChunkX86 : public Chunk {
class ThunkChunkX86 : public NonSectionChunk {
public:
ThunkChunkX86(Defined *I, Chunk *D, Defined *H)
: Imp(I), Desc(D), Helper(H) {}
@ -317,7 +316,7 @@ public:
Defined *Helper = nullptr;
};
class ThunkChunkARM : public Chunk {
class ThunkChunkARM : public NonSectionChunk {
public:
ThunkChunkARM(Defined *I, Chunk *D, Defined *H)
: Imp(I), Desc(D), Helper(H) {}
@ -341,7 +340,7 @@ public:
Defined *Helper = nullptr;
};
class ThunkChunkARM64 : public Chunk {
class ThunkChunkARM64 : public NonSectionChunk {
public:
ThunkChunkARM64(Defined *I, Chunk *D, Defined *H)
: Imp(I), Desc(D), Helper(H) {}
@ -363,7 +362,7 @@ public:
};
// A chunk for the import descriptor table.
class DelayAddressChunk : public Chunk {
class DelayAddressChunk : public NonSectionChunk {
public:
explicit DelayAddressChunk(Chunk *C) : Thunk(C) {
setAlignment(Config->Wordsize);
@ -393,7 +392,7 @@ public:
// Read Microsoft PE/COFF spec 5.3 for details.
// A chunk for the export descriptor table.
class ExportDirectoryChunk : public Chunk {
class ExportDirectoryChunk : public NonSectionChunk {
public:
ExportDirectoryChunk(int I, int J, Chunk *D, Chunk *A, Chunk *N, Chunk *O)
: MaxOrdinal(I), NameTabSize(J), DLLName(D), AddressTab(A), NameTab(N),
@ -424,7 +423,7 @@ public:
Chunk *OrdinalTab;
};
class AddressTableChunk : public Chunk {
class AddressTableChunk : public NonSectionChunk {
public:
explicit AddressTableChunk(size_t MaxOrdinal) : Size(MaxOrdinal + 1) {}
size_t getSize() const override { return Size * 4; }
@ -450,7 +449,7 @@ private:
size_t Size;
};
class NamePointersChunk : public Chunk {
class NamePointersChunk : public NonSectionChunk {
public:
explicit NamePointersChunk(std::vector<Chunk *> &V) : Chunks(V) {}
size_t getSize() const override { return Chunks.size() * 4; }
@ -466,7 +465,7 @@ private:
std::vector<Chunk *> Chunks;
};
class ExportOrdinalChunk : public Chunk {
class ExportOrdinalChunk : public NonSectionChunk {
public:
explicit ExportOrdinalChunk(size_t I) : Size(I) {}
size_t getSize() const override { return Size * 2; }

View File

@ -88,7 +88,7 @@ OutputSection *Chunk::getOutputSection() const {
namespace {
class DebugDirectoryChunk : public Chunk {
class DebugDirectoryChunk : public NonSectionChunk {
public:
DebugDirectoryChunk(const std::vector<Chunk *> &R, bool WriteRepro)
: Records(R), WriteRepro(WriteRepro) {}
@ -143,7 +143,7 @@ private:
bool WriteRepro;
};
class CVDebugRecordChunk : public Chunk {
class CVDebugRecordChunk : public NonSectionChunk {
public:
size_t getSize() const override {
return sizeof(codeview::DebugInfo) + Config->PDBAltPath.size() + 1;