[lld] r261912 - ELF: Implement ICF.

Rui Ueyama via llvm-commits llvm-commits at lists.llvm.org
Thu Feb 25 10:43:52 PST 2016


Author: ruiu
Date: Thu Feb 25 12:43:51 2016
New Revision: 261912

URL: http://llvm.org/viewvc/llvm-project?rev=261912&view=rev
Log:
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

Added:
    lld/trunk/ELF/ICF.cpp
    lld/trunk/ELF/ICF.h
    lld/trunk/test/ELF/Inputs/icf2.s
    lld/trunk/test/ELF/icf1.s
    lld/trunk/test/ELF/icf2.s
    lld/trunk/test/ELF/icf3.s
    lld/trunk/test/ELF/icf4.s
    lld/trunk/test/ELF/icf5.s
    lld/trunk/test/ELF/icf6.s
    lld/trunk/test/ELF/icf7.s
Modified:
    lld/trunk/ELF/CMakeLists.txt
    lld/trunk/ELF/Config.h
    lld/trunk/ELF/Driver.cpp
    lld/trunk/ELF/InputFiles.cpp
    lld/trunk/ELF/InputSection.cpp
    lld/trunk/ELF/InputSection.h
    lld/trunk/ELF/Options.td
    lld/trunk/ELF/OutputSections.cpp
    lld/trunk/ELF/Symbols.cpp
    lld/trunk/ELF/Symbols.h

Modified: lld/trunk/ELF/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/CMakeLists.txt?rev=261912&r1=261911&r2=261912&view=diff
==============================================================================
--- lld/trunk/ELF/CMakeLists.txt (original)
+++ lld/trunk/ELF/CMakeLists.txt Thu Feb 25 12:43:51 2016
@@ -6,6 +6,7 @@ add_lld_library(lldELF2
   Driver.cpp
   DriverUtils.cpp
   Error.cpp
+  ICF.cpp
   InputFiles.cpp
   InputSection.cpp
   LinkerScript.cpp

Modified: lld/trunk/ELF/Config.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Config.h?rev=261912&r1=261911&r2=261912&view=diff
==============================================================================
--- lld/trunk/ELF/Config.h (original)
+++ lld/trunk/ELF/Config.h Thu Feb 25 12:43:51 2016
@@ -63,6 +63,7 @@ struct Configuration {
   bool ExportDynamic;
   bool GcSections;
   bool GnuHash = false;
+  bool ICF;
   bool Mips64EL = false;
   bool NoInhibitExec;
   bool NoUndefined;

Modified: lld/trunk/ELF/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Driver.cpp?rev=261912&r1=261911&r2=261912&view=diff
==============================================================================
--- lld/trunk/ELF/Driver.cpp (original)
+++ lld/trunk/ELF/Driver.cpp Thu Feb 25 12:43:51 2016
@@ -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::Inpu
   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 <class ELFT> void LinkerDriver:
   Symtab.scanShlibUndefined();
   if (Config->GcSections)
     markLive<ELFT>(&Symtab);
+  if (Config->ICF)
+    doIcf<ELFT>(&Symtab);
   writeResult<ELFT>(&Symtab);
 }

Added: lld/trunk/ELF/ICF.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/ICF.cpp?rev=261912&view=auto
==============================================================================
--- lld/trunk/ELF/ICF.cpp (added)
+++ lld/trunk/ELF/ICF.cpp Thu Feb 25 12:43:51 2016
@@ -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 ELFT> class ICF {
+  typedef typename ELFFile<ELFT>::Elf_Shdr Elf_Shdr;
+  typedef typename ELFFile<ELFT>::Elf_Sym Elf_Sym;
+  typedef typename ELFFile<ELFT>::uintX_t uintX_t;
+  typedef Elf_Rel_Impl<ELFT, false> Elf_Rel;
+
+  using Comparator = std::function<bool(const InputSection<ELFT> *,
+                                        const InputSection<ELFT> *)>;
+
+public:
+  void run(SymbolTable<ELFT> *Symtab);
+
+private:
+  uint64_t NextId = 1;
+
+  static void setLive(SymbolTable<ELFT> *S);
+  static uint64_t relSize(InputSection<ELFT> *S);
+  static uint64_t getHash(InputSection<ELFT> *S);
+  static bool isEligible(InputSectionBase<ELFT> *Sec);
+  static std::vector<InputSection<ELFT> *> getSections(SymbolTable<ELFT> *S);
+  static SymbolBody *getSymbol(const InputSection<ELFT> *Sec,
+                               const Elf_Rel *Rel);
+
+  void segregate(InputSection<ELFT> **Begin, InputSection<ELFT> **End,
+                 Comparator Eq);
+
+  void forEachGroup(std::vector<InputSection<ELFT> *> &V, Comparator Eq);
+
+  template <class RelTy>
+  static bool relocationEq(iterator_range<const RelTy *> RA,
+                           iterator_range<const RelTy *> RB);
+
+  template <class RelTy>
+  static bool variableEq(const InputSection<ELFT> *A,
+                         const InputSection<ELFT> *B,
+                         iterator_range<const RelTy *> RA,
+                         iterator_range<const RelTy *> RB);
+
+  static bool equalsConstant(const InputSection<ELFT> *A,
+                             const InputSection<ELFT> *B);
+
+  static bool equalsVariable(const InputSection<ELFT> *A,
+                             const InputSection<ELFT> *B);
+};
+}
+}
+
+// Returns a hash seed for relocation sections for S.
+template <class ELFT> uint64_t ICF<ELFT>::relSize(InputSection<ELFT> *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 <class ELFT> uint64_t ICF<ELFT>::getHash(InputSection<ELFT> *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 <class ELFT> bool ICF<ELFT>::isEligible(InputSectionBase<ELFT> *Sec) {
+  if (!Sec || Sec == InputSection<ELFT>::Discarded || !Sec->Live)
+    return false;
+  auto *S = dyn_cast<InputSection<ELFT>>(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 <class ELFT>
+std::vector<InputSection<ELFT> *>
+ICF<ELFT>::getSections(SymbolTable<ELFT> *Symtab) {
+  std::vector<InputSection<ELFT> *> V;
+  for (const std::unique_ptr<ObjectFile<ELFT>> &F : Symtab->getObjectFiles())
+    for (InputSectionBase<ELFT> *S : F->getSections())
+      if (isEligible(S))
+        V.push_back(cast<InputSection<ELFT>>(S));
+  return V;
+}
+
+template <class ELFT>
+SymbolBody *ICF<ELFT>::getSymbol(const InputSection<ELFT> *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 <class ELFT>
+void ICF<ELFT>::segregate(InputSection<ELFT> **Begin, InputSection<ELFT> **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<ELFT> **I = Begin;
+  for (;;) {
+    InputSection<ELFT> *Head = *I;
+    auto Bound = std::stable_partition(
+        I + 1, End, [&](InputSection<ELFT> *S) { return Eq(Head, S); });
+    if (Bound == End)
+      return;
+    uint64_t Id = NextId++;
+    for (; I != Bound; ++I)
+      (*I)->GroupId = Id;
+  }
+}
+
+template <class ELFT>
+void ICF<ELFT>::forEachGroup(std::vector<InputSection<ELFT> *> &V,
+                             Comparator Eq) {
+  for (auto I = V.begin(), E = V.end(); I != E;) {
+    InputSection<ELFT> *Head = *I;
+    auto Bound = std::find_if(I + 1, E, [&](InputSection<ELFT> *S) {
+      return S->GroupId != Head->GroupId;
+    });
+    segregate(&*I, &*Bound, Eq);
+    I = Bound;
+  }
+}
+
+// Compare two lists of relocations.
+template <class ELFT>
+template <class RelTy>
+bool ICF<ELFT>::relocationEq(iterator_range<const RelTy *> RelsA,
+                             iterator_range<const RelTy *> 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<ELFT>(*IA) != getAddend<ELFT>(*IB))
+      return false;
+  return true;
+}
+
+// Compare "non-moving" part of two InputSections, namely everything
+// except relocation targets.
+template <class ELFT>
+bool ICF<ELFT>::equalsConstant(const InputSection<ELFT> *A,
+                               const InputSection<ELFT> *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<ELFT> &FileA = A->File->getObj();
+    ELFFile<ELFT> &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 <class ELFT>
+template <class RelTy>
+bool ICF<ELFT>::variableEq(const InputSection<ELFT> *A,
+                           const InputSection<ELFT> *B,
+                           iterator_range<const RelTy *> RelsA,
+                           iterator_range<const RelTy *> 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<DefinedRegular<ELFT>>(SA->repl());
+    auto *DB = dyn_cast<DefinedRegular<ELFT>>(SB->repl());
+    if (!DA || !DB)
+      return false;
+    if (DA->Sym.st_value != DB->Sym.st_value)
+      return false;
+    InputSection<ELFT> *X = dyn_cast<InputSection<ELFT>>(DA->Section);
+    InputSection<ELFT> *Y = dyn_cast<InputSection<ELFT>>(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 <class ELFT>
+bool ICF<ELFT>::equalsVariable(const InputSection<ELFT> *A,
+                               const InputSection<ELFT> *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<ELFT> &FileA = A->File->getObj();
+    ELFFile<ELFT> &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 <class ELFT> void ICF<ELFT>::run(SymbolTable<ELFT> *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<InputSection<ELFT> *> V = getSections(Symtab);
+  for (InputSection<ELFT> *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<ELFT> *A, InputSection<ELFT> *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<ELFT> *Head = *I++;
+    auto Bound = std::find_if(I, E, [&](InputSection<ELFT> *S) {
+      return Head->GroupId != S->GroupId;
+    });
+    if (I == Bound)
+      continue;
+    if (Config->Verbose)
+      llvm::outs() << "Selected " << Head->getSectionName() << "\n";
+    while (I != Bound) {
+      InputSection<ELFT> *S = *I++;
+      if (Config->Verbose)
+        llvm::outs() << "  Removed " << S->getSectionName() << "\n";
+      Head->replace(S);
+    }
+  }
+}
+
+// ICF entry point function.
+template <class ELFT> void elf2::doIcf(SymbolTable<ELFT> *Symtab) {
+  ICF<ELFT>().run(Symtab);
+}
+
+template void elf2::doIcf(SymbolTable<ELF32LE> *);
+template void elf2::doIcf(SymbolTable<ELF32BE> *);
+template void elf2::doIcf(SymbolTable<ELF64LE> *);
+template void elf2::doIcf(SymbolTable<ELF64BE> *);

Added: lld/trunk/ELF/ICF.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/ICF.h?rev=261912&view=auto
==============================================================================
--- lld/trunk/ELF/ICF.h (added)
+++ lld/trunk/ELF/ICF.h Thu Feb 25 12:43:51 2016
@@ -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 ELFT> class SymbolTable;
+
+template <class ELFT> void doIcf(SymbolTable<ELFT> *);
+}
+}
+
+#endif

Modified: lld/trunk/ELF/InputFiles.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/InputFiles.cpp?rev=261912&r1=261911&r2=261912&view=diff
==============================================================================
--- lld/trunk/ELF/InputFiles.cpp (original)
+++ lld/trunk/ELF/InputFiles.cpp Thu Feb 25 12:43:51 2016
@@ -291,7 +291,10 @@ elf2::ObjectFile<ELFT>::getSection(const
     return nullptr;
   if (Index >= Sections.size() || !Sections[Index])
     fatal("Invalid section index");
-  return Sections[Index];
+  InputSectionBase<ELFT> *S = Sections[Index];
+  if (S == InputSectionBase<ELFT>::Discarded)
+    return S;
+  return S->Repl;
 }
 
 template <class ELFT>

Modified: lld/trunk/ELF/InputSection.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/InputSection.cpp?rev=261912&r1=261911&r2=261912&view=diff
==============================================================================
--- lld/trunk/ELF/InputSection.cpp (original)
+++ lld/trunk/ELF/InputSection.cpp Thu Feb 25 12:43:51 2016
@@ -25,7 +25,7 @@ template <class ELFT>
 InputSectionBase<ELFT>::InputSectionBase(ObjectFile<ELFT> *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<ELFT>::getRelocTarget(c
   uint32_t SymIndex = Rel.getSymbol(Config->Mips64EL);
   if (SymbolBody *B = File->getSymbolBody(SymIndex))
     if (auto *D = dyn_cast<DefinedRegular<ELFT>>(B->repl()))
-      return D->Section;
+      return D->Section->Repl;
   // Local symbol
   if (const Elf_Sym *Sym = File->getLocalSymbol(SymIndex))
     if (InputSectionBase<ELFT> *Sec = File->getSection(*Sym))
@@ -319,6 +319,13 @@ template <class ELFT> void InputSection<
 }
 
 template <class ELFT>
+void InputSection<ELFT>::replace(InputSection<ELFT> *Other) {
+  this->Align = std::max(this->Align, Other->Align);
+  Other->Repl = this->Repl;
+  Other->Live = false;
+}
+
+template <class ELFT>
 SplitInputSection<ELFT>::SplitInputSection(
     ObjectFile<ELFT> *File, const Elf_Shdr *Header,
     typename InputSectionBase<ELFT>::Kind SectionKind)

Modified: lld/trunk/ELF/InputSection.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/InputSection.h?rev=261912&r1=261911&r2=261912&view=diff
==============================================================================
--- lld/trunk/ELF/InputSection.h (original)
+++ lld/trunk/ELF/InputSection.h Thu Feb 25 12:43:51 2016
@@ -18,6 +18,7 @@
 namespace lld {
 namespace elf2 {
 
+template <class ELFT> class ICF;
 template <class ELFT> class ObjectFile;
 template <class ELFT> class OutputSection;
 template <class ELFT> 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<ELFT> *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 ELFT> class InputSection : public InputSectionBase<ELFT> {
+  friend ICF<ELFT>;
   typedef InputSectionBase<ELFT> Base;
   typedef typename llvm::object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr;
   typedef typename llvm::object::ELFFile<ELFT>::Elf_Rela Elf_Rela;
@@ -168,6 +177,12 @@ private:
 
   template <bool isRela>
   void copyRelocations(uint8_t *Buf, RelIteratorRange<isRela> Rels);
+
+  // Called by ICF to merge two input sections.
+  void replace(InputSection<ELFT> *Other);
+
+  // Used by ICF.
+  uint64_t GroupId = 0;
 };
 
 // MIPS .reginfo section provides information on the registers used by the code

Modified: lld/trunk/ELF/Options.td
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Options.td?rev=261912&r1=261911&r2=261912&view=diff
==============================================================================
--- lld/trunk/ELF/Options.td (original)
+++ lld/trunk/ELF/Options.td Thu Feb 25 12:43:51 2016
@@ -57,6 +57,9 @@ def fini : Separate<["-"], "fini">, Meta
 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">;
 

Modified: lld/trunk/ELF/OutputSections.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/OutputSections.cpp?rev=261912&r1=261911&r2=261912&view=diff
==============================================================================
--- lld/trunk/ELF/OutputSections.cpp (original)
+++ lld/trunk/ELF/OutputSections.cpp Thu Feb 25 12:43:51 2016
@@ -762,6 +762,7 @@ template <class ELFT> void OutputSection
 
 template <class ELFT>
 void OutputSection<ELFT>::addSection(InputSectionBase<ELFT> *C) {
+  assert(C->Live);
   auto *S = cast<InputSection<ELFT>>(C);
   Sections.push_back(S);
   S->OutSec = this;

Modified: lld/trunk/ELF/Symbols.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Symbols.cpp?rev=261912&r1=261911&r2=261912&view=diff
==============================================================================
--- lld/trunk/ELF/Symbols.cpp (original)
+++ lld/trunk/ELF/Symbols.cpp Thu Feb 25 12:43:51 2016
@@ -42,6 +42,7 @@ typename ELFFile<ELFT>::uintX_t SymbolBo
     // 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.

Modified: lld/trunk/ELF/Symbols.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Symbols.h?rev=261912&r1=261911&r2=261912&view=diff
==============================================================================
--- lld/trunk/ELF/Symbols.h (original)
+++ lld/trunk/ELF/Symbols.h Thu Feb 25 12:43:51 2016
@@ -221,16 +221,27 @@ public:
   DefinedRegular(StringRef N, const Elf_Sym &Sym,
                  InputSectionBase<ELFT> *Section)
       : DefinedElf<ELFT>(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<ELFT> *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<ELFT> *&Section;
+
+private:
+  static InputSectionBase<ELFT> *NullInputSection;
 };
 
+template <class ELFT>
+InputSectionBase<ELFT> *DefinedRegular<ELFT>::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

Added: lld/trunk/test/ELF/Inputs/icf2.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/Inputs/icf2.s?rev=261912&view=auto
==============================================================================
--- lld/trunk/test/ELF/Inputs/icf2.s (added)
+++ lld/trunk/test/ELF/Inputs/icf2.s Thu Feb 25 12:43:51 2016
@@ -0,0 +1,5 @@
+.globl f1, f2
+.section .text.f2, "ax"
+f2:
+  mov $60, %rdi
+  call f1

Added: lld/trunk/test/ELF/icf1.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/icf1.s?rev=261912&view=auto
==============================================================================
--- lld/trunk/test/ELF/icf1.s (added)
+++ lld/trunk/test/ELF/icf1.s Thu Feb 25 12:43:51 2016
@@ -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

Added: lld/trunk/test/ELF/icf2.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/icf2.s?rev=261912&view=auto
==============================================================================
--- lld/trunk/test/ELF/icf2.s (added)
+++ lld/trunk/test/ELF/icf2.s Thu Feb 25 12:43:51 2016
@@ -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

Added: lld/trunk/test/ELF/icf3.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/icf3.s?rev=261912&view=auto
==============================================================================
--- lld/trunk/test/ELF/icf3.s (added)
+++ lld/trunk/test/ELF/icf3.s Thu Feb 25 12:43:51 2016
@@ -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

Added: lld/trunk/test/ELF/icf4.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/icf4.s?rev=261912&view=auto
==============================================================================
--- lld/trunk/test/ELF/icf4.s (added)
+++ lld/trunk/test/ELF/icf4.s Thu Feb 25 12:43:51 2016
@@ -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

Added: lld/trunk/test/ELF/icf5.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/icf5.s?rev=261912&view=auto
==============================================================================
--- lld/trunk/test/ELF/icf5.s (added)
+++ lld/trunk/test/ELF/icf5.s Thu Feb 25 12:43:51 2016
@@ -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

Added: lld/trunk/test/ELF/icf6.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/icf6.s?rev=261912&view=auto
==============================================================================
--- lld/trunk/test/ELF/icf6.s (added)
+++ lld/trunk/test/ELF/icf6.s Thu Feb 25 12:43:51 2016
@@ -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

Added: lld/trunk/test/ELF/icf7.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/icf7.s?rev=261912&view=auto
==============================================================================
--- lld/trunk/test/ELF/icf7.s (added)
+++ lld/trunk/test/ELF/icf7.s Thu Feb 25 12:43:51 2016
@@ -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




More information about the llvm-commits mailing list