Index: ELF/CMakeLists.txt =================================================================== --- ELF/CMakeLists.txt +++ ELF/CMakeLists.txt @@ -49,6 +49,7 @@ BitWriter Core DebugInfoDWARF + Demangle LTO MC Object Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -40,6 +40,9 @@ // For --discard-{all,locals,none}. enum class DiscardPolicy { Default, All, Locals, None }; +// For --icf={none,safe,all}. +enum class ICFPolicy { None, Safe, All }; + // For --strip-{all,debug}. enum class StripPolicy { None, All, Debug }; @@ -137,7 +140,6 @@ bool GnuUnique; bool HasDynamicList = false; bool HasDynSymTab; - bool ICF; bool IgnoreDataAddressEquality; bool IgnoreFunctionAddressEquality; bool LTODebugPassManager; @@ -183,6 +185,7 @@ bool ZRetpolineplt; bool ZWxneeded; DiscardPolicy Discard; + ICFPolicy ICF; OrphanHandlingPolicy OrphanHandling; SortSectionPolicy SortSection; StripPolicy Strip; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -294,8 +294,8 @@ error("-r and -shared may not be used together"); if (Config->GcSections) error("-r and --gc-sections may not be used together"); - if (Config->ICF) - error("-r and --icf may not be used together"); + if (Config->ICF != ICFPolicy::None) + error("-r and --icf={safe,all} may not be used together"); if (Config->Pie) error("-r and -pie may not be used together"); } @@ -487,6 +487,17 @@ return DiscardPolicy::None; } +static ICFPolicy getICF(opt::InputArgList &Args) { + auto *Arg = Args.getLastArg(OPT_icf_none, OPT_icf_safe, OPT_icf_all); + if (!Arg) + return ICFPolicy::None; + if (Arg->getOption().getID() == OPT_icf_safe) + return ICFPolicy::Safe; + if (Arg->getOption().getID() == OPT_icf_all) + return ICFPolicy::All; + return ICFPolicy::None; +} + static StringRef getDynamicLinker(opt::InputArgList &Args) { auto *Arg = Args.getLastArg(OPT_dynamic_linker, OPT_no_dynamic_linker); if (!Arg || Arg->getOption().getID() == OPT_no_dynamic_linker) @@ -707,7 +718,7 @@ Config->GcSections = Args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false); Config->GnuUnique = Args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true); Config->GdbIndex = Args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false); - Config->ICF = Args.hasFlag(OPT_icf_all, OPT_icf_none, false); + Config->ICF = getICF(Args); Config->IgnoreDataAddressEquality = Args.hasArg(OPT_ignore_data_address_equality); Config->IgnoreFunctionAddressEquality = @@ -1329,7 +1340,7 @@ markLive(); demoteSymbols(); mergeSections(); - if (Config->ICF) { + if (Config->ICF != ICFPolicy::None) { findKeepUniqueSections(Args); doIcf(); } Index: ELF/ICF.cpp =================================================================== --- ELF/ICF.cpp +++ ELF/ICF.cpp @@ -82,6 +82,7 @@ #include "lld/Common/Threads.h" #include "llvm/ADT/Hashing.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/Demangle/Demangle.h" #include "llvm/Object/ELF.h" #include #include @@ -162,7 +163,7 @@ } // Returns true if section S is subject of ICF. -static bool isEligible(InputSection *S) { +static bool isEligible(llvm::ItaniumPartialDemangler *D, InputSection *S) { if (!S->Live || S->KeepUnique || !(S->Flags & SHF_ALLOC)) return false; @@ -194,6 +195,22 @@ if (isValidCIdentifier(S->Name)) return false; + if (Config->ICF == ICFPolicy::Safe) { + size_t Pos = S->Name.rfind('.'); + if (Pos == StringRef::npos) + return false; + StringRef Mangled = S->Name.substr(Pos+1); + // _ZN + // _ZZ + // The prefixes are used as a prefilter. + if ((Mangled.startswith("_ZN") || Mangled.startswith("_ZZ")) && + !D->partialDemangle(std::string(Mangled).c_str()) && D->isCtorOrDtor()) + return true; + + // Sections not associated with a ctor/dtor are non-eligible. + return false; + } + return true; } @@ -436,9 +453,10 @@ // The main function of ICF. template void ICF::run() { // Collect sections to merge. + llvm::ItaniumPartialDemangler D; for (InputSectionBase *Sec : InputSections) if (auto *S = dyn_cast(Sec)) - if (isEligible(S)) + if (isEligible(&D, S)) Sections.push_back(S); // Initially, we use hash values to partition sections. Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -177,6 +177,8 @@ def icf_all: F<"icf=all">, HelpText<"Enable identical code folding">; +def icf_safe: F<"icf=safe">, HelpText<"Enable identical code folding for ctors and dtors">; + def icf_none: F<"icf=none">, HelpText<"Disable identical code folding">; def ignore_function_address_equality: F<"ignore-function-address-equality">, Index: test/ELF/driver.test =================================================================== --- test/ELF/driver.test +++ test/ELF/driver.test @@ -37,7 +37,7 @@ ## Attempt to use -r and --icf together # RUN: not ld.lld -r --icf=all %t -o %tfail 2>&1 | FileCheck -check-prefix=ERR4 %s -# ERR4: -r and --icf may not be used together +# ERR4: -r and --icf={safe,all} may not be used together ## Attempt to use -r and -pie together # RUN: not ld.lld -r -pie %t -o %tfail 2>&1 | FileCheck -check-prefix=ERR5 %s Index: test/ELF/icf-safe.s =================================================================== --- /dev/null +++ test/ELF/icf-safe.s @@ -0,0 +1,22 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: ld.lld %t -o %t.exe --icf=safe --print-icf-sections | FileCheck %s +# CHECK: selected section {{.*}}:(.text._ZN1AC2Ev) +# CHECK-NEXT: removing identical section {{.*}}:(.text._ZN1AD2Ev) +# CHECK-NOT: removing identical section + +# A::A() +.section .text._ZN1AC2Ev,"ax",@progbits +.byte 42 + +# A::~A() +.section .text._ZN1AD2Ev,"ax",@progbits +.byte 42 + +# A::f() +.section .text._ZN1A1fEv,"ax",@progbits +.byte 42 + +# f +.section .text.f,"ax",@progbits +.byte 42