[GOLD] PowerPC64 localentry:0 plt call optimization
authorAlan Modra <amodra@gmail.com>
Fri, 23 Jun 2017 11:09:43 +0000 (20:39 +0930)
committerAlan Modra <amodra@gmail.com>
Fri, 23 Jun 2017 11:09:43 +0000 (20:39 +0930)
elfcpp/
* elfcpp.h (DT_PPC64_OPT): Define.
* powerpc.h (PPC64_OPT_TLS, PPC64_OPT_MULTI_TOC,
PPC64_OPT_LOCALENTRY): Define.
gold/
* options.h (General_options): Add plt_localentry.
* powerpc.cc (Target_powerpc::st_other): New function.
(Target_powerpc::plt_localentry0_, plt_localentry0_init_,
has_localentry0_): New vars.
(Target_powerpc::plt_localentry0, set_has_localentry0,
is_elfv2_localentry0): New functions.
(Target_powerpc::Branch_info::mark_pltcall): Don't set tocsave or
return true for localentry:0 calls.
(Stub_table::Plt_stub_ent::localentry0_): New var.
(Stub_table::add_plt_call_entry): Set localentry0_ and has_localentry0_.
Don't set r2save_ for localentry:0 calls.
(Output_data_glink::do_write): Save r2 in __glink_PLTresolve for elfv2.
(Target_powerpc::scan_relocs): Default plt_localentry0_.
(Target_powerpc::do_finalize_sections): Set DT_PPC64_OPT.
(Target_powerpc::Relocate::relocate): Don't require nop following
calls for localentry:0 plt calls, and don't change nop.

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

index 6a61d61fee1bf2809d6731be3394909dd7b3ee32..0be1bcb9cdd0dee96011b896f6b09acb8da01fed 100644 (file)
@@ -1,3 +1,9 @@
+2017-06-21  Alan Modra  <amodra@gmail.com>
+
+       * elfcpp.h (DT_PPC64_OPT): Define.
+       * powerpc.h (PPC64_OPT_TLS, PPC64_OPT_MULTI_TOC,
+       PPC64_OPT_LOCALENTRY): Define.
+
 2017-01-02  Alan Modra  <amodra@gmail.com>
 
        Update year range in copyright notice of all files.
index 82eb37feb3e29c9ad37289cddfe72e76720aa117..a57f5476f1205f1d1d7c558d4c77f74cbfcde872 100644 (file)
@@ -775,6 +775,9 @@ enum DT
   DT_PPC64_OPD = 0x70000001,
   DT_PPC64_OPDSZ = 0x70000002,
 
+  // Specify whether various optimisations are possible.
+  DT_PPC64_OPT = 0x70000003,
+
   // The index of an STT_SPARC_REGISTER symbol within the DT_SYMTAB
   // symbol table.  One dynamic entry exists for every STT_SPARC_REGISTER
   // symbol in the symbol table.
index 99647bd98551e393b9693fe896468611f03a7df7..3dc082867774b9f158b3db01f7483ef82d9ad759 100644 (file)
@@ -228,6 +228,14 @@ enum
   EF_PPC64_ABI = 3
 };
 
+// DT_PPC64_OPT bits
+enum
+{
+  PPC64_OPT_TLS = 1,
+  PPC64_OPT_MULTI_TOC = 2,
+  PPC64_OPT_LOCALENTRY = 4
+};
+
 enum
 {
   // The ELFv2 ABI uses three bits in the symbol st_other field of a
index 0428b038b919e4d2496d2c84631ff0617948acb6..7a0a1cfc845543a0b44eaedd88e319997bab2cb3 100644 (file)
@@ -1,3 +1,22 @@
+2017-06-23  Alan Modra  <amodra@gmail.com>
+
+       * options.h (General_options): Add plt_localentry.
+       * powerpc.cc (Target_powerpc::st_other): New function.
+       (Target_powerpc::plt_localentry0_, plt_localentry0_init_,
+       has_localentry0_): New vars.
+       (Target_powerpc::plt_localentry0, set_has_localentry0,
+       is_elfv2_localentry0): New functions.
+       (Target_powerpc::Branch_info::mark_pltcall): Don't set tocsave or
+       return true for localentry:0 calls.
+       (Stub_table::Plt_stub_ent::localentry0_): New var.
+       (Stub_table::add_plt_call_entry): Set localentry0_ and has_localentry0_.
+       Don't set r2save_ for localentry:0 calls.
+       (Output_data_glink::do_write): Save r2 in __glink_PLTresolve for elfv2.
+       (Target_powerpc::scan_relocs): Default plt_localentry0_.
+       (Target_powerpc::do_finalize_sections): Set DT_PPC64_OPT.
+       (Target_powerpc::Relocate::relocate): Don't require nop following
+       calls for localentry:0 plt calls, and don't change nop.
+
 2017-06-23  Alan Modra  <amodra@gmail.com>
 
        * powerpc.cc (Target_powerpc::tocsave_loc_): New var.
index 202d4b0e8d20249a0eef1d3f9a63f19ca41ed038..c7c032bbd7708456cd2c70b91da0528f8ad12c9b 100644 (file)
@@ -1104,6 +1104,10 @@ class General_options
             N_("(PowerPC64 only) Align PLT call stubs to fit cache lines"),
             N_("[=P2ALIGN]"), true, int, int, options::parse_uint, false);
 
+  DEFINE_bool(plt_localentry, options::TWO_DASHES, '\0', false,
+             N_("(PowerPC64 only) Optimize calls to ELFv2 localentry:0 functions"),
+             N_("(PowerPC64 only) Don't optimize ELFv2 calls"));
+
   DEFINE_bool(plt_static_chain, options::TWO_DASHES, '\0', false,
              N_("(PowerPC64 only) PLT call stubs should load r11"),
              N_("(PowerPC64 only) PLT call stubs should not load r11"));
index 0d1f0e6c208a68ecb1d12a65b6c628dc367022a6..a046bbee33f7ed127bd064a00852fd0ced22b191 100644 (file)
@@ -372,6 +372,12 @@ public:
   void
   set_abiversion(int ver);
 
+  unsigned int
+  st_other (unsigned int symndx) const
+  {
+    return this->st_other_[symndx];
+  }
+
   unsigned int
   ppc64_local_entry_offset(const Symbol* sym) const
   { return elfcpp::ppc64_decode_local_entry(sym->nonvis() >> 3); }
@@ -605,7 +611,9 @@ class Target_powerpc : public Sized_target<size, big_endian>
       glink_(NULL), rela_dyn_(NULL), copy_relocs_(),
       tlsld_got_offset_(-1U),
       stub_tables_(), branch_lookup_table_(), branch_info_(), tocsave_loc_(),
-      plt_thread_safe_(false), relax_failed_(false), relax_fail_count_(0),
+      plt_thread_safe_(false), plt_localentry0_(false),
+      plt_localentry0_init_(false), has_localentry0_(false),
+      relax_failed_(false), relax_fail_count_(0),
       stub_group_size_(0), savres_section_(0)
   {
   }
@@ -1000,6 +1008,49 @@ class Target_powerpc : public Sized_target<size, big_endian>
   plt_thread_safe() const
   { return this->plt_thread_safe_; }
 
+  bool
+  plt_localentry0() const
+  { return this->plt_localentry0_; }
+
+  void
+  set_has_localentry0()
+  {
+    this->has_localentry0_ = true;
+  }
+
+  bool
+  is_elfv2_localentry0(const Symbol* gsym) const
+  {
+    return (size == 64
+           && this->abiversion() >= 2
+           && this->plt_localentry0()
+           && gsym->type() == elfcpp::STT_FUNC
+           && gsym->is_defined()
+           && gsym->nonvis() >> 3 == 0);
+  }
+
+  bool
+  is_elfv2_localentry0(const Sized_relobj_file<size, big_endian>* object,
+                      unsigned int r_sym) const
+  {
+    const Powerpc_relobj<size, big_endian>* ppc_object
+      = static_cast<const Powerpc_relobj<size, big_endian>*>(object);
+
+    if (size == 64
+       && this->abiversion() >= 2
+       && this->plt_localentry0()
+       && ppc_object->st_other(r_sym) >> 5 == 0)
+      {
+       const Symbol_value<size>* psymval = object->local_symbol(r_sym);
+       bool is_ordinary;
+       if (!psymval->is_ifunc_symbol()
+           && psymval->input_shndx(&is_ordinary) != elfcpp::SHN_UNDEF
+           && is_ordinary)
+         return true;
+      }
+    return false;
+  }
+
   int
   abiversion () const
   { return this->processor_specific_flags() & elfcpp::EF_PPC64_ABI; }
@@ -1474,6 +1525,9 @@ class Target_powerpc : public Sized_target<size, big_endian>
   Tocsave_loc tocsave_loc_;
 
   bool plt_thread_safe_;
+  bool plt_localentry0_;
+  bool plt_localentry0_init_;
+  bool has_localentry0_;
 
   bool relax_failed_;
   int relax_fail_count_;
@@ -2953,8 +3007,10 @@ Target_powerpc<size, big_endian>::Branch_info::mark_pltcall(
     sym = symtab->resolve_forwards(sym);
   const Sized_symbol<size>* gsym = static_cast<const Sized_symbol<size>*>(sym);
   if (gsym != NULL
-      ? gsym->use_plt_offset(Scan::get_reference_flags(this->r_type_, target))
-      : this->object_->local_has_plt_offset(this->r_sym_))
+      ? (gsym->use_plt_offset(Scan::get_reference_flags(this->r_type_, target))
+        && !target->is_elfv2_localentry0(gsym))
+      : (this->object_->local_has_plt_offset(this->r_sym_)
+        && !target->is_elfv2_localentry0(this->object_, this->r_sym_)))
     {
       this->tocsave_ = 1;
       return true;
@@ -3988,12 +4044,13 @@ class Stub_table : public Output_relaxed_input_section
   struct Plt_stub_ent
   {
     Plt_stub_ent(unsigned int off, unsigned int indx)
-      : off_(off), indx_(indx), r2save_(0)
+      : off_(off), indx_(indx), r2save_(0), localentry0_(0)
     { }
 
     unsigned int off_;
-    unsigned int indx_ : 31;
+    unsigned int indx_ : 30;
     unsigned int r2save_ : 1;
+    unsigned int localentry0_ : 1;
   };
   typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
   static const Address invalid_address = static_cast<Address>(0) - 1;
@@ -4438,8 +4495,18 @@ Stub_table<size, big_endian>::add_plt_call_entry(
   std::pair<typename Plt_stub_entries::iterator, bool> p
     = this->plt_call_stubs_.insert(std::make_pair(key, ent));
   if (p.second)
-    this->plt_size_ = ent.off_ + this->plt_call_size(p.first);
-  if (size == 64 && !tocsave)
+    {
+      this->plt_size_ = ent.off_ + this->plt_call_size(p.first);
+      if (size == 64
+         && this->targ_->is_elfv2_localentry0(gsym))
+       {
+         p.first->second.localentry0_ = 1;
+         this->targ_->set_has_localentry0();
+       }
+    }
+  if (size == 64
+      && !tocsave
+      && !p.first->second.localentry0_)
     p.first->second.r2save_ = 1;
   return this->can_reach_stub(from, ent.off_, r_type);
 }
@@ -4459,8 +4526,18 @@ Stub_table<size, big_endian>::add_plt_call_entry(
   std::pair<typename Plt_stub_entries::iterator, bool> p
     = this->plt_call_stubs_.insert(std::make_pair(key, ent));
   if (p.second)
-    this->plt_size_ = ent.off_ + this->plt_call_size(p.first);
-  if (size == 64 && !tocsave)
+    {
+      this->plt_size_ = ent.off_ + this->plt_call_size(p.first);
+      if (size == 64
+         && this->targ_->is_elfv2_localentry0(object, locsym_index))
+       {
+         p.first->second.localentry0_ = 1;
+         this->targ_->set_has_localentry0();
+       }
+    }
+  if (size == 64
+      && !tocsave
+      && !p.first->second.localentry0_)
     p.first->second.r2save_ = 1;
   return this->can_reach_stub(from, ent.off_, r_type);
 }
@@ -5183,6 +5260,7 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of)
              write_insn<big_endian>(p, mflr_0),                p += 4;
              write_insn<big_endian>(p, bcl_20_31),             p += 4;
              write_insn<big_endian>(p, mflr_11),               p += 4;
+             write_insn<big_endian>(p, std_2_1 + 24),          p += 4;
              write_insn<big_endian>(p, ld_2_11 + l(-16)),      p += 4;
              write_insn<big_endian>(p, mtlr_0),                p += 4;
              write_insn<big_endian>(p, sub_12_12_11),          p += 4;
@@ -7574,6 +7652,21 @@ Target_powerpc<size, big_endian>::scan_relocs(
   typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, big_endian>
       Classify_reloc;
 
+  if (!this->plt_localentry0_init_)
+    {
+      bool plt_localentry0 = false;
+      if (size == 64
+         && this->abiversion() >= 2)
+       {
+         if (parameters->options().user_set_plt_localentry())
+           plt_localentry0 = parameters->options().plt_localentry();
+         else
+           plt_localentry0 = symtab->lookup("GLIBC_2.26", NULL) != NULL;
+       }
+      this->plt_localentry0_ = plt_localentry0;
+      this->plt_localentry0_init_ = true;
+    }
+
   if (sh_type == elfcpp::SHT_REL)
     {
       gold_error(_("%s: unsupported REL reloc section"),
@@ -7770,6 +7863,9 @@ Target_powerpc<size, big_endian>::do_finalize_sections(
                                            (this->glink_->pltresolve_size
                                             - 32));
            }
+         if (this->has_localentry0_)
+           odyn->add_constant(elfcpp::DT_PPC64_OPT,
+                              elfcpp::PPC64_OPT_LOCALENTRY);
        }
     }
 
@@ -7914,6 +8010,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
     = static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
   Address value = 0;
   bool has_stub_value = false;
+  bool localentry0 = false;
   unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
   if ((gsym != NULL
        ? gsym->use_plt_offset(Scan::get_reference_flags(r_type, target))
@@ -7969,6 +8066,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
                          && next_rela.get_r_offset() == rela.get_r_offset() + 4)
                        value += 4;
                    }
+                 localentry0 = ent->localentry0_;
                  has_stub_value = true;
                }
            }
@@ -8012,8 +8110,8 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
        {
          typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
          Valtype* wv = reinterpret_cast<Valtype*>(view);
-         bool can_plt_call = false;
-         if (rela.get_r_offset() + 8 <= view_size)
+         bool can_plt_call = localentry0;
+         if (!localentry0 && rela.get_r_offset() + 8 <= view_size)
            {
              Valtype insn = elfcpp::Swap<32, big_endian>::readval(wv);
              Valtype insn2 = elfcpp::Swap<32, big_endian>::readval(wv + 1);