include/opcode/
[deliverable/binutils-gdb.git] / gas / config / tc-mips.c
index 90886d597c90a9b1279027ff302e4b807d40c2f6..91e6a970b0614869654a1bd07df376a380cb7745 100644 (file)
 #include "dwarf2dbg.h"
 #include "dw2gencfi.h"
 
+/* Check assumptions made in this file.  */
+typedef char static_assert1[sizeof (offsetT) < 8 ? -1 : 1];
+typedef char static_assert2[sizeof (valueT) < 8 ? -1 : 1];
+
 #ifdef DEBUG
 #define DBG(x) printf x
 #else
 #define DBG(x)
 #endif
 
-#ifdef OBJ_MAYBE_ELF
+#define SKIP_SPACE_TABS(S) \
+  do { while (*(S) == ' ' || *(S) == '\t') ++(S); } while (0)
+
 /* Clean up namespace so we can include obj-elf.h too.  */
 static int mips_output_flavor (void);
 static int mips_output_flavor (void) { return OUTPUT_FLAVOR; }
@@ -61,11 +67,8 @@ static int mips_output_flavor (void) { return OUTPUT_FLAVOR; }
 /* Fix any of them that we actually care about.  */
 #undef OUTPUT_FLAVOR
 #define OUTPUT_FLAVOR mips_output_flavor()
-#endif
 
-#if defined (OBJ_ELF)
 #include "elf/mips.h"
-#endif
 
 #ifndef ECOFF_DEBUGGING
 #define NO_ECOFF_DEBUGGING
@@ -85,9 +88,7 @@ int mips_flag_pdr = TRUE;
 
 #include "ecoff.h"
 
-#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
 static char *mips_regmask_frag;
-#endif
 
 #define ZERO 0
 #define ATREG 1
@@ -233,6 +234,10 @@ struct mips_set_options
   /* Non-zero if we should not autoextend mips16 instructions.
      Changed by `.set autoextend' and `.set noautoextend'.  */
   int noautoextend;
+  /* True if we should only emit 32-bit microMIPS instructions.
+     Changed by `.set insn32' and `.set noinsn32', and the -minsn32
+     and -mno-insn32 command line options.  */
+  bfd_boolean insn32;
   /* 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.  */
@@ -270,12 +275,15 @@ static int file_mips_soft_float = 0;
 /* 1 if -msingle-float, 0 if -mdouble-float.  The default is 0.   */
 static int file_mips_single_float = 0;
 
+/* True if -mnan=2008, false if -mnan=legacy.  */
+static bfd_boolean mips_flag_nan2008 = FALSE;
+
 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, /* gp32 */ 0,
-  /* fp32 */ 0, /* arch */ CPU_UNKNOWN, /* sym32 */ FALSE,
+  /* nomove */ 0, /* nobopt */ 0, /* noautoextend */ 0, /* insn32 */ FALSE,
+  /* gp32 */ 0, /* fp32 */ 0, /* arch */ CPU_UNKNOWN, /* sym32 */ FALSE,
   /* soft_float */ FALSE, /* single_float */ FALSE
 };
 
@@ -530,6 +538,10 @@ static int mips_32bitmode = 0;
 #define HAVE_CODE_COMPRESSION                                          \
   ((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_SMIN (-GPR_SMAX - 1)
+
 /* MIPS PIC level.  */
 
 enum mips_pic_level mips_pic;
@@ -684,11 +696,18 @@ static struct mips_cl_insn micromips_nop16_insn;
 static struct mips_cl_insn micromips_nop32_insn;
 
 /* The appropriate nop for the current mode.  */
-#define NOP_INSN (mips_opts.mips16 ? &mips16_nop_insn \
-                 : (mips_opts.micromips ? &micromips_nop16_insn : &nop_insn))
+#define NOP_INSN (mips_opts.mips16                                     \
+                 ? &mips16_nop_insn                                    \
+                 : (mips_opts.micromips                                \
+                    ? (mips_opts.insn32                                \
+                       ? &micromips_nop32_insn                         \
+                       : &micromips_nop16_insn)                        \
+                    : &nop_insn))
 
 /* The size of NOP_INSN in bytes.  */
-#define NOP_INSN_SIZE (HAVE_CODE_COMPRESSION ? 2 : 4)
+#define NOP_INSN_SIZE ((mips_opts.mips16                               \
+                       || (mips_opts.micromips && !mips_opts.insn32))  \
+                      ? 2 : 4)
 
 /* If this is set, it points to a frag holding nop instructions which
    were inserted before the start of a noreorder section.  If those
@@ -736,18 +755,6 @@ static struct mips_hi_fixup *mips_hi_fixup_list;
 
 static fragS *prev_reloc_op_frag;
 
-/* Map normal MIPS register numbers to mips16 register numbers.  */
-
-#define X ILLEGAL_REG
-static const int mips32_to_16_reg_map[] =
-{
-  X, X, 2, 3, 4, 5, 6, 7,
-  X, X, X, X, X, X, X, X,
-  0, 1, X, X, X, X, X, X,
-  X, X, X, X, X, X, X, X
-};
-#undef X
-
 /* Map mips16 register numbers to normal MIPS register numbers.  */
 
 static const unsigned int mips16_to_32_reg_map[] =
@@ -755,47 +762,6 @@ static const unsigned int mips16_to_32_reg_map[] =
   16, 17, 2, 3, 4, 5, 6, 7
 };
 
-/* Map normal MIPS register numbers to microMIPS register numbers.  */
-
-#define mips32_to_micromips_reg_b_map  mips32_to_16_reg_map
-#define mips32_to_micromips_reg_c_map  mips32_to_16_reg_map
-#define mips32_to_micromips_reg_d_map  mips32_to_16_reg_map
-#define mips32_to_micromips_reg_e_map  mips32_to_16_reg_map
-#define mips32_to_micromips_reg_f_map  mips32_to_16_reg_map
-#define mips32_to_micromips_reg_g_map  mips32_to_16_reg_map
-#define mips32_to_micromips_reg_l_map  mips32_to_16_reg_map
-
-#define X ILLEGAL_REG
-/* reg type h: 4, 5, 6.  */
-static const int mips32_to_micromips_reg_h_map[] =
-{
-  X, X, X, X, 4, 5, 6, X,
-  X, X, X, X, X, X, X, X,
-  X, X, X, X, X, X, X, X,
-  X, X, X, X, X, X, X, X
-};
-
-/* reg type m: 0, 17, 2, 3, 16, 18, 19, 20.  */
-static const int mips32_to_micromips_reg_m_map[] =
-{
-  0, X, 2, 3, X, X, X, X,
-  X, X, X, X, X, X, X, X,
-  4, 1, 5, 6, 7, X, X, X,
-  X, X, X, X, X, X, X, X
-};
-
-/* reg type q: 0, 2-7. 17.  */
-static const int mips32_to_micromips_reg_q_map[] =
-{
-  0, X, 2, 3, 4, 5, 6, 7,
-  X, X, X, X, X, X, X, X,
-  X, 1, X, X, X, X, X, X,
-  X, X, X, X, X, X, X, X
-};
-
-#define mips32_to_micromips_reg_n_map  mips32_to_micromips_reg_m_map
-#undef X
-
 /* Map microMIPS register numbers to normal MIPS register numbers.  */
 
 #define micromips_to_32_reg_b_map      mips16_to_32_reg_map
@@ -806,13 +772,11 @@ static const int mips32_to_micromips_reg_q_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_map[] =
+static const unsigned int micromips_to_32_reg_h_map1[] =
 {
   5, 5, 6, 4, 4, 4, 4, 4
 };
-
-/* The microMIPS registers with type i.  */
-static const unsigned int micromips_to_32_reg_i_map[] =
+static const unsigned int micromips_to_32_reg_h_map2[] =
 {
   6, 7, 7, 21, 22, 5, 6, 7
 };
@@ -833,18 +797,6 @@ static const unsigned int micromips_to_32_reg_q_map[] =
   0, 17, 2, 3, 4, 5, 6, 7
 };
 
-/* microMIPS imm type B.  */
-static const int micromips_imm_b_map[] =
-{
-  1, 4, 8, 12, 16, 20, 24, -1
-};
-
-/* microMIPS imm type C.  */
-static const int micromips_imm_c_map[] =
-{
-  128, 1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 255, 32768, 65535
-};
-
 /* Classifies the kind of instructions we're interested in when
    implementing -mfix-vr4120.  */
 enum fix_vr4120_class
@@ -1156,36 +1108,11 @@ static int mips_relax_branch;
   (((x) &~ (offsetT) 0xffffffff) == 0                                  \
    || (((x) &~ (offsetT) 0xffffffff) == ~ (offsetT) 0xffffffff))
 
-/* Replace bits MASK << SHIFT of STRUCT with the equivalent bits in
-   VALUE << SHIFT.  VALUE is evaluated exactly once.  */
-#define INSERT_BITS(STRUCT, VALUE, MASK, SHIFT) \
-  (STRUCT) = (((STRUCT) & ~((MASK) << (SHIFT))) \
-             | (((VALUE) & (MASK)) << (SHIFT)))
-
 /* Extract bits MASK << SHIFT from STRUCT and shift them right
    SHIFT places.  */
 #define EXTRACT_BITS(STRUCT, MASK, SHIFT) \
   (((STRUCT) >> (SHIFT)) & (MASK))
 
-/* Change INSN's opcode so that the operand given by FIELD has value VALUE.
-   INSN is a mips_cl_insn structure and VALUE is evaluated exactly once.
-
-   include/opcode/mips.h specifies operand fields using the macros
-   OP_MASK_<FIELD> and OP_SH_<FIELD>.  The MIPS16 equivalents start
-   with "MIPS16OP" instead of "OP".  */
-#define INSERT_OPERAND(MICROMIPS, FIELD, INSN, VALUE) \
-  do \
-    if (!(MICROMIPS)) \
-      INSERT_BITS ((INSN).insn_opcode, VALUE, \
-                  OP_MASK_##FIELD, OP_SH_##FIELD); \
-    else \
-      INSERT_BITS ((INSN).insn_opcode, VALUE, \
-                  MICROMIPSOP_MASK_##FIELD, MICROMIPSOP_SH_##FIELD); \
-  while (0)
-#define MIPS16_INSERT_OPERAND(FIELD, INSN, VALUE) \
-  INSERT_BITS ((INSN).insn_opcode, VALUE, \
-               MIPS16OP_MASK_##FIELD, MIPS16OP_SH_##FIELD)
-
 /* Extract the operand given by FIELD from mips_cl_insn INSN.  */
 #define EXTRACT_OPERAND(MICROMIPS, FIELD, INSN) \
   (!(MICROMIPS) \
@@ -1274,7 +1201,7 @@ static void mips16_macro_build
 static void load_register (int, expressionS *, int);
 static void macro_start (void);
 static void macro_end (void);
-static void macro (struct mips_cl_insn * ip);
+static void macro (struct mips_cl_insn *ip, char *str);
 static void mips16_macro (struct mips_cl_insn * ip);
 static void mips_ip (char *str, struct mips_cl_insn * ip);
 static void mips16_ip (char *str, struct mips_cl_insn * ip);
@@ -1308,6 +1235,7 @@ static void s_gpdword (int);
 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_mips_ent (int);
@@ -1320,8 +1248,6 @@ static void s_mips_file (int);
 static void s_mips_loc (int);
 static bfd_boolean pic_need_relax (symbolS *, asection *);
 static int relaxed_branch_length (fragS *, asection *, int);
-static int validate_mips_insn (const struct mips_opcode *);
-static int validate_micromips_insn (const struct mips_opcode *);
 static int relaxed_micromips_16bit_branch_length (fragS *, asection *, int);
 static int relaxed_micromips_32bit_branch_length (fragS *, asection *, int);
 
@@ -1416,6 +1342,8 @@ enum options
     OPTION_GP64,
     OPTION_RELAX_BRANCH,
     OPTION_NO_RELAX_BRANCH,
+    OPTION_INSN32,
+    OPTION_NO_INSN32,
     OPTION_MSHARED,
     OPTION_MNO_SHARED,
     OPTION_MSYM32,
@@ -1425,7 +1353,6 @@ enum options
     OPTION_SINGLE_FLOAT,
     OPTION_DOUBLE_FLOAT,
     OPTION_32,
-#ifdef OBJ_ELF
     OPTION_CALL_SHARED,
     OPTION_CALL_NONPIC,
     OPTION_NON_SHARED,
@@ -1438,7 +1365,7 @@ enum options
     OPTION_PDR,
     OPTION_NO_PDR,
     OPTION_MVXWORKS_PIC,
-#endif /* OBJ_ELF */
+    OPTION_NAN,
     OPTION_END_OF_ENUM
   };
 
@@ -1524,6 +1451,8 @@ struct option md_longopts[] =
   {"mgp64", no_argument, NULL, OPTION_GP64},
   {"relax-branch", no_argument, NULL, OPTION_RELAX_BRANCH},
   {"no-relax-branch", no_argument, NULL, OPTION_NO_RELAX_BRANCH},
+  {"minsn32", no_argument, NULL, OPTION_INSN32},
+  {"mno-insn32", no_argument, NULL, OPTION_NO_INSN32},
   {"mshared", no_argument, NULL, OPTION_MSHARED},
   {"mno-shared", no_argument, NULL, OPTION_MNO_SHARED},
   {"msym32", no_argument, NULL, OPTION_MSYM32},
@@ -1539,7 +1468,6 @@ struct option md_longopts[] =
   {"32", no_argument, NULL, OPTION_32},
 
   /* ELF-specific options.  */
-#ifdef OBJ_ELF
   {"KPIC", no_argument, NULL, OPTION_CALL_SHARED},
   {"call_shared", no_argument, NULL, OPTION_CALL_SHARED},
   {"call_nonpic", no_argument, NULL, OPTION_CALL_NONPIC},
@@ -1553,7 +1481,7 @@ struct option md_longopts[] =
   {"mpdr", no_argument, NULL, OPTION_PDR},
   {"mno-pdr", no_argument, NULL, OPTION_NO_PDR},
   {"mvxworks-pic", no_argument, NULL, OPTION_MVXWORKS_PIC},
-#endif /* OBJ_ELF */
+  {"mnan", required_argument, NULL, OPTION_NAN},
 
   {NULL, no_argument, NULL, 0}
 };
@@ -1678,6 +1606,7 @@ static const pseudo_typeS mips_pseudo_table[] =
   {"ehword", s_ehword, 0},
   {"cpadd", s_cpadd, 0},
   {"insn", s_insn, 0},
+  {"nan", s_nan, 0},
 
   /* Relatively generic pseudo-ops that happen to be used on MIPS
      chips.  */
@@ -1801,17 +1730,18 @@ mips_mark_labels (void)
 \f
 static char *expr_end;
 
-/* Expressions which appear in instructions.  These are set by
-   mips_ip.  */
+/* Expressions which appear in macro instructions.  These are set by
+   mips_ip and read by macro.  */
 
 static expressionS imm_expr;
 static expressionS imm2_expr;
-static expressionS offset_expr;
 
-/* Relocs associated with imm_expr and offset_expr.  */
+/* The relocatable field in an instruction and the relocs associated
+   with it.  These variables are used for instructions like LUI and
+   JAL as well as true offsets.  They are also used for address
+   operands in macros.  */
 
-static bfd_reloc_code_real_type imm_reloc[3]
-  = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+static expressionS offset_expr;
 static bfd_reloc_code_real_type offset_reloc[3]
   = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
 
@@ -1826,12 +1756,10 @@ static unsigned int forced_insn_length;
 
 static bfd_boolean mips_assembling_insn;
 
-#ifdef OBJ_ELF
 /* The pdr segment for per procedure frame/regmask info.  Not used for
    ECOFF debugging.  */
 
 static segT pdr_seg;
-#endif
 
 /* The default target format to use.  */
 
@@ -2038,6 +1966,15 @@ create_insn (struct mips_cl_insn *insn, const struct mips_opcode *mo)
   insn->cleared_p = 0;
 }
 
+/* Install UVAL as the value of OPERAND in INSN.  */
+
+static inline void
+insn_insert_operand (struct mips_cl_insn *insn,
+                    const struct mips_operand *operand, unsigned int uval)
+{
+  insn->insn_opcode = mips_insert_operand (operand, insn->insn_opcode, uval);
+}
+
 /* Record the current MIPS16/microMIPS mode in now_seg.  */
 
 static void
@@ -2462,20 +2399,33 @@ static const struct regname reg_names_n32n64[] = {
   {0, 0}
 };
 
-/* Check if S points at a valid register specifier according to TYPES.
-   If so, then return 1, advance S to consume the specifier and store
-   the register's number in REGNOP, otherwise return 0.  */
+/* Register symbols $v0 and $v1 map to GPRs 2 and 3, but they can also be
+   interpreted as vector registers 0 and 1.  If SYMVAL is the value of one
+   of these register symbols, return the associated vector register,
+   otherwise return SYMVAL itself.  */
 
-static int
-reg_lookup (char **s, unsigned int types, unsigned int *regnop)
+static unsigned int
+mips_prefer_vec_regno (unsigned int symval)
 {
-  symbolS *symbolP;
-  char *e;
+  if ((symval & -2) == (RTYPE_GP | 2))
+    return RTYPE_VEC | (symval & 1);
+  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).  */
+
+static bfd_boolean
+mips_parse_register (char **sptr, unsigned int *symval)
+{
+  symbolS *symbol;
+  char *s, *e;
   char save_c;
-  int reg = -1;
 
   /* Find end of name.  */
-  e = *s;
+  s = e = *sptr;
   if (is_name_beginner (*e))
     ++e;
   while (is_part_of_name (*e))
@@ -2485,99 +2435,294 @@ reg_lookup (char **s, unsigned int types, unsigned int *regnop)
   save_c = *e;
   *e = '\0';
 
-  /* Look for a register symbol.  */
-  if ((symbolP = symbol_find (*s)) && S_GET_SEGMENT (symbolP) == reg_section)
+  /* Look up the name.  */
+  symbol = symbol_find (s);
+  *e = save_c;
+
+  if (!symbol || S_GET_SEGMENT (symbol) != reg_section)
+    return FALSE;
+
+  *sptr = e;
+  *symval = S_GET_VALUE (symbol);
+  return TRUE;
+}
+
+/* Check if SPTR points at a valid register specifier according to TYPES.
+   If so, then return 1, advance S to consume the specifier and store
+   the register's number in REGNOP, otherwise return 0.  */
+
+static int
+reg_lookup (char **s, unsigned int types, unsigned int *regnop)
+{
+  unsigned int regno;
+
+  if (mips_parse_register (s, &regno))
     {
-      int r = S_GET_VALUE (symbolP);
-      if (r & types)
-       reg = r & RNUM_MASK;
-      else if ((types & RTYPE_VEC) && (r & ~1) == (RTYPE_GP | 2))
-       /* Convert GP reg $v0/1 to MDMX reg $v0/1!  */
-       reg = (r & RNUM_MASK) - 2;
+      if (types & RTYPE_VEC)
+       regno = mips_prefer_vec_regno (regno);
+      if (regno & types)
+       regno &= RNUM_MASK;
+      else
+       regno = ~0;
     }
-  /* Else see if this is a register defined in an itbl entry.  */
-  else if ((types & RTYPE_GP) && itbl_have_entries)
+  else
     {
-      char *n = *s;
-      unsigned long r;
-
-      if (*n == '$')
-       ++n;
-      if (itbl_get_reg_val (n, &r))
-       reg = r & RNUM_MASK;
+      if (types & RWARN)
+       as_warn (_("Unrecognized register name `%s'"), *s);
+      regno = ~0;
     }
-
-  /* Advance to next token if a register was recognised.  */
-  if (reg >= 0)
-    *s = e;
-  else if (types & RWARN)
-    as_warn (_("Unrecognized register name `%s'"), *s);
-
-  *e = save_c;
   if (regnop)
-    *regnop = reg;
-  return reg >= 0;
+    *regnop = regno;
+  return regno <= RNUM_MASK;
 }
 
-/* Check if S points at a valid register list according to TYPES.
-   If so, then return 1, advance S to consume the list and store
-   the registers present on the list as a bitmask of ones in REGLISTP,
-   otherwise return 0.  A valid list comprises a comma-separated
-   enumeration of valid single registers and/or dash-separated
-   contiguous register ranges as determined by their numbers.
+/* Token types for parsed operand lists.  */
+enum mips_operand_token_type {
+  /* A plain register, e.g. $f2.  */
+  OT_REG,
 
-   As a special exception if one of s0-s7 registers is specified as
-   the range's lower delimiter and s8 (fp) is its upper one, then no
-   registers whose numbers place them between s7 and s8 (i.e. $24-$29)
-   are selected; they have to be listed separately if needed.  */
+  /* An element of a vector, e.g. $v0[1].  */
+  OT_REG_ELEMENT,
 
-static int
-reglist_lookup (char **s, unsigned int types, unsigned int *reglistp)
-{
-  unsigned int reglist = 0;
-  unsigned int lastregno;
-  bfd_boolean ok = TRUE;
-  unsigned int regmask;
-  char *s_endlist = *s;
-  char *s_reset = *s;
+  /* A continuous range of registers, e.g. $s0-$s4.  */
+  OT_REG_RANGE,
+
+  /* A (possibly relocated) expression.  */
+  OT_INTEGER,
+
+  /* A floating-point value.  */
+  OT_FLOAT,
+
+  /* A single character.  This can be '(', ')' or ',', but '(' only appears
+     before OT_REGs.  */
+  OT_CHAR,
+
+  /* The end of the operand list.  */
+  OT_END
+};
+
+/* A parsed operand token.  */
+struct mips_operand_token
+{
+  /* The type of token.  */
+  enum mips_operand_token_type type;
+  union
+  {
+    /* The register symbol value for an OT_REG.  */
+    unsigned int regno;
+
+    /* The register symbol value and index for an OT_REG_ELEMENT.  */
+    struct {
+      unsigned int regno;
+      addressT index;
+    } reg_element;
+
+    /* The two register symbol values involved in an OT_REG_RANGE.  */
+    struct {
+      unsigned int regno1;
+      unsigned int regno2;
+    } reg_range;
+
+    /* The value of an OT_INTEGER.  The value is represented as an
+       expression and the relocation operators that were applied to
+       that expression.  The reloc entries are BFD_RELOC_UNUSED if no
+       relocation operators were used.  */
+    struct {
+      expressionS value;
+      bfd_reloc_code_real_type relocs[3];
+    } integer;
+
+    /* The binary data for an OT_FLOAT constant, and the number of bytes
+       in the constant.  */
+    struct {
+      unsigned char data[8];
+      int length;
+    } flt;
+
+    /* The character represented by an OT_CHAR.  */
+    char ch;
+  } u;
+};
+
+/* An obstack used to construct lists of mips_operand_tokens.  */
+static struct obstack mips_operand_tokens;
+
+/* Give TOKEN type TYPE and add it to mips_operand_tokens.  */
+
+static void
+mips_add_token (struct mips_operand_token *token,
+               enum mips_operand_token_type type)
+{
+  token->type = type;
+  obstack_grow (&mips_operand_tokens, token, sizeof (*token));
+}
+
+/* Check whether S is '(' followed by a register name.  Add OT_CHAR
+   and OT_REG tokens for them if so, and return a pointer to the first
+   unconsumed character.  Return null otherwise.  */
+
+static char *
+mips_parse_base_start (char *s)
+{
+  struct mips_operand_token token;
   unsigned int regno;
 
-  while (reg_lookup (s, types, &regno))
+  if (*s != '(')
+    return 0;
+
+  ++s;
+  SKIP_SPACE_TABS (s);
+  if (!mips_parse_register (&s, &regno))
+    return 0;
+
+  token.u.ch = '(';
+  mips_add_token (&token, OT_CHAR);
+
+  token.u.regno = regno;
+  mips_add_token (&token, OT_REG);
+
+  return s;
+}
+
+/* Parse one or more tokens from S.  Return a pointer to the first
+   unconsumed character on success.  Return null if an error was found
+   and store the error text in insn_error.  FLOAT_FORMAT is as for
+   mips_parse_arguments.  */
+
+static char *
+mips_parse_argument_token (char *s, char float_format)
+{
+  char *end, *save_in, *err;
+  unsigned int regno1, regno2;
+  struct mips_operand_token token;
+
+  /* First look for "($reg", since we want to treat that as an
+     OT_CHAR and OT_REG rather than an expression.  */
+  end = mips_parse_base_start (s);
+  if (end)
+    return end;
+
+  /* Handle other characters that end up as OT_CHARs.  */
+  if (*s == ')' || *s == ',')
+    {
+      token.u.ch = *s;
+      mips_add_token (&token, OT_CHAR);
+      ++s;
+      return s;
+    }
+
+  /* Handle tokens that start with a register.  */
+  if (mips_parse_register (&s, &regno1))
     {
-      lastregno = regno;
-      if (**s == '-')
+      SKIP_SPACE_TABS (s);
+      if (*s == '-')
        {
-         (*s)++;
-         ok = reg_lookup (s, types, &lastregno);
-         if (ok && lastregno < regno)
-           ok = FALSE;
-         if (!ok)
-           break;
-       }
+         /* A register range.  */
+         ++s;
+         SKIP_SPACE_TABS (s);
+         if (!mips_parse_register (&s, &regno2))
+           {
+             insn_error = _("Invalid register range");
+             return 0;
+           }
 
-      if (lastregno == FP && regno >= S0 && regno <= S7)
+         token.u.reg_range.regno1 = regno1;
+         token.u.reg_range.regno2 = regno2;
+         mips_add_token (&token, OT_REG_RANGE);
+         return s;
+       }
+      else if (*s == '[')
        {
-         lastregno = S7;
-         reglist |= 1 << FP;
+         /* A vector element.  */
+         expressionS element;
+
+         ++s;
+         SKIP_SPACE_TABS (s);
+         my_getExpression (&element, s);
+         if (element.X_op != O_constant)
+           {
+             insn_error = _("Vector element must be constant");
+             return 0;
+           }
+         s = expr_end;
+         SKIP_SPACE_TABS (s);
+         if (*s != ']')
+           {
+             insn_error = _("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;
        }
-      regmask = 1 << lastregno;
-      regmask = (regmask << 1) - 1;
-      regmask ^= (1 << regno) - 1;
-      reglist |= regmask;
 
-      s_endlist = *s;
-      if (**s != ',')
-       break;
-      (*s)++;
+      /* Looks like just a plain register.  */
+      token.u.regno = regno1;
+      mips_add_token (&token, OT_REG);
+      return s;
     }
 
-  if (ok)
-    *s = s_endlist;
-  else
-    *s = s_reset;
-  if (reglistp)
-    *reglistp = reglist;
-  return ok && reglist != 0;
+  if (float_format)
+    {
+      /* First try to treat expressions as floats.  */
+      save_in = input_line_pointer;
+      input_line_pointer = s;
+      err = md_atof (float_format, (char *) token.u.flt.data,
+                    &token.u.flt.length);
+      end = input_line_pointer;
+      input_line_pointer = save_in;
+      if (err && *err)
+       {
+         insn_error = err;
+         return 0;
+       }
+      if (s != end)
+       {
+         mips_add_token (&token, OT_FLOAT);
+         return end;
+       }
+    }
+
+  /* Treat everything else as an integer expression.  */
+  token.u.integer.relocs[0] = BFD_RELOC_UNUSED;
+  token.u.integer.relocs[1] = BFD_RELOC_UNUSED;
+  token.u.integer.relocs[2] = BFD_RELOC_UNUSED;
+  my_getSmallExpression (&token.u.integer.value, token.u.integer.relocs, s);
+  s = expr_end;
+  mips_add_token (&token, OT_INTEGER);
+  return s;
+}
+
+/* S points to the operand list for an instruction.  FLOAT_FORMAT is 'f'
+   if expressions should be treated as 32-bit floating-point constants,
+   'd' if they should be treated as 64-bit floating-point constants,
+   or 0 if they should be treated as integer expressions (the usual case).
+
+   Return a list of tokens on success, otherwise return 0.  The caller
+   must obstack_free the list after use.  */
+
+static struct mips_operand_token *
+mips_parse_arguments (char *s, char float_format)
+{
+  struct mips_operand_token token;
+
+  SKIP_SPACE_TABS (s);
+  while (*s)
+    {
+      s = mips_parse_argument_token (s, float_format);
+      if (!s)
+       {
+         obstack_free (&mips_operand_tokens,
+                       obstack_finish (&mips_operand_tokens));
+         return 0;
+       }
+      SKIP_SPACE_TABS (s);
+    }
+  mips_add_token (&token, OT_END);
+  return (struct mips_operand_token *) obstack_finish (&mips_operand_tokens);
 }
 
 /* Return TRUE if opcode MO is valid on the currently selected ISA, ASE
@@ -2640,6 +2785,13 @@ is_size_valid (const struct mips_opcode *mo)
   if (!mips_opts.micromips)
     return TRUE;
 
+  if (mips_opts.insn32)
+    {
+      if (mo->pinfo != INSN_MACRO && micromips_insn_length (mo) != 4)
+       return FALSE;
+      if ((mo->pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0)
+       return FALSE;
+    }
   if (!forced_insn_length)
     return TRUE;
   if (mo->pinfo == INSN_MACRO)
@@ -2679,6 +2831,111 @@ 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.  */
+
+static int
+validate_mips_insn (const struct mips_opcode *opcode,
+                   unsigned long insn_bits,
+                   const struct mips_operand *(*decode_operand) (const char *))
+{
+  const char *s;
+  unsigned long used_bits, doubled, undefined;
+  const struct mips_operand *operand;
+
+  if ((opcode->mask & opcode->match) != opcode->match)
+    {
+      as_bad (_("internal: bad mips opcode (mask error): %s %s"),
+             opcode->name, opcode->args);
+      return 0;
+    }
+  used_bits = 0;
+  for (s = opcode->args; *s; ++s)
+    switch (*s)
+      {
+      case ',':
+      case '(':
+      case ')':
+       break;
+
+      default:
+       operand = decode_operand (s);
+       if (!operand)
+         {
+           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));
+       /* Skip prefix characters.  */
+       if (*s == '+' || *s == 'm')
+         ++s;
+       break;
+      }
+  doubled = used_bits & opcode->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;
+  undefined = ~used_bits & insn_bits;
+  if (undefined)
+    {
+      as_bad (_("internal: bad mips opcode (bits 0x%08lx undefined): %s %s"),
+             undefined, opcode->name, opcode->args);
+      return 0;
+    }
+  used_bits &= ~insn_bits;
+  if (used_bits)
+    {
+      as_bad (_("internal: bad mips opcode (bits 0x%08lx defined): %s %s"),
+             used_bits, opcode->name, opcode->args);
+      return 0;
+    }
+  return 1;
+}
+
+/* The microMIPS version of validate_mips_insn.  */
+
+static int
+validate_micromips_insn (const struct mips_opcode *opc)
+{
+  unsigned long insn_bits;
+  unsigned long major;
+  unsigned int length;
+
+  length = micromips_insn_length (opc);
+  if (length != 2 && length != 4)
+    {
+      as_bad (_("Internal error: bad microMIPS opcode (incorrect length: %u): "
+               "%s %s"), length, opc->name, opc->args);
+      return 0;
+    }
+  major = opc->match >> (10 + 8 * (length - 2));
+  if ((length == 2 && (major & 7) != 1 && (major & 6) != 2)
+      || (length == 4 && (major & 7) != 0 && (major & 4) != 4))
+    {
+      as_bad (_("Internal error: bad microMIPS opcode "
+               "(opcode/length mismatch): %s %s"), opc->name, opc->args);
+      return 0;
+    }
+
+  /* Shift piecewise to avoid an overflow where unsigned long is 32-bit.  */
+  insn_bits = 1 << 4 * length;
+  insn_bits <<= 4 * length;
+  insn_bits -= 1;
+  return validate_mips_insn (opc, insn_bits, decode_micromips_operand);
+}
+
 /* This function is called once, at assembler startup time.  It should set up
    all the tables, etc. that the MD part of the assembler will need.  */
 
@@ -2717,7 +2974,8 @@ md_begin (void)
        {
          if (mips_opcodes[i].pinfo != INSN_MACRO)
            {
-             if (!validate_mips_insn (&mips_opcodes[i]))
+             if (!validate_mips_insn (&mips_opcodes[i], 0xffffffff,
+                                      decode_mips_operand))
                broken = 1;
              if (nop_insn.insn_mo == NULL && strcmp (name, "nop") == 0)
                {
@@ -2822,6 +3080,8 @@ md_begin (void)
                                       reg_names_o32[i].num, /* & RNUM_MASK, */
                                       &zero_address_frag));
 
+  obstack_init (&mips_operand_tokens);
+
   mips_no_prev_insn ();
 
   mips_gprmask = 0;
@@ -2835,93 +3095,88 @@ md_begin (void)
 
   bfd_set_gp_size (stdoutput, g_switch_value);
 
-#ifdef OBJ_ELF
-  if (IS_ELF)
+  /* On a native system other than VxWorks, sections must be aligned
+     to 16 byte boundaries.  When configured for an embedded ELF
+     target, we don't bother.  */
+  if (strncmp (TARGET_OS, "elf", 3) != 0
+      && strncmp (TARGET_OS, "vxworks", 7) != 0)
     {
-      /* On a native system other than VxWorks, sections must be aligned
-        to 16 byte boundaries.  When configured for an embedded ELF
-        target, we don't bother.  */
-      if (strncmp (TARGET_OS, "elf", 3) != 0
-         && strncmp (TARGET_OS, "vxworks", 7) != 0)
-       {
-         (void) bfd_set_section_alignment (stdoutput, text_section, 4);
-         (void) bfd_set_section_alignment (stdoutput, data_section, 4);
-         (void) bfd_set_section_alignment (stdoutput, bss_section, 4);
-       }
+      (void) bfd_set_section_alignment (stdoutput, text_section, 4);
+      (void) bfd_set_section_alignment (stdoutput, data_section, 4);
+      (void) bfd_set_section_alignment (stdoutput, bss_section, 4);
+    }
 
-      /* Create a .reginfo section for register masks and a .mdebug
-        section for debugging information.  */
+  /* Create a .reginfo section for register masks and a .mdebug
+     section for debugging information.  */
+  {
+    segT seg;
+    subsegT subseg;
+    flagword flags;
+    segT sec;
+
+    seg = now_seg;
+    subseg = now_subseg;
+
+    /* The ABI says this section should be loaded so that the
+       running program can access it.  However, we don't load it
+       if we are configured for an embedded target */
+    flags = SEC_READONLY | SEC_DATA;
+    if (strncmp (TARGET_OS, "elf", 3) != 0)
+      flags |= SEC_ALLOC | SEC_LOAD;
+
+    if (mips_abi != N64_ABI)
       {
-       segT seg;
-       subsegT subseg;
-       flagword flags;
-       segT sec;
-
-       seg = now_seg;
-       subseg = now_subseg;
-
-       /* The ABI says this section should be loaded so that the
-          running program can access it.  However, we don't load it
-          if we are configured for an embedded target */
-       flags = SEC_READONLY | SEC_DATA;
-       if (strncmp (TARGET_OS, "elf", 3) != 0)
-         flags |= SEC_ALLOC | SEC_LOAD;
-
-       if (mips_abi != N64_ABI)
-         {
-           sec = subseg_new (".reginfo", (subsegT) 0);
-
-           bfd_set_section_flags (stdoutput, sec, flags);
-           bfd_set_section_alignment (stdoutput, sec, HAVE_NEWABI ? 3 : 2);
+       sec = subseg_new (".reginfo", (subsegT) 0);
 
-           mips_regmask_frag = frag_more (sizeof (Elf32_External_RegInfo));
-         }
-       else
-         {
-           /* The 64-bit ABI uses a .MIPS.options section rather than
-               .reginfo section.  */
-           sec = subseg_new (".MIPS.options", (subsegT) 0);
-           bfd_set_section_flags (stdoutput, sec, flags);
-           bfd_set_section_alignment (stdoutput, sec, 3);
+       bfd_set_section_flags (stdoutput, sec, flags);
+       bfd_set_section_alignment (stdoutput, sec, HAVE_NEWABI ? 3 : 2);
 
-           /* Set up the option header.  */
-           {
-             Elf_Internal_Options opthdr;
-             char *f;
-
-             opthdr.kind = ODK_REGINFO;
-             opthdr.size = (sizeof (Elf_External_Options)
-                            + sizeof (Elf64_External_RegInfo));
-             opthdr.section = 0;
-             opthdr.info = 0;
-             f = frag_more (sizeof (Elf_External_Options));
-             bfd_mips_elf_swap_options_out (stdoutput, &opthdr,
-                                            (Elf_External_Options *) f);
-
-             mips_regmask_frag = frag_more (sizeof (Elf64_External_RegInfo));
-           }
-         }
+       mips_regmask_frag = frag_more (sizeof (Elf32_External_RegInfo));
+      }
+    else
+      {
+       /* The 64-bit ABI uses a .MIPS.options section rather than
+          .reginfo section.  */
+       sec = subseg_new (".MIPS.options", (subsegT) 0);
+       bfd_set_section_flags (stdoutput, sec, flags);
+       bfd_set_section_alignment (stdoutput, sec, 3);
 
-       if (ECOFF_DEBUGGING)
-         {
-           sec = subseg_new (".mdebug", (subsegT) 0);
-           (void) bfd_set_section_flags (stdoutput, sec,
-                                         SEC_HAS_CONTENTS | SEC_READONLY);
-           (void) bfd_set_section_alignment (stdoutput, sec, 2);
-         }
-       else if (mips_flag_pdr)
-         {
-           pdr_seg = subseg_new (".pdr", (subsegT) 0);
-           (void) bfd_set_section_flags (stdoutput, pdr_seg,
-                                         SEC_READONLY | SEC_RELOC
-                                         | SEC_DEBUGGING);
-           (void) bfd_set_section_alignment (stdoutput, pdr_seg, 2);
-         }
+       /* Set up the option header.  */
+       {
+         Elf_Internal_Options opthdr;
+         char *f;
+
+         opthdr.kind = ODK_REGINFO;
+         opthdr.size = (sizeof (Elf_External_Options)
+                        + sizeof (Elf64_External_RegInfo));
+         opthdr.section = 0;
+         opthdr.info = 0;
+         f = frag_more (sizeof (Elf_External_Options));
+         bfd_mips_elf_swap_options_out (stdoutput, &opthdr,
+                                        (Elf_External_Options *) f);
+
+         mips_regmask_frag = frag_more (sizeof (Elf64_External_RegInfo));
+       }
+      }
 
-       subseg_set (seg, subseg);
+    if (ECOFF_DEBUGGING)
+      {
+       sec = subseg_new (".mdebug", (subsegT) 0);
+       (void) bfd_set_section_flags (stdoutput, sec,
+                                     SEC_HAS_CONTENTS | SEC_READONLY);
+       (void) bfd_set_section_alignment (stdoutput, sec, 2);
       }
-    }
-#endif /* OBJ_ELF */
+    else if (mips_flag_pdr)
+      {
+       pdr_seg = subseg_new (".pdr", (subsegT) 0);
+       (void) bfd_set_section_flags (stdoutput, pdr_seg,
+                                     SEC_READONLY | SEC_RELOC
+                                     | SEC_DEBUGGING);
+       (void) bfd_set_section_alignment (stdoutput, pdr_seg, 2);
+      }
+
+    subseg_set (seg, subseg);
+  }
 
   if (! ECOFF_DEBUGGING)
     md_obj_begin ();
@@ -2948,9 +3203,6 @@ md_assemble (char *str)
   imm_expr.X_op = O_absent;
   imm2_expr.X_op = O_absent;
   offset_expr.X_op = O_absent;
-  imm_reloc[0] = BFD_RELOC_UNUSED;
-  imm_reloc[1] = BFD_RELOC_UNUSED;
-  imm_reloc[2] = BFD_RELOC_UNUSED;
   offset_reloc[0] = BFD_RELOC_UNUSED;
   offset_reloc[1] = BFD_RELOC_UNUSED;
   offset_reloc[2] = BFD_RELOC_UNUSED;
@@ -2975,14 +3227,12 @@ md_assemble (char *str)
       if (mips_opts.mips16)
        mips16_macro (&insn);
       else
-       macro (&insn);
+       macro (&insn, str);
       macro_end ();
     }
   else
     {
-      if (imm_expr.X_op != O_absent)
-       append_insn (&insn, &imm_expr, imm_reloc, FALSE);
-      else if (offset_expr.X_op != O_absent)
+      if (offset_expr.X_op != O_absent)
        append_insn (&insn, &offset_expr, offset_reloc, FALSE);
       else
        append_insn (&insn, NULL, unused_reloc, FALSE);
@@ -3081,6 +3331,13 @@ jalr_reloc_p (bfd_reloc_code_real_type reloc)
   return reloc == BFD_RELOC_MIPS_JALR || reloc == BFD_RELOC_MICROMIPS_JALR;
 }
 
+static inline bfd_boolean
+gprel16_reloc_p (bfd_reloc_code_real_type reloc)
+{
+  return (reloc == BFD_RELOC_GPREL16 || reloc == BFD_RELOC_MIPS16_GPREL
+         || reloc == BFD_RELOC_MICROMIPS_GPREL16);
+}
+
 /* Return true if RELOC is a PC-relative relocation that does not have
    full address range.  */
 
@@ -3140,32 +3397,6 @@ fixup_has_matching_lo_p (fixS *fixp)
          && fixp->fx_offset == fixp->fx_next->fx_offset);
 }
 
-/* This function returns true if modifying a register requires a
-   delay.  */
-
-static int
-reg_needs_delay (unsigned int reg)
-{
-  unsigned long prev_pinfo;
-
-  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)))
-    {
-      /* A load from a coprocessor or from memory.  All load delays
-        delay the use of general register rt for one instruction.  */
-      /* Itbl support may require additional care here.  */
-      know (prev_pinfo & INSN_WRITE_GPR_T);
-      if (reg == EXTRACT_OPERAND (mips_opts.micromips, RT, history[0]))
-       return 1;
-    }
-
-  return 0;
-}
-
 /* Move all labels in LABELS to the current insertion point.  TEXT_P
    says whether the labels refer to text or data.  */
 
@@ -3206,14 +3437,12 @@ s_is_linkonce (symbolS *sym, segT from_seg)
     {
       if ((bfd_get_section_flags (stdoutput, symseg) & SEC_LINK_ONCE))
        linkonce = TRUE;
-#ifdef OBJ_ELF
       /* The GNU toolchain uses an extension for ELF: a section
         beginning with the magic string .gnu.linkonce is a
         linkonce section.  */
       if (strncmp (segment_name (symseg), ".gnu.linkonce",
                   sizeof ".gnu.linkonce" - 1) == 0)
        linkonce = TRUE;
-#endif
     }
   return linkonce;
 }
@@ -3231,15 +3460,10 @@ mips_compressed_mark_label (symbolS *label)
 {
   gas_assert (HAVE_CODE_COMPRESSION);
 
-#if defined(OBJ_ELF) || defined(OBJ_MAYBE_ELF)
-  if (IS_ELF)
-    {
-      if (mips_opts.mips16)
-       S_SET_OTHER (label, ELF_ST_SET_MIPS16 (S_GET_OTHER (label)));
-      else
-       S_SET_OTHER (label, ELF_ST_SET_MICROMIPS (S_GET_OTHER (label)));
-    }
-#endif
+  if (mips_opts.mips16)
+    S_SET_OTHER (label, ELF_ST_SET_MIPS16 (S_GET_OTHER (label)));
+  else
+    S_SET_OTHER (label, ELF_ST_SET_MICROMIPS (S_GET_OTHER (label)));
   if ((S_GET_VALUE (label) & 1) == 0
       /* Don't adjust the address if the label is global or weak, or
         in a link-once section, since we'll be emitting symbol reloc
@@ -3323,12 +3547,8 @@ delayed_branch_p (const struct mips_cl_insn *ip)
 static inline bfd_boolean
 compact_branch_p (const struct mips_cl_insn *ip)
 {
-  if (mips_opts.mips16)
-    return (ip->insn_mo->pinfo & (MIPS16_INSN_UNCOND_BRANCH
-                                 | MIPS16_INSN_COND_BRANCH)) != 0;
-  else
-    return (ip->insn_mo->pinfo2 & (INSN2_UNCOND_BRANCH
-                                  | INSN2_COND_BRANCH)) != 0;
+  return (ip->insn_mo->pinfo2 & (INSN2_UNCOND_BRANCH
+                                | INSN2_COND_BRANCH)) != 0;
 }
 
 /* Return true if IP is an unconditional branch or jump.  */
@@ -3337,9 +3557,7 @@ static inline bfd_boolean
 uncond_branch_p (const struct mips_cl_insn *ip)
 {
   return ((ip->insn_mo->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0
-         || (mips_opts.mips16
-             ? (ip->insn_mo->pinfo & MIPS16_INSN_UNCOND_BRANCH) != 0
-             : (ip->insn_mo->pinfo2 & INSN2_UNCOND_BRANCH) != 0));
+         || (ip->insn_mo->pinfo2 & INSN2_UNCOND_BRANCH) != 0);
 }
 
 /* Return true if IP is a branch-likely instruction.  */
@@ -3378,9 +3596,9 @@ gpr_mod_mask (const struct mips_cl_insn *ip)
        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 (pinfo2 & INSN2_MOD_SP)
-       mask |= 1 << SP;
     }
+  if (pinfo2 & INSN2_MOD_SP)
+    mask |= 1 << SP;
   return mask;
 }
 
@@ -3405,8 +3623,6 @@ gpr_read_mask (const struct mips_cl_insn *ip)
        mask |= 1 << TREG;
       if (pinfo & MIPS16_INSN_READ_SP)
        mask |= 1 << SP;
-      if (pinfo & MIPS16_INSN_READ_31)
-       mask |= 1 << RA;
       if (pinfo & MIPS16_INSN_READ_Z)
        mask |= 1 << (mips16_to_32_reg_map
                      [MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip)]);
@@ -3423,11 +3639,11 @@ gpr_read_mask (const struct mips_cl_insn *ip)
        mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RS, *ip);
       if (pinfo2 & INSN2_READ_GP)
        mask |= 1 << GP;
-      if (pinfo2 & INSN2_READ_GPR_31)
-       mask |= 1 << RA;
       if (pinfo2 & INSN2_READ_GPR_Z)
        mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RZ, *ip);
     }
+  if (pinfo2 & INSN2_READ_GPR_31)
+    mask |= 1 << RA;
   if (mips_opts.micromips)
     {
       if (pinfo2 & INSN2_READ_GPR_MC)
@@ -3473,8 +3689,6 @@ gpr_write_mask (const struct mips_cl_insn *ip)
        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_SP)
-       mask |= 1 << SP;
       if (pinfo & MIPS16_INSN_WRITE_31)
        mask |= 1 << RA;
       if (pinfo & MIPS16_INSN_WRITE_GPR_Y)
@@ -3497,10 +3711,10 @@ gpr_write_mask (const struct mips_cl_insn *ip)
     {
       if (pinfo2 & INSN2_WRITE_GPR_MB)
        mask |= 1 << micromips_to_32_reg_b_map[EXTRACT_OPERAND (1, MB, *ip)];
-      if (pinfo2 & INSN2_WRITE_GPR_MHI)
+      if (pinfo2 & INSN2_WRITE_GPR_MH)
        {
-         mask |= 1 << micromips_to_32_reg_h_map[EXTRACT_OPERAND (1, MH, *ip)];
-         mask |= 1 << micromips_to_32_reg_i_map[EXTRACT_OPERAND (1, MI, *ip)];
+         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);
@@ -3571,10360 +3785,8673 @@ fpr_write_mask (const struct mips_cl_insn *ip)
   return mask;
 }
 
-/* Classify an instruction according to the FIX_VR4120_* enumeration.
-   Return NUM_FIX_VR4120_CLASSES if the instruction isn't affected
-   by VR4120 errata.  */
+/* Operand OPNUM of INSN is an odd-numbered floating-point register.
+   Check whether that is allowed.  */
 
-static unsigned int
-classify_vr4120_insn (const char *name)
+static bfd_boolean
+mips_oddfpreg_ok (const struct mips_opcode *insn, int opnum)
 {
-  if (strncmp (name, "macc", 4) == 0)
-    return FIX_VR4120_MACC;
-  if (strncmp (name, "dmacc", 5) == 0)
-    return FIX_VR4120_DMACC;
-  if (strncmp (name, "mult", 4) == 0)
-    return FIX_VR4120_MULT;
-  if (strncmp (name, "dmult", 5) == 0)
-    return FIX_VR4120_DMULT;
-  if (strstr (name, "div"))
-    return FIX_VR4120_DIV;
-  if (strcmp (name, "mtlo") == 0 || strcmp (name, "mthi") == 0)
-    return FIX_VR4120_MTHILO;
-  return NUM_FIX_VR4120_CLASSES;
+  const char *s = insn->name;
+
+  if (insn->pinfo == INSN_MACRO)
+    /* Let a macro pass, we'll catch it later when it is expanded.  */
+    return TRUE;
+
+  if (ISA_HAS_ODD_SINGLE_FPR (mips_opts.isa) || mips_opts.arch == CPU_R5900)
+    {
+      /* Allow odd registers for single-precision ops.  */
+      switch (insn->pinfo & (FP_S | FP_D))
+       {
+       case FP_S:
+       case 0:
+         return TRUE;
+       case FP_D:
+         return FALSE;
+       default:
+         break;
+       }
+
+      /* Cvt.w.x and cvt.x.w allow an odd register for a 'w' or 's' operand.  */
+      s = strchr (insn->name, '.');
+      if (s != NULL && opnum == 2)
+       s = strchr (s + 1, '.');
+      return (s != NULL && (s[1] == 'w' || s[1] == 's'));
+    }
+
+  /* Single-precision coprocessor loads and moves are OK too.  */
+  if ((insn->pinfo & FP_S)
+      && (insn->pinfo & (INSN_COPROC_MEMORY_DELAY | INSN_STORE_MEMORY
+                        | INSN_LOAD_COPROC_DELAY | INSN_COPROC_MOVE_DELAY)))
+    return TRUE;
+
+  return FALSE;
 }
 
-#define INSN_ERET  0x42000018
-#define INSN_DERET 0x4200001f
+/* 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.  */
 
-/* Return the number of instructions that must separate INSN1 and INSN2,
-   where INSN1 is the earlier instruction.  Return the worst-case value
-   for any INSN2 if INSN2 is null.  */
+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);
+}
 
-static unsigned int
-insns_between (const struct mips_cl_insn *insn1,
-              const struct mips_cl_insn *insn2)
+/* Report an invalid combination of position and size operands for a bitfield
+   operation.  POS and SIZE are the values that were given.  */
+
+static void
+report_bad_field (offsetT pos, offsetT size)
 {
-  unsigned long pinfo1, pinfo2;
-  unsigned int mask;
+  as_bad (_("Invalid field specification (position %ld, size %ld)"),
+         (unsigned long) pos, (unsigned long) size);
+}
 
-  /* This function needs to know which pinfo flags are set for INSN2
-     and which registers INSN2 uses.  The former is stored in PINFO2 and
-     the latter is tested via INSN2_USES_GPR.  If INSN2 is null, PINFO2
-     will have every flag set and INSN2_USES_GPR will always return true.  */
-  pinfo1 = insn1->insn_mo->pinfo;
-  pinfo2 = insn2 ? insn2->insn_mo->pinfo : ~0U;
+/* Information about an instruction argument that we're trying to match.  */
+struct mips_arg_info
+{
+  /* The instruction so far.  */
+  struct mips_cl_insn *insn;
 
-#define INSN2_USES_GPR(REG) \
-  (insn2 == NULL || (gpr_read_mask (insn2) & (1U << (REG))) != 0)
+  /* The first unconsumed operand token.  */
+  struct mips_operand_token *token;
 
-  /* For most targets, write-after-read dependencies on the HI and LO
-     registers must be separated by at least two instructions.  */
-  if (!hilo_interlocks)
+  /* The 1-based operand number, in terms of insn->insn_mo->args.  */
+  int opnum;
+
+  /* The 1-based argument number, for error reporting.  This does not
+     count elided optional registers, etc..  */
+  int argnum;
+
+  /* The last OP_REG operand seen, or ILLEGAL_REG if none.  */
+  unsigned int last_regno;
+
+  /* If the first operand was an OP_REG, this is the register that it
+     specified, otherwise it is ILLEGAL_REG.  */
+  unsigned int dest_regno;
+
+  /* The value of the last OP_INT operand.  Only used for OP_MSB,
+     where it gives the lsb position.  */
+  unsigned int last_op_int;
+
+  /* If true, match routines should silently reject invalid arguments.
+     If false, match routines can accept invalid arguments as long as
+     they report an appropriate error.  They still have the option of
+     silently rejecting arguments, in which case a generic "Invalid operands"
+     style of error will be used instead.  */
+  bfd_boolean soft_match;
+
+  /* If true, the OP_INT match routine should treat plain symbolic operands
+     as if a relocation operator like %lo(...) had been used.  This is only
+     ever true if the operand can be relocated.  */
+  bfd_boolean allow_nonconst;
+
+  /* When true, the OP_INT match routine should allow unsigned N-bit
+     arguments to be used where a signed N-bit operand is expected.  */
+  bfd_boolean lax_max;
+
+  /* True if a reference to the current AT register was seen.  */
+  bfd_boolean seen_at;
+};
+
+/* Try to match an OT_CHAR token for character CH.  Consume the token
+   and return true on success, otherwise return false.  */
+
+static bfd_boolean
+match_char (struct mips_arg_info *arg, char ch)
+{
+  if (arg->token->type == OT_CHAR && arg->token->u.ch == ch)
     {
-      if ((pinfo1 & INSN_READ_LO) && (pinfo2 & INSN_WRITE_LO))
-       return 2;
-      if ((pinfo1 & INSN_READ_HI) && (pinfo2 & INSN_WRITE_HI))
-       return 2;
+      ++arg->token;
+      if (ch == ',')
+       arg->argnum += 1;
+      return TRUE;
     }
+  return FALSE;
+}
 
-  /* If we're working around r7000 errata, there must be two instructions
-     between an mfhi or mflo and any instruction that uses the result.  */
-  if (mips_7000_hilo_fix
-      && !mips_opts.micromips
-      && MF_HILO_INSN (pinfo1)
-      && INSN2_USES_GPR (EXTRACT_OPERAND (0, RD, *insn1)))
-    return 2;
+/* Try to get an expression from the next tokens in ARG.  Consume the
+   tokens and return true on success, storing the expression value in
+   VALUE and relocation types in R.  */
 
-  /* If we're working around 24K errata, one instruction is required
-     if an ERET or DERET is followed by a branch instruction.  */
-  if (mips_fix_24k && !mips_opts.micromips)
+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 (insn1->insn_opcode == INSN_ERET
-         || insn1->insn_opcode == INSN_DERET)
-       {
-         if (insn2 == NULL
-             || insn2->insn_opcode == INSN_ERET
-             || insn2->insn_opcode == INSN_DERET
-             || delayed_branch_p (insn2))
-           return 1;
-       }
+      *value = arg->token->u.integer.value;
+      memcpy (r, arg->token->u.integer.relocs, 3 * sizeof (*r));
+      ++arg->token;
+      return TRUE;
     }
 
-  /* If working around VR4120 errata, check for combinations that need
-     a single intervening instruction.  */
-  if (mips_fix_vr4120 && !mips_opts.micromips)
+  /* 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)
     {
-      unsigned int class1, class2;
-
-      class1 = classify_vr4120_insn (insn1->insn_mo->name);
-      if (class1 != NUM_FIX_VR4120_CLASSES && vr4120_conflicts[class1] != 0)
-       {
-         if (insn2 == NULL)
-           return 1;
-         class2 = classify_vr4120_insn (insn2->insn_mo->name);
-         if (vr4120_conflicts[class1] & (1 << class2))
-           return 1;
-       }
+      value->X_add_number = arg->token->u.regno;
+      ++arg->token;
     }
-
-  if (!HAVE_CODE_COMPRESSION)
+  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 == ')')
     {
-      /* 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)))
-       {
-         know (pinfo1 & INSN_WRITE_GPR_T);
-         if (INSN2_USES_GPR (EXTRACT_OPERAND (0, RT, *insn1)))
-           return 1;
-       }
+      value->X_add_number = arg->token[1].u.regno;
+      arg->token += 3;
+    }
+  else
+    return FALSE;
 
-      /* Check for generic coprocessor hazards.
+  value->X_op = O_register;
+  r[0] = r[1] = r[2] = BFD_RELOC_UNUSED;
+  return TRUE;
+}
 
-        This case is not handled very well.  There is no special
-        knowledge of CP0 handling, and the coprocessors other than
-        the floating point unit are not distinguished at all.  */
-      /* 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))
-              || (!cop_mem_interlocks && (pinfo1 & INSN_COPROC_MEMORY_DELAY)))
-       {
-         /* Handle cases where INSN1 writes to a known general coprocessor
-            register.  There must be a one instruction delay before INSN2
-            if INSN2 reads that register, otherwise no delay is needed.  */
-         mask = fpr_write_mask (insn1);
-         if (mask != 0)
-           {
-             if (!insn2 || (mask & fpr_read_mask (insn2)) != 0)
-               return 1;
-           }
-         else
-           {
-             /* Read-after-write dependencies on the control registers
-                require a two-instruction gap.  */
-             if ((pinfo1 & INSN_WRITE_COND_CODE)
-                 && (pinfo2 & INSN_READ_COND_CODE))
-               return 2;
+/* Try to get a constant expression from the next tokens in ARG.  Consume
+   the tokens and return return true on success, storing the constant value
+   in *VALUE.  Use FALLBACK as the value if the match succeeded with an
+   error.  */
 
-             /* We don't know exactly what INSN1 does.  If INSN2 is
-                also a coprocessor instruction, assume there must be
-                a one instruction gap.  */
-             if (pinfo2 & INSN_COP)
-               return 1;
-           }
-       }
+static bfd_boolean
+match_const_int (struct mips_arg_info *arg, offsetT *value, offsetT fallback)
+{
+  expressionS ex;
+  bfd_reloc_code_real_type r[3];
 
-      /* Check for read-after-write dependencies on the coprocessor
-        control registers in cases where INSN1 does not need a general
-        coprocessor delay.  This means that INSN1 is a floating point
-        comparison instruction.  */
-      /* Itbl support may require additional care here.  */
-      else if (!cop_interlocks
-              && (pinfo1 & INSN_WRITE_COND_CODE)
-              && (pinfo2 & INSN_READ_COND_CODE))
-       return 1;
+  if (!match_expression (arg, &ex, r))
+    return FALSE;
+
+  if (r[0] == BFD_RELOC_UNUSED && ex.X_op == O_constant)
+    *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;
     }
+  return TRUE;
+}
 
-#undef INSN2_USES_GPR
+/* Return the RTYPE_* flags for a register operand of type TYPE that
+   appears in instruction OPCODE.  */
 
-  return 0;
+static unsigned int
+convert_reg_type (const struct mips_opcode *opcode,
+                 enum mips_reg_operand_type type)
+{
+  switch (type)
+    {
+    case OP_REG_GP:
+      return RTYPE_NUM | RTYPE_GP;
+
+    case OP_REG_FP:
+      /* Allow vector register names for MDMX if the instruction is a 64-bit
+        FPR load, store or move (including moves to and from GPRs).  */
+      if ((mips_opts.ase & ASE_MDMX)
+         && (opcode->pinfo & FP_D)
+         && (opcode->pinfo & (INSN_COPROC_MOVE_DELAY
+                              | INSN_COPROC_MEMORY_DELAY
+                              | INSN_LOAD_COPROC_DELAY
+                              | INSN_LOAD_MEMORY_DELAY
+                              | INSN_STORE_MEMORY)))
+       return RTYPE_FPU | RTYPE_VEC;
+      return RTYPE_FPU;
+
+    case OP_REG_CCC:
+      if (opcode->pinfo & (FP_D | FP_S))
+       return RTYPE_CCC | RTYPE_FCC;
+      return RTYPE_CCC;
+
+    case OP_REG_VEC:
+      if (opcode->membership & INSN_5400)
+       return RTYPE_FPU;
+      return RTYPE_FPU | RTYPE_VEC;
+
+    case OP_REG_ACC:
+      return RTYPE_ACC;
+
+    case OP_REG_COPRO:
+      if (opcode->name[strlen (opcode->name) - 1] == '0')
+       return RTYPE_NUM | RTYPE_CP0;
+      return RTYPE_NUM;
+
+    case OP_REG_HW:
+      return RTYPE_NUM;
+    }
+  abort ();
 }
 
-/* Return the number of nops that would be needed to work around the
-   VR4130 mflo/mfhi errata if instruction INSN immediately followed
-   the MAX_VR4130_NOPS instructions described by HIST.  Ignore hazards
-   that are contained within the first IGNORE instructions of HIST.  */
+/* ARG is register REGNO, of type TYPE.  Warn about any dubious registers.  */
 
-static int
-nops_for_vr4130 (int ignore, const struct mips_cl_insn *hist,
-                const struct mips_cl_insn *insn)
+static void
+check_regno (struct mips_arg_info *arg,
+            enum mips_reg_operand_type type, unsigned int regno)
 {
-  int i, j;
-  unsigned int mask;
+  if (AT && type == OP_REG_GP && regno == AT)
+    arg->seen_at = TRUE;
 
-  /* Check if the instruction writes to HI or LO.  MTHI and MTLO
-     are not affected by the errata.  */
-  if (insn != 0
-      && ((insn->insn_mo->pinfo & (INSN_WRITE_HI | INSN_WRITE_LO)) == 0
-         || strcmp (insn->insn_mo->name, "mtlo") == 0
-         || strcmp (insn->insn_mo->name, "mthi") == 0))
-    return 0;
+  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);
 
-  /* Search for the first MFLO or MFHI.  */
-  for (i = 0; i < MAX_VR4130_NOPS; i++)
-    if (MF_HILO_INSN (hist[i].insn_mo->pinfo))
-      {
-       /* Extract the destination register.  */
-       mask = gpr_write_mask (&hist[i]);
+  if (type == OP_REG_CCC)
+    {
+      const char *name;
+      size_t length;
+
+      name = arg->insn->insn_mo->name;
+      length = strlen (name);
+      if ((regno & 1) != 0
+         && ((length >= 3 && strcmp (name + length - 3, ".ps") == 0)
+             || (length >= 5 && strncmp (name + length - 5, "any2", 4) == 0)))
+       as_warn (_("Condition code register should be even for %s, was %d"),
+                name, regno);
+
+      if ((regno & 3) != 0
+         && (length >= 5 && strncmp (name + length - 5, "any4", 4) == 0))
+       as_warn (_("Condition code register should be 0 or 4 for %s, was %d"),
+                name, regno);
+    }
+}
 
-       /* No nops are needed if INSN reads that register.  */
-       if (insn != NULL && (gpr_read_mask (insn) & mask) != 0)
-         return 0;
+/* ARG is a register with symbol value SYMVAL.  Try to interpret it as
+   a register of type TYPE.  Return true on success, storing the register
+   number in *REGNO and warning about any dubious uses.  */
 
-       /* ...or if any of the intervening instructions do.  */
-       for (j = 0; j < i; j++)
-         if (gpr_read_mask (&hist[j]) & mask)
-           return 0;
+static bfd_boolean
+match_regno (struct mips_arg_info *arg, enum mips_reg_operand_type type,
+            unsigned int symval, unsigned int *regno)
+{
+  if (type == OP_REG_VEC)
+    symval = mips_prefer_vec_regno (symval);
+  if (!(symval & convert_reg_type (arg->insn->insn_mo, type)))
+    return FALSE;
 
-       if (i >= ignore)
-         return MAX_VR4130_NOPS - i;
-      }
-  return 0;
+  *regno = symval & RNUM_MASK;
+  check_regno (arg, type, *regno);
+  return TRUE;
 }
 
-#define BASE_REG_EQ(INSN1, INSN2)      \
-  ((((INSN1) >> OP_SH_RS) & OP_MASK_RS) \
-      == (((INSN2) >> OP_SH_RS) & OP_MASK_RS))
-
-/* Return the minimum alignment for this store instruction.  */
+/* Try to interpret the next token in ARG as a register of type TYPE.
+   Consume the token and return true on success, storing the register
+   number in *REGNO.  Return false on failure.  */
 
-static int
-fix_24k_align_to (const struct mips_opcode *mo)
+static bfd_boolean
+match_reg (struct mips_arg_info *arg, enum mips_reg_operand_type type,
+          unsigned int *regno)
 {
-  if (strcmp (mo->name, "sh") == 0)
-    return 2;
-
-  if (strcmp (mo->name, "swc1") == 0
-      || strcmp (mo->name, "swc2") == 0
-      || strcmp (mo->name, "sw") == 0
-      || strcmp (mo->name, "sc") == 0
-      || strcmp (mo->name, "s.s") == 0)
-    return 4;
-
-  if (strcmp (mo->name, "sdc1") == 0
-      || strcmp (mo->name, "sdc2") == 0
-      || strcmp (mo->name, "s.d") == 0)
-    return 8;
-
-  /* sb, swl, swr */
-  return 1;
+  if (arg->token->type == OT_REG
+      && match_regno (arg, type, arg->token->u.regno, regno))
+    {
+      ++arg->token;
+      return TRUE;
+    }
+  return FALSE;
 }
 
-struct fix_24k_store_info
-  {
-    /* Immediate offset, if any, for this store instruction.  */
-    short off;
-    /* Alignment required by this store instruction.  */
-    int align_to;
-    /* True for register offsets.  */
-    int register_offset;
-  };
-
-/* Comparison function used by qsort.  */
+/* Try to interpret the next token in ARG as a range of registers of type TYPE.
+   Consume the token and return true on success, storing the register numbers
+   in *REGNO1 and *REGNO2.  Return false on failure.  */
 
-static int
-fix_24k_sort (const void *a, const void *b)
+static bfd_boolean
+match_reg_range (struct mips_arg_info *arg, enum mips_reg_operand_type type,
+                unsigned int *regno1, unsigned int *regno2)
 {
-  const struct fix_24k_store_info *pos1 = a;
-  const struct fix_24k_store_info *pos2 = b;
-
-  return (pos1->off - pos2->off);
+  if (match_reg (arg, type, regno1))
+    {
+      *regno2 = *regno1;
+      return TRUE;
+    }
+  if (arg->token->type == OT_REG_RANGE
+      && match_regno (arg, type, arg->token->u.reg_range.regno1, regno1)
+      && match_regno (arg, type, arg->token->u.reg_range.regno2, regno2)
+      && *regno1 <= *regno2)
+    {
+      ++arg->token;
+      return TRUE;
+    }
+  return FALSE;
 }
 
-/* INSN is a store instruction.  Try to record the store information
-   in STINFO.  Return false if the information isn't known.  */
+/* OP_INT matcher.  */
 
 static bfd_boolean
-fix_24k_record_store_info (struct fix_24k_store_info *stinfo,
-                          const struct mips_cl_insn *insn)
+match_int_operand (struct mips_arg_info *arg,
+                  const struct mips_operand *operand_base)
 {
-  /* The instruction must have a known offset.  */
-  if (!insn->complete_p || !strstr (insn->insn_mo->args, "o("))
-    return FALSE;
+  const struct mips_int_operand *operand;
+  unsigned int uval, mask;
+  int min_val, max_val, factor;
+  offsetT sval;
+  bfd_boolean print_hex;
+
+  operand = (const struct mips_int_operand *) operand_base;
+  factor = 1 << operand->shift;
+  mask = (1 << operand_base->size) - 1;
+  max_val = (operand->max_val + operand->bias) << operand->shift;
+  min_val = max_val - (mask << operand->shift);
+  if (arg->lax_max)
+    max_val = mask << operand->shift;
+
+  if (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))
+    {
+      /* The operand can be relocated.  */
+      if (!match_expression (arg, &offset_expr, offset_reloc))
+       return FALSE;
+
+      if (offset_reloc[0] != BFD_RELOC_UNUSED)
+       /* Relocation operators were used.  Accept the arguent and
+          leave the relocation value in offset_expr and offset_relocs
+          for the caller to process.  */
+       return TRUE;
 
-  stinfo->off = (insn->insn_opcode >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE;
-  stinfo->align_to = fix_24k_align_to (insn->insn_mo);
-  return TRUE;
-}
+      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)
+           return FALSE;
+         offset_reloc[0] = BFD_RELOC_LO16;
+         return TRUE;
+       }
 
-/* Return the number of nops that would be needed to work around the 24k
-   "lost data on stores during refill" errata if instruction INSN
-   immediately followed the 2 instructions described by HIST.
-   Ignore hazards that are contained within the first IGNORE
-   instructions of HIST.
+      /* Clear the global state; we're going to install the operand
+        ourselves.  */
+      sval = offset_expr.X_add_number;
+      offset_expr.X_op = O_absent;
+    }
+  else
+    {
+      if (!match_const_int (arg, &sval, min_val))
+       return FALSE;
+    }
 
-   Problem: The FSB (fetch store buffer) acts as an intermediate buffer
-   for the data cache refills and store data. The following describes
-   the scenario where the store data could be lost.
+  arg->last_op_int = sval;
 
-   * A data cache miss, due to either a load or a store, causing fill
-     data to be supplied by the memory subsystem
-   * The first three doublewords of fill data are returned and written
-     into the cache
-   * A sequence of four stores occurs in consecutive cycles around the
-     final doubleword of the fill:
-   * Store A
-   * Store B
-   * Store C
-   * Zero, One or more instructions
-   * Store D
+  /* 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.
 
-   The four stores A-D must be to different doublewords of the line that
-   is being filled. The fourth instruction in the sequence above permits
-   the fill of the final doubleword to be transferred from the FSB into
-   the cache. In the sequence above, the stores may be either integer
-   (sb, sh, sw, swr, swl, sc) or coprocessor (swc1/swc2, sdc1/sdc2,
-   swxc1, sdxc1, suxc1) stores, as long as the four stores are to
-   different doublewords on the line. If the floating point unit is
-   running in 1:2 mode, it is not possible to create the sequence above
-   using only floating point store instructions.
+     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 (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;
+    }
 
-   In this case, the cache line being filled is incorrectly marked
-   invalid, thereby losing the data from any store to the line that
-   occurs between the original miss and the completion of the five
-   cycle sequence shown above.
+  uval = (unsigned int) sval >> operand->shift;
+  uval -= operand->bias;
 
-   The workarounds are:
+  /* Handle -mfix-cn63xxp1.  */
+  if (arg->opnum == 1
+      && mips_fix_cn63xxp1
+      && !mips_opts.micromips
+      && strcmp ("pref", arg->insn->insn_mo->name) == 0)
+    switch (uval)
+      {
+      case 5:
+      case 25:
+      case 26:
+      case 27:
+      case 28:
+      case 29:
+      case 30:
+      case 31:
+       /* These are ok.  */
+       break;
 
-   * Run the data cache in write-through mode.
-   * Insert a non-store instruction between
-     Store A and Store B or Store B and Store C.  */
-  
-static int
-nops_for_24k (int ignore, const struct mips_cl_insn *hist,
-             const struct mips_cl_insn *insn)
+      default:
+       /* The rest must be changed to 28.  */
+       uval = 28;
+       break;
+      }
+
+  insn_insert_operand (arg->insn, operand_base, uval);
+  return TRUE;
+}
+
+/* OP_MAPPED_INT matcher.  */
+
+static bfd_boolean
+match_mapped_int_operand (struct mips_arg_info *arg,
+                         const struct mips_operand *operand_base)
 {
-  struct fix_24k_store_info pos[3];
-  int align, i, base_offset;
+  const struct mips_mapped_int_operand *operand;
+  unsigned int uval, num_vals;
+  offsetT sval;
 
-  if (ignore >= 2)
-    return 0;
+  operand = (const struct mips_mapped_int_operand *) operand_base;
+  if (!match_const_int (arg, &sval, operand->int_map[0]))
+    return FALSE;
 
-  /* If the previous instruction wasn't a store, there's nothing to
-     worry about.  */
-  if ((hist[0].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
-    return 0;
+  num_vals = 1 << operand_base->size;
+  for (uval = 0; uval < num_vals; uval++)
+    if (operand->int_map[uval] == sval)
+      break;
+  if (uval == num_vals)
+    return FALSE;
 
-  /* If the instructions after the previous one are unknown, we have
-     to assume the worst.  */
-  if (!insn)
-    return 1;
+  insn_insert_operand (arg->insn, operand_base, uval);
+  return TRUE;
+}
 
-  /* Check whether we are dealing with three consecutive stores.  */
-  if ((insn->insn_mo->pinfo & INSN_STORE_MEMORY) == 0
-      || (hist[1].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
-    return 0;
+/* OP_MSB matcher.  */
 
-  /* If we don't know the relationship between the store addresses,
-     assume the worst.  */
-  if (!BASE_REG_EQ (insn->insn_opcode, hist[0].insn_opcode)
-      || !BASE_REG_EQ (insn->insn_opcode, hist[1].insn_opcode))
-    return 1;
+static bfd_boolean
+match_msb_operand (struct mips_arg_info *arg,
+                  const struct mips_operand *operand_base)
+{
+  const struct mips_msb_operand *operand;
+  int min_val, max_val, max_high;
+  offsetT size, sval, high;
 
-  if (!fix_24k_record_store_info (&pos[0], insn)
-      || !fix_24k_record_store_info (&pos[1], &hist[0])
-      || !fix_24k_record_store_info (&pos[2], &hist[1]))
-    return 1;
+  operand = (const struct mips_msb_operand *) operand_base;
+  min_val = operand->bias;
+  max_val = min_val + (1 << operand_base->size) - 1;
+  max_high = operand->opsize;
 
-  qsort (&pos, 3, sizeof (struct fix_24k_store_info), fix_24k_sort);
+  if (!match_const_int (arg, &size, 1))
+    return FALSE;
 
-  /* Pick a value of ALIGN and X such that all offsets are adjusted by
-     X bytes and such that the base register + X is known to be aligned
-     to align bytes.  */
+  high = size + arg->last_op_int;
+  sval = operand->add_lsb ? high : size;
 
-  if (((insn->insn_opcode >> OP_SH_RS) & OP_MASK_RS) == SP)
-    align = 8;
-  else
+  if (size < 0 || high > max_high || sval < min_val || sval > max_val)
     {
-      align = pos[0].align_to;
-      base_offset = pos[0].off;
-      for (i = 1; i < 3; i++)
-       if (align < pos[i].align_to)
-         {
-           align = pos[i].align_to;
-           base_offset = pos[i].off;
-         }
-      for (i = 0; i < 3; i++)
-       pos[i].off -= base_offset;
+      if (arg->soft_match)
+       return FALSE;
+      report_bad_field (arg->last_op_int, size);
+      sval = min_val;
     }
+  insn_insert_operand (arg->insn, operand_base, sval - min_val);
+  return TRUE;
+}
 
-  pos[0].off &= ~align + 1;
-  pos[1].off &= ~align + 1;
-  pos[2].off &= ~align + 1;
+/* OP_REG matcher.  */
 
-  /* If any two stores write to the same chunk, they also write to the
-     same doubleword.  The offsets are still sorted at this point.  */
-  if (pos[0].off == pos[1].off || pos[1].off == pos[2].off)
-    return 0;
+static bfd_boolean
+match_reg_operand (struct mips_arg_info *arg,
+                  const struct mips_operand *operand_base)
+{
+  const struct mips_reg_operand *operand;
+  unsigned int regno, uval, num_vals;
 
-  /* A range of at least 9 bytes is needed for the stores to be in
-     non-overlapping doublewords.  */
-  if (pos[2].off - pos[0].off <= 8)
-    return 0;
+  operand = (const struct mips_reg_operand *) operand_base;
+  if (!match_reg (arg, operand->reg_type, &regno))
+    return FALSE;
 
-  if (pos[2].off - pos[1].off >= 24
-      || pos[1].off - pos[0].off >= 24
-      || pos[2].off - pos[0].off >= 32)
-    return 0;
+  if (operand->reg_map)
+    {
+      num_vals = 1 << operand->root.size;
+      for (uval = 0; uval < num_vals; uval++)
+       if (operand->reg_map[uval] == regno)
+         break;
+      if (num_vals == uval)
+       return FALSE;
+    }
+  else
+    uval = regno;
 
-  return 1;
+  arg->last_regno = regno;
+  if (arg->opnum == 1)
+    arg->dest_regno = regno;
+  insn_insert_operand (arg->insn, operand_base, uval);
+  return TRUE;
 }
 
-/* Return the number of nops that would be needed if instruction INSN
-   immediately followed the MAX_NOPS instructions given by HIST,
-   where HIST[0] is the most recent instruction.  Ignore hazards
-   between INSN and the first IGNORE instructions in HIST.
+/* OP_REG_PAIR matcher.  */
 
-   If INSN is null, return the worse-case number of nops for any
-   instruction.  */
+static bfd_boolean
+match_reg_pair_operand (struct mips_arg_info *arg,
+                       const struct mips_operand *operand_base)
+{
+  const struct mips_reg_pair_operand *operand;
+  unsigned int regno1, regno2, uval, num_vals;
 
-static int
-nops_for_insn (int ignore, const struct mips_cl_insn *hist,
-              const struct mips_cl_insn *insn)
+  operand = (const struct mips_reg_pair_operand *) operand_base;
+  if (!match_reg (arg, operand->reg_type, &regno1)
+      || !match_char (arg, ',')
+      || !match_reg (arg, operand->reg_type, &regno2))
+    return FALSE;
+
+  num_vals = 1 << operand_base->size;
+  for (uval = 0; uval < num_vals; uval++)
+    if (operand->reg1_map[uval] == regno1 && operand->reg2_map[uval] == regno2)
+      break;
+  if (uval == num_vals)
+    return FALSE;
+
+  insn_insert_operand (arg->insn, operand_base, uval);
+  return TRUE;
+}
+
+/* OP_PCREL matcher.  The caller chooses the relocation type.  */
+
+static bfd_boolean
+match_pcrel_operand (struct mips_arg_info *arg)
 {
-  int i, nops, tmp_nops;
+  bfd_reloc_code_real_type r[3];
 
-  nops = 0;
-  for (i = ignore; i < MAX_DELAY_NOPS; i++)
-    {
-      tmp_nops = insns_between (hist + i, insn) - i;
-      if (tmp_nops > nops)
-       nops = tmp_nops;
-    }
+  return match_expression (arg, &offset_expr, r) && r[0] == BFD_RELOC_UNUSED;
+}
 
-  if (mips_fix_vr4130 && !mips_opts.micromips)
-    {
-      tmp_nops = nops_for_vr4130 (ignore, hist, insn);
-      if (tmp_nops > nops)
-       nops = tmp_nops;
-    }
+/* OP_PERF_REG matcher.  */
 
-  if (mips_fix_24k && !mips_opts.micromips)
+static bfd_boolean
+match_perf_reg_operand (struct mips_arg_info *arg,
+                       const struct mips_operand *operand)
+{
+  offsetT sval;
+
+  if (!match_const_int (arg, &sval, 0))
+    return FALSE;
+
+  if (sval != 0
+      && (sval != 1
+         || (mips_opts.arch == CPU_R5900
+             && (strcmp (arg->insn->insn_mo->name, "mfps") == 0
+                 || strcmp (arg->insn->insn_mo->name, "mtps") == 0))))
     {
-      tmp_nops = nops_for_24k (ignore, hist, insn);
-      if (tmp_nops > nops)
-       nops = tmp_nops;
+      if (arg->soft_match)
+       return FALSE;
+      as_bad (_("Invalid performance register (%ld)"), (unsigned long) sval);
     }
 
-  return nops;
+  insn_insert_operand (arg->insn, operand, sval);
+  return TRUE;
 }
 
-/* The variable arguments provide NUM_INSNS extra instructions that
-   might be added to HIST.  Return the largest number of nops that
-   would be needed after the extended sequence, ignoring hazards
-   in the first IGNORE instructions.  */
+/* OP_ADDIUSP matcher.  */
 
-static int
-nops_for_sequence (int num_insns, int ignore,
-                  const struct mips_cl_insn *hist, ...)
+static bfd_boolean
+match_addiusp_operand (struct mips_arg_info *arg,
+                      const struct mips_operand *operand)
 {
-  va_list args;
-  struct mips_cl_insn buffer[MAX_NOPS];
-  struct mips_cl_insn *cursor;
-  int nops;
+  offsetT sval;
+  unsigned int uval;
 
-  va_start (args, hist);
-  cursor = buffer + num_insns;
-  memcpy (cursor, hist, (MAX_NOPS - num_insns) * sizeof (*cursor));
-  while (cursor > buffer)
-    *--cursor = *va_arg (args, const struct mips_cl_insn *);
+  if (!match_const_int (arg, &sval, -256))
+    return FALSE;
 
-  nops = nops_for_insn (ignore, buffer, NULL);
-  va_end (args);
-  return nops;
+  if (sval % 4)
+    return FALSE;
+
+  sval /= 4;
+  if (!(sval >= -258 && sval <= 257) || (sval >= -2 && sval <= 1))
+    return FALSE;
+
+  uval = (unsigned int) sval;
+  uval = ((uval >> 1) & ~0xff) | (uval & 0xff);
+  insn_insert_operand (arg->insn, operand, uval);
+  return TRUE;
 }
 
-/* Like nops_for_insn, but if INSN is a branch, take into account the
-   worst-case delay for the branch target.  */
+/* OP_CLO_CLZ_DEST matcher.  */
 
-static int
-nops_for_insn_or_target (int ignore, const struct mips_cl_insn *hist,
-                        const struct mips_cl_insn *insn)
+static bfd_boolean
+match_clo_clz_dest_operand (struct mips_arg_info *arg,
+                           const struct mips_operand *operand)
 {
-  int nops, tmp_nops;
+  unsigned int regno;
 
-  nops = nops_for_insn (ignore, hist, insn);
-  if (delayed_branch_p (insn))
-    {
-      tmp_nops = nops_for_sequence (2, ignore ? ignore + 2 : 0,
-                                   hist, insn, get_delay_slot_nop (insn));
-      if (tmp_nops > nops)
-       nops = tmp_nops;
-    }
-  else if (compact_branch_p (insn))
-    {
-      tmp_nops = nops_for_sequence (1, ignore ? ignore + 1 : 0, hist, insn);
-      if (tmp_nops > nops)
-       nops = tmp_nops;
-    }
-  return nops;
+  if (!match_reg (arg, OP_REG_GP, &regno))
+    return FALSE;
+
+  insn_insert_operand (arg->insn, operand, regno | (regno << 5));
+  return TRUE;
 }
 
-/* Fix NOP issue: Replace nops by "or at,at,zero".  */
+/* OP_LWM_SWM_LIST matcher.  */
 
-static void
-fix_loongson2f_nop (struct mips_cl_insn * ip)
+static bfd_boolean
+match_lwm_swm_list_operand (struct mips_arg_info *arg,
+                           const struct mips_operand *operand)
 {
-  gas_assert (!HAVE_CODE_COMPRESSION);
-  if (strcmp (ip->insn_mo->name, "nop") == 0)
-    ip->insn_opcode = LOONGSON2F_NOP_INSN;
-}
+  unsigned int reglist, sregs, ra, regno1, regno2;
+  struct mips_arg_info reset;
 
-/* Fix Jump Issue: Eliminate instruction fetch from outside 256M region
-                   jr target pc &= 'hffff_ffff_cfff_ffff.  */
+  reglist = 0;
+  if (!match_reg_range (arg, OP_REG_GP, &regno1, &regno2))
+    return FALSE;
+  do
+    {
+      if (regno2 == FP && regno1 >= S0 && regno1 <= S7)
+       {
+         reglist |= 1 << FP;
+         regno2 = S7;
+       }
+      reglist |= ((1U << regno2 << 1) - 1) & -(1U << regno1);
+      reset = *arg;
+    }
+  while (match_char (arg, ',')
+        && match_reg_range (arg, OP_REG_GP, &regno1, &regno2));
+  *arg = reset;
 
-static void
-fix_loongson2f_jump (struct mips_cl_insn * ip)
-{
-  gas_assert (!HAVE_CODE_COMPRESSION);
-  if (strcmp (ip->insn_mo->name, "j") == 0
-      || strcmp (ip->insn_mo->name, "jr") == 0
-      || strcmp (ip->insn_mo->name, "jalr") == 0)
+  if (operand->size == 2)
     {
-      int sreg;
-      expressionS ep;
+      /* The list must include both ra and s0-sN, for 0 <= N <= 3.  E.g.:
 
-      if (! mips_opts.at)
-        return;
+        s0, ra
+        s0, s1, ra, s2, s3
+        s0-s2, ra
 
-      sreg = EXTRACT_OPERAND (0, RS, *ip);
-      if (sreg == ZERO || sreg == KT0 || sreg == KT1 || sreg == ATREG)
-        return;
+        and any permutations of these.  */
+      if ((reglist & 0xfff1ffff) != 0x80010000)
+       return FALSE;
 
-      ep.X_op = O_constant;
-      ep.X_add_number = 0xcfff0000;
-      macro_build (&ep, "lui", "t,u", ATREG, BFD_RELOC_HI16);
-      ep.X_add_number = 0xffff;
-      macro_build (&ep, "ori", "t,r,i", ATREG, ATREG, BFD_RELOC_LO16);
-      macro_build (NULL, "and", "d,v,t", sreg, sreg, ATREG);
+      sregs = (reglist >> 17) & 7;
+      ra = 0;
+    }
+  else
+    {
+      /* The list must include at least one of ra and s0-sN,
+        for 0 <= N <= 8.  (Note that there is a gap between s7 and s8,
+        which are $23 and $30 respectively.)  E.g.:
+
+        ra
+        s0
+        ra, s0, s1, s2
+        s0-s8
+        s0-s5, ra
+
+        and any permutations of these.  */
+      if ((reglist & 0x3f00ffff) != 0)
+       return FALSE;
+
+      ra = (reglist >> 27) & 0x10;
+      sregs = ((reglist >> 22) & 0x100) | ((reglist >> 16) & 0xff);
     }
+  sregs += 1;
+  if ((sregs & -sregs) != sregs)
+    return FALSE;
+
+  insn_insert_operand (arg->insn, operand, (ffs (sregs) - 1) | ra);
+  return TRUE;
 }
 
-static void
-fix_loongson2f (struct mips_cl_insn * ip)
+/* OP_ENTRY_EXIT_LIST matcher.  */
+
+static unsigned int
+match_entry_exit_operand (struct mips_arg_info *arg,
+                         const struct mips_operand *operand)
 {
-  if (mips_fix_loongson2f_nop)
-    fix_loongson2f_nop (ip);
+  unsigned int mask;
+  bfd_boolean is_exit;
 
-  if (mips_fix_loongson2f_jump)
-    fix_loongson2f_jump (ip);
+  /* The format is the same for both ENTRY and EXIT, but the constraints
+     are different.  */
+  is_exit = strcmp (arg->insn->insn_mo->name, "exit") == 0;
+  mask = (is_exit ? 7 << 3 : 0);
+  do
+    {
+      unsigned int regno1, regno2;
+      bfd_boolean is_freg;
+
+      if (match_reg_range (arg, OP_REG_GP, &regno1, &regno2))
+       is_freg = FALSE;
+      else if (match_reg_range (arg, OP_REG_FP, &regno1, &regno2))
+       is_freg = TRUE;
+      else
+       return FALSE;
+
+      if (is_exit && is_freg && regno1 == 0 && regno2 < 2)
+       {
+         mask &= ~(7 << 3);
+         mask |= (5 + regno2) << 3;
+       }
+      else if (!is_exit && regno1 == 4 && regno2 >= 4 && regno2 <= 7)
+       mask |= (regno2 - 3) << 3;
+      else if (regno1 == 16 && regno2 >= 16 && regno2 <= 17)
+       mask |= (regno2 - 15) << 1;
+      else if (regno1 == RA && regno2 == RA)
+       mask |= 1;
+      else
+       return FALSE;
+    }
+  while (match_char (arg, ','));
+
+  insn_insert_operand (arg->insn, operand, mask);
+  return TRUE;
 }
 
-/* IP is a branch that has a delay slot, and we need to fill it
-   automatically.   Return true if we can do that by swapping IP
-   with the previous instruction.
-   ADDRESS_EXPR is an operand of the instruction to be used with
-   RELOC_TYPE.  */
+/* OP_SAVE_RESTORE_LIST matcher.  */
 
 static bfd_boolean
-can_swap_branch_p (struct mips_cl_insn *ip, expressionS *address_expr,
-  bfd_reloc_code_real_type *reloc_type)
+match_save_restore_list_operand (struct mips_arg_info *arg)
 {
-  unsigned long pinfo, pinfo2, prev_pinfo, prev_pinfo2;
-  unsigned int gpr_read, gpr_write, prev_gpr_read, prev_gpr_write;
+  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;
+  args = 0;
+  statics = 0;
+  sregs = 0;
+  do
+    {
+      unsigned int regno1, regno2;
 
-  /* -O2 and above is required for this optimization.  */
-  if (mips_optimize < 2)
-    return FALSE;
+      if (arg->token->type == OT_INTEGER)
+       {
+         /* Handle the frame size.  */
+         if (!match_const_int (arg, &frame_size, 0))
+           return FALSE;
+         num_frame_sizes += 1;
+       }
+      else
+       {
+         if (!match_reg_range (arg, OP_REG_GP, &regno1, &regno2))
+           return FALSE;
 
-  /* If we have seen .set volatile or .set nomove, don't optimize.  */
-  if (mips_opts.nomove)
-    return FALSE;
+         while (regno1 <= regno2)
+           {
+             if (regno1 >= 4 && regno1 <= 7)
+               {
+                 if (num_frame_sizes == 0)
+                   /* args $a0-$a3 */
+                   args |= 1 << (regno1 - 4);
+                 else
+                   /* statics $a0-$a3 */
+                   statics |= 1 << (regno1 - 4);
+               }
+             else if (regno1 >= 16 && regno1 <= 23)
+               /* $s0-$s7 */
+               sregs |= 1 << (regno1 - 16);
+             else if (regno1 == 30)
+               /* $s8 */
+               sregs |= 1 << 8;
+             else if (regno1 == 31)
+               /* Add $ra to insn.  */
+               opcode |= 0x40;
+             else
+               return FALSE;
+             regno1 += 1;
+             if (regno1 == 24)
+               regno1 = 30;
+           }
+       }
+    }
+  while (match_char (arg, ','));
 
-  /* We can't swap if the previous instruction's position is fixed.  */
-  if (history[0].fixed_p)
+  /* Encode args/statics combination.  */
+  if (args & statics)
     return FALSE;
+  else if (args == 0xf)
+    /* All $a0-$a3 are args.  */
+    opcode |= MIPS16_ALL_ARGS << 16;
+  else if (statics == 0xf)
+    /* All $a0-$a3 are statics.  */
+    opcode |= MIPS16_ALL_STATICS << 16;
+  else
+    {
+      /* Count arg registers.  */
+      num_args = 0;
+      while (args & 0x1)
+       {
+         args >>= 1;
+         num_args += 1;
+       }
+      if (args != 0)
+       return FALSE;
 
-  /* If the previous previous insn was in a .set noreorder, we can't
-     swap.  Actually, the MIPS assembler will swap in this situation.
-     However, gcc configured -with-gnu-as will generate code like
+      /* Count static registers.  */
+      num_statics = 0;
+      while (statics & 0x8)
+       {
+         statics = (statics << 1) & 0xf;
+         num_statics += 1;
+       }
+      if (statics != 0)
+       return FALSE;
 
-       .set    noreorder
-       lw      $4,XXX
-       .set    reorder
-       INSN
-       bne     $4,$0,foo
+      /* Encode args/statics.  */
+      opcode |= ((num_args << 2) | num_statics) << 16;
+    }
 
-     in which we can not swap the bne and INSN.  If gcc is not configured
-     -with-gnu-as, it does not output the .set pseudo-ops.  */
-  if (history[1].noreorder_p)
-    return FALSE;
+  /* Encode $s0/$s1.  */
+  if (sregs & (1 << 0))                /* $s0 */
+    opcode |= 0x20;
+  if (sregs & (1 << 1))                /* $s1 */
+    opcode |= 0x10;
+  sregs >>= 2;
 
-  /* If the previous instruction had a fixup in mips16 mode, we can not swap.
-     This means that the previous instruction was a 4-byte one anyhow.  */
-  if (mips_opts.mips16 && history[0].fixp[0])
+  /* Encode $s2-$s8. */
+  num_sregs = 0;
+  while (sregs & 1)
+    {
+      sregs >>= 1;
+      num_sregs += 1;
+    }
+  if (sregs != 0)
     return FALSE;
+  opcode |= num_sregs << 24;
+
+  /* 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)
+    {
+      frame_size /= 8;
+      opcode |= (((frame_size & 0xf0) << 16)
+                | (frame_size & 0x0f));
+    }
 
-  /* If the branch is itself the target of a branch, we can not swap.
-     We cheat on this; all we check for is whether there is a label on
-     this instruction.  If there are any branches to anything other than
-     a label, users must use .set noreorder.  */
-  if (seg_info (now_seg)->label_list)
-    return FALSE;
+  if (error)
+    {
+      if (arg->soft_match)
+       return FALSE;
+      as_bad ("%s", error);
+    }
 
-  /* If the previous instruction is in a variant frag other than this
-     branch's one, we cannot do the swap.  This does not apply to
-     MIPS16 code, which uses variant frags for different purposes.  */
-  if (!mips_opts.mips16
-      && history[0].frag
-      && history[0].frag->fr_type == rs_machine_dependent)
-    return FALSE;
+  /* Finally build the instruction.  */
+  if ((opcode >> 16) != 0 || frame_size == 0)
+    opcode |= MIPS16_EXTEND;
+  arg->insn->insn_opcode = opcode;
+  return TRUE;
+}
 
-  /* We do not swap with instructions that cannot architecturally
-     be placed in a branch delay slot, such as SYNC or ERET.  We
-     also refrain from swapping with a trap instruction, since it
-     complicates trap handlers to have the trap instruction be in
-     a delay slot.  */
-  prev_pinfo = history[0].insn_mo->pinfo;
-  if (prev_pinfo & INSN_NO_DELAY_SLOT)
-    return FALSE;
+/* OP_MDMX_IMM_REG matcher.  */
 
-  /* Check for conflicts between the branch and the instructions
-     before the candidate delay slot.  */
-  if (nops_for_insn (0, history + 1, ip) > 0)
-    return FALSE;
+static bfd_boolean
+match_mdmx_imm_reg_operand (struct mips_arg_info *arg,
+                           const struct mips_operand *operand)
+{
+  unsigned int regno, uval;
+  bfd_boolean is_qh;
+  const struct mips_opcode *opcode;
 
-  /* Check for conflicts between the swapped sequence and the
-     target of the branch.  */
-  if (nops_for_sequence (2, 0, history + 1, ip, history) > 0)
-    return FALSE;
+  /* The mips_opcode records whether this is an octobyte or quadhalf
+     instruction.  Start out with that bit in place.  */
+  opcode = arg->insn->insn_mo;
+  uval = mips_extract_operand (operand, opcode->match);
+  is_qh = (uval != 0);
 
-  /* If the branch reads a register that the previous
-     instruction sets, we can not swap.  */
-  gpr_read = gpr_read_mask (ip);
-  prev_gpr_write = gpr_write_mask (&history[0]);
-  if (gpr_read & prev_gpr_write)
-    return FALSE;
+  if (arg->token->type == OT_REG || arg->token->type == OT_REG_ELEMENT)
+    {
+      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);
+       }
 
-  /* If the branch writes a register that the previous
-     instruction sets, we can not swap.  */
-  gpr_write = gpr_write_mask (ip);
-  if (gpr_write & prev_gpr_write)
-    return FALSE;
+      /* Check whether this is a vector register or a broadcast of
+        a single element.  */
+      if (arg->token->type == OT_REG_ELEMENT)
+       {
+         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->soft_match)
+               return FALSE;
+             as_bad (_("Invalid element selector"));
+           }
+         else
+           uval |= arg->token->u.reg_element.index << (is_qh ? 2 : 1) << 5;
+       }
+      else
+       {
+         /* A full vector.  */
+         if ((opcode->membership & INSN_5400)
+             && (strcmp (opcode->name, "sll.ob") == 0
+                 || strcmp (opcode->name, "srl.ob") == 0))
+           {
+             if (arg->soft_match)
+               return FALSE;
+             as_bad (_("Operand %d of `%s' must be scalar"),
+                     arg->argnum, opcode->name);
+           }
 
-  /* If the branch writes a register that the previous
-     instruction reads, we can not swap.  */
-  prev_gpr_read = gpr_read_mask (&history[0]);
-  if (gpr_write & prev_gpr_read)
-    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 one instruction sets a condition code and the
-     other one uses a condition code, we can not swap.  */
-  pinfo = ip->insn_mo->pinfo;
-  if ((pinfo & INSN_READ_COND_CODE)
-      && (prev_pinfo & INSN_WRITE_COND_CODE))
-    return FALSE;
-  if ((pinfo & INSN_WRITE_COND_CODE)
-      && (prev_pinfo & INSN_READ_COND_CODE))
-    return FALSE;
+      if (!match_const_int (arg, &sval, 0))
+       return FALSE;
+      if (sval < 0 || sval > 31)
+       {
+         if (arg->soft_match)
+           return FALSE;
+         report_bad_range (arg->insn, arg->argnum, sval, 0, 31, FALSE);
+       }
+      uval |= (sval & 31);
+      if (is_qh)
+       uval |= MDMX_FMTSEL_IMM_QH << 5;
+      else
+       uval |= MDMX_FMTSEL_IMM_OB << 5;
+    }
+  insn_insert_operand (arg->insn, operand, uval);
+  return TRUE;
+}
 
-  /* If the previous instruction uses the PC, we can not swap.  */
-  prev_pinfo2 = history[0].insn_mo->pinfo2;
-  if (mips_opts.mips16 && (prev_pinfo & MIPS16_INSN_READ_PC))
-    return FALSE;
-  if (mips_opts.micromips && (prev_pinfo2 & INSN2_READ_PC))
-    return FALSE;
+/* OP_PC matcher.  */
 
-  /* If the previous instruction has an incorrect size for a fixed
-     branch delay slot in microMIPS mode, we cannot swap.  */
-  pinfo2 = ip->insn_mo->pinfo2;
-  if (mips_opts.micromips
-      && (pinfo2 & INSN2_BRANCH_DELAY_16BIT)
-      && insn_length (history) != 2)
-    return FALSE;
-  if (mips_opts.micromips
-      && (pinfo2 & INSN2_BRANCH_DELAY_32BIT)
-      && insn_length (history) != 4)
-    return FALSE;
-
-  /* On R5900 short loops need to be fixed by inserting a nop in
-     the branch delay slots.
-     A short loop can be terminated too early.  */
-  if (mips_opts.arch == CPU_R5900
-      /* Check if instruction has a parameter, ignore "j $31". */
-      && (address_expr != NULL)
-      /* Parameter must be 16 bit. */
-      && (*reloc_type == BFD_RELOC_16_PCREL_S2)
-      /* Branch to same segment. */
-      && (S_GET_SEGMENT(address_expr->X_add_symbol) == now_seg)
-      /* Branch to same code fragment. */
-      && (symbol_get_frag(address_expr->X_add_symbol) == frag_now)
-      /* Can only calculate branch offset if value is known. */
-      && symbol_constant_p(address_expr->X_add_symbol)
-      /* Check if branch is really conditional. */
-      && !((ip->insn_opcode & 0xffff0000) == 0x10000000   /* beq $0,$0 */
-       || (ip->insn_opcode & 0xffff0000) == 0x04010000   /* bgez $0 */
-       || (ip->insn_opcode & 0xffff0000) == 0x04110000)) /* bgezal $0 */
+static bfd_boolean
+match_pc_operand (struct mips_arg_info *arg)
+{
+  if (arg->token->type == OT_REG && (arg->token->u.regno & RTYPE_PC))
     {
-      int distance;
-      /* Check if loop is shorter than 6 instructions including
-         branch and delay slot.  */
-      distance = frag_now_fix() - S_GET_VALUE(address_expr->X_add_symbol);
-      if (distance <= 20)
-        {
-          int i;
-          int rv;
-
-          rv = FALSE;
-          /* When the loop includes branches or jumps,
-             it is not a short loop. */
-          for (i = 0; i < (distance / 4); i++)
-            {
-              if ((history[i].cleared_p)
-                  || delayed_branch_p(&history[i]))
-                {
-                  rv = TRUE;
-                  break;
-                }
-            }
-          if (rv == FALSE)
-            {
-              /* Insert nop after branch to fix short loop. */
-              return FALSE;
-            }
-        }
+      ++arg->token;
+      return TRUE;
     }
-
-  return TRUE;
+  return FALSE;
 }
 
-/* Decide how we should add IP to the instruction stream.
-   ADDRESS_EXPR is an operand of the instruction to be used with
-   RELOC_TYPE.  */
+/* OP_REPEAT_DEST_REG and OP_REPEAT_PREV_REG matcher.  OTHER_REGNO is the
+   register that we need to match.  */
 
-static enum append_method
-get_append_method (struct mips_cl_insn *ip, expressionS *address_expr,
-  bfd_reloc_code_real_type *reloc_type)
+static bfd_boolean
+match_tied_reg_operand (struct mips_arg_info *arg, unsigned int other_regno)
 {
-  unsigned long pinfo;
+  unsigned int regno;
 
-  /* The relaxed version of a macro sequence must be inherently
-     hazard-free.  */
-  if (mips_relax.sequence == 2)
-    return APPEND_ADD;
+  return match_reg (arg, OP_REG_GP, &regno) && regno == other_regno;
+}
 
-  /* We must not dabble with instructions in a ".set norerorder" block.  */
-  if (mips_opts.noreorder)
-    return APPEND_ADD;
+/* Read a floating-point constant from S for LI.S or LI.D.  LENGTH is
+   the length of the value in bytes (4 for float, 8 for double) and
+   USING_GPRS says whether the destination is a GPR rather than an FPR.
 
-  /* Otherwise, it's our responsibility to fill branch delay slots.  */
-  if (delayed_branch_p (ip))
-    {
-      if (!branch_likely_p (ip)
-         && can_swap_branch_p (ip, address_expr, reloc_type))
-       return APPEND_SWAP;
+   Return the constant in IMM and OFFSET as follows:
 
-      pinfo = ip->insn_mo->pinfo;
-      if (mips_opts.mips16
-         && ISA_SUPPORTS_MIPS16E
-         && (pinfo & (MIPS16_INSN_READ_X | MIPS16_INSN_READ_31)))
-       return APPEND_ADD_COMPACT;
+   - If the constant should be loaded via memory, set IMM to O_absent and
+     OFFSET to the memory address.
 
-      return APPEND_ADD_WITH_NOP;
-    }
+   - Otherwise, if the constant should be loaded into two 32-bit registers,
+     set IMM to the O_constant to load into the high register and OFFSET
+     to the corresponding value for the low register.
 
-  return APPEND_ADD;
-}
+   - Otherwise, set IMM to the full O_constant and set OFFSET to O_absent.
 
-/* IP is a MIPS16 instruction whose opcode we have just changed.
-   Point IP->insn_mo to the new opcode's definition.  */
+   These constants only appear as the last operand in an instruction,
+   and every instruction that accepts them in any variant accepts them
+   in all variants.  This means we don't have to worry about backing out
+   any changes if the instruction does not match.  We just match
+   unconditionally and report an error if the constant is invalid.  */
 
-static void
-find_altered_mips16_opcode (struct mips_cl_insn *ip)
+static bfd_boolean
+match_float_constant (struct mips_arg_info *arg, expressionS *imm,
+                     expressionS *offset, int length, bfd_boolean using_gprs)
 {
-  const struct mips_opcode *mo, *end;
-
-  end = &mips16_opcodes[bfd_mips16_num_opcodes];
-  for (mo = ip->insn_mo; mo < end; mo++)
-    if ((ip->insn_opcode & mo->mask) == mo->match)
-      {
-       ip->insn_mo = mo;
-       return;
-      }
-  abort ();
-}
+  char *p;
+  segT seg, new_seg;
+  subsegT subseg;
+  const char *newname;
+  unsigned char *data;
+
+  /* Where the constant is placed is based on how the MIPS assembler
+     does things:
+
+     length == 4 && using_gprs  -- immediate value only
+     length == 8 && using_gprs  -- .rdata or immediate value
+     length == 4 && !using_gprs -- .lit4 or immediate value
+     length == 8 && !using_gprs -- .lit8 or immediate value
+
+     The .lit4 and .lit8 sections are only used if permitted by the
+     -G argument.  */
+  if (arg->token->type != OT_FLOAT)
+    return FALSE;
 
-/* For microMIPS macros, we need to generate a local number label
-   as the target of branches.  */
-#define MICROMIPS_LABEL_CHAR           '\037'
-static unsigned long micromips_target_label;
-static char micromips_target_name[32];
+  gas_assert (arg->token->u.flt.length == length);
+  data = arg->token->u.flt.data;
+  ++arg->token;
 
-static char *
-micromips_label_name (void)
-{
-  char *p = micromips_target_name;
-  char symbol_name_temporary[24];
-  unsigned long l;
-  int i;
+  /* Handle 32-bit constants for which an immediate value is best.  */
+  if (length == 4
+      && (using_gprs
+         || g_switch_value < 4
+         || (data[0] == 0 && data[1] == 0)
+         || (data[2] == 0 && data[3] == 0)))
+    {
+      imm->X_op = O_constant;
+      if (!target_big_endian)
+       imm->X_add_number = bfd_getl32 (data);
+      else
+       imm->X_add_number = bfd_getb32 (data);
+      offset->X_op = O_absent;
+      return TRUE;
+    }
 
-  if (*p)
-    return p;
+  /* 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))
+      && ((data[0] == 0 && data[1] == 0)
+         || (data[2] == 0 && data[3] == 0))
+      && ((data[4] == 0 && data[5] == 0)
+         || (data[6] == 0 && data[7] == 0)))
+    {
+      /* The value is simple enough to load with a couple of instructions.
+        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)
+       {
+         imm->X_op = O_constant;
+         offset->X_op = O_constant;
+         if (!target_big_endian)
+           {
+             imm->X_add_number = bfd_getl32 (data + 4);
+             offset->X_add_number = bfd_getl32 (data);
+           }
+         else
+           {
+             imm->X_add_number = bfd_getb32 (data);
+             offset->X_add_number = bfd_getb32 (data + 4);
+           }
+         if (offset->X_add_number == 0)
+           offset->X_op = O_absent;
+       }
+      else
+       {
+         imm->X_op = O_constant;
+         if (!target_big_endian)
+           imm->X_add_number = bfd_getl64 (data);
+         else
+           imm->X_add_number = bfd_getb64 (data);
+         offset->X_op = O_absent;
+       }
+      return TRUE;
+    }
 
-  i = 0;
-  l = micromips_target_label;
-#ifdef LOCAL_LABEL_PREFIX
-  *p++ = LOCAL_LABEL_PREFIX;
-#endif
-  *p++ = 'L';
-  *p++ = MICROMIPS_LABEL_CHAR;
-  do
+  /* Switch to the right section.  */
+  seg = now_seg;
+  subseg = now_subseg;
+  if (length == 4)
     {
-      symbol_name_temporary[i++] = l % 10 + '0';
-      l /= 10;
+      gas_assert (!using_gprs && g_switch_value >= 4);
+      newname = ".lit4";
+    }
+  else
+    {
+      if (using_gprs || g_switch_value < 8)
+       newname = RDATA_SECTION_NAME;
+      else
+       newname = ".lit8";
     }
-  while (l != 0);
-  while (i > 0)
-    *p++ = symbol_name_temporary[--i];
-  *p = '\0';
 
-  return micromips_target_name;
+  new_seg = subseg_new (newname, (subsegT) 0);
+  bfd_set_section_flags (stdoutput, new_seg,
+                        SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA);
+  frag_align (length == 4 ? 2 : 3, 0, 0);
+  if (strncmp (TARGET_OS, "elf", 3) != 0)
+    record_alignment (new_seg, 4);
+  else
+    record_alignment (new_seg, length == 4 ? 2 : 3);
+  if (seg == now_seg)
+    as_bad (_("Can't use floating point insn in this section"));
+
+  /* Set the argument to the current address in the section.  */
+  imm->X_op = O_absent;
+  offset->X_op = O_symbol;
+  offset->X_add_symbol = symbol_temp_new_now ();
+  offset->X_add_number = 0;
+
+  /* Put the floating point number into the section.  */
+  p = frag_more (length);
+  memcpy (p, data, length);
+
+  /* Switch back to the original section.  */
+  subseg_set (seg, subseg);
+  return TRUE;
 }
 
-static void
-micromips_label_expr (expressionS *label_expr)
-{
-  label_expr->X_op = O_symbol;
-  label_expr->X_add_symbol = symbol_find_or_make (micromips_label_name ());
-  label_expr->X_add_number = 0;
-}
+/* S is the text seen for ARG.  Match it against OPERAND.  Return the end
+   of the argument text if the match is successful, otherwise return null.  */
 
-static void
-micromips_label_inc (void)
+static bfd_boolean
+match_operand (struct mips_arg_info *arg,
+              const struct mips_operand *operand)
 {
-  micromips_target_label++;
-  *micromips_target_name = '\0';
-}
+  switch (operand->type)
+    {
+    case OP_INT:
+      return match_int_operand (arg, operand);
 
-static void
-micromips_add_label (void)
-{
-  symbolS *s;
+    case OP_MAPPED_INT:
+      return match_mapped_int_operand (arg, operand);
 
-  s = colon (micromips_label_name ());
-  micromips_label_inc ();
-#if defined(OBJ_ELF) || defined(OBJ_MAYBE_ELF)
-  if (IS_ELF)
-    S_SET_OTHER (s, ELF_ST_SET_MICROMIPS (S_GET_OTHER (s)));
-#else
-  (void) s;
-#endif
+    case OP_MSB:
+      return match_msb_operand (arg, operand);
+
+    case OP_REG:
+      return match_reg_operand (arg, operand);
+
+    case OP_REG_PAIR:
+      return match_reg_pair_operand (arg, operand);
+
+    case OP_PCREL:
+      return match_pcrel_operand (arg);
+
+    case OP_PERF_REG:
+      return match_perf_reg_operand (arg, operand);
+
+    case OP_ADDIUSP_INT:
+      return match_addiusp_operand (arg, operand);
+
+    case OP_CLO_CLZ_DEST:
+      return match_clo_clz_dest_operand (arg, operand);
+
+    case OP_LWM_SWM_LIST:
+      return match_lwm_swm_list_operand (arg, operand);
+
+    case OP_ENTRY_EXIT_LIST:
+      return match_entry_exit_operand (arg, operand);
+
+    case OP_SAVE_RESTORE_LIST:
+      return match_save_restore_list_operand (arg);
+
+    case OP_MDMX_IMM_REG:
+      return match_mdmx_imm_reg_operand (arg, operand);
+
+    case OP_REPEAT_DEST_REG:
+      return match_tied_reg_operand (arg, arg->dest_regno);
+
+    case OP_REPEAT_PREV_REG:
+      return match_tied_reg_operand (arg, arg->last_regno);
+
+    case OP_PC:
+      return match_pc_operand (arg);
+    }
+  abort ();
 }
 
-/* If assembling microMIPS code, then return the microMIPS reloc
-   corresponding to the requested one if any.  Otherwise return
-   the reloc unchanged.  */
+/* ARG is the state after successfully matching an instruction.
+   Issue any queued-up warnings.  */
 
-static bfd_reloc_code_real_type
-micromips_map_reloc (bfd_reloc_code_real_type reloc)
+static void
+check_completed_insn (struct mips_arg_info *arg)
 {
-  static const bfd_reloc_code_real_type relocs[][2] =
-    {
-      /* Keep sorted incrementally by the left-hand key.  */
-      { BFD_RELOC_16_PCREL_S2, BFD_RELOC_MICROMIPS_16_PCREL_S1 },
-      { BFD_RELOC_GPREL16, BFD_RELOC_MICROMIPS_GPREL16 },
-      { BFD_RELOC_MIPS_JMP, BFD_RELOC_MICROMIPS_JMP },
-      { BFD_RELOC_HI16, BFD_RELOC_MICROMIPS_HI16 },
-      { BFD_RELOC_HI16_S, BFD_RELOC_MICROMIPS_HI16_S },
-      { BFD_RELOC_LO16, BFD_RELOC_MICROMIPS_LO16 },
-      { BFD_RELOC_MIPS_LITERAL, BFD_RELOC_MICROMIPS_LITERAL },
-      { BFD_RELOC_MIPS_GOT16, BFD_RELOC_MICROMIPS_GOT16 },
-      { BFD_RELOC_MIPS_CALL16, BFD_RELOC_MICROMIPS_CALL16 },
-      { BFD_RELOC_MIPS_GOT_HI16, BFD_RELOC_MICROMIPS_GOT_HI16 },
-      { BFD_RELOC_MIPS_GOT_LO16, BFD_RELOC_MICROMIPS_GOT_LO16 },
-      { BFD_RELOC_MIPS_CALL_HI16, BFD_RELOC_MICROMIPS_CALL_HI16 },
-      { BFD_RELOC_MIPS_CALL_LO16, BFD_RELOC_MICROMIPS_CALL_LO16 },
-      { BFD_RELOC_MIPS_SUB, BFD_RELOC_MICROMIPS_SUB },
-      { BFD_RELOC_MIPS_GOT_PAGE, BFD_RELOC_MICROMIPS_GOT_PAGE },
-      { BFD_RELOC_MIPS_GOT_OFST, BFD_RELOC_MICROMIPS_GOT_OFST },
-      { BFD_RELOC_MIPS_GOT_DISP, BFD_RELOC_MICROMIPS_GOT_DISP },
-      { BFD_RELOC_MIPS_HIGHEST, BFD_RELOC_MICROMIPS_HIGHEST },
-      { BFD_RELOC_MIPS_HIGHER, BFD_RELOC_MICROMIPS_HIGHER },
-      { BFD_RELOC_MIPS_SCN_DISP, BFD_RELOC_MICROMIPS_SCN_DISP },
-      { BFD_RELOC_MIPS_TLS_GD, BFD_RELOC_MICROMIPS_TLS_GD },
-      { BFD_RELOC_MIPS_TLS_LDM, BFD_RELOC_MICROMIPS_TLS_LDM },
-      { BFD_RELOC_MIPS_TLS_DTPREL_HI16, BFD_RELOC_MICROMIPS_TLS_DTPREL_HI16 },
-      { BFD_RELOC_MIPS_TLS_DTPREL_LO16, BFD_RELOC_MICROMIPS_TLS_DTPREL_LO16 },
-      { BFD_RELOC_MIPS_TLS_GOTTPREL, BFD_RELOC_MICROMIPS_TLS_GOTTPREL },
-      { BFD_RELOC_MIPS_TLS_TPREL_HI16, BFD_RELOC_MICROMIPS_TLS_TPREL_HI16 },
-      { BFD_RELOC_MIPS_TLS_TPREL_LO16, BFD_RELOC_MICROMIPS_TLS_TPREL_LO16 }
-    };
-  bfd_reloc_code_real_type r;
-  size_t i;
-
-  if (!mips_opts.micromips)
-    return reloc;
-  for (i = 0; i < ARRAY_SIZE (relocs); i++)
+  if (arg->seen_at)
     {
-      r = relocs[i][0];
-      if (r > reloc)
-       return reloc;
-      if (r == reloc)
-       return relocs[i][1];
+      if (AT == ATREG)
+       as_warn (_("Used $at without \".set noat\""));
+      else
+       as_warn (_("Used $%u with \".set at=$%u\""), AT, AT);
     }
-  return reloc;
 }
 
-/* Try to resolve relocation RELOC against constant OPERAND at assembly time.
-   Return true on success, storing the resolved value in RESULT.  */
+/* Return true if modifying general-purpose register REG needs a delay.  */
 
 static bfd_boolean
-calculate_reloc (bfd_reloc_code_real_type reloc, offsetT operand,
-                offsetT *result)
+reg_needs_delay (unsigned int reg)
 {
-  switch (reloc)
-    {
-    case BFD_RELOC_MIPS_HIGHEST:
-    case BFD_RELOC_MICROMIPS_HIGHEST:
-      *result = ((operand + 0x800080008000ull) >> 48) & 0xffff;
-      return TRUE;
+  unsigned long prev_pinfo;
 
-    case BFD_RELOC_MIPS_HIGHER:
-    case BFD_RELOC_MICROMIPS_HIGHER:
-      *result = ((operand + 0x80008000ull) >> 32) & 0xffff;
-      return TRUE;
-
-    case BFD_RELOC_HI16_S:
-    case BFD_RELOC_MICROMIPS_HI16_S:
-    case BFD_RELOC_MIPS16_HI16_S:
-      *result = ((operand + 0x8000) >> 16) & 0xffff;
-      return TRUE;
-
-    case BFD_RELOC_HI16:
-    case BFD_RELOC_MICROMIPS_HI16:
-    case BFD_RELOC_MIPS16_HI16:
-      *result = (operand >> 16) & 0xffff;
-      return TRUE;
-
-    case BFD_RELOC_LO16:
-    case BFD_RELOC_MICROMIPS_LO16:
-    case BFD_RELOC_MIPS16_LO16:
-      *result = operand & 0xffff;
-      return TRUE;
-
-    case BFD_RELOC_UNUSED:
-      *result = operand;
-      return TRUE;
+  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))
+      && (gpr_write_mask (&history[0]) & (1 << reg)))
+    return TRUE;
 
-    default:
-      return FALSE;
-    }
+  return FALSE;
 }
 
-/* Output an instruction.  IP is the instruction information.
-   ADDRESS_EXPR is an operand of the instruction to be used with
-   RELOC_TYPE.  EXPANSIONP is true if the instruction is part of
-   a macro expansion.  */
+/* Classify an instruction according to the FIX_VR4120_* enumeration.
+   Return NUM_FIX_VR4120_CLASSES if the instruction isn't affected
+   by VR4120 errata.  */
 
-static void
-append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
-            bfd_reloc_code_real_type *reloc_type, bfd_boolean expansionp)
+static unsigned int
+classify_vr4120_insn (const char *name)
 {
-  unsigned long prev_pinfo2, pinfo;
-  bfd_boolean relaxed_branch = FALSE;
-  enum append_method method;
-  bfd_boolean relax32;
-  int branch_disp;
+  if (strncmp (name, "macc", 4) == 0)
+    return FIX_VR4120_MACC;
+  if (strncmp (name, "dmacc", 5) == 0)
+    return FIX_VR4120_DMACC;
+  if (strncmp (name, "mult", 4) == 0)
+    return FIX_VR4120_MULT;
+  if (strncmp (name, "dmult", 5) == 0)
+    return FIX_VR4120_DMULT;
+  if (strstr (name, "div"))
+    return FIX_VR4120_DIV;
+  if (strcmp (name, "mtlo") == 0 || strcmp (name, "mthi") == 0)
+    return FIX_VR4120_MTHILO;
+  return NUM_FIX_VR4120_CLASSES;
+}
 
-  if (mips_fix_loongson2f && !HAVE_CODE_COMPRESSION)
-    fix_loongson2f (ip);
+#define INSN_ERET  0x42000018
+#define INSN_DERET 0x4200001f
 
-  file_ase_mips16 |= mips_opts.mips16;
-  file_ase_micromips |= mips_opts.micromips;
+/* Return the number of instructions that must separate INSN1 and INSN2,
+   where INSN1 is the earlier instruction.  Return the worst-case value
+   for any INSN2 if INSN2 is null.  */
 
-  prev_pinfo2 = history[0].insn_mo->pinfo2;
-  pinfo = ip->insn_mo->pinfo;
+static unsigned int
+insns_between (const struct mips_cl_insn *insn1,
+              const struct mips_cl_insn *insn2)
+{
+  unsigned long pinfo1, pinfo2;
+  unsigned int mask;
 
-  if (mips_opts.micromips
-      && !expansionp
-      && (((prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0
-          && 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"),
-            (prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0 ? 16 : 32);
+  /* If INFO2 is null, pessimistically assume that all flags are set for
+     the second instruction.  */
+  pinfo1 = insn1->insn_mo->pinfo;
+  pinfo2 = insn2 ? insn2->insn_mo->pinfo : ~0U;
 
-  if (address_expr == NULL)
-    ip->complete_p = 1;
-  else if (reloc_type[0] <= BFD_RELOC_UNUSED
-          && reloc_type[1] == BFD_RELOC_UNUSED
-          && reloc_type[2] == BFD_RELOC_UNUSED
-          && address_expr->X_op == O_constant)
+  /* For most targets, write-after-read dependencies on the HI and LO
+     registers must be separated by at least two instructions.  */
+  if (!hilo_interlocks)
     {
-      switch (*reloc_type)
-       {
-       case BFD_RELOC_MIPS_JMP:
-         {
-           int shift;
-
-           shift = mips_opts.micromips ? 1 : 2;
-           if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
-             as_bad (_("jump to misaligned address (0x%lx)"),
-                     (unsigned long) address_expr->X_add_number);
-           ip->insn_opcode |= ((address_expr->X_add_number >> shift)
-                               & 0x3ffffff);
-           ip->complete_p = 1;
-         }
-         break;
-
-       case BFD_RELOC_MIPS16_JMP:
-         if ((address_expr->X_add_number & 3) != 0)
-           as_bad (_("jump to misaligned address (0x%lx)"),
-                   (unsigned long) address_expr->X_add_number);
-         ip->insn_opcode |=
-           (((address_expr->X_add_number & 0x7c0000) << 3)
-              | ((address_expr->X_add_number & 0xf800000) >> 7)
-              | ((address_expr->X_add_number & 0x3fffc) >> 2));
-         ip->complete_p = 1;
-         break;
+      if ((pinfo1 & INSN_READ_LO) && (pinfo2 & INSN_WRITE_LO))
+       return 2;
+      if ((pinfo1 & INSN_READ_HI) && (pinfo2 & INSN_WRITE_HI))
+       return 2;
+    }
 
-       case BFD_RELOC_16_PCREL_S2:
-         {
-           int shift;
+  /* If we're working around r7000 errata, there must be two instructions
+     between an mfhi or mflo and any instruction that uses the result.  */
+  if (mips_7000_hilo_fix
+      && !mips_opts.micromips
+      && MF_HILO_INSN (pinfo1)
+      && (insn2 == NULL || (gpr_read_mask (insn2) & gpr_write_mask (insn1))))
+    return 2;
 
-           shift = mips_opts.micromips ? 1 : 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 (!mips_relax_branch)
-             {
-               if ((address_expr->X_add_number + (1 << (shift + 15)))
-                   & ~((1 << (shift + 16)) - 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)
-                                   & 0xffff);
-             }
-         }
-         break;
+  /* If we're working around 24K errata, one instruction is required
+     if an ERET or DERET is followed by a branch instruction.  */
+  if (mips_fix_24k && !mips_opts.micromips)
+    {
+      if (insn1->insn_opcode == INSN_ERET
+         || insn1->insn_opcode == INSN_DERET)
+       {
+         if (insn2 == NULL
+             || insn2->insn_opcode == INSN_ERET
+             || insn2->insn_opcode == INSN_DERET
+             || delayed_branch_p (insn2))
+           return 1;
+       }
+    }
 
-       default:
-         {
-           offsetT value;
+  /* If working around VR4120 errata, check for combinations that need
+     a single intervening instruction.  */
+  if (mips_fix_vr4120 && !mips_opts.micromips)
+    {
+      unsigned int class1, class2;
 
-           if (calculate_reloc (*reloc_type, address_expr->X_add_number,
-                                &value))
-             {
-               ip->insn_opcode |= value & 0xffff;
-               ip->complete_p = 1;
-             }
-         }
-         break;
+      class1 = classify_vr4120_insn (insn1->insn_mo->name);
+      if (class1 != NUM_FIX_VR4120_CLASSES && vr4120_conflicts[class1] != 0)
+       {
+         if (insn2 == NULL)
+           return 1;
+         class2 = classify_vr4120_insn (insn2->insn_mo->name);
+         if (vr4120_conflicts[class1] & (1 << class2))
+           return 1;
        }
     }
 
-  if (mips_relax.sequence != 2 && !mips_opts.noreorder)
+  if (!HAVE_CODE_COMPRESSION)
     {
-      /* There are a lot of optimizations we could do that we don't.
-        In particular, we do not, in general, reorder instructions.
-        If you use gcc with optimization, it will reorder
-        instructions and generally do much more optimization then we
-        do here; repeating all that work in the assembler would only
-        benefit hand written assembly code, and does not seem worth
-        it.  */
-      int nops = (mips_optimize == 0
-                 ? nops_for_insn (0, history, NULL)
-                 : nops_for_insn_or_target (0, history, ip));
-      if (nops > 0)
+      /* 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)))
        {
-         fragS *old_frag;
-         unsigned long old_frag_offset;
-         int i;
-
-         old_frag = frag_now;
-         old_frag_offset = frag_now_fix ();
+         if (insn2 == NULL || (gpr_read_mask (insn2) & gpr_write_mask (insn1)))
+           return 1;
+       }
 
-         for (i = 0; i < nops; i++)
-           add_fixed_insn (NOP_INSN);
-         insert_into_history (0, nops, NOP_INSN);
+      /* Check for generic coprocessor hazards.
 
-         if (listing)
+        This case is not handled very well.  There is no special
+        knowledge of CP0 handling, and the coprocessors other than
+        the floating point unit are not distinguished at all.  */
+      /* 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))
+              || (!cop_mem_interlocks && (pinfo1 & INSN_COPROC_MEMORY_DELAY)))
+       {
+         /* Handle cases where INSN1 writes to a known general coprocessor
+            register.  There must be a one instruction delay before INSN2
+            if INSN2 reads that register, otherwise no delay is needed.  */
+         mask = fpr_write_mask (insn1);
+         if (mask != 0)
            {
-             listing_prev_line ();
-             /* We may be at the start of a variant frag.  In case we
-                 are, make sure there is enough space for the frag
-                 after the frags created by listing_prev_line.  The
-                 argument to frag_grow here must be at least as large
-                 as the argument to all other calls to frag_grow in
-                 this file.  We don't have to worry about being in the
-                 middle of a variant frag, because the variants insert
-                 all needed nop instructions themselves.  */
-             frag_grow (40);
+             if (!insn2 || (mask & fpr_read_mask (insn2)) != 0)
+               return 1;
            }
+         else
+           {
+             /* Read-after-write dependencies on the control registers
+                require a two-instruction gap.  */
+             if ((pinfo1 & INSN_WRITE_COND_CODE)
+                 && (pinfo2 & INSN_READ_COND_CODE))
+               return 2;
 
-         mips_move_text_labels ();
-
-#ifndef NO_ECOFF_DEBUGGING
-         if (ECOFF_DEBUGGING)
-           ecoff_fix_loc (old_frag, old_frag_offset);
-#endif
+             /* We don't know exactly what INSN1 does.  If INSN2 is
+                also a coprocessor instruction, assume there must be
+                a one instruction gap.  */
+             if (pinfo2 & INSN_COP)
+               return 1;
+           }
        }
+
+      /* Check for read-after-write dependencies on the coprocessor
+        control registers in cases where INSN1 does not need a general
+        coprocessor delay.  This means that INSN1 is a floating point
+        comparison instruction.  */
+      /* Itbl support may require additional care here.  */
+      else if (!cop_interlocks
+              && (pinfo1 & INSN_WRITE_COND_CODE)
+              && (pinfo2 & INSN_READ_COND_CODE))
+       return 1;
     }
-  else if (mips_relax.sequence != 2 && prev_nop_frag != NULL)
-    {
-      int nops;
 
-      /* Work out how many nops in prev_nop_frag are needed by IP,
-        ignoring hazards generated by the first prev_nop_frag_since
-        instructions.  */
-      nops = nops_for_insn_or_target (prev_nop_frag_since, history, ip);
-      gas_assert (nops <= prev_nop_frag_holds);
+  return 0;
+}
 
-      /* Enforce NOPS as a minimum.  */
-      if (nops > prev_nop_frag_required)
-       prev_nop_frag_required = nops;
+/* Return the number of nops that would be needed to work around the
+   VR4130 mflo/mfhi errata if instruction INSN immediately followed
+   the MAX_VR4130_NOPS instructions described by HIST.  Ignore hazards
+   that are contained within the first IGNORE instructions of HIST.  */
 
-      if (prev_nop_frag_holds == prev_nop_frag_required)
-       {
-         /* Settle for the current number of nops.  Update the history
-            accordingly (for the benefit of any future .set reorder code).  */
-         prev_nop_frag = NULL;
-         insert_into_history (prev_nop_frag_since,
-                              prev_nop_frag_holds, NOP_INSN);
-       }
-      else
-       {
-         /* Allow this instruction to replace one of the nops that was
-            tentatively added to prev_nop_frag.  */
-         prev_nop_frag->fr_fix -= NOP_INSN_SIZE;
-         prev_nop_frag_holds--;
-         prev_nop_frag_since++;
-       }
-    }
+static int
+nops_for_vr4130 (int ignore, const struct mips_cl_insn *hist,
+                const struct mips_cl_insn *insn)
+{
+  int i, j;
+  unsigned int mask;
 
-  method = get_append_method (ip, address_expr, reloc_type);
-  branch_disp = method == APPEND_SWAP ? insn_length (history) : 0;
+  /* Check if the instruction writes to HI or LO.  MTHI and MTLO
+     are not affected by the errata.  */
+  if (insn != 0
+      && ((insn->insn_mo->pinfo & (INSN_WRITE_HI | INSN_WRITE_LO)) == 0
+         || strcmp (insn->insn_mo->name, "mtlo") == 0
+         || strcmp (insn->insn_mo->name, "mthi") == 0))
+    return 0;
 
-#ifdef OBJ_ELF
-  dwarf2_emit_insn (0);
-  /* We want MIPS16 and microMIPS debug info to use ISA-encoded addresses,
-     so "move" the instruction address accordingly.
+  /* Search for the first MFLO or MFHI.  */
+  for (i = 0; i < MAX_VR4130_NOPS; i++)
+    if (MF_HILO_INSN (hist[i].insn_mo->pinfo))
+      {
+       /* Extract the destination register.  */
+       mask = gpr_write_mask (&hist[i]);
 
-     Also, it doesn't seem appropriate for the assembler to reorder .loc
-     entries.  If this instruction is a branch that we are going to swap
-     with the previous instruction, the two instructions should be
-     treated as a unit, and the debug information for both instructions
-     should refer to the start of the branch sequence.  Using the
-     current position is certainly wrong when swapping a 32-bit branch
-     and a 16-bit delay slot, since the current position would then be
-     in the middle of a branch.  */
-  dwarf2_move_insn ((HAVE_CODE_COMPRESSION ? 1 : 0) - branch_disp);
-#endif
+       /* No nops are needed if INSN reads that register.  */
+       if (insn != NULL && (gpr_read_mask (insn) & mask) != 0)
+         return 0;
 
-  relax32 = (mips_relax_branch
-            /* Don't try branch relaxation within .set nomacro, or within
-               .set noat if we use $at for PIC computations.  If it turns
-               out that the branch was out-of-range, we'll get an error.  */
-            && !mips_opts.warn_about_macros
-            && (mips_opts.at || mips_pic == NO_PIC)
-            /* Don't relax BPOSGE32/64 or BC1ANY2T/F and BC1ANY4T/F
-               as they have no complementing branches.  */
-            && !(ip->insn_mo->ase & (ASE_MIPS3D | ASE_DSP64 | ASE_DSP)));
+       /* ...or if any of the intervening instructions do.  */
+       for (j = 0; j < i; j++)
+         if (gpr_read_mask (&hist[j]) & mask)
+           return 0;
 
-  if (!HAVE_CODE_COMPRESSION
-      && address_expr
-      && relax32
-      && *reloc_type == BFD_RELOC_16_PCREL_S2
-      && delayed_branch_p (ip))
-    {
-      relaxed_branch = TRUE;
-      add_relaxed_insn (ip, (relaxed_branch_length
-                            (NULL, NULL,
-                             uncond_branch_p (ip) ? -1
-                             : branch_likely_p (ip) ? 1
-                             : 0)), 4,
-                       RELAX_BRANCH_ENCODE
-                       (AT,
-                        uncond_branch_p (ip),
-                        branch_likely_p (ip),
-                        pinfo & INSN_WRITE_GPR_31,
-                        0),
-                       address_expr->X_add_symbol,
-                       address_expr->X_add_number);
-      *reloc_type = BFD_RELOC_UNUSED;
-    }
-  else if (mips_opts.micromips
-          && address_expr
-          && ((relax32 && *reloc_type == BFD_RELOC_16_PCREL_S2)
-              || *reloc_type > BFD_RELOC_UNUSED)
-          && (delayed_branch_p (ip) || compact_branch_p (ip))
-          /* Don't try branch relaxation when users specify
-             16-bit/32-bit instructions.  */
-          && !forced_insn_length)
-    {
-      bfd_boolean relax16 = *reloc_type > BFD_RELOC_UNUSED;
-      int type = relax16 ? *reloc_type - BFD_RELOC_UNUSED : 0;
-      int uncond = uncond_branch_p (ip) ? -1 : 0;
-      int compact = compact_branch_p (ip);
-      int al = pinfo & INSN_WRITE_GPR_31;
-      int length32;
+       if (i >= ignore)
+         return MAX_VR4130_NOPS - i;
+      }
+  return 0;
+}
 
-      gas_assert (address_expr != NULL);
-      gas_assert (!mips_relax.sequence);
+#define BASE_REG_EQ(INSN1, INSN2)      \
+  ((((INSN1) >> OP_SH_RS) & OP_MASK_RS) \
+      == (((INSN2) >> OP_SH_RS) & OP_MASK_RS))
 
-      relaxed_branch = TRUE;
-      length32 = relaxed_micromips_32bit_branch_length (NULL, NULL, uncond);
-      add_relaxed_insn (ip, relax32 ? length32 : 4, relax16 ? 2 : 4,
-                       RELAX_MICROMIPS_ENCODE (type, AT, uncond, compact, al,
-                                               relax32, 0, 0),
-                       address_expr->X_add_symbol,
-                       address_expr->X_add_number);
-      *reloc_type = BFD_RELOC_UNUSED;
-    }
-  else if (mips_opts.mips16 && *reloc_type > BFD_RELOC_UNUSED)
-    {
-      /* We need to set up a variant frag.  */
-      gas_assert (address_expr != NULL);
-      add_relaxed_insn (ip, 4, 0,
-                       RELAX_MIPS16_ENCODE
-                       (*reloc_type - BFD_RELOC_UNUSED,
-                        forced_insn_length == 2, forced_insn_length == 4,
-                        delayed_branch_p (&history[0]),
-                        history[0].mips16_absolute_jump_p),
-                       make_expr_symbol (address_expr), 0);
-    }
-  else if (mips_opts.mips16 && insn_length (ip) == 2)
-    {
-      if (!delayed_branch_p (ip))
-       /* Make sure there is enough room to swap this instruction with
-          a following jump instruction.  */
-       frag_grow (6);
-      add_fixed_insn (ip);
-    }
-  else
-    {
-      if (mips_opts.mips16
-         && mips_opts.noreorder
-         && delayed_branch_p (&history[0]))
-       as_warn (_("extended instruction in delay slot"));
+/* Return the minimum alignment for this store instruction.  */
 
-      if (mips_relax.sequence)
-       {
-         /* If we've reached the end of this frag, turn it into a variant
-            frag and record the information for the instructions we've
-            written so far.  */
-         if (frag_room () < 4)
-           relax_close_frag ();
-         mips_relax.sizes[mips_relax.sequence - 1] += insn_length (ip);
-       }
+static int
+fix_24k_align_to (const struct mips_opcode *mo)
+{
+  if (strcmp (mo->name, "sh") == 0)
+    return 2;
 
-      if (mips_relax.sequence != 2)
-       {
-         if (mips_macro_warning.first_insn_sizes[0] == 0)
-           mips_macro_warning.first_insn_sizes[0] = insn_length (ip);
-         mips_macro_warning.sizes[0] += insn_length (ip);
-         mips_macro_warning.insns[0]++;
-       }
-      if (mips_relax.sequence != 1)
-       {
-         if (mips_macro_warning.first_insn_sizes[1] == 0)
-           mips_macro_warning.first_insn_sizes[1] = insn_length (ip);
-         mips_macro_warning.sizes[1] += insn_length (ip);
-         mips_macro_warning.insns[1]++;
-       }
+  if (strcmp (mo->name, "swc1") == 0
+      || strcmp (mo->name, "swc2") == 0
+      || strcmp (mo->name, "sw") == 0
+      || strcmp (mo->name, "sc") == 0
+      || strcmp (mo->name, "s.s") == 0)
+    return 4;
 
-      if (mips_opts.mips16)
-       {
-         ip->fixed_p = 1;
-         ip->mips16_absolute_jump_p = (*reloc_type == BFD_RELOC_MIPS16_JMP);
-       }
-      add_fixed_insn (ip);
-    }
+  if (strcmp (mo->name, "sdc1") == 0
+      || strcmp (mo->name, "sdc2") == 0
+      || strcmp (mo->name, "s.d") == 0)
+    return 8;
 
-  if (!ip->complete_p && *reloc_type < BFD_RELOC_UNUSED)
-    {
-      bfd_reloc_code_real_type final_type[3];
-      reloc_howto_type *howto0;
-      reloc_howto_type *howto;
-      int i;
+  /* sb, swl, swr */
+  return 1;
+}
 
-      /* Perform any necessary conversion to microMIPS relocations
-        and find out how many relocations there actually are.  */
-      for (i = 0; i < 3 && reloc_type[i] != BFD_RELOC_UNUSED; i++)
-       final_type[i] = micromips_map_reloc (reloc_type[i]);
+struct fix_24k_store_info
+  {
+    /* Immediate offset, if any, for this store instruction.  */
+    short off;
+    /* Alignment required by this store instruction.  */
+    int align_to;
+    /* True for register offsets.  */
+    int register_offset;
+  };
 
-      /* In a compound relocation, it is the final (outermost)
-        operator that determines the relocated field.  */
-      howto = howto0 = bfd_reloc_type_lookup (stdoutput, final_type[i - 1]);
-      if (!howto)
-       abort ();
-
-      if (i > 1)
-       howto0 = bfd_reloc_type_lookup (stdoutput, final_type[0]);
-      ip->fixp[0] = fix_new_exp (ip->frag, ip->where,
-                                bfd_get_reloc_size (howto),
-                                address_expr,
-                                howto0 && howto0->pc_relative,
-                                final_type[0]);
+/* Comparison function used by qsort.  */
 
-      /* Tag symbols that have a R_MIPS16_26 relocation against them.  */
-      if (final_type[0] == BFD_RELOC_MIPS16_JMP && ip->fixp[0]->fx_addsy)
-       *symbol_get_tc (ip->fixp[0]->fx_addsy) = 1;
+static int
+fix_24k_sort (const void *a, const void *b)
+{
+  const struct fix_24k_store_info *pos1 = a;
+  const struct fix_24k_store_info *pos2 = b;
 
-      /* These relocations can have an addend that won't fit in
-        4 octets for 64bit assembly.  */
-      if (HAVE_64BIT_GPRS
-         && ! howto->partial_inplace
-         && (reloc_type[0] == BFD_RELOC_16
-             || reloc_type[0] == BFD_RELOC_32
-             || reloc_type[0] == BFD_RELOC_MIPS_JMP
-             || reloc_type[0] == BFD_RELOC_GPREL16
-             || reloc_type[0] == BFD_RELOC_MIPS_LITERAL
-             || reloc_type[0] == BFD_RELOC_GPREL32
-             || reloc_type[0] == BFD_RELOC_64
-             || reloc_type[0] == BFD_RELOC_CTOR
-             || reloc_type[0] == BFD_RELOC_MIPS_SUB
-             || reloc_type[0] == BFD_RELOC_MIPS_HIGHEST
-             || reloc_type[0] == BFD_RELOC_MIPS_HIGHER
-             || reloc_type[0] == BFD_RELOC_MIPS_SCN_DISP
-             || reloc_type[0] == BFD_RELOC_MIPS_REL16
-             || reloc_type[0] == BFD_RELOC_MIPS_RELGOT
-             || reloc_type[0] == BFD_RELOC_MIPS16_GPREL
-             || hi16_reloc_p (reloc_type[0])
-             || lo16_reloc_p (reloc_type[0])))
-       ip->fixp[0]->fx_no_overflow = 1;
+  return (pos1->off - pos2->off);
+}
 
-      /* These relocations can have an addend that won't fit in 2 octets.  */
-      if (reloc_type[0] == BFD_RELOC_MICROMIPS_7_PCREL_S1
-         || reloc_type[0] == BFD_RELOC_MICROMIPS_10_PCREL_S1)
-       ip->fixp[0]->fx_no_overflow = 1;
+/* INSN is a store instruction.  Try to record the store information
+   in STINFO.  Return false if the information isn't known.  */
 
-      if (mips_relax.sequence)
-       {
-         if (mips_relax.first_fixup == 0)
-           mips_relax.first_fixup = ip->fixp[0];
-       }
-      else if (reloc_needs_lo_p (*reloc_type))
-       {
-         struct mips_hi_fixup *hi_fixup;
+static bfd_boolean
+fix_24k_record_store_info (struct fix_24k_store_info *stinfo,
+                          const struct mips_cl_insn *insn)
+{
+  /* The instruction must have a known offset.  */
+  if (!insn->complete_p || !strstr (insn->insn_mo->args, "o("))
+    return FALSE;
 
-         /* Reuse the last entry if it already has a matching %lo.  */
-         hi_fixup = mips_hi_fixup_list;
-         if (hi_fixup == 0
-             || !fixup_has_matching_lo_p (hi_fixup->fixp))
-           {
-             hi_fixup = ((struct mips_hi_fixup *)
-                         xmalloc (sizeof (struct mips_hi_fixup)));
-             hi_fixup->next = mips_hi_fixup_list;
-             mips_hi_fixup_list = hi_fixup;
-           }
-         hi_fixup->fixp = ip->fixp[0];
-         hi_fixup->seg = now_seg;
-       }
+  stinfo->off = (insn->insn_opcode >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE;
+  stinfo->align_to = fix_24k_align_to (insn->insn_mo);
+  return TRUE;
+}
 
-      /* Add fixups for the second and third relocations, if given.
-        Note that the ABI allows the second relocation to be
-        against RSS_UNDEF, RSS_GP, RSS_GP0 or RSS_LOC.  At the
-        moment we only use RSS_UNDEF, but we could add support
-        for the others if it ever becomes necessary.  */
-      for (i = 1; i < 3; i++)
-       if (reloc_type[i] != BFD_RELOC_UNUSED)
-         {
-           ip->fixp[i] = fix_new (ip->frag, ip->where,
-                                  ip->fixp[0]->fx_size, NULL, 0,
-                                  FALSE, final_type[i]);
+/* Return the number of nops that would be needed to work around the 24k
+   "lost data on stores during refill" errata if instruction INSN
+   immediately followed the 2 instructions described by HIST.
+   Ignore hazards that are contained within the first IGNORE
+   instructions of HIST.
 
-           /* Use fx_tcbit to mark compound relocs.  */
-           ip->fixp[0]->fx_tcbit = 1;
-           ip->fixp[i]->fx_tcbit = 1;
-         }
-    }
-  install_insn (ip);
+   Problem: The FSB (fetch store buffer) acts as an intermediate buffer
+   for the data cache refills and store data. The following describes
+   the scenario where the store data could be lost.
 
-  /* Update the register mask information.  */
-  mips_gprmask |= gpr_read_mask (ip) | gpr_write_mask (ip);
-  mips_cprmask[1] |= fpr_read_mask (ip) | fpr_write_mask (ip);
+   * A data cache miss, due to either a load or a store, causing fill
+     data to be supplied by the memory subsystem
+   * The first three doublewords of fill data are returned and written
+     into the cache
+   * A sequence of four stores occurs in consecutive cycles around the
+     final doubleword of the fill:
+   * Store A
+   * Store B
+   * Store C
+   * Zero, One or more instructions
+   * Store D
 
-  switch (method)
-    {
-    case APPEND_ADD:
-      insert_into_history (0, 1, ip);
-      break;
+   The four stores A-D must be to different doublewords of the line that
+   is being filled. The fourth instruction in the sequence above permits
+   the fill of the final doubleword to be transferred from the FSB into
+   the cache. In the sequence above, the stores may be either integer
+   (sb, sh, sw, swr, swl, sc) or coprocessor (swc1/swc2, sdc1/sdc2,
+   swxc1, sdxc1, suxc1) stores, as long as the four stores are to
+   different doublewords on the line. If the floating point unit is
+   running in 1:2 mode, it is not possible to create the sequence above
+   using only floating point store instructions.
 
-    case APPEND_ADD_WITH_NOP:
-      {
-       struct mips_cl_insn *nop;
+   In this case, the cache line being filled is incorrectly marked
+   invalid, thereby losing the data from any store to the line that
+   occurs between the original miss and the completion of the five
+   cycle sequence shown above.
 
-       insert_into_history (0, 1, ip);
-       nop = get_delay_slot_nop (ip);
-       add_fixed_insn (nop);
-       insert_into_history (0, 1, nop);
-       if (mips_relax.sequence)
-         mips_relax.sizes[mips_relax.sequence - 1] += insn_length (nop);
-      }
-      break;
+   The workarounds are:
 
-    case APPEND_ADD_COMPACT:
-      /* Convert MIPS16 jr/jalr into a "compact" jump.  */
-      gas_assert (mips_opts.mips16);
-      ip->insn_opcode |= 0x0080;
-      find_altered_mips16_opcode (ip);
-      install_insn (ip);
-      insert_into_history (0, 1, ip);
-      break;
+   * Run the data cache in write-through mode.
+   * Insert a non-store instruction between
+     Store A and Store B or Store B and Store C.  */
+  
+static int
+nops_for_24k (int ignore, const struct mips_cl_insn *hist,
+             const struct mips_cl_insn *insn)
+{
+  struct fix_24k_store_info pos[3];
+  int align, i, base_offset;
 
-    case APPEND_SWAP:
-      {
-       struct mips_cl_insn delay = history[0];
-       if (mips_opts.mips16)
-         {
-           know (delay.frag == ip->frag);
-           move_insn (ip, delay.frag, delay.where);
-           move_insn (&delay, ip->frag, ip->where + insn_length (ip));
-         }
-       else if (relaxed_branch || delay.frag != ip->frag)
-         {
-           /* Add the delay slot instruction to the end of the
-              current frag and shrink the fixed part of the
-              original frag.  If the branch occupies the tail of
-              the latter, move it backwards to cover the gap.  */
-           delay.frag->fr_fix -= branch_disp;
-           if (delay.frag == ip->frag)
-             move_insn (ip, ip->frag, ip->where - branch_disp);
-           add_fixed_insn (&delay);
-         }
-       else
-         {
-           move_insn (&delay, ip->frag,
-                      ip->where - branch_disp + insn_length (ip));
-           move_insn (ip, history[0].frag, history[0].where);
-         }
-       history[0] = *ip;
-       delay.fixed_p = 1;
-       insert_into_history (0, 1, &delay);
-      }
-      break;
-    }
+  if (ignore >= 2)
+    return 0;
 
-  /* If we have just completed an unconditional branch, clear the history.  */
-  if ((delayed_branch_p (&history[1]) && uncond_branch_p (&history[1]))
-      || (compact_branch_p (&history[0]) && uncond_branch_p (&history[0])))
-    {
-      unsigned int i;
+  /* If the previous instruction wasn't a store, there's nothing to
+     worry about.  */
+  if ((hist[0].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
+    return 0;
 
-      mips_no_prev_insn ();
+  /* If the instructions after the previous one are unknown, we have
+     to assume the worst.  */
+  if (!insn)
+    return 1;
 
-      for (i = 0; i < ARRAY_SIZE (history); i++)
-       history[i].cleared_p = 1;
-    }
+  /* Check whether we are dealing with three consecutive stores.  */
+  if ((insn->insn_mo->pinfo & INSN_STORE_MEMORY) == 0
+      || (hist[1].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
+    return 0;
 
-  /* We need to emit a label at the end of branch-likely macros.  */
-  if (emit_branch_likely_macro)
-    {
-      emit_branch_likely_macro = FALSE;
-      micromips_add_label ();
-    }
+  /* If we don't know the relationship between the store addresses,
+     assume the worst.  */
+  if (!BASE_REG_EQ (insn->insn_opcode, hist[0].insn_opcode)
+      || !BASE_REG_EQ (insn->insn_opcode, hist[1].insn_opcode))
+    return 1;
 
-  /* We just output an insn, so the next one doesn't have a label.  */
-  mips_clear_insn_labels ();
-}
+  if (!fix_24k_record_store_info (&pos[0], insn)
+      || !fix_24k_record_store_info (&pos[1], &hist[0])
+      || !fix_24k_record_store_info (&pos[2], &hist[1]))
+    return 1;
 
-/* Forget that there was any previous instruction or label.
-   When BRANCH is true, the branch history is also flushed.  */
+  qsort (&pos, 3, sizeof (struct fix_24k_store_info), fix_24k_sort);
 
-static void
-mips_no_prev_insn (void)
-{
-  prev_nop_frag = NULL;
-  insert_into_history (0, ARRAY_SIZE (history), NOP_INSN);
-  mips_clear_insn_labels ();
-}
+  /* Pick a value of ALIGN and X such that all offsets are adjusted by
+     X bytes and such that the base register + X is known to be aligned
+     to align bytes.  */
 
-/* This function must be called before we emit something other than
-   instructions.  It is like mips_no_prev_insn except that it inserts
-   any NOPS that might be needed by previous instructions.  */
-
-void
-mips_emit_delays (void)
-{
-  if (! mips_opts.noreorder)
+  if (((insn->insn_opcode >> OP_SH_RS) & OP_MASK_RS) == SP)
+    align = 8;
+  else
     {
-      int nops = nops_for_insn (0, history, NULL);
-      if (nops > 0)
-       {
-         while (nops-- > 0)
-           add_fixed_insn (NOP_INSN);
-         mips_move_text_labels ();
-       }
+      align = pos[0].align_to;
+      base_offset = pos[0].off;
+      for (i = 1; i < 3; i++)
+       if (align < pos[i].align_to)
+         {
+           align = pos[i].align_to;
+           base_offset = pos[i].off;
+         }
+      for (i = 0; i < 3; i++)
+       pos[i].off -= base_offset;
     }
-  mips_no_prev_insn ();
-}
-
-/* Start a (possibly nested) noreorder block.  */
 
-static void
-start_noreorder (void)
-{
-  if (mips_opts.noreorder == 0)
-    {
-      unsigned int i;
-      int nops;
+  pos[0].off &= ~align + 1;
+  pos[1].off &= ~align + 1;
+  pos[2].off &= ~align + 1;
 
-      /* None of the instructions before the .set noreorder can be moved.  */
-      for (i = 0; i < ARRAY_SIZE (history); i++)
-       history[i].fixed_p = 1;
+  /* If any two stores write to the same chunk, they also write to the
+     same doubleword.  The offsets are still sorted at this point.  */
+  if (pos[0].off == pos[1].off || pos[1].off == pos[2].off)
+    return 0;
 
-      /* Insert any nops that might be needed between the .set noreorder
-        block and the previous instructions.  We will later remove any
-        nops that turn out not to be needed.  */
-      nops = nops_for_insn (0, history, NULL);
-      if (nops > 0)
-       {
-         if (mips_optimize != 0)
-           {
-             /* Record the frag which holds the nop instructions, so
-                 that we can remove them if we don't need them.  */
-             frag_grow (nops * NOP_INSN_SIZE);
-             prev_nop_frag = frag_now;
-             prev_nop_frag_holds = nops;
-             prev_nop_frag_required = 0;
-             prev_nop_frag_since = 0;
-           }
+  /* A range of at least 9 bytes is needed for the stores to be in
+     non-overlapping doublewords.  */
+  if (pos[2].off - pos[0].off <= 8)
+    return 0;
 
-         for (; nops > 0; --nops)
-           add_fixed_insn (NOP_INSN);
+  if (pos[2].off - pos[1].off >= 24
+      || pos[1].off - pos[0].off >= 24
+      || pos[2].off - pos[0].off >= 32)
+    return 0;
 
-         /* Move on to a new frag, so that it is safe to simply
-            decrease the size of prev_nop_frag.  */
-         frag_wane (frag_now);
-         frag_new (0);
-         mips_move_text_labels ();
-       }
-      mips_mark_labels ();
-      mips_clear_insn_labels ();
-    }
-  mips_opts.noreorder++;
-  mips_any_noreorder = 1;
+  return 1;
 }
 
-/* End a nested noreorder block.  */
+/* Return the number of nops that would be needed if instruction INSN
+   immediately followed the MAX_NOPS instructions given by HIST,
+   where HIST[0] is the most recent instruction.  Ignore hazards
+   between INSN and the first IGNORE instructions in HIST.
 
-static void
-end_noreorder (void)
+   If INSN is null, return the worse-case number of nops for any
+   instruction.  */
+
+static int
+nops_for_insn (int ignore, const struct mips_cl_insn *hist,
+              const struct mips_cl_insn *insn)
 {
-  mips_opts.noreorder--;
-  if (mips_opts.noreorder == 0 && prev_nop_frag != NULL)
+  int i, nops, tmp_nops;
+
+  nops = 0;
+  for (i = ignore; i < MAX_DELAY_NOPS; i++)
     {
-      /* Commit to inserting prev_nop_frag_required nops and go back to
-        handling nop insertion the .set reorder way.  */
-      prev_nop_frag->fr_fix -= ((prev_nop_frag_holds - prev_nop_frag_required)
-                               * NOP_INSN_SIZE);
-      insert_into_history (prev_nop_frag_since,
-                          prev_nop_frag_required, NOP_INSN);
-      prev_nop_frag = NULL;
+      tmp_nops = insns_between (hist + i, insn) - i;
+      if (tmp_nops > nops)
+       nops = tmp_nops;
     }
-}
 
-/* Set up global variables for the start of a new macro.  */
+  if (mips_fix_vr4130 && !mips_opts.micromips)
+    {
+      tmp_nops = nops_for_vr4130 (ignore, hist, insn);
+      if (tmp_nops > nops)
+       nops = tmp_nops;
+    }
 
-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))
+  if (mips_fix_24k && !mips_opts.micromips)
     {
-    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;
+      tmp_nops = nops_for_24k (ignore, hist, insn);
+      if (tmp_nops > nops)
+       nops = tmp_nops;
     }
-  mips_macro_warning.first_frag = NULL;
+
+  return nops;
 }
 
-/* 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.  */
+/* The variable arguments provide NUM_INSNS extra instructions that
+   might be added to HIST.  Return the largest number of nops that
+   would be needed after the extended sequence, ignoring hazards
+   in the first IGNORE instructions.  */
 
-static const char *
-macro_warning (relax_substateT subtype)
+static int
+nops_for_sequence (int num_insns, int ignore,
+                  const struct mips_cl_insn *hist, ...)
 {
-  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;
+  va_list args;
+  struct mips_cl_insn buffer[MAX_NOPS];
+  struct mips_cl_insn *cursor;
+  int nops;
+
+  va_start (args, hist);
+  cursor = buffer + num_insns;
+  memcpy (cursor, hist, (MAX_NOPS - num_insns) * sizeof (*cursor));
+  while (cursor > buffer)
+    *--cursor = *va_arg (args, const struct mips_cl_insn *);
+
+  nops = nops_for_insn (ignore, buffer, NULL);
+  va_end (args);
+  return nops;
 }
 
-/* Finish up a macro.  Emit warnings as appropriate.  */
+/* Like nops_for_insn, but if INSN is a branch, take into account the
+   worst-case delay for the branch target.  */
 
-static void
-macro_end (void)
+static int
+nops_for_insn_or_target (int ignore, const struct mips_cl_insn *hist,
+                        const struct mips_cl_insn *insn)
 {
-  /* Relaxation warning flags.  */
-  relax_substateT subtype = 0;
+  int nops, tmp_nops;
 
-  /* Check delay slot size requirements.  */
-  if (mips_macro_warning.delay_slot_length == 2)
-    subtype |= RELAX_DELAY_SLOT_16BIT;
-  if (mips_macro_warning.delay_slot_length != 0)
+  nops = nops_for_insn (ignore, hist, insn);
+  if (delayed_branch_p (insn))
     {
-      if (mips_macro_warning.delay_slot_length
-         != mips_macro_warning.first_insn_sizes[0])
-       subtype |= RELAX_DELAY_SLOT_SIZE_FIRST;
-      if (mips_macro_warning.delay_slot_length
-         != mips_macro_warning.first_insn_sizes[1])
-       subtype |= RELAX_DELAY_SLOT_SIZE_SECOND;
+      tmp_nops = nops_for_sequence (2, ignore ? ignore + 2 : 0,
+                                   hist, insn, get_delay_slot_nop (insn));
+      if (tmp_nops > nops)
+       nops = tmp_nops;
     }
-
-  /* Check instruction count requirements.  */
-  if (mips_macro_warning.insns[0] > 1 || mips_macro_warning.insns[1] > 1)
+  else if (compact_branch_p (insn))
     {
-      if (mips_macro_warning.insns[1] > mips_macro_warning.insns[0])
-       subtype |= RELAX_SECOND_LONGER;
-      if (mips_opts.warn_about_macros)
-       subtype |= RELAX_NOMACRO;
-      if (mips_macro_warning.delay_slot_p)
-       subtype |= RELAX_DELAY_SLOT;
+      tmp_nops = nops_for_sequence (1, ignore ? ignore + 1 : 0, hist, insn);
+      if (tmp_nops > nops)
+       nops = tmp_nops;
     }
+  return nops;
+}
 
-  /* If both alternatives fail to fill a delay slot correctly,
-     emit the warning now.  */
-  if ((subtype & RELAX_DELAY_SLOT_SIZE_FIRST) != 0
-      && (subtype & RELAX_DELAY_SLOT_SIZE_SECOND) != 0)
-    {
-      relax_substateT s;
-      const char *msg;
-
-      s = subtype & (RELAX_DELAY_SLOT_16BIT
-                    | RELAX_DELAY_SLOT_SIZE_FIRST
-                    | RELAX_DELAY_SLOT_SIZE_SECOND);
-      msg = macro_warning (s);
-      if (msg != NULL)
-       as_warn ("%s", msg);
-      subtype &= ~s;
-    }
-
-  /* If both implementations are longer than 1 instruction, then emit the
-     warning now.  */
-  if (mips_macro_warning.insns[0] > 1 && mips_macro_warning.insns[1] > 1)
-    {
-      relax_substateT s;
-      const char *msg;
-
-      s = subtype & (RELAX_SECOND_LONGER | RELAX_NOMACRO | RELAX_DELAY_SLOT);
-      msg = macro_warning (s);
-      if (msg != NULL)
-       as_warn ("%s", msg);
-      subtype &= ~s;
-    }
-
-  /* If any flags still set, then one implementation might need a warning
-     and the other either will need one of a different kind or none at all.
-     Pass any remaining flags over to relaxation.  */
-  if (mips_macro_warning.first_frag != NULL)
-    mips_macro_warning.first_frag->fr_subtype |= subtype;
-}
-
-/* Instruction operand formats used in macros that vary between
-   standard MIPS and microMIPS code.  */
-
-static const char * const brk_fmt[2] = { "c", "mF" };
-static const char * const cop12_fmt[2] = { "E,o(b)", "E,~(b)" };
-static const char * const jalr_fmt[2] = { "d,s", "t,s" };
-static const char * const lui_fmt[2] = { "t,u", "s,u" };
-static const char * const mem12_fmt[2] = { "t,o(b)", "t,~(b)" };
-static const char * const mfhl_fmt[2] = { "d", "mj" };
-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])
-#define COP12_FMT (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 MFHL_FMT (mfhl_fmt[mips_opts.micromips])
-#define SHFT_FMT (shft_fmt[mips_opts.micromips])
-#define TRAP_FMT (trap_fmt[mips_opts.micromips])
-
-/* Read a macro's relocation codes from *ARGS and store them in *R.
-   The first argument in *ARGS will be either the code for a single
-   relocation or -1 followed by the three codes that make up a
-   composite relocation.  */
+/* Fix NOP issue: Replace nops by "or at,at,zero".  */
 
 static void
-macro_read_relocs (va_list *args, bfd_reloc_code_real_type *r)
+fix_loongson2f_nop (struct mips_cl_insn * ip)
 {
-  int i, next;
-
-  next = va_arg (*args, int);
-  if (next >= 0)
-    r[0] = (bfd_reloc_code_real_type) next;
-  else
-    for (i = 0; i < 3; i++)
-      r[i] = (bfd_reloc_code_real_type) va_arg (*args, int);
+  gas_assert (!HAVE_CODE_COMPRESSION);
+  if (strcmp (ip->insn_mo->name, "nop") == 0)
+    ip->insn_opcode = LOONGSON2F_NOP_INSN;
 }
 
-/* Build an instruction created by a macro expansion.  This is passed
-   a pointer to the count of instructions created so far, an
-   expression, the name of the instruction to build, an operand format
-   string, and corresponding arguments.  */
+/* Fix Jump Issue: Eliminate instruction fetch from outside 256M region
+                   jr target pc &= 'hffff_ffff_cfff_ffff.  */
 
 static void
-macro_build (expressionS *ep, const char *name, const char *fmt, ...)
+fix_loongson2f_jump (struct mips_cl_insn * ip)
 {
-  const struct mips_opcode *mo = NULL;
-  bfd_reloc_code_real_type r[3];
-  const struct mips_opcode *amo;
-  struct hash_control *hash;
-  struct mips_cl_insn insn;
-  va_list args;
-
-  va_start (args, fmt);
-
-  if (mips_opts.mips16)
+  gas_assert (!HAVE_CODE_COMPRESSION);
+  if (strcmp (ip->insn_mo->name, "j") == 0
+      || strcmp (ip->insn_mo->name, "jr") == 0
+      || strcmp (ip->insn_mo->name, "jalr") == 0)
     {
-      mips16_macro_build (ep, name, fmt, &args);
-      va_end (args);
-      return;
-    }
+      int sreg;
+      expressionS ep;
 
-  r[0] = BFD_RELOC_UNUSED;
-  r[1] = BFD_RELOC_UNUSED;
-  r[2] = BFD_RELOC_UNUSED;
-  hash = mips_opts.micromips ? micromips_op_hash : op_hash;
-  amo = (struct mips_opcode *) hash_find (hash, name);
-  gas_assert (amo);
-  gas_assert (strcmp (name, amo->name) == 0);
+      if (! mips_opts.at)
+        return;
 
-  do
-    {
-      /* Search until we get a match for NAME.  It is assumed here that
-        macros will never generate MDMX, MIPS-3D, or MT instructions.
-        We try to match an instruction that fulfils the branch delay
-        slot instruction length requirement (if any) of the previous
-        instruction.  While doing this we record the first instruction
-        seen that matches all the other conditions and use it anyway
-        if the requirement cannot be met; we will issue an appropriate
-        warning later on.  */
-      if (strcmp (fmt, amo->args) == 0
-         && amo->pinfo != INSN_MACRO
-         && is_opcode_valid (amo)
-         && is_size_valid (amo))
-       {
-         if (is_delay_slot_valid (amo))
-           {
-             mo = amo;
-             break;
-           }
-         else if (!mo)
-           mo = amo;
-       }
+      sreg = EXTRACT_OPERAND (0, RS, *ip);
+      if (sreg == ZERO || sreg == KT0 || sreg == KT1 || sreg == ATREG)
+        return;
 
-      ++amo;
-      gas_assert (amo->name);
+      ep.X_op = O_constant;
+      ep.X_add_number = 0xcfff0000;
+      macro_build (&ep, "lui", "t,u", ATREG, BFD_RELOC_HI16);
+      ep.X_add_number = 0xffff;
+      macro_build (&ep, "ori", "t,r,i", ATREG, ATREG, BFD_RELOC_LO16);
+      macro_build (NULL, "and", "d,v,t", sreg, sreg, ATREG);
     }
-  while (strcmp (name, amo->name) == 0);
-
-  gas_assert (mo);
-  create_insn (&insn, mo);
-  for (;;)
-    {
-      switch (*fmt++)
-       {
-       case '\0':
-         break;
+}
 
-       case ',':
-       case '(':
-       case ')':
-         continue;
+static void
+fix_loongson2f (struct mips_cl_insn * ip)
+{
+  if (mips_fix_loongson2f_nop)
+    fix_loongson2f_nop (ip);
 
-       case '+':
-         switch (*fmt++)
-           {
-           case 'A':
-           case 'E':
-             INSERT_OPERAND (mips_opts.micromips,
-                             EXTLSB, insn, va_arg (args, int));
-             continue;
+  if (mips_fix_loongson2f_jump)
+    fix_loongson2f_jump (ip);
+}
 
-           case 'B':
-           case 'F':
-             /* Note that in the macro case, these arguments are already
-                in MSB form.  (When handling the instruction in the
-                non-macro case, these arguments are sizes from which
-                MSB values must be calculated.)  */
-             INSERT_OPERAND (mips_opts.micromips,
-                             INSMSB, insn, va_arg (args, int));
-             continue;
+/* IP is a branch that has a delay slot, and we need to fill it
+   automatically.   Return true if we can do that by swapping IP
+   with the previous instruction.
+   ADDRESS_EXPR is an operand of the instruction to be used with
+   RELOC_TYPE.  */
 
-           case 'J':
-             gas_assert (!mips_opts.micromips);
-             INSERT_OPERAND (0, CODE10, insn, va_arg (args, int));
-             continue;
+static bfd_boolean
+can_swap_branch_p (struct mips_cl_insn *ip, expressionS *address_expr,
+                  bfd_reloc_code_real_type *reloc_type)
+{
+  unsigned long pinfo, pinfo2, prev_pinfo, prev_pinfo2;
+  unsigned int gpr_read, gpr_write, prev_gpr_read, prev_gpr_write;
 
-           case 'C':
-           case 'G':
-           case 'H':
-             /* Note that in the macro case, these arguments are already
-                in MSBD form.  (When handling the instruction in the
-                non-macro case, these arguments are sizes from which
-                MSBD values must be calculated.)  */
-             INSERT_OPERAND (mips_opts.micromips,
-                             EXTMSBD, insn, va_arg (args, int));
-             continue;
+  /* -O2 and above is required for this optimization.  */
+  if (mips_optimize < 2)
+    return FALSE;
 
-           case 'Q':
-             gas_assert (!mips_opts.micromips);
-             INSERT_OPERAND (0, SEQI, insn, va_arg (args, int));
-             continue;
+  /* If we have seen .set volatile or .set nomove, don't optimize.  */
+  if (mips_opts.nomove)
+    return FALSE;
 
-           case 'j':
-             INSERT_OPERAND (mips_opts.micromips, EVAOFFSET, insn, va_arg (args, int));
-             continue;
+  /* We can't swap if the previous instruction's position is fixed.  */
+  if (history[0].fixed_p)
+    return FALSE;
 
-           default:
-             abort ();
-           }
-         continue;
+  /* If the previous previous insn was in a .set noreorder, we can't
+     swap.  Actually, the MIPS assembler will swap in this situation.
+     However, gcc configured -with-gnu-as will generate code like
 
-       case '2':
-         INSERT_OPERAND (mips_opts.micromips, BP, insn, va_arg (args, int));
-         continue;
+       .set    noreorder
+       lw      $4,XXX
+       .set    reorder
+       INSN
+       bne     $4,$0,foo
 
-       case 'n':
-         gas_assert (mips_opts.micromips);
-       case 't':
-       case 'w':
-       case 'E':
-         INSERT_OPERAND (mips_opts.micromips, RT, insn, va_arg (args, int));
-         continue;
+     in which we can not swap the bne and INSN.  If gcc is not configured
+     -with-gnu-as, it does not output the .set pseudo-ops.  */
+  if (history[1].noreorder_p)
+    return FALSE;
 
-       case 'c':
-         gas_assert (!mips_opts.micromips);
-         INSERT_OPERAND (0, CODE, insn, va_arg (args, int));
-         continue;
+  /* If the previous instruction had a fixup in mips16 mode, we can not swap.
+     This means that the previous instruction was a 4-byte one anyhow.  */
+  if (mips_opts.mips16 && history[0].fixp[0])
+    return FALSE;
 
-       case 'W':
-         gas_assert (!mips_opts.micromips);
-       case 'T':
-         INSERT_OPERAND (mips_opts.micromips, FT, insn, va_arg (args, int));
-         continue;
+  /* If the branch is itself the target of a branch, we can not swap.
+     We cheat on this; all we check for is whether there is a label on
+     this instruction.  If there are any branches to anything other than
+     a label, users must use .set noreorder.  */
+  if (seg_info (now_seg)->label_list)
+    return FALSE;
 
-       case 'G':
-         if (mips_opts.micromips)
-           INSERT_OPERAND (1, RS, insn, va_arg (args, int));
-         else
-           INSERT_OPERAND (0, RD, insn, va_arg (args, int));
-         continue;
+  /* If the previous instruction is in a variant frag other than this
+     branch's one, we cannot do the swap.  This does not apply to
+     MIPS16 code, which uses variant frags for different purposes.  */
+  if (!mips_opts.mips16
+      && history[0].frag
+      && history[0].frag->fr_type == rs_machine_dependent)
+    return FALSE;
 
-       case 'K':
-         gas_assert (!mips_opts.micromips);
-       case 'd':
-         INSERT_OPERAND (mips_opts.micromips, RD, insn, va_arg (args, int));
-         continue;
+  /* We do not swap with instructions that cannot architecturally
+     be placed in a branch delay slot, such as SYNC or ERET.  We
+     also refrain from swapping with a trap instruction, since it
+     complicates trap handlers to have the trap instruction be in
+     a delay slot.  */
+  prev_pinfo = history[0].insn_mo->pinfo;
+  if (prev_pinfo & INSN_NO_DELAY_SLOT)
+    return FALSE;
 
-       case 'U':
-         gas_assert (!mips_opts.micromips);
-         {
-           int tmp = va_arg (args, int);
+  /* Check for conflicts between the branch and the instructions
+     before the candidate delay slot.  */
+  if (nops_for_insn (0, history + 1, ip) > 0)
+    return FALSE;
 
-           INSERT_OPERAND (0, RT, insn, tmp);
-           INSERT_OPERAND (0, RD, insn, tmp);
-         }
-         continue;
+  /* Check for conflicts between the swapped sequence and the
+     target of the branch.  */
+  if (nops_for_sequence (2, 0, history + 1, ip, history) > 0)
+    return FALSE;
 
-       case 'V':
-       case 'S':
-         gas_assert (!mips_opts.micromips);
-         INSERT_OPERAND (0, FS, insn, va_arg (args, int));
-         continue;
+  /* If the branch reads a register that the previous
+     instruction sets, we can not swap.  */
+  gpr_read = gpr_read_mask (ip);
+  prev_gpr_write = gpr_write_mask (&history[0]);
+  if (gpr_read & prev_gpr_write)
+    return FALSE;
 
-       case 'z':
-         continue;
+  /* If the branch writes a register that the previous
+     instruction sets, we can not swap.  */
+  gpr_write = gpr_write_mask (ip);
+  if (gpr_write & prev_gpr_write)
+    return FALSE;
 
-       case '<':
-         INSERT_OPERAND (mips_opts.micromips,
-                         SHAMT, insn, va_arg (args, int));
-         continue;
-
-       case 'D':
-         gas_assert (!mips_opts.micromips);
-         INSERT_OPERAND (0, FD, insn, va_arg (args, int));
-         continue;
-
-       case 'B':
-         gas_assert (!mips_opts.micromips);
-         INSERT_OPERAND (0, CODE20, insn, va_arg (args, int));
-         continue;
-
-       case 'J':
-         gas_assert (!mips_opts.micromips);
-         INSERT_OPERAND (0, CODE19, insn, va_arg (args, int));
-         continue;
+  /* If the branch writes a register that the previous
+     instruction reads, we can not swap.  */
+  prev_gpr_read = gpr_read_mask (&history[0]);
+  if (gpr_write & prev_gpr_read)
+    return FALSE;
 
-       case 'q':
-         gas_assert (!mips_opts.micromips);
-         INSERT_OPERAND (0, CODE2, insn, va_arg (args, int));
-         continue;
+  /* If one instruction sets a condition code and the
+     other one uses a condition code, we can not swap.  */
+  pinfo = ip->insn_mo->pinfo;
+  if ((pinfo & INSN_READ_COND_CODE)
+      && (prev_pinfo & INSN_WRITE_COND_CODE))
+    return FALSE;
+  if ((pinfo & INSN_WRITE_COND_CODE)
+      && (prev_pinfo & INSN_READ_COND_CODE))
+    return FALSE;
 
-       case 'b':
-       case 's':
-       case 'r':
-       case 'v':
-         INSERT_OPERAND (mips_opts.micromips, RS, insn, va_arg (args, int));
-         continue;
+  /* If the previous instruction uses the PC, we can not swap.  */
+  prev_pinfo2 = history[0].insn_mo->pinfo2;
+  if (prev_pinfo2 & INSN2_READ_PC)
+    return FALSE;
 
-       case 'i':
-       case 'j':
-         macro_read_relocs (&args, r);
-         gas_assert (*r == BFD_RELOC_GPREL16
-                     || *r == BFD_RELOC_MIPS_HIGHER
-                     || *r == BFD_RELOC_HI16_S
-                     || *r == BFD_RELOC_LO16
-                     || *r == BFD_RELOC_MIPS_GOT_OFST);
-         continue;
+  /* If the previous instruction has an incorrect size for a fixed
+     branch delay slot in microMIPS mode, we cannot swap.  */
+  pinfo2 = ip->insn_mo->pinfo2;
+  if (mips_opts.micromips
+      && (pinfo2 & INSN2_BRANCH_DELAY_16BIT)
+      && insn_length (history) != 2)
+    return FALSE;
+  if (mips_opts.micromips
+      && (pinfo2 & INSN2_BRANCH_DELAY_32BIT)
+      && insn_length (history) != 4)
+    return FALSE;
 
-       case 'o':
-         macro_read_relocs (&args, r);
-         continue;
+  /* On R5900 short loops need to be fixed by inserting a nop in
+     the branch delay slots.
+     A short loop can be terminated too early.  */
+  if (mips_opts.arch == CPU_R5900
+      /* Check if instruction has a parameter, ignore "j $31". */
+      && (address_expr != NULL)
+      /* Parameter must be 16 bit. */
+      && (*reloc_type == BFD_RELOC_16_PCREL_S2)
+      /* Branch to same segment. */
+      && (S_GET_SEGMENT(address_expr->X_add_symbol) == now_seg)
+      /* Branch to same code fragment. */
+      && (symbol_get_frag(address_expr->X_add_symbol) == frag_now)
+      /* Can only calculate branch offset if value is known. */
+      && symbol_constant_p(address_expr->X_add_symbol)
+      /* Check if branch is really conditional. */
+      && !((ip->insn_opcode & 0xffff0000) == 0x10000000   /* beq $0,$0 */
+       || (ip->insn_opcode & 0xffff0000) == 0x04010000   /* bgez $0 */
+       || (ip->insn_opcode & 0xffff0000) == 0x04110000)) /* bgezal $0 */
+    {
+      int distance;
+      /* Check if loop is shorter than 6 instructions including
+         branch and delay slot.  */
+      distance = frag_now_fix() - S_GET_VALUE(address_expr->X_add_symbol);
+      if (distance <= 20)
+        {
+          int i;
+          int rv;
 
-       case 'u':
-         macro_read_relocs (&args, r);
-         gas_assert (ep != NULL
-                     && (ep->X_op == O_constant
-                         || (ep->X_op == O_symbol
-                             && (*r == BFD_RELOC_MIPS_HIGHEST
-                                 || *r == BFD_RELOC_HI16_S
-                                 || *r == BFD_RELOC_HI16
-                                 || *r == BFD_RELOC_GPREL16
-                                 || *r == BFD_RELOC_MIPS_GOT_HI16
-                                 || *r == BFD_RELOC_MIPS_CALL_HI16))));
-         continue;
+          rv = FALSE;
+          /* When the loop includes branches or jumps,
+             it is not a short loop. */
+          for (i = 0; i < (distance / 4); i++)
+            {
+              if ((history[i].cleared_p)
+                  || delayed_branch_p(&history[i]))
+                {
+                  rv = TRUE;
+                  break;
+                }
+            }
+          if (rv == FALSE)
+            {
+              /* Insert nop after branch to fix short loop. */
+              return FALSE;
+            }
+        }
+    }
 
-       case 'p':
-         gas_assert (ep != NULL);
+  return TRUE;
+}
 
-         /*
-          * This allows macro() to pass an immediate expression for
-          * creating short branches without creating a symbol.
-          *
-          * We don't allow branch relaxation for these branches, as
-          * they should only appear in ".set nomacro" anyway.
-          */
-         if (ep->X_op == O_constant)
-           {
-             /* For microMIPS we always use relocations for branches.
-                So we should not resolve immediate values.  */
-             gas_assert (!mips_opts.micromips);
+/* Decide how we should add IP to the instruction stream.
+   ADDRESS_EXPR is an operand of the instruction to be used with
+   RELOC_TYPE.  */
 
-             if ((ep->X_add_number & 3) != 0)
-               as_bad (_("branch to misaligned address (0x%lx)"),
-                       (unsigned long) ep->X_add_number);
-             if ((ep->X_add_number + 0x20000) & ~0x3ffff)
-               as_bad (_("branch address range overflow (0x%lx)"),
-                       (unsigned long) ep->X_add_number);
-             insn.insn_opcode |= (ep->X_add_number >> 2) & 0xffff;
-             ep = NULL;
-           }
-         else
-           *r = BFD_RELOC_16_PCREL_S2;
-         continue;
+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;
 
-       case 'a':
-         gas_assert (ep != NULL);
-         *r = BFD_RELOC_MIPS_JMP;
-         continue;
+  /* The relaxed version of a macro sequence must be inherently
+     hazard-free.  */
+  if (mips_relax.sequence == 2)
+    return APPEND_ADD;
 
-       case 'C':
-         gas_assert (!mips_opts.micromips);
-         INSERT_OPERAND (0, COPZ, insn, va_arg (args, unsigned long));
-         continue;
+  /* We must not dabble with instructions in a ".set norerorder" block.  */
+  if (mips_opts.noreorder)
+    return APPEND_ADD;
 
-       case 'k':
-         INSERT_OPERAND (mips_opts.micromips,
-                         CACHE, insn, va_arg (args, unsigned long));
-         continue;
+  /* Otherwise, it's our responsibility to fill branch delay slots.  */
+  if (delayed_branch_p (ip))
+    {
+      if (!branch_likely_p (ip)
+         && can_swap_branch_p (ip, address_expr, reloc_type))
+       return APPEND_SWAP;
 
-       case '|':
-         gas_assert (mips_opts.micromips);
-         INSERT_OPERAND (1, TRAP, insn, va_arg (args, int));
-         continue;
+      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))
+       return APPEND_ADD_COMPACT;
 
-       case '.':
-         gas_assert (mips_opts.micromips);
-         INSERT_OPERAND (1, OFFSET10, insn, va_arg (args, int));
-         continue;
+      return APPEND_ADD_WITH_NOP;
+    }
 
-       case '\\':
-         INSERT_OPERAND (mips_opts.micromips,
-                         3BITPOS, insn, va_arg (args, unsigned int));
-         continue;
+  return APPEND_ADD;
+}
 
-       case '~':
-         INSERT_OPERAND (mips_opts.micromips,
-                         OFFSET12, insn, va_arg (args, unsigned long));
-         continue;
+/* IP is a MIPS16 instruction whose opcode we have just changed.
+   Point IP->insn_mo to the new opcode's definition.  */
 
-       case 'N':
-         gas_assert (mips_opts.micromips);
-         INSERT_OPERAND (1, BCC, insn, va_arg (args, int));
-         continue;
+static void
+find_altered_mips16_opcode (struct mips_cl_insn *ip)
+{
+  const struct mips_opcode *mo, *end;
 
-       case 'm':       /* Opcode extension character.  */
-         gas_assert (mips_opts.micromips);
-         switch (*fmt++)
-           {
-           case 'j':
-             INSERT_OPERAND (1, MJ, insn, va_arg (args, int));
-             break;
+  end = &mips16_opcodes[bfd_mips16_num_opcodes];
+  for (mo = ip->insn_mo; mo < end; mo++)
+    if ((ip->insn_opcode & mo->mask) == mo->match)
+      {
+       ip->insn_mo = mo;
+       return;
+      }
+  abort ();
+}
 
-           case 'p':
-             INSERT_OPERAND (1, MP, insn, va_arg (args, int));
-             break;
+/* For microMIPS macros, we need to generate a local number label
+   as the target of branches.  */
+#define MICROMIPS_LABEL_CHAR           '\037'
+static unsigned long micromips_target_label;
+static char micromips_target_name[32];
 
-           case 'F':
-             INSERT_OPERAND (1, IMMF, insn, va_arg (args, int));
-             break;
+static char *
+micromips_label_name (void)
+{
+  char *p = micromips_target_name;
+  char symbol_name_temporary[24];
+  unsigned long l;
+  int i;
 
-           default:
-             abort ();
-           }
-         continue;
+  if (*p)
+    return p;
 
-       default:
-         abort ();
-       }
-      break;
+  i = 0;
+  l = micromips_target_label;
+#ifdef LOCAL_LABEL_PREFIX
+  *p++ = LOCAL_LABEL_PREFIX;
+#endif
+  *p++ = 'L';
+  *p++ = MICROMIPS_LABEL_CHAR;
+  do
+    {
+      symbol_name_temporary[i++] = l % 10 + '0';
+      l /= 10;
     }
-  va_end (args);
-  gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
+  while (l != 0);
+  while (i > 0)
+    *p++ = symbol_name_temporary[--i];
+  *p = '\0';
 
-  append_insn (&insn, ep, r, TRUE);
+  return micromips_target_name;
 }
 
 static void
-mips16_macro_build (expressionS *ep, const char *name, const char *fmt,
-                   va_list *args)
+micromips_label_expr (expressionS *label_expr)
 {
-  struct mips_opcode *mo;
-  struct mips_cl_insn insn;
-  bfd_reloc_code_real_type r[3]
-    = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
-
-  mo = (struct mips_opcode *) hash_find (mips16_op_hash, name);
-  gas_assert (mo);
-  gas_assert (strcmp (name, mo->name) == 0);
+  label_expr->X_op = O_symbol;
+  label_expr->X_add_symbol = symbol_find_or_make (micromips_label_name ());
+  label_expr->X_add_number = 0;
+}
 
-  while (strcmp (fmt, mo->args) != 0 || mo->pinfo == INSN_MACRO)
-    {
-      ++mo;
-      gas_assert (mo->name);
-      gas_assert (strcmp (name, mo->name) == 0);
+static void
+micromips_label_inc (void)
+{
+  micromips_target_label++;
+  *micromips_target_name = '\0';
+}
+
+static void
+micromips_add_label (void)
+{
+  symbolS *s;
+
+  s = colon (micromips_label_name ());
+  micromips_label_inc ();
+  S_SET_OTHER (s, ELF_ST_SET_MICROMIPS (S_GET_OTHER (s)));
+}
+
+/* If assembling microMIPS code, then return the microMIPS reloc
+   corresponding to the requested one if any.  Otherwise return
+   the reloc unchanged.  */
+
+static bfd_reloc_code_real_type
+micromips_map_reloc (bfd_reloc_code_real_type reloc)
+{
+  static const bfd_reloc_code_real_type relocs[][2] =
+    {
+      /* Keep sorted incrementally by the left-hand key.  */
+      { BFD_RELOC_16_PCREL_S2, BFD_RELOC_MICROMIPS_16_PCREL_S1 },
+      { BFD_RELOC_GPREL16, BFD_RELOC_MICROMIPS_GPREL16 },
+      { BFD_RELOC_MIPS_JMP, BFD_RELOC_MICROMIPS_JMP },
+      { BFD_RELOC_HI16, BFD_RELOC_MICROMIPS_HI16 },
+      { BFD_RELOC_HI16_S, BFD_RELOC_MICROMIPS_HI16_S },
+      { BFD_RELOC_LO16, BFD_RELOC_MICROMIPS_LO16 },
+      { BFD_RELOC_MIPS_LITERAL, BFD_RELOC_MICROMIPS_LITERAL },
+      { BFD_RELOC_MIPS_GOT16, BFD_RELOC_MICROMIPS_GOT16 },
+      { BFD_RELOC_MIPS_CALL16, BFD_RELOC_MICROMIPS_CALL16 },
+      { BFD_RELOC_MIPS_GOT_HI16, BFD_RELOC_MICROMIPS_GOT_HI16 },
+      { BFD_RELOC_MIPS_GOT_LO16, BFD_RELOC_MICROMIPS_GOT_LO16 },
+      { BFD_RELOC_MIPS_CALL_HI16, BFD_RELOC_MICROMIPS_CALL_HI16 },
+      { BFD_RELOC_MIPS_CALL_LO16, BFD_RELOC_MICROMIPS_CALL_LO16 },
+      { BFD_RELOC_MIPS_SUB, BFD_RELOC_MICROMIPS_SUB },
+      { BFD_RELOC_MIPS_GOT_PAGE, BFD_RELOC_MICROMIPS_GOT_PAGE },
+      { BFD_RELOC_MIPS_GOT_OFST, BFD_RELOC_MICROMIPS_GOT_OFST },
+      { BFD_RELOC_MIPS_GOT_DISP, BFD_RELOC_MICROMIPS_GOT_DISP },
+      { BFD_RELOC_MIPS_HIGHEST, BFD_RELOC_MICROMIPS_HIGHEST },
+      { BFD_RELOC_MIPS_HIGHER, BFD_RELOC_MICROMIPS_HIGHER },
+      { BFD_RELOC_MIPS_SCN_DISP, BFD_RELOC_MICROMIPS_SCN_DISP },
+      { BFD_RELOC_MIPS_TLS_GD, BFD_RELOC_MICROMIPS_TLS_GD },
+      { BFD_RELOC_MIPS_TLS_LDM, BFD_RELOC_MICROMIPS_TLS_LDM },
+      { BFD_RELOC_MIPS_TLS_DTPREL_HI16, BFD_RELOC_MICROMIPS_TLS_DTPREL_HI16 },
+      { BFD_RELOC_MIPS_TLS_DTPREL_LO16, BFD_RELOC_MICROMIPS_TLS_DTPREL_LO16 },
+      { BFD_RELOC_MIPS_TLS_GOTTPREL, BFD_RELOC_MICROMIPS_TLS_GOTTPREL },
+      { BFD_RELOC_MIPS_TLS_TPREL_HI16, BFD_RELOC_MICROMIPS_TLS_TPREL_HI16 },
+      { BFD_RELOC_MIPS_TLS_TPREL_LO16, BFD_RELOC_MICROMIPS_TLS_TPREL_LO16 }
+    };
+  bfd_reloc_code_real_type r;
+  size_t i;
+
+  if (!mips_opts.micromips)
+    return reloc;
+  for (i = 0; i < ARRAY_SIZE (relocs); i++)
+    {
+      r = relocs[i][0];
+      if (r > reloc)
+       return reloc;
+      if (r == reloc)
+       return relocs[i][1];
     }
+  return reloc;
+}
 
-  create_insn (&insn, mo);
-  for (;;)
+/* Try to resolve relocation RELOC against constant OPERAND at assembly time.
+   Return true on success, storing the resolved value in RESULT.  */
+
+static bfd_boolean
+calculate_reloc (bfd_reloc_code_real_type reloc, offsetT operand,
+                offsetT *result)
+{
+  switch (reloc)
     {
-      int c;
+    case BFD_RELOC_MIPS_HIGHEST:
+    case BFD_RELOC_MICROMIPS_HIGHEST:
+      *result = ((operand + 0x800080008000ull) >> 48) & 0xffff;
+      return TRUE;
 
-      c = *fmt++;
-      switch (c)
-       {
-       case '\0':
-         break;
+    case BFD_RELOC_MIPS_HIGHER:
+    case BFD_RELOC_MICROMIPS_HIGHER:
+      *result = ((operand + 0x80008000ull) >> 32) & 0xffff;
+      return TRUE;
 
-       case ',':
-       case '(':
-       case ')':
-         continue;
+    case BFD_RELOC_HI16_S:
+    case BFD_RELOC_MICROMIPS_HI16_S:
+    case BFD_RELOC_MIPS16_HI16_S:
+      *result = ((operand + 0x8000) >> 16) & 0xffff;
+      return TRUE;
 
-       case 'y':
-       case 'w':
-         MIPS16_INSERT_OPERAND (RY, insn, va_arg (*args, int));
-         continue;
+    case BFD_RELOC_HI16:
+    case BFD_RELOC_MICROMIPS_HI16:
+    case BFD_RELOC_MIPS16_HI16:
+      *result = (operand >> 16) & 0xffff;
+      return TRUE;
 
-       case 'x':
-       case 'v':
-         MIPS16_INSERT_OPERAND (RX, insn, va_arg (*args, int));
-         continue;
+    case BFD_RELOC_LO16:
+    case BFD_RELOC_MICROMIPS_LO16:
+    case BFD_RELOC_MIPS16_LO16:
+      *result = operand & 0xffff;
+      return TRUE;
 
-       case 'z':
-         MIPS16_INSERT_OPERAND (RZ, insn, va_arg (*args, int));
-         continue;
+    case BFD_RELOC_UNUSED:
+      *result = operand;
+      return TRUE;
 
-       case 'Z':
-         MIPS16_INSERT_OPERAND (MOVE32Z, insn, va_arg (*args, int));
-         continue;
+    default:
+      return FALSE;
+    }
+}
 
-       case '0':
-       case 'S':
-       case 'P':
-       case 'R':
-         continue;
+/* Output an instruction.  IP is the instruction information.
+   ADDRESS_EXPR is an operand of the instruction to be used with
+   RELOC_TYPE.  EXPANSIONP is true if the instruction is part of
+   a macro expansion.  */
 
-       case 'X':
-         MIPS16_INSERT_OPERAND (REGR32, insn, va_arg (*args, int));
-         continue;
+static void
+append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
+            bfd_reloc_code_real_type *reloc_type, bfd_boolean expansionp)
+{
+  unsigned long prev_pinfo2, pinfo;
+  bfd_boolean relaxed_branch = FALSE;
+  enum append_method method;
+  bfd_boolean relax32;
+  int branch_disp;
+
+  if (mips_fix_loongson2f && !HAVE_CODE_COMPRESSION)
+    fix_loongson2f (ip);
+
+  file_ase_mips16 |= mips_opts.mips16;
+  file_ase_micromips |= mips_opts.micromips;
+
+  prev_pinfo2 = history[0].insn_mo->pinfo2;
+  pinfo = ip->insn_mo->pinfo;
+
+  if (mips_opts.micromips
+      && !expansionp
+      && (((prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0
+          && 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"),
+            (prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0 ? 16 : 32);
 
-       case 'Y':
+  if (address_expr == NULL)
+    ip->complete_p = 1;
+  else if (reloc_type[0] <= BFD_RELOC_UNUSED
+          && reloc_type[1] == BFD_RELOC_UNUSED
+          && reloc_type[2] == BFD_RELOC_UNUSED
+          && address_expr->X_op == O_constant)
+    {
+      switch (*reloc_type)
+       {
+       case BFD_RELOC_MIPS_JMP:
          {
-           int regno;
+           int shift;
 
-           regno = va_arg (*args, int);
-           regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3);
-           MIPS16_INSERT_OPERAND (REG32R, insn, regno);
+           shift = mips_opts.micromips ? 1 : 2;
+           if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
+             as_bad (_("jump to misaligned address (0x%lx)"),
+                     (unsigned long) address_expr->X_add_number);
+           ip->insn_opcode |= ((address_expr->X_add_number >> shift)
+                               & 0x3ffffff);
+           ip->complete_p = 1;
          }
-         continue;
+         break;
 
-       case '<':
-       case '>':
-       case '4':
-       case '5':
-       case 'H':
-       case 'W':
-       case 'D':
-       case 'j':
-       case '8':
-       case 'V':
-       case 'C':
-       case 'U':
-       case 'k':
-       case 'K':
-       case 'p':
-       case 'q':
-         {
-           offsetT value;
+       case BFD_RELOC_MIPS16_JMP:
+         if ((address_expr->X_add_number & 3) != 0)
+           as_bad (_("jump to misaligned address (0x%lx)"),
+                   (unsigned long) address_expr->X_add_number);
+         ip->insn_opcode |=
+           (((address_expr->X_add_number & 0x7c0000) << 3)
+              | ((address_expr->X_add_number & 0xf800000) >> 7)
+              | ((address_expr->X_add_number & 0x3fffc) >> 2));
+         ip->complete_p = 1;
+         break;
 
-           gas_assert (ep != NULL);
+       case BFD_RELOC_16_PCREL_S2:
+         {
+           int shift;
 
-           if (ep->X_op != O_constant)
-             *r = (int) BFD_RELOC_UNUSED + c;
-           else if (calculate_reloc (*r, ep->X_add_number, &value))
+           shift = mips_opts.micromips ? 1 : 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 (!mips_relax_branch)
              {
-               mips16_immed (NULL, 0, c, *r, value, 0, &insn.insn_opcode);
-               ep = NULL;
-               *r = BFD_RELOC_UNUSED;
+               if ((address_expr->X_add_number + (1 << (shift + 15)))
+                   & ~((1 << (shift + 16)) - 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)
+                                   & 0xffff);
              }
          }
-         continue;
+         break;
 
-       case '6':
-         MIPS16_INSERT_OPERAND (IMM6, insn, va_arg (*args, int));
-         continue;
-       }
+       default:
+         {
+           offsetT value;
 
-      break;
+           if (calculate_reloc (*reloc_type, address_expr->X_add_number,
+                                &value))
+             {
+               ip->insn_opcode |= value & 0xffff;
+               ip->complete_p = 1;
+             }
+         }
+         break;
+       }
     }
 
-  gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
-
-  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);
-}
+  if (mips_relax.sequence != 2 && !mips_opts.noreorder)
+    {
+      /* There are a lot of optimizations we could do that we don't.
+        In particular, we do not, in general, reorder instructions.
+        If you use gcc with optimization, it will reorder
+        instructions and generally do much more optimization then we
+        do here; repeating all that work in the assembler would only
+        benefit hand written assembly code, and does not seem worth
       it.  */
+      int nops = (mips_optimize == 0
+                 ? nops_for_insn (0, history, NULL)
+                 : nops_for_insn_or_target (0, history, ip));
+      if (nops > 0)
+       {
+         fragS *old_frag;
+         unsigned long old_frag_offset;
+         int i;
 
-/*
- * 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);
-}
+         old_frag = frag_now;
+         old_frag_offset = frag_now_fix ();
 
-/*
- * Generate a "jalr" instruction with a relocation hint to the called
- * function.  This occurs in NewABI PIC code.
- */
-static void
-macro_build_jalr (expressionS *ep, int cprestore)
-{
-  static const bfd_reloc_code_real_type jalr_relocs[2]
-    = { BFD_RELOC_MIPS_JALR, BFD_RELOC_MICROMIPS_JALR };
-  bfd_reloc_code_real_type jalr_reloc = jalr_relocs[mips_opts.micromips];
-  const char *jalr;
-  char *f = NULL;
+         for (i = 0; i < nops; i++)
+           add_fixed_insn (NOP_INSN);
+         insert_into_history (0, nops, NOP_INSN);
 
-  if (MIPS_JALR_HINT_P (ep))
-    {
-      frag_grow (8);
-      f = frag_more (0);
+         if (listing)
+           {
+             listing_prev_line ();
+             /* We may be at the start of a variant frag.  In case we
+                 are, make sure there is enough space for the frag
+                 after the frags created by listing_prev_line.  The
+                 argument to frag_grow here must be at least as large
+                 as the argument to all other calls to frag_grow in
+                 this file.  We don't have to worry about being in the
+                 middle of a variant frag, because the variants insert
+                 all needed nop instructions themselves.  */
+             frag_grow (40);
+           }
+
+         mips_move_text_labels ();
+
+#ifndef NO_ECOFF_DEBUGGING
+         if (ECOFF_DEBUGGING)
+           ecoff_fix_loc (old_frag, old_frag_offset);
+#endif
+       }
     }
-  if (mips_opts.micromips)
+  else if (mips_relax.sequence != 2 && prev_nop_frag != NULL)
     {
-      jalr = mips_opts.noreorder && !cprestore ? "jalr" : "jalrs";
-      if (MIPS_JALR_HINT_P (ep)
-         || (history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
-       macro_build (NULL, jalr, "t,s", RA, PIC_CALL_REG);
-      else
-       macro_build (NULL, jalr, "mj", PIC_CALL_REG);
-    }
-  else
-    macro_build (NULL, "jalr", "d,s", RA, PIC_CALL_REG);
-  if (MIPS_JALR_HINT_P (ep))
-    fix_new_exp (frag_now, f - frag_now->fr_literal, 4, ep, FALSE, jalr_reloc);
-}
+      int nops;
 
-/*
- * Generate a "lui" instruction.
- */
-static void
-macro_build_lui (expressionS *ep, int regnum)
-{
-  gas_assert (! mips_opts.mips16);
+      /* Work out how many nops in prev_nop_frag are needed by IP,
+        ignoring hazards generated by the first prev_nop_frag_since
+        instructions.  */
+      nops = nops_for_insn_or_target (prev_nop_frag_since, history, ip);
+      gas_assert (nops <= prev_nop_frag_holds);
 
-  if (ep->X_op != O_constant)
-    {
-      gas_assert (ep->X_op == O_symbol);
-      /* _gp_disp is a special case, used from s_cpload.
-        __gnu_local_gp is used if mips_no_shared.  */
-      gas_assert (mips_pic == NO_PIC
-             || (! HAVE_NEWABI
-                 && strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0)
-             || (! mips_in_shared
-                 && strcmp (S_GET_NAME (ep->X_add_symbol),
-                             "__gnu_local_gp") == 0));
+      /* Enforce NOPS as a minimum.  */
+      if (nops > prev_nop_frag_required)
+       prev_nop_frag_required = nops;
+
+      if (prev_nop_frag_holds == prev_nop_frag_required)
+       {
+         /* Settle for the current number of nops.  Update the history
+            accordingly (for the benefit of any future .set reorder code).  */
+         prev_nop_frag = NULL;
+         insert_into_history (prev_nop_frag_since,
+                              prev_nop_frag_holds, NOP_INSN);
+       }
+      else
+       {
+         /* Allow this instruction to replace one of the nops that was
+            tentatively added to prev_nop_frag.  */
+         prev_nop_frag->fr_fix -= NOP_INSN_SIZE;
+         prev_nop_frag_holds--;
+         prev_nop_frag_since++;
+       }
     }
 
-  macro_build (ep, "lui", LUI_FMT, regnum, BFD_RELOC_HI16_S);
-}
+  method = get_append_method (ip, address_expr, reloc_type);
+  branch_disp = method == APPEND_SWAP ? insn_length (history) : 0;
 
-/* Generate a sequence of instructions to do a load or store from a constant
-   offset off of a base register (breg) into/from a target register (treg),
-   using AT if necessary.  */
-static void
-macro_build_ldst_constoffset (expressionS *ep, const char *op,
-                             int treg, int breg, int dbl)
-{
-  gas_assert (ep->X_op == O_constant);
+  dwarf2_emit_insn (0);
+  /* We want MIPS16 and microMIPS debug info to use ISA-encoded addresses,
+     so "move" the instruction address accordingly.
 
-  /* Sign-extending 32-bit constants makes their handling easier.  */
-  if (!dbl)
-    normalize_constant_expr (ep);
+     Also, it doesn't seem appropriate for the assembler to reorder .loc
+     entries.  If this instruction is a branch that we are going to swap
+     with the previous instruction, the two instructions should be
+     treated as a unit, and the debug information for both instructions
+     should refer to the start of the branch sequence.  Using the
+     current position is certainly wrong when swapping a 32-bit branch
+     and a 16-bit delay slot, since the current position would then be
+     in the middle of a branch.  */
+  dwarf2_move_insn ((HAVE_CODE_COMPRESSION ? 1 : 0) - branch_disp);
 
-  /* Right now, this routine can only handle signed 32-bit constants.  */
-  if (! IS_SEXT_32BIT_NUM(ep->X_add_number + 0x8000))
-    as_warn (_("operand overflow"));
+  relax32 = (mips_relax_branch
+            /* Don't try branch relaxation within .set nomacro, or within
+               .set noat if we use $at for PIC computations.  If it turns
+               out that the branch was out-of-range, we'll get an error.  */
+            && !mips_opts.warn_about_macros
+            && (mips_opts.at || mips_pic == NO_PIC)
+            /* Don't relax BPOSGE32/64 or BC1ANY2T/F and BC1ANY4T/F
+               as they have no complementing branches.  */
+            && !(ip->insn_mo->ase & (ASE_MIPS3D | ASE_DSP64 | ASE_DSP)));
 
-  if (IS_SEXT_16BIT_NUM(ep->X_add_number))
+  if (!HAVE_CODE_COMPRESSION
+      && address_expr
+      && relax32
+      && *reloc_type == BFD_RELOC_16_PCREL_S2
+      && delayed_branch_p (ip))
     {
-      /* Signed 16-bit offset will fit in the op.  Easy!  */
-      macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, breg);
+      relaxed_branch = TRUE;
+      add_relaxed_insn (ip, (relaxed_branch_length
+                            (NULL, NULL,
+                             uncond_branch_p (ip) ? -1
+                             : branch_likely_p (ip) ? 1
+                             : 0)), 4,
+                       RELAX_BRANCH_ENCODE
+                       (AT,
+                        uncond_branch_p (ip),
+                        branch_likely_p (ip),
+                        pinfo & INSN_WRITE_GPR_31,
+                        0),
+                       address_expr->X_add_symbol,
+                       address_expr->X_add_number);
+      *reloc_type = BFD_RELOC_UNUSED;
     }
-  else
+  else if (mips_opts.micromips
+          && address_expr
+          && ((relax32 && *reloc_type == BFD_RELOC_16_PCREL_S2)
+              || *reloc_type > BFD_RELOC_UNUSED)
+          && (delayed_branch_p (ip) || compact_branch_p (ip))
+          /* Don't try branch relaxation when users specify
+             16-bit/32-bit instructions.  */
+          && !forced_insn_length)
     {
-      /* 32-bit offset, need multiple instructions and AT, like:
-          lui      $tempreg,const_hi       (BFD_RELOC_HI16_S)
-          addu     $tempreg,$tempreg,$breg
-           <op>     $treg,const_lo($tempreg)   (BFD_RELOC_LO16)
-         to handle the complete offset.  */
-      macro_build_lui (ep, AT);
-      macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
-      macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, AT);
+      bfd_boolean relax16 = *reloc_type > BFD_RELOC_UNUSED;
+      int type = relax16 ? *reloc_type - BFD_RELOC_UNUSED : 0;
+      int uncond = uncond_branch_p (ip) ? -1 : 0;
+      int compact = compact_branch_p (ip);
+      int al = pinfo & INSN_WRITE_GPR_31;
+      int length32;
 
-      if (!mips_opts.at)
-       as_bad (_("Macro used $at after \".set noat\""));
-    }
-}
+      gas_assert (address_expr != NULL);
+      gas_assert (!mips_relax.sequence);
 
-/*                     set_at()
- * Generates code to set the $at register to true (one)
- * if reg is less than the immediate expression.
- */
-static void
-set_at (int reg, int unsignedp)
-{
-  if (imm_expr.X_op == O_constant
-      && 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);
-      macro_build (NULL, unsignedp ? "sltu" : "slt", "d,v,t", AT, reg, AT);
+      relaxed_branch = TRUE;
+      length32 = relaxed_micromips_32bit_branch_length (NULL, NULL, uncond);
+      add_relaxed_insn (ip, relax32 ? length32 : 4, relax16 ? 2 : 4,
+                       RELAX_MICROMIPS_ENCODE (type, AT, uncond, compact, al,
+                                               relax32, 0, 0),
+                       address_expr->X_add_symbol,
+                       address_expr->X_add_number);
+      *reloc_type = BFD_RELOC_UNUSED;
     }
-}
-
-/* Warn if an expression is not a constant.  */
-
-static void
-check_absolute_expr (struct mips_cl_insn *ip, expressionS *ex)
-{
-  if (ex->X_op == O_big)
-    as_bad (_("unsupported large constant"));
-  else if (ex->X_op != O_constant)
-    as_bad (_("Instruction %s requires absolute expression"),
-           ip->insn_mo->name);
-
-  if (HAVE_32BIT_GPRS)
-    normalize_constant_expr (ex);
-}
-
-/* Count the leading zeroes by performing a binary chop. This is a
-   bulky bit of source, but performance is a LOT better for the
-   majority of values than a simple loop to count the bits:
-       for (lcnt = 0; (lcnt < 32); lcnt++)
-         if ((v) & (1 << (31 - lcnt)))
-           break;
-  However it is not code size friendly, and the gain will drop a bit
-  on certain cached systems.
-*/
-#define COUNT_TOP_ZEROES(v)             \
-  (((v) & ~0xffff) == 0                 \
-   ? ((v) & ~0xff) == 0                 \
-     ? ((v) & ~0xf) == 0                \
-       ? ((v) & ~0x3) == 0              \
-         ? ((v) & ~0x1) == 0            \
-           ? !(v)                       \
-             ? 32                       \
-             : 31                       \
-           : 30                         \
-         : ((v) & ~0x7) == 0            \
-           ? 29                         \
-           : 28                         \
-       : ((v) & ~0x3f) == 0             \
-         ? ((v) & ~0x1f) == 0           \
-           ? 27                         \
-           : 26                         \
-         : ((v) & ~0x7f) == 0           \
-           ? 25                         \
-           : 24                         \
-     : ((v) & ~0xfff) == 0              \
-       ? ((v) & ~0x3ff) == 0            \
-         ? ((v) & ~0x1ff) == 0          \
-           ? 23                         \
-           : 22                         \
-         : ((v) & ~0x7ff) == 0          \
-           ? 21                         \
-           : 20                         \
-       : ((v) & ~0x3fff) == 0           \
-         ? ((v) & ~0x1fff) == 0         \
-           ? 19                         \
-           : 18                         \
-         : ((v) & ~0x7fff) == 0         \
-           ? 17                         \
-           : 16                         \
-   : ((v) & ~0xffffff) == 0             \
-     ? ((v) & ~0xfffff) == 0            \
-       ? ((v) & ~0x3ffff) == 0          \
-         ? ((v) & ~0x1ffff) == 0        \
-           ? 15                         \
-           : 14                         \
-         : ((v) & ~0x7ffff) == 0        \
-           ? 13                         \
-           : 12                         \
-       : ((v) & ~0x3fffff) == 0         \
-         ? ((v) & ~0x1fffff) == 0       \
-           ? 11                         \
-           : 10                         \
-         : ((v) & ~0x7fffff) == 0       \
-           ? 9                          \
-           : 8                          \
-     : ((v) & ~0xfffffff) == 0          \
-       ? ((v) & ~0x3ffffff) == 0        \
-         ? ((v) & ~0x1ffffff) == 0      \
-           ? 7                          \
-           : 6                          \
-         : ((v) & ~0x7ffffff) == 0      \
-           ? 5                          \
-           : 4                          \
-       : ((v) & ~0x3fffffff) == 0       \
-         ? ((v) & ~0x1fffffff) == 0     \
-           ? 3                          \
-           : 2                          \
-         : ((v) & ~0x7fffffff) == 0     \
-           ? 1                          \
-           : 0)
-
-/*                     load_register()
- *  This routine generates the least number of instructions necessary to load
- *  an absolute expression value into a register.
- */
-static void
-load_register (int reg, expressionS *ep, int dbl)
-{
-  int freg;
-  expressionS hi32, lo32;
-
-  if (ep->X_op != O_big)
+  else if (mips_opts.mips16 && *reloc_type > BFD_RELOC_UNUSED)
     {
-      gas_assert (ep->X_op == O_constant);
+      /* We need to set up a variant frag.  */
+      gas_assert (address_expr != NULL);
+      add_relaxed_insn (ip, 4, 0,
+                       RELAX_MIPS16_ENCODE
+                       (*reloc_type - BFD_RELOC_UNUSED,
+                        forced_insn_length == 2, forced_insn_length == 4,
+                        delayed_branch_p (&history[0]),
+                        history[0].mips16_absolute_jump_p),
+                       make_expr_symbol (address_expr), 0);
+    }
+  else if (mips_opts.mips16 && insn_length (ip) == 2)
+    {
+      if (!delayed_branch_p (ip))
+       /* Make sure there is enough room to swap this instruction with
+          a following jump instruction.  */
+       frag_grow (6);
+      add_fixed_insn (ip);
+    }
+  else
+    {
+      if (mips_opts.mips16
+         && mips_opts.noreorder
+         && delayed_branch_p (&history[0]))
+       as_warn (_("extended instruction in delay slot"));
 
-      /* Sign-extending 32-bit constants makes their handling easier.  */
-      if (!dbl)
-       normalize_constant_expr (ep);
+      if (mips_relax.sequence)
+       {
+         /* If we've reached the end of this frag, turn it into a variant
+            frag and record the information for the instructions we've
+            written so far.  */
+         if (frag_room () < 4)
+           relax_close_frag ();
+         mips_relax.sizes[mips_relax.sequence - 1] += insn_length (ip);
+       }
 
-      if (IS_SEXT_16BIT_NUM (ep->X_add_number))
+      if (mips_relax.sequence != 2)
        {
-         /* We can handle 16 bit signed values with an addiu to
-            $zero.  No need to ever use daddiu here, since $zero and
-            the result are always correct in 32 bit mode.  */
-         macro_build (ep, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
-         return;
+         if (mips_macro_warning.first_insn_sizes[0] == 0)
+           mips_macro_warning.first_insn_sizes[0] = insn_length (ip);
+         mips_macro_warning.sizes[0] += insn_length (ip);
+         mips_macro_warning.insns[0]++;
        }
-      else if (ep->X_add_number >= 0 && ep->X_add_number < 0x10000)
+      if (mips_relax.sequence != 1)
        {
-         /* We can handle 16 bit unsigned values with an ori to
-             $zero.  */
-         macro_build (ep, "ori", "t,r,i", reg, 0, BFD_RELOC_LO16);
-         return;
+         if (mips_macro_warning.first_insn_sizes[1] == 0)
+           mips_macro_warning.first_insn_sizes[1] = insn_length (ip);
+         mips_macro_warning.sizes[1] += insn_length (ip);
+         mips_macro_warning.insns[1]++;
        }
-      else if ((IS_SEXT_32BIT_NUM (ep->X_add_number)))
+
+      if (mips_opts.mips16)
        {
-         /* 32 bit values require an lui.  */
-         macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
-         if ((ep->X_add_number & 0xffff) != 0)
-           macro_build (ep, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
-         return;
+         ip->fixed_p = 1;
+         ip->mips16_absolute_jump_p = (*reloc_type == BFD_RELOC_MIPS16_JMP);
        }
+      add_fixed_insn (ip);
     }
 
-  /* The value is larger than 32 bits.  */
-
-  if (!dbl || HAVE_32BIT_GPRS)
+  if (!ip->complete_p && *reloc_type < BFD_RELOC_UNUSED)
     {
-      char value[32];
-
-      sprintf_vma (value, ep->X_add_number);
-      as_bad (_("Number (0x%s) larger than 32 bits"), value);
-      macro_build (ep, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
-      return;
-    }
+      bfd_reloc_code_real_type final_type[3];
+      reloc_howto_type *howto0;
+      reloc_howto_type *howto;
+      int i;
 
-  if (ep->X_op != O_big)
-    {
-      hi32 = *ep;
-      hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
-      hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
-      hi32.X_add_number &= 0xffffffff;
-      lo32 = *ep;
-      lo32.X_add_number &= 0xffffffff;
-    }
-  else
-    {
-      gas_assert (ep->X_add_number > 2);
-      if (ep->X_add_number == 3)
-       generic_bignum[3] = 0;
-      else if (ep->X_add_number > 4)
-       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;
-      hi32.X_add_number = generic_bignum[2] + (generic_bignum[3] << 16);
-    }
+      /* Perform any necessary conversion to microMIPS relocations
+        and find out how many relocations there actually are.  */
+      for (i = 0; i < 3 && reloc_type[i] != BFD_RELOC_UNUSED; i++)
+       final_type[i] = micromips_map_reloc (reloc_type[i]);
 
-  if (hi32.X_add_number == 0)
-    freg = 0;
-  else
-    {
-      int shift, bit;
-      unsigned long hi, lo;
+      /* In a compound relocation, it is the final (outermost)
+        operator that determines the relocated field.  */
+      howto = howto0 = bfd_reloc_type_lookup (stdoutput, final_type[i - 1]);
+      if (!howto)
+       abort ();
 
-      if (hi32.X_add_number == (offsetT) 0xffffffff)
-       {
-         if ((lo32.X_add_number & 0xffff8000) == 0xffff8000)
-           {
-             macro_build (&lo32, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
-             return;
-           }
-         if (lo32.X_add_number & 0x80000000)
-           {
-             macro_build (&lo32, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
-             if (lo32.X_add_number & 0xffff)
-               macro_build (&lo32, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
-             return;
-           }
-       }
+      if (i > 1)
+       howto0 = bfd_reloc_type_lookup (stdoutput, final_type[0]);
+      ip->fixp[0] = fix_new_exp (ip->frag, ip->where,
+                                bfd_get_reloc_size (howto),
+                                address_expr,
+                                howto0 && howto0->pc_relative,
+                                final_type[0]);
 
-      /* Check for 16bit shifted constant.  We know that hi32 is
-         non-zero, so start the mask on the first bit of the hi32
-         value.  */
-      shift = 17;
-      do
-       {
-         unsigned long himask, lomask;
+      /* Tag symbols that have a R_MIPS16_26 relocation against them.  */
+      if (final_type[0] == BFD_RELOC_MIPS16_JMP && ip->fixp[0]->fx_addsy)
+       *symbol_get_tc (ip->fixp[0]->fx_addsy) = 1;
 
-         if (shift < 32)
-           {
-             himask = 0xffff >> (32 - shift);
-             lomask = (0xffff << shift) & 0xffffffff;
-           }
-         else
-           {
-             himask = 0xffff << (shift - 32);
-             lomask = 0;
-           }
-         if ((hi32.X_add_number & ~(offsetT) himask) == 0
-             && (lo32.X_add_number & ~(offsetT) lomask) == 0)
-           {
-             expressionS tmp;
+      /* These relocations can have an addend that won't fit in
+        4 octets for 64bit assembly.  */
+      if (HAVE_64BIT_GPRS
+         && ! howto->partial_inplace
+         && (reloc_type[0] == BFD_RELOC_16
+             || reloc_type[0] == BFD_RELOC_32
+             || reloc_type[0] == BFD_RELOC_MIPS_JMP
+             || reloc_type[0] == BFD_RELOC_GPREL16
+             || reloc_type[0] == BFD_RELOC_MIPS_LITERAL
+             || reloc_type[0] == BFD_RELOC_GPREL32
+             || reloc_type[0] == BFD_RELOC_64
+             || reloc_type[0] == BFD_RELOC_CTOR
+             || reloc_type[0] == BFD_RELOC_MIPS_SUB
+             || reloc_type[0] == BFD_RELOC_MIPS_HIGHEST
+             || reloc_type[0] == BFD_RELOC_MIPS_HIGHER
+             || reloc_type[0] == BFD_RELOC_MIPS_SCN_DISP
+             || reloc_type[0] == BFD_RELOC_MIPS_REL16
+             || reloc_type[0] == BFD_RELOC_MIPS_RELGOT
+             || reloc_type[0] == BFD_RELOC_MIPS16_GPREL
+             || hi16_reloc_p (reloc_type[0])
+             || lo16_reloc_p (reloc_type[0])))
+       ip->fixp[0]->fx_no_overflow = 1;
 
-             tmp.X_op = O_constant;
-             if (shift < 32)
-               tmp.X_add_number = ((hi32.X_add_number << (32 - shift))
-                                   | (lo32.X_add_number >> shift));
-             else
-               tmp.X_add_number = hi32.X_add_number >> (shift - 32);
-             macro_build (&tmp, "ori", "t,r,i", reg, 0, BFD_RELOC_LO16);
-             macro_build (NULL, (shift >= 32) ? "dsll32" : "dsll", SHFT_FMT,
-                          reg, reg, (shift >= 32) ? shift - 32 : shift);
-             return;
-           }
-         ++shift;
-       }
-      while (shift <= (64 - 16));
+      /* These relocations can have an addend that won't fit in 2 octets.  */
+      if (reloc_type[0] == BFD_RELOC_MICROMIPS_7_PCREL_S1
+         || reloc_type[0] == BFD_RELOC_MICROMIPS_10_PCREL_S1)
+       ip->fixp[0]->fx_no_overflow = 1;
 
-      /* Find the bit number of the lowest one bit, and store the
-         shifted value in hi/lo.  */
-      hi = (unsigned long) (hi32.X_add_number & 0xffffffff);
-      lo = (unsigned long) (lo32.X_add_number & 0xffffffff);
-      if (lo != 0)
+      if (mips_relax.sequence)
        {
-         bit = 0;
-         while ((lo & 1) == 0)
-           {
-             lo >>= 1;
-             ++bit;
-           }
-         lo |= (hi & (((unsigned long) 1 << bit) - 1)) << (32 - bit);
-         hi >>= bit;
+         if (mips_relax.first_fixup == 0)
+           mips_relax.first_fixup = ip->fixp[0];
        }
-      else
+      else if (reloc_needs_lo_p (*reloc_type))
        {
-         bit = 32;
-         while ((hi & 1) == 0)
-           {
-             hi >>= 1;
-             ++bit;
-           }
-         lo = hi;
-         hi = 0;
-       }
+         struct mips_hi_fixup *hi_fixup;
 
-      /* Optimize if the shifted value is a (power of 2) - 1.  */
-      if ((hi == 0 && ((lo + 1) & lo) == 0)
-         || (lo == 0xffffffff && ((hi + 1) & hi) == 0))
-       {
-         shift = COUNT_TOP_ZEROES ((unsigned int) hi32.X_add_number);
-         if (shift != 0)
+         /* Reuse the last entry if it already has a matching %lo.  */
+         hi_fixup = mips_hi_fixup_list;
+         if (hi_fixup == 0
+             || !fixup_has_matching_lo_p (hi_fixup->fixp))
            {
-             expressionS tmp;
-
-             /* This instruction will set the register to be all
-                 ones.  */
-             tmp.X_op = O_constant;
-             tmp.X_add_number = (offsetT) -1;
-             macro_build (&tmp, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
-             if (bit != 0)
-               {
-                 bit += shift;
-                 macro_build (NULL, (bit >= 32) ? "dsll32" : "dsll", SHFT_FMT,
-                              reg, reg, (bit >= 32) ? bit - 32 : bit);
-               }
-             macro_build (NULL, (shift >= 32) ? "dsrl32" : "dsrl", SHFT_FMT,
-                          reg, reg, (shift >= 32) ? shift - 32 : shift);
-             return;
+             hi_fixup = ((struct mips_hi_fixup *)
+                         xmalloc (sizeof (struct mips_hi_fixup)));
+             hi_fixup->next = mips_hi_fixup_list;
+             mips_hi_fixup_list = hi_fixup;
            }
+         hi_fixup->fixp = ip->fixp[0];
+         hi_fixup->seg = now_seg;
        }
 
-      /* Sign extend hi32 before calling load_register, because we can
-         generally get better code when we load a sign extended value.  */
-      if ((hi32.X_add_number & 0x80000000) != 0)
-       hi32.X_add_number |= ~(offsetT) 0xffffffff;
-      load_register (reg, &hi32, 0);
-      freg = reg;
+      /* Add fixups for the second and third relocations, if given.
+        Note that the ABI allows the second relocation to be
+        against RSS_UNDEF, RSS_GP, RSS_GP0 or RSS_LOC.  At the
+        moment we only use RSS_UNDEF, but we could add support
+        for the others if it ever becomes necessary.  */
+      for (i = 1; i < 3; i++)
+       if (reloc_type[i] != BFD_RELOC_UNUSED)
+         {
+           ip->fixp[i] = fix_new (ip->frag, ip->where,
+                                  ip->fixp[0]->fx_size, NULL, 0,
+                                  FALSE, final_type[i]);
+
+           /* Use fx_tcbit to mark compound relocs.  */
+           ip->fixp[0]->fx_tcbit = 1;
+           ip->fixp[i]->fx_tcbit = 1;
+         }
     }
-  if ((lo32.X_add_number & 0xffff0000) == 0)
+  install_insn (ip);
+
+  /* Update the register mask information.  */
+  mips_gprmask |= gpr_read_mask (ip) | gpr_write_mask (ip);
+  mips_cprmask[1] |= fpr_read_mask (ip) | fpr_write_mask (ip);
+
+  switch (method)
     {
-      if (freg != 0)
-       {
-         macro_build (NULL, "dsll32", SHFT_FMT, reg, freg, 0);
-         freg = reg;
-       }
+    case APPEND_ADD:
+      insert_into_history (0, 1, ip);
+      break;
+
+    case APPEND_ADD_WITH_NOP:
+      {
+       struct mips_cl_insn *nop;
+
+       insert_into_history (0, 1, ip);
+       nop = get_delay_slot_nop (ip);
+       add_fixed_insn (nop);
+       insert_into_history (0, 1, nop);
+       if (mips_relax.sequence)
+         mips_relax.sizes[mips_relax.sequence - 1] += insn_length (nop);
+      }
+      break;
+
+    case APPEND_ADD_COMPACT:
+      /* Convert MIPS16 jr/jalr into a "compact" jump.  */
+      gas_assert (mips_opts.mips16);
+      ip->insn_opcode |= 0x0080;
+      find_altered_mips16_opcode (ip);
+      install_insn (ip);
+      insert_into_history (0, 1, ip);
+      break;
+
+    case APPEND_SWAP:
+      {
+       struct mips_cl_insn delay = history[0];
+       if (mips_opts.mips16)
+         {
+           know (delay.frag == ip->frag);
+           move_insn (ip, delay.frag, delay.where);
+           move_insn (&delay, ip->frag, ip->where + insn_length (ip));
+         }
+       else if (relaxed_branch || delay.frag != ip->frag)
+         {
+           /* Add the delay slot instruction to the end of the
+              current frag and shrink the fixed part of the
+              original frag.  If the branch occupies the tail of
+              the latter, move it backwards to cover the gap.  */
+           delay.frag->fr_fix -= branch_disp;
+           if (delay.frag == ip->frag)
+             move_insn (ip, ip->frag, ip->where - branch_disp);
+           add_fixed_insn (&delay);
+         }
+       else
+         {
+           move_insn (&delay, ip->frag,
+                      ip->where - branch_disp + insn_length (ip));
+           move_insn (ip, history[0].frag, history[0].where);
+         }
+       history[0] = *ip;
+       delay.fixed_p = 1;
+       insert_into_history (0, 1, &delay);
+      }
+      break;
     }
-  else
+
+  /* If we have just completed an unconditional branch, clear the history.  */
+  if ((delayed_branch_p (&history[1]) && uncond_branch_p (&history[1]))
+      || (compact_branch_p (&history[0]) && uncond_branch_p (&history[0])))
     {
-      expressionS mid16;
+      unsigned int i;
 
-      if ((freg == 0) && (lo32.X_add_number == (offsetT) 0xffffffff))
-       {
-         macro_build (&lo32, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
-         macro_build (NULL, "dsrl32", SHFT_FMT, reg, reg, 0);
-         return;
-       }
+      mips_no_prev_insn ();
 
-      if (freg != 0)
-       {
-         macro_build (NULL, "dsll", SHFT_FMT, reg, freg, 16);
-         freg = reg;
-       }
-      mid16 = lo32;
-      mid16.X_add_number >>= 16;
-      macro_build (&mid16, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
-      macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
-      freg = reg;
+      for (i = 0; i < ARRAY_SIZE (history); i++)
+       history[i].cleared_p = 1;
     }
-  if ((lo32.X_add_number & 0xffff) != 0)
-    macro_build (&lo32, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
+
+  /* We need to emit a label at the end of branch-likely macros.  */
+  if (emit_branch_likely_macro)
+    {
+      emit_branch_likely_macro = FALSE;
+      micromips_add_label ();
+    }
+
+  /* We just output an insn, so the next one doesn't have a label.  */
+  mips_clear_insn_labels ();
 }
 
-static inline void
-load_delay_nop (void)
+/* Forget that there was any previous instruction or label.
+   When BRANCH is true, the branch history is also flushed.  */
+
+static void
+mips_no_prev_insn (void)
 {
-  if (!gpr_interlocks)
-    macro_build (NULL, "nop", "");
+  prev_nop_frag = NULL;
+  insert_into_history (0, ARRAY_SIZE (history), NOP_INSN);
+  mips_clear_insn_labels ();
 }
 
-/* Load an address into a register.  */
+/* This function must be called before we emit something other than
+   instructions.  It is like mips_no_prev_insn except that it inserts
+   any NOPS that might be needed by previous instructions.  */
 
-static void
-load_address (int reg, expressionS *ep, int *used_at)
+void
+mips_emit_delays (void)
 {
-  if (ep->X_op != O_constant
-      && ep->X_op != O_symbol)
+  if (! mips_opts.noreorder)
     {
-      as_bad (_("expression too complex"));
-      ep->X_op = O_constant;
+      int nops = nops_for_insn (0, history, NULL);
+      if (nops > 0)
+       {
+         while (nops-- > 0)
+           add_fixed_insn (NOP_INSN);
+         mips_move_text_labels ();
+       }
     }
+  mips_no_prev_insn ();
+}
 
-  if (ep->X_op == O_constant)
-    {
-      load_register (reg, ep, HAVE_64BIT_ADDRESSES);
-      return;
-    }
+/* Start a (possibly nested) noreorder block.  */
 
-  if (mips_pic == NO_PIC)
+static void
+start_noreorder (void)
+{
+  if (mips_opts.noreorder == 0)
     {
-      /* If this is a reference to a GP relative symbol, we want
-          addiu        $reg,$gp,<sym>          (BFD_RELOC_GPREL16)
-        Otherwise we want
-          lui          $reg,<sym>              (BFD_RELOC_HI16_S)
-          addiu        $reg,$reg,<sym>         (BFD_RELOC_LO16)
-        If we have an addend, we always use the latter form.
-
-        With 64bit address space and a usable $at we want
-          lui          $reg,<sym>              (BFD_RELOC_MIPS_HIGHEST)
-          lui          $at,<sym>               (BFD_RELOC_HI16_S)
-          daddiu       $reg,<sym>              (BFD_RELOC_MIPS_HIGHER)
-          daddiu       $at,<sym>               (BFD_RELOC_LO16)
-          dsll32       $reg,0
-          daddu        $reg,$reg,$at
+      unsigned int i;
+      int nops;
 
-        If $at is already in use, we use a path which is suboptimal
-        on superscalar processors.
-          lui          $reg,<sym>              (BFD_RELOC_MIPS_HIGHEST)
-          daddiu       $reg,<sym>              (BFD_RELOC_MIPS_HIGHER)
-          dsll         $reg,16
-          daddiu       $reg,<sym>              (BFD_RELOC_HI16_S)
-          dsll         $reg,16
-          daddiu       $reg,<sym>              (BFD_RELOC_LO16)
+      /* None of the instructions before the .set noreorder can be moved.  */
+      for (i = 0; i < ARRAY_SIZE (history); i++)
+       history[i].fixed_p = 1;
 
-        For GP relative symbols in 64bit address space we can use
-        the same sequence as in 32bit address space.  */
-      if (HAVE_64BIT_SYMBOLS)
+      /* Insert any nops that might be needed between the .set noreorder
+        block and the previous instructions.  We will later remove any
+        nops that turn out not to be needed.  */
+      nops = nops_for_insn (0, history, NULL);
+      if (nops > 0)
        {
-         if ((valueT) ep->X_add_number <= MAX_GPREL_OFFSET
-             && !nopic_need_relax (ep->X_add_symbol, 1))
+         if (mips_optimize != 0)
            {
-             relax_start (ep->X_add_symbol);
-             macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg,
-                          mips_gp_register, BFD_RELOC_GPREL16);
-             relax_switch ();
+             /* Record the frag which holds the nop instructions, so
+                 that we can remove them if we don't need them.  */
+             frag_grow (nops * NOP_INSN_SIZE);
+             prev_nop_frag = frag_now;
+             prev_nop_frag_holds = nops;
+             prev_nop_frag_required = 0;
+             prev_nop_frag_since = 0;
            }
 
-         if (*used_at == 0 && mips_opts.at)
-           {
-             macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_HIGHEST);
-             macro_build (ep, "lui", LUI_FMT, AT, BFD_RELOC_HI16_S);
-             macro_build (ep, "daddiu", "t,r,j", reg, reg,
-                          BFD_RELOC_MIPS_HIGHER);
-             macro_build (ep, "daddiu", "t,r,j", AT, AT, BFD_RELOC_LO16);
-             macro_build (NULL, "dsll32", SHFT_FMT, reg, reg, 0);
-             macro_build (NULL, "daddu", "d,v,t", reg, reg, AT);
-             *used_at = 1;
-           }
-         else
-           {
-             macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_HIGHEST);
-             macro_build (ep, "daddiu", "t,r,j", reg, reg,
-                          BFD_RELOC_MIPS_HIGHER);
-             macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
-             macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_HI16_S);
-             macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
-             macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_LO16);
-           }
+         for (; nops > 0; --nops)
+           add_fixed_insn (NOP_INSN);
 
-         if (mips_relax.sequence)
-           relax_end ();
-       }
-      else
-       {
-         if ((valueT) ep->X_add_number <= MAX_GPREL_OFFSET
-             && !nopic_need_relax (ep->X_add_symbol, 1))
-           {
-             relax_start (ep->X_add_symbol);
-             macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg,
-                          mips_gp_register, BFD_RELOC_GPREL16);
-             relax_switch ();
-           }
-         macro_build_lui (ep, reg);
-         macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j",
-                      reg, reg, BFD_RELOC_LO16);
-         if (mips_relax.sequence)
-           relax_end ();
+         /* Move on to a new frag, so that it is safe to simply
+            decrease the size of prev_nop_frag.  */
+         frag_wane (frag_now);
+         frag_new (0);
+         mips_move_text_labels ();
        }
+      mips_mark_labels ();
+      mips_clear_insn_labels ();
     }
-  else if (!mips_big_got)
-    {
-      expressionS ex;
-
-      /* If this is a reference to an external symbol, we want
-          lw           $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT16)
-        Otherwise we want
-          lw           $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT16)
-          nop
-          addiu        $reg,$reg,<sym>         (BFD_RELOC_LO16)
-        If there is a constant, it must be added in after.
+  mips_opts.noreorder++;
+  mips_any_noreorder = 1;
+}
 
-        If we have NewABI, we want
-          lw           $reg,<sym+cst>($gp)     (BFD_RELOC_MIPS_GOT_DISP)
-         unless we're referencing a global symbol with a non-zero
-         offset, in which case cst must be added separately.  */
-      if (HAVE_NEWABI)
-       {
-         if (ep->X_add_number)
-           {
-             ex.X_add_number = ep->X_add_number;
-             ep->X_add_number = 0;
-             relax_start (ep->X_add_symbol);
-             macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
-                          BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
-             if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
-               as_bad (_("PIC code offset overflow (max 16 signed bits)"));
-             ex.X_op = O_constant;
-             macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j",
-                          reg, reg, BFD_RELOC_LO16);
-             ep->X_add_number = ex.X_add_number;
-             relax_switch ();
-           }
-         macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
-                      BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
-         if (mips_relax.sequence)
-           relax_end ();
-       }
-      else
-       {
-         ex.X_add_number = ep->X_add_number;
-         ep->X_add_number = 0;
-         macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
-                      BFD_RELOC_MIPS_GOT16, mips_gp_register);
-         load_delay_nop ();
-         relax_start (ep->X_add_symbol);
-         relax_switch ();
-         macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
-                      BFD_RELOC_LO16);
-         relax_end ();
+/* End a nested noreorder block.  */
 
-         if (ex.X_add_number != 0)
-           {
-             if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
-               as_bad (_("PIC code offset overflow (max 16 signed bits)"));
-             ex.X_op = O_constant;
-             macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j",
-                          reg, reg, BFD_RELOC_LO16);
-           }
-       }
+static void
+end_noreorder (void)
+{
+  mips_opts.noreorder--;
+  if (mips_opts.noreorder == 0 && prev_nop_frag != NULL)
+    {
+      /* Commit to inserting prev_nop_frag_required nops and go back to
+        handling nop insertion the .set reorder way.  */
+      prev_nop_frag->fr_fix -= ((prev_nop_frag_holds - prev_nop_frag_required)
+                               * NOP_INSN_SIZE);
+      insert_into_history (prev_nop_frag_since,
+                          prev_nop_frag_required, NOP_INSN);
+      prev_nop_frag = NULL;
     }
-  else if (mips_big_got)
+}
+
+/* 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))
     {
-      expressionS ex;
+    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;
+}
 
-      /* This is the large GOT case.  If this is a reference to an
-        external symbol, we want
-          lui          $reg,<sym>              (BFD_RELOC_MIPS_GOT_HI16)
-          addu         $reg,$reg,$gp
-          lw           $reg,<sym>($reg)        (BFD_RELOC_MIPS_GOT_LO16)
+/* 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.  */
 
-        Otherwise, for a reference to a local symbol in old ABI, we want
-          lw           $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT16)
-          nop
-          addiu        $reg,$reg,<sym>         (BFD_RELOC_LO16)
-        If there is a constant, it must be added in after.
+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;
+}
 
-        In the NewABI, for local symbols, with or without offsets, we want:
-          lw           $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT_PAGE)
-          addiu        $reg,$reg,<sym>         (BFD_RELOC_MIPS_GOT_OFST)
-      */
-      if (HAVE_NEWABI)
-       {
-         ex.X_add_number = ep->X_add_number;
-         ep->X_add_number = 0;
-         relax_start (ep->X_add_symbol);
-         macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_GOT_HI16);
-         macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                      reg, reg, mips_gp_register);
-         macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)",
-                      reg, BFD_RELOC_MIPS_GOT_LO16, reg);
-         if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
-           as_bad (_("PIC code offset overflow (max 16 signed bits)"));
-         else if (ex.X_add_number)
-           {
-             ex.X_op = O_constant;
-             macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
-                          BFD_RELOC_LO16);
-           }
+/* Finish up a macro.  Emit warnings as appropriate.  */
 
-         ep->X_add_number = ex.X_add_number;
-         relax_switch ();
-         macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
-                      BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
-         macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
-                      BFD_RELOC_MIPS_GOT_OFST);
-         relax_end ();
-       }
-      else
-       {
-         ex.X_add_number = ep->X_add_number;
-         ep->X_add_number = 0;
-         relax_start (ep->X_add_symbol);
-         macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_GOT_HI16);
-         macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                      reg, reg, mips_gp_register);
-         macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)",
-                      reg, BFD_RELOC_MIPS_GOT_LO16, reg);
-         relax_switch ();
-         if (reg_needs_delay (mips_gp_register))
-           {
-             /* We need a nop before loading from $gp.  This special
-                check is required because the lui which starts the main
-                instruction stream does not refer to $gp, and so will not
-                insert the nop which may be required.  */
-             macro_build (NULL, "nop", "");
-           }
-         macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
-                      BFD_RELOC_MIPS_GOT16, mips_gp_register);
-         load_delay_nop ();
-         macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
-                      BFD_RELOC_LO16);
-         relax_end ();
+static void
+macro_end (void)
+{
+  /* Relaxation warning flags.  */
+  relax_substateT subtype = 0;
 
-         if (ex.X_add_number != 0)
-           {
-             if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
-               as_bad (_("PIC code offset overflow (max 16 signed bits)"));
-             ex.X_op = O_constant;
-             macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
-                          BFD_RELOC_LO16);
-           }
-       }
+  /* Check delay slot size requirements.  */
+  if (mips_macro_warning.delay_slot_length == 2)
+    subtype |= RELAX_DELAY_SLOT_16BIT;
+  if (mips_macro_warning.delay_slot_length != 0)
+    {
+      if (mips_macro_warning.delay_slot_length
+         != mips_macro_warning.first_insn_sizes[0])
+       subtype |= RELAX_DELAY_SLOT_SIZE_FIRST;
+      if (mips_macro_warning.delay_slot_length
+         != mips_macro_warning.first_insn_sizes[1])
+       subtype |= RELAX_DELAY_SLOT_SIZE_SECOND;
     }
-  else
-    abort ();
 
-  if (!mips_opts.at && *used_at == 1)
-    as_bad (_("Macro used $at after \".set noat\""));
-}
+  /* Check instruction count requirements.  */
+  if (mips_macro_warning.insns[0] > 1 || mips_macro_warning.insns[1] > 1)
+    {
+      if (mips_macro_warning.insns[1] > mips_macro_warning.insns[0])
+       subtype |= RELAX_SECOND_LONGER;
+      if (mips_opts.warn_about_macros)
+       subtype |= RELAX_NOMACRO;
+      if (mips_macro_warning.delay_slot_p)
+       subtype |= RELAX_DELAY_SLOT;
+    }
 
-/* Move the contents of register SOURCE into register DEST.  */
+  /* If both alternatives fail to fill a delay slot correctly,
+     emit the warning now.  */
+  if ((subtype & RELAX_DELAY_SLOT_SIZE_FIRST) != 0
+      && (subtype & RELAX_DELAY_SLOT_SIZE_SECOND) != 0)
+    {
+      relax_substateT s;
+      const char *msg;
 
-static void
-move_register (int dest, int source)
-{
-  /* Prefer to use a 16-bit microMIPS instruction unless the previous
-     instruction specifically requires a 32-bit one.  */
-  if (mips_opts.micromips
-      && !(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",
-                dest, source, 0);
-}
+      s = subtype & (RELAX_DELAY_SLOT_16BIT
+                    | RELAX_DELAY_SLOT_SIZE_FIRST
+                    | RELAX_DELAY_SLOT_SIZE_SECOND);
+      msg = macro_warning (s);
+      if (msg != NULL)
+       as_warn ("%s", msg);
+      subtype &= ~s;
+    }
 
-/* Emit an SVR4 PIC sequence to load address LOCAL into DEST, where
-   LOCAL is the sum of a symbol and a 16-bit or 32-bit displacement.
-   The two alternatives are:
+  /* If both implementations are longer than 1 instruction, then emit the
+     warning now.  */
+  if (mips_macro_warning.insns[0] > 1 && mips_macro_warning.insns[1] > 1)
+    {
+      relax_substateT s;
+      const char *msg;
 
-   Global symbol               Local sybmol
-   -------------               ------------
-   lw DEST,%got(SYMBOL)                lw DEST,%got(SYMBOL + OFFSET)
-   ...                         ...
-   addiu DEST,DEST,OFFSET      addiu DEST,DEST,%lo(SYMBOL + OFFSET)
+      s = subtype & (RELAX_SECOND_LONGER | RELAX_NOMACRO | RELAX_DELAY_SLOT);
+      msg = macro_warning (s);
+      if (msg != NULL)
+       as_warn ("%s", msg);
+      subtype &= ~s;
+    }
 
-   load_got_offset emits the first instruction and add_got_offset
-   emits the second for a 16-bit offset or add_got_offset_hilo emits
-   a sequence to add a 32-bit offset using a scratch register.  */
+  /* If any flags still set, then one implementation might need a warning
+     and the other either will need one of a different kind or none at all.
+     Pass any remaining flags over to relaxation.  */
+  if (mips_macro_warning.first_frag != NULL)
+    mips_macro_warning.first_frag->fr_subtype |= subtype;
+}
 
-static void
-load_got_offset (int dest, expressionS *local)
-{
-  expressionS global;
+/* Instruction operand formats used in macros that vary between
+   standard MIPS and microMIPS code.  */
 
-  global = *local;
-  global.X_add_number = 0;
+static const char * const brk_fmt[2][2] = { { "c", "c" }, { "mF", "c" } };
+static const char * const cop12_fmt[2] = { "E,o(b)", "E,~(b)" };
+static const char * const jalr_fmt[2] = { "d,s", "t,s" };
+static const char * const lui_fmt[2] = { "t,u", "s,u" };
+static const char * const mem12_fmt[2] = { "t,o(b)", "t,~(b)" };
+static const char * const mfhl_fmt[2][2] = { { "d", "d" }, { "mj", "s" } };
+static const char * const shft_fmt[2] = { "d,w,<", "t,r,<" };
+static const char * const trap_fmt[2] = { "s,t,q", "s,t,|" };
 
-  relax_start (local->X_add_symbol);
-  macro_build (&global, ADDRESS_LOAD_INSN, "t,o(b)", dest,
-              BFD_RELOC_MIPS_GOT16, mips_gp_register);
-  relax_switch ();
-  macro_build (local, ADDRESS_LOAD_INSN, "t,o(b)", dest,
-              BFD_RELOC_MIPS_GOT16, mips_gp_register);
-  relax_end ();
-}
+#define BRK_FMT (brk_fmt[mips_opts.micromips][mips_opts.insn32])
+#define COP12_FMT (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 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])
+
+/* Read a macro's relocation codes from *ARGS and store them in *R.
+   The first argument in *ARGS will be either the code for a single
+   relocation or -1 followed by the three codes that make up a
+   composite relocation.  */
 
 static void
-add_got_offset (int dest, expressionS *local)
+macro_read_relocs (va_list *args, bfd_reloc_code_real_type *r)
 {
-  expressionS global;
-
-  global.X_op = O_constant;
-  global.X_op_symbol = NULL;
-  global.X_add_symbol = NULL;
-  global.X_add_number = local->X_add_number;
+  int i, next;
 
-  relax_start (local->X_add_symbol);
-  macro_build (&global, ADDRESS_ADDI_INSN, "t,r,j",
-              dest, dest, BFD_RELOC_LO16);
-  relax_switch ();
-  macro_build (local, ADDRESS_ADDI_INSN, "t,r,j", dest, dest, BFD_RELOC_LO16);
-  relax_end ();
+  next = va_arg (*args, int);
+  if (next >= 0)
+    r[0] = (bfd_reloc_code_real_type) next;
+  else
+    {
+      for (i = 0; i < 3; i++)
+       r[i] = (bfd_reloc_code_real_type) va_arg (*args, int);
+      /* This function is only used for 16-bit relocation fields.
+        To make the macro code simpler, treat an unrelocated value
+        in the same way as BFD_RELOC_LO16.  */
+      if (r[0] == BFD_RELOC_UNUSED)
+       r[0] = BFD_RELOC_LO16;
+    }
 }
 
+/* Build an instruction created by a macro expansion.  This is passed
+   a pointer to the count of instructions created so far, an
+   expression, the name of the instruction to build, an operand format
+   string, and corresponding arguments.  */
+
 static void
-add_got_offset_hilo (int dest, expressionS *local, int tmp)
+macro_build (expressionS *ep, const char *name, const char *fmt, ...)
 {
-  expressionS global;
-  int hold_mips_optimize;
+  const struct mips_opcode *mo = NULL;
+  bfd_reloc_code_real_type r[3];
+  const struct mips_opcode *amo;
+  const struct mips_operand *operand;
+  struct hash_control *hash;
+  struct mips_cl_insn insn;
+  va_list args;
+  unsigned int uval;
 
-  global.X_op = O_constant;
-  global.X_op_symbol = NULL;
-  global.X_add_symbol = NULL;
-  global.X_add_number = local->X_add_number;
+  va_start (args, fmt);
 
-  relax_start (local->X_add_symbol);
-  load_register (tmp, &global, HAVE_64BIT_ADDRESSES);
-  relax_switch ();
-  /* Set mips_optimize around the lui instruction to avoid
-     inserting an unnecessary nop after the lw.  */
-  hold_mips_optimize = mips_optimize;
-  mips_optimize = 2;
-  macro_build_lui (&global, tmp);
-  mips_optimize = hold_mips_optimize;
-  macro_build (local, ADDRESS_ADDI_INSN, "t,r,j", tmp, tmp, BFD_RELOC_LO16);
-  relax_end ();
+  if (mips_opts.mips16)
+    {
+      mips16_macro_build (ep, name, fmt, &args);
+      va_end (args);
+      return;
+    }
 
-  macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dest, dest, tmp);
-}
+  r[0] = BFD_RELOC_UNUSED;
+  r[1] = BFD_RELOC_UNUSED;
+  r[2] = BFD_RELOC_UNUSED;
+  hash = mips_opts.micromips ? micromips_op_hash : op_hash;
+  amo = (struct mips_opcode *) hash_find (hash, name);
+  gas_assert (amo);
+  gas_assert (strcmp (name, amo->name) == 0);
 
-/* Emit a sequence of instructions to emulate a branch likely operation.
-   BR is an ordinary branch corresponding to one to be emulated.  BRNEG
-   is its complementing branch with the original condition negated.
-   CALL is set if the original branch specified the link operation.
-   EP, FMT, SREG and TREG specify the usual macro_build() parameters.
-
-   Code like this is produced in the noreorder mode:
+  do
+    {
+      /* Search until we get a match for NAME.  It is assumed here that
+        macros will never generate MDMX, MIPS-3D, or MT instructions.
+        We try to match an instruction that fulfils the branch delay
+        slot instruction length requirement (if any) of the previous
+        instruction.  While doing this we record the first instruction
+        seen that matches all the other conditions and use it anyway
+        if the requirement cannot be met; we will issue an appropriate
+        warning later on.  */
+      if (strcmp (fmt, amo->args) == 0
+         && amo->pinfo != INSN_MACRO
+         && is_opcode_valid (amo)
+         && is_size_valid (amo))
+       {
+         if (is_delay_slot_valid (amo))
+           {
+             mo = amo;
+             break;
+           }
+         else if (!mo)
+           mo = amo;
+       }
 
-       BRNEG   <args>, 1f
-        nop
-       b       <sym>
-        delay slot (executed only if branch taken)
-    1:
+      ++amo;
+      gas_assert (amo->name);
+    }
+  while (strcmp (name, amo->name) == 0);
 
-   or, if CALL is set:
+  gas_assert (mo);
+  create_insn (&insn, mo);
+  for (; *fmt; ++fmt)
+    {
+      switch (*fmt)
+       {
+       case ',':
+       case '(':
+       case ')':
+       case 'z':
+         break;
 
-       BRNEG   <args>, 1f
-        nop
-       bal     <sym>
-        delay slot (executed only if branch taken)
-    1:
+       case 'i':
+       case 'j':
+         macro_read_relocs (&args, r);
+         gas_assert (*r == BFD_RELOC_GPREL16
+                     || *r == BFD_RELOC_MIPS_HIGHER
+                     || *r == BFD_RELOC_HI16_S
+                     || *r == BFD_RELOC_LO16
+                     || *r == BFD_RELOC_MIPS_GOT_OFST);
+         break;
 
-   In the reorder mode the delay slot would be filled with a nop anyway,
-   so code produced is simply:
+       case 'o':
+         macro_read_relocs (&args, r);
+         break;
 
-       BR      <args>, <sym>
-        nop
+       case 'u':
+         macro_read_relocs (&args, r);
+         gas_assert (ep != NULL
+                     && (ep->X_op == O_constant
+                         || (ep->X_op == O_symbol
+                             && (*r == BFD_RELOC_MIPS_HIGHEST
+                                 || *r == BFD_RELOC_HI16_S
+                                 || *r == BFD_RELOC_HI16
+                                 || *r == BFD_RELOC_GPREL16
+                                 || *r == BFD_RELOC_MIPS_GOT_HI16
+                                 || *r == BFD_RELOC_MIPS_CALL_HI16))));
+         break;
 
-   This function is used when producing code for the microMIPS ASE that
-   does not implement branch likely instructions in hardware.  */
+       case 'p':
+         gas_assert (ep != NULL);
 
-static void
-macro_build_branch_likely (const char *br, const char *brneg,
-                          int call, expressionS *ep, const char *fmt,
-                          unsigned int sreg, unsigned int treg)
-{
-  int noreorder = mips_opts.noreorder;
-  expressionS expr1;
+         /*
+          * This allows macro() to pass an immediate expression for
+          * creating short branches without creating a symbol.
+          *
+          * We don't allow branch relaxation for these branches, as
+          * they should only appear in ".set nomacro" anyway.
+          */
+         if (ep->X_op == O_constant)
+           {
+             /* For microMIPS we always use relocations for branches.
+                So we should not resolve immediate values.  */
+             gas_assert (!mips_opts.micromips);
 
-  gas_assert (mips_opts.micromips);
-  start_noreorder ();
-  if (noreorder)
-    {
-      micromips_label_expr (&expr1);
-      macro_build (&expr1, brneg, fmt, sreg, treg);
-      macro_build (NULL, "nop", "");
-      macro_build (ep, call ? "bal" : "b", "p");
+             if ((ep->X_add_number & 3) != 0)
+               as_bad (_("branch to misaligned address (0x%lx)"),
+                       (unsigned long) ep->X_add_number);
+             if ((ep->X_add_number + 0x20000) & ~0x3ffff)
+               as_bad (_("branch address range overflow (0x%lx)"),
+                       (unsigned long) ep->X_add_number);
+             insn.insn_opcode |= (ep->X_add_number >> 2) & 0xffff;
+             ep = NULL;
+           }
+         else
+           *r = BFD_RELOC_16_PCREL_S2;
+         break;
 
-      /* Set to true so that append_insn adds a label.  */
-      emit_branch_likely_macro = TRUE;
-    }
-  else
-    {
-      macro_build (ep, br, fmt, sreg, treg);
-      macro_build (NULL, "nop", "");
-    }
-  end_noreorder ();
-}
+       case 'a':
+         gas_assert (ep != NULL);
+         *r = BFD_RELOC_MIPS_JMP;
+         break;
 
-/* Emit a coprocessor branch-likely macro specified by TYPE, using CC as
-   the condition code tested.  EP specifies the branch target.  */
+       default:
+         operand = (mips_opts.micromips
+                    ? decode_micromips_operand (fmt)
+                    : decode_mips_operand (fmt));
+         if (!operand)
+           abort ();
 
-static void
-macro_build_branch_ccl (int type, expressionS *ep, unsigned int cc)
-{
-  const int call = 0;
-  const char *brneg;
-  const char *br;
+         uval = va_arg (args, int);
+         if (operand->type == OP_CLO_CLZ_DEST)
+           uval |= (uval << 5);
+         insn_insert_operand (&insn, operand, uval);
 
-  switch (type)
-    {
-    case M_BC1FL:
-      br = "bc1f";
-      brneg = "bc1t";
-      break;
-    case M_BC1TL:
-      br = "bc1t";
-      brneg = "bc1f";
-      break;
-    case M_BC2FL:
-      br = "bc2f";
-      brneg = "bc2t";
-      break;
-    case M_BC2TL:
-      br = "bc2t";
-      brneg = "bc2f";
-      break;
-    default:
-      abort ();
+         if (*fmt == '+' || *fmt == 'm')
+           ++fmt;
+         break;
+       }
     }
-  macro_build_branch_likely (br, brneg, call, ep, "N,p", cc, ZERO);
-}
+  va_end (args);
+  gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
 
-/* Emit a two-argument branch macro specified by TYPE, using SREG as
-   the register tested.  EP specifies the branch target.  */
+  append_insn (&insn, ep, r, TRUE);
+}
 
 static void
-macro_build_branch_rs (int type, expressionS *ep, unsigned int sreg)
+mips16_macro_build (expressionS *ep, const char *name, const char *fmt,
+                   va_list *args)
 {
-  const char *brneg = NULL;
-  const char *br;
-  int call = 0;
+  struct mips_opcode *mo;
+  struct mips_cl_insn insn;
+  const struct mips_operand *operand;
+  bfd_reloc_code_real_type r[3]
+    = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
 
-  switch (type)
+  mo = (struct mips_opcode *) hash_find (mips16_op_hash, name);
+  gas_assert (mo);
+  gas_assert (strcmp (name, mo->name) == 0);
+
+  while (strcmp (fmt, mo->args) != 0 || mo->pinfo == INSN_MACRO)
     {
-    case M_BGEZ:
-      br = "bgez";
-      break;
-    case M_BGEZL:
-      br = mips_opts.micromips ? "bgez" : "bgezl";
-      brneg = "bltz";
-      break;
-    case M_BGEZALL:
-      gas_assert (mips_opts.micromips);
-      br = "bgezals";
-      brneg = "bltz";
-      call = 1;
-      break;
-    case M_BGTZ:
-      br = "bgtz";
-      break;
-    case M_BGTZL:
-      br = mips_opts.micromips ? "bgtz" : "bgtzl";
-      brneg = "blez";
-      break;
-    case M_BLEZ:
-      br = "blez";
-      break;
-    case M_BLEZL:
-      br = mips_opts.micromips ? "blez" : "blezl";
-      brneg = "bgtz";
-      break;
-    case M_BLTZ:
-      br = "bltz";
-      break;
-    case M_BLTZL:
-      br = mips_opts.micromips ? "bltz" : "bltzl";
-      brneg = "bgez";
-      break;
-    case M_BLTZALL:
-      gas_assert (mips_opts.micromips);
-      br = "bltzals";
-      brneg = "bgez";
-      call = 1;
-      break;
-    default:
-      abort ();
+      ++mo;
+      gas_assert (mo->name);
+      gas_assert (strcmp (name, mo->name) == 0);
     }
-  if (mips_opts.micromips && brneg)
-    macro_build_branch_likely (br, brneg, call, ep, "s,p", sreg, ZERO);
-  else
-    macro_build (ep, br, "s,p", sreg);
-}
-
-/* Emit a three-argument branch macro specified by TYPE, using SREG and
-   TREG as the registers tested.  EP specifies the branch target.  */
-
-static void
-macro_build_branch_rsrt (int type, expressionS *ep,
-                        unsigned int sreg, unsigned int treg)
-{
-  const char *brneg = NULL;
-  const int call = 0;
-  const char *br;
 
-  switch (type)
+  create_insn (&insn, mo);
+  for (; *fmt; ++fmt)
     {
-    case M_BEQ:
-    case M_BEQ_I:
-      br = "beq";
-      break;
-    case M_BEQL:
-    case M_BEQL_I:
-      br = mips_opts.micromips ? "beq" : "beql";
-      brneg = "bne";
-      break;
-    case M_BNE:
-    case M_BNE_I:
-      br = "bne";
-      break;
-    case M_BNEL:
-    case M_BNEL_I:
-      br = mips_opts.micromips ? "bne" : "bnel";
-      brneg = "beq";
-      break;
-    default:
-      abort ();
+      int c;
+
+      c = *fmt;
+      switch (c)
+       {
+       case ',':
+       case '(':
+       case ')':
+         break;
+
+       case '0':
+       case 'S':
+       case 'P':
+       case 'R':
+         break;
+
+       case '<':
+       case '>':
+       case '4':
+       case '5':
+       case 'H':
+       case 'W':
+       case 'D':
+       case 'j':
+       case '8':
+       case 'V':
+       case 'C':
+       case 'U':
+       case 'k':
+       case 'K':
+       case 'p':
+       case 'q':
+         {
+           offsetT value;
+
+           gas_assert (ep != NULL);
+
+           if (ep->X_op != O_constant)
+             *r = (int) BFD_RELOC_UNUSED + c;
+           else if (calculate_reloc (*r, ep->X_add_number, &value))
+             {
+               mips16_immed (NULL, 0, c, *r, value, 0, &insn.insn_opcode);
+               ep = NULL;
+               *r = BFD_RELOC_UNUSED;
+             }
+         }
+         break;
+
+       default:
+         operand = decode_mips16_operand (c, FALSE);
+         if (!operand)
+           abort ();
+
+         insn_insert_operand (&insn, operand, va_arg (*args, int));
+         break;
+       }
     }
-  if (mips_opts.micromips && brneg)
-    macro_build_branch_likely (br, brneg, call, ep, "s,t,p", sreg, treg);
-  else
-    macro_build (ep, br, "s,t,p", sreg, treg);
+
+  gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
+
+  append_insn (&insn, ep, r, TRUE);
 }
 
 /*
- *                     Build macros
- *   This routine implements the seemingly endless macro or synthesized
- * instructions and addressing modes in the mips assembly language. Many
- * of these macros are simple and are similar to each other. These could
- * probably be handled by some kind of table or grammar approach instead of
- * this verbose method. Others are not simple macros but are more like
- * optimizing code generation.
- *   One interesting optimization is when several store macros appear
- * consecutively that would load AT with the upper half of the same address.
- * The ensuing load upper instructions are ommited. This implies some kind
- * of global optimization. We currently only optimize within a single macro.
- *   For many of the load and store macros if the address is specified as a
- * constant expression in the first 64k of memory (ie ld $2,0x4000c) we
- * first load register 'at' with zero and use it as the base register. The
- * mips assembler simply uses register $zero. Just one tiny optimization
- * we're missing.
+ * Sign-extend 32-bit mode constants that have bit 31 set and all
+ * higher bits unset.
  */
 static void
-macro (struct mips_cl_insn *ip)
+normalize_constant_expr (expressionS *ex)
 {
-  unsigned int treg, sreg, dreg, breg;
-  unsigned int tempreg;
-  int mask;
-  int used_at = 0;
-  expressionS label_expr;
-  expressionS expr1;
-  expressionS *ep;
-  const char *s;
-  const char *s2;
-  const char *fmt;
-  int likely = 0;
-  int coproc = 0;
-  int offbits = 16;
-  int call = 0;
-  int jals = 0;
-  int dbl = 0;
-  int imm = 0;
-  int ust = 0;
-  int lp = 0;
-  int ab = 0;
-  int off;
-  offsetT maxnum;
-  bfd_reloc_code_real_type r;
-  int hold_mips_optimize;
+  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);
+}
 
-  gas_assert (! mips_opts.mips16);
+/*
+ * 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);
+}
 
-  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);
-  mask = ip->insn_mo->mask;
+/*
+ * Generate a "jalr" instruction with a relocation hint to the called
+ * function.  This occurs in NewABI PIC code.
+ */
+static void
+macro_build_jalr (expressionS *ep, int cprestore)
+{
+  static const bfd_reloc_code_real_type jalr_relocs[2]
+    = { BFD_RELOC_MIPS_JALR, BFD_RELOC_MICROMIPS_JALR };
+  bfd_reloc_code_real_type jalr_reloc = jalr_relocs[mips_opts.micromips];
+  const char *jalr;
+  char *f = NULL;
 
-  label_expr.X_op = O_constant;
-  label_expr.X_op_symbol = NULL;
-  label_expr.X_add_symbol = NULL;
-  label_expr.X_add_number = 0;
+  if (MIPS_JALR_HINT_P (ep))
+    {
+      frag_grow (8);
+      f = frag_more (0);
+    }
+  if (mips_opts.micromips)
+    {
+      jalr = ((mips_opts.noreorder && !cprestore) || mips_opts.insn32
+             ? "jalr" : "jalrs");
+      if (MIPS_JALR_HINT_P (ep)
+         || mips_opts.insn32
+         || (history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
+       macro_build (NULL, jalr, "t,s", RA, PIC_CALL_REG);
+      else
+       macro_build (NULL, jalr, "mj", PIC_CALL_REG);
+    }
+  else
+    macro_build (NULL, "jalr", "d,s", RA, PIC_CALL_REG);
+  if (MIPS_JALR_HINT_P (ep))
+    fix_new_exp (frag_now, f - frag_now->fr_literal, 4, ep, FALSE, jalr_reloc);
+}
 
-  expr1.X_op = O_constant;
-  expr1.X_op_symbol = NULL;
-  expr1.X_add_symbol = NULL;
-  expr1.X_add_number = 1;
+/*
+ * Generate a "lui" instruction.
+ */
+static void
+macro_build_lui (expressionS *ep, int regnum)
+{
+  gas_assert (! mips_opts.mips16);
 
-  switch (mask)
+  if (ep->X_op != O_constant)
     {
-    case M_DABS:
-      dbl = 1;
-    case M_ABS:
-      /*    bgez    $a0,1f
-           move    v0,$a0
-           sub     v0,$zero,$a0
-        1:
-       */
+      gas_assert (ep->X_op == O_symbol);
+      /* _gp_disp is a special case, used from s_cpload.
+        __gnu_local_gp is used if mips_no_shared.  */
+      gas_assert (mips_pic == NO_PIC
+             || (! HAVE_NEWABI
+                 && strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0)
+             || (! mips_in_shared
+                 && strcmp (S_GET_NAME (ep->X_add_symbol),
+                             "__gnu_local_gp") == 0));
+    }
 
-      start_noreorder ();
+  macro_build (ep, "lui", LUI_FMT, regnum, BFD_RELOC_HI16_S);
+}
 
-      if (mips_opts.micromips)
-       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 (NULL, "nop", "");
-      else
-       move_register (dreg, sreg);
-      macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", dreg, 0, sreg);
-      if (mips_opts.micromips)
-       micromips_add_label ();
+/* Generate a sequence of instructions to do a load or store from a constant
+   offset off of a base register (breg) into/from a target register (treg),
+   using AT if necessary.  */
+static void
+macro_build_ldst_constoffset (expressionS *ep, const char *op,
+                             int treg, int breg, int dbl)
+{
+  gas_assert (ep->X_op == O_constant);
 
-      end_noreorder ();
-      break;
+  /* Sign-extending 32-bit constants makes their handling easier.  */
+  if (!dbl)
+    normalize_constant_expr (ep);
 
-    case M_ADD_I:
-      s = "addi";
-      s2 = "add";
-      goto do_addi;
-    case M_ADDU_I:
-      s = "addiu";
-      s2 = "addu";
-      goto do_addi;
-    case M_DADD_I:
-      dbl = 1;
-      s = "daddi";
-      s2 = "dadd";
-      if (!mips_opts.micromips)
-       goto do_addi;
-      if (imm_expr.X_op == O_constant
-         && 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);
-         break;
-       }
-      goto do_addi_i;
-    case M_DADDU_I:
-      dbl = 1;
-      s = "daddiu";
-      s2 = "daddu";
-    do_addi:
-      if (imm_expr.X_op == O_constant
-         && 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);
-         break;
-       }
-    do_addi_i:
-      used_at = 1;
-      load_register (AT, &imm_expr, dbl);
-      macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
-      break;
+  /* Right now, this routine can only handle signed 32-bit constants.  */
+  if (! IS_SEXT_32BIT_NUM(ep->X_add_number + 0x8000))
+    as_warn (_("operand overflow"));
 
-    case M_AND_I:
-      s = "andi";
-      s2 = "and";
-      goto do_bit;
-    case M_OR_I:
-      s = "ori";
-      s2 = "or";
-      goto do_bit;
-    case M_NOR_I:
-      s = "";
-      s2 = "nor";
-      goto do_bit;
-    case M_XOR_I:
-      s = "xori";
-      s2 = "xor";
-    do_bit:
-      if (imm_expr.X_op == O_constant
-         && 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);
-         else
-           {
-             macro_build (&imm_expr, "ori", "t,r,i",
-                          treg, sreg, BFD_RELOC_LO16);
-             macro_build (NULL, "nor", "d,v,t", treg, treg, 0);
-           }
-         break;
-       }
+  if (IS_SEXT_16BIT_NUM(ep->X_add_number))
+    {
+      /* Signed 16-bit offset will fit in the op.  Easy!  */
+      macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, breg);
+    }
+  else
+    {
+      /* 32-bit offset, need multiple instructions and AT, like:
+          lui      $tempreg,const_hi       (BFD_RELOC_HI16_S)
+          addu     $tempreg,$tempreg,$breg
+           <op>     $treg,const_lo($tempreg)   (BFD_RELOC_LO16)
+         to handle the complete offset.  */
+      macro_build_lui (ep, AT);
+      macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
+      macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, AT);
 
-      used_at = 1;
+      if (!mips_opts.at)
+       as_bad (_("Macro used $at after \".set noat\""));
+    }
+}
+
+/*                     set_at()
+ * Generates code to set the $at register to true (one)
+ * if reg is less than the immediate expression.
+ */
+static void
+set_at (int reg, int unsignedp)
+{
+  if (imm_expr.X_op == O_constant
+      && 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);
-      macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
-      break;
+      macro_build (NULL, unsignedp ? "sltu" : "slt", "d,v,t", AT, reg, AT);
+    }
+}
 
-    case M_BALIGN:
-      switch (imm_expr.X_add_number)
-       {
-       case 0:
-         macro_build (NULL, "nop", "");
-         break;
-       case 2:
-         macro_build (NULL, "packrl.ph", "d,s,t", treg, treg, sreg);
-         break;
-       case 1:
-       case 3:
-         macro_build (NULL, "balign", "t,s,2", treg, sreg,
-                      (int) imm_expr.X_add_number);
-         break;
-       default:
-         as_bad (_("BALIGN immediate not 0, 1, 2 or 3 (%lu)"),
-                 (unsigned long) imm_expr.X_add_number);
-         break;
-       }
-      break;
+/* Count the leading zeroes by performing a binary chop. This is a
+   bulky bit of source, but performance is a LOT better for the
+   majority of values than a simple loop to count the bits:
+       for (lcnt = 0; (lcnt < 32); lcnt++)
+         if ((v) & (1 << (31 - lcnt)))
+           break;
+  However it is not code size friendly, and the gain will drop a bit
+  on certain cached systems.
+*/
+#define COUNT_TOP_ZEROES(v)             \
+  (((v) & ~0xffff) == 0                 \
+   ? ((v) & ~0xff) == 0                 \
+     ? ((v) & ~0xf) == 0                \
+       ? ((v) & ~0x3) == 0              \
+         ? ((v) & ~0x1) == 0            \
+           ? !(v)                       \
+             ? 32                       \
+             : 31                       \
+           : 30                         \
+         : ((v) & ~0x7) == 0            \
+           ? 29                         \
+           : 28                         \
+       : ((v) & ~0x3f) == 0             \
+         ? ((v) & ~0x1f) == 0           \
+           ? 27                         \
+           : 26                         \
+         : ((v) & ~0x7f) == 0           \
+           ? 25                         \
+           : 24                         \
+     : ((v) & ~0xfff) == 0              \
+       ? ((v) & ~0x3ff) == 0            \
+         ? ((v) & ~0x1ff) == 0          \
+           ? 23                         \
+           : 22                         \
+         : ((v) & ~0x7ff) == 0          \
+           ? 21                         \
+           : 20                         \
+       : ((v) & ~0x3fff) == 0           \
+         ? ((v) & ~0x1fff) == 0         \
+           ? 19                         \
+           : 18                         \
+         : ((v) & ~0x7fff) == 0         \
+           ? 17                         \
+           : 16                         \
+   : ((v) & ~0xffffff) == 0             \
+     ? ((v) & ~0xfffff) == 0            \
+       ? ((v) & ~0x3ffff) == 0          \
+         ? ((v) & ~0x1ffff) == 0        \
+           ? 15                         \
+           : 14                         \
+         : ((v) & ~0x7ffff) == 0        \
+           ? 13                         \
+           : 12                         \
+       : ((v) & ~0x3fffff) == 0         \
+         ? ((v) & ~0x1fffff) == 0       \
+           ? 11                         \
+           : 10                         \
+         : ((v) & ~0x7fffff) == 0       \
+           ? 9                          \
+           : 8                          \
+     : ((v) & ~0xfffffff) == 0          \
+       ? ((v) & ~0x3ffffff) == 0        \
+         ? ((v) & ~0x1ffffff) == 0      \
+           ? 7                          \
+           : 6                          \
+         : ((v) & ~0x7ffffff) == 0      \
+           ? 5                          \
+           : 4                          \
+       : ((v) & ~0x3fffffff) == 0       \
+         ? ((v) & ~0x1fffffff) == 0     \
+           ? 3                          \
+           : 2                          \
+         : ((v) & ~0x7fffffff) == 0     \
+           ? 1                          \
+           : 0)
 
-    case M_BC1FL:
-    case M_BC1TL:
-    case M_BC2FL:
-    case M_BC2TL:
-      gas_assert (mips_opts.micromips);
-      macro_build_branch_ccl (mask, &offset_expr,
-                             EXTRACT_OPERAND (1, BCC, *ip));
-      break;
+/*                     load_register()
+ *  This routine generates the least number of instructions necessary to load
+ *  an absolute expression value into a register.
+ */
+static void
+load_register (int reg, expressionS *ep, int dbl)
+{
+  int freg;
+  expressionS hi32, lo32;
 
-    case M_BEQ_I:
-    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;
-      else
+  if (ep->X_op != O_big)
+    {
+      gas_assert (ep->X_op == O_constant);
+
+      /* Sign-extending 32-bit constants makes their handling easier.  */
+      if (!dbl)
+       normalize_constant_expr (ep);
+
+      if (IS_SEXT_16BIT_NUM (ep->X_add_number))
        {
-         treg = AT;
-         used_at = 1;
-         load_register (treg, &imm_expr, HAVE_64BIT_GPRS);
+         /* We can handle 16 bit signed values with an addiu to
+            $zero.  No need to ever use daddiu here, since $zero and
+            the result are always correct in 32 bit mode.  */
+         macro_build (ep, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
+         return;
        }
-      /* Fall through.  */
-    case M_BEQL:
-    case M_BNEL:
-      macro_build_branch_rsrt (mask, &offset_expr, sreg, treg);
-      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);
-      else
+      else if (ep->X_add_number >= 0 && ep->X_add_number < 0x10000)
        {
-         used_at = 1;
-         macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
-         macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
-                                  &offset_expr, AT, ZERO);
+         /* We can handle 16 bit unsigned values with an ori to
+             $zero.  */
+         macro_build (ep, "ori", "t,r,i", reg, 0, BFD_RELOC_LO16);
+         return;
        }
-      break;
-
-    case M_BGEZL:
-    case M_BGEZALL:
-    case M_BGTZL:
-    case M_BLEZL:
-    case M_BLTZL:
-    case M_BLTZALL:
-      macro_build_branch_rs (mask, &offset_expr, sreg);
-      break;
+      else if ((IS_SEXT_32BIT_NUM (ep->X_add_number)))
+       {
+         /* 32 bit values require an lui.  */
+         macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
+         if ((ep->X_add_number & 0xffff) != 0)
+           macro_build (ep, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
+         return;
+       }
+    }
 
-    case M_BGTL_I:
-      likely = 1;
-    case M_BGT_I:
-      /* Check for > max integer.  */
-      maxnum = 0x7fffffff;
-      if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
+  /* The value is larger than 32 bits.  */
+
+  if (!dbl || HAVE_32BIT_GPRS)
+    {
+      char value[32];
+
+      sprintf_vma (value, ep->X_add_number);
+      as_bad (_("Number (0x%s) larger than 32 bits"), value);
+      macro_build (ep, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
+      return;
+    }
+
+  if (ep->X_op != O_big)
+    {
+      hi32 = *ep;
+      hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
+      hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
+      hi32.X_add_number &= 0xffffffff;
+      lo32 = *ep;
+      lo32.X_add_number &= 0xffffffff;
+    }
+  else
+    {
+      gas_assert (ep->X_add_number > 2);
+      if (ep->X_add_number == 3)
+       generic_bignum[3] = 0;
+      else if (ep->X_add_number > 4)
+       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;
+      hi32.X_add_number = generic_bignum[2] + (generic_bignum[3] << 16);
+    }
+
+  if (hi32.X_add_number == 0)
+    freg = 0;
+  else
+    {
+      int shift, bit;
+      unsigned long hi, lo;
+
+      if (hi32.X_add_number == (offsetT) 0xffffffff)
        {
-         maxnum <<= 16;
-         maxnum |= 0xffff;
-         maxnum <<= 16;
-         maxnum |= 0xffff;
+         if ((lo32.X_add_number & 0xffff8000) == 0xffff8000)
+           {
+             macro_build (&lo32, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
+             return;
+           }
+         if (lo32.X_add_number & 0x80000000)
+           {
+             macro_build (&lo32, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
+             if (lo32.X_add_number & 0xffff)
+               macro_build (&lo32, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
+             return;
+           }
        }
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number >= maxnum
-         && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4))
+
+      /* Check for 16bit shifted constant.  We know that hi32 is
+         non-zero, so start the mask on the first bit of the hi32
+         value.  */
+      shift = 17;
+      do
        {
-       do_false:
-         /* Result is always false.  */
-         if (! likely)
-           macro_build (NULL, "nop", "");
+         unsigned long himask, lomask;
+
+         if (shift < 32)
+           {
+             himask = 0xffff >> (32 - shift);
+             lomask = (0xffff << shift) & 0xffffffff;
+           }
          else
-           macro_build_branch_rsrt (M_BNEL, &offset_expr, ZERO, ZERO);
-         break;
+           {
+             himask = 0xffff << (shift - 32);
+             lomask = 0;
+           }
+         if ((hi32.X_add_number & ~(offsetT) himask) == 0
+             && (lo32.X_add_number & ~(offsetT) lomask) == 0)
+           {
+             expressionS tmp;
+
+             tmp.X_op = O_constant;
+             if (shift < 32)
+               tmp.X_add_number = ((hi32.X_add_number << (32 - shift))
+                                   | (lo32.X_add_number >> shift));
+             else
+               tmp.X_add_number = hi32.X_add_number >> (shift - 32);
+             macro_build (&tmp, "ori", "t,r,i", reg, 0, BFD_RELOC_LO16);
+             macro_build (NULL, (shift >= 32) ? "dsll32" : "dsll", SHFT_FMT,
+                          reg, reg, (shift >= 32) ? shift - 32 : shift);
+             return;
+           }
+         ++shift;
        }
-      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)
+      while (shift <= (64 - 16));
+
+      /* Find the bit number of the lowest one bit, and store the
+         shifted value in hi/lo.  */
+      hi = (unsigned long) (hi32.X_add_number & 0xffffffff);
+      lo = (unsigned long) (lo32.X_add_number & 0xffffffff);
+      if (lo != 0)
        {
-         macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ,
-                                &offset_expr, sreg);
-         break;
+         bit = 0;
+         while ((lo & 1) == 0)
+           {
+             lo >>= 1;
+             ++bit;
+           }
+         lo |= (hi & (((unsigned long) 1 << bit) - 1)) << (32 - bit);
+         hi >>= bit;
        }
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+      else
        {
-         macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ,
-                                &offset_expr, sreg);
-         break;
+         bit = 32;
+         while ((hi & 1) == 0)
+           {
+             hi >>= 1;
+             ++bit;
+           }
+         lo = hi;
+         hi = 0;
        }
-      maxnum = 0x7fffffff;
-      if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
+
+      /* Optimize if the shifted value is a (power of 2) - 1.  */
+      if ((hi == 0 && ((lo + 1) & lo) == 0)
+         || (lo == 0xffffffff && ((hi + 1) & hi) == 0))
        {
-         maxnum <<= 16;
-         maxnum |= 0xffff;
-         maxnum <<= 16;
-         maxnum |= 0xffff;
+         shift = COUNT_TOP_ZEROES ((unsigned int) hi32.X_add_number);
+         if (shift != 0)
+           {
+             expressionS tmp;
+
+             /* This instruction will set the register to be all
+                 ones.  */
+             tmp.X_op = O_constant;
+             tmp.X_add_number = (offsetT) -1;
+             macro_build (&tmp, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
+             if (bit != 0)
+               {
+                 bit += shift;
+                 macro_build (NULL, (bit >= 32) ? "dsll32" : "dsll", SHFT_FMT,
+                              reg, reg, (bit >= 32) ? bit - 32 : bit);
+               }
+             macro_build (NULL, (shift >= 32) ? "dsrl32" : "dsrl", SHFT_FMT,
+                          reg, reg, (shift >= 32) ? shift - 32 : shift);
+             return;
+           }
        }
-      maxnum = - maxnum - 1;
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number <= maxnum
-         && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4))
+
+      /* Sign extend hi32 before calling load_register, because we can
+         generally get better code when we load a sign extended value.  */
+      if ((hi32.X_add_number & 0x80000000) != 0)
+       hi32.X_add_number |= ~(offsetT) 0xffffffff;
+      load_register (reg, &hi32, 0);
+      freg = reg;
+    }
+  if ((lo32.X_add_number & 0xffff0000) == 0)
+    {
+      if (freg != 0)
        {
-       do_true:
-         /* result is always true */
-         as_warn (_("Branch %s is always true"), ip->insn_mo->name);
-         macro_build (&offset_expr, "b", "p");
-         break;
+         macro_build (NULL, "dsll32", SHFT_FMT, reg, freg, 0);
+         freg = reg;
        }
-      used_at = 1;
-      set_at (sreg, 0);
-      macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
-                              &offset_expr, AT, ZERO);
-      break;
+    }
+  else
+    {
+      expressionS mid16;
 
-    case M_BGEUL:
-      likely = 1;
-    case M_BGEU:
-      if (treg == 0)
-       goto do_true;
-      else if (sreg == 0)
-       macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
-                                &offset_expr, ZERO, treg);
-      else
+      if ((freg == 0) && (lo32.X_add_number == (offsetT) 0xffffffff))
        {
-         used_at = 1;
-         macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
-         macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
-                                  &offset_expr, AT, ZERO);
+         macro_build (&lo32, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
+         macro_build (NULL, "dsrl32", SHFT_FMT, reg, reg, 0);
+         return;
        }
-      break;
 
-    case M_BGTUL_I:
-      likely = 1;
-    case M_BGTU_I:
-      if (sreg == 0
-         || (HAVE_32BIT_GPRS
-             && imm_expr.X_op == O_constant
-             && 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)
-       goto do_true;
-      else if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
-       macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
-                                &offset_expr, sreg, ZERO);
-      else
+      if (freg != 0)
        {
-         used_at = 1;
-         set_at (sreg, 1);
-         macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
-                                  &offset_expr, AT, ZERO);
+         macro_build (NULL, "dsll", SHFT_FMT, reg, freg, 16);
+         freg = reg;
        }
-      break;
-
-    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);
-      else
-       {
-         used_at = 1;
-         macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
-         macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
-                                  &offset_expr, AT, ZERO);
-       }
-      break;
+      mid16 = lo32;
+      mid16.X_add_number >>= 16;
+      macro_build (&mid16, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
+      macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
+      freg = reg;
+    }
+  if ((lo32.X_add_number & 0xffff) != 0)
+    macro_build (&lo32, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
+}
 
-    case M_BGTUL:
-      likely = 1;
-    case M_BGTU:
-      if (treg == 0)
-       macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
-                                &offset_expr, sreg, ZERO);
-      else if (sreg == 0)
-       goto do_false;
-      else
-       {
-         used_at = 1;
-         macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
-         macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
-                                  &offset_expr, AT, ZERO);
-       }
-      break;
+static inline void
+load_delay_nop (void)
+{
+  if (!gpr_interlocks)
+    macro_build (NULL, "nop", "");
+}
 
-    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);
-      else
-       {
-         used_at = 1;
-         macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
-         macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
-                                  &offset_expr, AT, ZERO);
-       }
-      break;
+/* Load an address into a register.  */
 
-    case M_BLEL_I:
-      likely = 1;
-    case M_BLE_I:
-      maxnum = 0x7fffffff;
-      if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
+static void
+load_address (int reg, expressionS *ep, int *used_at)
+{
+  if (ep->X_op != O_constant
+      && ep->X_op != O_symbol)
+    {
+      as_bad (_("expression too complex"));
+      ep->X_op = O_constant;
+    }
+
+  if (ep->X_op == O_constant)
+    {
+      load_register (reg, ep, HAVE_64BIT_ADDRESSES);
+      return;
+    }
+
+  if (mips_pic == NO_PIC)
+    {
+      /* If this is a reference to a GP relative symbol, we want
+          addiu        $reg,$gp,<sym>          (BFD_RELOC_GPREL16)
+        Otherwise we want
+          lui          $reg,<sym>              (BFD_RELOC_HI16_S)
+          addiu        $reg,$reg,<sym>         (BFD_RELOC_LO16)
+        If we have an addend, we always use the latter form.
+
+        With 64bit address space and a usable $at we want
+          lui          $reg,<sym>              (BFD_RELOC_MIPS_HIGHEST)
+          lui          $at,<sym>               (BFD_RELOC_HI16_S)
+          daddiu       $reg,<sym>              (BFD_RELOC_MIPS_HIGHER)
+          daddiu       $at,<sym>               (BFD_RELOC_LO16)
+          dsll32       $reg,0
+          daddu        $reg,$reg,$at
+
+        If $at is already in use, we use a path which is suboptimal
+        on superscalar processors.
+          lui          $reg,<sym>              (BFD_RELOC_MIPS_HIGHEST)
+          daddiu       $reg,<sym>              (BFD_RELOC_MIPS_HIGHER)
+          dsll         $reg,16
+          daddiu       $reg,<sym>              (BFD_RELOC_HI16_S)
+          dsll         $reg,16
+          daddiu       $reg,<sym>              (BFD_RELOC_LO16)
+
+        For GP relative symbols in 64bit address space we can use
+        the same sequence as in 32bit address space.  */
+      if (HAVE_64BIT_SYMBOLS)
        {
-         maxnum <<= 16;
-         maxnum |= 0xffff;
-         maxnum <<= 16;
-         maxnum |= 0xffff;
+         if ((valueT) ep->X_add_number <= MAX_GPREL_OFFSET
+             && !nopic_need_relax (ep->X_add_symbol, 1))
+           {
+             relax_start (ep->X_add_symbol);
+             macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg,
+                          mips_gp_register, BFD_RELOC_GPREL16);
+             relax_switch ();
+           }
+
+         if (*used_at == 0 && mips_opts.at)
+           {
+             macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_HIGHEST);
+             macro_build (ep, "lui", LUI_FMT, AT, BFD_RELOC_HI16_S);
+             macro_build (ep, "daddiu", "t,r,j", reg, reg,
+                          BFD_RELOC_MIPS_HIGHER);
+             macro_build (ep, "daddiu", "t,r,j", AT, AT, BFD_RELOC_LO16);
+             macro_build (NULL, "dsll32", SHFT_FMT, reg, reg, 0);
+             macro_build (NULL, "daddu", "d,v,t", reg, reg, AT);
+             *used_at = 1;
+           }
+         else
+           {
+             macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_HIGHEST);
+             macro_build (ep, "daddiu", "t,r,j", reg, reg,
+                          BFD_RELOC_MIPS_HIGHER);
+             macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
+             macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_HI16_S);
+             macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
+             macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_LO16);
+           }
+
+         if (mips_relax.sequence)
+           relax_end ();
        }
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number >= maxnum
-         && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4))
-       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);
       else
        {
-         used_at = 1;
-         set_at (sreg, 0);
-         macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
-                                  &offset_expr, AT, ZERO);
+         if ((valueT) ep->X_add_number <= MAX_GPREL_OFFSET
+             && !nopic_need_relax (ep->X_add_symbol, 1))
+           {
+             relax_start (ep->X_add_symbol);
+             macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg,
+                          mips_gp_register, BFD_RELOC_GPREL16);
+             relax_switch ();
+           }
+         macro_build_lui (ep, reg);
+         macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j",
+                      reg, reg, BFD_RELOC_LO16);
+         if (mips_relax.sequence)
+           relax_end ();
        }
-      break;
+    }
+  else if (!mips_big_got)
+    {
+      expressionS ex;
 
-    case M_BLEUL:
-      likely = 1;
-    case M_BLEU:
-      if (treg == 0)
-       macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
-                                &offset_expr, sreg, ZERO);
-      else if (sreg == 0)
-       goto do_true;
-      else
-       {
-         used_at = 1;
-         macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
-         macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
-                                  &offset_expr, AT, ZERO);
-       }
-      break;
+      /* If this is a reference to an external symbol, we want
+          lw           $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT16)
+        Otherwise we want
+          lw           $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT16)
+          nop
+          addiu        $reg,$reg,<sym>         (BFD_RELOC_LO16)
+        If there is a constant, it must be added in after.
 
-    case M_BLEUL_I:
-      likely = 1;
-    case M_BLEU_I:
-      if (sreg == 0
-         || (HAVE_32BIT_GPRS
-             && imm_expr.X_op == O_constant
-             && 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)
-       goto do_false;
-      else if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
-       macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
-                                &offset_expr, sreg, ZERO);
-      else
+        If we have NewABI, we want
+          lw           $reg,<sym+cst>($gp)     (BFD_RELOC_MIPS_GOT_DISP)
+         unless we're referencing a global symbol with a non-zero
+         offset, in which case cst must be added separately.  */
+      if (HAVE_NEWABI)
        {
-         used_at = 1;
-         set_at (sreg, 1);
-         macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
-                                  &offset_expr, AT, ZERO);
+         if (ep->X_add_number)
+           {
+             ex.X_add_number = ep->X_add_number;
+             ep->X_add_number = 0;
+             relax_start (ep->X_add_symbol);
+             macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+                          BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+             if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+               as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+             ex.X_op = O_constant;
+             macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j",
+                          reg, reg, BFD_RELOC_LO16);
+             ep->X_add_number = ex.X_add_number;
+             relax_switch ();
+           }
+         macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+                      BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+         if (mips_relax.sequence)
+           relax_end ();
        }
-      break;
-
-    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);
       else
        {
-         used_at = 1;
-         macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
-         macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
-                                  &offset_expr, AT, ZERO);
+         ex.X_add_number = ep->X_add_number;
+         ep->X_add_number = 0;
+         macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+                      BFD_RELOC_MIPS_GOT16, mips_gp_register);
+         load_delay_nop ();
+         relax_start (ep->X_add_symbol);
+         relax_switch ();
+         macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+                      BFD_RELOC_LO16);
+         relax_end ();
+
+         if (ex.X_add_number != 0)
+           {
+             if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+               as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+             ex.X_op = O_constant;
+             macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j",
+                          reg, reg, BFD_RELOC_LO16);
+           }
        }
-      break;
+    }
+  else if (mips_big_got)
+    {
+      expressionS ex;
 
-    case M_BLTUL:
-      likely = 1;
-    case M_BLTU:
-      if (treg == 0)
-       goto do_false;
-      else if (sreg == 0)
-       macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
-                                &offset_expr, ZERO, treg);
-      else
+      /* This is the large GOT case.  If this is a reference to an
+        external symbol, we want
+          lui          $reg,<sym>              (BFD_RELOC_MIPS_GOT_HI16)
+          addu         $reg,$reg,$gp
+          lw           $reg,<sym>($reg)        (BFD_RELOC_MIPS_GOT_LO16)
+
+        Otherwise, for a reference to a local symbol in old ABI, we want
+          lw           $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT16)
+          nop
+          addiu        $reg,$reg,<sym>         (BFD_RELOC_LO16)
+        If there is a constant, it must be added in after.
+
+        In the NewABI, for local symbols, with or without offsets, we want:
+          lw           $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT_PAGE)
+          addiu        $reg,$reg,<sym>         (BFD_RELOC_MIPS_GOT_OFST)
+      */
+      if (HAVE_NEWABI)
        {
-         used_at = 1;
-         macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
-         macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
-                                  &offset_expr, AT, ZERO);
+         ex.X_add_number = ep->X_add_number;
+         ep->X_add_number = 0;
+         relax_start (ep->X_add_symbol);
+         macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_GOT_HI16);
+         macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                      reg, reg, mips_gp_register);
+         macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)",
+                      reg, BFD_RELOC_MIPS_GOT_LO16, reg);
+         if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+           as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+         else if (ex.X_add_number)
+           {
+             ex.X_op = O_constant;
+             macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+                          BFD_RELOC_LO16);
+           }
+
+         ep->X_add_number = ex.X_add_number;
+         relax_switch ();
+         macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+                      BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
+         macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+                      BFD_RELOC_MIPS_GOT_OFST);
+         relax_end ();
        }
-      break;
+      else
+       {
+         ex.X_add_number = ep->X_add_number;
+         ep->X_add_number = 0;
+         relax_start (ep->X_add_symbol);
+         macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_GOT_HI16);
+         macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                      reg, reg, mips_gp_register);
+         macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)",
+                      reg, BFD_RELOC_MIPS_GOT_LO16, reg);
+         relax_switch ();
+         if (reg_needs_delay (mips_gp_register))
+           {
+             /* We need a nop before loading from $gp.  This special
+                check is required because the lui which starts the main
+                instruction stream does not refer to $gp, and so will not
+                insert the nop which may be required.  */
+             macro_build (NULL, "nop", "");
+           }
+         macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+                      BFD_RELOC_MIPS_GOT16, mips_gp_register);
+         load_delay_nop ();
+         macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+                      BFD_RELOC_LO16);
+         relax_end ();
 
-    case M_DEXT:
-      {
-       /* Use unsigned arithmetic.  */
-       addressT pos;
-       addressT size;
+         if (ex.X_add_number != 0)
+           {
+             if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+               as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+             ex.X_op = O_constant;
+             macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+                          BFD_RELOC_LO16);
+           }
+       }
+    }
+  else
+    abort ();
 
-       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 (!mips_opts.at && *used_at == 1)
+    as_bad (_("Macro used $at after \".set noat\""));
+}
 
-       if (pos > 63)
-         {
-           as_bad (_("Improper position (%lu)"), (unsigned long) pos);
-           pos = 1;
-         }
-       if (size == 0 || size > 64 || (pos + size - 1) > 63)
-         {
-           as_bad (_("Improper extract size (%lu, position %lu)"),
-                   (unsigned long) size, (unsigned long) pos);
-           size = 1;
-         }
+/* Move the contents of register SOURCE into register DEST.  */
 
-       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;
+static void
+move_register (int dest, int source)
+{
+  /* Prefer to use a 16-bit microMIPS instruction unless the previous
+     instruction specifically requires a 32-bit one.  */
+  if (mips_opts.micromips
+      && !mips_opts.insn32
+      && !(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",
+                dest, source, 0);
+}
 
-    case M_DINS:
-      {
-       /* Use unsigned arithmetic.  */
-       addressT pos;
-       addressT size;
+/* Emit an SVR4 PIC sequence to load address LOCAL into DEST, where
+   LOCAL is the sum of a symbol and a 16-bit or 32-bit displacement.
+   The two alternatives are:
 
-       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;
-         }
+   Global symbol               Local sybmol
+   -------------               ------------
+   lw DEST,%got(SYMBOL)                lw DEST,%got(SYMBOL + OFFSET)
+   ...                         ...
+   addiu DEST,DEST,OFFSET      addiu DEST,DEST,%lo(SYMBOL + OFFSET)
 
-       if (pos > 63)
-         {
-           as_bad (_("Improper position (%lu)"), (unsigned long) pos);
-           pos = 1;
-         }
-       if (size == 0 || size > 64 || (pos + size - 1) > 63)
-         {
-           as_bad (_("Improper insert size (%lu, position %lu)"),
-                   (unsigned long) size, (unsigned long) pos);
-           size = 1;
-         }
+   load_got_offset emits the first instruction and add_got_offset
+   emits the second for a 16-bit offset or add_got_offset_hilo emits
+   a sequence to add a 32-bit offset using a scratch register.  */
 
-       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;
+static void
+load_got_offset (int dest, expressionS *local)
+{
+  expressionS global;
 
-    case M_DDIV_3:
-      dbl = 1;
-    case M_DIV_3:
-      s = "mflo";
-      goto do_div3;
-    case M_DREM_3:
-      dbl = 1;
-    case M_REM_3:
-      s = "mfhi";
-    do_div3:
-      if (treg == 0)
-       {
-         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;
-       }
+  global = *local;
+  global.X_add_number = 0;
 
-      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);
-       }
-      else
-       {
-         if (mips_opts.micromips)
-           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 (NULL, "break", BRK_FMT, 7);
-         if (mips_opts.micromips)
-           micromips_add_label ();
-       }
-      expr1.X_add_number = -1;
-      used_at = 1;
-      load_register (AT, &expr1, dbl);
-      if (mips_opts.micromips)
-       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);
-      if (dbl)
-       {
-         expr1.X_add_number = 1;
-         load_register (AT, &expr1, dbl);
-         macro_build (NULL, "dsll32", SHFT_FMT, AT, AT, 31);
-       }
-      else
-       {
-         expr1.X_add_number = 0x80000000;
-         macro_build (&expr1, "lui", LUI_FMT, AT, BFD_RELOC_HI16);
-       }
-      if (mips_trap)
-       {
-         macro_build (NULL, "teq", TRAP_FMT, sreg, 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 ();
-       }
-      else
-       {
-         if (mips_opts.micromips)
-           micromips_label_expr (&label_expr);
-         else
-           label_expr.X_add_number = 8;
-         macro_build (&label_expr, "bne", "s,t,p", sreg, AT);
-         macro_build (NULL, "nop", "");
+  relax_start (local->X_add_symbol);
+  macro_build (&global, ADDRESS_LOAD_INSN, "t,o(b)", dest,
+              BFD_RELOC_MIPS_GOT16, mips_gp_register);
+  relax_switch ();
+  macro_build (local, ADDRESS_LOAD_INSN, "t,o(b)", dest,
+              BFD_RELOC_MIPS_GOT16, mips_gp_register);
+  relax_end ();
+}
 
-         /* We want to close the noreorder block as soon as possible, so
-            that later insns are available for delay slot filling.  */
-         end_noreorder ();
+static void
+add_got_offset (int dest, expressionS *local)
+{
+  expressionS global;
 
-         macro_build (NULL, "break", BRK_FMT, 6);
-       }
-      if (mips_opts.micromips)
-       micromips_add_label ();
-      macro_build (NULL, s, MFHL_FMT, dreg);
-      break;
+  global.X_op = O_constant;
+  global.X_op_symbol = NULL;
+  global.X_add_symbol = NULL;
+  global.X_add_number = local->X_add_number;
 
-    case M_DIV_3I:
-      s = "div";
-      s2 = "mflo";
-      goto do_divi;
-    case M_DIVU_3I:
-      s = "divu";
-      s2 = "mflo";
-      goto do_divi;
-    case M_REM_3I:
-      s = "div";
-      s2 = "mfhi";
-      goto do_divi;
-    case M_REMU_3I:
-      s = "divu";
-      s2 = "mfhi";
-      goto do_divi;
-    case M_DDIV_3I:
-      dbl = 1;
-      s = "ddiv";
-      s2 = "mflo";
-      goto do_divi;
-    case M_DDIVU_3I:
-      dbl = 1;
-      s = "ddivu";
-      s2 = "mflo";
-      goto do_divi;
-    case M_DREM_3I:
-      dbl = 1;
-      s = "ddiv";
-      s2 = "mfhi";
-      goto do_divi;
-    case M_DREMU_3I:
-      dbl = 1;
-      s = "ddivu";
-      s2 = "mfhi";
-    do_divi:
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
-       {
-         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 (strcmp (s2, "mflo") == 0)
-           move_register (dreg, sreg);
-         else
-           move_register (dreg, ZERO);
-         break;
-       }
-      if (imm_expr.X_op == O_constant
-         && 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);
-           }
-         else
-           move_register (dreg, ZERO);
-         break;
-       }
+  relax_start (local->X_add_symbol);
+  macro_build (&global, ADDRESS_ADDI_INSN, "t,r,j",
+              dest, dest, BFD_RELOC_LO16);
+  relax_switch ();
+  macro_build (local, ADDRESS_ADDI_INSN, "t,r,j", dest, dest, BFD_RELOC_LO16);
+  relax_end ();
+}
 
-      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);
-      break;
+static void
+add_got_offset_hilo (int dest, expressionS *local, int tmp)
+{
+  expressionS global;
+  int hold_mips_optimize;
 
-    case M_DIVU_3:
-      s = "divu";
-      s2 = "mflo";
-      goto do_divu3;
-    case M_REMU_3:
-      s = "divu";
-      s2 = "mfhi";
-      goto do_divu3;
-    case M_DDIVU_3:
-      s = "ddivu";
-      s2 = "mflo";
-      goto do_divu3;
-    case M_DREMU_3:
-      s = "ddivu";
-      s2 = "mfhi";
-    do_divu3:
-      start_noreorder ();
-      if (mips_trap)
-       {
-         macro_build (NULL, "teq", TRAP_FMT, treg, ZERO, 7);
-         macro_build (NULL, s, "z,s,t", sreg, treg);
-         /* We want to close the noreorder block as soon as possible, so
-            that later insns are available for delay slot filling.  */
-         end_noreorder ();
-       }
-      else
-       {
-         if (mips_opts.micromips)
-           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);
+  global.X_op = O_constant;
+  global.X_op_symbol = NULL;
+  global.X_add_symbol = NULL;
+  global.X_add_number = local->X_add_number;
 
-         /* We want to close the noreorder block as soon as possible, so
-            that later insns are available for delay slot filling.  */
-         end_noreorder ();
-         macro_build (NULL, "break", BRK_FMT, 7);
-         if (mips_opts.micromips)
-           micromips_add_label ();
-       }
-      macro_build (NULL, s2, MFHL_FMT, dreg);
-      break;
+  relax_start (local->X_add_symbol);
+  load_register (tmp, &global, HAVE_64BIT_ADDRESSES);
+  relax_switch ();
+  /* Set mips_optimize around the lui instruction to avoid
+     inserting an unnecessary nop after the lw.  */
+  hold_mips_optimize = mips_optimize;
+  mips_optimize = 2;
+  macro_build_lui (&global, tmp);
+  mips_optimize = hold_mips_optimize;
+  macro_build (local, ADDRESS_ADDI_INSN, "t,r,j", tmp, tmp, BFD_RELOC_LO16);
+  relax_end ();
 
-    case M_DLCA_AB:
-      dbl = 1;
-    case M_LCA_AB:
-      call = 1;
-      goto do_la;
-    case M_DLA_AB:
-      dbl = 1;
-    case M_LA_AB:
-    do_la:
-      /* Load the address of a symbol into a register.  If breg is not
-        zero, we then add a base register to it.  */
+  macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dest, dest, tmp);
+}
 
-      if (dbl && HAVE_32BIT_GPRS)
-       as_warn (_("dla used to load 32-bit register"));
+/* Emit a sequence of instructions to emulate a branch likely operation.
+   BR is an ordinary branch corresponding to one to be emulated.  BRNEG
+   is its complementing branch with the original condition negated.
+   CALL is set if the original branch specified the link operation.
+   EP, FMT, SREG and TREG specify the usual macro_build() parameters.
 
-      if (!dbl && HAVE_64BIT_OBJECTS)
-       as_warn (_("la used to load 64-bit address"));
+   Code like this is produced in the noreorder mode:
 
-      if (offset_expr.X_op == O_constant
-         && offset_expr.X_add_number >= -0x8000
-         && offset_expr.X_add_number < 0x8000)
-       {
-         macro_build (&offset_expr, ADDRESS_ADDI_INSN,
-                      "t,r,j", treg, sreg, BFD_RELOC_LO16);
-         break;
-       }
+       BRNEG   <args>, 1f
+        nop
+       b       <sym>
+        delay slot (executed only if branch taken)
+    1:
 
-      if (mips_opts.at && (treg == breg))
-       {
-         tempreg = AT;
-         used_at = 1;
-       }
-      else
-       {
-         tempreg = treg;
-       }
+   or, if CALL is set:
 
-      if (offset_expr.X_op != O_symbol
-         && offset_expr.X_op != O_constant)
-       {
-         as_bad (_("Expression too complex"));
-         offset_expr.X_op = O_constant;
-       }
+       BRNEG   <args>, 1f
+        nop
+       bal     <sym>
+        delay slot (executed only if branch taken)
+    1:
 
-      if (offset_expr.X_op == O_constant)
-       load_register (tempreg, &offset_expr, HAVE_64BIT_ADDRESSES);
-      else if (mips_pic == NO_PIC)
-       {
-         /* If this is a reference to a GP relative symbol, we want
-              addiu    $tempreg,$gp,<sym>      (BFD_RELOC_GPREL16)
-            Otherwise we want
-              lui      $tempreg,<sym>          (BFD_RELOC_HI16_S)
-              addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
-            If we have a constant, we need two instructions anyhow,
-            so we may as well always use the latter form.
+   In the reorder mode the delay slot would be filled with a nop anyway,
+   so code produced is simply:
 
-            With 64bit address space and a usable $at we want
-              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
-              lui      $at,<sym>               (BFD_RELOC_HI16_S)
-              daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
-              daddiu   $at,<sym>               (BFD_RELOC_LO16)
-              dsll32   $tempreg,0
-              daddu    $tempreg,$tempreg,$at
+       BR      <args>, <sym>
+        nop
 
-            If $at is already in use, we use a path which is suboptimal
-            on superscalar processors.
-              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
-              daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
-              dsll     $tempreg,16
-              daddiu   $tempreg,<sym>          (BFD_RELOC_HI16_S)
-              dsll     $tempreg,16
-              daddiu   $tempreg,<sym>          (BFD_RELOC_LO16)
+   This function is used when producing code for the microMIPS ASE that
+   does not implement branch likely instructions in hardware.  */
 
-            For GP relative symbols in 64bit address space we can use
-            the same sequence as in 32bit address space.  */
-         if (HAVE_64BIT_SYMBOLS)
-           {
-             if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
-                 && !nopic_need_relax (offset_expr.X_add_symbol, 1))
-               {
-                 relax_start (offset_expr.X_add_symbol);
-                 macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
-                              tempreg, mips_gp_register, BFD_RELOC_GPREL16);
-                 relax_switch ();
-               }
+static void
+macro_build_branch_likely (const char *br, const char *brneg,
+                          int call, expressionS *ep, const char *fmt,
+                          unsigned int sreg, unsigned int treg)
+{
+  int noreorder = mips_opts.noreorder;
+  expressionS expr1;
 
-             if (used_at == 0 && mips_opts.at)
-               {
-                 macro_build (&offset_expr, "lui", LUI_FMT,
-                              tempreg, BFD_RELOC_MIPS_HIGHEST);
-                 macro_build (&offset_expr, "lui", LUI_FMT,
-                              AT, BFD_RELOC_HI16_S);
-                 macro_build (&offset_expr, "daddiu", "t,r,j",
-                              tempreg, tempreg, BFD_RELOC_MIPS_HIGHER);
-                 macro_build (&offset_expr, "daddiu", "t,r,j",
-                              AT, AT, BFD_RELOC_LO16);
-                 macro_build (NULL, "dsll32", SHFT_FMT, tempreg, tempreg, 0);
-                 macro_build (NULL, "daddu", "d,v,t", tempreg, tempreg, AT);
-                 used_at = 1;
-               }
-             else
-               {
-                 macro_build (&offset_expr, "lui", LUI_FMT,
-                              tempreg, BFD_RELOC_MIPS_HIGHEST);
-                 macro_build (&offset_expr, "daddiu", "t,r,j",
-                              tempreg, tempreg, BFD_RELOC_MIPS_HIGHER);
-                 macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
-                 macro_build (&offset_expr, "daddiu", "t,r,j",
-                              tempreg, tempreg, BFD_RELOC_HI16_S);
-                 macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
-                 macro_build (&offset_expr, "daddiu", "t,r,j",
-                              tempreg, tempreg, BFD_RELOC_LO16);
-               }
+  gas_assert (mips_opts.micromips);
+  start_noreorder ();
+  if (noreorder)
+    {
+      micromips_label_expr (&expr1);
+      macro_build (&expr1, brneg, fmt, sreg, treg);
+      macro_build (NULL, "nop", "");
+      macro_build (ep, call ? "bal" : "b", "p");
 
-             if (mips_relax.sequence)
-               relax_end ();
-           }
-         else
-           {
-             if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
-                 && !nopic_need_relax (offset_expr.X_add_symbol, 1))
-               {
-                 relax_start (offset_expr.X_add_symbol);
-                 macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
-                              tempreg, mips_gp_register, BFD_RELOC_GPREL16);
-                 relax_switch ();
-               }
-             if (!IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
-               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);
-             if (mips_relax.sequence)
-               relax_end ();
-           }
-       }
-      else if (!mips_big_got && !HAVE_NEWABI)
-       {
-         int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
+      /* Set to true so that append_insn adds a label.  */
+      emit_branch_likely_macro = TRUE;
+    }
+  else
+    {
+      macro_build (ep, br, fmt, sreg, treg);
+      macro_build (NULL, "nop", "");
+    }
+  end_noreorder ();
+}
 
-         /* If this is a reference to an external symbol, and there
-            is no constant, we want
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
-            or for lca or if tempreg is PIC_CALL_REG
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_CALL16)
-            For a local symbol, we want
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
-              nop
-              addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+/* Emit a coprocessor branch-likely macro specified by TYPE, using CC as
+   the condition code tested.  EP specifies the branch target.  */
 
-            If we have a small constant, and this is a reference to
-            an external symbol, we want
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
-              nop
-              addiu    $tempreg,$tempreg,<constant>
-            For a local symbol, we want the same instruction
-            sequence, but we output a BFD_RELOC_LO16 reloc on the
-            addiu instruction.
+static void
+macro_build_branch_ccl (int type, expressionS *ep, unsigned int cc)
+{
+  const int call = 0;
+  const char *brneg;
+  const char *br;
 
-            If we have a large constant, and this is a reference to
-            an external symbol, we want
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
-              lui      $at,<hiconstant>
-              addiu    $at,$at,<loconstant>
-              addu     $tempreg,$tempreg,$at
-            For a local symbol, we want the same instruction
-            sequence, but we output a BFD_RELOC_LO16 reloc on the
-            addiu instruction.
-          */
+  switch (type)
+    {
+    case M_BC1FL:
+      br = "bc1f";
+      brneg = "bc1t";
+      break;
+    case M_BC1TL:
+      br = "bc1t";
+      brneg = "bc1f";
+      break;
+    case M_BC2FL:
+      br = "bc2f";
+      brneg = "bc2t";
+      break;
+    case M_BC2TL:
+      br = "bc2t";
+      brneg = "bc2f";
+      break;
+    default:
+      abort ();
+    }
+  macro_build_branch_likely (br, brneg, call, ep, "N,p", cc, ZERO);
+}
 
-         if (offset_expr.X_add_number == 0)
-           {
-             if (mips_pic == SVR4_PIC
-                 && breg == 0
-                 && (call || tempreg == PIC_CALL_REG))
-               lw_reloc_type = (int) BFD_RELOC_MIPS_CALL16;
+/* Emit a two-argument branch macro specified by TYPE, using SREG as
+   the register tested.  EP specifies the branch target.  */
 
-             relax_start (offset_expr.X_add_symbol);
-             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
-                          lw_reloc_type, mips_gp_register);
-             if (breg != 0)
-               {
-                 /* We're going to put in an addu instruction using
-                    tempreg, so we may as well insert the nop right
-                    now.  */
-                 load_delay_nop ();
-               }
-             relax_switch ();
-             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
-                          tempreg, BFD_RELOC_MIPS_GOT16, mips_gp_register);
-             load_delay_nop ();
-             macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
-                          tempreg, tempreg, BFD_RELOC_LO16);
-             relax_end ();
-             /* FIXME: If breg == 0, and the next instruction uses
-                $tempreg, then if this variant case is used an extra
-                nop will be generated.  */
-           }
-         else if (offset_expr.X_add_number >= -0x8000
-                  && offset_expr.X_add_number < 0x8000)
-           {
-             load_got_offset (tempreg, &offset_expr);
-             load_delay_nop ();
-             add_got_offset (tempreg, &offset_expr);
-           }
-         else
-           {
-             expr1.X_add_number = offset_expr.X_add_number;
-             offset_expr.X_add_number =
-               SEXT_16BIT (offset_expr.X_add_number);
-             load_got_offset (tempreg, &offset_expr);
-             offset_expr.X_add_number = expr1.X_add_number;
-             /* 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
-                we want to load the constant into AT, we add our
-                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)
-               {
-                 load_delay_nop ();
-                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                              treg, AT, breg);
-                 breg = 0;
-                 tempreg = treg;
-               }
-             add_got_offset_hilo (tempreg, &offset_expr, AT);
-             used_at = 1;
-           }
-       }
-      else if (!mips_big_got && HAVE_NEWABI)
-       {
-         int add_breg_early = 0;
+static void
+macro_build_branch_rs (int type, expressionS *ep, unsigned int sreg)
+{
+  const char *brneg = NULL;
+  const char *br;
+  int call = 0;
 
-         /* If this is a reference to an external, and there is no
-            constant, or local symbol (*), with or without a
-            constant, we want
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_DISP)
-            or for lca or if tempreg is PIC_CALL_REG
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_CALL16)
+  switch (type)
+    {
+    case M_BGEZ:
+      br = "bgez";
+      break;
+    case M_BGEZL:
+      br = mips_opts.micromips ? "bgez" : "bgezl";
+      brneg = "bltz";
+      break;
+    case M_BGEZALL:
+      gas_assert (mips_opts.micromips);
+      br = mips_opts.insn32 ? "bgezal" : "bgezals";
+      brneg = "bltz";
+      call = 1;
+      break;
+    case M_BGTZ:
+      br = "bgtz";
+      break;
+    case M_BGTZL:
+      br = mips_opts.micromips ? "bgtz" : "bgtzl";
+      brneg = "blez";
+      break;
+    case M_BLEZ:
+      br = "blez";
+      break;
+    case M_BLEZL:
+      br = mips_opts.micromips ? "blez" : "blezl";
+      brneg = "bgtz";
+      break;
+    case M_BLTZ:
+      br = "bltz";
+      break;
+    case M_BLTZL:
+      br = mips_opts.micromips ? "bltz" : "bltzl";
+      brneg = "bgez";
+      break;
+    case M_BLTZALL:
+      gas_assert (mips_opts.micromips);
+      br = mips_opts.insn32 ? "bltzal" : "bltzals";
+      brneg = "bgez";
+      call = 1;
+      break;
+    default:
+      abort ();
+    }
+  if (mips_opts.micromips && brneg)
+    macro_build_branch_likely (br, brneg, call, ep, "s,p", sreg, ZERO);
+  else
+    macro_build (ep, br, "s,p", sreg);
+}
 
-            If we have a small constant, and this is a reference to
-            an external symbol, we want
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_DISP)
-              addiu    $tempreg,$tempreg,<constant>
+/* Emit a three-argument branch macro specified by TYPE, using SREG and
+   TREG as the registers tested.  EP specifies the branch target.  */
 
-            If we have a large constant, and this is a reference to
-            an external symbol, we want
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_DISP)
-              lui      $at,<hiconstant>
-              addiu    $at,$at,<loconstant>
-              addu     $tempreg,$tempreg,$at
+static void
+macro_build_branch_rsrt (int type, expressionS *ep,
+                        unsigned int sreg, unsigned int treg)
+{
+  const char *brneg = NULL;
+  const int call = 0;
+  const char *br;
 
-            (*) Other assemblers seem to prefer GOT_PAGE/GOT_OFST for
-            local symbols, even though it introduces an additional
-            instruction.  */
+  switch (type)
+    {
+    case M_BEQ:
+    case M_BEQ_I:
+      br = "beq";
+      break;
+    case M_BEQL:
+    case M_BEQL_I:
+      br = mips_opts.micromips ? "beq" : "beql";
+      brneg = "bne";
+      break;
+    case M_BNE:
+    case M_BNE_I:
+      br = "bne";
+      break;
+    case M_BNEL:
+    case M_BNEL_I:
+      br = mips_opts.micromips ? "bne" : "bnel";
+      brneg = "beq";
+      break;
+    default:
+      abort ();
+    }
+  if (mips_opts.micromips && brneg)
+    macro_build_branch_likely (br, brneg, call, ep, "s,t,p", sreg, treg);
+  else
+    macro_build (ep, br, "s,t,p", sreg, treg);
+}
 
-         if (offset_expr.X_add_number)
-           {
-             expr1.X_add_number = offset_expr.X_add_number;
-             offset_expr.X_add_number = 0;
+/* Return the high part that should be loaded in order to make the low
+   part of VALUE accessible using an offset of OFFBITS bits.  */
 
-             relax_start (offset_expr.X_add_symbol);
-             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
-                          BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+static offsetT
+offset_high_part (offsetT value, unsigned int offbits)
+{
+  offsetT bias;
+  addressT low_mask;
+
+  if (offbits == 0)
+    return value;
+  bias = 1 << (offbits - 1);
+  low_mask = bias * 2 - 1;
+  return (value + bias) & ~low_mask;
+}
 
-             if (expr1.X_add_number >= -0x8000
-                 && expr1.X_add_number < 0x8000)
-               {
-                 macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
-                              tempreg, tempreg, BFD_RELOC_LO16);
-               }
-             else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
-               {
-                 /* 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
-                    we want to load the constant into AT, we add our
-                    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)
-                   dreg = tempreg;
-                 else
-                   {
-                     gas_assert (tempreg == AT);
-                     macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                                  treg, AT, breg);
-                     dreg = treg;
-                     add_breg_early = 1;
-                   }
+/* Return true if the value stored in offset_expr and offset_reloc
+   fits into a signed offset of OFFBITS bits.  RANGE is the maximum
+   amount that the caller wants to add without inducing overflow
+   and ALIGN is the known alignment of the value in bytes.  */
 
-                 load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
-                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                              dreg, dreg, AT);
+static bfd_boolean
+small_offset_p (unsigned int range, unsigned int align, unsigned int offbits)
+{
+  if (offbits == 16)
+    {
+      /* Accept any relocation operator if overflow isn't a concern.  */
+      if (range < align && *offset_reloc != BFD_RELOC_UNUSED)
+       return TRUE;
 
-                 used_at = 1;
-               }
-             else
-               as_bad (_("PIC code offset overflow (max 32 signed bits)"));
+      /* These relocations are guaranteed not to overflow in correct links.  */
+      if (*offset_reloc == BFD_RELOC_MIPS_LITERAL
+         || gprel16_reloc_p (*offset_reloc))
+       return TRUE;
+    }
+  if (offset_expr.X_op == O_constant
+      && offset_high_part (offset_expr.X_add_number, offbits) == 0
+      && offset_high_part (offset_expr.X_add_number + range, offbits) == 0)
+    return TRUE;
+  return FALSE;
+}
 
-             relax_switch ();
-             offset_expr.X_add_number = expr1.X_add_number;
+/*
+ *                     Build macros
+ *   This routine implements the seemingly endless macro or synthesized
+ * instructions and addressing modes in the mips assembly language. Many
+ * of these macros are simple and are similar to each other. These could
+ * probably be handled by some kind of table or grammar approach instead of
+ * this verbose method. Others are not simple macros but are more like
+ * optimizing code generation.
+ *   One interesting optimization is when several store macros appear
+ * consecutively that would load AT with the upper half of the same address.
+ * The ensuing load upper instructions are ommited. This implies some kind
+ * of global optimization. We currently only optimize within a single macro.
+ *   For many of the load and store macros if the address is specified as a
+ * constant expression in the first 64k of memory (ie ld $2,0x4000c) we
+ * first load register 'at' with zero and use it as the base register. The
+ * mips assembler simply uses register $zero. Just one tiny optimization
+ * we're missing.
+ */
+static void
+macro (struct mips_cl_insn *ip, char *str)
+{
+  unsigned int treg, sreg, dreg, breg;
+  unsigned int tempreg;
+  int mask;
+  int used_at = 0;
+  expressionS label_expr;
+  expressionS expr1;
+  expressionS *ep;
+  const char *s;
+  const char *s2;
+  const char *fmt;
+  int likely = 0;
+  int coproc = 0;
+  int offbits = 16;
+  int call = 0;
+  int jals = 0;
+  int dbl = 0;
+  int imm = 0;
+  int ust = 0;
+  int lp = 0;
+  bfd_boolean large_offset;
+  int off;
+  int hold_mips_optimize;
+  unsigned int align;
 
-             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
-                          BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
-             if (add_breg_early)
-               {
-                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                              treg, tempreg, breg);
-                 breg = 0;
-                 tempreg = treg;
-               }
-             relax_end ();
-           }
-         else if (breg == 0 && (call || tempreg == PIC_CALL_REG))
-           {
-             relax_start (offset_expr.X_add_symbol);
-             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
-                          BFD_RELOC_MIPS_CALL16, mips_gp_register);
-             relax_switch ();
-             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
-                          BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
-             relax_end ();
-           }
-         else
-           {
-             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
-                          BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
-           }
-       }
-      else if (mips_big_got && !HAVE_NEWABI)
-       {
-         int gpdelay;
-         int lui_reloc_type = (int) BFD_RELOC_MIPS_GOT_HI16;
-         int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT_LO16;
-         int local_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
+  gas_assert (! mips_opts.mips16);
 
-         /* This is the large GOT case.  If this is a reference to an
-            external symbol, and there is no constant, we want
-              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
-              addu     $tempreg,$tempreg,$gp
-              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
-            or for lca or if tempreg is PIC_CALL_REG
-              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_CALL_HI16)
-              addu     $tempreg,$tempreg,$gp
-              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_CALL_LO16)
-            For a local symbol, we want
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
-              nop
-              addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+  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);
+  mask = ip->insn_mo->mask;
 
-            If we have a small constant, and this is a reference to
-            an external symbol, we want
-              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
-              addu     $tempreg,$tempreg,$gp
-              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
-              nop
-              addiu    $tempreg,$tempreg,<constant>
-            For a local symbol, we want
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
-              nop
-              addiu    $tempreg,$tempreg,<constant> (BFD_RELOC_LO16)
+  label_expr.X_op = O_constant;
+  label_expr.X_op_symbol = NULL;
+  label_expr.X_add_symbol = NULL;
+  label_expr.X_add_number = 0;
 
-            If we have a large constant, and this is a reference to
-            an external symbol, we want
-              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
-              addu     $tempreg,$tempreg,$gp
-              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
-              lui      $at,<hiconstant>
-              addiu    $at,$at,<loconstant>
-              addu     $tempreg,$tempreg,$at
-            For a local symbol, we want
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
-              lui      $at,<hiconstant>
-              addiu    $at,$at,<loconstant>    (BFD_RELOC_LO16)
-              addu     $tempreg,$tempreg,$at
-         */
+  expr1.X_op = O_constant;
+  expr1.X_op_symbol = NULL;
+  expr1.X_add_symbol = NULL;
+  expr1.X_add_number = 1;
+  align = 1;
 
-         expr1.X_add_number = offset_expr.X_add_number;
-         offset_expr.X_add_number = 0;
-         relax_start (offset_expr.X_add_symbol);
-         gpdelay = reg_needs_delay (mips_gp_register);
-         if (expr1.X_add_number == 0 && breg == 0
-             && (call || tempreg == PIC_CALL_REG))
-           {
-             lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16;
-             lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16;
-           }
-         macro_build (&offset_expr, "lui", LUI_FMT, tempreg, lui_reloc_type);
-         macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                      tempreg, tempreg, mips_gp_register);
-         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
-                      tempreg, lw_reloc_type, tempreg);
-         if (expr1.X_add_number == 0)
-           {
-             if (breg != 0)
-               {
-                 /* We're going to put in an addu instruction using
-                    tempreg, so we may as well insert the nop right
-                    now.  */
-                 load_delay_nop ();
-               }
-           }
-         else if (expr1.X_add_number >= -0x8000
-                  && expr1.X_add_number < 0x8000)
-           {
-             load_delay_nop ();
-             macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
-                          tempreg, tempreg, BFD_RELOC_LO16);
-           }
-         else
-           {
-             /* 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
-                we want to load the constant into AT, we add our
-                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)
-               dreg = tempreg;
-             else
-               {
-                 gas_assert (tempreg == AT);
-                 load_delay_nop ();
-                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                              treg, AT, breg);
-                 dreg = treg;
-               }
+  switch (mask)
+    {
+    case M_DABS:
+      dbl = 1;
+    case M_ABS:
+      /*    bgez    $a0,1f
+           move    v0,$a0
+           sub     v0,$zero,$a0
+        1:
+       */
 
-             load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
-             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
+      start_noreorder ();
 
-             used_at = 1;
-           }
-         offset_expr.X_add_number = SEXT_16BIT (expr1.X_add_number);
-         relax_switch ();
+      if (mips_opts.micromips)
+       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 (NULL, "nop", "");
+      else
+       move_register (dreg, sreg);
+      macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", dreg, 0, sreg);
+      if (mips_opts.micromips)
+       micromips_add_label ();
 
-         if (gpdelay)
-           {
-             /* This is needed because this instruction uses $gp, but
-                the first instruction on the main stream does not.  */
-             macro_build (NULL, "nop", "");
-           }
+      end_noreorder ();
+      break;
 
-         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
-                      local_reloc_type, mips_gp_register);
-         if (expr1.X_add_number >= -0x8000
-             && expr1.X_add_number < 0x8000)
-           {
-             load_delay_nop ();
-             macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
-                          tempreg, tempreg, BFD_RELOC_LO16);
-             /* FIXME: If add_number is 0, and there was no base
-                register, the external symbol case ended with a load,
-                so if the symbol turns out to not be external, and
-                the next instruction uses tempreg, an unnecessary nop
-                will be inserted.  */
-           }
+    case M_ADD_I:
+      s = "addi";
+      s2 = "add";
+      goto do_addi;
+    case M_ADDU_I:
+      s = "addiu";
+      s2 = "addu";
+      goto do_addi;
+    case M_DADD_I:
+      dbl = 1;
+      s = "daddi";
+      s2 = "dadd";
+      if (!mips_opts.micromips)
+       goto do_addi;
+      if (imm_expr.X_op == O_constant
+         && 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);
+         break;
+       }
+      goto do_addi_i;
+    case M_DADDU_I:
+      dbl = 1;
+      s = "daddiu";
+      s2 = "daddu";
+    do_addi:
+      if (imm_expr.X_op == O_constant
+         && 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);
+         break;
+       }
+    do_addi_i:
+      used_at = 1;
+      load_register (AT, &imm_expr, dbl);
+      macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
+      break;
+
+    case M_AND_I:
+      s = "andi";
+      s2 = "and";
+      goto do_bit;
+    case M_OR_I:
+      s = "ori";
+      s2 = "or";
+      goto do_bit;
+    case M_NOR_I:
+      s = "";
+      s2 = "nor";
+      goto do_bit;
+    case M_XOR_I:
+      s = "xori";
+      s2 = "xor";
+    do_bit:
+      if (imm_expr.X_op == O_constant
+         && 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);
          else
            {
-             if (breg == treg)
-               {
-                 /* 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;
-                 /* We set breg to 0 because we have arranged to add
-                    it in in both cases.  */
-                 breg = 0;
-               }
-
-             macro_build_lui (&expr1, AT);
-             macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
-                          AT, AT, BFD_RELOC_LO16);
-             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                          tempreg, tempreg, AT);
-             used_at = 1;
+             macro_build (&imm_expr, "ori", "t,r,i",
+                          treg, sreg, BFD_RELOC_LO16);
+             macro_build (NULL, "nor", "d,v,t", treg, treg, 0);
            }
-         relax_end ();
+         break;
        }
-      else if (mips_big_got && HAVE_NEWABI)
-       {
-         int lui_reloc_type = (int) BFD_RELOC_MIPS_GOT_HI16;
-         int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT_LO16;
-         int add_breg_early = 0;
-
-         /* This is the large GOT case.  If this is a reference to an
-            external symbol, and there is no constant, we want
-              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
-              add      $tempreg,$tempreg,$gp
-              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
-            or for lca or if tempreg is PIC_CALL_REG
-              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_CALL_HI16)
-              add      $tempreg,$tempreg,$gp
-              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_CALL_LO16)
-
-            If we have a small constant, and this is a reference to
-            an external symbol, we want
-              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
-              add      $tempreg,$tempreg,$gp
-              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
-              addi     $tempreg,$tempreg,<constant>
 
-            If we have a large constant, and this is a reference to
-            an external symbol, we want
-              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
-              addu     $tempreg,$tempreg,$gp
-              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
-              lui      $at,<hiconstant>
-              addi     $at,$at,<loconstant>
-              add      $tempreg,$tempreg,$at
+      used_at = 1;
+      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+      macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
+      break;
 
-            If we have NewABI, and we know it's a local symbol, we want
-              lw       $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT_PAGE)
-              addiu    $reg,$reg,<sym>         (BFD_RELOC_MIPS_GOT_OFST)
-            otherwise we have to resort to GOT_HI16/GOT_LO16.  */
-
-         relax_start (offset_expr.X_add_symbol);
-
-         expr1.X_add_number = offset_expr.X_add_number;
-         offset_expr.X_add_number = 0;
-
-         if (expr1.X_add_number == 0 && breg == 0
-             && (call || tempreg == PIC_CALL_REG))
-           {
-             lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16;
-             lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16;
-           }
-         macro_build (&offset_expr, "lui", LUI_FMT, tempreg, lui_reloc_type);
-         macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                      tempreg, tempreg, mips_gp_register);
-         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
-                      tempreg, lw_reloc_type, tempreg);
-
-         if (expr1.X_add_number == 0)
-           ;
-         else if (expr1.X_add_number >= -0x8000
-                  && expr1.X_add_number < 0x8000)
-           {
-             macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
-                          tempreg, tempreg, BFD_RELOC_LO16);
-           }
-         else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
-           {
-             /* 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
-                we want to load the constant into AT, we add our
-                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)
-               dreg = tempreg;
-             else
-               {
-                 gas_assert (tempreg == AT);
-                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                              treg, AT, breg);
-                 dreg = treg;
-                 add_breg_early = 1;
-               }
-
-             load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
-             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
-
-             used_at = 1;
-           }
-         else
-           as_bad (_("PIC code offset overflow (max 32 signed bits)"));
-
-         relax_switch ();
-         offset_expr.X_add_number = expr1.X_add_number;
-         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
-                      BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
-         macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
-                      tempreg, BFD_RELOC_MIPS_GOT_OFST);
-         if (add_breg_early)
-           {
-             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                          treg, tempreg, breg);
-             breg = 0;
-             tempreg = treg;
-           }
-         relax_end ();
+    case M_BALIGN:
+      switch (imm_expr.X_add_number)
+       {
+       case 0:
+         macro_build (NULL, "nop", "");
+         break;
+       case 2:
+         macro_build (NULL, "packrl.ph", "d,s,t", treg, treg, sreg);
+         break;
+       case 1:
+       case 3:
+         macro_build (NULL, "balign", "t,s,2", treg, sreg,
+                      (int) imm_expr.X_add_number);
+         break;
+       default:
+         as_bad (_("BALIGN immediate not 0, 1, 2 or 3 (%lu)"),
+                 (unsigned long) imm_expr.X_add_number);
+         break;
        }
-      else
-       abort ();
-
-      if (breg != 0)
-       macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, tempreg, breg);
-      break;
-
-    case M_MSGSND:
-      gas_assert (!mips_opts.micromips);
-      {
-       unsigned long temp = (treg << 16) | (0x01);
-       macro_build (NULL, "c2", "C", temp);
-      }
-      break;
-
-    case M_MSGLD:
-      gas_assert (!mips_opts.micromips);
-      {
-       unsigned long temp = (0x02);
-       macro_build (NULL, "c2", "C", temp);
-      }
       break;
 
-    case M_MSGLD_T:
-      gas_assert (!mips_opts.micromips);
-      {
-       unsigned long temp = (treg << 16) | (0x02);
-       macro_build (NULL, "c2", "C", temp);
-      }
+    case M_BC1FL:
+    case M_BC1TL:
+    case M_BC2FL:
+    case M_BC2TL:
+      gas_assert (mips_opts.micromips);
+      macro_build_branch_ccl (mask, &offset_expr,
+                             EXTRACT_OPERAND (1, BCC, *ip));
       break;
 
-    case M_MSGWAIT:
-      gas_assert (!mips_opts.micromips);
-      macro_build (NULL, "c2", "C", 3);
+    case M_BEQ_I:
+    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;
+      else
+       {
+         treg = AT;
+         used_at = 1;
+         load_register (treg, &imm_expr, HAVE_64BIT_GPRS);
+       }
+      /* Fall through.  */
+    case M_BEQL:
+    case M_BNEL:
+      macro_build_branch_rsrt (mask, &offset_expr, sreg, treg);
       break;
 
-    case M_MSGWAIT_T:
-      gas_assert (!mips_opts.micromips);
-      {
-       unsigned long temp = (treg << 16) | 0x03;
-       macro_build (NULL, "c2", "C", temp);
-      }
+    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);
+      else
+       {
+         used_at = 1;
+         macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
+         macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+                                  &offset_expr, AT, ZERO);
+       }
       break;
 
-    case M_J_A:
-      /* The j instruction may not be used in PIC code, since it
-        requires an absolute address.  We convert it to a b
-        instruction.  */
-      if (mips_pic == NO_PIC)
-       macro_build (&offset_expr, "j", "a");
-      else
-       macro_build (&offset_expr, "b", "p");
+    case M_BGEZL:
+    case M_BGEZALL:
+    case M_BGTZL:
+    case M_BLEZL:
+    case M_BLTZL:
+    case M_BLTZALL:
+      macro_build_branch_rs (mask, &offset_expr, sreg);
       break;
 
-      /* The jal instructions must be handled as macros because when
-        generating PIC code they expand to multi-instruction
-        sequences.  Normally they are simple instructions.  */
-    case M_JALS_1:
-      dreg = RA;
-      /* Fall through.  */
-    case M_JALS_2:
-      gas_assert (mips_opts.micromips);
-      jals = 1;
-      goto jal;
-    case M_JAL_1:
-      dreg = RA;
-      /* Fall through.  */
-    case M_JAL_2:
-    jal:
-      if (mips_pic == NO_PIC)
+    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)
        {
-         s = jals ? "jalrs" : "jalr";
-         if (mips_opts.micromips
-             && dreg == RA
-             && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
-           macro_build (NULL, s, "mj", sreg);
+       do_false:
+         /* Result is always false.  */
+         if (! likely)
+           macro_build (NULL, "nop", "");
          else
-           macro_build (NULL, s, JALR_FMT, dreg, sreg);
+           macro_build_branch_rsrt (M_BNEL, &offset_expr, ZERO, ZERO);
+         break;
        }
-      else
+      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)
        {
-         int cprestore = (mips_pic == SVR4_PIC && !HAVE_NEWABI
-                          && mips_cprestore_offset >= 0);
+         macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ,
+                                &offset_expr, sreg);
+         break;
+       }
+      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+       {
+         macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ,
+                                &offset_expr, sreg);
+         break;
+       }
+      if (imm_expr.X_op == O_constant && imm_expr.X_add_number <= GPR_SMIN)
+       {
+       do_true:
+         /* result is always true */
+         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);
+      macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+                              &offset_expr, AT, ZERO);
+      break;
 
-         if (sreg != PIC_CALL_REG)
-           as_warn (_("MIPS PIC call to register other than $25"));
+    case M_BGEUL:
+      likely = 1;
+    case M_BGEU:
+      if (treg == 0)
+       goto do_true;
+      else if (sreg == 0)
+       macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+                                &offset_expr, ZERO, treg);
+      else
+       {
+         used_at = 1;
+         macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
+         macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+                                  &offset_expr, AT, ZERO);
+       }
+      break;
 
-         s = (mips_opts.micromips && (!mips_opts.noreorder || cprestore)
-              ? "jalrs" : "jalr");
-         if (mips_opts.micromips
-             && dreg == RA
-             && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
-           macro_build (NULL, s, "mj", sreg);
-         else
-           macro_build (NULL, s, JALR_FMT, dreg, sreg);
-         if (mips_pic == SVR4_PIC && !HAVE_NEWABI)
-           {
-             if (mips_cprestore_offset < 0)
-               as_warn (_("No .cprestore pseudo-op used in PIC code"));
-             else
-               {
-                 if (!mips_frame_reg_valid)
-                   {
-                     as_warn (_("No .frame pseudo-op used in PIC code"));
-                     /* Quiet this warning.  */
-                     mips_frame_reg_valid = 1;
-                   }
-                 if (!mips_cprestore_valid)
-                   {
-                     as_warn (_("No .cprestore pseudo-op used in PIC code"));
-                     /* Quiet this warning.  */
-                     mips_cprestore_valid = 1;
-                   }
-                 if (mips_opts.noreorder)
-                   macro_build (NULL, "nop", "");
-                 expr1.X_add_number = mips_cprestore_offset;
-                 macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
-                                               mips_gp_register,
-                                               mips_frame_reg,
-                                               HAVE_64BIT_ADDRESSES);
-               }
-           }
+    case M_BGTUL_I:
+      likely = 1;
+    case M_BGTU_I:
+      if (sreg == 0
+         || (HAVE_32BIT_GPRS
+             && imm_expr.X_op == O_constant
+             && 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)
+       goto do_true;
+      else if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+       macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+                                &offset_expr, sreg, ZERO);
+      else
+       {
+         used_at = 1;
+         set_at (sreg, 1);
+         macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+                                  &offset_expr, AT, ZERO);
        }
-
       break;
 
-    case M_JALS_A:
-      gas_assert (mips_opts.micromips);
-      jals = 1;
-      /* Fall through.  */
-    case M_JAL_A:
-      if (mips_pic == NO_PIC)
-       macro_build (&offset_expr, jals ? "jals" : "jal", "a");
-      else if (mips_pic == SVR4_PIC)
+    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);
+      else
        {
-         /* If this is a reference to an external symbol, and we are
-            using a small GOT, we want
-              lw       $25,<sym>($gp)          (BFD_RELOC_MIPS_CALL16)
-              nop
-              jalr     $ra,$25
-              nop
-              lw       $gp,cprestore($sp)
-            The cprestore value is set using the .cprestore
-            pseudo-op.  If we are using a big GOT, we want
-              lui      $25,<sym>               (BFD_RELOC_MIPS_CALL_HI16)
-              addu     $25,$25,$gp
-              lw       $25,<sym>($25)          (BFD_RELOC_MIPS_CALL_LO16)
-              nop
-              jalr     $ra,$25
-              nop
-              lw       $gp,cprestore($sp)
-            If the symbol is not external, we want
-              lw       $25,<sym>($gp)          (BFD_RELOC_MIPS_GOT16)
-              nop
-              addiu    $25,$25,<sym>           (BFD_RELOC_LO16)
-              jalr     $ra,$25
-              nop
-              lw $gp,cprestore($sp)
-
-            For NewABI, we use the same CALL16 or CALL_HI16/CALL_LO16
-            sequences above, minus nops, unless the symbol is local,
-            which enables us to use GOT_PAGE/GOT_OFST (big got) or
-            GOT_DISP.  */
-         if (HAVE_NEWABI)
-           {
-             if (!mips_big_got)
-               {
-                 relax_start (offset_expr.X_add_symbol);
-                 macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
-                              PIC_CALL_REG, BFD_RELOC_MIPS_CALL16,
-                              mips_gp_register);
-                 relax_switch ();
-                 macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
-                              PIC_CALL_REG, BFD_RELOC_MIPS_GOT_DISP,
-                              mips_gp_register);
-                 relax_end ();
-               }
-             else
-               {
-                 relax_start (offset_expr.X_add_symbol);
-                 macro_build (&offset_expr, "lui", LUI_FMT, PIC_CALL_REG,
-                              BFD_RELOC_MIPS_CALL_HI16);
-                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
-                              PIC_CALL_REG, mips_gp_register);
-                 macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
-                              PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16,
-                              PIC_CALL_REG);
-                 relax_switch ();
-                 macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
-                              PIC_CALL_REG, BFD_RELOC_MIPS_GOT_PAGE,
-                              mips_gp_register);
-                 macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
-                              PIC_CALL_REG, PIC_CALL_REG,
-                              BFD_RELOC_MIPS_GOT_OFST);
-                 relax_end ();
-               }
-
-             macro_build_jalr (&offset_expr, 0);
-           }
-         else
-           {
-             relax_start (offset_expr.X_add_symbol);
-             if (!mips_big_got)
-               {
-                 macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
-                              PIC_CALL_REG, BFD_RELOC_MIPS_CALL16,
-                              mips_gp_register);
-                 load_delay_nop ();
-                 relax_switch ();
-               }
-             else
-               {
-                 int gpdelay;
-
-                 gpdelay = reg_needs_delay (mips_gp_register);
-                 macro_build (&offset_expr, "lui", LUI_FMT, PIC_CALL_REG,
-                              BFD_RELOC_MIPS_CALL_HI16);
-                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
-                              PIC_CALL_REG, mips_gp_register);
-                 macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
-                              PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16,
-                              PIC_CALL_REG);
-                 load_delay_nop ();
-                 relax_switch ();
-                 if (gpdelay)
-                   macro_build (NULL, "nop", "");
-               }
-             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
-                          PIC_CALL_REG, BFD_RELOC_MIPS_GOT16,
-                          mips_gp_register);
-             load_delay_nop ();
-             macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
-                          PIC_CALL_REG, PIC_CALL_REG, BFD_RELOC_LO16);
-             relax_end ();
-             macro_build_jalr (&offset_expr, mips_cprestore_offset >= 0);
-
-             if (mips_cprestore_offset < 0)
-               as_warn (_("No .cprestore pseudo-op used in PIC code"));
-             else
-               {
-                 if (!mips_frame_reg_valid)
-                   {
-                     as_warn (_("No .frame pseudo-op used in PIC code"));
-                     /* Quiet this warning.  */
-                     mips_frame_reg_valid = 1;
-                   }
-                 if (!mips_cprestore_valid)
-                   {
-                     as_warn (_("No .cprestore pseudo-op used in PIC code"));
-                     /* Quiet this warning.  */
-                     mips_cprestore_valid = 1;
-                   }
-                 if (mips_opts.noreorder)
-                   macro_build (NULL, "nop", "");
-                 expr1.X_add_number = mips_cprestore_offset;
-                 macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
-                                               mips_gp_register,
-                                               mips_frame_reg,
-                                               HAVE_64BIT_ADDRESSES);
-               }
-           }
+         used_at = 1;
+         macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
+         macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+                                  &offset_expr, AT, ZERO);
        }
-      else if (mips_pic == VXWORKS_PIC)
-       as_bad (_("Non-PIC jump used in PIC library"));
+      break;
+
+    case M_BGTUL:
+      likely = 1;
+    case M_BGTU:
+      if (treg == 0)
+       macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+                                &offset_expr, sreg, ZERO);
+      else if (sreg == 0)
+       goto do_false;
       else
-       abort ();
+       {
+         used_at = 1;
+         macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
+         macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+                                  &offset_expr, AT, ZERO);
+       }
+      break;
 
+    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);
+      else
+       {
+         used_at = 1;
+         macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
+         macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+                                  &offset_expr, AT, ZERO);
+       }
       break;
 
-    case M_LBUE_AB:
-      ab = 1;
-    case M_LBUE_OB:
-      s = "lbue";
-      fmt = "t,+j(b)";
-      offbits = 9;
-      goto ld_st;
-    case M_LHUE_AB:
-      ab = 1;
-    case M_LHUE_OB:
-      s = "lhue";
-      fmt = "t,+j(b)";
-      offbits = 9;
-      goto ld_st;
-    case M_LBE_AB:
-      ab = 1;
-    case M_LBE_OB:
-      s = "lbe";
-      fmt = "t,+j(b)";
-      offbits = 9;
-      goto ld_st;
-    case M_LHE_AB:
-      ab = 1;
-    case M_LHE_OB:
-      s = "lhe";
-      fmt = "t,+j(b)";
-      offbits = 9;
-      goto ld_st;
-    case M_LLE_AB:
-      ab = 1;
-    case M_LLE_OB:
-      s = "lle";
-      fmt = "t,+j(b)";
-      offbits = 9;
-      goto ld_st;
-    case M_LWE_AB:
-      ab = 1;
-    case M_LWE_OB:
-      s = "lwe";
-      fmt = "t,+j(b)";
-      offbits = 9;
-      goto ld_st;
-    case M_LWLE_AB:
-      ab = 1;
-    case M_LWLE_OB:
-      s = "lwle";
-      fmt = "t,+j(b)";
-      offbits = 9;
-      goto ld_st;
-    case M_LWRE_AB:
-      ab = 1;
-    case M_LWRE_OB:
-      s = "lwre";
-      fmt = "t,+j(b)";
-      offbits = 9;
-      goto ld_st;
-    case M_SBE_AB:
-      ab = 1;
-    case M_SBE_OB:
-      s = "sbe";
-      fmt = "t,+j(b)";
-      offbits = 9;
-      goto ld_st;
-    case M_SCE_AB:
-      ab = 1;
-    case M_SCE_OB:
-      s = "sce";
-      fmt = "t,+j(b)";
-      offbits = 9;
-      goto ld_st;
-    case M_SHE_AB:
-      ab = 1;
-    case M_SHE_OB:
-      s = "she";
-      fmt = "t,+j(b)";
-      offbits = 9;
-      goto ld_st;
-    case M_SWE_AB:
-      ab = 1;
-    case M_SWE_OB:
-      s = "swe";
-      fmt = "t,+j(b)";
-      offbits = 9;
-      goto ld_st;
-    case M_SWLE_AB:
-      ab = 1;
-    case M_SWLE_OB:
-      s = "swle";
-      fmt = "t,+j(b)";
-      offbits = 9;
-      goto ld_st;
-    case M_SWRE_AB:
-      ab = 1;
-    case M_SWRE_OB:
-      s = "swre";
-      fmt = "t,+j(b)";
-      offbits = 9;
-      goto ld_st;
-    case M_ACLR_AB:
-      ab = 1;
-    case M_ACLR_OB:
-      s = "aclr";
-      treg = EXTRACT_OPERAND (mips_opts.micromips, 3BITPOS, *ip);
-      fmt = "\\,~(b)";
-      offbits = 12;
-      goto ld_st;
-    case M_ASET_AB:
-      ab = 1;
-    case M_ASET_OB:
-      s = "aset";
-      treg = EXTRACT_OPERAND (mips_opts.micromips, 3BITPOS, *ip);
-      fmt = "\\,~(b)";
-      offbits = 12;
-      goto ld_st;
-    case M_LB_AB:
-      ab = 1;
-      s = "lb";
-      fmt = "t,o(b)";
-      goto ld;
-    case M_LBU_AB:
-      ab = 1;
-      s = "lbu";
-      fmt = "t,o(b)";
-      goto ld;
-    case M_LH_AB:
-      ab = 1;
-      s = "lh";
-      fmt = "t,o(b)";
-      goto ld;
-    case M_LHU_AB:
-      ab = 1;
-      s = "lhu";
-      fmt = "t,o(b)";
-      goto ld;
-    case M_LW_AB:
-      ab = 1;
-      s = "lw";
-      fmt = "t,o(b)";
-      goto ld;
-    case M_LWC0_AB:
-      ab = 1;
-      gas_assert (!mips_opts.micromips);
-      s = "lwc0";
-      fmt = "E,o(b)";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld_st;
-    case M_LWC1_AB:
-      ab = 1;
-      s = "lwc1";
-      fmt = "T,o(b)";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld_st;
-    case M_LWC2_AB:
-      ab = 1;
-    case M_LWC2_OB:
-      s = "lwc2";
-      fmt = COP12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld_st;
-    case M_LWC3_AB:
-      ab = 1;
-      gas_assert (!mips_opts.micromips);
-      s = "lwc3";
-      fmt = "E,o(b)";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld_st;
-    case M_LWL_AB:
-      ab = 1;
-    case M_LWL_OB:
-      s = "lwl";
-      fmt = MEM12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
-      goto ld_st;
-    case M_LWR_AB:
-      ab = 1;
-    case M_LWR_OB:
-      s = "lwr";
-      fmt = MEM12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
-      goto ld_st;
-    case M_LDC1_AB:
-      ab = 1;
-      s = "ldc1";
-      fmt = "T,o(b)";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld_st;
-    case M_LDC2_AB:
-      ab = 1;
-    case M_LDC2_OB:
-      s = "ldc2";
-      fmt = COP12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld_st;
-    case M_LQC2_AB:
-      ab = 1;
-      s = "lqc2";
-      fmt = "E,o(b)";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld_st;
-    case M_LDC3_AB:
-      ab = 1;
-      s = "ldc3";
-      fmt = "E,o(b)";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld_st;
-    case M_LDL_AB:
-      ab = 1;
-    case M_LDL_OB:
-      s = "ldl";
-      fmt = MEM12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
-      goto ld_st;
-    case M_LDR_AB:
-      ab = 1;
-    case M_LDR_OB:
-      s = "ldr";
-      fmt = MEM12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
-      goto ld_st;
-    case M_LL_AB:
-      ab = 1;
-    case M_LL_OB:
-      s = "ll";
-      fmt = MEM12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
-      goto ld;
-    case M_LLD_AB:
-      ab = 1;
-    case M_LLD_OB:
-      s = "lld";
-      fmt = MEM12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
-      goto ld;
-    case M_LWU_AB:
-      ab = 1;
-    case M_LWU_OB:
-      s = "lwu";
-      fmt = MEM12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
-      goto ld;
-    case M_LWP_AB:
-      ab = 1;
-    case M_LWP_OB:
-      gas_assert (mips_opts.micromips);
-      s = "lwp";
-      fmt = "t,~(b)";
-      offbits = 12;
-      lp = 1;
-      goto ld;
-    case M_LDP_AB:
-      ab = 1;
-    case M_LDP_OB:
-      gas_assert (mips_opts.micromips);
-      s = "ldp";
-      fmt = "t,~(b)";
-      offbits = 12;
-      lp = 1;
-      goto ld;
-    case M_LWM_AB:
-      ab = 1;
-    case M_LWM_OB:
-      gas_assert (mips_opts.micromips);
-      s = "lwm";
-      fmt = "n,~(b)";
-      offbits = 12;
-      goto ld_st;
-    case M_LDM_AB:
-      ab = 1;
-    case M_LDM_OB:
-      gas_assert (mips_opts.micromips);
-      s = "ldm";
-      fmt = "n,~(b)";
-      offbits = 12;
-      goto ld_st;
+    case M_BLEL_I:
+      likely = 1;
+    case M_BLE_I:
+      if (imm_expr.X_op == O_constant && 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);
+      else
+       {
+         used_at = 1;
+         set_at (sreg, 0);
+         macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+                                  &offset_expr, AT, ZERO);
+       }
+      break;
+
+    case M_BLEUL:
+      likely = 1;
+    case M_BLEU:
+      if (treg == 0)
+       macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+                                &offset_expr, sreg, ZERO);
+      else if (sreg == 0)
+       goto do_true;
+      else
+       {
+         used_at = 1;
+         macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
+         macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+                                  &offset_expr, AT, ZERO);
+       }
+      break;
+
+    case M_BLEUL_I:
+      likely = 1;
+    case M_BLEU_I:
+      if (sreg == 0
+         || (HAVE_32BIT_GPRS
+             && imm_expr.X_op == O_constant
+             && 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)
+       goto do_false;
+      else if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+       macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+                                &offset_expr, sreg, ZERO);
+      else
+       {
+         used_at = 1;
+         set_at (sreg, 1);
+         macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+                                  &offset_expr, AT, ZERO);
+       }
+      break;
+
+    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);
+      else
+       {
+         used_at = 1;
+         macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
+         macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+                                  &offset_expr, AT, ZERO);
+       }
+      break;
+
+    case M_BLTUL:
+      likely = 1;
+    case M_BLTU:
+      if (treg == 0)
+       goto do_false;
+      else if (sreg == 0)
+       macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+                                &offset_expr, ZERO, treg);
+      else
+       {
+         used_at = 1;
+         macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
+         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:
+      s = "mflo";
+      goto do_div3;
+    case M_DREM_3:
+      dbl = 1;
+    case M_REM_3:
+      s = "mfhi";
+    do_div3:
+      if (treg == 0)
+       {
+         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;
+       }
+
+      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);
+       }
+      else
+       {
+         if (mips_opts.micromips)
+           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 (NULL, "break", BRK_FMT, 7);
+         if (mips_opts.micromips)
+           micromips_add_label ();
+       }
+      expr1.X_add_number = -1;
+      used_at = 1;
+      load_register (AT, &expr1, dbl);
+      if (mips_opts.micromips)
+       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);
+      if (dbl)
+       {
+         expr1.X_add_number = 1;
+         load_register (AT, &expr1, dbl);
+         macro_build (NULL, "dsll32", SHFT_FMT, AT, AT, 31);
+       }
+      else
+       {
+         expr1.X_add_number = 0x80000000;
+         macro_build (&expr1, "lui", LUI_FMT, AT, BFD_RELOC_HI16);
+       }
+      if (mips_trap)
+       {
+         macro_build (NULL, "teq", TRAP_FMT, sreg, 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 ();
+       }
+      else
+       {
+         if (mips_opts.micromips)
+           micromips_label_expr (&label_expr);
+         else
+           label_expr.X_add_number = 8;
+         macro_build (&label_expr, "bne", "s,t,p", sreg, AT);
+         macro_build (NULL, "nop", "");
+
+         /* We want to close the noreorder block as soon as possible, so
+            that later insns are available for delay slot filling.  */
+         end_noreorder ();
+
+         macro_build (NULL, "break", BRK_FMT, 6);
+       }
+      if (mips_opts.micromips)
+       micromips_add_label ();
+      macro_build (NULL, s, MFHL_FMT, dreg);
+      break;
+
+    case M_DIV_3I:
+      s = "div";
+      s2 = "mflo";
+      goto do_divi;
+    case M_DIVU_3I:
+      s = "divu";
+      s2 = "mflo";
+      goto do_divi;
+    case M_REM_3I:
+      s = "div";
+      s2 = "mfhi";
+      goto do_divi;
+    case M_REMU_3I:
+      s = "divu";
+      s2 = "mfhi";
+      goto do_divi;
+    case M_DDIV_3I:
+      dbl = 1;
+      s = "ddiv";
+      s2 = "mflo";
+      goto do_divi;
+    case M_DDIVU_3I:
+      dbl = 1;
+      s = "ddivu";
+      s2 = "mflo";
+      goto do_divi;
+    case M_DREM_3I:
+      dbl = 1;
+      s = "ddiv";
+      s2 = "mfhi";
+      goto do_divi;
+    case M_DREMU_3I:
+      dbl = 1;
+      s = "ddivu";
+      s2 = "mfhi";
+    do_divi:
+      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+       {
+         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 (strcmp (s2, "mflo") == 0)
+           move_register (dreg, sreg);
+         else
+           move_register (dreg, ZERO);
+         break;
+       }
+      if (imm_expr.X_op == O_constant
+         && 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);
+           }
+         else
+           move_register (dreg, ZERO);
+         break;
+       }
 
-    ld:
-      /* We don't want to use $0 as tempreg.  */
-      if (breg == treg + lp || treg + lp == ZERO)
-       goto ld_st;
+      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);
+      break;
+
+    case M_DIVU_3:
+      s = "divu";
+      s2 = "mflo";
+      goto do_divu3;
+    case M_REMU_3:
+      s = "divu";
+      s2 = "mfhi";
+      goto do_divu3;
+    case M_DDIVU_3:
+      s = "ddivu";
+      s2 = "mflo";
+      goto do_divu3;
+    case M_DREMU_3:
+      s = "ddivu";
+      s2 = "mfhi";
+    do_divu3:
+      start_noreorder ();
+      if (mips_trap)
+       {
+         macro_build (NULL, "teq", TRAP_FMT, treg, ZERO, 7);
+         macro_build (NULL, s, "z,s,t", sreg, treg);
+         /* We want to close the noreorder block as soon as possible, so
+            that later insns are available for delay slot filling.  */
+         end_noreorder ();
+       }
       else
-       tempreg = treg + lp;
-      goto ld_noat;
+       {
+         if (mips_opts.micromips)
+           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);
 
-    case M_SB_AB:
-      ab = 1;
-      s = "sb";
-      fmt = "t,o(b)";
-      goto ld_st;
-    case M_SH_AB:
-      ab = 1;
-      s = "sh";
-      fmt = "t,o(b)";
-      goto ld_st;
-    case M_SW_AB:
-      ab = 1;
-      s = "sw";
-      fmt = "t,o(b)";
-      goto ld_st;
-    case M_SWC0_AB:
-      ab = 1;
-      gas_assert (!mips_opts.micromips);
-      s = "swc0";
-      fmt = "E,o(b)";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld_st;
-    case M_SWC1_AB:
-      ab = 1;
-      s = "swc1";
-      fmt = "T,o(b)";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld_st;
-    case M_SWC2_AB:
-      ab = 1;
-    case M_SWC2_OB:
-      s = "swc2";
-      fmt = COP12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld_st;
-    case M_SWC3_AB:
-      ab = 1;
-      gas_assert (!mips_opts.micromips);
-      s = "swc3";
-      fmt = "E,o(b)";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld_st;
-    case M_SWL_AB:
-      ab = 1;
-    case M_SWL_OB:
-      s = "swl";
-      fmt = MEM12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
-      goto ld_st;
-    case M_SWR_AB:
-      ab = 1;
-    case M_SWR_OB:
-      s = "swr";
-      fmt = MEM12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
-      goto ld_st;
-    case M_SC_AB:
-      ab = 1;
-    case M_SC_OB:
-      s = "sc";
-      fmt = MEM12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
-      goto ld_st;
-    case M_SCD_AB:
-      ab = 1;
-    case M_SCD_OB:
-      s = "scd";
-      fmt = MEM12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
-      goto ld_st;
-    case M_CACHE_AB:
-      ab = 1;
-    case M_CACHE_OB:
-      s = "cache";
-      fmt = mips_opts.micromips ? "k,~(b)" : "k,o(b)";
-      offbits = (mips_opts.micromips ? 12 : 16);
-      goto ld_st;
-    case M_CACHEE_AB:
-      ab = 1;
-    case M_CACHEE_OB:
-      s = "cachee";
-      fmt = "k,+j(b)";
-      offbits = 9;
-      goto ld_st;
-    case M_PREF_AB:
-      ab = 1;
-    case M_PREF_OB:
-      s = "pref";
-      fmt = !mips_opts.micromips ? "k,o(b)" : "k,~(b)";
-      offbits = (mips_opts.micromips ? 12 : 16);
-      goto ld_st;
-    case M_PREFE_AB:
-      ab = 1;
-    case M_PREFE_OB:
-      s = "prefe";
-      fmt = "k,+j(b)";
-      offbits = 9;
-      goto ld_st;
-    case M_SDC1_AB:
-      ab = 1;
-      s = "sdc1";
-      fmt = "T,o(b)";
-      coproc = 1;
-      /* Itbl support may require additional care here.  */
-      goto ld_st;
-    case M_SDC2_AB:
-      ab = 1;
-    case M_SDC2_OB:
-      s = "sdc2";
-      fmt = COP12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld_st;
-    case M_SQC2_AB:
-      ab = 1;
-      s = "sqc2";
-      fmt = "E,o(b)";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld_st;
-    case M_SDC3_AB:
-      ab = 1;
-      gas_assert (!mips_opts.micromips);
-      s = "sdc3";
-      fmt = "E,o(b)";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld_st;
-    case M_SDL_AB:
-      ab = 1;
-    case M_SDL_OB:
-      s = "sdl";
-      fmt = MEM12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
-      goto ld_st;
-    case M_SDR_AB:
-      ab = 1;
-    case M_SDR_OB:
-      s = "sdr";
-      fmt = MEM12_FMT;
-      offbits = (mips_opts.micromips ? 12 : 16);
-      goto ld_st;
-    case M_SWP_AB:
-      ab = 1;
-    case M_SWP_OB:
-      gas_assert (mips_opts.micromips);
-      s = "swp";
-      fmt = "t,~(b)";
-      offbits = 12;
-      goto ld_st;
-    case M_SDP_AB:
-      ab = 1;
-    case M_SDP_OB:
-      gas_assert (mips_opts.micromips);
-      s = "sdp";
-      fmt = "t,~(b)";
-      offbits = 12;
-      goto ld_st;
-    case M_SWM_AB:
-      ab = 1;
-    case M_SWM_OB:
-      gas_assert (mips_opts.micromips);
-      s = "swm";
-      fmt = "n,~(b)";
-      offbits = 12;
-      goto ld_st;
-    case M_SDM_AB:
-      ab = 1;
-    case M_SDM_OB:
-      gas_assert (mips_opts.micromips);
-      s = "sdm";
-      fmt = "n,~(b)";
-      offbits = 12;
+         /* We want to close the noreorder block as soon as possible, so
+            that later insns are available for delay slot filling.  */
+         end_noreorder ();
+         macro_build (NULL, "break", BRK_FMT, 7);
+         if (mips_opts.micromips)
+           micromips_add_label ();
+       }
+      macro_build (NULL, s2, MFHL_FMT, dreg);
+      break;
+
+    case M_DLCA_AB:
+      dbl = 1;
+    case M_LCA_AB:
+      call = 1;
+      goto do_la;
+    case M_DLA_AB:
+      dbl = 1;
+    case M_LA_AB:
+    do_la:
+      /* 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)
+       as_warn (_("dla used to load 32-bit register"));
+
+      if (!dbl && HAVE_64BIT_OBJECTS)
+       as_warn (_("la used to load 64-bit address"));
+
+      if (small_offset_p (0, align, 16))
+       {
+         macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", treg, breg,
+                      -1, offset_reloc[0], offset_reloc[1], offset_reloc[2]);
+         break;
+       }
+
+      if (mips_opts.at && (treg == breg))
+       {
+         tempreg = AT;
+         used_at = 1;
+       }
+      else
+       {
+         tempreg = treg;
+       }
+
+      if (offset_expr.X_op != O_symbol
+         && offset_expr.X_op != O_constant)
+       {
+         as_bad (_("Expression too complex"));
+         offset_expr.X_op = O_constant;
+       }
+
+      if (offset_expr.X_op == O_constant)
+       load_register (tempreg, &offset_expr, HAVE_64BIT_ADDRESSES);
+      else if (mips_pic == NO_PIC)
+       {
+         /* If this is a reference to a GP relative symbol, we want
+              addiu    $tempreg,$gp,<sym>      (BFD_RELOC_GPREL16)
+            Otherwise we want
+              lui      $tempreg,<sym>          (BFD_RELOC_HI16_S)
+              addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+            If we have a constant, we need two instructions anyhow,
+            so we may as well always use the latter form.
+
+            With 64bit address space and a usable $at we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
+              lui      $at,<sym>               (BFD_RELOC_HI16_S)
+              daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
+              daddiu   $at,<sym>               (BFD_RELOC_LO16)
+              dsll32   $tempreg,0
+              daddu    $tempreg,$tempreg,$at
 
-    ld_st:
-      tempreg = AT;
-      used_at = 1;
-    ld_noat:
-      if (offset_expr.X_op != O_constant
-         && offset_expr.X_op != O_symbol)
-       {
-         as_bad (_("Expression too complex"));
-         offset_expr.X_op = O_constant;
-       }
+            If $at is already in use, we use a path which is suboptimal
+            on superscalar processors.
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
+              daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
+              dsll     $tempreg,16
+              daddiu   $tempreg,<sym>          (BFD_RELOC_HI16_S)
+              dsll     $tempreg,16
+              daddiu   $tempreg,<sym>          (BFD_RELOC_LO16)
 
-      if (HAVE_32BIT_ADDRESSES
-         && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
-       {
-         char value [32];
+            For GP relative symbols in 64bit address space we can use
+            the same sequence as in 32bit address space.  */
+         if (HAVE_64BIT_SYMBOLS)
+           {
+             if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+                 && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+               {
+                 relax_start (offset_expr.X_add_symbol);
+                 macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+                              tempreg, mips_gp_register, BFD_RELOC_GPREL16);
+                 relax_switch ();
+               }
 
-         sprintf_vma (value, offset_expr.X_add_number);
-         as_bad (_("Number (0x%s) larger than 32 bits"), value);
-       }
+             if (used_at == 0 && mips_opts.at)
+               {
+                 macro_build (&offset_expr, "lui", LUI_FMT,
+                              tempreg, BFD_RELOC_MIPS_HIGHEST);
+                 macro_build (&offset_expr, "lui", LUI_FMT,
+                              AT, BFD_RELOC_HI16_S);
+                 macro_build (&offset_expr, "daddiu", "t,r,j",
+                              tempreg, tempreg, BFD_RELOC_MIPS_HIGHER);
+                 macro_build (&offset_expr, "daddiu", "t,r,j",
+                              AT, AT, BFD_RELOC_LO16);
+                 macro_build (NULL, "dsll32", SHFT_FMT, tempreg, tempreg, 0);
+                 macro_build (NULL, "daddu", "d,v,t", tempreg, tempreg, AT);
+                 used_at = 1;
+               }
+             else
+               {
+                 macro_build (&offset_expr, "lui", LUI_FMT,
+                              tempreg, BFD_RELOC_MIPS_HIGHEST);
+                 macro_build (&offset_expr, "daddiu", "t,r,j",
+                              tempreg, tempreg, BFD_RELOC_MIPS_HIGHER);
+                 macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
+                 macro_build (&offset_expr, "daddiu", "t,r,j",
+                              tempreg, tempreg, BFD_RELOC_HI16_S);
+                 macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
+                 macro_build (&offset_expr, "daddiu", "t,r,j",
+                              tempreg, tempreg, BFD_RELOC_LO16);
+               }
 
-      /* A constant expression in PIC code can be handled just as it
-        is in non PIC code.  */
-      if (offset_expr.X_op == O_constant)
+             if (mips_relax.sequence)
+               relax_end ();
+           }
+         else
+           {
+             if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+                 && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+               {
+                 relax_start (offset_expr.X_add_symbol);
+                 macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+                              tempreg, mips_gp_register, BFD_RELOC_GPREL16);
+                 relax_switch ();
+               }
+             if (!IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+               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);
+             if (mips_relax.sequence)
+               relax_end ();
+           }
+       }
+      else if (!mips_big_got && !HAVE_NEWABI)
        {
-         int hipart = 0;
+         int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
 
-         expr1.X_add_number = offset_expr.X_add_number;
-         normalize_address_expr (&expr1);
-         if ((offbits == 0 || offbits == 16)
-             && !IS_SEXT_16BIT_NUM (expr1.X_add_number))
+         /* If this is a reference to an external symbol, and there
+            is no constant, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+            or for lca or if tempreg is PIC_CALL_REG
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_CALL16)
+            For a local symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              nop
+              addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+
+            If we have a small constant, and this is a reference to
+            an external symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              nop
+              addiu    $tempreg,$tempreg,<constant>
+            For a local symbol, we want the same instruction
+            sequence, but we output a BFD_RELOC_LO16 reloc on the
+            addiu instruction.
+
+            If we have a large constant, and this is a reference to
+            an external symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              lui      $at,<hiconstant>
+              addiu    $at,$at,<loconstant>
+              addu     $tempreg,$tempreg,$at
+            For a local symbol, we want the same instruction
+            sequence, but we output a BFD_RELOC_LO16 reloc on the
+            addiu instruction.
+          */
+
+         if (offset_expr.X_add_number == 0)
            {
-             expr1.X_add_number = ((expr1.X_add_number + 0x8000)
-                                   & ~(bfd_vma) 0xffff);
-             hipart = 1;
+             if (mips_pic == SVR4_PIC
+                 && breg == 0
+                 && (call || tempreg == PIC_CALL_REG))
+               lw_reloc_type = (int) BFD_RELOC_MIPS_CALL16;
+
+             relax_start (offset_expr.X_add_symbol);
+             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                          lw_reloc_type, mips_gp_register);
+             if (breg != 0)
+               {
+                 /* We're going to put in an addu instruction using
+                    tempreg, so we may as well insert the nop right
+                    now.  */
+                 load_delay_nop ();
+               }
+             relax_switch ();
+             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                          tempreg, BFD_RELOC_MIPS_GOT16, mips_gp_register);
+             load_delay_nop ();
+             macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+                          tempreg, tempreg, BFD_RELOC_LO16);
+             relax_end ();
+             /* FIXME: If breg == 0, and the next instruction uses
+                $tempreg, then if this variant case is used an extra
+                nop will be generated.  */
            }
-         else if (offbits == 12 && !IS_SEXT_12BIT_NUM (expr1.X_add_number))
+         else if (offset_expr.X_add_number >= -0x8000
+                  && offset_expr.X_add_number < 0x8000)
            {
-             expr1.X_add_number = ((expr1.X_add_number + 0x800)
-                                   & ~(bfd_vma) 0xfff);
-             hipart = 1;
+             load_got_offset (tempreg, &offset_expr);
+             load_delay_nop ();
+             add_got_offset (tempreg, &offset_expr);
            }
-         else if (offbits == 9 && !IS_SEXT_9BIT_NUM (expr1.X_add_number))
+         else
            {
-             expr1.X_add_number = ((expr1.X_add_number + 0x100)
-                                   & ~(bfd_vma) 0x1ff);
-             hipart = 1;
+             expr1.X_add_number = offset_expr.X_add_number;
+             offset_expr.X_add_number =
+               SEXT_16BIT (offset_expr.X_add_number);
+             load_got_offset (tempreg, &offset_expr);
+             offset_expr.X_add_number = expr1.X_add_number;
+             /* 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
+                we want to load the constant into AT, we add our
+                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)
+               {
+                 load_delay_nop ();
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                              treg, AT, breg);
+                 breg = 0;
+                 tempreg = treg;
+               }
+             add_got_offset_hilo (tempreg, &offset_expr, AT);
+             used_at = 1;
            }
-         if (hipart)
+       }
+      else if (!mips_big_got && HAVE_NEWABI)
+       {
+         int add_breg_early = 0;
+
+         /* If this is a reference to an external, and there is no
+            constant, or local symbol (*), with or without a
+            constant, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_DISP)
+            or for lca or if tempreg is PIC_CALL_REG
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_CALL16)
+
+            If we have a small constant, and this is a reference to
+            an external symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_DISP)
+              addiu    $tempreg,$tempreg,<constant>
+
+            If we have a large constant, and this is a reference to
+            an external symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_DISP)
+              lui      $at,<hiconstant>
+              addiu    $at,$at,<loconstant>
+              addu     $tempreg,$tempreg,$at
+
+            (*) Other assemblers seem to prefer GOT_PAGE/GOT_OFST for
+            local symbols, even though it introduces an additional
+            instruction.  */
+
+         if (offset_expr.X_add_number)
            {
-             load_register (tempreg, &expr1, HAVE_64BIT_ADDRESSES);
-             if (breg != 0)
-               macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                            tempreg, tempreg, breg);
-             breg = tempreg;
+             expr1.X_add_number = offset_expr.X_add_number;
+             offset_expr.X_add_number = 0;
+
+             relax_start (offset_expr.X_add_symbol);
+             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                          BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+
+             if (expr1.X_add_number >= -0x8000
+                 && expr1.X_add_number < 0x8000)
+               {
+                 macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
+                              tempreg, tempreg, BFD_RELOC_LO16);
+               }
+             else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
+               {
+                 /* 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
+                    we want to load the constant into AT, we add our
+                    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)
+                   dreg = tempreg;
+                 else
+                   {
+                     gas_assert (tempreg == AT);
+                     macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                                  treg, AT, breg);
+                     dreg = treg;
+                     add_breg_early = 1;
+                   }
+
+                 load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                              dreg, dreg, AT);
+
+                 used_at = 1;
+               }
+             else
+               as_bad (_("PIC code offset overflow (max 32 signed bits)"));
+
+             relax_switch ();
+             offset_expr.X_add_number = expr1.X_add_number;
+
+             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                          BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+             if (add_breg_early)
+               {
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                              treg, tempreg, breg);
+                 breg = 0;
+                 tempreg = treg;
+               }
+             relax_end ();
            }
-         if (offbits == 0)
+         else if (breg == 0 && (call || tempreg == PIC_CALL_REG))
            {
-             if (offset_expr.X_add_number == 0)
-               tempreg = breg;
-             else
-               macro_build (&offset_expr, ADDRESS_ADDI_INSN,
-                            "t,r,j", tempreg, breg, BFD_RELOC_LO16);
-             macro_build (NULL, s, fmt, treg, tempreg);
+             relax_start (offset_expr.X_add_symbol);
+             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                          BFD_RELOC_MIPS_CALL16, mips_gp_register);
+             relax_switch ();
+             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                          BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+             relax_end ();
            }
-         else if (offbits == 16)
-           macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16, breg);
          else
-           macro_build (NULL, s, fmt,
-                        treg, (unsigned long) offset_expr.X_add_number, breg);
-       }
-      else if (offbits != 16)
-       {
-         /* The offset field is too narrow to be used for a low-part
-            relocation, so load the whole address into the auxillary
-            register.  In the case of "A(b)" addresses, we first load
-            absolute address "A" into the register and then add base
-            register "b".  In the case of "o(b)" addresses, we simply
-            need to add 16-bit offset "o" to base register "b", and
-            offset_reloc already contains the relocations associated
-            with "o".  */
-         if (ab)
            {
-             load_address (tempreg, &offset_expr, &used_at);
-             if (breg != 0)
-               macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                            tempreg, tempreg, breg);
+             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                          BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
            }
-         else
-           macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
-                        tempreg, breg, -1,
-                        offset_reloc[0], offset_reloc[1], offset_reloc[2]);
-         expr1.X_add_number = 0;
-         if (offbits == 0)
-           macro_build (NULL, s, fmt, treg, tempreg);
-         else
-           macro_build (NULL, s, fmt,
-                        treg, (unsigned long) expr1.X_add_number, tempreg);
        }
-      else if (mips_pic == NO_PIC)
+      else if (mips_big_got && !HAVE_NEWABI)
        {
-         /* 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)
-            Otherwise, if there is no base register, we want
-              lui      $tempreg,<sym>          (BFD_RELOC_HI16_S)
-              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
-            If we have a constant, we need two instructions anyhow,
-            so we always use the latter form.
+         int gpdelay;
+         int lui_reloc_type = (int) BFD_RELOC_MIPS_GOT_HI16;
+         int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT_LO16;
+         int local_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
 
-            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)
-            Otherwise we want
-              lui      $tempreg,<sym>          (BFD_RELOC_HI16_S)
-              addu     $tempreg,$tempreg,$breg
-              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
-            With a constant we always use the latter case.
+         /* This is the large GOT case.  If this is a reference to an
+            external symbol, and there is no constant, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
+              addu     $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+            or for lca or if tempreg is PIC_CALL_REG
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_CALL_HI16)
+              addu     $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_CALL_LO16)
+            For a local symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              nop
+              addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
 
-            With 64bit address space and no base register and $at usable,
-            we want
-              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
-              lui      $at,<sym>               (BFD_RELOC_HI16_S)
-              daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
-              dsll32   $tempreg,0
-              daddu    $tempreg,$at
-              <op>     $treg,<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)
-              daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
-              daddu    $at,$breg
-              dsll32   $tempreg,0
-              daddu    $tempreg,$at
-              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+            If we have a small constant, and this is a reference to
+            an external symbol, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
+              addu     $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+              nop
+              addiu    $tempreg,$tempreg,<constant>
+            For a local symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              nop
+              addiu    $tempreg,$tempreg,<constant> (BFD_RELOC_LO16)
 
-            Without $at we can't generate the optimal path for superscalar
-            processors here since this would require two temporary registers.
-              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
-              daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
-              dsll     $tempreg,16
-              daddiu   $tempreg,<sym>          (BFD_RELOC_HI16_S)
-              dsll     $tempreg,16
-              <op>     $treg,<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)
-              dsll     $tempreg,16
-              daddiu   $tempreg,<sym>          (BFD_RELOC_HI16_S)
-              dsll     $tempreg,16
-              daddu    $tempreg,$tempreg,$breg
-              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+            If we have a large constant, and this is a reference to
+            an external symbol, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
+              addu     $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+              lui      $at,<hiconstant>
+              addiu    $at,$at,<loconstant>
+              addu     $tempreg,$tempreg,$at
+            For a local symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              lui      $at,<hiconstant>
+              addiu    $at,$at,<loconstant>    (BFD_RELOC_LO16)
+              addu     $tempreg,$tempreg,$at
+         */
 
-            For GP relative symbols in 64bit address space we can use
-            the same sequence as in 32bit address space.  */
-         if (HAVE_64BIT_SYMBOLS)
+         expr1.X_add_number = offset_expr.X_add_number;
+         offset_expr.X_add_number = 0;
+         relax_start (offset_expr.X_add_symbol);
+         gpdelay = reg_needs_delay (mips_gp_register);
+         if (expr1.X_add_number == 0 && breg == 0
+             && (call || tempreg == PIC_CALL_REG))
            {
-             if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
-                 && !nopic_need_relax (offset_expr.X_add_symbol, 1))
-               {
-                 relax_start (offset_expr.X_add_symbol);
-                 if (breg == 0)
-                   {
-                     macro_build (&offset_expr, s, fmt, treg,
-                                  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,
-                                  BFD_RELOC_GPREL16, tempreg);
-                   }
-                 relax_switch ();
-               }
-
-             if (used_at == 0 && mips_opts.at)
+             lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16;
+             lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16;
+           }
+         macro_build (&offset_expr, "lui", LUI_FMT, tempreg, lui_reloc_type);
+         macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                      tempreg, tempreg, mips_gp_register);
+         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                      tempreg, lw_reloc_type, tempreg);
+         if (expr1.X_add_number == 0)
+           {
+             if (breg != 0)
                {
-                 macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
-                              BFD_RELOC_MIPS_HIGHEST);
-                 macro_build (&offset_expr, "lui", LUI_FMT, AT,
-                              BFD_RELOC_HI16_S);
-                 macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
-                              tempreg, BFD_RELOC_MIPS_HIGHER);
-                 if (breg != 0)
-                   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,
-                              tempreg);
-                 used_at = 1;
+                 /* We're going to put in an addu instruction using
+                    tempreg, so we may as well insert the nop right
+                    now.  */
+                 load_delay_nop ();
                }
+           }
+         else if (expr1.X_add_number >= -0x8000
+                  && expr1.X_add_number < 0x8000)
+           {
+             load_delay_nop ();
+             macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
+                          tempreg, tempreg, BFD_RELOC_LO16);
+           }
+         else
+           {
+             /* 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
+                we want to load the constant into AT, we add our
+                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)
+               dreg = tempreg;
              else
                {
-                 macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
-                              BFD_RELOC_MIPS_HIGHEST);
-                 macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
-                              tempreg, BFD_RELOC_MIPS_HIGHER);
-                 macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
-                 macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
-                              tempreg, BFD_RELOC_HI16_S);
-                 macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
-                 if (breg != 0)
-                   macro_build (NULL, "daddu", "d,v,t",
-                                tempreg, tempreg, breg);
-                 macro_build (&offset_expr, s, fmt, treg,
-                              BFD_RELOC_LO16, tempreg);
+                 gas_assert (tempreg == AT);
+                 load_delay_nop ();
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                              treg, AT, breg);
+                 dreg = treg;
                }
 
-             if (mips_relax.sequence)
-               relax_end ();
-             break;
+             load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
+
+             used_at = 1;
            }
+         offset_expr.X_add_number = SEXT_16BIT (expr1.X_add_number);
+         relax_switch ();
 
-         if (breg == 0)
+         if (gpdelay)
            {
-             if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
-                 && !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,
-                              mips_gp_register);
-                 relax_switch ();
-               }
-             macro_build_lui (&offset_expr, tempreg);
-             macro_build (&offset_expr, s, fmt, treg,
-                          BFD_RELOC_LO16, tempreg);
-             if (mips_relax.sequence)
-               relax_end ();
+             /* This is needed because this instruction uses $gp, but
+                the first instruction on the main stream does not.  */
+             macro_build (NULL, "nop", "");
+           }
+
+         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                      local_reloc_type, mips_gp_register);
+         if (expr1.X_add_number >= -0x8000
+             && expr1.X_add_number < 0x8000)
+           {
+             load_delay_nop ();
+             macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+                          tempreg, tempreg, BFD_RELOC_LO16);
+             /* FIXME: If add_number is 0, and there was no base
+                register, the external symbol case ended with a load,
+                so if the symbol turns out to not be external, and
+                the next instruction uses tempreg, an unnecessary nop
+                will be inserted.  */
            }
          else
            {
-             if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
-                 && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+             if (breg == treg)
                {
-                 relax_start (offset_expr.X_add_symbol);
+                 /* 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",
-                              tempreg, breg, mips_gp_register);
-                 macro_build (&offset_expr, s, fmt, treg,
-                              BFD_RELOC_GPREL16, tempreg);
-                 relax_switch ();
+                              treg, AT, breg);
+                 tempreg = treg;
+                 /* We set breg to 0 because we have arranged to add
+                    it in in both cases.  */
+                 breg = 0;
                }
-             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,
-                          BFD_RELOC_LO16, tempreg);
-             if (mips_relax.sequence)
-               relax_end ();
-           }
-       }
-      else if (!mips_big_got)
-       {
-         int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
-
-         /* If this is a reference to an external symbol, we want
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
-              nop
-              <op>     $treg,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)
-
-            For NewABI, we want
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_PAGE)
-              <op>     $treg,<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
-            <op> instruction.  We don't handle constants larger than
-            16 bits, because we have no way to load the upper 16 bits
-            (actually, we could handle them for the subset of cases
-            in which we are not using $at).  */
-         gas_assert (offset_expr.X_op == O_symbol);
-         if (HAVE_NEWABI)
-           {
-             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
-                          BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
-             if (breg != 0)
-               macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                            tempreg, tempreg, breg);
-             macro_build (&offset_expr, s, fmt, treg,
-                          BFD_RELOC_MIPS_GOT_OFST, tempreg);
-             break;
+             macro_build_lui (&expr1, AT);
+             macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+                          AT, AT, BFD_RELOC_LO16);
+             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                          tempreg, tempreg, AT);
+             used_at = 1;
            }
-         expr1.X_add_number = offset_expr.X_add_number;
-         offset_expr.X_add_number = 0;
-         if (expr1.X_add_number < -0x8000
-             || expr1.X_add_number >= 0x8000)
-           as_bad (_("PIC code offset overflow (max 16 signed bits)"));
-         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
-                      lw_reloc_type, mips_gp_register);
-         load_delay_nop ();
-         relax_start (offset_expr.X_add_symbol);
-         relax_switch ();
-         macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
-                      tempreg, BFD_RELOC_LO16);
          relax_end ();
-         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);
        }
-      else if (mips_big_got && !HAVE_NEWABI)
+      else if (mips_big_got && HAVE_NEWABI)
        {
-         int gpdelay;
+         int lui_reloc_type = (int) BFD_RELOC_MIPS_GOT_HI16;
+         int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT_LO16;
+         int add_breg_early = 0;
 
-         /* If this is a reference to an external symbol, we want
+         /* This is the large GOT case.  If this is a reference to an
+            external symbol, and there is no constant, we want
               lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
-              addu     $tempreg,$tempreg,$gp
+              add      $tempreg,$tempreg,$gp
               lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
-              <op>     $treg,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)
-            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
-            16 bits, because we have no way to load the upper 16 bits
-            (actually, we could handle them for the subset of cases
-            in which we are not using $at).  */
-         gas_assert (offset_expr.X_op == O_symbol);
-         expr1.X_add_number = offset_expr.X_add_number;
-         offset_expr.X_add_number = 0;
-         if (expr1.X_add_number < -0x8000
-             || expr1.X_add_number >= 0x8000)
-           as_bad (_("PIC code offset overflow (max 16 signed bits)"));
-         gpdelay = reg_needs_delay (mips_gp_register);
-         relax_start (offset_expr.X_add_symbol);
-         macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
-                      BFD_RELOC_MIPS_GOT_HI16);
-         macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg,
-                      mips_gp_register);
-         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
-                      BFD_RELOC_MIPS_GOT_LO16, tempreg);
-         relax_switch ();
-         if (gpdelay)
-           macro_build (NULL, "nop", "");
-         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
-                      BFD_RELOC_MIPS_GOT16, mips_gp_register);
-         load_delay_nop ();
-         macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
-                      tempreg, BFD_RELOC_LO16);
-         relax_end ();
+            or for lca or if tempreg is PIC_CALL_REG
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_CALL_HI16)
+              add      $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_CALL_LO16)
 
-         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);
-       }
-      else if (mips_big_got && HAVE_NEWABI)
-       {
-         /* If this is a reference to an external symbol, we want
+            If we have a small constant, and this is a reference to
+            an external symbol, we want
               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)
-            Otherwise, for local symbols, we want:
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_PAGE)
-              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_MIPS_GOT_OFST)  */
-         gas_assert (offset_expr.X_op == O_symbol);
+              addi     $tempreg,$tempreg,<constant>
+
+            If we have a large constant, and this is a reference to
+            an external symbol, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
+              addu     $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+              lui      $at,<hiconstant>
+              addi     $at,$at,<loconstant>
+              add      $tempreg,$tempreg,$at
+
+            If we have NewABI, and we know it's a local symbol, we want
+              lw       $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT_PAGE)
+              addiu    $reg,$reg,<sym>         (BFD_RELOC_MIPS_GOT_OFST)
+            otherwise we have to resort to GOT_HI16/GOT_LO16.  */
+
+         relax_start (offset_expr.X_add_symbol);
+
          expr1.X_add_number = offset_expr.X_add_number;
          offset_expr.X_add_number = 0;
-         if (expr1.X_add_number < -0x8000
-             || expr1.X_add_number >= 0x8000)
-           as_bad (_("PIC code offset overflow (max 16 signed bits)"));
-         relax_start (offset_expr.X_add_symbol);
-         macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
-                      BFD_RELOC_MIPS_GOT_HI16);
-         macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg,
-                      mips_gp_register);
-         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
-                      BFD_RELOC_MIPS_GOT_LO16, tempreg);
-         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);
+
+         if (expr1.X_add_number == 0 && breg == 0
+             && (call || tempreg == PIC_CALL_REG))
+           {
+             lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16;
+             lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16;
+           }
+         macro_build (&offset_expr, "lui", LUI_FMT, tempreg, lui_reloc_type);
+         macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                      tempreg, tempreg, mips_gp_register);
+         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                      tempreg, lw_reloc_type, tempreg);
+
+         if (expr1.X_add_number == 0)
+           ;
+         else if (expr1.X_add_number >= -0x8000
+                  && expr1.X_add_number < 0x8000)
+           {
+             macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
+                          tempreg, tempreg, BFD_RELOC_LO16);
+           }
+         else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
+           {
+             /* 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
+                we want to load the constant into AT, we add our
+                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)
+               dreg = tempreg;
+             else
+               {
+                 gas_assert (tempreg == AT);
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                              treg, AT, breg);
+                 dreg = treg;
+                 add_breg_early = 1;
+               }
+
+             load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
+
+             used_at = 1;
+           }
+         else
+           as_bad (_("PIC code offset overflow (max 32 signed bits)"));
 
          relax_switch ();
          offset_expr.X_add_number = expr1.X_add_number;
          macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
                       BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
-         if (breg != 0)
-           macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                        tempreg, tempreg, breg);
-         macro_build (&offset_expr, s, fmt, treg,
-                      BFD_RELOC_MIPS_GOT_OFST, tempreg);
+         macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
+                      tempreg, BFD_RELOC_MIPS_GOT_OFST);
+         if (add_breg_early)
+           {
+             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                          treg, tempreg, breg);
+             breg = 0;
+             tempreg = treg;
+           }
          relax_end ();
        }
       else
        abort ();
 
+      if (breg != 0)
+       macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, tempreg, breg);
+      break;
+
+    case M_MSGSND:
+      gas_assert (!mips_opts.micromips);
+      macro_build (NULL, "c2", "C", (treg << 16) | 0x01);
       break;
 
-    case M_LI:
-    case M_LI_S:
-      load_register (treg, &imm_expr, 0);
+    case M_MSGLD:
+      gas_assert (!mips_opts.micromips);
+      macro_build (NULL, "c2", "C", 0x02);
       break;
 
-    case M_DLI:
-      load_register (treg, &imm_expr, 1);
+    case M_MSGLD_T:
+      gas_assert (!mips_opts.micromips);
+      macro_build (NULL, "c2", "C", (treg << 16) | 0x02);
       break;
 
-    case M_LI_SS:
-      if (imm_expr.X_op == O_constant)
+    case M_MSGWAIT:
+      gas_assert (!mips_opts.micromips);
+      macro_build (NULL, "c2", "C", 3);
+      break;
+
+    case M_MSGWAIT_T:
+      gas_assert (!mips_opts.micromips);
+      macro_build (NULL, "c2", "C", (treg << 16) | 0x03);
+      break;
+
+    case M_J_A:
+      /* The j instruction may not be used in PIC code, since it
+        requires an absolute address.  We convert it to a b
+        instruction.  */
+      if (mips_pic == NO_PIC)
+       macro_build (&offset_expr, "j", "a");
+      else
+       macro_build (&offset_expr, "b", "p");
+      break;
+
+      /* The jal instructions must be handled as macros because when
+        generating PIC code they expand to multi-instruction
+        sequences.  Normally they are simple instructions.  */
+    case M_JALS_1:
+      dreg = RA;
+      /* Fall through.  */
+    case M_JALS_2:
+      gas_assert (mips_opts.micromips);
+      if (mips_opts.insn32)
        {
-         used_at = 1;
-         load_register (AT, &imm_expr, 0);
-         macro_build (NULL, "mtc1", "t,G", AT, treg);
+         as_bad (_("Opcode not supported in the `insn32' mode `%s'"), str);
          break;
        }
-      else
+      jals = 1;
+      goto jal;
+    case M_JAL_1:
+      dreg = RA;
+      /* Fall through.  */
+    case M_JAL_2:
+    jal:
+      if (mips_pic == NO_PIC)
        {
-         gas_assert (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,
-                      BFD_RELOC_MIPS_LITERAL, mips_gp_register);
-         break;
+         s = jals ? "jalrs" : "jalr";
+         if (mips_opts.micromips
+             && !mips_opts.insn32
+             && dreg == RA
+             && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
+           macro_build (NULL, s, "mj", sreg);
+         else
+           macro_build (NULL, s, JALR_FMT, dreg, sreg);
        }
-
-    case M_LI_D:
-      /* Check if we have a constant in IMM_EXPR.  If the GPRs are 64 bits
-         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)
+      else
        {
-         if (HAVE_64BIT_GPRS)
-           load_register (treg, &imm_expr, 1);
+         int cprestore = (mips_pic == SVR4_PIC && !HAVE_NEWABI
+                          && mips_cprestore_offset >= 0);
+
+         if (sreg != PIC_CALL_REG)
+           as_warn (_("MIPS PIC call to register other than $25"));
+
+         s = ((mips_opts.micromips
+               && !mips_opts.insn32
+               && (!mips_opts.noreorder || cprestore))
+              ? "jalrs" : "jalr");
+         if (mips_opts.micromips
+             && !mips_opts.insn32
+             && dreg == RA
+             && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
+           macro_build (NULL, s, "mj", sreg);
          else
+           macro_build (NULL, s, JALR_FMT, dreg, sreg);
+         if (mips_pic == SVR4_PIC && !HAVE_NEWABI)
            {
-             int hreg, lreg;
-
-             if (target_big_endian)
-               {
-                 hreg = treg;
-                 lreg = treg + 1;
-               }
+             if (mips_cprestore_offset < 0)
+               as_warn (_("No .cprestore pseudo-op used in PIC code"));
              else
                {
-                 hreg = treg + 1;
-                 lreg = treg;
-               }
-
-             if (hreg <= 31)
-               load_register (hreg, &imm_expr, 0);
-             if (lreg <= 31)
-               {
-                 if (offset_expr.X_op == O_absent)
-                   move_register (lreg, 0);
-                 else
+                 if (!mips_frame_reg_valid)
                    {
-                     gas_assert (offset_expr.X_op == O_constant);
-                     load_register (lreg, &offset_expr, 0);
+                     as_warn (_("No .frame pseudo-op used in PIC code"));
+                     /* Quiet this warning.  */
+                     mips_frame_reg_valid = 1;
+                   }
+                 if (!mips_cprestore_valid)
+                   {
+                     as_warn (_("No .cprestore pseudo-op used in PIC code"));
+                     /* Quiet this warning.  */
+                     mips_cprestore_valid = 1;
                    }
+                 if (mips_opts.noreorder)
+                   macro_build (NULL, "nop", "");
+                 expr1.X_add_number = mips_cprestore_offset;
+                 macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
+                                               mips_gp_register,
+                                               mips_frame_reg,
+                                               HAVE_64BIT_ADDRESSES);
                }
            }
-         break;
        }
 
-      /* We know that sym is in the .rdata section.  First we get the
-        upper 16 bits of the address.  */
-      if (mips_pic == NO_PIC)
-       {
-         macro_build_lui (&offset_expr, AT);
-         used_at = 1;
-       }
-      else
-       {
-         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
-                      BFD_RELOC_MIPS_GOT16, mips_gp_register);
-         used_at = 1;
-       }
+      break;
 
-      /* Now we load the register(s).  */
-      if (HAVE_64BIT_GPRS)
+    case M_JALS_A:
+      gas_assert (mips_opts.micromips);
+      if (mips_opts.insn32)
        {
-         used_at = 1;
-         macro_build (&offset_expr, "ld", "t,o(b)", treg, BFD_RELOC_LO16, AT);
+         as_bad (_("Opcode not supported in the `insn32' mode `%s'"), str);
+         break;
        }
-      else
+      jals = 1;
+      /* Fall through.  */
+    case M_JAL_A:
+      if (mips_pic == NO_PIC)
+       macro_build (&offset_expr, jals ? "jals" : "jal", "a");
+      else if (mips_pic == SVR4_PIC)
        {
-         used_at = 1;
-         macro_build (&offset_expr, "lw", "t,o(b)", treg, BFD_RELOC_LO16, AT);
-         if (treg != 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);
-           }
-       }
-      break;
+         /* If this is a reference to an external symbol, and we are
+            using a small GOT, we want
+              lw       $25,<sym>($gp)          (BFD_RELOC_MIPS_CALL16)
+              nop
+              jalr     $ra,$25
+              nop
+              lw       $gp,cprestore($sp)
+            The cprestore value is set using the .cprestore
+            pseudo-op.  If we are using a big GOT, we want
+              lui      $25,<sym>               (BFD_RELOC_MIPS_CALL_HI16)
+              addu     $25,$25,$gp
+              lw       $25,<sym>($25)          (BFD_RELOC_MIPS_CALL_LO16)
+              nop
+              jalr     $ra,$25
+              nop
+              lw       $gp,cprestore($sp)
+            If the symbol is not external, we want
+              lw       $25,<sym>($gp)          (BFD_RELOC_MIPS_GOT16)
+              nop
+              addiu    $25,$25,<sym>           (BFD_RELOC_LO16)
+              jalr     $ra,$25
+              nop
+              lw $gp,cprestore($sp)
 
-    case M_LI_DD:
-      /* Check if we have a constant in IMM_EXPR.  If the FPRs are 64 bits
-         wide, IMM_EXPR is the entire value and the GPRs are known to be 64
-         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)
-       {
-         used_at = 1;
-         load_register (AT, &imm_expr, HAVE_64BIT_FPRS);
-         if (HAVE_64BIT_FPRS)
+            For NewABI, we use the same CALL16 or CALL_HI16/CALL_LO16
+            sequences above, minus nops, unless the symbol is local,
+            which enables us to use GOT_PAGE/GOT_OFST (big got) or
+            GOT_DISP.  */
+         if (HAVE_NEWABI)
            {
-             gas_assert (HAVE_64BIT_GPRS);
-             macro_build (NULL, "dmtc1", "t,S", AT, treg);
+             if (!mips_big_got)
+               {
+                 relax_start (offset_expr.X_add_symbol);
+                 macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                              PIC_CALL_REG, BFD_RELOC_MIPS_CALL16,
+                              mips_gp_register);
+                 relax_switch ();
+                 macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                              PIC_CALL_REG, BFD_RELOC_MIPS_GOT_DISP,
+                              mips_gp_register);
+                 relax_end ();
+               }
+             else
+               {
+                 relax_start (offset_expr.X_add_symbol);
+                 macro_build (&offset_expr, "lui", LUI_FMT, PIC_CALL_REG,
+                              BFD_RELOC_MIPS_CALL_HI16);
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
+                              PIC_CALL_REG, mips_gp_register);
+                 macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                              PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16,
+                              PIC_CALL_REG);
+                 relax_switch ();
+                 macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                              PIC_CALL_REG, BFD_RELOC_MIPS_GOT_PAGE,
+                              mips_gp_register);
+                 macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+                              PIC_CALL_REG, PIC_CALL_REG,
+                              BFD_RELOC_MIPS_GOT_OFST);
+                 relax_end ();
+               }
+
+             macro_build_jalr (&offset_expr, 0);
            }
          else
            {
-             macro_build (NULL, "mtc1", "t,G", AT, treg + 1);
-             if (offset_expr.X_op == O_absent)
-               macro_build (NULL, "mtc1", "t,G", 0, treg);
+             relax_start (offset_expr.X_add_symbol);
+             if (!mips_big_got)
+               {
+                 macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                              PIC_CALL_REG, BFD_RELOC_MIPS_CALL16,
+                              mips_gp_register);
+                 load_delay_nop ();
+                 relax_switch ();
+               }
              else
                {
-                 gas_assert (offset_expr.X_op == O_constant);
-                 load_register (AT, &offset_expr, 0);
-                 macro_build (NULL, "mtc1", "t,G", AT, treg);
+                 int gpdelay;
+
+                 gpdelay = reg_needs_delay (mips_gp_register);
+                 macro_build (&offset_expr, "lui", LUI_FMT, PIC_CALL_REG,
+                              BFD_RELOC_MIPS_CALL_HI16);
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
+                              PIC_CALL_REG, mips_gp_register);
+                 macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                              PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16,
+                              PIC_CALL_REG);
+                 load_delay_nop ();
+                 relax_switch ();
+                 if (gpdelay)
+                   macro_build (NULL, "nop", "");
                }
-           }
-         break;
-       }
+             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                          PIC_CALL_REG, BFD_RELOC_MIPS_GOT16,
+                          mips_gp_register);
+             load_delay_nop ();
+             macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+                          PIC_CALL_REG, PIC_CALL_REG, BFD_RELOC_LO16);
+             relax_end ();
+             macro_build_jalr (&offset_expr, mips_cprestore_offset >= 0);
 
-      gas_assert (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)
-       {
-         if (CPU_HAS_LDC1_SDC1 (mips_opts.arch) || mips_opts.micromips)
-           {
-             macro_build (&offset_expr, "ldc1", "T,o(b)", treg,
-                          BFD_RELOC_MIPS_LITERAL, mips_gp_register);
-             break;
+             if (mips_cprestore_offset < 0)
+               as_warn (_("No .cprestore pseudo-op used in PIC code"));
+             else
+               {
+                 if (!mips_frame_reg_valid)
+                   {
+                     as_warn (_("No .frame pseudo-op used in PIC code"));
+                     /* Quiet this warning.  */
+                     mips_frame_reg_valid = 1;
+                   }
+                 if (!mips_cprestore_valid)
+                   {
+                     as_warn (_("No .cprestore pseudo-op used in PIC code"));
+                     /* Quiet this warning.  */
+                     mips_cprestore_valid = 1;
+                   }
+                 if (mips_opts.noreorder)
+                   macro_build (NULL, "nop", "");
+                 expr1.X_add_number = mips_cprestore_offset;
+                 macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
+                                               mips_gp_register,
+                                               mips_frame_reg,
+                                               HAVE_64BIT_ADDRESSES);
+               }
            }
-         breg = mips_gp_register;
-         r = BFD_RELOC_MIPS_LITERAL;
-         goto dob;
        }
+      else if (mips_pic == VXWORKS_PIC)
+       as_bad (_("Non-PIC jump used in PIC library"));
       else
-       {
-         gas_assert (strcmp (s, RDATA_SECTION_NAME) == 0);
-         used_at = 1;
-         if (mips_pic != NO_PIC)
-           macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
-                        BFD_RELOC_MIPS_GOT16, mips_gp_register);
-         else
-           {
-             /* FIXME: This won't work for a 64 bit address.  */
-             macro_build_lui (&offset_expr, AT);
-           }
-
-         if (CPU_HAS_LDC1_SDC1 (mips_opts.arch) || mips_opts.micromips)
-           {
-             macro_build (&offset_expr, "ldc1", "T,o(b)",
-                          treg, BFD_RELOC_LO16, AT);
-             break;
-           }
-         breg = AT;
-         r = BFD_RELOC_LO16;
-         goto dob;
-       }
-
-    case M_L_DOB:
-      /* Even on a big endian machine $fn comes before $fn+1.  We have
-        to adjust when loading from memory.  */
-      r = BFD_RELOC_LO16;
-    dob:
-      gas_assert (!mips_opts.micromips);
-      gas_assert (!CPU_HAS_LDC1_SDC1 (mips_opts.arch));
-      macro_build (&offset_expr, "lwc1", "T,o(b)",
-                  target_big_endian ? treg + 1 : treg, r, breg);
-      /* FIXME: A possible overflow which I don't know how to deal
-        with.  */
-      offset_expr.X_add_number += 4;
-      macro_build (&offset_expr, "lwc1", "T,o(b)",
-                  target_big_endian ? treg : treg + 1, r, breg);
-      break;
+       abort ();
 
-    case M_S_DOB:
-      gas_assert (!mips_opts.micromips);
-      gas_assert (!CPU_HAS_LDC1_SDC1 (mips_opts.arch));
-      /* Even on a big endian machine $fn comes before $fn+1.  We have
-        to adjust when storing to memory.  */
-      macro_build (&offset_expr, "swc1", "T,o(b)",
-                  target_big_endian ? treg + 1 : treg, BFD_RELOC_LO16, breg);
-      offset_expr.X_add_number += 4;
-      macro_build (&offset_expr, "swc1", "T,o(b)",
-                  target_big_endian ? treg : treg + 1, BFD_RELOC_LO16, breg);
       break;
 
-    case M_L_DAB:
+    case M_LBUE_AB:
+      s = "lbue";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_LHUE_AB:
+      s = "lhue";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_LBE_AB:
+      s = "lbe";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_LHE_AB:
+      s = "lhe";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_LLE_AB:
+      s = "lle";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_LWE_AB:
+      s = "lwe";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_LWLE_AB:
+      s = "lwle";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_LWRE_AB:
+      s = "lwre";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_SBE_AB:
+      s = "sbe";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_SCE_AB:
+      s = "sce";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_SHE_AB:
+      s = "she";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_SWE_AB:
+      s = "swe";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_SWLE_AB:
+      s = "swle";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_SWRE_AB:
+      s = "swre";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      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;
+    case M_LB_AB:
+      s = "lb";
+      fmt = "t,o(b)";
+      goto ld;
+    case M_LBU_AB:
+      s = "lbu";
+      fmt = "t,o(b)";
+      goto ld;
+    case M_LH_AB:
+      s = "lh";
+      fmt = "t,o(b)";
+      goto ld;
+    case M_LHU_AB:
+      s = "lhu";
+      fmt = "t,o(b)";
+      goto ld;
+    case M_LW_AB:
+      s = "lw";
+      fmt = "t,o(b)";
+      goto ld;
+    case M_LWC0_AB:
       gas_assert (!mips_opts.micromips);
-      /*
-       * The MIPS assembler seems to check for X_add_number not
-       * being double aligned and generating:
-       *       lui     at,%hi(foo+1)
-       *       addu    at,at,v1
-       *       addiu   at,at,%lo(foo+1)
-       *       lwc1    f2,0(at)
-       *       lwc1    f3,4(at)
-       * But, the resulting address is the same after relocation so why
-       * generate the extra instruction?
-       */
+      s = "lwc0";
+      fmt = "E,o(b)";
       /* Itbl support may require additional care here.  */
       coproc = 1;
-      fmt = "T,o(b)";
-      if (CPU_HAS_LDC1_SDC1 (mips_opts.arch))
-       {
-         s = "ldc1";
-         goto ld_st;
-       }
+      goto ld_st;
+    case M_LWC1_AB:
       s = "lwc1";
-      goto ldd_std;
-
-    case M_S_DAB:
+      fmt = "T,o(b)";
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_LWC2_AB:
+      s = "lwc2";
+      fmt = COP12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_LWC3_AB:
       gas_assert (!mips_opts.micromips);
+      s = "lwc3";
+      fmt = "E,o(b)";
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_LWL_AB:
+      s = "lwl";
+      fmt = MEM12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      goto ld_st;
+    case M_LWR_AB:
+      s = "lwr";
+      fmt = MEM12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      goto ld_st;
+    case M_LDC1_AB:
+      s = "ldc1";
+      fmt = "T,o(b)";
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_LDC2_AB:
+      s = "ldc2";
+      fmt = COP12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_LQC2_AB:
+      s = "lqc2";
+      fmt = "E,o(b)";
       /* Itbl support may require additional care here.  */
       coproc = 1;
-      fmt = "T,o(b)";
-      if (CPU_HAS_LDC1_SDC1 (mips_opts.arch))
-       {
-         s = "sdc1";
-         goto ld_st;
-       }
-      s = "swc1";
-      goto ldd_std;
-
-    case M_LQ_AB:
-      fmt = "t,o(b)";
-      s = "lq";
-      goto ld;
-
-    case M_SQ_AB:
-      fmt = "t,o(b)";
-      s = "sq";
       goto ld_st;
-
-    case M_LD_AB:
-      fmt = "t,o(b)";
-      if (HAVE_64BIT_GPRS)
-       {
-         s = "ld";
-         goto ld;
-       }
-      s = "lw";
-      goto ldd_std;
-
-    case M_SD_AB:
-      fmt = "t,o(b)";
-      if (HAVE_64BIT_GPRS)
-       {
-         s = "sd";
-         goto ld_st;
-       }
-      s = "sw";
-
-    ldd_std:
-      if (offset_expr.X_op != O_symbol
-         && offset_expr.X_op != O_constant)
-       {
-         as_bad (_("Expression too complex"));
-         offset_expr.X_op = O_constant;
-       }
-
-      if (HAVE_32BIT_ADDRESSES
-         && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
-       {
-         char value [32];
-
-         sprintf_vma (value, offset_expr.X_add_number);
-         as_bad (_("Number (0x%s) larger than 32 bits"), value);
-       }
-
-      /* Even on a big endian machine $fn comes before $fn+1.  We have
-        to adjust when loading from memory.  We set coproc if we must
-        load $fn+1 first.  */
+    case M_LDC3_AB:
+      s = "ldc3";
+      fmt = "E,o(b)";
       /* Itbl support may require additional care here.  */
-      if (!target_big_endian)
-       coproc = 0;
-
-      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)
-            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)
-            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)
-            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.  */
-         if (offset_expr.X_op == O_symbol
-             && (valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
-             && !nopic_need_relax (offset_expr.X_add_symbol, 1))
-           {
-             relax_start (offset_expr.X_add_symbol);
-             if (breg == 0)
-               {
-                 tempreg = mips_gp_register;
-               }
-             else
-               {
-                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                              AT, breg, mips_gp_register);
-                 tempreg = AT;
-                 used_at = 1;
-               }
-
-             /* Itbl support may require additional care here.  */
-             macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
-                          BFD_RELOC_GPREL16, tempreg);
-             offset_expr.X_add_number += 4;
-
-             /* Set mips_optimize to 2 to avoid inserting an
-                 undesired nop.  */
-             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,
-                          BFD_RELOC_GPREL16, tempreg);
-             mips_optimize = hold_mips_optimize;
-
-             relax_switch ();
-
-             offset_expr.X_add_number -= 4;
-           }
-         used_at = 1;
-         macro_build_lui (&offset_expr, AT);
-         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,
-                      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,
-                      BFD_RELOC_LO16, AT);
-         if (mips_relax.sequence)
-           relax_end ();
-       }
-      else if (!mips_big_got)
-       {
-         /* 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)
-            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)
-            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.  */
-         used_at = 1;
-         expr1.X_add_number = offset_expr.X_add_number;
-         if (expr1.X_add_number < -0x8000
-             || expr1.X_add_number >= 0x8000 - 4)
-           as_bad (_("PIC code offset overflow (max 16 signed bits)"));
-         load_got_offset (AT, &offset_expr);
-         load_delay_nop ();
-         if (breg != 0)
-           macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
-
-         /* Set mips_optimize to 2 to avoid inserting an undesired
-             nop.  */
-         hold_mips_optimize = mips_optimize;
-         mips_optimize = 2;
-
-         /* Itbl support may require additional care here.  */
-         relax_start (offset_expr.X_add_symbol);
-         macro_build (&expr1, s, fmt, coproc ? treg + 1 : treg,
-                      BFD_RELOC_LO16, AT);
-         expr1.X_add_number += 4;
-         macro_build (&expr1, s, fmt, coproc ? treg : treg + 1,
-                      BFD_RELOC_LO16, AT);
-         relax_switch ();
-         macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
-                      BFD_RELOC_LO16, AT);
-         offset_expr.X_add_number += 4;
-         macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
-                      BFD_RELOC_LO16, AT);
-         relax_end ();
-
-         mips_optimize = hold_mips_optimize;
-       }
-      else if (mips_big_got)
-       {
-         int gpdelay;
-
-         /* If this is a reference to an external symbol, we want
-              lui      $at,<sym>               (BFD_RELOC_MIPS_GOT_HI16)
-              addu     $at,$at,$gp
-              lw       $at,<sym>($at)          (BFD_RELOC_MIPS_GOT_LO16)
-              nop
-              <op>     $treg,0($at)
-              <op>     $treg+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)
-            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.  */
-         used_at = 1;
-         expr1.X_add_number = offset_expr.X_add_number;
-         offset_expr.X_add_number = 0;
-         if (expr1.X_add_number < -0x8000
-             || expr1.X_add_number >= 0x8000 - 4)
-           as_bad (_("PIC code offset overflow (max 16 signed bits)"));
-         gpdelay = reg_needs_delay (mips_gp_register);
-         relax_start (offset_expr.X_add_symbol);
-         macro_build (&offset_expr, "lui", LUI_FMT,
-                      AT, BFD_RELOC_MIPS_GOT_HI16);
-         macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                      AT, AT, mips_gp_register);
-         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
-                      AT, BFD_RELOC_MIPS_GOT_LO16, AT);
-         load_delay_nop ();
-         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,
-                      BFD_RELOC_LO16, AT);
-         expr1.X_add_number += 4;
-
-         /* Set mips_optimize to 2 to avoid inserting an undesired
-             nop.  */
-         hold_mips_optimize = mips_optimize;
-         mips_optimize = 2;
-         /* Itbl support may require additional care here.  */
-         macro_build (&expr1, s, fmt, coproc ? treg : treg + 1,
-                      BFD_RELOC_LO16, AT);
-         mips_optimize = hold_mips_optimize;
-         expr1.X_add_number -= 4;
-
-         relax_switch ();
-         offset_expr.X_add_number = expr1.X_add_number;
-         if (gpdelay)
-           macro_build (NULL, "nop", "");
-         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
-                      BFD_RELOC_MIPS_GOT16, mips_gp_register);
-         load_delay_nop ();
-         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,
-                      BFD_RELOC_LO16, AT);
-         offset_expr.X_add_number += 4;
+      coproc = 1;
+      goto ld_st;
+    case M_LDL_AB:
+      s = "ldl";
+      fmt = MEM12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      goto ld_st;
+    case M_LDR_AB:
+      s = "ldr";
+      fmt = MEM12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      goto ld_st;
+    case M_LL_AB:
+      s = "ll";
+      fmt = MEM12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      goto ld;
+    case M_LLD_AB:
+      s = "lld";
+      fmt = MEM12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      goto ld;
+    case M_LWU_AB:
+      s = "lwu";
+      fmt = MEM12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      goto ld;
+    case M_LWP_AB:
+      gas_assert (mips_opts.micromips);
+      s = "lwp";
+      fmt = "t,~(b)";
+      offbits = 12;
+      lp = 1;
+      goto ld;
+    case M_LDP_AB:
+      gas_assert (mips_opts.micromips);
+      s = "ldp";
+      fmt = "t,~(b)";
+      offbits = 12;
+      lp = 1;
+      goto ld;
+    case M_LWM_AB:
+      gas_assert (mips_opts.micromips);
+      s = "lwm";
+      fmt = "n,~(b)";
+      offbits = 12;
+      goto ld_st;
+    case M_LDM_AB:
+      gas_assert (mips_opts.micromips);
+      s = "ldm";
+      fmt = "n,~(b)";
+      offbits = 12;
+      goto ld_st;
 
-         /* Set mips_optimize to 2 to avoid inserting an undesired
-             nop.  */
-         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,
-                      BFD_RELOC_LO16, AT);
-         mips_optimize = hold_mips_optimize;
-         relax_end ();
-       }
+    ld:
+      /* We don't want to use $0 as tempreg.  */
+      if (breg == treg + lp || treg + lp == ZERO)
+       goto ld_st;
       else
-       abort ();
-
-      break;
-
-    case M_LD_OB:
-      s = HAVE_64BIT_GPRS ? "ld" : "lw";
-      goto sd_ob;
-    case M_SD_OB:
-      s = HAVE_64BIT_GPRS ? "sd" : "sw";
-    sd_ob:
-      macro_build (&offset_expr, s, "t,o(b)", treg,
-                  -1, offset_reloc[0], offset_reloc[1], offset_reloc[2],
-                  breg);
-      if (!HAVE_64BIT_GPRS)
-       {
-         offset_expr.X_add_number += 4;
-         macro_build (&offset_expr, s, "t,o(b)", treg + 1,
-                      -1, offset_reloc[0], offset_reloc[1], offset_reloc[2],
-                      breg);
-       }
-      break;
+       tempreg = treg + lp;
+      goto ld_noat;
 
-       
-    case M_SAA_AB:
-      ab = 1;
-    case M_SAA_OB:
-      s = "saa";
-      offbits = 0;
-      fmt = "t,(b)";
+    case M_SB_AB:
+      s = "sb";
+      fmt = "t,o(b)";
       goto ld_st;
-    case M_SAAD_AB:
-      ab = 1;
-    case M_SAAD_OB:
-      s = "saad";
-      offbits = 0;
-      fmt = "t,(b)";
+    case M_SH_AB:
+      s = "sh";
+      fmt = "t,o(b)";
       goto ld_st;
-
-   /* New code added to support COPZ instructions.
-      This code builds table entries out of the macros in mip_opcodes.
-      R4000 uses interlocks to handle coproc delays.
-      Other chips (like the R3000) require nops to be inserted for delays.
-
-      FIXME: Currently, we require that the user handle delays.
-      In order to fill delay slots for non-interlocked chips,
-      we must have a way to specify delays based on the coprocessor.
-      Eg. 4 cycles if load coproc reg from memory, 1 if in cache, etc.
-      What are the side-effects of the cop instruction?
-      What cache support might we have and what are its effects?
-      Both coprocessor & memory require delays. how long???
-      What registers are read/set/modified?
-
-      If an itbl is provided to interpret cop instructions,
-      this knowledge can be encoded in the itbl spec.  */
-
-    case M_COP0:
-      s = "c0";
-      goto copz;
-    case M_COP1:
-      s = "c1";
-      goto copz;
-    case M_COP2:
-      s = "c2";
-      goto copz;
-    case M_COP3:
-      s = "c3";
-    copz:
+    case M_SW_AB:
+      s = "sw";
+      fmt = "t,o(b)";
+      goto ld_st;
+    case M_SWC0_AB:
       gas_assert (!mips_opts.micromips);
-      /* For now we just do C (same as Cz).  The parameter will be
-         stored in insn_opcode by mips_ip.  */
-      macro_build (NULL, s, "C", ip->insn_opcode);
-      break;
-
-    case M_MOVE:
-      move_register (dreg, sreg);
-      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);
-        }
-      else
-        {
-      macro_build (NULL, dbl ? "dmultu" : "multu", "s,t", sreg, treg);
-      macro_build (NULL, "mflo", MFHL_FMT, dreg);
-        }
-      break;
-
-    case M_DMUL_I:
-      dbl = 1;
-    case M_MUL_I:
-      /* The MIPS assembler some times generates shifts and adds.  I'm
-        not trying to be that fancy. GCC should do this for us
-        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);
-      break;
-
-    case M_DMULO_I:
-      dbl = 1;
-    case M_MULO_I:
-      imm = 1;
-      goto do_mulo;
+      s = "swc0";
+      fmt = "E,o(b)";
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_SWC1_AB:
+      s = "swc1";
+      fmt = "T,o(b)";
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_SWC2_AB:
+      s = "swc2";
+      fmt = COP12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_SWC3_AB:
+      gas_assert (!mips_opts.micromips);
+      s = "swc3";
+      fmt = "E,o(b)";
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_SWL_AB:
+      s = "swl";
+      fmt = MEM12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      goto ld_st;
+    case M_SWR_AB:
+      s = "swr";
+      fmt = MEM12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      goto ld_st;
+    case M_SC_AB:
+      s = "sc";
+      fmt = MEM12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      goto ld_st;
+    case M_SCD_AB:
+      s = "scd";
+      fmt = MEM12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 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);
+      goto ld_st;
+    case M_CACHEE_AB:
+      s = "cachee";
+      fmt = "k,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_PREF_AB:
+      s = "pref";
+      fmt = !mips_opts.micromips ? "k,o(b)" : "k,~(b)";
+      offbits = (mips_opts.micromips ? 12 : 16);
+      goto ld_st;
+    case M_PREFE_AB:
+      s = "prefe";
+      fmt = "k,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_SDC1_AB:
+      s = "sdc1";
+      fmt = "T,o(b)";
+      coproc = 1;
+      /* Itbl support may require additional care here.  */
+      goto ld_st;
+    case M_SDC2_AB:
+      s = "sdc2";
+      fmt = COP12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_SQC2_AB:
+      s = "sqc2";
+      fmt = "E,o(b)";
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_SDC3_AB:
+      gas_assert (!mips_opts.micromips);
+      s = "sdc3";
+      fmt = "E,o(b)";
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_SDL_AB:
+      s = "sdl";
+      fmt = MEM12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      goto ld_st;
+    case M_SDR_AB:
+      s = "sdr";
+      fmt = MEM12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      goto ld_st;
+    case M_SWP_AB:
+      gas_assert (mips_opts.micromips);
+      s = "swp";
+      fmt = "t,~(b)";
+      offbits = 12;
+      goto ld_st;
+    case M_SDP_AB:
+      gas_assert (mips_opts.micromips);
+      s = "sdp";
+      fmt = "t,~(b)";
+      offbits = 12;
+      goto ld_st;
+    case M_SWM_AB:
+      gas_assert (mips_opts.micromips);
+      s = "swm";
+      fmt = "n,~(b)";
+      offbits = 12;
+      goto ld_st;
+    case M_SDM_AB:
+      gas_assert (mips_opts.micromips);
+      s = "sdm";
+      fmt = "n,~(b)";
+      offbits = 12;
 
-    case M_DMULO:
-      dbl = 1;
-    case M_MULO:
-    do_mulo:
-      start_noreorder ();
-      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, "mfhi", MFHL_FMT, AT);
-      if (mips_trap)
-       macro_build (NULL, "tne", TRAP_FMT, dreg, AT, 6);
-      else
+    ld_st:
+      tempreg = AT;
+    ld_noat:
+      if (small_offset_p (0, align, 16))
        {
-         if (mips_opts.micromips)
-           micromips_label_expr (&label_expr);
+         /* 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],
+                        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);
+             else
+               macro_build (NULL, s, fmt, treg,
+                            (int) offset_expr.X_add_number, breg);
+           }
          else
-           label_expr.X_add_number = 8;
-         macro_build (&label_expr, "beq", "s,t,p", dreg, AT);
-         macro_build (NULL, "nop", "");
-         macro_build (NULL, "break", BRK_FMT, 6);
-         if (mips_opts.micromips)
-           micromips_add_label ();
+           {
+             if (tempreg == AT)
+               used_at = 1;
+             macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+                          tempreg, breg, -1, offset_reloc[0],
+                          offset_reloc[1], offset_reloc[2]);
+             if (offbits == 0)
+               macro_build (NULL, s, fmt, treg, tempreg);
+             else
+               macro_build (NULL, s, fmt, treg, 0, tempreg);
+           }
+         break;
        }
-      end_noreorder ();
-      macro_build (NULL, "mflo", MFHL_FMT, dreg);
-      break;
 
-    case M_DMULOU_I:
-      dbl = 1;
-    case M_MULOU_I:
-      imm = 1;
-      goto do_mulou;
+      if (tempreg == AT)
+       used_at = 1;
 
-    case M_DMULOU:
-      dbl = 1;
-    case M_MULOU:
-    do_mulou:
-      start_noreorder ();
-      used_at = 1;
-      if (imm)
-       load_register (AT, &imm_expr, dbl);
-      macro_build (NULL, dbl ? "dmultu" : "multu", "s,t",
-                  sreg, imm ? AT : treg);
-      macro_build (NULL, "mfhi", MFHL_FMT, AT);
-      macro_build (NULL, "mflo", MFHL_FMT, dreg);
-      if (mips_trap)
-       macro_build (NULL, "tne", TRAP_FMT, AT, ZERO, 6);
-      else
+      if (offset_expr.X_op != O_constant
+         && offset_expr.X_op != O_symbol)
        {
-         if (mips_opts.micromips)
-           micromips_label_expr (&label_expr);
-         else
-           label_expr.X_add_number = 8;
-         macro_build (&label_expr, "beq", "s,t,p", AT, ZERO);
-         macro_build (NULL, "nop", "");
-         macro_build (NULL, "break", BRK_FMT, 6);
-         if (mips_opts.micromips)
-           micromips_add_label ();
+         as_bad (_("Expression too complex"));
+         offset_expr.X_op = O_constant;
        }
-      end_noreorder ();
-      break;
 
-    case M_DROL:
-      if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+      if (HAVE_32BIT_ADDRESSES
+         && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
        {
-         if (dreg == sreg)
+         char value [32];
+
+         sprintf_vma (value, offset_expr.X_add_number);
+         as_bad (_("Number (0x%s) larger than 32 bits"), value);
+       }
+
+      /* A constant expression in PIC code can be handled just as it
+        is in non PIC code.  */
+      if (offset_expr.X_op == O_constant)
+       {
+         expr1.X_add_number = offset_high_part (offset_expr.X_add_number,
+                                                offbits == 0 ? 16 : offbits);
+         offset_expr.X_add_number -= expr1.X_add_number;
+
+         load_register (tempreg, &expr1, HAVE_64BIT_ADDRESSES);
+         if (breg != 0)
+           macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                        tempreg, tempreg, breg);
+         if (offbits == 0)
            {
-             tempreg = AT;
-             used_at = 1;
+             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);
            }
+         else if (offbits == 16)
+           macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16, tempreg);
          else
-           {
-             tempreg = dreg;
-           }
-         macro_build (NULL, "dnegu", "d,w", tempreg, treg);
-         macro_build (NULL, "drorv", "d,t,s", dreg, sreg, tempreg);
-         break;
+           macro_build (NULL, s, fmt, treg,
+                        (int) offset_expr.X_add_number, tempreg);
        }
-      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);
-      break;
-
-    case M_ROL:
-      if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+      else if (offbits != 16)
        {
-         if (dreg == sreg)
+         /* The offset field is too narrow to be used for a low-part
+            relocation, so load the whole address into the auxillary
+            register.  */
+         load_address (tempreg, &offset_expr, &used_at);
+         if (breg != 0)
+           macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                        tempreg, tempreg, breg);
+         if (offbits == 0)
+           macro_build (NULL, s, fmt, treg, tempreg);
+         else
+           macro_build (NULL, s, fmt, treg, 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)
+            Otherwise, if there is no base register, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_HI16_S)
+              <op>     $treg,<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)
+            Otherwise we want
+              lui      $tempreg,<sym>          (BFD_RELOC_HI16_S)
+              addu     $tempreg,$tempreg,$breg
+              <op>     $treg,<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,
+            we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
+              lui      $at,<sym>               (BFD_RELOC_HI16_S)
+              daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
+              dsll32   $tempreg,0
+              daddu    $tempreg,$at
+              <op>     $treg,<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)
+              daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
+              daddu    $at,$breg
+              dsll32   $tempreg,0
+              daddu    $tempreg,$at
+              <op>     $treg,<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.
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
+              daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
+              dsll     $tempreg,16
+              daddiu   $tempreg,<sym>          (BFD_RELOC_HI16_S)
+              dsll     $tempreg,16
+              <op>     $treg,<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)
+              dsll     $tempreg,16
+              daddiu   $tempreg,<sym>          (BFD_RELOC_HI16_S)
+              dsll     $tempreg,16
+              daddu    $tempreg,$tempreg,$breg
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+
+            For GP relative symbols in 64bit address space we can use
+            the same sequence as in 32bit address space.  */
+         if (HAVE_64BIT_SYMBOLS)
+           {
+             if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+                 && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+               {
+                 relax_start (offset_expr.X_add_symbol);
+                 if (breg == 0)
+                   {
+                     macro_build (&offset_expr, s, fmt, treg,
+                                  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,
+                                  BFD_RELOC_GPREL16, tempreg);
+                   }
+                 relax_switch ();
+               }
+
+             if (used_at == 0 && mips_opts.at)
+               {
+                 macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
+                              BFD_RELOC_MIPS_HIGHEST);
+                 macro_build (&offset_expr, "lui", LUI_FMT, AT,
+                              BFD_RELOC_HI16_S);
+                 macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
+                              tempreg, BFD_RELOC_MIPS_HIGHER);
+                 if (breg != 0)
+                   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,
+                              tempreg);
+                 used_at = 1;
+               }
+             else
+               {
+                 macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
+                              BFD_RELOC_MIPS_HIGHEST);
+                 macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
+                              tempreg, BFD_RELOC_MIPS_HIGHER);
+                 macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
+                 macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
+                              tempreg, BFD_RELOC_HI16_S);
+                 macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
+                 if (breg != 0)
+                   macro_build (NULL, "daddu", "d,v,t",
+                                tempreg, tempreg, breg);
+                 macro_build (&offset_expr, s, fmt, treg,
+                              BFD_RELOC_LO16, tempreg);
+               }
+
+             if (mips_relax.sequence)
+               relax_end ();
+             break;
+           }
+
+         if (breg == 0)
            {
-             tempreg = AT;
-             used_at = 1;
+             if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+                 && !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,
+                              mips_gp_register);
+                 relax_switch ();
+               }
+             macro_build_lui (&offset_expr, tempreg);
+             macro_build (&offset_expr, s, fmt, treg,
+                          BFD_RELOC_LO16, tempreg);
+             if (mips_relax.sequence)
+               relax_end ();
            }
          else
            {
-             tempreg = dreg;
+             if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+                 && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+               {
+                 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,
+                              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,
+                          BFD_RELOC_LO16, tempreg);
+             if (mips_relax.sequence)
+               relax_end ();
            }
-         macro_build (NULL, "negu", "d,w", tempreg, treg);
-         macro_build (NULL, "rorv", "d,t,s", dreg, sreg, 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);
-      break;
+      else if (!mips_big_got)
+       {
+         int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
 
-    case M_DROL_I:
-      {
-       unsigned int rot;
-       char *l;
-       char *rr;
+         /* If this is a reference to an external symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              nop
+              <op>     $treg,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)
 
-       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);
-           else
-             macro_build (NULL, "dror", SHFT_FMT, dreg, sreg, rot);
-           break;
-         }
-       if (rot == 0)
-         {
-           macro_build (NULL, "dsrl", SHFT_FMT, dreg, sreg, 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);
-      }
-      break;
+            For NewABI, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_PAGE)
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_MIPS_GOT_OFST)
 
-    case M_ROL_I:
-      {
-       unsigned int rot;
+            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
+            16 bits, because we have no way to load the upper 16 bits
+            (actually, we could handle them for the subset of cases
+            in which we are not using $at).  */
+         gas_assert (offset_expr.X_op == O_symbol);
+         if (HAVE_NEWABI)
+           {
+             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                          BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
+             if (breg != 0)
+               macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                            tempreg, tempreg, breg);
+             macro_build (&offset_expr, s, fmt, treg,
+                          BFD_RELOC_MIPS_GOT_OFST, tempreg);
+             break;
+           }
+         expr1.X_add_number = offset_expr.X_add_number;
+         offset_expr.X_add_number = 0;
+         if (expr1.X_add_number < -0x8000
+             || expr1.X_add_number >= 0x8000)
+           as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                      lw_reloc_type, mips_gp_register);
+         load_delay_nop ();
+         relax_start (offset_expr.X_add_symbol);
+         relax_switch ();
+         macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
+                      tempreg, BFD_RELOC_LO16);
+         relax_end ();
+         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);
+       }
+      else if (mips_big_got && !HAVE_NEWABI)
+       {
+         int gpdelay;
 
-       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);
-           break;
-         }
-       if (rot == 0)
-         {
-           macro_build (NULL, "srl", SHFT_FMT, dreg, sreg, 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);
-      }
-      break;
+         /* If this is a reference to an external symbol, we want
+              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)
+            Otherwise we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              nop
+              addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+              <op>     $treg,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
+            16 bits, because we have no way to load the upper 16 bits
+            (actually, we could handle them for the subset of cases
+            in which we are not using $at).  */
+         gas_assert (offset_expr.X_op == O_symbol);
+         expr1.X_add_number = offset_expr.X_add_number;
+         offset_expr.X_add_number = 0;
+         if (expr1.X_add_number < -0x8000
+             || expr1.X_add_number >= 0x8000)
+           as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+         gpdelay = reg_needs_delay (mips_gp_register);
+         relax_start (offset_expr.X_add_symbol);
+         macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
+                      BFD_RELOC_MIPS_GOT_HI16);
+         macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg,
+                      mips_gp_register);
+         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                      BFD_RELOC_MIPS_GOT_LO16, tempreg);
+         relax_switch ();
+         if (gpdelay)
+           macro_build (NULL, "nop", "");
+         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                      BFD_RELOC_MIPS_GOT16, mips_gp_register);
+         load_delay_nop ();
+         macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
+                      tempreg, BFD_RELOC_LO16);
+         relax_end ();
 
-    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);
-         break;
+         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);
        }
-      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);
-      break;
-
-    case M_ROR:
-      if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+      else if (mips_big_got && HAVE_NEWABI)
        {
-         macro_build (NULL, "rorv", "d,t,s", dreg, sreg, treg);
-         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);
-      break;
+         /* If this is a reference to an external symbol, we want
+              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)
+            Otherwise, for local symbols, we want:
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_PAGE)
+              <op>     $treg,<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;
+         if (expr1.X_add_number < -0x8000
+             || expr1.X_add_number >= 0x8000)
+           as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+         relax_start (offset_expr.X_add_symbol);
+         macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
+                      BFD_RELOC_MIPS_GOT_HI16);
+         macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg,
+                      mips_gp_register);
+         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                      BFD_RELOC_MIPS_GOT_LO16, tempreg);
+         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);
+
+         relax_switch ();
+         offset_expr.X_add_number = expr1.X_add_number;
+         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                      BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
+         if (breg != 0)
+           macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                        tempreg, tempreg, breg);
+         macro_build (&offset_expr, s, fmt, treg,
+                      BFD_RELOC_MIPS_GOT_OFST, tempreg);
+         relax_end ();
+       }
+      else
+       abort ();
 
-    case M_DROR_I:
-      {
-       unsigned int rot;
-       char *l;
-       char *rr;
+      break;
 
-       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);
-           else
-             macro_build (NULL, "dror", SHFT_FMT, dreg, sreg, rot);
-           break;
-         }
-       if (rot == 0)
-         {
-           macro_build (NULL, "dsrl", SHFT_FMT, dreg, sreg, 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);
-      }
+    case M_JRADDIUSP:
+      gas_assert (mips_opts.micromips);
+      gas_assert (mips_opts.insn32);
+      start_noreorder ();
+      macro_build (NULL, "jr", "s", RA);
+      expr1.X_add_number = EXTRACT_OPERAND (1, IMMP, *ip) << 2;
+      macro_build (&expr1, "addiu", "t,r,j", SP, SP, BFD_RELOC_LO16);
+      end_noreorder ();
       break;
 
-    case M_ROR_I:
-      {
-       unsigned int rot;
+    case M_JRC:
+      gas_assert (mips_opts.micromips);
+      gas_assert (mips_opts.insn32);
+      macro_build (NULL, "jr", "s", sreg);
+      if (mips_opts.noreorder)
+       macro_build (NULL, "nop", "");
+      break;
 
-       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);
-           break;
-         }
-       if (rot == 0)
-         {
-           macro_build (NULL, "srl", SHFT_FMT, dreg, sreg, 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);
-      }
+    case M_LI:
+    case M_LI_S:
+      load_register (treg, &imm_expr, 0);
       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);
-      else
-       {
-         macro_build (NULL, "xor", "d,v,t", dreg, sreg, treg);
-         macro_build (&expr1, "sltiu", "t,r,j", dreg, dreg, BFD_RELOC_LO16);
-       }
+    case M_DLI:
+      load_register (treg, &imm_expr, 1);
       break;
 
-    case M_SEQ_I:
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
-       {
-         macro_build (&expr1, "sltiu", "t,r,j", dreg, sreg, BFD_RELOC_LO16);
-         break;
-       }
-      if (sreg == 0)
-       {
-         as_warn (_("Instruction %s: result is always false"),
-                  ip->insn_mo->name);
-         move_register (dreg, 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,
-                      (int) imm_expr.X_add_number);
-         break;
-       }
-      if (imm_expr.X_op == O_constant
-         && 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
-              && 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);
-       }
-      else if (CPU_HAS_SEQ (mips_opts.arch))
+    case M_LI_SS:
+      if (imm_expr.X_op == O_constant)
        {
          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, 0);
+         macro_build (NULL, "mtc1", "t,G", AT, treg);
          break;
        }
       else
        {
-         load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-         macro_build (NULL, "xor", "d,v,t", dreg, sreg, AT);
-         used_at = 1;
+         gas_assert (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,
+                      BFD_RELOC_MIPS_LITERAL, mips_gp_register);
+         break;
        }
-      macro_build (&expr1, "sltiu", "t,r,j", dreg, dreg, BFD_RELOC_LO16);
-      break;
-
-    case M_SGE:                /* sreg >= treg <==> not (sreg < treg) */
-      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);
-      break;
 
-    case M_SGE_I:              /* sreg >= I <==> not (sreg < I) */
-    case M_SGEU_I:
-      if (imm_expr.X_op == O_constant
-         && 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);
-       }
-      else
+    case M_LI_D:
+      /* Check if we have a constant in IMM_EXPR.  If the GPRs are 64 bits
+         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)
        {
-         load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-         macro_build (NULL, mask == M_SGE_I ? "slt" : "sltu", "d,v,t",
-                      dreg, sreg, AT);
-         used_at = 1;
-       }
-      macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
-      break;
-
-    case M_SGT:                /* sreg > treg  <==>  treg < sreg */
-      s = "slt";
-      goto sgt;
-    case M_SGTU:
-      s = "sltu";
-    sgt:
-      macro_build (NULL, s, "d,v,t", dreg, treg, sreg);
-      break;
-
-    case M_SGT_I:              /* sreg > I  <==>  I < sreg */
-      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);
-      break;
-
-    case M_SLE:        /* sreg <= treg  <==>  treg >= sreg  <==>  not (treg < sreg) */
-      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);
-      break;
+         if (HAVE_64BIT_GPRS)
+           load_register (treg, &imm_expr, 1);
+         else
+           {
+             int hreg, lreg;
 
-    case M_SLE_I:      /* sreg <= I <==> I >= sreg <==> not (I < sreg) */
-      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);
-      break;
+             if (target_big_endian)
+               {
+                 hreg = treg;
+                 lreg = treg + 1;
+               }
+             else
+               {
+                 hreg = treg + 1;
+                 lreg = treg;
+               }
 
-    case M_SLT_I:
-      if (imm_expr.X_op == O_constant
-         && 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);
+             if (hreg <= 31)
+               load_register (hreg, &imm_expr, 0);
+             if (lreg <= 31)
+               {
+                 if (offset_expr.X_op == O_absent)
+                   move_register (lreg, 0);
+                 else
+                   {
+                     gas_assert (offset_expr.X_op == O_constant);
+                     load_register (lreg, &offset_expr, 0);
+                   }
+               }
+           }
          break;
        }
-      used_at = 1;
-      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build (NULL, "slt", "d,v,t", dreg, sreg, AT);
-      break;
 
-    case M_SLTU_I:
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number >= -0x8000
-         && imm_expr.X_add_number < 0x8000)
+      /* We know that sym is in the .rdata section.  First we get the
+        upper 16 bits of the address.  */
+      if (mips_pic == NO_PIC)
        {
-         macro_build (&imm_expr, "sltiu", "t,r,j", dreg, sreg,
-                      BFD_RELOC_LO16);
-         break;
+         macro_build_lui (&offset_expr, AT);
+         used_at = 1;
+       }
+      else
+       {
+         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
+                      BFD_RELOC_MIPS_GOT16, mips_gp_register);
+         used_at = 1;
        }
-      used_at = 1;
-      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build (NULL, "sltu", "d,v,t", dreg, sreg, 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);
+      /* Now we load the register(s).  */
+      if (HAVE_64BIT_GPRS)
+       {
+         used_at = 1;
+         macro_build (&offset_expr, "ld", "t,o(b)", treg, BFD_RELOC_LO16, AT);
+       }
       else
        {
-         macro_build (NULL, "xor", "d,v,t", dreg, sreg, treg);
-         macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
+         used_at = 1;
+         macro_build (&offset_expr, "lw", "t,o(b)", treg, BFD_RELOC_LO16, AT);
+         if (treg != 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);
+           }
        }
       break;
 
-    case M_SNE_I:
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+    case M_LI_DD:
+      /* Check if we have a constant in IMM_EXPR.  If the FPRs are 64 bits
+         wide, IMM_EXPR is the entire value and the GPRs are known to be 64
+         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)
        {
-         macro_build (NULL, "sltu", "d,v,t", dreg, 0, sreg);
+         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);
+           }
+         else
+           {
+             macro_build (NULL, "mtc1", "t,G", AT, treg + 1);
+             if (offset_expr.X_op == O_absent)
+               macro_build (NULL, "mtc1", "t,G", 0, treg);
+             else
+               {
+                 gas_assert (offset_expr.X_op == O_constant);
+                 load_register (AT, &offset_expr, 0);
+                 macro_build (NULL, "mtc1", "t,G", AT, treg);
+               }
+           }
          break;
        }
-      if (sreg == 0)
-       {
-         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);
-         break;
+
+      gas_assert (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;
+         offset_reloc[0] = BFD_RELOC_MIPS_LITERAL;
+         offset_reloc[1] = BFD_RELOC_UNUSED;
+         offset_reloc[2] = BFD_RELOC_UNUSED;
        }
-      if (CPU_HAS_SEQ (mips_opts.arch)
-         && -512 <= imm_expr.X_add_number
-         && imm_expr.X_add_number < 512)
+      else
        {
-         macro_build (NULL, "snei", "t,r,+Q", dreg, sreg,
-                      (int) imm_expr.X_add_number);
-         break;
-       }
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number >= 0
-         && imm_expr.X_add_number < 0x10000)
+         gas_assert (strcmp (s, RDATA_SECTION_NAME) == 0);
+         used_at = 1;
+         if (mips_pic != NO_PIC)
+           macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
+                        BFD_RELOC_MIPS_GOT16, mips_gp_register);
+         else
+           {
+             /* FIXME: This won't work for a 64 bit address.  */
+             macro_build_lui (&offset_expr, AT);
+           }
+
+         breg = AT;
+         offset_reloc[0] = BFD_RELOC_LO16;
+         offset_reloc[1] = BFD_RELOC_UNUSED;
+         offset_reloc[2] = BFD_RELOC_UNUSED;
+       }
+      align = 8;
+      /* Fall through */
+
+    case M_L_DAB:
+      /*
+       * The MIPS assembler seems to check for X_add_number not
+       * being double aligned and generating:
+       *       lui     at,%hi(foo+1)
+       *       addu    at,at,v1
+       *       addiu   at,at,%lo(foo+1)
+       *       lwc1    f2,0(at)
+       *       lwc1    f3,4(at)
+       * But, the resulting address is the same after relocation so why
+       * generate the extra instruction?
+       */
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      fmt = "T,o(b)";
+      if (CPU_HAS_LDC1_SDC1 (mips_opts.arch))
        {
-         macro_build (&imm_expr, "xori", "t,r,i", dreg, sreg, BFD_RELOC_LO16);
+         s = "ldc1";
+         goto ld_st;
        }
-      else if (imm_expr.X_op == O_constant
-              && imm_expr.X_add_number > -0x8000
-              && imm_expr.X_add_number < 0)
+      s = "lwc1";
+      goto ldd_std;
+
+    case M_S_DAB:
+      gas_assert (!mips_opts.micromips);
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      fmt = "T,o(b)";
+      if (CPU_HAS_LDC1_SDC1 (mips_opts.arch))
        {
-         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);
+         s = "sdc1";
+         goto ld_st;
        }
-      else if (CPU_HAS_SEQ (mips_opts.arch))
+      s = "swc1";
+      goto ldd_std;
+
+    case M_LQ_AB:
+      fmt = "t,o(b)";
+      s = "lq";
+      goto ld;
+
+    case M_SQ_AB:
+      fmt = "t,o(b)";
+      s = "sq";
+      goto ld_st;
+
+    case M_LD_AB:
+      fmt = "t,o(b)";
+      if (HAVE_64BIT_GPRS)
        {
-         used_at = 1;
-         load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-         macro_build (NULL, "sne", "d,v,t", dreg, sreg, AT);
-         break;
+         s = "ld";
+         goto ld;
        }
-      else
+      s = "lw";
+      goto ldd_std;
+
+    case M_SD_AB:
+      fmt = "t,o(b)";
+      if (HAVE_64BIT_GPRS)
        {
-         load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-         macro_build (NULL, "xor", "d,v,t", dreg, sreg, AT);
-         used_at = 1;
+         s = "sd";
+         goto ld_st;
        }
-      macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
-      break;
+      s = "sw";
 
-    case M_SUB_I:
-      s = "addi";
-      s2 = "sub";
-      goto do_subi;
-    case M_SUBU_I:
-      s = "addiu";
-      s2 = "subu";
-      goto do_subi;
-    case M_DSUB_I:
-      dbl = 1;
-      s = "daddi";
-      s2 = "dsub";
-      if (!mips_opts.micromips)
-       goto do_subi;
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number > -0x200
-         && imm_expr.X_add_number <= 0x200)
+    ldd_std:
+      /* Even on a big endian machine $fn comes before $fn+1.  We have
+        to adjust when loading from memory.  We set coproc if we must
+        load $fn+1 first.  */
+      /* Itbl support may require additional care here.  */
+      if (!target_big_endian)
+       coproc = 0;
+
+      if (small_offset_p (0, align, 16))
        {
-         macro_build (NULL, s, "t,r,.", dreg, sreg, -imm_expr.X_add_number);
+         ep = &offset_expr;
+         if (!small_offset_p (4, align, 16))
+           {
+             macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", AT, breg,
+                          -1, offset_reloc[0], offset_reloc[1],
+                          offset_reloc[2]);
+             expr1.X_add_number = 0;
+             ep = &expr1;
+             breg = AT;
+             used_at = 1;
+             offset_reloc[0] = BFD_RELOC_LO16;
+             offset_reloc[1] = BFD_RELOC_UNUSED;
+             offset_reloc[2] = BFD_RELOC_UNUSED;
+           }
+         if (strcmp (s, "lw") == 0 && treg == breg)
+           {
+             ep->X_add_number += 4;
+             macro_build (ep, s, fmt, treg + 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],
+                          offset_reloc[1], offset_reloc[2], breg);
+           }
+         else
+           {
+             macro_build (ep, s, fmt, coproc ? treg + 1 : treg, -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,
+                          offset_reloc[0], offset_reloc[1], offset_reloc[2],
+                          breg);
+           }
          break;
        }
-      goto do_subi_i;
-    case M_DSUBU_I:
-      dbl = 1;
-      s = "daddiu";
-      s2 = "dsubu";
-    do_subi:
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number > -0x8000
-         && imm_expr.X_add_number <= 0x8000)
+
+      if (offset_expr.X_op != O_symbol
+         && offset_expr.X_op != O_constant)
        {
-         imm_expr.X_add_number = -imm_expr.X_add_number;
-         macro_build (&imm_expr, s, "t,r,j", dreg, sreg, BFD_RELOC_LO16);
-         break;
+         as_bad (_("Expression too complex"));
+         offset_expr.X_op = O_constant;
        }
-    do_subi_i:
-      used_at = 1;
-      load_register (AT, &imm_expr, dbl);
-      macro_build (NULL, s2, "d,v,t", dreg, sreg, AT);
-      break;
 
-    case M_TEQ_I:
-      s = "teq";
-      goto trap;
-    case M_TGE_I:
-      s = "tge";
-      goto trap;
-    case M_TGEU_I:
-      s = "tgeu";
-      goto trap;
-    case M_TLT_I:
-      s = "tlt";
-      goto trap;
-    case M_TLTU_I:
-      s = "tltu";
-      goto trap;
-    case M_TNE_I:
-      s = "tne";
-    trap:
-      used_at = 1;
-      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build (NULL, s, "s,t", sreg, AT);
-      break;
+      if (HAVE_32BIT_ADDRESSES
+         && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+       {
+         char value [32];
 
-    case M_TRUNCWS:
-    case M_TRUNCWD:
-      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 */
+         sprintf_vma (value, offset_expr.X_add_number);
+         as_bad (_("Number (0x%s) larger than 32 bits"), value);
+       }
 
-      /*
-       * 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, "nop", "");
-      expr1.X_add_number = 3;
-      macro_build (&expr1, "ori", "t,r,i", AT, treg, 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);
-      macro_build (NULL, "nop", "");
-      end_noreorder ();
-      break;
+      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)
+            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)
+            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)
+            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.  */
+         if (offset_expr.X_op == O_symbol
+             && (valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+             && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+           {
+             relax_start (offset_expr.X_add_symbol);
+             if (breg == 0)
+               {
+                 tempreg = mips_gp_register;
+               }
+             else
+               {
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                              AT, breg, mips_gp_register);
+                 tempreg = AT;
+                 used_at = 1;
+               }
 
-    case M_ULH_A:
-      ab = 1;
-    case M_ULH:
-      s = "lb";
-      s2 = "lbu";
-      off = 1;
-      goto uld_st;
-    case M_ULHU_A:
-      ab = 1;
-    case M_ULHU:
-      s = "lbu";
-      s2 = "lbu";
-      off = 1;
-      goto uld_st;
-    case M_ULW_A:
-      ab = 1;
-    case M_ULW:
-      s = "lwl";
-      s2 = "lwr";
-      offbits = (mips_opts.micromips ? 12 : 16);
-      off = 3;
-      goto uld_st;
-    case M_ULD_A:
-      ab = 1;
-    case M_ULD:
-      s = "ldl";
-      s2 = "ldr";
-      offbits = (mips_opts.micromips ? 12 : 16);
-      off = 7;
-      goto uld_st;
-    case M_USH_A:
-      ab = 1;
-    case M_USH:
-      s = "sb";
-      s2 = "sb";
-      off = 1;
-      ust = 1;
-      goto uld_st;
-    case M_USW_A:
-      ab = 1;
-    case M_USW:
-      s = "swl";
-      s2 = "swr";
-      offbits = (mips_opts.micromips ? 12 : 16);
-      off = 3;
-      ust = 1;
-      goto uld_st;
-    case M_USD_A:
-      ab = 1;
-    case M_USD:
-      s = "sdl";
-      s2 = "sdr";
-      offbits = (mips_opts.micromips ? 12 : 16);
-      off = 7;
-      ust = 1;
+             /* Itbl support may require additional care here.  */
+             macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
+                          BFD_RELOC_GPREL16, tempreg);
+             offset_expr.X_add_number += 4;
 
-    uld_st:
-      if (!ab && offset_expr.X_add_number >= 0x8000 - off)
-       as_bad (_("Operand overflow"));
+             /* Set mips_optimize to 2 to avoid inserting an
+                 undesired nop.  */
+             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,
+                          BFD_RELOC_GPREL16, tempreg);
+             mips_optimize = hold_mips_optimize;
 
-      ep = &offset_expr;
-      expr1.X_add_number = 0;
-      if (ab)
-       {
+             relax_switch ();
+
+             offset_expr.X_add_number -= 4;
+           }
          used_at = 1;
-         tempreg = AT;
-         load_address (tempreg, ep, &used_at);
+         if (offset_high_part (offset_expr.X_add_number, 16)
+             != offset_high_part (offset_expr.X_add_number + 4, 16))
+           {
+             load_address (AT, &offset_expr, &used_at);
+             offset_expr.X_op = O_constant;
+             offset_expr.X_add_number = 0;
+           }
+         else
+           macro_build_lui (&offset_expr, AT);
          if (breg != 0)
-           macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                        tempreg, tempreg, breg);
-         breg = tempreg;
-         tempreg = treg;
-         ep = &expr1;
+           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,
+                      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,
+                      BFD_RELOC_LO16, AT);
+         if (mips_relax.sequence)
+           relax_end ();
        }
-      else if (offbits == 12
-              && (offset_expr.X_op != O_constant
-                  || !IS_SEXT_12BIT_NUM (offset_expr.X_add_number)
-                  || !IS_SEXT_12BIT_NUM (offset_expr.X_add_number + off)))
+      else if (!mips_big_got)
        {
+         /* 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)
+            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)
+            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.  */
          used_at = 1;
-         tempreg = AT;
-         macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", tempreg, breg,
-                      -1, offset_reloc[0], offset_reloc[1], offset_reloc[2]);
-         breg = tempreg;
-         tempreg = treg;
-         ep = &expr1;
+         expr1.X_add_number = offset_expr.X_add_number;
+         if (expr1.X_add_number < -0x8000
+             || expr1.X_add_number >= 0x8000 - 4)
+           as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+         load_got_offset (AT, &offset_expr);
+         load_delay_nop ();
+         if (breg != 0)
+           macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+
+         /* Set mips_optimize to 2 to avoid inserting an undesired
+             nop.  */
+         hold_mips_optimize = mips_optimize;
+         mips_optimize = 2;
+
+         /* Itbl support may require additional care here.  */
+         relax_start (offset_expr.X_add_symbol);
+         macro_build (&expr1, s, fmt, coproc ? treg + 1 : treg,
+                      BFD_RELOC_LO16, AT);
+         expr1.X_add_number += 4;
+         macro_build (&expr1, s, fmt, coproc ? treg : treg + 1,
+                      BFD_RELOC_LO16, AT);
+         relax_switch ();
+         macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
+                      BFD_RELOC_LO16, AT);
+         offset_expr.X_add_number += 4;
+         macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
+                      BFD_RELOC_LO16, AT);
+         relax_end ();
+
+         mips_optimize = hold_mips_optimize;
        }
-      else if (!ust && treg == breg)
+      else if (mips_big_got)
        {
+         int gpdelay;
+
+         /* If this is a reference to an external symbol, we want
+              lui      $at,<sym>               (BFD_RELOC_MIPS_GOT_HI16)
+              addu     $at,$at,$gp
+              lw       $at,<sym>($at)          (BFD_RELOC_MIPS_GOT_LO16)
+              nop
+              <op>     $treg,0($at)
+              <op>     $treg+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)
+            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.  */
          used_at = 1;
-         tempreg = AT;
-       }
-      else
-       tempreg = treg;
+         expr1.X_add_number = offset_expr.X_add_number;
+         offset_expr.X_add_number = 0;
+         if (expr1.X_add_number < -0x8000
+             || expr1.X_add_number >= 0x8000 - 4)
+           as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+         gpdelay = reg_needs_delay (mips_gp_register);
+         relax_start (offset_expr.X_add_symbol);
+         macro_build (&offset_expr, "lui", LUI_FMT,
+                      AT, BFD_RELOC_MIPS_GOT_HI16);
+         macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                      AT, AT, mips_gp_register);
+         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                      AT, BFD_RELOC_MIPS_GOT_LO16, AT);
+         load_delay_nop ();
+         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,
+                      BFD_RELOC_LO16, AT);
+         expr1.X_add_number += 4;
 
-      if (off == 1)
-       goto ulh_sh;
+         /* Set mips_optimize to 2 to avoid inserting an undesired
+             nop.  */
+         hold_mips_optimize = mips_optimize;
+         mips_optimize = 2;
+         /* Itbl support may require additional care here.  */
+         macro_build (&expr1, s, fmt, coproc ? treg : treg + 1,
+                      BFD_RELOC_LO16, AT);
+         mips_optimize = hold_mips_optimize;
+         expr1.X_add_number -= 4;
 
-      if (!target_big_endian)
-       ep->X_add_number += off;
-      if (offbits != 12)
-       macro_build (ep, s, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
-      else
-       macro_build (NULL, s, "t,~(b)",
-                    tempreg, (unsigned long) ep->X_add_number, breg);
+         relax_switch ();
+         offset_expr.X_add_number = expr1.X_add_number;
+         if (gpdelay)
+           macro_build (NULL, "nop", "");
+         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
+                      BFD_RELOC_MIPS_GOT16, mips_gp_register);
+         load_delay_nop ();
+         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,
+                      BFD_RELOC_LO16, AT);
+         offset_expr.X_add_number += 4;
 
-      if (!target_big_endian)
-       ep->X_add_number -= off;
-      else
-       ep->X_add_number += off;
-      if (offbits != 12)
-       macro_build (ep, s2, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
+         /* Set mips_optimize to 2 to avoid inserting an undesired
+             nop.  */
+         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,
+                      BFD_RELOC_LO16, AT);
+         mips_optimize = hold_mips_optimize;
+         relax_end ();
+       }
       else
-       macro_build (NULL, s2, "t,~(b)",
-                    tempreg, (unsigned long) ep->X_add_number, breg);
+       abort ();
 
-      /* If necessary, move the result in tempreg to the final destination.  */
-      if (!ust && treg != tempreg)
-        {
-         /* Protect second load's delay slot.  */
-         load_delay_nop ();
-         move_register (treg, tempreg);
-       }
       break;
+       
+    case M_SAA_AB:
+      s = "saa";
+      offbits = 0;
+      fmt = "t,(b)";
+      goto ld_st;
+    case M_SAAD_AB:
+      s = "saad";
+      offbits = 0;
+      fmt = "t,(b)";
+      goto ld_st;
 
-    ulh_sh:
-      used_at = 1;
-      if (target_big_endian == ust)
-       ep->X_add_number += off;
-      tempreg = ust || ab ? treg : AT;
-      macro_build (ep, s, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
+   /* New code added to support COPZ instructions.
+      This code builds table entries out of the macros in mip_opcodes.
+      R4000 uses interlocks to handle coproc delays.
+      Other chips (like the R3000) require nops to be inserted for delays.
 
-      /* For halfword transfers we need a temporary register to shuffle
-         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 == ab ? treg : AT;
-      if (ust)
-       macro_build (NULL, "srl", SHFT_FMT, tempreg, treg, 8);
+      FIXME: Currently, we require that the user handle delays.
+      In order to fill delay slots for non-interlocked chips,
+      we must have a way to specify delays based on the coprocessor.
+      Eg. 4 cycles if load coproc reg from memory, 1 if in cache, etc.
+      What are the side-effects of the cop instruction?
+      What cache support might we have and what are its effects?
+      Both coprocessor & memory require delays. how long???
+      What registers are read/set/modified?
 
-      if (target_big_endian == ust)
-       ep->X_add_number -= off;
-      else
-       ep->X_add_number += off;
-      macro_build (ep, s2, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
+      If an itbl is provided to interpret cop instructions,
+      this knowledge can be encoded in the itbl spec.  */
 
-      /* For M_USH_A re-retrieve the LSB.  */
-      if (ust && ab)
-       {
-         if (target_big_endian)
-           ep->X_add_number += off;
-         else
-           ep->X_add_number -= off;
-         macro_build (&expr1, "lbu", "t,o(b)", AT, BFD_RELOC_LO16, AT);
-       }
-      /* For ULH and M_USH_A OR the LSB in.  */
-      if (!ust || ab)
-       {
-         tempreg = !ab ? AT : treg;
-         macro_build (NULL, "sll", SHFT_FMT, tempreg, tempreg, 8);
-         macro_build (NULL, "or", "d,v,t", treg, treg, AT);
-       }
+    case M_COP0:
+      s = "c0";
+      goto copz;
+    case M_COP1:
+      s = "c1";
+      goto copz;
+    case M_COP2:
+      s = "c2";
+      goto copz;
+    case M_COP3:
+      s = "c3";
+    copz:
+      gas_assert (!mips_opts.micromips);
+      /* For now we just do C (same as Cz).  The parameter will be
+         stored in insn_opcode by mips_ip.  */
+      macro_build (NULL, s, "C", (int) ip->insn_opcode);
       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);
+    case M_MOVE:
+      move_register (dreg, sreg);
       break;
-    }
-  if (!mips_opts.at && used_at)
-    as_bad (_("Macro used $at after \".set noat\""));
-}
-
-/* Implement macros in mips16 mode.  */
-
-static void
-mips16_macro (struct mips_cl_insn *ip)
-{
-  int mask;
-  int xreg, yreg, zreg, tmp;
-  expressionS expr1;
-  int dbl;
-  const char *s, *s2, *s3;
-
-  mask = ip->insn_mo->mask;
 
-  xreg = MIPS16_EXTRACT_OPERAND (RX, *ip);
-  yreg = MIPS16_EXTRACT_OPERAND (RY, *ip);
-  zreg = MIPS16_EXTRACT_OPERAND (RZ, *ip);
-
-  expr1.X_op = O_constant;
-  expr1.X_op_symbol = NULL;
-  expr1.X_add_symbol = NULL;
-  expr1.X_add_number = 1;
-
-  dbl = 0;
-
-  switch (mask)
-    {
-    default:
-      abort ();
+    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);
+      break;
 
-    case M_DDIV_3:
-      dbl = 1;
-    case M_DIV_3:
-      s = "mflo";
-      goto do_div3;
-    case M_DREM_3:
+    case M_DMUL:
       dbl = 1;
-    case M_REM_3:
-      s = "mfhi";
-    do_div3:
-      start_noreorder ();
-      macro_build (NULL, dbl ? "ddiv" : "div", "0,x,y", xreg, yreg);
-      expr1.X_add_number = 2;
-      macro_build (&expr1, "bnez", "x,p", yreg);
-      macro_build (NULL, "break", "6", 7);
+    case M_MUL:
+      if (mips_opts.arch == CPU_R5900)
+        {
+          macro_build (NULL, dbl ? "dmultu" : "multu", "d,s,t", dreg, sreg, treg);
+        }
+      else
+        {
+      macro_build (NULL, dbl ? "dmultu" : "multu", "s,t", sreg, treg);
+      macro_build (NULL, "mflo", MFHL_FMT, dreg);
+        }
+      break;
 
-      /* FIXME: The normal code checks for of -1 / -0x80000000 here,
-         since that causes an overflow.  We should do that as well,
-         but I don't see how to do the comparisons without a temporary
-         register.  */
-      end_noreorder ();
-      macro_build (NULL, s, "x", zreg);
+    case M_DMUL_I:
+      dbl = 1;
+    case M_MUL_I:
+      /* The MIPS assembler some times generates shifts and adds.  I'm
+        not trying to be that fancy. GCC should do this for us
+        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);
       break;
 
-    case M_DIVU_3:
-      s = "divu";
-      s2 = "mflo";
-      goto do_divu3;
-    case M_REMU_3:
-      s = "divu";
-      s2 = "mfhi";
-      goto do_divu3;
-    case M_DDIVU_3:
-      s = "ddivu";
-      s2 = "mflo";
-      goto do_divu3;
-    case M_DREMU_3:
-      s = "ddivu";
-      s2 = "mfhi";
-    do_divu3:
+    case M_DMULO_I:
+      dbl = 1;
+    case M_MULO_I:
+      imm = 1;
+      goto do_mulo;
+
+    case M_DMULO:
+      dbl = 1;
+    case M_MULO:
+    do_mulo:
       start_noreorder ();
-      macro_build (NULL, s, "0,x,y", xreg, yreg);
-      expr1.X_add_number = 2;
-      macro_build (&expr1, "bnez", "x,p", yreg);
-      macro_build (NULL, "break", "6", 7);
+      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, "mfhi", MFHL_FMT, AT);
+      if (mips_trap)
+       macro_build (NULL, "tne", TRAP_FMT, dreg, 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 (NULL, "nop", "");
+         macro_build (NULL, "break", BRK_FMT, 6);
+         if (mips_opts.micromips)
+           micromips_add_label ();
+       }
       end_noreorder ();
-      macro_build (NULL, s2, "x", zreg);
+      macro_build (NULL, "mflo", MFHL_FMT, dreg);
       break;
 
-    case M_DMUL:
+    case M_DMULOU_I:
       dbl = 1;
-    case M_MUL:
-      macro_build (NULL, dbl ? "dmultu" : "multu", "x,y", xreg, yreg);
-      macro_build (NULL, "mflo", "x", zreg);
-      break;
+    case M_MULOU_I:
+      imm = 1;
+      goto do_mulou;
 
-    case M_DSUBU_I:
+    case M_DMULOU:
       dbl = 1;
-      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);
+    case M_MULOU:
+    do_mulou:
+      start_noreorder ();
+      used_at = 1;
+      if (imm)
+       load_register (AT, &imm_expr, dbl);
+      macro_build (NULL, dbl ? "dmultu" : "multu", "s,t",
+                  sreg, imm ? AT : treg);
+      macro_build (NULL, "mfhi", MFHL_FMT, AT);
+      macro_build (NULL, "mflo", MFHL_FMT, dreg);
+      if (mips_trap)
+       macro_build (NULL, "tne", TRAP_FMT, AT, ZERO, 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", AT, ZERO);
+         macro_build (NULL, "nop", "");
+         macro_build (NULL, "break", BRK_FMT, 6);
+         if (mips_opts.micromips)
+           micromips_add_label ();
+       }
+      end_noreorder ();
       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);
+    case M_DROL:
+      if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+       {
+         if (dreg == sreg)
+           {
+             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);
+         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);
       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);
+    case M_ROL:
+      if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+       {
+         if (dreg == sreg)
+           {
+             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);
+         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);
       break;
 
-    case M_BEQ:
-      s = "cmp";
-      s2 = "bteqz";
-      goto do_branch;
-    case M_BNE:
-      s = "cmp";
-      s2 = "btnez";
-      goto do_branch;
-    case M_BLT:
-      s = "slt";
-      s2 = "btnez";
-      goto do_branch;
-    case M_BLTU:
-      s = "sltu";
-      s2 = "btnez";
-      goto do_branch;
-    case M_BLE:
-      s = "slt";
-      s2 = "bteqz";
-      goto do_reverse_branch;
-    case M_BLEU:
-      s = "sltu";
-      s2 = "bteqz";
-      goto do_reverse_branch;
-    case M_BGE:
-      s = "slt";
-      s2 = "bteqz";
-      goto do_branch;
-    case M_BGEU:
-      s = "sltu";
-      s2 = "bteqz";
-      goto do_branch;
-    case M_BGT:
-      s = "slt";
-      s2 = "btnez";
-      goto do_reverse_branch;
-    case M_BGTU:
-      s = "sltu";
-      s2 = "btnez";
-
-    do_reverse_branch:
-      tmp = xreg;
-      xreg = yreg;
-      yreg = tmp;
+    case M_DROL_I:
+      {
+       unsigned int rot;
+       char *l;
+       char *rr;
 
-    do_branch:
-      macro_build (NULL, s, "x,y", xreg, yreg);
-      macro_build (&offset_expr, s2, "p");
+       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);
+           else
+             macro_build (NULL, "dror", SHFT_FMT, dreg, sreg, rot);
+           break;
+         }
+       if (rot == 0)
+         {
+           macro_build (NULL, "dsrl", SHFT_FMT, dreg, sreg, 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);
+      }
       break;
 
-    case M_BEQ_I:
-      s = "cmpi";
-      s2 = "bteqz";
-      s3 = "x,U";
-      goto do_branch_i;
-    case M_BNE_I:
-      s = "cmpi";
-      s2 = "btnez";
-      s3 = "x,U";
-      goto do_branch_i;
-    case M_BLT_I:
-      s = "slti";
-      s2 = "btnez";
-      s3 = "x,8";
-      goto do_branch_i;
-    case M_BLTU_I:
-      s = "sltiu";
-      s2 = "btnez";
-      s3 = "x,8";
-      goto do_branch_i;
-    case M_BLE_I:
-      s = "slti";
-      s2 = "btnez";
-      s3 = "x,8";
-      goto do_addone_branch_i;
-    case M_BLEU_I:
-      s = "sltiu";
-      s2 = "btnez";
-      s3 = "x,8";
-      goto do_addone_branch_i;
-    case M_BGE_I:
-      s = "slti";
-      s2 = "bteqz";
-      s3 = "x,8";
-      goto do_branch_i;
-    case M_BGEU_I:
-      s = "sltiu";
-      s2 = "bteqz";
-      s3 = "x,8";
-      goto do_branch_i;
-    case M_BGT_I:
-      s = "slti";
-      s2 = "bteqz";
-      s3 = "x,8";
-      goto do_addone_branch_i;
-    case M_BGTU_I:
-      s = "sltiu";
-      s2 = "bteqz";
-      s3 = "x,8";
-
-    do_addone_branch_i:
-      if (imm_expr.X_op != O_constant)
-       as_bad (_("Unsupported large constant"));
-      ++imm_expr.X_add_number;
+    case M_ROL_I:
+      {
+       unsigned int rot;
 
-    do_branch_i:
-      macro_build (&imm_expr, s, s3, xreg);
-      macro_build (&offset_expr, s2, "p");
+       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);
+           break;
+         }
+       if (rot == 0)
+         {
+           macro_build (NULL, "srl", SHFT_FMT, dreg, sreg, 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);
+      }
       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_DROR:
+      if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+       {
+         macro_build (NULL, "drorv", "d,t,s", dreg, sreg, treg);
+         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);
+      break;
 
-/* For consistency checking, verify that all bits are specified either
-   by the match/mask part of the instruction definition, or by the
-   operand list.  */
-static int
-validate_mips_insn (const struct mips_opcode *opc)
-{
-  const char *p = opc->args;
-  char c;
-  unsigned long used_bits = opc->mask;
+    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);
+         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);
+      break;
 
-  if ((used_bits & opc->match) != opc->match)
-    {
-      as_bad (_("internal: bad mips opcode (mask error): %s %s"),
-             opc->name, opc->args);
-      return 0;
-    }
-#define USE_BITS(mask,shift)   (used_bits |= ((mask) << (shift)))
-  while (*p)
-    switch (c = *p++)
+    case M_DROR_I:
       {
-      case ',': break;
-      case '(': break;
-      case ')': break;
-      case '+':
-       switch (c = *p++)
+       unsigned int rot;
+       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))
          {
-         case '1': USE_BITS (OP_MASK_UDI1,     OP_SH_UDI1);    break;
-         case '2': USE_BITS (OP_MASK_UDI2,     OP_SH_UDI2);    break;
-         case '3': USE_BITS (OP_MASK_UDI3,     OP_SH_UDI3);    break;
-         case '4': USE_BITS (OP_MASK_UDI4,     OP_SH_UDI4);    break;
-         case 'A': USE_BITS (OP_MASK_SHAMT,    OP_SH_SHAMT);   break;
-         case 'B': USE_BITS (OP_MASK_INSMSB,   OP_SH_INSMSB);  break;
-         case 'C': USE_BITS (OP_MASK_EXTMSBD,  OP_SH_EXTMSBD); break;
-         case 'D': USE_BITS (OP_MASK_RD,       OP_SH_RD);
-                   USE_BITS (OP_MASK_SEL,      OP_SH_SEL);     break;
-         case 'E': USE_BITS (OP_MASK_SHAMT,    OP_SH_SHAMT);   break;
-         case 'F': USE_BITS (OP_MASK_INSMSB,   OP_SH_INSMSB);  break;
-         case 'G': USE_BITS (OP_MASK_EXTMSBD,  OP_SH_EXTMSBD); break;
-         case 'H': USE_BITS (OP_MASK_EXTMSBD,  OP_SH_EXTMSBD); break;
-         case 'I': break;
-         case 'J': USE_BITS (OP_MASK_CODE10,   OP_SH_CODE10);  break;
-         case 't': USE_BITS (OP_MASK_RT,       OP_SH_RT);      break;
-         case 'T': USE_BITS (OP_MASK_RT,       OP_SH_RT);
-                   USE_BITS (OP_MASK_SEL,      OP_SH_SEL);     break;
-         case 'x': USE_BITS (OP_MASK_BBITIND,  OP_SH_BBITIND); break;
-         case 'X': USE_BITS (OP_MASK_BBITIND,  OP_SH_BBITIND); break;
-         case 'p': USE_BITS (OP_MASK_CINSPOS,  OP_SH_CINSPOS); break;
-         case 'P': USE_BITS (OP_MASK_CINSPOS,  OP_SH_CINSPOS); break;
-         case 'Q': USE_BITS (OP_MASK_SEQI,     OP_SH_SEQI);    break;
-         case 's': USE_BITS (OP_MASK_CINSLM1,  OP_SH_CINSLM1); break;
-         case 'S': USE_BITS (OP_MASK_CINSLM1,  OP_SH_CINSLM1); break;
-         case 'z': USE_BITS (OP_MASK_RZ,       OP_SH_RZ);      break;
-         case 'Z': USE_BITS (OP_MASK_FZ,       OP_SH_FZ);      break;
-         case 'a': USE_BITS (OP_MASK_OFFSET_A, OP_SH_OFFSET_A); break;
-         case 'b': USE_BITS (OP_MASK_OFFSET_B, OP_SH_OFFSET_B); break;
-         case 'c': USE_BITS (OP_MASK_OFFSET_C, OP_SH_OFFSET_C); break;
-         case 'j': USE_BITS (OP_MASK_EVAOFFSET, OP_SH_EVAOFFSET); break;
-
-         default:
-           as_bad (_("internal: bad mips opcode (unknown extension operand type `+%c'): %s %s"),
-                   c, opc->name, opc->args);
-           return 0;
+           if (rot >= 32)
+             macro_build (NULL, "dror32", SHFT_FMT, dreg, sreg, rot - 32);
+           else
+             macro_build (NULL, "dror", SHFT_FMT, dreg, sreg, rot);
+           break;
          }
-       break;
-      case '<': USE_BITS (OP_MASK_SHAMT,       OP_SH_SHAMT);   break;
-      case '>':        USE_BITS (OP_MASK_SHAMT,        OP_SH_SHAMT);   break;
-      case 'A': break;
-      case 'B': USE_BITS (OP_MASK_CODE20,       OP_SH_CODE20);  break;
-      case 'C':        USE_BITS (OP_MASK_COPZ,         OP_SH_COPZ);    break;
-      case 'D':        USE_BITS (OP_MASK_FD,           OP_SH_FD);      break;
-      case 'E':        USE_BITS (OP_MASK_RT,           OP_SH_RT);      break;
-      case 'F': break;
-      case 'G':        USE_BITS (OP_MASK_RD,           OP_SH_RD);      break;
-      case 'H': USE_BITS (OP_MASK_SEL,         OP_SH_SEL);     break;
-      case 'I': break;
-      case 'J': USE_BITS (OP_MASK_CODE19,       OP_SH_CODE19);  break;
-      case 'K':        USE_BITS (OP_MASK_RD,           OP_SH_RD);      break;
-      case 'L': break;
-      case 'M':        USE_BITS (OP_MASK_CCC,          OP_SH_CCC);     break;
-      case 'N':        USE_BITS (OP_MASK_BCC,          OP_SH_BCC);     break;
-      case 'O':        USE_BITS (OP_MASK_ALN,          OP_SH_ALN);     break;
-      case 'Q':        USE_BITS (OP_MASK_VSEL,         OP_SH_VSEL);
-               USE_BITS (OP_MASK_FT,           OP_SH_FT);      break;
-      case 'R':        USE_BITS (OP_MASK_FR,           OP_SH_FR);      break;
-      case 'S':        USE_BITS (OP_MASK_FS,           OP_SH_FS);      break;
-      case 'T':        USE_BITS (OP_MASK_FT,           OP_SH_FT);      break;
-      case 'V':        USE_BITS (OP_MASK_FS,           OP_SH_FS);      break;
-      case 'W':        USE_BITS (OP_MASK_FT,           OP_SH_FT);      break;
-      case 'X':        USE_BITS (OP_MASK_FD,           OP_SH_FD);      break;
-      case 'Y':        USE_BITS (OP_MASK_FS,           OP_SH_FS);      break;
-      case 'Z':        USE_BITS (OP_MASK_FT,           OP_SH_FT);      break;
-      case 'a':        USE_BITS (OP_MASK_TARGET,       OP_SH_TARGET);  break;
-      case 'b':        USE_BITS (OP_MASK_RS,           OP_SH_RS);      break;
-      case 'c':        USE_BITS (OP_MASK_CODE,         OP_SH_CODE);    break;
-      case 'd':        USE_BITS (OP_MASK_RD,           OP_SH_RD);      break;
-      case 'f': break;
-      case 'h':        USE_BITS (OP_MASK_PREFX,        OP_SH_PREFX);   break;
-      case 'i':        USE_BITS (OP_MASK_IMMEDIATE,    OP_SH_IMMEDIATE); break;
-      case 'j':        USE_BITS (OP_MASK_DELTA,        OP_SH_DELTA);   break;
-      case 'k':        USE_BITS (OP_MASK_CACHE,        OP_SH_CACHE);   break;
-      case 'l': break;
-      case 'o': USE_BITS (OP_MASK_DELTA,       OP_SH_DELTA);   break;
-      case 'p':        USE_BITS (OP_MASK_DELTA,        OP_SH_DELTA);   break;
-      case 'q':        USE_BITS (OP_MASK_CODE2,        OP_SH_CODE2);   break;
-      case 'r': USE_BITS (OP_MASK_RS,          OP_SH_RS);      break;
-      case 's':        USE_BITS (OP_MASK_RS,           OP_SH_RS);      break;
-      case 't':        USE_BITS (OP_MASK_RT,           OP_SH_RT);      break;
-      case 'u':        USE_BITS (OP_MASK_IMMEDIATE,    OP_SH_IMMEDIATE); break;
-      case 'v':        USE_BITS (OP_MASK_RS,           OP_SH_RS);      break;
-      case 'w':        USE_BITS (OP_MASK_RT,           OP_SH_RT);      break;
-      case 'x': break;
-      case 'z': break;
-      case 'P': USE_BITS (OP_MASK_PERFREG,     OP_SH_PERFREG); break;
-      case 'U': USE_BITS (OP_MASK_RD,           OP_SH_RD);
-               USE_BITS (OP_MASK_RT,           OP_SH_RT);      break;
-      case 'e': USE_BITS (OP_MASK_VECBYTE,     OP_SH_VECBYTE); break;
-      case '%': USE_BITS (OP_MASK_VECALIGN,    OP_SH_VECALIGN); break;
-      case '[': break;
-      case ']': break;
-      case '1':        USE_BITS (OP_MASK_SHAMT,        OP_SH_SHAMT);   break;
-      case '2': USE_BITS (OP_MASK_BP,          OP_SH_BP);      break;
-      case '3': USE_BITS (OP_MASK_SA3,         OP_SH_SA3);     break;
-      case '4': USE_BITS (OP_MASK_SA4,         OP_SH_SA4);     break;
-      case '5': USE_BITS (OP_MASK_IMM8,        OP_SH_IMM8);    break;
-      case '6': USE_BITS (OP_MASK_RS,          OP_SH_RS);      break;
-      case '7': USE_BITS (OP_MASK_DSPACC,      OP_SH_DSPACC);  break;
-      case '8': USE_BITS (OP_MASK_WRDSP,       OP_SH_WRDSP);   break;
-      case '9': USE_BITS (OP_MASK_DSPACC_S,    OP_SH_DSPACC_S);break;
-      case '0': USE_BITS (OP_MASK_DSPSFT,      OP_SH_DSPSFT);  break;
-      case '\'': USE_BITS (OP_MASK_RDDSP,      OP_SH_RDDSP);   break;
-      case ':': USE_BITS (OP_MASK_DSPSFT_7,    OP_SH_DSPSFT_7);break;
-      case '@': USE_BITS (OP_MASK_IMM10,       OP_SH_IMM10);   break;
-      case '!': USE_BITS (OP_MASK_MT_U,                OP_SH_MT_U);    break;
-      case '$': USE_BITS (OP_MASK_MT_H,                OP_SH_MT_H);    break;
-      case '*': USE_BITS (OP_MASK_MTACC_T,     OP_SH_MTACC_T); break;
-      case '&': USE_BITS (OP_MASK_MTACC_D,     OP_SH_MTACC_D); break;
-      case '\\': USE_BITS (OP_MASK_3BITPOS,    OP_SH_3BITPOS); break;
-      case '~': USE_BITS (OP_MASK_OFFSET12,    OP_SH_OFFSET12); break;
-      case 'g': USE_BITS (OP_MASK_RD,          OP_SH_RD);      break;
-      default:
-       as_bad (_("internal: bad mips opcode (unknown operand type `%c'): %s %s"),
-               c, opc->name, opc->args);
-       return 0;
+       if (rot == 0)
+         {
+           macro_build (NULL, "dsrl", SHFT_FMT, dreg, sreg, 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);
       }
-#undef USE_BITS
-  if (used_bits != 0xffffffff)
-    {
-      as_bad (_("internal: bad mips opcode (bits 0x%lx undefined): %s %s"),
-             ~used_bits & 0xffffffff, opc->name, opc->args);
-      return 0;
-    }
-  return 1;
-}
-
-/* For consistency checking, verify that the length implied matches the
-   major opcode and that all bits are specified either by the match/mask
-   part of the instruction definition, or by the operand list.  */
-
-static int
-validate_micromips_insn (const struct mips_opcode *opc)
-{
-  unsigned long match = opc->match;
-  unsigned long mask = opc->mask;
-  const char *p = opc->args;
-  unsigned long insn_bits;
-  unsigned long used_bits;
-  unsigned long major;
-  unsigned int length;
-  char e;
-  char c;
-
-  if ((mask & match) != match)
-    {
-      as_bad (_("Internal error: bad microMIPS opcode (mask error): %s %s"),
-             opc->name, opc->args);
-      return 0;
-    }
-  length = micromips_insn_length (opc);
-  if (length != 2 && length != 4)
-    {
-      as_bad (_("Internal error: bad microMIPS opcode (incorrect length: %u): "
-               "%s %s"), length, opc->name, opc->args);
-      return 0;
-    }
-  major = match >> (10 + 8 * (length - 2));
-  if ((length == 2 && (major & 7) != 1 && (major & 6) != 2)
-      || (length == 4 && (major & 7) != 0 && (major & 4) != 4))
-    {
-      as_bad (_("Internal error: bad microMIPS opcode "
-               "(opcode/length mismatch): %s %s"), opc->name, opc->args);
-      return 0;
-    }
+      break;
 
-  /* Shift piecewise to avoid an overflow where unsigned long is 32-bit.  */
-  insn_bits = 1 << 4 * length;
-  insn_bits <<= 4 * length;
-  insn_bits -= 1;
-  used_bits = mask;
-#define USE_BITS(field) \
-  (used_bits |= MICROMIPSOP_MASK_##field << MICROMIPSOP_SH_##field)
-  while (*p)
-    switch (c = *p++)
+    case M_ROR_I:
       {
-      case ',': break;
-      case '(': break;
-      case ')': break;
-      case '+':
-       e = c;
-       switch (c = *p++)
+       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))
          {
-         case 'A': USE_BITS (EXTLSB);  break;
-         case 'B': USE_BITS (INSMSB);  break;
-         case 'C': USE_BITS (EXTMSBD); break;
-         case 'D': USE_BITS (RS);      USE_BITS (SEL); break;
-         case 'E': USE_BITS (EXTLSB);  break;
-         case 'F': USE_BITS (INSMSB);  break;
-         case 'G': USE_BITS (EXTMSBD); break;
-         case 'H': USE_BITS (EXTMSBD); break;
-         case 'j': USE_BITS (EVAOFFSET);       break;
-         default:
-           as_bad (_("Internal error: bad mips opcode "
-                     "(unknown extension operand type `%c%c'): %s %s"),
-                   e, c, opc->name, opc->args);
-           return 0;
+           macro_build (NULL, "ror", SHFT_FMT, dreg, sreg, rot);
+           break;
          }
-       break;
-      case 'm':
-       e = c;
-       switch (c = *p++)
+       if (rot == 0)
          {
-         case 'A': USE_BITS (IMMA);    break;
-         case 'B': USE_BITS (IMMB);    break;
-         case 'C': USE_BITS (IMMC);    break;
-         case 'D': USE_BITS (IMMD);    break;
-         case 'E': USE_BITS (IMME);    break;
-         case 'F': USE_BITS (IMMF);    break;
-         case 'G': USE_BITS (IMMG);    break;
-         case 'H': USE_BITS (IMMH);    break;
-         case 'I': USE_BITS (IMMI);    break;
-         case 'J': USE_BITS (IMMJ);    break;
-         case 'L': USE_BITS (IMML);    break;
-         case 'M': USE_BITS (IMMM);    break;
-         case 'N': USE_BITS (IMMN);    break;
-         case 'O': USE_BITS (IMMO);    break;
-         case 'P': USE_BITS (IMMP);    break;
-         case 'Q': USE_BITS (IMMQ);    break;
-         case 'U': USE_BITS (IMMU);    break;
-         case 'W': USE_BITS (IMMW);    break;
-         case 'X': USE_BITS (IMMX);    break;
-         case 'Y': USE_BITS (IMMY);    break;
-         case 'Z': break;
-         case 'a': break;
-         case 'b': USE_BITS (MB);      break;
-         case 'c': USE_BITS (MC);      break;
-         case 'd': USE_BITS (MD);      break;
-         case 'e': USE_BITS (ME);      break;
-         case 'f': USE_BITS (MF);      break;
-         case 'g': USE_BITS (MG);      break;
-         case 'h': USE_BITS (MH);      break;
-         case 'i': USE_BITS (MI);      break;
-         case 'j': USE_BITS (MJ);      break;
-         case 'l': USE_BITS (ML);      break;
-         case 'm': USE_BITS (MM);      break;
-         case 'n': USE_BITS (MN);      break;
-         case 'p': USE_BITS (MP);      break;
-         case 'q': USE_BITS (MQ);      break;
-         case 'r': break;
-         case 's': break;
-         case 't': break;
-         case 'x': break;
-         case 'y': break;
-         case 'z': break;
-         default:
-           as_bad (_("Internal error: bad mips opcode "
-                     "(unknown extension operand type `%c%c'): %s %s"),
-                   e, c, opc->name, opc->args);
-           return 0;
+           macro_build (NULL, "srl", SHFT_FMT, dreg, sreg, 0);
+           break;
          }
-       break;
-      case '.': USE_BITS (OFFSET10);   break;
-      case '1': USE_BITS (STYPE);      break;
-      case '2': USE_BITS (BP);         break;
-      case '3': USE_BITS (SA3);                break;
-      case '4': USE_BITS (SA4);                break;
-      case '5': USE_BITS (IMM8);       break;
-      case '6': USE_BITS (RS);         break;
-      case '7': USE_BITS (DSPACC);     break;
-      case '8': USE_BITS (WRDSP);      break;
-      case '0': USE_BITS (DSPSFT);     break;
-      case '<': USE_BITS (SHAMT);      break;
-      case '>': USE_BITS (SHAMT);      break;
-      case '@': USE_BITS (IMM10);      break;
-      case 'B': USE_BITS (CODE10);     break;
-      case 'C': USE_BITS (COPZ);       break;
-      case 'D': USE_BITS (FD);         break;
-      case 'E': USE_BITS (RT);         break;
-      case 'G': USE_BITS (RS);         break;
-      case 'H': USE_BITS (SEL);                break;
-      case 'K': USE_BITS (RS);         break;
-      case 'M': USE_BITS (CCC);                break;
-      case 'N': USE_BITS (BCC);                break;
-      case 'R': USE_BITS (FR);         break;
-      case 'S': USE_BITS (FS);         break;
-      case 'T': USE_BITS (FT);         break;
-      case 'V': USE_BITS (FS);         break;
-      case '\\': USE_BITS (3BITPOS);   break;
-      case '^': USE_BITS (RD);         break;
-      case 'a': USE_BITS (TARGET);     break;
-      case 'b': USE_BITS (RS);         break;
-      case 'c': USE_BITS (CODE);       break;
-      case 'd': USE_BITS (RD);         break;
-      case 'h': USE_BITS (PREFX);      break;
-      case 'i': USE_BITS (IMMEDIATE);  break;
-      case 'j': USE_BITS (DELTA);      break;
-      case 'k': USE_BITS (CACHE);      break;
-      case 'n': USE_BITS (RT);         break;
-      case 'o': USE_BITS (DELTA);      break;
-      case 'p': USE_BITS (DELTA);      break;
-      case 'q': USE_BITS (CODE2);      break;
-      case 'r': USE_BITS (RS);         break;
-      case 's': USE_BITS (RS);         break;
-      case 't': USE_BITS (RT);         break;
-      case 'u': USE_BITS (IMMEDIATE);  break;
-      case 'v': USE_BITS (RS);         break;
-      case 'w': USE_BITS (RT);         break;
-      case 'y': USE_BITS (RS3);                break;
-      case 'z': break;
-      case '|': USE_BITS (TRAP);       break;
-      case '~': USE_BITS (OFFSET12);   break;
-      default:
-       as_bad (_("Internal error: bad microMIPS opcode "
-                 "(unknown operand type `%c'): %s %s"),
-               c, opc->name, opc->args);
-       return 0;
+       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);
       }
-#undef USE_BITS
-  if (used_bits != insn_bits)
-    {
-      if (~used_bits & insn_bits)
-       as_bad (_("Internal error: bad microMIPS opcode "
-                 "(bits 0x%lx undefined): %s %s"),
-               ~used_bits & insn_bits, opc->name, opc->args);
-      if (used_bits & ~insn_bits)
-       as_bad (_("Internal error: bad microMIPS opcode "
-                 "(bits 0x%lx defined): %s %s"),
-               used_bits & ~insn_bits, opc->name, opc->args);
-      return 0;
-    }
-  return 1;
-}
-
-/* UDI immediates.  */
-struct mips_immed {
-  char         type;
-  unsigned int shift;
-  unsigned long        mask;
-  const char * desc;
-};
-
-static const struct mips_immed mips_immed[] = {
-  { '1',       OP_SH_UDI1,     OP_MASK_UDI1,           0},
-  { '2',       OP_SH_UDI2,     OP_MASK_UDI2,           0},
-  { '3',       OP_SH_UDI3,     OP_MASK_UDI3,           0},
-  { '4',       OP_SH_UDI4,     OP_MASK_UDI4,           0},
-  { 0,0,0,0 }
-};
-
-/* Check whether an odd floating-point register is allowed.  */
-static int
-mips_oddfpreg_ok (const struct mips_opcode *insn, int argnum)
-{
-  const char *s = insn->name;
+      break;
 
-  if (insn->pinfo == INSN_MACRO)
-    /* Let a macro pass, we'll catch it later when it is expanded.  */
-    return 1;
+    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);
+      else
+       {
+         macro_build (NULL, "xor", "d,v,t", dreg, sreg, treg);
+         macro_build (&expr1, "sltiu", "t,r,j", dreg, dreg, BFD_RELOC_LO16);
+       }
+      break;
 
-  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 M_SEQ_I:
+      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
        {
-       case FP_S:
-       case 0:
-         return 1;     /* both single precision - ok */
-       case FP_D:
-         return 0;     /* both double precision - fail */
-       default:
+         macro_build (&expr1, "sltiu", "t,r,j", dreg, sreg, BFD_RELOC_LO16);
          break;
        }
+      if (sreg == 0)
+       {
+         as_warn (_("Instruction %s: result is always false"),
+                  ip->insn_mo->name);
+         move_register (dreg, 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,
+                      (int) imm_expr.X_add_number);
+         break;
+       }
+      if (imm_expr.X_op == O_constant
+         && 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
+              && 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);
+       }
+      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);
+         break;
+       }
+      else
+       {
+         load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+         macro_build (NULL, "xor", "d,v,t", dreg, sreg, AT);
+         used_at = 1;
+       }
+      macro_build (&expr1, "sltiu", "t,r,j", dreg, dreg, BFD_RELOC_LO16);
+      break;
 
-      /* Cvt.w.x and cvt.x.w allow an odd register for a 'w' or 's' operand.  */
-      s = strchr (insn->name, '.');
-      if (argnum == 2)
-       s = s != NULL ? strchr (s + 1, '.') : NULL;
-      return (s != NULL && (s[1] == 'w' || s[1] == 's'));
-    } 
-
-  /* Single-precision coprocessor loads and moves are OK too.  */
-  if ((insn->pinfo & FP_S)
-      && (insn->pinfo & (INSN_COPROC_MEMORY_DELAY | INSN_STORE_MEMORY
-                        | INSN_LOAD_COPROC_DELAY | INSN_COPROC_MOVE_DELAY)))
-    return 1;
+    case M_SGE:                /* sreg >= treg <==> not (sreg < treg) */
+      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);
+      break;
 
-  return 0;
-}
+    case M_SGE_I:              /* sreg >= I <==> not (sreg < I) */
+    case M_SGEU_I:
+      if (imm_expr.X_op == O_constant
+         && 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);
+       }
+      else
+       {
+         load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+         macro_build (NULL, mask == M_SGE_I ? "slt" : "sltu", "d,v,t",
+                      dreg, sreg, AT);
+         used_at = 1;
+       }
+      macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
+      break;
 
-/* Check if EXPR is a constant between MIN (inclusive) and MAX (exclusive)
-   taking bits from BIT up.  */
-static int
-expr_const_in_range (expressionS *ep, offsetT min, offsetT max, int bit)
-{
-  return (ep->X_op == O_constant
-         && (ep->X_add_number & ((1 << bit) - 1)) == 0
-         && ep->X_add_number >= min << bit
-         && ep->X_add_number < max << bit);
-}
+    case M_SGT:                /* sreg > treg  <==>  treg < sreg */
+      s = "slt";
+      goto sgt;
+    case M_SGTU:
+      s = "sltu";
+    sgt:
+      macro_build (NULL, s, "d,v,t", dreg, treg, sreg);
+      break;
 
-/* This routine assembles an instruction into its binary format.  As a
-   side effect, it sets one of the global variables imm_reloc or
-   offset_reloc to the type of relocation to do if one of the operands
-   is an address expression.  */
+    case M_SGT_I:              /* sreg > I  <==>  I < sreg */
+      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);
+      break;
 
-static void
-mips_ip (char *str, struct mips_cl_insn *ip)
-{
-  bfd_boolean wrong_delay_slot_insns = FALSE;
-  bfd_boolean need_delay_slot_ok = TRUE;
-  struct mips_opcode *firstinsn = NULL;
-  const struct mips_opcode *past;
-  struct hash_control *hash;
-  char *s;
-  const char *args;
-  char c = 0;
-  struct mips_opcode *insn;
-  char *argsStart;
-  unsigned int regno;
-  unsigned int lastregno;
-  unsigned int destregno = 0;
-  unsigned int lastpos = 0;
-  unsigned int limlo, limhi;
-  int sizelo;
-  char *s_reset;
-  offsetT min_range, max_range;
-  long opend;
-  char *name;
-  int argnum;
-  unsigned int rtype;
-  char *dot;
-  long end;
+    case M_SLE:        /* sreg <= treg  <==>  treg >= sreg  <==>  not (treg < sreg) */
+      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);
+      break;
 
-  insn_error = NULL;
+    case M_SLE_I:      /* sreg <= I <==> I >= sreg <==> not (I < sreg) */
+      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);
+      break;
 
-  if (mips_opts.micromips)
-    {
-      hash = micromips_op_hash;
-      past = &micromips_opcodes[bfd_micromips_num_opcodes];
-    }
-  else
-    {
-      hash = op_hash;
-      past = &mips_opcodes[NUMOPCODES];
-    }
-  forced_insn_length = 0;
-  insn = NULL;
+    case M_SLT_I:
+      if (imm_expr.X_op == O_constant
+         && 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);
+         break;
+       }
+      used_at = 1;
+      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+      macro_build (NULL, "slt", "d,v,t", dreg, sreg, AT);
+      break;
 
-  /* 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;
+    case M_SLTU_I:
+      if (imm_expr.X_op == O_constant
+         && imm_expr.X_add_number >= -0x8000
+         && imm_expr.X_add_number < 0x8000)
+       {
+         macro_build (&imm_expr, "sltiu", "t,r,j", dreg, sreg,
+                      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);
+      break;
 
-  /* Make a copy of the instruction so that we can fiddle with it.  */
-  name = alloca (end + 1);
-  memcpy (name, str, end);
-  name[end] = '\0';
+    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);
+      else
+       {
+         macro_build (NULL, "xor", "d,v,t", dreg, sreg, treg);
+         macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
+       }
+      break;
 
-  for (;;)
-    {
-      insn = (struct mips_opcode *) hash_find (hash, name);
+    case M_SNE_I:
+      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+       {
+         macro_build (NULL, "sltu", "d,v,t", dreg, 0, sreg);
+         break;
+       }
+      if (sreg == 0)
+       {
+         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);
+         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,
+                      (int) imm_expr.X_add_number);
+         break;
+       }
+      if (imm_expr.X_op == O_constant
+         && 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
+              && 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);
+       }
+      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);
+         break;
+       }
+      else
+       {
+         load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+         macro_build (NULL, "xor", "d,v,t", dreg, sreg, AT);
+         used_at = 1;
+       }
+      macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
+      break;
 
-      if (insn != NULL || !mips_opts.micromips)
-       break;
-      if (forced_insn_length)
-       break;
+    case M_SUB_I:
+      s = "addi";
+      s2 = "sub";
+      goto do_subi;
+    case M_SUBU_I:
+      s = "addiu";
+      s2 = "subu";
+      goto do_subi;
+    case M_DSUB_I:
+      dbl = 1;
+      s = "daddi";
+      s2 = "dsub";
+      if (!mips_opts.micromips)
+       goto do_subi;
+      if (imm_expr.X_op == O_constant
+         && 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);
+         break;
+       }
+      goto do_subi_i;
+    case M_DSUBU_I:
+      dbl = 1;
+      s = "daddiu";
+      s2 = "dsubu";
+    do_subi:
+      if (imm_expr.X_op == O_constant
+         && 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);
+         break;
+       }
+    do_subi_i:
+      used_at = 1;
+      load_register (AT, &imm_expr, dbl);
+      macro_build (NULL, s2, "d,v,t", dreg, sreg, AT);
+      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)
-    {
-      insn_error = _("Unrecognized opcode");
-      return;
-    }
+    case M_TEQ_I:
+      s = "teq";
+      goto trap;
+    case M_TGE_I:
+      s = "tge";
+      goto trap;
+    case M_TGEU_I:
+      s = "tgeu";
+      goto trap;
+    case M_TLT_I:
+      s = "tlt";
+      goto trap;
+    case M_TLTU_I:
+      s = "tltu";
+      goto trap;
+    case M_TNE_I:
+      s = "tne";
+    trap:
+      used_at = 1;
+      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+      macro_build (NULL, s, "s,t", sreg, AT);
+      break;
 
-  /* 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.  */
-  argsStart = s = str + end;
-  for (;;)
-    {
-      bfd_boolean delay_slot_ok;
-      bfd_boolean size_ok;
-      bfd_boolean ok;
+    case M_TRUNCWS:
+    case M_TRUNCWD:
+      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 */
 
-      gas_assert (strcmp (insn->name, name) == 0);
+      /*
+       * 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, "nop", "");
+      expr1.X_add_number = 3;
+      macro_build (&expr1, "ori", "t,r,i", AT, treg, 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);
+      macro_build (NULL, "nop", "");
+      end_noreorder ();
+      break;
+
+    case M_ULH_AB:
+      s = "lb";
+      s2 = "lbu";
+      off = 1;
+      goto uld_st;
+    case M_ULHU_AB:
+      s = "lbu";
+      s2 = "lbu";
+      off = 1;
+      goto uld_st;
+    case M_ULW_AB:
+      s = "lwl";
+      s2 = "lwr";
+      offbits = (mips_opts.micromips ? 12 : 16);
+      off = 3;
+      goto uld_st;
+    case M_ULD_AB:
+      s = "ldl";
+      s2 = "ldr";
+      offbits = (mips_opts.micromips ? 12 : 16);
+      off = 7;
+      goto uld_st;
+    case M_USH_AB:
+      s = "sb";
+      s2 = "sb";
+      off = 1;
+      ust = 1;
+      goto uld_st;
+    case M_USW_AB:
+      s = "swl";
+      s2 = "swr";
+      offbits = (mips_opts.micromips ? 12 : 16);
+      off = 3;
+      ust = 1;
+      goto uld_st;
+    case M_USD_AB:
+      s = "sdl";
+      s2 = "sdr";
+      offbits = (mips_opts.micromips ? 12 : 16);
+      off = 7;
+      ust = 1;
 
-      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;
-       }
-      if (!ok || !size_ok || delay_slot_ok != need_delay_slot_ok)
+    uld_st:
+      large_offset = !small_offset_p (off, align, offbits);
+      ep = &offset_expr;
+      expr1.X_add_number = 0;
+      if (large_offset)
        {
-         static char buf[256];
-
-         if (insn + 1 < past && strcmp (insn->name, insn[1].name) == 0)
-           {
-             ++insn;
-             continue;
-           }
-         if (wrong_delay_slot_insns && need_delay_slot_ok)
+         used_at = 1;
+         tempreg = AT;
+         if (small_offset_p (0, align, 16))
+           macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", tempreg, breg, -1,
+                        offset_reloc[0], offset_reloc[1], offset_reloc[2]);
+         else
            {
-             gas_assert (firstinsn);
-             need_delay_slot_ok = FALSE;
-             past = insn + 1;
-             insn = firstinsn;
-             continue;
+             load_address (tempreg, ep, &used_at);
+             if (breg != 0)
+               macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                            tempreg, tempreg, breg);
            }
-
-         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
-           sprintf (buf, _("Unrecognized %u-bit version of microMIPS opcode"),
-                    8 * forced_insn_length);
-         insn_error = buf;
-
-         return;
+         offset_reloc[0] = BFD_RELOC_LO16;
+         offset_reloc[1] = BFD_RELOC_UNUSED;
+         offset_reloc[2] = BFD_RELOC_UNUSED;
+         breg = tempreg;
+         tempreg = treg;
+         ep = &expr1;
        }
-
-      create_insn (ip, insn);
-      insn_error = NULL;
-      argnum = 1;
-      lastregno = 0xffffffff;
-      for (args = insn->args;; ++args)
+      else if (!ust && treg == breg)
        {
-         int is_mdmx;
-
-         s += strspn (s, " \t");
-         is_mdmx = 0;
-         switch (*args)
-           {
-           case '\0':          /* end of args */
-             if (*s == '\0')
-               return;
-             break;
-
-           case '2':
-             /* DSP 2-bit unsigned immediate in bit 11 (for standard MIPS
-                code) or 14 (for microMIPS code).  */
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if ((unsigned long) imm_expr.X_add_number != 1
-                 && (unsigned long) imm_expr.X_add_number != 3)
-               {
-                 as_bad (_("BALIGN immediate not 1 or 3 (%lu)"),
-                         (unsigned long) imm_expr.X_add_number);
-               }
-             INSERT_OPERAND (mips_opts.micromips,
-                             BP, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
-
-           case '3':
-             /* DSP 3-bit unsigned immediate in bit 13 (for standard MIPS
-                code) or 21 (for microMIPS code).  */
-             {
-               unsigned long mask = (mips_opts.micromips
-                                     ? MICROMIPSOP_MASK_SA3 : OP_MASK_SA3);
-
-               my_getExpression (&imm_expr, s);
-               check_absolute_expr (ip, &imm_expr);
-               if ((unsigned long) imm_expr.X_add_number > mask)
-                 as_bad (_("DSP immediate not in range 0..%lu (%lu)"),
-                         mask, (unsigned long) imm_expr.X_add_number);
-               INSERT_OPERAND (mips_opts.micromips,
-                               SA3, *ip, imm_expr.X_add_number);
-               imm_expr.X_op = O_absent;
-               s = expr_end;
-             }
-             continue;
-
-           case '4':
-             /* DSP 4-bit unsigned immediate in bit 12 (for standard MIPS
-                code) or 21 (for microMIPS code).  */
-             {
-               unsigned long mask = (mips_opts.micromips
-                                     ? MICROMIPSOP_MASK_SA4 : OP_MASK_SA4);
-
-               my_getExpression (&imm_expr, s);
-               check_absolute_expr (ip, &imm_expr);
-               if ((unsigned long) imm_expr.X_add_number > mask)
-                 as_bad (_("DSP immediate not in range 0..%lu (%lu)"),
-                         mask, (unsigned long) imm_expr.X_add_number);
-               INSERT_OPERAND (mips_opts.micromips,
-                               SA4, *ip, imm_expr.X_add_number);
-               imm_expr.X_op = O_absent;
-               s = expr_end;
-             }
-             continue;
-
-           case '5':
-             /* DSP 8-bit unsigned immediate in bit 13 (for standard MIPS
-                code) or 16 (for microMIPS code).  */
-             {
-               unsigned long mask = (mips_opts.micromips
-                                     ? MICROMIPSOP_MASK_IMM8 : OP_MASK_IMM8);
-
-               my_getExpression (&imm_expr, s);
-               check_absolute_expr (ip, &imm_expr);
-               if ((unsigned long) imm_expr.X_add_number > mask)
-                 as_bad (_("DSP immediate not in range 0..%lu (%lu)"),
-                         mask, (unsigned long) imm_expr.X_add_number);
-               INSERT_OPERAND (mips_opts.micromips,
-                               IMM8, *ip, imm_expr.X_add_number);
-               imm_expr.X_op = O_absent;
-               s = expr_end;
-             }
-             continue;
-
-           case '6':
-             /* DSP 5-bit unsigned immediate in bit 16 (for standard MIPS
-                code) or 21 (for microMIPS code).  */
-             {
-               unsigned long mask = (mips_opts.micromips
-                                     ? MICROMIPSOP_MASK_RS : OP_MASK_RS);
-
-               my_getExpression (&imm_expr, s);
-               check_absolute_expr (ip, &imm_expr);
-               if ((unsigned long) imm_expr.X_add_number > mask)
-                 as_bad (_("DSP immediate not in range 0..%lu (%lu)"),
-                         mask, (unsigned long) imm_expr.X_add_number);
-               INSERT_OPERAND (mips_opts.micromips,
-                               RS, *ip, imm_expr.X_add_number);
-               imm_expr.X_op = O_absent;
-               s = expr_end;
-             }
-             continue;
-
-           case '7': /* Four DSP accumulators in bits 11,12.  */
-             if (s[0] == '$' && s[1] == 'a' && s[2] == 'c'
-                 && s[3] >= '0' && s[3] <= '3')
-               {
-                 regno = s[3] - '0';
-                 s += 4;
-                 INSERT_OPERAND (mips_opts.micromips, DSPACC, *ip, regno);
-                 continue;
-               }
-             else
-               as_bad (_("Invalid dsp acc register"));
-             break;
-
-           case '8':
-             /* DSP 6-bit unsigned immediate in bit 11 (for standard MIPS
-                code) or 14 (for microMIPS code).  */
-             {
-               unsigned long mask = (mips_opts.micromips
-                                     ? MICROMIPSOP_MASK_WRDSP
-                                     : OP_MASK_WRDSP);
-
-               my_getExpression (&imm_expr, s);
-               check_absolute_expr (ip, &imm_expr);
-               if ((unsigned long) imm_expr.X_add_number > mask)
-                 as_bad (_("DSP immediate not in range 0..%lu (%lu)"),
-                         mask, (unsigned long) imm_expr.X_add_number);
-               INSERT_OPERAND (mips_opts.micromips,
-                               WRDSP, *ip, imm_expr.X_add_number);
-               imm_expr.X_op = O_absent;
-               s = expr_end;
-             }
-             continue;
-
-           case '9': /* Four DSP accumulators in bits 21,22.  */
-             gas_assert (!mips_opts.micromips);
-             if (s[0] == '$' && s[1] == 'a' && s[2] == 'c'
-                 && s[3] >= '0' && s[3] <= '3')
-               {
-                 regno = s[3] - '0';
-                 s += 4;
-                 INSERT_OPERAND (0, DSPACC_S, *ip, regno);
-                 continue;
-               }
-             else
-               as_bad (_("Invalid dsp acc register"));
-             break;
-
-           case '0':
-             /* DSP 6-bit signed immediate in bit 16 (for standard MIPS
-                code) or 20 (for microMIPS code).  */
-             {
-               long mask = (mips_opts.micromips
-                            ? MICROMIPSOP_MASK_DSPSFT : OP_MASK_DSPSFT);
-
-               my_getExpression (&imm_expr, s);
-               check_absolute_expr (ip, &imm_expr);
-               min_range = -((mask + 1) >> 1);
-               max_range = ((mask + 1) >> 1) - 1;
-               if (imm_expr.X_add_number < min_range
-                   || imm_expr.X_add_number > max_range)
-                 as_bad (_("DSP immediate not in range %ld..%ld (%ld)"),
-                         (long) min_range, (long) max_range,
-                         (long) imm_expr.X_add_number);
-               INSERT_OPERAND (mips_opts.micromips,
-                               DSPSFT, *ip, imm_expr.X_add_number);
-               imm_expr.X_op = O_absent;
-               s = expr_end;
-             }
-             continue;
-
-           case '\'': /* DSP 6-bit unsigned immediate in bit 16.  */
-             gas_assert (!mips_opts.micromips);
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if (imm_expr.X_add_number & ~OP_MASK_RDDSP)
-               {
-                 as_bad (_("DSP immediate not in range 0..%d (%lu)"),
-                         OP_MASK_RDDSP,
-                         (unsigned long) imm_expr.X_add_number);
-               }
-             INSERT_OPERAND (0, RDDSP, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
-
-           case ':': /* DSP 7-bit signed immediate in bit 19.  */
-             gas_assert (!mips_opts.micromips);
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             min_range = -((OP_MASK_DSPSFT_7 + 1) >> 1);
-             max_range = ((OP_MASK_DSPSFT_7 + 1) >> 1) - 1;
-             if (imm_expr.X_add_number < min_range ||
-                 imm_expr.X_add_number > max_range)
-               {
-                 as_bad (_("DSP immediate not in range %ld..%ld (%ld)"),
-                         (long) min_range, (long) max_range,
-                         (long) imm_expr.X_add_number);
-               }
-             INSERT_OPERAND (0, DSPSFT_7, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
-
-           case '@': /* DSP 10-bit signed immediate in bit 16.  */
-             {
-               long mask = (mips_opts.micromips
-                            ? MICROMIPSOP_MASK_IMM10 : OP_MASK_IMM10);
-
-               my_getExpression (&imm_expr, s);
-               check_absolute_expr (ip, &imm_expr);
-               min_range = -((mask + 1) >> 1);
-               max_range = ((mask + 1) >> 1) - 1;
-               if (imm_expr.X_add_number < min_range
-                   || imm_expr.X_add_number > max_range)
-                 as_bad (_("DSP immediate not in range %ld..%ld (%ld)"),
-                         (long) min_range, (long) max_range,
-                         (long) imm_expr.X_add_number);
-               INSERT_OPERAND (mips_opts.micromips,
-                               IMM10, *ip, imm_expr.X_add_number);
-               imm_expr.X_op = O_absent;
-               s = expr_end;
-             }
-             continue;
-
-           case '^': /* DSP 5-bit unsigned immediate in bit 11.  */
-             gas_assert (mips_opts.micromips);
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if (imm_expr.X_add_number & ~MICROMIPSOP_MASK_RD)
-               as_bad (_("DSP immediate not in range 0..%d (%lu)"),
-                       MICROMIPSOP_MASK_RD,
-                       (unsigned long) imm_expr.X_add_number);
-             INSERT_OPERAND (1, RD, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
-
-            case '!': /* MT usermode flag bit.  */
-             gas_assert (!mips_opts.micromips);
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if (imm_expr.X_add_number & ~OP_MASK_MT_U)
-               as_bad (_("MT usermode bit not 0 or 1 (%lu)"),
-                       (unsigned long) imm_expr.X_add_number);
-             INSERT_OPERAND (0, MT_U, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+         used_at = 1;
+         tempreg = AT;
+       }
+      else
+       tempreg = treg;
 
-            case '$': /* MT load high flag bit.  */
-             gas_assert (!mips_opts.micromips);
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if (imm_expr.X_add_number & ~OP_MASK_MT_H)
-               as_bad (_("MT load high bit not 0 or 1 (%lu)"),
-                       (unsigned long) imm_expr.X_add_number);
-             INSERT_OPERAND (0, MT_H, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+      if (off == 1)
+       goto ulh_sh;
 
-           case '*': /* Four DSP accumulators in bits 18,19.  */
-             gas_assert (!mips_opts.micromips);
-             if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
-                 s[3] >= '0' && s[3] <= '3')
-               {
-                 regno = s[3] - '0';
-                 s += 4;
-                 INSERT_OPERAND (0, MTACC_T, *ip, regno);
-                 continue;
-               }
-             else
-               as_bad (_("Invalid dsp/smartmips acc register"));
-             break;
+      if (!target_big_endian)
+       ep->X_add_number += off;
+      if (offbits == 12)
+       macro_build (NULL, s, "t,~(b)", tempreg, (int) ep->X_add_number, breg);
+      else
+       macro_build (ep, s, "t,o(b)", tempreg, -1,
+                    offset_reloc[0], offset_reloc[1], offset_reloc[2], breg);
 
-           case '&': /* Four DSP accumulators in bits 13,14.  */
-             gas_assert (!mips_opts.micromips);
-             if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
-                 s[3] >= '0' && s[3] <= '3')
-               {
-                 regno = s[3] - '0';
-                 s += 4;
-                 INSERT_OPERAND (0, MTACC_D, *ip, regno);
-                 continue;
-               }
-             else
-               as_bad (_("Invalid dsp/smartmips acc register"));
-             break;
+      if (!target_big_endian)
+       ep->X_add_number -= off;
+      else
+       ep->X_add_number += off;
+      if (offbits == 12)
+       macro_build (NULL, s2, "t,~(b)",
+                    tempreg, (int) ep->X_add_number, breg);
+      else
+       macro_build (ep, s2, "t,o(b)", tempreg, -1,
+                    offset_reloc[0], offset_reloc[1], offset_reloc[2], breg);
 
-           case '\\':          /* 3-bit bit position.  */
-             {
-               unsigned long mask = (mips_opts.micromips
-                                     ? MICROMIPSOP_MASK_3BITPOS
-                                     : OP_MASK_3BITPOS);
-
-               my_getExpression (&imm_expr, s);
-               check_absolute_expr (ip, &imm_expr);
-               if ((unsigned long) imm_expr.X_add_number > mask)
-                 as_warn (_("Bit position for %s not in range 0..%lu (%lu)"),
-                          ip->insn_mo->name,
-                          mask, (unsigned long) imm_expr.X_add_number);
-               INSERT_OPERAND (mips_opts.micromips,
-                               3BITPOS, *ip, imm_expr.X_add_number);
-               imm_expr.X_op = O_absent;
-               s = expr_end;
-             }
-             continue;
+      /* If necessary, move the result in tempreg to the final destination.  */
+      if (!ust && treg != tempreg)
+        {
+         /* Protect second load's delay slot.  */
+         load_delay_nop ();
+         move_register (treg, tempreg);
+       }
+      break;
 
-           case ',':
-             ++argnum;
-             if (*s++ == *args)
-               continue;
-             s--;
-             switch (*++args)
-               {
-               case 'r':
-               case 'v':
-                 INSERT_OPERAND (mips_opts.micromips, RS, *ip, lastregno);
-                 continue;
+    ulh_sh:
+      used_at = 1;
+      if (target_big_endian == ust)
+       ep->X_add_number += off;
+      tempreg = ust || large_offset ? treg : AT;
+      macro_build (ep, s, "t,o(b)", tempreg, -1,
+                  offset_reloc[0], offset_reloc[1], offset_reloc[2], breg);
 
-               case 'w':
-                 INSERT_OPERAND (mips_opts.micromips, RT, *ip, lastregno);
-                 continue;
+      /* For halfword transfers we need a temporary register to shuffle
+         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;
+      if (ust)
+       macro_build (NULL, "srl", SHFT_FMT, tempreg, treg, 8);
 
-               case 'W':
-                 gas_assert (!mips_opts.micromips);
-                 INSERT_OPERAND (0, FT, *ip, lastregno);
-                 continue;
+      if (target_big_endian == ust)
+       ep->X_add_number -= off;
+      else
+       ep->X_add_number += off;
+      macro_build (ep, s2, "t,o(b)", tempreg, -1,
+                  offset_reloc[0], offset_reloc[1], offset_reloc[2], breg);
 
-               case 'V':
-                 INSERT_OPERAND (mips_opts.micromips, FS, *ip, lastregno);
-                 continue;
-               }
-             break;
+      /* For M_USH_A re-retrieve the LSB.  */
+      if (ust && large_offset)
+       {
+         if (target_big_endian)
+           ep->X_add_number += off;
+         else
+           ep->X_add_number -= off;
+         macro_build (&expr1, "lbu", "t,o(b)", AT, -1,
+                      offset_reloc[0], offset_reloc[1], offset_reloc[2], AT);
+       }
+      /* For ULH and M_USH_A OR the LSB in.  */
+      if (!ust || large_offset)
+       {
+         tempreg = !large_offset ? AT : treg;
+         macro_build (NULL, "sll", SHFT_FMT, tempreg, tempreg, 8);
+         macro_build (NULL, "or", "d,v,t", treg, treg, AT);
+       }
+      break;
 
-           case '(':
-             /* Handle optional base register.
-                Either the base register is omitted or
-                we must have a left paren.  */
-             /* This is dependent on the next operand specifier
-                is a base register specification.  */
-             gas_assert (args[1] == 'b'
-                         || (mips_opts.micromips
-                             && args[1] == 'm'
-                             && (args[2] == 'l' || args[2] == 'n'
-                                 || args[2] == 's' || args[2] == 'a')));
-             if (*s == '\0' && args[1] == 'b')
-               return;
-             /* Fall through.  */
-
-           case ')':           /* These must match exactly.  */
-             if (*s++ == *args)
-               continue;
-             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);
+      break;
+    }
+  if (!mips_opts.at && used_at)
+    as_bad (_("Macro used $at after \".set noat\""));
+}
 
-           case '[':           /* These must match exactly.  */
-           case ']':
-             gas_assert (!mips_opts.micromips);
-             if (*s++ == *args)
-               continue;
-             break;
+/* Implement macros in mips16 mode.  */
 
-           case '+':           /* Opcode extension character.  */
-             switch (*++args)
-               {
-               case '1':       /* UDI immediates.  */
-               case '2':
-               case '3':
-               case '4':
-                 gas_assert (!mips_opts.micromips);
-                 {
-                   const struct mips_immed *imm = mips_immed;
-
-                   while (imm->type && imm->type != *args)
-                     ++imm;
-                   if (! imm->type)
-                     abort ();
-                   my_getExpression (&imm_expr, s);
-                   check_absolute_expr (ip, &imm_expr);
-                   if ((unsigned long) imm_expr.X_add_number & ~imm->mask)
-                     {
-                       as_warn (_("Illegal %s number (%lu, 0x%lx)"),
-                                imm->desc ? imm->desc : ip->insn_mo->name,
-                                (unsigned long) imm_expr.X_add_number,
-                                (unsigned long) imm_expr.X_add_number);
-                       imm_expr.X_add_number &= imm->mask;
-                     }
-                   ip->insn_opcode |= ((unsigned long) imm_expr.X_add_number
-                                       << imm->shift);
-                   imm_expr.X_op = O_absent;
-                   s = expr_end;
-                 }
-                 continue;
+static void
+mips16_macro (struct mips_cl_insn *ip)
+{
+  int mask;
+  int xreg, yreg, zreg, tmp;
+  expressionS expr1;
+  int dbl;
+  const char *s, *s2, *s3;
 
-               case 'J':               /* 10-bit hypcall code.  */
-                 gas_assert (!mips_opts.micromips);
-                 {
-                   unsigned long mask = OP_MASK_CODE10;
-
-                   my_getExpression (&imm_expr, s);
-                   check_absolute_expr (ip, &imm_expr);
-                   if ((unsigned long) imm_expr.X_add_number > mask)
-                     as_warn (_("Code for %s not in range 0..%lu (%lu)"),
-                              ip->insn_mo->name,
-                              mask, (unsigned long) imm_expr.X_add_number);
-                   INSERT_OPERAND (0, CODE10, *ip, imm_expr.X_add_number);
-                   imm_expr.X_op = O_absent;
-                   s = expr_end;
-                 }
-                 continue;
+  mask = ip->insn_mo->mask;
 
-               case 'A':               /* ins/ext position, becomes LSB.  */
-                 limlo = 0;
-                 limhi = 31;
-                 goto do_lsb;
-               case 'E':
-                 limlo = 32;
-                 limhi = 63;
-                 goto do_lsb;
-               do_lsb:
-                 my_getExpression (&imm_expr, s);
-                 check_absolute_expr (ip, &imm_expr);
-                 if ((unsigned long) imm_expr.X_add_number < limlo
-                     || (unsigned long) imm_expr.X_add_number > limhi)
-                   {
-                     as_bad (_("Improper position (%lu)"),
-                             (unsigned long) imm_expr.X_add_number);
-                     imm_expr.X_add_number = limlo;
-                   }
-                 lastpos = imm_expr.X_add_number;
-                 INSERT_OPERAND (mips_opts.micromips,
-                                 EXTLSB, *ip, imm_expr.X_add_number);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+  xreg = MIPS16_EXTRACT_OPERAND (RX, *ip);
+  yreg = MIPS16_EXTRACT_OPERAND (RY, *ip);
+  zreg = MIPS16_EXTRACT_OPERAND (RZ, *ip);
 
-               case 'B':               /* ins size, becomes MSB.  */
-                 limlo = 1;
-                 limhi = 32;
-                 goto do_msb;
-               case 'F':
-                 limlo = 33;
-                 limhi = 64;
-                 goto do_msb;
-               do_msb:
-                 my_getExpression (&imm_expr, s);
-                 check_absolute_expr (ip, &imm_expr);
-                 /* Check for negative input so that small negative numbers
-                    will not succeed incorrectly.  The checks against
-                    (pos+size) transitively check "size" itself,
-                    assuming that "pos" is reasonable.  */
-                 if ((long) imm_expr.X_add_number < 0
-                     || ((unsigned long) imm_expr.X_add_number
-                         + lastpos) < limlo
-                     || ((unsigned long) imm_expr.X_add_number
-                         + lastpos) > limhi)
-                   {
-                     as_bad (_("Improper insert size (%lu, position %lu)"),
-                             (unsigned long) imm_expr.X_add_number,
-                             (unsigned long) lastpos);
-                     imm_expr.X_add_number = limlo - lastpos;
-                   }
-                 INSERT_OPERAND (mips_opts.micromips, INSMSB, *ip,
-                                 lastpos + imm_expr.X_add_number - 1);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+  expr1.X_op = O_constant;
+  expr1.X_op_symbol = NULL;
+  expr1.X_add_symbol = NULL;
+  expr1.X_add_number = 1;
 
-               case 'C':               /* ext size, becomes MSBD.  */
-                 limlo = 1;
-                 limhi = 32;
-                 sizelo = 1;
-                 goto do_msbd;
-               case 'G':
-                 limlo = 33;
-                 limhi = 64;
-                 sizelo = 33;
-                 goto do_msbd;
-               case 'H':
-                 limlo = 33;
-                 limhi = 64;
-                 sizelo = 1;
-                 goto do_msbd;
-               do_msbd:
-                 my_getExpression (&imm_expr, s);
-                 check_absolute_expr (ip, &imm_expr);
-                 /* The checks against (pos+size) don't transitively check
-                    "size" itself, assuming that "pos" is reasonable.
-                    We also need to check the lower bound of "size".  */
-                 if ((long) imm_expr.X_add_number < sizelo
-                     || ((unsigned long) imm_expr.X_add_number
-                         + lastpos) < limlo
-                     || ((unsigned long) imm_expr.X_add_number
-                         + lastpos) > limhi)
-                   {
-                     as_bad (_("Improper extract size (%lu, position %lu)"),
-                             (unsigned long) imm_expr.X_add_number,
-                             (unsigned long) lastpos);
-                     imm_expr.X_add_number = limlo - lastpos;
-                   }
-                 INSERT_OPERAND (mips_opts.micromips,
-                                 EXTMSBD, *ip, imm_expr.X_add_number - 1);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+  dbl = 0;
 
-               case 'D':
-                 /* +D is for disassembly only; never match.  */
-                 break;
+  switch (mask)
+    {
+    default:
+      abort ();
 
-               case 'I':
-                 /* "+I" is like "I", except that imm2_expr is used.  */
-                 my_getExpression (&imm2_expr, s);
-                 if (imm2_expr.X_op != O_big
-                     && imm2_expr.X_op != O_constant)
-                 insn_error = _("absolute expression required");
-                 if (HAVE_32BIT_GPRS)
-                   normalize_constant_expr (&imm2_expr);
-                 s = expr_end;
-                 continue;
+    case M_DDIV_3:
+      dbl = 1;
+    case M_DIV_3:
+      s = "mflo";
+      goto do_div3;
+    case M_DREM_3:
+      dbl = 1;
+    case M_REM_3:
+      s = "mfhi";
+    do_div3:
+      start_noreorder ();
+      macro_build (NULL, dbl ? "ddiv" : "div", "0,x,y", xreg, yreg);
+      expr1.X_add_number = 2;
+      macro_build (&expr1, "bnez", "x,p", yreg);
+      macro_build (NULL, "break", "6", 7);
 
-               case 'T': /* Coprocessor register.  */
-                 gas_assert (!mips_opts.micromips);
-                 /* +T is for disassembly only; never match.  */
-                 break;
+      /* FIXME: The normal code checks for of -1 / -0x80000000 here,
+         since that causes an overflow.  We should do that as well,
+         but I don't see how to do the comparisons without a temporary
+         register.  */
+      end_noreorder ();
+      macro_build (NULL, s, "x", zreg);
+      break;
 
-               case 't': /* Coprocessor register number.  */
-                 gas_assert (!mips_opts.micromips);
-                 if (s[0] == '$' && ISDIGIT (s[1]))
-                   {
-                     ++s;
-                     regno = 0;
-                     do
-                       {
-                         regno *= 10;
-                         regno += *s - '0';
-                         ++s;
-                       }
-                     while (ISDIGIT (*s));
-                     if (regno > 31)
-                       as_bad (_("Invalid register number (%d)"), regno);
-                     else
-                       {
-                         INSERT_OPERAND (0, RT, *ip, regno);
-                         continue;
-                       }
-                   }
-                 else
-                   as_bad (_("Invalid coprocessor 0 register number"));
-                 break;
+    case M_DIVU_3:
+      s = "divu";
+      s2 = "mflo";
+      goto do_divu3;
+    case M_REMU_3:
+      s = "divu";
+      s2 = "mfhi";
+      goto do_divu3;
+    case M_DDIVU_3:
+      s = "ddivu";
+      s2 = "mflo";
+      goto do_divu3;
+    case M_DREMU_3:
+      s = "ddivu";
+      s2 = "mfhi";
+    do_divu3:
+      start_noreorder ();
+      macro_build (NULL, s, "0,x,y", xreg, yreg);
+      expr1.X_add_number = 2;
+      macro_build (&expr1, "bnez", "x,p", yreg);
+      macro_build (NULL, "break", "6", 7);
+      end_noreorder ();
+      macro_build (NULL, s2, "x", zreg);
+      break;
 
-               case 'x':
-                 /* bbit[01] and bbit[01]32 bit index.  Give error if index
-                    is not in the valid range.  */
-                 gas_assert (!mips_opts.micromips);
-                 my_getExpression (&imm_expr, s);
-                 check_absolute_expr (ip, &imm_expr);
-                 if ((unsigned) imm_expr.X_add_number > 31)
-                   {
-                     as_bad (_("Improper bit index (%lu)"),
-                             (unsigned long) imm_expr.X_add_number);
-                     imm_expr.X_add_number = 0;
-                   }
-                 INSERT_OPERAND (0, BBITIND, *ip, imm_expr.X_add_number);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+    case M_DMUL:
+      dbl = 1;
+    case M_MUL:
+      macro_build (NULL, dbl ? "dmultu" : "multu", "x,y", xreg, yreg);
+      macro_build (NULL, "mflo", "x", zreg);
+      break;
 
-               case 'X':
-                 /* bbit[01] bit index when bbit is used but we generate
-                    bbit[01]32 because the index is over 32.  Move to the
-                    next candidate if index is not in the valid range.  */
-                 gas_assert (!mips_opts.micromips);
-                 my_getExpression (&imm_expr, s);
-                 check_absolute_expr (ip, &imm_expr);
-                 if ((unsigned) imm_expr.X_add_number < 32
-                     || (unsigned) imm_expr.X_add_number > 63)
-                   break;
-                 INSERT_OPERAND (0, BBITIND, *ip, imm_expr.X_add_number - 32);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+    case M_DSUBU_I:
+      dbl = 1;
+      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);
+      break;
 
-               case 'p':
-                 /* cins, cins32, exts and exts32 position field.  Give error
-                    if it's not in the valid range.  */
-                 gas_assert (!mips_opts.micromips);
-                 my_getExpression (&imm_expr, s);
-                 check_absolute_expr (ip, &imm_expr);
-                 if ((unsigned) imm_expr.X_add_number > 31)
-                   {
-                     as_bad (_("Improper position (%lu)"),
-                             (unsigned long) imm_expr.X_add_number);
-                     imm_expr.X_add_number = 0;
-                   }
-                 /* Make the pos explicit to simplify +S.  */
-                 lastpos = imm_expr.X_add_number + 32;
-                 INSERT_OPERAND (0, CINSPOS, *ip, imm_expr.X_add_number);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+    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);
+      break;
 
-               case 'P':
-                 /* cins, cins32, exts and exts32 position field.  Move to
-                    the next candidate if it's not in the valid range.  */
-                 gas_assert (!mips_opts.micromips);
-                 my_getExpression (&imm_expr, s);
-                 check_absolute_expr (ip, &imm_expr);
-                 if ((unsigned) imm_expr.X_add_number < 32
-                     || (unsigned) imm_expr.X_add_number > 63)
-                   break;
-                 lastpos = imm_expr.X_add_number;
-                 INSERT_OPERAND (0, CINSPOS, *ip, imm_expr.X_add_number - 32);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+    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);
+      break;
 
-               case 's':
-                 /* cins and exts length-minus-one field.  */
-                 gas_assert (!mips_opts.micromips);
-                 my_getExpression (&imm_expr, s);
-                 check_absolute_expr (ip, &imm_expr);
-                 if ((unsigned long) imm_expr.X_add_number > 31)
-                   {
-                     as_bad (_("Improper size (%lu)"),
-                             (unsigned long) imm_expr.X_add_number);
-                     imm_expr.X_add_number = 0;
-                   }
-                 INSERT_OPERAND (0, CINSLM1, *ip, imm_expr.X_add_number);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+    case M_BEQ:
+      s = "cmp";
+      s2 = "bteqz";
+      goto do_branch;
+    case M_BNE:
+      s = "cmp";
+      s2 = "btnez";
+      goto do_branch;
+    case M_BLT:
+      s = "slt";
+      s2 = "btnez";
+      goto do_branch;
+    case M_BLTU:
+      s = "sltu";
+      s2 = "btnez";
+      goto do_branch;
+    case M_BLE:
+      s = "slt";
+      s2 = "bteqz";
+      goto do_reverse_branch;
+    case M_BLEU:
+      s = "sltu";
+      s2 = "bteqz";
+      goto do_reverse_branch;
+    case M_BGE:
+      s = "slt";
+      s2 = "bteqz";
+      goto do_branch;
+    case M_BGEU:
+      s = "sltu";
+      s2 = "bteqz";
+      goto do_branch;
+    case M_BGT:
+      s = "slt";
+      s2 = "btnez";
+      goto do_reverse_branch;
+    case M_BGTU:
+      s = "sltu";
+      s2 = "btnez";
 
-               case 'S':
-                 /* cins32/exts32 and cins/exts aliasing cint32/exts32
-                    length-minus-one field.  */
-                 gas_assert (!mips_opts.micromips);
-                 my_getExpression (&imm_expr, s);
-                 check_absolute_expr (ip, &imm_expr);
-                 if ((long) imm_expr.X_add_number < 0
-                     || (unsigned long) imm_expr.X_add_number + lastpos > 63)
-                   {
-                     as_bad (_("Improper size (%lu)"),
-                             (unsigned long) imm_expr.X_add_number);
-                     imm_expr.X_add_number = 0;
-                   }
-                 INSERT_OPERAND (0, CINSLM1, *ip, imm_expr.X_add_number);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+    do_reverse_branch:
+      tmp = xreg;
+      xreg = yreg;
+      yreg = tmp;
 
-               case 'Q':
-                 /* seqi/snei immediate field.  */
-                 gas_assert (!mips_opts.micromips);
-                 my_getExpression (&imm_expr, s);
-                 check_absolute_expr (ip, &imm_expr);
-                 if ((long) imm_expr.X_add_number < -512
-                     || (long) imm_expr.X_add_number >= 512)
-                   {
-                     as_bad (_("Improper immediate (%ld)"),
-                              (long) imm_expr.X_add_number);
-                     imm_expr.X_add_number = 0;
-                   }
-                 INSERT_OPERAND (0, SEQI, *ip, imm_expr.X_add_number);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+    do_branch:
+      macro_build (NULL, s, "x,y", xreg, yreg);
+      macro_build (&offset_expr, s2, "p");
+      break;
 
-               case 'a': /* 8-bit signed offset in bit 6 */
-                 gas_assert (!mips_opts.micromips);
-                 my_getExpression (&imm_expr, s);
-                 check_absolute_expr (ip, &imm_expr);
-                 min_range = -((OP_MASK_OFFSET_A + 1) >> 1);
-                 max_range = ((OP_MASK_OFFSET_A + 1) >> 1) - 1;
-                 if (imm_expr.X_add_number < min_range
-                     || imm_expr.X_add_number > max_range)
-                   {
-                     as_bad (_("Offset not in range %ld..%ld (%ld)"),
-                             (long) min_range, (long) max_range,
-                             (long) imm_expr.X_add_number);
-                   }
-                 INSERT_OPERAND (0, OFFSET_A, *ip, imm_expr.X_add_number);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+    case M_BEQ_I:
+      s = "cmpi";
+      s2 = "bteqz";
+      s3 = "x,U";
+      goto do_branch_i;
+    case M_BNE_I:
+      s = "cmpi";
+      s2 = "btnez";
+      s3 = "x,U";
+      goto do_branch_i;
+    case M_BLT_I:
+      s = "slti";
+      s2 = "btnez";
+      s3 = "x,8";
+      goto do_branch_i;
+    case M_BLTU_I:
+      s = "sltiu";
+      s2 = "btnez";
+      s3 = "x,8";
+      goto do_branch_i;
+    case M_BLE_I:
+      s = "slti";
+      s2 = "btnez";
+      s3 = "x,8";
+      goto do_addone_branch_i;
+    case M_BLEU_I:
+      s = "sltiu";
+      s2 = "btnez";
+      s3 = "x,8";
+      goto do_addone_branch_i;
+    case M_BGE_I:
+      s = "slti";
+      s2 = "bteqz";
+      s3 = "x,8";
+      goto do_branch_i;
+    case M_BGEU_I:
+      s = "sltiu";
+      s2 = "bteqz";
+      s3 = "x,8";
+      goto do_branch_i;
+    case M_BGT_I:
+      s = "slti";
+      s2 = "bteqz";
+      s3 = "x,8";
+      goto do_addone_branch_i;
+    case M_BGTU_I:
+      s = "sltiu";
+      s2 = "bteqz";
+      s3 = "x,8";
 
-               case 'b': /* 8-bit signed offset in bit 3 */
-                 gas_assert (!mips_opts.micromips);
-                 my_getExpression (&imm_expr, s);
-                 check_absolute_expr (ip, &imm_expr);
-                 min_range = -((OP_MASK_OFFSET_B + 1) >> 1);
-                 max_range = ((OP_MASK_OFFSET_B + 1) >> 1) - 1;
-                 if (imm_expr.X_add_number < min_range
-                     || imm_expr.X_add_number > max_range)
-                   {
-                     as_bad (_("Offset not in range %ld..%ld (%ld)"),
-                             (long) min_range, (long) max_range,
-                             (long) imm_expr.X_add_number);
-                   }
-                 INSERT_OPERAND (0, OFFSET_B, *ip, imm_expr.X_add_number);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+    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 (&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 'c': /* 9-bit signed offset in bit 6 */
-                 gas_assert (!mips_opts.micromips);
-                 my_getExpression (&imm_expr, s);
-                 check_absolute_expr (ip, &imm_expr);
-                 min_range = -((OP_MASK_OFFSET_C + 1) >> 1);
-                 max_range = ((OP_MASK_OFFSET_C + 1) >> 1) - 1;
-                 /* We check the offset range before adjusted.  */
-                 min_range <<= 4;
-                 max_range <<= 4;
-                 if (imm_expr.X_add_number < min_range
-                     || imm_expr.X_add_number > max_range)
-                   {
-                     as_bad (_("Offset not in range %ld..%ld (%ld)"),
-                             (long) min_range, (long) max_range,
-                             (long) imm_expr.X_add_number);
-                   }
-                 if (imm_expr.X_add_number & 0xf)
-                   {
-                     as_bad (_("Offset not 16 bytes alignment (%ld)"),
-                             (long) imm_expr.X_add_number);
-                   }
-                 /* Right shift 4 bits to adjust the offset operand.  */
-                 INSERT_OPERAND (0, OFFSET_C, *ip,
-                                 imm_expr.X_add_number >> 4);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+/* 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.  */
 
-               case 'z':
-                 gas_assert (!mips_opts.micromips);
-                 if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, &regno))
-                   break;
-                 if (regno == AT && mips_opts.at)
-                   {
-                     if (mips_opts.at == ATREG)
-                       as_warn (_("used $at without \".set noat\""));
-                     else
-                       as_warn (_("used $%u with \".set at=$%u\""),
-                                regno, mips_opts.at);
-                   }
-                 INSERT_OPERAND (0, RZ, *ip, regno);
-                 continue;
+static void
+mips_ip (char *str, struct mips_cl_insn *ip)
+{
+  bfd_boolean wrong_delay_slot_insns = FALSE;
+  bfd_boolean need_delay_slot_ok = TRUE;
+  struct mips_opcode *firstinsn = NULL;
+  const struct mips_opcode *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;
+  struct mips_operand_token *tokens;
+  bfd_boolean optional_reg;
 
-               case 'Z':
-                 gas_assert (!mips_opts.micromips);
-                 if (!reg_lookup (&s, RTYPE_FPU, &regno))
-                   break;
-                 INSERT_OPERAND (0, FZ, *ip, regno);
-                 continue;
+  insn_error = NULL;
 
-               case 'j':
-                 {
-                   int shift = 8;
-                   size_t i;
-                   /* Check whether there is only a single bracketed expression
-                      left.  If so, it must be the base register and the
-                      constant must be zero.  */
-                   if (*s == '(' && strchr (s + 1, '(') == 0)
-                     continue;
+  if (mips_opts.micromips)
+    {
+      hash = micromips_op_hash;
+      past = &micromips_opcodes[bfd_micromips_num_opcodes];
+    }
+  else
+    {
+      hash = op_hash;
+      past = &mips_opcodes[NUMOPCODES];
+    }
+  forced_insn_length = 0;
+  insn = NULL;
 
-                   /* If this value won't fit into the offset, then go find
-                      a macro that will generate a 16- or 32-bit offset code
-                      pattern.  */
-                   i = my_getSmallExpression (&imm_expr, imm_reloc, s);
-                   if ((i == 0 && (imm_expr.X_op != O_constant
-                                   || imm_expr.X_add_number >= 1 << shift
-                                   || imm_expr.X_add_number < -1 << shift))
-                       || i > 0)
-                     {
-                       imm_expr.X_op = O_absent;
-                       break;
-                     }
-                   INSERT_OPERAND (mips_opts.micromips, EVAOFFSET, *ip,
-                                   imm_expr.X_add_number);
-                   imm_expr.X_op = O_absent;
-                   s = expr_end;
-                 }
-                 continue;
+  /* 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;
 
-               default:
-                 as_bad (_("Internal error: bad %s opcode "
-                           "(unknown extension operand type `+%c'): %s %s"),
-                         mips_opts.micromips ? "microMIPS" : "MIPS",
-                         *args, insn->name, insn->args);
-                 /* Further processing is fruitless.  */
-                 return;
-               }
-             break;
+  /* Make a copy of the instruction so that we can fiddle with it.  */
+  name = alloca (end + 1);
+  memcpy (name, str, end);
+  name[end] = '\0';
 
-           case '.':           /* 10-bit offset.  */
-             gas_assert (mips_opts.micromips);
-           case '~':           /* 12-bit offset.  */
-             {
-               int shift = *args == '.' ? 9 : 11;
-               size_t i;
+  for (;;)
+    {
+      insn = (struct mips_opcode *) hash_find (hash, name);
 
-               /* Check whether there is only a single bracketed expression
-                  left.  If so, it must be the base register and the
-                  constant must be zero.  */
-               if (*s == '(' && strchr (s + 1, '(') == 0)
-                 continue;
+      if (insn != NULL || !mips_opts.micromips)
+       break;
+      if (forced_insn_length)
+       break;
 
-               /* If this value won't fit into the offset, then go find
-                  a macro that will generate a 16- or 32-bit offset code
-                  pattern.  */
-               i = my_getSmallExpression (&imm_expr, imm_reloc, s);
-               if ((i == 0 && (imm_expr.X_op != O_constant
-                               || imm_expr.X_add_number >= 1 << shift
-                               || imm_expr.X_add_number < -1 << shift))
-                   || i > 0)
-                 {
-                   imm_expr.X_op = O_absent;
-                   break;
-                 }
-               if (shift == 9)
-                 INSERT_OPERAND (1, OFFSET10, *ip, imm_expr.X_add_number);
-               else
-                 INSERT_OPERAND (mips_opts.micromips,
-                                 OFFSET12, *ip, imm_expr.X_add_number);
-               imm_expr.X_op = O_absent;
-               s = expr_end;
-             }
-             continue;
+      /* 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)
+    {
+      insn_error = _("Unrecognized opcode");
+      return;
+    }
 
-           case '<':           /* must be at least one digit */
-             /*
-              * According to the manual, if the shift amount is greater
-              * than 31 or less than 0, then the shift amount should be
-              * mod 32.  In reality the mips assembler issues an error.
-              * We issue a warning and mask out all but the low 5 bits.
-              */
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if ((unsigned long) imm_expr.X_add_number > 31)
-               as_warn (_("Improper shift amount (%lu)"),
-                        (unsigned long) imm_expr.X_add_number);
-             INSERT_OPERAND (mips_opts.micromips,
-                             SHAMT, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+  if (strcmp (name, "li.s") == 0)
+    format = 'f';
+  else if (strcmp (name, "li.d") == 0)
+    format = 'd';
+  else
+    format = 0;
+  tokens = mips_parse_arguments (str + end, format);
+  if (!tokens)
+    return;
 
-           case '>':           /* shift amount minus 32 */
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if ((unsigned long) imm_expr.X_add_number < 32
-                 || (unsigned long) imm_expr.X_add_number > 63)
-               break;
-             INSERT_OPERAND (mips_opts.micromips,
-                             SHAMT, *ip, imm_expr.X_add_number - 32);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+  /* 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;
 
-           case 'k':           /* CACHE code.  */
-           case 'h':           /* PREFX code.  */
-           case '1':           /* SYNC type.  */
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if ((unsigned long) imm_expr.X_add_number > 31)
-               as_warn (_("Invalid value for `%s' (%lu)"),
-                        ip->insn_mo->name,
-                        (unsigned long) imm_expr.X_add_number);
-             switch (*args)
-               {
-               case 'k':
-                 if (mips_fix_cn63xxp1
-                     && !mips_opts.micromips
-                     && strcmp ("pref", insn->name) == 0)
-                   switch (imm_expr.X_add_number)
-                     {
-                     case 5:
-                     case 25:
-                     case 26:
-                     case 27:
-                     case 28:
-                     case 29:
-                     case 30:
-                     case 31:  /* These are ok.  */
-                       break;
-
-                     default:  /* The rest must be changed to 28.  */
-                       imm_expr.X_add_number = 28;
-                       break;
-                     }
-                 INSERT_OPERAND (mips_opts.micromips,
-                                 CACHE, *ip, imm_expr.X_add_number);
-                 break;
-               case 'h':
-                 INSERT_OPERAND (mips_opts.micromips,
-                                 PREFX, *ip, imm_expr.X_add_number);
-                 break;
-               case '1':
-                 INSERT_OPERAND (mips_opts.micromips,
-                                 STYPE, *ip, imm_expr.X_add_number);
-                 break;
-               }
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+      gas_assert (strcmp (insn->name, name) == 0);
 
-           case 'c':           /* BREAK code.  */
-             {
-               unsigned long mask = (mips_opts.micromips
-                                     ? MICROMIPSOP_MASK_CODE
-                                     : OP_MASK_CODE);
-
-               my_getExpression (&imm_expr, s);
-               check_absolute_expr (ip, &imm_expr);
-               if ((unsigned long) imm_expr.X_add_number > mask)
-                 as_warn (_("Code for %s not in range 0..%lu (%lu)"),
-                          ip->insn_mo->name,
-                          mask, (unsigned long) imm_expr.X_add_number);
-               INSERT_OPERAND (mips_opts.micromips,
-                               CODE, *ip, imm_expr.X_add_number);
-               imm_expr.X_op = O_absent;
-               s = expr_end;
-             }
-             continue;
+      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];
 
-           case 'q':           /* Lower BREAK code.  */
-             {
-               unsigned long mask = (mips_opts.micromips
-                                     ? MICROMIPSOP_MASK_CODE2
-                                     : OP_MASK_CODE2);
-
-               my_getExpression (&imm_expr, s);
-               check_absolute_expr (ip, &imm_expr);
-               if ((unsigned long) imm_expr.X_add_number > mask)
-                 as_warn (_("Lower code for %s not in range 0..%lu (%lu)"),
-                          ip->insn_mo->name,
-                          mask, (unsigned long) imm_expr.X_add_number);
-               INSERT_OPERAND (mips_opts.micromips,
-                               CODE2, *ip, imm_expr.X_add_number);
-               imm_expr.X_op = O_absent;
-               s = expr_end;
-             }
+         if (more_alts)
+           {
+             ++insn;
              continue;
-
-           case 'B':           /* 20- or 10-bit syscall/break/wait code.  */
-             {
-               unsigned long mask = (mips_opts.micromips
-                                     ? MICROMIPSOP_MASK_CODE10
-                                     : OP_MASK_CODE20);
-
-               my_getExpression (&imm_expr, s);
-               check_absolute_expr (ip, &imm_expr);
-               if ((unsigned long) imm_expr.X_add_number > mask)
-                 as_warn (_("Code for %s not in range 0..%lu (%lu)"),
-                          ip->insn_mo->name,
-                          mask, (unsigned long) imm_expr.X_add_number);
-               if (mips_opts.micromips)
-                 INSERT_OPERAND (1, CODE10, *ip, imm_expr.X_add_number);
-               else
-                 INSERT_OPERAND (0, CODE20, *ip, imm_expr.X_add_number);
-               imm_expr.X_op = O_absent;
-               s = expr_end;
-             }
+           }
+         if (wrong_delay_slot_insns && need_delay_slot_ok)
+           {
+             gas_assert (firstinsn);
+             need_delay_slot_ok = FALSE;
+             past = insn + 1;
+             insn = firstinsn;
              continue;
+           }
 
-           case 'C':           /* 25- or 23-bit coprocessor code.  */
-             {
-               unsigned long mask = (mips_opts.micromips
-                                     ? MICROMIPSOP_MASK_COPZ
-                                     : OP_MASK_COPZ);
-
-               my_getExpression (&imm_expr, s);
-               check_absolute_expr (ip, &imm_expr);
-               if ((unsigned long) imm_expr.X_add_number > mask)
-                 as_warn (_("Coproccesor code > %u bits (%lu)"),
-                          mips_opts.micromips ? 23U : 25U,
-                          (unsigned long) imm_expr.X_add_number);
-               INSERT_OPERAND (mips_opts.micromips,
-                               COPZ, *ip, imm_expr.X_add_number);
-               imm_expr.X_op = O_absent;
-               s = expr_end;
-             }
-             continue;
+         obstack_free (&mips_operand_tokens, tokens);
+         if (insn_error)
+           return;
 
-           case 'J':           /* 19-bit WAIT code.  */
-             gas_assert (!mips_opts.micromips);
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE19)
-               {
-                 as_warn (_("Illegal 19-bit code (%lu)"),
-                          (unsigned long) imm_expr.X_add_number);
-                 imm_expr.X_add_number &= OP_MASK_CODE19;
-               }
-             INSERT_OPERAND (0, CODE19, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+         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;
 
-           case 'P':           /* Performance register.  */
-             gas_assert (!mips_opts.micromips);
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if (imm_expr.X_add_number != 0 && imm_expr.X_add_number != 1)
-               as_warn (_("Invalid performance register (%lu)"),
-                        (unsigned long) imm_expr.X_add_number);
-             if (imm_expr.X_add_number != 0 && mips_opts.arch == CPU_R5900
-               && (!strcmp(insn->name,"mfps") || !strcmp(insn->name,"mtps")))
-               as_warn (_("Invalid performance register (%lu)"),
-                 (unsigned long) imm_expr.X_add_number);
-             INSERT_OPERAND (0, PERFREG, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+         return;
+       }
 
-           case 'G':           /* Coprocessor destination register.  */
-             {
-               unsigned long opcode = ip->insn_opcode;
-               unsigned long mask;
-               unsigned int types;
-               int cop0;
+      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;
 
-               if (mips_opts.micromips)
-                 {
-                   mask = ~((MICROMIPSOP_MASK_RT << MICROMIPSOP_SH_RT)
-                            | (MICROMIPSOP_MASK_RS << MICROMIPSOP_SH_RS)
-                            | (MICROMIPSOP_MASK_SEL << MICROMIPSOP_SH_SEL));
-                   opcode &= mask;
-                   switch (opcode)
-                     {
-                     case 0x000000fc:                          /* mfc0  */
-                     case 0x000002fc:                          /* mtc0  */
-                     case 0x580000fc:                          /* dmfc0 */
-                     case 0x580002fc:                          /* dmtc0 */
-                       cop0 = 1;
-                       break;
-                     default:
-                       cop0 = 0;
-                       break;
-                     }
-                 }
-               else
-                 {
-                   opcode = (opcode >> OP_SH_OP) & OP_MASK_OP;
-                   cop0 = opcode == OP_OP_COP0;
-                 }
-               types = RTYPE_NUM | (cop0 ? RTYPE_CP0 : RTYPE_GP);
-               ok = reg_lookup (&s, types, &regno);
-               if (mips_opts.micromips)
-                 INSERT_OPERAND (1, RS, *ip, regno);
-               else
-                 INSERT_OPERAND (0, RD, *ip, regno);
-               if (ok)
+      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])
                  {
-                   lastregno = regno;
+                 case 'r':
+                 case 'v':
+                 case 'w':
+                 case 'W':
+                 case 'V':
+                   arg.token = tokens;
+                   arg.argnum = 1;
                    continue;
                  }
-             }
-             break;
 
-           case 'y':           /* ALNV.PS source register.  */
-             gas_assert (mips_opts.micromips);
-             goto do_reg;
-           case 'x':           /* Ignore register name.  */
-           case 'U':           /* Destination register (CLO/CLZ).  */
-           case 'g':           /* Coprocessor destination register.  */
-             gas_assert (!mips_opts.micromips);
-           case 'b':           /* Base register.  */
-           case 'd':           /* Destination register.  */
-           case 's':           /* Source register.  */
-           case 't':           /* Target register.  */
-           case 'r':           /* Both target and source.  */
-           case 'v':           /* Both dest and source.  */
-           case 'w':           /* Both dest and target.  */
-           case 'E':           /* Coprocessor target register.  */
-           case 'K':           /* RDHWR destination register.  */
-           case 'z':           /* Must be zero register.  */
-           do_reg:
-             s_reset = s;
-             if (*args == 'E' || *args == 'K')
-               ok = reg_lookup (&s, RTYPE_NUM, &regno);
-             else
+             /* 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)
                {
-                 ok = reg_lookup (&s, RTYPE_NUM | RTYPE_GP, &regno);
-                 if (regno == AT && mips_opts.at)
-                   {
-                     if (mips_opts.at == ATREG)
-                       as_warn (_("Used $at without \".set noat\""));
-                     else
-                       as_warn (_("Used $%u with \".set at=$%u\""),
-                                regno, mips_opts.at);
-                   }
+                 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"));
                }
-             if (ok)
-               {
-                 c = *args;
-                 if (*s == ' ')
-                   ++s;
-                 if (args[1] != *s)
-                   {
-                     if (c == 'r' || c == 'v' || c == 'w')
-                       {
-                         regno = lastregno;
-                         s = s_reset;
-                         ++args;
-                       }
-                   }
-                 /* 'z' only matches $0.  */
-                 if (c == 'z' && regno != 0)
-                   break;
+             check_completed_insn (&arg);
+             obstack_free (&mips_operand_tokens, tokens);
+             return;
+           }
 
-                 if (c == 's' && !strncmp (ip->insn_mo->name, "jalr", 4))
-                   {
-                     if (regno == lastregno)
-                       {
-                         insn_error
-                           = _("Source and destination must be different");
-                         continue;
-                       }
-                     if (regno == 31 && lastregno == 0xffffffff)
-                       {
-                         insn_error
-                           = _("A destination register must be supplied");
-                         continue;
-                       }
-                   }
-                 /* Now that we have assembled one operand, we use the args
-                    string to figure out where it goes in the instruction.  */
-                 switch (c)
-                   {
-                   case 'r':
-                   case 's':
-                   case 'v':
-                   case 'b':
-                     INSERT_OPERAND (mips_opts.micromips, RS, *ip, regno);
-                     break;
-
-                   case 'K':
-                     if (mips_opts.micromips)
-                       INSERT_OPERAND (1, RS, *ip, regno);
-                     else
-                       INSERT_OPERAND (0, RD, *ip, regno);
-                     break;
-
-                   case 'd':
-                   case 'g':
-                     INSERT_OPERAND (mips_opts.micromips, RD, *ip, regno);
-                     break;
-
-                   case 'U':
-                     gas_assert (!mips_opts.micromips);
-                     INSERT_OPERAND (0, RD, *ip, regno);
-                     INSERT_OPERAND (0, RT, *ip, regno);
-                     break;
-
-                   case 'w':
-                   case 't':
-                   case 'E':
-                     INSERT_OPERAND (mips_opts.micromips, RT, *ip, regno);
-                     break;
-
-                   case 'y':
-                     gas_assert (mips_opts.micromips);
-                     INSERT_OPERAND (1, RS3, *ip, regno);
-                     break;
-
-                   case 'x':
-                     /* This case exists because on the r3000 trunc
-                        expands into a macro which requires a gp
-                        register.  On the r6000 or r4000 it is
-                        assembled into a single instruction which
-                        ignores the register.  Thus the insn version
-                        is MIPS_ISA2 and uses 'x', and the macro
-                        version is MIPS_ISA1 and uses 't'.  */
-                     break;
-
-                   case 'z':
-                     /* This case is for the div instruction, which
-                        acts differently if the destination argument
-                        is $0.  This only matches $0, and is checked
-                        outside the switch.  */
-                     break;
-                   }
-                 lastregno = regno;
-                 continue;
-               }
-             switch (*args++)
+         /* 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 'r':
-               case 'v':
-                 INSERT_OPERAND (mips_opts.micromips, RS, *ip, lastregno);
-                 continue;
+               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 'w':
-                 INSERT_OPERAND (mips_opts.micromips, RT, *ip, lastregno);
+               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 'O':           /* MDMX alignment immediate constant.  */
-             gas_assert (!mips_opts.micromips);
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if ((unsigned long) imm_expr.X_add_number > OP_MASK_ALN)
-               as_warn (_("Improper align amount (%ld), using low bits"),
-                        (long) imm_expr.X_add_number);
-             INSERT_OPERAND (0, ALN, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+           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 'Q':           /* MDMX vector, element sel, or const.  */
-             if (s[0] != '$')
-               {
-                 /* MDMX Immediate.  */
-                 gas_assert (!mips_opts.micromips);
-                 my_getExpression (&imm_expr, s);
-                 check_absolute_expr (ip, &imm_expr);
-                 if ((unsigned long) imm_expr.X_add_number > OP_MASK_FT)
-                   as_warn (_("Invalid MDMX Immediate (%ld)"),
-                            (long) imm_expr.X_add_number);
-                 INSERT_OPERAND (0, FT, *ip, imm_expr.X_add_number);
-                 if (ip->insn_opcode & (OP_MASK_VSEL << OP_SH_VSEL))
-                   ip->insn_opcode |= MDMX_FMTSEL_IMM_QH << OP_SH_VSEL;
-                 else
-                   ip->insn_opcode |= MDMX_FMTSEL_IMM_OB << OP_SH_VSEL;
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
-               }
-             /* Not MDMX Immediate.  Fall through.  */
-           case 'X':           /* MDMX destination register.  */
-           case 'Y':           /* MDMX source register.  */
-           case 'Z':           /* MDMX target register.  */
-             is_mdmx = 1;
+           case 'r':
+           case 'v':
+           case 'w':
            case 'W':
-             gas_assert (!mips_opts.micromips);
-           case 'D':           /* Floating point destination register.  */
-           case 'S':           /* Floating point source register.  */
-           case 'T':           /* Floating point target register.  */
-           case 'R':           /* Floating point source register.  */
            case 'V':
-             rtype = RTYPE_FPU;
-             if (is_mdmx
-                 || ((mips_opts.ase & ASE_MDMX)
-                     && (ip->insn_mo->pinfo & FP_D)
-                     && (ip->insn_mo->pinfo & (INSN_COPROC_MOVE_DELAY
-                                               | INSN_COPROC_MEMORY_DELAY
-                                               | INSN_LOAD_COPROC_DELAY
-                                               | INSN_LOAD_MEMORY_DELAY
-                                               | INSN_STORE_MEMORY))))
-               rtype |= RTYPE_VEC;
-             s_reset = s;
-             if (reg_lookup (&s, rtype, &regno))
-               {
-                 if ((regno & 1) != 0
-                     && HAVE_32BIT_FPRS
-                     && !mips_oddfpreg_ok (ip->insn_mo, argnum))
-                   as_warn (_("Float register should be even, was %d"),
-                            regno);
-
-                 c = *args;
-                 if (*s == ' ')
-                   ++s;
-                 if (args[1] != *s)
-                   {
-                     if (c == 'V' || c == 'W')
-                       {
-                         regno = lastregno;
-                         s = s_reset;
-                         ++args;
-                       }
-                   }
-                 switch (c)
-                   {
-                   case 'D':
-                   case 'X':
-                     INSERT_OPERAND (mips_opts.micromips, FD, *ip, regno);
-                     break;
-
-                   case 'V':
-                   case 'S':
-                   case 'Y':
-                     INSERT_OPERAND (mips_opts.micromips, FS, *ip, regno);
-                     break;
-
-                   case 'Q':
-                     /* This is like 'Z', but also needs to fix the MDMX
-                        vector/scalar select bits.  Note that the
-                        scalar immediate case is handled above.  */
-                     if (*s == '[')
-                       {
-                         int is_qh = (ip->insn_opcode & (1 << OP_SH_VSEL));
-                         int max_el = (is_qh ? 3 : 7);
-                         s++;
-                         my_getExpression(&imm_expr, s);
-                         check_absolute_expr (ip, &imm_expr);
-                         s = expr_end;
-                         if (imm_expr.X_add_number > max_el)
-                           as_bad (_("Bad element selector %ld"),
-                                   (long) imm_expr.X_add_number);
-                         imm_expr.X_add_number &= max_el;
-                         ip->insn_opcode |= (imm_expr.X_add_number
-                                             << (OP_SH_VSEL +
-                                                 (is_qh ? 2 : 1)));
-                         imm_expr.X_op = O_absent;
-                         if (*s != ']')
-                           as_warn (_("Expecting ']' found '%s'"), s);
-                         else
-                           s++;
-                       }
-                     else
-                        {
-                          if (ip->insn_opcode & (OP_MASK_VSEL << OP_SH_VSEL))
-                            ip->insn_opcode |= (MDMX_FMTSEL_VEC_QH
-                                               << OP_SH_VSEL);
-                         else
-                           ip->insn_opcode |= (MDMX_FMTSEL_VEC_OB <<
-                                               OP_SH_VSEL);
-                       }
-                      /* Fall through.  */
-                   case 'W':
-                   case 'T':
-                   case 'Z':
-                     INSERT_OPERAND (mips_opts.micromips, FT, *ip, regno);
-                     break;
-
-                   case 'R':
-                     INSERT_OPERAND (mips_opts.micromips, FR, *ip, regno);
-                     break;
-                   }
-                 lastregno = regno;
-                 continue;
-               }
-
-             switch (*args++)
-               {
-               case 'V':
-                 INSERT_OPERAND (mips_opts.micromips, FS, *ip, lastregno);
-                 continue;
-
-               case 'W':
-                 INSERT_OPERAND (mips_opts.micromips, FT, *ip, lastregno);
-                 continue;
-               }
+             /* 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':
-             my_getExpression (&imm_expr, s);
-             if (imm_expr.X_op != O_big
-                 && imm_expr.X_op != O_constant)
+             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);
-             s = expr_end;
              continue;
 
            case 'A':
-             my_getExpression (&offset_expr, s);
-             normalize_address_expr (&offset_expr);
-             *imm_reloc = BFD_RELOC_32;
-             s = expr_end;
-             continue;
-
-           case 'F':
-           case 'L':
-           case 'f':
-           case 'l':
-             {
-               int f64;
-               int using_gprs;
-               char *save_in;
-               char *err;
-               unsigned char temp[8];
-               int len;
-               unsigned int length;
-               segT seg;
-               subsegT subseg;
-               char *p;
-
-               /* These only appear as the last operand in an
-                  instruction, and every instruction that accepts
-                  them in any variant accepts them in all variants.
-                  This means we don't have to worry about backing out
-                  any changes if the instruction does not match.
-
-                  The difference between them is the size of the
-                  floating point constant and where it goes.  For 'F'
-                  and 'L' the constant is 64 bits; for 'f' and 'l' it
-                  is 32 bits.  Where the constant is placed is based
-                  on how the MIPS assembler does things:
-                   F -- .rdata
-                   L -- .lit8
-                   f -- immediate value
-                   l -- .lit4
-
-                   The .lit4 and .lit8 sections are only used if
-                   permitted by the -G argument.
-
-                   The code below needs to know whether the target register
-                   is 32 or 64 bits wide.  It relies on the fact 'f' and
-                   'F' are used with GPR-based instructions and 'l' and
-                   'L' are used with FPR-based instructions.  */
-
-               f64 = *args == 'F' || *args == 'L';
-               using_gprs = *args == 'F' || *args == 'f';
-
-               save_in = input_line_pointer;
-               input_line_pointer = s;
-               err = md_atof (f64 ? 'd' : 'f', (char *) temp, &len);
-               length = len;
-               s = input_line_pointer;
-               input_line_pointer = save_in;
-               if (err != NULL && *err != '\0')
-                 {
-                   as_bad (_("Bad floating point constant: %s"), err);
-                   memset (temp, '\0', sizeof temp);
-                   length = f64 ? 8 : 4;
-                 }
-
-               gas_assert (length == (unsigned) (f64 ? 8 : 4));
-
-               if (*args == 'f'
-                   || (*args == 'l'
-                       && (g_switch_value < 4
-                           || (temp[0] == 0 && temp[1] == 0)
-                           || (temp[2] == 0 && temp[3] == 0))))
-                 {
-                   imm_expr.X_op = O_constant;
-                   if (!target_big_endian)
-                     imm_expr.X_add_number = bfd_getl32 (temp);
-                   else
-                     imm_expr.X_add_number = bfd_getb32 (temp);
-                 }
-               else if (length > 4
-                        && !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.  */
-                        && (using_gprs
-                            || !(HAVE_64BIT_FPRS && HAVE_32BIT_GPRS))
-                        && ((temp[0] == 0 && temp[1] == 0)
-                            || (temp[2] == 0 && temp[3] == 0))
-                        && ((temp[4] == 0 && temp[5] == 0)
-                            || (temp[6] == 0 && temp[7] == 0)))
-                 {
-                   /* The value is simple enough to load with a couple of
-                      instructions.  If using 32-bit registers, set
-                      imm_expr to the high order 32 bits and offset_expr to
-                      the low order 32 bits.  Otherwise, set imm_expr to
-                      the entire 64 bit constant.  */
-                   if (using_gprs ? HAVE_32BIT_GPRS : HAVE_32BIT_FPRS)
-                     {
-                       imm_expr.X_op = O_constant;
-                       offset_expr.X_op = O_constant;
-                       if (!target_big_endian)
-                         {
-                           imm_expr.X_add_number = bfd_getl32 (temp + 4);
-                           offset_expr.X_add_number = bfd_getl32 (temp);
-                         }
-                       else
-                         {
-                           imm_expr.X_add_number = bfd_getb32 (temp);
-                           offset_expr.X_add_number = bfd_getb32 (temp + 4);
-                         }
-                       if (offset_expr.X_add_number == 0)
-                         offset_expr.X_op = O_absent;
-                     }
-                   else if (sizeof (imm_expr.X_add_number) > 4)
-                     {
-                       imm_expr.X_op = O_constant;
-                       if (!target_big_endian)
-                         imm_expr.X_add_number = bfd_getl64 (temp);
-                       else
-                         imm_expr.X_add_number = bfd_getb64 (temp);
-                     }
-                   else
-                     {
-                       imm_expr.X_op = O_big;
-                       imm_expr.X_add_number = 4;
-                       if (!target_big_endian)
-                         {
-                           generic_bignum[0] = bfd_getl16 (temp);
-                           generic_bignum[1] = bfd_getl16 (temp + 2);
-                           generic_bignum[2] = bfd_getl16 (temp + 4);
-                           generic_bignum[3] = bfd_getl16 (temp + 6);
-                         }
-                       else
-                         {
-                           generic_bignum[0] = bfd_getb16 (temp + 6);
-                           generic_bignum[1] = bfd_getb16 (temp + 4);
-                           generic_bignum[2] = bfd_getb16 (temp + 2);
-                           generic_bignum[3] = bfd_getb16 (temp);
-                         }
-                     }
-                 }
-               else
-                 {
-                   const char *newname;
-                   segT new_seg;
-
-                   /* Switch to the right section.  */
-                   seg = now_seg;
-                   subseg = now_subseg;
-                   switch (*args)
-                     {
-                     default: /* unused default case avoids warnings.  */
-                     case 'L':
-                       newname = RDATA_SECTION_NAME;
-                       if (g_switch_value >= 8)
-                         newname = ".lit8";
-                       break;
-                     case 'F':
-                       newname = RDATA_SECTION_NAME;
-                       break;
-                     case 'l':
-                       gas_assert (g_switch_value >= 4);
-                       newname = ".lit4";
-                       break;
-                     }
-                   new_seg = subseg_new (newname, (subsegT) 0);
-                   if (IS_ELF)
-                     bfd_set_section_flags (stdoutput, new_seg,
-                                            (SEC_ALLOC
-                                             | SEC_LOAD
-                                             | SEC_READONLY
-                                             | SEC_DATA));
-                   frag_align (*args == 'l' ? 2 : 3, 0, 0);
-                   if (IS_ELF && strncmp (TARGET_OS, "elf", 3) != 0)
-                     record_alignment (new_seg, 4);
-                   else
-                     record_alignment (new_seg, *args == 'l' ? 2 : 3);
-                   if (seg == now_seg)
-                     as_bad (_("Can't use floating point insn in this section"));
-
-                   /* Set the argument to the current address in the
-                      section.  */
-                   offset_expr.X_op = O_symbol;
-                   offset_expr.X_add_symbol = symbol_temp_new_now ();
-                   offset_expr.X_add_number = 0;
-
-                   /* Put the floating point number into the section.  */
-                   p = frag_more ((int) length);
-                   memcpy (p, temp, length);
-
-                   /* Switch back to the original section.  */
-                   subseg_set (seg, subseg);
-                 }
-             }
-             continue;
-
-           case 'i':           /* 16-bit unsigned immediate.  */
-           case 'j':           /* 16-bit signed immediate.  */
-             *imm_reloc = BFD_RELOC_LO16;
-             if (my_getSmallExpression (&imm_expr, imm_reloc, s) == 0)
-               {
-                 int more;
-                 offsetT minval, maxval;
-
-                 more = (insn + 1 < past
-                         && strcmp (insn->name, insn[1].name) == 0);
-
-                 /* If the expression was written as an unsigned number,
-                    only treat it as signed if there are no more
-                    alternatives.  */
-                 if (more
-                     && *args == 'j'
-                     && sizeof (imm_expr.X_add_number) <= 4
-                     && imm_expr.X_op == O_constant
-                     && imm_expr.X_add_number < 0
-                     && imm_expr.X_unsigned
-                     && HAVE_64BIT_GPRS)
-                   break;
-
-                 /* For compatibility with older assemblers, we accept
-                    0x8000-0xffff as signed 16-bit numbers when only
-                    signed numbers are allowed.  */
-                 if (*args == 'i')
-                   minval = 0, maxval = 0xffff;
-                 else if (more)
-                   minval = -0x8000, maxval = 0x7fff;
-                 else
-                   minval = -0x8000, maxval = 0xffff;
-
-                 if (imm_expr.X_op != O_constant
-                     || imm_expr.X_add_number < minval
-                     || imm_expr.X_add_number > maxval)
-                   {
-                     if (more)
-                       break;
-                     if (imm_expr.X_op == O_constant
-                         || imm_expr.X_op == O_big)
-                       as_bad (_("Expression out of range"));
-                   }
-               }
-             s = expr_end;
-             continue;
-
-           case 'o':           /* 16-bit offset.  */
-             offset_reloc[0] = BFD_RELOC_LO16;
-             offset_reloc[1] = BFD_RELOC_UNUSED;
-             offset_reloc[2] = BFD_RELOC_UNUSED;
-
-             /* Check whether there is only a single bracketed expression
-                left.  If so, it must be the base register and the
-                constant must be zero.  */
-             if (*s == '(' && strchr (s + 1, '(') == 0)
+             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;
-                 continue;
                }
-
-             /* If this value won't fit into a 16 bit offset, then go
-                find a macro that will generate the 32 bit offset
-                code pattern.  */
-             if (my_getSmallExpression (&offset_expr, offset_reloc, s) == 0
-                 && (offset_expr.X_op != O_constant
-                     || offset_expr.X_add_number >= 0x8000
-                     || offset_expr.X_add_number < -0x8000))
-               break;
-
-             s = expr_end;
-             continue;
-
-           case 'p':           /* PC-relative offset.  */
-             *offset_reloc = BFD_RELOC_16_PCREL_S2;
-             my_getExpression (&offset_expr, s);
-             s = expr_end;
+             else if (match_expression (&arg, &offset_expr, offset_reloc))
+               normalize_address_expr (&offset_expr);
+             else
+               insn_error = _("absolute expression required");
              continue;
 
-           case 'u':           /* Upper 16 bits.  */
-             *imm_reloc = BFD_RELOC_LO16;
-             if (my_getSmallExpression (&imm_expr, imm_reloc, s) == 0
-                 && imm_expr.X_op == O_constant
-                 && (imm_expr.X_add_number < 0
-                     || imm_expr.X_add_number >= 0x10000))
-               as_bad (_("lui expression (%lu) not in range 0..65535"),
-                       (unsigned long) imm_expr.X_add_number);
-             s = expr_end;
+           case 'F':
+             if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+                                        8, TRUE))
+               insn_error = _("floating-point expression required");
              continue;
 
-           case 'a':           /* 26-bit address.  */
-             *offset_reloc = BFD_RELOC_MIPS_JMP;
-             my_getExpression (&offset_expr, s);
-             s = expr_end;
+           case 'L':
+             if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+                                        8, FALSE))
+               insn_error = _("floating-point expression required");
              continue;
 
-           case 'N':           /* 3-bit branch condition code.  */
-           case 'M':           /* 3-bit compare condition code.  */
-             rtype = RTYPE_CCC;
-             if (ip->insn_mo->pinfo & (FP_D | FP_S))
-               rtype |= RTYPE_FCC;
-             if (!reg_lookup (&s, rtype, &regno))
-               break;
-             if ((strcmp (str + strlen (str) - 3, ".ps") == 0
-                  || strcmp (str + strlen (str) - 5, "any2f") == 0
-                  || strcmp (str + strlen (str) - 5, "any2t") == 0)
-                 && (regno & 1) != 0)
-               as_warn (_("Condition code register should be even for %s, "
-                          "was %d"),
-                        str, regno);
-             if ((strcmp (str + strlen (str) - 5, "any4f") == 0
-                  || strcmp (str + strlen (str) - 5, "any4t") == 0)
-                 && (regno & 3) != 0)
-               as_warn (_("Condition code register should be 0 or 4 for %s, "
-                          "was %d"),
-                        str, regno);
-             if (*args == 'N')
-               INSERT_OPERAND (mips_opts.micromips, BCC, *ip, regno);
-             else
-               INSERT_OPERAND (mips_opts.micromips, CCC, *ip, regno);
+           case 'f':
+             if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+                                        4, TRUE))
+               insn_error = _("floating-point expression required");
              continue;
 
-           case 'H':
-             if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
-               s += 2;
-             if (ISDIGIT (*s))
-               {
-                 c = 0;
-                 do
-                   {
-                     c *= 10;
-                     c += *s - '0';
-                     ++s;
-                   }
-                 while (ISDIGIT (*s));
-               }
-             else
-               c = 8; /* Invalid sel value.  */
-
-             if (c > 7)
-               as_bad (_("Invalid coprocessor sub-selection value (0-7)"));
-             INSERT_OPERAND (mips_opts.micromips, SEL, *ip, c);
+           case 'l':
+             if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+                                        4, FALSE))
+               insn_error = _("floating-point expression required");
              continue;
 
-           case 'e':
-             gas_assert (!mips_opts.micromips);
-             /* Must be at least one digit.  */
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-
-             if ((unsigned long) imm_expr.X_add_number
-                 > (unsigned long) OP_MASK_VECBYTE)
-               {
-                 as_bad (_("bad byte vector index (%ld)"),
-                          (long) imm_expr.X_add_number);
-                 imm_expr.X_add_number = 0;
-               }
+             /* ??? 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;
 
-             INSERT_OPERAND (0, VECBYTE, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+           case 'u':
+             /* There are no macro implementations for out-of-range values.  */
+             arg.allow_nonconst = TRUE;
+             break;
 
-           case '%':
-             gas_assert (!mips_opts.micromips);
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
+           case 'o':
+             /* There should always be a macro implementation.  */
+             arg.allow_nonconst = FALSE;
+             break;
 
-             if ((unsigned long) imm_expr.X_add_number
-                 > (unsigned long) OP_MASK_VECALIGN)
-               {
-                 as_bad (_("bad byte vector index (%ld)"),
-                          (long) imm_expr.X_add_number);
-                 imm_expr.X_add_number = 0;
-               }
+           case 'p':
+             *offset_reloc = BFD_RELOC_16_PCREL_S2;
+             break;
 
-             INSERT_OPERAND (0, VECALIGN, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+           case 'a':
+             *offset_reloc = BFD_RELOC_MIPS_JMP;
+             break;
 
-           case 'm':           /* Opcode extension character.  */
+           case 'm':
              gas_assert (mips_opts.micromips);
-             c = *++args;
+             c = args[1];
              switch (c)
                {
-               case 'r':
-                 if (strncmp (s, "$pc", 3) == 0)
-                   {
-                     s += 3;
-                     continue;
-                   }
-                 break;
-
-               case 'a':
-               case 'b':
+               case 't':
                case 'c':
-               case 'd':
                case 'e':
-               case 'f':
-               case 'g':
-               case 'h':
-               case 'i':
-               case 'j':
-               case 'l':
-               case 'm':
-               case 'n':
-               case 'p':
-               case 'q':
-               case 's':
-               case 't':
-               case 'x':
-               case 'y':
-               case 'z':
-                 s_reset = s;
-                 ok = reg_lookup (&s, RTYPE_NUM | RTYPE_GP, &regno);
-                 if (regno == AT && mips_opts.at)
-                   {
-                     if (mips_opts.at == ATREG)
-                       as_warn (_("Used $at without \".set noat\""));
-                     else
-                       as_warn (_("Used $%u with \".set at=$%u\""),
-                                regno, mips_opts.at);
-                   }
-                 if (!ok)
-                   {
-                     if (c == 'c')
-                       {
-                         gas_assert (args[1] == ',');
-                         regno = lastregno;
-                         ++args;
-                       }
-                     else if (c == 't')
-                       {
-                         gas_assert (args[1] == ',');
-                         ++args;
-                         continue;                     /* Nothing to do.  */
-                       }
-                     else
-                       break;
-                   }
-
-                 if (c == 'j' && !strncmp (ip->insn_mo->name, "jalr", 4))
-                   {
-                     if (regno == lastregno)
-                       {
-                         insn_error
-                           = _("Source and destination must be different");
-                         continue;
-                       }
-                     if (regno == 31 && lastregno == 0xffffffff)
-                       {
-                         insn_error
-                           = _("A destination register must be supplied");
-                         continue;
-                       }
-                   }
-
-                 if (*s == ' ')
-                   ++s;
-                 if (args[1] != *s)
-                   {
-                     if (c == 'e')
-                       {
-                         gas_assert (args[1] == ',');
-                         regno = lastregno;
-                         s = s_reset;
-                         ++args;
-                       }
-                     else if (c == 't')
-                       {
-                         gas_assert (args[1] == ',');
-                         s = s_reset;
-                         ++args;
-                         continue;                     /* Nothing to do.  */
-                       }
-                   }
-
-                 /* Make sure regno is the same as lastregno.  */
-                 if (c == 't' && regno != lastregno)
-                   break;
-
-                 /* Make sure regno is the same as destregno.  */
-                 if (c == 'x' && regno != destregno)
-                   break;
-
-                 /* We need to save regno, before regno maps to the
-                    microMIPS register encoding.  */
-                 lastregno = regno;
-
-                 if (c == 'f')
-                   destregno = regno;
-
-                 switch (c)
-                   {
-                     case 'a':
-                       if (regno != GP)
-                         regno = ILLEGAL_REG;
-                       break;
-
-                     case 'b':
-                       regno = mips32_to_micromips_reg_b_map[regno];
-                       break;
-
-                     case 'c':
-                       regno = mips32_to_micromips_reg_c_map[regno];
-                       break;
-
-                     case 'd':
-                       regno = mips32_to_micromips_reg_d_map[regno];
-                       break;
-
-                     case 'e':
-                       regno = mips32_to_micromips_reg_e_map[regno];
-                       break;
-
-                     case 'f':
-                       regno = mips32_to_micromips_reg_f_map[regno];
-                       break;
-
-                     case 'g':
-                       regno = mips32_to_micromips_reg_g_map[regno];
-                       break;
-
-                     case 'h':
-                       regno = mips32_to_micromips_reg_h_map[regno];
-                       break;
-
-                     case 'i':
-                       switch (EXTRACT_OPERAND (1, MI, *ip))
-                         {
-                           case 4:
-                             if (regno == 21)
-                               regno = 3;
-                             else if (regno == 22)
-                               regno = 4;
-                             else if (regno == 5)
-                               regno = 5;
-                             else if (regno == 6)
-                               regno = 6;
-                             else if (regno == 7)
-                               regno = 7;
-                             else
-                               regno = ILLEGAL_REG;
-                             break;
-
-                           case 5:
-                             if (regno == 6)
-                               regno = 0;
-                             else if (regno == 7)
-                               regno = 1;
-                             else
-                               regno = ILLEGAL_REG;
-                             break;
-
-                           case 6:
-                             if (regno == 7)
-                               regno = 2;
-                             else
-                               regno = ILLEGAL_REG;
-                             break;
-
-                           default:
-                             regno = ILLEGAL_REG;
-                             break;
-                         }
-                       break;
-
-                     case 'l':
-                       regno = mips32_to_micromips_reg_l_map[regno];
-                       break;
-
-                     case 'm':
-                       regno = mips32_to_micromips_reg_m_map[regno];
-                       break;
-
-                     case 'n':
-                       regno = mips32_to_micromips_reg_n_map[regno];
-                       break;
-
-                     case 'q':
-                       regno = mips32_to_micromips_reg_q_map[regno];
-                       break;
-
-                     case 's':
-                       if (regno != SP)
-                         regno = ILLEGAL_REG;
-                       break;
-
-                     case 'y':
-                       if (regno != 31)
-                         regno = ILLEGAL_REG;
-                       break;
-
-                     case 'z':
-                       if (regno != ZERO)
-                         regno = ILLEGAL_REG;
-                       break;
-
-                     case 'j': /* Do nothing.  */
-                     case 'p':
-                     case 't':
-                     case 'x':
-                       break;
-
-                     default:
-                       abort ();
-                   }
-
-                 if (regno == ILLEGAL_REG)
-                   break;
-
-                 switch (c)
-                   {
-                     case 'b':
-                       INSERT_OPERAND (1, MB, *ip, regno);
-                       break;
-
-                     case 'c':
-                       INSERT_OPERAND (1, MC, *ip, regno);
-                       break;
-
-                     case 'd':
-                       INSERT_OPERAND (1, MD, *ip, regno);
-                       break;
-
-                     case 'e':
-                       INSERT_OPERAND (1, ME, *ip, regno);
-                       break;
-
-                     case 'f':
-                       INSERT_OPERAND (1, MF, *ip, regno);
-                       break;
-
-                     case 'g':
-                       INSERT_OPERAND (1, MG, *ip, regno);
-                       break;
-
-                     case 'h':
-                       INSERT_OPERAND (1, MH, *ip, regno);
-                       break;
-
-                     case 'i':
-                       INSERT_OPERAND (1, MI, *ip, regno);
-                       break;
-
-                     case 'j':
-                       INSERT_OPERAND (1, MJ, *ip, regno);
-                       break;
-
-                     case 'l':
-                       INSERT_OPERAND (1, ML, *ip, regno);
-                       break;
-
-                     case 'm':
-                       INSERT_OPERAND (1, MM, *ip, regno);
-                       break;
-
-                     case 'n':
-                       INSERT_OPERAND (1, MN, *ip, regno);
-                       break;
-
-                     case 'p':
-                       INSERT_OPERAND (1, MP, *ip, regno);
-                       break;
-
-                     case 'q':
-                       INSERT_OPERAND (1, MQ, *ip, regno);
-                       break;
-
-                     case 'a': /* Do nothing.  */
-                     case 's': /* Do nothing.  */
-                     case 't': /* Do nothing.  */
-                     case 'x': /* Do nothing.  */
-                     case 'y': /* Do nothing.  */
-                     case 'z': /* Do nothing.  */
-                       break;
-
-                     default:
-                       abort ();
-                   }
-                 continue;
-
-               case 'A':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
-
-                   /* Check whether there is only a single bracketed
-                      expression left.  If so, it must be the base register
-                      and the constant must be zero.  */
-                   if (*s == '(' && strchr (s + 1, '(') == 0)
-                     {
-                       INSERT_OPERAND (1, IMMA, *ip, 0);
-                       continue;
-                     }
-
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, -64, 64, 2))
-                     break;
-
-                   imm = ep.X_add_number >> 2;
-                   INSERT_OPERAND (1, IMMA, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
-
-               case 'B':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
-
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || ep.X_op != O_constant)
-                     break;
-
-                   for (imm = 0; imm < 8; imm++)
-                     if (micromips_imm_b_map[imm] == ep.X_add_number)
-                       break;
-                   if (imm >= 8)
-                     break;
-
-                   INSERT_OPERAND (1, IMMB, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
-
-               case 'C':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
-
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || ep.X_op != O_constant)
-                     break;
-
-                   for (imm = 0; imm < 16; imm++)
-                     if (micromips_imm_c_map[imm] == ep.X_add_number)
-                       break;
-                   if (imm >= 16)
-                     break;
-
-                   INSERT_OPERAND (1, IMMC, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
-
-               case 'D':       /* pc relative offset */
-               case 'E':       /* pc relative offset */
-                 my_getExpression (&offset_expr, s);
-                 if (offset_expr.X_op == O_register)
-                   break;
+                 /* 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;
-                 s = expr_end;
-                 continue;
-
-               case 'F':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
-
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, 0, 16, 0))
-                     break;
-
-                   imm = ep.X_add_number;
-                   INSERT_OPERAND (1, IMMF, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
-
-               case 'G':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
-
-                   /* Check whether there is only a single bracketed
-                      expression left.  If so, it must be the base register
-                      and the constant must be zero.  */
-                   if (*s == '(' && strchr (s + 1, '(') == 0)
-                     {
-                       INSERT_OPERAND (1, IMMG, *ip, 0);
-                       continue;
-                     }
-
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, -1, 15, 0))
-                     break;
-
-                   imm = ep.X_add_number & 15;
-                   INSERT_OPERAND (1, IMMG, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
-
-               case 'H':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
-
-                   /* Check whether there is only a single bracketed
-                      expression left.  If so, it must be the base register
-                      and the constant must be zero.  */
-                   if (*s == '(' && strchr (s + 1, '(') == 0)
-                     {
-                       INSERT_OPERAND (1, IMMH, *ip, 0);
-                       continue;
-                     }
-
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, 0, 16, 1))
-                     break;
-
-                   imm = ep.X_add_number >> 1;
-                   INSERT_OPERAND (1, IMMH, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
-
-               case 'I':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
-
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, -1, 127, 0))
-                     break;
-
-                   imm = ep.X_add_number & 127;
-                   INSERT_OPERAND (1, IMMI, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
-
-               case 'J':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
-
-                   /* Check whether there is only a single bracketed
-                      expression left.  If so, it must be the base register
-                      and the constant must be zero.  */
-                   if (*s == '(' && strchr (s + 1, '(') == 0)
-                     {
-                       INSERT_OPERAND (1, IMMJ, *ip, 0);
-                       continue;
-                     }
-
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, 0, 16, 2))
-                     break;
-
-                   imm = ep.X_add_number >> 2;
-                   INSERT_OPERAND (1, IMMJ, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
-
-               case 'L':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
-
-                   /* Check whether there is only a single bracketed
-                      expression left.  If so, it must be the base register
-                      and the constant must be zero.  */
-                   if (*s == '(' && strchr (s + 1, '(') == 0)
-                     {
-                       INSERT_OPERAND (1, IMML, *ip, 0);
-                       continue;
-                     }
-
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, 0, 16, 0))
-                     break;
-
-                   imm = ep.X_add_number;
-                   INSERT_OPERAND (1, IMML, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
-
-               case 'M':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
-
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, 1, 9, 0))
-                     break;
-
-                   imm = ep.X_add_number & 7;
-                   INSERT_OPERAND (1, IMMM, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
-
-               case 'N':       /* Register list for lwm and swm.  */
-                 {
-                   /* A comma-separated list of registers and/or
-                      dash-separated contiguous ranges including
-                      both ra and a set of one or more registers
-                      starting at s0 up to s3 which have to be
-                      consecutive, e.g.:
-
-                      s0, ra
-                      s0, s1, ra, s2, s3
-                      s0-s2, ra
-
-                      and any permutations of these.  */
-                   unsigned int reglist;
-                   int imm;
-
-                   if (!reglist_lookup (&s, RTYPE_NUM | RTYPE_GP, &reglist))
-                     break;
-
-                   if ((reglist & 0xfff1ffff) != 0x80010000)
-                     break;
-
-                   reglist = (reglist >> 17) & 7;
-                   reglist += 1;
-                   if ((reglist & -reglist) != reglist)
-                     break;
-
-                   imm = ffs (reglist) - 1;
-                   INSERT_OPERAND (1, IMMN, *ip, imm);
-                 }
-                 continue;
-
-               case 'O':       /* sdbbp 4-bit code.  */
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
-
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, 0, 16, 0))
-                     break;
-
-                   imm = ep.X_add_number;
-                   INSERT_OPERAND (1, IMMO, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
-
-               case 'P':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
-
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, 0, 32, 2))
-                     break;
-
-                   imm = ep.X_add_number >> 2;
-                   INSERT_OPERAND (1, IMMP, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
-
-               case 'Q':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
-
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, -0x400000, 0x400000, 2))
-                     break;
-
-                   imm = ep.X_add_number >> 2;
-                   INSERT_OPERAND (1, IMMQ, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
-
-               case 'U':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
-
-                   /* Check whether there is only a single bracketed
-                      expression left.  If so, it must be the base register
-                      and the constant must be zero.  */
-                   if (*s == '(' && strchr (s + 1, '(') == 0)
-                     {
-                       INSERT_OPERAND (1, IMMU, *ip, 0);
-                       continue;
-                     }
-
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, 0, 32, 2))
-                     break;
-
-                   imm = ep.X_add_number >> 2;
-                   INSERT_OPERAND (1, IMMU, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
-
-               case 'W':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
-
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, 0, 64, 2))
-                     break;
-
-                   imm = ep.X_add_number >> 2;
-                   INSERT_OPERAND (1, IMMW, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
-
-               case 'X':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
-
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, -8, 8, 0))
-                     break;
-
-                   imm = ep.X_add_number;
-                   INSERT_OPERAND (1, IMMX, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
-
-               case 'Y':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
-
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || expr_const_in_range (&ep, -2, 2, 2)
-                       || !expr_const_in_range (&ep, -258, 258, 2))
-                     break;
-
-                   imm = ep.X_add_number >> 2;
-                   imm = ((imm >> 1) & ~0xff) | (imm & 0xff);
-                   INSERT_OPERAND (1, IMMY, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
-
-               case 'Z':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, 0, 1, 0))
-                     break;
-                 }
-                 s = expr_end;
-                 continue;
-
-               default:
-                 as_bad (_("Internal error: bad microMIPS opcode "
-                           "(unknown extension operand type `m%c'): %s %s"),
-                         *args, insn->name, insn->args);
-                 /* Further processing is fruitless.  */
-                 return;
+                 break;
                }
              break;
+           }
 
-           case 'n':           /* Register list for 32-bit lwm and swm.  */
-             gas_assert (mips_opts.micromips);
-             {
-               /* A comma-separated list of registers and/or
-                  dash-separated contiguous ranges including
-                  at least one of ra and a set of one or more
-                  registers starting at s0 up to s7 and then
-                  s8 which have to be consecutive, e.g.:
-
-                  ra
-                  s0
-                  ra, s0, s1, s2
-                  s0-s8
-                  s0-s5, ra
-
-                  and any permutations of these.  */
-               unsigned int reglist;
-               int imm;
-               int ra;
-
-               if (!reglist_lookup (&s, RTYPE_NUM | RTYPE_GP, &reglist))
-                 break;
-
-               if ((reglist & 0x3f00ffff) != 0)
-                 break;
+         operand = (mips_opts.micromips
+                    ? decode_micromips_operand (args)
+                    : decode_mips_operand (args));
+         if (!operand)
+           abort ();
 
-               ra = (reglist >> 27) & 0x10;
-               reglist = ((reglist >> 22) & 0x100) | ((reglist >> 16) & 0xff);
-               reglist += 1;
-               if ((reglist & -reglist) != reglist)
-                 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;
+           }
 
-               imm = (ffs (reglist) - 1) | ra;
-               INSERT_OPERAND (1, RT, *ip, imm);
-               imm_expr.X_op = O_absent;
-             }
-             continue;
+         if (!match_operand (&arg, operand))
+           break;
 
-           case '|':           /* 4-bit trap code.  */
-             gas_assert (mips_opts.micromips);
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if ((unsigned long) imm_expr.X_add_number
-                 > MICROMIPSOP_MASK_TRAP)
-               as_bad (_("Trap code (%lu) for %s not in 0..15 range"),
-                       (unsigned long) imm_expr.X_add_number,
-                       ip->insn_mo->name);
-             INSERT_OPERAND (1, TRAP, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+         /* Skip prefixes.  */
+         if (*args == '+' || *args == 'm')
+           args++;
 
-           default:
-             as_bad (_("Bad char = '%c'\n"), *args);
-             abort ();
-           }
-         break;
+         continue;
        }
       /* Args don't match.  */
-      s = argsStart;
       insn_error = _("Illegal operands");
-      if (insn + 1 < past && !strcmp (insn->name, insn[1].name))
+      if (more_alts)
        {
          ++insn;
          continue;
        }
-      else if (wrong_delay_slot_insns && need_delay_slot_ok)
+      if (wrong_delay_slot_insns && need_delay_slot_ok)
        {
          gas_assert (firstinsn);
          need_delay_slot_ok = FALSE;
@@ -13932,18 +12459,14 @@ mips_ip (char *str, struct mips_cl_insn *ip)
          insn = firstinsn;
          continue;
        }
+      obstack_free (&mips_operand_tokens, tokens);
       return;
     }
 }
 
-#define SKIP_SPACE_TABS(S) { while (*(S) == ' ' || *(S) == '\t') ++(S); }
-
-/* This routine assembles an instruction into its binary format when
-   assembling for the mips16.  As a side effect, it sets one of the
-   global variables imm_reloc or offset_reloc to the type of relocation
-   to do if one of the operands is an address expression.  It also sets
-   forced_insn_length to the resulting instruction size in bytes if the
-   user explicitly requested a small or extended instruction.  */
+/* As for mips_ip, but used when assembling MIPS16 code.
+   Also set forced_insn_length to the resulting instruction size in
+   bytes if the user explicitly requested a small or extended instruction.  */
 
 static void
 mips16_ip (char *str, struct mips_cl_insn *ip)
@@ -13951,11 +12474,11 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
   char *s;
   const char *args;
   struct mips_opcode *insn;
-  char *argsstart;
-  unsigned int regno;
-  unsigned int lastregno = 0;
-  char *s_reset;
-  size_t i;
+  const struct mips_operand *operand;
+  const struct mips_operand *ext_operand;
+  struct mips_arg_info arg;
+  struct mips_operand_token *tokens;
+  bfd_boolean optional_reg;
 
   insn_error = NULL;
 
@@ -14002,18 +12525,24 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
       return;
     }
 
-  argsstart = s;
+  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 (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes]
-             && strcmp (insn->name, insn[1].name) == 0)
+         if (more_alts)
            {
              ++insn;
              continue;
@@ -14029,574 +12558,197 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
                           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;
-      imm_reloc[0] = BFD_RELOC_UNUSED;
-      imm_reloc[1] = BFD_RELOC_UNUSED;
-      imm_reloc[2] = BFD_RELOC_UNUSED;
       imm2_expr.X_op = O_absent;
       offset_expr.X_op = O_absent;
       offset_reloc[0] = BFD_RELOC_UNUSED;
       offset_reloc[1] = BFD_RELOC_UNUSED;
       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 (*s == ' ')
-           ++s;
-
-         /* In this switch statement we call break if we did not find
-             a match, continue if we did find a match, or return if we
-             are done.  */
-
-         c = *args;
-         switch (c)
+         if (arg.token->type == OT_END)
            {
-           case '\0':
-             if (*s == '\0')
-               {
-                 offsetT value;
-
-                 /* Stuff the immediate value in now, if we can.  */
-                 if (imm_expr.X_op == O_constant
-                     && *imm_reloc > BFD_RELOC_UNUSED
-                     && insn->pinfo != INSN_MACRO
-                     && calculate_reloc (*offset_reloc,
-                                         imm_expr.X_add_number, &value))
-                   {
-                     mips16_immed (NULL, 0, *imm_reloc - BFD_RELOC_UNUSED,
-                                   *offset_reloc, value, forced_insn_length,
-                                   &ip->insn_opcode);
-                     imm_expr.X_op = O_absent;
-                     *imm_reloc = BFD_RELOC_UNUSED;
-                     *offset_reloc = BFD_RELOC_UNUSED;
-                   }
-
-                 return;
-               }
-             break;
+             offsetT value;
 
-           case ',':
-             if (*s++ == c)
-               continue;
-             s--;
-             switch (*++args)
-               {
-               case 'v':
-                 MIPS16_INSERT_OPERAND (RX, *ip, lastregno);
-                 continue;
-               case 'w':
-                 MIPS16_INSERT_OPERAND (RY, *ip, lastregno);
-                 continue;
-               }
-             break;
+             /* 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;
+                 }
 
-           case '(':
-           case ')':
-             if (*s++ == c)
-               continue;
-             break;
+             /* Fail the match if there were too few operands.  */
+             if (*args)
+               break;
 
-           case 'v':
-           case 'w':
-             if (s[0] != '$')
-               {
-                 if (c == 'v')
-                   MIPS16_INSERT_OPERAND (RX, *ip, lastregno);
-                 else
-                   MIPS16_INSERT_OPERAND (RY, *ip, lastregno);
-                 ++args;
-                 continue;
-               }
-             /* Fall through.  */
-           case 'x':
-           case 'y':
-           case 'z':
-           case 'Z':
-           case '0':
-           case 'S':
-           case 'R':
-           case 'X':
-           case 'Y':
-             s_reset = s;
-             if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, &regno))
+             /* Successful match.  Stuff the immediate value in now, if
+                we can.  */
+             if (insn->pinfo == INSN_MACRO)
                {
-                 if (c == 'v' || c == 'w')
-                   {
-                     if (c == 'v')
-                       MIPS16_INSERT_OPERAND (RX, *ip, lastregno);
-                     else
-                       MIPS16_INSERT_OPERAND (RY, *ip, lastregno);
-                     ++args;
-                     continue;
-                   }
-                 break;
+                 gas_assert (relax_char == 0);
+                 gas_assert (*offset_reloc == BFD_RELOC_UNUSED);
                }
-
-             if (*s == ' ')
-               ++s;
-             if (args[1] != *s)
+             else if (relax_char
+                      && offset_expr.X_op == O_constant
+                      && calculate_reloc (*offset_reloc,
+                                          offset_expr.X_add_number,
+                                          &value))
                {
-                 if (c == 'v' || c == 'w')
-                   {
-                     regno = mips16_to_32_reg_map[lastregno];
-                     s = s_reset;
-                     ++args;
-                   }
+                 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;
                }
-
-             switch (c)
+             else if (relax_char && *offset_reloc != BFD_RELOC_UNUSED)
                {
-               case 'x':
-               case 'y':
-               case 'z':
-               case 'v':
-               case 'w':
-               case 'Z':
-                 regno = mips32_to_16_reg_map[regno];
-                 break;
-
-               case '0':
-                 if (regno != 0)
-                   regno = ILLEGAL_REG;
-                 break;
-
-               case 'S':
-                 if (regno != SP)
-                   regno = ILLEGAL_REG;
-                 break;
-
-               case 'R':
-                 if (regno != RA)
-                   regno = ILLEGAL_REG;
-                 break;
-
-               case 'X':
-               case 'Y':
-                 if (regno == AT && mips_opts.at)
-                   {
-                     if (mips_opts.at == ATREG)
-                       as_warn (_("used $at without \".set noat\""));
-                     else
-                       as_warn (_("used $%u with \".set at=$%u\""),
-                                regno, mips_opts.at);
-                   }
-                 break;
-
-               default:
-                 abort ();
+                 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;
 
-             if (regno == ILLEGAL_REG)
-               break;
+             check_completed_insn (&arg);
+             obstack_free (&mips_operand_tokens, tokens);
+             return;
+           }
 
-             switch (c)
-               {
-               case 'x':
-               case 'v':
-                 MIPS16_INSERT_OPERAND (RX, *ip, regno);
-                 break;
-               case 'y':
-               case 'w':
-                 MIPS16_INSERT_OPERAND (RY, *ip, regno);
-                 break;
-               case 'z':
-                 MIPS16_INSERT_OPERAND (RZ, *ip, regno);
-                 break;
-               case 'Z':
-                 MIPS16_INSERT_OPERAND (MOVE32Z, *ip, regno);
-               case '0':
-               case 'S':
-               case 'R':
-                 break;
-               case 'X':
-                 MIPS16_INSERT_OPERAND (REGR32, *ip, regno);
-                 break;
-               case 'Y':
-                 regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3);
-                 MIPS16_INSERT_OPERAND (REG32R, *ip, regno);
-                 break;
-               default:
-                 abort ();
-               }
+         /* Fail the match if the line has too many operands.   */
+         if (*args == 0)
+           break;
 
-             lastregno = regno;
-             continue;
+         /* Handle characters that need to match exactly.  */
+         if (*args == '(' || *args == ')' || *args == ',')
+           {
+             if (match_char (&arg, *args))
+               continue;
+             break;
+           }
 
-           case 'P':
-             if (strncmp (s, "$pc", 3) == 0)
-               {
-                 s += 3;
-                 continue;
-               }
+         arg.opnum += 1;
+         optional_reg = FALSE;
+         c = *args;
+         switch (c)
+           {
+           case 'v':
+           case 'w':
+             optional_reg = (args[1] == ',');
              break;
 
-           case '5':
-           case 'H':
-           case 'W':
-           case 'D':
-           case 'j':
-           case 'V':
-           case 'C':
-           case 'U':
-           case 'k':
-           case 'K':
-             i = my_getSmallExpression (&imm_expr, imm_reloc, s);
-             if (i > 0)
-               {
-                 if (imm_expr.X_op != O_constant)
-                   {
-                     forced_insn_length = 4;
-                     ip->insn_opcode |= MIPS16_EXTEND;
-                   }
-                 else
-                   {
-                     /* We need to relax this instruction.  */
-                     *offset_reloc = *imm_reloc;
-                     *imm_reloc = (int) BFD_RELOC_UNUSED + c;
-                   }
-                 s = expr_end;
-                 continue;
-               }
-             *imm_reloc = BFD_RELOC_UNUSED;
-             /* Fall through.  */
-           case '<':
-           case '>':
-           case '[':
-           case ']':
-           case '4':
-           case '8':
-             my_getExpression (&imm_expr, s);
-             if (imm_expr.X_op == O_register)
-               {
-                 /* What we thought was an expression turned out to
-                     be a register.  */
-
-                 if (s[0] == '(' && args[1] == '(')
-                   {
-                     /* It looks like the expression was omitted
-                        before a register indirection, which means
-                        that the expression is implicitly zero.  We
-                        still set up imm_expr, so that we handle
-                        explicit extensions correctly.  */
-                     imm_expr.X_op = O_constant;
-                     imm_expr.X_add_number = 0;
-                     *imm_reloc = (int) BFD_RELOC_UNUSED + c;
-                     continue;
-                   }
-
-                 break;
-               }
-
-             /* We need to relax this instruction.  */
-             *imm_reloc = (int) BFD_RELOC_UNUSED + c;
-             s = expr_end;
-             continue;
-
            case 'p':
            case 'q':
            case 'A':
            case 'B':
            case 'E':
-             /* We use offset_reloc rather than imm_reloc for the PC
-                 relative operands.  This lets macros with both
-                 immediate and address operands work correctly.  */
-             my_getExpression (&offset_expr, s);
-
-             if (offset_expr.X_op == O_register)
-               break;
-
-             /* We need to relax this instruction.  */
-             *offset_reloc = (int) BFD_RELOC_UNUSED + c;
-             s = expr_end;
-             continue;
+             relax_char = c;
+             break;
 
-           case '6':           /* break code */
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if ((unsigned long) imm_expr.X_add_number > 63)
-               as_warn (_("Invalid value for `%s' (%lu)"),
-                        ip->insn_mo->name,
-                        (unsigned long) imm_expr.X_add_number);
-             MIPS16_INSERT_OPERAND (IMM6, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
+           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':           /* 26 bit address */
-             my_getExpression (&offset_expr, s);
-             s = expr_end;
+           case 'a':
+           case 'i':
              *offset_reloc = BFD_RELOC_MIPS16_JMP;
              ip->insn_opcode <<= 16;
-             continue;
-
-           case 'l':           /* register list for entry macro */
-           case 'L':           /* register list for exit macro */
-             {
-               int mask;
-
-               if (c == 'l')
-                 mask = 0;
-               else
-                 mask = 7 << 3;
-               while (*s != '\0')
-                 {
-                   unsigned int freg, reg1, reg2;
-
-                   while (*s == ' ' || *s == ',')
-                     ++s;
-                   if (reg_lookup (&s, RTYPE_GP | RTYPE_NUM, &reg1))
-                     freg = 0;
-                   else if (reg_lookup (&s, RTYPE_FPU, &reg1))
-                     freg = 1;
-                   else
-                     {
-                       as_bad (_("can't parse register list"));
-                       break;
-                     }
-                   if (*s == ' ')
-                     ++s;
-                   if (*s != '-')
-                     reg2 = reg1;
-                   else
-                     {
-                       ++s;
-                       if (!reg_lookup (&s, freg ? RTYPE_FPU 
-                                        : (RTYPE_GP | RTYPE_NUM), &reg2))
-                         {
-                           as_bad (_("invalid register list"));
-                           break;
-                         }
-                     }
-                   if (freg && reg1 == 0 && reg2 == 0 && c == 'L')
-                     {
-                       mask &= ~ (7 << 3);
-                       mask |= 5 << 3;
-                     }
-                   else if (freg && reg1 == 0 && reg2 == 1 && c == 'L')
-                     {
-                       mask &= ~ (7 << 3);
-                       mask |= 6 << 3;
-                     }
-                   else if (reg1 == 4 && reg2 >= 4 && reg2 <= 7 && c != 'L')
-                     mask |= (reg2 - 3) << 3;
-                   else if (reg1 == 16 && reg2 >= 16 && reg2 <= 17)
-                     mask |= (reg2 - 15) << 1;
-                   else if (reg1 == RA && reg2 == RA)
-                     mask |= 1;
-                   else
-                     {
-                       as_bad (_("invalid register list"));
-                       break;
-                     }
-                 }
-               /* The mask is filled in in the opcode table for the
-                   benefit of the disassembler.  We remove it before
-                   applying the actual mask.  */
-               ip->insn_opcode &= ~ ((7 << 3) << MIPS16OP_SH_IMM6);
-               ip->insn_opcode |= mask << MIPS16OP_SH_IMM6;
-             }
-           continue;
-
-           case 'm':           /* Register list for save insn.  */
-           case 'M':           /* Register list for restore insn.  */
-             {
-               int opcode = ip->insn_opcode;
-               int framesz = 0, seen_framesz = 0;
-               int nargs = 0, statics = 0, sregs = 0;
-
-               while (*s != '\0')
-                 {
-                   unsigned int reg1, reg2;
-
-                   SKIP_SPACE_TABS (s);
-                   while (*s == ',')
-                     ++s;
-                   SKIP_SPACE_TABS (s);
-
-                   my_getExpression (&imm_expr, s);
-                   if (imm_expr.X_op == O_constant)
-                     {
-                       /* Handle the frame size.  */
-                       if (seen_framesz)
-                         {
-                           as_bad (_("more than one frame size in list"));
-                           break;
-                         }
-                       seen_framesz = 1;
-                       framesz = imm_expr.X_add_number;
-                       imm_expr.X_op = O_absent;
-                       s = expr_end;
-                       continue;
-                     }
-
-                   if (! reg_lookup (&s, RTYPE_GP | RTYPE_NUM, &reg1))
-                     {
-                       as_bad (_("can't parse register list"));
-                       break;
-                     }
-
-                   while (*s == ' ')
-                     ++s;
-
-                   if (*s != '-')
-                     reg2 = reg1;
-                   else
-                     {
-                       ++s;
-                       if (! reg_lookup (&s, RTYPE_GP | RTYPE_NUM, &reg2)
-                           || reg2 < reg1)
-                         {
-                           as_bad (_("can't parse register list"));
-                           break;
-                         }
-                     }
-
-                   while (reg1 <= reg2)
-                     {
-                       if (reg1 >= 4 && reg1 <= 7)
-                         {
-                           if (!seen_framesz)
-                               /* args $a0-$a3 */
-                               nargs |= 1 << (reg1 - 4);
-                           else
-                               /* statics $a0-$a3 */
-                               statics |= 1 << (reg1 - 4);
-                         }
-                       else if ((reg1 >= 16 && reg1 <= 23) || reg1 == 30)
-                         {
-                           /* $s0-$s8 */
-                           sregs |= 1 << ((reg1 == 30) ? 8 : (reg1 - 16));
-                         }
-                       else if (reg1 == 31)
-                         {
-                           /* Add $ra to insn.  */
-                           opcode |= 0x40;
-                         }
-                       else
-                         {
-                           as_bad (_("unexpected register in list"));
-                           break;
-                         }
-                       if (++reg1 == 24)
-                         reg1 = 30;
-                     }
-                 }
-
-               /* Encode args/statics combination.  */
-               if (nargs & statics)
-                 as_bad (_("arg/static registers overlap"));
-               else if (nargs == 0xf)
-                 /* All $a0-$a3 are args.  */
-                 opcode |= MIPS16_ALL_ARGS << 16;
-               else if (statics == 0xf)
-                 /* All $a0-$a3 are statics.  */
-                 opcode |= MIPS16_ALL_STATICS << 16;
-               else 
-                 {
-                   int narg = 0, nstat = 0;
-
-                   /* Count arg registers.  */
-                   while (nargs & 0x1)
-                     {
-                       nargs >>= 1;
-                       narg++;
-                     }
-                   if (nargs != 0)
-                     as_bad (_("invalid arg register list"));
-
-                   /* Count static registers.  */
-                   while (statics & 0x8)
-                     {
-                       statics = (statics << 1) & 0xf;
-                       nstat++;
-                     }
-                   if (statics != 0) 
-                     as_bad (_("invalid static register list"));
-
-                   /* Encode args/statics.  */
-                   opcode |= ((narg << 2) | nstat) << 16;
-                 }
+             break;
+           }
 
-               /* Encode $s0/$s1.  */
-               if (sregs & (1 << 0))           /* $s0 */
-                 opcode |= 0x20;
-               if (sregs & (1 << 1))           /* $s1 */
-                 opcode |= 0x10;
-               sregs >>= 2;
+         operand = decode_mips16_operand (c, FALSE);
+         if (!operand)
+           abort ();
 
-               if (sregs != 0)
-                 {
-                   /* Count regs $s2-$s8.  */
-                   int nsreg = 0;
-                   while (sregs & 1)
-                     {
-                       sregs >>= 1;
-                       nsreg++;
-                     }
-                   if (sregs != 0)
-                     as_bad (_("invalid static register list"));
-                   /* Encode $s2-$s8. */
-                   opcode |= nsreg << 24;
-                 }
+         /* '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;
+                   }
 
-               /* Encode frame size.  */
-               if (!seen_framesz)
-                 as_bad (_("missing frame size"));
-               else if ((framesz & 7) != 0 || framesz < 0
-                        || framesz > 0xff * 8)
-                 as_bad (_("invalid frame size"));
-               else if (framesz != 128 || (opcode >> 16) != 0)
-                 {
-                   framesz /= 8;
-                   opcode |= (((framesz & 0xf0) << 16)
-                            | (framesz & 0x0f));
-                 }
+                 /* 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;
 
-               /* Finally build the instruction.  */
-               if ((opcode >> 16) != 0 || framesz == 0)
-                 opcode |= MIPS16_EXTEND;
-               ip->insn_opcode = opcode;
-             }
-           continue;
+                 /* '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;
 
-           case 'e':           /* extend code */
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if ((unsigned long) imm_expr.X_add_number > 0x7ff)
-               {
-                 as_warn (_("Invalid value for `%s' (%lu)"),
-                          ip->insn_mo->name,
-                          (unsigned long) imm_expr.X_add_number);
-                 imm_expr.X_add_number &= 0x7ff;
+                 relax_char = c;
+                 continue;
                }
-             ip->insn_opcode |= imm_expr.X_add_number;
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+           }
 
-           default:
-             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;
            }
-         break;
+
+         if (!match_operand (&arg, operand))
+           break;
+         continue;
        }
 
       /* Args don't match.  */
-      if (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes] &&
-         strcmp (insn->name, insn[1].name) == 0)
+      if (more_alts)
        {
          ++insn;
-         s = argsstart;
          continue;
        }
 
       insn_error = _("illegal operands");
 
+      obstack_free (&mips_operand_tokens, tokens);
       return;
     }
 }
@@ -14795,7 +12947,6 @@ struct percent_op_match
 static const struct percent_op_match mips_percent_op[] =
 {
   {"%lo", BFD_RELOC_LO16},
-#ifdef OBJ_ELF
   {"%call_hi", BFD_RELOC_MIPS_CALL_HI16},
   {"%call_lo", BFD_RELOC_MIPS_CALL_LO16},
   {"%call16", BFD_RELOC_MIPS_CALL16},
@@ -14817,7 +12968,6 @@ 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},
-#endif
   {"%hi", BFD_RELOC_HI16_S}
 };
 
@@ -14971,7 +13121,6 @@ md_number_to_chars (char *buf, valueT val, int n)
     number_to_chars_littleendian (buf, val, n);
 }
 \f
-#ifdef OBJ_ELF
 static int support_64bit_objects(void)
 {
   const char **list, **l;
@@ -14986,7 +13135,6 @@ static int support_64bit_objects(void)
   free (list);
   return yes;
 }
-#endif /* OBJ_ELF */
 
 /* Set STRING_PTR (either &mips_arch_string or &mips_tune_string) to
    NEW_VALUE.  Warn if another value was already specified.  Note:
@@ -15224,6 +13372,14 @@ md_parse_option (int c, char *arg)
       mips_relax_branch = 0;
       break;
 
+    case OPTION_INSN32:
+      mips_opts.insn32 = TRUE;
+      break;
+
+    case OPTION_NO_INSN32:
+      mips_opts.insn32 = FALSE;
+      break;
+
     case OPTION_MSHARED:
       mips_in_shared = TRUE;
       break;
@@ -15240,36 +13396,20 @@ md_parse_option (int c, char *arg)
       mips_opts.sym32 = FALSE;
       break;
 
-#ifdef OBJ_ELF
       /* When generating ELF code, we permit -KPIC and -call_shared to
         select SVR4_PIC, and -non_shared to select no PIC.  This is
         intended to be compatible with Irix 5.  */
     case OPTION_CALL_SHARED:
-      if (!IS_ELF)
-       {
-         as_bad (_("-call_shared is supported only for ELF format"));
-         return 0;
-       }
       mips_pic = SVR4_PIC;
       mips_abicalls = TRUE;
       break;
 
     case OPTION_CALL_NONPIC:
-      if (!IS_ELF)
-       {
-         as_bad (_("-call_nonpic is supported only for ELF format"));
-         return 0;
-       }
       mips_pic = NO_PIC;
       mips_abicalls = TRUE;
       break;
 
     case OPTION_NON_SHARED:
-      if (!IS_ELF)
-       {
-         as_bad (_("-non_shared is supported only for ELF format"));
-         return 0;
-       }
       mips_pic = NO_PIC;
       mips_abicalls = FALSE;
       break;
@@ -15280,7 +13420,6 @@ md_parse_option (int c, char *arg)
     case OPTION_XGOT:
       mips_big_got = 1;
       break;
-#endif /* OBJ_ELF */
 
     case 'G':
       g_switch_value = atoi (arg);
@@ -15290,33 +13429,18 @@ md_parse_option (int c, char *arg)
       /* The -32, -n32 and -64 options are shortcuts for -mabi=32, -mabi=n32
         and -mabi=64.  */
     case OPTION_32:
-      if (IS_ELF)
-       mips_abi = O32_ABI;
-      /* We silently ignore -32 for non-ELF targets.  This greatly
-        simplifies the construction of the MIPS GAS test cases.  */
+      mips_abi = O32_ABI;
       break;
 
-#ifdef OBJ_ELF
     case OPTION_N32:
-      if (!IS_ELF)
-       {
-         as_bad (_("-n32 is supported for ELF format only"));
-         return 0;
-       }
       mips_abi = N32_ABI;
       break;
 
     case OPTION_64:
-      if (!IS_ELF)
-       {
-         as_bad (_("-64 is supported for ELF format only"));
-         return 0;
-       }
       mips_abi = N64_ABI;
       if (!support_64bit_objects())
        as_fatal (_("No compiled in support for 64 bit object file format"));
       break;
-#endif /* OBJ_ELF */
 
     case OPTION_GP32:
       file_mips_gp32 = 1;
@@ -15350,13 +13474,7 @@ md_parse_option (int c, char *arg)
       file_mips_soft_float = 0;
       break;
 
-#ifdef OBJ_ELF
     case OPTION_MABI:
-      if (!IS_ELF)
-       {
-         as_bad (_("-mabi is supported for ELF format only"));
-         return 0;
-       }
       if (strcmp (arg, "32") == 0)
        mips_abi = O32_ABI;
       else if (strcmp (arg, "o64") == 0)
@@ -15378,7 +13496,6 @@ md_parse_option (int c, char *arg)
          return 0;
        }
       break;
-#endif /* OBJ_ELF */
 
     case OPTION_M7000_HILO_FIX:
       mips_7000_hilo_fix = TRUE;
@@ -15388,7 +13505,6 @@ md_parse_option (int c, char *arg)
       mips_7000_hilo_fix = FALSE;
       break;
 
-#ifdef OBJ_ELF
     case OPTION_MDEBUG:
       mips_flag_mdebug = TRUE;
       break;
@@ -15408,7 +13524,18 @@ md_parse_option (int c, char *arg)
     case OPTION_MVXWORKS_PIC:
       mips_pic = VXWORKS_PIC;
       break;
-#endif /* OBJ_ELF */
+
+    case OPTION_NAN:
+      if (strcmp (arg, "2008") == 0)
+       mips_flag_nan2008 = TRUE;
+      else if (strcmp (arg, "legacy") == 0)
+       mips_flag_nan2008 = FALSE;
+      else
+       {
+         as_fatal (_("Invalid NaN setting -mnan=%s"), arg);
+         return 0;
+       }
+      break;
 
     default:
       return 0;
@@ -16198,16 +14325,13 @@ s_change_sec (int sec)
 {
   segT seg;
 
-#ifdef OBJ_ELF
   /* The ELF backend needs to know that we are changing sections, so
      that .previous works correctly.  We could do something like check
      for an obj_section_change_hook macro, but that might be confusing
      as it would not be appropriate to use it in the section changing
      functions in read.c, since obj-elf.c intercepts those.  FIXME:
      This should be cleaner, somehow.  */
-  if (IS_ELF)
-    obj_elf_section_change_hook ();
-#endif
+  obj_elf_section_change_hook ();
 
   mips_emit_delays ();
 
@@ -16227,37 +14351,28 @@ s_change_sec (int sec)
     case 'r':
       seg = subseg_new (RDATA_SECTION_NAME,
                        (subsegT) get_absolute_expression ());
-      if (IS_ELF)
-       {
-         bfd_set_section_flags (stdoutput, seg, (SEC_ALLOC | SEC_LOAD
-                                                 | SEC_READONLY | SEC_RELOC
-                                                 | SEC_DATA));
-         if (strncmp (TARGET_OS, "elf", 3) != 0)
-           record_alignment (seg, 4);
-       }
+      bfd_set_section_flags (stdoutput, seg, (SEC_ALLOC | SEC_LOAD
+                                             | SEC_READONLY | SEC_RELOC
+                                             | SEC_DATA));
+      if (strncmp (TARGET_OS, "elf", 3) != 0)
+       record_alignment (seg, 4);
       demand_empty_rest_of_line ();
       break;
 
     case 's':
       seg = subseg_new (".sdata", (subsegT) get_absolute_expression ());
-      if (IS_ELF)
-       {
-         bfd_set_section_flags (stdoutput, seg,
-                                SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA);
-         if (strncmp (TARGET_OS, "elf", 3) != 0)
-           record_alignment (seg, 4);
-       }
+      bfd_set_section_flags (stdoutput, seg,
+                            SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA);
+      if (strncmp (TARGET_OS, "elf", 3) != 0)
+       record_alignment (seg, 4);
       demand_empty_rest_of_line ();
       break;
 
     case 'B':
       seg = subseg_new (".sbss", (subsegT) get_absolute_expression ());
-      if (IS_ELF)
-       {
-         bfd_set_section_flags (stdoutput, seg, SEC_ALLOC);
-         if (strncmp (TARGET_OS, "elf", 3) != 0)
-           record_alignment (seg, 4);
-       }
+      bfd_set_section_flags (stdoutput, seg, SEC_ALLOC);
+      if (strncmp (TARGET_OS, "elf", 3) != 0)
+       record_alignment (seg, 4);
       demand_empty_rest_of_line ();
       break;
     }
@@ -16268,7 +14383,6 @@ s_change_sec (int sec)
 void
 s_change_section (int ignore ATTRIBUTE_UNUSED)
 {
-#ifdef OBJ_ELF
   char *section_name;
   char c;
   char next_c = 0;
@@ -16277,9 +14391,6 @@ s_change_section (int ignore ATTRIBUTE_UNUSED)
   int section_entry_size;
   int section_alignment;
 
-  if (!IS_ELF)
-    return;
-
   section_name = input_line_pointer;
   c = get_symbol_end ();
   if (c)
@@ -16339,7 +14450,6 @@ s_change_section (int ignore ATTRIBUTE_UNUSED)
 
   if (now_seg->name != section_name)
     free (section_name);
-#endif /* OBJ_ELF */
 }
 
 void
@@ -16694,6 +14804,10 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
     mips_opts.noautoextend = 0;
   else if (strcmp (name, "noautoextend") == 0)
     mips_opts.noautoextend = 1;
+  else if (strcmp (name, "insn32") == 0)
+    mips_opts.insn32 = TRUE;
+  else if (strcmp (name, "noinsn32") == 0)
+    mips_opts.insn32 = FALSE;
   else if (strcmp (name, "push") == 0)
     {
       struct mips_option_stack *s;
@@ -17313,6 +15427,30 @@ s_insn (int ignore ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 
+/* Handle the .nan pseudo-op.  */
+
+static void
+s_nan (int ignore ATTRIBUTE_UNUSED)
+{
+  static const char str_legacy[] = "legacy";
+  static const char str_2008[] = "2008";
+  size_t i;
+
+  for (i = 0; !is_end_of_line[(unsigned char) input_line_pointer[i]]; i++);
+
+  if (i == sizeof (str_2008) - 1
+      && memcmp (input_line_pointer, str_2008, i) == 0)
+    mips_flag_nan2008 = TRUE;
+  else if (i == sizeof (str_legacy) - 1
+          && memcmp (input_line_pointer, str_legacy, i) == 0)
+    mips_flag_nan2008 = FALSE;
+  else
+    as_bad (_("Bad .nan directive"));
+
+  input_line_pointer += i;
+  demand_empty_rest_of_line ();
+}
+
 /* Handle a .stab[snd] directive.  Ideally these directives would be
    implemented in a transparent way, so that removing them would not
    have any effect on the generated instructions.  However, s_stab
@@ -17407,17 +15545,14 @@ md_section_align (asection *seg, valueT addr)
 {
   int align = bfd_get_section_alignment (stdoutput, seg);
 
-  if (IS_ELF)
-    {
-      /* We don't need to align ELF sections to the full alignment.
-        However, Irix 5 may prefer that we align them at least to a 16
-        byte boundary.  We don't bother to align the sections if we
-        are targeted for an embedded system.  */
-      if (strncmp (TARGET_OS, "elf", 3) == 0)
-        return addr;
-      if (align > 4)
-        align = 4;
-    }
+  /* We don't need to align ELF sections to the full alignment.
+     However, Irix 5 may prefer that we align them at least to a 16
+     byte boundary.  We don't bother to align the sections if we
+     are targeted for an embedded system.  */
+  if (strncmp (TARGET_OS, "elf", 3) == 0)
+    return addr;
+  if (align > 4)
+    align = 4;
 
   return ((addr + (1 << align) - 1) & (-1 << align));
 }
@@ -17524,11 +15659,8 @@ pic_need_relax (symbolS *sym, asection *segtype)
          && !bfd_is_abs_section (symsec)
          && !bfd_is_com_section (symsec)
          && !s_is_linkonce (sym, segtype)
-#ifdef OBJ_ELF
          /* A global or weak symbol is treated as external.  */
-         && (!IS_ELF || (! S_IS_WEAK (sym) && ! S_IS_EXTERNAL (sym)))
-#endif
-         );
+         && (!S_IS_WEAK (sym) && !S_IS_EXTERNAL (sym)));
 }
 
 
@@ -18045,7 +16177,6 @@ mips_fix_adjustable (fixS *fixp)
          || jalr_reloc_p (fixp->fx_r_type)))
     return 0;
 
-#ifdef OBJ_ELF
   /* R_MIPS16_26 relocations against non-MIPS16 functions might resolve
      to a floating-point stub.  The same is true for non-R_MIPS16_26
      relocations against MIPS16 functions; in this case, the stub becomes
@@ -18088,15 +16219,13 @@ mips_fix_adjustable (fixS *fixp)
      targets.)  This approach is a little simpler than trying to detect
      stub sections, and gives the "all or nothing" per-symbol consistency
      that we have for MIPS16 symbols.  */
-  if (IS_ELF
-      && fixp->fx_subsy == NULL
+  if (fixp->fx_subsy == NULL
       && (ELF_ST_IS_MIPS16 (S_GET_OTHER (fixp->fx_addsy))
          || *symbol_get_tc (fixp->fx_addsy)
          || (HAVE_IN_PLACE_ADDENDS
              && ELF_ST_IS_MICROMIPS (S_GET_OTHER (fixp->fx_addsy))
              && jmp_reloc_p (fixp->fx_r_type))))
     return 0;
-#endif
 
   return 1;
 }
@@ -18128,14 +16257,6 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
       /* At this point, fx_addnumber is "symbol offset - pcrel address".
         Relocations want only the symbol offset.  */
       reloc->addend = fixp->fx_addnumber + reloc->address;
-      if (!IS_ELF)
-       {
-         /* A gruesome hack which is a result of the gruesome gas
-            reloc handling.  What's worse, for COFF (as opposed to
-            ECOFF), we might need yet another copy of reloc->address.
-            See bfd_install_relocation.  */
-         reloc->addend += reloc->address;
-       }
     }
   else
     reloc->addend = fixp->fx_addnumber;
@@ -18546,10 +16667,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
          /* Make a label at the end for use with the branch.  */
          l = symbol_new (micromips_label_name (), asec, fragp->fr_fix, fragp);
          micromips_label_inc ();
-#if defined(OBJ_ELF) || defined(OBJ_MAYBE_ELF)
-         if (IS_ELF)
-           S_SET_OTHER (l, ELF_ST_SET_MICROMIPS (S_GET_OTHER (l)));
-#endif
+         S_SET_OTHER (l, ELF_ST_SET_MICROMIPS (S_GET_OTHER (l)));
 
          /* Refer to it.  */
          fixp = fix_new (fragp, buf - fragp->fr_literal, 4, l, 0, TRUE,
@@ -18783,8 +16901,6 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
     }
 }
 
-#ifdef OBJ_ELF
-
 /* This function is called after the relocs have been generated.
    We've been storing mips16 text labels as odd.  Here we convert them
    back to even for the convenience of the debugger.  */
@@ -18795,9 +16911,6 @@ mips_frob_file_after_relocs (void)
   asymbol **syms;
   unsigned int count, i;
 
-  if (!IS_ELF)
-    return;
-
   syms = bfd_get_outsymbols (stdoutput);
   count = bfd_get_symcount (stdoutput);
   for (i = 0; i < count; i++, syms++)
@@ -18812,8 +16925,6 @@ mips_frob_file_after_relocs (void)
       }
 }
 
-#endif
-
 /* This function is called whenever a label is defined, including fake
    labels instantiated off the dot special symbol.  It is used when
    handling branch delays; if a branch has a label, we assume we cannot
@@ -18846,9 +16957,7 @@ void
 mips_define_label (symbolS *sym)
 {
   mips_record_label (sym);
-#ifdef OBJ_ELF
   dwarf2_emit_label (sym);
-#endif
 }
 
 /* This function is called by tc_new_dot_label whenever a new dot symbol
@@ -18862,8 +16971,6 @@ mips_add_dot_label (symbolS *sym)
     mips_compressed_mark_label (sym);
 }
 \f
-#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
-
 /* Some special processing for a MIPS ELF file.  */
 
 void
@@ -18943,14 +17050,15 @@ mips_elf_final_processing (void)
   if (mips_32bitmode)
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_32BITMODE;
 
+  if (mips_flag_nan2008)
+    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
 }
-
-#endif /* OBJ_ELF || OBJ_MAYBE_ELF */
 \f
 typedef struct proc {
   symbolS *func_sym;
@@ -19034,7 +17142,7 @@ mips_handle_align (fragS *fragp)
       *p++ = '\0';
       /* Fall through.  */
     case 2:
-      if (nop_opcode == NOP_OPCODE_MICROMIPS)
+      if (nop_opcode == NOP_OPCODE_MICROMIPS && !mips_opts.insn32)
        {
          p = write_compressed_insn (p, micromips_nop16_insn.insn_opcode, 2);
          break;
@@ -19200,7 +17308,6 @@ s_mips_end (int x ATTRIBUTE_UNUSED)
   else
     as_warn (_(".end directive missing or unknown symbol"));
 
-#ifdef OBJ_ELF
   /* Create an expression to calculate the size of the function.  */
   if (p && cur_proc_ptr)
     {
@@ -19217,7 +17324,7 @@ s_mips_end (int x ATTRIBUTE_UNUSED)
     }
 
   /* Generate a .pdr section.  */
-  if (IS_ELF && !ECOFF_DEBUGGING && mips_flag_pdr)
+  if (!ECOFF_DEBUGGING && mips_flag_pdr)
     {
       segT saved_seg = now_seg;
       subsegT saved_subseg = now_subseg;
@@ -19249,7 +17356,6 @@ s_mips_end (int x ATTRIBUTE_UNUSED)
 
       subseg_set (saved_seg, saved_subseg);
     }
-#endif /* OBJ_ELF */
 
   cur_proc_ptr = NULL;
 }
@@ -19307,8 +17413,9 @@ s_mips_ent (int aent)
 static void
 s_mips_frame (int ignore ATTRIBUTE_UNUSED)
 {
-#ifdef OBJ_ELF
-  if (IS_ELF && !ECOFF_DEBUGGING)
+  if (ECOFF_DEBUGGING)
+    s_ignore (ignore);
+  else
     {
       long val;
 
@@ -19336,9 +17443,6 @@ s_mips_frame (int ignore ATTRIBUTE_UNUSED)
 
       demand_empty_rest_of_line ();
     }
-  else
-#endif /* OBJ_ELF */
-    s_ignore (ignore);
 }
 
 /* The .fmask and .mask directives. If the mdebug section is present
@@ -19350,8 +17454,9 @@ s_mips_frame (int ignore ATTRIBUTE_UNUSED)
 static void
 s_mips_mask (int reg_type)
 {
-#ifdef OBJ_ELF
-  if (IS_ELF && !ECOFF_DEBUGGING)
+  if (ECOFF_DEBUGGING)
+    s_ignore (reg_type);
+  else
     {
       long mask, off;
 
@@ -19385,9 +17490,6 @@ s_mips_mask (int reg_type)
 
       demand_empty_rest_of_line ();
     }
-  else
-#endif /* OBJ_ELF */
-    s_ignore (reg_type);
 }
 
 /* A table describing all the processors gas knows about.  Names are
@@ -19766,6 +17868,9 @@ MIPS options:\n\
 -mvirt                 generate Virtualization instructions\n\
 -mno-virt              do not generate Virtualization instructions\n"));
   fprintf (stream, _("\
+-minsn32               only generate 32-bit microMIPS instructions\n\
+-mno-insn32            generate all microMIPS instructions\n"));
+  fprintf (stream, _("\
 -mfix-loongson2f-jump  work around Loongson2F JUMP instructions\n\
 -mfix-loongson2f-nop   work around Loongson2F NOP errata\n\
 -mfix-vr4120           work around certain VR4120 errata\n\
@@ -19785,9 +17890,16 @@ MIPS options:\n\
 -msingle-float         only allow 32-bit floating-point operations\n\
 -mdouble-float         allow 32-bit and 64-bit floating-point operations\n\
 --[no-]construct-floats        [dis]allow floating point values to be constructed\n\
---[no-]relax-branch    [dis]allow out-of-range branches to be relaxed\n"
-                    ));
-#ifdef OBJ_ELF
+--[no-]relax-branch    [dis]allow out-of-range branches to be relaxed\n\
+-mnan=ENCODING         select an IEEE 754 NaN encoding convention, either of:\n"));
+
+  first = 1;
+
+  show (stream, "legacy", &column, &first);
+  show (stream, "2008", &column, &first);
+
+  fputc ('\n', stream);
+
   fprintf (stream, _("\
 -KPIC, -call_shared    generate SVR4 position independent code\n\
 -call_nonpic           generate non-PIC code that can operate with DSOs\n\
@@ -19813,7 +17925,6 @@ MIPS options:\n\
 -32                    create o32 ABI object file (default)\n\
 -n32                   create n32 ABI object file\n\
 -64                    create 64 ABI object file\n"));
-#endif
 }
 
 #ifdef TE_IRIX
This page took 0.242932 seconds and 4 git commands to generate.