Index: ELF/ICF.cpp =================================================================== --- ELF/ICF.cpp +++ ELF/ICF.cpp @@ -75,6 +75,8 @@ #include "ICF.h" #include "Config.h" +#include "LinkerScript.h" +#include "OutputSections.h" #include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" @@ -305,10 +307,8 @@ return false; // If two sections have different output sections, we cannot merge them. - // FIXME: This doesn't do the right thing in the case where there is a linker - // script. We probably need to move output section assignment before ICF to - // get the correct behaviour here. - if (getOutputSectionName(A) != getOutputSectionName(B)) + if (A->Parent != B->Parent || + getOutputSectionName(A) != getOutputSectionName(B)) return false; if (A->AreRelocsRela) @@ -436,6 +436,15 @@ if (isEligible(S)) Sections.push_back(S); + // When linker script is present, we do not want to perform ICF on + // sections that belong to the different output sections. Here we + // predict the output sections and store them as parents temporarily. + std::vector PredictedOS = + Script->predictOutputSections(Sections); + if (!PredictedOS.empty()) + for (size_t I = 0; I < Sections.size(); ++I) + Sections[I]->Parent = PredictedOS[I]; + // Initially, we use hash values to partition sections. parallelForEach(Sections, [&](InputSection *S) { // Set MSB to 1 to avoid collisions with non-hash IDs. @@ -477,6 +486,11 @@ IS->Live = false; } }); + + // Set predicted temporarily parents back to zero. + if (!PredictedOS.empty()) + for (InputSection *Sec : Sections) + Sec->Parent = nullptr; } // ICF entry point function. Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -254,6 +254,9 @@ uint64_t Dot; public: + std::vector + predictOutputSections(ArrayRef V); + OutputSection *createOutputSection(StringRef Name, StringRef Location); OutputSection *getOrCreateOutputSection(StringRef Name); Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -372,6 +372,41 @@ sortSections(Vec, Pat.SortOuter); } +static bool matches(StringRef Name, std::vector &V) { + for (const SectionPattern &Pat : V) + if (Pat.SectionPat.match(Name)) + return true; + return false; +} + +// For the given list of input sections method returns the list of predicted +// output sections. Method do only basic matching and allows us to prevent doing +// ICF when output sections are predicted to be different. Returns an empty list +// if linker script is not used. +std::vector +LinkerScript::predictOutputSections(ArrayRef V) { + if (!Script->HasSectionsCommand) + return {}; + + std::vector Ret(V.size()); + for (BaseCommand *OutBase : SectionCommands) { + if (auto *OS = dyn_cast(OutBase)) { + for (BaseCommand *InBase : OS->SectionCommands) { + InputSectionDescription *Cmd = + dyn_cast(InBase); + if (!Cmd) + continue; + + parallelForEachN(0, V.size(), [&](size_t I) { + if (!Ret[I] && matches(V[I]->Name, Cmd->SectionPatterns)) + Ret[I] = OS; + }); + } + } + } + return Ret; +} + // Compute and remember which sections the InputSectionDescription matches. std::vector LinkerScript::computeInputSections(const InputSectionDescription *Cmd) { Index: test/ELF/linkerscript/icf-output-section.s =================================================================== --- test/ELF/linkerscript/icf-output-section.s +++ test/ELF/linkerscript/icf-output-section.s @@ -0,0 +1,36 @@ +# REQUIRES: x86 + +# RUN: echo "SECTIONS { \ +# RUN: .text.foo : { *(.text.foo) } .text.bar : { *(.text.bar) } \ +# RUN: .rodata.foo : { *(.rodata.foo) } .rodata.bar : { *(.rodata.bar) } }" > %t.script +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o +# RUN: ld.lld %t.o --script %t.script -o %t --icf=all --ignore-data-address-equality --print-icf-sections | count 0 + +.global _start +.type _start,@function +_start: + nop + +.global foo_func +.type foo_func,@function +.section .text.foo,"ax",@progbits +foo_func: + ret + +.global bar_func +.type bar_func,@function +.section .text.bar,"ax",@progbits +bar_func: + ret + +.global foo_data +.type foo_data,@object +.section .rodata.foo,"a",@progbits +foo_data: +.long 42 + +.global bar_data +.type bar_data,@object +.section .rodata.bar,"a",@progbits +bar_data: +.long 42