[COFF] Drop unused comdat sections when GC is turned off

Summary:
Adds a "Discarded" bool to SectionChunk to indicate if the section was
discarded by COMDAT deduplication. The Writer still just checks
`isLive()`.

Fixes PR33446

Reviewers: ruiu

Subscribers: llvm-commits

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

llvm-svn: 305582
This commit is contained in:
Reid Kleckner 2017-06-16 20:47:19 +00:00
parent f1b9f3a23f
commit 79ac99b3e8
6 changed files with 98 additions and 6 deletions

View File

@ -37,8 +37,14 @@ SectionChunk::SectionChunk(ObjectFile *F, const coff_section *H)
Align = Header->getAlignment();
// Only COMDAT sections are subject of dead-stripping.
Live = !isCOMDAT();
// Chunks may be discarded during comdat merging.
Discarded = false;
// 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
// stripping will be in a comdat.
Live = !Config->DoGC || !isCOMDAT();
}
static void add16(uint8_t *P, int16_t V) { write16le(P, read16le(P) + V); }
@ -226,8 +232,12 @@ bool SectionChunk::isCOMDAT() const {
void SectionChunk::printDiscardedMessage() const {
// Removed by dead-stripping. If it's removed by ICF, ICF already
// printed out the name, so don't repeat that here.
if (Sym && this == Repl)
message("Discarded " + Sym->getName());
if (Sym && this == Repl) {
if (Discarded)
message("Discarded comdat symbol " + Sym->getName());
else if (!Live)
message("Discarded " + Sym->getName());
}
}
StringRef SectionChunk::getDebugName() {

View File

@ -159,13 +159,23 @@ public:
StringRef getDebugName() override;
void setSymbol(DefinedRegular *S) { if (!Sym) Sym = S; }
// Returns true if the chunk was not dropped by GC or COMDAT deduplication.
bool isLive() { return Live && !Discarded; }
// Used by the garbage collector.
bool isLive() { return !Config->DoGC || Live; }
void markLive() {
assert(Config->DoGC && "should only mark things live from GC");
assert(!isLive() && "Cannot mark an already live section!");
Live = true;
}
// Returns true if this chunk was dropped by COMDAT deduplication.
bool isDiscarded() const { return Discarded; }
// Used by the SymbolTable when discarding unused comdat sections. This is
// redundant when GC is enabled, as all comdat sections will start out dead.
void markDiscarded() { Discarded = true; }
// 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()),
@ -196,6 +206,9 @@ private:
llvm::iterator_range<const coff_relocation *> Relocs;
size_t NumRelocs;
// True if this chunk was discarded because it was a duplicate comdat section.
bool Discarded;
// Used by the garbage collector.
bool Live;

View File

@ -249,8 +249,12 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
auto *Aux = reinterpret_cast<const coff_aux_section_definition *>(AuxP);
if (Aux->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
if (auto *ParentSC = cast_or_null<SectionChunk>(
SparseChunks[Aux->getNumber(Sym.isBigObj())]))
SparseChunks[Aux->getNumber(Sym.isBigObj())])) {
ParentSC->addAssociative(SC);
// If we already discarded the parent, discard the child.
if (ParentSC->isDiscarded())
SC->markDiscarded();
}
SC->Checksum = Aux->CheckSum;
}

View File

@ -244,6 +244,12 @@ Symbol *SymbolTable::addRegular(InputFile *F, StringRef N, bool IsCOMDAT,
reportDuplicate(S, F);
} else if (SP == SP_NEW) {
replaceBody<DefinedRegular>(S, F, N, IsCOMDAT, /*IsExternal*/ true, Sym, C);
} else if (SP == SP_EXISTING && IsCOMDAT && C) {
C->markDiscarded();
// Discard associative chunks that we've parsed so far. No need to recurse
// because an associative section cannot have children.
for (SectionChunk *Child : C->children())
Child->markDiscarded();
}
return S;
}

View File

@ -0,0 +1,13 @@
# Defines foo and foo_assoc globals. foo is comdat, and foo_assoc is comdat
# associative with it. foo_assoc should be discarded iff foo is discarded,
# either by linker GC or normal comdat merging.
.section .rdata,"dr",associative,foo
.p2align 3
.quad foo
.section .data,"dw",discard,foo
.globl foo # @foo
.p2align 2
foo:
.long 42

View File

@ -0,0 +1,46 @@
# RUN: llvm-mc %s -filetype=obj -o %t1.obj
# RUN: llvm-mc %S/Inputs/associative-comdat-2.s -filetype=obj -o %t2.obj
# RUN: lld-link -entry:main %t1.obj %t2.obj -out:%t.gc.exe
# RUN: llvm-readobj -sections %t.gc.exe | FileCheck %s
# RUN: lld-link -entry:main %t1.obj %t2.obj -opt:noref -out:%t.nogc.exe
# RUN: llvm-readobj -sections %t.nogc.exe | FileCheck %s
# CHECK: Sections [
# CHECK: Section {
# CHECK: Number: 1
# CHECK-LABEL: Name: .data (2E 64 61 74 61 00 00 00)
# CHECK-NEXT: VirtualSize: 0x4
# CHECK: Section {
# CHECK-LABEL: Name: .rdata (2E 72 64 61 74 61 00 00)
# This is the critical check to show that only *one* definition of
# foo_assoc was retained. This *must* be 8, not 16.
# CHECK-NEXT: VirtualSize: 0x8
.text
.def main;
.scl 2;
.type 32;
.endef
.globl main # -- Begin function main
.p2align 4, 0x90
main: # @main
# BB#0:
movl foo(%rip), %eax
retq
# -- End function
# Defines foo and foo_assoc globals. foo is comdat, and foo_assoc is comdat
# associative with it. foo_assoc should be discarded iff foo is discarded,
# either by linker GC or normal comdat merging.
.section .rdata,"dr",associative,foo
.p2align 3
.quad foo
.section .data,"dw",discard,foo
.globl foo # @foo
.p2align 2
foo:
.long 42