gold: Add support for sparc GOTDATA optimizations in Gold.
authorDavid S. Miller <davem@redhat.com>
Tue, 17 Apr 2012 02:29:46 +0000 (02:29 +0000)
committerDavid S. Miller <davem@redhat.com>
Tue, 17 Apr 2012 02:29:46 +0000 (02:29 +0000)
gold/

* sparc.cc (Target_sparc::got_address): New function.
(Sparc_relocate_functions::gdop_hix22): New function.
(Sparc_relocate_functions::gdop_lox10): New function.
(Target_sparc::Scan::local): Do not emit a GOT entry for GOTDATA
relocs.
(Target_sparc::Scan::local): Likewise if the global symbol is not
preemptible and is not IFUNC.
(Target_sparc::Relocate::relocate): Perform GOTDATA code
transformations for local and non-preemptible non-IFUNC global
symbols.

gold/ChangeLog
gold/sparc.cc

index c9bfd7972658523d684ec3818f140ad281fb0b53..363f368939b160f71c4d2054143aacaf4a55e41f 100644 (file)
@@ -1,5 +1,16 @@
 2012-04-16  David S. Miller  <davem@davemloft.net>
 
+       * sparc.cc (Target_sparc::got_address): New function.
+       (Sparc_relocate_functions::gdop_hix22): New function.
+       (Sparc_relocate_functions::gdop_lox10): New function.
+       (Target_sparc::Scan::local): Do not emit a GOT entry for GOTDATA
+       relocs.
+       (Target_sparc::Scan::local): Likewise if the global symbol is not
+       preemptible and is not IFUNC.
+       (Target_sparc::Relocate::relocate): Perform GOTDATA code
+       transformations for local and non-preemptible non-IFUNC global
+       symbols.
+
        * gdb-index.cc (Gdb_index::do_write): Use Swap_unaligned when
        writing out 64-bit part of ranges.
 
index d1e83eb1671fb04046021a472558f80d5f5cb8a3..762da424dce0f26d324cabe43cc5b23219d495fe 100644 (file)
@@ -185,6 +185,15 @@ class Target_sparc : public Sized_target<size, big_endian>
     return this->got_size() / (size / 8);
   }
 
+  // Return the address of the GOT.
+  uint64_t
+  got_address() const
+  {
+    if (this->got_ == NULL)
+      return 0;
+    return this->got_->address();
+  }
+
   // Return the number of entries in the PLT.
   unsigned int
   plt_entry_count() const;
@@ -1065,6 +1074,28 @@ public:
     elfcpp::Swap<32, true>::writeval(wv, val | reloc);
   }
 
+  // R_SPARC_GOTDATA_OP_HIX22: @gdopoff(Symbol + Addend) >> 10
+  static inline void
+  gdop_hix22(unsigned char* view,
+            typename elfcpp::Elf_types<size>::Elf_Addr value,
+            typename elfcpp::Elf_types<size>::Elf_Addr addend)
+  {
+    typedef typename elfcpp::Swap<32, true>::Valtype Valtype;
+    Valtype* wv = reinterpret_cast<Valtype*>(view);
+    Valtype val = elfcpp::Swap<32, true>::readval(wv);
+    int32_t reloc = static_cast<int32_t>(value + addend);
+
+    val &= ~0x3fffff;
+
+    if (reloc < 0)
+      reloc ^= ~static_cast<int32_t>(0);
+    reloc >>= 10;
+
+    reloc &= 0x3fffff;
+
+    elfcpp::Swap<32, true>::writeval(wv, val | reloc);
+  }
+
   // R_SPARC_HIX22: ((Symbol + Addend) ^ 0xffffffffffffffff) >> 10
   static inline void
   hix22(unsigned char* view,
@@ -1106,6 +1137,26 @@ public:
     elfcpp::Swap<32, true>::writeval(wv, val | reloc);
   }
 
+  // R_SPARC_GOTDATA_OP_LOX10: (@gdopoff(Symbol + Addend) & 0x3ff) | 0x1c00
+  static inline void
+  gdop_lox10(unsigned char* view,
+            typename elfcpp::Elf_types<size>::Elf_Addr value,
+            typename elfcpp::Elf_types<size>::Elf_Addr addend)
+  {
+    typedef typename elfcpp::Swap<32, true>::Valtype Valtype;
+    Valtype* wv = reinterpret_cast<Valtype*>(view);
+    Valtype val = elfcpp::Swap<32, true>::readval(wv);
+    int32_t reloc = static_cast<int32_t>(value + addend);
+
+    if (reloc < 0)
+      reloc = (reloc & 0x3ff) | 0x1c00;
+    else
+      reloc = (reloc & 0x3ff);
+
+    val &= ~0x1fff;
+    elfcpp::Swap<32, true>::writeval(wv, val | reloc);
+  }
+
   // R_SPARC_LOX10: ((Symbol + Addend) & 0x3ff) | 0x1c00
   static inline void
   lox10(unsigned char* view,
@@ -2266,6 +2317,10 @@ Target_sparc<size, big_endian>::Scan::local(
     case elfcpp::R_SPARC_GOTDATA_OP:
     case elfcpp::R_SPARC_GOTDATA_OP_HIX22:
     case elfcpp::R_SPARC_GOTDATA_OP_LOX10:
+      // We will optimize this into a GOT relative relocation
+      // and code transform the GOT load into an addition.
+      break;
+
     case elfcpp::R_SPARC_GOT10:
     case elfcpp::R_SPARC_GOT13:
     case elfcpp::R_SPARC_GOT22:
@@ -2695,6 +2750,15 @@ Target_sparc<size, big_endian>::Scan::global(
     case elfcpp::R_SPARC_GOTDATA_OP:
     case elfcpp::R_SPARC_GOTDATA_OP_HIX22:
     case elfcpp::R_SPARC_GOTDATA_OP_LOX10:
+      if (gsym->is_defined()
+          && !gsym->is_from_dynobj()
+          && !gsym->is_preemptible()
+         && !is_ifunc)
+       {
+         // We will optimize this into a GOT relative relocation
+         // and code transform the GOT load into an addition.
+         break;
+       }
     case elfcpp::R_SPARC_GOT10:
     case elfcpp::R_SPARC_GOT13:
     case elfcpp::R_SPARC_GOT22:
@@ -3076,6 +3140,7 @@ Target_sparc<size, big_endian>::Relocate::relocate(
                        typename elfcpp::Elf_types<size>::Elf_Addr address,
                        section_size_type view_size)
 {
+  bool orig_is_ifunc = psymval->is_ifunc_symbol();
   r_type &= 0xff;
 
   if (this->ignore_gd_add_)
@@ -3108,7 +3173,7 @@ Target_sparc<size, big_endian>::Relocate::relocate(
 
       psymval = &symval;
     }
-  else if (gsym == NULL && psymval->is_ifunc_symbol())
+  else if (gsym == NULL && orig_is_ifunc)
     {
       unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
       if (object->local_has_plt_offset(r_sym))
@@ -3125,11 +3190,24 @@ Target_sparc<size, big_endian>::Relocate::relocate(
   // pointer points to the beginning, not the end, of the table.
   // So we just use the plain offset.
   unsigned int got_offset = 0;
+  bool gdop_valid = false;
   switch (r_type)
     {
     case elfcpp::R_SPARC_GOTDATA_OP:
     case elfcpp::R_SPARC_GOTDATA_OP_HIX22:
     case elfcpp::R_SPARC_GOTDATA_OP_LOX10:
+      // If this is local, we did not create a GOT entry because we
+      // intend to transform this into a GOT relative relocation.
+      if (gsym == NULL
+         || (gsym->is_defined()
+             && !gsym->is_from_dynobj()
+             && !gsym->is_preemptible()
+             && !orig_is_ifunc))
+       {
+         got_offset = psymval->value(object, 0) - target->got_address();
+         gdop_valid = true;
+         break;
+       }
     case elfcpp::R_SPARC_GOT10:
     case elfcpp::R_SPARC_GOT13:
     case elfcpp::R_SPARC_GOT22:
@@ -3248,14 +3326,37 @@ Target_sparc<size, big_endian>::Relocate::relocate(
       break;
 
     case elfcpp::R_SPARC_GOTDATA_OP:
+      if (gdop_valid)
+       {
+         typedef typename elfcpp::Swap<32, true>::Valtype Insntype;
+         Insntype* wv = reinterpret_cast<Insntype*>(view);
+         Insntype val;
+
+         // {ld,ldx} [%rs1 + %rs2], %rd --> add %rs1, %rs2, %rd
+         val = elfcpp::Swap<32, true>::readval(wv);
+         val = 0x80000000 | (val & 0x3e07c01f);
+         elfcpp::Swap<32, true>::writeval(wv, val);
+       }
       break;
 
     case elfcpp::R_SPARC_GOTDATA_OP_LOX10:
+      if (gdop_valid)
+       {
+         Reloc::gdop_lox10(view, got_offset, addend);
+         break;
+       }
+      /* Fall through.  */
     case elfcpp::R_SPARC_GOT13:
       Reloc::rela32_13(view, got_offset, addend);
       break;
 
     case elfcpp::R_SPARC_GOTDATA_OP_HIX22:
+      if (gdop_valid)
+       {
+         Reloc::gdop_hix22(view, got_offset, addend);
+         break;
+       }
+      /* Fall through.  */
     case elfcpp::R_SPARC_GOT22:
       Reloc::hi22(view, got_offset, addend);
       break;