[4/5,GOLD] PowerPC got reloc optimisation

Message ID 10b11ae5eea282d32068c6de585cd013dc4f83c6.1562653487.git.amodra@gmail.com
State New
Headers show
Series
  • PowerPC pc-relative support
Related show

Commit Message

Alan Modra July 9, 2019, 8:56 a.m.
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.

Patch

diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index c8d4414df7..3748f116c1 100644
--- a/gold/powerpc.cc
+++ b/gold/powerpc.cc
@@ -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)
 	    {