Index: lld/trunk/COFF/ICF.cpp =================================================================== --- lld/trunk/COFF/ICF.cpp +++ lld/trunk/COFF/ICF.cpp @@ -45,6 +45,8 @@ 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 @@ 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 @@ } } +// 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 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 @@ 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 @@ 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. Index: lld/trunk/test/COFF/icf-pdata.s =================================================================== --- lld/trunk/test/COFF/icf-pdata.s +++ lld/trunk/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"