* config/tc-sparc.c (sparc_relax): New.
authorJakub Jelinek <jakub@redhat.com>
Fri, 26 May 2000 06:45:55 +0000 (06:45 +0000)
committerJakub Jelinek <jakub@redhat.com>
Fri, 26 May 2000 06:45:55 +0000 (06:45 +0000)
(md_longopts): Add -relax and -no-relax options.
(md_parse_options, md_show_usage): Likewise.
(md_apply_fix3): Optimize tail call into branch always if possible.

gas/ChangeLog
gas/config/tc-sparc.c

index 39a43ed3a374cfe02f781015cc619add0bbf551e..6a0764cb1c1e88e06c6bbda636cb946b7ca9ac60 100644 (file)
@@ -1,3 +1,10 @@
+2000-05-26  Jakub Jelinek  <jakub@redhat.com>
+
+       * config/tc-sparc.c (sparc_relax): New.
+       (md_longopts): Add -relax and -no-relax options.
+       (md_parse_options, md_show_usage): Likewise.
+       (md_apply_fix3): Optimize tail call into branch always if possible.
+
 Thu May  4 15:27:07 2000  Donald Lindsay  <dlindsay@cygnus.com>
 
        * config/tc-d10v.c (write_2_short, parallel_ok, md_assemble,
index 149610e4699f263061c5d24b128b18e767e8193d..bd086d22d920fd93efbab4f712f1036ef04809cd 100644 (file)
@@ -91,6 +91,9 @@ static enum sparc_opcode_arch_val warn_after_architecture;
    has been used in -64.  */
 static int no_undeclared_regs;
 
+/* Non-zero if we should try to relax jumps and calls.  */
+static int sparc_relax;
+
 /* Non-zero if we are generating PIC code.  */
 int sparc_pic_code;
 
@@ -415,6 +418,10 @@ struct option md_longopts[] = {
 #define OPTION_UNDECLARED_REGS (OPTION_MD_BASE + 13)
   {"undeclared-regs", no_argument, NULL, OPTION_UNDECLARED_REGS},
 #endif
+#define OPTION_RELAX (OPTION_MD_BASE + 14)
+  {"relax", no_argument, NULL, OPTION_RELAX},
+#define OPTION_NO_RELAX (OPTION_MD_BASE + 15)
+  {"no-relax", no_argument, NULL, OPTION_NO_RELAX},
   {NULL, no_argument, NULL, 0}
 };
 size_t md_longopts_size = sizeof(md_longopts);
@@ -574,6 +581,14 @@ md_parse_option (c, arg)
       break;
 #endif
 
+    case OPTION_RELAX:
+      sparc_relax = 1;
+      break;
+
+    case OPTION_NO_RELAX:
+      sparc_relax = 0;
+      break;
+
     default:
       return 0;
     }
@@ -605,7 +620,9 @@ md_show_usage (stream)
                        specify variant of SPARC architecture\n\
 -bump                  warn when assembler switches architectures\n\
 -sparc                 ignored\n\
---enforce-aligned-data force .long, etc., to be aligned correctly\n"));
+--enforce-aligned-data force .long, etc., to be aligned correctly\n\
+-relax                 relax jumps and branches (default)\n\
+-no-relax              avoid changing any jumps and branches\n"));
 #ifdef OBJ_AOUT
   fprintf (stream, _("\
 -k                     generate PIC\n"));
@@ -2915,7 +2932,91 @@ md_apply_fix3 (fixP, value, segment)
              || fixP->fx_addsy == NULL
              || symbol_section_p (fixP->fx_addsy))
            ++val;
+
          insn |= val & 0x3fffffff;
+
+         /* See if we have a delay slot */
+         if (sparc_relax && fixP->fx_where + 8 <= fixP->fx_frag->fr_fix)
+           {
+#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)
+
+             long delay;
+
+             /* 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.  */
+             if (INSN_BIG_ENDIAN)
+               delay = bfd_getb32 ((unsigned char *) buf + 4);
+             else
+               delay = bfd_getl32 ((unsigned char *) buf + 4);
+             if ((insn & OP(~0)) != OP(1) || (delay & OP(~0)) != OP(2))
+               break;
+             if ((delay & OP3(~0)) != OP3(0x3d) /* restore */
+                 && ((delay & OP3(0x28)) != 0 /* arithmetic */
+                     || ((delay & RD(~0)) != RD(O7))))
+               break;
+             if ((delay & RS1(~0)) == RS1(O7)
+                 || ((delay & F3I(~0)) == 0
+                     && (delay & RS2(~0)) == RS2(O7)))
+               break;
+             /* Ensure the branch will fit into simm22.  */
+             if ((val & 0x3fe00000)
+                 && (val & 0x3fe00000) != 0x3fe00000)
+               break;
+             /* Check if the arch is v9 and branch will fit
+                into simm19.  */
+             if (((val & 0x3c0000) == 0
+                  || (val & 0x3c0000) == 0x3c0000)
+                 && (sparc_arch_size == 64
+                     || current_architecture >= SPARC_OPCODE_ARCH_V9))
+               /* ba,pt %xcc */
+               insn = INSN_BPA | (val & 0x7ffff);
+             else
+               /* ba */
+               insn = INSN_BA | (val & 0x3fffff);
+             if (fixP->fx_where >= 4
+                 && ((delay & (0xffffffff ^ RS1(~0)))
+                     == (INSN_OR | RD(O7) | RS2(G0))))
+               {
+                 long setter;
+                 int reg;
+
+                 if (INSN_BIG_ENDIAN)
+                   setter = bfd_getb32 ((unsigned char *) buf - 4);
+                 else
+                   setter = bfd_getl32 ((unsigned char *) buf - 4);
+                 if ((setter & (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 = (delay & RS1(~0)) >> 14;
+                 if (reg != ((setter & RD(~0)) >> 25)
+                     || reg == G0 || reg == O7)
+                   break;
+
+                 if (INSN_BIG_ENDIAN)
+                   bfd_putb32 (INSN_NOP, (unsigned char *) buf + 4);
+                 else
+                   bfd_putl32 (INSN_NOP, (unsigned char *) buf + 4);
+               }
+           }
          break;
 
        case BFD_RELOC_SPARC_11: