]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gold/powerpc.cc
PowerPC TPREL_HA/LO optimisation
[thirdparty/binutils-gdb.git] / gold / powerpc.cc
index 9c2a906bcde77647b446b819d4bd9cefaae88372..b77b14f67481475c948a11e8751ab4b7a96c6fba 100644 (file)
@@ -650,6 +650,7 @@ class Target_powerpc : public Sized_target<size, big_endian>
       powerxx_stubs_(false), plt_thread_safe_(false), plt_localentry0_(false),
       plt_localentry0_init_(false), has_localentry0_(false),
       has_tls_get_addr_opt_(false),
+      tprel_opt_(parameters->options().tls_optimize()),
       relax_failed_(false), relax_fail_count_(0),
       stub_group_size_(0), savres_section_(0),
       tls_get_addr_(NULL), tls_get_addr_opt_(NULL),
@@ -1136,6 +1137,14 @@ class Target_powerpc : public Sized_target<size, big_endian>
     return false;
   }
 
+  bool
+  tprel_opt() const
+  { return this->tprel_opt_; }
+
+  void
+  set_tprel_opt(bool val)
+  { this->tprel_opt_ = val; }
+
   // Remember any symbols seen with non-zero localentry, even those
   // not providing a definition
   bool
@@ -1693,6 +1702,7 @@ class Target_powerpc : public Sized_target<size, big_endian>
   bool plt_localentry0_init_;
   bool has_localentry0_;
   bool has_tls_get_addr_opt_;
+  bool tprel_opt_;
 
   bool relax_failed_;
   int relax_fail_count_;
@@ -8144,10 +8154,6 @@ Target_powerpc<size, big_endian>::Scan::local(
 
   switch (r_type)
     {
-    case elfcpp::R_POWERPC_TPREL16:
-    case elfcpp::R_POWERPC_TPREL16_LO:
-    case elfcpp::R_POWERPC_TPREL16_HI:
-    case elfcpp::R_POWERPC_TPREL16_HA:
     case elfcpp::R_PPC64_TPREL16_DS:
     case elfcpp::R_PPC64_TPREL16_LO_DS:
     case elfcpp::R_PPC64_TPREL16_HIGH:
@@ -8157,12 +8163,55 @@ Target_powerpc<size, big_endian>::Scan::local(
     case elfcpp::R_PPC64_TPREL16_HIGHEST:
     case elfcpp::R_PPC64_TPREL16_HIGHESTA:
     case elfcpp::R_PPC64_TPREL34:
+      if (size != 64)
+       break;
+      // Fall through.
+    case elfcpp::R_POWERPC_TPREL16:
+    case elfcpp::R_POWERPC_TPREL16_LO:
+    case elfcpp::R_POWERPC_TPREL16_HI:
+    case elfcpp::R_POWERPC_TPREL16_HA:
       layout->set_has_static_tls();
       break;
     default:
       break;
     }
 
+  switch (r_type)
+    {
+    case elfcpp::R_POWERPC_TPREL16_HA:
+      if (target->tprel_opt())
+       {
+         section_size_type slen;
+         const unsigned char* view = NULL;
+         view = ppc_object->section_contents(data_shndx, &slen, false);
+         section_size_type off
+           = convert_to_section_size_type(reloc.get_r_offset()) & -4;
+         if (off < slen)
+           {
+             uint32_t insn = elfcpp::Swap<32, big_endian>::readval(view + off);
+             if ((insn & ((0x3fu << 26) | 0x1f << 16))
+                 != ((15u << 26) | ((size == 32 ? 2 : 13) << 16)))
+               target->set_tprel_opt(false);
+           }
+       }
+      break;
+
+    case elfcpp::R_PPC64_TPREL16_HIGH:
+    case elfcpp::R_PPC64_TPREL16_HIGHA:
+    case elfcpp::R_PPC64_TPREL16_HIGHER:
+    case elfcpp::R_PPC64_TPREL16_HIGHERA:
+    case elfcpp::R_PPC64_TPREL16_HIGHEST:
+    case elfcpp::R_PPC64_TPREL16_HIGHESTA:
+      if (size != 64)
+       break;
+      // Fall through.
+    case elfcpp::R_POWERPC_TPREL16_HI:
+      target->set_tprel_opt(false);
+      break;
+    default:
+      break;
+    }
+
   switch (r_type)
     {
     case elfcpp::R_PPC64_D34:
@@ -8902,10 +8951,6 @@ Target_powerpc<size, big_endian>::Scan::global(
 
   switch (r_type)
     {
-    case elfcpp::R_POWERPC_TPREL16:
-    case elfcpp::R_POWERPC_TPREL16_LO:
-    case elfcpp::R_POWERPC_TPREL16_HI:
-    case elfcpp::R_POWERPC_TPREL16_HA:
     case elfcpp::R_PPC64_TPREL16_DS:
     case elfcpp::R_PPC64_TPREL16_LO_DS:
     case elfcpp::R_PPC64_TPREL16_HIGH:
@@ -8915,12 +8960,55 @@ Target_powerpc<size, big_endian>::Scan::global(
     case elfcpp::R_PPC64_TPREL16_HIGHEST:
     case elfcpp::R_PPC64_TPREL16_HIGHESTA:
     case elfcpp::R_PPC64_TPREL34:
+      if (size != 64)
+       break;
+      // Fall through.
+    case elfcpp::R_POWERPC_TPREL16:
+    case elfcpp::R_POWERPC_TPREL16_LO:
+    case elfcpp::R_POWERPC_TPREL16_HI:
+    case elfcpp::R_POWERPC_TPREL16_HA:
       layout->set_has_static_tls();
       break;
     default:
       break;
     }
 
+  switch (r_type)
+    {
+    case elfcpp::R_POWERPC_TPREL16_HA:
+      if (target->tprel_opt())
+       {
+         section_size_type slen;
+         const unsigned char* view = NULL;
+         view = ppc_object->section_contents(data_shndx, &slen, false);
+         section_size_type off
+           = convert_to_section_size_type(reloc.get_r_offset()) & -4;
+         if (off < slen)
+           {
+             uint32_t insn = elfcpp::Swap<32, big_endian>::readval(view + off);
+             if ((insn & ((0x3fu << 26) | 0x1f << 16))
+                 != ((15u << 26) | ((size == 32 ? 2 : 13) << 16)))
+               target->set_tprel_opt(false);
+           }
+       }
+      break;
+
+    case elfcpp::R_PPC64_TPREL16_HIGH:
+    case elfcpp::R_PPC64_TPREL16_HIGHA:
+    case elfcpp::R_PPC64_TPREL16_HIGHER:
+    case elfcpp::R_PPC64_TPREL16_HIGHERA:
+    case elfcpp::R_PPC64_TPREL16_HIGHEST:
+    case elfcpp::R_PPC64_TPREL16_HIGHESTA:
+      if (size != 64)
+       break;
+      // Fall through.
+    case elfcpp::R_POWERPC_TPREL16_HI:
+      target->set_tprel_opt(false);
+      break;
+    default:
+      break;
+    }
+
   switch (r_type)
     {
     case elfcpp::R_PPC64_D34:
@@ -10876,10 +10964,9 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       break;
     }
 
-  if (size == 64
-      && (gsym
-         ? relative_value_is_known(gsym)
-         : relative_value_is_known(psymval)))
+  if (gsym
+      ? relative_value_is_known(gsym)
+      : relative_value_is_known(psymval))
     {
       Insn* iview;
       Insn* iview2;
@@ -10904,7 +10991,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
        case elfcpp::R_POWERPC_GOT_DTPREL16_HA:
        case elfcpp::R_POWERPC_GOT16_HA:
        case elfcpp::R_PPC64_TOC16_HA:
-         if (parameters->options().toc_optimize())
+         if (size == 64 && parameters->options().toc_optimize())
            {
              iview = reinterpret_cast<Insn*>(view - d_offset);
              insn = elfcpp::Swap<32, big_endian>::readval(iview);
@@ -10936,7 +11023,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
        case elfcpp::R_PPC64_GOT16_LO_DS:
        case elfcpp::R_PPC64_TOC16_LO:
        case elfcpp::R_PPC64_TOC16_LO_DS:
-         if (parameters->options().toc_optimize())
+         if (size == 64 && parameters->options().toc_optimize())
            {
              iview = reinterpret_cast<Insn*>(view - d_offset);
              insn = elfcpp::Swap<32, big_endian>::readval(iview);
@@ -10975,7 +11062,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
          break;
 
        case elfcpp::R_PPC64_GOT_PCREL34:
-         if (parameters->options().toc_optimize())
+         if (size == 64 && parameters->options().toc_optimize())
            {
              iview = reinterpret_cast<Insn*>(view);
              pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
@@ -11001,63 +11088,57 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
          break;
 
        case elfcpp::R_PPC64_PCREL34:
-         {
-           iview = reinterpret_cast<Insn*>(view);
-           pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
-           pinsn <<= 32;
-           pinsn |= elfcpp::Swap<32, big_endian>::readval(iview + 1);
-           if ((pinsn & ((-1ULL << 50) | (63ULL << 26)))
-               != ((1ULL << 58) | (2ULL << 56) | (1ULL << 52)
-                   | (14ULL << 26) /* paddi */))
-             break;
+         if (size == 64)
+           {
+             iview = reinterpret_cast<Insn*>(view);
+             pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
+             pinsn <<= 32;
+             pinsn |= elfcpp::Swap<32, big_endian>::readval(iview + 1);
+             if ((pinsn & ((-1ULL << 50) | (63ULL << 26)))
+                 != ((1ULL << 58) | (2ULL << 56) | (1ULL << 52)
+                     | (14ULL << 26) /* paddi */))
+               break;
 
-         pcrelopt:
-           const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
-           elfcpp::Shdr<size, big_endian> shdr(relinfo->reloc_shdr);
-           size_t reloc_count = shdr.get_sh_size() / reloc_size;
-           if (relnum >= reloc_count - 1)
-             break;
+           pcrelopt:
+             const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
+             elfcpp::Shdr<size, big_endian> shdr(relinfo->reloc_shdr);
+             size_t reloc_count = shdr.get_sh_size() / reloc_size;
+             if (relnum >= reloc_count - 1)
+               break;
 
-           Reltype next_rela(preloc + reloc_size);
-           if ((elfcpp::elf_r_type<size>(next_rela.get_r_info())
-                != elfcpp::R_PPC64_PCREL_OPT)
-               || next_rela.get_r_offset() != rela.get_r_offset())
-             break;
+             Reltype next_rela(preloc + reloc_size);
+             if ((elfcpp::elf_r_type<size>(next_rela.get_r_info())
+                  != elfcpp::R_PPC64_PCREL_OPT)
+                 || next_rela.get_r_offset() != rela.get_r_offset())
+               break;
 
-           Address off = next_rela.get_r_addend();
-           if (off == 0)
-             off = 8; // zero means next insn.
-           if (off + rela.get_r_offset() + 4 > view_size)
-             break;
+             Address off = next_rela.get_r_addend();
+             if (off == 0)
+               off = 8; // zero means next insn.
+             if (off + rela.get_r_offset() + 4 > view_size)
+               break;
 
-           iview2 = reinterpret_cast<Insn*>(view + off);
-           pinsn2 = elfcpp::Swap<32, big_endian>::readval(iview2);
-           pinsn2 <<= 32;
-           if ((pinsn2 & (63ULL << 58)) == 1ULL << 58)
-             break;
-           if (xlate_pcrel_opt(&pinsn, &pinsn2))
-             {
-               elfcpp::Swap<32, big_endian>::writeval(iview, pinsn >> 32);
-               elfcpp::Swap<32, big_endian>::writeval(iview + 1,
-                                                      pinsn & 0xffffffff);
-               elfcpp::Swap<32, big_endian>::writeval(iview2, pinsn2 >> 32);
-             }
-         }
+             iview2 = reinterpret_cast<Insn*>(view + off);
+             pinsn2 = elfcpp::Swap<32, big_endian>::readval(iview2);
+             pinsn2 <<= 32;
+             if ((pinsn2 & (63ULL << 58)) == 1ULL << 58)
+               break;
+             if (xlate_pcrel_opt(&pinsn, &pinsn2))
+               {
+                 elfcpp::Swap<32, big_endian>::writeval(iview, pinsn >> 32);
+                 elfcpp::Swap<32, big_endian>::writeval(iview + 1,
+                                                        pinsn & 0xffffffff);
+                 elfcpp::Swap<32, big_endian>::writeval(iview2, pinsn2 >> 32);
+               }
+           }
          break;
 
        case elfcpp::R_POWERPC_TPREL16_HA:
-         if (parameters->options().tls_optimize() && value + 0x8000 < 0x10000)
+         if (target->tprel_opt() && value + 0x8000 < 0x10000)
            {
              Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
-             Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
-             if ((insn & ((0x3f << 26) | 0x1f << 16))
-                 != ((15u << 26) | ((size == 32 ? 2 : 13) << 16)))
-               ;
-             else
-               {
-                 elfcpp::Swap<32, big_endian>::writeval(iview, nop);
-                 return true;
-               }
+             elfcpp::Swap<32, big_endian>::writeval(iview, nop);
+             return true;
            }
          break;
 
@@ -11067,7 +11148,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
            break;
          // Fall through.
        case elfcpp::R_POWERPC_TPREL16_LO:
-         if (parameters->options().tls_optimize() && value + 0x8000 < 0x10000)
+         if (target->tprel_opt() && value + 0x8000 < 0x10000)
            {
              Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
              Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
@@ -11078,29 +11159,12 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
          break;
 
        case elfcpp::R_PPC64_ENTRY:
-         value = (target->got_section()->output_section()->address()
-                  + object->toc_base_offset());
-         if (value + 0x80008000 <= 0xffffffff
-             && !parameters->options().output_is_position_independent())
-           {
-             Insn* iview = reinterpret_cast<Insn*>(view);
-             Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview);
-             Insn insn2 = elfcpp::Swap<32, big_endian>::readval(iview + 1);
-
-             if ((insn1 & ~0xfffc) == ld_2_12
-                 && insn2 == add_2_2_12)
-               {
-                 insn1 = lis_2 + ha(value);
-                 elfcpp::Swap<32, big_endian>::writeval(iview, insn1);
-                 insn2 = addi_2_2 + l(value);
-                 elfcpp::Swap<32, big_endian>::writeval(iview + 1, insn2);
-                 return true;
-               }
-           }
-         else
+         if (size == 64)
            {
-             value -= address;
-             if (value + 0x80008000 <= 0xffffffff)
+             value = (target->got_section()->output_section()->address()
+                      + object->toc_base_offset());
+             if (value + 0x80008000 <= 0xffffffff
+                 && !parameters->options().output_is_position_independent())
                {
                  Insn* iview = reinterpret_cast<Insn*>(view);
                  Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview);
@@ -11109,13 +11173,33 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
                  if ((insn1 & ~0xfffc) == ld_2_12
                      && insn2 == add_2_2_12)
                    {
-                     insn1 = addis_2_12 + ha(value);
+                     insn1 = lis_2 + ha(value);
                      elfcpp::Swap<32, big_endian>::writeval(iview, insn1);
                      insn2 = addi_2_2 + l(value);
                      elfcpp::Swap<32, big_endian>::writeval(iview + 1, insn2);
                      return true;
                    }
                }
+             else
+               {
+                 value -= address;
+                 if (value + 0x80008000 <= 0xffffffff)
+                   {
+                     Insn* iview = reinterpret_cast<Insn*>(view);
+                     Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview);
+                     Insn insn2 = elfcpp::Swap<32, big_endian>::readval(iview + 1);
+
+                     if ((insn1 & ~0xfffc) == ld_2_12
+                         && insn2 == add_2_2_12)
+                       {
+                         insn1 = addis_2_12 + ha(value);
+                         elfcpp::Swap<32, big_endian>::writeval(iview, insn1);
+                         insn2 = addi_2_2 + l(value);
+                         elfcpp::Swap<32, big_endian>::writeval(iview + 1, insn2);
+                         return true;
+                       }
+                   }
+               }
            }
          break;
 
@@ -11127,7 +11211,8 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
          //            lis 2,.TOC.@ha
          //            addi 2,2,.TOC.@l
          // if .TOC. is in range.  */
-         if (value + address - 4 + 0x80008000 <= 0xffffffff
+         if (size == 64
+             && value + address - 4 + 0x80008000 <= 0xffffffff
              && relnum + 1 > 1
              && preloc != NULL
              && target->abiversion() >= 2