include/opcode/
[deliverable/binutils-gdb.git] / gas / config / tc-mips.c
index d2b1f6376f759ecee31c3ea04d9f7f328c87e5d5..91e6a970b0614869654a1bd07df376a380cb7745 100644 (file)
@@ -44,6 +44,9 @@ typedef char static_assert2[sizeof (valueT) < 8 ? -1 : 1];
 #define DBG(x)
 #endif
 
+#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; }
@@ -272,6 +275,9 @@ 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,
@@ -749,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[] =
@@ -768,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
@@ -819,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
 };
@@ -846,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
@@ -1169,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) \
@@ -1321,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);
@@ -1333,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);
 
@@ -1452,6 +1365,7 @@ enum options
     OPTION_PDR,
     OPTION_NO_PDR,
     OPTION_MVXWORKS_PIC,
+    OPTION_NAN,
     OPTION_END_OF_ENUM
   };
 
@@ -1567,6 +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},
+  {"mnan", required_argument, NULL, OPTION_NAN},
 
   {NULL, no_argument, NULL, 0}
 };
@@ -1691,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.  */
@@ -1814,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};
 
@@ -2049,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
@@ -2473,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))
@@ -2496,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
@@ -2697,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.  */
 
@@ -2735,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)
                {
@@ -2840,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;
@@ -2961,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;
@@ -2993,9 +3232,7 @@ md_assemble (char *str)
     }
   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);
@@ -3094,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.  */
 
@@ -3153,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.  */
 
@@ -3329,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.  */
@@ -3343,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.  */
@@ -3384,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;
 }
 
@@ -3411,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)]);
@@ -3429,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)
@@ -3479,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)
@@ -3503,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);
@@ -3577,5980 +3785,5075 @@ 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;
-}
-
-#define INSN_ERET  0x42000018
-#define INSN_DERET 0x4200001f
+  const char *s = insn->name;
 
-/* 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.  */
+  if (insn->pinfo == INSN_MACRO)
+    /* Let a macro pass, we'll catch it later when it is expanded.  */
+    return TRUE;
 
-static unsigned int
-insns_between (const struct mips_cl_insn *insn1,
-              const struct mips_cl_insn *insn2)
-{
-  unsigned long pinfo1, pinfo2;
-  unsigned int mask;
-
-  /* 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;
-
-#define INSN2_USES_GPR(REG) \
-  (insn2 == NULL || (gpr_read_mask (insn2) & (1U << (REG))) != 0)
-
-  /* For most targets, write-after-read dependencies on the HI and LO
-     registers must be separated by at least two instructions.  */
-  if (!hilo_interlocks)
-    {
-      if ((pinfo1 & INSN_READ_LO) && (pinfo2 & INSN_WRITE_LO))
-       return 2;
-      if ((pinfo1 & INSN_READ_HI) && (pinfo2 & INSN_WRITE_HI))
-       return 2;
-    }
-
-  /* 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;
-
-  /* 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 (ISA_HAS_ODD_SINGLE_FPR (mips_opts.isa) || mips_opts.arch == CPU_R5900)
     {
-      if (insn1->insn_opcode == INSN_ERET
-         || insn1->insn_opcode == INSN_DERET)
+      /* Allow odd registers for single-precision ops.  */
+      switch (insn->pinfo & (FP_S | FP_D))
        {
-         if (insn2 == NULL
-             || insn2->insn_opcode == INSN_ERET
-             || insn2->insn_opcode == INSN_DERET
-             || delayed_branch_p (insn2))
-           return 1;
+       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'));
     }
 
-  /* 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;
+  /* 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;
 
-      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;
-       }
-    }
+  return FALSE;
+}
 
-  if (!HAVE_CODE_COMPRESSION)
-    {
-      /* 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;
-       }
+/* 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.  */
 
-      /* Check for generic coprocessor hazards.
+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);
+}
 
-        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;
+/* Report an invalid combination of position and size operands for a bitfield
+   operation.  POS and SIZE are the values that were given.  */
 
-             /* 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 void
+report_bad_field (offsetT pos, offsetT size)
+{
+  as_bad (_("Invalid field specification (position %ld, size %ld)"),
+         (unsigned long) pos, (unsigned long) size);
+}
 
-      /* 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;
-    }
+/* Information about an instruction argument that we're trying to match.  */
+struct mips_arg_info
+{
+  /* The instruction so far.  */
+  struct mips_cl_insn *insn;
 
-#undef INSN2_USES_GPR
+  /* The first unconsumed operand token.  */
+  struct mips_operand_token *token;
 
-  return 0;
-}
+  /* The 1-based operand number, in terms of insn->insn_mo->args.  */
+  int opnum;
 
-/* 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.  */
+  /* The 1-based argument number, for error reporting.  This does not
+     count elided optional registers, etc..  */
+  int argnum;
 
-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;
+  /* The last OP_REG operand seen, or ILLEGAL_REG if none.  */
+  unsigned int last_regno;
 
-  /* 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 the first operand was an OP_REG, this is the register that it
+     specified, otherwise it is ILLEGAL_REG.  */
+  unsigned int dest_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]);
+  /* The value of the last OP_INT operand.  Only used for OP_MSB,
+     where it gives the lsb position.  */
+  unsigned int last_op_int;
 
-       /* No nops are needed if INSN reads that register.  */
-       if (insn != NULL && (gpr_read_mask (insn) & mask) != 0)
-         return 0;
+  /* 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;
 
-       /* ...or if any of the intervening instructions do.  */
-       for (j = 0; j < i; j++)
-         if (gpr_read_mask (&hist[j]) & mask)
-           return 0;
+  /* 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;
 
-       if (i >= ignore)
-         return MAX_VR4130_NOPS - i;
-      }
-  return 0;
-}
+  /* 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;
 
-#define BASE_REG_EQ(INSN1, INSN2)      \
-  ((((INSN1) >> OP_SH_RS) & OP_MASK_RS) \
-      == (((INSN2) >> OP_SH_RS) & OP_MASK_RS))
+  /* True if a reference to the current AT register was seen.  */
+  bfd_boolean seen_at;
+};
 
-/* Return the minimum alignment for this store instruction.  */
+/* Try to match an OT_CHAR token for character CH.  Consume the token
+   and return true on success, otherwise return false.  */
 
-static int
-fix_24k_align_to (const struct mips_opcode *mo)
+static bfd_boolean
+match_char (struct mips_arg_info *arg, char ch)
 {
-  if (strcmp (mo->name, "sh") == 0)
-    return 2;
+  if (arg->token->type == OT_CHAR && arg->token->u.ch == ch)
+    {
+      ++arg->token;
+      if (ch == ',')
+       arg->argnum += 1;
+      return TRUE;
+    }
+  return FALSE;
+}
 
-  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;
+/* 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 (strcmp (mo->name, "sdc1") == 0
-      || strcmp (mo->name, "sdc2") == 0
-      || strcmp (mo->name, "s.d") == 0)
-    return 8;
+static bfd_boolean
+match_expression (struct mips_arg_info *arg, expressionS *value,
+                 bfd_reloc_code_real_type *r)
+{
+  if (arg->token->type == OT_INTEGER)
+    {
+      *value = arg->token->u.integer.value;
+      memcpy (r, arg->token->u.integer.relocs, 3 * sizeof (*r));
+      ++arg->token;
+      return TRUE;
+    }
 
-  /* sb, swl, swr */
-  return 1;
+  /* 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)
+    {
+      value->X_add_number = arg->token->u.regno;
+      ++arg->token;
+    }
+  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 == ')')
+    {
+      value->X_add_number = arg->token[1].u.regno;
+      arg->token += 3;
+    }
+  else
+    return FALSE;
+
+  value->X_op = O_register;
+  r[0] = r[1] = r[2] = BFD_RELOC_UNUSED;
+  return TRUE;
 }
 
-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;
-  };
+/* 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.  */
 
-/* Comparison function used by qsort.  */
+static bfd_boolean
+match_const_int (struct mips_arg_info *arg, offsetT *value, offsetT fallback)
+{
+  expressionS ex;
+  bfd_reloc_code_real_type r[3];
 
-static int
-fix_24k_sort (const void *a, const void *b)
+  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;
+}
+
+/* Return the RTYPE_* flags for a register operand of type TYPE that
+   appears in instruction OPCODE.  */
+
+static unsigned int
+convert_reg_type (const struct mips_opcode *opcode,
+                 enum mips_reg_operand_type type)
 {
-  const struct fix_24k_store_info *pos1 = a;
-  const struct fix_24k_store_info *pos2 = b;
+  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 (pos1->off - pos2->off);
+/* ARG is register REGNO, of type TYPE.  Warn about any dubious registers.  */
+
+static void
+check_regno (struct mips_arg_info *arg,
+            enum mips_reg_operand_type type, unsigned int regno)
+{
+  if (AT && type == OP_REG_GP && regno == AT)
+    arg->seen_at = TRUE;
+
+  if (type == OP_REG_FP
+      && (regno & 1) != 0
+      && HAVE_32BIT_FPRS
+      && !mips_oddfpreg_ok (arg->insn->insn_mo, arg->opnum))
+    as_warn (_("Float register should be even, was %d"), regno);
+
+  if (type == OP_REG_CCC)
+    {
+      const char *name;
+      size_t length;
+
+      name = arg->insn->insn_mo->name;
+      length = strlen (name);
+      if ((regno & 1) != 0
+         && ((length >= 3 && strcmp (name + length - 3, ".ps") == 0)
+             || (length >= 5 && strncmp (name + length - 5, "any2", 4) == 0)))
+       as_warn (_("Condition code register should be even for %s, was %d"),
+                name, regno);
+
+      if ((regno & 3) != 0
+         && (length >= 5 && strncmp (name + length - 5, "any4", 4) == 0))
+       as_warn (_("Condition code register should be 0 or 4 for %s, was %d"),
+                name, regno);
+    }
 }
 
-/* INSN is a store instruction.  Try to record the store information
-   in STINFO.  Return false if the information isn't known.  */
+/* 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.  */
 
 static bfd_boolean
-fix_24k_record_store_info (struct fix_24k_store_info *stinfo,
-                          const struct mips_cl_insn *insn)
+match_regno (struct mips_arg_info *arg, enum mips_reg_operand_type type,
+            unsigned int symval, unsigned int *regno)
 {
-  /* The instruction must have a known offset.  */
-  if (!insn->complete_p || !strstr (insn->insn_mo->args, "o("))
+  if (type == OP_REG_VEC)
+    symval = mips_prefer_vec_regno (symval);
+  if (!(symval & convert_reg_type (arg->insn->insn_mo, type)))
     return FALSE;
 
-  stinfo->off = (insn->insn_opcode >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE;
-  stinfo->align_to = fix_24k_align_to (insn->insn_mo);
+  *regno = symval & RNUM_MASK;
+  check_regno (arg, type, *regno);
   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.
-
-   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.
+/* 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.  */
 
-   * 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
+static bfd_boolean
+match_reg (struct mips_arg_info *arg, enum mips_reg_operand_type type,
+          unsigned int *regno)
+{
+  if (arg->token->type == OT_REG
+      && match_regno (arg, type, arg->token->u.regno, regno))
+    {
+      ++arg->token;
+      return TRUE;
+    }
+  return FALSE;
+}
 
-   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.
+/* 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.  */
 
-   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.
+static bfd_boolean
+match_reg_range (struct mips_arg_info *arg, enum mips_reg_operand_type type,
+                unsigned int *regno1, unsigned int *regno2)
+{
+  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;
+}
 
-   The workarounds are:
+/* OP_INT matcher.  */
 
-   * 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)
+static bfd_boolean
+match_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_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 (ignore >= 2)
-    return 0;
+      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;
 
-  /* 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;
+      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;
+       }
 
-  /* If the instructions after the previous one are unknown, we have
-     to assume the worst.  */
-  if (!insn)
-    return 1;
+      /* 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;
+    }
 
-  /* 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;
+  arg->last_op_int = sval;
 
-  /* 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;
+  /* 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.
 
-  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;
+     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;
+    }
 
-  qsort (&pos, 3, sizeof (struct fix_24k_store_info), fix_24k_sort);
+  uval = (unsigned int) sval >> operand->shift;
+  uval -= operand->bias;
 
-  /* 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.  */
+  /* 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;
 
-  if (((insn->insn_opcode >> OP_SH_RS) & OP_MASK_RS) == SP)
-    align = 8;
-  else
-    {
-      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;
-    }
+      default:
+       /* The rest must be changed to 28.  */
+       uval = 28;
+       break;
+      }
 
-  pos[0].off &= ~align + 1;
-  pos[1].off &= ~align + 1;
-  pos[2].off &= ~align + 1;
+  insn_insert_operand (arg->insn, operand_base, uval);
+  return TRUE;
+}
 
-  /* 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;
+/* OP_MAPPED_INT matcher.  */
 
-  /* 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;
+static bfd_boolean
+match_mapped_int_operand (struct mips_arg_info *arg,
+                         const struct mips_operand *operand_base)
+{
+  const struct mips_mapped_int_operand *operand;
+  unsigned int uval, num_vals;
+  offsetT sval;
 
-  if (pos[2].off - pos[1].off >= 24
-      || pos[1].off - pos[0].off >= 24
-      || pos[2].off - pos[0].off >= 32)
-    return 0;
+  operand = (const struct mips_mapped_int_operand *) operand_base;
+  if (!match_const_int (arg, &sval, operand->int_map[0]))
+    return FALSE;
 
-  return 1;
-}
+  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;
 
-/* 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.
+  insn_insert_operand (arg->insn, operand_base, uval);
+  return TRUE;
+}
 
-   If INSN is null, return the worse-case number of nops for any
-   instruction.  */
+/* OP_MSB matcher.  */
 
-static int
-nops_for_insn (int ignore, const struct mips_cl_insn *hist,
-              const struct mips_cl_insn *insn)
+static bfd_boolean
+match_msb_operand (struct mips_arg_info *arg,
+                  const struct mips_operand *operand_base)
 {
-  int i, nops, tmp_nops;
+  const struct mips_msb_operand *operand;
+  int min_val, max_val, max_high;
+  offsetT size, sval, high;
 
-  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;
-    }
+  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;
 
-  if (mips_fix_vr4130 && !mips_opts.micromips)
+  if (!match_const_int (arg, &size, 1))
+    return FALSE;
+
+  high = size + arg->last_op_int;
+  sval = operand->add_lsb ? high : size;
+
+  if (size < 0 || high > max_high || sval < min_val || sval > max_val)
     {
-      tmp_nops = nops_for_vr4130 (ignore, hist, insn);
-      if (tmp_nops > nops)
-       nops = tmp_nops;
+      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;
+}
 
-  if (mips_fix_24k && !mips_opts.micromips)
+/* OP_REG matcher.  */
+
+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;
+
+  operand = (const struct mips_reg_operand *) operand_base;
+  if (!match_reg (arg, operand->reg_type, &regno))
+    return FALSE;
+
+  if (operand->reg_map)
     {
-      tmp_nops = nops_for_24k (ignore, hist, insn);
-      if (tmp_nops > nops)
-       nops = tmp_nops;
+      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 nops;
+  arg->last_regno = regno;
+  if (arg->opnum == 1)
+    arg->dest_regno = regno;
+  insn_insert_operand (arg->insn, operand_base, uval);
+  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_REG_PAIR matcher.  */
 
-static int
-nops_for_sequence (int num_insns, int ignore,
-                  const struct mips_cl_insn *hist, ...)
+static bfd_boolean
+match_reg_pair_operand (struct mips_arg_info *arg,
+                       const struct mips_operand *operand_base)
 {
-  va_list args;
-  struct mips_cl_insn buffer[MAX_NOPS];
-  struct mips_cl_insn *cursor;
-  int nops;
+  const struct mips_reg_pair_operand *operand;
+  unsigned int regno1, regno2, uval, num_vals;
 
-  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 *);
+  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;
 
-  nops = nops_for_insn (ignore, buffer, NULL);
-  va_end (args);
-  return nops;
+  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;
 }
 
-/* Like nops_for_insn, but if INSN is a branch, take into account the
-   worst-case delay for the branch target.  */
+/* OP_PCREL matcher.  The caller chooses the relocation type.  */
 
-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_pcrel_operand (struct mips_arg_info *arg)
 {
-  int nops, tmp_nops;
+  bfd_reloc_code_real_type r[3];
 
-  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;
+  return match_expression (arg, &offset_expr, r) && r[0] == BFD_RELOC_UNUSED;
 }
 
-/* Fix NOP issue: Replace nops by "or at,at,zero".  */
+/* OP_PERF_REG matcher.  */
 
-static void
-fix_loongson2f_nop (struct mips_cl_insn * ip)
+static bfd_boolean
+match_perf_reg_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;
-}
+  offsetT sval;
 
-/* Fix Jump Issue: Eliminate instruction fetch from outside 256M region
-                   jr target pc &= 'hffff_ffff_cfff_ffff.  */
+  if (!match_const_int (arg, &sval, 0))
+    return FALSE;
 
-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 (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))))
     {
-      int sreg;
-      expressionS ep;
-
-      if (! mips_opts.at)
-        return;
-
-      sreg = EXTRACT_OPERAND (0, RS, *ip);
-      if (sreg == ZERO || sreg == KT0 || sreg == KT1 || sreg == ATREG)
-        return;
-
-      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);
+      if (arg->soft_match)
+       return FALSE;
+      as_bad (_("Invalid performance register (%ld)"), (unsigned long) sval);
     }
-}
-
-static void
-fix_loongson2f (struct mips_cl_insn * ip)
-{
-  if (mips_fix_loongson2f_nop)
-    fix_loongson2f_nop (ip);
 
-  if (mips_fix_loongson2f_jump)
-    fix_loongson2f_jump (ip);
+  insn_insert_operand (arg->insn, operand, sval);
+  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_ADDIUSP matcher.  */
 
 static bfd_boolean
-can_swap_branch_p (struct mips_cl_insn *ip, expressionS *address_expr,
-  bfd_reloc_code_real_type *reloc_type)
+match_addiusp_operand (struct mips_arg_info *arg,
+                      const struct mips_operand *operand)
 {
-  unsigned long pinfo, pinfo2, prev_pinfo, prev_pinfo2;
-  unsigned int gpr_read, gpr_write, prev_gpr_read, prev_gpr_write;
+  offsetT sval;
+  unsigned int uval;
 
-  /* -O2 and above is required for this optimization.  */
-  if (mips_optimize < 2)
+  if (!match_const_int (arg, &sval, -256))
     return FALSE;
 
-  /* If we have seen .set volatile or .set nomove, don't optimize.  */
-  if (mips_opts.nomove)
+  if (sval % 4)
     return FALSE;
 
-  /* We can't swap if the previous instruction's position is fixed.  */
-  if (history[0].fixed_p)
+  sval /= 4;
+  if (!(sval >= -258 && sval <= 257) || (sval >= -2 && sval <= 1))
     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
+  uval = (unsigned int) sval;
+  uval = ((uval >> 1) & ~0xff) | (uval & 0xff);
+  insn_insert_operand (arg->insn, operand, uval);
+  return TRUE;
+}
 
-       .set    noreorder
-       lw      $4,XXX
-       .set    reorder
-       INSN
-       bne     $4,$0,foo
+/* OP_CLO_CLZ_DEST matcher.  */
 
-     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;
+static bfd_boolean
+match_clo_clz_dest_operand (struct mips_arg_info *arg,
+                           const struct mips_operand *operand)
+{
+  unsigned int regno;
 
-  /* 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])
+  if (!match_reg (arg, OP_REG_GP, &regno))
     return FALSE;
 
-  /* 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;
+  insn_insert_operand (arg->insn, operand, regno | (regno << 5));
+  return TRUE;
+}
 
-  /* 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;
+/* OP_LWM_SWM_LIST matcher.  */
 
-  /* 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;
+static bfd_boolean
+match_lwm_swm_list_operand (struct mips_arg_info *arg,
+                           const struct mips_operand *operand)
+{
+  unsigned int reglist, sregs, ra, regno1, regno2;
+  struct mips_arg_info reset;
 
-  /* Check for conflicts between the branch and the instructions
-     before the candidate delay slot.  */
-  if (nops_for_insn (0, history + 1, ip) > 0)
+  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;
 
-  /* 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;
+  if (operand->size == 2)
+    {
+      /* The list must include both ra and s0-sN, for 0 <= N <= 3.  E.g.:
 
-  /* 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;
+        s0, ra
+        s0, s1, ra, s2, s3
+        s0-s2, ra
 
-  /* 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;
+        and any permutations of these.  */
+      if ((reglist & 0xfff1ffff) != 0x80010000)
+       return FALSE;
 
-  /* 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;
+      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;
 
-  /* 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))
+      ra = (reglist >> 27) & 0x10;
+      sregs = ((reglist >> 22) & 0x100) | ((reglist >> 16) & 0xff);
+    }
+  sregs += 1;
+  if ((sregs & -sregs) != sregs)
     return FALSE;
 
-  /* 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;
+  insn_insert_operand (arg->insn, operand, (ffs (sregs) - 1) | ra);
+  return TRUE;
+}
 
-  /* 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;
+/* OP_ENTRY_EXIT_LIST matcher.  */
 
-  /* 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 unsigned int
+match_entry_exit_operand (struct mips_arg_info *arg,
+                         const struct mips_operand *operand)
+{
+  unsigned int mask;
+  bfd_boolean is_exit;
+
+  /* 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
     {
-      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;
+      unsigned int regno1, regno2;
+      bfd_boolean is_freg;
 
-          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;
-            }
-        }
+      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;
 }
 
-/* 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_SAVE_RESTORE_LIST matcher.  */
 
-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_save_restore_list_operand (struct mips_arg_info *arg)
 {
-  unsigned long pinfo;
+  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;
 
-  /* The relaxed version of a macro sequence must be inherently
-     hazard-free.  */
-  if (mips_relax.sequence == 2)
-    return APPEND_ADD;
+      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;
 
-  /* We must not dabble with instructions in a ".set norerorder" block.  */
-  if (mips_opts.noreorder)
-    return APPEND_ADD;
+         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, ','));
 
-  /* Otherwise, it's our responsibility to fill branch delay slots.  */
-  if (delayed_branch_p (ip))
+  /* 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
     {
-      if (!branch_likely_p (ip)
-         && can_swap_branch_p (ip, address_expr, reloc_type))
-       return APPEND_SWAP;
+      /* Count arg registers.  */
+      num_args = 0;
+      while (args & 0x1)
+       {
+         args >>= 1;
+         num_args += 1;
+       }
+      if (args != 0)
+       return FALSE;
 
-      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;
+      /* Count static registers.  */
+      num_statics = 0;
+      while (statics & 0x8)
+       {
+         statics = (statics << 1) & 0xf;
+         num_statics += 1;
+       }
+      if (statics != 0)
+       return FALSE;
 
-      return APPEND_ADD_WITH_NOP;
+      /* Encode args/statics.  */
+      opcode |= ((num_args << 2) | num_statics) << 16;
     }
 
-  return APPEND_ADD;
-}
-
-/* IP is a MIPS16 instruction whose opcode we have just changed.
-   Point IP->insn_mo to the new opcode's definition.  */
+  /* Encode $s0/$s1.  */
+  if (sregs & (1 << 0))                /* $s0 */
+    opcode |= 0x20;
+  if (sregs & (1 << 1))                /* $s1 */
+    opcode |= 0x10;
+  sregs >>= 2;
 
-static void
-find_altered_mips16_opcode (struct mips_cl_insn *ip)
-{
-  const struct mips_opcode *mo, *end;
+  /* 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));
+    }
 
-  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 ();
+  if (error)
+    {
+      if (arg->soft_match)
+       return FALSE;
+      as_bad ("%s", error);
+    }
+
+  /* Finally build the instruction.  */
+  if ((opcode >> 16) != 0 || frame_size == 0)
+    opcode |= MIPS16_EXTEND;
+  arg->insn->insn_opcode = opcode;
+  return TRUE;
 }
 
-/* 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];
+/* OP_MDMX_IMM_REG matcher.  */
 
-static char *
-micromips_label_name (void)
+static bfd_boolean
+match_mdmx_imm_reg_operand (struct mips_arg_info *arg,
+                           const struct mips_operand *operand)
 {
-  char *p = micromips_target_name;
-  char symbol_name_temporary[24];
-  unsigned long l;
-  int i;
+  unsigned int regno, uval;
+  bfd_boolean is_qh;
+  const struct mips_opcode *opcode;
 
-  if (*p)
-    return p;
+  /* 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);
 
-  i = 0;
-  l = micromips_target_label;
-#ifdef LOCAL_LABEL_PREFIX
-  *p++ = LOCAL_LABEL_PREFIX;
-#endif
-  *p++ = 'L';
-  *p++ = MICROMIPS_LABEL_CHAR;
-  do
+  if (arg->token->type == OT_REG || arg->token->type == OT_REG_ELEMENT)
     {
-      symbol_name_temporary[i++] = l % 10 + '0';
-      l /= 10;
+      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);
+       }
+
+      /* 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 (!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;
     }
-  while (l != 0);
-  while (i > 0)
-    *p++ = symbol_name_temporary[--i];
-  *p = '\0';
+  else
+    {
+      offsetT sval;
 
-  return micromips_target_name;
+      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;
 }
 
-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;
-}
+/* OP_PC matcher.  */
 
-static void
-micromips_label_inc (void)
+static bfd_boolean
+match_pc_operand (struct mips_arg_info *arg)
 {
-  micromips_target_label++;
-  *micromips_target_name = '\0';
+  if (arg->token->type == OT_REG && (arg->token->u.regno & RTYPE_PC))
+    {
+      ++arg->token;
+      return TRUE;
+    }
+  return FALSE;
 }
 
-static void
-micromips_add_label (void)
+/* OP_REPEAT_DEST_REG and OP_REPEAT_PREV_REG matcher.  OTHER_REGNO is the
+   register that we need to match.  */
+
+static bfd_boolean
+match_tied_reg_operand (struct mips_arg_info *arg, unsigned int other_regno)
 {
-  symbolS *s;
+  unsigned int regno;
 
-  s = colon (micromips_label_name ());
-  micromips_label_inc ();
-  S_SET_OTHER (s, ELF_ST_SET_MICROMIPS (S_GET_OTHER (s)));
+  return match_reg (arg, OP_REG_GP, &regno) && regno == other_regno;
 }
 
-/* If assembling microMIPS code, then return the microMIPS reloc
-   corresponding to the requested one if any.  Otherwise return
-   the reloc unchanged.  */
+/* 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.
 
-static bfd_reloc_code_real_type
-micromips_map_reloc (bfd_reloc_code_real_type reloc)
+   Return the constant in IMM and OFFSET as follows:
+
+   - If the constant should be loaded via memory, set IMM to O_absent and
+     OFFSET to the memory address.
+
+   - 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.
+
+   - Otherwise, set IMM to the full O_constant and set OFFSET to O_absent.
+
+   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 bfd_boolean
+match_float_constant (struct mips_arg_info *arg, expressionS *imm,
+                     expressionS *offset, int length, bfd_boolean using_gprs)
 {
-  static const bfd_reloc_code_real_type relocs[][2] =
+  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;
+
+  gas_assert (arg->token->u.flt.length == length);
+  data = arg->token->u.flt.data;
+  ++arg->token;
+
+  /* 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)))
     {
-      /* 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;
+      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 (!mips_opts.micromips)
-    return reloc;
-  for (i = 0; i < ARRAY_SIZE (relocs); i++)
+  /* 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)))
     {
-      r = relocs[i][0];
-      if (r > reloc)
-       return reloc;
-      if (r == reloc)
-       return relocs[i][1];
+      /* 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;
     }
-  return reloc;
+
+  /* Switch to the right section.  */
+  seg = now_seg;
+  subseg = now_subseg;
+  if (length == 4)
+    {
+      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";
+    }
+
+  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;
 }
 
-/* Try to resolve relocation RELOC against constant OPERAND at assembly time.
-   Return true on success, storing the resolved value in RESULT.  */
+/* 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 bfd_boolean
-calculate_reloc (bfd_reloc_code_real_type reloc, offsetT operand,
-                offsetT *result)
+match_operand (struct mips_arg_info *arg,
+              const struct mips_operand *operand)
 {
-  switch (reloc)
+  switch (operand->type)
     {
-    case BFD_RELOC_MIPS_HIGHEST:
-    case BFD_RELOC_MICROMIPS_HIGHEST:
-      *result = ((operand + 0x800080008000ull) >> 48) & 0xffff;
-      return TRUE;
+    case OP_INT:
+      return match_int_operand (arg, operand);
 
-    case BFD_RELOC_MIPS_HIGHER:
-    case BFD_RELOC_MICROMIPS_HIGHER:
-      *result = ((operand + 0x80008000ull) >> 32) & 0xffff;
-      return TRUE;
+    case OP_MAPPED_INT:
+      return match_mapped_int_operand (arg, operand);
 
-    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 OP_MSB:
+      return match_msb_operand (arg, operand);
 
-    case BFD_RELOC_HI16:
-    case BFD_RELOC_MICROMIPS_HI16:
-    case BFD_RELOC_MIPS16_HI16:
-      *result = (operand >> 16) & 0xffff;
-      return TRUE;
+    case OP_REG:
+      return match_reg_operand (arg, operand);
 
-    case BFD_RELOC_LO16:
-    case BFD_RELOC_MICROMIPS_LO16:
-    case BFD_RELOC_MIPS16_LO16:
-      *result = operand & 0xffff;
-      return TRUE;
+    case OP_REG_PAIR:
+      return match_reg_pair_operand (arg, operand);
 
-    case BFD_RELOC_UNUSED:
-      *result = operand;
-      return TRUE;
+    case OP_PCREL:
+      return match_pcrel_operand (arg);
 
-    default:
-      return FALSE;
+    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 ();
 }
 
-/* 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.  */
+/* ARG is the state after successfully matching an instruction.
+   Issue any queued-up warnings.  */
 
 static void
-append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
-            bfd_reloc_code_real_type *reloc_type, bfd_boolean expansionp)
+check_completed_insn (struct mips_arg_info *arg)
 {
-  unsigned long prev_pinfo2, pinfo;
-  bfd_boolean relaxed_branch = FALSE;
-  enum append_method method;
-  bfd_boolean relax32;
-  int branch_disp;
+  if (arg->seen_at)
+    {
+      if (AT == ATREG)
+       as_warn (_("Used $at without \".set noat\""));
+      else
+       as_warn (_("Used $%u with \".set at=$%u\""), AT, AT);
+    }
+}
 
-  if (mips_fix_loongson2f && !HAVE_CODE_COMPRESSION)
-    fix_loongson2f (ip);
+/* Return true if modifying general-purpose register REG needs a delay.  */
 
-  file_ase_mips16 |= mips_opts.mips16;
-  file_ase_micromips |= mips_opts.micromips;
+static bfd_boolean
+reg_needs_delay (unsigned int reg)
+{
+  unsigned long prev_pinfo;
 
-  prev_pinfo2 = history[0].insn_mo->pinfo2;
-  pinfo = ip->insn_mo->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))
+      && (gpr_write_mask (&history[0]) & (1 << reg)))
+    return TRUE;
 
-  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);
+  return FALSE;
+}
 
-  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 shift;
+/* Classify an instruction according to the FIX_VR4120_* enumeration.
+   Return NUM_FIX_VR4120_CLASSES if the instruction isn't affected
+   by VR4120 errata.  */
 
-           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;
+static unsigned int
+classify_vr4120_insn (const char *name)
+{
+  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;
+}
 
-       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;
+#define INSN_ERET  0x42000018
+#define INSN_DERET 0x4200001f
 
-       case BFD_RELOC_16_PCREL_S2:
-         {
-           int shift;
+/* 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.  */
 
-           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;
+static unsigned int
+insns_between (const struct mips_cl_insn *insn1,
+              const struct mips_cl_insn *insn2)
+{
+  unsigned long pinfo1, pinfo2;
+  unsigned int mask;
 
-       default:
-         {
-           offsetT value;
+  /* 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 (calculate_reloc (*reloc_type, address_expr->X_add_number,
-                                &value))
-             {
-               ip->insn_opcode |= value & 0xffff;
-               ip->complete_p = 1;
-             }
-         }
-         break;
+  /* For most targets, write-after-read dependencies on the HI and LO
+     registers must be separated by at least two instructions.  */
+  if (!hilo_interlocks)
+    {
+      if ((pinfo1 & INSN_READ_LO) && (pinfo2 & INSN_WRITE_LO))
+       return 2;
+      if ((pinfo1 & INSN_READ_HI) && (pinfo2 & INSN_WRITE_HI))
+       return 2;
+    }
+
+  /* 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;
+
+  /* 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;
        }
     }
 
-  if (mips_relax.sequence != 2 && !mips_opts.noreorder)
+  /* If working around VR4120 errata, check for combinations that need
+     a single intervening instruction.  */
+  if (mips_fix_vr4120 && !mips_opts.micromips)
     {
-      /* 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)
+      unsigned int class1, class2;
+
+      class1 = classify_vr4120_insn (insn1->insn_mo->name);
+      if (class1 != NUM_FIX_VR4120_CLASSES && vr4120_conflicts[class1] != 0)
        {
-         fragS *old_frag;
-         unsigned long old_frag_offset;
-         int i;
+         if (insn2 == NULL)
+           return 1;
+         class2 = classify_vr4120_insn (insn2->insn_mo->name);
+         if (vr4120_conflicts[class1] & (1 << class2))
+           return 1;
+       }
+    }
 
-         old_frag = frag_now;
-         old_frag_offset = frag_now_fix ();
+  if (!HAVE_CODE_COMPRESSION)
+    {
+      /* Check for GPR or coprocessor load delays.  All such delays
+        are on the RT register.  */
+      /* Itbl support may require additional care here.  */
+      if ((!gpr_interlocks && (pinfo1 & INSN_LOAD_MEMORY_DELAY))
+         || (!cop_interlocks && (pinfo1 & INSN_LOAD_COPROC_DELAY)))
+       {
+         if (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;
 
-  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);
+       /* 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 ();
+/* Comparison function used by qsort.  */
 
-      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]);
+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;
 
-      /* 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;
+  return (pos1->off - pos2->off);
+}
 
-      /* 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;
+/* INSN is a store instruction.  Try to record the store information
+   in STINFO.  Return false if the information isn't known.  */
 
-      /* 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;
+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;
 
-      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;
+  stinfo->off = (insn->insn_opcode >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE;
+  stinfo->align_to = fix_24k_align_to (insn->insn_mo);
+  return TRUE;
+}
 
-         /* 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;
-       }
+/* 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.
 
-      /* 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]);
+   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.
 
-           /* Use fx_tcbit to mark compound relocs.  */
-           ip->fixp[0]->fx_tcbit = 1;
-           ip->fixp[i]->fx_tcbit = 1;
-         }
-    }
-  install_insn (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
 
-  /* 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);
+   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.
 
-  switch (method)
-    {
-    case APPEND_ADD:
-      insert_into_history (0, 1, ip);
-      break;
+   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.
 
-    case APPEND_ADD_WITH_NOP:
-      {
-       struct mips_cl_insn *nop;
+   The workarounds are:
 
-       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;
+   * 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_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;
+  if (ignore >= 2)
+    return 0;
 
-    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 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;
 
-  /* 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 instructions after the previous one are unknown, we have
+     to assume the worst.  */
+  if (!insn)
+    return 1;
 
-      mips_no_prev_insn ();
+  /* 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;
 
-      for (i = 0; i < ARRAY_SIZE (history); i++)
-       history[i].cleared_p = 1;
-    }
+  /* 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 need to emit a label at the end of branch-likely macros.  */
-  if (emit_branch_likely_macro)
+  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;
+
+  qsort (&pos, 3, sizeof (struct fix_24k_store_info), fix_24k_sort);
+
+  /* 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.  */
+
+  if (((insn->insn_opcode >> OP_SH_RS) & OP_MASK_RS) == SP)
+    align = 8;
+  else
     {
-      emit_branch_likely_macro = FALSE;
-      micromips_add_label ();
+      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;
     }
 
-  /* We just output an insn, so the next one doesn't have a label.  */
-  mips_clear_insn_labels ();
-}
+  pos[0].off &= ~align + 1;
+  pos[1].off &= ~align + 1;
+  pos[2].off &= ~align + 1;
 
-/* Forget that there was any previous instruction or label.
-   When BRANCH is true, the branch history is also flushed.  */
+  /* 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 void
-mips_no_prev_insn (void)
-{
-  prev_nop_frag = NULL;
-  insert_into_history (0, ARRAY_SIZE (history), NOP_INSN);
-  mips_clear_insn_labels ();
+  /* 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;
+
+  if (pos[2].off - pos[1].off >= 24
+      || pos[1].off - pos[0].off >= 24
+      || pos[2].off - pos[0].off >= 32)
+    return 0;
+
+  return 1;
 }
 
-/* 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.  */
+/* 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.
 
-void
-mips_emit_delays (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)
 {
-  if (! mips_opts.noreorder)
+  int i, nops, tmp_nops;
+
+  nops = 0;
+  for (i = ignore; i < MAX_DELAY_NOPS; i++)
     {
-      int nops = nops_for_insn (0, history, NULL);
-      if (nops > 0)
-       {
-         while (nops-- > 0)
-           add_fixed_insn (NOP_INSN);
-         mips_move_text_labels ();
-       }
+      tmp_nops = insns_between (hist + i, insn) - i;
+      if (tmp_nops > nops)
+       nops = tmp_nops;
     }
-  mips_no_prev_insn ();
-}
 
-/* Start a (possibly nested) noreorder block.  */
+  if (mips_fix_vr4130 && !mips_opts.micromips)
+    {
+      tmp_nops = nops_for_vr4130 (ignore, hist, insn);
+      if (tmp_nops > nops)
+       nops = tmp_nops;
+    }
 
-static void
-start_noreorder (void)
-{
-  if (mips_opts.noreorder == 0)
+  if (mips_fix_24k && !mips_opts.micromips)
     {
-      unsigned int i;
-      int nops;
+      tmp_nops = nops_for_24k (ignore, hist, insn);
+      if (tmp_nops > nops)
+       nops = tmp_nops;
+    }
 
-      /* None of the instructions before the .set noreorder can be moved.  */
-      for (i = 0; i < ARRAY_SIZE (history); i++)
-       history[i].fixed_p = 1;
+  return nops;
+}
 
-      /* 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;
-           }
+/* 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.  */
 
-         for (; nops > 0; --nops)
-           add_fixed_insn (NOP_INSN);
+static int
+nops_for_sequence (int num_insns, int ignore,
+                  const struct mips_cl_insn *hist, ...)
+{
+  va_list args;
+  struct mips_cl_insn buffer[MAX_NOPS];
+  struct mips_cl_insn *cursor;
+  int nops;
 
-         /* 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;
+  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;
 }
 
-/* End a nested noreorder block.  */
+/* Like nops_for_insn, but if INSN is a branch, take into account the
+   worst-case delay for the branch target.  */
 
-static void
-end_noreorder (void)
+static int
+nops_for_insn_or_target (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 nops, tmp_nops;
+
+  nops = nops_for_insn (ignore, hist, insn);
+  if (delayed_branch_p (insn))
     {
-      /* 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 = nops_for_sequence (2, ignore ? ignore + 2 : 0,
+                                   hist, insn, get_delay_slot_nop (insn));
+      if (tmp_nops > nops)
+       nops = tmp_nops;
     }
-}
-
-/* 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))
+  else if (compact_branch_p (insn))
     {
-    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_sequence (1, ignore ? ignore + 1 : 0, 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.  */
+/* Fix NOP issue: Replace nops by "or at,at,zero".  */
 
-static const char *
-macro_warning (relax_substateT subtype)
+static void
+fix_loongson2f_nop (struct mips_cl_insn * ip)
 {
-  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;
+  gas_assert (!HAVE_CODE_COMPRESSION);
+  if (strcmp (ip->insn_mo->name, "nop") == 0)
+    ip->insn_opcode = LOONGSON2F_NOP_INSN;
 }
 
-/* Finish up a macro.  Emit warnings as appropriate.  */
+/* Fix Jump Issue: Eliminate instruction fetch from outside 256M region
+                   jr target pc &= 'hffff_ffff_cfff_ffff.  */
 
 static void
-macro_end (void)
+fix_loongson2f_jump (struct mips_cl_insn * ip)
 {
-  /* Relaxation warning flags.  */
-  relax_substateT subtype = 0;
-
-  /* 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)
+  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 (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;
-    }
+      int sreg;
+      expressionS ep;
 
-  /* 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;
-    }
+      if (! mips_opts.at)
+        return;
 
-  /* 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;
+      sreg = EXTRACT_OPERAND (0, RS, *ip);
+      if (sreg == ZERO || sreg == KT0 || sreg == KT1 || sreg == ATREG)
+        return;
 
-      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;
+      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);
     }
+}
 
-  /* 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;
-    }
+static void
+fix_loongson2f (struct mips_cl_insn * ip)
+{
+  if (mips_fix_loongson2f_nop)
+    fix_loongson2f_nop (ip);
 
-  /* 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;
+  if (mips_fix_loongson2f_jump)
+    fix_loongson2f_jump (ip);
 }
 
-/* Instruction operand formats used in macros that vary between
-   standard MIPS and microMIPS code.  */
-
-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,|" };
-
-#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
-macro_read_relocs (va_list *args, bfd_reloc_code_real_type *r)
-{
-  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);
-}
-
-/* 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.  */
+/* 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.  */
 
-static void
-macro_build (expressionS *ep, const char *name, const char *fmt, ...)
+static bfd_boolean
+can_swap_branch_p (struct mips_cl_insn *ip, expressionS *address_expr,
+                  bfd_reloc_code_real_type *reloc_type)
 {
-  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);
+  unsigned long pinfo, pinfo2, prev_pinfo, prev_pinfo2;
+  unsigned int gpr_read, gpr_write, prev_gpr_read, prev_gpr_write;
 
-  if (mips_opts.mips16)
-    {
-      mips16_macro_build (ep, name, fmt, &args);
-      va_end (args);
-      return;
-    }
+  /* -O2 and above is required for this optimization.  */
+  if (mips_optimize < 2)
+    return FALSE;
 
-  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 we have seen .set volatile or .set nomove, don't optimize.  */
+  if (mips_opts.nomove)
+    return FALSE;
 
-  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;
-       }
+  /* We can't swap if the previous instruction's position is fixed.  */
+  if (history[0].fixed_p)
+    return FALSE;
 
-      ++amo;
-      gas_assert (amo->name);
-    }
-  while (strcmp (name, amo->name) == 0);
+  /* 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
 
-  gas_assert (mo);
-  create_insn (&insn, mo);
-  for (;;)
-    {
-      switch (*fmt++)
-       {
-       case '\0':
-         break;
+       .set    noreorder
+       lw      $4,XXX
+       .set    reorder
+       INSN
+       bne     $4,$0,foo
 
-       case ',':
-       case '(':
-       case ')':
-         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 '+':
-         switch (*fmt++)
-           {
-           case 'A':
-           case 'E':
-             INSERT_OPERAND (mips_opts.micromips,
-                             EXTLSB, 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 '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;
+  /* 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 'J':
-             gas_assert (!mips_opts.micromips);
-             INSERT_OPERAND (0, CODE10, 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 '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;
+  /* 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 'Q':
-             gas_assert (!mips_opts.micromips);
-             INSERT_OPERAND (0, SEQI, insn, va_arg (args, int));
-             continue;
+  /* 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;
 
-           case 'j':
-             INSERT_OPERAND (mips_opts.micromips, EVAOFFSET, insn, va_arg (args, int));
-             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;
 
-           default:
-             abort ();
-           }
-         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 '2':
-         INSERT_OPERAND (mips_opts.micromips, BP, insn, va_arg (args, int));
-         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 'n':
-         gas_assert (mips_opts.micromips);
-       case 't':
-       case 'w':
-       case 'E':
-         INSERT_OPERAND (mips_opts.micromips, RT, 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 'c':
-         INSERT_OPERAND (mips_opts.micromips, CODE, 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 'W':
-         gas_assert (!mips_opts.micromips);
-       case 'T':
-         INSERT_OPERAND (mips_opts.micromips, FT, 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 '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 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 'K':
-         gas_assert (!mips_opts.micromips);
-       case 'd':
-         INSERT_OPERAND (mips_opts.micromips, RD, insn, va_arg (args, int));
-         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':
-         gas_assert (!mips_opts.micromips);
-         {
-           int tmp = va_arg (args, int);
-
-           INSERT_OPERAND (0, RT, insn, tmp);
-           INSERT_OPERAND (0, RD, insn, tmp);
-         }
-         continue;
-
-       case 'V':
-       case 'S':
-         gas_assert (!mips_opts.micromips);
-         INSERT_OPERAND (0, FS, insn, va_arg (args, int));
-         continue;
-
-       case 'z':
-         continue;
-
-       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;
-
-       case 'q':
-         gas_assert (!mips_opts.micromips);
-         INSERT_OPERAND (0, CODE2, insn, va_arg (args, int));
-         continue;
-
-       case 'b':
-       case 's':
-       case 'r':
-       case 'v':
-         INSERT_OPERAND (mips_opts.micromips, RS, insn, va_arg (args, int));
-         continue;
-
-       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;
-
-       case 'o':
-         macro_read_relocs (&args, r);
-         continue;
-
-       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);
-
-  while (strcmp (fmt, mo->args) != 0 || mo->pinfo == INSN_MACRO)
-    {
-      ++mo;
-      gas_assert (mo->name);
-      gas_assert (strcmp (name, mo->name) == 0);
-    }
-
-  create_insn (&insn, mo);
-  for (;;)
-    {
-      int c;
+  label_expr->X_op = O_symbol;
+  label_expr->X_add_symbol = symbol_find_or_make (micromips_label_name ());
+  label_expr->X_add_number = 0;
+}
 
-      c = *fmt++;
-      switch (c)
-       {
-       case '\0':
-         break;
+static void
+micromips_label_inc (void)
+{
+  micromips_target_label++;
+  *micromips_target_name = '\0';
+}
 
-       case ',':
-       case '(':
-       case ')':
-         continue;
+static void
+micromips_add_label (void)
+{
+  symbolS *s;
 
-       case 'y':
-       case 'w':
-         MIPS16_INSERT_OPERAND (RY, insn, va_arg (*args, int));
-         continue;
+  s = colon (micromips_label_name ());
+  micromips_label_inc ();
+  S_SET_OTHER (s, ELF_ST_SET_MICROMIPS (S_GET_OTHER (s)));
+}
 
-       case 'x':
-       case 'v':
-         MIPS16_INSERT_OPERAND (RX, insn, va_arg (*args, int));
-         continue;
+/* If assembling microMIPS code, then return the microMIPS reloc
+   corresponding to the requested one if any.  Otherwise return
+   the reloc unchanged.  */
 
-       case 'z':
-         MIPS16_INSERT_OPERAND (RZ, insn, va_arg (*args, int));
-         continue;
-
-       case 'Z':
-         MIPS16_INSERT_OPERAND (MOVE32Z, insn, va_arg (*args, int));
-         continue;
+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;
 
-       case '0':
-       case 'S':
-       case 'P':
-       case 'R':
-         continue;
+  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;
+}
 
-       case 'X':
-         MIPS16_INSERT_OPERAND (REGR32, insn, va_arg (*args, int));
-         continue;
+/* Try to resolve relocation RELOC against constant OPERAND at assembly time.
+   Return true on success, storing the resolved value in RESULT.  */
 
-       case 'Y':
-         {
-           int regno;
+static bfd_boolean
+calculate_reloc (bfd_reloc_code_real_type reloc, offsetT operand,
+                offsetT *result)
+{
+  switch (reloc)
+    {
+    case BFD_RELOC_MIPS_HIGHEST:
+    case BFD_RELOC_MICROMIPS_HIGHEST:
+      *result = ((operand + 0x800080008000ull) >> 48) & 0xffff;
+      return TRUE;
 
-           regno = va_arg (*args, int);
-           regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3);
-           MIPS16_INSERT_OPERAND (REG32R, insn, regno);
-         }
-         continue;
+    case BFD_RELOC_MIPS_HIGHER:
+    case BFD_RELOC_MICROMIPS_HIGHER:
+      *result = ((operand + 0x80008000ull) >> 32) & 0xffff;
+      return TRUE;
 
-       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_HI16_S:
+    case BFD_RELOC_MICROMIPS_HI16_S:
+    case BFD_RELOC_MIPS16_HI16_S:
+      *result = ((operand + 0x8000) >> 16) & 0xffff;
+      return TRUE;
 
-           gas_assert (ep != NULL);
+    case BFD_RELOC_HI16:
+    case BFD_RELOC_MICROMIPS_HI16:
+    case BFD_RELOC_MIPS16_HI16:
+      *result = (operand >> 16) & 0xffff;
+      return TRUE;
 
-           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;
-             }
-         }
-         continue;
+    case BFD_RELOC_LO16:
+    case BFD_RELOC_MICROMIPS_LO16:
+    case BFD_RELOC_MIPS16_LO16:
+      *result = operand & 0xffff;
+      return TRUE;
 
-       case '6':
-         MIPS16_INSERT_OPERAND (IMM6, insn, va_arg (*args, int));
-         continue;
-       }
+    case BFD_RELOC_UNUSED:
+      *result = operand;
+      return TRUE;
 
-      break;
+    default:
+      return FALSE;
     }
-
-  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);
-}
+/* 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.  */
 
-/*
- * Sign-extend 32-bit mode address offsets that have bit 31 set and
- * all higher bits unset.
- */
 static void
-normalize_address_expr (expressionS *ex)
+append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
+            bfd_reloc_code_real_type *reloc_type, bfd_boolean expansionp)
 {
-  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);
-}
+  unsigned long prev_pinfo2, pinfo;
+  bfd_boolean relaxed_branch = FALSE;
+  enum append_method method;
+  bfd_boolean relax32;
+  int branch_disp;
 
-/*
- * 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;
+  if (mips_fix_loongson2f && !HAVE_CODE_COMPRESSION)
+    fix_loongson2f (ip);
 
-  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);
-}
+  file_ase_mips16 |= mips_opts.mips16;
+  file_ase_micromips |= mips_opts.micromips;
 
-/*
- * Generate a "lui" instruction.
- */
-static void
-macro_build_lui (expressionS *ep, int regnum)
-{
-  gas_assert (! mips_opts.mips16);
+  prev_pinfo2 = history[0].insn_mo->pinfo2;
+  pinfo = ip->insn_mo->pinfo;
 
-  if (ep->X_op != O_constant)
+  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 (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)
     {
-      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));
-    }
+      switch (*reloc_type)
+       {
+       case BFD_RELOC_MIPS_JMP:
+         {
+           int shift;
 
-  macro_build (ep, "lui", LUI_FMT, regnum, BFD_RELOC_HI16_S);
-}
+           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;
 
-/* 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);
+       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;
 
-  /* Sign-extending 32-bit constants makes their handling easier.  */
-  if (!dbl)
-    normalize_constant_expr (ep);
+       case BFD_RELOC_16_PCREL_S2:
+         {
+           int shift;
 
-  /* 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"));
+           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 (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);
+       default:
+         {
+           offsetT value;
 
-      if (!mips_opts.at)
-       as_bad (_("Macro used $at after \".set noat\""));
+           if (calculate_reloc (*reloc_type, address_expr->X_add_number,
+                                &value))
+             {
+               ip->insn_opcode |= value & 0xffff;
+               ip->complete_p = 1;
+             }
+         }
+         break;
+       }
     }
-}
 
-/*                     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
+  if (mips_relax.sequence != 2 && !mips_opts.noreorder)
     {
-      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build (NULL, unsignedp ? "sltu" : "slt", "d,v,t", AT, reg, AT);
-    }
-}
-
-/* Warn if an expression is not a constant.  */
+      /* 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;
 
-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);
+         old_frag = frag_now;
+         old_frag_offset = frag_now_fix ();
 
-  if (HAVE_32BIT_GPRS)
-    normalize_constant_expr (ex);
-}
+         for (i = 0; i < nops; i++)
+           add_fixed_insn (NOP_INSN);
+         insert_into_history (0, nops, NOP_INSN);
 
-/* 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)
+         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);
+           }
 
-/*                     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;
+         mips_move_text_labels ();
 
-  if (ep->X_op != O_big)
+#ifndef NO_ECOFF_DEBUGGING
+         if (ECOFF_DEBUGGING)
+           ecoff_fix_loc (old_frag, old_frag_offset);
+#endif
+       }
+    }
+  else if (mips_relax.sequence != 2 && prev_nop_frag != NULL)
     {
-      gas_assert (ep->X_op == O_constant);
+      int nops;
 
-      /* Sign-extending 32-bit constants makes their handling easier.  */
-      if (!dbl)
-       normalize_constant_expr (ep);
+      /* 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 (IS_SEXT_16BIT_NUM (ep->X_add_number))
+      /* 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)
        {
-         /* 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;
+         /* 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 if (ep->X_add_number >= 0 && ep->X_add_number < 0x10000)
+      else
        {
-         /* 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;
-       }
-      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;
+         /* 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++;
        }
     }
 
-  /* The value is larger than 32 bits.  */
+  method = get_append_method (ip, address_expr, reloc_type);
+  branch_disp = method == APPEND_SWAP ? insn_length (history) : 0;
 
-  if (!dbl || HAVE_32BIT_GPRS)
-    {
-      char value[32];
+  dwarf2_emit_insn (0);
+  /* We want MIPS16 and microMIPS debug info to use ISA-encoded addresses,
+     so "move" the instruction address accordingly.
 
-      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;
+     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);
+
+  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 (!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 (ep->X_op != O_big)
+      gas_assert (address_expr != NULL);
+      gas_assert (!mips_relax.sequence);
+
+      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)
     {
-      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;
+      /* 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
+  else if (mips_opts.mips16 && insn_length (ip) == 2)
     {
-      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 (!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);
     }
-
-  if (hi32.X_add_number == 0)
-    freg = 0;
   else
     {
-      int shift, bit;
-      unsigned long hi, lo;
-
-      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 (mips_opts.mips16
+         && mips_opts.noreorder
+         && delayed_branch_p (&history[0]))
+       as_warn (_("extended instruction in delay slot"));
 
-      /* 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
+      if (mips_relax.sequence)
        {
-         unsigned long himask, lomask;
-
-         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;
-
-             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 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);
        }
-      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)
+      if (mips_relax.sequence != 2)
        {
-         bit = 0;
-         while ((lo & 1) == 0)
-           {
-             lo >>= 1;
-             ++bit;
-           }
-         lo |= (hi & (((unsigned long) 1 << bit) - 1)) << (32 - bit);
-         hi >>= bit;
+         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 (mips_relax.sequence != 1)
        {
-         bit = 32;
-         while ((hi & 1) == 0)
-           {
-             hi >>= 1;
-             ++bit;
-           }
-         lo = hi;
-         hi = 0;
+         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]++;
        }
 
-      /* Optimize if the shifted value is a (power of 2) - 1.  */
-      if ((hi == 0 && ((lo + 1) & lo) == 0)
-         || (lo == 0xffffffff && ((hi + 1) & hi) == 0))
+      if (mips_opts.mips16)
        {
-         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;
-           }
+         ip->fixed_p = 1;
+         ip->mips16_absolute_jump_p = (*reloc_type == BFD_RELOC_MIPS16_JMP);
        }
-
-      /* 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_fixed_insn (ip);
     }
-  if ((lo32.X_add_number & 0xffff0000) == 0)
-    {
-      if (freg != 0)
-       {
-         macro_build (NULL, "dsll32", SHFT_FMT, reg, freg, 0);
-         freg = reg;
-       }
-    }
-  else
+
+  if (!ip->complete_p && *reloc_type < BFD_RELOC_UNUSED)
     {
-      expressionS mid16;
+      bfd_reloc_code_real_type final_type[3];
+      reloc_howto_type *howto0;
+      reloc_howto_type *howto;
+      int i;
 
-      if ((freg == 0) && (lo32.X_add_number == (offsetT) 0xffffffff))
+      /* 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]);
+
+      /* 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]);
+
+      /* 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;
+
+      /* 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;
+
+      /* 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;
+
+      if (mips_relax.sequence)
        {
-         macro_build (&lo32, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
-         macro_build (NULL, "dsrl32", SHFT_FMT, reg, reg, 0);
-         return;
+         if (mips_relax.first_fixup == 0)
+           mips_relax.first_fixup = ip->fixp[0];
        }
-
-      if (freg != 0)
+      else if (reloc_needs_lo_p (*reloc_type))
        {
-         macro_build (NULL, "dsll", SHFT_FMT, reg, freg, 16);
-         freg = reg;
+         struct mips_hi_fixup *hi_fixup;
+
+         /* 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;
        }
-      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);
-}
 
-static inline void
-load_delay_nop (void)
-{
-  if (!gpr_interlocks)
-    macro_build (NULL, "nop", "");
-}
+      /* 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]);
 
-/* Load an address into a register.  */
+           /* Use fx_tcbit to mark compound relocs.  */
+           ip->fixp[0]->fx_tcbit = 1;
+           ip->fixp[i]->fx_tcbit = 1;
+         }
+    }
+  install_insn (ip);
 
-static void
-load_address (int reg, expressionS *ep, int *used_at)
-{
-  if (ep->X_op != O_constant
-      && ep->X_op != O_symbol)
+  /* 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)
     {
-      as_bad (_("expression too complex"));
-      ep->X_op = O_constant;
+    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;
     }
 
-  if (ep->X_op == O_constant)
+  /* 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])))
     {
-      load_register (reg, ep, HAVE_64BIT_ADDRESSES);
-      return;
+      unsigned int i;
+
+      mips_no_prev_insn ();
+
+      for (i = 0; i < ARRAY_SIZE (history); i++)
+       history[i].cleared_p = 1;
     }
 
-  if (mips_pic == NO_PIC)
+  /* We need to emit a label at the end of branch-likely macros.  */
+  if (emit_branch_likely_macro)
     {
-      /* 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.
+      emit_branch_likely_macro = FALSE;
+      micromips_add_label ();
+    }
 
-        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
+  /* We just output an insn, so the next one doesn't have a label.  */
+  mips_clear_insn_labels ();
+}
 
-        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)
+/* Forget that there was any previous instruction or label.
+   When BRANCH is true, the branch history is also flushed.  */
 
-        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) 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 ();
-           }
+static void
+mips_no_prev_insn (void)
+{
+  prev_nop_frag = NULL;
+  insert_into_history (0, ARRAY_SIZE (history), NOP_INSN);
+  mips_clear_insn_labels ();
+}
 
-         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);
-           }
+/* 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.  */
 
-         if (mips_relax.sequence)
-           relax_end ();
-       }
-      else
+void
+mips_emit_delays (void)
+{
+  if (! mips_opts.noreorder)
+    {
+      int 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))
-           {
-             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 ();
+         while (nops-- > 0)
+           add_fixed_insn (NOP_INSN);
+         mips_move_text_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_no_prev_insn ();
+}
 
-        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 ();
+/* Start a (possibly 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);
-           }
-       }
-    }
-  else if (mips_big_got)
+static void
+start_noreorder (void)
+{
+  if (mips_opts.noreorder == 0)
     {
-      expressionS ex;
-
-      /* 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)
+      unsigned int i;
+      int nops;
 
-        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.
+      /* None of the instructions before the .set noreorder can be moved.  */
+      for (i = 0; i < ARRAY_SIZE (history); i++)
+       history[i].fixed_p = 1;
 
-        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)
+      /* 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)
        {
-         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)
+         if (mips_optimize != 0)
            {
-             ex.X_op = O_constant;
-             macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
-                          BFD_RELOC_LO16);
+             /* 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;
            }
 
-         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 ();
+         for (; nops > 0; --nops)
+           add_fixed_insn (NOP_INSN);
 
-         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);
-           }
+         /* 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
-    abort ();
-
-  if (!mips_opts.at && *used_at == 1)
-    as_bad (_("Macro used $at after \".set noat\""));
+  mips_opts.noreorder++;
+  mips_any_noreorder = 1;
 }
 
-/* Move the contents of register SOURCE into register DEST.  */
+/* End a nested noreorder block.  */
 
 static void
-move_register (int dest, int source)
+end_noreorder (void)
 {
-  /* 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);
+  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;
+    }
 }
 
-/* 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:
-
-   Global symbol               Local sybmol
-   -------------               ------------
-   lw DEST,%got(SYMBOL)                lw DEST,%got(SYMBOL + OFFSET)
-   ...                         ...
-   addiu DEST,DEST,OFFSET      addiu DEST,DEST,%lo(SYMBOL + OFFSET)
-
-   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.  */
+/* Set up global variables for the start of a new macro.  */
 
 static void
-load_got_offset (int dest, expressionS *local)
+macro_start (void)
 {
-  expressionS global;
-
-  global = *local;
-  global.X_add_number = 0;
-
-  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 ();
+  memset (&mips_macro_warning.sizes, 0, sizeof (mips_macro_warning.sizes));
+  memset (&mips_macro_warning.first_insn_sizes, 0,
+         sizeof (mips_macro_warning.first_insn_sizes));
+  memset (&mips_macro_warning.insns, 0, sizeof (mips_macro_warning.insns));
+  mips_macro_warning.delay_slot_p = (mips_opts.noreorder
+                                    && delayed_branch_p (&history[0]));
+  switch (history[0].insn_mo->pinfo2
+         & (INSN2_BRANCH_DELAY_32BIT | INSN2_BRANCH_DELAY_16BIT))
+    {
+    case INSN2_BRANCH_DELAY_32BIT:
+      mips_macro_warning.delay_slot_length = 4;
+      break;
+    case INSN2_BRANCH_DELAY_16BIT:
+      mips_macro_warning.delay_slot_length = 2;
+      break;
+    default:
+      mips_macro_warning.delay_slot_length = 0;
+      break;
+    }
+  mips_macro_warning.first_frag = NULL;
 }
 
-static void
-add_got_offset (int dest, expressionS *local)
-{
-  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;
+/* 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.  */
 
-  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 ();
+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;
 }
 
+/* Finish up a macro.  Emit warnings as appropriate.  */
+
 static void
-add_got_offset_hilo (int dest, expressionS *local, int tmp)
+macro_end (void)
 {
-  expressionS global;
-  int hold_mips_optimize;
+  /* Relaxation warning flags.  */
+  relax_substateT subtype = 0;
 
-  global.X_op = O_constant;
-  global.X_op_symbol = NULL;
-  global.X_add_symbol = NULL;
-  global.X_add_number = local->X_add_number;
+  /* 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;
+    }
 
-  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 ();
+  /* 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;
+    }
 
-  macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dest, dest, tmp);
-}
+  /* 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;
 
-/* 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.
+      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;
+    }
 
-   Code like this is produced in the noreorder mode:
+  /* 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;
 
-       BRNEG   <args>, 1f
-        nop
-       b       <sym>
-        delay slot (executed only if branch taken)
-    1:
+      s = subtype & (RELAX_SECOND_LONGER | RELAX_NOMACRO | RELAX_DELAY_SLOT);
+      msg = macro_warning (s);
+      if (msg != NULL)
+       as_warn ("%s", msg);
+      subtype &= ~s;
+    }
 
-   or, if CALL is set:
+  /* 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;
+}
 
-       BRNEG   <args>, 1f
-        nop
-       bal     <sym>
-        delay slot (executed only if branch taken)
-    1:
+/* Instruction operand formats used in macros that vary between
+   standard MIPS and microMIPS code.  */
 
-   In the reorder mode the delay slot would be filled with a nop anyway,
-   so code produced is simply:
+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,|" };
 
-       BR      <args>, <sym>
-        nop
+#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])
 
-   This function is used when producing code for the microMIPS ASE that
-   does not implement branch likely instructions in hardware.  */
+/* 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
-macro_build_branch_likely (const char *br, const char *brneg,
-                          int call, expressionS *ep, const char *fmt,
-                          unsigned int sreg, unsigned int treg)
+macro_read_relocs (va_list *args, bfd_reloc_code_real_type *r)
 {
-  int noreorder = mips_opts.noreorder;
-  expressionS expr1;
-
-  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");
+  int i, next;
 
-      /* Set to true so that append_insn adds a label.  */
-      emit_branch_likely_macro = TRUE;
-    }
+  next = va_arg (*args, int);
+  if (next >= 0)
+    r[0] = (bfd_reloc_code_real_type) next;
   else
     {
-      macro_build (ep, br, fmt, sreg, treg);
-      macro_build (NULL, "nop", "");
+      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;
     }
-  end_noreorder ();
 }
 
-/* Emit a coprocessor branch-likely macro specified by TYPE, using CC as
-   the condition code tested.  EP specifies the branch target.  */
+/* 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
-macro_build_branch_ccl (int type, expressionS *ep, unsigned int cc)
+macro_build (expressionS *ep, const char *name, const char *fmt, ...)
 {
-  const int call = 0;
-  const char *brneg;
-  const char *br;
+  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;
 
-  switch (type)
+  va_start (args, fmt);
+
+  if (mips_opts.mips16)
     {
-    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 ();
+      mips16_macro_build (ep, name, fmt, &args);
+      va_end (args);
+      return;
     }
-  macro_build_branch_likely (br, brneg, call, ep, "N,p", cc, ZERO);
-}
 
-/* Emit a two-argument branch macro specified by TYPE, using SREG as
-   the register tested.  EP specifies the branch target.  */
+  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);
 
-static void
-macro_build_branch_rs (int type, expressionS *ep, unsigned int sreg)
-{
-  const char *brneg = NULL;
-  const char *br;
-  int call = 0;
+  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;
+       }
 
-  switch (type)
+      ++amo;
+      gas_assert (amo->name);
+    }
+  while (strcmp (name, amo->name) == 0);
+
+  gas_assert (mo);
+  create_insn (&insn, mo);
+  for (; *fmt; ++fmt)
     {
-    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 ();
+      switch (*fmt)
+       {
+       case ',':
+       case '(':
+       case ')':
+       case 'z':
+         break;
+
+       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;
+
+       case 'o':
+         macro_read_relocs (&args, r);
+         break;
+
+       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;
+
+       case 'p':
+         gas_assert (ep != NULL);
+
+         /*
+          * 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);
+
+             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;
+
+       case 'a':
+         gas_assert (ep != NULL);
+         *r = BFD_RELOC_MIPS_JMP;
+         break;
+
+       default:
+         operand = (mips_opts.micromips
+                    ? decode_micromips_operand (fmt)
+                    : decode_mips_operand (fmt));
+         if (!operand)
+           abort ();
+
+         uval = va_arg (args, int);
+         if (operand->type == OP_CLO_CLZ_DEST)
+           uval |= (uval << 5);
+         insn_insert_operand (&insn, operand, uval);
+
+         if (*fmt == '+' || *fmt == 'm')
+           ++fmt;
+         break;
+       }
     }
-  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);
-}
+  va_end (args);
+  gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
 
-/* Emit a three-argument branch macro specified by TYPE, using SREG and
-   TREG as the registers tested.  EP specifies the branch target.  */
+  append_insn (&insn, ep, r, TRUE);
+}
 
 static void
-macro_build_branch_rsrt (int type, expressionS *ep,
-                        unsigned int sreg, unsigned int treg)
+mips16_macro_build (expressionS *ep, const char *name, const char *fmt,
+                   va_list *args)
 {
-  const char *brneg = NULL;
-  const int call = 0;
-  const char *br;
+  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_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 ();
+      ++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,t,p", sreg, treg);
-  else
-    macro_build (ep, br, "s,t,p", sreg, treg);
-}
 
-/*
- *                     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;
-  int ab = 0;
-  int off;
-  bfd_reloc_code_real_type r;
-  int hold_mips_optimize;
-
-  gas_assert (! mips_opts.mips16);
-
-  treg = EXTRACT_OPERAND (mips_opts.micromips, RT, *ip);
-  dreg = EXTRACT_OPERAND (mips_opts.micromips, RD, *ip);
-  sreg = breg = EXTRACT_OPERAND (mips_opts.micromips, RS, *ip);
-  mask = ip->insn_mo->mask;
-
-  label_expr.X_op = O_constant;
-  label_expr.X_op_symbol = NULL;
-  label_expr.X_add_symbol = NULL;
-  label_expr.X_add_number = 0;
-
-  expr1.X_op = O_constant;
-  expr1.X_op_symbol = NULL;
-  expr1.X_add_symbol = NULL;
-  expr1.X_add_number = 1;
-
-  switch (mask)
+  create_insn (&insn, mo);
+  for (; *fmt; ++fmt)
     {
-    case M_DABS:
-      dbl = 1;
-    case M_ABS:
-      /*    bgez    $a0,1f
-           move    v0,$a0
-           sub     v0,$zero,$a0
-        1:
-       */
-
-      start_noreorder ();
-
-      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 ();
-
-      end_noreorder ();
-      break;
+      int c;
 
-    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)
+      c = *fmt;
+      switch (c)
        {
-         macro_build (&imm_expr, s, "t,r,j", treg, sreg, BFD_RELOC_LO16);
+       case ',':
+       case '(':
+       case ')':
          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
-           {
-             macro_build (&imm_expr, "ori", "t,r,i",
-                          treg, sreg, BFD_RELOC_LO16);
-             macro_build (NULL, "nor", "d,v,t", treg, treg, 0);
-           }
+       case '0':
+       case 'S':
+       case 'P':
+       case 'R':
          break;
-       }
 
-      used_at = 1;
-      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
-      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 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);
+           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:
-         as_bad (_("BALIGN immediate not 0, 1, 2 or 3 (%lu)"),
-                 (unsigned long) imm_expr.X_add_number);
+         operand = decode_mips16_operand (c, FALSE);
+         if (!operand)
+           abort ();
+
+         insn_insert_operand (&insn, operand, va_arg (*args, int));
          break;
        }
-      break;
+    }
 
-    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;
+  gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
 
-    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;
+  append_insn (&insn, ep, r, TRUE);
+}
 
-    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;
+/*
+ * 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);
+}
 
-    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;
+/*
+ * 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);
+}
 
-    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)
-       {
-       do_false:
-         /* Result is always false.  */
-         if (! likely)
-           macro_build (NULL, "nop", "");
-         else
-           macro_build_branch_rsrt (M_BNEL, &offset_expr, ZERO, ZERO);
-         break;
-       }
-      if (imm_expr.X_op != O_constant)
-       as_bad (_("Unsupported large constant"));
-      ++imm_expr.X_add_number;
-      /* FALLTHROUGH */
-    case M_BGE_I:
-    case M_BGEL_I:
-      if (mask == M_BGEL_I)
-       likely = 1;
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
-       {
-         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;
+/*
+ * 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;
 
-    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);
+  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
-       {
-         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;
+       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);
+}
 
-    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;
+/*
+ * Generate a "lui" instruction.
+ */
+static void
+macro_build_lui (expressionS *ep, int regnum)
+{
+  gas_assert (! mips_opts.mips16);
 
-    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;
+  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));
+    }
 
-    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;
+  macro_build (ep, "lui", LUI_FMT, regnum, BFD_RELOC_HI16_S);
+}
 
-    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;
+/* 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);
 
-    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;
+  /* Sign-extending 32-bit constants makes their handling easier.  */
+  if (!dbl)
+    normalize_constant_expr (ep);
 
-    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;
+  /* 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_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 (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);
+
+      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, unsignedp ? "sltu" : "slt", "d,v,t", AT, reg, AT);
+    }
+}
+
+/* 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)
+    {
+      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))
        {
-         used_at = 1;
-         set_at (sreg, 1);
-         macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
-                                  &offset_expr, AT, ZERO);
+         /* 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;
        }
-      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
+      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_BNEL : M_BNE,
-                                  &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_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
+      else if ((IS_SEXT_32BIT_NUM (ep->X_add_number)))
        {
-         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);
+         /* 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;
        }
-      break;
+    }
 
-    case M_DEXT:
-      {
-       /* Use unsigned arithmetic.  */
-       addressT pos;
-       addressT size;
+  /* The value is larger than 32 bits.  */
 
-       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 (!dbl || HAVE_32BIT_GPRS)
+    {
+      char value[32];
 
-       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;
-         }
+      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 (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 (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 (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;
-         }
+  if (hi32.X_add_number == 0)
+    freg = 0;
+  else
+    {
+      int shift, bit;
+      unsigned long hi, lo;
 
-       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;
+      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;
+           }
+       }
 
-    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)
+      /* 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
        {
-         as_warn (_("Divide by zero."));
-         if (mips_trap)
-           macro_build (NULL, "teq", TRAP_FMT, ZERO, ZERO, 7);
+         unsigned long himask, lomask;
+
+         if (shift < 32)
+           {
+             himask = 0xffff >> (32 - shift);
+             lomask = (0xffff << shift) & 0xffffffff;
+           }
          else
-           macro_build (NULL, "break", BRK_FMT, 7);
-         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;
        }
+      while (shift <= (64 - 16));
 
-      start_noreorder ();
-      if (mips_trap)
+      /* 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 (NULL, "teq", TRAP_FMT, treg, ZERO, 7);
-         macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
+         bit = 0;
+         while ((lo & 1) == 0)
+           {
+             lo >>= 1;
+             ++bit;
+           }
+         lo |= (hi & (((unsigned long) 1 << bit) - 1)) << (32 - bit);
+         hi >>= bit;
        }
       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 ();
+         bit = 32;
+         while ((hi & 1) == 0)
+           {
+             hi >>= 1;
+             ++bit;
+           }
+         lo = hi;
+         hi = 0;
        }
-      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)
+
+      /* Optimize if the shifted value is a (power of 2) - 1.  */
+      if ((hi == 0 && ((lo + 1) & lo) == 0)
+         || (lo == 0xffffffff && ((hi + 1) & hi) == 0))
        {
-         expr1.X_add_number = 1;
-         load_register (AT, &expr1, dbl);
-         macro_build (NULL, "dsll32", SHFT_FMT, AT, AT, 31);
+         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;
+           }
        }
-      else
+
+      /* 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)
        {
-         expr1.X_add_number = 0x80000000;
-         macro_build (&expr1, "lui", LUI_FMT, AT, BFD_RELOC_HI16);
+         macro_build (NULL, "dsll32", SHFT_FMT, reg, freg, 0);
+         freg = reg;
        }
-      if (mips_trap)
+    }
+  else
+    {
+      expressionS mid16;
+
+      if ((freg == 0) && (lo32.X_add_number == (offsetT) 0xffffffff))
        {
-         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 ();
+         macro_build (&lo32, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
+         macro_build (NULL, "dsrl32", SHFT_FMT, reg, reg, 0);
+         return;
        }
-      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 (freg != 0)
        {
-         if (strcmp (s2, "mflo") == 0)
-           {
-             macro_build (NULL, dbl ? "dneg" : "neg", "d,w", dreg, sreg);
-           }
-         else
-           move_register (dreg, ZERO);
-         break;
+         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;
+    }
+  if ((lo32.X_add_number & 0xffff) != 0)
+    macro_build (&lo32, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
+}
 
-      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 inline void
+load_delay_nop (void)
+{
+  if (!gpr_interlocks)
+    macro_build (NULL, "nop", "");
+}
 
-    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);
+/* Load an address into a register.  */
 
-         /* 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;
+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;
+    }
 
-    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 (ep->X_op == O_constant)
+    {
+      load_register (reg, ep, HAVE_64BIT_ADDRESSES);
+      return;
+    }
 
-      if (dbl && HAVE_32BIT_GPRS)
-       as_warn (_("dla used to load 32-bit register"));
+  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.
 
-      if (!dbl && HAVE_64BIT_OBJECTS)
-       as_warn (_("la used to load 64-bit address"));
+        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 (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;
-       }
+        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)
 
-      if (mips_opts.at && (treg == breg))
+        For GP relative symbols in 64bit address space we can use
+        the same sequence as in 32bit address space.  */
+      if (HAVE_64BIT_SYMBOLS)
        {
-         tempreg = AT;
-         used_at = 1;
+         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 ();
        }
       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 ((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 ();
        }
+    }
+  else if (!mips_big_got)
+    {
+      expressionS ex;
 
-      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
-
-            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)
-
-            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 ();
-               }
-
-             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);
-               }
+      /* 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.
 
-             if (mips_relax.sequence)
-               relax_end ();
-           }
-         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)
+       {
+         if (ep->X_add_number)
            {
-             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 ();
+             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 if (!mips_big_got && !HAVE_NEWABI)
+      else
        {
-         int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
+         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 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 (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 if (mips_big_got)
+    {
+      expressionS ex;
 
-            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.
+      /* 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)
 
-            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.
-          */
+        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.
 
-         if (offset_expr.X_add_number == 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)
            {
-             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.  */
+             ex.X_op = O_constant;
+             macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+                          BFD_RELOC_LO16);
            }
-         else if (offset_expr.X_add_number >= -0x8000
-                  && offset_expr.X_add_number < 0x8000)
+
+         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))
            {
-             load_got_offset (tempreg, &offset_expr);
-             load_delay_nop ();
-             add_got_offset (tempreg, &offset_expr);
+             /* 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", "");
            }
-         else
+         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 ();
+
+         if (ex.X_add_number != 0)
            {
-             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 (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 if (!mips_big_got && HAVE_NEWABI)
-       {
-         int add_breg_early = 0;
+    }
+  else
+    abort ();
 
-         /* 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 (!mips_opts.at && *used_at == 1)
+    as_bad (_("Macro used $at after \".set noat\""));
+}
 
-            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>
+/* Move the contents of register SOURCE into register DEST.  */
 
-            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
+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);
+}
 
-            (*) Other assemblers seem to prefer GOT_PAGE/GOT_OFST for
-            local symbols, even though it introduces an additional
-            instruction.  */
+/* 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 (offset_expr.X_add_number)
-           {
-             expr1.X_add_number = offset_expr.X_add_number;
-             offset_expr.X_add_number = 0;
+   Global symbol               Local sybmol
+   -------------               ------------
+   lw DEST,%got(SYMBOL)                lw DEST,%got(SYMBOL + OFFSET)
+   ...                         ...
+   addiu DEST,DEST,OFFSET      addiu DEST,DEST,%lo(SYMBOL + OFFSET)
 
-             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);
+   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 (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;
-                   }
+static void
+load_got_offset (int dest, expressionS *local)
+{
+  expressionS global;
 
-                 load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
-                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                              dreg, dreg, AT);
+  global = *local;
+  global.X_add_number = 0;
 
-                 used_at = 1;
-               }
-             else
-               as_bad (_("PIC code offset overflow (max 32 signed bits)"));
+  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 ();
+}
 
-             relax_switch ();
-             offset_expr.X_add_number = expr1.X_add_number;
+static void
+add_got_offset (int dest, expressionS *local)
+{
+  expressionS global;
 
-             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;
+  global.X_op = O_constant;
+  global.X_op_symbol = NULL;
+  global.X_add_symbol = NULL;
+  global.X_add_number = local->X_add_number;
 
-         /* 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)
+  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 ();
+}
 
-            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)
+static void
+add_got_offset_hilo (int dest, expressionS *local, int tmp)
+{
+  expressionS global;
+  int hold_mips_optimize;
 
-            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
-         */
+  global.X_op = O_constant;
+  global.X_op_symbol = NULL;
+  global.X_add_symbol = NULL;
+  global.X_add_number = local->X_add_number;
 
-         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;
-               }
+  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 ();
 
-             load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
-             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
+  macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dest, dest, tmp);
+}
 
-             used_at = 1;
-           }
-         offset_expr.X_add_number = SEXT_16BIT (expr1.X_add_number);
-         relax_switch ();
+/* 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 (gpdelay)
-           {
-             /* This is needed because this instruction uses $gp, but
-                the first instruction on the main stream does not.  */
-             macro_build (NULL, "nop", "");
-           }
+   Code like this is produced in the noreorder mode:
 
-         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 (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;
-               }
+       BRNEG   <args>, 1f
+        nop
+       b       <sym>
+        delay slot (executed only if branch taken)
+    1:
 
-             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;
-           }
-         relax_end ();
-       }
-      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;
+   or, if CALL is set:
 
-         /* 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)
+       BRNEG   <args>, 1f
+        nop
+       bal     <sym>
+        delay slot (executed only if branch taken)
+    1:
 
-            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>
+   In the reorder mode the delay slot would be filled with a nop anyway,
+   so code produced is simply:
 
-            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
+       BR      <args>, <sym>
+        nop
 
-            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.  */
+   This function is used when producing code for the microMIPS ASE that
+   does not implement branch likely instructions in hardware.  */
 
-         relax_start (offset_expr.X_add_symbol);
+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;
 
-         expr1.X_add_number = offset_expr.X_add_number;
-         offset_expr.X_add_number = 0;
+  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 (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);
+      /* 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 (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;
-               }
+/* Emit a coprocessor branch-likely macro specified by TYPE, using CC as
+   the condition code tested.  EP specifies the branch target.  */
 
-             load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
-             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
+static void
+macro_build_branch_ccl (int type, expressionS *ep, unsigned int cc)
+{
+  const int call = 0;
+  const char *brneg;
+  const char *br;
 
-             used_at = 1;
-           }
-         else
-           as_bad (_("PIC code offset overflow (max 32 signed bits)"));
+  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);
+}
 
-         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 ();
-       }
-      else
-       abort ();
+/* Emit a two-argument branch macro specified by TYPE, using SREG as
+   the register tested.  EP specifies the branch target.  */
 
-      if (breg != 0)
-       macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, tempreg, breg);
-      break;
+static void
+macro_build_branch_rs (int type, expressionS *ep, unsigned int sreg)
+{
+  const char *brneg = NULL;
+  const char *br;
+  int call = 0;
 
-    case M_MSGSND:
-      gas_assert (!mips_opts.micromips);
-      {
-       unsigned long temp = (treg << 16) | (0x01);
-       macro_build (NULL, "c2", "C", temp);
-      }
+  switch (type)
+    {
+    case M_BGEZ:
+      br = "bgez";
       break;
-
-    case M_MSGLD:
-      gas_assert (!mips_opts.micromips);
-      {
-       unsigned long temp = (0x02);
-       macro_build (NULL, "c2", "C", temp);
-      }
+    case M_BGEZL:
+      br = mips_opts.micromips ? "bgez" : "bgezl";
+      brneg = "bltz";
       break;
-
-    case M_MSGLD_T:
-      gas_assert (!mips_opts.micromips);
-      {
-       unsigned long temp = (treg << 16) | (0x02);
-       macro_build (NULL, "c2", "C", temp);
-      }
+    case M_BGEZALL:
+      gas_assert (mips_opts.micromips);
+      br = mips_opts.insn32 ? "bgezal" : "bgezals";
+      brneg = "bltz";
+      call = 1;
       break;
-
-    case M_MSGWAIT:
-      gas_assert (!mips_opts.micromips);
-      macro_build (NULL, "c2", "C", 3);
+    case M_BGTZ:
+      br = "bgtz";
       break;
-
-    case M_MSGWAIT_T:
-      gas_assert (!mips_opts.micromips);
-      {
-       unsigned long temp = (treg << 16) | 0x03;
-       macro_build (NULL, "c2", "C", temp);
-      }
+    case M_BGTZL:
+      br = mips_opts.micromips ? "bgtz" : "bgtzl";
+      brneg = "blez";
       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_BLEZ:
+      br = "blez";
       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:
+    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);
-      if (mips_opts.insn32)
-       {
-         as_bad (_("Opcode not supported in the `insn32' mode `%s'"), str);
-         break;
-       }
-      jals = 1;
-      goto jal;
-    case M_JAL_1:
-      dreg = RA;
-      /* Fall through.  */
-    case M_JAL_2:
-    jal:
-      if (mips_pic == NO_PIC)
-       {
-         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);
-       }
-      else
-       {
-         int cprestore = (mips_pic == SVR4_PIC && !HAVE_NEWABI
-                          && mips_cprestore_offset >= 0);
+      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 (sreg != PIC_CALL_REG)
-           as_warn (_("MIPS PIC call to register other than $25"));
+/* Emit a three-argument branch macro specified by TYPE, using SREG and
+   TREG as the registers tested.  EP specifies the branch target.  */
 
-         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)
-           {
-             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);
-               }
-           }
-       }
+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)
+    {
+    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);
+}
 
-    case M_JALS_A:
-      gas_assert (mips_opts.micromips);
-      if (mips_opts.insn32)
-       {
-         as_bad (_("Opcode not supported in the `insn32' mode `%s'"), str);
-         break;
-       }
-      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)
-       {
-         /* 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)
+/* Return the high part that should be loaded in order to make the low
+   part of VALUE accessible using an offset of OFFBITS bits.  */
 
-            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 ();
-               }
+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;
+}
 
-             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;
+/* 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.  */
 
-                 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);
+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;
 
-             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);
-               }
-           }
-       }
-      else if (mips_pic == VXWORKS_PIC)
-       as_bad (_("Non-PIC jump used in PIC library"));
-      else
-       abort ();
+      /* 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;
+}
 
-      break;
+/*
+ *                     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;
 
-    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;
+  gas_assert (! mips_opts.mips16);
 
-    ld:
-      /* We don't want to use $0 as tempreg.  */
-      if (breg == treg + lp || treg + lp == ZERO)
-       goto ld_st;
-      else
-       tempreg = treg + lp;
-      goto ld_noat;
+  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;
 
-    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;
+  label_expr.X_op = O_constant;
+  label_expr.X_op_symbol = NULL;
+  label_expr.X_add_symbol = NULL;
+  label_expr.X_add_number = 0;
 
-    ld_st:
-      tempreg = AT;
-      used_at = 1;
-    ld_noat:
-      if (offset_expr.X_op != O_constant
-         && offset_expr.X_op != O_symbol)
+  expr1.X_op = O_constant;
+  expr1.X_op_symbol = NULL;
+  expr1.X_add_symbol = NULL;
+  expr1.X_add_number = 1;
+  align = 1;
+
+  switch (mask)
+    {
+    case M_DABS:
+      dbl = 1;
+    case M_ABS:
+      /*    bgez    $a0,1f
+           move    v0,$a0
+           sub     v0,$zero,$a0
+        1:
+       */
+
+      start_noreorder ();
+
+      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 ();
+
+      end_noreorder ();
+      break;
+
+    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)
        {
-         as_bad (_("Expression too complex"));
-         offset_expr.X_op = O_constant;
+         macro_build (NULL, s, "t,r,.", treg, sreg, imm_expr.X_add_number);
+         break;
        }
-
-      if (HAVE_32BIT_ADDRESSES
-         && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+      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)
        {
-         char value [32];
-
-         sprintf_vma (value, offset_expr.X_add_number);
-         as_bad (_("Number (0x%s) larger than 32 bits"), value);
+         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;
 
-      /* A constant expression in PIC code can be handled just as it
-        is in non PIC code.  */
-      if (offset_expr.X_op == O_constant)
+    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)
        {
-         int hipart = 0;
-
-         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))
-           {
-             expr1.X_add_number = ((expr1.X_add_number + 0x8000)
-                                   & ~(bfd_vma) 0xffff);
-             hipart = 1;
-           }
-         else if (offbits == 12 && !IS_SEXT_12BIT_NUM (expr1.X_add_number))
-           {
-             expr1.X_add_number = ((expr1.X_add_number + 0x800)
-                                   & ~(bfd_vma) 0xfff);
-             hipart = 1;
-           }
-         else if (offbits == 9 && !IS_SEXT_9BIT_NUM (expr1.X_add_number))
-           {
-             expr1.X_add_number = ((expr1.X_add_number + 0x100)
-                                   & ~(bfd_vma) 0x1ff);
-             hipart = 1;
-           }
-         if (hipart)
-           {
-             load_register (tempreg, &expr1, HAVE_64BIT_ADDRESSES);
-             if (breg != 0)
-               macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                            tempreg, tempreg, breg);
-             breg = tempreg;
-           }
-         if (offbits == 0)
-           {
-             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);
-           }
-         else if (offbits == 16)
-           macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16, breg);
+         if (mask != M_NOR_I)
+           macro_build (&imm_expr, s, "t,r,i", treg, sreg, BFD_RELOC_LO16);
          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 (&imm_expr, "ori", "t,r,i",
+                          treg, sreg, BFD_RELOC_LO16);
+             macro_build (NULL, "nor", "d,v,t", treg, treg, 0);
            }
-         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);
+         break;
        }
-      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.
+      used_at = 1;
+      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+      macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
+      break;
 
-            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)
+    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;
 
-            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)
+    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;
 
-            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 ();
-               }
+    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;
 
-             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);
-               }
+    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;
 
-             if (mips_relax.sequence)
-               relax_end ();
-             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;
 
-         if (breg == 0)
-           {
-             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 ();
-           }
+    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)
+       {
+       do_false:
+         /* Result is always false.  */
+         if (! likely)
+           macro_build (NULL, "nop", "");
          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 (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_branch_rsrt (M_BNEL, &offset_expr, ZERO, ZERO);
+         break;
        }
-      else if (!mips_big_got)
+      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 lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
+         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 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)
+    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;
 
-            For NewABI, we want
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_PAGE)
-              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_MIPS_GOT_OFST)
+    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;
 
-            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);
+    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);
        }
-      else if (mips_big_got && !HAVE_NEWABI)
+      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
        {
-         int gpdelay;
+         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;
 
-         /* 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_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;
 
-         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);
+    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);
        }
-      else if (mips_big_got && HAVE_NEWABI)
+      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
        {
-         /* 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);
+         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;
 
-         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 ();
+    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
-       abort ();
+       {
+         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_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_DINS:
+      {
+       /* Use unsigned arithmetic.  */
+       addressT pos;
+       addressT size;
 
-    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 || 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;
+         }
 
-    case M_LI:
-    case M_LI_S:
-      load_register (treg, &imm_expr, 0);
-      break;
+       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;
+         }
 
-    case M_DLI:
-      load_register (treg, &imm_expr, 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_LI_SS:
-      if (imm_expr.X_op == O_constant)
+    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)
        {
-         used_at = 1;
-         load_register (AT, &imm_expr, 0);
-         macro_build (NULL, "mtc1", "t,G", AT, treg);
+         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;
        }
-      else
+
+      start_noreorder ();
+      if (mips_trap)
        {
-         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 (NULL, "teq", TRAP_FMT, treg, ZERO, 7);
+         macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
        }
-
-    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);
+         if (mips_opts.micromips)
+           micromips_label_expr (&label_expr);
          else
-           {
-             int hreg, lreg;
-
-             if (target_big_endian)
-               {
-                 hreg = treg;
-                 lreg = treg + 1;
-               }
-             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
-                   {
-                     gas_assert (offset_expr.X_op == O_constant);
-                     load_register (lreg, &offset_expr, 0);
-                   }
-               }
-           }
-         break;
+           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 ();
        }
-
-      /* We know that sym is in the .rdata section.  First we get the
-        upper 16 bits of the address.  */
-      if (mips_pic == NO_PIC)
+      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)
        {
-         macro_build_lui (&offset_expr, AT);
-         used_at = 1;
+         expr1.X_add_number = 1;
+         load_register (AT, &expr1, dbl);
+         macro_build (NULL, "dsll32", SHFT_FMT, AT, AT, 31);
        }
       else
        {
-         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
-                      BFD_RELOC_MIPS_GOT16, mips_gp_register);
-         used_at = 1;
+         expr1.X_add_number = 0x80000000;
+         macro_build (&expr1, "lui", LUI_FMT, AT, BFD_RELOC_HI16);
        }
-
-      /* Now we load the register(s).  */
-      if (HAVE_64BIT_GPRS)
+      if (mips_trap)
        {
-         used_at = 1;
-         macro_build (&offset_expr, "ld", "t,o(b)", treg, BFD_RELOC_LO16, AT);
+         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
        {
-         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);
-           }
+         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_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)
+    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)
        {
-         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);
-           }
+         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, "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);
-               }
+             macro_build (NULL, dbl ? "dneg" : "neg", "d,w", dreg, sreg);
            }
+         else
+           move_register (dreg, ZERO);
          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)
+      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)
        {
-         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;
-           }
-         breg = mips_gp_register;
-         r = BFD_RELOC_MIPS_LITERAL;
-         goto dob;
+         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
        {
-         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);
+         if (mips_opts.micromips)
+           micromips_label_expr (&label_expr);
          else
-           {
-             /* FIXME: This won't work for a 64 bit address.  */
-             macro_build_lui (&offset_expr, AT);
-           }
+           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);
 
-         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;
+         /* 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 ();
        }
-
-    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);
+      macro_build (NULL, s2, MFHL_FMT, dreg);
       break;
 
-    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_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.  */
 
-    case M_L_DAB:
-      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?
-       */
-      /* 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;
-       }
-      s = "lwc1";
-      goto ldd_std;
+      if (dbl && HAVE_32BIT_GPRS)
+       as_warn (_("dla used to load 32-bit register"));
 
-    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))
+      if (!dbl && HAVE_64BIT_OBJECTS)
+       as_warn (_("la used to load 64-bit address"));
+
+      if (small_offset_p (0, align, 16))
        {
-         s = "sdc1";
-         goto ld_st;
+         macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", treg, breg,
+                      -1, offset_reloc[0], offset_reloc[1], offset_reloc[2]);
+         break;
        }
-      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)
+      if (mips_opts.at && (treg == breg))
        {
-         s = "ld";
-         goto ld;
+         tempreg = AT;
+         used_at = 1;
        }
-      s = "lw";
-      goto ldd_std;
-
-    case M_SD_AB:
-      fmt = "t,o(b)";
-      if (HAVE_64BIT_GPRS)
+      else
        {
-         s = "sd";
-         goto ld_st;
+         tempreg = treg;
        }
-      s = "sw";
 
-    ldd_std:
       if (offset_expr.X_op != O_symbol
          && offset_expr.X_op != O_constant)
        {
@@ -9558,4357 +8861,3597 @@ macro (struct mips_cl_insn *ip, char *str)
          offset_expr.X_op = O_constant;
        }
 
-      if (HAVE_32BIT_ADDRESSES
-         && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+      if (offset_expr.X_op == O_constant)
+       load_register (tempreg, &offset_expr, HAVE_64BIT_ADDRESSES);
+      else if (mips_pic == NO_PIC)
        {
-         char value [32];
+         /* 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.
 
-         sprintf_vma (value, offset_expr.X_add_number);
-         as_bad (_("Number (0x%s) larger than 32 bits"), value);
-       }
+            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
 
-      /* 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 $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 (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))
+            For GP relative symbols in 64bit address space we can use
+            the same sequence as in 32bit address space.  */
+         if (HAVE_64BIT_SYMBOLS)
            {
-             relax_start (offset_expr.X_add_symbol);
-             if (breg == 0)
+             if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+                 && !nopic_need_relax (offset_expr.X_add_symbol, 1))
                {
-                 tempreg = mips_gp_register;
+                 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 (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 (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                              AT, breg, mips_gp_register);
-                 tempreg = AT;
-                 used_at = 1;
+                 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);
                }
 
-             /* 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;
+             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 ();
            }
-         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)
+      else if (!mips_big_got && !HAVE_NEWABI)
        {
-         int gpdelay;
+         int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
 
-         /* 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)
+         /* 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
-              <op>     $treg,0($at)
-              <op>     $treg+1,4($at)
-            Otherwise we want
-              lw       $at,<sym>($gp)          (BFD_RELOC_MIPS_GOT16)
+              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
-              <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;
+              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.
 
-         /* 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 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.
+          */
 
-         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 (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;
 
-         /* 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 ();
+             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
-       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)
+      else if (!mips_big_got && HAVE_NEWABI)
        {
-         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;
-
-       
-    case M_SAA_AB:
-      ab = 1;
-    case M_SAA_OB:
-      s = "saa";
-      offbits = 0;
-      fmt = "t,(b)";
-      goto ld_st;
-    case M_SAAD_AB:
-      ab = 1;
-    case M_SAAD_OB:
-      s = "saad";
-      offbits = 0;
-      fmt = "t,(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?
+         int add_breg_early = 0;
 
-      If an itbl is provided to interpret cop instructions,
-      this knowledge can be encoded in the itbl spec.  */
+         /* 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)
 
-    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", ip->insn_opcode);
-      break;
+            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>
 
-    case M_MOVE:
-      move_register (dreg, sreg);
-      break;
+            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
 
-    case M_MOVEP:
-      gas_assert (mips_opts.micromips);
-      gas_assert (mips_opts.insn32);
-      dreg = micromips_to_32_reg_h_map[EXTRACT_OPERAND (1, MH, *ip)];
-      breg = micromips_to_32_reg_i_map[EXTRACT_OPERAND (1, MI, *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;
+            (*) Other assemblers seem to prefer GOT_PAGE/GOT_OFST for
+            local symbols, even though it introduces an additional
+            instruction.  */
 
-    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;
+         if (offset_expr.X_add_number)
+           {
+             expr1.X_add_number = offset_expr.X_add_number;
+             offset_expr.X_add_number = 0;
 
-    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;
+             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);
 
-    case M_DMULO_I:
-      dbl = 1;
-    case M_MULO_I:
-      imm = 1;
-      goto do_mulo;
+             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;
+                   }
 
-    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
-       {
-         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, "mflo", MFHL_FMT, dreg);
-      break;
+                 load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                              dreg, dreg, AT);
 
-    case M_DMULOU_I:
-      dbl = 1;
-    case M_MULOU_I:
-      imm = 1;
-      goto do_mulou;
+                 used_at = 1;
+               }
+             else
+               as_bad (_("PIC code offset overflow (max 32 signed bits)"));
 
-    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 (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;
+             relax_switch ();
+             offset_expr.X_add_number = expr1.X_add_number;
 
-    case M_DROL:
-      if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
-       {
-         if (dreg == sreg)
+             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))
            {
-             tempreg = AT;
-             used_at = 1;
+             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
            {
-             tempreg = dreg;
+             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                          BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
            }
-         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_ROL:
-      if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+      else if (mips_big_got && !HAVE_NEWABI)
        {
-         if (dreg == sreg)
+         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;
+
+         /* 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)
+
+            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)
+
+            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_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))
            {
-             tempreg = AT;
-             used_at = 1;
+             lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16;
+             lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16;
            }
-         else
+         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)
            {
-             tempreg = dreg;
+             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 ();
+               }
            }
-         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 (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;
+               }
 
-    case M_DROL_I:
-      {
-       unsigned int rot;
-       char *l;
-       char *rr;
+             load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
 
-       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;
+             used_at = 1;
+           }
+         offset_expr.X_add_number = SEXT_16BIT (expr1.X_add_number);
+         relax_switch ();
 
-    case M_ROL_I:
-      {
-       unsigned int rot;
+         if (gpdelay)
+           {
+             /* This is needed because this instruction uses $gp, but
+                the first instruction on the main stream does not.  */
+             macro_build (NULL, "nop", "");
+           }
 
-       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;
+         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 (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;
+               }
 
-    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;
+             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;
+           }
+         relax_end ();
        }
-      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;
+      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>
 
-    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 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
 
-    case M_DROR_I:
-      {
-       unsigned int rot;
-       char *l;
-       char *rr;
+            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.  */
 
-       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);
-      }
-      break;
+         relax_start (offset_expr.X_add_symbol);
 
-    case M_ROR_I:
-      {
-       unsigned int rot;
+         expr1.X_add_number = offset_expr.X_add_number;
+         offset_expr.X_add_number = 0;
 
-       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);
-      }
-      break;
+         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);
 
-    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 (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;
+               }
 
-    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))
-       {
-         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;
+             load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
 
-    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;
+             used_at = 1;
+           }
+         else
+           as_bad (_("PIC code offset overflow (max 32 signed bits)"));
 
-    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);
+         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 ();
        }
       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;
-
-    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;
+       abort ();
 
-    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);
+      if (breg != 0)
+       macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, tempreg, breg);
       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);
+
+    case M_MSGSND:
+      gas_assert (!mips_opts.micromips);
+      macro_build (NULL, "c2", "C", (treg << 16) | 0x01);
       break;
 
-    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);
+    case M_MSGLD:
+      gas_assert (!mips_opts.micromips);
+      macro_build (NULL, "c2", "C", 0x02);
       break;
 
-    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);
+    case M_MSGLD_T:
+      gas_assert (!mips_opts.micromips);
+      macro_build (NULL, "c2", "C", (treg << 16) | 0x02);
       break;
 
-    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);
+    case M_MSGWAIT:
+      gas_assert (!mips_opts.micromips);
+      macro_build (NULL, "c2", "C", 3);
       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);
+    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 (NULL, "xor", "d,v,t", dreg, sreg, treg);
-         macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
-       }
+       macro_build (&offset_expr, "b", "p");
       break;
 
-    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)
+      /* 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)
        {
-         macro_build (NULL, "snei", "t,r,+Q", dreg, sreg,
-                      (int) imm_expr.X_add_number);
+         as_bad (_("Opcode not supported in the `insn32' mode `%s'"), str);
          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))
+      jals = 1;
+      goto jal;
+    case M_JAL_1:
+      dreg = RA;
+      /* Fall through.  */
+    case M_JAL_2:
+    jal:
+      if (mips_pic == NO_PIC)
        {
-         used_at = 1;
-         load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-         macro_build (NULL, "sne", "d,v,t", dreg, sreg, AT);
-         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);
        }
       else
        {
-         load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-         macro_build (NULL, "xor", "d,v,t", dreg, sreg, AT);
-         used_at = 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)
+           {
+             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);
+               }
+           }
        }
-      macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
+
       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)
+    case M_JALS_A:
+      gas_assert (mips_opts.micromips);
+      if (mips_opts.insn32)
        {
-         macro_build (NULL, s, "t,r,.", dreg, sreg, -imm_expr.X_add_number);
+         as_bad (_("Opcode not supported in the `insn32' mode `%s'"), str);
          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)
+      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)
        {
-         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;
+         /* 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 ();
+               }
 
-    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;
+             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;
 
-    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 */
+                 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);
+               }
+           }
+       }
+      else if (mips_pic == VXWORKS_PIC)
+       as_bad (_("Non-PIC jump used in PIC library"));
+      else
+       abort ();
 
-      /*
-       * 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_A:
-      ab = 1;
-    case M_ULH:
+    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";
-      s2 = "lbu";
-      off = 1;
-      goto uld_st;
-    case M_ULHU_A:
-      ab = 1;
-    case M_ULHU:
+      fmt = "t,o(b)";
+      goto ld;
+    case M_LBU_AB:
       s = "lbu";
-      s2 = "lbu";
-      off = 1;
-      goto uld_st;
-    case M_ULW_A:
-      ab = 1;
-    case M_ULW:
+      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);
+      s = "lwc0";
+      fmt = "E,o(b)";
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_LWC1_AB:
+      s = "lwc1";
+      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";
-      s2 = "lwr";
+      fmt = MEM12_FMT;
       offbits = (mips_opts.micromips ? 12 : 16);
-      off = 3;
-      goto uld_st;
-    case M_ULD_A:
-      ab = 1;
-    case M_ULD:
+      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;
+      goto ld_st;
+    case M_LDC3_AB:
+      s = "ldc3";
+      fmt = "E,o(b)";
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_LDL_AB:
       s = "ldl";
-      s2 = "ldr";
+      fmt = MEM12_FMT;
       offbits = (mips_opts.micromips ? 12 : 16);
-      off = 7;
-      goto uld_st;
-    case M_USH_A:
-      ab = 1;
-    case M_USH:
+      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;
+
+    ld:
+      /* We don't want to use $0 as tempreg.  */
+      if (breg == treg + lp || treg + lp == ZERO)
+       goto ld_st;
+      else
+       tempreg = treg + lp;
+      goto ld_noat;
+
+    case M_SB_AB:
       s = "sb";
-      s2 = "sb";
-      off = 1;
-      ust = 1;
-      goto uld_st;
-    case M_USW_A:
-      ab = 1;
-    case M_USW:
+      fmt = "t,o(b)";
+      goto ld_st;
+    case M_SH_AB:
+      s = "sh";
+      fmt = "t,o(b)";
+      goto ld_st;
+    case M_SW_AB:
+      s = "sw";
+      fmt = "t,o(b)";
+      goto ld_st;
+    case M_SWC0_AB:
+      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:
+      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";
-      s2 = "swr";
+      fmt = MEM12_FMT;
       offbits = (mips_opts.micromips ? 12 : 16);
-      off = 3;
-      ust = 1;
-      goto uld_st;
-    case M_USD_A:
-      ab = 1;
-    case M_USD:
+      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";
-      s2 = "sdr";
+      fmt = MEM12_FMT;
       offbits = (mips_opts.micromips ? 12 : 16);
-      off = 7;
-      ust = 1;
-
-    uld_st:
-      if (!ab && offset_expr.X_add_number >= 0x8000 - off)
-       as_bad (_("Operand overflow"));
+      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;
 
-      ep = &offset_expr;
-      expr1.X_add_number = 0;
-      if (ab)
-       {
-         used_at = 1;
-         tempreg = AT;
-         load_address (tempreg, ep, &used_at);
-         if (breg != 0)
-           macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                        tempreg, tempreg, breg);
-         breg = tempreg;
-         tempreg = treg;
-         ep = &expr1;
-       }
-      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)))
-       {
-         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;
-       }
-      else if (!ust && treg == breg)
+    ld_st:
+      tempreg = AT;
+    ld_noat:
+      if (small_offset_p (0, align, 16))
        {
-         used_at = 1;
-         tempreg = AT;
+         /* 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
+           {
+             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;
        }
-      else
-       tempreg = treg;
-
-      if (off == 1)
-       goto ulh_sh;
-
-      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);
 
-      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);
-      else
-       macro_build (NULL, s2, "t,~(b)",
-                    tempreg, (unsigned long) ep->X_add_number, breg);
+      if (tempreg == AT)
+       used_at = 1;
 
-      /* 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);
+      if (offset_expr.X_op != O_constant
+         && offset_expr.X_op != O_symbol)
+       {
+         as_bad (_("Expression too complex"));
+         offset_expr.X_op = O_constant;
        }
-      break;
-
-    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);
 
-      /* 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);
+      if (HAVE_32BIT_ADDRESSES
+         && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+       {
+         char value [32];
 
-      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);
+         sprintf_vma (value, offset_expr.X_add_number);
+         as_bad (_("Number (0x%s) larger than 32 bits"), value);
+       }
 
-      /* For M_USH_A re-retrieve the LSB.  */
-      if (ust && ab)
+      /* 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 (target_big_endian)
-           ep->X_add_number += off;
+         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)
+           {
+             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
-           ep->X_add_number -= off;
-         macro_build (&expr1, "lbu", "t,o(b)", AT, BFD_RELOC_LO16, AT);
+           macro_build (NULL, s, fmt, treg,
+                        (int) offset_expr.X_add_number, tempreg);
        }
-      /* For ULH and M_USH_A OR the LSB in.  */
-      if (!ust || ab)
+      else if (offbits != 16)
        {
-         tempreg = !ab ? AT : treg;
-         macro_build (NULL, "sll", SHFT_FMT, tempreg, tempreg, 8);
-         macro_build (NULL, "or", "d,v,t", treg, treg, AT);
+         /* 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);
        }
-      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\""));
-}
-
-/* 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_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);
-
-      /* 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 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 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 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 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;
+      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.
 
-    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;
+            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.
 
-    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";
+            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)
 
-    do_reverse_branch:
-      tmp = xreg;
-      xreg = yreg;
-      yreg = tmp;
+            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)
 
-    do_branch:
-      macro_build (NULL, s, "x,y", xreg, yreg);
-      macro_build (&offset_expr, s2, "p");
-      break;
+            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 ();
+               }
 
-    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";
+             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);
+               }
 
-    do_addone_branch_i:
-      if (imm_expr.X_op != O_constant)
-       as_bad (_("Unsupported large constant"));
-      ++imm_expr.X_add_number;
+             if (mips_relax.sequence)
+               relax_end ();
+             break;
+           }
 
-    do_branch_i:
-      macro_build (&imm_expr, s, s3, xreg);
-      macro_build (&offset_expr, s2, "p");
-      break;
+         if (breg == 0)
+           {
+             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
+           {
+             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 ();
+           }
+       }
+      else if (!mips_big_got)
+       {
+         int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
 
-    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);
-    }
-}
+         /* 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 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;
+            For NewABI, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_PAGE)
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_MIPS_GOT_OFST)
 
-  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 ',': break;
-      case '(': break;
-      case ')': break;
-      case '+':
-       switch (c = *p++)
-         {
-         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;
-         }
-       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_STYPE,       OP_SH_STYPE);   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;
-      }
-#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;
-}
+            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;
 
-/* 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.  */
+         /* 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 ();
 
-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 (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
+              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);
 
-  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;
-    }
+         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 ();
 
-  /* 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 ',': break;
-      case '(': break;
-      case ')': break;
-      case '+':
-       e = c;
-       switch (c = *p++)
-         {
-         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;
-         }
-       break;
-      case 'm':
-       e = c;
-       switch (c = *p++)
-         {
-         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;
-         }
-       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;
-      }
-#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;
-}
+      break;
 
-/* UDI immediates.  */
-struct mips_immed {
-  char         type;
-  unsigned int shift;
-  unsigned long        mask;
-  const char * desc;
-};
+    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;
 
-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 }
-};
+    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;
 
-/* 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;
+    case M_LI:
+    case M_LI_S:
+      load_register (treg, &imm_expr, 0);
+      break;
 
-  if (insn->pinfo == INSN_MACRO)
-    /* Let a macro pass, we'll catch it later when it is expanded.  */
-    return 1;
+    case M_DLI:
+      load_register (treg, &imm_expr, 1);
+      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_LI_SS:
+      if (imm_expr.X_op == O_constant)
        {
-       case FP_S:
-       case 0:
-         return 1;     /* both single precision - ok */
-       case FP_D:
-         return 0;     /* both double precision - fail */
-       default:
+         used_at = 1;
+         load_register (AT, &imm_expr, 0);
+         macro_build (NULL, "mtc1", "t,G", AT, treg);
+         break;
+       }
+      else
+       {
+         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;
        }
 
-      /* Cvt.w.x and cvt.x.w allow an odd register for a 'w' or 's' operand.  */
-      s = strchr (insn->name, '.');
-      if (argnum == 2)
-       s = s != NULL ? strchr (s + 1, '.') : NULL;
-      return (s != NULL && (s[1] == 'w' || s[1] == 's'));
-    } 
-
-  /* Single-precision coprocessor loads and moves are OK too.  */
-  if ((insn->pinfo & FP_S)
-      && (insn->pinfo & (INSN_COPROC_MEMORY_DELAY | INSN_STORE_MEMORY
-                        | INSN_LOAD_COPROC_DELAY | INSN_COPROC_MOVE_DELAY)))
-    return 1;
-
-  return 0;
-}
+    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)
+       {
+         if (HAVE_64BIT_GPRS)
+           load_register (treg, &imm_expr, 1);
+         else
+           {
+             int hreg, lreg;
 
-/* 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);
-}
+             if (target_big_endian)
+               {
+                 hreg = treg;
+                 lreg = treg + 1;
+               }
+             else
+               {
+                 hreg = treg + 1;
+                 lreg = treg;
+               }
 
-/* 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.  */
+             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;
+       }
 
-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;
+      /* 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;
+       }
 
-  insn_error = NULL;
+      /* 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
+       {
+         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 (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_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)
+           {
+             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;
+       }
 
-  /* 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;
+      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;
+       }
+      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);
+           }
 
-  /* Make a copy of the instruction so that we can fiddle with it.  */
-  name = alloca (end + 1);
-  memcpy (name, str, end);
-  name[end] = '\0';
+         breg = AT;
+         offset_reloc[0] = BFD_RELOC_LO16;
+         offset_reloc[1] = BFD_RELOC_UNUSED;
+         offset_reloc[2] = BFD_RELOC_UNUSED;
+       }
+      align = 8;
+      /* Fall through */
 
-  for (;;)
-    {
-      insn = (struct mips_opcode *) hash_find (hash, name);
+    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))
+       {
+         s = "ldc1";
+         goto ld_st;
+       }
+      s = "lwc1";
+      goto ldd_std;
 
-      if (insn != NULL || !mips_opts.micromips)
-       break;
-      if (forced_insn_length)
-       break;
+    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))
+       {
+         s = "sdc1";
+         goto ld_st;
+       }
+      s = "swc1";
+      goto ldd_std;
 
-      /* 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_LQ_AB:
+      fmt = "t,o(b)";
+      s = "lq";
+      goto ld;
 
-  /* 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_SQ_AB:
+      fmt = "t,o(b)";
+      s = "sq";
+      goto ld_st;
 
-      gas_assert (strcmp (insn->name, name) == 0);
+    case M_LD_AB:
+      fmt = "t,o(b)";
+      if (HAVE_64BIT_GPRS)
+       {
+         s = "ld";
+         goto ld;
+       }
+      s = "lw";
+      goto ldd_std;
 
-      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)
+    case M_SD_AB:
+      fmt = "t,o(b)";
+      if (HAVE_64BIT_GPRS)
        {
-         firstinsn = insn;
-         wrong_delay_slot_insns = TRUE;
+         s = "sd";
+         goto ld_st;
        }
-      if (!ok || !size_ok || delay_slot_ok != need_delay_slot_ok)
-       {
-         static char buf[256];
+      s = "sw";
+
+    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 (insn + 1 < past && strcmp (insn->name, insn[1].name) == 0)
+      if (small_offset_p (0, align, 16))
+       {
+         ep = &offset_expr;
+         if (!small_offset_p (4, align, 16))
            {
-             ++insn;
-             continue;
+             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 (wrong_delay_slot_insns && need_delay_slot_ok)
+         if (strcmp (s, "lw") == 0 && treg == breg)
            {
-             gas_assert (firstinsn);
-             need_delay_slot_ok = FALSE;
-             past = insn + 1;
-             insn = firstinsn;
-             continue;
+             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);
            }
-
-         if (insn_error)
-           return;
-
-         if (!ok)
-           sprintf (buf, _("Opcode not supported on this processor: %s (%s)"),
-                    mips_cpu_info_from_arch (mips_opts.arch)->name,
-                    mips_cpu_info_from_isa (mips_opts.isa)->name);
-         else if (mips_opts.insn32)
-           sprintf (buf, _("Opcode not supported in the `insn32' mode"));
          else
-           sprintf (buf, _("Unrecognized %u-bit version of microMIPS opcode"),
-                    8 * forced_insn_length);
-         insn_error = buf;
-
-         return;
+           {
+             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;
        }
 
-      create_insn (ip, insn);
-      insn_error = NULL;
-      argnum = 1;
-      lastregno = 0xffffffff;
-      for (args = insn->args;; ++args)
+      if (offset_expr.X_op != O_symbol
+         && offset_expr.X_op != O_constant)
        {
-         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 21 (for standard MIPS
-                code) or 13 (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 21 (for standard MIPS
-                code) or 12 (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 16 (for standard MIPS
-                code) or 13 (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 21 (for standard MIPS
-                code) or 16 (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 bit 11 (for standard MIPS code)
-                or 14 (for microMIPS code).  */
-             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 20 (for standard MIPS
-                code) or 16 (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;
+         as_bad (_("Expression too complex"));
+         offset_expr.X_op = O_constant;
+       }
 
-            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 (HAVE_32BIT_ADDRESSES
+         && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+       {
+         char value [32];
 
-           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;
+         sprintf_vma (value, offset_expr.X_add_number);
+         as_bad (_("Number (0x%s) larger than 32 bits"), value);
+       }
 
-           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')
+      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)
                {
-                 regno = s[3] - '0';
-                 s += 4;
-                 INSERT_OPERAND (0, MTACC_D, *ip, regno);
-                 continue;
+                 tempreg = mips_gp_register;
                }
              else
-               as_bad (_("Invalid dsp/smartmips acc register"));
-             break;
-
-           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;
-
-           case ',':
-             ++argnum;
-             if (*s++ == *args)
-               continue;
-             s--;
-             switch (*++args)
                {
-               case 'r':
-               case 'v':
-                 INSERT_OPERAND (mips_opts.micromips, RS, *ip, lastregno);
-                 continue;
-
-               case 'w':
-                 INSERT_OPERAND (mips_opts.micromips, RT, *ip, lastregno);
-                 continue;
-
-               case 'W':
-                 gas_assert (!mips_opts.micromips);
-                 INSERT_OPERAND (0, FT, *ip, lastregno);
-                 continue;
-
-               case 'V':
-                 INSERT_OPERAND (mips_opts.micromips, FS, *ip, lastregno);
-                 continue;
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                              AT, breg, mips_gp_register);
+                 tempreg = AT;
+                 used_at = 1;
                }
-             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;
-
-           case '[':           /* These must match exactly.  */
-           case ']':
-             gas_assert (!mips_opts.micromips);
-             if (*s++ == *args)
-               continue;
-             break;
 
-           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;
-
-               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;
-
-               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;
-
-               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;
-
-               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;
-
-               case 'D':
-                 /* +D is for disassembly only; never match.  */
-                 break;
-
-               case 'I':
-                 /* "+I" is like "I", except that imm2_expr is used.  */
-                 my_getExpression (&imm2_expr, s);
-                 if (imm2_expr.X_op != O_big
-                     && imm2_expr.X_op != O_constant)
-                 insn_error = _("absolute expression required");
-                 if (HAVE_32BIT_GPRS)
-                   normalize_constant_expr (&imm2_expr);
-                 s = expr_end;
-                 continue;
-
-               case 'T': /* Coprocessor register.  */
-                 gas_assert (!mips_opts.micromips);
-                 /* +T is for disassembly only; never match.  */
-                 break;
+             /* 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;
 
-               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;
+             /* 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;
 
-               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;
+             relax_switch ();
 
-               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;
+             offset_expr.X_add_number -= 4;
+           }
+         used_at = 1;
+         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", 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);
 
-               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;
+         /* Set mips_optimize to 2 to avoid inserting an undesired
+             nop.  */
+         hold_mips_optimize = mips_optimize;
+         mips_optimize = 2;
 
-               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;
+         /* 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 ();
 
-               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;
+         mips_optimize = hold_mips_optimize;
+       }
+      else if (mips_big_got)
+       {
+         int gpdelay;
 
-               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;
+         /* 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;
 
-               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;
+         /* 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;
 
-               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;
+         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;
 
-               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;
+         /* 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
+       abort ();
 
-               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;
+      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;
 
-               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;
+   /* 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.
 
-               case 'Z':
-                 gas_assert (!mips_opts.micromips);
-                 if (!reg_lookup (&s, RTYPE_FPU, &regno))
-                   break;
-                 INSERT_OPERAND (0, FZ, *ip, regno);
-                 continue;
+      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?
 
-               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 an itbl is provided to interpret cop instructions,
+      this knowledge can be encoded in the itbl spec.  */
 
-                   /* 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;
+    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:
-                 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;
+    case M_MOVE:
+      move_register (dreg, sreg);
+      break;
 
-           case '.':           /* 10-bit offset.  */
-             gas_assert (mips_opts.micromips);
-           case '~':           /* 12-bit offset.  */
-             {
-               int shift = *args == '.' ? 9 : 11;
-               size_t i;
+    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;
 
-               /* 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;
+    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;
 
-               /* 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;
+    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 '<':           /* 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;
+    case M_DMULO_I:
+      dbl = 1;
+    case M_MULO_I:
+      imm = 1;
+      goto do_mulo;
 
-           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;
+    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
+       {
+         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, "mflo", MFHL_FMT, dreg);
+      break;
 
-           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;
+    case M_DMULOU_I:
+      dbl = 1;
+    case M_MULOU_I:
+      imm = 1;
+      goto do_mulou;
 
-           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;
+    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 (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 '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;
-             }
-             continue;
+    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 '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;
-             }
-             continue;
+    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 '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;
+    case M_DROL_I:
+      {
+       unsigned int rot;
+       char *l;
+       char *rr;
 
-           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 (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 '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;
+    case M_ROL_I:
+      {
+       unsigned int rot;
 
-           case 'G':           /* Coprocessor destination register.  */
-             {
-               unsigned long opcode = ip->insn_opcode;
-               unsigned long mask;
-               unsigned int types;
-               int cop0;
+       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 (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)
-                 {
-                   lastregno = regno;
-                   continue;
-                 }
-             }
-             break;
+    case M_DROR:
+      if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+       {
+         macro_build (NULL, "drorv", "d,t,s", dreg, sreg, treg);
+         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;
 
-           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
-               {
-                 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)
-               {
-                 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;
+    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 (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++)
-               {
-               case 'r':
-               case 'v':
-                 INSERT_OPERAND (mips_opts.micromips, RS, *ip, lastregno);
-                 continue;
+    case M_DROR_I:
+      {
+       unsigned int rot;
+       char *l;
+       char *rr;
 
-               case 'w':
-                 INSERT_OPERAND (mips_opts.micromips, RT, *ip, lastregno);
-                 continue;
-               }
-             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);
+      }
+      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 M_ROR_I:
+      {
+       unsigned int rot;
 
-           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 '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;
-               }
+       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);
+      }
+      break;
 
-             switch (*args++)
-               {
-               case 'V':
-                 INSERT_OPERAND (mips_opts.micromips, FS, *ip, lastregno);
-                 continue;
+    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;
 
-               case 'W':
-                 INSERT_OPERAND (mips_opts.micromips, FT, *ip, lastregno);
-                 continue;
-               }
-             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))
+       {
+         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;
 
-           case 'I':
-             my_getExpression (&imm_expr, s);
-             if (imm_expr.X_op != O_big
-                 && imm_expr.X_op != O_constant)
-               insn_error = _("absolute expression required");
-             if (HAVE_32BIT_GPRS)
-               normalize_constant_expr (&imm_expr);
-             s = expr_end;
-             continue;
+    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 'A':
-             my_getExpression (&offset_expr, s);
-             normalize_address_expr (&offset_expr);
-             *imm_reloc = BFD_RELOC_32;
-             s = expr_end;
-             continue;
+    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;
 
-           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;
-                 }
+    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;
 
-               gas_assert (length == (unsigned) (f64 ? 8 : 4));
+    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;
 
-               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
-                     {
-                       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
-                 {
-                   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);
-                   bfd_set_section_flags (stdoutput, new_seg,
-                                          (SEC_ALLOC
-                                           | SEC_LOAD
-                                           | SEC_READONLY
-                                           | SEC_DATA));
-                   frag_align (*args == 'l' ? 2 : 3, 0, 0);
-                   if (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 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;
 
-           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);
-
-                 /* 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;
+    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 (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 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;
 
-           case 'o':           /* 16-bit offset.  */
-             offset_reloc[0] = BFD_RELOC_LO16;
-             offset_reloc[1] = BFD_RELOC_UNUSED;
-             offset_reloc[2] = BFD_RELOC_UNUSED;
+    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;
 
-             /* 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)
-               {
-                 offset_expr.X_op = O_constant;
-                 offset_expr.X_add_number = 0;
-                 continue;
-               }
+    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;
 
-             /* 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;
+    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;
 
-             s = expr_end;
-             continue;
+    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;
 
-           case 'p':           /* PC-relative offset.  */
-             *offset_reloc = BFD_RELOC_16_PCREL_S2;
-             my_getExpression (&offset_expr, s);
-             s = expr_end;
-             continue;
+    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;
 
-           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;
-             continue;
+    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 */
 
-           case 'a':           /* 26-bit address.  */
-             *offset_reloc = BFD_RELOC_MIPS_JMP;
-             my_getExpression (&offset_expr, s);
-             s = expr_end;
-             continue;
+      /*
+       * 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 '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);
-             continue;
+    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;
 
-           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.  */
+    uld_st:
+      large_offset = !small_offset_p (off, align, offbits);
+      ep = &offset_expr;
+      expr1.X_add_number = 0;
+      if (large_offset)
+       {
+         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
+           {
+             load_address (tempreg, ep, &used_at);
+             if (breg != 0)
+               macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                            tempreg, tempreg, breg);
+           }
+         offset_reloc[0] = BFD_RELOC_LO16;
+         offset_reloc[1] = BFD_RELOC_UNUSED;
+         offset_reloc[2] = BFD_RELOC_UNUSED;
+         breg = tempreg;
+         tempreg = treg;
+         ep = &expr1;
+       }
+      else if (!ust && treg == breg)
+       {
+         used_at = 1;
+         tempreg = AT;
+       }
+      else
+       tempreg = treg;
 
-             if (c > 7)
-               as_bad (_("Invalid coprocessor sub-selection value (0-7)"));
-             INSERT_OPERAND (mips_opts.micromips, SEL, *ip, c);
-             continue;
+      if (off == 1)
+       goto ulh_sh;
 
-           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 (!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);
 
-             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;
-               }
+      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);
 
-             INSERT_OPERAND (0, VECBYTE, *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 '%':
-             gas_assert (!mips_opts.micromips);
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
+    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);
 
-             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;
-               }
+      /* 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);
 
-             INSERT_OPERAND (0, VECALIGN, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             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 'm':           /* Opcode extension character.  */
-             gas_assert (mips_opts.micromips);
-             c = *++args;
-             switch (c)
-               {
-               case 'r':
-                 if (strncmp (s, "$pc", 3) == 0)
-                   {
-                     s += 3;
-                     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 'a':
-               case 'b':
-               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;
-                   }
+    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\""));
+}
 
-                 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;
-                       }
-                   }
+/* Implement macros in mips16 mode.  */
 
-                 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.  */
-                       }
-                   }
+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;
 
-                 /* Make sure regno is the same as lastregno.  */
-                 if (c == 't' && regno != lastregno)
-                   break;
+  mask = ip->insn_mo->mask;
 
-                 /* Make sure regno is the same as destregno.  */
-                 if (c == 'x' && regno != destregno)
-                   break;
+  xreg = MIPS16_EXTRACT_OPERAND (RX, *ip);
+  yreg = MIPS16_EXTRACT_OPERAND (RY, *ip);
+  zreg = MIPS16_EXTRACT_OPERAND (RZ, *ip);
 
-                 /* We need to save regno, before regno maps to the
-                    microMIPS register encoding.  */
-                 lastregno = regno;
+  expr1.X_op = O_constant;
+  expr1.X_op_symbol = NULL;
+  expr1.X_add_symbol = NULL;
+  expr1.X_add_number = 1;
 
-                 if (c == 'f')
-                   destregno = regno;
+  dbl = 0;
 
-                 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 ();
-                   }
+  switch (mask)
+    {
+    default:
+      abort ();
 
-                 if (regno == ILLEGAL_REG)
-                   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:
+      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);
 
-                 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;
+      /* 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 '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 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 'B':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
+    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;
 
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || ep.X_op != O_constant)
-                     break;
+    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;
 
-                   for (imm = 0; imm < 8; imm++)
-                     if (micromips_imm_b_map[imm] == ep.X_add_number)
-                       break;
-                   if (imm >= 8)
-                     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);
+      break;
 
-                   INSERT_OPERAND (1, IMMB, *ip, imm);
-                 }
-                 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 'C':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
+    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";
 
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || ep.X_op != O_constant)
-                     break;
+    do_reverse_branch:
+      tmp = xreg;
+      xreg = yreg;
+      yreg = tmp;
 
-                   for (imm = 0; imm < 16; imm++)
-                     if (micromips_imm_c_map[imm] == ep.X_add_number)
-                       break;
-                   if (imm >= 16)
-                     break;
+    do_branch:
+      macro_build (NULL, s, "x,y", xreg, yreg);
+      macro_build (&offset_expr, s2, "p");
+      break;
 
-                   INSERT_OPERAND (1, IMMC, *ip, imm);
-                 }
-                 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 'D':       /* pc relative offset */
-               case 'E':       /* pc relative offset */
-                 my_getExpression (&offset_expr, s);
-                 if (offset_expr.X_op == O_register)
-                   break;
+    do_addone_branch_i:
+      if (imm_expr.X_op != O_constant)
+       as_bad (_("Unsupported large constant"));
+      ++imm_expr.X_add_number;
 
-                 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;
+    do_branch_i:
+      macro_build (&imm_expr, s, s3, xreg);
+      macro_build (&offset_expr, s2, "p");
+      break;
 
-               case 'F':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
+    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);
+    }
+}
 
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, 0, 16, 0))
-                     break;
+/* 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.  */
 
-                   imm = ep.X_add_number;
-                   INSERT_OPERAND (1, IMMF, *ip, imm);
-                 }
-                 s = expr_end;
-                 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 '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;
+  insn_error = NULL;
 
-               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;
+  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 'I':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
+  /* 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;
 
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, -1, 127, 0))
-                     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';
 
-                   imm = ep.X_add_number & 127;
-                   INSERT_OPERAND (1, IMMI, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
+  for (;;)
+    {
+      insn = (struct mips_opcode *) hash_find (hash, name);
 
-               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;
+      if (insn != NULL || !mips_opts.micromips)
+       break;
+      if (forced_insn_length)
+       break;
 
-               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;
+      /* 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':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
+  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;
 
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, 1, 9, 0))
-                     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.  */
+  for (;;)
+    {
+      bfd_boolean delay_slot_ok;
+      bfd_boolean size_ok;
+      bfd_boolean ok;
+      bfd_boolean more_alts;
 
-                   imm = ep.X_add_number & 7;
-                   INSERT_OPERAND (1, IMMM, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
+      gas_assert (strcmp (insn->name, name) == 0);
 
-               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.:
+      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];
 
-                      s0, ra
-                      s0, s1, ra, s2, s3
-                      s0-s2, ra
+         if (more_alts)
+           {
+             ++insn;
+             continue;
+           }
+         if (wrong_delay_slot_insns && need_delay_slot_ok)
+           {
+             gas_assert (firstinsn);
+             need_delay_slot_ok = FALSE;
+             past = insn + 1;
+             insn = firstinsn;
+             continue;
+           }
 
-                      and any permutations of these.  */
-                   unsigned int reglist;
-                   int imm;
+         obstack_free (&mips_operand_tokens, tokens);
+         if (insn_error)
+           return;
 
-                   if (!reglist_lookup (&s, RTYPE_NUM | RTYPE_GP, &reglist))
-                     break;
+         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;
 
-                   if ((reglist & 0xfff1ffff) != 0x80010000)
-                     break;
+         return;
+       }
 
-                   reglist = (reglist >> 17) & 7;
-                   reglist += 1;
-                   if ((reglist & -reglist) != reglist)
-                     break;
+      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;
 
-                   imm = ffs (reglist) - 1;
-                   INSERT_OPERAND (1, IMMN, *ip, imm);
+      create_insn (ip, insn);
+      insn_error = NULL;
+      memset (&arg, 0, sizeof (arg));
+      arg.insn = ip;
+      arg.token = tokens;
+      arg.argnum = 1;
+      arg.last_regno = ILLEGAL_REG;
+      arg.dest_regno = ILLEGAL_REG;
+      arg.soft_match = (more_alts
+                       || (wrong_delay_slot_insns && need_delay_slot_ok));
+      for (args = insn->args;; ++args)
+       {
+         if (arg.token->type == OT_END)
+           {
+             /* Handle unary instructions in which only one operand is given.
+                The source is then the same as the destination.  */
+             if (arg.opnum == 1 && *args == ',')
+               switch (args[1])
+                 {
+                 case 'r':
+                 case 'v':
+                 case 'w':
+                 case 'W':
+                 case 'V':
+                   arg.token = tokens;
+                   arg.argnum = 1;
+                   continue;
                  }
-                 continue;
 
-               case 'O':       /* sdbbp 4-bit code.  */
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
+             /* Treat elided base registers as $0.  */
+             if (strcmp (args, "(b)") == 0)
+               args += 3;
 
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, 0, 16, 0))
-                     break;
+             /* Fail the match if there were too few operands.  */
+             if (*args)
+               break;
 
-                   imm = ep.X_add_number;
-                   INSERT_OPERAND (1, IMMO, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
+             /* Successful match.  */
+             if (arg.dest_regno == arg.last_regno
+                 && strncmp (ip->insn_mo->name, "jalr", 4) == 0)
+               {
+                 if (arg.opnum == 2)
+                   as_bad (_("Source and destination must be different"));
+                 else if (arg.last_regno == 31)
+                   as_bad (_("A destination register must be supplied"));
+               }
+             check_completed_insn (&arg);
+             obstack_free (&mips_operand_tokens, tokens);
+             return;
+           }
 
-               case 'P':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
+         /* Fail the match if the line has too many operands.   */
+         if (*args == 0)
+           break;
 
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, 0, 32, 2))
-                     break;
+         /* Handle characters that need to match exactly.  */
+         if (*args == '(' || *args == ')' || *args == ',')
+           {
+             if (match_char (&arg, *args))
+               continue;
+             break;
+           }
 
-                   imm = ep.X_add_number >> 2;
-                   INSERT_OPERAND (1, IMMP, *ip, imm);
-                 }
-                 s = expr_end;
+         /* Handle special macro operands.  Work out the properties of
+            other operands.  */
+         arg.opnum += 1;
+         arg.lax_max = FALSE;
+         optional_reg = FALSE;
+         switch (*args)
+           {
+           case '+':
+             switch (args[1])
+               {
+               case '1':
+               case '2':
+               case '3':
+               case '4':
+               case 'B':
+               case 'C':
+               case 'F':
+               case 'G':
+               case 'H':
+               case 'J':
+               case 'Q':
+               case 'S':
+               case 's':
+                 /* If these integer forms come last, there is no other
+                    form of the instruction that could match.  Prefer to
+                    give detailed error messages where possible.  */
+                 if (args[2] == 0)
+                   arg.soft_match = FALSE;
+                 break;
+
+               case 'I':
+                 /* "+I" is like "I", except that imm2_expr is used.  */
+                 if (match_const_int (&arg, &imm2_expr.X_add_number, 0))
+                   imm2_expr.X_op = O_constant;
+                 else
+                   insn_error = _("absolute expression required");
+                 if (HAVE_32BIT_GPRS)
+                   normalize_constant_expr (&imm2_expr);
+                 ++args;
                  continue;
 
-               case 'Q':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
+               case 'i':
+                 *offset_reloc = BFD_RELOC_MIPS_JMP;
+                 break;
+               }
+             break;
 
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, -0x400000, 0x400000, 2))
-                     break;
+           case '\'':
+           case ':':
+           case '@':
+           case '^':
+           case '$':
+           case '\\':
+           case '%':
+           case '|':
+           case '0':
+           case '1':
+           case '2':
+           case '3':
+           case '4':
+           case '5':
+           case '6':
+           case '8':
+           case 'B':
+           case 'C':
+           case 'J':
+           case 'O':
+           case 'P':
+           case 'Q':
+           case 'c':
+           case 'h':
+           case 'q':
+             /* If these integer forms come last, there is no other
+                form of the instruction that could match.  Prefer to
+                give detailed error messages where possible.  */
+             if (args[1] == 0)
+               arg.soft_match = FALSE;
+             break;
 
-                   imm = ep.X_add_number >> 2;
-                   INSERT_OPERAND (1, IMMQ, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
+           case 'r':
+           case 'v':
+           case 'w':
+           case 'W':
+           case 'V':
+             /* We have already matched a comma by this point, so the register
+                is only optional if there is another operand to come.  */
+             gas_assert (arg.opnum == 2);
+             optional_reg = (args[1] == ',');
+             break;
 
-               case '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 '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 'W':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
+           case 'A':
+             if (arg.token->type == OT_CHAR && arg.token->u.ch == '(')
+               {
+                 /* Assume that the offset has been elided and that what
+                    we saw was a base register.  The match will fail later
+                    if that assumption turns out to be wrong.  */
+                 offset_expr.X_op = O_constant;
+                 offset_expr.X_add_number = 0;
+               }
+             else if (match_expression (&arg, &offset_expr, offset_reloc))
+               normalize_address_expr (&offset_expr);
+             else
+               insn_error = _("absolute expression required");
+             continue;
 
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, 0, 64, 2))
-                     break;
+           case 'F':
+             if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+                                        8, TRUE))
+               insn_error = _("floating-point expression required");
+             continue;
 
-                   imm = ep.X_add_number >> 2;
-                   INSERT_OPERAND (1, IMMW, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
+           case 'L':
+             if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+                                        8, FALSE))
+               insn_error = _("floating-point expression required");
+             continue;
 
-               case 'X':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
-                   int imm;
+           case 'f':
+             if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+                                        4, TRUE))
+               insn_error = _("floating-point expression required");
+             continue;
 
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, -8, 8, 0))
-                     break;
+           case 'l':
+             if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+                                        4, FALSE))
+               insn_error = _("floating-point expression required");
+             continue;
 
-                   imm = ep.X_add_number;
-                   INSERT_OPERAND (1, IMMX, *ip, imm);
-                 }
-                 s = expr_end;
-                 continue;
+             /* ??? This is the traditional behavior, but is flaky if
+                there are alternative versions of the same instruction
+                for different subarchitectures.  The next alternative
+                might not be suitable.  */
+           case 'j':
+             /* For compatibility with older assemblers, we accept
+                0x8000-0xffff as signed 16-bit numbers when only
+                signed numbers are allowed.  */
+             arg.lax_max = !more_alts;
+           case 'i':
+             /* Only accept non-constant operands if this is the
+                final alternative.  Later alternatives might include
+                a macro implementation.  */
+             arg.allow_nonconst = !more_alts;
+             break;
 
-               case '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 'u':
+             /* There are no macro implementations for out-of-range values.  */
+             arg.allow_nonconst = TRUE;
+             break;
 
-               case 'Z':
-                 {
-                   bfd_reloc_code_real_type r[3];
-                   expressionS ep;
+           case 'o':
+             /* There should always be a macro implementation.  */
+             arg.allow_nonconst = FALSE;
+             break;
 
-                   if (my_getSmallExpression (&ep, r, s) > 0
-                       || !expr_const_in_range (&ep, 0, 1, 0))
-                     break;
-                 }
-                 s = expr_end;
-                 continue;
+           case 'p':
+             *offset_reloc = BFD_RELOC_16_PCREL_S2;
+             break;
 
-               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;
-               }
+           case 'a':
+             *offset_reloc = BFD_RELOC_MIPS_JMP;
              break;
 
-           case 'n':           /* Register list for 32-bit lwm and swm.  */
+           case 'm':
              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))
+             c = args[1];
+             switch (c)
+               {
+               case 't':
+               case 'c':
+               case 'e':
+                 /* We have already matched a comma by this point,
+                    so the register is only optional if there is another
+                    operand to come.  */
+                 gas_assert (arg.opnum == 2);
+                 optional_reg = (args[2] == ',');
                  break;
 
-               if ((reglist & 0x3f00ffff) != 0)
+               case 'D':
+               case 'E':
+                 if (!forced_insn_length)
+                   *offset_reloc = (int) BFD_RELOC_UNUSED + c;
+                 else if (c == 'D')
+                   *offset_reloc = BFD_RELOC_MICROMIPS_10_PCREL_S1;
+                 else
+                   *offset_reloc = BFD_RELOC_MICROMIPS_7_PCREL_S1;
                  break;
+               }
+             break;
+           }
 
-               ra = (reglist >> 27) & 0x10;
-               reglist = ((reglist >> 22) & 0x100) | ((reglist >> 16) & 0xff);
-               reglist += 1;
-               if ((reglist & -reglist) != reglist)
-                 break;
+         operand = (mips_opts.micromips
+                    ? decode_micromips_operand (args)
+                    : decode_mips_operand (args));
+         if (!operand)
+           abort ();
 
-               imm = (ffs (reglist) - 1) | ra;
-               INSERT_OPERAND (1, RT, *ip, imm);
-               imm_expr.X_op = O_absent;
-             }
-             continue;
+         if (optional_reg
+             && (arg.token[0].type != OT_REG
+                 || arg.token[1].type == OT_END))
+           {
+             /* Assume that the register has been elided and is the
+                same as the first operand.  */
+             arg.token = tokens;
+             arg.argnum = 1;
+           }
 
-           case '|':           /* 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;
+         if (!match_operand (&arg, operand))
+           break;
 
-           default:
-             as_bad (_("Bad char = '%c'\n"), *args);
-             abort ();
-           }
-         break;
+         /* Skip prefixes.  */
+         if (*args == '+' || *args == 'm')
+           args++;
+
+         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;
@@ -13916,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)
@@ -13935,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;
 
@@ -13986,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;
@@ -14013,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;
-                   }
+             offsetT value;
 
-                 return;
-               }
-             break;
-
-           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;
     }
 }
@@ -15357,6 +13525,18 @@ md_parse_option (int c, char *arg)
       mips_pic = VXWORKS_PIC;
       break;
 
+    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;
     }
@@ -17247,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
@@ -18846,6 +17050,9 @@ 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))
@@ -19683,8 +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"
-                    ));
+--[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\
This page took 0.237223 seconds and 4 git commands to generate.