gas/
[external/binutils.git] / gas / config / tc-mips.c
index 595ab74..4e878c8 100644 (file)
@@ -272,6 +272,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,
@@ -779,15 +782,6 @@ static const unsigned int mips16_to_32_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[] =
 {
@@ -819,13 +813,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
 };
@@ -1321,6 +1313,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);
@@ -1452,6 +1445,7 @@ enum options
     OPTION_PDR,
     OPTION_NO_PDR,
     OPTION_MVXWORKS_PIC,
+    OPTION_NAN,
     OPTION_END_OF_ENUM
   };
 
@@ -1567,6 +1561,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 +1686,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 +1810,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};
 
@@ -2591,6 +2588,19 @@ reglist_lookup (char **s, unsigned int types, unsigned int *reglistp)
   return ok && reglist != 0;
 }
 
+static unsigned int
+mips_lookup_reg_pair (unsigned int regno1, unsigned int regno2,
+                     const unsigned int *map1, const unsigned int *map2,
+                     unsigned int count)
+{
+  unsigned int i;
+
+  for (i = 0; i < count; i++)
+    if (map1[i] == regno1 && map2[i] == regno2)
+      return i;
+  return ILLEGAL_REG;
+}
+
 /* Return TRUE if opcode MO is valid on the currently selected ISA, ASE
    and architecture.  Use is_opcode_valid_16 for MIPS16 opcodes.  */
 
@@ -2961,9 +2971,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 +3000,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 +3099,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.  */
 
@@ -3503,10 +3515,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);
@@ -5263,8 +5275,15 @@ macro_read_relocs (va_list *args, bfd_reloc_code_real_type *r)
   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);
+    {
+      for (i = 0; i < 3; i++)
+       r[i] = (bfd_reloc_code_real_type) va_arg (*args, int);
+      /* This function is only used for 16-bit relocation fields.
+        To make the macro code simpler, treat an unrelocated value
+        in the same way as BFD_RELOC_LO16.  */
+      if (r[0] == BFD_RELOC_UNUSED)
+       r[0] = BFD_RELOC_LO16;
+    }
 }
 
 /* Build an instruction created by a macro expansion.  This is passed
@@ -5540,12 +5559,12 @@ macro_build (expressionS *ep, const char *name, const char *fmt, ...)
 
        case 'C':
          gas_assert (!mips_opts.micromips);
-         INSERT_OPERAND (0, COPZ, insn, va_arg (args, unsigned long));
+         INSERT_OPERAND (0, COPZ, insn, va_arg (args, int));
          continue;
 
        case 'k':
          INSERT_OPERAND (mips_opts.micromips,
-                         CACHE, insn, va_arg (args, unsigned long));
+                         CACHE, insn, va_arg (args, int));
          continue;
 
        case '|':
@@ -5560,12 +5579,12 @@ macro_build (expressionS *ep, const char *name, const char *fmt, ...)
 
        case '\\':
          INSERT_OPERAND (mips_opts.micromips,
-                         3BITPOS, insn, va_arg (args, unsigned int));
+                         3BITPOS, insn, va_arg (args, int));
          continue;
 
        case '~':
          INSERT_OPERAND (mips_opts.micromips,
-                         OFFSET12, insn, va_arg (args, unsigned long));
+                         OFFSET12, insn, va_arg (args, int));
          continue;
 
        case 'N':
@@ -5669,16 +5688,6 @@ mips16_macro_build (expressionS *ep, const char *name, const char *fmt,
          MIPS16_INSERT_OPERAND (REGR32, insn, va_arg (*args, int));
          continue;
 
-       case 'Y':
-         {
-           int regno;
-
-           regno = va_arg (*args, int);
-           regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3);
-           MIPS16_INSERT_OPERAND (REG32R, insn, regno);
-         }
-         continue;
-
        case '<':
        case '>':
        case '4':
@@ -6732,6 +6741,48 @@ macro_build_branch_rsrt (int type, expressionS *ep,
     macro_build (ep, br, "s,t,p", sreg, treg);
 }
 
+/* Return the high part that should be loaded in order to make the low
+   part of VALUE accessible using an offset of OFFBITS bits.  */
+
+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;
+}
+
+/* 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.  */
+
+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;
+
+      /* 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;
+}
+
 /*
  *                     Build macros
  *   This routine implements the seemingly endless macro or synthesized
@@ -6772,10 +6823,10 @@ macro (struct mips_cl_insn *ip, char *str)
   int imm = 0;
   int ust = 0;
   int lp = 0;
-  int ab = 0;
+  bfd_boolean large_offset;
   int off;
-  bfd_reloc_code_real_type r;
   int hold_mips_optimize;
+  unsigned int align;
 
   gas_assert (! mips_opts.mips16);
 
@@ -6793,6 +6844,7 @@ macro (struct mips_cl_insn *ip, char *str)
   expr1.X_op_symbol = NULL;
   expr1.X_add_symbol = NULL;
   expr1.X_add_number = 1;
+  align = 1;
 
   switch (mask)
     {
@@ -7538,12 +7590,10 @@ macro (struct mips_cl_insn *ip, char *str)
       if (!dbl && HAVE_64BIT_OBJECTS)
        as_warn (_("la used to load 64-bit address"));
 
-      if (offset_expr.X_op == O_constant
-         && offset_expr.X_add_number >= -0x8000
-         && offset_expr.X_add_number < 0x8000)
+      if (small_offset_p (0, align, 16))
        {
-         macro_build (&offset_expr, ADDRESS_ADDI_INSN,
-                      "t,r,j", treg, sreg, BFD_RELOC_LO16);
+         macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", treg, breg,
+                      -1, offset_reloc[0], offset_reloc[1], offset_reloc[2]);
          break;
        }
 
@@ -8120,26 +8170,17 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_MSGSND:
       gas_assert (!mips_opts.micromips);
-      {
-       unsigned long temp = (treg << 16) | (0x01);
-       macro_build (NULL, "c2", "C", temp);
-      }
+      macro_build (NULL, "c2", "C", (treg << 16) | 0x01);
       break;
 
     case M_MSGLD:
       gas_assert (!mips_opts.micromips);
-      {
-       unsigned long temp = (0x02);
-       macro_build (NULL, "c2", "C", temp);
-      }
+      macro_build (NULL, "c2", "C", 0x02);
       break;
 
     case M_MSGLD_T:
       gas_assert (!mips_opts.micromips);
-      {
-       unsigned long temp = (treg << 16) | (0x02);
-       macro_build (NULL, "c2", "C", temp);
-      }
+      macro_build (NULL, "c2", "C", (treg << 16) | 0x02);
       break;
 
     case M_MSGWAIT:
@@ -8149,10 +8190,7 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_MSGWAIT_T:
       gas_assert (!mips_opts.micromips);
-      {
-       unsigned long temp = (treg << 16) | 0x03;
-       macro_build (NULL, "c2", "C", temp);
-      }
+      macro_build (NULL, "c2", "C", (treg << 16) | 0x03);
       break;
 
     case M_J_A:
@@ -8395,146 +8433,108 @@ macro (struct mips_cl_insn *ip, char *str)
       break;
 
     case M_LBUE_AB:
-      ab = 1;
-    case M_LBUE_OB:
       s = "lbue";
       fmt = "t,+j(b)";
       offbits = 9;
       goto ld_st;
     case M_LHUE_AB:
-      ab = 1;
-    case M_LHUE_OB:
       s = "lhue";
       fmt = "t,+j(b)";
       offbits = 9;
       goto ld_st;
     case M_LBE_AB:
-      ab = 1;
-    case M_LBE_OB:
       s = "lbe";
       fmt = "t,+j(b)";
       offbits = 9;
       goto ld_st;
     case M_LHE_AB:
-      ab = 1;
-    case M_LHE_OB:
       s = "lhe";
       fmt = "t,+j(b)";
       offbits = 9;
       goto ld_st;
     case M_LLE_AB:
-      ab = 1;
-    case M_LLE_OB:
       s = "lle";
       fmt = "t,+j(b)";
       offbits = 9;
       goto ld_st;
     case M_LWE_AB:
-      ab = 1;
-    case M_LWE_OB:
       s = "lwe";
       fmt = "t,+j(b)";
       offbits = 9;
       goto ld_st;
     case M_LWLE_AB:
-      ab = 1;
-    case M_LWLE_OB:
       s = "lwle";
       fmt = "t,+j(b)";
       offbits = 9;
       goto ld_st;
     case M_LWRE_AB:
-      ab = 1;
-    case M_LWRE_OB:
       s = "lwre";
       fmt = "t,+j(b)";
       offbits = 9;
       goto ld_st;
     case M_SBE_AB:
-      ab = 1;
-    case M_SBE_OB:
       s = "sbe";
       fmt = "t,+j(b)";
       offbits = 9;
       goto ld_st;
     case M_SCE_AB:
-      ab = 1;
-    case M_SCE_OB:
       s = "sce";
       fmt = "t,+j(b)";
       offbits = 9;
       goto ld_st;
     case M_SHE_AB:
-      ab = 1;
-    case M_SHE_OB:
       s = "she";
       fmt = "t,+j(b)";
       offbits = 9;
       goto ld_st;
     case M_SWE_AB:
-      ab = 1;
-    case M_SWE_OB:
       s = "swe";
       fmt = "t,+j(b)";
       offbits = 9;
       goto ld_st;
     case M_SWLE_AB:
-      ab = 1;
-    case M_SWLE_OB:
       s = "swle";
       fmt = "t,+j(b)";
       offbits = 9;
       goto ld_st;
     case M_SWRE_AB:
-      ab = 1;
-    case M_SWRE_OB:
       s = "swre";
       fmt = "t,+j(b)";
       offbits = 9;
       goto ld_st;
     case M_ACLR_AB:
-      ab = 1;
-    case M_ACLR_OB:
       s = "aclr";
       treg = EXTRACT_OPERAND (mips_opts.micromips, 3BITPOS, *ip);
       fmt = "\\,~(b)";
       offbits = 12;
       goto ld_st;
     case M_ASET_AB:
-      ab = 1;
-    case M_ASET_OB:
       s = "aset";
       treg = EXTRACT_OPERAND (mips_opts.micromips, 3BITPOS, *ip);
       fmt = "\\,~(b)";
       offbits = 12;
       goto ld_st;
     case M_LB_AB:
-      ab = 1;
       s = "lb";
       fmt = "t,o(b)";
       goto ld;
     case M_LBU_AB:
-      ab = 1;
       s = "lbu";
       fmt = "t,o(b)";
       goto ld;
     case M_LH_AB:
-      ab = 1;
       s = "lh";
       fmt = "t,o(b)";
       goto ld;
     case M_LHU_AB:
-      ab = 1;
       s = "lhu";
       fmt = "t,o(b)";
       goto ld;
     case M_LW_AB:
-      ab = 1;
       s = "lw";
       fmt = "t,o(b)";
       goto ld;
     case M_LWC0_AB:
-      ab = 1;
       gas_assert (!mips_opts.micromips);
       s = "lwc0";
       fmt = "E,o(b)";
@@ -8542,15 +8542,12 @@ macro (struct mips_cl_insn *ip, char *str)
       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);
@@ -8558,7 +8555,6 @@ macro (struct mips_cl_insn *ip, char *str)
       coproc = 1;
       goto ld_st;
     case M_LWC3_AB:
-      ab = 1;
       gas_assert (!mips_opts.micromips);
       s = "lwc3";
       fmt = "E,o(b)";
@@ -8566,29 +8562,22 @@ macro (struct mips_cl_insn *ip, char *str)
       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);
@@ -8596,57 +8585,43 @@ macro (struct mips_cl_insn *ip, char *str)
       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)";
@@ -8654,8 +8629,6 @@ macro (struct mips_cl_insn *ip, char *str)
       lp = 1;
       goto ld;
     case M_LDP_AB:
-      ab = 1;
-    case M_LDP_OB:
       gas_assert (mips_opts.micromips);
       s = "ldp";
       fmt = "t,~(b)";
@@ -8663,16 +8636,12 @@ macro (struct mips_cl_insn *ip, char *str)
       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)";
@@ -8688,22 +8657,18 @@ macro (struct mips_cl_insn *ip, char *str)
       goto ld_noat;
 
     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)";
@@ -8711,15 +8676,12 @@ macro (struct mips_cl_insn *ip, char *str)
       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);
@@ -8727,7 +8689,6 @@ macro (struct mips_cl_insn *ip, char *str)
       coproc = 1;
       goto ld_st;
     case M_SWC3_AB:
-      ab = 1;
       gas_assert (!mips_opts.micromips);
       s = "swc3";
       fmt = "E,o(b)";
@@ -8735,71 +8696,52 @@ macro (struct mips_cl_insn *ip, char *str)
       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);
@@ -8807,14 +8749,12 @@ macro (struct mips_cl_insn *ip, char *str)
       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)";
@@ -8822,46 +8762,34 @@ macro (struct mips_cl_insn *ip, char *str)
       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)";
@@ -8869,8 +8797,41 @@ macro (struct mips_cl_insn *ip, char *str)
 
     ld_st:
       tempreg = AT;
-      used_at = 1;
     ld_noat:
+      if (small_offset_p (0, align, 16))
+       {
+         /* The first case exists for M_LD_AB and M_SD_AB, which are
+            macros for o32 but which should act like normal instructions
+            otherwise.  */
+         if (offbits == 16)
+           macro_build (&offset_expr, s, fmt, treg, -1, offset_reloc[0],
+                        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;
+       }
+
+      if (tempreg == AT)
+       used_at = 1;
+
       if (offset_expr.X_op != O_constant
          && offset_expr.X_op != O_symbol)
        {
@@ -8891,79 +8852,40 @@ macro (struct mips_cl_insn *ip, char *str)
         is in non PIC code.  */
       if (offset_expr.X_op == O_constant)
        {
-         int hipart = 0;
+         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;
 
-         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;
-           }
+         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)
-               tempreg = breg;
-             else
+             if (offset_expr.X_add_number != 0)
                macro_build (&offset_expr, ADDRESS_ADDI_INSN,
-                            "t,r,j", tempreg, breg, BFD_RELOC_LO16);
+                            "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, breg);
+           macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16, tempreg);
          else
-           macro_build (NULL, s, fmt,
-                        treg, (unsigned long) offset_expr.X_add_number, breg);
+           macro_build (NULL, s, fmt, treg,
+                        (int) offset_expr.X_add_number, tempreg);
        }
       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);
-           }
-         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;
+            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, (unsigned long) expr1.X_add_number, tempreg);
+           macro_build (NULL, s, fmt, treg, 0, tempreg);
        }
       else if (mips_pic == NO_PIC)
        {
@@ -9421,16 +9343,11 @@ macro (struct mips_cl_insn *ip, char *str)
                  && offset_expr.X_add_number == 0);
       s = segment_name (S_GET_SEGMENT (offset_expr.X_add_symbol));
       if (strcmp (s, ".lit8") == 0)
-       {
-         if (CPU_HAS_LDC1_SDC1 (mips_opts.arch) || mips_opts.micromips)
-           {
-             macro_build (&offset_expr, "ldc1", "T,o(b)", treg,
-                          BFD_RELOC_MIPS_LITERAL, mips_gp_register);
-             break;
-           }
-         breg = mips_gp_register;
-         r = BFD_RELOC_MIPS_LITERAL;
-         goto dob;
+       {
+         breg = mips_gp_register;
+         offset_reloc[0] = BFD_RELOC_MIPS_LITERAL;
+         offset_reloc[1] = BFD_RELOC_UNUSED;
+         offset_reloc[2] = BFD_RELOC_UNUSED;
        }
       else
        {
@@ -9445,47 +9362,15 @@ macro (struct mips_cl_insn *ip, char *str)
              macro_build_lui (&offset_expr, AT);
            }
 
-         if (CPU_HAS_LDC1_SDC1 (mips_opts.arch) || mips_opts.micromips)
-           {
-             macro_build (&offset_expr, "ldc1", "T,o(b)",
-                          treg, BFD_RELOC_LO16, AT);
-             break;
-           }
          breg = AT;
-         r = BFD_RELOC_LO16;
-         goto dob;
-       }
-
-    case M_L_DOB:
-      /* Even on a big endian machine $fn comes before $fn+1.  We have
-        to adjust when loading from memory.  */
-      r = BFD_RELOC_LO16;
-    dob:
-      gas_assert (!mips_opts.micromips);
-      gas_assert (!CPU_HAS_LDC1_SDC1 (mips_opts.arch));
-      macro_build (&offset_expr, "lwc1", "T,o(b)",
-                  target_big_endian ? treg + 1 : treg, r, breg);
-      /* FIXME: A possible overflow which I don't know how to deal
-        with.  */
-      offset_expr.X_add_number += 4;
-      macro_build (&offset_expr, "lwc1", "T,o(b)",
-                  target_big_endian ? treg : treg + 1, r, breg);
-      break;
-
-    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;
+         offset_reloc[0] = BFD_RELOC_LO16;
+         offset_reloc[1] = BFD_RELOC_UNUSED;
+         offset_reloc[2] = BFD_RELOC_UNUSED;
+       }
+      align = 8;
+      /* Fall through */
 
     case M_L_DAB:
-      gas_assert (!mips_opts.micromips);
       /*
        * The MIPS assembler seems to check for X_add_number not
        * being double aligned and generating:
@@ -9551,6 +9436,51 @@ macro (struct mips_cl_insn *ip, char *str)
       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 (small_offset_p (0, align, 16))
+       {
+         ep = &offset_expr;
+         if (!small_offset_p (4, align, 16))
+           {
+             macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", AT, breg,
+                          -1, offset_reloc[0], offset_reloc[1],
+                          offset_reloc[2]);
+             expr1.X_add_number = 0;
+             ep = &expr1;
+             breg = AT;
+             used_at = 1;
+             offset_reloc[0] = BFD_RELOC_LO16;
+             offset_reloc[1] = BFD_RELOC_UNUSED;
+             offset_reloc[2] = BFD_RELOC_UNUSED;
+           }
+         if (strcmp (s, "lw") == 0 && treg == breg)
+           {
+             ep->X_add_number += 4;
+             macro_build (ep, s, fmt, treg + 1, -1, offset_reloc[0],
+                          offset_reloc[1], offset_reloc[2], breg);
+             ep->X_add_number -= 4;
+             macro_build (ep, s, fmt, treg, -1, offset_reloc[0],
+                          offset_reloc[1], offset_reloc[2], breg);
+           }
+         else
+           {
+             macro_build (ep, s, fmt, coproc ? treg + 1 : treg, -1,
+                          offset_reloc[0], offset_reloc[1], offset_reloc[2],
+                          breg);
+             ep->X_add_number += 4;
+             macro_build (ep, s, fmt, coproc ? treg : treg + 1, -1,
+                          offset_reloc[0], offset_reloc[1], offset_reloc[2],
+                          breg);
+           }
+         break;
+       }
+
       if (offset_expr.X_op != O_symbol
          && offset_expr.X_op != O_constant)
        {
@@ -9567,13 +9497,6 @@ macro (struct mips_cl_insn *ip, char *str)
          as_bad (_("Number (0x%s) larger than 32 bits"), value);
        }
 
-      /* Even on a big endian machine $fn comes before $fn+1.  We have
-        to adjust when loading from memory.  We set coproc if we must
-        load $fn+1 first.  */
-      /* Itbl support may require additional care here.  */
-      if (!target_big_endian)
-       coproc = 0;
-
       if (mips_pic == NO_PIC || offset_expr.X_op == O_constant)
        {
          /* If this is a reference to a GP relative symbol, we want
@@ -9626,7 +9549,15 @@ macro (struct mips_cl_insn *ip, char *str)
              offset_expr.X_add_number -= 4;
            }
          used_at = 1;
-         macro_build_lui (&offset_expr, AT);
+         if (offset_high_part (offset_expr.X_add_number, 16)
+             != offset_high_part (offset_expr.X_add_number + 4, 16))
+           {
+             load_address (AT, &offset_expr, &used_at);
+             offset_expr.X_op = O_constant;
+             offset_expr.X_add_number = 0;
+           }
+         else
+           macro_build_lui (&offset_expr, AT);
          if (breg != 0)
            macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
          /* Itbl support may require additional care here.  */
@@ -9766,36 +9697,13 @@ macro (struct mips_cl_insn *ip, char *str)
        abort ();
 
       break;
-
-    case M_LD_OB:
-      s = HAVE_64BIT_GPRS ? "ld" : "lw";
-      goto sd_ob;
-    case M_SD_OB:
-      s = HAVE_64BIT_GPRS ? "sd" : "sw";
-    sd_ob:
-      macro_build (&offset_expr, s, "t,o(b)", treg,
-                  -1, offset_reloc[0], offset_reloc[1], offset_reloc[2],
-                  breg);
-      if (!HAVE_64BIT_GPRS)
-       {
-         offset_expr.X_add_number += 4;
-         macro_build (&offset_expr, s, "t,o(b)", treg + 1,
-                      -1, offset_reloc[0], offset_reloc[1], offset_reloc[2],
-                      breg);
-       }
-      break;
-
        
     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)";
@@ -9833,7 +9741,7 @@ macro (struct mips_cl_insn *ip, char *str)
       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);
+      macro_build (NULL, s, "C", (int) ip->insn_opcode);
       break;
 
     case M_MOVE:
@@ -9843,8 +9751,8 @@ macro (struct mips_cl_insn *ip, char *str)
     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)];
+      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);
@@ -10451,56 +10359,42 @@ macro (struct mips_cl_insn *ip, char *str)
       end_noreorder ();
       break;
 
-    case M_ULH_A:
-      ab = 1;
-    case M_ULH:
+    case M_ULH_AB:
       s = "lb";
       s2 = "lbu";
       off = 1;
       goto uld_st;
-    case M_ULHU_A:
-      ab = 1;
-    case M_ULHU:
+    case M_ULHU_AB:
       s = "lbu";
       s2 = "lbu";
       off = 1;
       goto uld_st;
-    case M_ULW_A:
-      ab = 1;
-    case M_ULW:
+    case M_ULW_AB:
       s = "lwl";
       s2 = "lwr";
       offbits = (mips_opts.micromips ? 12 : 16);
       off = 3;
       goto uld_st;
-    case M_ULD_A:
-      ab = 1;
-    case M_ULD:
+    case M_ULD_AB:
       s = "ldl";
       s2 = "ldr";
       offbits = (mips_opts.micromips ? 12 : 16);
       off = 7;
       goto uld_st;
-    case M_USH_A:
-      ab = 1;
-    case M_USH:
+    case M_USH_AB:
       s = "sb";
       s2 = "sb";
       off = 1;
       ust = 1;
       goto uld_st;
-    case M_USW_A:
-      ab = 1;
-    case M_USW:
+    case M_USW_AB:
       s = "swl";
       s2 = "swr";
       offbits = (mips_opts.micromips ? 12 : 16);
       off = 3;
       ust = 1;
       goto uld_st;
-    case M_USD_A:
-      ab = 1;
-    case M_USD:
+    case M_USD_AB:
       s = "sdl";
       s2 = "sdr";
       offbits = (mips_opts.micromips ? 12 : 16);
@@ -10508,32 +10402,26 @@ macro (struct mips_cl_insn *ip, char *str)
       ust = 1;
 
     uld_st:
-      if (!ab && offset_expr.X_add_number >= 0x8000 - off)
-       as_bad (_("Operand overflow"));
-
+      large_offset = !small_offset_p (off, align, offbits);
       ep = &offset_expr;
       expr1.X_add_number = 0;
-      if (ab)
+      if (large_offset)
        {
          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]);
+         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;
@@ -10551,21 +10439,22 @@ macro (struct mips_cl_insn *ip, char *str)
 
       if (!target_big_endian)
        ep->X_add_number += off;
-      if (offbits != 12)
-       macro_build (ep, s, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
+      if (offbits == 12)
+       macro_build (NULL, s, "t,~(b)", tempreg, (int) ep->X_add_number, breg);
       else
-       macro_build (NULL, s, "t,~(b)",
-                    tempreg, (unsigned long) ep->X_add_number, breg);
+       macro_build (ep, s, "t,o(b)", tempreg, -1,
+                    offset_reloc[0], offset_reloc[1], offset_reloc[2], 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
+      if (offbits == 12)
        macro_build (NULL, s2, "t,~(b)",
-                    tempreg, (unsigned long) ep->X_add_number, breg);
+                    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);
 
       /* If necessary, move the result in tempreg to the final destination.  */
       if (!ust && treg != tempreg)
@@ -10580,14 +10469,15 @@ macro (struct mips_cl_insn *ip, char *str)
       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);
+      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);
 
       /* 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;
+      tempreg = ust == large_offset ? treg : AT;
       if (ust)
        macro_build (NULL, "srl", SHFT_FMT, tempreg, treg, 8);
 
@@ -10595,21 +10485,23 @@ macro (struct mips_cl_insn *ip, char *str)
        ep->X_add_number -= off;
       else
        ep->X_add_number += off;
-      macro_build (ep, s2, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
+      macro_build (ep, s2, "t,o(b)", tempreg, -1,
+                  offset_reloc[0], offset_reloc[1], offset_reloc[2], breg);
 
       /* For M_USH_A re-retrieve the LSB.  */
-      if (ust && ab)
+      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, BFD_RELOC_LO16, AT);
+         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 || ab)
+      if (!ust || large_offset)
        {
-         tempreg = !ab ? AT : treg;
+         tempreg = !large_offset ? AT : treg;
          macro_build (NULL, "sll", SHFT_FMT, tempreg, tempreg, 8);
          macro_build (NULL, "or", "d,v,t", treg, treg, AT);
        }
@@ -10889,8 +10781,6 @@ validate_mips_insn (const struct mips_opcode *opc)
          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;
@@ -10898,8 +10788,6 @@ validate_mips_insn (const struct mips_opcode *opc)
          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;
@@ -10912,6 +10800,7 @@ validate_mips_insn (const struct mips_opcode *opc)
          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 'i': USE_BITS (OP_MASK_TARGET,   OP_SH_TARGET);  break;
          case 'j': USE_BITS (OP_MASK_EVAOFFSET, OP_SH_EVAOFFSET); break;
 
          default:
@@ -10973,9 +10862,7 @@ validate_mips_insn (const struct mips_opcode *opc)
                USE_BITS (OP_MASK_RT,           OP_SH_RT);      break;
       case 'e': USE_BITS (OP_MASK_VECBYTE,     OP_SH_VECBYTE); break;
       case '%': USE_BITS (OP_MASK_VECALIGN,    OP_SH_VECALIGN); break;
-      case '[': break;
-      case ']': break;
-      case '1':        USE_BITS (OP_MASK_SHAMT,        OP_SH_SHAMT);   break;
+      case '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;
@@ -11069,11 +10956,11 @@ validate_micromips_insn (const struct mips_opcode *opc)
          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 'i': USE_BITS (TARGET);  break;
          case 'j': USE_BITS (EVAOFFSET);       break;
          default:
            as_bad (_("Internal error: bad mips opcode "
@@ -11115,7 +11002,6 @@ validate_micromips_insn (const struct mips_opcode *opc)
          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;
@@ -11274,10 +11160,12 @@ expr_const_in_range (expressionS *ep, offsetT min, offsetT max, int bit)
          && ep->X_add_number < max << bit);
 }
 
-/* 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.  */
+/* 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.  */
 
 static void
 mips_ip (char *str, struct mips_cl_insn *ip)
@@ -11292,7 +11180,7 @@ mips_ip (char *str, struct mips_cl_insn *ip)
   char c = 0;
   struct mips_opcode *insn;
   char *argsStart;
-  unsigned int regno;
+  unsigned int regno, regno2;
   unsigned int lastregno;
   unsigned int destregno = 0;
   unsigned int lastpos = 0;
@@ -11420,6 +11308,13 @@ mips_ip (char *str, struct mips_cl_insn *ip)
          return;
        }
 
+      imm_expr.X_op = O_absent;
+      imm2_expr.X_op = O_absent;
+      offset_expr.X_op = O_absent;
+      offset_reloc[0] = BFD_RELOC_UNUSED;
+      offset_reloc[1] = BFD_RELOC_UNUSED;
+      offset_reloc[2] = BFD_RELOC_UNUSED;
+
       create_insn (ip, insn);
       insn_error = NULL;
       argnum = 1;
@@ -11787,13 +11682,6 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                continue;
              break;
 
-           case '[':           /* These must match exactly.  */
-           case ']':
-             gas_assert (!mips_opts.micromips);
-             if (*s++ == *args)
-               continue;
-             break;
-
            case '+':           /* Opcode extension character.  */
              switch (*++args)
                {
@@ -11938,10 +11826,6 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                  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);
@@ -11953,11 +11837,6 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                  s = expr_end;
                  continue;
 
-               case 'T': /* Coprocessor register.  */
-                 gas_assert (!mips_opts.micromips);
-                 /* +T is for disassembly only; never match.  */
-                 break;
-
                case 't': /* Coprocessor register number.  */
                  gas_assert (!mips_opts.micromips);
                  if (s[0] == '$' && ISDIGIT (s[1]))
@@ -12027,8 +11906,7 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                              (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;
+                 lastpos = imm_expr.X_add_number;
                  INSERT_OPERAND (0, CINSPOS, *ip, imm_expr.X_add_number);
                  imm_expr.X_op = O_absent;
                  s = expr_end;
@@ -12050,11 +11928,12 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                  continue;
 
                case 's':
-                 /* cins and exts length-minus-one field.  */
+                 /* cins32 and exts32 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)
+                 if ((unsigned long) imm_expr.X_add_number > 31
+                     || (unsigned long) imm_expr.X_add_number + lastpos > 31)
                    {
                      as_bad (_("Improper size (%lu)"),
                              (unsigned long) imm_expr.X_add_number);
@@ -12066,12 +11945,11 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                  continue;
 
                case 'S':
-                 /* cins32/exts32 and cins/exts aliasing cint32/exts32
-                    length-minus-one field.  */
+                 /* cins/exts 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
+                 if ((unsigned long) imm_expr.X_add_number > 31
                      || (unsigned long) imm_expr.X_add_number + lastpos > 63)
                    {
                      as_bad (_("Improper size (%lu)"),
@@ -12186,10 +12064,15 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                  INSERT_OPERAND (0, FZ, *ip, regno);
                  continue;
 
+               case 'i':
+                 goto jump;
+
                case 'j':
                  {
                    int shift = 8;
                    size_t i;
+                   bfd_reloc_code_real_type r[3];
+
                    /* Check whether there is only a single bracketed expression
                       left.  If so, it must be the base register and the
                       constant must be zero.  */
@@ -12199,7 +12082,7 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                    /* 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);
+                   i = my_getSmallExpression (&imm_expr, r, s);
                    if ((i == 0 && (imm_expr.X_op != O_constant
                                    || imm_expr.X_add_number >= 1 << shift
                                    || imm_expr.X_add_number < -1 << shift))
@@ -12231,6 +12114,7 @@ mips_ip (char *str, struct mips_cl_insn *ip)
              {
                int shift = *args == '.' ? 9 : 11;
                size_t i;
+               bfd_reloc_code_real_type r[3];
 
                /* Check whether there is only a single bracketed expression
                   left.  If so, it must be the base register and the
@@ -12241,7 +12125,7 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                /* 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);
+               i = my_getSmallExpression (&imm_expr, r, s);
                if ((i == 0 && (imm_expr.X_op != O_constant
                                || imm_expr.X_add_number >= 1 << shift
                                || imm_expr.X_add_number < -1 << shift))
@@ -12665,7 +12549,7 @@ mips_ip (char *str, struct mips_cl_insn *ip)
            case 'X':           /* MDMX destination register.  */
            case 'Y':           /* MDMX source register.  */
            case 'Z':           /* MDMX target register.  */
-             is_mdmx = 1;
+             is_mdmx = !(insn->membership & INSN_5400);
            case 'W':
              gas_assert (!mips_opts.micromips);
            case 'D':           /* Floating point destination register.  */
@@ -12721,6 +12605,11 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                      /* 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 ((ip->insn_mo->membership & INSN_5400)
+                         && strcmp (insn->name, "rzu.ob") == 0)
+                       as_bad (_("Operand %d of `%s' must be an immediate"),
+                               argnum, ip->insn_mo->name);
+
                      if (*s == '[')
                        {
                          int is_qh = (ip->insn_opcode & (1 << OP_SH_VSEL));
@@ -12743,7 +12632,13 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                            s++;
                        }
                      else
-                        {
+                       {
+                         if ((ip->insn_mo->membership & INSN_5400)
+                             && (strcmp (insn->name, "sll.ob") == 0
+                                 || strcmp (insn->name, "srl.ob") == 0))
+                           as_bad (_("Operand %d of `%s' must be scalar"),
+                                   argnum, ip->insn_mo->name);
+
                           if (ip->insn_opcode & (OP_MASK_VSEL << OP_SH_VSEL))
                             ip->insn_opcode |= (MDMX_FMTSEL_VEC_QH
                                                << OP_SH_VSEL);
@@ -12789,10 +12684,20 @@ mips_ip (char *str, struct mips_cl_insn *ip)
              continue;
 
            case 'A':
-             my_getExpression (&offset_expr, s);
-             normalize_address_expr (&offset_expr);
-             *imm_reloc = BFD_RELOC_32;
-             s = expr_end;
+             my_getSmallExpression (&offset_expr, offset_reloc, s);
+             if (offset_expr.X_op == O_register)
+               {
+                 /* 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
+               {
+                 normalize_address_expr (&offset_expr);
+                 s = expr_end;
+               }
              continue;
 
            case 'F':
@@ -12966,8 +12871,8 @@ mips_ip (char *str, struct mips_cl_insn *ip)
 
            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)
+             *offset_reloc = BFD_RELOC_LO16;
+             if (my_getSmallExpression (&offset_expr, offset_reloc, s) == 0)
                {
                  int more;
                  offsetT minval, maxval;
@@ -12985,14 +12890,14 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                  else
                    minval = -0x8000, maxval = 0xffff;
 
-                 if (imm_expr.X_op != O_constant
-                     || imm_expr.X_add_number < minval
-                     || imm_expr.X_add_number > maxval)
+                 if (offset_expr.X_op != O_constant
+                     || offset_expr.X_add_number < minval
+                     || offset_expr.X_add_number > maxval)
                    {
                      if (more)
                        break;
-                     if (imm_expr.X_op == O_constant
-                         || imm_expr.X_op == O_big)
+                     if (offset_expr.X_op == O_constant
+                         || offset_expr.X_op == O_big)
                        as_bad (_("Expression out of range"));
                    }
                }
@@ -13033,17 +12938,18 @@ mips_ip (char *str, struct mips_cl_insn *ip)
              continue;
 
            case 'u':           /* Upper 16 bits.  */
-             *imm_reloc = BFD_RELOC_LO16;
-             if (my_getSmallExpression (&imm_expr, imm_reloc, s) == 0
-                 && imm_expr.X_op == O_constant
-                 && (imm_expr.X_add_number < 0
-                     || imm_expr.X_add_number >= 0x10000))
+             *offset_reloc = BFD_RELOC_LO16;
+             if (my_getSmallExpression (&offset_expr, offset_reloc, s) == 0
+                 && offset_expr.X_op == O_constant
+                 && (offset_expr.X_add_number < 0
+                     || offset_expr.X_add_number >= 0x10000))
                as_bad (_("lui expression (%lu) not in range 0..65535"),
-                       (unsigned long) imm_expr.X_add_number);
+                       (unsigned long) offset_expr.X_add_number);
              s = expr_end;
              continue;
 
            case 'a':           /* 26-bit address.  */
+           jump:
              *offset_reloc = BFD_RELOC_MIPS_JMP;
              my_getExpression (&offset_expr, s);
              s = expr_end;
@@ -13155,7 +13061,6 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                case 'f':
                case 'g':
                case 'h':
-               case 'i':
                case 'j':
                case 'l':
                case 'm':
@@ -13278,47 +13183,32 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                        break;
 
                      case 'h':
-                       regno = mips32_to_micromips_reg_h_map[regno];
-                       break;
-
-                     case 'i':
-                       switch (EXTRACT_OPERAND (1, MI, *ip))
+                       s += strspn (s, " \t");
+                       if (*s != ',')
+                         {
+                           regno = ILLEGAL_REG;
+                           break;
+                         }
+                       ++s;
+                       s += strspn (s, " \t");
+                       ok = reg_lookup (&s, RTYPE_NUM | RTYPE_GP, &regno2);
+                       if (!ok)
                          {
-                           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;
+                           regno = ILLEGAL_REG;
+                           break;
                          }
+                       if (regno2 == 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\""),
+                                      regno2, mips_opts.at);
+                         }
+                       regno = (mips_lookup_reg_pair
+                                (regno, regno2,
+                                 micromips_to_32_reg_h_map1,
+                                 micromips_to_32_reg_h_map2, 8));
                        break;
 
                      case 'l':
@@ -13395,10 +13285,6 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                        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;
@@ -13922,12 +13808,9 @@ mips_ip (char *str, struct mips_cl_insn *ip)
 
 #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)
@@ -13990,6 +13873,7 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
   for (;;)
     {
       bfd_boolean ok;
+      char relax_char;
 
       gas_assert (strcmp (insn->name, str) == 0);
 
@@ -14019,14 +13903,12 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
 
       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;
       for (args = insn->args; 1; ++args)
        {
          int c;
@@ -14047,19 +13929,31 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
                  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))
+                 if (insn->pinfo == INSN_MACRO)
                    {
-                     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;
+                     gas_assert (relax_char == 0);
+                     gas_assert (*offset_reloc == BFD_RELOC_UNUSED);
+                   }
+                 else if (relax_char
+                          && offset_expr.X_op == O_constant
+                          && calculate_reloc (*offset_reloc,
+                                              offset_expr.X_add_number,
+                                              &value))
+                   {
+                     mips16_immed (NULL, 0, relax_char, *offset_reloc, value,
+                                   forced_insn_length, &ip->insn_opcode);
+                     offset_expr.X_op = O_absent;
                      *offset_reloc = BFD_RELOC_UNUSED;
                    }
+                 else if (relax_char && *offset_reloc != BFD_RELOC_UNUSED)
+                   {
+                     if (forced_insn_length == 2)
+                       as_bad (_("invalid unextended operand value"));
+                     forced_insn_length = 4;
+                     ip->insn_opcode |= MIPS16_EXTEND;
+                   }
+                 else if (relax_char)
+                   *offset_reloc = (int) BFD_RELOC_UNUSED + relax_char;
 
                  return;
                }
@@ -14230,24 +14124,14 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
            case 'U':
            case 'k':
            case 'K':
-             i = my_getSmallExpression (&imm_expr, imm_reloc, s);
+             i = my_getSmallExpression (&offset_expr, offset_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;
-                   }
+                 relax_char = c;
                  s = expr_end;
                  continue;
                }
-             *imm_reloc = BFD_RELOC_UNUSED;
+             *offset_reloc = BFD_RELOC_UNUSED;
              /* Fall through.  */
            case '<':
            case '>':
@@ -14255,8 +14139,8 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
            case ']':
            case '4':
            case '8':
-             my_getExpression (&imm_expr, s);
-             if (imm_expr.X_op == O_register)
+             my_getExpression (&offset_expr, s);
+             if (offset_expr.X_op == O_register)
                {
                  /* What we thought was an expression turned out to
                      be a register.  */
@@ -14266,11 +14150,11 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
                      /* 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
+                        still set up offset_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;
+                     offset_expr.X_op = O_constant;
+                     offset_expr.X_add_number = 0;
+                     relax_char = c;
                      continue;
                    }
 
@@ -14278,7 +14162,7 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
                }
 
              /* We need to relax this instruction.  */
-             *imm_reloc = (int) BFD_RELOC_UNUSED + c;
+             relax_char = c;
              s = expr_end;
              continue;
 
@@ -14296,7 +14180,7 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
                break;
 
              /* We need to relax this instruction.  */
-             *offset_reloc = (int) BFD_RELOC_UNUSED + c;
+             relax_char = c;
              s = expr_end;
              continue;
 
@@ -14312,7 +14196,18 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
              s = expr_end;
              continue;
 
+           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 'a':           /* 26 bit address */
+           case 'i':
              my_getExpression (&offset_expr, s);
              s = expr_end;
              *offset_reloc = BFD_RELOC_MIPS16_JMP;
@@ -15357,6 +15252,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 +17154,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 +18777,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 +19617,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\