* config/tc-mips.c (macro_build_ldst_constoffset,load_register,macro):
[deliverable/binutils-gdb.git] / gas / config / tc-mips.c
index e2e3f4a3294ecfedfd3e214047fd949ef7780977..cd1b424a5d778f3d2cbf3d6f1cd3a676d365eb62 100644 (file)
@@ -266,18 +266,20 @@ static int mips_32bitmode = 0;
    || (ISA) == ISA_MIPS4             \
    || (ISA) == ISA_MIPS5             \
    || (ISA) == ISA_MIPS64            \
+   || (ISA) == ISA_MIPS64R2          \
    )
 
 /* Return true if ISA supports 64-bit right rotate (dror et al.)
    instructions.  */
 #define ISA_HAS_DROR(ISA) (    \
-   0                           \
+   (ISA) == ISA_MIPS64R2       \
    )
 
 /* Return true if ISA supports 32-bit right rotate (ror et al.)
    instructions.  */
 #define ISA_HAS_ROR(ISA) (     \
    (ISA) == ISA_MIPS32R2       \
+   || (ISA) == ISA_MIPS64R2    \
    )
 
 #define HAVE_32BIT_GPRS                                   \
@@ -302,8 +304,6 @@ static int mips_32bitmode = 0;
         && mips_pic != EMBEDDED_PIC))
 
 #define HAVE_64BIT_ADDRESSES (! HAVE_32BIT_ADDRESSES)
-#define HAVE_64BIT_ADDRESS_CONSTANTS (HAVE_64BIT_ADDRESSES \
-                                     || HAVE_64BIT_GPRS)
 
 /* Addresses are loaded in different ways, depending on the address size
    in use.  The n32 ABI Documentation also mandates the use of additions
@@ -1027,6 +1027,7 @@ static char *expr_end;
    mips_ip.  */
 
 static expressionS imm_expr;
+static expressionS imm2_expr;
 static expressionS offset_expr;
 
 /* Relocs associated with imm_expr and offset_expr.  */
@@ -1325,6 +1326,7 @@ md_assemble (char *str)
     = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
 
   imm_expr.X_op = O_absent;
+  imm2_expr.X_op = O_absent;
   offset_expr.X_op = O_absent;
   imm_reloc[0] = BFD_RELOC_UNUSED;
   imm_reloc[1] = BFD_RELOC_UNUSED;
@@ -2874,6 +2876,41 @@ macro_build (char *place, int *counter, expressionS *ep, const char *name,
        case ')':
          continue;
 
+       case '+':
+         switch (*fmt++)
+           {
+           case 'A':
+           case 'E':
+             insn.insn_opcode |= (va_arg (args, int)
+                                  & OP_MASK_SHAMT) << OP_SH_SHAMT;
+             continue;
+
+           case 'B':
+           case 'F':
+             /* Note that in the macro case, these arguments are already
+                in MSB form.  (When handling the instruction in the
+                non-macro case, these arguments are sizes from which
+                MSB values must be calculated.)  */
+             insn.insn_opcode |= (va_arg (args, int)
+                                  & OP_MASK_INSMSB) << OP_SH_INSMSB;
+             continue;
+
+           case 'C':
+           case 'G':
+           case 'H':
+             /* Note that in the macro case, these arguments are already
+                in MSBD form.  (When handling the instruction in the
+                non-macro case, these arguments are sizes from which
+                MSBD values must be calculated.)  */
+             insn.insn_opcode |= (va_arg (args, int)
+                                  & OP_MASK_EXTMSBD) << OP_SH_EXTMSBD;
+             continue;
+
+           default:
+             internalError ();
+           }
+         continue;
+
        case 't':
        case 'w':
        case 'E':
@@ -3225,12 +3262,23 @@ macro_build_lui (char *place, int *counter, expressionS *ep, int regnum)
    using AT if necessary.  */
 static void
 macro_build_ldst_constoffset (char *place, int *counter, expressionS *ep,
-                             const char *op, int treg, int breg)
+                             const char *op, int treg, int breg, int dbl)
 {
   assert (ep->X_op == O_constant);
 
+  /* Sign-extending 32-bit constants makes their handling easier.  */
+  if (! dbl && ! ((ep->X_add_number & ~((bfd_vma) 0x7fffffff))
+                 == ~((bfd_vma) 0x7fffffff)))
+    {
+      if (ep->X_add_number & ~((bfd_vma) 0xffffffff))
+       as_bad (_("too large constant specified"));
+
+    ep->X_add_number = (((ep->X_add_number & 0xffffffff) ^ 0x80000000)
+                       - 0x80000000);
+    }
+
   /* Right now, this routine can only handle signed 32-bit contants.  */
-  if (! IS_SEXT_32BIT_NUM(ep->X_add_number))
+  if (! IS_SEXT_32BIT_NUM(ep->X_add_number + 0x8000))
     as_warn (_("operand overflow"));
 
   if (IS_SEXT_16BIT_NUM(ep->X_add_number))
@@ -3381,12 +3429,19 @@ load_register (int *counter, int reg, expressionS *ep, int dbl)
   if (ep->X_op != O_big)
     {
       assert (ep->X_op == O_constant);
-      if (ep->X_add_number < 0x8000
-         && (ep->X_add_number >= 0
-             || (ep->X_add_number >= -0x8000
-                 && (! dbl
-                     || ! ep->X_unsigned
-                     || sizeof (ep->X_add_number) > 4))))
+
+      /* Sign-extending 32-bit constants makes their handling easier.  */
+      if (! dbl && ! ((ep->X_add_number & ~((bfd_vma) 0x7fffffff))
+                     == ~((bfd_vma) 0x7fffffff)))
+       {
+         if (ep->X_add_number & ~((bfd_vma) 0xffffffff))
+           as_bad (_("too large constant specified"));
+
+       ep->X_add_number = (((ep->X_add_number & 0xffffffff) ^ 0x80000000)
+                           - 0x80000000);
+       }
+
+      if (IS_SEXT_16BIT_NUM (ep->X_add_number))
        {
          /* We can handle 16 bit signed values with an addiu to
             $zero.  No need to ever use daddiu here, since $zero and
@@ -3403,17 +3458,7 @@ load_register (int *counter, int reg, expressionS *ep, int dbl)
                       BFD_RELOC_LO16);
          return;
        }
-      else if ((IS_SEXT_32BIT_NUM (ep->X_add_number)
-               && (! dbl
-                   || ! ep->X_unsigned
-                   || sizeof (ep->X_add_number) > 4
-                   || (ep->X_add_number & 0x80000000) == 0))
-              || ((HAVE_32BIT_GPRS || ! dbl)
-                  && (ep->X_add_number &~ (offsetT) 0xffffffff) == 0)
-              || (HAVE_32BIT_GPRS
-                  && ! dbl
-                  && ((ep->X_add_number &~ (offsetT) 0xffffffff)
-                      == ~ (offsetT) 0xffffffff)))
+      else if ((IS_SEXT_32BIT_NUM (ep->X_add_number)))
        {
          /* 32 bit values require an lui.  */
          macro_build (NULL, counter, ep, "lui", "t,u", reg, BFD_RELOC_HI16);
@@ -4431,6 +4476,104 @@ macro (struct mips_cl_insn *ip)
                   "s,t,p", AT, 0);
       break;
 
+    case M_DEXT:
+      {
+       unsigned long pos;
+       unsigned long size;
+
+        if (imm_expr.X_op != O_constant || imm2_expr.X_op != O_constant)
+         {
+           as_bad (_("Unsupported large constant"));
+           pos = size = 1;
+         }
+       else
+         {
+           pos = (unsigned long) imm_expr.X_add_number;
+           size = (unsigned long) imm2_expr.X_add_number;
+         }
+
+       if (pos > 63)
+         {
+           as_bad (_("Improper position (%lu)"), pos);
+           pos = 1;
+         }
+        if (size == 0 || size > 64
+           || (pos + size - 1) > 63)
+         {
+           as_bad (_("Improper extract size (%lu, position %lu)"),
+                   size, pos);
+           size = 1;
+         }
+
+       if (size <= 32 && pos < 32)
+         {
+           s = "dext";
+           fmt = "t,r,+A,+C";
+         }
+       else if (size <= 32)
+         {
+           s = "dextu";
+           fmt = "t,r,+E,+H";
+         }
+       else
+         {
+           s = "dextm";
+           fmt = "t,r,+A,+G";
+         }
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s,
+                    fmt, treg, sreg, pos, size - 1);
+      }
+      return;
+
+    case M_DINS:
+      {
+       unsigned long pos;
+       unsigned long size;
+
+        if (imm_expr.X_op != O_constant || imm2_expr.X_op != O_constant)
+         {
+           as_bad (_("Unsupported large constant"));
+           pos = size = 1;
+         }
+       else
+         {
+           pos = (unsigned long) imm_expr.X_add_number;
+           size = (unsigned long) imm2_expr.X_add_number;
+         }
+
+       if (pos > 63)
+         {
+           as_bad (_("Improper position (%lu)"), pos);
+           pos = 1;
+         }
+        if (size == 0 || size > 64
+           || (pos + size - 1) > 63)
+         {
+           as_bad (_("Improper insert size (%lu, position %lu)"),
+                   size, pos);
+           size = 1;
+         }
+
+       if (pos < 32 && (pos + size - 1) < 32)
+         {
+           s = "dins";
+           fmt = "t,r,+A,+B";
+         }
+       else if (pos >= 32)
+         {
+           s = "dinsu";
+           fmt = "t,r,+E,+F";
+         }
+       else
+         {
+           s = "dinsm";
+           fmt = "t,r,+A,+F";
+         }
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s,
+                    fmt, treg, sreg, pos, pos + size - 1);
+      }
+      return;
+
     case M_DDIV_3:
       dbl = 1;
     case M_DIV_3:
@@ -4970,7 +5113,7 @@ macro (struct mips_cl_insn *ip)
                                RELAX_ENCODE (8, 4, 0, 0, 0, 0),
                                offset_expr.X_add_symbol, 0, NULL);
                }
-             else if (IS_SEXT_32BIT_NUM (expr1.X_add_number))
+             else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
                {
                  int dreg;
 
@@ -5318,7 +5461,7 @@ macro (struct mips_cl_insn *ip)
                                          mips_opts.warn_about_macros),
                            offset_expr.X_add_symbol, 0, NULL);
            }
-         else if (IS_SEXT_32BIT_NUM (expr1.X_add_number))
+         else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
            {
              int dreg;
 
@@ -5450,7 +5593,8 @@ macro (struct mips_cl_insn *ip)
                  macro_build_ldst_constoffset (NULL, &icnt, &expr1,
                                                ADDRESS_LOAD_INSN,
                                                mips_gp_register,
-                                               mips_frame_reg);
+                                               mips_frame_reg,
+                                               HAVE_64BIT_ADDRESSES);
                }
            }
        }
@@ -5601,7 +5745,8 @@ macro (struct mips_cl_insn *ip)
                  macro_build_ldst_constoffset (NULL, &icnt, &expr1,
                                                ADDRESS_LOAD_INSN,
                                                mips_gp_register,
-                                               mips_frame_reg);
+                                               mips_frame_reg,
+                                               HAVE_64BIT_ADDRESSES);
                }
            }
        }
@@ -5791,6 +5936,22 @@ macro (struct mips_cl_insn *ip)
       else
        fmt = "t,o(b)";
 
+      /* Sign-extending 32-bit constants makes their handling easier.
+         The HAVE_64BIT_GPRS... part is due to the linux kernel hack
+         described below.  */
+      if ((! HAVE_64BIT_ADDRESSES
+          && (! HAVE_64BIT_GPRS && offset_expr.X_op == O_constant))
+          && (offset_expr.X_op == O_constant)
+         && ! ((offset_expr.X_add_number & ~((bfd_vma) 0x7fffffff))
+               == ~((bfd_vma) 0x7fffffff)))
+       {
+         if (offset_expr.X_add_number & ~((bfd_vma) 0xffffffff))
+           as_bad (_("too large constant specified"));
+
+       offset_expr.X_add_number = (((offset_expr.X_add_number & 0xffffffff)
+                                    ^ 0x80000000) - 0x80000000);
+       }
+
       /* For embedded PIC, we allow loads where the offset is calculated
          by subtracting a symbol in the current segment from an unknown
          symbol, relative to a base register, e.g.:
@@ -5923,10 +6084,12 @@ macro (struct mips_cl_insn *ip)
             end up converting the binary to ELF32 for a number of
             platforms whose boot loaders don't support ELF64
             binaries.  */
-         if ((offset_expr.X_op != O_constant && HAVE_64BIT_ADDRESSES)
-             || (offset_expr.X_op == O_constant
-                 && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number + 0x8000)
-                 && HAVE_64BIT_ADDRESS_CONSTANTS))
+         if ((HAVE_64BIT_ADDRESSES
+              && ! (offset_expr.X_op == O_constant
+                    && IS_SEXT_32BIT_NUM (offset_expr.X_add_number + 0x8000)))
+             || (HAVE_64BIT_GPRS
+                 && offset_expr.X_op == O_constant
+                 && ! IS_SEXT_32BIT_NUM (offset_expr.X_add_number + 0x8000)))
            {
              p = NULL;
 
@@ -5973,9 +6136,9 @@ macro (struct mips_cl_insn *ip)
 
              return;
            }
-         else if (offset_expr.X_op == O_constant
-                  && !HAVE_64BIT_ADDRESS_CONSTANTS
-                  && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+
+         if (offset_expr.X_op == O_constant
+             && ! IS_SEXT_32BIT_NUM (offset_expr.X_add_number + 0x8000))
            as_bad (_("load/store address overflow (max 32 bits)"));
 
          if (breg == 0)
@@ -8035,6 +8198,11 @@ validate_mips_insn (const struct mips_opcode *opc)
          case 'C': USE_BITS (OP_MASK_EXTMSBD,  OP_SH_EXTMSBD); break;
          case 'D': USE_BITS (OP_MASK_RD,       OP_SH_RD);
                    USE_BITS (OP_MASK_SEL,      OP_SH_SEL);     break;
+         case 'E': USE_BITS (OP_MASK_SHAMT,    OP_SH_SHAMT);   break;
+         case 'F': USE_BITS (OP_MASK_INSMSB,   OP_SH_INSMSB);  break;
+         case 'G': USE_BITS (OP_MASK_EXTMSBD,  OP_SH_EXTMSBD); break;
+         case 'H': USE_BITS (OP_MASK_EXTMSBD,  OP_SH_EXTMSBD); break;
+         case 'I': break;
          default:
            as_bad (_("internal: bad mips opcode (unknown extension operand type `+%c'): %s %s"),
                    c, opc->name, opc->args);
@@ -8294,6 +8462,12 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                case 'A':               /* ins/ext position, becomes LSB.  */
                  limlo = 0;
                  limhi = 31;
+                 goto do_lsb;
+               case 'E':
+                 limlo = 32;
+                 limhi = 63;
+                 goto do_lsb;
+do_lsb:
                  my_getExpression (&imm_expr, s);
                  check_absolute_expr (ip, &imm_expr);
                  if ((unsigned long) imm_expr.X_add_number < limlo
@@ -8313,6 +8487,12 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                case 'B':               /* ins size, becomes MSB.  */
                  limlo = 1;
                  limhi = 32;
+                 goto do_msb;
+               case 'F':
+                 limlo = 33;
+                 limhi = 64;
+                 goto do_msb;
+do_msb:
                  my_getExpression (&imm_expr, s);
                  check_absolute_expr (ip, &imm_expr);
                  /* Check for negative input so that small negative numbers
@@ -8339,6 +8519,16 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                case 'C':               /* ext size, becomes MSBD.  */
                  limlo = 1;
                  limhi = 32;
+                 goto do_msbd;
+               case 'G':
+                 limlo = 33;
+                 limhi = 64;
+                 goto do_msbd;
+               case 'H':
+                 limlo = 33;
+                 limhi = 64;
+                 goto do_msbd;
+do_msbd:
                  my_getExpression (&imm_expr, s);
                  check_absolute_expr (ip, &imm_expr);
                  /* Check for negative input so that small negative numbers
@@ -8366,6 +8556,15 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                  /* +D is for disassembly only; never match.  */
                  break;
 
+               case 'I':
+                 /* "+I" is like "I", except that imm2_expr is used.  */
+                 my_getExpression (&imm2_expr, s);
+                 if (imm2_expr.X_op != O_big
+                     && imm2_expr.X_op != O_constant)
+                 insn_error = _("absolute expression required");
+                 s = expr_end;
+                 continue;
+
                default:
                  as_bad (_("internal: bad mips opcode (unknown extension operand type `+%c'): %s %s"),
                    *args, insn->name, insn->args);
@@ -9171,7 +9370,18 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                }
              while (ISDIGIT (*s));
              if (regno > 7)
-               as_bad (_("invalid condition code register $fcc%d"), regno);
+               as_bad (_("Invalid condition code register $fcc%d"), regno);
+             if ((strcmp(str + strlen(str) - 3, ".ps") == 0
+                  || strcmp(str + strlen(str) - 5, "any2f") == 0
+                  || strcmp(str + strlen(str) - 5, "any2t") == 0)
+                 && (regno & 1) != 0)
+               as_warn(_("Condition code register should be even for %s, was %d"),
+                       str, regno);
+             if ((strcmp(str + strlen(str) - 5, "any4f") == 0
+                  || strcmp(str + strlen(str) - 5, "any4t") == 0)
+                 && (regno & 3) != 0)
+               as_warn(_("Condition code register should be 0 or 4 for %s, was %d"),
+                       str, regno);
              if (*args == 'N')
                ip->insn_opcode |= regno << OP_SH_BCC;
              else
@@ -9333,6 +9543,7 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
       imm_reloc[0] = BFD_RELOC_UNUSED;
       imm_reloc[1] = BFD_RELOC_UNUSED;
       imm_reloc[2] = BFD_RELOC_UNUSED;
+      imm2_expr.X_op = O_absent;
       offset_expr.X_op = O_absent;
       offset_reloc[0] = BFD_RELOC_UNUSED;
       offset_reloc[1] = BFD_RELOC_UNUSED;
@@ -10231,9 +10442,11 @@ struct option md_longopts[] =
   {"mips64", no_argument, NULL, OPTION_MIPS64},
 #define OPTION_MIPS32R2 (OPTION_ARCH_BASE + 9)
   {"mips32r2", no_argument, NULL, OPTION_MIPS32R2},
+#define OPTION_MIPS64R2 (OPTION_ARCH_BASE + 10)
+  {"mips64r2", no_argument, NULL, OPTION_MIPS64R2},
 
   /* Options which specify Application Specific Extensions (ASEs).  */
-#define OPTION_ASE_BASE (OPTION_ARCH_BASE + 10)
+#define OPTION_ASE_BASE (OPTION_ARCH_BASE + 11)
 #define OPTION_MIPS16 (OPTION_ASE_BASE + 0)
   {"mips16", no_argument, NULL, OPTION_MIPS16},
 #define OPTION_NO_MIPS16 (OPTION_ASE_BASE + 1)
@@ -10438,6 +10651,10 @@ md_parse_option (int c, char *arg)
       file_mips_isa = ISA_MIPS32R2;
       break;
 
+    case OPTION_MIPS64R2:
+      file_mips_isa = ISA_MIPS64R2;
+      break;
+
     case OPTION_MIPS64:
       file_mips_isa = ISA_MIPS64;
       break;
@@ -11995,6 +12212,8 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
        mips_opts.isa = ISA_MIPS32R2;
       else if (strcmp (name, "mips64") == 0)
        mips_opts.isa = ISA_MIPS64;
+      else if (strcmp (name, "mips64r2") == 0)
+       mips_opts.isa = ISA_MIPS64R2;
       else if (strcmp (name, "arch=default") == 0)
        {
          reset = 1;
@@ -12032,6 +12251,7 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
        case ISA_MIPS4:
        case ISA_MIPS5:
        case ISA_MIPS64:
+       case ISA_MIPS64R2:
          mips_opts.gp32 = 0;
          mips_opts.fp32 = 0;
          break;
@@ -12302,17 +12522,17 @@ s_cprestore (int ignore ATTRIBUTE_UNUSED)
   ex.X_add_number = mips_cprestore_offset;
 
   macro_build_ldst_constoffset (NULL, &icnt, &ex, ADDRESS_STORE_INSN,
-                               mips_gp_register, SP);
+                               mips_gp_register, SP, HAVE_64BIT_ADDRESSES);
 
   demand_empty_rest_of_line ();
 }
 
 /* Handle the .cpreturn pseudo-op defined for NewABI PIC code. If an offset
-   was given in the preceeding .gpsetup, it results in:
+   was given in the preceeding .cpsetup, it results in:
      ld                $gp, offset($sp)
 
    If a register $reg2 was given there, it results in:
-     daddiu    $gp, $gp, $reg2
+     daddu     $gp, $reg2, $0
  */
 static void
 s_cpreturn (int ignore ATTRIBUTE_UNUSED)
@@ -14277,6 +14497,7 @@ static const struct mips_cpu_info mips_cpu_info_table[] =
   { "mips32",         1,      ISA_MIPS32,     CPU_MIPS32 },
   { "mips32r2",       1,      ISA_MIPS32R2,   CPU_MIPS32R2 },
   { "mips64",         1,      ISA_MIPS64,     CPU_MIPS64 },
+  { "mips64r2",       1,      ISA_MIPS64R2,   CPU_MIPS64R2 },
 
   /* MIPS I */
   { "r3000",          0,      ISA_MIPS1,      CPU_R3000 },
@@ -14508,6 +14729,7 @@ MIPS options:\n\
 -mips32                 generate MIPS32 ISA instructions\n\
 -mips32r2               generate MIPS32 release 2 ISA instructions\n\
 -mips64                 generate MIPS64 ISA instructions\n\
+-mips64r2               generate MIPS64 release 2 ISA instructions\n\
 -march=CPU/-mtune=CPU  generate code/schedule for CPU, where CPU is one of:\n"));
 
   first = 1;
This page took 0.037779 seconds and 4 git commands to generate.