* options.h (General_options): Add no_toc_optimize.
authorAlan Modra <amodra@gmail.com>
Fri, 7 Dec 2012 00:03:18 +0000 (00:03 +0000)
committerAlan Modra <amodra@gmail.com>
Fri, 7 Dec 2012 00:03:18 +0000 (00:03 +0000)
* powerpc.cc (ok_lo_toc_insn): New function.
(Target_powerpc::Relocate::relocate): Optimize toc access sequences.

gold/ChangeLog
gold/options.h
gold/powerpc.cc

index 53be4b6..25898c5 100644 (file)
@@ -1,3 +1,9 @@
+2012-12-07  Alan Modra  <amodra@gmail.com>
+
+       * options.h (General_options): Add no_toc_optimize.
+       * powerpc.cc (ok_lo_toc_insn): New function.
+       (Target_powerpc::Relocate::relocate): Optimize toc access sequences.
+
 2012-12-06  Alan Modra  <amodra@gmail.com>
 
        * options.h (General_options): Add plt_align, plt_static_chain,
index 216620a..1a25b7b 100644 (file)
@@ -1109,6 +1109,9 @@ class General_options
   DEFINE_uint64(Ttext, options::ONE_DASH, '\0', -1U,
                 N_("Set the address of the text segment"), N_("ADDRESS"));
 
+  DEFINE_bool(no_toc_optimize, options::TWO_DASHES, '\0', false,
+             N_("(PowerPC64 only) Don't optimize TOC code sequences"), NULL);
+
   DEFINE_set(undefined, options::TWO_DASHES, 'u',
             N_("Create undefined reference to SYMBOL"), N_("SYMBOL"));
 
index a5c6b38..7fa68d7 100644 (file)
@@ -5441,6 +5441,33 @@ Target_powerpc<size, big_endian>::do_finalize_sections(
     this->copy_relocs_.emit(this->rela_dyn_section(layout));
 }
 
+// Return TRUE iff INSN is one we expect on a _LO variety toc/got
+// reloc.
+
+static bool
+ok_lo_toc_insn(uint32_t insn)
+{
+  return ((insn & (0x3f << 26)) == 14u << 26 /* addi */
+         || (insn & (0x3f << 26)) == 32u << 26 /* lwz */
+         || (insn & (0x3f << 26)) == 34u << 26 /* lbz */
+         || (insn & (0x3f << 26)) == 36u << 26 /* stw */
+         || (insn & (0x3f << 26)) == 38u << 26 /* stb */
+         || (insn & (0x3f << 26)) == 40u << 26 /* lhz */
+         || (insn & (0x3f << 26)) == 42u << 26 /* lha */
+         || (insn & (0x3f << 26)) == 44u << 26 /* sth */
+         || (insn & (0x3f << 26)) == 46u << 26 /* lmw */
+         || (insn & (0x3f << 26)) == 47u << 26 /* stmw */
+         || (insn & (0x3f << 26)) == 48u << 26 /* lfs */
+         || (insn & (0x3f << 26)) == 50u << 26 /* lfd */
+         || (insn & (0x3f << 26)) == 52u << 26 /* stfs */
+         || (insn & (0x3f << 26)) == 54u << 26 /* stfd */
+         || ((insn & (0x3f << 26)) == 58u << 26 /* lwa,ld,lmd */
+             && (insn & 3) != 1)
+         || ((insn & (0x3f << 26)) == 62u << 26 /* std, stmd */
+             && ((insn & 3) == 0 || (insn & 3) == 3))
+         || (insn & (0x3f << 26)) == 12u << 26 /* addic */);
+}
+
 // Return the value to use for a branch relocation.
 
 template<int size, bool big_endian>
@@ -6021,6 +6048,75 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       break;
     }
 
+  if (size == 64)
+    {
+      // Multi-instruction sequences that access the TOC can be
+      // optimized, eg. addis ra,r2,0; addi rb,ra,x;
+      // to             nop;           addi rb,r2,x;
+      switch (r_type)
+       {
+       default:
+         break;
+
+       case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
+       case elfcpp::R_POWERPC_GOT_TLSGD16_HA:
+       case elfcpp::R_POWERPC_GOT_TPREL16_HA:
+       case elfcpp::R_POWERPC_GOT_DTPREL16_HA:
+       case elfcpp::R_POWERPC_GOT16_HA:
+       case elfcpp::R_PPC64_TOC16_HA:
+         if (!parameters->options().no_toc_optimize())
+           {
+             Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+             Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+             if ((insn & ((0x3f << 26) | 0x1f << 16))
+                 != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */)
+               gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
+                                      _("toc optimization is not supported "
+                                        "for %#08x instruction"), insn);
+             else if (value + 0x8000 < 0x10000)
+               {
+                 elfcpp::Swap<32, big_endian>::writeval(iview, nop);
+                 return true;
+               }
+           }
+         break;
+
+       case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
+       case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
+       case elfcpp::R_POWERPC_GOT_TPREL16_LO:
+       case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
+       case elfcpp::R_POWERPC_GOT16_LO:
+       case elfcpp::R_PPC64_GOT16_LO_DS:
+       case elfcpp::R_PPC64_TOC16_LO:
+       case elfcpp::R_PPC64_TOC16_LO_DS:
+         if (!parameters->options().no_toc_optimize())
+           {
+             Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+             Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+             if (!ok_lo_toc_insn(insn))
+               gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
+                                      _("toc optimization is not supported "
+                                        "for %#08x instruction"), insn);
+             else if (value + 0x8000 < 0x10000)
+               {
+                 if ((insn & (0x3f << 26)) == 12u << 26 /* addic */)
+                   {
+                     // Transform addic to addi when we change reg.
+                     insn &= ~((0x3f << 26) | (0x1f << 16));
+                     insn |= (14u << 26) | (2 << 16);
+                   }
+                 else
+                   {
+                     insn &= ~(0x1f << 16);
+                     insn |= 2 << 16;
+                   }
+                 elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+               }
+           }
+         break;
+       }
+    }
+
   typename Reloc::Overflow_check overflow = Reloc::CHECK_NONE;
   switch (r_type)
     {