* config/tc-sparc.c (U0x80000000, U0xffffffff): New constants.
[binutils-gdb.git] / gas / config / tc-mips.c
index 40f98dfd216078be6a1d24f2b370743e8d93934d..d64d4eb3e562b2e5016240c5557ade497f8741af 100644 (file)
@@ -174,10 +174,10 @@ struct mips_set_options
 };
 
 /* True if -mgp32 was passed.  */
-static int file_mips_gp32 = 0;
+static int file_mips_gp32 = -1;
 
 /* True if -mfp32 was passed.  */
-static int file_mips_fp32 = 0;
+static int file_mips_fp32 = -1;
 
 /* This is the struct we use to hold the current set of options.  Note
    that we must set the isa field to ISA_UNKNOWN and the mips16 field to
@@ -407,15 +407,23 @@ static offsetT mips_cprestore_offset = -1;
 
 /* Similiar for NewABI PIC code, where $gp is callee-saved.  NewABI has some
    more optimizations, it can use a register value instead of a memory-saved
-   offset and even an other than GP as global pointer.  */
+   offset and even an other register than $gp as global pointer.  */
 static offsetT mips_cpreturn_offset = -1;
 static int mips_cpreturn_register = -1;
 static int mips_gp_register = GP;
 
+/* Whether mips_cprestore_offset has been set in the current function
+   (or whether it has already been warned about, if not).  */
+static int mips_cprestore_valid = 0;
+
 /* This is the register which holds the stack frame, as set by the
    .frame pseudo-op.  This is needed to implement .cprestore.  */
 static int mips_frame_reg = SP;
 
+/* Whether mips_frame_reg has been set in the current function
+   (or whether it has already been warned about, if not).  */
+static int mips_frame_reg_valid = 0;
+
 /* To output NOP instructions correctly, we need to keep information
    about the previous two instructions.  */
 
@@ -694,6 +702,7 @@ static void mips16_ip PARAMS ((char *str, struct mips_cl_insn * ip));
 static void mips16_immed PARAMS ((char *, unsigned int, int, offsetT, boolean,
                                  boolean, boolean, unsigned long *,
                                  boolean *, unsigned short *));
+static int my_getPercentOp PARAMS ((char **, unsigned int *, int *));
 static int my_getSmallParser PARAMS ((char **, unsigned int *, int *));
 static int my_getSmallExpression PARAMS ((expressionS *, char *));
 static void my_getExpression PARAMS ((expressionS *, char *));
@@ -1056,6 +1065,71 @@ md_begin ()
                  "Use -march instead of -mcpu."));
     }
 
+#if 1
+  /* For backward compatibility, let -mipsN set various defaults.  */
+  /* This code should go away, to be replaced with something rather more
+     draconian.  Until GCC 3.1 has been released for some reasonable
+     amount of time, however, we need to support this.  */
+  if (mips_opts.isa != ISA_UNKNOWN)
+    {
+      /* Translate -mipsN to the appropriate settings of file_mips_gp32
+        and file_mips_fp32.  Tag binaries as using the mipsN ISA.  */
+      if (file_mips_gp32 < 0)
+       {
+         if (ISA_HAS_64BIT_REGS (mips_opts.isa))
+           file_mips_gp32 = 0;
+         else
+           file_mips_gp32 = 1;
+       }
+      if (file_mips_fp32 < 0)
+       {
+         if (ISA_HAS_64BIT_REGS (mips_opts.isa))
+           file_mips_fp32 = 0;
+         else
+           file_mips_fp32 = 1;
+       }
+
+      ci = mips_cpu_info_from_isa (mips_opts.isa);
+      assert (ci != NULL);
+      /* -mipsN has higher priority than -mcpu but lower than -march.  */
+      if (mips_arch == CPU_UNKNOWN)
+       mips_arch = ci->cpu;
+
+      /* Default mips_abi.  */
+      if (mips_opts.abi == NO_ABI)
+       {
+         if (mips_opts.isa == ISA_MIPS1 || mips_opts.isa == ISA_MIPS2)
+           mips_opts.abi = O32_ABI;
+         else if (mips_opts.isa == ISA_MIPS3 || mips_opts.isa == ISA_MIPS4)
+           mips_opts.abi = O64_ABI;
+       }
+    }
+
+  if (mips_arch == CPU_UNKNOWN && mips_cpu != CPU_UNKNOWN)
+    {
+      ci = mips_cpu_info_from_cpu (mips_cpu);
+      assert (ci != NULL);
+      mips_arch = ci->cpu;
+      as_warn (_("The -mcpu option is deprecated.  Please use -march and "
+                "-mtune instead."));
+    }
+
+  /* Set tune from -mcpu, not from -mipsN.  */
+  if (mips_tune == CPU_UNKNOWN && mips_cpu != CPU_UNKNOWN)
+    {
+      ci = mips_cpu_info_from_cpu (mips_cpu);
+      assert (ci != NULL);
+      mips_tune = ci->cpu;
+    }
+
+  /* At this point, mips_arch will either be CPU_UNKNOWN if no ARCH was
+     specified on the command line, or some other value if one was.
+     Similarly, mips_opts.isa will be ISA_UNKNOWN if not specified on
+     the command line, or will be set otherwise if one was.  */
+
+  if (mips_arch != CPU_UNKNOWN && mips_opts.isa != ISA_UNKNOWN)
+    /* Handled above.  */;
+#else
   if (mips_arch == CPU_UNKNOWN && mips_cpu != CPU_UNKNOWN)
     {
       ci = mips_cpu_info_from_cpu (mips_cpu);
@@ -1069,6 +1143,7 @@ md_begin ()
      specified on the command line, or some other value if one was.
      Similarly, mips_opts.isa will be ISA_UNKNOWN if not specified on
      the command line, or will be set otherwise if one was.  */
+
   if (mips_arch != CPU_UNKNOWN && mips_opts.isa != ISA_UNKNOWN)
     {
       /* We have to check if the isa is the default isa of arch.  Otherwise
@@ -1089,6 +1164,7 @@ md_begin ()
          mips_arch = ci->cpu;
        }
     }
+#endif
   else if (mips_arch != CPU_UNKNOWN && mips_opts.isa == ISA_UNKNOWN)
     {
       /* We have ARCH, we need ISA.  */
@@ -1150,6 +1226,11 @@ md_begin ()
   if (! bfd_set_arch_mach (stdoutput, bfd_arch_mips, mips_arch))
     as_warn (_("Could not set architecture and machine"));
 
+  if (file_mips_gp32 < 0)
+    file_mips_gp32 = 0;
+  if (file_mips_fp32 < 0)
+    file_mips_fp32 = 0;
+
   file_mips_isa = mips_opts.isa;
   file_mips_abi = mips_opts.abi;
   mips_opts.gp32 = file_mips_gp32;
@@ -3272,6 +3353,11 @@ check_absolute_expr (ip, ex)
            ? 1                          \
            : 0)
 
+/* Is the given value a sign-extended 32-bit value?  */
+#define IS_SEXT_32BIT_NUM(x)                                           \
+  (((x) &~ (offsetT) 0x7fffffff) == 0                                  \
+   || (((x) &~ (offsetT) 0x7fffffff) == ~ (offsetT) 0x7fffffff))
+
 /*                     load_register()
  *  This routine generates the least number of instructions neccessary to load
  *  an absolute expression value into a register.
@@ -3311,9 +3397,7 @@ load_register (counter, reg, ep, dbl)
                       (int) BFD_RELOC_LO16);
          return;
        }
-      else if ((((ep->X_add_number &~ (offsetT) 0x7fffffff) == 0
-                || ((ep->X_add_number &~ (offsetT) 0x7fffffff)
-                    == ~ (offsetT) 0x7fffffff))
+      else if ((IS_SEXT_32BIT_NUM (ep->X_add_number)
                && (! dbl
                    || ! ep->X_unsigned
                    || sizeof (ep->X_add_number) > 4
@@ -3339,7 +3423,8 @@ load_register (counter, reg, ep, dbl)
 
   if (HAVE_32BIT_GPRS)
     {
-      as_bad (_("Number larger than 32 bits"));
+      as_bad (_("Number (0x%lx) larger than 32 bits"),
+             (unsigned long) ep->X_add_number);
       macro_build ((char *) NULL, counter, ep, "addiu", "t,r,j", reg, 0,
                   (int) BFD_RELOC_LO16);
       return;
@@ -3374,7 +3459,7 @@ load_register (counter, reg, ep, dbl)
       int shift, bit;
       unsigned long hi, lo;
 
-      if (hi32.X_add_number == 0xffffffff)
+      if (hi32.X_add_number == (offsetT) 0xffffffff)
        {
          if ((lo32.X_add_number & 0xffff8000) == 0xffff8000)
            {
@@ -3513,27 +3598,27 @@ load_register (counter, reg, ep, dbl)
     {
       expressionS mid16;
 
-      if ((freg == 0) && (lo32.X_add_number == 0xffffffff))
+      if ((freg == 0) && (lo32.X_add_number == (offsetT) 0xffffffff))
        {
          macro_build ((char *) NULL, counter, &lo32, "lui", "t,u", reg,
                       (int) BFD_RELOC_HI16);
-         macro_build ((char *) NULL, counter, NULL, "dsrl32", "d,w,<", reg,
-                      reg, 0);
+         macro_build ((char *) NULL, counter, (expressionS *) NULL,
+                      "dsrl32", "d,w,<", reg, reg, 0);
          return;
        }
 
       if (freg != 0)
        {
-         macro_build ((char *) NULL, counter, NULL, "dsll", "d,w,<", reg,
-                      freg, 16);
+         macro_build ((char *) NULL, counter, (expressionS *) NULL, "dsll",
+                      "d,w,<", reg, freg, 16);
          freg = reg;
        }
       mid16 = lo32;
       mid16.X_add_number >>= 16;
       macro_build ((char *) NULL, counter, &mid16, "ori", "t,r,i", reg,
                   freg, (int) BFD_RELOC_LO16);
-      macro_build ((char *) NULL, counter, NULL, "dsll", "d,w,<", reg,
-                  reg, 16);
+      macro_build ((char *) NULL, counter, (expressionS *) NULL, "dsll",
+                  "d,w,<", reg, reg, 16);
       freg = reg;
     }
   if ((lo32.X_add_number & 0xffff) != 0)
@@ -3592,7 +3677,7 @@ load_address (counter, reg, ep, dbl, used_at)
           dsll         $reg,16
           daddiu       $reg,<sym>              (BFD_RELOC_LO16)
        */
-      if (HAVE_64BIT_ADDRESSES)
+      if (dbl)
        {
          p = NULL;
 
@@ -3644,13 +3729,12 @@ load_address (counter, reg, ep, dbl, used_at)
              p = frag_var (rs_machine_dependent, 8, 0,
                            RELAX_ENCODE (4, 8, 0, 4, 0,
                                          mips_opts.warn_about_macros),
-                           ep->X_add_symbol, (offsetT) 0, (char *) NULL);
+                           ep->X_add_symbol, 0, NULL);
            }
          macro_build_lui (p, counter, ep, reg);
          if (p != NULL)
            p += 4;
-         macro_build (p, counter, ep,
-                      HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
+         macro_build (p, counter, ep, dbl ? "daddiu" : "addiu",
                       "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
        }
     }
@@ -3713,15 +3797,13 @@ load_address (counter, reg, ep, dbl, used_at)
       macro_build ((char *) NULL, counter, ep, "lui", "t,u", reg,
                   (int) BFD_RELOC_MIPS_GOT_HI16);
       macro_build ((char *) NULL, counter, (expressionS *) NULL,
-                  HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                  "d,v,t", reg, reg, GP);
-      macro_build ((char *) NULL, counter, ep,
-                  HAVE_32BIT_ADDRESSES ? "lw" : "ld",
+                  dbl ? "daddu" : "addu", "d,v,t", reg, reg, GP);
+      macro_build ((char *) NULL, counter, ep, dbl ? "ld" : "lw",
                   "t,o(b)", reg, (int) BFD_RELOC_MIPS_GOT_LO16, reg);
       p = frag_var (rs_machine_dependent, 12 + off, 0,
                    RELAX_ENCODE (12, 12 + off, off, 8 + off, 0,
                                  mips_opts.warn_about_macros),
-                   ep->X_add_symbol, (offsetT) 0, (char *) NULL);
+                   ep->X_add_symbol, 0, NULL);
       if (off > 0)
        {
          /* We need a nop before loading from $gp.  This special
@@ -3731,20 +3813,19 @@ load_address (counter, reg, ep, dbl, used_at)
          macro_build (p, counter, (expressionS *) NULL, "nop", "");
          p += 4;
        }
-      macro_build (p, counter, ep, HAVE_32BIT_ADDRESSES ? "lw" : "ld",
+      macro_build (p, counter, ep, dbl ? "ld" : "lw",
                   "t,o(b)", reg, (int) BFD_RELOC_MIPS_GOT16, GP);
       p += 4;
       macro_build (p, counter, (expressionS *) NULL, "nop", "");
       p += 4;
-      macro_build (p, counter, ep, HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
+      macro_build (p, counter, ep, dbl ? "daddiu" : "addiu",
                   "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
       if (ex.X_add_number != 0)
        {
          if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
            as_bad (_("PIC code offset overflow (max 16 signed bits)"));
          ex.X_op = O_constant;
-         macro_build ((char *) NULL, counter, &ex,
-                      HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
+         macro_build ((char *) NULL, counter, &ex, dbl ? "daddiu" : "addiu",
                       "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
        }
     }
@@ -4071,7 +4152,7 @@ macro (ip)
       if (sreg == 0
          || (HAVE_32BIT_GPRS
              && imm_expr.X_op == O_constant
-             && imm_expr.X_add_number == 0xffffffff))
+             && imm_expr.X_add_number == (offsetT) 0xffffffff))
        goto do_false;
       if (imm_expr.X_op != O_constant)
        as_bad (_("Unsupported large constant"));
@@ -4216,7 +4297,7 @@ macro (ip)
       if (sreg == 0
          || (HAVE_32BIT_GPRS
              && imm_expr.X_op == O_constant
-             && imm_expr.X_add_number == 0xffffffff))
+             && imm_expr.X_add_number == (offsetT) 0xffffffff))
        goto do_true;
       if (imm_expr.X_op != O_constant)
        as_bad (_("Unsupported large constant"));
@@ -4632,8 +4713,7 @@ macro (ip)
                p = frag_var (rs_machine_dependent, 8, 0,
                              RELAX_ENCODE (4, 8, 0, 4, 0,
                                            mips_opts.warn_about_macros),
-                             offset_expr.X_add_symbol, (offsetT) 0,
-                             (char *) NULL);
+                             offset_expr.X_add_symbol, 0, NULL);
              }
            macro_build_lui (p, &icnt, &offset_expr, tempreg);
            if (p != NULL)
@@ -5042,6 +5122,18 @@ macro (ip)
                as_warn (_("No .cprestore pseudo-op used in PIC code"));
              else
                {
+                 if (! mips_frame_reg_valid)
+                   {
+                     as_warn (_("No .frame pseudo-op used in PIC code"));
+                     /* Quiet this warning.  */
+                     mips_frame_reg_valid = 1;
+                   }
+                 if (! mips_cprestore_valid)
+                   {
+                     as_warn (_("No .cprestore pseudo-op used in PIC code"));
+                     /* Quiet this warning.  */
+                     mips_cprestore_valid = 1;
+                   }
                  expr1.X_add_number = mips_cprestore_offset;
                  macro_build ((char *) NULL, &icnt, &expr1,
                               HAVE_32BIT_ADDRESSES ? "lw" : "ld", "t,o(b)",
@@ -5143,6 +5235,18 @@ macro (ip)
                as_warn (_("No .cprestore pseudo-op used in PIC code"));
              else
                {
+                 if (! mips_frame_reg_valid)
+                   {
+                     as_warn (_("No .frame pseudo-op used in PIC code"));
+                     /* Quiet this warning.  */
+                     mips_frame_reg_valid = 1;
+                   }
+                 if (! mips_cprestore_valid)
+                   {
+                     as_warn (_("No .cprestore pseudo-op used in PIC code"));
+                     /* Quiet this warning.  */
+                     mips_cprestore_valid = 1;
+                   }
                  if (mips_opts.noreorder)
                    macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                             "nop", "");
@@ -5443,8 +5547,17 @@ macro (ip)
               dsll     $tempreg,16
               daddu    $tempreg,$tempreg,$breg
               <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+
+            If we have 64-bit addresses, as an optimization, for
+            addresses which are 32-bit constants (e.g. kseg0/kseg1
+            addresses) we fall back to the 32-bit address generation
+            mechanism since it is more efficient.  This code should
+            probably attempt to generate 64-bit constants more
+            efficiently in general.
           */
-         if (HAVE_64BIT_ADDRESSES)
+         if (HAVE_64BIT_ADDRESSES
+             && !(offset_expr.X_op == O_constant
+                  && IS_SEXT_32BIT_NUM (offset_expr.X_add_number)))
            {
              p = NULL;
 
@@ -5507,8 +5620,7 @@ macro (ip)
                                              (mips_opts.warn_about_macros
                                               || (used_at
                                                   && mips_opts.noat))),
-                               offset_expr.X_add_symbol, (offsetT) 0,
-                               (char *) NULL);
+                               offset_expr.X_add_symbol, 0, NULL);
                  used_at = 0;
                }
              macro_build_lui (p, &icnt, &offset_expr, tempreg);
@@ -5532,8 +5644,7 @@ macro (ip)
                               treg, (int) BFD_RELOC_GPREL16, tempreg);
                  p = frag_var (rs_machine_dependent, 12, 0,
                                RELAX_ENCODE (8, 12, 0, 8, 0, 0),
-                               offset_expr.X_add_symbol, (offsetT) 0,
-                               (char *) NULL);
+                               offset_expr.X_add_symbol, 0, NULL);
                }
              macro_build_lui (p, &icnt, &offset_expr, tempreg);
              if (p != NULL)
@@ -5757,8 +5868,7 @@ macro (ip)
         upper 16 bits of the address.  */
       if (mips_pic == NO_PIC)
        {
-         /* FIXME: This won't work for a 64 bit address.  */
-         macro_build_lui ((char *) NULL, &icnt, &offset_expr, AT);
+         macro_build_lui (NULL, &icnt, &offset_expr, AT);
        }
       else if (mips_pic == SVR4_PIC)
        {
@@ -5862,7 +5972,7 @@ macro (ip)
          else
            {
              /* FIXME: This won't work for a 64 bit address.  */
-             macro_build_lui ((char *) NULL, &icnt, &offset_expr, AT);
+             macro_build_lui (NULL, &icnt, &offset_expr, AT);
            }
 
          if (mips_opts.isa != ISA_MIPS1)
@@ -6065,8 +6175,7 @@ macro (ip)
              p = frag_var (rs_machine_dependent, 12 + off, 0,
                            RELAX_ENCODE (8 + off, 12 + off, 0, 4 + off, 1,
                                          used_at && mips_opts.noat),
-                           offset_expr.X_add_symbol, (offsetT) 0,
-                           (char *) NULL);
+                           offset_expr.X_add_symbol, 0, NULL);
 
              /* We just generated two relocs.  When tc_gen_reloc
                 handles this case, it will skip the first reloc and
@@ -9218,26 +9327,24 @@ static struct percent_op_match
    const enum small_ex_type type;
 } percent_op[] =
 {
-#ifdef OBJ_ELF
-  {"%half", S_EX_HALF},
-#endif
-  {"%hi", S_EX_HI},
   {"%lo", S_EX_LO},
 #ifdef OBJ_ELF
-  {"%gp_rel", S_EX_GP_REL},
-  {"%got", S_EX_GOT},
+  {"%call_hi", S_EX_CALL_HI},
+  {"%call_lo", S_EX_CALL_LO},
   {"%call16", S_EX_CALL16},
   {"%got_disp", S_EX_GOT_DISP},
   {"%got_page", S_EX_GOT_PAGE},
   {"%got_ofst", S_EX_GOT_OFST},
   {"%got_hi", S_EX_GOT_HI},
   {"%got_lo", S_EX_GOT_LO},
-  {"%neg", S_EX_NEG},
-  {"%higher", S_EX_HIGHER},
+  {"%got", S_EX_GOT},
+  {"%gp_rel", S_EX_GP_REL},
+  {"%half", S_EX_HALF},
   {"%highest", S_EX_HIGHEST},
-  {"%call_hi", S_EX_CALL_HI},
-  {"%call_lo", S_EX_CALL_LO}
+  {"%higher", S_EX_HIGHER},
+  {"%neg", S_EX_NEG},
 #endif
+  {"%hi", S_EX_HI}
 };
 
 /* Parse small expression input.  STR gets adjusted to eat up whitespace.
@@ -9251,10 +9358,9 @@ my_getSmallParser (str, len, nestlevel)
      unsigned int *len;
      int *nestlevel;
 {
-  int type = S_EX_NONE;
-
   *len = 0;
   *str += strspn (*str, " \t");
+  /* Check for expression in parentheses.  */
   if (**str == '(')
     {
       char *b = *str + 1 + strspn (*str + 1, " \t");
@@ -9281,50 +9387,68 @@ my_getSmallParser (str, len, nestlevel)
                 }
            }
        }
+      /* Check for percent_op (in parentheses).  */
       else if (b[0] == '%')
        {
          *str = b;
-         goto percent_op;
+         return my_getPercentOp (str, len, nestlevel);
        }
 
-      /* Some other expression in the braces.  */
-      *len = strcspn (*str, ")") + 1;
+      /* Some other expression in the parentheses, which can contain
+        parentheses itself. Attempt to find the matching one.  */
+      {
+       int pcnt = 1;
+       char *s;
+
+       *len = 1;
+       for (s = *str + 1; *s && pcnt; s++, (*len)++)
+         {
+           if (*s == '(')
+             pcnt++;
+           else if (*s == ')')
+             pcnt--;
+         }
+      }
     }
-  /* Check for percent_op.  */
+  /* Check for percent_op (outside of parentheses).  */
   else if (*str[0] == '%')
-    {
-      char *tmp;
-      unsigned int i;
+    return my_getPercentOp (str, len, nestlevel);
 
-percent_op:
-      tmp = *str + 1;
-      i = 0;
+  /* Any other expression.  */
+  return S_EX_NONE;
+}
 
-      while (ISALPHA (*tmp) || *tmp == '_')
-       {
-         *tmp = TOLOWER (*tmp);
-         tmp++;
-       }
-      while (i < (sizeof (percent_op) / sizeof (struct percent_op_match)))
+static int
+my_getPercentOp (str, len, nestlevel)
+     char **str;
+     unsigned int *len;
+     int *nestlevel;
+{
+  char *tmp = *str + 1;
+  unsigned int i = 0;
+
+  while (ISALPHA (*tmp) || *tmp == '_')
+    {
+      *tmp = TOLOWER (*tmp);
+      tmp++;
+    }
+  while (i < (sizeof (percent_op) / sizeof (struct percent_op_match)))
+    {
+      if (strncmp (*str, percent_op[i].str, strlen (percent_op[i].str)))
+         i++;
+      else
        {
-         if (strncmp (*str, percent_op[i].str, strlen (percent_op[i].str)))
-             i++;
-         else
-           {
-             type = percent_op[i].type;
+         int type = percent_op[i].type;
 
-             /* Only %hi and %lo are allowed for OldABI.  */
-             if (! HAVE_NEWABI && type != S_EX_HI && type != S_EX_LO)
-               return S_EX_NONE;
+         /* Only %hi and %lo are allowed for OldABI.  */
+         if (! HAVE_NEWABI && type != S_EX_HI && type != S_EX_LO)
+           return S_EX_NONE;
 
-             *len = strlen (percent_op[i].str);
-             (*nestlevel)++;
-             return type;
-           }
+         *len = strlen (percent_op[i].str);
+         (*nestlevel)++;
+         return type;
        }
     }
-
-  /* Any other expression.  */
   return S_EX_NONE;
 }
 
@@ -9336,46 +9460,59 @@ my_getSmallExpression (ep, str)
   static char *oldstr = NULL;
   int c = S_EX_NONE;
   int oldc;
-  int nest_level = 0;
+  int nestlevel = -1;
   unsigned int len;
 
-  /* Don't update oldstr if the last call had nested percent_op's.  */
+  /* Don't update oldstr if the last call had nested percent_op's. We need
+     it to parse the outer ones later.  */
   if (! oldstr)
     oldstr = str;
 
   do
     {
       oldc = c;
-      c = my_getSmallParser (&str, &len, &nest_level);
+      c = my_getSmallParser (&str, &len, &nestlevel);
       if (c != S_EX_NONE && c != S_EX_REGISTER)
        str += len;
     }
   while (c != S_EX_NONE && c != S_EX_REGISTER);
 
-  /* A percent_op was encountered.  */
-  if (nest_level)
+  if (nestlevel >= 0)
     {
-      /* Don't try to get an expression if it is already blanked out.  */
+      /* A percent_op was encountered.  Don't try to get an expression if
+        it is already blanked out.  */
       if (*(str + strspn (str + 1, " )")) != ')')
        {
          char save;
 
+         /* Let my_getExpression() stop at the closing parenthesis.  */
          save = *(str + len);
          *(str + len) = '\0';
          my_getExpression (ep, str);
          *(str + len) = save;
        }
-      if (nest_level > 1)
+      if (nestlevel > 0)
        {
-         /* blank out including the % sign.  */
-         char *p = strrchr (oldstr, '%');
-         memset (p, ' ', str - p + len);
+         /* Blank out including the % sign and the proper matching
+            parenthesis.  */
+         int pcnt = 1;
+         char *s = strrchr (oldstr, '%');
+         char *end;
+
+         for (end = strchr (s, '(') + 1; *end && pcnt; end++)
+           {
+             if (*end == '(')
+               pcnt++;
+             else if (*end == ')')
+               pcnt--;
+           }
+
+         memset (s, ' ', end - s);
          str = oldstr;
        }
       else
-       {
-         expr_end = strchr (str, ')') + 1;
-       }
+       expr_end = str + len;
+
       c = oldc;
     }
   else if (c == S_EX_NONE)
@@ -9395,7 +9532,8 @@ my_getSmallExpression (ep, str)
       as_fatal(_("internal error"));
     }
 
-  if (nest_level <= 1)
+  if (nestlevel <= 0)
+    /* All percent_op's have been handled.  */
     oldstr = NULL;
 
   return c;
@@ -11280,14 +11418,14 @@ s_cpload (ignore)
 
    If offset is given, this results in:
      sd                $gp, offset($sp)
-     lui       $gp, %gp_rel(%neg(%hi(label)))
-     daddiu    $gp, $gp, %gp_rel(%neg(%lo(label)))
+     lui       $gp, %hi(%neg(%gp_rel(label)))
+     daddiu    $gp, $gp, %lo(%neg(%gp_rel(label)))
      addu      $gp, $gp, $reg1
 
    If $reg2 is given, this results in:
      daddu     $reg2, $gp, $0
-     lui       $gp, %gp_rel(%neg(%hi(label)))
-     daddiu    $gp, $gp, %gp_rel(%neg(%lo(label)))
+     lui       $gp, %hi(%neg(%gp_rel(label)))
+     daddiu    $gp, $gp, %lo(%neg(%gp_rel(label)))
      addu      $gp, $gp, $reg1
  */
 static void
@@ -11408,6 +11546,7 @@ s_cprestore (ignore)
     }
 
   mips_cprestore_offset = get_absolute_expression ();
+  mips_cprestore_valid = 1;
 
   ex.X_op = O_constant;
   ex.X_add_symbol = NULL;
@@ -11606,7 +11745,7 @@ s_mips_weakext (ignore)
     {
       if (S_IS_DEFINED (symbolP))
        {
-         as_bad ("Ignoring attempt to redefine symbol `%s'.",
+         as_bad ("ignoring attempt to redefine symbol %s",
                  S_GET_NAME (symbolP));
          ignore_rest_of_line ();
          return;
@@ -11674,7 +11813,11 @@ tc_get_register (frame)
       input_line_pointer += 2;
     }
   if (frame)
-    mips_frame_reg = reg != 0 ? reg : SP;
+    {
+      mips_frame_reg = reg != 0 ? reg : SP;
+      mips_frame_reg_valid = 1;
+      mips_cprestore_valid = 0;
+    }
   return reg;
 }
 
@@ -12138,32 +12281,43 @@ tc_gen_reloc (section, fixp)
        as_fatal (_("Double check fx_r_type in tc-mips.c:tc_gen_reloc"));
       fixp->fx_r_type = BFD_RELOC_GPREL32;
     }
-  else if (fixp->fx_pcrel == 0 || OUTPUT_FLAVOR == bfd_target_elf_flavour)
-    reloc->addend = fixp->fx_addnumber;
   else if (fixp->fx_r_type == BFD_RELOC_PCREL_LO16)
     {
-      /* We use a special addend for an internal RELLO reloc.  */
-      if (symbol_section_p (fixp->fx_addsy))
-       reloc->addend = reloc->address - S_GET_VALUE (fixp->fx_subsy);
+      if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+       reloc->addend = fixp->fx_addnumber;
       else
-       reloc->addend = fixp->fx_addnumber + reloc->address;
+       {
+         /* We use a special addend for an internal RELLO reloc.  */
+         if (symbol_section_p (fixp->fx_addsy))
+           reloc->addend = reloc->address - S_GET_VALUE (fixp->fx_subsy);
+         else
+           reloc->addend = fixp->fx_addnumber + reloc->address;
+       }
     }
   else if (fixp->fx_r_type == BFD_RELOC_PCREL_HI16_S)
     {
       assert (fixp->fx_next != NULL
              && fixp->fx_next->fx_r_type == BFD_RELOC_PCREL_LO16);
-      /* We use a special addend for an internal RELHI reloc.  The
-        reloc is relative to the RELLO; adjust the addend
+
+      /* The reloc is relative to the RELLO; adjust the addend
         accordingly.  */
-      if (symbol_section_p (fixp->fx_addsy))
-       reloc->addend = (fixp->fx_next->fx_frag->fr_address
-                        + fixp->fx_next->fx_where
-                        - S_GET_VALUE (fixp->fx_subsy));
+      if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+       reloc->addend = fixp->fx_next->fx_addnumber;
       else
-       reloc->addend = (fixp->fx_addnumber
-                        + fixp->fx_next->fx_frag->fr_address
-                        + fixp->fx_next->fx_where);
+       {
+         /* We use a special addend for an internal RELHI reloc.  */
+         if (symbol_section_p (fixp->fx_addsy))
+           reloc->addend = (fixp->fx_next->fx_frag->fr_address
+                            + fixp->fx_next->fx_where
+                            - S_GET_VALUE (fixp->fx_subsy));
+         else
+           reloc->addend = (fixp->fx_addnumber
+                            + fixp->fx_next->fx_frag->fr_address
+                            + fixp->fx_next->fx_where);
+       }
     }
+  else if (fixp->fx_pcrel == 0 || OUTPUT_FLAVOR == bfd_target_elf_flavour)
+    reloc->addend = fixp->fx_addnumber;
   else
     {
       if (OUTPUT_FLAVOR != bfd_target_aout_flavour)
@@ -12681,7 +12835,7 @@ get_number ()
       negative = 1;
     }
   if (!ISDIGIT (*input_line_pointer))
-    as_bad (_("Expected simple number."));
+    as_bad (_("expected simple number"));
   if (input_line_pointer[0] == '0')
     {
       if (input_line_pointer[1] == 'x')
@@ -12709,7 +12863,7 @@ get_number ()
     {
       printf (_(" *input_line_pointer == '%c' 0x%02x\n"),
              *input_line_pointer, *input_line_pointer);
-      as_warn (_("Invalid number"));
+      as_warn (_("invalid number"));
       return -1;
     }
   while (ISDIGIT (*input_line_pointer))
@@ -12742,6 +12896,10 @@ s_mips_end (x)
   symbolS *p;
   int maybe_text;
 
+  /* Following functions need their own .frame and .cprestore directives.  */
+  mips_frame_reg_valid = 0;
+  mips_cprestore_valid = 0;
+
   if (!is_end_of_line[(unsigned char) *input_line_pointer])
     {
       p = get_symbol ();
@@ -12859,6 +13017,10 @@ s_mips_ent (aent)
 
   if (!aent)
     {
+      /* This function needs its own .frame and .cprestore directives.  */
+      mips_frame_reg_valid = 0;
+      mips_cprestore_valid = 0;
+
       cur_proc_ptr = &cur_proc;
       memset (cur_proc_ptr, '\0', sizeof (procS));