[lld] 88ce27c - [LLD][ELF] Improve ICF for relocations to ineligible sections via "aliases"

Andrew Ng via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 15 04:48:33 PDT 2020


Author: Andrew Ng
Date: 2020-10-15T12:43:14+01:00
New Revision: 88ce27c39c5e42d8a85ac1144d2ae0fae68e8853

URL: https://github.com/llvm/llvm-project/commit/88ce27c39c5e42d8a85ac1144d2ae0fae68e8853
DIFF: https://github.com/llvm/llvm-project/commit/88ce27c39c5e42d8a85ac1144d2ae0fae68e8853.diff

LOG: [LLD][ELF] Improve ICF for relocations to ineligible sections via "aliases"

ICF was not able to merge equivalent sections because of relocations to
sections ineligible for ICF that use alternative symbols, e.g. symbol
aliases or section relative relocations.

Merging in this scenario has been enabled by giving the sections that
are ineligible for ICF a unique ID, i.e. an equivalence class of their
own. This approach also provides another benefit as it improves the
hashing that is used to perform the initial equivalance grouping for
ICF. This is because the ICF ineligible sections can now contribute a
unique value towards the hashes instead of the same value of zero. This
has been seen to reduce link time with ICF by ~68% for objects compiled
with -fprofile-instr-generate.

In order to facilitate this use of a unique ID, the existing
inconsistent approach to the setting of the InputSection eqClass in ICF
has been changed so that there is a clear distinction between the
eqClass values of ICF eligible sections and those of the ineligible
sections that have a unique ID. This inconsistency could have caused
incorrect equivalence class equality in the past, although it appears
that no issues were encountered in actual use.

Differential Revision: https://reviews.llvm.org/D88830

Added: 
    lld/test/ELF/icf-ineligible.s

Modified: 
    lld/ELF/ICF.cpp

Removed: 
    


################################################################################
diff  --git a/lld/ELF/ICF.cpp b/lld/ELF/ICF.cpp
index f3c477c1d2c2..5cf944d39c81 100644
--- a/lld/ELF/ICF.cpp
+++ b/lld/ELF/ICF.cpp
@@ -102,7 +102,7 @@ template <class ELFT> class ICF {
   void run();
 
 private:
-  void segregate(size_t begin, size_t end, bool constant);
+  void segregate(size_t begin, size_t end, uint32_t eqClassBase, bool constant);
 
   template <class RelTy>
   bool constantEq(const InputSection *a, ArrayRef<RelTy> relsA,
@@ -197,7 +197,8 @@ static bool isEligible(InputSection *s) {
 
 // Split an equivalence class into smaller classes.
 template <class ELFT>
-void ICF<ELFT>::segregate(size_t begin, size_t end, bool constant) {
+void ICF<ELFT>::segregate(size_t begin, size_t end, uint32_t eqClassBase,
+                          bool constant) {
   // This loop rearranges sections in [Begin, End) so that all sections
   // that are equal in terms of equals{Constant,Variable} are contiguous
   // in [Begin, End).
@@ -219,10 +220,11 @@ void ICF<ELFT>::segregate(size_t begin, size_t end, bool constant) {
     size_t mid = bound - sections.begin();
 
     // Now we split [Begin, End) into [Begin, Mid) and [Mid, End) by
-    // updating the sections in [Begin, Mid). We use Mid as an equivalence
-    // class ID because every group ends with a unique index.
+    // updating the sections in [Begin, Mid). We use Mid as the basis for
+    // the equivalence class ID because every group ends with a unique index.
+    // Add this to eqClassBase to avoid equality with unique IDs.
     for (size_t i = begin; i < mid; ++i)
-      sections[i]->eqClass[next] = mid;
+      sections[i]->eqClass[next] = eqClassBase + mid;
 
     // If we created a group, we need to iterate the main loop again.
     if (mid != end)
@@ -353,8 +355,8 @@ bool ICF<ELFT>::variableEq(const InputSection *secA, ArrayRef<RelTy> ra,
       continue;
     auto *y = cast<InputSection>(db->section);
 
-    // Ineligible sections are in the special equivalence class 0.
-    // They can never be the same in terms of the equivalence class.
+    // Sections that are in the special equivalence class 0, can never be the
+    // same in terms of the equivalence class.
     if (x->eqClass[current] == 0)
       return false;
     if (x->eqClass[current] != y->eqClass[current])
@@ -443,7 +445,7 @@ static void combineRelocHashes(unsigned cnt, InputSection *isec,
       if (auto *relSec = dyn_cast_or_null<InputSection>(d->section))
         hash += relSec->eqClass[cnt % 2];
   }
-  // Set MSB to 1 to avoid collisions with non-hash IDs.
+  // Set MSB to 1 to avoid collisions with unique IDs.
   isec->eqClass[(cnt + 1) % 2] = hash | (1U << 31);
 }
 
@@ -471,18 +473,26 @@ template <class ELFT> void ICF<ELFT>::run() {
   uint32_t uniqueId = 0;
   for (Partition &part : partitions)
     part.ehFrame->iterateFDEWithLSDA<ELFT>(
-        [&](InputSection &s) { s.eqClass[0] = ++uniqueId; });
+        [&](InputSection &s) { s.eqClass[0] = s.eqClass[1] = ++uniqueId; });
 
   // Collect sections to merge.
   for (InputSectionBase *sec : inputSections) {
     auto *s = cast<InputSection>(sec);
-    if (isEligible(s) && s->eqClass[0] == 0)
-      sections.push_back(s);
+    if (s->eqClass[0] == 0) {
+      if (isEligible(s))
+        sections.push_back(s);
+      else
+        // Ineligible sections are assigned unique IDs, i.e. each section
+        // belongs to an equivalence class of its own.
+        s->eqClass[0] = s->eqClass[1] = ++uniqueId;
+    }
   }
 
   // Initially, we use hash values to partition sections.
-  parallelForEach(
-      sections, [&](InputSection *s) { s->eqClass[0] = xxHash64(s->data()); });
+  parallelForEach(sections, [&](InputSection *s) {
+    // Set MSB to 1 to avoid collisions with unique IDs.
+    s->eqClass[0] = xxHash64(s->data()) | (1U << 31);
+  });
 
   // Perform 2 rounds of relocation hash propagation. 2 is an empirical value to
   // reduce the average sizes of equivalence classes, i.e. segregate() which has
@@ -502,14 +512,20 @@ template <class ELFT> void ICF<ELFT>::run() {
     return a->eqClass[0] < b->eqClass[0];
   });
 
-  // Compare static contents and assign unique IDs for each static content.
-  forEachClass([&](size_t begin, size_t end) { segregate(begin, end, true); });
+  // Compare static contents and assign unique equivalence class IDs for each
+  // static content. Use a base offset for these IDs to ensure no overlap with
+  // the unique IDs already assigned.
+  uint32_t eqClassBase = ++uniqueId;
+  forEachClass([&](size_t begin, size_t end) {
+    segregate(begin, end, eqClassBase, true);
+  });
 
   // Split groups by comparing relocations until convergence is obtained.
   do {
     repeat = false;
-    forEachClass(
-        [&](size_t begin, size_t end) { segregate(begin, end, false); });
+    forEachClass([&](size_t begin, size_t end) {
+      segregate(begin, end, eqClassBase, false);
+    });
   } while (repeat);
 
   log("ICF needed " + Twine(cnt) + " iterations");

diff  --git a/lld/test/ELF/icf-ineligible.s b/lld/test/ELF/icf-ineligible.s
new file mode 100644
index 000000000000..bc63f9f35beb
--- /dev/null
+++ b/lld/test/ELF/icf-ineligible.s
@@ -0,0 +1,44 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: ld.lld %t.o -o /dev/null --keep-unique fu --icf=all --print-icf-sections | FileCheck %s
+
+## Check that ICF is able to merge equivalent sections with relocations to
+## 
diff erent symbols, e.g. aliases, that refer to the same section which is
+## ineligible for ICF.
+
+# CHECK: selected section {{.*}}:(.text.f1)
+# CHECK:   removing identical section {{.*}}:(.text.f2)
+# CHECK:   removing identical section {{.*}}:(.text.f3)
+# CHECK: selected section {{.*}}:(.text.f4)
+# CHECK:   removing identical section {{.*}}:(.text.f5)
+
+.globl d, d_alias, fu, f1, f2, f3, f4, f5
+
+.section .data.d,"aw", at progbits
+d:
+d_alias:
+.long 42
+
+.section .text.fu,"ax", at progbits
+fu:
+  nop
+
+.section .text.f1,"ax", at progbits
+f1:
+.quad d
+
+.section .text.f2,"ax", at progbits
+f2:
+.quad d_alias
+
+.section .text.f3,"ax", at progbits
+f3:
+.quad .data.d
+
+.section .text.f4,"ax", at progbits
+f4:
+.quad fu
+
+.section .text.f5,"ax", at progbits
+f5:
+.quad .text.fu


        


More information about the llvm-commits mailing list