* config/tc-mips.c (MAX_VR4130_NOPS, MAX_DELAY_NOPS): New macros.
[deliverable/binutils-gdb.git] / gas / config / tc-mips.c
index 938148c862e711b967b0ae783b8a318752055ebe..c16125bf13439796209eb0e0fd208e752ff7fff6 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
-   Free Software Foundation, Inc.
+   Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+   2003, 2004, 2005 Free Software Foundation, Inc.
    Contributed by the OSF and Ralph Campbell.
    Written by Keith Knowles and Ralph Campbell, working independently.
    Modified for ECOFF and R4000 support by Ian Lance Taylor of Cygnus
 #include "subsegs.h"
 #include "safe-ctype.h"
 
-#ifdef USE_STDARG
 #include <stdarg.h>
-#endif
-#ifdef USE_VARARGS
-#include <varargs.h>
-#endif
 
 #include "opcode/mips.h"
 #include "itbl-ops.h"
@@ -47,8 +42,8 @@
 
 #ifdef OBJ_MAYBE_ELF
 /* Clean up namespace so we can include obj-elf.h too.  */
-static int mips_output_flavor PARAMS ((void));
-static int mips_output_flavor () { return OUTPUT_FLAVOR; }
+static int mips_output_flavor (void);
+static int mips_output_flavor (void) { return OUTPUT_FLAVOR; }
 #undef OBJ_PROCESS_STAB
 #undef OUTPUT_FLAVOR
 #undef S_GET_ALIGN
@@ -79,6 +74,15 @@ static int mips_output_flavor () { return OUTPUT_FLAVOR; }
 
 int mips_flag_mdebug = -1;
 
+/* Control generation of .pdr sections.  Off by default on IRIX: the native
+   linker doesn't know about and discards them, but relocations against them
+   remain, leading to rld crashes.  */
+#ifdef TE_IRIX
+int mips_flag_pdr = FALSE;
+#else
+int mips_flag_pdr = TRUE;
+#endif
+
 #include "ecoff.h"
 
 #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
@@ -107,9 +111,7 @@ static char *mips_regmask_frag;
 extern int target_big_endian;
 
 /* The name of the readonly data section.  */
-#define RDATA_SECTION_NAME (OUTPUT_FLAVOR == bfd_target_aout_flavour \
-                           ? ".data" \
-                           : OUTPUT_FLAVOR == bfd_target_ecoff_flavour \
+#define RDATA_SECTION_NAME (OUTPUT_FLAVOR == bfd_target_ecoff_flavour \
                            ? ".rdata" \
                            : OUTPUT_FLAVOR == bfd_target_coff_flavour \
                            ? ".rdata" \
@@ -117,6 +119,43 @@ extern int target_big_endian;
                            ? ".rodata" \
                            : (abort (), ""))
 
+/* Information about an instruction, including its format, operands
+   and fixups.  */
+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.  */
+  unsigned long insn_opcode;
+
+  /* The frag that contains the instruction.  */
+  struct frag *frag;
+
+  /* The offset into FRAG of the first instruction byte.  */
+  long where;
+
+  /* The relocs associated with the instruction, if any.  */
+  fixS *fixp[3];
+
+  /* True if this entry cannot be moved from its current position.  */
+  unsigned int fixed_p : 1;
+
+  /* True if this instruction occured in a .set noreorder block.  */
+  unsigned int noreorder_p : 1;
+
+  /* True for mips16 instructions that jump to an absolute address.  */
+  unsigned int mips16_absolute_jump_p : 1;
+};
+
 /* The ABI to use.  */
 enum mips_abi_level
 {
@@ -134,6 +173,10 @@ static enum mips_abi_level mips_abi = NO_ABI;
 /* Whether or not we have code that can call pic code.  */
 int mips_abicalls = FALSE;
 
+/* Whether or not we have code which can be put into a shared
+   library.  */
+static bfd_boolean mips_in_shared = TRUE;
+
 /* This is the set of options which may be modified by the .set
    pseudo-op.  We use a struct so that .set push and .set pop are more
    reliable.  */
@@ -180,6 +223,11 @@ struct mips_set_options
      is passed but can changed if the assembler code uses .set mipsN.  */
   int gp32;
   int fp32;
+  /* MIPS architecture (CPU) type.  Changed by .set arch=FOO, the -march
+     command line option, and the default CPU.  */
+  int arch;
+  /* True if ".set sym32" is in effect.  */
+  bfd_boolean sym32;
 };
 
 /* True if -mgp32 was passed.  */
@@ -194,7 +242,7 @@ static int file_mips_fp32 = -1;
 
 static struct mips_set_options mips_opts =
 {
-  ISA_UNKNOWN, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0
+  ISA_UNKNOWN, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, CPU_UNKNOWN, FALSE
 };
 
 /* These variables are filled in with the masks of registers used.
@@ -219,33 +267,17 @@ static int file_ase_mips3d;
 static int file_ase_mdmx;
 
 /* The argument of the -march= flag.  The architecture we are assembling.  */
-static int mips_arch = CPU_UNKNOWN;
+static int file_mips_arch = CPU_UNKNOWN;
 static const char *mips_arch_string;
-static const struct mips_cpu_info *mips_arch_info;
 
 /* The argument of the -mtune= flag.  The architecture for which we
    are optimizing.  */
 static int mips_tune = CPU_UNKNOWN;
 static const char *mips_tune_string;
-static const struct mips_cpu_info *mips_tune_info;
 
 /* True when generating 32-bit code for a 64-bit processor.  */
 static int mips_32bitmode = 0;
 
-/* Some ISA's have delay slots for instructions which read or write
-   from a coprocessor (eg. mips1-mips3); some don't (eg mips4).
-   Return true if instructions marked INSN_LOAD_COPROC_DELAY,
-   INSN_COPROC_MOVE_DELAY, or INSN_WRITE_COND_CODE actually have a
-   delay slot in this ISA.  The uses of this macro assume that any
-   ISA that has delay slots for one of these, has them for all.  They
-   also assume that ISAs which don't have delays for these insns, don't
-   have delays for the INSN_LOAD_MEMORY_DELAY instructions either.  */
-#define ISA_HAS_COPROC_DELAYS(ISA) (        \
-   (ISA) == ISA_MIPS1                       \
-   || (ISA) == ISA_MIPS2                    \
-   || (ISA) == ISA_MIPS3                    \
-   )
-
 /* True if the given ABI requires 32-bit registers.  */
 #define ABI_NEEDS_32BIT_REGS(ABI) ((ABI) == O32_ABI)
 
@@ -261,18 +293,20 @@ static int mips_32bitmode = 0;
    || (ISA) == ISA_MIPS4             \
    || (ISA) == ISA_MIPS5             \
    || (ISA) == ISA_MIPS64            \
+   || (ISA) == ISA_MIPS64R2          \
    )
 
 /* Return true if ISA supports 64-bit right rotate (dror et al.)
    instructions.  */
 #define ISA_HAS_DROR(ISA) (    \
-   0                           \
+   (ISA) == ISA_MIPS64R2       \
    )
 
 /* Return true if ISA supports 32-bit right rotate (ror et al.)
    instructions.  */
 #define ISA_HAS_ROR(ISA) (     \
    (ISA) == ISA_MIPS32R2       \
+   || (ISA) == ISA_MIPS64R2    \
    )
 
 #define HAVE_32BIT_GPRS                                   \
@@ -288,17 +322,34 @@ static int mips_32bitmode = 0;
 
 #define HAVE_64BIT_OBJECTS (mips_abi == N64_ABI)
 
-/* We can only have 64bit addresses if the object file format
-   supports it.  */
-#define HAVE_32BIT_ADDRESSES                           \
-   (HAVE_32BIT_GPRS                                    \
-    || ((bfd_arch_bits_per_address (stdoutput) == 32   \
-         || ! HAVE_64BIT_OBJECTS)                      \
-        && mips_pic != EMBEDDED_PIC))
+/* True if relocations are stored in-place.  */
+#define HAVE_IN_PLACE_ADDENDS (!HAVE_NEWABI)
 
-#define HAVE_64BIT_ADDRESSES (! HAVE_32BIT_ADDRESSES)
-#define HAVE_64BIT_ADDRESS_CONSTANTS (HAVE_64BIT_ADDRESSES \
-                                     || HAVE_64BIT_GPRS)
+/* The ABI-derived address size.  */
+#define HAVE_64BIT_ADDRESSES \
+  (HAVE_64BIT_GPRS && (mips_abi == EABI_ABI || mips_abi == N64_ABI))
+#define HAVE_32BIT_ADDRESSES (!HAVE_64BIT_ADDRESSES)
+
+/* The size of symbolic constants (i.e., expressions of the form
+   "SYMBOL" or "SYMBOL + OFFSET").  */
+#define HAVE_32BIT_SYMBOLS \
+  (HAVE_32BIT_ADDRESSES || !HAVE_64BIT_OBJECTS || mips_opts.sym32)
+#define HAVE_64BIT_SYMBOLS (!HAVE_32BIT_SYMBOLS)
+
+/* Addresses are loaded in different ways, depending on the address size
+   in use.  The n32 ABI Documentation also mandates the use of additions
+   with overflow checking, but existing implementations don't follow it.  */
+#define ADDRESS_ADD_INSN                                               \
+   (HAVE_32BIT_ADDRESSES ? "addu" : "daddu")
+
+#define ADDRESS_ADDI_INSN                                              \
+   (HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu")
+
+#define ADDRESS_LOAD_INSN                                              \
+   (HAVE_32BIT_ADDRESSES ? "lw" : "ld")
+
+#define ADDRESS_STORE_INSN                                             \
+   (HAVE_32BIT_ADDRESSES ? "sw" : "sd")
 
 /* Return true if the given CPU supports the MIPS16 ASE.  */
 #define CPU_HAS_MIPS16(cpu)                                            \
@@ -319,31 +370,60 @@ static int mips_32bitmode = 0;
 /* True if CPU has a ror instruction.  */
 #define CPU_HAS_ROR(CPU)       CPU_HAS_DROR (CPU)
 
-/* Whether the processor uses hardware interlocks to protect
-   reads from the HI and LO registers, and thus does not
-   require nops to be inserted.  */
-
-#define hilo_interlocks (mips_arch == CPU_R4010                       \
-                         || mips_arch == CPU_VR5500                   \
-                         || mips_arch == CPU_SB1                      \
-                         )
+/* True if mflo and mfhi can be immediately followed by instructions
+   which write to the HI and LO registers.
+
+   According to MIPS specifications, MIPS ISAs I, II, and III need
+   (at least) two instructions between the reads of HI/LO and
+   instructions which write them, and later ISAs do not.  Contradicting
+   the MIPS specifications, some MIPS IV processor user manuals (e.g.
+   the UM for the NEC Vr5000) document needing the instructions between
+   HI/LO reads and writes, as well.  Therefore, we declare only MIPS32,
+   MIPS64 and later ISAs to have the interlocks, plus any specific
+   earlier-ISA CPUs for which CPU documentation declares that the
+   instructions are really interlocked.  */
+#define hilo_interlocks \
+  (mips_opts.isa == ISA_MIPS32                        \
+   || mips_opts.isa == ISA_MIPS32R2                   \
+   || mips_opts.isa == ISA_MIPS64                     \
+   || mips_opts.isa == ISA_MIPS64R2                   \
+   || mips_opts.arch == CPU_R4010                     \
+   || mips_opts.arch == CPU_R10000                    \
+   || mips_opts.arch == CPU_R12000                    \
+   || mips_opts.arch == CPU_RM7000                    \
+   || mips_opts.arch == CPU_VR5500                    \
+   )
 
 /* Whether the processor uses hardware interlocks to protect reads
-   from the GPRs, and thus does not require nops to be inserted.  */
+   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_arch == CPU_VR5400  \
-   || mips_arch == CPU_VR5500  \
-   || mips_arch == CPU_R3900)
-
-/* As with other "interlocks" this is used by hardware that has FP
-   (co-processor) interlocks.  */
+   || mips_opts.arch == CPU_R3900)
+
+/* Whether the processor uses hardware interlocks to avoid delays
+   required by coprocessor instructions, and thus does not require
+   nops to be inserted.  This applies to instructions marked
+   INSN_LOAD_COPROC_DELAY, INSN_COPROC_MOVE_DELAY, and to delays
+   between instructions marked INSN_WRITE_COND_CODE and ones marked
+   INSN_READ_COND_CODE.  These nops are only required at MIPS ISA
+   levels I, II, and III.  */
 /* Itbl support may require additional care here.  */
-#define cop_interlocks (mips_arch == CPU_R4300                        \
-                        || mips_arch == CPU_VR5400                    \
-                        || mips_arch == CPU_VR5500                    \
-                        || mips_arch == CPU_SB1                       \
-                       )
+#define cop_interlocks                                \
+  ((mips_opts.isa != ISA_MIPS1                        \
+    && mips_opts.isa != ISA_MIPS2                     \
+    && mips_opts.isa != ISA_MIPS3)                    \
+   || mips_opts.arch == CPU_R4300                     \
+   )
+
+/* 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)
 
 /* Is this a mfhi or mflo instruction?  */
 #define MF_HILO_INSN(PINFO) \
@@ -353,9 +433,6 @@ static int mips_32bitmode = 0;
 
 enum mips_pic_level mips_pic;
 
-/* Warn about all NOPS that the assembler generates.  */
-static int warn_nops = 0;
-
 /* 1 if we should generate 32 bit offsets from the $gp register in
    SVR4_PIC mode.  Currently has no meaning in other modes.  */
 static int mips_big_got = 0;
@@ -400,7 +477,7 @@ static int g_switch_seen = 0;
 
    I don't know if a fix is needed for the SVR4_PIC mode.  I've only
    fixed it for the non-PIC mode.  KR 95/04/07  */
-static int nopic_need_relax PARAMS ((symbolS *, int));
+static int nopic_need_relax (symbolS *, int);
 
 /* handle of the OPCODE hash table */
 static struct hash_control *op_hash = NULL;
@@ -447,7 +524,7 @@ static int auto_align = 1;
    variable.  */
 static offsetT mips_cprestore_offset = -1;
 
-/* Similiar for NewABI PIC code, where $gp is callee-saved.  NewABI has some
+/* Similar for NewABI PIC code, where $gp is callee-saved.  NewABI has some
    more optimizations, it can use a register value instead of a memory-saved
    offset and even an other register than $gp as global pointer.  */
 static offsetT mips_cpreturn_offset = -1;
@@ -480,44 +557,27 @@ static int mips_optimize = 2;
    equivalent to seeing no -g option at all.  */
 static int mips_debug = 0;
 
-/* The previous instruction.  */
-static struct mips_cl_insn prev_insn;
-
-/* The instruction before prev_insn.  */
-static struct mips_cl_insn prev_prev_insn;
-
-/* If we don't want information for prev_insn or prev_prev_insn, we
-   point the insn_mo field at this dummy integer.  */
-static const struct mips_opcode dummy_opcode = { NULL, NULL, 0, 0, 0, 0 };
+/* The maximum number of NOPs needed to avoid the VR4130 mflo/mfhi errata.  */
+#define MAX_VR4130_NOPS 4
 
-/* Non-zero if prev_insn is valid.  */
-static int prev_insn_valid;
+/* The maximum number of NOPs needed to fill delay slots.  */
+#define MAX_DELAY_NOPS 2
 
-/* The frag for the previous instruction.  */
-static struct frag *prev_insn_frag;
+/* The maximum number of NOPs needed for any purpose.  */
+#define MAX_NOPS 4
 
-/* The offset into prev_insn_frag for the previous instruction.  */
-static long prev_insn_where;
+/* A list of previous instructions, with index 0 being the most recent.
+   We need to look back MAX_NOPS instructions when filling delay slots
+   or working around processor errata.  We need to look back one
+   instruction further if we're thinking about using history[0] to
+   fill a branch delay slot.  */
+static struct mips_cl_insn history[1 + MAX_NOPS];
 
-/* The reloc type for the previous instruction, if any.  */
-static bfd_reloc_code_real_type prev_insn_reloc_type[3];
+/* Nop instructions used by emit_nop.  */
+static struct mips_cl_insn nop_insn, mips16_nop_insn;
 
-/* The reloc for the previous instruction, if any.  */
-static fixS *prev_insn_fixp[3];
-
-/* Non-zero if the previous instruction was in a delay slot.  */
-static int prev_insn_is_delay_slot;
-
-/* Non-zero if the previous instruction was in a .set noreorder.  */
-static int prev_insn_unreordered;
-
-/* Non-zero if the previous instruction uses an extend opcode (if
-   mips16).  */
-static int prev_insn_extended;
-
-/* Non-zero if the previous previous instruction was in a .set
-   noreorder.  */
-static int prev_prev_insn_unreordered;
+/* The appropriate nop for the current mode.  */
+#define NOP_INSN (mips_opts.mips16 ? &mips16_nop_insn : &nop_insn)
 
 /* If this is set, it points to a frag holding nop instructions which
    were inserted before the start of a noreorder section.  If those
@@ -584,7 +644,28 @@ static const unsigned int mips16_to_32_reg_map[] =
   16, 17, 2, 3, 4, 5, 6, 7
 };
 
-static int mips_fix_4122_bugs;
+/* Classifies the kind of instructions we're interested in when
+   implementing -mfix-vr4120.  */
+enum fix_vr4120_class {
+  FIX_VR4120_MACC,
+  FIX_VR4120_DMACC,
+  FIX_VR4120_MULT,
+  FIX_VR4120_DMULT,
+  FIX_VR4120_DIV,
+  FIX_VR4120_MTHILO,
+  NUM_FIX_VR4120_CLASSES
+};
+
+/* Given two FIX_VR4120_* values X and Y, bit Y of element X is set if
+   there must be at least one other instruction between an instruction
+   of type X and an instruction of type Y.  */
+static unsigned int vr4120_conflicts[NUM_FIX_VR4120_CLASSES];
+
+/* True if -mfix-vr4120 is in force.  */
+static int mips_fix_vr4120;
+
+/* ...likewise -mfix-vr4130.  */
+static int mips_fix_vr4130;
 
 /* We don't relax branches by default, since this causes us to expand
    `la .l2 - .l1' if there's a branch between .l1 and .l2, because we
@@ -593,81 +674,61 @@ static int mips_fix_4122_bugs;
 
 static int mips_relax_branch;
 \f
-/* Since the MIPS does not have multiple forms of PC relative
-   instructions, we do not have to do relaxing as is done on other
-   platforms.  However, we do have to handle GP relative addressing
-   correctly, which turns out to be a similar problem.
-
-   Every macro that refers to a symbol can occur in (at least) two
-   forms, one with GP relative addressing and one without.  For
-   example, loading a global variable into a register generally uses
-   a macro instruction like this:
-     lw $4,i
-   If i can be addressed off the GP register (this is true if it is in
-   the .sbss or .sdata section, or if it is known to be smaller than
-   the -G argument) this will generate the following instruction:
-     lw $4,i($gp)
-   This instruction will use a GPREL reloc.  If i can not be addressed
-   off the GP register, the following instruction sequence will be used:
-     lui $at,i
-     lw $4,i($at)
-   In this case the first instruction will have a HI16 reloc, and the
-   second reloc will have a LO16 reloc.  Both relocs will be against
-   the symbol i.
-
-   The issue here is that we may not know whether i is GP addressable
-   until after we see the instruction that uses it.  Therefore, we
-   want to be able to choose the final instruction sequence only at
-   the end of the assembly.  This is similar to the way other
-   platforms choose the size of a PC relative instruction only at the
-   end of assembly.
-
-   When generating position independent code we do not use GP
-   addressing in quite the same way, but the issue still arises as
-   external symbols and local symbols must be handled differently.
-
-   We handle these issues by actually generating both possible
-   instruction sequences.  The longer one is put in a frag_var with
-   type rs_machine_dependent.  We encode what to do with the frag in
-   the subtype field.  We encode (1) the number of existing bytes to
-   replace, (2) the number of new bytes to use, (3) the offset from
-   the start of the existing bytes to the first reloc we must generate
-   (that is, the offset is applied from the start of the existing
-   bytes after they are replaced by the new bytes, if any), (4) the
-   offset from the start of the existing bytes to the second reloc,
-   (5) whether a third reloc is needed (the third reloc is always four
-   bytes after the second reloc), and (6) whether to warn if this
-   variant is used (this is sometimes needed if .set nomacro or .set
-   noat is in effect).  All these numbers are reasonably small.
-
-   Generating two instruction sequences must be handled carefully to
-   ensure that delay slots are handled correctly.  Fortunately, there
-   are a limited number of cases.  When the second instruction
-   sequence is generated, append_insn is directed to maintain the
-   existing delay slot information, so it continues to apply to any
-   code after the second instruction sequence.  This means that the
-   second instruction sequence must not impose any requirements not
-   required by the first instruction sequence.
-
-   These variant frags are then handled in functions called by the
-   machine independent code.  md_estimate_size_before_relax returns
-   the final size of the frag.  md_convert_frag sets up the final form
-   of the frag.  tc_gen_reloc adjust the first reloc and adds a second
-   one if needed.  */
-#define RELAX_ENCODE(old, new, reloc1, reloc2, reloc3, warn) \
-  ((relax_substateT) \
-   (((old) << 23) \
-    | ((new) << 16) \
-    | (((reloc1) + 64) << 9) \
-    | (((reloc2) + 64) << 2) \
-    | ((reloc3) ? (1 << 1) : 0) \
-    | ((warn) ? 1 : 0)))
-#define RELAX_OLD(i) (((i) >> 23) & 0x7f)
-#define RELAX_NEW(i) (((i) >> 16) & 0x7f)
-#define RELAX_RELOC1(i) ((valueT) (((i) >> 9) & 0x7f) - 64)
-#define RELAX_RELOC2(i) ((valueT) (((i) >> 2) & 0x7f) - 64)
-#define RELAX_RELOC3(i) (((i) >> 1) & 1)
-#define RELAX_WARN(i) ((i) & 1)
+/* The expansion of many macros depends on the type of symbol that
+   they refer to.  For example, when generating position-dependent code,
+   a macro that refers to a symbol may have two different expansions,
+   one which uses GP-relative addresses and one which uses absolute
+   addresses.  When generating SVR4-style PIC, a macro may have
+   different expansions for local and global symbols.
+
+   We handle these situations by generating both sequences and putting
+   them in variant frags.  In position-dependent code, the first sequence
+   will be the GP-relative one and the second sequence will be the
+   absolute one.  In SVR4 PIC, the first sequence will be for global
+   symbols and the second will be for local symbols.
+
+   The frag's "subtype" is RELAX_ENCODE (FIRST, SECOND), where FIRST and
+   SECOND are the lengths of the two sequences in bytes.  These fields
+   can be extracted using RELAX_FIRST() and RELAX_SECOND().  In addition,
+   the subtype has the following flags:
+
+   RELAX_USE_SECOND
+       Set if it has been decided that we should use the second
+       sequence instead of the first.
+
+   RELAX_SECOND_LONGER
+       Set in the first variant frag if the macro's second implementation
+       is longer than its first.  This refers to the macro as a whole,
+       not an individual relaxation.
+
+   RELAX_NOMACRO
+       Set in the first variant frag if the macro appeared in a .set nomacro
+       block and if one alternative requires a warning but the other does not.
+
+   RELAX_DELAY_SLOT
+       Like RELAX_NOMACRO, but indicates that the macro appears in a branch
+       delay slot.
+
+   The frag's "opcode" points to the first fixup for relaxable code.
+
+   Relaxable macros are generated using a sequence such as:
+
+      relax_start (SYMBOL);
+      ... generate first expansion ...
+      relax_switch ();
+      ... generate second expansion ...
+      relax_end ();
+
+   The code and fixups for the unwanted alternative are discarded
+   by md_convert_frag.  */
+#define RELAX_ENCODE(FIRST, SECOND) (((FIRST) << 8) | (SECOND))
+
+#define RELAX_FIRST(X) (((X) >> 8) & 0xff)
+#define RELAX_SECOND(X) ((X) & 0xff)
+#define RELAX_USE_SECOND 0x10000
+#define RELAX_SECOND_LONGER 0x20000
+#define RELAX_NOMACRO 0x40000
+#define RELAX_DELAY_SLOT 0x80000
 
 /* Branch without likely bit.  If label is out of range, we turn:
 
@@ -799,166 +860,133 @@ static int mips_relax_branch;
   (((x) &~ (offsetT) 0x7fff) == 0                                      \
    || (((x) &~ (offsetT) 0x7fff) == ~ (offsetT) 0x7fff))
 
+/* Replace bits MASK << SHIFT of STRUCT with the equivalent bits in
+   VALUE << SHIFT.  VALUE is evaluated exactly once.  */
+#define INSERT_BITS(STRUCT, VALUE, MASK, SHIFT) \
+  (STRUCT) = (((STRUCT) & ~((MASK) << (SHIFT))) \
+             | (((VALUE) & (MASK)) << (SHIFT)))
+
+/* Extract bits MASK << SHIFT from STRUCT and shift them right
+   SHIFT places.  */
+#define EXTRACT_BITS(STRUCT, MASK, SHIFT) \
+  (((STRUCT) >> (SHIFT)) & (MASK))
+
+/* Change INSN's opcode so that the operand given by FIELD has value VALUE.
+   INSN is a mips_cl_insn structure and VALUE is evaluated exactly once.
+
+   include/opcode/mips.h specifies operand fields using the macros
+   OP_MASK_<FIELD> and OP_SH_<FIELD>.  The MIPS16 equivalents start
+   with "MIPS16OP" instead of "OP".  */
+#define INSERT_OPERAND(FIELD, INSN, VALUE) \
+  INSERT_BITS ((INSN).insn_opcode, VALUE, OP_MASK_##FIELD, OP_SH_##FIELD)
+#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 MIPS16_EXTRACT_OPERAND(FIELD, INSN) \
+  EXTRACT_BITS ((INSN).insn_opcode, \
+               MIPS16OP_MASK_##FIELD, \
+               MIPS16OP_SH_##FIELD)
+\f
+/* Global variables used when generating relaxable macros.  See the
+   comment above RELAX_ENCODE for more details about how relaxation
+   is used.  */
+static struct {
+  /* 0 if we're not emitting a relaxable macro.
+     1 if we're emitting the first of the two relaxation alternatives.
+     2 if we're emitting the second alternative.  */
+  int sequence;
+
+  /* The first relaxable fixup in the current frag.  (In other words,
+     the first fixup that refers to relaxable code.)  */
+  fixS *first_fixup;
+
+  /* sizes[0] says how many bytes of the first alternative are stored in
+     the current frag.  Likewise sizes[1] for the second alternative.  */
+  unsigned int sizes[2];
+
+  /* The symbol on which the choice of sequence depends.  */
+  symbolS *symbol;
+} mips_relax;
+\f
+/* Global variables used to decide whether a macro needs a warning.  */
+static struct {
+  /* True if the macro is in a branch delay slot.  */
+  bfd_boolean delay_slot_p;
+
+  /* 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];
+
+  /* The first variant frag for this macro.  */
+  fragS *first_frag;
+} mips_macro_warning;
 \f
 /* Prototypes for static functions.  */
 
-#ifdef __STDC__
-#define internalError() \
+#define internalError()                                                        \
     as_fatal (_("internal Error, line %d, %s"), __LINE__, __FILE__)
-#else
-#define internalError() as_fatal (_("MIPS internal Error"));
-#endif
 
 enum mips_regclass { MIPS_GR_REG, MIPS_FP_REG, MIPS16_REG };
 
-static inline bfd_boolean reloc_needs_lo_p
-  PARAMS ((bfd_reloc_code_real_type));
-static inline bfd_boolean fixup_has_matching_lo_p
-  PARAMS ((fixS *));
-static int insn_uses_reg
-  PARAMS ((struct mips_cl_insn *ip, unsigned int reg,
-          enum mips_regclass class));
-static int reg_needs_delay
-  PARAMS ((unsigned int));
-static void mips16_mark_labels
-  PARAMS ((void));
 static void append_insn
-  PARAMS ((char *place, struct mips_cl_insn * ip, expressionS * p,
-          bfd_reloc_code_real_type *r));
-static void mips_no_prev_insn
-  PARAMS ((int));
-static void mips_emit_delays
-  PARAMS ((bfd_boolean));
-#ifdef USE_STDARG
-static void macro_build
-  PARAMS ((char *place, int *counter, expressionS * ep, const char *name,
-          const char *fmt, ...));
-#else
-static void macro_build ();
-#endif
+  (struct mips_cl_insn *ip, expressionS *p, bfd_reloc_code_real_type *r);
+static void mips_no_prev_insn (void);
 static void mips16_macro_build
-  PARAMS ((char *, int *, expressionS *, const char *, const char *, va_list));
-static void macro_build_jalr
-  PARAMS ((int, expressionS *));
-static void macro_build_lui
-  PARAMS ((char *place, int *counter, expressionS * ep, int regnum));
-static void macro_build_ldst_constoffset
-  PARAMS ((char *place, int *counter, expressionS * ep, const char *op,
-          int valreg, int breg));
-static void set_at
-  PARAMS ((int *counter, int reg, int unsignedp));
-static void check_absolute_expr
-  PARAMS ((struct mips_cl_insn * ip, expressionS *));
-static void load_register
-  PARAMS ((int *, int, expressionS *, int));
-static void load_address
-  PARAMS ((int *, int, expressionS *, int *));
-static void move_register
-  PARAMS ((int *, int, int));
-static void macro
-  PARAMS ((struct mips_cl_insn * ip));
-static void mips16_macro
-  PARAMS ((struct mips_cl_insn * ip));
+  (expressionS *, const char *, const char *, va_list);
+static void load_register (int, expressionS *, int);
+static void macro_start (void);
+static void macro_end (void);
+static void macro (struct mips_cl_insn * ip);
+static void mips16_macro (struct mips_cl_insn * ip);
 #ifdef LOSING_COMPILER
-static void macro2
-  PARAMS ((struct mips_cl_insn * ip));
+static void macro2 (struct mips_cl_insn * ip);
 #endif
-static void mips_ip
-  PARAMS ((char *str, struct mips_cl_insn * ip));
-static void mips16_ip
-  PARAMS ((char *str, 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
-  PARAMS ((char *, unsigned int, int, offsetT, bfd_boolean, bfd_boolean,
-          bfd_boolean, unsigned long *, bfd_boolean *, unsigned short *));
-static bfd_boolean parse_relocation
-  PARAMS ((char **, bfd_reloc_code_real_type *));
+  (char *, unsigned int, int, offsetT, bfd_boolean, bfd_boolean, bfd_boolean,
+   unsigned long *, bfd_boolean *, unsigned short *);
 static size_t my_getSmallExpression
-  PARAMS ((expressionS *, bfd_reloc_code_real_type *, char *));
-static void my_getExpression
-  PARAMS ((expressionS *, char *));
-#ifdef OBJ_ELF
-static int support_64bit_objects
-  PARAMS((void));
-#endif
-static void mips_set_option_string
-  PARAMS ((const char **, const char *));
-static symbolS *get_symbol
-  PARAMS ((void));
-static void mips_align
-  PARAMS ((int to, int fill, symbolS *label));
-static void s_align
-  PARAMS ((int));
-static void s_change_sec
-  PARAMS ((int));
-static void s_change_section
-  PARAMS ((int));
-static void s_cons
-  PARAMS ((int));
-static void s_float_cons
-  PARAMS ((int));
-static void s_mips_globl
-  PARAMS ((int));
-static void s_option
-  PARAMS ((int));
-static void s_mipsset
-  PARAMS ((int));
-static void s_abicalls
-  PARAMS ((int));
-static void s_cpload
-  PARAMS ((int));
-static void s_cpsetup
-  PARAMS ((int));
-static void s_cplocal
-  PARAMS ((int));
-static void s_cprestore
-  PARAMS ((int));
-static void s_cpreturn
-  PARAMS ((int));
-static void s_gpvalue
-  PARAMS ((int));
-static void s_gpword
-  PARAMS ((int));
-static void s_gpdword
-  PARAMS ((int));
-static void s_cpadd
-  PARAMS ((int));
-static void s_insn
-  PARAMS ((int));
-static void md_obj_begin
-  PARAMS ((void));
-static void md_obj_end
-  PARAMS ((void));
-static long get_number
-  PARAMS ((void));
-static void s_mips_ent
-  PARAMS ((int));
-static void s_mips_end
-  PARAMS ((int));
-static void s_mips_frame
-  PARAMS ((int));
-static void s_mips_mask
-  PARAMS ((int));
-static void s_mips_stab
-  PARAMS ((int));
-static void s_mips_weakext
-  PARAMS ((int));
-static void s_mips_file
-  PARAMS ((int));
-static void s_mips_loc
-  PARAMS ((int));
-static bfd_boolean pic_need_relax
-  PARAMS ((symbolS *, asection *));
-static int mips16_extended_frag
-  PARAMS ((fragS *, asection *, long));
+  (expressionS *, bfd_reloc_code_real_type *, char *);
+static void my_getExpression (expressionS *, char *);
+static void s_align (int);
+static void s_change_sec (int);
+static void s_change_section (int);
+static void s_cons (int);
+static void s_float_cons (int);
+static void s_mips_globl (int);
+static void s_option (int);
+static void s_mipsset (int);
+static void s_abicalls (int);
+static void s_cpload (int);
+static void s_cpsetup (int);
+static void s_cplocal (int);
+static void s_cprestore (int);
+static void s_cpreturn (int);
+static void s_gpvalue (int);
+static void s_gpword (int);
+static void s_gpdword (int);
+static void s_cpadd (int);
+static void s_insn (int);
+static void md_obj_begin (void);
+static void md_obj_end (void);
+static void s_mips_ent (int);
+static void s_mips_end (int);
+static void s_mips_frame (int);
+static void s_mips_mask (int reg_type);
+static void s_mips_stab (int);
+static void s_mips_weakext (int);
+static void s_mips_file (int);
+static void s_mips_loc (int);
+static bfd_boolean pic_need_relax (symbolS *, asection *);
 static int relaxed_branch_length (fragS *, asection *, int);
-static int validate_mips_insn
-  PARAMS ((const struct mips_opcode *));
-static void show
-  PARAMS ((FILE *, const char *, int *, int *));
-#ifdef OBJ_ELF
-static int mips_need_elf_addend_fixup
-  PARAMS ((fixS *));
-#endif
+static int validate_mips_insn (const struct mips_opcode *);
 
 /* Table and functions used to map between CPU/ISA names, and
    ISA levels, and CPU numbers.  */
@@ -971,18 +999,9 @@ struct mips_cpu_info
   int cpu;                    /* CPU number (default CPU if ISA).  */
 };
 
-static void mips_set_architecture
-  PARAMS ((const struct mips_cpu_info *));
-static void mips_set_tune
-  PARAMS ((const struct mips_cpu_info *));
-static bfd_boolean mips_strict_matching_cpu_name_p
-  PARAMS ((const char *, const char *));
-static bfd_boolean mips_matching_cpu_name_p
-  PARAMS ((const char *, const char *));
-static const struct mips_cpu_info *mips_parse_cpu
-  PARAMS ((const char *, const char *));
-static const struct mips_cpu_info *mips_cpu_info_from_isa
-  PARAMS ((int));
+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);
+static const struct mips_cpu_info *mips_cpu_info_from_arch (int);
 \f
 /* Pseudo-op table.
 
@@ -1075,10 +1094,10 @@ static const pseudo_typeS mips_nonecoff_pseudo_table[] =
   { NULL, NULL, 0 },
 };
 
-extern void pop_insert PARAMS ((const pseudo_typeS *));
+extern void pop_insert (const pseudo_typeS *);
 
 void
-mips_pop_insert ()
+mips_pop_insert (void)
 {
   pop_insert (mips_pseudo_table);
   if (! ECOFF_DEBUGGING)
@@ -1096,10 +1115,10 @@ struct insn_label_list
 static struct insn_label_list *insn_labels;
 static struct insn_label_list *free_insn_labels;
 
-static void mips_clear_insn_labels PARAMS ((void));
+static void mips_clear_insn_labels (void);
 
 static inline void
-mips_clear_insn_labels ()
+mips_clear_insn_labels (void)
 {
   register struct insn_label_list **pl;
 
@@ -1115,6 +1134,7 @@ static char *expr_end;
    mips_ip.  */
 
 static expressionS imm_expr;
+static expressionS imm2_expr;
 static expressionS offset_expr;
 
 /* Relocs associated with imm_expr and offset_expr.  */
@@ -1138,12 +1158,10 @@ static segT pdr_seg;
 /* The default target format to use.  */
 
 const char *
-mips_target_format ()
+mips_target_format (void)
 {
   switch (OUTPUT_FLAVOR)
     {
-    case bfd_target_aout_flavour:
-      return target_big_endian ? "a.out-mips-big" : "a.out-mips-little";
     case bfd_target_ecoff_flavour:
       return target_big_endian ? "ecoff-bigmips" : ECOFF_LITTLE_FORMAT;
     case bfd_target_coff_flavour:
@@ -1177,17 +1195,185 @@ mips_target_format ()
     }
 }
 
+/* Return the length of instruction INSN.  */
+
+static inline unsigned int
+insn_length (const struct mips_cl_insn *insn)
+{
+  if (!mips_opts.mips16)
+    return 4;
+  return insn->mips16_absolute_jump_p || insn->use_extend ? 4 : 2;
+}
+
+/* Initialise INSN from opcode entry MO.  Leave its position unspecified.  */
+
+static void
+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;
+  for (i = 0; i < ARRAY_SIZE (insn->fixp); i++)
+    insn->fixp[i] = NULL;
+  insn->fixed_p = (mips_opts.noreorder > 0);
+  insn->noreorder_p = (mips_opts.noreorder > 0);
+  insn->mips16_absolute_jump_p = 0;
+}
+
+/* Install INSN at the location specified by its "frag" and "where" fields.  */
+
+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);
+    }
+  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);
+    }
+}
+
+/* Move INSN to offset WHERE in FRAG.  Adjust the fixups accordingly
+   and install the opcode in the new location.  */
+
+static void
+move_insn (struct mips_cl_insn *insn, fragS *frag, long where)
+{
+  size_t i;
+
+  insn->frag = frag;
+  insn->where = where;
+  for (i = 0; i < ARRAY_SIZE (insn->fixp); i++)
+    if (insn->fixp[i] != NULL)
+      {
+       insn->fixp[i]->fx_frag = frag;
+       insn->fixp[i]->fx_where = where;
+      }
+  install_insn (insn);
+}
+
+/* Add INSN to the end of the output.  */
+
+static void
+add_fixed_insn (struct mips_cl_insn *insn)
+{
+  char *f = frag_more (insn_length (insn));
+  move_insn (insn, frag_now, f - frag_now->fr_literal);
+}
+
+/* Start a variant frag and move INSN to the start of the variant part,
+   marking it as fixed.  The other arguments are as for frag_var.  */
+
+static void
+add_relaxed_insn (struct mips_cl_insn *insn, int max_chars, int var,
+                 relax_substateT subtype, symbolS *symbol, offsetT offset)
+{
+  frag_grow (max_chars);
+  move_insn (insn, frag_now, frag_more (0) - frag_now->fr_literal);
+  insn->fixed_p = 1;
+  frag_var (rs_machine_dependent, max_chars, var,
+           subtype, symbol, offset, NULL);
+}
+
+/* Insert N copies of INSN into the history buffer, starting at
+   position FIRST.  Neither FIRST nor N need to be clipped.  */
+
+static void
+insert_into_history (unsigned int first, unsigned int n,
+                    const struct mips_cl_insn *insn)
+{
+  if (mips_relax.sequence != 2)
+    {
+      unsigned int i;
+
+      for (i = ARRAY_SIZE (history); i-- > first;)
+       if (i >= first + n)
+         history[i] = history[i - n];
+       else
+         history[i] = *insn;
+    }
+}
+
+/* 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.  */
+
+static void
+init_vr4120_conflicts (void)
+{
+#define CONFLICT(FIRST, SECOND) \
+    vr4120_conflicts[FIX_VR4120_##FIRST] |= 1 << FIX_VR4120_##SECOND
+
+  /* Errata 21 - [D]DIV[U] after [D]MACC */
+  CONFLICT (MACC, DIV);
+  CONFLICT (DMACC, DIV);
+
+  /* Errata 23 - Continuous DMULT[U]/DMACC instructions.  */
+  CONFLICT (DMULT, DMULT);
+  CONFLICT (DMULT, DMACC);
+  CONFLICT (DMACC, DMULT);
+  CONFLICT (DMACC, DMACC);
+
+  /* Errata 24 - MT{LO,HI} after [D]MACC */
+  CONFLICT (MACC, MTHILO);
+  CONFLICT (DMACC, MTHILO);
+
+  /* VR4181A errata MD(1): "If a MULT, MULTU, DMULT or DMULTU
+     instruction is executed immediately after a MACC or DMACC
+     instruction, the result of [either instruction] is incorrect."  */
+  CONFLICT (MACC, MULT);
+  CONFLICT (MACC, DMULT);
+  CONFLICT (DMACC, MULT);
+  CONFLICT (DMACC, DMULT);
+
+  /* VR4181A errata MD(4): "If a MACC or DMACC instruction is
+     executed immediately after a DMULT, DMULTU, DIV, DIVU,
+     DDIV or DDIVU instruction, the result of the MACC or
+     DMACC instruction is incorrect.".  */
+  CONFLICT (DMULT, MACC);
+  CONFLICT (DMULT, DMACC);
+  CONFLICT (DIV, MACC);
+  CONFLICT (DIV, DMACC);
+
+#undef CONFLICT
+}
+
 /* This function is called once, at assembler startup time.  It should
    set up all the tables, etc. that the MD part of the assembler will need.  */
 
 void
-md_begin ()
+md_begin (void)
 {
   register const char *retval = NULL;
   int i = 0;
   int broken = 0;
 
-  if (! bfd_set_arch_mach (stdoutput, bfd_arch_mips, mips_arch))
+  if (! bfd_set_arch_mach (stdoutput, bfd_arch_mips, file_mips_arch))
     as_warn (_("Could not set architecture and machine"));
 
   op_hash = hash_new ();
@@ -1196,7 +1382,7 @@ md_begin ()
     {
       const char *name = mips_opcodes[i].name;
 
-      retval = hash_insert (op_hash, name, (PTR) &mips_opcodes[i]);
+      retval = hash_insert (op_hash, name, (void *) &mips_opcodes[i]);
       if (retval != NULL)
        {
          fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
@@ -1210,6 +1396,11 @@ md_begin ()
            {
              if (!validate_mips_insn (&mips_opcodes[i]))
                broken = 1;
+             if (nop_insn.insn_mo == NULL && strcmp (name, "nop") == 0)
+               {
+                 create_insn (&nop_insn, mips_opcodes + i);
+                 nop_insn.fixed_p = 1;
+               }
            }
          ++i;
        }
@@ -1223,7 +1414,7 @@ md_begin ()
     {
       const char *name = mips16_opcodes[i].name;
 
-      retval = hash_insert (mips16_op_hash, name, (PTR) &mips16_opcodes[i]);
+      retval = hash_insert (mips16_op_hash, name, (void *) &mips16_opcodes[i]);
       if (retval != NULL)
        as_fatal (_("internal: can't hash `%s': %s"),
                  mips16_opcodes[i].name, retval);
@@ -1237,6 +1428,11 @@ md_begin ()
                       mips16_opcodes[i].name, mips16_opcodes[i].args);
              broken = 1;
            }
+         if (mips16_nop_insn.insn_mo == NULL && strcmp (name, "nop") == 0)
+           {
+             create_insn (&mips16_nop_insn, mips16_opcodes + i);
+             mips16_nop_insn.fixed_p = 1;
+           }
          ++i;
        }
       while (i < bfd_mips16_num_opcodes
@@ -1289,7 +1485,7 @@ md_begin ()
                                       &zero_address_frag));
     }
 
-  mips_no_prev_insn (FALSE);
+  mips_no_prev_insn ();
 
   mips_gprmask = 0;
   mips_cprmask[0] = 0;
@@ -1300,8 +1496,7 @@ md_begin ()
   /* set the default alignment for the text section (2**2) */
   record_alignment (text_section, 2);
 
-  if (USE_GLOBAL_POINTER_OPT)
-    bfd_set_gp_size (stdoutput, g_switch_value);
+  bfd_set_gp_size (stdoutput, g_switch_value);
 
   if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
     {
@@ -1380,7 +1575,7 @@ md_begin ()
            (void) bfd_set_section_alignment (stdoutput, sec, 2);
          }
 #ifdef OBJ_ELF
-       else if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+       else if (OUTPUT_FLAVOR == bfd_target_elf_flavour && mips_flag_pdr)
          {
            pdr_seg = subseg_new (".pdr", (subsegT) 0);
            (void) bfd_set_section_flags (stdoutput, pdr_seg,
@@ -1396,24 +1591,27 @@ md_begin ()
 
   if (! ECOFF_DEBUGGING)
     md_obj_begin ();
+
+  if (mips_fix_vr4120)
+    init_vr4120_conflicts ();
 }
 
 void
-md_mips_end ()
+md_mips_end (void)
 {
   if (! ECOFF_DEBUGGING)
     md_obj_end ();
 }
 
 void
-md_assemble (str)
-     char *str;
+md_assemble (char *str)
 {
   struct mips_cl_insn insn;
   bfd_reloc_code_real_type unused_reloc[3]
     = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
 
   imm_expr.X_op = O_absent;
+  imm2_expr.X_op = O_absent;
   offset_expr.X_op = O_absent;
   imm_reloc[0] = BFD_RELOC_UNUSED;
   imm_reloc[1] = BFD_RELOC_UNUSED;
@@ -1439,19 +1637,21 @@ md_assemble (str)
 
   if (insn.insn_mo->pinfo == INSN_MACRO)
     {
+      macro_start ();
       if (mips_opts.mips16)
        mips16_macro (&insn);
       else
        macro (&insn);
+      macro_end ();
     }
   else
     {
       if (imm_expr.X_op != O_absent)
-       append_insn (NULL, &insn, &imm_expr, imm_reloc);
+       append_insn (&insn, &imm_expr, imm_reloc);
       else if (offset_expr.X_op != O_absent)
-       append_insn (NULL, &insn, &offset_expr, offset_reloc);
+       append_insn (&insn, &offset_expr, offset_reloc);
       else
-       append_insn (NULL, &insn, NULL, unused_reloc);
+       append_insn (&insn, NULL, unused_reloc);
     }
 }
 
@@ -1460,22 +1660,23 @@ md_assemble (str)
    applied to local symbols.  */
 
 static inline bfd_boolean
-reloc_needs_lo_p (reloc)
-     bfd_reloc_code_real_type reloc;
+reloc_needs_lo_p (bfd_reloc_code_real_type reloc)
 {
-  return (reloc == BFD_RELOC_HI16_S
-         || reloc == BFD_RELOC_MIPS_GOT16);
+  return (HAVE_IN_PLACE_ADDENDS
+         && (reloc == BFD_RELOC_HI16_S
+             || reloc == BFD_RELOC_MIPS_GOT16
+             || reloc == BFD_RELOC_MIPS16_HI16_S));
 }
 
 /* Return true if the given fixup is followed by a matching R_MIPS_LO16
    relocation.  */
 
 static inline bfd_boolean
-fixup_has_matching_lo_p (fixp)
-     fixS *fixp;
+fixup_has_matching_lo_p (fixS *fixp)
 {
   return (fixp->fx_next != NULL
-         && fixp->fx_next->fx_r_type == BFD_RELOC_LO16
+         && (fixp->fx_next->fx_r_type == BFD_RELOC_LO16
+            || fixp->fx_next->fx_r_type == BFD_RELOC_MIPS16_LO16)
          && fixp->fx_addsy == fixp->fx_next->fx_addsy
          && fixp->fx_offset == fixp->fx_next->fx_offset);
 }
@@ -1484,10 +1685,8 @@ fixup_has_matching_lo_p (fixp)
    of register.  */
 
 static int
-insn_uses_reg (ip, reg, class)
-     struct mips_cl_insn *ip;
-     unsigned int reg;
-     enum mips_regclass class;
+insn_uses_reg (const struct mips_cl_insn *ip, unsigned int reg,
+              enum mips_regclass class)
 {
   if (class == MIPS16_REG)
     {
@@ -1511,38 +1710,33 @@ insn_uses_reg (ip, reg, class)
         because there is no instruction that sets both $f0 and $f1
         and requires a delay.  */
       if ((ip->insn_mo->pinfo & INSN_READ_FPR_S)
-         && ((((ip->insn_opcode >> OP_SH_FS) & OP_MASK_FS) &~(unsigned)1)
+         && ((EXTRACT_OPERAND (FS, *ip) & ~(unsigned) 1)
              == (reg &~ (unsigned) 1)))
        return 1;
       if ((ip->insn_mo->pinfo & INSN_READ_FPR_T)
-         && ((((ip->insn_opcode >> OP_SH_FT) & OP_MASK_FT) &~(unsigned)1)
+         && ((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)
-         && ((ip->insn_opcode >> OP_SH_RS) & OP_MASK_RS) == reg)
+         && EXTRACT_OPERAND (RS, *ip) == reg)
        return 1;
       if ((ip->insn_mo->pinfo & INSN_READ_GPR_T)
-         && ((ip->insn_opcode >> OP_SH_RT) & OP_MASK_RT) == reg)
+         && EXTRACT_OPERAND (RT, *ip) == reg)
        return 1;
     }
   else
     {
       if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_X)
-         && (mips16_to_32_reg_map[((ip->insn_opcode >> MIPS16OP_SH_RX)
-                                   & MIPS16OP_MASK_RX)]
-             == reg))
+         && 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[((ip->insn_opcode >> MIPS16OP_SH_RY)
-                                   & MIPS16OP_MASK_RY)]
-             == reg))
+         && 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[((ip->insn_opcode >> MIPS16OP_SH_MOVE32Z)
-                                   & MIPS16OP_MASK_MOVE32Z)]
+         && (mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip)]
              == reg))
        return 1;
       if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_T) && reg == TREG)
@@ -1552,8 +1746,7 @@ insn_uses_reg (ip, reg, class)
       if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_31) && reg == RA)
        return 1;
       if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_GPR_X)
-         && ((ip->insn_opcode >> MIPS16OP_SH_REGR32)
-             & MIPS16OP_MASK_REGR32) == reg)
+         && MIPS16_EXTRACT_OPERAND (REGR32, *ip) == reg)
        return 1;
     }
 
@@ -1564,31 +1757,48 @@ insn_uses_reg (ip, reg, class)
    delay.  */
 
 static int
-reg_needs_delay (reg)
-     unsigned int reg;
+reg_needs_delay (unsigned int reg)
 {
   unsigned long prev_pinfo;
 
-  prev_pinfo = prev_insn.insn_mo->pinfo;
+  prev_pinfo = history[0].insn_mo->pinfo;
   if (! mips_opts.noreorder
-      && ISA_HAS_COPROC_DELAYS (mips_opts.isa)
-      && ((prev_pinfo & INSN_LOAD_COPROC_DELAY)
-         || (! gpr_interlocks
-             && (prev_pinfo & INSN_LOAD_MEMORY_DELAY))))
-    {
-      /* A load from a coprocessor or from memory.  All load
-        delays delay the use of general register rt for one
-        instruction on the r3000.  The r6000 and r4000 use
-        interlocks.  */
+      && (((prev_pinfo & INSN_LOAD_MEMORY_DELAY)
+          && ! gpr_interlocks)
+         || ((prev_pinfo & INSN_LOAD_COPROC_DELAY)
+             && ! cop_interlocks)))
+    {
+      /* A load from a coprocessor or from memory.  All load delays
+        delay the use of general register rt for one instruction.  */
       /* Itbl support may require additional care here.  */
       know (prev_pinfo & INSN_WRITE_GPR_T);
-      if (reg == ((prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT))
+      if (reg == EXTRACT_OPERAND (RT, history[0]))
        return 1;
     }
 
   return 0;
 }
 
+/* Move all labels in insn_labels to the current insertion point.  */
+
+static void
+mips_move_labels (void)
+{
+  struct insn_label_list *l;
+  valueT val;
+
+  for (l = insn_labels; l != NULL; l = l->next)
+    {
+      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)
+       ++val;
+      S_SET_VALUE (l->label, val);
+    }
+}
+
 /* 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
@@ -1598,7 +1808,7 @@ reg_needs_delay (reg)
    to make them odd again.  */
 
 static void
-mips16_mark_labels ()
+mips16_mark_labels (void)
 {
   if (mips_opts.mips16)
     {
@@ -1618,332 +1828,354 @@ mips16_mark_labels ()
     }
 }
 
-/* Output an instruction.  PLACE is where to put the instruction; if
-   it is NULL, this uses frag_more to get room.  IP is the instruction
-   information.  ADDRESS_EXPR is an operand of the instruction to be
-   used with RELOC_TYPE.  */
+/* End the current frag.  Make it a variant frag and record the
+   relaxation info.  */
 
 static void
-append_insn (place, ip, address_expr, reloc_type)
-     char *place;
-     struct mips_cl_insn *ip;
-     expressionS *address_expr;
-     bfd_reloc_code_real_type *reloc_type;
+relax_close_frag (void)
 {
-  register unsigned long prev_pinfo, pinfo;
-  char *f;
-  fixS *fixp[3];
-  int nops = 0;
-  bfd_boolean force_new_frag = FALSE;
+  mips_macro_warning.first_frag = frag_now;
+  frag_var (rs_machine_dependent, 0, 0,
+           RELAX_ENCODE (mips_relax.sizes[0], mips_relax.sizes[1]),
+           mips_relax.symbol, 0, (char *) mips_relax.first_fixup);
 
-  /* Mark instruction labels in mips16 mode.  */
-  mips16_mark_labels ();
+  memset (&mips_relax.sizes, 0, sizeof (mips_relax.sizes));
+  mips_relax.first_fixup = 0;
+}
 
-  prev_pinfo = prev_insn.insn_mo->pinfo;
-  pinfo = ip->insn_mo->pinfo;
+/* Start a new relaxation sequence whose expansion depends on SYMBOL.
+   See the comment above RELAX_ENCODE for more details.  */
 
-  if (place == NULL && (! mips_opts.noreorder || prev_nop_frag != NULL))
+static void
+relax_start (symbolS *symbol)
+{
+  assert (mips_relax.sequence == 0);
+  mips_relax.sequence = 1;
+  mips_relax.symbol = symbol;
+}
+
+/* Start generating the second version of a relaxable sequence.
+   See the comment above RELAX_ENCODE for more details.  */
+
+static void
+relax_switch (void)
+{
+  assert (mips_relax.sequence == 1);
+  mips_relax.sequence = 2;
+}
+
+/* End the current relaxable sequence.  */
+
+static void
+relax_end (void)
+{
+  assert (mips_relax.sequence == 2);
+  relax_close_frag ();
+  mips_relax.sequence = 0;
+}
+
+/* Classify an instruction according to the FIX_VR4120_* enumeration.
+   Return NUM_FIX_VR4120_CLASSES if the instruction isn't affected
+   by VR4120 errata.  */
+
+static unsigned int
+classify_vr4120_insn (const char *name)
+{
+  if (strncmp (name, "macc", 4) == 0)
+    return FIX_VR4120_MACC;
+  if (strncmp (name, "dmacc", 5) == 0)
+    return FIX_VR4120_DMACC;
+  if (strncmp (name, "mult", 4) == 0)
+    return FIX_VR4120_MULT;
+  if (strncmp (name, "dmult", 5) == 0)
+    return FIX_VR4120_DMULT;
+  if (strstr (name, "div"))
+    return FIX_VR4120_DIV;
+  if (strcmp (name, "mtlo") == 0 || strcmp (name, "mthi") == 0)
+    return FIX_VR4120_MTHILO;
+  return NUM_FIX_VR4120_CLASSES;
+}
+
+/* Return the number of instructions that must separate INSN1 and INSN2,
+   where INSN1 is the earlier instruction.  Return the worst-case value
+   for any INSN2 if INSN2 is null.  */
+
+static unsigned int
+insns_between (const struct mips_cl_insn *insn1,
+              const struct mips_cl_insn *insn2)
+{
+  unsigned long pinfo1, pinfo2;
+
+  /* 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.  */
+  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))
+
+  /* For most targets, write-after-read dependencies on the HI and LO
+     registers must be separated by at least two instructions.  */
+  if (!hilo_interlocks)
     {
-      int prev_prev_nop;
+      if ((pinfo1 & INSN_READ_LO) && (pinfo2 & INSN_WRITE_LO))
+       return 2;
+      if ((pinfo1 & INSN_READ_HI) && (pinfo2 & INSN_WRITE_HI))
+       return 2;
+    }
 
-      /* If the previous insn required any delay slots, see if we need
-        to insert a NOP or two.  There are eight kinds of possible
-        hazards, of which an instruction can have at most one type.
-        (1) a load from memory delay
-        (2) a load from a coprocessor delay
-        (3) an unconditional branch delay
-        (4) a conditional branch delay
-        (5) a move to coprocessor register delay
-        (6) a load coprocessor register from memory delay
-        (7) a coprocessor condition code delay
-        (8) a HI/LO special register delay
+  /* 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
+      && MF_HILO_INSN (pinfo1)
+      && INSN2_USES_REG (EXTRACT_OPERAND (RD, *insn1), MIPS_GR_REG))
+    return 2;
 
-        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.  */
+  /* If working around VR4120 errata, check for combinations that need
+     a single intervening instruction.  */
+  if (mips_fix_vr4120)
+    {
+      unsigned int class1, class2;
 
-      /* This is how a NOP is emitted.  */
-#define emit_nop()                                     \
-  (mips_opts.mips16                                    \
-   ? md_number_to_chars (frag_more (2), 0x6500, 2)     \
-   : md_number_to_chars (frag_more (4), 0, 4))
-
-      /* The previous insn might require a delay slot, depending upon
-        the contents of the current insn.  */
-      if (! mips_opts.mips16
-         && ISA_HAS_COPROC_DELAYS (mips_opts.isa)
-         && (((prev_pinfo & INSN_LOAD_COPROC_DELAY)
-               && ! cop_interlocks)
-             || (! gpr_interlocks
-                 && (prev_pinfo & INSN_LOAD_MEMORY_DELAY))))
-       {
-         /* A load from a coprocessor or from memory.  All load
-            delays delay the use of general register rt for one
-            instruction on the r3000.  The r6000 and r4000 use
-            interlocks.  */
-         /* Itbl support may require additional care here.  */
-         know (prev_pinfo & INSN_WRITE_GPR_T);
-         if (mips_optimize == 0
-             || insn_uses_reg (ip,
-                               ((prev_insn.insn_opcode >> OP_SH_RT)
-                                & OP_MASK_RT),
-                               MIPS_GR_REG))
-           ++nops;
-       }
-      else if (! mips_opts.mips16
-              && ISA_HAS_COPROC_DELAYS (mips_opts.isa)
-              && (((prev_pinfo & INSN_COPROC_MOVE_DELAY)
-                   && ! cop_interlocks)
-                  || (mips_opts.isa == ISA_MIPS1
-                      && (prev_pinfo & INSN_COPROC_MEMORY_DELAY))))
-       {
-         /* A generic coprocessor delay.  The previous instruction
-            modified a coprocessor general or control register.  If
-            it modified a control register, we need to avoid any
-            coprocessor instruction (this is probably not always
-            required, but it sometimes is).  If it modified a general
-            register, we avoid using that register.
-
-            On the r6000 and r4000 loading a coprocessor register
-            from memory is interlocked, and does not require a delay.
-
-            This case is not handled very well.  There is no special
-            knowledge of CP0 handling, and the coprocessors other
-            than the floating point unit are not distinguished at
-            all.  */
-          /* Itbl support may require additional care here. FIXME!
-             Need to modify this to include knowledge about
-             user specified delays!  */
-         if (prev_pinfo & INSN_WRITE_FPR_T)
+      class1 = classify_vr4120_insn (insn1->insn_mo->name);
+      if (class1 != NUM_FIX_VR4120_CLASSES && vr4120_conflicts[class1] != 0)
+       {
+         if (insn2 == NULL)
+           return 1;
+         class2 = classify_vr4120_insn (insn2->insn_mo->name);
+         if (vr4120_conflicts[class1] & (1 << class2))
+           return 1;
+       }
+    }
+
+  if (!mips_opts.mips16)
+    {
+      /* Check for GPR or coprocessor load delays.  All such delays
+        are on the RT register.  */
+      /* Itbl support may require additional care here.  */
+      if ((!gpr_interlocks && (pinfo1 & INSN_LOAD_MEMORY_DELAY))
+         || (!cop_interlocks && (pinfo1 & INSN_LOAD_COPROC_DELAY)))
+       {
+         know (pinfo1 & INSN_WRITE_GPR_T);
+         if (INSN2_USES_REG (EXTRACT_OPERAND (RT, *insn1), MIPS_GR_REG))
+           return 1;
+       }
+
+      /* Check for generic coprocessor hazards.
+
+        This case is not handled very well.  There is no special
+        knowledge of CP0 handling, and the coprocessors other than
+        the floating point unit are not distinguished at all.  */
+      /* Itbl support may require additional care here. FIXME!
+        Need to modify this to include knowledge about
+        user specified delays!  */
+      else if ((!cop_interlocks && (pinfo1 & INSN_COPROC_MOVE_DELAY))
+              || (!cop_mem_interlocks && (pinfo1 & INSN_COPROC_MEMORY_DELAY)))
+       {
+         /* Handle cases where INSN1 writes to a known general coprocessor
+            register.  There must be a one instruction delay before INSN2
+            if INSN2 reads that register, otherwise no delay is needed.  */
+         if (pinfo1 & INSN_WRITE_FPR_T)
            {
-             if (mips_optimize == 0
-                 || insn_uses_reg (ip,
-                                   ((prev_insn.insn_opcode >> OP_SH_FT)
-                                    & OP_MASK_FT),
-                                   MIPS_FP_REG))
-               ++nops;
+             if (INSN2_USES_REG (EXTRACT_OPERAND (FT, *insn1), MIPS_FP_REG))
+               return 1;
            }
-         else if (prev_pinfo & INSN_WRITE_FPR_S)
+         else if (pinfo1 & INSN_WRITE_FPR_S)
            {
-             if (mips_optimize == 0
-                 || insn_uses_reg (ip,
-                                   ((prev_insn.insn_opcode >> OP_SH_FS)
-                                    & OP_MASK_FS),
-                                   MIPS_FP_REG))
-               ++nops;
+             if (INSN2_USES_REG (EXTRACT_OPERAND (FS, *insn1), MIPS_FP_REG))
+               return 1;
            }
          else
            {
-             /* We don't know exactly what the previous instruction
-                does.  If the current instruction uses a coprocessor
-                register, we must insert a NOP.  If previous
-                instruction may set the condition codes, and the
-                current instruction uses them, we must insert two
-                NOPS.  */
-              /* Itbl support may require additional care here.  */
-             if (mips_optimize == 0
-                 || ((prev_pinfo & INSN_WRITE_COND_CODE)
-                     && (pinfo & INSN_READ_COND_CODE)))
-               nops += 2;
-             else if (pinfo & INSN_COP)
-               ++nops;
+             /* Read-after-write dependencies on the control registers
+                require a two-instruction gap.  */
+             if ((pinfo1 & INSN_WRITE_COND_CODE)
+                 && (pinfo2 & INSN_READ_COND_CODE))
+               return 2;
+
+             /* We don't know exactly what INSN1 does.  If INSN2 is
+                also a coprocessor instruction, assume there must be
+                a one instruction gap.  */
+             if (pinfo2 & INSN_COP)
+               return 1;
            }
        }
-      else if (! mips_opts.mips16
-              && ISA_HAS_COPROC_DELAYS (mips_opts.isa)
-              && (prev_pinfo & INSN_WRITE_COND_CODE)
-               && ! cop_interlocks)
-       {
-         /* The previous instruction sets the coprocessor condition
-            codes, but does not require a general coprocessor delay
-            (this means it is a floating point comparison
-            instruction).  If this instruction uses the condition
-            codes, we need to insert a single NOP.  */
-         /* Itbl support may require additional care here.  */
-         if (mips_optimize == 0
-             || (pinfo & INSN_READ_COND_CODE))
-           ++nops;
-       }
-
-      /* If we're fixing up mfhi/mflo for the r7000 and the
-        previous insn was an mfhi/mflo and the current insn
-        reads the register that the mfhi/mflo wrote to, then
-        insert two nops.  */
-
-      else if (mips_7000_hilo_fix
-              && MF_HILO_INSN (prev_pinfo)
-              && insn_uses_reg (ip, ((prev_insn.insn_opcode >> OP_SH_RD)
-                                     & OP_MASK_RD),
-                                MIPS_GR_REG))
-       {
-         nops += 2;
-       }
-
-      /* If we're fixing up mfhi/mflo for the r7000 and the
-        2nd previous insn was an mfhi/mflo and the current insn
-        reads the register that the mfhi/mflo wrote to, then
-        insert one nop.  */
-
-      else if (mips_7000_hilo_fix
-              && MF_HILO_INSN (prev_prev_insn.insn_opcode)
-              && insn_uses_reg (ip, ((prev_prev_insn.insn_opcode >> OP_SH_RD)
-                                       & OP_MASK_RD),
-                                    MIPS_GR_REG))
-
-       {
-         ++nops;
-       }
-
-      else if (prev_pinfo & INSN_READ_LO)
-       {
-         /* The previous instruction reads the LO register; if the
-            current instruction writes to the LO register, we must
-            insert two NOPS.  Some newer processors have interlocks.
-            Also the tx39's multiply instructions can be exectuted
-             immediatly after a read from HI/LO (without the delay),
-             though the tx39's divide insns still do require the
-            delay.  */
-         if (! (hilo_interlocks
-                || (mips_tune == CPU_R3900 && (pinfo & INSN_MULT)))
-             && (mips_optimize == 0
-                 || (pinfo & INSN_WRITE_LO)))
-           nops += 2;
-         /* Most mips16 branch insns don't have a delay slot.
-            If a read from LO is immediately followed by a branch
-            to a write to LO we have a read followed by a write
-            less than 2 insns away.  We assume the target of
-            a branch might be a write to LO, and insert a nop
-            between a read and an immediately following branch.  */
-         else if (mips_opts.mips16
-                  && (mips_optimize == 0
-                      || (pinfo & MIPS16_INSN_BRANCH)))
-           ++nops;
-       }
-      else if (prev_insn.insn_mo->pinfo & INSN_READ_HI)
-       {
-         /* The previous instruction reads the HI register; if the
-            current instruction writes to the HI register, we must
-            insert a NOP.  Some newer processors have interlocks.
-            Also the note tx39's multiply above.  */
-         if (! (hilo_interlocks
-                || (mips_tune == CPU_R3900 && (pinfo & INSN_MULT)))
-             && (mips_optimize == 0
-                 || (pinfo & INSN_WRITE_HI)))
-           nops += 2;
-         /* Most mips16 branch insns don't have a delay slot.
-            If a read from HI is immediately followed by a branch
-            to a write to HI we have a read followed by a write
-            less than 2 insns away.  We assume the target of
-            a branch might be a write to HI, and insert a nop
-            between a read and an immediately following branch.  */
-         else if (mips_opts.mips16
-                  && (mips_optimize == 0
-                      || (pinfo & MIPS16_INSN_BRANCH)))
-           ++nops;
-       }
-
-      /* If the previous instruction was in a noreorder section, then
-         we don't want to insert the nop after all.  */
+
+      /* Check for read-after-write dependencies on the coprocessor
+        control registers in cases where INSN1 does not need a general
+        coprocessor delay.  This means that INSN1 is a floating point
+        comparison instruction.  */
       /* Itbl support may require additional care here.  */
-      if (prev_insn_unreordered)
-       nops = 0;
-
-      /* There are two cases which require two intervening
-        instructions: 1) setting the condition codes using a move to
-        coprocessor instruction which requires a general coprocessor
-        delay and then reading the condition codes 2) reading the HI
-        or LO register and then writing to it (except on processors
-        which have interlocks).  If we are not already emitting a NOP
-        instruction, we must check for these cases compared to the
-        instruction previous to the previous instruction.  */
-      if ((! mips_opts.mips16
-          && ISA_HAS_COPROC_DELAYS (mips_opts.isa)
-          && (prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY)
-          && (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
-          && (pinfo & INSN_READ_COND_CODE)
-          && ! cop_interlocks)
-         || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)
-             && (pinfo & INSN_WRITE_LO)
-             && ! (hilo_interlocks
-                   || (mips_tune == CPU_R3900 && (pinfo & INSN_MULT))))
-         || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
-             && (pinfo & INSN_WRITE_HI)
-             && ! (hilo_interlocks
-                   || (mips_tune == CPU_R3900 && (pinfo & INSN_MULT)))))
-       prev_prev_nop = 1;
-      else
-       prev_prev_nop = 0;
+      else if (!cop_interlocks
+              && (pinfo1 & INSN_WRITE_COND_CODE)
+              && (pinfo2 & INSN_READ_COND_CODE))
+       return 1;
+    }
 
-      if (prev_prev_insn_unreordered)
-       prev_prev_nop = 0;
+#undef INSN2_USES_REG
 
-      if (prev_prev_nop && nops == 0)
-       ++nops;
+  return 0;
+}
 
-      if (mips_fix_4122_bugs && prev_insn.insn_mo->name)
-       {
-         /* We're out of bits in pinfo, so we must resort to string
-            ops here.  Shortcuts are selected based on opcodes being
-            limited to the VR4122 instruction set.  */
-         int min_nops = 0;
-         const char *pn = prev_insn.insn_mo->name;
-         const char *tn = ip->insn_mo->name;
-         if (strncmp(pn, "macc", 4) == 0
-             || strncmp(pn, "dmacc", 5) == 0)
-           {
-             /* Errata 21 - [D]DIV[U] after [D]MACC */
-             if (strstr (tn, "div"))
-               {
-                 min_nops = 1;
-               }
+/* 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 HISTORY.  */
 
-             /* Errata 23 - Continuous DMULT[U]/DMACC instructions */
-             if (pn[0] == 'd' /* dmacc */
-                 && (strncmp(tn, "dmult", 5) == 0
-                     || strncmp(tn, "dmacc", 5) == 0))
-               {
-                 min_nops = 1;
-               }
+static int
+nops_for_vr4130 (const struct mips_cl_insn *history,
+                const struct mips_cl_insn *insn)
+{
+  int i, j, reg;
+
+  /* Check if the instruction writes to HI or LO.  MTHI and MTLO
+     are not affected by the errata.  */
+  if (insn != 0
+      && ((insn->insn_mo->pinfo & (INSN_WRITE_HI | INSN_WRITE_LO)) == 0
+         || strcmp (insn->insn_mo->name, "mtlo") == 0
+         || strcmp (insn->insn_mo->name, "mthi") == 0))
+    return 0;
 
-             /* Errata 24 - MT{LO,HI} after [D]MACC */
-             if (strcmp (tn, "mtlo") == 0
-                 || strcmp (tn, "mthi") == 0)
-               {
-                 min_nops = 1;
-               }
+  /* Search for the first MFLO or MFHI.  */
+  for (i = 0; i < MAX_VR4130_NOPS; i++)
+    if (!history[i].noreorder_p && MF_HILO_INSN (history[i].insn_mo->pinfo))
+      {
+       /* Extract the destination register.  */
+       if (mips_opts.mips16)
+         reg = mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, history[i])];
+       else
+         reg = EXTRACT_OPERAND (RD, history[i]);
 
-           }
-         else if (strncmp(pn, "dmult", 5) == 0
-                  && (strncmp(tn, "dmult", 5) == 0
-                      || strncmp(tn, "dmacc", 5) == 0))
-           {
-             /* Here is the rest of errata 23.  */
-             min_nops = 1;
-           }
-         if (nops < min_nops)
-           nops = min_nops;
-       }
+       /* No nops are needed if INSN reads that register.  */
+       if (insn != NULL && insn_uses_reg (insn, reg, MIPS_GR_REG))
+         return 0;
+
+       /* ...or if any of the intervening instructions do.  */
+       for (j = 0; j < i; j++)
+         if (insn_uses_reg (&history[j], reg, MIPS_GR_REG))
+           return 0;
+
+       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 HISTORY,
+   where HISTORY[0] is the most recent instruction.  If INSN is null,
+   return the worse-case number of nops for any instruction.  */
+
+static int
+nops_for_insn (const struct mips_cl_insn *history,
+              const struct mips_cl_insn *insn)
+{
+  int i, nops, tmp_nops;
+
+  nops = 0;
+  for (i = 0; i < MAX_DELAY_NOPS; i++)
+    if (!history[i].noreorder_p)
+      {
+       tmp_nops = insns_between (history + i, insn) - i;
+       if (tmp_nops > nops)
+         nops = tmp_nops;
+      }
 
-      /* If we are being given a nop instruction, don't bother with
-        one of the nops we would otherwise output.  This will only
-        happen when a nop instruction is used with mips_optimize set
-        to 0.  */
-      if (nops > 0
-         && ! mips_opts.noreorder
-         && ip->insn_opcode == (unsigned) (mips_opts.mips16 ? 0x6500 : 0))
-       --nops;
+  if (mips_fix_vr4130)
+    {
+      tmp_nops = nops_for_vr4130 (history, insn);
+      if (tmp_nops > nops)
+       nops = tmp_nops;
+    }
+
+  return nops;
+}
 
-      /* Now emit the right number of NOP instructions.  */
-      if (nops > 0 && ! mips_opts.noreorder)
+/* The variable arguments provide NUM_INSNS extra instructions that
+   might be added to HISTORY.  Return the largest number of nops that
+   would be needed after the extended sequence.  */
+
+static int
+nops_for_sequence (int num_insns, const struct mips_cl_insn *history, ...)
+{
+  va_list args;
+  struct mips_cl_insn buffer[MAX_NOPS];
+  struct mips_cl_insn *cursor;
+  int nops;
+
+  va_start (args, history);
+  cursor = buffer + num_insns;
+  memcpy (cursor, history, (MAX_NOPS - num_insns) * sizeof (*cursor));
+  while (cursor > buffer)
+    *--cursor = *va_arg (args, const struct mips_cl_insn *);
+
+  nops = nops_for_insn (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 (const struct mips_cl_insn *history,
+                        const struct mips_cl_insn *insn)
+{
+  int nops, tmp_nops;
+
+  nops = nops_for_insn (history, insn);
+  if (insn->insn_mo->pinfo & (INSN_UNCOND_BRANCH_DELAY
+                             | INSN_COND_BRANCH_DELAY
+                             | INSN_COND_BRANCH_LIKELY))
+    {
+      tmp_nops = nops_for_sequence (2, history, insn, NOP_INSN);
+      if (tmp_nops > nops)
+       nops = tmp_nops;
+    }
+  else if (mips_opts.mips16 && (insn->insn_mo->pinfo & MIPS16_INSN_BRANCH))
+    {
+      tmp_nops = nops_for_sequence (1, history, insn);
+      if (tmp_nops > nops)
+       nops = tmp_nops;
+    }
+  return nops;
+}
+
+/* Output an instruction.  IP is the instruction information.
+   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)
+{
+  register unsigned long prev_pinfo, pinfo;
+  relax_stateT prev_insn_frag_type = 0;
+  bfd_boolean relaxed_branch = FALSE;
+
+  /* Mark instruction labels in mips16 mode.  */
+  mips16_mark_labels ();
+
+  prev_pinfo = history[0].insn_mo->pinfo;
+  pinfo = ip->insn_mo->pinfo;
+
+  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;
-         struct insn_label_list *l;
 
          old_frag = frag_now;
          old_frag_offset = frag_now_fix ();
@@ -1965,65 +2197,56 @@ append_insn (place, ip, address_expr, reloc_type)
              frag_grow (40);
            }
 
-         for (l = insn_labels; l != NULL; l = l->next)
-           {
-             valueT val;
-
-             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)
-               ++val;
-             S_SET_VALUE (l->label, val);
-           }
+         mips_move_labels ();
 
 #ifndef NO_ECOFF_DEBUGGING
          if (ECOFF_DEBUGGING)
            ecoff_fix_loc (old_frag, old_frag_offset);
 #endif
        }
-      else if (prev_nop_frag != NULL)
-       {
-         /* We have a frag holding nops we may be able to remove.  If
-             we don't need any nops, we can decrease the size of
-             prev_nop_frag by the size of one instruction.  If we do
-             need some nops, we count them in prev_nops_required.  */
-         if (prev_nop_frag_since == 0)
-           {
-             if (nops == 0)
-               {
-                 prev_nop_frag->fr_fix -= mips_opts.mips16 ? 2 : 4;
-                 --prev_nop_frag_holds;
-               }
-             else
-               prev_nop_frag_required += nops;
-           }
-         else
-           {
-             if (prev_prev_nop == 0)
-               {
-                 prev_nop_frag->fr_fix -= mips_opts.mips16 ? 2 : 4;
-                 --prev_nop_frag_holds;
-               }
-             else
-               ++prev_nop_frag_required;
-           }
-
-         if (prev_nop_frag_holds <= prev_nop_frag_required)
-           prev_nop_frag = NULL;
+    }
+  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);
+      assert (nops <= prev_nop_frag_holds);
 
-         ++prev_nop_frag_since;
+      /* Enforce NOPS as a minimum.  */
+      if (nops > prev_nop_frag_required)
+       prev_nop_frag_required = nops;
 
-         /* Sanity check: by the time we reach the second instruction
-             after prev_nop_frag, we should have used up all the nops
-             one way or another.  */
-         assert (prev_nop_frag_since <= 1 || prev_nop_frag == NULL);
+      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++;
        }
     }
 
-  if (place == NULL
-      && address_expr
+#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
+
+  /* Record the frag type before frag_var.  */
+  if (history[0].frag)
+    prev_insn_frag_type = history[0].frag->fr_type;
+
+  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)
@@ -2035,36 +2258,33 @@ append_insn (place, ip, address_expr, reloc_type)
       && !(mips_opts.noat && mips_pic != NO_PIC)
       && !mips_opts.mips16)
     {
-      f = frag_var (rs_machine_dependent,
-                   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,
-                   0);
+      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.  */
       assert (mips_opts.mips16 && address_expr != NULL);
-      f = frag_var (rs_machine_dependent, 4, 0,
-                   RELAX_MIPS16_ENCODE (*reloc_type - BFD_RELOC_UNUSED,
-                                        mips16_small, mips16_ext,
-                                        (prev_pinfo
-                                         & INSN_UNCOND_BRANCH_DELAY),
-                                        (*prev_insn_reloc_type
-                                         == BFD_RELOC_MIPS16_JMP)),
-                   make_expr_symbol (address_expr), 0, NULL);
-    }
-  else if (place != NULL)
-    f = place;
+      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)
@@ -2072,7 +2292,7 @@ append_insn (place, ip, address_expr, reloc_type)
       /* Make sure there is enough room to swap this instruction with
          a following jump instruction.  */
       frag_grow (6);
-      f = frag_more (2);
+      add_fixed_insn (ip);
     }
   else
     {
@@ -2081,15 +2301,34 @@ append_insn (place, ip, address_expr, reloc_type)
          && (prev_pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
        as_warn (_("extended instruction in delay slot"));
 
-      f = frag_more (4);
+      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 (mips_relax.sequence != 2)
+       mips_macro_warning.sizes[0] += 4;
+      if (mips_relax.sequence != 1)
+       mips_macro_warning.sizes[1] += 4;
+
+      if (mips_opts.mips16)
+       {
+         ip->fixed_p = 1;
+         ip->mips16_absolute_jump_p = (*reloc_type == BFD_RELOC_MIPS16_JMP);
+       }
+      add_fixed_insn (ip);
     }
 
-  fixp[0] = fixp[1] = fixp[2] = NULL;
-  if (address_expr != NULL && *reloc_type < BFD_RELOC_UNUSED)
+  if (address_expr != NULL && *reloc_type <= BFD_RELOC_UNUSED)
     {
       if (address_expr->X_op == O_constant)
        {
-         valueT tmp;
+         unsigned int tmp;
 
          switch (*reloc_type)
            {
@@ -2098,25 +2337,25 @@ append_insn (place, ip, address_expr, reloc_type)
              break;
 
            case BFD_RELOC_MIPS_HIGHEST:
-             tmp = (address_expr->X_add_number + 0x800080008000) >> 16;
-             tmp >>= 16;
-             ip->insn_opcode |= (tmp >> 16) & 0xffff;
+             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 + 0x80008000) >> 16;
-             ip->insn_opcode |= (tmp >> 16) & 0xffff;
+             tmp = (address_expr->X_add_number + 0x80008000ull) >> 32;
+             ip->insn_opcode |= tmp & 0xffff;
              break;
 
            case BFD_RELOC_HI16_S:
-             ip->insn_opcode |= ((address_expr->X_add_number + 0x8000)
-                                 >> 16) & 0xffff;
+             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;
 
+           case BFD_RELOC_UNUSED:
            case BFD_RELOC_LO16:
            case BFD_RELOC_MIPS_GOT_DISP:
              ip->insn_opcode |= address_expr->X_add_number & 0xffff;
@@ -2152,135 +2391,112 @@ append_insn (place, ip, address_expr, reloc_type)
              internalError ();
            }
        }
-      else
-       {
+      else if (*reloc_type < BFD_RELOC_UNUSED)
        need_reloc:
-         /* Don't generate a reloc if we are writing into a variant frag.  */
-         if (place == NULL)
-           {
-             reloc_howto_type *howto;
-             int i;
+       {
+         reloc_howto_type *howto;
+         int i;
 
-             /* 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;
+         /* 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;
 
-             howto = bfd_reloc_type_lookup (stdoutput, reloc_type[i - 1]);
-             fixp[0] = fix_new_exp (frag_now, f - frag_now->fr_literal,
-                                    bfd_get_reloc_size(howto),
+         howto = bfd_reloc_type_lookup (stdoutput, reloc_type[i - 1]);
+         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]);
 
-             /* 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_HI16_S
-                     || reloc_type[0] == BFD_RELOC_LO16
-                     || 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))
-               fixp[0]->fx_no_overflow = 1;
-
-             if (reloc_needs_lo_p (*reloc_type))
-               {
-                 struct mips_hi_fixup *hi_fixup;
+         /* 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_HI16_S
+                 || reloc_type[0] == BFD_RELOC_LO16
+                 || 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
+                 || reloc_type[0] == BFD_RELOC_MIPS16_HI16_S
+                 || reloc_type[0] == BFD_RELOC_MIPS16_LO16))
+           ip->fixp[0]->fx_no_overflow = 1;
+
+         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;
 
-                 /* 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 = fixp[0];
-                 hi_fixup->seg = now_seg;
+             /* 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;
+           }
 
-             /* 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)
-                 {
-                   address_expr->X_op = O_absent;
-                   address_expr->X_add_symbol = 0;
-                   address_expr->X_add_number = 0;
+         /* 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]);
 
-                   fixp[i] = fix_new_exp (frag_now, fixp[0]->fx_where,
-                                          fixp[0]->fx_size, address_expr,
-                                          FALSE, reloc_type[i]);
-                 }
-           }
+               /* Use fx_tcbit to mark compound relocs.  */
+               ip->fixp[0]->fx_tcbit = 1;
+               ip->fixp[i]->fx_tcbit = 1;
+             }
        }
     }
-
-  if (! mips_opts.mips16)
-    {
-      md_number_to_chars (f, ip->insn_opcode, 4);
-#ifdef OBJ_ELF
-      dwarf2_emit_insn (4);
-#endif
-    }
-  else if (*reloc_type == BFD_RELOC_MIPS16_JMP)
-    {
-      md_number_to_chars (f, ip->insn_opcode >> 16, 2);
-      md_number_to_chars (f + 2, ip->insn_opcode & 0xffff, 2);
-#ifdef OBJ_ELF
-      dwarf2_emit_insn (4);
-#endif
-    }
-  else
-    {
-      if (ip->use_extend)
-       {
-         md_number_to_chars (f, 0xf000 | ip->extend, 2);
-         f += 2;
-       }
-      md_number_to_chars (f, ip->insn_opcode, 2);
-#ifdef OBJ_ELF
-      dwarf2_emit_insn (ip->use_extend ? 4 : 2);
-#endif
-    }
+  install_insn (ip);
 
   /* Update the register mask information.  */
   if (! mips_opts.mips16)
     {
       if (pinfo & INSN_WRITE_GPR_D)
-       mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD);
+       mips_gprmask |= 1 << EXTRACT_OPERAND (RD, *ip);
       if ((pinfo & (INSN_WRITE_GPR_T | INSN_READ_GPR_T)) != 0)
-       mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RT) & OP_MASK_RT);
+       mips_gprmask |= 1 << EXTRACT_OPERAND (RT, *ip);
       if (pinfo & INSN_READ_GPR_S)
-       mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RS) & OP_MASK_RS);
+       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 << ((ip->insn_opcode >> OP_SH_FD) & OP_MASK_FD);
+       mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FD, *ip);
       if ((pinfo & (INSN_WRITE_FPR_S | INSN_READ_FPR_S)) != 0)
-       mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FS) & OP_MASK_FS);
+       mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FS, *ip);
       if ((pinfo & (INSN_WRITE_FPR_T | INSN_READ_FPR_T)) != 0)
-       mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FT) & OP_MASK_FT);
+       mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FT, *ip);
       if ((pinfo & INSN_READ_FPR_R) != 0)
-       mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FR) & OP_MASK_FR);
+       mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FR, *ip);
       if (pinfo & INSN_COP)
        {
          /* We don't keep enough information to sort these cases out.
@@ -2294,14 +2510,11 @@ append_insn (place, ip, address_expr, reloc_type)
   else
     {
       if (pinfo & (MIPS16_INSN_WRITE_X | MIPS16_INSN_READ_X))
-       mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_RX)
-                             & MIPS16OP_MASK_RX);
+       mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RX, *ip);
       if (pinfo & (MIPS16_INSN_WRITE_Y | MIPS16_INSN_READ_Y))
-       mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_RY)
-                             & MIPS16OP_MASK_RY);
+       mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RY, *ip);
       if (pinfo & MIPS16_INSN_WRITE_Z)
-       mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_RZ)
-                             & MIPS16OP_MASK_RZ);
+       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))
@@ -2311,14 +2524,12 @@ append_insn (place, ip, address_expr, reloc_type)
       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 << ((ip->insn_opcode >> MIPS16OP_SH_MOVE32Z)
-                             & MIPS16OP_MASK_MOVE32Z);
+       mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip);
       if (pinfo & MIPS16_INSN_READ_GPR_X)
-       mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_REGR32)
-                             & MIPS16OP_MASK_REGR32);
+       mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (REGR32, *ip);
     }
 
-  if (place == NULL && ! mips_opts.noreorder)
+  if (mips_relax.sequence != 2 && !mips_opts.noreorder)
     {
       /* Filling the branch delay slot is more complex.  We try to
         switch the branch with the previous instruction, which we can
@@ -2332,15 +2543,9 @@ append_insn (place, ip, address_expr, reloc_type)
              /* If we have seen .set volatile or .set nomove, don't
                 optimize.  */
              || mips_opts.nomove != 0
-             /* If we had to emit any NOP instructions, then we
-                already know we can not swap.  */
-             || nops != 0
-             /* If we don't even know the previous insn, we can not
-                swap.  */
-             || ! prev_insn_valid
-             /* If the previous insn is already in a branch delay
-                slot, then we can not swap.  */
-             || prev_insn_is_delay_slot
+             /* 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
@@ -2352,62 +2557,26 @@ append_insn (place, ip, address_expr, reloc_type)
                   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.  We don't have to check
-                prev_insn_unreordered, because prev_insn_valid will
-                be 0 in that case.  We don't want to use
-                prev_prev_insn_valid, because we do want to be able
-                to swap at the start of a function.  */
-             || prev_prev_insn_unreordered
+                .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.  */
              || insn_labels != NULL
-             /* If the previous instruction is in a variant frag, we
-                can not do the swap.  This does not apply to the
-                mips16, which uses variant frags for different
-                purposes.  */
-             || (! mips_opts.mips16
-                 && prev_insn_frag->fr_type == rs_machine_dependent)
-             /* If the branch reads the condition codes, we don't
-                even try to swap, because in the sequence
-                  ctc1 $X,$31
-                  INSN
-                  INSN
-                  bc1t LABEL
-                we can not swap, and I don't feel like handling that
-                case.  */
+             /* 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
-                 && ISA_HAS_COPROC_DELAYS (mips_opts.isa)
-                 && (pinfo & INSN_READ_COND_CODE))
-             /* We can not swap with an instruction that requires a
-                delay slot, becase the target of the branch might
-                interfere with that instruction.  */
-             || (! mips_opts.mips16
-                 && ISA_HAS_COPROC_DELAYS (mips_opts.isa)
-                 && (prev_pinfo
-              /* Itbl support may require additional care here.  */
-                     & (INSN_LOAD_COPROC_DELAY
-                        | INSN_COPROC_MOVE_DELAY
-                        | INSN_WRITE_COND_CODE)))
-             || (! (hilo_interlocks
-                    || (mips_tune == CPU_R3900 && (pinfo & INSN_MULT)))
-                 && (prev_pinfo
-                     & (INSN_READ_LO
-                        | INSN_READ_HI)))
-             || (! mips_opts.mips16
-                 && ! gpr_interlocks
-                 && (prev_pinfo & INSN_LOAD_MEMORY_DELAY))
-             || (! mips_opts.mips16
-                 && mips_opts.isa == ISA_MIPS1
-                  /* Itbl support may require additional care here.  */
-                 && (prev_pinfo & INSN_COPROC_MEMORY_DELAY))
-             /* We can not swap with a branch instruction.  */
-             || (prev_pinfo
-                 & (INSN_UNCOND_BRANCH_DELAY
-                    | INSN_COND_BRANCH_DELAY
-                    | INSN_COND_BRANCH_LIKELY))
+                 && 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.  */
@@ -2416,43 +2585,33 @@ append_insn (place, ip, address_expr, reloc_type)
                 instruction sets, we can not swap.  */
              || (! mips_opts.mips16
                  && (prev_pinfo & INSN_WRITE_GPR_T)
-                 && insn_uses_reg (ip,
-                                   ((prev_insn.insn_opcode >> OP_SH_RT)
-                                    & OP_MASK_RT),
+                 && 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,
-                                   ((prev_insn.insn_opcode >> OP_SH_RD)
-                                    & OP_MASK_RD),
+                 && 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,
-                                        ((prev_insn.insn_opcode
-                                          >> MIPS16OP_SH_RX)
-                                         & MIPS16OP_MASK_RX),
-                                        MIPS16_REG))
+                      && (insn_uses_reg
+                          (ip, MIPS16_EXTRACT_OPERAND (RX, history[0]),
+                           MIPS16_REG)))
                      || ((prev_pinfo & MIPS16_INSN_WRITE_Y)
-                         && insn_uses_reg (ip,
-                                           ((prev_insn.insn_opcode
-                                             >> MIPS16OP_SH_RY)
-                                            & MIPS16OP_MASK_RY),
-                                           MIPS16_REG))
+                         && (insn_uses_reg
+                             (ip, MIPS16_EXTRACT_OPERAND (RY, history[0]),
+                              MIPS16_REG)))
                      || ((prev_pinfo & MIPS16_INSN_WRITE_Z)
-                         && insn_uses_reg (ip,
-                                           ((prev_insn.insn_opcode
-                                             >> MIPS16OP_SH_RZ)
-                                            & MIPS16OP_MASK_RZ),
-                                           MIPS16_REG))
+                         && (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 (prev_insn.
-                                                                    insn_opcode),
+                                           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
@@ -2460,61 +2619,37 @@ append_insn (place, ip, address_expr, reloc_type)
              || (! mips_opts.mips16
                  && (prev_pinfo & INSN_WRITE_GPR_T)
                  && (((pinfo & INSN_WRITE_GPR_D)
-                      && (((prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT)
-                          == ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD)))
+                      && (EXTRACT_OPERAND (RT, history[0])
+                          == EXTRACT_OPERAND (RD, *ip)))
                      || ((pinfo & INSN_WRITE_GPR_31)
-                         && (((prev_insn.insn_opcode >> OP_SH_RT)
-                              & OP_MASK_RT)
-                             == RA))))
+                         && EXTRACT_OPERAND (RT, history[0]) == RA)))
              || (! mips_opts.mips16
                  && (prev_pinfo & INSN_WRITE_GPR_D)
                  && (((pinfo & INSN_WRITE_GPR_D)
-                      && (((prev_insn.insn_opcode >> OP_SH_RD) & OP_MASK_RD)
-                          == ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD)))
+                      && (EXTRACT_OPERAND (RD, history[0])
+                          == EXTRACT_OPERAND (RD, *ip)))
                      || ((pinfo & INSN_WRITE_GPR_31)
-                         && (((prev_insn.insn_opcode >> OP_SH_RD)
-                              & OP_MASK_RD)
-                             == RA))))
+                         && 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 (prev_insn.insn_opcode)
+                         && (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 (&prev_insn,
-                                   ((ip->insn_opcode >> OP_SH_RD)
-                                    & OP_MASK_RD),
+                 && insn_uses_reg (&history[0],
+                                   EXTRACT_OPERAND (RD, *ip),
                                    MIPS_GR_REG))
              || (! mips_opts.mips16
                  && (pinfo & INSN_WRITE_GPR_31)
-                 && insn_uses_reg (&prev_insn, RA, MIPS_GR_REG))
+                 && insn_uses_reg (&history[0], RA, MIPS_GR_REG))
              || (mips_opts.mips16
                  && (pinfo & MIPS16_INSN_WRITE_31)
-                 && insn_uses_reg (&prev_insn, RA, MIPS_GR_REG))
-             /* If we are generating embedded PIC code, the branch
-                might be expanded into a sequence which uses $at, so
-                we can't swap with an instruction which reads it.  */
-             || (mips_pic == EMBEDDED_PIC
-                 && insn_uses_reg (&prev_insn, AT, MIPS_GR_REG))
-             /* If the previous previous instruction has a load
-                delay, and sets a register that the branch reads, we
-                can not swap.  */
-             || (! mips_opts.mips16
-                 && ISA_HAS_COPROC_DELAYS (mips_opts.isa)
-              /* Itbl support may require additional care here.  */
-                 && ((prev_prev_insn.insn_mo->pinfo & INSN_LOAD_COPROC_DELAY)
-                     || (! gpr_interlocks
-                         && (prev_prev_insn.insn_mo->pinfo
-                             & INSN_LOAD_MEMORY_DELAY)))
-                 && insn_uses_reg (ip,
-                                   ((prev_prev_insn.insn_opcode >> OP_SH_RT)
-                                    & OP_MASK_RT),
-                                   MIPS_GR_REG))
+                 && 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)
@@ -2525,13 +2660,10 @@ append_insn (place, ip, address_expr, reloc_type)
                  swap.  */
              || (mips_opts.mips16
                  && (prev_pinfo & MIPS16_INSN_READ_PC))
-             /* If the previous instruction was extended, we can not
-                 swap.  */
-             || (mips_opts.mips16 && prev_insn_extended)
              /* 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 && prev_insn_fixp[0])
+             || (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))
@@ -2540,125 +2672,46 @@ append_insn (place, ip, address_expr, reloc_type)
                 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 ();
-             /* Update the previous insn information.  */
-             prev_prev_insn = *ip;
-             prev_insn.insn_mo = &dummy_opcode;
+             if (mips_relax.sequence)
+               mips_relax.sizes[mips_relax.sequence - 1] += 4;
            }
          else
            {
              /* It looks like we can actually do the swap.  */
-             if (! mips_opts.mips16)
+             struct mips_cl_insn delay = history[0];
+             if (mips_opts.mips16)
                {
-                 char *prev_f;
-                 char temp[4];
-
-                 prev_f = prev_insn_frag->fr_literal + prev_insn_where;
-                 memcpy (temp, prev_f, 4);
-                 memcpy (prev_f, f, 4);
-                 memcpy (f, temp, 4);
-                 if (prev_insn_fixp[0])
-                   {
-                     prev_insn_fixp[0]->fx_frag = frag_now;
-                     prev_insn_fixp[0]->fx_where = f - frag_now->fr_literal;
-                   }
-                 if (prev_insn_fixp[1])
-                   {
-                     prev_insn_fixp[1]->fx_frag = frag_now;
-                     prev_insn_fixp[1]->fx_where = f - frag_now->fr_literal;
-                   }
-                 if (prev_insn_fixp[2])
-                   {
-                     prev_insn_fixp[2]->fx_frag = frag_now;
-                     prev_insn_fixp[2]->fx_where = f - frag_now->fr_literal;
-                   }
-                 if (prev_insn_fixp[0] && HAVE_NEWABI
-                     && prev_insn_frag != frag_now
-                     && (prev_insn_fixp[0]->fx_r_type
-                         == BFD_RELOC_MIPS_GOT_DISP
-                         || (prev_insn_fixp[0]->fx_r_type
-                             == BFD_RELOC_MIPS_CALL16)))
-                   {
-                     /* To avoid confusion in tc_gen_reloc, we must
-                        ensure that this does not become a variant
-                        frag.  */
-                     force_new_frag = TRUE;
-                   }
-                 if (fixp[0])
-                   {
-                     fixp[0]->fx_frag = prev_insn_frag;
-                     fixp[0]->fx_where = prev_insn_where;
-                   }
-                 if (fixp[1])
-                   {
-                     fixp[1]->fx_frag = prev_insn_frag;
-                     fixp[1]->fx_where = prev_insn_where;
-                   }
-                 if (fixp[2])
-                   {
-                     fixp[2]->fx_frag = prev_insn_frag;
-                     fixp[2]->fx_where = prev_insn_where;
-                   }
+                 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
                {
-                 char *prev_f;
-                 char temp[2];
-
-                 assert (prev_insn_fixp[0] == NULL);
-                 assert (prev_insn_fixp[1] == NULL);
-                 assert (prev_insn_fixp[2] == NULL);
-                 prev_f = prev_insn_frag->fr_literal + prev_insn_where;
-                 memcpy (temp, prev_f, 2);
-                 memcpy (prev_f, f, 2);
-                 if (*reloc_type != BFD_RELOC_MIPS16_JMP)
-                   {
-                     assert (*reloc_type == BFD_RELOC_UNUSED);
-                     memcpy (f, temp, 2);
-                   }
-                 else
-                   {
-                     memcpy (f, f + 2, 2);
-                     memcpy (f + 2, temp, 2);
-                   }
-                 if (fixp[0])
-                   {
-                     fixp[0]->fx_frag = prev_insn_frag;
-                     fixp[0]->fx_where = prev_insn_where;
-                   }
-                 if (fixp[1])
-                   {
-                     fixp[1]->fx_frag = prev_insn_frag;
-                     fixp[1]->fx_where = prev_insn_where;
-                   }
-                 if (fixp[2])
-                   {
-                     fixp[2]->fx_frag = prev_insn_frag;
-                     fixp[2]->fx_where = prev_insn_where;
-                   }
+                 move_insn (&delay, ip->frag, ip->where);
+                 move_insn (ip, history[0].frag, history[0].where);
                }
-
-             /* Update the previous insn information; leave prev_insn
-                unchanged.  */
-             prev_prev_insn = *ip;
+             history[0] = *ip;
+             delay.fixed_p = 1;
+             insert_into_history (0, 1, &delay);
            }
-         prev_insn_is_delay_slot = 1;
 
          /* If that was an unconditional branch, forget the previous
             insn information.  */
          if (pinfo & INSN_UNCOND_BRANCH_DELAY)
-           {
-             prev_prev_insn.insn_mo = &dummy_opcode;
-             prev_insn.insn_mo = &dummy_opcode;
-           }
-
-         prev_insn_fixp[0] = NULL;
-         prev_insn_fixp[1] = NULL;
-         prev_insn_fixp[2] = NULL;
-         prev_insn_reloc_type[0] = BFD_RELOC_UNUSED;
-         prev_insn_reloc_type[1] = BFD_RELOC_UNUSED;
-         prev_insn_reloc_type[2] = BFD_RELOC_UNUSED;
-         prev_insn_extended = 0;
+           mips_no_prev_insn ();
        }
       else if (pinfo & INSN_COND_BRANCH_LIKELY)
        {
@@ -2666,185 +2719,70 @@ append_insn (place, ip, address_expr, reloc_type)
             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 ();
-         /* Update the previous insn information.  */
-         prev_prev_insn = *ip;
-         prev_insn.insn_mo = &dummy_opcode;
-         prev_insn_fixp[0] = NULL;
-         prev_insn_fixp[1] = NULL;
-         prev_insn_fixp[2] = NULL;
-         prev_insn_reloc_type[0] = BFD_RELOC_UNUSED;
-         prev_insn_reloc_type[1] = BFD_RELOC_UNUSED;
-         prev_insn_reloc_type[2] = BFD_RELOC_UNUSED;
-         prev_insn_extended = 0;
        }
       else
-       {
-         /* Update the previous insn information.  */
-         if (nops > 0)
-           prev_prev_insn.insn_mo = &dummy_opcode;
-         else
-           prev_prev_insn = prev_insn;
-         prev_insn = *ip;
-
-         /* Any time we see a branch, we always fill the delay slot
-            immediately; since this insn is not a branch, we know it
-            is not in a delay slot.  */
-         prev_insn_is_delay_slot = 0;
-
-         prev_insn_fixp[0] = fixp[0];
-         prev_insn_fixp[1] = fixp[1];
-         prev_insn_fixp[2] = fixp[2];
-         prev_insn_reloc_type[0] = reloc_type[0];
-         prev_insn_reloc_type[1] = reloc_type[1];
-         prev_insn_reloc_type[2] = reloc_type[2];
-         if (mips_opts.mips16)
-           prev_insn_extended = (ip->use_extend
-                                 || *reloc_type > BFD_RELOC_UNUSED);
-       }
-
-      prev_prev_insn_unreordered = prev_insn_unreordered;
-      prev_insn_unreordered = 0;
-      prev_insn_frag = frag_now;
-      prev_insn_where = f - frag_now->fr_literal;
-      prev_insn_valid = 1;
-    }
-  else if (place == NULL)
-    {
-      /* We need to record a bit of information even when we are not
-         reordering, in order to determine the base address for mips16
-         PC relative relocs.  */
-      prev_prev_insn = prev_insn;
-      prev_insn = *ip;
-      prev_insn_reloc_type[0] = reloc_type[0];
-      prev_insn_reloc_type[1] = reloc_type[1];
-      prev_insn_reloc_type[2] = reloc_type[2];
-      prev_prev_insn_unreordered = prev_insn_unreordered;
-      prev_insn_unreordered = 1;
+       insert_into_history (0, 1, ip);
     }
+  else
+    insert_into_history (0, 1, ip);
 
   /* We just output an insn, so the next one doesn't have a label.  */
   mips_clear_insn_labels ();
-
-  /* We must ensure that the frag to which an instruction that was
-     moved from a non-variant frag doesn't become a variant frag,
-     otherwise tc_gen_reloc may get confused.  */
-  if (force_new_frag)
-    {
-      frag_wane (frag_now);
-      frag_new (0);
-    }
 }
 
-/* This function forgets that there was any previous instruction or
-   label.  If PRESERVE is non-zero, it remembers enough information to
-   know whether nops are needed before a noreorder section.  */
+/* Forget that there was any previous instruction or label.  */
 
 static void
-mips_no_prev_insn (preserve)
-     int preserve;
+mips_no_prev_insn (void)
 {
-  if (! preserve)
-    {
-      prev_insn.insn_mo = &dummy_opcode;
-      prev_prev_insn.insn_mo = &dummy_opcode;
-      prev_nop_frag = NULL;
-      prev_nop_frag_holds = 0;
-      prev_nop_frag_required = 0;
-      prev_nop_frag_since = 0;
-    }
-  prev_insn_valid = 0;
-  prev_insn_is_delay_slot = 0;
-  prev_insn_unreordered = 0;
-  prev_insn_extended = 0;
-  prev_insn_reloc_type[0] = BFD_RELOC_UNUSED;
-  prev_insn_reloc_type[1] = BFD_RELOC_UNUSED;
-  prev_insn_reloc_type[2] = BFD_RELOC_UNUSED;
-  prev_prev_insn_unreordered = 0;
+  prev_nop_frag = NULL;
+  insert_into_history (0, ARRAY_SIZE (history), NOP_INSN);
   mips_clear_insn_labels ();
 }
 
-/* This function must be called whenever we turn on noreorder or emit
-   something other than instructions.  It inserts any NOPS which might
-   be needed by the previous instruction, and clears the information
-   kept for the previous instructions.  The INSNS parameter is true if
-   instructions are to follow.  */
+/* This function must be called before we emit something other than
+   instructions.  It is like mips_no_prev_insn except that it inserts
+   any NOPS that might be needed by previous instructions.  */
 
-static void
-mips_emit_delays (insns)
-     bfd_boolean insns;
+void
+mips_emit_delays (void)
 {
   if (! mips_opts.noreorder)
     {
-      int nops;
-
-      nops = 0;
-      if ((! mips_opts.mips16
-          && ISA_HAS_COPROC_DELAYS (mips_opts.isa)
-          && (! cop_interlocks
-               && (prev_insn.insn_mo->pinfo
-                   & (INSN_LOAD_COPROC_DELAY
-                      | INSN_COPROC_MOVE_DELAY
-                      | INSN_WRITE_COND_CODE))))
-         || (! hilo_interlocks
-             && (prev_insn.insn_mo->pinfo
-                 & (INSN_READ_LO
-                    | INSN_READ_HI)))
-         || (! mips_opts.mips16
-             && ! gpr_interlocks
-             && (prev_insn.insn_mo->pinfo
-                  & INSN_LOAD_MEMORY_DELAY))
-         || (! mips_opts.mips16
-             && mips_opts.isa == ISA_MIPS1
-             && (prev_insn.insn_mo->pinfo
-                 & INSN_COPROC_MEMORY_DELAY)))
-       {
-         /* Itbl support may require additional care here.  */
-         ++nops;
-         if ((! mips_opts.mips16
-              && ISA_HAS_COPROC_DELAYS (mips_opts.isa)
-              && (! cop_interlocks
-                   && prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE))
-             || (! hilo_interlocks
-                 && ((prev_insn.insn_mo->pinfo & INSN_READ_HI)
-                     || (prev_insn.insn_mo->pinfo & INSN_READ_LO))))
-           ++nops;
-
-         if (prev_insn_unreordered)
-           nops = 0;
-       }
-      else if ((! mips_opts.mips16
-               && ISA_HAS_COPROC_DELAYS (mips_opts.isa)
-               && (! cop_interlocks
-                    && prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE))
-              || (! hilo_interlocks
-                  && ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
-                      || (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO))))
+      int nops = nops_for_insn (history, NULL);
+      if (nops > 0)
        {
-         /* Itbl support may require additional care here.  */
-         if (! prev_prev_insn_unreordered)
-           ++nops;
+         while (nops-- > 0)
+           add_fixed_insn (NOP_INSN);
+         mips_move_labels ();
        }
+    }
+  mips_no_prev_insn ();
+}
 
-      if (mips_fix_4122_bugs && prev_insn.insn_mo->name)
-       {
-         int min_nops = 0;
-         const char *pn = prev_insn.insn_mo->name;
-         if (strncmp(pn, "macc", 4) == 0
-             || strncmp(pn, "dmacc", 5) == 0
-             || strncmp(pn, "dmult", 5) == 0)
-           {
-             min_nops = 1;
-           }
-         if (nops < min_nops)
-           nops = min_nops;
-       }
+/* Start a (possibly nested) noreorder block.  */
+
+static void
+start_noreorder (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)
        {
-         struct insn_label_list *l;
-
-         if (insns)
+         if (mips_optimize != 0)
            {
              /* Record the frag which holds the nop instructions, so
                  that we can remove them if we don't need them.  */
@@ -2856,36 +2794,120 @@ mips_emit_delays (insns)
            }
 
          for (; nops > 0; --nops)
-           emit_nop ();
+           add_fixed_insn (NOP_INSN);
 
-         if (insns)
-           {
-             /* 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);
-           }
+         /* 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;
+}
 
-         for (l = insn_labels; l != NULL; l = l->next)
-           {
-             valueT val;
+/* End a nested noreorder block.  */
 
-             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)
-               ++val;
-             S_SET_VALUE (l->label, val);
-           }
+static void
+end_noreorder (void)
+{
+  mips_opts.noreorder--;
+  if (mips_opts.noreorder == 0 && prev_nop_frag != NULL)
+    {
+      /* Commit to inserting prev_nop_frag_required nops and go back to
+        handling nop insertion the .set reorder way.  */
+      prev_nop_frag->fr_fix -= ((prev_nop_frag_holds - prev_nop_frag_required)
+                               * (mips_opts.mips16 ? 2 : 4));
+      insert_into_history (prev_nop_frag_since,
+                          prev_nop_frag_required, NOP_INSN);
+      prev_nop_frag = NULL;
+    }
+}
+
+/* Set up global variables for the start of a new macro.  */
+
+static void
+macro_start (void)
+{
+  memset (&mips_macro_warning.sizes, 0, sizeof (mips_macro_warning.sizes));
+  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);
+}
+
+/* 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.  */
+
+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;
+}
+
+/* Finish up a macro.  Emit warnings as appropriate.  */
+
+static void
+macro_end (void)
+{
+  if (mips_macro_warning.sizes[0] > 4 || mips_macro_warning.sizes[1] > 4)
+    {
+      relax_substateT subtype;
+
+      /* 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;
+
+      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 (msg);
+       }
+      else
+       {
+         /* One implementation might need a warning but the other
+            definitely doesn't.  */
+         mips_macro_warning.first_frag->fr_subtype |= subtype;
        }
     }
+}
 
-  /* Mark instruction labels in mips16 mode.  */
-  if (insns)
-    mips16_mark_labels ();
+/* Read a macro's relocation codes from *ARGS and store them in *R.
+   The first argument in *ARGS will be either the code for a single
+   relocation or -1 followed by the three codes that make up a
+   composite relocation.  */
+
+static void
+macro_read_relocs (va_list *args, bfd_reloc_code_real_type *r)
+{
+  int i, next;
 
-  mips_no_prev_insn (insns);
+  next = va_arg (*args, int);
+  if (next >= 0)
+    r[0] = (bfd_reloc_code_real_type) next;
+  else
+    for (i = 0; i < 3; i++)
+      r[i] = (bfd_reloc_code_real_type) va_arg (*args, int);
 }
 
 /* Build an instruction created by a macro expansion.  This is passed
@@ -2893,61 +2915,19 @@ mips_emit_delays (insns)
    expression, the name of the instruction to build, an operand format
    string, and corresponding arguments.  */
 
-#ifdef USE_STDARG
-static void
-macro_build (char *place,
-            int *counter,
-            expressionS * ep,
-            const char *name,
-            const char *fmt,
-            ...)
-#else
 static void
-macro_build (place, counter, ep, name, fmt, va_alist)
-     char *place;
-     int *counter;
-     expressionS *ep;
-     const char *name;
-     const char *fmt;
-     va_dcl
-#endif
+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;
 
-#ifdef USE_STDARG
   va_start (args, fmt);
-#else
-  va_start (args);
-#endif
-
-  /*
-   * If the macro is about to expand into a second instruction,
-   * print a warning if needed. We need to pass ip as a parameter
-   * to generate a better warning message here...
-   */
-  if (mips_opts.warn_about_macros && place == NULL && *counter == 1)
-    as_warn (_("Macro instruction expanded into multiple instructions"));
-
-  /*
-   * If the macro is about to expand into a second instruction,
-   * and it is in a delay slot, print a warning.
-   */
-  if (place == NULL
-      && *counter == 1
-      && mips_opts.noreorder
-      && (prev_prev_insn.insn_mo->pinfo
-         & (INSN_UNCOND_BRANCH_DELAY | INSN_COND_BRANCH_DELAY
-            | INSN_COND_BRANCH_LIKELY)) != 0)
-    as_warn (_("Macro instruction expanded into multiple instructions in a branch delay slot"));
-
-  if (place == NULL)
-    ++*counter;                /* bump instruction counter */
 
   if (mips_opts.mips16)
     {
-      mips16_macro_build (place, counter, ep, name, fmt, args);
+      mips16_macro_build (ep, name, fmt, args);
       va_end (args);
       return;
     }
@@ -2955,30 +2935,26 @@ macro_build (place, counter, ep, name, fmt, va_alist)
   r[0] = BFD_RELOC_UNUSED;
   r[1] = BFD_RELOC_UNUSED;
   r[2] = BFD_RELOC_UNUSED;
-  insn.insn_mo = (struct mips_opcode *) hash_find (op_hash, name);
-  assert (insn.insn_mo);
-  assert (strcmp (name, insn.insn_mo->name) == 0);
-
-  /* Search until we get a match for NAME.  */
-  while (1)
-    {
-      /* It is assumed here that macros will never generate
-         MDMX or MIPS-3D instructions.  */
-      if (strcmp (fmt, insn.insn_mo->args) == 0
-         && insn.insn_mo->pinfo != INSN_MACRO
-         && OPCODE_IS_MEMBER (insn.insn_mo,
-                              (mips_opts.isa
-                               | (file_ase_mips16 ? INSN_MIPS16 : 0)),
-                              mips_arch)
-         && (mips_arch != CPU_R4650 || (insn.insn_mo->pinfo & FP_D) == 0))
-       break;
-
-      ++insn.insn_mo;
-      assert (insn.insn_mo->name);
-      assert (strcmp (name, insn.insn_mo->name) == 0);
-    }
-
-  insn.insn_opcode = insn.insn_mo->match;
+  mo = (struct mips_opcode *) hash_find (op_hash, name);
+  assert (mo);
+  assert (strcmp (name, mo->name) == 0);
+
+  /* Search until we get a match for NAME.  It is assumed here that
+     macros will never generate MDMX or MIPS-3D instructions.  */
+  while (strcmp (fmt, mo->args) != 0
+        || mo->pinfo == INSN_MACRO
+        || !OPCODE_IS_MEMBER (mo,
+                              (mips_opts.isa
+                               | (file_ase_mips16 ? INSN_MIPS16 : 0)),
+                              mips_opts.arch)
+        || (mips_opts.arch == CPU_R4650 && (mo->pinfo & FP_D) != 0))
+    {
+      ++mo;
+      assert (mo->name);
+      assert (strcmp (name, mo->name) == 0);
+    }
+
+  create_insn (&insn, mo);
   for (;;)
     {
       switch (*fmt++)
@@ -2991,75 +2967,107 @@ macro_build (place, counter, ep, name, fmt, va_alist)
        case ')':
          continue;
 
+       case '+':
+         switch (*fmt++)
+           {
+           case 'A':
+           case 'E':
+             INSERT_OPERAND (SHAMT, insn, va_arg (args, int));
+             continue;
+
+           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;
+
+           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;
+
+           default:
+             internalError ();
+           }
+         continue;
+
        case 't':
        case 'w':
        case 'E':
-         insn.insn_opcode |= va_arg (args, int) << OP_SH_RT;
+         INSERT_OPERAND (RT, insn, va_arg (args, int));
          continue;
 
        case 'c':
-         insn.insn_opcode |= va_arg (args, int) << OP_SH_CODE;
+         INSERT_OPERAND (CODE, insn, va_arg (args, int));
          continue;
 
        case 'T':
        case 'W':
-         insn.insn_opcode |= va_arg (args, int) << OP_SH_FT;
+         INSERT_OPERAND (FT, insn, va_arg (args, int));
          continue;
 
        case 'd':
        case 'G':
        case 'K':
-         insn.insn_opcode |= va_arg (args, int) << OP_SH_RD;
+         INSERT_OPERAND (RD, insn, va_arg (args, int));
          continue;
 
        case 'U':
          {
            int tmp = va_arg (args, int);
 
-           insn.insn_opcode |= tmp << OP_SH_RT;
-           insn.insn_opcode |= tmp << OP_SH_RD;
+           INSERT_OPERAND (RT, insn, tmp);
+           INSERT_OPERAND (RD, insn, tmp);
            continue;
          }
 
        case 'V':
        case 'S':
-         insn.insn_opcode |= va_arg (args, int) << OP_SH_FS;
+         INSERT_OPERAND (FS, insn, va_arg (args, int));
          continue;
 
        case 'z':
          continue;
 
        case '<':
-         insn.insn_opcode |= va_arg (args, int) << OP_SH_SHAMT;
+         INSERT_OPERAND (SHAMT, insn, va_arg (args, int));
          continue;
 
        case 'D':
-         insn.insn_opcode |= va_arg (args, int) << OP_SH_FD;
+         INSERT_OPERAND (FD, insn, va_arg (args, int));
          continue;
 
        case 'B':
-         insn.insn_opcode |= va_arg (args, int) << OP_SH_CODE20;
+         INSERT_OPERAND (CODE20, insn, va_arg (args, int));
          continue;
 
        case 'J':
-         insn.insn_opcode |= va_arg (args, int) << OP_SH_CODE19;
+         INSERT_OPERAND (CODE19, insn, va_arg (args, int));
          continue;
 
        case 'q':
-         insn.insn_opcode |= va_arg (args, int) << OP_SH_CODE2;
+         INSERT_OPERAND (CODE2, insn, va_arg (args, int));
          continue;
 
        case 'b':
        case 's':
        case 'r':
        case 'v':
-         insn.insn_opcode |= va_arg (args, int) << OP_SH_RS;
+         INSERT_OPERAND (RS, insn, va_arg (args, int));
          continue;
 
        case 'i':
        case 'j':
        case 'o':
-         *r = (bfd_reloc_code_real_type) va_arg (args, int);
+         macro_read_relocs (&args, r);
          assert (*r == BFD_RELOC_GPREL16
                  || *r == BFD_RELOC_MIPS_LITERAL
                  || *r == BFD_RELOC_MIPS_HIGHER
@@ -3071,13 +3079,11 @@ macro_build (place, counter, ep, name, fmt, va_alist)
                  || *r == BFD_RELOC_MIPS_GOT_PAGE
                  || *r == BFD_RELOC_MIPS_GOT_OFST
                  || *r == BFD_RELOC_MIPS_GOT_LO16
-                 || *r == BFD_RELOC_MIPS_CALL_LO16
-                 || (ep->X_op == O_subtract
-                     && *r == BFD_RELOC_PCREL_LO16));
+                 || *r == BFD_RELOC_MIPS_CALL_LO16);
          continue;
 
        case 'u':
-         *r = (bfd_reloc_code_real_type) va_arg (args, int);
+         macro_read_relocs (&args, r);
          assert (ep != NULL
                  && (ep->X_op == O_constant
                      || (ep->X_op == O_symbol
@@ -3086,9 +3092,7 @@ macro_build (place, counter, ep, name, fmt, va_alist)
                              || *r == BFD_RELOC_HI16
                              || *r == BFD_RELOC_GPREL16
                              || *r == BFD_RELOC_MIPS_GOT_HI16
-                             || *r == BFD_RELOC_MIPS_CALL_HI16))
-                     || (ep->X_op == O_subtract
-                         && *r == BFD_RELOC_PCREL_HI16_S)));
+                             || *r == BFD_RELOC_MIPS_CALL_HI16))));
          continue;
 
        case 'p':
@@ -3126,37 +3130,30 @@ macro_build (place, counter, ep, name, fmt, va_alist)
   va_end (args);
   assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
 
-  append_insn (place, &insn, ep, r);
+  append_insn (&insn, ep, r);
 }
 
 static void
-mips16_macro_build (place, counter, ep, name, fmt, args)
-     char *place;
-     int *counter ATTRIBUTE_UNUSED;
-     expressionS *ep;
-     const char *name;
-     const char *fmt;
-     va_list args;
+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};
 
-  insn.insn_mo = (struct mips_opcode *) hash_find (mips16_op_hash, name);
-  assert (insn.insn_mo);
-  assert (strcmp (name, insn.insn_mo->name) == 0);
+  mo = (struct mips_opcode *) hash_find (mips16_op_hash, name);
+  assert (mo);
+  assert (strcmp (name, mo->name) == 0);
 
-  while (strcmp (fmt, insn.insn_mo->args) != 0
-        || insn.insn_mo->pinfo == INSN_MACRO)
+  while (strcmp (fmt, mo->args) != 0 || mo->pinfo == INSN_MACRO)
     {
-      ++insn.insn_mo;
-      assert (insn.insn_mo->name);
-      assert (strcmp (name, insn.insn_mo->name) == 0);
+      ++mo;
+      assert (mo->name);
+      assert (strcmp (name, mo->name) == 0);
     }
 
-  insn.insn_opcode = insn.insn_mo->match;
-  insn.use_extend = FALSE;
-
+  create_insn (&insn, mo);
   for (;;)
     {
       int c;
@@ -3174,20 +3171,20 @@ mips16_macro_build (place, counter, ep, name, fmt, args)
 
        case 'y':
        case 'w':
-         insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_RY;
+         MIPS16_INSERT_OPERAND (RY, insn, va_arg (args, int));
          continue;
 
        case 'x':
        case 'v':
-         insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_RX;
+         MIPS16_INSERT_OPERAND (RX, insn, va_arg (args, int));
          continue;
 
        case 'z':
-         insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_RZ;
+         MIPS16_INSERT_OPERAND (RZ, insn, va_arg (args, int));
          continue;
 
        case 'Z':
-         insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_MOVE32Z;
+         MIPS16_INSERT_OPERAND (MOVE32Z, insn, va_arg (args, int));
          continue;
 
        case '0':
@@ -3197,7 +3194,7 @@ mips16_macro_build (place, counter, ep, name, fmt, args)
          continue;
 
        case 'X':
-         insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_REGR32;
+         MIPS16_INSERT_OPERAND (REGR32, insn, va_arg (args, int));
          continue;
 
        case 'Y':
@@ -3243,7 +3240,7 @@ mips16_macro_build (place, counter, ep, name, fmt, args)
          continue;
 
        case '6':
-         insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_IMM6;
+         MIPS16_INSERT_OPERAND (IMM6, insn, va_arg (args, int));
          continue;
        }
 
@@ -3252,7 +3249,7 @@ mips16_macro_build (place, counter, ep, name, fmt, args)
 
   assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
 
-  append_insn (place, &insn, ep, r);
+  append_insn (&insn, ep, r);
 }
 
 /*
@@ -3260,19 +3257,16 @@ mips16_macro_build (place, counter, ep, name, fmt, args)
  * function.  This occurs in NewABI PIC code.
  */
 static void
-macro_build_jalr (icnt, ep)
-     int icnt;
-     expressionS *ep;
+macro_build_jalr (expressionS *ep)
 {
-  char *f = NULL;;
+  char *f = NULL;
 
   if (HAVE_NEWABI)
     {
-      frag_grow (4);
+      frag_grow (8);
       f = frag_more (0);
     }
-  macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "jalr", "d,s",
-              RA, PIC_CALL_REG);
+  macro_build (NULL, "jalr", "d,s", RA, PIC_CALL_REG);
   if (HAVE_NEWABI)
     fix_new_exp (frag_now, f - frag_now->fr_literal,
                 4, ep, FALSE, BFD_RELOC_MIPS_JALR);
@@ -3282,13 +3276,10 @@ macro_build_jalr (icnt, ep)
  * Generate a "lui" instruction.
  */
 static void
-macro_build_lui (place, counter, ep, regnum)
-     char *place;
-     int *counter;
-     expressionS *ep;
-     int regnum;
+macro_build_lui (expressionS *ep, int regnum)
 {
   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};
@@ -3297,13 +3288,7 @@ macro_build_lui (place, counter, ep, regnum)
 
   assert (! mips_opts.mips16);
 
-  if (place == NULL)
-    high_expr = *ep;
-  else
-    {
-      high_expr.X_op = O_constant;
-      high_expr.X_add_number = ep->X_add_number;
-    }
+  high_expr = *ep;
 
   if (high_expr.X_op == O_constant)
     {
@@ -3315,61 +3300,61 @@ macro_build_lui (place, counter, ep, regnum)
   else
     {
       assert (ep->X_op == O_symbol);
-      /* _gp_disp is a special case, used from s_cpload.  */
+      /* _gp_disp is a special case, used from s_cpload.
+        __gnu_local_gp is used if mips_no_shared.  */
       assert (mips_pic == NO_PIC
              || (! HAVE_NEWABI
-                 && strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0));
+                 && 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 the macro is about to expand into a second instruction,
-   * print a warning if needed. We need to pass ip as a parameter
-   * to generate a better warning message here...
-   */
-  if (mips_opts.warn_about_macros && place == NULL && *counter == 1)
-    as_warn (_("Macro instruction expanded into multiple instructions"));
-
-  if (place == NULL)
-    ++*counter;                /* bump instruction counter */
-
-  insn.insn_mo = (struct mips_opcode *) hash_find (op_hash, name);
-  assert (insn.insn_mo);
-  assert (strcmp (name, insn.insn_mo->name) == 0);
-  assert (strcmp (fmt, insn.insn_mo->args) == 0);
+  mo = hash_find (op_hash, name);
+  assert (strcmp (name, mo->name) == 0);
+  assert (strcmp (fmt, mo->args) == 0);
+  create_insn (&insn, mo);
 
-  insn.insn_opcode = insn.insn_mo->match | (regnum << OP_SH_RT);
+  insn.insn_opcode = insn.insn_mo->match;
+  INSERT_OPERAND (RT, insn, regnum);
   if (*r == BFD_RELOC_UNUSED)
     {
       insn.insn_opcode |= high_expr.X_add_number;
-      append_insn (place, &insn, NULL, r);
+      append_insn (&insn, NULL, r);
     }
   else
-    append_insn (place, &insn, &high_expr, r);
+    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 (place, counter, ep, op, treg, breg)
-     char *place;
-     int *counter;
-     expressionS *ep;
-     const char *op;
-     int treg, breg;
+macro_build_ldst_constoffset (expressionS *ep, const char *op,
+                             int treg, int breg, int dbl)
 {
   assert (ep->X_op == O_constant);
 
-  /* Right now, this routine can only handle signed 32-bit contants.  */
-  if (! IS_SEXT_32BIT_NUM(ep->X_add_number))
+  /* Sign-extending 32-bit constants makes their handling easier.  */
+  if (! dbl && ! ((ep->X_add_number & ~((bfd_vma) 0x7fffffff))
+                 == ~((bfd_vma) 0x7fffffff)))
+    {
+      if (ep->X_add_number & ~((bfd_vma) 0xffffffff))
+       as_bad (_("constant too large"));
+
+      ep->X_add_number = (((ep->X_add_number & 0xffffffff) ^ 0x80000000)
+                         - 0x80000000);
+    }
+
+  /* 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))
     {
       /* Signed 16-bit offset will fit in the op.  Easy!  */
-      macro_build (place, counter, ep, op, "t,o(b)", treg,
-                  (int) BFD_RELOC_LO16, breg);
+      macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, breg);
     }
   else
     {
@@ -3378,20 +3363,12 @@ macro_build_ldst_constoffset (place, counter, ep, op, treg, breg)
           addu     $tempreg,$tempreg,$breg
            <op>     $treg,const_lo($tempreg)   (BFD_RELOC_LO16)
          to handle the complete offset.  */
-      macro_build_lui (place, counter, ep, AT);
-      if (place != NULL)
-       place += 4;
-      macro_build (place, counter, (expressionS *) NULL,
-                  HAVE_32BIT_ADDRESSES ? HAVE_NEWABI
-                  ? "add" : "addu" : "daddu",
-                  "d,v,t", AT, AT, breg);
-      if (place != NULL)
-       place += 4;
-      macro_build (place, counter, ep, op, "t,o(b)", treg,
-                  (int) BFD_RELOC_LO16, AT);
+      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.noat)
-       as_warn (_("Macro used $at after \".set noat\""));
+       as_bad (_("Macro used $at after \".set noat\""));
     }
 }
 
@@ -3400,37 +3377,39 @@ macro_build_ldst_constoffset (place, counter, ep, op, treg, breg)
  * if reg is less than the immediate expression.
  */
 static void
-set_at (counter, reg, unsignedp)
-     int *counter;
-     int reg;
-     int unsignedp;
+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 ((char *) NULL, counter, &imm_expr,
-                unsignedp ? "sltiu" : "slti",
-                "t,r,j", AT, reg, (int) BFD_RELOC_LO16);
+    macro_build (&imm_expr, unsignedp ? "sltiu" : "slti", "t,r,j",
+                AT, reg, BFD_RELOC_LO16);
   else
     {
-      load_register (counter, AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build ((char *) NULL, counter, (expressionS *) NULL,
-                  unsignedp ? "sltu" : "slt",
-                  "d,v,t", AT, reg, AT);
+      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+      macro_build (NULL, unsignedp ? "sltu" : "slt", "d,v,t", AT, reg, AT);
     }
 }
 
+static void
+normalize_constant_expr (expressionS *ex)
+{
+  if (ex->X_op == O_constant && HAVE_32BIT_GPRS)
+    ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
+                       - 0x80000000);
+}
+
 /* Warn if an expression is not a constant.  */
 
 static void
-check_absolute_expr (ip, ex)
-     struct mips_cl_insn *ip;
-     expressionS *ex;
+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);
+
+  normalize_constant_expr (ex);
 }
 
 /* Count the leading zeroes by performing a binary chop. This is a
@@ -3510,15 +3489,11 @@ check_absolute_expr (ip, ex)
            : 0)
 
 /*                     load_register()
- *  This routine generates the least number of instructions neccessary to load
+ *  This routine generates the least number of instructions necessary to load
  *  an absolute expression value into a register.
  */
 static void
-load_register (counter, reg, ep, dbl)
-     int *counter;
-     int reg;
-     expressionS *ep;
-     int dbl;
+load_register (int reg, expressionS *ep, int dbl)
 {
   int freg;
   expressionS hi32, lo32;
@@ -3526,46 +3501,39 @@ load_register (counter, reg, ep, dbl)
   if (ep->X_op != O_big)
     {
       assert (ep->X_op == O_constant);
-      if (ep->X_add_number < 0x8000
-         && (ep->X_add_number >= 0
-             || (ep->X_add_number >= -0x8000
-                 && (! dbl
-                     || ! ep->X_unsigned
-                     || sizeof (ep->X_add_number) > 4))))
+
+      /* Sign-extending 32-bit constants makes their handling easier.  */
+      if (! dbl && ! ((ep->X_add_number & ~((bfd_vma) 0x7fffffff))
+                     == ~((bfd_vma) 0x7fffffff)))
+       {
+         if (ep->X_add_number & ~((bfd_vma) 0xffffffff))
+           as_bad (_("constant too large"));
+
+         ep->X_add_number = (((ep->X_add_number & 0xffffffff) ^ 0x80000000)
+                             - 0x80000000);
+       }
+
+      if (IS_SEXT_16BIT_NUM (ep->X_add_number))
        {
          /* 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 ((char *) NULL, counter, ep, "addiu", "t,r,j", reg, 0,
-                      (int) BFD_RELOC_LO16);
+         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 ((char *) NULL, counter, ep, "ori", "t,r,i", reg, 0,
-                      (int) BFD_RELOC_LO16);
+         macro_build (ep, "ori", "t,r,i", reg, 0, BFD_RELOC_LO16);
          return;
        }
-      else if ((IS_SEXT_32BIT_NUM (ep->X_add_number)
-               && (! dbl
-                   || ! ep->X_unsigned
-                   || sizeof (ep->X_add_number) > 4
-                   || (ep->X_add_number & 0x80000000) == 0))
-              || ((HAVE_32BIT_GPRS || ! dbl)
-                  && (ep->X_add_number &~ (offsetT) 0xffffffff) == 0)
-              || (HAVE_32BIT_GPRS
-                  && ! dbl
-                  && ((ep->X_add_number &~ (offsetT) 0xffffffff)
-                      == ~ (offsetT) 0xffffffff)))
+      else if ((IS_SEXT_32BIT_NUM (ep->X_add_number)))
        {
          /* 32 bit values require an lui.  */
-         macro_build ((char *) NULL, counter, ep, "lui", "t,u", reg,
-                      (int) BFD_RELOC_HI16);
+         macro_build (ep, "lui", "t,u", reg, BFD_RELOC_HI16);
          if ((ep->X_add_number & 0xffff) != 0)
-           macro_build ((char *) NULL, counter, ep, "ori", "t,r,i", reg, reg,
-                        (int) BFD_RELOC_LO16);
+           macro_build (ep, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
          return;
        }
     }
@@ -3576,8 +3544,7 @@ load_register (counter, reg, ep, dbl)
     {
       as_bad (_("Number (0x%lx) larger than 32 bits"),
              (unsigned long) ep->X_add_number);
-      macro_build ((char *) NULL, counter, ep, "addiu", "t,r,j", reg, 0,
-                  (int) BFD_RELOC_LO16);
+      macro_build (ep, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
       return;
     }
 
@@ -3614,17 +3581,14 @@ load_register (counter, reg, ep, dbl)
        {
          if ((lo32.X_add_number & 0xffff8000) == 0xffff8000)
            {
-             macro_build ((char *) NULL, counter, &lo32, "addiu", "t,r,j",
-                          reg, 0, (int) BFD_RELOC_LO16);
+             macro_build (&lo32, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
              return;
            }
          if (lo32.X_add_number & 0x80000000)
            {
-             macro_build ((char *) NULL, counter, &lo32, "lui", "t,u", reg,
-                          (int) BFD_RELOC_HI16);
+             macro_build (&lo32, "lui", "t,u", reg, BFD_RELOC_HI16);
              if (lo32.X_add_number & 0xffff)
-               macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i",
-                            reg, reg, (int) BFD_RELOC_LO16);
+               macro_build (&lo32, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
              return;
            }
        }
@@ -3658,13 +3622,9 @@ load_register (counter, reg, ep, dbl)
                                    | (lo32.X_add_number >> shift));
              else
                tmp.X_add_number = hi32.X_add_number >> (shift - 32);
-             macro_build ((char *) NULL, counter, &tmp,
-                          "ori", "t,r,i", reg, 0,
-                          (int) BFD_RELOC_LO16);
-             macro_build ((char *) NULL, counter, (expressionS *) NULL,
-                          (shift >= 32) ? "dsll32" : "dsll",
-                          "d,w,<", reg, reg,
-                          (shift >= 32) ? shift - 32 : shift);
+             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;
@@ -3711,20 +3671,15 @@ load_register (counter, reg, ep, dbl)
                  ones.  */
              tmp.X_op = O_constant;
              tmp.X_add_number = (offsetT) -1;
-             macro_build ((char *) NULL, counter, &tmp, "addiu", "t,r,j",
-                          reg, 0, (int) BFD_RELOC_LO16);
+             macro_build (&tmp, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
              if (bit != 0)
                {
                  bit += shift;
-                 macro_build ((char *) NULL, counter, (expressionS *) NULL,
-                              (bit >= 32) ? "dsll32" : "dsll",
-                              "d,w,<", reg, reg,
-                              (bit >= 32) ? bit - 32 : bit);
+                 macro_build (NULL, (bit >= 32) ? "dsll32" : "dsll", "d,w,<",
+                              reg, reg, (bit >= 32) ? bit - 32 : bit);
                }
-             macro_build ((char *) NULL, counter, (expressionS *) NULL,
-                          (shift >= 32) ? "dsrl32" : "dsrl",
-                          "d,w,<", reg, reg,
-                          (shift >= 32) ? shift - 32 : shift);
+             macro_build (NULL, (shift >= 32) ? "dsrl32" : "dsrl", "d,w,<",
+                          reg, reg, (shift >= 32) ? shift - 32 : shift);
              return;
            }
        }
@@ -3733,15 +3688,14 @@ load_register (counter, reg, ep, dbl)
          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 (counter, reg, &hi32, 0);
+      load_register (reg, &hi32, 0);
       freg = reg;
     }
   if ((lo32.X_add_number & 0xffff0000) == 0)
     {
       if (freg != 0)
        {
-         macro_build ((char *) NULL, counter, (expressionS *) NULL,
-                      "dsll32", "d,w,<", reg, freg, 0);
+         macro_build (NULL, "dsll32", "d,w,<", reg, freg, 0);
          freg = reg;
        }
     }
@@ -3751,43 +3705,38 @@ load_register (counter, reg, ep, dbl)
 
       if ((freg == 0) && (lo32.X_add_number == (offsetT) 0xffffffff))
        {
-         macro_build ((char *) NULL, counter, &lo32, "lui", "t,u", reg,
-                      (int) BFD_RELOC_HI16);
-         macro_build ((char *) NULL, counter, (expressionS *) NULL,
-                      "dsrl32", "d,w,<", reg, reg, 0);
+         macro_build (&lo32, "lui", "t,u", reg, BFD_RELOC_HI16);
+         macro_build (NULL, "dsrl32", "d,w,<", reg, reg, 0);
          return;
        }
 
       if (freg != 0)
        {
-         macro_build ((char *) NULL, counter, (expressionS *) NULL, "dsll",
-                      "d,w,<", reg, freg, 16);
+         macro_build (NULL, "dsll", "d,w,<", reg, freg, 16);
          freg = reg;
        }
       mid16 = lo32;
       mid16.X_add_number >>= 16;
-      macro_build ((char *) NULL, counter, &mid16, "ori", "t,r,i", reg,
-                  freg, (int) BFD_RELOC_LO16);
-      macro_build ((char *) NULL, counter, (expressionS *) NULL, "dsll",
-                  "d,w,<", reg, reg, 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 ((char *) NULL, counter, &lo32, "ori", "t,r,i", reg, freg,
-                (int) BFD_RELOC_LO16);
+    macro_build (&lo32, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
+}
+
+static inline void
+load_delay_nop (void)
+{
+  if (!gpr_interlocks)
+    macro_build (NULL, "nop", "");
 }
 
 /* Load an address into a register.  */
 
 static void
-load_address (counter, reg, ep, used_at)
-     int *counter;
-     int reg;
-     expressionS *ep;
-     int *used_at;
+load_address (int reg, expressionS *ep, int *used_at)
 {
-  char *p = NULL;
-
   if (ep->X_op != O_constant
       && ep->X_op != O_symbol)
     {
@@ -3797,7 +3746,7 @@ load_address (counter, reg, ep, used_at)
 
   if (ep->X_op == O_constant)
     {
-      load_register (counter, reg, ep, HAVE_64BIT_ADDRESSES);
+      load_register (reg, ep, HAVE_64BIT_ADDRESSES);
       return;
     }
 
@@ -3826,66 +3775,60 @@ load_address (counter, reg, ep, used_at)
           daddiu       $reg,<sym>              (BFD_RELOC_HI16_S)
           dsll         $reg,16
           daddiu       $reg,<sym>              (BFD_RELOC_LO16)
-       */
-      if (HAVE_64BIT_ADDRESSES)
+
+        For GP relative symbols in 64bit address space we can use
+        the same sequence as in 32bit address space.  */
+      if (HAVE_64BIT_SYMBOLS)
        {
-         /* We don't do GP optimization for now because RELAX_ENCODE can't
-            hold the data for such large chunks.  */
+         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.noat)
+         if (*used_at == 0 && !mips_opts.noat)
            {
-             macro_build (p, counter, ep, "lui", "t,u",
-                          reg, (int) BFD_RELOC_MIPS_HIGHEST);
-             macro_build (p, counter, ep, "lui", "t,u",
-                          AT, (int) BFD_RELOC_HI16_S);
-             macro_build (p, counter, ep, "daddiu", "t,r,j",
-                          reg, reg, (int) BFD_RELOC_MIPS_HIGHER);
-             macro_build (p, counter, ep, "daddiu", "t,r,j",
-                          AT, AT, (int) BFD_RELOC_LO16);
-             macro_build (p, counter, (expressionS *) NULL, "dsll32",
-                          "d,w,<", reg, reg, 0);
-             macro_build (p, counter, (expressionS *) NULL, "daddu",
-                          "d,v,t", reg, reg, AT);
+             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;
            }
          else
            {
-             macro_build (p, counter, ep, "lui", "t,u",
-                          reg, (int) BFD_RELOC_MIPS_HIGHEST);
-             macro_build (p, counter, ep, "daddiu", "t,r,j",
-                          reg, reg, (int) BFD_RELOC_MIPS_HIGHER);
-             macro_build (p, counter, (expressionS *) NULL, "dsll",
-                          "d,w,<", reg, reg, 16);
-             macro_build (p, counter, ep, "daddiu", "t,r,j",
-                          reg, reg, (int) BFD_RELOC_HI16_S);
-             macro_build (p, counter, (expressionS *) NULL, "dsll",
-                          "d,w,<", reg, reg, 16);
-             macro_build (p, counter, ep, "daddiu", "t,r,j",
-                          reg, reg, (int) BFD_RELOC_LO16);
+             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);
            }
+
+         if (mips_relax.sequence)
+           relax_end ();
        }
       else
        {
          if ((valueT) ep->X_add_number <= MAX_GPREL_OFFSET
-             && ! nopic_need_relax (ep->X_add_symbol, 1))
+             && !nopic_need_relax (ep->X_add_symbol, 1))
            {
-             frag_grow (20);
-             macro_build ((char *) NULL, counter, ep,
-                          HAVE_32BIT_ADDRESSES ? HAVE_NEWABI
-                          ? "addi" : "addiu" : "daddiu", "t,r,j",
-                          reg, mips_gp_register, (int) BFD_RELOC_GPREL16);
-             p = frag_var (rs_machine_dependent, 8, 0,
-                           RELAX_ENCODE (4, 8, 0, 4, 0,
-                                         mips_opts.warn_about_macros),
-                           ep->X_add_symbol, 0, NULL);
+             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 (p, counter, ep, reg);
-         if (p != NULL)
-           p += 4;
-         macro_build (p, counter, ep,
-                      HAVE_32BIT_ADDRESSES ? HAVE_NEWABI
-                      ? "addi" : "addiu" : "daddiu",
-                      "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+         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_pic == SVR4_PIC && ! mips_big_got)
@@ -3906,72 +3849,52 @@ load_address (counter, reg, ep, used_at)
          offset, in which case cst must be added separately.  */
       if (HAVE_NEWABI)
        {
-         frag_grow (12);
-
          if (ep->X_add_number)
            {
-             frag_now->tc_frag_data.tc_fr_offset =
-               ex.X_add_number = ep->X_add_number;
+             ex.X_add_number = ep->X_add_number;
              ep->X_add_number = 0;
-             macro_build ((char *) NULL, counter, ep,
-                          HAVE_32BIT_ADDRESSES ? "lw" : "ld", "t,o(b)", reg,
-                          (int) BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+             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 ((char *) NULL, counter, &ex,
-                          HAVE_32BIT_ADDRESSES ? "addi" : "daddiu",
-                          "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
-             p = frag_var (rs_machine_dependent, 8, 0,
-                           RELAX_ENCODE (8, 4, 0, 0, 0,
-                                         mips_opts.warn_about_macros),
-                           ep->X_add_symbol, 0, (char *) NULL);
+             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 (p, counter, ep,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld", "t,o(b)", reg,
-                      (int) BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
-
-         if (! p)
-           {
-             /* To avoid confusion in tc_gen_reloc, we must ensure
-                that this does not become a variant frag.  */
-             frag_wane (frag_now);
-             frag_new (0);
-           }
+         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;
-         frag_grow (20);
-         macro_build ((char *) NULL, counter, ep,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld", "t,o(b)",
-                      reg, (int) BFD_RELOC_MIPS_GOT16, mips_gp_register);
-         macro_build ((char *) NULL, counter, (expressionS *) NULL, "nop", "");
-         p = frag_var (rs_machine_dependent, 4, 0,
-                       RELAX_ENCODE (0, 4, -8, 0, 0, mips_opts.warn_about_macros),
-                       ep->X_add_symbol, (offsetT) 0, (char *) NULL);
-         macro_build (p, counter, ep,
-                      HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
-                      "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+         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 ((char *) NULL, counter, &ex,
-                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
-                          "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+             macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j",
+                          reg, reg, BFD_RELOC_LO16);
            }
        }
     }
   else if (mips_pic == SVR4_PIC)
     {
       expressionS ex;
-      int off;
 
       /* This is the large GOT case.  If this is a reference to an
         external symbol, we want
@@ -3991,116 +3914,156 @@ load_address (counter, reg, ep, used_at)
       */
       if (HAVE_NEWABI)
        {
-         frag_grow (24);
-
-         frag_now->tc_frag_data.tc_fr_offset =
-           ex.X_add_number = ep->X_add_number;
+         ex.X_add_number = ep->X_add_number;
          ep->X_add_number = 0;
-         macro_build ((char *) NULL, counter, ep, "lui", "t,u", reg,
-                      (int) BFD_RELOC_MIPS_GOT_HI16);
-         macro_build ((char *) NULL, counter, (expressionS *) NULL,
-                      HAVE_32BIT_ADDRESSES ? "add" : "daddu", "d,v,t", reg,
-                      reg, mips_gp_register);
-         macro_build ((char *) NULL, counter, ep,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                      "t,o(b)", reg, (int) BFD_RELOC_MIPS_GOT_LO16, reg);
+         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 ((char *) NULL, counter, &ex,
-                          HAVE_32BIT_ADDRESSES ? "addi" : "daddiu",
-                          "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+             macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+                          BFD_RELOC_LO16);
            }
 
          ep->X_add_number = ex.X_add_number;
-         p = frag_var (rs_machine_dependent, 8, 0,
-                       RELAX_ENCODE (ex.X_add_number ? 16 : 12, 8, 0, 4, 0,
-                                     mips_opts.warn_about_macros),
-                       ep->X_add_symbol, 0, (char *) NULL);
-         macro_build (p, counter, ep,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld", "t,o(b)", reg,
-                      (int) BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
-         macro_build (p + 4, counter, ep,
-                      HAVE_32BIT_ADDRESSES ? "addi" : "daddiu", "t,r,j",
-                      reg, reg, (int) BFD_RELOC_MIPS_GOT_OFST);
+         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))
-           off = 4;
-         else
-           off = 0;
-         frag_grow (32);
-         macro_build ((char *) NULL, counter, ep, "lui", "t,u", reg,
-                      (int) BFD_RELOC_MIPS_GOT_HI16);
-         macro_build ((char *) NULL, counter, (expressionS *) NULL,
-                      HAVE_32BIT_ADDRESSES ? "addu" : "daddu", "d,v,t", reg,
-                      reg, mips_gp_register);
-         macro_build ((char *) NULL, counter, ep,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                      "t,o(b)", reg, (int) BFD_RELOC_MIPS_GOT_LO16, reg);
-         p = frag_var (rs_machine_dependent, 12 + off, 0,
-                       RELAX_ENCODE (12, 12 + off, off, 8 + off, 0,
-                                     mips_opts.warn_about_macros),
-                       ep->X_add_symbol, 0, NULL);
-         if (off > 0)
            {
              /* 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 (p, counter, (expressionS *) NULL, "nop", "");
-               p += 4;
+             macro_build (NULL, "nop", "");
            }
-         macro_build (p, counter, ep,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld", "t,o(b)", reg,
-                      (int) BFD_RELOC_MIPS_GOT16, mips_gp_register);
-         p += 4;
-         macro_build (p, counter, (expressionS *) NULL, "nop", "");
-         p += 4;
-         macro_build (p, counter, ep,
-                      HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
-                      "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+         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 ((char *) NULL, counter, &ex,
-                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
-                          "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+             macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+                          BFD_RELOC_LO16);
            }
        }
     }
-  else if (mips_pic == EMBEDDED_PIC)
-    {
-      /* We always do
-          addiu        $reg,$gp,<sym>          (BFD_RELOC_GPREL16)
-       */
-      macro_build ((char *) NULL, counter, ep,
-                  HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
-                  "t,r,j", reg, mips_gp_register, (int) BFD_RELOC_GPREL16);
-    }
   else
     abort ();
+
+  if (mips_opts.noat && *used_at == 1)
+    as_bad (_("Macro used $at after \".set noat\""));
 }
 
 /* Move the contents of register SOURCE into register DEST.  */
 
 static void
-move_register (counter, dest, source)
-     int *counter;
-     int dest;
-     int source;
+move_register (int dest, int source)
+{
+  macro_build (NULL, HAVE_32BIT_GPRS ? "addu" : "daddu", "d,v,t",
+              dest, source, 0);
+}
+
+/* Emit an SVR4 PIC sequence to load address LOCAL into DEST, where
+   LOCAL is the sum of a symbol and a 16-bit or 32-bit displacement.
+   The two alternatives are:
+
+   Global symbol               Local sybmol
+   -------------               ------------
+   lw DEST,%got(SYMBOL)                lw DEST,%got(SYMBOL + OFFSET)
+   ...                         ...
+   addiu DEST,DEST,OFFSET      addiu DEST,DEST,%lo(SYMBOL + OFFSET)
+
+   load_got_offset emits the first instruction and add_got_offset
+   emits the second for a 16-bit offset or add_got_offset_hilo emits
+   a sequence to add a 32-bit offset using a scratch register.  */
+
+static void
+load_got_offset (int dest, expressionS *local)
 {
-  macro_build ((char *) NULL, counter, (expressionS *) NULL,
-              HAVE_32BIT_GPRS ? "addu" : "daddu",
-              "d,v,t", dest, source, 0);
+  expressionS global;
+
+  global = *local;
+  global.X_add_number = 0;
+
+  relax_start (local->X_add_symbol);
+  macro_build (&global, ADDRESS_LOAD_INSN, "t,o(b)", dest,
+              BFD_RELOC_MIPS_GOT16, mips_gp_register);
+  relax_switch ();
+  macro_build (local, ADDRESS_LOAD_INSN, "t,o(b)", dest,
+              BFD_RELOC_MIPS_GOT16, mips_gp_register);
+  relax_end ();
+}
+
+static void
+add_got_offset (int dest, expressionS *local)
+{
+  expressionS global;
+
+  global.X_op = O_constant;
+  global.X_op_symbol = NULL;
+  global.X_add_symbol = NULL;
+  global.X_add_number = local->X_add_number;
+
+  relax_start (local->X_add_symbol);
+  macro_build (&global, ADDRESS_ADDI_INSN, "t,r,j",
+              dest, dest, BFD_RELOC_LO16);
+  relax_switch ();
+  macro_build (local, ADDRESS_ADDI_INSN, "t,r,j", dest, dest, BFD_RELOC_LO16);
+  relax_end ();
+}
+
+static void
+add_got_offset_hilo (int dest, expressionS *local, int tmp)
+{
+  expressionS global;
+  int hold_mips_optimize;
+
+  global.X_op = O_constant;
+  global.X_op_symbol = NULL;
+  global.X_add_symbol = NULL;
+  global.X_add_number = local->X_add_number;
+
+  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 ();
+
+  macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dest, dest, tmp);
 }
 
 /*
@@ -4108,11 +4071,11 @@ move_register (counter, dest, source)
  *   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 grammer aproach instead of
+ * 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
- * consecutivly that would load AT with the upper half of the same address.
+ * 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
@@ -4122,13 +4085,11 @@ move_register (counter, dest, source)
  * we're missing.
  */
 static void
-macro (ip)
-     struct mips_cl_insn *ip;
+macro (struct mips_cl_insn *ip)
 {
   register int treg, sreg, dreg, breg;
   int tempreg;
   int mask;
-  int icnt = 0;
   int used_at = 0;
   expressionS expr1;
   const char *s;
@@ -4139,8 +4100,9 @@ macro (ip)
   int coproc = 0;
   int lr = 0;
   int imm = 0;
-  offsetT maxnum;
+  int call = 0;
   int off;
+  offsetT maxnum;
   bfd_reloc_code_real_type r;
   int hold_mips_optimize;
 
@@ -4156,30 +4118,6 @@ macro (ip)
   expr1.X_add_symbol = NULL;
   expr1.X_add_number = 1;
 
-  /* Umatched fixups should not be put in the same frag as a relaxable
-     macro.  For example, suppose we have:
-
-       lui $4,%hi(l1)          # 1
-       la $5,l2                # 2
-       addiu $4,$4,%lo(l1)     # 3
-
-     If instructions 1 and 2 were put in the same frag, md_frob_file would
-     move the fixup for #1 after the fixups for the "unrelaxed" version of
-     #2.  This would confuse tc_gen_reloc, which expects the relocations
-     for #2 to be the last for that frag.
-
-     Also, if tc_gen_reloc sees certain relocations in a variant frag,
-     it assumes that they belong to a relaxable macro.  We mustn't put
-     other uses of such relocations into a variant frag.
-
-     To avoid both problems, finish the current frag it contains a
-     %reloc() operator.  The macro then goes into a new frag.  */
-  if (prev_reloc_op_frag == frag_now)
-    {
-      frag_wane (frag_now);
-      frag_new (0);
-    }
-
   switch (mask)
     {
     case M_DABS:
@@ -4190,22 +4128,18 @@ macro (ip)
         sub v0,$zero,$a0
         */
 
-      mips_emit_delays (TRUE);
-      ++mips_opts.noreorder;
-      mips_any_noreorder = 1;
+      start_noreorder ();
 
       expr1.X_add_number = 8;
-      macro_build ((char *) NULL, &icnt, &expr1, "bgez", "s,p", sreg);
+      macro_build (&expr1, "bgez", "s,p", sreg);
       if (dreg == sreg)
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "",
-                    0);
+       macro_build (NULL, "nop", "", 0);
       else
-       move_register (&icnt, dreg, sreg);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                  dbl ? "dsub" : "sub", "d,v,t", dreg, 0, sreg);
+       move_register (dreg, sreg);
+      macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", dreg, 0, sreg);
 
-      --mips_opts.noreorder;
-      return;
+      end_noreorder ();
+      break;
 
     case M_ADD_I:
       s = "addi";
@@ -4229,13 +4163,12 @@ macro (ip)
          && imm_expr.X_add_number >= -0x8000
          && imm_expr.X_add_number < 0x8000)
        {
-         macro_build ((char *) NULL, &icnt, &imm_expr, s, "t,r,j", treg, sreg,
-                      (int) BFD_RELOC_LO16);
-         return;
+         macro_build (&imm_expr, s, "t,r,j", treg, sreg, BFD_RELOC_LO16);
+         break;
        }
-      load_register (&icnt, AT, &imm_expr, dbl);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s2, "d,v,t",
-                  treg, sreg, AT);
+      used_at = 1;
+      load_register (AT, &imm_expr, dbl);
+      macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
       break;
 
     case M_AND_I:
@@ -4259,21 +4192,19 @@ macro (ip)
          && imm_expr.X_add_number < 0x10000)
        {
          if (mask != M_NOR_I)
-           macro_build ((char *) NULL, &icnt, &imm_expr, s, "t,r,i", treg,
-                        sreg, (int) BFD_RELOC_LO16);
+           macro_build (&imm_expr, s, "t,r,i", treg, sreg, BFD_RELOC_LO16);
          else
            {
-             macro_build ((char *) NULL, &icnt, &imm_expr, "ori", "t,r,i",
-                          treg, sreg, (int) BFD_RELOC_LO16);
-             macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nor",
-                          "d,v,t", treg, treg, 0);
+             macro_build (&imm_expr, "ori", "t,r,i",
+                          treg, sreg, BFD_RELOC_LO16);
+             macro_build (NULL, "nor", "d,v,t", treg, treg, 0);
            }
-         return;
+         break;
        }
 
-      load_register (&icnt, AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s2, "d,v,t",
-                  treg, sreg, AT);
+      used_at = 1;
+      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+      macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
       break;
 
     case M_BEQ_I:
@@ -4292,12 +4223,12 @@ macro (ip)
     beq_i:
       if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr, s, "s,t,p", sreg,
-                      0);
-         return;
+         macro_build (&offset_expr, s, "s,t,p", sreg, 0);
+         break;
        }
-      load_register (&icnt, AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build ((char *) NULL, &icnt, &offset_expr, s, "s,t,p", sreg, AT);
+      used_at = 1;
+      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+      macro_build (&offset_expr, s, "s,t,p", sreg, AT);
       break;
 
     case M_BGEL:
@@ -4305,20 +4236,17 @@ macro (ip)
     case M_BGE:
       if (treg == 0)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bgezl" : "bgez", "s,p", sreg);
-         return;
+         macro_build (&offset_expr, likely ? "bgezl" : "bgez", "s,p", sreg);
+         break;
        }
       if (sreg == 0)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "blezl" : "blez", "s,p", treg);
-         return;
+         macro_build (&offset_expr, likely ? "blezl" : "blez", "s,p", treg);
+         break;
        }
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "slt", "d,v,t",
-                  AT, sreg, treg);
-      macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "beql" : "beq", "s,t,p", AT, 0);
+      used_at = 1;
+      macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
+      macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, 0);
       break;
 
     case M_BGTL_I:
@@ -4340,22 +4268,10 @@ macro (ip)
        do_false:
          /* result is always false */
          if (! likely)
-           {
-             if (warn_nops)
-               as_warn (_("Branch %s is always false (nop)"),
-                        ip->insn_mo->name);
-             macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop",
-                          "", 0);
-           }
+           macro_build (NULL, "nop", "", 0);
          else
-           {
-             if (warn_nops)
-               as_warn (_("Branch likely %s is always false"),
-                        ip->insn_mo->name);
-             macro_build ((char *) NULL, &icnt, &offset_expr, "bnel",
-                          "s,t,p", 0, 0);
-           }
-         return;
+           macro_build (&offset_expr, "bnel", "s,t,p", 0, 0);
+         break;
        }
       if (imm_expr.X_op != O_constant)
        as_bad (_("Unsupported large constant"));
@@ -4367,15 +4283,13 @@ macro (ip)
        likely = 1;
       if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bgezl" : "bgez", "s,p", sreg);
-         return;
+         macro_build (&offset_expr, likely ? "bgezl" : "bgez", "s,p", sreg);
+         break;
        }
       if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bgtzl" : "bgtz", "s,p", sreg);
-         return;
+         macro_build (&offset_expr, likely ? "bgtzl" : "bgtz", "s,p", sreg);
+         break;
        }
       maxnum = 0x7fffffff;
       if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
@@ -4393,12 +4307,12 @@ macro (ip)
        do_true:
          /* result is always true */
          as_warn (_("Branch %s is always true"), ip->insn_mo->name);
-         macro_build ((char *) NULL, &icnt, &offset_expr, "b", "p");
-         return;
+         macro_build (&offset_expr, "b", "p");
+         break;
        }
-      set_at (&icnt, sreg, 0);
-      macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "beql" : "beq", "s,t,p", AT, 0);
+      used_at = 1;
+      set_at (sreg, 0);
+      macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, 0);
       break;
 
     case M_BGEUL:
@@ -4408,14 +4322,13 @@ macro (ip)
        goto do_true;
       if (sreg == 0)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "beql" : "beq", "s,t,p", 0, treg);
-         return;
+         macro_build (&offset_expr, likely ? "beql" : "beq",
+                      "s,t,p", 0, treg);
+         break;
        }
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sltu",
-                  "d,v,t", AT, sreg, treg);
-      macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "beql" : "beq", "s,t,p", AT, 0);
+      used_at = 1;
+      macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
+      macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, 0);
       break;
 
     case M_BGTUL_I:
@@ -4438,13 +4351,13 @@ macro (ip)
        goto do_true;
       if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bnel" : "bne", "s,t,p", sreg, 0);
-         return;
+         macro_build (&offset_expr, likely ? "bnel" : "bne",
+                      "s,t,p", sreg, 0);
+         break;
        }
-      set_at (&icnt, sreg, 1);
-      macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "beql" : "beq", "s,t,p", AT, 0);
+      used_at = 1;
+      set_at (sreg, 1);
+      macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, 0);
       break;
 
     case M_BGTL:
@@ -4452,20 +4365,17 @@ macro (ip)
     case M_BGT:
       if (treg == 0)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bgtzl" : "bgtz", "s,p", sreg);
-         return;
+         macro_build (&offset_expr, likely ? "bgtzl" : "bgtz", "s,p", sreg);
+         break;
        }
       if (sreg == 0)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bltzl" : "bltz", "s,p", treg);
-         return;
+         macro_build (&offset_expr, likely ? "bltzl" : "bltz", "s,p", treg);
+         break;
        }
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "slt", "d,v,t",
-                  AT, treg, sreg);
-      macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "bnel" : "bne", "s,t,p", AT, 0);
+      used_at = 1;
+      macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
+      macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, 0);
       break;
 
     case M_BGTUL:
@@ -4473,16 +4383,15 @@ macro (ip)
     case M_BGTU:
       if (treg == 0)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bnel" : "bne", "s,t,p", sreg, 0);
-         return;
+         macro_build (&offset_expr, likely ? "bnel" : "bne",
+                      "s,t,p", sreg, 0);
+         break;
        }
       if (sreg == 0)
        goto do_false;
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sltu",
-                  "d,v,t", AT, treg, sreg);
-      macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "bnel" : "bne", "s,t,p", AT, 0);
+      used_at = 1;
+      macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
+      macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, 0);
       break;
 
     case M_BLEL:
@@ -4490,20 +4399,17 @@ macro (ip)
     case M_BLE:
       if (treg == 0)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "blezl" : "blez", "s,p", sreg);
-         return;
+         macro_build (&offset_expr, likely ? "blezl" : "blez", "s,p", sreg);
+         break;
        }
       if (sreg == 0)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bgezl" : "bgez", "s,p", treg);
-         return;
+         macro_build (&offset_expr, likely ? "bgezl" : "bgez", "s,p", treg);
+         break;
        }
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "slt", "d,v,t",
-                  AT, treg, sreg);
-      macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "beql" : "beq", "s,t,p", AT, 0);
+      used_at = 1;
+      macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
+      macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, 0);
       break;
 
     case M_BLEL_I:
@@ -4531,19 +4437,17 @@ macro (ip)
        likely = 1;
       if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bltzl" : "bltz", "s,p", sreg);
-         return;
+         macro_build (&offset_expr, likely ? "bltzl" : "bltz", "s,p", sreg);
+         break;
        }
       if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "blezl" : "blez", "s,p", sreg);
-         return;
+         macro_build (&offset_expr, likely ? "blezl" : "blez", "s,p", sreg);
+         break;
        }
-      set_at (&icnt, sreg, 0);
-      macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "bnel" : "bne", "s,t,p", AT, 0);
+      used_at = 1;
+      set_at (sreg, 0);
+      macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, 0);
       break;
 
     case M_BLEUL:
@@ -4551,16 +4455,15 @@ macro (ip)
     case M_BLEU:
       if (treg == 0)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "beql" : "beq", "s,t,p", sreg, 0);
-         return;
+         macro_build (&offset_expr, likely ? "beql" : "beq",
+                      "s,t,p", sreg, 0);
+         break;
        }
       if (sreg == 0)
        goto do_true;
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sltu",
-                  "d,v,t", AT, treg, sreg);
-      macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "beql" : "beq", "s,t,p", AT, 0);
+      used_at = 1;
+      macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
+      macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, 0);
       break;
 
     case M_BLEUL_I:
@@ -4583,14 +4486,13 @@ macro (ip)
        goto do_false;
       if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "beql" : "beq",
+         macro_build (&offset_expr, likely ? "beql" : "beq",
                       "s,t,p", sreg, 0);
-         return;
+         break;
        }
-      set_at (&icnt, sreg, 1);
-      macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "bnel" : "bne", "s,t,p", AT, 0);
+      used_at = 1;
+      set_at (sreg, 1);
+      macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, 0);
       break;
 
     case M_BLTL:
@@ -4598,20 +4500,17 @@ macro (ip)
     case M_BLT:
       if (treg == 0)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bltzl" : "bltz", "s,p", sreg);
-         return;
+         macro_build (&offset_expr, likely ? "bltzl" : "bltz", "s,p", sreg);
+         break;
        }
       if (sreg == 0)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bgtzl" : "bgtz", "s,p", treg);
-         return;
+         macro_build (&offset_expr, likely ? "bgtzl" : "bgtz", "s,p", treg);
+         break;
        }
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "slt", "d,v,t",
-                  AT, sreg, treg);
-      macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "bnel" : "bne", "s,t,p", AT, 0);
+      used_at = 1;
+      macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
+      macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, 0);
       break;
 
     case M_BLTUL:
@@ -4621,15 +4520,110 @@ macro (ip)
        goto do_false;
       if (sreg == 0)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bnel" : "bne", "s,t,p", 0, treg);
-         return;
+         macro_build (&offset_expr, likely ? "bnel" : "bne",
+                      "s,t,p", 0, treg);
+         break;
        }
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sltu",
-                  "d,v,t", AT, sreg,
-                  treg);
-      macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "bnel" : "bne", "s,t,p", AT, 0);
+      used_at = 1;
+      macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
+      macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, 0);
+      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;
+         }
+
+       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;
+         }
+
+       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;
+
+    case M_DINS:
+      {
+       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;
+         }
+
+       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 (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, pos,
+                    pos + size - 1);
+      }
       break;
 
     case M_DDIV_3:
@@ -4646,76 +4640,61 @@ macro (ip)
        {
          as_warn (_("Divide by zero."));
          if (mips_trap)
-           macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "teq",
-                        "s,t,q", 0, 0, 7);
+           macro_build (NULL, "teq", "s,t,q", 0, 0, 7);
          else
-           macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "break",
-                        "c", 7);
-         return;
+           macro_build (NULL, "break", "c", 7);
+         break;
        }
 
-      mips_emit_delays (TRUE);
-      ++mips_opts.noreorder;
-      mips_any_noreorder = 1;
+      start_noreorder ();
       if (mips_trap)
        {
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "teq",
-                      "s,t,q", treg, 0, 7);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                      dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
+         macro_build (NULL, "teq", "s,t,q", treg, 0, 7);
+         macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
        }
       else
        {
          expr1.X_add_number = 8;
-         macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", treg, 0);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                      dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "break",
-                      "c", 7);
+         macro_build (&expr1, "bne", "s,t,p", treg, 0);
+         macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
+         macro_build (NULL, "break", "c", 7);
        }
       expr1.X_add_number = -1;
-      macro_build ((char *) NULL, &icnt, &expr1,
-                  dbl ? "daddiu" : "addiu",
-                  "t,r,j", AT, 0, (int) BFD_RELOC_LO16);
+      used_at = 1;
+      load_register (AT, &expr1, dbl);
       expr1.X_add_number = mips_trap ? (dbl ? 12 : 8) : (dbl ? 20 : 16);
-      macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", treg, AT);
+      macro_build (&expr1, "bne", "s,t,p", treg, AT);
       if (dbl)
        {
          expr1.X_add_number = 1;
-         macro_build ((char *) NULL, &icnt, &expr1, "daddiu", "t,r,j", AT, 0,
-                      (int) BFD_RELOC_LO16);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "dsll32",
-                      "d,w,<", AT, AT, 31);
+         load_register (AT, &expr1, dbl);
+         macro_build (NULL, "dsll32", "d,w,<", AT, AT, 31);
        }
       else
        {
          expr1.X_add_number = 0x80000000;
-         macro_build ((char *) NULL, &icnt, &expr1, "lui", "t,u", AT,
-                      (int) BFD_RELOC_HI16);
+         macro_build (&expr1, "lui", "t,u", AT, BFD_RELOC_HI16);
        }
       if (mips_trap)
        {
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "teq",
-                      "s,t,q", sreg, AT, 6);
+         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.  */
-         --mips_opts.noreorder;
+         end_noreorder ();
        }
       else
        {
          expr1.X_add_number = 8;
-         macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", sreg, AT);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "",
-                      0);
+         macro_build (&expr1, "bne", "s,t,p", sreg, AT);
+         macro_build (NULL, "nop", "", 0);
 
          /* We want to close the noreorder block as soon as possible, so
             that later insns are available for delay slot filling.  */
-         --mips_opts.noreorder;
+         end_noreorder ();
 
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "break",
-                      "c", 6);
+         macro_build (NULL, "break", "c", 6);
        }
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "d", dreg);
+      macro_build (NULL, s, "d", dreg);
       break;
 
     case M_DIV_3I:
@@ -4758,20 +4737,18 @@ macro (ip)
        {
          as_warn (_("Divide by zero."));
          if (mips_trap)
-           macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "teq",
-                        "s,t,q", 0, 0, 7);
+           macro_build (NULL, "teq", "s,t,q", 0, 0, 7);
          else
-           macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "break",
-                        "c", 7);
-         return;
+           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 (&icnt, dreg, sreg);
+           move_register (dreg, sreg);
          else
-           move_register (&icnt, dreg, 0);
-         return;
+           move_register (dreg, 0);
+         break;
        }
       if (imm_expr.X_op == O_constant
          && imm_expr.X_add_number == -1
@@ -4779,18 +4756,17 @@ macro (ip)
        {
          if (strcmp (s2, "mflo") == 0)
            {
-             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                          dbl ? "dneg" : "neg", "d,w", dreg, sreg);
+             macro_build (NULL, dbl ? "dneg" : "neg", "d,w", dreg, sreg);
            }
          else
-           move_register (&icnt, dreg, 0);
-         return;
+           move_register (dreg, 0);
+         break;
        }
 
-      load_register (&icnt, AT, &imm_expr, dbl);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "z,s,t",
-                  sreg, AT);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s2, "d", dreg);
+      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;
 
     case M_DIVU_3:
@@ -4809,38 +4785,38 @@ macro (ip)
       s = "ddivu";
       s2 = "mfhi";
     do_divu3:
-      mips_emit_delays (TRUE);
-      ++mips_opts.noreorder;
-      mips_any_noreorder = 1;
+      start_noreorder ();
       if (mips_trap)
        {
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "teq",
-                      "s,t,q", treg, 0, 7);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "z,s,t",
-                      sreg, treg);
+         macro_build (NULL, "teq", "s,t,q", treg, 0, 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.  */
-         --mips_opts.noreorder;
+         end_noreorder ();
        }
       else
        {
          expr1.X_add_number = 8;
-         macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", treg, 0);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "z,s,t",
-                      sreg, treg);
+         macro_build (&expr1, "bne", "s,t,p", treg, 0);
+         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.  */
-         --mips_opts.noreorder;
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "break",
-                      "c", 7);
+         end_noreorder ();
+         macro_build (NULL, "break", "c", 7);
        }
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s2, "d", dreg);
-      return;
+      macro_build (NULL, s2, "d", 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.  */
 
@@ -4854,66 +4830,19 @@ macro (ip)
          && offset_expr.X_add_number >= -0x8000
          && offset_expr.X_add_number < 0x8000)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      (dbl || HAVE_64BIT_ADDRESSES) ? "daddiu" :
-                      HAVE_NEWABI ? "addi" : "addiu",
-                      "t,r,j", treg, sreg, (int) BFD_RELOC_LO16);
-         return;
+         macro_build (&offset_expr, ADDRESS_ADDI_INSN,
+                      "t,r,j", treg, sreg, BFD_RELOC_LO16);
+         break;
        }
 
-      if (treg == breg)
+      if (!mips_opts.noat && (treg == breg))
        {
          tempreg = AT;
          used_at = 1;
        }
       else
        {
-         tempreg = treg;
-         used_at = 0;
-       }
-
-      /* When generating embedded PIC code, we permit expressions of
-        the form
-          la   $treg,foo-bar
-          la   $treg,foo-bar($breg)
-        where bar is an address in the current section.  These are used
-        when getting the addresses of functions.  We don't permit
-        X_add_number to be non-zero, because if the symbol is
-        external the relaxing code needs to know that any addend is
-        purely the offset to X_op_symbol.  */
-      if (mips_pic == EMBEDDED_PIC
-         && offset_expr.X_op == O_subtract
-         && (symbol_constant_p (offset_expr.X_op_symbol)
-             ? S_GET_SEGMENT (offset_expr.X_op_symbol) == now_seg
-             : (symbol_equated_p (offset_expr.X_op_symbol)
-                && (S_GET_SEGMENT
-                    (symbol_get_value_expression (offset_expr.X_op_symbol)
-                     ->X_add_symbol)
-                    == now_seg)))
-         && (offset_expr.X_add_number == 0
-             || OUTPUT_FLAVOR == bfd_target_elf_flavour))
-       {
-         if (breg == 0)
-           {
-             tempreg = treg;
-             used_at = 0;
-             macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
-                          tempreg, (int) BFD_RELOC_PCREL_HI16_S);
-           }
-         else
-           {
-             macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
-                          tempreg, (int) BFD_RELOC_PCREL_HI16_S);
-             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                          (dbl || HAVE_64BIT_ADDRESSES) ? "daddu" : "addu",
-                          "d,v,t", tempreg, tempreg, breg);
-           }
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      (dbl || HAVE_64BIT_ADDRESSES) ? "daddiu" : "addiu",
-                      "t,r,j", treg, tempreg, (int) BFD_RELOC_PCREL_LO16);
-         if (! used_at)
-           return;
-         break;
+         tempreg = treg;
        }
 
       if (offset_expr.X_op != O_symbol
@@ -4924,10 +4853,7 @@ macro (ip)
        }
 
       if (offset_expr.X_op == O_constant)
-       load_register (&icnt, tempreg, &offset_expr,
-                      ((mips_pic == EMBEDDED_PIC || mips_pic == NO_PIC)
-                       ? (dbl || HAVE_64BIT_ADDRESSES)
-                       : HAVE_64BIT_ADDRESSES));
+       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
@@ -4938,82 +4864,84 @@ macro (ip)
             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)
-         */
-         char *p = NULL;
-         if (HAVE_64BIT_ADDRESSES)
+            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)
            {
-             /* We don't do GP optimization for now because RELAX_ENCODE can't
-                hold the data for such large chunks.  */
+             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.noat)
+             if (used_at == 0 && !mips_opts.noat)
                {
-                 macro_build (p, &icnt, &offset_expr, "lui", "t,u",
-                              tempreg, (int) BFD_RELOC_MIPS_HIGHEST);
-                 macro_build (p, &icnt, &offset_expr, "lui", "t,u",
-                              AT, (int) BFD_RELOC_HI16_S);
-                 macro_build (p, &icnt, &offset_expr, "daddiu", "t,r,j",
-                              tempreg, tempreg, (int) BFD_RELOC_MIPS_HIGHER);
-                 macro_build (p, &icnt, &offset_expr, "daddiu", "t,r,j",
-                              AT, AT, (int) BFD_RELOC_LO16);
-                 macro_build (p, &icnt, (expressionS *) NULL, "dsll32",
-                              "d,w,<", tempreg, tempreg, 0);
-                 macro_build (p, &icnt, (expressionS *) NULL, "daddu",
-                              "d,v,t", tempreg, tempreg, 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 (p, &icnt, &offset_expr, "lui", "t,u",
-                              tempreg, (int) BFD_RELOC_MIPS_HIGHEST);
-                 macro_build (p, &icnt, &offset_expr, "daddiu", "t,r,j",
-                              tempreg, tempreg, (int) BFD_RELOC_MIPS_HIGHER);
-                 macro_build (p, &icnt, (expressionS *) NULL, "dsll", "d,w,<",
-                              tempreg, tempreg, 16);
-                 macro_build (p, &icnt, &offset_expr, "daddiu", "t,r,j",
-                              tempreg, tempreg, (int) BFD_RELOC_HI16_S);
-                 macro_build (p, &icnt, (expressionS *) NULL, "dsll", "d,w,<",
-                              tempreg, tempreg, 16);
-                 macro_build (p, &icnt, &offset_expr, "daddiu", "t,r,j",
-                              tempreg, tempreg, (int) BFD_RELOC_LO16);
+                 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);
                }
+
+             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))
+                 && !nopic_need_relax (offset_expr.X_add_symbol, 1))
                {
-                 frag_grow (20);
-                 macro_build ((char *) NULL, &icnt, &offset_expr,
-                              HAVE_NEWABI ? "addi" : "addiu",
-                              "t,r,j", tempreg, mips_gp_register,
-                              (int) BFD_RELOC_GPREL16);
-                 p = frag_var (rs_machine_dependent, 8, 0,
-                               RELAX_ENCODE (4, 8, 0, 4, 0,
-                                             mips_opts.warn_about_macros),
-                               offset_expr.X_add_symbol, 0, NULL);
+                 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 ();
                }
-             macro_build_lui (p, &icnt, &offset_expr, tempreg);
-             if (p != NULL)
-               p += 4;
-             macro_build (p, &icnt, &offset_expr,
-                          HAVE_NEWABI ? "addi" : "addiu",
-                          "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
+             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_pic == SVR4_PIC && ! mips_big_got && ! HAVE_NEWABI)
@@ -5023,7 +4951,7 @@ macro (ip)
          /* 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 if tempreg is PIC_CALL_REG
+            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)
@@ -5050,64 +4978,46 @@ macro (ip)
             addiu instruction.
           */
 
-         expr1.X_add_number = offset_expr.X_add_number;
-         offset_expr.X_add_number = 0;
-         frag_grow (32);
-         if (expr1.X_add_number == 0 && tempreg == PIC_CALL_REG)
-           lw_reloc_type = (int) BFD_RELOC_MIPS_CALL16;
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                      "t,o(b)", tempreg, lw_reloc_type, mips_gp_register);
-         if (expr1.X_add_number == 0)
+         if (offset_expr.X_add_number == 0)
            {
-             int off;
-             char *p;
+             if (breg == 0 && (call || tempreg == PIC_CALL_REG))
+               lw_reloc_type = (int) BFD_RELOC_MIPS_CALL16;
 
-             if (breg == 0)
-               off = 0;
-             else
+             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.  */
-                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              "nop", "");
-                 off = 4;
-               }
-             p = frag_var (rs_machine_dependent, 8 - off, 0,
-                           RELAX_ENCODE (0, 8 - off, -4 - off, 4 - off, 0,
-                                         (breg == 0
-                                          ? mips_opts.warn_about_macros
-                                          : 0)),
-                           offset_expr.X_add_symbol, 0, NULL);
-             if (breg == 0)
-               {
-                 macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
-                 p += 4;
+                 load_delay_nop ();
                }
-             macro_build (p, &icnt, &expr1,
-                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
-                          "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
+             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 (expr1.X_add_number >= -0x8000
-                  && expr1.X_add_number < 0x8000)
+         else if (offset_expr.X_add_number >= -0x8000
+                  && offset_expr.X_add_number < 0x8000)
            {
-             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                          "nop", "");
-             macro_build ((char *) NULL, &icnt, &expr1,
-                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
-                          "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
-             frag_var (rs_machine_dependent, 0, 0,
-                       RELAX_ENCODE (0, 0, -12, -4, 0, 0),
-                       offset_expr.X_add_symbol, 0, NULL);
+             load_got_offset (tempreg, &offset_expr);
+             load_delay_nop ();
+             add_got_offset (tempreg, &offset_expr);
            }
          else
            {
-             int off1;
-
+             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
@@ -5115,50 +5025,27 @@ macro (ip)
                 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)
-               off1 = 0;
-             else
+             if (breg == treg)
                {
-                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              "nop", "");
-                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                              "d,v,t", treg, AT, breg);
+                 load_delay_nop ();
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                              treg, AT, breg);
                  breg = 0;
                  tempreg = treg;
-                 off1 = -8;
                }
-
-             /* 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 (NULL, &icnt, &expr1, AT);
-             mips_optimize = hold_mips_optimize;
-
-             macro_build ((char *) NULL, &icnt, &expr1,
-                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
-                          "t,r,j", AT, AT, (int) BFD_RELOC_LO16);
-             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                          "d,v,t", tempreg, tempreg, AT);
-             frag_var (rs_machine_dependent, 0, 0,
-                       RELAX_ENCODE (0, 0, -16 + off1, -8, 0, 0),
-                       offset_expr.X_add_symbol, 0, NULL);
+             add_got_offset_hilo (tempreg, &offset_expr, AT);
              used_at = 1;
            }
        }
       else if (mips_pic == SVR4_PIC && ! mips_big_got && HAVE_NEWABI)
        {
-         char *p = NULL;
-         int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT_DISP;
-         int adj = 0;
+         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 if tempreg is PIC_CALL_REG
+            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
@@ -5177,32 +5064,22 @@ macro (ip)
             local symbols, even though it introduces an additional
             instruction.  */
 
-         frag_grow (28);
-         if (offset_expr.X_add_number == 0 && tempreg == PIC_CALL_REG)
-           lw_reloc_type = (int) BFD_RELOC_MIPS_CALL16;
          if (offset_expr.X_add_number)
            {
-             frag_now->tc_frag_data.tc_fr_offset =
-               expr1.X_add_number = offset_expr.X_add_number;
+             expr1.X_add_number = offset_expr.X_add_number;
              offset_expr.X_add_number = 0;
 
-             macro_build ((char *) NULL, &icnt, &offset_expr,
-                          HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                          "t,o(b)", tempreg, lw_reloc_type,
-                          mips_gp_register);
+             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 ((char *) NULL, &icnt, &expr1,
-                              HAVE_32BIT_ADDRESSES ? "addi" : "daddiu",
-                              "t,r,j", tempreg, tempreg,
-                              (int) BFD_RELOC_LO16);
-                 p = frag_var (rs_machine_dependent, 4, 0,
-                               RELAX_ENCODE (8, 4, 0, 0, 0, 0),
-                               offset_expr.X_add_symbol, 0, NULL);
+                 macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
+                              tempreg, tempreg, BFD_RELOC_LO16);
                }
-             else if (IS_SEXT_32BIT_NUM (expr1.X_add_number))
+             else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
                {
                  int dreg;
 
@@ -5218,70 +5095,54 @@ macro (ip)
                  else
                    {
                      assert (tempreg == AT);
-                     macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                                  HAVE_32BIT_ADDRESSES ? "add" : "daddu",
-                                  "d,v,t", treg, AT, breg);
+                     macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                                  treg, AT, breg);
                      dreg = treg;
-                     adj = 4;
+                     add_breg_early = 1;
                    }
 
-                 macro_build_lui ((char *) NULL, &icnt, &expr1, AT);
-                 macro_build ((char *) NULL, &icnt, &expr1,
-                              HAVE_32BIT_ADDRESSES ? "addi" : "daddiu",
-                              "t,r,j", AT, AT, (int) BFD_RELOC_LO16);
-                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              HAVE_32BIT_ADDRESSES ? "add" : "daddu",
-                              "d,v,t", dreg, dreg, AT);
-
-                 p = frag_var (rs_machine_dependent, 4 + adj, 0,
-                               RELAX_ENCODE (16 + adj, 4 + adj,
-                                             0, 0, 0, 0),
-                               offset_expr.X_add_symbol, 0, NULL);
+                 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 (p, &icnt, &offset_expr,
-                          HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                          "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT_DISP,
-                          mips_gp_register);
-             if (adj)
+             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 (p + 4, &icnt, (expressionS *) NULL,
-                              HAVE_32BIT_ADDRESSES ? "add" : "daddu",
-                              "d,v,t", treg, tempreg, breg);
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                              treg, tempreg, breg);
                  breg = 0;
                  tempreg = treg;
                }
+             relax_end ();
            }
-         else
+         else if (breg == 0 && (call || tempreg == PIC_CALL_REG))
            {
-             macro_build ((char *) NULL, &icnt, &offset_expr,
-                          HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                          "t,o(b)", tempreg, lw_reloc_type,
-                          mips_gp_register);
-             if (lw_reloc_type != BFD_RELOC_MIPS_GOT_DISP)
-               p = frag_var (rs_machine_dependent, 0, 0,
-                             RELAX_ENCODE (0, 0, -4, 0, 0, 0),
-                             offset_expr.X_add_symbol, 0, NULL);
+             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 ();
            }
-
-         if (! p)
+         else
            {
-             /* To avoid confusion in tc_gen_reloc, we must ensure
-                that this does not become a variant frag.  */
-             frag_wane (frag_now);
-             frag_new (0);
+             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                          BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
            }
        }
       else if (mips_pic == SVR4_PIC && ! HAVE_NEWABI)
        {
-         int gpdel;
-         char *p;
+         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;
@@ -5291,7 +5152,7 @@ macro (ip)
               lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
               addu     $tempreg,$tempreg,$gp
               lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
-            or if tempreg is PIC_CALL_REG
+            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)
@@ -5329,67 +5190,39 @@ macro (ip)
 
          expr1.X_add_number = offset_expr.X_add_number;
          offset_expr.X_add_number = 0;
-         frag_grow (52);
-         if (reg_needs_delay (mips_gp_register))
-           gpdel = 4;
-         else
-           gpdel = 0;
-         if (expr1.X_add_number == 0 && tempreg == PIC_CALL_REG)
+         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 ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
-                      tempreg, lui_reloc_type);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                      HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                      "d,v,t", tempreg, tempreg, mips_gp_register);
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                      "t,o(b)", tempreg, lw_reloc_type, tempreg);
+         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)
            {
-             int off;
-
-             if (breg == 0)
-               off = 0;
-             else
+             if (breg != 0)
                {
                  /* We're going to put in an addu instruction using
                     tempreg, so we may as well insert the nop right
                     now.  */
-                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              "nop", "");
-                 off = 4;
+                 load_delay_nop ();
                }
-
-             p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
-                           RELAX_ENCODE (12 + off, 12 + gpdel, gpdel,
-                                         8 + gpdel, 0,
-                                         (breg == 0
-                                          ? mips_opts.warn_about_macros
-                                          : 0)),
-                           offset_expr.X_add_symbol, 0, NULL);
            }
          else if (expr1.X_add_number >= -0x8000
                   && expr1.X_add_number < 0x8000)
            {
-             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                          "nop", "");
-             macro_build ((char *) NULL, &icnt, &expr1,
-                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
-                          "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
-
-             p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
-                           RELAX_ENCODE (20, 12 + gpdel, gpdel, 8 + gpdel, 0,
-                                         (breg == 0
-                                          ? mips_opts.warn_about_macros
-                                          : 0)),
-                           offset_expr.X_add_symbol, 0, NULL);
+             load_delay_nop ();
+             macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
+                          tempreg, tempreg, BFD_RELOC_LO16);
            }
          else
            {
-             int adj, dreg;
+             int dreg;
 
              /* If we are going to add in a base register, and the
                 target register and the base register are the same,
@@ -5399,69 +5232,40 @@ macro (ip)
                 register into the register now, and pretend we were
                 not using a base register.  */
              if (breg != treg)
-               {
-                 adj = 0;
-                 dreg = tempreg;
-               }
+               dreg = tempreg;
              else
                {
                  assert (tempreg == AT);
-                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              "nop", "");
-                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                              "d,v,t", treg, AT, breg);
+                 load_delay_nop ();
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                              treg, AT, breg);
                  dreg = treg;
-                 adj = 8;
                }
 
-             /* 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 (NULL, &icnt, &expr1, AT);
-             mips_optimize = hold_mips_optimize;
-
-             macro_build ((char *) NULL, &icnt, &expr1,
-                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
-                          "t,r,j", AT, AT, (int) BFD_RELOC_LO16);
-             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                          "d,v,t", dreg, dreg, AT);
-
-             p = frag_var (rs_machine_dependent, 16 + gpdel + adj, 0,
-                           RELAX_ENCODE (24 + adj, 16 + gpdel + adj, gpdel,
-                                         8 + gpdel, 0,
-                                         (breg == 0
-                                          ? mips_opts.warn_about_macros
-                                          : 0)),
-                           offset_expr.X_add_symbol, 0, NULL);
+             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 =
+           ((expr1.X_add_number + 0x8000) & 0xffff) - 0x8000;
+         relax_switch ();
 
-         if (gpdel > 0)
+         if (gpdelay)
            {
              /* This is needed because this instruction uses $gp, but
                 the first instruction on the main stream does not.  */
-             macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
-             p += 4;
+             macro_build (NULL, "nop", "");
            }
 
-         macro_build (p, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                      "t,o(b)", tempreg,
-                      local_reloc_type,
-                      mips_gp_register);
-         p += 4;
+         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)
            {
-             macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
-             p += 4;
-             macro_build (p, &icnt, &expr1,
-                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
-                          "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
+             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
@@ -5475,43 +5279,36 @@ macro (ip)
                  /* We must add in the base register now, as in the
                     external symbol case.  */
                  assert (tempreg == AT);
-                 macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
-                 p += 4;
-                 macro_build (p, &icnt, (expressionS *) NULL,
-                              HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                              "d,v,t", treg, AT, breg);
-                 p += 4;
+                 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 (p, &icnt, &expr1, AT);
-             p += 4;
-             macro_build (p, &icnt, &expr1,
-                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
-                          "t,r,j", AT, AT, (int) BFD_RELOC_LO16);
-             p += 4;
-             macro_build (p, &icnt, (expressionS *) NULL,
-                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                          "d,v,t", tempreg, tempreg, AT);
-             p += 4;
+             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_pic == SVR4_PIC && HAVE_NEWABI)
        {
-         char *p = NULL;
          int lui_reloc_type = (int) BFD_RELOC_MIPS_GOT_HI16;
          int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT_LO16;
-         int adj = 0;
+         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 if tempreg is PIC_CALL_REG
+            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)
@@ -5537,46 +5334,32 @@ macro (ip)
               addiu    $reg,$reg,<sym>         (BFD_RELOC_MIPS_GOT_OFST)
             otherwise we have to resort to GOT_HI16/GOT_LO16.  */
 
-         frag_grow (40);
+         relax_start (offset_expr.X_add_symbol);
 
-         frag_now->tc_frag_data.tc_fr_offset =
-           expr1.X_add_number = offset_expr.X_add_number;
+         expr1.X_add_number = offset_expr.X_add_number;
          offset_expr.X_add_number = 0;
 
-         if (expr1.X_add_number == 0 && tempreg == PIC_CALL_REG)
+         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 ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
-                      tempreg, lui_reloc_type);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                      HAVE_32BIT_ADDRESSES ? "add" : "daddu",
-                      "d,v,t", tempreg, tempreg, mips_gp_register);
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                      "t,o(b)", tempreg, lw_reloc_type, tempreg);
+         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)
-           {
-             p = frag_var (rs_machine_dependent, 8, 0,
-                           RELAX_ENCODE (12, 8, 0, 4, 0,
-                                         mips_opts.warn_about_macros),
-                           offset_expr.X_add_symbol, 0, NULL);
-           }
+           ;
          else if (expr1.X_add_number >= -0x8000
                   && expr1.X_add_number < 0x8000)
            {
-             macro_build ((char *) NULL, &icnt, &expr1,
-                          HAVE_32BIT_ADDRESSES ? "addi" : "daddiu",
-                          "t,r,j", tempreg, tempreg,
-                          (int) BFD_RELOC_LO16);
-             p = frag_var (rs_machine_dependent, 8, 0,
-                           RELAX_ENCODE (16, 8, 0, 4, 0,
-                                         mips_opts.warn_about_macros),
-                           offset_expr.X_add_symbol, 0, NULL);
+             macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
+                          tempreg, tempreg, BFD_RELOC_LO16);
            }
-         else if (IS_SEXT_32BIT_NUM (expr1.X_add_number))
+         else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
            {
              int dreg;
 
@@ -5592,82 +5375,40 @@ macro (ip)
              else
                {
                  assert (tempreg == AT);
-                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              HAVE_32BIT_ADDRESSES ? "add" : "daddu",
-                              "d,v,t", treg, AT, breg);
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                              treg, AT, breg);
                  dreg = treg;
-                 adj = 4;
+                 add_breg_early = 1;
                }
 
-             /* Set mips_optimize around the lui instruction to avoid
-                inserting an unnecessary nop after the lw.  */
-             macro_build_lui ((char *) NULL, &icnt, &expr1, AT);
-             macro_build ((char *) NULL, &icnt, &expr1,
-                          HAVE_32BIT_ADDRESSES ? "addi" : "daddiu",
-                          "t,r,j", AT, AT, (int) BFD_RELOC_LO16);
-             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                          HAVE_32BIT_ADDRESSES ? "add" : "daddu",
-                          "d,v,t", dreg, dreg, AT);
-
-             p = frag_var (rs_machine_dependent, 8 + adj, 0,
-                           RELAX_ENCODE (24 + adj, 8 + adj,
-                                         0, 4, 0,
-                                         (breg == 0
-                                          ? mips_opts.warn_about_macros
-                                          : 0)),
-                           offset_expr.X_add_symbol, 0, NULL);
+             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 (p, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld", "t,o(b)",
-                      tempreg,
-                      (int) BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
-         macro_build (p + 4, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "addi" : "daddiu", "t,r,j",
-                      tempreg, tempreg, (int) BFD_RELOC_MIPS_GOT_OFST);
-         if (adj)
+         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 (p + 8, &icnt, (expressionS *) NULL,
-                          HAVE_32BIT_ADDRESSES ? "add" : "daddu",
-                          "d,v,t", treg, tempreg, breg);
+             macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                          treg, tempreg, breg);
              breg = 0;
              tempreg = treg;
            }
-       }
-      else if (mips_pic == EMBEDDED_PIC)
-       {
-         /* We use
-              addiu    $tempreg,$gp,<sym>      (BFD_RELOC_GPREL16)
-            */
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu", "t,r,j",
-                      tempreg, mips_gp_register, (int) BFD_RELOC_GPREL16);
+         relax_end ();
        }
       else
        abort ();
 
       if (breg != 0)
-       {
-         char *s;
-
-         if (mips_pic == EMBEDDED_PIC || mips_pic == NO_PIC)
-           s = (dbl || HAVE_64BIT_ADDRESSES) ? "daddu" :
-             HAVE_NEWABI ? "add" : "addu";
-         else
-           s = HAVE_64BIT_ADDRESSES ? "daddu" : HAVE_NEWABI ? "add" : "addu";
-
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s,
-                      "d,v,t", treg, tempreg, breg);
-       }
-
-      if (! used_at)
-       return;
-
+       macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, tempreg, breg);
       break;
 
     case M_J_A:
@@ -5675,10 +5416,10 @@ macro (ip)
         requires an absolute address.  We convert it to a b
         instruction.  */
       if (mips_pic == NO_PIC)
-       macro_build ((char *) NULL, &icnt, &offset_expr, "j", "a");
+       macro_build (&offset_expr, "j", "a");
       else
-       macro_build ((char *) NULL, &icnt, &offset_expr, "b", "p");
-      return;
+       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
@@ -5687,17 +5428,14 @@ macro (ip)
       dreg = RA;
       /* Fall through.  */
     case M_JAL_2:
-      if (mips_pic == NO_PIC
-         || mips_pic == EMBEDDED_PIC)
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "jalr",
-                    "d,s", dreg, sreg);
+      if (mips_pic == NO_PIC)
+       macro_build (NULL, "jalr", "d,s", dreg, sreg);
       else if (mips_pic == SVR4_PIC)
        {
          if (sreg != PIC_CALL_REG)
            as_warn (_("MIPS PIC call to register other than $25"));
 
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "jalr",
-                      "d,s", dreg, sreg);
+         macro_build (NULL, "jalr", "d,s", dreg, sreg);
          if (! HAVE_NEWABI)
            {
              if (mips_cprestore_offset < 0)
@@ -5717,24 +5455,23 @@ macro (ip)
                      mips_cprestore_valid = 1;
                    }
                  expr1.X_add_number = mips_cprestore_offset;
-                 macro_build_ldst_constoffset ((char *) NULL, &icnt, &expr1,
-                                               HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                                               mips_gp_register, mips_frame_reg);
+                 macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
+                                               mips_gp_register,
+                                               mips_frame_reg,
+                                               HAVE_64BIT_ADDRESSES);
                }
            }
        }
       else
        abort ();
 
-      return;
+      break;
 
     case M_JAL_A:
       if (mips_pic == NO_PIC)
-       macro_build ((char *) NULL, &icnt, &offset_expr, "jal", "a");
+       macro_build (&offset_expr, "jal", "a");
       else if (mips_pic == SVR4_PIC)
        {
-         char *p;
-
          /* 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)
@@ -5767,103 +5504,74 @@ macro (ip)
            {
              if (! mips_big_got)
                {
-                 frag_grow (4);
-                 macro_build ((char *) NULL, &icnt, &offset_expr,
-                              HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                              "t,o(b)", PIC_CALL_REG,
-                              (int) BFD_RELOC_MIPS_CALL16,
+                 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);
-                 frag_var (rs_machine_dependent, 0, 0,
-                           RELAX_ENCODE (0, 0, -4, 0, 0, 0),
-                           offset_expr.X_add_symbol, 0, NULL);
+                 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
                {
-                 frag_grow (20);
-                 macro_build ((char *) NULL, &icnt, &offset_expr, "lui",
-                              "t,u", PIC_CALL_REG,
-                              (int) BFD_RELOC_MIPS_CALL_HI16);
-                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              HAVE_32BIT_ADDRESSES ? "add" : "daddu",
-                              "d,v,t", PIC_CALL_REG, PIC_CALL_REG,
-                              mips_gp_register);
-                 macro_build ((char *) NULL, &icnt, &offset_expr,
-                              HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                              "t,o(b)", PIC_CALL_REG,
-                              (int) BFD_RELOC_MIPS_CALL_LO16, PIC_CALL_REG);
-                 p = frag_var (rs_machine_dependent, 8, 0,
-                               RELAX_ENCODE (12, 8, 0, 4, 0, 0),
-                               offset_expr.X_add_symbol, 0, NULL);
-                 macro_build (p, &icnt, &offset_expr,
-                              HAVE_32BIT_ADDRESSES ? "lw" : "ld", "t,o(b)",
-                              PIC_CALL_REG, (int) BFD_RELOC_MIPS_GOT_PAGE,
+                 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);
+                 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 (p + 4, &icnt, &offset_expr,
-                              HAVE_32BIT_ADDRESSES ? "addi" : "daddiu",
-                              "t,r,j", PIC_CALL_REG, PIC_CALL_REG,
-                              (int) BFD_RELOC_MIPS_GOT_OFST);
+                 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 (icnt, &offset_expr);
+             macro_build_jalr (&offset_expr);
            }
          else
            {
-             frag_grow (40);
+             relax_start (offset_expr.X_add_symbol);
              if (! mips_big_got)
                {
-                 macro_build ((char *) NULL, &icnt, &offset_expr,
-                              HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                              "t,o(b)", PIC_CALL_REG,
-                              (int) BFD_RELOC_MIPS_CALL16, mips_gp_register);
-                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              "nop", "");
-                 p = frag_var (rs_machine_dependent, 4, 0,
-                               RELAX_ENCODE (0, 4, -8, 0, 0, 0),
-                               offset_expr.X_add_symbol, 0, NULL);
+                 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 gpdel;
-
-                 if (reg_needs_delay (mips_gp_register))
-                   gpdel = 4;
-                 else
-                   gpdel = 0;
-                 macro_build ((char *) NULL, &icnt, &offset_expr, "lui",
-                              "t,u", PIC_CALL_REG,
-                              (int) BFD_RELOC_MIPS_CALL_HI16);
-                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                              "d,v,t", PIC_CALL_REG, PIC_CALL_REG,
-                              mips_gp_register);
-                 macro_build ((char *) NULL, &icnt, &offset_expr,
-                              HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                              "t,o(b)", PIC_CALL_REG,
-                              (int) BFD_RELOC_MIPS_CALL_LO16, PIC_CALL_REG);
-                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              "nop", "");
-                 p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
-                               RELAX_ENCODE (16, 12 + gpdel, gpdel,
-                                             8 + gpdel, 0, 0),
-                               offset_expr.X_add_symbol, 0, NULL);
-                 if (gpdel > 0)
-                   {
-                     macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
-                     p += 4;
-                   }
-                 macro_build (p, &icnt, &offset_expr,
-                              HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                              "t,o(b)", PIC_CALL_REG,
-                              (int) BFD_RELOC_MIPS_GOT16, mips_gp_register);
-                 p += 4;
-                 macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
-                 p += 4;
+                 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", "");
                }
-             macro_build (p, &icnt, &offset_expr,
-                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
-                          "t,r,j", PIC_CALL_REG, PIC_CALL_REG,
-                          (int) BFD_RELOC_LO16);
-             macro_build_jalr (icnt, &offset_expr);
+             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);
 
              if (mips_cprestore_offset < 0)
                as_warn (_("No .cprestore pseudo-op used in PIC code"));
@@ -5882,26 +5590,19 @@ macro (ip)
                      mips_cprestore_valid = 1;
                    }
                  if (mips_opts.noreorder)
-                   macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                                "nop", "");
+                   macro_build (NULL, "nop", "");
                  expr1.X_add_number = mips_cprestore_offset;
-                 macro_build_ldst_constoffset ((char *) NULL, &icnt, &expr1,
-                                               HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                                               mips_gp_register, mips_frame_reg);
+                 macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
+                                               mips_gp_register,
+                                               mips_frame_reg,
+                                               HAVE_64BIT_ADDRESSES);
                }
            }
        }
-      else if (mips_pic == EMBEDDED_PIC)
-       {
-         macro_build ((char *) NULL, &icnt, &offset_expr, "bal", "p");
-         /* The linker may expand the call to a longer sequence which
-            uses $at, so we must break rather than return.  */
-         break;
-       }
       else
        abort ();
 
-      return;
+      break;
 
     case M_LB_AB:
       s = "lb";
@@ -5947,10 +5648,10 @@ macro (ip)
       lr = 1;
       goto ld;
     case M_LDC1_AB:
-      if (mips_arch == CPU_R4650)
+      if (mips_opts.arch == CPU_R4650)
        {
          as_bad (_("opcode not supported on this processor"));
-         return;
+         break;
        }
       s = "ldc1";
       /* Itbl support may require additional care here.  */
@@ -5991,7 +5692,6 @@ macro (ip)
       else
        {
          tempreg = treg;
-         used_at = 0;
        }
       goto ld_st;
     case M_SB_AB:
@@ -6036,10 +5736,10 @@ macro (ip)
       s = "scd";
       goto st;
     case M_SDC1_AB:
-      if (mips_arch == CPU_R4650)
+      if (mips_opts.arch == CPU_R4650)
        {
          as_bad (_("opcode not supported on this processor"));
-         return;
+         break;
        }
       s = "sdc1";
       coproc = 1;
@@ -6077,46 +5777,6 @@ macro (ip)
       else
        fmt = "t,o(b)";
 
-      /* For embedded PIC, we allow loads where the offset is calculated
-         by subtracting a symbol in the current segment from an unknown
-         symbol, relative to a base register, e.g.:
-               <op>    $treg, <sym>-<localsym>($breg)
-        This is used by the compiler for switch statements.  */
-      if (mips_pic == EMBEDDED_PIC
-          && offset_expr.X_op == O_subtract
-          && (symbol_constant_p (offset_expr.X_op_symbol)
-              ? S_GET_SEGMENT (offset_expr.X_op_symbol) == now_seg
-              : (symbol_equated_p (offset_expr.X_op_symbol)
-                 && (S_GET_SEGMENT
-                     (symbol_get_value_expression (offset_expr.X_op_symbol)
-                      ->X_add_symbol)
-                     == now_seg)))
-          && breg != 0
-          && (offset_expr.X_add_number == 0
-              || OUTPUT_FLAVOR == bfd_target_elf_flavour))
-        {
-          /* For this case, we output the instructions:
-                lui     $tempreg,<sym>          (BFD_RELOC_PCREL_HI16_S)
-                addiu   $tempreg,$tempreg,$breg
-                <op>    $treg,<sym>($tempreg)   (BFD_RELOC_PCREL_LO16)
-             If the relocation would fit entirely in 16 bits, it would be
-             nice to emit:
-                <op>    $treg,<sym>($breg)      (BFD_RELOC_PCREL_LO16)
-             instead, but that seems quite difficult.  */
-          macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
-                       tempreg, (int) BFD_RELOC_PCREL_HI16_S);
-          macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                       ((bfd_arch_bits_per_address (stdoutput) == 32
-                         || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                        ? HAVE_NEWABI ? "add" : "addu" : "daddu"),
-                       "d,v,t", tempreg, tempreg, breg);
-          macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt, treg,
-                       (int) BFD_RELOC_PCREL_LO16, tempreg);
-          if (! used_at)
-            return;
-          break;
-        }
-
       if (offset_expr.X_op != O_constant
          && offset_expr.X_op != O_symbol)
        {
@@ -6126,11 +5786,22 @@ macro (ip)
 
       /* A constant expression in PIC code can be handled just as it
         is in non PIC code.  */
-      if (mips_pic == NO_PIC
-         || offset_expr.X_op == O_constant)
+      if (offset_expr.X_op == O_constant)
        {
-         char *p;
+         if (HAVE_32BIT_ADDRESSES
+             && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+           as_bad (_("constant too large"));
 
+         expr1.X_add_number = ((offset_expr.X_add_number + 0x8000)
+                               & ~(bfd_vma) 0xffff);
+         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)
@@ -6184,145 +5855,106 @@ macro (ip)
               daddu    $tempreg,$tempreg,$breg
               <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
 
-            If we have 64-bit addresses, as an optimization, for
-            addresses which are 32-bit constants (e.g. kseg0/kseg1
-            addresses) we fall back to the 32-bit address generation
-            mechanism since it is more efficient.  Note that due to
-            the signed offset used by memory operations, the 32-bit
-            range is shifted down by 32768 here.  This code should
-            probably attempt to generate 64-bit constants more
-            efficiently in general.
-
-            As an extension for architectures with 64-bit registers,
-            we don't truncate 64-bit addresses given as literal
-            constants down to 32 bits, to support existing practice
-            in the mips64 Linux (the kernel), that compiles source
-            files with -mabi=64, assembling them as o32 or n32 (with
-            -Wa,-32 or -Wa,-n32).  This is not beautiful, but since
-            the whole kernel is loaded into a memory region that is
-            addressible with sign-extended 32-bit addresses, it is
-            wasteful to compute the upper 32 bits of every
-            non-literal address, that takes more space and time.
-            Some day this should probably be implemented as an
-            assembler option, such that the kernel doesn't have to
-            use such ugly hacks, even though it will still have to
-            end up converting the binary to ELF32 for a number of
-            platforms whose boot loaders don't support ELF64
-            binaries.  */
-         if ((offset_expr.X_op != O_constant && HAVE_64BIT_ADDRESSES)
-             || (offset_expr.X_op == O_constant
-                 && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number + 0x8000)
-                 && HAVE_64BIT_ADDRESS_CONSTANTS))
+            For GP relative symbols in 64bit address space we can use
+            the same sequence as in 32bit address space.  */
+         if (HAVE_64BIT_SYMBOLS)
            {
-             p = NULL;
-
-             /* We don't do GP optimization for now because RELAX_ENCODE can't
-                hold the data for such large chunks.  */
+             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.noat)
+             if (used_at == 0 && !mips_opts.noat)
                {
-                 macro_build (p, &icnt, &offset_expr, "lui", "t,u",
-                              tempreg, (int) BFD_RELOC_MIPS_HIGHEST);
-                 macro_build (p, &icnt, &offset_expr, "lui", "t,u",
-                              AT, (int) BFD_RELOC_HI16_S);
-                 macro_build (p, &icnt, &offset_expr, "daddiu", "t,r,j",
-                              tempreg, tempreg, (int) BFD_RELOC_MIPS_HIGHER);
+                 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 (p, &icnt, (expressionS *) NULL, "daddu",
-                                "d,v,t", AT, AT, breg);
-                 macro_build (p, &icnt, (expressionS *) NULL, "dsll32",
-                              "d,w,<", tempreg, tempreg, 0);
-                 macro_build (p, &icnt, (expressionS *) NULL, "daddu",
-                              "d,v,t", tempreg, tempreg, AT);
-                 macro_build (p, &icnt, &offset_expr, s,
-                              fmt, treg, (int) BFD_RELOC_LO16, tempreg);
+                   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 (p, &icnt, &offset_expr, "lui", "t,u",
-                              tempreg, (int) BFD_RELOC_MIPS_HIGHEST);
-                 macro_build (p, &icnt, &offset_expr, "daddiu", "t,r,j",
-                              tempreg, tempreg, (int) BFD_RELOC_MIPS_HIGHER);
-                 macro_build (p, &icnt, (expressionS *) NULL, "dsll",
-                              "d,w,<", tempreg, tempreg, 16);
-                 macro_build (p, &icnt, &offset_expr, "daddiu", "t,r,j",
-                              tempreg, tempreg, (int) BFD_RELOC_HI16_S);
-                 macro_build (p, &icnt, (expressionS *) NULL, "dsll",
-                              "d,w,<", tempreg, tempreg, 16);
+                 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 (p, &icnt, (expressionS *) NULL, "daddu",
-                                "d,v,t", tempreg, tempreg, breg);
-                 macro_build (p, &icnt, &offset_expr, s,
-                              fmt, treg, (int) BFD_RELOC_LO16, tempreg);
+                   macro_build (NULL, "daddu", "d,v,t",
+                                tempreg, tempreg, breg);
+                 macro_build (&offset_expr, s, fmt, treg,
+                              BFD_RELOC_LO16, tempreg);
                }
 
-             return;
+             if (mips_relax.sequence)
+               relax_end ();
+             break;
            }
-         else if (offset_expr.X_op == O_constant
-                  && !HAVE_64BIT_ADDRESS_CONSTANTS
-                  && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
-           as_bad (_("load/store address overflow (max 32 bits)"));
 
          if (breg == 0)
            {
-             if ((valueT) offset_expr.X_add_number > MAX_GPREL_OFFSET
-                 || nopic_need_relax (offset_expr.X_add_symbol, 1))
-               p = NULL;
-             else
+             if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+                 && !nopic_need_relax (offset_expr.X_add_symbol, 1))
                {
-                 frag_grow (20);
-                 macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
-                              treg, (int) BFD_RELOC_GPREL16,
+                 relax_start (offset_expr.X_add_symbol);
+                 macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_GPREL16,
                               mips_gp_register);
-                 p = frag_var (rs_machine_dependent, 8, 0,
-                               RELAX_ENCODE (4, 8, 0, 4, 0,
-                                             (mips_opts.warn_about_macros
-                                              || (used_at
-                                                  && mips_opts.noat))),
-                               offset_expr.X_add_symbol, 0, NULL);
-                 used_at = 0;
+                 relax_switch ();
                }
-             macro_build_lui (p, &icnt, &offset_expr, tempreg);
-             if (p != NULL)
-               p += 4;
-             macro_build (p, &icnt, &offset_expr, s, fmt, treg,
-                          (int) BFD_RELOC_LO16, tempreg);
+             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))
-               p = NULL;
-             else
+             if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+                 && !nopic_need_relax (offset_expr.X_add_symbol, 1))
                {
-                 frag_grow (28);
-                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              HAVE_32BIT_ADDRESSES ? HAVE_NEWABI
-                              ? "add" : "addu" : "daddu",
-                              "d,v,t", tempreg, breg, mips_gp_register);
-                 macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
-                              treg, (int) BFD_RELOC_GPREL16, tempreg);
-                 p = frag_var (rs_machine_dependent, 12, 0,
-                               RELAX_ENCODE (8, 12, 0, 8, 0, 0),
-                               offset_expr.X_add_symbol, 0, NULL);
+                 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 (p, &icnt, &offset_expr, tempreg);
-             if (p != NULL)
-               p += 4;
-             macro_build (p, &icnt, (expressionS *) NULL,
-                          HAVE_32BIT_ADDRESSES ? HAVE_NEWABI
-                          ? "add" : "addu" : "daddu",
-                          "d,v,t", tempreg, tempreg, breg);
-             if (p != NULL)
-               p += 4;
-             macro_build (p, &icnt, &offset_expr, s, fmt, treg,
-                          (int) BFD_RELOC_LO16, tempreg);
+             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_pic == SVR4_PIC && ! mips_big_got)
        {
-         char *p;
          int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
 
          /* If this is a reference to an external symbol, we want
@@ -6348,20 +5980,13 @@ macro (ip)
          assert (offset_expr.X_op == O_symbol);
          if (HAVE_NEWABI)
            {
-             macro_build ((char *) NULL, &icnt, &offset_expr,
-                          HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                          "t,o(b)", tempreg, BFD_RELOC_MIPS_GOT_PAGE,
-                          mips_gp_register);
+             macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                          BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
              if (breg != 0)
-               macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                            HAVE_32BIT_ADDRESSES ? "add" : "daddu",
-                            "d,v,t", tempreg, tempreg, breg);
-             macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt, treg,
-                          (int) BFD_RELOC_MIPS_GOT_OFST, tempreg);
-
-             if (! used_at)
-               return;
-
+               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;
@@ -6369,28 +5994,22 @@ macro (ip)
          if (expr1.X_add_number < -0x8000
              || expr1.X_add_number >= 0x8000)
            as_bad (_("PIC code offset overflow (max 16 signed bits)"));
-         frag_grow (20);
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld", "t,o(b)", tempreg,
-                      (int) lw_reloc_type, mips_gp_register);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
-         p = frag_var (rs_machine_dependent, 4, 0,
-                       RELAX_ENCODE (0, 4, -8, 0, 0, 0),
-                       offset_expr.X_add_symbol, 0, NULL);
-         macro_build (p, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
-                      "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
+         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 ((char *) NULL, &icnt, (expressionS *) NULL,
-                        HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                        "d,v,t", tempreg, tempreg, breg);
-         macro_build ((char *) NULL, &icnt, &expr1, s, fmt, treg,
-                      (int) BFD_RELOC_LO16, tempreg);
+           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_pic == SVR4_PIC && ! HAVE_NEWABI)
        {
-         int gpdel;
-         char *p;
+         int gpdelay;
 
          /* If this is a reference to an external symbol, we want
               lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
@@ -6414,50 +6033,31 @@ macro (ip)
          if (expr1.X_add_number < -0x8000
              || expr1.X_add_number >= 0x8000)
            as_bad (_("PIC code offset overflow (max 16 signed bits)"));
-         if (reg_needs_delay (mips_gp_register))
-           gpdel = 4;
-         else
-           gpdel = 0;
-         frag_grow (36);
-         macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
-                      tempreg, (int) BFD_RELOC_MIPS_GOT_HI16);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                      HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                      "d,v,t", tempreg, tempreg, mips_gp_register);
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                      "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT_LO16,
-                      tempreg);
-         p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
-                       RELAX_ENCODE (12, 12 + gpdel, gpdel, 8 + gpdel, 0, 0),
-                       offset_expr.X_add_symbol, 0, NULL);
-         if (gpdel > 0)
-           {
-             macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
-             p += 4;
-           }
-         macro_build (p, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                      "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16,
+         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);
-         p += 4;
-         macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
-         p += 4;
-         macro_build (p, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
-                      "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
+         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 ((char *) NULL, &icnt, (expressionS *) NULL,
-                        HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                        "d,v,t", tempreg, tempreg, breg);
-         macro_build ((char *) NULL, &icnt, &expr1, s, fmt, treg,
-                      (int) BFD_RELOC_LO16, tempreg);
+           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_pic == SVR4_PIC && HAVE_NEWABI)
        {
-         char *p;
-         int bregsz = breg != 0 ? 4 : 0;
-
          /* If this is a reference to an external symbol, we want
               lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
               add      $tempreg,$tempreg,$gp
@@ -6467,93 +6067,54 @@ macro (ip)
               lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_PAGE)
               <op>     $treg,<sym>($tempreg)   (BFD_RELOC_MIPS_GOT_OFST)  */
          assert (offset_expr.X_op == O_symbol);
-         frag_now->tc_frag_data.tc_fr_offset =
-           expr1.X_add_number = offset_expr.X_add_number;
+         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)"));
-         frag_grow (36);
-         macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
-                      tempreg, (int) BFD_RELOC_MIPS_GOT_HI16);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                      HAVE_32BIT_ADDRESSES ? "add" : "daddu",
-                      "d,v,t", tempreg, tempreg, mips_gp_register);
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                      "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT_LO16,
-                      tempreg);
+         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 ((char *) NULL, &icnt, (expressionS *) NULL,
-                        HAVE_32BIT_ADDRESSES ? "add" : "daddu",
-                        "d,v,t", tempreg, tempreg, breg);
-         macro_build ((char *) NULL, &icnt, &expr1, s, fmt, treg,
-                      (int) BFD_RELOC_LO16, tempreg);
+           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;
-         p = frag_var (rs_machine_dependent, 12 + bregsz, 0,
-                       RELAX_ENCODE (16 + bregsz, 8 + bregsz,
-                                     0, 4 + bregsz, 0, 0),
-                       offset_expr.X_add_symbol, 0, NULL);
-         macro_build (p, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                      "t,o(b)", tempreg,
-                      (int) BFD_RELOC_MIPS_GOT_PAGE,
-                      mips_gp_register);
+         macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                      BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
          if (breg != 0)
-           macro_build (p + 4, &icnt, (expressionS *) NULL,
-                        HAVE_32BIT_ADDRESSES ? "add" : "daddu",
-                        "d,v,t", tempreg, tempreg, breg);
-         macro_build (p + 4 + bregsz, &icnt, &offset_expr, s, fmt, treg,
-                      (int) BFD_RELOC_MIPS_GOT_OFST, tempreg);
-       }
-      else if (mips_pic == EMBEDDED_PIC)
-       {
-         /* If there is no base register, we want
-              <op>     $treg,<sym>($gp)        (BFD_RELOC_GPREL16)
-            If there is a base register, we want
-              addu     $tempreg,$breg,$gp
-              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_GPREL16)
-            */
-         assert (offset_expr.X_op == O_symbol);
-         if (breg == 0)
-           {
-             macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
-                          treg, (int) BFD_RELOC_GPREL16, mips_gp_register);
-             used_at = 0;
-           }
-         else
-           {
-             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                          "d,v,t", tempreg, breg, mips_gp_register);
-             macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
-                          treg, (int) BFD_RELOC_GPREL16, tempreg);
-           }
+           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 ();
 
-      if (! used_at)
-       return;
-
       break;
 
     case M_LI:
     case M_LI_S:
-      load_register (&icnt, treg, &imm_expr, 0);
-      return;
+      load_register (treg, &imm_expr, 0);
+      break;
 
     case M_DLI:
-      load_register (&icnt, treg, &imm_expr, 1);
-      return;
+      load_register (treg, &imm_expr, 1);
+      break;
 
     case M_LI_SS:
       if (imm_expr.X_op == O_constant)
        {
-         load_register (&icnt, AT, &imm_expr, 0);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                      "mtc1", "t,G", AT, treg);
+         used_at = 1;
+         load_register (AT, &imm_expr, 0);
+         macro_build (NULL, "mtc1", "t,G", AT, treg);
          break;
        }
       else
@@ -6563,9 +6124,9 @@ macro (ip)
                                           (offset_expr.X_add_symbol)),
                             ".lit4") == 0
                  && offset_expr.X_add_number == 0);
-         macro_build ((char *) NULL, &icnt, &offset_expr, "lwc1", "T,o(b)",
-                      treg, (int) BFD_RELOC_MIPS_LITERAL, mips_gp_register);
-         return;
+         macro_build (&offset_expr, "lwc1", "T,o(b)", treg,
+                      BFD_RELOC_MIPS_LITERAL, mips_gp_register);
+         break;
        }
 
     case M_LI_D:
@@ -6576,7 +6137,7 @@ macro (ip)
       if (imm_expr.X_op == O_constant || imm_expr.X_op == O_big)
        {
          if (HAVE_64BIT_GPRS)
-           load_register (&icnt, treg, &imm_expr, 1);
+           load_register (treg, &imm_expr, 1);
          else
            {
              int hreg, lreg;
@@ -6593,70 +6154,56 @@ macro (ip)
                }
 
              if (hreg <= 31)
-               load_register (&icnt, hreg, &imm_expr, 0);
+               load_register (hreg, &imm_expr, 0);
              if (lreg <= 31)
                {
                  if (offset_expr.X_op == O_absent)
-                   move_register (&icnt, lreg, 0);
+                   move_register (lreg, 0);
                  else
                    {
                      assert (offset_expr.X_op == O_constant);
-                     load_register (&icnt, lreg, &offset_expr, 0);
+                     load_register (lreg, &offset_expr, 0);
                    }
                }
            }
-         return;
+         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 (NULL, &icnt, &offset_expr, AT);
+         macro_build_lui (&offset_expr, AT);
+         used_at = 1;
        }
       else if (mips_pic == SVR4_PIC)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                      "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16,
-                      mips_gp_register);
-       }
-      else if (mips_pic == EMBEDDED_PIC)
-       {
-         /* For embedded PIC we pick up the entire address off $gp in
-            a single instruction.  */
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu", "t,r,j", AT,
-                      mips_gp_register, (int) BFD_RELOC_GPREL16);
-         offset_expr.X_op = O_constant;
-         offset_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;
        }
       else
        abort ();
 
       /* Now we load the register(s).  */
       if (HAVE_64BIT_GPRS)
-       macro_build ((char *) NULL, &icnt, &offset_expr, "ld", "t,o(b)",
-                    treg, (int) BFD_RELOC_LO16, AT);
+       {
+         used_at = 1;
+         macro_build (&offset_expr, "ld", "t,o(b)", treg, BFD_RELOC_LO16, AT);
+       }
       else
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr, "lw", "t,o(b)",
-                      treg, (int) BFD_RELOC_LO16, 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 ((char *) NULL, &icnt, &offset_expr, "lw", "t,o(b)",
-                          treg + 1, (int) BFD_RELOC_LO16, AT);
+             macro_build (&offset_expr, "lw", "t,o(b)",
+                          treg + 1, BFD_RELOC_LO16, AT);
            }
        }
-
-      /* To avoid confusion in tc_gen_reloc, we must ensure that this
-        does not become a variant frag.  */
-      frag_wane (frag_now);
-      frag_new (0);
-
       break;
 
     case M_LI_DD:
@@ -6667,26 +6214,23 @@ macro (ip)
          OFFSET_EXPR.  */
       if (imm_expr.X_op == O_constant || imm_expr.X_op == O_big)
        {
-         load_register (&icnt, AT, &imm_expr, HAVE_64BIT_FPRS);
+         used_at = 1;
+         load_register (AT, &imm_expr, HAVE_64BIT_FPRS);
          if (HAVE_64BIT_FPRS)
            {
              assert (HAVE_64BIT_GPRS);
-             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                          "dmtc1", "t,S", AT, treg);
+             macro_build (NULL, "dmtc1", "t,S", AT, treg);
            }
          else
            {
-             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                          "mtc1", "t,G", AT, treg + 1);
+             macro_build (NULL, "mtc1", "t,G", AT, treg + 1);
              if (offset_expr.X_op == O_absent)
-               macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                            "mtc1", "t,G", 0, treg);
+               macro_build (NULL, "mtc1", "t,G", 0, treg);
              else
                {
                  assert (offset_expr.X_op == O_constant);
-                 load_register (&icnt, AT, &offset_expr, 0);
-                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              "mtc1", "t,G", AT, treg);
+                 load_register (AT, &offset_expr, 0);
+                 macro_build (NULL, "mtc1", "t,G", AT, treg);
                }
            }
          break;
@@ -6699,10 +6243,9 @@ macro (ip)
        {
          if (mips_opts.isa != ISA_MIPS1)
            {
-             macro_build ((char *) NULL, &icnt, &offset_expr, "ldc1",
-                          "T,o(b)", treg, (int) BFD_RELOC_MIPS_LITERAL,
-                          mips_gp_register);
-             return;
+             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;
@@ -6711,27 +6254,20 @@ macro (ip)
       else
        {
          assert (strcmp (s, RDATA_SECTION_NAME) == 0);
+         used_at = 1;
          if (mips_pic == SVR4_PIC)
-           macro_build ((char *) NULL, &icnt, &offset_expr,
-                        HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                        "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16,
-                        mips_gp_register);
+           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 (NULL, &icnt, &offset_expr, AT);
+             macro_build_lui (&offset_expr, AT);
            }
 
          if (mips_opts.isa != ISA_MIPS1)
            {
-             macro_build ((char *) NULL, &icnt, &offset_expr, "ldc1",
-                          "T,o(b)", treg, (int) BFD_RELOC_LO16, AT);
-
-             /* To avoid confusion in tc_gen_reloc, we must ensure
-                that this does not become a variant frag.  */
-             frag_wane (frag_now);
-             frag_new (0);
-
+             macro_build (&offset_expr, "ldc1", "T,o(b)",
+                          treg, BFD_RELOC_LO16, AT);
              break;
            }
          breg = AT;
@@ -6740,33 +6276,23 @@ macro (ip)
        }
 
     case M_L_DOB:
-      if (mips_arch == CPU_R4650)
+      if (mips_opts.arch == CPU_R4650)
        {
          as_bad (_("opcode not supported on this processor"));
-         return;
+         break;
        }
       /* Even on a big endian machine $fn comes before $fn+1.  We have
         to adjust when loading from memory.  */
       r = BFD_RELOC_LO16;
     dob:
       assert (mips_opts.isa == ISA_MIPS1);
-      macro_build ((char *) NULL, &icnt, &offset_expr, "lwc1", "T,o(b)",
-                  target_big_endian ? treg + 1 : treg,
-                  (int) r, breg);
+      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 ((char *) NULL, &icnt, &offset_expr, "lwc1", "T,o(b)",
-                  target_big_endian ? treg : treg + 1,
-                  (int) r, breg);
-
-      /* To avoid confusion in tc_gen_reloc, we must ensure that this
-        does not become a variant frag.  */
-      frag_wane (frag_now);
-      frag_new (0);
-
-      if (breg != AT)
-       return;
+      macro_build (&offset_expr, "lwc1", "T,o(b)",
+                  target_big_endian ? treg : treg + 1, r, breg);
       break;
 
     case M_L_DAB:
@@ -6781,10 +6307,10 @@ macro (ip)
        * But, the resulting address is the same after relocation so why
        * generate the extra instruction?
        */
-      if (mips_arch == CPU_R4650)
+      if (mips_opts.arch == CPU_R4650)
        {
          as_bad (_("opcode not supported on this processor"));
-         return;
+         break;
        }
       /* Itbl support may require additional care here.  */
       coproc = 1;
@@ -6799,10 +6325,10 @@ macro (ip)
       goto ldd_std;
 
     case M_S_DAB:
-      if (mips_arch == CPU_R4650)
+      if (mips_opts.arch == CPU_R4650)
        {
          as_bad (_("opcode not supported on this processor"));
-         return;
+         break;
        }
 
       if (mips_opts.isa != ISA_MIPS1)
@@ -6839,11 +6365,6 @@ macro (ip)
       fmt = "t,o(b)";
 
     ldd_std:
-      /* We do _not_ bother to allow embedded PIC (symbol-local_symbol)
-        loads for the case of doing a pair of loads to simulate an 'ld'.
-        This is not currently done by the compiler, and assembly coders
-        writing embedded-pic code can cope.  */
-
       if (offset_expr.X_op != O_symbol
          && offset_expr.X_op != O_constant)
        {
@@ -6861,8 +6382,6 @@ macro (ip)
       if (mips_pic == NO_PIC
          || offset_expr.X_op == O_constant)
        {
-         char *p;
-
          /* 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)
@@ -6877,39 +6396,25 @@ macro (ip)
             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 ((valueT) offset_expr.X_add_number > MAX_GPREL_OFFSET
-             || nopic_need_relax (offset_expr.X_add_symbol, 1))
-           {
-             p = NULL;
-             used_at = 1;
-           }
-         else
+         if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+             && !nopic_need_relax (offset_expr.X_add_symbol, 1))
            {
-             int off;
-
+             relax_start (offset_expr.X_add_symbol);
              if (breg == 0)
                {
-                 frag_grow (28);
                  tempreg = mips_gp_register;
-                 off = 0;
-                 used_at = 0;
                }
              else
                {
-                 frag_grow (36);
-                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              HAVE_32BIT_ADDRESSES ? HAVE_NEWABI
-                              ? "add" : "addu" : "daddu",
-                              "d,v,t", AT, breg, mips_gp_register);
+                 macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+                              AT, breg, mips_gp_register);
                  tempreg = AT;
-                 off = 4;
                  used_at = 1;
                }
 
              /* Itbl support may require additional care here.  */
-             macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
-                          coproc ? treg + 1 : treg,
-                          (int) BFD_RELOC_GPREL16, tempreg);
+             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
@@ -6917,15 +6422,11 @@ macro (ip)
              hold_mips_optimize = mips_optimize;
              mips_optimize = 2;
              /* Itbl support may require additional care here.  */
-             macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
-                          coproc ? treg : treg + 1,
-                          (int) BFD_RELOC_GPREL16, tempreg);
+             macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
+                          BFD_RELOC_GPREL16, tempreg);
              mips_optimize = hold_mips_optimize;
 
-             p = frag_var (rs_machine_dependent, 12 + off, 0,
-                           RELAX_ENCODE (8 + off, 12 + off, 0, 4 + off, 1,
-                                         used_at && mips_opts.noat),
-                           offset_expr.X_add_symbol, 0, NULL);
+             relax_switch ();
 
              /* We just generated two relocs.  When tc_gen_reloc
                 handles this case, it will skip the first reloc and
@@ -6948,35 +6449,23 @@ macro (ip)
                  offset_expr.X_op = O_constant;
                }
            }
-         macro_build_lui (p, &icnt, &offset_expr, AT);
-         if (p != NULL)
-           p += 4;
+         used_at = 1;
+         macro_build_lui (&offset_expr, AT);
          if (breg != 0)
-           {
-             macro_build (p, &icnt, (expressionS *) NULL,
-                          HAVE_32BIT_ADDRESSES ? HAVE_NEWABI
-                          ? "add" : "addu" : "daddu",
-                          "d,v,t", AT, breg, AT);
-             if (p != NULL)
-               p += 4;
-           }
+           macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
          /* Itbl support may require additional care here.  */
-         macro_build (p, &icnt, &offset_expr, s, fmt,
-                      coproc ? treg + 1 : treg,
-                      (int) BFD_RELOC_LO16, AT);
-         if (p != NULL)
-           p += 4;
+         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 (p, &icnt, &offset_expr, s, fmt,
-                      coproc ? treg : treg + 1,
-                      (int) BFD_RELOC_LO16, AT);
+         macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
+                      BFD_RELOC_LO16, AT);
+         if (mips_relax.sequence)
+           relax_end ();
        }
       else if (mips_pic == SVR4_PIC && ! mips_big_got)
        {
-         int off;
-
          /* If this is a reference to an external symbol, we want
               lw       $at,<sym>($gp)          (BFD_RELOC_MIPS_GOT16)
               nop
@@ -6992,48 +6481,39 @@ macro (ip)
             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)"));
-         if (breg == 0)
-           off = 0;
-         else
-           off = 4;
-         frag_grow (24 + off);
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld", "t,o(b)", AT,
-                      (int) BFD_RELOC_MIPS_GOT16, mips_gp_register);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
+         load_got_offset (AT, &offset_expr);
+         load_delay_nop ();
          if (breg != 0)
-           macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                        HAVE_32BIT_ADDRESSES ? HAVE_NEWABI
-                        ? "add" : "addu" : "daddu",
-                        "d,v,t", AT, breg, AT);
-         /* Itbl support may require additional care here.  */
-         macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
-                      coproc ? treg + 1 : treg,
-                      (int) BFD_RELOC_LO16, AT);
-         expr1.X_add_number += 4;
+           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.  */
-         macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
-                      coproc ? treg : treg + 1,
-                      (int) BFD_RELOC_LO16, AT);
-         mips_optimize = hold_mips_optimize;
+         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 ();
 
-         (void) frag_var (rs_machine_dependent, 0, 0,
-                          RELAX_ENCODE (0, 0, -16 - off, -8, 1, 0),
-                          offset_expr.X_add_symbol, 0, NULL);
+         mips_optimize = hold_mips_optimize;
        }
       else if (mips_pic == SVR4_PIC)
        {
-         int gpdel, off;
-         char *p;
+         int gpdelay;
 
          /* If this is a reference to an external symbol, we want
               lui      $at,<sym>               (BFD_RELOC_MIPS_GOT_HI16)
@@ -7056,34 +6536,20 @@ macro (ip)
          if (expr1.X_add_number < -0x8000
              || expr1.X_add_number >= 0x8000 - 4)
            as_bad (_("PIC code offset overflow (max 16 signed bits)"));
-         if (reg_needs_delay (mips_gp_register))
-           gpdel = 4;
-         else
-           gpdel = 0;
-         if (breg == 0)
-           off = 0;
-         else
-           off = 4;
-         frag_grow (56);
-         macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
-                      AT, (int) BFD_RELOC_MIPS_GOT_HI16);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                      HAVE_32BIT_ADDRESSES ? HAVE_NEWABI
-                      ? "add" : "addu" : "daddu",
-                      "d,v,t", AT, AT, mips_gp_register);
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                      "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT_LO16, AT);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
+         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 ((char *) NULL, &icnt, (expressionS *) NULL,
-                        HAVE_32BIT_ADDRESSES ? HAVE_NEWABI
-                        ? "add" : "addu" : "daddu",
-                        "d,v,t", AT, breg, AT);
+           macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
          /* Itbl support may require additional care here.  */
-         macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
-                      coproc ? treg + 1 : treg,
-                      (int) BFD_RELOC_LO16, AT);
+         macro_build (&expr1, s, fmt, coproc ? treg + 1 : treg,
+                      BFD_RELOC_LO16, AT);
          expr1.X_add_number += 4;
 
          /* Set mips_optimize to 2 to avoid inserting an undesired
@@ -7091,93 +6557,38 @@ macro (ip)
          hold_mips_optimize = mips_optimize;
          mips_optimize = 2;
          /* Itbl support may require additional care here.  */
-         macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
-                      coproc ? treg : treg + 1,
-                      (int) BFD_RELOC_LO16, AT);
+         macro_build (&expr1, s, fmt, coproc ? treg : treg + 1,
+                      BFD_RELOC_LO16, AT);
          mips_optimize = hold_mips_optimize;
          expr1.X_add_number -= 4;
 
-         p = frag_var (rs_machine_dependent, 16 + gpdel + off, 0,
-                       RELAX_ENCODE (24 + off, 16 + gpdel + off, gpdel,
-                                     8 + gpdel + off, 1, 0),
-                       offset_expr.X_add_symbol, 0, NULL);
-         if (gpdel > 0)
-           {
-             macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
-             p += 4;
-           }
-         macro_build (p, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                      "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16,
-                      mips_gp_register);
-         p += 4;
-         macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
-         p += 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 (p, &icnt, (expressionS *) NULL,
-                          HAVE_32BIT_ADDRESSES ? HAVE_NEWABI
-                          ? "add" : "addu" : "daddu",
-                          "d,v,t", AT, breg, AT);
-             p += 4;
-           }
+           macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
          /* Itbl support may require additional care here.  */
-         macro_build (p, &icnt, &expr1, s, fmt,
-                      coproc ? treg + 1 : treg,
-                      (int) BFD_RELOC_LO16, AT);
-         p += 4;
-         expr1.X_add_number += 4;
+         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 (p, &icnt, &expr1, s, fmt,
-                      coproc ? treg : treg + 1,
-                      (int) BFD_RELOC_LO16, AT);
+         macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
+                      BFD_RELOC_LO16, AT);
          mips_optimize = hold_mips_optimize;
-       }
-      else if (mips_pic == EMBEDDED_PIC)
-       {
-         /* If there is no base register, we use
-              <op>     $treg,<sym>($gp)        (BFD_RELOC_GPREL16)
-              <op>     $treg+1,<sym>+4($gp)    (BFD_RELOC_GPREL16)
-            If we have a base register, we use
-              addu     $at,$breg,$gp
-              <op>     $treg,<sym>($at)        (BFD_RELOC_GPREL16)
-              <op>     $treg+1,<sym>+4($at)    (BFD_RELOC_GPREL16)
-            */
-         if (breg == 0)
-           {
-             tempreg = mips_gp_register;
-             used_at = 0;
-           }
-         else
-           {
-             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                          "d,v,t", AT, breg, mips_gp_register);
-             tempreg = AT;
-             used_at = 1;
-           }
-
-         /* Itbl support may require additional care here.  */
-         macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
-                      coproc ? treg + 1 : treg,
-                      (int) BFD_RELOC_GPREL16, tempreg);
-         offset_expr.X_add_number += 4;
-         /* Itbl support may require additional care here.  */
-         macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
-                      coproc ? treg : treg + 1,
-                      (int) BFD_RELOC_GPREL16, tempreg);
+         relax_end ();
        }
       else
        abort ();
 
-      if (! used_at)
-       return;
-
       break;
 
     case M_LD_OB:
@@ -7187,12 +6598,10 @@ macro (ip)
       s = "sw";
     sd_ob:
       assert (HAVE_32BIT_ADDRESSES);
-      macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
-                  (int) BFD_RELOC_LO16, breg);
+      macro_build (&offset_expr, s, "t,o(b)", treg, BFD_RELOC_LO16, breg);
       offset_expr.X_add_number += 4;
-      macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg + 1,
-                  (int) BFD_RELOC_LO16, breg);
-      return;
+      macro_build (&offset_expr, s, "t,o(b)", treg + 1, BFD_RELOC_LO16, breg);
+      break;
 
    /* New code added to support COPZ instructions.
       This code builds table entries out of the macros in mip_opcodes.
@@ -7225,13 +6634,12 @@ macro (ip)
     copz:
       /* For now we just do C (same as Cz).  The parameter will be
          stored in insn_opcode by mips_ip.  */
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "C",
-                  ip->insn_opcode);
-      return;
+      macro_build (NULL, s, "C", ip->insn_opcode);
+      break;
 
     case M_MOVE:
-      move_register (&icnt, dreg, sreg);
-      return;
+      move_register (dreg, sreg);
+      break;
 
 #ifdef LOSING_COMPILER
     default:
@@ -7249,24 +6657,22 @@ macro (ip)
          s = ip->insn_mo->name;
          s2 = "cop3";
          coproc = ITBL_DECODE_PNUM (immed_expr);;
-         macro_build ((char *) NULL, &icnt, &immed_expr, s, "C");
-         return;
+         macro_build (&immed_expr, s, "C");
+         break;
        }
       macro2 (ip);
-      return;
+      break;
     }
-  if (mips_opts.noat)
-    as_warn (_("Macro used $at after \".set noat\""));
+  if (mips_opts.noat && used_at)
+    as_bad (_("Macro used $at after \".set noat\""));
 }
 
 static void
-macro2 (ip)
-     struct mips_cl_insn *ip;
+macro2 (struct mips_cl_insn *ip)
 {
   register int treg, sreg, dreg, breg;
   int tempreg;
   int mask;
-  int icnt = 0;
   int used_at;
   expressionS expr1;
   const char *s;
@@ -7280,7 +6686,6 @@ macro2 (ip)
   int off;
   offsetT maxnum;
   bfd_reloc_code_real_type r;
-  char *p;
 
   treg = (ip->insn_opcode >> 16) & 0x1f;
   dreg = (ip->insn_opcode >> 11) & 0x1f;
@@ -7299,11 +6704,9 @@ macro2 (ip)
     case M_DMUL:
       dbl = 1;
     case M_MUL:
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                  dbl ? "dmultu" : "multu", "s,t", sreg, treg);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "mflo", "d",
-                  dreg);
-      return;
+      macro_build (NULL, dbl ? "dmultu" : "multu", "s,t", sreg, treg);
+      macro_build (NULL, "mflo", "d", dreg);
+      break;
 
     case M_DMUL_I:
       dbl = 1;
@@ -7311,11 +6714,10 @@ macro2 (ip)
       /* The MIPS assembler some times generates shifts and adds.  I'm
         not trying to be that fancy. GCC should do this for us
         anyway.  */
-      load_register (&icnt, AT, &imm_expr, dbl);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                  dbl ? "dmult" : "mult", "s,t", sreg, AT);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "mflo", "d",
-                  dreg);
+      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);
       break;
 
     case M_DMULO_I:
@@ -7328,34 +6730,25 @@ macro2 (ip)
       dbl = 1;
     case M_MULO:
     do_mulo:
-      mips_emit_delays (TRUE);
-      ++mips_opts.noreorder;
-      mips_any_noreorder = 1;
+      start_noreorder ();
+      used_at = 1;
       if (imm)
-       load_register (&icnt, AT, &imm_expr, dbl);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                  dbl ? "dmult" : "mult", "s,t", sreg, imm ? AT : treg);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "mflo", "d",
-                  dreg);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                  dbl ? "dsra32" : "sra", "d,w,<", dreg, dreg, RA);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "mfhi", "d",
-                  AT);
+       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 ((char *) NULL, &icnt, (expressionS *) NULL, "tne",
-                    "s,t,q", dreg, AT, 6);
+       macro_build (NULL, "tne", "s,t,q", dreg, AT, 6);
       else
        {
          expr1.X_add_number = 8;
-         macro_build ((char *) NULL, &icnt, &expr1, "beq", "s,t,p", dreg,
-                      AT);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "",
-                      0);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "break",
-                      "c", 6);
+         macro_build (&expr1, "beq", "s,t,p", dreg, AT);
+         macro_build (NULL, "nop", "", 0);
+         macro_build (NULL, "break", "c", 6);
        }
-      --mips_opts.noreorder;
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "mflo", "d", dreg);
+      end_noreorder ();
+      macro_build (NULL, "mflo", "d", dreg);
       break;
 
     case M_DMULOU_I:
@@ -7368,35 +6761,28 @@ macro2 (ip)
       dbl = 1;
     case M_MULOU:
     do_mulou:
-      mips_emit_delays (TRUE);
-      ++mips_opts.noreorder;
-      mips_any_noreorder = 1;
+      start_noreorder ();
+      used_at = 1;
       if (imm)
-       load_register (&icnt, AT, &imm_expr, dbl);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                  dbl ? "dmultu" : "multu",
-                  "s,t", sreg, imm ? AT : treg);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "mfhi", "d",
-                  AT);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "mflo", "d",
-                  dreg);
+       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 ((char *) NULL, &icnt, (expressionS *) NULL, "tne",
-                    "s,t,q", AT, 0, 6);
+       macro_build (NULL, "tne", "s,t,q", AT, 0, 6);
       else
        {
          expr1.X_add_number = 8;
-         macro_build ((char *) NULL, &icnt, &expr1, "beq", "s,t,p", AT, 0);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "",
-                      0);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "break",
-                      "c", 6);
+         macro_build (&expr1, "beq", "s,t,p", AT, 0);
+         macro_build (NULL, "nop", "", 0);
+         macro_build (NULL, "break", "c", 6);
        }
-      --mips_opts.noreorder;
+      end_noreorder ();
       break;
 
     case M_DROL:
-      if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_arch))
+      if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
        {
          if (dreg == sreg)
            {
@@ -7406,28 +6792,20 @@ macro2 (ip)
          else
            {
              tempreg = dreg;
-             used_at = 0;
            }
-         macro_build ((char *) NULL, &icnt, NULL, "dnegu",
-                      "d,w", tempreg, treg);
-         macro_build ((char *) NULL, &icnt, NULL, "drorv",
-                      "d,t,s", dreg, sreg, tempreg);
-         if (used_at)
-           break;
-         return;
+         macro_build (NULL, "dnegu", "d,w", tempreg, treg);
+         macro_build (NULL, "drorv", "d,t,s", dreg, sreg, tempreg);
+         break;
        }
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "dsubu",
-                  "d,v,t", AT, 0, treg);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "dsrlv",
-                  "d,t,s", AT, sreg, AT);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "dsllv",
-                  "d,t,s", dreg, sreg, treg);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "or",
-                  "d,v,t", dreg, dreg, AT);
+      used_at = 1;
+      macro_build (NULL, "dsubu", "d,v,t", AT, 0, treg);
+      macro_build (NULL, "dsrlv", "d,t,s", AT, sreg, AT);
+      macro_build (NULL, "dsllv", "d,t,s", dreg, sreg, treg);
+      macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
       break;
 
     case M_ROL:
-      if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_arch))
+      if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
        {
          if (dreg == sreg)
            {
@@ -7437,24 +6815,16 @@ macro2 (ip)
          else
            {
              tempreg = dreg;
-             used_at = 0;
            }
-         macro_build ((char *) NULL, &icnt, NULL, "negu",
-                      "d,w", tempreg, treg);
-         macro_build ((char *) NULL, &icnt, NULL, "rorv",
-                      "d,t,s", dreg, sreg, tempreg);
-         if (used_at)
-           break;
-         return;
+         macro_build (NULL, "negu", "d,w", tempreg, treg);
+         macro_build (NULL, "rorv", "d,t,s", dreg, sreg, tempreg);
+         break;
        }
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "subu",
-                  "d,v,t", AT, 0, treg);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "srlv",
-                  "d,t,s", AT, sreg, AT);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sllv",
-                  "d,t,s", dreg, sreg, treg);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "or",
-                  "d,v,t", dreg, dreg, AT);
+      used_at = 1;
+      macro_build (NULL, "subu", "d,v,t", AT, 0, treg);
+      macro_build (NULL, "srlv", "d,t,s", AT, sreg, AT);
+      macro_build (NULL, "sllv", "d,t,s", dreg, sreg, treg);
+      macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
       break;
 
     case M_DROL_I:
@@ -7465,32 +6835,27 @@ macro2 (ip)
        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_arch))
+       if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
          {
            rot = (64 - rot) & 0x3f;
            if (rot >= 32)
-             macro_build ((char *) NULL, &icnt, NULL, "dror32",
-                          "d,w,<", dreg, sreg, rot - 32);
+             macro_build (NULL, "dror32", "d,w,<", dreg, sreg, rot - 32);
            else
-             macro_build ((char *) NULL, &icnt, NULL, "dror",
-                          "d,w,<", dreg, sreg, rot);
-           return;
+             macro_build (NULL, "dror", "d,w,<", dreg, sreg, rot);
+           break;
          }
        if (rot == 0)
          {
-           macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "dsrl",
-                        "d,w,<", dreg, sreg, 0);
-           return;
+           macro_build (NULL, "dsrl", "d,w,<", dreg, sreg, 0);
+           break;
          }
        l = (rot < 0x20) ? "dsll" : "dsll32";
        r = ((0x40 - rot) < 0x20) ? "dsrl" : "dsrl32";
        rot &= 0x1f;
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, l,
-                    "d,w,<", AT, sreg, rot);
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, r,
-                    "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f);
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "or",
-                    "d,v,t", dreg, dreg, AT);
+       used_at = 1;
+       macro_build (NULL, l, "d,w,<", AT, sreg, rot);
+       macro_build (NULL, r, "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f);
+       macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
       }
       break;
 
@@ -7501,59 +6866,47 @@ macro2 (ip)
        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_arch))
+       if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
          {
-           macro_build ((char *) NULL, &icnt, NULL, "ror",
-                        "d,w,<", dreg, sreg, (32 - rot) & 0x1f);
-           return;
+           macro_build (NULL, "ror", "d,w,<", dreg, sreg, (32 - rot) & 0x1f);
+           break;
          }
        if (rot == 0)
          {
-           macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "srl",
-                        "d,w,<", dreg, sreg, 0);
-           return;
+           macro_build (NULL, "srl", "d,w,<", dreg, sreg, 0);
+           break;
          }
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sll",
-                    "d,w,<", AT, sreg, rot);
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "srl",
-                    "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f);
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "or",
-                    "d,v,t", dreg, dreg, AT);
+       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;
 
     case M_DROR:
-      if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_arch))
+      if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
        {
-         macro_build ((char *) NULL, &icnt, NULL, "drorv",
-                      "d,t,s", dreg, sreg, treg);
-         return;
+         macro_build (NULL, "drorv", "d,t,s", dreg, sreg, treg);
+         break;
        }
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "dsubu",
-                  "d,v,t", AT, 0, treg);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "dsllv",
-                  "d,t,s", AT, sreg, AT);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "dsrlv",
-                  "d,t,s", dreg, sreg, treg);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "or",
-                  "d,v,t", dreg, dreg, AT);
+      used_at = 1;
+      macro_build (NULL, "dsubu", "d,v,t", AT, 0, 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_arch))
+      if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
        {
-         macro_build ((char *) NULL, &icnt, NULL, "rorv",
-                      "d,t,s", dreg, sreg, treg);
-         return;
+         macro_build (NULL, "rorv", "d,t,s", dreg, sreg, treg);
+         break;
        }
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "subu",
-                  "d,v,t", AT, 0, treg);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sllv",
-                  "d,t,s", AT, sreg, AT);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "srlv",
-                  "d,t,s", dreg, sreg, treg);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "or",
-                  "d,v,t", dreg, dreg, AT);
+      used_at = 1;
+      macro_build (NULL, "subu", "d,v,t", AT, 0, 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_DROR_I:
@@ -7564,31 +6917,26 @@ macro2 (ip)
        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_arch))
+       if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
          {
            if (rot >= 32)
-             macro_build ((char *) NULL, &icnt, NULL, "dror32",
-                          "d,w,<", dreg, sreg, rot - 32);
+             macro_build (NULL, "dror32", "d,w,<", dreg, sreg, rot - 32);
            else
-             macro_build ((char *) NULL, &icnt, NULL, "dror",
-                          "d,w,<", dreg, sreg, rot);
-           return;
+             macro_build (NULL, "dror", "d,w,<", dreg, sreg, rot);
+           break;
          }
        if (rot == 0)
          {
-           macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "dsrl",
-                        "d,w,<", dreg, sreg, 0);
-           return;
+           macro_build (NULL, "dsrl", "d,w,<", dreg, sreg, 0);
+           break;
          }
        r = (rot < 0x20) ? "dsrl" : "dsrl32";
        l = ((0x40 - rot) < 0x20) ? "dsll" : "dsll32";
        rot &= 0x1f;
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, r,
-                    "d,w,<", AT, sreg, rot);
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, l,
-                    "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f);
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "or",
-                    "d,v,t", dreg, dreg, AT);
+       used_at = 1;
+       macro_build (NULL, r, "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;
 
@@ -7599,106 +6947,86 @@ macro2 (ip)
        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_arch))
+       if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
          {
-           macro_build ((char *) NULL, &icnt, NULL, "ror",
-                        "d,w,<", dreg, sreg, rot);
-           return;
+           macro_build (NULL, "ror", "d,w,<", dreg, sreg, rot);
+           break;
          }
        if (rot == 0)
          {
-           macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "srl",
-                        "d,w,<", dreg, sreg, 0);
-           return;
+           macro_build (NULL, "srl", "d,w,<", dreg, sreg, 0);
+           break;
          }
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "srl",
-                    "d,w,<", AT, sreg, rot);
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sll",
-                    "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f);
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "or",
-                    "d,v,t", dreg, dreg, AT);
+       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;
 
     case M_S_DOB:
-      if (mips_arch == CPU_R4650)
+      if (mips_opts.arch == CPU_R4650)
        {
          as_bad (_("opcode not supported on this processor"));
-         return;
+         break;
        }
       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 ((char *) NULL, &icnt, &offset_expr, "swc1", "T,o(b)",
-                  target_big_endian ? treg + 1 : treg,
-                  (int) BFD_RELOC_LO16, breg);
+      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 ((char *) NULL, &icnt, &offset_expr, "swc1", "T,o(b)",
-                  target_big_endian ? treg : treg + 1,
-                  (int) BFD_RELOC_LO16, breg);
-      return;
+      macro_build (&offset_expr, "swc1", "T,o(b)",
+                  target_big_endian ? treg : treg + 1, BFD_RELOC_LO16, breg);
+      break;
 
     case M_SEQ:
       if (sreg == 0)
-       macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg,
-                    treg, (int) BFD_RELOC_LO16);
+       macro_build (&expr1, "sltiu", "t,r,j", dreg, treg, BFD_RELOC_LO16);
       else if (treg == 0)
-       macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg,
-                    sreg, (int) BFD_RELOC_LO16);
+       macro_build (&expr1, "sltiu", "t,r,j", dreg, sreg, BFD_RELOC_LO16);
       else
        {
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "xor",
-                      "d,v,t", dreg, sreg, treg);
-         macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg,
-                      dreg, (int) BFD_RELOC_LO16);
+         macro_build (NULL, "xor", "d,v,t", dreg, sreg, treg);
+         macro_build (&expr1, "sltiu", "t,r,j", dreg, dreg, BFD_RELOC_LO16);
        }
-      return;
+      break;
 
     case M_SEQ_I:
       if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
        {
-         macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg,
-                      sreg, (int) BFD_RELOC_LO16);
-         return;
+         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 (&icnt, dreg, 0);
-         return;
+         move_register (dreg, 0);
+         break;
        }
       if (imm_expr.X_op == O_constant
          && imm_expr.X_add_number >= 0
          && imm_expr.X_add_number < 0x10000)
        {
-         macro_build ((char *) NULL, &icnt, &imm_expr, "xori", "t,r,i", dreg,
-                      sreg, (int) BFD_RELOC_LO16);
-         used_at = 0;
+         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 ((char *) NULL, &icnt, &imm_expr,
-                      HAVE_32BIT_GPRS ? "addiu" : "daddiu",
-                      "t,r,j", dreg, sreg,
-                      (int) BFD_RELOC_LO16);
-         used_at = 0;
+         macro_build (&imm_expr, HAVE_32BIT_GPRS ? "addiu" : "daddiu",
+                      "t,r,j", dreg, sreg, BFD_RELOC_LO16);
        }
       else
        {
-         load_register (&icnt, AT, &imm_expr, HAVE_64BIT_GPRS);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "xor",
-                      "d,v,t", dreg, sreg, AT);
+         load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+         macro_build (NULL, "xor", "d,v,t", dreg, sreg, AT);
          used_at = 1;
        }
-      macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg, dreg,
-                  (int) BFD_RELOC_LO16);
-      if (used_at)
-       break;
-      return;
+      macro_build (&expr1, "sltiu", "t,r,j", dreg, dreg, BFD_RELOC_LO16);
+      break;
 
     case M_SGE:                /* sreg >= treg <==> not (sreg < treg) */
       s = "slt";
@@ -7706,11 +7034,9 @@ macro2 (ip)
     case M_SGEU:
       s = "sltu";
     sge:
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "d,v,t",
-                  dreg, sreg, treg);
-      macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg,
-                  (int) BFD_RELOC_LO16);
-      return;
+      macro_build (NULL, s, "d,v,t", dreg, sreg, treg);
+      macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
+      break;
 
     case M_SGE_I:              /* sreg >= I <==> not (sreg < I) */
     case M_SGEU_I:
@@ -7718,24 +7044,18 @@ macro2 (ip)
          && imm_expr.X_add_number >= -0x8000
          && imm_expr.X_add_number < 0x8000)
        {
-         macro_build ((char *) NULL, &icnt, &imm_expr,
-                      mask == M_SGE_I ? "slti" : "sltiu",
-                      "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16);
-         used_at = 0;
+         macro_build (&imm_expr, mask == M_SGE_I ? "slti" : "sltiu", "t,r,j",
+                      dreg, sreg, BFD_RELOC_LO16);
        }
       else
        {
-         load_register (&icnt, AT, &imm_expr, HAVE_64BIT_GPRS);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                      mask == M_SGE_I ? "slt" : "sltu", "d,v,t", dreg, sreg,
-                      AT);
+         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 ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg,
-                  (int) BFD_RELOC_LO16);
-      if (used_at)
-       break;
-      return;
+      macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
+      break;
 
     case M_SGT:                /* sreg > treg  <==>  treg < sreg */
       s = "slt";
@@ -7743,9 +7063,8 @@ macro2 (ip)
     case M_SGTU:
       s = "sltu";
     sgt:
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "d,v,t",
-                  dreg, treg, sreg);
-      return;
+      macro_build (NULL, s, "d,v,t", dreg, treg, sreg);
+      break;
 
     case M_SGT_I:              /* sreg > I  <==>  I < sreg */
       s = "slt";
@@ -7753,9 +7072,9 @@ macro2 (ip)
     case M_SGTU_I:
       s = "sltu";
     sgti:
-      load_register (&icnt, AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "d,v,t",
-                  dreg, AT, sreg);
+      used_at = 1;
+      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+      macro_build (NULL, s, "d,v,t", dreg, AT, sreg);
       break;
 
     case M_SLE:        /* sreg <= treg  <==>  treg >= sreg  <==>  not (treg < sreg) */
@@ -7764,11 +7083,9 @@ macro2 (ip)
     case M_SLEU:
       s = "sltu";
     sle:
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "d,v,t",
-                  dreg, treg, sreg);
-      macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg,
-                  (int) BFD_RELOC_LO16);
-      return;
+      macro_build (NULL, s, "d,v,t", dreg, treg, sreg);
+      macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
+      break;
 
     case M_SLE_I:      /* sreg <= I <==> I >= sreg <==> not (I < sreg) */
       s = "slt";
@@ -7776,11 +7093,10 @@ macro2 (ip)
     case M_SLEU_I:
       s = "sltu";
     slei:
-      load_register (&icnt, AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "d,v,t",
-                  dreg, AT, sreg);
-      macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg,
-                  (int) BFD_RELOC_LO16);
+      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_SLT_I:
@@ -7788,13 +7104,12 @@ macro2 (ip)
          && imm_expr.X_add_number >= -0x8000
          && imm_expr.X_add_number < 0x8000)
        {
-         macro_build ((char *) NULL, &icnt, &imm_expr, "slti", "t,r,j",
-                      dreg, sreg, (int) BFD_RELOC_LO16);
-         return;
+         macro_build (&imm_expr, "slti", "t,r,j", dreg, sreg, BFD_RELOC_LO16);
+         break;
        }
-      load_register (&icnt, AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "slt", "d,v,t",
-                  dreg, sreg, AT);
+      used_at = 1;
+      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+      macro_build (NULL, "slt", "d,v,t", dreg, sreg, AT);
       break;
 
     case M_SLTU_I:
@@ -7802,77 +7117,63 @@ macro2 (ip)
          && imm_expr.X_add_number >= -0x8000
          && imm_expr.X_add_number < 0x8000)
        {
-         macro_build ((char *) NULL, &icnt, &imm_expr, "sltiu", "t,r,j",
-                      dreg, sreg, (int) BFD_RELOC_LO16);
-         return;
+         macro_build (&imm_expr, "sltiu", "t,r,j", dreg, sreg,
+                      BFD_RELOC_LO16);
+         break;
        }
-      load_register (&icnt, AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sltu",
-                  "d,v,t", dreg, sreg, AT);
+      used_at = 1;
+      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+      macro_build (NULL, "sltu", "d,v,t", dreg, sreg, AT);
       break;
 
     case M_SNE:
       if (sreg == 0)
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sltu",
-                    "d,v,t", dreg, 0, treg);
+       macro_build (NULL, "sltu", "d,v,t", dreg, 0, treg);
       else if (treg == 0)
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sltu",
-                    "d,v,t", dreg, 0, sreg);
+       macro_build (NULL, "sltu", "d,v,t", dreg, 0, sreg);
       else
        {
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "xor",
-                      "d,v,t", dreg, sreg, treg);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sltu",
-                      "d,v,t", dreg, 0, dreg);
+         macro_build (NULL, "xor", "d,v,t", dreg, sreg, treg);
+         macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
        }
-      return;
+      break;
 
     case M_SNE_I:
       if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
        {
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sltu",
-                      "d,v,t", dreg, 0, sreg);
-         return;
+         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 ((char *) NULL, &icnt, &expr1,
-                      HAVE_32BIT_GPRS ? "addiu" : "daddiu",
-                      "t,r,j", dreg, 0, (int) BFD_RELOC_LO16);
-         return;
+         macro_build (&expr1, HAVE_32BIT_GPRS ? "addiu" : "daddiu", "t,r,j",
+                      dreg, 0, BFD_RELOC_LO16);
+         break;
        }
       if (imm_expr.X_op == O_constant
          && imm_expr.X_add_number >= 0
          && imm_expr.X_add_number < 0x10000)
        {
-         macro_build ((char *) NULL, &icnt, &imm_expr, "xori", "t,r,i",
-                      dreg, sreg, (int) BFD_RELOC_LO16);
-         used_at = 0;
+         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 ((char *) NULL, &icnt, &imm_expr,
-                      HAVE_32BIT_GPRS ? "addiu" : "daddiu",
-                      "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16);
-         used_at = 0;
+         macro_build (&imm_expr, HAVE_32BIT_GPRS ? "addiu" : "daddiu",
+                      "t,r,j", dreg, sreg, BFD_RELOC_LO16);
        }
       else
        {
-         load_register (&icnt, AT, &imm_expr, HAVE_64BIT_GPRS);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "xor",
-                      "d,v,t", dreg, sreg, AT);
+         load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+         macro_build (NULL, "xor", "d,v,t", dreg, sreg, AT);
          used_at = 1;
        }
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sltu",
-                  "d,v,t", dreg, 0, dreg);
-      if (used_at)
-       break;
-      return;
+      macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
+      break;
 
     case M_DSUB_I:
       dbl = 1;
@@ -7882,14 +7183,13 @@ macro2 (ip)
          && imm_expr.X_add_number <= 0x8000)
        {
          imm_expr.X_add_number = -imm_expr.X_add_number;
-         macro_build ((char *) NULL, &icnt, &imm_expr,
-                      dbl ? "daddi" : "addi",
-                      "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16);
-         return;
+         macro_build (&imm_expr, dbl ? "daddi" : "addi", "t,r,j",
+                      dreg, sreg, BFD_RELOC_LO16);
+         break;
        }
-      load_register (&icnt, AT, &imm_expr, dbl);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                  dbl ? "dsub" : "sub", "d,v,t", dreg, sreg, AT);
+      used_at = 1;
+      load_register (AT, &imm_expr, dbl);
+      macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", dreg, sreg, AT);
       break;
 
     case M_DSUBU_I:
@@ -7900,14 +7200,13 @@ macro2 (ip)
          && imm_expr.X_add_number <= 0x8000)
        {
          imm_expr.X_add_number = -imm_expr.X_add_number;
-         macro_build ((char *) NULL, &icnt, &imm_expr,
-                      dbl ? "daddiu" : "addiu",
-                      "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16);
-         return;
+         macro_build (&imm_expr, dbl ? "daddiu" : "addiu", "t,r,j",
+                      dreg, sreg, BFD_RELOC_LO16);
+         break;
        }
-      load_register (&icnt, AT, &imm_expr, dbl);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                  dbl ? "dsubu" : "subu", "d,v,t", dreg, sreg, AT);
+      used_at = 1;
+      load_register (AT, &imm_expr, dbl);
+      macro_build (NULL, dbl ? "dsubu" : "subu", "d,v,t", dreg, sreg, AT);
       break;
 
     case M_TEQ_I:
@@ -7928,14 +7227,15 @@ macro2 (ip)
     case M_TNE_I:
       s = "tne";
     trap:
-      load_register (&icnt, AT, &imm_expr, HAVE_64BIT_GPRS);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "s,t", sreg,
-                  AT);
+      used_at = 1;
+      load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+      macro_build (NULL, s, "s,t", sreg, AT);
       break;
 
     case M_TRUNCWS:
     case M_TRUNCWD:
       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 */
 
@@ -7943,29 +7243,21 @@ macro2 (ip)
        * Is the double cfc1 instruction a bug in the mips assembler;
        * or is there a reason for it?
        */
-      mips_emit_delays (TRUE);
-      ++mips_opts.noreorder;
-      mips_any_noreorder = 1;
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "cfc1", "t,G",
-                  treg, RA);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "cfc1", "t,G",
-                  treg, RA);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
+      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 ((char *) NULL, &icnt, &expr1, "ori", "t,r,i", AT, treg,
-                  (int) BFD_RELOC_LO16);
+      macro_build (&expr1, "ori", "t,r,i", AT, treg, BFD_RELOC_LO16);
       expr1.X_add_number = 2;
-      macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", AT, AT,
-                    (int) BFD_RELOC_LO16);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "ctc1", "t,G",
-                  AT, RA);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-             mask == M_TRUNCWD ? "cvt.w.d" : "cvt.w.s", "D,S", dreg, sreg);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "ctc1", "t,G",
-                  treg, RA);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
-      --mips_opts.noreorder;
+      macro_build (&expr1, "xori", "t,r,i", AT, AT, BFD_RELOC_LO16);
+      macro_build (NULL, "ctc1", "t,G", AT, RA);
+      macro_build (NULL, "nop", "");
+      macro_build (NULL, mask == M_TRUNCWD ? "cvt.w.d" : "cvt.w.s", "D,S",
+                  dreg, sreg);
+      macro_build (NULL, "ctc1", "t,G", treg, RA);
+      macro_build (NULL, "nop", "");
+      end_noreorder ();
       break;
 
     case M_ULH:
@@ -7974,22 +7266,19 @@ macro2 (ip)
     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 ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", AT,
-                  (int) BFD_RELOC_LO16, breg);
+      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 ((char *) NULL, &icnt, &offset_expr, "lbu", "t,o(b)", treg,
-                  (int) BFD_RELOC_LO16, breg);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sll", "d,w,<",
-                  AT, AT, 8);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "or", "d,v,t",
-                  treg, treg, AT);
+      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 M_ULD:
@@ -8007,25 +7296,25 @@ macro2 (ip)
       if (treg != breg)
        tempreg = treg;
       else
-       tempreg = AT;
+       {
+         used_at = 1;
+         tempreg = AT;
+       }
       if (! target_big_endian)
        offset_expr.X_add_number += off;
-      macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", tempreg,
-                  (int) BFD_RELOC_LO16, breg);
+      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 ((char *) NULL, &icnt, &offset_expr, s2, "t,o(b)", tempreg,
-                  (int) BFD_RELOC_LO16, breg);
+      macro_build (&offset_expr, s2, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
 
       /* If necessary, move the result in tempreg the final destination.  */
       if (treg == tempreg)
-        return;
+        break;
       /* Protect second load's delay slot.  */
-      if (!gpr_interlocks)
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
-      move_register (&icnt, treg, tempreg);
+      load_delay_nop ();
+      move_register (treg, tempreg);
       break;
 
     case M_ULD_A:
@@ -8039,67 +7328,53 @@ macro2 (ip)
       off = 3;
     ulwa:
       used_at = 1;
-      load_address (&icnt, AT, &offset_expr, &used_at);
+      load_address (AT, &offset_expr, &used_at);
       if (breg != 0)
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                    HAVE_32BIT_ADDRESSES ? HAVE_NEWABI
-                    ? "add" : "addu" : "daddu",
-                    "d,v,t", AT, AT, breg);
+       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 ((char *) NULL, &icnt, &expr1, s, "t,o(b)", treg,
-                  (int) BFD_RELOC_LO16, AT);
+      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 ((char *) NULL, &icnt, &expr1, s2, "t,o(b)", treg,
-                  (int) BFD_RELOC_LO16, AT);
+      macro_build (&expr1, s2, "t,o(b)", treg, BFD_RELOC_LO16, AT);
       break;
 
     case M_ULH_A:
     case M_ULHU_A:
       used_at = 1;
-      load_address (&icnt, AT, &offset_expr, &used_at);
+      load_address (AT, &offset_expr, &used_at);
       if (breg != 0)
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                    HAVE_32BIT_ADDRESSES ? HAVE_NEWABI
-                    ? "add" : "addu" : "daddu",
-                    "d,v,t", AT, AT, breg);
+       macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
       if (target_big_endian)
        expr1.X_add_number = 0;
-      macro_build ((char *) NULL, &icnt, &expr1,
-                  mask == M_ULH_A ? "lb" : "lbu", "t,o(b)", treg,
-                  (int) BFD_RELOC_LO16, AT);
+      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 ((char *) NULL, &icnt, &expr1, "lbu", "t,o(b)", AT,
-                  (int) BFD_RELOC_LO16, AT);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sll", "d,w,<",
-                  treg, treg, 8);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "or", "d,v,t",
-                  treg, treg, AT);
+      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 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 ((char *) NULL, &icnt, &offset_expr, "sb", "t,o(b)", treg,
-                  (int) BFD_RELOC_LO16, breg);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "srl", "d,w,<",
-                  AT, treg, 8);
+      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 ((char *) NULL, &icnt, &offset_expr, "sb", "t,o(b)", AT,
-                  (int) BFD_RELOC_LO16, breg);
+      macro_build (&offset_expr, "sb", "t,o(b)", AT, BFD_RELOC_LO16, breg);
       break;
 
     case M_USD:
@@ -8116,15 +7391,13 @@ macro2 (ip)
        as_bad (_("operand overflow"));
       if (! target_big_endian)
        offset_expr.X_add_number += off;
-      macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
-                  (int) BFD_RELOC_LO16, breg);
+      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 ((char *) NULL, &icnt, &offset_expr, s2, "t,o(b)", treg,
-                  (int) BFD_RELOC_LO16, breg);
-      return;
+      macro_build (&offset_expr, s2, "t,o(b)", treg, BFD_RELOC_LO16, breg);
+      break;
 
     case M_USD_A:
       s = "sdl";
@@ -8137,56 +7410,42 @@ macro2 (ip)
       off = 3;
     uswa:
       used_at = 1;
-      load_address (&icnt, AT, &offset_expr, &used_at);
+      load_address (AT, &offset_expr, &used_at);
       if (breg != 0)
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                    HAVE_32BIT_ADDRESSES ? HAVE_NEWABI
-                    ? "add" : "addu" : "daddu",
-                    "d,v,t", AT, AT, breg);
+       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 ((char *) NULL, &icnt, &expr1, s, "t,o(b)", treg,
-                  (int) BFD_RELOC_LO16, AT);
+      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 ((char *) NULL, &icnt, &expr1, s2, "t,o(b)", treg,
-                  (int) BFD_RELOC_LO16, AT);
+      macro_build (&expr1, s2, "t,o(b)", treg, BFD_RELOC_LO16, AT);
       break;
 
     case M_USH_A:
       used_at = 1;
-      load_address (&icnt, AT, &offset_expr, &used_at);
+      load_address (AT, &offset_expr, &used_at);
       if (breg != 0)
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                    HAVE_32BIT_ADDRESSES ? HAVE_NEWABI
-                    ? "add" : "addu" : "daddu",
-                    "d,v,t", AT, AT, breg);
+       macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
       if (! target_big_endian)
        expr1.X_add_number = 0;
-      macro_build ((char *) NULL, &icnt, &expr1, "sb", "t,o(b)", treg,
-                  (int) BFD_RELOC_LO16, AT);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "srl", "d,w,<",
-                  treg, treg, 8);
+      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 ((char *) NULL, &icnt, &expr1, "sb", "t,o(b)", treg,
-                  (int) BFD_RELOC_LO16, AT);
+      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 ((char *) NULL, &icnt, &expr1, "lbu", "t,o(b)", AT,
-                  (int) BFD_RELOC_LO16, AT);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sll", "d,w,<",
-                  treg, treg, 8);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "or", "d,v,t",
-                  treg, treg, AT);
+      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;
 
     default:
@@ -8195,30 +7454,26 @@ macro2 (ip)
       as_bad (_("Macro %s not implemented yet"), ip->insn_mo->name);
       break;
     }
-  if (mips_opts.noat)
-    as_warn (_("Macro used $at after \".set noat\""));
+  if (mips_opts.noat && used_at)
+    as_bad (_("Macro used $at after \".set noat\""));
 }
 
 /* Implement macros in mips16 mode.  */
 
 static void
-mips16_macro (ip)
-     struct mips_cl_insn *ip;
+mips16_macro (struct mips_cl_insn *ip)
 {
   int mask;
   int xreg, yreg, zreg, tmp;
-  int icnt;
   expressionS expr1;
   int dbl;
   const char *s, *s2, *s3;
 
   mask = ip->insn_mo->mask;
 
-  xreg = (ip->insn_opcode >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX;
-  yreg = (ip->insn_opcode >> MIPS16OP_SH_RY) & MIPS16OP_MASK_RY;
-  zreg = (ip->insn_opcode >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ;
-
-  icnt = 0;
+  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;
@@ -8242,23 +7497,18 @@ mips16_macro (ip)
     case M_REM_3:
       s = "mfhi";
     do_div3:
-      mips_emit_delays (TRUE);
-      ++mips_opts.noreorder;
-      mips_any_noreorder = 1;
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                  dbl ? "ddiv" : "div",
-                  "0,x,y", xreg, yreg);
+      start_noreorder ();
+      macro_build (NULL, dbl ? "ddiv" : "div", "0,x,y", xreg, yreg);
       expr1.X_add_number = 2;
-      macro_build ((char *) NULL, &icnt, &expr1, "bnez", "x,p", yreg);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "break", "6",
-                  7);
+      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.  */
-      --mips_opts.noreorder;
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "x", zreg);
+      end_noreorder ();
+      macro_build (NULL, s, "x", zreg);
       break;
 
     case M_DIVU_3:
@@ -8277,27 +7527,21 @@ mips16_macro (ip)
       s = "ddivu";
       s2 = "mfhi";
     do_divu3:
-      mips_emit_delays (TRUE);
-      ++mips_opts.noreorder;
-      mips_any_noreorder = 1;
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "0,x,y",
-                  xreg, yreg);
+      start_noreorder ();
+      macro_build (NULL, s, "0,x,y", xreg, yreg);
       expr1.X_add_number = 2;
-      macro_build ((char *) NULL, &icnt, &expr1, "bnez", "x,p", yreg);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "break",
-                  "6", 7);
-      --mips_opts.noreorder;
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s2, "x", zreg);
+      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 ((char *) NULL, &icnt, (expressionS *) NULL,
-                  dbl ? "dmultu" : "multu", "x,y", xreg, yreg);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "mflo", "x",
-                  zreg);
-      return;
+      macro_build (NULL, dbl ? "dmultu" : "multu", "x,y", xreg, yreg);
+      macro_build (NULL, "mflo", "x", zreg);
+      break;
 
     case M_DSUBU_I:
       dbl = 1;
@@ -8307,24 +7551,21 @@ mips16_macro (ip)
       if (imm_expr.X_op != O_constant)
        as_bad (_("Unsupported large constant"));
       imm_expr.X_add_number = -imm_expr.X_add_number;
-      macro_build ((char *) NULL, &icnt, &imm_expr,
-                  dbl ? "daddiu" : "addiu", "y,x,4", yreg, xreg);
+      macro_build (&imm_expr, dbl ? "daddiu" : "addiu", "y,x,4", yreg, xreg);
       break;
 
     case M_SUBU_I_2:
       if (imm_expr.X_op != O_constant)
        as_bad (_("Unsupported large constant"));
       imm_expr.X_add_number = -imm_expr.X_add_number;
-      macro_build ((char *) NULL, &icnt, &imm_expr, "addiu",
-                  "x,k", xreg);
+      macro_build (&imm_expr, "addiu", "x,k", xreg);
       break;
 
     case M_DSUBU_I_2:
       if (imm_expr.X_op != O_constant)
        as_bad (_("Unsupported large constant"));
       imm_expr.X_add_number = -imm_expr.X_add_number;
-      macro_build ((char *) NULL, &icnt, &imm_expr, "daddiu",
-                  "y,j", yreg);
+      macro_build (&imm_expr, "daddiu", "y,j", yreg);
       break;
 
     case M_BEQ:
@@ -8373,9 +7614,8 @@ mips16_macro (ip)
       yreg = tmp;
 
     do_branch:
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "x,y",
-                  xreg, yreg);
-      macro_build ((char *) NULL, &icnt, &offset_expr, s2, "p");
+      macro_build (NULL, s, "x,y", xreg, yreg);
+      macro_build (&offset_expr, s2, "p");
       break;
 
     case M_BEQ_I:
@@ -8434,19 +7674,18 @@ mips16_macro (ip)
       ++imm_expr.X_add_number;
 
     do_branch_i:
-      macro_build ((char *) NULL, &icnt, &imm_expr, s, s3, xreg);
-      macro_build ((char *) NULL, &icnt, &offset_expr, s2, "p");
+      macro_build (&imm_expr, s, s3, xreg);
+      macro_build (&offset_expr, s2, "p");
       break;
 
     case M_ABS:
       expr1.X_add_number = 0;
-      macro_build ((char *) NULL, &icnt, &expr1, "slti", "x,8", yreg);
+      macro_build (&expr1, "slti", "x,8", yreg);
       if (xreg != yreg)
-       move_register (&icnt, xreg, yreg);
+       move_register (xreg, yreg);
       expr1.X_add_number = 2;
-      macro_build ((char *) NULL, &icnt, &expr1, "bteqz", "p");
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                  "neg", "x,w", xreg, xreg);
+      macro_build (&expr1, "bteqz", "p");
+      macro_build (NULL, "neg", "x,w", xreg, xreg);
     }
 }
 
@@ -8454,8 +7693,7 @@ mips16_macro (ip)
    by the match/mask part of the instruction definition, or by the
    operand list.  */
 static int
-validate_mips_insn (opc)
-     const struct mips_opcode *opc;
+validate_mips_insn (const struct mips_opcode *opc)
 {
   const char *p = opc->args;
   char c;
@@ -8482,6 +7720,11 @@ validate_mips_insn (opc)
          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;
          default:
            as_bad (_("internal: bad mips opcode (unknown extension operand type `+%c'): %s %s"),
                    c, opc->name, opc->args);
@@ -8564,9 +7807,7 @@ validate_mips_insn (opc)
    is an address expression.  */
 
 static void
-mips_ip (str, ip)
-     char *str;
-     struct mips_cl_insn *ip;
+mips_ip (char *str, struct mips_cl_insn *ip)
 {
   char *s;
   const char *args;
@@ -8642,14 +7883,14 @@ mips_ip (str, ip)
                             | (file_ase_mips16 ? INSN_MIPS16 : 0)
                             | (mips_opts.ase_mdmx ? INSN_MDMX : 0)
                             | (mips_opts.ase_mips3d ? INSN_MIPS3D : 0)),
-                           mips_arch))
+                           mips_opts.arch))
        ok = TRUE;
       else
        ok = FALSE;
 
       if (insn->pinfo != INSN_MACRO)
        {
-         if (mips_arch == CPU_R4650 && (insn->pinfo & FP_D) != 0)
+         if (mips_opts.arch == CPU_R4650 && (insn->pinfo & FP_D) != 0)
            ok = FALSE;
        }
 
@@ -8666,15 +7907,10 @@ mips_ip (str, ip)
              if (!insn_error)
                {
                  static char buf[100];
-                 if (mips_arch_info->is_isa)
-                   sprintf (buf,
-                            _("opcode not supported at this ISA level (%s)"),
-                            mips_cpu_info_from_isa (mips_opts.isa)->name);
-                 else
-                   sprintf (buf,
-                            _("opcode not supported on this processor: %s (%s)"),
-                            mips_arch_info->name,
-                            mips_cpu_info_from_isa (mips_opts.isa)->name);
+                 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;
                }
              if (save_c)
@@ -8683,8 +7919,7 @@ mips_ip (str, ip)
            }
        }
 
-      ip->insn_mo = insn;
-      ip->insn_opcode = insn->match;
+      create_insn (ip, insn);
       insn_error = NULL;
       for (args = insn->args;; ++args)
        {
@@ -8707,19 +7942,19 @@ mips_ip (str, ip)
                {
                case 'r':
                case 'v':
-                 ip->insn_opcode |= lastregno << OP_SH_RS;
+                 INSERT_OPERAND (RS, *ip, lastregno);
                  continue;
 
                case 'w':
-                 ip->insn_opcode |= lastregno << OP_SH_RT;
+                 INSERT_OPERAND (RT, *ip, lastregno);
                  continue;
 
                case 'W':
-                 ip->insn_opcode |= lastregno << OP_SH_FT;
+                 INSERT_OPERAND (FT, *ip, lastregno);
                  continue;
 
                case 'V':
-                 ip->insn_opcode |= lastregno << OP_SH_FS;
+                 INSERT_OPERAND (FS, *ip, lastregno);
                  continue;
                }
              break;
@@ -8748,6 +7983,12 @@ mips_ip (str, ip)
                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
@@ -8758,8 +7999,7 @@ mips_ip (str, ip)
                      imm_expr.X_add_number = limlo;
                    }
                  lastpos = imm_expr.X_add_number;
-                 ip->insn_opcode |= (imm_expr.X_add_number
-                                     & OP_MASK_SHAMT) << OP_SH_SHAMT;
+                 INSERT_OPERAND (SHAMT, *ip, imm_expr.X_add_number);
                  imm_expr.X_op = O_absent;
                  s = expr_end;
                  continue;
@@ -8767,6 +8007,12 @@ mips_ip (str, ip)
                case 'B':               /* ins size, becomes MSB.  */
                  limlo = 1;
                  limhi = 32;
+                 goto do_msb;
+               case 'F':
+                 limlo = 33;
+                 limhi = 64;
+                 goto do_msb;
+do_msb:
                  my_getExpression (&imm_expr, s);
                  check_absolute_expr (ip, &imm_expr);
                  /* Check for negative input so that small negative numbers
@@ -8784,8 +8030,8 @@ mips_ip (str, ip)
                              (unsigned long) lastpos);
                      imm_expr.X_add_number = limlo - lastpos;
                    }
-                 ip->insn_opcode |= ((lastpos + imm_expr.X_add_number - 1)
-                                     & OP_MASK_INSMSB) << OP_SH_INSMSB;
+                 INSERT_OPERAND (INSMSB, *ip,
+                                lastpos + imm_expr.X_add_number - 1);
                  imm_expr.X_op = O_absent;
                  s = expr_end;
                  continue;
@@ -8793,6 +8039,16 @@ mips_ip (str, ip)
                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
@@ -8810,8 +8066,7 @@ mips_ip (str, ip)
                              (unsigned long) lastpos);
                      imm_expr.X_add_number = limlo - lastpos;
                    }
-                 ip->insn_opcode |= ((imm_expr.X_add_number - 1)
-                                     & OP_MASK_EXTMSBD) << OP_SH_EXTMSBD;
+                 INSERT_OPERAND (EXTMSBD, *ip, imm_expr.X_add_number - 1);
                  imm_expr.X_op = O_absent;
                  s = expr_end;
                  continue;
@@ -8820,6 +8075,16 @@ mips_ip (str, ip)
                  /* +D is for disassembly only; never match.  */
                  break;
 
+               case 'I':
+                 /* "+I" is like "I", except that imm2_expr is used.  */
+                 my_getExpression (&imm2_expr, s);
+                 if (imm2_expr.X_op != O_big
+                     && imm2_expr.X_op != O_constant)
+                 insn_error = _("absolute expression required");
+                 normalize_constant_expr (&imm2_expr);
+                 s = expr_end;
+                 continue;
+
                default:
                  as_bad (_("internal: bad mips opcode (unknown extension operand type `+%c'): %s %s"),
                    *args, insn->name, insn->args);
@@ -8838,12 +8103,9 @@ mips_ip (str, ip)
              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);
-                 imm_expr.X_add_number &= OP_MASK_SHAMT;
-               }
-             ip->insn_opcode |= imm_expr.X_add_number << OP_SH_SHAMT;
+               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;
@@ -8854,7 +8116,7 @@ mips_ip (str, ip)
              if ((unsigned long) imm_expr.X_add_number < 32
                  || (unsigned long) imm_expr.X_add_number > 63)
                break;
-             ip->insn_opcode |= (imm_expr.X_add_number - 32) << OP_SH_SHAMT;
+             INSERT_OPERAND (SHAMT, *ip, imm_expr.X_add_number - 32);
              imm_expr.X_op = O_absent;
              s = expr_end;
              continue;
@@ -8864,16 +8126,13 @@ mips_ip (str, ip)
              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);
-                 imm_expr.X_add_number &= 0x1f;
-               }
+               as_warn (_("Invalid value for `%s' (%lu)"),
+                        ip->insn_mo->name,
+                        (unsigned long) imm_expr.X_add_number);
              if (*args == 'k')
-               ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CACHE;
+               INSERT_OPERAND (CACHE, *ip, imm_expr.X_add_number);
              else
-               ip->insn_opcode |= imm_expr.X_add_number << OP_SH_PREFX;
+               INSERT_OPERAND (PREFX, *ip, imm_expr.X_add_number);
              imm_expr.X_op = O_absent;
              s = expr_end;
              continue;
@@ -8882,12 +8141,9 @@ mips_ip (str, ip)
              my_getExpression (&imm_expr, s);
              check_absolute_expr (ip, &imm_expr);
              if ((unsigned long) imm_expr.X_add_number > 1023)
-               {
-                 as_warn (_("Illegal break code (%lu)"),
-                          (unsigned long) imm_expr.X_add_number);
-                 imm_expr.X_add_number &= OP_MASK_CODE;
-               }
-             ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CODE;
+               as_warn (_("Illegal break code (%lu)"),
+                        (unsigned long) imm_expr.X_add_number);
+             INSERT_OPERAND (CODE, *ip, imm_expr.X_add_number);
              imm_expr.X_op = O_absent;
              s = expr_end;
              continue;
@@ -8896,12 +8152,9 @@ mips_ip (str, ip)
              my_getExpression (&imm_expr, s);
              check_absolute_expr (ip, &imm_expr);
              if ((unsigned long) imm_expr.X_add_number > 1023)
-               {
-                 as_warn (_("Illegal lower break code (%lu)"),
-                          (unsigned long) imm_expr.X_add_number);
-                 imm_expr.X_add_number &= OP_MASK_CODE2;
-               }
-             ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CODE2;
+               as_warn (_("Illegal lower break code (%lu)"),
+                        (unsigned long) imm_expr.X_add_number);
+             INSERT_OPERAND (CODE2, *ip, imm_expr.X_add_number);
              imm_expr.X_op = O_absent;
              s = expr_end;
              continue;
@@ -8912,7 +8165,7 @@ mips_ip (str, ip)
              if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE20)
                as_warn (_("Illegal 20-bit code (%lu)"),
                         (unsigned long) imm_expr.X_add_number);
-             ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CODE20;
+             INSERT_OPERAND (CODE20, *ip, imm_expr.X_add_number);
              imm_expr.X_op = O_absent;
              s = expr_end;
              continue;
@@ -8937,7 +8190,7 @@ mips_ip (str, ip)
              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);
-             ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CODE19;
+             INSERT_OPERAND (CODE19, *ip, imm_expr.X_add_number);
              imm_expr.X_op = O_absent;
              s = expr_end;
              continue;
@@ -8946,12 +8199,9 @@ mips_ip (str, ip)
              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);
-                 imm_expr.X_add_number &= OP_MASK_PERFREG;
-               }
-             ip->insn_opcode |= (imm_expr.X_add_number << OP_SH_PERFREG);
+               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;
@@ -9088,21 +8338,21 @@ mips_ip (str, ip)
                    case 's':
                    case 'v':
                    case 'b':
-                     ip->insn_opcode |= regno << OP_SH_RS;
+                     INSERT_OPERAND (RS, *ip, regno);
                      break;
                    case 'd':
                    case 'G':
                    case 'K':
-                     ip->insn_opcode |= regno << OP_SH_RD;
+                     INSERT_OPERAND (RD, *ip, regno);
                      break;
                    case 'U':
-                     ip->insn_opcode |= regno << OP_SH_RD;
-                     ip->insn_opcode |= regno << OP_SH_RT;
+                     INSERT_OPERAND (RD, *ip, regno);
+                     INSERT_OPERAND (RT, *ip, regno);
                      break;
                    case 'w':
                    case 't':
                    case 'E':
-                     ip->insn_opcode |= regno << OP_SH_RT;
+                     INSERT_OPERAND (RT, *ip, regno);
                      break;
                    case 'x':
                      /* This case exists because on the r3000 trunc
@@ -9133,10 +8383,10 @@ mips_ip (str, ip)
                {
                case 'r':
                case 'v':
-                 ip->insn_opcode |= lastregno << OP_SH_RS;
+                 INSERT_OPERAND (RS, *ip, lastregno);
                  continue;
                case 'w':
-                 ip->insn_opcode |= lastregno << OP_SH_RT;
+                 INSERT_OPERAND (RT, *ip, lastregno);
                  continue;
                }
              break;
@@ -9145,12 +8395,9 @@ mips_ip (str, ip)
              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);
-                 imm_expr.X_add_number &= OP_MASK_ALN;
-               }
-             ip->insn_opcode |= imm_expr.X_add_number << OP_SH_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;
@@ -9162,17 +8409,13 @@ mips_ip (str, ip)
                  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);
-                     imm_expr.X_add_number &= OP_MASK_FT;
-                   }
-                 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;
-                 ip->insn_opcode |= imm_expr.X_add_number << OP_SH_FT;
                  imm_expr.X_op = O_absent;
                  s = expr_end;
                  continue;
@@ -9235,12 +8478,12 @@ mips_ip (str, ip)
                    {
                    case 'D':
                    case 'X':
-                     ip->insn_opcode |= regno << OP_SH_FD;
+                     INSERT_OPERAND (FD, *ip, regno);
                      break;
                    case 'V':
                    case 'S':
                    case 'Y':
-                     ip->insn_opcode |= regno << OP_SH_FS;
+                     INSERT_OPERAND (FS, *ip, regno);
                      break;
                    case 'Q':
                      /* This is like 'Z', but also needs to fix the MDMX
@@ -9261,6 +8504,7 @@ mips_ip (str, ip)
                          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
@@ -9279,10 +8523,10 @@ mips_ip (str, ip)
                    case 'W':
                    case 'T':
                    case 'Z':
-                     ip->insn_opcode |= regno << OP_SH_FT;
+                     INSERT_OPERAND (FT, *ip, regno);
                      break;
                    case 'R':
-                     ip->insn_opcode |= regno << OP_SH_FR;
+                     INSERT_OPERAND (FR, *ip, regno);
                      break;
                    }
                  lastregno = regno;
@@ -9292,10 +8536,10 @@ mips_ip (str, ip)
              switch (*args++)
                {
                case 'V':
-                 ip->insn_opcode |= lastregno << OP_SH_FS;
+                 INSERT_OPERAND (FS, *ip, lastregno);
                  continue;
                case 'W':
-                 ip->insn_opcode |= lastregno << OP_SH_FT;
+                 INSERT_OPERAND (FT, *ip, lastregno);
                  continue;
                }
              break;
@@ -9305,6 +8549,7 @@ mips_ip (str, ip)
              if (imm_expr.X_op != O_big
                  && imm_expr.X_op != O_constant)
                insn_error = _("absolute expression required");
+             normalize_constant_expr (&imm_expr);
              s = expr_end;
              continue;
 
@@ -9349,13 +8594,6 @@ mips_ip (str, ip)
                    The .lit4 and .lit8 sections are only used if
                    permitted by the -G argument.
 
-                   When generating embedded PIC code, we use the
-                   .lit8 section but not the .lit4 section (we can do
-                   .lit4 inline easily; we need to put .lit8
-                   somewhere in the data segment, and using .lit8
-                   permits the linker to eventually combine identical
-                   .lit8 entries).
-
                    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
@@ -9381,9 +8619,7 @@ mips_ip (str, ip)
 
                if (*args == 'f'
                    || (*args == 'l'
-                       && (! USE_GLOBAL_POINTER_OPT
-                           || mips_pic == EMBEDDED_PIC
-                           || g_switch_value < 4
+                       && (g_switch_value < 4
                            || (temp[0] == 0 && temp[1] == 0)
                            || (temp[2] == 0 && temp[3] == 0))))
                  {
@@ -9470,19 +8706,14 @@ mips_ip (str, ip)
                      default: /* unused default case avoids warnings.  */
                      case 'L':
                        newname = RDATA_SECTION_NAME;
-                       if ((USE_GLOBAL_POINTER_OPT && g_switch_value >= 8)
-                           || mips_pic == EMBEDDED_PIC)
+                       if (g_switch_value >= 8)
                          newname = ".lit8";
                        break;
                      case 'F':
-                       if (mips_pic == EMBEDDED_PIC)
-                         newname = ".lit8";
-                       else
-                         newname = RDATA_SECTION_NAME;
+                       newname = RDATA_SECTION_NAME;
                        break;
                      case 'l':
-                       assert (!USE_GLOBAL_POINTER_OPT
-                               || g_switch_value >= 4);
+                       assert (g_switch_value >= 4);
                        newname = ".lit4";
                        break;
                      }
@@ -9625,11 +8856,22 @@ mips_ip (str, ip)
                }
              while (ISDIGIT (*s));
              if (regno > 7)
-               as_bad (_("invalid condition code register $fcc%d"), regno);
+               as_bad (_("Invalid condition code register $fcc%d"), regno);
+             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')
-               ip->insn_opcode |= regno << OP_SH_BCC;
+               INSERT_OPERAND (BCC, *ip, regno);
              else
-               ip->insn_opcode |= regno << OP_SH_CCC;
+               INSERT_OPERAND (CCC, *ip, regno);
              continue;
 
            case 'H':
@@ -9667,7 +8909,7 @@ mips_ip (str, ip)
                  imm_expr.X_add_number = 0;
                }
 
-             ip->insn_opcode |= imm_expr.X_add_number << OP_SH_VECBYTE;
+             INSERT_OPERAND (VECBYTE, *ip, imm_expr.X_add_number);
              imm_expr.X_op = O_absent;
              s = expr_end;
              continue;
@@ -9684,7 +8926,7 @@ mips_ip (str, ip)
                  imm_expr.X_add_number = 0;
                }
 
-             ip->insn_opcode |= imm_expr.X_add_number << OP_SH_VECALIGN;
+             INSERT_OPERAND (VECALIGN, *ip, imm_expr.X_add_number);
              imm_expr.X_op = O_absent;
              s = expr_end;
              continue;
@@ -9719,9 +8961,7 @@ mips_ip (str, ip)
    requested a small or extended instruction.  */
 
 static void
-mips16_ip (str, ip)
-     char *str;
-     struct mips_cl_insn *ip;
+mips16_ip (char *str, struct mips_cl_insn *ip)
 {
   char *s;
   const char *args;
@@ -9730,6 +8970,7 @@ mips16_ip (str, ip)
   unsigned int regno;
   unsigned int lastregno = 0;
   char *s_reset;
+  size_t i;
 
   insn_error = NULL;
 
@@ -9782,13 +9023,12 @@ mips16_ip (str, ip)
     {
       assert (strcmp (insn->name, str) == 0);
 
-      ip->insn_mo = insn;
-      ip->insn_opcode = insn->match;
-      ip->use_extend = FALSE;
+      create_insn (ip, insn);
       imm_expr.X_op = O_absent;
       imm_reloc[0] = BFD_RELOC_UNUSED;
       imm_reloc[1] = BFD_RELOC_UNUSED;
       imm_reloc[2] = BFD_RELOC_UNUSED;
+      imm2_expr.X_op = O_absent;
       offset_expr.X_op = O_absent;
       offset_reloc[0] = BFD_RELOC_UNUSED;
       offset_reloc[1] = BFD_RELOC_UNUSED;
@@ -9815,8 +9055,34 @@ mips16_ip (str, ip)
                      && *imm_reloc > BFD_RELOC_UNUSED
                      && insn->pinfo != INSN_MACRO)
                    {
+                     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,
-                                   imm_expr.X_add_number, TRUE, mips16_small,
+                                   tmp, TRUE, mips16_small,
                                    mips16_ext, &ip->insn_opcode,
                                    &ip->use_extend, &ip->extend);
                      imm_expr.X_op = O_absent;
@@ -9834,10 +9100,10 @@ mips16_ip (str, ip)
              switch (*++args)
                {
                case 'v':
-                 ip->insn_opcode |= lastregno << MIPS16OP_SH_RX;
+                 MIPS16_INSERT_OPERAND (RX, *ip, lastregno);
                  continue;
                case 'w':
-                 ip->insn_opcode |= lastregno << MIPS16OP_SH_RY;
+                 MIPS16_INSERT_OPERAND (RY, *ip, lastregno);
                  continue;
                }
              break;
@@ -9853,9 +9119,9 @@ mips16_ip (str, ip)
              if (s[0] != '$')
                {
                  if (c == 'v')
-                   ip->insn_opcode |= lastregno << MIPS16OP_SH_RX;
+                   MIPS16_INSERT_OPERAND (RX, *ip, lastregno);
                  else
-                   ip->insn_opcode |= lastregno << MIPS16OP_SH_RY;
+                   MIPS16_INSERT_OPERAND (RY, *ip, lastregno);
                  ++args;
                  continue;
                }
@@ -9990,27 +9256,27 @@ mips16_ip (str, ip)
                {
                case 'x':
                case 'v':
-                 ip->insn_opcode |= regno << MIPS16OP_SH_RX;
+                 MIPS16_INSERT_OPERAND (RX, *ip, regno);
                  break;
                case 'y':
                case 'w':
-                 ip->insn_opcode |= regno << MIPS16OP_SH_RY;
+                 MIPS16_INSERT_OPERAND (RY, *ip, regno);
                  break;
                case 'z':
-                 ip->insn_opcode |= regno << MIPS16OP_SH_RZ;
+                 MIPS16_INSERT_OPERAND (RZ, *ip, regno);
                  break;
                case 'Z':
-                 ip->insn_opcode |= regno << MIPS16OP_SH_MOVE32Z;
+                 MIPS16_INSERT_OPERAND (MOVE32Z, *ip, regno);
                case '0':
                case 'S':
                case 'R':
                  break;
                case 'X':
-                 ip->insn_opcode |= regno << MIPS16OP_SH_REGR32;
+                 MIPS16_INSERT_OPERAND (REGR32, *ip, regno);
                  break;
                case 'Y':
                  regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3);
-                 ip->insn_opcode |= regno << MIPS16OP_SH_REG32R;
+                 MIPS16_INSERT_OPERAND (REG32R, *ip, regno);
                  break;
                default:
                  internalError ();
@@ -10027,47 +9293,43 @@ mips16_ip (str, ip)
                }
              break;
 
-           case '<':
-           case '>':
-           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':
-             if (s[0] == '%'
-                 && strncmp (s + 1, "gprel(", sizeof "gprel(" - 1) == 0)
+             i = my_getSmallExpression (&imm_expr, imm_reloc, s);
+             if (i > 0)
                {
-                 /* This is %gprel(SYMBOL).  We need to read SYMBOL,
-                     and generate the appropriate reloc.  If the text
-                     inside %gprel is not a symbol name with an
-                     optional offset, then we generate a normal reloc
-                     and will probably fail later.  */
-                 my_getExpression (&imm_expr, s + sizeof "%gprel" - 1);
-                 if (imm_expr.X_op == O_symbol)
+                 if (imm_expr.X_op != O_constant)
                    {
                      mips16_ext = TRUE;
-                     *imm_reloc = BFD_RELOC_MIPS16_GPREL;
-                     s = expr_end;
                      ip->use_extend = TRUE;
                      ip->extend = 0;
-                     continue;
                    }
+                 else
+                   {
+                     /* We need to relax this instruction.  */
+                     *offset_reloc = *imm_reloc;
+                     *imm_reloc = (int) BFD_RELOC_UNUSED + c;
+                   }
+                 s = expr_end;
+                 continue;
                }
-             else
-               {
-                 /* Just pick up a normal expression.  */
-                 my_getExpression (&imm_expr, s);
-               }
-
+             *imm_reloc = BFD_RELOC_UNUSED;
+             /* Fall through.  */
+           case '<':
+           case '>':
+           case '[':
+           case ']':
+           case '4':
+           case '8':
+             my_getExpression (&imm_expr, s);
              if (imm_expr.X_op == O_register)
                {
                  /* What we thought was an expression turned out to
@@ -10116,13 +9378,10 @@ mips16_ip (str, ip)
              my_getExpression (&imm_expr, s);
              check_absolute_expr (ip, &imm_expr);
              if ((unsigned long) imm_expr.X_add_number > 63)
-               {
-                 as_warn (_("Invalid value for `%s' (%lu)"),
-                          ip->insn_mo->name,
-                          (unsigned long) imm_expr.X_add_number);
-                 imm_expr.X_add_number &= 0x3f;
-               }
-             ip->insn_opcode |= imm_expr.X_add_number << MIPS16OP_SH_IMM6;
+               as_warn (_("Invalid value for `%s' (%lu)"),
+                        ip->insn_mo->name,
+                        (unsigned long) imm_expr.X_add_number);
+             MIPS16_INSERT_OPERAND (IMM6, *ip, imm_expr.X_add_number);
              imm_expr.X_op = O_absent;
              s = expr_end;
              continue;
@@ -10327,18 +9586,10 @@ static const struct mips16_immed_operand mips16_immed_operands[] =
    WARN is true, warn if EXT does not match reality.  */
 
 static void
-mips16_immed (file, line, type, val, warn, small, ext, insn, use_extend,
-             extend)
-     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, offsetT val,
+             bfd_boolean warn, bfd_boolean small, bfd_boolean ext,
+             unsigned long *insn, bfd_boolean *use_extend,
+             unsigned short *extend)
 {
   register const struct mips16_immed_operand *op;
   int mintiny, maxtiny;
@@ -10437,11 +9688,13 @@ mips16_immed (file, line, type, val, warn, small, ext, insn, use_extend,
     }
 }
 \f
-static const struct percent_op_match
+struct percent_op_match
 {
   const char *str;
   bfd_reloc_code_real_type reloc;
-} percent_op[] =
+};
+
+static const struct percent_op_match mips_percent_op[] =
 {
   {"%lo", BFD_RELOC_LO16},
 #ifdef OBJ_ELF
@@ -10459,25 +9712,54 @@ static const struct percent_op_match
   {"%highest", BFD_RELOC_MIPS_HIGHEST},
   {"%higher", BFD_RELOC_MIPS_HIGHER},
   {"%neg", BFD_RELOC_MIPS_SUB},
+  {"%tlsgd", BFD_RELOC_MIPS_TLS_GD},
+  {"%tlsldm", BFD_RELOC_MIPS_TLS_LDM},
+  {"%dtprel_hi", BFD_RELOC_MIPS_TLS_DTPREL_HI16},
+  {"%dtprel_lo", BFD_RELOC_MIPS_TLS_DTPREL_LO16},
+  {"%tprel_hi", BFD_RELOC_MIPS_TLS_TPREL_HI16},
+  {"%tprel_lo", BFD_RELOC_MIPS_TLS_TPREL_LO16},
+  {"%gottprel", BFD_RELOC_MIPS_TLS_GOTTPREL},
 #endif
   {"%hi", BFD_RELOC_HI16_S}
 };
 
+static const struct percent_op_match mips16_percent_op[] =
+{
+  {"%lo", BFD_RELOC_MIPS16_LO16},
+  {"%gprel", BFD_RELOC_MIPS16_GPREL},
+  {"%hi", BFD_RELOC_MIPS16_HI16_S}
+};
+
 
 /* Return true if *STR points to a relocation operator.  When returning true,
    move *STR over the operator and store its relocation code in *RELOC.
    Leave both *STR and *RELOC alone when returning false.  */
 
 static bfd_boolean
-parse_relocation (str, reloc)
-     char **str;
-     bfd_reloc_code_real_type *reloc;
+parse_relocation (char **str, bfd_reloc_code_real_type *reloc)
 {
-  size_t i;
+  const struct percent_op_match *percent_op;
+  size_t limit, i;
+
+  if (mips_opts.mips16)
+    {
+      percent_op = mips16_percent_op;
+      limit = ARRAY_SIZE (mips16_percent_op);
+    }
+  else
+    {
+      percent_op = mips_percent_op;
+      limit = ARRAY_SIZE (mips_percent_op);
+    }
 
-  for (i = 0; i < ARRAY_SIZE (percent_op); i++)
+  for (i = 0; i < limit; i++)
     if (strncasecmp (*str, percent_op[i].str, strlen (percent_op[i].str)) == 0)
       {
+       int len = strlen (percent_op[i].str);
+
+       if (!ISSPACE ((*str)[len]) && (*str)[len] != '(')
+         continue;
+
        *str += strlen (percent_op[i].str);
        *reloc = percent_op[i].reloc;
 
@@ -10487,7 +9769,7 @@ parse_relocation (str, reloc)
          {
            as_bad ("relocation %s isn't supported by the current ABI",
                    percent_op[i].str);
-           *reloc = BFD_RELOC_LO16;
+           *reloc = BFD_RELOC_UNUSED;
          }
        return TRUE;
       }
@@ -10499,14 +9781,11 @@ parse_relocation (str, reloc)
    expression in *EP and the relocations in the array starting
    at RELOC.  Return the number of relocation operators used.
 
-   On exit, EXPR_END points to the first character after the expression.
-   If no relocation operators are used, RELOC[0] is set to BFD_RELOC_LO16.  */
+   On exit, EXPR_END points to the first character after the expression.  */
 
 static size_t
-my_getSmallExpression (ep, reloc, str)
-     expressionS *ep;
-     bfd_reloc_code_real_type *reloc;
-     char *str;
+my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc,
+                      char *str)
 {
   bfd_reloc_code_real_type reversed_reloc[3];
   size_t reloc_index, i;
@@ -10548,9 +9827,7 @@ my_getSmallExpression (ep, reloc, str)
 
   expr_end = str;
 
-  if (reloc_index == 0)
-    reloc[0] = BFD_RELOC_LO16;
-  else
+  if (reloc_index != 0)
     {
       prev_reloc_op_frag = frag_now;
       for (i = 0; i < reloc_index; i++)
@@ -10561,9 +9838,7 @@ my_getSmallExpression (ep, reloc, str)
 }
 
 static void
-my_getExpression (ep, str)
-     expressionS *ep;
-     char *str;
+my_getExpression (expressionS *ep, char *str)
 {
   char *save_in;
   valueT val;
@@ -10594,10 +9869,7 @@ my_getExpression (ep, str)
    returned, or NULL on OK.  */
 
 char *
-md_atof (type, litP, sizeP)
-     int type;
-     char *litP;
-     int *sizeP;
+md_atof (int type, char *litP, int *sizeP)
 {
   int prec;
   LITTLENUM_TYPE words[4];
@@ -10629,7 +9901,7 @@ md_atof (type, litP, sizeP)
     {
       for (i = prec - 1; i >= 0; i--)
        {
-         md_number_to_chars (litP, (valueT) words[i], 2);
+         md_number_to_chars (litP, words[i], 2);
          litP += 2;
        }
     }
@@ -10637,7 +9909,7 @@ md_atof (type, litP, sizeP)
     {
       for (i = 0; i < prec; i++)
        {
-         md_number_to_chars (litP, (valueT) words[i], 2);
+         md_number_to_chars (litP, words[i], 2);
          litP += 2;
        }
     }
@@ -10646,10 +9918,7 @@ md_atof (type, litP, sizeP)
 }
 
 void
-md_number_to_chars (buf, val, n)
-     char *buf;
-     valueT val;
-     int n;
+md_number_to_chars (char *buf, valueT val, int n)
 {
   if (target_big_endian)
     number_to_chars_bigendian (buf, val, n);
@@ -10680,7 +9949,7 @@ static int support_64bit_objects(void)
 }
 #endif /* OBJ_ELF */
 
-const char *md_shortopts = "nO::g::G:";
+const char *md_shortopts = "O::g::G:";
 
 struct option md_longopts[] =
 {
@@ -10707,9 +9976,11 @@ struct option md_longopts[] =
   {"mips64", no_argument, NULL, OPTION_MIPS64},
 #define OPTION_MIPS32R2 (OPTION_ARCH_BASE + 9)
   {"mips32r2", no_argument, NULL, OPTION_MIPS32R2},
+#define OPTION_MIPS64R2 (OPTION_ARCH_BASE + 10)
+  {"mips64r2", no_argument, NULL, OPTION_MIPS64R2},
 
   /* Options which specify Application Specific Extensions (ASEs).  */
-#define OPTION_ASE_BASE (OPTION_ARCH_BASE + 10)
+#define OPTION_ASE_BASE (OPTION_ARCH_BASE + 11)
 #define OPTION_MIPS16 (OPTION_ASE_BASE + 0)
   {"mips16", no_argument, NULL, OPTION_MIPS16},
 #define OPTION_NO_MIPS16 (OPTION_ASE_BASE + 1)
@@ -10749,45 +10020,55 @@ struct option md_longopts[] =
 #define OPTION_MNO_7000_HILO_FIX (OPTION_FIX_BASE + 1)
   {"no-fix-7000", no_argument, NULL, OPTION_MNO_7000_HILO_FIX},
   {"mno-fix7000", no_argument, NULL, OPTION_MNO_7000_HILO_FIX},
-#define OPTION_FIX_VR4122 (OPTION_FIX_BASE + 2)
-#define OPTION_NO_FIX_VR4122 (OPTION_FIX_BASE + 3)
-  {"mfix-vr4122-bugs",    no_argument, NULL, OPTION_FIX_VR4122},
-  {"no-mfix-vr4122-bugs", no_argument, NULL, OPTION_NO_FIX_VR4122},
+#define OPTION_FIX_VR4120 (OPTION_FIX_BASE + 2)
+#define OPTION_NO_FIX_VR4120 (OPTION_FIX_BASE + 3)
+  {"mfix-vr4120",    no_argument, NULL, OPTION_FIX_VR4120},
+  {"mno-fix-vr4120", no_argument, NULL, OPTION_NO_FIX_VR4120},
+#define OPTION_FIX_VR4130 (OPTION_FIX_BASE + 4)
+#define OPTION_NO_FIX_VR4130 (OPTION_FIX_BASE + 5)
+  {"mfix-vr4130",    no_argument, NULL, OPTION_FIX_VR4130},
+  {"mno-fix-vr4130", no_argument, NULL, OPTION_NO_FIX_VR4130},
 
   /* Miscellaneous options.  */
-#define OPTION_MISC_BASE (OPTION_FIX_BASE + 4)
-#define OPTION_MEMBEDDED_PIC (OPTION_MISC_BASE + 0)
-  {"membedded-pic", no_argument, NULL, OPTION_MEMBEDDED_PIC},
-#define OPTION_TRAP (OPTION_MISC_BASE + 1)
+#define OPTION_MISC_BASE (OPTION_FIX_BASE + 6)
+#define OPTION_TRAP (OPTION_MISC_BASE + 0)
   {"trap", no_argument, NULL, OPTION_TRAP},
   {"no-break", no_argument, NULL, OPTION_TRAP},
-#define OPTION_BREAK (OPTION_MISC_BASE + 2)
+#define OPTION_BREAK (OPTION_MISC_BASE + 1)
   {"break", no_argument, NULL, OPTION_BREAK},
   {"no-trap", no_argument, NULL, OPTION_BREAK},
-#define OPTION_EB (OPTION_MISC_BASE + 3)
+#define OPTION_EB (OPTION_MISC_BASE + 2)
   {"EB", no_argument, NULL, OPTION_EB},
-#define OPTION_EL (OPTION_MISC_BASE + 4)
+#define OPTION_EL (OPTION_MISC_BASE + 3)
   {"EL", no_argument, NULL, OPTION_EL},
-#define OPTION_FP32 (OPTION_MISC_BASE + 5)
+#define OPTION_FP32 (OPTION_MISC_BASE + 4)
   {"mfp32", no_argument, NULL, OPTION_FP32},
-#define OPTION_GP32 (OPTION_MISC_BASE + 6)
+#define OPTION_GP32 (OPTION_MISC_BASE + 5)
   {"mgp32", no_argument, NULL, OPTION_GP32},
-#define OPTION_CONSTRUCT_FLOATS (OPTION_MISC_BASE + 7)
+#define OPTION_CONSTRUCT_FLOATS (OPTION_MISC_BASE + 6)
   {"construct-floats", no_argument, NULL, OPTION_CONSTRUCT_FLOATS},
-#define OPTION_NO_CONSTRUCT_FLOATS (OPTION_MISC_BASE + 8)
+#define OPTION_NO_CONSTRUCT_FLOATS (OPTION_MISC_BASE + 7)
   {"no-construct-floats", no_argument, NULL, OPTION_NO_CONSTRUCT_FLOATS},
-#define OPTION_FP64 (OPTION_MISC_BASE + 9)
+#define OPTION_FP64 (OPTION_MISC_BASE + 8)
   {"mfp64", no_argument, NULL, OPTION_FP64},
-#define OPTION_GP64 (OPTION_MISC_BASE + 10)
+#define OPTION_GP64 (OPTION_MISC_BASE + 9)
   {"mgp64", no_argument, NULL, OPTION_GP64},
-#define OPTION_RELAX_BRANCH (OPTION_MISC_BASE + 11)
-#define OPTION_NO_RELAX_BRANCH (OPTION_MISC_BASE + 12)
+#define OPTION_RELAX_BRANCH (OPTION_MISC_BASE + 10)
+#define OPTION_NO_RELAX_BRANCH (OPTION_MISC_BASE + 11)
   {"relax-branch", no_argument, NULL, OPTION_RELAX_BRANCH},
   {"no-relax-branch", no_argument, NULL, OPTION_NO_RELAX_BRANCH},
+#define OPTION_MSHARED (OPTION_MISC_BASE + 12)
+#define OPTION_MNO_SHARED (OPTION_MISC_BASE + 13)
+  {"mshared", no_argument, NULL, OPTION_MSHARED},
+  {"mno-shared", no_argument, NULL, OPTION_MNO_SHARED},
+#define OPTION_MSYM32 (OPTION_MISC_BASE + 14)
+#define OPTION_MNO_SYM32 (OPTION_MISC_BASE + 15)
+  {"msym32", no_argument, NULL, OPTION_MSYM32},
+  {"mno-sym32", no_argument, NULL, OPTION_MNO_SYM32},
 
   /* ELF-specific options.  */
 #ifdef OBJ_ELF
-#define OPTION_ELF_BASE    (OPTION_MISC_BASE + 13)
+#define OPTION_ELF_BASE    (OPTION_MISC_BASE + 16)
 #define OPTION_CALL_SHARED (OPTION_ELF_BASE + 0)
   {"KPIC",        no_argument, NULL, OPTION_CALL_SHARED},
   {"call_shared", no_argument, NULL, OPTION_CALL_SHARED},
@@ -10807,6 +10088,10 @@ struct option md_longopts[] =
   {"mdebug", no_argument, NULL, OPTION_MDEBUG},
 #define OPTION_NO_MDEBUG   (OPTION_ELF_BASE + 8)
   {"no-mdebug", no_argument, NULL, OPTION_NO_MDEBUG},
+#define OPTION_PDR        (OPTION_ELF_BASE + 9)
+  {"mpdr", no_argument, NULL, OPTION_PDR},
+#define OPTION_NO_PDR     (OPTION_ELF_BASE + 10)
+  {"mno-pdr", no_argument, NULL, OPTION_NO_PDR},
 #endif /* OBJ_ELF */
 
   {NULL, no_argument, NULL, 0}
@@ -10820,8 +10105,7 @@ size_t md_longopts_size = sizeof (md_longopts);
    in a later argument.  */
 
 static void
-mips_set_option_string (string_ptr, new_value)
-     const char **string_ptr, *new_value;
+mips_set_option_string (const char **string_ptr, const char *new_value)
 {
   if (*string_ptr != 0 && strcasecmp (*string_ptr, new_value) != 0)
     as_warn (_("A different %s was already specified, is now %s"),
@@ -10832,9 +10116,7 @@ mips_set_option_string (string_ptr, new_value)
 }
 
 int
-md_parse_option (c, arg)
-     int c;
-     char *arg;
+md_parse_option (int c, char *arg)
 {
   switch (c)
     {
@@ -10862,10 +10144,6 @@ md_parse_option (c, arg)
       target_big_endian = 0;
       break;
 
-    case 'n':
-      warn_nops = 1;
-      break;
-
     case 'O':
       if (arg && arg[1] == '0')
        mips_optimize = 1;
@@ -10913,6 +10191,10 @@ md_parse_option (c, arg)
       file_mips_isa = ISA_MIPS32R2;
       break;
 
+    case OPTION_MIPS64R2:
+      file_mips_isa = ISA_MIPS64R2;
+      break;
+
     case OPTION_MIPS64:
       file_mips_isa = ISA_MIPS64;
       break;
@@ -10967,12 +10249,12 @@ md_parse_option (c, arg)
 
     case OPTION_MIPS16:
       mips_opts.mips16 = 1;
-      mips_no_prev_insn (FALSE);
+      mips_no_prev_insn ();
       break;
 
     case OPTION_NO_MIPS16:
       mips_opts.mips16 = 0;
-      mips_no_prev_insn (FALSE);
+      mips_no_prev_insn ();
       break;
 
     case OPTION_MIPS3D:
@@ -10983,22 +10265,20 @@ md_parse_option (c, arg)
       mips_opts.ase_mips3d = 0;
       break;
 
-    case OPTION_MEMBEDDED_PIC:
-      mips_pic = EMBEDDED_PIC;
-      if (USE_GLOBAL_POINTER_OPT && g_switch_seen)
-       {
-         as_bad (_("-G may not be used with embedded PIC code"));
-         return 0;
-       }
-      g_switch_value = 0x7fffffff;
+    case OPTION_FIX_VR4120:
+      mips_fix_vr4120 = 1;
+      break;
+
+    case OPTION_NO_FIX_VR4120:
+      mips_fix_vr4120 = 0;
       break;
 
-    case OPTION_FIX_VR4122:
-      mips_fix_4122_bugs = 1;
+    case OPTION_FIX_VR4130:
+      mips_fix_vr4130 = 1;
       break;
 
-    case OPTION_NO_FIX_VR4122:
-      mips_fix_4122_bugs = 0;
+    case OPTION_NO_FIX_VR4130:
+      mips_fix_vr4130 = 0;
       break;
 
     case OPTION_RELAX_BRANCH:
@@ -11009,6 +10289,22 @@ md_parse_option (c, arg)
       mips_relax_branch = 0;
       break;
 
+    case OPTION_MSHARED:
+      mips_in_shared = TRUE;
+      break;
+
+    case OPTION_MNO_SHARED:
+      mips_in_shared = FALSE;
+      break;
+
+    case OPTION_MSYM32:
+      mips_opts.sym32 = TRUE;
+      break;
+
+    case OPTION_MNO_SYM32:
+      mips_opts.sym32 = FALSE;
+      break;
+
 #ifdef OBJ_ELF
       /* When generating ELF code, we permit -KPIC and -call_shared to
         select SVR4_PIC, and -non_shared to select no PIC.  This is
@@ -11048,19 +10344,13 @@ md_parse_option (c, arg)
 #endif /* OBJ_ELF */
 
     case 'G':
-      if (! USE_GLOBAL_POINTER_OPT)
-       {
-         as_bad (_("-G is not supported for this configuration"));
-         return 0;
-       }
-      else if (mips_pic == SVR4_PIC || mips_pic == EMBEDDED_PIC)
+      g_switch_value = atoi (arg);
+      g_switch_seen = 1;
+      if (mips_pic == SVR4_PIC && g_switch_value != 0)
        {
-         as_bad (_("-G may not be used with SVR4 or embedded PIC code"));
+         as_bad (_("-G may not be used with SVR4 PIC code"));
          return 0;
        }
-      else
-       g_switch_value = atoi (arg);
-      g_switch_seen = 1;
       break;
 
 #ifdef OBJ_ELF
@@ -11158,6 +10448,14 @@ md_parse_option (c, arg)
     case OPTION_NO_MDEBUG:
       mips_flag_mdebug = FALSE;
       break;
+
+    case OPTION_PDR:
+      mips_flag_pdr = TRUE;
+      break;
+
+    case OPTION_NO_PDR:
+      mips_flag_pdr = FALSE;
+      break;
 #endif /* OBJ_ELF */
 
     default:
@@ -11171,13 +10469,12 @@ md_parse_option (c, arg)
    described by INFO.  */
 
 static void
-mips_set_architecture (info)
-     const struct mips_cpu_info *info;
+mips_set_architecture (const struct mips_cpu_info *info)
 {
   if (info != 0)
     {
-      mips_arch_info = info;
-      mips_arch = info->cpu;
+      file_mips_arch = info->cpu;
+      mips_opts.arch = info->cpu;
       mips_opts.isa = info->isa;
     }
 }
@@ -11186,25 +10483,23 @@ mips_set_architecture (info)
 /* Likewise for tuning.  */
 
 static void
-mips_set_tune (info)
-     const struct mips_cpu_info *info;
+mips_set_tune (const struct mips_cpu_info *info)
 {
   if (info != 0)
-    {
-      mips_tune_info = info;
-      mips_tune = info->cpu;
-    }
+    mips_tune = info->cpu;
 }
 
 
 void
-mips_after_parse_args ()
+mips_after_parse_args (void)
 {
+  const struct mips_cpu_info *arch_info = 0;
+  const struct mips_cpu_info *tune_info = 0;
+
   /* GP relative stuff not working for PE */
-  if (strncmp (TARGET_OS, "pe", 2) == 0
-      && g_switch_value != 0)
+  if (strncmp (TARGET_OS, "pe", 2) == 0)
     {
-      if (g_switch_seen)
+      if (g_switch_seen && g_switch_value != 0)
        as_bad (_("-G not supported in this configuration."));
       g_switch_value = 0;
     }
@@ -11218,41 +10513,44 @@ mips_after_parse_args ()
      as much as possible.  */
 
   if (mips_arch_string != 0)
-    mips_set_architecture (mips_parse_cpu ("-march", mips_arch_string));
-
-  if (mips_tune_string != 0)
-    mips_set_tune (mips_parse_cpu ("-mtune", mips_tune_string));
+    arch_info = mips_parse_cpu ("-march", mips_arch_string);
 
   if (file_mips_isa != ISA_UNKNOWN)
     {
       /* Handle -mipsN.  At this point, file_mips_isa contains the
-        ISA level specified by -mipsN, while mips_opts.isa contains
+        ISA level specified by -mipsN, while arch_info->isa contains
         the -march selection (if any).  */
-      if (mips_arch_info != 0)
+      if (arch_info != 0)
        {
          /* -march takes precedence over -mipsN, since it is more descriptive.
             There's no harm in specifying both as long as the ISA levels
             are the same.  */
-         if (file_mips_isa != mips_opts.isa)
+         if (file_mips_isa != arch_info->isa)
            as_bad (_("-%s conflicts with the other architecture options, which imply -%s"),
                    mips_cpu_info_from_isa (file_mips_isa)->name,
-                   mips_cpu_info_from_isa (mips_opts.isa)->name);
+                   mips_cpu_info_from_isa (arch_info->isa)->name);
        }
       else
-       mips_set_architecture (mips_cpu_info_from_isa (file_mips_isa));
+       arch_info = mips_cpu_info_from_isa (file_mips_isa);
     }
 
-  if (mips_arch_info == 0)
-    mips_set_architecture (mips_parse_cpu ("default CPU",
-                                          MIPS_CPU_STRING_DEFAULT));
+  if (arch_info == 0)
+    arch_info = mips_parse_cpu ("default CPU", MIPS_CPU_STRING_DEFAULT);
 
-  if (ABI_NEEDS_64BIT_REGS (mips_abi) && !ISA_HAS_64BIT_REGS (mips_opts.isa))
+  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",
-           mips_arch_info->name);
+           arch_info->name);
+
+  mips_set_architecture (arch_info);
+
+  /* Optimize for file_mips_arch, unless -mtune selects a different processor.  */
+  if (mips_tune_string != 0)
+    tune_info = mips_parse_cpu ("-mtune", mips_tune_string);
 
-  /* Optimize for mips_arch, unless -mtune selects a different processor.  */
-  if (mips_tune_info == 0)
-    mips_set_tune (mips_arch_info);
+  if (tune_info == 0)
+    mips_set_tune (arch_info);
+  else
+    mips_set_tune (tune_info);
 
   if (file_mips_gp32 >= 0)
     {
@@ -11285,10 +10583,11 @@ mips_after_parse_args ()
 
   /* End of GCC-shared inference code.  */
 
-  /* ??? When do we want this flag to be set?   Who uses it?  */
-  if (file_mips_gp32 == 1
-      && mips_abi == NO_ABI
-      && ISA_HAS_64BIT_REGS (mips_opts.isa))
+  /* This flag is set when we have a 64-bit capable CPU but use only
+     32-bit wide registers.  Note that EABI does not use it.  */
+  if (ISA_HAS_64BIT_REGS (mips_opts.isa)
+      && ((mips_abi == NO_ABI && file_mips_gp32 == 1)
+         || mips_abi == O32_ABI))
     mips_32bitmode = 1;
 
   if (mips_opts.isa == ISA_MIPS1 && mips_trap)
@@ -11297,11 +10596,11 @@ mips_after_parse_args ()
   /* If the selected architecture includes support for ASEs, enable
      generation of code for them.  */
   if (mips_opts.mips16 == -1)
-    mips_opts.mips16 = (CPU_HAS_MIPS16 (mips_arch)) ? 1 : 0;
+    mips_opts.mips16 = (CPU_HAS_MIPS16 (file_mips_arch)) ? 1 : 0;
   if (mips_opts.ase_mips3d == -1)
-    mips_opts.ase_mips3d = (CPU_HAS_MIPS3D (mips_arch)) ? 1 : 0;
+    mips_opts.ase_mips3d = (CPU_HAS_MIPS3D (file_mips_arch)) ? 1 : 0;
   if (mips_opts.ase_mdmx == -1)
-    mips_opts.ase_mdmx = (CPU_HAS_MDMX (mips_arch)) ? 1 : 0;
+    mips_opts.ase_mdmx = (CPU_HAS_MDMX (file_mips_arch)) ? 1 : 0;
 
   file_mips_isa = mips_opts.isa;
   file_ase_mips16 = mips_opts.mips16;
@@ -11322,7 +10621,7 @@ mips_after_parse_args ()
 }
 \f
 void
-mips_init_after_args ()
+mips_init_after_args (void)
 {
   /* initialize opcodes */
   bfd_mips_num_opcodes = bfd_mips_num_builtin_opcodes;
@@ -11330,8 +10629,7 @@ mips_init_after_args ()
 }
 
 long
-md_pcrel_from (fixP)
-     fixS *fixP;
+md_pcrel_from (fixS *fixP)
 {
   valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
   switch (fixP->fx_r_type)
@@ -11353,7 +10651,7 @@ md_pcrel_from (fixP)
    mips-tfile, in which case we must generate all local labels.  */
 
 void
-mips_frob_file_before_adjust ()
+mips_frob_file_before_adjust (void)
 {
 #ifndef NO_ECOFF_DEBUGGING
   if (ECOFF_DEBUGGING
@@ -11363,20 +10661,64 @@ mips_frob_file_before_adjust ()
 #endif
 }
 
-/* Sort any unmatched HI16_S relocs so that they immediately precede
-   the corresponding LO reloc.  This is called before md_apply_fix3 and
-   tc_gen_reloc.  Unmatched HI16_S relocs can only be generated by
-   explicit use of the %hi modifier.  */
+/* Sort any unmatched HI16 and GOT16 relocs so that they immediately precede
+   the corresponding LO16 reloc.  This is called before md_apply_fix3 and
+   tc_gen_reloc.  Unmatched relocs can only be generated by use of explicit
+   relocation operators.
+
+   For our purposes, a %lo() expression matches a %got() or %hi()
+   expression if:
+
+      (a) it refers to the same symbol; and
+      (b) the offset applied in the %lo() expression is no lower than
+         the offset applied in the %got() or %hi().
+
+   (b) allows us to cope with code like:
+
+       lui     $4,%hi(foo)
+       lh      $4,%lo(foo+2)($4)
+
+   ...which is legal on RELA targets, and has a well-defined behaviour
+   if the user knows that adding 2 to "foo" will not induce a carry to
+   the high 16 bits.
+
+   When several %lo()s match a particular %got() or %hi(), we use the
+   following rules to distinguish them:
+
+     (1) %lo()s with smaller offsets are a better match than %lo()s with
+         higher offsets.
+
+     (2) %lo()s with no matching %got() or %hi() are better than those
+         that already have a matching %got() or %hi().
+
+     (3) later %lo()s are better than earlier %lo()s.
+
+   These rules are applied in order.
+
+   (1) means, among other things, that %lo()s with identical offsets are
+   chosen if they exist.
+
+   (2) means that we won't associate several high-part relocations with
+   the same low-part relocation unless there's no alternative.  Having
+   several high parts for the same low part is a GNU extension; this rule
+   allows careful users to avoid it.
+
+   (3) is purely cosmetic.  mips_hi_fixup_list is is in reverse order,
+   with the last high-part relocation being at the front of the list.
+   It therefore makes sense to choose the last matching low-part
+   relocation, all other things being equal.  It's also easier
+   to code that way.  */
 
 void
-mips_frob_file ()
+mips_frob_file (void)
 {
   struct mips_hi_fixup *l;
 
   for (l = mips_hi_fixup_list; l != NULL; l = l->next)
     {
       segment_info_type *seginfo;
-      int pass;
+      bfd_boolean matched_lo_p;
+      fixS **hi_pos, **lo_pos, **pos;
 
       assert (reloc_needs_lo_p (l->fixp->fx_r_type));
 
@@ -11390,86 +10732,60 @@ mips_frob_file ()
       if (fixup_has_matching_lo_p (l->fixp))
        continue;
 
-      /* Look through the fixups for this segment for a matching %lo.
-         When we find one, move the %hi just in front of it.  We do
-         this in two passes.  In the first pass, we try to find a
-         unique %lo.  In the second pass, we permit multiple %hi
-         relocs for a single %lo (this is a GNU extension).  */
       seginfo = seg_info (l->seg);
-      for (pass = 0; pass < 2; pass++)
-       {
-         fixS *f, *prev;
 
-         prev = NULL;
-         for (f = seginfo->fix_root; f != NULL; f = f->fx_next)
+      /* Set HI_POS to the position of this relocation in the chain.
+        Set LO_POS to the position of the chosen low-part relocation.
+        MATCHED_LO_P is true on entry to the loop if *POS is a low-part
+        relocation that matches an immediately-preceding high-part
+        relocation.  */
+      hi_pos = NULL;
+      lo_pos = NULL;
+      matched_lo_p = FALSE;
+      for (pos = &seginfo->fix_root; *pos != NULL; pos = &(*pos)->fx_next)
+       {
+         if (*pos == l->fixp)
+           hi_pos = pos;
+
+         if ((*pos)->fx_r_type == BFD_RELOC_LO16
+             && (*pos)->fx_addsy == l->fixp->fx_addsy
+             && (*pos)->fx_offset >= l->fixp->fx_offset
+             && (lo_pos == NULL
+                 || (*pos)->fx_offset < (*lo_pos)->fx_offset
+                 || (!matched_lo_p
+                     && (*pos)->fx_offset == (*lo_pos)->fx_offset)))
+           lo_pos = pos;
+
+         matched_lo_p = (reloc_needs_lo_p ((*pos)->fx_r_type)
+                         && fixup_has_matching_lo_p (*pos));
+       }
+
+      /* If we found a match, remove the high-part relocation from its
+        current position and insert it before the low-part relocation.
+        Make the offsets match so that fixup_has_matching_lo_p()
+        will return true.
+
+        We don't warn about unmatched high-part relocations since some
+        versions of gcc have been known to emit dead "lui ...%hi(...)"
+        instructions.  */
+      if (lo_pos != NULL)
+       {
+         l->fixp->fx_offset = (*lo_pos)->fx_offset;
+         if (l->fixp->fx_next != *lo_pos)
            {
-             /* Check whether this is a %lo fixup which matches l->fixp.  */
-             if (f->fx_r_type == BFD_RELOC_LO16
-                 && f->fx_addsy == l->fixp->fx_addsy
-                 && f->fx_offset == l->fixp->fx_offset
-                 && (pass == 1
-                     || prev == NULL
-                     || !reloc_needs_lo_p (prev->fx_r_type)
-                     || !fixup_has_matching_lo_p (prev)))
-               {
-                 fixS **pf;
-
-                 /* Move l->fixp before f.  */
-                 for (pf = &seginfo->fix_root;
-                      *pf != l->fixp;
-                      pf = &(*pf)->fx_next)
-                   assert (*pf != NULL);
-
-                 *pf = l->fixp->fx_next;
-
-                 l->fixp->fx_next = f;
-                 if (prev == NULL)
-                   seginfo->fix_root = l->fixp;
-                 else
-                   prev->fx_next = l->fixp;
-
-                 break;
-               }
-
-             prev = f;
+             *hi_pos = l->fixp->fx_next;
+             l->fixp->fx_next = *lo_pos;
+             *lo_pos = l->fixp;
            }
-
-         if (f != NULL)
-           break;
-
-#if 0 /* GCC code motion plus incomplete dead code elimination
-        can leave a %hi without a %lo.  */
-         if (pass == 1)
-           as_warn_where (l->fixp->fx_file, l->fixp->fx_line,
-                          _("Unmatched %%hi reloc"));
-#endif
        }
     }
 }
 
-/* When generating embedded PIC code we need to use a special
-   relocation to represent the difference of two symbols in the .text
-   section (switch tables use a difference of this sort).  See
-   include/coff/mips.h for details.  This macro checks whether this
-   fixup requires the special reloc.  */
-#define SWITCH_TABLE(fixp) \
-  ((fixp)->fx_r_type == BFD_RELOC_32 \
-   && OUTPUT_FLAVOR != bfd_target_elf_flavour \
-   && (fixp)->fx_addsy != NULL \
-   && (fixp)->fx_subsy != NULL \
-   && S_GET_SEGMENT ((fixp)->fx_addsy) == text_section \
-   && S_GET_SEGMENT ((fixp)->fx_subsy) == text_section)
-
-/* When generating embedded PIC code we must keep all PC relative
-   relocations, in case the linker has to relax a call.  We also need
-   to keep relocations for switch table entries.
-
-   We may have combined relocations without symbols in the N32/N64 ABI.
+/* We may have combined relocations without symbols in the N32/N64 ABI.
    We have to prevent gas from dropping them.  */
 
 int
-mips_force_relocation (fixp)
-     fixS *fixp;
+mips_force_relocation (fixS *fixp)
 {
   if (generic_force_reloc (fixp))
     return 1;
@@ -11481,11 +10797,7 @@ mips_force_relocation (fixp)
          || fixp->fx_r_type == BFD_RELOC_LO16))
     return 1;
 
-  return (mips_pic == EMBEDDED_PIC
-         && (fixp->fx_pcrel
-             || SWITCH_TABLE (fixp)
-             || fixp->fx_r_type == BFD_RELOC_PCREL_HI16_S
-             || fixp->fx_r_type == BFD_RELOC_PCREL_LO16));
+  return 0;
 }
 
 /* This hook is called before a fix is simplified.  We don't really
@@ -11502,9 +10814,7 @@ mips_force_relocation (fixp)
    simplifications.  */
 
 int
-mips_validate_fix (fixP, seg)
-     struct fix *fixP;
-     asection *seg;
+mips_validate_fix (struct fix *fixP, asection *seg)
 {
   /* There's a lot of discussion on whether it should be possible to
      use R_MIPS_PC16 to represent branch relocations.  The outcome
@@ -11527,9 +10837,8 @@ mips_validate_fix (fixP, seg)
      whole function).  */
 
   if (fixP->fx_r_type == BFD_RELOC_16_PCREL_S2
-      && (((OUTPUT_FLAVOR == bfd_target_ecoff_flavour
-           || OUTPUT_FLAVOR == bfd_target_elf_flavour)
-          && mips_pic != EMBEDDED_PIC)
+      && ((OUTPUT_FLAVOR == bfd_target_ecoff_flavour
+          || OUTPUT_FLAVOR == bfd_target_elf_flavour)
          || bfd_reloc_type_lookup (stdoutput, BFD_RELOC_16_PCREL_S2) == NULL)
       && fixP->fx_addsy)
     {
@@ -11550,8 +10859,9 @@ mips_validate_fix (fixP, seg)
        {
          symbolS *sym = fixP->fx_addsy;
 
-         as_warn_where (fixP->fx_file, fixP->fx_line,
-                        _("Pretending global symbol used as branch target is local."));
+         if (mips_pic == SVR4_PIC)
+           as_warn_where (fixP->fx_file, fixP->fx_line,
+                          _("Pretending global symbol used as branch target is local."));
 
          fixP->fx_addsy = symbol_create (S_GET_NAME (sym),
                                          S_GET_SEGMENT (sym),
@@ -11567,43 +10877,13 @@ mips_validate_fix (fixP, seg)
   return 1;
 }
 
-#ifdef OBJ_ELF
-static int
-mips_need_elf_addend_fixup (fixP)
-     fixS *fixP;
-{
-  if (S_GET_OTHER (fixP->fx_addsy) == STO_MIPS16)
-    return 1;
-  if (mips_pic == EMBEDDED_PIC
-      && S_IS_WEAK (fixP->fx_addsy))
-    return 1;
-  if (mips_pic != EMBEDDED_PIC
-      && (S_IS_WEAK (fixP->fx_addsy)
-         || S_IS_EXTERNAL (fixP->fx_addsy))
-      && !S_IS_COMMON (fixP->fx_addsy))
-    return 1;
-  if (((bfd_get_section_flags (stdoutput,
-                              S_GET_SEGMENT (fixP->fx_addsy))
-       & (SEC_LINK_ONCE | SEC_MERGE)) != 0)
-      || !strncmp (segment_name (S_GET_SEGMENT (fixP->fx_addsy)),
-                  ".gnu.linkonce",
-                  sizeof (".gnu.linkonce") - 1))
-    return 1;
-  return 0;
-}
-#endif
-
 /* Apply a fixup to the object file.  */
 
 void
-md_apply_fix3 (fixP, valP, seg)
-     fixS *fixP;
-     valueT *valP;
-     segT seg ATTRIBUTE_UNUSED;
+md_apply_fix3 (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 {
   bfd_byte *buf;
   long insn;
-  static int previous_fx_r_type = 0;
   reloc_howto_type *howto;
 
   /* We ignore generic BFD relocations we don't know about.  */
@@ -11621,75 +10901,33 @@ md_apply_fix3 (fixP, valP, seg)
 
   buf = (bfd_byte *) (fixP->fx_frag->fr_literal + fixP->fx_where);
 
-  /* If we aren't adjusting this fixup to be against the section
-     symbol, we need to adjust the value.  */
-#ifdef OBJ_ELF
-  if (fixP->fx_addsy != NULL && OUTPUT_FLAVOR == bfd_target_elf_flavour)
-    {
-      if (mips_need_elf_addend_fixup (fixP)
-         && howto->partial_inplace
-         && fixP->fx_r_type != BFD_RELOC_GPREL16
-         && fixP->fx_r_type != BFD_RELOC_GPREL32
-         && fixP->fx_r_type != BFD_RELOC_MIPS16_GPREL)
-       {
-         /* In this case, the bfd_install_relocation routine will
-            incorrectly add the symbol value back in.  We just want
-            the addend to appear in the object file.
-
-            The condition above used to include
-            "&& (! fixP->fx_pcrel || howto->pcrel_offset)".
-
-            However, howto can't be trusted here, because we
-            might change the reloc type in tc_gen_reloc.  We can
-            check howto->partial_inplace because that conversion
-            happens to preserve howto->partial_inplace; but it
-            does not preserve howto->pcrel_offset.  I've just
-            eliminated the check, because all MIPS PC-relative
-            relocations are marked howto->pcrel_offset.
-
-            howto->pcrel_offset was originally added for
-            R_MIPS_PC16, which is generated for code like
-
-                   globl g1 .text
-                   .text
-                   .space 20
-            g1:
-            x:
-                   bal g1
-          */
-         *valP -= S_GET_VALUE (fixP->fx_addsy);
-       }
+  assert (! fixP->fx_pcrel);
 
-      /* This code was generated using trial and error and so is
-        fragile and not trustworthy.  If you change it, you should
-        rerun the elf-rel, elf-rel2, and empic testcases and ensure
-        they still pass.  */
-      if (fixP->fx_pcrel)
-       {
-         *valP += fixP->fx_frag->fr_address + fixP->fx_where;
+  /* Don't treat parts of a composite relocation as done.  There are two
+     reasons for this:
 
-         /* BFD's REL handling, for MIPS, is _very_ weird.
-            This gives the right results, but it can't possibly
-            be the way things are supposed to work.  */
-         *valP += fixP->fx_frag->fr_address + fixP->fx_where;
-       }
-    }
-#endif
+     (1) The second and third parts will be against 0 (RSS_UNDEF) but
+        should nevertheless be emitted if the first part is.
 
-  /* We are not done if this is a composite relocation to set up gp.  */
-  if (fixP->fx_addsy == NULL && ! fixP->fx_pcrel
-      && !(fixP->fx_r_type == BFD_RELOC_MIPS_SUB
-          || (fixP->fx_r_type == BFD_RELOC_64
-              && (previous_fx_r_type == BFD_RELOC_GPREL32
-                  || previous_fx_r_type == BFD_RELOC_GPREL16))
-          || (previous_fx_r_type == BFD_RELOC_MIPS_SUB
-              && (fixP->fx_r_type == BFD_RELOC_HI16_S
-                  || fixP->fx_r_type == BFD_RELOC_LO16))))
+     (2) In normal usage, composite relocations are never assembly-time
+        constants.  The easiest way of dealing with the pathological
+        exceptions is to generate a relocation against STN_UNDEF and
+        leave everything up to the linker.  */
+  if (fixP->fx_addsy == NULL && fixP->fx_tcbit == 0)
     fixP->fx_done = 1;
-  previous_fx_r_type = fixP->fx_r_type;
 
   switch (fixP->fx_r_type)
     {
+    case BFD_RELOC_MIPS_TLS_GD:
+    case BFD_RELOC_MIPS_TLS_LDM:
+    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_TPREL_HI16:
+    case BFD_RELOC_MIPS_TLS_TPREL_LO16:
+      S_SET_THREAD_LOCAL (fixP->fx_addsy);
+      /* fall through */
+
     case BFD_RELOC_MIPS_JMP:
     case BFD_RELOC_MIPS_SHIFT5:
     case BFD_RELOC_MIPS_SHIFT6:
@@ -11718,9 +10956,9 @@ md_apply_fix3 (fixP, valP, seg)
     case BFD_RELOC_MIPS_CALL_HI16:
     case BFD_RELOC_MIPS_CALL_LO16:
     case BFD_RELOC_MIPS16_GPREL:
-      if (fixP->fx_pcrel)
-       as_bad_where (fixP->fx_file, fixP->fx_line,
-                     _("Invalid PC relative reloc"));
+    case BFD_RELOC_MIPS16_HI16:
+    case BFD_RELOC_MIPS16_HI16_S:
+      assert (! fixP->fx_pcrel);
       /* Nothing needed to do. The value comes from the reloc entry */
       break;
 
@@ -11731,47 +10969,13 @@ md_apply_fix3 (fixP, valP, seg)
       *valP = 0;
       break;
 
-    case BFD_RELOC_PCREL_HI16_S:
-      /* The addend for this is tricky if it is internal, so we just
-        do everything here rather than in bfd_install_relocation.  */
-      if (OUTPUT_FLAVOR == bfd_target_elf_flavour && !fixP->fx_done)
-       break;
-      if (fixP->fx_addsy
-         && (symbol_get_bfdsym (fixP->fx_addsy)->flags & BSF_SECTION_SYM) == 0)
-       {
-         /* For an external symbol adjust by the address to make it
-            pcrel_offset.  We use the address of the RELLO reloc
-            which follows this one.  */
-         *valP += (fixP->fx_next->fx_frag->fr_address
-                   + fixP->fx_next->fx_where);
-       }
-      *valP = ((*valP + 0x8000) >> 16) & 0xffff;
-      if (target_big_endian)
-       buf += 2;
-      md_number_to_chars ((char *) buf, *valP, 2);
-      break;
-
-    case BFD_RELOC_PCREL_LO16:
-      /* The addend for this is tricky if it is internal, so we just
-        do everything here rather than in bfd_install_relocation.  */
-      if (OUTPUT_FLAVOR == bfd_target_elf_flavour && !fixP->fx_done)
-       break;
-      if (fixP->fx_addsy
-         && (symbol_get_bfdsym (fixP->fx_addsy)->flags & BSF_SECTION_SYM) == 0)
-       *valP += fixP->fx_frag->fr_address + fixP->fx_where;
-      if (target_big_endian)
-       buf += 2;
-      md_number_to_chars ((char *) buf, *valP, 2);
-      break;
-
     case BFD_RELOC_64:
       /* This is handled like BFD_RELOC_32, but we output a sign
          extended value if we are only 32 bits.  */
-      if (fixP->fx_done
-         || (mips_pic == EMBEDDED_PIC && SWITCH_TABLE (fixP)))
+      if (fixP->fx_done)
        {
          if (8 <= sizeof (valueT))
-           md_number_to_chars (buf, *valP, 8);
+           md_number_to_chars ((char *) buf, *valP, 8);
          else
            {
              valueT hiv;
@@ -11792,23 +10996,22 @@ md_apply_fix3 (fixP, valP, seg)
     case BFD_RELOC_32:
       /* 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.  We also need
-        to fill in the value if this is an embedded PIC switch table
-        entry.  */
-      if (fixP->fx_done
-         || (mips_pic == EMBEDDED_PIC && SWITCH_TABLE (fixP)))
-       md_number_to_chars (buf, *valP, 4);
+        resolved when it appears but is later defined.   */
+      if (fixP->fx_done)
+       md_number_to_chars ((char *) buf, *valP, 4);
       break;
 
     case BFD_RELOC_16:
       /* If we are deleting this reloc entry, we must fill in the
          value now.  */
-      assert (fixP->fx_size == 2);
       if (fixP->fx_done)
-       md_number_to_chars (buf, *valP, 2);
+       md_number_to_chars ((char *) buf, *valP, 2);
       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)
@@ -11844,13 +11047,13 @@ md_apply_fix3 (fixP, valP, seg)
       if (*valP + 0x20000 <= 0x3ffff)
        {
          insn |= (*valP >> 2) & 0xffff;
-         md_number_to_chars ((char *) buf, (valueT) insn, 4);
+         md_number_to_chars ((char *) buf, insn, 4);
        }
       else if (mips_pic == NO_PIC
               && fixP->fx_done
               && fixP->fx_frag->fr_address >= text_section->vma
               && (fixP->fx_frag->fr_address
-                  < text_section->vma + text_section->_raw_size)
+                  < text_section->vma + bfd_get_section_size (text_section))
               && ((insn & 0xffff0000) == 0x10000000     /* beq $0,$0 */
                   || (insn & 0xffff0000) == 0x04010000  /* bgez $0 */
                   || (insn & 0xffff0000) == 0x04110000)) /* bgezal $0 */
@@ -11866,7 +11069,7 @@ md_apply_fix3 (fixP, valP, seg)
          fixP->fx_done = 0;
          fixP->fx_addsy = section_symbol (text_section);
          *valP += md_pcrel_from (fixP);
-         md_number_to_chars ((char *) buf, (valueT) insn, 4);
+         md_number_to_chars ((char *) buf, insn, 4);
        }
       else
        {
@@ -11898,96 +11101,8 @@ md_apply_fix3 (fixP, valP, seg)
   fixP->fx_addnumber = *valP;
 }
 
-#if 0
-void
-printInsn (oc)
-     unsigned long oc;
-{
-  const struct mips_opcode *p;
-  int treg, sreg, dreg, shamt;
-  short imm;
-  const char *args;
-  int i;
-
-  for (i = 0; i < NUMOPCODES; ++i)
-    {
-      p = &mips_opcodes[i];
-      if (((oc & p->mask) == p->match) && (p->pinfo != INSN_MACRO))
-       {
-         printf ("%08lx %s\t", oc, p->name);
-         treg = (oc >> 16) & 0x1f;
-         sreg = (oc >> 21) & 0x1f;
-         dreg = (oc >> 11) & 0x1f;
-         shamt = (oc >> 6) & 0x1f;
-         imm = oc;
-         for (args = p->args;; ++args)
-           {
-             switch (*args)
-               {
-               case '\0':
-                 printf ("\n");
-                 break;
-
-               case ',':
-               case '(':
-               case ')':
-                 printf ("%c", *args);
-                 continue;
-
-               case 'r':
-                 assert (treg == sreg);
-                 printf ("$%d,$%d", treg, sreg);
-                 continue;
-
-               case 'd':
-               case 'G':
-                 printf ("$%d", dreg);
-                 continue;
-
-               case 't':
-               case 'E':
-                 printf ("$%d", treg);
-                 continue;
-
-               case 'k':
-                 printf ("0x%x", treg);
-                 continue;
-
-               case 'b':
-               case 's':
-                 printf ("$%d", sreg);
-                 continue;
-
-               case 'a':
-                 printf ("0x%08lx", oc & 0x1ffffff);
-                 continue;
-
-               case 'i':
-               case 'j':
-               case 'o':
-               case 'u':
-                 printf ("%d", imm);
-                 continue;
-
-               case '<':
-               case '>':
-                 printf ("$%d", shamt);
-                 continue;
-
-               default:
-                 internalError ();
-               }
-             break;
-           }
-         return;
-       }
-    }
-  printf (_("%08lx  UNDEFINED\n"), oc);
-}
-#endif
-
 static symbolS *
-get_symbol ()
+get_symbol (void)
 {
   int c;
   char *name;
@@ -12004,12 +11119,9 @@ get_symbol ()
    also automatically adjusts any preceding label.  */
 
 static void
-mips_align (to, fill, label)
-     int to;
-     int fill;
-     symbolS *label;
+mips_align (int to, int fill, symbolS *label)
 {
-  mips_emit_delays (FALSE);
+  mips_emit_delays ();
   frag_align (to, fill, 0);
   record_alignment (now_seg, to);
   if (label != NULL)
@@ -12024,8 +11136,7 @@ mips_align (to, fill, label)
    alignment used by the data creating pseudo-ops.  */
 
 static void
-s_align (x)
-     int x ATTRIBUTE_UNUSED;
+s_align (int x ATTRIBUTE_UNUSED)
 {
   register int temp;
   register long temp_fill;
@@ -12033,7 +11144,7 @@ s_align (x)
 
   /*
 
-    o  Note that the assembler pulls down any immediately preceeding label
+    o  Note that the assembler pulls down any immediately preceding label
        to the aligned address.
     o  It's not documented but auto alignment is reinstated by
        a .align pseudo instruction.
@@ -12072,26 +11183,11 @@ s_align (x)
   demand_empty_rest_of_line ();
 }
 
-void
-mips_flush_pending_output ()
-{
-  mips_emit_delays (FALSE);
-  mips_clear_insn_labels ();
-}
-
 static void
-s_change_sec (sec)
-     int sec;
+s_change_sec (int sec)
 {
   segT seg;
 
-  /* When generating embedded PIC code, we only use the .text, .lit8,
-     .sdata and .sbss sections.  We change the .data and .rdata
-     pseudo-ops to use .sdata.  */
-  if (mips_pic == EMBEDDED_PIC
-      && (sec == 'd' || sec == 'r'))
-    sec = 's';
-
 #ifdef OBJ_ELF
   /* The ELF backend needs to know that we are changing sections, so
      that .previous works correctly.  We could do something like check
@@ -12102,7 +11198,7 @@ s_change_sec (sec)
   obj_elf_section_change_hook ();
 #endif
 
-  mips_emit_delays (FALSE);
+  mips_emit_delays ();
   switch (sec)
     {
     case 't':
@@ -12117,60 +11213,37 @@ s_change_sec (sec)
       break;
 
     case 'r':
-      if (USE_GLOBAL_POINTER_OPT)
-       {
-         seg = subseg_new (RDATA_SECTION_NAME,
-                           (subsegT) get_absolute_expression ());
-         if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
-           {
-             bfd_set_section_flags (stdoutput, seg,
-                                    (SEC_ALLOC
-                                     | SEC_LOAD
-                                     | SEC_READONLY
-                                     | SEC_RELOC
-                                     | SEC_DATA));
-             if (strcmp (TARGET_OS, "elf") != 0)
-               record_alignment (seg, 4);
-           }
-         demand_empty_rest_of_line ();
-       }
-      else
+      seg = subseg_new (RDATA_SECTION_NAME,
+                       (subsegT) get_absolute_expression ());
+      if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
        {
-         as_bad (_("No read only data section in this object file format"));
-         demand_empty_rest_of_line ();
-         return;
+         bfd_set_section_flags (stdoutput, seg, (SEC_ALLOC | SEC_LOAD
+                                                 | SEC_READONLY | SEC_RELOC
+                                                 | SEC_DATA));
+         if (strcmp (TARGET_OS, "elf") != 0)
+           record_alignment (seg, 4);
        }
+      demand_empty_rest_of_line ();
       break;
 
     case 's':
-      if (USE_GLOBAL_POINTER_OPT)
-       {
-         seg = subseg_new (".sdata", (subsegT) get_absolute_expression ());
-         if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
-           {
-             bfd_set_section_flags (stdoutput, seg,
-                                    SEC_ALLOC | SEC_LOAD | SEC_RELOC
-                                    | SEC_DATA);
-             if (strcmp (TARGET_OS, "elf") != 0)
-               record_alignment (seg, 4);
-           }
-         demand_empty_rest_of_line ();
-         break;
-       }
-      else
+      seg = subseg_new (".sdata", (subsegT) get_absolute_expression ());
+      if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
        {
-         as_bad (_("Global pointers not supported; recompile -G 0"));
-         demand_empty_rest_of_line ();
-         return;
+         bfd_set_section_flags (stdoutput, seg,
+                                SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA);
+         if (strcmp (TARGET_OS, "elf") != 0)
+           record_alignment (seg, 4);
        }
+      demand_empty_rest_of_line ();
+      break;
     }
 
   auto_align = 1;
 }
 
 void
-s_change_section (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+s_change_section (int ignore ATTRIBUTE_UNUSED)
 {
 #ifdef OBJ_ELF
   char *section_name;
@@ -12220,6 +11293,22 @@ s_change_section (ignore)
 
   section_name = xstrdup (section_name);
 
+  /* When using the generic form of .section (as implemented by obj-elf.c),
+     there's no way to set the section type to SHT_MIPS_DWARF.  Users have
+     traditionally had to fall back on the more common @progbits instead.
+
+     There's nothing really harmful in this, since bfd will correct
+     SHT_PROGBITS to SHT_MIPS_DWARF before writing out the file.  But it
+     means that, for backwards compatibiltiy, the special_section entries
+     for dwarf sections must use SHT_PROGBITS rather than SHT_MIPS_DWARF.
+
+     Even so, we shouldn't force users of the MIPS .section syntax to
+     incorrectly label the sections as SHT_PROGBITS.  The best compromise
+     seems to be to map SHT_MIPS_DWARF to SHT_PROGBITS before calling the
+     generic type-checking code.  */
+  if (section_type == SHT_MIPS_DWARF)
+    section_type = SHT_PROGBITS;
+
   obj_elf_change_section (section_name, section_type, section_flag,
                          section_entry_size, 0, 0, 0);
 
@@ -12229,19 +11318,18 @@ s_change_section (ignore)
 }
 
 void
-mips_enable_auto_align ()
+mips_enable_auto_align (void)
 {
   auto_align = 1;
 }
 
 static void
-s_cons (log_size)
-     int log_size;
+s_cons (int log_size)
 {
   symbolS *label;
 
   label = insn_labels != NULL ? insn_labels->label : NULL;
-  mips_emit_delays (FALSE);
+  mips_emit_delays ();
   if (log_size > 0 && auto_align)
     mips_align (log_size, 0, label);
   mips_clear_insn_labels ();
@@ -12249,14 +11337,13 @@ s_cons (log_size)
 }
 
 static void
-s_float_cons (type)
-     int type;
+s_float_cons (int type)
 {
   symbolS *label;
 
   label = insn_labels != NULL ? insn_labels->label : NULL;
 
-  mips_emit_delays (FALSE);
+  mips_emit_delays ();
 
   if (auto_align)
     {
@@ -12278,8 +11365,7 @@ s_float_cons (type)
    considered to be the address of a function.  */
 
 static void
-s_mips_globl (x)
-     int x ATTRIBUTE_UNUSED;
+s_mips_globl (int x ATTRIBUTE_UNUSED)
 {
   char *name;
   int c;
@@ -12319,8 +11405,7 @@ s_mips_globl (x)
 }
 
 static void
-s_option (x)
-     int x ATTRIBUTE_UNUSED;
+s_option (int x ATTRIBUTE_UNUSED)
 {
   char *opt;
   char c;
@@ -12347,7 +11432,7 @@ s_option (x)
       else
        as_bad (_(".option pic%d not supported"), i);
 
-      if (USE_GLOBAL_POINTER_OPT && mips_pic == SVR4_PIC)
+      if (mips_pic == SVR4_PIC)
        {
          if (g_switch_seen && g_switch_value != 0)
            as_warn (_("-G may not be used with SVR4 PIC code"));
@@ -12375,8 +11460,7 @@ static struct mips_option_stack *mips_opts_stack;
 /* Handle the .set pseudo-op.  */
 
 static void
-s_mipsset (x)
-     int x ATTRIBUTE_UNUSED;
+s_mipsset (int x ATTRIBUTE_UNUSED)
 {
   char *name = input_line_pointer, ch;
 
@@ -12387,22 +11471,13 @@ s_mipsset (x)
 
   if (strcmp (name, "reorder") == 0)
     {
-      if (mips_opts.noreorder && prev_nop_frag != NULL)
-       {
-         /* If we still have pending nops, we can discard them.  The
-            usual nop handling will insert any that are still
-            needed.  */
-         prev_nop_frag->fr_fix -= (prev_nop_frag_holds
-                                   * (mips_opts.mips16 ? 2 : 4));
-         prev_nop_frag = NULL;
-       }
-      mips_opts.noreorder = 0;
+      if (mips_opts.noreorder)
+       end_noreorder ();
     }
   else if (strcmp (name, "noreorder") == 0)
     {
-      mips_emit_delays (TRUE);
-      mips_opts.noreorder = 1;
-      mips_any_noreorder = 1;
+      if (!mips_opts.noreorder)
+       start_noreorder ();
     }
   else if (strcmp (name, "at") == 0)
     {
@@ -12452,35 +11527,46 @@ s_mipsset (x)
     mips_opts.ase_mdmx = 1;
   else if (strcmp (name, "nomdmx") == 0)
     mips_opts.ase_mdmx = 0;
-  else if (strncmp (name, "mips", 4) == 0)
+  else if (strncmp (name, "mips", 4) == 0 || strncmp (name, "arch=", 5) == 0)
     {
       int reset = 0;
 
-      /* Permit the user to change the ISA on the fly.  Needless to
-        say, misuse can cause serious problems.  */
-      if (strcmp (name, "mips0") == 0)
+      /* Permit the user to change the ISA and architecture on the fly.
+        Needless to say, misuse can cause serious problems.  */
+      if (strcmp (name, "mips0") == 0 || strcmp (name, "arch=default") == 0)
        {
          reset = 1;
          mips_opts.isa = file_mips_isa;
+         mips_opts.arch = file_mips_arch;
+       }
+      else if (strncmp (name, "arch=", 5) == 0)
+       {
+         const struct mips_cpu_info *p;
+
+         p = mips_parse_cpu("internal use", name + 5);
+         if (!p)
+           as_bad (_("unknown architecture %s"), name + 5);
+         else
+           {
+             mips_opts.arch = p->cpu;
+             mips_opts.isa = p->isa;
+           }
+       }
+      else if (strncmp (name, "mips", 4) == 0)
+       {
+         const struct mips_cpu_info *p;
+
+         p = mips_parse_cpu("internal use", name);
+         if (!p)
+           as_bad (_("unknown ISA level %s"), name + 4);
+         else
+           {
+             mips_opts.arch = p->cpu;
+             mips_opts.isa = p->isa;
+           }
        }
-      else if (strcmp (name, "mips1") == 0)
-       mips_opts.isa = ISA_MIPS1;
-      else if (strcmp (name, "mips2") == 0)
-       mips_opts.isa = ISA_MIPS2;
-      else if (strcmp (name, "mips3") == 0)
-       mips_opts.isa = ISA_MIPS3;
-      else if (strcmp (name, "mips4") == 0)
-       mips_opts.isa = ISA_MIPS4;
-      else if (strcmp (name, "mips5") == 0)
-       mips_opts.isa = ISA_MIPS5;
-      else if (strcmp (name, "mips32") == 0)
-       mips_opts.isa = ISA_MIPS32;
-      else if (strcmp (name, "mips32r2") == 0)
-       mips_opts.isa = ISA_MIPS32R2;
-      else if (strcmp (name, "mips64") == 0)
-       mips_opts.isa = ISA_MIPS64;
       else
-       as_bad (_("unknown ISA level %s"), name + 4);
+       as_bad (_("unknown ISA or architecture %s"), name);
 
       switch (mips_opts.isa)
        {
@@ -12497,6 +11583,7 @@ s_mipsset (x)
        case ISA_MIPS4:
        case ISA_MIPS5:
        case ISA_MIPS64:
+       case ISA_MIPS64R2:
          mips_opts.gp32 = 0;
          mips_opts.fp32 = 0;
          break;
@@ -12535,22 +11622,19 @@ s_mipsset (x)
          /* If we're changing the reorder mode we need to handle
              delay slots correctly.  */
          if (s->options.noreorder && ! mips_opts.noreorder)
-           mips_emit_delays (TRUE);
+           start_noreorder ();
          else if (! s->options.noreorder && mips_opts.noreorder)
-           {
-             if (prev_nop_frag != NULL)
-               {
-                 prev_nop_frag->fr_fix -= (prev_nop_frag_holds
-                                           * (mips_opts.mips16 ? 2 : 4));
-                 prev_nop_frag = NULL;
-               }
-           }
+           end_noreorder ();
 
          mips_opts = s->options;
          mips_opts_stack = s->next;
          free (s);
        }
     }
+  else if (strcmp (name, "sym32") == 0)
+    mips_opts.sym32 = TRUE;
+  else if (strcmp (name, "nosym32") == 0)
+    mips_opts.sym32 = FALSE;
   else
     {
       as_warn (_("Tried to set unrecognized symbol: %s\n"), name);
@@ -12563,17 +11647,15 @@ s_mipsset (x)
    .option pic2.  It means to generate SVR4 PIC calls.  */
 
 static void
-s_abicalls (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+s_abicalls (int ignore ATTRIBUTE_UNUSED)
 {
   mips_pic = SVR4_PIC;
   mips_abicalls = TRUE;
-  if (USE_GLOBAL_POINTER_OPT)
-    {
-      if (g_switch_seen && g_switch_value != 0)
-       as_warn (_("-G may not be used with SVR4 PIC code"));
-      g_switch_value = 0;
-    }
+
+  if (g_switch_seen && g_switch_value != 0)
+    as_warn (_("-G may not be used with SVR4 PIC code"));
+  g_switch_value = 0;
+
   bfd_set_gp_size (stdoutput, 0);
   demand_empty_rest_of_line ();
 }
@@ -12586,14 +11668,22 @@ s_abicalls (ignore)
        lui     $gp,%hi(_gp_disp)
        addiu   $gp,$gp,%lo(_gp_disp)
        addu    $gp,$gp,.cpload argument
-   The .cpload argument is normally $25 == $t9.  */
+   The .cpload argument is normally $25 == $t9.
+
+   The -mno-shared option changes this to:
+       lui     $gp,%hi(__gnu_local_gp)
+       addiu   $gp,$gp,%lo(__gnu_local_gp)
+   and the argument is ignored.  This saves an instruction, but the
+   resulting code is not position independent; it uses an absolute
+   address for __gnu_local_gp.  Thus code assembled with -mno-shared
+   can go into an ordinary executable, but not into a shared library.  */
 
 static void
-s_cpload (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+s_cpload (int ignore ATTRIBUTE_UNUSED)
 {
   expressionS ex;
-  int icnt = 0;
+  int reg;
+  int in_shared;
 
   /* If we are not generating SVR4 PIC code, or if this is NewABI code,
      .cpload is ignored.  */
@@ -12607,20 +11697,29 @@ s_cpload (ignore)
   if (mips_opts.noreorder == 0)
     as_warn (_(".cpload not in noreorder section"));
 
+  reg = tc_get_register (0);
+
+  /* If we need to produce a 64-bit address, we are better off using
+     the default instruction sequence.  */
+  in_shared = mips_in_shared || HAVE_64BIT_SYMBOLS;
+
   ex.X_op = O_symbol;
-  ex.X_add_symbol = symbol_find_or_make ("_gp_disp");
+  ex.X_add_symbol = symbol_find_or_make (in_shared ? "_gp_disp" :
+                                         "__gnu_local_gp");
   ex.X_op_symbol = NULL;
   ex.X_add_number = 0;
 
   /* In ELF, this symbol is implicitly an STT_OBJECT symbol.  */
   symbol_get_bfdsym (ex.X_add_symbol)->flags |= BSF_OBJECT;
 
-  macro_build_lui (NULL, &icnt, &ex, mips_gp_register);
-  macro_build ((char *) NULL, &icnt, &ex, "addiu", "t,r,j",
-              mips_gp_register, mips_gp_register, (int) BFD_RELOC_LO16);
-
-  macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "addu", "d,v,t",
-              mips_gp_register, mips_gp_register, tc_get_register (0));
+  macro_start ();
+  macro_build_lui (&ex, mips_gp_register);
+  macro_build (&ex, "addiu", "t,r,j", mips_gp_register,
+              mips_gp_register, BFD_RELOC_LO16);
+  if (in_shared)
+    macro_build (NULL, "addu", "d,v,t", mips_gp_register,
+                mips_gp_register, reg);
+  macro_end ();
 
   demand_empty_rest_of_line ();
 }
@@ -12639,16 +11738,19 @@ s_cpload (ignore)
      lui       $gp, %hi(%neg(%gp_rel(label)))
      addiu     $gp, $gp, %lo(%neg(%gp_rel(label)))
      daddu     $gp, $gp, $reg1
-   $reg1 is normally $25 == $t9.  */
+   $reg1 is normally $25 == $t9.
+
+   The -mno-shared option replaces the last three instructions with
+       lui     $gp,%hi(_gp)
+       addiu   $gp,$gp,%lo(_gp)
+   */
+
 static void
-s_cpsetup (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+s_cpsetup (int ignore ATTRIBUTE_UNUSED)
 {
   expressionS ex_off;
   expressionS ex_sym;
   int reg1;
-  int icnt = 0;
-  char *f;
 
   /* If we are not generating SVR4 PIC code, .cpsetup is ignored.
      We also need NewABI support.  */
@@ -12689,6 +11791,7 @@ s_cpsetup (ignore)
   SKIP_WHITESPACE ();
   expression (&ex_sym);
 
+  macro_start ();
   if (mips_cpreturn_register == -1)
     {
       ex_off.X_op = O_constant;
@@ -12696,42 +11799,50 @@ s_cpsetup (ignore)
       ex_off.X_op_symbol = NULL;
       ex_off.X_add_number = mips_cpreturn_offset;
 
-      macro_build ((char *) NULL, &icnt, &ex_off, "sd", "t,o(b)",
-                  mips_gp_register, (int) BFD_RELOC_LO16, mips_frame_reg);
+      macro_build (&ex_off, "sd", "t,o(b)", mips_gp_register,
+                  BFD_RELOC_LO16, SP);
+    }
+  else
+    macro_build (NULL, "daddu", "d,v,t", mips_cpreturn_register,
+                mips_gp_register, 0);
+
+  if (mips_in_shared || HAVE_64BIT_SYMBOLS)
+    {
+      macro_build (&ex_sym, "lui", "t,u", mips_gp_register,
+                  -1, BFD_RELOC_GPREL16, BFD_RELOC_MIPS_SUB,
+                  BFD_RELOC_HI16_S);
+
+      macro_build (&ex_sym, "addiu", "t,r,j", mips_gp_register,
+                  mips_gp_register, -1, BFD_RELOC_GPREL16,
+                  BFD_RELOC_MIPS_SUB, BFD_RELOC_LO16);
+
+      macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", mips_gp_register,
+                  mips_gp_register, reg1);
     }
   else
-    macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "daddu",
-                "d,v,t", mips_cpreturn_register, mips_gp_register, 0);
-
-  /* Ensure there's room for the next two instructions, so that `f'
-     doesn't end up with an address in the wrong frag.  */
-  frag_grow (8);
-  f = frag_more (0);
-  macro_build ((char *) NULL, &icnt, &ex_sym, "lui", "t,u", mips_gp_register,
-              (int) BFD_RELOC_GPREL16);
-  fix_new (frag_now, f - frag_now->fr_literal,
-          8, NULL, 0, 0, BFD_RELOC_MIPS_SUB);
-  fix_new (frag_now, f - frag_now->fr_literal,
-          4, NULL, 0, 0, BFD_RELOC_HI16_S);
-
-  f = frag_more (0);
-  macro_build ((char *) NULL, &icnt, &ex_sym, "addiu", "t,r,j",
-              mips_gp_register, mips_gp_register, (int) BFD_RELOC_GPREL16);
-  fix_new (frag_now, f - frag_now->fr_literal,
-          8, NULL, 0, 0, BFD_RELOC_MIPS_SUB);
-  fix_new (frag_now, f - frag_now->fr_literal,
-          4, NULL, 0, 0, BFD_RELOC_LO16);
-
-  macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-              HAVE_64BIT_ADDRESSES ? "daddu" : "add", "d,v,t",
-              mips_gp_register, mips_gp_register, reg1);
+    {
+      expressionS ex;
+
+      ex.X_op = O_symbol;
+      ex.X_add_symbol = symbol_find_or_make ("_gp");
+      ex.X_op_symbol = NULL;
+      ex.X_add_number = 0;
+
+      /* In ELF, this symbol is implicitly an STT_OBJECT symbol.  */
+      symbol_get_bfdsym (ex.X_add_symbol)->flags |= BSF_OBJECT;
+
+      macro_build_lui (&ex, mips_gp_register);
+      macro_build (&ex, "addiu", "t,r,j", mips_gp_register,
+                  mips_gp_register, BFD_RELOC_LO16);
+    }
+
+  macro_end ();
 
   demand_empty_rest_of_line ();
 }
 
 static void
-s_cplocal (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+s_cplocal (int ignore ATTRIBUTE_UNUSED)
 {
   /* If we are not generating SVR4 PIC code, or if this is not NewABI code,
    .cplocal is ignored.  */
@@ -12750,11 +11861,9 @@ s_cplocal (ignore)
    call $gp is restored from that location.  */
 
 static void
-s_cprestore (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+s_cprestore (int ignore ATTRIBUTE_UNUSED)
 {
   expressionS ex;
-  int icnt = 0;
 
   /* If we are not generating SVR4 PIC code, or if this is NewABI code,
      .cprestore is ignored.  */
@@ -12772,26 +11881,25 @@ s_cprestore (ignore)
   ex.X_op_symbol = NULL;
   ex.X_add_number = mips_cprestore_offset;
 
-  macro_build_ldst_constoffset ((char *) NULL, &icnt, &ex,
-                               HAVE_32BIT_ADDRESSES ? "sw" : "sd",
-                               mips_gp_register, mips_frame_reg);
+  macro_start ();
+  macro_build_ldst_constoffset (&ex, ADDRESS_STORE_INSN, mips_gp_register,
+                               SP, HAVE_64BIT_ADDRESSES);
+  macro_end ();
 
   demand_empty_rest_of_line ();
 }
 
 /* Handle the .cpreturn pseudo-op defined for NewABI PIC code. If an offset
-   was given in the preceeding .gpsetup, it results in:
+   was given in the preceding .cpsetup, it results in:
      ld                $gp, offset($sp)
 
    If a register $reg2 was given there, it results in:
-     daddiu    $gp, $gp, $reg2
+     daddu     $gp, $reg2, $0
  */
 static void
-s_cpreturn (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+s_cpreturn (int ignore ATTRIBUTE_UNUSED)
 {
   expressionS ex;
-  int icnt = 0;
 
   /* If we are not generating SVR4 PIC code, .cpreturn is ignored.
      We also need NewABI support.  */
@@ -12801,6 +11909,7 @@ s_cpreturn (ignore)
       return;
     }
 
+  macro_start ();
   if (mips_cpreturn_register == -1)
     {
       ex.X_op = O_constant;
@@ -12808,12 +11917,12 @@ s_cpreturn (ignore)
       ex.X_op_symbol = NULL;
       ex.X_add_number = mips_cpreturn_offset;
 
-      macro_build ((char *) NULL, &icnt, &ex, "ld", "t,o(b)",
-                  mips_gp_register, (int) BFD_RELOC_LO16, mips_frame_reg);
+      macro_build (&ex, "ld", "t,o(b)", mips_gp_register, BFD_RELOC_LO16, SP);
     }
   else
-    macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "daddu",
-                "d,v,t", mips_gp_register, mips_cpreturn_register, 0);
+    macro_build (NULL, "daddu", "d,v,t", mips_gp_register,
+                mips_cpreturn_register, 0);
+  macro_end ();
 
   demand_empty_rest_of_line ();
 }
@@ -12822,8 +11931,7 @@ s_cpreturn (ignore)
    code.  It sets the offset to use in gp_rel relocations.  */
 
 static void
-s_gpvalue (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+s_gpvalue (int ignore ATTRIBUTE_UNUSED)
 {
   /* If we are not generating SVR4 PIC code, .gpvalue is ignored.
      We also need NewABI support.  */
@@ -12842,8 +11950,7 @@ s_gpvalue (ignore)
    code.  It generates a 32 bit GP relative reloc.  */
 
 static void
-s_gpword (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+s_gpword (int ignore ATTRIBUTE_UNUSED)
 {
   symbolS *label;
   expressionS ex;
@@ -12857,7 +11964,7 @@ s_gpword (ignore)
     }
 
   label = insn_labels != NULL ? insn_labels->label : NULL;
-  mips_emit_delays (TRUE);
+  mips_emit_delays ();
   if (auto_align)
     mips_align (2, 0, label);
   mips_clear_insn_labels ();
@@ -12871,7 +11978,7 @@ s_gpword (ignore)
     }
 
   p = frag_more (4);
-  md_number_to_chars (p, (valueT) 0, 4);
+  md_number_to_chars (p, 0, 4);
   fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, FALSE,
               BFD_RELOC_GPREL32);
 
@@ -12879,8 +11986,7 @@ s_gpword (ignore)
 }
 
 static void
-s_gpdword (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+s_gpdword (int ignore ATTRIBUTE_UNUSED)
 {
   symbolS *label;
   expressionS ex;
@@ -12894,7 +12000,7 @@ s_gpdword (ignore)
     }
 
   label = insn_labels != NULL ? insn_labels->label : NULL;
-  mips_emit_delays (TRUE);
+  mips_emit_delays ();
   if (auto_align)
     mips_align (3, 0, label);
   mips_clear_insn_labels ();
@@ -12908,16 +12014,13 @@ s_gpdword (ignore)
     }
 
   p = frag_more (8);
-  md_number_to_chars (p, (valueT) 0, 8);
+  md_number_to_chars (p, 0, 8);
   fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, FALSE,
-              BFD_RELOC_GPREL32);
+              BFD_RELOC_GPREL32)->fx_tcbit = 1;
 
   /* GPREL32 composed with 64 gives a 64-bit GP offset.  */
-  ex.X_op = O_absent;
-  ex.X_add_symbol = 0;
-  ex.X_add_number = 0;
-  fix_new_exp (frag_now, p - frag_now->fr_literal, 8, &ex, FALSE,
-              BFD_RELOC_64);
+  fix_new (frag_now, p - frag_now->fr_literal, 8, NULL, 0,
+          FALSE, BFD_RELOC_64)->fx_tcbit = 1;
 
   demand_empty_rest_of_line ();
 }
@@ -12926,10 +12029,8 @@ s_gpdword (ignore)
    tables in SVR4 PIC code.  */
 
 static void
-s_cpadd (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+s_cpadd (int ignore ATTRIBUTE_UNUSED)
 {
-  int icnt = 0;
   int reg;
 
   /* This is ignored when not generating SVR4 PIC code.  */
@@ -12940,10 +12041,10 @@ s_cpadd (ignore)
     }
 
   /* Add $gp to the register named as an argument.  */
+  macro_start ();
   reg = tc_get_register (0);
-  macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-              HAVE_32BIT_ADDRESSES ? HAVE_NEWABI ? "add" : "addu" : "daddu",
-              "d,v,t", reg, reg, mips_gp_register);
+  macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", reg, reg, mips_gp_register);
+  macro_end ();
 
   demand_empty_rest_of_line ();
 }
@@ -12958,8 +12059,7 @@ s_cpadd (ignore)
    again.  */
 
 static void
-s_insn (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+s_insn (int ignore ATTRIBUTE_UNUSED)
 {
   mips16_mark_labels ();
 
@@ -12973,8 +12073,7 @@ s_insn (ignore)
    really a mips16 text label.  */
 
 static void
-s_mips_stab (type)
-     int type;
+s_mips_stab (int type)
 {
   if (type == 'n')
     mips16_mark_labels ();
@@ -12986,8 +12085,7 @@ s_mips_stab (type)
  */
 
 static void
-s_mips_weakext (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+s_mips_weakext (int ignore ATTRIBUTE_UNUSED)
 {
   char *name;
   int c;
@@ -13036,8 +12134,7 @@ s_mips_weakext (ignore)
    register, so that we can record it in mips_frame_reg.  */
 
 int
-tc_get_register (frame)
-     int frame;
+tc_get_register (int frame)
 {
   int reg;
 
@@ -13116,9 +12213,7 @@ tc_get_register (frame)
 }
 
 valueT
-md_section_align (seg, addr)
-     asection *seg;
-     valueT addr;
+md_section_align (asection *seg, valueT addr)
 {
   int align = bfd_get_section_alignment (stdoutput, seg);
 
@@ -13142,14 +12237,12 @@ md_section_align (seg, addr)
    undefined earlier.)  */
 
 static int
-nopic_need_relax (sym, before_relaxing)
-     symbolS *sym;
-     int before_relaxing;
+nopic_need_relax (symbolS *sym, int before_relaxing)
 {
   if (sym == 0)
     return 0;
 
-  if (USE_GLOBAL_POINTER_OPT && g_switch_value > 0)
+  if (g_switch_value > 0)
     {
       const char *symname;
       int change;
@@ -13212,9 +12305,7 @@ nopic_need_relax (sym, before_relaxing)
 /* Return true if the given symbol should be considered local for SVR4 PIC.  */
 
 static bfd_boolean
-pic_need_relax (sym, segtype)
-     symbolS *sym;
-     asection *segtype;
+pic_need_relax (symbolS *sym, asection *segtype)
 {
   asection *symsec;
   bfd_boolean linkonce;
@@ -13258,9 +12349,7 @@ pic_need_relax (sym, segtype)
 #ifdef OBJ_ELF
          /* A global or weak symbol is treated as external.  */
          && (OUTPUT_FLAVOR != bfd_target_elf_flavour
-             || (! S_IS_WEAK (sym)
-                 && (! S_IS_EXTERNAL (sym)
-                     || mips_pic == EMBEDDED_PIC)))
+             || (! S_IS_WEAK (sym) && ! S_IS_EXTERNAL (sym)))
 #endif
          );
 }
@@ -13270,10 +12359,7 @@ pic_need_relax (sym, segtype)
    extended opcode.  SEC is the section the frag is in.  */
 
 static int
-mips16_extended_frag (fragp, sec, stretch)
-     fragS *fragp;
-     asection *sec;
-     long stretch;
+mips16_extended_frag (fragS *fragp, asection *sec, long stretch)
 {
   int type;
   register const struct mips16_immed_operand *op;
@@ -13473,10 +12559,7 @@ mips16_extended_frag (fragp, sec, stretch)
    whether an unconditional (-1), branch-likely (+1) or regular (0)
    branch is to be computed.  */
 static int
-relaxed_branch_length (fragp, sec, update)
-     fragS *fragp;
-     asection *sec;
-     int update;
+relaxed_branch_length (fragS *fragp, asection *sec, int update)
 {
   bfd_boolean toofar;
   int length;
@@ -13540,9 +12623,7 @@ relaxed_branch_length (fragp, sec, update)
    decide whether we are using an extended opcode or not.  */
 
 int
-md_estimate_size_before_relax (fragp, segtype)
-     fragS *fragp;
-     asection *segtype;
+md_estimate_size_before_relax (fragS *fragp, asection *segtype)
 {
   int change;
 
@@ -13568,35 +12649,21 @@ md_estimate_size_before_relax (fragp, segtype)
 
   if (change)
     {
-      /* Record the offset to the first reloc in the fr_opcode field.
-        This lets md_convert_frag and tc_gen_reloc know that the code
-        must be expanded.  */
-      fragp->fr_opcode = (fragp->fr_literal
-                         + fragp->fr_fix
-                         - RELAX_OLD (fragp->fr_subtype)
-                         + RELAX_RELOC1 (fragp->fr_subtype));
-      /* FIXME: This really needs as_warn_where.  */
-      if (RELAX_WARN (fragp->fr_subtype))
-       as_warn (_("AT used after \".set noat\" or macro used after "
-                  "\".set nomacro\""));
-
-      return RELAX_NEW (fragp->fr_subtype) - RELAX_OLD (fragp->fr_subtype);
+      fragp->fr_subtype |= RELAX_USE_SECOND;
+      return -RELAX_FIRST (fragp->fr_subtype);
     }
-
-  return 0;
+  else
+    return -RELAX_SECOND (fragp->fr_subtype);
 }
 
 /* This is called to see whether a reloc against a defined symbol
-   should be converted into a reloc against a section.  Don't adjust
-   MIPS16 jump relocations, so we don't have to worry about the format
-   of the offset in the .o file.  Don't adjust relocations against
-   mips16 symbols, so that the linker can find them if it needs to set
-   up a stub.  */
+   should be converted into a reloc against a section.  */
 
 int
-mips_fix_adjustable (fixp)
-     fixS *fixp;
+mips_fix_adjustable (fixS *fixp)
 {
+  /* Don't adjust MIPS16 jump relocations, so we don't have to worry
+     about the format of the offset in the .o file. */
   if (fixp->fx_r_type == BFD_RELOC_MIPS16_JMP)
     return 0;
 
@@ -13607,7 +12674,28 @@ mips_fix_adjustable (fixp)
   if (fixp->fx_addsy == NULL)
     return 1;
 
+  /* If symbol SYM is in a mergeable section, relocations of the form
+     SYM + 0 can usually be made section-relative.  The mergeable data
+     is then identified by the section offset rather than by the symbol.
+
+     However, if we're generating REL LO16 relocations, the offset is split
+     between the LO16 and parterning high part relocation.  The linker will
+     need to recalculate the complete offset in order to correctly identify
+     the merge data.
+
+     The linker has traditionally not looked for the parterning high part
+     relocation, and has thus allowed orphaned R_MIPS_LO16 relocations to be
+     placed anywhere.  Rather than break backwards compatibility by changing
+     this, it seems better not to force the issue, and instead keep the
+     original symbol.  This will work with either linker behavior.  */
+  if ((fixp->fx_r_type == BFD_RELOC_LO16 || reloc_needs_lo_p (fixp->fx_r_type))
+      && HAVE_IN_PLACE_ADDENDS
+      && (S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_MERGE) != 0)
+    return 0;
+
 #ifdef OBJ_ELF
+  /* Don't adjust relocations against mips16 symbols, so that the linker
+     can find them if it needs to set up a stub.  */
   if (OUTPUT_FLAVOR == bfd_target_elf_flavour
       && S_GET_OTHER (fixp->fx_addsy) == STO_MIPS16
       && fixp->fx_subsy == NULL)
@@ -13621,9 +12709,7 @@ mips_fix_adjustable (fixp)
    format.  */
 
 arelent **
-tc_gen_reloc (section, fixp)
-     asection *section ATTRIBUTE_UNUSED;
-     fixS *fixp;
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
 {
   static arelent *retval[4];
   arelent *reloc;
@@ -13635,170 +12721,8 @@ tc_gen_reloc (section, fixp)
   *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
   reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
 
-  if (mips_pic == EMBEDDED_PIC
-      && SWITCH_TABLE (fixp))
-    {
-      /* For a switch table entry we use a special reloc.  The addend
-        is actually the difference between the reloc address and the
-        subtrahend.  */
-      reloc->addend = reloc->address - S_GET_VALUE (fixp->fx_subsy);
-      if (OUTPUT_FLAVOR != bfd_target_ecoff_flavour)
-       as_fatal (_("Double check fx_r_type in tc-mips.c:tc_gen_reloc"));
-      fixp->fx_r_type = BFD_RELOC_GPREL32;
-    }
-  else if (fixp->fx_r_type == BFD_RELOC_PCREL_LO16)
-    {
-      if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
-       reloc->addend = fixp->fx_addnumber;
-      else
-       {
-         /* We use a special addend for an internal RELLO reloc.  */
-         if (symbol_section_p (fixp->fx_addsy))
-           reloc->addend = reloc->address - S_GET_VALUE (fixp->fx_subsy);
-         else
-           reloc->addend = fixp->fx_addnumber + reloc->address;
-       }
-    }
-  else if (fixp->fx_r_type == BFD_RELOC_PCREL_HI16_S)
-    {
-      assert (fixp->fx_next != NULL
-             && fixp->fx_next->fx_r_type == BFD_RELOC_PCREL_LO16);
-
-      /* The reloc is relative to the RELLO; adjust the addend
-        accordingly.  */
-      if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
-       reloc->addend = fixp->fx_next->fx_addnumber;
-      else
-       {
-         /* We use a special addend for an internal RELHI reloc.  */
-         if (symbol_section_p (fixp->fx_addsy))
-           reloc->addend = (fixp->fx_next->fx_frag->fr_address
-                            + fixp->fx_next->fx_where
-                            - S_GET_VALUE (fixp->fx_subsy));
-         else
-           reloc->addend = (fixp->fx_addnumber
-                            + fixp->fx_next->fx_frag->fr_address
-                            + fixp->fx_next->fx_where);
-       }
-    }
-  else if (fixp->fx_pcrel == 0 || OUTPUT_FLAVOR == bfd_target_elf_flavour)
-    reloc->addend = fixp->fx_addnumber;
-  else
-    {
-      if (OUTPUT_FLAVOR != bfd_target_aout_flavour)
-       /* A gruesome hack which is a result of the gruesome gas reloc
-          handling.  */
-       reloc->addend = reloc->address;
-      else
-       reloc->addend = -reloc->address;
-    }
-
-  /* If this is a variant frag, we may need to adjust the existing
-     reloc and generate a new one.  */
-  if (fixp->fx_frag->fr_opcode != NULL
-      && ((fixp->fx_r_type == BFD_RELOC_GPREL16
-          && ! HAVE_NEWABI)
-         || (fixp->fx_r_type == BFD_RELOC_MIPS_GOT_DISP
-             && HAVE_NEWABI)
-         || fixp->fx_r_type == BFD_RELOC_MIPS_GOT16
-         || fixp->fx_r_type == BFD_RELOC_MIPS_CALL16
-         || fixp->fx_r_type == BFD_RELOC_MIPS_GOT_HI16
-         || fixp->fx_r_type == BFD_RELOC_MIPS_GOT_LO16
-         || fixp->fx_r_type == BFD_RELOC_MIPS_CALL_HI16
-         || fixp->fx_r_type == BFD_RELOC_MIPS_CALL_LO16)
-    )
-    {
-      arelent *reloc2;
-
-      assert (! RELAX_MIPS16_P (fixp->fx_frag->fr_subtype));
-
-      /* If this is not the last reloc in this frag, then we have two
-        GPREL relocs, or a GOT_HI16/GOT_LO16 pair, or a
-        CALL_HI16/CALL_LO16, both of which are being replaced.  Let
-        the second one handle all of them.  */
-      if (fixp->fx_next != NULL
-         && fixp->fx_frag == fixp->fx_next->fx_frag)
-       {
-         assert ((fixp->fx_r_type == BFD_RELOC_GPREL16
-                  && fixp->fx_next->fx_r_type == BFD_RELOC_GPREL16)
-                 || (fixp->fx_r_type == BFD_RELOC_MIPS_GOT_HI16
-                     && (fixp->fx_next->fx_r_type
-                         == BFD_RELOC_MIPS_GOT_LO16))
-                 || (fixp->fx_r_type == BFD_RELOC_MIPS_CALL_HI16
-                     && (fixp->fx_next->fx_r_type
-                         == BFD_RELOC_MIPS_CALL_LO16)));
-         retval[0] = NULL;
-         return retval;
-       }
-
-      fixp->fx_where = fixp->fx_frag->fr_opcode - fixp->fx_frag->fr_literal;
-      reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
-      reloc->addend += fixp->fx_frag->tc_frag_data.tc_fr_offset;
-      reloc2 = retval[1] = (arelent *) xmalloc (sizeof (arelent));
-      reloc2->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
-      *reloc2->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
-      reloc2->address = (reloc->address
-                        + (RELAX_RELOC2 (fixp->fx_frag->fr_subtype)
-                           - RELAX_RELOC1 (fixp->fx_frag->fr_subtype)));
-      reloc2->addend = fixp->fx_addnumber - S_GET_VALUE (fixp->fx_addsy)
-       + fixp->fx_frag->tc_frag_data.tc_fr_offset;
-      reloc2->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_LO16);
-      assert (reloc2->howto != NULL);
-
-      if (RELAX_RELOC3 (fixp->fx_frag->fr_subtype))
-       {
-         arelent *reloc3;
-
-         reloc3 = retval[2] = (arelent *) xmalloc (sizeof (arelent));
-         *reloc3 = *reloc2;
-         reloc3->address += 4;
-       }
-
-      if (mips_pic == NO_PIC)
-       {
-         assert (fixp->fx_r_type == BFD_RELOC_GPREL16);
-         fixp->fx_r_type = BFD_RELOC_HI16_S;
-       }
-      else if (mips_pic == SVR4_PIC)
-       {
-         switch (fixp->fx_r_type)
-           {
-           default:
-             abort ();
-           case BFD_RELOC_MIPS_GOT16:
-             break;
-           case BFD_RELOC_MIPS_GOT_LO16:
-           case BFD_RELOC_MIPS_CALL_LO16:
-             if (HAVE_NEWABI)
-               {
-                 fixp->fx_r_type = BFD_RELOC_MIPS_GOT_PAGE;
-                 reloc2->howto = bfd_reloc_type_lookup
-                   (stdoutput, BFD_RELOC_MIPS_GOT_OFST);
-               }
-             else
-               fixp->fx_r_type = BFD_RELOC_MIPS_GOT16;
-             break;
-           case BFD_RELOC_MIPS_CALL16:
-           case BFD_RELOC_MIPS_GOT_OFST:
-           case BFD_RELOC_MIPS_GOT_DISP:
-             if (HAVE_NEWABI)
-               {
-                 /* It may seem nonsensical to relax GOT_DISP to
-                    GOT_DISP, but we're actually turning a GOT_DISP
-                    without offset into a GOT_DISP with an offset,
-                    getting rid of the separate addition, which we can
-                    do when the symbol is found to be local.  */
-                 fixp->fx_r_type = BFD_RELOC_MIPS_GOT_DISP;
-                 retval[1] = NULL;
-               }
-             else
-               fixp->fx_r_type = BFD_RELOC_MIPS_GOT16;
-             break;
-           }
-       }
-      else
-       abort ();
-    }
+  assert (! fixp->fx_pcrel);
+  reloc->addend = fixp->fx_addnumber;
 
   /* Since the old MIPS ELF ABI uses Rel instead of Rela, encode the vtable
      entry to be used in the relocation's section offset.  */
@@ -13808,49 +12732,16 @@ tc_gen_reloc (section, fixp)
       reloc->addend = 0;
     }
 
-  /* Since DIFF_EXPR_OK is defined in tc-mips.h, it is possible that
-     fixup_segment converted a non-PC relative reloc into a PC
-     relative reloc.  In such a case, we need to convert the reloc
-     code.  */
   code = fixp->fx_r_type;
-  if (fixp->fx_pcrel)
-    {
-      switch (code)
-       {
-       case BFD_RELOC_8:
-         code = BFD_RELOC_8_PCREL;
-         break;
-       case BFD_RELOC_16:
-         code = BFD_RELOC_16_PCREL;
-         break;
-       case BFD_RELOC_32:
-         code = BFD_RELOC_32_PCREL;
-         break;
-       case BFD_RELOC_64:
-         code = BFD_RELOC_64_PCREL;
-         break;
-       case BFD_RELOC_8_PCREL:
-       case BFD_RELOC_16_PCREL:
-       case BFD_RELOC_32_PCREL:
-       case BFD_RELOC_64_PCREL:
-       case BFD_RELOC_16_PCREL_S2:
-       case BFD_RELOC_PCREL_HI16_S:
-       case BFD_RELOC_PCREL_LO16:
-         break;
-       default:
-         as_bad_where (fixp->fx_file, fixp->fx_line,
-                       _("Cannot make %s relocation PC relative"),
-                       bfd_get_reloc_code_name (code));
-       }
-    }
 
-  /* To support a PC relative reloc when generating embedded PIC code
-     for ECOFF, we use a Cygnus extension.  We check for that here to
-     make sure that we don't let such a reloc escape normally.  */
+  /* To support a PC relative reloc, we used a Cygnus extension.
+     We check for that here to make sure that we don't let such a
+     reloc escape normally.  (FIXME: This was formerly used by
+     embedded-PIC support, but is now used by branch handling in
+     general.  That probably should be fixed.)  */
   if ((OUTPUT_FLAVOR == bfd_target_ecoff_flavour
        || OUTPUT_FLAVOR == bfd_target_elf_flavour)
-      && code == BFD_RELOC_16_PCREL_S2
-      && mips_pic != EMBEDDED_PIC)
+      && code == BFD_RELOC_16_PCREL_S2)
     reloc->howto = NULL;
   else
     reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
@@ -13870,10 +12761,7 @@ tc_gen_reloc (section, fixp)
    the current size of the frag should change.  */
 
 int
-mips_relax_frag (sec, fragp, stretch)
-     asection *sec;
-     fragS *fragp;
-     long stretch;
+mips_relax_frag (asection *sec, fragS *fragp, long stretch)
 {
   if (RELAX_BRANCH_P (fragp->fr_subtype))
     {
@@ -13908,14 +12796,8 @@ mips_relax_frag (sec, fragp, stretch)
 /* Convert a machine dependent frag.  */
 
 void
-md_convert_frag (abfd, asec, fragp)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     segT asec;
-     fragS *fragp;
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
 {
-  int old, new;
-  char *fixptr;
-
   if (RELAX_BRANCH_P (fragp->fr_subtype))
     {
       bfd_byte *buf;
@@ -13945,7 +12827,7 @@ md_convert_frag (abfd, asec, fragp)
          fixp->fx_file = fragp->fr_file;
          fixp->fx_line = fragp->fr_line;
 
-         md_number_to_chars ((char *)buf, insn, 4);
+         md_number_to_chars ((char *) buf, insn, 4);
          buf += 4;
        }
       else
@@ -14017,11 +12899,11 @@ md_convert_frag (abfd, asec, fragp)
          i--;
          insn |= i;
          /* Branch over the jump.  */
-         md_number_to_chars ((char *)buf, insn, 4);
+         md_number_to_chars ((char *) buf, insn, 4);
          buf += 4;
 
          /* Nop */
-         md_number_to_chars ((char*)buf, 0, 4);
+         md_number_to_chars ((char *) buf, 0, 4);
          buf += 4;
 
          if (RELAX_BRANCH_LIKELY (fragp->fr_subtype))
@@ -14040,10 +12922,10 @@ md_convert_frag (abfd, asec, fragp)
                 delay slot.  */
 
              insn |= i;
-             md_number_to_chars ((char *)buf, insn, 4);
+             md_number_to_chars ((char *) buf, insn, 4);
              buf += 4;
 
-             md_number_to_chars ((char *)buf, 0, 4);
+             md_number_to_chars ((char *) buf, 0, 4);
              buf += 4;
            }
 
@@ -14062,7 +12944,7 @@ md_convert_frag (abfd, asec, fragp)
              fixp->fx_file = fragp->fr_file;
              fixp->fx_line = fragp->fr_line;
 
-             md_number_to_chars ((char*)buf, insn, 4);
+             md_number_to_chars ((char *) buf, insn, 4);
              buf += 4;
            }
          else
@@ -14084,13 +12966,13 @@ md_convert_frag (abfd, asec, fragp)
              fixp->fx_file = fragp->fr_file;
              fixp->fx_line = fragp->fr_line;
 
-             md_number_to_chars ((char*)buf, insn, 4);
+             md_number_to_chars ((char *) buf, insn, 4);
              buf += 4;
 
              if (mips_opts.isa == ISA_MIPS1)
                {
                  /* nop */
-                 md_number_to_chars ((char*)buf, 0, 4);
+                 md_number_to_chars ((char *) buf, 0, 4);
                  buf += 4;
                }
 
@@ -14102,7 +12984,7 @@ md_convert_frag (abfd, asec, fragp)
              fixp->fx_file = fragp->fr_file;
              fixp->fx_line = fragp->fr_line;
 
-             md_number_to_chars ((char*)buf, insn, 4);
+             md_number_to_chars ((char *) buf, insn, 4);
              buf += 4;
 
              /* j(al)r $at.  */
@@ -14111,7 +12993,7 @@ md_convert_frag (abfd, asec, fragp)
              else
                insn = 0x00200008;
 
-             md_number_to_chars ((char*)buf, insn, 4);
+             md_number_to_chars ((char *) buf, insn, 4);
              buf += 4;
            }
        }
@@ -14215,17 +13097,57 @@ md_convert_frag (abfd, asec, fragp)
     }
   else
     {
-      if (fragp->fr_opcode == NULL)
-       return;
+      int first, second;
+      fixS *fixp;
+
+      first = RELAX_FIRST (fragp->fr_subtype);
+      second = RELAX_SECOND (fragp->fr_subtype);
+      fixp = (fixS *) fragp->fr_opcode;
+
+      /* 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))
+       {
+         const char *msg = macro_warning (fragp->fr_subtype);
+         if (msg != 0)
+           as_warn_where (fragp->fr_file, fragp->fr_line, msg);
+       }
+
+      /* Go through all the fixups for the first sequence.  Disable them
+        (by marking them as done) if we're going to use the second
+        sequence instead.  */
+      while (fixp
+            && fixp->fx_frag == fragp
+            && fixp->fx_where < fragp->fr_fix - second)
+       {
+         if (fragp->fr_subtype & RELAX_USE_SECOND)
+           fixp->fx_done = 1;
+         fixp = fixp->fx_next;
+       }
 
-      old = RELAX_OLD (fragp->fr_subtype);
-      new = RELAX_NEW (fragp->fr_subtype);
-      fixptr = fragp->fr_literal + fragp->fr_fix;
+      /* Go through the fixups for the second sequence.  Disable them if
+        we're going to use the first sequence, otherwise adjust their
+        addresses to account for the relaxation.  */
+      while (fixp && fixp->fx_frag == fragp)
+       {
+         if (fragp->fr_subtype & RELAX_USE_SECOND)
+           fixp->fx_where -= first;
+         else
+           fixp->fx_done = 1;
+         fixp = fixp->fx_next;
+       }
 
-      if (new > 0)
-       memmove (fixptr - old, fixptr, new);
+      /* Now modify the frag contents.  */
+      if (fragp->fr_subtype & RELAX_USE_SECOND)
+       {
+         char *start;
 
-      fragp->fr_fix += new - old;
+         start = fragp->fr_literal + fragp->fr_fix - first - second;
+         memmove (start, start + first, second);
+         fragp->fr_fix -= first;
+       }
+      else
+       fragp->fr_fix -= second;
     }
 }
 
@@ -14236,7 +13158,7 @@ md_convert_frag (abfd, asec, fragp)
    back to even for the convenience of the debugger.  */
 
 void
-mips_frob_file_after_relocs ()
+mips_frob_file_after_relocs (void)
 {
   asymbol **syms;
   unsigned int count, i;
@@ -14267,8 +13189,7 @@ mips_frob_file_after_relocs ()
    can not move it.  */
 
 void
-mips_define_label (sym)
-     symbolS *sym;
+mips_define_label (symbolS *sym)
 {
   struct insn_label_list *l;
 
@@ -14290,7 +13211,7 @@ mips_define_label (sym)
 /* Some special processing for a MIPS ELF file.  */
 
 void
-mips_elf_final_processing ()
+mips_elf_final_processing (void)
 {
   /* Write out the register information.  */
   if (mips_abi != N64_ABI)
@@ -14371,7 +13292,8 @@ mips_elf_final_processing ()
 #endif /* OBJ_ELF || OBJ_MAYBE_ELF */
 \f
 typedef struct proc {
-  symbolS *isym;
+  symbolS *func_sym;
+  symbolS *func_end_sym;
   unsigned long reg_mask;
   unsigned long reg_offset;
   unsigned long fpreg_mask;
@@ -14388,8 +13310,7 @@ static int numprocs;
 /* Fill in an rs_align_code fragment.  */
 
 void
-mips_handle_align (fragp)
-     fragS *fragp;
+mips_handle_align (fragS *fragp)
 {
   if (fragp->fr_type != rs_align_code)
     return;
@@ -14419,12 +13340,12 @@ mips_handle_align (fragp)
 }
 
 static void
-md_obj_begin ()
+md_obj_begin (void)
 {
 }
 
 static void
-md_obj_end ()
+md_obj_end (void)
 {
   /* check for premature end, nesting errors, etc */
   if (cur_proc_ptr)
@@ -14432,7 +13353,7 @@ md_obj_end ()
 }
 
 static long
-get_number ()
+get_number (void)
 {
   int negative = 0;
   long val = 0;
@@ -14487,8 +13408,7 @@ get_number ()
    case .file implies DWARF-2.  */
 
 static void
-s_mips_file (x)
-     int x ATTRIBUTE_UNUSED;
+s_mips_file (int x ATTRIBUTE_UNUSED)
 {
   static int first_file_directive = 0;
 
@@ -14510,7 +13430,7 @@ s_mips_file (x)
       if (filename != NULL && ! first_file_directive)
        {
          (void) new_logical_line (filename, -1);
-         s_app_file_string (filename);
+         s_app_file_string (filename, 0);
        }
       first_file_directive = 1;
     }
@@ -14519,8 +13439,7 @@ s_mips_file (x)
 /* The .loc directive, implying DWARF-2.  */
 
 static void
-s_mips_loc (x)
-     int x ATTRIBUTE_UNUSED;
+s_mips_loc (int x ATTRIBUTE_UNUSED)
 {
   if (!ECOFF_DEBUGGING)
     dwarf2_directive_loc (0);
@@ -14529,8 +13448,7 @@ s_mips_loc (x)
 /* The .end directive.  */
 
 static void
-s_mips_end (x)
-     int x ATTRIBUTE_UNUSED;
+s_mips_end (int x ATTRIBUTE_UNUSED)
 {
   symbolS *p;
 
@@ -14559,7 +13477,7 @@ s_mips_end (x)
   if (p != NULL)
     {
       assert (S_GET_NAME (p));
-      if (strcmp (S_GET_NAME (p), S_GET_NAME (cur_proc_ptr->isym)))
+      if (strcmp (S_GET_NAME (p), S_GET_NAME (cur_proc_ptr->func_sym)))
        as_warn (_(".end symbol does not match .ent symbol."));
 
       if (debug_type == DEBUG_STABS)
@@ -14570,8 +13488,24 @@ s_mips_end (x)
     as_warn (_(".end directive missing or unknown symbol"));
 
 #ifdef OBJ_ELF
+  /* Create an expression to calculate the size of the function.  */
+  if (p && cur_proc_ptr)
+    {
+      OBJ_SYMFIELD_TYPE *obj = symbol_get_obj (p);
+      expressionS *exp = xmalloc (sizeof (expressionS));
+
+      obj->size = exp;
+      exp->X_op = O_subtract;
+      exp->X_add_symbol = symbol_temp_new_now ();
+      exp->X_op_symbol = p;
+      exp->X_add_number = 0;
+
+      cur_proc_ptr->func_end_sym = exp->X_add_symbol;
+    }
+
   /* Generate a .pdr section.  */
-  if (OUTPUT_FLAVOR == bfd_target_elf_flavour && ! ECOFF_DEBUGGING)
+  if (OUTPUT_FLAVOR == bfd_target_elf_flavour && ! ECOFF_DEBUGGING
+      && mips_flag_pdr)
     {
       segT saved_seg = now_seg;
       subsegT saved_subseg = now_subseg;
@@ -14596,13 +13530,13 @@ s_mips_end (x)
 
       fragp = frag_more (7 * 4);
 
-      md_number_to_chars (fragp,      (valueT) cur_proc_ptr->reg_mask, 4);
-      md_number_to_chars (fragp +  4, (valueT) cur_proc_ptr->reg_offset, 4);
-      md_number_to_chars (fragp +  8, (valueT) cur_proc_ptr->fpreg_mask, 4);
-      md_number_to_chars (fragp + 12, (valueT) cur_proc_ptr->fpreg_offset, 4);
-      md_number_to_chars (fragp + 16, (valueT) cur_proc_ptr->frame_offset, 4);
-      md_number_to_chars (fragp + 20, (valueT) cur_proc_ptr->frame_reg, 4);
-      md_number_to_chars (fragp + 24, (valueT) cur_proc_ptr->pc_reg, 4);
+      md_number_to_chars (fragp, cur_proc_ptr->reg_mask, 4);
+      md_number_to_chars (fragp + 4, cur_proc_ptr->reg_offset, 4);
+      md_number_to_chars (fragp + 8, cur_proc_ptr->fpreg_mask, 4);
+      md_number_to_chars (fragp + 12, cur_proc_ptr->fpreg_offset, 4);
+      md_number_to_chars (fragp + 16, cur_proc_ptr->frame_offset, 4);
+      md_number_to_chars (fragp + 20, cur_proc_ptr->frame_reg, 4);
+      md_number_to_chars (fragp + 24, cur_proc_ptr->pc_reg, 4);
 
       subseg_set (saved_seg, saved_subseg);
     }
@@ -14614,8 +13548,7 @@ s_mips_end (x)
 /* The .aent and .ent directives.  */
 
 static void
-s_mips_ent (aent)
-     int aent;
+s_mips_ent (int aent)
 {
   symbolS *symbolP;
 
@@ -14642,7 +13575,7 @@ s_mips_ent (aent)
       cur_proc_ptr = &cur_proc;
       memset (cur_proc_ptr, '\0', sizeof (procS));
 
-      cur_proc_ptr->isym = symbolP;
+      cur_proc_ptr->func_sym = symbolP;
 
       symbol_get_bfdsym (symbolP)->flags |= BSF_FUNCTION;
 
@@ -14663,8 +13596,7 @@ s_mips_ent (aent)
    symbol table (in the mdebug section).  */
 
 static void
-s_mips_frame (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+s_mips_frame (int ignore ATTRIBUTE_UNUSED)
 {
 #ifdef OBJ_ELF
   if (OUTPUT_FLAVOR == bfd_target_elf_flavour && ! ECOFF_DEBUGGING)
@@ -14707,8 +13639,7 @@ s_mips_frame (ignore)
    make reference to the ecoff symbol table (in the mdebug section).  */
 
 static void
-s_mips_mask (reg_type)
-     char reg_type;
+s_mips_mask (int reg_type)
 {
 #ifdef OBJ_ELF
   if (OUTPUT_FLAVOR == bfd_target_elf_flavour && ! ECOFF_DEBUGGING)
@@ -14750,30 +13681,6 @@ s_mips_mask (reg_type)
     s_ignore (reg_type);
 }
 
-/* The .loc directive.  */
-
-#if 0
-static void
-s_loc (x)
-     int x;
-{
-  symbolS *symbolP;
-  int lineno;
-  int addroff;
-
-  assert (now_seg == text_section);
-
-  lineno = get_number ();
-  addroff = frag_now_fix ();
-
-  symbolP = symbol_new ("", N_SLINE, addroff, frag_now);
-  S_SET_TYPE (symbolP, N_SLINE);
-  S_SET_OTHER (symbolP, 0);
-  S_SET_DESC (symbolP, lineno);
-  symbolP->sy_segment = now_seg;
-}
-#endif
-
 /* A table describing all the processors gas knows about.  Names are
    matched in the order listed.
 
@@ -14790,6 +13697,7 @@ static const struct mips_cpu_info mips_cpu_info_table[] =
   { "mips32",         1,      ISA_MIPS32,     CPU_MIPS32 },
   { "mips32r2",       1,      ISA_MIPS32R2,   CPU_MIPS32R2 },
   { "mips64",         1,      ISA_MIPS64,     CPU_MIPS64 },
+  { "mips64r2",       1,      ISA_MIPS64R2,   CPU_MIPS64R2 },
 
   /* MIPS I */
   { "r3000",          0,      ISA_MIPS1,      CPU_R3000 },
@@ -14825,10 +13733,11 @@ static const struct mips_cpu_info mips_cpu_info_table[] =
   { "rm5231",         0,      ISA_MIPS4,      CPU_R5000 },
   { "rm5261",         0,      ISA_MIPS4,      CPU_R5000 },
   { "rm5721",         0,      ISA_MIPS4,      CPU_R5000 },
-  { "r7000",          0,      ISA_MIPS4,      CPU_R5000 },
+  { "rm7000",         0,      ISA_MIPS4,      CPU_RM7000 },
+  { "rm9000",         0,      ISA_MIPS4,      CPU_RM9000 },
 
   /* MIPS 32 */
-  { "4kc",            0,      ISA_MIPS32,     CPU_MIPS32, },
+  { "4kc",            0,      ISA_MIPS32,     CPU_MIPS32 },
   { "4km",            0,      ISA_MIPS32,     CPU_MIPS32 },
   { "4kp",            0,      ISA_MIPS32,     CPU_MIPS32 },
 
@@ -14850,8 +13759,7 @@ static const struct mips_cpu_info mips_cpu_info_table[] =
    Note: this function is shared between GCC and GAS.  */
 
 static bfd_boolean
-mips_strict_matching_cpu_name_p (canonical, given)
-     const char *canonical, *given;
+mips_strict_matching_cpu_name_p (const char *canonical, const char *given)
 {
   while (*given != 0 && TOLOWER (*given) == TOLOWER (*canonical))
     given++, canonical++;
@@ -14867,8 +13775,7 @@ mips_strict_matching_cpu_name_p (canonical, given)
    Note: this function is shared between GCC and GAS.  */
 
 static bfd_boolean
-mips_matching_cpu_name_p (canonical, given)
-     const char *canonical, *given;
+mips_matching_cpu_name_p (const char *canonical, const char *given)
 {
   /* First see if the name matches exactly, or with a final "000"
      turned into "k".  */
@@ -14903,8 +13810,7 @@ mips_matching_cpu_name_p (canonical, given)
    A similar function exists in GCC.  */
 
 static const struct mips_cpu_info *
-mips_parse_cpu (option, cpu_string)
-     const char *option, *cpu_string;
+mips_parse_cpu (const char *option, const char *cpu_string)
 {
   const struct mips_cpu_info *p;
 
@@ -14950,8 +13856,7 @@ mips_parse_cpu (option, cpu_string)
    ISA_MIPS* enumeration).  */
 
 static const struct mips_cpu_info *
-mips_cpu_info_from_isa (isa)
-     int isa;
+mips_cpu_info_from_isa (int isa)
 {
   int i;
 
@@ -14962,13 +13867,21 @@ mips_cpu_info_from_isa (isa)
 
   return NULL;
 }
+
+static const struct mips_cpu_info *
+mips_cpu_info_from_arch (int arch)
+{
+  int i;
+
+  for (i = 0; mips_cpu_info_table[i].name != NULL; i++)
+    if (arch == mips_cpu_info_table[i].cpu)
+      return (&mips_cpu_info_table[i]);
+
+  return NULL;
+}
 \f
 static void
-show (stream, string, col_p, first_p)
-     FILE *stream;
-     const char *string;
-     int *col_p;
-     int *first_p;
+show (FILE *stream, const char *string, int *col_p, int *first_p)
 {
   if (*first_p)
     {
@@ -14994,15 +13907,13 @@ show (stream, string, col_p, first_p)
 }
 
 void
-md_show_usage (stream)
-     FILE *stream;
+md_show_usage (FILE *stream)
 {
   int column, first;
   size_t i;
 
   fprintf (stream, _("\
 MIPS options:\n\
--membedded-pic         generate embedded position independent code\n\
 -EB                    generate big endian output\n\
 -EL                    generate little endian output\n\
 -g, -g2                        do not remove unneeded NOPs or swap branches\n\
@@ -15017,6 +13928,7 @@ MIPS options:\n\
 -mips32                 generate MIPS32 ISA instructions\n\
 -mips32r2               generate MIPS32 release 2 ISA instructions\n\
 -mips64                 generate MIPS64 ISA instructions\n\
+-mips64r2               generate MIPS64 release 2 ISA instructions\n\
 -march=CPU/-mtune=CPU  generate code/schedule for CPU, where CPU is one of:\n"));
 
   first = 1;
@@ -15043,11 +13955,14 @@ MIPS options:\n\
 -mips16                        generate mips16 instructions\n\
 -no-mips16             do not generate mips16 instructions\n"));
   fprintf (stream, _("\
+-mfix-vr4120           work around certain VR4120 errata\n\
+-mfix-vr4130           work around VR4130 mflo/mfhi errata\n\
 -mgp32                 use 32-bit GPRs, regardless of the chosen ISA\n\
 -mfp32                 use 32-bit FPRs, regardless of the chosen ISA\n\
+-mno-shared            optimize output for executables\n\
+-msym32                        assume all symbols have 32-bit values\n\
 -O0                    remove unneeded NOPs, do not swap branches\n\
 -O                     remove unneeded NOPs and swap branches\n\
--n                     warn about NOPs generated from macros\n\
 --[no-]construct-floats [dis]allow floating point values to be constructed\n\
 --trap, --no-break     trap exception on div by 0 and mult overflow\n\
 --break, --no-trap     break exception on div by 0 and mult overflow\n"));
@@ -15056,6 +13971,9 @@ MIPS options:\n\
 -KPIC, -call_shared    generate SVR4 position independent code\n\
 -non_shared            do not generate position independent code\n\
 -xgot                  assume a 32 bit GOT\n\
+-mpdr, -mno-pdr                enable/disable creation of .pdr sections\n\
+-mshared, -mno-shared   disable/enable .cpload optimization for\n\
+                        non-shared code\n\
 -mabi=ABI              create ABI conformant object file for:\n"));
 
   first = 1;
@@ -15076,7 +13994,7 @@ MIPS options:\n\
 }
 
 enum dwarf2_format
-mips_dwarf2_format ()
+mips_dwarf2_format (void)
 {
   if (mips_abi == N64_ABI)
     {
@@ -15089,3 +14007,12 @@ mips_dwarf2_format ()
   else
     return dwarf2_format_32bit;
 }
+
+int
+mips_dwarf2_addr_size (void)
+{
+  if (mips_abi == N64_ABI)
+    return 8;
+  else
+    return 4;
+}
This page took 0.33754 seconds and 4 git commands to generate.