gas/
authorRichard Sandiford <rdsandiford@googlemail.com>
Sun, 14 Jul 2013 13:44:25 +0000 (13:44 +0000)
committerRichard Sandiford <rdsandiford@googlemail.com>
Sun, 14 Jul 2013 13:44:25 +0000 (13:44 +0000)
* config/tc-mips.c (mips_oddfpreg_ok): Move further up file.
Change return type to bfd_boolean.
(report_bad_range, report_bad_field): New functions.
(mips_arg_info): New structure.
(match_const_int, convert_reg_type, check_regno, match_int_operand)
(match_mapped_int_operand, match_msb_operand, match_reg_operand)
(match_reg_pair_operand, match_pcrel_operand, match_perf_reg_operand)
(match_addiusp_operand, match_clo_clz_dest_operand)
(match_lwm_swm_list_operand, match_mdmx_imm_reg_operand)
(match_pc_operand, match_tied_reg_operand, match_operand)
(check_completed_insn): New functions, commented out for now.

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

index 353474f0da0abea3e8e73bf616cfbfd7c4e129b0..77dc6299388d3c34e41d28489c396aebdcdb7479 100644 (file)
@@ -1,3 +1,17 @@
+2013-07-14  Richard Sandiford  <rdsandiford@googlemail.com>
+
+       * config/tc-mips.c (mips_oddfpreg_ok): Move further up file.
+       Change return type to bfd_boolean.
+       (report_bad_range, report_bad_field): New functions.
+       (mips_arg_info): New structure.
+       (match_const_int, convert_reg_type, check_regno, match_int_operand)
+       (match_mapped_int_operand, match_msb_operand, match_reg_operand)
+       (match_reg_pair_operand, match_pcrel_operand, match_perf_reg_operand)
+       (match_addiusp_operand, match_clo_clz_dest_operand)
+       (match_lwm_swm_list_operand, match_mdmx_imm_reg_operand)
+       (match_pc_operand, match_tied_reg_operand, match_operand)
+       (check_completed_insn): New functions, commented out for now.
+
 2013-07-14  Richard Sandiford  <rdsandiford@googlemail.com>
 
        * config/tc-mips.c (insn_insert_operand): New function.
index 2a33f421379c3178ee76f834a00580e868eac9c2..e0e2d1d0595baac503c08316558a9880b53807b5 100644 (file)
@@ -3702,6 +3702,847 @@ fpr_write_mask (const struct mips_cl_insn *ip)
   return mask;
 }
 
+/* Operand OPNUM of INSN is an odd-numbered floating-point register.
+   Check whether that is allowed.  */
+
+static bfd_boolean
+mips_oddfpreg_ok (const struct mips_opcode *insn, int opnum)
+{
+  const char *s = insn->name;
+
+  if (insn->pinfo == INSN_MACRO)
+    /* Let a macro pass, we'll catch it later when it is expanded.  */
+    return TRUE;
+
+  if (ISA_HAS_ODD_SINGLE_FPR (mips_opts.isa) || mips_opts.arch == CPU_R5900)
+    {
+      /* Allow odd registers for single-precision ops.  */
+      switch (insn->pinfo & (FP_S | FP_D))
+       {
+       case FP_S:
+       case 0:
+         return TRUE;
+       case FP_D:
+         return FALSE;
+       default:
+         break;
+       }
+
+      /* Cvt.w.x and cvt.x.w allow an odd register for a 'w' or 's' operand.  */
+      s = strchr (insn->name, '.');
+      if (s != NULL && opnum == 2)
+       s = strchr (s + 1, '.');
+      return (s != NULL && (s[1] == 'w' || s[1] == 's'));
+    }
+
+  /* Single-precision coprocessor loads and moves are OK too.  */
+  if ((insn->pinfo & FP_S)
+      && (insn->pinfo & (INSN_COPROC_MEMORY_DELAY | INSN_STORE_MEMORY
+                        | INSN_LOAD_COPROC_DELAY | INSN_COPROC_MOVE_DELAY)))
+    return TRUE;
+
+  return FALSE;
+}
+
+#if 0
+/* Report that user-supplied argument ARGNUM for INSN was VAL, but should
+   have been in the range [MIN_VAL, MAX_VAL].  PRINT_HEX says whether
+   this operand is normally printed in hex or decimal.  */
+
+static void
+report_bad_range (struct mips_cl_insn *insn, int argnum,
+                 offsetT val, int min_val, int max_val,
+                 bfd_boolean print_hex)
+{
+  if (print_hex && val >= 0)
+    as_bad (_("Operand %d of `%s' must be in the range [0x%x, 0x%x],"
+             " was 0x%lx."),
+           argnum, insn->insn_mo->name, min_val, max_val, (unsigned long) val);
+  else if (print_hex)
+    as_bad (_("Operand %d of `%s' must be in the range [0x%x, 0x%x],"
+             " was %ld."),
+           argnum, insn->insn_mo->name, min_val, max_val, (unsigned long) val);
+  else
+    as_bad (_("Operand %d of `%s' must be in the range [%d, %d],"
+             " was %ld."),
+           argnum, insn->insn_mo->name, min_val, max_val, (unsigned long) val);
+}
+
+/* Report an invalid combination of position and size operands for a bitfield
+   operation.  POS and SIZE are the values that were given.  */
+
+static void
+report_bad_field (offsetT pos, offsetT size)
+{
+  as_bad (_("Invalid field specification (position %ld, size %ld)"),
+         (unsigned long) pos, (unsigned long) size);
+}
+
+/* Information about an instruction argument that we're trying to match.  */
+struct mips_arg_info
+{
+  /* The instruction so far.  */
+  struct mips_cl_insn *insn;
+
+  /* The 1-based operand number, in terms of insn->insn_mo->args.  */
+  int opnum;
+
+  /* The 1-based argument number, for error reporting.  This does not
+     count elided optional registers, etc..  */
+  int argnum;
+
+  /* The last OP_REG operand seen, or ILLEGAL_REG if none.  */
+  unsigned int last_regno;
+
+  /* If the first operand was an OP_REG, this is the register that it
+     specified, otherwise it is ILLEGAL_REG.  */
+  unsigned int dest_regno;
+
+  /* The value of the last OP_INT operand.  Only used for OP_MSB,
+     where it gives the lsb position.  */
+  unsigned int last_op_int;
+
+  /* If true, match routines should silently reject invalid arguments.
+     If false, match routines can accept invalid arguments as long as
+     they report an appropriate error.  They still have the option of
+     silently rejecting arguments, in which case a generic "Invalid operands"
+     style of error will be used instead.  */
+  bfd_boolean soft_match;
+
+  /* If true, the OP_INT match routine should treat plain symbolic operands
+     as if a relocation operator like %lo(...) had been used.  This is only
+     ever true if the operand can be relocated.  */
+  bfd_boolean allow_nonconst;
+
+  /* When true, the OP_INT match routine should allow unsigned N-bit
+     arguments to be used where a signed N-bit operand is expected.  */
+  bfd_boolean lax_max;
+
+  /* When true, the OP_REG match routine should assume that another operand
+     appears after this one.  It should fail the match if the register it
+     sees is at the end of the argument list.  */
+  bfd_boolean optional_reg;
+
+  /* True if a reference to the current AT register was seen.  */
+  bfd_boolean seen_at;
+};
+
+/* Match a constant integer at S for ARG.  Return null if the match failed.
+   Otherwise return the end of the matched string and store the constant value
+   in *VALUE.  In the latter case, use FALLBACK as the value if the match
+   succeeded with an error.  */
+
+static char *
+match_const_int (struct mips_arg_info *arg, char *s, offsetT *value,
+                offsetT fallback)
+{
+  expressionS ex;
+  bfd_reloc_code_real_type r[3];
+  int num_relocs;
+
+  num_relocs = my_getSmallExpression (&ex, r, s);
+  if (*s == '(' && ex.X_op == O_register)
+    {
+      /* Assume that the constant has been elided and that S is a base
+        register.  The rest of the match will fail if the assumption
+        turns out to be wrong.  */
+      *value = 0;
+      return s;
+    }
+
+  if (num_relocs == 0 && ex.X_op == O_constant)
+    *value = ex.X_add_number;
+  else
+    {
+      /* If we got a register rather than an expression, the default
+        "Invalid operands" style of error seems more appropriate.  */
+      if (arg->soft_match || ex.X_op == O_register)
+       return 0;
+      as_bad (_("Operand %d of `%s' must be constant"),
+             arg->argnum, arg->insn->insn_mo->name);
+      *value = fallback;
+    }
+  return expr_end;
+}
+
+/* Return the RTYPE_* flags for a register operand of type TYPE that
+   appears in instruction OPCODE.  */
+
+static unsigned int
+convert_reg_type (const struct mips_opcode *opcode,
+                 enum mips_reg_operand_type type)
+{
+  switch (type)
+    {
+    case OP_REG_GP:
+      return RTYPE_NUM | RTYPE_GP;
+
+    case OP_REG_FP:
+      /* Allow vector register names for MDMX if the instruction is a 64-bit
+        FPR load, store or move (including moves to and from GPRs).  */
+      if ((mips_opts.ase & ASE_MDMX)
+         && (opcode->pinfo & FP_D)
+         && (opcode->pinfo & (INSN_COPROC_MOVE_DELAY
+                              | INSN_COPROC_MEMORY_DELAY
+                              | INSN_LOAD_COPROC_DELAY
+                              | INSN_LOAD_MEMORY_DELAY
+                              | INSN_STORE_MEMORY)))
+       return RTYPE_FPU | RTYPE_VEC;
+      return RTYPE_FPU;
+
+    case OP_REG_CCC:
+      if (opcode->pinfo & (FP_D | FP_S))
+       return RTYPE_CCC | RTYPE_FCC;
+      return RTYPE_CCC;
+
+    case OP_REG_VEC:
+      if (opcode->membership & INSN_5400)
+       return RTYPE_FPU;
+      return RTYPE_FPU | RTYPE_VEC;
+
+    case OP_REG_ACC:
+      return RTYPE_ACC;
+
+    case OP_REG_COPRO:
+      if (opcode->name[strlen (opcode->name) - 1] == '0')
+       return RTYPE_NUM | RTYPE_CP0;
+      return RTYPE_NUM;
+
+    case OP_REG_HW:
+      return RTYPE_NUM;
+    }
+  abort ();
+}
+
+/* ARG is register REGNO, of type TYPE.  Warn about any dubious registers.  */
+
+static void
+check_regno (struct mips_arg_info *arg,
+            enum mips_reg_operand_type type, unsigned int regno)
+{
+  if (AT && type == OP_REG_GP && regno == AT)
+    arg->seen_at = TRUE;
+
+  if (type == OP_REG_FP
+      && (regno & 1) != 0
+      && HAVE_32BIT_FPRS
+      && !mips_oddfpreg_ok (arg->insn->insn_mo, arg->opnum))
+    as_warn (_("Float register should be even, was %d"), regno);
+
+  if (type == OP_REG_CCC)
+    {
+      const char *name;
+      size_t length;
+
+      name = arg->insn->insn_mo->name;
+      length = strlen (name);
+      if ((regno & 1) != 0
+         && ((length >= 3 && strcmp (name + length - 3, ".ps") == 0)
+             || (length >= 5 && strncmp (name + length - 5, "any2", 4) == 0)))
+       as_warn (_("Condition code register should be even for %s, was %d"),
+                name, regno);
+
+      if ((regno & 3) != 0
+         && (length >= 5 && strncmp (name + length - 5, "any4", 4) == 0))
+       as_warn (_("Condition code register should be 0 or 4 for %s, was %d"),
+                name, regno);
+    }
+}
+
+/* OP_INT matcher.  */
+
+static char *
+match_int_operand (struct mips_arg_info *arg,
+                  const struct mips_operand *operand_base, char *s)
+{
+  const struct mips_int_operand *operand;
+  unsigned int uval, mask;
+  int min_val, max_val, factor;
+  offsetT sval;
+  bfd_boolean print_hex;
+
+  operand = (const struct mips_int_operand *) operand_base;
+  factor = 1 << operand->shift;
+  mask = (1 << operand_base->size) - 1;
+  max_val = (operand->max_val + operand->bias) << operand->shift;
+  min_val = max_val - (mask << operand->shift);
+  if (arg->lax_max)
+    max_val = mask << operand->shift;
+
+  if (operand_base->lsb == 0
+      && operand_base->size == 16
+      && operand->shift == 0
+      && operand->bias == 0
+      && (operand->max_val == 32767 || operand->max_val == 65535))
+    {
+      /* The operand can be relocated.  */
+      offset_reloc[0] = BFD_RELOC_LO16;
+      offset_reloc[1] = BFD_RELOC_UNUSED;
+      offset_reloc[2] = BFD_RELOC_UNUSED;
+      if (my_getSmallExpression (&offset_expr, offset_reloc, s) > 0)
+       /* Relocation operators were used.  Accept the arguent and
+          leave the relocation value in offset_expr and offset_relocs
+          for the caller to process.  */
+       return expr_end;
+      if (*s == '(' && offset_expr.X_op == O_register)
+       /* Assume that the constant has been elided and that S is a base
+          register.  The rest of the match will fail if the assumption
+          turns out to be wrong.  */
+       sval = 0;
+      else
+       {
+         s = expr_end;
+         if (offset_expr.X_op != O_constant)
+           /* If non-constant operands are allowed then leave them for
+              the caller to process, otherwise fail the match.  */
+           return arg->allow_nonconst ? s : 0;
+         sval = offset_expr.X_add_number;
+       }
+      /* Clear the global state; we're going to install the operand
+        ourselves.  */
+      offset_reloc[0] = BFD_RELOC_UNUSED;
+      offset_expr.X_op = O_absent;
+    }
+  else
+    {
+      s = match_const_int (arg, s, &sval, min_val);
+      if (!s)
+       return 0;
+    }
+
+  arg->last_op_int = sval;
+
+  /* Check the range.  If there's a problem, record the lowest acceptable
+     value in arg->last_op_int in order to prevent an unhelpful error
+     from OP_MSB too.
+
+     Bit counts have traditionally been printed in hex by the disassembler
+     but printed as decimal in error messages.  Only resort to hex if
+     the operand is bigger than 6 bits.  */
+  print_hex = operand->print_hex && operand_base->size > 6;
+  if (sval < min_val || sval > max_val)
+    {
+      if (arg->soft_match)
+       return 0;
+      report_bad_range (arg->insn, arg->argnum, sval, min_val, max_val,
+                       print_hex);
+      arg->last_op_int = min_val;
+    }
+  else if (sval % factor)
+    {
+      if (arg->soft_match)
+       return 0;
+      as_bad (print_hex && sval >= 0
+             ? _("Operand %d of `%s' must be a factor of %d, was 0x%lx.")
+             : _("Operand %d of `%s' must be a factor of %d, was %ld."),
+             arg->argnum, arg->insn->insn_mo->name, factor,
+             (unsigned long) sval);
+      arg->last_op_int = min_val;
+    }
+
+  uval = (unsigned int) sval >> operand->shift;
+  uval -= operand->bias;
+
+  /* Handle -mfix-cn63xxp1.  */
+  if (arg->opnum == 1
+      && mips_fix_cn63xxp1
+      && !mips_opts.micromips
+      && strcmp ("pref", arg->insn->insn_mo->name) == 0)
+    switch (uval)
+      {
+      case 5:
+      case 25:
+      case 26:
+      case 27:
+      case 28:
+      case 29:
+      case 30:
+      case 31:
+       /* These are ok.  */
+       break;
+
+      default:
+       /* The rest must be changed to 28.  */
+       uval = 28;
+       break;
+      }
+
+  insn_insert_operand (arg->insn, operand_base, uval);
+  return s;
+}
+
+/* OP_MAPPED_INT matcher.  */
+
+static char *
+match_mapped_int_operand (struct mips_arg_info *arg,
+                         const struct mips_operand *operand_base, char *s)
+{
+  const struct mips_mapped_int_operand *operand;
+  unsigned int uval, num_vals;
+  offsetT sval;
+
+  operand = (const struct mips_mapped_int_operand *) operand_base;
+  s = match_const_int (arg, s, &sval, operand->int_map[0]);
+  if (!s)
+    return 0;
+
+  num_vals = 1 << operand_base->size;
+  for (uval = 0; uval < num_vals; uval++)
+    if (operand->int_map[uval] == sval)
+      break;
+  if (uval == num_vals)
+    return 0;
+
+  insn_insert_operand (arg->insn, operand_base, uval);
+  return s;
+}
+
+/* OP_MSB matcher.  */
+
+static char *
+match_msb_operand (struct mips_arg_info *arg,
+                  const struct mips_operand *operand_base, char *s)
+{
+  const struct mips_msb_operand *operand;
+  int min_val, max_val, max_high;
+  offsetT size, sval, high;
+
+  operand = (const struct mips_msb_operand *) operand_base;
+  min_val = operand->bias;
+  max_val = min_val + (1 << operand_base->size) - 1;
+  max_high = operand->opsize;
+
+  s = match_const_int (arg, s, &size, 1);
+  if (!s)
+    return 0;
+
+  high = size + arg->last_op_int;
+  sval = operand->add_lsb ? high : size;
+
+  if (size < 0 || high > max_high || sval < min_val || sval > max_val)
+    {
+      if (arg->soft_match)
+       return 0;
+      report_bad_field (arg->last_op_int, size);
+      sval = min_val;
+    }
+  insn_insert_operand (arg->insn, operand_base, sval - min_val);
+  return s;
+}
+
+/* OP_REG matcher.  */
+
+static char *
+match_reg_operand (struct mips_arg_info *arg,
+                  const struct mips_operand *operand_base, char *s)
+{
+  const struct mips_reg_operand *operand;
+  unsigned int regno, uval, num_vals, types;
+
+  operand = (const struct mips_reg_operand *) operand_base;
+  types = convert_reg_type (arg->insn->insn_mo, operand->reg_type);
+  if (!reg_lookup (&s, types, &regno))
+    return 0;
+
+  SKIP_SPACE_TABS (s);
+  if (arg->optional_reg && *s == 0)
+    return 0;
+
+  if (operand->reg_map)
+    {
+      num_vals = 1 << operand->root.size;
+      for (uval = 0; uval < num_vals; uval++)
+       if (operand->reg_map[uval] == regno)
+         break;
+      if (num_vals == uval)
+       return 0;
+    }
+  else
+    uval = regno;
+
+  check_regno (arg, operand->reg_type, regno);
+  arg->last_regno = regno;
+  if (arg->opnum == 1)
+    arg->dest_regno = regno;
+  insn_insert_operand (arg->insn, operand_base, uval);
+  return s;
+}
+
+/* OP_REG_PAIR matcher.  */
+
+static char *
+match_reg_pair_operand (struct mips_arg_info *arg,
+                       const struct mips_operand *operand_base, char *s)
+{
+  const struct mips_reg_pair_operand *operand;
+  unsigned int regno1, regno2, uval, num_vals, types;
+
+  operand = (const struct mips_reg_pair_operand *) operand_base;
+  types = convert_reg_type (arg->insn->insn_mo, operand->reg_type);
+
+  if (!reg_lookup (&s, types, &regno1))
+    return 0;
+
+  SKIP_SPACE_TABS (s);
+  if (*s++ != ',')
+    return 0;
+  arg->argnum += 1;
+
+  if (!reg_lookup (&s, types, &regno2))
+    return 0;
+
+  num_vals = 1 << operand_base->size;
+  for (uval = 0; uval < num_vals; uval++)
+    if (operand->reg1_map[uval] == regno1 && operand->reg2_map[uval] == regno2)
+      break;
+  if (uval == num_vals)
+    return 0;
+
+  check_regno (arg, operand->reg_type, regno1);
+  check_regno (arg, operand->reg_type, regno2);
+  insn_insert_operand (arg->insn, operand_base, uval);
+  return s;
+}
+
+/* OP_PCREL matcher.  The caller chooses the relocation type.  */
+
+static char *
+match_pcrel_operand (char *s)
+{
+  my_getExpression (&offset_expr, s);
+  return expr_end;
+}
+
+/* OP_PERF_REG matcher.  */
+
+static char *
+match_perf_reg_operand (struct mips_arg_info *arg,
+                       const struct mips_operand *operand, char *s)
+{
+  offsetT sval;
+
+  s = match_const_int (arg, s, &sval, 0);
+  if (!s)
+    return 0;
+
+  if (sval != 0
+      && (sval != 1
+         || (mips_opts.arch == CPU_R5900
+             && (strcmp (arg->insn->insn_mo->name, "mfps") == 0
+                 || strcmp (arg->insn->insn_mo->name, "mtps") == 0))))
+    {
+      if (arg->soft_match)
+       return 0;
+      as_bad (_("Invalid performance register (%ld)"), (unsigned long) sval);
+    }
+
+  insn_insert_operand (arg->insn, operand, sval);
+  return s;
+}
+
+/* OP_ADDIUSP matcher.  */
+
+static char *
+match_addiusp_operand (struct mips_arg_info *arg,
+                      const struct mips_operand *operand, char *s)
+{
+  offsetT sval;
+  unsigned int uval;
+
+  s = match_const_int (arg, s, &sval, -256);
+  if (!s)
+    return 0;
+
+  if (sval % 4)
+    return 0;
+
+  sval /= 4;
+  if (!(sval >= -258 && sval <= 257) || (sval >= -2 && sval <= 1))
+    return 0;
+
+  uval = (unsigned int) sval;
+  uval = ((uval >> 1) & ~0xff) | (uval & 0xff);
+  insn_insert_operand (arg->insn, operand, uval);
+  return s;
+}
+
+/* OP_CLO_CLZ_DEST matcher.  */
+
+static char *
+match_clo_clz_dest_operand (struct mips_arg_info *arg,
+                           const struct mips_operand *operand, char *s)
+{
+  unsigned int regno;
+
+  if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, &regno))
+    return 0;
+
+  check_regno (arg, OP_REG_GP, regno);
+  insn_insert_operand (arg->insn, operand, regno | (regno << 5));
+  return s;
+}
+
+/* OP_LWM_SWM_LIST matcher.  */
+
+static char *
+match_lwm_swm_list_operand (struct mips_arg_info *arg,
+                           const struct mips_operand *operand, char *s)
+{
+  unsigned int reglist, sregs, ra;
+
+  if (!reglist_lookup (&s, RTYPE_NUM | RTYPE_GP, &reglist))
+    return 0;
+
+  if (operand->size == 2)
+    {
+      /* The list must include both ra and s0-sN, for 0 <= N <= 3.  E.g.:
+
+        s0, ra
+        s0, s1, ra, s2, s3
+        s0-s2, ra
+
+        and any permutations of these.  */
+      if ((reglist & 0xfff1ffff) != 0x80010000)
+       return 0;
+
+      sregs = (reglist >> 17) & 7;
+      ra = 0;
+    }
+  else
+    {
+      /* The list must include at least one of ra and s0-sN,
+        for 0 <= N <= 8.  (Note that there is a gap between s7 and s8,
+        which are $23 and $30 respectively.)  E.g.:
+
+        ra
+        s0
+        ra, s0, s1, s2
+        s0-s8
+        s0-s5, ra
+
+        and any permutations of these.  */
+      if ((reglist & 0x3f00ffff) != 0)
+       return 0;
+
+      ra = (reglist >> 27) & 0x10;
+      sregs = ((reglist >> 22) & 0x100) | ((reglist >> 16) & 0xff);
+    }
+  sregs += 1;
+  if ((sregs & -sregs) != sregs)
+    return 0;
+
+  insn_insert_operand (arg->insn, operand, (ffs (sregs) - 1) | ra);
+  return s;
+}
+
+/* OP_MDMX_IMM_REG matcher.  */
+
+static char *
+match_mdmx_imm_reg_operand (struct mips_arg_info *arg,
+                           const struct mips_operand *operand, char *s)
+{
+  unsigned int regno, uval, types;
+  bfd_boolean is_qh;
+  const struct mips_opcode *opcode;
+
+  /* The mips_opcode records whether this is an octobyte or quadhalf
+     instruction.  Start out with that bit in place.  */
+  opcode = arg->insn->insn_mo;
+  uval = mips_extract_operand (operand, opcode->match);
+  is_qh = (uval != 0);
+
+  types = convert_reg_type (arg->insn->insn_mo, OP_REG_VEC);
+  if (reg_lookup (&s, types, &regno))
+    {
+      if ((opcode->membership & INSN_5400)
+         && strcmp (opcode->name, "rzu.ob") == 0)
+       {
+         if (arg->soft_match)
+           return 0;
+         as_bad (_("Operand %d of `%s' must be an immediate"),
+                 arg->argnum, opcode->name);
+       }
+
+      /* Check whether this is a vector register or a broadcast of
+        a single element.  */
+      SKIP_SPACE_TABS (s);
+      if (*s == '[')
+       {
+         /* Read the element number.  */
+         expressionS value;
+
+         ++s;
+         SKIP_SPACE_TABS (s);
+         my_getExpression (&value, s);
+         s = expr_end;
+         if (value.X_op != O_constant
+             || value.X_add_number < 0
+             || value.X_add_number > (is_qh ? 3 : 7))
+           {
+             if (arg->soft_match)
+               return 0;
+             as_bad (_("Invalid element selector"));
+             value.X_add_number = 0;
+           }
+         uval |= (unsigned int) value.X_add_number << (is_qh ? 2 : 1) << 5;
+         SKIP_SPACE_TABS (s);
+         if (*s == ']')
+           ++s;
+         else
+           {
+             if (arg->soft_match)
+               return 0;
+             as_bad (_("Expecting ']' found '%s'"), s);
+           }
+       }
+      else
+       {
+         /* A full vector.  */
+         if ((opcode->membership & INSN_5400)
+             && (strcmp (opcode->name, "sll.ob") == 0
+                 || strcmp (opcode->name, "srl.ob") == 0))
+           {
+             if (arg->soft_match)
+               return 0;
+             as_bad (_("Operand %d of `%s' must be scalar"),
+                     arg->argnum, opcode->name);
+           }
+
+         if (is_qh)
+           uval |= MDMX_FMTSEL_VEC_QH << 5;
+         else
+           uval |= MDMX_FMTSEL_VEC_OB << 5;
+       }
+      check_regno (arg, OP_REG_FP, regno);
+      uval |= regno;
+    }
+  else
+    {
+      offsetT sval;
+
+      s = match_const_int (arg, s, &sval, 0);
+      if (!s)
+       return 0;
+      if (sval < 0 || sval > 31)
+       {
+         if (arg->soft_match)
+           return 0;
+         report_bad_range (arg->insn, arg->argnum, sval, 0, 31, FALSE);
+       }
+      uval |= (sval & 31);
+      if (is_qh)
+       uval |= MDMX_FMTSEL_IMM_QH << 5;
+      else
+       uval |= MDMX_FMTSEL_IMM_OB << 5;
+    }
+  insn_insert_operand (arg->insn, operand, uval);
+  return s;
+}
+
+/* OP_PC matcher.  */
+
+static char *
+match_pc_operand (char *s)
+{
+  if (strncmp (s, "$pc", 3) != 0)
+    return 0;
+  s += 3;
+  SKIP_SPACE_TABS (s);
+  return s;
+}
+
+/* OP_REPEAT_DEST_REG and OP_REPEAT_PREV_REG matcher.  OTHER_REGNO is the
+   register that we need to match.  */
+
+static char *
+match_tied_reg_operand (struct mips_arg_info *arg, char *s,
+                       unsigned int other_regno)
+{
+  unsigned int regno;
+
+  if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, &regno)
+      || regno != other_regno)
+    return 0;
+  SKIP_SPACE_TABS (s);
+  if (arg->optional_reg && *s == 0)
+    return 0;
+  return s;
+}
+
+/* S is the text seen for ARG.  Match it against OPERAND.  Return the end
+   of the argument text if the match is successful, otherwise return null.  */
+
+static char *
+match_operand (struct mips_arg_info *arg,
+              const struct mips_operand *operand, char *s)
+{
+  switch (operand->type)
+    {
+    case OP_INT:
+      return match_int_operand (arg, operand, s);
+
+    case OP_MAPPED_INT:
+      return match_mapped_int_operand (arg, operand, s);
+
+    case OP_MSB:
+      return match_msb_operand (arg, operand, s);
+
+    case OP_REG:
+      return match_reg_operand (arg, operand, s);
+
+    case OP_REG_PAIR:
+      return match_reg_pair_operand (arg, operand, s);
+
+    case OP_PCREL:
+      return match_pcrel_operand (s);
+
+    case OP_PERF_REG:
+      return match_perf_reg_operand (arg, operand, s);
+
+    case OP_ADDIUSP_INT:
+      return match_addiusp_operand (arg, operand, s);
+
+    case OP_CLO_CLZ_DEST:
+      return match_clo_clz_dest_operand (arg, operand, s);
+
+    case OP_LWM_SWM_LIST:
+      return match_lwm_swm_list_operand (arg, operand, s);
+
+    case OP_ENTRY_EXIT_LIST:
+    case OP_SAVE_RESTORE_LIST:
+      abort ();
+
+    case OP_MDMX_IMM_REG:
+      return match_mdmx_imm_reg_operand (arg, operand, s);
+
+    case OP_REPEAT_DEST_REG:
+      return match_tied_reg_operand (arg, s, arg->dest_regno);
+
+    case OP_REPEAT_PREV_REG:
+      return match_tied_reg_operand (arg, s, arg->last_regno);
+
+    case OP_PC:
+      return match_pc_operand (s);
+    }
+  abort ();
+}
+
+/* ARG is the state after successfully matching an instruction.
+   Issue any queued-up warnings.  */
+
+static void
+check_completed_insn (struct mips_arg_info *arg)
+{
+  if (arg->seen_at)
+    {
+      if (AT == ATREG)
+       as_warn (_("Used $at without \".set noat\""));
+      else
+       as_warn (_("Used $%u with \".set at=$%u\""), AT, AT);
+    }
+}
+#endif
+
 /* Classify an instruction according to the FIX_VR4120_* enumeration.
    Return NUM_FIX_VR4120_CLASSES if the instruction isn't affected
    by VR4120 errata.  */
@@ -10676,46 +11517,6 @@ static const struct mips_immed mips_immed[] = {
   { 0,0,0,0 }
 };
 
-/* Check whether an odd floating-point register is allowed.  */
-static int
-mips_oddfpreg_ok (const struct mips_opcode *insn, int argnum)
-{
-  const char *s = insn->name;
-
-  if (insn->pinfo == INSN_MACRO)
-    /* Let a macro pass, we'll catch it later when it is expanded.  */
-    return 1;
-
-  if (ISA_HAS_ODD_SINGLE_FPR (mips_opts.isa) || (mips_opts.arch == CPU_R5900))
-    {
-      /* Allow odd registers for single-precision ops.  */
-      switch (insn->pinfo & (FP_S | FP_D))
-       {
-       case FP_S:
-       case 0:
-         return 1;     /* both single precision - ok */
-       case FP_D:
-         return 0;     /* both double precision - fail */
-       default:
-         break;
-       }
-
-      /* Cvt.w.x and cvt.x.w allow an odd register for a 'w' or 's' operand.  */
-      s = strchr (insn->name, '.');
-      if (argnum == 2)
-       s = s != NULL ? strchr (s + 1, '.') : NULL;
-      return (s != NULL && (s[1] == 'w' || s[1] == 's'));
-    } 
-
-  /* Single-precision coprocessor loads and moves are OK too.  */
-  if ((insn->pinfo & FP_S)
-      && (insn->pinfo & (INSN_COPROC_MEMORY_DELAY | INSN_STORE_MEMORY
-                        | INSN_LOAD_COPROC_DELAY | INSN_COPROC_MOVE_DELAY)))
-    return 1;
-
-  return 0;
-}
-
 /* Check if EXPR is a constant between MIN (inclusive) and MAX (exclusive)
    taking bits from BIT up.  */
 static int