[ELF][MIPS] Reduce number of redundant entries in the local part of MIPS GOT
authorSimon Atanasyan <simon@atanasyan.com>
Tue, 29 Mar 2016 14:07:22 +0000 (14:07 +0000)
committerSimon Atanasyan <simon@atanasyan.com>
Tue, 29 Mar 2016 14:07:22 +0000 (14:07 +0000)
Local symbol which requires GOT entry initialized by "page" address.
This address is high 16 bits of sum of the symbol value and the relocation
addend. In the relocation scanning phase final values of symbols are unknown
so to reduce number of allocated GOT entries do the following trick. Save
all output sections referenced by GOT relocations during the relocation
scanning phase. Then later in the `GotSection::finalize` method calculate
number of "pages" required to cover all saved output sections and allocate
appropriate number of GOT entries. We assume the worst case - each 64kb
page of the output section has at least one GOT relocation against it.

Differential Revision: http://reviews.llvm.org/D18349

llvm-svn: 264730

lld/ELF/OutputSections.cpp
lld/ELF/OutputSections.h
lld/test/ELF/mips-got-redundant.s

index 9421c48..8ea9033 100644 (file)
@@ -115,16 +115,30 @@ template <class ELFT> void GotSection<ELFT>::addEntry(SymbolBody &Sym) {
     //
     // See "Global Offset Table" in Chapter 5:
     // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
-    //
-    // FIXME (simon): Now LLD allocates GOT entries for each
-    // "local symbol+addend" pair. That should be fixed to reduce size
-    // of generated GOT.
-    if (Sym.isPreemptible())
-      Sym.MustBeInDynSym = true;
-    else {
+    if (Sym.isLocal()) {
+      // At this point we do not know final symbol value so to reduce number
+      // of allocated GOT entries do the following trick. Save all output
+      // sections referenced by GOT relocations. Then later in the `finalize`
+      // method calculate number of "pages" required to cover all saved output
+      // section and allocate appropriate number of GOT entries.
+      auto *OutSec = cast<DefinedRegular<ELFT>>(&Sym)->Section->OutSec;
+      MipsOutSections.insert(OutSec);
+      return;
+    }
+    if (!Sym.isPreemptible()) {
+      // In case of non-local symbols require an entry in the local part
+      // of MIPS GOT, we set GotIndex to 1 just to accent that this symbol
+      // has the GOT entry and escape creation more redundant GOT entries.
+      // FIXME (simon): We can try to store such symbols in the `Entries`
+      // container. But in that case we have to sort out that container
+      // and update GotIndex assigned to symbols.
+      Sym.GotIndex = 1;
       ++MipsLocalEntries;
       return;
     }
+    // All preemptible symbols with MIPS GOT entries should be represented
+    // in the dynamic symbols table.
+    Sym.MustBeInDynSym = true;
   }
   Sym.GotIndex = Entries.size();
   Entries.push_back(&Sym);
@@ -191,6 +205,14 @@ unsigned GotSection<ELFT>::getMipsLocalEntriesNum() const {
 }
 
 template <class ELFT> void GotSection<ELFT>::finalize() {
+  for (const OutputSectionBase<ELFT> *OutSec : MipsOutSections) {
+    // Calculate an upper bound of MIPS GOT entries required to store page
+    // addresses of local symbols. We assume the worst case - each 64kb
+    // page of the output section has at least one GOT relocation against it.
+    // Add 0x8000 to the section's size because the page address stored
+    // in the GOT entry is calculated as (value + 0x8000) & ~0xffff.
+    MipsLocalEntries += (OutSec->getSize() + 0x8000 + 0xfffe) / 0xffff;
+  }
   this->Header.sh_size =
       (Target->GotHeaderEntriesNum + MipsLocalEntries + Entries.size()) *
       sizeof(uintX_t);
@@ -1348,8 +1370,12 @@ SymbolTableSection<ELFT>::SymbolTableSection(
 // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
 static bool sortMipsSymbols(const std::pair<SymbolBody *, unsigned> &L,
                             const std::pair<SymbolBody *, unsigned> &R) {
-  if (!L.first->isInGot() || !R.first->isInGot())
-    return R.first->isInGot();
+  // Sort entries related to non-local preemptible symbols by GOT indexes.
+  // All other entries go to the first part of GOT in arbitrary order.
+  bool LIsInLocalGot = !L.first->isInGot() || !L.first->isPreemptible();
+  bool RIsInLocalGot = !R.first->isInGot() || !R.first->isPreemptible();
+  if (LIsInLocalGot || RIsInLocalGot)
+    return !RIsInLocalGot;
   return L.first->GotIndex < R.first->GotIndex;
 }
 
index 0b4edb9..a35fa1e 100644 (file)
@@ -13,6 +13,7 @@
 #include "Config.h"
 
 #include "lld/Core/LLVM.h"
+#include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/MC/StringTableBuilder.h"
 #include "llvm/Object/ELF.h"
 
@@ -124,6 +125,8 @@ private:
   std::vector<const SymbolBody *> Entries;
   uint32_t TlsIndexOff = -1;
   uint32_t MipsLocalEntries = 0;
+  // Output sections referenced by MIPS GOT relocations.
+  llvm::SmallPtrSet<const OutputSectionBase<ELFT> *, 10> MipsOutSections;
   llvm::DenseMap<uintX_t, size_t> MipsLocalGotPos;
 
   uintX_t getMipsLocalEntryAddr(uintX_t EntryValue);
index 0526042..07c3c24 100644 (file)
 # CHECK-NEXT:     Initial: 0x40008
 #                          ^-- glb1
 # CHECK-NEXT:   }
-# CHECK-NEXT:   Entry {
-# CHECK-NEXT:     Address: 0x20014
-# CHECK-NEXT:     Access: -32732
-# CHECK-NEXT:     Initial: 0x0
-# CHECK-NEXT:   }
-# CHECK-NEXT:   Entry {
-# CHECK-NEXT:     Address: 0x20018
-# CHECK-NEXT:     Access: -32728
-# CHECK-NEXT:     Initial: 0x0
-# CHECK-NEXT:   }
-# CHECK-NEXT:   Entry {
-# CHECK-NEXT:     Address: 0x2001C
-# CHECK-NEXT:     Access: -32724
-# CHECK-NEXT:     Initial: 0x0
-# CHECK-NEXT:   }
 # CHECK-NEXT: ]
 
   .text