[GOLD] PowerPC got reloc optimisation
authorAlan Modra <amodra@gmail.com>
Wed, 26 Jun 2019 12:38:54 +0000 (22:08 +0930)
committerAlan Modra <amodra@gmail.com>
Sat, 13 Jul 2019 00:27:50 +0000 (09:57 +0930)
Note that gold won't remove unused GOT entries, in contrast to ld.bfd
which will.

* powerpc.cc (Powerpc_relobj::make_got_relative): New function.
(relative_value_is_known): New functions.
(Target_powerpc::Relocate::relocate): Edit code using
GOT16_HA, GOT16_LO_DS, and GOT_PCREL34 relocs.

gold/ChangeLog
gold/powerpc.cc

index d6e2c0c5f7f1f00143c5bc6e8a94fcc30b7e0e17..01fc7bb0f11816d7cc3b4f458b296122d66f08f9 100644 (file)
@@ -1,3 +1,10 @@
+2019-07-13  Alan Modra  <amodra@gmail.com>
+
+       * powerpc.cc (Powerpc_relobj::make_got_relative): New function.
+       (relative_value_is_known): New functions.
+       (Target_powerpc::Relocate::relocate): Edit code using
+       GOT16_HA, GOT16_LO_DS, and GOT_PCREL34 relocs.
+
 2019-07-13  Alan Modra  <amodra@gmail.com>
 
        * powerpc.cc (Target_powerpc): Add powerxx_stubs_ and accessor
index c8d4414df7184a13f5ed8f339786f4a8eeb1e06f..3748f116c1632e69c757e9626f1b1fbf47976d04 100644 (file)
@@ -249,6 +249,12 @@ public:
   make_toc_relative(Target_powerpc<size, big_endian>* target,
                    Address* value);
 
+  bool
+  make_got_relative(Target_powerpc<size, big_endian>* target,
+                   const Symbol_value<size>* psymval,
+                   Address addend,
+                   Address* value);
+
   // Perform the Sized_relobj_file method, then set up opd info from
   // .opd relocs.
   void
@@ -2387,6 +2393,25 @@ Powerpc_relobj<size, big_endian>::make_toc_relative(
   return true;
 }
 
+template<int size, bool big_endian>
+bool
+Powerpc_relobj<size, big_endian>::make_got_relative(
+    Target_powerpc<size, big_endian>* target,
+    const Symbol_value<size>* psymval,
+    Address addend,
+    Address* value)
+{
+  Address addr = psymval->value(this, addend);
+  Address got_base = (target->got_section()->output_section()->address()
+                     + this->toc_base_offset());
+  addr -= got_base;
+  if (addr + 0x80008000 > 0xffffffff)
+    return false;
+
+  *value = addr;
+  return true;
+}
+
 // Perform the Sized_relobj_file method, then set up opd info from
 // .opd relocs.
 
@@ -9660,6 +9685,37 @@ Target_powerpc<size, big_endian>::symval_for_branch(
   return true;
 }
 
+template<int size>
+static bool
+relative_value_is_known(const Sized_symbol<size>* gsym)
+{
+  if (gsym->type() == elfcpp::STT_GNU_IFUNC)
+    return false;
+
+  if (gsym->is_from_dynobj()
+      || gsym->is_undefined()
+      || gsym->is_preemptible())
+    return false;
+
+  if (gsym->is_absolute())
+    return !parameters->options().output_is_position_independent();
+
+  return true;
+}
+
+template<int size>
+static bool
+relative_value_is_known(const Symbol_value<size>* psymval)
+{
+  if (psymval->is_ifunc_symbol())
+    return false;
+
+  bool is_ordinary;
+  unsigned int shndx = psymval->input_shndx(&is_ordinary);
+
+  return is_ordinary && shndx != elfcpp::SHN_UNDEF;
+}
+
 // Perform a relocation.
 
 template<int size, bool big_endian>
@@ -10446,7 +10502,10 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       break;
     }
 
-  if (size == 64)
+  if (size == 64
+      && (gsym
+         ? relative_value_is_known(gsym)
+         : relative_value_is_known(psymval)))
     {
       switch (r_type)
        {
@@ -10460,12 +10519,6 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
          // and
          //     addis ra,r2,0; addi rb,ra,x@toc@l;
          // to  nop;           addi rb,r2,x@toc;
-         // FIXME: the @got sequence shown above is not yet
-         // optimized.  Note that gcc as of 2017-01-07 doesn't use
-         // the ELF @got relocs except for TLS, instead using the
-         // PowerOpen variant of a compiler managed GOT (called TOC).
-         // The PowerOpen TOC sequence equivalent to the first
-         // example is optimized.
        case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
        case elfcpp::R_POWERPC_GOT_TLSGD16_HA:
        case elfcpp::R_POWERPC_GOT_TPREL16_HA:
@@ -10476,8 +10529,12 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
            {
              Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
              Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
-             if (r_type == elfcpp::R_PPC64_TOC16_HA
-                 && object->make_toc_relative(target, &value))
+             if ((r_type == elfcpp::R_PPC64_TOC16_HA
+                  && object->make_toc_relative(target, &value))
+                 || (r_type == elfcpp::R_POWERPC_GOT16_HA
+                     && object->make_got_relative(target, psymval,
+                                                  rela.get_r_addend(),
+                                                  &value)))
                {
                  gold_assert((insn & ((0x3f << 26) | 0x1f << 16))
                              == ((15u << 26) | (2 << 16)));
@@ -10505,8 +10562,12 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
              Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
              Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
              bool changed = false;
-             if (r_type == elfcpp::R_PPC64_TOC16_LO_DS
-                 && object->make_toc_relative(target, &value))
+             if ((r_type == elfcpp::R_PPC64_TOC16_LO_DS
+                  && object->make_toc_relative(target, &value))
+                 || (r_type == elfcpp::R_PPC64_GOT16_LO_DS
+                     && object->make_got_relative(target, psymval,
+                                                  rela.get_r_addend(),
+                                                  &value)))
                {
                  gold_assert ((insn & (0x3f << 26)) == 58u << 26 /* ld */);
                  insn ^= (14u << 26) ^ (58u << 26);
@@ -10534,6 +10595,31 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
            }
          break;
 
+       case elfcpp::R_PPC64_GOT_PCREL34:
+         if (parameters->options().toc_optimize())
+           {
+             Insn* iview = reinterpret_cast<Insn*>(view);
+             uint64_t insn = elfcpp::Swap<32, big_endian>::readval(iview);
+             insn <<= 32;
+             insn |= elfcpp::Swap<32, big_endian>::readval(iview + 1);
+             if ((insn & ((-1ULL << 50) | (63ULL << 26)))
+                  != ((1ULL << 58) | (1ULL << 52) | (57ULL << 26) /* pld */))
+               break;
+
+             Address relval = psymval->value(object, rela.get_r_addend());
+             relval -= address;
+             if (relval + (1ULL << 33) < 1ULL << 34)
+               {
+                 value = relval;
+                 // Replace with paddi
+                 insn += (2ULL << 56) + (14ULL << 26) - (57ULL << 26);
+                 elfcpp::Swap<32, big_endian>::writeval(iview, insn >> 32);
+                 elfcpp::Swap<32, big_endian>::writeval(iview + 1,
+                                                        insn & 0xffffffff);
+               }
+           }
+         break;
+
        case elfcpp::R_POWERPC_TPREL16_HA:
          if (parameters->options().tls_optimize() && value + 0x8000 < 0x10000)
            {