* config/tc-mips.c (ADDRESS_ADD_INSN,ADDRESS_ADDI_INSN): Remove
[deliverable/binutils-gdb.git] / gas / config / tc-mips.c
index 89a5931e89aa1d38d393553399f3f751e7736a33..8b89be84ae435737962e9455e589a1db7e334c4f 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-mips.c -- assemble code for a MIPS chip.
-   Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
+   Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
    Free Software Foundation, Inc.
    Contributed by the OSF and Ralph Campbell.
    Written by Keith Knowles and Ralph Campbell, working independently.
@@ -37,6 +37,7 @@
 
 #include "opcode/mips.h"
 #include "itbl-ops.h"
+#include "dwarf2dbg.h"
 
 #ifdef DEBUG
 #define DBG(x) printf x
@@ -76,12 +77,15 @@ static int mips_output_flavor () { return OUTPUT_FLAVOR; }
 #define ECOFF_DEBUGGING 0
 #endif
 
+int mips_flag_mdebug = -1;
+
 #include "ecoff.h"
 
 #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
 static char *mips_regmask_frag;
 #endif
 
+#define ZERO 0
 #define AT  1
 #define TREG 24
 #define PIC_CALL_REG 25
@@ -125,7 +129,10 @@ enum mips_abi_level
 };
 
 /* MIPS ABI we are using for this output file.  */
-static enum mips_abi_level file_mips_abi = NO_ABI;
+static enum mips_abi_level mips_abi = NO_ABI;
+
+/* Whether or not we have code that can call pic code.  */
+int mips_abicalls = FALSE;
 
 /* 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
@@ -141,6 +148,7 @@ struct mips_set_options
      if they have not been initialized.  Changed by `.set <asename>', by
      command line options, and based on the default architecture.  */
   int ase_mips3d;
+  int ase_mdmx;
   /* Whether we are assembling for the mips16 processor.  0 if we are
      not, 1 if we are, and -1 if the value has not been initialized.
      Changed by `.set mips16' and `.set nomips16', and the -mips16 and
@@ -172,9 +180,6 @@ struct mips_set_options
      is passed but can changed if the assembler code uses .set mipsN.  */
   int gp32;
   int fp32;
-  /* The ABI currently in use. This is changed by .set mipsN to loosen
-     restrictions and doesn't affect the whole file.  */
-  enum mips_abi_level abi;
 };
 
 /* True if -mgp32 was passed.  */
@@ -184,12 +189,12 @@ static int file_mips_gp32 = -1;
 static int file_mips_fp32 = -1;
 
 /* This is the struct we use to hold the current set of options.  Note
-   that we must set the isa field to ISA_UNKNOWN and the mips16 field to
+   that we must set the isa field to ISA_UNKNOWN and the ASE fields to
    -1 to indicate that they have not been initialized.  */
 
 static struct mips_set_options mips_opts =
 {
-  ISA_UNKNOWN, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, NO_ABI
+  ISA_UNKNOWN, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0
 };
 
 /* These variables are filled in with the masks of registers used.
@@ -201,25 +206,30 @@ unsigned long mips_cprmask[4];
 /* MIPS ISA we are using for this output file.  */
 static int file_mips_isa = ISA_UNKNOWN;
 
+/* True if -mips16 was passed or implied by arguments passed on the
+   command line (e.g., by -march).  */
+static int file_ase_mips16;
+
 /* True if -mips3d was passed or implied by arguments passed on the
    command line (e.g., by -march).  */
 static int file_ase_mips3d;
 
-/* The argument of the -mcpu= flag.  Historical for code generation.  */
-static int mips_cpu = CPU_UNKNOWN;
+/* True if -mdmx was passed or implied by arguments passed on the
+   command line (e.g., by -march).  */
+static int file_ase_mdmx;
 
 /* The argument of the -march= flag.  The architecture we are assembling.  */
 static int 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;
 
-/* Whether we should mark the file EABI64 or EABI32.  */
-static int mips_eabi64 = 0;
-
-/* If they asked for mips1 or mips2 and a cpu that is
-   mips3 or greater, then mark the object file 32BITMODE.  */
+/* 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
@@ -236,6 +246,15 @@ static int mips_32bitmode = 0;
    || (ISA) == ISA_MIPS3                    \
    )
 
+/* True if the given ABI requires 32-bit registers.  */
+#define ABI_NEEDS_32BIT_REGS(ABI) ((ABI) == O32_ABI)
+
+/* Likewise 64-bit registers.  */
+#define ABI_NEEDS_64BIT_REGS(ABI) \
+  ((ABI) == N32_ABI              \
+   || (ABI) == N64_ABI           \
+   || (ABI) == O64_ABI)
+
 /*  Return true if ISA supports 64 bit gp register instructions.  */
 #define ISA_HAS_64BIT_REGS(ISA) (    \
    (ISA) == ISA_MIPS3                \
@@ -244,22 +263,30 @@ static int mips_32bitmode = 0;
    || (ISA) == ISA_MIPS64            \
    )
 
+/* Return true if ISA supports 64-bit right rotate (dror et al.)
+   instructions.  */
+#define ISA_HAS_DROR(ISA) (    \
+   0                           \
+   )
+
+/* Return true if ISA supports 32-bit right rotate (ror et al.)
+   instructions.  */
+#define ISA_HAS_ROR(ISA) (     \
+   (ISA) == ISA_MIPS32R2       \
+   )
+
 #define HAVE_32BIT_GPRS                                   \
-    (mips_opts.gp32                                \
-     || mips_opts.abi == O32_ABI                   \
-     || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
+    (mips_opts.gp32 || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
 
 #define HAVE_32BIT_FPRS                            \
-    (mips_opts.fp32                                \
-     || mips_opts.abi == O32_ABI                   \
-     || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
+    (mips_opts.fp32 || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
 
 #define HAVE_64BIT_GPRS (! HAVE_32BIT_GPRS)
 #define HAVE_64BIT_FPRS (! HAVE_32BIT_FPRS)
 
-#define HAVE_NEWABI (mips_opts.abi == N32_ABI || mips_opts.abi == N64_ABI)
+#define HAVE_NEWABI (mips_abi == N32_ABI || mips_abi == N64_ABI)
 
-#define HAVE_64BIT_OBJECTS (mips_opts.abi == N64_ABI)
+#define HAVE_64BIT_OBJECTS (mips_abi == N64_ABI)
 
 /* We can only have 64bit addresses if the object file format
    supports it.  */
@@ -270,16 +297,49 @@ static int mips_32bitmode = 0;
         && mips_pic != EMBEDDED_PIC))
 
 #define HAVE_64BIT_ADDRESSES (! HAVE_32BIT_ADDRESSES)
+#define HAVE_64BIT_ADDRESS_CONSTANTS (HAVE_64BIT_ADDRESSES \
+                                     || HAVE_64BIT_GPRS)
+
+/* 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)                                            \
+   (strncmp (TARGET_CPU, "mips16", sizeof ("mips16") - 1) == 0         \
+    || strncmp (TARGET_CANONICAL, "mips-lsi-elf", sizeof ("mips-lsi-elf") - 1) == 0)
 
 /* Return true if the given CPU supports the MIPS3D ASE.  */
 #define CPU_HAS_MIPS3D(cpu)    ((cpu) == CPU_SB1      \
                                 )
 
+/* Return true if the given CPU supports the MDMX ASE.  */
+#define CPU_HAS_MDMX(cpu)      (FALSE                 \
+                                )
+
+/* True if CPU has a dror instruction.  */
+#define CPU_HAS_DROR(CPU)      ((CPU) == CPU_VR5400 || (CPU) == CPU_VR5500)
+
+/* True if CPU has a ror instruction.  */
+#define CPU_HAS_ROR(CPU)       CPU_HAS_DROR (CPU)
+
 /* 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                      \
                          )
 
@@ -287,12 +347,16 @@ static int mips_32bitmode = 0;
    from the GPRs, and thus does not require nops to be inserted.  */
 #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.  */
 /* 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                       \
                        )
 
@@ -302,26 +366,7 @@ static int mips_32bitmode = 0;
 
 /* MIPS PIC level.  */
 
-enum mips_pic_level
-{
-  /* Do not generate PIC code.  */
-  NO_PIC,
-
-  /* Generate PIC code as in Irix 4.  This is not implemented, and I'm
-     not sure what it is supposed to do.  */
-  IRIX4_PIC,
-
-  /* Generate PIC code as in the SVR4 MIPS ABI.  */
-  SVR4_PIC,
-
-  /* Generate PIC code without using a global offset table: the data
-     segment has a maximum size of 64K, all data references are off
-     the $gp register, and all text references are PC relative.  This
-     is used on some embedded systems.  */
-  EMBEDDED_PIC
-};
-
-static enum mips_pic_level mips_pic;
+enum mips_pic_level mips_pic;
 
 /* Warn about all NOPS that the assembler generates.  */
 static int warn_nops = 0;
@@ -423,6 +468,7 @@ static offsetT mips_cprestore_offset = -1;
 static offsetT mips_cpreturn_offset = -1;
 static int mips_cpreturn_register = -1;
 static int mips_gp_register = GP;
+static int mips_gprel_offset = 0;
 
 /* Whether mips_cprestore_offset has been set in the current function
    (or whether it has already been warned about, if not).  */
@@ -529,6 +575,11 @@ struct mips_hi_fixup
 
 static struct mips_hi_fixup *mips_hi_fixup_list;
 
+/* The frag containing the last explicit relocation operator.
+   Null if explicit relocations have not been used.  */
+
+static fragS *prev_reloc_op_frag;
+
 /* Map normal MIPS register numbers to mips16 register numbers.  */
 
 #define X ILLEGAL_REG
@@ -547,6 +598,15 @@ static const unsigned int mips16_to_32_reg_map[] =
 {
   16, 17, 2, 3, 4, 5, 6, 7
 };
+
+static int mips_fix_4122_bugs;
+
+/* 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
+   fail to compute the offset before expanding the macro to the most
+   efficient expansion.  */
+
+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
@@ -624,6 +684,86 @@ static const unsigned int mips16_to_32_reg_map[] =
 #define RELAX_RELOC3(i) (((i) >> 1) & 1)
 #define RELAX_WARN(i) ((i) & 1)
 
+/* Branch without likely bit.  If label is out of range, we turn:
+
+       beq reg1, reg2, label
+       delay slot
+
+   into
+
+        bne reg1, reg2, 0f
+        nop
+        j label
+     0: delay slot
+
+   with the following opcode replacements:
+
+       beq <-> bne
+       blez <-> bgtz
+       bltz <-> bgez
+       bc1f <-> bc1t
+
+       bltzal <-> bgezal  (with jal label instead of j label)
+
+   Even though keeping the delay slot instruction in the delay slot of
+   the branch would be more efficient, it would be very tricky to do
+   correctly, because we'd have to introduce a variable frag *after*
+   the delay slot instruction, and expand that instead.  Let's do it
+   the easy way for now, even if the branch-not-taken case now costs
+   one additional instruction.  Out-of-range branches are not supposed
+   to be common, anyway.
+
+   Branch likely.  If label is out of range, we turn:
+
+       beql reg1, reg2, label
+       delay slot (annulled if branch not taken)
+
+   into
+
+        beql reg1, reg2, 1f
+        nop
+        beql $0, $0, 2f
+        nop
+     1: j[al] label
+        delay slot (executed only if branch taken)
+     2:
+
+   It would be possible to generate a shorter sequence by losing the
+   likely bit, generating something like:
+
+       bne reg1, reg2, 0f
+       nop
+       j[al] label
+       delay slot (executed only if branch taken)
+     0:
+
+       beql -> bne
+       bnel -> beq
+       blezl -> bgtz
+       bgtzl -> blez
+       bltzl -> bgez
+       bgezl -> bltz
+       bc1fl -> bc1t
+       bc1tl -> bc1f
+
+       bltzall -> bgezal  (with jal label instead of j label)
+       bgezall -> bltzal  (ditto)
+
+
+   but it's not clear that it would actually improve performance.  */
+#define RELAX_BRANCH_ENCODE(uncond, likely, link, toofar) \
+  ((relax_substateT) \
+   (0xc0000000 \
+    | ((toofar) ? 1 : 0) \
+    | ((link) ? 2 : 0) \
+    | ((likely) ? 4 : 0) \
+    | ((uncond) ? 8 : 0)))
+#define RELAX_BRANCH_P(i) (((i) & 0xf0000000) == 0xc0000000)
+#define RELAX_BRANCH_UNCOND(i) (((i) & 8) != 0)
+#define RELAX_BRANCH_LIKELY(i) (((i) & 4) != 0)
+#define RELAX_BRANCH_LINK(i) (((i) & 2) != 0)
+#define RELAX_BRANCH_TOOFAR(i) (((i) & 1) != 0)
+
 /* For mips16 code, we use an entirely different form of relaxation.
    mips16 supports two versions of most instructions which take
    immediate values: a small one which takes some small value, and a
@@ -651,7 +791,7 @@ static const unsigned int mips16_to_32_reg_map[] =
    | ((ext) ? 0x200 : 0)                                       \
    | ((dslot) ? 0x400 : 0)                                     \
    | ((jal_dslot) ? 0x800 : 0))
-#define RELAX_MIPS16_P(i) (((i) & 0x80000000) != 0)
+#define RELAX_MIPS16_P(i) (((i) & 0xc0000000) == 0x80000000)
 #define RELAX_MIPS16_TYPE(i) ((i) & 0xff)
 #define RELAX_MIPS16_USER_SMALL(i) (((i) & 0x100) != 0)
 #define RELAX_MIPS16_USER_EXT(i) (((i) & 0x200) != 0)
@@ -663,6 +803,17 @@ static const unsigned int mips16_to_32_reg_map[] =
 #define RELAX_MIPS16_LONG_BRANCH(i) (((i) & 0x2000) != 0)
 #define RELAX_MIPS16_MARK_LONG_BRANCH(i) ((i) | 0x2000)
 #define RELAX_MIPS16_CLEAR_LONG_BRANCH(i) ((i) &~ 0x2000)
+
+/* Is the given value a sign-extended 32-bit value?  */
+#define IS_SEXT_32BIT_NUM(x)                                           \
+  (((x) &~ (offsetT) 0x7fffffff) == 0                                  \
+   || (((x) &~ (offsetT) 0x7fffffff) == ~ (offsetT) 0x7fffffff))
+
+/* Is the given value a sign-extended 16-bit value?  */
+#define IS_SEXT_16BIT_NUM(x)                                           \
+  (((x) &~ (offsetT) 0x7fff) == 0                                      \
+   || (((x) &~ (offsetT) 0x7fff) == ~ (offsetT) 0x7fff))
+
 \f
 /* Prototypes for static functions.  */
 
@@ -675,116 +826,155 @@ static const unsigned int mips16_to_32_reg_map[] =
 
 enum mips_regclass { MIPS_GR_REG, MIPS_FP_REG, MIPS16_REG };
 
-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,
-                                boolean));
-static void mips_no_prev_insn PARAMS ((int));
-static void mips_emit_delays PARAMS ((boolean));
+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,
-                                ...));
+static void macro_build
+  PARAMS ((char *place, int *counter, expressionS * ep, const char *name,
+          const char *fmt, ...));
 #else
 static void macro_build ();
 #endif
-static void mips16_macro_build PARAMS ((char *, int *, expressionS *,
-                                       const char *, const char *,
-                                       va_list));
-static void macro_build_lui PARAMS ((char *place, int *counter,
-                                    expressionS * ep, int regnum));
-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));
+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));
 #ifdef LOSING_COMPILER
-static void macro2 PARAMS ((struct mips_cl_insn * ip));
+static void macro2
+  PARAMS ((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 mips16_immed PARAMS ((char *, unsigned int, int, offsetT, boolean,
-                                 boolean, boolean, unsigned long *,
-                                 boolean *, unsigned short *));
-static int my_getPercentOp PARAMS ((char **, unsigned int *, int *));
-static int my_getSmallParser PARAMS ((char **, unsigned int *, int *));
-static int my_getSmallExpression PARAMS ((expressionS *, char *));
-static void my_getExpression PARAMS ((expressionS *, char *));
+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 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 *));
+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));
+static int support_64bit_objects
+  PARAMS((void));
 #endif
-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_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_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_file PARAMS ((int));
-static int mips16_extended_frag PARAMS ((fragS *, asection *, long));
-static const char *mips_isa_to_str PARAMS ((int));
-static const char *mips_cpu_to_str PARAMS ((int));
-static int validate_mips_insn PARAMS ((const struct mips_opcode *));
-static void show PARAMS ((FILE *, char *, int *, int *));
+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));
+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 *));
+static int mips_need_elf_addend_fixup
+  PARAMS ((fixS *));
 #endif
 
-/* Return values of my_getSmallExpression().  */
-
-enum small_ex_type
-{
-  S_EX_NONE = 0,
-  S_EX_REGISTER,
-
-  /* Direct relocation creation by %percent_op().  */
-  S_EX_HALF,
-  S_EX_HI,
-  S_EX_LO,
-  S_EX_GP_REL,
-  S_EX_GOT,
-  S_EX_CALL16,
-  S_EX_GOT_DISP,
-  S_EX_GOT_PAGE,
-  S_EX_GOT_OFST,
-  S_EX_GOT_HI,
-  S_EX_GOT_LO,
-  S_EX_NEG,
-  S_EX_HIGHER,
-  S_EX_HIGHEST,
-  S_EX_CALL_HI,
-  S_EX_CALL_LO
-};
-
 /* Table and functions used to map between CPU/ISA names, and
    ISA levels, and CPU numbers.  */
 
@@ -796,9 +986,18 @@ struct mips_cpu_info
   int cpu;                    /* CPU number (default CPU if ISA).  */
 };
 
-static const struct mips_cpu_info *mips_cpu_info_from_name PARAMS ((const char *));
-static const struct mips_cpu_info *mips_cpu_info_from_isa PARAMS ((int));
-static const struct mips_cpu_info *mips_cpu_info_from_cpu PARAMS ((int));
+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));
 \f
 /* Pseudo-op table.
 
@@ -834,6 +1033,7 @@ static const pseudo_typeS mips_pseudo_table[] =
   {"cpreturn", s_cpreturn, 0},
   {"gpvalue", s_gpvalue, 0},
   {"gpword", s_gpword, 0},
+  {"gpdword", s_gpdword, 0},
   {"cpadd", s_cpadd, 0},
   {"insn", s_insn, 0},
 
@@ -860,15 +1060,14 @@ static const pseudo_typeS mips_pseudo_table[] =
   {"long", s_cons, 2},
   {"octa", s_cons, 4},
   {"quad", s_cons, 3},
+  {"section", s_change_section, 0},
   {"short", s_cons, 1},
   {"single", s_float_cons, 'f'},
   {"stabn", s_mips_stab, 'n'},
   {"text", s_change_sec, 't'},
   {"word", s_cons, 2},
 
-#ifdef MIPS_STABS_ELF
   { "extern", ecoff_directive_extern, 0},
-#endif
 
   { NULL, NULL, 0 },
 };
@@ -882,10 +1081,10 @@ static const pseudo_typeS mips_nonecoff_pseudo_table[] =
   {"end", s_mips_end, 0},
   {"endb", s_ignore, 0},
   {"ent", s_mips_ent, 0},
-  {"file", s_file, 0},
+  {"file", s_mips_file, 0},
   {"fmask", s_mips_mask, 'F'},
   {"frame", s_mips_frame, 0},
-  {"loc", s_ignore, 0},
+  {"loc", s_mips_loc, 0},
   {"mask", s_mips_mask, 'R'},
   {"verstamp", s_ignore, 0},
   { NULL, NULL, 0 },
@@ -940,50 +1139,17 @@ static bfd_reloc_code_real_type imm_reloc[3]
 static bfd_reloc_code_real_type offset_reloc[3]
   = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
 
-/* This is set by mips_ip if imm_reloc is an unmatched HI16_S reloc.  */
-
-static boolean imm_unmatched_hi;
-
 /* These are set by mips16_ip if an explicit extension is used.  */
 
-static boolean mips16_small, mips16_ext;
+static bfd_boolean mips16_small, mips16_ext;
 
-#ifdef MIPS_STABS_ELF
-/* The pdr segment for per procedure frame/regmask info */
+#ifdef OBJ_ELF
+/* The pdr segment for per procedure frame/regmask info.  Not used for
+   ECOFF debugging.  */
 
 static segT pdr_seg;
 #endif
 
-static const char *
-mips_isa_to_str (isa)
-     int isa;
-{
-  const struct mips_cpu_info *ci;
-  static char s[20];
-
-  ci = mips_cpu_info_from_isa (isa);
-  if (ci != NULL)
-    return (ci->name);
-
-  sprintf (s, "ISA#%d", isa);
-  return s;
-}
-
-static const char *
-mips_cpu_to_str (cpu)
-     int cpu;
-{
-  const struct mips_cpu_info *ci;
-  static char s[16];
-
-  ci = mips_cpu_info_from_cpu (cpu);
-  if (ci != NULL)
-    return (ci->name);
-
-  sprintf (s, "CPU#%d", cpu);
-  return s;
-}
-
 /* The default target format to use.  */
 
 const char *
@@ -999,17 +1165,26 @@ mips_target_format ()
       return "pe-mips";
     case bfd_target_elf_flavour:
 #ifdef TE_TMIPS
-      /* This is traditional mips */
+      /* This is traditional mips */
       return (target_big_endian
-             ? (HAVE_64BIT_OBJECTS ? "elf64-tradbigmips"
-                : "elf32-tradbigmips")
-             : (HAVE_64BIT_OBJECTS ? "elf64-tradlittlemips"
-                : "elf32-tradlittlemips"));
+             ? (HAVE_64BIT_OBJECTS
+                ? "elf64-tradbigmips"
+                : (HAVE_NEWABI
+                   ? "elf32-ntradbigmips" : "elf32-tradbigmips"))
+             : (HAVE_64BIT_OBJECTS
+                ? "elf64-tradlittlemips"
+                : (HAVE_NEWABI
+                   ? "elf32-ntradlittlemips" : "elf32-tradlittlemips")));
 #else
       return (target_big_endian
-             ? (HAVE_64BIT_OBJECTS ? "elf64-bigmips" : "elf32-bigmips")
-             : (HAVE_64BIT_OBJECTS ? "elf64-littlemips"
-                : "elf32-littlemips"));
+             ? (HAVE_64BIT_OBJECTS
+                ? "elf64-bigmips"
+                : (HAVE_NEWABI
+                   ? "elf32-nbigmips" : "elf32-bigmips"))
+             : (HAVE_64BIT_OBJECTS
+                ? "elf64-littlemips"
+                : (HAVE_NEWABI
+                   ? "elf32-nlittlemips" : "elf32-littlemips")));
 #endif
     default:
       abort ();
@@ -1025,235 +1200,11 @@ md_begin ()
 {
   register const char *retval = NULL;
   int i = 0;
-  const char *cpu;
-  char *a = NULL;
   int broken = 0;
-  int mips_isa_from_cpu;
-  int target_cpu_had_mips16 = 0;
-  const struct mips_cpu_info *ci;
-
-  /* GP relative stuff not working for PE */
-  if (strncmp (TARGET_OS, "pe", 2) == 0
-      && g_switch_value != 0)
-    {
-      if (g_switch_seen)
-       as_bad (_("-G not supported in this configuration."));
-      g_switch_value = 0;
-    }
-
-  cpu = TARGET_CPU;
-  if (strcmp (cpu + (sizeof TARGET_CPU) - 3, "el") == 0)
-    {
-      a = xmalloc (sizeof TARGET_CPU);
-      strcpy (a, TARGET_CPU);
-      a[(sizeof TARGET_CPU) - 3] = '\0';
-      cpu = a;
-    }
-
-  if (strncmp (cpu, "mips16", sizeof "mips16" - 1) == 0)
-    {
-      target_cpu_had_mips16 = 1;
-      cpu += sizeof "mips16" - 1;
-    }
-
-  if (mips_opts.mips16 < 0)
-    mips_opts.mips16 = target_cpu_had_mips16;
-
-  /* Backward compatibility for historic -mcpu= option.  Check for
-     incompatible options, warn if -mcpu is used.  */
-  if (mips_cpu != CPU_UNKNOWN
-      && mips_arch != CPU_UNKNOWN
-      && mips_cpu != mips_arch)
-    {
-      as_fatal (_("The -mcpu option can't be used together with -march. "
-                 "Use -mtune instead of -mcpu."));
-    }
-
-  if (mips_cpu != CPU_UNKNOWN
-      && mips_tune != CPU_UNKNOWN
-      && mips_cpu != mips_tune)
-    {
-      as_fatal (_("The -mcpu option can't be used together with -mtune. "
-                 "Use -march instead of -mcpu."));
-    }
-
-#if 1
-  /* For backward compatibility, let -mipsN set various defaults.  */
-  /* This code should go away, to be replaced with something rather more
-     draconian.  Until GCC 3.1 has been released for some reasonable
-     amount of time, however, we need to support this.  */
-  if (mips_opts.isa != ISA_UNKNOWN)
-    {
-      /* Translate -mipsN to the appropriate settings of file_mips_gp32
-        and file_mips_fp32.  Tag binaries as using the mipsN ISA.  */
-      if (file_mips_gp32 < 0)
-       {
-         if (ISA_HAS_64BIT_REGS (mips_opts.isa))
-           file_mips_gp32 = 0;
-         else
-           file_mips_gp32 = 1;
-       }
-      if (file_mips_fp32 < 0)
-       {
-         if (ISA_HAS_64BIT_REGS (mips_opts.isa))
-           file_mips_fp32 = 0;
-         else
-           file_mips_fp32 = 1;
-       }
-
-      ci = mips_cpu_info_from_isa (mips_opts.isa);
-      assert (ci != NULL);
-      /* -mipsN has higher priority than -mcpu but lower than -march.  */
-      if (mips_arch == CPU_UNKNOWN)
-       mips_arch = ci->cpu;
-
-      /* Default mips_abi.  */
-      if (mips_opts.abi == NO_ABI)
-       {
-         if (mips_opts.isa == ISA_MIPS1 || mips_opts.isa == ISA_MIPS2)
-           mips_opts.abi = O32_ABI;
-         else if (mips_opts.isa == ISA_MIPS3 || mips_opts.isa == ISA_MIPS4)
-           mips_opts.abi = O64_ABI;
-       }
-    }
-
-  if (mips_arch == CPU_UNKNOWN && mips_cpu != CPU_UNKNOWN)
-    {
-      ci = mips_cpu_info_from_cpu (mips_cpu);
-      assert (ci != NULL);
-      mips_arch = ci->cpu;
-      as_warn (_("The -mcpu option is deprecated.  Please use -march and "
-                "-mtune instead."));
-    }
-
-  /* Set tune from -mcpu, not from -mipsN.  */
-  if (mips_tune == CPU_UNKNOWN && mips_cpu != CPU_UNKNOWN)
-    {
-      ci = mips_cpu_info_from_cpu (mips_cpu);
-      assert (ci != NULL);
-      mips_tune = ci->cpu;
-    }
-
-  /* At this point, mips_arch will either be CPU_UNKNOWN if no ARCH was
-     specified on the command line, or some other value if one was.
-     Similarly, mips_opts.isa will be ISA_UNKNOWN if not specified on
-     the command line, or will be set otherwise if one was.  */
-
-  if (mips_arch != CPU_UNKNOWN && mips_opts.isa != ISA_UNKNOWN)
-    /* Handled above.  */;
-#else
-  if (mips_arch == CPU_UNKNOWN && mips_cpu != CPU_UNKNOWN)
-    {
-      ci = mips_cpu_info_from_cpu (mips_cpu);
-      assert (ci != NULL);
-      mips_arch = ci->cpu;
-      as_warn (_("The -mcpu option is deprecated.  Please use -march and "
-                "-mtune instead."));
-    }
-
-  /* At this point, mips_arch will either be CPU_UNKNOWN if no ARCH was
-     specified on the command line, or some other value if one was.
-     Similarly, mips_opts.isa will be ISA_UNKNOWN if not specified on
-     the command line, or will be set otherwise if one was.  */
-
-  if (mips_arch != CPU_UNKNOWN && mips_opts.isa != ISA_UNKNOWN)
-    {
-      /* We have to check if the isa is the default isa of arch.  Otherwise
-         we'll get invalid object file headers.  */
-      ci = mips_cpu_info_from_cpu (mips_arch);
-      assert (ci != NULL);
-      if (mips_opts.isa != ci->isa)
-       {
-         /* This really should be an error instead of a warning, but old
-            compilers only have -mcpu which sets both arch and tune.  For
-            now, we discard arch and preserve tune.  */
-         as_warn (_("The -march option is incompatible to -mipsN and "
-                    "therefore ignored."));
-         if (mips_tune == CPU_UNKNOWN)
-           mips_tune = mips_arch;
-         ci = mips_cpu_info_from_isa (mips_opts.isa);
-         assert (ci != NULL);
-         mips_arch = ci->cpu;
-       }
-    }
-#endif
-  else if (mips_arch != CPU_UNKNOWN && mips_opts.isa == ISA_UNKNOWN)
-    {
-      /* We have ARCH, we need ISA.  */
-      ci = mips_cpu_info_from_cpu (mips_arch);
-      assert (ci != NULL);
-      mips_opts.isa = ci->isa;
-    }
-  else if (mips_arch == CPU_UNKNOWN && mips_opts.isa != ISA_UNKNOWN)
-    {
-      /* We have ISA, we need default ARCH.  */
-      ci = mips_cpu_info_from_isa (mips_opts.isa);
-      assert (ci != NULL);
-      mips_arch = ci->cpu;
-    }
-  else
-    {
-      /* We need to set both ISA and ARCH from target cpu.  */
-      ci = mips_cpu_info_from_name (cpu);
-      if (ci == NULL)
-       ci = mips_cpu_info_from_cpu (CPU_R3000);
-      assert (ci != NULL);
-      mips_opts.isa = ci->isa;
-      mips_arch = ci->cpu;
-    }
-
-  if (mips_tune == CPU_UNKNOWN)
-    mips_tune = mips_arch;
-
-  ci = mips_cpu_info_from_cpu (mips_arch);
-  assert (ci != NULL);
-  mips_isa_from_cpu = ci->isa;
-
-  /* End of TARGET_CPU processing, get rid of malloced memory
-     if necessary.  */
-  cpu = NULL;
-  if (a != NULL)
-    {
-      free (a);
-      a = NULL;
-    }
-
-  if (mips_opts.isa == ISA_MIPS1 && mips_trap)
-    as_bad (_("trap exception not supported at ISA 1"));
-
-  /* Set the EABI kind based on the ISA before the user gets
-     to change the ISA with directives.  This isn't really
-     the best, but then neither is basing the abi on the isa.  */
-  if (ISA_HAS_64BIT_REGS (mips_opts.isa)
-      && mips_opts.abi == EABI_ABI)
-    mips_eabi64 = 1;
-
-  /* If they asked for mips1 or mips2 and a cpu that is
-     mips3 or greater, then mark the object file 32BITMODE.  */
-  if (mips_isa_from_cpu != ISA_UNKNOWN
-      && ! ISA_HAS_64BIT_REGS (mips_opts.isa)
-      && ISA_HAS_64BIT_REGS (mips_isa_from_cpu))
-    mips_32bitmode = 1;
-
-  /* If the selected architecture includes support for ASEs, enable
-     generation of code for them.  */
-  if (mips_opts.ase_mips3d == -1 && CPU_HAS_MIPS3D (mips_arch))
-    mips_opts.ase_mips3d = 1;
 
   if (! bfd_set_arch_mach (stdoutput, bfd_arch_mips, mips_arch))
     as_warn (_("Could not set architecture and machine"));
 
-  if (file_mips_gp32 < 0)
-    file_mips_gp32 = 0;
-  if (file_mips_fp32 < 0)
-    file_mips_fp32 = 0;
-
-  file_mips_isa = mips_opts.isa;
-  file_mips_abi = mips_opts.abi;
-  file_ase_mips3d = mips_opts.ase_mips3d;
-  mips_opts.gp32 = file_mips_gp32;
-  mips_opts.fp32 = file_mips_fp32;
-
   op_hash = hash_new ();
 
   for (i = 0; i < NUMOPCODES;)
@@ -1320,6 +1271,8 @@ md_begin ()
       symbol_table_insert (symbol_new (buf, reg_section, i,
                                       &zero_address_frag));
     }
+  symbol_table_insert (symbol_new ("$ra", reg_section, RA,
+                                  &zero_address_frag));
   symbol_table_insert (symbol_new ("$fp", reg_section, FP,
                                   &zero_address_frag));
   symbol_table_insert (symbol_new ("$sp", reg_section, SP,
@@ -1332,10 +1285,26 @@ md_begin ()
                                   &zero_address_frag));
   symbol_table_insert (symbol_new ("$kt1", reg_section, KT1,
                                   &zero_address_frag));
+  symbol_table_insert (symbol_new ("$zero", reg_section, ZERO,
+                                  &zero_address_frag));
   symbol_table_insert (symbol_new ("$pc", reg_section, -1,
                                   &zero_address_frag));
 
-  mips_no_prev_insn (false);
+  /* If we don't add these register names to the symbol table, they
+     may end up being added as regular symbols by operand(), and then
+     make it to the object file as undefined in case they're not
+     regarded as local symbols.  They're local in o32, since `$' is a
+     local symbol prefix, but not in n32 or n64.  */
+  for (i = 0; i < 8; i++)
+    {
+      char buf[6];
+
+      sprintf (buf, "$fcc%i", i);
+      symbol_table_insert (symbol_new (buf, reg_section, -1,
+                                      &zero_address_frag));
+    }
+
+  mips_no_prev_insn (FALSE);
 
   mips_gprmask = 0;
   mips_cprmask[0] = 0;
@@ -1379,7 +1348,7 @@ md_begin ()
        if (strcmp (TARGET_OS, "elf") != 0)
          flags |= SEC_ALLOC | SEC_LOAD;
 
-       if (file_mips_abi != N64_ABI)
+       if (mips_abi != N64_ABI)
          {
            sec = subseg_new (".reginfo", (subsegT) 0);
 
@@ -1425,12 +1394,15 @@ md_begin ()
                                          SEC_HAS_CONTENTS | SEC_READONLY);
            (void) bfd_set_section_alignment (stdoutput, sec, 2);
          }
-
-#ifdef MIPS_STABS_ELF
-       pdr_seg = subseg_new (".pdr", (subsegT) 0);
-       (void) bfd_set_section_flags (stdoutput, pdr_seg,
-                            SEC_READONLY | SEC_RELOC | SEC_DEBUGGING);
-       (void) bfd_set_section_alignment (stdoutput, pdr_seg, 2);
+#ifdef OBJ_ELF
+       else if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+         {
+           pdr_seg = subseg_new (".pdr", (subsegT) 0);
+           (void) bfd_set_section_flags (stdoutput, pdr_seg,
+                                         SEC_READONLY | SEC_RELOC
+                                         | SEC_DEBUGGING);
+           (void) bfd_set_section_alignment (stdoutput, pdr_seg, 2);
+         }
 #endif
 
        subseg_set (seg, subseg);
@@ -1457,7 +1429,6 @@ md_assemble (str)
     = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
 
   imm_expr.X_op = O_absent;
-  imm_unmatched_hi = false;
   offset_expr.X_op = O_absent;
   imm_reloc[0] = BFD_RELOC_UNUSED;
   imm_reloc[1] = BFD_RELOC_UNUSED;
@@ -1491,14 +1462,39 @@ md_assemble (str)
   else
     {
       if (imm_expr.X_op != O_absent)
-       append_insn (NULL, &insn, &imm_expr, imm_reloc, imm_unmatched_hi);
+       append_insn (NULL, &insn, &imm_expr, imm_reloc);
       else if (offset_expr.X_op != O_absent)
-       append_insn (NULL, &insn, &offset_expr, offset_reloc, false);
+       append_insn (NULL, &insn, &offset_expr, offset_reloc);
       else
-       append_insn (NULL, &insn, NULL, unused_reloc, false);
+       append_insn (NULL, &insn, NULL, unused_reloc);
     }
 }
 
+/* Return true if the given relocation might need a matching %lo().
+   Note that R_MIPS_GOT16 relocations only need a matching %lo() when
+   applied to local symbols.  */
+
+static inline bfd_boolean
+reloc_needs_lo_p (reloc)
+     bfd_reloc_code_real_type reloc;
+{
+  return (reloc == BFD_RELOC_HI16_S
+         || reloc == BFD_RELOC_MIPS_GOT16);
+}
+
+/* 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;
+{
+  return (fixp->fx_next != NULL
+         && fixp->fx_next->fx_r_type == BFD_RELOC_LO16
+         && fixp->fx_addsy == fixp->fx_next->fx_addsy
+         && fixp->fx_offset == fixp->fx_next->fx_offset);
+}
+
 /* See whether instruction IP reads register REG.  CLASS is the type
    of register.  */
 
@@ -1515,8 +1511,8 @@ insn_uses_reg (ip, reg, class)
       class = MIPS_GR_REG;
     }
 
-  /* Don't report on general register 0, since it never changes.  */
-  if (class == MIPS_GR_REG && reg == 0)
+  /* Don't report on general register ZERO, since it never changes.  */
+  if (class == MIPS_GR_REG && reg == ZERO)
     return 0;
 
   if (class == MIPS_FP_REG)
@@ -1643,17 +1639,17 @@ mips16_mark_labels ()
    used with RELOC_TYPE.  */
 
 static void
-append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
+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;
-     boolean unmatched_hi;
 {
   register unsigned long prev_pinfo, pinfo;
   char *f;
   fixS *fixp[3];
   int nops = 0;
+  bfd_boolean force_new_frag = FALSE;
 
   /* Mark instruction labels in mips16 mode.  */
   mips16_mark_labels ();
@@ -1903,6 +1899,50 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
       if (prev_prev_nop && nops == 0)
        ++nops;
 
+      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;
+               }
+
+             /* 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;
+               }
+
+             /* Errata 24 - MT{LO,HI} after [D]MACC */
+             if (strcmp (tn, "mtlo") == 0
+                 || strcmp (tn, "mthi") == 0)
+               {
+                 min_nops = 1;
+               }
+
+           }
+         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;
+       }
+
       /* 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
@@ -1997,7 +2037,35 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
        }
     }
 
-  if (*reloc_type > BFD_RELOC_UNUSED)
+  if (place == NULL
+      && address_expr
+      && *reloc_type == BFD_RELOC_16_PCREL_S2
+      && (pinfo & INSN_UNCOND_BRANCH_DELAY || pinfo & INSN_COND_BRANCH_DELAY
+         || pinfo & INSN_COND_BRANCH_LIKELY)
+      && mips_relax_branch
+      /* Don't try branch relaxation within .set nomacro, or within
+        .set noat if we use $at for PIC computations.  If it turns
+        out that the branch was out-of-range, we'll get an error.  */
+      && !mips_opts.warn_about_macros
+      && !(mips_opts.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);
+      *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);
@@ -2065,6 +2133,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
              break;
 
            case BFD_RELOC_LO16:
+           case BFD_RELOC_MIPS_GOT_DISP:
              ip->insn_opcode |= address_expr->X_add_number & 0xffff;
              break;
 
@@ -2072,8 +2141,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
              if ((address_expr->X_add_number & 3) != 0)
                as_bad (_("jump to misaligned address (0x%lx)"),
                        (unsigned long) address_expr->X_add_number);
-             if (address_expr->X_add_number & ~0xfffffff
-                 || address_expr->X_add_number > 0x7fffffc)
+             if (address_expr->X_add_number & ~0xfffffff)
                as_bad (_("jump address range overflow (0x%lx)"),
                        (unsigned long) address_expr->X_add_number);
              ip->insn_opcode |= (address_expr->X_add_number >> 2) & 0x3ffffff;
@@ -2083,8 +2151,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
              if ((address_expr->X_add_number & 3) != 0)
                as_bad (_("jump to misaligned address (0x%lx)"),
                        (unsigned long) address_expr->X_add_number);
-             if (address_expr->X_add_number & ~0xfffffff
-                 || address_expr->X_add_number > 0x7fffffc)
+             if (address_expr->X_add_number & ~0xfffffff)
                as_bad (_("jump address range overflow (0x%lx)"),
                        (unsigned long) address_expr->X_add_number);
              ip->insn_opcode |=
@@ -2093,10 +2160,6 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                 | ((address_expr->X_add_number & 0x3fffc) >> 2));
              break;
 
-           case BFD_RELOC_16_PCREL:
-             ip->insn_opcode |= address_expr->X_add_number & 0xffff;
-             break;
-
            case BFD_RELOC_16_PCREL_S2:
              goto need_reloc;
 
@@ -2110,122 +2173,96 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
          /* Don't generate a reloc if we are writing into a variant frag.  */
          if (place == NULL)
            {
-             fixp[0] = fix_new_exp (frag_now, f - frag_now->fr_literal, 4,
+             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;
+
+             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),
                                     address_expr,
-                                    (*reloc_type == BFD_RELOC_16_PCREL
-                                     || *reloc_type == BFD_RELOC_16_PCREL_S2),
+                                    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 &&
-                 (*reloc_type == BFD_RELOC_16
-                  || *reloc_type == BFD_RELOC_32
-                  || *reloc_type == BFD_RELOC_MIPS_JMP
-                  || *reloc_type == BFD_RELOC_HI16_S
-                  || *reloc_type == BFD_RELOC_LO16
-                  || *reloc_type == BFD_RELOC_GPREL16
-                  || *reloc_type == BFD_RELOC_MIPS_LITERAL
-                  || *reloc_type == BFD_RELOC_GPREL32
-                  || *reloc_type == BFD_RELOC_64
-                  || *reloc_type == BFD_RELOC_CTOR
-                  || *reloc_type == BFD_RELOC_MIPS_SUB
-                  || *reloc_type == BFD_RELOC_MIPS_HIGHEST
-                  || *reloc_type == BFD_RELOC_MIPS_HIGHER
-                  || *reloc_type == BFD_RELOC_MIPS_SCN_DISP
-                  || *reloc_type == BFD_RELOC_MIPS_REL16
-                  || *reloc_type == BFD_RELOC_MIPS_RELGOT))
+             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 (unmatched_hi)
+             if (reloc_needs_lo_p (*reloc_type))
                {
                  struct mips_hi_fixup *hi_fixup;
 
-                 assert (*reloc_type == BFD_RELOC_HI16_S);
-                 hi_fixup = ((struct mips_hi_fixup *)
-                             xmalloc (sizeof (struct mips_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;
-                 hi_fixup->next = mips_hi_fixup_list;
-                 mips_hi_fixup_list = hi_fixup;
                }
 
-             if (reloc_type[1] != BFD_RELOC_UNUSED)
-               {
-                 /* FIXME: This symbol can be one of
-                    RSS_UNDEF, RSS_GP, RSS_GP0, RSS_LOC.  */
-                 address_expr->X_op = O_absent;
-                 address_expr->X_add_symbol = 0;
-                 address_expr->X_add_number = 0;
-
-                 fixp[1] = fix_new_exp (frag_now, f - frag_now->fr_literal,
-                                        4, address_expr, false,
-                                        reloc_type[1]);
-
-                 /* These relocations can have an addend that won't fit in
-                    4 octets for 64bit assembly.  */
-                 if (HAVE_64BIT_GPRS &&
-                     (*reloc_type == BFD_RELOC_16
-                      || *reloc_type == BFD_RELOC_32
-                      || *reloc_type == BFD_RELOC_MIPS_JMP
-                      || *reloc_type == BFD_RELOC_HI16_S
-                      || *reloc_type == BFD_RELOC_LO16
-                      || *reloc_type == BFD_RELOC_GPREL16
-                      || *reloc_type == BFD_RELOC_MIPS_LITERAL
-                      || *reloc_type == BFD_RELOC_GPREL32
-                      || *reloc_type == BFD_RELOC_64
-                      || *reloc_type == BFD_RELOC_CTOR
-                      || *reloc_type == BFD_RELOC_MIPS_SUB
-                      || *reloc_type == BFD_RELOC_MIPS_HIGHEST
-                      || *reloc_type == BFD_RELOC_MIPS_HIGHER
-                      || *reloc_type == BFD_RELOC_MIPS_SCN_DISP
-                      || *reloc_type == BFD_RELOC_MIPS_REL16
-                      || *reloc_type == BFD_RELOC_MIPS_RELGOT))
-                   fixp[1]->fx_no_overflow = 1;
-
-                 if (reloc_type[2] != BFD_RELOC_UNUSED)
-                   {
-                     address_expr->X_op = O_absent;
-                     address_expr->X_add_symbol = 0;
-                     address_expr->X_add_number = 0;
-
-                     fixp[2] = fix_new_exp (frag_now,
-                                            f - frag_now->fr_literal, 4,
-                                            address_expr, false,
-                                            reloc_type[2]);
-
-                     /* These relocations can have an addend that won't fit in
-                        4 octets for 64bit assembly.  */
-                     if (HAVE_64BIT_GPRS &&
-                         (*reloc_type == BFD_RELOC_16
-                          || *reloc_type == BFD_RELOC_32
-                          || *reloc_type == BFD_RELOC_MIPS_JMP
-                          || *reloc_type == BFD_RELOC_HI16_S
-                          || *reloc_type == BFD_RELOC_LO16
-                          || *reloc_type == BFD_RELOC_GPREL16
-                          || *reloc_type == BFD_RELOC_MIPS_LITERAL
-                          || *reloc_type == BFD_RELOC_GPREL32
-                          || *reloc_type == BFD_RELOC_64
-                          || *reloc_type == BFD_RELOC_CTOR
-                          || *reloc_type == BFD_RELOC_MIPS_SUB
-                          || *reloc_type == BFD_RELOC_MIPS_HIGHEST
-                          || *reloc_type == BFD_RELOC_MIPS_HIGHER
-                          || *reloc_type == BFD_RELOC_MIPS_SCN_DISP
-                          || *reloc_type == BFD_RELOC_MIPS_REL16
-                          || *reloc_type == BFD_RELOC_MIPS_RELGOT))
-                       fixp[2]->fx_no_overflow = 1;
-                   }
-               }
+             /* 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;
+
+                   fixp[i] = fix_new_exp (frag_now, fixp[0]->fx_where,
+                                          fixp[0]->fx_size, address_expr,
+                                          FALSE, reloc_type[i]);
+                 }
            }
        }
     }
 
   if (! mips_opts.mips16)
-    md_number_to_chars (f, ip->insn_opcode, 4);
+    {
+      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
     {
@@ -2235,6 +2272,9 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
          f += 2;
        }
       md_number_to_chars (f, ip->insn_opcode, 2);
+#ifdef OBJ_ELF
+      dwarf2_emit_insn (ip->use_extend ? 4 : 2);
+#endif
     }
 
   /* Update the register mask information.  */
@@ -2547,6 +2587,18 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                      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;
@@ -2689,11 +2741,10 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
   /* We just output an insn, so the next one doesn't have a label.  */
   mips_clear_insn_labels ();
 
-  /* We must ensure that a fixup associated with an unmatched %hi
-     reloc does not become a variant frag.  Otherwise, the
-     rearrangement of %hi relocs in frob_file may confuse
-     tc_gen_reloc.  */
-  if (unmatched_hi)
+  /* 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);
@@ -2736,7 +2787,7 @@ mips_no_prev_insn (preserve)
 
 static void
 mips_emit_delays (insns)
-     boolean insns;
+     bfd_boolean insns;
 {
   if (! mips_opts.noreorder)
     {
@@ -2790,6 +2841,20 @@ mips_emit_delays (insns)
            ++nops;
        }
 
+      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;
+       }
+
       if (nops > 0)
        {
          struct insn_label_list *l;
@@ -2913,10 +2978,13 @@ macro_build (place, counter, ep, name, fmt, va_alist)
   while (1)
     {
       /* It is assumed here that macros will never generate
-         MIPS-3D instructions.  */
+         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, mips_arch)
+         && 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;
 
@@ -2955,6 +3023,7 @@ macro_build (place, counter, ep, name, fmt, va_alist)
 
        case 'd':
        case 'G':
+       case 'K':
          insn.insn_opcode |= va_arg (args, int) << OP_SH_RD;
          continue;
 
@@ -3013,6 +3082,9 @@ macro_build (place, counter, ep, name, fmt, va_alist)
                  || *r == BFD_RELOC_LO16
                  || *r == BFD_RELOC_MIPS_GOT16
                  || *r == BFD_RELOC_MIPS_CALL16
+                 || *r == BFD_RELOC_MIPS_GOT_DISP
+                 || *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
@@ -3049,10 +3121,7 @@ macro_build (place, counter, ep, name, fmt, va_alist)
              ep = NULL;
            }
          else
-           if (mips_pic == EMBEDDED_PIC)
-             *r = BFD_RELOC_16_PCREL_S2;
-           else
-             *r = BFD_RELOC_16_PCREL;
+           *r = BFD_RELOC_16_PCREL_S2;
          continue;
 
        case 'a':
@@ -3072,7 +3141,7 @@ 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, false);
+  append_insn (place, &insn, ep, r);
 }
 
 static void
@@ -3101,7 +3170,7 @@ mips16_macro_build (place, counter, ep, name, fmt, args)
     }
 
   insn.insn_opcode = insn.insn_mo->match;
-  insn.use_extend = false;
+  insn.use_extend = FALSE;
 
   for (;;)
     {
@@ -3179,8 +3248,8 @@ mips16_macro_build (place, counter, ep, name, fmt, args)
              *r = (int) BFD_RELOC_UNUSED + c;
            else
              {
-               mips16_immed (NULL, 0, c, ep->X_add_number, false, false,
-                             false, &insn.insn_opcode, &insn.use_extend,
+               mips16_immed (NULL, 0, c, ep->X_add_number, FALSE, FALSE,
+                             FALSE, &insn.insn_opcode, &insn.use_extend,
                              &insn.extend);
                ep = NULL;
                *r = BFD_RELOC_UNUSED;
@@ -3198,7 +3267,30 @@ mips16_macro_build (place, counter, ep, name, fmt, args)
 
   assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
 
-  append_insn (place, &insn, ep, r, false);
+  append_insn (place, &insn, ep, r);
+}
+
+/*
+ * Generate a "jalr" instruction with a relocation hint to the called
+ * function.  This occurs in NewABI PIC code.
+ */
+static void
+macro_build_jalr (icnt, ep)
+     int icnt;
+     expressionS *ep;
+{
+  char *f = NULL;;
+
+  if (HAVE_NEWABI)
+    {
+      frag_grow (4);
+      f = frag_more (0);
+    }
+  macro_build ((char *) NULL, &icnt, (expressionS *) 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);
 }
 
 /*
@@ -3215,8 +3307,8 @@ macro_build_lui (place, counter, ep, regnum)
   struct mips_cl_insn insn;
   bfd_reloc_code_real_type r[3]
     = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
-  CONST char *name = "lui";
-  CONST char *fmt = "t,u";
+  const char *name = "lui";
+  const char *fmt = "t,u";
 
   assert (! mips_opts.mips16);
 
@@ -3235,12 +3327,13 @@ macro_build_lui (place, counter, ep, regnum)
                                >> 16) & 0xffff;
       *r = BFD_RELOC_UNUSED;
     }
-  else if (! HAVE_NEWABI)
+  else
     {
       assert (ep->X_op == O_symbol);
       /* _gp_disp is a special case, used from s_cpload.  */
       assert (mips_pic == NO_PIC
-             || strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0);
+             || (! HAVE_NEWABI
+                 && strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0));
       *r = BFD_RELOC_HI16_S;
     }
 
@@ -3264,29 +3357,74 @@ macro_build_lui (place, counter, ep, regnum)
   if (*r == BFD_RELOC_UNUSED)
     {
       insn.insn_opcode |= high_expr.X_add_number;
-      append_insn (place, &insn, NULL, r, false);
+      append_insn (place, &insn, NULL, r);
     }
   else
-    append_insn (place, &insn, &high_expr, r, false);
+    append_insn (place, &insn, &high_expr, r);
 }
 
-/*                     set_at()
- * Generates code to set the $at register to true (one)
- * if reg is less than the immediate expression.
- */
+/* 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
-set_at (counter, reg, unsignedp)
+macro_build_ldst_constoffset (place, counter, ep, op, treg, breg)
+     char *place;
      int *counter;
-     int reg;
-     int unsignedp;
+     expressionS *ep;
+     const char *op;
+     int treg, breg;
 {
-  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);
-  else
+  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))
+    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);
+    }
+  else
+    {
+      /* 32-bit offset, need multiple instructions and AT, like:
+          lui      $tempreg,const_hi       (BFD_RELOC_HI16_S)
+          addu     $tempreg,$tempreg,$breg
+           <op>     $treg,const_lo($tempreg)   (BFD_RELOC_LO16)
+         to handle the complete offset.  */
+      macro_build_lui (place, counter, ep, AT);
+      if (place != NULL)
+       place += 4;
+      macro_build (place, counter, (expressionS *) NULL, ADDRESS_ADD_INSN,
+                  "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);
+
+      if (mips_opts.noat)
+       as_warn (_("Macro used $at after \".set noat\""));
+    }
+}
+
+/*                     set_at()
+ * Generates code to set the $at register to true (one)
+ * if reg is less than the immediate expression.
+ */
+static void
+set_at (counter, reg, unsignedp)
+     int *counter;
+     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);
+  else
     {
       load_register (counter, AT, &imm_expr, HAVE_64BIT_GPRS);
       macro_build ((char *) NULL, counter, (expressionS *) NULL,
@@ -3384,11 +3522,6 @@ check_absolute_expr (ip, ex)
            ? 1                          \
            : 0)
 
-/* Is the given value a sign-extended 32-bit value?  */
-#define IS_SEXT_32BIT_NUM(x)                                           \
-  (((x) &~ (offsetT) 0x7fffffff) == 0                                  \
-   || (((x) &~ (offsetT) 0x7fffffff) == ~ (offsetT) 0x7fffffff))
-
 /*                     load_register()
  *  This routine generates the least number of instructions neccessary to load
  *  an absolute expression value into a register.
@@ -3696,9 +3829,9 @@ load_address (counter, reg, ep, used_at)
           daddiu       $reg,<sym>              (BFD_RELOC_MIPS_HIGHER)
           daddiu       $at,<sym>               (BFD_RELOC_LO16)
           dsll32       $reg,0
-          dadd         $reg,$reg,$at
+          daddu        $reg,$reg,$at
 
-        If $at is already in use, we use an path which is suboptimal
+        If $at is already in use, we use a path which is suboptimal
         on superscalar processors.
           lui          $reg,<sym>              (BFD_RELOC_MIPS_HIGHEST)
           daddiu       $reg,<sym>              (BFD_RELOC_MIPS_HIGHER)
@@ -3712,7 +3845,7 @@ load_address (counter, reg, ep, used_at)
          /* We don't do GP optimization for now because RELAX_ENCODE can't
             hold the data for such large chunks.  */
 
-         if (*used_at == 0)
+         if (*used_at == 0 && ! mips_opts.noat)
            {
              macro_build (p, counter, ep, "lui", "t,u",
                           reg, (int) BFD_RELOC_MIPS_HIGHEST);
@@ -3724,7 +3857,7 @@ load_address (counter, reg, ep, used_at)
                           AT, AT, (int) BFD_RELOC_LO16);
              macro_build (p, counter, (expressionS *) NULL, "dsll32",
                           "d,w,<", reg, reg, 0);
-             macro_build (p, counter, (expressionS *) NULL, "dadd",
+             macro_build (p, counter, (expressionS *) NULL, "daddu",
                           "d,v,t", reg, reg, AT);
              *used_at = 1;
            }
@@ -3750,9 +3883,9 @@ load_address (counter, reg, ep, used_at)
              && ! nopic_need_relax (ep->X_add_symbol, 1))
            {
              frag_grow (20);
-             macro_build ((char *) NULL, counter, ep,
-                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu", "t,r,j",
-                          reg, mips_gp_register, (int) BFD_RELOC_GPREL16);
+             macro_build ((char *) NULL, counter, ep, ADDRESS_ADDI_INSN,
+                          "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),
@@ -3761,8 +3894,7 @@ load_address (counter, reg, ep, used_at)
          macro_build_lui (p, counter, ep, reg);
          if (p != NULL)
            p += 4;
-         macro_build (p, counter, ep,
-                      HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
+         macro_build (p, counter, ep, ADDRESS_ADDI_INSN,
                       "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
        }
     }
@@ -3776,28 +3908,70 @@ load_address (counter, reg, ep, used_at)
           lw           $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT16)
           nop
           addiu        $reg,$reg,<sym>         (BFD_RELOC_LO16)
-        If there is a constant, it must be added in after.  */
-      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);
-      if (ex.X_add_number != 0)
+        If there is a constant, it must be added in after.
+
+        If we have NewABI, we want
+          lw           $reg,<sym+cst>($gp)     (BFD_RELOC_MIPS_GOT_DISP)
+         unless we're referencing a global symbol with a non-zero
+         offset, in which case cst must be added separately.  */
+      if (HAVE_NEWABI)
        {
-         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",
+         frag_grow (12);
+
+         if (ep->X_add_number)
+           {
+             frag_now->tc_frag_data.tc_fr_offset =
+               ex.X_add_number = ep->X_add_number;
+             ep->X_add_number = 0;
+             macro_build ((char *) NULL, counter, ep, ADDRESS_LOAD_INSN,
+                          "t,o(b)", reg, (int) 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, ADDRESS_ADDI_INSN,
+                          "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);
+             ep->X_add_number = ex.X_add_number;
+           }
+
+         macro_build (p, counter, ep, ADDRESS_LOAD_INSN, "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);
+           }
+       }
+      else
+       {
+         ex.X_add_number = ep->X_add_number;
+         ep->X_add_number = 0;
+         frag_grow (20);
+         macro_build ((char *) NULL, counter, ep, ADDRESS_LOAD_INSN,
+                      "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, ADDRESS_ADDI_INSN,
                       "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+
+         if (ex.X_add_number != 0)
+           {
+             if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+               as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+             ex.X_op = O_constant;
+             macro_build ((char *) NULL, counter, &ex, ADDRESS_ADDI_INSN,
+                          "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+           }
        }
     }
   else if (mips_pic == SVR4_PIC)
@@ -3810,51 +3984,93 @@ load_address (counter, reg, ep, used_at)
           lui          $reg,<sym>              (BFD_RELOC_MIPS_GOT_HI16)
           addu         $reg,$reg,$gp
           lw           $reg,<sym>($reg)        (BFD_RELOC_MIPS_GOT_LO16)
-        Otherwise, for a reference to a local symbol, we want
+
+        Otherwise, for a reference to a local symbol in old ABI, we want
           lw           $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT16)
           nop
           addiu        $reg,$reg,<sym>         (BFD_RELOC_LO16)
-        If there is a constant, it must be added in after.  */
-      ex.X_add_number = ep->X_add_number;
-      ep->X_add_number = 0;
-      if (reg_needs_delay (GP))
-       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,
-                  dbl ? "daddu" : "addu", "d,v,t", reg, reg, GP);
-      macro_build ((char *) NULL, counter, ep, dbl ? "ld" : "lw",
-                  "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 (p, counter, ep, dbl ? "ld" : "lw",
-                  "t,o(b)", reg, (int) BFD_RELOC_MIPS_GOT16, GP);
-      p += 4;
-      macro_build (p, counter, (expressionS *) NULL, "nop", "");
-      p += 4;
-      macro_build (p, counter, ep, dbl ? "daddiu" : "addiu",
-                  "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
-      if (ex.X_add_number != 0)
+        If there is a constant, it must be added in after.
+
+        In the NewABI, for local symbols, with or without offsets, we want:
+          lw           $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT_PAGE)
+          addiu        $reg,$reg,<sym>         (BFD_RELOC_MIPS_GOT_OFST)
+      */
+      if (HAVE_NEWABI)
        {
+         frag_grow (24);
+
+         frag_now->tc_frag_data.tc_fr_offset =
+           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,
+                      ADDRESS_ADD_INSN, "d,v,t", reg, reg, mips_gp_register);
+         macro_build ((char *) NULL, counter, ep, ADDRESS_LOAD_INSN,
+                      "t,o(b)", reg, (int) 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)"));
-         ex.X_op = O_constant;
-         macro_build ((char *) NULL, counter, &ex, dbl ? "daddiu" : "addiu",
+         else if (ex.X_add_number)
+           {
+             ex.X_op = O_constant;
+             macro_build ((char *) NULL, counter, &ex, ADDRESS_ADDI_INSN,
+                          "t,r,j", reg, reg, (int) 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, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+                      (int) BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
+         macro_build (p + 4, counter, ep, ADDRESS_ADDI_INSN, "t,r,j",
+                      reg, reg, (int) BFD_RELOC_MIPS_GOT_OFST);
+       }
+      else
+       {
+         ex.X_add_number = ep->X_add_number;
+         ep->X_add_number = 0;
+         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,
+                      ADDRESS_ADD_INSN, "d,v,t", reg, reg, mips_gp_register);
+         macro_build ((char *) NULL, counter, ep, ADDRESS_LOAD_INSN,
+                      "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 (p, counter, ep, ADDRESS_LOAD_INSN, "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, ADDRESS_ADDI_INSN,
                       "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+
+         if (ex.X_add_number != 0)
+           {
+             if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+               as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+             ex.X_op = O_constant;
+             macro_build ((char *) NULL, counter, &ex, ADDRESS_ADDI_INSN,
+                          "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+           }
        }
     }
   else if (mips_pic == EMBEDDED_PIC)
@@ -3862,8 +4078,7 @@ load_address (counter, reg, ep, used_at)
       /* We always do
           addiu        $reg,$gp,<sym>          (BFD_RELOC_GPREL16)
        */
-      macro_build ((char *) NULL, counter, ep,
-                  HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
+      macro_build ((char *) NULL, counter, ep, ADDRESS_ADDI_INSN,
                   "t,r,j", reg, mips_gp_register, (int) BFD_RELOC_GPREL16);
     }
   else
@@ -3936,6 +4151,30 @@ 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:
@@ -3946,7 +4185,7 @@ macro (ip)
         sub v0,$zero,$a0
         */
 
-      mips_emit_delays (true);
+      mips_emit_delays (TRUE);
       ++mips_opts.noreorder;
       mips_any_noreorder = 1;
 
@@ -4403,20 +4642,20 @@ macro (ip)
          as_warn (_("Divide by zero."));
          if (mips_trap)
            macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "teq",
-                        "s,t", 0, 0);
+                        "s,t,q", 0, 0, 7);
          else
            macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "break",
                         "c", 7);
          return;
        }
 
-      mips_emit_delays (true);
+      mips_emit_delays (TRUE);
       ++mips_opts.noreorder;
       mips_any_noreorder = 1;
       if (mips_trap)
        {
          macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "teq",
-                      "s,t", treg, 0);
+                      "s,t,q", treg, 0, 7);
          macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                       dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
        }
@@ -4452,7 +4691,7 @@ macro (ip)
       if (mips_trap)
        {
          macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "teq",
-                      "s,t", sreg, AT);
+                      "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;
@@ -4515,7 +4754,7 @@ macro (ip)
          as_warn (_("Divide by zero."));
          if (mips_trap)
            macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "teq",
-                        "s,t", 0, 0);
+                        "s,t,q", 0, 0, 7);
          else
            macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "break",
                         "c", 7);
@@ -4565,13 +4804,13 @@ macro (ip)
       s = "ddivu";
       s2 = "mfhi";
     do_divu3:
-      mips_emit_delays (true);
+      mips_emit_delays (TRUE);
       ++mips_opts.noreorder;
       mips_any_noreorder = 1;
       if (mips_trap)
        {
          macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "teq",
-                      "s,t", treg, 0);
+                      "s,t,q", treg, 0, 7);
          macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "z,s,t",
                       sreg, treg);
          /* We want to close the noreorder block as soon as possible, so
@@ -4606,6 +4845,16 @@ macro (ip)
       if (! dbl && HAVE_64BIT_OBJECTS)
        as_warn (_("la used to load 64-bit address"));
 
+      if (offset_expr.X_op == O_constant
+         && offset_expr.X_add_number >= -0x8000
+         && offset_expr.X_add_number < 0x8000)
+       {
+         macro_build ((char *) NULL, &icnt, &offset_expr,
+                      (dbl || HAVE_64BIT_ADDRESSES) ? "daddiu" : "addiu",
+                      "t,r,j", treg, sreg, (int) BFD_RELOC_LO16);
+         return;
+       }
+
       if (treg == breg)
        {
          tempreg = AT;
@@ -4689,9 +4938,9 @@ macro (ip)
              daddiu    $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
              daddiu    $at,<sym>               (BFD_RELOC_LO16)
              dsll32    $tempreg,0
-             dadd      $tempreg,$tempreg,$at
+             daddu     $tempreg,$tempreg,$at
 
-           If $at is already in use, we use an path which is suboptimal
+           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)
@@ -4706,7 +4955,7 @@ macro (ip)
              /* We don't do GP optimization for now because RELAX_ENCODE can't
                 hold the data for such large chunks.  */
 
-             if (used_at == 0)
+             if (used_at == 0 && ! mips_opts.noat)
                {
                  macro_build (p, &icnt, &offset_expr, "lui", "t,u",
                               tempreg, (int) BFD_RELOC_MIPS_HIGHEST);
@@ -4718,8 +4967,8 @@ macro (ip)
                               AT, AT, (int) BFD_RELOC_LO16);
                  macro_build (p, &icnt, (expressionS *) NULL, "dsll32",
                               "d,w,<", tempreg, tempreg, 0);
-                 macro_build (p, &icnt, (expressionS *) NULL, "dadd", "d,v,t",
-                              tempreg, tempreg, AT);
+                 macro_build (p, &icnt, (expressionS *) NULL, "daddu",
+                              "d,v,t", tempreg, tempreg, AT);
                  used_at = 1;
                }
              else
@@ -4744,9 +4993,9 @@ macro (ip)
                  && ! nopic_need_relax (offset_expr.X_add_symbol, 1))
                {
                  frag_grow (20);
-                 macro_build ((char *) NULL, &icnt, &offset_expr, "addiu",
-                              "t,r,j", tempreg, mips_gp_register,
-                              (int) BFD_RELOC_GPREL16);
+                 macro_build ((char *) NULL, &icnt, &offset_expr,
+                              ADDRESS_ADDI_INSN, "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),
@@ -4755,11 +5004,11 @@ macro (ip)
              macro_build_lui (p, &icnt, &offset_expr, tempreg);
              if (p != NULL)
                p += 4;
-             macro_build (p, &icnt, &offset_expr, "addiu",
+             macro_build (p, &icnt, &offset_expr, ADDRESS_ADDI_INSN,
                           "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
            }
        }
-      else if (mips_pic == SVR4_PIC && ! mips_big_got)
+      else if (mips_pic == SVR4_PIC && ! mips_big_got && ! HAVE_NEWABI)
        {
          int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
 
@@ -4790,14 +5039,15 @@ macro (ip)
               addu     $tempreg,$tempreg,$at
             For a local symbol, we want the same instruction
             sequence, but we output a BFD_RELOC_LO16 reloc on the
-            addiu instruction.  */
+            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",
+         macro_build ((char *) NULL, &icnt, &offset_expr, ADDRESS_LOAD_INSN,
                       "t,o(b)", tempreg, lw_reloc_type, mips_gp_register);
          if (expr1.X_add_number == 0)
            {
@@ -4826,8 +5076,7 @@ macro (ip)
                  macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
                  p += 4;
                }
-             macro_build (p, &icnt, &expr1,
-                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
+             macro_build (p, &icnt, &expr1, ADDRESS_ADDI_INSN,
                           "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
              /* FIXME: If breg == 0, and the next instruction uses
                 $tempreg, then if this variant case is used an extra
@@ -4838,8 +5087,7 @@ macro (ip)
            {
              macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                           "nop", "");
-             macro_build ((char *) NULL, &icnt, &expr1,
-                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
+             macro_build ((char *) NULL, &icnt, &expr1, ADDRESS_ADDI_INSN,
                           "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
              frag_var (rs_machine_dependent, 0, 0,
                        RELAX_ENCODE (0, 0, -12, -4, 0, 0),
@@ -4863,8 +5111,7 @@ macro (ip)
                  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);
+                              ADDRESS_ADD_INSN, "d,v,t", treg, AT, breg);
                  breg = 0;
                  tempreg = treg;
                  off1 = -8;
@@ -4877,24 +5124,147 @@ macro (ip)
              macro_build_lui (NULL, &icnt, &expr1, AT);
              mips_optimize = hold_mips_optimize;
 
-             macro_build ((char *) NULL, &icnt, &expr1,
-                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
+             macro_build ((char *) NULL, &icnt, &expr1, ADDRESS_ADDI_INSN,
                           "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);
+                          ADDRESS_ADD_INSN, "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);
              used_at = 1;
            }
        }
-      else if (mips_pic == SVR4_PIC)
+      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;
+
+         /* 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
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_CALL16)
+
+            If we have a small constant, and this is a reference to
+            an external symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_DISP)
+              addiu    $tempreg,$tempreg,<constant>
+
+            If we have a large constant, and this is a reference to
+            an external symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_DISP)
+              lui      $at,<hiconstant>
+              addiu    $at,$at,<loconstant>
+              addu     $tempreg,$tempreg,$at
+
+            (*) Other assemblers seem to prefer GOT_PAGE/GOT_OFST for
+            local symbols, even though it introduces an additional
+            instruction.  */
+
+         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;
+             offset_expr.X_add_number = 0;
+
+             macro_build ((char *) NULL, &icnt, &offset_expr,
+                          ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+                          lw_reloc_type, mips_gp_register);
+
+             if (expr1.X_add_number >= -0x8000
+                 && expr1.X_add_number < 0x8000)
+               {
+                 macro_build ((char *) NULL, &icnt, &expr1,
+                              ADDRESS_ADDI_INSN, "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);
+               }
+             else if (IS_SEXT_32BIT_NUM (expr1.X_add_number))
+               {
+                 int dreg;
+
+                 /* If we are going to add in a base register, and the
+                    target register and the base register are the same,
+                    then we are using AT as a temporary register.  Since
+                    we want to load the constant into AT, we add our
+                    current AT (from the global offset table) and the
+                    register into the register now, and pretend we were
+                    not using a base register.  */
+                 if (breg != treg)
+                   dreg = tempreg;
+                 else
+                   {
+                     assert (tempreg == AT);
+                     macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                                  ADDRESS_ADD_INSN, "d,v,t", treg, AT, breg);
+                     dreg = treg;
+                     adj = 4;
+                   }
+
+                 macro_build_lui ((char *) NULL, &icnt, &expr1, AT);
+                 macro_build ((char *) NULL, &icnt, &expr1,
+                              ADDRESS_ADDI_INSN, "t,r,j", AT, AT,
+                              (int) BFD_RELOC_LO16);
+                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                              ADDRESS_ADD_INSN, "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);
+
+                 used_at = 1;
+               }
+             else
+               as_bad (_("PIC code offset overflow (max 32 signed bits)"));
+
+             offset_expr.X_add_number = expr1.X_add_number;
+
+             macro_build (p, &icnt, &offset_expr, ADDRESS_LOAD_INSN,
+                          "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT_DISP,
+                          mips_gp_register);
+             if (adj)
+               {
+                 macro_build (p + 4, &icnt, (expressionS *) NULL,
+                              ADDRESS_ADD_INSN, "d,v,t",
+                              treg, tempreg, breg);
+                 breg = 0;
+                 tempreg = treg;
+               }
+           }
+         else
+           {
+             macro_build ((char *) NULL, &icnt, &offset_expr,
+                          ADDRESS_LOAD_INSN, "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);
+           }
+
+         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);
+           }
+       }
+      else if (mips_pic == SVR4_PIC && ! HAVE_NEWABI)
        {
          int gpdel;
          char *p;
          int lui_reloc_type = (int) BFD_RELOC_MIPS_GOT_HI16;
          int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT_LO16;
+         int local_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
 
          /* This is the large GOT case.  If this is a reference to an
             external symbol, and there is no constant, we want
@@ -4935,11 +5305,12 @@ macro (ip)
               lui      $at,<hiconstant>
               addiu    $at,$at,<loconstant>    (BFD_RELOC_LO16)
               addu     $tempreg,$tempreg,$at
-            */
+         */
+
          expr1.X_add_number = offset_expr.X_add_number;
          offset_expr.X_add_number = 0;
          frag_grow (52);
-         if (reg_needs_delay (GP))
+         if (reg_needs_delay (mips_gp_register))
            gpdel = 4;
          else
            gpdel = 0;
@@ -4951,10 +5322,9 @@ macro (ip)
          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",
+                      ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg,
+                      mips_gp_register);
+         macro_build ((char *) NULL, &icnt, &offset_expr, ADDRESS_LOAD_INSN,
                       "t,o(b)", tempreg, lw_reloc_type, tempreg);
          if (expr1.X_add_number == 0)
            {
@@ -4985,8 +5355,7 @@ macro (ip)
            {
              macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                           "nop", "");
-             macro_build ((char *) NULL, &icnt, &expr1,
-                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
+             macro_build ((char *) NULL, &icnt, &expr1, ADDRESS_ADDI_INSN,
                           "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
 
              p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
@@ -5018,8 +5387,7 @@ macro (ip)
                  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);
+                              ADDRESS_ADD_INSN, "d,v,t", treg, AT, breg);
                  dreg = treg;
                  adj = 8;
                }
@@ -5031,12 +5399,10 @@ macro (ip)
              macro_build_lui (NULL, &icnt, &expr1, AT);
              mips_optimize = hold_mips_optimize;
 
-             macro_build ((char *) NULL, &icnt, &expr1,
-                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
+             macro_build ((char *) NULL, &icnt, &expr1, ADDRESS_ADDI_INSN,
                           "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);
+                          ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
 
              p = frag_var (rs_machine_dependent, 16 + gpdel + adj, 0,
                            RELAX_ENCODE (24 + adj, 16 + gpdel + adj, gpdel,
@@ -5052,119 +5418,246 @@ macro (ip)
          if (gpdel > 0)
            {
              /* This is needed because this instruction uses $gp, but
-                 the first instruction on the main stream does not.  */
+                the first instruction on the main stream does not.  */
              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,
-                      mips_gp_register);
+
+         macro_build (p, &icnt, &offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                      tempreg, local_reloc_type, mips_gp_register);
          p += 4;
          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",
+             macro_build (p, &icnt, &expr1, ADDRESS_ADDI_INSN,
                           "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
              /* FIXME: If add_number is 0, and there was no base
-                 register, the external symbol case ended with a load,
-                 so if the symbol turns out to not be external, and
-                 the next instruction uses tempreg, an unnecessary nop
-                 will be inserted.  */
+                register, the external symbol case ended with a load,
+                so if the symbol turns out to not be external, and
+                the next instruction uses tempreg, an unnecessary nop
+                will be inserted.  */
            }
          else
            {
              if (breg == treg)
                {
                  /* We must add in the base register now, as in the
-                     external symbol case.  */
+                    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);
+                              ADDRESS_ADD_INSN, "d,v,t", treg, AT, breg);
                  p += 4;
                  tempreg = treg;
                  /* We set breg to 0 because we have arranged to add
-                     it in in both cases.  */
+                    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",
+             macro_build (p, &icnt, &expr1, ADDRESS_ADDI_INSN,
                           "t,r,j", AT, AT, (int) BFD_RELOC_LO16);
              p += 4;
-             macro_build (p, &icnt, (expressionS *) NULL,
-                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
+             macro_build (p, &icnt, (expressionS *) NULL, ADDRESS_ADD_INSN,
                           "d,v,t", tempreg, tempreg, AT);
              p += 4;
            }
        }
-      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);
-       }
-      else
-       abort ();
-
-      if (breg != 0)
+      else if (mips_pic == SVR4_PIC && HAVE_NEWABI)
        {
-         char *s;
+         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;
 
-         if (mips_pic == EMBEDDED_PIC || mips_pic == NO_PIC)
-           s = (dbl || HAVE_64BIT_ADDRESSES) ? "daddu" : "addu";
-         else
-           s = HAVE_64BIT_ADDRESSES ? "daddu" : "addu";
+         /* 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
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_CALL_HI16)
+              add      $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_CALL_LO16)
 
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s,
-                      "d,v,t", treg, tempreg, breg);
-       }
+            If we have a small constant, and this is a reference to
+            an external symbol, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
+              add      $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+              addi     $tempreg,$tempreg,<constant>
 
-      if (! used_at)
-       return;
+            If we have a large constant, and this is a reference to
+            an external symbol, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
+              addu     $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+              lui      $at,<hiconstant>
+              addi     $at,$at,<loconstant>
+              add      $tempreg,$tempreg,$at
 
-      break;
+            If we have NewABI, and we know it's a local symbol, we want
+              lw       $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT_PAGE)
+              addiu    $reg,$reg,<sym>         (BFD_RELOC_MIPS_GOT_OFST)
+            otherwise we have to resort to GOT_HI16/GOT_LO16.  */
 
-    case M_J_A:
-      /* The j instruction may not be used in PIC code, since it
-        requires an absolute address.  We convert it to a b
-        instruction.  */
-      if (mips_pic == NO_PIC)
-       macro_build ((char *) NULL, &icnt, &offset_expr, "j", "a");
-      else
-       macro_build ((char *) NULL, &icnt, &offset_expr, "b", "p");
-      return;
+         frag_grow (40);
 
-      /* The jal instructions must be handled as macros because when
-        generating PIC code they expand to multi-instruction
-        sequences.  Normally they are simple instructions.  */
-    case M_JAL_1:
-      dreg = RA;
-      /* Fall through.  */
-    case M_JAL_2:
-      if (mips_pic == NO_PIC
-         || mips_pic == EMBEDDED_PIC)
-       macro_build ((char *) NULL, &icnt, (expressionS *) 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"));
+         frag_now->tc_frag_data.tc_fr_offset =
+           expr1.X_add_number = offset_expr.X_add_number;
+         offset_expr.X_add_number = 0;
 
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "jalr",
-                      "d,s", dreg, sreg);
-         if (! HAVE_NEWABI)
+         if (expr1.X_add_number == 0 && 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,
+                      ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg,
+                      mips_gp_register);
+         macro_build ((char *) NULL, &icnt, &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, ADDRESS_ADDI_INSN,
+                          "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);
+           }
+         else if (IS_SEXT_32BIT_NUM (expr1.X_add_number))
+           {
+             int dreg;
+
+             /* If we are going to add in a base register, and the
+                target register and the base register are the same,
+                then we are using AT as a temporary register.  Since
+                we want to load the constant into AT, we add our
+                current AT (from the global offset table) and the
+                register into the register now, and pretend we were
+                not using a base register.  */
+             if (breg != treg)
+               dreg = tempreg;
+             else
+               {
+                 assert (tempreg == AT);
+                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                              ADDRESS_ADD_INSN, "d,v,t", treg, AT, breg);
+                 dreg = treg;
+                 adj = 4;
+               }
+
+             /* 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, ADDRESS_ADDI_INSN,
+                          "t,r,j", AT, AT, (int) BFD_RELOC_LO16);
+             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                          ADDRESS_ADD_INSN, "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);
+
+             used_at = 1;
+           }
+         else
+           as_bad (_("PIC code offset overflow (max 32 signed bits)"));
+
+         offset_expr.X_add_number = expr1.X_add_number;
+         macro_build (p, &icnt, &offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+                      tempreg, (int) BFD_RELOC_MIPS_GOT_PAGE,
+                      mips_gp_register);
+         macro_build (p + 4, &icnt, &offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+                      tempreg, tempreg, (int) BFD_RELOC_MIPS_GOT_OFST);
+         if (adj)
+           {
+             macro_build (p + 8, &icnt, (expressionS *) 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, ADDRESS_ADDI_INSN,
+                       "t,r,j", tempreg, mips_gp_register,
+                      (int) BFD_RELOC_GPREL16);
+       }
+      else
+       abort ();
+
+      if (breg != 0)
+       {
+         char *s;
+
+         if (mips_pic == EMBEDDED_PIC || mips_pic == NO_PIC)
+           s = (dbl || HAVE_64BIT_ADDRESSES) ? "daddu" : "addu";
+         else
+           s = ADDRESS_ADD_INSN;
+
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s,
+                      "d,v,t", treg, tempreg, breg);
+       }
+
+      if (! used_at)
+       return;
+
+      break;
+
+    case M_J_A:
+      /* The j instruction may not be used in PIC code, since it
+        requires an absolute address.  We convert it to a b
+        instruction.  */
+      if (mips_pic == NO_PIC)
+       macro_build ((char *) NULL, &icnt, &offset_expr, "j", "a");
+      else
+       macro_build ((char *) NULL, &icnt, &offset_expr, "b", "p");
+      return;
+
+      /* The jal instructions must be handled as macros because when
+        generating PIC code they expand to multi-instruction
+        sequences.  Normally they are simple instructions.  */
+    case M_JAL_1:
+      dreg = RA;
+      /* Fall through.  */
+    case M_JAL_2:
+      if (mips_pic == NO_PIC
+         || mips_pic == EMBEDDED_PIC)
+       macro_build ((char *) NULL, &icnt, (expressionS *) 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);
+         if (! HAVE_NEWABI)
            {
              if (mips_cprestore_offset < 0)
                as_warn (_("No .cprestore pseudo-op used in PIC code"));
@@ -5183,10 +5676,10 @@ macro (ip)
                      mips_cprestore_valid = 1;
                    }
                  expr1.X_add_number = mips_cprestore_offset;
-                 macro_build ((char *) NULL, &icnt, &expr1,
-                              HAVE_32BIT_ADDRESSES ? "lw" : "ld", "t,o(b)",
-                              mips_gp_register, (int) BFD_RELOC_LO16,
-                              mips_frame_reg);
+                 macro_build_ldst_constoffset ((char *) NULL, &icnt, &expr1,
+                                               ADDRESS_LOAD_INSN,
+                                               mips_gp_register,
+                                               mips_frame_reg);
                }
            }
        }
@@ -5224,64 +5717,105 @@ macro (ip)
               addiu    $25,$25,<sym>           (BFD_RELOC_LO16)
               jalr     $ra,$25
               nop
-              lw $gp,cprestore($sp) */
-         frag_grow (40);
-         if (! mips_big_got)
+              lw $gp,cprestore($sp)
+
+            For NewABI, we use the same CALL16 or CALL_HI16/CALL_LO16
+            sequences above, minus nops, unless the symbol is local,
+            which enables us to use GOT_PAGE/GOT_OFST (big got) or
+            GOT_DISP.  */
+         if (HAVE_NEWABI)
            {
-             macro_build ((char *) NULL, &icnt, &offset_expr,
-                          HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                          "t,o(b)", PIC_CALL_REG,
-                          (int) BFD_RELOC_MIPS_CALL16, GP);
-             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                          "nop", "");
-             p = frag_var (rs_machine_dependent, 4, 0,
-                           RELAX_ENCODE (0, 4, -8, 0, 0, 0),
+             if (! mips_big_got)
+               {
+                 frag_grow (4);
+                 macro_build ((char *) NULL, &icnt, &offset_expr,
+                              ADDRESS_LOAD_INSN, "t,o(b)", PIC_CALL_REG,
+                              (int) 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);
+               }
+             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,
+                              ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
+                              PIC_CALL_REG, mips_gp_register);
+                 macro_build ((char *) NULL, &icnt, &offset_expr,
+                              ADDRESS_LOAD_INSN, "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, ADDRESS_LOAD_INSN,
+                              "t,o(b)", PIC_CALL_REG,
+                              (int) BFD_RELOC_MIPS_GOT_PAGE,
+                              mips_gp_register);
+                 macro_build (p + 4, &icnt, &offset_expr, ADDRESS_ADDI_INSN,
+                              "t,r,j", PIC_CALL_REG, PIC_CALL_REG,
+                              (int) BFD_RELOC_MIPS_GOT_OFST);
+               }
+
+             macro_build_jalr (icnt, &offset_expr);
            }
          else
            {
-             int gpdel;
-
-             if (reg_needs_delay (GP))
-               gpdel = 4;
+             frag_grow (40);
+             if (! mips_big_got)
+               {
+                 macro_build ((char *) NULL, &icnt, &offset_expr,
+                              ADDRESS_LOAD_INSN, "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);
+               }
              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, GP);
-             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)
                {
+                 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,
+                              ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
+                              PIC_CALL_REG, mips_gp_register);
+                 macro_build ((char *) NULL, &icnt, &offset_expr,
+                              ADDRESS_LOAD_INSN, "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, ADDRESS_LOAD_INSN,
+                              "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;
                }
-             macro_build (p, &icnt, &offset_expr,
-                          HAVE_32BIT_ADDRESSES ? "lw" : "ld",
-                          "t,o(b)", PIC_CALL_REG,
-                          (int) BFD_RELOC_MIPS_GOT16, GP);
-             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", PIC_CALL_REG, PIC_CALL_REG,
-                      (int) BFD_RELOC_LO16);
-         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                      "jalr", "s", PIC_CALL_REG);
-         if (! HAVE_NEWABI)
-           {
+             macro_build (p, &icnt, &offset_expr, ADDRESS_ADDI_INSN,
+                          "t,r,j", PIC_CALL_REG, PIC_CALL_REG,
+                          (int) BFD_RELOC_LO16);
+             macro_build_jalr (icnt, &offset_expr);
+
              if (mips_cprestore_offset < 0)
                as_warn (_("No .cprestore pseudo-op used in PIC code"));
              else
@@ -5302,10 +5836,10 @@ macro (ip)
                    macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                                 "nop", "");
                  expr1.X_add_number = mips_cprestore_offset;
-                 macro_build ((char *) NULL, &icnt, &expr1,
-                              HAVE_32BIT_ADDRESSES ? "lw" : "ld", "t,o(b)",
-                              mips_gp_register, (int) BFD_RELOC_LO16,
-                              mips_frame_reg);
+                 macro_build_ldst_constoffset ((char *) NULL, &icnt, &expr1,
+                                               ADDRESS_LOAD_INSN,
+                                               mips_gp_register,
+                                               mips_frame_reg);
                }
            }
        }
@@ -5605,20 +6139,39 @@ macro (ip)
             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.  This code should
+            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.
-          */
-         if (HAVE_64BIT_ADDRESSES
-             && !(offset_expr.X_op == O_constant
-                  && IS_SEXT_32BIT_NUM (offset_expr.X_add_number)))
+
+            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))
            {
              p = NULL;
 
              /* We don't do GP optimization for now because RELAX_ENCODE can't
                 hold the data for such large chunks.  */
 
-             if (used_at == 0)
+             if (used_at == 0 && ! mips_opts.noat)
                {
                  macro_build (p, &icnt, &offset_expr, "lui", "t,u",
                               tempreg, (int) BFD_RELOC_MIPS_HIGHEST);
@@ -5658,6 +6211,10 @@ macro (ip)
 
              return;
            }
+         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)
            {
@@ -5693,8 +6250,8 @@ macro (ip)
                {
                  frag_grow (28);
                  macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                              "d,v,t", tempreg, breg, mips_gp_register);
+                              ADDRESS_ADD_INSN, "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,
@@ -5704,8 +6261,7 @@ macro (ip)
              macro_build_lui (p, &icnt, &offset_expr, tempreg);
              if (p != NULL)
                p += 4;
-             macro_build (p, &icnt, (expressionS *) NULL,
-                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
+             macro_build (p, &icnt, (expressionS *) NULL, ADDRESS_ADD_INSN,
                           "d,v,t", tempreg, tempreg, breg);
              if (p != NULL)
                p += 4;
@@ -5716,6 +6272,7 @@ macro (ip)
       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
               lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
@@ -5726,6 +6283,11 @@ macro (ip)
               nop
               addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
               <op>     $treg,0($tempreg)
+
+            For NewABI, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_PAGE)
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_MIPS_GOT_OFST)
+
             If there is a base register, we add it to $tempreg before
             the <op>.  If there is a constant, we stick it in the
             <op> instruction.  We don't handle constants larger than
@@ -5733,30 +6295,45 @@ macro (ip)
             (actually, we could handle them for the subset of cases
             in which we are not using $at).  */
          assert (offset_expr.X_op == O_symbol);
+         if (HAVE_NEWABI)
+           {
+             macro_build ((char *) NULL, &icnt, &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,
+                            ADDRESS_ADD_INSN, "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;
+
+             break;
+           }
          expr1.X_add_number = offset_expr.X_add_number;
          offset_expr.X_add_number = 0;
          if (expr1.X_add_number < -0x8000
              || expr1.X_add_number >= 0x8000)
            as_bad (_("PIC code offset overflow (max 16 signed bits)"));
          frag_grow (20);
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld", "t,o(b)", tempreg,
-                      (int) BFD_RELOC_MIPS_GOT16, mips_gp_register);
+         macro_build ((char *) NULL, &icnt, &offset_expr, ADDRESS_LOAD_INSN,
+                      "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",
+         macro_build (p, &icnt, &offset_expr, ADDRESS_ADDI_INSN,
                       "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
          if (breg != 0)
            macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                        HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                        "d,v,t", tempreg, tempreg, breg);
+                        ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg, breg);
          macro_build ((char *) NULL, &icnt, &expr1, s, fmt, treg,
                       (int) BFD_RELOC_LO16, tempreg);
        }
-      else if (mips_pic == SVR4_PIC)
+      else if (mips_pic == SVR4_PIC && ! HAVE_NEWABI)
        {
          int gpdel;
          char *p;
@@ -5783,7 +6360,7 @@ 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 (GP))
+         if (reg_needs_delay (mips_gp_register))
            gpdel = 4;
          else
            gpdel = 0;
@@ -5791,10 +6368,9 @@ macro (ip)
          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",
+                      ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg,
+                      mips_gp_register);
+         macro_build ((char *) NULL, &icnt, &offset_expr, ADDRESS_LOAD_INSN,
                       "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT_LO16,
                       tempreg);
          p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
@@ -5805,22 +6381,68 @@ macro (ip)
              macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
              p += 4;
            }
-         macro_build (p, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
+         macro_build (p, &icnt, &offset_expr, ADDRESS_LOAD_INSN,
                       "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16,
                       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",
+         macro_build (p, &icnt, &offset_expr, ADDRESS_ADDI_INSN,
                       "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
          if (breg != 0)
            macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                        HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                        "d,v,t", tempreg, tempreg, breg);
+                        ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg, breg);
+         macro_build ((char *) NULL, &icnt, &expr1, s, fmt, treg,
+                      (int) 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
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+              <op>     $treg,<ofst>($tempreg)
+            Otherwise, for local symbols, we want:
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT_PAGE)
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_MIPS_GOT_OFST)  */
+         assert (offset_expr.X_op == O_symbol);
+         frag_now->tc_frag_data.tc_fr_offset =
+           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,
+                      ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg,
+                      mips_gp_register);
+         macro_build ((char *) NULL, &icnt, &offset_expr, ADDRESS_LOAD_INSN,
+                      "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT_LO16,
+                      tempreg);
+         if (breg != 0)
+           macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                        ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg, breg);
          macro_build ((char *) NULL, &icnt, &expr1, s, fmt, treg,
                       (int) BFD_RELOC_LO16, tempreg);
+
+         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, ADDRESS_LOAD_INSN, "t,o(b)",
+                      tempreg, (int) BFD_RELOC_MIPS_GOT_PAGE,
+                      mips_gp_register);
+         if (breg != 0)
+           macro_build (p + 4, &icnt, (expressionS *) NULL,
+                        ADDRESS_ADD_INSN, "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)
        {
@@ -5840,8 +6462,8 @@ macro (ip)
          else
            {
              macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                          "d,v,t", tempreg, breg, mips_gp_register);
+                          ADDRESS_ADD_INSN, "d,v,t", tempreg, breg,
+                          mips_gp_register);
              macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
                           treg, (int) BFD_RELOC_GPREL16, tempreg);
            }
@@ -5931,8 +6553,7 @@ macro (ip)
        }
       else if (mips_pic == SVR4_PIC)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
+         macro_build ((char *) NULL, &icnt, &offset_expr, ADDRESS_LOAD_INSN,
                       "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16,
                       mips_gp_register);
        }
@@ -5940,9 +6561,9 @@ macro (ip)
        {
          /* 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);
+         macro_build ((char *) NULL, &icnt, &offset_expr, ADDRESS_ADDI_INSN,
+                      "t,r,j", AT, mips_gp_register,
+                      (int) BFD_RELOC_GPREL16);
          offset_expr.X_op = O_constant;
          offset_expr.X_add_number = 0;
        }
@@ -6028,9 +6649,8 @@ macro (ip)
          assert (strcmp (s, RDATA_SECTION_NAME) == 0);
          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);
+                        ADDRESS_LOAD_INSN, "t,o(b)", AT,
+                        (int) BFD_RELOC_MIPS_GOT16, mips_gp_register);
          else
            {
              /* FIXME: This won't work for a 64 bit address.  */
@@ -6213,8 +6833,8 @@ macro (ip)
                {
                  frag_grow (36);
                  macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                              "d,v,t", AT, breg, mips_gp_register);
+                              ADDRESS_ADD_INSN, "d,v,t", AT, breg,
+                              mips_gp_register);
                  tempreg = AT;
                  off = 4;
                  used_at = 1;
@@ -6267,8 +6887,7 @@ macro (ip)
            p += 4;
          if (breg != 0)
            {
-             macro_build (p, &icnt, (expressionS *) NULL,
-                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
+             macro_build (p, &icnt, (expressionS *) NULL, ADDRESS_ADD_INSN,
                           "d,v,t", AT, breg, AT);
              if (p != NULL)
                p += 4;
@@ -6314,14 +6933,13 @@ macro (ip)
          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, &offset_expr, ADDRESS_LOAD_INSN,
+                      "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16,
+                      mips_gp_register);
          macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
          if (breg != 0)
            macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                        HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                        "d,v,t", AT, breg, AT);
+                        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,
@@ -6380,16 +6998,13 @@ macro (ip)
          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 ? "addu" : "daddu",
-                      "d,v,t", AT, AT, mips_gp_register);
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
+                      ADDRESS_ADD_INSN, "d,v,t", AT, AT, mips_gp_register);
+         macro_build ((char *) NULL, &icnt, &offset_expr, ADDRESS_LOAD_INSN,
                       "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT_LO16, AT);
          macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
          if (breg != 0)
            macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                        HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                        "d,v,t", AT, breg, AT);
+                        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,
@@ -6416,8 +7031,7 @@ macro (ip)
              macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
              p += 4;
            }
-         macro_build (p, &icnt, &offset_expr,
-                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
+         macro_build (p, &icnt, &offset_expr, ADDRESS_LOAD_INSN,
                       "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16,
                       mips_gp_register);
          p += 4;
@@ -6425,8 +7039,7 @@ macro (ip)
          p += 4;
          if (breg != 0)
            {
-             macro_build (p, &icnt, (expressionS *) NULL,
-                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
+             macro_build (p, &icnt, (expressionS *) NULL, ADDRESS_ADD_INSN,
                           "d,v,t", AT, breg, AT);
              p += 4;
            }
@@ -6465,8 +7078,8 @@ macro (ip)
          else
            {
              macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                          "d,v,t", AT, breg, mips_gp_register);
+                          ADDRESS_ADD_INSN, "d,v,t", AT, breg,
+                          mips_gp_register);
              tempreg = AT;
              used_at = 1;
            }
@@ -6637,7 +7250,7 @@ macro2 (ip)
       dbl = 1;
     case M_MULO:
     do_mulo:
-      mips_emit_delays (true);
+      mips_emit_delays (TRUE);
       ++mips_opts.noreorder;
       mips_any_noreorder = 1;
       if (imm)
@@ -6651,8 +7264,8 @@ macro2 (ip)
       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "mfhi", "d",
                   AT);
       if (mips_trap)
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "tne", "s,t",
-                    dreg, AT);
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "tne",
+                    "s,t,q", dreg, AT, 6);
       else
        {
          expr1.X_add_number = 8;
@@ -6677,7 +7290,7 @@ macro2 (ip)
       dbl = 1;
     case M_MULOU:
     do_mulou:
-      mips_emit_delays (true);
+      mips_emit_delays (TRUE);
       ++mips_opts.noreorder;
       mips_any_noreorder = 1;
       if (imm)
@@ -6690,8 +7303,8 @@ macro2 (ip)
       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "mflo", "d",
                   dreg);
       if (mips_trap)
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "tne", "s,t",
-                    AT, 0);
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "tne",
+                    "s,t,q", AT, 0, 6);
       else
        {
          expr1.X_add_number = 8;
@@ -6705,6 +7318,26 @@ macro2 (ip)
       break;
 
     case M_DROL:
+      if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_arch))
+       {
+         if (dreg == sreg)
+           {
+             tempreg = AT;
+             used_at = 1;
+           }
+         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 ((char *) NULL, &icnt, (expressionS *) NULL, "dsubu",
                   "d,v,t", AT, 0, treg);
       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "dsrlv",
@@ -6716,6 +7349,26 @@ macro2 (ip)
       break;
 
     case M_ROL:
+      if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_arch))
+       {
+         if (dreg == sreg)
+           {
+             tempreg = AT;
+             used_at = 1;
+           }
+         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 ((char *) NULL, &icnt, (expressionS *) NULL, "subu",
                   "d,v,t", AT, 0, treg);
       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "srlv",
@@ -6732,16 +7385,31 @@ macro2 (ip)
        char *l, *r;
 
        if (imm_expr.X_op != O_constant)
-         as_bad (_("rotate count too large"));
+         as_bad (_("Improper rotate count"));
        rot = imm_expr.X_add_number & 0x3f;
-       if (! rot)
-         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,
+       if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_arch))
+         {
+           rot = (64 - rot) & 0x3f;
+           if (rot >= 32)
+             macro_build ((char *) NULL, &icnt, NULL, "dror32",
+                          "d,w,<", dreg, sreg, rot - 32);
+           else
+             macro_build ((char *) NULL, &icnt, NULL, "dror",
+                          "d,w,<", dreg, sreg, rot);
+           return;
+         }
+       if (rot == 0)
+         {
+           macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "dsrl",
+                        "d,w,<", dreg, sreg, 0);
+           return;
+         }
+       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);
@@ -6753,10 +7421,20 @@ macro2 (ip)
        unsigned int rot;
 
        if (imm_expr.X_op != O_constant)
-         as_bad (_("rotate count too large"));
+         as_bad (_("Improper rotate count"));
        rot = imm_expr.X_add_number & 0x1f;
-       if (! rot)
-         break;
+       if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_arch))
+         {
+           macro_build ((char *) NULL, &icnt, NULL, "ror",
+                        "d,w,<", dreg, sreg, (32 - rot) & 0x1f);
+           return;
+         }
+       if (rot == 0)
+         {
+           macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "srl",
+                        "d,w,<", dreg, sreg, 0);
+           return;
+         }
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sll",
                     "d,w,<", AT, sreg, rot);
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "srl",
@@ -6767,6 +7445,12 @@ macro2 (ip)
       break;
 
     case M_DROR:
+      if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_arch))
+       {
+         macro_build ((char *) NULL, &icnt, NULL, "drorv",
+                      "d,t,s", dreg, sreg, treg);
+         return;
+       }
       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "dsubu",
                   "d,v,t", AT, 0, treg);
       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "dsllv",
@@ -6778,6 +7462,12 @@ macro2 (ip)
       break;
 
     case M_ROR:
+      if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_arch))
+       {
+         macro_build ((char *) NULL, &icnt, NULL, "rorv",
+                      "d,t,s", dreg, sreg, treg);
+         return;
+       }
       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "subu",
                   "d,v,t", AT, 0, treg);
       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sllv",
@@ -6794,10 +7484,24 @@ macro2 (ip)
        char *l, *r;
 
        if (imm_expr.X_op != O_constant)
-         as_bad (_("rotate count too large"));
+         as_bad (_("Improper rotate count"));
        rot = imm_expr.X_add_number & 0x3f;
-       if (! rot)
-         break;
+       if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_arch))
+         {
+           if (rot >= 32)
+             macro_build ((char *) NULL, &icnt, NULL, "dror32",
+                          "d,w,<", dreg, sreg, rot - 32);
+           else
+             macro_build ((char *) NULL, &icnt, NULL, "dror",
+                          "d,w,<", dreg, sreg, rot);
+           return;
+         }
+       if (rot == 0)
+         {
+           macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "dsrl",
+                        "d,w,<", dreg, sreg, 0);
+           return;
+         }
        r = (rot < 0x20) ? "dsrl" : "dsrl32";
        l = ((0x40 - rot) < 0x20) ? "dsll" : "dsll32";
        rot &= 0x1f;
@@ -6815,10 +7519,20 @@ macro2 (ip)
        unsigned int rot;
 
        if (imm_expr.X_op != O_constant)
-         as_bad (_("rotate count too large"));
+         as_bad (_("Improper rotate count"));
        rot = imm_expr.X_add_number & 0x1f;
-       if (! rot)
-         break;
+       if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_arch))
+         {
+           macro_build ((char *) NULL, &icnt, NULL, "ror",
+                        "d,w,<", dreg, sreg, rot);
+           return;
+         }
+       if (rot == 0)
+         {
+           macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "srl",
+                        "d,w,<", dreg, sreg, 0);
+           return;
+         }
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "srl",
                     "d,w,<", AT, sreg, rot);
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sll",
@@ -6891,8 +7605,7 @@ macro2 (ip)
          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);
+                      "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16);
          used_at = 0;
        }
       else
@@ -7151,7 +7864,7 @@ 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_emit_delays (TRUE);
       ++mips_opts.noreorder;
       mips_any_noreorder = 1;
       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "cfc1", "t,G",
@@ -7184,19 +7897,18 @@ macro2 (ip)
     ulh:
       if (offset_expr.X_add_number >= 0x7fff)
        as_bad (_("operand overflow"));
-      /* avoid load delay */
       if (! target_big_endian)
        ++offset_expr.X_add_number;
-      macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
+      macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", AT,
                   (int) 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)", AT,
+      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,<",
-                  treg, treg, 8);
+                  AT, AT, 8);
       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "or", "d,v,t",
                   treg, treg, AT);
       break;
@@ -7213,17 +7925,29 @@ macro2 (ip)
     ulw:
       if (offset_expr.X_add_number >= 0x8000 - off)
        as_bad (_("operand overflow"));
+      if (treg != breg)
+       tempreg = treg;
+      else
+       tempreg = AT;
       if (! target_big_endian)
        offset_expr.X_add_number += off;
-      macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
+      macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", tempreg,
                   (int) 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,
+      macro_build ((char *) NULL, &icnt, &offset_expr, s2, "t,o(b)", tempreg,
                   (int) BFD_RELOC_LO16, breg);
-      return;
+
+      /* If necessary, move the result in tempreg the final destination.  */
+      if (treg == tempreg)
+        return;
+      /* Protect second load's delay slot.  */
+      if (!gpr_interlocks)
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
+      move_register (&icnt, treg, tempreg);
+      break;
 
     case M_ULD_A:
       s = "ldl";
@@ -7239,8 +7963,7 @@ macro2 (ip)
       load_address (&icnt, AT, &offset_expr, &used_at);
       if (breg != 0)
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                    HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                    "d,v,t", AT, AT, breg);
+                    ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
       if (! target_big_endian)
        expr1.X_add_number = off;
       else
@@ -7261,8 +7984,7 @@ macro2 (ip)
       load_address (&icnt, AT, &offset_expr, &used_at);
       if (breg != 0)
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                    HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                    "d,v,t", AT, AT, breg);
+                    ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
       if (target_big_endian)
        expr1.X_add_number = 0;
       macro_build ((char *) NULL, &icnt, &expr1,
@@ -7335,8 +8057,7 @@ macro2 (ip)
       load_address (&icnt, AT, &offset_expr, &used_at);
       if (breg != 0)
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                    HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                    "d,v,t", AT, AT, breg);
+                    ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
       if (! target_big_endian)
        expr1.X_add_number = off;
       else
@@ -7356,8 +8077,7 @@ macro2 (ip)
       load_address (&icnt, AT, &offset_expr, &used_at);
       if (breg != 0)
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                    HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
-                    "d,v,t", AT, AT, breg);
+                    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,
@@ -7435,7 +8155,7 @@ mips16_macro (ip)
     case M_REM_3:
       s = "mfhi";
     do_div3:
-      mips_emit_delays (true);
+      mips_emit_delays (TRUE);
       ++mips_opts.noreorder;
       mips_any_noreorder = 1;
       macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
@@ -7470,7 +8190,7 @@ mips16_macro (ip)
       s = "ddivu";
       s2 = "mfhi";
     do_divu3:
-      mips_emit_delays (true);
+      mips_emit_delays (TRUE);
       ++mips_opts.noreorder;
       mips_any_noreorder = 1;
       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "0,x,y",
@@ -7667,6 +8387,20 @@ validate_mips_insn (opc)
       case ',': break;
       case '(': break;
       case ')': break;
+      case '+':
+       switch (c = *p++)
+         {
+         case 'A': USE_BITS (OP_MASK_SHAMT,    OP_SH_SHAMT);   break;
+         case 'B': USE_BITS (OP_MASK_INSMSB,   OP_SH_INSMSB);  break;
+         case 'C': USE_BITS (OP_MASK_EXTMSBD,  OP_SH_EXTMSBD); break;
+         case 'D': USE_BITS (OP_MASK_RD,       OP_SH_RD);
+                   USE_BITS (OP_MASK_SEL,      OP_SH_SEL);     break;
+         default:
+           as_bad (_("internal: bad mips opcode (unknown extension operand type `+%c'): %s %s"),
+                   c, opc->name, opc->args);
+           return 0;
+         }
+       break;
       case '<': USE_BITS (OP_MASK_SHAMT,       OP_SH_SHAMT);   break;
       case '>':        USE_BITS (OP_MASK_SHAMT,        OP_SH_SHAMT);   break;
       case 'A': break;
@@ -7679,14 +8413,21 @@ validate_mips_insn (opc)
       case 'H': USE_BITS (OP_MASK_SEL,         OP_SH_SEL);     break;
       case 'I': break;
       case 'J': USE_BITS (OP_MASK_CODE19,       OP_SH_CODE19);  break;
+      case 'K':        USE_BITS (OP_MASK_RD,           OP_SH_RD);      break;
       case 'L': break;
       case 'M':        USE_BITS (OP_MASK_CCC,          OP_SH_CCC);     break;
       case 'N':        USE_BITS (OP_MASK_BCC,          OP_SH_BCC);     break;
+      case 'O':        USE_BITS (OP_MASK_ALN,          OP_SH_ALN);     break;
+      case 'Q':        USE_BITS (OP_MASK_VSEL,         OP_SH_VSEL);
+               USE_BITS (OP_MASK_FT,           OP_SH_FT);      break;
       case 'R':        USE_BITS (OP_MASK_FR,           OP_SH_FR);      break;
       case 'S':        USE_BITS (OP_MASK_FS,           OP_SH_FS);      break;
       case 'T':        USE_BITS (OP_MASK_FT,           OP_SH_FT);      break;
       case 'V':        USE_BITS (OP_MASK_FS,           OP_SH_FS);      break;
       case 'W':        USE_BITS (OP_MASK_FT,           OP_SH_FT);      break;
+      case 'X':        USE_BITS (OP_MASK_FD,           OP_SH_FD);      break;
+      case 'Y':        USE_BITS (OP_MASK_FS,           OP_SH_FS);      break;
+      case 'Z':        USE_BITS (OP_MASK_FT,           OP_SH_FT);      break;
       case 'a':        USE_BITS (OP_MASK_TARGET,       OP_SH_TARGET);  break;
       case 'b':        USE_BITS (OP_MASK_RS,           OP_SH_RS);      break;
       case 'c':        USE_BITS (OP_MASK_CODE,         OP_SH_CODE);    break;
@@ -7711,6 +8452,10 @@ validate_mips_insn (opc)
       case 'P': USE_BITS (OP_MASK_PERFREG,     OP_SH_PERFREG); break;
       case 'U': USE_BITS (OP_MASK_RD,           OP_SH_RD);
                USE_BITS (OP_MASK_RT,           OP_SH_RT);      break;
+      case 'e': USE_BITS (OP_MASK_VECBYTE,     OP_SH_VECBYTE); break;
+      case '%': USE_BITS (OP_MASK_VECALIGN,    OP_SH_VECALIGN); break;
+      case '[': break;
+      case ']': break;
       default:
        as_bad (_("internal: bad mips opcode (unknown operand type `%c'): %s %s"),
                c, opc->name, opc->args);
@@ -7743,6 +8488,8 @@ mips_ip (str, ip)
   char *argsStart;
   unsigned int regno;
   unsigned int lastregno = 0;
+  unsigned int lastpos = 0;
+  unsigned int limlo, limhi;
   char *s_reset;
   char save_c = 0;
 
@@ -7799,22 +8546,24 @@ mips_ip (str, ip)
   argsStart = s;
   for (;;)
     {
-      boolean ok;
+      bfd_boolean ok;
 
       assert (strcmp (insn->name, str) == 0);
 
       if (OPCODE_IS_MEMBER (insn,
                            (mips_opts.isa
+                            | (file_ase_mips16 ? INSN_MIPS16 : 0)
+                            | (mips_opts.ase_mdmx ? INSN_MDMX : 0)
                             | (mips_opts.ase_mips3d ? INSN_MIPS3D : 0)),
                            mips_arch))
-       ok = true;
+       ok = TRUE;
       else
-       ok = false;
+       ok = FALSE;
 
       if (insn->pinfo != INSN_MACRO)
        {
          if (mips_arch == CPU_R4650 && (insn->pinfo & FP_D) != 0)
-           ok = false;
+           ok = FALSE;
        }
 
       if (! ok)
@@ -7830,11 +8579,15 @@ mips_ip (str, ip)
              if (!insn_error)
                {
                  static char buf[100];
-                 sprintf (buf,
-                          _("opcode not supported on this processor: %s (%s)"),
-                          mips_cpu_to_str (mips_arch),
-                          mips_isa_to_str (mips_opts.isa));
-
+                 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);
                  insn_error = buf;
                }
              if (save_c)
@@ -7848,7 +8601,10 @@ mips_ip (str, ip)
       insn_error = NULL;
       for (args = insn->args;; ++args)
        {
+         int is_mdmx;
+
          s += strspn (s, " \t");
+         is_mdmx = 0;
          switch (*args)
            {
            case '\0':          /* end of args */
@@ -7893,10 +8649,98 @@ mips_ip (str, ip)
                return;
 
            case ')':           /* these must match exactly */
+           case '[':
+           case ']':
              if (*s++ == *args)
                continue;
              break;
 
+           case '+':           /* Opcode extension character.  */
+             switch (*++args)
+               {
+               case 'A':               /* ins/ext position, becomes LSB.  */
+                 limlo = 0;
+                 limhi = 31;
+                 my_getExpression (&imm_expr, s);
+                 check_absolute_expr (ip, &imm_expr);
+                 if ((unsigned long) imm_expr.X_add_number < limlo
+                     || (unsigned long) imm_expr.X_add_number > limhi)
+                   {
+                     as_bad (_("Improper position (%lu)"),
+                             (unsigned long) imm_expr.X_add_number);
+                     imm_expr.X_add_number = limlo;
+                   }
+                 lastpos = imm_expr.X_add_number;
+                 ip->insn_opcode |= (imm_expr.X_add_number
+                                     & OP_MASK_SHAMT) << OP_SH_SHAMT;
+                 imm_expr.X_op = O_absent;
+                 s = expr_end;
+                 continue;
+
+               case 'B':               /* ins size, becomes MSB.  */
+                 limlo = 1;
+                 limhi = 32;
+                 my_getExpression (&imm_expr, s);
+                 check_absolute_expr (ip, &imm_expr);
+                 /* Check for negative input so that small negative numbers
+                    will not succeed incorrectly.  The checks against
+                    (pos+size) transitively check "size" itself,
+                    assuming that "pos" is reasonable.  */
+                 if ((long) imm_expr.X_add_number < 0
+                     || ((unsigned long) imm_expr.X_add_number
+                         + lastpos) < limlo
+                     || ((unsigned long) imm_expr.X_add_number
+                         + lastpos) > limhi)
+                   {
+                     as_bad (_("Improper insert size (%lu, position %lu)"),
+                             (unsigned long) imm_expr.X_add_number,
+                             (unsigned long) lastpos);
+                     imm_expr.X_add_number = limlo - lastpos;
+                   }
+                 ip->insn_opcode |= ((lastpos + imm_expr.X_add_number - 1)
+                                     & OP_MASK_INSMSB) << OP_SH_INSMSB;
+                 imm_expr.X_op = O_absent;
+                 s = expr_end;
+                 continue;
+
+               case 'C':               /* ext size, becomes MSBD.  */
+                 limlo = 1;
+                 limhi = 32;
+                 my_getExpression (&imm_expr, s);
+                 check_absolute_expr (ip, &imm_expr);
+                 /* Check for negative input so that small negative numbers
+                    will not succeed incorrectly.  The checks against
+                    (pos+size) transitively check "size" itself,
+                    assuming that "pos" is reasonable.  */
+                 if ((long) imm_expr.X_add_number < 0
+                     || ((unsigned long) imm_expr.X_add_number
+                         + lastpos) < limlo
+                     || ((unsigned long) imm_expr.X_add_number
+                         + lastpos) > limhi)
+                   {
+                     as_bad (_("Improper extract size (%lu, position %lu)"),
+                             (unsigned long) imm_expr.X_add_number,
+                             (unsigned long) lastpos);
+                     imm_expr.X_add_number = limlo - lastpos;
+                   }
+                 ip->insn_opcode |= ((imm_expr.X_add_number - 1)
+                                     & OP_MASK_EXTMSBD) << OP_SH_EXTMSBD;
+                 imm_expr.X_op = O_absent;
+                 s = expr_end;
+                 continue;
+
+               case 'D':
+                 /* +D is for disassembly only; never match.  */
+                 break;
+
+               default:
+                 as_bad (_("internal: bad mips opcode (unknown extension operand type `+%c'): %s %s"),
+                   *args, insn->name, insn->args);
+                 /* Further processing is fruitless.  */
+                 return;
+               }
+             break;
+
            case '<':           /* must be at least one digit */
              /*
               * According to the manual, if the shift amount is greater
@@ -7908,8 +8752,8 @@ mips_ip (str, ip)
              check_absolute_expr (ip, &imm_expr);
              if ((unsigned long) imm_expr.X_add_number > 31)
                {
-                 as_warn (_("Improper shift amount (%ld)"),
-                          (long) imm_expr.X_add_number);
+                 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;
@@ -7950,10 +8794,10 @@ mips_ip (str, ip)
            case 'c':           /* break code */
              my_getExpression (&imm_expr, s);
              check_absolute_expr (ip, &imm_expr);
-             if ((unsigned) imm_expr.X_add_number > 1023)
+             if ((unsigned long) imm_expr.X_add_number > 1023)
                {
-                 as_warn (_("Illegal break code (%ld)"),
-                          (long) imm_expr.X_add_number);
+                 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;
@@ -7964,10 +8808,10 @@ mips_ip (str, ip)
            case 'q':           /* lower break code */
              my_getExpression (&imm_expr, s);
              check_absolute_expr (ip, &imm_expr);
-             if ((unsigned) imm_expr.X_add_number > 1023)
+             if ((unsigned long) imm_expr.X_add_number > 1023)
                {
-                 as_warn (_("Illegal lower break code (%ld)"),
-                          (long) imm_expr.X_add_number);
+                 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;
@@ -7978,9 +8822,9 @@ mips_ip (str, ip)
            case 'B':           /* 20-bit syscall/break code.  */
              my_getExpression (&imm_expr, s);
              check_absolute_expr (ip, &imm_expr);
-             if ((unsigned) imm_expr.X_add_number > OP_MASK_CODE20)
-               as_warn (_("Illegal 20-bit code (%ld)"),
-                        (long) imm_expr.X_add_number);
+             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;
              imm_expr.X_op = O_absent;
              s = expr_end;
@@ -7991,8 +8835,8 @@ mips_ip (str, ip)
              check_absolute_expr (ip, &imm_expr);
              if ((unsigned long) imm_expr.X_add_number >= (1 << 25))
                {
-                 as_warn (_("Coproccesor code > 25 bits (%ld)"),
-                          (long) imm_expr.X_add_number);
+                 as_warn (_("Coproccesor code > 25 bits (%lu)"),
+                          (unsigned long) imm_expr.X_add_number);
                  imm_expr.X_add_number &= ((1 << 25) - 1);
                }
              ip->insn_opcode |= imm_expr.X_add_number;
@@ -8003,9 +8847,9 @@ mips_ip (str, ip)
            case 'J':           /* 19-bit wait code.  */
              my_getExpression (&imm_expr, s);
              check_absolute_expr (ip, &imm_expr);
-             if ((unsigned) imm_expr.X_add_number > OP_MASK_CODE19)
-               as_warn (_("Illegal 19-bit code (%ld)"),
-                        (long) imm_expr.X_add_number);
+             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;
              imm_expr.X_op = O_absent;
              s = expr_end;
@@ -8016,8 +8860,8 @@ mips_ip (str, ip)
              check_absolute_expr (ip, &imm_expr);
              if (imm_expr.X_add_number != 0 && imm_expr.X_add_number != 1)
                {
-                 as_warn (_("Invalid performance register (%ld)"),
-                          (long) imm_expr.X_add_number);
+                 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);
@@ -8034,6 +8878,7 @@ mips_ip (str, ip)
            case 'w':           /* both dest and target */
            case 'E':           /* coprocessor target register */
            case 'G':           /* coprocessor destination register */
+           case 'K':           /* 'rdhwr' destination register */
            case 'x':           /* ignore register name */
            case 'z':           /* must be zero register */
            case 'U':           /* destination register (clo/clz).  */
@@ -8055,11 +8900,16 @@ mips_ip (str, ip)
                      if (regno > 31)
                        as_bad (_("Invalid register number (%d)"), regno);
                    }
-                 else if (*args == 'E' || *args == 'G')
+                 else if (*args == 'E' || *args == 'G' || *args == 'K')
                    goto notreg;
                  else
                    {
-                     if (s[1] == 'f' && s[2] == 'p')
+                     if (s[1] == 'r' && s[2] == 'a')
+                       {
+                         s += 3;
+                         regno = RA;
+                       }
+                     else if (s[1] == 'f' && s[2] == 'p')
                        {
                          s += 3;
                          regno = FP;
@@ -8089,6 +8939,11 @@ mips_ip (str, ip)
                          s += 4;
                          regno = KT1;
                        }
+                     else if (s[1] == 'z' && s[2] == 'e' && s[3] == 'r' && s[4] == 'o')
+                       {
+                         s += 5;
+                         regno = ZERO;
+                       }
                      else if (itbl_have_entries)
                        {
                          char *p, *n;
@@ -8119,7 +8974,8 @@ mips_ip (str, ip)
                  if (regno == AT
                      && ! mips_opts.noat
                      && *args != 'E'
-                     && *args != 'G')
+                     && *args != 'G'
+                     && *args != 'K')
                    as_warn (_("Used $at without \".set noat\""));
                  c = *args;
                  if (*s == ' ')
@@ -8149,6 +9005,7 @@ mips_ip (str, ip)
                      break;
                    case 'd':
                    case 'G':
+                   case 'K':
                      ip->insn_opcode |= regno << OP_SH_RD;
                      break;
                    case 'U':
@@ -8197,6 +9054,47 @@ mips_ip (str, ip)
                }
              break;
 
+           case 'O':           /* MDMX alignment immediate constant.  */
+             my_getExpression (&imm_expr, s);
+             check_absolute_expr (ip, &imm_expr);
+             if ((unsigned long) imm_expr.X_add_number > OP_MASK_ALN)
+               {
+                 as_warn ("Improper align amount (%ld), using low bits",
+                          (long) imm_expr.X_add_number);
+                 imm_expr.X_add_number &= OP_MASK_ALN;
+               }
+             ip->insn_opcode |= imm_expr.X_add_number << OP_SH_ALN;
+             imm_expr.X_op = O_absent;
+             s = expr_end;
+             continue;
+
+           case 'Q':           /* MDMX vector, element sel, or const.  */
+             if (s[0] != '$')
+               {
+                 /* MDMX Immediate.  */
+                 my_getExpression (&imm_expr, s);
+                 check_absolute_expr (ip, &imm_expr);
+                 if ((unsigned long) imm_expr.X_add_number > OP_MASK_FT)
+                   {
+                     as_warn (_("Invalid MDMX Immediate (%ld)"),
+                              (long) imm_expr.X_add_number);
+                     imm_expr.X_add_number &= OP_MASK_FT;
+                   }
+                 imm_expr.X_add_number &= OP_MASK_FT;
+                 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;
+               }
+             /* Not MDMX Immediate.  Fall through.  */
+           case 'X':           /* MDMX destination register.  */
+           case 'Y':           /* MDMX source register.  */
+           case 'Z':           /* MDMX target register.  */
+             is_mdmx = 1;
            case 'D':           /* floating point destination register */
            case 'S':           /* floating point source register */
            case 'T':           /* floating point target register */
@@ -8204,8 +9102,11 @@ mips_ip (str, ip)
            case 'V':
            case 'W':
              s_reset = s;
-             if (s[0] == '$' && s[1] == 'f'
-                 && ISDIGIT (s[2]))
+             /* Accept $fN for FP and MDMX register numbers, and in
+                 addition accept $vN for MDMX register numbers.  */
+             if ((s[0] == '$' && s[1] == 'f' && ISDIGIT (s[2]))
+                 || (is_mdmx != 0 && s[0] == '$' && s[1] == 'v'
+                     && ISDIGIT (s[2])))
                {
                  s += 2;
                  regno = 0;
@@ -8246,14 +9147,51 @@ mips_ip (str, ip)
                  switch (c)
                    {
                    case 'D':
+                   case 'X':
                      ip->insn_opcode |= regno << OP_SH_FD;
                      break;
                    case 'V':
                    case 'S':
+                   case 'Y':
                      ip->insn_opcode |= regno << OP_SH_FS;
                      break;
+                   case 'Q':
+                     /* This is like 'Z', but also needs to fix the MDMX
+                        vector/scalar select bits.  Note that the
+                        scalar immediate case is handled above.  */
+                     if (*s == '[')
+                       {
+                         int is_qh = (ip->insn_opcode & (1 << OP_SH_VSEL));
+                         int max_el = (is_qh ? 3 : 7);
+                         s++;
+                         my_getExpression(&imm_expr, s);
+                         check_absolute_expr (ip, &imm_expr);
+                         s = expr_end;
+                         if (imm_expr.X_add_number > max_el)
+                           as_bad(_("Bad element selector %ld"),
+                                  (long) imm_expr.X_add_number);
+                         imm_expr.X_add_number &= max_el;
+                         ip->insn_opcode |= (imm_expr.X_add_number
+                                             << (OP_SH_VSEL +
+                                                 (is_qh ? 2 : 1)));
+                         if (*s != ']')
+                           as_warn(_("Expecting ']' found '%s'"), s);
+                         else
+                           s++;
+                       }
+                     else
+                        {
+                          if (ip->insn_opcode & (OP_MASK_VSEL << OP_SH_VSEL))
+                            ip->insn_opcode |= (MDMX_FMTSEL_VEC_QH
+                                               << OP_SH_VSEL);
+                         else
+                           ip->insn_opcode |= (MDMX_FMTSEL_VEC_OB <<
+                                               OP_SH_VSEL);
+                       }
+                      /* Fall through */
                    case 'W':
                    case 'T':
+                   case 'Z':
                      ip->insn_opcode |= regno << OP_SH_FT;
                      break;
                    case 'R':
@@ -8498,182 +9436,82 @@ mips_ip (str, ip)
            case 'i':           /* 16 bit unsigned immediate */
            case 'j':           /* 16 bit signed immediate */
              *imm_reloc = BFD_RELOC_LO16;
-             c = my_getSmallExpression (&imm_expr, s);
-             if (c != S_EX_NONE)
-               {
-                 if (c != S_EX_LO)
-                   {
-                     if (imm_expr.X_op == O_constant)
-                       imm_expr.X_add_number =
-                         (imm_expr.X_add_number >> 16) & 0xffff;
-#ifdef OBJ_ELF
-                     else if (c == S_EX_HIGHEST)
-                       *imm_reloc = BFD_RELOC_MIPS_HIGHEST;
-                     else if (c == S_EX_HIGHER)
-                       *imm_reloc = BFD_RELOC_MIPS_HIGHER;
-                     else if (c == S_EX_GP_REL)
-                       {
-                         /* This occurs in NewABI only.  */
-                         c = my_getSmallExpression (&imm_expr, s);
-                         if (c != S_EX_NEG)
-                           as_bad (_("bad composition of relocations"));
-                         else
-                           {
-                             c = my_getSmallExpression (&imm_expr, s);
-                             if (c != S_EX_LO)
-                               as_bad (_("bad composition of relocations"));
-                             else
-                               {
-                                 imm_reloc[0] = BFD_RELOC_GPREL16;
-                                 imm_reloc[1] = BFD_RELOC_MIPS_SUB;
-                                 imm_reloc[2] = BFD_RELOC_LO16;
-                               }
-                           }
-                       }
-#endif
-                     else if (c == S_EX_HI)
-                       {
-                         *imm_reloc = BFD_RELOC_HI16_S;
-                         imm_unmatched_hi = true;
-                       }
-                     else
-                       *imm_reloc = BFD_RELOC_HI16;
-                   }
-                 else if (imm_expr.X_op == O_constant)
-                   imm_expr.X_add_number &= 0xffff;
-               }
-             if (*args == 'i')
-               {
-                 if ((c == S_EX_NONE && imm_expr.X_op != O_constant)
-                     || ((imm_expr.X_add_number < 0
-                          || imm_expr.X_add_number >= 0x10000)
-                         && imm_expr.X_op == O_constant))
-                   {
-                     if (insn + 1 < &mips_opcodes[NUMOPCODES] &&
-                         !strcmp (insn->name, insn[1].name))
-                       break;
-                     if (imm_expr.X_op == O_constant
-                         || imm_expr.X_op == O_big)
-                       as_bad (_("16 bit expression not in range 0..65535"));
-                   }
-               }
-             else
+             if (my_getSmallExpression (&imm_expr, imm_reloc, s) == 0)
                {
                  int more;
-                 offsetT max;
-
-                 /* The upper bound should be 0x8000, but
-                    unfortunately the MIPS assembler accepts numbers
-                    from 0x8000 to 0xffff and sign extends them, and
-                    we want to be compatible.  We only permit this
-                    extended range for an instruction which does not
-                    provide any further alternates, since those
-                    alternates may handle other cases.  People should
-                    use the numbers they mean, rather than relying on
-                    a mysterious sign extension.  */
-                 more = (insn + 1 < &mips_opcodes[NUMOPCODES] &&
-                         strcmp (insn->name, insn[1].name) == 0);
-                 if (more)
-                   max = 0x8000;
+                 offsetT minval, maxval;
+
+                 more = (insn + 1 < &mips_opcodes[NUMOPCODES]
+                         && strcmp (insn->name, insn[1].name) == 0);
+
+                 /* If the expression was written as an unsigned number,
+                    only treat it as signed if there are no more
+                    alternatives.  */
+                 if (more
+                     && *args == 'j'
+                     && sizeof (imm_expr.X_add_number) <= 4
+                     && imm_expr.X_op == O_constant
+                     && imm_expr.X_add_number < 0
+                     && imm_expr.X_unsigned
+                     && HAVE_64BIT_GPRS)
+                   break;
+
+                 /* For compatibility with older assemblers, we accept
+                    0x8000-0xffff as signed 16-bit numbers when only
+                    signed numbers are allowed.  */
+                 if (*args == 'i')
+                   minval = 0, maxval = 0xffff;
+                 else if (more)
+                   minval = -0x8000, maxval = 0x7fff;
                  else
-                   max = 0x10000;
-                 if ((c == S_EX_NONE && imm_expr.X_op != O_constant)
-                     || ((imm_expr.X_add_number < -0x8000
-                          || imm_expr.X_add_number >= max)
-                         && imm_expr.X_op == O_constant)
-                     || (more
-                         && imm_expr.X_add_number < 0
-                         && HAVE_64BIT_GPRS
-                         && imm_expr.X_unsigned
-                         && sizeof (imm_expr.X_add_number) <= 4))
+                   minval = -0x8000, maxval = 0xffff;
+
+                 if (imm_expr.X_op != O_constant
+                     || imm_expr.X_add_number < minval
+                     || imm_expr.X_add_number > maxval)
                    {
                      if (more)
                        break;
                      if (imm_expr.X_op == O_constant
                          || imm_expr.X_op == O_big)
-                       as_bad (_("16 bit expression not in range -32768..32767"));
+                       as_bad (_("expression out of range"));
                    }
                }
              s = expr_end;
              continue;
 
            case 'o':           /* 16 bit offset */
-             c = my_getSmallExpression (&offset_expr, s);
+             /* Check whether there is only a single bracketed expression
+                left.  If so, it must be the base register and the
+                constant must be zero.  */
+             if (*s == '(' && strchr (s + 1, '(') == 0)
+               {
+                 offset_expr.X_op = O_constant;
+                 offset_expr.X_add_number = 0;
+                 continue;
+               }
 
              /* If this value won't fit into a 16 bit offset, then go
                 find a macro that will generate the 32 bit offset
                 code pattern.  */
-             if (c == S_EX_NONE
+             if (my_getSmallExpression (&offset_expr, offset_reloc, s) == 0
                  && (offset_expr.X_op != O_constant
                      || offset_expr.X_add_number >= 0x8000
                      || offset_expr.X_add_number < -0x8000))
                break;
 
-             if (c == S_EX_HI)
-               {
-                 if (offset_expr.X_op != O_constant)
-                   break;
-                 offset_expr.X_add_number =
-                   (offset_expr.X_add_number >> 16) & 0xffff;
-               }
-             *offset_reloc = BFD_RELOC_LO16;
              s = expr_end;
              continue;
 
            case 'p':           /* pc relative offset */
-             if (mips_pic == EMBEDDED_PIC)
-               *offset_reloc = BFD_RELOC_16_PCREL_S2;
-             else
-               *offset_reloc = BFD_RELOC_16_PCREL;
+             *offset_reloc = BFD_RELOC_16_PCREL_S2;
              my_getExpression (&offset_expr, s);
              s = expr_end;
              continue;
 
            case 'u':           /* upper 16 bits */
-             c = my_getSmallExpression (&imm_expr, s);
-             *imm_reloc = BFD_RELOC_LO16;
-             if (c != S_EX_NONE)
-               {
-                 if (c != S_EX_LO)
-                   {
-                     if (imm_expr.X_op == O_constant)
-                       imm_expr.X_add_number =
-                         (imm_expr.X_add_number >> 16) & 0xffff;
-                     else if (c == S_EX_HI)
-                       {
-                         *imm_reloc = BFD_RELOC_HI16_S;
-                         imm_unmatched_hi = true;
-                       }
-#ifdef OBJ_ELF
-                     else if (c == S_EX_HIGHEST)
-                       *imm_reloc = BFD_RELOC_MIPS_HIGHEST;
-                     else if (c == S_EX_GP_REL)
-                       {
-                         /* This occurs in NewABI only.  */
-                         c = my_getSmallExpression (&imm_expr, s);
-                         if (c != S_EX_NEG)
-                           as_bad (_("bad composition of relocations"));
-                         else
-                           {
-                             c = my_getSmallExpression (&imm_expr, s);
-                             if (c != S_EX_HI)
-                               as_bad (_("bad composition of relocations"));
-                             else
-                               {
-                                 imm_reloc[0] = BFD_RELOC_GPREL16;
-                                 imm_reloc[1] = BFD_RELOC_MIPS_SUB;
-                                 imm_reloc[2] = BFD_RELOC_HI16_S;
-                               }
-                           }
-                       }
-#endif
-                     else
-                       *imm_reloc = BFD_RELOC_HI16;
-                   }
-                 else if (imm_expr.X_op == O_constant)
-                   imm_expr.X_add_number &= 0xffff;
-               }
-             if (imm_expr.X_op == O_constant
+             if (my_getSmallExpression (&imm_expr, imm_reloc, s) == 0
+                 && imm_expr.X_op == O_constant
                  && (imm_expr.X_add_number < 0
                      || imm_expr.X_add_number >= 0x10000))
                as_bad (_("lui expression not in range 0..65535"));
@@ -8729,6 +9567,41 @@ mips_ip (str, ip)
              ip->insn_opcode |= c;
              continue;
 
+           case 'e':
+             /* Must be at least one digit.  */
+             my_getExpression (&imm_expr, s);
+             check_absolute_expr (ip, &imm_expr);
+
+             if ((unsigned long) imm_expr.X_add_number
+                 > (unsigned long) OP_MASK_VECBYTE)
+               {
+                 as_bad (_("bad byte vector index (%ld)"),
+                          (long) imm_expr.X_add_number);
+                 imm_expr.X_add_number = 0;
+               }
+
+             ip->insn_opcode |= imm_expr.X_add_number << OP_SH_VECBYTE;
+             imm_expr.X_op = O_absent;
+             s = expr_end;
+             continue;
+
+           case '%':
+             my_getExpression (&imm_expr, s);
+             check_absolute_expr (ip, &imm_expr);
+
+             if ((unsigned long) imm_expr.X_add_number
+                 > (unsigned long) OP_MASK_VECALIGN)
+               {
+                 as_bad (_("bad byte vector index (%ld)"),
+                          (long) imm_expr.X_add_number);
+                 imm_expr.X_add_number = 0;
+               }
+
+             ip->insn_opcode |= imm_expr.X_add_number << OP_SH_VECALIGN;
+             imm_expr.X_op = O_absent;
+             s = expr_end;
+             continue;
+
            default:
              as_bad (_("bad char = '%c'\n"), *args);
              internalError ();
@@ -8773,8 +9646,8 @@ mips16_ip (str, ip)
 
   insn_error = NULL;
 
-  mips16_small = false;
-  mips16_ext = false;
+  mips16_small = FALSE;
+  mips16_ext = FALSE;
 
   for (s = str; ISLOWER (*s); ++s)
     ;
@@ -8791,14 +9664,14 @@ mips16_ip (str, ip)
       if (s[1] == 't' && s[2] == ' ')
        {
          *s = '\0';
-         mips16_small = true;
+         mips16_small = TRUE;
          s += 3;
          break;
        }
       else if (s[1] == 'e' && s[2] == ' ')
        {
          *s = '\0';
-         mips16_ext = true;
+         mips16_ext = TRUE;
          s += 3;
          break;
        }
@@ -8809,7 +9682,7 @@ mips16_ip (str, ip)
     }
 
   if (mips_opts.noautoextend && ! mips16_ext)
-    mips16_small = true;
+    mips16_small = TRUE;
 
   if ((insn = (struct mips_opcode *) hash_find (mips16_op_hash, str)) == NULL)
     {
@@ -8824,7 +9697,7 @@ mips16_ip (str, ip)
 
       ip->insn_mo = insn;
       ip->insn_opcode = insn->match;
-      ip->use_extend = false;
+      ip->use_extend = FALSE;
       imm_expr.X_op = O_absent;
       imm_reloc[0] = BFD_RELOC_UNUSED;
       imm_reloc[1] = BFD_RELOC_UNUSED;
@@ -8856,7 +9729,7 @@ mips16_ip (str, ip)
                      && insn->pinfo != INSN_MACRO)
                    {
                      mips16_immed (NULL, 0, *imm_reloc - BFD_RELOC_UNUSED,
-                                   imm_expr.X_add_number, true, mips16_small,
+                                   imm_expr.X_add_number, TRUE, mips16_small,
                                    mips16_ext, &ip->insn_opcode,
                                    &ip->use_extend, &ip->extend);
                      imm_expr.X_op = O_absent;
@@ -8931,7 +9804,12 @@ mips16_ip (str, ip)
                }
              else
                {
-                 if (s[1] == 'f' && s[2] == 'p')
+                 if (s[1] == 'r' && s[2] == 'a')
+                   {
+                     s += 3;
+                     regno = RA;
+                   }
+                 else if (s[1] == 'f' && s[2] == 'p')
                    {
                      s += 3;
                      regno = FP;
@@ -8961,6 +9839,11 @@ mips16_ip (str, ip)
                      s += 4;
                      regno = KT1;
                    }
+                 else if (s[1] == 'z' && s[2] == 'e' && s[3] == 'r' && s[4] == 'o')
+                   {
+                     s += 5;
+                     regno = ZERO;
+                   }
                  else
                    break;
                }
@@ -9084,10 +9967,10 @@ mips16_ip (str, ip)
                  my_getExpression (&imm_expr, s + sizeof "%gprel" - 1);
                  if (imm_expr.X_op == O_symbol)
                    {
-                     mips16_ext = true;
+                     mips16_ext = TRUE;
                      *imm_reloc = BFD_RELOC_MIPS16_GPREL;
                      s = expr_end;
-                     ip->use_extend = true;
+                     ip->use_extend = TRUE;
                      ip->extend = 0;
                      continue;
                    }
@@ -9363,16 +10246,16 @@ mips16_immed (file, line, type, val, warn, small, ext, insn, use_extend,
      unsigned int line;
      int type;
      offsetT val;
-     boolean warn;
-     boolean small;
-     boolean ext;
+     bfd_boolean warn;
+     bfd_boolean small;
+     bfd_boolean ext;
      unsigned long *insn;
-     boolean *use_extend;
+     bfd_boolean *use_extend;
      unsigned short *extend;
 {
   register const struct mips16_immed_operand *op;
   int mintiny, maxtiny;
-  boolean needext;
+  bfd_boolean needext;
 
   op = mips16_immed_operands;
   while (op->type != type)
@@ -9407,9 +10290,9 @@ mips16_immed (file, line, type, val, warn, small, ext, insn, use_extend,
   if ((val & ((1 << op->shift) - 1)) != 0
       || val < (mintiny << op->shift)
       || val > (maxtiny << op->shift))
-    needext = true;
+    needext = TRUE;
   else
-    needext = false;
+    needext = FALSE;
 
   if (warn && ext && ! needext)
     as_warn_where (file, line,
@@ -9421,7 +10304,7 @@ mips16_immed (file, line, type, val, warn, small, ext, insn, use_extend,
     {
       int insnval;
 
-      *use_extend = false;
+      *use_extend = FALSE;
       insnval = ((val >> op->shift) & ((1 << op->nbits) - 1));
       insnval <<= op->op_shift;
       *insn |= insnval;
@@ -9445,7 +10328,7 @@ mips16_immed (file, line, type, val, warn, small, ext, insn, use_extend,
        as_bad_where (file, line,
                      _("operand value out of range for instruction"));
 
-      *use_extend = true;
+      *use_extend = TRUE;
       if (op->extbits == 16)
        {
          extval = ((val >> 11) & 0x1f) | (val & 0x7e0);
@@ -9467,222 +10350,127 @@ mips16_immed (file, line, type, val, warn, small, ext, insn, use_extend,
     }
 }
 \f
-static struct percent_op_match
+static const struct percent_op_match
 {
-   const char *str;
-   const enum small_ex_type type;
+  const char *str;
+  bfd_reloc_code_real_type reloc;
 } percent_op[] =
 {
-  {"%lo", S_EX_LO},
+  {"%lo", BFD_RELOC_LO16},
 #ifdef OBJ_ELF
-  {"%call_hi", S_EX_CALL_HI},
-  {"%call_lo", S_EX_CALL_LO},
-  {"%call16", S_EX_CALL16},
-  {"%got_disp", S_EX_GOT_DISP},
-  {"%got_page", S_EX_GOT_PAGE},
-  {"%got_ofst", S_EX_GOT_OFST},
-  {"%got_hi", S_EX_GOT_HI},
-  {"%got_lo", S_EX_GOT_LO},
-  {"%got", S_EX_GOT},
-  {"%gp_rel", S_EX_GP_REL},
-  {"%half", S_EX_HALF},
-  {"%highest", S_EX_HIGHEST},
-  {"%higher", S_EX_HIGHER},
-  {"%neg", S_EX_NEG},
+  {"%call_hi", BFD_RELOC_MIPS_CALL_HI16},
+  {"%call_lo", BFD_RELOC_MIPS_CALL_LO16},
+  {"%call16", BFD_RELOC_MIPS_CALL16},
+  {"%got_disp", BFD_RELOC_MIPS_GOT_DISP},
+  {"%got_page", BFD_RELOC_MIPS_GOT_PAGE},
+  {"%got_ofst", BFD_RELOC_MIPS_GOT_OFST},
+  {"%got_hi", BFD_RELOC_MIPS_GOT_HI16},
+  {"%got_lo", BFD_RELOC_MIPS_GOT_LO16},
+  {"%got", BFD_RELOC_MIPS_GOT16},
+  {"%gp_rel", BFD_RELOC_GPREL16},
+  {"%half", BFD_RELOC_16},
+  {"%highest", BFD_RELOC_MIPS_HIGHEST},
+  {"%higher", BFD_RELOC_MIPS_HIGHER},
+  {"%neg", BFD_RELOC_MIPS_SUB},
 #endif
-  {"%hi", S_EX_HI}
+  {"%hi", BFD_RELOC_HI16_S}
 };
 
-/* Parse small expression input.  STR gets adjusted to eat up whitespace.
-   It detects valid "%percent_op(...)" and "($reg)" strings.  Percent_op's
-   can be nested, this is handled by blanking the innermost, parsing the
-   rest by subsequent calls.  */
 
-static int
-my_getSmallParser (str, len, nestlevel)
+/* 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;
-     unsigned int *len;
-     int *nestlevel;
+     bfd_reloc_code_real_type *reloc;
 {
-  *len = 0;
-  *str += strspn (*str, " \t");
-  /* Check for expression in parentheses.  */
-  if (**str == '(')
-    {
-      char *b = *str + 1 + strspn (*str + 1, " \t");
-      char *e;
-
-      /* Check for base register.  */
-      if (b[0] == '$')
-       {
-         if (strchr (b, ')')
-             && (e = b + strcspn (b, ") \t"))
-             && e - b > 1 && e - b < 4)
-           {
-             if ((e - b == 3
-                  && ((b[1] == 'f' && b[2] == 'p')
-                      || (b[1] == 's' && b[2] == 'p')
-                      || (b[1] == 'g' && b[2] == 'p')
-                      || (b[1] == 'a' && b[2] == 't')
-                      || (ISDIGIT (b[1])
-                          && ISDIGIT (b[2]))))
-                 || (ISDIGIT (b[1])))
-               {
-                 *len = strcspn (*str, ")") + 1;
-                 return S_EX_REGISTER;
-               }
-           }
-       }
-      /* Check for percent_op (in parentheses).  */
-      else if (b[0] == '%')
-       {
-         *str = b;
-         return my_getPercentOp (str, len, nestlevel);
-       }
+  size_t i;
 
-      /* Some other expression in the parentheses, which can contain
-        parentheses itself. Attempt to find the matching one.  */
+  for (i = 0; i < ARRAY_SIZE (percent_op); i++)
+    if (strncasecmp (*str, percent_op[i].str, strlen (percent_op[i].str)) == 0)
       {
-       int pcnt = 1;
-       char *s;
+       *str += strlen (percent_op[i].str);
+       *reloc = percent_op[i].reloc;
 
-       *len = 1;
-       for (s = *str + 1; *s && pcnt; s++, (*len)++)
+       /* Check whether the output BFD supports this relocation.
+          If not, issue an error and fall back on something safe.  */
+       if (!bfd_reloc_type_lookup (stdoutput, percent_op[i].reloc))
          {
-           if (*s == '(')
-             ++pcnt;
-           else if (*s == ')')
-             --pcnt;
+           as_bad ("relocation %s isn't supported by the current ABI",
+                   percent_op[i].str);
+           *reloc = BFD_RELOC_LO16;
          }
+       return TRUE;
       }
-    }
-  /* Check for percent_op (outside of parentheses).  */
-  else if (*str[0] == '%')
-    return my_getPercentOp (str, len, nestlevel);
-
-  /* Any other expression.  */
-  return S_EX_NONE;
+  return FALSE;
 }
 
-static int
-my_getPercentOp (str, len, nestlevel)
-     char **str;
-     unsigned int *len;
-     int *nestlevel;
-{
-  char *tmp = *str + 1;
-  unsigned int i = 0;
-
-  while (ISALPHA (*tmp) || *tmp == '_')
-    {
-      *tmp = TOLOWER (*tmp);
-      tmp++;
-    }
-  while (i < (sizeof (percent_op) / sizeof (struct percent_op_match)))
-    {
-      if (strncmp (*str, percent_op[i].str, strlen (percent_op[i].str)))
-       i++;
-      else
-       {
-         int type = percent_op[i].type;
 
-         /* Only %hi and %lo are allowed for OldABI.  */
-         if (! HAVE_NEWABI && type != S_EX_HI && type != S_EX_LO)
-           return S_EX_NONE;
+/* Parse string STR as a 16-bit relocatable operand.  Store the
+   expression in *EP and the relocations in the array starting
+   at RELOC.  Return the number of relocation operators used.
 
-         *len = strlen (percent_op[i].str);
-         ++(*nestlevel);
-         return type;
-       }
-    }
-  return S_EX_NONE;
-}
+   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.  */
 
-static int
-my_getSmallExpression (ep, str)
+static size_t
+my_getSmallExpression (ep, reloc, str)
      expressionS *ep;
+     bfd_reloc_code_real_type *reloc;
      char *str;
 {
-  static char *oldstr = NULL;
-  int c = S_EX_NONE;
-  int oldc;
-  int nestlevel = -1;
-  unsigned int len;
-
-  /* Don't update oldstr if the last call had nested percent_op's. We need
-     it to parse the outer ones later.  */
-  if (! oldstr)
-    oldstr = str;
-
+  bfd_reloc_code_real_type reversed_reloc[3];
+  size_t reloc_index, i;
+  int crux_depth, str_depth;
+  char *crux;
+
+  /* Search for the start of the main expression, recoding relocations
+     in REVERSED_RELOC.  End the loop with CRUX pointing to the start
+     of the main expression and with CRUX_DEPTH containing the number
+     of open brackets at that point.  */
+  reloc_index = -1;
+  str_depth = 0;
   do
     {
-      oldc = c;
-      c = my_getSmallParser (&str, &len, &nestlevel);
-      if (c != S_EX_NONE && c != S_EX_REGISTER)
-       str += len;
+      reloc_index++;
+      crux = str;
+      crux_depth = str_depth;
+
+      /* Skip over whitespace and brackets, keeping count of the number
+        of brackets.  */
+      while (*str == ' ' || *str == '\t' || *str == '(')
+       if (*str++ == '(')
+         str_depth++;
     }
-  while (c != S_EX_NONE && c != S_EX_REGISTER);
+  while (*str == '%'
+        && reloc_index < (HAVE_NEWABI ? 3 : 1)
+        && parse_relocation (&str, &reversed_reloc[reloc_index]));
 
-  if (nestlevel >= 0)
-    {
-      /* A percent_op was encountered.  Don't try to get an expression if
-        it is already blanked out.  */
-      if (*(str + strspn (str + 1, " )")) != ')')
-       {
-         char save;
+  my_getExpression (ep, crux);
+  str = expr_end;
 
-         /* Let my_getExpression() stop at the closing parenthesis.  */
-         save = *(str + len);
-         *(str + len) = '\0';
-         my_getExpression (ep, str);
-         *(str + len) = save;
-       }
-      if (nestlevel > 0)
-       {
-         /* Blank out including the % sign and the proper matching
-            parenthesis.  */
-         int pcnt = 1;
-         char *s = strrchr (oldstr, '%');
-         char *end;
+  /* Match every open bracket.  */
+  while (crux_depth > 0 && (*str == ')' || *str == ' ' || *str == '\t'))
+    if (*str++ == ')')
+      crux_depth--;
 
-         for (end = strchr (s, '(') + 1; *end && pcnt; end++)
-           {
-             if (*end == '(')
-               ++pcnt;
-             else if (*end == ')')
-               --pcnt;
-           }
+  if (crux_depth > 0)
+    as_bad ("unclosed '('");
 
-         memset (s, ' ', end - s);
-         str = oldstr;
-       }
-      else
-       expr_end = str + len;
+  expr_end = str;
 
-      c = oldc;
-    }
-  else if (c == S_EX_NONE)
-    {
-      my_getExpression (ep, str);
-    }
-  else if (c == S_EX_REGISTER)
-    {
-      ep->X_op = O_constant;
-      expr_end = str;
-      ep->X_add_symbol = NULL;
-      ep->X_op_symbol = NULL;
-      ep->X_add_number = 0;
-    }
+  if (reloc_index == 0)
+    reloc[0] = BFD_RELOC_LO16;
   else
     {
-      as_fatal (_("internal error"));
+      prev_reloc_op_frag = frag_now;
+      for (i = 0; i < reloc_index; i++)
+       reloc[i] = reversed_reloc[reloc_index - 1 - i];
     }
 
-  if (nestlevel <= 0)
-    /* All percent_op's have been handled.  */
-    oldstr = NULL;
-
-  return c;
+  return reloc_index;
 }
 
 static void
@@ -9786,6 +10574,7 @@ md_number_to_chars (buf, val, n)
 static int support_64bit_objects(void)
 {
   const char **list, **l;
+  int yes;
 
   list = bfd_target_list ();
   for (l = list; *l != NULL; l++)
@@ -9798,88 +10587,120 @@ static int support_64bit_objects(void)
        || strcmp (*l, "elf64-littlemips") == 0)
 #endif
       break;
+  yes = (*l != NULL);
   free (list);
-  return (*l != NULL);
+  return yes;
 }
 #endif /* OBJ_ELF */
 
-CONST char *md_shortopts = "nO::g::G:";
+const char *md_shortopts = "nO::g::G:";
 
 struct option md_longopts[] =
 {
-#define OPTION_MIPS1 (OPTION_MD_BASE + 1)
+  /* Options which specify architecture.  */
+#define OPTION_ARCH_BASE    (OPTION_MD_BASE)
+#define OPTION_MARCH (OPTION_ARCH_BASE + 0)
+  {"march", required_argument, NULL, OPTION_MARCH},
+#define OPTION_MTUNE (OPTION_ARCH_BASE + 1)
+  {"mtune", required_argument, NULL, OPTION_MTUNE},
+#define OPTION_MIPS1 (OPTION_ARCH_BASE + 2)
   {"mips0", no_argument, NULL, OPTION_MIPS1},
   {"mips1", no_argument, NULL, OPTION_MIPS1},
-#define OPTION_MIPS2 (OPTION_MD_BASE + 2)
+#define OPTION_MIPS2 (OPTION_ARCH_BASE + 3)
   {"mips2", no_argument, NULL, OPTION_MIPS2},
-#define OPTION_MIPS3 (OPTION_MD_BASE + 3)
+#define OPTION_MIPS3 (OPTION_ARCH_BASE + 4)
   {"mips3", no_argument, NULL, OPTION_MIPS3},
-#define OPTION_MIPS4 (OPTION_MD_BASE + 4)
+#define OPTION_MIPS4 (OPTION_ARCH_BASE + 5)
   {"mips4", no_argument, NULL, OPTION_MIPS4},
-#define OPTION_MIPS5 (OPTION_MD_BASE + 5)
+#define OPTION_MIPS5 (OPTION_ARCH_BASE + 6)
   {"mips5", no_argument, NULL, OPTION_MIPS5},
-#define OPTION_MIPS32 (OPTION_MD_BASE + 6)
+#define OPTION_MIPS32 (OPTION_ARCH_BASE + 7)
   {"mips32", no_argument, NULL, OPTION_MIPS32},
-#define OPTION_MIPS64 (OPTION_MD_BASE + 7)
+#define OPTION_MIPS64 (OPTION_ARCH_BASE + 8)
   {"mips64", no_argument, NULL, OPTION_MIPS64},
-#define OPTION_MEMBEDDED_PIC (OPTION_MD_BASE + 8)
-  {"membedded-pic", no_argument, NULL, OPTION_MEMBEDDED_PIC},
-#define OPTION_TRAP (OPTION_MD_BASE + 9)
-  {"trap", no_argument, NULL, OPTION_TRAP},
-  {"no-break", no_argument, NULL, OPTION_TRAP},
-#define OPTION_BREAK (OPTION_MD_BASE + 10)
-  {"break", no_argument, NULL, OPTION_BREAK},
-  {"no-trap", no_argument, NULL, OPTION_BREAK},
-#define OPTION_EB (OPTION_MD_BASE + 11)
-  {"EB", no_argument, NULL, OPTION_EB},
-#define OPTION_EL (OPTION_MD_BASE + 12)
-  {"EL", no_argument, NULL, OPTION_EL},
-#define OPTION_MIPS16 (OPTION_MD_BASE + 13)
+#define OPTION_MIPS32R2 (OPTION_ARCH_BASE + 9)
+  {"mips32r2", no_argument, NULL, OPTION_MIPS32R2},
+
+  /* Options which specify Application Specific Extensions (ASEs).  */
+#define OPTION_ASE_BASE (OPTION_ARCH_BASE + 10)
+#define OPTION_MIPS16 (OPTION_ASE_BASE + 0)
   {"mips16", no_argument, NULL, OPTION_MIPS16},
-#define OPTION_NO_MIPS16 (OPTION_MD_BASE + 14)
+#define OPTION_NO_MIPS16 (OPTION_ASE_BASE + 1)
   {"no-mips16", no_argument, NULL, OPTION_NO_MIPS16},
-#define OPTION_M7000_HILO_FIX (OPTION_MD_BASE + 15)
-  {"mfix7000", no_argument, NULL, OPTION_M7000_HILO_FIX},
-#define OPTION_NO_M7000_HILO_FIX (OPTION_MD_BASE + 16)
-  {"no-fix-7000", no_argument, NULL, OPTION_NO_M7000_HILO_FIX},
-#define OPTION_FP32 (OPTION_MD_BASE + 17)
-  {"mfp32", no_argument, NULL, OPTION_FP32},
-#define OPTION_GP32 (OPTION_MD_BASE + 18)
-  {"mgp32", no_argument, NULL, OPTION_GP32},
-#define OPTION_CONSTRUCT_FLOATS (OPTION_MD_BASE + 19)
-  {"construct-floats", no_argument, NULL, OPTION_CONSTRUCT_FLOATS},
-#define OPTION_NO_CONSTRUCT_FLOATS (OPTION_MD_BASE + 20)
-  {"no-construct-floats", no_argument, NULL, OPTION_NO_CONSTRUCT_FLOATS},
-#define OPTION_MARCH (OPTION_MD_BASE + 21)
-  {"march", required_argument, NULL, OPTION_MARCH},
-#define OPTION_MTUNE (OPTION_MD_BASE + 22)
-  {"mtune", required_argument, NULL, OPTION_MTUNE},
-#define OPTION_MCPU (OPTION_MD_BASE + 23)
-  {"mcpu", required_argument, NULL, OPTION_MCPU},
-#define OPTION_M4650 (OPTION_MD_BASE + 24)
+#define OPTION_MIPS3D (OPTION_ASE_BASE + 2)
+  {"mips3d", no_argument, NULL, OPTION_MIPS3D},
+#define OPTION_NO_MIPS3D (OPTION_ASE_BASE + 3)
+  {"no-mips3d", no_argument, NULL, OPTION_NO_MIPS3D},
+#define OPTION_MDMX (OPTION_ASE_BASE + 4)
+  {"mdmx", no_argument, NULL, OPTION_MDMX},
+#define OPTION_NO_MDMX (OPTION_ASE_BASE + 5)
+  {"no-mdmx", no_argument, NULL, OPTION_NO_MDMX},
+
+  /* Old-style architecture options.  Don't add more of these.  */
+#define OPTION_COMPAT_ARCH_BASE (OPTION_ASE_BASE + 6)
+#define OPTION_M4650 (OPTION_COMPAT_ARCH_BASE + 0)
   {"m4650", no_argument, NULL, OPTION_M4650},
-#define OPTION_NO_M4650 (OPTION_MD_BASE + 25)
+#define OPTION_NO_M4650 (OPTION_COMPAT_ARCH_BASE + 1)
   {"no-m4650", no_argument, NULL, OPTION_NO_M4650},
-#define OPTION_M4010 (OPTION_MD_BASE + 26)
+#define OPTION_M4010 (OPTION_COMPAT_ARCH_BASE + 2)
   {"m4010", no_argument, NULL, OPTION_M4010},
-#define OPTION_NO_M4010 (OPTION_MD_BASE + 27)
+#define OPTION_NO_M4010 (OPTION_COMPAT_ARCH_BASE + 3)
   {"no-m4010", no_argument, NULL, OPTION_NO_M4010},
-#define OPTION_M4100 (OPTION_MD_BASE + 28)
+#define OPTION_M4100 (OPTION_COMPAT_ARCH_BASE + 4)
   {"m4100", no_argument, NULL, OPTION_M4100},
-#define OPTION_NO_M4100 (OPTION_MD_BASE + 29)
+#define OPTION_NO_M4100 (OPTION_COMPAT_ARCH_BASE + 5)
   {"no-m4100", no_argument, NULL, OPTION_NO_M4100},
-#define OPTION_M3900 (OPTION_MD_BASE + 30)
+#define OPTION_M3900 (OPTION_COMPAT_ARCH_BASE + 6)
   {"m3900", no_argument, NULL, OPTION_M3900},
-#define OPTION_NO_M3900 (OPTION_MD_BASE + 31)
+#define OPTION_NO_M3900 (OPTION_COMPAT_ARCH_BASE + 7)
   {"no-m3900", no_argument, NULL, OPTION_NO_M3900},
-#define OPTION_GP64 (OPTION_MD_BASE + 32)
+
+  /* Options which enable bug fixes.  */
+#define OPTION_FIX_BASE    (OPTION_COMPAT_ARCH_BASE + 8)
+#define OPTION_M7000_HILO_FIX (OPTION_FIX_BASE + 0)
+  {"mfix7000", no_argument, NULL, OPTION_M7000_HILO_FIX},
+#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},
+
+  /* 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)
+  {"trap", no_argument, NULL, OPTION_TRAP},
+  {"no-break", no_argument, NULL, OPTION_TRAP},
+#define OPTION_BREAK (OPTION_MISC_BASE + 2)
+  {"break", no_argument, NULL, OPTION_BREAK},
+  {"no-trap", no_argument, NULL, OPTION_BREAK},
+#define OPTION_EB (OPTION_MISC_BASE + 3)
+  {"EB", no_argument, NULL, OPTION_EB},
+#define OPTION_EL (OPTION_MISC_BASE + 4)
+  {"EL", no_argument, NULL, OPTION_EL},
+#define OPTION_FP32 (OPTION_MISC_BASE + 5)
+  {"mfp32", no_argument, NULL, OPTION_FP32},
+#define OPTION_GP32 (OPTION_MISC_BASE + 6)
+  {"mgp32", no_argument, NULL, OPTION_GP32},
+#define OPTION_CONSTRUCT_FLOATS (OPTION_MISC_BASE + 7)
+  {"construct-floats", no_argument, NULL, OPTION_CONSTRUCT_FLOATS},
+#define OPTION_NO_CONSTRUCT_FLOATS (OPTION_MISC_BASE + 8)
+  {"no-construct-floats", no_argument, NULL, OPTION_NO_CONSTRUCT_FLOATS},
+#define OPTION_FP64 (OPTION_MISC_BASE + 9)
+  {"mfp64", no_argument, NULL, OPTION_FP64},
+#define OPTION_GP64 (OPTION_MISC_BASE + 10)
   {"mgp64", no_argument, NULL, OPTION_GP64},
-#define OPTION_MIPS3D (OPTION_MD_BASE + 33)
-  {"mips3d", no_argument, NULL, OPTION_MIPS3D},
-#define OPTION_NO_MIPS3D (OPTION_MD_BASE + 34)
-  {"no-mips3d", no_argument, NULL, OPTION_NO_MIPS3D},
+#define OPTION_RELAX_BRANCH (OPTION_MISC_BASE + 11)
+#define OPTION_NO_RELAX_BRANCH (OPTION_MISC_BASE + 12)
+  {"relax-branch", no_argument, NULL, OPTION_RELAX_BRANCH},
+  {"no-relax-branch", no_argument, NULL, OPTION_NO_RELAX_BRANCH},
+
+  /* ELF-specific options.  */
 #ifdef OBJ_ELF
-#define OPTION_ELF_BASE    (OPTION_MD_BASE + 35)
+#define OPTION_ELF_BASE    (OPTION_MISC_BASE + 13)
 #define OPTION_CALL_SHARED (OPTION_ELF_BASE + 0)
   {"KPIC",        no_argument, NULL, OPTION_CALL_SHARED},
   {"call_shared", no_argument, NULL, OPTION_CALL_SHARED},
@@ -9895,11 +10716,34 @@ struct option md_longopts[] =
   {"n32",         no_argument, NULL, OPTION_N32},
 #define OPTION_64          (OPTION_ELF_BASE + 6)
   {"64",          no_argument, NULL, OPTION_64},
+#define OPTION_MDEBUG      (OPTION_ELF_BASE + 7)
+  {"mdebug", no_argument, NULL, OPTION_MDEBUG},
+#define OPTION_NO_MDEBUG   (OPTION_ELF_BASE + 8)
+  {"no-mdebug", no_argument, NULL, OPTION_NO_MDEBUG},
 #endif /* OBJ_ELF */
+
   {NULL, no_argument, NULL, 0}
 };
 size_t md_longopts_size = sizeof (md_longopts);
 
+/* Set STRING_PTR (either &mips_arch_string or &mips_tune_string) to
+   NEW_VALUE.  Warn if another value was already specified.  Note:
+   we have to defer parsing the -march and -mtune arguments in order
+   to handle 'from-abi' correctly, since the ABI might be specified
+   in a later argument.  */
+
+static void
+mips_set_option_string (string_ptr, new_value)
+     const char **string_ptr, *new_value;
+{
+  if (*string_ptr != 0 && strcasecmp (*string_ptr, new_value) != 0)
+    as_warn (_("A different %s was already specified, is now %s"),
+            string_ptr == &mips_arch_string ? "-march" : "-mtune",
+            new_value);
+
+  *string_ptr = new_value;
+}
+
 int
 md_parse_option (c, arg)
      int c;
@@ -9955,143 +10799,93 @@ md_parse_option (c, arg)
       break;
 
     case OPTION_MIPS1:
-      mips_opts.isa = ISA_MIPS1;
+      file_mips_isa = ISA_MIPS1;
       break;
 
     case OPTION_MIPS2:
-      mips_opts.isa = ISA_MIPS2;
+      file_mips_isa = ISA_MIPS2;
       break;
 
     case OPTION_MIPS3:
-      mips_opts.isa = ISA_MIPS3;
+      file_mips_isa = ISA_MIPS3;
       break;
 
     case OPTION_MIPS4:
-      mips_opts.isa = ISA_MIPS4;
+      file_mips_isa = ISA_MIPS4;
       break;
 
     case OPTION_MIPS5:
-      mips_opts.isa = ISA_MIPS5;
+      file_mips_isa = ISA_MIPS5;
       break;
 
     case OPTION_MIPS32:
-      mips_opts.isa = ISA_MIPS32;
+      file_mips_isa = ISA_MIPS32;
+      break;
+
+    case OPTION_MIPS32R2:
+      file_mips_isa = ISA_MIPS32R2;
       break;
 
     case OPTION_MIPS64:
-      mips_opts.isa = ISA_MIPS64;
+      file_mips_isa = ISA_MIPS64;
       break;
 
     case OPTION_MTUNE:
-    case OPTION_MARCH:
-    case OPTION_MCPU:
-      {
-       int cpu = CPU_UNKNOWN;
-
-       /* Identify the processor type.  */
-       if (strcasecmp (arg, "default") != 0)
-         {
-           const struct mips_cpu_info *ci;
-
-           ci = mips_cpu_info_from_name (arg);
-           if (ci == NULL || ci->is_isa)
-             {
-               switch (c)
-                 {
-                 case OPTION_MTUNE:
-                   as_fatal (_("invalid architecture -mtune=%s"), arg);
-                   break;
-                 case OPTION_MARCH:
-                   as_fatal (_("invalid architecture -march=%s"), arg);
-                   break;
-                 case OPTION_MCPU:
-                   as_fatal (_("invalid architecture -mcpu=%s"), arg);
-                   break;
-                 }
-             }
-           else
-             cpu = ci->cpu;
-         }
+      mips_set_option_string (&mips_tune_string, arg);
+      break;
 
-       switch (c)
-         {
-         case OPTION_MTUNE:
-           if (mips_tune != CPU_UNKNOWN && mips_tune != cpu)
-             as_warn (_("A different -mtune= was already specified, is now "
-                        "-mtune=%s"), arg);
-           mips_tune = cpu;
-           break;
-         case OPTION_MARCH:
-           if (mips_arch != CPU_UNKNOWN && mips_arch != cpu)
-             as_warn (_("A different -march= was already specified, is now "
-                        "-march=%s"), arg);
-           mips_arch = cpu;
-           break;
-         case OPTION_MCPU:
-           if (mips_cpu != CPU_UNKNOWN && mips_cpu != cpu)
-             as_warn (_("A different -mcpu= was already specified, is now "
-                        "-mcpu=%s"), arg);
-           mips_cpu = cpu;
-         }
-      }
+    case OPTION_MARCH:
+      mips_set_option_string (&mips_arch_string, arg);
       break;
 
     case OPTION_M4650:
-      if ((mips_arch != CPU_UNKNOWN && mips_arch != CPU_R4650)
-         || (mips_tune != CPU_UNKNOWN && mips_tune != CPU_R4650))
-       as_warn (_("A different -march= or -mtune= was already specified, "
-                  "is now -m4650"));
-      mips_arch = CPU_R4650;
-      mips_tune = CPU_R4650;
+      mips_set_option_string (&mips_arch_string, "4650");
+      mips_set_option_string (&mips_tune_string, "4650");
       break;
 
     case OPTION_NO_M4650:
       break;
 
     case OPTION_M4010:
-      if ((mips_arch != CPU_UNKNOWN && mips_arch != CPU_R4010)
-         || (mips_tune != CPU_UNKNOWN && mips_tune != CPU_R4010))
-       as_warn (_("A different -march= or -mtune= was already specified, "
-                  "is now -m4010"));
-      mips_arch = CPU_R4010;
-      mips_tune = CPU_R4010;
+      mips_set_option_string (&mips_arch_string, "4010");
+      mips_set_option_string (&mips_tune_string, "4010");
       break;
 
     case OPTION_NO_M4010:
       break;
 
     case OPTION_M4100:
-      if ((mips_arch != CPU_UNKNOWN && mips_arch != CPU_VR4100)
-         || (mips_tune != CPU_UNKNOWN && mips_tune != CPU_VR4100))
-       as_warn (_("A different -march= or -mtune= was already specified, "
-                  "is now -m4100"));
-      mips_arch = CPU_VR4100;
-      mips_tune = CPU_VR4100;
+      mips_set_option_string (&mips_arch_string, "4100");
+      mips_set_option_string (&mips_tune_string, "4100");
       break;
 
     case OPTION_NO_M4100:
       break;
 
     case OPTION_M3900:
-      if ((mips_arch != CPU_UNKNOWN && mips_arch != CPU_R3900)
-         || (mips_tune != CPU_UNKNOWN && mips_tune != CPU_R3900))
-       as_warn (_("A different -march= or -mtune= was already specified, "
-                  "is now -m3900"));
-      mips_arch = CPU_R3900;
-      mips_tune = CPU_R3900;
+      mips_set_option_string (&mips_arch_string, "3900");
+      mips_set_option_string (&mips_tune_string, "3900");
       break;
 
     case OPTION_NO_M3900:
       break;
 
+    case OPTION_MDMX:
+      mips_opts.ase_mdmx = 1;
+      break;
+
+    case OPTION_NO_MDMX:
+      mips_opts.ase_mdmx = 0;
+      break;
+
     case OPTION_MIPS16:
       mips_opts.mips16 = 1;
-      mips_no_prev_insn (false);
+      mips_no_prev_insn (FALSE);
       break;
 
     case OPTION_NO_MIPS16:
       mips_opts.mips16 = 0;
-      mips_no_prev_insn (false);
+      mips_no_prev_insn (FALSE);
       break;
 
     case OPTION_MIPS3D:
@@ -10112,6 +10906,22 @@ md_parse_option (c, arg)
       g_switch_value = 0x7fffffff;
       break;
 
+    case OPTION_FIX_VR4122:
+      mips_fix_4122_bugs = 1;
+      break;
+
+    case OPTION_NO_FIX_VR4122:
+      mips_fix_4122_bugs = 0;
+      break;
+
+    case OPTION_RELAX_BRANCH:
+      mips_relax_branch = 1;
+      break;
+
+    case OPTION_NO_RELAX_BRANCH:
+      mips_relax_branch = 0;
+      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
@@ -10123,6 +10933,7 @@ md_parse_option (c, arg)
          return 0;
        }
       mips_pic = SVR4_PIC;
+      mips_abicalls = TRUE;
       if (g_switch_seen && g_switch_value != 0)
        {
          as_bad (_("-G may not be used with SVR4 PIC code"));
@@ -10138,6 +10949,7 @@ md_parse_option (c, arg)
          return 0;
        }
       mips_pic = NO_PIC;
+      mips_abicalls = FALSE;
       break;
 
       /* The -xgot option tells the assembler to use 32 offsets when
@@ -10173,7 +10985,7 @@ md_parse_option (c, arg)
          as_bad (_("-32 is supported for ELF format only"));
          return 0;
        }
-      mips_opts.abi = O32_ABI;
+      mips_abi = O32_ABI;
       break;
 
     case OPTION_N32:
@@ -10182,7 +10994,7 @@ md_parse_option (c, arg)
          as_bad (_("-n32 is supported for ELF format only"));
          return 0;
        }
-      mips_opts.abi = N32_ABI;
+      mips_abi = N32_ABI;
       break;
 
     case OPTION_64:
@@ -10191,7 +11003,7 @@ md_parse_option (c, arg)
          as_bad (_("-64 is supported for ELF format only"));
          return 0;
        }
-      mips_opts.abi = N64_ABI;
+      mips_abi = N64_ABI;
       if (! support_64bit_objects())
        as_fatal (_("No compiled in support for 64 bit object file format"));
       break;
@@ -10199,20 +11011,18 @@ md_parse_option (c, arg)
 
     case OPTION_GP32:
       file_mips_gp32 = 1;
-      if (mips_opts.abi != O32_ABI)
-       mips_opts.abi = NO_ABI;
       break;
 
     case OPTION_GP64:
       file_mips_gp32 = 0;
-      if (mips_opts.abi == O32_ABI)
-       mips_opts.abi = NO_ABI;
       break;
 
     case OPTION_FP32:
       file_mips_fp32 = 1;
-      if (mips_opts.abi != O32_ABI)
-       mips_opts.abi = NO_ABI;
+      break;
+
+    case OPTION_FP64:
+      file_mips_fp32 = 0;
       break;
 
 #ifdef OBJ_ELF
@@ -10223,20 +11033,20 @@ md_parse_option (c, arg)
          return 0;
        }
       if (strcmp (arg, "32") == 0)
-       mips_opts.abi = O32_ABI;
+       mips_abi = O32_ABI;
       else if (strcmp (arg, "o64") == 0)
-       mips_opts.abi = O64_ABI;
+       mips_abi = O64_ABI;
       else if (strcmp (arg, "n32") == 0)
-       mips_opts.abi = N32_ABI;
+       mips_abi = N32_ABI;
       else if (strcmp (arg, "64") == 0)
        {
-         mips_opts.abi = N64_ABI;
+         mips_abi = N64_ABI;
          if (! support_64bit_objects())
            as_fatal (_("No compiled in support for 64 bit object file "
                        "format"));
        }
       else if (strcmp (arg, "eabi") == 0)
-       mips_opts.abi = EABI_ABI;
+       mips_abi = EABI_ABI;
       else
        {
          as_fatal (_("invalid abi -mabi=%s"), arg);
@@ -10246,12 +11056,22 @@ md_parse_option (c, arg)
 #endif /* OBJ_ELF */
 
     case OPTION_M7000_HILO_FIX:
-      mips_7000_hilo_fix = true;
+      mips_7000_hilo_fix = TRUE;
+      break;
+
+    case OPTION_MNO_7000_HILO_FIX:
+      mips_7000_hilo_fix = FALSE;
+      break;
+
+#ifdef OBJ_ELF
+    case OPTION_MDEBUG:
+      mips_flag_mdebug = TRUE;
       break;
 
-    case OPTION_NO_M7000_HILO_FIX:
-      mips_7000_hilo_fix = false;
+    case OPTION_NO_MDEBUG:
+      mips_flag_mdebug = FALSE;
       break;
+#endif /* OBJ_ELF */
 
     default:
       return 0;
@@ -10259,134 +11079,159 @@ md_parse_option (c, arg)
 
   return 1;
 }
+\f
+/* Set up globals to generate code for the ISA or processor
+   described by INFO.  */
 
 static void
-show (stream, string, col_p, first_p)
-     FILE *stream;
-     char *string;
-     int *col_p;
-     int *first_p;
+mips_set_architecture (info)
+     const struct mips_cpu_info *info;
 {
-  if (*first_p)
+  if (info != 0)
     {
-      fprintf (stream, "%24s", "");
-      *col_p = 24;
+      mips_arch_info = info;
+      mips_arch = info->cpu;
+      mips_opts.isa = info->isa;
     }
-  else
+}
+
+
+/* Likewise for tuning.  */
+
+static void
+mips_set_tune (info)
+     const struct mips_cpu_info *info;
+{
+  if (info != 0)
     {
-      fprintf (stream, ", ");
-      *col_p += 2;
+      mips_tune_info = info;
+      mips_tune = info->cpu;
     }
+}
 
-  if (*col_p + strlen (string) > 72)
+
+void
+mips_after_parse_args ()
+{
+  /* GP relative stuff not working for PE */
+  if (strncmp (TARGET_OS, "pe", 2) == 0
+      && g_switch_value != 0)
     {
-      fprintf (stream, "\n%24s", "");
-      *col_p = 24;
+      if (g_switch_seen)
+       as_bad (_("-G not supported in this configuration."));
+      g_switch_value = 0;
     }
 
-  fprintf (stream, "%s", string);
-  *col_p += strlen (string);
+  if (mips_abi == NO_ABI)
+    mips_abi = MIPS_DEFAULT_ABI;
 
-  *first_p = 0;
-}
+  /* The following code determines the architecture and register size.
+     Similar code was added to GCC 3.3 (see override_options() in
+     config/mips/mips.c).  The GAS and GCC code should be kept in sync
+     as much as possible.  */
 
-void
-md_show_usage (stream)
-     FILE *stream;
-{
-  int column, first;
+  if (mips_arch_string != 0)
+    mips_set_architecture (mips_parse_cpu ("-march", mips_arch_string));
 
-  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\
--G NUM                 allow referencing objects up to NUM bytes\n\
-                       implicitly with the gp register [default 8]\n"));
-  fprintf (stream, _("\
--mips1                 generate MIPS ISA I instructions\n\
--mips2                 generate MIPS ISA II instructions\n\
--mips3                 generate MIPS ISA III instructions\n\
--mips4                 generate MIPS ISA IV instructions\n\
--mips5                  generate MIPS ISA V instructions\n\
--mips32                 generate MIPS32 ISA instructions\n\
--mips64                 generate MIPS64 ISA instructions\n\
--march=CPU/-mtune=CPU  generate code/schedule for CPU, where CPU is one of:\n"));
+  if (mips_tune_string != 0)
+    mips_set_tune (mips_parse_cpu ("-mtune", mips_tune_string));
 
-  first = 1;
+  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
+        the -march selection (if any).  */
+      if (mips_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)
+           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);
+       }
+      else
+       mips_set_architecture (mips_cpu_info_from_isa (file_mips_isa));
+    }
 
-  show (stream, "2000", &column, &first);
-  show (stream, "3000", &column, &first);
-  show (stream, "3900", &column, &first);
-  show (stream, "4000", &column, &first);
-  show (stream, "4010", &column, &first);
-  show (stream, "4100", &column, &first);
-  show (stream, "4111", &column, &first);
-  show (stream, "4300", &column, &first);
-  show (stream, "4400", &column, &first);
-  show (stream, "4600", &column, &first);
-  show (stream, "4650", &column, &first);
-  show (stream, "5000", &column, &first);
-  show (stream, "5200", &column, &first);
-  show (stream, "5230", &column, &first);
-  show (stream, "5231", &column, &first);
-  show (stream, "5261", &column, &first);
-  show (stream, "5721", &column, &first);
-  show (stream, "6000", &column, &first);
-  show (stream, "8000", &column, &first);
-  show (stream, "10000", &column, &first);
-  show (stream, "12000", &column, &first);
-  show (stream, "sb1", &column, &first);
-  fputc ('\n', stream);
+  if (mips_arch_info == 0)
+    mips_set_architecture (mips_parse_cpu ("default CPU",
+                                          MIPS_CPU_STRING_DEFAULT));
 
-  fprintf (stream, _("\
--mCPU                  equivalent to -march=CPU -mtune=CPU. Deprecated.\n\
--no-mCPU               don't generate code specific to CPU.\n\
-                       For -mCPU and -no-mCPU, CPU must be one of:\n"));
+  if (ABI_NEEDS_64BIT_REGS (mips_abi) && !ISA_HAS_64BIT_REGS (mips_opts.isa))
+    as_bad ("-march=%s is not compatible with the selected ABI",
+           mips_arch_info->name);
 
-  first = 1;
+  /* Optimize for mips_arch, unless -mtune selects a different processor.  */
+  if (mips_tune_info == 0)
+    mips_set_tune (mips_arch_info);
 
-  show (stream, "3900", &column, &first);
-  show (stream, "4010", &column, &first);
-  show (stream, "4100", &column, &first);
-  show (stream, "4650", &column, &first);
-  fputc ('\n', stream);
+  if (file_mips_gp32 >= 0)
+    {
+      /* The user specified the size of the integer registers.  Make sure
+        it agrees with the ABI and ISA.  */
+      if (file_mips_gp32 == 0 && !ISA_HAS_64BIT_REGS (mips_opts.isa))
+       as_bad (_("-mgp64 used with a 32-bit processor"));
+      else if (file_mips_gp32 == 1 && ABI_NEEDS_64BIT_REGS (mips_abi))
+       as_bad (_("-mgp32 used with a 64-bit ABI"));
+      else if (file_mips_gp32 == 0 && ABI_NEEDS_32BIT_REGS (mips_abi))
+       as_bad (_("-mgp64 used with a 32-bit ABI"));
+    }
+  else
+    {
+      /* Infer the integer register size from the ABI and processor.
+        Restrict ourselves to 32-bit registers if that's all the
+        processor has, or if the ABI cannot handle 64-bit registers.  */
+      file_mips_gp32 = (ABI_NEEDS_32BIT_REGS (mips_abi)
+                       || !ISA_HAS_64BIT_REGS (mips_opts.isa));
+    }
 
-  fprintf (stream, _("\
--mips16                        generate mips16 instructions\n\
--no-mips16             do not generate mips16 instructions\n"));
-  fprintf (stream, _("\
--mgp32                 use 32-bit GPRs, regardless of the chosen ISA\n\
--mfp32                 use 32-bit FPRs, regardless of the chosen ISA\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"));
-#ifdef OBJ_ELF
-  fprintf (stream, _("\
--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\
--mabi=ABI              create ABI conformant object file for:\n"));
+  /* ??? GAS treats single-float processors as though they had 64-bit
+     float registers (although it complains when double-precision
+     instructions are used).  As things stand, saying they have 32-bit
+     registers would lead to spurious "register must be even" messages.
+     So here we assume float registers are always the same size as
+     integer ones, unless the user says otherwise.  */
+  if (file_mips_fp32 < 0)
+    file_mips_fp32 = file_mips_gp32;
 
-  first = 1;
+  /* End of GCC-shared inference code.  */
 
-  show (stream, "32", &column, &first);
-  show (stream, "o64", &column, &first);
-  show (stream, "n32", &column, &first);
-  show (stream, "64", &column, &first);
-  show (stream, "eabi", &column, &first);
+  /* ??? 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))
+    mips_32bitmode = 1;
 
-  fputc ('\n', stream);
+  if (mips_opts.isa == ISA_MIPS1 && mips_trap)
+    as_bad (_("trap exception not supported at ISA 1"));
 
-  fprintf (stream, _("\
--32                    create o32 ABI object file (default)\n\
--n32                   create n32 ABI object file\n\
--64                    create 64 ABI object file\n"));
-#endif
+  /* 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;
+  if (mips_opts.ase_mips3d == -1)
+    mips_opts.ase_mips3d = (CPU_HAS_MIPS3D (mips_arch)) ? 1 : 0;
+  if (mips_opts.ase_mdmx == -1)
+    mips_opts.ase_mdmx = (CPU_HAS_MDMX (mips_arch)) ? 1 : 0;
+
+  file_mips_isa = mips_opts.isa;
+  file_ase_mips16 = mips_opts.mips16;
+  file_ase_mips3d = mips_opts.ase_mips3d;
+  file_ase_mdmx = mips_opts.ase_mdmx;
+  mips_opts.gp32 = file_mips_gp32;
+  mips_opts.fp32 = file_mips_fp32;
+
+  if (mips_flag_mdebug < 0)
+    {
+#ifdef OBJ_MAYBE_ECOFF
+      if (OUTPUT_FLAVOR == bfd_target_ecoff_flavour)
+       mips_flag_mdebug = 1;
+      else
+#endif /* OBJ_MAYBE_ECOFF */
+       mips_flag_mdebug = 0;
+    }
 }
 \f
 void
@@ -10401,20 +11246,16 @@ long
 md_pcrel_from (fixP)
      fixS *fixP;
 {
-  if (OUTPUT_FLAVOR != bfd_target_aout_flavour
-      && fixP->fx_addsy != (symbolS *) NULL
-      && ! S_IS_DEFINED (fixP->fx_addsy))
-    {
-      /* This makes a branch to an undefined symbol be a branch to the
-        current location.  */
-      if (mips_pic == EMBEDDED_PIC)
-       return 4;
-      else
-       return 1;
+  valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
+  switch (fixP->fx_r_type)
+    {
+    case BFD_RELOC_16_PCREL_S2:
+    case BFD_RELOC_MIPS_JMP:
+      /* Return the address of the delay slot.  */
+      return addr + 4;
+    default:
+      return addr;
     }
-
-  /* Return the address of the delay slot.  */
-  return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
 }
 
 /* This is called before the symbol table is processed.  In order to
@@ -10450,14 +11291,16 @@ mips_frob_file ()
       segment_info_type *seginfo;
       int pass;
 
-      assert (l->fixp->fx_r_type == BFD_RELOC_HI16_S);
+      assert (reloc_needs_lo_p (l->fixp->fx_r_type));
 
-      /* Check quickly whether the next fixup happens to be a matching
-         %lo.  */
-      if (l->fixp->fx_next != NULL
-         && l->fixp->fx_next->fx_r_type == BFD_RELOC_LO16
-         && l->fixp->fx_addsy == l->fixp->fx_next->fx_addsy
-         && l->fixp->fx_offset == l->fixp->fx_next->fx_offset)
+      /* If a GOT16 relocation turns out to be against a global symbol,
+        there isn't supposed to be a matching LO.  */
+      if (l->fixp->fx_r_type == BFD_RELOC_MIPS_GOT16
+         && !pic_need_relax (l->fixp->fx_addsy, l->seg))
+       continue;
+
+      /* Check quickly whether the next fixup happens to be a matching %lo.  */
+      if (fixup_has_matching_lo_p (l->fixp))
        continue;
 
       /* Look through the fixups for this segment for a matching %lo.
@@ -10479,9 +11322,8 @@ mips_frob_file ()
                  && f->fx_offset == l->fixp->fx_offset
                  && (pass == 1
                      || prev == NULL
-                     || prev->fx_r_type != BFD_RELOC_HI16_S
-                     || prev->fx_addsy != f->fx_addsy
-                     || prev->fx_offset !=  f->fx_offset))
+                     || !reloc_needs_lo_p (prev->fx_r_type)
+                     || !fixup_has_matching_lo_p (prev)))
                {
                  fixS **pf;
 
@@ -10542,8 +11384,7 @@ int
 mips_force_relocation (fixp)
      fixS *fixp;
 {
-  if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
-      || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+  if (generic_force_reloc (fixp))
     return 1;
 
   if (HAVE_NEWABI
@@ -10560,6 +11401,85 @@ mips_force_relocation (fixp)
              || fixp->fx_r_type == BFD_RELOC_PCREL_LO16));
 }
 
+/* This hook is called before a fix is simplified.  We don't really
+   decide whether to skip a fix here.  Rather, we turn global symbols
+   used as branch targets into local symbols, such that they undergo
+   simplification.  We can only do this if the symbol is defined and
+   it is in the same section as the branch.  If this doesn't hold, we
+   emit a better error message than just saying the relocation is not
+   valid for the selected object format.
+
+   FIXP is the fix-up we're going to try to simplify, SEG is the
+   segment in which the fix up occurs.  The return value should be
+   non-zero to indicate the fix-up is valid for further
+   simplifications.  */
+
+int
+mips_validate_fix (fixP, seg)
+     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
+     seems to be that it can, but gas/bfd are very broken in creating
+     RELA relocations for this, so for now we only accept branches to
+     symbols in the same section.  Anything else is of dubious value,
+     since there's no guarantee that at link time the symbol would be
+     in range.  Even for branches to local symbols this is arguably
+     wrong, since it we assume the symbol is not going to be
+     overridden, which should be possible per ELF library semantics,
+     but then, there isn't a dynamic relocation that could be used to
+     this effect, and the target would likely be out of range as well.
+
+     Unfortunately, it seems that there is too much code out there
+     that relies on branches to symbols that are global to be resolved
+     as if they were local, like the IRIX tools do, so we do it as
+     well, but with a warning so that people are reminded to fix their
+     code.  If we ever get back to using R_MIPS_PC16 for branch
+     targets, this entire block should go away (and probably the
+     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)
+         || bfd_reloc_type_lookup (stdoutput, BFD_RELOC_16_PCREL_S2) == NULL)
+      && fixP->fx_addsy)
+    {
+      if (! S_IS_DEFINED (fixP->fx_addsy))
+       {
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("Cannot branch to undefined symbol."));
+         /* Avoid any further errors about this fixup.  */
+         fixP->fx_done = 1;
+       }
+      else if (S_GET_SEGMENT (fixP->fx_addsy) != seg)
+       {
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("Cannot branch to symbol in another section."));
+         fixP->fx_done = 1;
+       }
+      else if (S_IS_EXTERNAL (fixP->fx_addsy))
+       {
+         symbolS *sym = fixP->fx_addsy;
+
+         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),
+                                         S_GET_VALUE (sym),
+                                         symbol_get_frag (sym));
+         copy_symbol_attributes (fixP->fx_addsy, sym);
+         S_CLEAR_EXTERNAL (fixP->fx_addsy);
+         assert (symbol_resolved_p (sym));
+         symbol_mark_resolved (fixP->fx_addsy);
+       }
+    }
+
+  return 1;
+}
+
 #ifdef OBJ_ELF
 static int
 mips_need_elf_addend_fixup (fixP)
@@ -10572,16 +11492,15 @@ mips_need_elf_addend_fixup (fixP)
     return 1;
   if (mips_pic != EMBEDDED_PIC
       && (S_IS_WEAK (fixP->fx_addsy)
-         || S_IS_EXTERN (fixP->fx_addsy))
+         || S_IS_EXTERNAL (fixP->fx_addsy))
       && !S_IS_COMMON (fixP->fx_addsy))
     return 1;
-  if (symbol_used_in_reloc_p (fixP->fx_addsy)
-      && (((bfd_get_section_flags (stdoutput,
-                                  S_GET_SEGMENT (fixP->fx_addsy))
-           & SEC_LINK_ONCE) != 0)
-         || !strncmp (segment_name (S_GET_SEGMENT (fixP->fx_addsy)),
-                      ".gnu.linkonce",
-                      sizeof (".gnu.linkonce") - 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;
 }
@@ -10597,108 +11516,90 @@ md_apply_fix3 (fixP, valP, seg)
 {
   bfd_byte *buf;
   long insn;
-  valueT value;
+  static int previous_fx_r_type = 0;
+  reloc_howto_type *howto;
+
+  /* We ignore generic BFD relocations we don't know about.  */
+  howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
+  if (! howto)
+    return;
 
   assert (fixP->fx_size == 4
          || fixP->fx_r_type == BFD_RELOC_16
-         || fixP->fx_r_type == BFD_RELOC_32
-         || fixP->fx_r_type == BFD_RELOC_MIPS_JMP
-         || fixP->fx_r_type == BFD_RELOC_HI16_S
-         || fixP->fx_r_type == BFD_RELOC_LO16
-         || fixP->fx_r_type == BFD_RELOC_GPREL16
-         || fixP->fx_r_type == BFD_RELOC_MIPS_LITERAL
-         || fixP->fx_r_type == BFD_RELOC_GPREL32
          || fixP->fx_r_type == BFD_RELOC_64
          || fixP->fx_r_type == BFD_RELOC_CTOR
          || fixP->fx_r_type == BFD_RELOC_MIPS_SUB
-         || fixP->fx_r_type == BFD_RELOC_MIPS_HIGHEST
-         || fixP->fx_r_type == BFD_RELOC_MIPS_HIGHER
-         || fixP->fx_r_type == BFD_RELOC_MIPS_SCN_DISP
-         || fixP->fx_r_type == BFD_RELOC_MIPS_REL16
-         || fixP->fx_r_type == BFD_RELOC_MIPS_RELGOT
          || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
          || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY);
 
-  value = *valP;
+  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))
-       {
-         valueT symval = S_GET_VALUE (fixP->fx_addsy);
-
-         value -= symval;
-         if (value != 0 && ! fixP->fx_pcrel)
-           {
-             /* 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.  */
-             value -= symval;
-
-             /* Make sure the addend is still non-zero.  If it became zero
-                after the last operation, set it to a spurious value and
-                subtract the same value from the object file's contents.  */
-             if (value == 0)
-               {
-                 value = 8;
-
-                 /* The in-place addends for LO16 relocations are signed;
-                    leave the matching HI16 in-place addends as zero.  */
-                 if (fixP->fx_r_type != BFD_RELOC_HI16_S)
-                   {
-                     reloc_howto_type *howto;
-                     bfd_vma contents, mask, field;
-
-                     howto = bfd_reloc_type_lookup (stdoutput,
-                                                    fixP->fx_r_type);
-
-                     contents = bfd_get_bits (fixP->fx_frag->fr_literal
-                                              + fixP->fx_where,
-                                              fixP->fx_size * 8,
-                                              target_big_endian);
-
-                     /* MASK has bits set where the relocation should go.
-                        FIELD is -value, shifted into the appropriate place
-                        for this relocation.  */
-                     mask = 1 << (howto->bitsize - 1);
-                     mask = (((mask - 1) << 1) | 1) << howto->bitpos;
-                     field = (-value >> howto->rightshift) << howto->bitpos;
-
-                     bfd_put_bits ((field & mask) | (contents & ~mask),
-                                   fixP->fx_frag->fr_literal + fixP->fx_where,
-                                   fixP->fx_size * 8,
-                                   target_big_endian);
-                   }
-               }
-           }
+      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);
        }
 
       /* 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 || fixP->fx_subsy != NULL)
+      if (fixP->fx_pcrel)
        {
-         value += fixP->fx_frag->fr_address + fixP->fx_where;
+         *valP += fixP->fx_frag->fr_address + fixP->fx_where;
 
          /* 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.  */
-         if ((fixP->fx_r_type != BFD_RELOC_16_PCREL
-              && fixP->fx_r_type != BFD_RELOC_16_PCREL_S2)
-             || S_GET_SEGMENT (fixP->fx_addsy) != undefined_section)
-           value += fixP->fx_frag->fr_address + fixP->fx_where;
+         *valP += fixP->fx_frag->fr_address + fixP->fx_where;
        }
     }
 #endif
 
-  fixP->fx_addnumber = value;  /* Remember value for tc_gen_reloc.  */
-
-  if (fixP->fx_addsy == NULL && ! fixP->fx_pcrel)
+  /* 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))))
     fixP->fx_done = 1;
+  previous_fx_r_type = fixP->fx_r_type;
 
   switch (fixP->fx_r_type)
     {
@@ -10740,15 +11641,13 @@ md_apply_fix3 (fixP, valP, seg)
       /* We currently always generate a reloc against a symbol, which
          means that we don't want an addend even if the symbol is
          defined.  */
-      fixP->fx_addnumber = 0;
+      *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
-         && value != 0)
+      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)
@@ -10756,30 +11655,26 @@ md_apply_fix3 (fixP, valP, seg)
          /* 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.  */
-         value += (fixP->fx_next->fx_frag->fr_address
+         *valP += (fixP->fx_next->fx_frag->fr_address
                    + fixP->fx_next->fx_where);
        }
-      value = ((value + 0x8000) >> 16) & 0xffff;
-      buf = (bfd_byte *) fixP->fx_frag->fr_literal + fixP->fx_where;
+      *valP = ((*valP + 0x8000) >> 16) & 0xffff;
       if (target_big_endian)
        buf += 2;
-      md_number_to_chars ((char *) buf, value, 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
-         && value != 0)
+      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)
-       value += fixP->fx_frag->fr_address + fixP->fx_where;
-      buf = (bfd_byte *) fixP->fx_frag->fr_literal + fixP->fx_where;
+       *valP += fixP->fx_frag->fr_address + fixP->fx_where;
       if (target_big_endian)
        buf += 2;
-      md_number_to_chars ((char *) buf, value, 2);
+      md_number_to_chars ((char *) buf, *valP, 2);
       break;
 
     case BFD_RELOC_64:
@@ -10789,24 +11684,19 @@ md_apply_fix3 (fixP, valP, seg)
          || (mips_pic == EMBEDDED_PIC && SWITCH_TABLE (fixP)))
        {
          if (8 <= sizeof (valueT))
-           md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                               value, 8);
+           md_number_to_chars (buf, *valP, 8);
          else
            {
-             long w1, w2;
-             long hiv;
+             valueT hiv;
 
-             w1 = w2 = fixP->fx_where;
-             if (target_big_endian)
-               w1 += 4;
-             else
-               w2 += 4;
-             md_number_to_chars (fixP->fx_frag->fr_literal + w1, value, 4);
-             if ((value & 0x80000000) != 0)
+             if ((*valP & 0x80000000) != 0)
                hiv = 0xffffffff;
              else
                hiv = 0;
-             md_number_to_chars (fixP->fx_frag->fr_literal + w2, hiv, 4);
+             md_number_to_chars ((char *)(buf + target_big_endian ? 4 : 0),
+                                 *valP, 4);
+             md_number_to_chars ((char *)(buf + target_big_endian ? 0 : 4),
+                                 hiv, 4);
            }
        }
       break;
@@ -10820,8 +11710,7 @@ md_apply_fix3 (fixP, valP, seg)
         entry.  */
       if (fixP->fx_done
          || (mips_pic == EMBEDDED_PIC && SWITCH_TABLE (fixP)))
-       md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                           value, 4);
+       md_number_to_chars (buf, *valP, 4);
       break;
 
     case BFD_RELOC_16:
@@ -10829,8 +11718,7 @@ md_apply_fix3 (fixP, valP, seg)
          value now.  */
       assert (fixP->fx_size == 2);
       if (fixP->fx_done)
-       md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                           value, 2);
+       md_number_to_chars (buf, *valP, 2);
       break;
 
     case BFD_RELOC_LO16:
@@ -10838,89 +11726,69 @@ md_apply_fix3 (fixP, valP, seg)
         up deleting a LO16 reloc.  See the 'o' case in mips_ip.  */
       if (fixP->fx_done)
        {
-         if (value + 0x8000 > 0xffff)
+         if (*valP + 0x8000 > 0xffff)
            as_bad_where (fixP->fx_file, fixP->fx_line,
                          _("relocation overflow"));
-         buf = (bfd_byte *) fixP->fx_frag->fr_literal + fixP->fx_where;
          if (target_big_endian)
            buf += 2;
-         md_number_to_chars ((char *) buf, value, 2);
+         md_number_to_chars ((char *) buf, *valP, 2);
        }
       break;
 
     case BFD_RELOC_16_PCREL_S2:
-      if ((value & 0x3) != 0)
+      if ((*valP & 0x3) != 0)
        as_bad_where (fixP->fx_file, fixP->fx_line,
-                     _("Branch to odd address (%lx)"), (long) value);
-
-      /* Fall through.  */
+                     _("Branch to odd address (%lx)"), (long) *valP);
 
-    case BFD_RELOC_16_PCREL:
       /*
        * We need to save the bits in the instruction since fixup_segment()
        * might be deleting the relocation entry (i.e., a branch within
        * the current segment).
        */
-      if (!fixP->fx_done && value != 0)
+      if (! fixP->fx_done)
        break;
-      /* If 'value' is zero, the remaining reloc code won't actually
-        do the store, so it must be done here.  This is probably
-        a bug somewhere.  */
-      if (!fixP->fx_done
-         && (fixP->fx_r_type != BFD_RELOC_16_PCREL_S2
-             || fixP->fx_addsy == NULL                 /* ??? */
-             || ! S_IS_DEFINED (fixP->fx_addsy)))
-       value -= fixP->fx_frag->fr_address + fixP->fx_where;
-
-      value = (offsetT) value >> 2;
 
       /* update old instruction data */
-      buf = (bfd_byte *) (fixP->fx_where + fixP->fx_frag->fr_literal);
       if (target_big_endian)
        insn = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
       else
        insn = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
 
-      if (value + 0x8000 <= 0xffff)
-       insn |= value & 0xffff;
-      else
+      if (*valP + 0x20000 <= 0x3ffff)
+       {
+         insn |= (*valP >> 2) & 0xffff;
+         md_number_to_chars ((char *) buf, (valueT) 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)
+              && ((insn & 0xffff0000) == 0x10000000     /* beq $0,$0 */
+                  || (insn & 0xffff0000) == 0x04010000  /* bgez $0 */
+                  || (insn & 0xffff0000) == 0x04110000)) /* bgezal $0 */
        {
          /* The branch offset is too large.  If this is an
              unconditional branch, and we are not generating PIC code,
              we can convert it to an absolute jump instruction.  */
-         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)
-             && ((insn & 0xffff0000) == 0x10000000      /* beq $0,$0 */
-                 || (insn & 0xffff0000) == 0x04010000   /* bgez $0 */
-                 || (insn & 0xffff0000) == 0x04110000)) /* bgezal $0 */
-           {
-             if ((insn & 0xffff0000) == 0x04110000)     /* bgezal $0 */
-               insn = 0x0c000000;      /* jal */
-             else
-               insn = 0x08000000;      /* j */
-             fixP->fx_r_type = BFD_RELOC_MIPS_JMP;
-             fixP->fx_done = 0;
-             fixP->fx_addsy = section_symbol (text_section);
-             fixP->fx_addnumber = (value << 2) + md_pcrel_from (fixP);
-           }
+         if ((insn & 0xffff0000) == 0x04110000)         /* bgezal $0 */
+           insn = 0x0c000000;  /* jal */
          else
-           {
-             /* FIXME.  It would be possible in principle to handle
-                 conditional branches which overflow.  They could be
-                 transformed into a branch around a jump.  This would
-                 require setting up variant frags for each different
-                 branch type.  The native MIPS assembler attempts to
-                 handle these cases, but it appears to do it
-                 incorrectly.  */
-             as_bad_where (fixP->fx_file, fixP->fx_line,
-                           _("Branch out of range"));
-           }
+           insn = 0x08000000;  /* j */
+         fixP->fx_r_type = BFD_RELOC_MIPS_JMP;
+         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);
+       }
+      else
+       {
+         /* If we got here, we have branch-relaxation disabled,
+            and there's nothing we can do to fix this instruction
+            without turning it into a longer sequence.  */
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("Branch out of range"));
        }
-
-      md_number_to_chars ((char *) buf, (valueT) insn, 4);
       break;
 
     case BFD_RELOC_VTABLE_INHERIT:
@@ -10938,6 +11806,9 @@ md_apply_fix3 (fixP, valP, seg)
     default:
       internalError ();
     }
+
+  /* Remember value for tc_gen_reloc.  */
+  fixP->fx_addnumber = *valP;
 }
 
 #if 0
@@ -11051,7 +11922,7 @@ mips_align (to, fill, label)
      int fill;
      symbolS *label;
 {
-  mips_emit_delays (false);
+  mips_emit_delays (FALSE);
   frag_align (to, fill, 0);
   record_alignment (now_seg, to);
   if (label != NULL)
@@ -11117,7 +11988,7 @@ s_align (x)
 void
 mips_flush_pending_output ()
 {
-  mips_emit_delays (false);
+  mips_emit_delays (FALSE);
   mips_clear_insn_labels ();
 }
 
@@ -11144,7 +12015,7 @@ s_change_sec (sec)
   obj_elf_section_change_hook ();
 #endif
 
-  mips_emit_delays (false);
+  mips_emit_delays (FALSE);
   switch (sec)
     {
     case 't':
@@ -11211,7 +12082,67 @@ s_change_sec (sec)
 }
 
 void
-mips_enable_auto_align ()
+s_change_section (ignore)
+     int ignore ATTRIBUTE_UNUSED;
+{
+#ifdef OBJ_ELF
+  char *section_name;
+  char c;
+  char next_c = 0;
+  int section_type;
+  int section_flag;
+  int section_entry_size;
+  int section_alignment;
+
+  if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+    return;
+
+  section_name = input_line_pointer;
+  c = get_symbol_end ();
+  if (c)
+    next_c = *(input_line_pointer + 1);
+
+  /* Do we have .section Name<,"flags">?  */
+  if (c != ',' || (c == ',' && next_c == '"'))
+    {
+      /* just after name is now '\0'.  */
+      *input_line_pointer = c;
+      input_line_pointer = section_name;
+      obj_elf_section (ignore);
+      return;
+    }
+  input_line_pointer++;
+
+  /* Do we have .section Name<,type><,flag><,entry_size><,alignment>  */
+  if (c == ',')
+    section_type = get_absolute_expression ();
+  else
+    section_type = 0;
+  if (*input_line_pointer++ == ',')
+    section_flag = get_absolute_expression ();
+  else
+    section_flag = 0;
+  if (*input_line_pointer++ == ',')
+    section_entry_size = get_absolute_expression ();
+  else
+    section_entry_size = 0;
+  if (*input_line_pointer++ == ',')
+    section_alignment = get_absolute_expression ();
+  else
+    section_alignment = 0;
+
+  section_name = xstrdup (section_name);
+
+  obj_elf_change_section (section_name, section_type, section_flag,
+                         section_entry_size, 0, 0, 0);
+
+  if (now_seg->name != section_name)
+    free (section_name);
+#endif /* OBJ_ELF */
+}
+
+void
+mips_enable_auto_align ()
 {
   auto_align = 1;
 }
@@ -11223,7 +12154,7 @@ s_cons (log_size)
   symbolS *label;
 
   label = insn_labels != NULL ? insn_labels->label : NULL;
-  mips_emit_delays (false);
+  mips_emit_delays (FALSE);
   if (log_size > 0 && auto_align)
     mips_align (log_size, 0, label);
   mips_clear_insn_labels ();
@@ -11238,7 +12169,7 @@ s_float_cons (type)
 
   label = insn_labels != NULL ? insn_labels->label : NULL;
 
-  mips_emit_delays (false);
+  mips_emit_delays (FALSE);
 
   if (auto_align)
     {
@@ -11322,7 +12253,10 @@ s_option (x)
       if (i == 0)
        mips_pic = NO_PIC;
       else if (i == 2)
+       {
        mips_pic = SVR4_PIC;
+         mips_abicalls = TRUE;
+       }
       else
        as_bad (_(".option pic%d not supported"), i);
 
@@ -11379,7 +12313,7 @@ s_mipsset (x)
     }
   else if (strcmp (name, "noreorder") == 0)
     {
-      mips_emit_delays (true);
+      mips_emit_delays (TRUE);
       mips_opts.noreorder = 1;
       mips_any_noreorder = 1;
     }
@@ -11427,33 +12361,55 @@ s_mipsset (x)
     mips_opts.ase_mips3d = 1;
   else if (strcmp (name, "nomips3d") == 0)
     mips_opts.ase_mips3d = 0;
+  else if (strcmp (name, "mdmx") == 0)
+    mips_opts.ase_mdmx = 1;
+  else if (strcmp (name, "nomdmx") == 0)
+    mips_opts.ase_mdmx = 0;
   else if (strncmp (name, "mips", 4) == 0)
     {
-      int isa;
+      int reset = 0;
 
       /* Permit the user to change the ISA on the fly.  Needless to
         say, misuse can cause serious problems.  */
-      isa = atoi (name + 4);
-      switch (isa)
+      if (strcmp (name, "mips0") == 0)
+       {
+         reset = 1;
+         mips_opts.isa = file_mips_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);
+
+      switch (mips_opts.isa)
        {
        case  0:
-         mips_opts.gp32 = file_mips_gp32;
-         mips_opts.fp32 = file_mips_fp32;
-         mips_opts.abi = file_mips_abi;
          break;
-       case  1:
-       case  2:
-       case 32:
+       case ISA_MIPS1:
+       case ISA_MIPS2:
+       case ISA_MIPS32:
+       case ISA_MIPS32R2:
          mips_opts.gp32 = 1;
          mips_opts.fp32 = 1;
          break;
-       case  3:
-       case  4:
-       case  5:
-       case 64:
-         /* Loosen ABI register width restriction.  */
-         if (mips_opts.abi == O32_ABI)
-           mips_opts.abi = NO_ABI;
+       case ISA_MIPS3:
+       case ISA_MIPS4:
+       case ISA_MIPS5:
+       case ISA_MIPS64:
          mips_opts.gp32 = 0;
          mips_opts.fp32 = 0;
          break;
@@ -11461,18 +12417,10 @@ s_mipsset (x)
          as_bad (_("unknown ISA level %s"), name + 4);
          break;
        }
-
-      switch (isa)
+      if (reset)
        {
-       case  0: mips_opts.isa = file_mips_isa;   break;
-       case  1: mips_opts.isa = ISA_MIPS1;       break;
-       case  2: mips_opts.isa = ISA_MIPS2;       break;
-       case  3: mips_opts.isa = ISA_MIPS3;       break;
-       case  4: mips_opts.isa = ISA_MIPS4;       break;
-       case  5: mips_opts.isa = ISA_MIPS5;       break;
-       case 32: mips_opts.isa = ISA_MIPS32;      break;
-       case 64: mips_opts.isa = ISA_MIPS64;      break;
-       default: as_bad (_("unknown ISA level %s"), name + 4); break;
+         mips_opts.gp32 = file_mips_gp32;
+         mips_opts.fp32 = file_mips_fp32;
        }
     }
   else if (strcmp (name, "autoextend") == 0)
@@ -11500,7 +12448,7 @@ 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);
+           mips_emit_delays (TRUE);
          else if (! s->options.noreorder && mips_opts.noreorder)
            {
              if (prev_nop_frag != NULL)
@@ -11532,6 +12480,7 @@ s_abicalls (ignore)
      int ignore ATTRIBUTE_UNUSED;
 {
   mips_pic = SVR4_PIC;
+  mips_abicalls = TRUE;
   if (USE_GLOBAL_POINTER_OPT)
     {
       if (g_switch_seen && g_switch_value != 0)
@@ -11595,15 +12544,15 @@ s_cpload (ignore)
    If offset is given, this results in:
      sd                $gp, offset($sp)
      lui       $gp, %hi(%neg(%gp_rel(label)))
-     daddiu    $gp, $gp, %lo(%neg(%gp_rel(label)))
-     addu      $gp, $gp, $reg1
+     addiu     $gp, $gp, %lo(%neg(%gp_rel(label)))
+     daddu     $gp, $gp, $reg1
 
    If $reg2 is given, this results in:
      daddu     $reg2, $gp, $0
      lui       $gp, %hi(%neg(%gp_rel(label)))
-     daddiu    $gp, $gp, %lo(%neg(%gp_rel(label)))
-     addu      $gp, $gp, $reg1
- */
+     addiu     $gp, $gp, %lo(%neg(%gp_rel(label)))
+     daddu     $gp, $gp, $reg1
  $reg1 is normally $25 == $t9.  */
 static void
 s_cpsetup (ignore)
      int ignore ATTRIBUTE_UNUSED;
@@ -11612,7 +12561,7 @@ s_cpsetup (ignore)
   expressionS ex_sym;
   int reg1;
   int icnt = 0;
-  char *sym;
+  char *f;
 
   /* If we are not generating SVR4 PIC code, .cpsetup is ignored.
      We also need NewABI support.  */
@@ -11630,12 +12579,18 @@ s_cpsetup (ignore)
       return;
     }
   else
-    input_line_pointer++;
+    ++input_line_pointer;
   SKIP_WHITESPACE ();
   if (*input_line_pointer == '$')
-    mips_cpreturn_register = tc_get_register (0);
+    {
+      mips_cpreturn_register = tc_get_register (0);
+      mips_cpreturn_offset = -1;
+    }
   else
-    mips_cpreturn_offset = get_absolute_expression ();
+    {
+      mips_cpreturn_offset = get_absolute_expression ();
+      mips_cpreturn_register = -1;
+    }
   SKIP_WHITESPACE ();
   if (*input_line_pointer != ',')
     {
@@ -11645,15 +12600,7 @@ s_cpsetup (ignore)
   else
     ++input_line_pointer;
   SKIP_WHITESPACE ();
-  sym = input_line_pointer;
-  while (ISALNUM (*input_line_pointer))
-    ++input_line_pointer;
-  *input_line_pointer = 0;
-
-  ex_sym.X_op = O_symbol;
-  ex_sym.X_add_symbol = symbol_find_or_make (sym);
-  ex_sym.X_op_symbol = NULL;
-  ex_sym.X_add_number = 0;
+  expression (&ex_sym);
 
   if (mips_cpreturn_register == -1)
     {
@@ -11669,17 +12616,27 @@ s_cpsetup (ignore)
     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, prev_insn_where, 0, NULL, 0, 0, BFD_RELOC_MIPS_SUB);
-  fix_new (frag_now, prev_insn_where, 0, NULL, 0, 0, BFD_RELOC_HI16_S);
+  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, prev_insn_where, 0, NULL, 0, 0, BFD_RELOC_MIPS_SUB);
-  fix_new (frag_now, prev_insn_where, 0, NULL, 0, 0, BFD_RELOC_LO16);
-  macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-              HAVE_64BIT_ADDRESSES ? "daddu" : "addu", "d,v,t",
-              mips_gp_register, mips_gp_register, reg1);
+  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, ADDRESS_ADD_INSN,
+              "d,v,t", mips_gp_register, mips_gp_register, reg1);
 
   demand_empty_rest_of_line ();
 }
@@ -11697,6 +12654,7 @@ s_cplocal (ignore)
     }
 
   mips_gp_register = tc_get_register (0);
+  demand_empty_rest_of_line ();
 }
 
 /* Handle the .cprestore pseudo-op.  This stores $gp into a given
@@ -11726,8 +12684,8 @@ s_cprestore (ignore)
   ex.X_op_symbol = NULL;
   ex.X_add_number = mips_cprestore_offset;
 
-  macro_build ((char *) NULL, &icnt, &ex, HAVE_32BIT_ADDRESSES ? "sw" : "sd",
-              "t,o(b)", mips_gp_register, (int) BFD_RELOC_LO16, SP);
+  macro_build_ldst_constoffset ((char *) NULL, &icnt, &ex, ADDRESS_STORE_INSN,
+                               mips_gp_register, SP);
 
   demand_empty_rest_of_line ();
 }
@@ -11786,7 +12744,7 @@ s_gpvalue (ignore)
       return;
     }
 
-  mips_cpreturn_offset = get_absolute_expression ();
+  mips_gprel_offset = get_absolute_expression ();
 
   demand_empty_rest_of_line ();
 }
@@ -11810,7 +12768,7 @@ s_gpword (ignore)
     }
 
   label = insn_labels != NULL ? insn_labels->label : NULL;
-  mips_emit_delays (true);
+  mips_emit_delays (TRUE);
   if (auto_align)
     mips_align (2, 0, label);
   mips_clear_insn_labels ();
@@ -11825,9 +12783,53 @@ s_gpword (ignore)
 
   p = frag_more (4);
   md_number_to_chars (p, (valueT) 0, 4);
-  fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, false,
+  fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, FALSE,
+              BFD_RELOC_GPREL32);
+
+  demand_empty_rest_of_line ();
+}
+
+static void
+s_gpdword (ignore)
+     int ignore ATTRIBUTE_UNUSED;
+{
+  symbolS *label;
+  expressionS ex;
+  char *p;
+
+  /* When not generating PIC code, this is treated as .dword.  */
+  if (mips_pic != SVR4_PIC)
+    {
+      s_cons (3);
+      return;
+    }
+
+  label = insn_labels != NULL ? insn_labels->label : NULL;
+  mips_emit_delays (TRUE);
+  if (auto_align)
+    mips_align (3, 0, label);
+  mips_clear_insn_labels ();
+
+  expression (&ex);
+
+  if (ex.X_op != O_symbol || ex.X_add_number != 0)
+    {
+      as_bad (_("Unsupported use of .gpdword"));
+      ignore_rest_of_line ();
+    }
+
+  p = frag_more (8);
+  md_number_to_chars (p, (valueT) 0, 8);
+  fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, FALSE,
               BFD_RELOC_GPREL32);
 
+  /* 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);
+
   demand_empty_rest_of_line ();
 }
 
@@ -11841,9 +12843,8 @@ s_cpadd (ignore)
   int icnt = 0;
   int reg;
 
-  /* This is ignored when not generating SVR4 PIC code or if this is NewABI
-     code.  */
-  if (mips_pic != SVR4_PIC || HAVE_NEWABI)
+  /* This is ignored when not generating SVR4 PIC code.  */
+  if (mips_pic != SVR4_PIC)
     {
       s_ignore (0);
       return;
@@ -11851,8 +12852,7 @@ s_cpadd (ignore)
 
   /* Add $gp to the register named as an argument.  */
   reg = tc_get_register (0);
-  macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-              HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
+  macro_build ((char *) NULL, &icnt, (expressionS *) NULL, ADDRESS_ADD_INSN,
               "d,v,t", reg, reg, mips_gp_register);
 
   demand_empty_rest_of_line ();
@@ -11955,7 +12955,7 @@ tc_get_register (frame)
   if (*input_line_pointer++ != '$')
     {
       as_warn (_("expected `$'"));
-      reg = 0;
+      reg = ZERO;
     }
   else if (ISDIGIT (*input_line_pointer))
     {
@@ -11963,25 +12963,58 @@ tc_get_register (frame)
       if (reg < 0 || reg >= 32)
        {
          as_warn (_("Bad register number"));
-         reg = 0;
+         reg = ZERO;
        }
     }
   else
     {
-      if (strncmp (input_line_pointer, "fp", 2) == 0)
-       reg = FP;
+      if (strncmp (input_line_pointer, "ra", 2) == 0)
+       {
+         reg = RA;
+         input_line_pointer += 2;
+       }
+      else if (strncmp (input_line_pointer, "fp", 2) == 0)
+       {
+         reg = FP;
+         input_line_pointer += 2;
+       }
       else if (strncmp (input_line_pointer, "sp", 2) == 0)
-       reg = SP;
+       {
+         reg = SP;
+         input_line_pointer += 2;
+       }
       else if (strncmp (input_line_pointer, "gp", 2) == 0)
-       reg = GP;
+       {
+         reg = GP;
+         input_line_pointer += 2;
+       }
       else if (strncmp (input_line_pointer, "at", 2) == 0)
-       reg = AT;
+       {
+         reg = AT;
+         input_line_pointer += 2;
+       }
+      else if (strncmp (input_line_pointer, "kt0", 3) == 0)
+       {
+         reg = KT0;
+         input_line_pointer += 3;
+       }
+      else if (strncmp (input_line_pointer, "kt1", 3) == 0)
+       {
+         reg = KT1;
+         input_line_pointer += 3;
+       }
+      else if (strncmp (input_line_pointer, "zero", 4) == 0)
+       {
+         reg = ZERO;
+         input_line_pointer += 4;
+       }
       else
        {
          as_warn (_("Unrecognized register name"));
-         reg = 0;
+         reg = ZERO;
+         while (ISALNUM(*input_line_pointer))
+          input_line_pointer++;
        }
-      input_line_pointer += 2;
     }
   if (frame)
     {
@@ -12085,6 +13118,64 @@ nopic_need_relax (sym, before_relaxing)
     return 1;
 }
 
+
+/* 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;
+{
+  asection *symsec;
+  bfd_boolean linkonce;
+
+  /* Handle the case of a symbol equated to another symbol.  */
+  while (symbol_equated_reloc_p (sym))
+    {
+      symbolS *n;
+
+      /* It's possible to get a loop here in a badly written
+        program.  */
+      n = symbol_get_value_expression (sym)->X_add_symbol;
+      if (n == sym)
+       break;
+      sym = n;
+    }
+
+  symsec = S_GET_SEGMENT (sym);
+
+  /* duplicate the test for LINK_ONCE sections as in adjust_reloc_syms */
+  linkonce = FALSE;
+  if (symsec != segtype && ! S_IS_LOCAL (sym))
+    {
+      if ((bfd_get_section_flags (stdoutput, symsec) & SEC_LINK_ONCE)
+         != 0)
+       linkonce = TRUE;
+
+      /* The GNU toolchain uses an extension for ELF: a section
+        beginning with the magic string .gnu.linkonce is a linkonce
+        section.  */
+      if (strncmp (segment_name (symsec), ".gnu.linkonce",
+                  sizeof ".gnu.linkonce" - 1) == 0)
+       linkonce = TRUE;
+    }
+
+  /* This must duplicate the test in adjust_reloc_syms.  */
+  return (symsec != &bfd_und_section
+         && symsec != &bfd_abs_section
+         && ! bfd_is_com_section (symsec)
+         && !linkonce
+#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)))
+#endif
+         );
+}
+
+
 /* Given a mips16 variant frag FRAGP, return non-zero if it needs an
    extended opcode.  SEC is the section the frag is in.  */
 
@@ -12286,6 +13377,73 @@ mips16_extended_frag (fragp, sec, stretch)
     return 0;
 }
 
+/* Compute the length of a branch sequence, and adjust the
+   RELAX_BRANCH_TOOFAR bit accordingly.  If FRAGP is NULL, the
+   worst-case length is computed, with UPDATE being used to indicate
+   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;
+{
+  bfd_boolean toofar;
+  int length;
+
+  if (fragp
+      && S_IS_DEFINED (fragp->fr_symbol)
+      && sec == S_GET_SEGMENT (fragp->fr_symbol))
+    {
+      addressT addr;
+      offsetT val;
+
+      val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
+
+      addr = fragp->fr_address + fragp->fr_fix + 4;
+
+      val -= addr;
+
+      toofar = val < - (0x8000 << 2) || val >= (0x8000 << 2);
+    }
+  else if (fragp)
+    /* If the symbol is not defined or it's in a different segment,
+       assume the user knows what's going on and emit a short
+       branch.  */
+    toofar = FALSE;
+  else
+    toofar = TRUE;
+
+  if (fragp && update && toofar != RELAX_BRANCH_TOOFAR (fragp->fr_subtype))
+    fragp->fr_subtype
+      = RELAX_BRANCH_ENCODE (RELAX_BRANCH_UNCOND (fragp->fr_subtype),
+                            RELAX_BRANCH_LIKELY (fragp->fr_subtype),
+                            RELAX_BRANCH_LINK (fragp->fr_subtype),
+                            toofar);
+
+  length = 4;
+  if (toofar)
+    {
+      if (fragp ? RELAX_BRANCH_LIKELY (fragp->fr_subtype) : (update > 0))
+       length += 8;
+
+      if (mips_pic != NO_PIC)
+       {
+         /* Additional space for PIC loading of target address.  */
+         length += 8;
+         if (mips_opts.isa == ISA_MIPS1)
+           /* Additional space for $at-stabilizing nop.  */
+           length += 4;
+       }
+
+      /* If branch is conditional.  */
+      if (fragp ? !RELAX_BRANCH_UNCOND (fragp->fr_subtype) : (update >= 0))
+       length += 8;
+    }
+
+  return length;
+}
+
 /* Estimate the size of a frag before relaxing.  Unless this is the
    mips16, we are not really relaxing here, and the final size is
    encoded in the subtype information.  For the mips16, we have to
@@ -12296,8 +13454,15 @@ md_estimate_size_before_relax (fragp, segtype)
      fragS *fragp;
      asection *segtype;
 {
-  int change = 0;
-  boolean linkonce = false;
+  int change;
+
+  if (RELAX_BRANCH_P (fragp->fr_subtype))
+    {
+
+      fragp->fr_var = relaxed_branch_length (fragp, segtype, FALSE);
+
+      return fragp->fr_var;
+    }
 
   if (RELAX_MIPS16_P (fragp->fr_subtype))
     /* We don't want to modify the EXTENDED bit here; it might get us
@@ -12305,59 +13470,9 @@ md_estimate_size_before_relax (fragp, segtype)
     return (RELAX_MIPS16_EXTENDED (fragp->fr_subtype) ? 4 : 2);
 
   if (mips_pic == NO_PIC)
-    {
-      change = nopic_need_relax (fragp->fr_symbol, 0);
-    }
+    change = nopic_need_relax (fragp->fr_symbol, 0);
   else if (mips_pic == SVR4_PIC)
-    {
-      symbolS *sym;
-      asection *symsec;
-
-      sym = fragp->fr_symbol;
-
-      /* Handle the case of a symbol equated to another symbol.  */
-      while (symbol_equated_reloc_p (sym))
-       {
-         symbolS *n;
-
-         /* It's possible to get a loop here in a badly written
-             program.  */
-         n = symbol_get_value_expression (sym)->X_add_symbol;
-         if (n == sym)
-           break;
-         sym = n;
-       }
-
-      symsec = S_GET_SEGMENT (sym);
-
-      /* duplicate the test for LINK_ONCE sections as in adjust_reloc_syms */
-      if (symsec != segtype && ! S_IS_LOCAL (sym))
-       {
-         if ((bfd_get_section_flags (stdoutput, symsec) & SEC_LINK_ONCE)
-             != 0)
-           linkonce = true;
-
-         /* The GNU toolchain uses an extension for ELF: a section
-            beginning with the magic string .gnu.linkonce is a linkonce
-            section.  */
-         if (strncmp (segment_name (symsec), ".gnu.linkonce",
-                      sizeof ".gnu.linkonce" - 1) == 0)
-           linkonce = true;
-       }
-
-      /* This must duplicate the test in adjust_reloc_syms.  */
-      change = (symsec != &bfd_und_section
-               && symsec != &bfd_abs_section
-               && ! bfd_is_com_section (symsec)
-               && !linkonce
-#ifdef OBJ_ELF
-               /* A global or weak symbol is treated as external.  */
-               && (OUTPUT_FLAVOR != bfd_target_elf_flavour
-                   || (! S_IS_WEAK (sym)
-                       && (! S_IS_EXTERN (sym) || mips_pic == EMBEDDED_PIC)))
-#endif
-               );
-    }
+    change = pic_need_relax (fragp->fr_symbol, segtype);
   else
     abort ();
 
@@ -12392,26 +13507,23 @@ int
 mips_fix_adjustable (fixp)
      fixS *fixp;
 {
-#ifdef OBJ_ELF
-  /* Prevent all adjustments to global symbols.  */
-  if (OUTPUT_FLAVOR == bfd_target_elf_flavour
-      && mips_pic != EMBEDDED_PIC
-      && (S_IS_EXTERN (fixp->fx_addsy) || S_IS_WEAK (fixp->fx_addsy)))
-    return 0;
-#endif
   if (fixp->fx_r_type == BFD_RELOC_MIPS16_JMP)
     return 0;
+
   if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
       || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
     return 0;
+
   if (fixp->fx_addsy == NULL)
     return 1;
+
 #ifdef OBJ_ELF
   if (OUTPUT_FLAVOR == bfd_target_elf_flavour
       && S_GET_OTHER (fixp->fx_addsy) == STO_MIPS16
       && fixp->fx_subsy == NULL)
     return 0;
 #endif
+
   return 1;
 }
 
@@ -12427,9 +13539,8 @@ tc_gen_reloc (section, fixp)
   arelent *reloc;
   bfd_reloc_code_real_type code;
 
-  reloc = retval[0] = (arelent *) xmalloc (sizeof (arelent));
-  retval[1] = NULL;
-
+  memset (retval, 0, sizeof(retval));
+  reloc = retval[0] = (arelent *) xcalloc (1, sizeof (arelent));
   reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
   *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
   reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
@@ -12495,14 +13606,17 @@ tc_gen_reloc (section, fixp)
   /* 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
+      && ((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)
-      && ! HAVE_NEWABI)
+    )
     {
       arelent *reloc2;
 
@@ -12529,14 +13643,15 @@ tc_gen_reloc (section, fixp)
 
       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));
-      retval[2] = NULL;
       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;
+      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);
 
@@ -12545,7 +13660,6 @@ tc_gen_reloc (section, fixp)
          arelent *reloc3;
 
          reloc3 = retval[2] = (arelent *) xmalloc (sizeof (arelent));
-         retval[3] = NULL;
          *reloc3 = *reloc2;
          reloc3->address += 4;
        }
@@ -12563,10 +13677,32 @@ tc_gen_reloc (section, fixp)
              abort ();
            case BFD_RELOC_MIPS_GOT16:
              break;
-           case BFD_RELOC_MIPS_CALL16:
            case BFD_RELOC_MIPS_GOT_LO16:
            case BFD_RELOC_MIPS_CALL_LO16:
-             fixp->fx_r_type = BFD_RELOC_MIPS_GOT16;
+             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;
            }
        }
@@ -12574,9 +13710,9 @@ tc_gen_reloc (section, fixp)
        abort ();
     }
 
-  /* Since MIPS ELF uses Rel instead of Rela, encode the vtable entry
-     to be used in the relocation's section offset.  */
-  if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+  /* Since the old MIPS ELF ABI uses Rel instead of Rela, encode the vtable
+     entry to be used in the relocation's section offset.  */
+  if (! HAVE_NEWABI && fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
     {
       reloc->address = reloc->addend;
       reloc->addend = 0;
@@ -12618,19 +13754,6 @@ tc_gen_reloc (section, fixp)
        }
     }
 
-#ifdef OBJ_ELF
-  /* md_apply_fix3 has a double-subtraction hack to get
-     bfd_install_relocation to behave nicely.  GPREL relocations are
-     handled correctly without this hack, so undo it here.  We can't
-     stop md_apply_fix3 from subtracting twice in the first place since
-     the fake addend is required for variant frags above.  */
-  if (fixp->fx_addsy != NULL && OUTPUT_FLAVOR == bfd_target_elf_flavour
-      && code == BFD_RELOC_GPREL16
-      && reloc->addend != 0
-      && mips_need_elf_addend_fixup (fixp))
-    reloc->addend += S_GET_VALUE (fixp->fx_addsy);
-#endif
-
   /* 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.  */
@@ -12657,10 +13780,20 @@ tc_gen_reloc (section, fixp)
    the current size of the frag should change.  */
 
 int
-mips_relax_frag (fragp, stretch)
+mips_relax_frag (sec, fragp, stretch)
+     asection *sec;
      fragS *fragp;
      long stretch;
 {
+  if (RELAX_BRANCH_P (fragp->fr_subtype))
+    {
+      offsetT old_var = fragp->fr_var;
+
+      fragp->fr_var = relaxed_branch_length (fragp, sec, TRUE);
+
+      return fragp->fr_var - old_var;
+    }
+
   if (! RELAX_MIPS16_P (fragp->fr_subtype))
     return 0;
 
@@ -12693,15 +13826,223 @@ md_convert_frag (abfd, asec, fragp)
   int old, new;
   char *fixptr;
 
+  if (RELAX_BRANCH_P (fragp->fr_subtype))
+    {
+      bfd_byte *buf;
+      unsigned long insn;
+      expressionS exp;
+      fixS *fixp;
+
+      buf = (bfd_byte *)fragp->fr_literal + fragp->fr_fix;
+
+      if (target_big_endian)
+       insn = bfd_getb32 (buf);
+      else
+       insn = bfd_getl32 (buf);
+
+      if (!RELAX_BRANCH_TOOFAR (fragp->fr_subtype))
+       {
+         /* We generate a fixup instead of applying it right now
+            because, if there are linker relaxations, we're going to
+            need the relocations.  */
+         exp.X_op = O_symbol;
+         exp.X_add_symbol = fragp->fr_symbol;
+         exp.X_add_number = fragp->fr_offset;
+
+         fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
+                             4, &exp, 1,
+                             BFD_RELOC_16_PCREL_S2);
+         fixp->fx_file = fragp->fr_file;
+         fixp->fx_line = fragp->fr_line;
+
+         md_number_to_chars ((char *)buf, insn, 4);
+         buf += 4;
+       }
+      else
+       {
+         int i;
+
+         as_warn_where (fragp->fr_file, fragp->fr_line,
+                        _("relaxed out-of-range branch into a jump"));
+
+         if (RELAX_BRANCH_UNCOND (fragp->fr_subtype))
+           goto uncond;
+
+         if (!RELAX_BRANCH_LIKELY (fragp->fr_subtype))
+           {
+             /* Reverse the branch.  */
+             switch ((insn >> 28) & 0xf)
+               {
+               case 4:
+                 /* bc[0-3][tf]l? and bc1any[24][ft] instructions can
+                    have the condition reversed by tweaking a single
+                    bit, and their opcodes all have 0x4???????.  */
+                 assert ((insn & 0xf1000000) == 0x41000000);
+                 insn ^= 0x00010000;
+                 break;
+
+               case 0:
+                 /* bltz       0x04000000      bgez    0x04010000
+                    bltzal     0x04100000      bgezal  0x04110000 */
+                 assert ((insn & 0xfc0e0000) == 0x04000000);
+                 insn ^= 0x00010000;
+                 break;
+
+               case 1:
+                 /* beq        0x10000000      bne     0x14000000
+                    blez       0x18000000      bgtz    0x1c000000 */
+                 insn ^= 0x04000000;
+                 break;
+
+               default:
+                 abort ();
+               }
+           }
+
+         if (RELAX_BRANCH_LINK (fragp->fr_subtype))
+           {
+             /* Clear the and-link bit.  */
+             assert ((insn & 0xfc1c0000) == 0x04100000);
+
+             /* bltzal 0x04100000      bgezal  0x04110000
+               bltzall 0x04120000     bgezall  0x04130000 */
+             insn &= ~0x00100000;
+           }
+
+         /* Branch over the branch (if the branch was likely) or the
+            full jump (not likely case).  Compute the offset from the
+            current instruction to branch to.  */
+         if (RELAX_BRANCH_LIKELY (fragp->fr_subtype))
+           i = 16;
+         else
+           {
+             /* How many bytes in instructions we've already emitted?  */
+             i = buf - (bfd_byte *)fragp->fr_literal - fragp->fr_fix;
+             /* How many bytes in instructions from here to the end?  */
+             i = fragp->fr_var - i;
+           }
+         /* Convert to instruction count.  */
+         i >>= 2;
+         /* Branch counts from the next instruction.  */
+         i--;
+         insn |= i;
+         /* Branch over the jump.  */
+         md_number_to_chars ((char *)buf, insn, 4);
+         buf += 4;
+
+         /* Nop */
+         md_number_to_chars ((char*)buf, 0, 4);
+         buf += 4;
+
+         if (RELAX_BRANCH_LIKELY (fragp->fr_subtype))
+           {
+             /* beql $0, $0, 2f */
+             insn = 0x50000000;
+             /* Compute the PC offset from the current instruction to
+                the end of the variable frag.  */
+             /* How many bytes in instructions we've already emitted?  */
+             i = buf - (bfd_byte *)fragp->fr_literal - fragp->fr_fix;
+             /* How many bytes in instructions from here to the end?  */
+             i = fragp->fr_var - i;
+             /* Convert to instruction count.  */
+             i >>= 2;
+             /* Don't decrement i, because we want to branch over the
+                delay slot.  */
+
+             insn |= i;
+             md_number_to_chars ((char *)buf, insn, 4);
+             buf += 4;
+
+             md_number_to_chars ((char *)buf, 0, 4);
+             buf += 4;
+           }
+
+       uncond:
+         if (mips_pic == NO_PIC)
+           {
+             /* j or jal.  */
+             insn = (RELAX_BRANCH_LINK (fragp->fr_subtype)
+                     ? 0x0c000000 : 0x08000000);
+             exp.X_op = O_symbol;
+             exp.X_add_symbol = fragp->fr_symbol;
+             exp.X_add_number = fragp->fr_offset;
+
+             fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
+                                 4, &exp, 0, BFD_RELOC_MIPS_JMP);
+             fixp->fx_file = fragp->fr_file;
+             fixp->fx_line = fragp->fr_line;
+
+             md_number_to_chars ((char*)buf, insn, 4);
+             buf += 4;
+           }
+         else
+           {
+             /* lw/ld $at, <sym>($gp)  R_MIPS_GOT16 */
+             insn = HAVE_64BIT_ADDRESSES ? 0xdf810000 : 0x8f810000;
+             exp.X_op = O_symbol;
+             exp.X_add_symbol = fragp->fr_symbol;
+             exp.X_add_number = fragp->fr_offset;
+
+             if (fragp->fr_offset)
+               {
+                 exp.X_add_symbol = make_expr_symbol (&exp);
+                 exp.X_add_number = 0;
+               }
+
+             fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
+                                 4, &exp, 0, BFD_RELOC_MIPS_GOT16);
+             fixp->fx_file = fragp->fr_file;
+             fixp->fx_line = fragp->fr_line;
+
+             md_number_to_chars ((char*)buf, insn, 4);
+             buf += 4;
+
+             if (mips_opts.isa == ISA_MIPS1)
+               {
+                 /* nop */
+                 md_number_to_chars ((char*)buf, 0, 4);
+                 buf += 4;
+               }
+
+             /* d/addiu $at, $at, <sym>  R_MIPS_LO16 */
+             insn = HAVE_64BIT_ADDRESSES ? 0x64210000 : 0x24210000;
+
+             fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
+                                 4, &exp, 0, BFD_RELOC_LO16);
+             fixp->fx_file = fragp->fr_file;
+             fixp->fx_line = fragp->fr_line;
+
+             md_number_to_chars ((char*)buf, insn, 4);
+             buf += 4;
+
+             /* j(al)r $at.  */
+             if (RELAX_BRANCH_LINK (fragp->fr_subtype))
+               insn = 0x0020f809;
+             else
+               insn = 0x00200008;
+
+             md_number_to_chars ((char*)buf, insn, 4);
+             buf += 4;
+           }
+       }
+
+      assert (buf == (bfd_byte *)fragp->fr_literal
+             + fragp->fr_fix + fragp->fr_var);
+
+      fragp->fr_fix += fragp->fr_var;
+
+      return;
+    }
+
   if (RELAX_MIPS16_P (fragp->fr_subtype))
     {
       int type;
       register const struct mips16_immed_operand *op;
-      boolean small, ext;
+      bfd_boolean small, ext;
       offsetT val;
       bfd_byte *buf;
       unsigned long insn;
-      boolean use_extend;
+      bfd_boolean use_extend;
       unsigned short extend;
 
       type = RELAX_MIPS16_TYPE (fragp->fr_subtype);
@@ -12711,13 +14052,13 @@ md_convert_frag (abfd, asec, fragp)
 
       if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
        {
-         small = false;
-         ext = true;
+         small = FALSE;
+         ext = TRUE;
        }
       else
        {
-         small = true;
-         ext = false;
+         small = TRUE;
+         ext = FALSE;
        }
 
       resolve_symbol_value (fragp->fr_symbol);
@@ -12792,7 +14133,7 @@ md_convert_frag (abfd, asec, fragp)
       fixptr = fragp->fr_literal + fragp->fr_fix;
 
       if (new > 0)
-       memcpy (fixptr - old, fixptr, new);
+       memmove (fixptr - old, fixptr, new);
 
       fragp->fr_fix += new - old;
     }
@@ -12862,7 +14203,7 @@ void
 mips_elf_final_processing ()
 {
   /* Write out the register information.  */
-  if (file_mips_abi != N64_ABI)
+  if (mips_abi != N64_ABI)
     {
       Elf32_RegInfo s;
 
@@ -12899,29 +14240,36 @@ mips_elf_final_processing ()
   if (mips_any_noreorder)
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_NOREORDER;
   if (mips_pic != NO_PIC)
+    {
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_PIC;
+      elf_elfheader (stdoutput)->e_flags |= EF_MIPS_CPIC;
+    }
+  if (mips_abicalls)
+    elf_elfheader (stdoutput)->e_flags |= EF_MIPS_CPIC;
 
   /* Set MIPS ELF flags for ASEs.  */
+  if (file_ase_mips16)
+    elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_M16;
 #if 0 /* XXX FIXME */
   if (file_ase_mips3d)
     elf_elfheader (stdoutput)->e_flags |= ???;
 #endif
+  if (file_ase_mdmx)
+    elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_MDMX;
 
   /* Set the MIPS ELF ABI flags.  */
-  if (file_mips_abi == NO_ABI)
-    ;
-  else if (file_mips_abi == O32_ABI)
+  if (mips_abi == O32_ABI && USE_E_MIPS_ABI_O32)
     elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_O32;
-  else if (file_mips_abi == O64_ABI)
+  else if (mips_abi == O64_ABI)
     elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_O64;
-  else if (file_mips_abi == EABI_ABI)
+  else if (mips_abi == EABI_ABI)
     {
-      if (mips_eabi64)
+      if (!file_mips_gp32)
        elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_EABI64;
       else
        elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_EABI32;
     }
-  else if (file_mips_abi == N32_ABI)
+  else if (mips_abi == N32_ABI)
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ABI2;
 
   /* Nothing to do for N64_ABI.  */
@@ -13045,14 +14393,47 @@ get_number ()
 }
 
 /* The .file directive; just like the usual .file directive, but there
-   is an initial number which is the ECOFF file index.  */
+   is an initial number which is the ECOFF file index.  In the non-ECOFF
+   case .file implies DWARF-2.  */
+
+static void
+s_mips_file (x)
+     int x ATTRIBUTE_UNUSED;
+{
+  static int first_file_directive = 0;
+
+  if (ECOFF_DEBUGGING)
+    {
+      get_number ();
+      s_app_file (0);
+    }
+  else
+    {
+      char *filename;
+
+      filename = dwarf2_directive_file (0);
+
+      /* Versions of GCC up to 3.1 start files with a ".file"
+        directive even for stabs output.  Make sure that this
+        ".file" is handled.  Note that you need a version of GCC
+         after 3.1 in order to support DWARF-2 on MIPS.  */
+      if (filename != NULL && ! first_file_directive)
+       {
+         (void) new_logical_line (filename, -1);
+         s_app_file_string (filename);
+       }
+      first_file_directive = 1;
+    }
+}
+
+/* The .loc directive, implying DWARF-2.  */
 
 static void
-s_file (x)
+s_mips_loc (x)
      int x ATTRIBUTE_UNUSED;
 {
-  get_number ();
-  s_app_file (0);
+  if (!ECOFF_DEBUGGING)
+    dwarf2_directive_loc (0);
 }
 
 /* The .end directive.  */
@@ -13062,7 +14443,6 @@ s_mips_end (x)
      int x ATTRIBUTE_UNUSED;
 {
   symbolS *p;
-  int maybe_text;
 
   /* Following functions need their own .frame and .cprestore directives.  */
   mips_frame_reg_valid = 0;
@@ -13076,19 +14456,7 @@ s_mips_end (x)
   else
     p = NULL;
 
-#ifdef BFD_ASSEMBLER
-  if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
-    maybe_text = 1;
-  else
-    maybe_text = 0;
-#else
-  if (now_seg != data_section && now_seg != bss_section)
-    maybe_text = 1;
-  else
-    maybe_text = 0;
-#endif
-
-  if (!maybe_text)
+  if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) == 0)
     as_warn (_(".end not in text section"));
 
   if (!cur_proc_ptr)
@@ -13103,46 +14471,52 @@ s_mips_end (x)
       assert (S_GET_NAME (p));
       if (strcmp (S_GET_NAME (p), S_GET_NAME (cur_proc_ptr->isym)))
        as_warn (_(".end symbol does not match .ent symbol."));
+
+      if (debug_type == DEBUG_STABS)
+       stabs_generate_asm_endfunc (S_GET_NAME (p),
+                                   S_GET_NAME (p));
     }
   else
     as_warn (_(".end directive missing or unknown symbol"));
 
-#ifdef MIPS_STABS_ELF
-  {
-    segT saved_seg = now_seg;
-    subsegT saved_subseg = now_subseg;
-    valueT dot;
-    expressionS exp;
-    char *fragp;
+#ifdef OBJ_ELF
+  /* Generate a .pdr section.  */
+  if (OUTPUT_FLAVOR == bfd_target_elf_flavour && ! ECOFF_DEBUGGING)
+    {
+      segT saved_seg = now_seg;
+      subsegT saved_subseg = now_subseg;
+      valueT dot;
+      expressionS exp;
+      char *fragp;
 
-    dot = frag_now_fix ();
+      dot = frag_now_fix ();
 
 #ifdef md_flush_pending_output
-    md_flush_pending_output ();
+      md_flush_pending_output ();
 #endif
 
-    assert (pdr_seg);
-    subseg_set (pdr_seg, 0);
+      assert (pdr_seg);
+      subseg_set (pdr_seg, 0);
 
-    /* Write the symbol.  */
-    exp.X_op = O_symbol;
-    exp.X_add_symbol = p;
-    exp.X_add_number = 0;
-    emit_expr (&exp, 4);
+      /* Write the symbol.  */
+      exp.X_op = O_symbol;
+      exp.X_add_symbol = p;
+      exp.X_add_number = 0;
+      emit_expr (&exp, 4);
 
-    fragp = frag_more (7 * 4);
+      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,      (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);
 
-    subseg_set (saved_seg, saved_subseg);
-  }
-#endif /* MIPS_STABS_ELF */
+      subseg_set (saved_seg, saved_subseg);
+    }
+#endif /* OBJ_ELF */
 
   cur_proc_ptr = NULL;
 }
@@ -13154,7 +14528,6 @@ s_mips_ent (aent)
      int aent;
 {
   symbolS *symbolP;
-  int maybe_text;
 
   symbolP = get_symbol ();
   if (*input_line_pointer == ',')
@@ -13164,19 +14537,7 @@ s_mips_ent (aent)
       || *input_line_pointer == '-')
     get_number ();
 
-#ifdef BFD_ASSEMBLER
-  if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
-    maybe_text = 1;
-  else
-    maybe_text = 0;
-#else
-  if (now_seg != data_section && now_seg != bss_section)
-    maybe_text = 1;
-  else
-    maybe_text = 0;
-#endif
-
-  if (!maybe_text)
+  if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) == 0)
     as_warn (_(".ent or .aent not in text section."));
 
   if (!aent && cur_proc_ptr)
@@ -13196,6 +14557,10 @@ s_mips_ent (aent)
       symbol_get_bfdsym (symbolP)->flags |= BSF_FUNCTION;
 
       ++numprocs;
+
+      if (debug_type == DEBUG_STABS)
+        stabs_generate_asm_func (S_GET_NAME (symbolP),
+                                S_GET_NAME (symbolP));
     }
 
   demand_empty_rest_of_line ();
@@ -13211,36 +14576,38 @@ static void
 s_mips_frame (ignore)
      int ignore ATTRIBUTE_UNUSED;
 {
-#ifdef MIPS_STABS_ELF
+#ifdef OBJ_ELF
+  if (OUTPUT_FLAVOR == bfd_target_elf_flavour && ! ECOFF_DEBUGGING)
+    {
+      long val;
 
-  long val;
+      if (cur_proc_ptr == (procS *) NULL)
+       {
+         as_warn (_(".frame outside of .ent"));
+         demand_empty_rest_of_line ();
+         return;
+       }
 
-  if (cur_proc_ptr == (procS *) NULL)
-    {
-      as_warn (_(".frame outside of .ent"));
-      demand_empty_rest_of_line ();
-      return;
-    }
+      cur_proc_ptr->frame_reg = tc_get_register (1);
 
-  cur_proc_ptr->frame_reg = tc_get_register (1);
+      SKIP_WHITESPACE ();
+      if (*input_line_pointer++ != ','
+         || get_absolute_expression_and_terminator (&val) != ',')
+       {
+         as_warn (_("Bad .frame directive"));
+         --input_line_pointer;
+         demand_empty_rest_of_line ();
+         return;
+       }
+
+      cur_proc_ptr->frame_offset = val;
+      cur_proc_ptr->pc_reg = tc_get_register (0);
 
-  SKIP_WHITESPACE ();
-  if (*input_line_pointer++ != ','
-      || get_absolute_expression_and_terminator (&val) != ',')
-    {
-      as_warn (_("Bad .frame directive"));
-      --input_line_pointer;
       demand_empty_rest_of_line ();
-      return;
     }
-
-  cur_proc_ptr->frame_offset = val;
-  cur_proc_ptr->pc_reg = tc_get_register (0);
-
-  demand_empty_rest_of_line ();
-#else
-  s_ignore (ignore);
-#endif /* MIPS_STABS_ELF */
+  else
+#endif /* OBJ_ELF */
+    s_ignore (ignore);
 }
 
 /* The .fmask and .mask directives. If the mdebug section is present
@@ -13253,41 +14620,44 @@ static void
 s_mips_mask (reg_type)
      char reg_type;
 {
-#ifdef MIPS_STABS_ELF
-  long mask, off;
-
-  if (cur_proc_ptr == (procS *) NULL)
+#ifdef OBJ_ELF
+  if (OUTPUT_FLAVOR == bfd_target_elf_flavour && ! ECOFF_DEBUGGING)
     {
-      as_warn (_(".mask/.fmask outside of .ent"));
-      demand_empty_rest_of_line ();
-      return;
-    }
+      long mask, off;
 
-  if (get_absolute_expression_and_terminator (&mask) != ',')
-    {
-      as_warn (_("Bad .mask/.fmask directive"));
-      --input_line_pointer;
-      demand_empty_rest_of_line ();
-      return;
-    }
+      if (cur_proc_ptr == (procS *) NULL)
+       {
+         as_warn (_(".mask/.fmask outside of .ent"));
+         demand_empty_rest_of_line ();
+         return;
+       }
+
+      if (get_absolute_expression_and_terminator (&mask) != ',')
+       {
+         as_warn (_("Bad .mask/.fmask directive"));
+         --input_line_pointer;
+         demand_empty_rest_of_line ();
+         return;
+       }
 
-  off = get_absolute_expression ();
+      off = get_absolute_expression ();
 
-  if (reg_type == 'F')
-    {
-      cur_proc_ptr->fpreg_mask = mask;
-      cur_proc_ptr->fpreg_offset = off;
+      if (reg_type == 'F')
+       {
+         cur_proc_ptr->fpreg_mask = mask;
+         cur_proc_ptr->fpreg_offset = off;
+       }
+      else
+       {
+         cur_proc_ptr->reg_mask = mask;
+         cur_proc_ptr->reg_offset = off;
+       }
+
+      demand_empty_rest_of_line ();
     }
   else
-    {
-      cur_proc_ptr->reg_mask = mask;
-      cur_proc_ptr->reg_offset = off;
-    }
-
-  demand_empty_rest_of_line ();
-#else
-  s_ignore (reg_type);
-#endif /* MIPS_STABS_ELF */
+#endif /* OBJ_ELF */
+    s_ignore (reg_type);
 }
 
 /* The .loc directive.  */
@@ -13314,176 +14684,181 @@ s_loc (x)
 }
 #endif
 
-/* CPU name/ISA/number mapping table.
+/* A table describing all the processors gas knows about.  Names are
+   matched in the order listed.
 
-   Entries are grouped by type.  The first matching CPU or ISA entry
-   gets chosen by CPU or ISA, so it should be the 'canonical' name
-   for that type.  Entries after that within the type are sorted
-   alphabetically.
-
-   Case is ignored in comparison, so put the canonical entry in the
-   appropriate case but everything else in lower case to ease eye pain.  */
+   To ease comparison, please keep this table in the same order as
+   gcc's mips_cpu_info_table[].  */
 static const struct mips_cpu_info mips_cpu_info_table[] =
 {
-  /* MIPS1 ISA */
-  { "MIPS1",          1,      ISA_MIPS1,      CPU_R3000, },
-  { "mips",           1,      ISA_MIPS1,      CPU_R3000, },
+  /* Entries for generic ISAs */
+  { "mips1",          1,      ISA_MIPS1,      CPU_R3000 },
+  { "mips2",          1,      ISA_MIPS2,      CPU_R6000 },
+  { "mips3",          1,      ISA_MIPS3,      CPU_R4000 },
+  { "mips4",          1,      ISA_MIPS4,      CPU_R8000 },
+  { "mips5",          1,      ISA_MIPS5,      CPU_MIPS5 },
+  { "mips32",         1,      ISA_MIPS32,     CPU_MIPS32 },
+  { "mips32r2",       1,      ISA_MIPS32R2,   CPU_MIPS32R2 },
+  { "mips64",         1,      ISA_MIPS64,     CPU_MIPS64 },
+
+  /* MIPS I */
+  { "r3000",          0,      ISA_MIPS1,      CPU_R3000 },
+  { "r2000",          0,      ISA_MIPS1,      CPU_R3000 },
+  { "r3900",          0,      ISA_MIPS1,      CPU_R3900 },
+
+  /* MIPS II */
+  { "r6000",          0,      ISA_MIPS2,      CPU_R6000 },
+
+  /* MIPS III */
+  { "r4000",          0,      ISA_MIPS3,      CPU_R4000 },
+  { "r4010",          0,      ISA_MIPS2,      CPU_R4010 },
+  { "vr4100",         0,      ISA_MIPS3,      CPU_VR4100 },
+  { "vr4111",         0,      ISA_MIPS3,      CPU_R4111 },
+  { "vr4120",         0,      ISA_MIPS3,      CPU_VR4120 },
+  { "vr4130",         0,      ISA_MIPS3,      CPU_VR4120 },
+  { "vr4181",         0,      ISA_MIPS3,      CPU_R4111 },
+  { "vr4300",         0,      ISA_MIPS3,      CPU_R4300 },
+  { "r4400",          0,      ISA_MIPS3,      CPU_R4400 },
+  { "r4600",          0,      ISA_MIPS3,      CPU_R4600 },
+  { "orion",          0,      ISA_MIPS3,      CPU_R4600 },
+  { "r4650",          0,      ISA_MIPS3,      CPU_R4650 },
+
+  /* MIPS IV */
+  { "r8000",          0,      ISA_MIPS4,      CPU_R8000 },
+  { "r10000",         0,      ISA_MIPS4,      CPU_R10000 },
+  { "r12000",         0,      ISA_MIPS4,      CPU_R12000 },
+  { "vr5000",         0,      ISA_MIPS4,      CPU_R5000 },
+  { "vr5400",         0,      ISA_MIPS4,      CPU_VR5400 },
+  { "vr5500",         0,      ISA_MIPS4,      CPU_VR5500 },
+  { "rm5200",         0,      ISA_MIPS4,      CPU_R5000 },
+  { "rm5230",         0,      ISA_MIPS4,      CPU_R5000 },
+  { "rm5231",         0,      ISA_MIPS4,      CPU_R5000 },
+  { "rm5261",         0,      ISA_MIPS4,      CPU_R5000 },
+  { "rm5721",         0,      ISA_MIPS4,      CPU_R5000 },
+  { "r7000",          0,      ISA_MIPS4,      CPU_R5000 },
+
+  /* MIPS 32 */
+  { "4kc",            0,      ISA_MIPS32,     CPU_MIPS32, },
+  { "4km",            0,      ISA_MIPS32,     CPU_MIPS32 },
+  { "4kp",            0,      ISA_MIPS32,     CPU_MIPS32 },
 
-  /* MIPS2 ISA */
-  { "MIPS2",          1,      ISA_MIPS2,      CPU_R6000, },
+  /* MIPS 64 */
+  { "5kc",            0,      ISA_MIPS64,     CPU_MIPS64 },
+  { "20kc",           0,      ISA_MIPS64,     CPU_MIPS64 },
 
-  /* MIPS3 ISA */
-  { "MIPS3",          1,      ISA_MIPS3,      CPU_R4000, },
+  /* Broadcom SB-1 CPU core */
+  { "sb1",            0,      ISA_MIPS64,     CPU_SB1 },
 
-  /* MIPS4 ISA */
-  { "MIPS4",          1,      ISA_MIPS4,      CPU_R8000, },
+  /* End marker */
+  { NULL, 0, 0, 0 }
+};
 
-  /* MIPS5 ISA */
-  { "MIPS5",          1,      ISA_MIPS5,      CPU_MIPS5, },
-  { "Generic-MIPS5",  0,      ISA_MIPS5,      CPU_MIPS5, },
 
-  /* MIPS32 ISA */
-  { "MIPS32",         1,      ISA_MIPS32,     CPU_MIPS32, },
-  { "mipsisa32",      0,      ISA_MIPS32,     CPU_MIPS32, },
-  { "Generic-MIPS32", 0,      ISA_MIPS32,     CPU_MIPS32, },
-  { "4kc",            0,      ISA_MIPS32,     CPU_MIPS32, },
-  { "4km",            0,      ISA_MIPS32,     CPU_MIPS32, },
-  { "4kp",            0,      ISA_MIPS32,     CPU_MIPS32, },
-
-  /* For historical reasons.  */
-  { "MIPS64",         1,      ISA_MIPS3,      CPU_R4000, },
-
-  /* MIPS64 ISA */
-  { "mipsisa64",      1,      ISA_MIPS64,     CPU_MIPS64, },
-  { "Generic-MIPS64", 0,      ISA_MIPS64,     CPU_MIPS64, },
-  { "5kc",            0,      ISA_MIPS64,     CPU_MIPS64, },
-  { "20kc",           0,      ISA_MIPS64,     CPU_MIPS64, },
-
-  /* R2000 CPU */
-  { "R2000",          0,      ISA_MIPS1,      CPU_R2000, },
-  { "2000",           0,      ISA_MIPS1,      CPU_R2000, },
-  { "2k",             0,      ISA_MIPS1,      CPU_R2000, },
-  { "r2k",            0,      ISA_MIPS1,      CPU_R2000, },
-
-  /* R3000 CPU */
-  { "R3000",          0,      ISA_MIPS1,      CPU_R3000, },
-  { "3000",           0,      ISA_MIPS1,      CPU_R3000, },
-  { "3k",             0,      ISA_MIPS1,      CPU_R3000, },
-  { "r3k",            0,      ISA_MIPS1,      CPU_R3000, },
-
-  /* TX3900 CPU */
-  { "R3900",          0,      ISA_MIPS1,      CPU_R3900, },
-  { "3900",           0,      ISA_MIPS1,      CPU_R3900, },
-  { "mipstx39",       0,      ISA_MIPS1,      CPU_R3900, },
-
-  /* R4000 CPU */
-  { "R4000",          0,      ISA_MIPS3,      CPU_R4000, },
-  { "4000",           0,      ISA_MIPS3,      CPU_R4000, },
-  { "4k",             0,      ISA_MIPS3,      CPU_R4000, },   /* beware */
-  { "r4k",            0,      ISA_MIPS3,      CPU_R4000, },
-
-  /* R4010 CPU */
-  { "R4010",          0,      ISA_MIPS2,      CPU_R4010, },
-  { "4010",           0,      ISA_MIPS2,      CPU_R4010, },
-
-  /* R4400 CPU */
-  { "R4400",          0,      ISA_MIPS3,      CPU_R4400, },
-  { "4400",           0,      ISA_MIPS3,      CPU_R4400, },
-
-  /* R4600 CPU */
-  { "R4600",          0,      ISA_MIPS3,      CPU_R4600, },
-  { "4600",           0,      ISA_MIPS3,      CPU_R4600, },
-  { "mips64orion",    0,      ISA_MIPS3,      CPU_R4600, },
-  { "orion",          0,      ISA_MIPS3,      CPU_R4600, },
-
-  /* R4650 CPU */
-  { "R4650",          0,      ISA_MIPS3,      CPU_R4650, },
-  { "4650",           0,      ISA_MIPS3,      CPU_R4650, },
-
-  /* R6000 CPU */
-  { "R6000",          0,      ISA_MIPS2,      CPU_R6000, },
-  { "6000",           0,      ISA_MIPS2,      CPU_R6000, },
-  { "6k",             0,      ISA_MIPS2,      CPU_R6000, },
-  { "r6k",            0,      ISA_MIPS2,      CPU_R6000, },
-
-  /* R8000 CPU */
-  { "R8000",          0,      ISA_MIPS4,      CPU_R8000, },
-  { "8000",           0,      ISA_MIPS4,      CPU_R8000, },
-  { "8k",             0,      ISA_MIPS4,      CPU_R8000, },
-  { "r8k",            0,      ISA_MIPS4,      CPU_R8000, },
-
-  /* R10000 CPU */
-  { "R10000",         0,      ISA_MIPS4,      CPU_R10000, },
-  { "10000",          0,      ISA_MIPS4,      CPU_R10000, },
-  { "10k",            0,      ISA_MIPS4,      CPU_R10000, },
-  { "r10k",           0,      ISA_MIPS4,      CPU_R10000, },
-
-  /* R12000 CPU */
-  { "R12000",         0,      ISA_MIPS4,      CPU_R12000, },
-  { "12000",          0,      ISA_MIPS4,      CPU_R12000, },
-  { "12k",            0,      ISA_MIPS4,      CPU_R12000, },
-  { "r12k",           0,      ISA_MIPS4,      CPU_R12000, },
-
-  /* VR4100 CPU */
-  { "VR4100",         0,      ISA_MIPS3,      CPU_VR4100, },
-  { "4100",           0,      ISA_MIPS3,      CPU_VR4100, },
-  { "mips64vr4100",   0,      ISA_MIPS3,      CPU_VR4100, },
-  { "r4100",          0,      ISA_MIPS3,      CPU_VR4100, },
-
-  /* VR4111 CPU */
-  { "VR4111",         0,      ISA_MIPS3,      CPU_R4111, },
-  { "4111",           0,      ISA_MIPS3,      CPU_R4111, },
-  { "mips64vr4111",   0,      ISA_MIPS3,      CPU_R4111, },
-  { "r4111",          0,      ISA_MIPS3,      CPU_R4111, },
-
-  /* VR4300 CPU */
-  { "VR4300",         0,      ISA_MIPS3,      CPU_R4300, },
-  { "4300",           0,      ISA_MIPS3,      CPU_R4300, },
-  { "mips64vr4300",   0,      ISA_MIPS3,      CPU_R4300, },
-  { "r4300",          0,      ISA_MIPS3,      CPU_R4300, },
-
-  /* VR5000 CPU */
-  { "VR5000",         0,      ISA_MIPS4,      CPU_R5000, },
-  { "5000",           0,      ISA_MIPS4,      CPU_R5000, },
-  { "5k",             0,      ISA_MIPS4,      CPU_R5000, },
-  { "mips64vr5000",   0,      ISA_MIPS4,      CPU_R5000, },
-  { "r5000",          0,      ISA_MIPS4,      CPU_R5000, },
-  { "r5200",          0,      ISA_MIPS4,      CPU_R5000, },
-  { "rm5200",         0,      ISA_MIPS4,      CPU_R5000, },
-  { "r5230",          0,      ISA_MIPS4,      CPU_R5000, },
-  { "rm5230",         0,      ISA_MIPS4,      CPU_R5000, },
-  { "r5231",          0,      ISA_MIPS4,      CPU_R5000, },
-  { "rm5231",         0,      ISA_MIPS4,      CPU_R5000, },
-  { "r5261",          0,      ISA_MIPS4,      CPU_R5000, },
-  { "rm5261",         0,      ISA_MIPS4,      CPU_R5000, },
-  { "r5721",          0,      ISA_MIPS4,      CPU_R5000, },
-  { "rm5721",         0,      ISA_MIPS4,      CPU_R5000, },
-  { "r5k",            0,      ISA_MIPS4,      CPU_R5000, },
-  { "r7000",          0,      ISA_MIPS4,      CPU_R5000, },
-
-  /* Broadcom SB-1 CPU */
-  { "SB-1",           0,      ISA_MIPS64,     CPU_SB1, },
-  { "sb-1250",        0,      ISA_MIPS64,     CPU_SB1, },
-  { "sb1",            0,      ISA_MIPS64,     CPU_SB1, },
-  { "sb1250",         0,      ISA_MIPS64,     CPU_SB1, },
-
-  /* End marker.  */
-  { NULL, 0, 0, 0, },
-};
+/* Return true if GIVEN is the same as CANONICAL, or if it is CANONICAL
+   with a final "000" replaced by "k".  Ignore case.
+
+   Note: this function is shared between GCC and GAS.  */
+
+static bfd_boolean
+mips_strict_matching_cpu_name_p (canonical, given)
+     const char *canonical, *given;
+{
+  while (*given != 0 && TOLOWER (*given) == TOLOWER (*canonical))
+    given++, canonical++;
+
+  return ((*given == 0 && *canonical == 0)
+         || (strcmp (canonical, "000") == 0 && strcasecmp (given, "k") == 0));
+}
+
+
+/* Return true if GIVEN matches CANONICAL, where GIVEN is a user-supplied
+   CPU name.  We've traditionally allowed a lot of variation here.
+
+   Note: this function is shared between GCC and GAS.  */
+
+static bfd_boolean
+mips_matching_cpu_name_p (canonical, given)
+     const char *canonical, *given;
+{
+  /* First see if the name matches exactly, or with a final "000"
+     turned into "k".  */
+  if (mips_strict_matching_cpu_name_p (canonical, given))
+    return TRUE;
+
+  /* If not, try comparing based on numerical designation alone.
+     See if GIVEN is an unadorned number, or 'r' followed by a number.  */
+  if (TOLOWER (*given) == 'r')
+    given++;
+  if (!ISDIGIT (*given))
+    return FALSE;
+
+  /* Skip over some well-known prefixes in the canonical name,
+     hoping to find a number there too.  */
+  if (TOLOWER (canonical[0]) == 'v' && TOLOWER (canonical[1]) == 'r')
+    canonical += 2;
+  else if (TOLOWER (canonical[0]) == 'r' && TOLOWER (canonical[1]) == 'm')
+    canonical += 2;
+  else if (TOLOWER (canonical[0]) == 'r')
+    canonical += 1;
+
+  return mips_strict_matching_cpu_name_p (canonical, given);
+}
+
+
+/* Parse an option that takes the name of a processor as its argument.
+   OPTION is the name of the option and CPU_STRING is the argument.
+   Return the corresponding processor enumeration if the CPU_STRING is
+   recognized, otherwise report an error and return null.
+
+   A similar function exists in GCC.  */
 
 static const struct mips_cpu_info *
-mips_cpu_info_from_name (name)
-     const char *name;
+mips_parse_cpu (option, cpu_string)
+     const char *option, *cpu_string;
 {
-  int i;
+  const struct mips_cpu_info *p;
 
-  for (i = 0; mips_cpu_info_table[i].name != NULL; i++)
-    if (strcasecmp (name, mips_cpu_info_table[i].name) == 0)
-      return (&mips_cpu_info_table[i]);
+  /* 'from-abi' selects the most compatible architecture for the given
+     ABI: MIPS I for 32-bit ABIs and MIPS III for 64-bit ABIs.  For the
+     EABIs, we have to decide whether we're using the 32-bit or 64-bit
+     version.  Look first at the -mgp options, if given, otherwise base
+     the choice on MIPS_DEFAULT_64BIT.
 
-  return NULL;
+     Treat NO_ABI like the EABIs.  One reason to do this is that the
+     plain 'mips' and 'mips64' configs have 'from-abi' as their default
+     architecture.  This code picks MIPS I for 'mips' and MIPS III for
+     'mips64', just as we did in the days before 'from-abi'.  */
+  if (strcasecmp (cpu_string, "from-abi") == 0)
+    {
+      if (ABI_NEEDS_32BIT_REGS (mips_abi))
+       return mips_cpu_info_from_isa (ISA_MIPS1);
+
+      if (ABI_NEEDS_64BIT_REGS (mips_abi))
+       return mips_cpu_info_from_isa (ISA_MIPS3);
+
+      if (file_mips_gp32 >= 0)
+       return mips_cpu_info_from_isa (file_mips_gp32 ? ISA_MIPS1 : ISA_MIPS3);
+
+      return mips_cpu_info_from_isa (MIPS_DEFAULT_64BIT
+                                    ? ISA_MIPS3
+                                    : ISA_MIPS1);
+    }
+
+  /* 'default' has traditionally been a no-op.  Probably not very useful.  */
+  if (strcasecmp (cpu_string, "default") == 0)
+    return 0;
+
+  for (p = mips_cpu_info_table; p->name != 0; p++)
+    if (mips_matching_cpu_name_p (p->name, cpu_string))
+      return p;
+
+  as_bad ("Bad value (%s) for %s", cpu_string, option);
+  return 0;
 }
 
+/* Return the canonical processor information for ISA (a member of the
+   ISA_MIPS* enumeration).  */
+
 static const struct mips_cpu_info *
 mips_cpu_info_from_isa (isa)
      int isa;
@@ -13492,22 +14867,135 @@ mips_cpu_info_from_isa (isa)
 
   for (i = 0; mips_cpu_info_table[i].name != NULL; i++)
     if (mips_cpu_info_table[i].is_isa
-      && isa == mips_cpu_info_table[i].isa)
+       && isa == mips_cpu_info_table[i].isa)
       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;
+{
+  if (*first_p)
+    {
+      fprintf (stream, "%24s", "");
+      *col_p = 24;
+    }
+  else
+    {
+      fprintf (stream, ", ");
+      *col_p += 2;
+    }
 
-static const struct mips_cpu_info *
-mips_cpu_info_from_cpu (cpu)
-     int cpu;
+  if (*col_p + strlen (string) > 72)
+    {
+      fprintf (stream, "\n%24s", "");
+      *col_p = 24;
+    }
+
+  fprintf (stream, "%s", string);
+  *col_p += strlen (string);
+
+  *first_p = 0;
+}
+
+void
+md_show_usage (stream)
+     FILE *stream;
 {
-  int i;
+  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\
+-G NUM                 allow referencing objects up to NUM bytes\n\
+                       implicitly with the gp register [default 8]\n"));
+  fprintf (stream, _("\
+-mips1                 generate MIPS ISA I instructions\n\
+-mips2                 generate MIPS ISA II instructions\n\
+-mips3                 generate MIPS ISA III instructions\n\
+-mips4                 generate MIPS ISA IV instructions\n\
+-mips5                  generate MIPS ISA V instructions\n\
+-mips32                 generate MIPS32 ISA instructions\n\
+-mips32r2               generate MIPS32 release 2 ISA instructions\n\
+-mips64                 generate MIPS64 ISA instructions\n\
+-march=CPU/-mtune=CPU  generate code/schedule for CPU, where CPU is one of:\n"));
+
+  first = 1;
 
   for (i = 0; mips_cpu_info_table[i].name != NULL; i++)
-    if (!mips_cpu_info_table[i].is_isa
-      && cpu == mips_cpu_info_table[i].cpu)
-      return (&mips_cpu_info_table[i]);
+    show (stream, mips_cpu_info_table[i].name, &column, &first);
+  show (stream, "from-abi", &column, &first);
+  fputc ('\n', stream);
 
-  return NULL;
+  fprintf (stream, _("\
+-mCPU                  equivalent to -march=CPU -mtune=CPU. Deprecated.\n\
+-no-mCPU               don't generate code specific to CPU.\n\
+                       For -mCPU and -no-mCPU, CPU must be one of:\n"));
+
+  first = 1;
+
+  show (stream, "3900", &column, &first);
+  show (stream, "4010", &column, &first);
+  show (stream, "4100", &column, &first);
+  show (stream, "4650", &column, &first);
+  fputc ('\n', stream);
+
+  fprintf (stream, _("\
+-mips16                        generate mips16 instructions\n\
+-no-mips16             do not generate mips16 instructions\n"));
+  fprintf (stream, _("\
+-mgp32                 use 32-bit GPRs, regardless of the chosen ISA\n\
+-mfp32                 use 32-bit FPRs, regardless of the chosen ISA\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"));
+#ifdef OBJ_ELF
+  fprintf (stream, _("\
+-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\
+-mabi=ABI              create ABI conformant object file for:\n"));
+
+  first = 1;
+
+  show (stream, "32", &column, &first);
+  show (stream, "o64", &column, &first);
+  show (stream, "n32", &column, &first);
+  show (stream, "64", &column, &first);
+  show (stream, "eabi", &column, &first);
+
+  fputc ('\n', stream);
+
+  fprintf (stream, _("\
+-32                    create o32 ABI object file (default)\n\
+-n32                   create n32 ABI object file\n\
+-64                    create 64 ABI object file\n"));
+#endif
+}
+
+enum dwarf2_format
+mips_dwarf2_format ()
+{
+  if (mips_abi == N64_ABI)
+    {
+#ifdef TE_IRIX
+      return dwarf2_format_64bit_irix;
+#else
+      return dwarf2_format_64bit;
+#endif
+    }
+  else
+    return dwarf2_format_32bit;
 }
This page took 0.196666 seconds and 4 git commands to generate.