aboutsummaryrefslogtreecommitdiff
path: root/gold/powerpc.cc
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2012-12-07 00:03:18 +0000
committerAlan Modra <amodra@gmail.com>2012-12-07 00:03:18 +0000
commitaba6bc71dd8b4f9944bc4c7f4cf73f58f7960595 (patch)
treef6d79afd5a3097bb9af9c00c9145adc528a6e111 /gold/powerpc.cc
parent9cb2f5a93d9fceb4420021e14a8c724e94c9e7e6 (diff)
downloadgdb-aba6bc71dd8b4f9944bc4c7f4cf73f58f7960595.zip
gdb-aba6bc71dd8b4f9944bc4c7f4cf73f58f7960595.tar.gz
gdb-aba6bc71dd8b4f9944bc4c7f4cf73f58f7960595.tar.bz2
* options.h (General_options): Add no_toc_optimize.
* powerpc.cc (ok_lo_toc_insn): New function. (Target_powerpc::Relocate::relocate): Optimize toc access sequences.
Diffstat (limited to 'gold/powerpc.cc')
-rw-r--r--gold/powerpc.cc96
1 files changed, 96 insertions, 0 deletions
diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index a5c6b38..7fa68d7 100644
--- a/gold/powerpc.cc
+++ b/gold/powerpc.cc
@@ -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)
{