COFF: Devirtualize mark(), markLive() and isCOMDAT().
Only SectionChunk can be dead-stripped. Previously, all types of chunks implemented these functions, but their functions were blank. Likewise, only DefinedRegular and DefinedCOMDAT symbols can be dead-stripped. markLive() function was implemented for other symbol types, but they were blank. I started thinking that the change I made in r240319 was a mistake. I separated DefinedCOMDAT from DefinedRegular because I thought that would make the code cleaner, but now we want to handle them as the same type here. Maybe we should roll it back. This change should improve readability a bit as this removes some dubious uses of reinterpret_cast. Previously, we assumed that all COMDAT chunks are actually SectionChunks, which was not very obvious. llvm-svn: 240675
This commit is contained in:
parent
6bbffc4b97
commit
fc510f4cf8
|
|
@ -28,7 +28,7 @@ namespace lld {
|
||||||
namespace coff {
|
namespace coff {
|
||||||
|
|
||||||
SectionChunk::SectionChunk(ObjectFile *F, const coff_section *H)
|
SectionChunk::SectionChunk(ObjectFile *F, const coff_section *H)
|
||||||
: File(F), Ptr(this), Header(H),
|
: Chunk(SectionKind), File(F), Ptr(this), Header(H),
|
||||||
Relocs(File->getCOFFObj()->getRelocations(Header)),
|
Relocs(File->getCOFFObj()->getRelocations(Header)),
|
||||||
NumRelocs(std::distance(Relocs.begin(), Relocs.end())) {
|
NumRelocs(std::distance(Relocs.begin(), Relocs.end())) {
|
||||||
// Initialize SectionName.
|
// Initialize SectionName.
|
||||||
|
|
@ -39,16 +39,9 @@ SectionChunk::SectionChunk(ObjectFile *F, const coff_section *H)
|
||||||
if (Shift > 0)
|
if (Shift > 0)
|
||||||
Align = uint32_t(1) << (Shift - 1);
|
Align = uint32_t(1) << (Shift - 1);
|
||||||
|
|
||||||
// When a new chunk is created, we don't if if it's going to make it
|
|
||||||
// to the final output. Initially all sections are unmarked in terms
|
|
||||||
// of garbage collection. The writer will call markLive() to mark
|
|
||||||
// all reachable section chunks.
|
|
||||||
Live = false;
|
|
||||||
|
|
||||||
// COMDAT sections are not GC root. Non-text sections are not
|
// COMDAT sections are not GC root. Non-text sections are not
|
||||||
// subject of garbage collection (thus they are root).
|
// subject of garbage collection (thus they are root).
|
||||||
if (!isCOMDAT() && !(Header->Characteristics & IMAGE_SCN_CNT_CODE))
|
Root = !isCOMDAT() && !(Header->Characteristics & IMAGE_SCN_CNT_CODE);
|
||||||
Root = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add16(uint8_t *P, int16_t V) { write16le(P, read16le(P) + V); }
|
static void add16(uint8_t *P, int16_t V) { write16le(P, read16le(P) + V); }
|
||||||
|
|
@ -93,13 +86,17 @@ void SectionChunk::mark() {
|
||||||
// Mark all symbols listed in the relocation table for this section.
|
// Mark all symbols listed in the relocation table for this section.
|
||||||
for (const coff_relocation &Rel : Relocs) {
|
for (const coff_relocation &Rel : Relocs) {
|
||||||
SymbolBody *B = File->getSymbolBody(Rel.SymbolTableIndex);
|
SymbolBody *B = File->getSymbolBody(Rel.SymbolTableIndex);
|
||||||
if (auto *Def = dyn_cast<Defined>(B))
|
if (auto *D = dyn_cast<DefinedRegular>(B)) {
|
||||||
Def->markLive();
|
D->markLive();
|
||||||
|
} else if (auto *D = dyn_cast<DefinedCOMDAT>(B)) {
|
||||||
|
D->markLive();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark associative sections if any.
|
// Mark associative sections if any.
|
||||||
for (Chunk *C : AssocChildren)
|
for (Chunk *C : AssocChildren)
|
||||||
C->markLive();
|
if (auto *SC = dyn_cast<SectionChunk>(C))
|
||||||
|
SC->markLive();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SectionChunk::addAssociative(SectionChunk *Child) {
|
void SectionChunk::addAssociative(SectionChunk *Child) {
|
||||||
|
|
@ -139,7 +136,7 @@ bool SectionChunk::isCOMDAT() const {
|
||||||
return Header->Characteristics & IMAGE_SCN_LNK_COMDAT;
|
return Header->Characteristics & IMAGE_SCN_LNK_COMDAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SectionChunk::printDiscardedMessage() {
|
void SectionChunk::printDiscardedMessage() const {
|
||||||
if (this == Ptr) {
|
if (this == Ptr) {
|
||||||
// Removed by dead-stripping.
|
// Removed by dead-stripping.
|
||||||
llvm::dbgs() << "Discarded " << Sym->getName() << "\n";
|
llvm::dbgs() << "Discarded " << Sym->getName() << "\n";
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,8 @@ class OutputSection;
|
||||||
// doesn't even have actual data (if common or bss).
|
// doesn't even have actual data (if common or bss).
|
||||||
class Chunk {
|
class Chunk {
|
||||||
public:
|
public:
|
||||||
|
enum Kind { SectionKind, OtherKind };
|
||||||
|
Kind kind() const { return ChunkKind; }
|
||||||
virtual ~Chunk() = default;
|
virtual ~Chunk() = default;
|
||||||
|
|
||||||
// Returns the size of this chunk (even if this is a common or BSS.)
|
// Returns the size of this chunk (even if this is a common or BSS.)
|
||||||
|
|
@ -71,24 +73,6 @@ public:
|
||||||
llvm_unreachable("unimplemented getSectionName");
|
llvm_unreachable("unimplemented getSectionName");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called if the garbage collector decides to not include this chunk
|
|
||||||
// in a final output. It's supposed to print out a log message to stdout.
|
|
||||||
// It is illegal to call this function on non-section chunks because
|
|
||||||
// only section chunks are subject of garbage collection.
|
|
||||||
virtual void printDiscardedMessage() {
|
|
||||||
llvm_unreachable("unimplemented printDiscardedMessage");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if this is a COMDAT section. Usually, it is an error
|
|
||||||
// if there are more than one defined symbols having the same name,
|
|
||||||
// but symbols at begining of COMDAT sections allowed to duplicate.
|
|
||||||
virtual bool isCOMDAT() const { return false; }
|
|
||||||
|
|
||||||
// Used by the garbage collector.
|
|
||||||
bool isRoot() { return Root; }
|
|
||||||
bool isLive() { return Live; }
|
|
||||||
void markLive() { if (!Live) mark(); }
|
|
||||||
|
|
||||||
// An output section has pointers to chunks in the section, and each
|
// An output section has pointers to chunks in the section, and each
|
||||||
// chunk has a back pointer to an output section.
|
// chunk has a back pointer to an output section.
|
||||||
void setOutputSection(OutputSection *O) { Out = O; }
|
void setOutputSection(OutputSection *O) { Out = O; }
|
||||||
|
|
@ -103,6 +87,9 @@ public:
|
||||||
virtual StringRef getDebugName() { return ""; }
|
virtual StringRef getDebugName() { return ""; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
Chunk(Kind K = OtherKind) : ChunkKind(K) {}
|
||||||
|
const Kind ChunkKind;
|
||||||
|
|
||||||
// The RVA of this chunk in the output. The writer sets a value.
|
// The RVA of this chunk in the output. The writer sets a value.
|
||||||
uint64_t RVA = 0;
|
uint64_t RVA = 0;
|
||||||
|
|
||||||
|
|
@ -114,25 +101,24 @@ protected:
|
||||||
|
|
||||||
// The alignment of this chunk. The writer uses the value.
|
// The alignment of this chunk. The writer uses the value.
|
||||||
uint32_t Align = 1;
|
uint32_t Align = 1;
|
||||||
|
|
||||||
// Used by the garbage collector.
|
|
||||||
virtual void mark() {}
|
|
||||||
bool Live = true;
|
|
||||||
bool Root = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// A chunk corresponding a section of an input file.
|
// A chunk corresponding a section of an input file.
|
||||||
class SectionChunk : public Chunk {
|
class SectionChunk : public Chunk {
|
||||||
public:
|
public:
|
||||||
SectionChunk(ObjectFile *File, const coff_section *Header);
|
SectionChunk(ObjectFile *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 override { return Header->SizeOfRawData; }
|
||||||
void writeTo(uint8_t *Buf) override;
|
void writeTo(uint8_t *Buf) override;
|
||||||
bool hasData() const override;
|
bool hasData() const override;
|
||||||
uint32_t getPermissions() const override;
|
uint32_t getPermissions() const override;
|
||||||
StringRef getSectionName() const override { return SectionName; }
|
StringRef getSectionName() const override { return SectionName; }
|
||||||
void printDiscardedMessage() override;
|
|
||||||
bool isCOMDAT() const override;
|
|
||||||
void getBaserels(std::vector<uint32_t> *Res, Defined *ImageBase) override;
|
void getBaserels(std::vector<uint32_t> *Res, Defined *ImageBase) override;
|
||||||
|
bool isCOMDAT() const;
|
||||||
|
|
||||||
|
// Called if the garbage collector decides to not include this chunk
|
||||||
|
// in a final output. It's supposed to print out a log message to stdout.
|
||||||
|
void printDiscardedMessage() const;
|
||||||
|
|
||||||
// Adds COMDAT associative sections to this COMDAT section. A chunk
|
// Adds COMDAT associative sections to this COMDAT section. A chunk
|
||||||
// and its children are treated as a group by the garbage collector.
|
// and its children are treated as a group by the garbage collector.
|
||||||
|
|
@ -141,15 +127,18 @@ public:
|
||||||
StringRef getDebugName() override;
|
StringRef getDebugName() override;
|
||||||
void setSymbol(DefinedCOMDAT *S) { if (!Sym) Sym = S; }
|
void setSymbol(DefinedCOMDAT *S) { if (!Sym) Sym = S; }
|
||||||
|
|
||||||
uint64_t getHash() const;
|
// Used by the garbage collector.
|
||||||
bool equals(const SectionChunk *Other) const;
|
bool isRoot() { return Root; }
|
||||||
|
bool isLive() { return Live; }
|
||||||
|
void markLive() { if (!Live) mark(); }
|
||||||
|
|
||||||
// Used for ICF (Identical COMDAT Folding)
|
// Used for ICF (Identical COMDAT Folding)
|
||||||
SectionChunk *repl();
|
SectionChunk *repl();
|
||||||
void replaceWith(SectionChunk *Other);
|
void replaceWith(SectionChunk *Other);
|
||||||
|
uint64_t getHash() const;
|
||||||
|
bool equals(const SectionChunk *Other) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void mark() override;
|
|
||||||
ArrayRef<uint8_t> getContents() const;
|
ArrayRef<uint8_t> getContents() const;
|
||||||
|
|
||||||
// A file this chunk was created from.
|
// A file this chunk was created from.
|
||||||
|
|
@ -167,6 +156,11 @@ private:
|
||||||
llvm::iterator_range<const coff_relocation *> Relocs;
|
llvm::iterator_range<const coff_relocation *> Relocs;
|
||||||
size_t NumRelocs;
|
size_t NumRelocs;
|
||||||
|
|
||||||
|
// Used by the garbage collector.
|
||||||
|
void mark();
|
||||||
|
bool Live = false;
|
||||||
|
bool Root;
|
||||||
|
|
||||||
// Chunks are basically unnamed chunks of bytes.
|
// Chunks are basically unnamed chunks of bytes.
|
||||||
// Symbols are associated for debugging and logging purposs only.
|
// Symbols are associated for debugging and logging purposs only.
|
||||||
DefinedCOMDAT *Sym = nullptr;
|
DefinedCOMDAT *Sym = nullptr;
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,9 @@ void doICF(const std::vector<Chunk *> &Chunks) {
|
||||||
std::unordered_set<SectionChunk *, Hasher, Equals> Set;
|
std::unordered_set<SectionChunk *, Hasher, Equals> Set;
|
||||||
bool removed = false;
|
bool removed = false;
|
||||||
for (Chunk *C : Chunks) {
|
for (Chunk *C : Chunks) {
|
||||||
if (!C->isCOMDAT() || !C->isLive())
|
auto *SC = dyn_cast<SectionChunk>(C);
|
||||||
|
if (!SC || !SC->isCOMDAT() || !SC->isLive())
|
||||||
continue;
|
continue;
|
||||||
auto *SC = reinterpret_cast<SectionChunk *>(C);
|
|
||||||
auto P = Set.insert(SC);
|
auto P = Set.insert(SC);
|
||||||
bool Inserted = P.second;
|
bool Inserted = P.second;
|
||||||
if (Inserted)
|
if (Inserted)
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,7 @@ SymbolBody *ObjectFile::createSymbolBody(COFFSymbolRef Sym, const void *AuxP,
|
||||||
return new (Alloc) Undefined(Name);
|
return new (Alloc) Undefined(Name);
|
||||||
}
|
}
|
||||||
if (Sym.isCommon()) {
|
if (Sym.isCommon()) {
|
||||||
Chunk *C = new (Alloc) CommonChunk(Sym);
|
auto *C = new (Alloc) CommonChunk(Sym);
|
||||||
Chunks.push_back(C);
|
Chunks.push_back(C);
|
||||||
return new (Alloc) DefinedCommon(COFFObj.get(), Sym, C);
|
return new (Alloc) DefinedCommon(COFFObj.get(), Sym, C);
|
||||||
}
|
}
|
||||||
|
|
@ -213,10 +213,10 @@ SymbolBody *ObjectFile::createSymbolBody(COFFSymbolRef Sym, const void *AuxP,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Chunk *C = SparseChunks[Sym.getSectionNumber()]) {
|
Chunk *C = SparseChunks[Sym.getSectionNumber()];
|
||||||
if (!C->isCOMDAT())
|
if (auto *SC = cast_or_null<SectionChunk>(C)) {
|
||||||
return new (Alloc) DefinedRegular(COFFObj.get(), Sym, C);
|
if (!SC->isCOMDAT())
|
||||||
auto *SC = reinterpret_cast<SectionChunk *>(C);
|
return new (Alloc) DefinedRegular(COFFObj.get(), Sym, SC);
|
||||||
auto *B = new (Alloc) DefinedCOMDAT(COFFObj.get(), Sym, SC);
|
auto *B = new (Alloc) DefinedCOMDAT(COFFObj.get(), Sym, SC);
|
||||||
if (Sym.getValue() == 0 && !AuxP)
|
if (Sym.getValue() == 0 && !AuxP)
|
||||||
SC->setSymbol(B);
|
SC->setSymbol(B);
|
||||||
|
|
|
||||||
|
|
@ -108,17 +108,13 @@ public:
|
||||||
// The writer uses this information to apply relocations.
|
// The writer uses this information to apply relocations.
|
||||||
virtual uint64_t getFileOff() = 0;
|
virtual uint64_t getFileOff() = 0;
|
||||||
|
|
||||||
// Called by the garbage collector. All Defined subclasses should
|
|
||||||
// know how to call depending symbols' markLive functions.
|
|
||||||
virtual void markLive() {}
|
|
||||||
|
|
||||||
int compare(SymbolBody *Other) override;
|
int compare(SymbolBody *Other) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Regular defined symbols read from object file symbol tables.
|
// Regular defined symbols read from object file symbol tables.
|
||||||
class DefinedRegular : public Defined {
|
class DefinedRegular : public Defined {
|
||||||
public:
|
public:
|
||||||
DefinedRegular(COFFObjectFile *F, COFFSymbolRef S, Chunk *C)
|
DefinedRegular(COFFObjectFile *F, COFFSymbolRef S, SectionChunk *C)
|
||||||
: Defined(DefinedRegularKind), COFFFile(F), Sym(S), Data(C) {}
|
: Defined(DefinedRegularKind), COFFFile(F), Sym(S), Data(C) {}
|
||||||
|
|
||||||
static bool classof(const SymbolBody *S) {
|
static bool classof(const SymbolBody *S) {
|
||||||
|
|
@ -128,15 +124,15 @@ public:
|
||||||
StringRef getName() override;
|
StringRef getName() override;
|
||||||
uint64_t getRVA() override { return Data->getRVA() + Sym.getValue(); }
|
uint64_t getRVA() override { return Data->getRVA() + Sym.getValue(); }
|
||||||
bool isExternal() override { return Sym.isExternal(); }
|
bool isExternal() override { return Sym.isExternal(); }
|
||||||
void markLive() override { Data->markLive(); }
|
|
||||||
uint64_t getFileOff() override { return Data->getFileOff() + Sym.getValue(); }
|
uint64_t getFileOff() override { return Data->getFileOff() + Sym.getValue(); }
|
||||||
int compare(SymbolBody *Other) override;
|
int compare(SymbolBody *Other) override;
|
||||||
|
void markLive() { Data->markLive(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
StringRef Name;
|
StringRef Name;
|
||||||
COFFObjectFile *COFFFile;
|
COFFObjectFile *COFFFile;
|
||||||
COFFSymbolRef Sym;
|
COFFSymbolRef Sym;
|
||||||
Chunk *Data;
|
SectionChunk *Data;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DefinedCOMDAT : public Defined {
|
class DefinedCOMDAT : public Defined {
|
||||||
|
|
@ -155,8 +151,8 @@ public:
|
||||||
StringRef getName() override;
|
StringRef getName() override;
|
||||||
uint64_t getRVA() override { return Data->repl()->getRVA() + Sym.getValue(); }
|
uint64_t getRVA() override { return Data->repl()->getRVA() + Sym.getValue(); }
|
||||||
bool isExternal() override { return Sym.isExternal(); }
|
bool isExternal() override { return Sym.isExternal(); }
|
||||||
void markLive() override { Data->repl()->markLive(); }
|
|
||||||
int compare(SymbolBody *Other) override;
|
int compare(SymbolBody *Other) override;
|
||||||
|
void markLive() { Data->repl()->markLive(); }
|
||||||
Chunk *getChunk() { return Data->repl(); }
|
Chunk *getChunk() { return Data->repl(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -168,7 +164,7 @@ private:
|
||||||
|
|
||||||
class DefinedCommon : public Defined {
|
class DefinedCommon : public Defined {
|
||||||
public:
|
public:
|
||||||
DefinedCommon(COFFObjectFile *F, COFFSymbolRef S, Chunk *C)
|
DefinedCommon(COFFObjectFile *F, COFFSymbolRef S, CommonChunk *C)
|
||||||
: Defined(DefinedCommonKind), COFFFile(F), Sym(S), Data(C) {}
|
: Defined(DefinedCommonKind), COFFFile(F), Sym(S), Data(C) {}
|
||||||
|
|
||||||
static bool classof(const SymbolBody *S) {
|
static bool classof(const SymbolBody *S) {
|
||||||
|
|
@ -178,7 +174,6 @@ public:
|
||||||
StringRef getName() override;
|
StringRef getName() override;
|
||||||
uint64_t getRVA() override { return Data->getRVA(); }
|
uint64_t getRVA() override { return Data->getRVA(); }
|
||||||
bool isExternal() override { return Sym.isExternal(); }
|
bool isExternal() override { return Sym.isExternal(); }
|
||||||
void markLive() override { Data->markLive(); }
|
|
||||||
uint64_t getFileOff() override { return Data->getFileOff(); }
|
uint64_t getFileOff() override { return Data->getFileOff(); }
|
||||||
int compare(SymbolBody *Other) override;
|
int compare(SymbolBody *Other) override;
|
||||||
|
|
||||||
|
|
@ -188,7 +183,7 @@ private:
|
||||||
StringRef Name;
|
StringRef Name;
|
||||||
COFFObjectFile *COFFFile;
|
COFFObjectFile *COFFFile;
|
||||||
COFFSymbolRef Sym;
|
COFFSymbolRef Sym;
|
||||||
Chunk *Data;
|
CommonChunk *Data;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Absolute symbols.
|
// Absolute symbols.
|
||||||
|
|
|
||||||
|
|
@ -111,11 +111,18 @@ void OutputSection::writeHeaderTo(uint8_t *Buf) {
|
||||||
void Writer::markLive() {
|
void Writer::markLive() {
|
||||||
if (!Config->DoGC)
|
if (!Config->DoGC)
|
||||||
return;
|
return;
|
||||||
for (StringRef Name : Config->GCRoots)
|
for (StringRef Name : Config->GCRoots) {
|
||||||
cast<Defined>(Symtab->find(Name))->markLive();
|
SymbolBody *B = Symtab->find(Name);
|
||||||
|
if (auto *D = dyn_cast<DefinedRegular>(B)) {
|
||||||
|
D->markLive();
|
||||||
|
} else if (auto *D = dyn_cast<DefinedCOMDAT>(B)) {
|
||||||
|
D->markLive();
|
||||||
|
}
|
||||||
|
}
|
||||||
for (Chunk *C : Symtab->getChunks())
|
for (Chunk *C : Symtab->getChunks())
|
||||||
if (C->isRoot())
|
if (auto *SC = dyn_cast<SectionChunk>(C))
|
||||||
C->markLive();
|
if (SC->isRoot())
|
||||||
|
SC->markLive();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Hasher {
|
struct Hasher {
|
||||||
|
|
@ -139,11 +146,14 @@ void Writer::createSections() {
|
||||||
// First, bin chunks by name.
|
// First, bin chunks by name.
|
||||||
std::map<StringRef, std::vector<Chunk *>> Map;
|
std::map<StringRef, std::vector<Chunk *>> Map;
|
||||||
for (Chunk *C : Symtab->getChunks()) {
|
for (Chunk *C : Symtab->getChunks()) {
|
||||||
if (Config->DoGC && !C->isLive()) {
|
if (Config->DoGC) {
|
||||||
|
auto *SC = dyn_cast<SectionChunk>(C);
|
||||||
|
if (SC && !SC->isLive()) {
|
||||||
if (Config->Verbose)
|
if (Config->Verbose)
|
||||||
C->printDiscardedMessage();
|
SC->printDiscardedMessage();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Map[C->getSectionName()].push_back(C);
|
Map[C->getSectionName()].push_back(C);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue