From 0b28952993ae0f0d17153b4059900c60013ecb63 Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Thu, 25 Feb 2016 18:43:51 +0000 Subject: [PATCH] ELF: Implement ICF. This patch implements the same algorithm as LLD/COFF's ICF. I'm not going to repeat the same description about how it works, so you want to read the comment in ICF.cpp in this patch if you want to know the details. This algorithm should be more powerful than the ICF algorithm implemented in GNU gold. It can even merge mutually-recursive functions (which is harder than one might think). ICF is a fairly effective size optimization. Here are some examples. LLD: 37.14 MB -> 35.80 MB (-3.6%) Clang: 59.41 MB -> 57.80 MB (-2.7%) The lacking feature is "safe" version of ICF. This merges all identical sections. That is not compatible with a C/C++ language requirement that two distinct functions must have distinct addresses. But as long as your program do not rely on the pointer equality (which is in many cases true), your program should work with the feature. LLD works fine for example. GNU gold implements so-called "safe ICF" that identifies functions that are safe to merge by heuristics -- for example, gold thinks that constructors are safe to merge because there is no way to take an address of a constructor in C++. We have a different idea which David Majnemer suggested that we add NOPs at beginning of merged functions so that two or more pointers can have distinct values. We can do whichever we want, but this patch does not include neither. http://reviews.llvm.org/D17529 llvm-svn: 261912 --- lld/ELF/CMakeLists.txt | 1 + lld/ELF/Config.h | 1 + lld/ELF/Driver.cpp | 4 + lld/ELF/ICF.cpp | 382 +++++++++++++++++++++++++++++++++++++++++++++ lld/ELF/ICF.h | 22 +++ lld/ELF/InputFiles.cpp | 5 +- lld/ELF/InputSection.cpp | 11 +- lld/ELF/InputSection.h | 15 ++ lld/ELF/Options.td | 3 + lld/ELF/OutputSections.cpp | 1 + lld/ELF/Symbols.cpp | 1 + lld/ELF/Symbols.h | 17 +- lld/test/ELF/Inputs/icf2.s | 5 + lld/test/ELF/icf1.s | 23 +++ lld/test/ELF/icf2.s | 17 ++ lld/test/ELF/icf3.s | 19 +++ lld/test/ELF/icf4.s | 19 +++ lld/test/ELF/icf5.s | 19 +++ lld/test/ELF/icf6.s | 23 +++ lld/test/ELF/icf7.s | 29 ++++ 20 files changed, 611 insertions(+), 6 deletions(-) create mode 100644 lld/ELF/ICF.cpp create mode 100644 lld/ELF/ICF.h create mode 100644 lld/test/ELF/Inputs/icf2.s create mode 100644 lld/test/ELF/icf1.s create mode 100644 lld/test/ELF/icf2.s create mode 100644 lld/test/ELF/icf3.s create mode 100644 lld/test/ELF/icf4.s create mode 100644 lld/test/ELF/icf5.s create mode 100644 lld/test/ELF/icf6.s create mode 100644 lld/test/ELF/icf7.s diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt index 3dcb65f..ade5edc 100644 --- a/lld/ELF/CMakeLists.txt +++ b/lld/ELF/CMakeLists.txt @@ -6,6 +6,7 @@ add_lld_library(lldELF2 Driver.cpp DriverUtils.cpp Error.cpp + ICF.cpp InputFiles.cpp InputSection.cpp LinkerScript.cpp diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 8fed3eb..b601b1e 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -63,6 +63,7 @@ struct Configuration { bool ExportDynamic; bool GcSections; bool GnuHash = false; + bool ICF; bool Mips64EL = false; bool NoInhibitExec; bool NoUndefined; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index f8141de..6b7ff3d 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -10,6 +10,7 @@ #include "Driver.h" #include "Config.h" #include "Error.h" +#include "ICF.h" #include "InputFiles.h" #include "LinkerScript.h" #include "SymbolTable.h" @@ -217,6 +218,7 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { Config->EnableNewDtags = !Args.hasArg(OPT_disable_new_dtags); Config->ExportDynamic = Args.hasArg(OPT_export_dynamic); Config->GcSections = Args.hasArg(OPT_gc_sections); + Config->ICF = Args.hasArg(OPT_icf); Config->NoInhibitExec = Args.hasArg(OPT_noinhibit_exec); Config->NoUndefined = Args.hasArg(OPT_no_undefined); Config->PrintGcSections = Args.hasArg(OPT_print_gc_sections); @@ -375,5 +377,7 @@ template void LinkerDriver::link(opt::InputArgList &Args) { Symtab.scanShlibUndefined(); if (Config->GcSections) markLive(&Symtab); + if (Config->ICF) + doIcf(&Symtab); writeResult(&Symtab); } diff --git a/lld/ELF/ICF.cpp b/lld/ELF/ICF.cpp new file mode 100644 index 0000000..599d946 --- /dev/null +++ b/lld/ELF/ICF.cpp @@ -0,0 +1,382 @@ +//===- ICF.cpp ------------------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Identical Code Folding is a feature to merge sections not by name (which +// is regular comdat handling) but by contents. If two non-writable sections +// have the same data, relocations, attributes, etc., then the two +// are considered identical and merged by the linker. This optimization +// makes outputs smaller. +// +// ICF is theoretically a problem of reducing graphs by merging as many +// identical subgraphs as possible if we consider sections as vertices and +// relocations as edges. It may sound simple, but it is a bit more +// complicated than you might think. The order of processing sections +// matters because merging two sections can make other sections, whose +// relocations now point to the same section, mergeable. Graphs may contain +// cycles. We need a sophisticated algorithm to do this properly and +// efficiently. +// +// What we do in this file is this. We split sections into groups. Sections +// in the same group are considered identical. +// +// We begin by optimistically putting all sections into a single equivalence +// class. Then we apply a series of checks that split this initial +// equivalence class into more and more refined equivalence classes based on +// the properties by which a section can be distinguished. +// +// We begin by checking that the section contents and flags are the +// same. This only needs to be done once since these properties don't depend +// on the current equivalence class assignment. +// +// Then we split the equivalence classes based on checking that their +// relocations are the same, where relocation targets are compared by their +// equivalence class, not the concrete section. This may need to be done +// multiple times because as the equivalence classes are refined, two +// sections that had a relocation target in the same equivalence class may +// now target different equivalence classes, and hence these two sections +// must be put in different equivalence classes (whereas in the previous +// iteration they were not since the relocation target was the same.) +// +// Our algorithm is smart enough to merge the following mutually-recursive +// functions. +// +// void foo() { bar(); } +// void bar() { foo(); } +// +// This algorithm is so-called "optimistic" algorithm described in +// http://research.google.com/pubs/pub36912.html. (Note that what GNU +// gold implemented is different from the optimistic algorithm.) +// +//===----------------------------------------------------------------------===// + +#include "ICF.h" +#include "Config.h" +#include "OutputSections.h" +#include "SymbolTable.h" + +#include "llvm/ADT/Hashing.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lld; +using namespace lld::elf2; +using namespace llvm; +using namespace llvm::ELF; +using namespace llvm::object; + +namespace lld { +namespace elf2 { +template class ICF { + typedef typename ELFFile::Elf_Shdr Elf_Shdr; + typedef typename ELFFile::Elf_Sym Elf_Sym; + typedef typename ELFFile::uintX_t uintX_t; + typedef Elf_Rel_Impl Elf_Rel; + + using Comparator = std::function *, + const InputSection *)>; + +public: + void run(SymbolTable *Symtab); + +private: + uint64_t NextId = 1; + + static void setLive(SymbolTable *S); + static uint64_t relSize(InputSection *S); + static uint64_t getHash(InputSection *S); + static bool isEligible(InputSectionBase *Sec); + static std::vector *> getSections(SymbolTable *S); + static SymbolBody *getSymbol(const InputSection *Sec, + const Elf_Rel *Rel); + + void segregate(InputSection **Begin, InputSection **End, + Comparator Eq); + + void forEachGroup(std::vector *> &V, Comparator Eq); + + template + static bool relocationEq(iterator_range RA, + iterator_range RB); + + template + static bool variableEq(const InputSection *A, + const InputSection *B, + iterator_range RA, + iterator_range RB); + + static bool equalsConstant(const InputSection *A, + const InputSection *B); + + static bool equalsVariable(const InputSection *A, + const InputSection *B); +}; +} +} + +// Returns a hash seed for relocation sections for S. +template uint64_t ICF::relSize(InputSection *S) { + uint64_t Ret = 0; + for (const Elf_Shdr *H : S->RelocSections) + Ret += H->sh_size; + return Ret; +} + +// Returns a hash value for S. Note that the information about +// relocation targets is not included in the hash value. +template uint64_t ICF::getHash(InputSection *S) { + uint64_t Flags = S->getSectionHdr()->sh_flags; + uint64_t H = hash_combine(Flags, S->getSize()); + if (S->RelocSections.empty()) + return H; + return hash_combine(H, relSize(S)); +} + +// Returns true if Sec is subject of ICF. +template bool ICF::isEligible(InputSectionBase *Sec) { + if (!Sec || Sec == InputSection::Discarded || !Sec->Live) + return false; + auto *S = dyn_cast>(Sec); + if (!S) + return false; + + // .init and .fini contains instructions that must be executed to + // initialize and finalize the process. They cannot and should not + // be merged. + StringRef Name = S->getSectionName(); + if (Name == ".init" || Name == ".fini") + return false; + + const Elf_Shdr &H = *S->getSectionHdr(); + return (H.sh_flags & SHF_ALLOC) && (~H.sh_flags & SHF_WRITE); +} + +template +std::vector *> +ICF::getSections(SymbolTable *Symtab) { + std::vector *> V; + for (const std::unique_ptr> &F : Symtab->getObjectFiles()) + for (InputSectionBase *S : F->getSections()) + if (isEligible(S)) + V.push_back(cast>(S)); + return V; +} + +template +SymbolBody *ICF::getSymbol(const InputSection *Sec, + const Elf_Rel *Rel) { + uint32_t SymIdx = Rel->getSymbol(Config->Mips64EL); + return Sec->File->getSymbolBody(SymIdx); +} + +// All sections between Begin and End must have the same group ID before +// you call this function. This function compare sections between Begin +// and End using Eq and assign new group IDs for new groups. +template +void ICF::segregate(InputSection **Begin, InputSection **End, + Comparator Eq) { + // This loop rearranges [Begin, End) so that all sections that are + // equal in terms of Eq are contiguous. The algorithm is quadratic in + // the worst case, but that is not an issue in practice because the + // number of distinct sections in [Begin, End) is usually very small. + InputSection **I = Begin; + for (;;) { + InputSection *Head = *I; + auto Bound = std::stable_partition( + I + 1, End, [&](InputSection *S) { return Eq(Head, S); }); + if (Bound == End) + return; + uint64_t Id = NextId++; + for (; I != Bound; ++I) + (*I)->GroupId = Id; + } +} + +template +void ICF::forEachGroup(std::vector *> &V, + Comparator Eq) { + for (auto I = V.begin(), E = V.end(); I != E;) { + InputSection *Head = *I; + auto Bound = std::find_if(I + 1, E, [&](InputSection *S) { + return S->GroupId != Head->GroupId; + }); + segregate(&*I, &*Bound, Eq); + I = Bound; + } +} + +// Compare two lists of relocations. +template +template +bool ICF::relocationEq(iterator_range RelsA, + iterator_range RelsB) { + const RelTy *IA = RelsA.begin(); + const RelTy *EA = RelsA.end(); + const RelTy *IB = RelsB.begin(); + const RelTy *EB = RelsB.end(); + if (EA - IA != EB - IB) + return false; + for (; IA != EA; ++IA, ++IB) + if (IA->r_offset != IB->r_offset || + IA->getType(Config->Mips64EL) != IB->getType(Config->Mips64EL) || + getAddend(*IA) != getAddend(*IB)) + return false; + return true; +} + +// Compare "non-moving" part of two InputSections, namely everything +// except relocation targets. +template +bool ICF::equalsConstant(const InputSection *A, + const InputSection *B) { + if (A->RelocSections.size() != B->RelocSections.size()) + return false; + + for (size_t I = 0, E = A->RelocSections.size(); I != E; ++I) { + const Elf_Shdr *RA = A->RelocSections[I]; + const Elf_Shdr *RB = B->RelocSections[I]; + ELFFile &FileA = A->File->getObj(); + ELFFile &FileB = B->File->getObj(); + if (RA->sh_type == SHT_RELA) { + if (!relocationEq(FileA.relas(RA), FileB.relas(RB))) + return false; + } else { + if (!relocationEq(FileA.rels(RA), FileB.rels(RB))) + return false; + } + } + + return A->getSectionHdr()->sh_flags == B->getSectionHdr()->sh_flags && + A->getSize() == B->getSize() && + A->getSectionData() == B->getSectionData(); +} + +template +template +bool ICF::variableEq(const InputSection *A, + const InputSection *B, + iterator_range RelsA, + iterator_range RelsB) { + const RelTy *IA = RelsA.begin(); + const RelTy *EA = RelsA.end(); + const RelTy *IB = RelsB.begin(); + for (; IA != EA; ++IA, ++IB) { + // If both IA and BA are pointing to the same local symbol, + // this "if" condition must be true. + if (A->File == B->File && + IA->getSymbol(Config->Mips64EL) == IB->getSymbol(Config->Mips64EL)) + continue; + + // Otherwise, IA and BA must be pointing to the global symbols. + SymbolBody *SA = getSymbol(A, (const Elf_Rel *)IA); + SymbolBody *SB = getSymbol(B, (const Elf_Rel *)IB); + if (!SA || !SB) + return false; + + // The global symbols should be simply the same. + if (SA->repl() == SB->repl()) + continue; + + // Or, the symbols should be pointing to the same section + // in terms of the group ID. + auto *DA = dyn_cast>(SA->repl()); + auto *DB = dyn_cast>(SB->repl()); + if (!DA || !DB) + return false; + if (DA->Sym.st_value != DB->Sym.st_value) + return false; + InputSection *X = dyn_cast>(DA->Section); + InputSection *Y = dyn_cast>(DB->Section); + if (X && Y && X->GroupId && X->GroupId == Y->GroupId) + continue; + return false; + } + return true; +} + +// Compare "moving" part of two InputSections, namely relocation targets. +template +bool ICF::equalsVariable(const InputSection *A, + const InputSection *B) { + for (size_t I = 0, E = A->RelocSections.size(); I != E; ++I) { + const Elf_Shdr *RA = A->RelocSections[I]; + const Elf_Shdr *RB = B->RelocSections[I]; + ELFFile &FileA = A->File->getObj(); + ELFFile &FileB = B->File->getObj(); + if (RA->sh_type == SHT_RELA) { + if (!variableEq(A, B, FileA.relas(RA), FileB.relas(RB))) + return false; + } else { + if (!variableEq(A, B, FileA.rels(RA), FileB.rels(RB))) + return false; + } + } + return true; +} + +// The main function of ICF. +template void ICF::run(SymbolTable *Symtab) { + // Initially, we use hash values as section group IDs. Therefore, + // if two sections have the same ID, they are likely (but not + // guaranteed) to have the same static contents in terms of ICF. + std::vector *> V = getSections(Symtab); + for (InputSection *S : V) + // Set MSB on to avoid collisions with serial group IDs + S->GroupId = getHash(S) | (uint64_t(1) << 63); + + // From now on, sections in V are ordered so that sections in + // the same group are consecutive in the vector. + std::stable_sort(V.begin(), V.end(), + [](InputSection *A, InputSection *B) { + return A->GroupId < B->GroupId; + }); + + // Compare static contents and assign unique IDs for each static content. + forEachGroup(V, equalsConstant); + + // Split groups by comparing relocations until we get a convergence. + int Cnt = 1; + for (;;) { + ++Cnt; + uint64_t Id = NextId; + forEachGroup(V, equalsVariable); + if (Id == NextId) + break; + } + if (Config->Verbose) + llvm::outs() << "ICF needed " << Cnt << " iterations.\n"; + + // Merge sections in the same group. + for (auto I = V.begin(), E = V.end(); I != E;) { + InputSection *Head = *I++; + auto Bound = std::find_if(I, E, [&](InputSection *S) { + return Head->GroupId != S->GroupId; + }); + if (I == Bound) + continue; + if (Config->Verbose) + llvm::outs() << "Selected " << Head->getSectionName() << "\n"; + while (I != Bound) { + InputSection *S = *I++; + if (Config->Verbose) + llvm::outs() << " Removed " << S->getSectionName() << "\n"; + Head->replace(S); + } + } +} + +// ICF entry point function. +template void elf2::doIcf(SymbolTable *Symtab) { + ICF().run(Symtab); +} + +template void elf2::doIcf(SymbolTable *); +template void elf2::doIcf(SymbolTable *); +template void elf2::doIcf(SymbolTable *); +template void elf2::doIcf(SymbolTable *); diff --git a/lld/ELF/ICF.h b/lld/ELF/ICF.h new file mode 100644 index 0000000..034f55c --- /dev/null +++ b/lld/ELF/ICF.h @@ -0,0 +1,22 @@ +//===- ICF.h --------------------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_ELF_ICF_H +#define LLD_ELF_ICF_H + +namespace lld { +namespace elf2 { + +template class SymbolTable; + +template void doIcf(SymbolTable *); +} +} + +#endif diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 2d90beb..da2e30c 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -291,7 +291,10 @@ elf2::ObjectFile::getSection(const Elf_Sym &Sym) const { return nullptr; if (Index >= Sections.size() || !Sections[Index]) fatal("Invalid section index"); - return Sections[Index]; + InputSectionBase *S = Sections[Index]; + if (S == InputSectionBase::Discarded) + return S; + return S->Repl; } template diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index e902e00..b594638 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -25,7 +25,7 @@ template InputSectionBase::InputSectionBase(ObjectFile *File, const Elf_Shdr *Header, Kind SectionKind) - : Header(Header), File(File), SectionKind(SectionKind) { + : Header(Header), File(File), SectionKind(SectionKind), Repl(this) { // The garbage collector sets sections' Live bits. // If GC is disabled, all sections are considered live by default. Live = !Config->GcSections; @@ -81,7 +81,7 @@ InputSectionBase::getRelocTarget(const Elf_Rel &Rel) const { uint32_t SymIndex = Rel.getSymbol(Config->Mips64EL); if (SymbolBody *B = File->getSymbolBody(SymIndex)) if (auto *D = dyn_cast>(B->repl())) - return D->Section; + return D->Section->Repl; // Local symbol if (const Elf_Sym *Sym = File->getLocalSymbol(SymIndex)) if (InputSectionBase *Sec = File->getSection(*Sym)) @@ -319,6 +319,13 @@ template void InputSection::writeTo(uint8_t *Buf) { } template +void InputSection::replace(InputSection *Other) { + this->Align = std::max(this->Align, Other->Align); + Other->Repl = this->Repl; + Other->Live = false; +} + +template SplitInputSection::SplitInputSection( ObjectFile *File, const Elf_Shdr *Header, typename InputSectionBase::Kind SectionKind) diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h index fe1ec19c..945b32a 100644 --- a/lld/ELF/InputSection.h +++ b/lld/ELF/InputSection.h @@ -18,6 +18,7 @@ namespace lld { namespace elf2 { +template class ICF; template class ObjectFile; template class OutputSection; template class OutputSectionBase; @@ -47,6 +48,13 @@ public: // Used for garbage collection. bool Live = false; + // This pointer points to the "real" instance of this instance. + // Usually Repl == this. However, if ICF merges two sections, + // Repl pointer of one section points to another section. So, + // if you need to get a pointer to this instance, do not use + // this but instead this->Repl. + InputSectionBase *Repl; + // Returns the size of this section (even if this is a common or BSS.) size_t getSize() const { return Header->sh_size; } @@ -136,6 +144,7 @@ public: // This corresponds to a non SHF_MERGE section of an input file. template class InputSection : public InputSectionBase { + friend ICF; typedef InputSectionBase Base; typedef typename llvm::object::ELFFile::Elf_Shdr Elf_Shdr; typedef typename llvm::object::ELFFile::Elf_Rela Elf_Rela; @@ -168,6 +177,12 @@ private: template void copyRelocations(uint8_t *Buf, RelIteratorRange Rels); + + // Called by ICF to merge two input sections. + void replace(InputSection *Other); + + // Used by ICF. + uint64_t GroupId = 0; }; // MIPS .reginfo section provides information on the registers used by the code diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index f0f2fac..18c6fd9 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -57,6 +57,9 @@ def fini : Separate<["-"], "fini">, MetaVarName<"">, def hash_style : Separate<["--", "-"], "hash-style">, HelpText<"Specify hash style (sysv, gnu or both)">; +def icf : Flag<["--"], "icf=all">, + HelpText<"Enable Identical Code Folding.">; + def gc_sections : Flag<["--"], "gc-sections">, HelpText<"Enable garbage collection of unused sections">; diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index b2a676a..4027c55 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -762,6 +762,7 @@ template void OutputSection::finalize() { template void OutputSection::addSection(InputSectionBase *C) { + assert(C->Live); auto *S = cast>(C); Sections.push_back(S); S->OutSec = this; diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp index c5198e3..a974778 100644 --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -42,6 +42,7 @@ typename ELFFile::uintX_t SymbolBody::getVA() const { // This is an absolute symbol. if (!SC) return D->Sym.st_value; + assert(SC->Live); // Symbol offsets for AMDGPU need to be the offset in bytes of the symbol // from the beginning of the section. diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index 2d84910..073dfeb 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -221,16 +221,27 @@ public: DefinedRegular(StringRef N, const Elf_Sym &Sym, InputSectionBase *Section) : DefinedElf(SymbolBody::DefinedRegularKind, N, Sym), - Section(Section) {} + Section(Section ? Section->Repl : NullInputSection) {} static bool classof(const SymbolBody *S) { return S->kind() == SymbolBody::DefinedRegularKind; } - // If this is null, the symbol is absolute. - InputSectionBase *Section; + // The input section this symbol belongs to. Notice that this is + // a reference to a pointer. We are using two levels of indirections + // because of ICF. If ICF decides two sections need to be merged, it + // manipulates this Section pointers so that they point to the same + // section. This is a bit tricky, so be careful to not be confused. + // If this is null, the symbol is an absolute symbol. + InputSectionBase *&Section; + +private: + static InputSectionBase *NullInputSection; }; +template +InputSectionBase *DefinedRegular::NullInputSection; + // DefinedSynthetic is a class to represent linker-generated ELF symbols. // The difference from the regular symbol is that DefinedSynthetic symbols // don't belong to any input files or sections. Thus, its constructor diff --git a/lld/test/ELF/Inputs/icf2.s b/lld/test/ELF/Inputs/icf2.s new file mode 100644 index 0000000..d332130 --- /dev/null +++ b/lld/test/ELF/Inputs/icf2.s @@ -0,0 +1,5 @@ +.globl f1, f2 +.section .text.f2, "ax" +f2: + mov $60, %rdi + call f1 diff --git a/lld/test/ELF/icf1.s b/lld/test/ELF/icf1.s new file mode 100644 index 0000000..8a9ec02 --- /dev/null +++ b/lld/test/ELF/icf1.s @@ -0,0 +1,23 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: ld.lld %t -o %t2 --icf=all --verbose | FileCheck %s + +# CHECK: Selected .text.f1 +# CHECK: Removed .text.f2 + +.globl _start, f1, f2 +_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 diff --git a/lld/test/ELF/icf2.s b/lld/test/ELF/icf2.s new file mode 100644 index 0000000..ea6734c --- /dev/null +++ b/lld/test/ELF/icf2.s @@ -0,0 +1,17 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/icf2.s -o %t2 +# RUN: ld.lld %t1 %t2 -o %t --icf=all --verbose | FileCheck %s + +# CHECK: Selected .text.f1 +# CHECK: Removed .text.f2 + +.globl _start, f1, f2 +_start: + ret + +.section .text.f1, "ax" +f1: + mov $60, %rdi + call f2 diff --git a/lld/test/ELF/icf3.s b/lld/test/ELF/icf3.s new file mode 100644 index 0000000..9f39ff6 --- /dev/null +++ b/lld/test/ELF/icf3.s @@ -0,0 +1,19 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/icf2.s -o %t2 +# RUN: ld.lld %t1 %t2 -o %t --icf=all --verbose | FileCheck %s + +# CHECK-NOT: Selected .text.f1 +# CHECK-NOT: Selected .text.f2 + +.globl _start, f1, f2 +_start: + ret + +# This section is not mergeable because the content is different from f2. +.section .text.f1, "ax" +f1: + mov $60, %rdi + call f2 + mov $0, %rax diff --git a/lld/test/ELF/icf4.s b/lld/test/ELF/icf4.s new file mode 100644 index 0000000..ad16d48 --- /dev/null +++ b/lld/test/ELF/icf4.s @@ -0,0 +1,19 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: ld.lld %t -o %t --icf=all --verbose | FileCheck %s + +# CHECK-NOT: Selected .text.f1 +# CHECK-NOT: Selected .text.f2 + +.globl _start, f1, f2 +_start: + ret + +.section .text.f1, "ax" +f1: + mov $1, %rax + +.section .text.f2, "ax" +f2: + mov $0, %rax diff --git a/lld/test/ELF/icf5.s b/lld/test/ELF/icf5.s new file mode 100644 index 0000000..cf46658 --- /dev/null +++ b/lld/test/ELF/icf5.s @@ -0,0 +1,19 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: ld.lld %t -o %t --icf=all --verbose | FileCheck %s + +# CHECK-NOT: Selected .text.f1 +# CHECK-NOT: Selected .text.f2 + +.globl _start, f1, f2 +_start: + ret + +.section .text.f1, "ax" +f1: + mov $0, %rax + +.section .text.f2, "awx" +f2: + mov $0, %rax diff --git a/lld/test/ELF/icf6.s b/lld/test/ELF/icf6.s new file mode 100644 index 0000000..ecb62fe --- /dev/null +++ b/lld/test/ELF/icf6.s @@ -0,0 +1,23 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: ld.lld %t -o %t2 --icf=all --verbose | FileCheck %s + +# CHECK-NOT: Selected .text.f1 +# CHECK-NOT: Selected .text.f2 + +.globl _start, f1, f2 +_start: + ret + +.section .init, "ax" +f1: + mov $60, %rax + mov $42, %rdi + syscall + +.section .fini, "ax" +f2: + mov $60, %rax + mov $42, %rdi + syscall diff --git a/lld/test/ELF/icf7.s b/lld/test/ELF/icf7.s new file mode 100644 index 0000000..88b5a5f --- /dev/null +++ b/lld/test/ELF/icf7.s @@ -0,0 +1,29 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: ld.lld %t -o %t2 --icf=all --verbose | FileCheck %s +# RUN: llvm-objdump -t %t2 | FileCheck -check-prefix=ALIGN %s + +# CHECK: Selected .text.f1 +# CHECK: Removed .text.f2 + +# ALIGN: 0000000000011000 .text 00000000 _start +# ALIGN: 0000000000011100 .text 00000000 f1 + +.globl _start, f1, f2 +_start: + ret + +.section .text.f1, "ax" + .align 1 +f1: + mov $60, %rax + mov $42, %rdi + syscall + +.section .text.f2, "ax" + .align 256 +f2: + mov $60, %rax + mov $42, %rdi + syscall -- 2.7.4