ChangeLog rotatation and copyright year update
[deliverable/binutils-gdb.git] / gas / config / tc-mips.c
index 91e6a970b0614869654a1bd07df376a380cb7745..af184303f2883dafb8250742bd6b324b4ab4076a 100644 (file)
@@ -1,7 +1,5 @@
 /* tc-mips.c -- assemble code for a MIPS chip.
-   Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
-   Free Software Foundation, Inc.
+   Copyright (C) 1993-2015 Free Software Foundation, Inc.
    Contributed by the OSF and Ralph Campbell.
    Written by Keith Knowles and Ralph Campbell, working independently.
    Modified for ECOFF and R4000 support by Ian Lance Taylor of Cygnus
@@ -44,6 +42,8 @@ typedef char static_assert2[sizeof (valueT) < 8 ? -1 : 1];
 #define DBG(x)
 #endif
 
+#define streq(a, b)           (strcmp (a, b) == 0)
+
 #define SKIP_SPACE_TABS(S) \
   do { while (*(S) == ' ' || *(S) == '\t') ++(S); } while (0)
 
@@ -89,6 +89,7 @@ int mips_flag_pdr = TRUE;
 #include "ecoff.h"
 
 static char *mips_regmask_frag;
+static char *mips_flags_frag;
 
 #define ZERO 0
 #define ATREG 1
@@ -241,8 +242,8 @@ struct mips_set_options
   /* Restrict general purpose registers and floating point registers
      to 32 bit.  This is initially determined when -mgp32 or -mfp32
      is passed but can changed if the assembler code uses .set mipsN.  */
-  int gp32;
-  int fp32;
+  int gp;
+  int fp;
   /* MIPS architecture (CPU) type.  Changed by .set arch=FOO, the -march
      command line option, and the default CPU.  */
   int arch;
@@ -257,40 +258,45 @@ struct mips_set_options
      Changed by .set singlefloat or .set doublefloat, command-line options
      -msingle-float or -mdouble-float.  The default is false.  */
   bfd_boolean single_float;
-};
 
-/* 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 ASE fields to
-   -1 to indicate that they have not been initialized.  */
+  /* 1 if single-precision operations on odd-numbered registers are
+     allowed.  */
+  int oddspreg;
+};
 
-/* True if -mgp32 was passed.  */
-static int file_mips_gp32 = -1;
+/* Specifies whether module level options have been checked yet.  */
+static bfd_boolean file_mips_opts_checked = FALSE;
 
-/* True if -mfp32 was passed.  */
-static int file_mips_fp32 = -1;
+/* Do we support nan2008?  0 if we don't, 1 if we do, and -1 if the
+   value has not been initialized.  Changed by `.nan legacy' and
+   `.nan 2008', and the -mnan=legacy and -mnan=2008 command line
+   options, and the default CPU.  */
+static int mips_nan2008 = -1;
 
-/* 1 if -msoft-float, 0 if -mhard-float.  The default is 0.  */
-static int file_mips_soft_float = 0;
+/* This is the struct we use to hold the module level set of options.
+   Note that we must set the isa field to ISA_UNKNOWN and the ASE, gp and
+   fp fields to -1 to indicate that they have not been initialized.  */
 
-/* 1 if -msingle-float, 0 if -mdouble-float.  The default is 0.   */
-static int file_mips_single_float = 0;
+static struct mips_set_options file_mips_opts =
+{
+  /* isa */ ISA_UNKNOWN, /* ase */ 0, /* mips16 */ -1, /* micromips */ -1,
+  /* noreorder */ 0,  /* at */ ATREG, /* warn_about_macros */ 0,
+  /* nomove */ 0, /* nobopt */ 0, /* noautoextend */ 0, /* insn32 */ FALSE,
+  /* gp */ -1, /* fp */ -1, /* arch */ CPU_UNKNOWN, /* sym32 */ FALSE,
+  /* soft_float */ FALSE, /* single_float */ FALSE, /* oddspreg */ -1
+};
 
-/* True if -mnan=2008, false if -mnan=legacy.  */
-static bfd_boolean mips_flag_nan2008 = FALSE;
+/* This is similar to file_mips_opts, but for the current set of options.  */
 
 static struct mips_set_options mips_opts =
 {
   /* isa */ ISA_UNKNOWN, /* ase */ 0, /* mips16 */ -1, /* micromips */ -1,
   /* noreorder */ 0,  /* at */ ATREG, /* warn_about_macros */ 0,
   /* nomove */ 0, /* nobopt */ 0, /* noautoextend */ 0, /* insn32 */ FALSE,
-  /* gp32 */ 0, /* fp32 */ 0, /* arch */ CPU_UNKNOWN, /* sym32 */ FALSE,
-  /* soft_float */ FALSE, /* single_float */ FALSE
+  /* gp */ -1, /* fp */ -1, /* arch */ CPU_UNKNOWN, /* sym32 */ FALSE,
+  /* soft_float */ FALSE, /* single_float */ FALSE, /* oddspreg */ -1
 };
 
-/* The set of ASEs that were selected on the command line, either
-   explicitly via ASE options or implicitly through things like -march.  */
-static unsigned int file_ase;
-
 /* Which bits of file_ase were explicitly set or cleared by ASE options.  */
 static unsigned int file_ase_explicit;
 
@@ -300,16 +306,17 @@ static unsigned int file_ase_explicit;
 unsigned long mips_gprmask;
 unsigned long mips_cprmask[4];
 
-/* MIPS ISA we are using for this output file.  */
-static int file_mips_isa = ISA_UNKNOWN;
-
 /* True if any MIPS16 code was produced.  */
 static int file_ase_mips16;
 
 #define ISA_SUPPORTS_MIPS16E (mips_opts.isa == ISA_MIPS32              \
                              || mips_opts.isa == ISA_MIPS32R2          \
+                             || mips_opts.isa == ISA_MIPS32R3          \
+                             || mips_opts.isa == ISA_MIPS32R5          \
                              || mips_opts.isa == ISA_MIPS64            \
-                             || mips_opts.isa == ISA_MIPS64R2)
+                             || mips_opts.isa == ISA_MIPS64R2          \
+                             || mips_opts.isa == ISA_MIPS64R3          \
+                             || mips_opts.isa == ISA_MIPS64R5)
 
 /* True if any microMIPS code was produced.  */
 static int file_ase_micromips;
@@ -327,7 +334,6 @@ static int file_ase_micromips;
 #endif
 
 /* The argument of the -march= flag.  The architecture we are assembling.  */
-static int file_mips_arch = CPU_UNKNOWN;
 static const char *mips_arch_string;
 
 /* The argument of the -mtune= flag.  The architecture for which we
@@ -347,13 +353,20 @@ static int mips_32bitmode = 0;
    || (ABI) == N64_ABI                 \
    || (ABI) == O64_ABI)
 
+#define ISA_IS_R6(ISA)                 \
+  ((ISA) == ISA_MIPS32R6               \
+   || (ISA) == ISA_MIPS64R6)
+
 /*  Return true if ISA supports 64 bit wide gp registers.  */
 #define ISA_HAS_64BIT_REGS(ISA)                \
   ((ISA) == ISA_MIPS3                  \
    || (ISA) == ISA_MIPS4               \
    || (ISA) == ISA_MIPS5               \
    || (ISA) == ISA_MIPS64              \
-   || (ISA) == ISA_MIPS64R2)
+   || (ISA) == ISA_MIPS64R2            \
+   || (ISA) == ISA_MIPS64R3            \
+   || (ISA) == ISA_MIPS64R5            \
+   || (ISA) == ISA_MIPS64R6)
 
 /*  Return true if ISA supports 64 bit wide float registers.  */
 #define ISA_HAS_64BIT_FPRS(ISA)                \
@@ -361,13 +374,22 @@ static int mips_32bitmode = 0;
    || (ISA) == ISA_MIPS4               \
    || (ISA) == ISA_MIPS5               \
    || (ISA) == ISA_MIPS32R2            \
+   || (ISA) == ISA_MIPS32R3            \
+   || (ISA) == ISA_MIPS32R5            \
+   || (ISA) == ISA_MIPS32R6            \
    || (ISA) == ISA_MIPS64              \
-   || (ISA) == ISA_MIPS64R2)
+   || (ISA) == ISA_MIPS64R2            \
+   || (ISA) == ISA_MIPS64R3            \
+   || (ISA) == ISA_MIPS64R5            \
+   || (ISA) == ISA_MIPS64R6)
 
 /* Return true if ISA supports 64-bit right rotate (dror et al.)
    instructions.  */
 #define ISA_HAS_DROR(ISA)              \
   ((ISA) == ISA_MIPS64R2               \
+   || (ISA) == ISA_MIPS64R3            \
+   || (ISA) == ISA_MIPS64R5            \
+   || (ISA) == ISA_MIPS64R6            \
    || (mips_opts.micromips             \
        && ISA_HAS_64BIT_REGS (ISA))    \
    )
@@ -376,32 +398,69 @@ static int mips_32bitmode = 0;
    instructions.  */
 #define ISA_HAS_ROR(ISA)               \
   ((ISA) == ISA_MIPS32R2               \
+   || (ISA) == ISA_MIPS32R3            \
+   || (ISA) == ISA_MIPS32R5            \
+   || (ISA) == ISA_MIPS32R6            \
    || (ISA) == ISA_MIPS64R2            \
+   || (ISA) == ISA_MIPS64R3            \
+   || (ISA) == ISA_MIPS64R5            \
+   || (ISA) == ISA_MIPS64R6            \
    || (mips_opts.ase & ASE_SMARTMIPS)  \
    || mips_opts.micromips              \
    )
 
 /* Return true if ISA supports single-precision floats in odd registers.  */
-#define ISA_HAS_ODD_SINGLE_FPR(ISA)    \
-  ((ISA) == ISA_MIPS32                 \
-   || (ISA) == ISA_MIPS32R2            \
-   || (ISA) == ISA_MIPS64              \
-   || (ISA) == ISA_MIPS64R2)
+#define ISA_HAS_ODD_SINGLE_FPR(ISA, CPU)\
+  (((ISA) == ISA_MIPS32                        \
+    || (ISA) == ISA_MIPS32R2           \
+    || (ISA) == ISA_MIPS32R3           \
+    || (ISA) == ISA_MIPS32R5           \
+    || (ISA) == ISA_MIPS32R6           \
+    || (ISA) == ISA_MIPS64             \
+    || (ISA) == ISA_MIPS64R2           \
+    || (ISA) == ISA_MIPS64R3           \
+    || (ISA) == ISA_MIPS64R5           \
+    || (ISA) == ISA_MIPS64R6           \
+    || (CPU) == CPU_R5900)             \
+   && (CPU) != CPU_LOONGSON_3A)
 
 /* Return true if ISA supports move to/from high part of a 64-bit
    floating-point register. */
 #define ISA_HAS_MXHC1(ISA)             \
   ((ISA) == ISA_MIPS32R2               \
-   || (ISA) == ISA_MIPS64R2)
-
-#define HAVE_32BIT_GPRS                                   \
-    (mips_opts.gp32 || !ISA_HAS_64BIT_REGS (mips_opts.isa))
+   || (ISA) == ISA_MIPS32R3            \
+   || (ISA) == ISA_MIPS32R5            \
+   || (ISA) == ISA_MIPS32R6            \
+   || (ISA) == ISA_MIPS64R2            \
+   || (ISA) == ISA_MIPS64R3            \
+   || (ISA) == ISA_MIPS64R5            \
+   || (ISA) == ISA_MIPS64R6)
+
+/*  Return true if ISA supports legacy NAN.  */
+#define ISA_HAS_LEGACY_NAN(ISA)                \
+  ((ISA) == ISA_MIPS1                  \
+   || (ISA) == ISA_MIPS2               \
+   || (ISA) == ISA_MIPS3               \
+   || (ISA) == ISA_MIPS4               \
+   || (ISA) == ISA_MIPS5               \
+   || (ISA) == ISA_MIPS32              \
+   || (ISA) == ISA_MIPS32R2            \
+   || (ISA) == ISA_MIPS32R3            \
+   || (ISA) == ISA_MIPS32R5            \
+   || (ISA) == ISA_MIPS64              \
+   || (ISA) == ISA_MIPS64R2            \
+   || (ISA) == ISA_MIPS64R3            \
+   || (ISA) == ISA_MIPS64R5)
 
-#define HAVE_32BIT_FPRS                            \
-    (mips_opts.fp32 || !ISA_HAS_64BIT_FPRS (mips_opts.isa))
+#define GPR_SIZE \
+    (mips_opts.gp == 64 && !ISA_HAS_64BIT_REGS (mips_opts.isa) \
+     ? 32 \
+     : mips_opts.gp)
 
-#define HAVE_64BIT_GPRS (!HAVE_32BIT_GPRS)
-#define HAVE_64BIT_FPRS (!HAVE_32BIT_FPRS)
+#define FPR_SIZE \
+    (mips_opts.fp == 64 && !ISA_HAS_64BIT_FPRS (mips_opts.isa) \
+     ? 32 \
+     : mips_opts.fp)
 
 #define HAVE_NEWABI (mips_abi == N32_ABI || mips_abi == N64_ABI)
 
@@ -412,7 +471,7 @@ static int mips_32bitmode = 0;
 
 /* The ABI-derived address size.  */
 #define HAVE_64BIT_ADDRESSES \
-  (HAVE_64BIT_GPRS && (mips_abi == EABI_ABI || mips_abi == N64_ABI))
+  (GPR_SIZE == 64 && (mips_abi == EABI_ABI || mips_abi == N64_ABI))
 #define HAVE_32BIT_ADDRESSES (!HAVE_64BIT_ADDRESSES)
 
 /* The size of symbolic constants (i.e., expressions of the form
@@ -451,7 +510,8 @@ static int mips_32bitmode = 0;
 #define CPU_HAS_ROR(CPU)       CPU_HAS_DROR (CPU)
 
 /* True if CPU is in the Octeon family */
-#define CPU_IS_OCTEON(CPU) ((CPU) == CPU_OCTEON || (CPU) == CPU_OCTEONP || (CPU) == CPU_OCTEON2)
+#define CPU_IS_OCTEON(CPU) ((CPU) == CPU_OCTEON || (CPU) == CPU_OCTEONP \
+                           || (CPU) == CPU_OCTEON2 || (CPU) == CPU_OCTEON3)
 
 /* True if CPU has seq/sne and seqi/snei instructions.  */
 #define CPU_HAS_SEQ(CPU)       (CPU_IS_OCTEON (CPU))
@@ -475,8 +535,14 @@ static int mips_32bitmode = 0;
 #define hilo_interlocks \
   (mips_opts.isa == ISA_MIPS32                        \
    || mips_opts.isa == ISA_MIPS32R2                   \
+   || mips_opts.isa == ISA_MIPS32R3                   \
+   || mips_opts.isa == ISA_MIPS32R5                   \
+   || mips_opts.isa == ISA_MIPS32R6                   \
    || mips_opts.isa == ISA_MIPS64                     \
    || mips_opts.isa == ISA_MIPS64R2                   \
+   || mips_opts.isa == ISA_MIPS64R3                   \
+   || mips_opts.isa == ISA_MIPS64R5                   \
+   || mips_opts.isa == ISA_MIPS64R6                   \
    || mips_opts.arch == CPU_R4010                     \
    || mips_opts.arch == CPU_R5900                     \
    || mips_opts.arch == CPU_R10000                    \
@@ -491,7 +557,7 @@ static int mips_32bitmode = 0;
 /* Whether the processor uses hardware interlocks to protect reads
    from the GPRs after they are loaded from memory, and thus does not
    require nops to be inserted.  This applies to instructions marked
-   INSN_LOAD_MEMORY_DELAY.  These nops are only required at MIPS ISA
+   INSN_LOAD_MEMORY.  These nops are only required at MIPS ISA
    level I and microMIPS mode instructions are always interlocked.  */
 #define gpr_interlocks                                \
   (mips_opts.isa != ISA_MIPS1                         \
@@ -503,8 +569,8 @@ static int mips_32bitmode = 0;
 /* Whether the processor uses hardware interlocks to avoid delays
    required by coprocessor instructions, and thus does not require
    nops to be inserted.  This applies to instructions marked
-   INSN_LOAD_COPROC_DELAY, INSN_COPROC_MOVE_DELAY, and to delays
-   between instructions marked INSN_WRITE_COND_CODE and ones marked
+   INSN_LOAD_COPROC, INSN_COPROC_MOVE, and to delays between
+   instructions marked INSN_WRITE_COND_CODE and ones marked
    INSN_READ_COND_CODE.  These nops are only required at MIPS ISA
    levels I, II, and III and microMIPS mode instructions are always
    interlocked.  */
@@ -539,7 +605,7 @@ static int mips_32bitmode = 0;
   ((mips_opts.mips16 | mips_opts.micromips) != 0)
 
 /* The minimum and maximum signed values that can be stored in a GPR.  */
-#define GPR_SMAX ((offsetT) (((valueT) 1 << (HAVE_64BIT_GPRS ? 63 : 31)) - 1))
+#define GPR_SMAX ((offsetT) (((valueT) 1 << (GPR_SIZE - 1)) - 1))
 #define GPR_SMIN (-GPR_SMAX - 1)
 
 /* MIPS PIC level.  */
@@ -630,7 +696,43 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
    but nothing is ideal around here.
  */
 
-static char *insn_error;
+/* Types of printf format used for instruction-related error messages.
+   "I" means int ("%d") and "S" means string ("%s"). */
+enum mips_insn_error_format {
+  ERR_FMT_PLAIN,
+  ERR_FMT_I,
+  ERR_FMT_SS,
+};
+
+/* Information about an error that was found while assembling the current
+   instruction.  */
+struct mips_insn_error {
+  /* We sometimes need to match an instruction against more than one
+     opcode table entry.  Errors found during this matching are reported
+     against a particular syntactic argument rather than against the
+     instruction as a whole.  We grade these messages so that errors
+     against argument N have a greater priority than an error against
+     any argument < N, since the former implies that arguments up to N
+     were acceptable and that the opcode entry was therefore a closer match.
+     If several matches report an error against the same argument,
+     we only use that error if it is the same in all cases.
+
+     min_argnum is the minimum argument number for which an error message
+     should be accepted.  It is 0 if MSG is against the instruction as
+     a whole.  */
+  int min_argnum;
+
+  /* The printf()-style message, including its format and arguments.  */
+  enum mips_insn_error_format format;
+  const char *msg;
+  union {
+    int i;
+    const char *ss[2];
+  } u;
+};
+
+/* The error that should be reported for the current instruction.  */
+static struct mips_insn_error insn_error;
 
 static int auto_align = 1;
 
@@ -689,6 +791,15 @@ static int mips_debug = 0;
    fill a branch delay slot.  */
 static struct mips_cl_insn history[1 + MAX_NOPS];
 
+/* Arrays of operands for each instruction.  */
+#define MAX_OPERANDS 6
+struct mips_operand_array {
+  const struct mips_operand *operand[MAX_OPERANDS];
+};
+static struct mips_operand_array *mips_operands;
+static struct mips_operand_array *mips16_operands;
+static struct mips_operand_array *micromips_operands;
+
 /* Nop instructions used by emit_nop.  */
 static struct mips_cl_insn nop_insn;
 static struct mips_cl_insn mips16_nop_insn;
@@ -764,12 +875,7 @@ static const unsigned int mips16_to_32_reg_map[] =
 
 /* Map microMIPS register numbers to normal MIPS register numbers.  */
 
-#define micromips_to_32_reg_b_map      mips16_to_32_reg_map
-#define micromips_to_32_reg_c_map      mips16_to_32_reg_map
 #define micromips_to_32_reg_d_map      mips16_to_32_reg_map
-#define micromips_to_32_reg_e_map      mips16_to_32_reg_map
-#define micromips_to_32_reg_f_map      mips16_to_32_reg_map
-#define micromips_to_32_reg_g_map      mips16_to_32_reg_map
 
 /* The microMIPS registers with type h.  */
 static const unsigned int micromips_to_32_reg_h_map1[] =
@@ -781,8 +887,6 @@ static const unsigned int micromips_to_32_reg_h_map2[] =
   6, 7, 7, 21, 22, 5, 6, 7
 };
 
-#define micromips_to_32_reg_l_map      mips16_to_32_reg_map
-
 /* The microMIPS registers with type m.  */
 static const unsigned int micromips_to_32_reg_m_map[] =
 {
@@ -791,12 +895,6 @@ static const unsigned int micromips_to_32_reg_m_map[] =
 
 #define micromips_to_32_reg_n_map      micromips_to_32_reg_m_map
 
-/* The microMIPS registers with type q.  */
-static const unsigned int micromips_to_32_reg_q_map[] =
-{
-  0, 17, 2, 3, 4, 5, 6, 7
-};
-
 /* Classifies the kind of instructions we're interested in when
    implementing -mfix-vr4120.  */
 enum fix_vr4120_class
@@ -833,6 +931,9 @@ static int mips_fix_vr4130;
 /* ...likewise -mfix-24k.  */
 static int mips_fix_24k;
 
+/* ...likewise -mfix-rm7000  */
+static int mips_fix_rm7000;
+
 /* ...likewise -mfix-cn63xxp1 */
 static bfd_boolean mips_fix_cn63xxp1;
 
@@ -1236,8 +1337,7 @@ static void s_ehword (int);
 static void s_cpadd (int);
 static void s_insn (int);
 static void s_nan (int);
-static void md_obj_begin (void);
-static void md_obj_end (void);
+static void s_module (int);
 static void s_mips_ent (int);
 static void s_mips_end (int);
 static void s_mips_frame (int);
@@ -1250,6 +1350,7 @@ static bfd_boolean pic_need_relax (symbolS *, asection *);
 static int relaxed_branch_length (fragS *, asection *, int);
 static int relaxed_micromips_16bit_branch_length (fragS *, asection *, int);
 static int relaxed_micromips_32bit_branch_length (fragS *, asection *, int);
+static void file_mips_check_options (void);
 
 /* Table and functions used to map between CPU/ISA names, and
    ISA levels, and CPU numbers.  */
@@ -1284,7 +1385,13 @@ enum options
     OPTION_MIPS32,
     OPTION_MIPS64,
     OPTION_MIPS32R2,
+    OPTION_MIPS32R3,
+    OPTION_MIPS32R5,
+    OPTION_MIPS32R6,
     OPTION_MIPS64R2,
+    OPTION_MIPS64R3,
+    OPTION_MIPS64R5,
+    OPTION_MIPS64R6,
     OPTION_MIPS16,
     OPTION_NO_MIPS16,
     OPTION_MIPS3D,
@@ -1297,12 +1404,16 @@ enum options
     OPTION_NO_MT,
     OPTION_VIRT,
     OPTION_NO_VIRT,
+    OPTION_MSA,
+    OPTION_NO_MSA,
     OPTION_SMARTMIPS,
     OPTION_NO_SMARTMIPS,
     OPTION_DSPR2,
     OPTION_NO_DSPR2,
     OPTION_EVA,
     OPTION_NO_EVA,
+    OPTION_XPA,
+    OPTION_NO_XPA,
     OPTION_MICROMIPS,
     OPTION_NO_MICROMIPS,
     OPTION_MCU,
@@ -1320,6 +1431,8 @@ enum options
     OPTION_MNO_7000_HILO_FIX,
     OPTION_FIX_24K,
     OPTION_NO_FIX_24K,
+    OPTION_FIX_RM7000,
+    OPTION_NO_FIX_RM7000,
     OPTION_FIX_LOONGSON2F_JUMP,
     OPTION_NO_FIX_LOONGSON2F_JUMP,
     OPTION_FIX_LOONGSON2F_NOP,
@@ -1339,6 +1452,7 @@ enum options
     OPTION_CONSTRUCT_FLOATS,
     OPTION_NO_CONSTRUCT_FLOATS,
     OPTION_FP64,
+    OPTION_FPXX,
     OPTION_GP64,
     OPTION_RELAX_BRANCH,
     OPTION_NO_RELAX_BRANCH,
@@ -1366,6 +1480,8 @@ enum options
     OPTION_NO_PDR,
     OPTION_MVXWORKS_PIC,
     OPTION_NAN,
+    OPTION_ODD_SPREG,
+    OPTION_NO_ODD_SPREG,
     OPTION_END_OF_ENUM
   };
 
@@ -1383,7 +1499,13 @@ struct option md_longopts[] =
   {"mips32", no_argument, NULL, OPTION_MIPS32},
   {"mips64", no_argument, NULL, OPTION_MIPS64},
   {"mips32r2", no_argument, NULL, OPTION_MIPS32R2},
+  {"mips32r3", no_argument, NULL, OPTION_MIPS32R3},
+  {"mips32r5", no_argument, NULL, OPTION_MIPS32R5},
+  {"mips32r6", no_argument, NULL, OPTION_MIPS32R6},
   {"mips64r2", no_argument, NULL, OPTION_MIPS64R2},
+  {"mips64r3", no_argument, NULL, OPTION_MIPS64R3},
+  {"mips64r5", no_argument, NULL, OPTION_MIPS64R5},
+  {"mips64r6", no_argument, NULL, OPTION_MIPS64R6},
 
   /* Options which specify Application Specific Extensions (ASEs).  */
   {"mips16", no_argument, NULL, OPTION_MIPS16},
@@ -1408,6 +1530,10 @@ struct option md_longopts[] =
   {"mno-mcu", no_argument, NULL, OPTION_NO_MCU},
   {"mvirt", no_argument, NULL, OPTION_VIRT},
   {"mno-virt", no_argument, NULL, OPTION_NO_VIRT},
+  {"mmsa", no_argument, NULL, OPTION_MSA},
+  {"mno-msa", no_argument, NULL, OPTION_NO_MSA},
+  {"mxpa", no_argument, NULL, OPTION_XPA},
+  {"mno-xpa", no_argument, NULL, OPTION_NO_XPA},
 
   /* Old-style architecture options.  Don't add more of these.  */
   {"m4650", no_argument, NULL, OPTION_M4650},
@@ -1433,6 +1559,8 @@ struct option md_longopts[] =
   {"mno-fix-vr4130", no_argument, NULL, OPTION_NO_FIX_VR4130},
   {"mfix-24k",    no_argument, NULL, OPTION_FIX_24K},
   {"mno-fix-24k", no_argument, NULL, OPTION_NO_FIX_24K},
+  {"mfix-rm7000",    no_argument, NULL, OPTION_FIX_RM7000},
+  {"mno-fix-rm7000", no_argument, NULL, OPTION_NO_FIX_RM7000},
   {"mfix-cn63xxp1", no_argument, NULL, OPTION_FIX_CN63XXP1},
   {"mno-fix-cn63xxp1", no_argument, NULL, OPTION_NO_FIX_CN63XXP1},
 
@@ -1448,6 +1576,7 @@ struct option md_longopts[] =
   {"construct-floats", no_argument, NULL, OPTION_CONSTRUCT_FLOATS},
   {"no-construct-floats", no_argument, NULL, OPTION_NO_CONSTRUCT_FLOATS},
   {"mfp64", no_argument, NULL, OPTION_FP64},
+  {"mfpxx", no_argument, NULL, OPTION_FPXX},
   {"mgp64", no_argument, NULL, OPTION_GP64},
   {"relax-branch", no_argument, NULL, OPTION_RELAX_BRANCH},
   {"no-relax-branch", no_argument, NULL, OPTION_NO_RELAX_BRANCH},
@@ -1461,6 +1590,8 @@ struct option md_longopts[] =
   {"mhard-float", no_argument, NULL, OPTION_HARD_FLOAT},
   {"msingle-float", no_argument, NULL, OPTION_SINGLE_FLOAT},
   {"mdouble-float", no_argument, NULL, OPTION_DOUBLE_FLOAT},
+  {"modd-spreg", no_argument, NULL, OPTION_ODD_SPREG},
+  {"mno-odd-spreg", no_argument, NULL, OPTION_NO_ODD_SPREG},
 
   /* Strictly speaking this next option is ELF specific,
      but we allow it for other ports as well in order to
@@ -1514,51 +1645,74 @@ struct mips_ase
   int mips64_rev;
   int micromips32_rev;
   int micromips64_rev;
+
+  /* The architecture where the ASE was removed or -1 if the extension has not
+     been removed.  */
+  int rem_rev;
 };
 
 /* A table of all supported ASEs.  */
 static const struct mips_ase mips_ases[] = {
   { "dsp", ASE_DSP, ASE_DSP64,
     OPTION_DSP, OPTION_NO_DSP,
-    2, 2, 2, 2 },
+    2, 2, 2, 2,
+    -1 },
 
   { "dspr2", ASE_DSP | ASE_DSPR2, 0,
     OPTION_DSPR2, OPTION_NO_DSPR2,
-    2, 2, 2, 2 },
+    2, 2, 2, 2,
+    -1 },
 
   { "eva", ASE_EVA, 0,
     OPTION_EVA, OPTION_NO_EVA,
-    2, 2, 2, 2 },
+     2,  2,  2,  2,
+    -1 },
 
   { "mcu", ASE_MCU, 0,
     OPTION_MCU, OPTION_NO_MCU,
-    2, 2, 2, 2 },
+     2,  2,  2,  2,
+    -1 },
 
   /* Deprecated in MIPS64r5, but we don't implement that yet.  */
   { "mdmx", ASE_MDMX, 0,
     OPTION_MDMX, OPTION_NO_MDMX,
-    -1, 1, -1, -1 },
+    -1, 1, -1, -1,
+     6 },
 
   /* Requires 64-bit FPRs, so the minimum MIPS32 revision is 2.  */
   { "mips3d", ASE_MIPS3D, 0,
     OPTION_MIPS3D, OPTION_NO_MIPS3D,
-    2, 1, -1, -1 },
+    2, 1, -1, -1,
+    6 },
 
   { "mt", ASE_MT, 0,
     OPTION_MT, OPTION_NO_MT,
-    2, 2, -1, -1 },
+     2,  2, -1, -1,
+    -1 },
 
   { "smartmips", ASE_SMARTMIPS, 0,
     OPTION_SMARTMIPS, OPTION_NO_SMARTMIPS,
-    1, -1, -1, -1 },
+    1, -1, -1, -1,
+    6 },
 
   { "virt", ASE_VIRT, ASE_VIRT64,
     OPTION_VIRT, OPTION_NO_VIRT,
-    2, 2, 2, 2 }
+     2,  2,  2,  2,
+    -1 },
+
+  { "msa", ASE_MSA, ASE_MSA64,
+    OPTION_MSA, OPTION_NO_MSA,
+     2,  2,  2,  2,
+    -1 },
+
+  { "xpa", ASE_XPA, 0,
+    OPTION_XPA, OPTION_NO_XPA,
+     2,  2, -1, -1,
+    -1 },
 };
 
 /* The set of ASEs that require -mfp64.  */
-#define FP64_ASES (ASE_MIPS3D | ASE_MDMX)
+#define FP64_ASES (ASE_MIPS3D | ASE_MDMX | ASE_MSA)
 
 /* Groups of ASE_* flags that represent different revisions of an ASE.  */
 static const unsigned int mips_ase_groups[] = {
@@ -1607,6 +1761,7 @@ static const pseudo_typeS mips_pseudo_table[] =
   {"cpadd", s_cpadd, 0},
   {"insn", s_insn, 0},
   {"nan", s_nan, 0},
+  {"module", s_module, 0},
 
   /* Relatively generic pseudo-ops that happen to be used on MIPS
      chips.  */
@@ -1674,6 +1829,7 @@ static const pseudo_typeS mips_nonecoff_pseudo_table[] =
 int
 mips_address_bytes (void)
 {
+  file_mips_check_options ();
   return HAVE_64BIT_ADDRESSES ? 8 : 4;
 }
 
@@ -1705,7 +1861,7 @@ static void mips_compressed_mark_labels (void);
 static inline void
 mips_clear_insn_labels (void)
 {
-  register struct insn_label_list **pl;
+  struct insn_label_list **pl;
   segment_info_type *si;
 
   if (now_seg)
@@ -1730,11 +1886,10 @@ mips_mark_labels (void)
 \f
 static char *expr_end;
 
-/* Expressions which appear in macro instructions.  These are set by
-   mips_ip and read by macro.  */
+/* An expression in a macro instruction.  This is set by mips_ip and
+   mips16_ip and when populated is always an O_constant.  */
 
 static expressionS imm_expr;
-static expressionS imm2_expr;
 
 /* The relocatable field in an instruction and the relocs associated
    with it.  These variables are used for instructions like LUI and
@@ -1809,6 +1964,15 @@ mips_isa_rev (void)
   if (mips_opts.isa == ISA_MIPS32R2 || mips_opts.isa == ISA_MIPS64R2)
     return 2;
 
+  if (mips_opts.isa == ISA_MIPS32R3 || mips_opts.isa == ISA_MIPS64R3)
+    return 3;
+
+  if (mips_opts.isa == ISA_MIPS32R5 || mips_opts.isa == ISA_MIPS64R5)
+    return 5;
+
+  if (mips_opts.isa == ISA_MIPS32R6 || mips_opts.isa == ISA_MIPS64R6)
+    return 6;
+
   /* microMIPS implies revision 2 or above.  */
   if (mips_opts.micromips)
     return 2;
@@ -1854,18 +2018,28 @@ mips_check_isa_supports_ase (const struct mips_ase *ase)
       base = mips_opts.micromips ? "microMIPS" : "MIPS";
       size = ISA_HAS_64BIT_REGS (mips_opts.isa) ? 64 : 32;
       if (min_rev < 0)
-       as_warn (_("The %d-bit %s architecture does not support the"
+       as_warn (_("the %d-bit %s architecture does not support the"
                   " `%s' extension"), size, base, ase->name);
       else
-       as_warn (_("The `%s' extension requires %s%d revision %d or greater"),
+       as_warn (_("the `%s' extension requires %s%d revision %d or greater"),
                 ase->name, base, size, min_rev);
     }
+  else if ((ase->rem_rev > 0 && mips_isa_rev () >= ase->rem_rev)
+          && (warned_isa & ase->flags) != ase->flags)
+    {
+      warned_isa |= ase->flags;
+      base = mips_opts.micromips ? "microMIPS" : "MIPS";
+      size = ISA_HAS_64BIT_REGS (mips_opts.isa) ? 64 : 32;
+      as_warn (_("the `%s' extension was removed in %s%d revision %d"),
+              ase->name, base, size, ase->rem_rev);
+    }
+
   if ((ase->flags & FP64_ASES)
-      && mips_opts.fp32
+      && mips_opts.fp != 64
       && (warned_fp32 & ase->flags) != ase->flags)
     {
       warned_fp32 |= ase->flags;
-      as_warn (_("The `%s' extension requires 64-bit FPRs"), ase->name);
+      as_warn (_("the `%s' extension requires 64-bit FPRs"), ase->name);
     }
 }
 
@@ -1889,14 +2063,15 @@ mips_check_isa_supports_ases (void)
    that were affected.  */
 
 static unsigned int
-mips_set_ase (const struct mips_ase *ase, bfd_boolean enabled_p)
+mips_set_ase (const struct mips_ase *ase, struct mips_set_options *opts,
+             bfd_boolean enabled_p)
 {
   unsigned int mask;
 
   mask = mips_ase_mask (ase->flags);
-  mips_opts.ase &= ~mask;
+  opts->ase &= ~mask;
   if (enabled_p)
-    mips_opts.ase |= ase->flags;
+    opts->ase |= ase->flags;
   return mask;
 }
 
@@ -1966,6 +2141,39 @@ create_insn (struct mips_cl_insn *insn, const struct mips_opcode *mo)
   insn->cleared_p = 0;
 }
 
+/* Get a list of all the operands in INSN.  */
+
+static const struct mips_operand_array *
+insn_operands (const struct mips_cl_insn *insn)
+{
+  if (insn->insn_mo >= &mips_opcodes[0]
+      && insn->insn_mo < &mips_opcodes[NUMOPCODES])
+    return &mips_operands[insn->insn_mo - &mips_opcodes[0]];
+
+  if (insn->insn_mo >= &mips16_opcodes[0]
+      && insn->insn_mo < &mips16_opcodes[bfd_mips16_num_opcodes])
+    return &mips16_operands[insn->insn_mo - &mips16_opcodes[0]];
+
+  if (insn->insn_mo >= &micromips_opcodes[0]
+      && insn->insn_mo < &micromips_opcodes[bfd_micromips_num_opcodes])
+    return &micromips_operands[insn->insn_mo - &micromips_opcodes[0]];
+
+  abort ();
+}
+
+/* Get a description of operand OPNO of INSN.  */
+
+static const struct mips_operand *
+insn_opno (const struct mips_cl_insn *insn, unsigned opno)
+{
+  const struct mips_operand_array *operands;
+
+  operands = insn_operands (insn);
+  if (opno >= MAX_OPERANDS || !operands->operand[opno])
+    abort ();
+  return operands->operand[opno];
+}
+
 /* Install UVAL as the value of OPERAND in INSN.  */
 
 static inline void
@@ -1975,6 +2183,15 @@ insn_insert_operand (struct mips_cl_insn *insn,
   insn->insn_opcode = mips_insert_operand (operand, insn->insn_opcode, uval);
 }
 
+/* Extract the value of OPERAND from INSN.  */
+
+static inline unsigned
+insn_extract_operand (const struct mips_cl_insn *insn,
+                     const struct mips_operand *operand)
+{
+  return mips_extract_operand (operand, insn->insn_opcode);
+}
+
 /* Record the current MIPS16/microMIPS mode in now_seg.  */
 
 static void
@@ -2119,6 +2336,111 @@ insert_into_history (unsigned int first, unsigned int n,
     }
 }
 
+/* Clear the error in insn_error.  */
+
+static void
+clear_insn_error (void)
+{
+  memset (&insn_error, 0, sizeof (insn_error));
+}
+
+/* Possibly record error message MSG for the current instruction.
+   If the error is about a particular argument, ARGNUM is the 1-based
+   number of that argument, otherwise it is 0.  FORMAT is the format
+   of MSG.  Return true if MSG was used, false if the current message
+   was kept.  */
+
+static bfd_boolean
+set_insn_error_format (int argnum, enum mips_insn_error_format format,
+                      const char *msg)
+{
+  if (argnum == 0)
+    {
+      /* Give priority to errors against specific arguments, and to
+        the first whole-instruction message.  */
+      if (insn_error.msg)
+       return FALSE;
+    }
+  else
+    {
+      /* Keep insn_error if it is against a later argument.  */
+      if (argnum < insn_error.min_argnum)
+       return FALSE;
+
+      /* If both errors are against the same argument but are different,
+        give up on reporting a specific error for this argument.
+        See the comment about mips_insn_error for details.  */
+      if (argnum == insn_error.min_argnum
+         && insn_error.msg
+         && strcmp (insn_error.msg, msg) != 0)
+       {
+         insn_error.msg = 0;
+         insn_error.min_argnum += 1;
+         return FALSE;
+       }
+    }
+  insn_error.min_argnum = argnum;
+  insn_error.format = format;
+  insn_error.msg = msg;
+  return TRUE;
+}
+
+/* Record an instruction error with no % format fields.  ARGNUM and MSG are
+   as for set_insn_error_format.  */
+
+static void
+set_insn_error (int argnum, const char *msg)
+{
+  set_insn_error_format (argnum, ERR_FMT_PLAIN, msg);
+}
+
+/* Record an instruction error with one %d field I.  ARGNUM and MSG are
+   as for set_insn_error_format.  */
+
+static void
+set_insn_error_i (int argnum, const char *msg, int i)
+{
+  if (set_insn_error_format (argnum, ERR_FMT_I, msg))
+    insn_error.u.i = i;
+}
+
+/* Record an instruction error with two %s fields S1 and S2.  ARGNUM and MSG
+   are as for set_insn_error_format.  */
+
+static void
+set_insn_error_ss (int argnum, const char *msg, const char *s1, const char *s2)
+{
+  if (set_insn_error_format (argnum, ERR_FMT_SS, msg))
+    {
+      insn_error.u.ss[0] = s1;
+      insn_error.u.ss[1] = s2;
+    }
+}
+
+/* Report the error in insn_error, which is against assembly code STR.  */
+
+static void
+report_insn_error (const char *str)
+{
+  const char *msg;
+
+  msg = ACONCAT ((insn_error.msg, " `%s'", NULL));
+  switch (insn_error.format)
+    {
+    case ERR_FMT_PLAIN:
+      as_bad (msg, str);
+      break;
+
+    case ERR_FMT_I:
+      as_bad (msg, insn_error.u.i, str);
+      break;
+
+    case ERR_FMT_SS:
+      as_bad (msg, insn_error.u.ss[0], insn_error.u.ss[1], str);
+      break;
+    }
+}
+
 /* Initialize vr4120_conflicts.  There is a bit of duplication here:
    the idea is to make it obvious at a glance that each errata is
    included.  */
@@ -2168,18 +2490,25 @@ struct regname {
   unsigned int num;
 };
 
-#define RTYPE_MASK     0x1ff00
-#define RTYPE_NUM      0x00100
-#define RTYPE_FPU      0x00200
-#define RTYPE_FCC      0x00400
-#define RTYPE_VEC      0x00800
-#define RTYPE_GP       0x01000
-#define RTYPE_CP0      0x02000
-#define RTYPE_PC       0x04000
-#define RTYPE_ACC      0x08000
-#define RTYPE_CCC      0x10000
-#define RNUM_MASK      0x000ff
-#define RWARN          0x80000
+#define RNUM_MASK      0x00000ff
+#define RTYPE_MASK     0x0ffff00
+#define RTYPE_NUM      0x0000100
+#define RTYPE_FPU      0x0000200
+#define RTYPE_FCC      0x0000400
+#define RTYPE_VEC      0x0000800
+#define RTYPE_GP       0x0001000
+#define RTYPE_CP0      0x0002000
+#define RTYPE_PC       0x0004000
+#define RTYPE_ACC      0x0008000
+#define RTYPE_CCC      0x0010000
+#define RTYPE_VI       0x0020000
+#define RTYPE_VF       0x0040000
+#define RTYPE_R5900_I  0x0080000
+#define RTYPE_R5900_Q  0x0100000
+#define RTYPE_R5900_R  0x0200000
+#define RTYPE_R5900_ACC        0x0400000
+#define RTYPE_MSA      0x0800000
+#define RWARN          0x8000000
 
 #define GENERIC_REGISTER_NUMBERS \
     {"$0",     RTYPE_NUM | 0},  \
@@ -2365,6 +2694,18 @@ struct regname {
     {"$v30",   RTYPE_VEC | 30}, \
     {"$v31",   RTYPE_VEC | 31}
 
+#define R5900_I_NAMES \
+    {"$I",     RTYPE_R5900_I | 0}
+
+#define R5900_Q_NAMES \
+    {"$Q",     RTYPE_R5900_Q | 0}
+
+#define R5900_R_NAMES \
+    {"$R",     RTYPE_R5900_R | 0}
+
+#define R5900_ACC_NAMES \
+    {"$ACC",   RTYPE_R5900_ACC | 0 }
+
 #define MIPS_DSP_ACCUMULATOR_NAMES \
     {"$ac0",   RTYPE_ACC | 0}, \
     {"$ac1",   RTYPE_ACC | 1}, \
@@ -2385,6 +2726,10 @@ static const struct regname reg_names[] = {
 
   MIPS16_SPECIAL_REGISTER_NAMES,
   MDMX_VECTOR_REGISTER_NAMES,
+  R5900_I_NAMES,
+  R5900_Q_NAMES,
+  R5900_R_NAMES,
+  R5900_ACC_NAMES,
   MIPS_DSP_ACCUMULATOR_NAMES,
   {0, 0}
 };
@@ -2412,24 +2757,14 @@ mips_prefer_vec_regno (unsigned int symval)
   return symval;
 }
 
-/* Return true if the string at *SPTR is a valid register name.  If so,
-   move *SPTR past the register and store the register's symbol value
-   in *SYMVAL.  This symbol value includes the register number
-   (RNUM_MASK) and register type (RTYPE_MASK).  */
+/* Return true if string [S, E) is a valid register name, storing its
+   symbol value in *SYMVAL_PTR if so.  */
 
 static bfd_boolean
-mips_parse_register (char **sptr, unsigned int *symval)
+mips_parse_register_1 (char *s, char *e, unsigned int *symval_ptr)
 {
-  symbolS *symbol;
-  char *s, *e;
   char save_c;
-
-  /* Find end of name.  */
-  s = e = *sptr;
-  if (is_name_beginner (*e))
-    ++e;
-  while (is_part_of_name (*e))
-    ++e;
+  symbolS *symbol;
 
   /* Terminate name.  */
   save_c = *e;
@@ -2442,8 +2777,63 @@ mips_parse_register (char **sptr, unsigned int *symval)
   if (!symbol || S_GET_SEGMENT (symbol) != reg_section)
     return FALSE;
 
+  *symval_ptr = S_GET_VALUE (symbol);
+  return TRUE;
+}
+
+/* Return true if the string at *SPTR is a valid register name.  Allow it
+   to have a VU0-style channel suffix of the form x?y?z?w? if CHANNELS_PTR
+   is nonnull.
+
+   When returning true, move *SPTR past the register, store the
+   register's symbol value in *SYMVAL_PTR and the channel mask in
+   *CHANNELS_PTR (if nonnull).  The symbol value includes the register
+   number (RNUM_MASK) and register type (RTYPE_MASK).  The channel mask
+   is a 4-bit value of the form XYZW and is 0 if no suffix was given.  */
+
+static bfd_boolean
+mips_parse_register (char **sptr, unsigned int *symval_ptr,
+                    unsigned int *channels_ptr)
+{
+  char *s, *e, *m;
+  const char *q;
+  unsigned int channels, symval, bit;
+
+  /* Find end of name.  */
+  s = e = *sptr;
+  if (is_name_beginner (*e))
+    ++e;
+  while (is_part_of_name (*e))
+    ++e;
+
+  channels = 0;
+  if (!mips_parse_register_1 (s, e, &symval))
+    {
+      if (!channels_ptr)
+       return FALSE;
+
+      /* Eat characters from the end of the string that are valid
+        channel suffixes.  The preceding register must be $ACC or
+        end with a digit, so there is no ambiguity.  */
+      bit = 1;
+      m = e;
+      for (q = "wzyx"; *q; q++, bit <<= 1)
+       if (m > s && m[-1] == *q)
+         {
+           --m;
+           channels |= bit;
+         }
+
+      if (channels == 0
+         || !mips_parse_register_1 (s, m, &symval)
+         || (symval & (RTYPE_VI | RTYPE_VF | RTYPE_R5900_ACC)) == 0)
+       return FALSE;
+    }
+
   *sptr = e;
-  *symval = S_GET_VALUE (symbol);
+  *symval_ptr = symval;
+  if (channels_ptr)
+    *channels_ptr = channels;
   return TRUE;
 }
 
@@ -2456,7 +2846,7 @@ reg_lookup (char **s, unsigned int types, unsigned int *regnop)
 {
   unsigned int regno;
 
-  if (mips_parse_register (s, &regno))
+  if (mips_parse_register (s, &regno, NULL))
     {
       if (types & RTYPE_VEC)
        regno = mips_prefer_vec_regno (regno);
@@ -2468,7 +2858,7 @@ reg_lookup (char **s, unsigned int types, unsigned int *regnop)
   else
     {
       if (types & RWARN)
-       as_warn (_("Unrecognized register name `%s'"), *s);
+       as_warn (_("unrecognized register name `%s'"), *s);
       regno = ~0;
     }
   if (regnop)
@@ -2476,13 +2866,37 @@ reg_lookup (char **s, unsigned int types, unsigned int *regnop)
   return regno <= RNUM_MASK;
 }
 
+/* Parse a VU0 "x?y?z?w?" channel mask at S and store the associated
+   mask in *CHANNELS.  Return a pointer to the first unconsumed character.  */
+
+static char *
+mips_parse_vu0_channels (char *s, unsigned int *channels)
+{
+  unsigned int i;
+
+  *channels = 0;
+  for (i = 0; i < 4; i++)
+    if (*s == "xyzw"[i])
+      {
+       *channels |= 1 << (3 - i);
+       ++s;
+      }
+  return s;
+}
+
 /* Token types for parsed operand lists.  */
 enum mips_operand_token_type {
   /* A plain register, e.g. $f2.  */
   OT_REG,
 
-  /* An element of a vector, e.g. $v0[1].  */
-  OT_REG_ELEMENT,
+  /* A 4-bit XYZW channel mask.  */
+  OT_CHANNELS,
+
+  /* A constant vector index, e.g. [1].  */
+  OT_INTEGER_INDEX,
+
+  /* A register vector index, e.g. [$2].  */
+  OT_REG_INDEX,
 
   /* A continuous range of registers, e.g. $s0-$s4.  */
   OT_REG_RANGE,
@@ -2497,6 +2911,9 @@ enum mips_operand_token_type {
      before OT_REGs.  */
   OT_CHAR,
 
+  /* A doubled character, either "--" or "++".  */
+  OT_DOUBLE_CHAR,
+
   /* The end of the operand list.  */
   OT_END
 };
@@ -2508,14 +2925,14 @@ struct mips_operand_token
   enum mips_operand_token_type type;
   union
   {
-    /* The register symbol value for an OT_REG.  */
+    /* The register symbol value for an OT_REG or OT_REG_INDEX.  */
     unsigned int regno;
 
-    /* The register symbol value and index for an OT_REG_ELEMENT.  */
-    struct {
-      unsigned int regno;
-      addressT index;
-    } reg_element;
+    /* The 4-bit channel mask for an OT_CHANNEL_SUFFIX.  */
+    unsigned int channels;
+
+    /* The integer value of an OT_INTEGER_INDEX.  */
+    addressT index;
 
     /* The two register symbol values involved in an OT_REG_RANGE.  */
     struct {
@@ -2539,7 +2956,7 @@ struct mips_operand_token
       int length;
     } flt;
 
-    /* The character represented by an OT_CHAR.  */
+    /* The character represented by an OT_CHAR or OT_DOUBLE_CHAR.  */
     char ch;
   } u;
 };
@@ -2565,22 +2982,56 @@ static char *
 mips_parse_base_start (char *s)
 {
   struct mips_operand_token token;
-  unsigned int regno;
+  unsigned int regno, channels;
+  bfd_boolean decrement_p;
 
   if (*s != '(')
     return 0;
 
   ++s;
   SKIP_SPACE_TABS (s);
-  if (!mips_parse_register (&s, &regno))
+
+  /* Only match "--" as part of a base expression.  In other contexts "--X"
+     is a double negative.  */
+  decrement_p = (s[0] == '-' && s[1] == '-');
+  if (decrement_p)
+    {
+      s += 2;
+      SKIP_SPACE_TABS (s);
+    }
+
+  /* Allow a channel specifier because that leads to better error messages
+     than treating something like "$vf0x++" as an expression.  */
+  if (!mips_parse_register (&s, &regno, &channels))
     return 0;
 
   token.u.ch = '(';
   mips_add_token (&token, OT_CHAR);
 
+  if (decrement_p)
+    {
+      token.u.ch = '-';
+      mips_add_token (&token, OT_DOUBLE_CHAR);
+    }
+
   token.u.regno = regno;
   mips_add_token (&token, OT_REG);
 
+  if (channels)
+    {
+      token.u.channels = channels;
+      mips_add_token (&token, OT_CHANNELS);
+    }
+
+  /* For consistency, only match "++" as part of base expressions too.  */
+  SKIP_SPACE_TABS (s);
+  if (s[0] == '+' && s[1] == '+')
+    {
+      s += 2;
+      token.u.ch = '+';
+      mips_add_token (&token, OT_DOUBLE_CHAR);
+    }
+
   return s;
 }
 
@@ -2593,7 +3044,7 @@ static char *
 mips_parse_argument_token (char *s, char float_format)
 {
   char *end, *save_in, *err;
-  unsigned int regno1, regno2;
+  unsigned int regno1, regno2, channels;
   struct mips_operand_token token;
 
   /* First look for "($reg", since we want to treat that as an
@@ -2612,17 +3063,28 @@ mips_parse_argument_token (char *s, char float_format)
     }
 
   /* Handle tokens that start with a register.  */
-  if (mips_parse_register (&s, &regno1))
+  if (mips_parse_register (&s, &regno1, &channels))
     {
+      if (channels)
+       {
+         /* A register and a VU0 channel suffix.  */
+         token.u.regno = regno1;
+         mips_add_token (&token, OT_REG);
+
+         token.u.channels = channels;
+         mips_add_token (&token, OT_CHANNELS);
+         return s;
+       }
+
       SKIP_SPACE_TABS (s);
       if (*s == '-')
        {
          /* A register range.  */
          ++s;
          SKIP_SPACE_TABS (s);
-         if (!mips_parse_register (&s, &regno2))
+         if (!mips_parse_register (&s, &regno2, NULL))
            {
-             insn_error = _("Invalid register range");
+             set_insn_error (0, _("invalid register range"));
              return 0;
            }
 
@@ -2631,37 +3093,40 @@ mips_parse_argument_token (char *s, char float_format)
          mips_add_token (&token, OT_REG_RANGE);
          return s;
        }
-      else if (*s == '[')
-       {
-         /* A vector element.  */
-         expressionS element;
 
+      /* Add the register itself.  */
+      token.u.regno = regno1;
+      mips_add_token (&token, OT_REG);
+
+      /* Check for a vector index.  */
+      if (*s == '[')
+       {
          ++s;
          SKIP_SPACE_TABS (s);
-         my_getExpression (&element, s);
-         if (element.X_op != O_constant)
+         if (mips_parse_register (&s, &token.u.regno, NULL))
+           mips_add_token (&token, OT_REG_INDEX);
+         else
            {
-             insn_error = _("Vector element must be constant");
-             return 0;
+             expressionS element;
+
+             my_getExpression (&element, s);
+             if (element.X_op != O_constant)
+               {
+                 set_insn_error (0, _("vector element must be constant"));
+                 return 0;
+               }
+             s = expr_end;
+             token.u.index = element.X_add_number;
+             mips_add_token (&token, OT_INTEGER_INDEX);
            }
-         s = expr_end;
          SKIP_SPACE_TABS (s);
          if (*s != ']')
            {
-             insn_error = _("Missing `]'");
+             set_insn_error (0, _("missing `]'"));
              return 0;
            }
          ++s;
-
-         token.u.reg_element.regno = regno1;
-         token.u.reg_element.index = element.X_add_number;
-         mips_add_token (&token, OT_REG_ELEMENT);
-         return s;
        }
-
-      /* Looks like just a plain register.  */
-      token.u.regno = regno1;
-      mips_add_token (&token, OT_REG);
       return s;
     }
 
@@ -2676,7 +3141,7 @@ mips_parse_argument_token (char *s, char float_format)
       input_line_pointer = save_in;
       if (err && *err)
        {
-         insn_error = err;
+         set_insn_error (0, err);
          return 0;
        }
       if (s != end)
@@ -2831,28 +3296,36 @@ is_delay_slot_valid (const struct mips_opcode *mo)
   return TRUE;
 }
 
-/* For consistency checking, verify that all bits of OPCODE are
-   specified either by the match/mask part of the instruction
-   definition, or by the operand list.  INSN_BITS says which
-   bits of the instruction are significant and DECODE_OPERAND
-   provides the mips_operand description of each operand.  */
+/* For consistency checking, verify that all bits of OPCODE are specified
+   either by the match/mask part of the instruction definition, or by the
+   operand list.  Also build up a list of operands in OPERANDS.
+
+   INSN_BITS says which bits of the instruction are significant.
+   If OPCODE is a standard or microMIPS instruction, DECODE_OPERAND
+   provides the mips_operand description of each operand.  DECODE_OPERAND
+   is null for MIPS16 instructions.  */
 
 static int
 validate_mips_insn (const struct mips_opcode *opcode,
                    unsigned long insn_bits,
-                   const struct mips_operand *(*decode_operand) (const char *))
+                   const struct mips_operand *(*decode_operand) (const char *),
+                   struct mips_operand_array *operands)
 {
   const char *s;
-  unsigned long used_bits, doubled, undefined;
+  unsigned long used_bits, doubled, undefined, opno, mask;
   const struct mips_operand *operand;
 
-  if ((opcode->mask & opcode->match) != opcode->match)
+  mask = (opcode->pinfo == INSN_MACRO ? 0 : opcode->mask);
+  if ((mask & opcode->match) != opcode->match)
     {
       as_bad (_("internal: bad mips opcode (mask error): %s %s"),
              opcode->name, opcode->args);
       return 0;
     }
   used_bits = 0;
+  opno = 0;
+  if (opcode->pinfo2 & INSN2_VU0_CHANNEL_SUFFIX)
+    used_bits = mips_insert_operand (&mips_vu0_channel_mask, used_bits, -1);
   for (s = opcode->args; *s; ++s)
     switch (*s)
       {
@@ -2861,34 +3334,49 @@ validate_mips_insn (const struct mips_opcode *opcode,
       case ')':
        break;
 
+      case '#':
+       s++;
+       break;
+
       default:
-       operand = decode_operand (s);
-       if (!operand)
+       if (!decode_operand)
+         operand = decode_mips16_operand (*s, FALSE);
+       else
+         operand = decode_operand (s);
+       if (!operand && opcode->pinfo != INSN_MACRO)
          {
            as_bad (_("internal: unknown operand type: %s %s"),
                    opcode->name, opcode->args);
            return 0;
          }
-       used_bits |= ((1 << operand->size) - 1) << operand->lsb;
-       if (operand->type == OP_MDMX_IMM_REG)
-         /* Bit 5 is the format selector (OB vs QH).  The opcode table
-            has separate entries for each format.  */
-         used_bits &= ~(1 << (operand->lsb + 5));
+       gas_assert (opno < MAX_OPERANDS);
+       operands->operand[opno] = operand;
+       if (operand && operand->type != OP_VU0_MATCH_SUFFIX)
+         {
+           used_bits = mips_insert_operand (operand, used_bits, -1);
+           if (operand->type == OP_MDMX_IMM_REG)
+             /* Bit 5 is the format selector (OB vs QH).  The opcode table
+                has separate entries for each format.  */
+             used_bits &= ~(1 << (operand->lsb + 5));
+           if (operand->type == OP_ENTRY_EXIT_LIST)
+             used_bits &= ~(mask & 0x700);
+         }
        /* Skip prefix characters.  */
-       if (*s == '+' || *s == 'm')
+       if (decode_operand && (*s == '+' || *s == 'm' || *s == '-'))
          ++s;
+       opno += 1;
        break;
       }
-  doubled = used_bits & opcode->mask & insn_bits;
+  doubled = used_bits & mask & insn_bits;
   if (doubled)
     {
       as_bad (_("internal: bad mips opcode (bits 0x%08lx doubly defined):"
                " %s %s"), doubled, opcode->name, opcode->args);
       return 0;
     }
-  used_bits |= opcode->mask;
+  used_bits |= mask;
   undefined = ~used_bits & insn_bits;
-  if (undefined)
+  if (opcode->pinfo != INSN_MACRO && undefined)
     {
       as_bad (_("internal: bad mips opcode (bits 0x%08lx undefined): %s %s"),
              undefined, opcode->name, opcode->args);
@@ -2904,19 +3392,44 @@ validate_mips_insn (const struct mips_opcode *opcode,
   return 1;
 }
 
+/* The MIPS16 version of validate_mips_insn.  */
+
+static int
+validate_mips16_insn (const struct mips_opcode *opcode,
+                     struct mips_operand_array *operands)
+{
+  if (opcode->args[0] == 'a' || opcode->args[0] == 'i')
+    {
+      /* In this case OPCODE defines the first 16 bits in a 32-bit jump
+        instruction.  Use TMP to describe the full instruction.  */
+      struct mips_opcode tmp;
+
+      tmp = *opcode;
+      tmp.match <<= 16;
+      tmp.mask <<= 16;
+      return validate_mips_insn (&tmp, 0xffffffff, 0, operands);
+    }
+  return validate_mips_insn (opcode, 0xffff, 0, operands);
+}
+
 /* The microMIPS version of validate_mips_insn.  */
 
 static int
-validate_micromips_insn (const struct mips_opcode *opc)
+validate_micromips_insn (const struct mips_opcode *opc,
+                        struct mips_operand_array *operands)
 {
   unsigned long insn_bits;
   unsigned long major;
   unsigned int length;
 
+  if (opc->pinfo == INSN_MACRO)
+    return validate_mips_insn (opc, 0xffffffff, decode_micromips_operand,
+                              operands);
+
   length = micromips_insn_length (opc);
   if (length != 2 && length != 4)
     {
-      as_bad (_("Internal error: bad microMIPS opcode (incorrect length: %u): "
+      as_bad (_("internal error: bad microMIPS opcode (incorrect length: %u): "
                "%s %s"), length, opc->name, opc->args);
       return 0;
     }
@@ -2924,7 +3437,7 @@ validate_micromips_insn (const struct mips_opcode *opc)
   if ((length == 2 && (major & 7) != 1 && (major & 6) != 2)
       || (length == 4 && (major & 7) != 0 && (major & 4) != 4))
     {
-      as_bad (_("Internal error: bad microMIPS opcode "
+      as_bad (_("internal error: bad microMIPS opcode "
                "(opcode/length mismatch): %s %s"), opc->name, opc->args);
       return 0;
     }
@@ -2933,7 +3446,8 @@ validate_micromips_insn (const struct mips_opcode *opc)
   insn_bits = 1 << 4 * length;
   insn_bits <<= 4 * length;
   insn_bits -= 1;
-  return validate_mips_insn (opc, insn_bits, decode_micromips_operand);
+  return validate_mips_insn (opc, insn_bits, decode_micromips_operand,
+                            operands);
 }
 
 /* This function is called once, at assembler startup time.  It should set up
@@ -2953,11 +3467,12 @@ md_begin (void)
       g_switch_value = 0;
     }
 
-  if (! bfd_set_arch_mach (stdoutput, bfd_arch_mips, file_mips_arch))
-    as_warn (_("Could not set architecture and machine"));
+  if (! bfd_set_arch_mach (stdoutput, bfd_arch_mips, file_mips_opts.arch))
+    as_warn (_("could not set architecture and machine"));
 
   op_hash = hash_new ();
 
+  mips_operands = XCNEWVEC (struct mips_operand_array, NUMOPCODES);
   for (i = 0; i < NUMOPCODES;)
     {
       const char *name = mips_opcodes[i].name;
@@ -2968,22 +3483,19 @@ md_begin (void)
          fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
                   mips_opcodes[i].name, retval);
          /* Probably a memory allocation problem?  Give up now.  */
-         as_fatal (_("Broken assembler.  No assembly attempted."));
+         as_fatal (_("broken assembler, no assembly attempted"));
        }
       do
        {
-         if (mips_opcodes[i].pinfo != INSN_MACRO)
+         if (!validate_mips_insn (&mips_opcodes[i], 0xffffffff,
+                                  decode_mips_operand, &mips_operands[i]))
+           broken = 1;
+         if (nop_insn.insn_mo == NULL && strcmp (name, "nop") == 0)
            {
-             if (!validate_mips_insn (&mips_opcodes[i], 0xffffffff,
-                                      decode_mips_operand))
-               broken = 1;
-             if (nop_insn.insn_mo == NULL && strcmp (name, "nop") == 0)
-               {
-                 create_insn (&nop_insn, mips_opcodes + i);
-                 if (mips_fix_loongson2f_nop)
-                   nop_insn.insn_opcode = LOONGSON2F_NOP_INSN;
-                 nop_insn.fixed_p = 1;
-               }
+             create_insn (&nop_insn, mips_opcodes + i);
+             if (mips_fix_loongson2f_nop)
+               nop_insn.insn_opcode = LOONGSON2F_NOP_INSN;
+             nop_insn.fixed_p = 1;
            }
          ++i;
        }
@@ -2991,6 +3503,8 @@ md_begin (void)
     }
 
   mips16_op_hash = hash_new ();
+  mips16_operands = XCNEWVEC (struct mips_operand_array,
+                             bfd_mips16_num_opcodes);
 
   i = 0;
   while (i < bfd_mips16_num_opcodes)
@@ -3003,14 +3517,8 @@ md_begin (void)
                  mips16_opcodes[i].name, retval);
       do
        {
-         if (mips16_opcodes[i].pinfo != INSN_MACRO
-             && ((mips16_opcodes[i].match & mips16_opcodes[i].mask)
-                 != mips16_opcodes[i].match))
-           {
-             fprintf (stderr, _("internal error: bad mips16 opcode: %s %s\n"),
-                      mips16_opcodes[i].name, mips16_opcodes[i].args);
-             broken = 1;
-           }
+         if (!validate_mips16_insn (&mips16_opcodes[i], &mips16_operands[i]))
+           broken = 1;
          if (mips16_nop_insn.insn_mo == NULL && strcmp (name, "nop") == 0)
            {
              create_insn (&mips16_nop_insn, mips16_opcodes + i);
@@ -3023,6 +3531,8 @@ md_begin (void)
     }
 
   micromips_op_hash = hash_new ();
+  micromips_operands = XCNEWVEC (struct mips_operand_array,
+                                bfd_micromips_num_opcodes);
 
   i = 0;
   while (i < bfd_micromips_num_opcodes)
@@ -3035,33 +3545,36 @@ md_begin (void)
        as_fatal (_("internal: can't hash `%s': %s"),
                  micromips_opcodes[i].name, retval);
       do
-        if (micromips_opcodes[i].pinfo != INSN_MACRO)
-          {
-            struct mips_cl_insn *micromips_nop_insn;
+       {
+         struct mips_cl_insn *micromips_nop_insn;
 
-            if (!validate_micromips_insn (&micromips_opcodes[i]))
-              broken = 1;
+         if (!validate_micromips_insn (&micromips_opcodes[i],
+                                       &micromips_operands[i]))
+           broken = 1;
 
-           if (micromips_insn_length (micromips_opcodes + i) == 2)
-             micromips_nop_insn = &micromips_nop16_insn;
-           else if (micromips_insn_length (micromips_opcodes + i) == 4)
-             micromips_nop_insn = &micromips_nop32_insn;
-           else
-             continue;
+         if (micromips_opcodes[i].pinfo != INSN_MACRO)
+           {
+             if (micromips_insn_length (micromips_opcodes + i) == 2)
+               micromips_nop_insn = &micromips_nop16_insn;
+             else if (micromips_insn_length (micromips_opcodes + i) == 4)
+               micromips_nop_insn = &micromips_nop32_insn;
+             else
+               continue;
 
-            if (micromips_nop_insn->insn_mo == NULL
-               && strcmp (name, "nop") == 0)
-              {
-                create_insn (micromips_nop_insn, micromips_opcodes + i);
-                micromips_nop_insn->fixed_p = 1;
-              }
-          }
+             if (micromips_nop_insn->insn_mo == NULL
+                 && strcmp (name, "nop") == 0)
+               {
+                 create_insn (micromips_nop_insn, micromips_opcodes + i);
+                 micromips_nop_insn->fixed_p = 1;
+               }
+           }
+       }
       while (++i < bfd_micromips_num_opcodes
             && strcmp (micromips_opcodes[i].name, name) == 0);
     }
 
   if (broken)
-    as_fatal (_("Broken assembler.  No assembly attempted."));
+    as_fatal (_("broken assembler, no assembly attempted"));
 
   /* We add all the general register names to the symbol table.  This
      helps us detect invalid uses of them.  */
@@ -3080,6 +3593,27 @@ md_begin (void)
                                       reg_names_o32[i].num, /* & RNUM_MASK, */
                                       &zero_address_frag));
 
+  for (i = 0; i < 32; i++)
+    {
+      char regname[7];
+
+      /* R5900 VU0 floating-point register.  */
+      regname[sizeof (rename) - 1] = 0;
+      snprintf (regname, sizeof (regname) - 1, "$vf%d", i);
+      symbol_table_insert (symbol_new (regname, reg_section,
+                                      RTYPE_VF | i, &zero_address_frag));
+
+      /* R5900 VU0 integer register.  */
+      snprintf (regname, sizeof (regname) - 1, "$vi%d", i);
+      symbol_table_insert (symbol_new (regname, reg_section,
+                                      RTYPE_VI | i, &zero_address_frag));
+
+      /* MSA register.  */
+      snprintf (regname, sizeof (regname) - 1, "$w%d", i);
+      symbol_table_insert (symbol_new (regname, reg_section,
+                                      RTYPE_MSA | i, &zero_address_frag));
+    }
+
   obstack_init (&mips_operand_tokens);
 
   mips_no_prev_insn ();
@@ -3159,6 +3693,12 @@ md_begin (void)
        }
       }
 
+    sec = subseg_new (".MIPS.abiflags", (subsegT) 0);
+    bfd_set_section_flags (stdoutput, sec,
+                          SEC_READONLY | SEC_DATA | SEC_ALLOC | SEC_LOAD);
+    bfd_set_section_alignment (stdoutput, sec, 3);
+    mips_flags_frag = frag_more (sizeof (Elf_External_ABIFlags_v0));
+
     if (ECOFF_DEBUGGING)
       {
        sec = subseg_new (".mdebug", (subsegT) 0);
@@ -3178,37 +3718,283 @@ md_begin (void)
     subseg_set (seg, subseg);
   }
 
-  if (! ECOFF_DEBUGGING)
-    md_obj_begin ();
-
   if (mips_fix_vr4120)
     init_vr4120_conflicts ();
 }
 
-void
-md_mips_end (void)
+static inline void
+fpabi_incompatible_with (int fpabi, const char *what)
 {
-  mips_emit_delays ();
-  if (! ECOFF_DEBUGGING)
-    md_obj_end ();
+  as_warn (_(".gnu_attribute %d,%d is incompatible with `%s'"),
+          Tag_GNU_MIPS_ABI_FP, fpabi, what);
 }
 
-void
-md_assemble (char *str)
+static inline void
+fpabi_requires (int fpabi, const char *what)
 {
-  struct mips_cl_insn insn;
-  bfd_reloc_code_real_type unused_reloc[3]
-    = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+  as_warn (_(".gnu_attribute %d,%d requires `%s'"),
+          Tag_GNU_MIPS_ABI_FP, fpabi, what);
+}
 
-  imm_expr.X_op = O_absent;
-  imm2_expr.X_op = O_absent;
-  offset_expr.X_op = O_absent;
-  offset_reloc[0] = BFD_RELOC_UNUSED;
-  offset_reloc[1] = BFD_RELOC_UNUSED;
+/* Check -mabi and register sizes against the specified FP ABI.  */
+static void
+check_fpabi (int fpabi)
+{
+  switch (fpabi)
+    {
+    case Val_GNU_MIPS_ABI_FP_DOUBLE:
+      if (file_mips_opts.soft_float)
+       fpabi_incompatible_with (fpabi, "softfloat");
+      else if (file_mips_opts.single_float)
+       fpabi_incompatible_with (fpabi, "singlefloat");
+      if (file_mips_opts.gp == 64 && file_mips_opts.fp == 32)
+       fpabi_incompatible_with (fpabi, "gp=64 fp=32");
+      else if (file_mips_opts.gp == 32 && file_mips_opts.fp == 64)
+       fpabi_incompatible_with (fpabi, "gp=32 fp=64");
+      break;
+
+    case Val_GNU_MIPS_ABI_FP_XX:
+      if (mips_abi != O32_ABI)
+       fpabi_requires (fpabi, "-mabi=32");
+      else if (file_mips_opts.soft_float)
+       fpabi_incompatible_with (fpabi, "softfloat");
+      else if (file_mips_opts.single_float)
+       fpabi_incompatible_with (fpabi, "singlefloat");
+      else if (file_mips_opts.fp != 0)
+       fpabi_requires (fpabi, "fp=xx");
+      break;
+
+    case Val_GNU_MIPS_ABI_FP_64A:
+    case Val_GNU_MIPS_ABI_FP_64:
+      if (mips_abi != O32_ABI)
+       fpabi_requires (fpabi, "-mabi=32");
+      else if (file_mips_opts.soft_float)
+       fpabi_incompatible_with (fpabi, "softfloat");
+      else if (file_mips_opts.single_float)
+       fpabi_incompatible_with (fpabi, "singlefloat");
+      else if (file_mips_opts.fp != 64)
+       fpabi_requires (fpabi, "fp=64");
+      else if (fpabi == Val_GNU_MIPS_ABI_FP_64 && !file_mips_opts.oddspreg)
+       fpabi_incompatible_with (fpabi, "nooddspreg");
+      else if (fpabi == Val_GNU_MIPS_ABI_FP_64A && file_mips_opts.oddspreg)
+       fpabi_requires (fpabi, "nooddspreg");
+      break;
+
+    case Val_GNU_MIPS_ABI_FP_SINGLE:
+      if (file_mips_opts.soft_float)
+       fpabi_incompatible_with (fpabi, "softfloat");
+      else if (!file_mips_opts.single_float)
+       fpabi_requires (fpabi, "singlefloat");
+      break;
+
+    case Val_GNU_MIPS_ABI_FP_SOFT:
+      if (!file_mips_opts.soft_float)
+       fpabi_requires (fpabi, "softfloat");
+      break;
+
+    case Val_GNU_MIPS_ABI_FP_OLD_64:
+      as_warn (_(".gnu_attribute %d,%d is no longer supported"),
+              Tag_GNU_MIPS_ABI_FP, fpabi);
+      break;
+
+    default:
+      as_warn (_(".gnu_attribute %d,%d is not a recognized"
+                " floating-point ABI"), Tag_GNU_MIPS_ABI_FP, fpabi);
+      break;
+    }
+}
+
+/* Perform consistency checks on the current options.  */
+
+static void
+mips_check_options (struct mips_set_options *opts, bfd_boolean abi_checks)
+{
+  /* Check the size of integer registers agrees with the ABI and ISA.  */
+  if (opts->gp == 64 && !ISA_HAS_64BIT_REGS (opts->isa))
+    as_bad (_("`gp=64' used with a 32-bit processor"));
+  else if (abi_checks
+          && opts->gp == 32 && ABI_NEEDS_64BIT_REGS (mips_abi))
+    as_bad (_("`gp=32' used with a 64-bit ABI"));
+  else if (abi_checks
+          && opts->gp == 64 && ABI_NEEDS_32BIT_REGS (mips_abi))
+    as_bad (_("`gp=64' used with a 32-bit ABI"));
+
+  /* Check the size of the float registers agrees with the ABI and ISA.  */
+  switch (opts->fp)
+    {
+    case 0:
+      if (!CPU_HAS_LDC1_SDC1 (opts->arch))
+       as_bad (_("`fp=xx' used with a cpu lacking ldc1/sdc1 instructions"));
+      else if (opts->single_float == 1)
+       as_bad (_("`fp=xx' cannot be used with `singlefloat'"));
+      break;
+    case 64:
+      if (!ISA_HAS_64BIT_FPRS (opts->isa))
+       as_bad (_("`fp=64' used with a 32-bit fpu"));
+      else if (abi_checks
+              && ABI_NEEDS_32BIT_REGS (mips_abi)
+              && !ISA_HAS_MXHC1 (opts->isa))
+       as_warn (_("`fp=64' used with a 32-bit ABI"));
+      break;
+    case 32:
+      if (abi_checks
+         && ABI_NEEDS_64BIT_REGS (mips_abi))
+       as_warn (_("`fp=32' used with a 64-bit ABI"));
+      if (ISA_IS_R6 (mips_opts.isa) && opts->single_float == 0)
+       as_bad (_("`fp=32' used with a MIPS R6 cpu"));
+      break;
+    default:
+      as_bad (_("Unknown size of floating point registers"));
+      break;
+    }
+
+  if (ABI_NEEDS_64BIT_REGS (mips_abi) && !opts->oddspreg)
+    as_bad (_("`nooddspreg` cannot be used with a 64-bit ABI"));
+
+  if (opts->micromips == 1 && opts->mips16 == 1)
+    as_bad (_("`mips16' cannot be used with `micromips'"));
+  else if (ISA_IS_R6 (mips_opts.isa)
+          && (opts->micromips == 1
+              || opts->mips16 == 1))
+    as_fatal (_("`%s' can not be used with `%s'"),
+             opts->micromips ? "micromips" : "mips16",
+             mips_cpu_info_from_isa (mips_opts.isa)->name);
+
+  if (ISA_IS_R6 (opts->isa) && mips_relax_branch)
+    as_fatal (_("branch relaxation is not supported in `%s'"),
+             mips_cpu_info_from_isa (opts->isa)->name);
+}
+
+/* Perform consistency checks on the module level options exactly once.
+   This is a deferred check that happens:
+     at the first .set directive
+     or, at the first pseudo op that generates code (inc .dc.a)
+     or, at the first instruction
+     or, at the end.  */
+
+static void
+file_mips_check_options (void)
+{
+  const struct mips_cpu_info *arch_info = 0;
+
+  if (file_mips_opts_checked)
+    return;
+
+  /* The following code determines the register size.
+     Similar code was added to GCC 3.3 (see override_options() in
+     config/mips/mips.c).  The GAS and GCC code should be kept in sync
+     as much as possible.  */
+
+  if (file_mips_opts.gp < 0)
+    {
+      /* Infer the integer register size from the ABI and processor.
+        Restrict ourselves to 32-bit registers if that's all the
+        processor has, or if the ABI cannot handle 64-bit registers.  */
+      file_mips_opts.gp = (ABI_NEEDS_32BIT_REGS (mips_abi)
+                          || !ISA_HAS_64BIT_REGS (file_mips_opts.isa))
+                         ? 32 : 64;
+    }
+
+  if (file_mips_opts.fp < 0)
+    {
+      /* No user specified float register size.
+        ??? GAS treats single-float processors as though they had 64-bit
+        float registers (although it complains when double-precision
+        instructions are used).  As things stand, saying they have 32-bit
+        registers would lead to spurious "register must be even" messages.
+        So here we assume float registers are never smaller than the
+        integer ones.  */
+      if (file_mips_opts.gp == 64)
+       /* 64-bit integer registers implies 64-bit float registers.  */
+       file_mips_opts.fp = 64;
+      else if ((file_mips_opts.ase & FP64_ASES)
+              && ISA_HAS_64BIT_FPRS (file_mips_opts.isa))
+       /* Handle ASEs that require 64-bit float registers, if possible.  */
+       file_mips_opts.fp = 64;
+      else if (ISA_IS_R6 (mips_opts.isa))
+       /* R6 implies 64-bit float registers.  */
+       file_mips_opts.fp = 64;
+      else
+       /* 32-bit float registers.  */
+       file_mips_opts.fp = 32;
+    }
+
+  arch_info = mips_cpu_info_from_arch (file_mips_opts.arch);
+
+  /* Disable operations on odd-numbered floating-point registers by default
+     when using the FPXX ABI.  */
+  if (file_mips_opts.oddspreg < 0)
+    {
+      if (file_mips_opts.fp == 0)
+       file_mips_opts.oddspreg = 0;
+      else
+       file_mips_opts.oddspreg = 1;
+    }
+
+  /* End of GCC-shared inference code.  */
+
+  /* This flag is set when we have a 64-bit capable CPU but use only
+     32-bit wide registers.  Note that EABI does not use it.  */
+  if (ISA_HAS_64BIT_REGS (file_mips_opts.isa)
+      && ((mips_abi == NO_ABI && file_mips_opts.gp == 32)
+         || mips_abi == O32_ABI))
+    mips_32bitmode = 1;
+
+  if (file_mips_opts.isa == ISA_MIPS1 && mips_trap)
+    as_bad (_("trap exception not supported at ISA 1"));
+
+  /* If the selected architecture includes support for ASEs, enable
+     generation of code for them.  */
+  if (file_mips_opts.mips16 == -1)
+    file_mips_opts.mips16 = (CPU_HAS_MIPS16 (file_mips_opts.arch)) ? 1 : 0;
+  if (file_mips_opts.micromips == -1)
+    file_mips_opts.micromips = (CPU_HAS_MICROMIPS (file_mips_opts.arch))
+                               ? 1 : 0;
+
+  if (mips_nan2008 == -1)
+    mips_nan2008 = (ISA_HAS_LEGACY_NAN (file_mips_opts.isa)) ? 0 : 1;
+  else if (!ISA_HAS_LEGACY_NAN (file_mips_opts.isa) && mips_nan2008 == 0)
+    as_fatal (_("`%s' does not support legacy NaN"),
+             mips_cpu_info_from_arch (file_mips_opts.arch)->name);
+
+  /* Some ASEs require 64-bit FPRs, so -mfp32 should stop those ASEs from
+     being selected implicitly.  */
+  if (file_mips_opts.fp != 64)
+    file_ase_explicit |= ASE_MIPS3D | ASE_MDMX | ASE_MSA;
+
+  /* If the user didn't explicitly select or deselect a particular ASE,
+     use the default setting for the CPU.  */
+  file_mips_opts.ase |= (arch_info->ase & ~file_ase_explicit);
+
+  /* Set up the current options.  These may change throughout assembly.  */
+  mips_opts = file_mips_opts;
+
+  mips_check_isa_supports_ases ();
+  mips_check_options (&file_mips_opts, TRUE);
+  file_mips_opts_checked = TRUE;
+
+  if (!bfd_set_arch_mach (stdoutput, bfd_arch_mips, file_mips_opts.arch))
+    as_warn (_("could not set architecture and machine"));
+}
+
+void
+md_assemble (char *str)
+{
+  struct mips_cl_insn insn;
+  bfd_reloc_code_real_type unused_reloc[3]
+    = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+
+  file_mips_check_options ();
+
+  imm_expr.X_op = O_absent;
+  offset_expr.X_op = O_absent;
+  offset_reloc[0] = BFD_RELOC_UNUSED;
+  offset_reloc[1] = BFD_RELOC_UNUSED;
   offset_reloc[2] = BFD_RELOC_UNUSED;
 
   mips_mark_labels ();
   mips_assembling_insn = TRUE;
+  clear_insn_error ();
 
   if (mips_opts.mips16)
     mips16_ip (str, &insn);
@@ -3219,8 +4005,8 @@ md_assemble (char *str)
            str, insn.insn_opcode));
     }
 
-  if (insn_error)
-    as_bad ("%s `%s'", insn_error, str);
+  if (insn_error.msg)
+    report_insn_error (str);
   else if (insn.insn_mo->pinfo == INSN_MACRO)
     {
       macro_start ();
@@ -3350,9 +4136,15 @@ limited_pcrel_reloc_p (bfd_reloc_code_real_type reloc)
     case BFD_RELOC_MICROMIPS_7_PCREL_S1:
     case BFD_RELOC_MICROMIPS_10_PCREL_S1:
     case BFD_RELOC_MICROMIPS_16_PCREL_S1:
+    case BFD_RELOC_MIPS_21_PCREL_S2:
+    case BFD_RELOC_MIPS_26_PCREL_S2:
+    case BFD_RELOC_MIPS_18_PCREL_S3:
+    case BFD_RELOC_MIPS_19_PCREL_S2:
       return TRUE;
 
     case BFD_RELOC_32_PCREL:
+    case BFD_RELOC_HI16_S_PCREL:
+    case BFD_RELOC_LO16_PCREL:
       return HAVE_64BIT_ADDRESSES;
 
     default:
@@ -3580,26 +4372,139 @@ get_delay_slot_nop (const struct mips_cl_insn *ip)
   return NOP_INSN;
 }
 
-/* Return the mask of core registers that IP reads or writes.  */
+/* Return a mask that has bit N set if OPCODE reads the register(s)
+   in operand N.  */
 
 static unsigned int
-gpr_mod_mask (const struct mips_cl_insn *ip)
+insn_read_mask (const struct mips_opcode *opcode)
 {
-  unsigned long pinfo2;
-  unsigned int mask;
+  return (opcode->pinfo & INSN_READ_ALL) >> INSN_READ_SHIFT;
+}
 
-  mask = 0;
-  pinfo2 = ip->insn_mo->pinfo2;
-  if (mips_opts.micromips)
+/* Return a mask that has bit N set if OPCODE writes to the register(s)
+   in operand N.  */
+
+static unsigned int
+insn_write_mask (const struct mips_opcode *opcode)
+{
+  return (opcode->pinfo & INSN_WRITE_ALL) >> INSN_WRITE_SHIFT;
+}
+
+/* Return a mask of the registers specified by operand OPERAND of INSN.
+   Ignore registers of type OP_REG_<t> unless bit OP_REG_<t> of TYPE_MASK
+   is set.  */
+
+static unsigned int
+operand_reg_mask (const struct mips_cl_insn *insn,
+                 const struct mips_operand *operand,
+                 unsigned int type_mask)
+{
+  unsigned int uval, vsel;
+
+  switch (operand->type)
+    {
+    case OP_INT:
+    case OP_MAPPED_INT:
+    case OP_MSB:
+    case OP_PCREL:
+    case OP_PERF_REG:
+    case OP_ADDIUSP_INT:
+    case OP_ENTRY_EXIT_LIST:
+    case OP_REPEAT_DEST_REG:
+    case OP_REPEAT_PREV_REG:
+    case OP_PC:
+    case OP_VU0_SUFFIX:
+    case OP_VU0_MATCH_SUFFIX:
+    case OP_IMM_INDEX:
+      abort ();
+
+    case OP_REG:
+    case OP_OPTIONAL_REG:
+      {
+       const struct mips_reg_operand *reg_op;
+
+       reg_op = (const struct mips_reg_operand *) operand;
+       if (!(type_mask & (1 << reg_op->reg_type)))
+         return 0;
+       uval = insn_extract_operand (insn, operand);
+       return 1 << mips_decode_reg_operand (reg_op, uval);
+      }
+
+    case OP_REG_PAIR:
+      {
+       const struct mips_reg_pair_operand *pair_op;
+
+       pair_op = (const struct mips_reg_pair_operand *) operand;
+       if (!(type_mask & (1 << pair_op->reg_type)))
+         return 0;
+       uval = insn_extract_operand (insn, operand);
+       return (1 << pair_op->reg1_map[uval]) | (1 << pair_op->reg2_map[uval]);
+      }
+
+    case OP_CLO_CLZ_DEST:
+      if (!(type_mask & (1 << OP_REG_GP)))
+       return 0;
+      uval = insn_extract_operand (insn, operand);
+      return (1 << (uval & 31)) | (1 << (uval >> 5));
+
+    case OP_SAME_RS_RT:
+      if (!(type_mask & (1 << OP_REG_GP)))
+       return 0;
+      uval = insn_extract_operand (insn, operand);
+      gas_assert ((uval & 31) == (uval >> 5));
+      return 1 << (uval & 31);
+
+    case OP_CHECK_PREV:
+    case OP_NON_ZERO_REG:
+      if (!(type_mask & (1 << OP_REG_GP)))
+       return 0;
+      uval = insn_extract_operand (insn, operand);
+      return 1 << (uval & 31);
+
+    case OP_LWM_SWM_LIST:
+      abort ();
+
+    case OP_SAVE_RESTORE_LIST:
+      abort ();
+
+    case OP_MDMX_IMM_REG:
+      if (!(type_mask & (1 << OP_REG_VEC)))
+       return 0;
+      uval = insn_extract_operand (insn, operand);
+      vsel = uval >> 5;
+      if ((vsel & 0x18) == 0x18)
+       return 0;
+      return 1 << (uval & 31);
+
+    case OP_REG_INDEX:
+      if (!(type_mask & (1 << OP_REG_GP)))
+       return 0;
+      return 1 << insn_extract_operand (insn, operand);
+    }
+  abort ();
+}
+
+/* Return a mask of the registers specified by operands OPNO_MASK of INSN,
+   where bit N of OPNO_MASK is set if operand N should be included.
+   Ignore registers of type OP_REG_<t> unless bit OP_REG_<t> of TYPE_MASK
+   is set.  */
+
+static unsigned int
+insn_reg_mask (const struct mips_cl_insn *insn,
+              unsigned int type_mask, unsigned int opno_mask)
+{
+  unsigned int opno, reg_mask;
+
+  opno = 0;
+  reg_mask = 0;
+  while (opno_mask != 0)
     {
-      if (pinfo2 & INSN2_MOD_GPR_MD)
-       mask |= 1 << micromips_to_32_reg_d_map[EXTRACT_OPERAND (1, MD, *ip)];
-      if (pinfo2 & INSN2_MOD_GPR_MF)
-       mask |= 1 << micromips_to_32_reg_f_map[EXTRACT_OPERAND (1, MF, *ip)];
+      if (opno_mask & 1)
+       reg_mask |= operand_reg_mask (insn, insn_opno (insn, opno), type_mask);
+      opno_mask >>= 1;
+      opno += 1;
     }
-  if (pinfo2 & INSN2_MOD_SP)
-    mask |= 1 << SP;
-  return mask;
+  return reg_mask;
 }
 
 /* Return the mask of core registers that IP reads.  */
@@ -3610,60 +4515,24 @@ gpr_read_mask (const struct mips_cl_insn *ip)
   unsigned long pinfo, pinfo2;
   unsigned int mask;
 
-  mask = gpr_mod_mask (ip);
+  mask = insn_reg_mask (ip, 1 << OP_REG_GP, insn_read_mask (ip->insn_mo));
   pinfo = ip->insn_mo->pinfo;
   pinfo2 = ip->insn_mo->pinfo2;
-  if (mips_opts.mips16)
-    {
-      if (pinfo & MIPS16_INSN_READ_X)
-       mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, *ip)];
-      if (pinfo & MIPS16_INSN_READ_Y)
-       mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RY, *ip)];
-      if (pinfo & MIPS16_INSN_READ_T)
-       mask |= 1 << TREG;
-      if (pinfo & MIPS16_INSN_READ_SP)
-       mask |= 1 << SP;
-      if (pinfo & MIPS16_INSN_READ_Z)
-       mask |= 1 << (mips16_to_32_reg_map
-                     [MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip)]);
-      if (pinfo & MIPS16_INSN_READ_GPR_X)
-       mask |= 1 << MIPS16_EXTRACT_OPERAND (REGR32, *ip);
-    }
-  else
-    {
-      if (pinfo2 & INSN2_READ_GPR_D)
-       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RD, *ip);
-      if (pinfo & INSN_READ_GPR_T)
-       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RT, *ip);
-      if (pinfo & INSN_READ_GPR_S)
-       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RS, *ip);
-      if (pinfo2 & INSN2_READ_GP)
-       mask |= 1 << GP;
-      if (pinfo2 & INSN2_READ_GPR_Z)
-       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RZ, *ip);
-    }
+  if (pinfo & INSN_UDI)
+    {
+      /* UDI instructions have traditionally been assumed to read RS
+        and RT.  */
+      mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RT, *ip);
+      mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RS, *ip);
+    }
+  if (pinfo & INSN_READ_GPR_24)
+    mask |= 1 << 24;
+  if (pinfo2 & INSN2_READ_GPR_16)
+    mask |= 1 << 16;
+  if (pinfo2 & INSN2_READ_SP)
+    mask |= 1 << SP;
   if (pinfo2 & INSN2_READ_GPR_31)
-    mask |= 1 << RA;
-  if (mips_opts.micromips)
-    {
-      if (pinfo2 & INSN2_READ_GPR_MC)
-       mask |= 1 << micromips_to_32_reg_c_map[EXTRACT_OPERAND (1, MC, *ip)];
-      if (pinfo2 & INSN2_READ_GPR_ME)
-       mask |= 1 << micromips_to_32_reg_e_map[EXTRACT_OPERAND (1, ME, *ip)];
-      if (pinfo2 & INSN2_READ_GPR_MG)
-       mask |= 1 << micromips_to_32_reg_g_map[EXTRACT_OPERAND (1, MG, *ip)];
-      if (pinfo2 & INSN2_READ_GPR_MJ)
-       mask |= 1 << EXTRACT_OPERAND (1, MJ, *ip);
-      if (pinfo2 & INSN2_READ_GPR_MMN)
-       {
-         mask |= 1 << micromips_to_32_reg_m_map[EXTRACT_OPERAND (1, MM, *ip)];
-         mask |= 1 << micromips_to_32_reg_n_map[EXTRACT_OPERAND (1, MN, *ip)];
-       }
-      if (pinfo2 & INSN2_READ_GPR_MP)
-       mask |= 1 << EXTRACT_OPERAND (1, MP, *ip);
-      if (pinfo2 & INSN2_READ_GPR_MQ)
-       mask |= 1 << micromips_to_32_reg_q_map[EXTRACT_OPERAND (1, MQ, *ip)];
-    }
+    mask |= 1 << 31;
   /* Don't include register 0.  */
   return mask & ~1;
 }
@@ -3676,51 +4545,18 @@ gpr_write_mask (const struct mips_cl_insn *ip)
   unsigned long pinfo, pinfo2;
   unsigned int mask;
 
-  mask = gpr_mod_mask (ip);
+  mask = insn_reg_mask (ip, 1 << OP_REG_GP, insn_write_mask (ip->insn_mo));
   pinfo = ip->insn_mo->pinfo;
   pinfo2 = ip->insn_mo->pinfo2;
-  if (mips_opts.mips16)
-    {
-      if (pinfo & MIPS16_INSN_WRITE_X)
-       mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, *ip)];
-      if (pinfo & MIPS16_INSN_WRITE_Y)
-       mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RY, *ip)];
-      if (pinfo & MIPS16_INSN_WRITE_Z)
-       mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RZ, *ip)];
-      if (pinfo & MIPS16_INSN_WRITE_T)
-       mask |= 1 << TREG;
-      if (pinfo & MIPS16_INSN_WRITE_31)
-       mask |= 1 << RA;
-      if (pinfo & MIPS16_INSN_WRITE_GPR_Y)
-       mask |= 1 << MIPS16OP_EXTRACT_REG32R (ip->insn_opcode);
-    }
-  else
-    {
-      if (pinfo & INSN_WRITE_GPR_D)
-       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RD, *ip);
-      if (pinfo & INSN_WRITE_GPR_T)
-       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RT, *ip);
-      if (pinfo & INSN_WRITE_GPR_S)
-       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RS, *ip);
-      if (pinfo & INSN_WRITE_GPR_31)
-       mask |= 1 << RA;
-      if (pinfo2 & INSN2_WRITE_GPR_Z)
-       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RZ, *ip);
-    }
-  if (mips_opts.micromips)
-    {
-      if (pinfo2 & INSN2_WRITE_GPR_MB)
-       mask |= 1 << micromips_to_32_reg_b_map[EXTRACT_OPERAND (1, MB, *ip)];
-      if (pinfo2 & INSN2_WRITE_GPR_MH)
-       {
-         mask |= 1 << micromips_to_32_reg_h_map1[EXTRACT_OPERAND (1, MH, *ip)];
-         mask |= 1 << micromips_to_32_reg_h_map2[EXTRACT_OPERAND (1, MH, *ip)];
-       }
-      if (pinfo2 & INSN2_WRITE_GPR_MJ)
-       mask |= 1 << EXTRACT_OPERAND (1, MJ, *ip);
-      if (pinfo2 & INSN2_WRITE_GPR_MP)
-       mask |= 1 << EXTRACT_OPERAND (1, MP, *ip);
-    }
+  if (pinfo & INSN_WRITE_GPR_24)
+    mask |= 1 << 24;
+  if (pinfo & INSN_WRITE_GPR_31)
+    mask |= 1 << 31;
+  if (pinfo & INSN_UDI)
+    /* UDI instructions have traditionally been assumed to write to RD.  */
+    mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RD, *ip);
+  if (pinfo2 & INSN2_WRITE_SP)
+    mask |= 1 << SP;
   /* Don't include register 0.  */
   return mask & ~1;
 }
@@ -3730,28 +4566,16 @@ gpr_write_mask (const struct mips_cl_insn *ip)
 static unsigned int
 fpr_read_mask (const struct mips_cl_insn *ip)
 {
-  unsigned long pinfo, pinfo2;
+  unsigned long pinfo;
   unsigned int mask;
 
-  mask = 0;
+  mask = insn_reg_mask (ip, ((1 << OP_REG_FP) | (1 << OP_REG_VEC)
+                            | (1 << OP_REG_MSA)),
+                       insn_read_mask (ip->insn_mo));
   pinfo = ip->insn_mo->pinfo;
-  pinfo2 = ip->insn_mo->pinfo2;
-  if (!mips_opts.mips16)
-    {
-      if (pinfo2 & INSN2_READ_FPR_D)
-       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FD, *ip);
-      if (pinfo & INSN_READ_FPR_S)
-       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FS, *ip);
-      if (pinfo & INSN_READ_FPR_T)
-       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FT, *ip);
-      if (pinfo & INSN_READ_FPR_R)
-       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FR, *ip);
-      if (pinfo2 & INSN2_READ_FPR_Z)
-       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FZ, *ip);
-    }
   /* Conservatively treat all operands to an FP_D instruction are doubles.
      (This is overly pessimistic for things like cvt.d.s.)  */
-  if (HAVE_32BIT_FPRS && (pinfo & FP_D))
+  if (FPR_SIZE != 64 && (pinfo & FP_D))
     mask |= mask << 1;
   return mask;
 }
@@ -3761,26 +4585,16 @@ fpr_read_mask (const struct mips_cl_insn *ip)
 static unsigned int
 fpr_write_mask (const struct mips_cl_insn *ip)
 {
-  unsigned long pinfo, pinfo2;
+  unsigned long pinfo;
   unsigned int mask;
 
-  mask = 0;
+  mask = insn_reg_mask (ip, ((1 << OP_REG_FP) | (1 << OP_REG_VEC)
+                            | (1 << OP_REG_MSA)),
+                       insn_write_mask (ip->insn_mo));
   pinfo = ip->insn_mo->pinfo;
-  pinfo2 = ip->insn_mo->pinfo2;
-  if (!mips_opts.mips16)
-    {
-      if (pinfo & INSN_WRITE_FPR_D)
-       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FD, *ip);
-      if (pinfo & INSN_WRITE_FPR_S)
-       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FS, *ip);
-      if (pinfo & INSN_WRITE_FPR_T)
-       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FT, *ip);
-      if (pinfo2 & INSN2_WRITE_FPR_Z)
-       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FZ, *ip);
-    }
   /* Conservatively treat all operands to an FP_D instruction are doubles.
      (This is overly pessimistic for things like cvt.s.d.)  */
-  if (HAVE_32BIT_FPRS && (pinfo & FP_D))
+  if (FPR_SIZE != 64 && (pinfo & FP_D))
     mask |= mask << 1;
   return mask;
 }
@@ -3792,72 +4606,42 @@ static bfd_boolean
 mips_oddfpreg_ok (const struct mips_opcode *insn, int opnum)
 {
   const char *s = insn->name;
+  bfd_boolean oddspreg = (ISA_HAS_ODD_SINGLE_FPR (mips_opts.isa, mips_opts.arch)
+                         || FPR_SIZE == 64)
+                        && mips_opts.oddspreg;
 
   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.  */
+  /* Single-precision coprocessor loads and moves are OK for 32-bit registers,
+     otherwise it depends on oddspreg.  */
   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;
-}
-
-/* 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.  */
+      && (insn->pinfo & (INSN_LOAD_MEMORY | INSN_STORE_MEMORY
+                        | INSN_LOAD_COPROC | INSN_COPROC_MOVE)))
+    return FPR_SIZE == 32 || oddspreg;
 
-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);
-}
+  /* Allow odd registers for single-precision ops and double-precision if the
+     floating-point registers are 64-bit wide.  */
+  switch (insn->pinfo & (FP_S | FP_D))
+    {
+    case FP_S:
+    case 0:
+      return oddspreg;
+    case FP_D:
+      return FPR_SIZE == 64;
+    default:
+      break;
+    }
 
-/* Report an invalid combination of position and size operands for a bitfield
-   operation.  POS and SIZE are the values that were given.  */
+  /* 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, '.');
+  if (s != NULL && (s[1] == 'w' || s[1] == 's'))
+    return oddspreg;
 
-static void
-report_bad_field (offsetT pos, offsetT size)
-{
-  as_bad (_("Invalid field specification (position %ld, size %ld)"),
-         (unsigned long) pos, (unsigned long) size);
+  return FPR_SIZE == 64;
 }
 
 /* Information about an instruction argument that we're trying to match.  */
@@ -3887,26 +4671,33 @@ struct mips_arg_info
      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;
+  /* If true, match routines should assume that no later instruction
+     alternative matches and should therefore be as accomodating as
+     possible.  Match routines should not report errors if something
+     is only invalid for !LAX_MATCH.  */
+  bfd_boolean lax_match;
 
   /* True if a reference to the current AT register was seen.  */
   bfd_boolean seen_at;
 };
 
+/* Record that the argument is out of range.  */
+
+static void
+match_out_of_range (struct mips_arg_info *arg)
+{
+  set_insn_error_i (arg->argnum, _("operand %d out of range"), arg->argnum);
+}
+
+/* Record that the argument isn't constant but needs to be.  */
+
+static void
+match_not_constant (struct mips_arg_info *arg)
+{
+  set_insn_error_i (arg->argnum, _("operand %d must be constant"),
+                   arg->argnum);
+}
+
 /* Try to match an OT_CHAR token for character CH.  Consume the token
    and return true on success, otherwise return false.  */
 
@@ -3931,37 +4722,38 @@ static bfd_boolean
 match_expression (struct mips_arg_info *arg, expressionS *value,
                  bfd_reloc_code_real_type *r)
 {
-  if (arg->token->type == OT_INTEGER)
+  /* If the next token is a '(' that was parsed as being part of a base
+     expression, assume we have an elided offset.  The later match will fail
+     if this turns out to be wrong.  */
+  if (arg->token->type == OT_CHAR && arg->token->u.ch == '(')
     {
-      *value = arg->token->u.integer.value;
-      memcpy (r, arg->token->u.integer.relocs, 3 * sizeof (*r));
-      ++arg->token;
+      value->X_op = O_constant;
+      value->X_add_number = 0;
+      r[0] = r[1] = r[2] = BFD_RELOC_UNUSED;
       return TRUE;
     }
 
-  /* Error-reporting is more consistent if we treat registers as O_register
-     rather than rejecting them outright.  "$1", "($1)" and "(($1))" are
-     then handled in the same way.  */
-  if (arg->token->type == OT_REG)
+  /* Reject register-based expressions such as "0+$2" and "(($2))".
+     For plain registers the default error seems more appropriate.  */
+  if (arg->token->type == OT_INTEGER
+      && arg->token->u.integer.value.X_op == O_register)
     {
-      value->X_add_number = arg->token->u.regno;
-      ++arg->token;
+      set_insn_error (arg->argnum, _("register value used as expression"));
+      return FALSE;
     }
-  else if (arg->token[0].type == OT_CHAR
-          && arg->token[0].u.ch == '('
-          && arg->token[1].type == OT_REG
-          && arg->token[2].type == OT_CHAR
-          && arg->token[2].u.ch == ')')
+
+  if (arg->token->type == OT_INTEGER)
     {
-      value->X_add_number = arg->token[1].u.regno;
-      arg->token += 3;
+      *value = arg->token->u.integer.value;
+      memcpy (r, arg->token->u.integer.relocs, 3 * sizeof (*r));
+      ++arg->token;
+      return TRUE;
     }
-  else
-    return FALSE;
 
-  value->X_op = O_register;
-  r[0] = r[1] = r[2] = BFD_RELOC_UNUSED;
-  return TRUE;
+  set_insn_error_i
+    (arg->argnum, _("operand %d must be an immediate expression"),
+     arg->argnum);
+  return FALSE;
 }
 
 /* Try to get a constant expression from the next tokens in ARG.  Consume
@@ -3970,7 +4762,7 @@ match_expression (struct mips_arg_info *arg, expressionS *value,
    error.  */
 
 static bfd_boolean
-match_const_int (struct mips_arg_info *arg, offsetT *value, offsetT fallback)
+match_const_int (struct mips_arg_info *arg, offsetT *value)
 {
   expressionS ex;
   bfd_reloc_code_real_type r[3];
@@ -3982,11 +4774,8 @@ match_const_int (struct mips_arg_info *arg, offsetT *value, offsetT fallback)
     *value = ex.X_add_number;
   else
     {
-      if (arg->soft_match)
-       return FALSE;
-      as_bad (_("Operand %d of `%s' must be constant"),
-             arg->argnum, arg->insn->insn_mo->name);
-      *value = fallback;
+      match_not_constant (arg);
+      return FALSE;
     }
   return TRUE;
 }
@@ -4008,10 +4797,10 @@ convert_reg_type (const struct mips_opcode *opcode,
         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
+         && (opcode->pinfo & (INSN_COPROC_MOVE
                               | INSN_COPROC_MEMORY_DELAY
-                              | INSN_LOAD_COPROC_DELAY
-                              | INSN_LOAD_MEMORY_DELAY
+                              | INSN_LOAD_COPROC
+                              | INSN_LOAD_MEMORY
                               | INSN_STORE_MEMORY)))
        return RTYPE_FPU | RTYPE_VEC;
       return RTYPE_FPU;
@@ -4036,6 +4825,30 @@ convert_reg_type (const struct mips_opcode *opcode,
 
     case OP_REG_HW:
       return RTYPE_NUM;
+
+    case OP_REG_VI:
+      return RTYPE_NUM | RTYPE_VI;
+
+    case OP_REG_VF:
+      return RTYPE_NUM | RTYPE_VF;
+
+    case OP_REG_R5900_I:
+      return RTYPE_R5900_I;
+
+    case OP_REG_R5900_Q:
+      return RTYPE_R5900_Q;
+
+    case OP_REG_R5900_R:
+      return RTYPE_R5900_R;
+
+    case OP_REG_R5900_ACC:
+      return RTYPE_R5900_ACC;
+
+    case OP_REG_MSA:
+      return RTYPE_MSA;
+
+    case OP_REG_MSA_CTRL:
+      return RTYPE_NUM;
     }
   abort ();
 }
@@ -4051,9 +4864,16 @@ check_regno (struct mips_arg_info *arg,
 
   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);
+    {
+      /* This was a warning prior to introducing O32 FPXX and FP64 support
+        so maintain a warning for FP32 but raise an error for the new
+        cases.  */
+      if (FPR_SIZE == 32)
+       as_warn (_("float register should be even, was %d"), regno);
+      else
+       as_bad (_("float register should be even, was %d"), regno);
+    }
 
   if (type == OP_REG_CCC)
     {
@@ -4065,12 +4885,12 @@ check_regno (struct mips_arg_info *arg,
       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"),
+       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"),
+       as_warn (_("condition code register should be 0 or 4 for %s, was %d"),
                 name, regno);
     }
 }
@@ -4141,28 +4961,20 @@ match_int_operand (struct mips_arg_info *arg,
                   const struct mips_operand *operand_base)
 {
   const struct mips_int_operand *operand;
-  unsigned int uval, mask;
+  unsigned int uval;
   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;
+  min_val = mips_int_operand_min (operand);
+  max_val = mips_int_operand_max (operand);
 
-  if (arg->token->type == OT_CHAR && arg->token->u.ch == '(')
-    /* Assume we have an elided offset.  The later match will fail
-       if this turns out to be wrong.  */
-    sval = 0;
-  else if (operand_base->lsb == 0
-          && operand_base->size == 16
-          && operand->shift == 0
-          && operand->bias == 0
-          && (operand->max_val == 32767 || operand->max_val == 65535))
+  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.  */
       if (!match_expression (arg, &offset_expr, offset_reloc))
@@ -4176,9 +4988,9 @@ match_int_operand (struct mips_arg_info *arg,
 
       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.  */
-         if (!arg->allow_nonconst)
+         /* Accept non-constant operands if no later alternative matches,
+            leaving it for the caller to process.  */
+         if (!arg->lax_match)
            return FALSE;
          offset_reloc[0] = BFD_RELOC_LO16;
          return TRUE;
@@ -4188,41 +5000,29 @@ match_int_operand (struct mips_arg_info *arg,
         ourselves.  */
       sval = offset_expr.X_add_number;
       offset_expr.X_op = O_absent;
+
+      /* For compatibility with older assemblers, we accept
+        0x8000-0xffff as signed 16-bit numbers when only
+        signed numbers are allowed.  */
+      if (sval > max_val)
+       {
+         max_val = ((1 << operand_base->size) - 1) << operand->shift;
+         if (!arg->lax_match && sval <= max_val)
+           return FALSE;
+       }
     }
   else
     {
-      if (!match_const_int (arg, &sval, min_val))
+      if (!match_const_int (arg, &sval))
        return FALSE;
     }
 
   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 FALSE;
-      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 (sval < min_val || sval > max_val || sval % factor)
     {
-      if (arg->soft_match)
-       return FALSE;
-      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;
+      match_out_of_range (arg);
+      return FALSE;
     }
 
   uval = (unsigned int) sval >> operand->shift;
@@ -4267,7 +5067,7 @@ match_mapped_int_operand (struct mips_arg_info *arg,
   offsetT sval;
 
   operand = (const struct mips_mapped_int_operand *) operand_base;
-  if (!match_const_int (arg, &sval, operand->int_map[0]))
+  if (!match_const_int (arg, &sval))
     return FALSE;
 
   num_vals = 1 << operand_base->size;
@@ -4275,7 +5075,10 @@ match_mapped_int_operand (struct mips_arg_info *arg,
     if (operand->int_map[uval] == sval)
       break;
   if (uval == num_vals)
-    return FALSE;
+    {
+      match_out_of_range (arg);
+      return FALSE;
+    }
 
   insn_insert_operand (arg->insn, operand_base, uval);
   return TRUE;
@@ -4296,7 +5099,7 @@ match_msb_operand (struct mips_arg_info *arg,
   max_val = min_val + (1 << operand_base->size) - 1;
   max_high = operand->opsize;
 
-  if (!match_const_int (arg, &size, 1))
+  if (!match_const_int (arg, &size))
     return FALSE;
 
   high = size + arg->last_op_int;
@@ -4304,10 +5107,8 @@ match_msb_operand (struct mips_arg_info *arg,
 
   if (size < 0 || high > max_high || sval < min_val || sval > max_val)
     {
-      if (arg->soft_match)
-       return FALSE;
-      report_bad_field (arg->last_op_int, size);
-      sval = min_val;
+      match_out_of_range (arg);
+      return FALSE;
     }
   insn_insert_operand (arg->insn, operand_base, sval - min_val);
   return TRUE;
@@ -4389,7 +5190,7 @@ match_perf_reg_operand (struct mips_arg_info *arg,
 {
   offsetT sval;
 
-  if (!match_const_int (arg, &sval, 0))
+  if (!match_const_int (arg, &sval))
     return FALSE;
 
   if (sval != 0
@@ -4398,9 +5199,8 @@ match_perf_reg_operand (struct mips_arg_info *arg,
              && (strcmp (arg->insn->insn_mo->name, "mfps") == 0
                  || strcmp (arg->insn->insn_mo->name, "mtps") == 0))))
     {
-      if (arg->soft_match)
-       return FALSE;
-      as_bad (_("Invalid performance register (%ld)"), (unsigned long) sval);
+      set_insn_error (arg->argnum, _("invalid performance register"));
+      return FALSE;
     }
 
   insn_insert_operand (arg->insn, operand, sval);
@@ -4416,15 +5216,21 @@ match_addiusp_operand (struct mips_arg_info *arg,
   offsetT sval;
   unsigned int uval;
 
-  if (!match_const_int (arg, &sval, -256))
+  if (!match_const_int (arg, &sval))
     return FALSE;
 
   if (sval % 4)
-    return FALSE;
+    {
+      match_out_of_range (arg);
+      return FALSE;
+    }
 
   sval /= 4;
   if (!(sval >= -258 && sval <= 257) || (sval >= -2 && sval <= 1))
-    return FALSE;
+    {
+      match_out_of_range (arg);
+      return FALSE;
+    }
 
   uval = (unsigned int) sval;
   uval = ((uval >> 1) & ~0xff) | (uval & 0xff);
@@ -4447,6 +5253,58 @@ match_clo_clz_dest_operand (struct mips_arg_info *arg,
   return TRUE;
 }
 
+/* OP_CHECK_PREV matcher.  */
+
+static bfd_boolean
+match_check_prev_operand (struct mips_arg_info *arg,
+                         const struct mips_operand *operand_base)
+{
+  const struct mips_check_prev_operand *operand;
+  unsigned int regno;
+
+  operand = (const struct mips_check_prev_operand *) operand_base;
+
+  if (!match_reg (arg, OP_REG_GP, &regno))
+    return FALSE;
+
+  if (!operand->zero_ok && regno == 0)
+    return FALSE;
+
+  if ((operand->less_than_ok && regno < arg->last_regno)
+      || (operand->greater_than_ok && regno > arg->last_regno)
+      || (operand->equal_ok && regno == arg->last_regno))
+    {
+      arg->last_regno = regno;
+      insn_insert_operand (arg->insn, operand_base, regno);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+/* OP_SAME_RS_RT matcher.  */
+
+static bfd_boolean
+match_same_rs_rt_operand (struct mips_arg_info *arg,
+                         const struct mips_operand *operand)
+{
+  unsigned int regno;
+
+  if (!match_reg (arg, OP_REG_GP, &regno))
+    return FALSE;
+
+  if (regno == 0)
+    {
+      set_insn_error (arg->argnum, _("the source register must not be $0"));
+      return FALSE;
+    }
+
+  arg->last_regno = regno;
+
+  insn_insert_operand (arg->insn, operand, regno | (regno << 5));
+  return TRUE;
+}
+
 /* OP_LWM_SWM_LIST matcher.  */
 
 static bfd_boolean
@@ -4568,9 +5426,7 @@ match_save_restore_list_operand (struct mips_arg_info *arg)
   unsigned int opcode, args, statics, sregs;
   unsigned int num_frame_sizes, num_args, num_statics, num_sregs;
   offsetT frame_size;
-  const char *error;
 
-  error = 0;
   opcode = arg->insn->insn_opcode;
   frame_size = 0;
   num_frame_sizes = 0;
@@ -4584,7 +5440,7 @@ match_save_restore_list_operand (struct mips_arg_info *arg)
       if (arg->token->type == OT_INTEGER)
        {
          /* Handle the frame size.  */
-         if (!match_const_int (arg, &frame_size, 0))
+         if (!match_const_int (arg, &frame_size))
            return FALSE;
          num_frame_sizes += 1;
        }
@@ -4678,25 +5534,27 @@ match_save_restore_list_operand (struct mips_arg_info *arg)
 
   /* Encode frame size.  */
   if (num_frame_sizes == 0)
-    error = _("Missing frame size");
-  else if (num_frame_sizes > 1)
-    error = _("Frame size specified twice");
-  else if ((frame_size & 7) != 0 || frame_size < 0 || frame_size > 0xff * 8)
-    error = _("Invalid frame size");
-  else if (frame_size != 128 || (opcode >> 16) != 0)
+    {
+      set_insn_error (arg->argnum, _("missing frame size"));
+      return FALSE;
+    }
+  if (num_frame_sizes > 1)
+    {
+      set_insn_error (arg->argnum, _("frame size specified twice"));
+      return FALSE;
+    }
+  if ((frame_size & 7) != 0 || frame_size < 0 || frame_size > 0xff * 8)
+    {
+      set_insn_error (arg->argnum, _("invalid frame size"));
+      return FALSE;
+    }
+  if (frame_size != 128 || (opcode >> 16) != 0)
     {
       frame_size /= 8;
       opcode |= (((frame_size & 0xf0) << 16)
                 | (frame_size & 0x0f));
     }
 
-  if (error)
-    {
-      if (arg->soft_match)
-       return FALSE;
-      as_bad ("%s", error);
-    }
-
   /* Finally build the instruction.  */
   if ((opcode >> 16) != 0 || frame_size == 0)
     opcode |= MIPS16_EXTEND;
@@ -4720,32 +5578,31 @@ match_mdmx_imm_reg_operand (struct mips_arg_info *arg,
   uval = mips_extract_operand (operand, opcode->match);
   is_qh = (uval != 0);
 
-  if (arg->token->type == OT_REG || arg->token->type == OT_REG_ELEMENT)
+  if (arg->token->type == OT_REG)
     {
       if ((opcode->membership & INSN_5400)
          && strcmp (opcode->name, "rzu.ob") == 0)
        {
-         if (arg->soft_match)
-           return FALSE;
-         as_bad (_("Operand %d of `%s' must be an immediate"),
-                 arg->argnum, opcode->name);
+         set_insn_error_i (arg->argnum, _("operand %d must be an immediate"),
+                           arg->argnum);
+         return FALSE;
        }
 
+      if (!match_regno (arg, OP_REG_VEC, arg->token->u.regno, &regno))
+       return FALSE;
+      ++arg->token;
+
       /* Check whether this is a vector register or a broadcast of
         a single element.  */
-      if (arg->token->type == OT_REG_ELEMENT)
+      if (arg->token->type == OT_INTEGER_INDEX)
        {
-         if (!match_regno (arg, OP_REG_VEC, arg->token->u.reg_element.regno,
-                           &regno))
-           return FALSE;
-         if (arg->token->u.reg_element.index > (is_qh ? 3 : 7))
+         if (arg->token->u.index > (is_qh ? 3 : 7))
            {
-             if (arg->soft_match)
-               return FALSE;
-             as_bad (_("Invalid element selector"));
+             set_insn_error (arg->argnum, _("invalid element selector"));
+             return FALSE;
            }
-         else
-           uval |= arg->token->u.reg_element.index << (is_qh ? 2 : 1) << 5;
+         uval |= arg->token->u.index << (is_qh ? 2 : 1) << 5;
+         ++arg->token;
        }
       else
        {
@@ -4754,33 +5611,28 @@ match_mdmx_imm_reg_operand (struct mips_arg_info *arg,
              && (strcmp (opcode->name, "sll.ob") == 0
                  || strcmp (opcode->name, "srl.ob") == 0))
            {
-             if (arg->soft_match)
-               return FALSE;
-             as_bad (_("Operand %d of `%s' must be scalar"),
-                     arg->argnum, opcode->name);
+             set_insn_error_i (arg->argnum, _("operand %d must be scalar"),
+                               arg->argnum);
+             return FALSE;
            }
 
-         if (!match_regno (arg, OP_REG_VEC, arg->token->u.regno, &regno))
-           return FALSE;
          if (is_qh)
            uval |= MDMX_FMTSEL_VEC_QH << 5;
          else
            uval |= MDMX_FMTSEL_VEC_OB << 5;
        }
       uval |= regno;
-      ++arg->token;
     }
   else
     {
       offsetT sval;
 
-      if (!match_const_int (arg, &sval, 0))
+      if (!match_const_int (arg, &sval))
        return FALSE;
       if (sval < 0 || sval > 31)
        {
-         if (arg->soft_match)
-           return FALSE;
-         report_bad_range (arg->insn, arg->argnum, sval, 0, 31, FALSE);
+         match_out_of_range (arg);
+         return FALSE;
        }
       uval |= (sval & 31);
       if (is_qh)
@@ -4792,6 +5644,47 @@ match_mdmx_imm_reg_operand (struct mips_arg_info *arg,
   return TRUE;
 }
 
+/* OP_IMM_INDEX matcher.  */
+
+static bfd_boolean
+match_imm_index_operand (struct mips_arg_info *arg,
+                        const struct mips_operand *operand)
+{
+  unsigned int max_val;
+
+  if (arg->token->type != OT_INTEGER_INDEX)
+    return FALSE;
+
+  max_val = (1 << operand->size) - 1;
+  if (arg->token->u.index > max_val)
+    {
+      match_out_of_range (arg);
+      return FALSE;
+    }
+  insn_insert_operand (arg->insn, operand, arg->token->u.index);
+  ++arg->token;
+  return TRUE;
+}
+
+/* OP_REG_INDEX matcher.  */
+
+static bfd_boolean
+match_reg_index_operand (struct mips_arg_info *arg,
+                        const struct mips_operand *operand)
+{
+  unsigned int regno;
+
+  if (arg->token->type != OT_REG_INDEX)
+    return FALSE;
+
+  if (!match_regno (arg, OP_REG_GP, arg->token->u.regno, &regno))
+    return FALSE;
+
+  insn_insert_operand (arg->insn, operand, regno);
+  ++arg->token;
+  return TRUE;
+}
+
 /* OP_PC matcher.  */
 
 static bfd_boolean
@@ -4805,6 +5698,25 @@ match_pc_operand (struct mips_arg_info *arg)
   return FALSE;
 }
 
+/* OP_NON_ZERO_REG matcher.  */
+
+static bfd_boolean
+match_non_zero_reg_operand (struct mips_arg_info *arg,
+                           const struct mips_operand *operand)
+{
+  unsigned int regno;
+
+  if (!match_reg (arg, OP_REG_GP, &regno))
+    return FALSE;
+
+  if (regno == 0)
+    return FALSE;
+
+  arg->last_regno = regno;
+  insn_insert_operand (arg->insn, operand, regno);
+  return TRUE;
+}
+
 /* OP_REPEAT_DEST_REG and OP_REPEAT_PREV_REG matcher.  OTHER_REGNO is the
    register that we need to match.  */
 
@@ -4858,7 +5770,10 @@ match_float_constant (struct mips_arg_info *arg, expressionS *imm,
      The .lit4 and .lit8 sections are only used if permitted by the
      -G argument.  */
   if (arg->token->type != OT_FLOAT)
-    return FALSE;
+    {
+      set_insn_error (arg->argnum, _("floating-point expression required"));
+      return FALSE;
+    }
 
   gas_assert (arg->token->u.flt.length == length);
   data = arg->token->u.flt.data;
@@ -4883,13 +5798,16 @@ match_float_constant (struct mips_arg_info *arg, expressionS *imm,
   /* Handle 64-bit constants for which an immediate value is best.  */
   if (length == 8
       && !mips_disable_float_construction
-      /* Constants can only be constructed in GPRs and copied
-        to FPRs if the GPRs are at least as wide as the FPRs.
-        Force the constant into memory if we are using 64-bit FPRs
-        but the GPRs are only 32 bits wide.  */
-      /* ??? No longer true with the addition of MTHC1, but this
-        is legacy code...  */
-      && (using_gprs || !(HAVE_64BIT_FPRS && HAVE_32BIT_GPRS))
+      /* Constants can only be constructed in GPRs and copied to FPRs if the
+        GPRs are at least as wide as the FPRs or MTHC1 is available.
+        Unlike most tests for 32-bit floating-point registers this check
+        specifically looks for GPR_SIZE == 32 as the FPXX ABI does not
+        permit 64-bit moves without MXHC1.
+        Force the constant into memory otherwise.  */
+      && (using_gprs
+         || GPR_SIZE == 64
+         || ISA_HAS_MXHC1 (mips_opts.isa)
+         || FPR_SIZE == 32)
       && ((data[0] == 0 && data[1] == 0)
          || (data[2] == 0 && data[3] == 0))
       && ((data[4] == 0 && data[5] == 0)
@@ -4899,7 +5817,7 @@ match_float_constant (struct mips_arg_info *arg, expressionS *imm,
         If using 32-bit registers, set IMM to the high order 32 bits and
         OFFSET to the low order 32 bits.  Otherwise, set IMM to the entire
         64 bit constant.  */
-      if (using_gprs ? HAVE_32BIT_GPRS : HAVE_32BIT_FPRS)
+      if (GPR_SIZE == 32 || (!using_gprs && FPR_SIZE != 64))
        {
          imm->X_op = O_constant;
          offset->X_op = O_constant;
@@ -4953,7 +5871,7 @@ match_float_constant (struct mips_arg_info *arg, expressionS *imm,
   else
     record_alignment (new_seg, length == 4 ? 2 : 3);
   if (seg == now_seg)
-    as_bad (_("Can't use floating point insn in this section"));
+    as_bad (_("cannot use `%s' in this section"), arg->insn->insn_mo->name);
 
   /* Set the argument to the current address in the section.  */
   imm->X_op = O_absent;
@@ -4970,6 +5888,42 @@ match_float_constant (struct mips_arg_info *arg, expressionS *imm,
   return TRUE;
 }
 
+/* OP_VU0_SUFFIX and OP_VU0_MATCH_SUFFIX matcher; MATCH_P selects between
+   them.  */
+
+static bfd_boolean
+match_vu0_suffix_operand (struct mips_arg_info *arg,
+                         const struct mips_operand *operand,
+                         bfd_boolean match_p)
+{
+  unsigned int uval;
+
+  /* The operand can be an XYZW mask or a single 2-bit channel index
+     (with X being 0).  */
+  gas_assert (operand->size == 2 || operand->size == 4);
+
+  /* The suffix can be omitted when it is already part of the opcode.  */
+  if (arg->token->type != OT_CHANNELS)
+    return match_p;
+
+  uval = arg->token->u.channels;
+  if (operand->size == 2)
+    {
+      /* Check that a single bit is set and convert it into a 2-bit index.  */
+      if ((uval & -uval) != uval)
+       return FALSE;
+      uval = 4 - ffs (uval);
+    }
+
+  if (match_p && insn_extract_operand (arg->insn, operand) != uval)
+    return FALSE;
+
+  ++arg->token;
+  if (!match_p)
+    insn_insert_operand (arg->insn, operand, uval);
+  return TRUE;
+}
+
 /* 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.  */
 
@@ -4989,6 +5943,7 @@ match_operand (struct mips_arg_info *arg,
       return match_msb_operand (arg, operand);
 
     case OP_REG:
+    case OP_OPTIONAL_REG:
       return match_reg_operand (arg, operand);
 
     case OP_REG_PAIR:
@@ -5026,6 +5981,27 @@ match_operand (struct mips_arg_info *arg,
 
     case OP_PC:
       return match_pc_operand (arg);
+
+    case OP_VU0_SUFFIX:
+      return match_vu0_suffix_operand (arg, operand, FALSE);
+
+    case OP_VU0_MATCH_SUFFIX:
+      return match_vu0_suffix_operand (arg, operand, TRUE);
+
+    case OP_IMM_INDEX:
+      return match_imm_index_operand (arg, operand);
+
+    case OP_REG_INDEX:
+      return match_reg_index_operand (arg, operand);
+
+    case OP_SAME_RS_RT:
+      return match_same_rs_rt_operand (arg, operand);
+
+    case OP_CHECK_PREV:
+      return match_check_prev_operand (arg, operand);
+
+    case OP_NON_ZERO_REG:
+      return match_non_zero_reg_operand (arg, operand);
     }
   abort ();
 }
@@ -5039,9 +6015,9 @@ check_completed_insn (struct mips_arg_info *arg)
   if (arg->seen_at)
     {
       if (AT == ATREG)
-       as_warn (_("Used $at without \".set noat\""));
+       as_warn (_("used $at without \".set noat\""));
       else
-       as_warn (_("Used $%u with \".set at=$%u\""), AT, AT);
+       as_warn (_("used $%u with \".set at=$%u\""), AT, AT);
     }
 }
 
@@ -5054,8 +6030,8 @@ reg_needs_delay (unsigned int reg)
 
   prev_pinfo = history[0].insn_mo->pinfo;
   if (!mips_opts.noreorder
-      && (((prev_pinfo & INSN_LOAD_MEMORY_DELAY) && !gpr_interlocks)
-         || ((prev_pinfo & INSN_LOAD_COPROC_DELAY) && !cop_interlocks))
+      && (((prev_pinfo & INSN_LOAD_MEMORY) && !gpr_interlocks)
+         || ((prev_pinfo & INSN_LOAD_COPROC) && !cop_interlocks))
       && (gpr_write_mask (&history[0]) & (1 << reg)))
     return TRUE;
 
@@ -5084,8 +6060,10 @@ classify_vr4120_insn (const char *name)
   return NUM_FIX_VR4120_CLASSES;
 }
 
-#define INSN_ERET  0x42000018
-#define INSN_DERET 0x4200001f
+#define INSN_ERET      0x42000018
+#define INSN_DERET     0x4200001f
+#define INSN_DMULT     0x1c
+#define INSN_DMULTU    0x1d
 
 /* Return the number of instructions that must separate INSN1 and INSN2,
    where INSN1 is the earlier instruction.  Return the worst-case value
@@ -5136,6 +6114,18 @@ insns_between (const struct mips_cl_insn *insn1,
        }
     }
 
+  /* If we're working around PMC RM7000 errata, there must be three
+     nops between a dmult and a load instruction.  */
+  if (mips_fix_rm7000 && !mips_opts.micromips)
+    {
+      if ((insn1->insn_opcode & insn1->insn_mo->mask) == INSN_DMULT
+         || (insn1->insn_opcode & insn1->insn_mo->mask) == INSN_DMULTU)
+       {
+         if (pinfo2 & INSN_LOAD_MEMORY)
+          return 3;
+       }
+    }
+
   /* If working around VR4120 errata, check for combinations that need
      a single intervening instruction.  */
   if (mips_fix_vr4120 && !mips_opts.micromips)
@@ -5158,8 +6148,8 @@ insns_between (const struct mips_cl_insn *insn1,
       /* Check for GPR or coprocessor load delays.  All such delays
         are on the RT register.  */
       /* Itbl support may require additional care here.  */
-      if ((!gpr_interlocks && (pinfo1 & INSN_LOAD_MEMORY_DELAY))
-         || (!cop_interlocks && (pinfo1 & INSN_LOAD_COPROC_DELAY)))
+      if ((!gpr_interlocks && (pinfo1 & INSN_LOAD_MEMORY))
+         || (!cop_interlocks && (pinfo1 & INSN_LOAD_COPROC)))
        {
          if (insn2 == NULL || (gpr_read_mask (insn2) & gpr_write_mask (insn1)))
            return 1;
@@ -5173,7 +6163,7 @@ insns_between (const struct mips_cl_insn *insn1,
       /* Itbl support may require additional care here. FIXME!
         Need to modify this to include knowledge about
         user specified delays!  */
-      else if ((!cop_interlocks && (pinfo1 & INSN_COPROC_MOVE_DELAY))
+      else if ((!cop_interlocks && (pinfo1 & INSN_COPROC_MOVE))
               || (!cop_mem_interlocks && (pinfo1 & INSN_COPROC_MEMORY_DELAY)))
        {
          /* Handle cases where INSN1 writes to a known general coprocessor
@@ -5212,6 +6202,14 @@ insns_between (const struct mips_cl_insn *insn1,
        return 1;
     }
 
+  /* Forbidden slots can not contain Control Transfer Instructions (CTIs)
+     CTIs include all branches and jumps, nal, eret, eretnc, deret, wait
+     and pause.  */
+  if ((insn1->insn_mo->pinfo2 & INSN2_FORBIDDEN_SLOT)
+      && ((pinfo2 & INSN_NO_DELAY_SLOT)
+         || (insn2 && delayed_branch_p (insn2))))
+    return 1;
+
   return 0;
 }
 
@@ -5597,6 +6595,7 @@ can_swap_branch_p (struct mips_cl_insn *ip, expressionS *address_expr,
 {
   unsigned long pinfo, pinfo2, prev_pinfo, prev_pinfo2;
   unsigned int gpr_read, gpr_write, prev_gpr_read, prev_gpr_write;
+  unsigned int fpr_read, prev_fpr_write;
 
   /* -O2 and above is required for this optimization.  */
   if (mips_optimize < 2)
@@ -5671,6 +6670,11 @@ can_swap_branch_p (struct mips_cl_insn *ip, expressionS *address_expr,
   if (gpr_read & prev_gpr_write)
     return FALSE;
 
+  fpr_read = fpr_read_mask (ip);
+  prev_fpr_write = fpr_write_mask (&history[0]);
+  if (fpr_read & prev_fpr_write)
+    return FALSE;
+
   /* If the branch writes a register that the previous
      instruction sets, we can not swap.  */
   gpr_write = gpr_write_mask (ip);
@@ -5769,8 +6773,6 @@ static enum append_method
 get_append_method (struct mips_cl_insn *ip, expressionS *address_expr,
                   bfd_reloc_code_real_type *reloc_type)
 {
-  unsigned long pinfo, pinfo2;
-
   /* The relaxed version of a macro sequence must be inherently
      hazard-free.  */
   if (mips_relax.sequence == 2)
@@ -5787,12 +6789,9 @@ get_append_method (struct mips_cl_insn *ip, expressionS *address_expr,
          && can_swap_branch_p (ip, address_expr, reloc_type))
        return APPEND_SWAP;
 
-      pinfo = ip->insn_mo->pinfo;
-      pinfo2 = ip->insn_mo->pinfo2;
       if (mips_opts.mips16
          && ISA_SUPPORTS_MIPS16E
-         && ((pinfo & MIPS16_INSN_READ_X) != 0
-             || (pinfo2 & INSN2_READ_GPR_31) != 0))
+         && gpr_read_mask (ip) != 0)
        return APPEND_ADD_COMPACT;
 
       return APPEND_ADD_WITH_NOP;
@@ -6011,7 +7010,7 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
           && micromips_insn_length (ip->insn_mo) != 2)
          || ((prev_pinfo2 & INSN2_BRANCH_DELAY_32BIT) != 0
              && micromips_insn_length (ip->insn_mo) != 4)))
-    as_warn (_("Wrong size instruction in a %u-bit branch delay slot"),
+    as_warn (_("wrong size instruction in a %u-bit branch delay slot"),
             (prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0 ? 16 : 32);
 
   if (address_expr == NULL)
@@ -6068,6 +7067,40 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
          }
          break;
 
+       case BFD_RELOC_MIPS_21_PCREL_S2:
+         {
+           int shift;
+
+           shift = 2;
+           if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
+             as_bad (_("branch to misaligned address (0x%lx)"),
+                     (unsigned long) address_expr->X_add_number);
+           if ((address_expr->X_add_number + (1 << (shift + 20)))
+               & ~((1 << (shift + 21)) - 1))
+             as_bad (_("branch address range overflow (0x%lx)"),
+                     (unsigned long) address_expr->X_add_number);
+           ip->insn_opcode |= ((address_expr->X_add_number >> shift)
+                               & 0x1fffff);
+         }
+         break;
+
+       case BFD_RELOC_MIPS_26_PCREL_S2:
+         {
+           int shift;
+
+           shift = 2;
+           if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
+             as_bad (_("branch to misaligned address (0x%lx)"),
+                     (unsigned long) address_expr->X_add_number);
+           if ((address_expr->X_add_number + (1 << (shift + 25)))
+               & ~((1 << (shift + 26)) - 1))
+             as_bad (_("branch address range overflow (0x%lx)"),
+                     (unsigned long) address_expr->X_add_number);
+           ip->insn_opcode |= ((address_expr->X_add_number >> shift)
+                               & 0x3ffffff);
+         }
+         break;
+
        default:
          {
            offsetT value;
@@ -6331,7 +7364,7 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
 
       /* These relocations can have an addend that won't fit in
         4 octets for 64bit assembly.  */
-      if (HAVE_64BIT_GPRS
+      if (GPR_SIZE == 64
          && ! howto->partial_inplace
          && (reloc_type[0] == BFD_RELOC_16
              || reloc_type[0] == BFD_RELOC_32
@@ -6583,54 +7616,650 @@ end_noreorder (void)
     }
 }
 
-/* Set up global variables for the start of a new macro.  */
+/* Sign-extend 32-bit mode constants that have bit 31 set and all
+   higher bits unset.  */
 
 static void
-macro_start (void)
+normalize_constant_expr (expressionS *ex)
 {
-  memset (&mips_macro_warning.sizes, 0, sizeof (mips_macro_warning.sizes));
-  memset (&mips_macro_warning.first_insn_sizes, 0,
-         sizeof (mips_macro_warning.first_insn_sizes));
-  memset (&mips_macro_warning.insns, 0, sizeof (mips_macro_warning.insns));
-  mips_macro_warning.delay_slot_p = (mips_opts.noreorder
-                                    && delayed_branch_p (&history[0]));
-  switch (history[0].insn_mo->pinfo2
-         & (INSN2_BRANCH_DELAY_32BIT | INSN2_BRANCH_DELAY_16BIT))
-    {
-    case INSN2_BRANCH_DELAY_32BIT:
-      mips_macro_warning.delay_slot_length = 4;
-      break;
-    case INSN2_BRANCH_DELAY_16BIT:
-      mips_macro_warning.delay_slot_length = 2;
-      break;
-    default:
-      mips_macro_warning.delay_slot_length = 0;
-      break;
-    }
-  mips_macro_warning.first_frag = NULL;
+  if (ex->X_op == O_constant
+      && IS_ZEXT_32BIT_NUM (ex->X_add_number))
+    ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
+                       - 0x80000000);
 }
 
-/* Given that a macro is longer than one instruction or of the wrong size,
-   return the appropriate warning for it.  Return null if no warning is
-   needed.  SUBTYPE is a bitmask of RELAX_DELAY_SLOT, RELAX_DELAY_SLOT_16BIT,
-   RELAX_DELAY_SLOT_SIZE_FIRST, RELAX_DELAY_SLOT_SIZE_SECOND,
-   and RELAX_NOMACRO.  */
+/* Sign-extend 32-bit mode address offsets that have bit 31 set and
+   all higher bits unset.  */
 
-static const char *
-macro_warning (relax_substateT subtype)
+static void
+normalize_address_expr (expressionS *ex)
 {
-  if (subtype & RELAX_DELAY_SLOT)
-    return _("Macro instruction expanded into multiple instructions"
-            " in a branch delay slot");
-  else if (subtype & RELAX_NOMACRO)
-    return _("Macro instruction expanded into multiple instructions");
-  else if (subtype & (RELAX_DELAY_SLOT_SIZE_FIRST
-                     | RELAX_DELAY_SLOT_SIZE_SECOND))
-    return ((subtype & RELAX_DELAY_SLOT_16BIT)
-           ? _("Macro instruction expanded into a wrong size instruction"
-               " in a 16-bit branch delay slot")
-           : _("Macro instruction expanded into a wrong size instruction"
-               " in a 32-bit branch delay slot"));
+  if (((ex->X_op == O_constant && HAVE_32BIT_ADDRESSES)
+       || (ex->X_op == O_symbol && HAVE_32BIT_SYMBOLS))
+      && IS_ZEXT_32BIT_NUM (ex->X_add_number))
+    ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
+                       - 0x80000000);
+}
+
+/* Try to match TOKENS against OPCODE, storing the result in INSN.
+   Return true if the match was successful.
+
+   OPCODE_EXTRA is a value that should be ORed into the opcode
+   (used for VU0 channel suffixes, etc.).  MORE_ALTS is true if
+   there are more alternatives after OPCODE and SOFT_MATCH is
+   as for mips_arg_info.  */
+
+static bfd_boolean
+match_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
+           struct mips_operand_token *tokens, unsigned int opcode_extra,
+           bfd_boolean lax_match, bfd_boolean complete_p)
+{
+  const char *args;
+  struct mips_arg_info arg;
+  const struct mips_operand *operand;
+  char c;
+
+  imm_expr.X_op = O_absent;
+  offset_expr.X_op = O_absent;
+  offset_reloc[0] = BFD_RELOC_UNUSED;
+  offset_reloc[1] = BFD_RELOC_UNUSED;
+  offset_reloc[2] = BFD_RELOC_UNUSED;
+
+  create_insn (insn, opcode);
+  /* When no opcode suffix is specified, assume ".xyzw". */
+  if ((opcode->pinfo2 & INSN2_VU0_CHANNEL_SUFFIX) != 0 && opcode_extra == 0)
+    insn->insn_opcode |= 0xf << mips_vu0_channel_mask.lsb;
+  else
+    insn->insn_opcode |= opcode_extra;
+  memset (&arg, 0, sizeof (arg));
+  arg.insn = insn;
+  arg.token = tokens;
+  arg.argnum = 1;
+  arg.last_regno = ILLEGAL_REG;
+  arg.dest_regno = ILLEGAL_REG;
+  arg.lax_match = lax_match;
+  for (args = opcode->args;; ++args)
+    {
+      if (arg.token->type == OT_END)
+       {
+         /* Handle unary instructions in which only one operand is given.
+            The source is then the same as the destination.  */
+         if (arg.opnum == 1 && *args == ',')
+           {
+             operand = (mips_opts.micromips
+                        ? decode_micromips_operand (args + 1)
+                        : decode_mips_operand (args + 1));
+             if (operand && mips_optional_operand_p (operand))
+               {
+                 arg.token = tokens;
+                 arg.argnum = 1;
+                 continue;
+               }
+           }
+
+         /* Treat elided base registers as $0.  */
+         if (strcmp (args, "(b)") == 0)
+           args += 3;
+
+         if (args[0] == '+')
+           switch (args[1])
+             {
+             case 'K':
+             case 'N':
+               /* The register suffix is optional. */
+               args += 2;
+               break;
+             }
+
+         /* Fail the match if there were too few operands.  */
+         if (*args)
+           return FALSE;
+
+         /* Successful match.  */
+         if (!complete_p)
+           return TRUE;
+         clear_insn_error ();
+         if (arg.dest_regno == arg.last_regno
+             && strncmp (insn->insn_mo->name, "jalr", 4) == 0)
+           {
+             if (arg.opnum == 2)
+               set_insn_error
+                 (0, _("source and destination must be different"));
+             else if (arg.last_regno == 31)
+               set_insn_error
+                 (0, _("a destination register must be supplied"));
+           }
+         else if (arg.last_regno == 31
+                  && (strncmp (insn->insn_mo->name, "bltzal", 6) == 0
+                      || strncmp (insn->insn_mo->name, "bgezal", 6) == 0))
+           set_insn_error (0, _("the source register must not be $31"));
+         check_completed_insn (&arg);
+         return TRUE;
+       }
+
+      /* Fail the match if the line has too many operands.   */
+      if (*args == 0)
+       return FALSE;
+
+      /* Handle characters that need to match exactly.  */
+      if (*args == '(' || *args == ')' || *args == ',')
+       {
+         if (match_char (&arg, *args))
+           continue;
+         return FALSE;
+       }
+      if (*args == '#')
+       {
+         ++args;
+         if (arg.token->type == OT_DOUBLE_CHAR
+             && arg.token->u.ch == *args)
+           {
+             ++arg.token;
+             continue;
+           }
+         return FALSE;
+       }
+
+      /* Handle special macro operands.  Work out the properties of
+        other operands.  */
+      arg.opnum += 1;
+      switch (*args)
+       {
+       case '-':
+         switch (args[1])
+           {
+           case 'A':
+             *offset_reloc = BFD_RELOC_MIPS_19_PCREL_S2;
+             break;
+
+           case 'B':
+             *offset_reloc = BFD_RELOC_MIPS_18_PCREL_S3;
+             break;
+           }
+         break;
+
+       case '+':
+         switch (args[1])
+           {
+           case 'i':
+             *offset_reloc = BFD_RELOC_MIPS_JMP;
+             break;
+
+           case '\'':
+             *offset_reloc = BFD_RELOC_MIPS_26_PCREL_S2;
+             break;
+
+           case '\"':
+             *offset_reloc = BFD_RELOC_MIPS_21_PCREL_S2;
+             break;
+           }
+         break;
+
+       case 'I':
+         if (!match_const_int (&arg, &imm_expr.X_add_number))
+           return FALSE;
+         imm_expr.X_op = O_constant;
+         if (GPR_SIZE == 32)
+           normalize_constant_expr (&imm_expr);
+         continue;
+
+       case 'A':
+         if (arg.token->type == OT_CHAR && arg.token->u.ch == '(')
+           {
+             /* Assume that the offset has been elided and that what
+                we saw was a base register.  The match will fail later
+                if that assumption turns out to be wrong.  */
+             offset_expr.X_op = O_constant;
+             offset_expr.X_add_number = 0;
+           }
+         else
+           {
+             if (!match_expression (&arg, &offset_expr, offset_reloc))
+               return FALSE;
+             normalize_address_expr (&offset_expr);
+           }
+         continue;
+
+       case 'F':
+         if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+                                    8, TRUE))
+           return FALSE;
+         continue;
+
+       case 'L':
+         if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+                                    8, FALSE))
+           return FALSE;
+         continue;
+
+       case 'f':
+         if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+                                    4, TRUE))
+           return FALSE;
+         continue;
+
+       case 'l':
+         if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+                                    4, FALSE))
+           return FALSE;
+         continue;
+
+       case 'p':
+         *offset_reloc = BFD_RELOC_16_PCREL_S2;
+         break;
+
+       case 'a':
+         *offset_reloc = BFD_RELOC_MIPS_JMP;
+         break;
+
+       case 'm':
+         gas_assert (mips_opts.micromips);
+         c = args[1];
+         switch (c)
+           {
+           case 'D':
+           case 'E':
+             if (!forced_insn_length)
+               *offset_reloc = (int) BFD_RELOC_UNUSED + c;
+             else if (c == 'D')
+               *offset_reloc = BFD_RELOC_MICROMIPS_10_PCREL_S1;
+             else
+               *offset_reloc = BFD_RELOC_MICROMIPS_7_PCREL_S1;
+             break;
+           }
+         break;
+       }
+
+      operand = (mips_opts.micromips
+                ? decode_micromips_operand (args)
+                : decode_mips_operand (args));
+      if (!operand)
+       abort ();
+
+      /* Skip prefixes.  */
+      if (*args == '+' || *args == 'm' || *args == '-')
+       args++;
+
+      if (mips_optional_operand_p (operand)
+         && args[1] == ','
+         && (arg.token[0].type != OT_REG
+             || arg.token[1].type == OT_END))
+       {
+         /* Assume that the register has been elided and is the
+            same as the first operand.  */
+         arg.token = tokens;
+         arg.argnum = 1;
+       }
+
+      if (!match_operand (&arg, operand))
+       return FALSE;
+    }
+}
+
+/* Like match_insn, but for MIPS16.  */
+
+static bfd_boolean
+match_mips16_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
+                  struct mips_operand_token *tokens)
+{
+  const char *args;
+  const struct mips_operand *operand;
+  const struct mips_operand *ext_operand;
+  struct mips_arg_info arg;
+  int relax_char;
+
+  create_insn (insn, opcode);
+  imm_expr.X_op = O_absent;
+  offset_expr.X_op = O_absent;
+  offset_reloc[0] = BFD_RELOC_UNUSED;
+  offset_reloc[1] = BFD_RELOC_UNUSED;
+  offset_reloc[2] = BFD_RELOC_UNUSED;
+  relax_char = 0;
+
+  memset (&arg, 0, sizeof (arg));
+  arg.insn = insn;
+  arg.token = tokens;
+  arg.argnum = 1;
+  arg.last_regno = ILLEGAL_REG;
+  arg.dest_regno = ILLEGAL_REG;
+  relax_char = 0;
+  for (args = opcode->args;; ++args)
+    {
+      int c;
+
+      if (arg.token->type == OT_END)
+       {
+         offsetT value;
+
+         /* Handle unary instructions in which only one operand is given.
+            The source is then the same as the destination.  */
+         if (arg.opnum == 1 && *args == ',')
+           {
+             operand = decode_mips16_operand (args[1], FALSE);
+             if (operand && mips_optional_operand_p (operand))
+               {
+                 arg.token = tokens;
+                 arg.argnum = 1;
+                 continue;
+               }
+           }
+
+         /* Fail the match if there were too few operands.  */
+         if (*args)
+           return FALSE;
+
+         /* Successful match.  Stuff the immediate value in now, if
+            we can.  */
+         clear_insn_error ();
+         if (opcode->pinfo == INSN_MACRO)
+           {
+             gas_assert (relax_char == 0 || relax_char == 'p');
+             gas_assert (*offset_reloc == BFD_RELOC_UNUSED);
+           }
+         else if (relax_char
+                  && offset_expr.X_op == O_constant
+                  && calculate_reloc (*offset_reloc,
+                                      offset_expr.X_add_number,
+                                      &value))
+           {
+             mips16_immed (NULL, 0, relax_char, *offset_reloc, value,
+                           forced_insn_length, &insn->insn_opcode);
+             offset_expr.X_op = O_absent;
+             *offset_reloc = BFD_RELOC_UNUSED;
+           }
+         else if (relax_char && *offset_reloc != BFD_RELOC_UNUSED)
+           {
+             if (forced_insn_length == 2)
+               set_insn_error (0, _("invalid unextended operand value"));
+             forced_insn_length = 4;
+             insn->insn_opcode |= MIPS16_EXTEND;
+           }
+         else if (relax_char)
+           *offset_reloc = (int) BFD_RELOC_UNUSED + relax_char;
+
+         check_completed_insn (&arg);
+         return TRUE;
+       }
+
+      /* Fail the match if the line has too many operands.   */
+      if (*args == 0)
+       return FALSE;
+
+      /* Handle characters that need to match exactly.  */
+      if (*args == '(' || *args == ')' || *args == ',')
+       {
+         if (match_char (&arg, *args))
+           continue;
+         return FALSE;
+       }
+
+      arg.opnum += 1;
+      c = *args;
+      switch (c)
+       {
+       case 'p':
+       case 'q':
+       case 'A':
+       case 'B':
+       case 'E':
+         relax_char = c;
+         break;
+
+       case 'I':
+         if (!match_const_int (&arg, &imm_expr.X_add_number))
+           return FALSE;
+         imm_expr.X_op = O_constant;
+         if (GPR_SIZE == 32)
+           normalize_constant_expr (&imm_expr);
+         continue;
+
+       case 'a':
+       case 'i':
+         *offset_reloc = BFD_RELOC_MIPS16_JMP;
+         insn->insn_opcode <<= 16;
+         break;
+       }
+
+      operand = decode_mips16_operand (c, FALSE);
+      if (!operand)
+       abort ();
+
+      /* '6' is a special case.  It is used for BREAK and SDBBP,
+        whose operands are only meaningful to the software that decodes
+        them.  This means that there is no architectural reason why
+        they cannot be prefixed by EXTEND, but in practice,
+        exception handlers will only look at the instruction
+        itself.  We therefore allow '6' to be extended when
+        disassembling but not when assembling.  */
+      if (operand->type != OP_PCREL && c != '6')
+       {
+         ext_operand = decode_mips16_operand (c, TRUE);
+         if (operand != ext_operand)
+           {
+             if (arg.token->type == OT_CHAR && arg.token->u.ch == '(')
+               {
+                 offset_expr.X_op = O_constant;
+                 offset_expr.X_add_number = 0;
+                 relax_char = c;
+                 continue;
+               }
+
+             /* We need the OT_INTEGER check because some MIPS16
+                immediate variants are listed before the register ones.  */
+             if (arg.token->type != OT_INTEGER
+                 || !match_expression (&arg, &offset_expr, offset_reloc))
+               return FALSE;
+
+             /* '8' is used for SLTI(U) and has traditionally not
+                been allowed to take relocation operators.  */
+             if (offset_reloc[0] != BFD_RELOC_UNUSED
+                 && (ext_operand->size != 16 || c == '8'))
+               return FALSE;
+
+             relax_char = c;
+             continue;
+           }
+       }
+
+      if (mips_optional_operand_p (operand)
+         && args[1] == ','
+         && (arg.token[0].type != OT_REG
+             || arg.token[1].type == OT_END))
+       {
+         /* Assume that the register has been elided and is the
+            same as the first operand.  */
+         arg.token = tokens;
+         arg.argnum = 1;
+       }
+
+      if (!match_operand (&arg, operand))
+       return FALSE;
+    }
+}
+
+/* Record that the current instruction is invalid for the current ISA.  */
+
+static void
+match_invalid_for_isa (void)
+{
+  set_insn_error_ss
+    (0, _("opcode not supported on this processor: %s (%s)"),
+     mips_cpu_info_from_arch (mips_opts.arch)->name,
+     mips_cpu_info_from_isa (mips_opts.isa)->name);
+}
+
+/* Try to match TOKENS against a series of opcode entries, starting at FIRST.
+   Return true if a definite match or failure was found, storing any match
+   in INSN.  OPCODE_EXTRA is a value that should be ORed into the opcode
+   (to handle things like VU0 suffixes).  LAX_MATCH is true if we have already
+   tried and failed to match under normal conditions and now want to try a
+   more relaxed match.  */
+
+static bfd_boolean
+match_insns (struct mips_cl_insn *insn, const struct mips_opcode *first,
+            const struct mips_opcode *past, struct mips_operand_token *tokens,
+            int opcode_extra, bfd_boolean lax_match)
+{
+  const struct mips_opcode *opcode;
+  const struct mips_opcode *invalid_delay_slot;
+  bfd_boolean seen_valid_for_isa, seen_valid_for_size;
+
+  /* Search for a match, ignoring alternatives that don't satisfy the
+     current ISA or forced_length.  */
+  invalid_delay_slot = 0;
+  seen_valid_for_isa = FALSE;
+  seen_valid_for_size = FALSE;
+  opcode = first;
+  do
+    {
+      gas_assert (strcmp (opcode->name, first->name) == 0);
+      if (is_opcode_valid (opcode))
+       {
+         seen_valid_for_isa = TRUE;
+         if (is_size_valid (opcode))
+           {
+             bfd_boolean delay_slot_ok;
+
+             seen_valid_for_size = TRUE;
+             delay_slot_ok = is_delay_slot_valid (opcode);
+             if (match_insn (insn, opcode, tokens, opcode_extra,
+                             lax_match, delay_slot_ok))
+               {
+                 if (!delay_slot_ok)
+                   {
+                     if (!invalid_delay_slot)
+                       invalid_delay_slot = opcode;
+                   }
+                 else
+                   return TRUE;
+               }
+           }
+       }
+      ++opcode;
+    }
+  while (opcode < past && strcmp (opcode->name, first->name) == 0);
+
+  /* If the only matches we found had the wrong length for the delay slot,
+     pick the first such match.  We'll issue an appropriate warning later.  */
+  if (invalid_delay_slot)
+    {
+      if (match_insn (insn, invalid_delay_slot, tokens, opcode_extra,
+                     lax_match, TRUE))
+       return TRUE;
+      abort ();
+    }
+
+  /* Handle the case where we didn't try to match an instruction because
+     all the alternatives were incompatible with the current ISA.  */
+  if (!seen_valid_for_isa)
+    {
+      match_invalid_for_isa ();
+      return TRUE;
+    }
+
+  /* Handle the case where we didn't try to match an instruction because
+     all the alternatives were of the wrong size.  */
+  if (!seen_valid_for_size)
+    {
+      if (mips_opts.insn32)
+       set_insn_error (0, _("opcode not supported in the `insn32' mode"));
+      else
+       set_insn_error_i
+         (0, _("unrecognized %d-bit version of microMIPS opcode"),
+          8 * forced_insn_length);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+/* Like match_insns, but for MIPS16.  */
+
+static bfd_boolean
+match_mips16_insns (struct mips_cl_insn *insn, const struct mips_opcode *first,
+                   struct mips_operand_token *tokens)
+{
+  const struct mips_opcode *opcode;
+  bfd_boolean seen_valid_for_isa;
+
+  /* Search for a match, ignoring alternatives that don't satisfy the
+     current ISA.  There are no separate entries for extended forms so
+     we deal with forced_length later.  */
+  seen_valid_for_isa = FALSE;
+  opcode = first;
+  do
+    {
+      gas_assert (strcmp (opcode->name, first->name) == 0);
+      if (is_opcode_valid_16 (opcode))
+       {
+         seen_valid_for_isa = TRUE;
+         if (match_mips16_insn (insn, opcode, tokens))
+           return TRUE;
+       }
+      ++opcode;
+    }
+  while (opcode < &mips16_opcodes[bfd_mips16_num_opcodes]
+        && strcmp (opcode->name, first->name) == 0);
+
+  /* Handle the case where we didn't try to match an instruction because
+     all the alternatives were incompatible with the current ISA.  */
+  if (!seen_valid_for_isa)
+    {
+      match_invalid_for_isa ();
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+/* Set up global variables for the start of a new macro.  */
+
+static void
+macro_start (void)
+{
+  memset (&mips_macro_warning.sizes, 0, sizeof (mips_macro_warning.sizes));
+  memset (&mips_macro_warning.first_insn_sizes, 0,
+         sizeof (mips_macro_warning.first_insn_sizes));
+  memset (&mips_macro_warning.insns, 0, sizeof (mips_macro_warning.insns));
+  mips_macro_warning.delay_slot_p = (mips_opts.noreorder
+                                    && delayed_branch_p (&history[0]));
+  switch (history[0].insn_mo->pinfo2
+         & (INSN2_BRANCH_DELAY_32BIT | INSN2_BRANCH_DELAY_16BIT))
+    {
+    case INSN2_BRANCH_DELAY_32BIT:
+      mips_macro_warning.delay_slot_length = 4;
+      break;
+    case INSN2_BRANCH_DELAY_16BIT:
+      mips_macro_warning.delay_slot_length = 2;
+      break;
+    default:
+      mips_macro_warning.delay_slot_length = 0;
+      break;
+    }
+  mips_macro_warning.first_frag = NULL;
+}
+
+/* Given that a macro is longer than one instruction or of the wrong size,
+   return the appropriate warning for it.  Return null if no warning is
+   needed.  SUBTYPE is a bitmask of RELAX_DELAY_SLOT, RELAX_DELAY_SLOT_16BIT,
+   RELAX_DELAY_SLOT_SIZE_FIRST, RELAX_DELAY_SLOT_SIZE_SECOND,
+   and RELAX_NOMACRO.  */
+
+static const char *
+macro_warning (relax_substateT subtype)
+{
+  if (subtype & RELAX_DELAY_SLOT)
+    return _("macro instruction expanded into multiple instructions"
+            " in a branch delay slot");
+  else if (subtype & RELAX_NOMACRO)
+    return _("macro instruction expanded into multiple instructions");
+  else if (subtype & (RELAX_DELAY_SLOT_SIZE_FIRST
+                     | RELAX_DELAY_SLOT_SIZE_SECOND))
+    return ((subtype & RELAX_DELAY_SLOT_16BIT)
+           ? _("macro instruction expanded into a wrong size instruction"
+               " in a 16-bit branch delay slot")
+           : _("macro instruction expanded into a wrong size instruction"
+               " in a 32-bit branch delay slot"));
   else
     return 0;
 }
@@ -6718,10 +8347,13 @@ static const char * const shft_fmt[2] = { "d,w,<", "t,r,<" };
 static const char * const trap_fmt[2] = { "s,t,q", "s,t,|" };
 
 #define BRK_FMT (brk_fmt[mips_opts.micromips][mips_opts.insn32])
-#define COP12_FMT (cop12_fmt[mips_opts.micromips])
+#define COP12_FMT (ISA_IS_R6 (mips_opts.isa) ? "E,+:(d)" \
+                                            : cop12_fmt[mips_opts.micromips])
 #define JALR_FMT (jalr_fmt[mips_opts.micromips])
 #define LUI_FMT (lui_fmt[mips_opts.micromips])
 #define MEM12_FMT (mem12_fmt[mips_opts.micromips])
+#define LL_SC_FMT (ISA_IS_R6 (mips_opts.isa) ? "t,+j(b)" \
+                                            : mem12_fmt[mips_opts.micromips])
 #define MFHL_FMT (mfhl_fmt[mips_opts.micromips][mips_opts.insn32])
 #define SHFT_FMT (shft_fmt[mips_opts.micromips])
 #define TRAP_FMT (trap_fmt[mips_opts.micromips])
@@ -6899,7 +8531,7 @@ macro_build (expressionS *ep, const char *name, const char *fmt, ...)
            uval |= (uval << 5);
          insn_insert_operand (&insn, operand, uval);
 
-         if (*fmt == '+' || *fmt == 'm')
+         if (*fmt == '+' || *fmt == 'm' || *fmt == '-')
            ++fmt;
          break;
        }
@@ -6997,33 +8629,6 @@ mips16_macro_build (expressionS *ep, const char *name, const char *fmt,
   append_insn (&insn, ep, r, TRUE);
 }
 
-/*
- * Sign-extend 32-bit mode constants that have bit 31 set and all
- * higher bits unset.
- */
-static void
-normalize_constant_expr (expressionS *ex)
-{
-  if (ex->X_op == O_constant
-      && IS_ZEXT_32BIT_NUM (ex->X_add_number))
-    ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
-                       - 0x80000000);
-}
-
-/*
- * Sign-extend 32-bit mode address offsets that have bit 31 set and
- * all higher bits unset.
- */
-static void
-normalize_address_expr (expressionS *ex)
-{
-  if (((ex->X_op == O_constant && HAVE_32BIT_ADDRESSES)
-       || (ex->X_op == O_symbol && HAVE_32BIT_SYMBOLS))
-      && IS_ZEXT_32BIT_NUM (ex->X_add_number))
-    ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
-                       - 0x80000000);
-}
-
 /*
  * Generate a "jalr" instruction with a relocation hint to the called
  * function.  This occurs in NewABI PIC code.
@@ -7117,7 +8722,7 @@ macro_build_ldst_constoffset (expressionS *ep, const char *op,
       macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, AT);
 
       if (!mips_opts.at)
-       as_bad (_("Macro used $at after \".set noat\""));
+       as_bad (_("macro used $at after \".set noat\""));
     }
 }
 
@@ -7128,14 +8733,13 @@ macro_build_ldst_constoffset (expressionS *ep, const char *op,
 static void
 set_at (int reg, int unsignedp)
 {
-  if (imm_expr.X_op == O_constant
-      && imm_expr.X_add_number >= -0x8000
+  if (imm_expr.X_add_number >= -0x8000
       && imm_expr.X_add_number < 0x8000)
     macro_build (&imm_expr, unsignedp ? "sltiu" : "slti", "t,r,j",
                 AT, reg, BFD_RELOC_LO16);
   else
     {
-      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+      load_register (AT, &imm_expr, GPR_SIZE == 64);
       macro_build (NULL, unsignedp ? "sltu" : "slt", "d,v,t", AT, reg, AT);
     }
 }
@@ -7261,12 +8865,12 @@ load_register (int reg, expressionS *ep, int dbl)
 
   /* The value is larger than 32 bits.  */
 
-  if (!dbl || HAVE_32BIT_GPRS)
+  if (!dbl || GPR_SIZE == 32)
     {
       char value[32];
 
       sprintf_vma (value, ep->X_add_number);
-      as_bad (_("Number (0x%s) larger than 32 bits"), value);
+      as_bad (_("number (0x%s) larger than 32 bits"), value);
       macro_build (ep, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
       return;
     }
@@ -7286,7 +8890,7 @@ load_register (int reg, expressionS *ep, int dbl)
       if (ep->X_add_number == 3)
        generic_bignum[3] = 0;
       else if (ep->X_add_number > 4)
-       as_bad (_("Number larger than 64 bits"));
+       as_bad (_("number larger than 64 bits"));
       lo32.X_op = O_constant;
       lo32.X_add_number = generic_bignum[0] + (generic_bignum[1] << 16);
       hi32.X_op = O_constant;
@@ -7702,7 +9306,7 @@ load_address (int reg, expressionS *ep, int *used_at)
     abort ();
 
   if (!mips_opts.at && *used_at == 1)
-    as_bad (_("Macro used $at after \".set noat\""));
+    as_bad (_("macro used $at after \".set noat\""));
 }
 
 /* Move the contents of register SOURCE into register DEST.  */
@@ -7717,7 +9321,7 @@ move_register (int dest, int source)
       && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
     macro_build (NULL, "move", "mp,mj", dest, source);
   else
-    macro_build (NULL, HAVE_32BIT_GPRS ? "addu" : "daddu", "d,v,t",
+    macro_build (NULL, GPR_SIZE == 32 ? "addu" : "daddu", "d,v,t",
                 dest, source, 0);
 }
 
@@ -8053,7 +9657,8 @@ small_offset_p (unsigned int range, unsigned int align, unsigned int offbits)
 static void
 macro (struct mips_cl_insn *ip, char *str)
 {
-  unsigned int treg, sreg, dreg, breg;
+  const struct mips_operand_array *operands;
+  unsigned int breg, i;
   unsigned int tempreg;
   int mask;
   int used_at = 0;
@@ -8076,12 +9681,17 @@ macro (struct mips_cl_insn *ip, char *str)
   int off;
   int hold_mips_optimize;
   unsigned int align;
+  unsigned int op[MAX_OPERANDS];
 
   gas_assert (! mips_opts.mips16);
 
-  treg = EXTRACT_OPERAND (mips_opts.micromips, RT, *ip);
-  dreg = EXTRACT_OPERAND (mips_opts.micromips, RD, *ip);
-  sreg = breg = EXTRACT_OPERAND (mips_opts.micromips, RS, *ip);
+  operands = insn_operands (ip);
+  for (i = 0; i < MAX_OPERANDS; i++)
+    if (operands->operand[i])
+      op[i] = insn_extract_operand (ip, operands->operand[i]);
+    else
+      op[i] = -1;
+
   mask = ip->insn_mo->mask;
 
   label_expr.X_op = O_constant;
@@ -8112,12 +9722,12 @@ macro (struct mips_cl_insn *ip, char *str)
        micromips_label_expr (&label_expr);
       else
        label_expr.X_add_number = 8;
-      macro_build (&label_expr, "bgez", "s,p", sreg);
-      if (dreg == sreg)
+      macro_build (&label_expr, "bgez", "s,p", op[1]);
+      if (op[0] == op[1])
        macro_build (NULL, "nop", "");
       else
-       move_register (dreg, sreg);
-      macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", dreg, 0, sreg);
+       move_register (op[0], op[1]);
+      macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", op[0], 0, op[1]);
       if (mips_opts.micromips)
        micromips_add_label ();
 
@@ -8138,11 +9748,11 @@ macro (struct mips_cl_insn *ip, char *str)
       s2 = "dadd";
       if (!mips_opts.micromips)
        goto do_addi;
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number >= -0x200
+      if (imm_expr.X_add_number >= -0x200
          && imm_expr.X_add_number < 0x200)
        {
-         macro_build (NULL, s, "t,r,.", treg, sreg, imm_expr.X_add_number);
+         macro_build (NULL, s, "t,r,.", op[0], op[1],
+                      (int) imm_expr.X_add_number);
          break;
        }
       goto do_addi_i;
@@ -8151,17 +9761,16 @@ macro (struct mips_cl_insn *ip, char *str)
       s = "daddiu";
       s2 = "daddu";
     do_addi:
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number >= -0x8000
+      if (imm_expr.X_add_number >= -0x8000
          && imm_expr.X_add_number < 0x8000)
        {
-         macro_build (&imm_expr, s, "t,r,j", treg, sreg, BFD_RELOC_LO16);
+         macro_build (&imm_expr, s, "t,r,j", op[0], op[1], BFD_RELOC_LO16);
          break;
        }
     do_addi_i:
       used_at = 1;
       load_register (AT, &imm_expr, dbl);
-      macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
+      macro_build (NULL, s2, "d,v,t", op[0], op[1], AT);
       break;
 
     case M_AND_I:
@@ -8180,24 +9789,23 @@ macro (struct mips_cl_insn *ip, char *str)
       s = "xori";
       s2 = "xor";
     do_bit:
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number >= 0
+      if (imm_expr.X_add_number >= 0
          && imm_expr.X_add_number < 0x10000)
        {
          if (mask != M_NOR_I)
-           macro_build (&imm_expr, s, "t,r,i", treg, sreg, BFD_RELOC_LO16);
+           macro_build (&imm_expr, s, "t,r,i", op[0], op[1], BFD_RELOC_LO16);
          else
            {
              macro_build (&imm_expr, "ori", "t,r,i",
-                          treg, sreg, BFD_RELOC_LO16);
-             macro_build (NULL, "nor", "d,v,t", treg, treg, 0);
+                          op[0], op[1], BFD_RELOC_LO16);
+             macro_build (NULL, "nor", "d,v,t", op[0], op[0], 0);
            }
          break;
        }
 
       used_at = 1;
-      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
+      load_register (AT, &imm_expr, GPR_SIZE == 64);
+      macro_build (NULL, s2, "d,v,t", op[0], op[1], AT);
       break;
 
     case M_BALIGN:
@@ -8207,11 +9815,11 @@ macro (struct mips_cl_insn *ip, char *str)
          macro_build (NULL, "nop", "");
          break;
        case 2:
-         macro_build (NULL, "packrl.ph", "d,s,t", treg, treg, sreg);
+         macro_build (NULL, "packrl.ph", "d,s,t", op[0], op[0], op[1]);
          break;
        case 1:
        case 3:
-         macro_build (NULL, "balign", "t,s,2", treg, sreg,
+         macro_build (NULL, "balign", "t,s,2", op[0], op[1],
                       (int) imm_expr.X_add_number);
          break;
        default:
@@ -8234,31 +9842,31 @@ macro (struct mips_cl_insn *ip, char *str)
     case M_BEQL_I:
     case M_BNE_I:
     case M_BNEL_I:
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
-       treg = 0;
+      if (imm_expr.X_add_number == 0)
+       op[1] = 0;
       else
        {
-         treg = AT;
+         op[1] = AT;
          used_at = 1;
-         load_register (treg, &imm_expr, HAVE_64BIT_GPRS);
+         load_register (op[1], &imm_expr, GPR_SIZE == 64);
        }
       /* Fall through.  */
     case M_BEQL:
     case M_BNEL:
-      macro_build_branch_rsrt (mask, &offset_expr, sreg, treg);
+      macro_build_branch_rsrt (mask, &offset_expr, op[0], op[1]);
       break;
 
     case M_BGEL:
       likely = 1;
     case M_BGE:
-      if (treg == 0)
-       macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ, &offset_expr, sreg);
-      else if (sreg == 0)
-       macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, treg);
+      if (op[1] == 0)
+       macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ, &offset_expr, op[0]);
+      else if (op[0] == 0)
+       macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, op[1]);
       else
        {
          used_at = 1;
-         macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
+         macro_build (NULL, "slt", "d,v,t", AT, op[0], op[1]);
          macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
                                   &offset_expr, AT, ZERO);
        }
@@ -8270,14 +9878,14 @@ macro (struct mips_cl_insn *ip, char *str)
     case M_BLEZL:
     case M_BLTZL:
     case M_BLTZALL:
-      macro_build_branch_rs (mask, &offset_expr, sreg);
+      macro_build_branch_rs (mask, &offset_expr, op[0]);
       break;
 
     case M_BGTL_I:
       likely = 1;
     case M_BGT_I:
       /* Check for > max integer.  */
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number >= GPR_SMAX)
+      if (imm_expr.X_add_number >= GPR_SMAX)
        {
        do_false:
          /* Result is always false.  */
@@ -8287,36 +9895,34 @@ macro (struct mips_cl_insn *ip, char *str)
            macro_build_branch_rsrt (M_BNEL, &offset_expr, ZERO, ZERO);
          break;
        }
-      if (imm_expr.X_op != O_constant)
-       as_bad (_("Unsupported large constant"));
       ++imm_expr.X_add_number;
       /* FALLTHROUGH */
     case M_BGE_I:
     case M_BGEL_I:
       if (mask == M_BGEL_I)
        likely = 1;
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+      if (imm_expr.X_add_number == 0)
        {
          macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ,
-                                &offset_expr, sreg);
+                                &offset_expr, op[0]);
          break;
        }
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+      if (imm_expr.X_add_number == 1)
        {
          macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ,
-                                &offset_expr, sreg);
+                                &offset_expr, op[0]);
          break;
        }
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number <= GPR_SMIN)
+      if (imm_expr.X_add_number <= GPR_SMIN)
        {
        do_true:
          /* result is always true */
-         as_warn (_("Branch %s is always true"), ip->insn_mo->name);
+         as_warn (_("branch %s is always true"), ip->insn_mo->name);
          macro_build (&offset_expr, "b", "p");
          break;
        }
       used_at = 1;
-      set_at (sreg, 0);
+      set_at (op[0], 0);
       macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
                               &offset_expr, AT, ZERO);
       break;
@@ -8324,15 +9930,15 @@ macro (struct mips_cl_insn *ip, char *str)
     case M_BGEUL:
       likely = 1;
     case M_BGEU:
-      if (treg == 0)
+      if (op[1] == 0)
        goto do_true;
-      else if (sreg == 0)
+      else if (op[0] == 0)
        macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
-                                &offset_expr, ZERO, treg);
+                                &offset_expr, ZERO, op[1]);
       else
        {
          used_at = 1;
-         macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
+         macro_build (NULL, "sltu", "d,v,t", AT, op[0], op[1]);
          macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
                                   &offset_expr, AT, ZERO);
        }
@@ -8341,28 +9947,25 @@ macro (struct mips_cl_insn *ip, char *str)
     case M_BGTUL_I:
       likely = 1;
     case M_BGTU_I:
-      if (sreg == 0
-         || (HAVE_32BIT_GPRS
-             && imm_expr.X_op == O_constant
+      if (op[0] == 0
+         || (GPR_SIZE == 32
              && imm_expr.X_add_number == -1))
        goto do_false;
-      if (imm_expr.X_op != O_constant)
-       as_bad (_("Unsupported large constant"));
       ++imm_expr.X_add_number;
       /* FALLTHROUGH */
     case M_BGEU_I:
     case M_BGEUL_I:
       if (mask == M_BGEUL_I)
        likely = 1;
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+      if (imm_expr.X_add_number == 0)
        goto do_true;
-      else if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+      else if (imm_expr.X_add_number == 1)
        macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
-                                &offset_expr, sreg, ZERO);
+                                &offset_expr, op[0], ZERO);
       else
        {
          used_at = 1;
-         set_at (sreg, 1);
+         set_at (op[0], 1);
          macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
                                   &offset_expr, AT, ZERO);
        }
@@ -8371,14 +9974,14 @@ macro (struct mips_cl_insn *ip, char *str)
     case M_BGTL:
       likely = 1;
     case M_BGT:
-      if (treg == 0)
-       macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ, &offset_expr, sreg);
-      else if (sreg == 0)
-       macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, treg);
+      if (op[1] == 0)
+       macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ, &offset_expr, op[0]);
+      else if (op[0] == 0)
+       macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, op[1]);
       else
        {
          used_at = 1;
-         macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
+         macro_build (NULL, "slt", "d,v,t", AT, op[1], op[0]);
          macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
                                   &offset_expr, AT, ZERO);
        }
@@ -8387,15 +9990,15 @@ macro (struct mips_cl_insn *ip, char *str)
     case M_BGTUL:
       likely = 1;
     case M_BGTU:
-      if (treg == 0)
+      if (op[1] == 0)
        macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
-                                &offset_expr, sreg, ZERO);
-      else if (sreg == 0)
+                                &offset_expr, op[0], ZERO);
+      else if (op[0] == 0)
        goto do_false;
       else
        {
          used_at = 1;
-         macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
+         macro_build (NULL, "sltu", "d,v,t", AT, op[1], op[0]);
          macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
                                   &offset_expr, AT, ZERO);
        }
@@ -8404,14 +10007,14 @@ macro (struct mips_cl_insn *ip, char *str)
     case M_BLEL:
       likely = 1;
     case M_BLE:
-      if (treg == 0)
-       macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, sreg);
-      else if (sreg == 0)
-       macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ, &offset_expr, treg);
+      if (op[1] == 0)
+       macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, op[0]);
+      else if (op[0] == 0)
+       macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ, &offset_expr, op[1]);
       else
        {
          used_at = 1;
-         macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
+         macro_build (NULL, "slt", "d,v,t", AT, op[1], op[0]);
          macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
                                   &offset_expr, AT, ZERO);
        }
@@ -8420,24 +10023,22 @@ macro (struct mips_cl_insn *ip, char *str)
     case M_BLEL_I:
       likely = 1;
     case M_BLE_I:
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number >= GPR_SMAX)
+      if (imm_expr.X_add_number >= GPR_SMAX)
        goto do_true;
-      if (imm_expr.X_op != O_constant)
-       as_bad (_("Unsupported large constant"));
       ++imm_expr.X_add_number;
       /* FALLTHROUGH */
     case M_BLT_I:
     case M_BLTL_I:
       if (mask == M_BLTL_I)
        likely = 1;
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
-       macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, sreg);
-      else if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
-       macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, sreg);
+      if (imm_expr.X_add_number == 0)
+       macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, op[0]);
+      else if (imm_expr.X_add_number == 1)
+       macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, op[0]);
       else
        {
          used_at = 1;
-         set_at (sreg, 0);
+         set_at (op[0], 0);
          macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
                                   &offset_expr, AT, ZERO);
        }
@@ -8446,15 +10047,15 @@ macro (struct mips_cl_insn *ip, char *str)
     case M_BLEUL:
       likely = 1;
     case M_BLEU:
-      if (treg == 0)
+      if (op[1] == 0)
        macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
-                                &offset_expr, sreg, ZERO);
-      else if (sreg == 0)
+                                &offset_expr, op[0], ZERO);
+      else if (op[0] == 0)
        goto do_true;
       else
        {
          used_at = 1;
-         macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
+         macro_build (NULL, "sltu", "d,v,t", AT, op[1], op[0]);
          macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
                                   &offset_expr, AT, ZERO);
        }
@@ -8463,28 +10064,25 @@ macro (struct mips_cl_insn *ip, char *str)
     case M_BLEUL_I:
       likely = 1;
     case M_BLEU_I:
-      if (sreg == 0
-         || (HAVE_32BIT_GPRS
-             && imm_expr.X_op == O_constant
+      if (op[0] == 0
+         || (GPR_SIZE == 32
              && imm_expr.X_add_number == -1))
        goto do_true;
-      if (imm_expr.X_op != O_constant)
-       as_bad (_("Unsupported large constant"));
       ++imm_expr.X_add_number;
       /* FALLTHROUGH */
     case M_BLTU_I:
     case M_BLTUL_I:
       if (mask == M_BLTUL_I)
        likely = 1;
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+      if (imm_expr.X_add_number == 0)
        goto do_false;
-      else if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+      else if (imm_expr.X_add_number == 1)
        macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
-                                &offset_expr, sreg, ZERO);
+                                &offset_expr, op[0], ZERO);
       else
        {
          used_at = 1;
-         set_at (sreg, 1);
+         set_at (op[0], 1);
          macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
                                   &offset_expr, AT, ZERO);
        }
@@ -8493,14 +10091,14 @@ macro (struct mips_cl_insn *ip, char *str)
     case M_BLTL:
       likely = 1;
     case M_BLT:
-      if (treg == 0)
-       macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, sreg);
-      else if (sreg == 0)
-       macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ, &offset_expr, treg);
+      if (op[1] == 0)
+       macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, op[0]);
+      else if (op[0] == 0)
+       macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ, &offset_expr, op[1]);
       else
        {
          used_at = 1;
-         macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
+         macro_build (NULL, "slt", "d,v,t", AT, op[0], op[1]);
          macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
                                   &offset_expr, AT, ZERO);
        }
@@ -8509,116 +10107,20 @@ macro (struct mips_cl_insn *ip, char *str)
     case M_BLTUL:
       likely = 1;
     case M_BLTU:
-      if (treg == 0)
+      if (op[1] == 0)
        goto do_false;
-      else if (sreg == 0)
+      else if (op[0] == 0)
        macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
-                                &offset_expr, ZERO, treg);
+                                &offset_expr, ZERO, op[1]);
       else
        {
          used_at = 1;
-         macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
+         macro_build (NULL, "sltu", "d,v,t", AT, op[0], op[1]);
          macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
                                   &offset_expr, AT, ZERO);
        }
       break;
 
-    case M_DEXT:
-      {
-       /* Use unsigned arithmetic.  */
-       addressT pos;
-       addressT size;
-
-       if (imm_expr.X_op != O_constant || imm2_expr.X_op != O_constant)
-         {
-           as_bad (_("Unsupported large constant"));
-           pos = size = 1;
-         }
-       else
-         {
-           pos = imm_expr.X_add_number;
-           size = imm2_expr.X_add_number;
-         }
-
-       if (pos > 63)
-         {
-           report_bad_range (ip, 3, pos, 0, 63, FALSE);
-           pos = 1;
-         }
-       if (size == 0 || size > 64 || (pos + size - 1) > 63)
-         {
-           report_bad_field (pos, size);
-           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 ((expressionS *) NULL, s, fmt, treg, sreg, (int) pos,
-                    (int) (size - 1));
-      }
-      break;
-
-    case M_DINS:
-      {
-       /* Use unsigned arithmetic.  */
-       addressT pos;
-       addressT size;
-
-       if (imm_expr.X_op != O_constant || imm2_expr.X_op != O_constant)
-         {
-           as_bad (_("Unsupported large constant"));
-           pos = size = 1;
-         }
-       else
-         {
-           pos = imm_expr.X_add_number;
-           size = imm2_expr.X_add_number;
-         }
-
-       if (pos > 63)
-         {
-           report_bad_range (ip, 3, pos, 0, 63, FALSE);
-           pos = 1;
-         }
-       if (size == 0 || size > 64 || (pos + size - 1) > 63)
-         {
-           report_bad_field (pos, size);
-           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 ((expressionS *) NULL, s, fmt, treg, sreg, (int) pos,
-                    (int) (pos + size - 1));
-      }
-      break;
-
     case M_DDIV_3:
       dbl = 1;
     case M_DIV_3:
@@ -8629,9 +10131,9 @@ macro (struct mips_cl_insn *ip, char *str)
     case M_REM_3:
       s = "mfhi";
     do_div3:
-      if (treg == 0)
+      if (op[2] == 0)
        {
-         as_warn (_("Divide by zero."));
+         as_warn (_("divide by zero"));
          if (mips_trap)
            macro_build (NULL, "teq", TRAP_FMT, ZERO, ZERO, 7);
          else
@@ -8642,8 +10144,8 @@ macro (struct mips_cl_insn *ip, char *str)
       start_noreorder ();
       if (mips_trap)
        {
-         macro_build (NULL, "teq", TRAP_FMT, treg, ZERO, 7);
-         macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
+         macro_build (NULL, "teq", TRAP_FMT, op[2], ZERO, 7);
+         macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", op[1], op[2]);
        }
       else
        {
@@ -8651,8 +10153,8 @@ macro (struct mips_cl_insn *ip, char *str)
            micromips_label_expr (&label_expr);
          else
            label_expr.X_add_number = 8;
-         macro_build (&label_expr, "bne", "s,t,p", treg, ZERO);
-         macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
+         macro_build (&label_expr, "bne", "s,t,p", op[2], ZERO);
+         macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", op[1], op[2]);
          macro_build (NULL, "break", BRK_FMT, 7);
          if (mips_opts.micromips)
            micromips_add_label ();
@@ -8664,7 +10166,7 @@ macro (struct mips_cl_insn *ip, char *str)
        micromips_label_expr (&label_expr);
       else
        label_expr.X_add_number = mips_trap ? (dbl ? 12 : 8) : (dbl ? 20 : 16);
-      macro_build (&label_expr, "bne", "s,t,p", treg, AT);
+      macro_build (&label_expr, "bne", "s,t,p", op[2], AT);
       if (dbl)
        {
          expr1.X_add_number = 1;
@@ -8678,7 +10180,7 @@ macro (struct mips_cl_insn *ip, char *str)
        }
       if (mips_trap)
        {
-         macro_build (NULL, "teq", TRAP_FMT, sreg, AT, 6);
+         macro_build (NULL, "teq", TRAP_FMT, op[1], AT, 6);
          /* We want to close the noreorder block as soon as possible, so
             that later insns are available for delay slot filling.  */
          end_noreorder ();
@@ -8689,7 +10191,7 @@ macro (struct mips_cl_insn *ip, char *str)
            micromips_label_expr (&label_expr);
          else
            label_expr.X_add_number = 8;
-         macro_build (&label_expr, "bne", "s,t,p", sreg, AT);
+         macro_build (&label_expr, "bne", "s,t,p", op[1], AT);
          macro_build (NULL, "nop", "");
 
          /* We want to close the noreorder block as soon as possible, so
@@ -8700,7 +10202,7 @@ macro (struct mips_cl_insn *ip, char *str)
        }
       if (mips_opts.micromips)
        micromips_add_label ();
-      macro_build (NULL, s, MFHL_FMT, dreg);
+      macro_build (NULL, s, MFHL_FMT, op[0]);
       break;
 
     case M_DIV_3I:
@@ -8739,40 +10241,36 @@ macro (struct mips_cl_insn *ip, char *str)
       s = "ddivu";
       s2 = "mfhi";
     do_divi:
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+      if (imm_expr.X_add_number == 0)
        {
-         as_warn (_("Divide by zero."));
+         as_warn (_("divide by zero"));
          if (mips_trap)
            macro_build (NULL, "teq", TRAP_FMT, ZERO, ZERO, 7);
          else
            macro_build (NULL, "break", BRK_FMT, 7);
          break;
        }
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+      if (imm_expr.X_add_number == 1)
        {
          if (strcmp (s2, "mflo") == 0)
-           move_register (dreg, sreg);
+           move_register (op[0], op[1]);
          else
-           move_register (dreg, ZERO);
+           move_register (op[0], ZERO);
          break;
        }
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number == -1
-         && s[strlen (s) - 1] != 'u')
+      if (imm_expr.X_add_number == -1 && s[strlen (s) - 1] != 'u')
        {
          if (strcmp (s2, "mflo") == 0)
-           {
-             macro_build (NULL, dbl ? "dneg" : "neg", "d,w", dreg, sreg);
-           }
+           macro_build (NULL, dbl ? "dneg" : "neg", "d,w", op[0], op[1]);
          else
-           move_register (dreg, ZERO);
+           move_register (op[0], ZERO);
          break;
        }
 
       used_at = 1;
       load_register (AT, &imm_expr, dbl);
-      macro_build (NULL, s, "z,s,t", sreg, AT);
-      macro_build (NULL, s2, MFHL_FMT, dreg);
+      macro_build (NULL, s, "z,s,t", op[1], AT);
+      macro_build (NULL, s2, MFHL_FMT, op[0]);
       break;
 
     case M_DIVU_3:
@@ -8794,8 +10292,8 @@ macro (struct mips_cl_insn *ip, char *str)
       start_noreorder ();
       if (mips_trap)
        {
-         macro_build (NULL, "teq", TRAP_FMT, treg, ZERO, 7);
-         macro_build (NULL, s, "z,s,t", sreg, treg);
+         macro_build (NULL, "teq", TRAP_FMT, op[2], ZERO, 7);
+         macro_build (NULL, s, "z,s,t", op[1], op[2]);
          /* We want to close the noreorder block as soon as possible, so
             that later insns are available for delay slot filling.  */
          end_noreorder ();
@@ -8806,8 +10304,8 @@ macro (struct mips_cl_insn *ip, char *str)
            micromips_label_expr (&label_expr);
          else
            label_expr.X_add_number = 8;
-         macro_build (&label_expr, "bne", "s,t,p", treg, ZERO);
-         macro_build (NULL, s, "z,s,t", sreg, treg);
+         macro_build (&label_expr, "bne", "s,t,p", op[2], ZERO);
+         macro_build (NULL, s, "z,s,t", op[1], op[2]);
 
          /* We want to close the noreorder block as soon as possible, so
             that later insns are available for delay slot filling.  */
@@ -8816,7 +10314,7 @@ macro (struct mips_cl_insn *ip, char *str)
          if (mips_opts.micromips)
            micromips_add_label ();
        }
-      macro_build (NULL, s2, MFHL_FMT, dreg);
+      macro_build (NULL, s2, MFHL_FMT, op[0]);
       break;
 
     case M_DLCA_AB:
@@ -8831,7 +10329,8 @@ macro (struct mips_cl_insn *ip, char *str)
       /* Load the address of a symbol into a register.  If breg is not
         zero, we then add a base register to it.  */
 
-      if (dbl && HAVE_32BIT_GPRS)
+      breg = op[2];
+      if (dbl && GPR_SIZE == 32)
        as_warn (_("dla used to load 32-bit register"));
 
       if (!dbl && HAVE_64BIT_OBJECTS)
@@ -8839,25 +10338,23 @@ macro (struct mips_cl_insn *ip, char *str)
 
       if (small_offset_p (0, align, 16))
        {
-         macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", treg, breg,
+         macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", op[0], breg,
                       -1, offset_reloc[0], offset_reloc[1], offset_reloc[2]);
          break;
        }
 
-      if (mips_opts.at && (treg == breg))
+      if (mips_opts.at && (op[0] == breg))
        {
          tempreg = AT;
          used_at = 1;
        }
       else
-       {
-         tempreg = treg;
-       }
+       tempreg = op[0];
 
       if (offset_expr.X_op != O_symbol
          && offset_expr.X_op != O_constant)
        {
-         as_bad (_("Expression too complex"));
+         as_bad (_("expression too complex"));
          offset_expr.X_op = O_constant;
        }
 
@@ -8945,7 +10442,7 @@ macro (struct mips_cl_insn *ip, char *str)
                  relax_switch ();
                }
              if (!IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
-               as_bad (_("Offset too large"));
+               as_bad (_("offset too large"));
              macro_build_lui (&offset_expr, tempreg);
              macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
                           tempreg, tempreg, BFD_RELOC_LO16);
@@ -9036,13 +10533,13 @@ macro (struct mips_cl_insn *ip, char *str)
                 current AT (from the global offset table) and the
                 register into the register now, and pretend we were
                 not using a base register.  */
-             if (breg == treg)
+             if (breg == op[0])
                {
                  load_delay_nop ();
                  macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                              treg, AT, breg);
+                              op[0], AT, breg);
                  breg = 0;
-                 tempreg = treg;
+                 tempreg = op[0];
                }
              add_got_offset_hilo (tempreg, &offset_expr, AT);
              used_at = 1;
@@ -9092,6 +10589,8 @@ macro (struct mips_cl_insn *ip, char *str)
                }
              else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
                {
+                 unsigned int dreg;
+
                  /* If we are going to add in a base register, and the
                     target register and the base register are the same,
                     then we are using AT as a temporary register.  Since
@@ -9099,14 +10598,14 @@ macro (struct mips_cl_insn *ip, char *str)
                     current AT (from the global offset table) and the
                     register into the register now, and pretend we were
                     not using a base register.  */
-                 if (breg != treg)
+                 if (breg != op[0])
                    dreg = tempreg;
                  else
                    {
                      gas_assert (tempreg == AT);
                      macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                                  treg, AT, breg);
-                     dreg = treg;
+                                  op[0], AT, breg);
+                     dreg = op[0];
                      add_breg_early = 1;
                    }
 
@@ -9127,9 +10626,9 @@ macro (struct mips_cl_insn *ip, char *str)
              if (add_breg_early)
                {
                  macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                              treg, tempreg, breg);
+                              op[0], tempreg, breg);
                  breg = 0;
-                 tempreg = treg;
+                 tempreg = op[0];
                }
              relax_end ();
            }
@@ -9231,6 +10730,8 @@ macro (struct mips_cl_insn *ip, char *str)
            }
          else
            {
+             unsigned int dreg;
+
              /* If we are going to add in a base register, and the
                 target register and the base register are the same,
                 then we are using AT as a temporary register.  Since
@@ -9238,15 +10739,15 @@ macro (struct mips_cl_insn *ip, char *str)
                 current AT (from the global offset table) and the
                 register into the register now, and pretend we were
                 not using a base register.  */
-             if (breg != treg)
+             if (breg != op[0])
                dreg = tempreg;
              else
                {
                  gas_assert (tempreg == AT);
                  load_delay_nop ();
                  macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                              treg, AT, breg);
-                 dreg = treg;
+                              op[0], AT, breg);
+                 dreg = op[0];
                }
 
              load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
@@ -9280,15 +10781,15 @@ macro (struct mips_cl_insn *ip, char *str)
            }
          else
            {
-             if (breg == treg)
+             if (breg == op[0])
                {
                  /* We must add in the base register now, as in the
                     external symbol case.  */
                  gas_assert (tempreg == AT);
                  load_delay_nop ();
                  macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                              treg, AT, breg);
-                 tempreg = treg;
+                              op[0], AT, breg);
+                 tempreg = op[0];
                  /* We set breg to 0 because we have arranged to add
                     it in in both cases.  */
                  breg = 0;
@@ -9367,6 +10868,8 @@ macro (struct mips_cl_insn *ip, char *str)
            }
          else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
            {
+             unsigned int dreg;
+
              /* If we are going to add in a base register, and the
                 target register and the base register are the same,
                 then we are using AT as a temporary register.  Since
@@ -9374,14 +10877,14 @@ macro (struct mips_cl_insn *ip, char *str)
                 current AT (from the global offset table) and the
                 register into the register now, and pretend we were
                 not using a base register.  */
-             if (breg != treg)
+             if (breg != op[0])
                dreg = tempreg;
              else
                {
                  gas_assert (tempreg == AT);
                  macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                              treg, AT, breg);
-                 dreg = treg;
+                              op[0], AT, breg);
+                 dreg = op[0];
                  add_breg_early = 1;
                }
 
@@ -9402,9 +10905,9 @@ macro (struct mips_cl_insn *ip, char *str)
          if (add_breg_early)
            {
              macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                          treg, tempreg, breg);
+                          op[0], tempreg, breg);
              breg = 0;
-             tempreg = treg;
+             tempreg = op[0];
            }
          relax_end ();
        }
@@ -9412,12 +10915,12 @@ macro (struct mips_cl_insn *ip, char *str)
        abort ();
 
       if (breg != 0)
-       macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, tempreg, breg);
+       macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", op[0], tempreg, breg);
       break;
 
     case M_MSGSND:
       gas_assert (!mips_opts.micromips);
-      macro_build (NULL, "c2", "C", (treg << 16) | 0x01);
+      macro_build (NULL, "c2", "C", (op[0] << 16) | 0x01);
       break;
 
     case M_MSGLD:
@@ -9427,7 +10930,7 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_MSGLD_T:
       gas_assert (!mips_opts.micromips);
-      macro_build (NULL, "c2", "C", (treg << 16) | 0x02);
+      macro_build (NULL, "c2", "C", (op[0] << 16) | 0x02);
       break;
 
     case M_MSGWAIT:
@@ -9437,7 +10940,7 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_MSGWAIT_T:
       gas_assert (!mips_opts.micromips);
-      macro_build (NULL, "c2", "C", (treg << 16) | 0x03);
+      macro_build (NULL, "c2", "C", (op[0] << 16) | 0x03);
       break;
 
     case M_J_A:
@@ -9454,19 +10957,21 @@ macro (struct mips_cl_insn *ip, char *str)
         generating PIC code they expand to multi-instruction
         sequences.  Normally they are simple instructions.  */
     case M_JALS_1:
-      dreg = RA;
+      op[1] = op[0];
+      op[0] = RA;
       /* Fall through.  */
     case M_JALS_2:
       gas_assert (mips_opts.micromips);
       if (mips_opts.insn32)
        {
-         as_bad (_("Opcode not supported in the `insn32' mode `%s'"), str);
+         as_bad (_("opcode not supported in the `insn32' mode `%s'"), str);
          break;
        }
       jals = 1;
       goto jal;
     case M_JAL_1:
-      dreg = RA;
+      op[1] = op[0];
+      op[0] = RA;
       /* Fall through.  */
     case M_JAL_2:
     jal:
@@ -9475,18 +10980,18 @@ macro (struct mips_cl_insn *ip, char *str)
          s = jals ? "jalrs" : "jalr";
          if (mips_opts.micromips
              && !mips_opts.insn32
-             && dreg == RA
+             && op[0] == RA
              && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
-           macro_build (NULL, s, "mj", sreg);
+           macro_build (NULL, s, "mj", op[1]);
          else
-           macro_build (NULL, s, JALR_FMT, dreg, sreg);
+           macro_build (NULL, s, JALR_FMT, op[0], op[1]);
        }
       else
        {
          int cprestore = (mips_pic == SVR4_PIC && !HAVE_NEWABI
                           && mips_cprestore_offset >= 0);
 
-         if (sreg != PIC_CALL_REG)
+         if (op[1] != PIC_CALL_REG)
            as_warn (_("MIPS PIC call to register other than $25"));
 
          s = ((mips_opts.micromips
@@ -9495,26 +11000,26 @@ macro (struct mips_cl_insn *ip, char *str)
               ? "jalrs" : "jalr");
          if (mips_opts.micromips
              && !mips_opts.insn32
-             && dreg == RA
+             && op[0] == RA
              && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
-           macro_build (NULL, s, "mj", sreg);
+           macro_build (NULL, s, "mj", op[1]);
          else
-           macro_build (NULL, s, JALR_FMT, dreg, sreg);
+           macro_build (NULL, s, JALR_FMT, op[0], op[1]);
          if (mips_pic == SVR4_PIC && !HAVE_NEWABI)
            {
              if (mips_cprestore_offset < 0)
-               as_warn (_("No .cprestore pseudo-op used in PIC code"));
+               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"));
+                     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"));
+                     as_warn (_("no .cprestore pseudo-op used in PIC code"));
                      /* Quiet this warning.  */
                      mips_cprestore_valid = 1;
                    }
@@ -9535,7 +11040,7 @@ macro (struct mips_cl_insn *ip, char *str)
       gas_assert (mips_opts.micromips);
       if (mips_opts.insn32)
        {
-         as_bad (_("Opcode not supported in the `insn32' mode `%s'"), str);
+         as_bad (_("opcode not supported in the `insn32' mode `%s'"), str);
          break;
        }
       jals = 1;
@@ -9647,18 +11152,18 @@ macro (struct mips_cl_insn *ip, char *str)
              macro_build_jalr (&offset_expr, mips_cprestore_offset >= 0);
 
              if (mips_cprestore_offset < 0)
-               as_warn (_("No .cprestore pseudo-op used in PIC code"));
+               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"));
+                     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"));
+                     as_warn (_("no .cprestore pseudo-op used in PIC code"));
                      /* Quiet this warning.  */
                      mips_cprestore_valid = 1;
                    }
@@ -9673,7 +11178,7 @@ macro (struct mips_cl_insn *ip, char *str)
            }
        }
       else if (mips_pic == VXWORKS_PIC)
-       as_bad (_("Non-PIC jump used in PIC library"));
+       as_bad (_("non-PIC jump used in PIC library"));
       else
        abort ();
 
@@ -9751,13 +11256,11 @@ macro (struct mips_cl_insn *ip, char *str)
       goto ld_st;
     case M_ACLR_AB:
       s = "aclr";
-      treg = EXTRACT_OPERAND (mips_opts.micromips, 3BITPOS, *ip);
       fmt = "\\,~(b)";
       offbits = 12;
       goto ld_st;
     case M_ASET_AB:
       s = "aset";
-      treg = EXTRACT_OPERAND (mips_opts.micromips, 3BITPOS, *ip);
       fmt = "\\,~(b)";
       offbits = 12;
       goto ld_st;
@@ -9797,7 +11300,9 @@ macro (struct mips_cl_insn *ip, char *str)
     case M_LWC2_AB:
       s = "lwc2";
       fmt = COP12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
+      offbits = (mips_opts.micromips ? 12
+                : ISA_IS_R6 (mips_opts.isa) ? 11
+                : 16);
       /* Itbl support may require additional care here.  */
       coproc = 1;
       goto ld_st;
@@ -9827,13 +11332,15 @@ macro (struct mips_cl_insn *ip, char *str)
     case M_LDC2_AB:
       s = "ldc2";
       fmt = COP12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
+      offbits = (mips_opts.micromips ? 12
+                : ISA_IS_R6 (mips_opts.isa) ? 11
+                : 16);
       /* Itbl support may require additional care here.  */
       coproc = 1;
       goto ld_st;
     case M_LQC2_AB:
       s = "lqc2";
-      fmt = "E,o(b)";
+      fmt = "+7,o(b)";
       /* Itbl support may require additional care here.  */
       coproc = 1;
       goto ld_st;
@@ -9855,13 +11362,17 @@ macro (struct mips_cl_insn *ip, char *str)
       goto ld_st;
     case M_LL_AB:
       s = "ll";
-      fmt = MEM12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
+      fmt = LL_SC_FMT;
+      offbits = (mips_opts.micromips ? 12
+                : ISA_IS_R6 (mips_opts.isa) ? 9
+                : 16);
       goto ld;
     case M_LLD_AB:
       s = "lld";
-      fmt = MEM12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
+      fmt = LL_SC_FMT;
+      offbits = (mips_opts.micromips ? 12
+                : ISA_IS_R6 (mips_opts.isa) ? 9
+                : 16);
       goto ld;
     case M_LWU_AB:
       s = "lwu";
@@ -9897,10 +11408,10 @@ macro (struct mips_cl_insn *ip, char *str)
 
     ld:
       /* We don't want to use $0 as tempreg.  */
-      if (breg == treg + lp || treg + lp == ZERO)
+      if (op[2] == op[0] + lp || op[0] + lp == ZERO)
        goto ld_st;
       else
-       tempreg = treg + lp;
+       tempreg = op[0] + lp;
       goto ld_noat;
 
     case M_SB_AB:
@@ -9931,7 +11442,9 @@ macro (struct mips_cl_insn *ip, char *str)
     case M_SWC2_AB:
       s = "swc2";
       fmt = COP12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
+      offbits = (mips_opts.micromips ? 12
+                : ISA_IS_R6 (mips_opts.isa) ? 11
+                : 16);
       /* Itbl support may require additional care here.  */
       coproc = 1;
       goto ld_st;
@@ -9954,18 +11467,26 @@ macro (struct mips_cl_insn *ip, char *str)
       goto ld_st;
     case M_SC_AB:
       s = "sc";
-      fmt = MEM12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
+      fmt = LL_SC_FMT;
+      offbits = (mips_opts.micromips ? 12
+                : ISA_IS_R6 (mips_opts.isa) ? 9
+                : 16);
       goto ld_st;
     case M_SCD_AB:
       s = "scd";
-      fmt = MEM12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
+      fmt = LL_SC_FMT;
+      offbits = (mips_opts.micromips ? 12
+                : ISA_IS_R6 (mips_opts.isa) ? 9
+                : 16);
       goto ld_st;
     case M_CACHE_AB:
       s = "cache";
-      fmt = mips_opts.micromips ? "k,~(b)" : "k,o(b)";
-      offbits = (mips_opts.micromips ? 12 : 16);
+      fmt = (mips_opts.micromips ? "k,~(b)"
+            : ISA_IS_R6 (mips_opts.isa) ? "k,+j(b)"
+            : "k,o(b)");
+      offbits = (mips_opts.micromips ? 12
+                : ISA_IS_R6 (mips_opts.isa) ? 9
+                : 16);
       goto ld_st;
     case M_CACHEE_AB:
       s = "cachee";
@@ -9974,8 +11495,12 @@ macro (struct mips_cl_insn *ip, char *str)
       goto ld_st;
     case M_PREF_AB:
       s = "pref";
-      fmt = !mips_opts.micromips ? "k,o(b)" : "k,~(b)";
-      offbits = (mips_opts.micromips ? 12 : 16);
+      fmt = (mips_opts.micromips ? "k,~(b)"
+            : ISA_IS_R6 (mips_opts.isa) ? "k,+j(b)"
+            : "k,o(b)");
+      offbits = (mips_opts.micromips ? 12
+                : ISA_IS_R6 (mips_opts.isa) ? 9
+                : 16);
       goto ld_st;
     case M_PREFE_AB:
       s = "prefe";
@@ -9991,13 +11516,15 @@ macro (struct mips_cl_insn *ip, char *str)
     case M_SDC2_AB:
       s = "sdc2";
       fmt = COP12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
+      offbits = (mips_opts.micromips ? 12
+                : ISA_IS_R6 (mips_opts.isa) ? 11
+                : 16);
       /* Itbl support may require additional care here.  */
       coproc = 1;
       goto ld_st;
     case M_SQC2_AB:
       s = "sqc2";
-      fmt = "E,o(b)";
+      fmt = "+7,o(b)";
       /* Itbl support may require additional care here.  */
       coproc = 1;
       goto ld_st;
@@ -10045,20 +11572,21 @@ macro (struct mips_cl_insn *ip, char *str)
     ld_st:
       tempreg = AT;
     ld_noat:
+      breg = op[2];
       if (small_offset_p (0, align, 16))
        {
          /* The first case exists for M_LD_AB and M_SD_AB, which are
             macros for o32 but which should act like normal instructions
             otherwise.  */
          if (offbits == 16)
-           macro_build (&offset_expr, s, fmt, treg, -1, offset_reloc[0],
+           macro_build (&offset_expr, s, fmt, op[0], -1, offset_reloc[0],
                         offset_reloc[1], offset_reloc[2], breg);
          else if (small_offset_p (0, align, offbits))
            {
              if (offbits == 0)
-               macro_build (NULL, s, fmt, treg, breg);
+               macro_build (NULL, s, fmt, op[0], breg);
              else
-               macro_build (NULL, s, fmt, treg,
+               macro_build (NULL, s, fmt, op[0],
                             (int) offset_expr.X_add_number, breg);
            }
          else
@@ -10069,9 +11597,9 @@ macro (struct mips_cl_insn *ip, char *str)
                           tempreg, breg, -1, offset_reloc[0],
                           offset_reloc[1], offset_reloc[2]);
              if (offbits == 0)
-               macro_build (NULL, s, fmt, treg, tempreg);
+               macro_build (NULL, s, fmt, op[0], tempreg);
              else
-               macro_build (NULL, s, fmt, treg, 0, tempreg);
+               macro_build (NULL, s, fmt, op[0], 0, tempreg);
            }
          break;
        }
@@ -10082,7 +11610,7 @@ macro (struct mips_cl_insn *ip, char *str)
       if (offset_expr.X_op != O_constant
          && offset_expr.X_op != O_symbol)
        {
-         as_bad (_("Expression too complex"));
+         as_bad (_("expression too complex"));
          offset_expr.X_op = O_constant;
        }
 
@@ -10092,7 +11620,7 @@ macro (struct mips_cl_insn *ip, char *str)
          char value [32];
 
          sprintf_vma (value, offset_expr.X_add_number);
-         as_bad (_("Number (0x%s) larger than 32 bits"), value);
+         as_bad (_("number (0x%s) larger than 32 bits"), value);
        }
 
       /* A constant expression in PIC code can be handled just as it
@@ -10112,12 +11640,12 @@ macro (struct mips_cl_insn *ip, char *str)
              if (offset_expr.X_add_number != 0)
                macro_build (&offset_expr, ADDRESS_ADDI_INSN,
                             "t,r,j", tempreg, tempreg, BFD_RELOC_LO16);
-             macro_build (NULL, s, fmt, treg, tempreg);
+             macro_build (NULL, s, fmt, op[0], tempreg);
            }
          else if (offbits == 16)
-           macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16, tempreg);
+           macro_build (&offset_expr, s, fmt, op[0], BFD_RELOC_LO16, tempreg);
          else
-           macro_build (NULL, s, fmt, treg,
+           macro_build (NULL, s, fmt, op[0],
                         (int) offset_expr.X_add_number, tempreg);
        }
       else if (offbits != 16)
@@ -10130,29 +11658,29 @@ macro (struct mips_cl_insn *ip, char *str)
            macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
                         tempreg, tempreg, breg);
          if (offbits == 0)
-           macro_build (NULL, s, fmt, treg, tempreg);
+           macro_build (NULL, s, fmt, op[0], tempreg);
          else
-           macro_build (NULL, s, fmt, treg, 0, tempreg);
+           macro_build (NULL, s, fmt, op[0], 0, tempreg);
        }
       else if (mips_pic == NO_PIC)
        {
          /* If this is a reference to a GP relative symbol, and there
             is no base register, we want
-              <op>     $treg,<sym>($gp)        (BFD_RELOC_GPREL16)
+              <op>     op[0],<sym>($gp)        (BFD_RELOC_GPREL16)
             Otherwise, if there is no base register, we want
               lui      $tempreg,<sym>          (BFD_RELOC_HI16_S)
-              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+              <op>     op[0],<sym>($tempreg)   (BFD_RELOC_LO16)
             If we have a constant, we need two instructions anyhow,
             so we always use the latter form.
 
             If we have a base register, and this is a reference to a
             GP relative symbol, we want
               addu     $tempreg,$breg,$gp
-              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_GPREL16)
+              <op>     op[0],<sym>($tempreg)   (BFD_RELOC_GPREL16)
             Otherwise we want
               lui      $tempreg,<sym>          (BFD_RELOC_HI16_S)
               addu     $tempreg,$tempreg,$breg
-              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+              <op>     op[0],<sym>($tempreg)   (BFD_RELOC_LO16)
             With a constant we always use the latter case.
 
             With 64bit address space and no base register and $at usable,
@@ -10162,7 +11690,7 @@ macro (struct mips_cl_insn *ip, char *str)
               daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
               dsll32   $tempreg,0
               daddu    $tempreg,$at
-              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+              <op>     op[0],<sym>($tempreg)   (BFD_RELOC_LO16)
             If we have a base register, we want
               lui      $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
               lui      $at,<sym>               (BFD_RELOC_HI16_S)
@@ -10170,7 +11698,7 @@ macro (struct mips_cl_insn *ip, char *str)
               daddu    $at,$breg
               dsll32   $tempreg,0
               daddu    $tempreg,$at
-              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+              <op>     op[0],<sym>($tempreg)   (BFD_RELOC_LO16)
 
             Without $at we can't generate the optimal path for superscalar
             processors here since this would require two temporary registers.
@@ -10179,7 +11707,7 @@ macro (struct mips_cl_insn *ip, char *str)
               dsll     $tempreg,16
               daddiu   $tempreg,<sym>          (BFD_RELOC_HI16_S)
               dsll     $tempreg,16
-              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+              <op>     op[0],<sym>($tempreg)   (BFD_RELOC_LO16)
             If we have a base register, we want
               lui      $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
               daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
@@ -10187,7 +11715,7 @@ macro (struct mips_cl_insn *ip, char *str)
               daddiu   $tempreg,<sym>          (BFD_RELOC_HI16_S)
               dsll     $tempreg,16
               daddu    $tempreg,$tempreg,$breg
-              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+              <op>     op[0],<sym>($tempreg)   (BFD_RELOC_LO16)
 
             For GP relative symbols in 64bit address space we can use
             the same sequence as in 32bit address space.  */
@@ -10199,14 +11727,14 @@ macro (struct mips_cl_insn *ip, char *str)
                  relax_start (offset_expr.X_add_symbol);
                  if (breg == 0)
                    {
-                     macro_build (&offset_expr, s, fmt, treg,
+                     macro_build (&offset_expr, s, fmt, op[0],
                                   BFD_RELOC_GPREL16, mips_gp_register);
                    }
                  else
                    {
                      macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
                                   tempreg, breg, mips_gp_register);
-                     macro_build (&offset_expr, s, fmt, treg,
+                     macro_build (&offset_expr, s, fmt, op[0],
                                   BFD_RELOC_GPREL16, tempreg);
                    }
                  relax_switch ();
@@ -10224,7 +11752,7 @@ macro (struct mips_cl_insn *ip, char *str)
                    macro_build (NULL, "daddu", "d,v,t", AT, AT, breg);
                  macro_build (NULL, "dsll32", SHFT_FMT, tempreg, tempreg, 0);
                  macro_build (NULL, "daddu", "d,v,t", tempreg, tempreg, AT);
-                 macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16,
+                 macro_build (&offset_expr, s, fmt, op[0], BFD_RELOC_LO16,
                               tempreg);
                  used_at = 1;
                }
@@ -10241,7 +11769,7 @@ macro (struct mips_cl_insn *ip, char *str)
                  if (breg != 0)
                    macro_build (NULL, "daddu", "d,v,t",
                                 tempreg, tempreg, breg);
-                 macro_build (&offset_expr, s, fmt, treg,
+                 macro_build (&offset_expr, s, fmt, op[0],
                               BFD_RELOC_LO16, tempreg);
                }
 
@@ -10256,12 +11784,12 @@ macro (struct mips_cl_insn *ip, char *str)
                  && !nopic_need_relax (offset_expr.X_add_symbol, 1))
                {
                  relax_start (offset_expr.X_add_symbol);
-                 macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_GPREL16,
+                 macro_build (&offset_expr, s, fmt, op[0], BFD_RELOC_GPREL16,
                               mips_gp_register);
                  relax_switch ();
                }
              macro_build_lui (&offset_expr, tempreg);
-             macro_build (&offset_expr, s, fmt, treg,
+             macro_build (&offset_expr, s, fmt, op[0],
                           BFD_RELOC_LO16, tempreg);
              if (mips_relax.sequence)
                relax_end ();
@@ -10274,14 +11802,14 @@ macro (struct mips_cl_insn *ip, char *str)
                  relax_start (offset_expr.X_add_symbol);
                  macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
                               tempreg, breg, mips_gp_register);
-                 macro_build (&offset_expr, s, fmt, treg,
+                 macro_build (&offset_expr, s, fmt, op[0],
                               BFD_RELOC_GPREL16, tempreg);
                  relax_switch ();
                }
              macro_build_lui (&offset_expr, tempreg);
              macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
                           tempreg, tempreg, breg);
-             macro_build (&offset_expr, s, fmt, treg,
+             macro_build (&offset_expr, s, fmt, op[0],
                           BFD_RELOC_LO16, tempreg);
              if (mips_relax.sequence)
                relax_end ();
@@ -10294,16 +11822,16 @@ macro (struct mips_cl_insn *ip, char *str)
          /* If this is a reference to an external symbol, we want
               lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
               nop
-              <op>     $treg,0($tempreg)
+              <op>     op[0],0($tempreg)
             Otherwise we want
               lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
               nop
               addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
-              <op>     $treg,0($tempreg)
+              <op>     op[0],0($tempreg)
 
             For NewABI, we want
               lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_PAGE)
-              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_MIPS_GOT_OFST)
+              <op>     op[0],<sym>($tempreg)   (BFD_RELOC_MIPS_GOT_OFST)
 
             If there is a base register, we add it to $tempreg before
             the <op>.  If there is a constant, we stick it in the
@@ -10319,7 +11847,7 @@ macro (struct mips_cl_insn *ip, char *str)
              if (breg != 0)
                macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
                             tempreg, tempreg, breg);
-             macro_build (&offset_expr, s, fmt, treg,
+             macro_build (&offset_expr, s, fmt, op[0],
                           BFD_RELOC_MIPS_GOT_OFST, tempreg);
              break;
            }
@@ -10339,7 +11867,7 @@ macro (struct mips_cl_insn *ip, char *str)
          if (breg != 0)
            macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
                         tempreg, tempreg, breg);
-         macro_build (&expr1, s, fmt, treg, BFD_RELOC_LO16, tempreg);
+         macro_build (&expr1, s, fmt, op[0], BFD_RELOC_LO16, tempreg);
        }
       else if (mips_big_got && !HAVE_NEWABI)
        {
@@ -10349,12 +11877,12 @@ macro (struct mips_cl_insn *ip, char *str)
               lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
               addu     $tempreg,$tempreg,$gp
               lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
-              <op>     $treg,0($tempreg)
+              <op>     op[0],0($tempreg)
             Otherwise we want
               lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
               nop
               addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
-              <op>     $treg,0($tempreg)
+              <op>     op[0],0($tempreg)
             If there is a base register, we add it to $tempreg before
             the <op>.  If there is a constant, we stick it in the
             <op> instruction.  We don't handle constants larger than
@@ -10388,7 +11916,7 @@ macro (struct mips_cl_insn *ip, char *str)
          if (breg != 0)
            macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
                         tempreg, tempreg, breg);
-         macro_build (&expr1, s, fmt, treg, BFD_RELOC_LO16, tempreg);
+         macro_build (&expr1, s, fmt, op[0], BFD_RELOC_LO16, tempreg);
        }
       else if (mips_big_got && HAVE_NEWABI)
        {
@@ -10396,10 +11924,10 @@ macro (struct mips_cl_insn *ip, char *str)
               lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
               add      $tempreg,$tempreg,$gp
               lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
-              <op>     $treg,<ofst>($tempreg)
+              <op>     op[0],<ofst>($tempreg)
             Otherwise, for local symbols, we want:
               lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_PAGE)
-              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_MIPS_GOT_OFST)  */
+              <op>     op[0],<sym>($tempreg)   (BFD_RELOC_MIPS_GOT_OFST)  */
          gas_assert (offset_expr.X_op == O_symbol);
          expr1.X_add_number = offset_expr.X_add_number;
          offset_expr.X_add_number = 0;
@@ -10416,7 +11944,7 @@ macro (struct mips_cl_insn *ip, char *str)
          if (breg != 0)
            macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
                         tempreg, tempreg, breg);
-         macro_build (&expr1, s, fmt, treg, BFD_RELOC_LO16, tempreg);
+         macro_build (&expr1, s, fmt, op[0], BFD_RELOC_LO16, tempreg);
 
          relax_switch ();
          offset_expr.X_add_number = expr1.X_add_number;
@@ -10425,7 +11953,7 @@ macro (struct mips_cl_insn *ip, char *str)
          if (breg != 0)
            macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
                         tempreg, tempreg, breg);
-         macro_build (&offset_expr, s, fmt, treg,
+         macro_build (&offset_expr, s, fmt, op[0],
                       BFD_RELOC_MIPS_GOT_OFST, tempreg);
          relax_end ();
        }
@@ -10439,7 +11967,7 @@ macro (struct mips_cl_insn *ip, char *str)
       gas_assert (mips_opts.insn32);
       start_noreorder ();
       macro_build (NULL, "jr", "s", RA);
-      expr1.X_add_number = EXTRACT_OPERAND (1, IMMP, *ip) << 2;
+      expr1.X_add_number = op[0] << 2;
       macro_build (&expr1, "addiu", "t,r,j", SP, SP, BFD_RELOC_LO16);
       end_noreorder ();
       break;
@@ -10447,18 +11975,18 @@ macro (struct mips_cl_insn *ip, char *str)
     case M_JRC:
       gas_assert (mips_opts.micromips);
       gas_assert (mips_opts.insn32);
-      macro_build (NULL, "jr", "s", sreg);
+      macro_build (NULL, "jr", "s", op[0]);
       if (mips_opts.noreorder)
        macro_build (NULL, "nop", "");
       break;
 
     case M_LI:
     case M_LI_S:
-      load_register (treg, &imm_expr, 0);
+      load_register (op[0], &imm_expr, 0);
       break;
 
     case M_DLI:
-      load_register (treg, &imm_expr, 1);
+      load_register (op[0], &imm_expr, 1);
       break;
 
     case M_LI_SS:
@@ -10466,17 +11994,18 @@ macro (struct mips_cl_insn *ip, char *str)
        {
          used_at = 1;
          load_register (AT, &imm_expr, 0);
-         macro_build (NULL, "mtc1", "t,G", AT, treg);
+         macro_build (NULL, "mtc1", "t,G", AT, op[0]);
          break;
        }
       else
        {
-         gas_assert (offset_expr.X_op == O_symbol
+         gas_assert (imm_expr.X_op == O_absent
+                     && offset_expr.X_op == O_symbol
                      && strcmp (segment_name (S_GET_SEGMENT
                                               (offset_expr.X_add_symbol)),
                                 ".lit4") == 0
                      && offset_expr.X_add_number == 0);
-         macro_build (&offset_expr, "lwc1", "T,o(b)", treg,
+         macro_build (&offset_expr, "lwc1", "T,o(b)", op[0],
                       BFD_RELOC_MIPS_LITERAL, mips_gp_register);
          break;
        }
@@ -10486,23 +12015,23 @@ macro (struct mips_cl_insn *ip, char *str)
          wide, IMM_EXPR is the entire value.  Otherwise IMM_EXPR is the high
          order 32 bits of the value and the low order 32 bits are either
          zero or in OFFSET_EXPR.  */
-      if (imm_expr.X_op == O_constant || imm_expr.X_op == O_big)
+      if (imm_expr.X_op == O_constant)
        {
-         if (HAVE_64BIT_GPRS)
-           load_register (treg, &imm_expr, 1);
+         if (GPR_SIZE == 64)
+           load_register (op[0], &imm_expr, 1);
          else
            {
              int hreg, lreg;
 
              if (target_big_endian)
                {
-                 hreg = treg;
-                 lreg = treg + 1;
+                 hreg = op[0];
+                 lreg = op[0] + 1;
                }
              else
                {
-                 hreg = treg + 1;
-                 lreg = treg;
+                 hreg = op[0] + 1;
+                 lreg = op[0];
                }
 
              if (hreg <= 31)
@@ -10520,6 +12049,7 @@ macro (struct mips_cl_insn *ip, char *str)
            }
          break;
        }
+      gas_assert (imm_expr.X_op == O_absent);
 
       /* We know that sym is in the .rdata section.  First we get the
         upper 16 bits of the address.  */
@@ -10536,22 +12066,24 @@ macro (struct mips_cl_insn *ip, char *str)
        }
 
       /* Now we load the register(s).  */
-      if (HAVE_64BIT_GPRS)
+      if (GPR_SIZE == 64)
        {
          used_at = 1;
-         macro_build (&offset_expr, "ld", "t,o(b)", treg, BFD_RELOC_LO16, AT);
+         macro_build (&offset_expr, "ld", "t,o(b)", op[0],
+                      BFD_RELOC_LO16, AT);
        }
       else
        {
          used_at = 1;
-         macro_build (&offset_expr, "lw", "t,o(b)", treg, BFD_RELOC_LO16, AT);
-         if (treg != RA)
+         macro_build (&offset_expr, "lw", "t,o(b)", op[0],
+                      BFD_RELOC_LO16, AT);
+         if (op[0] != RA)
            {
              /* FIXME: How in the world do we deal with the possible
                 overflow here?  */
              offset_expr.X_add_number += 4;
              macro_build (&offset_expr, "lw", "t,o(b)",
-                          treg + 1, BFD_RELOC_LO16, AT);
+                          op[0] + 1, BFD_RELOC_LO16, AT);
            }
        }
       break;
@@ -10562,36 +12094,41 @@ macro (struct mips_cl_insn *ip, char *str)
          bits wide as well.  Otherwise IMM_EXPR is the high order 32 bits of
          the value and the low order 32 bits are either zero or in
          OFFSET_EXPR.  */
-      if (imm_expr.X_op == O_constant || imm_expr.X_op == O_big)
+      if (imm_expr.X_op == O_constant)
        {
          used_at = 1;
-         load_register (AT, &imm_expr, HAVE_64BIT_FPRS);
-         if (HAVE_64BIT_FPRS)
-           {
-             gas_assert (HAVE_64BIT_GPRS);
-             macro_build (NULL, "dmtc1", "t,S", AT, treg);
-           }
+         load_register (AT, &imm_expr, FPR_SIZE == 64);
+         if (FPR_SIZE == 64 && GPR_SIZE == 64)
+           macro_build (NULL, "dmtc1", "t,S", AT, op[0]);
          else
            {
-             macro_build (NULL, "mtc1", "t,G", AT, treg + 1);
+             if (ISA_HAS_MXHC1 (mips_opts.isa))
+               macro_build (NULL, "mthc1", "t,G", AT, op[0]);
+             else if (FPR_SIZE != 32)
+               as_bad (_("Unable to generate `%s' compliant code "
+                         "without mthc1"),
+                       (FPR_SIZE == 64) ? "fp64" : "fpxx");
+             else
+               macro_build (NULL, "mtc1", "t,G", AT, op[0] + 1);
              if (offset_expr.X_op == O_absent)
-               macro_build (NULL, "mtc1", "t,G", 0, treg);
+               macro_build (NULL, "mtc1", "t,G", 0, op[0]);
              else
                {
                  gas_assert (offset_expr.X_op == O_constant);
                  load_register (AT, &offset_expr, 0);
-                 macro_build (NULL, "mtc1", "t,G", AT, treg);
+                 macro_build (NULL, "mtc1", "t,G", AT, op[0]);
                }
            }
          break;
        }
 
-      gas_assert (offset_expr.X_op == O_symbol
+      gas_assert (imm_expr.X_op == O_absent
+                 && offset_expr.X_op == O_symbol
                  && offset_expr.X_add_number == 0);
       s = segment_name (S_GET_SEGMENT (offset_expr.X_add_symbol));
       if (strcmp (s, ".lit8") == 0)
        {
-         breg = mips_gp_register;
+         op[2] = mips_gp_register;
          offset_reloc[0] = BFD_RELOC_MIPS_LITERAL;
          offset_reloc[1] = BFD_RELOC_UNUSED;
          offset_reloc[2] = BFD_RELOC_UNUSED;
@@ -10609,7 +12146,7 @@ macro (struct mips_cl_insn *ip, char *str)
              macro_build_lui (&offset_expr, AT);
            }
 
-         breg = AT;
+         op[2] = AT;
          offset_reloc[0] = BFD_RELOC_LO16;
          offset_reloc[1] = BFD_RELOC_UNUSED;
          offset_reloc[2] = BFD_RELOC_UNUSED;
@@ -10665,7 +12202,7 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_LD_AB:
       fmt = "t,o(b)";
-      if (HAVE_64BIT_GPRS)
+      if (GPR_SIZE == 64)
        {
          s = "ld";
          goto ld;
@@ -10675,7 +12212,7 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_SD_AB:
       fmt = "t,o(b)";
-      if (HAVE_64BIT_GPRS)
+      if (GPR_SIZE == 64)
        {
          s = "sd";
          goto ld_st;
@@ -10690,6 +12227,7 @@ macro (struct mips_cl_insn *ip, char *str)
       if (!target_big_endian)
        coproc = 0;
 
+      breg = op[2];
       if (small_offset_p (0, align, 16))
        {
          ep = &offset_expr;
@@ -10706,22 +12244,22 @@ macro (struct mips_cl_insn *ip, char *str)
              offset_reloc[1] = BFD_RELOC_UNUSED;
              offset_reloc[2] = BFD_RELOC_UNUSED;
            }
-         if (strcmp (s, "lw") == 0 && treg == breg)
+         if (strcmp (s, "lw") == 0 && op[0] == breg)
            {
              ep->X_add_number += 4;
-             macro_build (ep, s, fmt, treg + 1, -1, offset_reloc[0],
+             macro_build (ep, s, fmt, op[0] + 1, -1, offset_reloc[0],
                           offset_reloc[1], offset_reloc[2], breg);
              ep->X_add_number -= 4;
-             macro_build (ep, s, fmt, treg, -1, offset_reloc[0],
+             macro_build (ep, s, fmt, op[0], -1, offset_reloc[0],
                           offset_reloc[1], offset_reloc[2], breg);
            }
          else
            {
-             macro_build (ep, s, fmt, coproc ? treg + 1 : treg, -1,
+             macro_build (ep, s, fmt, coproc ? op[0] + 1 : op[0], -1,
                           offset_reloc[0], offset_reloc[1], offset_reloc[2],
                           breg);
              ep->X_add_number += 4;
-             macro_build (ep, s, fmt, coproc ? treg : treg + 1, -1,
+             macro_build (ep, s, fmt, coproc ? op[0] : op[0] + 1, -1,
                           offset_reloc[0], offset_reloc[1], offset_reloc[2],
                           breg);
            }
@@ -10731,7 +12269,7 @@ macro (struct mips_cl_insn *ip, char *str)
       if (offset_expr.X_op != O_symbol
          && offset_expr.X_op != O_constant)
        {
-         as_bad (_("Expression too complex"));
+         as_bad (_("expression too complex"));
          offset_expr.X_op = O_constant;
        }
 
@@ -10741,22 +12279,22 @@ macro (struct mips_cl_insn *ip, char *str)
          char value [32];
 
          sprintf_vma (value, offset_expr.X_add_number);
-         as_bad (_("Number (0x%s) larger than 32 bits"), value);
+         as_bad (_("number (0x%s) larger than 32 bits"), value);
        }
 
       if (mips_pic == NO_PIC || offset_expr.X_op == O_constant)
        {
          /* If this is a reference to a GP relative symbol, we want
-              <op>     $treg,<sym>($gp)        (BFD_RELOC_GPREL16)
-              <op>     $treg+1,<sym>+4($gp)    (BFD_RELOC_GPREL16)
+              <op>     op[0],<sym>($gp)        (BFD_RELOC_GPREL16)
+              <op>     op[0]+1,<sym>+4($gp)    (BFD_RELOC_GPREL16)
             If we have a base register, we use this
               addu     $at,$breg,$gp
-              <op>     $treg,<sym>($at)        (BFD_RELOC_GPREL16)
-              <op>     $treg+1,<sym>+4($at)    (BFD_RELOC_GPREL16)
+              <op>     op[0],<sym>($at)        (BFD_RELOC_GPREL16)
+              <op>     op[0]+1,<sym>+4($at)    (BFD_RELOC_GPREL16)
             If this is not a GP relative symbol, we want
               lui      $at,<sym>               (BFD_RELOC_HI16_S)
-              <op>     $treg,<sym>($at)        (BFD_RELOC_LO16)
-              <op>     $treg+1,<sym>+4($at)    (BFD_RELOC_LO16)
+              <op>     op[0],<sym>($at)        (BFD_RELOC_LO16)
+              <op>     op[0]+1,<sym>+4($at)    (BFD_RELOC_LO16)
             If there is a base register, we add it to $at after the
             lui instruction.  If there is a constant, we always use
             the last case.  */
@@ -10778,7 +12316,7 @@ macro (struct mips_cl_insn *ip, char *str)
                }
 
              /* Itbl support may require additional care here.  */
-             macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
+             macro_build (&offset_expr, s, fmt, coproc ? op[0] + 1 : op[0],
                           BFD_RELOC_GPREL16, tempreg);
              offset_expr.X_add_number += 4;
 
@@ -10787,7 +12325,7 @@ macro (struct mips_cl_insn *ip, char *str)
              hold_mips_optimize = mips_optimize;
              mips_optimize = 2;
              /* Itbl support may require additional care here.  */
-             macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
+             macro_build (&offset_expr, s, fmt, coproc ? op[0] : op[0] + 1,
                           BFD_RELOC_GPREL16, tempreg);
              mips_optimize = hold_mips_optimize;
 
@@ -10808,12 +12346,12 @@ macro (struct mips_cl_insn *ip, char *str)
          if (breg != 0)
            macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
          /* Itbl support may require additional care here.  */
-         macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
+         macro_build (&offset_expr, s, fmt, coproc ? op[0] + 1 : op[0],
                       BFD_RELOC_LO16, AT);
          /* FIXME: How do we handle overflow here?  */
          offset_expr.X_add_number += 4;
          /* Itbl support may require additional care here.  */
-         macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
+         macro_build (&offset_expr, s, fmt, coproc ? op[0] : op[0] + 1,
                       BFD_RELOC_LO16, AT);
          if (mips_relax.sequence)
            relax_end ();
@@ -10823,13 +12361,13 @@ macro (struct mips_cl_insn *ip, char *str)
          /* If this is a reference to an external symbol, we want
               lw       $at,<sym>($gp)          (BFD_RELOC_MIPS_GOT16)
               nop
-              <op>     $treg,0($at)
-              <op>     $treg+1,4($at)
+              <op>     op[0],0($at)
+              <op>     op[0]+1,4($at)
             Otherwise we want
               lw       $at,<sym>($gp)          (BFD_RELOC_MIPS_GOT16)
               nop
-              <op>     $treg,<sym>($at)        (BFD_RELOC_LO16)
-              <op>     $treg+1,<sym>+4($at)    (BFD_RELOC_LO16)
+              <op>     op[0],<sym>($at)        (BFD_RELOC_LO16)
+              <op>     op[0]+1,<sym>+4($at)    (BFD_RELOC_LO16)
             If there is a base register we add it to $at before the
             lwc1 instructions.  If there is a constant we include it
             in the lwc1 instructions.  */
@@ -10850,16 +12388,16 @@ macro (struct mips_cl_insn *ip, char *str)
 
          /* Itbl support may require additional care here.  */
          relax_start (offset_expr.X_add_symbol);
-         macro_build (&expr1, s, fmt, coproc ? treg + 1 : treg,
+         macro_build (&expr1, s, fmt, coproc ? op[0] + 1 : op[0],
                       BFD_RELOC_LO16, AT);
          expr1.X_add_number += 4;
-         macro_build (&expr1, s, fmt, coproc ? treg : treg + 1,
+         macro_build (&expr1, s, fmt, coproc ? op[0] : op[0] + 1,
                       BFD_RELOC_LO16, AT);
          relax_switch ();
-         macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
+         macro_build (&offset_expr, s, fmt, coproc ? op[0] + 1 : op[0],
                       BFD_RELOC_LO16, AT);
          offset_expr.X_add_number += 4;
-         macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
+         macro_build (&offset_expr, s, fmt, coproc ? op[0] : op[0] + 1,
                       BFD_RELOC_LO16, AT);
          relax_end ();
 
@@ -10874,13 +12412,13 @@ macro (struct mips_cl_insn *ip, char *str)
               addu     $at,$at,$gp
               lw       $at,<sym>($at)          (BFD_RELOC_MIPS_GOT_LO16)
               nop
-              <op>     $treg,0($at)
-              <op>     $treg+1,4($at)
+              <op>     op[0],0($at)
+              <op>     op[0]+1,4($at)
             Otherwise we want
               lw       $at,<sym>($gp)          (BFD_RELOC_MIPS_GOT16)
               nop
-              <op>     $treg,<sym>($at)        (BFD_RELOC_LO16)
-              <op>     $treg+1,<sym>+4($at)    (BFD_RELOC_LO16)
+              <op>     op[0],<sym>($at)        (BFD_RELOC_LO16)
+              <op>     op[0]+1,<sym>+4($at)    (BFD_RELOC_LO16)
             If there is a base register we add it to $at before the
             lwc1 instructions.  If there is a constant we include it
             in the lwc1 instructions.  */
@@ -10902,7 +12440,7 @@ macro (struct mips_cl_insn *ip, char *str)
          if (breg != 0)
            macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
          /* Itbl support may require additional care here.  */
-         macro_build (&expr1, s, fmt, coproc ? treg + 1 : treg,
+         macro_build (&expr1, s, fmt, coproc ? op[0] + 1 : op[0],
                       BFD_RELOC_LO16, AT);
          expr1.X_add_number += 4;
 
@@ -10911,7 +12449,7 @@ macro (struct mips_cl_insn *ip, char *str)
          hold_mips_optimize = mips_optimize;
          mips_optimize = 2;
          /* Itbl support may require additional care here.  */
-         macro_build (&expr1, s, fmt, coproc ? treg : treg + 1,
+         macro_build (&expr1, s, fmt, coproc ? op[0] : op[0] + 1,
                       BFD_RELOC_LO16, AT);
          mips_optimize = hold_mips_optimize;
          expr1.X_add_number -= 4;
@@ -10926,7 +12464,7 @@ macro (struct mips_cl_insn *ip, char *str)
          if (breg != 0)
            macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
          /* Itbl support may require additional care here.  */
-         macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
+         macro_build (&offset_expr, s, fmt, coproc ? op[0] + 1 : op[0],
                       BFD_RELOC_LO16, AT);
          offset_expr.X_add_number += 4;
 
@@ -10935,7 +12473,7 @@ macro (struct mips_cl_insn *ip, char *str)
          hold_mips_optimize = mips_optimize;
          mips_optimize = 2;
          /* Itbl support may require additional care here.  */
-         macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
+         macro_build (&offset_expr, s, fmt, coproc ? op[0] : op[0] + 1,
                       BFD_RELOC_LO16, AT);
          mips_optimize = hold_mips_optimize;
          relax_end ();
@@ -10947,11 +12485,11 @@ macro (struct mips_cl_insn *ip, char *str)
        
     case M_SAA_AB:
       s = "saa";
-      offbits = 0;
-      fmt = "t,(b)";
-      goto ld_st;
+      goto saa_saad;
     case M_SAAD_AB:
       s = "saad";
+    saa_saad:
+      gas_assert (!mips_opts.micromips);
       offbits = 0;
       fmt = "t,(b)";
       goto ld_st;
@@ -10992,31 +12530,28 @@ macro (struct mips_cl_insn *ip, char *str)
       break;
 
     case M_MOVE:
-      move_register (dreg, sreg);
+      move_register (op[0], op[1]);
       break;
 
     case M_MOVEP:
       gas_assert (mips_opts.micromips);
       gas_assert (mips_opts.insn32);
-      dreg = micromips_to_32_reg_h_map1[EXTRACT_OPERAND (1, MH, *ip)];
-      breg = micromips_to_32_reg_h_map2[EXTRACT_OPERAND (1, MH, *ip)];
-      sreg = micromips_to_32_reg_m_map[EXTRACT_OPERAND (1, MM, *ip)];
-      treg = micromips_to_32_reg_n_map[EXTRACT_OPERAND (1, MN, *ip)];
-      move_register (dreg, sreg);
-      move_register (breg, treg);
+      move_register (micromips_to_32_reg_h_map1[op[0]],
+                    micromips_to_32_reg_m_map[op[1]]);
+      move_register (micromips_to_32_reg_h_map2[op[0]],
+                    micromips_to_32_reg_n_map[op[2]]);
       break;
 
     case M_DMUL:
       dbl = 1;
     case M_MUL:
       if (mips_opts.arch == CPU_R5900)
-        {
-          macro_build (NULL, dbl ? "dmultu" : "multu", "d,s,t", dreg, sreg, treg);
-        }
+       macro_build (NULL, dbl ? "dmultu" : "multu", "d,s,t", op[0], op[1],
+                    op[2]);
       else
         {
-      macro_build (NULL, dbl ? "dmultu" : "multu", "s,t", sreg, treg);
-      macro_build (NULL, "mflo", MFHL_FMT, dreg);
+         macro_build (NULL, dbl ? "dmultu" : "multu", "s,t", op[1], op[2]);
+         macro_build (NULL, "mflo", MFHL_FMT, op[0]);
         }
       break;
 
@@ -11028,8 +12563,8 @@ macro (struct mips_cl_insn *ip, char *str)
         anyway.  */
       used_at = 1;
       load_register (AT, &imm_expr, dbl);
-      macro_build (NULL, dbl ? "dmult" : "mult", "s,t", sreg, AT);
-      macro_build (NULL, "mflo", MFHL_FMT, dreg);
+      macro_build (NULL, dbl ? "dmult" : "mult", "s,t", op[1], AT);
+      macro_build (NULL, "mflo", MFHL_FMT, op[0]);
       break;
 
     case M_DMULO_I:
@@ -11046,26 +12581,27 @@ macro (struct mips_cl_insn *ip, char *str)
       used_at = 1;
       if (imm)
        load_register (AT, &imm_expr, dbl);
-      macro_build (NULL, dbl ? "dmult" : "mult", "s,t", sreg, imm ? AT : treg);
-      macro_build (NULL, "mflo", MFHL_FMT, dreg);
-      macro_build (NULL, dbl ? "dsra32" : "sra", SHFT_FMT, dreg, dreg, RA);
+      macro_build (NULL, dbl ? "dmult" : "mult", "s,t",
+                  op[1], imm ? AT : op[2]);
+      macro_build (NULL, "mflo", MFHL_FMT, op[0]);
+      macro_build (NULL, dbl ? "dsra32" : "sra", SHFT_FMT, op[0], op[0], 31);
       macro_build (NULL, "mfhi", MFHL_FMT, AT);
       if (mips_trap)
-       macro_build (NULL, "tne", TRAP_FMT, dreg, AT, 6);
+       macro_build (NULL, "tne", TRAP_FMT, op[0], AT, 6);
       else
        {
          if (mips_opts.micromips)
            micromips_label_expr (&label_expr);
          else
            label_expr.X_add_number = 8;
-         macro_build (&label_expr, "beq", "s,t,p", dreg, AT);
+         macro_build (&label_expr, "beq", "s,t,p", op[0], AT);
          macro_build (NULL, "nop", "");
          macro_build (NULL, "break", BRK_FMT, 6);
          if (mips_opts.micromips)
            micromips_add_label ();
        }
       end_noreorder ();
-      macro_build (NULL, "mflo", MFHL_FMT, dreg);
+      macro_build (NULL, "mflo", MFHL_FMT, op[0]);
       break;
 
     case M_DMULOU_I:
@@ -11083,9 +12619,9 @@ macro (struct mips_cl_insn *ip, char *str)
       if (imm)
        load_register (AT, &imm_expr, dbl);
       macro_build (NULL, dbl ? "dmultu" : "multu", "s,t",
-                  sreg, imm ? AT : treg);
+                  op[1], imm ? AT : op[2]);
       macro_build (NULL, "mfhi", MFHL_FMT, AT);
-      macro_build (NULL, "mflo", MFHL_FMT, dreg);
+      macro_build (NULL, "mflo", MFHL_FMT, op[0]);
       if (mips_trap)
        macro_build (NULL, "tne", TRAP_FMT, AT, ZERO, 6);
       else
@@ -11106,47 +12642,43 @@ macro (struct mips_cl_insn *ip, char *str)
     case M_DROL:
       if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
        {
-         if (dreg == sreg)
+         if (op[0] == op[1])
            {
              tempreg = AT;
              used_at = 1;
            }
          else
-           {
-             tempreg = dreg;
-           }
-         macro_build (NULL, "dnegu", "d,w", tempreg, treg);
-         macro_build (NULL, "drorv", "d,t,s", dreg, sreg, tempreg);
+           tempreg = op[0];
+         macro_build (NULL, "dnegu", "d,w", tempreg, op[2]);
+         macro_build (NULL, "drorv", "d,t,s", op[0], op[1], tempreg);
          break;
        }
       used_at = 1;
-      macro_build (NULL, "dsubu", "d,v,t", AT, ZERO, treg);
-      macro_build (NULL, "dsrlv", "d,t,s", AT, sreg, AT);
-      macro_build (NULL, "dsllv", "d,t,s", dreg, sreg, treg);
-      macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+      macro_build (NULL, "dsubu", "d,v,t", AT, ZERO, op[2]);
+      macro_build (NULL, "dsrlv", "d,t,s", AT, op[1], AT);
+      macro_build (NULL, "dsllv", "d,t,s", op[0], op[1], op[2]);
+      macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
       break;
 
     case M_ROL:
       if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
        {
-         if (dreg == sreg)
+         if (op[0] == op[1])
            {
              tempreg = AT;
              used_at = 1;
            }
          else
-           {
-             tempreg = dreg;
-           }
-         macro_build (NULL, "negu", "d,w", tempreg, treg);
-         macro_build (NULL, "rorv", "d,t,s", dreg, sreg, tempreg);
+           tempreg = op[0];
+         macro_build (NULL, "negu", "d,w", tempreg, op[2]);
+         macro_build (NULL, "rorv", "d,t,s", op[0], op[1], tempreg);
          break;
        }
       used_at = 1;
-      macro_build (NULL, "subu", "d,v,t", AT, ZERO, treg);
-      macro_build (NULL, "srlv", "d,t,s", AT, sreg, AT);
-      macro_build (NULL, "sllv", "d,t,s", dreg, sreg, treg);
-      macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+      macro_build (NULL, "subu", "d,v,t", AT, ZERO, op[2]);
+      macro_build (NULL, "srlv", "d,t,s", AT, op[1], AT);
+      macro_build (NULL, "sllv", "d,t,s", op[0], op[1], op[2]);
+      macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
       break;
 
     case M_DROL_I:
@@ -11155,30 +12687,28 @@ macro (struct mips_cl_insn *ip, char *str)
        char *l;
        char *rr;
 
-       if (imm_expr.X_op != O_constant)
-         as_bad (_("Improper rotate count"));
        rot = imm_expr.X_add_number & 0x3f;
        if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
          {
            rot = (64 - rot) & 0x3f;
            if (rot >= 32)
-             macro_build (NULL, "dror32", SHFT_FMT, dreg, sreg, rot - 32);
+             macro_build (NULL, "dror32", SHFT_FMT, op[0], op[1], rot - 32);
            else
-             macro_build (NULL, "dror", SHFT_FMT, dreg, sreg, rot);
+             macro_build (NULL, "dror", SHFT_FMT, op[0], op[1], rot);
            break;
          }
        if (rot == 0)
          {
-           macro_build (NULL, "dsrl", SHFT_FMT, dreg, sreg, 0);
+           macro_build (NULL, "dsrl", SHFT_FMT, op[0], op[1], 0);
            break;
          }
        l = (rot < 0x20) ? "dsll" : "dsll32";
        rr = ((0x40 - rot) < 0x20) ? "dsrl" : "dsrl32";
        rot &= 0x1f;
        used_at = 1;
-       macro_build (NULL, l, SHFT_FMT, AT, sreg, rot);
-       macro_build (NULL, rr, SHFT_FMT, dreg, sreg, (0x20 - rot) & 0x1f);
-       macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+       macro_build (NULL, l, SHFT_FMT, AT, op[1], rot);
+       macro_build (NULL, rr, SHFT_FMT, op[0], op[1], (0x20 - rot) & 0x1f);
+       macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
       }
       break;
 
@@ -11186,50 +12716,49 @@ macro (struct mips_cl_insn *ip, char *str)
       {
        unsigned int rot;
 
-       if (imm_expr.X_op != O_constant)
-         as_bad (_("Improper rotate count"));
        rot = imm_expr.X_add_number & 0x1f;
        if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
          {
-           macro_build (NULL, "ror", SHFT_FMT, dreg, sreg, (32 - rot) & 0x1f);
+           macro_build (NULL, "ror", SHFT_FMT, op[0], op[1],
+                        (32 - rot) & 0x1f);
            break;
          }
        if (rot == 0)
          {
-           macro_build (NULL, "srl", SHFT_FMT, dreg, sreg, 0);
+           macro_build (NULL, "srl", SHFT_FMT, op[0], op[1], 0);
            break;
          }
        used_at = 1;
-       macro_build (NULL, "sll", SHFT_FMT, AT, sreg, rot);
-       macro_build (NULL, "srl", SHFT_FMT, dreg, sreg, (0x20 - rot) & 0x1f);
-       macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+       macro_build (NULL, "sll", SHFT_FMT, AT, op[1], rot);
+       macro_build (NULL, "srl", SHFT_FMT, op[0], op[1], (0x20 - rot) & 0x1f);
+       macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
       }
       break;
 
     case M_DROR:
       if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
        {
-         macro_build (NULL, "drorv", "d,t,s", dreg, sreg, treg);
+         macro_build (NULL, "drorv", "d,t,s", op[0], op[1], op[2]);
          break;
        }
       used_at = 1;
-      macro_build (NULL, "dsubu", "d,v,t", AT, ZERO, treg);
-      macro_build (NULL, "dsllv", "d,t,s", AT, sreg, AT);
-      macro_build (NULL, "dsrlv", "d,t,s", dreg, sreg, treg);
-      macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+      macro_build (NULL, "dsubu", "d,v,t", AT, ZERO, op[2]);
+      macro_build (NULL, "dsllv", "d,t,s", AT, op[1], AT);
+      macro_build (NULL, "dsrlv", "d,t,s", op[0], op[1], op[2]);
+      macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
       break;
 
     case M_ROR:
       if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
        {
-         macro_build (NULL, "rorv", "d,t,s", dreg, sreg, treg);
+         macro_build (NULL, "rorv", "d,t,s", op[0], op[1], op[2]);
          break;
        }
       used_at = 1;
-      macro_build (NULL, "subu", "d,v,t", AT, ZERO, treg);
-      macro_build (NULL, "sllv", "d,t,s", AT, sreg, AT);
-      macro_build (NULL, "srlv", "d,t,s", dreg, sreg, treg);
-      macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+      macro_build (NULL, "subu", "d,v,t", AT, ZERO, op[2]);
+      macro_build (NULL, "sllv", "d,t,s", AT, op[1], AT);
+      macro_build (NULL, "srlv", "d,t,s", op[0], op[1], op[2]);
+      macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
       break;
 
     case M_DROR_I:
@@ -11238,29 +12767,27 @@ macro (struct mips_cl_insn *ip, char *str)
        char *l;
        char *rr;
 
-       if (imm_expr.X_op != O_constant)
-         as_bad (_("Improper rotate count"));
        rot = imm_expr.X_add_number & 0x3f;
        if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
          {
            if (rot >= 32)
-             macro_build (NULL, "dror32", SHFT_FMT, dreg, sreg, rot - 32);
+             macro_build (NULL, "dror32", SHFT_FMT, op[0], op[1], rot - 32);
            else
-             macro_build (NULL, "dror", SHFT_FMT, dreg, sreg, rot);
+             macro_build (NULL, "dror", SHFT_FMT, op[0], op[1], rot);
            break;
          }
        if (rot == 0)
          {
-           macro_build (NULL, "dsrl", SHFT_FMT, dreg, sreg, 0);
+           macro_build (NULL, "dsrl", SHFT_FMT, op[0], op[1], 0);
            break;
          }
        rr = (rot < 0x20) ? "dsrl" : "dsrl32";
        l = ((0x40 - rot) < 0x20) ? "dsll" : "dsll32";
        rot &= 0x1f;
        used_at = 1;
-       macro_build (NULL, rr, SHFT_FMT, AT, sreg, rot);
-       macro_build (NULL, l, SHFT_FMT, dreg, sreg, (0x20 - rot) & 0x1f);
-       macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+       macro_build (NULL, rr, SHFT_FMT, AT, op[1], rot);
+       macro_build (NULL, l, SHFT_FMT, op[0], op[1], (0x20 - rot) & 0x1f);
+       macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
       }
       break;
 
@@ -11268,249 +12795,238 @@ macro (struct mips_cl_insn *ip, char *str)
       {
        unsigned int rot;
 
-       if (imm_expr.X_op != O_constant)
-         as_bad (_("Improper rotate count"));
        rot = imm_expr.X_add_number & 0x1f;
        if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
          {
-           macro_build (NULL, "ror", SHFT_FMT, dreg, sreg, rot);
+           macro_build (NULL, "ror", SHFT_FMT, op[0], op[1], rot);
            break;
          }
        if (rot == 0)
          {
-           macro_build (NULL, "srl", SHFT_FMT, dreg, sreg, 0);
+           macro_build (NULL, "srl", SHFT_FMT, op[0], op[1], 0);
            break;
          }
        used_at = 1;
-       macro_build (NULL, "srl", SHFT_FMT, AT, sreg, rot);
-       macro_build (NULL, "sll", SHFT_FMT, dreg, sreg, (0x20 - rot) & 0x1f);
-       macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+       macro_build (NULL, "srl", SHFT_FMT, AT, op[1], rot);
+       macro_build (NULL, "sll", SHFT_FMT, op[0], op[1], (0x20 - rot) & 0x1f);
+       macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
       }
       break;
 
     case M_SEQ:
-      if (sreg == 0)
-       macro_build (&expr1, "sltiu", "t,r,j", dreg, treg, BFD_RELOC_LO16);
-      else if (treg == 0)
-       macro_build (&expr1, "sltiu", "t,r,j", dreg, sreg, BFD_RELOC_LO16);
+      if (op[1] == 0)
+       macro_build (&expr1, "sltiu", "t,r,j", op[0], op[2], BFD_RELOC_LO16);
+      else if (op[2] == 0)
+       macro_build (&expr1, "sltiu", "t,r,j", op[0], op[1], BFD_RELOC_LO16);
       else
        {
-         macro_build (NULL, "xor", "d,v,t", dreg, sreg, treg);
-         macro_build (&expr1, "sltiu", "t,r,j", dreg, dreg, BFD_RELOC_LO16);
+         macro_build (NULL, "xor", "d,v,t", op[0], op[1], op[2]);
+         macro_build (&expr1, "sltiu", "t,r,j", op[0], op[0], BFD_RELOC_LO16);
        }
       break;
 
     case M_SEQ_I:
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+      if (imm_expr.X_add_number == 0)
        {
-         macro_build (&expr1, "sltiu", "t,r,j", dreg, sreg, BFD_RELOC_LO16);
+         macro_build (&expr1, "sltiu", "t,r,j", op[0], op[1], BFD_RELOC_LO16);
          break;
        }
-      if (sreg == 0)
+      if (op[1] == 0)
        {
-         as_warn (_("Instruction %s: result is always false"),
+         as_warn (_("instruction %s: result is always false"),
                   ip->insn_mo->name);
-         move_register (dreg, 0);
+         move_register (op[0], 0);
          break;
        }
       if (CPU_HAS_SEQ (mips_opts.arch)
          && -512 <= imm_expr.X_add_number
          && imm_expr.X_add_number < 512)
        {
-         macro_build (NULL, "seqi", "t,r,+Q", dreg, sreg,
+         macro_build (NULL, "seqi", "t,r,+Q", op[0], op[1],
                       (int) imm_expr.X_add_number);
          break;
        }
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number >= 0
+      if (imm_expr.X_add_number >= 0
          && imm_expr.X_add_number < 0x10000)
-       {
-         macro_build (&imm_expr, "xori", "t,r,i", dreg, sreg, BFD_RELOC_LO16);
-       }
-      else if (imm_expr.X_op == O_constant
-              && imm_expr.X_add_number > -0x8000
+       macro_build (&imm_expr, "xori", "t,r,i", op[0], op[1], BFD_RELOC_LO16);
+      else if (imm_expr.X_add_number > -0x8000
               && imm_expr.X_add_number < 0)
        {
          imm_expr.X_add_number = -imm_expr.X_add_number;
-         macro_build (&imm_expr, HAVE_32BIT_GPRS ? "addiu" : "daddiu",
-                      "t,r,j", dreg, sreg, BFD_RELOC_LO16);
+         macro_build (&imm_expr, GPR_SIZE == 32 ? "addiu" : "daddiu",
+                      "t,r,j", op[0], op[1], BFD_RELOC_LO16);
        }
       else if (CPU_HAS_SEQ (mips_opts.arch))
        {
          used_at = 1;
-         load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-         macro_build (NULL, "seq", "d,v,t", dreg, sreg, AT);
+         load_register (AT, &imm_expr, GPR_SIZE == 64);
+         macro_build (NULL, "seq", "d,v,t", op[0], op[1], AT);
          break;
        }
       else
        {
-         load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-         macro_build (NULL, "xor", "d,v,t", dreg, sreg, AT);
+         load_register (AT, &imm_expr, GPR_SIZE == 64);
+         macro_build (NULL, "xor", "d,v,t", op[0], op[1], AT);
          used_at = 1;
        }
-      macro_build (&expr1, "sltiu", "t,r,j", dreg, dreg, BFD_RELOC_LO16);
+      macro_build (&expr1, "sltiu", "t,r,j", op[0], op[0], BFD_RELOC_LO16);
       break;
 
-    case M_SGE:                /* sreg >= treg <==> not (sreg < treg) */
+    case M_SGE:                /* X >= Y  <==>  not (X < Y) */
       s = "slt";
       goto sge;
     case M_SGEU:
       s = "sltu";
     sge:
-      macro_build (NULL, s, "d,v,t", dreg, sreg, treg);
-      macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
+      macro_build (NULL, s, "d,v,t", op[0], op[1], op[2]);
+      macro_build (&expr1, "xori", "t,r,i", op[0], op[0], BFD_RELOC_LO16);
       break;
 
-    case M_SGE_I:              /* sreg >= I <==> not (sreg < I) */
+    case M_SGE_I:      /* X >= I  <==>  not (X < I) */
     case M_SGEU_I:
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number >= -0x8000
+      if (imm_expr.X_add_number >= -0x8000
          && imm_expr.X_add_number < 0x8000)
-       {
-         macro_build (&imm_expr, mask == M_SGE_I ? "slti" : "sltiu", "t,r,j",
-                      dreg, sreg, BFD_RELOC_LO16);
-       }
+       macro_build (&imm_expr, mask == M_SGE_I ? "slti" : "sltiu", "t,r,j",
+                    op[0], op[1], BFD_RELOC_LO16);
       else
        {
-         load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+         load_register (AT, &imm_expr, GPR_SIZE == 64);
          macro_build (NULL, mask == M_SGE_I ? "slt" : "sltu", "d,v,t",
-                      dreg, sreg, AT);
+                      op[0], op[1], AT);
          used_at = 1;
        }
-      macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
+      macro_build (&expr1, "xori", "t,r,i", op[0], op[0], BFD_RELOC_LO16);
       break;
 
-    case M_SGT:                /* sreg > treg  <==>  treg < sreg */
+    case M_SGT:                /* X > Y  <==>  Y < X */
       s = "slt";
       goto sgt;
     case M_SGTU:
       s = "sltu";
     sgt:
-      macro_build (NULL, s, "d,v,t", dreg, treg, sreg);
+      macro_build (NULL, s, "d,v,t", op[0], op[2], op[1]);
       break;
 
-    case M_SGT_I:              /* sreg > I  <==>  I < sreg */
+    case M_SGT_I:      /* X > I  <==>  I < X */
       s = "slt";
       goto sgti;
     case M_SGTU_I:
       s = "sltu";
     sgti:
       used_at = 1;
-      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build (NULL, s, "d,v,t", dreg, AT, sreg);
+      load_register (AT, &imm_expr, GPR_SIZE == 64);
+      macro_build (NULL, s, "d,v,t", op[0], AT, op[1]);
       break;
 
-    case M_SLE:        /* sreg <= treg  <==>  treg >= sreg  <==>  not (treg < sreg) */
+    case M_SLE:                /* X <= Y  <==>  Y >= X  <==>  not (Y < X) */
       s = "slt";
       goto sle;
     case M_SLEU:
       s = "sltu";
     sle:
-      macro_build (NULL, s, "d,v,t", dreg, treg, sreg);
-      macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
+      macro_build (NULL, s, "d,v,t", op[0], op[2], op[1]);
+      macro_build (&expr1, "xori", "t,r,i", op[0], op[0], BFD_RELOC_LO16);
       break;
 
-    case M_SLE_I:      /* sreg <= I <==> I >= sreg <==> not (I < sreg) */
+    case M_SLE_I:      /* X <= I  <==>  I >= X  <==>  not (I < X) */
       s = "slt";
       goto slei;
     case M_SLEU_I:
       s = "sltu";
     slei:
       used_at = 1;
-      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build (NULL, s, "d,v,t", dreg, AT, sreg);
-      macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
+      load_register (AT, &imm_expr, GPR_SIZE == 64);
+      macro_build (NULL, s, "d,v,t", op[0], AT, op[1]);
+      macro_build (&expr1, "xori", "t,r,i", op[0], op[0], BFD_RELOC_LO16);
       break;
 
     case M_SLT_I:
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number >= -0x8000
+      if (imm_expr.X_add_number >= -0x8000
          && imm_expr.X_add_number < 0x8000)
        {
-         macro_build (&imm_expr, "slti", "t,r,j", dreg, sreg, BFD_RELOC_LO16);
+         macro_build (&imm_expr, "slti", "t,r,j", op[0], op[1],
+                      BFD_RELOC_LO16);
          break;
        }
       used_at = 1;
-      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build (NULL, "slt", "d,v,t", dreg, sreg, AT);
+      load_register (AT, &imm_expr, GPR_SIZE == 64);
+      macro_build (NULL, "slt", "d,v,t", op[0], op[1], AT);
       break;
 
     case M_SLTU_I:
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number >= -0x8000
+      if (imm_expr.X_add_number >= -0x8000
          && imm_expr.X_add_number < 0x8000)
        {
-         macro_build (&imm_expr, "sltiu", "t,r,j", dreg, sreg,
+         macro_build (&imm_expr, "sltiu", "t,r,j", op[0], op[1],
                       BFD_RELOC_LO16);
          break;
        }
       used_at = 1;
-      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build (NULL, "sltu", "d,v,t", dreg, sreg, AT);
+      load_register (AT, &imm_expr, GPR_SIZE == 64);
+      macro_build (NULL, "sltu", "d,v,t", op[0], op[1], AT);
       break;
 
     case M_SNE:
-      if (sreg == 0)
-       macro_build (NULL, "sltu", "d,v,t", dreg, 0, treg);
-      else if (treg == 0)
-       macro_build (NULL, "sltu", "d,v,t", dreg, 0, sreg);
+      if (op[1] == 0)
+       macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[2]);
+      else if (op[2] == 0)
+       macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[1]);
       else
        {
-         macro_build (NULL, "xor", "d,v,t", dreg, sreg, treg);
-         macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
+         macro_build (NULL, "xor", "d,v,t", op[0], op[1], op[2]);
+         macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[0]);
        }
       break;
 
     case M_SNE_I:
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+      if (imm_expr.X_add_number == 0)
        {
-         macro_build (NULL, "sltu", "d,v,t", dreg, 0, sreg);
+         macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[1]);
          break;
        }
-      if (sreg == 0)
+      if (op[1] == 0)
        {
-         as_warn (_("Instruction %s: result is always true"),
+         as_warn (_("instruction %s: result is always true"),
                   ip->insn_mo->name);
-         macro_build (&expr1, HAVE_32BIT_GPRS ? "addiu" : "daddiu", "t,r,j",
-                      dreg, 0, BFD_RELOC_LO16);
+         macro_build (&expr1, GPR_SIZE == 32 ? "addiu" : "daddiu", "t,r,j",
+                      op[0], 0, BFD_RELOC_LO16);
          break;
        }
       if (CPU_HAS_SEQ (mips_opts.arch)
          && -512 <= imm_expr.X_add_number
          && imm_expr.X_add_number < 512)
        {
-         macro_build (NULL, "snei", "t,r,+Q", dreg, sreg,
+         macro_build (NULL, "snei", "t,r,+Q", op[0], op[1],
                       (int) imm_expr.X_add_number);
          break;
        }
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number >= 0
+      if (imm_expr.X_add_number >= 0
          && imm_expr.X_add_number < 0x10000)
        {
-         macro_build (&imm_expr, "xori", "t,r,i", dreg, sreg, BFD_RELOC_LO16);
+         macro_build (&imm_expr, "xori", "t,r,i", op[0], op[1],
+                      BFD_RELOC_LO16);
        }
-      else if (imm_expr.X_op == O_constant
-              && imm_expr.X_add_number > -0x8000
+      else if (imm_expr.X_add_number > -0x8000
               && imm_expr.X_add_number < 0)
        {
          imm_expr.X_add_number = -imm_expr.X_add_number;
-         macro_build (&imm_expr, HAVE_32BIT_GPRS ? "addiu" : "daddiu",
-                      "t,r,j", dreg, sreg, BFD_RELOC_LO16);
+         macro_build (&imm_expr, GPR_SIZE == 32 ? "addiu" : "daddiu",
+                      "t,r,j", op[0], op[1], BFD_RELOC_LO16);
        }
       else if (CPU_HAS_SEQ (mips_opts.arch))
        {
          used_at = 1;
-         load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-         macro_build (NULL, "sne", "d,v,t", dreg, sreg, AT);
+         load_register (AT, &imm_expr, GPR_SIZE == 64);
+         macro_build (NULL, "sne", "d,v,t", op[0], op[1], AT);
          break;
        }
       else
        {
-         load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-         macro_build (NULL, "xor", "d,v,t", dreg, sreg, AT);
+         load_register (AT, &imm_expr, GPR_SIZE == 64);
+         macro_build (NULL, "xor", "d,v,t", op[0], op[1], AT);
          used_at = 1;
        }
-      macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
+      macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[0]);
       break;
 
     case M_SUB_I:
@@ -11527,11 +13043,11 @@ macro (struct mips_cl_insn *ip, char *str)
       s2 = "dsub";
       if (!mips_opts.micromips)
        goto do_subi;
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number > -0x200
+      if (imm_expr.X_add_number > -0x200
          && imm_expr.X_add_number <= 0x200)
        {
-         macro_build (NULL, s, "t,r,.", dreg, sreg, -imm_expr.X_add_number);
+         macro_build (NULL, s, "t,r,.", op[0], op[1],
+                      (int) -imm_expr.X_add_number);
          break;
        }
       goto do_subi_i;
@@ -11540,18 +13056,17 @@ macro (struct mips_cl_insn *ip, char *str)
       s = "daddiu";
       s2 = "dsubu";
     do_subi:
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number > -0x8000
+      if (imm_expr.X_add_number > -0x8000
          && imm_expr.X_add_number <= 0x8000)
        {
          imm_expr.X_add_number = -imm_expr.X_add_number;
-         macro_build (&imm_expr, s, "t,r,j", dreg, sreg, BFD_RELOC_LO16);
+         macro_build (&imm_expr, s, "t,r,j", op[0], op[1], BFD_RELOC_LO16);
          break;
        }
     do_subi_i:
       used_at = 1;
       load_register (AT, &imm_expr, dbl);
-      macro_build (NULL, s2, "d,v,t", dreg, sreg, AT);
+      macro_build (NULL, s2, "d,v,t", op[0], op[1], AT);
       break;
 
     case M_TEQ_I:
@@ -11573,8 +13088,8 @@ macro (struct mips_cl_insn *ip, char *str)
       s = "tne";
     trap:
       used_at = 1;
-      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build (NULL, s, "s,t", sreg, AT);
+      load_register (AT, &imm_expr, GPR_SIZE == 64);
+      macro_build (NULL, s, "s,t", op[0], AT);
       break;
 
     case M_TRUNCWS:
@@ -11582,26 +13097,24 @@ macro (struct mips_cl_insn *ip, char *str)
       gas_assert (!mips_opts.micromips);
       gas_assert (mips_opts.isa == ISA_MIPS1);
       used_at = 1;
-      sreg = (ip->insn_opcode >> 11) & 0x1f;   /* floating reg */
-      dreg = (ip->insn_opcode >> 06) & 0x1f;   /* floating reg */
 
       /*
        * Is the double cfc1 instruction a bug in the mips assembler;
        * or is there a reason for it?
        */
       start_noreorder ();
-      macro_build (NULL, "cfc1", "t,G", treg, RA);
-      macro_build (NULL, "cfc1", "t,G", treg, RA);
+      macro_build (NULL, "cfc1", "t,G", op[2], RA);
+      macro_build (NULL, "cfc1", "t,G", op[2], RA);
       macro_build (NULL, "nop", "");
       expr1.X_add_number = 3;
-      macro_build (&expr1, "ori", "t,r,i", AT, treg, BFD_RELOC_LO16);
+      macro_build (&expr1, "ori", "t,r,i", AT, op[2], BFD_RELOC_LO16);
       expr1.X_add_number = 2;
       macro_build (&expr1, "xori", "t,r,i", AT, AT, BFD_RELOC_LO16);
       macro_build (NULL, "ctc1", "t,G", AT, RA);
       macro_build (NULL, "nop", "");
       macro_build (NULL, mask == M_TRUNCWD ? "cvt.w.d" : "cvt.w.s", "D,S",
-                  dreg, sreg);
-      macro_build (NULL, "ctc1", "t,G", treg, RA);
+                  op[0], op[1]);
+      macro_build (NULL, "ctc1", "t,G", op[2], RA);
       macro_build (NULL, "nop", "");
       end_noreorder ();
       break;
@@ -11649,6 +13162,7 @@ macro (struct mips_cl_insn *ip, char *str)
       ust = 1;
 
     uld_st:
+      breg = op[2];
       large_offset = !small_offset_p (off, align, offbits);
       ep = &offset_expr;
       expr1.X_add_number = 0;
@@ -11670,16 +13184,16 @@ macro (struct mips_cl_insn *ip, char *str)
          offset_reloc[1] = BFD_RELOC_UNUSED;
          offset_reloc[2] = BFD_RELOC_UNUSED;
          breg = tempreg;
-         tempreg = treg;
+         tempreg = op[0];
          ep = &expr1;
        }
-      else if (!ust && treg == breg)
+      else if (!ust && op[0] == breg)
        {
          used_at = 1;
          tempreg = AT;
        }
       else
-       tempreg = treg;
+       tempreg = op[0];
 
       if (off == 1)
        goto ulh_sh;
@@ -11704,11 +13218,11 @@ macro (struct mips_cl_insn *ip, char *str)
                     offset_reloc[0], offset_reloc[1], offset_reloc[2], breg);
 
       /* If necessary, move the result in tempreg to the final destination.  */
-      if (!ust && treg != tempreg)
+      if (!ust && op[0] != tempreg)
         {
          /* Protect second load's delay slot.  */
          load_delay_nop ();
-         move_register (treg, tempreg);
+         move_register (op[0], tempreg);
        }
       break;
 
@@ -11716,7 +13230,7 @@ macro (struct mips_cl_insn *ip, char *str)
       used_at = 1;
       if (target_big_endian == ust)
        ep->X_add_number += off;
-      tempreg = ust || large_offset ? treg : AT;
+      tempreg = ust || large_offset ? op[0] : AT;
       macro_build (ep, s, "t,o(b)", tempreg, -1,
                   offset_reloc[0], offset_reloc[1], offset_reloc[2], breg);
 
@@ -11724,9 +13238,9 @@ macro (struct mips_cl_insn *ip, char *str)
          bytes.  Unfortunately for M_USH_A we have none available before
          the next store as AT holds the base address.  We deal with this
          case by clobbering TREG and then restoring it as with ULH.  */
-      tempreg = ust == large_offset ? treg : AT;
+      tempreg = ust == large_offset ? op[0] : AT;
       if (ust)
-       macro_build (NULL, "srl", SHFT_FMT, tempreg, treg, 8);
+       macro_build (NULL, "srl", SHFT_FMT, tempreg, op[0], 8);
 
       if (target_big_endian == ust)
        ep->X_add_number -= off;
@@ -11748,20 +13262,20 @@ macro (struct mips_cl_insn *ip, char *str)
       /* For ULH and M_USH_A OR the LSB in.  */
       if (!ust || large_offset)
        {
-         tempreg = !large_offset ? AT : treg;
+         tempreg = !large_offset ? AT : op[0];
          macro_build (NULL, "sll", SHFT_FMT, tempreg, tempreg, 8);
-         macro_build (NULL, "or", "d,v,t", treg, treg, AT);
+         macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
        }
       break;
 
     default:
       /* FIXME: Check if this is one of the itbl macros, since they
         are added dynamically.  */
-      as_bad (_("Macro %s not implemented yet"), ip->insn_mo->name);
+      as_bad (_("macro %s not implemented yet"), ip->insn_mo->name);
       break;
     }
   if (!mips_opts.at && used_at)
-    as_bad (_("Macro used $at after \".set noat\""));
+    as_bad (_("macro used $at after \".set noat\""));
 }
 
 /* Implement macros in mips16 mode.  */
@@ -11769,17 +13283,23 @@ macro (struct mips_cl_insn *ip, char *str)
 static void
 mips16_macro (struct mips_cl_insn *ip)
 {
+  const struct mips_operand_array *operands;
   int mask;
-  int xreg, yreg, zreg, tmp;
+  int tmp;
   expressionS expr1;
   int dbl;
   const char *s, *s2, *s3;
+  unsigned int op[MAX_OPERANDS];
+  unsigned int i;
 
   mask = ip->insn_mo->mask;
 
-  xreg = MIPS16_EXTRACT_OPERAND (RX, *ip);
-  yreg = MIPS16_EXTRACT_OPERAND (RY, *ip);
-  zreg = MIPS16_EXTRACT_OPERAND (RZ, *ip);
+  operands = insn_operands (ip);
+  for (i = 0; i < MAX_OPERANDS; i++)
+    if (operands->operand[i])
+      op[i] = insn_extract_operand (ip, operands->operand[i]);
+    else
+      op[i] = -1;
 
   expr1.X_op = O_constant;
   expr1.X_op_symbol = NULL;
@@ -11804,9 +13324,9 @@ mips16_macro (struct mips_cl_insn *ip)
       s = "mfhi";
     do_div3:
       start_noreorder ();
-      macro_build (NULL, dbl ? "ddiv" : "div", "0,x,y", xreg, yreg);
+      macro_build (NULL, dbl ? "ddiv" : "div", "0,x,y", op[1], op[2]);
       expr1.X_add_number = 2;
-      macro_build (&expr1, "bnez", "x,p", yreg);
+      macro_build (&expr1, "bnez", "x,p", op[2]);
       macro_build (NULL, "break", "6", 7);
 
       /* FIXME: The normal code checks for of -1 / -0x80000000 here,
@@ -11814,7 +13334,7 @@ mips16_macro (struct mips_cl_insn *ip)
          but I don't see how to do the comparisons without a temporary
          register.  */
       end_noreorder ();
-      macro_build (NULL, s, "x", zreg);
+      macro_build (NULL, s, "x", op[0]);
       break;
 
     case M_DIVU_3:
@@ -11834,19 +13354,19 @@ mips16_macro (struct mips_cl_insn *ip)
       s2 = "mfhi";
     do_divu3:
       start_noreorder ();
-      macro_build (NULL, s, "0,x,y", xreg, yreg);
+      macro_build (NULL, s, "0,x,y", op[1], op[2]);
       expr1.X_add_number = 2;
-      macro_build (&expr1, "bnez", "x,p", yreg);
+      macro_build (&expr1, "bnez", "x,p", op[2]);
       macro_build (NULL, "break", "6", 7);
       end_noreorder ();
-      macro_build (NULL, s2, "x", zreg);
+      macro_build (NULL, s2, "x", op[0]);
       break;
 
     case M_DMUL:
       dbl = 1;
     case M_MUL:
-      macro_build (NULL, dbl ? "dmultu" : "multu", "x,y", xreg, yreg);
-      macro_build (NULL, "mflo", "x", zreg);
+      macro_build (NULL, dbl ? "dmultu" : "multu", "x,y", op[1], op[2]);
+      macro_build (NULL, "mflo", "x", op[0]);
       break;
 
     case M_DSUBU_I:
@@ -11854,24 +13374,18 @@ mips16_macro (struct mips_cl_insn *ip)
       goto do_subu;
     case M_SUBU_I:
     do_subu:
-      if (imm_expr.X_op != O_constant)
-       as_bad (_("Unsupported large constant"));
       imm_expr.X_add_number = -imm_expr.X_add_number;
-      macro_build (&imm_expr, dbl ? "daddiu" : "addiu", "y,x,4", yreg, xreg);
+      macro_build (&imm_expr, dbl ? "daddiu" : "addiu", "y,x,4", op[0], op[1]);
       break;
 
     case M_SUBU_I_2:
-      if (imm_expr.X_op != O_constant)
-       as_bad (_("Unsupported large constant"));
       imm_expr.X_add_number = -imm_expr.X_add_number;
-      macro_build (&imm_expr, "addiu", "x,k", xreg);
+      macro_build (&imm_expr, "addiu", "x,k", op[0]);
       break;
 
     case M_DSUBU_I_2:
-      if (imm_expr.X_op != O_constant)
-       as_bad (_("Unsupported large constant"));
       imm_expr.X_add_number = -imm_expr.X_add_number;
-      macro_build (&imm_expr, "daddiu", "y,j", yreg);
+      macro_build (&imm_expr, "daddiu", "y,j", op[0]);
       break;
 
     case M_BEQ:
@@ -11915,12 +13429,12 @@ mips16_macro (struct mips_cl_insn *ip)
       s2 = "btnez";
 
     do_reverse_branch:
-      tmp = xreg;
-      xreg = yreg;
-      yreg = tmp;
+      tmp = op[1];
+      op[1] = op[0];
+      op[0] = tmp;
 
     do_branch:
-      macro_build (NULL, s, "x,y", xreg, yreg);
+      macro_build (NULL, s, "x,y", op[0], op[1]);
       macro_build (&offset_expr, s2, "p");
       break;
 
@@ -11975,55 +13489,108 @@ mips16_macro (struct mips_cl_insn *ip)
       s3 = "x,8";
 
     do_addone_branch_i:
-      if (imm_expr.X_op != O_constant)
-       as_bad (_("Unsupported large constant"));
       ++imm_expr.X_add_number;
 
     do_branch_i:
-      macro_build (&imm_expr, s, s3, xreg);
+      macro_build (&imm_expr, s, s3, op[0]);
       macro_build (&offset_expr, s2, "p");
       break;
 
-    case M_ABS:
-      expr1.X_add_number = 0;
-      macro_build (&expr1, "slti", "x,8", yreg);
-      if (xreg != yreg)
-       move_register (xreg, yreg);
-      expr1.X_add_number = 2;
-      macro_build (&expr1, "bteqz", "p");
-      macro_build (NULL, "neg", "x,w", xreg, xreg);
+    case M_ABS:
+      expr1.X_add_number = 0;
+      macro_build (&expr1, "slti", "x,8", op[1]);
+      if (op[0] != op[1])
+       macro_build (NULL, "move", "y,X", op[0], mips16_to_32_reg_map[op[1]]);
+      expr1.X_add_number = 2;
+      macro_build (&expr1, "bteqz", "p");
+      macro_build (NULL, "neg", "x,w", op[0], op[0]);
+      break;
+    }
+}
+
+/* Look up instruction [START, START + LENGTH) in HASH.  Record any extra
+   opcode bits in *OPCODE_EXTRA.  */
+
+static struct mips_opcode *
+mips_lookup_insn (struct hash_control *hash, const char *start,
+                 ssize_t length, unsigned int *opcode_extra)
+{
+  char *name, *dot, *p;
+  unsigned int mask, suffix;
+  ssize_t opend;
+  struct mips_opcode *insn;
+
+  /* Make a copy of the instruction so that we can fiddle with it.  */
+  name = alloca (length + 1);
+  memcpy (name, start, length);
+  name[length] = '\0';
+
+  /* Look up the instruction as-is.  */
+  insn = (struct mips_opcode *) hash_find (hash, name);
+  if (insn)
+    return insn;
+
+  dot = strchr (name, '.');
+  if (dot && dot[1])
+    {
+      /* Try to interpret the text after the dot as a VU0 channel suffix.  */
+      p = mips_parse_vu0_channels (dot + 1, &mask);
+      if (*p == 0 && mask != 0)
+       {
+         *dot = 0;
+         insn = (struct mips_opcode *) hash_find (hash, name);
+         *dot = '.';
+         if (insn && (insn->pinfo2 & INSN2_VU0_CHANNEL_SUFFIX) != 0)
+           {
+             *opcode_extra |= mask << mips_vu0_channel_mask.lsb;
+             return insn;
+           }
+       }
+    }
+
+  if (mips_opts.micromips)
+    {
+      /* See if there's an instruction size override suffix,
+        either `16' or `32', at the end of the mnemonic proper,
+        that defines the operation, i.e. before the first `.'
+        character if any.  Strip it and retry.  */
+      opend = dot != NULL ? dot - name : length;
+      if (opend >= 3 && name[opend - 2] == '1' && name[opend - 1] == '6')
+       suffix = 2;
+      else if (name[opend - 2] == '3' && name[opend - 1] == '2')
+       suffix = 4;
+      else
+       suffix = 0;
+      if (suffix)
+       {
+         memcpy (name + opend - 2, name + opend, length - opend + 1);
+         insn = (struct mips_opcode *) hash_find (hash, name);
+         if (insn)
+           {
+             forced_insn_length = suffix;
+             return insn;
+           }
+       }
     }
+
+  return NULL;
 }
 
 /* Assemble an instruction into its binary format.  If the instruction
-   is a macro, set imm_expr, imm2_expr and offset_expr to the values
-   associated with "I", "+I" and "A" operands respectively.  Otherwise
-   store the value of the relocatable field (if any) in offset_expr.
-   In both cases set offset_reloc to the relocation operators applied
-   to offset_expr.  */
+   is a macro, set imm_expr and offset_expr to the values associated
+   with "I" and "A" operands respectively.  Otherwise store the value
+   of the relocatable field (if any) in offset_expr.  In both cases
+   set offset_reloc to the relocation operators applied to offset_expr.  */
 
 static void
-mips_ip (char *str, struct mips_cl_insn *ip)
+mips_ip (char *str, struct mips_cl_insn *insn)
 {
-  bfd_boolean wrong_delay_slot_insns = FALSE;
-  bfd_boolean need_delay_slot_ok = TRUE;
-  struct mips_opcode *firstinsn = NULL;
-  const struct mips_opcode *past;
+  const struct mips_opcode *first, *past;
   struct hash_control *hash;
-  const char *args;
-  char c = 0;
-  struct mips_opcode *insn;
-  long opend;
-  char *name;
-  char *dot;
   char format;
-  long end;
-  const struct mips_operand *operand;
-  struct mips_arg_info arg;
+  size_t end;
   struct mips_operand_token *tokens;
-  bfd_boolean optional_reg;
-
-  insn_error = NULL;
+  unsigned int opcode_extra;
 
   if (mips_opts.micromips)
     {
@@ -12036,51 +13603,22 @@ mips_ip (char *str, struct mips_cl_insn *ip)
       past = &mips_opcodes[NUMOPCODES];
     }
   forced_insn_length = 0;
-  insn = NULL;
+  opcode_extra = 0;
 
   /* We first try to match an instruction up to a space or to the end.  */
   for (end = 0; str[end] != '\0' && !ISSPACE (str[end]); end++)
     continue;
 
-  /* Make a copy of the instruction so that we can fiddle with it.  */
-  name = alloca (end + 1);
-  memcpy (name, str, end);
-  name[end] = '\0';
-
-  for (;;)
-    {
-      insn = (struct mips_opcode *) hash_find (hash, name);
-
-      if (insn != NULL || !mips_opts.micromips)
-       break;
-      if (forced_insn_length)
-       break;
-
-      /* See if there's an instruction size override suffix,
-         either `16' or `32', at the end of the mnemonic proper,
-         that defines the operation, i.e. before the first `.'
-         character if any.  Strip it and retry.  */
-      dot = strchr (name, '.');
-      opend = dot != NULL ? dot - name : end;
-      if (opend < 3)
-       break;
-      if (name[opend - 2] == '1' && name[opend - 1] == '6')
-       forced_insn_length = 2;
-      else if (name[opend - 2] == '3' && name[opend - 1] == '2')
-       forced_insn_length = 4;
-      else
-       break;
-      memcpy (name + opend - 2, name + opend, end - opend + 1);
-    }
-  if (insn == NULL)
+  first = mips_lookup_insn (hash, str, end, &opcode_extra);
+  if (first == NULL)
     {
-      insn_error = _("Unrecognized opcode");
+      set_insn_error (0, _("unrecognized opcode"));
       return;
     }
 
-  if (strcmp (name, "li.s") == 0)
+  if (strcmp (first->name, "li.s") == 0)
     format = 'f';
-  else if (strcmp (name, "li.d") == 0)
+  else if (strcmp (first->name, "li.d") == 0)
     format = 'd';
   else
     format = 0;
@@ -12088,380 +13626,11 @@ mips_ip (char *str, struct mips_cl_insn *ip)
   if (!tokens)
     return;
 
-  /* For microMIPS instructions placed in a fixed-length branch delay slot
-     we make up to two passes over the relevant fragment of the opcode
-     table.  First we try instructions that meet the delay slot's length
-     requirement.  If none matched, then we retry with the remaining ones
-     and if one matches, then we use it and then issue an appropriate
-     warning later on.  */
-  for (;;)
-    {
-      bfd_boolean delay_slot_ok;
-      bfd_boolean size_ok;
-      bfd_boolean ok;
-      bfd_boolean more_alts;
-
-      gas_assert (strcmp (insn->name, name) == 0);
-
-      ok = is_opcode_valid (insn);
-      size_ok = is_size_valid (insn);
-      delay_slot_ok = is_delay_slot_valid (insn);
-      if (!delay_slot_ok && !wrong_delay_slot_insns)
-       {
-         firstinsn = insn;
-         wrong_delay_slot_insns = TRUE;
-       }
-      more_alts = (insn + 1 < past
-                  && strcmp (insn[0].name, insn[1].name) == 0);
-      if (!ok || !size_ok || delay_slot_ok != need_delay_slot_ok)
-       {
-         static char buf[256];
-
-         if (more_alts)
-           {
-             ++insn;
-             continue;
-           }
-         if (wrong_delay_slot_insns && need_delay_slot_ok)
-           {
-             gas_assert (firstinsn);
-             need_delay_slot_ok = FALSE;
-             past = insn + 1;
-             insn = firstinsn;
-             continue;
-           }
-
-         obstack_free (&mips_operand_tokens, tokens);
-         if (insn_error)
-           return;
-
-         if (!ok)
-           sprintf (buf, _("Opcode not supported on this processor: %s (%s)"),
-                    mips_cpu_info_from_arch (mips_opts.arch)->name,
-                    mips_cpu_info_from_isa (mips_opts.isa)->name);
-         else if (mips_opts.insn32)
-           sprintf (buf, _("Opcode not supported in the `insn32' mode"));
-         else
-           sprintf (buf, _("Unrecognized %u-bit version of microMIPS opcode"),
-                    8 * forced_insn_length);
-         insn_error = buf;
-
-         return;
-       }
-
-      imm_expr.X_op = O_absent;
-      imm2_expr.X_op = O_absent;
-      offset_expr.X_op = O_absent;
-      offset_reloc[0] = BFD_RELOC_UNUSED;
-      offset_reloc[1] = BFD_RELOC_UNUSED;
-      offset_reloc[2] = BFD_RELOC_UNUSED;
-
-      create_insn (ip, insn);
-      insn_error = NULL;
-      memset (&arg, 0, sizeof (arg));
-      arg.insn = ip;
-      arg.token = tokens;
-      arg.argnum = 1;
-      arg.last_regno = ILLEGAL_REG;
-      arg.dest_regno = ILLEGAL_REG;
-      arg.soft_match = (more_alts
-                       || (wrong_delay_slot_insns && need_delay_slot_ok));
-      for (args = insn->args;; ++args)
-       {
-         if (arg.token->type == OT_END)
-           {
-             /* Handle unary instructions in which only one operand is given.
-                The source is then the same as the destination.  */
-             if (arg.opnum == 1 && *args == ',')
-               switch (args[1])
-                 {
-                 case 'r':
-                 case 'v':
-                 case 'w':
-                 case 'W':
-                 case 'V':
-                   arg.token = tokens;
-                   arg.argnum = 1;
-                   continue;
-                 }
-
-             /* Treat elided base registers as $0.  */
-             if (strcmp (args, "(b)") == 0)
-               args += 3;
-
-             /* Fail the match if there were too few operands.  */
-             if (*args)
-               break;
-
-             /* Successful match.  */
-             if (arg.dest_regno == arg.last_regno
-                 && strncmp (ip->insn_mo->name, "jalr", 4) == 0)
-               {
-                 if (arg.opnum == 2)
-                   as_bad (_("Source and destination must be different"));
-                 else if (arg.last_regno == 31)
-                   as_bad (_("A destination register must be supplied"));
-               }
-             check_completed_insn (&arg);
-             obstack_free (&mips_operand_tokens, tokens);
-             return;
-           }
-
-         /* Fail the match if the line has too many operands.   */
-         if (*args == 0)
-           break;
-
-         /* Handle characters that need to match exactly.  */
-         if (*args == '(' || *args == ')' || *args == ',')
-           {
-             if (match_char (&arg, *args))
-               continue;
-             break;
-           }
-
-         /* Handle special macro operands.  Work out the properties of
-            other operands.  */
-         arg.opnum += 1;
-         arg.lax_max = FALSE;
-         optional_reg = FALSE;
-         switch (*args)
-           {
-           case '+':
-             switch (args[1])
-               {
-               case '1':
-               case '2':
-               case '3':
-               case '4':
-               case 'B':
-               case 'C':
-               case 'F':
-               case 'G':
-               case 'H':
-               case 'J':
-               case 'Q':
-               case 'S':
-               case 's':
-                 /* If these integer forms come last, there is no other
-                    form of the instruction that could match.  Prefer to
-                    give detailed error messages where possible.  */
-                 if (args[2] == 0)
-                   arg.soft_match = FALSE;
-                 break;
-
-               case 'I':
-                 /* "+I" is like "I", except that imm2_expr is used.  */
-                 if (match_const_int (&arg, &imm2_expr.X_add_number, 0))
-                   imm2_expr.X_op = O_constant;
-                 else
-                   insn_error = _("absolute expression required");
-                 if (HAVE_32BIT_GPRS)
-                   normalize_constant_expr (&imm2_expr);
-                 ++args;
-                 continue;
-
-               case 'i':
-                 *offset_reloc = BFD_RELOC_MIPS_JMP;
-                 break;
-               }
-             break;
-
-           case '\'':
-           case ':':
-           case '@':
-           case '^':
-           case '$':
-           case '\\':
-           case '%':
-           case '|':
-           case '0':
-           case '1':
-           case '2':
-           case '3':
-           case '4':
-           case '5':
-           case '6':
-           case '8':
-           case 'B':
-           case 'C':
-           case 'J':
-           case 'O':
-           case 'P':
-           case 'Q':
-           case 'c':
-           case 'h':
-           case 'q':
-             /* If these integer forms come last, there is no other
-                form of the instruction that could match.  Prefer to
-                give detailed error messages where possible.  */
-             if (args[1] == 0)
-               arg.soft_match = FALSE;
-             break;
-
-           case 'r':
-           case 'v':
-           case 'w':
-           case 'W':
-           case 'V':
-             /* We have already matched a comma by this point, so the register
-                is only optional if there is another operand to come.  */
-             gas_assert (arg.opnum == 2);
-             optional_reg = (args[1] == ',');
-             break;
-
-           case 'I':
-             if (match_const_int (&arg, &imm_expr.X_add_number, 0))
-               imm_expr.X_op = O_constant;
-             else
-               insn_error = _("absolute expression required");
-             if (HAVE_32BIT_GPRS)
-               normalize_constant_expr (&imm_expr);
-             continue;
-
-           case 'A':
-             if (arg.token->type == OT_CHAR && arg.token->u.ch == '(')
-               {
-                 /* Assume that the offset has been elided and that what
-                    we saw was a base register.  The match will fail later
-                    if that assumption turns out to be wrong.  */
-                 offset_expr.X_op = O_constant;
-                 offset_expr.X_add_number = 0;
-               }
-             else if (match_expression (&arg, &offset_expr, offset_reloc))
-               normalize_address_expr (&offset_expr);
-             else
-               insn_error = _("absolute expression required");
-             continue;
-
-           case 'F':
-             if (!match_float_constant (&arg, &imm_expr, &offset_expr,
-                                        8, TRUE))
-               insn_error = _("floating-point expression required");
-             continue;
-
-           case 'L':
-             if (!match_float_constant (&arg, &imm_expr, &offset_expr,
-                                        8, FALSE))
-               insn_error = _("floating-point expression required");
-             continue;
-
-           case 'f':
-             if (!match_float_constant (&arg, &imm_expr, &offset_expr,
-                                        4, TRUE))
-               insn_error = _("floating-point expression required");
-             continue;
-
-           case 'l':
-             if (!match_float_constant (&arg, &imm_expr, &offset_expr,
-                                        4, FALSE))
-               insn_error = _("floating-point expression required");
-             continue;
-
-             /* ??? This is the traditional behavior, but is flaky if
-                there are alternative versions of the same instruction
-                for different subarchitectures.  The next alternative
-                might not be suitable.  */
-           case 'j':
-             /* For compatibility with older assemblers, we accept
-                0x8000-0xffff as signed 16-bit numbers when only
-                signed numbers are allowed.  */
-             arg.lax_max = !more_alts;
-           case 'i':
-             /* Only accept non-constant operands if this is the
-                final alternative.  Later alternatives might include
-                a macro implementation.  */
-             arg.allow_nonconst = !more_alts;
-             break;
-
-           case 'u':
-             /* There are no macro implementations for out-of-range values.  */
-             arg.allow_nonconst = TRUE;
-             break;
-
-           case 'o':
-             /* There should always be a macro implementation.  */
-             arg.allow_nonconst = FALSE;
-             break;
-
-           case 'p':
-             *offset_reloc = BFD_RELOC_16_PCREL_S2;
-             break;
-
-           case 'a':
-             *offset_reloc = BFD_RELOC_MIPS_JMP;
-             break;
-
-           case 'm':
-             gas_assert (mips_opts.micromips);
-             c = args[1];
-             switch (c)
-               {
-               case 't':
-               case 'c':
-               case 'e':
-                 /* We have already matched a comma by this point,
-                    so the register is only optional if there is another
-                    operand to come.  */
-                 gas_assert (arg.opnum == 2);
-                 optional_reg = (args[2] == ',');
-                 break;
-
-               case 'D':
-               case 'E':
-                 if (!forced_insn_length)
-                   *offset_reloc = (int) BFD_RELOC_UNUSED + c;
-                 else if (c == 'D')
-                   *offset_reloc = BFD_RELOC_MICROMIPS_10_PCREL_S1;
-                 else
-                   *offset_reloc = BFD_RELOC_MICROMIPS_7_PCREL_S1;
-                 break;
-               }
-             break;
-           }
-
-         operand = (mips_opts.micromips
-                    ? decode_micromips_operand (args)
-                    : decode_mips_operand (args));
-         if (!operand)
-           abort ();
-
-         if (optional_reg
-             && (arg.token[0].type != OT_REG
-                 || arg.token[1].type == OT_END))
-           {
-             /* Assume that the register has been elided and is the
-                same as the first operand.  */
-             arg.token = tokens;
-             arg.argnum = 1;
-           }
-
-         if (!match_operand (&arg, operand))
-           break;
-
-         /* Skip prefixes.  */
-         if (*args == '+' || *args == 'm')
-           args++;
+  if (!match_insns (insn, first, past, tokens, opcode_extra, FALSE)
+      && !match_insns (insn, first, past, tokens, opcode_extra, TRUE))
+    set_insn_error (0, _("invalid operands"));
 
-         continue;
-       }
-      /* Args don't match.  */
-      insn_error = _("Illegal operands");
-      if (more_alts)
-       {
-         ++insn;
-         continue;
-       }
-      if (wrong_delay_slot_insns && need_delay_slot_ok)
-       {
-         gas_assert (firstinsn);
-         need_delay_slot_ok = FALSE;
-         past = insn + 1;
-         insn = firstinsn;
-         continue;
-       }
-      obstack_free (&mips_operand_tokens, tokens);
-      return;
-    }
+  obstack_free (&mips_operand_tokens, tokens);
 }
 
 /* As for mips_ip, but used when assembling MIPS16 code.
@@ -12469,344 +13638,68 @@ mips_ip (char *str, struct mips_cl_insn *ip)
    bytes if the user explicitly requested a small or extended instruction.  */
 
 static void
-mips16_ip (char *str, struct mips_cl_insn *ip)
+mips16_ip (char *str, struct mips_cl_insn *insn)
 {
-  char *s;
-  const char *args;
-  struct mips_opcode *insn;
-  const struct mips_operand *operand;
-  const struct mips_operand *ext_operand;
-  struct mips_arg_info arg;
+  char *end, *s, c;
+  struct mips_opcode *first;
   struct mips_operand_token *tokens;
-  bfd_boolean optional_reg;
-
-  insn_error = NULL;
 
   forced_insn_length = 0;
 
   for (s = str; ISLOWER (*s); ++s)
     ;
-  switch (*s)
-    {
-    case '\0':
-      break;
-
-    case ' ':
-      *s++ = '\0';
-      break;
-
-    case '.':
-      if (s[1] == 't' && s[2] == ' ')
-       {
-         *s = '\0';
-         forced_insn_length = 2;
-         s += 3;
-         break;
-       }
-      else if (s[1] == 'e' && s[2] == ' ')
-       {
-         *s = '\0';
-         forced_insn_length = 4;
-         s += 3;
-         break;
-       }
-      /* Fall through.  */
-    default:
-      insn_error = _("unknown opcode");
-      return;
-    }
-
-  if (mips_opts.noautoextend && !forced_insn_length)
-    forced_insn_length = 2;
-
-  if ((insn = (struct mips_opcode *) hash_find (mips16_op_hash, str)) == NULL)
-    {
-      insn_error = _("unrecognized opcode");
-      return;
-    }
-
-  tokens = mips_parse_arguments (s, 0);
-  if (!tokens)
-    return;
-
-  for (;;)
-    {
-      bfd_boolean ok;
-      bfd_boolean more_alts;
-      char relax_char;
-
-      gas_assert (strcmp (insn->name, str) == 0);
-
-      ok = is_opcode_valid_16 (insn);
-      more_alts = (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes]
-                  && strcmp (insn[0].name, insn[1].name) == 0);
-      if (! ok)
-       {
-         if (more_alts)
-           {
-             ++insn;
-             continue;
-           }
-         else
-           {
-             if (!insn_error)
-               {
-                 static char buf[100];
-                 sprintf (buf,
-                          _("Opcode not supported on this processor: %s (%s)"),
-                          mips_cpu_info_from_arch (mips_opts.arch)->name,
-                          mips_cpu_info_from_isa (mips_opts.isa)->name);
-                 insn_error = buf;
-               }
-             obstack_free (&mips_operand_tokens, tokens);
-             return;
-           }
-       }
-
-      create_insn (ip, insn);
-      imm_expr.X_op = O_absent;
-      imm2_expr.X_op = O_absent;
-      offset_expr.X_op = O_absent;
-      offset_reloc[0] = BFD_RELOC_UNUSED;
-      offset_reloc[1] = BFD_RELOC_UNUSED;
-      offset_reloc[2] = BFD_RELOC_UNUSED;
-      relax_char = 0;
-
-      memset (&arg, 0, sizeof (arg));
-      arg.insn = ip;
-      arg.token = tokens;
-      arg.argnum = 1;
-      arg.last_regno = ILLEGAL_REG;
-      arg.dest_regno = ILLEGAL_REG;
-      arg.soft_match = more_alts;
-      relax_char = 0;
-      for (args = insn->args; 1; ++args)
-       {
-         int c;
-
-         if (arg.token->type == OT_END)
-           {
-             offsetT value;
-
-             /* Handle unary instructions in which only one operand is given.
-                The source is then the same as the destination.  */
-             if (arg.opnum == 1 && *args == ',')
-               switch (args[1])
-                 {
-                 case 'v':
-                 case 'w':
-                   arg.token = tokens;
-                   arg.argnum = 1;
-                   continue;
-                 }
-
-             /* Fail the match if there were too few operands.  */
-             if (*args)
-               break;
-
-             /* Successful match.  Stuff the immediate value in now, if
-                we can.  */
-             if (insn->pinfo == INSN_MACRO)
-               {
-                 gas_assert (relax_char == 0);
-                 gas_assert (*offset_reloc == BFD_RELOC_UNUSED);
-               }
-             else if (relax_char
-                      && offset_expr.X_op == O_constant
-                      && calculate_reloc (*offset_reloc,
-                                          offset_expr.X_add_number,
-                                          &value))
-               {
-                 mips16_immed (NULL, 0, relax_char, *offset_reloc, value,
-                               forced_insn_length, &ip->insn_opcode);
-                 offset_expr.X_op = O_absent;
-                 *offset_reloc = BFD_RELOC_UNUSED;
-               }
-             else if (relax_char && *offset_reloc != BFD_RELOC_UNUSED)
-               {
-                 if (forced_insn_length == 2)
-                   as_bad (_("invalid unextended operand value"));
-                 forced_insn_length = 4;
-                 ip->insn_opcode |= MIPS16_EXTEND;
-               }
-             else if (relax_char)
-               *offset_reloc = (int) BFD_RELOC_UNUSED + relax_char;
-
-             check_completed_insn (&arg);
-             obstack_free (&mips_operand_tokens, tokens);
-             return;
-           }
-
-         /* Fail the match if the line has too many operands.   */
-         if (*args == 0)
-           break;
-
-         /* Handle characters that need to match exactly.  */
-         if (*args == '(' || *args == ')' || *args == ',')
-           {
-             if (match_char (&arg, *args))
-               continue;
-             break;
-           }
-
-         arg.opnum += 1;
-         optional_reg = FALSE;
-         c = *args;
-         switch (c)
-           {
-           case 'v':
-           case 'w':
-             optional_reg = (args[1] == ',');
-             break;
-
-           case 'p':
-           case 'q':
-           case 'A':
-           case 'B':
-           case 'E':
-             relax_char = c;
-             break;
-
-           case 'I':
-             if (match_const_int (&arg, &imm_expr.X_add_number, 0))
-               imm_expr.X_op = O_constant;
-             else
-               insn_error = _("absolute expression required");
-             if (HAVE_32BIT_GPRS)
-               normalize_constant_expr (&imm_expr);
-             continue;
-
-           case 'a':
-           case 'i':
-             *offset_reloc = BFD_RELOC_MIPS16_JMP;
-             ip->insn_opcode <<= 16;
-             break;
-           }
-
-         operand = decode_mips16_operand (c, FALSE);
-         if (!operand)
-           abort ();
-
-         /* '6' is a special case.  It is used for BREAK and SDBBP,
-            whose operands are only meaningful to the software that decodes
-            them.  This means that there is no architectural reason why
-            they cannot be prefixed by EXTEND, but in practice,
-            exception handlers will only look at the instruction
-            itself.  We therefore allow '6' to be extended when
-            disassembling but not when assembling.  */
-         if (operand->type != OP_PCREL && c != '6')
-           {
-             ext_operand = decode_mips16_operand (c, TRUE);
-             if (operand != ext_operand)
-               {
-                 if (arg.token->type == OT_CHAR && arg.token->u.ch == '(')
-                   {
-                     offset_expr.X_op = O_constant;
-                     offset_expr.X_add_number = 0;
-                     relax_char = c;
-                     continue;
-                   }
-
-                 /* We need the OT_INTEGER check because some MIPS16
-                    immediate variants are listed before the register ones.  */
-                 if (arg.token->type != OT_INTEGER
-                     || !match_expression (&arg, &offset_expr, offset_reloc))
-                   break;
-
-                 /* '8' is used for SLTI(U) and has traditionally not
-                    been allowed to take relocation operators.  */
-                 if (offset_reloc[0] != BFD_RELOC_UNUSED
-                     && (ext_operand->size != 16 || c == '8'))
-                   break;
-
-                 relax_char = c;
-                 continue;
-               }
-           }
+  end = s;
+  c = *end;
+  switch (c)
+    {
+    case '\0':
+      break;
 
-         if (optional_reg
-             && (arg.token[0].type != OT_REG
-                 || arg.token[1].type == OT_END))
-           {
-             /* Assume that the register has been elided and is the
-                same as the first operand.  */
-             arg.token = tokens;
-             arg.argnum = 1;
-           }
+    case ' ':
+      s++;
+      break;
 
-         if (!match_operand (&arg, operand))
-           break;
-         continue;
+    case '.':
+      if (s[1] == 't' && s[2] == ' ')
+       {
+         forced_insn_length = 2;
+         s += 3;
+         break;
        }
-
-      /* Args don't match.  */
-      if (more_alts)
+      else if (s[1] == 'e' && s[2] == ' ')
        {
-         ++insn;
-         continue;
+         forced_insn_length = 4;
+         s += 3;
+         break;
        }
+      /* Fall through.  */
+    default:
+      set_insn_error (0, _("unrecognized opcode"));
+      return;
+    }
+
+  if (mips_opts.noautoextend && !forced_insn_length)
+    forced_insn_length = 2;
 
-      insn_error = _("illegal operands");
+  *end = 0;
+  first = (struct mips_opcode *) hash_find (mips16_op_hash, str);
+  *end = c;
 
-      obstack_free (&mips_operand_tokens, tokens);
+  if (!first)
+    {
+      set_insn_error (0, _("unrecognized opcode"));
       return;
     }
-}
-
-/* This structure holds information we know about a mips16 immediate
-   argument type.  */
 
-struct mips16_immed_operand
-{
-  /* The type code used in the argument string in the opcode table.  */
-  int type;
-  /* The number of bits in the short form of the opcode.  */
-  int nbits;
-  /* The number of bits in the extended form of the opcode.  */
-  int extbits;
-  /* The amount by which the short form is shifted when it is used;
-     for example, the sw instruction has a shift count of 2.  */
-  int shift;
-  /* The amount by which the short form is shifted when it is stored
-     into the instruction code.  */
-  int op_shift;
-  /* Non-zero if the short form is unsigned.  */
-  int unsp;
-  /* Non-zero if the extended form is unsigned.  */
-  int extu;
-  /* Non-zero if the value is PC relative.  */
-  int pcrel;
-};
+  tokens = mips_parse_arguments (s, 0);
+  if (!tokens)
+    return;
 
-/* The mips16 immediate operand types.  */
-
-static const struct mips16_immed_operand mips16_immed_operands[] =
-{
-  { '<',  3,  5, 0, MIPS16OP_SH_RZ,   1, 1, 0 },
-  { '>',  3,  5, 0, MIPS16OP_SH_RX,   1, 1, 0 },
-  { '[',  3,  6, 0, MIPS16OP_SH_RZ,   1, 1, 0 },
-  { ']',  3,  6, 0, MIPS16OP_SH_RX,   1, 1, 0 },
-  { '4',  4, 15, 0, MIPS16OP_SH_IMM4, 0, 0, 0 },
-  { '5',  5, 16, 0, MIPS16OP_SH_IMM5, 1, 0, 0 },
-  { 'H',  5, 16, 1, MIPS16OP_SH_IMM5, 1, 0, 0 },
-  { 'W',  5, 16, 2, MIPS16OP_SH_IMM5, 1, 0, 0 },
-  { 'D',  5, 16, 3, MIPS16OP_SH_IMM5, 1, 0, 0 },
-  { 'j',  5, 16, 0, MIPS16OP_SH_IMM5, 0, 0, 0 },
-  { '8',  8, 16, 0, MIPS16OP_SH_IMM8, 1, 0, 0 },
-  { 'V',  8, 16, 2, MIPS16OP_SH_IMM8, 1, 0, 0 },
-  { 'C',  8, 16, 3, MIPS16OP_SH_IMM8, 1, 0, 0 },
-  { 'U',  8, 16, 0, MIPS16OP_SH_IMM8, 1, 1, 0 },
-  { 'k',  8, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 0 },
-  { 'K',  8, 16, 3, MIPS16OP_SH_IMM8, 0, 0, 0 },
-  { 'p',  8, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 1 },
-  { 'q', 11, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 1 },
-  { 'A',  8, 16, 2, MIPS16OP_SH_IMM8, 1, 0, 1 },
-  { 'B',  5, 16, 3, MIPS16OP_SH_IMM5, 1, 0, 1 },
-  { 'E',  5, 16, 2, MIPS16OP_SH_IMM5, 1, 0, 1 }
-};
+  if (!match_mips16_insns (insn, first, tokens))
+    set_insn_error (0, _("invalid operands"));
 
-#define MIPS16_NUM_IMMED \
-  (sizeof mips16_immed_operands / sizeof mips16_immed_operands[0])
+  obstack_free (&mips_operand_tokens, tokens);
+}
 
 /* Marshal immediate value VAL for an extended MIPS16 instruction.
    NBITS is the number of significant bits in VAL.  */
@@ -12833,6 +13726,43 @@ mips16_immed_extend (offsetT val, unsigned int nbits)
   return (extval << 16) | val;
 }
 
+/* Like decode_mips16_operand, but require the operand to be defined and
+   require it to be an integer.  */
+
+static const struct mips_int_operand *
+mips16_immed_operand (int type, bfd_boolean extended_p)
+{
+  const struct mips_operand *operand;
+
+  operand = decode_mips16_operand (type, extended_p);
+  if (!operand || (operand->type != OP_INT && operand->type != OP_PCREL))
+    abort ();
+  return (const struct mips_int_operand *) operand;
+}
+
+/* Return true if SVAL fits OPERAND.  RELOC is as for mips16_immed.  */
+
+static bfd_boolean
+mips16_immed_in_range_p (const struct mips_int_operand *operand,
+                        bfd_reloc_code_real_type reloc, offsetT sval)
+{
+  int min_val, max_val;
+
+  min_val = mips_int_operand_min (operand);
+  max_val = mips_int_operand_max (operand);
+  if (reloc != BFD_RELOC_UNUSED)
+    {
+      if (min_val < 0)
+       sval = SEXT_16BIT (sval);
+      else
+       sval &= 0xffff;
+    }
+
+  return (sval >= min_val
+         && sval <= max_val
+         && (sval & ((1 << operand->shift) - 1)) == 0);
+}
+
 /* Install immediate value VAL into MIPS16 instruction *INSN,
    extending it if necessary.  The instruction in *INSN may
    already be extended.
@@ -12849,46 +13779,11 @@ mips16_immed (char *file, unsigned int line, int type,
              bfd_reloc_code_real_type reloc, offsetT val,
              unsigned int user_insn_length, unsigned long *insn)
 {
-  const struct mips16_immed_operand *op;
-  int mintiny, maxtiny;
-
-  op = mips16_immed_operands;
-  while (op->type != type)
-    {
-      ++op;
-      gas_assert (op < mips16_immed_operands + MIPS16_NUM_IMMED);
-    }
-
-  if (op->unsp)
-    {
-      if (type == '<' || type == '>' || type == '[' || type == ']')
-       {
-         mintiny = 1;
-         maxtiny = 1 << op->nbits;
-       }
-      else
-       {
-         mintiny = 0;
-         maxtiny = (1 << op->nbits) - 1;
-       }
-      if (reloc != BFD_RELOC_UNUSED)
-       val &= 0xffff;
-    }
-  else
-    {
-      mintiny = - (1 << (op->nbits - 1));
-      maxtiny = (1 << (op->nbits - 1)) - 1;
-      if (reloc != BFD_RELOC_UNUSED)
-       val = SEXT_16BIT (val);
-    }
-
-  /* Branch offsets have an implicit 0 in the lowest bit.  */
-  if (type == 'p' || type == 'q')
-    val /= 2;
+  const struct mips_int_operand *operand;
+  unsigned int uval, length;
 
-  if ((val & ((1 << op->shift) - 1)) != 0
-      || val < (mintiny << op->shift)
-      || val > (maxtiny << op->shift))
+  operand = mips16_immed_operand (type, FALSE);
+  if (!mips16_immed_in_range_p (operand, reloc, val))
     {
       /* We need an extended instruction.  */
       if (user_insn_length == 2)
@@ -12905,37 +13800,19 @@ mips16_immed (char *file, unsigned int line, int type,
                     _("extended operand requested but not required"));
     }
 
-  if (mips16_opcode_length (*insn) == 2)
+  length = mips16_opcode_length (*insn);
+  if (length == 4)
     {
-      int insnval;
-
-      insnval = ((val >> op->shift) & ((1 << op->nbits) - 1));
-      insnval <<= op->op_shift;
-      *insn |= insnval;
+      operand = mips16_immed_operand (type, TRUE);
+      if (!mips16_immed_in_range_p (operand, reloc, val))
+       as_bad_where (file, line,
+                     _("operand value out of range for instruction"));
     }
+  uval = ((unsigned int) val >> operand->shift) - operand->bias;
+  if (length == 2)
+    *insn = mips_insert_operand (&operand->root, *insn, uval);
   else
-    {
-      long minext, maxext;
-
-      if (reloc == BFD_RELOC_UNUSED)
-       {
-         if (op->extu)
-           {
-             minext = 0;
-             maxext = (1 << op->extbits) - 1;
-           }
-         else
-           {
-             minext = - (1 << (op->extbits - 1));
-             maxext = (1 << (op->extbits - 1)) - 1;
-           }
-         if (val < minext || val > maxext)
-           as_bad_where (file, line,
-                         _("operand value out of range for instruction"));
-       }
-
-      *insn |= mips16_immed_extend (val, op->extbits);
-    }
+    *insn |= mips16_immed_extend (uval, operand->root.size);
 }
 \f
 struct percent_op_match
@@ -12968,7 +13845,9 @@ static const struct percent_op_match mips_percent_op[] =
   {"%tprel_hi", BFD_RELOC_MIPS_TLS_TPREL_HI16},
   {"%tprel_lo", BFD_RELOC_MIPS_TLS_TPREL_LO16},
   {"%gottprel", BFD_RELOC_MIPS_TLS_GOTTPREL},
-  {"%hi", BFD_RELOC_HI16_S}
+  {"%hi", BFD_RELOC_HI16_S},
+  {"%pcrel_hi", BFD_RELOC_HI16_S_PCREL},
+  {"%pcrel_lo", BFD_RELOC_LO16_PCREL}
 };
 
 static const struct percent_op_match mips16_percent_op[] =
@@ -13146,7 +14025,7 @@ static void
 mips_set_option_string (const char **string_ptr, const char *new_value)
 {
   if (*string_ptr != 0 && strcasecmp (*string_ptr, new_value) != 0)
-    as_warn (_("A different %s was already specified, is now %s"),
+    as_warn (_("a different %s was already specified, is now %s"),
             string_ptr == &mips_arch_string ? "-march" : "-mtune",
             new_value);
 
@@ -13161,7 +14040,7 @@ md_parse_option (int c, char *arg)
   for (i = 0; i < ARRAY_SIZE (mips_ases); i++)
     if (c == mips_ases[i].option_on || c == mips_ases[i].option_off)
       {
-       file_ase_explicit |= mips_set_ase (&mips_ases[i],
+       file_ase_explicit |= mips_set_ase (&mips_ases[i], &file_mips_opts,
                                           c == mips_ases[i].option_on);
        return 1;
       }
@@ -13211,39 +14090,63 @@ md_parse_option (int c, char *arg)
       break;
 
     case OPTION_MIPS1:
-      file_mips_isa = ISA_MIPS1;
+      file_mips_opts.isa = ISA_MIPS1;
       break;
 
     case OPTION_MIPS2:
-      file_mips_isa = ISA_MIPS2;
+      file_mips_opts.isa = ISA_MIPS2;
       break;
 
     case OPTION_MIPS3:
-      file_mips_isa = ISA_MIPS3;
+      file_mips_opts.isa = ISA_MIPS3;
       break;
 
     case OPTION_MIPS4:
-      file_mips_isa = ISA_MIPS4;
+      file_mips_opts.isa = ISA_MIPS4;
       break;
 
     case OPTION_MIPS5:
-      file_mips_isa = ISA_MIPS5;
+      file_mips_opts.isa = ISA_MIPS5;
       break;
 
     case OPTION_MIPS32:
-      file_mips_isa = ISA_MIPS32;
+      file_mips_opts.isa = ISA_MIPS32;
       break;
 
     case OPTION_MIPS32R2:
-      file_mips_isa = ISA_MIPS32R2;
+      file_mips_opts.isa = ISA_MIPS32R2;
+      break;
+
+    case OPTION_MIPS32R3:
+      file_mips_opts.isa = ISA_MIPS32R3;
+      break;
+
+    case OPTION_MIPS32R5:
+      file_mips_opts.isa = ISA_MIPS32R5;
+      break;
+
+    case OPTION_MIPS32R6:
+      file_mips_opts.isa = ISA_MIPS32R6;
       break;
 
     case OPTION_MIPS64R2:
-      file_mips_isa = ISA_MIPS64R2;
+      file_mips_opts.isa = ISA_MIPS64R2;
+      break;
+
+    case OPTION_MIPS64R3:
+      file_mips_opts.isa = ISA_MIPS64R3;
+      break;
+
+    case OPTION_MIPS64R5:
+      file_mips_opts.isa = ISA_MIPS64R5;
+      break;
+
+    case OPTION_MIPS64R6:
+      file_mips_opts.isa = ISA_MIPS64R6;
       break;
 
     case OPTION_MIPS64:
-      file_mips_isa = ISA_MIPS64;
+      file_mips_opts.isa = ISA_MIPS64;
       break;
 
     case OPTION_MTUNE:
@@ -13287,32 +14190,32 @@ md_parse_option (int c, char *arg)
       break;
 
     case OPTION_MICROMIPS:
-      if (mips_opts.mips16 == 1)
+      if (file_mips_opts.mips16 == 1)
        {
          as_bad (_("-mmicromips cannot be used with -mips16"));
          return 0;
        }
-      mips_opts.micromips = 1;
+      file_mips_opts.micromips = 1;
       mips_no_prev_insn ();
       break;
 
     case OPTION_NO_MICROMIPS:
-      mips_opts.micromips = 0;
+      file_mips_opts.micromips = 0;
       mips_no_prev_insn ();
       break;
 
     case OPTION_MIPS16:
-      if (mips_opts.micromips == 1)
+      if (file_mips_opts.micromips == 1)
        {
          as_bad (_("-mips16 cannot be used with -micromips"));
          return 0;
        }
-      mips_opts.mips16 = 1;
+      file_mips_opts.mips16 = 1;
       mips_no_prev_insn ();
       break;
 
     case OPTION_NO_MIPS16:
-      mips_opts.mips16 = 0;
+      file_mips_opts.mips16 = 0;
       mips_no_prev_insn ();
       break;
 
@@ -13324,6 +14227,14 @@ md_parse_option (int c, char *arg)
       mips_fix_24k = 0;
       break;
 
+    case OPTION_FIX_RM7000:
+      mips_fix_rm7000 = 1;
+      break;
+
+    case OPTION_NO_FIX_RM7000:
+      mips_fix_rm7000 = 0;
+      break;
+
     case OPTION_FIX_LOONGSON2F_JUMP:
       mips_fix_loongson2f_jump = TRUE;
       break;
@@ -13373,11 +14284,11 @@ md_parse_option (int c, char *arg)
       break;
 
     case OPTION_INSN32:
-      mips_opts.insn32 = TRUE;
+      file_mips_opts.insn32 = TRUE;
       break;
 
     case OPTION_NO_INSN32:
-      mips_opts.insn32 = FALSE;
+      file_mips_opts.insn32 = FALSE;
       break;
 
     case OPTION_MSHARED:
@@ -13389,11 +14300,11 @@ md_parse_option (int c, char *arg)
       break;
 
     case OPTION_MSYM32:
-      mips_opts.sym32 = TRUE;
+      file_mips_opts.sym32 = TRUE;
       break;
 
     case OPTION_MNO_SYM32:
-      mips_opts.sym32 = FALSE;
+      file_mips_opts.sym32 = FALSE;
       break;
 
       /* When generating ELF code, we permit -KPIC and -call_shared to
@@ -13439,39 +14350,51 @@ md_parse_option (int c, char *arg)
     case OPTION_64:
       mips_abi = N64_ABI;
       if (!support_64bit_objects())
-       as_fatal (_("No compiled in support for 64 bit object file format"));
+       as_fatal (_("no compiled in support for 64 bit object file format"));
       break;
 
     case OPTION_GP32:
-      file_mips_gp32 = 1;
+      file_mips_opts.gp = 32;
       break;
 
     case OPTION_GP64:
-      file_mips_gp32 = 0;
+      file_mips_opts.gp = 64;
       break;
 
     case OPTION_FP32:
-      file_mips_fp32 = 1;
+      file_mips_opts.fp = 32;
+      break;
+
+    case OPTION_FPXX:
+      file_mips_opts.fp = 0;
       break;
 
     case OPTION_FP64:
-      file_mips_fp32 = 0;
+      file_mips_opts.fp = 64;
+      break;
+
+    case OPTION_ODD_SPREG:
+      file_mips_opts.oddspreg = 1;
+      break;
+
+    case OPTION_NO_ODD_SPREG:
+      file_mips_opts.oddspreg = 0;
       break;
 
     case OPTION_SINGLE_FLOAT:
-      file_mips_single_float = 1;
+      file_mips_opts.single_float = 1;
       break;
 
     case OPTION_DOUBLE_FLOAT:
-      file_mips_single_float = 0;
+      file_mips_opts.single_float = 0;
       break;
 
     case OPTION_SOFT_FLOAT:
-      file_mips_soft_float = 1;
+      file_mips_opts.soft_float = 1;
       break;
 
     case OPTION_HARD_FLOAT:
-      file_mips_soft_float = 0;
+      file_mips_opts.soft_float = 0;
       break;
 
     case OPTION_MABI:
@@ -13485,7 +14408,7 @@ md_parse_option (int c, char *arg)
        {
          mips_abi = N64_ABI;
          if (! support_64bit_objects())
-           as_fatal (_("No compiled in support for 64 bit object file "
+           as_fatal (_("no compiled in support for 64 bit object file "
                        "format"));
        }
       else if (strcmp (arg, "eabi") == 0)
@@ -13527,12 +14450,12 @@ md_parse_option (int c, char *arg)
 
     case OPTION_NAN:
       if (strcmp (arg, "2008") == 0)
-       mips_flag_nan2008 = TRUE;
+       mips_nan2008 = 1;
       else if (strcmp (arg, "legacy") == 0)
-       mips_flag_nan2008 = FALSE;
+       mips_nan2008 = 0;
       else
        {
-         as_fatal (_("Invalid NaN setting -mnan=%s"), arg);
+         as_fatal (_("invalid NaN setting -mnan=%s"), arg);
          return 0;
        }
       break;
@@ -13546,22 +14469,7 @@ md_parse_option (int c, char *arg)
   return 1;
 }
 \f
-/* Set up globals to generate code for the ISA or processor
-   described by INFO.  */
-
-static void
-mips_set_architecture (const struct mips_cpu_info *info)
-{
-  if (info != 0)
-    {
-      file_mips_arch = info->cpu;
-      mips_opts.arch = info->cpu;
-      mips_opts.isa = info->isa;
-    }
-}
-
-
-/* Likewise for tuning.  */
+/* Set up globals to tune for the ISA or processor described by INFO.  */
 
 static void
 mips_set_tune (const struct mips_cpu_info *info)
@@ -13581,14 +14489,14 @@ mips_after_parse_args (void)
   if (strncmp (TARGET_OS, "pe", 2) == 0)
     {
       if (g_switch_seen && g_switch_value != 0)
-       as_bad (_("-G not supported in this configuration."));
+       as_bad (_("-G not supported in this configuration"));
       g_switch_value = 0;
     }
 
   if (mips_abi == NO_ABI)
     mips_abi = MIPS_DEFAULT_ABI;
 
-  /* The following code determines the architecture and register size.
+  /* The following code determines the architecture.
      Similar code was added to GCC 3.3 (see override_options() in
      config/mips/mips.c).  The GAS and GCC code should be kept in sync
      as much as possible.  */
@@ -13596,9 +14504,9 @@ mips_after_parse_args (void)
   if (mips_arch_string != 0)
     arch_info = mips_parse_cpu ("-march", mips_arch_string);
 
-  if (file_mips_isa != ISA_UNKNOWN)
+  if (file_mips_opts.isa != ISA_UNKNOWN)
     {
-      /* Handle -mipsN.  At this point, file_mips_isa contains the
+      /* Handle -mipsN.  At this point, file_mips_opts.isa contains the
         ISA level specified by -mipsN, while arch_info->isa contains
         the -march selection (if any).  */
       if (arch_info != 0)
@@ -13606,13 +14514,14 @@ mips_after_parse_args (void)
          /* -march takes precedence over -mipsN, since it is more descriptive.
             There's no harm in specifying both as long as the ISA levels
             are the same.  */
-         if (file_mips_isa != arch_info->isa)
-           as_bad (_("-%s conflicts with the other architecture options, which imply -%s"),
-                   mips_cpu_info_from_isa (file_mips_isa)->name,
+         if (file_mips_opts.isa != arch_info->isa)
+           as_bad (_("-%s conflicts with the other architecture options,"
+                     " which imply -%s"),
+                   mips_cpu_info_from_isa (file_mips_opts.isa)->name,
                    mips_cpu_info_from_isa (arch_info->isa)->name);
        }
       else
-       arch_info = mips_cpu_info_from_isa (file_mips_isa);
+       arch_info = mips_cpu_info_from_isa (file_mips_opts.isa);
     }
 
   if (arch_info == 0)
@@ -13625,9 +14534,17 @@ mips_after_parse_args (void)
     as_bad (_("-march=%s is not compatible with the selected ABI"),
            arch_info->name);
 
-  mips_set_architecture (arch_info);
+  file_mips_opts.arch = arch_info->cpu;
+  file_mips_opts.isa = arch_info->isa;
+
+  /* Set up initial mips_opts state.  */
+  mips_opts = file_mips_opts;
 
-  /* Optimize for file_mips_arch, unless -mtune selects a different processor.  */
+  /* The register size inference code is now placed in
+     file_mips_check_options.  */
+
+  /* Optimize for file_mips_opts.arch, unless -mtune selects a different
+     processor.  */
   if (mips_tune_string != 0)
     tune_info = mips_parse_cpu ("-mtune", mips_tune_string);
 
@@ -13636,101 +14553,6 @@ mips_after_parse_args (void)
   else
     mips_set_tune (tune_info);
 
-  if (file_mips_gp32 >= 0)
-    {
-      /* The user specified the size of the integer registers.  Make sure
-        it agrees with the ABI and ISA.  */
-      if (file_mips_gp32 == 0 && !ISA_HAS_64BIT_REGS (mips_opts.isa))
-       as_bad (_("-mgp64 used with a 32-bit processor"));
-      else if (file_mips_gp32 == 1 && ABI_NEEDS_64BIT_REGS (mips_abi))
-       as_bad (_("-mgp32 used with a 64-bit ABI"));
-      else if (file_mips_gp32 == 0 && ABI_NEEDS_32BIT_REGS (mips_abi))
-       as_bad (_("-mgp64 used with a 32-bit ABI"));
-    }
-  else
-    {
-      /* Infer the integer register size from the ABI and processor.
-        Restrict ourselves to 32-bit registers if that's all the
-        processor has, or if the ABI cannot handle 64-bit registers.  */
-      file_mips_gp32 = (ABI_NEEDS_32BIT_REGS (mips_abi)
-                       || !ISA_HAS_64BIT_REGS (mips_opts.isa));
-    }
-
-  switch (file_mips_fp32)
-    {
-    default:
-    case -1:
-      /* No user specified float register size.
-        ??? GAS treats single-float processors as though they had 64-bit
-        float registers (although it complains when double-precision
-        instructions are used).  As things stand, saying they have 32-bit
-        registers would lead to spurious "register must be even" messages.
-        So here we assume float registers are never smaller than the
-        integer ones.  */
-      if (file_mips_gp32 == 0)
-       /* 64-bit integer registers implies 64-bit float registers.  */
-       file_mips_fp32 = 0;
-      else if ((mips_opts.ase & FP64_ASES)
-              && ISA_HAS_64BIT_FPRS (mips_opts.isa))
-       /* -mips3d and -mdmx imply 64-bit float registers, if possible.  */
-       file_mips_fp32 = 0;
-      else
-       /* 32-bit float registers.  */
-       file_mips_fp32 = 1;
-      break;
-
-    /* The user specified the size of the float registers.  Check if it
-       agrees with the ABI and ISA.  */
-    case 0:
-      if (!ISA_HAS_64BIT_FPRS (mips_opts.isa))
-       as_bad (_("-mfp64 used with a 32-bit fpu"));
-      else if (ABI_NEEDS_32BIT_REGS (mips_abi)
-              && !ISA_HAS_MXHC1 (mips_opts.isa))
-       as_warn (_("-mfp64 used with a 32-bit ABI"));
-      break;
-    case 1:
-      if (ABI_NEEDS_64BIT_REGS (mips_abi))
-       as_warn (_("-mfp32 used with a 64-bit ABI"));
-      break;
-    }
-
-  /* End of GCC-shared inference code.  */
-
-  /* This flag is set when we have a 64-bit capable CPU but use only
-     32-bit wide registers.  Note that EABI does not use it.  */
-  if (ISA_HAS_64BIT_REGS (mips_opts.isa)
-      && ((mips_abi == NO_ABI && file_mips_gp32 == 1)
-         || mips_abi == O32_ABI))
-    mips_32bitmode = 1;
-
-  if (mips_opts.isa == ISA_MIPS1 && mips_trap)
-    as_bad (_("trap exception not supported at ISA 1"));
-
-  /* If the selected architecture includes support for ASEs, enable
-     generation of code for them.  */
-  if (mips_opts.mips16 == -1)
-    mips_opts.mips16 = (CPU_HAS_MIPS16 (file_mips_arch)) ? 1 : 0;
-  if (mips_opts.micromips == -1)
-    mips_opts.micromips = (CPU_HAS_MICROMIPS (file_mips_arch)) ? 1 : 0;
-
-  /* MIPS3D and MDMX require 64-bit FPRs, so -mfp32 should stop those
-     ASEs from being selected implicitly.  */
-  if (file_mips_fp32 == 1)
-    file_ase_explicit |= ASE_MIPS3D | ASE_MDMX;
-
-  /* If the user didn't explicitly select or deselect a particular ASE,
-     use the default setting for the CPU.  */
-  mips_opts.ase |= (arch_info->ase & ~file_ase_explicit);
-
-  file_mips_isa = mips_opts.isa;
-  file_ase = mips_opts.ase;
-  mips_opts.gp32 = file_mips_gp32;
-  mips_opts.fp32 = file_mips_fp32;
-  mips_opts.soft_float = file_mips_soft_float;
-  mips_opts.single_float = file_mips_single_float;
-
-  mips_check_isa_supports_ases ();
-
   if (mips_flag_mdebug < 0)
     mips_flag_mdebug = 0;
 }
@@ -13757,18 +14579,13 @@ md_pcrel_from (fixS *fixP)
     case BFD_RELOC_MICROMIPS_16_PCREL_S1:
     case BFD_RELOC_MICROMIPS_JMP:
     case BFD_RELOC_16_PCREL_S2:
+    case BFD_RELOC_MIPS_21_PCREL_S2:
+    case BFD_RELOC_MIPS_26_PCREL_S2:
     case BFD_RELOC_MIPS_JMP:
       /* Return the address of the delay slot.  */
       return addr + 4;
 
-    case BFD_RELOC_32_PCREL:
-      return addr;
-
     default:
-      /* We have no relocation type for PC relative MIPS16 instructions.  */
-      if (fixP->fx_addsy && S_GET_SEGMENT (fixP->fx_addsy) != now_seg)
-       as_bad_where (fixP->fx_file, fixP->fx_line,
-                     _("PC relative MIPS16 instruction references a different section"));
       return addr;
     }
 }
@@ -13929,6 +14746,17 @@ mips_force_relocation (fixS *fixp)
       || fixp->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1)
     return 1;
 
+  /* We want all PC-relative relocations to be kept for R6 relaxation.  */
+  if (ISA_IS_R6 (mips_opts.isa)
+      && (fixp->fx_r_type == BFD_RELOC_16_PCREL_S2
+         || fixp->fx_r_type == BFD_RELOC_MIPS_21_PCREL_S2
+         || fixp->fx_r_type == BFD_RELOC_MIPS_26_PCREL_S2
+         || fixp->fx_r_type == BFD_RELOC_MIPS_18_PCREL_S3
+         || fixp->fx_r_type == BFD_RELOC_MIPS_19_PCREL_S2
+         || fixp->fx_r_type == BFD_RELOC_HI16_S_PCREL
+         || fixp->fx_r_type == BFD_RELOC_LO16_PCREL))
+    return 1;
+
   return 0;
 }
 
@@ -13965,13 +14793,44 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
   unsigned long insn;
   reloc_howto_type *howto;
 
-  /* We ignore generic BFD relocations we don't know about.  */
-  howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
-  if (! howto)
-    return;
+  if (fixP->fx_pcrel)
+    switch (fixP->fx_r_type)
+      {
+      case BFD_RELOC_16_PCREL_S2:
+      case BFD_RELOC_MICROMIPS_7_PCREL_S1:
+      case BFD_RELOC_MICROMIPS_10_PCREL_S1:
+      case BFD_RELOC_MICROMIPS_16_PCREL_S1:
+      case BFD_RELOC_32_PCREL:
+      case BFD_RELOC_MIPS_21_PCREL_S2:
+      case BFD_RELOC_MIPS_26_PCREL_S2:
+      case BFD_RELOC_MIPS_18_PCREL_S3:
+      case BFD_RELOC_MIPS_19_PCREL_S2:
+      case BFD_RELOC_HI16_S_PCREL:
+      case BFD_RELOC_LO16_PCREL:
+       break;
+
+      case BFD_RELOC_32:
+       fixP->fx_r_type = BFD_RELOC_32_PCREL;
+       break;
+
+      default:
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("PC-relative reference to a different section"));
+       break;
+      }
+
+  /* Handle BFD_RELOC_8, since it's easy.  Punt on other bfd relocations
+     that have no MIPS ELF equivalent.  */
+  if (fixP->fx_r_type != BFD_RELOC_8)
+    {
+      howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
+      if (!howto)
+       return;
+    }
 
   gas_assert (fixP->fx_size == 2
              || fixP->fx_size == 4
+             || fixP->fx_r_type == BFD_RELOC_8
              || fixP->fx_r_type == BFD_RELOC_16
              || fixP->fx_r_type == BFD_RELOC_64
              || fixP->fx_r_type == BFD_RELOC_CTOR
@@ -13983,12 +14842,6 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 
   buf = fixP->fx_frag->fr_literal + fixP->fx_where;
 
-  gas_assert (!fixP->fx_pcrel || fixP->fx_r_type == BFD_RELOC_16_PCREL_S2
-             || fixP->fx_r_type == BFD_RELOC_MICROMIPS_7_PCREL_S1
-             || fixP->fx_r_type == BFD_RELOC_MICROMIPS_10_PCREL_S1
-             || fixP->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1
-             || fixP->fx_r_type == BFD_RELOC_32_PCREL);
-
   /* Don't treat parts of a composite relocation as done.  There are two
      reasons for this:
 
@@ -14109,7 +14962,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
            }
          else
            as_bad_where (fixP->fx_file, fixP->fx_line,
-                         _("Unsupported constant in relocation"));
+                         _("unsupported constant in relocation"));
        }
       break;
 
@@ -14138,6 +14991,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     case BFD_RELOC_32:
     case BFD_RELOC_32_PCREL:
     case BFD_RELOC_16:
+    case BFD_RELOC_8:
       /* If we are deleting this reloc entry, we must fill in the
         value now.  This can happen if we have a .word which is not
         resolved when it appears but is later defined.  */
@@ -14145,10 +14999,46 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
        md_number_to_chars (buf, *valP, fixP->fx_size);
       break;
 
+    case BFD_RELOC_MIPS_21_PCREL_S2:
+    case BFD_RELOC_MIPS_26_PCREL_S2:
+      if ((*valP & 0x3) != 0)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("branch to misaligned address (%lx)"), (long) *valP);
+
+      gas_assert (!fixP->fx_done);
+      break;
+
+    case BFD_RELOC_MIPS_18_PCREL_S3:
+      if ((S_GET_VALUE (fixP->fx_addsy) & 0x7) != 0)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("PC-relative access using misaligned symbol (%lx)"),
+                     (long) S_GET_VALUE (fixP->fx_addsy));
+      if ((fixP->fx_offset & 0x7) != 0)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("PC-relative access using misaligned offset (%lx)"),
+                     (long) fixP->fx_offset);
+
+      gas_assert (!fixP->fx_done);
+      break;
+
+    case BFD_RELOC_MIPS_19_PCREL_S2:
+      if ((*valP & 0x3) != 0)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("PC-relative access to misaligned address (%lx)"),
+                     (long) (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset));
+
+      gas_assert (!fixP->fx_done);
+      break;
+
+    case BFD_RELOC_HI16_S_PCREL:
+    case BFD_RELOC_LO16_PCREL:
+      gas_assert (!fixP->fx_done);
+      break;
+
     case BFD_RELOC_16_PCREL_S2:
       if ((*valP & 0x3) != 0)
        as_bad_where (fixP->fx_file, fixP->fx_line,
-                     _("Branch to misaligned address (%lx)"), (long) *valP);
+                     _("branch to misaligned address (%lx)"), (long) *valP);
 
       /* We need to save the bits in the instruction since fixup_segment()
         might be deleting the relocation entry (i.e., a branch within
@@ -14192,7 +15082,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
             and there's nothing we can do to fix this instruction
             without turning it into a longer sequence.  */
          as_bad_where (fixP->fx_file, fixP->fx_line,
-                       _("Branch out of range"));
+                       _("branch out of range"));
        }
       break;
 
@@ -14290,10 +15180,10 @@ s_align (int x ATTRIBUTE_UNUSED)
 
   temp = get_absolute_expression ();
   if (temp > max_alignment)
-    as_bad (_("Alignment too large: %d. assumed."), temp = max_alignment);
+    as_bad (_("alignment too large, %d assumed"), temp = max_alignment);
   else if (temp < 0)
     {
-      as_warn (_("Alignment negative: 0 assumed."));
+      as_warn (_("alignment negative, 0 assumed"));
       temp = 0;
     }
   if (*input_line_pointer == ',')
@@ -14589,7 +15479,7 @@ s_option (int x ATTRIBUTE_UNUSED)
        }
     }
   else
-    as_warn (_("Unrecognized option \"%s\""), opt);
+    as_warn (_("unrecognized option \"%s\""), opt);
 
   *input_line_pointer = c;
   demand_empty_rest_of_line ();
@@ -14605,92 +15495,39 @@ struct mips_option_stack
 
 static struct mips_option_stack *mips_opts_stack;
 
-/* Handle the .set pseudo-op.  */
-
-static void
-s_mipsset (int x ATTRIBUTE_UNUSED)
+static bfd_boolean
+parse_code_option (char * name)
 {
-  char *name = input_line_pointer, ch;
   const struct mips_ase *ase;
-
-  while (!is_end_of_line[(unsigned char) *input_line_pointer])
-    ++input_line_pointer;
-  ch = *input_line_pointer;
-  *input_line_pointer = '\0';
-
-  if (strcmp (name, "reorder") == 0)
-    {
-      if (mips_opts.noreorder)
-       end_noreorder ();
-    }
-  else if (strcmp (name, "noreorder") == 0)
-    {
-      if (!mips_opts.noreorder)
-       start_noreorder ();
-    }
-  else if (strncmp (name, "at=", 3) == 0)
+  if (strncmp (name, "at=", 3) == 0)
     {
       char *s = name + 3;
 
       if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, &mips_opts.at))
-       as_bad (_("Unrecognized register name `%s'"), s);
+       as_bad (_("unrecognized register name `%s'"), s);
     }
   else if (strcmp (name, "at") == 0)
-    {
-      mips_opts.at = ATREG;
-    }
+    mips_opts.at = ATREG;
   else if (strcmp (name, "noat") == 0)
-    {
-      mips_opts.at = ZERO;
-    }
-  else if (strcmp (name, "macro") == 0)
-    {
-      mips_opts.warn_about_macros = 0;
-    }
-  else if (strcmp (name, "nomacro") == 0)
-    {
-      if (mips_opts.noreorder == 0)
-       as_bad (_("`noreorder' must be set before `nomacro'"));
-      mips_opts.warn_about_macros = 1;
-    }
+    mips_opts.at = ZERO;
   else if (strcmp (name, "move") == 0 || strcmp (name, "novolatile") == 0)
-    {
-      mips_opts.nomove = 0;
-    }
+    mips_opts.nomove = 0;
   else if (strcmp (name, "nomove") == 0 || strcmp (name, "volatile") == 0)
-    {
-      mips_opts.nomove = 1;
-    }
+    mips_opts.nomove = 1;
   else if (strcmp (name, "bopt") == 0)
-    {
-      mips_opts.nobopt = 0;
-    }
+    mips_opts.nobopt = 0;
   else if (strcmp (name, "nobopt") == 0)
-    {
-      mips_opts.nobopt = 1;
-    }
-  else if (strcmp (name, "gp=default") == 0)
-    mips_opts.gp32 = file_mips_gp32;
-  else if (strcmp (name, "gp=32") == 0)
-    mips_opts.gp32 = 1;
-  else if (strcmp (name, "gp=64") == 0)
-    {
-      if (!ISA_HAS_64BIT_REGS (mips_opts.isa))
-       as_warn (_("%s isa does not support 64-bit registers"),
-                mips_cpu_info_from_isa (mips_opts.isa)->name);
-      mips_opts.gp32 = 0;
-    }
-  else if (strcmp (name, "fp=default") == 0)
-    mips_opts.fp32 = file_mips_fp32;
+    mips_opts.nobopt = 1;
+  else if (strcmp (name, "gp=32") == 0)
+    mips_opts.gp = 32;
+  else if (strcmp (name, "gp=64") == 0)
+    mips_opts.gp = 64;
   else if (strcmp (name, "fp=32") == 0)
-    mips_opts.fp32 = 1;
+    mips_opts.fp = 32;
+  else if (strcmp (name, "fp=xx") == 0)
+    mips_opts.fp = 0;
   else if (strcmp (name, "fp=64") == 0)
-    {
-      if (!ISA_HAS_64BIT_FPRS (mips_opts.isa))
-       as_warn (_("%s isa does not support 64-bit floating point registers"),
-                mips_cpu_info_from_isa (mips_opts.isa)->name);
-      mips_opts.fp32 = 0;
-    }
+    mips_opts.fp = 64;
   else if (strcmp (name, "softfloat") == 0)
     mips_opts.soft_float = 1;
   else if (strcmp (name, "hardfloat") == 0)
@@ -14699,47 +15536,35 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
     mips_opts.single_float = 1;
   else if (strcmp (name, "doublefloat") == 0)
     mips_opts.single_float = 0;
+  else if (strcmp (name, "nooddspreg") == 0)
+    mips_opts.oddspreg = 0;
+  else if (strcmp (name, "oddspreg") == 0)
+    mips_opts.oddspreg = 1;
   else if (strcmp (name, "mips16") == 0
           || strcmp (name, "MIPS-16") == 0)
-    {
-      if (mips_opts.micromips == 1)
-       as_fatal (_("`mips16' cannot be used with `micromips'"));
-      mips_opts.mips16 = 1;
-    }
+    mips_opts.mips16 = 1;
   else if (strcmp (name, "nomips16") == 0
           || strcmp (name, "noMIPS-16") == 0)
     mips_opts.mips16 = 0;
   else if (strcmp (name, "micromips") == 0)
-    {
-      if (mips_opts.mips16 == 1)
-       as_fatal (_("`micromips' cannot be used with `mips16'"));
-      mips_opts.micromips = 1;
-    }
+    mips_opts.micromips = 1;
   else if (strcmp (name, "nomicromips") == 0)
     mips_opts.micromips = 0;
   else if (name[0] == 'n'
           && name[1] == 'o'
           && (ase = mips_lookup_ase (name + 2)))
-    mips_set_ase (ase, FALSE);
+    mips_set_ase (ase, &mips_opts, FALSE);
   else if ((ase = mips_lookup_ase (name)))
-    mips_set_ase (ase, TRUE);
+    mips_set_ase (ase, &mips_opts, TRUE);
   else if (strncmp (name, "mips", 4) == 0 || strncmp (name, "arch=", 5) == 0)
     {
-      int reset = 0;
-
       /* Permit the user to change the ISA and architecture on the fly.
         Needless to say, misuse can cause serious problems.  */
-      if (strcmp (name, "mips0") == 0 || strcmp (name, "arch=default") == 0)
-       {
-         reset = 1;
-         mips_opts.isa = file_mips_isa;
-         mips_opts.arch = file_mips_arch;
-       }
-      else if (strncmp (name, "arch=", 5) == 0)
+      if (strncmp (name, "arch=", 5) == 0)
        {
          const struct mips_cpu_info *p;
 
-         p = mips_parse_cpu("internal use", name + 5);
+         p = mips_parse_cpu ("internal use", name + 5);
          if (!p)
            as_bad (_("unknown architecture %s"), name + 5);
          else
@@ -14752,7 +15577,7 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
        {
          const struct mips_cpu_info *p;
 
-         p = mips_parse_cpu("internal use", name);
+         p = mips_parse_cpu ("internal use", name);
          if (!p)
            as_bad (_("unknown ISA level %s"), name + 4);
          else
@@ -14763,42 +15588,6 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
        }
       else
        as_bad (_("unknown ISA or architecture %s"), name);
-
-      switch (mips_opts.isa)
-       {
-       case  0:
-         break;
-       case ISA_MIPS1:
-       case ISA_MIPS2:
-       case ISA_MIPS32:
-       case ISA_MIPS32R2:
-         mips_opts.gp32 = 1;
-         mips_opts.fp32 = 1;
-         break;
-       case ISA_MIPS3:
-       case ISA_MIPS4:
-       case ISA_MIPS5:
-       case ISA_MIPS64:
-       case ISA_MIPS64R2:
-         mips_opts.gp32 = 0;
-         if (mips_opts.arch == CPU_R5900)
-           {
-               mips_opts.fp32 = 1;
-           }
-         else
-           {
-         mips_opts.fp32 = 0;
-           }
-         break;
-       default:
-         as_bad (_("unknown ISA level %s"), name + 4);
-         break;
-       }
-      if (reset)
-       {
-         mips_opts.gp32 = file_mips_gp32;
-         mips_opts.fp32 = file_mips_fp32;
-       }
     }
   else if (strcmp (name, "autoextend") == 0)
     mips_opts.noautoextend = 0;
@@ -14808,6 +15597,68 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
     mips_opts.insn32 = TRUE;
   else if (strcmp (name, "noinsn32") == 0)
     mips_opts.insn32 = FALSE;
+  else if (strcmp (name, "sym32") == 0)
+    mips_opts.sym32 = TRUE;
+  else if (strcmp (name, "nosym32") == 0)
+    mips_opts.sym32 = FALSE;
+  else
+    return FALSE;
+  return TRUE;
+}
+
+/* Handle the .set pseudo-op.  */
+
+static void
+s_mipsset (int x ATTRIBUTE_UNUSED)
+{
+  char *name = input_line_pointer, ch;
+  int prev_isa = mips_opts.isa;
+
+  file_mips_check_options ();
+
+  while (!is_end_of_line[(unsigned char) *input_line_pointer])
+    ++input_line_pointer;
+  ch = *input_line_pointer;
+  *input_line_pointer = '\0';
+
+  if (strchr (name, ','))
+    {
+      /* Generic ".set" directive; use the generic handler.  */
+      *input_line_pointer = ch;
+      input_line_pointer = name;
+      s_set (0);
+      return;
+    }
+
+  if (strcmp (name, "reorder") == 0)
+    {
+      if (mips_opts.noreorder)
+       end_noreorder ();
+    }
+  else if (strcmp (name, "noreorder") == 0)
+    {
+      if (!mips_opts.noreorder)
+       start_noreorder ();
+    }
+  else if (strcmp (name, "macro") == 0)
+    mips_opts.warn_about_macros = 0;
+  else if (strcmp (name, "nomacro") == 0)
+    {
+      if (mips_opts.noreorder == 0)
+       as_bad (_("`noreorder' must be set before `nomacro'"));
+      mips_opts.warn_about_macros = 1;
+    }
+  else if (strcmp (name, "gp=default") == 0)
+    mips_opts.gp = file_mips_opts.gp;
+  else if (strcmp (name, "fp=default") == 0)
+    mips_opts.fp = file_mips_opts.fp;
+  else if (strcmp (name, "mips0") == 0 || strcmp (name, "arch=default") == 0)
+    {
+      mips_opts.isa = file_mips_opts.isa;
+      mips_opts.arch = file_mips_opts.arch;
+      mips_opts.gp = file_mips_opts.gp;
+      mips_opts.fp = file_mips_opts.fp;
+    }
   else if (strcmp (name, "push") == 0)
     {
       struct mips_option_stack *s;
@@ -14838,23 +15689,87 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
          free (s);
        }
     }
-  else if (strcmp (name, "sym32") == 0)
-    mips_opts.sym32 = TRUE;
-  else if (strcmp (name, "nosym32") == 0)
-    mips_opts.sym32 = FALSE;
-  else if (strchr (name, ','))
+  else if (!parse_code_option (name))
+    as_warn (_("tried to set unrecognized symbol: %s\n"), name);
+
+  /* The use of .set [arch|cpu]= historically 'fixes' the width of gp and fp
+     registers based on what is supported by the arch/cpu.  */
+  if (mips_opts.isa != prev_isa)
     {
-      /* Generic ".set" directive; use the generic handler.  */
-      *input_line_pointer = ch;
-      input_line_pointer = name;
-      s_set (0);
-      return;
+      switch (mips_opts.isa)
+       {
+       case 0:
+         break;
+       case ISA_MIPS1:
+         /* MIPS I cannot support FPXX.  */
+         mips_opts.fp = 32;
+         /* fall-through.  */
+       case ISA_MIPS2:
+       case ISA_MIPS32:
+       case ISA_MIPS32R2:
+       case ISA_MIPS32R3:
+       case ISA_MIPS32R5:
+         mips_opts.gp = 32;
+         if (mips_opts.fp != 0)
+           mips_opts.fp = 32;
+         break;
+       case ISA_MIPS32R6:
+         mips_opts.gp = 32;
+         mips_opts.fp = 64;
+         break;
+       case ISA_MIPS3:
+       case ISA_MIPS4:
+       case ISA_MIPS5:
+       case ISA_MIPS64:
+       case ISA_MIPS64R2:
+       case ISA_MIPS64R3:
+       case ISA_MIPS64R5:
+       case ISA_MIPS64R6:
+         mips_opts.gp = 64;
+         if (mips_opts.fp != 0)
+           {
+             if (mips_opts.arch == CPU_R5900)
+               mips_opts.fp = 32;
+             else
+               mips_opts.fp = 64;
+           }
+         break;
+       default:
+         as_bad (_("unknown ISA level %s"), name + 4);
+         break;
+       }
     }
-  else
+
+  mips_check_options (&mips_opts, FALSE);
+
+  mips_check_isa_supports_ases ();
+  *input_line_pointer = ch;
+  demand_empty_rest_of_line ();
+}
+
+/* Handle the .module pseudo-op.  */
+
+static void
+s_module (int ignore ATTRIBUTE_UNUSED)
+{
+  char *name = input_line_pointer, ch;
+
+  while (!is_end_of_line[(unsigned char) *input_line_pointer])
+    ++input_line_pointer;
+  ch = *input_line_pointer;
+  *input_line_pointer = '\0';
+
+  if (!file_mips_opts_checked)
     {
-      as_warn (_("Tried to set unrecognized symbol: %s\n"), name);
+      if (!parse_code_option (name))
+       as_bad (_(".module used with unrecognized symbol: %s\n"), name);
+
+      /* Update module level settings from mips_opts.  */
+      file_mips_opts = mips_opts;
     }
-  mips_check_isa_supports_ases ();
+  else
+    as_bad (_(".module is not permitted after generating code"));
+
   *input_line_pointer = ch;
   demand_empty_rest_of_line ();
 }
@@ -14901,6 +15816,8 @@ s_cpload (int ignore ATTRIBUTE_UNUSED)
   int reg;
   int in_shared;
 
+  file_mips_check_options ();
+
   /* If we are not generating SVR4 PIC code, or if this is NewABI code,
      .cpload is ignored.  */
   if (mips_pic != SVR4_PIC || HAVE_NEWABI)
@@ -14978,6 +15895,8 @@ s_cpsetup (int ignore ATTRIBUTE_UNUSED)
   expressionS ex_sym;
   int reg1;
 
+  file_mips_check_options ();
+
   /* If we are not generating SVR4 PIC code, .cpsetup is ignored.
      We also need NewABI support.  */
   if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
@@ -15081,6 +16000,8 @@ s_cpsetup (int ignore ATTRIBUTE_UNUSED)
 static void
 s_cplocal (int ignore ATTRIBUTE_UNUSED)
 {
+  file_mips_check_options ();
+
   /* If we are not generating SVR4 PIC code, or if this is not NewABI code,
      .cplocal is ignored.  */
   if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
@@ -15109,6 +16030,8 @@ s_cprestore (int ignore ATTRIBUTE_UNUSED)
 {
   expressionS ex;
 
+  file_mips_check_options ();
+
   /* If we are not generating SVR4 PIC code, or if this is NewABI code,
      .cprestore is ignored.  */
   if (mips_pic != SVR4_PIC || HAVE_NEWABI)
@@ -15156,6 +16079,8 @@ s_cpreturn (int ignore ATTRIBUTE_UNUSED)
 {
   expressionS ex;
 
+  file_mips_check_options ();
+
   /* If we are not generating SVR4 PIC code, .cpreturn is ignored.
      We also need NewABI support.  */
   if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
@@ -15209,7 +16134,7 @@ s_tls_rel_directive (const size_t bytes, const char *dirstr,
 
   if (ex.X_op != O_symbol)
     {
-      as_bad (_("Unsupported use of %s"), dirstr);
+      as_bad (_("unsupported use of %s"), dirstr);
       ignore_rest_of_line ();
     }
 
@@ -15300,7 +16225,7 @@ s_gpword (int ignore ATTRIBUTE_UNUSED)
 
   if (ex.X_op != O_symbol || ex.X_add_number != 0)
     {
-      as_bad (_("Unsupported use of .gpword"));
+      as_bad (_("unsupported use of .gpword"));
       ignore_rest_of_line ();
     }
 
@@ -15338,7 +16263,7 @@ s_gpdword (int ignore ATTRIBUTE_UNUSED)
 
   if (ex.X_op != O_symbol || ex.X_add_number != 0)
     {
-      as_bad (_("Unsupported use of .gpdword"));
+      as_bad (_("unsupported use of .gpdword"));
       ignore_rest_of_line ();
     }
 
@@ -15370,7 +16295,7 @@ s_ehword (int ignore ATTRIBUTE_UNUSED)
 
   if (ex.X_op != O_symbol || ex.X_add_number != 0)
     {
-      as_bad (_("Unsupported use of .ehword"));
+      as_bad (_("unsupported use of .ehword"));
       ignore_rest_of_line ();
     }
 
@@ -15390,6 +16315,8 @@ s_cpadd (int ignore ATTRIBUTE_UNUSED)
 {
   int reg;
 
+  file_mips_check_options ();
+
   /* This is ignored when not generating SVR4 PIC code.  */
   if (mips_pic != SVR4_PIC)
     {
@@ -15422,6 +16349,10 @@ s_cpadd (int ignore ATTRIBUTE_UNUSED)
 static void
 s_insn (int ignore ATTRIBUTE_UNUSED)
 {
+  file_mips_check_options ();
+  file_ase_mips16 |= mips_opts.mips16;
+  file_ase_micromips |= mips_opts.micromips;
+
   mips_mark_labels ();
 
   demand_empty_rest_of_line ();
@@ -15440,12 +16371,18 @@ s_nan (int ignore ATTRIBUTE_UNUSED)
 
   if (i == sizeof (str_2008) - 1
       && memcmp (input_line_pointer, str_2008, i) == 0)
-    mips_flag_nan2008 = TRUE;
+    mips_nan2008 = 1;
   else if (i == sizeof (str_legacy) - 1
           && memcmp (input_line_pointer, str_legacy, i) == 0)
-    mips_flag_nan2008 = FALSE;
+    {
+      if (ISA_HAS_LEGACY_NAN (file_mips_opts.isa))
+       mips_nan2008 = 0;
+      else
+       as_bad (_("`%s' does not support legacy NaN"),
+                 mips_cpu_info_from_isa (file_mips_opts.isa)->name);
+    }
   else
-    as_bad (_("Bad .nan directive"));
+    as_bad (_("bad .nan directive"));
 
   input_line_pointer += i;
   demand_empty_rest_of_line ();
@@ -15671,9 +16608,8 @@ static int
 mips16_extended_frag (fragS *fragp, asection *sec, long stretch)
 {
   int type;
-  const struct mips16_immed_operand *op;
+  const struct mips_int_operand *operand;
   offsetT val;
-  int mintiny, maxtiny;
   segT symsec;
   fragS *sym_frag;
 
@@ -15683,39 +16619,17 @@ mips16_extended_frag (fragS *fragp, asection *sec, long stretch)
     return 1;
 
   type = RELAX_MIPS16_TYPE (fragp->fr_subtype);
-  op = mips16_immed_operands;
-  while (op->type != type)
-    {
-      ++op;
-      gas_assert (op < mips16_immed_operands + MIPS16_NUM_IMMED);
-    }
-
-  if (op->unsp)
-    {
-      if (type == '<' || type == '>' || type == '[' || type == ']')
-       {
-         mintiny = 1;
-         maxtiny = 1 << op->nbits;
-       }
-      else
-       {
-         mintiny = 0;
-         maxtiny = (1 << op->nbits) - 1;
-       }
-    }
-  else
-    {
-      mintiny = - (1 << (op->nbits - 1));
-      maxtiny = (1 << (op->nbits - 1)) - 1;
-    }
+  operand = mips16_immed_operand (type, FALSE);
 
   sym_frag = symbol_get_frag (fragp->fr_symbol);
   val = S_GET_VALUE (fragp->fr_symbol);
   symsec = S_GET_SEGMENT (fragp->fr_symbol);
 
-  if (op->pcrel)
+  if (operand->root.type == OP_PCREL)
     {
+      const struct mips_pcrel_operand *pcrel_op;
       addressT addr;
+      offsetT maxtiny;
 
       /* We won't have the section when we are called from
          mips_relax_frag.  However, we will always have been called
@@ -15723,6 +16637,7 @@ mips16_extended_frag (fragS *fragp, asection *sec, long stretch)
          branch to a different section, we mark it as such.  If SEC is
          NULL, and the frag is not marked, then it must be a branch to
          the same section.  */
+      pcrel_op = (const struct mips_pcrel_operand *) operand;
       if (sec == NULL)
        {
          if (RELAX_MIPS16_LONG_BRANCH (fragp->fr_subtype))
@@ -15792,7 +16707,7 @@ mips16_extended_frag (fragS *fragp, asection *sec, long stretch)
          PC relative load or add is the instruction itself, but if it
          is in a delay slot (in which case it can not be extended) use
          the address of the instruction whose delay slot it is in.  */
-      if (type == 'p' || type == 'q')
+      if (pcrel_op->include_isa_bit)
        {
          addr += 2;
 
@@ -15804,26 +16719,21 @@ mips16_extended_frag (fragS *fragp, asection *sec, long stretch)
 
          /* Ignore the low bit in the target, since it will be set
              for a text label.  */
-         if ((val & 1) != 0)
-           --val;
+         val &= -2;
        }
       else if (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype))
        addr -= 4;
       else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype))
        addr -= 2;
 
-      val -= addr & ~ ((1 << op->shift) - 1);
-
-      /* Branch offsets have an implicit 0 in the lowest bit.  */
-      if (type == 'p' || type == 'q')
-       val /= 2;
+      val -= addr & -(1 << pcrel_op->align_log2);
 
       /* If any of the shifted bits are set, we must use an extended
          opcode.  If the address depends on the size of this
          instruction, this can lead to a loop, so we arrange to always
          use an extended opcode.  We only check this when we are in
          the main relaxation loop, when SEC is NULL.  */
-      if ((val & ((1 << op->shift) - 1)) != 0 && sec == NULL)
+      if ((val & ((1 << operand->shift) - 1)) != 0 && sec == NULL)
        {
          fragp->fr_subtype =
            RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
@@ -15831,8 +16741,8 @@ mips16_extended_frag (fragS *fragp, asection *sec, long stretch)
        }
 
       /* If we are about to mark a frag as extended because the value
-         is precisely maxtiny + 1, then there is a chance of an
-         infinite loop as in the following code:
+         is precisely the next value above maxtiny, then there is a
+         chance of an infinite loop as in the following code:
             la $4,foo
             .skip      1020
             .align     2
@@ -15841,8 +16751,9 @@ mips16_extended_frag (fragS *fragp, asection *sec, long stretch)
         away, so the la can be shrunk, but then foo is 0x400 away, so
         the la must be extended.  To avoid this loop, we mark the
         frag as extended if it was small, and is about to become
-        extended with a value of maxtiny + 1.  */
-      if (val == ((maxtiny + 1) << op->shift)
+        extended with the next value above maxtiny.  */
+      maxtiny = mips_int_operand_max (operand);
+      if (val == maxtiny + (1 << operand->shift)
          && ! RELAX_MIPS16_EXTENDED (fragp->fr_subtype)
          && sec == NULL)
        {
@@ -15854,12 +16765,7 @@ mips16_extended_frag (fragS *fragp, asection *sec, long stretch)
   else if (symsec != absolute_section && sec != NULL)
     as_bad_where (fragp->fr_file, fragp->fr_line, _("unsupported relocation"));
 
-  if ((val & ((1 << op->shift) - 1)) != 0
-      || val < (mintiny << op->shift)
-      || val > (maxtiny << op->shift))
-    return 1;
-  else
-    return 0;
+  return !mips16_immed_in_range_p (operand, BFD_RELOC_UNUSED, val);
 }
 
 /* Compute the length of a branch sequence, and adjust the
@@ -16171,8 +17077,11 @@ mips_fix_adjustable (fixS *fixp)
   /* There is no place to store an in-place offset for JALR relocations.
      Likewise an in-range offset of limited PC-relative relocations may
      overflow the in-place relocatable field if recalculated against the
-     start address of the symbol's containing section.  */
-  if (HAVE_IN_PLACE_ADDENDS
+     start address of the symbol's containing section.
+
+     Also, PC relative relocations for MIPS R6 need to be symbol rather than
+     section relative to allow linker relaxations to be performed later on.  */
+  if ((HAVE_IN_PLACE_ADDENDS || ISA_IS_R6 (mips_opts.isa))
       && (limited_pcrel_reloc_p (fixp->fx_r_type)
          || jalr_reloc_p (fixp->fx_r_type)))
     return 0;
@@ -16252,7 +17161,13 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
                  || fixp->fx_r_type == BFD_RELOC_MICROMIPS_7_PCREL_S1
                  || fixp->fx_r_type == BFD_RELOC_MICROMIPS_10_PCREL_S1
                  || fixp->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1
-                 || fixp->fx_r_type == BFD_RELOC_32_PCREL);
+                 || fixp->fx_r_type == BFD_RELOC_32_PCREL
+                 || fixp->fx_r_type == BFD_RELOC_MIPS_21_PCREL_S2
+                 || fixp->fx_r_type == BFD_RELOC_MIPS_26_PCREL_S2
+                 || fixp->fx_r_type == BFD_RELOC_MIPS_18_PCREL_S3
+                 || fixp->fx_r_type == BFD_RELOC_MIPS_19_PCREL_S2
+                 || fixp->fx_r_type == BFD_RELOC_HI16_S_PCREL
+                 || fixp->fx_r_type == BFD_RELOC_LO16_PCREL);
 
       /* At this point, fx_addnumber is "symbol offset - pcrel address".
         Relocations want only the symbol offset.  */
@@ -16275,7 +17190,8 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
   if (reloc->howto == NULL)
     {
       as_bad_where (fixp->fx_file, fixp->fx_line,
-                   _("Can not represent %s relocation in this object file format"),
+                   _("cannot represent %s relocation in this object file"
+                     " format"),
                    bfd_get_reloc_code_name (code));
       retval[0] = NULL;
     }
@@ -16369,7 +17285,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
          int i;
 
          as_warn_where (fragp->fr_file, fragp->fr_line,
-                        _("Relaxed out-of-range branch into a jump"));
+                        _("relaxed out-of-range branch into a jump"));
 
          if (RELAX_BRANCH_UNCOND (fragp->fr_subtype))
            goto uncond;
@@ -16380,11 +17296,21 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
              switch ((insn >> 28) & 0xf)
                {
                case 4:
-                 /* bc[0-3][tf]l? instructions can have the condition
-                    reversed by tweaking a single TF bit, and their
-                    opcodes all have 0x4???????.  */
-                 gas_assert ((insn & 0xf3e00000) == 0x41000000);
-                 insn ^= 0x00010000;
+                 if ((insn & 0xff000000) == 0x47000000
+                     || (insn & 0xff600000) == 0x45600000)
+                   {
+                     /* BZ.df/BNZ.df, BZ.V/BNZ.V can have the condition
+                        reversed by tweaking bit 23.  */
+                     insn ^= 0x00800000;
+                   }
+                 else
+                   {
+                     /* bc[0-3][tf]l? instructions can have the condition
+                        reversed by tweaking a single TF bit, and their
+                        opcodes all have 0x4???????.  */
+                     gas_assert ((insn & 0xf3e00000) == 0x41000000);
+                     insn ^= 0x00010000;
+                   }
                  break;
 
                case 0:
@@ -16622,7 +17548,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
 
       /* Relax 32-bit branches to a sequence of instructions.  */
       as_warn_where (fragp->fr_file, fragp->fr_line,
-                    _("Relaxed out-of-range branch into a jump"));
+                    _("relaxed out-of-range branch into a jump"));
 
       /* Set the short-delay-slot bit.  */
       short_ds = al && (insn & 0x02000000) != 0;
@@ -16651,6 +17577,11 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
                   || (insn & 0xffe30000) == 0x42800000         /* bc2f  */
                   || (insn & 0xffe30000) == 0x42a00000)        /* bc2t  */
            insn ^= 0x00200000;
+         else if ((insn & 0xff000000) == 0x83000000            /* BZ.df
+                                                                  BNZ.df  */
+                   || (insn & 0xff600000) == 0x81600000)       /* BZ.V
+                                                                  BNZ.V */
+           insn ^= 0x00800000;
          else
            abort ();
 
@@ -16747,7 +17678,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
   if (RELAX_MIPS16_P (fragp->fr_subtype))
     {
       int type;
-      const struct mips16_immed_operand *op;
+      const struct mips_int_operand *operand;
       offsetT val;
       char *buf;
       unsigned int user_length, length;
@@ -16755,42 +17686,41 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
       bfd_boolean ext;
 
       type = RELAX_MIPS16_TYPE (fragp->fr_subtype);
-      op = mips16_immed_operands;
-      while (op->type != type)
-       ++op;
+      operand = mips16_immed_operand (type, FALSE);
 
       ext = RELAX_MIPS16_EXTENDED (fragp->fr_subtype);
       val = resolve_symbol_value (fragp->fr_symbol);
-      if (op->pcrel)
+      if (operand->root.type == OP_PCREL)
        {
+         const struct mips_pcrel_operand *pcrel_op;
          addressT addr;
 
+         pcrel_op = (const struct mips_pcrel_operand *) operand;
          addr = fragp->fr_address + fragp->fr_fix;
 
          /* The rules for the base address of a PC relative reloc are
              complicated; see mips16_extended_frag.  */
-         if (type == 'p' || type == 'q')
+         if (pcrel_op->include_isa_bit)
            {
              addr += 2;
              if (ext)
                addr += 2;
              /* Ignore the low bit in the target, since it will be
                  set for a text label.  */
-             if ((val & 1) != 0)
-               --val;
+             val &= -2;
            }
          else if (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype))
            addr -= 4;
          else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype))
            addr -= 2;
 
-         addr &= ~ (addressT) ((1 << op->shift) - 1);
+         addr &= -(1 << pcrel_op->align_log2);
          val -= addr;
 
          /* Make sure the section winds up with the alignment we have
              assumed.  */
-         if (op->shift > 0)
-           record_alignment (asec, op->shift);
+         if (operand->shift > 0)
+           record_alignment (asec, operand->shift);
        }
 
       if (ext
@@ -16971,11 +17901,131 @@ mips_add_dot_label (symbolS *sym)
     mips_compressed_mark_label (sym);
 }
 \f
+/* Converting ASE flags from internal to .MIPS.abiflags values.  */
+static unsigned int
+mips_convert_ase_flags (int ase)
+{
+  unsigned int ext_ases = 0;
+
+  if (ase & ASE_DSP)
+    ext_ases |= AFL_ASE_DSP;
+  if (ase & ASE_DSPR2)
+    ext_ases |= AFL_ASE_DSPR2;
+  if (ase & ASE_EVA)
+    ext_ases |= AFL_ASE_EVA;
+  if (ase & ASE_MCU)
+    ext_ases |= AFL_ASE_MCU;
+  if (ase & ASE_MDMX)
+    ext_ases |= AFL_ASE_MDMX;
+  if (ase & ASE_MIPS3D)
+    ext_ases |= AFL_ASE_MIPS3D;
+  if (ase & ASE_MT)
+    ext_ases |= AFL_ASE_MT;
+  if (ase & ASE_SMARTMIPS)
+    ext_ases |= AFL_ASE_SMARTMIPS;
+  if (ase & ASE_VIRT)
+    ext_ases |= AFL_ASE_VIRT;
+  if (ase & ASE_MSA)
+    ext_ases |= AFL_ASE_MSA;
+  if (ase & ASE_XPA)
+    ext_ases |= AFL_ASE_XPA;
+
+  return ext_ases;
+}
 /* Some special processing for a MIPS ELF file.  */
 
 void
 mips_elf_final_processing (void)
 {
+  int fpabi;
+  Elf_Internal_ABIFlags_v0 flags;
+
+  flags.version = 0;
+  flags.isa_rev = 0;
+  switch (file_mips_opts.isa)
+    {
+    case INSN_ISA1:
+      flags.isa_level = 1;
+      break;
+    case INSN_ISA2:
+      flags.isa_level = 2;
+      break;
+    case INSN_ISA3:
+      flags.isa_level = 3;
+      break;
+    case INSN_ISA4:
+      flags.isa_level = 4;
+      break;
+    case INSN_ISA5:
+      flags.isa_level = 5;
+      break;
+    case INSN_ISA32:
+      flags.isa_level = 32;
+      flags.isa_rev = 1;
+      break;
+    case INSN_ISA32R2:
+      flags.isa_level = 32;
+      flags.isa_rev = 2;
+      break;
+    case INSN_ISA32R3:
+      flags.isa_level = 32;
+      flags.isa_rev = 3;
+      break;
+    case INSN_ISA32R5:
+      flags.isa_level = 32;
+      flags.isa_rev = 5;
+      break;
+    case INSN_ISA32R6:
+      flags.isa_level = 32;
+      flags.isa_rev = 6;
+      break;
+    case INSN_ISA64:
+      flags.isa_level = 64;
+      flags.isa_rev = 1;
+      break;
+    case INSN_ISA64R2:
+      flags.isa_level = 64;
+      flags.isa_rev = 2;
+      break;
+    case INSN_ISA64R3:
+      flags.isa_level = 64;
+      flags.isa_rev = 3;
+      break;
+    case INSN_ISA64R5:
+      flags.isa_level = 64;
+      flags.isa_rev = 5;
+      break;
+    case INSN_ISA64R6:
+      flags.isa_level = 64;
+      flags.isa_rev = 6;
+      break;
+    }
+
+  flags.gpr_size = file_mips_opts.gp == 32 ? AFL_REG_32 : AFL_REG_64;
+  flags.cpr1_size = file_mips_opts.soft_float ? AFL_REG_NONE
+                   : (file_mips_opts.ase & ASE_MSA) ? AFL_REG_128
+                   : (file_mips_opts.fp == 64) ? AFL_REG_64
+                   : AFL_REG_32;
+  flags.cpr2_size = AFL_REG_NONE;
+  flags.fp_abi = bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_GNU,
+                                           Tag_GNU_MIPS_ABI_FP);
+  flags.isa_ext = bfd_mips_isa_ext (stdoutput);
+  flags.ases = mips_convert_ase_flags (file_mips_opts.ase);
+  if (file_ase_mips16)
+    flags.ases |= AFL_ASE_MIPS16;
+  if (file_ase_micromips)
+    flags.ases |= AFL_ASE_MICROMIPS;
+  flags.flags1 = 0;
+  if ((ISA_HAS_ODD_SINGLE_FPR (file_mips_opts.isa, file_mips_opts.arch)
+       || file_mips_opts.fp == 64)
+      && file_mips_opts.oddspreg)
+    flags.flags1 |= AFL_FLAGS1_ODDSPREG;
+  flags.flags2 = 0;
+
+  bfd_mips_elf_swap_abiflags_v0_out (stdoutput, &flags,
+                                    ((Elf_External_ABIFlags_v0 *)
+                                    mips_flags_frag));
+
   /* Write out the register information.  */
   if (mips_abi != N64_ABI)
     {
@@ -17027,7 +18077,7 @@ mips_elf_final_processing (void)
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_M16;
   if (file_ase_micromips)
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_MICROMIPS;
-  if (file_ase & ASE_MDMX)
+  if (file_mips_opts.ase & ASE_MDMX)
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_MDMX;
 
   /* Set the MIPS ELF ABI flags.  */
@@ -17037,7 +18087,7 @@ mips_elf_final_processing (void)
     elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_O64;
   else if (mips_abi == EABI_ABI)
     {
-      if (!file_mips_gp32)
+      if (file_mips_opts.gp == 64)
        elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_EABI64;
       else
        elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_EABI32;
@@ -17050,14 +18100,14 @@ mips_elf_final_processing (void)
   if (mips_32bitmode)
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_32BITMODE;
 
-  if (mips_flag_nan2008)
+  if (mips_nan2008 == 1)
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_NAN2008;
 
-#if 0 /* XXX FIXME */
   /* 32 bit code with 64 bit FP registers.  */
-  if (!file_mips_fp32 && ABI_NEEDS_32BIT_REGS (mips_abi))
-    elf_elfheader (stdoutput)->e_flags |= ???;
-#endif
+  fpabi = bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_GNU,
+                                   Tag_GNU_MIPS_ABI_FP);
+  if (fpabi == Val_GNU_MIPS_ABI_FP_OLD_64)
+    elf_elfheader (stdoutput)->e_flags |= EF_MIPS_FP64;
 }
 \f
 typedef struct proc {
@@ -17160,19 +18210,6 @@ mips_handle_align (fragS *fragp)
   fragp->fr_var = size;
 }
 
-static void
-md_obj_begin (void)
-{
-}
-
-static void
-md_obj_end (void)
-{
-  /* Check for premature end, nesting errors, etc.  */
-  if (cur_proc_ptr)
-    as_warn (_("missing .end at end of assembly"));
-}
-
 static long
 get_number (void)
 {
@@ -17290,7 +18327,7 @@ s_mips_end (int x ATTRIBUTE_UNUSED)
 
   if (!cur_proc_ptr)
     {
-      as_warn (_(".end directive without a preceding .ent directive."));
+      as_warn (_(".end directive without a preceding .ent directive"));
       demand_empty_rest_of_line ();
       return;
     }
@@ -17299,7 +18336,7 @@ s_mips_end (int x ATTRIBUTE_UNUSED)
     {
       gas_assert (S_GET_NAME (p));
       if (strcmp (S_GET_NAME (p), S_GET_NAME (cur_proc_ptr->func_sym)))
-       as_warn (_(".end symbol does not match .ent symbol."));
+       as_warn (_(".end symbol does not match .ent symbol"));
 
       if (debug_type == DEBUG_STABS)
        stabs_generate_asm_endfunc (S_GET_NAME (p),
@@ -17376,7 +18413,7 @@ s_mips_ent (int aent)
     get_number ();
 
   if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) == 0)
-    as_warn (_(".ent or .aent not in text section."));
+    as_warn (_(".ent or .aent not in text section"));
 
   if (!aent && cur_proc_ptr)
     as_warn (_("missing .end"));
@@ -17432,7 +18469,7 @@ s_mips_frame (int ignore ATTRIBUTE_UNUSED)
       if (*input_line_pointer++ != ','
          || get_absolute_expression_and_terminator (&val) != ',')
        {
-         as_warn (_("Bad .frame directive"));
+         as_warn (_("bad .frame directive"));
          --input_line_pointer;
          demand_empty_rest_of_line ();
          return;
@@ -17469,7 +18506,7 @@ s_mips_mask (int reg_type)
 
       if (get_absolute_expression_and_terminator (&mask) != ',')
        {
-         as_warn (_("Bad .mask/.fmask directive"));
+         as_warn (_("bad .mask/.fmask directive"));
          --input_line_pointer;
          demand_empty_rest_of_line ();
          return;
@@ -17507,8 +18544,14 @@ static const struct mips_cpu_info mips_cpu_info_table[] =
   { "mips5",          MIPS_CPU_IS_ISA, 0,      ISA_MIPS5,    CPU_MIPS5 },
   { "mips32",         MIPS_CPU_IS_ISA, 0,      ISA_MIPS32,   CPU_MIPS32 },
   { "mips32r2",       MIPS_CPU_IS_ISA, 0,      ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "mips32r3",       MIPS_CPU_IS_ISA, 0,      ISA_MIPS32R3, CPU_MIPS32R3 },
+  { "mips32r5",       MIPS_CPU_IS_ISA, 0,      ISA_MIPS32R5, CPU_MIPS32R5 },
+  { "mips32r6",       MIPS_CPU_IS_ISA, 0,      ISA_MIPS32R6, CPU_MIPS32R6 },
   { "mips64",         MIPS_CPU_IS_ISA, 0,      ISA_MIPS64,   CPU_MIPS64 },
   { "mips64r2",       MIPS_CPU_IS_ISA, 0,      ISA_MIPS64R2, CPU_MIPS64R2 },
+  { "mips64r3",       MIPS_CPU_IS_ISA, 0,      ISA_MIPS64R3, CPU_MIPS64R3 },
+  { "mips64r5",       MIPS_CPU_IS_ISA, 0,      ISA_MIPS64R5, CPU_MIPS64R5 },
+  { "mips64r6",       MIPS_CPU_IS_ISA, 0,      ISA_MIPS64R6, CPU_MIPS64R6 },
 
   /* MIPS I */
   { "r3000",          0, 0,                    ISA_MIPS1,    CPU_R3000 },
@@ -17611,6 +18654,8 @@ static const struct mips_cpu_info mips_cpu_info_table[] =
   { "1004kf2_1",      0, ASE_DSP | ASE_MT,     ISA_MIPS32R2, CPU_MIPS32R2 },
   { "1004kf",         0, ASE_DSP | ASE_MT,     ISA_MIPS32R2, CPU_MIPS32R2 },
   { "1004kf1_1",      0, ASE_DSP | ASE_MT,     ISA_MIPS32R2, CPU_MIPS32R2 },
+  /* P5600 with EVA and Virtualization ASEs, other ASEs are optional.  */
+  { "p5600",          0, ASE_VIRT | ASE_EVA | ASE_XPA,         ISA_MIPS32R5, CPU_MIPS32R5 },
 
   /* MIPS 64 */
   { "5kc",            0, 0,                    ISA_MIPS64,   CPU_MIPS64 },
@@ -17623,7 +18668,7 @@ static const struct mips_cpu_info mips_cpu_info_table[] =
   /* Broadcom SB-1A CPU core */
   { "sb1a",           0, ASE_MIPS3D | ASE_MDMX,        ISA_MIPS64,   CPU_SB1 },
   
-  { "loongson3a",     0, 0,                    ISA_MIPS64,   CPU_LOONGSON_3A },
+  { "loongson3a",     0, 0,                    ISA_MIPS64R2, CPU_LOONGSON_3A },
 
   /* MIPS 64 Release 2 */
 
@@ -17631,6 +18676,7 @@ static const struct mips_cpu_info mips_cpu_info_table[] =
   { "octeon",        0, 0,                     ISA_MIPS64R2, CPU_OCTEON },
   { "octeon+",       0, 0,                     ISA_MIPS64R2, CPU_OCTEONP },
   { "octeon2",       0, 0,                     ISA_MIPS64R2, CPU_OCTEON2 },
+  { "octeon3",       0, ASE_VIRT | ASE_VIRT64, ISA_MIPS64R5, CPU_OCTEON3 },
 
   /* RMI Xlr */
   { "xlr",           0, 0,                     ISA_MIPS64,   CPU_XLR },
@@ -17724,8 +18770,9 @@ mips_parse_cpu (const char *option, const char *cpu_string)
       if (ABI_NEEDS_64BIT_REGS (mips_abi))
        return mips_cpu_info_from_isa (ISA_MIPS3);
 
-      if (file_mips_gp32 >= 0)
-       return mips_cpu_info_from_isa (file_mips_gp32 ? ISA_MIPS1 : ISA_MIPS3);
+      if (file_mips_opts.gp >= 0)
+       return mips_cpu_info_from_isa (file_mips_opts.gp == 32
+                                      ? ISA_MIPS1 : ISA_MIPS3);
 
       return mips_cpu_info_from_isa (MIPS_DEFAULT_64BIT
                                     ? ISA_MIPS3
@@ -17740,7 +18787,7 @@ mips_parse_cpu (const char *option, const char *cpu_string)
     if (mips_matching_cpu_name_p (p->name, cpu_string))
       return p;
 
-  as_bad (_("Bad value (%s) for %s"), cpu_string, option);
+  as_bad (_("bad value (%s) for %s"), cpu_string, option);
   return 0;
 }
 
@@ -17819,8 +18866,14 @@ MIPS options:\n\
 -mips5                  generate MIPS ISA V instructions\n\
 -mips32                 generate MIPS32 ISA instructions\n\
 -mips32r2               generate MIPS32 release 2 ISA instructions\n\
+-mips32r3               generate MIPS32 release 3 ISA instructions\n\
+-mips32r5               generate MIPS32 release 5 ISA instructions\n\
+-mips32r6               generate MIPS32 release 6 ISA instructions\n\
 -mips64                 generate MIPS64 ISA instructions\n\
 -mips64r2               generate MIPS64 release 2 ISA instructions\n\
+-mips64r3               generate MIPS64 release 3 ISA instructions\n\
+-mips64r5               generate MIPS64 release 5 ISA instructions\n\
+-mips64r6               generate MIPS64 release 6 ISA instructions\n\
 -march=CPU/-mtune=CPU  generate code/schedule for CPU, where CPU is one of:\n"));
 
   first = 1;
@@ -17865,6 +18918,12 @@ MIPS options:\n\
 -mmcu                  generate MCU instructions\n\
 -mno-mcu               do not generate MCU instructions\n"));
   fprintf (stream, _("\
+-mmsa                  generate MSA instructions\n\
+-mno-msa               do not generate MSA instructions\n"));
+  fprintf (stream, _("\
+-mxpa                  generate eXtended Physical Address (XPA) instructions\n\
+-mno-xpa               do not generate eXtended Physical Address (XPA) instructions\n"));
+  fprintf (stream, _("\
 -mvirt                 generate Virtualization instructions\n\
 -mno-virt              do not generate Virtualization instructions\n"));
   fprintf (stream, _("\
@@ -17965,3 +19024,91 @@ tc_mips_regname_to_dw2regnum (char *regname)
 
   return regnum;
 }
+
+/* Implement CONVERT_SYMBOLIC_ATTRIBUTE.
+   Given a symbolic attribute NAME, return the proper integer value.
+   Returns -1 if the attribute is not known.  */
+
+int
+mips_convert_symbolic_attribute (const char *name)
+{
+  static const struct
+  {
+    const char * name;
+    const int    tag;
+  }
+  attribute_table[] =
+    {
+#define T(tag) {#tag, tag}
+      T (Tag_GNU_MIPS_ABI_FP),
+      T (Tag_GNU_MIPS_ABI_MSA),
+#undef T
+    };
+  unsigned int i;
+
+  if (name == NULL)
+    return -1;
+
+  for (i = 0; i < ARRAY_SIZE (attribute_table); i++)
+    if (streq (name, attribute_table[i].name))
+      return attribute_table[i].tag;
+
+  return -1;
+}
+
+void
+md_mips_end (void)
+{
+  int fpabi = Val_GNU_MIPS_ABI_FP_ANY;
+
+  mips_emit_delays ();
+  if (cur_proc_ptr)
+    as_warn (_("missing .end at end of assembly"));
+
+  /* Just in case no code was emitted, do the consistency check.  */
+  file_mips_check_options ();
+
+  /* Set a floating-point ABI if the user did not.  */
+  if (obj_elf_seen_attribute (OBJ_ATTR_GNU, Tag_GNU_MIPS_ABI_FP))
+    {
+      /* Perform consistency checks on the floating-point ABI.  */
+      fpabi = bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_GNU,
+                                       Tag_GNU_MIPS_ABI_FP);
+      if (fpabi != Val_GNU_MIPS_ABI_FP_ANY)
+       check_fpabi (fpabi);
+    }
+  else
+    {
+      /* Soft-float gets precedence over single-float, the two options should
+         not be used together so this should not matter.  */
+      if (file_mips_opts.soft_float == 1)
+       fpabi = Val_GNU_MIPS_ABI_FP_SOFT;
+      /* Single-float gets precedence over all double_float cases.  */
+      else if (file_mips_opts.single_float == 1)
+       fpabi = Val_GNU_MIPS_ABI_FP_SINGLE;
+      else
+       {
+         switch (file_mips_opts.fp)
+           {
+           case 32:
+             if (file_mips_opts.gp == 32)
+               fpabi = Val_GNU_MIPS_ABI_FP_DOUBLE;
+             break;
+           case 0:
+             fpabi = Val_GNU_MIPS_ABI_FP_XX;
+             break;
+           case 64:
+             if (file_mips_opts.gp == 32 && !file_mips_opts.oddspreg)
+               fpabi = Val_GNU_MIPS_ABI_FP_64A;
+             else if (file_mips_opts.gp == 32)
+               fpabi = Val_GNU_MIPS_ABI_FP_64;
+             else
+               fpabi = Val_GNU_MIPS_ABI_FP_DOUBLE;
+             break;
+           }
+       }
+
+      bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_GNU,
+                               Tag_GNU_MIPS_ABI_FP, fpabi);
+    }
+}
This page took 0.157046 seconds and 4 git commands to generate.