* elf64-sparc.c (sparc64_elf_relax_section): New.
authorJakub Jelinek <jakub@redhat.com>
Fri, 26 May 2000 06:41:33 +0000 (06:41 +0000)
committerJakub Jelinek <jakub@redhat.com>
Fri, 26 May 2000 06:41:33 +0000 (06:41 +0000)
(sparc64_elf_relocate_section): Optimize tail call into branch always
if possible.
* elf32-sparc.c (elf32_sparc_relocate_section): Likewise.
(elf32_sparc_relax_section): New.

bfd/ChangeLog
bfd/elf32-sparc.c
bfd/elf64-sparc.c

index 8a87d01cd07c73c3cc632134ca47711cd6d70a65..52e4190211d381c270ff06a8b7a03b7b1e89de13 100644 (file)
@@ -1,3 +1,11 @@
+2000-05-26  Jakub Jelinek  <jakub@redhat.com>
+
+       * elf64-sparc.c (sparc64_elf_relax_section): New.
+       (sparc64_elf_relocate_section): Optimize tail call into branch always
+       if possible.
+       * elf32-sparc.c (elf32_sparc_relocate_section): Likewise.
+       (elf32_sparc_relax_section): New.
+
 2000-05-26  Jakub Jelinek  <jakub@redhat.com>
 
        * elf64-sparc.c: Add ATTRIBUTE_UNUSED to unused function parameters.
index 1c8d0ba275ae583cd3223e499950ea3aca8d5540..70d65a5f87bd3510805cf4a2e5f124c3c91efbe1 100644 (file)
@@ -24,6 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #include "libbfd.h"
 #include "elf-bfd.h"
 #include "elf/sparc.h"
+#include "opcode/sparc.h"
 
 static reloc_howto_type *elf32_sparc_reloc_type_lookup
   PARAMS ((bfd *, bfd_reloc_code_real_type));
@@ -36,6 +37,8 @@ static boolean elf32_sparc_adjust_dynamic_symbol
   PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
 static boolean elf32_sparc_size_dynamic_sections
   PARAMS ((bfd *, struct bfd_link_info *));
+static boolean elf32_sparc_relax_section
+  PARAMS ((bfd *, asection *, struct bfd_link_info *, boolean *));
 static boolean elf32_sparc_relocate_section
   PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
           Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
@@ -1059,6 +1062,23 @@ elf32_sparc_size_dynamic_sections (output_bfd, info)
   return true;
 }
 
+
+#define SET_SEC_DO_RELAX(section) do { elf_section_data(section)->tdata = (void *)1; } while (0)
+#define SEC_DO_RELAX(section) (elf_section_data(section)->tdata == (void *)1)
+
+/*ARGSUSED*/
+static boolean
+elf32_sparc_relax_section (abfd, section, link_info, again)
+     bfd *abfd ATTRIBUTE_UNUSED;
+     asection *section ATTRIBUTE_UNUSED;
+     struct bfd_link_info *link_info ATTRIBUTE_UNUSED;
+     boolean *again;
+{
+  *again = false;
+  SET_SEC_DO_RELAX (section);
+  return true;
+}
+
 /* Relocate a SPARC ELF section.  */
 
 static boolean
@@ -1518,6 +1538,7 @@ elf32_sparc_relocate_section (output_bfd, info, input_bfd, input_section,
          break;
        }
 
+      r = bfd_reloc_continue;
       if (r_type == R_SPARC_WDISP16)
        {
          bfd_vma x;
@@ -1549,7 +1570,97 @@ elf32_sparc_relocate_section (output_bfd, info, input_bfd, input_section,
          bfd_putl32 (/*input_bfd,*/ x, contents + rel->r_offset);
          r = bfd_reloc_ok;
        }
-      else
+      else if ((r_type == R_SPARC_WDISP30 || r_type == R_SPARC_WPLT30)
+              && SEC_DO_RELAX (input_section)
+              && rel->r_offset + 4 < input_section->_raw_size)
+       {
+#define G0             0
+#define O7             15
+#define XCC            (2 << 20)
+#define COND(x)                (((x)&0xf)<<25)
+#define CONDA          COND(0x8)
+#define INSN_BPA       (F2(0,1) | CONDA | BPRED | XCC)
+#define INSN_BA                (F2(0,2) | CONDA)
+#define INSN_OR                F3(2, 0x2, 0)
+#define INSN_NOP       F2(0,4)
+
+         bfd_vma x, y;
+
+         /* If the instruction is a call with either:
+            restore
+            arithmetic instruction with rd == %o7
+            where rs1 != %o7 and rs2 if it is register != %o7
+            then we can optimize if the call destination is near
+            by changing the call into a branch always.  */
+         x = bfd_get_32 (input_bfd, contents + rel->r_offset);
+         y = bfd_get_32 (input_bfd, contents + rel->r_offset + 4);
+         if ((x & OP(~0)) == OP(1) && (y & OP(~0)) == OP(2))
+           {
+             if (((y & OP3(~0)) == OP3(0x3d) /* restore */
+                  || ((y & OP3(0x28)) == 0 /* arithmetic */
+                      && (y & RD(~0)) == RD(O7)))
+                 && (y & RS1(~0)) != RS1(O7)
+                 && ((y & F3I(~0))
+                     || (y & RS2(~0)) != RS2(O7)))
+               {
+                 bfd_vma reloc;
+
+                 reloc = relocation + rel->r_addend - rel->r_offset;
+                 reloc -= (input_section->output_section->vma
+                          + input_section->output_offset);
+
+                 /* Ensure the reloc fits into simm22.  */
+                 if ((reloc & 3) == 0
+                     && ((reloc & ~(bfd_vma)0x7fffff) == 0
+                         || ((reloc | 0x7fffff) == ~(bfd_vma)0)))
+                   {
+                     reloc >>= 2;
+               
+                     /* Check whether it fits into simm19 on v9.  */
+                     if (((reloc & 0x3c0000) == 0
+                          || (reloc & 0x3c0000) == 0x3c0000)
+                         && (elf_elfheader (output_bfd)->e_flags & EF_SPARC_32PLUS))
+                       x = INSN_BPA | (reloc & 0x7ffff); /* ba,pt %xcc */
+                     else
+                       x = INSN_BA | (reloc & 0x3fffff); /* ba */
+                     bfd_put_32 (input_bfd, x, contents + rel->r_offset);
+                     r = bfd_reloc_ok;
+                     if (rel->r_offset >= 4
+                         && (y & (0xffffffff ^ RS1(~0)))
+                             == (INSN_OR | RD(O7) | RS2(G0)))
+                       {
+                         bfd_vma z;
+                         unsigned int reg;
+
+                         z = bfd_get_32 (input_bfd,
+                                         contents + rel->r_offset - 4);
+                         if ((z & (0xffffffff ^ RD(~0)))
+                             != (INSN_OR | RS1(O7) | RS2(G0)))
+                           break;
+
+                         /* The sequence was
+                            or %o7, %g0, %rN
+                            call foo
+                            or %rN, %g0, %o7
+
+                            If call foo was replaced with ba, replace
+                            or %rN, %g0, %o7 with nop.  */
+
+                         reg = (y & RS1(~0)) >> 14;
+                         if (reg != ((z & RD(~0)) >> 25)
+                             || reg == G0 || reg == O7)
+                           break;
+
+                         bfd_put_32 (input_bfd, INSN_NOP,
+                                     contents + rel->r_offset + 4);
+                       }
+
+                   }
+               }
+           }
+       }
+
+      if (r == bfd_reloc_continue)
        r = _bfd_final_link_relocate (howto, input_bfd, input_section,
                                      contents, rel->r_offset,
                                      relocation, rel->r_addend);
@@ -1967,6 +2078,7 @@ elf32_sparc_final_write_processing (abfd, linker)
 #define ELF_MAXPAGESIZE 0x10000
 
 #define bfd_elf32_bfd_reloc_type_lookup        elf32_sparc_reloc_type_lookup
+#define bfd_elf32_bfd_relax_section    elf32_sparc_relax_section
 #define elf_info_to_howto              elf32_sparc_info_to_howto
 #define elf_backend_create_dynamic_sections \
                                        _bfd_elf_create_dynamic_sections
index c9ac36c68092a570327de36c568c9f731410192c..ca2e7c1d2f4f5355bb15d88dd7c68383fcf68406 100644 (file)
@@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #include "sysdep.h"
 #include "libbfd.h"
 #include "elf-bfd.h"
+#include "opcode/sparc.h"
 
 /* This is defined if one wants to build upward compatible binaries
    with the original sparc64-elf toolchain.  The support is kept in for
@@ -65,6 +66,8 @@ static void sparc64_elf_symbol_processing
 static boolean sparc64_elf_merge_private_bfd_data
   PARAMS ((bfd *, bfd *));
 
+static boolean sparc64_elf_relax_section
+  PARAMS ((bfd *, asection *, struct bfd_link_info *, boolean *));
 static boolean sparc64_elf_relocate_section
   PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
           Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
@@ -1853,6 +1856,22 @@ sparc64_elf_size_dynamic_sections (output_bfd, info)
   return true;
 }
 \f
+#define SET_SEC_DO_RELAX(section) do { elf_section_data(section)->tdata = (void *)1; } while (0)
+#define SEC_DO_RELAX(section) (elf_section_data(section)->tdata == (void *)1)
+
+/*ARGSUSED*/
+static boolean
+sparc64_elf_relax_section (abfd, section, link_info, again)
+     bfd *abfd ATTRIBUTE_UNUSED;
+     asection *section ATTRIBUTE_UNUSED;
+     struct bfd_link_info *link_info ATTRIBUTE_UNUSED;
+     boolean *again;
+{
+  *again = false;
+  SET_SEC_DO_RELAX (section);
+  return true;
+}
+\f
 /* Relocate a SPARC64 ELF section.  */
 
 static boolean
@@ -2394,6 +2413,8 @@ sparc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
          relocation = (splt->output_section->vma
                        + splt->output_offset
                        + sparc64_elf_plt_entry_offset (h->plt.offset));
+         if (r_type == R_SPARC_WPLT30)
+           goto do_wplt30;
          goto do_default;
 
        case R_SPARC_OLO10:
@@ -2469,6 +2490,97 @@ sparc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
          }
          break;
 
+       case R_SPARC_WDISP30:
+       do_wplt30:
+         if (SEC_DO_RELAX (input_section)
+             && rel->r_offset + 4 < input_section->_raw_size)
+           {
+#define G0             0
+#define O7             15
+#define XCC            (2 << 20)
+#define COND(x)                (((x)&0xf)<<25)
+#define CONDA          COND(0x8)
+#define INSN_BPA       (F2(0,1) | CONDA | BPRED | XCC)
+#define INSN_BA                (F2(0,2) | CONDA)
+#define INSN_OR                F3(2, 0x2, 0)
+#define INSN_NOP       F2(0,4)
+
+             bfd_vma x, y;
+
+             /* If the instruction is a call with either:
+                restore
+                arithmetic instruction with rd == %o7
+                where rs1 != %o7 and rs2 if it is register != %o7
+                then we can optimize if the call destination is near
+                by changing the call into a branch always.  */
+             x = bfd_get_32 (input_bfd, contents + rel->r_offset);
+             y = bfd_get_32 (input_bfd, contents + rel->r_offset + 4);
+             if ((x & OP(~0)) == OP(1) && (y & OP(~0)) == OP(2))
+               {
+                 if (((y & OP3(~0)) == OP3(0x3d) /* restore */
+                      || ((y & OP3(0x28)) == 0 /* arithmetic */
+                          && (y & RD(~0)) == RD(O7)))
+                     && (y & RS1(~0)) != RS1(O7)
+                     && ((y & F3I(~0))
+                         || (y & RS2(~0)) != RS2(O7)))
+                   {
+                     bfd_vma reloc;
+
+                     reloc = relocation + rel->r_addend - rel->r_offset;
+                     reloc -= (input_section->output_section->vma
+                               + input_section->output_offset);
+                     if (reloc & 3)
+                       goto do_default;
+
+                     /* Ensure the branch fits into simm22.  */
+                     if ((reloc & ~(bfd_vma)0x7fffff)
+                          && ((reloc | 0x7fffff) != MINUS_ONE))
+                       goto do_default;
+                     reloc >>= 2;
+
+                     /* Check whether it fits into simm19.  */
+                     if ((reloc & 0x3c0000) == 0
+                         || (reloc & 0x3c0000) == 0x3c0000)
+                       x = INSN_BPA | (reloc & 0x7ffff); /* ba,pt %xcc */
+                     else
+                       x = INSN_BA | (reloc & 0x3fffff); /* ba */
+                     bfd_put_32 (input_bfd, x, contents + rel->r_offset);
+                     r = bfd_reloc_ok;
+                     if (rel->r_offset >= 4
+                         && (y & (0xffffffff ^ RS1(~0)))
+                            == (INSN_OR | RD(O7) | RS2(G0)))
+                       {
+                         bfd_vma z;
+                         unsigned int reg;
+
+                         z = bfd_get_32 (input_bfd,
+                                         contents + rel->r_offset - 4);
+                         if ((z & (0xffffffff ^ RD(~0)))
+                             != (INSN_OR | RS1(O7) | RS2(G0)))
+                           break;
+
+                         /* The sequence was
+                            or %o7, %g0, %rN
+                            call foo
+                            or %rN, %g0, %o7
+
+                            If call foo was replaced with ba, replace
+                            or %rN, %g0, %o7 with nop.  */
+
+                         reg = (y & RS1(~0)) >> 14;
+                         if (reg != ((z & RD(~0)) >> 25)
+                             || reg == G0 || reg == O7)
+                           break;
+
+                         bfd_put_32 (input_bfd, INSN_NOP,
+                                     contents + rel->r_offset + 4);
+                       }
+                     break;
+                   }
+               }
+           }
+         /* FALLTHROUGH */
+
        default:
        do_default:
          r = _bfd_final_link_relocate (howto, input_bfd, input_section,
@@ -2961,6 +3073,8 @@ const struct elf_size_info sparc64_elf_size_info =
   sparc64_elf_canonicalize_dynamic_reloc
 #define bfd_elf64_bfd_reloc_type_lookup \
   sparc64_elf_reloc_type_lookup
+#define bfd_elf64_bfd_relax_section \
+  sparc64_elf_relax_section
 
 #define elf_backend_create_dynamic_sections \
   _bfd_elf_create_dynamic_sections