Update all uses of md_apply_fix to use md_apply_fix3. Make it a void function.
[deliverable/binutils-gdb.git] / gas / config / tc-mips.c
index 19490a86b408cada3cd34f54fb50a1d49a921e1e..5c3da07c8b72d7be45932eee7c3c5b1481d3a9cd 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-mips.c -- assemble code for a MIPS chip.
-   Copyright (C) 1993, 94, 95, 96, 97, 98, 1999, 2000
+   Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
    Free Software Foundation, Inc.
    Contributed by the OSF and Ralph Campbell.
    Written by Keith Knowles and Ralph Campbell, working independently.
@@ -26,8 +26,7 @@
 #include "as.h"
 #include "config.h"
 #include "subsegs.h"
-
-#include <ctype.h>
+#include "safe-ctype.h"
 
 #ifdef USE_STDARG
 #include <stdarg.h>
@@ -103,40 +102,6 @@ static char *mips_regmask_frag;
 
 extern int target_big_endian;
 
-/* 1 is we should use the 64 bit MIPS ELF ABI, 0 if we should use the
-   32 bit ABI.  This has no meaning for ECOFF.
-   Note that the default is always 32 bit, even if "configured" for
-   64 bit [e.g. --target=mips64-elf].  */
-static int mips_64;
-
-/* The default target format to use.  */
-const char *
-mips_target_format ()
-{
-  switch (OUTPUT_FLAVOR)
-    {
-    case bfd_target_aout_flavour:
-      return target_big_endian ? "a.out-mips-big" : "a.out-mips-little";
-    case bfd_target_ecoff_flavour:
-      return target_big_endian ? "ecoff-bigmips" : ECOFF_LITTLE_FORMAT;
-    case bfd_target_coff_flavour:
-      return "pe-mips";
-    case bfd_target_elf_flavour:
-#ifdef TE_TMIPS
-      /* This is traditional mips */
-      return (target_big_endian
-              ? "elf32-tradbigmips" : "elf32-tradlittlemips");
-#else
-      return (target_big_endian
-             ? (mips_64 ? "elf64-bigmips" : "elf32-bigmips")
-             : (mips_64 ? "elf64-littlemips" : "elf32-littlemips"));
-#endif
-    default:
-      abort ();
-      return NULL;
-    }
-}
-
 /* The name of the readonly data section.  */
 #define RDATA_SECTION_NAME (OUTPUT_FLAVOR == bfd_target_aout_flavour \
                            ? ".data" \
@@ -148,6 +113,20 @@ mips_target_format ()
                            ? ".rodata" \
                            : (abort (), ""))
 
+/* The ABI to use.  */
+enum mips_abi_level
+{
+  NO_ABI = 0,
+  O32_ABI,
+  O64_ABI,
+  N32_ABI,
+  N64_ABI,
+  EABI_ABI
+};
+
+/* MIPS ABI we are using for this output file.  */
+static enum mips_abi_level file_mips_abi = NO_ABI;
+
 /* This is the set of options which may be modified by the .set
    pseudo-op.  We use a struct so that .set push and .set pop are more
    reliable.  */
@@ -184,13 +163,30 @@ struct mips_set_options
   /* Non-zero if we should not autoextend mips16 instructions.
      Changed by `.set autoextend' and `.set noautoextend'.  */
   int noautoextend;
+  /* Restrict general purpose registers and floating point registers
+     to 32 bit.  This is initially determined when -mgp32 or -mfp32
+     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.  */
+static int file_mips_gp32 = 0;
+
+/* True if -mfp32 was passed.  */
+static int file_mips_fp32 = 0;
+
 /* This is the struct we use to hold the current set of options.  Note
-   that we must set the isa and mips16 fields to -1 to indicate that
-   they have not been initialized.  */
+   that we must set the isa field to ISA_UNKNOWN and the mips16 field to
+   -1 to indicate that they have not been initialized.  */
 
-static struct mips_set_options mips_opts = { -1, -1, 0, 0, 0, 0, 0, 0 };
+static struct mips_set_options mips_opts =
+{
+  ISA_UNKNOWN, -1, 0, 0, 0, 0, 0, 0, 0, 0, NO_ABI
+};
 
 /* These variables are filled in with the masks of registers used.
    The object format code reads them and puts them in the appropriate
@@ -199,24 +195,25 @@ unsigned long mips_gprmask;
 unsigned long mips_cprmask[4];
 
 /* MIPS ISA we are using for this output file.  */
-static int file_mips_isa;
+static int file_mips_isa = ISA_UNKNOWN;
 
-/* The CPU type as a number: 2000, 3000, 4000, 4400, etc.  */
-static int mips_cpu = -1;
+/* The argument of the -mcpu= flag.  Historical for code generation.  */
+static int mips_cpu = CPU_UNKNOWN;
 
-/* The argument of the -mabi= flag.  */
-static char* mips_abi_string = 0;
+/* The argument of the -march= flag.  The architecture we are assembling.  */
+static int mips_arch = CPU_UNKNOWN;
 
-/* Wether we should mark the file EABI64 or EABI32.  */
+/* The argument of the -mtune= flag.  The architecture for which we
+   are optimizing.  */
+static int mips_tune = CPU_UNKNOWN;
+
+/* 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.  */
 static int mips_32bitmode = 0;
 
-/* True if -mgp32 was passed.  */
-static int mips_gp32 = 0;
-
 /* Some ISA's have delay slots for instructions which read or write
    from a coprocessor (eg. mips1-mips3); some don't (eg mips4).
    Return true if instructions marked INSN_LOAD_COPROC_DELAY,
@@ -226,57 +223,65 @@ static int mips_gp32 = 0;
    also assume that ISAs which don't have delays for these insns, don't
    have delays for the INSN_LOAD_MEMORY_DELAY instructions either.  */
 #define ISA_HAS_COPROC_DELAYS(ISA) (        \
-   (ISA) == 1                               \
-   || (ISA) == 2                            \
-   || (ISA) == 3                            \
+   (ISA) == ISA_MIPS1                       \
+   || (ISA) == ISA_MIPS2                    \
+   || (ISA) == ISA_MIPS3                    \
    )
 
 /*  Return true if ISA supports 64 bit gp register instructions.  */
 #define ISA_HAS_64BIT_REGS(ISA) (    \
-   (ISA) == 3                        \
-   || (ISA) == 4                     \
+   (ISA) == ISA_MIPS3                \
+   || (ISA) == ISA_MIPS4             \
+   || (ISA) == ISA_MIPS5             \
+   || (ISA) == ISA_MIPS64            \
    )
 
-/* 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 HAVE_32BIT_GPRS                                   \
+    (mips_opts.gp32                                \
+     || mips_opts.abi == O32_ABI                   \
+     || ! 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))
 
-   FIXME: GCC makes a distinction between -mcpu=FOO and -mFOO:
-   -mcpu=FOO schedules for FOO, but still produces code that meets the
-   requirements of MIPS ISA I.  For example, it won't generate any
-   FOO-specific instructions, and it will still assume that any
-   scheduling hazards described in MIPS ISA I are there, even if FOO
-   has interlocks.  -mFOO gives GCC permission to generate code that
-   will only run on a FOO; it will generate FOO-specific instructions,
-   and assume interlocks provided by a FOO.
+#define HAVE_64BIT_GPRS (! HAVE_32BIT_GPRS)
+#define HAVE_64BIT_FPRS (! HAVE_32BIT_FPRS)
 
-   However, GAS currently doesn't make this distinction; before Jan 28
-   1999, GAS's -mcpu=FOO implied -mFOO, which violates GCC's
-   assumptions.  The GCC driver passes these flags through to GAS, so
-   if GAS actually does anything that doesn't meet MIPS ISA I with
-   -mFOO, then GCC's -mcpu=FOO flag isn't going to work.
+#define HAVE_NEWABI (mips_opts.abi == N32_ABI || mips_opts.abi == N64_ABI)
 
-   And furthermore, it did not assume that -mFOO implied -mcpu=FOO,
-   which seems senseless --- why generate code which will only run on
-   a FOO, but schedule for something else?
+#define HAVE_64BIT_OBJECTS (mips_opts.abi == N64_ABI)
 
-   So now, at least, -mcpu=FOO and -mFOO are exactly equivalent.
+/* We can only have 64bit addresses if the object file format
+   supports it.  */
+#define HAVE_32BIT_ADDRESSES                           \
+   (HAVE_32BIT_GPRS                                    \
+    || ((bfd_arch_bits_per_address (stdoutput) == 32   \
+         || ! HAVE_64BIT_OBJECTS)                      \
+        && mips_pic != EMBEDDED_PIC))
 
-   -- Jim Blandy <jimb@cygnus.com> */
+#define HAVE_64BIT_ADDRESSES (! HAVE_32BIT_ADDRESSES)
+
+/* 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_cpu == CPU_R4010                       \
+#define hilo_interlocks (mips_arch == CPU_R4010                       \
+                         || mips_arch == CPU_SB1                      \
                          )
 
 /* Whether the processor uses hardware interlocks to protect reads
    from the GPRs, and thus does not require nops to be inserted.  */
 #define gpr_interlocks \
-  (mips_opts.isa != 1  \
-   || mips_cpu == CPU_R3900)
+  (mips_opts.isa != ISA_MIPS1  \
+   || 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_cpu == CPU_R4300                        \
+#define cop_interlocks (mips_arch == CPU_R4300                        \
+                        || mips_arch == CPU_SB1                       \
                        )
 
 /* Is this a mfhi or mflo instruction?  */
@@ -306,6 +311,9 @@ enum mips_pic_level
 
 static enum mips_pic_level mips_pic;
 
+/* Warn about all NOPS that the assembler generates.  */
+static int warn_nops = 0;
+
 /* 1 if we should generate 32 bit offsets from the GP register in
    SVR4_PIC mode.  Currently has no meaning in other modes.  */
 static int mips_big_got;
@@ -315,7 +323,7 @@ static int mips_big_got;
 static int mips_trap;
 
 /* 1 if double width floating point constants should not be constructed
-   by a assembling two single width halves into two single width floating
+   by assembling two single width halves into two single width floating
    point registers which just happen to alias the double width destination
    register.  On some architectures this aliasing can be disabled by a bit
    in the status register, and the setting of this bit cannot be determined
@@ -397,6 +405,13 @@ static int auto_align = 1;
    variable.  */
 static offsetT mips_cprestore_offset = -1;
 
+/* Similiar for NewABI PIC code, where $gp is callee-saved.  NewABI has some
+   more optimizations, it can use a register value instead of a memory-saved
+   offset and even an other than GP as global pointer.  */
+static offsetT mips_cpreturn_offset = -1;
+static int mips_cpreturn_register = -1;
+static int mips_gp_register = GP;
+
 /* This is the register which holds the stack frame, as set by the
    .frame pseudo-op.  This is needed to implement .cprestore.  */
 static int mips_frame_reg = SP;
@@ -434,10 +449,10 @@ static struct frag *prev_insn_frag;
 static long prev_insn_where;
 
 /* The reloc type for the previous instruction, if any.  */
-static bfd_reloc_code_real_type prev_insn_reloc_type;
+static bfd_reloc_code_real_type prev_insn_reloc_type[3];
 
 /* The reloc for the previous instruction, if any.  */
-static fixS *prev_insn_fixp;
+static fixS *prev_insn_fixp[3];
 
 /* Non-zero if the previous instruction was in a delay slot.  */
 static int prev_insn_is_delay_slot;
@@ -584,8 +599,8 @@ static const unsigned int mips16_to_32_reg_map[] =
     | ((warn) ? 1 : 0)))
 #define RELAX_OLD(i) (((i) >> 23) & 0x7f)
 #define RELAX_NEW(i) (((i) >> 16) & 0x7f)
-#define RELAX_RELOC1(i) ((bfd_vma)(((i) >> 9) & 0x7f) - 64)
-#define RELAX_RELOC2(i) ((bfd_vma)(((i) >> 2) & 0x7f) - 64)
+#define RELAX_RELOC1(i) ((valueT) (((i) >> 9) & 0x7f) - 64)
+#define RELAX_RELOC2(i) ((valueT) (((i) >> 2) & 0x7f) - 64)
 #define RELAX_RELOC3(i) (((i) >> 1) & 1)
 #define RELAX_WARN(i) ((i) & 1)
 
@@ -647,7 +662,7 @@ 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,
+                                bfd_reloc_code_real_type *r,
                                 boolean));
 static void mips_no_prev_insn PARAMS ((int));
 static void mips_emit_delays PARAMS ((boolean));
@@ -667,7 +682,8 @@ 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 *counter, int reg, expressionS *ep));
+static void load_address PARAMS ((int *, int, expressionS *, int, 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
@@ -678,8 +694,12 @@ 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_getSmallExpression PARAMS ((expressionS * ep, char *str));
-static void my_getExpression PARAMS ((expressionS * ep, char *str));
+static int my_getSmallParser PARAMS ((char **, unsigned int *, int *));
+static int my_getSmallExpression PARAMS ((expressionS *, char *));
+static void my_getExpression PARAMS ((expressionS *, char *));
+#ifdef OBJ_ELF
+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));
@@ -691,7 +711,11 @@ 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));
@@ -706,9 +730,51 @@ 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 char *mips_cpu_to_str PARAMS ((int));
-
+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 *));
+
+/* 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.  */
+
+struct mips_cpu_info
+{
+  const char *name;           /* CPU or ISA name.  */
+  int is_isa;                 /* Is this an ISA?  (If 0, a CPU.) */
+  int isa;                    /* ISA level.  */
+  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));
 \f
 /* Pseudo-op table.
 
@@ -730,7 +796,7 @@ static int validate_mips_insn PARAMS ((const struct mips_opcode *));
 
 static const pseudo_typeS mips_pseudo_table[] =
 {
- /* MIPS specific pseudo-ops.  */
 /* MIPS specific pseudo-ops.  */
   {"option", s_option, 0},
   {"set", s_mipsset, 0},
   {"rdata", s_change_sec, 'r'},
@@ -738,12 +804,16 @@ static const pseudo_typeS mips_pseudo_table[] =
   {"livereg", s_ignore, 0},
   {"abicalls", s_abicalls, 0},
   {"cpload", s_cpload, 0},
+  {"cpsetup", s_cpsetup, 0},
+  {"cplocal", s_cplocal, 0},
   {"cprestore", s_cprestore, 0},
+  {"cpreturn", s_cpreturn, 0},
+  {"gpvalue", s_gpvalue, 0},
   {"gpword", s_gpword, 0},
   {"cpadd", s_cpadd, 0},
   {"insn", s_insn, 0},
 
- /* Relatively generic pseudo-ops that happen to be used on MIPS
 /* Relatively generic pseudo-ops that happen to be used on MIPS
      chips.  */
   {"asciiz", stringer, 1},
   {"bss", s_change_sec, 'b'},
@@ -752,7 +822,7 @@ static const pseudo_typeS mips_pseudo_table[] =
   {"dword", s_cons, 3},
   {"weakext", s_mips_weakext, 0},
 
- /* These pseudo-ops are defined in read.c, but must be overridden
 /* These pseudo-ops are defined in read.c, but must be overridden
      here for one reason or another.  */
   {"align", s_align, 0},
   {"byte", s_cons, 0},
@@ -771,12 +841,18 @@ static const pseudo_typeS mips_pseudo_table[] =
   {"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 },
 };
 
-static const pseudo_typeS mips_nonecoff_pseudo_table[] = {
- /* These pseudo-ops should be defined by the object file format.
-    However, a.out doesn't support them, so we have versions here.  */
+static const pseudo_typeS mips_nonecoff_pseudo_table[] =
+{
+  /* These pseudo-ops should be defined by the object file format.
+     However, a.out doesn't support them, so we have versions here.  */
   {"aent", s_mips_ent, 1},
   {"bgnb", s_ignore, 0},
   {"end", s_mips_end, 0},
@@ -835,8 +911,10 @@ static expressionS offset_expr;
 
 /* Relocs associated with imm_expr and offset_expr.  */
 
-static bfd_reloc_code_real_type imm_reloc;
-static bfd_reloc_code_real_type offset_reloc;
+static bfd_reloc_code_real_type imm_reloc[3]
+  = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+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.  */
 
@@ -852,32 +930,66 @@ static boolean mips16_small, mips16_ext;
 static segT pdr_seg;
 #endif
 
-static char *
+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];
-  switch (cpu)
-    {
-    case CPU_R2000: return "R2000";
-    case CPU_R3000: return "R3000";
-    case CPU_R3900: return "R3900";
-    case CPU_R4000: return "R4000";
-    case CPU_R4010: return "R4010";
-    case CPU_VR4100: return "VR4100";
-    case CPU_R4111: return "R4111";
-    case CPU_R4300: return "R4300";
-    case CPU_R4400: return "R4400";
-    case CPU_R4600: return "R4600";
-    case CPU_R4650: return "R4650";
-    case CPU_R5000: return "R5000";
-    case CPU_R6000: return "R6000";
-    case CPU_R8000: return "R8000";
-    case CPU_R10000: return "R10000";
-    case CPU_4K: return "4K";
+
+  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 *
+mips_target_format ()
+{
+  switch (OUTPUT_FLAVOR)
+    {
+    case bfd_target_aout_flavour:
+      return target_big_endian ? "a.out-mips-big" : "a.out-mips-little";
+    case bfd_target_ecoff_flavour:
+      return target_big_endian ? "ecoff-bigmips" : ECOFF_LITTLE_FORMAT;
+    case bfd_target_coff_flavour:
+      return "pe-mips";
+    case bfd_target_elf_flavour:
+#ifdef TE_TMIPS
+      /* This is traditional mips */
+      return (target_big_endian
+             ? (HAVE_64BIT_OBJECTS ? "elf64-tradbigmips"
+                : "elf32-tradbigmips")
+             : (HAVE_64BIT_OBJECTS ? "elf64-tradlittlemips"
+                : "elf32-tradlittlemips"));
+#else
+      return (target_big_endian
+             ? (HAVE_64BIT_OBJECTS ? "elf64-bigmips" : "elf32-bigmips")
+             : (HAVE_64BIT_OBJECTS ? "elf64-littlemips"
+                : "elf32-littlemips"));
+#endif
     default:
-      sprintf (s, "%d", cpu);
-      return s;
+      abort ();
+      return NULL;
     }
 }
 
@@ -887,13 +999,14 @@ mips_cpu_to_str (cpu)
 void
 md_begin ()
 {
-  boolean ok = false;
   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
@@ -913,129 +1026,97 @@ md_begin ()
       cpu = a;
     }
 
-  if (mips_cpu < 0)
+  if (strncmp (cpu, "mips16", sizeof "mips16" - 1) == 0)
     {
-      /* Set mips_cpu based on TARGET_CPU, unless TARGET_CPU is
-         just the generic 'mips', in which case set mips_cpu based
-         on the given ISA, if any.  */
-
-      if (strcmp (cpu, "mips") == 0)
-        {
-         if (mips_opts.isa < 0)
-           mips_cpu = CPU_R3000;
-
-         else if (mips_opts.isa == 2)
-            mips_cpu = CPU_R6000;
-
-          else if (mips_opts.isa == 3)
-            mips_cpu = CPU_R4000;
-
-          else if (mips_opts.isa == 4)
-            mips_cpu = CPU_R8000;
-
-          else
-            mips_cpu = CPU_R3000;
-        }
-
-      else if (strcmp (cpu, "r3900") == 0
-               || strcmp (cpu, "mipstx39") == 0
-               )
-        mips_cpu = CPU_R3900;
-
-      else if (strcmp (cpu, "r6000") == 0
-              || strcmp (cpu, "mips2") == 0)
-        mips_cpu = CPU_R6000;
-
-      else if (strcmp (cpu, "mips64") == 0
-              || strcmp (cpu, "r4000") == 0
-              || strcmp (cpu, "mips3") == 0)
-        mips_cpu = CPU_R4000;
-
-      else if (strcmp (cpu, "r4400") == 0)
-        mips_cpu = CPU_R4400;
-
-      else if (strcmp (cpu, "mips64orion") == 0
-              || strcmp (cpu, "r4600") == 0)
-        mips_cpu = CPU_R4600;
-
-      else if (strcmp (cpu, "r4650") == 0)
-        mips_cpu = CPU_R4650;
-
-      else if (strcmp (cpu, "mips64vr4300") == 0)
-        mips_cpu = CPU_R4300;
-
-      else if (strcmp (cpu, "mips64vr4111") == 0)
-        mips_cpu = CPU_R4111;
-
-      else if (strcmp (cpu, "mips64vr4100") == 0)
-        mips_cpu = CPU_VR4100;
-
-      else if (strcmp (cpu, "r4010") == 0)
-        mips_cpu = CPU_R4010;
-
-      else if (strcmp (cpu, "4Kc") == 0
-              || strcmp (cpu, "4Kp") == 0
-              || strcmp (cpu, "4Km") == 0)
-       mips_cpu = CPU_4K;
-
-      else if (strcmp (cpu, "r5000") == 0
-              || strcmp (cpu, "mips64vr5000") == 0)
-        mips_cpu = CPU_R5000;
-
-      else if (strcmp (cpu, "r8000") == 0
-              || strcmp (cpu, "mips4") == 0)
-        mips_cpu = CPU_R8000;
-
-      else if (strcmp (cpu, "r10000") == 0)
-        mips_cpu = CPU_R10000;
-
-      else if (strcmp (cpu, "mips16") == 0)
-        mips_cpu = 0; /* FIXME */
-
-      else
-        mips_cpu = CPU_R3000;
+      target_cpu_had_mips16 = 1;
+      cpu += sizeof "mips16" - 1;
     }
 
-  if (mips_cpu == CPU_R3000
-      || mips_cpu == CPU_R3900)
-    mips_isa_from_cpu = 1;
-
-  else if (mips_cpu == CPU_R6000
-          || mips_cpu == CPU_R4010)
-    mips_isa_from_cpu = 2;
-
-  else if (mips_cpu == CPU_R4000
-          || mips_cpu == CPU_VR4100
-          || mips_cpu == CPU_R4111
-          || mips_cpu == CPU_R4400
-          || mips_cpu == CPU_R4300
-          || mips_cpu == CPU_R4600
-          || mips_cpu == CPU_R4650)
-    mips_isa_from_cpu = 3;
+  if (mips_opts.mips16 < 0)
+    mips_opts.mips16 = target_cpu_had_mips16;
 
-  else if (mips_cpu == CPU_R5000
-          || mips_cpu == CPU_R8000
-               || mips_cpu == CPU_R10000)
-    mips_isa_from_cpu = 4;
+  /* 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."));
+    }
 
-  else
-    mips_isa_from_cpu = -1;
+  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 (mips_opts.isa == -1)
+  if (mips_arch == CPU_UNKNOWN && mips_cpu != CPU_UNKNOWN)
     {
-      if (mips_isa_from_cpu != -1)
-       mips_opts.isa = mips_isa_from_cpu;
-      else
-       mips_opts.isa = 1;
+      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."));
     }
 
-  if (mips_opts.mips16 < 0)
+  /* 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)
     {
-      if (strncmp (TARGET_CPU, "mips16", sizeof "mips16" - 1) == 0)
-       mips_opts.mips16 = 1;
-      else
-       mips_opts.mips16 = 0;
+      /* 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;
+       }
+    }
+  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.  */
@@ -1046,51 +1127,30 @@ md_begin ()
       a = NULL;
     }
 
-  if (mips_opts.isa == 1 && mips_trap)
+  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_abi_string
-      && 0 == strcmp (mips_abi_string,"eabi"))
+      && mips_opts.abi == EABI_ABI)
     mips_eabi64 = 1;
 
-  if (mips_cpu != 0 && mips_cpu != -1)
-    {
-      ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, mips_cpu);
-
-      /* 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 != -1
-         && ! ISA_HAS_64BIT_REGS (mips_opts.isa)
-         && ISA_HAS_64BIT_REGS (mips_isa_from_cpu))
-       mips_32bitmode = 1;
-    }
-  else
-    {
-      switch (mips_opts.isa)
-       {
-       case 1:
-         ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, CPU_R3000);
-         break;
-       case 2:
-         ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, CPU_R6000);
-         break;
-       case 3:
-         ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, CPU_R4000);
-         break;
-       case 4:
-         ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, CPU_R8000);
-         break;
-       }
-    }
+  /* 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 (! ok)
+  if (! bfd_set_arch_mach (stdoutput, bfd_arch_mips, mips_arch))
     as_warn (_("Could not set architecture and machine"));
 
   file_mips_isa = mips_opts.isa;
+  file_mips_abi = mips_opts.abi;
+  mips_opts.gp32 = file_mips_gp32;
+  mips_opts.fp32 = file_mips_fp32;
 
   op_hash = hash_new ();
 
@@ -1217,7 +1277,7 @@ md_begin ()
        if (strcmp (TARGET_OS, "elf") != 0)
          flags |= SEC_ALLOC | SEC_LOAD;
 
-       if (! mips_64)
+       if (! HAVE_NEWABI)
          {
            sec = subseg_new (".reginfo", (subsegT) 0);
 
@@ -1291,20 +1351,26 @@ md_assemble (str)
      char *str;
 {
   struct mips_cl_insn insn;
+  bfd_reloc_code_real_type unused_reloc[3]
+    = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
 
   imm_expr.X_op = O_absent;
-  imm_reloc = BFD_RELOC_UNUSED;
   imm_unmatched_hi = false;
   offset_expr.X_op = O_absent;
-  offset_reloc = BFD_RELOC_UNUSED;
+  imm_reloc[0] = BFD_RELOC_UNUSED;
+  imm_reloc[1] = BFD_RELOC_UNUSED;
+  imm_reloc[2] = BFD_RELOC_UNUSED;
+  offset_reloc[0] = BFD_RELOC_UNUSED;
+  offset_reloc[1] = BFD_RELOC_UNUSED;
+  offset_reloc[2] = BFD_RELOC_UNUSED;
 
   if (mips_opts.mips16)
     mips16_ip (str, &insn);
   else
     {
       mips_ip (str, &insn);
-      DBG((_("returned from mips_ip(%s) insn_opcode = 0x%x\n"),
-               str, insn.insn_opcode));
+      DBG ((_("returned from mips_ip(%s) insn_opcode = 0x%x\n"),
+           str, insn.insn_opcode));
     }
 
   if (insn_error)
@@ -1323,12 +1389,11 @@ md_assemble (str)
   else
     {
       if (imm_expr.X_op != O_absent)
-       append_insn ((char *) NULL, &insn, &imm_expr, imm_reloc,
-                    imm_unmatched_hi);
+       append_insn (NULL, &insn, &imm_expr, imm_reloc, imm_unmatched_hi);
       else if (offset_expr.X_op != O_absent)
-       append_insn ((char *) NULL, &insn, &offset_expr, offset_reloc, false);
+       append_insn (NULL, &insn, &offset_expr, offset_reloc, false);
       else
-       append_insn ((char *) NULL, &insn, NULL, BFD_RELOC_UNUSED, false);
+       append_insn (NULL, &insn, NULL, unused_reloc, false);
     }
 }
 
@@ -1455,6 +1520,7 @@ mips16_mark_labels ()
   if (mips_opts.mips16)
     {
       struct insn_label_list *l;
+      valueT val;
 
       for (l = insn_labels; l != NULL; l = l->next)
        {
@@ -1462,8 +1528,9 @@ mips16_mark_labels ()
          if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
            S_SET_OTHER (l->label, STO_MIPS16);
 #endif
-         if ((S_GET_VALUE (l->label) & 1) == 0)
-           S_SET_VALUE (l->label, S_GET_VALUE (l->label) + 1);
+         val = S_GET_VALUE (l->label);
+         if ((val & 1) == 0)
+           S_SET_VALUE (l->label, val + 1);
        }
     }
 }
@@ -1478,12 +1545,12 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
      char *place;
      struct mips_cl_insn *ip;
      expressionS *address_expr;
-     bfd_reloc_code_real_type reloc_type;
+     bfd_reloc_code_real_type *reloc_type;
      boolean unmatched_hi;
 {
   register unsigned long prev_pinfo, pinfo;
   char *f;
-  fixS *fixp;
+  fixS *fixp[3];
   int nops = 0;
 
   /* Mark instruction labels in mips16 mode.  */
@@ -1536,7 +1603,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
             delays delay the use of general register rt for one
             instruction on the r3000.  The r6000 and r4000 use
             interlocks.  */
-          /* Itbl support may require additional care here.  */
+         /* Itbl support may require additional care here.  */
          know (prev_pinfo & INSN_WRITE_GPR_T);
          if (mips_optimize == 0
              || insn_uses_reg (ip,
@@ -1548,8 +1615,8 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
       else if (! mips_opts.mips16
               && ISA_HAS_COPROC_DELAYS (mips_opts.isa)
               && (((prev_pinfo & INSN_COPROC_MOVE_DELAY)
-                    && ! cop_interlocks)
-                  || (mips_opts.isa == 1
+                   && ! cop_interlocks)
+                  || (mips_opts.isa == ISA_MIPS1
                       && (prev_pinfo & INSN_COPROC_MEMORY_DELAY))))
        {
          /* A generic coprocessor delay.  The previous instruction
@@ -1614,7 +1681,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
             (this means it is a floating point comparison
             instruction).  If this instruction uses the condition
             codes, we need to insert a single NOP.  */
-          /* Itbl support may require additional care here.  */
+         /* Itbl support may require additional care here.  */
          if (mips_optimize == 0
              || (pinfo & INSN_READ_COND_CODE))
            ++nops;
@@ -1628,9 +1695,8 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
       else if (mips_7000_hilo_fix
               && MF_HILO_INSN (prev_pinfo)
               && insn_uses_reg (ip, ((prev_insn.insn_opcode >> OP_SH_RD)
-                                       & OP_MASK_RD),
-                                    MIPS_GR_REG))
-
+                                     & OP_MASK_RD),
+                                MIPS_GR_REG))
        {
          nops += 2;
        }
@@ -1660,7 +1726,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
              though the tx39's divide insns still do require the
             delay.  */
          if (! (hilo_interlocks
-                || (mips_cpu == CPU_R3900 && (pinfo & INSN_MULT)))
+                || (mips_tune == CPU_R3900 && (pinfo & INSN_MULT)))
              && (mips_optimize == 0
                  || (pinfo & INSN_WRITE_LO)))
            nops += 2;
@@ -1682,7 +1748,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
             insert a NOP.  Some newer processors have interlocks.
             Also the note tx39's multiply above.  */
          if (! (hilo_interlocks
-                || (mips_cpu == CPU_R3900 && (pinfo & INSN_MULT)))
+                || (mips_tune == CPU_R3900 && (pinfo & INSN_MULT)))
              && (mips_optimize == 0
                  || (pinfo & INSN_WRITE_HI)))
            nops += 2;
@@ -1721,11 +1787,11 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
          || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)
              && (pinfo & INSN_WRITE_LO)
              && ! (hilo_interlocks
-                   || (mips_cpu == CPU_R3900 && (pinfo & INSN_MULT))))
+                   || (mips_tune == CPU_R3900 && (pinfo & INSN_MULT))))
          || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
              && (pinfo & INSN_WRITE_HI)
              && ! (hilo_interlocks
-                   || (mips_cpu == CPU_R3900 && (pinfo & INSN_MULT)))))
+                   || (mips_tune == CPU_R3900 && (pinfo & INSN_MULT)))))
        prev_prev_nop = 1;
       else
        prev_prev_nop = 0;
@@ -1775,12 +1841,15 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
 
          for (l = insn_labels; l != NULL; l = l->next)
            {
+             valueT val;
+
              assert (S_GET_SEGMENT (l->label) == now_seg);
              symbol_set_frag (l->label, frag_now);
-             S_SET_VALUE (l->label, (valueT) frag_now_fix ());
+             val = (valueT) frag_now_fix ();
              /* mips16 text labels are stored as odd.  */
              if (mips_opts.mips16)
-               S_SET_VALUE (l->label, S_GET_VALUE (l->label) + 1);
+               val += 1;
+             S_SET_VALUE (l->label, val);
            }
 
 #ifndef NO_ECOFF_DEBUGGING
@@ -1827,25 +1896,24 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
        }
     }
 
-  if (reloc_type > BFD_RELOC_UNUSED)
+  if (*reloc_type > BFD_RELOC_UNUSED)
     {
       /* We need to set up a variant frag.  */
       assert (mips_opts.mips16 && address_expr != NULL);
       f = frag_var (rs_machine_dependent, 4, 0,
-                   RELAX_MIPS16_ENCODE (reloc_type - BFD_RELOC_UNUSED,
+                   RELAX_MIPS16_ENCODE (*reloc_type - BFD_RELOC_UNUSED,
                                         mips16_small, mips16_ext,
                                         (prev_pinfo
                                          & INSN_UNCOND_BRANCH_DELAY),
-                                        (prev_insn_reloc_type
+                                        (*prev_insn_reloc_type
                                          == BFD_RELOC_MIPS16_JMP)),
-                   make_expr_symbol (address_expr), (offsetT) 0,
-                   (char *) NULL);
+                   make_expr_symbol (address_expr), 0, NULL);
     }
   else if (place != NULL)
     f = place;
   else if (mips_opts.mips16
           && ! ip->use_extend
-          && reloc_type != BFD_RELOC_MIPS16_JMP)
+          && *reloc_type != BFD_RELOC_MIPS16_JMP)
     {
       /* Make sure there is enough room to swap this instruction with
          a following jump instruction.  */
@@ -1862,17 +1930,39 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
       f = frag_more (4);
     }
 
-  fixp = NULL;
-  if (address_expr != NULL && reloc_type < BFD_RELOC_UNUSED)
+  fixp[0] = fixp[1] = fixp[2] = NULL;
+  if (address_expr != NULL && *reloc_type < BFD_RELOC_UNUSED)
     {
       if (address_expr->X_op == O_constant)
        {
-         switch (reloc_type)
+         unsigned long tmp;
+
+         switch (*reloc_type)
            {
            case BFD_RELOC_32:
              ip->insn_opcode |= address_expr->X_add_number;
              break;
 
+           case BFD_RELOC_MIPS_HIGHEST:
+             tmp = (address_expr->X_add_number + 0x800080008000) >> 16;
+             tmp >>= 16;
+             ip->insn_opcode |= (tmp >> 16) & 0xffff;
+             break;
+
+           case BFD_RELOC_MIPS_HIGHER:
+             tmp = (address_expr->X_add_number + 0x80008000) >> 16;
+             ip->insn_opcode |= (tmp >> 16) & 0xffff;
+             break;
+
+           case BFD_RELOC_HI16_S:
+             ip->insn_opcode |= ((address_expr->X_add_number + 0x8000)
+                                 >> 16) & 0xffff;
+             break;
+
+           case BFD_RELOC_HI16:
+             ip->insn_opcode |= (address_expr->X_add_number >> 16) & 0xffff;
+             break;
+
            case BFD_RELOC_LO16:
              ip->insn_opcode |= address_expr->X_add_number & 0xffff;
              break;
@@ -1894,6 +1984,10 @@ 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;
 
@@ -1904,33 +1998,122 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
       else
        {
        need_reloc:
-         /* Don't generate a reloc if we are writing into a variant
-            frag.  */
+         /* Don't generate a reloc if we are writing into a variant frag.  */
          if (place == NULL)
            {
-             fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 4,
-                                 address_expr,
-                                 reloc_type == BFD_RELOC_16_PCREL_S2,
-                                 reloc_type);
+             fixp[0] = fix_new_exp (frag_now, f - frag_now->fr_literal, 4,
+                                    address_expr,
+                                    (*reloc_type == BFD_RELOC_16_PCREL
+                                     || *reloc_type == 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))
+               fixp[0]->fx_no_overflow = 1;
+
              if (unmatched_hi)
                {
                  struct mips_hi_fixup *hi_fixup;
 
-                 assert (reloc_type == BFD_RELOC_HI16_S);
+                 assert (*reloc_type == BFD_RELOC_HI16_S);
                  hi_fixup = ((struct mips_hi_fixup *)
                              xmalloc (sizeof (struct mips_hi_fixup)));
-                 hi_fixup->fixp = fixp;
+                 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;
+                   }
+               }
            }
        }
     }
 
   if (! mips_opts.mips16)
     md_number_to_chars (f, ip->insn_opcode, 4);
-  else if (reloc_type == BFD_RELOC_MIPS16_JMP)
+  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);
@@ -1972,7 +2155,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
             instruction.  May want to add this support in the future.  */
        }
       /* Never set the bit for $0, which is always zero.  */
-      mips_gprmask &=1 << 0;
+      mips_gprmask &= ~1 << 0;
     }
   else
     {
@@ -2075,7 +2258,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                         | INSN_COPROC_MOVE_DELAY
                         | INSN_WRITE_COND_CODE)))
              || (! (hilo_interlocks
-                    || (mips_cpu == CPU_R3900 && (pinfo & INSN_MULT)))
+                    || (mips_tune == CPU_R3900 && (pinfo & INSN_MULT)))
                  && (prev_pinfo
                      & (INSN_READ_LO
                         | INSN_READ_HI)))
@@ -2083,7 +2266,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                  && ! gpr_interlocks
                  && (prev_pinfo & INSN_LOAD_MEMORY_DELAY))
              || (! mips_opts.mips16
-                 && mips_opts.isa == 1
+                 && mips_opts.isa == ISA_MIPS1
                   /* Itbl support may require additional care here.  */
                  && (prev_pinfo & INSN_COPROC_MEMORY_DELAY))
              /* We can not swap with a branch instruction.  */
@@ -2214,7 +2397,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
              /* If the previous instruction had a fixup in mips16
                  mode, we can not swap.  This normally means that the
                  previous instruction was a 4 byte branch anyhow.  */
-             || (mips_opts.mips16 && prev_insn_fixp)
+             || (mips_opts.mips16 && prev_insn_fixp[0])
              /* If the previous instruction is a sync, sync.l, or
                 sync.p, we can not swap.  */
              || (prev_pinfo & INSN_SYNC))
@@ -2240,15 +2423,35 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                  memcpy (temp, prev_f, 4);
                  memcpy (prev_f, f, 4);
                  memcpy (f, temp, 4);
-                 if (prev_insn_fixp)
+                 if (prev_insn_fixp[0])
                    {
-                     prev_insn_fixp->fx_frag = frag_now;
-                     prev_insn_fixp->fx_where = f - frag_now->fr_literal;
+                     prev_insn_fixp[0]->fx_frag = frag_now;
+                     prev_insn_fixp[0]->fx_where = f - frag_now->fr_literal;
                    }
-                 if (fixp)
+                 if (prev_insn_fixp[1])
                    {
-                     fixp->fx_frag = prev_insn_frag;
-                     fixp->fx_where = prev_insn_where;
+                     prev_insn_fixp[1]->fx_frag = frag_now;
+                     prev_insn_fixp[1]->fx_where = f - frag_now->fr_literal;
+                   }
+                 if (prev_insn_fixp[2])
+                   {
+                     prev_insn_fixp[2]->fx_frag = frag_now;
+                     prev_insn_fixp[2]->fx_where = f - frag_now->fr_literal;
+                   }
+                 if (fixp[0])
+                   {
+                     fixp[0]->fx_frag = prev_insn_frag;
+                     fixp[0]->fx_where = prev_insn_where;
+                   }
+                 if (fixp[1])
+                   {
+                     fixp[1]->fx_frag = prev_insn_frag;
+                     fixp[1]->fx_where = prev_insn_where;
+                   }
+                 if (fixp[2])
+                   {
+                     fixp[2]->fx_frag = prev_insn_frag;
+                     fixp[2]->fx_where = prev_insn_where;
                    }
                }
              else
@@ -2256,13 +2459,15 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                  char *prev_f;
                  char temp[2];
 
-                 assert (prev_insn_fixp == NULL);
+                 assert (prev_insn_fixp[0] == NULL);
+                 assert (prev_insn_fixp[1] == NULL);
+                 assert (prev_insn_fixp[2] == NULL);
                  prev_f = prev_insn_frag->fr_literal + prev_insn_where;
                  memcpy (temp, prev_f, 2);
                  memcpy (prev_f, f, 2);
-                 if (reloc_type != BFD_RELOC_MIPS16_JMP)
+                 if (*reloc_type != BFD_RELOC_MIPS16_JMP)
                    {
-                     assert (reloc_type == BFD_RELOC_UNUSED);
+                     assert (*reloc_type == BFD_RELOC_UNUSED);
                      memcpy (f, temp, 2);
                    }
                  else
@@ -2270,10 +2475,20 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                      memcpy (f, f + 2, 2);
                      memcpy (f + 2, temp, 2);
                    }
-                 if (fixp)
+                 if (fixp[0])
                    {
-                     fixp->fx_frag = prev_insn_frag;
-                     fixp->fx_where = prev_insn_where;
+                     fixp[0]->fx_frag = prev_insn_frag;
+                     fixp[0]->fx_where = prev_insn_where;
+                   }
+                 if (fixp[1])
+                   {
+                     fixp[1]->fx_frag = prev_insn_frag;
+                     fixp[1]->fx_where = prev_insn_where;
+                   }
+                 if (fixp[2])
+                   {
+                     fixp[2]->fx_frag = prev_insn_frag;
+                     fixp[2]->fx_where = prev_insn_where;
                    }
                }
 
@@ -2291,8 +2506,12 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
              prev_insn.insn_mo = &dummy_opcode;
            }
 
-         prev_insn_fixp = NULL;
-         prev_insn_reloc_type = BFD_RELOC_UNUSED;
+         prev_insn_fixp[0] = NULL;
+         prev_insn_fixp[1] = NULL;
+         prev_insn_fixp[2] = NULL;
+         prev_insn_reloc_type[0] = BFD_RELOC_UNUSED;
+         prev_insn_reloc_type[1] = BFD_RELOC_UNUSED;
+         prev_insn_reloc_type[2] = BFD_RELOC_UNUSED;
          prev_insn_extended = 0;
        }
       else if (pinfo & INSN_COND_BRANCH_LIKELY)
@@ -2305,8 +2524,12 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
          /* Update the previous insn information.  */
          prev_prev_insn = *ip;
          prev_insn.insn_mo = &dummy_opcode;
-         prev_insn_fixp = NULL;
-         prev_insn_reloc_type = BFD_RELOC_UNUSED;
+         prev_insn_fixp[0] = NULL;
+         prev_insn_fixp[1] = NULL;
+         prev_insn_fixp[2] = NULL;
+         prev_insn_reloc_type[0] = BFD_RELOC_UNUSED;
+         prev_insn_reloc_type[1] = BFD_RELOC_UNUSED;
+         prev_insn_reloc_type[2] = BFD_RELOC_UNUSED;
          prev_insn_extended = 0;
        }
       else
@@ -2323,11 +2546,15 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
             is not in a delay slot.  */
          prev_insn_is_delay_slot = 0;
 
-         prev_insn_fixp = fixp;
-         prev_insn_reloc_type = reloc_type;
+         prev_insn_fixp[0] = fixp[0];
+         prev_insn_fixp[1] = fixp[1];
+         prev_insn_fixp[2] = fixp[2];
+         prev_insn_reloc_type[0] = reloc_type[0];
+         prev_insn_reloc_type[1] = reloc_type[1];
+         prev_insn_reloc_type[2] = reloc_type[2];
          if (mips_opts.mips16)
            prev_insn_extended = (ip->use_extend
-                                 || reloc_type > BFD_RELOC_UNUSED);
+                                 || *reloc_type > BFD_RELOC_UNUSED);
        }
 
       prev_prev_insn_unreordered = prev_insn_unreordered;
@@ -2343,7 +2570,9 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
          PC relative relocs.  */
       prev_prev_insn = prev_insn;
       prev_insn = *ip;
-      prev_insn_reloc_type = reloc_type;
+      prev_insn_reloc_type[0] = reloc_type[0];
+      prev_insn_reloc_type[1] = reloc_type[1];
+      prev_insn_reloc_type[2] = reloc_type[2];
       prev_prev_insn_unreordered = prev_insn_unreordered;
       prev_insn_unreordered = 1;
     }
@@ -2383,7 +2612,9 @@ mips_no_prev_insn (preserve)
   prev_insn_is_delay_slot = 0;
   prev_insn_unreordered = 0;
   prev_insn_extended = 0;
-  prev_insn_reloc_type = BFD_RELOC_UNUSED;
+  prev_insn_reloc_type[0] = BFD_RELOC_UNUSED;
+  prev_insn_reloc_type[1] = BFD_RELOC_UNUSED;
+  prev_insn_reloc_type[2] = BFD_RELOC_UNUSED;
   prev_prev_insn_unreordered = 0;
   mips_clear_insn_labels ();
 }
@@ -2419,11 +2650,11 @@ mips_emit_delays (insns)
              && (prev_insn.insn_mo->pinfo
                   & INSN_LOAD_MEMORY_DELAY))
          || (! mips_opts.mips16
-             && mips_opts.isa == 1
+             && mips_opts.isa == ISA_MIPS1
              && (prev_insn.insn_mo->pinfo
                  & INSN_COPROC_MEMORY_DELAY)))
        {
-          /* Itbl support may require additional care here.  */
+         /* Itbl support may require additional care here.  */
          ++nops;
          if ((! mips_opts.mips16
               && ISA_HAS_COPROC_DELAYS (mips_opts.isa)
@@ -2445,7 +2676,7 @@ mips_emit_delays (insns)
                   && ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
                       || (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO))))
        {
-          /* Itbl support may require additional care here.  */
+         /* Itbl support may require additional care here.  */
          if (! prev_prev_insn_unreordered)
            ++nops;
        }
@@ -2478,12 +2709,15 @@ mips_emit_delays (insns)
 
          for (l = insn_labels; l != NULL; l = l->next)
            {
+             valueT val;
+
              assert (S_GET_SEGMENT (l->label) == now_seg);
              symbol_set_frag (l->label, frag_now);
-             S_SET_VALUE (l->label, (valueT) frag_now_fix ());
+             val = (valueT) frag_now_fix ();
              /* mips16 text labels are stored as odd.  */
              if (mips_opts.mips16)
-               S_SET_VALUE (l->label, S_GET_VALUE (l->label) + 1);
+               val += 1;
+             S_SET_VALUE (l->label, val);
            }
        }
     }
@@ -2520,7 +2754,7 @@ macro_build (place, counter, ep, name, fmt, va_alist)
 #endif
 {
   struct mips_cl_insn insn;
-  bfd_reloc_code_real_type r;
+  bfd_reloc_code_real_type r[3];
   va_list args;
 
 #ifdef USE_STDARG
@@ -2547,7 +2781,9 @@ macro_build (place, counter, ep, name, fmt, va_alist)
       return;
     }
 
-  r = BFD_RELOC_UNUSED;
+  r[0] = BFD_RELOC_UNUSED;
+  r[1] = BFD_RELOC_UNUSED;
+  r[2] = BFD_RELOC_UNUSED;
   insn.insn_mo = (struct mips_opcode *) hash_find (op_hash, name);
   assert (insn.insn_mo);
   assert (strcmp (name, insn.insn_mo->name) == 0);
@@ -2557,9 +2793,8 @@ macro_build (place, counter, ep, name, fmt, va_alist)
     {
       if (strcmp (fmt, insn.insn_mo->args) == 0
          && insn.insn_mo->pinfo != INSN_MACRO
-         && OPCODE_IS_MEMBER (insn.insn_mo, mips_opts.isa, mips_cpu,
-                              mips_gp32)
-         && (mips_cpu != CPU_R4650 || (insn.insn_mo->pinfo & FP_D) == 0))
+         && OPCODE_IS_MEMBER (insn.insn_mo, mips_opts.isa, mips_arch)
+         && (mips_arch != CPU_R4650 || (insn.insn_mo->pinfo & FP_D) == 0))
        break;
 
       ++insn.insn_mo;
@@ -2583,83 +2818,97 @@ macro_build (place, counter, ep, name, fmt, va_alist)
        case 't':
        case 'w':
        case 'E':
-         insn.insn_opcode |= va_arg (args, int) << 16;
+         insn.insn_opcode |= va_arg (args, int) << OP_SH_RT;
          continue;
 
        case 'c':
+         insn.insn_opcode |= va_arg (args, int) << OP_SH_CODE;
+         continue;
+
        case 'T':
        case 'W':
-         insn.insn_opcode |= va_arg (args, int) << 16;
+         insn.insn_opcode |= va_arg (args, int) << OP_SH_FT;
          continue;
 
        case 'd':
        case 'G':
-         insn.insn_opcode |= va_arg (args, int) << 11;
+         insn.insn_opcode |= va_arg (args, int) << OP_SH_RD;
          continue;
 
+       case 'U':
+         {
+           int tmp = va_arg (args, int);
+
+           insn.insn_opcode |= tmp << OP_SH_RT;
+           insn.insn_opcode |= tmp << OP_SH_RD;
+           continue;
+         }
+
        case 'V':
        case 'S':
-         insn.insn_opcode |= va_arg (args, int) << 11;
+         insn.insn_opcode |= va_arg (args, int) << OP_SH_FS;
          continue;
 
        case 'z':
          continue;
 
        case '<':
-         insn.insn_opcode |= va_arg (args, int) << 6;
+         insn.insn_opcode |= va_arg (args, int) << OP_SH_SHAMT;
          continue;
 
        case 'D':
-         insn.insn_opcode |= va_arg (args, int) << 6;
+         insn.insn_opcode |= va_arg (args, int) << OP_SH_FD;
          continue;
 
        case 'B':
-         insn.insn_opcode |= va_arg (args, int) << 6;
+         insn.insn_opcode |= va_arg (args, int) << OP_SH_CODE20;
+         continue;
+
+       case 'J':
+         insn.insn_opcode |= va_arg (args, int) << OP_SH_CODE19;
          continue;
 
        case 'q':
-         insn.insn_opcode |= va_arg (args, int) << 6;
+         insn.insn_opcode |= va_arg (args, int) << OP_SH_CODE2;
          continue;
 
        case 'b':
        case 's':
        case 'r':
        case 'v':
-         insn.insn_opcode |= va_arg (args, int) << 21;
+         insn.insn_opcode |= va_arg (args, int) << OP_SH_RS;
          continue;
 
        case 'i':
        case 'j':
        case 'o':
-         r = (bfd_reloc_code_real_type) va_arg (args, int);
-         assert (r == BFD_RELOC_MIPS_GPREL
-                 || r == BFD_RELOC_MIPS_LITERAL
-                 || r == BFD_RELOC_LO16
-                 || r == BFD_RELOC_MIPS_GOT16
-                 || r == BFD_RELOC_MIPS_CALL16
-                 || r == BFD_RELOC_MIPS_GOT_LO16
-                 || r == BFD_RELOC_MIPS_CALL_LO16
+         *r = (bfd_reloc_code_real_type) va_arg (args, int);
+         assert (*r == BFD_RELOC_MIPS_GPREL
+                 || *r == BFD_RELOC_MIPS_LITERAL
+                 || *r == BFD_RELOC_MIPS_HIGHER
+                 || *r == BFD_RELOC_HI16_S
+                 || *r == BFD_RELOC_LO16
+                 || *r == BFD_RELOC_MIPS_GOT16
+                 || *r == BFD_RELOC_MIPS_CALL16
+                 || *r == BFD_RELOC_MIPS_GOT_LO16
+                 || *r == BFD_RELOC_MIPS_CALL_LO16
                  || (ep->X_op == O_subtract
-                     && r == BFD_RELOC_PCREL_LO16));
+                     && *r == BFD_RELOC_PCREL_LO16));
          continue;
 
        case 'u':
-         r = (bfd_reloc_code_real_type) va_arg (args, int);
+         *r = (bfd_reloc_code_real_type) va_arg (args, int);
          assert (ep != NULL
                  && (ep->X_op == O_constant
                      || (ep->X_op == O_symbol
-                         && (r == BFD_RELOC_HI16_S
-                             || r == BFD_RELOC_HI16
-                             || r == BFD_RELOC_MIPS_GOT_HI16
-                             || r == BFD_RELOC_MIPS_CALL_HI16))
+                         && (*r == BFD_RELOC_MIPS_HIGHEST
+                             || *r == BFD_RELOC_HI16_S
+                             || *r == BFD_RELOC_HI16
+                             || *r == BFD_RELOC_GPREL16
+                             || *r == BFD_RELOC_MIPS_GOT_HI16
+                             || *r == BFD_RELOC_MIPS_CALL_HI16))
                      || (ep->X_op == O_subtract
-                         && r == BFD_RELOC_PCREL_HI16_S)));
-         if (ep->X_op == O_constant)
-           {
-             insn.insn_opcode |= (ep->X_add_number >> 16) & 0xffff;
-             ep = NULL;
-             r = BFD_RELOC_UNUSED;
-           }
+                         && *r == BFD_RELOC_PCREL_HI16_S)));
          continue;
 
        case 'p':
@@ -2677,12 +2926,15 @@ macro_build (place, counter, ep, name, fmt, va_alist)
              ep = NULL;
            }
          else
-           r = BFD_RELOC_16_PCREL_S2;
+           if (mips_pic == EMBEDDED_PIC)
+             *r = BFD_RELOC_16_PCREL_S2;
+           else
+             *r = BFD_RELOC_16_PCREL;
          continue;
 
        case 'a':
          assert (ep != NULL);
-         r = BFD_RELOC_MIPS_JMP;
+         *r = BFD_RELOC_MIPS_JMP;
          continue;
 
        case 'C':
@@ -2695,7 +2947,7 @@ macro_build (place, counter, ep, name, fmt, va_alist)
       break;
     }
   va_end (args);
-  assert (r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
+  assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
 
   append_insn (place, &insn, ep, r, false);
 }
@@ -2710,9 +2962,9 @@ mips16_macro_build (place, counter, ep, name, fmt, args)
      va_list args;
 {
   struct mips_cl_insn insn;
-  bfd_reloc_code_real_type r;
+  bfd_reloc_code_real_type r[3]
+    = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
 
-  r = BFD_RELOC_UNUSED;
   insn.insn_mo = (struct mips_opcode *) hash_find (mips16_op_hash, name);
   assert (insn.insn_mo);
   assert (strcmp (name, insn.insn_mo->name) == 0);
@@ -2801,14 +3053,14 @@ mips16_macro_build (place, counter, ep, name, fmt, args)
            assert (ep != NULL);
 
            if (ep->X_op != O_constant)
-             r = BFD_RELOC_UNUSED + c;
+             *r = BFD_RELOC_UNUSED + c;
            else
              {
-               mips16_immed ((char *) NULL, 0, c, ep->X_add_number, false,
-                             false, false, &insn.insn_opcode,
-                             &insn.use_extend, &insn.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;
+               *r = BFD_RELOC_UNUSED;
              }
          }
          continue;
@@ -2821,7 +3073,7 @@ mips16_macro_build (place, counter, ep, name, fmt, args)
       break;
     }
 
-  assert (r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
+  assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
 
   append_insn (place, &insn, ep, r, false);
 }
@@ -2838,7 +3090,8 @@ macro_build_lui (place, counter, ep, regnum)
 {
   expressionS high_expr;
   struct mips_cl_insn insn;
-  bfd_reloc_code_real_type r;
+  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";
 
@@ -2855,19 +3108,17 @@ macro_build_lui (place, counter, ep, regnum)
   if (high_expr.X_op == O_constant)
     {
       /* we can compute the instruction now without a relocation entry */
-      if (high_expr.X_add_number & 0x8000)
-       high_expr.X_add_number += 0x10000;
-      high_expr.X_add_number =
-       ((unsigned long) high_expr.X_add_number >> 16) & 0xffff;
-      r = BFD_RELOC_UNUSED;
+      high_expr.X_add_number = ((high_expr.X_add_number + 0x8000)
+                               >> 16) & 0xffff;
+      *r = BFD_RELOC_UNUSED;
     }
-  else
+  else if (! HAVE_NEWABI)
     {
       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);
-      r = BFD_RELOC_HI16_S;
+      *r = BFD_RELOC_HI16_S;
     }
 
   /*
@@ -2887,7 +3138,7 @@ macro_build_lui (place, counter, ep, regnum)
   assert (strcmp (fmt, insn.insn_mo->args) == 0);
 
   insn.insn_opcode = insn.insn_mo->match | (regnum << OP_SH_RT);
-  if (r == BFD_RELOC_UNUSED)
+  if (*r == BFD_RELOC_UNUSED)
     {
       insn.insn_opcode |= high_expr.X_add_number;
       append_insn (place, &insn, NULL, r, false);
@@ -2915,7 +3166,7 @@ set_at (counter, reg, unsignedp)
   else
     {
       load_register (counter, AT, &imm_expr, 0);
-      macro_build ((char *) NULL, counter, NULL,
+      macro_build ((char *) NULL, counter, (expressionS *) NULL,
                   unsignedp ? "sltu" : "slt",
                   "d,v,t", AT, reg, AT);
     }
@@ -3056,9 +3307,9 @@ load_register (counter, reg, ep, dbl)
                    || ! ep->X_unsigned
                    || sizeof (ep->X_add_number) > 4
                    || (ep->X_add_number & 0x80000000) == 0))
-              || ((! ISA_HAS_64BIT_REGS (mips_opts.isa) || ! dbl)
+              || ((HAVE_32BIT_GPRS || ! dbl)
                   && (ep->X_add_number &~ (offsetT) 0xffffffff) == 0)
-              || (! ISA_HAS_64BIT_REGS (mips_opts.isa)
+              || (HAVE_32BIT_GPRS
                   && ! dbl
                   && ((ep->X_add_number &~ (offsetT) 0xffffffff)
                       == ~ (offsetT) 0xffffffff)))
@@ -3075,7 +3326,7 @@ load_register (counter, reg, ep, dbl)
 
   /* The value is larger than 32 bits.  */
 
-  if (! ISA_HAS_64BIT_REGS (mips_opts.isa))
+  if (HAVE_32BIT_GPRS)
     {
       as_bad (_("Number larger than 32 bits"));
       macro_build ((char *) NULL, counter, ep, "addiu", "t,r,j", reg, 0,
@@ -3113,63 +3364,65 @@ load_register (counter, reg, ep, dbl)
       unsigned long hi, lo;
 
       if (hi32.X_add_number == 0xffffffff)
-        {
-          if ((lo32.X_add_number & 0xffff8000) == 0xffff8000)
-            {
-              macro_build ((char *) NULL, counter, &lo32, "addiu", "t,r,j",
+       {
+         if ((lo32.X_add_number & 0xffff8000) == 0xffff8000)
+           {
+             macro_build ((char *) NULL, counter, &lo32, "addiu", "t,r,j",
                           reg, 0, (int) BFD_RELOC_LO16);
-              return;
-            }
-          if (lo32.X_add_number & 0x80000000)
-            {
-              macro_build ((char *) NULL, counter, &lo32, "lui", "t,u", reg,
-                           (int) BFD_RELOC_HI16);
+             return;
+           }
+         if (lo32.X_add_number & 0x80000000)
+           {
+             macro_build ((char *) NULL, counter, &lo32, "lui", "t,u", reg,
+                          (int) BFD_RELOC_HI16);
              if (lo32.X_add_number & 0xffff)
                macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i",
                             reg, reg, (int) BFD_RELOC_LO16);
-              return;
-            }
-        }
+             return;
+           }
+       }
 
       /* Check for 16bit shifted constant.  We know that hi32 is
          non-zero, so start the mask on the first bit of the hi32
          value.  */
       shift = 17;
       do
-       {
-        unsigned long himask, lomask;
-
-        if (shift < 32)
-          {
-            himask = 0xffff >> (32 - shift);
-            lomask = (0xffff << shift) & 0xffffffff;
-          }
-        else
-          {
-            himask = 0xffff << (shift - 32);
-            lomask = 0;
-          }
-        if ((hi32.X_add_number & ~ (offsetT) himask) == 0
-            && (lo32.X_add_number & ~ (offsetT) lomask) == 0)
-          {
-            expressionS tmp;
-
-            tmp.X_op = O_constant;
-            if (shift < 32)
-              tmp.X_add_number = ((hi32.X_add_number << (32 - shift))
-                                  | (lo32.X_add_number >> shift));
-            else
-              tmp.X_add_number = hi32.X_add_number >> (shift - 32);
-            macro_build ((char *) NULL, counter, &tmp, "ori", "t,r,i", reg, 0,
-                         (int) BFD_RELOC_LO16);
-            macro_build ((char *) NULL, counter, NULL,
-                         (shift >= 32) ? "dsll32" : "dsll",
-                         "d,w,<", reg, reg,
-                         (shift >= 32) ? shift - 32 : shift);
-            return;
-          }
-         shift++;
-       } while (shift <= (64 - 16));
+       {
+         unsigned long himask, lomask;
+
+         if (shift < 32)
+           {
+             himask = 0xffff >> (32 - shift);
+             lomask = (0xffff << shift) & 0xffffffff;
+           }
+         else
+           {
+             himask = 0xffff << (shift - 32);
+             lomask = 0;
+           }
+         if ((hi32.X_add_number & ~(offsetT) himask) == 0
+             && (lo32.X_add_number & ~(offsetT) lomask) == 0)
+           {
+             expressionS tmp;
+
+             tmp.X_op = O_constant;
+             if (shift < 32)
+               tmp.X_add_number = ((hi32.X_add_number << (32 - shift))
+                                   | (lo32.X_add_number >> shift));
+             else
+               tmp.X_add_number = hi32.X_add_number >> (shift - 32);
+             macro_build ((char *) NULL, counter, &tmp,
+                          "ori", "t,r,i", reg, 0,
+                          (int) BFD_RELOC_LO16);
+             macro_build ((char *) NULL, counter, (expressionS *) NULL,
+                          (shift >= 32) ? "dsll32" : "dsll",
+                          "d,w,<", reg, reg,
+                          (shift >= 32) ? shift - 32 : shift);
+             return;
+           }
+         shift++;
+       }
+      while (shift <= (64 - 16));
 
       /* Find the bit number of the lowest one bit, and store the
          shifted value in hi/lo.  */
@@ -3201,38 +3454,38 @@ load_register (counter, reg, ep, dbl)
       /* Optimize if the shifted value is a (power of 2) - 1.  */
       if ((hi == 0 && ((lo + 1) & lo) == 0)
          || (lo == 0xffffffff && ((hi + 1) & hi) == 0))
-        {
-          shift = COUNT_TOP_ZEROES ((unsigned int) hi32.X_add_number);
+       {
+         shift = COUNT_TOP_ZEROES ((unsigned int) hi32.X_add_number);
          if (shift != 0)
-            {
+           {
              expressionS tmp;
 
              /* This instruction will set the register to be all
                  ones.  */
-              tmp.X_op = O_constant;
-              tmp.X_add_number = (offsetT) -1;
-              macro_build ((char *) NULL, counter, &tmp, "addiu", "t,r,j",
+             tmp.X_op = O_constant;
+             tmp.X_add_number = (offsetT) -1;
+             macro_build ((char *) NULL, counter, &tmp, "addiu", "t,r,j",
                           reg, 0, (int) BFD_RELOC_LO16);
-              if (bit != 0)
-                {
-                  bit += shift;
-                  macro_build ((char *) NULL, counter, NULL,
-                               (bit >= 32) ? "dsll32" : "dsll",
-                               "d,w,<", reg, reg,
-                               (bit >= 32) ? bit - 32 : bit);
-                }
-              macro_build ((char *) NULL, counter, NULL,
+             if (bit != 0)
+               {
+                 bit += shift;
+                 macro_build ((char *) NULL, counter, (expressionS *) NULL,
+                              (bit >= 32) ? "dsll32" : "dsll",
+                              "d,w,<", reg, reg,
+                              (bit >= 32) ? bit - 32 : bit);
+               }
+             macro_build ((char *) NULL, counter, (expressionS *) NULL,
                           (shift >= 32) ? "dsrl32" : "dsrl",
-                           "d,w,<", reg, reg,
+                          "d,w,<", reg, reg,
                           (shift >= 32) ? shift - 32 : shift);
-              return;
-            }
-        }
+             return;
+           }
+       }
 
       /* Sign extend hi32 before calling load_register, because we can
          generally get better code when we load a sign extended value.  */
       if ((hi32.X_add_number & 0x80000000) != 0)
-       hi32.X_add_number |= ~ (offsetT) 0xffffffff;
+       hi32.X_add_number |= ~(offsetT) 0xffffffff;
       load_register (counter, reg, &hi32, 0);
       freg = reg;
     }
@@ -3240,8 +3493,8 @@ load_register (counter, reg, ep, dbl)
     {
       if (freg != 0)
        {
-         macro_build ((char *) NULL, counter, NULL, "dsll32", "d,w,<", reg,
-                      freg, 0);
+         macro_build ((char *) NULL, counter, (expressionS *) NULL,
+                      "dsll32", "d,w,<", reg, freg, 0);
          freg = reg;
        }
     }
@@ -3250,13 +3503,13 @@ load_register (counter, reg, ep, dbl)
       expressionS mid16;
 
       if ((freg == 0) && (lo32.X_add_number == 0xffffffff))
-        {
+       {
          macro_build ((char *) NULL, counter, &lo32, "lui", "t,u", reg,
                       (int) BFD_RELOC_HI16);
-          macro_build ((char *) NULL, counter, NULL, "dsrl32", "d,w,<", reg,
-                       reg, 0);
-          return;
-        }
+         macro_build ((char *) NULL, counter, NULL, "dsrl32", "d,w,<", reg,
+                      reg, 0);
+         return;
+       }
 
       if (freg != 0)
        {
@@ -3280,10 +3533,12 @@ load_register (counter, reg, ep, dbl)
 /* Load an address into a register.  */
 
 static void
-load_address (counter, reg, ep)
+load_address (counter, reg, ep, dbl, used_at)
      int *counter;
      int reg;
      expressionS *ep;
+     int dbl;
+     int *used_at;
 {
   char *p;
 
@@ -3296,7 +3551,7 @@ load_address (counter, reg, ep)
 
   if (ep->X_op == O_constant)
     {
-      load_register (counter, reg, ep, 0);
+      load_register (counter, reg, ep, dbl);
       return;
     }
 
@@ -3307,33 +3562,88 @@ load_address (counter, reg, ep)
         Otherwise we want
           lui          $reg,<sym>              (BFD_RELOC_HI16_S)
           addiu        $reg,$reg,<sym>         (BFD_RELOC_LO16)
-        If we have an addend, we always use the latter form.  */
-      if ((valueT) ep->X_add_number >= MAX_GPREL_OFFSET
-          || nopic_need_relax (ep->X_add_symbol, 1))
-       p = NULL;
-      else
+        If we have an addend, we always use the latter form.
+
+        With 64bit address space and a usable $at we want
+          lui          $reg,<sym>              (BFD_RELOC_MIPS_HIGHEST)
+          lui          $at,<sym>               (BFD_RELOC_HI16_S)
+          daddiu       $reg,<sym>              (BFD_RELOC_MIPS_HIGHER)
+          daddiu       $at,<sym>               (BFD_RELOC_LO16)
+          dsll32       $reg,0
+          dadd         $reg,$reg,$at
+
+        If $at is already in use, we use an path which is suboptimal
+        on superscalar processors.
+          lui          $reg,<sym>              (BFD_RELOC_MIPS_HIGHEST)
+          daddiu       $reg,<sym>              (BFD_RELOC_MIPS_HIGHER)
+          dsll         $reg,16
+          daddiu       $reg,<sym>              (BFD_RELOC_HI16_S)
+          dsll         $reg,16
+          daddiu       $reg,<sym>              (BFD_RELOC_LO16)
+       */
+      if (HAVE_64BIT_ADDRESSES)
        {
-         frag_grow (20);
-         macro_build ((char *) NULL, counter, ep,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "addiu" : "daddiu"),
-                      "t,r,j", reg, GP, (int) BFD_RELOC_MIPS_GPREL);
-         p = frag_var (rs_machine_dependent, 8, 0,
-                       RELAX_ENCODE (4, 8, 0, 4, 0,
-                                     mips_opts.warn_about_macros),
-                       ep->X_add_symbol, (offsetT) 0, (char *) NULL);
-       }
-      macro_build_lui (p, counter, ep, reg);
-      if (p != NULL)
-       p += 4;
-      macro_build (p, counter, ep,
-                  ((bfd_arch_bits_per_address (stdoutput) == 32
-                    || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                   ? "addiu" : "daddiu"),
-                  "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
-    }
-  else if (mips_pic == SVR4_PIC && ! mips_big_got)
+         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)
+           {
+             macro_build (p, counter, ep, "lui", "t,u",
+                          reg, (int) BFD_RELOC_MIPS_HIGHEST);
+             macro_build (p, counter, ep, "lui", "t,u",
+                          AT, (int) BFD_RELOC_HI16_S);
+             macro_build (p, counter, ep, "daddiu", "t,r,j",
+                          reg, reg, (int) BFD_RELOC_MIPS_HIGHER);
+             macro_build (p, counter, ep, "daddiu", "t,r,j",
+                          AT, AT, (int) BFD_RELOC_LO16);
+             macro_build (p, counter, (expressionS *) NULL, "dsll32",
+                          "d,w,<", reg, reg, 0);
+             macro_build (p, counter, (expressionS *) NULL, "dadd",
+                          "d,v,t", reg, reg, AT);
+             *used_at = 1;
+           }
+         else
+           {
+             macro_build (p, counter, ep, "lui", "t,u",
+                          reg, (int) BFD_RELOC_MIPS_HIGHEST);
+             macro_build (p, counter, ep, "daddiu", "t,r,j",
+                          reg, reg, (int) BFD_RELOC_MIPS_HIGHER);
+             macro_build (p, counter, (expressionS *) NULL, "dsll",
+                          "d,w,<", reg, reg, 16);
+             macro_build (p, counter, ep, "daddiu", "t,r,j",
+                          reg, reg, (int) BFD_RELOC_HI16_S);
+             macro_build (p, counter, (expressionS *) NULL, "dsll",
+                          "d,w,<", reg, reg, 16);
+             macro_build (p, counter, ep, "daddiu", "t,r,j",
+                          reg, reg, (int) BFD_RELOC_LO16);
+           }
+       }
+      else
+       {
+         p = NULL;
+         if ((valueT) ep->X_add_number <= MAX_GPREL_OFFSET
+             && ! 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, GP, (int) BFD_RELOC_MIPS_GPREL);
+             p = frag_var (rs_machine_dependent, 8, 0,
+                           RELAX_ENCODE (4, 8, 0, 4, 0,
+                                         mips_opts.warn_about_macros),
+                           ep->X_add_symbol, (offsetT) 0, (char *) NULL);
+           }
+         macro_build_lui (p, counter, ep, reg);
+         if (p != NULL)
+           p += 4;
+         macro_build (p, counter, ep,
+                      HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
+                      "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+       }
+    }
+  else if (mips_pic == SVR4_PIC && ! mips_big_got)
     {
       expressionS ex;
 
@@ -3348,18 +3658,14 @@ load_address (counter, reg, ep)
       ep->X_add_number = 0;
       frag_grow (20);
       macro_build ((char *) NULL, counter, ep,
-                  ((bfd_arch_bits_per_address (stdoutput) == 32
-                    || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                   ? "lw" : "ld"),
+                  HAVE_32BIT_ADDRESSES ? "lw" : "ld",
                   "t,o(b)", reg, (int) BFD_RELOC_MIPS_GOT16, GP);
       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,
-                  ((bfd_arch_bits_per_address (stdoutput) == 32
-                    || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                   ? "addiu" : "daddiu"),
+                  HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
                   "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
       if (ex.X_add_number != 0)
        {
@@ -3367,9 +3673,7 @@ load_address (counter, reg, ep)
            as_bad (_("PIC code offset overflow (max 16 signed bits)"));
          ex.X_op = O_constant;
          macro_build ((char *) NULL, counter, &ex,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "addiu" : "daddiu"),
+                      HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
                       "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
        }
     }
@@ -3398,14 +3702,10 @@ load_address (counter, reg, ep)
       macro_build ((char *) NULL, counter, ep, "lui", "t,u", reg,
                   (int) BFD_RELOC_MIPS_GOT_HI16);
       macro_build ((char *) NULL, counter, (expressionS *) NULL,
-                  ((bfd_arch_bits_per_address (stdoutput) == 32
-                    || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                   ? "addu" : "daddu"),
+                  HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                   "d,v,t", reg, reg, GP);
       macro_build ((char *) NULL, counter, ep,
-                  ((bfd_arch_bits_per_address (stdoutput) == 32
-                    || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                   ? "lw" : "ld"),
+                  HAVE_32BIT_ADDRESSES ? "lw" : "ld",
                   "t,o(b)", reg, (int) BFD_RELOC_MIPS_GOT_LO16, reg);
       p = frag_var (rs_machine_dependent, 12 + off, 0,
                    RELAX_ENCODE (12, 12 + off, off, 8 + off, 0,
@@ -3420,18 +3720,12 @@ load_address (counter, reg, ep)
          macro_build (p, counter, (expressionS *) NULL, "nop", "");
          p += 4;
        }
-      macro_build (p, counter, ep,
-                  ((bfd_arch_bits_per_address (stdoutput) == 32
-                    || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                   ? "lw" : "ld"),
+      macro_build (p, counter, ep, HAVE_32BIT_ADDRESSES ? "lw" : "ld",
                   "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,
-                  ((bfd_arch_bits_per_address (stdoutput) == 32
-                    || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                   ? "addiu" : "daddiu"),
+      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)
        {
@@ -3439,9 +3733,7 @@ load_address (counter, reg, ep)
            as_bad (_("PIC code offset overflow (max 16 signed bits)"));
          ex.X_op = O_constant;
          macro_build ((char *) NULL, counter, &ex,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "addiu" : "daddiu"),
+                      HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
                       "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
        }
     }
@@ -3451,15 +3743,26 @@ load_address (counter, reg, ep)
           addiu        $reg,$gp,<sym>          (BFD_RELOC_MIPS_GPREL)
         */
       macro_build ((char *) NULL, counter, ep,
-                  ((bfd_arch_bits_per_address (stdoutput) == 32
-                    || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                   ? "addiu" : "daddiu"),
+                  HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
                   "t,r,j", reg, GP, (int) BFD_RELOC_MIPS_GPREL);
     }
   else
     abort ();
 }
 
+/* Move the contents of register SOURCE into register DEST.  */
+
+static void
+move_register (counter, dest, source)
+     int *counter;
+     int dest;
+     int source;
+{
+  macro_build ((char *) NULL, counter, (expressionS *) NULL,
+              HAVE_32BIT_GPRS ? "addu" : "daddu",
+              "d,v,t", dest, source, 0);
+}
+
 /*
  *                     Build macros
  *   This routine implements the seemingly endless macro or synthesized
@@ -3531,12 +3834,12 @@ macro (ip)
       expr1.X_add_number = 8;
       macro_build ((char *) NULL, &icnt, &expr1, "bgez", "s,p", sreg);
       if (dreg == sreg)
-       macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "",
+                    0);
       else
-       macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", dreg, sreg, 0);
-      macro_build ((char *) NULL, &icnt, NULL,
-                  dbl ? "dsub" : "sub",
-                  "d,v,t", dreg, 0, sreg);
+       move_register (&icnt, dreg, sreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                  dbl ? "dsub" : "sub", "d,v,t", dreg, 0, sreg);
 
       --mips_opts.noreorder;
       return;
@@ -3568,7 +3871,8 @@ macro (ip)
          return;
        }
       load_register (&icnt, AT, &imm_expr, dbl);
-      macro_build ((char *) NULL, &icnt, NULL, s2, "d,v,t", treg, sreg, AT);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s2, "d,v,t",
+                  treg, sreg, AT);
       break;
 
     case M_AND_I:
@@ -3598,14 +3902,15 @@ macro (ip)
            {
              macro_build ((char *) NULL, &icnt, &imm_expr, "ori", "t,r,i",
                           treg, sreg, (int) BFD_RELOC_LO16);
-             macro_build ((char *) NULL, &icnt, NULL, "nor", "d,v,t",
-                          treg, treg, 0);
+             macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nor",
+                          "d,v,t", treg, treg, 0);
            }
          return;
        }
 
-      load_register (&icnt, AT, &imm_expr, 0);
-      macro_build ((char *) NULL, &icnt, NULL, s2, "d,v,t", treg, sreg, AT);
+      load_register (&icnt, AT, &imm_expr, HAVE_64BIT_GPRS);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s2, "d,v,t",
+                  treg, sreg, AT);
       break;
 
     case M_BEQ_I:
@@ -3638,21 +3943,19 @@ macro (ip)
       if (treg == 0)
        {
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bgezl" : "bgez",
-                      "s,p", sreg);
+                      likely ? "bgezl" : "bgez", "s,p", sreg);
          return;
        }
       if (sreg == 0)
        {
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "blezl" : "blez",
-                      "s,p", treg);
+                      likely ? "blezl" : "blez", "s,p", treg);
          return;
        }
-      macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", AT, sreg, treg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "slt", "d,v,t",
+                  AT, sreg, treg);
       macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "beql" : "beq",
-                  "s,t,p", AT, 0);
+                  likely ? "beql" : "beq", "s,t,p", AT, 0);
       break;
 
     case M_BGTL_I:
@@ -3660,7 +3963,7 @@ macro (ip)
     case M_BGT_I:
       /* check for > max integer */
       maxnum = 0x7fffffff;
-      if (ISA_HAS_64BIT_REGS (mips_opts.isa) && sizeof (maxnum) > 4)
+      if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
        {
          maxnum <<= 16;
          maxnum |= 0xffff;
@@ -3669,18 +3972,23 @@ macro (ip)
        }
       if (imm_expr.X_op == O_constant
          && imm_expr.X_add_number >= maxnum
-         && (! ISA_HAS_64BIT_REGS (mips_opts.isa) || sizeof (maxnum) > 4))
+         && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4))
        {
        do_false:
          /* result is always false */
          if (! likely)
            {
-             as_warn (_("Branch %s is always false (nop)"), ip->insn_mo->name);
-             macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
+             if (warn_nops)
+               as_warn (_("Branch %s is always false (nop)"),
+                        ip->insn_mo->name);
+             macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop",
+                          "", 0);
            }
          else
            {
-             as_warn (_("Branch likely %s is always false"), ip->insn_mo->name);
+             if (warn_nops)
+               as_warn (_("Branch likely %s is always false"),
+                        ip->insn_mo->name);
              macro_build ((char *) NULL, &icnt, &offset_expr, "bnel",
                           "s,t,p", 0, 0);
            }
@@ -3697,19 +4005,17 @@ macro (ip)
       if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
        {
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bgezl" : "bgez",
-                      "s,p", sreg);
+                      likely ? "bgezl" : "bgez", "s,p", sreg);
          return;
        }
       if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
        {
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bgtzl" : "bgtz",
-                      "s,p", sreg);
+                      likely ? "bgtzl" : "bgtz", "s,p", sreg);
          return;
        }
       maxnum = 0x7fffffff;
-      if (ISA_HAS_64BIT_REGS (mips_opts.isa) && sizeof (maxnum) > 4)
+      if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
        {
          maxnum <<= 16;
          maxnum |= 0xffff;
@@ -3719,7 +4025,7 @@ macro (ip)
       maxnum = - maxnum - 1;
       if (imm_expr.X_op == O_constant
          && imm_expr.X_add_number <= maxnum
-         && (! ISA_HAS_64BIT_REGS (mips_opts.isa) || sizeof (maxnum) > 4))
+         && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4))
        {
        do_true:
          /* result is always true */
@@ -3729,8 +4035,7 @@ macro (ip)
        }
       set_at (&icnt, sreg, 0);
       macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "beql" : "beq",
-                  "s,t,p", AT, 0);
+                  likely ? "beql" : "beq", "s,t,p", AT, 0);
       break;
 
     case M_BGEUL:
@@ -3741,22 +4046,20 @@ macro (ip)
       if (sreg == 0)
        {
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "beql" : "beq",
-                      "s,t,p", 0, treg);
+                      likely ? "beql" : "beq", "s,t,p", 0, treg);
          return;
        }
-      macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", AT, sreg,
-                  treg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sltu",
+                  "d,v,t", AT, sreg, treg);
       macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "beql" : "beq",
-                  "s,t,p", AT, 0);
+                  likely ? "beql" : "beq", "s,t,p", AT, 0);
       break;
 
     case M_BGTUL_I:
       likely = 1;
     case M_BGTU_I:
       if (sreg == 0
-         || (! ISA_HAS_64BIT_REGS (mips_opts.isa)
+         || (HAVE_32BIT_GPRS
              && imm_expr.X_op == O_constant
              && imm_expr.X_add_number == 0xffffffff))
        goto do_false;
@@ -3773,14 +4076,12 @@ macro (ip)
       if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
        {
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bnel" : "bne",
-                      "s,t,p", sreg, 0);
+                      likely ? "bnel" : "bne", "s,t,p", sreg, 0);
          return;
        }
       set_at (&icnt, sreg, 1);
       macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "beql" : "beq",
-                  "s,t,p", AT, 0);
+                  likely ? "beql" : "beq", "s,t,p", AT, 0);
       break;
 
     case M_BGTL:
@@ -3789,21 +4090,19 @@ macro (ip)
       if (treg == 0)
        {
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bgtzl" : "bgtz",
-                      "s,p", sreg);
+                      likely ? "bgtzl" : "bgtz", "s,p", sreg);
          return;
        }
       if (sreg == 0)
        {
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bltzl" : "bltz",
-                      "s,p", treg);
+                      likely ? "bltzl" : "bltz", "s,p", treg);
          return;
        }
-      macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", AT, treg, sreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "slt", "d,v,t",
+                  AT, treg, sreg);
       macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "bnel" : "bne",
-                  "s,t,p", AT, 0);
+                  likely ? "bnel" : "bne", "s,t,p", AT, 0);
       break;
 
     case M_BGTUL:
@@ -3812,17 +4111,15 @@ macro (ip)
       if (treg == 0)
        {
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bnel" : "bne",
-                      "s,t,p", sreg, 0);
+                      likely ? "bnel" : "bne", "s,t,p", sreg, 0);
          return;
        }
       if (sreg == 0)
        goto do_false;
-      macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", AT, treg,
-                  sreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sltu",
+                  "d,v,t", AT, treg, sreg);
       macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "bnel" : "bne",
-                  "s,t,p", AT, 0);
+                  likely ? "bnel" : "bne", "s,t,p", AT, 0);
       break;
 
     case M_BLEL:
@@ -3831,28 +4128,26 @@ macro (ip)
       if (treg == 0)
        {
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "blezl" : "blez",
-                      "s,p", sreg);
+                      likely ? "blezl" : "blez", "s,p", sreg);
          return;
        }
       if (sreg == 0)
        {
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bgezl" : "bgez",
-                      "s,p", treg);
+                      likely ? "bgezl" : "bgez", "s,p", treg);
          return;
        }
-      macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", AT, treg, sreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "slt", "d,v,t",
+                  AT, treg, sreg);
       macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "beql" : "beq",
-                  "s,t,p", AT, 0);
+                  likely ? "beql" : "beq", "s,t,p", AT, 0);
       break;
 
     case M_BLEL_I:
       likely = 1;
     case M_BLE_I:
       maxnum = 0x7fffffff;
-      if (ISA_HAS_64BIT_REGS (mips_opts.isa) && sizeof (maxnum) > 4)
+      if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
        {
          maxnum <<= 16;
          maxnum |= 0xffff;
@@ -3861,7 +4156,7 @@ macro (ip)
        }
       if (imm_expr.X_op == O_constant
          && imm_expr.X_add_number >= maxnum
-         && (! ISA_HAS_64BIT_REGS (mips_opts.isa) || sizeof (maxnum) > 4))
+         && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4))
        goto do_true;
       if (imm_expr.X_op != O_constant)
        as_bad (_("Unsupported large constant"));
@@ -3874,21 +4169,18 @@ macro (ip)
       if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
        {
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bltzl" : "bltz",
-                      "s,p", sreg);
+                      likely ? "bltzl" : "bltz", "s,p", sreg);
          return;
        }
       if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
        {
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "blezl" : "blez",
-                      "s,p", sreg);
+                      likely ? "blezl" : "blez", "s,p", sreg);
          return;
        }
       set_at (&icnt, sreg, 0);
       macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "bnel" : "bne",
-                  "s,t,p", AT, 0);
+                  likely ? "bnel" : "bne", "s,t,p", AT, 0);
       break;
 
     case M_BLEUL:
@@ -3897,24 +4189,22 @@ macro (ip)
       if (treg == 0)
        {
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "beql" : "beq",
-                      "s,t,p", sreg, 0);
+                      likely ? "beql" : "beq", "s,t,p", sreg, 0);
          return;
        }
       if (sreg == 0)
        goto do_true;
-      macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", AT, treg,
-                  sreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sltu",
+                  "d,v,t", AT, treg, sreg);
       macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "beql" : "beq",
-                  "s,t,p", AT, 0);
+                  likely ? "beql" : "beq", "s,t,p", AT, 0);
       break;
 
     case M_BLEUL_I:
       likely = 1;
     case M_BLEU_I:
       if (sreg == 0
-         || (! ISA_HAS_64BIT_REGS (mips_opts.isa)
+         || (HAVE_32BIT_GPRS
              && imm_expr.X_op == O_constant
              && imm_expr.X_add_number == 0xffffffff))
        goto do_true;
@@ -3937,8 +4227,7 @@ macro (ip)
        }
       set_at (&icnt, sreg, 1);
       macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "bnel" : "bne",
-                  "s,t,p", AT, 0);
+                  likely ? "bnel" : "bne", "s,t,p", AT, 0);
       break;
 
     case M_BLTL:
@@ -3947,21 +4236,19 @@ macro (ip)
       if (treg == 0)
        {
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bltzl" : "bltz",
-                      "s,p", sreg);
+                      likely ? "bltzl" : "bltz", "s,p", sreg);
          return;
        }
       if (sreg == 0)
        {
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bgtzl" : "bgtz",
-                      "s,p", treg);
+                      likely ? "bgtzl" : "bgtz", "s,p", treg);
          return;
        }
-      macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", AT, sreg, treg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "slt", "d,v,t",
+                  AT, sreg, treg);
       macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "bnel" : "bne",
-                  "s,t,p", AT, 0);
+                  likely ? "bnel" : "bne", "s,t,p", AT, 0);
       break;
 
     case M_BLTUL:
@@ -3972,15 +4259,14 @@ macro (ip)
       if (sreg == 0)
        {
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      likely ? "bnel" : "bne",
-                      "s,t,p", 0, treg);
+                      likely ? "bnel" : "bne", "s,t,p", 0, treg);
          return;
        }
-      macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", AT, sreg,
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sltu",
+                  "d,v,t", AT, sreg,
                   treg);
       macro_build ((char *) NULL, &icnt, &offset_expr,
-                  likely ? "bnel" : "bne",
-                  "s,t,p", AT, 0);
+                  likely ? "bnel" : "bne", "s,t,p", AT, 0);
       break;
 
     case M_DDIV_3:
@@ -3997,9 +4283,11 @@ macro (ip)
        {
          as_warn (_("Divide by zero."));
          if (mips_trap)
-           macro_build ((char *) NULL, &icnt, NULL, "teq", "s,t", 0, 0);
+           macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "teq",
+                        "s,t", 0, 0);
          else
-             macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7);
+           macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "break",
+                        "c", 7);
          return;
        }
 
@@ -4008,19 +4296,19 @@ macro (ip)
       mips_any_noreorder = 1;
       if (mips_trap)
        {
-         macro_build ((char *) NULL, &icnt, NULL, "teq", "s,t", treg, 0);
-         macro_build ((char *) NULL, &icnt, NULL,
-                      dbl ? "ddiv" : "div",
-                      "z,s,t", sreg, treg);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "teq",
+                      "s,t", treg, 0);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                      dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
        }
       else
        {
          expr1.X_add_number = 8;
          macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", treg, 0);
-         macro_build ((char *) NULL, &icnt, NULL,
-                      dbl ? "ddiv" : "div",
-                      "z,s,t", sreg, treg);
-           macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                      dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "break",
+                      "c", 7);
        }
       expr1.X_add_number = -1;
       macro_build ((char *) NULL, &icnt, &expr1,
@@ -4033,8 +4321,8 @@ macro (ip)
          expr1.X_add_number = 1;
          macro_build ((char *) NULL, &icnt, &expr1, "daddiu", "t,r,j", AT, 0,
                       (int) BFD_RELOC_LO16);
-         macro_build ((char *) NULL, &icnt, NULL, "dsll32", "d,w,<", AT, AT,
-                      31);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "dsll32",
+                      "d,w,<", AT, AT, 31);
        }
       else
        {
@@ -4044,7 +4332,8 @@ macro (ip)
        }
       if (mips_trap)
        {
-         macro_build ((char *) NULL, &icnt, NULL, "teq", "s,t", sreg, AT);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "teq",
+                      "s,t", sreg, AT);
          /* We want to close the noreorder block as soon as possible, so
             that later insns are available for delay slot filling.  */
          --mips_opts.noreorder;
@@ -4053,15 +4342,17 @@ macro (ip)
        {
          expr1.X_add_number = 8;
          macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", sreg, AT);
-         macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "",
+                      0);
 
          /* We want to close the noreorder block as soon as possible, so
             that later insns are available for delay slot filling.  */
          --mips_opts.noreorder;
 
-           macro_build ((char *) NULL, &icnt, NULL, "break", "c", 6);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "break",
+                      "c", 6);
        }
-      macro_build ((char *) NULL, &icnt, NULL, s, "d", dreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "d", dreg);
       break;
 
     case M_DIV_3I:
@@ -4104,18 +4395,19 @@ macro (ip)
        {
          as_warn (_("Divide by zero."));
          if (mips_trap)
-           macro_build ((char *) NULL, &icnt, NULL, "teq", "s,t", 0, 0);
+           macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "teq",
+                        "s,t", 0, 0);
          else
-             macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7);
+           macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "break",
+                        "c", 7);
          return;
        }
       if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
        {
          if (strcmp (s2, "mflo") == 0)
-           macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", dreg,
-                        sreg);
+           move_register (&icnt, dreg, sreg);
          else
-           macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", dreg, 0);
+           move_register (&icnt, dreg, 0);
          return;
        }
       if (imm_expr.X_op == O_constant
@@ -4124,21 +4416,18 @@ macro (ip)
        {
          if (strcmp (s2, "mflo") == 0)
            {
-             if (dbl)
-               macro_build ((char *) NULL, &icnt, NULL, "dneg", "d,w", dreg,
-                            sreg);
-             else
-               macro_build ((char *) NULL, &icnt, NULL, "neg", "d,w", dreg,
-                            sreg);
+             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                          dbl ? "dneg" : "neg", "d,w", dreg, sreg);
            }
          else
-           macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", dreg, 0);
+           move_register (&icnt, dreg, 0);
          return;
        }
 
       load_register (&icnt, AT, &imm_expr, dbl);
-      macro_build ((char *) NULL, &icnt, NULL, s, "z,s,t", sreg, AT);
-      macro_build ((char *) NULL, &icnt, NULL, s2, "d", dreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "z,s,t",
+                  sreg, AT);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s2, "d", dreg);
       break;
 
     case M_DIVU_3:
@@ -4162,8 +4451,10 @@ macro (ip)
       mips_any_noreorder = 1;
       if (mips_trap)
        {
-         macro_build ((char *) NULL, &icnt, NULL, "teq", "s,t", treg, 0);
-         macro_build ((char *) NULL, &icnt, NULL, s, "z,s,t", sreg, treg);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "teq",
+                      "s,t", treg, 0);
+         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
             that later insns are available for delay slot filling.  */
          --mips_opts.noreorder;
@@ -4172,14 +4463,16 @@ macro (ip)
        {
          expr1.X_add_number = 8;
          macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", treg, 0);
-         macro_build ((char *) NULL, &icnt, NULL, s, "z,s,t", sreg, treg);
+         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
             that later insns are available for delay slot filling.  */
          --mips_opts.noreorder;
-           macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "break",
+                      "c", 7);
        }
-      macro_build ((char *) NULL, &icnt, NULL, s2, "d", dreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s2, "d", dreg);
       return;
 
     case M_DLA_AB:
@@ -4188,9 +4481,21 @@ macro (ip)
       /* Load the address of a symbol into a register.  If breg is not
         zero, we then add a base register to it.  */
 
+      if (treg == breg)
+       {
+         tempreg = AT;
+         used_at = 1;
+       }
+      else
+       {
+         tempreg = treg;
+         used_at = 0;
+       }
+
       /* When generating embedded PIC code, we permit expressions of
         the form
-          la   $4,foo-bar
+          la   $treg,foo-bar
+          la   $treg,foo-bar($breg)
         where bar is an address in the current section.  These are used
         when getting the addresses of functions.  We don't permit
         X_add_number to be non-zero, because if the symbol is
@@ -4205,18 +4510,30 @@ macro (ip)
                     (symbol_get_value_expression (offset_expr.X_op_symbol)
                      ->X_add_symbol)
                     == now_seg)))
-         && breg == 0
          && (offset_expr.X_add_number == 0
              || OUTPUT_FLAVOR == bfd_target_elf_flavour))
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
-                      treg, (int) BFD_RELOC_PCREL_HI16_S);
+         if (breg == 0)
+           {
+             tempreg = treg;
+             used_at = 0;
+             macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
+                          tempreg, (int) BFD_RELOC_PCREL_HI16_S);
+           }
+         else
+           {
+             macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
+                          tempreg, (int) BFD_RELOC_PCREL_HI16_S);
+             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
+                          "d,v,t", tempreg, tempreg, breg);
+           }
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "addiu" : "daddiu"),
-                      "t,r,j", treg, treg, (int) BFD_RELOC_PCREL_LO16);
-         return;
+                      HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
+                      "t,r,j", treg, tempreg, (int) BFD_RELOC_PCREL_LO16);
+         if (! used_at)
+           return;
+         break;
        }
 
       if (offset_expr.X_op != O_symbol
@@ -4226,59 +4543,105 @@ macro (ip)
          offset_expr.X_op = O_constant;
        }
 
-      if (treg == breg)
-       {
-         tempreg = AT;
-         used_at = 1;
-       }
-      else
-       {
-         tempreg = treg;
-         used_at = 0;
-       }
-
       if (offset_expr.X_op == O_constant)
        load_register (&icnt, tempreg, &offset_expr, dbl);
       else if (mips_pic == NO_PIC)
        {
-         /* If this is a reference to an GP relative symbol, we want
+         /* If this is a reference to a GP relative symbol, we want
               addiu    $tempreg,$gp,<sym>      (BFD_RELOC_MIPS_GPREL)
             Otherwise we want
               lui      $tempreg,<sym>          (BFD_RELOC_HI16_S)
               addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
             If we have a constant, we need two instructions anyhow,
-            so we may as well always use the latter form.  */
-         if ((valueT) offset_expr.X_add_number >= MAX_GPREL_OFFSET
-             || nopic_need_relax (offset_expr.X_add_symbol, 1))
-           p = NULL;
-         else
+            so we may as well always use the latter form.
+
+           With 64bit address space and a usable $at we want
+             lui       $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
+             lui       $at,<sym>               (BFD_RELOC_HI16_S)
+             daddiu    $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
+             daddiu    $at,<sym>               (BFD_RELOC_LO16)
+             dsll32    $tempreg,0
+             dadd      $tempreg,$tempreg,$at
+
+           If $at is already in use, we use an path which is suboptimal
+           on superscalar processors.
+             lui       $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
+             daddiu    $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
+             dsll      $tempreg,16
+             daddiu    $tempreg,<sym>          (BFD_RELOC_HI16_S)
+             dsll      $tempreg,16
+             daddiu    $tempreg,<sym>          (BFD_RELOC_LO16)
+         */
+         p = NULL;
+         if (HAVE_64BIT_ADDRESSES)
            {
-             frag_grow (20);
-             macro_build ((char *) NULL, &icnt, &offset_expr,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "addiu" : "daddiu"),
-                          "t,r,j", tempreg, GP, (int) BFD_RELOC_MIPS_GPREL);
-             p = frag_var (rs_machine_dependent, 8, 0,
-                           RELAX_ENCODE (4, 8, 0, 4, 0,
-                                         mips_opts.warn_about_macros),
-                           offset_expr.X_add_symbol, (offsetT) 0,
-                           (char *) NULL);
-           }
-         macro_build_lui (p, &icnt, &offset_expr, tempreg);
-         if (p != NULL)
-           p += 4;
-         macro_build (p, &icnt, &offset_expr,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "addiu" : "daddiu"),
-                      "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
+             /* We don't do GP optimization for now because RELAX_ENCODE can't
+                hold the data for such large chunks.  */
+
+           if (used_at == 0)
+             {
+               macro_build (p, &icnt, &offset_expr, "lui", "t,u",
+                            tempreg, (int) BFD_RELOC_MIPS_HIGHEST);
+               macro_build (p, &icnt, &offset_expr, "lui", "t,u",
+                            AT, (int) BFD_RELOC_HI16_S);
+               macro_build (p, &icnt, &offset_expr, "daddiu", "t,r,j",
+                            tempreg, tempreg, (int) BFD_RELOC_MIPS_HIGHER);
+               macro_build (p, &icnt, &offset_expr, "daddiu", "t,r,j",
+                            AT, AT, (int) BFD_RELOC_LO16);
+               macro_build (p, &icnt, (expressionS *) NULL, "dsll32",
+                            "d,w,<", tempreg, tempreg, 0);
+               macro_build (p, &icnt, (expressionS *) NULL, "dadd", "d,v,t",
+                            tempreg, tempreg, AT);
+               used_at = 1;
+             }
+           else
+             {
+               macro_build (p, &icnt, &offset_expr, "lui", "t,u",
+                            tempreg, (int) BFD_RELOC_MIPS_HIGHEST);
+               macro_build (p, &icnt, &offset_expr, "daddiu", "t,r,j",
+                            tempreg, tempreg, (int) BFD_RELOC_MIPS_HIGHER);
+               macro_build (p, &icnt, (expressionS *) NULL, "dsll", "d,w,<",
+                            tempreg, tempreg, 16);
+               macro_build (p, &icnt, &offset_expr, "daddiu", "t,r,j",
+                            tempreg, tempreg, (int) BFD_RELOC_HI16_S);
+               macro_build (p, &icnt, (expressionS *) NULL, "dsll", "d,w,<",
+                            tempreg, tempreg, 16);
+               macro_build (p, &icnt, &offset_expr, "daddiu", "t,r,j",
+                            tempreg, tempreg, (int) BFD_RELOC_LO16);
+             }
+         }
+       else
+         {
+           if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+               && ! nopic_need_relax (offset_expr.X_add_symbol, 1))
+             {
+               frag_grow (20);
+               macro_build ((char *) NULL, &icnt, &offset_expr,
+                            HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
+                            "t,r,j", tempreg, GP, (int) BFD_RELOC_MIPS_GPREL);
+               p = frag_var (rs_machine_dependent, 8, 0,
+                             RELAX_ENCODE (4, 8, 0, 4, 0,
+                                           mips_opts.warn_about_macros),
+                             offset_expr.X_add_symbol, (offsetT) 0,
+                             (char *) NULL);
+             }
+           macro_build_lui (p, &icnt, &offset_expr, tempreg);
+           if (p != NULL)
+             p += 4;
+           macro_build (p, &icnt, &offset_expr,
+                        HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
+                        "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
+         }
        }
       else if (mips_pic == SVR4_PIC && ! mips_big_got)
        {
+         int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
+
          /* If this is a reference to an external symbol, and there
             is no constant, we want
               lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+            or if tempreg is PIC_CALL_REG
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_CALL16)
             For a local symbol, we want
               lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
               nop
@@ -4305,9 +4668,10 @@ macro (ip)
          expr1.X_add_number = offset_expr.X_add_number;
          offset_expr.X_add_number = 0;
          frag_grow (32);
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      dbl ? "ld" : "lw",
-                      "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP);
+         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, dbl ? "ld" : "lw",
+                      "t,o(b)", tempreg, lw_reloc_type, GP);
          if (expr1.X_add_number == 0)
            {
              int off;
@@ -4328,17 +4692,14 @@ macro (ip)
                                          (breg == 0
                                           ? mips_opts.warn_about_macros
                                           : 0)),
-                           offset_expr.X_add_symbol, (offsetT) 0,
-                           (char *) NULL);
+                           offset_expr.X_add_symbol, 0, NULL);
              if (breg == 0)
                {
                  macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
                  p += 4;
                }
              macro_build (p, &icnt, &expr1,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "addiu" : "daddiu"),
+                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
                           "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
@@ -4350,14 +4711,11 @@ macro (ip)
              macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                           "nop", "");
              macro_build ((char *) NULL, &icnt, &expr1,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "addiu" : "daddiu"),
+                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
                           "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
-             (void) frag_var (rs_machine_dependent, 0, 0,
-                              RELAX_ENCODE (0, 0, -12, -4, 0, 0),
-                              offset_expr.X_add_symbol, (offsetT) 0,
-                              (char *) NULL);
+             frag_var (rs_machine_dependent, 0, 0,
+                       RELAX_ENCODE (0, 0, -12, -4, 0, 0),
+                       offset_expr.X_add_symbol, 0, NULL);
            }
          else
            {
@@ -4377,9 +4735,7 @@ macro (ip)
                  macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                               "nop", "");
                  macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              ((bfd_arch_bits_per_address (stdoutput) == 32
-                                || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                               ? "addu" : "daddu"),
+                              HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                               "d,v,t", treg, AT, breg);
                  breg = 0;
                  tempreg = treg;
@@ -4390,35 +4746,36 @@ macro (ip)
                 inserting an unnecessary nop after the lw.  */
              hold_mips_optimize = mips_optimize;
              mips_optimize = 2;
-             macro_build_lui ((char *) NULL, &icnt, &expr1, AT);
+             macro_build_lui (NULL, &icnt, &expr1, AT);
              mips_optimize = hold_mips_optimize;
 
              macro_build ((char *) NULL, &icnt, &expr1,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "addiu" : "daddiu"),
+                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
                           "t,r,j", AT, AT, (int) BFD_RELOC_LO16);
              macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "addu" : "daddu"),
+                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                           "d,v,t", tempreg, tempreg, AT);
-             (void) frag_var (rs_machine_dependent, 0, 0,
-                              RELAX_ENCODE (0, 0, -16 + off1, -8, 0, 0),
-                              offset_expr.X_add_symbol, (offsetT) 0,
-                              (char *) NULL);
+             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)
        {
          int gpdel;
+         int lui_reloc_type = (int) BFD_RELOC_MIPS_GOT_HI16;
+         int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT_LO16;
 
          /* This is the large GOT case.  If this is a reference to an
             external symbol, and there is no constant, we want
               lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
               addu     $tempreg,$tempreg,$gp
               lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+            or if tempreg is PIC_CALL_REG
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_CALL_HI16)
+              addu     $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_CALL_LO16)
             For a local symbol, we want
               lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
               nop
@@ -4457,17 +4814,19 @@ macro (ip)
            gpdel = 4;
          else
            gpdel = 0;
+         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, (int) BFD_RELOC_MIPS_GOT_HI16);
+                      tempreg, lui_reloc_type);
          macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "addu" : "daddu"),
+                      HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                       "d,v,t", tempreg, tempreg, GP);
          macro_build ((char *) NULL, &icnt, &offset_expr,
                       dbl ? "ld" : "lw",
-                      "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT_LO16,
-                      tempreg);
+                      "t,o(b)", tempreg, lw_reloc_type, tempreg);
          if (expr1.X_add_number == 0)
            {
              int off;
@@ -4490,8 +4849,7 @@ macro (ip)
                                          (breg == 0
                                           ? mips_opts.warn_about_macros
                                           : 0)),
-                           offset_expr.X_add_symbol, (offsetT) 0,
-                           (char *) NULL);
+                           offset_expr.X_add_symbol, 0, NULL);
            }
          else if (expr1.X_add_number >= -0x8000
                   && expr1.X_add_number < 0x8000)
@@ -4499,9 +4857,7 @@ macro (ip)
              macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                           "nop", "");
              macro_build ((char *) NULL, &icnt, &expr1,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "addiu" : "daddiu"),
+                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
                           "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
 
              p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
@@ -4509,8 +4865,7 @@ macro (ip)
                                          (breg == 0
                                           ? mips_opts.warn_about_macros
                                           : 0)),
-                           offset_expr.X_add_symbol, (offsetT) 0,
-                           (char *) NULL);
+                           offset_expr.X_add_symbol, 0, NULL);
            }
          else
            {
@@ -4534,9 +4889,7 @@ macro (ip)
                  macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                               "nop", "");
                  macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              ((bfd_arch_bits_per_address (stdoutput) == 32
-                                || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                               ? "addu" : "daddu"),
+                              HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                               "d,v,t", treg, AT, breg);
                  dreg = treg;
                  adj = 8;
@@ -4546,18 +4899,14 @@ macro (ip)
                 inserting an unnecessary nop after the lw.  */
              hold_mips_optimize = mips_optimize;
              mips_optimize = 2;
-             macro_build_lui ((char *) NULL, &icnt, &expr1, AT);
+             macro_build_lui (NULL, &icnt, &expr1, AT);
              mips_optimize = hold_mips_optimize;
 
              macro_build ((char *) NULL, &icnt, &expr1,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "addiu" : "daddiu"),
+                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
                           "t,r,j", AT, AT, (int) BFD_RELOC_LO16);
              macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "addu" : "daddu"),
+                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                           "d,v,t", dreg, dreg, AT);
 
              p = frag_var (rs_machine_dependent, 16 + gpdel + adj, 0,
@@ -4566,8 +4915,7 @@ macro (ip)
                                          (breg == 0
                                           ? mips_opts.warn_about_macros
                                           : 0)),
-                           offset_expr.X_add_symbol, (offsetT) 0,
-                           (char *) NULL);
+                           offset_expr.X_add_symbol, 0, NULL);
 
              used_at = 1;
            }
@@ -4589,9 +4937,7 @@ macro (ip)
              macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
              p += 4;
              macro_build (p, &icnt, &expr1,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "addiu" : "daddiu"),
+                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
                           "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,
@@ -4609,9 +4955,7 @@ macro (ip)
                  macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
                  p += 4;
                  macro_build (p, &icnt, (expressionS *) NULL,
-                              ((bfd_arch_bits_per_address (stdoutput) == 32
-                                || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                               ? "addu" : "daddu"),
+                              HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                               "d,v,t", treg, AT, breg);
                  p += 4;
                  tempreg = treg;
@@ -4623,15 +4967,11 @@ macro (ip)
              macro_build_lui (p, &icnt, &expr1, AT);
              p += 4;
              macro_build (p, &icnt, &expr1,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "addiu" : "daddiu"),
+                          HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
                           "t,r,j", AT, AT, (int) BFD_RELOC_LO16);
              p += 4;
              macro_build (p, &icnt, (expressionS *) NULL,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "addu" : "daddu"),
+                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                           "d,v,t", tempreg, tempreg, AT);
              p += 4;
            }
@@ -4642,9 +4982,7 @@ macro (ip)
               addiu    $tempreg,$gp,<sym>      (BFD_RELOC_MIPS_GPREL)
             */
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "addiu" : "daddiu"),
+                      HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
                       "t,r,j", tempreg, GP, (int) BFD_RELOC_MIPS_GPREL);
        }
       else
@@ -4652,9 +4990,7 @@ macro (ip)
 
       if (breg != 0)
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                    ((bfd_arch_bits_per_address (stdoutput) == 32
-                      || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                     ? "addu" : "daddu"),
+                    HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                     "d,v,t", treg, tempreg, breg);
 
       if (! used_at)
@@ -4690,16 +5026,17 @@ macro (ip)
 
          macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "jalr",
                       "d,s", dreg, sreg);
-         if (mips_cprestore_offset < 0)
-           as_warn (_("No .cprestore pseudo-op used in PIC code"));
-         else
+         if (! HAVE_NEWABI)
            {
-             expr1.X_add_number = mips_cprestore_offset;
-             macro_build ((char *) NULL, &icnt, &expr1,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "lw" : "ld"),
-                          "t,o(b)", GP, (int) BFD_RELOC_LO16, mips_frame_reg);
+             if (mips_cprestore_offset < 0)
+               as_warn (_("No .cprestore pseudo-op used in PIC code"));
+             else
+               {
+                 expr1.X_add_number = mips_cprestore_offset;
+                 macro_build ((char *) NULL, &icnt, &expr1,
+                              HAVE_32BIT_ADDRESSES ? "lw" : "ld", "t,o(b)",
+                              GP, (int) BFD_RELOC_LO16, mips_frame_reg);
+               }
            }
        }
       else
@@ -4739,17 +5076,14 @@ macro (ip)
          if (! mips_big_got)
            {
              macro_build ((char *) NULL, &icnt, &offset_expr,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "lw" : "ld"),
+                          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),
-                           offset_expr.X_add_symbol, (offsetT) 0,
-                           (char *) NULL);
+                           offset_expr.X_add_symbol, 0, NULL);
            }
          else
            {
@@ -4762,14 +5096,10 @@ macro (ip)
              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,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "addu" : "daddu"),
+                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                           "d,v,t", PIC_CALL_REG, PIC_CALL_REG, GP);
              macro_build ((char *) NULL, &icnt, &offset_expr,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "lw" : "ld"),
+                          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,
@@ -4777,17 +5107,14 @@ macro (ip)
              p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
                            RELAX_ENCODE (16, 12 + gpdel, gpdel, 8 + gpdel,
                                          0, 0),
-                           offset_expr.X_add_symbol, (offsetT) 0,
-                           (char *) NULL);
+                           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,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "lw" : "ld"),
+                          HAVE_32BIT_ADDRESSES ? "lw" : "ld",
                           "t,o(b)", PIC_CALL_REG,
                           (int) BFD_RELOC_MIPS_GOT16, GP);
              p += 4;
@@ -4795,27 +5122,25 @@ macro (ip)
              p += 4;
            }
          macro_build (p, &icnt, &offset_expr,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "addiu" : "daddiu"),
+                      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 (mips_cprestore_offset < 0)
-           as_warn (_("No .cprestore pseudo-op used in PIC code"));
-         else
+         if (! HAVE_NEWABI)
            {
-             if (mips_opts.noreorder)
-               macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+             if (mips_cprestore_offset < 0)
+               as_warn (_("No .cprestore pseudo-op used in PIC code"));
+             else
+               {
+                 if (mips_opts.noreorder)
+                   macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                             "nop", "");
-             expr1.X_add_number = mips_cprestore_offset;
-             macro_build ((char *) NULL, &icnt, &expr1,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "lw" : "ld"),
-                          "t,o(b)", GP, (int) BFD_RELOC_LO16,
-                          mips_frame_reg);
+                 expr1.X_add_number = mips_cprestore_offset;
+                 macro_build ((char *) NULL, &icnt, &expr1,
+                              HAVE_32BIT_ADDRESSES ? "lw" : "ld", "t,o(b)",
+                              GP, (int) BFD_RELOC_LO16, mips_frame_reg);
+               }
            }
        }
       else if (mips_pic == EMBEDDED_PIC)
@@ -4874,7 +5199,7 @@ macro (ip)
       lr = 1;
       goto ld;
     case M_LDC1_AB:
-      if (mips_cpu == CPU_R4650)
+      if (mips_arch == CPU_R4650)
        {
          as_bad (_("opcode not supported on this processor"));
          return;
@@ -4963,7 +5288,7 @@ macro (ip)
       s = "scd";
       goto st;
     case M_SDC1_AB:
-      if (mips_cpu == CPU_R4650)
+      if (mips_arch == CPU_R4650)
        {
          as_bad (_("opcode not supported on this processor"));
          return;
@@ -5004,6 +5329,46 @@ macro (ip)
       else
        fmt = "t,o(b)";
 
+      /* For embedded PIC, we allow loads where the offset is calculated
+         by subtracting a symbol in the current segment from an unknown
+         symbol, relative to a base register, e.g.:
+               <op>    $treg, <sym>-<localsym>($breg)
+        This is used by the compiler for switch statements.  */
+      if (mips_pic == EMBEDDED_PIC
+          && offset_expr.X_op == O_subtract
+          && (symbol_constant_p (offset_expr.X_op_symbol)
+              ? S_GET_SEGMENT (offset_expr.X_op_symbol) == now_seg
+              : (symbol_equated_p (offset_expr.X_op_symbol)
+                 && (S_GET_SEGMENT
+                     (symbol_get_value_expression (offset_expr.X_op_symbol)
+                      ->X_add_symbol)
+                     == now_seg)))
+          && breg != 0
+          && (offset_expr.X_add_number == 0
+              || OUTPUT_FLAVOR == bfd_target_elf_flavour))
+        {
+          /* For this case, we output the instructions:
+                lui     $tempreg,<sym>          (BFD_RELOC_PCREL_HI16_S)
+                addiu   $tempreg,$tempreg,$breg
+                <op>    $treg,<sym>($tempreg)   (BFD_RELOC_PCREL_LO16)
+             If the relocation would fit entirely in 16 bits, it would be
+             nice to emit:
+                <op>    $treg,<sym>($breg)      (BFD_RELOC_PCREL_LO16)
+             instead, but that seems quite difficult.  */
+          macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
+                       tempreg, (int) BFD_RELOC_PCREL_HI16_S);
+          macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                       ((bfd_arch_bits_per_address (stdoutput) == 32
+                         || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
+                        ? "addu" : "daddu"),
+                       "d,v,t", tempreg, tempreg, breg);
+          macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt, treg,
+                       (int) BFD_RELOC_PCREL_LO16, tempreg);
+          if (! used_at)
+            return;
+          break;
+        }
+
       if (offset_expr.X_op != O_constant
          && offset_expr.X_op != O_symbol)
        {
@@ -5033,10 +5398,93 @@ macro (ip)
               lui      $tempreg,<sym>          (BFD_RELOC_HI16_S)
               addu     $tempreg,$tempreg,$breg
               <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
-            With a constant we always use the latter case.  */
+            With a constant we always use the latter case.
+
+            With 64bit address space and no base register and $at usable,
+            we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
+              lui      $at,<sym>               (BFD_RELOC_HI16_S)
+              daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
+              dsll32   $tempreg,0
+              daddu    $tempreg,$at
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+            If we have a base register, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
+              lui      $at,<sym>               (BFD_RELOC_HI16_S)
+              daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
+              daddu    $at,$breg
+              dsll32   $tempreg,0
+              daddu    $tempreg,$at
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+
+            Without $at we can't generate the optimal path for superscalar
+            processors here since this would require two temporary registers.
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
+              daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
+              dsll     $tempreg,16
+              daddiu   $tempreg,<sym>          (BFD_RELOC_HI16_S)
+              dsll     $tempreg,16
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+            If we have a base register, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHEST)
+              daddiu   $tempreg,<sym>          (BFD_RELOC_MIPS_HIGHER)
+              dsll     $tempreg,16
+              daddiu   $tempreg,<sym>          (BFD_RELOC_HI16_S)
+              dsll     $tempreg,16
+              daddu    $tempreg,$tempreg,$breg
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+          */
+         if (HAVE_64BIT_ADDRESSES)
+           {
+             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)
+               {
+                 macro_build (p, &icnt, &offset_expr, "lui", "t,u",
+                              tempreg, (int) BFD_RELOC_MIPS_HIGHEST);
+                 macro_build (p, &icnt, &offset_expr, "lui", "t,u",
+                              AT, (int) BFD_RELOC_HI16_S);
+                 macro_build (p, &icnt, &offset_expr, "daddiu", "t,r,j",
+                              tempreg, tempreg, (int) BFD_RELOC_MIPS_HIGHER);
+                 if (breg != 0)
+                   macro_build (p, &icnt, (expressionS *) NULL, "daddu",
+                                "d,v,t", AT, AT, breg);
+                 macro_build (p, &icnt, (expressionS *) NULL, "dsll32",
+                              "d,w,<", tempreg, tempreg, 0);
+                 macro_build (p, &icnt, (expressionS *) NULL, "daddu",
+                              "d,v,t", tempreg, tempreg, AT);
+                 macro_build (p, &icnt, &offset_expr, s,
+                              fmt, treg, (int) BFD_RELOC_LO16, tempreg);
+                 used_at = 1;
+               }
+             else
+               {
+                 macro_build (p, &icnt, &offset_expr, "lui", "t,u",
+                              tempreg, (int) BFD_RELOC_MIPS_HIGHEST);
+                 macro_build (p, &icnt, &offset_expr, "daddiu", "t,r,j",
+                              tempreg, tempreg, (int) BFD_RELOC_MIPS_HIGHER);
+                 macro_build (p, &icnt, (expressionS *) NULL, "dsll",
+                              "d,w,<", tempreg, tempreg, 16);
+                 macro_build (p, &icnt, &offset_expr, "daddiu", "t,r,j",
+                              tempreg, tempreg, (int) BFD_RELOC_HI16_S);
+                 macro_build (p, &icnt, (expressionS *) NULL, "dsll",
+                              "d,w,<", tempreg, tempreg, 16);
+                 if (breg != 0)
+                   macro_build (p, &icnt, (expressionS *) NULL, "daddu",
+                                "d,v,t", tempreg, tempreg, breg);
+                 macro_build (p, &icnt, &offset_expr, s,
+                              fmt, treg, (int) BFD_RELOC_LO16, tempreg);
+               }
+
+             return;
+           }
+
          if (breg == 0)
            {
-             if ((valueT) offset_expr.X_add_number >= MAX_GPREL_OFFSET
+             if ((valueT) offset_expr.X_add_number > MAX_GPREL_OFFSET
                  || nopic_need_relax (offset_expr.X_add_symbol, 1))
                p = NULL;
              else
@@ -5061,16 +5509,14 @@ macro (ip)
            }
          else
            {
-             if ((valueT) offset_expr.X_add_number >= MAX_GPREL_OFFSET
+             if ((valueT) offset_expr.X_add_number > MAX_GPREL_OFFSET
                  || nopic_need_relax (offset_expr.X_add_symbol, 1))
                p = NULL;
              else
                {
                  frag_grow (28);
                  macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              ((bfd_arch_bits_per_address (stdoutput) == 32
-                                || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                               ? "addu" : "daddu"),
+                              HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                               "d,v,t", tempreg, breg, GP);
                  macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
                               treg, (int) BFD_RELOC_MIPS_GPREL, tempreg);
@@ -5083,9 +5529,7 @@ macro (ip)
              if (p != NULL)
                p += 4;
              macro_build (p, &icnt, (expressionS *) NULL,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "addu" : "daddu"),
+                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                           "d,v,t", tempreg, tempreg, breg);
              if (p != NULL)
                p += 4;
@@ -5118,25 +5562,18 @@ macro (ip)
            as_bad (_("PIC code offset overflow (max 16 signed bits)"));
          frag_grow (20);
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "lw" : "ld"),
+                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
                       "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, 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),
-                       offset_expr.X_add_symbol, (offsetT) 0,
-                       (char *) NULL);
+                       offset_expr.X_add_symbol, 0, NULL);
          macro_build (p, &icnt, &offset_expr,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "addiu" : "daddiu"),
+                      HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
                       "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
          if (breg != 0)
            macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                        ((bfd_arch_bits_per_address (stdoutput) == 32
-                          || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                         ? "addu" : "daddu"),
+                        HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                         "d,v,t", tempreg, tempreg, breg);
          macro_build ((char *) NULL, &icnt, &expr1, s, fmt, treg,
                       (int) BFD_RELOC_LO16, tempreg);
@@ -5175,42 +5612,32 @@ 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,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "addu" : "daddu"),
+                      HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                       "d,v,t", tempreg, tempreg, GP);
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "lw" : "ld"),
+                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
                       "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT_LO16,
                       tempreg);
          p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
                        RELAX_ENCODE (12, 12 + gpdel, gpdel, 8 + gpdel, 0, 0),
-                       offset_expr.X_add_symbol, (offsetT) 0, (char *) NULL);
+                       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,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "lw" : "ld"),
+                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
                       "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP);
          p += 4;
          macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
          p += 4;
          macro_build (p, &icnt, &offset_expr,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "addiu" : "daddiu"),
+                      HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
                       "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
          if (breg != 0)
            macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                        ((bfd_arch_bits_per_address (stdoutput) == 32
-                          || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                         ? "addu" : "daddu"),
+                        HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                         "d,v,t", tempreg, tempreg, breg);
          macro_build ((char *) NULL, &icnt, &expr1, s, fmt, treg,
                       (int) BFD_RELOC_LO16, tempreg);
@@ -5233,9 +5660,7 @@ macro (ip)
          else
            {
              macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "addu" : "daddu"),
+                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                           "d,v,t", tempreg, breg, GP);
              macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
                           treg, (int) BFD_RELOC_MIPS_GPREL, tempreg);
@@ -5279,13 +5704,13 @@ macro (ip)
        }
 
     case M_LI_D:
-      /* If we have a constant in IMM_EXPR, then in mips3 mode it is
-         the entire value, and in mips1 mode it is the high order 32
-         bits of the value and the low order 32 bits are either zero
-         or in offset_expr.  */
+      /* Check if we have a constant in IMM_EXPR.  If the GPRs are 64 bits
+         wide, IMM_EXPR is the entire value.  Otherwise IMM_EXPR is the high
+         order 32 bits of the value and the low order 32 bits are either
+         zero or in OFFSET_EXPR.  */
       if (imm_expr.X_op == O_constant || imm_expr.X_op == O_big)
        {
-         if (ISA_HAS_64BIT_REGS (mips_opts.isa))
+         if (HAVE_64BIT_GPRS)
            load_register (&icnt, treg, &imm_expr, 1);
          else
            {
@@ -5307,8 +5732,7 @@ macro (ip)
              if (lreg <= 31)
                {
                  if (offset_expr.X_op == O_absent)
-                   macro_build ((char *) NULL, &icnt, NULL, "move", "d,s",
-                                lreg, 0);
+                   move_register (&icnt, lreg, 0);
                  else
                    {
                      assert (offset_expr.X_op == O_constant);
@@ -5329,9 +5753,7 @@ macro (ip)
       else if (mips_pic == SVR4_PIC)
        {
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "lw" : "ld"),
+                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
                       "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16, GP);
        }
       else if (mips_pic == EMBEDDED_PIC)
@@ -5339,9 +5761,7 @@ macro (ip)
          /* For embedded PIC we pick up the entire address off $gp in
             a single instruction.  */
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "addiu" : "daddiu"),
+                      HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu",
                       "t,r,j", AT, GP, (int) BFD_RELOC_MIPS_GPREL);
          offset_expr.X_op = O_constant;
          offset_expr.X_add_number = 0;
@@ -5350,7 +5770,7 @@ macro (ip)
        abort ();
 
       /* Now we load the register(s).  */
-      if (ISA_HAS_64BIT_REGS (mips_opts.isa))
+      if (HAVE_64BIT_GPRS)
        macro_build ((char *) NULL, &icnt, &offset_expr, "ld", "t,o(b)",
                     treg, (int) BFD_RELOC_LO16, AT);
       else
@@ -5375,16 +5795,20 @@ macro (ip)
       break;
 
     case M_LI_DD:
-      /* If we have a constant in IMM_EXPR, then in mips3 mode it is
-         the entire value, and in mips1 mode it is the high order 32
-         bits of the value and the low order 32 bits are either zero
-         or in offset_expr.  */
+      /* Check if we have a constant in IMM_EXPR.  If the FPRs are 64 bits
+         wide, IMM_EXPR is the entire value and the GPRs are known to be 64
+         bits wide as well.  Otherwise IMM_EXPR is the high order 32 bits of
+         the value and the low order 32 bits are either zero or in
+         OFFSET_EXPR.  */
       if (imm_expr.X_op == O_constant || imm_expr.X_op == O_big)
        {
-         load_register (&icnt, AT, &imm_expr, ISA_HAS_64BIT_REGS (mips_opts.isa));
-         if (ISA_HAS_64BIT_REGS (mips_opts.isa))
-           macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                        "dmtc1", "t,S", AT, treg);
+         load_register (&icnt, AT, &imm_expr, HAVE_64BIT_FPRS);
+         if (HAVE_64BIT_FPRS)
+           {
+             assert (HAVE_64BIT_GPRS);
+             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                          "dmtc1", "t,S", AT, treg);
+           }
          else
            {
              macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
@@ -5408,7 +5832,7 @@ macro (ip)
       s = segment_name (S_GET_SEGMENT (offset_expr.X_add_symbol));
       if (strcmp (s, ".lit8") == 0)
        {
-         if (mips_opts.isa != 1)
+         if (mips_opts.isa != ISA_MIPS1)
            {
              macro_build ((char *) NULL, &icnt, &offset_expr, "ldc1",
                           "T,o(b)", treg, (int) BFD_RELOC_MIPS_LITERAL, GP);
@@ -5423,9 +5847,7 @@ macro (ip)
          assert (strcmp (s, RDATA_SECTION_NAME) == 0);
          if (mips_pic == SVR4_PIC)
            macro_build ((char *) NULL, &icnt, &offset_expr,
-                        ((bfd_arch_bits_per_address (stdoutput) == 32
-                          || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                         ? "lw" : "ld"),
+                        HAVE_32BIT_ADDRESSES ? "lw" : "ld",
                         "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16, GP);
          else
            {
@@ -5433,7 +5855,7 @@ macro (ip)
              macro_build_lui ((char *) NULL, &icnt, &offset_expr, AT);
            }
 
-         if (mips_opts.isa != 1)
+         if (mips_opts.isa != ISA_MIPS1)
            {
              macro_build ((char *) NULL, &icnt, &offset_expr, "ldc1",
                           "T,o(b)", treg, (int) BFD_RELOC_LO16, AT);
@@ -5451,7 +5873,7 @@ macro (ip)
        }
 
     case M_L_DOB:
-      if (mips_cpu == CPU_R4650)
+      if (mips_arch == CPU_R4650)
        {
          as_bad (_("opcode not supported on this processor"));
          return;
@@ -5460,7 +5882,7 @@ macro (ip)
         to adjust when loading from memory.  */
       r = BFD_RELOC_LO16;
     dob:
-      assert (mips_opts.isa == 1);
+      assert (mips_opts.isa == ISA_MIPS1);
       macro_build ((char *) NULL, &icnt, &offset_expr, "lwc1", "T,o(b)",
                   target_big_endian ? treg + 1 : treg,
                   (int) r, breg);
@@ -5492,14 +5914,14 @@ macro (ip)
        * But, the resulting address is the same after relocation so why
        * generate the extra instruction?
        */
-      if (mips_cpu == CPU_R4650)
+      if (mips_arch == CPU_R4650)
        {
          as_bad (_("opcode not supported on this processor"));
          return;
        }
       /* Itbl support may require additional care here.  */
       coproc = 1;
-      if (mips_opts.isa != 1)
+      if (mips_opts.isa != ISA_MIPS1)
        {
          s = "ldc1";
          goto ld;
@@ -5510,13 +5932,13 @@ macro (ip)
       goto ldd_std;
 
     case M_S_DAB:
-      if (mips_cpu == CPU_R4650)
+      if (mips_arch == CPU_R4650)
        {
          as_bad (_("opcode not supported on this processor"));
          return;
        }
 
-      if (mips_opts.isa != 1)
+      if (mips_opts.isa != ISA_MIPS1)
        {
          s = "sdc1";
          goto st;
@@ -5529,7 +5951,7 @@ macro (ip)
       goto ldd_std;
 
     case M_LD_AB:
-      if (ISA_HAS_64BIT_REGS (mips_opts.isa))
+      if (HAVE_64BIT_GPRS)
        {
          s = "ld";
          goto ld;
@@ -5540,7 +5962,7 @@ macro (ip)
       goto ldd_std;
 
     case M_SD_AB:
-      if (ISA_HAS_64BIT_REGS (mips_opts.isa))
+      if (HAVE_64BIT_GPRS)
        {
          s = "sd";
          goto st;
@@ -5550,6 +5972,11 @@ macro (ip)
       fmt = "t,o(b)";
 
     ldd_std:
+      /* We do _not_ bother to allow embedded PIC (symbol-local_symbol)
+        loads for the case of doing a pair of loads to simulate an 'ld'.
+        This is not currently done by the compiler, and assembly coders
+        writing embedded-pic code can cope.  */
+
       if (offset_expr.X_op != O_symbol
          && offset_expr.X_op != O_constant)
        {
@@ -5581,7 +6008,7 @@ macro (ip)
             If there is a base register, we add it to $at after the
             lui instruction.  If there is a constant, we always use
             the last case.  */
-         if ((valueT) offset_expr.X_add_number >= MAX_GPREL_OFFSET
+         if ((valueT) offset_expr.X_add_number > MAX_GPREL_OFFSET
              || nopic_need_relax (offset_expr.X_add_symbol, 1))
            {
              p = NULL;
@@ -5602,16 +6029,14 @@ macro (ip)
                {
                  frag_grow (36);
                  macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                              ((bfd_arch_bits_per_address (stdoutput) == 32
-                                || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                               ? "addu" : "daddu"),
+                              HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                               "d,v,t", AT, breg, GP);
                  tempreg = AT;
                  off = 4;
                  used_at = 1;
                }
 
-              /* Itbl support may require additional care here.  */
+             /* Itbl support may require additional care here.  */
              macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
                           coproc ? treg + 1 : treg,
                           (int) BFD_RELOC_MIPS_GPREL, tempreg);
@@ -5621,7 +6046,7 @@ macro (ip)
                  undesired nop.  */
              hold_mips_optimize = mips_optimize;
              mips_optimize = 2;
-              /* Itbl support may require additional care here.  */
+             /* Itbl support may require additional care here.  */
              macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
                           coproc ? treg : treg + 1,
                           (int) BFD_RELOC_MIPS_GPREL, tempreg);
@@ -5660,14 +6085,12 @@ macro (ip)
          if (breg != 0)
            {
              macro_build (p, &icnt, (expressionS *) NULL,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "addu" : "daddu"),
+                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                           "d,v,t", AT, breg, AT);
              if (p != NULL)
                p += 4;
            }
-          /* Itbl support may require additional care here.  */
+         /* Itbl support may require additional care here.  */
          macro_build (p, &icnt, &offset_expr, s, fmt,
                       coproc ? treg + 1 : treg,
                       (int) BFD_RELOC_LO16, AT);
@@ -5675,7 +6098,7 @@ macro (ip)
            p += 4;
          /* FIXME: How do we handle overflow here?  */
          offset_expr.X_add_number += 4;
-          /* Itbl support may require additional care here.  */
+         /* Itbl support may require additional care here.  */
          macro_build (p, &icnt, &offset_expr, s, fmt,
                       coproc ? treg : treg + 1,
                       (int) BFD_RELOC_LO16, AT);
@@ -5709,18 +6132,14 @@ macro (ip)
            off = 4;
          frag_grow (24 + off);
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "lw" : "ld"),
+                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
                       "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16, GP);
          macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
          if (breg != 0)
            macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                        ((bfd_arch_bits_per_address (stdoutput) == 32
-                          || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                         ? "addu" : "daddu"),
+                        HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                         "d,v,t", AT, breg, AT);
-          /* Itbl support may require additional care here.  */
+         /* Itbl support may require additional care here.  */
          macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
                       coproc ? treg + 1 : treg,
                       (int) BFD_RELOC_LO16, AT);
@@ -5730,7 +6149,7 @@ macro (ip)
              nop.  */
          hold_mips_optimize = mips_optimize;
          mips_optimize = 2;
-          /* Itbl support may require additional care here.  */
+         /* Itbl support may require additional care here.  */
          macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
                       coproc ? treg : treg + 1,
                       (int) BFD_RELOC_LO16, AT);
@@ -5738,8 +6157,7 @@ macro (ip)
 
          (void) frag_var (rs_machine_dependent, 0, 0,
                           RELAX_ENCODE (0, 0, -16 - off, -8, 1, 0),
-                          offset_expr.X_add_symbol, (offsetT) 0,
-                          (char *) NULL);
+                          offset_expr.X_add_symbol, 0, NULL);
        }
       else if (mips_pic == SVR4_PIC)
        {
@@ -5778,23 +6196,17 @@ 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,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "addu" : "daddu"),
+                      HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                       "d,v,t", AT, AT, GP);
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "lw" : "ld"),
+                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
                       "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,
-                        ((bfd_arch_bits_per_address (stdoutput) == 32
-                          || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                         ? "addu" : "daddu"),
+                        HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                         "d,v,t", AT, breg, AT);
-          /* Itbl support may require additional care here.  */
+         /* Itbl support may require additional care here.  */
          macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
                       coproc ? treg + 1 : treg,
                       (int) BFD_RELOC_LO16, AT);
@@ -5804,7 +6216,7 @@ macro (ip)
              nop.  */
          hold_mips_optimize = mips_optimize;
          mips_optimize = 2;
-          /* Itbl support may require additional care here.  */
+         /* Itbl support may require additional care here.  */
          macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
                       coproc ? treg : treg + 1,
                       (int) BFD_RELOC_LO16, AT);
@@ -5814,17 +6226,14 @@ macro (ip)
          p = frag_var (rs_machine_dependent, 16 + gpdel + off, 0,
                        RELAX_ENCODE (24 + off, 16 + gpdel + off, gpdel,
                                      8 + gpdel + off, 1, 0),
-                       offset_expr.X_add_symbol, (offsetT) 0,
-                       (char *) NULL);
+                       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,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "lw" : "ld"),
+                      HAVE_32BIT_ADDRESSES ? "lw" : "ld",
                       "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16, GP);
          p += 4;
          macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
@@ -5832,13 +6241,11 @@ macro (ip)
          if (breg != 0)
            {
              macro_build (p, &icnt, (expressionS *) NULL,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "addu" : "daddu"),
+                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                           "d,v,t", AT, breg, AT);
              p += 4;
            }
-          /* Itbl support may require additional care here.  */
+         /* Itbl support may require additional care here.  */
          macro_build (p, &icnt, &expr1, s, fmt,
                       coproc ? treg + 1 : treg,
                       (int) BFD_RELOC_LO16, AT);
@@ -5849,7 +6256,7 @@ macro (ip)
              nop.  */
          hold_mips_optimize = mips_optimize;
          mips_optimize = 2;
-          /* Itbl support may require additional care here.  */
+         /* Itbl support may require additional care here.  */
          macro_build (p, &icnt, &expr1, s, fmt,
                       coproc ? treg : treg + 1,
                       (int) BFD_RELOC_LO16, AT);
@@ -5873,20 +6280,18 @@ macro (ip)
          else
            {
              macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                          ((bfd_arch_bits_per_address (stdoutput) == 32
-                            || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                           ? "addu" : "daddu"),
+                          HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                           "d,v,t", AT, breg, GP);
              tempreg = AT;
              used_at = 1;
            }
 
-          /* Itbl support may require additional care here.  */
+         /* Itbl support may require additional care here.  */
          macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
                       coproc ? treg + 1 : treg,
                       (int) BFD_RELOC_MIPS_GPREL, tempreg);
          offset_expr.X_add_number += 4;
-          /* Itbl support may require additional care here.  */
+         /* Itbl support may require additional care here.  */
          macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
                       coproc ? treg : treg + 1,
                       (int) BFD_RELOC_MIPS_GPREL, tempreg);
@@ -5905,8 +6310,7 @@ macro (ip)
     case M_SD_OB:
       s = "sw";
     sd_ob:
-      assert (bfd_arch_bits_per_address (stdoutput) == 32
-             || ! ISA_HAS_64BIT_REGS (mips_opts.isa));
+      assert (HAVE_32BIT_ADDRESSES);
       macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
       offset_expr.X_add_number += 4;
@@ -5949,6 +6353,10 @@ macro (ip)
                   ip->insn_opcode);
       return;
 
+    case M_MOVE:
+      move_register (&icnt, dreg, sreg);
+      return;
+
 #ifdef LOSING_COMPILER
     default:
       /* Try and see if this is a new itbl instruction.
@@ -5961,13 +6369,13 @@ macro (ip)
          Would it be more efficient to use mask (id) here? */
       if (itbl_have_entries
          && (immed_expr = itbl_assemble (ip->insn_mo->name, "")))
-        {
+       {
          s = ip->insn_mo->name;
          s2 = "cop3";
          coproc = ITBL_DECODE_PNUM (immed_expr);;
          macro_build ((char *) NULL, &icnt, &immed_expr, s, "C");
          return;
-        }
+       }
       macro2 (ip);
       return;
     }
@@ -6015,10 +6423,10 @@ macro2 (ip)
     case M_DMUL:
       dbl = 1;
     case M_MUL:
-      macro_build ((char *) NULL, &icnt, NULL,
-                  dbl ? "dmultu" : "multu",
-                  "s,t", sreg, treg);
-      macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                  dbl ? "dmultu" : "multu", "s,t", sreg, treg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "mflo", "d",
+                  dreg);
       return;
 
     case M_DMUL_I:
@@ -6028,10 +6436,10 @@ macro2 (ip)
         not trying to be that fancy. GCC should do this for us
         anyway.  */
       load_register (&icnt, AT, &imm_expr, dbl);
-      macro_build ((char *) NULL, &icnt, NULL,
-                  dbl ? "dmult" : "mult",
-                  "s,t", sreg, AT);
-      macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                  dbl ? "dmult" : "mult", "s,t", sreg, AT);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "mflo", "d",
+                  dreg);
       break;
 
     case M_DMULO_I:
@@ -6049,25 +6457,29 @@ macro2 (ip)
       mips_any_noreorder = 1;
       if (imm)
        load_register (&icnt, AT, &imm_expr, dbl);
-      macro_build ((char *) NULL, &icnt, NULL,
-                  dbl ? "dmult" : "mult",
-                  "s,t", sreg, imm ? AT : treg);
-      macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg);
-      macro_build ((char *) NULL, &icnt, NULL,
-                  dbl ? "dsra32" : "sra",
-                  "d,w,<", dreg, dreg, 31);
-      macro_build ((char *) NULL, &icnt, NULL, "mfhi", "d", AT);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                  dbl ? "dmult" : "mult", "s,t", sreg, imm ? AT : treg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "mflo", "d",
+                  dreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                  dbl ? "dsra32" : "sra", "d,w,<", dreg, dreg, 31);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "mfhi", "d",
+                  AT);
       if (mips_trap)
-       macro_build ((char *) NULL, &icnt, NULL, "tne", "s,t", dreg, AT);
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "tne", "s,t",
+                    dreg, AT);
       else
        {
          expr1.X_add_number = 8;
-         macro_build ((char *) NULL, &icnt, &expr1, "beq", "s,t,p", dreg, AT);
-         macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
-           macro_build ((char *) NULL, &icnt, NULL, "break", "c", 6);
+         macro_build ((char *) NULL, &icnt, &expr1, "beq", "s,t,p", dreg,
+                      AT);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "",
+                      0);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "break",
+                      "c", 6);
        }
       --mips_opts.noreorder;
-      macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "mflo", "d", dreg);
       break;
 
     case M_DMULOU_I:
@@ -6085,66 +6497,79 @@ macro2 (ip)
       mips_any_noreorder = 1;
       if (imm)
        load_register (&icnt, AT, &imm_expr, dbl);
-      macro_build ((char *) NULL, &icnt, NULL,
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                   dbl ? "dmultu" : "multu",
                   "s,t", sreg, imm ? AT : treg);
-      macro_build ((char *) NULL, &icnt, NULL, "mfhi", "d", AT);
-      macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "mfhi", "d",
+                  AT);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "mflo", "d",
+                  dreg);
       if (mips_trap)
-       macro_build ((char *) NULL, &icnt, NULL, "tne", "s,t", AT, 0);
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "tne", "s,t",
+                    AT, 0);
       else
        {
          expr1.X_add_number = 8;
          macro_build ((char *) NULL, &icnt, &expr1, "beq", "s,t,p", AT, 0);
-         macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
-           macro_build ((char *) NULL, &icnt, NULL, "break", "c", 6);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "",
+                      0);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "break",
+                      "c", 6);
        }
       --mips_opts.noreorder;
       break;
 
     case M_ROL:
-      macro_build ((char *) NULL, &icnt, NULL, "subu", "d,v,t", AT, 0, treg);
-      macro_build ((char *) NULL, &icnt, NULL, "srlv", "d,t,s", AT, sreg, AT);
-      macro_build ((char *) NULL, &icnt, NULL, "sllv", "d,t,s", dreg, sreg,
-                  treg);
-      macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "subu",
+                  "d,v,t", AT, 0, treg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "srlv",
+                  "d,t,s", AT, sreg, AT);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sllv",
+                  "d,t,s", dreg, sreg, treg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "or",
+                  "d,v,t", dreg, dreg, AT);
       break;
 
     case M_ROL_I:
       if (imm_expr.X_op != O_constant)
        as_bad (_("rotate count too large"));
-      macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", AT, sreg,
-                  (int) (imm_expr.X_add_number & 0x1f));
-      macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", dreg, sreg,
-                  (int) ((0 - imm_expr.X_add_number) & 0x1f));
-      macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sll", "d,w,<",
+                  AT, sreg, (int) (imm_expr.X_add_number & 0x1f));
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "srl", "d,w,<",
+                  dreg, sreg, (int) ((0 - imm_expr.X_add_number) & 0x1f));
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "or", "d,v,t",
+                  dreg, dreg, AT);
       break;
 
     case M_ROR:
-      macro_build ((char *) NULL, &icnt, NULL, "subu", "d,v,t", AT, 0, treg);
-      macro_build ((char *) NULL, &icnt, NULL, "sllv", "d,t,s", AT, sreg, AT);
-      macro_build ((char *) NULL, &icnt, NULL, "srlv", "d,t,s", dreg, sreg,
-                  treg);
-      macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "subu",
+                  "d,v,t", AT, 0, treg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sllv",
+                  "d,t,s", AT, sreg, AT);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "srlv",
+                  "d,t,s", dreg, sreg, treg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "or",
+                  "d,v,t", dreg, dreg, AT);
       break;
 
     case M_ROR_I:
       if (imm_expr.X_op != O_constant)
        as_bad (_("rotate count too large"));
-      macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", AT, sreg,
-                  (int) (imm_expr.X_add_number & 0x1f));
-      macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", dreg, sreg,
-                  (int) ((0 - imm_expr.X_add_number) & 0x1f));
-      macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "srl", "d,w,<",
+                  AT, sreg, (int) (imm_expr.X_add_number & 0x1f));
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sll", "d,w,<",
+                  dreg, sreg, (int) ((0 - imm_expr.X_add_number) & 0x1f));
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "or", "d,v,t",
+                  dreg, dreg, AT);
       break;
 
     case M_S_DOB:
-      if (mips_cpu == CPU_R4650)
+      if (mips_arch == CPU_R4650)
        {
          as_bad (_("opcode not supported on this processor"));
          return;
        }
-      assert (mips_opts.isa == 1);
+      assert (mips_opts.isa == ISA_MIPS1);
       /* Even on a big endian machine $fn comes before $fn+1.  We have
         to adjust when storing to memory.  */
       macro_build ((char *) NULL, &icnt, &offset_expr, "swc1", "T,o(b)",
@@ -6165,8 +6590,8 @@ macro2 (ip)
                     sreg, (int) BFD_RELOC_LO16);
       else
        {
-         macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg,
-                      sreg, treg);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "xor",
+                      "d,v,t", dreg, sreg, treg);
          macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg,
                       dreg, (int) BFD_RELOC_LO16);
        }
@@ -6183,7 +6608,7 @@ macro2 (ip)
        {
          as_warn (_("Instruction %s: result is always false"),
                   ip->insn_mo->name);
-         macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", dreg, 0);
+         move_register (&icnt, dreg, 0);
          return;
        }
       if (imm_expr.X_op == O_constant
@@ -6200,9 +6625,7 @@ macro2 (ip)
        {
          imm_expr.X_add_number = -imm_expr.X_add_number;
          macro_build ((char *) NULL, &icnt, &imm_expr,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "addiu" : "daddiu"),
+                      HAVE_32BIT_GPRS ? "addiu" : "daddiu",
                       "t,r,j", dreg, sreg,
                       (int) BFD_RELOC_LO16);
          used_at = 0;
@@ -6210,8 +6633,8 @@ macro2 (ip)
       else
        {
          load_register (&icnt, AT, &imm_expr, 0);
-         macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg,
-                      sreg, AT);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "xor",
+                      "d,v,t", dreg, sreg, AT);
          used_at = 1;
        }
       macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg, dreg,
@@ -6226,7 +6649,8 @@ macro2 (ip)
     case M_SGEU:
       s = "sltu";
     sge:
-      macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, sreg, treg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "d,v,t",
+                  dreg, sreg, treg);
       macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg,
                   (int) BFD_RELOC_LO16);
       return;
@@ -6245,9 +6669,9 @@ macro2 (ip)
       else
        {
          load_register (&icnt, AT, &imm_expr, 0);
-         macro_build ((char *) NULL, &icnt, NULL,
-                      mask == M_SGE_I ? "slt" : "sltu",
-                      "d,v,t", dreg, sreg, AT);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                      mask == M_SGE_I ? "slt" : "sltu", "d,v,t", dreg, sreg,
+                      AT);
          used_at = 1;
        }
       macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg,
@@ -6262,7 +6686,8 @@ macro2 (ip)
     case M_SGTU:
       s = "sltu";
     sgt:
-      macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, treg, sreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "d,v,t",
+                  dreg, treg, sreg);
       return;
 
     case M_SGT_I:              /* sreg > I  <==>  I < sreg */
@@ -6272,28 +6697,31 @@ macro2 (ip)
       s = "sltu";
     sgti:
       load_register (&icnt, AT, &imm_expr, 0);
-      macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, AT, sreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "d,v,t",
+                  dreg, AT, sreg);
       break;
 
-    case M_SLE:                /* sreg <= treg  <==>  treg >= sreg  <==>  not (treg < sreg) */
+    case M_SLE:        /* sreg <= treg  <==>  treg >= sreg  <==>  not (treg < sreg) */
       s = "slt";
       goto sle;
     case M_SLEU:
       s = "sltu";
     sle:
-      macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, treg, sreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "d,v,t",
+                  dreg, treg, sreg);
       macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg,
                   (int) BFD_RELOC_LO16);
       return;
 
-    case M_SLE_I:              /* sreg <= I <==> I >= sreg <==> not (I < sreg) */
+    case M_SLE_I:      /* sreg <= I <==> I >= sreg <==> not (I < sreg) */
       s = "slt";
       goto slei;
     case M_SLEU_I:
       s = "sltu";
     slei:
       load_register (&icnt, AT, &imm_expr, 0);
-      macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, AT, sreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "d,v,t",
+                  dreg, AT, sreg);
       macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg,
                   (int) BFD_RELOC_LO16);
       break;
@@ -6308,7 +6736,8 @@ macro2 (ip)
          return;
        }
       load_register (&icnt, AT, &imm_expr, 0);
-      macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", dreg, sreg, AT);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "slt", "d,v,t",
+                  dreg, sreg, AT);
       break;
 
     case M_SLTU_I:
@@ -6321,31 +6750,31 @@ macro2 (ip)
          return;
        }
       load_register (&icnt, AT, &imm_expr, 0);
-      macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, sreg,
-                  AT);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sltu",
+                  "d,v,t", dreg, sreg, AT);
       break;
 
     case M_SNE:
       if (sreg == 0)
-       macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, 0,
-                    treg);
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sltu",
+                    "d,v,t", dreg, 0, treg);
       else if (treg == 0)
-       macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, 0,
-                    sreg);
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sltu",
+                    "d,v,t", dreg, 0, sreg);
       else
        {
-         macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg,
-                      sreg, treg);
-         macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, 0,
-                      dreg);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "xor",
+                      "d,v,t", dreg, sreg, treg);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sltu",
+                      "d,v,t", dreg, 0, dreg);
        }
       return;
 
     case M_SNE_I:
       if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
        {
-         macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, 0,
-                      sreg);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sltu",
+                      "d,v,t", dreg, 0, sreg);
          return;
        }
       if (sreg == 0)
@@ -6353,9 +6782,7 @@ macro2 (ip)
          as_warn (_("Instruction %s: result is always true"),
                   ip->insn_mo->name);
          macro_build ((char *) NULL, &icnt, &expr1,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "addiu" : "daddiu"),
+                      HAVE_32BIT_GPRS ? "addiu" : "daddiu",
                       "t,r,j", dreg, 0, (int) BFD_RELOC_LO16);
          return;
        }
@@ -6373,20 +6800,19 @@ macro2 (ip)
        {
          imm_expr.X_add_number = -imm_expr.X_add_number;
          macro_build ((char *) NULL, &icnt, &imm_expr,
-                      ((bfd_arch_bits_per_address (stdoutput) == 32
-                        || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                       ? "addiu" : "daddiu"),
+                      HAVE_32BIT_GPRS ? "addiu" : "daddiu",
                       "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16);
          used_at = 0;
        }
       else
        {
          load_register (&icnt, AT, &imm_expr, 0);
-         macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg,
-                      sreg, AT);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "xor",
+                      "d,v,t", dreg, sreg, AT);
          used_at = 1;
        }
-      macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, 0, dreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sltu",
+                  "d,v,t", dreg, 0, dreg);
       if (used_at)
        break;
       return;
@@ -6405,9 +6831,8 @@ macro2 (ip)
          return;
        }
       load_register (&icnt, AT, &imm_expr, dbl);
-      macro_build ((char *) NULL, &icnt, NULL,
-                  dbl ? "dsub" : "sub",
-                  "d,v,t", dreg, sreg, AT);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                  dbl ? "dsub" : "sub", "d,v,t", dreg, sreg, AT);
       break;
 
     case M_DSUBU_I:
@@ -6424,9 +6849,8 @@ macro2 (ip)
          return;
        }
       load_register (&icnt, AT, &imm_expr, dbl);
-      macro_build ((char *) NULL, &icnt, NULL,
-                  dbl ? "dsubu" : "subu",
-                  "d,v,t", dreg, sreg, AT);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                  dbl ? "dsubu" : "subu", "d,v,t", dreg, sreg, AT);
       break;
 
     case M_TEQ_I:
@@ -6448,12 +6872,13 @@ macro2 (ip)
       s = "tne";
     trap:
       load_register (&icnt, AT, &imm_expr, 0);
-      macro_build ((char *) NULL, &icnt, NULL, s, "s,t", sreg, AT);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "s,t", sreg,
+                  AT);
       break;
 
     case M_TRUNCWS:
     case M_TRUNCWD:
-      assert (mips_opts.isa == 1);
+      assert (mips_opts.isa == ISA_MIPS1);
       sreg = (ip->insn_opcode >> 11) & 0x1f;   /* floating reg */
       dreg = (ip->insn_opcode >> 06) & 0x1f;   /* floating reg */
 
@@ -6464,21 +6889,25 @@ macro2 (ip)
       mips_emit_delays (true);
       ++mips_opts.noreorder;
       mips_any_noreorder = 1;
-      macro_build ((char *) NULL, &icnt, NULL, "cfc1", "t,G", treg, 31);
-      macro_build ((char *) NULL, &icnt, NULL, "cfc1", "t,G", treg, 31);
-      macro_build ((char *) NULL, &icnt, NULL, "nop", "");
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "cfc1", "t,G",
+                  treg, 31);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "cfc1", "t,G",
+                  treg, 31);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
       expr1.X_add_number = 3;
       macro_build ((char *) NULL, &icnt, &expr1, "ori", "t,r,i", AT, treg,
                   (int) BFD_RELOC_LO16);
       expr1.X_add_number = 2;
       macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", AT, AT,
                     (int) BFD_RELOC_LO16);
-      macro_build ((char *) NULL, &icnt, NULL, "ctc1", "t,G", AT, 31);
-      macro_build ((char *) NULL, &icnt, NULL, "nop", "");
-      macro_build ((char *) NULL, &icnt, NULL,
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "ctc1", "t,G",
+                  AT, 31);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
              mask == M_TRUNCWD ? "cvt.w.d" : "cvt.w.s", "D,S", dreg, sreg);
-      macro_build ((char *) NULL, &icnt, NULL, "ctc1", "t,G", treg, 31);
-      macro_build ((char *) NULL, &icnt, NULL, "nop", "");
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "ctc1", "t,G",
+                  treg, 31);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
       --mips_opts.noreorder;
       break;
 
@@ -6501,8 +6930,10 @@ macro2 (ip)
        offset_expr.X_add_number += 1;
       macro_build ((char *) NULL, &icnt, &offset_expr, "lbu", "t,o(b)", AT,
                   (int) BFD_RELOC_LO16, breg);
-      macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", treg, treg, 8);
-      macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", treg, treg, AT);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sll", "d,w,<",
+                  treg, treg, 8);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "or", "d,v,t",
+                  treg, treg, AT);
       break;
 
     case M_ULD:
@@ -6539,12 +6970,11 @@ macro2 (ip)
       s2 = "lwr";
       off = 3;
     ulwa:
-      load_address (&icnt, AT, &offset_expr);
+      used_at = 1;
+      load_address (&icnt, AT, &offset_expr, HAVE_64BIT_ADDRESSES, &used_at);
       if (breg != 0)
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                    ((bfd_arch_bits_per_address (stdoutput) == 32
-                      || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                     ? "addu" : "daddu"),
+                    HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                     "d,v,t", AT, AT, breg);
       if (! target_big_endian)
        expr1.X_add_number = off;
@@ -6562,12 +6992,11 @@ macro2 (ip)
 
     case M_ULH_A:
     case M_ULHU_A:
-      load_address (&icnt, AT, &offset_expr);
+      used_at = 1;
+      load_address (&icnt, AT, &offset_expr, HAVE_64BIT_ADDRESSES, &used_at);
       if (breg != 0)
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                    ((bfd_arch_bits_per_address (stdoutput) == 32
-                      || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                     ? "addu" : "daddu"),
+                    HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                     "d,v,t", AT, AT, breg);
       if (target_big_endian)
        expr1.X_add_number = 0;
@@ -6580,10 +7009,10 @@ macro2 (ip)
        expr1.X_add_number = 0;
       macro_build ((char *) NULL, &icnt, &expr1, "lbu", "t,o(b)", AT,
                   (int) BFD_RELOC_LO16, AT);
-      macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", treg,
-                  treg, 8);
-      macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", treg,
-                  treg, AT);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sll", "d,w,<",
+                  treg, treg, 8);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "or", "d,v,t",
+                  treg, treg, AT);
       break;
 
     case M_USH:
@@ -6593,7 +7022,8 @@ macro2 (ip)
        offset_expr.X_add_number += 1;
       macro_build ((char *) NULL, &icnt, &offset_expr, "sb", "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
-      macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", AT, treg, 8);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "srl", "d,w,<",
+                  AT, treg, 8);
       if (target_big_endian)
        offset_expr.X_add_number -= 1;
       else
@@ -6636,12 +7066,11 @@ macro2 (ip)
       s2 = "swr";
       off = 3;
     uswa:
-      load_address (&icnt, AT, &offset_expr);
+      used_at = 1;
+      load_address (&icnt, AT, &offset_expr, HAVE_64BIT_ADDRESSES, &used_at);
       if (breg != 0)
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                    ((bfd_arch_bits_per_address (stdoutput) == 32
-                      || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                     ? "addu" : "daddu"),
+                    HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                     "d,v,t", AT, AT, breg);
       if (! target_big_endian)
        expr1.X_add_number = off;
@@ -6658,19 +7087,18 @@ macro2 (ip)
       break;
 
     case M_USH_A:
-      load_address (&icnt, AT, &offset_expr);
+      used_at = 1;
+      load_address (&icnt, AT, &offset_expr, HAVE_64BIT_ADDRESSES, &used_at);
       if (breg != 0)
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                    ((bfd_arch_bits_per_address (stdoutput) == 32
-                      || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-                     ? "addu" : "daddu"),
+                    HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
                     "d,v,t", AT, AT, breg);
       if (! target_big_endian)
        expr1.X_add_number = 0;
       macro_build ((char *) NULL, &icnt, &expr1, "sb", "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, AT);
-      macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", treg,
-                  treg, 8);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "srl", "d,w,<",
+                  treg, treg, 8);
       if (! target_big_endian)
        expr1.X_add_number = 1;
       else
@@ -6683,10 +7111,10 @@ macro2 (ip)
        expr1.X_add_number = 1;
       macro_build ((char *) NULL, &icnt, &expr1, "lbu", "t,o(b)", AT,
                   (int) BFD_RELOC_LO16, AT);
-      macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", treg,
-                  treg, 8);
-      macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", treg,
-                  treg, AT);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "sll", "d,w,<",
+                  treg, treg, 8);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "or", "d,v,t",
+                  treg, treg, AT);
       break;
 
     default:
@@ -6745,19 +7173,20 @@ mips16_macro (ip)
       mips_emit_delays (true);
       ++mips_opts.noreorder;
       mips_any_noreorder = 1;
-      macro_build ((char *) NULL, &icnt, NULL,
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                   dbl ? "ddiv" : "div",
                   "0,x,y", xreg, yreg);
       expr1.X_add_number = 2;
       macro_build ((char *) NULL, &icnt, &expr1, "bnez", "x,p", yreg);
-       macro_build ((char *) NULL, &icnt, NULL, "break", "6", 7);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "break", "6",
+                  7);
 
       /* FIXME: The normal code checks for of -1 / -0x80000000 here,
          since that causes an overflow.  We should do that as well,
          but I don't see how to do the comparisons without a temporary
          register.  */
       --mips_opts.noreorder;
-      macro_build ((char *) NULL, &icnt, NULL, s, "x", zreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "x", zreg);
       break;
 
     case M_DIVU_3:
@@ -6779,21 +7208,23 @@ mips16_macro (ip)
       mips_emit_delays (true);
       ++mips_opts.noreorder;
       mips_any_noreorder = 1;
-      macro_build ((char *) NULL, &icnt, NULL, s, "0,x,y", xreg, yreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "0,x,y",
+                  xreg, yreg);
       expr1.X_add_number = 2;
       macro_build ((char *) NULL, &icnt, &expr1, "bnez", "x,p", yreg);
-       macro_build ((char *) NULL, &icnt, NULL, "break", "6", 7);
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "break",
+                    "6", 7);
       --mips_opts.noreorder;
-      macro_build ((char *) NULL, &icnt, NULL, s2, "x", zreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s2, "x", zreg);
       break;
 
     case M_DMUL:
       dbl = 1;
     case M_MUL:
-      macro_build ((char *) NULL, &icnt, NULL,
-                  dbl ? "dmultu" : "multu",
-                  "x,y", xreg, yreg);
-      macro_build ((char *) NULL, &icnt, NULL, "mflo", "x", zreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                  dbl ? "dmultu" : "multu", "x,y", xreg, yreg);
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "mflo", "x",
+                  zreg);
       return;
 
     case M_DSUBU_I:
@@ -6805,8 +7236,7 @@ mips16_macro (ip)
        as_bad (_("Unsupported large constant"));
       imm_expr.X_add_number = -imm_expr.X_add_number;
       macro_build ((char *) NULL, &icnt, &imm_expr,
-                  dbl ? "daddiu" : "addiu",
-                  "y,x,4", yreg, xreg);
+                  dbl ? "daddiu" : "addiu", "y,x,4", yreg, xreg);
       break;
 
     case M_SUBU_I_2:
@@ -6940,8 +7370,7 @@ mips16_macro (ip)
       expr1.X_add_number = 0;
       macro_build ((char *) NULL, &icnt, &expr1, "slti", "x,8",  yreg);
       if (xreg != yreg)
-       macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                    "move", "y,X", xreg, yreg);
+       move_register (&icnt, xreg, yreg);
       expr1.X_add_number = 2;
       macro_build ((char *) NULL, &icnt, &expr1, "bteqz", "p");
       macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
@@ -6976,7 +7405,7 @@ validate_mips_insn (opc)
       case '<': USE_BITS (OP_MASK_SHAMT,       OP_SH_SHAMT);   break;
       case '>':        USE_BITS (OP_MASK_SHAMT,        OP_SH_SHAMT);   break;
       case 'A': break;
-      case 'B':        USE_BITS (OP_MASK_SYSCALL,      OP_SH_SYSCALL); break;
+      case 'B': USE_BITS (OP_MASK_CODE20,       OP_SH_CODE20);  break;
       case 'C':        USE_BITS (OP_MASK_COPZ,         OP_SH_COPZ);    break;
       case 'D':        USE_BITS (OP_MASK_FD,           OP_SH_FD);      break;
       case 'E':        USE_BITS (OP_MASK_RT,           OP_SH_RT);      break;
@@ -6984,6 +7413,7 @@ validate_mips_insn (opc)
       case 'G':        USE_BITS (OP_MASK_RD,           OP_SH_RD);      break;
       case 'H': USE_BITS (OP_MASK_SEL,         OP_SH_SEL);     break;
       case 'I': break;
+      case 'J': USE_BITS (OP_MASK_CODE19,       OP_SH_CODE19);  break;
       case 'L': break;
       case 'M':        USE_BITS (OP_MASK_CCC,          OP_SH_CCC);     break;
       case 'N':        USE_BITS (OP_MASK_BCC,          OP_SH_BCC);     break;
@@ -7002,7 +7432,6 @@ validate_mips_insn (opc)
       case 'j':        USE_BITS (OP_MASK_DELTA,        OP_SH_DELTA);   break;
       case 'k':        USE_BITS (OP_MASK_CACHE,        OP_SH_CACHE);   break;
       case 'l': break;
-      case 'm': USE_BITS (OP_MASK_CODE20,      OP_SH_CODE20);  break;
       case 'o': USE_BITS (OP_MASK_DELTA,       OP_SH_DELTA);   break;
       case 'p':        USE_BITS (OP_MASK_DELTA,        OP_SH_DELTA);   break;
       case 'q':        USE_BITS (OP_MASK_CODE2,        OP_SH_CODE2);   break;
@@ -7015,6 +7444,8 @@ validate_mips_insn (opc)
       case 'x': break;
       case 'z': break;
       case 'P': USE_BITS (OP_MASK_PERFREG,     OP_SH_PERFREG); break;
+      case 'U': USE_BITS (OP_MASK_RD,           OP_SH_RD);
+               USE_BITS (OP_MASK_RT,           OP_SH_RT);      break;
       default:
        as_bad (_("internal: bad mips opcode (unknown operand type `%c'): %s %s"),
                c, opc->name, opc->args);
@@ -7056,13 +7487,13 @@ mips_ip (str, ip)
   /* If the instruction contains a '.', we first try to match an instruction
      including the '.'.  Then we try again without the '.'.  */
   insn = NULL;
-  for (s = str; *s != '\0' && !isspace ((unsigned char) *s); ++s)
+  for (s = str; *s != '\0' && !ISSPACE (*s); ++s)
     continue;
 
   /* If we stopped on whitespace, then replace the whitespace with null for
      the call to hash_find.  Save the character we replaced just in case we
      have to re-parse the instruction.  */
-  if (isspace ((unsigned char) *s))
+  if (ISSPACE (*s))
     {
       save_c = *s;
       *s++ = '\0';
@@ -7080,7 +7511,9 @@ mips_ip (str, ip)
        *(--s) = save_c;
 
       /* Scan up to the first '.' or whitespace.  */
-      for (s = str; *s != '\0' && *s != '.' && !isspace ((unsigned char) *s); ++s)
+      for (s = str;
+          *s != '\0' && *s != '.' && !ISSPACE (*s);
+          ++s)
        continue;
 
       /* If we did not find a '.', then we can quit now.  */
@@ -7108,14 +7541,14 @@ mips_ip (str, ip)
 
       assert (strcmp (insn->name, str) == 0);
 
-      if (OPCODE_IS_MEMBER (insn, mips_opts.isa, mips_cpu, mips_gp32))
+      if (OPCODE_IS_MEMBER (insn, mips_opts.isa, mips_arch))
        ok = true;
       else
        ok = false;
 
       if (insn->pinfo != INSN_MACRO)
        {
-         if (mips_cpu == CPU_R4650 && (insn->pinfo & FP_D) != 0)
+         if (mips_arch == CPU_R4650 && (insn->pinfo & FP_D) != 0)
            ok = false;
        }
 
@@ -7128,23 +7561,29 @@ mips_ip (str, ip)
              continue;
            }
          else
-           {
-             static char buf[100];
-             sprintf (buf,
-                      _("opcode not supported on this processor: %s (MIPS%d)"),
-                      mips_cpu_to_str (mips_cpu), mips_opts.isa);
+           {
+             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));
 
-             insn_error = buf;
+                 insn_error = buf;
+               }
+             if (save_c)
+               *(--s) = save_c;
              return;
            }
        }
 
       ip->insn_mo = insn;
       ip->insn_opcode = insn->match;
+      insn_error = NULL;
       for (args = insn->args;; ++args)
        {
-         if (*s == ' ')
-           ++s;
+         s += strspn (s, " \t");
          switch (*args)
            {
            case '\0':          /* end of args */
@@ -7160,16 +7599,19 @@ mips_ip (str, ip)
                {
                case 'r':
                case 'v':
-                 ip->insn_opcode |= lastregno << 21;
+                 ip->insn_opcode |= lastregno << OP_SH_RS;
                  continue;
 
                case 'w':
+                 ip->insn_opcode |= lastregno << OP_SH_RT;
+                 continue;
+
                case 'W':
-                 ip->insn_opcode |= lastregno << 16;
+                 ip->insn_opcode |= lastregno << OP_SH_FT;
                  continue;
 
                case 'V':
-                 ip->insn_opcode |= lastregno << 11;
+                 ip->insn_opcode |= lastregno << OP_SH_FS;
                  continue;
                }
              break;
@@ -7193,8 +7635,8 @@ mips_ip (str, ip)
            case '<':           /* must be at least one digit */
              /*
               * According to the manual, if the shift amount is greater
-              * than 31 or less than 0 the the shift amount should be
-              * mod 32. In reality the mips assembler issues an error.
+              * than 31 or less than 0, then the shift amount should be
+              * mod 32.  In reality the mips assembler issues an error.
               * We issue a warning and mask out all but the low 5 bits.
               */
              my_getExpression (&imm_expr, s);
@@ -7203,9 +7645,9 @@ mips_ip (str, ip)
                {
                  as_warn (_("Improper shift amount (%ld)"),
                           (long) imm_expr.X_add_number);
-                 imm_expr.X_add_number = imm_expr.X_add_number & 0x1f;
+                 imm_expr.X_add_number &= OP_MASK_SHAMT;
                }
-             ip->insn_opcode |= imm_expr.X_add_number << 6;
+             ip->insn_opcode |= imm_expr.X_add_number << OP_SH_SHAMT;
              imm_expr.X_op = O_absent;
              s = expr_end;
              continue;
@@ -7216,7 +7658,7 @@ mips_ip (str, ip)
              if ((unsigned long) imm_expr.X_add_number < 32
                  || (unsigned long) imm_expr.X_add_number > 63)
                break;
-             ip->insn_opcode |= (imm_expr.X_add_number - 32) << 6;
+             ip->insn_opcode |= (imm_expr.X_add_number - 32) << OP_SH_SHAMT;
              imm_expr.X_op = O_absent;
              s = expr_end;
              continue;
@@ -7247,9 +7689,9 @@ mips_ip (str, ip)
                {
                  as_warn (_("Illegal break code (%ld)"),
                           (long) imm_expr.X_add_number);
-                 imm_expr.X_add_number &= 0x3ff;
+                 imm_expr.X_add_number &= OP_MASK_CODE;
                }
-             ip->insn_opcode |= imm_expr.X_add_number << 16;
+             ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CODE;
              imm_expr.X_op = O_absent;
              s = expr_end;
              continue;
@@ -7261,69 +7703,62 @@ mips_ip (str, ip)
                {
                  as_warn (_("Illegal lower break code (%ld)"),
                           (long) imm_expr.X_add_number);
-                 imm_expr.X_add_number &= 0x3ff;
+                 imm_expr.X_add_number &= OP_MASK_CODE2;
                }
-             ip->insn_opcode |= imm_expr.X_add_number << 6;
+             ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CODE2;
              imm_expr.X_op = O_absent;
              s = expr_end;
              continue;
 
-           case 'm':           /* Full 20 bit break code.  */
+           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);
+             ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CODE20;
+             imm_expr.X_op = O_absent;
+             s = expr_end;
+             continue;
 
-             if ((unsigned) imm_expr.X_add_number > 0xfffff)
+            case 'C':           /* Coprocessor code */
+             my_getExpression (&imm_expr, s);
+             check_absolute_expr (ip, &imm_expr);
+             if ((unsigned long) imm_expr.X_add_number >= (1<<25))
                {
-                 as_warn (_("Illegal break code (%ld)"),
+                 as_warn (_("Coproccesor code > 25 bits (%ld)"),
                           (long) imm_expr.X_add_number);
-                 imm_expr.X_add_number &= 0xfffff;
+                 imm_expr.X_add_number &= ((1<<25) - 1);
                }
-
-             ip->insn_opcode |= imm_expr.X_add_number << 6;
+             ip->insn_opcode |= imm_expr.X_add_number;
              imm_expr.X_op = O_absent;
              s = expr_end;
-
              continue;
 
-           case 'B':           /* syscall code */
+           case 'J':           /* 19-bit wait code.  */
              my_getExpression (&imm_expr, s);
              check_absolute_expr (ip, &imm_expr);
-             if ((unsigned) imm_expr.X_add_number > 0xfffff)
-               as_warn (_("Illegal syscall code (%ld)"),
+             if ((unsigned) imm_expr.X_add_number > OP_MASK_CODE19)
+               as_warn (_("Illegal 19-bit code (%ld)"),
                         (long) imm_expr.X_add_number);
-             ip->insn_opcode |= imm_expr.X_add_number << 6;
+             ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CODE19;
              imm_expr.X_op = O_absent;
              s = expr_end;
              continue;
 
-            case 'C':           /* Coprocessor code */
-              my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-              if ((unsigned long) imm_expr.X_add_number >= (1<<25))
-               {
-                  as_warn (_("Coproccesor code > 25 bits (%ld)"),
-                          (long) imm_expr.X_add_number);
-                  imm_expr.X_add_number &= ((1<<25) - 1);
-               }
-              ip->insn_opcode |= imm_expr.X_add_number;
-              imm_expr.X_op = O_absent;
-              s = expr_end;
-              continue;
-
            case 'P':           /* Performance register */
-              my_getExpression (&imm_expr, s);
+             my_getExpression (&imm_expr, s);
              check_absolute_expr (ip, &imm_expr);
-              if (imm_expr.X_add_number != 0 && imm_expr.X_add_number != 1)
+             if (imm_expr.X_add_number != 0 && imm_expr.X_add_number != 1)
                {
-                  as_warn (_("Invalidate performance regster (%ld)"),
+                 as_warn (_("Invalid performance register (%ld)"),
                           (long) imm_expr.X_add_number);
-                  imm_expr.X_add_number &= 1;
+                 imm_expr.X_add_number &= OP_MASK_PERFREG;
                }
-              ip->insn_opcode |= (imm_expr.X_add_number << 1);
-              imm_expr.X_op = O_absent;
-              s = expr_end;
-              continue;
+             ip->insn_opcode |= (imm_expr.X_add_number << OP_SH_PERFREG);
+             imm_expr.X_op = O_absent;
+             s = expr_end;
+             continue;
 
            case 'b':           /* base register */
            case 'd':           /* destination register */
@@ -7336,11 +7771,12 @@ mips_ip (str, ip)
            case 'G':           /* coprocessor destination register */
            case 'x':           /* ignore register name */
            case 'z':           /* must be zero register */
+           case 'U':           /* destination register (clo/clz).  */
              s_reset = s;
              if (s[0] == '$')
                {
 
-                 if (isdigit ((unsigned char) s[1]))
+                 if (ISDIGIT (s[1]))
                    {
                      ++s;
                      regno = 0;
@@ -7350,7 +7786,7 @@ mips_ip (str, ip)
                          regno += *s - '0';
                          ++s;
                        }
-                     while (isdigit ((unsigned char) *s));
+                     while (ISDIGIT (*s));
                      if (regno > 31)
                        as_bad (_("Invalid register number (%d)"), regno);
                    }
@@ -7411,7 +7847,7 @@ mips_ip (str, ip)
                            }
                          else
                            goto notreg;
-                         }
+                       }
                      else
                        goto notreg;
                    }
@@ -7444,16 +7880,20 @@ mips_ip (str, ip)
                    case 's':
                    case 'v':
                    case 'b':
-                     ip->insn_opcode |= regno << 21;
+                     ip->insn_opcode |= regno << OP_SH_RS;
                      break;
                    case 'd':
                    case 'G':
-                     ip->insn_opcode |= regno << 11;
+                     ip->insn_opcode |= regno << OP_SH_RD;
+                     break;
+                   case 'U':
+                     ip->insn_opcode |= regno << OP_SH_RD;
+                     ip->insn_opcode |= regno << OP_SH_RT;
                      break;
                    case 'w':
                    case 't':
                    case 'E':
-                     ip->insn_opcode |= regno << 16;
+                     ip->insn_opcode |= regno << OP_SH_RT;
                      break;
                    case 'x':
                      /* This case exists because on the r3000 trunc
@@ -7484,10 +7924,10 @@ mips_ip (str, ip)
                {
                case 'r':
                case 'v':
-                 ip->insn_opcode |= lastregno << 21;
+                 ip->insn_opcode |= lastregno << OP_SH_RS;
                  continue;
                case 'w':
-                 ip->insn_opcode |= lastregno << 16;
+                 ip->insn_opcode |= lastregno << OP_SH_RT;
                  continue;
                }
              break;
@@ -7499,7 +7939,8 @@ mips_ip (str, ip)
            case 'V':
            case 'W':
              s_reset = s;
-             if (s[0] == '$' && s[1] == 'f' && isdigit ((unsigned char) s[2]))
+             if (s[0] == '$' && s[1] == 'f'
+                 && ISDIGIT (s[2]))
                {
                  s += 2;
                  regno = 0;
@@ -7509,13 +7950,13 @@ mips_ip (str, ip)
                      regno += *s - '0';
                      ++s;
                    }
-                 while (isdigit ((unsigned char) *s));
+                 while (ISDIGIT (*s));
 
                  if (regno > 31)
                    as_bad (_("Invalid float register number (%d)"), regno);
 
                  if ((regno & 1) != 0
-                     && ! ISA_HAS_64BIT_REGS (mips_opts.isa)
+                     && HAVE_32BIT_FPRS
                      && ! (strcmp (str, "mtc1") == 0
                            || strcmp (str, "mfc1") == 0
                            || strcmp (str, "lwc1") == 0
@@ -7540,18 +7981,18 @@ mips_ip (str, ip)
                  switch (c)
                    {
                    case 'D':
-                     ip->insn_opcode |= regno << 6;
+                     ip->insn_opcode |= regno << OP_SH_FD;
                      break;
                    case 'V':
                    case 'S':
-                     ip->insn_opcode |= regno << 11;
+                     ip->insn_opcode |= regno << OP_SH_FS;
                      break;
                    case 'W':
                    case 'T':
-                     ip->insn_opcode |= regno << 16;
+                     ip->insn_opcode |= regno << OP_SH_FT;
                      break;
                    case 'R':
-                     ip->insn_opcode |= regno << 21;
+                     ip->insn_opcode |= regno << OP_SH_FR;
                      break;
                    }
                  lastregno = regno;
@@ -7561,10 +8002,10 @@ mips_ip (str, ip)
              switch (*args++)
                {
                case 'V':
-                 ip->insn_opcode |= lastregno << 11;
+                 ip->insn_opcode |= lastregno << OP_SH_FS;
                  continue;
                case 'W':
-                 ip->insn_opcode |= lastregno << 16;
+                 ip->insn_opcode |= lastregno << OP_SH_FT;
                  continue;
                }
              break;
@@ -7579,7 +8020,7 @@ mips_ip (str, ip)
 
            case 'A':
              my_getExpression (&offset_expr, s);
-             imm_reloc = BFD_RELOC_32;
+             *imm_reloc = BFD_RELOC_32;
              s = expr_end;
              continue;
 
@@ -7589,6 +8030,7 @@ mips_ip (str, ip)
            case 'l':
              {
                int f64;
+               int using_gprs;
                char *save_in;
                char *err;
                unsigned char temp[8];
@@ -7622,9 +8064,15 @@ mips_ip (str, ip)
                    .lit4 inline easily; we need to put .lit8
                    somewhere in the data segment, and using .lit8
                    permits the linker to eventually combine identical
-                   .lit8 entries).  */
+                   .lit8 entries).
+
+                   The code below needs to know whether the target register
+                   is 32 or 64 bits wide.  It relies on the fact 'f' and
+                   'F' are used with GPR-based instructions and 'l' and
+                   'L' are used with FPR-based instructions.  */
 
                f64 = *args == 'F' || *args == 'L';
+               using_gprs = *args == 'F' || *args == 'f';
 
                save_in = input_line_pointer;
                input_line_pointer = s;
@@ -7657,18 +8105,24 @@ mips_ip (str, ip)
                  }
                else if (length > 4
                         && ! mips_disable_float_construction
+                        /* Constants can only be constructed in GPRs and
+                           copied to FPRs if the GPRs are at least as wide
+                           as the FPRs.  Force the constant into memory if
+                           we are using 64-bit FPRs but the GPRs are only
+                           32 bits wide.  */
+                        && (using_gprs
+                            || ! (HAVE_64BIT_FPRS && HAVE_32BIT_GPRS))
                         && ((temp[0] == 0 && temp[1] == 0)
                             || (temp[2] == 0 && temp[3] == 0))
                         && ((temp[4] == 0 && temp[5] == 0)
                             || (temp[6] == 0 && temp[7] == 0)))
                  {
-                   /* The value is simple enough to load with a
-                       couple of instructions.  In mips1 mode, set
-                       imm_expr to the high order 32 bits and
-                       offset_expr to the low order 32 bits.
-                       Otherwise, set imm_expr to the entire 64 bit
-                       constant.  */
-                   if (! ISA_HAS_64BIT_REGS (mips_opts.isa))
+                   /* The value is simple enough to load with a couple of
+                       instructions.  If using 32-bit registers, set
+                       imm_expr to the high order 32 bits and offset_expr to
+                       the low order 32 bits.  Otherwise, set imm_expr to
+                       the entire 64 bit constant.  */
+                   if (using_gprs ? HAVE_32BIT_GPRS : HAVE_32BIT_FPRS)
                      {
                        imm_expr.X_op = O_constant;
                        offset_expr.X_op = O_constant;
@@ -7778,32 +8232,57 @@ mips_ip (str, ip)
 
            case 'i':           /* 16 bit unsigned immediate */
            case 'j':           /* 16 bit signed immediate */
-             imm_reloc = BFD_RELOC_LO16;
+             *imm_reloc = BFD_RELOC_LO16;
              c = my_getSmallExpression (&imm_expr, s);
-             if (c != '\0')
+             if (c != S_EX_NONE)
                {
-                 if (c != 'l')
+                 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 == 'h')
+#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)
                        {
-                         imm_reloc = BFD_RELOC_HI16_S;
-                         imm_unmatched_hi = true;
-                       }
-                     else
-                       imm_reloc = BFD_RELOC_HI16;
+                         /* 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 == '\0' && imm_expr.X_op != O_constant)
+                 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))
+                          || imm_expr.X_add_number >= 0x10000)
+                         && imm_expr.X_op == O_constant))
                    {
                      if (insn + 1 < &mips_opcodes[NUMOPCODES] &&
                          !strcmp (insn->name, insn[1].name))
@@ -7833,13 +8312,13 @@ mips_ip (str, ip)
                    max = 0x8000;
                  else
                    max = 0x10000;
-                 if ((c == '\0' && imm_expr.X_op != O_constant)
+                 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)
+                          || imm_expr.X_add_number >= max)
+                         && imm_expr.X_op == O_constant)
                      || (more
                          && imm_expr.X_add_number < 0
-                         && ISA_HAS_64BIT_REGS (mips_opts.isa)
+                         && HAVE_64BIT_GPRS
                          && imm_expr.X_unsigned
                          && sizeof (imm_expr.X_add_number) <= 4))
                    {
@@ -7858,59 +8337,73 @@ mips_ip (str, ip)
 
              /* 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.  As a special hack, we accept the
-                difference of two local symbols as a constant.  This
-                is required to suppose embedded PIC switches, which
-                use an instruction which looks like
-                    lw $4,$L12-$LS12($4)
-                The problem with handling this in a more general
-                fashion is that the macro function doesn't expect to
-                see anything which can be handled in a single
-                constant instruction.  */
-             if (c == 0
+                code pattern.  */
+             if (c == S_EX_NONE
                  && (offset_expr.X_op != O_constant
                      || offset_expr.X_add_number >= 0x8000
-                     || offset_expr.X_add_number < -0x8000)
-                 && (mips_pic != EMBEDDED_PIC
-                     || offset_expr.X_op != O_subtract
-                     || (S_GET_SEGMENT (offset_expr.X_op_symbol)
-                         != now_seg)))
+                     || offset_expr.X_add_number < -0x8000))
                break;
 
-             if (c == 'h' || c == 'H')
+             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;
+             *offset_reloc = BFD_RELOC_LO16;
              s = expr_end;
              continue;
 
            case 'p':           /* pc relative offset */
-             offset_reloc = BFD_RELOC_16_PCREL_S2;
+             if (mips_pic == EMBEDDED_PIC)
+               *offset_reloc = BFD_RELOC_16_PCREL_S2;
+             else
+               *offset_reloc = BFD_RELOC_16_PCREL;
              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)
+             *imm_reloc = BFD_RELOC_LO16;
+             if (c != S_EX_NONE)
                {
-                 if (c != 'l')
+                 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 == 'h')
+                     else if (c == S_EX_HI)
                        {
-                         imm_reloc = BFD_RELOC_HI16_S;
+                         *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;
+                       *imm_reloc = BFD_RELOC_HI16;
                    }
                  else if (imm_expr.X_op == O_constant)
                    imm_expr.X_add_number &= 0xffff;
@@ -7925,7 +8418,7 @@ mips_ip (str, ip)
            case 'a':           /* 26 bit address */
              my_getExpression (&offset_expr, s);
              s = expr_end;
-             offset_reloc = BFD_RELOC_MIPS_JMP;
+             *offset_reloc = BFD_RELOC_MIPS_JMP;
              continue;
 
            case 'N':           /* 3 bit branch condition code */
@@ -7940,19 +8433,19 @@ mips_ip (str, ip)
                  regno += *s - '0';
                  ++s;
                }
-             while (isdigit ((unsigned char) *s));
+             while (ISDIGIT (*s));
              if (regno > 7)
                as_bad (_("invalid condition code register $fcc%d"), regno);
              if (*args == 'N')
                ip->insn_opcode |= regno << OP_SH_BCC;
              else
                ip->insn_opcode |= regno << OP_SH_CCC;
-              continue;
+             continue;
 
            case 'H':
              if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
                s += 2;
-             if (isdigit ((unsigned char) *s))
+             if (ISDIGIT (*s))
                {
                  c = 0;
                  do
@@ -7961,7 +8454,7 @@ mips_ip (str, ip)
                      c += *s - '0';
                      ++s;
                    }
-                 while (isdigit ((unsigned char) *s));
+                 while (ISDIGIT (*s));
                }
              else
                c = 8; /* Invalid sel value.  */
@@ -7983,8 +8476,11 @@ mips_ip (str, ip)
        {
          ++insn;
          s = argsStart;
+         insn_error = _("illegal operands");
          continue;
        }
+      if (save_c)
+       *(--s) = save_c;
       insn_error = _("illegal operands");
       return;
     }
@@ -8015,7 +8511,7 @@ mips16_ip (str, ip)
   mips16_small = false;
   mips16_ext = false;
 
-  for (s = str; islower ((unsigned char) *s); ++s)
+  for (s = str; ISLOWER (*s); ++s)
     ;
   switch (*s)
     {
@@ -8065,9 +8561,13 @@ mips16_ip (str, ip)
       ip->insn_opcode = insn->match;
       ip->use_extend = false;
       imm_expr.X_op = O_absent;
-      imm_reloc = BFD_RELOC_UNUSED;
+      imm_reloc[0] = BFD_RELOC_UNUSED;
+      imm_reloc[1] = BFD_RELOC_UNUSED;
+      imm_reloc[2] = BFD_RELOC_UNUSED;
       offset_expr.X_op = O_absent;
-      offset_reloc = BFD_RELOC_UNUSED;
+      offset_reloc[0] = BFD_RELOC_UNUSED;
+      offset_reloc[1] = BFD_RELOC_UNUSED;
+      offset_reloc[2] = BFD_RELOC_UNUSED;
       for (args = insn->args; 1; ++args)
        {
          int c;
@@ -8087,16 +8587,15 @@ mips16_ip (str, ip)
                {
                  /* Stuff the immediate value in now, if we can.  */
                  if (imm_expr.X_op == O_constant
-                     && imm_reloc > BFD_RELOC_UNUSED
+                     && *imm_reloc > BFD_RELOC_UNUSED
                      && insn->pinfo != INSN_MACRO)
                    {
-                     mips16_immed ((char *) NULL, 0,
-                                   imm_reloc - BFD_RELOC_UNUSED,
+                     mips16_immed (NULL, 0, *imm_reloc - BFD_RELOC_UNUSED,
                                    imm_expr.X_add_number, true, mips16_small,
                                    mips16_ext, &ip->insn_opcode,
                                    &ip->use_extend, &ip->extend);
                      imm_expr.X_op = O_absent;
-                     imm_reloc = BFD_RELOC_UNUSED;
+                     *imm_reloc = BFD_RELOC_UNUSED;
                    }
 
                  return;
@@ -8148,7 +8647,7 @@ mips16_ip (str, ip)
              if (s[0] != '$')
                break;
              s_reset = s;
-             if (isdigit ((unsigned char) s[1]))
+             if (ISDIGIT (s[1]))
                {
                  ++s;
                  regno = 0;
@@ -8158,7 +8657,7 @@ mips16_ip (str, ip)
                      regno += *s - '0';
                      ++s;
                    }
-                 while (isdigit ((unsigned char) *s));
+                 while (ISDIGIT (*s));
                  if (regno > 31)
                    {
                      as_bad (_("invalid register number (%d)"), regno);
@@ -8321,7 +8820,7 @@ mips16_ip (str, ip)
                  if (imm_expr.X_op == O_symbol)
                    {
                      mips16_ext = true;
-                     imm_reloc = BFD_RELOC_MIPS16_GPREL;
+                     *imm_reloc = BFD_RELOC_MIPS16_GPREL;
                      s = expr_end;
                      ip->use_extend = true;
                      ip->extend = 0;
@@ -8348,7 +8847,7 @@ mips16_ip (str, ip)
                         explicit extensions correctly.  */
                      imm_expr.X_op = O_constant;
                      imm_expr.X_add_number = 0;
-                     imm_reloc = (int) BFD_RELOC_UNUSED + c;
+                     *imm_reloc = (int) BFD_RELOC_UNUSED + c;
                      continue;
                    }
 
@@ -8356,7 +8855,7 @@ mips16_ip (str, ip)
                }
 
              /* We need to relax this instruction.  */
-             imm_reloc = (int) BFD_RELOC_UNUSED + c;
+             *imm_reloc = (int) BFD_RELOC_UNUSED + c;
              s = expr_end;
              continue;
 
@@ -8374,7 +8873,7 @@ mips16_ip (str, ip)
                break;
 
              /* We need to relax this instruction.  */
-             offset_reloc = (int) BFD_RELOC_UNUSED + c;
+             *offset_reloc = (int) BFD_RELOC_UNUSED + c;
              s = expr_end;
              continue;
 
@@ -8396,7 +8895,7 @@ mips16_ip (str, ip)
            case 'a':           /* 26 bit address */
              my_getExpression (&offset_expr, s);
              s = expr_end;
-             offset_reloc = BFD_RELOC_MIPS16_JMP;
+             *offset_reloc = BFD_RELOC_MIPS16_JMP;
              ip->insn_opcode <<= 16;
              continue;
 
@@ -8429,7 +8928,7 @@ mips16_ip (str, ip)
                        ++s;
                      }
                    reg1 = 0;
-                   while (isdigit ((unsigned char) *s))
+                   while (ISDIGIT (*s))
                      {
                        reg1 *= 10;
                        reg1 += *s - '0';
@@ -8456,7 +8955,7 @@ mips16_ip (str, ip)
                              }
                          }
                        reg2 = 0;
-                       while (isdigit ((unsigned char) *s))
+                       while (ISDIGIT (*s))
                          {
                            reg2 *= 10;
                            reg2 += *s - '0';
@@ -8648,7 +9147,8 @@ mips16_immed (file, line, type, val, warn, small, ext, insn, use_extend,
     needext = false;
 
   if (warn && ext && ! needext)
-    as_warn_where (file, line, _("extended operand requested but not required"));
+    as_warn_where (file, line,
+                  _("extended operand requested but not required"));
   if (small && needext)
     as_bad_where (file, line, _("invalid unextended operand value"));
 
@@ -8702,94 +9202,193 @@ mips16_immed (file, line, type, val, warn, small, ext, insn, use_extend,
     }
 }
 \f
-#define LP '('
-#define RP ')'
+static struct percent_op_match
+{
+   const char *str;
+   const enum small_ex_type type;
+} percent_op[] =
+{
+#ifdef OBJ_ELF
+  {"%half", S_EX_HALF},
+#endif
+  {"%hi", S_EX_HI},
+  {"%lo", S_EX_LO},
+#ifdef OBJ_ELF
+  {"%gp_rel", S_EX_GP_REL},
+  {"%got", S_EX_GOT},
+  {"%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},
+  {"%neg", S_EX_NEG},
+  {"%higher", S_EX_HIGHER},
+  {"%highest", S_EX_HIGHEST},
+  {"%call_hi", S_EX_CALL_HI},
+  {"%call_lo", S_EX_CALL_LO}
+#endif
+};
+
+/* 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_getSmallExpression (ep, str)
-     expressionS *ep;
-     char *str;
+my_getSmallParser (str, len, nestlevel)
+     char **str;
+     unsigned int *len;
+     int *nestlevel;
 {
-  char *sp;
-  int c = 0;
-
-  if (*str == ' ')
-    str++;
-  if (*str == LP
-      || (*str == '%' &&
-         ((str[1] == 'h' && str[2] == 'i')
-          || (str[1] == 'H' && str[2] == 'I')
-          || (str[1] == 'l' && str[2] == 'o'))
-         && str[3] == LP))
-    {
-      if (*str == LP)
-       c = 0;
-      else
-       {
-         c = str[1];
-         str += 3;
-       }
+  int type = S_EX_NONE;
 
-      /*
-       * A small expression may be followed by a base register.
-       * Scan to the end of this operand, and then back over a possible
-       * base register.  Then scan the small expression up to that
-       * point.  (Based on code in sparc.c...)
-       */
-      for (sp = str; *sp && *sp != ','; sp++)
-       ;
-      if (sp - 4 >= str && sp[-1] == RP)
+  *len = 0;
+  *str += strspn (*str, " \t");
+  if (**str == '(')
+    {
+      char *b = *str + 1 + strspn (*str + 1, " \t");
+      char *e;
+
+      /* Check for base register.  */
+      if (b[0] == '$')
        {
-         if (isdigit ((unsigned char) sp[-2]))
+         if (strchr (b, ')')
+             && (e = b + strcspn (b, ") \t"))
+             && e - b > 1 && e - b < 4)
            {
-             for (sp -= 3; sp >= str && isdigit ((unsigned char) *sp); sp--)
-               ;
-             if (*sp == '$' && sp > str && sp[-1] == LP)
-               {
-                 sp--;
-                 goto do_it;
-               }
+              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;
+                }
            }
-         else if (sp - 5 >= str
-                  && sp[-5] == LP
-                  && sp[-4] == '$'
-                  && ((sp[-3] == 'f' && sp[-2] == 'p')
-                      || (sp[-3] == 's' && sp[-2] == 'p')
-                      || (sp[-3] == 'g' && sp[-2] == 'p')
-                      || (sp[-3] == 'a' && sp[-2] == 't')))
+       }
+      else if (b[0] == '%')
+       {
+         *str = b;
+         goto percent_op;
+       }
+
+      /* Some other expression in the braces.  */
+      *len = strcspn (*str, ")") + 1;
+    }
+  /* Check for percent_op.  */
+  else if (*str[0] == '%')
+    {
+      char *tmp;
+      unsigned int i;
+
+percent_op:
+      tmp = *str + 1;
+      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
            {
-             sp -= 5;
-           do_it:
-             if (sp == str)
-               {
-                 /* no expression means zero offset */
-                 if (c)
-                   {
-                     /* %xx(reg) is an error */
-                     ep->X_op = O_absent;
-                     expr_end = str - 3;
-                   }
-                 else
-                   {
-                     ep->X_op = O_constant;
-                     expr_end = sp;
-                   }
-                 ep->X_add_symbol = NULL;
-                 ep->X_op_symbol = NULL;
-                 ep->X_add_number = 0;
-               }
-             else
-               {
-                 *sp = '\0';
-                 my_getExpression (ep, str);
-                 *sp = LP;
-               }
-             return c;
+             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;
+
+             *len = strlen (percent_op[i].str);
+             (*nestlevel)++;
+             return type;
            }
        }
     }
-  my_getExpression (ep, str);
-  return c;                    /* => %hi or %lo encountered */
+
+  /* Any other expression.  */
+  return S_EX_NONE;
+}
+
+static int
+my_getSmallExpression (ep, str)
+     expressionS *ep;
+     char *str;
+{
+  static char *oldstr = NULL;
+  int c = S_EX_NONE;
+  int oldc;
+  int nest_level = 0;
+  unsigned int len;
+
+  /* Don't update oldstr if the last call had nested percent_op's.  */
+  if (! oldstr)
+    oldstr = str;
+
+  do
+    {
+      oldc = c;
+      c = my_getSmallParser (&str, &len, &nest_level);
+      if (c != S_EX_NONE && c != S_EX_REGISTER)
+       str += len;
+    }
+  while (c != S_EX_NONE && c != S_EX_REGISTER);
+
+  /* A percent_op was encountered.  */
+  if (nest_level)
+    {
+      /* Don't try to get an expression if it is already blanked out.  */
+      if (*(str + strspn (str + 1, " )")) != ')')
+       {
+         char save;
+
+         save = *(str + len);
+         *(str + len) = '\0';
+         my_getExpression (ep, str);
+         *(str + len) = save;
+       }
+      if (nest_level > 1)
+       {
+         /* blank out including the % sign.  */
+         char *p = strrchr (oldstr, '%');
+         memset (p, ' ', str - p + len);
+         str = oldstr;
+       }
+      else
+       {
+         expr_end = strchr (str, ')') + 1;
+       }
+      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;
+    }
+  else
+    {
+      as_fatal(_("internal error"));
+    }
+
+  if (nest_level <= 1)
+    oldstr = NULL;
+
+  return c;
 }
 
 static void
@@ -8798,6 +9397,7 @@ my_getExpression (ep, str)
      char *str;
 {
   char *save_in;
+  valueT val;
 
   save_in = input_line_pointer;
   input_line_pointer = str;
@@ -8815,8 +9415,8 @@ my_getExpression (ep, str)
       && S_GET_SEGMENT (ep->X_add_symbol) == now_seg
       && symbol_get_frag (ep->X_add_symbol) == frag_now
       && symbol_constant_p (ep->X_add_symbol)
-      && S_GET_VALUE (ep->X_add_symbol) == frag_now_fix ())
-    S_SET_VALUE (ep->X_add_symbol, S_GET_VALUE (ep->X_add_symbol) + 1);
+      && (val = S_GET_VALUE (ep->X_add_symbol)) == frag_now_fix ())
+    S_SET_VALUE (ep->X_add_symbol, val + 1);
 }
 
 /* Turn a string in input_line_pointer into a floating point constant
@@ -8888,7 +9488,28 @@ md_number_to_chars (buf, val, n)
     number_to_chars_littleendian (buf, val, n);
 }
 \f
-CONST char *md_shortopts = "O::g::G:";
+#ifdef OBJ_ELF
+static int support_64bit_objects(void)
+{
+  const char **list, **l;
+
+  list = bfd_target_list ();
+  for (l = list; *l != NULL; l++)
+#ifdef TE_TMIPS
+    /* This is traditional mips */
+    if (strcmp (*l, "elf64-tradbigmips") == 0
+       || strcmp (*l, "elf64-tradlittlemips") == 0)
+#else
+    if (strcmp (*l, "elf64-bigmips") == 0
+       || strcmp (*l, "elf64-littlemips") == 0)
+#endif
+      break;
+  free (list);
+  return (*l != NULL);
+}
+#endif /* OBJ_ELF */
+
+CONST char *md_shortopts = "nO::g::G:";
 
 struct option md_longopts[] =
 {
@@ -8901,74 +9522,82 @@ struct option md_longopts[] =
   {"mips3", no_argument, NULL, OPTION_MIPS3},
 #define OPTION_MIPS4 (OPTION_MD_BASE + 4)
   {"mips4", no_argument, NULL, OPTION_MIPS4},
-#define OPTION_MCPU (OPTION_MD_BASE + 5)
-  {"mcpu", required_argument, NULL, OPTION_MCPU},
-#define OPTION_MEMBEDDED_PIC (OPTION_MD_BASE + 6)
+#define OPTION_MIPS5 (OPTION_MD_BASE + 5)
+  {"mips5", no_argument, NULL, OPTION_MIPS5},
+#define OPTION_MIPS32 (OPTION_MD_BASE + 6)
+  {"mips32", no_argument, NULL, OPTION_MIPS32},
+#define OPTION_MIPS64 (OPTION_MD_BASE + 7)
+  {"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 + 7)
+#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 + 8)
+#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 + 9)
+#define OPTION_EB (OPTION_MD_BASE + 11)
   {"EB", no_argument, NULL, OPTION_EB},
-#define OPTION_EL (OPTION_MD_BASE + 10)
+#define OPTION_EL (OPTION_MD_BASE + 12)
   {"EL", no_argument, NULL, OPTION_EL},
-#define OPTION_M4650 (OPTION_MD_BASE + 11)
+#define OPTION_MIPS16 (OPTION_MD_BASE + 13)
+  {"mips16", no_argument, NULL, OPTION_MIPS16},
+#define OPTION_NO_MIPS16 (OPTION_MD_BASE + 14)
+  {"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)
   {"m4650", no_argument, NULL, OPTION_M4650},
-#define OPTION_NO_M4650 (OPTION_MD_BASE + 12)
+#define OPTION_NO_M4650 (OPTION_MD_BASE + 25)
   {"no-m4650", no_argument, NULL, OPTION_NO_M4650},
-#define OPTION_M4010 (OPTION_MD_BASE + 13)
+#define OPTION_M4010 (OPTION_MD_BASE + 26)
   {"m4010", no_argument, NULL, OPTION_M4010},
-#define OPTION_NO_M4010 (OPTION_MD_BASE + 14)
+#define OPTION_NO_M4010 (OPTION_MD_BASE + 27)
   {"no-m4010", no_argument, NULL, OPTION_NO_M4010},
-#define OPTION_M4100 (OPTION_MD_BASE + 15)
+#define OPTION_M4100 (OPTION_MD_BASE + 28)
   {"m4100", no_argument, NULL, OPTION_M4100},
-#define OPTION_NO_M4100 (OPTION_MD_BASE + 16)
+#define OPTION_NO_M4100 (OPTION_MD_BASE + 29)
   {"no-m4100", no_argument, NULL, OPTION_NO_M4100},
-#define OPTION_MIPS16 (OPTION_MD_BASE + 17)
-  {"mips16", no_argument, NULL, OPTION_MIPS16},
-#define OPTION_NO_MIPS16 (OPTION_MD_BASE + 18)
-  {"no-mips16", no_argument, NULL, OPTION_NO_MIPS16},
-#define OPTION_M3900 (OPTION_MD_BASE + 19)
+#define OPTION_M3900 (OPTION_MD_BASE + 30)
   {"m3900", no_argument, NULL, OPTION_M3900},
-#define OPTION_NO_M3900 (OPTION_MD_BASE + 20)
+#define OPTION_NO_M3900 (OPTION_MD_BASE + 31)
   {"no-m3900", no_argument, NULL, OPTION_NO_M3900},
-#define OPTION_MABI (OPTION_MD_BASE + 21)
-  {"mabi", required_argument, NULL, OPTION_MABI},
-#define OPTION_M7000_HILO_FIX (OPTION_MD_BASE + 22)
-  {"mfix7000", no_argument, NULL, OPTION_M7000_HILO_FIX},
-#define OPTION_NO_M7000_HILO_FIX (OPTION_MD_BASE + 23)
-  {"no-fix-7000", no_argument, NULL, OPTION_NO_M7000_HILO_FIX},
-#define OPTION_GP32 (OPTION_MD_BASE + 24)
-  {"mgp32", no_argument, NULL, OPTION_GP32},
-#define OPTION_GP64 (OPTION_MD_BASE + 25)
+#define OPTION_GP64 (OPTION_MD_BASE + 32)
   {"mgp64", no_argument, NULL, OPTION_GP64},
-#define OPTION_CONSTRUCT_FLOATS (OPTION_MD_BASE + 26)
-  {"construct-floats", no_argument, NULL, OPTION_CONSTRUCT_FLOATS},
-#define OPTION_NO_CONSTRUCT_FLOATS (OPTION_MD_BASE + 27)
-  {"no-construct-floats", no_argument, NULL, OPTION_NO_CONSTRUCT_FLOATS},
-#define OPTION_MIPS32 (OPTION_MD_BASE + 28)
-  {"mips32", no_argument, NULL, OPTION_MIPS32},
-#define OPTION_NO_MIPS32 (OPTION_MD_BASE + 29)
-  {"no-mips32", no_argument, NULL, OPTION_NO_MIPS32},
-
 #ifdef OBJ_ELF
-#define OPTION_ELF_BASE    (OPTION_MD_BASE + 35)
+#define OPTION_ELF_BASE    (OPTION_MD_BASE + 33)
 #define OPTION_CALL_SHARED (OPTION_ELF_BASE + 0)
-#define OPTION_NON_SHARED  (OPTION_ELF_BASE + 1)
-#define OPTION_XGOT        (OPTION_ELF_BASE + 2)
-#define OPTION_32         (OPTION_ELF_BASE + 3)
-#define OPTION_64          (OPTION_ELF_BASE + 4)
   {"KPIC",        no_argument, NULL, OPTION_CALL_SHARED},
   {"call_shared", no_argument, NULL, OPTION_CALL_SHARED},
+#define OPTION_NON_SHARED  (OPTION_ELF_BASE + 1)
   {"non_shared",  no_argument, NULL, OPTION_NON_SHARED},
+#define OPTION_XGOT        (OPTION_ELF_BASE + 2)
   {"xgot",        no_argument, NULL, OPTION_XGOT},
+#define OPTION_MABI        (OPTION_ELF_BASE + 3)
+  {"mabi", required_argument, NULL, OPTION_MABI},
+#define OPTION_32         (OPTION_ELF_BASE + 4)
   {"32",          no_argument, NULL, OPTION_32},
+#define OPTION_N32        (OPTION_ELF_BASE + 5)
+  {"n32",         no_argument, NULL, OPTION_N32},
+#define OPTION_64          (OPTION_ELF_BASE + 6)
   {"64",          no_argument, NULL, OPTION_64},
-#endif
-
+#endif /* OBJ_ELF */
   {NULL, no_argument, NULL, 0}
 };
 size_t md_longopts_size = sizeof (md_longopts);
@@ -9004,6 +9633,10 @@ md_parse_option (c, arg)
       target_big_endian = 0;
       break;
 
+    case 'n':
+      warn_nops = 1;
+      break;
+
     case 'O':
       if (arg && arg[1] == '0')
        mips_optimize = 1;
@@ -9024,188 +9657,130 @@ md_parse_option (c, arg)
       break;
 
     case OPTION_MIPS1:
-      mips_opts.isa = 1;
+      mips_opts.isa = ISA_MIPS1;
       break;
 
     case OPTION_MIPS2:
-      mips_opts.isa = 2;
+      mips_opts.isa = ISA_MIPS2;
       break;
 
     case OPTION_MIPS3:
-      mips_opts.isa = 3;
+      mips_opts.isa = ISA_MIPS3;
       break;
 
     case OPTION_MIPS4:
-      mips_opts.isa = 4;
+      mips_opts.isa = ISA_MIPS4;
       break;
 
+    case OPTION_MIPS5:
+      mips_opts.isa = ISA_MIPS5;
+      break;
+
+    case OPTION_MIPS32:
+      mips_opts.isa = ISA_MIPS32;
+      break;
+
+    case OPTION_MIPS64:
+      mips_opts.isa = ISA_MIPS64;
+      break;
+
+    case OPTION_MTUNE:
+    case OPTION_MARCH:
     case OPTION_MCPU:
       {
-       char *p;
+       int cpu = CPU_UNKNOWN;
 
-       /* Identify the processor type */
-       p = arg;
-       if (strcmp (p, "default") == 0
-           || strcmp (p, "DEFAULT") == 0)
-         mips_cpu = -1;
-       else
+       /* Identify the processor type.  */
+       if (strcasecmp (arg, "default") != 0)
          {
-           int sv = 0;
-
-           /* We need to cope with the various "vr" prefixes for the 4300
-              processor.  */
-           if (*p == 'v' || *p == 'V')
-             {
-               sv = 1;
-               p++;
-             }
-
-           if (*p == 'r' || *p == 'R')
-             p++;
+           const struct mips_cpu_info *ci;
 
-           mips_cpu = -1;
-           switch (*p)
+           ci = mips_cpu_info_from_name (arg);
+           if (ci == NULL || ci->is_isa)
              {
-             case '1':
-               if (strcmp (p, "10000") == 0
-                   || strcmp (p, "10k") == 0
-                   || strcmp (p, "10K") == 0)
-                 mips_cpu = CPU_R10000;
-               break;
-
-             case '2':
-               if (strcmp (p, "2000") == 0
-                   || strcmp (p, "2k") == 0
-                   || strcmp (p, "2K") == 0)
-                 mips_cpu = CPU_R2000;
-               break;
-
-             case '3':
-               if (strcmp (p, "3000") == 0
-                   || strcmp (p, "3k") == 0
-                   || strcmp (p, "3K") == 0)
-                 mips_cpu = CPU_R3000;
-                else if (strcmp (p, "3900") == 0)
-                  mips_cpu = CPU_R3900;
-               break;
-
-             case '4':
-               if (strcmp (p, "4000") == 0
-                   || strcmp (p, "4k") == 0
-                   || strcmp (p, "4K") == 0)
-                 mips_cpu = CPU_R4000;
-               else if (strcmp (p, "4100") == 0)
-                    mips_cpu = CPU_VR4100;
-               else if (strcmp (p, "4111") == 0)
-                    mips_cpu = CPU_R4111;
-               else if (strcmp (p, "4300") == 0)
-                 mips_cpu = CPU_R4300;
-               else if (strcmp (p, "4400") == 0)
-                 mips_cpu = CPU_R4400;
-               else if (strcmp (p, "4600") == 0)
-                 mips_cpu = CPU_R4600;
-               else if (strcmp (p, "4650") == 0)
-                   mips_cpu = CPU_R4650;
-               else if (strcmp (p, "4010") == 0)
-                  mips_cpu = CPU_R4010;
-               else if (strcmp (p, "4Kc") == 0
-                        || strcmp (p, "4Kp") == 0
-                        || strcmp (p, "4Km") == 0)
-                 mips_cpu = CPU_MIPS32;
-               break;
-
-             case '5':
-               if (strcmp (p, "5000") == 0
-                   || strcmp (p, "5k") == 0
-                   || strcmp (p, "5K") == 0)
-                 mips_cpu = CPU_R5000;
-               break;
-
-             case '6':
-               if (strcmp (p, "6000") == 0
-                   || strcmp (p, "6k") == 0
-                   || strcmp (p, "6K") == 0)
-                 mips_cpu = CPU_R6000;
-               break;
-
-             case '8':
-               if (strcmp (p, "8000") == 0
-                   || strcmp (p, "8k") == 0
-                   || strcmp (p, "8K") == 0)
-                 mips_cpu = CPU_R8000;
-               break;
-
-             case 'o':
-               if (strcmp (p, "orion") == 0)
-                 mips_cpu = CPU_R4600;
-               break;
-
-             case 'm':
-             case 'M':
-               switch (atoi (p + 1))
+               switch (c)
                  {
-                 case 5200:
-                 case 5230:
-                 case 5231:
-                 case 5261:
-                 case 5721:
-                 case 7000:
-                   mips_cpu = CPU_R5000;
+                 case OPTION_MTUNE:
+                   as_fatal (_("invalid architecture -mtune=%s"), arg);
                    break;
-                 default:
+                 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;
+         }
 
-           if (sv
-               && (mips_cpu != CPU_R4300
-                   && mips_cpu != CPU_VR4100
-                   && mips_cpu != CPU_R4111
-                   && mips_cpu != CPU_R5000))
-             {
-               as_bad (_("ignoring invalid leading 'v' in -mcpu=%s switch"), arg);
-               return 0;
-             }
-
-           if (mips_cpu == -1)
-             {
-               as_bad (_("invalid architecture -mcpu=%s"), arg);
-               return 0;
-             }
+       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;
          }
       }
       break;
 
     case OPTION_M4650:
-      mips_cpu = CPU_R4650;
+      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;
       break;
 
     case OPTION_NO_M4650:
       break;
 
     case OPTION_M4010:
-      mips_cpu = CPU_R4010;
+      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;
       break;
 
     case OPTION_NO_M4010:
       break;
 
     case OPTION_M4100:
-      mips_cpu = CPU_VR4100;
+      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;
       break;
 
     case OPTION_NO_M4100:
       break;
 
-    case OPTION_MIPS32:
-      mips_cpu = CPU_MIPS32;
-      break;
-
-    case OPTION_NO_MIPS32:
-      break;
-
     case OPTION_M3900:
-      mips_cpu = CPU_R3900;
+      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;
       break;
 
     case OPTION_NO_M3900:
@@ -9231,6 +9806,7 @@ md_parse_option (c, arg)
       g_switch_value = 0x7fffffff;
       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
         intended to be compatible with Irix 5.  */
@@ -9264,6 +9840,7 @@ md_parse_option (c, arg)
     case OPTION_XGOT:
       mips_big_got = 1;
       break;
+#endif /* OBJ_ELF */
 
     case 'G':
       if (! USE_GLOBAL_POINTER_OPT)
@@ -9281,58 +9858,63 @@ md_parse_option (c, arg)
       g_switch_seen = 1;
       break;
 
+#ifdef OBJ_ELF
       /* The -32 and -64 options tell the assembler to output the 32
          bit or the 64 bit MIPS ELF format.  */
     case OPTION_32:
-      mips_64 = 0;
+      mips_opts.abi = O32_ABI;
       break;
 
-    case OPTION_64:
-      {
-       const char **list, **l;
+    case OPTION_N32:
+      mips_opts.abi = N32_ABI;
+      break;
 
-       list = bfd_target_list ();
-       for (l = list; *l != NULL; l++)
-         if (strcmp (*l, "elf64-bigmips") == 0
-             || strcmp (*l, "elf64-littlemips") == 0)
-           break;
-       if (*l == NULL)
-         as_fatal (_("No compiled in support for 64 bit object file format"));
-       free (list);
-       mips_64 = 1;
-      }
+    case OPTION_64:
+      mips_opts.abi = N64_ABI;
+      if (! support_64bit_objects())
+       as_fatal (_("No compiled in support for 64 bit object file format"));
       break;
+#endif /* OBJ_ELF */
 
     case OPTION_GP32:
-      mips_gp32 = 1;
-      mips_64 = 0;
-
-      /* We deliberately don't allow "-gp32" to set the MIPS_32BITMODE
-        flag in object files because to do so would make it
-        impossible to link with libraries compiled without "-gp32".
-        This is unnecessarily restrictive.
-
-        We could solve this problem by adding "-gp32" multilibs to
-        gcc, but to set this flag before gcc is built with such
-        multilibs will break too many systems.  */
-
-/*    mips_32bitmode = 1; */
+      file_mips_gp32 = 1;
+      if (mips_opts.abi != O32_ABI)
+       mips_opts.abi = NO_ABI;
       break;
 
     case OPTION_GP64:
-      mips_gp32 = 0;
-      mips_64 = 1;
-/*    mips_32bitmode = 0; */
+      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;
 
+#ifdef OBJ_ELF
     case OPTION_MABI:
-      if (strcmp (arg,"32") == 0
-         || strcmp (arg,"n32") == 0
-         || strcmp (arg,"64") == 0
-         || strcmp (arg,"o64") == 0
-         || strcmp (arg,"eabi") == 0)
-       mips_abi_string = arg;
+      if (strcmp (arg, "32") == 0)
+       mips_opts.abi = O32_ABI;
+      else if (strcmp (arg, "o64") == 0)
+       mips_opts.abi = O64_ABI;
+      else if (strcmp (arg, "n32") == 0)
+       mips_opts.abi = N32_ABI;
+      else if (strcmp (arg, "64") == 0)
+       {
+         mips_opts.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;
+      else
+       mips_opts.abi = NO_ABI;
       break;
+#endif /* OBJ_ELF */
 
     case OPTION_M7000_HILO_FIX:
       mips_7000_hilo_fix = true;
@@ -9385,20 +9967,23 @@ md_show_usage (stream)
 {
   int column, first;
 
-  fprintf(stream, _("\
+  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 uneeded NOPs or swap branches\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, _("\
+  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\
--mcpu=CPU              generate code for CPU, where CPU is one of:\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"));
 
   first = 1;
 
@@ -9414,16 +9999,20 @@ MIPS options:\n\
   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, "4Kc", &column, &first);
-  show (stream, "4Kp", &column, &first);
-  show (stream, "4Km", &column, &first);
+  show (stream, "12000", &column, &first);
+  show (stream, "sb1", &column, &first);
   fputc ('\n', stream);
 
   fprintf (stream, _("\
--mCPU                  equivalent to -mcpu=CPU.\n\
+-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"));
 
@@ -9436,24 +10025,25 @@ MIPS options:\n\
   fputc ('\n', stream);
 
   fprintf (stream, _("\
--mips32                 generate MIPS32 instructions\n"));
-
-  fprintf(stream, _("\
 -mips16                        generate mips16 instructions\n\
 -no-mips16             do not generate mips16 instructions\n"));
-  fprintf(stream, _("\
+  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, _("\
+  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\
--32                    create 32 bit object file (default)\n\
--64                    create 64 bit object file\n"));
+-32                    create o32 ABI object file (default)\n\
+-n32                   create n32 ABI object file\n\
+-64                    create 64 ABI object file\n"));
 #endif
 }
 \f
@@ -9462,7 +10052,7 @@ mips_init_after_args ()
 {
   /* initialize opcodes */
   bfd_mips_num_opcodes = bfd_mips_num_builtin_opcodes;
-  mips_opcodes = (struct mips_opcode*) mips_builtin_opcodes;
+  mips_opcodes = (struct mips_opcode *) mips_builtin_opcodes;
 }
 
 long
@@ -9475,44 +10065,16 @@ md_pcrel_from (fixP)
     {
       /* This makes a branch to an undefined symbol be a branch to the
         current location.  */
-      return 4;
+      if (mips_pic == EMBEDDED_PIC)
+       return 4;
+      else
+       return 1;
     }
 
   /* return the address of the delay slot */
   return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
 }
 
-/* This is called by emit_expr via TC_CONS_FIX_NEW when creating a
-   reloc for a cons.  We could use the definition there, except that
-   we want to handle 64 bit relocs specially.  */
-
-void
-cons_fix_new_mips (frag, where, nbytes, exp)
-     fragS *frag ATTRIBUTE_UNUSED;
-     int where;
-     unsigned int nbytes;
-     expressionS *exp;
-{
-#ifndef OBJ_ELF
-  /* If we are assembling in 32 bit mode, turn an 8 byte reloc into a
-     4 byte reloc.  */
-  if (nbytes == 8 && ! mips_64)
-    {
-      if (target_big_endian)
-       where += 4;
-      nbytes = 4;
-    }
-#endif
-
-  if (nbytes != 2 && nbytes != 4 && nbytes != 8)
-    as_bad (_("Unsupported reloc size %d"), nbytes);
-
-  fix_new_exp (frag_now, where, (int) nbytes, exp, 0,
-              (nbytes == 2
-               ? BFD_RELOC_16
-               : (nbytes == 4 ? BFD_RELOC_32 : BFD_RELOC_64)));
-}
-
 /* This is called before the symbol table is processed.  In order to
    work with gcc when using mips-tfile, we must keep all local labels.
    However, in other cases, we want to discard them.  If we were
@@ -9532,7 +10094,7 @@ mips_frob_file_before_adjust ()
 }
 
 /* Sort any unmatched HI16_S relocs so that they immediately precede
-   the corresponding LO reloc.  This is called before md_apply_fix and
+   the corresponding LO reloc.  This is called before md_apply_fix3 and
    tc_gen_reloc.  Unmatched HI16_S relocs can only be generated by
    explicit use of the %hi modifier.  */
 
@@ -9629,9 +10191,11 @@ mips_frob_file ()
 
 /* When generating embedded PIC code we must keep all PC relative
    relocations, in case the linker has to relax a call.  We also need
-   to keep relocations for switch table entries.  */
+   to keep relocations for switch table entries.
+
+   We may have combined relocations without symbols in the N32/N64 ABI.
+   We have to prevent gas from dropping them.  */
 
-/*ARGSUSED*/
 int
 mips_force_relocation (fixp)
      fixS *fixp;
@@ -9640,6 +10204,13 @@ mips_force_relocation (fixp)
       || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
     return 1;
 
+  if (HAVE_NEWABI
+      && S_GET_SEGMENT (fixp->fx_addsy) == bfd_abs_section_ptr
+      && (fixp->fx_r_type == BFD_RELOC_MIPS_SUB
+         || fixp->fx_r_type == BFD_RELOC_HI16_S
+         || fixp->fx_r_type == BFD_RELOC_LO16))
+    return 1;
+
   return (mips_pic == EMBEDDED_PIC
          && (fixp->fx_pcrel
              || SWITCH_TABLE (fixp)
@@ -9649,48 +10220,105 @@ mips_force_relocation (fixp)
 
 /* Apply a fixup to the object file.  */
 
-int
-md_apply_fix (fixP, valueP)
+void
+md_apply_fix3 (fixP, valP, seg)
      fixS *fixP;
-     valueT *valueP;
+     valueT * valP;
+     segT seg ATTRIBUTE_UNUSED;
 {
   unsigned char *buf;
-  long insn, value;
+  long insn;
+  valueT value;
 
   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 = *valueP;
+  value = * valP;
 
   /* 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 (S_GET_OTHER (fixP->fx_addsy) == STO_MIPS16
-        || S_IS_WEAK (fixP->fx_addsy)
-        || (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 (S_GET_OTHER (fixP->fx_addsy) == STO_MIPS16
+         || ((S_IS_WEAK (fixP->fx_addsy)
+              || S_IS_EXTERN (fixP->fx_addsy))
+             && !S_IS_COMMON (fixP->fx_addsy))
+         || (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))))
+
+       {
+         valueT symval = S_GET_VALUE (fixP->fx_addsy);
+
+         value -= symval;
+         if (value != 0
+             && ! fixP->fx_pcrel
+             && fixP->fx_r_type != BFD_RELOC_MIPS_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.  */
+             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;
 
-      {
-        value -= S_GET_VALUE (fixP->fx_addsy);
-        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.
-              FIXME: If this makes VALUE zero, we're toast.  */
-            value -= S_GET_VALUE (fixP->fx_addsy);
-          }
-      }
+                 /* 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);
+                   }
+               }
+           }
+       }
 
       /* This code was generated using trial and error and so is
         fragile and not trustworthy.  If you change it, you should
@@ -9703,14 +10331,15 @@ md_apply_fix (fixP, valueP)
          /* 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_S2
+         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;
        }
     }
 #endif
 
-  fixP->fx_addnumber = value;  /* Remember value for tc_gen_reloc */
+  fixP->fx_addnumber = value;  /* Remember value for tc_gen_reloc */
 
   if (fixP->fx_addsy == NULL && ! fixP->fx_pcrel)
     fixP->fx_done = 1;
@@ -9718,6 +10347,21 @@ md_apply_fix (fixP, valueP)
   switch (fixP->fx_r_type)
     {
     case BFD_RELOC_MIPS_JMP:
+    case BFD_RELOC_MIPS_SHIFT5:
+    case BFD_RELOC_MIPS_SHIFT6:
+    case BFD_RELOC_MIPS_GOT_DISP:
+    case BFD_RELOC_MIPS_GOT_PAGE:
+    case BFD_RELOC_MIPS_GOT_OFST:
+    case BFD_RELOC_MIPS_SUB:
+    case BFD_RELOC_MIPS_INSERT_A:
+    case BFD_RELOC_MIPS_INSERT_B:
+    case BFD_RELOC_MIPS_DELETE:
+    case BFD_RELOC_MIPS_HIGHEST:
+    case BFD_RELOC_MIPS_HIGHER:
+    case BFD_RELOC_MIPS_SCN_DISP:
+    case BFD_RELOC_MIPS_REL16:
+    case BFD_RELOC_MIPS_RELGOT:
+    case BFD_RELOC_MIPS_JALR:
     case BFD_RELOC_HI16:
     case BFD_RELOC_HI16_S:
     case BFD_RELOC_MIPS_GPREL:
@@ -9759,9 +10403,7 @@ md_apply_fix (fixP, valueP)
          value += (fixP->fx_next->fx_frag->fr_address
                    + fixP->fx_next->fx_where);
        }
-      if (value & 0x8000)
-       value += 0x10000;
-      value >>= 16;
+      value = ((value + 0x8000) >> 16) & 0xffff;
       buf = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
       if (target_big_endian)
        buf += 2;
@@ -9840,7 +10482,7 @@ md_apply_fix (fixP, valueP)
         up deleting a LO16 reloc.  See the 'o' case in mips_ip.  */
       if (fixP->fx_done)
        {
-         if (value < -0x8000 || value > 0x7fff)
+         if (value + 0x8000 > 0xffff)
            as_bad_where (fixP->fx_file, fixP->fx_line,
                          _("relocation overflow"));
          buf = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
@@ -9851,15 +10493,18 @@ md_apply_fix (fixP, valueP)
       break;
 
     case BFD_RELOC_16_PCREL_S2:
+      if ((value & 0x3) != 0)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("Branch to odd address (%lx)"), (long) value);
+
+      /* Fall through.  */
+
+    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 ((value & 0x3) != 0)
-       as_bad_where (fixP->fx_file, fixP->fx_line,
-                     _("Branch to odd address (%lx)"), value);
-
       if (!fixP->fx_done && value != 0)
        break;
       /* If 'value' is zero, the remaining reloc code won't actually
@@ -9868,7 +10513,7 @@ md_apply_fix (fixP, valueP)
       if (!fixP->fx_done)
        value -= fixP->fx_frag->fr_address + fixP->fx_where;
 
-      value >>= 2;
+      value = (offsetT) value >> 2;
 
       /* update old instruction data */
       buf = (unsigned char *) (fixP->fx_where + fixP->fx_frag->fr_literal);
@@ -9877,7 +10522,7 @@ md_apply_fix (fixP, valueP)
       else
        insn = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
 
-      if (value >= -0x8000 && value < 0x8000)
+      if (value + 0x8000 <= 0xffff)
        insn |= value & 0xffff;
       else
        {
@@ -9934,8 +10579,6 @@ md_apply_fix (fixP, valueP)
     default:
       internalError ();
     }
-
-  return 1;
 }
 
 #if 0
@@ -10135,7 +10778,7 @@ s_change_sec (sec)
 #ifdef OBJ_ELF
   /* The ELF backend needs to know that we are changing sections, so
      that .previous works correctly.  We could do something like check
-     for a obj_section_change_hook macro, but that might be confusing
+     for an obj_section_change_hook macro, but that might be confusing
      as it would not be appropriate to use it in the section changing
      functions in read.c, since obj-elf.c intercepts those.  FIXME:
      This should be cleaner, somehow.  */
@@ -10428,12 +11071,46 @@ s_mipsset (x)
       /* Permit the user to change the ISA on the fly.  Needless to
         say, misuse can cause serious problems.  */
       isa = atoi (name + 4);
-      if (isa == 0)
-       mips_opts.isa = file_mips_isa;
-      else if (isa < 1 || isa > 4)
-       as_bad (_("unknown ISA level"));
-      else
-       mips_opts.isa = isa;
+      switch (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:
+       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;
+       mips_opts.gp32 = 0;
+       mips_opts.fp32 = 0;
+       break;
+      default:
+       as_bad (_("unknown ISA level %s"), name + 4);
+       break;
+      }
+
+      switch (isa)
+      {
+      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;
+      }
     }
   else if (strcmp (name, "autoextend") == 0)
     mips_opts.noautoextend = 0;
@@ -10519,14 +11196,15 @@ s_cpload (ignore)
   expressionS ex;
   int icnt = 0;
 
-  /* If we are not generating SVR4 PIC code, .cpload is ignored.  */
-  if (mips_pic != SVR4_PIC)
+  /* If we are not generating SVR4 PIC code, or if this is NewABI code,
+     .cpload is ignored.  */
+  if (mips_pic != SVR4_PIC || HAVE_NEWABI)
     {
       s_ignore (0);
       return;
     }
 
-  /* .cpload should be a in .set noreorder section.  */
+  /* .cpload should be in a .set noreorder section.  */
   if (mips_opts.noreorder == 0)
     as_warn (_(".cpload not in noreorder section"));
 
@@ -10538,7 +11216,7 @@ s_cpload (ignore)
   /* In ELF, this symbol is implicitly an STT_OBJECT symbol.  */
   symbol_get_bfdsym (ex.X_add_symbol)->flags |= BSF_OBJECT;
 
-  macro_build_lui ((char *) NULL, &icnt, &ex, GP);
+  macro_build_lui (NULL, &icnt, &ex, GP);
   macro_build ((char *) NULL, &icnt, &ex, "addiu", "t,r,j", GP, GP,
               (int) BFD_RELOC_LO16);
 
@@ -10548,6 +11226,119 @@ s_cpload (ignore)
   demand_empty_rest_of_line ();
 }
 
+/* Handle the .cpsetup pseudo-op defined for NewABI PIC code.  The syntax is:
+     .cpsetup $reg1, offset|$reg2, label
+
+   If offset is given, this results in:
+     sd                $gp, offset($sp)
+     lui       $gp, %gp_rel(%neg(%hi(label)))
+     daddiu    $gp, $gp, %gp_rel(%neg(%lo(label)))
+     addu      $gp, $gp, $reg1
+
+   If $reg2 is given, this results in:
+     daddu     $reg2, $gp, $0
+     lui       $gp, %gp_rel(%neg(%hi(label)))
+     daddiu    $gp, $gp, %gp_rel(%neg(%lo(label)))
+     addu      $gp, $gp, $reg1
+ */
+static void
+s_cpsetup (ignore)
+     int ignore ATTRIBUTE_UNUSED;
+{
+  expressionS ex_off;
+  expressionS ex_sym;
+  int reg1;
+  int icnt = 0;
+  char *sym;
+
+  /* If we are not generating SVR4 PIC code, .cpload is ignored.
+     We also need NewABI support.  */
+  if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
+    {
+      s_ignore (0);
+      return;
+    }
+
+  reg1 = tc_get_register (0);
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer != ',')
+    {
+      as_bad (_("missing argument separator ',' for .cpsetup"));
+      return;
+    }
+  else
+    input_line_pointer++;
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer == '$')
+    mips_cpreturn_register = tc_get_register (0);
+  else
+    mips_cpreturn_offset = get_absolute_expression ();
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer != ',')
+    {
+      as_bad (_("missing argument separator ',' for .cpsetup"));
+      return;
+    }
+  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;
+
+  if (mips_cpreturn_register == -1)
+    {
+      ex_off.X_op = O_constant;
+      ex_off.X_add_symbol = NULL;
+      ex_off.X_op_symbol = NULL;
+      ex_off.X_add_number = mips_cpreturn_offset;
+
+      macro_build ((char *) NULL, &icnt, &ex_off, "sd", "t,o(b)",
+                  mips_gp_register, (int) BFD_RELOC_LO16, SP);
+    }
+  else
+    macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "daddu",
+                "d,v,t", mips_cpreturn_register, mips_gp_register, 0);
+
+  macro_build ((char *) NULL, &icnt, &ex_sym, "lui", "t,u", mips_gp_register,
+              (int) BFD_RELOC_GPREL16);
+  fix_new (frag_now, (char *) prev_insn_fixp - 4 - frag_now->fr_literal, 0,
+          NULL, 0, 0, BFD_RELOC_MIPS_SUB);
+  fix_new (frag_now, (char *) prev_insn_fixp - 4 - frag_now->fr_literal, 0,
+          NULL, 0, 0, BFD_RELOC_HI16_S);
+  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, (char *) prev_insn_fixp - 4 - frag_now->fr_literal, 0,
+          NULL, 0, 0, BFD_RELOC_MIPS_SUB);
+  fix_new (frag_now, (char *) prev_insn_fixp - 4 - frag_now->fr_literal, 0,
+          NULL, 0, 0, BFD_RELOC_LO16);
+  macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "daddu",
+              "d,v,t", mips_gp_register, mips_gp_register, reg1);
+
+  demand_empty_rest_of_line ();
+}
+
+static void
+s_cplocal (ignore)
+     int ignore ATTRIBUTE_UNUSED;
+{
+  /* If we are not generating SVR4 PIC code, or if this is not NewABI code,
+   .cplocal is ignored.  */
+  if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
+    {
+      s_ignore (0);
+      return;
+    }
+
+  mips_gp_register = tc_get_register (0);
+}
+
 /* Handle the .cprestore pseudo-op.  This stores $gp into a given
    offset from $sp.  The offset is remembered, and after making a PIC
    call $gp is restored from that location.  */
@@ -10559,8 +11350,9 @@ s_cprestore (ignore)
   expressionS ex;
   int icnt = 0;
 
-  /* If we are not generating SVR4 PIC code, .cprestore is ignored.  */
-  if (mips_pic != SVR4_PIC)
+  /* If we are not generating SVR4 PIC code, or if this is NewABI code,
+   .cprestore is ignored.  */
+  if (mips_pic != SVR4_PIC || HAVE_NEWABI)
     {
       s_ignore (0);
       return;
@@ -10574,14 +11366,71 @@ s_cprestore (ignore)
   ex.X_add_number = mips_cprestore_offset;
 
   macro_build ((char *) NULL, &icnt, &ex,
-              ((bfd_arch_bits_per_address (stdoutput) == 32
-                || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-               ? "sw" : "sd"),
+              HAVE_32BIT_ADDRESSES ? "sw" : "sd",
               "t,o(b)", GP, (int) BFD_RELOC_LO16, SP);
 
   demand_empty_rest_of_line ();
 }
 
+/* Handle the .cpreturn pseudo-op defined for NewABI PIC code. If an offset
+   was given in the preceeding .gpsetup, it results in:
+     ld                $gp, offset($sp)
+
+   If a register $reg2 was given there, it results in:
+     daddiu    $gp, $gp, $reg2
+ */
+static void
+s_cpreturn (ignore)
+     int ignore ATTRIBUTE_UNUSED;
+{
+  expressionS ex;
+  int icnt = 0;
+
+  /* If we are not generating SVR4 PIC code, .cpreturn is ignored.
+     We also need NewABI support.  */
+  if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
+    {
+      s_ignore (0);
+      return;
+    }
+
+  if (mips_cpreturn_register == -1)
+    {
+      ex.X_op = O_constant;
+      ex.X_add_symbol = NULL;
+      ex.X_op_symbol = NULL;
+      ex.X_add_number = mips_cpreturn_offset;
+
+      macro_build ((char *) NULL, &icnt, &ex, "ld", "t,o(b)",
+                  mips_gp_register, (int) BFD_RELOC_LO16, SP);
+    }
+  else
+    macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "daddu",
+                "d,v,t", mips_gp_register, mips_cpreturn_register, 0);
+
+  demand_empty_rest_of_line ();
+}
+
+/* Handle the .gpvalue pseudo-op.  This is used when generating NewABI PIC
+   code.  It sets the offset to use in gp_rel relocations.  */
+
+static void
+s_gpvalue (ignore)
+     int ignore ATTRIBUTE_UNUSED;
+{
+  /* If we are not generating SVR4 PIC code, .gpvalue is ignored.
+     We also need NewABI support.  */
+  if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
+    {
+      s_ignore (0);
+      return;
+    }
+
+  mips_cpreturn_offset = get_absolute_expression ();
+
+  demand_empty_rest_of_line ();
+}
+
 /* Handle the .gpword pseudo-op.  This is used when generating PIC
    code.  It generates a 32 bit GP relative reloc.  */
 
@@ -10632,8 +11481,9 @@ s_cpadd (ignore)
   int icnt = 0;
   int reg;
 
-  /* This is ignored when not generating SVR4 PIC code.  */
-  if (mips_pic != SVR4_PIC)
+  /* This is ignored when not generating SVR4 PIC code or if this is NewABI
+     code.  */
+  if (mips_pic != SVR4_PIC || HAVE_NEWABI)
     {
       s_ignore (0);
       return;
@@ -10642,9 +11492,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,
-              ((bfd_arch_bits_per_address (stdoutput) == 32
-                || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
-               ? "addu" : "daddu"),
+              HAVE_32BIT_ADDRESSES ? "addu" : "daddu",
               "d,v,t", reg, reg, GP);
 
   demand_empty_rest_of_line ();
@@ -10750,7 +11598,7 @@ tc_get_register (frame)
       as_warn (_("expected `$'"));
       reg = 0;
     }
-  else if (isdigit ((unsigned char) *input_line_pointer))
+  else if (ISDIGIT (*input_line_pointer))
     {
       reg = get_absolute_expression ();
       if (reg < 0 || reg >= 32)
@@ -10815,7 +11663,7 @@ nopic_need_relax (sym, before_relaxing)
   if (sym == 0)
     return 0;
 
-  if (USE_GLOBAL_POINTER_OPT)
+  if (USE_GLOBAL_POINTER_OPT && g_switch_value > 0)
     {
       const char *symname;
       int change;
@@ -10888,6 +11736,7 @@ mips16_extended_frag (fragp, sec, stretch)
   offsetT val;
   int mintiny, maxtiny;
   segT symsec;
+  fragS *sym_frag;
 
   if (RELAX_MIPS16_USER_SMALL (fragp->fr_subtype))
     return 0;
@@ -10921,29 +11770,9 @@ mips16_extended_frag (fragp, sec, stretch)
       maxtiny = (1 << (op->nbits - 1)) - 1;
     }
 
-  /* We can't always call S_GET_VALUE here, because we don't want to
-     lock in a particular frag address.  */
-  if (symbol_constant_p (fragp->fr_symbol))
-    {
-      val = (S_GET_VALUE (fragp->fr_symbol)
-            + symbol_get_frag (fragp->fr_symbol)->fr_address);
-      symsec = S_GET_SEGMENT (fragp->fr_symbol);
-    }
-  else if (symbol_equated_p (fragp->fr_symbol)
-          && (symbol_constant_p
-              (symbol_get_value_expression (fragp->fr_symbol)->X_add_symbol)))
-    {
-      symbolS *eqsym;
-
-      eqsym = symbol_get_value_expression (fragp->fr_symbol)->X_add_symbol;
-      val = (S_GET_VALUE (eqsym)
-            + symbol_get_frag (eqsym)->fr_address
-            + symbol_get_value_expression (fragp->fr_symbol)->X_add_number
-            + symbol_get_frag (fragp->fr_symbol)->fr_address);
-      symsec = S_GET_SEGMENT (eqsym);
-    }
-  else
-    return 1;
+  sym_frag = symbol_get_frag (fragp->fr_symbol);
+  val = S_GET_VALUE (fragp->fr_symbol);
+  symsec = S_GET_SEGMENT (fragp->fr_symbol);
 
   if (op->pcrel)
     {
@@ -10962,6 +11791,7 @@ mips16_extended_frag (fragp, sec, stretch)
        }
       else
        {
+         /* Must have been called from md_estimate_size_before_relax.  */
          if (symsec != sec)
            {
              fragp->fr_subtype =
@@ -10974,16 +11804,22 @@ mips16_extended_frag (fragp, sec, stretch)
 
              return 1;
            }
+         if (fragp != sym_frag && sym_frag->fr_address == 0)
+           /* Assume non-extended on the first relaxation pass.
+              The address we have calculated will be bogus if this is
+              a forward branch to another frag, as the forward frag
+              will have fr_address == 0.  */
+           return 0;
        }
 
       /* In this case, we know for sure that the symbol fragment is in
-        the same section.  If the fr_address of the symbol fragment
-        is greater then the address of this fragment we want to add
+        the same section.  If the relax_marker of the symbol fragment
+        differs from the relax_marker of this fragment, we have not
+        yet adjusted the symbol fragment fr_address.  We want to add
         in STRETCH in order to get a better estimate of the address.
         This particularly matters because of the shift bits.  */
       if (stretch != 0
-         && (symbol_get_frag (fragp->fr_symbol)->fr_address
-             >= fragp->fr_address))
+         && sym_frag->relax_marker != fragp->relax_marker)
        {
          fragS *f;
 
@@ -10993,9 +11829,7 @@ mips16_extended_frag (fragp, sec, stretch)
              This doesn't handle the fr_subtype field, which specifies
              a maximum number of bytes to skip when doing an
              alignment.  */
-         for (f = fragp;
-              f != NULL && f != symbol_get_frag (fragp->fr_symbol);
-              f = f->fr_next)
+         for (f = fragp; f != NULL && f != sym_frag; f = f->fr_next)
            {
              if (f->fr_type == rs_align || f->fr_type == rs_align_code)
                {
@@ -11094,7 +11928,6 @@ mips16_extended_frag (fragp, sec, stretch)
    encoded in the subtype information.  For the mips16, we have to
    decide whether we are using an extended opcode or not.  */
 
-/*ARGSUSED*/
 int
 md_estimate_size_before_relax (fragp, segtype)
      fragS *fragp;
@@ -11129,8 +11962,7 @@ md_estimate_size_before_relax (fragp, segtype)
       sym = fragp->fr_symbol;
 
       /* Handle the case of a symbol equated to another symbol.  */
-      while (symbol_equated_p (sym)
-            && (! S_IS_DEFINED (sym) || S_IS_COMMON (sym)))
+      while (symbol_equated_reloc_p (sym))
        {
          symbolS *n;
 
@@ -11146,18 +11978,18 @@ md_estimate_size_before_relax (fragp, segtype)
 
       /* 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;
-        }
+       {
+         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
@@ -11165,8 +11997,9 @@ md_estimate_size_before_relax (fragp, segtype)
                && ! bfd_is_com_section (symsec)
                && !linkonce
 #ifdef OBJ_ELF
-               /* A weak symbol is treated as external.  */
-               && ! S_IS_WEAK (sym)
+               /* A global or weak symbol is treated as external.  */
+               && (OUTPUT_FLAVOR == bfd_target_elf_flavour
+                   && ! (S_IS_EXTERN (sym) || S_IS_WEAK (sym)))
 #endif
                );
     }
@@ -11184,13 +12017,13 @@ md_estimate_size_before_relax (fragp, segtype)
                          + RELAX_RELOC1 (fragp->fr_subtype));
       /* FIXME: This really needs as_warn_where.  */
       if (RELAX_WARN (fragp->fr_subtype))
-       as_warn (_("AT used after \".set noat\" or macro used after \".set nomacro\""));
+       as_warn (_("AT used after \".set noat\" or macro used after "
+                  "\".set nomacro\""));
+
+      return RELAX_NEW (fragp->fr_subtype) - RELAX_OLD (fragp->fr_subtype);
     }
 
-  if (! change)
-    return 0;
-  else
-    return RELAX_NEW (fragp->fr_subtype) - RELAX_OLD (fragp->fr_subtype);
+  return 0;
 }
 
 /* This is called to see whether a reloc against a defined symbol
@@ -11204,6 +12037,12 @@ int
 mips_fix_adjustable (fixp)
      fixS *fixp;
 {
+#ifdef OBJ_ELF
+  /* Prevent all adjustments to global symbols.  */
+  if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+      && (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
@@ -11295,7 +12134,8 @@ tc_gen_reloc (section, fixp)
          || 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))
+         || fixp->fx_r_type == BFD_RELOC_MIPS_CALL_LO16)
+      && ! HAVE_NEWABI)
     {
       arelent *reloc2;
 
@@ -11444,7 +12284,7 @@ mips_relax_frag (fragp, stretch)
   if (! RELAX_MIPS16_P (fragp->fr_subtype))
     return 0;
 
-  if (mips16_extended_frag (fragp, (asection *) NULL, stretch))
+  if (mips16_extended_frag (fragp, NULL, stretch))
     {
       if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
        return 0;
@@ -11500,7 +12340,7 @@ md_convert_frag (abfd, asec, fragp)
          ext = false;
        }
 
-      resolve_symbol_value (fragp->fr_symbol, 1);
+      resolve_symbol_value (fragp->fr_symbol);
       val = S_GET_VALUE (fragp->fr_symbol);
       if (op->pcrel)
        {
@@ -11642,7 +12482,7 @@ void
 mips_elf_final_processing ()
 {
   /* Write out the register information.  */
-  if (! mips_64)
+  if (! HAVE_NEWABI)
     {
       Elf32_RegInfo s;
 
@@ -11682,19 +12522,23 @@ mips_elf_final_processing ()
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_PIC;
 
   /* Set the MIPS ELF ABI flags.  */
-  if (mips_abi_string == 0)
+  if (file_mips_abi == NO_ABI)
     ;
-  else if (strcmp (mips_abi_string,"32") == 0)
+  else if (file_mips_abi == O32_ABI)
     elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_O32;
-  else if (strcmp (mips_abi_string,"o64") == 0)
+  else if (file_mips_abi == O64_ABI)
     elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_O64;
-  else if (strcmp (mips_abi_string,"eabi") == 0)
+  else if (file_mips_abi == EABI_ABI)
     {
       if (mips_eabi64)
        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)
+    elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ABI2;
+
+  /* Nothing to do for "64".  */
 
   if (mips_32bitmode)
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_32BITMODE;
@@ -11702,51 +12546,52 @@ mips_elf_final_processing ()
 
 #endif /* OBJ_ELF || OBJ_MAYBE_ELF */
 \f
-typedef struct proc
-  {
-    symbolS *isym;
-    unsigned long reg_mask;
-    unsigned long reg_offset;
-    unsigned long fpreg_mask;
-    unsigned long fpreg_offset;
-    unsigned long frame_offset;
-    unsigned long frame_reg;
-    unsigned long pc_reg;
-  }
-procS;
+typedef struct proc {
+  symbolS *isym;
+  unsigned long reg_mask;
+  unsigned long reg_offset;
+  unsigned long fpreg_mask;
+  unsigned long fpreg_offset;
+  unsigned long frame_offset;
+  unsigned long frame_reg;
+  unsigned long pc_reg;
+} procS;
 
 static procS cur_proc;
 static procS *cur_proc_ptr;
 static int numprocs;
 
-/* When we align code in the .text section of mips16, use the correct two
-   byte nop pattern of 0x6500 (move $0,$0) */
+/* Fill in an rs_align_code fragment.  */
 
-int
-mips_do_align (n, fill, len, max)
-     int n;
-     const char *fill;
-     int len ATTRIBUTE_UNUSED;
-     int max;
+void
+mips_handle_align (fragp)
+     fragS *fragp;
 {
-  if (fill == NULL
-      && subseg_text_p (now_seg)
-      && n > 1
-      && mips_opts.mips16)
+  if (fragp->fr_type != rs_align_code)
+    return;
+
+  if (mips_opts.mips16)
     {
       static const unsigned char be_nop[] = { 0x65, 0x00 };
       static const unsigned char le_nop[] = { 0x00, 0x65 };
 
-      frag_align (1, 0, 0);
+      int bytes;
+      char *p;
 
-      if (target_big_endian)
-       frag_align_pattern (n, be_nop, 2, max);
-      else
-       frag_align_pattern (n, le_nop, 2, max);
-      return 1;
+      bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+      p = fragp->fr_literal + fragp->fr_fix;
+
+      if (bytes & 1)
+       {
+         *p++ = 0;
+         fragp->fr_fix += 1;
+       }
+
+      memcpy (p, (target_big_endian ? be_nop : le_nop), 2);
+      fragp->fr_var = 2;
     }
 
-  return 0;
+  /* For mips32, a nop is a zero, which we trivially get by doing nothing.  */
 }
 
 static void
@@ -11759,7 +12604,7 @@ md_obj_end ()
 {
   /* check for premature end, nesting errors, etc */
   if (cur_proc_ptr)
-    as_warn (_("missing `.end' at end of assembly"));
+    as_warn (_("missing .end at end of assembly"));
 }
 
 static long
@@ -11773,14 +12618,14 @@ get_number ()
       ++input_line_pointer;
       negative = 1;
     }
-  if (!isdigit ((unsigned char) *input_line_pointer))
+  if (!ISDIGIT (*input_line_pointer))
     as_bad (_("Expected simple number."));
   if (input_line_pointer[0] == '0')
     {
       if (input_line_pointer[1] == 'x')
        {
          input_line_pointer += 2;
-         while (isxdigit ((unsigned char) *input_line_pointer))
+         while (ISXDIGIT (*input_line_pointer))
            {
              val <<= 4;
              val |= hex_value (*input_line_pointer++);
@@ -11790,7 +12635,7 @@ get_number ()
       else
        {
          ++input_line_pointer;
-         while (isdigit ((unsigned char) *input_line_pointer))
+         while (ISDIGIT (*input_line_pointer))
            {
              val <<= 3;
              val |= *input_line_pointer++ - '0';
@@ -11798,14 +12643,14 @@ get_number ()
          return negative ? -val : val;
        }
     }
-  if (!isdigit ((unsigned char) *input_line_pointer))
+  if (!ISDIGIT (*input_line_pointer))
     {
       printf (_(" *input_line_pointer == '%c' 0x%02x\n"),
              *input_line_pointer, *input_line_pointer);
       as_warn (_("Invalid number"));
       return -1;
     }
-  while (isdigit ((unsigned char) *input_line_pointer))
+  while (ISDIGIT (*input_line_pointer))
     {
       val *= 10;
       val += *input_line_pointer++ - '0';
@@ -11878,9 +12723,7 @@ s_mips_end (x)
   {
     segT saved_seg = now_seg;
     subsegT saved_subseg = now_subseg;
-    fragS *saved_frag = frag_now;
     valueT dot;
-    segT seg;
     expressionS exp;
     char *fragp;
 
@@ -11893,21 +12736,21 @@ s_mips_end (x)
     assert (pdr_seg);
     subseg_set (pdr_seg, 0);
 
-    /* Write the symbol */
+    /* 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);
   }
@@ -11930,7 +12773,7 @@ s_mips_ent (aent)
   if (*input_line_pointer == ',')
     input_line_pointer++;
   SKIP_WHITESPACE ();
-  if (isdigit ((unsigned char) *input_line_pointer)
+  if (ISDIGIT (*input_line_pointer)
       || *input_line_pointer == '-')
     number = get_number ();
 
@@ -11950,7 +12793,7 @@ s_mips_ent (aent)
     as_warn (_(".ent or .aent not in text section."));
 
   if (!aent && cur_proc_ptr)
-    as_warn (_("missing `.end'"));
+    as_warn (_("missing .end"));
 
   if (!aent)
     {
@@ -11975,13 +12818,13 @@ s_mips_ent (aent)
 
 static void
 s_mips_frame (ignore)
-     int ignore;
+     int ignore ATTRIBUTE_UNUSED;
 {
 #ifdef MIPS_STABS_ELF
 
   long val;
 
-  if (cur_proc_ptr ==  (procS *) NULL)
+  if (cur_proc_ptr == (procS *) NULL)
     {
       as_warn (_(".frame outside of .ent"));
       demand_empty_rest_of_line ();
@@ -12079,3 +12922,201 @@ s_loc (x)
   symbolP->sy_segment = now_seg;
 }
 #endif
+
+/* CPU name/ISA/number mapping table.
+
+   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.  */
+static const struct mips_cpu_info mips_cpu_info_table[] =
+{
+  /* MIPS1 ISA */
+  { "MIPS1",          1,      ISA_MIPS1,      CPU_R3000, },
+  { "mips",           1,      ISA_MIPS1,      CPU_R3000, },
+
+  /* MIPS2 ISA */
+  { "MIPS2",          1,      ISA_MIPS2,      CPU_R6000, },
+
+  /* MIPS3 ISA */
+  { "MIPS3",          1,      ISA_MIPS3,      CPU_R4000, },
+
+  /* MIPS4 ISA */
+  { "MIPS4",          1,      ISA_MIPS4,      CPU_R8000, },
+
+  /* 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, },
+};
+
+static const struct mips_cpu_info *
+mips_cpu_info_from_name (name)
+     const char *name;
+{
+  int i;
+
+  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]);
+
+  return NULL;
+}
+
+static const struct mips_cpu_info *
+mips_cpu_info_from_isa (isa)
+     int isa;
+{
+  int i;
+
+  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)
+      return (&mips_cpu_info_table[i]);
+
+  return NULL;
+}
+
+static const struct mips_cpu_info *
+mips_cpu_info_from_cpu (cpu)
+     int cpu;
+{
+  int i;
+
+  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]);
+
+  return NULL;
+}
This page took 0.140221 seconds and 4 git commands to generate.