diff options
| author | Peter Collingbourne <peter@pcc.me.uk> | 2018-05-12 02:12:40 +0000 |
|---|---|---|
| committer | Peter Collingbourne <peter@pcc.me.uk> | 2018-05-12 02:12:40 +0000 |
| commit | 107f55005bc9c9de2378057f56ae02016795a3ae (patch) | |
| tree | 683122212bdc814715ac976decded224d25e22a1 | |
| parent | a41660df7e555bbd3a5ced17a99ffe75f267ec14 (diff) | |
| download | bcm5719-llvm-107f55005bc9c9de2378057f56ae02016795a3ae.tar.gz bcm5719-llvm-107f55005bc9c9de2378057f56ae02016795a3ae.zip | |
COFF: ICF a section and its associated sections as a unit.
This is needed to avoid merging two functions with identical
instructions but different xdata. It also reduces binary size by
deduplicating identical pdata sections.
Fixes PR35337.
Differential Revision: https://reviews.llvm.org/D46672
llvm-svn: 332169
| -rw-r--r-- | lld/COFF/ICF.cpp | 27 | ||||
| -rw-r--r-- | lld/test/COFF/icf-pdata.s | 97 |
2 files changed, 120 insertions, 4 deletions
diff --git a/lld/COFF/ICF.cpp b/lld/COFF/ICF.cpp index e967374fe1e..78d667fa704 100644 --- a/lld/COFF/ICF.cpp +++ b/lld/COFF/ICF.cpp @@ -45,6 +45,8 @@ public: private: void segregate(size_t Begin, size_t End, bool Constant); + bool assocEquals(const SectionChunk *A, const SectionChunk *B); + bool equalsConstant(const SectionChunk *A, const SectionChunk *B); bool equalsVariable(const SectionChunk *A, const SectionChunk *B); @@ -91,8 +93,9 @@ bool ICF::isEligible(SectionChunk *C) { if (C->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE) return true; - // .xdata unwind info sections are eligible. - if (C->getSectionName().split('$').first == ".xdata") + // .pdata and .xdata unwind info sections are eligible. + StringRef OutSecName = C->getSectionName().split('$').first; + if (OutSecName == ".pdata" || OutSecName == ".xdata") return true; // So are vtables. @@ -125,6 +128,19 @@ void ICF::segregate(size_t Begin, size_t End, bool Constant) { } } +// Returns true if two sections' associative children are equal. +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") + Classes.push_back(C->Class[Cnt % 2]); + return Classes; + }; + return ChildClasses(A) == ChildClasses(B); +} + // Compare "non-moving" part of two sections, namely everything // except relocation targets. bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) { @@ -154,7 +170,8 @@ bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) { return A->getOutputCharacteristics() == B->getOutputCharacteristics() && A->SectionName == B->SectionName && A->Alignment == B->Alignment && A->Header->SizeOfRawData == B->Header->SizeOfRawData && - A->Checksum == B->Checksum && A->getContents() == B->getContents(); + A->Checksum == B->Checksum && A->getContents() == B->getContents() && + assocEquals(A, B); } // Compare "moving" part of two sections, namely relocation targets. @@ -170,7 +187,9 @@ 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->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), + Eq) && + assocEquals(A, B); } // Find the first Chunk after Begin that has a different class from Begin. diff --git a/lld/test/COFF/icf-pdata.s b/lld/test/COFF/icf-pdata.s new file mode 100644 index 00000000000..ebd8479af8f --- /dev/null +++ b/lld/test/COFF/icf-pdata.s @@ -0,0 +1,97 @@ +# RUN: llvm-mc %s -triple x86_64-windows-msvc -filetype=obj -o %t.obj +# RUN: lld-link %t.obj -dll -noentry -out:%t.dll -merge:.xdata=.xdata +# RUN: llvm-readobj -sections -coff-exports %t.dll | FileCheck %s + +# CHECK: Name: .pdata +# CHECK-NEXT: VirtualSize: 0x18 +# CHECK: Name: .xdata +# CHECK-NEXT: VirtualSize: 0x10 + +# CHECK: Name: xdata1 +# CHECK-NEXT: RVA: 0x1010 +# CHECK: Name: xdata1a +# CHECK-NEXT: RVA: 0x1010 +# CHECK: Name: xdata1b +# CHECK-NEXT: RVA: 0x1030 + + .text +callee: + ret + + .def xdata1; + .scl 2; + .type 32; + .endef + .section .text,"xr",one_only,xdata1 + .globl xdata1 # -- Begin function xdata1 + .p2align 4, 0x90 +xdata1: # @xdata1 +.seh_proc xdata1 +# BB#0: # %entry + subq $40, %rsp + .seh_stackalloc 40 + .seh_endprologue + callq callee + nop + addq $40, %rsp + jmp callee # TAILCALL + .seh_handlerdata + .section .text,"xr",one_only,xdata1 + .seh_endproc + # -- End function + +# xdata1a is identical to xdata1, so it should be ICFd, and so should its pdata. +# It also has associative debug and CFG sections which should be ignored by ICF. + .def xdata1a; + .scl 2; + .type 32; + .endef + .section .text,"xr",one_only,xdata1a + .globl xdata1a # -- Begin function xdata1a + .p2align 4, 0x90 +xdata1a: # @xdata1a +.seh_proc xdata1a +# BB#0: # %entry + subq $40, %rsp + .seh_stackalloc 40 + .seh_endprologue + callq callee + nop + addq $40, %rsp + jmp callee # TAILCALL + .seh_handlerdata + .section .text,"xr",one_only,xdata1a + .seh_endproc + + .section .debug$S,"r",associative,xdata1a + .section .gfids$y,"r",associative,xdata1a + .section .gljmp$y,"r",associative,xdata1a + +# xdata1b's text is identical to xdata1, but its xdata specifies a different +# stack size, so it cannot be ICFd with xdata1. + .def xdata1b; + .scl 2; + .type 32; + .endef + .section .text,"xr",one_only,xdata1b + .globl xdata1b # -- Begin function xdata1b + .p2align 4, 0x90 +xdata1b: # @xdata1b +.seh_proc xdata1b +# BB#0: # %entry + subq $40, %rsp + .seh_stackalloc 48 + .seh_endprologue + callq callee + nop + addq $40, %rsp + jmp callee # TAILCALL + .seh_handlerdata + .section .text,"xr",one_only,xdata1b + .seh_endproc + # -- End function + + .section .drectve,"yn" + .ascii " -export:xdata1" + .ascii " -export:xdata1a" + .ascii " -export:xdata1b" |

