Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -101,6 +101,7 @@ std::vector VersionDefinitions; std::vector AuxiliaryList; std::vector FilterList; + std::vector KeepUnique; std::vector SearchPaths; std::vector SymbolOrderingFile; std::vector Undefined; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -707,6 +707,7 @@ Config->IgnoreFunctionAddressEquality = Args.hasArg(OPT_ignore_function_address_equality); Config->Init = Args.getLastArgValue(OPT_init, "_init"); + Config->KeepUnique = args::getStrings(Args, OPT_keep_unique); Config->LTOAAPipeline = Args.getLastArgValue(OPT_lto_aa_pipeline); Config->LTODebugPassManager = Args.hasArg(OPT_lto_debug_pass_manager); Config->LTONewPassManager = Args.hasArg(OPT_lto_new_pass_manager); Index: ELF/ICF.cpp =================================================================== --- ELF/ICF.cpp +++ ELF/ICF.cpp @@ -413,12 +413,31 @@ message(S); } +// Record sections that define symbols mentioned in --keep-unique +// these sections are inelligible for ICF. +static llvm::DenseSet findKeepUniqueSections() { + llvm::DenseSet KeepUniqueSections; + for (llvm::StringRef Name : Config->KeepUnique) { + if (Symbol *Sym = Symtab->find(Name)) { + if (auto *DefSym = dyn_cast(Sym)) + if (auto *IS = dyn_cast(DefSym->Section)) + KeepUniqueSections.insert(IS); + } else + warn("Could not find symbol " + Name + " to keep unique"); + } + return KeepUniqueSections; +} + // The main function of ICF. template void ICF::run() { + llvm::DenseSet KeepUniqueSections = + findKeepUniqueSections(); + // Collect sections to merge. for (InputSectionBase *Sec : InputSections) if (auto *S = dyn_cast(Sec)) - if (isEligible(S)) + if (isEligible(S) && + KeepUniqueSections.find(S) == KeepUniqueSections.end()) Sections.push_back(S); // Initially, we use hash values to partition sections. Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -192,6 +192,8 @@ defm just_symbols: Eq<"just-symbols">, HelpText<"Just link symbols">; +defm keep_unique: Eq<"keep-unique">, HelpText<"Do not fold this symbol during ICF">; + defm library: Eq<"library">, HelpText<"Root name of library to use">, MetaVarName<"">; Index: test/ELF/icf-keep-unique.s =================================================================== --- /dev/null +++ test/ELF/icf-keep-unique.s @@ -0,0 +1,53 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: ld.lld %t -o %t2 --icf=all --print-icf-sections | FileCheck %s +# RUN: ld.lld %t -o %t2 --keep-unique f2 --keep-unique f4 --keep-unique f5 --icf=all --print-icf-sections 2>&1 | FileCheck %s -check-prefix=CHECK-KEEP + +// Base case, expect only .text.f1 to be kept +// CHECK: selected section /linaro/icf/build/tools/lld/test/ELF/Output/icf-keep-unique.s.tmp:(.text.f1) +// CHECK-NEXT: removing identical section /linaro/icf/build/tools/lld/test/ELF/Output/icf-keep-unique.s.tmp:(.text.f2) +// CHECK-NEXT: removing identical section /linaro/icf/build/tools/lld/test/ELF/Output/icf-keep-unique.s.tmp:(.text.f3) +// CHECK-NEXT: removing identical section /linaro/icf/build/tools/lld/test/ELF/Output/icf-keep-unique.s.tmp:(.text.f4) +// CHECK-NEXT: removing identical section /linaro/icf/build/tools/lld/test/ELF/Output/icf-keep-unique.s.tmp:(.text.f5) + +// With --keep-unique f2, f4 and f5 we expect only f3 and f5 to be removed. +// f5 is not matched by --keep-unique as it is a local symbol. +// CHECK-KEEP: warning: Could not find symbol f5 to keep unique +// CHECK-KEEP: selected section /linaro/icf/build/tools/lld/test/ELF/Output/icf-keep-unique.s.tmp:(.text.f1) +// CHECK-KEEP-NEXT: removing identical section /linaro/icf/build/tools/lld/test/ELF/Output/icf-keep-unique.s.tmp:(.text.f3) +// CHECK-KEEP-NEXT: removing identical section /linaro/icf/build/tools/lld/test/ELF/Output/icf-keep-unique.s.tmp:(.text.f5) + .globl _start, f1, f2, f3, f4 +_start: + ret + + .section .text.f1, "ax" +f1: + mov $60, %rax + mov $42, %rdi + syscall + +.section .text.f2, "ax" +f2: + mov $60, %rax + mov $42, %rdi + syscall + +.section .text.f3, "ax" +f3: + mov $60, %rax + mov $42, %rdi + syscall + +.section .text.f4, "ax" +f4: + mov $60, %rax + mov $42, %rdi + syscall + +# f5 is local, not found by --keep-unique f5 +.section .text.f5, "ax" +f5: + mov $60, %rax + mov $42, %rdi + syscall