gas/
[deliverable/binutils-gdb.git] / gas / config / tc-mips.c
index 05d4959ab0c9f887df0454ce50b4e871afc20339..5ba0faf4b7b0e911027681835dbe46b107a77795 100644 (file)
@@ -1,6 +1,6 @@
 /* tc-mips.c -- assemble code for a MIPS chip.
    Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
    Free Software Foundation, Inc.
    Contributed by the OSF and Ralph Campbell.
    Written by Keith Knowles and Ralph Campbell, working independently.
@@ -91,6 +91,8 @@ static char *mips_regmask_frag;
 
 #define ZERO 0
 #define ATREG 1
+#define S0  16
+#define S7  23
 #define TREG 24
 #define PIC_CALL_REG 25
 #define KT0 26
@@ -121,6 +123,21 @@ extern int target_big_endian;
                            ? ".rodata" \
                            : (abort (), ""))
 
+/* Ways in which an instruction can be "appended" to the output.  */
+enum append_method {
+  /* Just add it normally.  */
+  APPEND_ADD,
+
+  /* Add it normally and then add a nop.  */
+  APPEND_ADD_WITH_NOP,
+
+  /* Turn an instruction with a delay slot into a "compact" version.  */
+  APPEND_ADD_COMPACT,
+
+  /* Insert the instruction before the last one.  */
+  APPEND_SWAP
+};
+
 /* Information about an instruction, including its format, operands
    and fixups.  */
 struct mips_cl_insn
@@ -128,15 +145,10 @@ struct mips_cl_insn
   /* The opcode's entry in mips_opcodes or mips16_opcodes.  */
   const struct mips_opcode *insn_mo;
 
-  /* True if this is a mips16 instruction and if we want the extended
-     form of INSN_MO.  */
-  bfd_boolean use_extend;
-
-  /* The 16-bit extension instruction to use when USE_EXTEND is true.  */
-  unsigned short extend;
-
   /* The 16-bit or 32-bit bitstring of the instruction itself.  This is
-     a copy of INSN_MO->match with the operands filled in.  */
+     a copy of INSN_MO->match with the operands filled in.  If we have
+     decided to use an extended MIPS16 instruction, this includes the
+     extension.  */
   unsigned long insn_opcode;
 
   /* The frag that contains the instruction.  */
@@ -156,6 +168,13 @@ struct mips_cl_insn
 
   /* True for mips16 instructions that jump to an absolute address.  */
   unsigned int mips16_absolute_jump_p : 1;
+
+  /* True if this instruction is complete.  */
+  unsigned int complete_p : 1;
+
+  /* True if this instruction is cleared from history by unconditional
+     branch.  */
+  unsigned int cleared_p : 1;
 };
 
 /* The ABI to use.  */
@@ -197,12 +216,20 @@ struct mips_set_options
   int ase_smartmips;
   int ase_dsp;
   int ase_dspr2;
+  int ase_eva;
   int ase_mt;
+  int ase_mcu;
+  int ase_virt;
   /* Whether we are assembling for the mips16 processor.  0 if we are
      not, 1 if we are, and -1 if the value has not been initialized.
      Changed by `.set mips16' and `.set nomips16', and the -mips16 and
      -nomips16 command line options, and the default CPU.  */
   int mips16;
+  /* Whether we are assembling for the mipsMIPS ASE.  0 if we are not,
+     1 if we are, and -1 if the value has not been initialized.  Changed
+     by `.set micromips' and `.set nomicromips', and the -mmicromips
+     and -mno-micromips command line options, and the default CPU.  */
+  int micromips;
   /* Non-zero if we should not reorder instructions.  Changed by `.set
      reorder' and `.set noreorder'.  */
   int noreorder;
@@ -266,11 +293,13 @@ static int file_mips_single_float = 0;
 static struct mips_set_options mips_opts =
 {
   /* isa */ ISA_UNKNOWN, /* ase_mips3d */ -1, /* ase_mdmx */ -1,
-  /* ase_smartmips */ 0, /* ase_dsp */ -1, /* ase_dspr2 */ -1, /* ase_mt */ -1,
-  /* mips16 */ -1, /* noreorder */ 0, /* at */ ATREG,
-  /* warn_about_macros */ 0, /* nomove */ 0, /* nobopt */ 0,
-  /* noautoextend */ 0, /* gp32 */ 0, /* fp32 */ 0, /* arch */ CPU_UNKNOWN,
-  /* sym32 */ FALSE, /* soft_float */ FALSE, /* single_float */ FALSE
+  /* ase_smartmips */ 0, /* ase_dsp */ -1, /* ase_dspr2 */ -1,
+  /* ase_eva */ -1, /* ase_mt */ -1, /* ase_mcu */ -1,
+  /* ase_virt */ -1, /* mips16 */ -1, /* micromips */ -1,
+  /* noreorder */ 0,  /* at */ ATREG, /* warn_about_macros */ 0,
+  /* nomove */ 0, /* nobopt */ 0, /* noautoextend */ 0, /* gp32 */ 0,
+  /* fp32 */ 0, /* arch */ CPU_UNKNOWN, /* sym32 */ FALSE,
+  /* soft_float */ FALSE, /* single_float */ FALSE
 };
 
 /* These variables are filled in with the masks of registers used.
@@ -282,8 +311,7 @@ unsigned long mips_cprmask[4];
 /* MIPS ISA we are using for this output file.  */
 static int file_mips_isa = ISA_UNKNOWN;
 
-/* True if -mips16 was passed or implied by arguments passed on the
-   command line (e.g., by -march).  */
+/* True if any MIPS16 code was produced.  */
 static int file_ase_mips16;
 
 #define ISA_SUPPORTS_MIPS16E (mips_opts.isa == ISA_MIPS32              \
@@ -291,6 +319,9 @@ static int file_ase_mips16;
                              || mips_opts.isa == ISA_MIPS64            \
                              || mips_opts.isa == ISA_MIPS64R2)
 
+/* True if any microMIPS code was produced.  */
+static int file_ase_micromips;
+
 /* True if we want to create R_MIPS_JALR for jalr $25.  */
 #ifdef TE_IRIX
 #define MIPS_JALR_HINT_P(EXPR) HAVE_NEWABI
@@ -323,7 +354,8 @@ static int file_ase_smartmips;
 static int file_ase_dsp;
 
 #define ISA_SUPPORTS_DSP_ASE (mips_opts.isa == ISA_MIPS32R2            \
-                             || mips_opts.isa == ISA_MIPS64R2)
+                             || mips_opts.isa == ISA_MIPS64R2          \
+                             || mips_opts.micromips)
 
 #define ISA_SUPPORTS_DSP64_ASE (mips_opts.isa == ISA_MIPS64R2)
 
@@ -332,7 +364,16 @@ static int file_ase_dsp;
 static int file_ase_dspr2;
 
 #define ISA_SUPPORTS_DSPR2_ASE (mips_opts.isa == ISA_MIPS32R2          \
-                               || mips_opts.isa == ISA_MIPS64R2)
+                               || mips_opts.isa == ISA_MIPS64R2        \
+                               || mips_opts.micromips)
+
+/* True if -meva was passed or implied by arguments passed on the
+   command line (e.g., by -march).  */
+static int file_ase_eva;
+
+#define ISA_SUPPORTS_EVA_ASE (mips_opts.isa == ISA_MIPS32R2            \
+                             || mips_opts.isa == ISA_MIPS64R2          \
+                             || mips_opts.micromips)
 
 /* True if -mmt was passed or implied by arguments passed on the
    command line (e.g., by -march).  */
@@ -341,6 +382,22 @@ static int file_ase_mt;
 #define ISA_SUPPORTS_MT_ASE (mips_opts.isa == ISA_MIPS32R2             \
                             || mips_opts.isa == ISA_MIPS64R2)
 
+#define ISA_SUPPORTS_MCU_ASE (mips_opts.isa == ISA_MIPS32R2            \
+                             || mips_opts.isa == ISA_MIPS64R2          \
+                             || mips_opts.micromips)
+
+/* True if -mvirt was passed or implied by arguments passed on the
+   command line (e.g., by -march). */
+static int file_ase_virt;
+
+#define ISA_SUPPORTS_VIRT_ASE (mips_opts.isa == ISA_MIPS32R2           \
+                              || mips_opts.isa == ISA_MIPS64R2         \
+                              || mips_opts.micromips)
+
+#define ISA_SUPPORTS_VIRT64_ASE (mips_opts.isa == ISA_MIPS64R2         \
+                                || (mips_opts.micromips                \
+                                    && ISA_HAS_64BIT_REGS (mips_opts.isa)))
+
 /* The argument of the -march= flag.  The architecture we are assembling.  */
 static int file_mips_arch = CPU_UNKNOWN;
 static const char *mips_arch_string;
@@ -382,14 +439,19 @@ static int mips_32bitmode = 0;
 /* Return true if ISA supports 64-bit right rotate (dror et al.)
    instructions.  */
 #define ISA_HAS_DROR(ISA)              \
-  ((ISA) == ISA_MIPS64R2)
+  ((ISA) == ISA_MIPS64R2               \
+   || (mips_opts.micromips             \
+       && ISA_HAS_64BIT_REGS (ISA))    \
+   )
 
 /* Return true if ISA supports 32-bit right rotate (ror et al.)
    instructions.  */
 #define ISA_HAS_ROR(ISA)               \
   ((ISA) == ISA_MIPS32R2               \
    || (ISA) == ISA_MIPS64R2            \
-   || mips_opts.ase_smartmips)
+   || mips_opts.ase_smartmips          \
+   || mips_opts.micromips              \
+   )
 
 /* Return true if ISA supports single-precision floats in odd registers.  */
 #define ISA_HAS_ODD_SINGLE_FPR(ISA)    \
@@ -451,19 +513,24 @@ static int mips_32bitmode = 0;
    (strncmp (TARGET_CPU, "mips16", sizeof ("mips16") - 1) == 0         \
     || strncmp (TARGET_CANONICAL, "mips-lsi-elf", sizeof ("mips-lsi-elf") - 1) == 0)
 
+/* Return true if the given CPU supports the microMIPS ASE.  */
+#define CPU_HAS_MICROMIPS(cpu) 0
+
 /* True if CPU has a dror instruction.  */
 #define CPU_HAS_DROR(CPU)      ((CPU) == CPU_VR5400 || (CPU) == CPU_VR5500)
 
 /* True if CPU has a ror instruction.  */
 #define CPU_HAS_ROR(CPU)       CPU_HAS_DROR (CPU)
 
+/* True if CPU is in the Octeon family */
+#define CPU_IS_OCTEON(CPU) ((CPU) == CPU_OCTEON || (CPU) == CPU_OCTEONP || (CPU) == CPU_OCTEON2)
+
 /* True if CPU has seq/sne and seqi/snei instructions.  */
-#define CPU_HAS_SEQ(CPU)       ((CPU) == CPU_OCTEON)
+#define CPU_HAS_SEQ(CPU)       (CPU_IS_OCTEON (CPU))
 
-/* True if CPU does not implement the all the coprocessor insns.  For these
-   CPUs only those COP insns are accepted that are explicitly marked to be
-   available on the CPU.  ISA membership for COP insns is ignored.  */
-#define NO_ISA_COP(CPU)                ((CPU) == CPU_OCTEON)
+/* True, if CPU has support for ldc1 and sdc1. */
+#define CPU_HAS_LDC1_SDC1(CPU) \
+   ((mips_opts.isa != ISA_MIPS1) && ((CPU) != CPU_R5900))
 
 /* True if mflo and mfhi can be immediately followed by instructions
    which write to the HI and LO registers.
@@ -483,22 +550,27 @@ static int mips_32bitmode = 0;
    || mips_opts.isa == ISA_MIPS64                     \
    || mips_opts.isa == ISA_MIPS64R2                   \
    || mips_opts.arch == CPU_R4010                     \
+   || mips_opts.arch == CPU_R5900                     \
    || mips_opts.arch == CPU_R10000                    \
    || mips_opts.arch == CPU_R12000                    \
    || mips_opts.arch == CPU_R14000                    \
    || mips_opts.arch == CPU_R16000                    \
    || mips_opts.arch == CPU_RM7000                    \
    || mips_opts.arch == CPU_VR5500                    \
+   || mips_opts.micromips                             \
    )
 
 /* Whether the processor uses hardware interlocks to protect reads
    from the GPRs after they are loaded from memory, and thus does not
    require nops to be inserted.  This applies to instructions marked
    INSN_LOAD_MEMORY_DELAY.  These nops are only required at MIPS ISA
-   level I.  */
-#define gpr_interlocks \
-  (mips_opts.isa != ISA_MIPS1  \
-   || mips_opts.arch == CPU_R3900)
+   level I and microMIPS mode instructions are always interlocked.  */
+#define gpr_interlocks                                \
+  (mips_opts.isa != ISA_MIPS1                         \
+   || mips_opts.arch == CPU_R3900                     \
+   || mips_opts.arch == CPU_R5900                     \
+   || mips_opts.micromips                             \
+   )
 
 /* Whether the processor uses hardware interlocks to avoid delays
    required by coprocessor instructions, and thus does not require
@@ -506,34 +578,37 @@ static int mips_32bitmode = 0;
    INSN_LOAD_COPROC_DELAY, INSN_COPROC_MOVE_DELAY, and to delays
    between instructions marked INSN_WRITE_COND_CODE and ones marked
    INSN_READ_COND_CODE.  These nops are only required at MIPS ISA
-   levels I, II, and III.  */
+   levels I, II, and III and microMIPS mode instructions are always
+   interlocked.  */
 /* Itbl support may require additional care here.  */
 #define cop_interlocks                                \
   ((mips_opts.isa != ISA_MIPS1                        \
     && mips_opts.isa != ISA_MIPS2                     \
     && mips_opts.isa != ISA_MIPS3)                    \
    || mips_opts.arch == CPU_R4300                     \
+   || mips_opts.micromips                             \
    )
 
 /* Whether the processor uses hardware interlocks to protect reads
    from coprocessor registers after they are loaded from memory, and
    thus does not require nops to be inserted.  This applies to
    instructions marked INSN_COPROC_MEMORY_DELAY.  These nops are only
-   requires at MIPS ISA level I.  */
-#define cop_mem_interlocks (mips_opts.isa != ISA_MIPS1)
+   requires at MIPS ISA level I and microMIPS mode instructions are
+   always interlocked.  */
+#define cop_mem_interlocks                            \
+  (mips_opts.isa != ISA_MIPS1                         \
+   || mips_opts.micromips                             \
+   )
 
 /* Is this a mfhi or mflo instruction?  */
 #define MF_HILO_INSN(PINFO) \
   ((PINFO & INSN_READ_HI) || (PINFO & INSN_READ_LO))
 
-/* Returns true for a (non floating-point) coprocessor instruction.  Reading
-   or writing the condition code is only possible on the coprocessors and
-   these insns are not marked with INSN_COP.  Thus for these insns use the
-   condition-code flags.  */
-#define COP_INSN(PINFO)                                                        \
-  (PINFO != INSN_MACRO                                                 \
-   && ((PINFO) & (FP_S | FP_D)) == 0                                   \
-   && ((PINFO) & (INSN_COP | INSN_READ_COND_CODE | INSN_WRITE_COND_CODE)))
+/* Whether code compression (either of the MIPS16 or the microMIPS ASEs)
+   has been selected.  This implies, in particular, that addresses of text
+   labels have their LSB set.  */
+#define HAVE_CODE_COMPRESSION                                          \
+  ((mips_opts.mips16 | mips_opts.micromips) != 0)
 
 /* MIPS PIC level.  */
 
@@ -591,6 +666,9 @@ static struct hash_control *op_hash = NULL;
 /* The opcode hash table we use for the mips16.  */
 static struct hash_control *mips16_op_hash = NULL;
 
+/* The opcode hash table we use for the microMIPS ASE.  */
+static struct hash_control *micromips_op_hash = NULL;
+
 /* This array holds the chars that always start a comment.  If the
     pre-processor is disabled, these aren't very useful */
 const char comment_chars[] = "#";
@@ -680,10 +758,17 @@ static int mips_debug = 0;
 static struct mips_cl_insn history[1 + MAX_NOPS];
 
 /* Nop instructions used by emit_nop.  */
-static struct mips_cl_insn nop_insn, mips16_nop_insn;
+static struct mips_cl_insn nop_insn;
+static struct mips_cl_insn mips16_nop_insn;
+static struct mips_cl_insn micromips_nop16_insn;
+static struct mips_cl_insn micromips_nop32_insn;
 
 /* The appropriate nop for the current mode.  */
-#define NOP_INSN (mips_opts.mips16 ? &mips16_nop_insn : &nop_insn)
+#define NOP_INSN (mips_opts.mips16 ? &mips16_nop_insn \
+                 : (mips_opts.micromips ? &micromips_nop16_insn : &nop_insn))
+
+/* The size of NOP_INSN in bytes.  */
+#define NOP_INSN_SIZE (HAVE_CODE_COMPRESSION ? 2 : 4)
 
 /* If this is set, it points to a frag holding nop instructions which
    were inserted before the start of a noreorder section.  If those
@@ -750,6 +835,96 @@ 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
+#define micromips_to_32_reg_c_map      mips16_to_32_reg_map
+#define micromips_to_32_reg_d_map      mips16_to_32_reg_map
+#define micromips_to_32_reg_e_map      mips16_to_32_reg_map
+#define micromips_to_32_reg_f_map      mips16_to_32_reg_map
+#define micromips_to_32_reg_g_map      mips16_to_32_reg_map
+
+/* The microMIPS registers with type h.  */
+static const unsigned int micromips_to_32_reg_h_map[] =
+{
+  5, 5, 6, 4, 4, 4, 4, 4
+};
+
+/* The microMIPS registers with type i.  */
+static const unsigned int micromips_to_32_reg_i_map[] =
+{
+  6, 7, 7, 21, 22, 5, 6, 7
+};
+
+#define micromips_to_32_reg_l_map      mips16_to_32_reg_map
+
+/* The microMIPS registers with type m.  */
+static const unsigned int micromips_to_32_reg_m_map[] =
+{
+  0, 17, 2, 3, 16, 18, 19, 20
+};
+
+#define micromips_to_32_reg_n_map      micromips_to_32_reg_m_map
+
+/* The microMIPS registers with type q.  */
+static const unsigned int micromips_to_32_reg_q_map[] =
+{
+  0, 17, 2, 3, 4, 5, 6, 7
+};
+
+/* 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
@@ -831,6 +1006,18 @@ static int mips_relax_branch;
        Like RELAX_NOMACRO, but indicates that the macro appears in a branch
        delay slot.
 
+   RELAX_DELAY_SLOT_16BIT
+       Like RELAX_DELAY_SLOT, but indicates that the delay slot requires a
+       16-bit instruction.
+
+   RELAX_DELAY_SLOT_SIZE_FIRST
+       Like RELAX_DELAY_SLOT, but indicates that the first implementation of
+       the macro is of the wrong size for the branch delay slot.
+
+   RELAX_DELAY_SLOT_SIZE_SECOND
+       Like RELAX_DELAY_SLOT, but indicates that the second implementation of
+       the macro is of the wrong size for the branch delay slot.
+
    The frag's "opcode" points to the first fixup for relaxable code.
 
    Relaxable macros are generated using a sequence such as:
@@ -851,6 +1038,9 @@ static int mips_relax_branch;
 #define RELAX_SECOND_LONGER 0x20000
 #define RELAX_NOMACRO 0x40000
 #define RELAX_DELAY_SLOT 0x80000
+#define RELAX_DELAY_SLOT_16BIT 0x100000
+#define RELAX_DELAY_SLOT_SIZE_FIRST 0x200000
+#define RELAX_DELAY_SLOT_SIZE_SECOND 0x400000
 
 /* Branch without likely bit.  If label is out of range, we turn:
 
@@ -919,18 +1109,20 @@ static int mips_relax_branch;
 
 
    but it's not clear that it would actually improve performance.  */
-#define RELAX_BRANCH_ENCODE(uncond, likely, link, toofar) \
-  ((relax_substateT) \
-   (0xc0000000 \
-    | ((toofar) ? 1 : 0) \
-    | ((link) ? 2 : 0) \
-    | ((likely) ? 4 : 0) \
-    | ((uncond) ? 8 : 0)))
+#define RELAX_BRANCH_ENCODE(at, uncond, likely, link, toofar)  \
+  ((relax_substateT)                                           \
+   (0xc0000000                                                 \
+    | ((at) & 0x1f)                                            \
+    | ((toofar) ? 0x20 : 0)                                    \
+    | ((link) ? 0x40 : 0)                                      \
+    | ((likely) ? 0x80 : 0)                                    \
+    | ((uncond) ? 0x100 : 0)))
 #define RELAX_BRANCH_P(i) (((i) & 0xf0000000) == 0xc0000000)
-#define RELAX_BRANCH_UNCOND(i) (((i) & 8) != 0)
-#define RELAX_BRANCH_LIKELY(i) (((i) & 4) != 0)
-#define RELAX_BRANCH_LINK(i) (((i) & 2) != 0)
-#define RELAX_BRANCH_TOOFAR(i) (((i) & 1) != 0)
+#define RELAX_BRANCH_UNCOND(i) (((i) & 0x100) != 0)
+#define RELAX_BRANCH_LIKELY(i) (((i) & 0x80) != 0)
+#define RELAX_BRANCH_LINK(i) (((i) & 0x40) != 0)
+#define RELAX_BRANCH_TOOFAR(i) (((i) & 0x20) != 0)
+#define RELAX_BRANCH_AT(i) ((i) & 0x1f)
 
 /* For mips16 code, we use an entirely different form of relaxation.
    mips16 supports two versions of most instructions which take
@@ -972,6 +1164,55 @@ static int mips_relax_branch;
 #define RELAX_MIPS16_MARK_LONG_BRANCH(i) ((i) | 0x2000)
 #define RELAX_MIPS16_CLEAR_LONG_BRANCH(i) ((i) &~ 0x2000)
 
+/* For microMIPS code, we use relaxation similar to one we use for
+   MIPS16 code.  Some instructions that take immediate values support
+   two encodings: a small one which takes some small value, and a
+   larger one which takes a 16 bit value.  As some branches also follow
+   this pattern, relaxing these values is required.
+
+   We can assemble both microMIPS and normal MIPS code in a single
+   object.  Therefore, we need to support this type of relaxation at
+   the same time that we support the relaxation described above.  We
+   use one of the high bits of the subtype field to distinguish these
+   cases.
+
+   The information we store for this type of relaxation is the argument
+   code found in the opcode file for this relocation, the register
+   selected as the assembler temporary, whether the branch is
+   unconditional, whether it is compact, whether it stores the link
+   address implicitly in $ra, whether relaxation of out-of-range 32-bit
+   branches to a sequence of instructions is enabled, and whether the
+   displacement of a branch is too large to fit as an immediate argument
+   of a 16-bit and a 32-bit branch, respectively.  */
+#define RELAX_MICROMIPS_ENCODE(type, at, uncond, compact, link,        \
+                              relax32, toofar16, toofar32)     \
+  (0x40000000                                                  \
+   | ((type) & 0xff)                                           \
+   | (((at) & 0x1f) << 8)                                      \
+   | ((uncond) ? 0x2000 : 0)                                   \
+   | ((compact) ? 0x4000 : 0)                                  \
+   | ((link) ? 0x8000 : 0)                                     \
+   | ((relax32) ? 0x10000 : 0)                                 \
+   | ((toofar16) ? 0x20000 : 0)                                        \
+   | ((toofar32) ? 0x40000 : 0))
+#define RELAX_MICROMIPS_P(i) (((i) & 0xc0000000) == 0x40000000)
+#define RELAX_MICROMIPS_TYPE(i) ((i) & 0xff)
+#define RELAX_MICROMIPS_AT(i) (((i) >> 8) & 0x1f)
+#define RELAX_MICROMIPS_UNCOND(i) (((i) & 0x2000) != 0)
+#define RELAX_MICROMIPS_COMPACT(i) (((i) & 0x4000) != 0)
+#define RELAX_MICROMIPS_LINK(i) (((i) & 0x8000) != 0)
+#define RELAX_MICROMIPS_RELAX32(i) (((i) & 0x10000) != 0)
+
+#define RELAX_MICROMIPS_TOOFAR16(i) (((i) & 0x20000) != 0)
+#define RELAX_MICROMIPS_MARK_TOOFAR16(i) ((i) | 0x20000)
+#define RELAX_MICROMIPS_CLEAR_TOOFAR16(i) ((i) & ~0x20000)
+#define RELAX_MICROMIPS_TOOFAR32(i) (((i) & 0x40000) != 0)
+#define RELAX_MICROMIPS_MARK_TOOFAR32(i) ((i) | 0x40000)
+#define RELAX_MICROMIPS_CLEAR_TOOFAR32(i) ((i) & ~0x40000)
+
+/* Sign-extend 16-bit value X.  */
+#define SEXT_16BIT(X) ((((X) + 0x8000) & 0xffff) - 0x8000)
+
 /* Is the given value a sign-extended 32-bit value?  */
 #define IS_SEXT_32BIT_NUM(x)                                           \
   (((x) &~ (offsetT) 0x7fffffff) == 0                                  \
@@ -982,6 +1223,14 @@ static int mips_relax_branch;
   (((x) &~ (offsetT) 0x7fff) == 0                                      \
    || (((x) &~ (offsetT) 0x7fff) == ~ (offsetT) 0x7fff))
 
+/* Is the given value a sign-extended 12-bit value?  */
+#define IS_SEXT_12BIT_NUM(x)                                           \
+  (((((x) & 0xfff) ^ 0x800LL) - 0x800LL) == (x))
+
+/* Is the given value a sign-extended 9-bit value?  */
+#define IS_SEXT_9BIT_NUM(x)                                            \
+  (((((x) & 0x1ff) ^ 0x100LL) - 0x100LL) == (x))
+
 /* Is the given value a zero-extended 32-bit value?  Or a negated one?  */
 #define IS_ZEXT_32BIT_NUM(x)                                           \
   (((x) &~ (offsetT) 0xffffffff) == 0                                  \
@@ -1004,20 +1253,36 @@ static int mips_relax_branch;
    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(FIELD, INSN, VALUE) \
-  INSERT_BITS ((INSN).insn_opcode, VALUE, OP_MASK_##FIELD, OP_SH_##FIELD)
+#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(FIELD, INSN) \
-  EXTRACT_BITS ((INSN).insn_opcode, OP_MASK_##FIELD, OP_SH_##FIELD)
+#define EXTRACT_OPERAND(MICROMIPS, FIELD, INSN) \
+  (!(MICROMIPS) \
+   ? EXTRACT_BITS ((INSN).insn_opcode, OP_MASK_##FIELD, OP_SH_##FIELD) \
+   : EXTRACT_BITS ((INSN).insn_opcode, \
+                  MICROMIPSOP_MASK_##FIELD, MICROMIPSOP_SH_##FIELD))
 #define MIPS16_EXTRACT_OPERAND(FIELD, INSN) \
   EXTRACT_BITS ((INSN).insn_opcode, \
                MIPS16OP_MASK_##FIELD, \
                MIPS16OP_SH_##FIELD)
+
+/* The MIPS16 EXTEND opcode, shifted left 16 places.  */
+#define MIPS16_EXTEND (0xf000U << 16)
 \f
+/* Whether or not we are emitting a branch-likely macro.  */
+static bfd_boolean emit_branch_likely_macro = FALSE;
+
 /* Global variables used when generating relaxable macros.  See the
    comment above RELAX_ENCODE for more details about how relaxation
    is used.  */
@@ -1044,25 +1309,44 @@ static struct {
   /* True if the macro is in a branch delay slot.  */
   bfd_boolean delay_slot_p;
 
+  /* Set to the length in bytes required if the macro is in a delay slot
+     that requires a specific length of instruction, otherwise zero.  */
+  unsigned int delay_slot_length;
+
   /* For relaxable macros, sizes[0] is the length of the first alternative
      in bytes and sizes[1] is the length of the second alternative.
      For non-relaxable macros, both elements give the length of the
      macro in bytes.  */
   unsigned int sizes[2];
 
+  /* For relaxable macros, first_insn_sizes[0] is the length of the first
+     instruction of the first alternative in bytes and first_insn_sizes[1]
+     is the length of the first instruction of the second alternative.
+     For non-relaxable macros, both elements give the length of the first
+     instruction in bytes.
+
+     Set to zero if we haven't yet seen the first instruction.  */
+  unsigned int first_insn_sizes[2];
+
+  /* For relaxable macros, insns[0] is the number of instructions for the
+     first alternative and insns[1] is the number of instructions for the
+     second alternative.
+
+     For non-relaxable macros, both elements give the number of
+     instructions for the macro.  */
+  unsigned int insns[2];
+
   /* The first variant frag for this macro.  */
   fragS *first_frag;
 } mips_macro_warning;
 \f
 /* Prototypes for static functions.  */
 
-#define internalError()                                                        \
-    as_fatal (_("internal Error, line %d, %s"), __LINE__, __FILE__)
-
 enum mips_regclass { MIPS_GR_REG, MIPS_FP_REG, MIPS16_REG };
 
 static void append_insn
-  (struct mips_cl_insn *, expressionS *, bfd_reloc_code_real_type *);
+  (struct mips_cl_insn *, expressionS *, bfd_reloc_code_real_type *,
+   bfd_boolean expansionp);
 static void mips_no_prev_insn (void);
 static void macro_build (expressionS *, const char *, const char *, ...);
 static void mips16_macro_build
@@ -1075,8 +1359,8 @@ static void mips16_macro (struct mips_cl_insn * ip);
 static void mips_ip (char *str, struct mips_cl_insn * ip);
 static void mips16_ip (char *str, struct mips_cl_insn * ip);
 static void mips16_immed
-  (char *, unsigned int, int, offsetT, bfd_boolean, bfd_boolean, bfd_boolean,
-   unsigned long *, bfd_boolean *, unsigned short *);
+  (char *, unsigned int, int, bfd_reloc_code_real_type, offsetT,
+   unsigned int, unsigned long *);
 static size_t my_getSmallExpression
   (expressionS *, bfd_reloc_code_real_type *, char *);
 static void my_getExpression (expressionS *, char *);
@@ -1096,9 +1380,12 @@ static void s_cprestore (int);
 static void s_cpreturn (int);
 static void s_dtprelword (int);
 static void s_dtpreldword (int);
+static void s_tprelword (int);
+static void s_tpreldword (int);
 static void s_gpvalue (int);
 static void s_gpword (int);
 static void s_gpdword (int);
+static void s_ehword (int);
 static void s_cpadd (int);
 static void s_insn (int);
 static void md_obj_begin (void);
@@ -1114,6 +1401,9 @@ 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);
 
 /* Table and functions used to map between CPU/ISA names, and
    ISA levels, and CPU numbers.  */
@@ -1121,18 +1411,13 @@ static int validate_mips_insn (const struct mips_opcode *);
 struct mips_cpu_info
 {
   const char *name;           /* CPU or ISA name.  */
-  int flags;                  /* ASEs available, or ISA flag.  */
+  int flags;                  /* MIPS_CPU_* flags.  */
+  int ase;                    /* Set of ASEs implemented by the CPU.  */
   int isa;                    /* ISA level.  */
   int cpu;                    /* CPU number (default CPU if ISA).  */
 };
 
 #define MIPS_CPU_IS_ISA                0x0001  /* Is this an ISA?  (If 0, a CPU.) */
-#define MIPS_CPU_ASE_SMARTMIPS 0x0002  /* CPU implements SmartMIPS ASE */
-#define MIPS_CPU_ASE_DSP       0x0004  /* CPU implements DSP ASE */
-#define MIPS_CPU_ASE_MT                0x0008  /* CPU implements MT ASE */
-#define MIPS_CPU_ASE_MIPS3D    0x0010  /* CPU implements MIPS-3D ASE */
-#define MIPS_CPU_ASE_MDMX      0x0020  /* CPU implements MDMX ASE */
-#define MIPS_CPU_ASE_DSPR2     0x0040  /* CPU implements DSP R2 ASE */
 
 static const struct mips_cpu_info *mips_parse_cpu (const char *, const char *);
 static const struct mips_cpu_info *mips_cpu_info_from_isa (int);
@@ -1171,9 +1456,12 @@ static const pseudo_typeS mips_pseudo_table[] =
   {"cpreturn", s_cpreturn, 0},
   {"dtprelword", s_dtprelword, 0},
   {"dtpreldword", s_dtpreldword, 0},
+  {"tprelword", s_tprelword, 0},
+  {"tpreldword", s_tpreldword, 0},
   {"gpvalue", s_gpvalue, 0},
   {"gpword", s_gpword, 0},
   {"gpdword", s_gpdword, 0},
+  {"ehword", s_ehword, 0},
   {"cpadd", s_cpadd, 0},
   {"insn", s_insn, 0},
 
@@ -1208,7 +1496,9 @@ static const pseudo_typeS mips_pseudo_table[] =
   {"section", s_change_section, 0},
   {"short", s_cons, 1},
   {"single", s_float_cons, 'f'},
+  {"stabd", s_mips_stab, 'd'},
   {"stabn", s_mips_stab, 'n'},
+  {"stabs", s_mips_stab, 's'},
   {"text", s_change_sec, 't'},
   {"word", s_cons, 2},
 
@@ -1235,6 +1525,15 @@ static const pseudo_typeS mips_nonecoff_pseudo_table[] =
   { NULL, NULL, 0 },
 };
 
+/* Export the ABI address size for use by TC_ADDRESS_BYTES for the
+   purpose of the `.dc.a' internal pseudo-op.  */
+
+int
+mips_address_bytes (void)
+{
+  return HAVE_64BIT_ADDRESSES ? 8 : 4;
+}
+
 extern void pop_insert (const pseudo_typeS *);
 
 void
@@ -1257,6 +1556,8 @@ static struct insn_label_list *free_insn_labels;
 #define label_list tc_segment_info_data.labels
 
 static void mips_clear_insn_labels (void);
+static void mips_mark_labels (void);
+static void mips_compressed_mark_labels (void);
 
 static inline void
 mips_clear_insn_labels (void)
@@ -1275,6 +1576,14 @@ mips_clear_insn_labels (void)
     }
 }
 
+/* Mark instruction labels in MIPS16/microMIPS mode.  */
+
+static inline void
+mips_mark_labels (void)
+{
+  if (HAVE_CODE_COMPRESSION)
+    mips_compressed_mark_labels ();
+}
 \f
 static char *expr_end;
 
@@ -1292,9 +1601,16 @@ static bfd_reloc_code_real_type imm_reloc[3]
 static bfd_reloc_code_real_type offset_reloc[3]
   = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
 
-/* These are set by mips16_ip if an explicit extension is used.  */
+/* This is set to the resulting size of the instruction to be produced
+   by mips16_ip if an explicit extension is used or by mips_ip if an
+   explicit size is supplied.  */
+
+static unsigned int forced_insn_length;
+
+/* True if we are assembling an instruction.  All dot symbols defined during
+   this time should be treated as code labels.  */
 
-static bfd_boolean mips16_small, mips16_ext;
+static bfd_boolean mips_assembling_insn;
 
 #ifdef OBJ_ELF
 /* The pdr segment for per procedure frame/regmask info.  Not used for
@@ -1305,6 +1621,14 @@ static segT pdr_seg;
 
 /* The default target format to use.  */
 
+#if defined (TE_FreeBSD)
+#define ELF_TARGET(PREFIX, ENDIAN) PREFIX "trad" ENDIAN "mips-freebsd"
+#elif defined (TE_TMIPS)
+#define ELF_TARGET(PREFIX, ENDIAN) PREFIX "trad" ENDIAN "mips"
+#else
+#define ELF_TARGET(PREFIX, ENDIAN) PREFIX ENDIAN "mips"
+#endif
+
 const char *
 mips_target_format (void)
 {
@@ -1321,42 +1645,54 @@ mips_target_format (void)
                ? "elf32-bigmips-vxworks"
                : "elf32-littlemips-vxworks");
 #endif
-#ifdef TE_TMIPS
-      /* This is traditional mips.  */
-      return (target_big_endian
-             ? (HAVE_64BIT_OBJECTS
-                ? "elf64-tradbigmips"
-                : (HAVE_NEWABI
-                   ? "elf32-ntradbigmips" : "elf32-tradbigmips"))
-             : (HAVE_64BIT_OBJECTS
-                ? "elf64-tradlittlemips"
-                : (HAVE_NEWABI
-                   ? "elf32-ntradlittlemips" : "elf32-tradlittlemips")));
-#else
       return (target_big_endian
              ? (HAVE_64BIT_OBJECTS
-                ? "elf64-bigmips"
+                ? ELF_TARGET ("elf64-", "big")
                 : (HAVE_NEWABI
-                   ? "elf32-nbigmips" : "elf32-bigmips"))
+                   ? ELF_TARGET ("elf32-n", "big")
+                   : ELF_TARGET ("elf32-", "big")))
              : (HAVE_64BIT_OBJECTS
-                ? "elf64-littlemips"
+                ? ELF_TARGET ("elf64-", "little")
                 : (HAVE_NEWABI
-                   ? "elf32-nlittlemips" : "elf32-littlemips")));
-#endif
+                   ? ELF_TARGET ("elf32-n", "little")
+                   : ELF_TARGET ("elf32-", "little"))));
     default:
       abort ();
       return NULL;
     }
 }
 
+/* Return the length of a microMIPS instruction in bytes.  If bits of
+   the mask beyond the low 16 are 0, then it is a 16-bit instruction.
+   Otherwise assume a 32-bit instruction; 48-bit instructions (0x1f
+   major opcode) will require further modifications to the opcode
+   table.  */
+
+static inline unsigned int
+micromips_insn_length (const struct mips_opcode *mo)
+{
+  return (mo->mask >> 16) == 0 ? 2 : 4;
+}
+
+/* Return the length of MIPS16 instruction OPCODE.  */
+
+static inline unsigned int
+mips16_opcode_length (unsigned long opcode)
+{
+  return (opcode >> 16) == 0 ? 2 : 4;
+}
+
 /* Return the length of instruction INSN.  */
 
 static inline unsigned int
 insn_length (const struct mips_cl_insn *insn)
 {
-  if (!mips_opts.mips16)
+  if (mips_opts.micromips)
+    return micromips_insn_length (insn->insn_mo);
+  else if (mips_opts.mips16)
+    return mips16_opcode_length (insn->insn_opcode);
+  else
     return 4;
-  return insn->mips16_absolute_jump_p || insn->use_extend ? 4 : 2;
 }
 
 /* Initialise INSN from opcode entry MO.  Leave its position unspecified.  */
@@ -1367,8 +1703,6 @@ create_insn (struct mips_cl_insn *insn, const struct mips_opcode *mo)
   size_t i;
 
   insn->insn_mo = mo;
-  insn->use_extend = FALSE;
-  insn->extend = 0;
   insn->insn_opcode = mo->match;
   insn->frag = NULL;
   insn->where = 0;
@@ -1377,18 +1711,78 @@ create_insn (struct mips_cl_insn *insn, const struct mips_opcode *mo)
   insn->fixed_p = (mips_opts.noreorder > 0);
   insn->noreorder_p = (mips_opts.noreorder > 0);
   insn->mips16_absolute_jump_p = 0;
+  insn->complete_p = 0;
+  insn->cleared_p = 0;
 }
 
-/* Record the current MIPS16 mode in now_seg.  */
+/* Record the current MIPS16/microMIPS mode in now_seg.  */
 
 static void
-mips_record_mips16_mode (void)
+mips_record_compressed_mode (void)
 {
   segment_info_type *si;
 
   si = seg_info (now_seg);
   if (si->tc_segment_info_data.mips16 != mips_opts.mips16)
     si->tc_segment_info_data.mips16 = mips_opts.mips16;
+  if (si->tc_segment_info_data.micromips != mips_opts.micromips)
+    si->tc_segment_info_data.micromips = mips_opts.micromips;
+}
+
+/* Read a standard MIPS instruction from BUF.  */
+
+static unsigned long
+read_insn (char *buf)
+{
+  if (target_big_endian)
+    return bfd_getb32 ((bfd_byte *) buf);
+  else
+    return bfd_getl32 ((bfd_byte *) buf);
+}
+
+/* Write standard MIPS instruction INSN to BUF.  Return a pointer to
+   the next byte.  */
+
+static char *
+write_insn (char *buf, unsigned int insn)
+{
+  md_number_to_chars (buf, insn, 4);
+  return buf + 4;
+}
+
+/* Read a microMIPS or MIPS16 opcode from BUF, given that it
+   has length LENGTH.  */
+
+static unsigned long
+read_compressed_insn (char *buf, unsigned int length)
+{
+  unsigned long insn;
+  unsigned int i;
+
+  insn = 0;
+  for (i = 0; i < length; i += 2)
+    {
+      insn <<= 16;
+      if (target_big_endian)
+       insn |= bfd_getb16 ((char *) buf);
+      else
+       insn |= bfd_getl16 ((char *) buf);
+      buf += 2;
+    }
+  return insn;
+}
+
+/* Write microMIPS or MIPS16 instruction INSN to BUF, given that the
+   instruction is LENGTH bytes long.  Return a pointer to the next byte.  */
+
+static char *
+write_compressed_insn (char *buf, unsigned int insn, unsigned int length)
+{
+  unsigned int i;
+
+  for (i = 0; i < length; i += 2)
+    md_number_to_chars (buf + i, insn >> ((length - i - 2) * 8), 2);
+  return buf + length;
 }
 
 /* Install INSN at the location specified by its "frag" and "where" fields.  */
@@ -1397,23 +1791,11 @@ static void
 install_insn (const struct mips_cl_insn *insn)
 {
   char *f = insn->frag->fr_literal + insn->where;
-  if (!mips_opts.mips16)
-    md_number_to_chars (f, insn->insn_opcode, 4);
-  else if (insn->mips16_absolute_jump_p)
-    {
-      md_number_to_chars (f, insn->insn_opcode >> 16, 2);
-      md_number_to_chars (f + 2, insn->insn_opcode & 0xffff, 2);
-    }
+  if (HAVE_CODE_COMPRESSION)
+    write_compressed_insn (f, insn->insn_opcode, insn_length (insn));
   else
-    {
-      if (insn->use_extend)
-       {
-         md_number_to_chars (f, 0xf000 | insn->extend, 2);
-         f += 2;
-       }
-      md_number_to_chars (f, insn->insn_opcode, 2);
-    }
-  mips_record_mips16_mode ();
+    write_insn (f, insn->insn_opcode);
+  mips_record_compressed_mode ();
 }
 
 /* Move INSN to offset WHERE in FRAG.  Adjust the fixups accordingly
@@ -1477,15 +1859,6 @@ insert_into_history (unsigned int first, unsigned int n,
     }
 }
 
-/* Emit a nop instruction, recording it in the history buffer.  */
-
-static void
-emit_nop (void)
-{
-  add_fixed_insn (NOP_INSN);
-  insert_into_history (0, 1, NOP_INSN);
-}
-
 /* Initialize vr4120_conflicts.  There is a bit of duplication here:
    the idea is to make it obvious at a glance that each errata is
    included.  */
@@ -1766,6 +2139,10 @@ 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.  */
+
 static int
 reg_lookup (char **s, unsigned int types, unsigned int *regnop)
 {
@@ -1819,37 +2196,101 @@ reg_lookup (char **s, unsigned int types, unsigned int *regnop)
   return reg >= 0;
 }
 
-/* Return TRUE if opcode MO is valid on the currently selected ISA and
-   architecture.  Use is_opcode_valid_16 for MIPS16 opcodes.  */
+/* 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.
+
+   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.  */
+
+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;
+  unsigned int regno;
+
+  while (reg_lookup (s, types, &regno))
+    {
+      lastregno = regno;
+      if (**s == '-')
+       {
+         (*s)++;
+         ok = reg_lookup (s, types, &lastregno);
+         if (ok && lastregno < regno)
+           ok = FALSE;
+         if (!ok)
+           break;
+       }
+
+      if (lastregno == FP && regno >= S0 && regno <= S7)
+       {
+         lastregno = S7;
+         reglist |= 1 << FP;
+       }
+      regmask = 1 << lastregno;
+      regmask = (regmask << 1) - 1;
+      regmask ^= (1 << regno) - 1;
+      reglist |= regmask;
+
+      s_endlist = *s;
+      if (**s != ',')
+       break;
+      (*s)++;
+    }
+
+  if (ok)
+    *s = s_endlist;
+  else
+    *s = s_reset;
+  if (reglistp)
+    *reglistp = reglist;
+  return ok && reglist != 0;
+}
+
+/* Return TRUE if opcode MO is valid on the currently selected ISA, ASE
+   and architecture.  Use is_opcode_valid_16 for MIPS16 opcodes.  */
 
 static bfd_boolean
 is_opcode_valid (const struct mips_opcode *mo)
 {
   int isa = mips_opts.isa;
+  int ase = 0;
   int fp_s, fp_d;
 
   if (mips_opts.ase_mdmx)
-    isa |= INSN_MDMX;
+    ase |= ASE_MDMX;
   if (mips_opts.ase_dsp)
-    isa |= INSN_DSP;
+    ase |= ASE_DSP;
   if (mips_opts.ase_dsp && ISA_SUPPORTS_DSP64_ASE)
-    isa |= INSN_DSP64;
+    ase |= ASE_DSP64;
   if (mips_opts.ase_dspr2)
-    isa |= INSN_DSPR2;
+    ase |= ASE_DSPR2;
+  if (mips_opts.ase_eva)
+    ase |= ASE_EVA;
   if (mips_opts.ase_mt)
-    isa |= INSN_MT;
+    ase |= ASE_MT;
   if (mips_opts.ase_mips3d)
-    isa |= INSN_MIPS3D;
+    ase |= ASE_MIPS3D;
   if (mips_opts.ase_smartmips)
-    isa |= INSN_SMARTMIPS;
-
-  /* Don't accept instructions based on the ISA if the CPU does not implement
-     all the coprocessor insns. */
-  if (NO_ISA_COP (mips_opts.arch)
-      && COP_INSN (mo->pinfo))
-    isa = 0;
-
-  if (!OPCODE_IS_MEMBER (mo, isa, mips_opts.arch))
+    ase |= ASE_SMARTMIPS;
+  if (mips_opts.ase_mcu)
+    ase |= ASE_MCU;
+  if (mips_opts.ase_virt)
+    ase |= ASE_VIRT;
+  if (mips_opts.ase_virt && ISA_SUPPORTS_VIRT64_ASE)
+    ase |= ASE_VIRT64;
+
+  if (!opcode_is_member (mo, isa, ase, mips_opts.arch))
     return FALSE;
 
   /* Check whether the instruction or macro requires single-precision or
@@ -1881,7 +2322,55 @@ is_opcode_valid (const struct mips_opcode *mo)
 static bfd_boolean
 is_opcode_valid_16 (const struct mips_opcode *mo)
 {
-  return OPCODE_IS_MEMBER (mo, mips_opts.isa, mips_opts.arch) ? TRUE : FALSE;
+  return opcode_is_member (mo, mips_opts.isa, 0, mips_opts.arch);
+}
+
+/* Return TRUE if the size of the microMIPS opcode MO matches one
+   explicitly requested.  Always TRUE in the standard MIPS mode.  */
+
+static bfd_boolean
+is_size_valid (const struct mips_opcode *mo)
+{
+  if (!mips_opts.micromips)
+    return TRUE;
+
+  if (!forced_insn_length)
+    return TRUE;
+  if (mo->pinfo == INSN_MACRO)
+    return FALSE;
+  return forced_insn_length == micromips_insn_length (mo);
+}
+
+/* Return TRUE if the microMIPS opcode MO is valid for the delay slot
+   of the preceding instruction.  Always TRUE in the standard MIPS mode.
+
+   We don't accept macros in 16-bit delay slots to avoid a case where
+   a macro expansion fails because it relies on a preceding 32-bit real
+   instruction to have matched and does not handle the operands correctly.
+   The only macros that may expand to 16-bit instructions are JAL that
+   cannot be placed in a delay slot anyway, and corner cases of BALIGN
+   and BGT (that likewise cannot be placed in a delay slot) that decay to
+   a NOP.  In all these cases the macros precede any corresponding real
+   instruction definitions in the opcode table, so they will match in the
+   second pass where the size of the delay slot is ignored and therefore
+   produce correct code.  */
+
+static bfd_boolean
+is_delay_slot_valid (const struct mips_opcode *mo)
+{
+  if (!mips_opts.micromips)
+    return TRUE;
+
+  if (mo->pinfo == INSN_MACRO)
+    return (history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_16BIT) == 0;
+  if ((history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT) != 0
+      && micromips_insn_length (mo) != 4)
+    return FALSE;
+  if ((history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0
+      && micromips_insn_length (mo) != 2)
+    return FALSE;
+
+  return TRUE;
 }
 
 /* This function is called once, at assembler startup time.  It should set up
@@ -1969,6 +2458,44 @@ md_begin (void)
             && strcmp (mips16_opcodes[i].name, name) == 0);
     }
 
+  micromips_op_hash = hash_new ();
+
+  i = 0;
+  while (i < bfd_micromips_num_opcodes)
+    {
+      const char *name = micromips_opcodes[i].name;
+
+      retval = hash_insert (micromips_op_hash, name,
+                           (void *) &micromips_opcodes[i]);
+      if (retval != NULL)
+       as_fatal (_("internal: can't hash `%s': %s"),
+                 micromips_opcodes[i].name, retval);
+      do
+        if (micromips_opcodes[i].pinfo != INSN_MACRO)
+          {
+            struct mips_cl_insn *micromips_nop_insn;
+
+            if (!validate_micromips_insn (&micromips_opcodes[i]))
+              broken = 1;
+
+           if (micromips_insn_length (micromips_opcodes + i) == 2)
+             micromips_nop_insn = &micromips_nop16_insn;
+           else if (micromips_insn_length (micromips_opcodes + i) == 4)
+             micromips_nop_insn = &micromips_nop32_insn;
+           else
+             continue;
+
+            if (micromips_nop_insn->insn_mo == NULL
+               && strcmp (name, "nop") == 0)
+              {
+                create_insn (micromips_nop_insn, micromips_opcodes + i);
+                micromips_nop_insn->fixed_p = 1;
+              }
+          }
+      while (++i < bfd_micromips_num_opcodes
+            && strcmp (micromips_opcodes[i].name, name) == 0);
+    }
+
   if (broken)
     as_fatal (_("Broken assembler.  No assembly attempted."));
 
@@ -2100,6 +2627,7 @@ md_begin (void)
 void
 md_mips_end (void)
 {
+  mips_emit_delays ();
   if (! ECOFF_DEBUGGING)
     md_obj_end ();
 }
@@ -2121,6 +2649,9 @@ md_assemble (char *str)
   offset_reloc[1] = BFD_RELOC_UNUSED;
   offset_reloc[2] = BFD_RELOC_UNUSED;
 
+  mips_mark_labels ();
+  mips_assembling_insn = TRUE;
+
   if (mips_opts.mips16)
     mips16_ip (str, &insn);
   else
@@ -2131,12 +2662,8 @@ md_assemble (char *str)
     }
 
   if (insn_error)
-    {
-      as_bad ("%s `%s'", insn_error, str);
-      return;
-    }
-
-  if (insn.insn_mo->pinfo == INSN_MACRO)
+    as_bad ("%s `%s'", insn_error, str);
+  else if (insn.insn_mo->pinfo == INSN_MACRO)
     {
       macro_start ();
       if (mips_opts.mips16)
@@ -2148,12 +2675,14 @@ md_assemble (char *str)
   else
     {
       if (imm_expr.X_op != O_absent)
-       append_insn (&insn, &imm_expr, imm_reloc);
+       append_insn (&insn, &imm_expr, imm_reloc, FALSE);
       else if (offset_expr.X_op != O_absent)
-       append_insn (&insn, &offset_expr, offset_reloc);
+       append_insn (&insn, &offset_expr, offset_reloc, FALSE);
       else
-       append_insn (&insn, NULL, unused_reloc);
+       append_insn (&insn, NULL, unused_reloc, FALSE);
     }
+
+  mips_assembling_insn = FALSE;
 }
 
 /* Convenience functions for abstracting away the differences between
@@ -2178,45 +2707,119 @@ mips16_reloc_p (bfd_reloc_code_real_type reloc)
     }
 }
 
+static inline bfd_boolean
+micromips_reloc_p (bfd_reloc_code_real_type reloc)
+{
+  switch (reloc)
+    {
+    case BFD_RELOC_MICROMIPS_7_PCREL_S1:
+    case BFD_RELOC_MICROMIPS_10_PCREL_S1:
+    case BFD_RELOC_MICROMIPS_16_PCREL_S1:
+    case BFD_RELOC_MICROMIPS_GPREL16:
+    case BFD_RELOC_MICROMIPS_JMP:
+    case BFD_RELOC_MICROMIPS_HI16:
+    case BFD_RELOC_MICROMIPS_HI16_S:
+    case BFD_RELOC_MICROMIPS_LO16:
+    case BFD_RELOC_MICROMIPS_LITERAL:
+    case BFD_RELOC_MICROMIPS_GOT16:
+    case BFD_RELOC_MICROMIPS_CALL16:
+    case BFD_RELOC_MICROMIPS_GOT_HI16:
+    case BFD_RELOC_MICROMIPS_GOT_LO16:
+    case BFD_RELOC_MICROMIPS_CALL_HI16:
+    case BFD_RELOC_MICROMIPS_CALL_LO16:
+    case BFD_RELOC_MICROMIPS_SUB:
+    case BFD_RELOC_MICROMIPS_GOT_PAGE:
+    case BFD_RELOC_MICROMIPS_GOT_OFST:
+    case BFD_RELOC_MICROMIPS_GOT_DISP:
+    case BFD_RELOC_MICROMIPS_HIGHEST:
+    case BFD_RELOC_MICROMIPS_HIGHER:
+    case BFD_RELOC_MICROMIPS_SCN_DISP:
+    case BFD_RELOC_MICROMIPS_JALR:
+      return TRUE;
+
+    default:
+      return FALSE;
+    }
+}
+
+static inline bfd_boolean
+jmp_reloc_p (bfd_reloc_code_real_type reloc)
+{
+  return reloc == BFD_RELOC_MIPS_JMP || reloc == BFD_RELOC_MICROMIPS_JMP;
+}
+
 static inline bfd_boolean
 got16_reloc_p (bfd_reloc_code_real_type reloc)
 {
-  return reloc == BFD_RELOC_MIPS_GOT16 || reloc == BFD_RELOC_MIPS16_GOT16;
+  return (reloc == BFD_RELOC_MIPS_GOT16 || reloc == BFD_RELOC_MIPS16_GOT16
+         || reloc == BFD_RELOC_MICROMIPS_GOT16);
 }
 
 static inline bfd_boolean
 hi16_reloc_p (bfd_reloc_code_real_type reloc)
 {
-  return reloc == BFD_RELOC_HI16_S || reloc == BFD_RELOC_MIPS16_HI16_S;
+  return (reloc == BFD_RELOC_HI16_S || reloc == BFD_RELOC_MIPS16_HI16_S
+         || reloc == BFD_RELOC_MICROMIPS_HI16_S);
 }
 
 static inline bfd_boolean
 lo16_reloc_p (bfd_reloc_code_real_type reloc)
 {
-  return reloc == BFD_RELOC_LO16 || reloc == BFD_RELOC_MIPS16_LO16;
+  return (reloc == BFD_RELOC_LO16 || reloc == BFD_RELOC_MIPS16_LO16
+         || reloc == BFD_RELOC_MICROMIPS_LO16);
 }
 
-/* Return true if the given relocation might need a matching %lo().
-   This is only "might" because SVR4 R_MIPS_GOT16 relocations only
-   need a matching %lo() when applied to local symbols.  */
-
 static inline bfd_boolean
-reloc_needs_lo_p (bfd_reloc_code_real_type reloc)
+jalr_reloc_p (bfd_reloc_code_real_type reloc)
 {
-  return (HAVE_IN_PLACE_ADDENDS
-         && (hi16_reloc_p (reloc)
-             /* VxWorks R_MIPS_GOT16 relocs never need a matching %lo();
-                all GOT16 relocations evaluate to "G".  */
-             || (got16_reloc_p (reloc) && mips_pic != VXWORKS_PIC)));
+  return reloc == BFD_RELOC_MIPS_JALR || reloc == BFD_RELOC_MICROMIPS_JALR;
 }
 
-/* Return the type of %lo() reloc needed by RELOC, given that
-   reloc_needs_lo_p.  */
+/* Return true if RELOC is a PC-relative relocation that does not have
+   full address range.  */
 
-static inline bfd_reloc_code_real_type
-matching_lo_reloc (bfd_reloc_code_real_type reloc)
+static inline bfd_boolean
+limited_pcrel_reloc_p (bfd_reloc_code_real_type reloc)
 {
-  return mips16_reloc_p (reloc) ? BFD_RELOC_MIPS16_LO16 : BFD_RELOC_LO16;
+  switch (reloc)
+    {
+    case BFD_RELOC_16_PCREL_S2:
+    case BFD_RELOC_MICROMIPS_7_PCREL_S1:
+    case BFD_RELOC_MICROMIPS_10_PCREL_S1:
+    case BFD_RELOC_MICROMIPS_16_PCREL_S1:
+      return TRUE;
+
+    case BFD_RELOC_32_PCREL:
+      return HAVE_64BIT_ADDRESSES;
+
+    default:
+      return FALSE;
+    }
+}
+
+/* Return true if the given relocation might need a matching %lo().
+   This is only "might" because SVR4 R_MIPS_GOT16 relocations only
+   need a matching %lo() when applied to local symbols.  */
+
+static inline bfd_boolean
+reloc_needs_lo_p (bfd_reloc_code_real_type reloc)
+{
+  return (HAVE_IN_PLACE_ADDENDS
+         && (hi16_reloc_p (reloc)
+             /* VxWorks R_MIPS_GOT16 relocs never need a matching %lo();
+                all GOT16 relocations evaluate to "G".  */
+             || (got16_reloc_p (reloc) && mips_pic != VXWORKS_PIC)));
+}
+
+/* Return the type of %lo() reloc needed by RELOC, given that
+   reloc_needs_lo_p.  */
+
+static inline bfd_reloc_code_real_type
+matching_lo_reloc (bfd_reloc_code_real_type reloc)
+{
+  return (mips16_reloc_p (reloc) ? BFD_RELOC_MIPS16_LO16
+         : (micromips_reloc_p (reloc) ? BFD_RELOC_MICROMIPS_LO16
+            : BFD_RELOC_LO16));
 }
 
 /* Return true if the given fixup is followed by a matching R_MIPS_LO16
@@ -2231,78 +2834,6 @@ fixup_has_matching_lo_p (fixS *fixp)
          && fixp->fx_offset == fixp->fx_next->fx_offset);
 }
 
-/* See whether instruction IP reads register REG.  CLASS is the type
-   of register.  */
-
-static int
-insn_uses_reg (const struct mips_cl_insn *ip, unsigned int reg,
-              enum mips_regclass regclass)
-{
-  if (regclass == MIPS16_REG)
-    {
-      gas_assert (mips_opts.mips16);
-      reg = mips16_to_32_reg_map[reg];
-      regclass = MIPS_GR_REG;
-    }
-
-  /* Don't report on general register ZERO, since it never changes.  */
-  if (regclass == MIPS_GR_REG && reg == ZERO)
-    return 0;
-
-  if (regclass == MIPS_FP_REG)
-    {
-      gas_assert (! mips_opts.mips16);
-      /* If we are called with either $f0 or $f1, we must check $f0.
-        This is not optimal, because it will introduce an unnecessary
-        NOP between "lwc1 $f0" and "swc1 $f1".  To fix this we would
-        need to distinguish reading both $f0 and $f1 or just one of
-        them.  Note that we don't have to check the other way,
-        because there is no instruction that sets both $f0 and $f1
-        and requires a delay.  */
-      if ((ip->insn_mo->pinfo & INSN_READ_FPR_S)
-         && ((EXTRACT_OPERAND (FS, *ip) & ~(unsigned) 1)
-             == (reg &~ (unsigned) 1)))
-       return 1;
-      if ((ip->insn_mo->pinfo & INSN_READ_FPR_T)
-         && ((EXTRACT_OPERAND (FT, *ip) & ~(unsigned) 1)
-             == (reg &~ (unsigned) 1)))
-       return 1;
-    }
-  else if (! mips_opts.mips16)
-    {
-      if ((ip->insn_mo->pinfo & INSN_READ_GPR_S)
-         && EXTRACT_OPERAND (RS, *ip) == reg)
-       return 1;
-      if ((ip->insn_mo->pinfo & INSN_READ_GPR_T)
-         && EXTRACT_OPERAND (RT, *ip) == reg)
-       return 1;
-    }
-  else
-    {
-      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_X)
-         && mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, *ip)] == reg)
-       return 1;
-      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Y)
-         && mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RY, *ip)] == reg)
-       return 1;
-      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Z)
-         && (mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip)]
-             == reg))
-       return 1;
-      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_T) && reg == TREG)
-       return 1;
-      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_SP) && reg == SP)
-       return 1;
-      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_31) && reg == RA)
-       return 1;
-      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_GPR_X)
-         && MIPS16_EXTRACT_OPERAND (REGR32, *ip) == reg)
-       return 1;
-    }
-
-  return 0;
-}
-
 /* This function returns true if modifying a register requires a
    delay.  */
 
@@ -2322,34 +2853,43 @@ reg_needs_delay (unsigned int reg)
         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 (RT, history[0]))
+      if (reg == EXTRACT_OPERAND (mips_opts.micromips, RT, history[0]))
        return 1;
     }
 
   return 0;
 }
 
-/* Move all labels in insn_labels to the current insertion point.  */
+/* Move all labels in LABELS to the current insertion point.  TEXT_P
+   says whether the labels refer to text or data.  */
 
 static void
-mips_move_labels (void)
+mips_move_labels (struct insn_label_list *labels, bfd_boolean text_p)
 {
-  segment_info_type *si = seg_info (now_seg);
   struct insn_label_list *l;
   valueT val;
 
-  for (l = si->label_list; l != NULL; l = l->next)
+  for (l = labels; l != NULL; l = l->next)
     {
       gas_assert (S_GET_SEGMENT (l->label) == now_seg);
       symbol_set_frag (l->label, frag_now);
       val = (valueT) frag_now_fix ();
-      /* mips16 text labels are stored as odd.  */
-      if (mips_opts.mips16)
+      /* MIPS16/microMIPS text labels are stored as odd.  */
+      if (text_p && HAVE_CODE_COMPRESSION)
        ++val;
       S_SET_VALUE (l->label, val);
     }
 }
 
+/* Move all labels in insn_labels to the current insertion point
+   and treat them as text labels.  */
+
+static void
+mips_move_text_labels (void)
+{
+  mips_move_labels (seg_info (now_seg)->label_list, TRUE);
+}
+
 static bfd_boolean
 s_is_linkonce (symbolS *sym, segT from_seg)
 {
@@ -2372,41 +2912,48 @@ s_is_linkonce (symbolS *sym, segT from_seg)
   return linkonce;
 }
 
-/* Mark instruction labels in mips16 mode.  This permits the linker to
-   handle them specially, such as generating jalx instructions when
-   needed.  We also make them odd for the duration of the assembly, in
-   order to generate the right sort of code.  We will make them even
+/* Mark MIPS16 or microMIPS instruction label LABEL.  This permits the
+   linker to handle them specially, such as generating jalx instructions
+   when needed.  We also make them odd for the duration of the assembly,
+   in order to generate the right sort of code.  We will make them even
    in the adjust_symtab routine, while leaving them marked.  This is
    convenient for the debugger and the disassembler.  The linker knows
    to make them odd again.  */
 
 static void
-mips16_mark_labels (void)
+mips_compressed_mark_label (symbolS *label)
 {
-  segment_info_type *si = seg_info (now_seg);
-  struct insn_label_list *l;
-
-  if (!mips_opts.mips16)
-    return;
-
-  for (l = si->label_list; l != NULL; l = l->next)
-   {
-      symbolS *label = l->label;
+  gas_assert (HAVE_CODE_COMPRESSION);
 
 #if defined(OBJ_ELF) || defined(OBJ_MAYBE_ELF)
-      if (IS_ELF)
+  if (IS_ELF)
+    {
+      if (mips_opts.mips16)
        S_SET_OTHER (label, ELF_ST_SET_MIPS16 (S_GET_OTHER (label)));
-#endif
-      if ((S_GET_VALUE (label) & 1) == 0
-       /* Don't adjust the address if the label is global or weak, or
-          in a link-once section, since we'll be emitting symbol reloc
-          references to it which will be patched up by the linker, and
-          the final value of the symbol may or may not be MIPS16.  */
-         && ! S_IS_WEAK (label)
-         && ! S_IS_EXTERNAL (label)
-         && ! s_is_linkonce (label, now_seg))
-       S_SET_VALUE (label, S_GET_VALUE (label) | 1);
+      else
+       S_SET_OTHER (label, ELF_ST_SET_MICROMIPS (S_GET_OTHER (label)));
     }
+#endif
+  if ((S_GET_VALUE (label) & 1) == 0
+      /* Don't adjust the address if the label is global or weak, or
+        in a link-once section, since we'll be emitting symbol reloc
+        references to it which will be patched up by the linker, and
+        the final value of the symbol may or may not be MIPS16/microMIPS.  */
+      && !S_IS_WEAK (label)
+      && !S_IS_EXTERNAL (label)
+      && !s_is_linkonce (label, now_seg))
+    S_SET_VALUE (label, S_GET_VALUE (label) | 1);
+}
+
+/* Mark preceding MIPS16 or microMIPS instruction labels.  */
+
+static void
+mips_compressed_mark_labels (void)
+{
+  struct insn_label_list *l;
+
+  for (l = seg_info (now_seg)->label_list; l != NULL; l = l->next)
+    mips_compressed_mark_label (l->label);
 }
 
 /* End the current frag.  Make it a variant frag and record the
@@ -2455,6 +3002,269 @@ relax_end (void)
   mips_relax.sequence = 0;
 }
 
+/* Return true if IP is a delayed branch or jump.  */
+
+static inline bfd_boolean
+delayed_branch_p (const struct mips_cl_insn *ip)
+{
+  return (ip->insn_mo->pinfo & (INSN_UNCOND_BRANCH_DELAY
+                               | INSN_COND_BRANCH_DELAY
+                               | INSN_COND_BRANCH_LIKELY)) != 0;
+}
+
+/* Return true if IP is a compact branch or jump.  */
+
+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 true if IP is an unconditional branch or jump.  */
+
+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));
+}
+
+/* Return true if IP is a branch-likely instruction.  */
+
+static inline bfd_boolean
+branch_likely_p (const struct mips_cl_insn *ip)
+{
+  return (ip->insn_mo->pinfo & INSN_COND_BRANCH_LIKELY) != 0;
+}
+
+/* Return the type of nop that should be used to fill the delay slot
+   of delayed branch IP.  */
+
+static struct mips_cl_insn *
+get_delay_slot_nop (const struct mips_cl_insn *ip)
+{
+  if (mips_opts.micromips
+      && (ip->insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
+    return &micromips_nop32_insn;
+  return NOP_INSN;
+}
+
+/* Return the mask of core registers that IP reads or writes.  */
+
+static unsigned int
+gpr_mod_mask (const struct mips_cl_insn *ip)
+{
+  unsigned long pinfo2;
+  unsigned int mask;
+
+  mask = 0;
+  pinfo2 = ip->insn_mo->pinfo2;
+  if (mips_opts.micromips)
+    {
+      if (pinfo2 & INSN2_MOD_GPR_MD)
+       mask |= 1 << micromips_to_32_reg_d_map[EXTRACT_OPERAND (1, MD, *ip)];
+      if (pinfo2 & INSN2_MOD_GPR_MF)
+       mask |= 1 << micromips_to_32_reg_f_map[EXTRACT_OPERAND (1, MF, *ip)];
+      if (pinfo2 & INSN2_MOD_SP)
+       mask |= 1 << SP;
+    }
+  return mask;
+}
+
+/* Return the mask of core registers that IP reads.  */
+
+static unsigned int
+gpr_read_mask (const struct mips_cl_insn *ip)
+{
+  unsigned long pinfo, pinfo2;
+  unsigned int mask;
+
+  mask = gpr_mod_mask (ip);
+  pinfo = ip->insn_mo->pinfo;
+  pinfo2 = ip->insn_mo->pinfo2;
+  if (mips_opts.mips16)
+    {
+      if (pinfo & MIPS16_INSN_READ_X)
+       mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, *ip)];
+      if (pinfo & MIPS16_INSN_READ_Y)
+       mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RY, *ip)];
+      if (pinfo & MIPS16_INSN_READ_T)
+       mask |= 1 << TREG;
+      if (pinfo & MIPS16_INSN_READ_SP)
+       mask |= 1 << SP;
+      if (pinfo & MIPS16_INSN_READ_31)
+       mask |= 1 << RA;
+      if (pinfo & MIPS16_INSN_READ_Z)
+       mask |= 1 << (mips16_to_32_reg_map
+                     [MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip)]);
+      if (pinfo & MIPS16_INSN_READ_GPR_X)
+       mask |= 1 << MIPS16_EXTRACT_OPERAND (REGR32, *ip);
+    }
+  else
+    {
+      if (pinfo2 & INSN2_READ_GPR_D)
+       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RD, *ip);
+      if (pinfo & INSN_READ_GPR_T)
+       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RT, *ip);
+      if (pinfo & INSN_READ_GPR_S)
+       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RS, *ip);
+      if (pinfo2 & INSN2_READ_GP)
+       mask |= 1 << GP;
+      if (pinfo2 & INSN2_READ_GPR_31)
+       mask |= 1 << RA;
+      if (pinfo2 & INSN2_READ_GPR_Z)
+       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RZ, *ip);
+    }
+  if (mips_opts.micromips)
+    {
+      if (pinfo2 & INSN2_READ_GPR_MC)
+       mask |= 1 << micromips_to_32_reg_c_map[EXTRACT_OPERAND (1, MC, *ip)];
+      if (pinfo2 & INSN2_READ_GPR_ME)
+       mask |= 1 << micromips_to_32_reg_e_map[EXTRACT_OPERAND (1, ME, *ip)];
+      if (pinfo2 & INSN2_READ_GPR_MG)
+       mask |= 1 << micromips_to_32_reg_g_map[EXTRACT_OPERAND (1, MG, *ip)];
+      if (pinfo2 & INSN2_READ_GPR_MJ)
+       mask |= 1 << EXTRACT_OPERAND (1, MJ, *ip);
+      if (pinfo2 & INSN2_READ_GPR_MMN)
+       {
+         mask |= 1 << micromips_to_32_reg_m_map[EXTRACT_OPERAND (1, MM, *ip)];
+         mask |= 1 << micromips_to_32_reg_n_map[EXTRACT_OPERAND (1, MN, *ip)];
+       }
+      if (pinfo2 & INSN2_READ_GPR_MP)
+       mask |= 1 << EXTRACT_OPERAND (1, MP, *ip);
+      if (pinfo2 & INSN2_READ_GPR_MQ)
+       mask |= 1 << micromips_to_32_reg_q_map[EXTRACT_OPERAND (1, MQ, *ip)];
+    }
+  /* Don't include register 0.  */
+  return mask & ~1;
+}
+
+/* Return the mask of core registers that IP writes.  */
+
+static unsigned int
+gpr_write_mask (const struct mips_cl_insn *ip)
+{
+  unsigned long pinfo, pinfo2;
+  unsigned int mask;
+
+  mask = gpr_mod_mask (ip);
+  pinfo = ip->insn_mo->pinfo;
+  pinfo2 = ip->insn_mo->pinfo2;
+  if (mips_opts.mips16)
+    {
+      if (pinfo & MIPS16_INSN_WRITE_X)
+       mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, *ip)];
+      if (pinfo & MIPS16_INSN_WRITE_Y)
+       mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RY, *ip)];
+      if (pinfo & MIPS16_INSN_WRITE_Z)
+       mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RZ, *ip)];
+      if (pinfo & MIPS16_INSN_WRITE_T)
+       mask |= 1 << TREG;
+      if (pinfo & MIPS16_INSN_WRITE_SP)
+       mask |= 1 << SP;
+      if (pinfo & MIPS16_INSN_WRITE_31)
+       mask |= 1 << RA;
+      if (pinfo & MIPS16_INSN_WRITE_GPR_Y)
+       mask |= 1 << MIPS16OP_EXTRACT_REG32R (ip->insn_opcode);
+    }
+  else
+    {
+      if (pinfo & INSN_WRITE_GPR_D)
+       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RD, *ip);
+      if (pinfo & INSN_WRITE_GPR_T)
+       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RT, *ip);
+      if (pinfo & INSN_WRITE_GPR_S)
+       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RS, *ip);
+      if (pinfo & INSN_WRITE_GPR_31)
+       mask |= 1 << RA;
+      if (pinfo2 & INSN2_WRITE_GPR_Z)
+       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RZ, *ip);
+    }
+  if (mips_opts.micromips)
+    {
+      if (pinfo2 & INSN2_WRITE_GPR_MB)
+       mask |= 1 << micromips_to_32_reg_b_map[EXTRACT_OPERAND (1, MB, *ip)];
+      if (pinfo2 & INSN2_WRITE_GPR_MHI)
+       {
+         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)];
+       }
+      if (pinfo2 & INSN2_WRITE_GPR_MJ)
+       mask |= 1 << EXTRACT_OPERAND (1, MJ, *ip);
+      if (pinfo2 & INSN2_WRITE_GPR_MP)
+       mask |= 1 << EXTRACT_OPERAND (1, MP, *ip);
+    }
+  /* Don't include register 0.  */
+  return mask & ~1;
+}
+
+/* Return the mask of floating-point registers that IP reads.  */
+
+static unsigned int
+fpr_read_mask (const struct mips_cl_insn *ip)
+{
+  unsigned long pinfo, pinfo2;
+  unsigned int mask;
+
+  mask = 0;
+  pinfo = ip->insn_mo->pinfo;
+  pinfo2 = ip->insn_mo->pinfo2;
+  if (!mips_opts.mips16)
+    {
+      if (pinfo2 & INSN2_READ_FPR_D)
+       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FD, *ip);
+      if (pinfo & INSN_READ_FPR_S)
+       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FS, *ip);
+      if (pinfo & INSN_READ_FPR_T)
+       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FT, *ip);
+      if (pinfo & INSN_READ_FPR_R)
+       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FR, *ip);
+      if (pinfo2 & INSN2_READ_FPR_Z)
+       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FZ, *ip);
+    }
+  /* Conservatively treat all operands to an FP_D instruction are doubles.
+     (This is overly pessimistic for things like cvt.d.s.)  */
+  if (HAVE_32BIT_FPRS && (pinfo & FP_D))
+    mask |= mask << 1;
+  return mask;
+}
+
+/* Return the mask of floating-point registers that IP writes.  */
+
+static unsigned int
+fpr_write_mask (const struct mips_cl_insn *ip)
+{
+  unsigned long pinfo, pinfo2;
+  unsigned int mask;
+
+  mask = 0;
+  pinfo = ip->insn_mo->pinfo;
+  pinfo2 = ip->insn_mo->pinfo2;
+  if (!mips_opts.mips16)
+    {
+      if (pinfo & INSN_WRITE_FPR_D)
+       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FD, *ip);
+      if (pinfo & INSN_WRITE_FPR_S)
+       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FS, *ip);
+      if (pinfo & INSN_WRITE_FPR_T)
+       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FT, *ip);
+      if (pinfo2 & INSN2_WRITE_FPR_Z)
+       mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FZ, *ip);
+    }
+  /* Conservatively treat all operands to an FP_D instruction are doubles.
+     (This is overly pessimistic for things like cvt.s.d.)  */
+  if (HAVE_32BIT_FPRS && (pinfo & FP_D))
+    mask |= mask << 1;
+  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.  */
@@ -2489,16 +3299,17 @@ 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_REG.  If INSN2 is null, PINFO2
-     will have every flag set and INSN2_USES_REG will always return true.  */
+     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_REG(REG, CLASS) \
-   (insn2 == NULL || insn_uses_reg (insn2, REG, CLASS))
+#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.  */
@@ -2513,13 +3324,14 @@ insns_between (const struct mips_cl_insn *insn1,
   /* 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_REG (EXTRACT_OPERAND (RD, *insn1), MIPS_GR_REG))
+      && 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)
+  if (mips_fix_24k && !mips_opts.micromips)
     {
       if (insn1->insn_opcode == INSN_ERET
          || insn1->insn_opcode == INSN_DERET)
@@ -2527,17 +3339,14 @@ insns_between (const struct mips_cl_insn *insn1,
          if (insn2 == NULL
              || insn2->insn_opcode == INSN_ERET
              || insn2->insn_opcode == INSN_DERET
-             || (insn2->insn_mo->pinfo
-                 & (INSN_UNCOND_BRANCH_DELAY
-                    | INSN_COND_BRANCH_DELAY
-                    | INSN_COND_BRANCH_LIKELY)) != 0)
+             || delayed_branch_p (insn2))
            return 1;
        }
     }
 
   /* If working around VR4120 errata, check for combinations that need
      a single intervening instruction.  */
-  if (mips_fix_vr4120)
+  if (mips_fix_vr4120 && !mips_opts.micromips)
     {
       unsigned int class1, class2;
 
@@ -2552,7 +3361,7 @@ insns_between (const struct mips_cl_insn *insn1,
        }
     }
 
-  if (!mips_opts.mips16)
+  if (!HAVE_CODE_COMPRESSION)
     {
       /* Check for GPR or coprocessor load delays.  All such delays
         are on the RT register.  */
@@ -2561,7 +3370,7 @@ insns_between (const struct mips_cl_insn *insn1,
          || (!cop_interlocks && (pinfo1 & INSN_LOAD_COPROC_DELAY)))
        {
          know (pinfo1 & INSN_WRITE_GPR_T);
-         if (INSN2_USES_REG (EXTRACT_OPERAND (RT, *insn1), MIPS_GR_REG))
+         if (INSN2_USES_GPR (EXTRACT_OPERAND (0, RT, *insn1)))
            return 1;
        }
 
@@ -2579,14 +3388,10 @@ insns_between (const struct mips_cl_insn *insn1,
          /* 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.  */
-         if (pinfo1 & INSN_WRITE_FPR_T)
-           {
-             if (INSN2_USES_REG (EXTRACT_OPERAND (FT, *insn1), MIPS_FP_REG))
-               return 1;
-           }
-         else if (pinfo1 & INSN_WRITE_FPR_S)
+         mask = fpr_write_mask (insn1);
+         if (mask != 0)
            {
-             if (INSN2_USES_REG (EXTRACT_OPERAND (FS, *insn1), MIPS_FP_REG))
+             if (!insn2 || (mask & fpr_read_mask (insn2)) != 0)
                return 1;
            }
          else
@@ -2616,20 +3421,22 @@ insns_between (const struct mips_cl_insn *insn1,
        return 1;
     }
 
-#undef INSN2_USES_REG
+#undef INSN2_USES_GPR
 
   return 0;
 }
 
 /* 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.  */
+   the MAX_VR4130_NOPS instructions described by HIST.  Ignore hazards
+   that are contained within the first IGNORE instructions of HIST.  */
 
 static int
-nops_for_vr4130 (const struct mips_cl_insn *hist,
+nops_for_vr4130 (int ignore, const struct mips_cl_insn *hist,
                 const struct mips_cl_insn *insn)
 {
-  int i, j, reg;
+  int i, j;
+  unsigned int mask;
 
   /* Check if the instruction writes to HI or LO.  MTHI and MTLO
      are not affected by the errata.  */
@@ -2644,121 +3451,318 @@ nops_for_vr4130 (const struct mips_cl_insn *hist,
     if (MF_HILO_INSN (hist[i].insn_mo->pinfo))
       {
        /* Extract the destination register.  */
-       if (mips_opts.mips16)
-         reg = mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, hist[i])];
-       else
-         reg = EXTRACT_OPERAND (RD, hist[i]);
+       mask = gpr_write_mask (&hist[i]);
 
        /* No nops are needed if INSN reads that register.  */
-       if (insn != NULL && insn_uses_reg (insn, reg, MIPS_GR_REG))
+       if (insn != NULL && (gpr_read_mask (insn) & mask) != 0)
          return 0;
 
        /* ...or if any of the intervening instructions do.  */
        for (j = 0; j < i; j++)
-         if (insn_uses_reg (&hist[j], reg, MIPS_GR_REG))
+         if (gpr_read_mask (&hist[j]) & mask)
            return 0;
 
-       return MAX_VR4130_NOPS - i;
+       if (i >= ignore)
+         return MAX_VR4130_NOPS - i;
       }
   return 0;
 }
 
-/* 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.  If INSN is null,
-   return the worse-case number of nops for any instruction.  */
+#define BASE_REG_EQ(INSN1, INSN2)      \
+  ((((INSN1) >> OP_SH_RS) & OP_MASK_RS) \
+      == (((INSN2) >> OP_SH_RS) & OP_MASK_RS))
+
+/* Return the minimum alignment for this store instruction.  */
 
 static int
-nops_for_insn (const struct mips_cl_insn *hist,
-              const struct mips_cl_insn *insn)
+fix_24k_align_to (const struct mips_opcode *mo)
 {
-  int i, nops, tmp_nops;
+  if (strcmp (mo->name, "sh") == 0)
+    return 2;
 
-  nops = 0;
-  for (i = 0; i < MAX_DELAY_NOPS; i++)
-    {
-      tmp_nops = insns_between (hist + i, insn) - i;
-      if (tmp_nops > nops)
-       nops = tmp_nops;
-    }
+  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_fix_vr4130)
-    {
-      tmp_nops = nops_for_vr4130 (hist, insn);
-      if (tmp_nops > nops)
-       nops = tmp_nops;
-    }
+  if (strcmp (mo->name, "sdc1") == 0
+      || strcmp (mo->name, "sdc2") == 0
+      || strcmp (mo->name, "s.d") == 0)
+    return 8;
 
-  return nops;
+  /* sb, swl, swr */
+  return 1;
 }
 
-/* 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.  */
+struct fix_24k_store_info
+  {
+    /* Immediate offset, if any, for this store instruction.  */
+    short off;
+    /* Alignment required by this store instruction.  */
+    int align_to;
+    /* True for register offsets.  */
+    int register_offset;
+  };
+
+/* Comparison function used by qsort.  */
 
 static int
-nops_for_sequence (int num_insns, const struct mips_cl_insn *hist, ...)
+fix_24k_sort (const void *a, const void *b)
 {
-  va_list args;
-  struct mips_cl_insn buffer[MAX_NOPS];
-  struct mips_cl_insn *cursor;
-  int nops;
-
-  va_start (args, hist);
-  cursor = buffer + num_insns;
-  memcpy (cursor, hist, (MAX_NOPS - num_insns) * sizeof (*cursor));
-  while (cursor > buffer)
-    *--cursor = *va_arg (args, const struct mips_cl_insn *);
+  const struct fix_24k_store_info *pos1 = a;
+  const struct fix_24k_store_info *pos2 = b;
 
-  nops = nops_for_insn (buffer, NULL);
-  va_end (args);
-  return nops;
+  return (pos1->off - pos2->off);
 }
 
-/* Like nops_for_insn, but if INSN is a branch, take into account the
-   worst-case delay for the branch target.  */
+/* INSN is a store instruction.  Try to record the store information
+   in STINFO.  Return false if the information isn't known.  */
 
-static int
-nops_for_insn_or_target (const struct mips_cl_insn *hist,
-                        const struct mips_cl_insn *insn)
+static bfd_boolean
+fix_24k_record_store_info (struct fix_24k_store_info *stinfo,
+                          const struct mips_cl_insn *insn)
 {
-  int nops, tmp_nops;
+  /* The instruction must have a known offset.  */
+  if (!insn->complete_p || !strstr (insn->insn_mo->args, "o("))
+    return FALSE;
 
-  nops = nops_for_insn (hist, insn);
-  if (insn->insn_mo->pinfo & (INSN_UNCOND_BRANCH_DELAY
-                             | INSN_COND_BRANCH_DELAY
-                             | INSN_COND_BRANCH_LIKELY))
-    {
-      tmp_nops = nops_for_sequence (2, hist, insn, NOP_INSN);
-      if (tmp_nops > nops)
-       nops = tmp_nops;
-    }
-  else if (mips_opts.mips16
-          && (insn->insn_mo->pinfo & (MIPS16_INSN_UNCOND_BRANCH
-                                      | MIPS16_INSN_COND_BRANCH)))
-    {
-      tmp_nops = nops_for_sequence (1, hist, insn);
-      if (tmp_nops > nops)
-       nops = tmp_nops;
-    }
-  return nops;
+  stinfo->off = (insn->insn_opcode >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE;
+  stinfo->align_to = fix_24k_align_to (insn->insn_mo);
+  return TRUE;
 }
 
-/* Fix NOP issue: Replace nops by "or at,at,zero".  */
-
-static void
-fix_loongson2f_nop (struct mips_cl_insn * ip)
+/* 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.
+
+   * 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
+
+   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.
+
+   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.
+
+   The workarounds are:
+
+   * 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)
 {
-  if (strcmp (ip->insn_mo->name, "nop") == 0)
-    ip->insn_opcode = LOONGSON2F_NOP_INSN;
-}
+  struct fix_24k_store_info pos[3];
+  int align, i, base_offset;
 
-/* Fix Jump Issue: Eliminate instruction fetch from outside 256M region
-                   jr target pc &= 'hffff_ffff_cfff_ffff.  */
+  if (ignore >= 2)
+    return 0;
+
+  /* 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 the instructions after the previous one are unknown, we have
+     to assume the worst.  */
+  if (!insn)
+    return 1;
+
+  /* Check whether we are dealing with three consecutive stores.  */
+  if ((insn->insn_mo->pinfo & INSN_STORE_MEMORY) == 0
+      || (hist[1].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
+    return 0;
+
+  /* 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;
+
+  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
+    {
+      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;
+    }
+
+  pos[0].off &= ~align + 1;
+  pos[1].off &= ~align + 1;
+  pos[2].off &= ~align + 1;
+
+  /* If any two stores write to the same chunk, they also write to the
+     same doubleword.  The offsets are still sorted at this point.  */
+  if (pos[0].off == pos[1].off || pos[1].off == pos[2].off)
+    return 0;
+
+  /* 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;
+}
+
+/* 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.
+
+   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)
+{
+  int i, nops, tmp_nops;
+
+  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;
+    }
+
+  if (mips_fix_vr4130 && !mips_opts.micromips)
+    {
+      tmp_nops = nops_for_vr4130 (ignore, hist, insn);
+      if (tmp_nops > nops)
+       nops = tmp_nops;
+    }
+
+  if (mips_fix_24k && !mips_opts.micromips)
+    {
+      tmp_nops = nops_for_24k (ignore, hist, insn);
+      if (tmp_nops > nops)
+       nops = tmp_nops;
+    }
+
+  return nops;
+}
+
+/* The variable arguments provide NUM_INSNS extra instructions that
+   might be added to HIST.  Return the largest number of nops that
+   would be needed after the extended sequence, ignoring hazards
+   in the first IGNORE instructions.  */
+
+static 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;
+
+  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;
+}
+
+/* Like nops_for_insn, but if INSN is a branch, take into account the
+   worst-case delay for the branch target.  */
+
+static int
+nops_for_insn_or_target (int ignore, const struct mips_cl_insn *hist,
+                        const struct mips_cl_insn *insn)
+{
+  int nops, tmp_nops;
+
+  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;
+}
+
+/* Fix NOP issue: Replace nops by "or at,at,zero".  */
+
+static void
+fix_loongson2f_nop (struct mips_cl_insn * ip)
+{
+  gas_assert (!HAVE_CODE_COMPRESSION);
+  if (strcmp (ip->insn_mo->name, "nop") == 0)
+    ip->insn_opcode = LOONGSON2F_NOP_INSN;
+}
+
+/* Fix Jump Issue: Eliminate instruction fetch from outside 256M region
+                   jr target pc &= 'hffff_ffff_cfff_ffff.  */
 
 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)
@@ -2769,7 +3773,7 @@ fix_loongson2f_jump (struct mips_cl_insn * ip)
       if (! mips_opts.at)
         return;
 
-      sreg = EXTRACT_OPERAND (RS, *ip);
+      sreg = EXTRACT_OPERAND (0, RS, *ip);
       if (sreg == ZERO || sreg == KT0 || sreg == KT1 || sreg == ATREG)
         return;
 
@@ -2792,3452 +3796,4193 @@ fix_loongson2f (struct mips_cl_insn * ip)
     fix_loongson2f_jump (ip);
 }
 
-/* Output an instruction.  IP is the instruction information.
+/* 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
-append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
-            bfd_reloc_code_real_type *reloc_type)
+static bfd_boolean
+can_swap_branch_p (struct mips_cl_insn *ip, expressionS *address_expr,
+  bfd_reloc_code_real_type *reloc_type)
 {
-  unsigned long prev_pinfo, pinfo;
-  relax_stateT prev_insn_frag_type = 0;
-  bfd_boolean relaxed_branch = FALSE;
-  segment_info_type *si = seg_info (now_seg);
+  unsigned long pinfo, pinfo2, prev_pinfo, prev_pinfo2;
+  unsigned int gpr_read, gpr_write, prev_gpr_read, prev_gpr_write;
 
-  if (mips_fix_loongson2f)
-    fix_loongson2f (ip);
+  /* -O2 and above is required for this optimization.  */
+  if (mips_optimize < 2)
+    return FALSE;
 
-  /* Mark instruction labels in mips16 mode.  */
-  mips16_mark_labels ();
+  /* If we have seen .set volatile or .set nomove, don't optimize.  */
+  if (mips_opts.nomove)
+    return FALSE;
 
-  prev_pinfo = history[0].insn_mo->pinfo;
-  pinfo = ip->insn_mo->pinfo;
+  /* We can't swap if the previous instruction's position is fixed.  */
+  if (history[0].fixed_p)
+    return FALSE;
 
-  if (mips_relax.sequence != 2 && !mips_opts.noreorder)
-    {
-      /* There are a lot of optimizations we could do that we don't.
-        In particular, we do not, in general, reorder instructions.
-        If you use gcc with optimization, it will reorder
-        instructions and generally do much more optimization then we
-        do here; repeating all that work in the assembler would only
-        benefit hand written assembly code, and does not seem worth
-        it.  */
-      int nops = (mips_optimize == 0
-                 ? nops_for_insn (history, NULL)
-                 : nops_for_insn_or_target (history, ip));
-      if (nops > 0)
-       {
-         fragS *old_frag;
-         unsigned long old_frag_offset;
-         int i;
+  /* 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
 
-         old_frag = frag_now;
-         old_frag_offset = frag_now_fix ();
+       .set    noreorder
+       lw      $4,XXX
+       .set    reorder
+       INSN
+       bne     $4,$0,foo
 
-         for (i = 0; i < nops; i++)
-           emit_nop ();
+     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;
 
-         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);
-           }
+  /* 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;
 
-         mips_move_labels ();
+  /* 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;
 
-#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)
-    {
-      /* Work out how many nops in prev_nop_frag are needed by IP.  */
-      int nops = nops_for_insn_or_target (history, ip);
-      gas_assert (nops <= prev_nop_frag_holds);
+  /* 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;
 
-      /* Enforce NOPS as a minimum.  */
-      if (nops > prev_nop_frag_required)
-       prev_nop_frag_required = nops;
+  /* 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;
 
-      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 -= mips_opts.mips16 ? 2 : 4;
-         prev_nop_frag_holds--;
-         prev_nop_frag_since++;
-       }
-    }
+  /* 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;
 
-#ifdef OBJ_ELF
-  /* The value passed to dwarf2_emit_insn is the distance between
-     the beginning of the current instruction and the address that
-     should be recorded in the debug tables.  For MIPS16 debug info
-     we want to use ISA-encoded addresses, so we pass -1 for an
-     address higher by one than the current.  */
-  dwarf2_emit_insn (mips_opts.mips16 ? -1 : 0);
-#endif
+  /* 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;
 
-  /* Record the frag type before frag_var.  */
-  if (history[0].frag)
-    prev_insn_frag_type = history[0].frag->fr_type;
+  /* If the branch reads a register that the previous
+     instruction sets, we can not swap.  */
+  gpr_read = gpr_read_mask (ip);
+  prev_gpr_write = gpr_write_mask (&history[0]);
+  if (gpr_read & prev_gpr_write)
+    return FALSE;
 
-  if (address_expr
-      && *reloc_type == BFD_RELOC_16_PCREL_S2
-      && (pinfo & INSN_UNCOND_BRANCH_DELAY || pinfo & INSN_COND_BRANCH_DELAY
-         || pinfo & INSN_COND_BRANCH_LIKELY)
-      && 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)
-      && !mips_opts.mips16)
-    {
-      relaxed_branch = TRUE;
-      add_relaxed_insn (ip, (relaxed_branch_length
-                            (NULL, NULL,
-                             (pinfo & INSN_UNCOND_BRANCH_DELAY) ? -1
-                             : (pinfo & INSN_COND_BRANCH_LIKELY) ? 1
-                             : 0)), 4,
-                       RELAX_BRANCH_ENCODE
-                       (pinfo & INSN_UNCOND_BRANCH_DELAY,
-                        pinfo & INSN_COND_BRANCH_LIKELY,
-                        pinfo & INSN_WRITE_GPR_31,
-                        0),
-                       address_expr->X_add_symbol,
-                       address_expr->X_add_number);
-      *reloc_type = BFD_RELOC_UNUSED;
-    }
-  else if (*reloc_type > BFD_RELOC_UNUSED)
-    {
-      /* We need to set up a variant frag.  */
-      gas_assert (mips_opts.mips16 && address_expr != NULL);
-      add_relaxed_insn (ip, 4, 0,
-                       RELAX_MIPS16_ENCODE
-                       (*reloc_type - BFD_RELOC_UNUSED,
-                        mips16_small, mips16_ext,
-                        prev_pinfo & INSN_UNCOND_BRANCH_DELAY,
-                        history[0].mips16_absolute_jump_p),
-                       make_expr_symbol (address_expr), 0);
-    }
-  else if (mips_opts.mips16
-          && ! ip->use_extend
-          && *reloc_type != BFD_RELOC_MIPS16_JMP)
-    {
-      if ((pinfo & INSN_UNCOND_BRANCH_DELAY) == 0)
-       /* 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
-         && (prev_pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
-       as_warn (_("extended instruction in delay slot"));
+  /* 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;
 
-      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] += 4;
-       }
+  /* If the branch writes a register that the previous
+     instruction reads, we can not swap.  */
+  prev_gpr_read = gpr_read_mask (&history[0]);
+  if (gpr_write & prev_gpr_read)
+    return FALSE;
 
-      if (mips_relax.sequence != 2)
-       mips_macro_warning.sizes[0] += 4;
-      if (mips_relax.sequence != 1)
-       mips_macro_warning.sizes[1] += 4;
+  /* If one instruction sets a condition code and the
+     other one uses a condition code, we can not swap.  */
+  pinfo = ip->insn_mo->pinfo;
+  if ((pinfo & INSN_READ_COND_CODE)
+      && (prev_pinfo & INSN_WRITE_COND_CODE))
+    return FALSE;
+  if ((pinfo & INSN_WRITE_COND_CODE)
+      && (prev_pinfo & INSN_READ_COND_CODE))
+    return FALSE;
 
-      if (mips_opts.mips16)
-       {
-         ip->fixed_p = 1;
-         ip->mips16_absolute_jump_p = (*reloc_type == BFD_RELOC_MIPS16_JMP);
-       }
-      add_fixed_insn (ip);
-    }
+  /* 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;
+
+  /* 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;
 
-  if (address_expr != NULL && *reloc_type <= BFD_RELOC_UNUSED)
+  /* 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 */
     {
-      if (address_expr->X_op == O_constant)
-       {
-         unsigned int tmp;
+      int distance;
+      /* Check if loop is shorter than 6 instructions including
+         branch and delay slot.  */
+      distance = frag_now_fix() - S_GET_VALUE(address_expr->X_add_symbol);
+      if (distance <= 20)
+        {
+          int i;
+          int rv;
+
+          rv = FALSE;
+          /* When the loop includes branches or jumps,
+             it is not a short loop. */
+          for (i = 0; i < (distance / 4); i++)
+            {
+              if ((history[i].cleared_p)
+                  || delayed_branch_p(&history[i]))
+                {
+                  rv = TRUE;
+                  break;
+                }
+            }
+          if (rv == FALSE)
+            {
+              /* Insert nop after branch to fix short loop. */
+              return FALSE;
+            }
+        }
+    }
 
-         switch (*reloc_type)
-           {
-           case BFD_RELOC_32:
-             ip->insn_opcode |= address_expr->X_add_number;
-             break;
+  return TRUE;
+}
 
-           case BFD_RELOC_MIPS_HIGHEST:
-             tmp = (address_expr->X_add_number + 0x800080008000ull) >> 48;
-             ip->insn_opcode |= tmp & 0xffff;
-             break;
-
-           case BFD_RELOC_MIPS_HIGHER:
-             tmp = (address_expr->X_add_number + 0x80008000ull) >> 32;
-             ip->insn_opcode |= tmp & 0xffff;
-             break;
-
-           case BFD_RELOC_HI16_S:
-             tmp = (address_expr->X_add_number + 0x8000) >> 16;
-             ip->insn_opcode |= tmp & 0xffff;
-             break;
-
-           case BFD_RELOC_HI16:
-             ip->insn_opcode |= (address_expr->X_add_number >> 16) & 0xffff;
-             break;
+/* Decide how we should add IP to the instruction stream.
+   ADDRESS_EXPR is an operand of the instruction to be used with
+   RELOC_TYPE.  */
 
-           case BFD_RELOC_UNUSED:
-           case BFD_RELOC_LO16:
-           case BFD_RELOC_MIPS_GOT_DISP:
-             ip->insn_opcode |= address_expr->X_add_number & 0xffff;
-             break;
+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;
 
-           case BFD_RELOC_MIPS_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 >> 2) & 0x3ffffff;
-             break;
+  /* The relaxed version of a macro sequence must be inherently
+     hazard-free.  */
+  if (mips_relax.sequence == 2)
+    return APPEND_ADD;
 
-           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));
-             break;
+  /* We must not dabble with instructions in a ".set norerorder" block.  */
+  if (mips_opts.noreorder)
+    return APPEND_ADD;
 
-           case BFD_RELOC_16_PCREL_S2:
-             if ((address_expr->X_add_number & 3) != 0)
-               as_bad (_("branch to misaligned address (0x%lx)"),
-                       (unsigned long) address_expr->X_add_number);
-             if (mips_relax_branch)
-               goto need_reloc;
-             if ((address_expr->X_add_number + 0x20000) & ~0x3ffff)
-               as_bad (_("branch address range overflow (0x%lx)"),
-                       (unsigned long) address_expr->X_add_number);
-             ip->insn_opcode |= (address_expr->X_add_number >> 2) & 0xffff;
-             break;
+  /* 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;
 
-           default:
-             internalError ();
-           }
-       }
-      else if (*reloc_type < BFD_RELOC_UNUSED)
-       need_reloc:
-       {
-         reloc_howto_type *howto;
-         int i;
+      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;
 
-         /* In a compound relocation, it is the final (outermost)
-            operator that determines the relocated field.  */
-         for (i = 1; i < 3; i++)
-           if (reloc_type[i] == BFD_RELOC_UNUSED)
-             break;
+      return APPEND_ADD_WITH_NOP;
+    }
 
-         howto = bfd_reloc_type_lookup (stdoutput, reloc_type[i - 1]);
-         if (howto == NULL)
-           {
-             /* To reproduce this failure try assembling gas/testsuites/
-                gas/mips/mips16-intermix.s with a mips-ecoff targeted
-                assembler.  */
-             as_bad (_("Unsupported MIPS relocation number %d"), reloc_type[i - 1]);
-             howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_16);
-           }
-         
-         ip->fixp[0] = fix_new_exp (ip->frag, ip->where,
-                                    bfd_get_reloc_size (howto),
-                                    address_expr,
-                                    reloc_type[0] == BFD_RELOC_16_PCREL_S2,
-                                    reloc_type[0]);
-
-         /* Tag symbols that have a R_MIPS16_26 relocation against them.  */
-         if (reloc_type[0] == BFD_RELOC_MIPS16_JMP
-             && ip->fixp[0]->fx_addsy)
-           *symbol_get_tc (ip->fixp[0]->fx_addsy) = 1;
+  return APPEND_ADD;
+}
 
-         /* 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;
+/* IP is a MIPS16 instruction whose opcode we have just changed.
+   Point IP->insn_mo to the new opcode's definition.  */
 
-         if (mips_relax.sequence)
-           {
-             if (mips_relax.first_fixup == 0)
-               mips_relax.first_fixup = ip->fixp[0];
-           }
-         else if (reloc_needs_lo_p (*reloc_type))
-           {
-             struct mips_hi_fixup *hi_fixup;
+static void
+find_altered_mips16_opcode (struct mips_cl_insn *ip)
+{
+  const struct mips_opcode *mo, *end;
 
-             /* 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;
-           }
+  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 ();
+}
 
-         /* 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, reloc_type[i]);
+/* 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];
 
-               /* Use fx_tcbit to mark compound relocs.  */
-               ip->fixp[0]->fx_tcbit = 1;
-               ip->fixp[i]->fx_tcbit = 1;
-             }
-       }
-    }
-  install_insn (ip);
+static char *
+micromips_label_name (void)
+{
+  char *p = micromips_target_name;
+  char symbol_name_temporary[24];
+  unsigned long l;
+  int i;
 
-  /* Update the register mask information.  */
-  if (! mips_opts.mips16)
-    {
-      if (pinfo & INSN_WRITE_GPR_D)
-       mips_gprmask |= 1 << EXTRACT_OPERAND (RD, *ip);
-      if ((pinfo & (INSN_WRITE_GPR_T | INSN_READ_GPR_T)) != 0)
-       mips_gprmask |= 1 << EXTRACT_OPERAND (RT, *ip);
-      if (pinfo & INSN_READ_GPR_S)
-       mips_gprmask |= 1 << EXTRACT_OPERAND (RS, *ip);
-      if (pinfo & INSN_WRITE_GPR_31)
-       mips_gprmask |= 1 << RA;
-      if (pinfo & INSN_WRITE_FPR_D)
-       mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FD, *ip);
-      if ((pinfo & (INSN_WRITE_FPR_S | INSN_READ_FPR_S)) != 0)
-       mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FS, *ip);
-      if ((pinfo & (INSN_WRITE_FPR_T | INSN_READ_FPR_T)) != 0)
-       mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FT, *ip);
-      if ((pinfo & INSN_READ_FPR_R) != 0)
-       mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FR, *ip);
-      if (pinfo & INSN_COP)
-       {
-         /* We don't keep enough information to sort these cases out.
-            The itbl support does keep this information however, although
-            we currently don't support itbl fprmats as part of the cop
-            instruction.  May want to add this support in the future.  */
-       }
-      /* Never set the bit for $0, which is always zero.  */
-      mips_gprmask &= ~1 << 0;
-    }
-  else
-    {
-      if (pinfo & (MIPS16_INSN_WRITE_X | MIPS16_INSN_READ_X))
-       mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RX, *ip);
-      if (pinfo & (MIPS16_INSN_WRITE_Y | MIPS16_INSN_READ_Y))
-       mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RY, *ip);
-      if (pinfo & MIPS16_INSN_WRITE_Z)
-       mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RZ, *ip);
-      if (pinfo & (MIPS16_INSN_WRITE_T | MIPS16_INSN_READ_T))
-       mips_gprmask |= 1 << TREG;
-      if (pinfo & (MIPS16_INSN_WRITE_SP | MIPS16_INSN_READ_SP))
-       mips_gprmask |= 1 << SP;
-      if (pinfo & (MIPS16_INSN_WRITE_31 | MIPS16_INSN_READ_31))
-       mips_gprmask |= 1 << RA;
-      if (pinfo & MIPS16_INSN_WRITE_GPR_Y)
-       mips_gprmask |= 1 << MIPS16OP_EXTRACT_REG32R (ip->insn_opcode);
-      if (pinfo & MIPS16_INSN_READ_Z)
-       mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip);
-      if (pinfo & MIPS16_INSN_READ_GPR_X)
-       mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (REGR32, *ip);
-    }
+  if (*p)
+    return p;
 
-  if (mips_relax.sequence != 2 && !mips_opts.noreorder)
+  i = 0;
+  l = micromips_target_label;
+#ifdef LOCAL_LABEL_PREFIX
+  *p++ = LOCAL_LABEL_PREFIX;
+#endif
+  *p++ = 'L';
+  *p++ = MICROMIPS_LABEL_CHAR;
+  do
     {
-      /* Filling the branch delay slot is more complex.  We try to
-        switch the branch with the previous instruction, which we can
-        do if the previous instruction does not set up a condition
-        that the branch tests and if the branch is not itself the
-        target of any branch.  */
-      if ((pinfo & INSN_UNCOND_BRANCH_DELAY)
-         || (pinfo & INSN_COND_BRANCH_DELAY))
-       {
-         if (mips_optimize < 2
-             /* If we have seen .set volatile or .set nomove, don't
-                optimize.  */
-             || mips_opts.nomove != 0
-             /* We can't swap if the previous instruction's position
-                is fixed.  */
-             || history[0].fixed_p
-             /* 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
-                  .set noreorder
-                  lw   $4,XXX
-                  .set reorder
-                  INSN
-                  bne  $4,$0,foo
-                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.  */
-             || history[1].noreorder_p
-             /* 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.  */
-             || si->label_list != NULL
-             /* 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 the mips16, which uses variant
-                frags for different purposes.  */
-             || (! mips_opts.mips16
-                 && prev_insn_frag_type == rs_machine_dependent)
-             /* Check for conflicts between the branch and the instructions
-                before the candidate delay slot.  */
-             || nops_for_insn (history + 1, ip) > 0
-             /* Check for conflicts between the swapped sequence and the
-                target of the branch.  */
-             || nops_for_sequence (2, history + 1, ip, history) > 0
-             /* We do not swap with a trap instruction, since it
-                complicates trap handlers to have the trap
-                instruction be in a delay slot.  */
-             || (prev_pinfo & INSN_TRAP)
-             /* If the branch reads a register that the previous
-                instruction sets, we can not swap.  */
-             || (! mips_opts.mips16
-                 && (prev_pinfo & INSN_WRITE_GPR_T)
-                 && insn_uses_reg (ip, EXTRACT_OPERAND (RT, history[0]),
-                                   MIPS_GR_REG))
-             || (! mips_opts.mips16
-                 && (prev_pinfo & INSN_WRITE_GPR_D)
-                 && insn_uses_reg (ip, EXTRACT_OPERAND (RD, history[0]),
-                                   MIPS_GR_REG))
-             || (mips_opts.mips16
-                 && (((prev_pinfo & MIPS16_INSN_WRITE_X)
-                      && (insn_uses_reg
-                          (ip, MIPS16_EXTRACT_OPERAND (RX, history[0]),
-                           MIPS16_REG)))
-                     || ((prev_pinfo & MIPS16_INSN_WRITE_Y)
-                         && (insn_uses_reg
-                             (ip, MIPS16_EXTRACT_OPERAND (RY, history[0]),
-                              MIPS16_REG)))
-                     || ((prev_pinfo & MIPS16_INSN_WRITE_Z)
-                         && (insn_uses_reg
-                             (ip, MIPS16_EXTRACT_OPERAND (RZ, history[0]),
-                              MIPS16_REG)))
-                     || ((prev_pinfo & MIPS16_INSN_WRITE_T)
-                         && insn_uses_reg (ip, TREG, MIPS_GR_REG))
-                     || ((prev_pinfo & MIPS16_INSN_WRITE_31)
-                         && insn_uses_reg (ip, RA, MIPS_GR_REG))
-                     || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y)
-                         && insn_uses_reg (ip,
-                                           MIPS16OP_EXTRACT_REG32R
-                                             (history[0].insn_opcode),
-                                           MIPS_GR_REG))))
-             /* If the branch writes a register that the previous
-                instruction sets, we can not swap (we know that
-                branches write only to RD or to $31).  */
-             || (! mips_opts.mips16
-                 && (prev_pinfo & INSN_WRITE_GPR_T)
-                 && (((pinfo & INSN_WRITE_GPR_D)
-                      && (EXTRACT_OPERAND (RT, history[0])
-                          == EXTRACT_OPERAND (RD, *ip)))
-                     || ((pinfo & INSN_WRITE_GPR_31)
-                         && EXTRACT_OPERAND (RT, history[0]) == RA)))
-             || (! mips_opts.mips16
-                 && (prev_pinfo & INSN_WRITE_GPR_D)
-                 && (((pinfo & INSN_WRITE_GPR_D)
-                      && (EXTRACT_OPERAND (RD, history[0])
-                          == EXTRACT_OPERAND (RD, *ip)))
-                     || ((pinfo & INSN_WRITE_GPR_31)
-                         && EXTRACT_OPERAND (RD, history[0]) == RA)))
-             || (mips_opts.mips16
-                 && (pinfo & MIPS16_INSN_WRITE_31)
-                 && ((prev_pinfo & MIPS16_INSN_WRITE_31)
-                     || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y)
-                         && (MIPS16OP_EXTRACT_REG32R (history[0].insn_opcode)
-                             == RA))))
-             /* If the branch writes a register that the previous
-                instruction reads, we can not swap (we know that
-                branches only write to RD or to $31).  */
-             || (! mips_opts.mips16
-                 && (pinfo & INSN_WRITE_GPR_D)
-                 && insn_uses_reg (&history[0],
-                                   EXTRACT_OPERAND (RD, *ip),
-                                   MIPS_GR_REG))
-             || (! mips_opts.mips16
-                 && (pinfo & INSN_WRITE_GPR_31)
-                 && insn_uses_reg (&history[0], RA, MIPS_GR_REG))
-             || (mips_opts.mips16
-                 && (pinfo & MIPS16_INSN_WRITE_31)
-                 && insn_uses_reg (&history[0], RA, MIPS_GR_REG))
-             /* If one instruction sets a condition code and the
-                 other one uses a condition code, we can not swap.  */
-             || ((pinfo & INSN_READ_COND_CODE)
-                 && (prev_pinfo & INSN_WRITE_COND_CODE))
-             || ((pinfo & INSN_WRITE_COND_CODE)
-                 && (prev_pinfo & INSN_READ_COND_CODE))
-             /* If the previous instruction uses the PC, we can not
-                 swap.  */
-             || (mips_opts.mips16
-                 && (prev_pinfo & MIPS16_INSN_READ_PC))
-             /* If the previous instruction had a fixup in mips16
-                 mode, we can not swap.  This normally means that the
-                 previous instruction was a 4 byte branch anyhow.  */
-             || (mips_opts.mips16 && history[0].fixp[0])
-             /* If the previous instruction is a sync, sync.l, or
-                sync.p, we can not swap.  */
-             || (prev_pinfo & INSN_SYNC)
-             /* If the previous instruction is an ERET or
-                DERET, avoid the swap.  */
-              || (history[0].insn_opcode == INSN_ERET)
-              || (history[0].insn_opcode == INSN_DERET))
-           {
-             if (mips_opts.mips16
-                 && (pinfo & INSN_UNCOND_BRANCH_DELAY)
-                 && (pinfo & (MIPS16_INSN_READ_X | MIPS16_INSN_READ_31))
-                 && ISA_SUPPORTS_MIPS16E)
-               {
-                 /* Convert MIPS16 jr/jalr into a "compact" jump.  */
-                 ip->insn_opcode |= 0x0080;
-                 install_insn (ip);
-                 insert_into_history (0, 1, ip);
-               } 
-             else
-               {
-                 /* We could do even better for unconditional branches to
-                    portions of this object file; we could pick up the
-                    instruction at the destination, put it in the delay
-                    slot, and bump the destination address.  */
-                 insert_into_history (0, 1, ip);
-                 emit_nop ();
-               }
-               
-             if (mips_relax.sequence)
-               mips_relax.sizes[mips_relax.sequence - 1] += 4;
-           }
-         else
-           {
-             /* It looks like we can actually do the 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)
-               {
-                 /* 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 -= 4;
-                 if (delay.frag == ip->frag)
-                   move_insn (ip, ip->frag, ip->where - 4);
-                 add_fixed_insn (&delay);
-               }
-             else
-               {
-                 move_insn (&delay, ip->frag, ip->where);
-                 move_insn (ip, history[0].frag, history[0].where);
-               }
-             history[0] = *ip;
-             delay.fixed_p = 1;
-             insert_into_history (0, 1, &delay);
-           }
-
-         /* If that was an unconditional branch, forget the previous
-            insn information.  */
-         if (pinfo & INSN_UNCOND_BRANCH_DELAY)
-           {
-             mips_no_prev_insn ();
-           }
-       }
-      else if (pinfo & INSN_COND_BRANCH_LIKELY)
-       {
-         /* We don't yet optimize a branch likely.  What we should do
-            is look at the target, copy the instruction found there
-            into the delay slot, and increment the branch to jump to
-            the next instruction.  */
-         insert_into_history (0, 1, ip);
-         emit_nop ();
-       }
-      else
-       insert_into_history (0, 1, ip);
+      symbol_name_temporary[i++] = l % 10 + '0';
+      l /= 10;
     }
-  else
-    insert_into_history (0, 1, ip);
+  while (l != 0);
+  while (i > 0)
+    *p++ = symbol_name_temporary[--i];
+  *p = '\0';
 
-  /* We just output an insn, so the next one doesn't have a label.  */
-  mips_clear_insn_labels ();
+  return micromips_target_name;
 }
 
-/* Forget that there was any previous instruction or label.  */
-
 static void
-mips_no_prev_insn (void)
+micromips_label_expr (expressionS *label_expr)
 {
-  prev_nop_frag = NULL;
-  insert_into_history (0, ARRAY_SIZE (history), NOP_INSN);
-  mips_clear_insn_labels ();
+  label_expr->X_op = O_symbol;
+  label_expr->X_add_symbol = symbol_find_or_make (micromips_label_name ());
+  label_expr->X_add_number = 0;
 }
 
-/* This function must be called before we emit something other than
-   instructions.  It is like mips_no_prev_insn except that it inserts
-   any NOPS that might be needed by previous instructions.  */
-
-void
-mips_emit_delays (void)
+static void
+micromips_label_inc (void)
 {
-  if (! mips_opts.noreorder)
-    {
-      int nops = nops_for_insn (history, NULL);
-      if (nops > 0)
-       {
-         while (nops-- > 0)
-           add_fixed_insn (NOP_INSN);
-         mips_move_labels ();
-       }
-    }
-  mips_no_prev_insn ();
+  micromips_target_label++;
+  *micromips_target_name = '\0';
 }
 
-/* Start a (possibly nested) noreorder block.  */
-
 static void
-start_noreorder (void)
+micromips_add_label (void)
 {
-  if (mips_opts.noreorder == 0)
-    {
-      unsigned int i;
-      int 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;
-
-      /* 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 (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 (mips_opts.mips16 ? nops * 2 : nops * 4);
-             prev_nop_frag = frag_now;
-             prev_nop_frag_holds = nops;
-             prev_nop_frag_required = 0;
-             prev_nop_frag_since = 0;
-           }
-
-         for (; nops > 0; --nops)
-           add_fixed_insn (NOP_INSN);
+  symbolS *s;
 
-         /* 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_labels ();
-       }
-      mips16_mark_labels ();
-      mips_clear_insn_labels ();
-    }
-  mips_opts.noreorder++;
-  mips_any_noreorder = 1;
+  s = colon (micromips_label_name ());
+  micromips_label_inc ();
+#if defined(OBJ_ELF) || defined(OBJ_MAYBE_ELF)
+  if (IS_ELF)
+    S_SET_OTHER (s, ELF_ST_SET_MICROMIPS (S_GET_OTHER (s)));
+#else
+  (void) s;
+#endif
 }
 
-/* End a nested noreorder block.  */
+/* If assembling microMIPS code, then return the microMIPS reloc
+   corresponding to the requested one if any.  Otherwise return
+   the reloc unchanged.  */
 
-static void
-end_noreorder (void)
+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;
 
-  mips_opts.noreorder--;
-  if (mips_opts.noreorder == 0 && prev_nop_frag != NULL)
+  if (!mips_opts.micromips)
+    return reloc;
+  for (i = 0; i < ARRAY_SIZE (relocs); i++)
     {
-      /* Commit to inserting prev_nop_frag_required nops and go back to
-        handling nop insertion the .set reorder way.  */
-      prev_nop_frag->fr_fix -= ((prev_nop_frag_holds - prev_nop_frag_required)
-                               * (mips_opts.mips16 ? 2 : 4));
-      insert_into_history (prev_nop_frag_since,
-                          prev_nop_frag_required, NOP_INSN);
-      prev_nop_frag = NULL;
+      r = relocs[i][0];
+      if (r > reloc)
+       return reloc;
+      if (r == reloc)
+       return relocs[i][1];
     }
+  return reloc;
 }
 
-/* Set up global variables for the start of a new macro.  */
+/* Try to resolve relocation RELOC against constant OPERAND at assembly time.
+   Return true on success, storing the resolved value in RESULT.  */
 
-static void
-macro_start (void)
+static bfd_boolean
+calculate_reloc (bfd_reloc_code_real_type reloc, offsetT operand,
+                offsetT *result)
 {
-  memset (&mips_macro_warning.sizes, 0, sizeof (mips_macro_warning.sizes));
-  mips_macro_warning.delay_slot_p = (mips_opts.noreorder
-                                    && (history[0].insn_mo->pinfo
-                                        & (INSN_UNCOND_BRANCH_DELAY
-                                           | INSN_COND_BRANCH_DELAY
-                                           | INSN_COND_BRANCH_LIKELY)) != 0);
-}
+  switch (reloc)
+    {
+    case BFD_RELOC_MIPS_HIGHEST:
+    case BFD_RELOC_MICROMIPS_HIGHEST:
+      *result = ((operand + 0x800080008000ull) >> 48) & 0xffff;
+      return TRUE;
 
-/* Given that a macro is longer than 4 bytes, return the appropriate warning
-   for it.  Return null if no warning is needed.  SUBTYPE is a bitmask of
-   RELAX_DELAY_SLOT and RELAX_NOMACRO.  */
+    case BFD_RELOC_MIPS_HIGHER:
+    case BFD_RELOC_MICROMIPS_HIGHER:
+      *result = ((operand + 0x80008000ull) >> 32) & 0xffff;
+      return TRUE;
 
-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
-    return 0;
-}
+    case BFD_RELOC_HI16_S:
+    case BFD_RELOC_MICROMIPS_HI16_S:
+    case BFD_RELOC_MIPS16_HI16_S:
+      *result = ((operand + 0x8000) >> 16) & 0xffff;
+      return TRUE;
 
-/* Finish up a macro.  Emit warnings as appropriate.  */
+    case BFD_RELOC_HI16:
+    case BFD_RELOC_MICROMIPS_HI16:
+    case BFD_RELOC_MIPS16_HI16:
+      *result = (operand >> 16) & 0xffff;
+      return TRUE;
 
-static void
-macro_end (void)
-{
-  if (mips_macro_warning.sizes[0] > 4 || mips_macro_warning.sizes[1] > 4)
-    {
-      relax_substateT subtype;
+    case BFD_RELOC_LO16:
+    case BFD_RELOC_MICROMIPS_LO16:
+    case BFD_RELOC_MIPS16_LO16:
+      *result = operand & 0xffff;
+      return TRUE;
 
-      /* Set up the relaxation warning flags.  */
-      subtype = 0;
-      if (mips_macro_warning.sizes[1] > mips_macro_warning.sizes[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;
+    case BFD_RELOC_UNUSED:
+      *result = operand;
+      return TRUE;
 
-      if (mips_macro_warning.sizes[0] > 4 && mips_macro_warning.sizes[1] > 4)
-       {
-         /* Either the macro has a single implementation or both
-            implementations are longer than 4 bytes.  Emit the
-            warning now.  */
-         const char *msg = macro_warning (subtype);
-         if (msg != 0)
-           as_warn ("%s", msg);
-       }
-      else
-       {
-         /* One implementation might need a warning but the other
-            definitely doesn't.  */
-         mips_macro_warning.first_frag->fr_subtype |= subtype;
-       }
+    default:
+      return FALSE;
     }
 }
 
-/* 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.  */
+/* 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.  */
 
 static void
-macro_read_relocs (va_list *args, bfd_reloc_code_real_type *r)
+append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
+            bfd_reloc_code_real_type *reloc_type, bfd_boolean expansionp)
 {
-  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);
-}
+  unsigned long prev_pinfo2, pinfo;
+  bfd_boolean relaxed_branch = FALSE;
+  enum append_method method;
+  bfd_boolean relax32;
+  int branch_disp;
 
-/* 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.  */
+  if (mips_fix_loongson2f && !HAVE_CODE_COMPRESSION)
+    fix_loongson2f (ip);
 
-static void
-macro_build (expressionS *ep, const char *name, const char *fmt, ...)
-{
-  const struct mips_opcode *mo;
-  struct mips_cl_insn insn;
-  bfd_reloc_code_real_type r[3];
-  va_list args;
+  file_ase_mips16 |= mips_opts.mips16;
+  file_ase_micromips |= mips_opts.micromips;
 
-  va_start (args, fmt);
+  prev_pinfo2 = history[0].insn_mo->pinfo2;
+  pinfo = ip->insn_mo->pinfo;
 
-  if (mips_opts.mips16)
+  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)
     {
-      mips16_macro_build (ep, name, fmt, &args);
-      va_end (args);
-      return;
-    }
+      switch (*reloc_type)
+       {
+       case BFD_RELOC_MIPS_JMP:
+         {
+           int shift;
+
+           shift = mips_opts.micromips ? 1 : 2;
+           if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
+             as_bad (_("jump to misaligned address (0x%lx)"),
+                     (unsigned long) address_expr->X_add_number);
+           ip->insn_opcode |= ((address_expr->X_add_number >> shift)
+                               & 0x3ffffff);
+           ip->complete_p = 1;
+         }
+         break;
 
-  r[0] = BFD_RELOC_UNUSED;
-  r[1] = BFD_RELOC_UNUSED;
-  r[2] = BFD_RELOC_UNUSED;
-  mo = (struct mips_opcode *) hash_find (op_hash, name);
-  gas_assert (mo);
-  gas_assert (strcmp (name, mo->name) == 0);
+       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;
 
-  while (1)
-    {
-      /* Search until we get a match for NAME.  It is assumed here that
-        macros will never generate MDMX, MIPS-3D, or MT instructions.  */
-      if (strcmp (fmt, mo->args) == 0
-         && mo->pinfo != INSN_MACRO
-         && is_opcode_valid (mo))
-       break;
+       case BFD_RELOC_16_PCREL_S2:
+         {
+           int shift;
 
-      ++mo;
-      gas_assert (mo->name);
-      gas_assert (strcmp (name, mo->name) == 0);
+           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;
+
+       default:
+         {
+           offsetT value;
+
+           if (calculate_reloc (*reloc_type, address_expr->X_add_number,
+                                &value))
+             {
+               ip->insn_opcode |= value & 0xffff;
+               ip->complete_p = 1;
+             }
+         }
+         break;
+       }
     }
 
-  create_insn (&insn, mo);
-  for (;;)
+  if (mips_relax.sequence != 2 && !mips_opts.noreorder)
     {
-      switch (*fmt++)
+      /* 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)
        {
-       case '\0':
-         break;
+         fragS *old_frag;
+         unsigned long old_frag_offset;
+         int i;
 
-       case ',':
-       case '(':
-       case ')':
-         continue;
+         old_frag = frag_now;
+         old_frag_offset = frag_now_fix ();
 
-       case '+':
-         switch (*fmt++)
+         for (i = 0; i < nops; i++)
+           add_fixed_insn (NOP_INSN);
+         insert_into_history (0, nops, NOP_INSN);
+
+         if (listing)
            {
-           case 'A':
-           case 'E':
-             INSERT_OPERAND (SHAMT, insn, va_arg (args, int));
-             continue;
+             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);
+           }
 
-           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 (INSMSB, insn, va_arg (args, int));
-             continue;
+         mips_move_text_labels ();
 
-           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 (EXTMSBD, insn, va_arg (args, int));
-             continue;
+#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)
+    {
+      int nops;
 
-           case 'Q':
-             INSERT_OPERAND (SEQI, insn, va_arg (args, int));
-             continue;
+      /* 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);
 
-           default:
-             internalError ();
-           }
-         continue;
+      /* Enforce NOPS as a minimum.  */
+      if (nops > prev_nop_frag_required)
+       prev_nop_frag_required = nops;
 
-       case '2':
-         INSERT_OPERAND (BP, insn, va_arg (args, int));
-         continue;
+      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++;
+       }
+    }
 
-       case 't':
-       case 'w':
-       case 'E':
-         INSERT_OPERAND (RT, insn, va_arg (args, int));
-         continue;
+  method = get_append_method (ip, address_expr, reloc_type);
+  branch_disp = method == APPEND_SWAP ? insn_length (history) : 0;
 
-       case 'c':
-         INSERT_OPERAND (CODE, insn, va_arg (args, int));
-         continue;
+#ifdef OBJ_ELF
+  dwarf2_emit_insn (0);
+  /* We want MIPS16 and microMIPS debug info to use ISA-encoded addresses,
+     so "move" the instruction address accordingly.
+
+     Also, it doesn't seem appropriate for the assembler to reorder .loc
+     entries.  If this instruction is a branch that we are going to swap
+     with the previous instruction, the two instructions should be
+     treated as a unit, and the debug information for both instructions
+     should refer to the start of the branch sequence.  Using the
+     current position is certainly wrong when swapping a 32-bit branch
+     and a 16-bit delay slot, since the current position would then be
+     in the middle of a branch.  */
+  dwarf2_move_insn ((HAVE_CODE_COMPRESSION ? 1 : 0) - branch_disp);
+#endif
 
-       case 'T':
-       case 'W':
-         INSERT_OPERAND (FT, insn, va_arg (args, int));
-         continue;
+  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;
 
-       case 'd':
-       case 'G':
-       case 'K':
-         INSERT_OPERAND (RD, insn, va_arg (args, int));
-         continue;
+      gas_assert (address_expr != NULL);
+      gas_assert (!mips_relax.sequence);
 
-       case 'U':
-         {
-           int tmp = va_arg (args, int);
+      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"));
 
-           INSERT_OPERAND (RT, insn, tmp);
-           INSERT_OPERAND (RD, insn, tmp);
-           continue;
-         }
+      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);
+       }
 
-       case 'V':
-       case 'S':
-         INSERT_OPERAND (FS, insn, va_arg (args, int));
-         continue;
+      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]++;
+       }
 
-       case 'z':
-         continue;
+      if (mips_opts.mips16)
+       {
+         ip->fixed_p = 1;
+         ip->mips16_absolute_jump_p = (*reloc_type == BFD_RELOC_MIPS16_JMP);
+       }
+      add_fixed_insn (ip);
+    }
 
-       case '<':
-         INSERT_OPERAND (SHAMT, insn, va_arg (args, int));
-         continue;
+  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;
 
-       case 'D':
-         INSERT_OPERAND (FD, insn, va_arg (args, int));
-         continue;
+      /* 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]);
 
-       case 'B':
-         INSERT_OPERAND (CODE20, insn, va_arg (args, int));
-         continue;
+      /* 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]);
 
-       case 'J':
-         INSERT_OPERAND (CODE19, insn, va_arg (args, int));
-         continue;
+      if (howto == NULL)
+       {
+         /* To reproduce this failure try assembling gas/testsuites/
+            gas/mips/mips16-intermix.s with a mips-ecoff targeted
+            assembler.  */
+         as_bad (_("Unsupported MIPS relocation number %d"),
+                 final_type[i - 1]);
+         howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_16);
+       }
 
-       case 'q':
-         INSERT_OPERAND (CODE2, insn, va_arg (args, int));
-         continue;
+      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;
 
-       case 'b':
-       case 's':
-       case 'r':
-       case 'v':
-         INSERT_OPERAND (RS, insn, va_arg (args, int));
-         continue;
+      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;
 
-       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;
+         /* 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;
+       }
 
-       case 'o':
-         macro_read_relocs (&args, r);
-         continue;
+      /* 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]);
 
-       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;
+           /* Use fx_tcbit to mark compound relocs.  */
+           ip->fixp[0]->fx_tcbit = 1;
+           ip->fixp[i]->fx_tcbit = 1;
+         }
+    }
+  install_insn (ip);
 
-       case 'p':
-         gas_assert (ep != NULL);
+  /* 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);
 
-         /*
-          * 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)
-           {
-             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;
+  switch (method)
+    {
+    case APPEND_ADD:
+      insert_into_history (0, 1, ip);
+      break;
 
-       case 'a':
-         gas_assert (ep != NULL);
-         *r = BFD_RELOC_MIPS_JMP;
-         continue;
+    case APPEND_ADD_WITH_NOP:
+      {
+       struct mips_cl_insn *nop;
 
-       case 'C':
-         INSERT_OPERAND (COPZ, insn, va_arg (args, unsigned long));
-         continue;
+       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 'k':
-         INSERT_OPERAND (CACHE, insn, va_arg (args, unsigned long));
-         continue;
+    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;
 
-       default:
-         internalError ();
-       }
+    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;
     }
-  va_end (args);
-  gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
-
-  append_insn (&insn, ep, r);
-}
 
-static void
-mips16_macro_build (expressionS *ep, const char *name, const char *fmt,
-                   va_list *args)
-{
-  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};
+  /* 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;
 
-  mo = (struct mips_opcode *) hash_find (mips16_op_hash, name);
-  gas_assert (mo);
-  gas_assert (strcmp (name, mo->name) == 0);
+      mips_no_prev_insn ();
 
-  while (strcmp (fmt, mo->args) != 0 || mo->pinfo == INSN_MACRO)
-    {
-      ++mo;
-      gas_assert (mo->name);
-      gas_assert (strcmp (name, mo->name) == 0);
+      for (i = 0; i < ARRAY_SIZE (history); i++)
+       history[i].cleared_p = 1;
     }
 
-  create_insn (&insn, mo);
-  for (;;)
+  /* We need to emit a label at the end of branch-likely macros.  */
+  if (emit_branch_likely_macro)
     {
-      int c;
-
-      c = *fmt++;
-      switch (c)
-       {
-       case '\0':
-         break;
-
-       case ',':
-       case '(':
-       case ')':
-         continue;
+      emit_branch_likely_macro = FALSE;
+      micromips_add_label ();
+    }
 
-       case 'y':
-       case 'w':
-         MIPS16_INSERT_OPERAND (RY, insn, va_arg (*args, int));
-         continue;
+  /* We just output an insn, so the next one doesn't have a label.  */
+  mips_clear_insn_labels ();
+}
 
-       case 'x':
-       case 'v':
-         MIPS16_INSERT_OPERAND (RX, insn, va_arg (*args, int));
-         continue;
+/* Forget that there was any previous instruction or label.
+   When BRANCH is true, the branch history is also flushed.  */
 
-       case 'z':
-         MIPS16_INSERT_OPERAND (RZ, insn, va_arg (*args, int));
-         continue;
+static void
+mips_no_prev_insn (void)
+{
+  prev_nop_frag = NULL;
+  insert_into_history (0, ARRAY_SIZE (history), NOP_INSN);
+  mips_clear_insn_labels ();
+}
 
-       case 'Z':
-         MIPS16_INSERT_OPERAND (MOVE32Z, insn, va_arg (*args, int));
-         continue;
+/* 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.  */
 
-       case '0':
-       case 'S':
-       case 'P':
-       case 'R':
-         continue;
+void
+mips_emit_delays (void)
+{
+  if (! mips_opts.noreorder)
+    {
+      int nops = nops_for_insn (0, history, NULL);
+      if (nops > 0)
+       {
+         while (nops-- > 0)
+           add_fixed_insn (NOP_INSN);
+         mips_move_text_labels ();
+       }
+    }
+  mips_no_prev_insn ();
+}
 
-       case 'X':
-         MIPS16_INSERT_OPERAND (REGR32, insn, va_arg (*args, int));
-         continue;
+/* Start a (possibly nested) noreorder block.  */
 
-       case 'Y':
-         {
-           int regno;
+static void
+start_noreorder (void)
+{
+  if (mips_opts.noreorder == 0)
+    {
+      unsigned int i;
+      int nops;
 
-           regno = va_arg (*args, int);
-           regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3);
-           MIPS16_INSERT_OPERAND (REG32R, insn, regno);
-         }
-         continue;
+      /* None of the instructions before the .set noreorder can be moved.  */
+      for (i = 0; i < ARRAY_SIZE (history); i++)
+       history[i].fixed_p = 1;
 
-       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':
-         {
-           gas_assert (ep != NULL);
+      /* 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;
+           }
 
-           if (ep->X_op != O_constant)
-             *r = (int) BFD_RELOC_UNUSED + c;
-           else
-             {
-               mips16_immed (NULL, 0, c, ep->X_add_number, FALSE, FALSE,
-                             FALSE, &insn.insn_opcode, &insn.use_extend,
-                             &insn.extend);
-               ep = NULL;
-               *r = BFD_RELOC_UNUSED;
-             }
-         }
-         continue;
+         for (; nops > 0; --nops)
+           add_fixed_insn (NOP_INSN);
 
-       case '6':
-         MIPS16_INSERT_OPERAND (IMM6, insn, va_arg (*args, int));
-         continue;
+         /* 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 ();
        }
-
-      break;
+      mips_mark_labels ();
+      mips_clear_insn_labels ();
     }
-
-  gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
-
-  append_insn (&insn, ep, r);
+  mips_opts.noreorder++;
+  mips_any_noreorder = 1;
 }
 
-/*
- * Sign-extend 32-bit mode constants that have bit 31 set and all
- * higher bits unset.
- */
+/* End a nested noreorder block.  */
+
 static void
-normalize_constant_expr (expressionS *ex)
+end_noreorder (void)
 {
-  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);
+  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;
+    }
 }
 
-/*
- * Sign-extend 32-bit mode address offsets that have bit 31 set and
- * all higher bits unset.
- */
+/* Set up global variables for the start of a new macro.  */
+
 static void
-normalize_address_expr (expressionS *ex)
-{
-  if (((ex->X_op == O_constant && HAVE_32BIT_ADDRESSES)
-       || (ex->X_op == O_symbol && HAVE_32BIT_SYMBOLS))
-      && IS_ZEXT_32BIT_NUM (ex->X_add_number))
-    ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
-                       - 0x80000000);
-}
-
-/*
- * Generate a "jalr" instruction with a relocation hint to the called
- * function.  This occurs in NewABI PIC code.
- */
-static void
-macro_build_jalr (expressionS *ep)
+macro_start (void)
 {
-  char *f = NULL;
-
-  if (MIPS_JALR_HINT_P (ep))
+  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))
     {
-      frag_grow (8);
-      f = frag_more (0);
+    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;
     }
-  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, BFD_RELOC_MIPS_JALR);
+  mips_macro_warning.first_frag = NULL;
 }
 
-/*
- * Generate a "lui" instruction.
- */
-static void
-macro_build_lui (expressionS *ep, int regnum)
+/* Given that a macro is longer than one instruction or of the wrong size,
+   return the appropriate warning for it.  Return null if no warning is
+   needed.  SUBTYPE is a bitmask of RELAX_DELAY_SLOT, RELAX_DELAY_SLOT_16BIT,
+   RELAX_DELAY_SLOT_SIZE_FIRST, RELAX_DELAY_SLOT_SIZE_SECOND,
+   and RELAX_NOMACRO.  */
+
+static const char *
+macro_warning (relax_substateT subtype)
 {
-  expressionS high_expr;
-  const 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};
-  const char *name = "lui";
-  const char *fmt = "t,u";
+  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 (! mips_opts.mips16);
+/* Finish up a macro.  Emit warnings as appropriate.  */
 
-  high_expr = *ep;
+static void
+macro_end (void)
+{
+  /* Relaxation warning flags.  */
+  relax_substateT subtype = 0;
 
-  if (high_expr.X_op == O_constant)
+  /* 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)
     {
-      /* We can compute the instruction now without a relocation entry.  */
-      high_expr.X_add_number = ((high_expr.X_add_number + 0x8000)
-                               >> 16) & 0xffff;
-      *r = BFD_RELOC_UNUSED;
+      if (mips_macro_warning.delay_slot_length
+         != mips_macro_warning.first_insn_sizes[0])
+       subtype |= RELAX_DELAY_SLOT_SIZE_FIRST;
+      if (mips_macro_warning.delay_slot_length
+         != mips_macro_warning.first_insn_sizes[1])
+       subtype |= RELAX_DELAY_SLOT_SIZE_SECOND;
     }
-  else
+
+  /* Check instruction count requirements.  */
+  if (mips_macro_warning.insns[0] > 1 || mips_macro_warning.insns[1] > 1)
     {
-      gas_assert (ep->X_op == O_symbol);
-      /* _gp_disp is a special case, used from s_cpload.
-        __gnu_local_gp is used if mips_no_shared.  */
-      gas_assert (mips_pic == NO_PIC
-             || (! HAVE_NEWABI
-                 && strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0)
-             || (! mips_in_shared
-                 && strcmp (S_GET_NAME (ep->X_add_symbol),
-                             "__gnu_local_gp") == 0));
-      *r = BFD_RELOC_HI16_S;
+      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;
     }
 
-  mo = hash_find (op_hash, name);
-  gas_assert (strcmp (name, mo->name) == 0);
-  gas_assert (strcmp (fmt, mo->args) == 0);
-  create_insn (&insn, mo);
-
-  insn.insn_opcode = insn.insn_mo->match;
-  INSERT_OPERAND (RT, insn, regnum);
-  if (*r == BFD_RELOC_UNUSED)
+  /* 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)
     {
-      insn.insn_opcode |= high_expr.X_add_number;
-      append_insn (&insn, NULL, r);
+      relax_substateT s;
+      const char *msg;
+
+      s = subtype & (RELAX_DELAY_SLOT_16BIT
+                    | RELAX_DELAY_SLOT_SIZE_FIRST
+                    | RELAX_DELAY_SLOT_SIZE_SECOND);
+      msg = macro_warning (s);
+      if (msg != NULL)
+       as_warn ("%s", msg);
+      subtype &= ~s;
     }
-  else
-    append_insn (&insn, &high_expr, r);
-}
-
-/* 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);
 
-  /* Sign-extending 32-bit constants makes their handling easier.  */
-  if (!dbl)
-    normalize_constant_expr (ep);
-
-  /* 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"));
-
-  if (IS_SEXT_16BIT_NUM(ep->X_add_number))
+  /* 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)
     {
-      /* Signed 16-bit offset will fit in the op.  Easy!  */
-      macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, breg);
+      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;
     }
-  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\""));
-    }
+  /* 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;
 }
 
-/*                     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);
-    }
-}
+/* Instruction operand formats used in macros that vary between
+   standard MIPS and microMIPS code.  */
+
+static const char * const brk_fmt[2] = { "c", "mF" };
+static const char * const cop12_fmt[2] = { "E,o(b)", "E,~(b)" };
+static const char * const jalr_fmt[2] = { "d,s", "t,s" };
+static const char * const lui_fmt[2] = { "t,u", "s,u" };
+static const char * const mem12_fmt[2] = { "t,o(b)", "t,~(b)" };
+static const char * const mfhl_fmt[2] = { "d", "mj" };
+static const char * const shft_fmt[2] = { "d,w,<", "t,r,<" };
+static const char * const trap_fmt[2] = { "s,t,q", "s,t,|" };
+
+#define BRK_FMT (brk_fmt[mips_opts.micromips])
+#define COP12_FMT (cop12_fmt[mips_opts.micromips])
+#define JALR_FMT (jalr_fmt[mips_opts.micromips])
+#define LUI_FMT (lui_fmt[mips_opts.micromips])
+#define MEM12_FMT (mem12_fmt[mips_opts.micromips])
+#define MFHL_FMT (mfhl_fmt[mips_opts.micromips])
+#define SHFT_FMT (shft_fmt[mips_opts.micromips])
+#define TRAP_FMT (trap_fmt[mips_opts.micromips])
 
-/* Warn if an expression is not a constant.  */
+/* 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
-check_absolute_expr (struct mips_cl_insn *ip, expressionS *ex)
+macro_read_relocs (va_list *args, bfd_reloc_code_real_type *r)
 {
-  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);
+  int i, next;
 
-  if (HAVE_32BIT_GPRS)
-    normalize_constant_expr (ex);
+  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);
 }
 
-/* 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)
+/* 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.  */
 
-/*                     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)
+macro_build (expressionS *ep, const char *name, const char *fmt, ...)
 {
-  int freg;
-  expressionS hi32, lo32;
+  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;
 
-  if (ep->X_op != O_big)
+  va_start (args, fmt);
+
+  if (mips_opts.mips16)
     {
-      gas_assert (ep->X_op == O_constant);
+      mips16_macro_build (ep, name, fmt, &args);
+      va_end (args);
+      return;
+    }
 
-      /* Sign-extending 32-bit constants makes their handling easier.  */
-      if (!dbl)
-       normalize_constant_expr (ep);
+  r[0] = BFD_RELOC_UNUSED;
+  r[1] = BFD_RELOC_UNUSED;
+  r[2] = BFD_RELOC_UNUSED;
+  hash = mips_opts.micromips ? micromips_op_hash : op_hash;
+  amo = (struct mips_opcode *) hash_find (hash, name);
+  gas_assert (amo);
+  gas_assert (strcmp (name, amo->name) == 0);
 
-      if (IS_SEXT_16BIT_NUM (ep->X_add_number))
+  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))
        {
-         /* 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;
-       }
-      else if (ep->X_add_number >= 0 && ep->X_add_number < 0x10000)
-       {
-         /* 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", "t,u", reg, BFD_RELOC_HI16);
-         if ((ep->X_add_number & 0xffff) != 0)
-           macro_build (ep, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
-         return;
+         if (is_delay_slot_valid (amo))
+           {
+             mo = amo;
+             break;
+           }
+         else if (!mo)
+           mo = amo;
        }
-    }
-
-  /* The value is larger than 32 bits.  */
-
-  if (!dbl || HAVE_32BIT_GPRS)
-    {
-      char value[32];
 
-      sprintf_vma (value, ep->X_add_number);
-      as_bad (_("Number (0x%s) larger than 32 bits"), value);
-      macro_build (ep, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
-      return;
+      ++amo;
+      gas_assert (amo->name);
     }
+  while (strcmp (name, amo->name) == 0);
 
-  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 (mo);
+  create_insn (&insn, mo);
+  for (;;)
     {
-      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);
-    }
+      switch (*fmt++)
+       {
+       case '\0':
+         break;
 
-  if (hi32.X_add_number == 0)
-    freg = 0;
-  else
-    {
-      int shift, bit;
-      unsigned long hi, lo;
+       case ',':
+       case '(':
+       case ')':
+         continue;
 
-      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)
+       case '+':
+         switch (*fmt++)
            {
-             macro_build (&lo32, "lui", "t,u", reg, BFD_RELOC_HI16);
-             if (lo32.X_add_number & 0xffff)
-               macro_build (&lo32, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
-             return;
-           }
-       }
+           case 'A':
+           case 'E':
+             INSERT_OPERAND (mips_opts.micromips,
+                             EXTLSB, insn, va_arg (args, int));
+             continue;
 
-      /* Check for 16bit shifted constant.  We know that hi32 is
-         non-zero, so start the mask on the first bit of the hi32
-         value.  */
-      shift = 17;
-      do
-       {
-         unsigned long himask, lomask;
+           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 (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;
+           case 'J':
+             gas_assert (!mips_opts.micromips);
+             INSERT_OPERAND (0, CODE10, insn, va_arg (args, int));
+             continue;
 
-             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", "d,w,<",
-                          reg, reg, (shift >= 32) ? shift - 32 : shift);
-             return;
-           }
-         ++shift;
-       }
-      while (shift <= (64 - 16));
+           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;
 
-      /* 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)
-       {
-         bit = 0;
-         while ((lo & 1) == 0)
-           {
-             lo >>= 1;
-             ++bit;
-           }
-         lo |= (hi & (((unsigned long) 1 << bit) - 1)) << (32 - bit);
-         hi >>= bit;
-       }
-      else
-       {
-         bit = 32;
-         while ((hi & 1) == 0)
-           {
-             hi >>= 1;
-             ++bit;
-           }
-         lo = hi;
-         hi = 0;
-       }
+           case 'Q':
+             gas_assert (!mips_opts.micromips);
+             INSERT_OPERAND (0, SEQI, insn, va_arg (args, int));
+             continue;
 
-      /* Optimize if the shifted value is a (power of 2) - 1.  */
-      if ((hi == 0 && ((lo + 1) & lo) == 0)
-         || (lo == 0xffffffff && ((hi + 1) & hi) == 0))
-       {
-         shift = COUNT_TOP_ZEROES ((unsigned int) hi32.X_add_number);
-         if (shift != 0)
-           {
-             expressionS tmp;
+           case 'j':
+             INSERT_OPERAND (mips_opts.micromips, EVAOFFSET, insn, va_arg (args, int));
+             continue;
 
-             /* 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", "d,w,<",
-                              reg, reg, (bit >= 32) ? bit - 32 : bit);
-               }
-             macro_build (NULL, (shift >= 32) ? "dsrl32" : "dsrl", "d,w,<",
-                          reg, reg, (shift >= 32) ? shift - 32 : shift);
-             return;
+           default:
+             abort ();
            }
-       }
+         continue;
 
-      /* 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)
-       {
-         macro_build (NULL, "dsll32", "d,w,<", reg, freg, 0);
-         freg = reg;
-       }
-    }
-  else
-    {
-      expressionS mid16;
+       case '2':
+         INSERT_OPERAND (mips_opts.micromips, BP, insn, va_arg (args, int));
+         continue;
 
-      if ((freg == 0) && (lo32.X_add_number == (offsetT) 0xffffffff))
-       {
-         macro_build (&lo32, "lui", "t,u", reg, BFD_RELOC_HI16);
-         macro_build (NULL, "dsrl32", "d,w,<", reg, reg, 0);
-         return;
-       }
+       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 (freg != 0)
-       {
-         macro_build (NULL, "dsll", "d,w,<", 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", "d,w,<", reg, reg, 16);
-      freg = reg;
-    }
-  if ((lo32.X_add_number & 0xffff) != 0)
-    macro_build (&lo32, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
-}
+       case 'c':
+         gas_assert (!mips_opts.micromips);
+         INSERT_OPERAND (0, CODE, insn, va_arg (args, int));
+         continue;
 
-static inline void
-load_delay_nop (void)
-{
-  if (!gpr_interlocks)
-    macro_build (NULL, "nop", "");
-}
+       case 'W':
+         gas_assert (!mips_opts.micromips);
+       case 'T':
+         INSERT_OPERAND (mips_opts.micromips, FT, insn, va_arg (args, int));
+         continue;
 
-/* Load an address into a register.  */
+       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;
 
-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 'K':
+         gas_assert (!mips_opts.micromips);
+       case 'd':
+         INSERT_OPERAND (mips_opts.micromips, RD, insn, va_arg (args, int));
+         continue;
 
-  if (ep->X_op == O_constant)
-    {
-      load_register (reg, ep, HAVE_64BIT_ADDRESSES);
-      return;
-    }
+       case 'U':
+         gas_assert (!mips_opts.micromips);
+         {
+           int tmp = va_arg (args, int);
 
-  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.
+           INSERT_OPERAND (0, RT, insn, tmp);
+           INSERT_OPERAND (0, RD, insn, tmp);
+         }
+         continue;
 
-        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
+       case 'V':
+       case 'S':
+         gas_assert (!mips_opts.micromips);
+         INSERT_OPERAND (0, FS, insn, va_arg (args, int));
+         continue;
 
-        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)
+       case 'z':
+         continue;
 
-        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 ();
-           }
+       case '<':
+         INSERT_OPERAND (mips_opts.micromips,
+                         SHAMT, insn, va_arg (args, int));
+         continue;
 
-         if (*used_at == 0 && mips_opts.at)
+       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;
+
+       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)
            {
-             macro_build (ep, "lui", "t,u", reg, BFD_RELOC_MIPS_HIGHEST);
-             macro_build (ep, "lui", "t,u", 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", "d,w,<", reg, reg, 0);
-             macro_build (NULL, "daddu", "d,v,t", reg, reg, AT);
-             *used_at = 1;
+             /* 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
-           {
-             macro_build (ep, "lui", "t,u", reg, BFD_RELOC_MIPS_HIGHEST);
-             macro_build (ep, "daddiu", "t,r,j", reg, reg,
-                          BFD_RELOC_MIPS_HIGHER);
-             macro_build (NULL, "dsll", "d,w,<", reg, reg, 16);
-             macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_HI16_S);
-             macro_build (NULL, "dsll", "d,w,<", reg, reg, 16);
-             macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_LO16);
-           }
+           *r = BFD_RELOC_16_PCREL_S2;
+         continue;
 
-         if (mips_relax.sequence)
-           relax_end ();
-       }
-      else
-       {
-         if ((valueT) ep->X_add_number <= MAX_GPREL_OFFSET
-             && !nopic_need_relax (ep->X_add_symbol, 1))
-           {
-             relax_start (ep->X_add_symbol);
-             macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg,
-                          mips_gp_register, BFD_RELOC_GPREL16);
-             relax_switch ();
-           }
-         macro_build_lui (ep, reg);
-         macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j",
-                      reg, reg, BFD_RELOC_LO16);
-         if (mips_relax.sequence)
-           relax_end ();
-       }
-    }
-  else if (!mips_big_got)
-    {
-      expressionS ex;
+       case 'a':
+         gas_assert (ep != NULL);
+         *r = BFD_RELOC_MIPS_JMP;
+         continue;
 
-      /* If this is a reference to an external symbol, we want
-          lw           $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT16)
-        Otherwise we want
-          lw           $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT16)
-          nop
-          addiu        $reg,$reg,<sym>         (BFD_RELOC_LO16)
-        If there is a constant, it must be added in after.
+       case 'C':
+         gas_assert (!mips_opts.micromips);
+         INSERT_OPERAND (0, COPZ, insn, va_arg (args, unsigned long));
+         continue;
 
-        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 ();
+       case 'k':
+         INSERT_OPERAND (mips_opts.micromips,
+                         CACHE, insn, va_arg (args, unsigned long));
+         continue;
 
-         if (ex.X_add_number != 0)
+       case '|':
+         gas_assert (mips_opts.micromips);
+         INSERT_OPERAND (1, TRAP, insn, va_arg (args, int));
+         continue;
+
+       case '.':
+         gas_assert (mips_opts.micromips);
+         INSERT_OPERAND (1, OFFSET10, insn, va_arg (args, int));
+         continue;
+
+       case '\\':
+         INSERT_OPERAND (mips_opts.micromips,
+                         3BITPOS, insn, va_arg (args, unsigned int));
+         continue;
+
+       case '~':
+         INSERT_OPERAND (mips_opts.micromips,
+                         OFFSET12, insn, va_arg (args, unsigned long));
+         continue;
+
+       case 'N':
+         gas_assert (mips_opts.micromips);
+         INSERT_OPERAND (1, BCC, insn, va_arg (args, int));
+         continue;
+
+       case 'm':       /* Opcode extension character.  */
+         gas_assert (mips_opts.micromips);
+         switch (*fmt++)
            {
-             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);
+           case 'j':
+             INSERT_OPERAND (1, MJ, insn, va_arg (args, int));
+             break;
+
+           case 'p':
+             INSERT_OPERAND (1, MP, insn, va_arg (args, int));
+             break;
+
+           case 'F':
+             INSERT_OPERAND (1, IMMF, insn, va_arg (args, int));
+             break;
+
+           default:
+             abort ();
            }
+         continue;
+
+       default:
+         abort ();
        }
+      break;
     }
-  else if (mips_big_got)
-    {
-      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)
+  va_end (args);
+  gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
 
-        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.
+  append_insn (&insn, ep, r, TRUE);
+}
 
-        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", "t,u", reg, BFD_RELOC_MIPS_GOT_HI16);
-         macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                      reg, reg, mips_gp_register);
-         macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)",
-                      reg, BFD_RELOC_MIPS_GOT_LO16, reg);
-         if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
-           as_bad (_("PIC code offset overflow (max 16 signed bits)"));
-         else if (ex.X_add_number)
-           {
-             ex.X_op = O_constant;
-             macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
-                          BFD_RELOC_LO16);
-           }
+static void
+mips16_macro_build (expressionS *ep, const char *name, const char *fmt,
+                   va_list *args)
+{
+  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};
 
-         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", "t,u", 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 ();
+  mo = (struct mips_opcode *) hash_find (mips16_op_hash, name);
+  gas_assert (mo);
+  gas_assert (strcmp (name, mo->name) == 0);
 
-         if (ex.X_add_number != 0)
-           {
-             if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
-               as_bad (_("PIC code offset overflow (max 16 signed bits)"));
-             ex.X_op = O_constant;
-             macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
-                          BFD_RELOC_LO16);
-           }
-       }
+  while (strcmp (fmt, mo->args) != 0 || mo->pinfo == INSN_MACRO)
+    {
+      ++mo;
+      gas_assert (mo->name);
+      gas_assert (strcmp (name, mo->name) == 0);
     }
-  else
-    abort ();
 
-  if (!mips_opts.at && *used_at == 1)
-    as_bad (_("Macro used $at after \".set noat\""));
-}
+  create_insn (&insn, mo);
+  for (;;)
+    {
+      int c;
 
-/* Move the contents of register SOURCE into register DEST.  */
+      c = *fmt++;
+      switch (c)
+       {
+       case '\0':
+         break;
 
-static void
-move_register (int dest, int source)
-{
-  macro_build (NULL, HAVE_32BIT_GPRS ? "addu" : "daddu", "d,v,t",
-              dest, source, 0);
-}
+       case ',':
+       case '(':
+       case ')':
+         continue;
 
-/* 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:
+       case 'y':
+       case 'w':
+         MIPS16_INSERT_OPERAND (RY, insn, va_arg (*args, int));
+         continue;
 
-   Global symbol               Local sybmol
-   -------------               ------------
-   lw DEST,%got(SYMBOL)                lw DEST,%got(SYMBOL + OFFSET)
-   ...                         ...
-   addiu DEST,DEST,OFFSET      addiu DEST,DEST,%lo(SYMBOL + OFFSET)
+       case 'x':
+       case 'v':
+         MIPS16_INSERT_OPERAND (RX, insn, va_arg (*args, int));
+         continue;
 
-   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.  */
+       case 'z':
+         MIPS16_INSERT_OPERAND (RZ, insn, va_arg (*args, int));
+         continue;
 
-static void
-load_got_offset (int dest, expressionS *local)
-{
-  expressionS global;
+       case 'Z':
+         MIPS16_INSERT_OPERAND (MOVE32Z, insn, va_arg (*args, int));
+         continue;
 
-  global = *local;
-  global.X_add_number = 0;
+       case '0':
+       case 'S':
+       case 'P':
+       case 'R':
+         continue;
 
-  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 ();
-}
+       case 'X':
+         MIPS16_INSERT_OPERAND (REGR32, insn, va_arg (*args, int));
+         continue;
 
-static void
-add_got_offset (int dest, expressionS *local)
-{
-  expressionS global;
+       case 'Y':
+         {
+           int regno;
 
-  global.X_op = O_constant;
-  global.X_op_symbol = NULL;
-  global.X_add_symbol = NULL;
-  global.X_add_number = local->X_add_number;
+           regno = va_arg (*args, int);
+           regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3);
+           MIPS16_INSERT_OPERAND (REG32R, insn, regno);
+         }
+         continue;
 
-  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 ();
-}
+       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;
 
-static void
-add_got_offset_hilo (int dest, expressionS *local, int tmp)
-{
-  expressionS global;
-  int hold_mips_optimize;
+           gas_assert (ep != NULL);
 
-  global.X_op = O_constant;
-  global.X_op_symbol = NULL;
-  global.X_add_symbol = NULL;
-  global.X_add_number = local->X_add_number;
+           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;
 
-  relax_start (local->X_add_symbol);
-  load_register (tmp, &global, HAVE_64BIT_ADDRESSES);
-  relax_switch ();
-  /* Set mips_optimize around the lui instruction to avoid
-     inserting an unnecessary nop after the lw.  */
-  hold_mips_optimize = mips_optimize;
-  mips_optimize = 2;
-  macro_build_lui (&global, tmp);
-  mips_optimize = hold_mips_optimize;
-  macro_build (local, ADDRESS_ADDI_INSN, "t,r,j", tmp, tmp, BFD_RELOC_LO16);
-  relax_end ();
+       case '6':
+         MIPS16_INSERT_OPERAND (IMM6, insn, va_arg (*args, int));
+         continue;
+       }
 
-  macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dest, dest, tmp);
+      break;
+    }
+
+  gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
+
+  append_insn (&insn, ep, r, TRUE);
 }
 
 /*
- *                     Build macros
- *   This routine implements the seemingly endless macro or synthesized
- * instructions and addressing modes in the mips assembly language. Many
- * of these macros are simple and are similar to each other. These could
- * probably be handled by some kind of table or grammar approach instead of
- * this verbose method. Others are not simple macros but are more like
- * optimizing code generation.
- *   One interesting optimization is when several store macros appear
- * consecutively that would load AT with the upper half of the same address.
- * The ensuing load upper instructions are ommited. This implies some kind
- * of global optimization. We currently only optimize within a single macro.
- *   For many of the load and store macros if the address is specified as a
- * constant expression in the first 64k of memory (ie ld $2,0x4000c) we
- * first load register 'at' with zero and use it as the base register. The
- * mips assembler simply uses register $zero. Just one tiny optimization
- * we're missing.
+ * Sign-extend 32-bit mode constants that have bit 31 set and all
+ * higher bits unset.
  */
 static void
-macro (struct mips_cl_insn *ip)
+normalize_constant_expr (expressionS *ex)
 {
-  unsigned int treg, sreg, dreg, breg;
-  unsigned int tempreg;
-  int mask;
-  int used_at = 0;
-  expressionS expr1;
-  const char *s;
-  const char *s2;
-  const char *fmt;
-  int likely = 0;
-  int dbl = 0;
-  int coproc = 0;
-  int lr = 0;
-  int imm = 0;
-  int call = 0;
-  int off;
-  offsetT maxnum;
-  bfd_reloc_code_real_type r;
-  int hold_mips_optimize;
+  if (ex->X_op == O_constant
+      && IS_ZEXT_32BIT_NUM (ex->X_add_number))
+    ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
+                       - 0x80000000);
+}
 
-  gas_assert (! mips_opts.mips16);
+/*
+ * Sign-extend 32-bit mode address offsets that have bit 31 set and
+ * all higher bits unset.
+ */
+static void
+normalize_address_expr (expressionS *ex)
+{
+  if (((ex->X_op == O_constant && HAVE_32BIT_ADDRESSES)
+       || (ex->X_op == O_symbol && HAVE_32BIT_SYMBOLS))
+      && IS_ZEXT_32BIT_NUM (ex->X_add_number))
+    ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
+                       - 0x80000000);
+}
 
-  treg = EXTRACT_OPERAND (RT, *ip);
-  dreg = EXTRACT_OPERAND (RD, *ip);
-  sreg = breg = EXTRACT_OPERAND (RS, *ip);
-  mask = ip->insn_mo->mask;
+/*
+ * Generate a "jalr" instruction with a relocation hint to the called
+ * function.  This occurs in NewABI PIC code.
+ */
+static void
+macro_build_jalr (expressionS *ep, int cprestore)
+{
+  static const bfd_reloc_code_real_type jalr_relocs[2]
+    = { BFD_RELOC_MIPS_JALR, BFD_RELOC_MICROMIPS_JALR };
+  bfd_reloc_code_real_type jalr_reloc = jalr_relocs[mips_opts.micromips];
+  const char *jalr;
+  char *f = NULL;
 
-  expr1.X_op = O_constant;
-  expr1.X_op_symbol = NULL;
-  expr1.X_add_symbol = NULL;
-  expr1.X_add_number = 1;
+  if (MIPS_JALR_HINT_P (ep))
+    {
+      frag_grow (8);
+      f = frag_more (0);
+    }
+  if (mips_opts.micromips)
+    {
+      jalr = mips_opts.noreorder && !cprestore ? "jalr" : "jalrs";
+      if (MIPS_JALR_HINT_P (ep)
+         || (history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
+       macro_build (NULL, jalr, "t,s", RA, PIC_CALL_REG);
+      else
+       macro_build (NULL, jalr, "mj", PIC_CALL_REG);
+    }
+  else
+    macro_build (NULL, "jalr", "d,s", RA, PIC_CALL_REG);
+  if (MIPS_JALR_HINT_P (ep))
+    fix_new_exp (frag_now, f - frag_now->fr_literal, 4, ep, FALSE, jalr_reloc);
+}
 
-  switch (mask)
+/*
+ * Generate a "lui" instruction.
+ */
+static void
+macro_build_lui (expressionS *ep, int regnum)
+{
+  gas_assert (! mips_opts.mips16);
+
+  if (ep->X_op != O_constant)
     {
-    case M_DABS:
-      dbl = 1;
-    case M_ABS:
-      /* bgez $a0,.+12
-        move v0,$a0
-        sub v0,$zero,$a0
-        */
+      gas_assert (ep->X_op == O_symbol);
+      /* _gp_disp is a special case, used from s_cpload.
+        __gnu_local_gp is used if mips_no_shared.  */
+      gas_assert (mips_pic == NO_PIC
+             || (! HAVE_NEWABI
+                 && strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0)
+             || (! mips_in_shared
+                 && strcmp (S_GET_NAME (ep->X_add_symbol),
+                             "__gnu_local_gp") == 0));
+    }
 
-      start_noreorder ();
+  macro_build (ep, "lui", LUI_FMT, regnum, BFD_RELOC_HI16_S);
+}
 
-      expr1.X_add_number = 8;
-      macro_build (&expr1, "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);
+/* Generate a sequence of instructions to do a load or store from a constant
+   offset off of a base register (breg) into/from a target register (treg),
+   using AT if necessary.  */
+static void
+macro_build_ldst_constoffset (expressionS *ep, const char *op,
+                             int treg, int breg, int dbl)
+{
+  gas_assert (ep->X_op == O_constant);
 
-      end_noreorder ();
-      break;
+  /* Sign-extending 32-bit constants makes their handling easier.  */
+  if (!dbl)
+    normalize_constant_expr (ep);
 
-    case M_ADD_I:
-      s = "addi";
-      s2 = "add";
-      goto do_addi;
-    case M_ADDU_I:
-      s = "addiu";
-      s2 = "addu";
-      goto do_addi;
-    case M_DADD_I:
-      dbl = 1;
-      s = "daddi";
-      s2 = "dadd";
-      goto do_addi;
-    case M_DADDU_I:
-      dbl = 1;
-      s = "daddiu";
-      s2 = "daddu";
-    do_addi:
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number >= -0x8000
-         && imm_expr.X_add_number < 0x8000)
-       {
-         macro_build (&imm_expr, s, "t,r,j", treg, sreg, BFD_RELOC_LO16);
-         break;
-       }
-      used_at = 1;
-      load_register (AT, &imm_expr, dbl);
-      macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
-      break;
+  /* Right now, this routine can only handle signed 32-bit constants.  */
+  if (! IS_SEXT_32BIT_NUM(ep->X_add_number + 0x8000))
+    as_warn (_("operand overflow"));
 
-    case M_AND_I:
-      s = "andi";
-      s2 = "and";
-      goto do_bit;
-    case M_OR_I:
-      s = "ori";
-      s2 = "or";
-      goto do_bit;
-    case M_NOR_I:
-      s = "";
-      s2 = "nor";
-      goto do_bit;
-    case M_XOR_I:
-      s = "xori";
-      s2 = "xor";
-    do_bit:
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number >= 0
-         && imm_expr.X_add_number < 0x10000)
-       {
-         if (mask != M_NOR_I)
-           macro_build (&imm_expr, s, "t,r,i", treg, sreg, BFD_RELOC_LO16);
-         else
-           {
-             macro_build (&imm_expr, "ori", "t,r,i",
-                          treg, sreg, BFD_RELOC_LO16);
-             macro_build (NULL, "nor", "d,v,t", treg, treg, 0);
-           }
-         break;
-       }
+  if (IS_SEXT_16BIT_NUM(ep->X_add_number))
+    {
+      /* Signed 16-bit offset will fit in the op.  Easy!  */
+      macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, breg);
+    }
+  else
+    {
+      /* 32-bit offset, need multiple instructions and AT, like:
+          lui      $tempreg,const_hi       (BFD_RELOC_HI16_S)
+          addu     $tempreg,$tempreg,$breg
+           <op>     $treg,const_lo($tempreg)   (BFD_RELOC_LO16)
+         to handle the complete offset.  */
+      macro_build_lui (ep, AT);
+      macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
+      macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, AT);
 
-      used_at = 1;
+      if (!mips_opts.at)
+       as_bad (_("Macro used $at after \".set noat\""));
+    }
+}
+
+/*                     set_at()
+ * Generates code to set the $at register to true (one)
+ * if reg is less than the immediate expression.
+ */
+static void
+set_at (int reg, int unsignedp)
+{
+  if (imm_expr.X_op == O_constant
+      && imm_expr.X_add_number >= -0x8000
+      && imm_expr.X_add_number < 0x8000)
+    macro_build (&imm_expr, unsignedp ? "sltiu" : "slti", "t,r,j",
+                AT, reg, BFD_RELOC_LO16);
+  else
+    {
       load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
-      break;
+      macro_build (NULL, unsignedp ? "sltu" : "slt", "d,v,t", AT, reg, AT);
+    }
+}
 
-    case M_BALIGN:
-      switch (imm_expr.X_add_number)
-       {
-       case 0:
-         macro_build (NULL, "nop", "");
-         break;
-       case 2:
-         macro_build (NULL, "packrl.ph", "d,s,t", treg, treg, sreg);
-         break;
-       default:
-         macro_build (NULL, "balign", "t,s,2", treg, sreg,
-                      (int) imm_expr.X_add_number);
-         break;
-       }
-      break;
+/* Warn if an expression is not a constant.  */
 
-    case M_BEQ_I:
-      s = "beq";
-      goto beq_i;
-    case M_BEQL_I:
-      s = "beql";
-      likely = 1;
-      goto beq_i;
-    case M_BNE_I:
-      s = "bne";
-      goto beq_i;
-    case M_BNEL_I:
-      s = "bnel";
-      likely = 1;
-    beq_i:
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
-       {
-         macro_build (&offset_expr, s, "s,t,p", sreg, ZERO);
-         break;
-       }
-      used_at = 1;
-      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build (&offset_expr, s, "s,t,p", sreg, AT);
-      break;
+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);
 
-    case M_BGEL:
-      likely = 1;
-    case M_BGE:
-      if (treg == 0)
-       {
-         macro_build (&offset_expr, likely ? "bgezl" : "bgez", "s,p", sreg);
-         break;
-       }
-      if (sreg == 0)
-       {
-         macro_build (&offset_expr, likely ? "blezl" : "blez", "s,p", treg);
-         break;
-       }
-      used_at = 1;
-      macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
-      macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, ZERO);
-      break;
+  if (HAVE_32BIT_GPRS)
+    normalize_constant_expr (ex);
+}
 
-    case M_BGTL_I:
-      likely = 1;
-    case M_BGT_I:
-      /* Check for > max integer.  */
-      maxnum = 0x7fffffff;
-      if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
-       {
-         maxnum <<= 16;
-         maxnum |= 0xffff;
-         maxnum <<= 16;
-         maxnum |= 0xffff;
-       }
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number >= maxnum
-         && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4))
-       {
-       do_false:
-         /* Result is always false.  */
-         if (! likely)
-           macro_build (NULL, "nop", "");
-         else
-           macro_build (&offset_expr, "bnel", "s,t,p", 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 (&offset_expr, likely ? "bgezl" : "bgez", "s,p", sreg);
-         break;
-       }
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+/* 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))
        {
-         macro_build (&offset_expr, likely ? "bgtzl" : "bgtz", "s,p", sreg);
-         break;
+         /* 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;
        }
-      maxnum = 0x7fffffff;
-      if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
+      else if (ep->X_add_number >= 0 && ep->X_add_number < 0x10000)
        {
-         maxnum <<= 16;
-         maxnum |= 0xffff;
-         maxnum <<= 16;
-         maxnum |= 0xffff;
+         /* 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;
        }
-      maxnum = - maxnum - 1;
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number <= maxnum
-         && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4))
+      else if ((IS_SEXT_32BIT_NUM (ep->X_add_number)))
        {
-       do_true:
-         /* result is always true */
-         as_warn (_("Branch %s is always true"), ip->insn_mo->name);
-         macro_build (&offset_expr, "b", "p");
-         break;
+         /* 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;
        }
-      used_at = 1;
-      set_at (sreg, 0);
-      macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, ZERO);
-      break;
+    }
 
-    case M_BGEUL:
-      likely = 1;
-    case M_BGEU:
-      if (treg == 0)
-       goto do_true;
-      if (sreg == 0)
-       {
-         macro_build (&offset_expr, likely ? "beql" : "beq",
-                      "s,t,p", ZERO, treg);
-         break;
-       }
-      used_at = 1;
-      macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
-      macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, ZERO);
-      break;
+  /* The value is larger than 32 bits.  */
 
-    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;
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
-       {
-         macro_build (&offset_expr, likely ? "bnel" : "bne",
-                      "s,t,p", sreg, ZERO);
-         break;
-       }
-      used_at = 1;
-      set_at (sreg, 1);
-      macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, ZERO);
-      break;
+  if (!dbl || HAVE_32BIT_GPRS)
+    {
+      char value[32];
 
-    case M_BGTL:
-      likely = 1;
-    case M_BGT:
-      if (treg == 0)
+      sprintf_vma (value, ep->X_add_number);
+      as_bad (_("Number (0x%s) larger than 32 bits"), value);
+      macro_build (ep, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
+      return;
+    }
+
+  if (ep->X_op != O_big)
+    {
+      hi32 = *ep;
+      hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
+      hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
+      hi32.X_add_number &= 0xffffffff;
+      lo32 = *ep;
+      lo32.X_add_number &= 0xffffffff;
+    }
+  else
+    {
+      gas_assert (ep->X_add_number > 2);
+      if (ep->X_add_number == 3)
+       generic_bignum[3] = 0;
+      else if (ep->X_add_number > 4)
+       as_bad (_("Number larger than 64 bits"));
+      lo32.X_op = O_constant;
+      lo32.X_add_number = generic_bignum[0] + (generic_bignum[1] << 16);
+      hi32.X_op = O_constant;
+      hi32.X_add_number = generic_bignum[2] + (generic_bignum[3] << 16);
+    }
+
+  if (hi32.X_add_number == 0)
+    freg = 0;
+  else
+    {
+      int shift, bit;
+      unsigned long hi, lo;
+
+      if (hi32.X_add_number == (offsetT) 0xffffffff)
        {
-         macro_build (&offset_expr, likely ? "bgtzl" : "bgtz", "s,p", sreg);
-         break;
+         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 (sreg == 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
        {
-         macro_build (&offset_expr, likely ? "bltzl" : "bltz", "s,p", treg);
-         break;
-       }
-      used_at = 1;
-      macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
-      macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, ZERO);
-      break;
+         unsigned long himask, lomask;
 
-    case M_BGTUL:
-      likely = 1;
-    case M_BGTU:
-      if (treg == 0)
-       {
-         macro_build (&offset_expr, likely ? "bnel" : "bne",
-                      "s,t,p", sreg, ZERO);
-         break;
-       }
-      if (sreg == 0)
-       goto do_false;
-      used_at = 1;
-      macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
-      macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, ZERO);
-      break;
+         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;
 
-    case M_BLEL:
-      likely = 1;
-    case M_BLE:
-      if (treg == 0)
-       {
-         macro_build (&offset_expr, likely ? "blezl" : "blez", "s,p", sreg);
-         break;
-       }
-      if (sreg == 0)
-       {
-         macro_build (&offset_expr, likely ? "bgezl" : "bgez", "s,p", treg);
-         break;
+             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;
        }
-      used_at = 1;
-      macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
-      macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, ZERO);
-      break;
+      while (shift <= (64 - 16));
 
-    case M_BLEL_I:
-      likely = 1;
-    case M_BLE_I:
-      maxnum = 0x7fffffff;
-      if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
-       {
-         maxnum <<= 16;
-         maxnum |= 0xffff;
-         maxnum <<= 16;
-         maxnum |= 0xffff;
-       }
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number >= maxnum
-         && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4))
-       goto do_true;
-      if (imm_expr.X_op != O_constant)
-       as_bad (_("Unsupported large constant"));
-      ++imm_expr.X_add_number;
-      /* FALLTHROUGH */
-    case M_BLT_I:
-    case M_BLTL_I:
-      if (mask == M_BLTL_I)
-       likely = 1;
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+      /* 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 (&offset_expr, likely ? "bltzl" : "bltz", "s,p", sreg);
-         break;
+         bit = 0;
+         while ((lo & 1) == 0)
+           {
+             lo >>= 1;
+             ++bit;
+           }
+         lo |= (hi & (((unsigned long) 1 << bit) - 1)) << (32 - bit);
+         hi >>= bit;
        }
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+      else
        {
-         macro_build (&offset_expr, likely ? "blezl" : "blez", "s,p", sreg);
-         break;
+         bit = 32;
+         while ((hi & 1) == 0)
+           {
+             hi >>= 1;
+             ++bit;
+           }
+         lo = hi;
+         hi = 0;
        }
-      used_at = 1;
-      set_at (sreg, 0);
-      macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, ZERO);
-      break;
 
-    case M_BLEUL:
-      likely = 1;
-    case M_BLEU:
-      if (treg == 0)
+      /* Optimize if the shifted value is a (power of 2) - 1.  */
+      if ((hi == 0 && ((lo + 1) & lo) == 0)
+         || (lo == 0xffffffff && ((hi + 1) & hi) == 0))
        {
-         macro_build (&offset_expr, likely ? "beql" : "beq",
-                      "s,t,p", sreg, ZERO);
-         break;
-       }
-      if (sreg == 0)
-       goto do_true;
-      used_at = 1;
-      macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
-      macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, ZERO);
-      break;
+         shift = COUNT_TOP_ZEROES ((unsigned int) hi32.X_add_number);
+         if (shift != 0)
+           {
+             expressionS tmp;
 
-    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;
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
-       {
-         macro_build (&offset_expr, likely ? "beql" : "beq",
-                      "s,t,p", sreg, ZERO);
-         break;
+             /* 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;
+           }
        }
-      used_at = 1;
-      set_at (sreg, 1);
-      macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, ZERO);
-      break;
 
-    case M_BLTL:
-      likely = 1;
-    case M_BLT:
-      if (treg == 0)
+      /* 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)
        {
-         macro_build (&offset_expr, likely ? "bltzl" : "bltz", "s,p", sreg);
-         break;
+         macro_build (NULL, "dsll32", SHFT_FMT, reg, freg, 0);
+         freg = reg;
        }
-      if (sreg == 0)
+    }
+  else
+    {
+      expressionS mid16;
+
+      if ((freg == 0) && (lo32.X_add_number == (offsetT) 0xffffffff))
        {
-         macro_build (&offset_expr, likely ? "bgtzl" : "bgtz", "s,p", treg);
-         break;
+         macro_build (&lo32, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
+         macro_build (NULL, "dsrl32", SHFT_FMT, reg, reg, 0);
+         return;
        }
-      used_at = 1;
-      macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
-      macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, ZERO);
-      break;
 
-    case M_BLTUL:
-      likely = 1;
-    case M_BLTU:
-      if (treg == 0)
-       goto do_false;
-      if (sreg == 0)
+      if (freg != 0)
        {
-         macro_build (&offset_expr, likely ? "bnel" : "bne",
-                      "s,t,p", ZERO, treg);
-         break;
+         macro_build (NULL, "dsll", SHFT_FMT, reg, freg, 16);
+         freg = reg;
        }
-      used_at = 1;
-      macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
-      macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, ZERO);
-      break;
-
-    case M_DEXT:
-      {
-       unsigned long pos;
-       unsigned long size;
-
-       if (imm_expr.X_op != O_constant || imm2_expr.X_op != O_constant)
-         {
-           as_bad (_("Unsupported large constant"));
-           pos = size = 1;
-         }
-       else
-         {
-           pos = (unsigned long) imm_expr.X_add_number;
-           size = (unsigned long) imm2_expr.X_add_number;
-         }
+      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);
+}
 
-       if (pos > 63)
-         {
-           as_bad (_("Improper position (%lu)"), pos);
-           pos = 1;
-         }
-       if (size == 0 || size > 64 || (pos + size - 1) > 63)
-         {
-           as_bad (_("Improper extract size (%lu, position %lu)"),
-                   size, pos);
-           size = 1;
-         }
+static inline void
+load_delay_nop (void)
+{
+  if (!gpr_interlocks)
+    macro_build (NULL, "nop", "");
+}
 
-       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, pos, size - 1);
-      }
-      break;
+/* Load an address into a register.  */
 
-    case M_DINS:
-      {
-       unsigned long pos;
-       unsigned long size;
+static void
+load_address (int reg, expressionS *ep, int *used_at)
+{
+  if (ep->X_op != O_constant
+      && ep->X_op != O_symbol)
+    {
+      as_bad (_("expression too complex"));
+      ep->X_op = O_constant;
+    }
 
-       if (imm_expr.X_op != O_constant || imm2_expr.X_op != O_constant)
-         {
-           as_bad (_("Unsupported large constant"));
-           pos = size = 1;
-         }
-       else
-         {
-           pos = (unsigned long) imm_expr.X_add_number;
-           size = (unsigned long) imm2_expr.X_add_number;
-         }
+  if (ep->X_op == O_constant)
+    {
+      load_register (reg, ep, HAVE_64BIT_ADDRESSES);
+      return;
+    }
 
-       if (pos > 63)
-         {
-           as_bad (_("Improper position (%lu)"), pos);
-           pos = 1;
-         }
-       if (size == 0 || size > 64 || (pos + size - 1) > 63)
-         {
-           as_bad (_("Improper insert size (%lu, position %lu)"),
-                   size, pos);
-           size = 1;
-         }
+  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 (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;
+        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
 
-    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)
+        If $at is already in use, we use a path which is suboptimal
+        on superscalar processors.
+          lui          $reg,<sym>              (BFD_RELOC_MIPS_HIGHEST)
+          daddiu       $reg,<sym>              (BFD_RELOC_MIPS_HIGHER)
+          dsll         $reg,16
+          daddiu       $reg,<sym>              (BFD_RELOC_HI16_S)
+          dsll         $reg,16
+          daddiu       $reg,<sym>              (BFD_RELOC_LO16)
+
+        For GP relative symbols in 64bit address space we can use
+        the same sequence as in 32bit address space.  */
+      if (HAVE_64BIT_SYMBOLS)
        {
-         as_warn (_("Divide by zero."));
-         if (mips_trap)
-           macro_build (NULL, "teq", "s,t,q", ZERO, ZERO, 7);
+         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 (NULL, "break", "c", 7);
-         break;
-       }
+           {
+             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);
+           }
 
-      start_noreorder ();
-      if (mips_trap)
-       {
-         macro_build (NULL, "teq", "s,t,q", treg, ZERO, 7);
-         macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
+         if (mips_relax.sequence)
+           relax_end ();
        }
       else
        {
-         expr1.X_add_number = 8;
-         macro_build (&expr1, "bne", "s,t,p", treg, ZERO);
-         macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
-         macro_build (NULL, "break", "c", 7);
+         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 ();
        }
-      expr1.X_add_number = -1;
-      used_at = 1;
-      load_register (AT, &expr1, dbl);
-      expr1.X_add_number = mips_trap ? (dbl ? 12 : 8) : (dbl ? 20 : 16);
-      macro_build (&expr1, "bne", "s,t,p", treg, AT);
-      if (dbl)
+    }
+  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.
+
+        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)
        {
-         expr1.X_add_number = 1;
-         load_register (AT, &expr1, dbl);
-         macro_build (NULL, "dsll32", "d,w,<", AT, AT, 31);
+         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
        {
-         expr1.X_add_number = 0x80000000;
-         macro_build (&expr1, "lui", "t,u", AT, BFD_RELOC_HI16);
+         ex.X_add_number = ep->X_add_number;
+         ep->X_add_number = 0;
+         macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+                      BFD_RELOC_MIPS_GOT16, mips_gp_register);
+         load_delay_nop ();
+         relax_start (ep->X_add_symbol);
+         relax_switch ();
+         macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+                      BFD_RELOC_LO16);
+         relax_end ();
+
+         if (ex.X_add_number != 0)
+           {
+             if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+               as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+             ex.X_op = O_constant;
+             macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j",
+                          reg, reg, BFD_RELOC_LO16);
+           }
        }
-      if (mips_trap)
+    }
+  else if (mips_big_got)
+    {
+      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)
+
+        Otherwise, for a reference to a local symbol in old ABI, we want
+          lw           $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT16)
+          nop
+          addiu        $reg,$reg,<sym>         (BFD_RELOC_LO16)
+        If there is a constant, it must be added in after.
+
+        In the NewABI, for local symbols, with or without offsets, we want:
+          lw           $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT_PAGE)
+          addiu        $reg,$reg,<sym>         (BFD_RELOC_MIPS_GOT_OFST)
+      */
+      if (HAVE_NEWABI)
        {
-         macro_build (NULL, "teq", "s,t,q", 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 ();
+         ex.X_add_number = ep->X_add_number;
+         ep->X_add_number = 0;
+         relax_start (ep->X_add_symbol);
+         macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_GOT_HI16);
+         macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                      reg, reg, mips_gp_register);
+         macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)",
+                      reg, BFD_RELOC_MIPS_GOT_LO16, reg);
+         if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+           as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+         else if (ex.X_add_number)
+           {
+             ex.X_op = O_constant;
+             macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+                          BFD_RELOC_LO16);
+           }
+
+         ep->X_add_number = ex.X_add_number;
+         relax_switch ();
+         macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+                      BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
+         macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+                      BFD_RELOC_MIPS_GOT_OFST);
+         relax_end ();
        }
       else
        {
-         expr1.X_add_number = 8;
-         macro_build (&expr1, "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", "c", 6);
-       }
-      macro_build (NULL, s, "d", 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", "s,t,q", ZERO, ZERO, 7);
-         else
-           macro_build (NULL, "break", "c", 7);
-         break;
-       }
-      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
-       {
-         if (strcmp (s2, "mflo") == 0)
-           move_register (dreg, sreg);
-         else
-           move_register (dreg, ZERO);
-         break;
-       }
-      if (imm_expr.X_op == O_constant
-         && imm_expr.X_add_number == -1
-         && s[strlen (s) - 1] != 'u')
-       {
-         if (strcmp (s2, "mflo") == 0)
+         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))
            {
-             macro_build (NULL, dbl ? "dneg" : "neg", "d,w", dreg, sreg);
+             /* 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 ();
+
+         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
-           move_register (dreg, ZERO);
-         break;
        }
+    }
+  else
+    abort ();
 
-      used_at = 1;
-      load_register (AT, &imm_expr, dbl);
-      macro_build (NULL, s, "z,s,t", sreg, AT);
-      macro_build (NULL, s2, "d", dreg);
-      break;
+  if (!mips_opts.at && *used_at == 1)
+    as_bad (_("Macro used $at after \".set noat\""));
+}
 
-    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", "s,t,q", 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
-       {
-         expr1.X_add_number = 8;
-         macro_build (&expr1, "bne", "s,t,p", treg, ZERO);
-         macro_build (NULL, s, "z,s,t", sreg, treg);
+/* Move the contents of register SOURCE into register DEST.  */
 
-         /* 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", "c", 7);
-       }
-      macro_build (NULL, s2, "d", dreg);
-      break;
+static void
+move_register (int dest, int source)
+{
+  /* Prefer to use a 16-bit microMIPS instruction unless the previous
+     instruction specifically requires a 32-bit one.  */
+  if (mips_opts.micromips
+      && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
+    macro_build (NULL, "move", "mp,mj", dest, source);
+  else
+    macro_build (NULL, HAVE_32BIT_GPRS ? "addu" : "daddu", "d,v,t",
+                dest, source, 0);
+}
 
-    case M_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.  */
+/* 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 (dbl && HAVE_32BIT_GPRS)
-       as_warn (_("dla used to load 32-bit register"));
+   Global symbol               Local sybmol
+   -------------               ------------
+   lw DEST,%got(SYMBOL)                lw DEST,%got(SYMBOL + OFFSET)
+   ...                         ...
+   addiu DEST,DEST,OFFSET      addiu DEST,DEST,%lo(SYMBOL + OFFSET)
 
-      if (!dbl && HAVE_64BIT_OBJECTS)
-       as_warn (_("la used to load 64-bit address"));
+   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 (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;
-       }
+static void
+load_got_offset (int dest, expressionS *local)
+{
+  expressionS global;
 
-      if (mips_opts.at && (treg == breg))
-       {
-         tempreg = AT;
-         used_at = 1;
-       }
-      else
-       {
-         tempreg = treg;
-       }
+  global = *local;
+  global.X_add_number = 0;
 
-      if (offset_expr.X_op != O_symbol
-         && offset_expr.X_op != O_constant)
-       {
-         as_bad (_("Expression too complex"));
-         offset_expr.X_op = O_constant;
-       }
+  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 ();
+}
 
-      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.
+static void
+add_got_offset (int dest, expressionS *local)
+{
+  expressionS global;
 
-            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
+  global.X_op = O_constant;
+  global.X_op_symbol = NULL;
+  global.X_add_symbol = NULL;
+  global.X_add_number = local->X_add_number;
 
-            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)
+  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 ();
+}
 
-            For GP relative symbols in 64bit address space we can use
-            the same sequence as in 32bit address space.  */
-         if (HAVE_64BIT_SYMBOLS)
-           {
-             if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
-                 && !nopic_need_relax (offset_expr.X_add_symbol, 1))
-               {
-                 relax_start (offset_expr.X_add_symbol);
-                 macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
-                              tempreg, mips_gp_register, BFD_RELOC_GPREL16);
-                 relax_switch ();
-               }
+static void
+add_got_offset_hilo (int dest, expressionS *local, int tmp)
+{
+  expressionS global;
+  int hold_mips_optimize;
 
-             if (used_at == 0 && mips_opts.at)
-               {
-                 macro_build (&offset_expr, "lui", "t,u",
-                              tempreg, BFD_RELOC_MIPS_HIGHEST);
-                 macro_build (&offset_expr, "lui", "t,u",
-                              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", "d,w,<", tempreg, tempreg, 0);
-                 macro_build (NULL, "daddu", "d,v,t", tempreg, tempreg, AT);
-                 used_at = 1;
-               }
-             else
-               {
-                 macro_build (&offset_expr, "lui", "t,u",
-                              tempreg, BFD_RELOC_MIPS_HIGHEST);
-                 macro_build (&offset_expr, "daddiu", "t,r,j",
-                              tempreg, tempreg, BFD_RELOC_MIPS_HIGHER);
-                 macro_build (NULL, "dsll", "d,w,<", tempreg, tempreg, 16);
-                 macro_build (&offset_expr, "daddiu", "t,r,j",
-                              tempreg, tempreg, BFD_RELOC_HI16_S);
-                 macro_build (NULL, "dsll", "d,w,<", tempreg, tempreg, 16);
-                 macro_build (&offset_expr, "daddiu", "t,r,j",
-                              tempreg, tempreg, BFD_RELOC_LO16);
-               }
+  global.X_op = O_constant;
+  global.X_op_symbol = NULL;
+  global.X_add_symbol = NULL;
+  global.X_add_number = local->X_add_number;
 
-             if (mips_relax.sequence)
-               relax_end ();
-           }
-         else
-           {
-             if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
-                 && !nopic_need_relax (offset_expr.X_add_symbol, 1))
-               {
-                 relax_start (offset_expr.X_add_symbol);
-                 macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
-                              tempreg, mips_gp_register, BFD_RELOC_GPREL16);
-                 relax_switch ();
-               }
-             if (!IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
-               as_bad (_("Offset too large"));
-             macro_build_lui (&offset_expr, tempreg);
-             macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
-                          tempreg, tempreg, BFD_RELOC_LO16);
-             if (mips_relax.sequence)
-               relax_end ();
-           }
-       }
-      else if (!mips_big_got && !HAVE_NEWABI)
-       {
-         int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
+  relax_start (local->X_add_symbol);
+  load_register (tmp, &global, HAVE_64BIT_ADDRESSES);
+  relax_switch ();
+  /* Set mips_optimize around the lui instruction to avoid
+     inserting an unnecessary nop after the lw.  */
+  hold_mips_optimize = mips_optimize;
+  mips_optimize = 2;
+  macro_build_lui (&global, tmp);
+  mips_optimize = hold_mips_optimize;
+  macro_build (local, ADDRESS_ADDI_INSN, "t,r,j", tmp, tmp, BFD_RELOC_LO16);
+  relax_end ();
 
-         /* If 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)
+  macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dest, dest, tmp);
+}
 
-            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.
+/* 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 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.
-          */
+   Code like this is produced in the noreorder mode:
 
-         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;
+       BRNEG   <args>, 1f
+        nop
+       b       <sym>
+        delay slot (executed only if branch taken)
+    1:
 
-             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 =
-               ((offset_expr.X_add_number + 0x8000) & 0xffff) - 0x8000;
-             load_got_offset (tempreg, &offset_expr);
-             offset_expr.X_add_number = expr1.X_add_number;
-             /* If we are going to add in a base register, and the
-                target register and the base register are the same,
-                then we are using AT as a temporary register.  Since
-                we want to load the constant into AT, we add our
-                current AT (from the global offset table) and the
-                register into the register now, and pretend we were
-                not using a base register.  */
-             if (breg == treg)
-               {
-                 load_delay_nop ();
-                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                              treg, AT, breg);
-                 breg = 0;
-                 tempreg = treg;
-               }
-             add_got_offset_hilo (tempreg, &offset_expr, AT);
-             used_at = 1;
-           }
-       }
-      else if (!mips_big_got && HAVE_NEWABI)
-       {
-         int add_breg_early = 0;
+   or, if CALL is set:
 
-         /* 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)
+       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
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_DISP)
-              addiu    $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
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_DISP)
-              lui      $at,<hiconstant>
-              addiu    $at,$at,<loconstant>
-              addu     $tempreg,$tempreg,$at
+       BR      <args>, <sym>
+        nop
 
-            (*) Other assemblers seem to prefer GOT_PAGE/GOT_OFST for
-            local symbols, even though it introduces an additional
-            instruction.  */
+   This function is used when producing code for the microMIPS ASE that
+   does not implement branch likely instructions in hardware.  */
 
-         if (offset_expr.X_add_number)
-           {
-             expr1.X_add_number = offset_expr.X_add_number;
-             offset_expr.X_add_number = 0;
+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;
 
-             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);
+  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 >= -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;
-                   }
+      /* 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 ();
+}
 
-                 load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
-                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                              dreg, dreg, AT);
+/* Emit a coprocessor branch-likely macro specified by TYPE, using CC as
+   the condition code tested.  EP specifies the branch target.  */
 
-                 used_at = 1;
-               }
-             else
-               as_bad (_("PIC code offset overflow (max 32 signed bits)"));
+static void
+macro_build_branch_ccl (int type, expressionS *ep, unsigned int cc)
+{
+  const int call = 0;
+  const char *brneg;
+  const char *br;
 
-             relax_switch ();
-             offset_expr.X_add_number = expr1.X_add_number;
+  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);
+}
 
-             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;
+/* Emit a two-argument branch macro specified by TYPE, using SREG as
+   the register tested.  EP specifies the branch target.  */
 
-         /* 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)
+static void
+macro_build_branch_rs (int type, expressionS *ep, unsigned int sreg)
+{
+  const char *brneg = NULL;
+  const char *br;
+  int call = 0;
 
-            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)
+  switch (type)
+    {
+    case M_BGEZ:
+      br = "bgez";
+      break;
+    case M_BGEZL:
+      br = mips_opts.micromips ? "bgez" : "bgezl";
+      brneg = "bltz";
+      break;
+    case M_BGEZALL:
+      gas_assert (mips_opts.micromips);
+      br = "bgezals";
+      brneg = "bltz";
+      call = 1;
+      break;
+    case M_BGTZ:
+      br = "bgtz";
+      break;
+    case M_BGTZL:
+      br = mips_opts.micromips ? "bgtz" : "bgtzl";
+      brneg = "blez";
+      break;
+    case M_BLEZ:
+      br = "blez";
+      break;
+    case M_BLEZL:
+      br = mips_opts.micromips ? "blez" : "blezl";
+      brneg = "bgtz";
+      break;
+    case M_BLTZ:
+      br = "bltz";
+      break;
+    case M_BLTZL:
+      br = mips_opts.micromips ? "bltz" : "bltzl";
+      brneg = "bgez";
+      break;
+    case M_BLTZALL:
+      gas_assert (mips_opts.micromips);
+      br = "bltzals";
+      brneg = "bgez";
+      call = 1;
+      break;
+    default:
+      abort ();
+    }
+  if (mips_opts.micromips && brneg)
+    macro_build_branch_likely (br, brneg, call, ep, "s,p", sreg, ZERO);
+  else
+    macro_build (ep, br, "s,p", sreg);
+}
 
-            If we have a 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
-         */
+/* Emit a three-argument branch macro specified by TYPE, using SREG and
+   TREG as the registers tested.  EP specifies the branch target.  */
 
-         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", "t,u", 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;
-               }
+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;
 
-             load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
-             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
+  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);
+}
 
-             used_at = 1;
-           }
-         offset_expr.X_add_number =
-           ((expr1.X_add_number + 0x8000) & 0xffff) - 0x8000;
-         relax_switch ();
+/*
+ *                     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)
+{
+  unsigned int treg, sreg, dreg, breg;
+  unsigned int tempreg;
+  int mask;
+  int used_at = 0;
+  expressionS label_expr;
+  expressionS expr1;
+  expressionS *ep;
+  const char *s;
+  const char *s2;
+  const char *fmt;
+  int likely = 0;
+  int coproc = 0;
+  int offbits = 16;
+  int call = 0;
+  int jals = 0;
+  int dbl = 0;
+  int imm = 0;
+  int ust = 0;
+  int lp = 0;
+  int ab = 0;
+  int off;
+  offsetT maxnum;
+  bfd_reloc_code_real_type r;
+  int hold_mips_optimize;
 
-         if (gpdelay)
-           {
-             /* This is needed because this instruction uses $gp, but
-                the first instruction on the main stream does not.  */
-             macro_build (NULL, "nop", "");
-           }
+  gas_assert (! mips_opts.mips16);
 
-         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;
-               }
+  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;
 
-             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;
+  label_expr.X_op = O_constant;
+  label_expr.X_op_symbol = NULL;
+  label_expr.X_add_symbol = NULL;
+  label_expr.X_add_number = 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)
+  expr1.X_op = O_constant;
+  expr1.X_op_symbol = NULL;
+  expr1.X_add_symbol = NULL;
+  expr1.X_add_number = 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>
+  switch (mask)
+    {
+    case M_DABS:
+      dbl = 1;
+    case M_ABS:
+      /*    bgez    $a0,1f
+           move    v0,$a0
+           sub     v0,$zero,$a0
+        1:
+       */
 
-            If we have a large constant, and this is a reference to
-            an external symbol, we want
-              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
-              addu     $tempreg,$tempreg,$gp
-              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
-              lui      $at,<hiconstant>
-              addi     $at,$at,<loconstant>
-              add      $tempreg,$tempreg,$at
-
-            If we have NewABI, and we know it's a local symbol, we want
-              lw       $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT_PAGE)
-              addiu    $reg,$reg,<sym>         (BFD_RELOC_MIPS_GOT_OFST)
-            otherwise we have to resort to GOT_HI16/GOT_LO16.  */
-
-         relax_start (offset_expr.X_add_symbol);
-
-         expr1.X_add_number = offset_expr.X_add_number;
-         offset_expr.X_add_number = 0;
+      start_noreorder ();
 
-         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", "t,u", 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 (mips_opts.micromips)
+       micromips_label_expr (&label_expr);
+      else
+       label_expr.X_add_number = 8;
+      macro_build (&label_expr, "bgez", "s,p", sreg);
+      if (dreg == sreg)
+       macro_build (NULL, "nop", "");
+      else
+       move_register (dreg, sreg);
+      macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", dreg, 0, sreg);
+      if (mips_opts.micromips)
+       micromips_add_label ();
 
-         if (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;
-               }
+      end_noreorder ();
+      break;
 
-             load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
-             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
+    case M_ADD_I:
+      s = "addi";
+      s2 = "add";
+      goto do_addi;
+    case M_ADDU_I:
+      s = "addiu";
+      s2 = "addu";
+      goto do_addi;
+    case M_DADD_I:
+      dbl = 1;
+      s = "daddi";
+      s2 = "dadd";
+      if (!mips_opts.micromips)
+       goto do_addi;
+      if (imm_expr.X_op == O_constant
+         && imm_expr.X_add_number >= -0x200
+         && imm_expr.X_add_number < 0x200)
+       {
+         macro_build (NULL, s, "t,r,.", treg, sreg, imm_expr.X_add_number);
+         break;
+       }
+      goto do_addi_i;
+    case M_DADDU_I:
+      dbl = 1;
+      s = "daddiu";
+      s2 = "daddu";
+    do_addi:
+      if (imm_expr.X_op == O_constant
+         && imm_expr.X_add_number >= -0x8000
+         && imm_expr.X_add_number < 0x8000)
+       {
+         macro_build (&imm_expr, s, "t,r,j", treg, sreg, BFD_RELOC_LO16);
+         break;
+       }
+    do_addi_i:
+      used_at = 1;
+      load_register (AT, &imm_expr, dbl);
+      macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
+      break;
 
-             used_at = 1;
-           }
+    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
-           as_bad (_("PIC code offset overflow (max 32 signed bits)"));
-
-         relax_switch ();
-         offset_expr.X_add_number = expr1.X_add_number;
-         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
-                      BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
-         macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
-                      tempreg, BFD_RELOC_MIPS_GOT_OFST);
-         if (add_breg_early)
            {
-             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                          treg, tempreg, breg);
-             breg = 0;
-             tempreg = treg;
+             macro_build (&imm_expr, "ori", "t,r,i",
+                          treg, sreg, BFD_RELOC_LO16);
+             macro_build (NULL, "nor", "d,v,t", treg, treg, 0);
            }
-         relax_end ();
+         break;
        }
-      else
-       abort ();
 
-      if (breg != 0)
-       macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, tempreg, breg);
+      used_at = 1;
+      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+      macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
       break;
 
-    case M_MSGSND:
-      {
-       unsigned long temp = (treg << 16) | (0x01);
-       macro_build (NULL, "c2", "C", temp);
-      }
-      /* AT is not used, just return */
-      return;
-
-    case M_MSGLD:
-      {
-       unsigned long temp = (0x02);
-       macro_build (NULL, "c2", "C", temp);
-      }
-      /* AT is not used, just return */
-      return;
-
-    case M_MSGLD_T:
-      {
-       unsigned long temp = (treg << 16) | (0x02);
-       macro_build (NULL, "c2", "C", temp);
-      }
-      /* AT is not used, just return */
-      return;
-
-    case M_MSGWAIT:
-      macro_build (NULL, "c2", "C", 3);
-      /* AT is not used, just return */
-      return;
+    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;
 
-    case M_MSGWAIT_T:
-      {
-       unsigned long temp = (treg << 16) | 0x03;
-       macro_build (NULL, "c2", "C", temp);
-      }
-      /* AT is not used, just return */
-      return;
+    case M_BC1FL:
+    case M_BC1TL:
+    case M_BC2FL:
+    case M_BC2TL:
+      gas_assert (mips_opts.micromips);
+      macro_build_branch_ccl (mask, &offset_expr,
+                             EXTRACT_OPERAND (1, BCC, *ip));
+      break;
 
-    case M_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");
+    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
-       macro_build (&offset_expr, "b", "p");
+       {
+         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;
 
-      /* 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_JAL_1:
-      dreg = RA;
-      /* Fall through.  */
-    case M_JAL_2:
-      if (mips_pic == NO_PIC)
-       macro_build (NULL, "jalr", "d,s", dreg, sreg);
+    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
        {
-         if (sreg != PIC_CALL_REG)
-           as_warn (_("MIPS PIC call to register other than $25"));
-
-         macro_build (NULL, "jalr", "d,s", 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);
-               }
-           }
+         used_at = 1;
+         macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
+         macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+                                  &offset_expr, AT, ZERO);
        }
+      break;
 
+    case M_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;
 
-    case M_JAL_A:
-      if (mips_pic == NO_PIC)
-       macro_build (&offset_expr, "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
+    case M_BGTL_I:
+      likely = 1;
+    case M_BGT_I:
+      /* Check for > max integer.  */
+      maxnum = 0x7fffffff;
+      if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
+       {
+         maxnum <<= 16;
+         maxnum |= 0xffff;
+         maxnum <<= 16;
+         maxnum |= 0xffff;
+       }
+      if (imm_expr.X_op == O_constant
+         && imm_expr.X_add_number >= maxnum
+         && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4))
+       {
+       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;
+       }
+      maxnum = 0x7fffffff;
+      if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
+       {
+         maxnum <<= 16;
+         maxnum |= 0xffff;
+         maxnum <<= 16;
+         maxnum |= 0xffff;
+       }
+      maxnum = - maxnum - 1;
+      if (imm_expr.X_op == O_constant
+         && imm_expr.X_add_number <= maxnum
+         && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4))
+       {
+       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;
+
+    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;
+
+    case M_BGTUL_I:
+      likely = 1;
+    case M_BGTU_I:
+      if (sreg == 0
+         || (HAVE_32BIT_GPRS
+             && imm_expr.X_op == O_constant
+             && imm_expr.X_add_number == -1))
+       goto do_false;
+      if (imm_expr.X_op != O_constant)
+       as_bad (_("Unsupported large constant"));
+      ++imm_expr.X_add_number;
+      /* FALLTHROUGH */
+    case M_BGEU_I:
+    case M_BGEUL_I:
+      if (mask == M_BGEUL_I)
+       likely = 1;
+      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+       goto do_true;
+      else if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+       macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+                                &offset_expr, sreg, ZERO);
+      else
+       {
+         used_at = 1;
+         set_at (sreg, 1);
+         macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+                                  &offset_expr, AT, ZERO);
+       }
+      break;
+
+    case M_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;
+
+    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;
+
+    case M_BLEL:
+      likely = 1;
+    case M_BLE:
+      if (treg == 0)
+       macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, sreg);
+      else if (sreg == 0)
+       macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ, &offset_expr, treg);
+      else
+       {
+         used_at = 1;
+         macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
+         macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+                                  &offset_expr, AT, ZERO);
+       }
+      break;
+
+    case M_BLEL_I:
+      likely = 1;
+    case M_BLE_I:
+      maxnum = 0x7fffffff;
+      if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
+       {
+         maxnum <<= 16;
+         maxnum |= 0xffff;
+         maxnum <<= 16;
+         maxnum |= 0xffff;
+       }
+      if (imm_expr.X_op == O_constant
+         && imm_expr.X_add_number >= maxnum
+         && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4))
+       goto do_true;
+      if (imm_expr.X_op != O_constant)
+       as_bad (_("Unsupported large constant"));
+      ++imm_expr.X_add_number;
+      /* FALLTHROUGH */
+    case M_BLT_I:
+    case M_BLTL_I:
+      if (mask == M_BLTL_I)
+       likely = 1;
+      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+       macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, sreg);
+      else if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+       macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, sreg);
+      else
+       {
+         used_at = 1;
+         set_at (sreg, 0);
+         macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+                                  &offset_expr, AT, ZERO);
+       }
+      break;
+
+    case M_BLEUL:
+      likely = 1;
+    case M_BLEU:
+      if (treg == 0)
+       macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+                                &offset_expr, sreg, ZERO);
+      else if (sreg == 0)
+       goto do_true;
+      else
+       {
+         used_at = 1;
+         macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
+         macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+                                  &offset_expr, AT, ZERO);
+       }
+      break;
+
+    case M_BLEUL_I:
+      likely = 1;
+    case M_BLEU_I:
+      if (sreg == 0
+         || (HAVE_32BIT_GPRS
+             && imm_expr.X_op == O_constant
+             && imm_expr.X_add_number == -1))
+       goto do_true;
+      if (imm_expr.X_op != O_constant)
+       as_bad (_("Unsupported large constant"));
+      ++imm_expr.X_add_number;
+      /* FALLTHROUGH */
+    case M_BLTU_I:
+    case M_BLTUL_I:
+      if (mask == M_BLTUL_I)
+       likely = 1;
+      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+       goto do_false;
+      else if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+       macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+                                &offset_expr, sreg, ZERO);
+      else
+       {
+         used_at = 1;
+         set_at (sreg, 1);
+         macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+                                  &offset_expr, AT, ZERO);
+       }
+      break;
+
+    case M_BLTL:
+      likely = 1;
+    case M_BLT:
+      if (treg == 0)
+       macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, sreg);
+      else if (sreg == 0)
+       macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ, &offset_expr, treg);
+      else
+       {
+         used_at = 1;
+         macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
+         macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+                                  &offset_expr, AT, ZERO);
+       }
+      break;
+
+    case M_BLTUL:
+      likely = 1;
+    case M_BLTU:
+      if (treg == 0)
+       goto do_false;
+      else if (sreg == 0)
+       macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+                                &offset_expr, ZERO, treg);
+      else
+       {
+         used_at = 1;
+         macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
+         macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+                                  &offset_expr, AT, ZERO);
+       }
+      break;
+
+    case M_DEXT:
+      {
+       /* Use unsigned arithmetic.  */
+       addressT pos;
+       addressT size;
+
+       if (imm_expr.X_op != O_constant || imm2_expr.X_op != O_constant)
+         {
+           as_bad (_("Unsupported large constant"));
+           pos = size = 1;
+         }
+       else
+         {
+           pos = imm_expr.X_add_number;
+           size = imm2_expr.X_add_number;
+         }
+
+       if (pos > 63)
+         {
+           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;
+         }
+
+       if (size <= 32 && pos < 32)
+         {
+           s = "dext";
+           fmt = "t,r,+A,+C";
+         }
+       else if (size <= 32)
+         {
+           s = "dextu";
+           fmt = "t,r,+E,+H";
+         }
+       else
+         {
+           s = "dextm";
+           fmt = "t,r,+A,+G";
+         }
+       macro_build ((expressionS *) NULL, s, fmt, treg, sreg, (int) pos,
+                    (int) (size - 1));
+      }
+      break;
+
+    case M_DINS:
+      {
+       /* Use unsigned arithmetic.  */
+       addressT pos;
+       addressT size;
+
+       if (imm_expr.X_op != O_constant || imm2_expr.X_op != O_constant)
+         {
+           as_bad (_("Unsupported large constant"));
+           pos = size = 1;
+         }
+       else
+         {
+           pos = imm_expr.X_add_number;
+           size = imm2_expr.X_add_number;
+         }
+
+       if (pos > 63)
+         {
+           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 (pos < 32 && (pos + size - 1) < 32)
+         {
+           s = "dins";
+           fmt = "t,r,+A,+B";
+         }
+       else if (pos >= 32)
+         {
+           s = "dinsu";
+           fmt = "t,r,+E,+F";
+         }
+       else
+         {
+           s = "dinsm";
+           fmt = "t,r,+A,+F";
+         }
+       macro_build ((expressionS *) NULL, s, fmt, treg, sreg, (int) pos,
+                    (int) (pos + size - 1));
+      }
+      break;
+
+    case M_DDIV_3:
+      dbl = 1;
+    case M_DIV_3:
+      s = "mflo";
+      goto do_div3;
+    case M_DREM_3:
+      dbl = 1;
+    case M_REM_3:
+      s = "mfhi";
+    do_div3:
+      if (treg == 0)
+       {
+         as_warn (_("Divide by zero."));
+         if (mips_trap)
+           macro_build (NULL, "teq", TRAP_FMT, ZERO, ZERO, 7);
+         else
+           macro_build (NULL, "break", BRK_FMT, 7);
+         break;
+       }
+
+      start_noreorder ();
+      if (mips_trap)
+       {
+         macro_build (NULL, "teq", TRAP_FMT, treg, ZERO, 7);
+         macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
+       }
+      else
+       {
+         if (mips_opts.micromips)
+           micromips_label_expr (&label_expr);
+         else
+           label_expr.X_add_number = 8;
+         macro_build (&label_expr, "bne", "s,t,p", treg, ZERO);
+         macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
+         macro_build (NULL, "break", BRK_FMT, 7);
+         if (mips_opts.micromips)
+           micromips_add_label ();
+       }
+      expr1.X_add_number = -1;
+      used_at = 1;
+      load_register (AT, &expr1, dbl);
+      if (mips_opts.micromips)
+       micromips_label_expr (&label_expr);
+      else
+       label_expr.X_add_number = mips_trap ? (dbl ? 12 : 8) : (dbl ? 20 : 16);
+      macro_build (&label_expr, "bne", "s,t,p", treg, AT);
+      if (dbl)
+       {
+         expr1.X_add_number = 1;
+         load_register (AT, &expr1, dbl);
+         macro_build (NULL, "dsll32", SHFT_FMT, AT, AT, 31);
+       }
+      else
+       {
+         expr1.X_add_number = 0x80000000;
+         macro_build (&expr1, "lui", LUI_FMT, AT, BFD_RELOC_HI16);
+       }
+      if (mips_trap)
+       {
+         macro_build (NULL, "teq", TRAP_FMT, sreg, AT, 6);
+         /* We want to close the noreorder block as soon as possible, so
+            that later insns are available for delay slot filling.  */
+         end_noreorder ();
+       }
+      else
+       {
+         if (mips_opts.micromips)
+           micromips_label_expr (&label_expr);
+         else
+           label_expr.X_add_number = 8;
+         macro_build (&label_expr, "bne", "s,t,p", sreg, AT);
+         macro_build (NULL, "nop", "");
+
+         /* We want to close the noreorder block as soon as possible, so
+            that later insns are available for delay slot filling.  */
+         end_noreorder ();
+
+         macro_build (NULL, "break", BRK_FMT, 6);
+       }
+      if (mips_opts.micromips)
+       micromips_add_label ();
+      macro_build (NULL, s, MFHL_FMT, dreg);
+      break;
+
+    case M_DIV_3I:
+      s = "div";
+      s2 = "mflo";
+      goto do_divi;
+    case M_DIVU_3I:
+      s = "divu";
+      s2 = "mflo";
+      goto do_divi;
+    case M_REM_3I:
+      s = "div";
+      s2 = "mfhi";
+      goto do_divi;
+    case M_REMU_3I:
+      s = "divu";
+      s2 = "mfhi";
+      goto do_divi;
+    case M_DDIV_3I:
+      dbl = 1;
+      s = "ddiv";
+      s2 = "mflo";
+      goto do_divi;
+    case M_DDIVU_3I:
+      dbl = 1;
+      s = "ddivu";
+      s2 = "mflo";
+      goto do_divi;
+    case M_DREM_3I:
+      dbl = 1;
+      s = "ddiv";
+      s2 = "mfhi";
+      goto do_divi;
+    case M_DREMU_3I:
+      dbl = 1;
+      s = "ddivu";
+      s2 = "mfhi";
+    do_divi:
+      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+       {
+         as_warn (_("Divide by zero."));
+         if (mips_trap)
+           macro_build (NULL, "teq", TRAP_FMT, ZERO, ZERO, 7);
+         else
+           macro_build (NULL, "break", BRK_FMT, 7);
+         break;
+       }
+      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+       {
+         if (strcmp (s2, "mflo") == 0)
+           move_register (dreg, sreg);
+         else
+           move_register (dreg, ZERO);
+         break;
+       }
+      if (imm_expr.X_op == O_constant
+         && imm_expr.X_add_number == -1
+         && s[strlen (s) - 1] != 'u')
+       {
+         if (strcmp (s2, "mflo") == 0)
+           {
+             macro_build (NULL, dbl ? "dneg" : "neg", "d,w", dreg, sreg);
+           }
+         else
+           move_register (dreg, ZERO);
+         break;
+       }
+
+      used_at = 1;
+      load_register (AT, &imm_expr, dbl);
+      macro_build (NULL, s, "z,s,t", sreg, AT);
+      macro_build (NULL, s2, MFHL_FMT, dreg);
+      break;
+
+    case M_DIVU_3:
+      s = "divu";
+      s2 = "mflo";
+      goto do_divu3;
+    case M_REMU_3:
+      s = "divu";
+      s2 = "mfhi";
+      goto do_divu3;
+    case M_DDIVU_3:
+      s = "ddivu";
+      s2 = "mflo";
+      goto do_divu3;
+    case M_DREMU_3:
+      s = "ddivu";
+      s2 = "mfhi";
+    do_divu3:
+      start_noreorder ();
+      if (mips_trap)
+       {
+         macro_build (NULL, "teq", TRAP_FMT, treg, ZERO, 7);
+         macro_build (NULL, s, "z,s,t", sreg, treg);
+         /* We want to close the noreorder block as soon as possible, so
+            that later insns are available for delay slot filling.  */
+         end_noreorder ();
+       }
+      else
+       {
+         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);
+
+         /* We want to close the noreorder block as soon as possible, so
+            that later insns are available for delay slot filling.  */
+         end_noreorder ();
+         macro_build (NULL, "break", BRK_FMT, 7);
+         if (mips_opts.micromips)
+           micromips_add_label ();
+       }
+      macro_build (NULL, s2, MFHL_FMT, dreg);
+      break;
+
+    case M_DLCA_AB:
+      dbl = 1;
+    case M_LCA_AB:
+      call = 1;
+      goto do_la;
+    case M_DLA_AB:
+      dbl = 1;
+    case M_LA_AB:
+    do_la:
+      /* Load the address of a symbol into a register.  If breg is not
+        zero, we then add a base register to it.  */
+
+      if (dbl && HAVE_32BIT_GPRS)
+       as_warn (_("dla used to load 32-bit register"));
+
+      if (!dbl && HAVE_64BIT_OBJECTS)
+       as_warn (_("la used to load 64-bit address"));
+
+      if (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 (mips_opts.at && (treg == breg))
+       {
+         tempreg = AT;
+         used_at = 1;
+       }
+      else
+       {
+         tempreg = treg;
+       }
+
+      if (offset_expr.X_op != O_symbol
+         && offset_expr.X_op != O_constant)
+       {
+         as_bad (_("Expression too complex"));
+         offset_expr.X_op = O_constant;
+       }
+
+      if (offset_expr.X_op == O_constant)
+       load_register (tempreg, &offset_expr, HAVE_64BIT_ADDRESSES);
+      else if (mips_pic == NO_PIC)
+       {
+         /* If this is a reference to a GP relative symbol, we want
+              addiu    $tempreg,$gp,<sym>      (BFD_RELOC_GPREL16)
+            Otherwise we want
+              lui      $tempreg,<sym>          (BFD_RELOC_HI16_S)
+              addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+            If we have a constant, we need two instructions anyhow,
+            so we may as well always use the latter form.
+
+            With 64bit address space and a usable $at we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
+              lui      $at,<sym>               (BFD_RELOC_HI16_S)
+              daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
+              daddiu   $at,<sym>               (BFD_RELOC_LO16)
+              dsll32   $tempreg,0
+              daddu    $tempreg,$tempreg,$at
+
+            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 (mips_relax.sequence)
+               relax_end ();
+           }
+         else
+           {
+             if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+                 && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+               {
+                 relax_start (offset_expr.X_add_symbol);
+                 macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+                              tempreg, mips_gp_register, BFD_RELOC_GPREL16);
+                 relax_switch ();
+               }
+             if (!IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+               as_bad (_("Offset too large"));
+             macro_build_lui (&offset_expr, tempreg);
+             macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+                          tempreg, tempreg, BFD_RELOC_LO16);
+             if (mips_relax.sequence)
+               relax_end ();
+           }
+       }
+      else if (!mips_big_got && !HAVE_NEWABI)
+       {
+         int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
+
+         /* If this is a reference to an external symbol, and there
+            is no constant, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+            or for lca or if tempreg is PIC_CALL_REG
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_CALL16)
+            For a local symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              nop
+              addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+
+            If we have a small constant, and this is a reference to
+            an external symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              nop
+              addiu    $tempreg,$tempreg,<constant>
+            For a local symbol, we want the same instruction
+            sequence, but we output a BFD_RELOC_LO16 reloc on the
+            addiu instruction.
+
+            If we have a large constant, and this is a reference to
+            an external symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              lui      $at,<hiconstant>
+              addiu    $at,$at,<loconstant>
+              addu     $tempreg,$tempreg,$at
+            For a local symbol, we want the same instruction
+            sequence, but we output a BFD_RELOC_LO16 reloc on the
+            addiu instruction.
+          */
+
+         if (offset_expr.X_add_number == 0)
+           {
+             if (mips_pic == SVR4_PIC
+                 && breg == 0
+                 && (call || tempreg == PIC_CALL_REG))
+               lw_reloc_type = (int) BFD_RELOC_MIPS_CALL16;
+
+             relax_start (offset_expr.X_add_symbol);
+             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                          lw_reloc_type, mips_gp_register);
+             if (breg != 0)
+               {
+                 /* We're going to put in an addu instruction using
+                    tempreg, so we may as well insert the nop right
+                    now.  */
+                 load_delay_nop ();
+               }
+             relax_switch ();
+             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                          tempreg, BFD_RELOC_MIPS_GOT16, mips_gp_register);
+             load_delay_nop ();
+             macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+                          tempreg, tempreg, BFD_RELOC_LO16);
+             relax_end ();
+             /* FIXME: If breg == 0, and the next instruction uses
+                $tempreg, then if this variant case is used an extra
+                nop will be generated.  */
+           }
+         else if (offset_expr.X_add_number >= -0x8000
+                  && offset_expr.X_add_number < 0x8000)
+           {
+             load_got_offset (tempreg, &offset_expr);
+             load_delay_nop ();
+             add_got_offset (tempreg, &offset_expr);
+           }
+         else
+           {
+             expr1.X_add_number = offset_expr.X_add_number;
+             offset_expr.X_add_number =
+               SEXT_16BIT (offset_expr.X_add_number);
+             load_got_offset (tempreg, &offset_expr);
+             offset_expr.X_add_number = expr1.X_add_number;
+             /* If we are going to add in a base register, and the
+                target register and the base register are the same,
+                then we are using AT as a temporary register.  Since
+                we want to load the constant into AT, we add our
+                current AT (from the global offset table) and the
+                register into the register now, and pretend we were
+                not using a base register.  */
+             if (breg == treg)
+               {
+                 load_delay_nop ();
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                              treg, AT, breg);
+                 breg = 0;
+                 tempreg = treg;
+               }
+             add_got_offset_hilo (tempreg, &offset_expr, AT);
+             used_at = 1;
+           }
+       }
+      else if (!mips_big_got && HAVE_NEWABI)
+       {
+         int add_breg_early = 0;
+
+         /* If this is a reference to an external, and there is no
+            constant, or local symbol (*), with or without a
+            constant, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_DISP)
+            or for lca or if tempreg is PIC_CALL_REG
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_CALL16)
+
+            If we have a small constant, and this is a reference to
+            an external symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_DISP)
+              addiu    $tempreg,$tempreg,<constant>
+
+            If we have a large constant, and this is a reference to
+            an external symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_DISP)
+              lui      $at,<hiconstant>
+              addiu    $at,$at,<loconstant>
+              addu     $tempreg,$tempreg,$at
+
+            (*) Other assemblers seem to prefer GOT_PAGE/GOT_OFST for
+            local symbols, even though it introduces an additional
+            instruction.  */
+
+         if (offset_expr.X_add_number)
+           {
+             expr1.X_add_number = offset_expr.X_add_number;
+             offset_expr.X_add_number = 0;
+
+             relax_start (offset_expr.X_add_symbol);
+             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                          BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+
+             if (expr1.X_add_number >= -0x8000
+                 && expr1.X_add_number < 0x8000)
+               {
+                 macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
+                              tempreg, tempreg, BFD_RELOC_LO16);
+               }
+             else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
+               {
+                 /* If we are going to add in a base register, and the
+                    target register and the base register are the same,
+                    then we are using AT as a temporary register.  Since
+                    we want to load the constant into AT, we add our
+                    current AT (from the global offset table) and the
+                    register into the register now, and pretend we were
+                    not using a base register.  */
+                 if (breg != treg)
+                   dreg = tempreg;
+                 else
+                   {
+                     gas_assert (tempreg == AT);
+                     macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                                  treg, AT, breg);
+                     dreg = treg;
+                     add_breg_early = 1;
+                   }
+
+                 load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                              dreg, dreg, AT);
+
+                 used_at = 1;
+               }
+             else
+               as_bad (_("PIC code offset overflow (max 32 signed bits)"));
+
+             relax_switch ();
+             offset_expr.X_add_number = expr1.X_add_number;
+
+             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                          BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+             if (add_breg_early)
+               {
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                              treg, tempreg, breg);
+                 breg = 0;
+                 tempreg = treg;
+               }
+             relax_end ();
+           }
+         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;
+
+         /* 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))
+           {
+             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;
+               }
+
+             load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
+
+             used_at = 1;
+           }
+         offset_expr.X_add_number = SEXT_16BIT (expr1.X_add_number);
+         relax_switch ();
+
+         if (gpdelay)
+           {
+             /* This is needed because this instruction uses $gp, but
+                the first instruction on the main stream does not.  */
+             macro_build (NULL, "nop", "");
+           }
+
+         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                      local_reloc_type, mips_gp_register);
+         if (expr1.X_add_number >= -0x8000
+             && expr1.X_add_number < 0x8000)
+           {
+             load_delay_nop ();
+             macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+                          tempreg, tempreg, BFD_RELOC_LO16);
+             /* FIXME: If add_number is 0, and there was no base
+                register, the external symbol case ended with a load,
+                so if the symbol turns out to not be external, and
+                the next instruction uses tempreg, an unnecessary nop
+                will be inserted.  */
+           }
+         else
+           {
+             if (breg == treg)
+               {
+                 /* We must add in the base register now, as in the
+                    external symbol case.  */
+                 gas_assert (tempreg == AT);
+                 load_delay_nop ();
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                              treg, AT, breg);
+                 tempreg = treg;
+                 /* We set breg to 0 because we have arranged to add
+                    it in in both cases.  */
+                 breg = 0;
+               }
+
+             macro_build_lui (&expr1, AT);
+             macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+                          AT, AT, BFD_RELOC_LO16);
+             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                          tempreg, tempreg, AT);
+             used_at = 1;
+           }
+         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;
+
+         /* This is the large GOT case.  If this is a reference to an
+            external symbol, and there is no constant, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
+              add      $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+            or for lca or if tempreg is PIC_CALL_REG
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_CALL_HI16)
+              add      $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_CALL_LO16)
+
+            If we have a small constant, and this is a reference to
+            an external symbol, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
+              add      $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+              addi     $tempreg,$tempreg,<constant>
+
+            If we have a large constant, and this is a reference to
+            an external symbol, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
+              addu     $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+              lui      $at,<hiconstant>
+              addi     $at,$at,<loconstant>
+              add      $tempreg,$tempreg,$at
+
+            If we have NewABI, and we know it's a local symbol, we want
+              lw       $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT_PAGE)
+              addiu    $reg,$reg,<sym>         (BFD_RELOC_MIPS_GOT_OFST)
+            otherwise we have to resort to GOT_HI16/GOT_LO16.  */
+
+         relax_start (offset_expr.X_add_symbol);
+
+         expr1.X_add_number = offset_expr.X_add_number;
+         offset_expr.X_add_number = 0;
+
+         if (expr1.X_add_number == 0 && breg == 0
+             && (call || tempreg == PIC_CALL_REG))
+           {
+             lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16;
+             lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16;
+           }
+         macro_build (&offset_expr, "lui", LUI_FMT, tempreg, lui_reloc_type);
+         macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                      tempreg, tempreg, mips_gp_register);
+         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                      tempreg, lw_reloc_type, tempreg);
+
+         if (expr1.X_add_number == 0)
+           ;
+         else if (expr1.X_add_number >= -0x8000
+                  && expr1.X_add_number < 0x8000)
+           {
+             macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
+                          tempreg, tempreg, BFD_RELOC_LO16);
+           }
+         else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
+           {
+             /* If we are going to add in a base register, and the
+                target register and the base register are the same,
+                then we are using AT as a temporary register.  Since
+                we want to load the constant into AT, we add our
+                current AT (from the global offset table) and the
+                register into the register now, and pretend we were
+                not using a base register.  */
+             if (breg != treg)
+               dreg = tempreg;
+             else
+               {
+                 gas_assert (tempreg == AT);
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                              treg, AT, breg);
+                 dreg = treg;
+                 add_breg_early = 1;
+               }
+
+             load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
+
+             used_at = 1;
+           }
+         else
+           as_bad (_("PIC code offset overflow (max 32 signed bits)"));
+
+         relax_switch ();
+         offset_expr.X_add_number = expr1.X_add_number;
+         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                      BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
+         macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
+                      tempreg, BFD_RELOC_MIPS_GOT_OFST);
+         if (add_breg_early)
+           {
+             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                          treg, tempreg, breg);
+             breg = 0;
+             tempreg = treg;
+           }
+         relax_end ();
+       }
+      else
+       abort ();
+
+      if (breg != 0)
+       macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, tempreg, breg);
+      break;
+
+    case M_MSGSND:
+      gas_assert (!mips_opts.micromips);
+      {
+       unsigned long temp = (treg << 16) | (0x01);
+       macro_build (NULL, "c2", "C", temp);
+      }
+      break;
+
+    case M_MSGLD:
+      gas_assert (!mips_opts.micromips);
+      {
+       unsigned long temp = (0x02);
+       macro_build (NULL, "c2", "C", temp);
+      }
+      break;
+
+    case M_MSGLD_T:
+      gas_assert (!mips_opts.micromips);
+      {
+       unsigned long temp = (treg << 16) | (0x02);
+       macro_build (NULL, "c2", "C", temp);
+      }
+      break;
+
+    case M_MSGWAIT:
+      gas_assert (!mips_opts.micromips);
+      macro_build (NULL, "c2", "C", 3);
+      break;
+
+    case M_MSGWAIT_T:
+      gas_assert (!mips_opts.micromips);
+      {
+       unsigned long temp = (treg << 16) | 0x03;
+       macro_build (NULL, "c2", "C", temp);
+      }
+      break;
+
+    case M_J_A:
+      /* The j instruction may not be used in PIC code, since it
+        requires an absolute address.  We convert it to a b
+        instruction.  */
+      if (mips_pic == NO_PIC)
+       macro_build (&offset_expr, "j", "a");
+      else
+       macro_build (&offset_expr, "b", "p");
+      break;
+
+      /* The jal instructions must be handled as macros because when
+        generating PIC code they expand to multi-instruction
+        sequences.  Normally they are simple instructions.  */
+    case M_JALS_1:
+      dreg = RA;
+      /* Fall through.  */
+    case M_JALS_2:
+      gas_assert (mips_opts.micromips);
+      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
+             && 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);
+
+         if (sreg != PIC_CALL_REG)
+           as_warn (_("MIPS PIC call to register other than $25"));
+
+         s = (mips_opts.micromips && (!mips_opts.noreorder || cprestore)
+              ? "jalrs" : "jalr");
+         if (mips_opts.micromips
+             && dreg == RA
+             && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
+           macro_build (NULL, s, "mj", sreg);
+         else
+           macro_build (NULL, s, JALR_FMT, dreg, sreg);
+         if (mips_pic == SVR4_PIC && !HAVE_NEWABI)
+           {
+             if (mips_cprestore_offset < 0)
+               as_warn (_("No .cprestore pseudo-op used in PIC code"));
+             else
+               {
+                 if (!mips_frame_reg_valid)
+                   {
+                     as_warn (_("No .frame pseudo-op used in PIC code"));
+                     /* Quiet this warning.  */
+                     mips_frame_reg_valid = 1;
+                   }
+                 if (!mips_cprestore_valid)
+                   {
+                     as_warn (_("No .cprestore pseudo-op used in PIC code"));
+                     /* Quiet this warning.  */
+                     mips_cprestore_valid = 1;
+                   }
+                 if (mips_opts.noreorder)
+                   macro_build (NULL, "nop", "");
+                 expr1.X_add_number = mips_cprestore_offset;
+                 macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
+                                               mips_gp_register,
+                                               mips_frame_reg,
+                                               HAVE_64BIT_ADDRESSES);
+               }
+           }
+       }
+
+      break;
+
+    case M_JALS_A:
+      gas_assert (mips_opts.micromips);
+      jals = 1;
+      /* Fall through.  */
+    case M_JAL_A:
+      if (mips_pic == NO_PIC)
+       macro_build (&offset_expr, jals ? "jals" : "jal", "a");
+      else if (mips_pic == SVR4_PIC)
+       {
+         /* 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)
@@ -6258,3207 +8003,4889 @@ macro (struct mips_cl_insn *ip)
               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)
+            For NewABI, we use the same CALL16 or CALL_HI16/CALL_LO16
+            sequences above, minus nops, unless the symbol is local,
+            which enables us to use GOT_PAGE/GOT_OFST (big got) or
+            GOT_DISP.  */
+         if (HAVE_NEWABI)
+           {
+             if (!mips_big_got)
+               {
+                 relax_start (offset_expr.X_add_symbol);
+                 macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                              PIC_CALL_REG, BFD_RELOC_MIPS_CALL16,
+                              mips_gp_register);
+                 relax_switch ();
+                 macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                              PIC_CALL_REG, BFD_RELOC_MIPS_GOT_DISP,
+                              mips_gp_register);
+                 relax_end ();
+               }
+             else
+               {
+                 relax_start (offset_expr.X_add_symbol);
+                 macro_build (&offset_expr, "lui", LUI_FMT, PIC_CALL_REG,
+                              BFD_RELOC_MIPS_CALL_HI16);
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
+                              PIC_CALL_REG, mips_gp_register);
+                 macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                              PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16,
+                              PIC_CALL_REG);
+                 relax_switch ();
+                 macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                              PIC_CALL_REG, BFD_RELOC_MIPS_GOT_PAGE,
+                              mips_gp_register);
+                 macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+                              PIC_CALL_REG, PIC_CALL_REG,
+                              BFD_RELOC_MIPS_GOT_OFST);
+                 relax_end ();
+               }
+
+             macro_build_jalr (&offset_expr, 0);
+           }
+         else
+           {
+             relax_start (offset_expr.X_add_symbol);
+             if (!mips_big_got)
+               {
+                 macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                              PIC_CALL_REG, BFD_RELOC_MIPS_CALL16,
+                              mips_gp_register);
+                 load_delay_nop ();
+                 relax_switch ();
+               }
+             else
+               {
+                 int gpdelay;
+
+                 gpdelay = reg_needs_delay (mips_gp_register);
+                 macro_build (&offset_expr, "lui", LUI_FMT, PIC_CALL_REG,
+                              BFD_RELOC_MIPS_CALL_HI16);
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
+                              PIC_CALL_REG, mips_gp_register);
+                 macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                              PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16,
+                              PIC_CALL_REG);
+                 load_delay_nop ();
+                 relax_switch ();
+                 if (gpdelay)
+                   macro_build (NULL, "nop", "");
+               }
+             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                          PIC_CALL_REG, BFD_RELOC_MIPS_GOT16,
+                          mips_gp_register);
+             load_delay_nop ();
+             macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+                          PIC_CALL_REG, PIC_CALL_REG, BFD_RELOC_LO16);
+             relax_end ();
+             macro_build_jalr (&offset_expr, mips_cprestore_offset >= 0);
+
+             if (mips_cprestore_offset < 0)
+               as_warn (_("No .cprestore pseudo-op used in PIC code"));
+             else
+               {
+                 if (!mips_frame_reg_valid)
+                   {
+                     as_warn (_("No .frame pseudo-op used in PIC code"));
+                     /* Quiet this warning.  */
+                     mips_frame_reg_valid = 1;
+                   }
+                 if (!mips_cprestore_valid)
+                   {
+                     as_warn (_("No .cprestore pseudo-op used in PIC code"));
+                     /* Quiet this warning.  */
+                     mips_cprestore_valid = 1;
+                   }
+                 if (mips_opts.noreorder)
+                   macro_build (NULL, "nop", "");
+                 expr1.X_add_number = mips_cprestore_offset;
+                 macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
+                                               mips_gp_register,
+                                               mips_frame_reg,
+                                               HAVE_64BIT_ADDRESSES);
+               }
+           }
+       }
+      else if (mips_pic == VXWORKS_PIC)
+       as_bad (_("Non-PIC jump used in PIC library"));
+      else
+       abort ();
+
+      break;
+
+    case M_LBUE_AB:
+      ab = 1;
+    case M_LBUE_OB:
+      s = "lbue";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_LHUE_AB:
+      ab = 1;
+    case M_LHUE_OB:
+      s = "lhue";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_LBE_AB:
+      ab = 1;
+    case M_LBE_OB:
+      s = "lbe";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_LHE_AB:
+      ab = 1;
+    case M_LHE_OB:
+      s = "lhe";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_LLE_AB:
+      ab = 1;
+    case M_LLE_OB:
+      s = "lle";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_LWE_AB:
+      ab = 1;
+    case M_LWE_OB:
+      s = "lwe";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_LWLE_AB:
+      ab = 1;
+    case M_LWLE_OB:
+      s = "lwle";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_LWRE_AB:
+      ab = 1;
+    case M_LWRE_OB:
+      s = "lwre";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_SBE_AB:
+      ab = 1;
+    case M_SBE_OB:
+      s = "sbe";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_SCE_AB:
+      ab = 1;
+    case M_SCE_OB:
+      s = "sce";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_SHE_AB:
+      ab = 1;
+    case M_SHE_OB:
+      s = "she";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_SWE_AB:
+      ab = 1;
+    case M_SWE_OB:
+      s = "swe";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_SWLE_AB:
+      ab = 1;
+    case M_SWLE_OB:
+      s = "swle";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_SWRE_AB:
+      ab = 1;
+    case M_SWRE_OB:
+      s = "swre";
+      fmt = "t,+j(b)";
+      offbits = 9;
+      goto ld_st;
+    case M_ACLR_AB:
+      ab = 1;
+    case M_ACLR_OB:
+      s = "aclr";
+      treg = EXTRACT_OPERAND (mips_opts.micromips, 3BITPOS, *ip);
+      fmt = "\\,~(b)";
+      offbits = 12;
+      goto ld_st;
+    case M_ASET_AB:
+      ab = 1;
+    case M_ASET_OB:
+      s = "aset";
+      treg = EXTRACT_OPERAND (mips_opts.micromips, 3BITPOS, *ip);
+      fmt = "\\,~(b)";
+      offbits = 12;
+      goto ld_st;
+    case M_LB_AB:
+      ab = 1;
+      s = "lb";
+      fmt = "t,o(b)";
+      goto ld;
+    case M_LBU_AB:
+      ab = 1;
+      s = "lbu";
+      fmt = "t,o(b)";
+      goto ld;
+    case M_LH_AB:
+      ab = 1;
+      s = "lh";
+      fmt = "t,o(b)";
+      goto ld;
+    case M_LHU_AB:
+      ab = 1;
+      s = "lhu";
+      fmt = "t,o(b)";
+      goto ld;
+    case M_LW_AB:
+      ab = 1;
+      s = "lw";
+      fmt = "t,o(b)";
+      goto ld;
+    case M_LWC0_AB:
+      ab = 1;
+      gas_assert (!mips_opts.micromips);
+      s = "lwc0";
+      fmt = "E,o(b)";
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_LWC1_AB:
+      ab = 1;
+      s = "lwc1";
+      fmt = "T,o(b)";
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_LWC2_AB:
+      ab = 1;
+    case M_LWC2_OB:
+      s = "lwc2";
+      fmt = COP12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_LWC3_AB:
+      ab = 1;
+      gas_assert (!mips_opts.micromips);
+      s = "lwc3";
+      fmt = "E,o(b)";
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_LWL_AB:
+      ab = 1;
+    case M_LWL_OB:
+      s = "lwl";
+      fmt = MEM12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      goto ld_st;
+    case M_LWR_AB:
+      ab = 1;
+    case M_LWR_OB:
+      s = "lwr";
+      fmt = MEM12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      goto ld_st;
+    case M_LDC1_AB:
+      ab = 1;
+      s = "ldc1";
+      fmt = "T,o(b)";
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_LDC2_AB:
+      ab = 1;
+    case M_LDC2_OB:
+      s = "ldc2";
+      fmt = COP12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_LQC2_AB:
+      ab = 1;
+      s = "lqc2";
+      fmt = "E,o(b)";
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_LDC3_AB:
+      ab = 1;
+      s = "ldc3";
+      fmt = "E,o(b)";
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      goto ld_st;
+    case M_LDL_AB:
+      ab = 1;
+    case M_LDL_OB:
+      s = "ldl";
+      fmt = MEM12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      goto ld_st;
+    case M_LDR_AB:
+      ab = 1;
+    case M_LDR_OB:
+      s = "ldr";
+      fmt = MEM12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      goto ld_st;
+    case M_LL_AB:
+      ab = 1;
+    case M_LL_OB:
+      s = "ll";
+      fmt = MEM12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      goto ld;
+    case M_LLD_AB:
+      ab = 1;
+    case M_LLD_OB:
+      s = "lld";
+      fmt = MEM12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      goto ld;
+    case M_LWU_AB:
+      ab = 1;
+    case M_LWU_OB:
+      s = "lwu";
+      fmt = MEM12_FMT;
+      offbits = (mips_opts.micromips ? 12 : 16);
+      goto ld;
+    case M_LWP_AB:
+      ab = 1;
+    case M_LWP_OB:
+      gas_assert (mips_opts.micromips);
+      s = "lwp";
+      fmt = "t,~(b)";
+      offbits = 12;
+      lp = 1;
+      goto ld;
+    case M_LDP_AB:
+      ab = 1;
+    case M_LDP_OB:
+      gas_assert (mips_opts.micromips);
+      s = "ldp";
+      fmt = "t,~(b)";
+      offbits = 12;
+      lp = 1;
+      goto ld;
+    case M_LWM_AB:
+      ab = 1;
+    case M_LWM_OB:
+      gas_assert (mips_opts.micromips);
+      s = "lwm";
+      fmt = "n,~(b)";
+      offbits = 12;
+      goto ld_st;
+    case M_LDM_AB:
+      ab = 1;
+    case M_LDM_OB:
+      gas_assert (mips_opts.micromips);
+      s = "ldm";
+      fmt = "n,~(b)";
+      offbits = 12;
+      goto ld_st;
+
+    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:
+      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;
+
+    ld_st:
+      tempreg = AT;
+      used_at = 1;
+    ld_noat:
+      if (offset_expr.X_op != O_constant
+         && offset_expr.X_op != O_symbol)
+       {
+         as_bad (_("Expression too complex"));
+         offset_expr.X_op = O_constant;
+       }
+
+      if (HAVE_32BIT_ADDRESSES
+         && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+       {
+         char value [32];
+
+         sprintf_vma (value, offset_expr.X_add_number);
+         as_bad (_("Number (0x%s) larger than 32 bits"), value);
+       }
+
+      /* A constant expression in PIC code can be handled just as it
+        is in non PIC code.  */
+      if (offset_expr.X_op == O_constant)
+       {
+         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);
+         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);
+           }
+         else
+           macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+                        tempreg, breg, -1,
+                        offset_reloc[0], offset_reloc[1], offset_reloc[2]);
+         expr1.X_add_number = 0;
+         if (offbits == 0)
+           macro_build (NULL, s, fmt, treg, tempreg);
+         else
+           macro_build (NULL, s, fmt,
+                        treg, (unsigned long) expr1.X_add_number, tempreg);
+       }
+      else if (mips_pic == NO_PIC)
+       {
+         /* If this is a reference to a GP relative symbol, and there
+            is no base register, we want
+              <op>     $treg,<sym>($gp)        (BFD_RELOC_GPREL16)
+            Otherwise, if there is no base register, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_HI16_S)
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+            If we have a constant, we need two instructions anyhow,
+            so we always use the latter form.
+
+            If we have a base register, and this is a reference to a
+            GP relative symbol, we want
+              addu     $tempreg,$breg,$gp
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_GPREL16)
+            Otherwise we want
+              lui      $tempreg,<sym>          (BFD_RELOC_HI16_S)
+              addu     $tempreg,$tempreg,$breg
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+            With a constant we always use the latter case.
+
+            With 64bit address space and no base register and $at usable,
+            we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
+              lui      $at,<sym>               (BFD_RELOC_HI16_S)
+              daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
+              dsll32   $tempreg,0
+              daddu    $tempreg,$at
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+            If we have a base register, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
+              lui      $at,<sym>               (BFD_RELOC_HI16_S)
+              daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
+              daddu    $at,$breg
+              dsll32   $tempreg,0
+              daddu    $tempreg,$at
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+
+            Without $at we can't generate the optimal path for superscalar
+            processors here since this would require two temporary registers.
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
+              daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
+              dsll     $tempreg,16
+              daddiu   $tempreg,<sym>          (BFD_RELOC_HI16_S)
+              dsll     $tempreg,16
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+            If we have a base register, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
+              daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
+              dsll     $tempreg,16
+              daddiu   $tempreg,<sym>          (BFD_RELOC_HI16_S)
+              dsll     $tempreg,16
+              daddu    $tempreg,$tempreg,$breg
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+
+            For GP relative symbols in 64bit address space we can use
+            the same sequence as in 32bit address space.  */
+         if (HAVE_64BIT_SYMBOLS)
+           {
+             if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+                 && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+               {
+                 relax_start (offset_expr.X_add_symbol);
+                 if (breg == 0)
+                   {
+                     macro_build (&offset_expr, s, fmt, treg,
+                                  BFD_RELOC_GPREL16, mips_gp_register);
+                   }
+                 else
+                   {
+                     macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                                  tempreg, breg, mips_gp_register);
+                     macro_build (&offset_expr, s, fmt, treg,
+                                  BFD_RELOC_GPREL16, tempreg);
+                   }
+                 relax_switch ();
+               }
+
+             if (used_at == 0 && mips_opts.at)
+               {
+                 macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
+                              BFD_RELOC_MIPS_HIGHEST);
+                 macro_build (&offset_expr, "lui", LUI_FMT, AT,
+                              BFD_RELOC_HI16_S);
+                 macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
+                              tempreg, BFD_RELOC_MIPS_HIGHER);
+                 if (breg != 0)
+                   macro_build (NULL, "daddu", "d,v,t", AT, AT, breg);
+                 macro_build (NULL, "dsll32", SHFT_FMT, tempreg, tempreg, 0);
+                 macro_build (NULL, "daddu", "d,v,t", tempreg, tempreg, AT);
+                 macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16,
+                              tempreg);
+                 used_at = 1;
+               }
+             else
+               {
+                 macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
+                              BFD_RELOC_MIPS_HIGHEST);
+                 macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
+                              tempreg, BFD_RELOC_MIPS_HIGHER);
+                 macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
+                 macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
+                              tempreg, BFD_RELOC_HI16_S);
+                 macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
+                 if (breg != 0)
+                   macro_build (NULL, "daddu", "d,v,t",
+                                tempreg, tempreg, breg);
+                 macro_build (&offset_expr, s, fmt, treg,
+                              BFD_RELOC_LO16, tempreg);
+               }
+
+             if (mips_relax.sequence)
+               relax_end ();
+             break;
+           }
+
+         if (breg == 0)
            {
-             if (!mips_big_got)
+             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_LOAD_INSN, "t,o(b)",
-                              PIC_CALL_REG, BFD_RELOC_MIPS_CALL16,
+                 macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_GPREL16,
                               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
+             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 (&offset_expr, "lui", "t,u", 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);
+                 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 (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
-                              PIC_CALL_REG, BFD_RELOC_MIPS_GOT_PAGE,
-                              mips_gp_register);
-                 macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
-                              PIC_CALL_REG, PIC_CALL_REG,
-                              BFD_RELOC_MIPS_GOT_OFST);
-                 relax_end ();
                }
+             macro_build_lui (&offset_expr, tempreg);
+             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                          tempreg, tempreg, breg);
+             macro_build (&offset_expr, s, fmt, treg,
+                          BFD_RELOC_LO16, tempreg);
+             if (mips_relax.sequence)
+               relax_end ();
+           }
+       }
+      else if (!mips_big_got)
+       {
+         int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
+
+         /* If this is a reference to an external symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              nop
+              <op>     $treg,0($tempreg)
+            Otherwise we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              nop
+              addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+              <op>     $treg,0($tempreg)
+
+            For NewABI, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_PAGE)
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_MIPS_GOT_OFST)
+
+            If there is a base register, we add it to $tempreg before
+            the <op>.  If there is a constant, we stick it in the
+            <op> instruction.  We don't handle constants larger than
+            16 bits, because we have no way to load the upper 16 bits
+            (actually, we could handle them for the subset of cases
+            in which we are not using $at).  */
+         gas_assert (offset_expr.X_op == O_symbol);
+         if (HAVE_NEWABI)
+           {
+             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                          BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
+             if (breg != 0)
+               macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                            tempreg, tempreg, breg);
+             macro_build (&offset_expr, s, fmt, treg,
+                          BFD_RELOC_MIPS_GOT_OFST, tempreg);
+             break;
+           }
+         expr1.X_add_number = offset_expr.X_add_number;
+         offset_expr.X_add_number = 0;
+         if (expr1.X_add_number < -0x8000
+             || expr1.X_add_number >= 0x8000)
+           as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                      lw_reloc_type, mips_gp_register);
+         load_delay_nop ();
+         relax_start (offset_expr.X_add_symbol);
+         relax_switch ();
+         macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
+                      tempreg, BFD_RELOC_LO16);
+         relax_end ();
+         if (breg != 0)
+           macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                        tempreg, tempreg, breg);
+         macro_build (&expr1, s, fmt, treg, BFD_RELOC_LO16, tempreg);
+       }
+      else if (mips_big_got && !HAVE_NEWABI)
+       {
+         int gpdelay;
+
+         /* If 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 ();
+
+         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);
+
+         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 ();
+
+      break;
+
+    case M_LI:
+    case M_LI_S:
+      load_register (treg, &imm_expr, 0);
+      break;
+
+    case M_DLI:
+      load_register (treg, &imm_expr, 1);
+      break;
+
+    case M_LI_SS:
+      if (imm_expr.X_op == O_constant)
+       {
+         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;
+       }
+
+    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;
+
+             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;
+       }
+
+      /* 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;
+       }
+
+      /* 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;
 
-             macro_build_jalr (&offset_expr);
+    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
            {
-             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 ();
-               }
+             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
                {
-                 int gpdelay;
-
-                 gpdelay = reg_needs_delay (mips_gp_register);
-                 macro_build (&offset_expr, "lui", "t,u", 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", "");
+                 gas_assert (offset_expr.X_op == O_constant);
+                 load_register (AT, &offset_expr, 0);
+                 macro_build (NULL, "mtc1", "t,G", AT, treg);
                }
-             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);
+           }
+         break;
+       }
 
-             if (mips_cprestore_offset < 0)
-               as_warn (_("No .cprestore pseudo-op used in PIC code"));
-             else
-               {
-                 if (!mips_frame_reg_valid)
-                   {
-                     as_warn (_("No .frame pseudo-op used in PIC code"));
-                     /* Quiet this warning.  */
-                     mips_frame_reg_valid = 1;
-                   }
-                 if (!mips_cprestore_valid)
-                   {
-                     as_warn (_("No .cprestore pseudo-op used in PIC code"));
-                     /* Quiet this warning.  */
-                     mips_cprestore_valid = 1;
-                   }
-                 if (mips_opts.noreorder)
-                   macro_build (NULL, "nop", "");
-                 expr1.X_add_number = mips_cprestore_offset;
-                 macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
-                                               mips_gp_register,
-                                               mips_frame_reg,
-                                               HAVE_64BIT_ADDRESSES);
-               }
+      gas_assert (offset_expr.X_op == O_symbol
+                 && offset_expr.X_add_number == 0);
+      s = segment_name (S_GET_SEGMENT (offset_expr.X_add_symbol));
+      if (strcmp (s, ".lit8") == 0)
+       {
+         if (CPU_HAS_LDC1_SDC1 (mips_opts.arch) || mips_opts.micromips)
+           {
+             macro_build (&offset_expr, "ldc1", "T,o(b)", treg,
+                          BFD_RELOC_MIPS_LITERAL, mips_gp_register);
+             break;
            }
+         breg = mips_gp_register;
+         r = BFD_RELOC_MIPS_LITERAL;
+         goto dob;
        }
-      else if (mips_pic == VXWORKS_PIC)
-       as_bad (_("Non-PIC jump used in PIC library"));
       else
-       abort ();
+       {
+         gas_assert (strcmp (s, RDATA_SECTION_NAME) == 0);
+         used_at = 1;
+         if (mips_pic != NO_PIC)
+           macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
+                        BFD_RELOC_MIPS_GOT16, mips_gp_register);
+         else
+           {
+             /* FIXME: This won't work for a 64 bit address.  */
+             macro_build_lui (&offset_expr, AT);
+           }
+
+         if (CPU_HAS_LDC1_SDC1 (mips_opts.arch) || mips_opts.micromips)
+           {
+             macro_build (&offset_expr, "ldc1", "T,o(b)",
+                          treg, BFD_RELOC_LO16, AT);
+             break;
+           }
+         breg = AT;
+         r = BFD_RELOC_LO16;
+         goto dob;
+       }
 
+    case M_L_DOB:
+      /* Even on a big endian machine $fn comes before $fn+1.  We have
+        to adjust when loading from memory.  */
+      r = BFD_RELOC_LO16;
+    dob:
+      gas_assert (!mips_opts.micromips);
+      gas_assert (!CPU_HAS_LDC1_SDC1 (mips_opts.arch));
+      macro_build (&offset_expr, "lwc1", "T,o(b)",
+                  target_big_endian ? treg + 1 : treg, r, breg);
+      /* FIXME: A possible overflow which I don't know how to deal
+        with.  */
+      offset_expr.X_add_number += 4;
+      macro_build (&offset_expr, "lwc1", "T,o(b)",
+                  target_big_endian ? treg : treg + 1, r, breg);
       break;
 
-    case M_LB_AB:
-      s = "lb";
-      goto ld;
-    case M_LBU_AB:
-      s = "lbu";
-      goto ld;
-    case M_LH_AB:
-      s = "lh";
-      goto ld;
-    case M_LHU_AB:
-      s = "lhu";
-      goto ld;
-    case M_LW_AB:
-      s = "lw";
-      goto ld;
-    case M_LWC0_AB:
-      s = "lwc0";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld;
-    case M_LWC1_AB:
-      s = "lwc1";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld;
-    case M_LWC2_AB:
-      s = "lwc2";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld;
-    case M_LWC3_AB:
-      s = "lwc3";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld;
-    case M_LWL_AB:
-      s = "lwl";
-      lr = 1;
-      goto ld;
-    case M_LWR_AB:
-      s = "lwr";
-      lr = 1;
-      goto ld;
-    case M_LDC1_AB:
-      s = "ldc1";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld;
-    case M_LDC2_AB:
-      s = "ldc2";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld;
-    case M_LDC3_AB:
-      s = "ldc3";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ld;
-    case M_LDL_AB:
-      s = "ldl";
-      lr = 1;
-      goto ld;
-    case M_LDR_AB:
-      s = "ldr";
-      lr = 1;
-      goto ld;
-    case M_LL_AB:
-      s = "ll";
-      goto ld;
-    case M_LLD_AB:
-      s = "lld";
-      goto ld;
-    case M_LWU_AB:
-      s = "lwu";
-    ld:
-      if (breg == treg || coproc || lr)
+    case M_S_DOB:
+      gas_assert (!mips_opts.micromips);
+      gas_assert (!CPU_HAS_LDC1_SDC1 (mips_opts.arch));
+      /* Even on a big endian machine $fn comes before $fn+1.  We have
+        to adjust when storing to memory.  */
+      macro_build (&offset_expr, "swc1", "T,o(b)",
+                  target_big_endian ? treg + 1 : treg, BFD_RELOC_LO16, breg);
+      offset_expr.X_add_number += 4;
+      macro_build (&offset_expr, "swc1", "T,o(b)",
+                  target_big_endian ? treg : treg + 1, BFD_RELOC_LO16, breg);
+      break;
+
+    case M_L_DAB:
+      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))
        {
-         tempreg = AT;
-         used_at = 1;
+         s = "ldc1";
+         goto ld_st;
        }
-      else
+      s = "lwc1";
+      goto ldd_std;
+
+    case M_S_DAB:
+      gas_assert (!mips_opts.micromips);
+      /* Itbl support may require additional care here.  */
+      coproc = 1;
+      fmt = "T,o(b)";
+      if (CPU_HAS_LDC1_SDC1 (mips_opts.arch))
        {
-         tempreg = treg;
+         s = "sdc1";
+         goto ld_st;
        }
+      s = "swc1";
+      goto ldd_std;
+
+    case M_LQ_AB:
+      fmt = "t,o(b)";
+      s = "lq";
+      goto ld;
+
+    case M_SQ_AB:
+      fmt = "t,o(b)";
+      s = "sq";
       goto ld_st;
-    case M_SB_AB:
-      s = "sb";
-      goto st;
-    case M_SH_AB:
-      s = "sh";
-      goto st;
-    case M_SW_AB:
+
+    case M_LD_AB:
+      fmt = "t,o(b)";
+      if (HAVE_64BIT_GPRS)
+       {
+         s = "ld";
+         goto ld;
+       }
+      s = "lw";
+      goto ldd_std;
+
+    case M_SD_AB:
+      fmt = "t,o(b)";
+      if (HAVE_64BIT_GPRS)
+       {
+         s = "sd";
+         goto ld_st;
+       }
       s = "sw";
-      goto st;
-    case M_SWC0_AB:
-      s = "swc0";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto st;
-    case M_SWC1_AB:
-      s = "swc1";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto st;
-    case M_SWC2_AB:
-      s = "swc2";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto st;
-    case M_SWC3_AB:
-      s = "swc3";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto st;
-    case M_SWL_AB:
-      s = "swl";
-      goto st;
-    case M_SWR_AB:
-      s = "swr";
-      goto st;
-    case M_SC_AB:
-      s = "sc";
-      goto st;
-    case M_SCD_AB:
-      s = "scd";
-      goto st;
-    case M_CACHE_AB:
-      s = "cache";
-      goto st;
-    case M_SDC1_AB:
-      s = "sdc1";
-      coproc = 1;
-      /* Itbl support may require additional care here.  */
-      goto st;
-    case M_SDC2_AB:
-      s = "sdc2";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto st;
-    case M_SDC3_AB:
-      s = "sdc3";
+
+    ldd_std:
+      if (offset_expr.X_op != O_symbol
+         && offset_expr.X_op != O_constant)
+       {
+         as_bad (_("Expression too complex"));
+         offset_expr.X_op = O_constant;
+       }
+
+      if (HAVE_32BIT_ADDRESSES
+         && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+       {
+         char value [32];
+
+         sprintf_vma (value, offset_expr.X_add_number);
+         as_bad (_("Number (0x%s) larger than 32 bits"), value);
+       }
+
+      /* Even on a big endian machine $fn comes before $fn+1.  We have
+        to adjust when loading from memory.  We set coproc if we must
+        load $fn+1 first.  */
       /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto st;
-    case M_SDL_AB:
-      s = "sdl";
-      goto st;
-    case M_SDR_AB:
-      s = "sdr";
-    st:
-      tempreg = AT;
-      used_at = 1;
-    ld_st:
-      if (coproc
-         && NO_ISA_COP (mips_opts.arch)
-         && (ip->insn_mo->pinfo2 & (INSN2_M_FP_S | INSN2_M_FP_D)) == 0)
+      if (!target_big_endian)
+       coproc = 0;
+
+      if (mips_pic == NO_PIC || offset_expr.X_op == O_constant)
        {
-         as_bad (_("Opcode not supported on this processor: %s"),
-                 mips_cpu_info_from_arch (mips_opts.arch)->name);
-         break;
+         /* If this is a reference to a GP relative symbol, we want
+              <op>     $treg,<sym>($gp)        (BFD_RELOC_GPREL16)
+              <op>     $treg+1,<sym>+4($gp)    (BFD_RELOC_GPREL16)
+            If we have a base register, we use this
+              addu     $at,$breg,$gp
+              <op>     $treg,<sym>($at)        (BFD_RELOC_GPREL16)
+              <op>     $treg+1,<sym>+4($at)    (BFD_RELOC_GPREL16)
+            If this is not a GP relative symbol, we want
+              lui      $at,<sym>               (BFD_RELOC_HI16_S)
+              <op>     $treg,<sym>($at)        (BFD_RELOC_LO16)
+              <op>     $treg+1,<sym>+4($at)    (BFD_RELOC_LO16)
+            If there is a base register, we add it to $at after the
+            lui instruction.  If there is a constant, we always use
+            the last case.  */
+         if (offset_expr.X_op == O_symbol
+             && (valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+             && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+           {
+             relax_start (offset_expr.X_add_symbol);
+             if (breg == 0)
+               {
+                 tempreg = mips_gp_register;
+               }
+             else
+               {
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                              AT, breg, mips_gp_register);
+                 tempreg = AT;
+                 used_at = 1;
+               }
+
+             /* Itbl support may require additional care here.  */
+             macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
+                          BFD_RELOC_GPREL16, tempreg);
+             offset_expr.X_add_number += 4;
+
+             /* Set mips_optimize to 2 to avoid inserting an
+                 undesired nop.  */
+             hold_mips_optimize = mips_optimize;
+             mips_optimize = 2;
+             /* Itbl support may require additional care here.  */
+             macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
+                          BFD_RELOC_GPREL16, tempreg);
+             mips_optimize = hold_mips_optimize;
+
+             relax_switch ();
+
+             offset_expr.X_add_number -= 4;
+           }
+         used_at = 1;
+         macro_build_lui (&offset_expr, AT);
+         if (breg != 0)
+           macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+         /* Itbl support may require additional care here.  */
+         macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
+                      BFD_RELOC_LO16, AT);
+         /* FIXME: How do we handle overflow here?  */
+         offset_expr.X_add_number += 4;
+         /* Itbl support may require additional care here.  */
+         macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
+                      BFD_RELOC_LO16, AT);
+         if (mips_relax.sequence)
+           relax_end ();
+       }
+      else if (!mips_big_got)
+       {
+         /* If this is a reference to an external symbol, we want
+              lw       $at,<sym>($gp)          (BFD_RELOC_MIPS_GOT16)
+              nop
+              <op>     $treg,0($at)
+              <op>     $treg+1,4($at)
+            Otherwise we want
+              lw       $at,<sym>($gp)          (BFD_RELOC_MIPS_GOT16)
+              nop
+              <op>     $treg,<sym>($at)        (BFD_RELOC_LO16)
+              <op>     $treg+1,<sym>+4($at)    (BFD_RELOC_LO16)
+            If there is a base register we add it to $at before the
+            lwc1 instructions.  If there is a constant we include it
+            in the lwc1 instructions.  */
+         used_at = 1;
+         expr1.X_add_number = offset_expr.X_add_number;
+         if (expr1.X_add_number < -0x8000
+             || expr1.X_add_number >= 0x8000 - 4)
+           as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+         load_got_offset (AT, &offset_expr);
+         load_delay_nop ();
+         if (breg != 0)
+           macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+
+         /* Set mips_optimize to 2 to avoid inserting an undesired
+             nop.  */
+         hold_mips_optimize = mips_optimize;
+         mips_optimize = 2;
+
+         /* Itbl support may require additional care here.  */
+         relax_start (offset_expr.X_add_symbol);
+         macro_build (&expr1, s, fmt, coproc ? treg + 1 : treg,
+                      BFD_RELOC_LO16, AT);
+         expr1.X_add_number += 4;
+         macro_build (&expr1, s, fmt, coproc ? treg : treg + 1,
+                      BFD_RELOC_LO16, AT);
+         relax_switch ();
+         macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
+                      BFD_RELOC_LO16, AT);
+         offset_expr.X_add_number += 4;
+         macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
+                      BFD_RELOC_LO16, AT);
+         relax_end ();
+
+         mips_optimize = hold_mips_optimize;
        }
+      else if (mips_big_got)
+       {
+         int gpdelay;
+
+         /* If this is a reference to an external symbol, we want
+              lui      $at,<sym>               (BFD_RELOC_MIPS_GOT_HI16)
+              addu     $at,$at,$gp
+              lw       $at,<sym>($at)          (BFD_RELOC_MIPS_GOT_LO16)
+              nop
+              <op>     $treg,0($at)
+              <op>     $treg+1,4($at)
+            Otherwise we want
+              lw       $at,<sym>($gp)          (BFD_RELOC_MIPS_GOT16)
+              nop
+              <op>     $treg,<sym>($at)        (BFD_RELOC_LO16)
+              <op>     $treg+1,<sym>+4($at)    (BFD_RELOC_LO16)
+            If there is a base register we add it to $at before the
+            lwc1 instructions.  If there is a constant we include it
+            in the lwc1 instructions.  */
+         used_at = 1;
+         expr1.X_add_number = offset_expr.X_add_number;
+         offset_expr.X_add_number = 0;
+         if (expr1.X_add_number < -0x8000
+             || expr1.X_add_number >= 0x8000 - 4)
+           as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+         gpdelay = reg_needs_delay (mips_gp_register);
+         relax_start (offset_expr.X_add_symbol);
+         macro_build (&offset_expr, "lui", LUI_FMT,
+                      AT, BFD_RELOC_MIPS_GOT_HI16);
+         macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                      AT, AT, mips_gp_register);
+         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                      AT, BFD_RELOC_MIPS_GOT_LO16, AT);
+         load_delay_nop ();
+         if (breg != 0)
+           macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+         /* Itbl support may require additional care here.  */
+         macro_build (&expr1, s, fmt, coproc ? treg + 1 : treg,
+                      BFD_RELOC_LO16, AT);
+         expr1.X_add_number += 4;
 
-      /* Itbl support may require additional care here.  */
-      if (mask == M_LWC1_AB
-         || mask == M_SWC1_AB
-         || mask == M_LDC1_AB
-         || mask == M_SDC1_AB
-         || mask == M_L_DAB
-         || mask == M_S_DAB)
-       fmt = "T,o(b)";
-      else if (mask == M_CACHE_AB)
-       fmt = "k,o(b)";
-      else if (coproc)
-       fmt = "E,o(b)";
+         /* Set mips_optimize to 2 to avoid inserting an undesired
+             nop.  */
+         hold_mips_optimize = mips_optimize;
+         mips_optimize = 2;
+         /* Itbl support may require additional care here.  */
+         macro_build (&expr1, s, fmt, coproc ? treg : treg + 1,
+                      BFD_RELOC_LO16, AT);
+         mips_optimize = hold_mips_optimize;
+         expr1.X_add_number -= 4;
+
+         relax_switch ();
+         offset_expr.X_add_number = expr1.X_add_number;
+         if (gpdelay)
+           macro_build (NULL, "nop", "");
+         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
+                      BFD_RELOC_MIPS_GOT16, mips_gp_register);
+         load_delay_nop ();
+         if (breg != 0)
+           macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+         /* Itbl support may require additional care here.  */
+         macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
+                      BFD_RELOC_LO16, AT);
+         offset_expr.X_add_number += 4;
+
+         /* 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
-       fmt = "t,o(b)";
+       abort ();
 
-      if (offset_expr.X_op != O_constant
-         && offset_expr.X_op != O_symbol)
+      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)
        {
-         as_bad (_("Expression too complex"));
-         offset_expr.X_op = O_constant;
+         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;
 
-      if (HAVE_32BIT_ADDRESSES
-         && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
-       {
-         char value [32];
+       
+    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;
 
-         sprintf_vma (value, offset_expr.X_add_number);
-         as_bad (_("Number (0x%s) larger than 32 bits"), value);
-       }
+   /* 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.
 
-      /* A constant expression in PIC code can be handled just as it
-        is in non PIC code.  */
-      if (offset_expr.X_op == O_constant)
-       {
-         expr1.X_add_number = ((offset_expr.X_add_number + 0x8000)
-                               & ~(bfd_vma) 0xffff);
-         normalize_address_expr (&expr1);
-         load_register (tempreg, &expr1, HAVE_64BIT_ADDRESSES);
-         if (breg != 0)
-           macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                        tempreg, tempreg, breg);
-         macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16, tempreg);
-       }
-      else if (mips_pic == NO_PIC)
-       {
-         /* If this is a reference to a GP relative symbol, and there
-            is no base register, we want
-              <op>     $treg,<sym>($gp)        (BFD_RELOC_GPREL16)
-            Otherwise, if there is no base register, we want
-              lui      $tempreg,<sym>          (BFD_RELOC_HI16_S)
-              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
-            If we have a constant, we need two instructions anyhow,
-            so we always use the latter form.
+      FIXME: Currently, we require that the user handle delays.
+      In order to fill delay slots for non-interlocked chips,
+      we must have a way to specify delays based on the coprocessor.
+      Eg. 4 cycles if load coproc reg from memory, 1 if in cache, etc.
+      What are the side-effects of the cop instruction?
+      What cache support might we have and what are its effects?
+      Both coprocessor & memory require delays. how long???
+      What registers are read/set/modified?
 
-            If 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.
+      If an itbl is provided to interpret cop instructions,
+      this knowledge can be encoded in the itbl spec.  */
 
-            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_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;
 
-            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_MOVE:
+      move_register (dreg, sreg);
+      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_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 (used_at == 0 && mips_opts.at)
-               {
-                 macro_build (&offset_expr, "lui", "t,u", tempreg,
-                              BFD_RELOC_MIPS_HIGHEST);
-                 macro_build (&offset_expr, "lui", "t,u", 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", "d,w,<", 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", "t,u", tempreg,
-                              BFD_RELOC_MIPS_HIGHEST);
-                 macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
-                              tempreg, BFD_RELOC_MIPS_HIGHER);
-                 macro_build (NULL, "dsll", "d,w,<", tempreg, tempreg, 16);
-                 macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
-                              tempreg, BFD_RELOC_HI16_S);
-                 macro_build (NULL, "dsll", "d,w,<", 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_DMUL_I:
+      dbl = 1;
+    case M_MUL_I:
+      /* The MIPS assembler some times generates shifts and adds.  I'm
+        not trying to be that fancy. GCC should do this for us
+        anyway.  */
+      used_at = 1;
+      load_register (AT, &imm_expr, dbl);
+      macro_build (NULL, dbl ? "dmult" : "mult", "s,t", sreg, AT);
+      macro_build (NULL, "mflo", MFHL_FMT, dreg);
+      break;
+
+    case M_DMULO_I:
+      dbl = 1;
+    case M_MULO_I:
+      imm = 1;
+      goto do_mulo;
+
+    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;
 
-             if (mips_relax.sequence)
-               relax_end ();
-             break;
-           }
+    case M_DMULOU_I:
+      dbl = 1;
+    case M_MULOU_I:
+      imm = 1;
+      goto do_mulou;
 
-         if (breg == 0)
+    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 M_DROL:
+      if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+       {
+         if (dreg == sreg)
            {
-             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 ();
+             tempreg = AT;
+             used_at = 1;
            }
          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 ();
+             tempreg = dreg;
            }
+         macro_build (NULL, "dnegu", "d,w", tempreg, treg);
+         macro_build (NULL, "drorv", "d,t,s", dreg, sreg, tempreg);
+         break;
        }
-      else if (!mips_big_got)
-       {
-         int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
-
-         /* If this is a reference to an external symbol, we want
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
-              nop
-              <op>     $treg,0($tempreg)
-            Otherwise we want
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
-              nop
-              addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
-              <op>     $treg,0($tempreg)
-
-            For NewABI, we want
-              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_PAGE)
-              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_MIPS_GOT_OFST)
+      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;
 
-            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)
+    case M_ROL:
+      if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+       {
+         if (dreg == sreg)
            {
-             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;
+             tempreg = AT;
+             used_at = 1;
            }
-         expr1.X_add_number = offset_expr.X_add_number;
-         offset_expr.X_add_number = 0;
-         if (expr1.X_add_number < -0x8000
-             || expr1.X_add_number >= 0x8000)
-           as_bad (_("PIC code offset overflow (max 16 signed bits)"));
-         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
-                      lw_reloc_type, mips_gp_register);
-         load_delay_nop ();
-         relax_start (offset_expr.X_add_symbol);
-         relax_switch ();
-         macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
-                      tempreg, BFD_RELOC_LO16);
-         relax_end ();
-         if (breg != 0)
-           macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                        tempreg, tempreg, breg);
-         macro_build (&expr1, s, fmt, treg, BFD_RELOC_LO16, tempreg);
+         else
+           {
+             tempreg = dreg;
+           }
+         macro_build (NULL, "negu", "d,w", tempreg, treg);
+         macro_build (NULL, "rorv", "d,t,s", dreg, sreg, tempreg);
+         break;
        }
-      else if (mips_big_got && !HAVE_NEWABI)
-       {
-         int gpdelay;
+      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;
 
-         /* 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", "t,u", 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_DROL_I:
+      {
+       unsigned int rot;
+       char *l;
+       char *rr;
 
-         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", "t,u", 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 (imm_expr.X_op != O_constant)
+         as_bad (_("Improper rotate count"));
+       rot = imm_expr.X_add_number & 0x3f;
+       if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+         {
+           rot = (64 - rot) & 0x3f;
+           if (rot >= 32)
+             macro_build (NULL, "dror32", SHFT_FMT, dreg, sreg, rot - 32);
+           else
+             macro_build (NULL, "dror", SHFT_FMT, dreg, sreg, rot);
+           break;
+         }
+       if (rot == 0)
+         {
+           macro_build (NULL, "dsrl", SHFT_FMT, dreg, sreg, 0);
+           break;
+         }
+       l = (rot < 0x20) ? "dsll" : "dsll32";
+       rr = ((0x40 - rot) < 0x20) ? "dsrl" : "dsrl32";
+       rot &= 0x1f;
+       used_at = 1;
+       macro_build (NULL, l, SHFT_FMT, AT, sreg, rot);
+       macro_build (NULL, rr, SHFT_FMT, dreg, sreg, (0x20 - rot) & 0x1f);
+       macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+      }
+      break;
+
+    case M_ROL_I:
+      {
+       unsigned int rot;
+
+       if (imm_expr.X_op != O_constant)
+         as_bad (_("Improper rotate count"));
+       rot = imm_expr.X_add_number & 0x1f;
+       if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+         {
+           macro_build (NULL, "ror", SHFT_FMT, dreg, sreg, (32 - rot) & 0x1f);
+           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;
 
-         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_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;
        }
-      else
-       abort ();
+      used_at = 1;
+      macro_build (NULL, "dsubu", "d,v,t", AT, ZERO, treg);
+      macro_build (NULL, "dsllv", "d,t,s", AT, sreg, AT);
+      macro_build (NULL, "dsrlv", "d,t,s", dreg, sreg, treg);
+      macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+      break;
 
+    case M_ROR:
+      if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+       {
+         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;
 
-    case M_LI:
-    case M_LI_S:
-      load_register (treg, &imm_expr, 0);
+    case M_DROR_I:
+      {
+       unsigned int rot;
+       char *l;
+       char *rr;
+
+       if (imm_expr.X_op != O_constant)
+         as_bad (_("Improper rotate count"));
+       rot = imm_expr.X_add_number & 0x3f;
+       if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+         {
+           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 M_DLI:
-      load_register (treg, &imm_expr, 1);
+    case M_ROR_I:
+      {
+       unsigned int rot;
+
+       if (imm_expr.X_op != O_constant)
+         as_bad (_("Improper rotate count"));
+       rot = imm_expr.X_add_number & 0x1f;
+       if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+         {
+           macro_build (NULL, "ror", SHFT_FMT, dreg, sreg, rot);
+           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;
 
-    case M_LI_SS:
-      if (imm_expr.X_op == O_constant)
+    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
        {
-         used_at = 1;
-         load_register (AT, &imm_expr, 0);
-         macro_build (NULL, "mtc1", "t,G", AT, treg);
+         macro_build (NULL, "xor", "d,v,t", dreg, sreg, treg);
+         macro_build (&expr1, "sltiu", "t,r,j", dreg, dreg, BFD_RELOC_LO16);
+       }
+      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;
        }
-      else
+      if (sreg == 0)
        {
-         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);
+         as_warn (_("Instruction %s: result is always false"),
+                  ip->insn_mo->name);
+         move_register (dreg, 0);
          break;
        }
-
-    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 (CPU_HAS_SEQ (mips_opts.arch)
+         && -512 <= imm_expr.X_add_number
+         && imm_expr.X_add_number < 512)
        {
-         if (HAVE_64BIT_GPRS)
-           load_register (treg, &imm_expr, 1);
-         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);
-                   }
-               }
-           }
+         macro_build (NULL, "seqi", "t,r,+Q", dreg, sreg,
+                      (int) imm_expr.X_add_number);
          break;
        }
-
-      /* We know that sym is in the .rdata section.  First we get the
-        upper 16 bits of the address.  */
-      if (mips_pic == NO_PIC)
+      if (imm_expr.X_op == O_constant
+         && imm_expr.X_add_number >= 0
+         && imm_expr.X_add_number < 0x10000)
        {
-         macro_build_lui (&offset_expr, AT);
-         used_at = 1;
+         macro_build (&imm_expr, "xori", "t,r,i", dreg, sreg, BFD_RELOC_LO16);
        }
-      else
+      else if (imm_expr.X_op == O_constant
+              && imm_expr.X_add_number > -0x8000
+              && imm_expr.X_add_number < 0)
        {
-         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
-                      BFD_RELOC_MIPS_GOT16, mips_gp_register);
-         used_at = 1;
+         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);
        }
-
-      /* Now we load the register(s).  */
-      if (HAVE_64BIT_GPRS)
+      else if (CPU_HAS_SEQ (mips_opts.arch))
        {
          used_at = 1;
-         macro_build (&offset_expr, "ld", "t,o(b)", treg, BFD_RELOC_LO16, AT);
+         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 (&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);
-           }
        }
+      macro_build (&expr1, "sltiu", "t,r,j", dreg, dreg, BFD_RELOC_LO16);
       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)
-       {
-         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;
-       }
+    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;
 
-      gas_assert (offset_expr.X_op == O_symbol
-                 && offset_expr.X_add_number == 0);
-      s = segment_name (S_GET_SEGMENT (offset_expr.X_add_symbol));
-      if (strcmp (s, ".lit8") == 0)
-       {
-         if (mips_opts.isa != ISA_MIPS1)
-           {
-             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;
+    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
        {
-         gas_assert (strcmp (s, RDATA_SECTION_NAME) == 0);
+         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;
-         if (mips_pic != NO_PIC)
-           macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
-                        BFD_RELOC_MIPS_GOT16, mips_gp_register);
-         else
-           {
-             /* FIXME: This won't work for a 64 bit address.  */
-             macro_build_lui (&offset_expr, AT);
-           }
-
-         if (mips_opts.isa != ISA_MIPS1)
-           {
-             macro_build (&offset_expr, "ldc1", "T,o(b)",
-                          treg, BFD_RELOC_LO16, AT);
-             break;
-           }
-         breg = AT;
-         r = BFD_RELOC_LO16;
-         goto dob;
        }
+      macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
+      break;
 
-    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.isa == ISA_MIPS1);
-      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);
+    case M_SGT:                /* sreg > treg  <==>  treg < sreg */
+      s = "slt";
+      goto sgt;
+    case M_SGTU:
+      s = "sltu";
+    sgt:
+      macro_build (NULL, s, "d,v,t", dreg, treg, sreg);
       break;
 
-    case M_S_DOB:
-      gas_assert (mips_opts.isa == ISA_MIPS1);
-      /* 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);
+    case M_SGT_I:              /* sreg > I  <==>  I < sreg */
+      s = "slt";
+      goto sgti;
+    case M_SGTU_I:
+      s = "sltu";
+    sgti:
+      used_at = 1;
+      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+      macro_build (NULL, s, "d,v,t", dreg, AT, sreg);
       break;
 
-    case M_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;
-      if (mips_opts.isa != ISA_MIPS1)
-       {
-         s = "ldc1";
-         goto ld;
-       }
+    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;
 
-      s = "lwc1";
-      fmt = "T,o(b)";
-      goto ldd_std;
+    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;
 
-    case M_S_DAB:
-      if (mips_opts.isa != ISA_MIPS1)
+    case M_SLT_I:
+      if (imm_expr.X_op == O_constant
+         && imm_expr.X_add_number >= -0x8000
+         && imm_expr.X_add_number < 0x8000)
        {
-         s = "sdc1";
-         goto st;
+         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;
 
-      s = "swc1";
-      fmt = "T,o(b)";
-      /* Itbl support may require additional care here.  */
-      coproc = 1;
-      goto ldd_std;
-
-    case M_LD_AB:
-      if (HAVE_64BIT_GPRS)
+    case M_SLTU_I:
+      if (imm_expr.X_op == O_constant
+         && imm_expr.X_add_number >= -0x8000
+         && imm_expr.X_add_number < 0x8000)
        {
-         s = "ld";
-         goto ld;
+         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;
 
-      s = "lw";
-      fmt = "t,o(b)";
-      goto ldd_std;
-
-    case M_SD_AB:
-      if (HAVE_64BIT_GPRS)
+    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
        {
-         s = "sd";
-         goto st;
+         macro_build (NULL, "xor", "d,v,t", dreg, sreg, treg);
+         macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
        }
+      break;
 
-      s = "sw";
-      fmt = "t,o(b)";
-
-    ldd_std:
-      if (offset_expr.X_op != O_symbol
-         && offset_expr.X_op != O_constant)
+    case M_SNE_I:
+      if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
        {
-         as_bad (_("Expression too complex"));
-         offset_expr.X_op = O_constant;
+         macro_build (NULL, "sltu", "d,v,t", dreg, 0, sreg);
+         break;
        }
-
-      if (HAVE_32BIT_ADDRESSES
-         && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+      if (sreg == 0)
        {
-         char value [32];
-
-         sprintf_vma (value, offset_expr.X_add_number);
-         as_bad (_("Number (0x%s) larger than 32 bits"), value);
+         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;
        }
-
-      /* 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 (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))
        {
-         /* If this is a reference to a GP relative symbol, we want
-              <op>     $treg,<sym>($gp)        (BFD_RELOC_GPREL16)
-              <op>     $treg+1,<sym>+4($gp)    (BFD_RELOC_GPREL16)
-            If we have a base register, we use this
-              addu     $at,$breg,$gp
-              <op>     $treg,<sym>($at)        (BFD_RELOC_GPREL16)
-              <op>     $treg+1,<sym>+4($at)    (BFD_RELOC_GPREL16)
-            If this is not a GP relative symbol, we want
-              lui      $at,<sym>               (BFD_RELOC_HI16_S)
-              <op>     $treg,<sym>($at)        (BFD_RELOC_LO16)
-              <op>     $treg+1,<sym>+4($at)    (BFD_RELOC_LO16)
-            If there is a base register, we add it to $at after the
-            lui instruction.  If there is a constant, we always use
-            the last case.  */
-         if (offset_expr.X_op == O_symbol
-             && (valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
-             && !nopic_need_relax (offset_expr.X_add_symbol, 1))
-           {
-             relax_start (offset_expr.X_add_symbol);
-             if (breg == 0)
-               {
-                 tempreg = mips_gp_register;
-               }
-             else
-               {
-                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
-                              AT, breg, mips_gp_register);
-                 tempreg = AT;
-                 used_at = 1;
-               }
-
-             /* Itbl support may require additional care here.  */
-             macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
-                          BFD_RELOC_GPREL16, tempreg);
-             offset_expr.X_add_number += 4;
-
-             /* Set mips_optimize to 2 to avoid inserting an
-                 undesired nop.  */
-             hold_mips_optimize = mips_optimize;
-             mips_optimize = 2;
-             /* Itbl support may require additional care here.  */
-             macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
-                          BFD_RELOC_GPREL16, tempreg);
-             mips_optimize = hold_mips_optimize;
-
-             relax_switch ();
-
-             offset_expr.X_add_number -= 4;
-           }
          used_at = 1;
-         macro_build_lui (&offset_expr, AT);
-         if (breg != 0)
-           macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
-         /* Itbl support may require additional care here.  */
-         macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
-                      BFD_RELOC_LO16, AT);
-         /* FIXME: How do we handle overflow here?  */
-         offset_expr.X_add_number += 4;
-         /* Itbl support may require additional care here.  */
-         macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
-                      BFD_RELOC_LO16, AT);
-         if (mips_relax.sequence)
-           relax_end ();
+         load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+         macro_build (NULL, "sne", "d,v,t", dreg, sreg, AT);
+         break;
        }
-      else if (!mips_big_got)
+      else
        {
-         /* 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.  */
+         load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+         macro_build (NULL, "xor", "d,v,t", dreg, sreg, AT);
          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 ();
+       }
+      macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
+      break;
 
-         mips_optimize = hold_mips_optimize;
+    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;
        }
-      else if (mips_big_got)
+      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)
        {
-         int gpdelay;
+         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, 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", "t,u",
-                      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 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;
 
-         /* 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 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 */
 
-         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;
+      /*
+       * 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;
 
-         /* 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 ();
+    case M_ULH_A:
+      ab = 1;
+    case M_ULH:
+      s = "lb";
+      s2 = "lbu";
+      off = 1;
+      goto uld_st;
+    case M_ULHU_A:
+      ab = 1;
+    case M_ULHU:
+      s = "lbu";
+      s2 = "lbu";
+      off = 1;
+      goto uld_st;
+    case M_ULW_A:
+      ab = 1;
+    case M_ULW:
+      s = "lwl";
+      s2 = "lwr";
+      offbits = (mips_opts.micromips ? 12 : 16);
+      off = 3;
+      goto uld_st;
+    case M_ULD_A:
+      ab = 1;
+    case M_ULD:
+      s = "ldl";
+      s2 = "ldr";
+      offbits = (mips_opts.micromips ? 12 : 16);
+      off = 7;
+      goto uld_st;
+    case M_USH_A:
+      ab = 1;
+    case M_USH:
+      s = "sb";
+      s2 = "sb";
+      off = 1;
+      ust = 1;
+      goto uld_st;
+    case M_USW_A:
+      ab = 1;
+    case M_USW:
+      s = "swl";
+      s2 = "swr";
+      offbits = (mips_opts.micromips ? 12 : 16);
+      off = 3;
+      ust = 1;
+      goto uld_st;
+    case M_USD_A:
+      ab = 1;
+    case M_USD:
+      s = "sdl";
+      s2 = "sdr";
+      offbits = (mips_opts.micromips ? 12 : 16);
+      off = 7;
+      ust = 1;
+
+    uld_st:
+      if (!ab && offset_expr.X_add_number >= 0x8000 - off)
+       as_bad (_("Operand overflow"));
+
+      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)
+       {
+         used_at = 1;
+         tempreg = AT;
        }
       else
-       abort ();
+       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 necessary, move the result in tempreg to the final destination.  */
+      if (!ust && treg != tempreg)
+        {
+         /* Protect second load's delay slot.  */
+         load_delay_nop ();
+         move_register (treg, tempreg);
+       }
       break;
 
-    case M_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)
+    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 (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);
+
+      /* For M_USH_A re-retrieve the LSB.  */
+      if (ust && ab)
        {
-         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);
+         if (target_big_endian)
+           ep->X_add_number += off;
+         else
+           ep->X_add_number -= off;
+         macro_build (&expr1, "lbu", "t,o(b)", AT, BFD_RELOC_LO16, AT);
+       }
+      /* For ULH and M_USH_A OR the LSB in.  */
+      if (!ust || ab)
+       {
+         tempreg = !ab ? AT : treg;
+         macro_build (NULL, "sll", SHFT_FMT, tempreg, tempreg, 8);
+         macro_build (NULL, "or", "d,v,t", treg, treg, AT);
        }
       break;
 
-   /* 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.
+    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\""));
+}
 
-      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?
+/* Implement macros in mips16 mode.  */
 
-      If an itbl is provided to interpret cop instructions,
-      this knowledge can be encoded in the itbl spec.  */
+static void
+mips16_macro (struct mips_cl_insn *ip)
+{
+  int mask;
+  int xreg, yreg, zreg, tmp;
+  expressionS expr1;
+  int dbl;
+  const char *s, *s2, *s3;
 
-    case 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:
-      if (NO_ISA_COP (mips_opts.arch)
-         && (ip->insn_mo->pinfo2 & INSN2_M_FP_S) == 0)
-       {
-         as_bad (_("opcode not supported on this processor: %s"),
-                 mips_cpu_info_from_arch (mips_opts.arch)->name);
-         break;
-       }
+  mask = ip->insn_mo->mask;
 
-      /* 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);
+  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_MOVE:
-      move_register (dreg, sreg);
+    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", "s,t", sreg, treg);
-      macro_build (NULL, "mflo", "d", dreg);
+      macro_build (NULL, dbl ? "dmultu" : "multu", "x,y", xreg, yreg);
+      macro_build (NULL, "mflo", "x", zreg);
       break;
 
-    case M_DMUL_I:
+    case M_DSUBU_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", "d", dreg);
+      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_DMULO_I:
-      dbl = 1;
-    case M_MULO_I:
-      imm = 1;
-      goto do_mulo;
+    case M_SUBU_I_2:
+      if (imm_expr.X_op != O_constant)
+       as_bad (_("Unsupported large constant"));
+      imm_expr.X_add_number = -imm_expr.X_add_number;
+      macro_build (&imm_expr, "addiu", "x,k", xreg);
+      break;
 
-    case 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", "d", dreg);
-      macro_build (NULL, dbl ? "dsra32" : "sra", "d,w,<", dreg, dreg, RA);
-      macro_build (NULL, "mfhi", "d", AT);
-      if (mips_trap)
-       macro_build (NULL, "tne", "s,t,q", dreg, AT, 6);
-      else
-       {
-         expr1.X_add_number = 8;
-         macro_build (&expr1, "beq", "s,t,p", dreg, AT);
-         macro_build (NULL, "nop", "");
-         macro_build (NULL, "break", "c", 6);
-       }
-      end_noreorder ();
-      macro_build (NULL, "mflo", "d", dreg);
+    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 M_BEQ:
+      s = "cmp";
+      s2 = "bteqz";
+      goto do_branch;
+    case M_BNE:
+      s = "cmp";
+      s2 = "btnez";
+      goto do_branch;
+    case M_BLT:
+      s = "slt";
+      s2 = "btnez";
+      goto do_branch;
+    case M_BLTU:
+      s = "sltu";
+      s2 = "btnez";
+      goto do_branch;
+    case M_BLE:
+      s = "slt";
+      s2 = "bteqz";
+      goto do_reverse_branch;
+    case M_BLEU:
+      s = "sltu";
+      s2 = "bteqz";
+      goto do_reverse_branch;
+    case M_BGE:
+      s = "slt";
+      s2 = "bteqz";
+      goto do_branch;
+    case M_BGEU:
+      s = "sltu";
+      s2 = "bteqz";
+      goto do_branch;
+    case M_BGT:
+      s = "slt";
+      s2 = "btnez";
+      goto do_reverse_branch;
+    case M_BGTU:
+      s = "sltu";
+      s2 = "btnez";
+
+    do_reverse_branch:
+      tmp = xreg;
+      xreg = yreg;
+      yreg = tmp;
+
+    do_branch:
+      macro_build (NULL, s, "x,y", xreg, yreg);
+      macro_build (&offset_expr, s2, "p");
       break;
 
-    case M_DMULOU_I:
-      dbl = 1;
-    case M_MULOU_I:
-      imm = 1;
-      goto do_mulou;
+    case M_BEQ_I:
+      s = "cmpi";
+      s2 = "bteqz";
+      s3 = "x,U";
+      goto do_branch_i;
+    case M_BNE_I:
+      s = "cmpi";
+      s2 = "btnez";
+      s3 = "x,U";
+      goto do_branch_i;
+    case M_BLT_I:
+      s = "slti";
+      s2 = "btnez";
+      s3 = "x,8";
+      goto do_branch_i;
+    case M_BLTU_I:
+      s = "sltiu";
+      s2 = "btnez";
+      s3 = "x,8";
+      goto do_branch_i;
+    case M_BLE_I:
+      s = "slti";
+      s2 = "btnez";
+      s3 = "x,8";
+      goto do_addone_branch_i;
+    case M_BLEU_I:
+      s = "sltiu";
+      s2 = "btnez";
+      s3 = "x,8";
+      goto do_addone_branch_i;
+    case M_BGE_I:
+      s = "slti";
+      s2 = "bteqz";
+      s3 = "x,8";
+      goto do_branch_i;
+    case M_BGEU_I:
+      s = "sltiu";
+      s2 = "bteqz";
+      s3 = "x,8";
+      goto do_branch_i;
+    case M_BGT_I:
+      s = "slti";
+      s2 = "bteqz";
+      s3 = "x,8";
+      goto do_addone_branch_i;
+    case M_BGTU_I:
+      s = "sltiu";
+      s2 = "bteqz";
+      s3 = "x,8";
+
+    do_addone_branch_i:
+      if (imm_expr.X_op != O_constant)
+       as_bad (_("Unsupported large constant"));
+      ++imm_expr.X_add_number;
 
-    case M_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", "d", AT);
-      macro_build (NULL, "mflo", "d", dreg);
-      if (mips_trap)
-       macro_build (NULL, "tne", "s,t,q", AT, ZERO, 6);
-      else
-       {
-         expr1.X_add_number = 8;
-         macro_build (&expr1, "beq", "s,t,p", AT, ZERO);
-         macro_build (NULL, "nop", "");
-         macro_build (NULL, "break", "c", 6);
-       }
-      end_noreorder ();
+    do_branch_i:
+      macro_build (&imm_expr, s, s3, xreg);
+      macro_build (&offset_expr, s2, "p");
       break;
 
-    case M_DROL:
-      if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
-       {
-         if (dreg == sreg)
-           {
-             tempreg = AT;
-             used_at = 1;
-           }
-         else
-           {
-             tempreg = dreg;
-           }
-         macro_build (NULL, "dnegu", "d,w", tempreg, treg);
-         macro_build (NULL, "drorv", "d,t,s", dreg, sreg, tempreg);
-         break;
-       }
-      used_at = 1;
-      macro_build (NULL, "dsubu", "d,v,t", AT, ZERO, treg);
-      macro_build (NULL, "dsrlv", "d,t,s", AT, sreg, AT);
-      macro_build (NULL, "dsllv", "d,t,s", dreg, sreg, treg);
-      macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
-      break;
+    case M_ABS:
+      expr1.X_add_number = 0;
+      macro_build (&expr1, "slti", "x,8", yreg);
+      if (xreg != yreg)
+       move_register (xreg, yreg);
+      expr1.X_add_number = 2;
+      macro_build (&expr1, "bteqz", "p");
+      macro_build (NULL, "neg", "x,w", xreg, xreg);
+    }
+}
 
-    case M_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;
+/* For consistency checking, verify that all bits are specified either
+   by the match/mask part of the instruction definition, or by the
+   operand list.  */
+static int
+validate_mips_insn (const struct mips_opcode *opc)
+{
+  const char *p = opc->args;
+  char c;
+  unsigned long used_bits = opc->mask;
 
-    case M_DROL_I:
+  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++)
       {
-       unsigned int rot;
-       char *l;
-       char *rr;
-
-       if (imm_expr.X_op != O_constant)
-         as_bad (_("Improper rotate count"));
-       rot = imm_expr.X_add_number & 0x3f;
-       if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
-         {
-           rot = (64 - rot) & 0x3f;
-           if (rot >= 32)
-             macro_build (NULL, "dror32", "d,w,<", dreg, sreg, rot - 32);
-           else
-             macro_build (NULL, "dror", "d,w,<", dreg, sreg, rot);
-           break;
-         }
-       if (rot == 0)
+      case ',': break;
+      case '(': break;
+      case ')': break;
+      case '+':
+       switch (c = *p++)
          {
-           macro_build (NULL, "dsrl", "d,w,<", dreg, sreg, 0);
-           break;
+         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;
          }
-       l = (rot < 0x20) ? "dsll" : "dsll32";
-       rr = ((0x40 - rot) < 0x20) ? "dsrl" : "dsrl32";
-       rot &= 0x1f;
-       used_at = 1;
-       macro_build (NULL, l, "d,w,<", AT, sreg, rot);
-       macro_build (NULL, rr, "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f);
-       macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+       break;
+      case '<': USE_BITS (OP_MASK_SHAMT,       OP_SH_SHAMT);   break;
+      case '>':        USE_BITS (OP_MASK_SHAMT,        OP_SH_SHAMT);   break;
+      case 'A': break;
+      case 'B': USE_BITS (OP_MASK_CODE20,       OP_SH_CODE20);  break;
+      case 'C':        USE_BITS (OP_MASK_COPZ,         OP_SH_COPZ);    break;
+      case 'D':        USE_BITS (OP_MASK_FD,           OP_SH_FD);      break;
+      case 'E':        USE_BITS (OP_MASK_RT,           OP_SH_RT);      break;
+      case 'F': break;
+      case 'G':        USE_BITS (OP_MASK_RD,           OP_SH_RD);      break;
+      case 'H': USE_BITS (OP_MASK_SEL,         OP_SH_SEL);     break;
+      case 'I': break;
+      case 'J': USE_BITS (OP_MASK_CODE19,       OP_SH_CODE19);  break;
+      case 'K':        USE_BITS (OP_MASK_RD,           OP_SH_RD);      break;
+      case 'L': break;
+      case 'M':        USE_BITS (OP_MASK_CCC,          OP_SH_CCC);     break;
+      case 'N':        USE_BITS (OP_MASK_BCC,          OP_SH_BCC);     break;
+      case 'O':        USE_BITS (OP_MASK_ALN,          OP_SH_ALN);     break;
+      case 'Q':        USE_BITS (OP_MASK_VSEL,         OP_SH_VSEL);
+               USE_BITS (OP_MASK_FT,           OP_SH_FT);      break;
+      case 'R':        USE_BITS (OP_MASK_FR,           OP_SH_FR);      break;
+      case 'S':        USE_BITS (OP_MASK_FS,           OP_SH_FS);      break;
+      case 'T':        USE_BITS (OP_MASK_FT,           OP_SH_FT);      break;
+      case 'V':        USE_BITS (OP_MASK_FS,           OP_SH_FS);      break;
+      case 'W':        USE_BITS (OP_MASK_FT,           OP_SH_FT);      break;
+      case 'X':        USE_BITS (OP_MASK_FD,           OP_SH_FD);      break;
+      case 'Y':        USE_BITS (OP_MASK_FS,           OP_SH_FS);      break;
+      case 'Z':        USE_BITS (OP_MASK_FT,           OP_SH_FT);      break;
+      case 'a':        USE_BITS (OP_MASK_TARGET,       OP_SH_TARGET);  break;
+      case 'b':        USE_BITS (OP_MASK_RS,           OP_SH_RS);      break;
+      case 'c':        USE_BITS (OP_MASK_CODE,         OP_SH_CODE);    break;
+      case 'd':        USE_BITS (OP_MASK_RD,           OP_SH_RD);      break;
+      case 'f': break;
+      case 'h':        USE_BITS (OP_MASK_PREFX,        OP_SH_PREFX);   break;
+      case 'i':        USE_BITS (OP_MASK_IMMEDIATE,    OP_SH_IMMEDIATE); break;
+      case 'j':        USE_BITS (OP_MASK_DELTA,        OP_SH_DELTA);   break;
+      case 'k':        USE_BITS (OP_MASK_CACHE,        OP_SH_CACHE);   break;
+      case 'l': break;
+      case 'o': USE_BITS (OP_MASK_DELTA,       OP_SH_DELTA);   break;
+      case 'p':        USE_BITS (OP_MASK_DELTA,        OP_SH_DELTA);   break;
+      case 'q':        USE_BITS (OP_MASK_CODE2,        OP_SH_CODE2);   break;
+      case 'r': USE_BITS (OP_MASK_RS,          OP_SH_RS);      break;
+      case 's':        USE_BITS (OP_MASK_RS,           OP_SH_RS);      break;
+      case 't':        USE_BITS (OP_MASK_RT,           OP_SH_RT);      break;
+      case 'u':        USE_BITS (OP_MASK_IMMEDIATE,    OP_SH_IMMEDIATE); break;
+      case 'v':        USE_BITS (OP_MASK_RS,           OP_SH_RS);      break;
+      case 'w':        USE_BITS (OP_MASK_RT,           OP_SH_RT);      break;
+      case 'x': break;
+      case 'z': break;
+      case 'P': USE_BITS (OP_MASK_PERFREG,     OP_SH_PERFREG); break;
+      case 'U': USE_BITS (OP_MASK_RD,           OP_SH_RD);
+               USE_BITS (OP_MASK_RT,           OP_SH_RT);      break;
+      case 'e': USE_BITS (OP_MASK_VECBYTE,     OP_SH_VECBYTE); break;
+      case '%': USE_BITS (OP_MASK_VECALIGN,    OP_SH_VECALIGN); break;
+      case '[': break;
+      case ']': break;
+      case '1':        USE_BITS (OP_MASK_SHAMT,        OP_SH_SHAMT);   break;
+      case '2': USE_BITS (OP_MASK_BP,          OP_SH_BP);      break;
+      case '3': USE_BITS (OP_MASK_SA3,         OP_SH_SA3);     break;
+      case '4': USE_BITS (OP_MASK_SA4,         OP_SH_SA4);     break;
+      case '5': USE_BITS (OP_MASK_IMM8,        OP_SH_IMM8);    break;
+      case '6': USE_BITS (OP_MASK_RS,          OP_SH_RS);      break;
+      case '7': USE_BITS (OP_MASK_DSPACC,      OP_SH_DSPACC);  break;
+      case '8': USE_BITS (OP_MASK_WRDSP,       OP_SH_WRDSP);   break;
+      case '9': USE_BITS (OP_MASK_DSPACC_S,    OP_SH_DSPACC_S);break;
+      case '0': USE_BITS (OP_MASK_DSPSFT,      OP_SH_DSPSFT);  break;
+      case '\'': USE_BITS (OP_MASK_RDDSP,      OP_SH_RDDSP);   break;
+      case ':': USE_BITS (OP_MASK_DSPSFT_7,    OP_SH_DSPSFT_7);break;
+      case '@': USE_BITS (OP_MASK_IMM10,       OP_SH_IMM10);   break;
+      case '!': USE_BITS (OP_MASK_MT_U,                OP_SH_MT_U);    break;
+      case '$': USE_BITS (OP_MASK_MT_H,                OP_SH_MT_H);    break;
+      case '*': USE_BITS (OP_MASK_MTACC_T,     OP_SH_MTACC_T); break;
+      case '&': USE_BITS (OP_MASK_MTACC_D,     OP_SH_MTACC_D); break;
+      case '\\': USE_BITS (OP_MASK_3BITPOS,    OP_SH_3BITPOS); break;
+      case '~': USE_BITS (OP_MASK_OFFSET12,    OP_SH_OFFSET12); break;
+      case 'g': USE_BITS (OP_MASK_RD,          OP_SH_RD);      break;
+      default:
+       as_bad (_("internal: bad mips opcode (unknown operand type `%c'): %s %s"),
+               c, opc->name, opc->args);
+       return 0;
       }
-      break;
-
-    case M_ROL_I:
-      {
-       unsigned int rot;
+#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 (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", "d,w,<", dreg, sreg, (32 - rot) & 0x1f);
-           break;
-         }
-       if (rot == 0)
-         {
-           macro_build (NULL, "srl", "d,w,<", dreg, sreg, 0);
-           break;
-         }
-       used_at = 1;
-       macro_build (NULL, "sll", "d,w,<", AT, sreg, rot);
-       macro_build (NULL, "srl", "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f);
-       macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
-      }
-      break;
+/* 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.  */
 
-    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;
+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;
 
-    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 ((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;
+    }
 
-    case M_DROR_I:
+  /* 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++)
       {
-       unsigned int rot;
-       char *l;
-       char *rr;
-
-       if (imm_expr.X_op != O_constant)
-         as_bad (_("Improper rotate count"));
-       rot = imm_expr.X_add_number & 0x3f;
-       if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+      case ',': break;
+      case '(': break;
+      case ')': break;
+      case '+':
+       e = c;
+       switch (c = *p++)
          {
-           if (rot >= 32)
-             macro_build (NULL, "dror32", "d,w,<", dreg, sreg, rot - 32);
-           else
-             macro_build (NULL, "dror", "d,w,<", dreg, sreg, rot);
-           break;
+         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;
          }
-       if (rot == 0)
+       break;
+      case 'm':
+       e = c;
+       switch (c = *p++)
          {
-           macro_build (NULL, "dsrl", "d,w,<", dreg, sreg, 0);
-           break;
+         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;
          }
-       rr = (rot < 0x20) ? "dsrl" : "dsrl32";
-       l = ((0x40 - rot) < 0x20) ? "dsll" : "dsll32";
-       rot &= 0x1f;
-       used_at = 1;
-       macro_build (NULL, rr, "d,w,<", AT, sreg, rot);
-       macro_build (NULL, l, "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f);
-       macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+       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;
       }
-      break;
+#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;
+}
 
-    case M_ROR_I:
-      {
-       unsigned int rot;
+/* UDI immediates.  */
+struct mips_immed {
+  char         type;
+  unsigned int shift;
+  unsigned long        mask;
+  const char * desc;
+};
 
-       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", "d,w,<", dreg, sreg, rot);
-           break;
-         }
-       if (rot == 0)
-         {
-           macro_build (NULL, "srl", "d,w,<", dreg, sreg, 0);
-           break;
-         }
-       used_at = 1;
-       macro_build (NULL, "srl", "d,w,<", AT, sreg, rot);
-       macro_build (NULL, "sll", "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f);
-       macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
-      }
-      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_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;
+/* 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_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))
+  if (insn->pinfo == INSN_MACRO)
+    /* Let a macro pass, we'll catch it later when it is expanded.  */
+    return 1;
+
+  if (ISA_HAS_ODD_SINGLE_FPR (mips_opts.isa) || (mips_opts.arch == CPU_R5900))
+    {
+      /* Allow odd registers for single-precision ops.  */
+      switch (insn->pinfo & (FP_S | FP_D))
        {
-         used_at = 1;
-         load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-         macro_build (NULL, "seq", "d,v,t", dreg, sreg, AT);
+       case FP_S:
+       case 0:
+         return 1;     /* both single precision - ok */
+       case FP_D:
+         return 0;     /* both double precision - fail */
+       default:
          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 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;
+      /* Cvt.w.x and cvt.x.w allow an odd register for a 'w' or 's' operand.  */
+      s = strchr (insn->name, '.');
+      if (argnum == 2)
+       s = s != NULL ? strchr (s + 1, '.') : NULL;
+      return (s != NULL && (s[1] == 'w' || s[1] == 's'));
+    } 
+
+  /* Single-precision coprocessor loads and moves are OK too.  */
+  if ((insn->pinfo & FP_S)
+      && (insn->pinfo & (INSN_COPROC_MEMORY_DELAY | INSN_STORE_MEMORY
+                        | INSN_LOAD_COPROC_DELAY | INSN_COPROC_MOVE_DELAY)))
+    return 1;
+
+  return 0;
+}
+
+/* Check if EXPR is a constant between MIN (inclusive) and MAX (exclusive)
+   taking bits from BIT up.  */
+static int
+expr_const_in_range (expressionS *ep, offsetT min, offsetT max, int bit)
+{
+  return (ep->X_op == O_constant
+         && (ep->X_add_number & ((1 << bit) - 1)) == 0
+         && ep->X_add_number >= min << bit
+         && ep->X_add_number < max << bit);
+}
 
-    case M_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;
+/* This routine assembles an instruction into its binary format.  As a
+   side effect, it sets one of the global variables imm_reloc or
+   offset_reloc to the type of relocation to do if one of the operands
+   is an address expression.  */
 
-    case M_SGT:                /* 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;
+static void
+mips_ip (char *str, struct mips_cl_insn *ip)
+{
+  bfd_boolean wrong_delay_slot_insns = FALSE;
+  bfd_boolean need_delay_slot_ok = TRUE;
+  struct mips_opcode *firstinsn = NULL;
+  const struct mips_opcode *past;
+  struct hash_control *hash;
+  char *s;
+  const char *args;
+  char c = 0;
+  struct mips_opcode *insn;
+  char *argsStart;
+  unsigned int regno;
+  unsigned int lastregno;
+  unsigned int destregno = 0;
+  unsigned int lastpos = 0;
+  unsigned int limlo, limhi;
+  int sizelo;
+  char *s_reset;
+  offsetT min_range, max_range;
+  long opend;
+  char *name;
+  int argnum;
+  unsigned int rtype;
+  char *dot;
+  long end;
 
-    case M_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;
+  insn_error = NULL;
 
-    case M_SLE:        /* sreg <= treg  <==>  treg >= sreg  <==>  not (treg < sreg) */
-      s = "slt";
-      goto sle;
-    case M_SLEU:
-      s = "sltu";
-    sle:
-      macro_build (NULL, s, "d,v,t", dreg, treg, sreg);
-      macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
-      break;
+  if (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_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;
+  /* We first try to match an instruction up to a space or to the end.  */
+  for (end = 0; str[end] != '\0' && !ISSPACE (str[end]); end++)
+    continue;
 
-    case M_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;
+  /* Make a copy of the instruction so that we can fiddle with it.  */
+  name = alloca (end + 1);
+  memcpy (name, str, end);
+  name[end] = '\0';
 
-    case M_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;
+  for (;;)
+    {
+      insn = (struct mips_opcode *) hash_find (hash, name);
 
-    case M_SNE:
-      if (sreg == 0)
-       macro_build (NULL, "sltu", "d,v,t", dreg, 0, treg);
-      else if (treg == 0)
-       macro_build (NULL, "sltu", "d,v,t", dreg, 0, sreg);
+      if (insn != NULL || !mips_opts.micromips)
+       break;
+      if (forced_insn_length)
+       break;
+
+      /* See if there's an instruction size override suffix,
+         either `16' or `32', at the end of the mnemonic proper,
+         that defines the operation, i.e. before the first `.'
+         character if any.  Strip it and retry.  */
+      dot = strchr (name, '.');
+      opend = dot != NULL ? dot - name : end;
+      if (opend < 3)
+       break;
+      if (name[opend - 2] == '1' && name[opend - 1] == '6')
+       forced_insn_length = 2;
+      else if (name[opend - 2] == '3' && name[opend - 1] == '2')
+       forced_insn_length = 4;
       else
-       {
-         macro_build (NULL, "xor", "d,v,t", dreg, sreg, treg);
-         macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
-       }
-      break;
+       break;
+      memcpy (name + opend - 2, name + opend, end - opend + 1);
+    }
+  if (insn == NULL)
+    {
+      insn_error = _("Unrecognized opcode");
+      return;
+    }
 
-    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)
+  /* 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;
+
+      gas_assert (strcmp (insn->name, name) == 0);
+
+      ok = is_opcode_valid (insn);
+      size_ok = is_size_valid (insn);
+      delay_slot_ok = is_delay_slot_valid (insn);
+      if (!delay_slot_ok && !wrong_delay_slot_insns)
        {
-         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);
+         firstinsn = insn;
+         wrong_delay_slot_insns = TRUE;
        }
-      else if (CPU_HAS_SEQ (mips_opts.arch))
+      if (!ok || !size_ok || delay_slot_ok != need_delay_slot_ok)
        {
-         used_at = 1;
-         load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
-         macro_build (NULL, "sne", "d,v,t", dreg, sreg, AT);
-         break;
+         static char buf[256];
+
+         if (insn + 1 < past && strcmp (insn->name, insn[1].name) == 0)
+           {
+             ++insn;
+             continue;
+           }
+         if (wrong_delay_slot_insns && need_delay_slot_ok)
+           {
+             gas_assert (firstinsn);
+             need_delay_slot_ok = FALSE;
+             past = insn + 1;
+             insn = firstinsn;
+             continue;
+           }
+
+         if (insn_error)
+           return;
+
+         if (!ok)
+           sprintf (buf, _("Opcode not supported on this processor: %s (%s)"),
+                    mips_cpu_info_from_arch (mips_opts.arch)->name,
+                    mips_cpu_info_from_isa (mips_opts.isa)->name);
+         else
+           sprintf (buf, _("Unrecognized %u-bit version of microMIPS opcode"),
+                    8 * forced_insn_length);
+         insn_error = buf;
+
+         return;
        }
-      else
+
+      create_insn (ip, insn);
+      insn_error = NULL;
+      argnum = 1;
+      lastregno = 0xffffffff;
+      for (args = insn->args;; ++args)
        {
-         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;
+         int is_mdmx;
+
+         s += strspn (s, " \t");
+         is_mdmx = 0;
+         switch (*args)
+           {
+           case '\0':          /* end of args */
+             if (*s == '\0')
+               return;
+             break;
+
+           case '2':
+             /* DSP 2-bit unsigned immediate in bit 11 (for standard MIPS
+                code) or 14 (for microMIPS code).  */
+             my_getExpression (&imm_expr, s);
+             check_absolute_expr (ip, &imm_expr);
+             if ((unsigned long) imm_expr.X_add_number != 1
+                 && (unsigned long) imm_expr.X_add_number != 3)
+               {
+                 as_bad (_("BALIGN immediate not 1 or 3 (%lu)"),
+                         (unsigned long) imm_expr.X_add_number);
+               }
+             INSERT_OPERAND (mips_opts.micromips,
+                             BP, *ip, imm_expr.X_add_number);
+             imm_expr.X_op = O_absent;
+             s = expr_end;
+             continue;
+
+           case '3':
+             /* DSP 3-bit unsigned immediate in bit 13 (for standard MIPS
+                code) or 21 (for microMIPS code).  */
+             {
+               unsigned long mask = (mips_opts.micromips
+                                     ? MICROMIPSOP_MASK_SA3 : OP_MASK_SA3);
+
+               my_getExpression (&imm_expr, s);
+               check_absolute_expr (ip, &imm_expr);
+               if ((unsigned long) imm_expr.X_add_number > mask)
+                 as_bad (_("DSP immediate not in range 0..%lu (%lu)"),
+                         mask, (unsigned long) imm_expr.X_add_number);
+               INSERT_OPERAND (mips_opts.micromips,
+                               SA3, *ip, imm_expr.X_add_number);
+               imm_expr.X_op = O_absent;
+               s = expr_end;
+             }
+             continue;
+
+           case '4':
+             /* DSP 4-bit unsigned immediate in bit 12 (for standard MIPS
+                code) or 21 (for microMIPS code).  */
+             {
+               unsigned long mask = (mips_opts.micromips
+                                     ? MICROMIPSOP_MASK_SA4 : OP_MASK_SA4);
+
+               my_getExpression (&imm_expr, s);
+               check_absolute_expr (ip, &imm_expr);
+               if ((unsigned long) imm_expr.X_add_number > mask)
+                 as_bad (_("DSP immediate not in range 0..%lu (%lu)"),
+                         mask, (unsigned long) imm_expr.X_add_number);
+               INSERT_OPERAND (mips_opts.micromips,
+                               SA4, *ip, imm_expr.X_add_number);
+               imm_expr.X_op = O_absent;
+               s = expr_end;
+             }
+             continue;
+
+           case '5':
+             /* DSP 8-bit unsigned immediate in bit 13 (for standard MIPS
+                code) or 16 (for microMIPS code).  */
+             {
+               unsigned long mask = (mips_opts.micromips
+                                     ? MICROMIPSOP_MASK_IMM8 : OP_MASK_IMM8);
+
+               my_getExpression (&imm_expr, s);
+               check_absolute_expr (ip, &imm_expr);
+               if ((unsigned long) imm_expr.X_add_number > mask)
+                 as_bad (_("DSP immediate not in range 0..%lu (%lu)"),
+                         mask, (unsigned long) imm_expr.X_add_number);
+               INSERT_OPERAND (mips_opts.micromips,
+                               IMM8, *ip, imm_expr.X_add_number);
+               imm_expr.X_op = O_absent;
+               s = expr_end;
+             }
+             continue;
+
+           case '6':
+             /* DSP 5-bit unsigned immediate in bit 16 (for standard MIPS
+                code) or 21 (for microMIPS code).  */
+             {
+               unsigned long mask = (mips_opts.micromips
+                                     ? MICROMIPSOP_MASK_RS : OP_MASK_RS);
+
+               my_getExpression (&imm_expr, s);
+               check_absolute_expr (ip, &imm_expr);
+               if ((unsigned long) imm_expr.X_add_number > mask)
+                 as_bad (_("DSP immediate not in range 0..%lu (%lu)"),
+                         mask, (unsigned long) imm_expr.X_add_number);
+               INSERT_OPERAND (mips_opts.micromips,
+                               RS, *ip, imm_expr.X_add_number);
+               imm_expr.X_op = O_absent;
+               s = expr_end;
+             }
+             continue;
 
-    case M_DSUB_I:
-      dbl = 1;
-    case M_SUB_I:
-      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, dbl ? "daddi" : "addi", "t,r,j",
-                      dreg, sreg, BFD_RELOC_LO16);
-         break;
-       }
-      used_at = 1;
-      load_register (AT, &imm_expr, dbl);
-      macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", dreg, sreg, AT);
-      break;
+           case '7': /* Four DSP accumulators in bits 11,12.  */
+             if (s[0] == '$' && s[1] == 'a' && s[2] == 'c'
+                 && s[3] >= '0' && s[3] <= '3')
+               {
+                 regno = s[3] - '0';
+                 s += 4;
+                 INSERT_OPERAND (mips_opts.micromips, DSPACC, *ip, regno);
+                 continue;
+               }
+             else
+               as_bad (_("Invalid dsp acc register"));
+             break;
 
-    case M_DSUBU_I:
-      dbl = 1;
-    case M_SUBU_I:
-      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, dbl ? "daddiu" : "addiu", "t,r,j",
-                      dreg, sreg, BFD_RELOC_LO16);
-         break;
-       }
-      used_at = 1;
-      load_register (AT, &imm_expr, dbl);
-      macro_build (NULL, dbl ? "dsubu" : "subu", "d,v,t", dreg, sreg, AT);
-      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 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 '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 M_TRUNCWS:
-    case M_TRUNCWD:
-      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 '0':
+             /* DSP 6-bit signed immediate in bit 16 (for standard MIPS
+                code) or 20 (for microMIPS code).  */
+             {
+               long mask = (mips_opts.micromips
+                            ? MICROMIPSOP_MASK_DSPSFT : OP_MASK_DSPSFT);
+
+               my_getExpression (&imm_expr, s);
+               check_absolute_expr (ip, &imm_expr);
+               min_range = -((mask + 1) >> 1);
+               max_range = ((mask + 1) >> 1) - 1;
+               if (imm_expr.X_add_number < min_range
+                   || imm_expr.X_add_number > max_range)
+                 as_bad (_("DSP immediate not in range %ld..%ld (%ld)"),
+                         (long) min_range, (long) max_range,
+                         (long) imm_expr.X_add_number);
+               INSERT_OPERAND (mips_opts.micromips,
+                               DSPSFT, *ip, imm_expr.X_add_number);
+               imm_expr.X_op = O_absent;
+               s = expr_end;
+             }
+             continue;
 
-      /*
-       * 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 '\'': /* 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 M_ULH:
-      s = "lb";
-      goto ulh;
-    case M_ULHU:
-      s = "lbu";
-    ulh:
-      used_at = 1;
-      if (offset_expr.X_add_number >= 0x7fff)
-       as_bad (_("Operand overflow"));
-      if (!target_big_endian)
-       ++offset_expr.X_add_number;
-      macro_build (&offset_expr, s, "t,o(b)", AT, BFD_RELOC_LO16, breg);
-      if (!target_big_endian)
-       --offset_expr.X_add_number;
-      else
-       ++offset_expr.X_add_number;
-      macro_build (&offset_expr, "lbu", "t,o(b)", treg, BFD_RELOC_LO16, breg);
-      macro_build (NULL, "sll", "d,w,<", AT, AT, 8);
-      macro_build (NULL, "or", "d,v,t", treg, treg, AT);
-      break;
+           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 M_ULD:
-      s = "ldl";
-      s2 = "ldr";
-      off = 7;
-      goto ulw;
-    case M_ULW:
-      s = "lwl";
-      s2 = "lwr";
-      off = 3;
-    ulw:
-      if (offset_expr.X_add_number >= 0x8000 - off)
-       as_bad (_("Operand overflow"));
-      if (treg != breg)
-       tempreg = treg;
-      else
-       {
-         used_at = 1;
-         tempreg = AT;
-       }
-      if (!target_big_endian)
-       offset_expr.X_add_number += off;
-      macro_build (&offset_expr, s, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
-      if (!target_big_endian)
-       offset_expr.X_add_number -= off;
-      else
-       offset_expr.X_add_number += off;
-      macro_build (&offset_expr, s2, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
+           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;
 
-      /* If necessary, move the result in tempreg to the final destination.  */
-      if (treg == tempreg)
-        break;
-      /* Protect second load's delay slot.  */
-      load_delay_nop ();
-      move_register (treg, tempreg);
-      break;
+           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 M_ULD_A:
-      s = "ldl";
-      s2 = "ldr";
-      off = 7;
-      goto ulwa;
-    case M_ULW_A:
-      s = "lwl";
-      s2 = "lwr";
-      off = 3;
-    ulwa:
-      used_at = 1;
-      load_address (AT, &offset_expr, &used_at);
-      if (breg != 0)
-       macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
-      if (!target_big_endian)
-       expr1.X_add_number = off;
-      else
-       expr1.X_add_number = 0;
-      macro_build (&expr1, s, "t,o(b)", treg, BFD_RELOC_LO16, AT);
-      if (!target_big_endian)
-       expr1.X_add_number = 0;
-      else
-       expr1.X_add_number = off;
-      macro_build (&expr1, s2, "t,o(b)", treg, BFD_RELOC_LO16, AT);
-      break;
+            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;
 
-    case M_ULH_A:
-    case M_ULHU_A:
-      used_at = 1;
-      load_address (AT, &offset_expr, &used_at);
-      if (breg != 0)
-       macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
-      if (target_big_endian)
-       expr1.X_add_number = 0;
-      macro_build (&expr1, mask == M_ULH_A ? "lb" : "lbu", "t,o(b)",
-                  treg, BFD_RELOC_LO16, AT);
-      if (target_big_endian)
-       expr1.X_add_number = 1;
-      else
-       expr1.X_add_number = 0;
-      macro_build (&expr1, "lbu", "t,o(b)", AT, BFD_RELOC_LO16, AT);
-      macro_build (NULL, "sll", "d,w,<", treg, treg, 8);
-      macro_build (NULL, "or", "d,v,t", treg, treg, AT);
-      break;
+            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;
 
-    case M_USH:
-      used_at = 1;
-      if (offset_expr.X_add_number >= 0x7fff)
-       as_bad (_("Operand overflow"));
-      if (target_big_endian)
-       ++offset_expr.X_add_number;
-      macro_build (&offset_expr, "sb", "t,o(b)", treg, BFD_RELOC_LO16, breg);
-      macro_build (NULL, "srl", "d,w,<", AT, treg, 8);
-      if (target_big_endian)
-       --offset_expr.X_add_number;
-      else
-       ++offset_expr.X_add_number;
-      macro_build (&offset_expr, "sb", "t,o(b)", AT, BFD_RELOC_LO16, breg);
-      break;
+           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;
 
-    case M_USD:
-      s = "sdl";
-      s2 = "sdr";
-      off = 7;
-      goto usw;
-    case M_USW:
-      s = "swl";
-      s2 = "swr";
-      off = 3;
-    usw:
-      if (offset_expr.X_add_number >= 0x8000 - off)
-       as_bad (_("Operand overflow"));
-      if (!target_big_endian)
-       offset_expr.X_add_number += off;
-      macro_build (&offset_expr, s, "t,o(b)", treg, BFD_RELOC_LO16, breg);
-      if (!target_big_endian)
-       offset_expr.X_add_number -= off;
-      else
-       offset_expr.X_add_number += off;
-      macro_build (&offset_expr, s2, "t,o(b)", treg, BFD_RELOC_LO16, breg);
-      break;
+           case '&': /* Four DSP accumulators in bits 13,14.  */
+             gas_assert (!mips_opts.micromips);
+             if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
+                 s[3] >= '0' && s[3] <= '3')
+               {
+                 regno = s[3] - '0';
+                 s += 4;
+                 INSERT_OPERAND (0, MTACC_D, *ip, regno);
+                 continue;
+               }
+             else
+               as_bad (_("Invalid dsp/smartmips acc register"));
+             break;
 
-    case M_USD_A:
-      s = "sdl";
-      s2 = "sdr";
-      off = 7;
-      goto uswa;
-    case M_USW_A:
-      s = "swl";
-      s2 = "swr";
-      off = 3;
-    uswa:
-      used_at = 1;
-      load_address (AT, &offset_expr, &used_at);
-      if (breg != 0)
-       macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
-      if (!target_big_endian)
-       expr1.X_add_number = off;
-      else
-       expr1.X_add_number = 0;
-      macro_build (&expr1, s, "t,o(b)", treg, BFD_RELOC_LO16, AT);
-      if (!target_big_endian)
-       expr1.X_add_number = 0;
-      else
-       expr1.X_add_number = off;
-      macro_build (&expr1, s2, "t,o(b)", treg, BFD_RELOC_LO16, AT);
-      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 M_USH_A:
-      used_at = 1;
-      load_address (AT, &offset_expr, &used_at);
-      if (breg != 0)
-       macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
-      if (!target_big_endian)
-       expr1.X_add_number = 0;
-      macro_build (&expr1, "sb", "t,o(b)", treg, BFD_RELOC_LO16, AT);
-      macro_build (NULL, "srl", "d,w,<", treg, treg, 8);
-      if (!target_big_endian)
-       expr1.X_add_number = 1;
-      else
-       expr1.X_add_number = 0;
-      macro_build (&expr1, "sb", "t,o(b)", treg, BFD_RELOC_LO16, AT);
-      if (!target_big_endian)
-       expr1.X_add_number = 0;
-      else
-       expr1.X_add_number = 1;
-      macro_build (&expr1, "lbu", "t,o(b)", AT, BFD_RELOC_LO16, AT);
-      macro_build (NULL, "sll", "d,w,<", treg, treg, 8);
-      macro_build (NULL, "or", "d,v,t", treg, treg, AT);
-      break;
+           case ',':
+             ++argnum;
+             if (*s++ == *args)
+               continue;
+             s--;
+             switch (*++args)
+               {
+               case 'r':
+               case 'v':
+                 INSERT_OPERAND (mips_opts.micromips, RS, *ip, lastregno);
+                 continue;
 
-    default:
-      /* FIXME: Check if this is one of the itbl macros, since they
-        are added dynamically.  */
-      as_bad (_("Macro %s not implemented yet"), ip->insn_mo->name);
-      break;
-    }
-  if (!mips_opts.at && used_at)
-    as_bad (_("Macro used $at after \".set noat\""));
-}
+               case 'w':
+                 INSERT_OPERAND (mips_opts.micromips, RT, *ip, lastregno);
+                 continue;
 
-/* Implement macros in mips16 mode.  */
+               case 'W':
+                 gas_assert (!mips_opts.micromips);
+                 INSERT_OPERAND (0, FT, *ip, lastregno);
+                 continue;
 
-static void
-mips16_macro (struct mips_cl_insn *ip)
-{
-  int mask;
-  int xreg, yreg, zreg, tmp;
-  expressionS expr1;
-  int dbl;
-  const char *s, *s2, *s3;
+               case 'V':
+                 INSERT_OPERAND (mips_opts.micromips, FS, *ip, lastregno);
+                 continue;
+               }
+             break;
 
-  mask = ip->insn_mo->mask;
+           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.  */
 
-  xreg = MIPS16_EXTRACT_OPERAND (RX, *ip);
-  yreg = MIPS16_EXTRACT_OPERAND (RY, *ip);
-  zreg = MIPS16_EXTRACT_OPERAND (RZ, *ip);
+           case ')':           /* These must match exactly.  */
+             if (*s++ == *args)
+               continue;
+             break;
 
-  expr1.X_op = O_constant;
-  expr1.X_op_symbol = NULL;
-  expr1.X_add_symbol = NULL;
-  expr1.X_add_number = 1;
+           case '[':           /* These must match exactly.  */
+           case ']':
+             gas_assert (!mips_opts.micromips);
+             if (*s++ == *args)
+               continue;
+             break;
 
-  dbl = 0;
+           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;
 
-  switch (mask)
-    {
-    default:
-      internalError ();
+                   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 M_DDIV_3:
-      dbl = 1;
-    case M_DIV_3:
-      s = "mflo";
-      goto do_div3;
-    case M_DREM_3:
-      dbl = 1;
-    case M_REM_3:
-      s = "mfhi";
-    do_div3:
-      start_noreorder ();
-      macro_build (NULL, dbl ? "ddiv" : "div", "0,x,y", xreg, yreg);
-      expr1.X_add_number = 2;
-      macro_build (&expr1, "bnez", "x,p", yreg);
-      macro_build (NULL, "break", "6", 7);
+               case 'J':               /* 10-bit hypcall code.  */
+                 gas_assert (!mips_opts.micromips);
+                 {
+                   unsigned long mask = OP_MASK_CODE10;
 
-      /* 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;
+                   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 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 '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 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 '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 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 'D':
+                 /* +D is for disassembly only; never match.  */
+                 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;
+               case 'I':
+                 /* "+I" is like "I", except that imm2_expr is used.  */
+                 my_getExpression (&imm2_expr, s);
+                 if (imm2_expr.X_op != O_big
+                     && imm2_expr.X_op != O_constant)
+                 insn_error = _("absolute expression required");
+                 if (HAVE_32BIT_GPRS)
+                   normalize_constant_expr (&imm2_expr);
+                 s = expr_end;
+                 continue;
 
-    case M_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 'T': /* Coprocessor register.  */
+                 gas_assert (!mips_opts.micromips);
+                 /* +T is for disassembly only; never match.  */
+                 break;
 
-    case M_BEQ:
-      s = "cmp";
-      s2 = "bteqz";
-      goto do_branch;
-    case M_BNE:
-      s = "cmp";
-      s2 = "btnez";
-      goto do_branch;
-    case M_BLT:
-      s = "slt";
-      s2 = "btnez";
-      goto do_branch;
-    case M_BLTU:
-      s = "sltu";
-      s2 = "btnez";
-      goto do_branch;
-    case M_BLE:
-      s = "slt";
-      s2 = "bteqz";
-      goto do_reverse_branch;
-    case M_BLEU:
-      s = "sltu";
-      s2 = "bteqz";
-      goto do_reverse_branch;
-    case M_BGE:
-      s = "slt";
-      s2 = "bteqz";
-      goto do_branch;
-    case M_BGEU:
-      s = "sltu";
-      s2 = "bteqz";
-      goto do_branch;
-    case M_BGT:
-      s = "slt";
-      s2 = "btnez";
-      goto do_reverse_branch;
-    case M_BGTU:
-      s = "sltu";
-      s2 = "btnez";
+               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;
 
-    do_reverse_branch:
-      tmp = xreg;
-      xreg = yreg;
-      yreg = tmp;
+               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;
 
-    do_branch:
-      macro_build (NULL, s, "x,y", xreg, yreg);
-      macro_build (&offset_expr, s2, "p");
-      break;
+               case 'X':
+                 /* bbit[01] bit index when bbit is used but we generate
+                    bbit[01]32 because the index is over 32.  Move to the
+                    next candidate if index is not in the valid range.  */
+                 gas_assert (!mips_opts.micromips);
+                 my_getExpression (&imm_expr, s);
+                 check_absolute_expr (ip, &imm_expr);
+                 if ((unsigned) imm_expr.X_add_number < 32
+                     || (unsigned) imm_expr.X_add_number > 63)
+                   break;
+                 INSERT_OPERAND (0, BBITIND, *ip, imm_expr.X_add_number - 32);
+                 imm_expr.X_op = O_absent;
+                 s = expr_end;
+                 continue;
 
-    case M_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 '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;
 
-    do_addone_branch_i:
-      if (imm_expr.X_op != O_constant)
-       as_bad (_("Unsupported large constant"));
-      ++imm_expr.X_add_number;
+               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;
 
-    do_branch_i:
-      macro_build (&imm_expr, s, s3, xreg);
-      macro_build (&offset_expr, s2, "p");
-      break;
+               case 's':
+                 /* cins and exts length-minus-one field.  */
+                 gas_assert (!mips_opts.micromips);
+                 my_getExpression (&imm_expr, s);
+                 check_absolute_expr (ip, &imm_expr);
+                 if ((unsigned long) imm_expr.X_add_number > 31)
+                   {
+                     as_bad (_("Improper size (%lu)"),
+                             (unsigned long) imm_expr.X_add_number);
+                     imm_expr.X_add_number = 0;
+                   }
+                 INSERT_OPERAND (0, CINSLM1, *ip, imm_expr.X_add_number);
+                 imm_expr.X_op = O_absent;
+                 s = expr_end;
+                 continue;
 
-    case M_ABS:
-      expr1.X_add_number = 0;
-      macro_build (&expr1, "slti", "x,8", yreg);
-      if (xreg != yreg)
-       move_register (xreg, yreg);
-      expr1.X_add_number = 2;
-      macro_build (&expr1, "bteqz", "p");
-      macro_build (NULL, "neg", "x,w", xreg, xreg);
-    }
-}
+               case '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;
 
-/* For consistency checking, verify that all bits are specified either
-   by the match/mask part of the instruction definition, or by the
-   operand list.  */
-static int
-validate_mips_insn (const struct mips_opcode *opc)
-{
-  const char *p = opc->args;
-  char c;
-  unsigned long used_bits = opc->mask;
+               case '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;
 
-  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 '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 '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;
 
-         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_SHAMT,        OP_SH_SHAMT);   break;
-      case '2': USE_BITS (OP_MASK_BP,          OP_SH_BP);      break;
-      case '3': USE_BITS (OP_MASK_SA3,         OP_SH_SA3);     break;
-      case '4': USE_BITS (OP_MASK_SA4,         OP_SH_SA4);     break;
-      case '5': USE_BITS (OP_MASK_IMM8,        OP_SH_IMM8);    break;
-      case '6': USE_BITS (OP_MASK_RS,          OP_SH_RS);      break;
-      case '7': USE_BITS (OP_MASK_DSPACC,      OP_SH_DSPACC);  break;
-      case '8': USE_BITS (OP_MASK_WRDSP,       OP_SH_WRDSP);   break;
-      case '9': USE_BITS (OP_MASK_DSPACC_S,    OP_SH_DSPACC_S);break;
-      case '0': USE_BITS (OP_MASK_DSPSFT,      OP_SH_DSPSFT);  break;
-      case '\'': USE_BITS (OP_MASK_RDDSP,      OP_SH_RDDSP);   break;
-      case ':': USE_BITS (OP_MASK_DSPSFT_7,    OP_SH_DSPSFT_7);break;
-      case '@': USE_BITS (OP_MASK_IMM10,       OP_SH_IMM10);   break;
-      case '!': USE_BITS (OP_MASK_MT_U,                OP_SH_MT_U);    break;
-      case '$': USE_BITS (OP_MASK_MT_H,                OP_SH_MT_H);    break;
-      case '*': USE_BITS (OP_MASK_MTACC_T,     OP_SH_MTACC_T); break;
-      case '&': USE_BITS (OP_MASK_MTACC_D,     OP_SH_MTACC_D); break;
-      case '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;
-}
+               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;
 
-/* UDI immediates.  */
-struct mips_immed {
-  char         type;
-  unsigned int shift;
-  unsigned long        mask;
-  const char * desc;
-};
+               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;
 
-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 '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;
 
-/* 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 'Z':
+                 gas_assert (!mips_opts.micromips);
+                 if (!reg_lookup (&s, RTYPE_FPU, &regno))
+                   break;
+                 INSERT_OPERAND (0, FZ, *ip, regno);
+                 continue;
 
-  if (insn->pinfo == INSN_MACRO)
-    /* Let a macro pass, we'll catch it later when it is expanded.  */
-    return 1;
+               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 (ISA_HAS_ODD_SINGLE_FPR (mips_opts.isa))
-    {
-      /* Allow odd registers for single-precision ops.  */
-      switch (insn->pinfo & (FP_S | FP_D))
-       {
-       case FP_S:
-       case 0:
-         return 1;     /* both single precision - ok */
-       case FP_D:
-         return 0;     /* both double precision - fail */
-       default:
-         break;
-       }
+                   /* 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;
 
-      /* 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'));
-    } 
+               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 '.':           /* 10-bit offset.  */
+             gas_assert (mips_opts.micromips);
+           case '~':           /* 12-bit offset.  */
+             {
+               int shift = *args == '.' ? 9 : 11;
+               size_t i;
 
-  /* 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;
+               /* 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;
 
-  return 0;
-}
+               /* 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;
 
-/* This routine assembles an instruction into its binary format.  As a
-   side effect, it sets one of the global variables imm_reloc or
-   offset_reloc to the type of relocation to do if one of the operands
-   is an address expression.  */
+           case '<':           /* 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;
 
-static void
-mips_ip (char *str, struct mips_cl_insn *ip)
-{
-  char *s;
-  const char *args;
-  char c = 0;
-  struct mips_opcode *insn;
-  char *argsStart;
-  unsigned int regno;
-  unsigned int lastregno = 0;
-  unsigned int lastpos = 0;
-  unsigned int limlo, limhi;
-  char *s_reset;
-  char save_c = 0;
-  offsetT min_range, max_range;
-  int argnum;
-  unsigned int rtype;
+           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;
 
-  insn_error = NULL;
+           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;
 
-  /* If the instruction contains a '.', we first try to match an instruction
-     including the '.'.  Then we try again without the '.'.  */
-  insn = NULL;
-  for (s = str; *s != '\0' && !ISSPACE (*s); ++s)
-    continue;
+                     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;
 
-  /* If we stopped on whitespace, then replace the whitespace with null for
-     the call to hash_find.  Save the character we replaced just in case we
-     have to re-parse the instruction.  */
-  if (ISSPACE (*s))
-    {
-      save_c = *s;
-      *s++ = '\0';
-    }
+           case 'c':           /* BREAK code.  */
+             {
+               unsigned long mask = (mips_opts.micromips
+                                     ? MICROMIPSOP_MASK_CODE
+                                     : OP_MASK_CODE);
+
+               my_getExpression (&imm_expr, s);
+               check_absolute_expr (ip, &imm_expr);
+               if ((unsigned long) imm_expr.X_add_number > mask)
+                 as_warn (_("Code for %s not in range 0..%lu (%lu)"),
+                          ip->insn_mo->name,
+                          mask, (unsigned long) imm_expr.X_add_number);
+               INSERT_OPERAND (mips_opts.micromips,
+                               CODE, *ip, imm_expr.X_add_number);
+               imm_expr.X_op = O_absent;
+               s = expr_end;
+             }
+             continue;
 
-  insn = (struct mips_opcode *) hash_find (op_hash, str);
+           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;
 
-  /* If we didn't find the instruction in the opcode table, try again, but
-     this time with just the instruction up to, but not including the
-     first '.'.  */
-  if (insn == NULL)
-    {
-      /* Restore the character we overwrite above (if any).  */
-      if (save_c)
-       *(--s) = save_c;
+           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;
 
-      /* Scan up to the first '.' or whitespace.  */
-      for (s = str;
-          *s != '\0' && *s != '.' && !ISSPACE (*s);
-          ++s)
-       continue;
+           case 'C':           /* 25- or 23-bit coprocessor code.  */
+             {
+               unsigned long mask = (mips_opts.micromips
+                                     ? MICROMIPSOP_MASK_COPZ
+                                     : OP_MASK_COPZ);
+
+               my_getExpression (&imm_expr, s);
+               check_absolute_expr (ip, &imm_expr);
+               if ((unsigned long) imm_expr.X_add_number > mask)
+                 as_warn (_("Coproccesor code > %u bits (%lu)"),
+                          mips_opts.micromips ? 23U : 25U,
+                          (unsigned long) imm_expr.X_add_number);
+               INSERT_OPERAND (mips_opts.micromips,
+                               COPZ, *ip, imm_expr.X_add_number);
+               imm_expr.X_op = O_absent;
+               s = expr_end;
+             }
+             continue;
 
-      /* If we did not find a '.', then we can quit now.  */
-      if (*s != '.')
-       {
-         insn_error = _("Unrecognized opcode");
-         return;
-       }
+           case 'J':           /* 19-bit WAIT code.  */
+             gas_assert (!mips_opts.micromips);
+             my_getExpression (&imm_expr, s);
+             check_absolute_expr (ip, &imm_expr);
+             if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE19)
+               {
+                 as_warn (_("Illegal 19-bit code (%lu)"),
+                          (unsigned long) imm_expr.X_add_number);
+                 imm_expr.X_add_number &= OP_MASK_CODE19;
+               }
+             INSERT_OPERAND (0, CODE19, *ip, imm_expr.X_add_number);
+             imm_expr.X_op = O_absent;
+             s = expr_end;
+             continue;
 
-      /* Lookup the instruction in the hash table.  */
-      *s++ = '\0';
-      if ((insn = (struct mips_opcode *) hash_find (op_hash, str)) == NULL)
-       {
-         insn_error = _("Unrecognized opcode");
-         return;
-       }
-    }
+           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;
 
-  argsStart = s;
-  for (;;)
-    {
-      bfd_boolean ok;
+           case 'G':           /* Coprocessor destination register.  */
+             {
+               unsigned long opcode = ip->insn_opcode;
+               unsigned long mask;
+               unsigned int types;
+               int cop0;
 
-      gas_assert (strcmp (insn->name, str) == 0);
+               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;
 
-      ok = is_opcode_valid (insn);
-      if (! ok)
-       {
-         if (insn + 1 < &mips_opcodes[NUMOPCODES]
-             && strcmp (insn->name, insn[1].name) == 0)
-           {
-             ++insn;
-             continue;
-           }
-         else
-           {
-             if (!insn_error)
+           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
                {
-                 static char buf[100];
-                 sprintf (buf,
-                          _("opcode not supported on this processor: %s (%s)"),
-                          mips_cpu_info_from_arch (mips_opts.arch)->name,
-                          mips_cpu_info_from_isa (mips_opts.isa)->name);
-                 insn_error = buf;
+                 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 (save_c)
-               *(--s) = save_c;
-             return;
-           }
-       }
+             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;
 
-      create_insn (ip, insn);
-      insn_error = NULL;
-      argnum = 1;
-      lastregno = 0xffffffff;
-      for (args = insn->args;; ++args)
-       {
-         int is_mdmx;
+                 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;
 
-         s += strspn (s, " \t");
-         is_mdmx = 0;
-         switch (*args)
-           {
-           case '\0':          /* end of args */
-             if (*s == '\0')
-               return;
-             break;
+                   case 'K':
+                     if (mips_opts.micromips)
+                       INSERT_OPERAND (1, RS, *ip, regno);
+                     else
+                       INSERT_OPERAND (0, RD, *ip, regno);
+                     break;
 
-           case '2': /* DSP 2-bit unsigned immediate in bit 11.  */
-             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 (BP, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+                   case 'd':
+                   case 'g':
+                     INSERT_OPERAND (mips_opts.micromips, RD, *ip, regno);
+                     break;
 
-           case '3': /* DSP 3-bit unsigned immediate in bit 21.  */
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if (imm_expr.X_add_number & ~OP_MASK_SA3)
-               {
-                 as_bad (_("DSP immediate not in range 0..%d (%lu)"),
-                         OP_MASK_SA3, (unsigned long) imm_expr.X_add_number);
-               }
-             INSERT_OPERAND (SA3, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+                   case 'U':
+                     gas_assert (!mips_opts.micromips);
+                     INSERT_OPERAND (0, RD, *ip, regno);
+                     INSERT_OPERAND (0, RT, *ip, regno);
+                     break;
 
-           case '4': /* DSP 4-bit unsigned immediate in bit 21.  */
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if (imm_expr.X_add_number & ~OP_MASK_SA4)
-               {
-                 as_bad (_("DSP immediate not in range 0..%d (%lu)"),
-                         OP_MASK_SA4, (unsigned long) imm_expr.X_add_number);
-               }
-             INSERT_OPERAND (SA4, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+                   case 'w':
+                   case 't':
+                   case 'E':
+                     INSERT_OPERAND (mips_opts.micromips, RT, *ip, regno);
+                     break;
 
-           case '5': /* DSP 8-bit unsigned immediate in bit 16.  */
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if (imm_expr.X_add_number & ~OP_MASK_IMM8)
-               {
-                 as_bad (_("DSP immediate not in range 0..%d (%lu)"),
-                         OP_MASK_IMM8, (unsigned long) imm_expr.X_add_number);
-               }
-             INSERT_OPERAND (IMM8, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+                   case 'y':
+                     gas_assert (mips_opts.micromips);
+                     INSERT_OPERAND (1, RS3, *ip, regno);
+                     break;
 
-           case '6': /* DSP 5-bit unsigned immediate in bit 21.  */
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if (imm_expr.X_add_number & ~OP_MASK_RS)
-               {
-                 as_bad (_("DSP immediate not in range 0..%d (%lu)"),
-                         OP_MASK_RS, (unsigned long) imm_expr.X_add_number);
-               }
-             INSERT_OPERAND (RS, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+                   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 '7': /* Four DSP accumulators in bits 11,12.  */
-             if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
-                 s[3] >= '0' && s[3] <= '3')
-               {
-                 regno = s[3] - '0';
-                 s += 4;
-                 INSERT_OPERAND (DSPACC, *ip, regno);
+                   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;
                }
-             else
-               as_bad (_("Invalid dsp acc register"));
-             break;
-
-           case '8': /* DSP 6-bit unsigned immediate in bit 11.  */
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if (imm_expr.X_add_number & ~OP_MASK_WRDSP)
+             switch (*args++)
                {
-                 as_bad (_("DSP immediate not in range 0..%d (%lu)"),
-                         OP_MASK_WRDSP,
-                         (unsigned long) imm_expr.X_add_number);
-               }
-             INSERT_OPERAND (WRDSP, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+               case 'r':
+               case 'v':
+                 INSERT_OPERAND (mips_opts.micromips, RS, *ip, lastregno);
+                 continue;
 
-           case '9': /* Four DSP accumulators in bits 21,22.  */
-             if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
-                 s[3] >= '0' && s[3] <= '3')
-               {
-                 regno = s[3] - '0';
-                 s += 4;
-                 INSERT_OPERAND (DSPACC_S, *ip, regno);
+               case 'w':
+                 INSERT_OPERAND (mips_opts.micromips, RT, *ip, lastregno);
                  continue;
                }
-             else
-               as_bad (_("Invalid dsp acc register"));
              break;
 
-           case '0': /* DSP 6-bit signed immediate in bit 20.  */
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             min_range = -((OP_MASK_DSPSFT + 1) >> 1);
-             max_range = ((OP_MASK_DSPSFT + 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 (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.  */
+           case 'O':           /* MDMX alignment immediate constant.  */
+             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 (RDDSP, *ip, imm_expr.X_add_number);
+             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 ':': /* DSP 7-bit signed immediate in bit 19.  */
-             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)
+           case 'Q':           /* MDMX vector, element sel, or const.  */
+             if (s[0] != '$')
                {
-                 as_bad (_("DSP immediate not in range %ld..%ld (%ld)"),
-                         (long) min_range, (long) max_range,
-                         (long) imm_expr.X_add_number);
+                 /* 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;
                }
-             INSERT_OPERAND (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.  */
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             min_range = -((OP_MASK_IMM10 + 1) >> 1);
-             max_range = ((OP_MASK_IMM10 + 1) >> 1) - 1;
-             if (imm_expr.X_add_number < min_range ||
-                 imm_expr.X_add_number > max_range)
+             /* 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_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))
                {
-                 as_bad (_("DSP immediate not in range %ld..%ld (%ld)"),
-                         (long) min_range, (long) max_range,
-                         (long) imm_expr.X_add_number);
-               }
-             INSERT_OPERAND (IMM10, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
-
-            case '!': /* MT usermode flag bit.  */
-             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 (MT_U, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+                 if ((regno & 1) != 0
+                     && HAVE_32BIT_FPRS
+                     && !mips_oddfpreg_ok (ip->insn_mo, argnum))
+                   as_warn (_("Float register should be even, was %d"),
+                            regno);
 
-            case '$': /* MT load high flag bit.  */
-             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 (MT_H, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+                 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 '*': /* Four DSP accumulators in bits 18,19.  */
-             if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
-                 s[3] >= '0' && s[3] <= '3')
-               {
-                 regno = s[3] - '0';
-                 s += 4;
-                 INSERT_OPERAND (MTACC_T, *ip, regno);
-                 continue;
-               }
-             else
-               as_bad (_("Invalid dsp/smartmips acc register"));
-             break;
+                   case 'V':
+                   case 'S':
+                   case 'Y':
+                     INSERT_OPERAND (mips_opts.micromips, FS, *ip, regno);
+                     break;
 
-           case '&': /* Four DSP accumulators in bits 13,14.  */
-             if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
-                 s[3] >= '0' && s[3] <= '3')
-               {
-                 regno = s[3] - '0';
-                 s += 4;
-                 INSERT_OPERAND (MTACC_D, *ip, regno);
+                   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;
                }
-             else
-               as_bad (_("Invalid dsp/smartmips acc register"));
-             break;
 
-           case ',':
-             ++argnum;
-             if (*s++ == *args)
-               continue;
-             s--;
-             switch (*++args)
+             switch (*args++)
                {
-               case 'r':
-               case 'v':
-                 INSERT_OPERAND (RS, *ip, lastregno);
-                 continue;
-
-               case 'w':
-                 INSERT_OPERAND (RT, *ip, lastregno);
+               case 'V':
+                 INSERT_OPERAND (mips_opts.micromips, FS, *ip, lastregno);
                  continue;
 
                case 'W':
-                 INSERT_OPERAND (FT, *ip, lastregno);
-                 continue;
-
-               case 'V':
-                 INSERT_OPERAND (FS, *ip, lastregno);
+                 INSERT_OPERAND (mips_opts.micromips, FT, *ip, lastregno);
                  continue;
                }
              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');
-             if (*s == '\0')
-               return;
+           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 ')':           /* These must match exactly.  */
-           case '[':
-           case ']':
-             if (*s++ == *args)
-               continue;
-             break;
+           case 'A':
+             my_getExpression (&offset_expr, s);
+             normalize_address_expr (&offset_expr);
+             *imm_reloc = BFD_RELOC_32;
+             s = expr_end;
+             continue;
 
-           case '+':           /* Opcode extension character.  */
-             switch (*++args)
-               {
-               case '1':       /* UDI immediates.  */
-               case '2':
-               case '3':
-               case '4':
-                 {
-                   const struct mips_immed *imm = mips_immed;
+           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;
 
-                   while (imm->type && imm->type != *args)
-                     ++imm;
-                   if (! imm->type)
-                     internalError ();
-                   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;
+               /* 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.
 
-               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 (SHAMT, *ip, imm_expr.X_add_number);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+                  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
 
-               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 (INSMSB, *ip,
-                                lastpos + imm_expr.X_add_number - 1);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+                   The .lit4 and .lit8 sections are only used if
+                   permitted by the -G argument.
 
-               case 'C':               /* ext size, becomes MSBD.  */
-                 limlo = 1;
-                 limhi = 32;
-                 goto do_msbd;
-               case 'G':
-                 limlo = 33;
-                 limhi = 64;
-                 goto do_msbd;
-               case 'H':
-                 limlo = 33;
-                 limhi = 64;
-                 goto do_msbd;
-               do_msbd:
-                 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 extract size (%lu, position %lu)"),
-                             (unsigned long) imm_expr.X_add_number,
-                             (unsigned long) lastpos);
-                     imm_expr.X_add_number = limlo - lastpos;
-                   }
-                 INSERT_OPERAND (EXTMSBD, *ip, imm_expr.X_add_number - 1);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+                   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 'D':
-                 /* +D is for disassembly only; never match.  */
-                 break;
+               gas_assert (length == (unsigned) (f64 ? 8 : 4));
 
-               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;
+               if (*args == 'f'
+                   || (*args == 'l'
+                       && (g_switch_value < 4
+                           || (temp[0] == 0 && temp[1] == 0)
+                           || (temp[2] == 0 && temp[3] == 0))))
+                 {
+                   imm_expr.X_op = O_constant;
+                   if (!target_big_endian)
+                     imm_expr.X_add_number = bfd_getl32 (temp);
+                   else
+                     imm_expr.X_add_number = bfd_getb32 (temp);
+                 }
+               else if (length > 4
+                        && !mips_disable_float_construction
+                        /* Constants can only be constructed in GPRs and
+                           copied to FPRs if the GPRs are at least as wide
+                           as the FPRs.  Force the constant into memory if
+                           we are using 64-bit FPRs but the GPRs are only
+                           32 bits wide.  */
+                        && (using_gprs
+                            || !(HAVE_64BIT_FPRS && HAVE_32BIT_GPRS))
+                        && ((temp[0] == 0 && temp[1] == 0)
+                            || (temp[2] == 0 && temp[3] == 0))
+                        && ((temp[4] == 0 && temp[5] == 0)
+                            || (temp[6] == 0 && temp[7] == 0)))
+                 {
+                   /* The value is simple enough to load with a couple of
+                      instructions.  If using 32-bit registers, set
+                      imm_expr to the high order 32 bits and offset_expr to
+                      the low order 32 bits.  Otherwise, set imm_expr to
+                      the entire 64 bit constant.  */
+                   if (using_gprs ? HAVE_32BIT_GPRS : HAVE_32BIT_FPRS)
+                     {
+                       imm_expr.X_op = O_constant;
+                       offset_expr.X_op = O_constant;
+                       if (!target_big_endian)
+                         {
+                           imm_expr.X_add_number = bfd_getl32 (temp + 4);
+                           offset_expr.X_add_number = bfd_getl32 (temp);
+                         }
+                       else
+                         {
+                           imm_expr.X_add_number = bfd_getb32 (temp);
+                           offset_expr.X_add_number = bfd_getb32 (temp + 4);
+                         }
+                       if (offset_expr.X_add_number == 0)
+                         offset_expr.X_op = O_absent;
+                     }
+                   else if (sizeof (imm_expr.X_add_number) > 4)
+                     {
+                       imm_expr.X_op = O_constant;
+                       if (!target_big_endian)
+                         imm_expr.X_add_number = bfd_getl64 (temp);
+                       else
+                         imm_expr.X_add_number = bfd_getb64 (temp);
+                     }
+                   else
+                     {
+                       imm_expr.X_op = O_big;
+                       imm_expr.X_add_number = 4;
+                       if (!target_big_endian)
+                         {
+                           generic_bignum[0] = bfd_getl16 (temp);
+                           generic_bignum[1] = bfd_getl16 (temp + 2);
+                           generic_bignum[2] = bfd_getl16 (temp + 4);
+                           generic_bignum[3] = bfd_getl16 (temp + 6);
+                         }
+                       else
+                         {
+                           generic_bignum[0] = bfd_getb16 (temp + 6);
+                           generic_bignum[1] = bfd_getb16 (temp + 4);
+                           generic_bignum[2] = bfd_getb16 (temp + 2);
+                           generic_bignum[3] = bfd_getb16 (temp);
+                         }
+                     }
+                 }
+               else
+                 {
+                   const char *newname;
+                   segT new_seg;
 
-               case 'T': /* Coprocessor register.  */
-                 /* +T is for disassembly only; never match.  */
-                 break;
+                   /* Switch to the right section.  */
+                   seg = now_seg;
+                   subseg = now_subseg;
+                   switch (*args)
+                     {
+                     default: /* unused default case avoids warnings.  */
+                     case 'L':
+                       newname = RDATA_SECTION_NAME;
+                       if (g_switch_value >= 8)
+                         newname = ".lit8";
+                       break;
+                     case 'F':
+                       newname = RDATA_SECTION_NAME;
+                       break;
+                     case 'l':
+                       gas_assert (g_switch_value >= 4);
+                       newname = ".lit4";
+                       break;
+                     }
+                   new_seg = subseg_new (newname, (subsegT) 0);
+                   if (IS_ELF)
+                     bfd_set_section_flags (stdoutput, new_seg,
+                                            (SEC_ALLOC
+                                             | SEC_LOAD
+                                             | SEC_READONLY
+                                             | SEC_DATA));
+                   frag_align (*args == 'l' ? 2 : 3, 0, 0);
+                   if (IS_ELF && strncmp (TARGET_OS, "elf", 3) != 0)
+                     record_alignment (new_seg, 4);
+                   else
+                     record_alignment (new_seg, *args == 'l' ? 2 : 3);
+                   if (seg == now_seg)
+                     as_bad (_("Can't use floating point insn in this section"));
 
-               case 't': /* Coprocessor register number.  */
-                 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 (RT, *ip, regno);
-                         continue;
-                       }
-                   }
-                 else
-                   as_bad (_("Invalid coprocessor 0 register number"));
-                 break;
+                   /* 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;
 
-               case 'x':
-                 /* bbit[01] and bbit[01]32 bit index.  Give error if index
-                    is not in the valid range.  */
-                 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 (BBITIND, *ip, imm_expr.X_add_number);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+                   /* Put the floating point number into the section.  */
+                   p = frag_more ((int) length);
+                   memcpy (p, temp, length);
 
-               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.  */
-                 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 (BBITIND, *ip, imm_expr.X_add_number - 32);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+                   /* Switch back to the original section.  */
+                   subseg_set (seg, subseg);
+                 }
+             }
+             continue;
 
-               case 'p':
-                 /* cins, cins32, exts and exts32 position field.  Give error
-                    if it's not in the valid range.  */
-                 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 (CINSPOS, *ip, imm_expr.X_add_number);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+           case 'i':           /* 16-bit unsigned immediate.  */
+           case 'j':           /* 16-bit signed immediate.  */
+             *imm_reloc = BFD_RELOC_LO16;
+             if (my_getSmallExpression (&imm_expr, imm_reloc, s) == 0)
+               {
+                 int more;
+                 offsetT minval, maxval;
 
-               case 'P':
-                 /* cins, cins32, exts and exts32 position field.  Move to
-                    the next candidate if it's not in the valid range.  */
-                 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 (CINSPOS, *ip, imm_expr.X_add_number - 32);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+                 more = (insn + 1 < past
+                         && strcmp (insn->name, insn[1].name) == 0);
 
-               case 's':
-                 /* cins and exts length-minus-one field.  */
-                 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 (CINSLM1, *ip, imm_expr.X_add_number);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+                 /* If the expression was written as an unsigned number,
+                    only treat it as signed if there are no more
+                    alternatives.  */
+                 if (more
+                     && *args == 'j'
+                     && sizeof (imm_expr.X_add_number) <= 4
+                     && imm_expr.X_op == O_constant
+                     && imm_expr.X_add_number < 0
+                     && imm_expr.X_unsigned
+                     && HAVE_64BIT_GPRS)
+                   break;
 
-               case 'S':
-                 /* cins32/exts32 and cins/exts aliasing cint32/exts32
-                    length-minus-one field.  */
-                 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 (CINSLM1, *ip, imm_expr.X_add_number);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
+                 /* 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 'Q':
-                 /* seqi/snei immediate field.  */
-                 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)
+                 if (imm_expr.X_op != O_constant
+                     || imm_expr.X_add_number < minval
+                     || imm_expr.X_add_number > maxval)
                    {
-                     as_bad (_("Improper immediate (%ld)"),
-                              (long) imm_expr.X_add_number);
-                     imm_expr.X_add_number = 0;
+                     if (more)
+                       break;
+                     if (imm_expr.X_op == O_constant
+                         || imm_expr.X_op == O_big)
+                       as_bad (_("Expression out of range"));
                    }
-                 INSERT_OPERAND (SEQI, *ip, imm_expr.X_add_number);
-                 imm_expr.X_op = O_absent;
-                 s = expr_end;
-                 continue;
-
-               default:
-                 as_bad (_("Internal error: bad mips opcode "
-                           "(unknown extension operand type `+%c'): %s %s"),
-                         *args, insn->name, insn->args);
-                 /* Further processing is fruitless.  */
-                 return;
                }
-             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 (SHAMT, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
              s = expr_end;
              continue;
 
-           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 (SHAMT, *ip, imm_expr.X_add_number - 32);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+           case 'o':           /* 16-bit offset.  */
+             offset_reloc[0] = BFD_RELOC_LO16;
+             offset_reloc[1] = BFD_RELOC_UNUSED;
+             offset_reloc[2] = BFD_RELOC_UNUSED;
 
-           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);
-             if (*args == 'k')
+             /* Check whether there is only a single bracketed expression
+                left.  If so, it must be the base register and the
+                constant must be zero.  */
+             if (*s == '(' && strchr (s + 1, '(') == 0)
                {
-                 if (mips_fix_cn63xxp1 && 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 (CACHE, *ip, imm_expr.X_add_number);
+                 offset_expr.X_op = O_constant;
+                 offset_expr.X_add_number = 0;
+                 continue;
                }
-             else if (*args == 'h')
-               INSERT_OPERAND (PREFX, *ip, imm_expr.X_add_number);
-             else
-               INSERT_OPERAND (SHAMT, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
+
+             /* If this value won't fit into a 16 bit offset, then go
+                find a macro that will generate the 32 bit offset
+                code pattern.  */
+             if (my_getSmallExpression (&offset_expr, offset_reloc, s) == 0
+                 && (offset_expr.X_op != O_constant
+                     || offset_expr.X_add_number >= 0x8000
+                     || offset_expr.X_add_number < -0x8000))
+               break;
+
              s = expr_end;
              continue;
 
-           case 'c':           /* BREAK code.  */
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE)
-               as_warn (_("Code for %s not in range 0..1023 (%lu)"),
-                        ip->insn_mo->name,
-                        (unsigned long) imm_expr.X_add_number);
-             INSERT_OPERAND (CODE, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
+           case 'p':           /* PC-relative offset.  */
+             *offset_reloc = BFD_RELOC_16_PCREL_S2;
+             my_getExpression (&offset_expr, s);
              s = expr_end;
              continue;
 
-           case 'q':           /* Lower BREAK code.  */
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE2)
-               as_warn (_("Lower code for %s not in range 0..1023 (%lu)"),
-                        ip->insn_mo->name,
-                        (unsigned long) imm_expr.X_add_number);
-             INSERT_OPERAND (CODE2, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
+           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 'B':           /* 20-bit SYSCALL/BREAK code.  */
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE20)
-               as_warn (_("Code for %s not in range 0..1048575 (%lu)"),
-                        ip->insn_mo->name,
-                        (unsigned long) imm_expr.X_add_number);
-             INSERT_OPERAND (CODE20, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
+           case 'a':           /* 26-bit address.  */
+             *offset_reloc = BFD_RELOC_MIPS_JMP;
+             my_getExpression (&offset_expr, s);
              s = expr_end;
              continue;
 
-           case 'C':           /* Coprocessor code.  */
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-             if ((unsigned long) imm_expr.X_add_number > OP_MASK_COPZ)
+           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 'H':
+             if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
+               s += 2;
+             if (ISDIGIT (*s))
                {
-                 as_warn (_("Coproccesor code > 25 bits (%lu)"),
-                          (unsigned long) imm_expr.X_add_number);
-                 imm_expr.X_add_number &= OP_MASK_COPZ;
+                 c = 0;
+                 do
+                   {
+                     c *= 10;
+                     c += *s - '0';
+                     ++s;
+                   }
+                 while (ISDIGIT (*s));
                }
-             INSERT_OPERAND (COPZ, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
+             else
+               c = 8; /* Invalid sel value.  */
+
+             if (c > 7)
+               as_bad (_("Invalid coprocessor sub-selection value (0-7)"));
+             INSERT_OPERAND (mips_opts.micromips, SEL, *ip, c);
              continue;
 
-           case 'J':           /* 19-bit WAIT code.  */
+           case 'e':
+             gas_assert (!mips_opts.micromips);
+             /* Must be at least one digit.  */
              my_getExpression (&imm_expr, s);
              check_absolute_expr (ip, &imm_expr);
-             if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE19)
+
+             if ((unsigned long) imm_expr.X_add_number
+                 > (unsigned long) OP_MASK_VECBYTE)
                {
-                 as_warn (_("Illegal 19-bit code (%lu)"),
-                          (unsigned long) imm_expr.X_add_number);
-                 imm_expr.X_add_number &= OP_MASK_CODE19;
+                 as_bad (_("bad byte vector index (%ld)"),
+                          (long) imm_expr.X_add_number);
+                 imm_expr.X_add_number = 0;
                }
-             INSERT_OPERAND (CODE19, *ip, imm_expr.X_add_number);
+
+             INSERT_OPERAND (0, VECBYTE, *ip, imm_expr.X_add_number);
              imm_expr.X_op = O_absent;
              s = expr_end;
              continue;
 
-           case 'P':           /* Performance register.  */
+           case '%':
+             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);
-             INSERT_OPERAND (PERFREG, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
 
-           case 'G':           /* Coprocessor destination register.  */
-             if (((ip->insn_opcode >> OP_SH_OP) & OP_MASK_OP) == OP_OP_COP0)
-               ok = reg_lookup (&s, RTYPE_NUM | RTYPE_CP0, &regno);
-             else
-               ok = reg_lookup (&s, RTYPE_NUM | RTYPE_GP, &regno);
-             INSERT_OPERAND (RD, *ip, regno);
-             if (ok) 
+             if ((unsigned long) imm_expr.X_add_number
+                 > (unsigned long) OP_MASK_VECALIGN)
                {
-                 lastregno = regno;
-                 continue;
+                 as_bad (_("bad byte vector index (%ld)"),
+                          (long) imm_expr.X_add_number);
+                 imm_expr.X_add_number = 0;
                }
-             else
-               break;
 
-           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 'x':           /* Ignore register name.  */
-           case 'z':           /* Must be zero register.  */
-           case 'U':           /* Destination register (CLO/CLZ).  */
-           case 'g':           /* Coprocessor destination register.  */
-             s_reset = s;
-             if (*args == 'E' || *args == 'K')
-               ok = reg_lookup (&s, RTYPE_NUM, &regno);
-             else
+             INSERT_OPERAND (0, VECALIGN, *ip, imm_expr.X_add_number);
+             imm_expr.X_op = O_absent;
+             s = expr_end;
+             continue;
+
+           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;
+
+               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)
                    {
@@ -9468,26 +12895,25 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                        as_warn (_("Used $%u with \".set at=$%u\""),
                                 regno, mips_opts.at);
                    }
-               }
-             if (ok)
-               {
-                 c = *args;
-                 if (*s == ' ')
-                   ++s;
-                 if (args[1] != *s)
+                 if (!ok)
                    {
-                     if (c == 'r' || c == 'v' || c == 'w')
+                     if (c == 'c')
                        {
+                         gas_assert (args[1] == ',');
                          regno = lastregno;
-                         s = s_reset;
                          ++args;
                        }
+                     else if (c == 't')
+                       {
+                         gas_assert (args[1] == ',');
+                         ++args;
+                         continue;                     /* Nothing to do.  */
+                       }
+                     else
+                       break;
                    }
-                 /* 'z' only matches $0.  */
-                 if (c == 'z' && regno != 0)
-                   break;
 
-                 if (c == 's' && !strncmp (ip->insn_mo->name, "jalr", 4))
+                 if (c == 'j' && !strncmp (ip->insn_mo->name, "jalr", 4))
                    {
                      if (regno == lastregno)
                        {
@@ -9502,614 +12928,712 @@ mips_ip (char *str, struct mips_cl_insn *ip)
                          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 (RS, *ip, regno);
-                     break;
-                   case 'd':
-                   case 'G':
-                   case 'K':
-                   case 'g':
-                     INSERT_OPERAND (RD, *ip, regno);
-                     break;
-                   case 'U':
-                     INSERT_OPERAND (RD, *ip, regno);
-                     INSERT_OPERAND (RT, *ip, regno);
-                     break;
-                   case 'w':
-                   case 't':
-                   case 'E':
-                     INSERT_OPERAND (RT, *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;
-                   case 'D':
-                     /* Itbl operand; not yet implemented. FIXME ?? */
-                     break;
-                     /* What about all other operands like 'i', which
-                        can be specified in the opcode table? */
-                   }
-                 lastregno = regno;
-                 continue;
-               }
-             switch (*args++)
-               {
-               case 'r':
-               case 'v':
-                 INSERT_OPERAND (RS, *ip, lastregno);
-                 continue;
-               case 'w':
-                 INSERT_OPERAND (RT, *ip, lastregno);
-                 continue;
-               }
-             break;
-
-           case 'O':           /* MDMX alignment immediate constant.  */
-             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 (ALN, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
-
-           case 'Q':           /* MDMX vector, element sel, or const.  */
-             if (s[0] != '$')
-               {
-                 /* MDMX Immediate.  */
-                 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 (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 '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':
-           case 'W':
-             rtype = RTYPE_FPU;
-             if (is_mdmx
-                 || (mips_opts.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')
+                     if (c == 'e')
                        {
+                         gas_assert (args[1] == ',');
                          regno = lastregno;
                          s = s_reset;
                          ++args;
                        }
+                     else if (c == 't')
+                       {
+                         gas_assert (args[1] == ',');
+                         s = s_reset;
+                         ++args;
+                         continue;                     /* Nothing to do.  */
+                       }
                    }
+
+                 /* Make sure regno is the same as lastregno.  */
+                 if (c == 't' && regno != lastregno)
+                   break;
+
+                 /* Make sure regno is the same as destregno.  */
+                 if (c == 'x' && regno != destregno)
+                   break;
+
+                 /* We need to save regno, before regno maps to the
+                    microMIPS register encoding.  */
+                 lastregno = regno;
+
+                 if (c == 'f')
+                   destregno = regno;
+
                  switch (c)
                    {
-                   case 'D':
-                   case 'X':
-                     INSERT_OPERAND (FD, *ip, regno);
-                     break;
-                   case 'V':
-                   case 'S':
-                   case 'Y':
-                     INSERT_OPERAND (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 (FT, *ip, regno);
-                     break;
-                   case 'R':
-                     INSERT_OPERAND (FR, *ip, regno);
-                     break;
+                     case 'a':
+                       if (regno != GP)
+                         regno = ILLEGAL_REG;
+                       break;
+
+                     case 'b':
+                       regno = mips32_to_micromips_reg_b_map[regno];
+                       break;
+
+                     case 'c':
+                       regno = mips32_to_micromips_reg_c_map[regno];
+                       break;
+
+                     case 'd':
+                       regno = mips32_to_micromips_reg_d_map[regno];
+                       break;
+
+                     case 'e':
+                       regno = mips32_to_micromips_reg_e_map[regno];
+                       break;
+
+                     case 'f':
+                       regno = mips32_to_micromips_reg_f_map[regno];
+                       break;
+
+                     case 'g':
+                       regno = mips32_to_micromips_reg_g_map[regno];
+                       break;
+
+                     case 'h':
+                       regno = mips32_to_micromips_reg_h_map[regno];
+                       break;
+
+                     case 'i':
+                       switch (EXTRACT_OPERAND (1, MI, *ip))
+                         {
+                           case 4:
+                             if (regno == 21)
+                               regno = 3;
+                             else if (regno == 22)
+                               regno = 4;
+                             else if (regno == 5)
+                               regno = 5;
+                             else if (regno == 6)
+                               regno = 6;
+                             else if (regno == 7)
+                               regno = 7;
+                             else
+                               regno = ILLEGAL_REG;
+                             break;
+
+                           case 5:
+                             if (regno == 6)
+                               regno = 0;
+                             else if (regno == 7)
+                               regno = 1;
+                             else
+                               regno = ILLEGAL_REG;
+                             break;
+
+                           case 6:
+                             if (regno == 7)
+                               regno = 2;
+                             else
+                               regno = ILLEGAL_REG;
+                             break;
+
+                           default:
+                             regno = ILLEGAL_REG;
+                             break;
+                         }
+                       break;
+
+                     case 'l':
+                       regno = mips32_to_micromips_reg_l_map[regno];
+                       break;
+
+                     case 'm':
+                       regno = mips32_to_micromips_reg_m_map[regno];
+                       break;
+
+                     case 'n':
+                       regno = mips32_to_micromips_reg_n_map[regno];
+                       break;
+
+                     case 'q':
+                       regno = mips32_to_micromips_reg_q_map[regno];
+                       break;
+
+                     case 's':
+                       if (regno != SP)
+                         regno = ILLEGAL_REG;
+                       break;
+
+                     case 'y':
+                       if (regno != 31)
+                         regno = ILLEGAL_REG;
+                       break;
+
+                     case 'z':
+                       if (regno != ZERO)
+                         regno = ILLEGAL_REG;
+                       break;
+
+                     case 'j': /* Do nothing.  */
+                     case 'p':
+                     case 't':
+                     case 'x':
+                       break;
+
+                     default:
+                       abort ();
+                   }
+
+                 if (regno == ILLEGAL_REG)
+                   break;
+
+                 switch (c)
+                   {
+                     case 'b':
+                       INSERT_OPERAND (1, MB, *ip, regno);
+                       break;
+
+                     case 'c':
+                       INSERT_OPERAND (1, MC, *ip, regno);
+                       break;
+
+                     case 'd':
+                       INSERT_OPERAND (1, MD, *ip, regno);
+                       break;
+
+                     case 'e':
+                       INSERT_OPERAND (1, ME, *ip, regno);
+                       break;
+
+                     case 'f':
+                       INSERT_OPERAND (1, MF, *ip, regno);
+                       break;
+
+                     case 'g':
+                       INSERT_OPERAND (1, MG, *ip, regno);
+                       break;
+
+                     case 'h':
+                       INSERT_OPERAND (1, MH, *ip, regno);
+                       break;
+
+                     case 'i':
+                       INSERT_OPERAND (1, MI, *ip, regno);
+                       break;
+
+                     case 'j':
+                       INSERT_OPERAND (1, MJ, *ip, regno);
+                       break;
+
+                     case 'l':
+                       INSERT_OPERAND (1, ML, *ip, regno);
+                       break;
+
+                     case 'm':
+                       INSERT_OPERAND (1, MM, *ip, regno);
+                       break;
+
+                     case 'n':
+                       INSERT_OPERAND (1, MN, *ip, regno);
+                       break;
+
+                     case 'p':
+                       INSERT_OPERAND (1, MP, *ip, regno);
+                       break;
+
+                     case 'q':
+                       INSERT_OPERAND (1, MQ, *ip, regno);
+                       break;
+
+                     case 'a': /* Do nothing.  */
+                     case 's': /* Do nothing.  */
+                     case 't': /* Do nothing.  */
+                     case 'x': /* Do nothing.  */
+                     case 'y': /* Do nothing.  */
+                     case 'z': /* Do nothing.  */
+                       break;
+
+                     default:
+                       abort ();
                    }
-                 lastregno = regno;
                  continue;
-               }
 
-             switch (*args++)
-               {
-               case 'V':
-                 INSERT_OPERAND (FS, *ip, lastregno);
-                 continue;
-               case 'W':
-                 INSERT_OPERAND (FT, *ip, lastregno);
+               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;
-               }
-             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 'B':
+                 {
+                   bfd_reloc_code_real_type r[3];
+                   expressionS ep;
+                   int imm;
 
-           case 'A':
-             my_getExpression (&offset_expr, s);
-             normalize_address_expr (&offset_expr);
-             *imm_reloc = BFD_RELOC_32;
-             s = expr_end;
-             continue;
+                   if (my_getSmallExpression (&ep, r, s) > 0
+                       || ep.X_op != O_constant)
+                     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;
+                   for (imm = 0; imm < 8; imm++)
+                     if (micromips_imm_b_map[imm] == ep.X_add_number)
+                       break;
+                   if (imm >= 8)
+                     break;
 
-               /* 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.
+                   INSERT_OPERAND (1, IMMB, *ip, imm);
+                 }
+                 s = expr_end;
+                 continue;
 
-                  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
+               case 'C':
+                 {
+                   bfd_reloc_code_real_type r[3];
+                   expressionS ep;
+                   int imm;
 
-                   The .lit4 and .lit8 sections are only used if
-                   permitted by the -G argument.
+                   if (my_getSmallExpression (&ep, r, s) > 0
+                       || ep.X_op != O_constant)
+                     break;
 
-                   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.  */
+                   for (imm = 0; imm < 16; imm++)
+                     if (micromips_imm_c_map[imm] == ep.X_add_number)
+                       break;
+                   if (imm >= 16)
+                     break;
 
-               f64 = *args == 'F' || *args == 'L';
-               using_gprs = *args == 'F' || *args == 'f';
+                   INSERT_OPERAND (1, IMMC, *ip, imm);
+                 }
+                 s = expr_end;
+                 continue;
 
-               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')
+               case 'D':       /* pc relative offset */
+               case 'E':       /* pc relative offset */
+                 my_getExpression (&offset_expr, s);
+                 if (offset_expr.X_op == O_register)
+                   break;
+
+                 if (!forced_insn_length)
+                   *offset_reloc = (int) BFD_RELOC_UNUSED + c;
+                 else if (c == 'D')
+                   *offset_reloc = BFD_RELOC_MICROMIPS_10_PCREL_S1;
+                 else
+                   *offset_reloc = BFD_RELOC_MICROMIPS_7_PCREL_S1;
+                 s = expr_end;
+                 continue;
+
+               case 'F':
                  {
-                   as_bad (_("Bad floating point constant: %s"), err);
-                   memset (temp, '\0', sizeof temp);
-                   length = f64 ? 8 : 4;
-                 }
+                   bfd_reloc_code_real_type r[3];
+                   expressionS ep;
+                   int imm;
 
-               gas_assert (length == (unsigned) (f64 ? 8 : 4));
+                   if (my_getSmallExpression (&ep, r, s) > 0
+                       || !expr_const_in_range (&ep, 0, 16, 0))
+                     break;
 
-               if (*args == 'f'
-                   || (*args == 'l'
-                       && (g_switch_value < 4
-                           || (temp[0] == 0 && temp[1] == 0)
-                           || (temp[2] == 0 && temp[3] == 0))))
+                   imm = ep.X_add_number;
+                   INSERT_OPERAND (1, IMMF, *ip, imm);
+                 }
+                 s = expr_end;
+                 continue;
+
+               case 'G':
                  {
-                   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);
+                   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);
                  }
-               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)))
+                 s = expr_end;
+                 continue;
+
+               case 'H':
                  {
-                   /* 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)
+                   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)
                      {
-                       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;
+                       INSERT_OPERAND (1, IMMH, *ip, 0);
+                       continue;
                      }
-                   else if (sizeof (imm_expr.X_add_number) > 4)
+
+                   if (my_getSmallExpression (&ep, r, s) > 0
+                       || !expr_const_in_range (&ep, 0, 16, 1))
+                     break;
+
+                   imm = ep.X_add_number >> 1;
+                   INSERT_OPERAND (1, IMMH, *ip, imm);
+                 }
+                 s = expr_end;
+                 continue;
+
+               case 'I':
+                 {
+                   bfd_reloc_code_real_type r[3];
+                   expressionS ep;
+                   int imm;
+
+                   if (my_getSmallExpression (&ep, r, s) > 0
+                       || !expr_const_in_range (&ep, -1, 127, 0))
+                     break;
+
+                   imm = ep.X_add_number & 127;
+                   INSERT_OPERAND (1, IMMI, *ip, imm);
+                 }
+                 s = expr_end;
+                 continue;
+
+               case 'J':
+                 {
+                   bfd_reloc_code_real_type r[3];
+                   expressionS ep;
+                   int imm;
+
+                   /* Check whether there is only a single bracketed
+                      expression left.  If so, it must be the base register
+                      and the constant must be zero.  */
+                   if (*s == '(' && strchr (s + 1, '(') == 0)
                      {
-                       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);
+                       INSERT_OPERAND (1, IMMJ, *ip, 0);
+                       continue;
                      }
-                   else
+
+                   if (my_getSmallExpression (&ep, r, s) > 0
+                       || !expr_const_in_range (&ep, 0, 16, 2))
+                     break;
+
+                   imm = ep.X_add_number >> 2;
+                   INSERT_OPERAND (1, IMMJ, *ip, imm);
+                 }
+                 s = expr_end;
+                 continue;
+
+               case 'L':
+                 {
+                   bfd_reloc_code_real_type r[3];
+                   expressionS ep;
+                   int imm;
+
+                   /* Check whether there is only a single bracketed
+                      expression left.  If so, it must be the base register
+                      and the constant must be zero.  */
+                   if (*s == '(' && strchr (s + 1, '(') == 0)
                      {
-                       imm_expr.X_op = O_big;
-                       imm_expr.X_add_number = 4;
-                       if (!target_big_endian)
-                         {
-                           generic_bignum[0] = bfd_getl16 (temp);
-                           generic_bignum[1] = bfd_getl16 (temp + 2);
-                           generic_bignum[2] = bfd_getl16 (temp + 4);
-                           generic_bignum[3] = bfd_getl16 (temp + 6);
-                         }
-                       else
-                         {
-                           generic_bignum[0] = bfd_getb16 (temp + 6);
-                           generic_bignum[1] = bfd_getb16 (temp + 4);
-                           generic_bignum[2] = bfd_getb16 (temp + 2);
-                           generic_bignum[3] = bfd_getb16 (temp);
-                         }
+                       INSERT_OPERAND (1, IMML, *ip, 0);
+                       continue;
                      }
+
+                   if (my_getSmallExpression (&ep, r, s) > 0
+                       || !expr_const_in_range (&ep, 0, 16, 0))
+                     break;
+
+                   imm = ep.X_add_number;
+                   INSERT_OPERAND (1, IMML, *ip, imm);
+                 }
+                 s = expr_end;
+                 continue;
+
+               case 'M':
+                 {
+                   bfd_reloc_code_real_type r[3];
+                   expressionS ep;
+                   int imm;
+
+                   if (my_getSmallExpression (&ep, r, s) > 0
+                       || !expr_const_in_range (&ep, 1, 9, 0))
+                     break;
+
+                   imm = ep.X_add_number & 7;
+                   INSERT_OPERAND (1, IMMM, *ip, imm);
+                 }
+                 s = expr_end;
+                 continue;
+
+               case 'N':       /* Register list for lwm and swm.  */
+                 {
+                   /* A comma-separated list of registers and/or
+                      dash-separated contiguous ranges including
+                      both ra and a set of one or more registers
+                      starting at s0 up to s3 which have to be
+                      consecutive, e.g.:
+
+                      s0, ra
+                      s0, s1, ra, s2, s3
+                      s0-s2, ra
+
+                      and any permutations of these.  */
+                   unsigned int reglist;
+                   int imm;
+
+                   if (!reglist_lookup (&s, RTYPE_NUM | RTYPE_GP, &reglist))
+                     break;
+
+                   if ((reglist & 0xfff1ffff) != 0x80010000)
+                     break;
+
+                   reglist = (reglist >> 17) & 7;
+                   reglist += 1;
+                   if ((reglist & -reglist) != reglist)
+                     break;
+
+                   imm = ffs (reglist) - 1;
+                   INSERT_OPERAND (1, IMMN, *ip, imm);
+                 }
+                 continue;
+
+               case 'O':       /* sdbbp 4-bit code.  */
+                 {
+                   bfd_reloc_code_real_type r[3];
+                   expressionS ep;
+                   int imm;
+
+                   if (my_getSmallExpression (&ep, r, s) > 0
+                       || !expr_const_in_range (&ep, 0, 16, 0))
+                     break;
+
+                   imm = ep.X_add_number;
+                   INSERT_OPERAND (1, IMMO, *ip, imm);
                  }
-               else
+                 s = expr_end;
+                 continue;
+
+               case 'P':
                  {
-                   const char *newname;
-                   segT new_seg;
+                   bfd_reloc_code_real_type r[3];
+                   expressionS ep;
+                   int imm;
 
-                   /* Switch to the right section.  */
-                   seg = now_seg;
-                   subseg = now_subseg;
-                   switch (*args)
-                     {
-                     default: /* unused default case avoids warnings.  */
-                     case 'L':
-                       newname = RDATA_SECTION_NAME;
-                       if (g_switch_value >= 8)
-                         newname = ".lit8";
-                       break;
-                     case 'F':
-                       newname = RDATA_SECTION_NAME;
-                       break;
-                     case 'l':
-                       gas_assert (g_switch_value >= 4);
-                       newname = ".lit4";
-                       break;
-                     }
-                   new_seg = subseg_new (newname, (subsegT) 0);
-                   if (IS_ELF)
-                     bfd_set_section_flags (stdoutput, new_seg,
-                                            (SEC_ALLOC
-                                             | SEC_LOAD
-                                             | SEC_READONLY
-                                             | SEC_DATA));
-                   frag_align (*args == 'l' ? 2 : 3, 0, 0);
-                   if (IS_ELF && strncmp (TARGET_OS, "elf", 3) != 0)
-                     record_alignment (new_seg, 4);
-                   else
-                     record_alignment (new_seg, *args == 'l' ? 2 : 3);
-                   if (seg == now_seg)
-                     as_bad (_("Can't use floating point insn in this section"));
+                   if (my_getSmallExpression (&ep, r, s) > 0
+                       || !expr_const_in_range (&ep, 0, 32, 2))
+                     break;
 
-                   /* 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;
+                   imm = ep.X_add_number >> 2;
+                   INSERT_OPERAND (1, IMMP, *ip, imm);
+                 }
+                 s = expr_end;
+                 continue;
 
-                   /* Put the floating point number into the section.  */
-                   p = frag_more ((int) length);
-                   memcpy (p, temp, length);
+               case 'Q':
+                 {
+                   bfd_reloc_code_real_type r[3];
+                   expressionS ep;
+                   int imm;
 
-                   /* Switch back to the original section.  */
-                   subseg_set (seg, subseg);
-                 }
-             }
-             continue;
+                   if (my_getSmallExpression (&ep, r, s) > 0
+                       || !expr_const_in_range (&ep, -0x400000, 0x400000, 2))
+                     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;
+                   imm = ep.X_add_number >> 2;
+                   INSERT_OPERAND (1, IMMQ, *ip, imm);
+                 }
+                 s = expr_end;
+                 continue;
 
-                 more = (insn + 1 < &mips_opcodes[NUMOPCODES]
-                         && strcmp (insn->name, insn[1].name) == 0);
+               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 the expression was written as an unsigned number,
-                    only treat it as signed if there are no more
-                    alternatives.  */
-                 if (more
-                     && *args == 'j'
-                     && sizeof (imm_expr.X_add_number) <= 4
-                     && imm_expr.X_op == O_constant
-                     && imm_expr.X_add_number < 0
-                     && imm_expr.X_unsigned
-                     && HAVE_64BIT_GPRS)
-                   break;
+                   if (my_getSmallExpression (&ep, r, s) > 0
+                       || !expr_const_in_range (&ep, 0, 32, 2))
+                     break;
 
-                 /* For compatibility with older assemblers, we accept
-                    0x8000-0xffff as signed 16-bit numbers when only
-                    signed numbers are allowed.  */
-                 if (*args == 'i')
-                   minval = 0, maxval = 0xffff;
-                 else if (more)
-                   minval = -0x8000, maxval = 0x7fff;
-                 else
-                   minval = -0x8000, maxval = 0xffff;
+                   imm = ep.X_add_number >> 2;
+                   INSERT_OPERAND (1, IMMU, *ip, imm);
+                 }
+                 s = expr_end;
+                 continue;
 
-                 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 'W':
+                 {
+                   bfd_reloc_code_real_type r[3];
+                   expressionS ep;
+                   int imm;
 
-           case 'o':           /* 16-bit offset.  */
-             offset_reloc[0] = BFD_RELOC_LO16;
-             offset_reloc[1] = BFD_RELOC_UNUSED;
-             offset_reloc[2] = BFD_RELOC_UNUSED;
+                   if (my_getSmallExpression (&ep, r, s) > 0
+                       || !expr_const_in_range (&ep, 0, 64, 2))
+                     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.  */
-             offset_reloc[0] = BFD_RELOC_LO16;
-             offset_reloc[1] = BFD_RELOC_UNUSED;
-             offset_reloc[2] = BFD_RELOC_UNUSED;
-             if (*s == '(' && strchr (s + 1, '(') == 0)
-               {
-                 offset_expr.X_op = O_constant;
-                 offset_expr.X_add_number = 0;
+                   imm = ep.X_add_number >> 2;
+                   INSERT_OPERAND (1, IMMW, *ip, imm);
+                 }
+                 s = expr_end;
                  continue;
-               }
-
-             /* If this value won't fit into a 16 bit offset, then go
-                find a macro that will generate the 32 bit offset
-                code pattern.  */
-             if (my_getSmallExpression (&offset_expr, offset_reloc, s) == 0
-                 && (offset_expr.X_op != O_constant
-                     || offset_expr.X_add_number >= 0x8000
-                     || offset_expr.X_add_number < -0x8000))
-               break;
 
-             s = expr_end;
-             continue;
+               case 'X':
+                 {
+                   bfd_reloc_code_real_type r[3];
+                   expressionS ep;
+                   int imm;
 
-           case 'p':           /* PC-relative offset.  */
-             *offset_reloc = BFD_RELOC_16_PCREL_S2;
-             my_getExpression (&offset_expr, s);
-             s = expr_end;
-             continue;
+                   if (my_getSmallExpression (&ep, r, s) > 0
+                       || !expr_const_in_range (&ep, -8, 8, 0))
+                     break;
 
-           case 'u':           /* Upper 16 bits.  */
-             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;
+                   imm = ep.X_add_number;
+                   INSERT_OPERAND (1, IMMX, *ip, imm);
+                 }
+                 s = expr_end;
+                 continue;
 
-           case 'a':           /* 26-bit address.  */
-             my_getExpression (&offset_expr, s);
-             s = expr_end;
-             *offset_reloc = BFD_RELOC_MIPS_JMP;
-             continue;
+               case 'Y':
+                 {
+                   bfd_reloc_code_real_type r[3];
+                   expressionS ep;
+                   int imm;
 
-           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 (BCC, *ip, regno);
-             else
-               INSERT_OPERAND (CCC, *ip, regno);
-             continue;
+                   if (my_getSmallExpression (&ep, r, s) > 0
+                       || expr_const_in_range (&ep, -2, 2, 2)
+                       || !expr_const_in_range (&ep, -258, 258, 2))
+                     break;
 
-           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.  */
+                   imm = ep.X_add_number >> 2;
+                   imm = ((imm >> 1) & ~0xff) | (imm & 0xff);
+                   INSERT_OPERAND (1, IMMY, *ip, imm);
+                 }
+                 s = expr_end;
+                 continue;
 
-             if (c > 7)
-               as_bad (_("Invalid coprocessor sub-selection value (0-7)"));
-             ip->insn_opcode |= c;
-             continue;
+               case 'Z':
+                 {
+                   bfd_reloc_code_real_type r[3];
+                   expressionS ep;
 
-           case 'e':
-             /* Must be at least one digit.  */
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
+                   if (my_getSmallExpression (&ep, r, s) > 0
+                       || !expr_const_in_range (&ep, 0, 1, 0))
+                     break;
+                 }
+                 s = expr_end;
+                 continue;
 
-             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;
+               default:
+                 as_bad (_("Internal error: bad microMIPS opcode "
+                           "(unknown extension operand type `m%c'): %s %s"),
+                         *args, insn->name, insn->args);
+                 /* Further processing is fruitless.  */
+                 return;
                }
+             break;
 
-             INSERT_OPERAND (VECBYTE, *ip, imm_expr.X_add_number);
-             imm_expr.X_op = O_absent;
-             s = expr_end;
-             continue;
+           case 'n':           /* Register list for 32-bit lwm and swm.  */
+             gas_assert (mips_opts.micromips);
+             {
+               /* A comma-separated list of registers and/or
+                  dash-separated contiguous ranges including
+                  at least one of ra and a set of one or more
+                  registers starting at s0 up to s7 and then
+                  s8 which have to be consecutive, e.g.:
+
+                  ra
+                  s0
+                  ra, s0, s1, s2
+                  s0-s8
+                  s0-s5, ra
+
+                  and any permutations of these.  */
+               unsigned int reglist;
+               int imm;
+               int ra;
+
+               if (!reglist_lookup (&s, RTYPE_NUM | RTYPE_GP, &reglist))
+                 break;
 
-           case '%':
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
+               if ((reglist & 0x3f00ffff) != 0)
+                 break;
 
-             if ((unsigned long) imm_expr.X_add_number
-                 > (unsigned long) OP_MASK_VECALIGN)
-               {
-                 as_bad (_("bad byte vector index (%ld)"),
-                          (long) imm_expr.X_add_number);
-                 imm_expr.X_add_number = 0;
-               }
+               ra = (reglist >> 27) & 0x10;
+               reglist = ((reglist >> 22) & 0x100) | ((reglist >> 16) & 0xff);
+               reglist += 1;
+               if ((reglist & -reglist) != reglist)
+                 break;
+
+               imm = (ffs (reglist) - 1) | ra;
+               INSERT_OPERAND (1, RT, *ip, imm);
+               imm_expr.X_op = O_absent;
+             }
+             continue;
 
-             INSERT_OPERAND (VECALIGN, *ip, imm_expr.X_add_number);
+           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;
 
            default:
              as_bad (_("Bad char = '%c'\n"), *args);
-             internalError ();
+             abort ();
            }
          break;
        }
       /* Args don't match.  */
-      if (insn + 1 < &mips_opcodes[NUMOPCODES] &&
-         !strcmp (insn->name, insn[1].name))
+      s = argsStart;
+      insn_error = _("Illegal operands");
+      if (insn + 1 < past && !strcmp (insn->name, insn[1].name))
        {
          ++insn;
-         s = argsStart;
-         insn_error = _("Illegal operands");
          continue;
        }
-      if (save_c)
-       *(--argsStart) = save_c;
-      insn_error = _("Illegal operands");
+      else if (wrong_delay_slot_insns && need_delay_slot_ok)
+       {
+         gas_assert (firstinsn);
+         need_delay_slot_ok = FALSE;
+         past = insn + 1;
+         insn = firstinsn;
+         continue;
+       }
       return;
     }
 }
@@ -10118,10 +13642,10 @@ mips_ip (char *str, struct mips_cl_insn *ip)
 
 /* 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 mips16_small and mips16_ext if the user explicitly
-   requested a small or extended instruction.  */
+   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.  */
 
 static void
 mips16_ip (char *str, struct mips_cl_insn *ip)
@@ -10137,8 +13661,7 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
 
   insn_error = NULL;
 
-  mips16_small = FALSE;
-  mips16_ext = FALSE;
+  forced_insn_length = 0;
 
   for (s = str; ISLOWER (*s); ++s)
     ;
@@ -10155,14 +13678,14 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
       if (s[1] == 't' && s[2] == ' ')
        {
          *s = '\0';
-         mips16_small = TRUE;
+         forced_insn_length = 2;
          s += 3;
          break;
        }
       else if (s[1] == 'e' && s[2] == ' ')
        {
          *s = '\0';
-         mips16_ext = TRUE;
+         forced_insn_length = 4;
          s += 3;
          break;
        }
@@ -10172,8 +13695,8 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
       return;
     }
 
-  if (mips_opts.noautoextend && ! mips16_ext)
-    mips16_small = TRUE;
+  if (mips_opts.noautoextend && !forced_insn_length)
+    forced_insn_length = 2;
 
   if ((insn = (struct mips_opcode *) hash_find (mips16_op_hash, str)) == NULL)
     {
@@ -10203,7 +13726,7 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
                {
                  static char buf[100];
                  sprintf (buf,
-                          _("opcode not supported on this processor: %s (%s)"),
+                          _("Opcode not supported on this processor: %s (%s)"),
                           mips_cpu_info_from_arch (mips_opts.arch)->name,
                           mips_cpu_info_from_isa (mips_opts.isa)->name);
                  insn_error = buf;
@@ -10239,45 +13762,21 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
            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
-                     && *imm_reloc != BFD_RELOC_MIPS16_GOT16
-                     && *imm_reloc != BFD_RELOC_MIPS16_CALL16
-                     && insn->pinfo != INSN_MACRO)
+                     && insn->pinfo != INSN_MACRO
+                     && calculate_reloc (*offset_reloc,
+                                         imm_expr.X_add_number, &value))
                    {
-                     valueT tmp;
-
-                     switch (*offset_reloc)
-                       {
-                         case BFD_RELOC_MIPS16_HI16_S:
-                           tmp = (imm_expr.X_add_number + 0x8000) >> 16;
-                           break;
-
-                         case BFD_RELOC_MIPS16_HI16:
-                           tmp = imm_expr.X_add_number >> 16;
-                           break;
-
-                         case BFD_RELOC_MIPS16_LO16:
-                           tmp = ((imm_expr.X_add_number + 0x8000) & 0xffff)
-                                 - 0x8000;
-                           break;
-
-                         case BFD_RELOC_UNUSED:
-                           tmp = imm_expr.X_add_number;
-                           break;
-
-                         default:
-                           internalError ();
-                       }
-                     *offset_reloc = BFD_RELOC_UNUSED;
-
                      mips16_immed (NULL, 0, *imm_reloc - BFD_RELOC_UNUSED,
-                                   tmp, TRUE, mips16_small,
-                                   mips16_ext, &ip->insn_opcode,
-                                   &ip->use_extend, &ip->extend);
+                                   *offset_reloc, value, forced_insn_length,
+                                   &ip->insn_opcode);
                      imm_expr.X_op = O_absent;
                      *imm_reloc = BFD_RELOC_UNUSED;
+                     *offset_reloc = BFD_RELOC_UNUSED;
                    }
 
                  return;
@@ -10392,7 +13891,7 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
                  break;
 
                default:
-                 internalError ();
+                 abort ();
                }
 
              if (regno == ILLEGAL_REG)
@@ -10425,7 +13924,7 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
                  MIPS16_INSERT_OPERAND (REG32R, *ip, regno);
                  break;
                default:
-                 internalError ();
+                 abort ();
                }
 
              lastregno = regno;
@@ -10454,9 +13953,8 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
                {
                  if (imm_expr.X_op != O_constant)
                    {
-                     mips16_ext = TRUE;
-                     ip->use_extend = TRUE;
-                     ip->extend = 0;
+                     forced_insn_length = 4;
+                     ip->insn_opcode |= MIPS16_EXTEND;
                    }
                  else
                    {
@@ -10610,7 +14108,7 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
            case 'm':           /* Register list for save insn.  */
            case 'M':           /* Register list for restore insn.  */
              {
-               int opcode = 0;
+               int opcode = ip->insn_opcode;
                int framesz = 0, seen_framesz = 0;
                int nargs = 0, statics = 0, sregs = 0;
 
@@ -10764,11 +14262,8 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
 
                /* Finally build the instruction.  */
                if ((opcode >> 16) != 0 || framesz == 0)
-                 {
-                   ip->use_extend = TRUE;
-                   ip->extend = opcode >> 16;
-                 }
-               ip->insn_opcode |= opcode & 0x7f;
+                 opcode |= MIPS16_EXTEND;
+               ip->insn_opcode = opcode;
              }
            continue;
 
@@ -10788,7 +14283,7 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
              continue;
 
            default:
-             internalError ();
+             abort ();
            }
          break;
        }
@@ -10863,23 +14358,49 @@ static const struct mips16_immed_operand mips16_immed_operands[] =
 #define MIPS16_NUM_IMMED \
   (sizeof mips16_immed_operands / sizeof mips16_immed_operands[0])
 
-/* Handle a mips16 instruction with an immediate value.  This or's the
-   small immediate value into *INSN.  It sets *USE_EXTEND to indicate
-   whether an extended value is needed; if one is needed, it sets
-   *EXTEND to the value.  The argument type is TYPE.  The value is VAL.
-   If SMALL is true, an unextended opcode was explicitly requested.
-   If EXT is true, an extended opcode was explicitly requested.  If
-   WARN is true, warn if EXT does not match reality.  */
+/* Marshal immediate value VAL for an extended MIPS16 instruction.
+   NBITS is the number of significant bits in VAL.  */
+
+static unsigned long
+mips16_immed_extend (offsetT val, unsigned int nbits)
+{
+  int extval;
+  if (nbits == 16)
+    {
+      extval = ((val >> 11) & 0x1f) | (val & 0x7e0);
+      val &= 0x1f;
+    }
+  else if (nbits == 15)
+    {
+      extval = ((val >> 11) & 0xf) | (val & 0x7f0);
+      val &= 0xf;
+    }
+  else
+    {
+      extval = ((val & 0x1f) << 6) | (val & 0x20);
+      val = 0;
+    }
+  return (extval << 16) | val;
+}
+
+/* Install immediate value VAL into MIPS16 instruction *INSN,
+   extending it if necessary.  The instruction in *INSN may
+   already be extended.
+
+   RELOC is the relocation that produced VAL, or BFD_RELOC_UNUSED
+   if none.  In the former case, VAL is a 16-bit number with no
+   defined signedness.
+
+   TYPE is the type of the immediate field.  USER_INSN_LENGTH
+   is the length that the user requested, or 0 if none.  */
 
 static void
-mips16_immed (char *file, unsigned int line, int type, offsetT val,
-             bfd_boolean warn, bfd_boolean small, bfd_boolean ext,
-             unsigned long *insn, bfd_boolean *use_extend,
-             unsigned short *extend)
+mips16_immed (char *file, unsigned int line, int type,
+             bfd_reloc_code_real_type reloc, offsetT val,
+             unsigned int user_insn_length, unsigned long *insn)
 {
   const struct mips16_immed_operand *op;
   int mintiny, maxtiny;
-  bfd_boolean needext;
 
   op = mips16_immed_operands;
   while (op->type != type)
@@ -10900,11 +14421,15 @@ mips16_immed (char *file, unsigned int line, int type, offsetT val,
          mintiny = 0;
          maxtiny = (1 << op->nbits) - 1;
        }
+      if (reloc != BFD_RELOC_UNUSED)
+       val &= 0xffff;
     }
   else
     {
       mintiny = - (1 << (op->nbits - 1));
       maxtiny = (1 << (op->nbits - 1)) - 1;
+      if (reloc != BFD_RELOC_UNUSED)
+       val = SEXT_16BIT (val);
     }
 
   /* Branch offsets have an implicit 0 in the lowest bit.  */
@@ -10914,21 +14439,26 @@ mips16_immed (char *file, unsigned int line, int type, offsetT val,
   if ((val & ((1 << op->shift) - 1)) != 0
       || val < (mintiny << op->shift)
       || val > (maxtiny << op->shift))
-    needext = TRUE;
-  else
-    needext = FALSE;
-
-  if (warn && ext && ! needext)
-    as_warn_where (file, line,
-                  _("extended operand requested but not required"));
-  if (small && needext)
-    as_bad_where (file, line, _("invalid unextended operand value"));
+    {
+      /* We need an extended instruction.  */
+      if (user_insn_length == 2)
+       as_bad_where (file, line, _("invalid unextended operand value"));
+      else
+       *insn |= MIPS16_EXTEND;
+    }
+  else if (user_insn_length == 4)
+    {
+      /* The operand doesn't force an unextended instruction to be extended.
+        Warn if the user wanted an extended instruction anyway.  */
+      *insn |= MIPS16_EXTEND;
+      as_warn_where (file, line,
+                    _("extended operand requested but not required"));
+    }
 
-  if (small || (! ext && ! needext))
+  if (mips16_opcode_length (*insn) == 2)
     {
       int insnval;
 
-      *use_extend = FALSE;
       insnval = ((val >> op->shift) & ((1 << op->nbits) - 1));
       insnval <<= op->op_shift;
       *insn |= insnval;
@@ -10936,41 +14466,25 @@ mips16_immed (char *file, unsigned int line, int type, offsetT val,
   else
     {
       long minext, maxext;
-      int extval;
 
-      if (op->extu)
+      if (reloc == BFD_RELOC_UNUSED)
        {
-         minext = 0;
-         maxext = (1 << op->extbits) - 1;
-       }
-      else
-       {
-         minext = - (1 << (op->extbits - 1));
-         maxext = (1 << (op->extbits - 1)) - 1;
-       }
-      if (val < minext || val > maxext)
-       as_bad_where (file, line,
-                     _("operand value out of range for instruction"));
-
-      *use_extend = TRUE;
-      if (op->extbits == 16)
-       {
-         extval = ((val >> 11) & 0x1f) | (val & 0x7e0);
-         val &= 0x1f;
-       }
-      else if (op->extbits == 15)
-       {
-         extval = ((val >> 11) & 0xf) | (val & 0x7f0);
-         val &= 0xf;
-       }
-      else
-       {
-         extval = ((val & 0x1f) << 6) | (val & 0x20);
-         val = 0;
+         if (op->extu)
+           {
+             minext = 0;
+             maxext = (1 << op->extbits) - 1;
+           }
+         else
+           {
+             minext = - (1 << (op->extbits - 1));
+             maxext = (1 << (op->extbits - 1)) - 1;
+           }
+         if (val < minext || val > maxext)
+           as_bad_where (file, line,
+                         _("operand value out of range for instruction"));
        }
 
-      *extend = (unsigned short) extval;
-      *insn |= val;
+      *insn |= mips16_immed_extend (val, op->extbits);
     }
 }
 \f
@@ -11015,7 +14529,14 @@ static const struct percent_op_match mips16_percent_op[] =
   {"%gprel", BFD_RELOC_MIPS16_GPREL},
   {"%got", BFD_RELOC_MIPS16_GOT16},
   {"%call16", BFD_RELOC_MIPS16_CALL16},
-  {"%hi", BFD_RELOC_MIPS16_HI16_S}
+  {"%hi", BFD_RELOC_MIPS16_HI16_S},
+  {"%tlsgd", BFD_RELOC_MIPS16_TLS_GD},
+  {"%tlsldm", BFD_RELOC_MIPS16_TLS_LDM},
+  {"%dtprel_hi", BFD_RELOC_MIPS16_TLS_DTPREL_HI16},
+  {"%dtprel_lo", BFD_RELOC_MIPS16_TLS_DTPREL_LO16},
+  {"%tprel_hi", BFD_RELOC_MIPS16_TLS_TPREL_HI16},
+  {"%tprel_lo", BFD_RELOC_MIPS16_TLS_TPREL_LO16},
+  {"%gottprel", BFD_RELOC_MIPS16_TLS_GOTTPREL}
 };
 
 
@@ -11129,26 +14650,12 @@ static void
 my_getExpression (expressionS *ep, char *str)
 {
   char *save_in;
-  valueT val;
 
   save_in = input_line_pointer;
   input_line_pointer = str;
   expression (ep);
   expr_end = input_line_pointer;
   input_line_pointer = save_in;
-
-  /* If we are in mips16 mode, and this is an expression based on `.',
-     then we bump the value of the symbol by 1 since that is how other
-     text symbols are handled.  We don't bother to handle complex
-     expressions, just `.' plus or minus a constant.  */
-  if (mips_opts.mips16
-      && ep->X_op == O_symbol
-      && strcmp (S_GET_NAME (ep->X_add_symbol), FAKE_LABEL_NAME) == 0
-      && S_GET_SEGMENT (ep->X_add_symbol) == now_seg
-      && symbol_get_frag (ep->X_add_symbol) == frag_now
-      && symbol_constant_p (ep->X_add_symbol)
-      && (val = S_GET_VALUE (ep->X_add_symbol)) == frag_now_fix ())
-    S_SET_VALUE (ep->X_add_symbol, val + 1);
 }
 
 char *
@@ -11174,14 +14681,8 @@ static int support_64bit_objects(void)
 
   list = bfd_target_list ();
   for (l = list; *l != NULL; l++)
-#ifdef TE_TMIPS
-    /* This is traditional mips */
-    if (strcmp (*l, "elf64-tradbigmips") == 0
-       || strcmp (*l, "elf64-tradlittlemips") == 0)
-#else
-    if (strcmp (*l, "elf64-bigmips") == 0
-       || strcmp (*l, "elf64-littlemips") == 0)
-#endif
+    if (strcmp (*l, ELF_TARGET ("elf64-", "big")) == 0
+       || strcmp (*l, ELF_TARGET ("elf64-", "little")) == 0)
       break;
   yes = (*l != NULL);
   free (list);
@@ -11214,10 +14715,18 @@ enum options
     OPTION_NO_DSP,
     OPTION_MT,
     OPTION_NO_MT,
+    OPTION_VIRT,
+    OPTION_NO_VIRT,
     OPTION_SMARTMIPS,
     OPTION_NO_SMARTMIPS,
     OPTION_DSPR2,
     OPTION_NO_DSPR2,
+    OPTION_EVA,
+    OPTION_NO_EVA,
+    OPTION_MICROMIPS,
+    OPTION_NO_MICROMIPS,
+    OPTION_MCU,
+    OPTION_NO_MCU,
     OPTION_COMPAT_ARCH_BASE,
     OPTION_M4650,
     OPTION_NO_M4650,
@@ -11310,6 +14819,14 @@ struct option md_longopts[] =
   {"mno-smartmips", no_argument, NULL, OPTION_NO_SMARTMIPS},
   {"mdspr2", no_argument, NULL, OPTION_DSPR2},
   {"mno-dspr2", no_argument, NULL, OPTION_NO_DSPR2},
+  {"meva", no_argument, NULL, OPTION_EVA},
+  {"mno-eva", no_argument, NULL, OPTION_NO_EVA},
+  {"mmicromips", no_argument, NULL, OPTION_MICROMIPS},
+  {"mno-micromips", no_argument, NULL, OPTION_NO_MICROMIPS},
+  {"mmcu", no_argument, NULL, OPTION_MCU},
+  {"mno-mcu", no_argument, NULL, OPTION_NO_MCU},
+  {"mvirt", no_argument, NULL, OPTION_VIRT},
+  {"mno-virt", no_argument, NULL, OPTION_NO_VIRT},
 
   /* Old-style architecture options.  Don't add more of these.  */
   {"m4650", no_argument, NULL, OPTION_M4650},
@@ -11556,6 +15073,14 @@ md_parse_option (int c, char *arg)
       mips_opts.ase_dsp = 0;
       break;
 
+    case OPTION_EVA:
+      mips_opts.ase_eva = 1;
+      break;
+
+    case OPTION_NO_EVA:
+      mips_opts.ase_eva = 0;
+      break;
+
     case OPTION_MT:
       mips_opts.ase_mt = 1;
       break;
@@ -11564,7 +15089,43 @@ md_parse_option (int c, char *arg)
       mips_opts.ase_mt = 0;
       break;
 
+    case OPTION_MCU:
+      mips_opts.ase_mcu = 1;
+      break;
+
+    case OPTION_NO_MCU:
+      mips_opts.ase_mcu = 0;
+      break;
+
+    case OPTION_MICROMIPS:
+      if (mips_opts.mips16 == 1)
+       {
+         as_bad (_("-mmicromips cannot be used with -mips16"));
+         return 0;
+       }
+      mips_opts.micromips = 1;
+      mips_no_prev_insn ();
+      break;
+
+    case OPTION_NO_MICROMIPS:
+      mips_opts.micromips = 0;
+      mips_no_prev_insn ();
+      break;
+
+    case OPTION_VIRT:
+      mips_opts.ase_virt = 1;
+      break;
+
+    case OPTION_NO_VIRT:
+      mips_opts.ase_virt = 0;
+      break;
+
     case OPTION_MIPS16:
+      if (mips_opts.micromips == 1)
+       {
+         as_bad (_("-mips16 cannot be used with -micromips"));
+         return 0;
+       }
       mips_opts.mips16 = 1;
       mips_no_prev_insn ();
       break;
@@ -11911,7 +15472,10 @@ mips_after_parse_args (void)
     }
 
   if (arch_info == 0)
-    arch_info = mips_parse_cpu ("default CPU", MIPS_CPU_STRING_DEFAULT);
+    {
+      arch_info = mips_parse_cpu ("default CPU", MIPS_CPU_STRING_DEFAULT);
+      gas_assert (arch_info);
+    }
 
   if (ABI_NEEDS_64BIT_REGS (mips_abi) && !ISA_HAS_64BIT_REGS (arch_info->isa))
     as_bad (_("-march=%s is not compatible with the selected ABI"),
@@ -12002,53 +15566,74 @@ mips_after_parse_args (void)
      generation of code for them.  */
   if (mips_opts.mips16 == -1)
     mips_opts.mips16 = (CPU_HAS_MIPS16 (file_mips_arch)) ? 1 : 0;
+  if (mips_opts.micromips == -1)
+    mips_opts.micromips = (CPU_HAS_MICROMIPS (file_mips_arch)) ? 1 : 0;
   if (mips_opts.ase_mips3d == -1)
-    mips_opts.ase_mips3d = ((arch_info->flags & MIPS_CPU_ASE_MIPS3D)
+    mips_opts.ase_mips3d = ((arch_info->ase & ASE_MIPS3D)
                            && file_mips_fp32 == 0) ? 1 : 0;
   if (mips_opts.ase_mips3d && file_mips_fp32 == 1)
     as_bad (_("-mfp32 used with -mips3d"));
 
   if (mips_opts.ase_mdmx == -1)
-    mips_opts.ase_mdmx = ((arch_info->flags & MIPS_CPU_ASE_MDMX)
+    mips_opts.ase_mdmx = ((arch_info->ase & ASE_MDMX)
                          && file_mips_fp32 == 0) ? 1 : 0;
   if (mips_opts.ase_mdmx && file_mips_fp32 == 1)
     as_bad (_("-mfp32 used with -mdmx"));
 
   if (mips_opts.ase_smartmips == -1)
-    mips_opts.ase_smartmips = (arch_info->flags & MIPS_CPU_ASE_SMARTMIPS) ? 1 : 0;
+    mips_opts.ase_smartmips = (arch_info->ase & ASE_SMARTMIPS) ? 1 : 0;
   if (mips_opts.ase_smartmips && !ISA_SUPPORTS_SMARTMIPS)
     as_warn (_("%s ISA does not support SmartMIPS"), 
             mips_cpu_info_from_isa (mips_opts.isa)->name);
 
   if (mips_opts.ase_dsp == -1)
-    mips_opts.ase_dsp = (arch_info->flags & MIPS_CPU_ASE_DSP) ? 1 : 0;
+    mips_opts.ase_dsp = (arch_info->ase & ASE_DSP) ? 1 : 0;
   if (mips_opts.ase_dsp && !ISA_SUPPORTS_DSP_ASE)
     as_warn (_("%s ISA does not support DSP ASE"), 
             mips_cpu_info_from_isa (mips_opts.isa)->name);
 
   if (mips_opts.ase_dspr2 == -1)
     {
-      mips_opts.ase_dspr2 = (arch_info->flags & MIPS_CPU_ASE_DSPR2) ? 1 : 0;
-      mips_opts.ase_dsp = (arch_info->flags & MIPS_CPU_ASE_DSP) ? 1 : 0;
+      mips_opts.ase_dspr2 = (arch_info->ase & ASE_DSPR2) ? 1 : 0;
+      mips_opts.ase_dsp = (arch_info->ase & ASE_DSP) ? 1 : 0;
     }
   if (mips_opts.ase_dspr2 && !ISA_SUPPORTS_DSPR2_ASE)
     as_warn (_("%s ISA does not support DSP R2 ASE"),
             mips_cpu_info_from_isa (mips_opts.isa)->name);
 
+  if (mips_opts.ase_eva == -1)
+     mips_opts.ase_eva = (arch_info->ase & ASE_EVA) ? 1 : 0;
+  if (mips_opts.ase_eva && !ISA_SUPPORTS_EVA_ASE)
+    as_warn (_("%s ISA does not support EVA ASE"),
+            mips_cpu_info_from_isa (mips_opts.isa)->name);
+
   if (mips_opts.ase_mt == -1)
-    mips_opts.ase_mt = (arch_info->flags & MIPS_CPU_ASE_MT) ? 1 : 0;
+    mips_opts.ase_mt = (arch_info->ase & ASE_MT) ? 1 : 0;
   if (mips_opts.ase_mt && !ISA_SUPPORTS_MT_ASE)
     as_warn (_("%s ISA does not support MT ASE"),
             mips_cpu_info_from_isa (mips_opts.isa)->name);
 
+  if (mips_opts.ase_mcu == -1)
+    mips_opts.ase_mcu = (arch_info->ase & ASE_MCU) ? 1 : 0;
+  if (mips_opts.ase_mcu && !ISA_SUPPORTS_MCU_ASE)
+      as_warn (_("%s ISA does not support MCU ASE"),
+              mips_cpu_info_from_isa (mips_opts.isa)->name);
+
+  if (mips_opts.ase_virt == -1)
+    mips_opts.ase_virt = (arch_info->ase & ASE_VIRT) ? 1 : 0;
+  if (mips_opts.ase_virt && !ISA_SUPPORTS_VIRT_ASE)
+    as_warn (_("%s ISA does not support Virtualization ASE"),
+            mips_cpu_info_from_isa (mips_opts.isa)->name);
+
   file_mips_isa = mips_opts.isa;
-  file_ase_mips16 = mips_opts.mips16;
   file_ase_mips3d = mips_opts.ase_mips3d;
   file_ase_mdmx = mips_opts.ase_mdmx;
   file_ase_smartmips = mips_opts.ase_smartmips;
   file_ase_dsp = mips_opts.ase_dsp;
   file_ase_dspr2 = mips_opts.ase_dspr2;
+  file_ase_eva = mips_opts.ase_eva;
   file_ase_mt = mips_opts.ase_mt;
+  file_ase_virt = mips_opts.ase_virt;
   mips_opts.gp32 = file_mips_gp32;
   mips_opts.fp32 = file_mips_fp32;
   mips_opts.soft_float = file_mips_soft_float;
@@ -12079,10 +15664,21 @@ md_pcrel_from (fixS *fixP)
   valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
   switch (fixP->fx_r_type)
     {
+    case BFD_RELOC_MICROMIPS_7_PCREL_S1:
+    case BFD_RELOC_MICROMIPS_10_PCREL_S1:
+      /* Return the address of the delay slot.  */
+      return addr + 2;
+
+    case BFD_RELOC_MICROMIPS_16_PCREL_S1:
+    case BFD_RELOC_MICROMIPS_JMP:
     case BFD_RELOC_16_PCREL_S2:
     case BFD_RELOC_MIPS_JMP:
       /* Return the address of the delay slot.  */
       return addr + 4;
+
+    case BFD_RELOC_32_PCREL:
+      return addr;
+
     default:
       /* We have no relocation type for PC relative MIPS16 instructions.  */
       if (fixP->fx_addsy && S_GET_SEGMENT (fixP->fx_addsy) != now_seg)
@@ -12173,9 +15769,11 @@ mips_frob_file (void)
       gas_assert (reloc_needs_lo_p (l->fixp->fx_r_type));
 
       /* If a GOT16 relocation turns out to be against a global symbol,
-        there isn't supposed to be a matching LO.  */
+        there isn't supposed to be a matching LO.  Ignore %gots against
+        constants; we'll report an error for those later.  */
       if (got16_reloc_p (l->fixp->fx_r_type)
-         && !pic_need_relax (l->fixp->fx_addsy, l->seg))
+         && !(l->fixp->fx_addsy
+              && pic_need_relax (l->fixp->fx_addsy, l->seg)))
        continue;
 
       /* Check quickly whether the next fixup happens to be a matching %lo.  */
@@ -12233,32 +15831,53 @@ mips_frob_file (void)
     }
 }
 
-/* We may have combined relocations without symbols in the N32/N64 ABI.
-   We have to prevent gas from dropping them.  */
-
 int
 mips_force_relocation (fixS *fixp)
 {
   if (generic_force_reloc (fixp))
     return 1;
 
-  if (HAVE_NEWABI
-      && S_GET_SEGMENT (fixp->fx_addsy) == bfd_abs_section_ptr
-      && (fixp->fx_r_type == BFD_RELOC_MIPS_SUB
-         || hi16_reloc_p (fixp->fx_r_type)
-         || lo16_reloc_p (fixp->fx_r_type)))
+  /* We want to keep BFD_RELOC_MICROMIPS_*_PCREL_S1 relocation,
+     so that the linker relaxation can update targets.  */
+  if (fixp->fx_r_type == BFD_RELOC_MICROMIPS_7_PCREL_S1
+      || fixp->fx_r_type == BFD_RELOC_MICROMIPS_10_PCREL_S1
+      || fixp->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1)
     return 1;
 
   return 0;
 }
 
+/* Read the instruction associated with RELOC from BUF.  */
+
+static unsigned int
+read_reloc_insn (char *buf, bfd_reloc_code_real_type reloc)
+{
+  if (mips16_reloc_p (reloc) || micromips_reloc_p (reloc))
+    return read_compressed_insn (buf, 4);
+  else
+    return read_insn (buf);
+}
+
+/* Write instruction INSN to BUF, given that it has been relocated
+   by RELOC.  */
+
+static void
+write_reloc_insn (char *buf, bfd_reloc_code_real_type reloc,
+                 unsigned long insn)
+{
+  if (mips16_reloc_p (reloc) || micromips_reloc_p (reloc))
+    write_compressed_insn (buf, insn, 4);
+  else
+    write_insn (buf, insn);
+}
+
 /* Apply a fixup to the object file.  */
 
 void
 md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 {
-  bfd_byte *buf;
-  long insn;
+  char *buf;
+  unsigned long insn;
   reloc_howto_type *howto;
 
   /* We ignore generic BFD relocations we don't know about.  */
@@ -12266,18 +15885,24 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
   if (! howto)
     return;
 
-  gas_assert (fixP->fx_size == 4
+  gas_assert (fixP->fx_size == 2
+             || fixP->fx_size == 4
              || fixP->fx_r_type == BFD_RELOC_16
              || fixP->fx_r_type == BFD_RELOC_64
              || fixP->fx_r_type == BFD_RELOC_CTOR
              || fixP->fx_r_type == BFD_RELOC_MIPS_SUB
+             || fixP->fx_r_type == BFD_RELOC_MICROMIPS_SUB
              || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
              || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY
              || fixP->fx_r_type == BFD_RELOC_MIPS_TLS_DTPREL64);
 
-  buf = (bfd_byte *) (fixP->fx_frag->fr_literal + fixP->fx_where);
+  buf = fixP->fx_frag->fr_literal + fixP->fx_where;
 
-  gas_assert (!fixP->fx_pcrel || fixP->fx_r_type == BFD_RELOC_16_PCREL_S2);
+  gas_assert (!fixP->fx_pcrel || fixP->fx_r_type == BFD_RELOC_16_PCREL_S2
+             || fixP->fx_r_type == BFD_RELOC_MICROMIPS_7_PCREL_S1
+             || fixP->fx_r_type == BFD_RELOC_MICROMIPS_10_PCREL_S1
+             || fixP->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1
+             || fixP->fx_r_type == BFD_RELOC_32_PCREL);
 
   /* Don't treat parts of a composite relocation as done.  There are two
      reasons for this:
@@ -12301,8 +15926,30 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     case BFD_RELOC_MIPS_TLS_DTPREL_HI16:
     case BFD_RELOC_MIPS_TLS_DTPREL_LO16:
     case BFD_RELOC_MIPS_TLS_GOTTPREL:
+    case BFD_RELOC_MIPS_TLS_TPREL32:
+    case BFD_RELOC_MIPS_TLS_TPREL64:
     case BFD_RELOC_MIPS_TLS_TPREL_HI16:
     case BFD_RELOC_MIPS_TLS_TPREL_LO16:
+    case BFD_RELOC_MICROMIPS_TLS_GD:
+    case BFD_RELOC_MICROMIPS_TLS_LDM:
+    case BFD_RELOC_MICROMIPS_TLS_DTPREL_HI16:
+    case BFD_RELOC_MICROMIPS_TLS_DTPREL_LO16:
+    case BFD_RELOC_MICROMIPS_TLS_GOTTPREL:
+    case BFD_RELOC_MICROMIPS_TLS_TPREL_HI16:
+    case BFD_RELOC_MICROMIPS_TLS_TPREL_LO16:
+    case BFD_RELOC_MIPS16_TLS_GD:
+    case BFD_RELOC_MIPS16_TLS_LDM:
+    case BFD_RELOC_MIPS16_TLS_DTPREL_HI16:
+    case BFD_RELOC_MIPS16_TLS_DTPREL_LO16:
+    case BFD_RELOC_MIPS16_TLS_GOTTPREL:
+    case BFD_RELOC_MIPS16_TLS_TPREL_HI16:
+    case BFD_RELOC_MIPS16_TLS_TPREL_LO16:
+      if (!fixP->fx_addsy)
+       {
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("TLS relocation against a constant"));
+         break;
+       }
       S_SET_THREAD_LOCAL (fixP->fx_addsy);
       /* fall through */
 
@@ -12324,6 +15971,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     case BFD_RELOC_MIPS_JALR:
     case BFD_RELOC_HI16:
     case BFD_RELOC_HI16_S:
+    case BFD_RELOC_LO16:
     case BFD_RELOC_GPREL16:
     case BFD_RELOC_MIPS_LITERAL:
     case BFD_RELOC_MIPS_CALL16:
@@ -12338,8 +15986,46 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     case BFD_RELOC_MIPS16_CALL16:
     case BFD_RELOC_MIPS16_HI16:
     case BFD_RELOC_MIPS16_HI16_S:
+    case BFD_RELOC_MIPS16_LO16:
     case BFD_RELOC_MIPS16_JMP:
-      /* Nothing needed to do.  The value comes from the reloc entry.  */
+    case BFD_RELOC_MICROMIPS_JMP:
+    case BFD_RELOC_MICROMIPS_GOT_DISP:
+    case BFD_RELOC_MICROMIPS_GOT_PAGE:
+    case BFD_RELOC_MICROMIPS_GOT_OFST:
+    case BFD_RELOC_MICROMIPS_SUB:
+    case BFD_RELOC_MICROMIPS_HIGHEST:
+    case BFD_RELOC_MICROMIPS_HIGHER:
+    case BFD_RELOC_MICROMIPS_SCN_DISP:
+    case BFD_RELOC_MICROMIPS_JALR:
+    case BFD_RELOC_MICROMIPS_HI16:
+    case BFD_RELOC_MICROMIPS_HI16_S:
+    case BFD_RELOC_MICROMIPS_LO16:
+    case BFD_RELOC_MICROMIPS_GPREL16:
+    case BFD_RELOC_MICROMIPS_LITERAL:
+    case BFD_RELOC_MICROMIPS_CALL16:
+    case BFD_RELOC_MICROMIPS_GOT16:
+    case BFD_RELOC_MICROMIPS_GOT_HI16:
+    case BFD_RELOC_MICROMIPS_GOT_LO16:
+    case BFD_RELOC_MICROMIPS_CALL_HI16:
+    case BFD_RELOC_MICROMIPS_CALL_LO16:
+    case BFD_RELOC_MIPS_EH:
+      if (fixP->fx_done)
+       {
+         offsetT value;
+
+         if (calculate_reloc (fixP->fx_r_type, *valP, &value))
+           {
+             insn = read_reloc_insn (buf, fixP->fx_r_type);
+             if (mips16_reloc_p (fixP->fx_r_type))
+               insn |= mips16_immed_extend (value, 16);
+             else
+               insn |= (value & 0xffff);
+             write_reloc_insn (buf, fixP->fx_r_type, insn);
+           }
+         else
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("Unsupported constant in relocation"));
+       }
       break;
 
     case BFD_RELOC_64:
@@ -12348,7 +16034,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       if (fixP->fx_done)
        {
          if (8 <= sizeof (valueT))
-           md_number_to_chars ((char *) buf, *valP, 8);
+           md_number_to_chars (buf, *valP, 8);
          else
            {
              valueT hiv;
@@ -12357,39 +16043,21 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
                hiv = 0xffffffff;
              else
                hiv = 0;
-             md_number_to_chars ((char *)(buf + (target_big_endian ? 4 : 0)),
-                                 *valP, 4);
-             md_number_to_chars ((char *)(buf + (target_big_endian ? 0 : 4)),
-                                 hiv, 4);
+             md_number_to_chars (buf + (target_big_endian ? 4 : 0), *valP, 4);
+             md_number_to_chars (buf + (target_big_endian ? 0 : 4), hiv, 4);
            }
        }
       break;
 
     case BFD_RELOC_RVA:
     case BFD_RELOC_32:
+    case BFD_RELOC_32_PCREL:
     case BFD_RELOC_16:
       /* If we are deleting this reloc entry, we must fill in the
         value now.  This can happen if we have a .word which is not
         resolved when it appears but is later defined.  */
       if (fixP->fx_done)
-       md_number_to_chars ((char *) buf, *valP, fixP->fx_size);
-      break;
-
-    case BFD_RELOC_LO16:
-    case BFD_RELOC_MIPS16_LO16:
-      /* FIXME: Now that embedded-PIC is gone, some of this code/comment
-        may be safe to remove, but if so it's not obvious.  */
-      /* When handling an embedded PIC switch statement, we can wind
-        up deleting a LO16 reloc.  See the 'o' case in mips_ip.  */
-      if (fixP->fx_done)
-       {
-         if (*valP + 0x8000 > 0xffff)
-           as_bad_where (fixP->fx_file, fixP->fx_line,
-                         _("relocation overflow"));
-         if (target_big_endian)
-           buf += 2;
-         md_number_to_chars ((char *) buf, *valP, 2);
-       }
+       md_number_to_chars (buf, *valP, fixP->fx_size);
       break;
 
     case BFD_RELOC_16_PCREL_S2:
@@ -12404,15 +16072,12 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
        break;
 
       /* Update old instruction data.  */
-      if (target_big_endian)
-       insn = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
-      else
-       insn = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+      insn = read_insn (buf);
 
       if (*valP + 0x20000 <= 0x3ffff)
        {
          insn |= (*valP >> 2) & 0xffff;
-         md_number_to_chars ((char *) buf, insn, 4);
+         write_insn (buf, insn);
        }
       else if (mips_pic == NO_PIC
               && fixP->fx_done
@@ -12434,7 +16099,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
          fixP->fx_done = 0;
          fixP->fx_addsy = section_symbol (text_section);
          *valP += md_pcrel_from (fixP);
-         md_number_to_chars ((char *) buf, insn, 4);
+         write_insn (buf, insn);
        }
       else
        {
@@ -12446,6 +16111,20 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
        }
       break;
 
+    case BFD_RELOC_MICROMIPS_7_PCREL_S1:
+    case BFD_RELOC_MICROMIPS_10_PCREL_S1:
+    case BFD_RELOC_MICROMIPS_16_PCREL_S1:
+      /* We adjust the offset back to even.  */
+      if ((*valP & 0x1) != 0)
+       --(*valP);
+
+      if (! fixP->fx_done)
+       break;
+
+      /* Should never visit here, because we keep the relocation.  */
+      abort ();
+      break;
+
     case BFD_RELOC_VTABLE_INHERIT:
       fixP->fx_done = 0;
       if (fixP->fx_addsy
@@ -12459,7 +16138,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       break;
 
     default:
-      internalError ();
+      abort ();
     }
 
   /* Remember value for tc_gen_reloc.  */
@@ -12484,25 +16163,27 @@ get_symbol (void)
    fill byte should be used, FILL points to an integer that contains
    that byte, otherwise FILL is null.
 
-   The MIPS assembler also automatically adjusts any preceding
-   label.  */
+   This function used to have the comment:
+
+      The MIPS assembler also automatically adjusts any preceding label.
+
+   The implementation therefore applied the adjustment to a maximum of
+   one label.  However, other label adjustments are applied to batches
+   of labels, and adjusting just one caused problems when new labels
+   were added for the sake of debugging or unwind information.
+   We therefore adjust all preceding labels (given as LABELS) instead.  */
 
 static void
-mips_align (int to, int *fill, symbolS *label)
+mips_align (int to, int *fill, struct insn_label_list *labels)
 {
   mips_emit_delays ();
-  mips_record_mips16_mode ();
+  mips_record_compressed_mode ();
   if (fill == NULL && subseg_text_p (now_seg))
     frag_align_code (to, 0);
   else
     frag_align (to, fill ? *fill : 0, 0);
   record_alignment (now_seg, to);
-  if (label != NULL)
-    {
-      gas_assert (S_GET_SEGMENT (label) == now_seg);
-      symbol_set_frag (label, frag_now);
-      S_SET_VALUE (label, (valueT) frag_now_fix ());
-    }
+  mips_move_labels (labels, FALSE);
 }
 
 /* Align to a given power of two.  .align 0 turns off the automatic
@@ -12544,7 +16225,7 @@ s_align (int x ATTRIBUTE_UNUSED)
       struct insn_label_list *l = si->label_list;
       /* Auto alignment should be switched on by next section change.  */
       auto_align = 1;
-      mips_align (temp, fill_ptr, l != NULL ? l->label : NULL);
+      mips_align (temp, fill_ptr, l);
     }
   else
     {
@@ -12714,14 +16395,12 @@ s_cons (int log_size)
 {
   segment_info_type *si = seg_info (now_seg);
   struct insn_label_list *l = si->label_list;
-  symbolS *label;
 
-  label = l != NULL ? l->label : NULL;
   mips_emit_delays ();
   if (log_size > 0 && auto_align)
-    mips_align (log_size, 0, label);
-  mips_clear_insn_labels ();
+    mips_align (log_size, 0, l);
   cons (1 << log_size);
+  mips_clear_insn_labels ();
 }
 
 static void
@@ -12729,23 +16408,19 @@ s_float_cons (int type)
 {
   segment_info_type *si = seg_info (now_seg);
   struct insn_label_list *l = si->label_list;
-  symbolS *label;
-
-  label = l != NULL ? l->label : NULL;
 
   mips_emit_delays ();
 
   if (auto_align)
     {
       if (type == 'd')
-       mips_align (3, 0, label);
+       mips_align (3, 0, l);
       else
-       mips_align (2, 0, label);
+       mips_align (2, 0, l);
     }
 
-  mips_clear_insn_labels ();
-
   float_cons (type);
+  mips_clear_insn_labels ();
 }
 
 /* Handle .globl.  We need to override it because on Irix 5 you are
@@ -12831,7 +16506,7 @@ s_option (int x ATTRIBUTE_UNUSED)
        mips_pic = NO_PIC;
       else if (i == 2)
        {
-       mips_pic = SVR4_PIC;
+         mips_pic = SVR4_PIC;
          mips_abicalls = TRUE;
        }
       else
@@ -12957,10 +16632,22 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
     mips_opts.single_float = 0;
   else if (strcmp (name, "mips16") == 0
           || strcmp (name, "MIPS-16") == 0)
-    mips_opts.mips16 = 1;
+    {
+      if (mips_opts.micromips == 1)
+       as_fatal (_("`mips16' cannot be used with `micromips'"));
+      mips_opts.mips16 = 1;
+    }
   else if (strcmp (name, "nomips16") == 0
           || strcmp (name, "noMIPS-16") == 0)
     mips_opts.mips16 = 0;
+  else if (strcmp (name, "micromips") == 0)
+    {
+      if (mips_opts.mips16 == 1)
+       as_fatal (_("`micromips' cannot be used with `mips16'"));
+      mips_opts.micromips = 1;
+    }
+  else if (strcmp (name, "nomicromips") == 0)
+    mips_opts.micromips = 0;
   else if (strcmp (name, "smartmips") == 0)
     {
       if (!ISA_SUPPORTS_SMARTMIPS)
@@ -13004,6 +16691,15 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
       mips_opts.ase_dspr2 = 0;
       mips_opts.ase_dsp = 0;
     }
+  else if (strcmp (name, "eva") == 0)
+    {
+      if (!ISA_SUPPORTS_EVA_ASE)
+       as_warn (_("%s ISA does not support EVA ASE"),
+                mips_cpu_info_from_isa (mips_opts.isa)->name);
+      mips_opts.ase_eva = 1;
+    }
+  else if (strcmp (name, "noeva") == 0)
+    mips_opts.ase_eva = 0;
   else if (strcmp (name, "mt") == 0)
     {
       if (!ISA_SUPPORTS_MT_ASE)
@@ -13013,6 +16709,19 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
     }
   else if (strcmp (name, "nomt") == 0)
     mips_opts.ase_mt = 0;
+  else if (strcmp (name, "mcu") == 0)
+    mips_opts.ase_mcu = 1;
+  else if (strcmp (name, "nomcu") == 0)
+    mips_opts.ase_mcu = 0;
+  else if (strcmp (name, "virt") == 0)
+    {
+      if (!ISA_SUPPORTS_VIRT_ASE)
+       as_warn (_("%s ISA does not support Virtualization ASE"), 
+                mips_cpu_info_from_isa (mips_opts.isa)->name);
+      mips_opts.ase_virt = 1;
+    }
+  else if (strcmp (name, "novirt") == 0)
+    mips_opts.ase_virt = 0;
   else if (strncmp (name, "mips", 4) == 0 || strncmp (name, "arch=", 5) == 0)
     {
       int reset = 0;
@@ -13071,7 +16780,14 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
        case ISA_MIPS64:
        case ISA_MIPS64R2:
          mips_opts.gp32 = 0;
+         if (mips_opts.arch == CPU_R5900)
+           {
+               mips_opts.fp32 = 1;
+           }
+         else
+           {
          mips_opts.fp32 = 0;
+           }
          break;
        default:
          as_bad (_("unknown ISA level %s"), name + 4);
@@ -13187,6 +16903,13 @@ s_cpload (int ignore ATTRIBUTE_UNUSED)
       return;
     }
 
+  if (mips_opts.mips16)
+    {
+      as_bad (_("%s not supported in MIPS16 mode"), ".cpload");
+      ignore_rest_of_line ();
+      return;
+    }
+
   /* .cpload should be in a .set noreorder section.  */
   if (mips_opts.noreorder == 0)
     as_warn (_(".cpload not in noreorder section"));
@@ -13206,6 +16929,9 @@ s_cpload (int ignore ATTRIBUTE_UNUSED)
   /* In ELF, this symbol is implicitly an STT_OBJECT symbol.  */
   symbol_get_bfdsym (ex.X_add_symbol)->flags |= BSF_OBJECT;
 
+  mips_mark_labels ();
+  mips_assembling_insn = TRUE;
+
   macro_start ();
   macro_build_lui (&ex, mips_gp_register);
   macro_build (&ex, "addiu", "t,r,j", mips_gp_register,
@@ -13215,6 +16941,7 @@ s_cpload (int ignore ATTRIBUTE_UNUSED)
                 mips_gp_register, reg);
   macro_end ();
 
+  mips_assembling_insn = FALSE;
   demand_empty_rest_of_line ();
 }
 
@@ -13253,6 +16980,13 @@ s_cpsetup (int ignore ATTRIBUTE_UNUSED)
       return;
     }
 
+  if (mips_opts.mips16)
+    {
+      as_bad (_("%s not supported in MIPS16 mode"), ".cpsetup");
+      ignore_rest_of_line ();
+      return;
+    }
+
   reg1 = tc_get_register (0);
   SKIP_WHITESPACE ();
   if (*input_line_pointer != ',')
@@ -13284,6 +17018,9 @@ s_cpsetup (int ignore ATTRIBUTE_UNUSED)
   SKIP_WHITESPACE ();
   expression (&ex_sym);
 
+  mips_mark_labels ();
+  mips_assembling_insn = TRUE;
+
   macro_start ();
   if (mips_cpreturn_register == -1)
     {
@@ -13301,7 +17038,7 @@ s_cpsetup (int ignore ATTRIBUTE_UNUSED)
 
   if (mips_in_shared || HAVE_64BIT_SYMBOLS)
     {
-      macro_build (&ex_sym, "lui", "t,u", mips_gp_register,
+      macro_build (&ex_sym, "lui", LUI_FMT, mips_gp_register,
                   -1, BFD_RELOC_GPREL16, BFD_RELOC_MIPS_SUB,
                   BFD_RELOC_HI16_S);
 
@@ -13331,6 +17068,7 @@ s_cpsetup (int ignore ATTRIBUTE_UNUSED)
 
   macro_end ();
 
+  mips_assembling_insn = FALSE;
   demand_empty_rest_of_line ();
 }
 
@@ -13345,6 +17083,13 @@ s_cplocal (int ignore ATTRIBUTE_UNUSED)
       return;
     }
 
+  if (mips_opts.mips16)
+    {
+      as_bad (_("%s not supported in MIPS16 mode"), ".cplocal");
+      ignore_rest_of_line ();
+      return;
+    }
+
   mips_gp_register = tc_get_register (0);
   demand_empty_rest_of_line ();
 }
@@ -13366,6 +17111,13 @@ s_cprestore (int ignore ATTRIBUTE_UNUSED)
       return;
     }
 
+  if (mips_opts.mips16)
+    {
+      as_bad (_("%s not supported in MIPS16 mode"), ".cprestore");
+      ignore_rest_of_line ();
+      return;
+    }
+
   mips_cprestore_offset = get_absolute_expression ();
   mips_cprestore_valid = 1;
 
@@ -13374,11 +17126,15 @@ s_cprestore (int ignore ATTRIBUTE_UNUSED)
   ex.X_op_symbol = NULL;
   ex.X_add_number = mips_cprestore_offset;
 
+  mips_mark_labels ();
+  mips_assembling_insn = TRUE;
+
   macro_start ();
   macro_build_ldst_constoffset (&ex, ADDRESS_STORE_INSN, mips_gp_register,
                                SP, HAVE_64BIT_ADDRESSES);
   macro_end ();
 
+  mips_assembling_insn = FALSE;
   demand_empty_rest_of_line ();
 }
 
@@ -13402,6 +17158,16 @@ s_cpreturn (int ignore ATTRIBUTE_UNUSED)
       return;
     }
 
+  if (mips_opts.mips16)
+    {
+      as_bad (_("%s not supported in MIPS16 mode"), ".cpreturn");
+      ignore_rest_of_line ();
+      return;
+    }
+
+  mips_mark_labels ();
+  mips_assembling_insn = TRUE;
+
   macro_start ();
   if (mips_cpreturn_register == -1)
     {
@@ -13417,15 +17183,18 @@ s_cpreturn (int ignore ATTRIBUTE_UNUSED)
                 mips_cpreturn_register, 0);
   macro_end ();
 
+  mips_assembling_insn = FALSE;
   demand_empty_rest_of_line ();
 }
 
-/* Handle the .dtprelword and .dtpreldword pseudo-ops.  They generate
-   a 32-bit or 64-bit DTP-relative relocation (BYTES says which) for
-   use in DWARF debug information.  */
+/* Handle a .dtprelword, .dtpreldword, .tprelword, or .tpreldword
+   pseudo-op; DIRSTR says which. The pseudo-op generates a BYTES-size
+   DTP- or TP-relative relocation of type RTYPE, for use in either DWARF
+   debug information or MIPS16 TLS.  */
 
 static void
-s_dtprel_internal (size_t bytes)
+s_tls_rel_directive (const size_t bytes, const char *dirstr,
+                    bfd_reloc_code_real_type rtype)
 {
   expressionS ex;
   char *p;
@@ -13434,20 +17203,15 @@ s_dtprel_internal (size_t bytes)
 
   if (ex.X_op != O_symbol)
     {
-      as_bad (_("Unsupported use of %s"), (bytes == 8
-                                          ? ".dtpreldword"
-                                          : ".dtprelword"));
+      as_bad (_("Unsupported use of %s"), dirstr);
       ignore_rest_of_line ();
     }
 
   p = frag_more (bytes);
   md_number_to_chars (p, 0, bytes);
-  fix_new_exp (frag_now, p - frag_now->fr_literal, bytes, &ex, FALSE,
-              (bytes == 8
-               ? BFD_RELOC_MIPS_TLS_DTPREL64
-               : BFD_RELOC_MIPS_TLS_DTPREL32));
-
+  fix_new_exp (frag_now, p - frag_now->fr_literal, bytes, &ex, FALSE, rtype);
   demand_empty_rest_of_line ();
+  mips_clear_insn_labels ();
 }
 
 /* Handle .dtprelword.  */
@@ -13455,7 +17219,7 @@ s_dtprel_internal (size_t bytes)
 static void
 s_dtprelword (int ignore ATTRIBUTE_UNUSED)
 {
-  s_dtprel_internal (4);
+  s_tls_rel_directive (4, ".dtprelword", BFD_RELOC_MIPS_TLS_DTPREL32);
 }
 
 /* Handle .dtpreldword.  */
@@ -13463,7 +17227,23 @@ s_dtprelword (int ignore ATTRIBUTE_UNUSED)
 static void
 s_dtpreldword (int ignore ATTRIBUTE_UNUSED)
 {
-  s_dtprel_internal (8);
+  s_tls_rel_directive (8, ".dtpreldword", BFD_RELOC_MIPS_TLS_DTPREL64);
+}
+
+/* Handle .tprelword.  */
+
+static void
+s_tprelword (int ignore ATTRIBUTE_UNUSED)
+{
+  s_tls_rel_directive (4, ".tprelword", BFD_RELOC_MIPS_TLS_TPREL32);
+}
+
+/* Handle .tpreldword.  */
+
+static void
+s_tpreldword (int ignore ATTRIBUTE_UNUSED)
+{
+  s_tls_rel_directive (8, ".tpreldword", BFD_RELOC_MIPS_TLS_TPREL64);
 }
 
 /* Handle the .gpvalue pseudo-op.  This is used when generating NewABI PIC
@@ -13493,7 +17273,6 @@ s_gpword (int ignore ATTRIBUTE_UNUSED)
 {
   segment_info_type *si;
   struct insn_label_list *l;
-  symbolS *label;
   expressionS ex;
   char *p;
 
@@ -13506,13 +17285,12 @@ s_gpword (int ignore ATTRIBUTE_UNUSED)
 
   si = seg_info (now_seg);
   l = si->label_list;
-  label = l != NULL ? l->label : NULL;
   mips_emit_delays ();
   if (auto_align)
-    mips_align (2, 0, label);
-  mips_clear_insn_labels ();
+    mips_align (2, 0, l);
 
   expression (&ex);
+  mips_clear_insn_labels ();
 
   if (ex.X_op != O_symbol || ex.X_add_number != 0)
     {
@@ -13533,7 +17311,6 @@ s_gpdword (int ignore ATTRIBUTE_UNUSED)
 {
   segment_info_type *si;
   struct insn_label_list *l;
-  symbolS *label;
   expressionS ex;
   char *p;
 
@@ -13546,13 +17323,12 @@ s_gpdword (int ignore ATTRIBUTE_UNUSED)
 
   si = seg_info (now_seg);
   l = si->label_list;
-  label = l != NULL ? l->label : NULL;
   mips_emit_delays ();
   if (auto_align)
-    mips_align (3, 0, label);
-  mips_clear_insn_labels ();
+    mips_align (3, 0, l);
 
   expression (&ex);
+  mips_clear_insn_labels ();
 
   if (ex.X_op != O_symbol || ex.X_add_number != 0)
     {
@@ -13572,6 +17348,34 @@ s_gpdword (int ignore ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 
+/* Handle the .ehword pseudo-op.  This is used when generating unwinding
+   tables.  It generates a R_MIPS_EH reloc.  */
+
+static void
+s_ehword (int ignore ATTRIBUTE_UNUSED)
+{
+  expressionS ex;
+  char *p;
+
+  mips_emit_delays ();
+
+  expression (&ex);
+  mips_clear_insn_labels ();
+
+  if (ex.X_op != O_symbol || ex.X_add_number != 0)
+    {
+      as_bad (_("Unsupported use of .ehword"));
+      ignore_rest_of_line ();
+    }
+
+  p = frag_more (4);
+  md_number_to_chars (p, 0, 4);
+  fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, FALSE,
+              BFD_RELOC_MIPS_EH);
+
+  demand_empty_rest_of_line ();
+}
+
 /* Handle the .cpadd pseudo-op.  This is used when dealing with switch
    tables in SVR4 PIC code.  */
 
@@ -13587,17 +17391,21 @@ s_cpadd (int ignore ATTRIBUTE_UNUSED)
       return;
     }
 
+  mips_mark_labels ();
+  mips_assembling_insn = TRUE;
+
   /* Add $gp to the register named as an argument.  */
   macro_start ();
   reg = tc_get_register (0);
   macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", reg, reg, mips_gp_register);
   macro_end ();
 
+  mips_assembling_insn = FALSE;
   demand_empty_rest_of_line ();
 }
 
 /* Handle the .insn pseudo-op.  This marks instruction labels in
-   mips16 mode.  This permits the linker to handle them specially,
+   mips16/micromips mode.  This permits the linker to handle them specially,
    such as generating jalx instructions when needed.  We also make
    them odd for the duration of the assembly, in order to generate the
    right sort of code.  We will make them even in the adjust_symtab
@@ -13608,23 +17416,29 @@ s_cpadd (int ignore ATTRIBUTE_UNUSED)
 static void
 s_insn (int ignore ATTRIBUTE_UNUSED)
 {
-  mips16_mark_labels ();
+  mips_mark_labels ();
 
   demand_empty_rest_of_line ();
 }
 
-/* Handle a .stabn directive.  We need these in order to mark a label
-   as being a mips16 text label correctly.  Sometimes the compiler
-   will emit a label, followed by a .stabn, and then switch sections.
-   If the label and .stabn are in mips16 mode, then the label is
-   really a mips16 text label.  */
+/* 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
+   internally changes the section, so in practice we need to decide
+   now whether the preceding label marks compressed code.  We do not
+   support changing the compression mode of a label after a .stab*
+   directive, such as in:
+
+   foo:
+       .stabs ...
+       .set mips16
+
+   so the current mode wins.  */
 
 static void
 s_mips_stab (int type)
 {
-  if (type == 'n')
-    mips16_mark_labels ();
-
+  mips_mark_labels ();
   s_stab (type);
 }
 
@@ -13814,8 +17628,8 @@ pic_need_relax (symbolS *sym, asection *segtype)
   symsec = S_GET_SEGMENT (sym);
 
   /* This must duplicate the test in adjust_reloc_syms.  */
-  return (symsec != &bfd_und_section
-         && symsec != &bfd_abs_section
+  return (!bfd_is_und_section (symsec)
+         && !bfd_is_abs_section (symsec)
          && !bfd_is_com_section (symsec)
          && !s_is_linkonce (sym, segtype)
 #ifdef OBJ_ELF
@@ -14060,7 +17874,8 @@ relaxed_branch_length (fragS *fragp, asection *sec, int update)
 
   if (fragp && update && toofar != RELAX_BRANCH_TOOFAR (fragp->fr_subtype))
     fragp->fr_subtype
-      = RELAX_BRANCH_ENCODE (RELAX_BRANCH_UNCOND (fragp->fr_subtype),
+      = RELAX_BRANCH_ENCODE (RELAX_BRANCH_AT (fragp->fr_subtype),
+                            RELAX_BRANCH_UNCOND (fragp->fr_subtype),
                             RELAX_BRANCH_LIKELY (fragp->fr_subtype),
                             RELAX_BRANCH_LINK (fragp->fr_subtype),
                             toofar);
@@ -14088,6 +17903,159 @@ relaxed_branch_length (fragS *fragp, asection *sec, int update)
   return length;
 }
 
+/* Compute the length of a branch sequence, and adjust the
+   RELAX_MICROMIPS_TOOFAR32 bit accordingly.  If FRAGP is NULL, the
+   worst-case length is computed, with UPDATE being used to indicate
+   whether an unconditional (-1), or regular (0) branch is to be
+   computed.  */
+
+static int
+relaxed_micromips_32bit_branch_length (fragS *fragp, asection *sec, int update)
+{
+  bfd_boolean toofar;
+  int length;
+
+  if (fragp
+      && S_IS_DEFINED (fragp->fr_symbol)
+      && sec == S_GET_SEGMENT (fragp->fr_symbol))
+    {
+      addressT addr;
+      offsetT val;
+
+      val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
+      /* Ignore the low bit in the target, since it will be set
+        for a text label.  */
+      if ((val & 1) != 0)
+       --val;
+
+      addr = fragp->fr_address + fragp->fr_fix + 4;
+
+      val -= addr;
+
+      toofar = val < - (0x8000 << 1) || val >= (0x8000 << 1);
+    }
+  else if (fragp)
+    /* If the symbol is not defined or it's in a different segment,
+       assume the user knows what's going on and emit a short
+       branch.  */
+    toofar = FALSE;
+  else
+    toofar = TRUE;
+
+  if (fragp && update
+      && toofar != RELAX_MICROMIPS_TOOFAR32 (fragp->fr_subtype))
+    fragp->fr_subtype = (toofar
+                        ? RELAX_MICROMIPS_MARK_TOOFAR32 (fragp->fr_subtype)
+                        : RELAX_MICROMIPS_CLEAR_TOOFAR32 (fragp->fr_subtype));
+
+  length = 4;
+  if (toofar)
+    {
+      bfd_boolean compact_known = fragp != NULL;
+      bfd_boolean compact = FALSE;
+      bfd_boolean uncond;
+
+      if (compact_known)
+       compact = RELAX_MICROMIPS_COMPACT (fragp->fr_subtype);
+      if (fragp)
+       uncond = RELAX_MICROMIPS_UNCOND (fragp->fr_subtype);
+      else
+       uncond = update < 0;
+
+      /* If label is out of range, we turn branch <br>:
+
+               <br>    label                   # 4 bytes
+           0:
+
+         into:
+
+               j       label                   # 4 bytes
+               nop                             # 2 bytes if compact && !PIC
+           0:
+       */
+      if (mips_pic == NO_PIC && (!compact_known || compact))
+       length += 2;
+
+      /* If assembling PIC code, we further turn:
+
+                       j       label                   # 4 bytes
+
+         into:
+
+                       lw/ld   at, %got(label)(gp)     # 4 bytes
+                       d/addiu at, %lo(label)          # 4 bytes
+                       jr/c    at                      # 2 bytes
+       */
+      if (mips_pic != NO_PIC)
+       length += 6;
+
+      /* If branch <br> is conditional, we prepend negated branch <brneg>:
+
+                       <brneg> 0f                      # 4 bytes
+                       nop                             # 2 bytes if !compact
+       */
+      if (!uncond)
+       length += (compact_known && compact) ? 4 : 6;
+    }
+
+  return length;
+}
+
+/* Compute the length of a branch, and adjust the RELAX_MICROMIPS_TOOFAR16
+   bit accordingly.  */
+
+static int
+relaxed_micromips_16bit_branch_length (fragS *fragp, asection *sec, int update)
+{
+  bfd_boolean toofar;
+
+  if (fragp
+      && S_IS_DEFINED (fragp->fr_symbol)
+      && sec == S_GET_SEGMENT (fragp->fr_symbol))
+    {
+      addressT addr;
+      offsetT val;
+      int type;
+
+      val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
+      /* Ignore the low bit in the target, since it will be set
+        for a text label.  */
+      if ((val & 1) != 0)
+       --val;
+
+      /* Assume this is a 2-byte branch.  */
+      addr = fragp->fr_address + fragp->fr_fix + 2;
+
+      /* We try to avoid the infinite loop by not adding 2 more bytes for
+        long branches.  */
+
+      val -= addr;
+
+      type = RELAX_MICROMIPS_TYPE (fragp->fr_subtype);
+      if (type == 'D')
+       toofar = val < - (0x200 << 1) || val >= (0x200 << 1);
+      else if (type == 'E')
+       toofar = val < - (0x40 << 1) || val >= (0x40 << 1);
+      else
+       abort ();
+    }
+  else
+    /* If the symbol is not defined or it's in a different segment,
+       we emit a normal 32-bit branch.  */
+    toofar = TRUE;
+
+  if (fragp && update
+      && toofar != RELAX_MICROMIPS_TOOFAR16 (fragp->fr_subtype))
+    fragp->fr_subtype
+      = toofar ? RELAX_MICROMIPS_MARK_TOOFAR16 (fragp->fr_subtype)
+              : RELAX_MICROMIPS_CLEAR_TOOFAR16 (fragp->fr_subtype);
+
+  if (toofar)
+    return 4;
+
+  return 2;
+}
+
 /* Estimate the size of a frag before relaxing.  Unless this is the
    mips16, we are not really relaxing here, and the final size is
    encoded in the subtype information.  For the mips16, we have to
@@ -14111,6 +18079,19 @@ md_estimate_size_before_relax (fragS *fragp, asection *segtype)
        into infinite loops.  We change it only in mips_relax_frag().  */
     return (RELAX_MIPS16_EXTENDED (fragp->fr_subtype) ? 4 : 2);
 
+  if (RELAX_MICROMIPS_P (fragp->fr_subtype))
+    {
+      int length = 4;
+
+      if (RELAX_MICROMIPS_TYPE (fragp->fr_subtype) != 0)
+       length = relaxed_micromips_16bit_branch_length (fragp, segtype, FALSE);
+      if (length == 4 && RELAX_MICROMIPS_RELAX32 (fragp->fr_subtype))
+       length = relaxed_micromips_32bit_branch_length (fragp, segtype, FALSE);
+      fragp->fr_var = length;
+
+      return length;
+    }
+
   if (mips_pic == NO_PIC)
     change = nopic_need_relax (fragp->fr_symbol, 0);
   else if (mips_pic == SVR4_PIC)
@@ -14163,8 +18144,13 @@ mips_fix_adjustable (fixS *fixp)
       && (S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_MERGE) != 0)
     return 0;
 
-  /* There is no place to store an in-place offset for JALR relocations.  */
-  if (fixp->fx_r_type == BFD_RELOC_MIPS_JALR && HAVE_IN_PLACE_ADDENDS)
+  /* There is no place to store an in-place offset for JALR relocations.
+     Likewise an in-range offset of limited PC-relative relocations may
+     overflow the in-place relocatable field if recalculated against the
+     start address of the symbol's containing section.  */
+  if (HAVE_IN_PLACE_ADDENDS
+      && (limited_pcrel_reloc_p (fixp->fx_r_type)
+         || jalr_reloc_p (fixp->fx_r_type)))
     return 0;
 
 #ifdef OBJ_ELF
@@ -14194,12 +18180,14 @@ mips_fix_adjustable (fixS *fixp)
 
      There is a further restriction:
 
-       5. We cannot reduce R_MIPS16_26 relocations against MIPS16 symbols
-         on targets with in-place addends; the relocation field cannot
+       5. We cannot reduce jump relocations (R_MIPS_26, R_MIPS16_26 or
+         R_MICROMIPS_26_S1) against MIPS16 or microMIPS symbols on
+         targets with in-place addends; the relocation field cannot
          encode the low bit.
 
-     For simplicity, we deal with (3)-(5) by not reducing _any_ relocation
-     against a MIPS16 symbol.
+     For simplicity, we deal with (3)-(4) by not reducing _any_ relocation
+     against a MIPS16 symbol.  We deal with (5) by by not reducing any
+     such relocations on REL targets.
 
      We deal with (1)-(2) by saying that, if there's a R_MIPS16_26
      relocation against some symbol R, no relocation against R may be
@@ -14211,7 +18199,10 @@ mips_fix_adjustable (fixS *fixp)
   if (IS_ELF
       && fixp->fx_subsy == NULL
       && (ELF_ST_IS_MIPS16 (S_GET_OTHER (fixp->fx_addsy))
-         || *symbol_get_tc (fixp->fx_addsy)))
+         || *symbol_get_tc (fixp->fx_addsy)
+         || (HAVE_IN_PLACE_ADDENDS
+             && ELF_ST_IS_MICROMIPS (S_GET_OTHER (fixp->fx_addsy))
+             && jmp_reloc_p (fixp->fx_r_type))))
     return 0;
 #endif
 
@@ -14236,7 +18227,11 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
 
   if (fixp->fx_pcrel)
     {
-      gas_assert (fixp->fx_r_type == BFD_RELOC_16_PCREL_S2);
+      gas_assert (fixp->fx_r_type == BFD_RELOC_16_PCREL_S2
+                 || fixp->fx_r_type == BFD_RELOC_MICROMIPS_7_PCREL_S1
+                 || fixp->fx_r_type == BFD_RELOC_MICROMIPS_10_PCREL_S1
+                 || fixp->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1
+                 || fixp->fx_r_type == BFD_RELOC_32_PCREL);
 
       /* At this point, fx_addnumber is "symbol offset - pcrel address".
         Relocations want only the symbol offset.  */
@@ -14290,6 +18285,20 @@ mips_relax_frag (asection *sec, fragS *fragp, long stretch)
       return fragp->fr_var - old_var;
     }
 
+  if (RELAX_MICROMIPS_P (fragp->fr_subtype))
+    {
+      offsetT old_var = fragp->fr_var;
+      offsetT new_var = 4;
+
+      if (RELAX_MICROMIPS_TYPE (fragp->fr_subtype) != 0)
+       new_var = relaxed_micromips_16bit_branch_length (fragp, sec, TRUE);
+      if (new_var == 4 && RELAX_MICROMIPS_RELAX32 (fragp->fr_subtype))
+       new_var = relaxed_micromips_32bit_branch_length (fragp, sec, TRUE);
+      fragp->fr_var = new_var;
+
+      return new_var - old_var;
+    }
+
   if (! RELAX_MIPS16_P (fragp->fr_subtype))
     return 0;
 
@@ -14318,17 +18327,13 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
 {
   if (RELAX_BRANCH_P (fragp->fr_subtype))
     {
-      bfd_byte *buf;
+      char *buf;
       unsigned long insn;
       expressionS exp;
       fixS *fixp;
 
-      buf = (bfd_byte *)fragp->fr_literal + fragp->fr_fix;
-
-      if (target_big_endian)
-       insn = bfd_getb32 (buf);
-      else
-       insn = bfd_getl32 (buf);
+      buf = fragp->fr_literal + fragp->fr_fix;
+      insn = read_insn (buf);
 
       if (!RELAX_BRANCH_TOOFAR (fragp->fr_subtype))
        {
@@ -14339,20 +18344,19 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
          exp.X_add_symbol = fragp->fr_symbol;
          exp.X_add_number = fragp->fr_offset;
 
-         fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
-                             4, &exp, TRUE, BFD_RELOC_16_PCREL_S2);
+         fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, TRUE,
+                             BFD_RELOC_16_PCREL_S2);
          fixp->fx_file = fragp->fr_file;
          fixp->fx_line = fragp->fr_line;
 
-         md_number_to_chars ((char *) buf, insn, 4);
-         buf += 4;
+         buf = write_insn (buf, insn);
        }
       else
        {
          int i;
 
          as_warn_where (fragp->fr_file, fragp->fr_line,
-                        _("relaxed out-of-range branch into a jump"));
+                        _("Relaxed out-of-range branch into a jump"));
 
          if (RELAX_BRANCH_UNCOND (fragp->fr_subtype))
            goto uncond;
@@ -14363,10 +18367,10 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
              switch ((insn >> 28) & 0xf)
                {
                case 4:
-                 /* bc[0-3][tf]l? and bc1any[24][ft] instructions can
-                    have the condition reversed by tweaking a single
-                    bit, and their opcodes all have 0x4???????.  */
-                 gas_assert ((insn & 0xf1000000) == 0x41000000);
+                 /* bc[0-3][tf]l? instructions can have the condition
+                    reversed by tweaking a single TF bit, and their
+                    opcodes all have 0x4???????.  */
+                 gas_assert ((insn & 0xf3e00000) == 0x41000000);
                  insn ^= 0x00010000;
                  break;
 
@@ -14406,7 +18410,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
          else
            {
              /* How many bytes in instructions we've already emitted?  */
-             i = buf - (bfd_byte *)fragp->fr_literal - fragp->fr_fix;
+             i = buf - fragp->fr_literal - fragp->fr_fix;
              /* How many bytes in instructions from here to the end?  */
              i = fragp->fr_var - i;
            }
@@ -14416,12 +18420,10 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
          i--;
          insn |= i;
          /* Branch over the jump.  */
-         md_number_to_chars ((char *) buf, insn, 4);
-         buf += 4;
+         buf = write_insn (buf, insn);
 
          /* nop */
-         md_number_to_chars ((char *) buf, 0, 4);
-         buf += 4;
+         buf = write_insn (buf, 0);
 
          if (RELAX_BRANCH_LIKELY (fragp->fr_subtype))
            {
@@ -14430,20 +18432,17 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
              /* Compute the PC offset from the current instruction to
                 the end of the variable frag.  */
              /* How many bytes in instructions we've already emitted?  */
-             i = buf - (bfd_byte *)fragp->fr_literal - fragp->fr_fix;
+             i = buf - fragp->fr_literal - fragp->fr_fix;
              /* How many bytes in instructions from here to the end?  */
              i = fragp->fr_var - i;
              /* Convert to instruction count.  */
              i >>= 2;
              /* Don't decrement i, because we want to branch over the
                 delay slot.  */
-
              insn |= i;
-             md_number_to_chars ((char *) buf, insn, 4);
-             buf += 4;
 
-             md_number_to_chars ((char *) buf, 0, 4);
-             buf += 4;
+             buf = write_insn (buf, insn);
+             buf = write_insn (buf, 0);
            }
 
        uncond:
@@ -14456,18 +18455,20 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
              exp.X_add_symbol = fragp->fr_symbol;
              exp.X_add_number = fragp->fr_offset;
 
-             fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
-                                 4, &exp, FALSE, BFD_RELOC_MIPS_JMP);
+             fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp,
+                                 FALSE, BFD_RELOC_MIPS_JMP);
              fixp->fx_file = fragp->fr_file;
              fixp->fx_line = fragp->fr_line;
 
-             md_number_to_chars ((char *) buf, insn, 4);
-             buf += 4;
+             buf = write_insn (buf, insn);
            }
          else
            {
+             unsigned long at = RELAX_BRANCH_AT (fragp->fr_subtype);
+
              /* lw/ld $at, <sym>($gp)  R_MIPS_GOT16 */
-             insn = HAVE_64BIT_ADDRESSES ? 0xdf810000 : 0x8f810000;
+             insn = HAVE_64BIT_ADDRESSES ? 0xdf800000 : 0x8f800000;
+             insn |= at << OP_SH_RT;
              exp.X_op = O_symbol;
              exp.X_add_symbol = fragp->fr_symbol;
              exp.X_add_number = fragp->fr_offset;
@@ -14478,48 +18479,258 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
                  exp.X_add_number = 0;
                }
 
-             fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
-                                 4, &exp, FALSE, BFD_RELOC_MIPS_GOT16);
+             fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp,
+                                 FALSE, BFD_RELOC_MIPS_GOT16);
              fixp->fx_file = fragp->fr_file;
              fixp->fx_line = fragp->fr_line;
 
-             md_number_to_chars ((char *) buf, insn, 4);
-             buf += 4;
+             buf = write_insn (buf, insn);
 
              if (mips_opts.isa == ISA_MIPS1)
-               {
-                 /* nop */
-                 md_number_to_chars ((char *) buf, 0, 4);
-                 buf += 4;
-               }
+               /* nop */
+               buf = write_insn (buf, 0);
 
              /* d/addiu $at, $at, <sym>  R_MIPS_LO16 */
-             insn = HAVE_64BIT_ADDRESSES ? 0x64210000 : 0x24210000;
+             insn = HAVE_64BIT_ADDRESSES ? 0x64000000 : 0x24000000;
+             insn |= at << OP_SH_RS | at << OP_SH_RT;
 
-             fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
-                                 4, &exp, FALSE, BFD_RELOC_LO16);
+             fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp,
+                                 FALSE, BFD_RELOC_LO16);
              fixp->fx_file = fragp->fr_file;
              fixp->fx_line = fragp->fr_line;
 
-             md_number_to_chars ((char *) buf, insn, 4);
-             buf += 4;
+             buf = write_insn (buf, insn);
 
              /* j(al)r $at.  */
              if (RELAX_BRANCH_LINK (fragp->fr_subtype))
-               insn = 0x0020f809;
+               insn = 0x0000f809;
              else
-               insn = 0x00200008;
+               insn = 0x00000008;
+             insn |= at << OP_SH_RS;
 
-             md_number_to_chars ((char *) buf, insn, 4);
-             buf += 4;
+             buf = write_insn (buf, insn);
            }
        }
 
-      gas_assert (buf == (bfd_byte *)fragp->fr_literal
-             + fragp->fr_fix + fragp->fr_var);
+      fragp->fr_fix += fragp->fr_var;
+      gas_assert (buf == fragp->fr_literal + fragp->fr_fix);
+      return;
+    }
+
+  /* Relax microMIPS branches.  */
+  if (RELAX_MICROMIPS_P (fragp->fr_subtype))
+    {
+      char *buf = fragp->fr_literal + fragp->fr_fix;
+      bfd_boolean compact = RELAX_MICROMIPS_COMPACT (fragp->fr_subtype);
+      bfd_boolean al = RELAX_MICROMIPS_LINK (fragp->fr_subtype);
+      int type = RELAX_MICROMIPS_TYPE (fragp->fr_subtype);
+      bfd_boolean short_ds;
+      unsigned long insn;
+      expressionS exp;
+      fixS *fixp;
+
+      exp.X_op = O_symbol;
+      exp.X_add_symbol = fragp->fr_symbol;
+      exp.X_add_number = fragp->fr_offset;
 
       fragp->fr_fix += fragp->fr_var;
 
+      /* Handle 16-bit branches that fit or are forced to fit.  */
+      if (type != 0 && !RELAX_MICROMIPS_TOOFAR16 (fragp->fr_subtype))
+       {
+         /* We generate a fixup instead of applying it right now,
+            because if there is linker relaxation, we're going to
+            need the relocations.  */
+         if (type == 'D')
+           fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 2, &exp, TRUE,
+                               BFD_RELOC_MICROMIPS_10_PCREL_S1);
+         else if (type == 'E')
+           fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 2, &exp, TRUE,
+                               BFD_RELOC_MICROMIPS_7_PCREL_S1);
+         else
+           abort ();
+
+         fixp->fx_file = fragp->fr_file;
+         fixp->fx_line = fragp->fr_line;
+
+         /* These relocations can have an addend that won't fit in
+            2 octets.  */
+         fixp->fx_no_overflow = 1;
+
+         return;
+       }
+
+      /* Handle 32-bit branches that fit or are forced to fit.  */
+      if (!RELAX_MICROMIPS_RELAX32 (fragp->fr_subtype)
+         || !RELAX_MICROMIPS_TOOFAR32 (fragp->fr_subtype))
+       {
+         /* We generate a fixup instead of applying it right now,
+            because if there is linker relaxation, we're going to
+            need the relocations.  */
+         fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, TRUE,
+                             BFD_RELOC_MICROMIPS_16_PCREL_S1);
+         fixp->fx_file = fragp->fr_file;
+         fixp->fx_line = fragp->fr_line;
+
+         if (type == 0)
+           return;
+       }
+
+      /* Relax 16-bit branches to 32-bit branches.  */
+      if (type != 0)
+       {
+         insn = read_compressed_insn (buf, 2);
+
+         if ((insn & 0xfc00) == 0xcc00)                /* b16  */
+           insn = 0x94000000;                          /* beq  */
+         else if ((insn & 0xdc00) == 0x8c00)           /* beqz16/bnez16  */
+           {
+             unsigned long regno;
+
+             regno = (insn >> MICROMIPSOP_SH_MD) & MICROMIPSOP_MASK_MD;
+             regno = micromips_to_32_reg_d_map [regno];
+             insn = ((insn & 0x2000) << 16) | 0x94000000;      /* beq/bne  */
+             insn |= regno << MICROMIPSOP_SH_RS;
+           }
+         else
+           abort ();
+
+         /* Nothing else to do, just write it out.  */
+         if (!RELAX_MICROMIPS_RELAX32 (fragp->fr_subtype)
+             || !RELAX_MICROMIPS_TOOFAR32 (fragp->fr_subtype))
+           {
+             buf = write_compressed_insn (buf, insn, 4);
+             gas_assert (buf == fragp->fr_literal + fragp->fr_fix);
+             return;
+           }
+       }
+      else
+       insn = read_compressed_insn (buf, 4);
+
+      /* Relax 32-bit branches to a sequence of instructions.  */
+      as_warn_where (fragp->fr_file, fragp->fr_line,
+                    _("Relaxed out-of-range branch into a jump"));
+
+      /* Set the short-delay-slot bit.  */
+      short_ds = al && (insn & 0x02000000) != 0;
+
+      if (!RELAX_MICROMIPS_UNCOND (fragp->fr_subtype))
+       {
+         symbolS *l;
+
+         /* Reverse the branch.  */
+         if ((insn & 0xfc000000) == 0x94000000                 /* beq  */
+             || (insn & 0xfc000000) == 0xb4000000)             /* bne  */
+           insn ^= 0x20000000;
+         else if ((insn & 0xffe00000) == 0x40000000            /* bltz  */
+                  || (insn & 0xffe00000) == 0x40400000         /* bgez  */
+                  || (insn & 0xffe00000) == 0x40800000         /* blez  */
+                  || (insn & 0xffe00000) == 0x40c00000         /* bgtz  */
+                  || (insn & 0xffe00000) == 0x40a00000         /* bnezc  */
+                  || (insn & 0xffe00000) == 0x40e00000         /* beqzc  */
+                  || (insn & 0xffe00000) == 0x40200000         /* bltzal  */
+                  || (insn & 0xffe00000) == 0x40600000         /* bgezal  */
+                  || (insn & 0xffe00000) == 0x42200000         /* bltzals  */
+                  || (insn & 0xffe00000) == 0x42600000)        /* bgezals  */
+           insn ^= 0x00400000;
+         else if ((insn & 0xffe30000) == 0x43800000            /* bc1f  */
+                  || (insn & 0xffe30000) == 0x43a00000         /* bc1t  */
+                  || (insn & 0xffe30000) == 0x42800000         /* bc2f  */
+                  || (insn & 0xffe30000) == 0x42a00000)        /* bc2t  */
+           insn ^= 0x00200000;
+         else
+           abort ();
+
+         if (al)
+           {
+             /* Clear the and-link and short-delay-slot bits.  */
+             gas_assert ((insn & 0xfda00000) == 0x40200000);
+
+             /* bltzal  0x40200000     bgezal  0x40600000  */
+             /* bltzals 0x42200000     bgezals 0x42600000  */
+             insn &= ~0x02200000;
+           }
+
+         /* Make a label at the end for use with the branch.  */
+         l = symbol_new (micromips_label_name (), asec, fragp->fr_fix, fragp);
+         micromips_label_inc ();
+#if defined(OBJ_ELF) || defined(OBJ_MAYBE_ELF)
+         if (IS_ELF)
+           S_SET_OTHER (l, ELF_ST_SET_MICROMIPS (S_GET_OTHER (l)));
+#endif
+
+         /* Refer to it.  */
+         fixp = fix_new (fragp, buf - fragp->fr_literal, 4, l, 0, TRUE,
+                         BFD_RELOC_MICROMIPS_16_PCREL_S1);
+         fixp->fx_file = fragp->fr_file;
+         fixp->fx_line = fragp->fr_line;
+
+         /* Branch over the jump.  */
+         buf = write_compressed_insn (buf, insn, 4);
+         if (!compact)
+           /* nop */
+           buf = write_compressed_insn (buf, 0x0c00, 2);
+       }
+
+      if (mips_pic == NO_PIC)
+       {
+         unsigned long jal = short_ds ? 0x74000000 : 0xf4000000; /* jal/s  */
+
+         /* j/jal/jals <sym>  R_MICROMIPS_26_S1  */
+         insn = al ? jal : 0xd4000000;
+
+         fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, FALSE,
+                             BFD_RELOC_MICROMIPS_JMP);
+         fixp->fx_file = fragp->fr_file;
+         fixp->fx_line = fragp->fr_line;
+
+         buf = write_compressed_insn (buf, insn, 4);
+         if (compact)
+           /* nop */
+           buf = write_compressed_insn (buf, 0x0c00, 2);
+       }
+      else
+       {
+         unsigned long at = RELAX_MICROMIPS_AT (fragp->fr_subtype);
+         unsigned long jalr = short_ds ? 0x45e0 : 0x45c0;      /* jalr/s  */
+         unsigned long jr = compact ? 0x45a0 : 0x4580;         /* jr/c  */
+
+         /* lw/ld $at, <sym>($gp)  R_MICROMIPS_GOT16  */
+         insn = HAVE_64BIT_ADDRESSES ? 0xdc1c0000 : 0xfc1c0000;
+         insn |= at << MICROMIPSOP_SH_RT;
+
+         if (exp.X_add_number)
+           {
+             exp.X_add_symbol = make_expr_symbol (&exp);
+             exp.X_add_number = 0;
+           }
+
+         fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, FALSE,
+                             BFD_RELOC_MICROMIPS_GOT16);
+         fixp->fx_file = fragp->fr_file;
+         fixp->fx_line = fragp->fr_line;
+
+         buf = write_compressed_insn (buf, insn, 4);
+
+         /* d/addiu $at, $at, <sym>  R_MICROMIPS_LO16  */
+         insn = HAVE_64BIT_ADDRESSES ? 0x5c000000 : 0x30000000;
+         insn |= at << MICROMIPSOP_SH_RT | at << MICROMIPSOP_SH_RS;
+
+         fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, FALSE,
+                             BFD_RELOC_MICROMIPS_LO16);
+         fixp->fx_file = fragp->fr_file;
+         fixp->fx_line = fragp->fr_line;
+
+         buf = write_compressed_insn (buf, insn, 4);
+
+         /* jr/jrc/jalr/jalrs $at  */
+         insn = al ? jalr : jr;
+         insn |= at << MICROMIPSOP_SH_MJ;
+
+         buf = write_compressed_insn (buf, insn, 2);
+       }
+
+      gas_assert (buf == fragp->fr_literal + fragp->fr_fix);
       return;
     }
 
@@ -14527,29 +18738,18 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
     {
       int type;
       const struct mips16_immed_operand *op;
-      bfd_boolean small, ext;
       offsetT val;
-      bfd_byte *buf;
+      char *buf;
+      unsigned int user_length, length;
       unsigned long insn;
-      bfd_boolean use_extend;
-      unsigned short extend;
+      bfd_boolean ext;
 
       type = RELAX_MIPS16_TYPE (fragp->fr_subtype);
       op = mips16_immed_operands;
       while (op->type != type)
        ++op;
 
-      if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
-       {
-         small = FALSE;
-         ext = TRUE;
-       }
-      else
-       {
-         small = TRUE;
-         ext = FALSE;
-       }
-
+      ext = RELAX_MIPS16_EXTENDED (fragp->fr_subtype);
       val = resolve_symbol_value (fragp->fr_symbol);
       if (op->pcrel)
        {
@@ -14589,44 +18789,68 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
        as_warn_where (fragp->fr_file, fragp->fr_line,
                       _("extended instruction in delay slot"));
 
-      buf = (bfd_byte *) (fragp->fr_literal + fragp->fr_fix);
+      buf = fragp->fr_literal + fragp->fr_fix;
 
-      if (target_big_endian)
-       insn = bfd_getb16 (buf);
-      else
-       insn = bfd_getl16 (buf);
+      insn = read_compressed_insn (buf, 2);
+      if (ext)
+       insn |= MIPS16_EXTEND;
 
-      mips16_immed (fragp->fr_file, fragp->fr_line, type, val,
-                   RELAX_MIPS16_USER_EXT (fragp->fr_subtype),
-                   small, ext, &insn, &use_extend, &extend);
+      if (RELAX_MIPS16_USER_EXT (fragp->fr_subtype))
+       user_length = 4;
+      else if (RELAX_MIPS16_USER_SMALL (fragp->fr_subtype))
+       user_length = 2;
+      else
+       user_length = 0;
 
-      if (use_extend)
-       {
-         md_number_to_chars ((char *) buf, 0xf000 | extend, 2);
-         fragp->fr_fix += 2;
-         buf += 2;
-       }
+      mips16_immed (fragp->fr_file, fragp->fr_line, type,
+                   BFD_RELOC_UNUSED, val, user_length, &insn);
 
-      md_number_to_chars ((char *) buf, insn, 2);
-      fragp->fr_fix += 2;
-      buf += 2;
+      length = (ext ? 4 : 2);
+      gas_assert (mips16_opcode_length (insn) == length);
+      write_compressed_insn (buf, insn, length);
+      fragp->fr_fix += length;
     }
   else
     {
+      relax_substateT subtype = fragp->fr_subtype;
+      bfd_boolean second_longer = (subtype & RELAX_SECOND_LONGER) != 0;
+      bfd_boolean use_second = (subtype & RELAX_USE_SECOND) != 0;
       int first, second;
       fixS *fixp;
 
-      first = RELAX_FIRST (fragp->fr_subtype);
-      second = RELAX_SECOND (fragp->fr_subtype);
+      first = RELAX_FIRST (subtype);
+      second = RELAX_SECOND (subtype);
       fixp = (fixS *) fragp->fr_opcode;
 
+      /* If the delay slot chosen does not match the size of the instruction,
+         then emit a warning.  */
+      if ((!use_second && (subtype & RELAX_DELAY_SLOT_SIZE_FIRST) != 0)
+          || (use_second && (subtype & RELAX_DELAY_SLOT_SIZE_SECOND) != 0))
+       {
+         relax_substateT s;
+         const char *msg;
+
+         s = subtype & (RELAX_DELAY_SLOT_16BIT
+                        | RELAX_DELAY_SLOT_SIZE_FIRST
+                        | RELAX_DELAY_SLOT_SIZE_SECOND);
+         msg = macro_warning (s);
+         if (msg != NULL)
+           as_warn_where (fragp->fr_file, fragp->fr_line, "%s", msg);
+         subtype &= ~s;
+       }
+
       /* Possibly emit a warning if we've chosen the longer option.  */
-      if (((fragp->fr_subtype & RELAX_USE_SECOND) != 0)
-         == ((fragp->fr_subtype & RELAX_SECOND_LONGER) != 0))
+      if (use_second == second_longer)
        {
-         const char *msg = macro_warning (fragp->fr_subtype);
-         if (msg != 0)
+         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_where (fragp->fr_file, fragp->fr_line, "%s", msg);
+         subtype &= ~s;
        }
 
       /* Go through all the fixups for the first sequence.  Disable them
@@ -14636,7 +18860,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
             && fixp->fx_frag == fragp
             && fixp->fx_where < fragp->fr_fix - second)
        {
-         if (fragp->fr_subtype & RELAX_USE_SECOND)
+         if (subtype & RELAX_USE_SECOND)
            fixp->fx_done = 1;
          fixp = fixp->fx_next;
        }
@@ -14646,7 +18870,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
         addresses to account for the relaxation.  */
       while (fixp && fixp->fx_frag == fragp)
        {
-         if (fragp->fr_subtype & RELAX_USE_SECOND)
+         if (subtype & RELAX_USE_SECOND)
            fixp->fx_where -= first;
          else
            fixp->fx_done = 1;
@@ -14654,7 +18878,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
        }
 
       /* Now modify the frag contents.  */
-      if (fragp->fr_subtype & RELAX_USE_SECOND)
+      if (subtype & RELAX_USE_SECOND)
        {
          char *start;
 
@@ -14685,27 +18909,27 @@ mips_frob_file_after_relocs (void)
   syms = bfd_get_outsymbols (stdoutput);
   count = bfd_get_symcount (stdoutput);
   for (i = 0; i < count; i++, syms++)
-    {
-      if (ELF_ST_IS_MIPS16 (elf_symbol (*syms)->internal_elf_sym.st_other)
-         && ((*syms)->value & 1) != 0)
-       {
-         (*syms)->value &= ~1;
-         /* If the symbol has an odd size, it was probably computed
-            incorrectly, so adjust that as well.  */
-         if ((elf_symbol (*syms)->internal_elf_sym.st_size & 1) != 0)
-           ++elf_symbol (*syms)->internal_elf_sym.st_size;
-       }
-    }
+    if (ELF_ST_IS_COMPRESSED (elf_symbol (*syms)->internal_elf_sym.st_other)
+       && ((*syms)->value & 1) != 0)
+      {
+       (*syms)->value &= ~1;
+       /* If the symbol has an odd size, it was probably computed
+          incorrectly, so adjust that as well.  */
+       if ((elf_symbol (*syms)->internal_elf_sym.st_size & 1) != 0)
+         ++elf_symbol (*syms)->internal_elf_sym.st_size;
+      }
 }
 
 #endif
 
-/* This function is called whenever a label is defined.  It is used
-   when handling branch delays; if a branch has a label, we assume we
-   can not move it.  */
+/* This function is called whenever a label is defined, including fake
+   labels instantiated off the dot special symbol.  It is used when
+   handling branch delays; if a branch has a label, we assume we cannot
+   move it.  This also bumps the value of the symbol by 1 in compressed
+   code.  */
 
-void
-mips_define_label (symbolS *sym)
+static void
+mips_record_label (symbolS *sym)
 {
   segment_info_type *si = seg_info (now_seg);
   struct insn_label_list *l;
@@ -14721,11 +18945,30 @@ mips_define_label (symbolS *sym)
   l->label = sym;
   l->next = si->label_list;
   si->label_list = l;
+}
 
+/* This function is called as tc_frob_label() whenever a label is defined
+   and adds a DWARF-2 record we only want for true labels.  */
+
+void
+mips_define_label (symbolS *sym)
+{
+  mips_record_label (sym);
 #ifdef OBJ_ELF
   dwarf2_emit_label (sym);
 #endif
 }
+
+/* This function is called by tc_new_dot_label whenever a new dot symbol
+   is defined.  */
+
+void
+mips_add_dot_label (symbolS *sym)
+{
+  mips_record_label (sym);
+  if (mips_assembling_insn && HAVE_CODE_COMPRESSION)
+    mips_compressed_mark_label (sym);
+}
 \f
 #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
 
@@ -14773,20 +19016,18 @@ mips_elf_final_processing (void)
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_NOREORDER;
   if (mips_pic != NO_PIC)
     {
-    elf_elfheader (stdoutput)->e_flags |= EF_MIPS_PIC;
+      elf_elfheader (stdoutput)->e_flags |= EF_MIPS_PIC;
       elf_elfheader (stdoutput)->e_flags |= EF_MIPS_CPIC;
     }
   if (mips_abicalls)
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_CPIC;
 
-  /* Set MIPS ELF flags for ASEs.  */
-  /* We may need to define a new flag for DSP ASE, and set this flag when
-     file_ase_dsp is true.  */
-  /* Same for DSP R2.  */
-  /* We may need to define a new flag for MT ASE, and set this flag when
-     file_ase_mt is true.  */
+  /* Set MIPS ELF flags for ASEs.  Note that not all ASEs have flags
+     defined at present; this might need to change in future.  */
   if (file_ase_mips16)
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_M16;
+  if (file_ase_micromips)
+    elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_MICROMIPS;
 #if 0 /* XXX FIXME */
   if (file_ase_mips3d)
     elf_elfheader (stdoutput)->e_flags |= ???;
@@ -14839,21 +19080,31 @@ static procS cur_proc;
 static procS *cur_proc_ptr;
 static int numprocs;
 
-/* Implement NOP_OPCODE.  We encode a MIPS16 nop as "1" and a normal
-   nop as "0".  */
+/* Implement NOP_OPCODE.  We encode a MIPS16 nop as "1", a microMIPS nop
+   as "2", and a normal nop as "0".  */
+
+#define NOP_OPCODE_MIPS                0
+#define NOP_OPCODE_MIPS16      1
+#define NOP_OPCODE_MICROMIPS   2
 
 char
 mips_nop_opcode (void)
 {
-  return seg_info (now_seg)->tc_segment_info_data.mips16;
+  if (seg_info (now_seg)->tc_segment_info_data.micromips)
+    return NOP_OPCODE_MICROMIPS;
+  else if (seg_info (now_seg)->tc_segment_info_data.mips16)
+    return NOP_OPCODE_MIPS16;
+  else
+    return NOP_OPCODE_MIPS;
 }
 
-/* Fill in an rs_align_code fragment.  This only needs to do something
-   for MIPS16 code, where 0 is not a nop.  */
+/* Fill in an rs_align_code fragment.  Unlike elsewhere we want to use
+   32-bit microMIPS NOPs here (if applicable).  */
 
 void
 mips_handle_align (fragS *fragp)
 {
+  char nop_opcode;
   char *p;
   int bytes, size, excess;
   valueT opcode;
@@ -14862,26 +19113,51 @@ mips_handle_align (fragS *fragp)
     return;
 
   p = fragp->fr_literal + fragp->fr_fix;
-  if (*p)
+  nop_opcode = *p;
+  switch (nop_opcode)
     {
+    case NOP_OPCODE_MICROMIPS:
+      opcode = micromips_nop32_insn.insn_opcode;
+      size = 4;
+      break;
+    case NOP_OPCODE_MIPS16:
       opcode = mips16_nop_insn.insn_opcode;
       size = 2;
-    }
-  else
-    {
+      break;
+    case NOP_OPCODE_MIPS:
+    default:
       opcode = nop_insn.insn_opcode;
       size = 4;
+      break;
     }
 
   bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
   excess = bytes % size;
-  if (excess != 0)
+
+  /* Handle the leading part if we're not inserting a whole number of
+     instructions, and make it the end of the fixed part of the frag.
+     Try to fit in a short microMIPS NOP if applicable and possible,
+     and use zeroes otherwise.  */
+  gas_assert (excess < 4);
+  fragp->fr_fix += excess;
+  switch (excess)
     {
-      /* If we're not inserting a whole number of instructions,
-        pad the end of the fixed part of the frag with zeros.  */
-      memset (p, 0, excess);
-      p += excess;
-      fragp->fr_fix += excess;
+    case 3:
+      *p++ = '\0';
+      /* Fall through.  */
+    case 2:
+      if (nop_opcode == NOP_OPCODE_MICROMIPS)
+       {
+         p = write_compressed_insn (p, micromips_nop16_insn.insn_opcode, 2);
+         break;
+       }
+      *p++ = '\0';
+      /* Fall through.  */
+    case 1:
+      *p++ = '\0';
+      /* Fall through.  */
+    case 0:
+      break;
     }
 
   md_number_to_chars (p, opcode, size);
@@ -15234,151 +19510,148 @@ s_mips_mask (int reg_type)
 static const struct mips_cpu_info mips_cpu_info_table[] =
 {
   /* Entries for generic ISAs */
-  { "mips1",          MIPS_CPU_IS_ISA,         ISA_MIPS1,      CPU_R3000 },
-  { "mips2",          MIPS_CPU_IS_ISA,         ISA_MIPS2,      CPU_R6000 },
-  { "mips3",          MIPS_CPU_IS_ISA,         ISA_MIPS3,      CPU_R4000 },
-  { "mips4",          MIPS_CPU_IS_ISA,         ISA_MIPS4,      CPU_R8000 },
-  { "mips5",          MIPS_CPU_IS_ISA,         ISA_MIPS5,      CPU_MIPS5 },
-  { "mips32",         MIPS_CPU_IS_ISA,         ISA_MIPS32,     CPU_MIPS32 },
-  { "mips32r2",       MIPS_CPU_IS_ISA,         ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "mips64",         MIPS_CPU_IS_ISA,         ISA_MIPS64,     CPU_MIPS64 },
-  { "mips64r2",       MIPS_CPU_IS_ISA,         ISA_MIPS64R2,   CPU_MIPS64R2 },
+  { "mips1",          MIPS_CPU_IS_ISA, 0,      ISA_MIPS1,    CPU_R3000 },
+  { "mips2",          MIPS_CPU_IS_ISA, 0,      ISA_MIPS2,    CPU_R6000 },
+  { "mips3",          MIPS_CPU_IS_ISA, 0,      ISA_MIPS3,    CPU_R4000 },
+  { "mips4",          MIPS_CPU_IS_ISA, 0,      ISA_MIPS4,    CPU_R8000 },
+  { "mips5",          MIPS_CPU_IS_ISA, 0,      ISA_MIPS5,    CPU_MIPS5 },
+  { "mips32",         MIPS_CPU_IS_ISA, 0,      ISA_MIPS32,   CPU_MIPS32 },
+  { "mips32r2",       MIPS_CPU_IS_ISA, 0,      ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "mips64",         MIPS_CPU_IS_ISA, 0,      ISA_MIPS64,   CPU_MIPS64 },
+  { "mips64r2",       MIPS_CPU_IS_ISA, 0,      ISA_MIPS64R2, CPU_MIPS64R2 },
 
   /* MIPS I */
-  { "r3000",          0,                       ISA_MIPS1,      CPU_R3000 },
-  { "r2000",          0,                       ISA_MIPS1,      CPU_R3000 },
-  { "r3900",          0,                       ISA_MIPS1,      CPU_R3900 },
+  { "r3000",          0, 0,                    ISA_MIPS1,    CPU_R3000 },
+  { "r2000",          0, 0,                    ISA_MIPS1,    CPU_R3000 },
+  { "r3900",          0, 0,                    ISA_MIPS1,    CPU_R3900 },
 
   /* MIPS II */
-  { "r6000",          0,                       ISA_MIPS2,      CPU_R6000 },
+  { "r6000",          0, 0,                    ISA_MIPS2,    CPU_R6000 },
 
   /* MIPS III */
-  { "r4000",          0,                       ISA_MIPS3,      CPU_R4000 },
-  { "r4010",          0,                       ISA_MIPS2,      CPU_R4010 },
-  { "vr4100",         0,                       ISA_MIPS3,      CPU_VR4100 },
-  { "vr4111",         0,                       ISA_MIPS3,      CPU_R4111 },
-  { "vr4120",         0,                       ISA_MIPS3,      CPU_VR4120 },
-  { "vr4130",         0,                       ISA_MIPS3,      CPU_VR4120 },
-  { "vr4181",         0,                       ISA_MIPS3,      CPU_R4111 },
-  { "vr4300",         0,                       ISA_MIPS3,      CPU_R4300 },
-  { "r4400",          0,                       ISA_MIPS3,      CPU_R4400 },
-  { "r4600",          0,                       ISA_MIPS3,      CPU_R4600 },
-  { "orion",          0,                       ISA_MIPS3,      CPU_R4600 },
-  { "r4650",          0,                       ISA_MIPS3,      CPU_R4650 },
+  { "r4000",          0, 0,                    ISA_MIPS3,    CPU_R4000 },
+  { "r4010",          0, 0,                    ISA_MIPS2,    CPU_R4010 },
+  { "vr4100",         0, 0,                    ISA_MIPS3,    CPU_VR4100 },
+  { "vr4111",         0, 0,                    ISA_MIPS3,    CPU_R4111 },
+  { "vr4120",         0, 0,                    ISA_MIPS3,    CPU_VR4120 },
+  { "vr4130",         0, 0,                    ISA_MIPS3,    CPU_VR4120 },
+  { "vr4181",         0, 0,                    ISA_MIPS3,    CPU_R4111 },
+  { "vr4300",         0, 0,                    ISA_MIPS3,    CPU_R4300 },
+  { "r4400",          0, 0,                    ISA_MIPS3,    CPU_R4400 },
+  { "r4600",          0, 0,                    ISA_MIPS3,    CPU_R4600 },
+  { "orion",          0, 0,                    ISA_MIPS3,    CPU_R4600 },
+  { "r4650",          0, 0,                    ISA_MIPS3,    CPU_R4650 },
+  { "r5900",          0, 0,                    ISA_MIPS3,    CPU_R5900 },
   /* ST Microelectronics Loongson 2E and 2F cores */
-  { "loongson2e",     0,                       ISA_MIPS3,   CPU_LOONGSON_2E },
-  { "loongson2f",     0,                       ISA_MIPS3,   CPU_LOONGSON_2F },
+  { "loongson2e",     0, 0,                    ISA_MIPS3,    CPU_LOONGSON_2E },
+  { "loongson2f",     0, 0,                    ISA_MIPS3,    CPU_LOONGSON_2F },
 
   /* MIPS IV */
-  { "r8000",          0,                       ISA_MIPS4,      CPU_R8000 },
-  { "r10000",         0,                       ISA_MIPS4,      CPU_R10000 },
-  { "r12000",         0,                       ISA_MIPS4,      CPU_R12000 },
-  { "r14000",         0,                       ISA_MIPS4,      CPU_R14000 },
-  { "r16000",         0,                       ISA_MIPS4,      CPU_R16000 },
-  { "vr5000",         0,                       ISA_MIPS4,      CPU_R5000 },
-  { "vr5400",         0,                       ISA_MIPS4,      CPU_VR5400 },
-  { "vr5500",         0,                       ISA_MIPS4,      CPU_VR5500 },
-  { "rm5200",         0,                       ISA_MIPS4,      CPU_R5000 },
-  { "rm5230",         0,                       ISA_MIPS4,      CPU_R5000 },
-  { "rm5231",         0,                       ISA_MIPS4,      CPU_R5000 },
-  { "rm5261",         0,                       ISA_MIPS4,      CPU_R5000 },
-  { "rm5721",         0,                       ISA_MIPS4,      CPU_R5000 },
-  { "rm7000",         0,                       ISA_MIPS4,      CPU_RM7000 },
-  { "rm9000",         0,                       ISA_MIPS4,      CPU_RM9000 },
+  { "r8000",          0, 0,                    ISA_MIPS4,    CPU_R8000 },
+  { "r10000",         0, 0,                    ISA_MIPS4,    CPU_R10000 },
+  { "r12000",         0, 0,                    ISA_MIPS4,    CPU_R12000 },
+  { "r14000",         0, 0,                    ISA_MIPS4,    CPU_R14000 },
+  { "r16000",         0, 0,                    ISA_MIPS4,    CPU_R16000 },
+  { "vr5000",         0, 0,                    ISA_MIPS4,    CPU_R5000 },
+  { "vr5400",         0, 0,                    ISA_MIPS4,    CPU_VR5400 },
+  { "vr5500",         0, 0,                    ISA_MIPS4,    CPU_VR5500 },
+  { "rm5200",         0, 0,                    ISA_MIPS4,    CPU_R5000 },
+  { "rm5230",         0, 0,                    ISA_MIPS4,    CPU_R5000 },
+  { "rm5231",         0, 0,                    ISA_MIPS4,    CPU_R5000 },
+  { "rm5261",         0, 0,                    ISA_MIPS4,    CPU_R5000 },
+  { "rm5721",         0, 0,                    ISA_MIPS4,    CPU_R5000 },
+  { "rm7000",         0, 0,                    ISA_MIPS4,    CPU_RM7000 },
+  { "rm9000",         0, 0,                    ISA_MIPS4,    CPU_RM9000 },
 
   /* MIPS 32 */
-  { "4kc",            0,                       ISA_MIPS32,     CPU_MIPS32 },
-  { "4km",            0,                       ISA_MIPS32,     CPU_MIPS32 },
-  { "4kp",            0,                       ISA_MIPS32,     CPU_MIPS32 },
-  { "4ksc",           MIPS_CPU_ASE_SMARTMIPS,  ISA_MIPS32,     CPU_MIPS32 },
+  { "4kc",            0, 0,                    ISA_MIPS32,   CPU_MIPS32 },
+  { "4km",            0, 0,                    ISA_MIPS32,   CPU_MIPS32 },
+  { "4kp",            0, 0,                    ISA_MIPS32,   CPU_MIPS32 },
+  { "4ksc",           0, ASE_SMARTMIPS,                ISA_MIPS32,   CPU_MIPS32 },
 
   /* MIPS 32 Release 2 */
-  { "4kec",           0,                       ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "4kem",           0,                       ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "4kep",           0,                       ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "4ksd",           MIPS_CPU_ASE_SMARTMIPS,  ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "m4k",            0,                       ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "m4kp",           0,                       ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "24kc",           0,                       ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "24kf2_1",        0,                       ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "24kf",           0,                       ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "24kf1_1",        0,                       ISA_MIPS32R2,   CPU_MIPS32R2 },
+  { "4kec",           0, 0,                    ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "4kem",           0, 0,                    ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "4kep",           0, 0,                    ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "4ksd",           0, ASE_SMARTMIPS,                ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "m4k",            0, 0,                    ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "m4kp",           0, 0,                    ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "m14k",           0, ASE_MCU,              ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "m14kc",          0, ASE_MCU,              ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "m14ke",          0, ASE_DSP | ASE_DSPR2 | ASE_MCU,
+                                               ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "m14kec",         0, ASE_DSP | ASE_DSPR2 | ASE_MCU,
+                                               ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "24kc",           0, 0,                    ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "24kf2_1",        0, 0,                    ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "24kf",           0, 0,                    ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "24kf1_1",        0, 0,                    ISA_MIPS32R2, CPU_MIPS32R2 },
   /* Deprecated forms of the above.  */
-  { "24kfx",          0,                       ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "24kx",           0,                       ISA_MIPS32R2,   CPU_MIPS32R2 },
+  { "24kfx",          0, 0,                    ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "24kx",           0, 0,                    ISA_MIPS32R2, CPU_MIPS32R2 },
   /* 24KE is a 24K with DSP ASE, other ASEs are optional.  */
-  { "24kec",          MIPS_CPU_ASE_DSP,                ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "24kef2_1",       MIPS_CPU_ASE_DSP,                ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "24kef",          MIPS_CPU_ASE_DSP,                ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "24kef1_1",       MIPS_CPU_ASE_DSP,                ISA_MIPS32R2,   CPU_MIPS32R2 },
+  { "24kec",          0, ASE_DSP,              ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "24kef2_1",       0, ASE_DSP,              ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "24kef",          0, ASE_DSP,              ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "24kef1_1",       0, ASE_DSP,              ISA_MIPS32R2, CPU_MIPS32R2 },
   /* Deprecated forms of the above.  */
-  { "24kefx",         MIPS_CPU_ASE_DSP,                ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "24kex",          MIPS_CPU_ASE_DSP,                ISA_MIPS32R2,   CPU_MIPS32R2 },
+  { "24kefx",         0, ASE_DSP,              ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "24kex",          0, ASE_DSP,              ISA_MIPS32R2, CPU_MIPS32R2 },
   /* 34K is a 24K with DSP and MT ASE, other ASEs are optional.  */
-  { "34kc",           MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT,
-                                               ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "34kf2_1",        MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT,
-                                               ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "34kf",           MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT,
-                                               ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "34kf1_1",        MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT,
-                                               ISA_MIPS32R2,   CPU_MIPS32R2 },
+  { "34kc",           0, ASE_DSP | ASE_MT,     ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "34kf2_1",        0, ASE_DSP | ASE_MT,     ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "34kf",           0, ASE_DSP | ASE_MT,     ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "34kf1_1",        0, ASE_DSP | ASE_MT,     ISA_MIPS32R2, CPU_MIPS32R2 },
   /* Deprecated forms of the above.  */
-  { "34kfx",          MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT,
-                                               ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "34kx",           MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT,
-                                               ISA_MIPS32R2,   CPU_MIPS32R2 },
+  { "34kfx",          0, ASE_DSP | ASE_MT,     ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "34kx",           0, ASE_DSP | ASE_MT,     ISA_MIPS32R2, CPU_MIPS32R2 },
+  /* 34Kn is a 34kc without DSP.  */
+  { "34kn",           0, ASE_MT,               ISA_MIPS32R2, CPU_MIPS32R2 },
   /* 74K with DSP and DSPR2 ASE, other ASEs are optional.  */
-  { "74kc",           MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2,
-                                               ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "74kf2_1",        MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2,
-                                               ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "74kf",           MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2,
-                                               ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "74kf1_1",        MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2,
-                                               ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "74kf3_2",        MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2,
-                                               ISA_MIPS32R2,   CPU_MIPS32R2 },
+  { "74kc",           0, ASE_DSP | ASE_DSPR2,  ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "74kf2_1",        0, ASE_DSP | ASE_DSPR2,  ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "74kf",           0, ASE_DSP | ASE_DSPR2,  ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "74kf1_1",        0, ASE_DSP | ASE_DSPR2,  ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "74kf3_2",        0, ASE_DSP | ASE_DSPR2,  ISA_MIPS32R2, CPU_MIPS32R2 },
   /* Deprecated forms of the above.  */
-  { "74kfx",          MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2,
-                                               ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "74kx",           MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2,
-                                               ISA_MIPS32R2,   CPU_MIPS32R2 },
+  { "74kfx",          0, ASE_DSP | ASE_DSPR2,  ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "74kx",           0, ASE_DSP | ASE_DSPR2,  ISA_MIPS32R2, CPU_MIPS32R2 },
   /* 1004K cores are multiprocessor versions of the 34K.  */
-  { "1004kc",         MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT,
-                                               ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "1004kf2_1",      MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT,
-                                               ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "1004kf",         MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT,
-                                               ISA_MIPS32R2,   CPU_MIPS32R2 },
-  { "1004kf1_1",      MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT,
-                                               ISA_MIPS32R2,   CPU_MIPS32R2 },
+  { "1004kc",         0, ASE_DSP | ASE_MT,     ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "1004kf2_1",      0, ASE_DSP | ASE_MT,     ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "1004kf",         0, ASE_DSP | ASE_MT,     ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "1004kf1_1",      0, ASE_DSP | ASE_MT,     ISA_MIPS32R2, CPU_MIPS32R2 },
 
   /* MIPS 64 */
-  { "5kc",            0,                       ISA_MIPS64,     CPU_MIPS64 },
-  { "5kf",            0,                       ISA_MIPS64,     CPU_MIPS64 },
-  { "20kc",           MIPS_CPU_ASE_MIPS3D,     ISA_MIPS64,     CPU_MIPS64 },
-  { "25kf",           MIPS_CPU_ASE_MIPS3D,     ISA_MIPS64,     CPU_MIPS64 },
+  { "5kc",            0, 0,                    ISA_MIPS64,   CPU_MIPS64 },
+  { "5kf",            0, 0,                    ISA_MIPS64,   CPU_MIPS64 },
+  { "20kc",           0, ASE_MIPS3D,           ISA_MIPS64,   CPU_MIPS64 },
+  { "25kf",           0, ASE_MIPS3D,           ISA_MIPS64,   CPU_MIPS64 },
 
   /* Broadcom SB-1 CPU core */
-  { "sb1",            MIPS_CPU_ASE_MIPS3D | MIPS_CPU_ASE_MDMX,
-                                               ISA_MIPS64,     CPU_SB1 },
+  { "sb1",            0, ASE_MIPS3D | ASE_MDMX,        ISA_MIPS64,   CPU_SB1 },
   /* Broadcom SB-1A CPU core */
-  { "sb1a",           MIPS_CPU_ASE_MIPS3D | MIPS_CPU_ASE_MDMX,
-                                               ISA_MIPS64,     CPU_SB1 },
+  { "sb1a",           0, ASE_MIPS3D | ASE_MDMX,        ISA_MIPS64,   CPU_SB1 },
   
-  { "loongson3a",     0,                       ISA_MIPS64,     CPU_LOONGSON_3A },
+  { "loongson3a",     0, 0,                    ISA_MIPS64,   CPU_LOONGSON_3A },
 
   /* MIPS 64 Release 2 */
 
   /* Cavium Networks Octeon CPU core */
-  { "octeon",        0,      ISA_MIPS64R2,   CPU_OCTEON },
+  { "octeon",        0, 0,                     ISA_MIPS64R2, CPU_OCTEON },
+  { "octeon+",       0, 0,                     ISA_MIPS64R2, CPU_OCTEONP },
+  { "octeon2",       0, 0,                     ISA_MIPS64R2, CPU_OCTEON2 },
 
   /* RMI Xlr */
-  { "xlr",           0,      ISA_MIPS64,     CPU_XLR },
+  { "xlr",           0, 0,                     ISA_MIPS64,   CPU_XLR },
+
+  /* Broadcom XLP.
+     XLP is mostly like XLR, with the prominent exception that it is
+     MIPS64R2 rather than MIPS64.  */
+  { "xlp",           0, 0,                     ISA_MIPS64R2, CPU_XLR },
 
   /* End marker */
-  { NULL, 0, 0, 0 }
+  { NULL, 0, 0, 0, 0 }
 };
 
 
@@ -15584,6 +19857,9 @@ MIPS options:\n\
 -mips16                        generate mips16 instructions\n\
 -no-mips16             do not generate mips16 instructions\n"));
   fprintf (stream, _("\
+-mmicromips            generate microMIPS instructions\n\
+-mno-micromips         do not generate microMIPS instructions\n"));
+  fprintf (stream, _("\
 -msmartmips            generate smartmips instructions\n\
 -mno-smartmips         do not generate smartmips instructions\n"));  
   fprintf (stream, _("\
@@ -15596,6 +19872,12 @@ MIPS options:\n\
 -mmt                   generate MT instructions\n\
 -mno-mt                        do not generate MT instructions\n"));
   fprintf (stream, _("\
+-mmcu                  generate MCU instructions\n\
+-mno-mcu               do not generate MCU instructions\n"));
+  fprintf (stream, _("\
+-mvirt                 generate Virtualization instructions\n\
+-mno-virt              do not generate Virtualization instructions\n"));
+  fprintf (stream, _("\
 -mfix-loongson2f-jump  work around Loongson2F JUMP instructions\n\
 -mfix-loongson2f-nop   work around Loongson2F NOP errata\n\
 -mfix-vr4120           work around certain VR4120 errata\n\
@@ -15614,7 +19896,8 @@ MIPS options:\n\
 -msoft-float           do not allow floating-point instructions\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-]construct-floats        [dis]allow floating point values to be constructed\n\
+--[no-]relax-branch    [dis]allow out-of-range branches to be relaxed\n"
                     ));
 #ifdef OBJ_ELF
   fprintf (stream, _("\
This page took 0.252154 seconds and 4 git commands to generate.