* config/tc-mips.c (md_section_align): Do align if OBJ_ELF, but
[binutils-gdb.git] / gas / config / tc-mips.c
index 0fc795d5eb2eb4902fc11d2a592dba1a79337660..38bcebc5dc467f66c9d8d9e87c013d778e180b3d 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-mips.c -- assemble code for a MIPS chip.
-   Copyright (C) 1993 Free Software Foundation, Inc.
+   Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc.
    Contributed by the OSF and Ralph Campbell.
    Written by Keith Knowles and Ralph Campbell, working independently.
    Modified for ECOFF and R4000 support by Ian Lance Taylor of Cygnus
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with GAS; see the file COPYING.  If not, write to
-   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+   along with GAS; see the file COPYING.  If not, write to the Free
+   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA.  */
 
 #include "as.h"
 #include "config.h"
 #include "subsegs.h"
+#include "libiberty.h"
 
 #include <ctype.h>
 
-#ifndef __STDC__
-#ifndef NO_STDARG
-#define NO_STDARG
-#endif
-#endif
-
-#ifndef NO_STDARG
+#ifdef USE_STDARG
 #include <stdarg.h>
-#else
-#ifndef NO_VARARGS
+#endif
+#ifdef USE_VARARGS
 #include <varargs.h>
-#endif /* NO_VARARGS */
-#endif /* NO_STDARG */
+#endif
 
 #include "opcode/mips.h"
 
-#ifdef OBJ_ELF
+#ifdef OBJ_MAYBE_ELF
+/* Clean up namespace so we can include obj-elf.h too.  */
+static int mips_output_flavor () { return OUTPUT_FLAVOR; }
+#undef OBJ_PROCESS_STAB
+#undef OUTPUT_FLAVOR
+#undef S_GET_ALIGN
+#undef S_GET_SIZE
+#undef S_SET_ALIGN
+#undef S_SET_SIZE
+#undef TARGET_SYMBOL_FIELDS
+#undef obj_frob_file
+#undef obj_frob_symbol
+#undef obj_pop_insert
+#undef obj_sec_sym_ok_for_reloc
+
+#include "obj-elf.h"
+/* Fix any of them that we actually care about.  */
+#undef OUTPUT_FLAVOR
+#define OUTPUT_FLAVOR mips_output_flavor()
+#endif
+
+#if defined (OBJ_ELF)
 #include "elf/mips.h"
+#endif
+
+#ifndef ECOFF_DEBUGGING
+#define NO_ECOFF_DEBUGGING
+#define ECOFF_DEBUGGING 0
+#endif
+
+#include "ecoff.h"
 
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
 static char *mips_regmask_frag;
 #endif
 
 #define AT  1
 #define PIC_CALL_REG 25
+#define KT0 26
+#define KT1 27
 #define GP  28
 #define SP  29
 #define FP  30
 #define RA  31
 
-/* Decide whether to do GP reference optimizations based on the object
-   file format.  */
-#undef GPOPT
-#ifdef OBJ_ECOFF
-#define GPOPT
-#endif
-#ifdef OBJ_ELF
-#define GPOPT
-#endif
+extern int target_big_endian;
 
-/* The default target format to use.  */
-#ifdef OBJ_AOUT
-#ifdef TARGET_BYTES_BIG_ENDIAN
-#define DEFAULT_TARGET_FORMAT "a.out-mips-big"
-#else
-#define DEFAULT_TARGET_FORMAT "a.out-mips-little"
-#endif
-#endif /* OBJ_AOUT */
-#ifdef OBJ_ECOFF
-#ifdef TARGET_BYTES_BIG_ENDIAN
-#define DEFAULT_TARGET_FORMAT "ecoff-bigmips"
-#else
-#define DEFAULT_TARGET_FORMAT "ecoff-littlemips"
-#endif
-#endif /* OBJ_ECOFF */
-#ifdef OBJ_ELF
-#ifdef TARGET_BYTES_BIG_ENDIAN
-#define DEFAULT_TARGET_FORMAT "elf32-bigmips"
-#else
-#define DEFAULT_TARGET_FORMAT "elf32-littlemips"
-#endif
-#endif /* OBJ_ELF */
+/* 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.  */
+static int mips_64;
 
-const char *mips_target_format = DEFAULT_TARGET_FORMAT;
+/* 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-littlemips";
+    case bfd_target_elf_flavour:
+      return (target_big_endian
+             ? (mips_64 ? "elf64-bigmips" : "elf32-bigmips")
+             : (mips_64 ? "elf64-littlemips" : "elf32-littlemips"));
+    default:
+      abort ();
+    }
+}
 
 /* The name of the readonly data section.  */
-#ifdef OBJ_AOUT
-#define RDATA_SECTION_NAME ".data"
-#endif
-#ifdef OBJ_ECOFF
-#define RDATA_SECTION_NAME ".rdata"
-#endif
-#ifdef OBJ_ELF
-#define RDATA_SECTION_NAME ".rodata"
-#endif
+#define RDATA_SECTION_NAME (OUTPUT_FLAVOR == bfd_target_aout_flavour \
+                           ? ".data" \
+                           : OUTPUT_FLAVOR == bfd_target_ecoff_flavour \
+                           ? ".rdata" \
+                           : OUTPUT_FLAVOR == bfd_target_elf_flavour \
+                           ? ".rodata" \
+                           : (abort (), ""))
 
 /* These variables are filled in with the masks of registers used.
    The object format code reads them and puts them in the appropriate
@@ -116,7 +131,24 @@ static int mips_isa = -1;
 static int file_mips_isa;
 
 /* The CPU type as a number: 2000, 3000, 4000, 4400, etc.  */
-static int mips_cpu;
+static int mips_cpu = -1;
+
+/* Whether the 4650 instructions (mad/madu) are permitted.  */
+static int mips_4650 = -1;
+
+/* Whether the 4010 instructions are permitted.  */
+static int mips_4010 = -1;
+
+/* Whether the 4100 MADD16 and DMADD16 are permitted. */
+static int mips_4100 = -1;
+
+/* Whether the processor uses hardware interlocks, and thus does not
+   require nops to be inserted.  */
+static int interlocks = -1;
+
+/* As with "interlocks" this is used by hardware that has FP
+   (co-processor) interlocks.  */
+static int cop_interlocks = -1;
 
 /* MIPS PIC level.  */
 
@@ -141,6 +173,10 @@ enum mips_pic_level
 
 static enum mips_pic_level mips_pic;
 
+/* 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;
+
 /* 1 if trap instructions should used for overflow rather than break
    instructions.  */
 static int mips_trap;
@@ -152,16 +188,27 @@ static int mips_nomove;
 static int mips_noat;
 static int mips_nobopt;
 
-#ifdef GPOPT
 /* The size of the small data section.  */
 static int g_switch_value = 8;
 /* Whether the -G option was used.  */
 static int g_switch_seen = 0;
-#endif
 
 #define N_RMASK 0xc4
 #define N_VFP   0xd4
 
+/* If we can determine in advance that GP optimization won't be
+   possible, we can skip the relaxation stuff that tries to produce
+   GP-relative references.  This makes delay slot optimization work
+   better.
+
+   This function can only provide a guess, but it seems to work for
+   gcc output.  If it guesses wrong, the only loss should be in
+   efficiency; it shouldn't introduce any bugs.
+
+   I don't know if a fix is needed for the SVR4_PIC mode.  I've only
+   fixed it for the non-PIC mode.  KR 95/04/07  */
+static int nopic_need_relax PARAMS ((symbolS *));
+
 /* handle of the OPCODE hash table */
 static struct hash_control *op_hash = NULL;
 
@@ -196,7 +243,7 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
 
 static char *insn_error;
 
-static int byte_order = BYTE_ORDER;
+static int byte_order;
 
 static int auto_align = 1;
 
@@ -222,6 +269,10 @@ static int mips_frame_reg = SP;
    insert NOPs.  */
 static int mips_optimize = 2;
 
+/* Debugging level.  -g sets this to 2.  -gN sets this to N.  -g0 is
+   equivalent to seeing no -g option at all.  */
+static int mips_debug = 0;
+
 /* The previous instruction.  */
 static struct mips_cl_insn prev_insn;
 
@@ -253,6 +304,31 @@ static int prev_insn_unreordered;
 /* Non-zero if the previous previous instruction was in a .set
    noreorder.  */
 static int prev_prev_insn_unreordered;
+
+/* For ECOFF and ELF, relocations against symbols are done in two
+   parts, with a HI relocation and a LO relocation.  Each relocation
+   has only 16 bits of space to store an addend.  This means that in
+   order for the linker to handle carries correctly, it must be able
+   to locate both the HI and the LO relocation.  This means that the
+   relocations must appear in order in the relocation table.
+
+   In order to implement this, we keep track of each unmatched HI
+   relocation.  We then sort them so that they immediately precede the
+   corresponding LO relocation. */
+
+struct mips_hi_fixup
+{
+  /* Next HI fixup.  */
+  struct mips_hi_fixup *next;
+  /* This fixup.  */
+  fixS *fixp;
+  /* The section this fixup is in.  */
+  segT seg;
+};
+
+/* The list of unmatched HI relocs.  */
+
+static struct mips_hi_fixup *mips_hi_fixup_list;
 \f
 /* Since the MIPS does not have multiple forms of PC relative
    instructions, we do not have to do relaxing as is done on other
@@ -325,8 +401,8 @@ static int prev_prev_insn_unreordered;
     | ((warn) ? 1 : 0)))
 #define RELAX_OLD(i) (((i) >> 24) & 0xff)
 #define RELAX_NEW(i) (((i) >> 16) & 0xff)
-#define RELAX_RELOC1(i) ((((i) >> 9) & 0x7f) - 64)
-#define RELAX_RELOC2(i) ((((i) >> 2) & 0x7f) - 64)
+#define RELAX_RELOC1(i) ((bfd_vma)(((i) >> 9) & 0x7f) - 64)
+#define RELAX_RELOC2(i) ((bfd_vma)(((i) >> 2) & 0x7f) - 64)
 #define RELAX_RELOC3(i) (((i) >> 1) & 1)
 #define RELAX_WARN(i) ((i) & 1)
 \f
@@ -341,21 +417,27 @@ static int prev_prev_insn_unreordered;
 
 static int insn_uses_reg PARAMS ((struct mips_cl_insn *ip,
                                  unsigned int reg, int fpr));
+static int reg_needs_delay PARAMS ((int));
 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 ((void));
 static void mips_emit_delays PARAMS ((void));
+#ifdef USE_STDARG
 static void macro_build PARAMS ((char *place, int *counter, expressionS * ep,
                                 const char *name, const char *fmt,
                                 ...));
+#else
+static void macro_build ();
+#endif
 static void macro_build_lui PARAMS ((char *place, int *counter,
                                     expressionS * ep, int regnum));
 static void set_at PARAMS ((int *counter, int reg, int unsignedp));
 static void check_absolute_expr PARAMS ((struct mips_cl_insn * ip,
                                         expressionS *));
-static void load_register PARAMS ((int *counter, int reg, expressionS * ep));
+static void load_register PARAMS ((int *, int, expressionS *, int));
 static void load_address PARAMS ((int *counter, int reg, expressionS *ep));
 static void macro PARAMS ((struct mips_cl_insn * ip));
 #ifdef LOSING_COMPILER
@@ -367,37 +449,23 @@ static void my_getExpression PARAMS ((expressionS * ep, char *str));
 static symbolS *get_symbol PARAMS ((void));
 static void mips_align PARAMS ((int to, int fill, symbolS *label));
 static void s_align PARAMS ((int));
-static void s_stringer PARAMS ((int));
 static void s_change_sec PARAMS ((int));
 static void s_cons PARAMS ((int));
-static void s_err PARAMS ((int));
-static void s_extern PARAMS ((int));
 static void s_float_cons PARAMS ((int));
 static void s_mips_globl PARAMS ((int));
 static void s_option PARAMS ((int));
 static void s_mipsset PARAMS ((int));
-static void s_mips_space PARAMS ((int));
 static void s_abicalls PARAMS ((int));
 static void s_cpload PARAMS ((int));
 static void s_cprestore PARAMS ((int));
 static void s_gpword PARAMS ((int));
 static void s_cpadd PARAMS ((int));
-#ifndef ECOFF_DEBUGGING
 static void md_obj_begin PARAMS ((void));
 static void md_obj_end PARAMS ((void));
 static long get_number PARAMS ((void));
 static void s_ent PARAMS ((int));
 static void s_mipsend PARAMS ((int));
 static void s_file PARAMS ((int));
-#if 0
-static void s_frame PARAMS ((int));
-static void s_loc PARAMS ((int));
-static void s_mask PARAMS ((char));
-#endif
-#endif
-#ifdef OBJ_ELF
-static void s_elf_section PARAMS ((int));
-#endif
 \f
 /* Pseudo-op table.
 
@@ -417,7 +485,7 @@ static void s_elf_section PARAMS ((int));
    they are not currently supported: .asm0, .endr, .lab, .repeat,
    .struct, .weakext.  */
 
-const pseudo_typeS md_pseudo_table[] =
+static const pseudo_typeS mips_pseudo_table[] =
 {
  /* MIPS specific pseudo-ops.  */
   {"option", s_option, 0},
@@ -425,15 +493,15 @@ const pseudo_typeS md_pseudo_table[] =
   {"rdata", s_change_sec, 'r'},
   {"sdata", s_change_sec, 's'},
   {"livereg", s_ignore, 0},
-  { "abicalls", s_abicalls, 0},
-  { "cpload", s_cpload, 0},
-  { "cprestore", s_cprestore, 0},
-  { "gpword", s_gpword, 0},
-  { "cpadd", s_cpadd, 0},
+  {"abicalls", s_abicalls, 0},
+  {"cpload", s_cpload, 0},
+  {"cprestore", s_cprestore, 0},
+  {"gpword", s_gpword, 0},
+  {"cpadd", s_cpadd, 0},
 
  /* Relatively generic pseudo-ops that happen to be used on MIPS
      chips.  */
-  {"asciiz", s_stringer, 1},
+  {"asciiz", stringer, 1},
   {"bss", s_change_sec, 'b'},
   {"err", s_err, 0},
   {"half", s_cons, 1},
@@ -442,12 +510,9 @@ const pseudo_typeS md_pseudo_table[] =
  /* These pseudo-ops are defined in read.c, but must be overridden
      here for one reason or another.  */
   {"align", s_align, 0},
-  {"ascii", s_stringer, 0},
-  {"asciz", s_stringer, 1},
   {"byte", s_cons, 0},
   {"data", s_change_sec, 'd'},
   {"double", s_float_cons, 'd'},
-  {"extern", s_extern, 0},
   {"float", s_float_cons, 'f'},
   {"globl", s_mips_globl, 0},
   {"global", s_mips_globl, 0},
@@ -458,11 +523,12 @@ const pseudo_typeS md_pseudo_table[] =
   {"quad", s_cons, 3},
   {"short", s_cons, 1},
   {"single", s_float_cons, 'f'},
-  {"space", s_mips_space, 0},
   {"text", s_change_sec, 't'},
   {"word", s_cons, 2},
+  { 0 },
+};
 
-#ifndef ECOFF_DEBUGGING
+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_ent, 1},
@@ -476,31 +542,35 @@ const pseudo_typeS md_pseudo_table[] =
   {"loc", s_ignore, 0},
   {"mask", s_ignore, 'R'},
   {"verstamp", s_ignore, 0},
-#endif
+  { 0 },
+};
 
-#ifdef OBJ_ELF
-  /* We need to tweak the ELF ".section" pseudo-op a bit.  */
-  {"section", s_elf_section, 0},
-#endif
+extern void pop_insert PARAMS ((const pseudo_typeS *));
 
- /* Sentinel.  */
-  {NULL}
-};
-\f
-const relax_typeS md_relax_table[] =
+void
+mips_pop_insert ()
 {
-  { 0 }
-};
-
+  pop_insert (mips_pseudo_table);
+  if (! ECOFF_DEBUGGING)
+    pop_insert (mips_nonecoff_pseudo_table);
+}
+\f
 static char *expr_end;
 
+/* Expressions which appear in instructions.  These are set by
+   mips_ip.  */
+
 static expressionS imm_expr;
 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;
 
-/* FIXME: This should be handled in a different way.  */
-extern int target_big_endian;
+/* This is set by mips_ip if imm_reloc is an unmatched HI16_S reloc.  */
+
+static boolean imm_unmatched_hi;
 
 /*
  * This function is called once, at assembler startup time.  It should
@@ -530,42 +600,110 @@ md_begin ()
       if (strcmp (cpu, "mips") == 0)
        {
          mips_isa = 1;
-         mips_cpu = 3000;
+         if (mips_cpu == -1)
+           mips_cpu = 3000;
        }
       else if (strcmp (cpu, "r6000") == 0
               || strcmp (cpu, "mips2") == 0)
        {
          mips_isa = 2;
-         mips_cpu = 6000;
+         if (mips_cpu == -1)
+           mips_cpu = 6000;
        }
       else if (strcmp (cpu, "mips64") == 0
               || strcmp (cpu, "r4000") == 0
               || strcmp (cpu, "mips3") == 0)
        {
          mips_isa = 3;
-         mips_cpu = 4000;
+         if (mips_cpu == -1)
+           mips_cpu = 4000;
        }
       else if (strcmp (cpu, "r4400") == 0)
        {
          mips_isa = 3;
-         mips_cpu = 4400;
+         if (mips_cpu == -1)
+           mips_cpu = 4400;
        }
       else if (strcmp (cpu, "mips64orion") == 0
               || strcmp (cpu, "r4600") == 0)
        {
          mips_isa = 3;
-         mips_cpu = 4600;
+         if (mips_cpu == -1)
+           mips_cpu = 4600;
+       }
+      else if (strcmp (cpu, "r4650") == 0)
+       {
+         mips_isa = 3;
+         if (mips_cpu == -1)
+           mips_cpu = 4650;
+         if (mips_4650 == -1)
+           mips_4650 = 1;
+       }
+      else if (strcmp (cpu, "mips64vr4300") == 0)
+       {
+         mips_isa = 3;
+         if (mips_cpu == -1)
+           mips_cpu = 4300;
+       }
+      else if (strcmp (cpu, "mips64vr4100") == 0)
+        {
+          mips_isa = 3;
+          if (mips_cpu == -1)
+            mips_cpu = 4100;
+          if (mips_4100 == -1)
+            mips_4100 = 1;
+        }
+      else if (strcmp (cpu, "r4010") == 0)
+       {
+         mips_isa = 2;
+         if (mips_cpu == -1)
+           mips_cpu = 4010;
+         if (mips_4010 == -1)
+           mips_4010 = 1;
+       }
+      else if (strcmp (cpu, "r8000") == 0
+              || strcmp (cpu, "mips4") == 0)
+       {
+         mips_isa = 4;
+         if (mips_cpu == -1)
+           mips_cpu = 8000;
+       }
+      else if (strcmp (cpu, "r10000") == 0)
+       {
+         mips_isa = 4;
+         if (mips_cpu == -1)
+           mips_cpu = 10000;
        }
       else
        {
          mips_isa = 1;
-         mips_cpu = 3000;
+         if (mips_cpu == -1)
+           mips_cpu = 3000;
        }
 
       if (a != NULL)
        free (a);
     }
 
+  if (mips_4650 < 0)
+    mips_4650 = 0;
+
+  if (mips_4010 < 0)
+    mips_4010 = 0;
+
+  if (mips_4100 < 0)
+    mips_4100 = 0;
+
+  if (mips_4650 || mips_4010 || mips_4100 || mips_cpu == 4300)
+    interlocks = 1;
+  else
+    interlocks = 0;
+
+  if (mips_cpu == 4300)
+    cop_interlocks = 1;
+  else
+    cop_interlocks = 0;
+
   if (mips_isa < 2 && mips_trap)
     as_bad ("trap exception not supported at ISA 1");
 
@@ -580,6 +718,9 @@ md_begin ()
     case 3:
       ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, 4000);
       break;
+    case 4:
+      ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, 8000);
+      break;
     }
   if (! ok)
     as_warn ("Could not set architecture and machine");
@@ -625,61 +766,92 @@ md_begin ()
   /* set the default alignment for the text section (2**2) */
   record_alignment (text_section, 2);
 
-  /* FIXME: This should be handled in a different way.  */
-  target_big_endian = byte_order == BIG_ENDIAN;
+  if (USE_GLOBAL_POINTER_OPT)
+    bfd_set_gp_size (stdoutput, g_switch_value);
 
-#ifdef GPOPT
-  bfd_set_gp_size (stdoutput, g_switch_value);
-#endif
+  if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+    {
+      /* Sections must be aligned to 16 byte boundaries.  */
+      (void) bfd_set_section_alignment (stdoutput, text_section, 4);
+      (void) bfd_set_section_alignment (stdoutput, data_section, 4);
+      (void) bfd_set_section_alignment (stdoutput, bss_section, 4);
 
+      /* Create a .reginfo section for register masks and a .mdebug
+        section for debugging information.  */
+      {
+       segT seg;
+       subsegT subseg;
+       segT sec;
+
+       seg = now_seg;
+       subseg = now_subseg;
+
+       if (! mips_64)
+         {
+           sec = subseg_new (".reginfo", (subsegT) 0);
+
+           /* The ABI says this section should be loaded so that the
+              running program can access it.  */
+           (void) bfd_set_section_flags (stdoutput, sec,
+                                         (SEC_ALLOC | SEC_LOAD
+                                          | SEC_READONLY | SEC_DATA));
+           (void) bfd_set_section_alignment (stdoutput, sec, 2);
+       
 #ifdef OBJ_ELF
-  /* Sections must be aligned to 16 byte boundaries.  */
-  (void) bfd_set_section_alignment (stdoutput, text_section, 4);
-  (void) bfd_set_section_alignment (stdoutput, data_section, 4);
-  (void) bfd_set_section_alignment (stdoutput, bss_section, 4);
+           mips_regmask_frag = frag_more (sizeof (Elf32_External_RegInfo));
+#endif
+         }
+       else
+         {
+           /* The 64-bit ABI uses a .MIPS.options section rather than
+               .reginfo section.  */
+           sec = subseg_new (".MIPS.options", (subsegT) 0);
+           (void) bfd_set_section_flags (stdoutput, sec,
+                                         (SEC_ALLOC | SEC_LOAD
+                                          | SEC_READONLY | SEC_DATA));
+           (void) bfd_set_section_alignment (stdoutput, sec, 3);
 
-  /* Create a .reginfo section for register masks and a .mdebug
-     section for debugging information.  */
-  {
-    segT seg;
-    subsegT subseg;
-    segT sec;
-
-    seg = now_seg;
-    subseg = now_subseg;
-    sec = subseg_new (".reginfo", (subsegT) 0);
-
-    /* The ABI says this section should be loaded so that the running
-       program can access it.  */
-    (void) bfd_set_section_flags (stdoutput, sec,
-                                 (SEC_ALLOC | SEC_LOAD
-                                  | SEC_READONLY | SEC_DATA));
-    (void) bfd_set_section_alignment (stdoutput, sec, 2);
-
-    mips_regmask_frag = frag_more (sizeof (Elf32_External_RegInfo));
-
-#ifdef ECOFF_DEBUGGING
-    sec = subseg_new (".mdebug", (subsegT) 0);
-    (void) bfd_set_section_flags (stdoutput, sec,
-                                 SEC_HAS_CONTENTS | SEC_READONLY);
-    (void) bfd_set_section_alignment (stdoutput, sec, 2);
+#ifdef OBJ_ELF
+           /* Set up the option header.  */
+           {
+             Elf_Internal_Options opthdr;
+             char *f;
+
+             opthdr.kind = ODK_REGINFO;
+             opthdr.size = (sizeof (Elf_External_Options)
+                            + sizeof (Elf64_External_RegInfo));
+             opthdr.section = 0;
+             opthdr.info = 0;
+             f = frag_more (sizeof (Elf_External_Options));
+             bfd_mips_elf_swap_options_out (stdoutput, &opthdr,
+                                            (Elf_External_Options *) f);
+
+             mips_regmask_frag = frag_more (sizeof (Elf64_External_RegInfo));
+           }
 #endif
+         }
 
-    subseg_set (seg, subseg);
-  }
-#endif /* OBJ_ELF */
+       if (ECOFF_DEBUGGING)
+         {
+           sec = subseg_new (".mdebug", (subsegT) 0);
+           (void) bfd_set_section_flags (stdoutput, sec,
+                                         SEC_HAS_CONTENTS | SEC_READONLY);
+           (void) bfd_set_section_alignment (stdoutput, sec, 2);
+         }
 
-#ifndef ECOFF_DEBUGGING
-  md_obj_begin ();
-#endif
+       subseg_set (seg, subseg);
+      }
+    }
+
+  if (! ECOFF_DEBUGGING)
+    md_obj_begin ();
 }
 
 void
 md_mips_end ()
 {
-#ifndef ECOFF_DEBUGGING
-  md_obj_end ();
-#endif
+  if (! ECOFF_DEBUGGING)
+    md_obj_end ();
 }
 
 void
@@ -689,7 +861,10 @@ md_assemble (str)
   struct mips_cl_insn insn;
 
   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;
 
   mips_ip (str, &insn);
   if (insn_error)
@@ -704,11 +879,12 @@ md_assemble (str)
   else
     {
       if (imm_expr.X_op != O_absent)
-       append_insn ((char *) NULL, &insn, &imm_expr, imm_reloc);
+       append_insn ((char *) 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);
+       append_insn ((char *) NULL, &insn, &offset_expr, offset_reloc, false);
       else
-       append_insn ((char *) NULL, &insn, NULL, BFD_RELOC_UNUSED);
+       append_insn ((char *) NULL, &insn, NULL, BFD_RELOC_UNUSED, false);
     }
 }
 
@@ -756,17 +932,46 @@ insn_uses_reg (ip, reg, fpr)
   return 0;
 }
 
+/* This function returns true if modifying a register requires a
+   delay.  */
+
+static int
+reg_needs_delay (reg)
+     int reg;
+{
+  unsigned long prev_pinfo;
+
+  prev_pinfo = prev_insn.insn_mo->pinfo;
+  if (! mips_noreorder
+      && mips_isa < 4
+      && ((prev_pinfo & INSN_LOAD_COPROC_DELAY)
+         || (mips_isa < 2
+             && (prev_pinfo & INSN_LOAD_MEMORY_DELAY))))
+    {
+      /* A load from a coprocessor or from memory.  All load
+        delays delay the use of general register rt for one
+        instruction on the r3000.  The r6000 and r4000 use
+        interlocks.  */
+      know (prev_pinfo & INSN_WRITE_GPR_T);
+      if (reg == ((prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT))
+       return 1;
+    }
+
+  return 0;
+}
+
 /* Output an instruction.  PLACE is where to put the instruction; if
    it is NULL, this uses frag_more to get room.  IP is the instruction
    information.  ADDRESS_EXPR is an operand of the instruction to be
    used with RELOC_TYPE.  */
 
 static void
-append_insn (place, ip, address_expr, reloc_type)
+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;
+     boolean unmatched_hi;
 {
   register unsigned long prev_pinfo, pinfo;
   char *f;
@@ -803,9 +1008,11 @@ append_insn (place, ip, address_expr, reloc_type)
 
       /* The previous insn might require a delay slot, depending upon
         the contents of the current insn.  */
-      if ((prev_pinfo & INSN_LOAD_COPROC_DELAY)
-         || (mips_isa < 2
-             && (prev_pinfo & INSN_LOAD_MEMORY_DELAY)))
+      if (mips_isa < 4
+         && (((prev_pinfo & INSN_LOAD_COPROC_DELAY)
+               && ! cop_interlocks)
+             || (mips_isa < 2
+                 && (prev_pinfo & INSN_LOAD_MEMORY_DELAY))))
        {
          /* A load from a coprocessor or from memory.  All load
             delays delay the use of general register rt for one
@@ -819,9 +1026,11 @@ append_insn (place, ip, address_expr, reloc_type)
                                0))
            ++nops;
        }
-      else if ((prev_pinfo & INSN_COPROC_MOVE_DELAY)
-              || (mips_isa < 2
-                  && (prev_pinfo & INSN_COPROC_MEMORY_DELAY)))
+      else if (mips_isa < 4
+              && (((prev_pinfo & INSN_COPROC_MOVE_DELAY)
+                    && ! cop_interlocks)
+                  || (mips_isa < 2
+                      && (prev_pinfo & INSN_COPROC_MEMORY_DELAY))))
        {
          /* A generic coprocessor delay.  The previous instruction
             modified a coprocessor general or control register.  If
@@ -871,7 +1080,9 @@ append_insn (place, ip, address_expr, reloc_type)
                ++nops;
            }
        }
-      else if (prev_pinfo & INSN_WRITE_COND_CODE)
+      else if (mips_isa < 4
+              && (prev_pinfo & INSN_WRITE_COND_CODE)
+               && ! cop_interlocks)
        {
          /* The previous instruction sets the coprocessor condition
             codes, but does not require a general coprocessor delay
@@ -886,18 +1097,22 @@ append_insn (place, ip, address_expr, reloc_type)
        {
          /* The previous instruction reads the LO register; if the
             current instruction writes to the LO register, we must
-            insert two NOPS.  */
-         if (mips_optimize == 0
-             || (pinfo & INSN_WRITE_LO))
+            insert two NOPS.  The R4650, VR4100 and VR4300 have
+            interlocks.  */
+         if (! interlocks
+             && (mips_optimize == 0
+                 || (pinfo & INSN_WRITE_LO)))
            nops += 2;
        }
       else if (prev_insn.insn_mo->pinfo & INSN_READ_HI)
        {
          /* The previous instruction reads the HI register; if the
             current instruction writes to the HI register, we must
-            insert a NOP.  */
-         if (mips_optimize == 0
-             || (pinfo & INSN_WRITE_HI))
+            insert a NOP.  The R4650, VR4100 and VR4300 have
+            interlocks.  */
+         if (! interlocks
+             && (mips_optimize == 0
+                 || (pinfo & INSN_WRITE_HI)))
            nops += 2;
        }
 
@@ -905,18 +1120,23 @@ append_insn (place, ip, address_expr, reloc_type)
         instructions: 1) setting the condition codes using a move to
         coprocessor instruction which requires a general coprocessor
         delay and then reading the condition codes 2) reading the HI
-        or LO register and then writing to it.  If we are not already
-        emitting a NOP instruction, we must check for these cases
-        compared to the instruction previous to the previous
+        or LO register and then writing to it (except on the R4650,
+        VR4100, and VR4300 which have interlocks).  If we are not
+        already emitting a NOP instruction, we must check for these
+        cases compared to the instruction previous to the previous
         instruction.  */
       if (nops == 0
-         && (((prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY)
+         && ((mips_isa < 4
+              && (prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY)
               && (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
-              && (pinfo & INSN_READ_COND_CODE))
+              && (pinfo & INSN_READ_COND_CODE)
+               && ! cop_interlocks)
              || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)
-                 && (pinfo & INSN_WRITE_LO))
+                 && (pinfo & INSN_WRITE_LO)
+                 && ! interlocks)
              || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
-                 && (pinfo & INSN_WRITE_HI))))
+                 && (pinfo & INSN_WRITE_HI)
+                 && ! interlocks)))
        ++nops;
 
       /* If we are being given a nop instruction, don't bother with
@@ -929,18 +1149,41 @@ append_insn (place, ip, address_expr, reloc_type)
       /* Now emit the right number of NOP instructions.  */
       if (nops > 0)
        {
+         fragS *old_frag;
+         unsigned long old_frag_offset;
          int i;
 
+         old_frag = frag_now;
+         old_frag_offset = frag_now_fix ();
+
          for (i = 0; i < nops; i++)
            emit_nop ();
+
          if (listing)
-           listing_prev_line ();
+           {
+             listing_prev_line ();
+             /* We may be at the start of a variant frag.  In case we
+                 are, make sure there is enough space for the frag
+                 after the frags created by listing_prev_line.  The
+                 argument to frag_grow here must be at least as large
+                 as the argument to all other calls to frag_grow in
+                 this file.  We don't have to worry about being in the
+                 middle of a variant frag, because the variants insert
+                 all needed nop instructions themselves.  */
+             frag_grow (40);
+           }
+
          if (insn_label != NULL)
            {
              assert (S_GET_SEGMENT (insn_label) == now_seg);
              insn_label->sy_frag = frag_now;
              S_SET_VALUE (insn_label, (valueT) frag_now_fix ());
            }
+
+#ifndef NO_ECOFF_DEBUGGING
+         if (ECOFF_DEBUGGING)
+           ecoff_fix_loc (old_frag, old_frag_offset);
+#endif
        }
     }
   
@@ -978,10 +1221,24 @@ append_insn (place, ip, address_expr, reloc_type)
          /* 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 = fix_new_exp (frag_now, f - frag_now->fr_literal, 4,
+                                 address_expr,
+                                 reloc_type == BFD_RELOC_16_PCREL_S2,
+                                 reloc_type);
+             if (unmatched_hi)
+               {
+                 struct mips_hi_fixup *hi_fixup;
+
+                 assert (reloc_type == BFD_RELOC_HI16_S);
+                 hi_fixup = ((struct mips_hi_fixup *)
+                             xmalloc (sizeof (struct mips_hi_fixup)));
+                 hi_fixup->fixp = fixp;
+                 hi_fixup->seg = now_seg;
+                 hi_fixup->next = mips_hi_fixup_list;
+                 mips_hi_fixup_list = hi_fixup;
+               }
+           }
        }
     }
 
@@ -1002,6 +1259,8 @@ append_insn (place, ip, address_expr, reloc_type)
     mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FS) & OP_MASK_FS);
   if ((pinfo & (INSN_WRITE_FPR_T | INSN_READ_FPR_T)) != 0)
     mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FT) & OP_MASK_FT);
+  if ((pinfo & INSN_READ_FPR_R) != 0)
+    mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FR) & OP_MASK_FR);
   if (pinfo & INSN_COP)
     {
       /* We don't keep enough information to sort these cases out.  */
@@ -1066,16 +1325,20 @@ append_insn (place, ip, address_expr, reloc_type)
                   bc1t LABEL
                 we can not swap, and I don't feel like handling that
                 case.  */
-             || (pinfo & INSN_READ_COND_CODE)
+             || (mips_isa < 4
+                 && (pinfo & INSN_READ_COND_CODE))
              /* We can not swap with an instruction that requires a
                 delay slot, becase the target of the branch might
                 interfere with that instruction.  */
-             || (prev_pinfo
-                 & (INSN_LOAD_COPROC_DELAY
-                    | INSN_COPROC_MOVE_DELAY
-                    | INSN_WRITE_COND_CODE
-                    | INSN_READ_LO
-                    | INSN_READ_HI))
+             || (mips_isa < 4
+                 && (prev_pinfo
+                     & (INSN_LOAD_COPROC_DELAY
+                        | INSN_COPROC_MOVE_DELAY
+                        | INSN_WRITE_COND_CODE)))
+             || (! interlocks
+                 && (prev_pinfo
+                     & (INSN_READ_LO
+                        | INSN_READ_HI)))
              || (mips_isa < 2
                  && (prev_pinfo
                      & (INSN_LOAD_MEMORY_DELAY
@@ -1138,10 +1401,11 @@ append_insn (place, ip, address_expr, reloc_type)
              /* If the previous previous instruction has a load
                 delay, and sets a register that the branch reads, we
                 can not swap.  */
-             || (((prev_prev_insn.insn_mo->pinfo & INSN_LOAD_COPROC_DELAY)
-                  || (mips_isa < 2
-                      && (prev_prev_insn.insn_mo->pinfo
-                          & INSN_LOAD_MEMORY_DELAY)))
+             || (mips_isa < 4
+                 && ((prev_prev_insn.insn_mo->pinfo & INSN_LOAD_COPROC_DELAY)
+                     || (mips_isa < 2
+                         && (prev_prev_insn.insn_mo->pinfo
+                             & INSN_LOAD_MEMORY_DELAY)))
                  && insn_uses_reg (ip,
                                    ((prev_prev_insn.insn_opcode >> OP_SH_RT)
                                     & OP_MASK_RT),
@@ -1256,26 +1520,36 @@ mips_emit_delays ()
       int nop;
 
       nop = 0;
-      if ((prev_insn.insn_mo->pinfo
-          & (INSN_LOAD_COPROC_DELAY
-             | INSN_COPROC_MOVE_DELAY
-             | INSN_WRITE_COND_CODE
-             | INSN_READ_LO
-             | INSN_READ_HI))
+      if ((mips_isa < 4
+          && (! cop_interlocks
+               && (prev_insn.insn_mo->pinfo
+                   & (INSN_LOAD_COPROC_DELAY
+                      | INSN_COPROC_MOVE_DELAY
+                      | INSN_WRITE_COND_CODE))))
+         || (! interlocks
+             && (prev_insn.insn_mo->pinfo
+                 & (INSN_READ_LO
+                    | INSN_READ_HI)))
          || (mips_isa < 2
              && (prev_insn.insn_mo->pinfo
                  & (INSN_LOAD_MEMORY_DELAY
                     | INSN_COPROC_MEMORY_DELAY))))
        {
          nop = 1;
-         if ((prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
-             || (prev_insn.insn_mo->pinfo & INSN_READ_HI)
-             || (prev_insn.insn_mo->pinfo & INSN_READ_LO))
+         if ((mips_isa < 4
+              && (! cop_interlocks
+                   && prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE))
+             || (! interlocks
+                 && ((prev_insn.insn_mo->pinfo & INSN_READ_HI)
+                     || (prev_insn.insn_mo->pinfo & INSN_READ_LO))))
            emit_nop ();
        }
-      else if ((prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
-              || (prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
-              || (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO))
+      else if ((mips_isa < 4
+               && (! cop_interlocks
+                    && prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE))
+              || (! interlocks
+                  && ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
+                      || (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO))))
        nop = 1;
       if (nop)
        {
@@ -1297,7 +1571,7 @@ mips_emit_delays ()
    expression, the name of the instruction to build, an operand format
    string, and corresponding arguments.  */
 
-#ifndef NO_STDARG
+#ifdef USE_STDARG
 static void
 macro_build (char *place,
             int *counter,
@@ -1305,7 +1579,7 @@ macro_build (char *place,
             const char *name,
             const char *fmt,
             ...)
-#else /* ! defined (NO_STDARG) */
+#else
 static void
 macro_build (place, counter, ep, name, fmt, va_alist)
      char *place;
@@ -1314,13 +1588,13 @@ macro_build (place, counter, ep, name, fmt, va_alist)
      const char *name;
      const char *fmt;
      va_dcl
-#endif /* ! defined (NO_STDARG) */
+#endif
 {
   struct mips_cl_insn insn;
   bfd_reloc_code_real_type r;
   va_list args;
 
-#ifndef NO_STDARG
+#ifdef USE_STDARG
   va_start (args, fmt);
 #else
   va_start (args);
@@ -1343,7 +1617,19 @@ macro_build (place, counter, ep, name, fmt, va_alist)
   assert (strcmp (name, insn.insn_mo->name) == 0);
 
   while (strcmp (fmt, insn.insn_mo->args) != 0
-        || insn.insn_mo->pinfo == INSN_MACRO)
+        || insn.insn_mo->pinfo == INSN_MACRO
+        || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_ISA2
+            && mips_isa < 2)
+        || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_ISA3
+            && mips_isa < 3)
+        || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_ISA4
+            && mips_isa < 4)
+        || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_4650
+            && ! mips_4650)
+        || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_4010
+            && ! mips_4010)
+        || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_4100
+            && ! mips_4100))
     {
       ++insn.insn_mo;
       assert (insn.insn_mo->name);
@@ -1415,9 +1701,10 @@ macro_build (place, counter, ep, name, fmt, va_alist)
                  || r == BFD_RELOC_LO16
                  || r == BFD_RELOC_MIPS_GOT16
                  || r == BFD_RELOC_MIPS_CALL16
+                 || r == BFD_RELOC_MIPS_GOT_LO16
+                 || r == BFD_RELOC_MIPS_CALL_LO16
                  || (ep->X_op == O_subtract
                      && now_seg == text_section
-                     && S_GET_SEGMENT (ep->X_op_symbol) == text_section
                      && r == BFD_RELOC_PCREL_LO16));
          continue;
 
@@ -1427,10 +1714,11 @@ macro_build (place, counter, ep, name, fmt, va_alist)
                  && (ep->X_op == O_constant
                      || (ep->X_op == O_symbol
                          && (r == BFD_RELOC_HI16_S
-                             || r == BFD_RELOC_HI16))
+                             || r == BFD_RELOC_HI16
+                             || r == BFD_RELOC_MIPS_GOT_HI16
+                             || r == BFD_RELOC_MIPS_CALL_HI16))
                      || (ep->X_op == O_subtract
                          && now_seg == text_section
-                         && S_GET_SEGMENT (ep->X_op_symbol) == text_section
                          && r == BFD_RELOC_PCREL_HI16_S)));
          if (ep->X_op == O_constant)
            {
@@ -1471,7 +1759,7 @@ macro_build (place, counter, ep, name, fmt, va_alist)
   va_end (args);
   assert (r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
 
-  append_insn (place, &insn, ep, r);
+  append_insn (place, &insn, ep, r, false);
 }
 
 /*
@@ -1495,7 +1783,7 @@ macro_build_lui (place, counter, ep, regnum)
   else
     {
       high_expr.X_op = O_constant;
-      high_expr.X_add_number = 0;
+      high_expr.X_add_number = ep->X_add_number;
     }
 
   if (high_expr.X_op == O_constant)
@@ -1536,10 +1824,10 @@ macro_build_lui (place, counter, ep, regnum)
   if (r == BFD_RELOC_UNUSED)
     {
       insn.insn_opcode |= high_expr.X_add_number;
-      append_insn (place, &insn, NULL, r);
+      append_insn (place, &insn, NULL, r, false);
     }
   else
-    append_insn (place, &insn, &high_expr, r);
+    append_insn (place, &insn, &high_expr, r, false);
 }
 
 /*                     set_at()
@@ -1558,7 +1846,7 @@ set_at (counter, reg, unsignedp)
                 "t,r,j", AT, reg, (int) BFD_RELOC_LO16);
   else
     {
-      load_register (counter, AT, &imm_expr);
+      load_register (counter, AT, &imm_expr, 0);
       macro_build ((char *) NULL, counter, NULL,
                   unsignedp ? "sltu" : "slt",
                   "d,v,t", AT, reg, AT);
@@ -1581,72 +1869,170 @@ check_absolute_expr (ip, ex)
  *  an absolute expression value into a register.
  */
 static void
-load_register (counter, reg, ep)
+load_register (counter, reg, ep, dbl)
      int *counter;
      int reg;
      expressionS *ep;
+     int dbl;
 {
-  assert (ep->X_op == O_constant);
-  if (ep->X_add_number >= -0x8000 && ep->X_add_number < 0x8000)
-    {
-      /* No need to ever use daddiu here, since we are adding in
-         register $zero.  */
-      macro_build ((char *) NULL, counter, ep, "addiu", "t,r,j", reg, 0,
-                  (int) BFD_RELOC_LO16);
-    }
-  else if (ep->X_add_number >= 0 && ep->X_add_number < 0x10000)
-    macro_build ((char *) NULL, counter, ep, "ori", "t,r,i", reg, 0,
-                (int) BFD_RELOC_LO16);
-  else if ((ep->X_add_number &~ (offsetT) 0x7fffffff) == 0
-          || ((ep->X_add_number &~ (offsetT) 0x7fffffff)
-              == ~ (offsetT) 0x7fffffff))
+  int shift, freg;
+  expressionS hi32, lo32, tmp;
+
+  if (ep->X_op != O_big)
     {
-      macro_build ((char *) NULL, counter, ep, "lui", "t,u", reg,
-                  (int) BFD_RELOC_HI16);
-      if ((ep->X_add_number & 0xffff) != 0)
-       macro_build ((char *) NULL, counter, ep, "ori", "t,r,i", reg, reg,
-                    (int) BFD_RELOC_LO16);
+      assert (ep->X_op == O_constant);
+      if (ep->X_add_number < 0x8000
+         && (ep->X_add_number >= 0
+             || (ep->X_add_number >= -0x8000
+                 && (! dbl
+                     || ! ep->X_unsigned
+                     || sizeof (ep->X_add_number) > 4))))
+       {
+         /* We can handle 16 bit signed values with an addiu to
+            $zero.  No need to ever use daddiu here, since $zero and
+            the result are always correct in 32 bit mode.  */
+         macro_build ((char *) NULL, counter, ep, "addiu", "t,r,j", reg, 0,
+                      (int) BFD_RELOC_LO16);
+         return;
+       }
+      else if (ep->X_add_number >= 0 && ep->X_add_number < 0x10000)
+       {
+         /* We can handle 16 bit unsigned values with an ori to
+             $zero.  */
+         macro_build ((char *) NULL, counter, ep, "ori", "t,r,i", reg, 0,
+                      (int) BFD_RELOC_LO16);
+         return;
+       }
+      else if ((((ep->X_add_number &~ (offsetT) 0x7fffffff) == 0
+                || ((ep->X_add_number &~ (offsetT) 0x7fffffff)
+                    == ~ (offsetT) 0x7fffffff))
+               && (! dbl
+                   || ! ep->X_unsigned
+                   || sizeof (ep->X_add_number) > 4
+                   || (ep->X_add_number & 0x80000000) == 0))
+              || (mips_isa < 3
+                  && (ep->X_add_number &~ 0xffffffff) == 0))
+       {
+         /* 32 bit values require an lui.  */
+         macro_build ((char *) NULL, counter, ep, "lui", "t,u", reg,
+                      (int) BFD_RELOC_HI16);
+         if ((ep->X_add_number & 0xffff) != 0)
+           macro_build ((char *) NULL, counter, ep, "ori", "t,r,i", reg, reg,
+                        (int) BFD_RELOC_LO16);
+         return;
+       }
+      else
+       {
+         /* 32 bit value with high bit set being loaded into a 64 bit
+             register.  We can't use lui, because that would
+             incorrectly set the 32 high bits.  */
+         generic_bignum[3] = 0;
+         generic_bignum[2] = 0;
+         generic_bignum[1] = (ep->X_add_number >> 16) & 0xffff;
+         generic_bignum[0] = ep->X_add_number & 0xffff;
+         tmp.X_op = O_big;
+         tmp.X_add_number = 4;
+         ep = &tmp;
+       }
     }
-  else if (mips_isa < 3)
+
+  /* The value is larger than 32 bits.  */
+
+  if (mips_isa < 3)
     {
       as_bad ("Number larger than 32 bits");
       macro_build ((char *) NULL, counter, ep, "addiu", "t,r,j", reg, 0,
                   (int) BFD_RELOC_LO16);
+      return;
     }
-  else
-    {
-      int shift;
-      expressionS hi32, lo32;
 
+  if (ep->X_op != O_big)
+    {
       hi32 = *ep;
       shift = 32;
       hi32.X_add_number >>= shift;
       hi32.X_add_number &= 0xffffffff;
       if ((hi32.X_add_number & 0x80000000) != 0)
        hi32.X_add_number |= ~ (offsetT) 0xffffffff;
-      load_register (counter, reg, &hi32);
       lo32 = *ep;
       lo32.X_add_number &= 0xffffffff;
-      if ((lo32.X_add_number & 0xffff0000) == 0)
-       macro_build ((char *) NULL, counter, NULL, "dsll32", "d,w,<", reg,
-                    reg, 0);
-      else
+    }
+  else
+    {
+      assert (ep->X_add_number > 2);
+      if (ep->X_add_number == 3)
+       generic_bignum[3] = 0;
+      else if (ep->X_add_number > 4)
+       as_bad ("Number larger than 64 bits");
+      lo32.X_op = O_constant;
+      lo32.X_add_number = generic_bignum[0] + (generic_bignum[1] << 16);
+      hi32.X_op = O_constant;
+      hi32.X_add_number = generic_bignum[2] + (generic_bignum[3] << 16);
+    }
+
+  if (hi32.X_add_number == 0)
+    freg = 0;
+  else
+    {
+      if (hi32.X_add_number == 0xffffffff)
+        {
+          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);
+              macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i", reg, reg,
+                           (int) BFD_RELOC_LO16);
+              return;
+            }
+        }
+      load_register (counter, reg, &hi32, 0);
+      freg = reg;
+    }
+  if ((lo32.X_add_number & 0xffff0000) == 0)
+    {
+      if (freg != 0)
        {
-         expressionS mid16;
+         macro_build ((char *) NULL, counter, NULL, "dsll32", "d,w,<", reg,
+                      freg, 0);
+         freg = reg;
+       }
+    }
+  else
+    {
+      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, 32);
+          return;
+        }
+
+      if (freg != 0)
+       {
          macro_build ((char *) NULL, counter, NULL, "dsll", "d,w,<", reg,
-                      reg, 16);
-         mid16 = lo32;
-         mid16.X_add_number >>= 16;
-         macro_build ((char *) NULL, counter, &mid16, "ori", "t,r,i", reg,
-                      reg, (int) BFD_RELOC_LO16);
-         macro_build ((char *) NULL, counter, NULL, "dsll", "d,w,<", reg,
-                      reg, 16);
-       }
-      if ((lo32.X_add_number & 0xffff) != 0)
-       macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i", reg, reg,
-                    (int) BFD_RELOC_LO16);
+                      freg, 16);
+         freg = reg;
+       }
+      mid16 = lo32;
+      mid16.X_add_number >>= 16;
+      macro_build ((char *) NULL, counter, &mid16, "ori", "t,r,i", reg,
+                  freg, (int) BFD_RELOC_LO16);
+      macro_build ((char *) NULL, counter, NULL, "dsll", "d,w,<", reg,
+                  reg, 16);
+      freg = reg;
     }
+  if ((lo32.X_add_number & 0xffff) != 0)
+    macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i", reg, freg,
+                (int) BFD_RELOC_LO16);
 }
 
 /* Load an address into a register.  */
@@ -1668,7 +2054,7 @@ load_address (counter, reg, ep)
 
   if (ep->X_op == O_constant)
     {
-      load_register (counter, reg, ep);
+      load_register (counter, reg, ep, 0);
       return;
     }
 
@@ -1680,7 +2066,8 @@ load_address (counter, reg, ep)
           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 (ep->X_add_number != 0)
+      if ((valueT) ep->X_add_number >= MAX_GPREL_OFFSET
+          || nopic_need_relax (ep->X_add_symbol))
        p = NULL;
       else
        {
@@ -1689,7 +2076,7 @@ load_address (counter, reg, ep)
                       mips_isa < 3 ? "addiu" : "daddiu",
                       "t,r,j", reg, GP, (int) BFD_RELOC_MIPS_GPREL);
          p = frag_var (rs_machine_dependent, 8, 0,
-                       RELAX_ENCODE (4, 8, -4, 0, 0, mips_warn_about_macros),
+                       RELAX_ENCODE (4, 8, 0, 4, 0, mips_warn_about_macros),
                        ep->X_add_symbol, (long) 0, (char *) NULL);
        }
       macro_build_lui (p, counter, ep, reg);
@@ -1699,7 +2086,7 @@ load_address (counter, reg, ep)
                   mips_isa < 3 ? "addiu" : "daddiu",
                   "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
     }
-  else if (mips_pic == SVR4_PIC)
+  else if (mips_pic == SVR4_PIC && ! mips_big_got)
     {
       expressionS ex;
 
@@ -1728,7 +2115,69 @@ load_address (counter, reg, ep)
          if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
            as_bad ("PIC code offset overflow (max 16 signed bits)");
          ex.X_op = O_constant;
-         macro_build (p, counter, &ex,
+         macro_build ((char *) NULL, counter, &ex,
+                      mips_isa < 3 ? "addiu" : "daddiu",
+                      "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+       }
+    }
+  else if (mips_pic == SVR4_PIC)
+    {
+      expressionS ex;
+      int off;
+
+      /* This is the large GOT case.  If this is a reference to an
+        external symbol, we want
+          lui          $reg,<sym>              (BFD_RELOC_MIPS_GOT_HI16)
+          addu         $reg,$reg,$gp
+          lw           $reg,<sym>($reg)        (BFD_RELOC_MIPS_GOT_LO16)
+        Otherwise, for a reference to a local symbol, we want
+          lw           $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT16)
+          nop
+          addiu        $reg,$reg,<sym>         (BFD_RELOC_LO16)
+        If there is a constant, it must be added in after.  */
+      ex.X_add_number = ep->X_add_number;
+      ep->X_add_number = 0;
+      if (reg_needs_delay (GP))
+       off = 4;
+      else
+       off = 0;
+      frag_grow (32);
+      macro_build ((char *) NULL, counter, ep, "lui", "t,u", reg,
+                  (int) BFD_RELOC_MIPS_GOT_HI16);
+      macro_build ((char *) NULL, counter, (expressionS *) NULL,
+                  mips_isa < 3 ? "addu" : "daddu",
+                  "d,v,t", reg, reg, GP);
+      macro_build ((char *) NULL, counter, ep,
+                  mips_isa < 3 ? "lw" : "ld",
+                  "t,o(b)", reg, (int) BFD_RELOC_MIPS_GOT_LO16, reg);
+      p = frag_var (rs_machine_dependent, 12 + off, 0,
+                   RELAX_ENCODE (12, 12 + off, off, 8 + off, 0,
+                                 mips_warn_about_macros),
+                   ep->X_add_symbol, (long) 0, (char *) NULL);
+      if (off > 0)
+       {
+         /* We need a nop before loading from $gp.  This special
+             check is required because the lui which starts the main
+             instruction stream does not refer to $gp, and so will not
+             insert the nop which may be required.  */
+         macro_build (p, counter, (expressionS *) NULL, "nop", "");
+         p += 4;
+       }
+      macro_build (p, counter, ep,
+                  mips_isa < 3 ? "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,
+                  mips_isa < 3 ? "addiu" : "daddiu",
+                  "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+      if (ex.X_add_number != 0)
+       {
+         if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+           as_bad ("PIC code offset overflow (max 16 signed bits)");
+         ex.X_op = O_constant;
+         macro_build ((char *) NULL, counter, &ex,
                       mips_isa < 3 ? "addiu" : "daddiu",
                       "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
        }
@@ -1780,7 +2229,9 @@ macro (ip)
   int likely = 0;
   int dbl = 0;
   int coproc = 0;
+  int lr = 0;
   offsetT maxnum;
+  int off;
   bfd_reloc_code_real_type r;
   char *p;
   int hold_mips_optimize;
@@ -1846,7 +2297,7 @@ macro (ip)
                       (int) BFD_RELOC_LO16);
          return;
        }
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, dbl);
       macro_build ((char *) NULL, &icnt, NULL, s2, "d,v,t", treg, sreg, AT);
       break;
 
@@ -1881,7 +2332,7 @@ macro (ip)
          return;
        }
 
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, 0);
       macro_build ((char *) NULL, &icnt, NULL, s2, "d,v,t", treg, sreg, AT);
       break;
 
@@ -1905,7 +2356,7 @@ macro (ip)
                       0);
          return;
        }
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, 0);
       macro_build ((char *) NULL, &icnt, &offset_expr, s, "s,t,p", sreg, AT);
       break;
 
@@ -2382,7 +2833,7 @@ macro (ip)
          return;
        }
 
-      load_register (&icnt, AT, &imm_expr);
+      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);
       break;
@@ -2420,6 +2871,8 @@ macro (ip)
       macro_build ((char *) NULL, &icnt, NULL, s2, "d", dreg);
       return;
 
+    case M_DLA_AB:
+      dbl = 1;
     case M_LA_AB:
       /* Load the address of a symbol into a register.  If breg is not
         zero, we then add a base register to it.  */
@@ -2435,7 +2888,12 @@ macro (ip)
       if (mips_pic == EMBEDDED_PIC
          && offset_expr.X_op == O_subtract
          && now_seg == text_section
-         && S_GET_SEGMENT (offset_expr.X_op_symbol) == text_section
+         && (offset_expr.X_op_symbol->sy_value.X_op == O_constant
+             ? S_GET_SEGMENT (offset_expr.X_op_symbol) == text_section
+             : (offset_expr.X_op_symbol->sy_value.X_op == O_symbol
+                && (S_GET_SEGMENT (offset_expr.X_op_symbol
+                                   ->sy_value.X_add_symbol)
+                    == text_section)))
          && breg == 0
          && offset_expr.X_add_number == 0)
        {
@@ -2466,7 +2924,7 @@ macro (ip)
        }
 
       if (offset_expr.X_op == O_constant)
-       load_register (&icnt, tempreg, &offset_expr);
+       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
@@ -2476,7 +2934,8 @@ macro (ip)
               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 (offset_expr.X_add_number != 0)
+         if ((valueT) offset_expr.X_add_number >= MAX_GPREL_OFFSET
+             || nopic_need_relax (offset_expr.X_add_symbol))
            p = NULL;
          else
            {
@@ -2497,7 +2956,7 @@ macro (ip)
                       mips_isa < 3 ? "addiu" : "daddiu",
                       "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
        }
-      else if (mips_pic == SVR4_PIC)
+      else if (mips_pic == SVR4_PIC && ! mips_big_got)
        {
          /* If this is a reference to an external symbol, and there
             is no constant, we want
@@ -2529,7 +2988,7 @@ macro (ip)
          offset_expr.X_add_number = 0;
          frag_grow (32);
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      mips_isa < 3 ? "lw" : "ld",
+                      dbl ? "ld" : "lw",
                       "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP);
          if (expr1.X_add_number == 0)
            {
@@ -2623,11 +3082,219 @@ macro (ip)
              used_at = 1;
            }
        }
-      else if (mips_pic == EMBEDDED_PIC)
+      else if (mips_pic == SVR4_PIC)
        {
-         /* We use
-              addiu    $tempreg,$gp,<sym>      (BFD_RELOC_MIPS_GPREL)
-            */
+         int gpdel;
+
+         /* 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)
+            For a local symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              nop
+              addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+
+            If we have a small constant, and this is a reference to
+            an external symbol, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
+              addu     $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+              nop
+              addiu    $tempreg,$tempreg,<constant>
+            For a local symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              nop
+              addiu    $tempreg,$tempreg,<constant> (BFD_RELOC_LO16)
+
+            If we have a large constant, and this is a reference to
+            an external symbol, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
+              addu     $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+              lui      $at,<hiconstant>
+              addiu    $at,$at,<loconstant>
+              addu     $tempreg,$tempreg,$at
+            For a local symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              lui      $at,<hiconstant>
+              addiu    $at,$at,<loconstant>    (BFD_RELOC_LO16)
+              addu     $tempreg,$tempreg,$at
+            */
+         expr1.X_add_number = offset_expr.X_add_number;
+         offset_expr.X_add_number = 0;
+         frag_grow (52);
+         if (reg_needs_delay (GP))
+           gpdel = 4;
+         else
+           gpdel = 0;
+         macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
+                      tempreg, (int) BFD_RELOC_MIPS_GOT_HI16);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                      mips_isa < 3 ? "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);
+         if (expr1.X_add_number == 0)
+           {
+             int off;
+
+             if (breg == 0)
+               off = 0;
+             else
+               {
+                 /* We're going to put in an addu instruction using
+                    tempreg, so we may as well insert the nop right
+                    now.  */
+                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                              "nop", "");
+                 off = 4;
+               }
+
+             p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
+                           RELAX_ENCODE (12 + off, 12 + gpdel, gpdel,
+                                         8 + gpdel, 0,
+                                         (breg == 0
+                                          ? mips_warn_about_macros
+                                          : 0)),
+                           offset_expr.X_add_symbol, (long) 0,
+                           (char *) NULL);
+           }
+         else if (expr1.X_add_number >= -0x8000
+                  && expr1.X_add_number < 0x8000)
+           {
+             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                          "nop", "");
+             macro_build ((char *) NULL, &icnt, &expr1,
+                          mips_isa < 3 ? "addiu" : "daddiu",
+                          "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
+
+             p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
+                           RELAX_ENCODE (20, 12 + gpdel, gpdel, 8 + gpdel, 0,
+                                         (breg == 0
+                                          ? mips_warn_about_macros
+                                          : 0)),
+                           offset_expr.X_add_symbol, (long) 0,
+                           (char *) NULL);
+           }
+         else
+           {
+             int adj, dreg;
+
+             /* If we are going to add in a base register, and the
+                target register and the base register are the same,
+                then we are using AT as a temporary register.  Since
+                we want to load the constant into AT, we add our
+                current AT (from the global offset table) and the
+                register into the register now, and pretend we were
+                not using a base register.  */
+             if (breg != treg)
+               {
+                 adj = 0;
+                 dreg = tempreg;
+               }
+             else
+               {
+                 assert (tempreg == AT);
+                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                              "nop", "");
+                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                              mips_isa < 3 ? "addu" : "daddu",
+                              "d,v,t", treg, AT, breg);
+                 dreg = treg;
+                 adj = 8;
+               }
+
+             /* Set mips_optimize around the lui instruction to avoid
+                inserting an unnecessary nop after the lw.  */
+             hold_mips_optimize = mips_optimize;
+             mips_optimize = 2;
+             macro_build_lui ((char *) NULL, &icnt, &expr1, AT);
+             mips_optimize = hold_mips_optimize;
+
+             macro_build ((char *) NULL, &icnt, &expr1,
+                          mips_isa < 3 ? "addiu" : "daddiu",
+                          "t,r,j", AT, AT, (int) BFD_RELOC_LO16);
+             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                          mips_isa < 3 ? "addu" : "daddu",
+                          "d,v,t", dreg, dreg, AT);
+
+             p = frag_var (rs_machine_dependent, 16 + gpdel + adj, 0,
+                           RELAX_ENCODE (24 + adj, 16 + gpdel + adj, gpdel,
+                                         8 + gpdel, 0,
+                                         (breg == 0
+                                          ? mips_warn_about_macros
+                                          : 0)),
+                           offset_expr.X_add_symbol, (long) 0,
+                           (char *) NULL);
+
+             used_at = 1;
+           }
+
+         if (gpdel > 0)
+           {
+             /* This is needed because this instruction uses $gp, but
+                 the first instruction on the main stream does not.  */
+             macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+             p += 4;
+           }
+         macro_build (p, &icnt, &offset_expr,
+                      dbl ? "ld" : "lw",
+                      "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP);
+         p += 4;
+         if (expr1.X_add_number >= -0x8000
+             && expr1.X_add_number < 0x8000)
+           {
+             macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+             p += 4;
+             macro_build (p, &icnt, &expr1,
+                          mips_isa < 3 ? "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,
+                 so if the symbol turns out to not be external, and
+                 the next instruction uses tempreg, an unnecessary nop
+                 will be inserted.  */
+           }
+         else
+           {
+             if (breg == treg)
+               {
+                 /* We must add in the base register now, as in the
+                     external symbol case.  */
+                 assert (tempreg == AT);
+                 macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+                 p += 4;
+                 macro_build (p, &icnt, (expressionS *) NULL,
+                              mips_isa < 3 ? "addu" : "daddu",
+                              "d,v,t", treg, AT, breg);
+                 p += 4;
+                 tempreg = treg;
+                 /* We set breg to 0 because we have arranged to add
+                     it in in both cases.  */
+                 breg = 0;
+               }
+
+             macro_build_lui (p, &icnt, &expr1, AT);
+             p += 4;
+             macro_build (p, &icnt, &expr1,
+                          mips_isa < 3 ? "addiu" : "daddiu",
+                          "t,r,j", AT, AT, (int) BFD_RELOC_LO16);
+             p += 4;
+             macro_build (p, &icnt, (expressionS *) NULL,
+                          mips_isa < 3 ? "addu" : "daddu",
+                          "d,v,t", tempreg, tempreg, AT);
+             p += 4;
+           }
+       }
+      else if (mips_pic == EMBEDDED_PIC)
+       {
+         /* We use
+              addiu    $tempreg,$gp,<sym>      (BFD_RELOC_MIPS_GPREL)
+            */
          macro_build ((char *) NULL, &icnt, &offset_expr,
                       mips_isa < 3 ? "addiu" : "daddiu",
                       "t,r,j", tempreg, GP, (int) BFD_RELOC_MIPS_GPREL);
@@ -2693,30 +3360,78 @@ macro (ip)
        macro_build ((char *) NULL, &icnt, &offset_expr, "jal", "a");
       else if (mips_pic == SVR4_PIC)
        {
-         /* If this is a reference to an external symbol, we want
+         /* If this is a reference to an external symbol, and we are
+            using a small GOT, we want
               lw       $25,<sym>($gp)          (BFD_RELOC_MIPS_CALL16)
               nop
               jalr     $25
               nop
               lw       $gp,cprestore($sp)
             The cprestore value is set using the .cprestore
-            pseudo-op.  If the symbol is not external, we want
+            pseudo-op.  If we are using a big GOT, we want
+              lui      $25,<sym>               (BFD_RELOC_MIPS_CALL_HI16)
+              addu     $25,$25,$gp
+              lw       $25,<sym>($25)          (BFD_RELOC_MIPS_CALL_LO16)
+              nop
+              jalr     $25
+              nop
+              lw       $gp,cprestore($sp)
+            If the symbol is not external, we want
               lw       $25,<sym>($gp)          (BFD_RELOC_MIPS_GOT16)
               nop
               addiu    $25,$25,<sym>           (BFD_RELOC_LO16)
               jalr     $25
               nop
-              lw       $gp,cprestore($sp)
-            */
-         frag_grow (20);
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      mips_isa < 3 ? "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, (long) 0, (char *) NULL);
+              lw $gp,cprestore($sp) */
+         frag_grow (40);
+         if (! mips_big_got)
+           {
+             macro_build ((char *) NULL, &icnt, &offset_expr,
+                          mips_isa < 3 ? "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, (long) 0, (char *) NULL);
+           }
+         else
+           {
+             int gpdel;
+
+             if (reg_needs_delay (GP))
+               gpdel = 4;
+             else
+               gpdel = 0;
+             macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
+                          PIC_CALL_REG, (int) BFD_RELOC_MIPS_CALL_HI16);
+             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                          mips_isa < 3 ? "addu" : "daddu",
+                          "d,v,t", PIC_CALL_REG, PIC_CALL_REG, GP);
+             macro_build ((char *) NULL, &icnt, &offset_expr,
+                          mips_isa < 3 ? "lw" : "ld",
+                          "t,o(b)", PIC_CALL_REG,
+                          (int) BFD_RELOC_MIPS_CALL_LO16, PIC_CALL_REG);
+             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                          "nop", "");
+             p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
+                           RELAX_ENCODE (16, 12 + gpdel, gpdel, 8 + gpdel,
+                                         0, 0),
+                           offset_expr.X_add_symbol, (long) 0, (char *) NULL);
+             if (gpdel > 0)
+               {
+                 macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+                 p += 4;
+               }
+             macro_build (p, &icnt, &offset_expr,
+                          mips_isa < 3 ? "lw" : "ld",
+                          "t,o(b)", PIC_CALL_REG,
+                          (int) BFD_RELOC_MIPS_GOT16, GP);
+             p += 4;
+             macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+             p += 4;
+           }                      
          macro_build (p, &icnt, &offset_expr,
                       mips_isa < 3 ? "addiu" : "daddiu",
                       "t,r,j", PIC_CALL_REG, PIC_CALL_REG,
@@ -2782,9 +3497,11 @@ macro (ip)
       goto ld;
     case M_LWL_AB:
       s = "lwl";
+      lr = 1;
       goto ld;
     case M_LWR_AB:
       s = "lwr";
+      lr = 1;
       goto ld;
     case M_LDC1_AB:
       s = "ldc1";
@@ -2800,9 +3517,11 @@ macro (ip)
       goto ld;
     case M_LDL_AB:
       s = "ldl";
+      lr = 1;
       goto ld;
     case M_LDR_AB:
       s = "ldr";
+      lr = 1;
       goto ld;
     case M_LL_AB:
       s = "ll";
@@ -2813,7 +3532,7 @@ macro (ip)
     case M_LWU_AB:
       s = "lwu";
     ld:
-      if (breg == treg || coproc)
+      if (breg == treg || coproc || lr)
        {
          tempreg = AT;
          used_at = 1;
@@ -2926,7 +3645,8 @@ macro (ip)
             With a constant we always use the latter case.  */
          if (breg == 0)
            {
-             if (offset_expr.X_add_number != 0)
+             if ((valueT) offset_expr.X_add_number >= MAX_GPREL_OFFSET
+                 || nopic_need_relax (offset_expr.X_add_symbol))
                p = NULL;
              else
                {
@@ -2949,7 +3669,8 @@ macro (ip)
            }
          else
            {
-             if (offset_expr.X_add_number != 0)
+             if ((valueT) offset_expr.X_add_number >= MAX_GPREL_OFFSET
+                 || nopic_need_relax (offset_expr.X_add_symbol))
                p = NULL;
              else
                {
@@ -2976,7 +3697,7 @@ macro (ip)
                           (int) BFD_RELOC_LO16, tempreg);
            }
        }
-      else if (mips_pic == SVR4_PIC)
+      else if (mips_pic == SVR4_PIC && ! mips_big_got)
        {
          /* If this is a reference to an external symbol, we want
               lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
@@ -3018,6 +3739,70 @@ macro (ip)
          macro_build ((char *) NULL, &icnt, &expr1, s, fmt, treg,
                       (int) BFD_RELOC_LO16, tempreg);
        }
+      else if (mips_pic == SVR4_PIC)
+       {
+         int gpdel;
+
+         /* If this is a reference to an external symbol, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
+              addu     $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+              <op>     $treg,0($tempreg)
+            Otherwise we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              nop
+              addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+              <op>     $treg,0($tempreg)
+            If there is a base register, we add it to $tempreg before
+            the <op>.  If there is a constant, we stick it in the
+            <op> instruction.  We don't handle constants larger than
+            16 bits, because we have no way to load the upper 16 bits
+            (actually, we could handle them for the subset of cases
+            in which we are not using $at).  */
+         assert (offset_expr.X_op == O_symbol);
+         expr1.X_add_number = offset_expr.X_add_number;
+         offset_expr.X_add_number = 0;
+         if (expr1.X_add_number < -0x8000
+             || expr1.X_add_number >= 0x8000)
+           as_bad ("PIC code offset overflow (max 16 signed bits)");
+         if (reg_needs_delay (GP))
+           gpdel = 4;
+         else
+           gpdel = 0;
+         frag_grow (36);
+         macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
+                      tempreg, (int) BFD_RELOC_MIPS_GOT_HI16);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                      mips_isa < 3 ? "addu" : "daddu",
+                      "d,v,t", tempreg, tempreg, GP);
+         macro_build ((char *) NULL, &icnt, &offset_expr,
+                      mips_isa < 3 ? "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, (long) 0, (char *) NULL);
+         if (gpdel > 0)
+           {
+             macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+             p += 4;
+           }
+         macro_build (p, &icnt, &offset_expr,
+                      mips_isa < 3 ? "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,
+                      mips_isa < 3 ? "addiu" : "daddiu",
+                      "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
+         if (breg != 0)
+           macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                        mips_isa < 3 ? "addu" : "daddu",
+                        "d,v,t", tempreg, tempreg, breg);
+         macro_build ((char *) NULL, &icnt, &expr1, s, fmt, treg,
+                      (int) BFD_RELOC_LO16, tempreg);
+       }
       else if (mips_pic == EMBEDDED_PIC)
        {
          /* If there is no base register, we want
@@ -3052,13 +3837,17 @@ macro (ip)
 
     case M_LI:
     case M_LI_S:
-      load_register (&icnt, treg, &imm_expr);
+      load_register (&icnt, treg, &imm_expr, 0);
+      return;
+
+    case M_DLI:
+      load_register (&icnt, treg, &imm_expr, 1);
       return;
 
     case M_LI_SS:
       if (imm_expr.X_op == O_constant)
        {
-         load_register (&icnt, AT, &imm_expr);
+         load_register (&icnt, AT, &imm_expr, 0);
          macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                       "mtc1", "t,G", AT, treg);
          break;
@@ -3160,6 +3949,12 @@ macro (ip)
            {
              macro_build ((char *) NULL, &icnt, &offset_expr, "ldc1",
                           "T,o(b)", treg, (int) BFD_RELOC_LO16, AT);
+
+             /* To avoid confusion in tc_gen_reloc, we must ensure
+                that this does not become a variant frag.  */
+             frag_wane (frag_now);
+             frag_new (0);
+
              break;
            }
          breg = AT;
@@ -3279,7 +4074,8 @@ 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 (offset_expr.X_add_number != 0)
+         if ((valueT) offset_expr.X_add_number >= MAX_GPREL_OFFSET
+             || nopic_need_relax (offset_expr.X_add_symbol))
            {
              p = NULL;
              used_at = 1;
@@ -3359,7 +4155,7 @@ macro (ip)
                       coproc ? treg : treg + 1,
                       (int) BFD_RELOC_LO16, AT);
        }         
-      else if (mips_pic == SVR4_PIC)
+      else if (mips_pic == SVR4_PIC && ! mips_big_got)
        {
          int off;
 
@@ -3414,6 +4210,106 @@ macro (ip)
                           offset_expr.X_add_symbol, (long) 0,
                           (char *) NULL);
        }
+      else if (mips_pic == SVR4_PIC)
+       {
+         int gpdel, off;
+
+         /* If this is a reference to an external symbol, we want
+              lui      $at,<sym>               (BFD_RELOC_MIPS_GOT_HI16)
+              addu     $at,$at,$gp
+              lw       $at,<sym>($at)          (BFD_RELOC_MIPS_GOT_LO16)
+              nop
+              <op>     $treg,0($at)
+              <op>     $treg+1,4($at)
+            Otherwise we want
+              lw       $at,<sym>($gp)          (BFD_RELOC_MIPS_GOT16)
+              nop
+              <op>     $treg,<sym>($at)        (BFD_RELOC_LO16)
+              <op>     $treg+1,<sym>+4($at)    (BFD_RELOC_LO16)
+            If there is a base register we add it to $at before the
+            lwc1 instructions.  If there is a constant we include it
+            in the lwc1 instructions.  */
+         used_at = 1;
+         expr1.X_add_number = offset_expr.X_add_number;
+         offset_expr.X_add_number = 0;
+         if (expr1.X_add_number < -0x8000
+             || expr1.X_add_number >= 0x8000 - 4)
+           as_bad ("PIC code offset overflow (max 16 signed bits)");
+         if (reg_needs_delay (GP))
+           gpdel = 4;
+         else
+           gpdel = 0;
+         if (breg == 0)
+           off = 0;
+         else
+           off = 4;
+         frag_grow (56);
+         macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
+                      AT, (int) BFD_RELOC_MIPS_GOT_HI16);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                      mips_isa < 3 ? "addu" : "daddu",
+                      "d,v,t", AT, AT, GP);
+         macro_build ((char *) NULL, &icnt, &offset_expr,
+                      mips_isa < 3 ? "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,
+                        mips_isa < 3 ? "addu" : "daddu",
+                        "d,v,t", AT, breg, AT);
+         macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
+                      coproc ? treg + 1 : treg,
+                      (int) BFD_RELOC_LO16, AT);
+         expr1.X_add_number += 4;
+
+         /* Set mips_optimize to 2 to avoid inserting an undesired
+             nop.  */
+         hold_mips_optimize = mips_optimize;
+         mips_optimize = 2;
+         macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
+                      coproc ? treg : treg + 1,
+                      (int) BFD_RELOC_LO16, AT);
+         mips_optimize = hold_mips_optimize;
+         expr1.X_add_number -= 4;
+
+         p = frag_var (rs_machine_dependent, 16 + gpdel + off, 0,
+                       RELAX_ENCODE (24 + off, 16 + gpdel + off, gpdel,
+                                     8 + gpdel + off, 1, 0),
+                       offset_expr.X_add_symbol, (long) 0,
+                       (char *) NULL);
+         if (gpdel > 0)
+           {
+             macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+             p += 4;
+           }
+         macro_build (p, &icnt, &offset_expr,
+                      mips_isa < 3 ? "lw" : "ld",
+                      "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16, GP);
+         p += 4;
+         macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+         p += 4;
+         if (breg != 0)
+           {
+             macro_build (p, &icnt, (expressionS *) NULL,
+                          mips_isa < 3 ? "addu" : "daddu",
+                          "d,v,t", AT, breg, AT);
+             p += 4;
+           }
+         macro_build (p, &icnt, &expr1, s, fmt,
+                      coproc ? treg + 1 : treg,
+                      (int) BFD_RELOC_LO16, AT);
+         p += 4;
+         expr1.X_add_number += 4;
+
+         /* Set mips_optimize to 2 to avoid inserting an undesired
+             nop.  */
+         hold_mips_optimize = mips_optimize;
+         mips_optimize = 2;
+         macro_build (p, &icnt, &expr1, s, fmt,
+                      coproc ? treg : treg + 1,
+                      (int) BFD_RELOC_LO16, AT);
+         mips_optimize = hold_mips_optimize;
+       }
       else if (mips_pic == EMBEDDED_PIC)
        {
          /* If there is no base register, we use
@@ -3492,6 +4388,8 @@ macro2 (ip)
   int likely = 0;
   int dbl = 0;
   int coproc = 0;
+  int lr = 0;
+  int off;
   offsetT maxnum;
   bfd_reloc_code_real_type r;
   char *p;
@@ -3525,7 +4423,7 @@ macro2 (ip)
       /* The MIPS assembler some times generates shifts and adds.  I'm
         not trying to be that fancy. GCC should do this for us
         anyway.  */
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, dbl);
       macro_build ((char *) NULL, &icnt, NULL,
                   dbl ? "dmult" : "mult",
                   "s,t", sreg, AT);
@@ -3592,9 +4490,9 @@ macro2 (ip)
 
     case M_ROL_I:
       macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", AT, sreg,
-                  imm_expr.X_add_number & 0x1f);
+                  (int) (imm_expr.X_add_number & 0x1f));
       macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", dreg, sreg,
-                  (0 - imm_expr.X_add_number) & 0x1f);
+                  (int) ((0 - imm_expr.X_add_number) & 0x1f));
       macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
       break;
 
@@ -3608,9 +4506,9 @@ macro2 (ip)
 
     case M_ROR_I:
       macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", AT, sreg,
-                  imm_expr.X_add_number & 0x1f);
+                  (int) (imm_expr.X_add_number & 0x1f));
       macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", dreg, sreg,
-                  (0 - imm_expr.X_add_number) & 0x1f);
+                  (int) ((0 - imm_expr.X_add_number) & 0x1f));
       macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
       break;
 
@@ -3674,7 +4572,7 @@ macro2 (ip)
        }
       else
        {
-         load_register (&icnt, AT, &imm_expr);
+         load_register (&icnt, AT, &imm_expr, 0);
          macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg,
                       sreg, AT);
          used_at = 1;
@@ -3707,7 +4605,7 @@ macro2 (ip)
        }
       else
        {
-         load_register (&icnt, AT, &imm_expr);
+         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);
@@ -3734,7 +4632,7 @@ macro2 (ip)
     case M_SGTU_I:
       s = "sltu";
     sgti:
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, 0);
       macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, AT, sreg);
       break;
 
@@ -3755,7 +4653,7 @@ macro2 (ip)
     case M_SLEU_I:
       s = "sltu";
     slei:
-      load_register (&icnt, AT, &imm_expr);
+      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, &expr1, "xori", "t,r,i", dreg, dreg,
                   (int) BFD_RELOC_LO16);
@@ -3768,7 +4666,7 @@ macro2 (ip)
                       dreg, sreg, (int) BFD_RELOC_LO16);
          return;
        }
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, 0);
       macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", dreg, sreg, AT);
       break;
 
@@ -3779,7 +4677,7 @@ macro2 (ip)
                       dreg, sreg, (int) BFD_RELOC_LO16);
          return;
        }
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, 0);
       macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, sreg,
                   AT);
       break;
@@ -3832,7 +4730,7 @@ macro2 (ip)
        }
       else
        {
-         load_register (&icnt, AT, &imm_expr);
+         load_register (&icnt, AT, &imm_expr, 0);
          macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg,
                       sreg, AT);
          used_at = 1;
@@ -3853,7 +4751,7 @@ macro2 (ip)
                       "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16);
          return;
        }
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, dbl);
       macro_build ((char *) NULL, &icnt, NULL,
                   dbl ? "dsub" : "sub",
                   "d,v,t", dreg, sreg, AT);
@@ -3870,7 +4768,7 @@ macro2 (ip)
                       "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16);
          return;
        }
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, dbl);
       macro_build ((char *) NULL, &icnt, NULL,
                   dbl ? "dsubu" : "subu",
                   "d,v,t", dreg, sreg, AT);
@@ -3894,7 +4792,7 @@ macro2 (ip)
     case M_TNE_I:
       s = "tne";
     trap:
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, 0);
       macro_build ((char *) NULL, &icnt, NULL, s, "s,t", sreg, AT);
       break;
 
@@ -3952,58 +4850,81 @@ macro2 (ip)
       macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", treg, treg, AT);
       break;
 
+    case M_ULD:
+      s = "ldl";
+      s2 = "ldr";
+      off = 7;
+      goto ulw;
     case M_ULW:
-      if (offset_expr.X_add_number >= 0x7ffd)
+      s = "lwl";
+      s2 = "lwr";
+      off = 3;
+    ulw:
+      if (offset_expr.X_add_number >= 0x8000 - off)
        as_bad ("operand overflow");
       if (byte_order == LITTLE_ENDIAN)
-       offset_expr.X_add_number += 3;
-      macro_build ((char *) NULL, &icnt, &offset_expr, "lwl", "t,o(b)", treg,
+       offset_expr.X_add_number += off;
+      macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
       if (byte_order == LITTLE_ENDIAN)
-       offset_expr.X_add_number -= 3;
+       offset_expr.X_add_number -= off;
       else
-       offset_expr.X_add_number += 3;
-      macro_build ((char *) NULL, &icnt, &offset_expr, "lwr", "t,o(b)", treg,
+       offset_expr.X_add_number += off;
+      macro_build ((char *) NULL, &icnt, &offset_expr, s2, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
       return;
 
+    case M_ULD_A:
+      s = "ldl";
+      s2 = "ldr";
+      off = 7;
+      goto ulwa;
+    case M_ULW_A:
+      s = "lwl";
+      s2 = "lwr";
+      off = 3;
+    ulwa:
+      load_address (&icnt, AT, &offset_expr);
+      if (breg != 0)
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                    mips_isa < 3 ? "addu" : "daddu",
+                    "d,v,t", AT, AT, breg);
+      if (byte_order == LITTLE_ENDIAN)
+       expr1.X_add_number = off;
+      else
+       expr1.X_add_number = 0;
+      macro_build ((char *) NULL, &icnt, &expr1, s, "t,o(b)", treg,
+                  (int) BFD_RELOC_LO16, AT);
+      if (byte_order == LITTLE_ENDIAN)
+       expr1.X_add_number = 0;
+      else
+       expr1.X_add_number = off;
+      macro_build ((char *) NULL, &icnt, &expr1, s2, "t,o(b)", treg,
+                  (int) BFD_RELOC_LO16, AT);
+      break;
+
     case M_ULH_A:
     case M_ULHU_A:
-    case M_ULW_A:
       load_address (&icnt, AT, &offset_expr);
-      if (mask == M_ULW_A)
-       {
-         if (byte_order == LITTLE_ENDIAN)
-           expr1.X_add_number = 3;
-         else
-           expr1.X_add_number = 0;
-         macro_build ((char *) NULL, &icnt, &expr1, "lwl", "t,o(b)", treg,
-                      (int) BFD_RELOC_LO16, AT);
-         if (byte_order == LITTLE_ENDIAN)
-           expr1.X_add_number = 0;
-         else
-           expr1.X_add_number = 3;
-         macro_build ((char *) NULL, &icnt, &expr1, "lwr", "t,o(b)", treg,
-                      (int) BFD_RELOC_LO16, AT);
-       }
+      if (breg != 0)
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                    mips_isa < 3 ? "addu" : "daddu",
+                    "d,v,t", AT, AT, breg);
+      if (byte_order == BIG_ENDIAN)
+       expr1.X_add_number = 0;
+      macro_build ((char *) NULL, &icnt, &expr1,
+                  mask == M_ULH_A ? "lb" : "lbu", "t,o(b)", treg,
+                  (int) BFD_RELOC_LO16, AT);
+      if (byte_order == BIG_ENDIAN)
+       expr1.X_add_number = 1;
       else
-       {
-         if (byte_order == BIG_ENDIAN)
-           expr1.X_add_number = 0;
-         macro_build ((char *) NULL, &icnt, &expr1,
-                      mask == M_ULH_A ? "lb" : "lbu", "t,o(b)", treg,
-                      (int) BFD_RELOC_LO16, AT);
-         if (byte_order == BIG_ENDIAN)
-           expr1.X_add_number = 1;
-         else
-           expr1.X_add_number = 0;
-         macro_build ((char *) NULL, &icnt, &expr1, "lbu", "t,o(b)", AT,
-                      (int) BFD_RELOC_LO16, AT);
-         macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", treg,
-                      treg, 8);
-         macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", treg,
-                      treg, AT);
-       }
+       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);
       break;
 
     case M_USH:
@@ -4022,64 +4943,87 @@ macro2 (ip)
                   (int) BFD_RELOC_LO16, breg);
       break;
 
+    case M_USD:
+      s = "sdl";
+      s2 = "sdr";
+      off = 7;
+      goto usw;
     case M_USW:
-      if (offset_expr.X_add_number >= 0x7ffd)
+      s = "swl";
+      s2 = "swr";
+      off = 3;
+    usw:
+      if (offset_expr.X_add_number >= 0x8000 - off)
        as_bad ("operand overflow");
       if (byte_order == LITTLE_ENDIAN)
-       offset_expr.X_add_number += 3;
-      macro_build ((char *) NULL, &icnt, &offset_expr, "swl", "t,o(b)", treg,
+       offset_expr.X_add_number += off;
+      macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
       if (byte_order == LITTLE_ENDIAN)
-       offset_expr.X_add_number -= 3;
+       offset_expr.X_add_number -= off;
       else
-       offset_expr.X_add_number += 3;
-      macro_build ((char *) NULL, &icnt, &offset_expr, "swr", "t,o(b)", treg,
+       offset_expr.X_add_number += off;
+      macro_build ((char *) NULL, &icnt, &offset_expr, s2, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
       return;
 
-    case M_USH_A:
+    case M_USD_A:
+      s = "sdl";
+      s2 = "sdr";
+      off = 7;
+      goto uswa;
     case M_USW_A:
+      s = "swl";
+      s2 = "swr";
+      off = 3;
+    uswa:
       load_address (&icnt, AT, &offset_expr);
-      if (mask == M_USW_A)
-       {
-         if (byte_order == LITTLE_ENDIAN)
-           expr1.X_add_number = 3;
-         else
-           expr1.X_add_number = 0;
-         macro_build ((char *) NULL, &icnt, &expr1, "swl", "t,o(b)", treg,
-                      (int) BFD_RELOC_LO16, AT);
-         if (byte_order == LITTLE_ENDIAN)
-           expr1.X_add_number = 0;
-         else
-           expr1.X_add_number = 3;
-         macro_build ((char *) NULL, &icnt, &expr1, "swr", "t,o(b)", treg,
-                      (int) BFD_RELOC_LO16, AT);
-       }
+      if (breg != 0)
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                    mips_isa < 3 ? "addu" : "daddu",
+                    "d,v,t", AT, AT, breg);
+      if (byte_order == LITTLE_ENDIAN)
+       expr1.X_add_number = off;
       else
-       {
-         if (byte_order == LITTLE_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);
-         if (byte_order == LITTLE_ENDIAN)
-           expr1.X_add_number = 1;
-         else
-           expr1.X_add_number = 0;
-         macro_build ((char *) NULL, &icnt, &expr1, "sb", "t,o(b)", treg,
-                      (int) BFD_RELOC_LO16, AT);
-         if (byte_order == LITTLE_ENDIAN)
-           expr1.X_add_number = 0;
-         else
-           expr1.X_add_number = 1;
-         macro_build ((char *) NULL, &icnt, &expr1, "lbu", "t,o(b)", AT,
-                      (int) BFD_RELOC_LO16, AT);
-         macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", treg,
-                      treg, 8);
-         macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", treg,
-                      treg, AT);
-       }
+       expr1.X_add_number = 0;
+      macro_build ((char *) NULL, &icnt, &expr1, s, "t,o(b)", treg,
+                  (int) BFD_RELOC_LO16, AT);
+      if (byte_order == LITTLE_ENDIAN)
+       expr1.X_add_number = 0;
+      else
+       expr1.X_add_number = off;
+      macro_build ((char *) NULL, &icnt, &expr1, s2, "t,o(b)", treg,
+                  (int) BFD_RELOC_LO16, AT);
+      break;
+
+    case M_USH_A:
+      load_address (&icnt, AT, &offset_expr);
+      if (breg != 0)
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                    mips_isa < 3 ? "addu" : "daddu",
+                    "d,v,t", AT, AT, breg);
+      if (byte_order == LITTLE_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);
+      if (byte_order == LITTLE_ENDIAN)
+       expr1.X_add_number = 1;
+      else
+       expr1.X_add_number = 0;
+      macro_build ((char *) NULL, &icnt, &expr1, "sb", "t,o(b)", treg,
+                  (int) BFD_RELOC_LO16, AT);
+      if (byte_order == LITTLE_ENDIAN)
+       expr1.X_add_number = 0;
+      else
+       expr1.X_add_number = 1;
+      macro_build ((char *) NULL, &icnt, &expr1, "lbu", "t,o(b)", AT,
+                  (int) BFD_RELOC_LO16, AT);
+      macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", treg,
+                  treg, 8);
+      macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", treg,
+                  treg, AT);
       break;
 
     default:
@@ -4090,12 +5034,11 @@ macro2 (ip)
     as_warn ("Macro used $at after \".set noat\"");
 }
 
+/* This routine assembles an instruction into its binary format.  As a
+   side effect, it sets one of the global variables imm_reloc or
+   offset_reloc to the type of relocation to do if one of the operands
+   is an address expression.  */
 
-/*
-This routine assembles an instruction into its binary format.  As a side
-effect it sets one of the global variables imm_reloc or offset_reloc to the
-type of relocation to do if one of the operands is an address expression.
-*/
 static void
 mips_ip (str, ip)
      char *str;
@@ -4112,7 +5055,7 @@ mips_ip (str, ip)
 
   insn_error = NULL;
 
-  for (s = str; islower (*s) || (*s >= '0' && *s <= '3') || *s == '.'; ++s)
+  for (s = str; islower (*s) || (*s >= '0' && *s <= '3') || *s == '6' || *s == '.'; ++s)
     continue;
   switch (*s)
     {
@@ -4128,8 +5071,7 @@ mips_ip (str, ip)
     }
   if ((insn = (struct mips_opcode *) hash_find (op_hash, str)) == NULL)
     {
-      as_warn ("`%s' not in hash table.", str);
-      insn_error = "ERROR: Unrecognized opcode";
+      insn_error = "unrecognized opcode";
       return;
     }
   argsStart = s;
@@ -4141,14 +5083,22 @@ mips_ip (str, ip)
 
       if (insn->pinfo == INSN_MACRO)
        insn_isa = insn->match;
-      else if (insn->pinfo & INSN_ISA2)
+      else if ((insn->pinfo & INSN_ISA) == INSN_ISA2)
        insn_isa = 2;
-      else if (insn->pinfo & INSN_ISA3)
+      else if ((insn->pinfo & INSN_ISA) == INSN_ISA3)
        insn_isa = 3;
+      else if ((insn->pinfo & INSN_ISA) == INSN_ISA4)
+       insn_isa = 4;
       else
        insn_isa = 1;
 
-      if (insn_isa > mips_isa)
+      if (insn_isa > mips_isa
+         || ((insn->pinfo & INSN_ISA) == INSN_4650
+             && ! mips_4650)
+         || ((insn->pinfo & INSN_ISA) == INSN_4010
+             && ! mips_4010)
+         || ((insn->pinfo & INSN_ISA) == INSN_4100
+             && ! mips_4100))
        {
          if (insn + 1 < &mips_opcodes[NUMOPCODES]
              && strcmp (insn->name, insn[1].name) == 0)
@@ -4156,7 +5106,8 @@ mips_ip (str, ip)
              ++insn;
              continue;
            }
-         as_warn ("Instruction not supported on this processor");
+         insn_error = "opcode not supported on this processor";
+         return;
        }
 
       ip->insn_mo = insn;
@@ -4241,15 +5192,20 @@ mips_ip (str, ip)
              continue;
 
            case 'k':           /* cache code */
+           case 'h':           /* prefx code */
              my_getExpression (&imm_expr, s);
              check_absolute_expr (ip, &imm_expr);
              if ((unsigned long) imm_expr.X_add_number > 31)
                {
-                 as_warn ("Invalid cahce opcode (%lu)",
+                 as_warn ("Invalid value for `%s' (%lu)",
+                          ip->insn_mo->name,
                           (unsigned long) imm_expr.X_add_number);
                  imm_expr.X_add_number &= 0x1f;
                }
-             ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CACHE;
+             if (*args == 'k')
+               ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CACHE;
+             else
+               ip->insn_opcode |= imm_expr.X_add_number << OP_SH_PREFX;
              imm_expr.X_op = O_absent;
              s = expr_end;
              continue;
@@ -4342,10 +5298,23 @@ mips_ip (str, ip)
                          s += 3;
                          regno = AT;
                        }
+                     else if (s[1] == 'k' && s[2] == 't' && s[3] == '0')
+                       {
+                         s += 4;
+                         regno = KT0;
+                       }
+                     else if (s[1] == 'k' && s[2] == 't' && s[3] == '1')
+                       {
+                         s += 4;
+                         regno = KT1;
+                       }
                      else
                        goto notreg;
                    }
-                 if (regno == AT && ! mips_noat)
+                 if (regno == AT
+                     && ! mips_noat
+                     && *args != 'E'
+                     && *args != 'G')
                    as_warn ("Used $at without \".set noat\"");
                  c = *args;
                  if (*s == ' ')
@@ -4414,6 +5383,7 @@ mips_ip (str, ip)
            case 'D':           /* floating point destination register */
            case 'S':           /* floating point source register */
            case 'T':           /* floating point target register */
+           case 'R':           /* floating point source register */
            case 'V':
            case 'W':
              s_reset = s;
@@ -4465,6 +5435,10 @@ mips_ip (str, ip)
                    case 'W':
                    case 'T':
                      ip->insn_opcode |= regno << 16;
+                     break;
+                   case 'R':
+                     ip->insn_opcode |= regno << 21;
+                     break;
                    }
                  lastregno = regno;
                  continue;
@@ -4482,7 +5456,9 @@ mips_ip (str, ip)
 
            case 'I':
              my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
+             if (imm_expr.X_op != O_big
+                 && imm_expr.X_op != O_constant)
+               insn_error = "absolute expression required";
              s = expr_end;
              continue;
 
@@ -4552,11 +5528,10 @@ mips_ip (str, ip)
 
                if (*args == 'f'
                    || (*args == 'l'
-                       && (mips_pic == EMBEDDED_PIC
-#ifdef GPOPT
-                           || g_switch_value < 4
-#endif
-                           )))
+                       && (! USE_GLOBAL_POINTER_OPT
+                           || mips_pic == EMBEDDED_PIC
+                           || g_switch_value < 4)
+                       ))
                  {
                    imm_expr.X_op = O_constant;
                    if (byte_order == LITTLE_ENDIAN)
@@ -4584,29 +5559,31 @@ mips_ip (str, ip)
                      {
                      default: /* unused default case avoids warnings.  */
                      case 'L':
-                       newname = ".lit8";
-#ifdef GPOPT
-                       if (g_switch_value < 8)
-                         newname = RDATA_SECTION_NAME;
-#endif
+                       newname = RDATA_SECTION_NAME;
+                       if (USE_GLOBAL_POINTER_OPT && g_switch_value >= 8)
+                         newname = ".lit8";
                        break;
                      case 'F':
                        newname = RDATA_SECTION_NAME;
                        break;
                      case 'l':
-#ifdef GPOPT
-                       assert (g_switch_value >= 4);
-#endif
+                       assert (!USE_GLOBAL_POINTER_OPT
+                               || g_switch_value >= 4);
                        newname = ".lit4";
                        break;
                      }
                    new_seg = subseg_new (newname, (subsegT) 0);
+                   if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+                     bfd_set_section_flags (stdoutput, new_seg,
+                                            (SEC_ALLOC
+                                             | SEC_LOAD
+                                             | SEC_READONLY
+                                             | SEC_DATA));
                    frag_align (*args == 'l' ? 2 : 3, 0);
-#ifdef OBJ_ELF
-                   record_alignment (new_seg, 4);
-#else
-                   record_alignment (new_seg, *args == 'l' ? 2 : 3);
-#endif
+                   if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+                     record_alignment (new_seg, 4);
+                   else
+                     record_alignment (new_seg, *args == 'l' ? 2 : 3);
                    if (seg == now_seg)
                      as_bad ("Can't use floating point insn in this section");
 
@@ -4632,7 +5609,7 @@ mips_ip (str, ip)
            case 'j':           /* 16 bit signed immediate */
              imm_reloc = BFD_RELOC_LO16;
              c = my_getSmallExpression (&imm_expr, s);
-             if (c)
+             if (c != '\0')
                {
                  if (c != 'l')
                    {
@@ -4640,22 +5617,29 @@ mips_ip (str, ip)
                        imm_expr.X_add_number =
                          (imm_expr.X_add_number >> 16) & 0xffff;
                      else if (c == 'h')
-                       imm_reloc = BFD_RELOC_HI16_S;
+                       {
+                         imm_reloc = BFD_RELOC_HI16_S;
+                         imm_unmatched_hi = true;
+                       }
                      else
                        imm_reloc = BFD_RELOC_HI16;
                    }
                }
-             else
-               check_absolute_expr (ip, &imm_expr);
              if (*args == 'i')
                {
-                 if (imm_expr.X_add_number < 0
-                     || imm_expr.X_add_number >= 0x10000)
+                 if ((c == '\0' && imm_expr.X_op != O_constant)
+                     || ((imm_expr.X_add_number < 0
+                           || imm_expr.X_add_number >= 0x10000)
+                          && imm_expr.X_op == O_constant))
                    {
                      if (insn + 1 < &mips_opcodes[NUMOPCODES] &&
                          !strcmp (insn->name, insn[1].name))
                        break;
-                     as_bad ("16 bit expression not in range 0..65535");
+                     if (imm_expr.X_op != O_constant
+                         && imm_expr.X_op != O_big)
+                       insn_error = "absolute expression required";
+                     else
+                       as_bad ("16 bit expression not in range 0..65535");
                    }
                }
              else
@@ -4678,12 +5662,23 @@ mips_ip (str, ip)
                    max = 0x8000;
                  else
                    max = 0x10000;
-                 if (imm_expr.X_add_number < -0x8000 ||
-                     imm_expr.X_add_number >= max)
+                 if ((c == '\0' && imm_expr.X_op != O_constant)
+                     || ((imm_expr.X_add_number < -0x8000
+                           || imm_expr.X_add_number >= max)
+                          && imm_expr.X_op == O_constant)
+                     || (more
+                         && imm_expr.X_add_number < 0
+                         && mips_isa >= 3
+                         && imm_expr.X_unsigned
+                         && sizeof (imm_expr.X_add_number) <= 4))
                    {
                      if (more)
                        break;
-                     as_bad ("16 bit expression not in range -32768..32767");
+                     if (imm_expr.X_op != O_constant
+                         && imm_expr.X_op != O_big)
+                       insn_error = "absolute expression required";
+                     else
+                       as_bad ("16 bit expression not in range -32768..32767");
                    }
                }
              s = expr_end;
@@ -4745,7 +5740,10 @@ mips_ip (str, ip)
                        imm_expr.X_add_number =
                          (imm_expr.X_add_number >> 16) & 0xffff;
                      else if (c == 'h')
-                       imm_reloc = BFD_RELOC_HI16_S;
+                       {
+                         imm_reloc = BFD_RELOC_HI16_S;
+                         imm_unmatched_hi = true;
+                       }
                      else
                        imm_reloc = BFD_RELOC_HI16;
                    }
@@ -4759,6 +5757,24 @@ mips_ip (str, ip)
              offset_reloc = BFD_RELOC_MIPS_JMP;
              continue;
 
+           case 'N':           /* 3 bit branch condition code */
+           case 'M':           /* 3 bit compare condition code */
+             my_getExpression (&imm_expr, s);
+             check_absolute_expr (ip, &imm_expr);
+              if ((unsigned long) imm_expr.X_add_number > 7)
+               {
+                  as_warn ("Condition code > 7 (%ld)",
+                          (long) imm_expr.X_add_number);
+                  imm_expr.X_add_number &= 7;
+               }
+             if (*args == 'N')
+               ip->insn_opcode |= imm_expr.X_add_number << OP_SH_BCC;
+             else
+               ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CCC;
+              imm_expr.X_op = O_absent;
+              s = expr_end;
+              continue;
+
            default:
              fprintf (stderr, "bad char = '%c'\n", *args);
              internalError ();
@@ -4773,7 +5789,7 @@ mips_ip (str, ip)
          s = argsStart;
          continue;
        }
-      insn_error = "ERROR: Illegal operands";
+      insn_error = "illegal operands";
       return;
     }
 }
@@ -4960,11 +5976,8 @@ md_number_to_chars (buf, val, n)
     }
 }
 \f
-#ifdef GPOPT
 CONST char *md_shortopts = "O::g::G:";
-#else
-CONST char *md_shortopts = "O::g::";
-#endif
+
 struct option md_longopts[] = {
 #define OPTION_MIPS1 (OPTION_MD_BASE + 1)
   {"mips0", no_argument, NULL, OPTION_MIPS1},
@@ -4973,27 +5986,47 @@ struct option md_longopts[] = {
   {"mips2", no_argument, NULL, OPTION_MIPS2},
 #define OPTION_MIPS3 (OPTION_MD_BASE + 3)
   {"mips3", no_argument, NULL, OPTION_MIPS3},
-#define OPTION_MCPU (OPTION_MD_BASE + 4)
+#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 + 5)
+#define OPTION_MEMBEDDED_PIC (OPTION_MD_BASE + 6)
   {"membedded-pic", no_argument, NULL, OPTION_MEMBEDDED_PIC},
-#define OPTION_TRAP (OPTION_MD_BASE + 8)
+#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 + 9)
+#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 + 10)
+#define OPTION_EB (OPTION_MD_BASE + 11)
   {"EB", no_argument, NULL, OPTION_EB},
-#define OPTION_EL (OPTION_MD_BASE + 11)
+#define OPTION_EL (OPTION_MD_BASE + 12)
   {"EL", no_argument, NULL, OPTION_EL},
-
+#define OPTION_M4650 (OPTION_MD_BASE + 13)
+  {"m4650", no_argument, NULL, OPTION_M4650},
+#define OPTION_NO_M4650 (OPTION_MD_BASE + 14)
+  {"no-m4650", no_argument, NULL, OPTION_NO_M4650},
+#define OPTION_M4010 (OPTION_MD_BASE + 15)
+  {"m4010", no_argument, NULL, OPTION_M4010},
+#define OPTION_NO_M4010 (OPTION_MD_BASE + 16)
+  {"no-m4010", no_argument, NULL, OPTION_NO_M4010},
+#define OPTION_M4100 (OPTION_MD_BASE + 17)
+  {"m4100", no_argument, NULL, OPTION_M4100},
+#define OPTION_NO_M4100 (OPTION_MD_BASE + 18)
+  {"no-m4100", no_argument, NULL, OPTION_NO_M4100},
+
+#define OPTION_CALL_SHARED (OPTION_MD_BASE + 7)
+#define OPTION_NON_SHARED (OPTION_MD_BASE + 8)
+#define OPTION_XGOT (OPTION_MD_BASE + 19)
+#define OPTION_32 (OPTION_MD_BASE + 20)
+#define OPTION_64 (OPTION_MD_BASE + 21)
 #ifdef OBJ_ELF
-#define OPTION_CALL_SHARED (OPTION_MD_BASE + 6)
   {"KPIC", no_argument, NULL, OPTION_CALL_SHARED},
+  {"xgot", no_argument, NULL, OPTION_XGOT},
   {"call_shared", no_argument, NULL, OPTION_CALL_SHARED},
-#define OPTION_NON_SHARED (OPTION_MD_BASE + 7)
   {"non_shared", no_argument, NULL, OPTION_NON_SHARED},
+  {"32", no_argument, NULL, OPTION_32},
+  {"64", no_argument, NULL, OPTION_64},
 #endif
 
   {NULL, no_argument, NULL, 0}
@@ -5016,29 +6049,11 @@ md_parse_option (c, arg)
       break;
 
     case OPTION_EB:
-      byte_order = BIG_ENDIAN;
-#ifdef OBJ_AOUT
-      mips_target_format = "a.out-mips-big";
-#endif
-#ifdef OBJ_ECOFF
-      mips_target_format = "ecoff-bigmips";
-#endif
-#ifdef OBJ_ELF
-      mips_target_format = "elf32-bigmips";
-#endif
+      target_big_endian = 1;
       break;
 
     case OPTION_EL:
-      byte_order = LITTLE_ENDIAN;
-#ifdef OBJ_AOUT
-      mips_target_format = "a.out-mips-little";
-#endif
-#ifdef OBJ_ECOFF
-      mips_target_format = "ecoff-littlemips";
-#endif
-#ifdef OBJ_ELF
-      mips_target_format = "elf32-littlemips";
-#endif
+      target_big_endian = 0;
       break;
 
     case 'O':
@@ -5049,23 +6064,39 @@ md_parse_option (c, arg)
       break;
 
     case 'g':
-      if (arg == NULL || arg[1] == '2')
+      if (arg == NULL)
+       mips_debug = 2;
+      else
+       mips_debug = atoi (arg);
+      /* When the MIPS assembler sees -g or -g2, it does not do
+         optimizations which limit full symbolic debugging.  We take
+         that to be equivalent to -O0.  */
+      if (mips_debug == 2)
        mips_optimize = 0;
       break;
 
     case OPTION_MIPS1:
       mips_isa = 1;
-      mips_cpu = 3000;
+      if (mips_cpu == -1)
+       mips_cpu = 3000;
       break;
 
     case OPTION_MIPS2:
       mips_isa = 2;
-      mips_cpu = 6000;
+      if (mips_cpu == -1)
+       mips_cpu = 6000;
       break;
 
     case OPTION_MIPS3:
       mips_isa = 3;
-      mips_cpu = 4000;
+      if (mips_cpu == -1)
+       mips_cpu = 4000;
+      break;
+
+    case OPTION_MIPS4:
+      mips_isa = 4;
+      if (mips_cpu == -1)
+       mips_cpu = 8000;
       break;
 
     case OPTION_MCPU:
@@ -5076,52 +6107,74 @@ md_parse_option (c, arg)
        p = arg;
        if (strcmp (p, "default") == 0
            || strcmp (p, "DEFAULT") == 0)
-         mips_isa = -1;
+         mips_cpu = -1;
        else
          {
+           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++;
 
-           mips_isa = -1;
+           mips_cpu = -1;
            switch (*p)
              {
+             case '1':
+               if (strcmp (p, "10000") == 0
+                   || strcmp (p, "10k") == 0
+                   || strcmp (p, "10K") == 0)
+                 mips_cpu = 10000;
+               break;
+
              case '2':
                if (strcmp (p, "2000") == 0
                    || strcmp (p, "2k") == 0
                    || strcmp (p, "2K") == 0)
-                 {
-                   mips_isa = 1;
-                   mips_cpu = 2000;
-                 }
+                 mips_cpu = 2000;
                break;
 
              case '3':
                if (strcmp (p, "3000") == 0
                    || strcmp (p, "3k") == 0
                    || strcmp (p, "3K") == 0)
-                 {
-                   mips_isa = 1;
-                   mips_cpu = 3000;
-                 }
+                 mips_cpu = 3000;
                break;
 
              case '4':
                if (strcmp (p, "4000") == 0
                    || strcmp (p, "4k") == 0
                    || strcmp (p, "4K") == 0)
-                 {
-                   mips_isa = 3;
-                   mips_cpu = 4000;
-                 }
+                 mips_cpu = 4000;
+               else if (strcmp (p, "4100") == 0)
+                  {
+                    mips_cpu = 4100;
+                    if (mips_4100 < 0)
+                      mips_4100 = 1;
+                  }
+               else if (strcmp (p, "4300") == 0)
+                 mips_cpu = 4300;
                else if (strcmp (p, "4400") == 0)
+                 mips_cpu = 4400;
+               else if (strcmp (p, "4600") == 0)
+                 mips_cpu = 4600;
+               else if (strcmp (p, "4650") == 0)
                  {
-                   mips_isa = 3;
-                   mips_cpu = 4400;
+                   mips_cpu = 4650;
+                   if (mips_4650 < 0)
+                     mips_4650 = 1;
                  }
-               else if (strcmp (p, "4600") == 0)
+               else if (strcmp (p, "4010") == 0)
                  {
-                   mips_isa = 3;
-                   mips_cpu = 4600;
+                   mips_cpu = 4010;
+                   if (mips_4010 < 0)
+                     mips_4010 = 1;
                  }
                break;
 
@@ -5129,22 +6182,29 @@ md_parse_option (c, arg)
                if (strcmp (p, "6000") == 0
                    || strcmp (p, "6k") == 0
                    || strcmp (p, "6K") == 0)
-                 {
-                   mips_isa = 2;
-                   mips_cpu = 6000;
-                 }
+                 mips_cpu = 6000;
+               break;
+
+             case '8':
+               if (strcmp (p, "8000") == 0
+                   || strcmp (p, "8k") == 0
+                   || strcmp (p, "8K") == 0)
+                 mips_cpu = 8000;
                break;
 
              case 'o':
                if (strcmp (p, "orion") == 0)
-                 {
-                   mips_isa = 3;
-                   mips_cpu = 4600;
-                 }
+                 mips_cpu = 4600;
                break;
              }
 
-           if (mips_isa == -1)
+           if (sv && mips_cpu != 4300 && mips_cpu != 4100)
+             {
+               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;
@@ -5153,23 +6213,49 @@ md_parse_option (c, arg)
       }
       break;
 
+    case OPTION_M4650:
+      mips_4650 = 1;
+      break;
+
+    case OPTION_NO_M4650:
+      mips_4650 = 0;
+      break;
+
+    case OPTION_M4010:
+      mips_4010 = 1;
+      break;
+
+    case OPTION_NO_M4010:
+      mips_4010 = 0;
+      break;
+
+    case OPTION_M4100:
+      mips_4100 = 1;
+      break;
+
+    case OPTION_NO_M4100:
+      mips_4100 = 0;
+      break;
+
     case OPTION_MEMBEDDED_PIC:
       mips_pic = EMBEDDED_PIC;
-#ifdef GPOPT
-      if (g_switch_seen)
+      if (USE_GLOBAL_POINTER_OPT && g_switch_seen)
        {
          as_bad ("-G may not be used with embedded PIC code");
          return 0;
        }
       g_switch_value = 0x7fffffff;
-#endif
       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.  */
+      /* 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.  */
     case OPTION_CALL_SHARED:
+      if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+       {
+         as_bad ("-call_shared is supported only for ELF format");
+         return 0;
+       }
       mips_pic = SVR4_PIC;
       if (g_switch_seen && g_switch_value != 0)
        {
@@ -5180,13 +6266,28 @@ md_parse_option (c, arg)
       break;
 
     case OPTION_NON_SHARED:
+      if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+       {
+         as_bad ("-non_shared is supported only for ELF format");
+         return 0;
+       }
       mips_pic = NO_PIC;
       break;
-#endif /* OBJ_ELF */
 
-#ifdef GPOPT
+      /* The -xgot option tells the assembler to use 32 offsets when
+         accessing the got in SVR4_PIC mode.  It is for Irix
+         compatibility.  */
+    case OPTION_XGOT:
+      mips_big_got = 1;
+      break;
+
     case 'G':
-      if (mips_pic == SVR4_PIC || mips_pic == EMBEDDED_PIC)
+      if (! USE_GLOBAL_POINTER_OPT)
+       {
+         as_bad ("-G is not supported for this configuration");
+         return 0;
+       }
+      else if (mips_pic == SVR4_PIC || mips_pic == EMBEDDED_PIC)
        {
          as_bad ("-G may not be used with SVR4 or embedded PIC code");
          return 0;
@@ -5195,7 +6296,28 @@ md_parse_option (c, arg)
        g_switch_value = atoi (arg);
       g_switch_seen = 1;
       break;
-#endif
+
+      /* 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;
+      break;
+
+    case OPTION_64:
+      {
+       const char **list, **l;
+
+       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;
+      }
+      break;
 
     default:
       return 0;
@@ -5220,6 +6342,16 @@ MIPS options:\n\
 -mips1, -mcpu=r{2,3}000        generate code for r2000 and r3000\n\
 -mips2, -mcpu=r6000    generate code for r6000\n\
 -mips3, -mcpu=r4000    generate code for r4000\n\
+-mips4, -mcpu=r8000    generate code for r8000\n\
+-mcpu=vr4300           generate code for vr4300\n\
+-mcpu=vr4100           generate code for vr4100\n\
+-m4650                 permit R4650 instructions\n\
+-no-m4650              do not permit R4650 instructions\n\
+-m4010                 permit R4010 instructions\n\
+-no-m4010              do not permit R4010 instructions\n\
+-m4100                  permit VR4100 instructions\n\
+-no-m4100              do not permit VR4100 instructions\n");
+  fprintf(stream, "\
 -O0                    remove unneeded NOPs, do not swap branches\n\
 -O                     remove unneeded NOPs and swap branches\n\
 --trap, --no-break     trap exception on div by 0 and mult overflow\n\
@@ -5227,23 +6359,34 @@ MIPS options:\n\
 #ifdef OBJ_ELF
   fprintf(stream, "\
 -KPIC, -call_shared    generate SVR4 position independent code\n\
--non_shared            do not generate 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");
 #endif
 }
+
+void
+mips_init_after_args ()
+{
+  if (target_big_endian)
+    byte_order = BIG_ENDIAN;
+  else
+    byte_order = LITTLE_ENDIAN;
+}
 \f
 long
 md_pcrel_from (fixP)
      fixS *fixP;
 {
-#ifndef OBJ_AOUT
-  if (fixP->fx_addsy != (symbolS *) NULL
+  if (OUTPUT_FLAVOR != bfd_target_aout_flavour
+      && fixP->fx_addsy != (symbolS *) NULL
       && ! S_IS_DEFINED (fixP->fx_addsy))
     {
       /* This makes a branch to an undefined symbol be a branch to the
         current location.  */
       return 4;
     }
-#endif
 
   /* return the address of the delay slot */
   return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
@@ -5261,21 +6404,101 @@ cons_fix_new_mips (frag, where, nbytes, exp)
      expressionS *exp;
 {
   /* If we are assembling in 32 bit mode, turn an 8 byte reloc into a
-     4 byte reloc.  
-     FIXME: There is no way to select anything but 32 bit mode right
-     now.  */
-  if (nbytes == 8)
+     4 byte reloc.  */
+  if (nbytes == 8 && ! mips_64)
     {
       if (byte_order == BIG_ENDIAN)
        where += 4;
       nbytes = 4;
     }
 
-  if (nbytes != 2 && nbytes != 4)
+  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 : BFD_RELOC_32);
+              (nbytes == 2
+               ? BFD_RELOC_16
+               : (nbytes == 4 ? BFD_RELOC_32 : BFD_RELOC_64)));
+}
+
+/* Sort any unmatched HI16_S relocs so that they immediately precede
+   the corresponding LO reloc.  This is called before md_apply_fix and
+   tc_gen_reloc.  Unmatched HI16_S relocs can only be generated by
+   explicit use of the %hi modifier.  */
+
+void
+mips_frob_file ()
+{
+  struct mips_hi_fixup *l;
+
+  for (l = mips_hi_fixup_list; l != NULL; l = l->next)
+    {
+      segment_info_type *seginfo;
+      int pass;
+
+      assert (l->fixp->fx_r_type == BFD_RELOC_HI16_S);
+
+      /* Check quickly whether the next fixup happens to be a matching
+         %lo.  */
+      if (l->fixp->fx_next != NULL
+         && l->fixp->fx_next->fx_r_type == BFD_RELOC_LO16
+         && l->fixp->fx_addsy == l->fixp->fx_next->fx_addsy
+         && l->fixp->fx_offset == l->fixp->fx_next->fx_offset)
+       continue;
+
+      /* Look through the fixups for this segment for a matching %lo.
+         When we find one, move the %hi just in front of it.  We do
+         this in two passes.  In the first pass, we try to find a
+         unique %lo.  In the second pass, we permit multiple %hi
+         relocs for a single %lo (this is a GNU extension).  */
+      seginfo = seg_info (l->seg);
+      for (pass = 0; pass < 2; pass++)
+       {
+         fixS *f, *prev;
+
+         prev = NULL;
+         for (f = seginfo->fix_root; f != NULL; f = f->fx_next)
+           {
+             /* Check whether this is a %lo fixup which matches l->fixp.  */
+             if (f->fx_r_type == BFD_RELOC_LO16
+                 && f->fx_addsy == l->fixp->fx_addsy
+                 && f->fx_offset == l->fixp->fx_offset
+                 && (pass == 1
+                     || prev == NULL
+                     || prev->fx_r_type != BFD_RELOC_HI16_S
+                     || prev->fx_addsy != f->fx_addsy
+                     || prev->fx_offset !=  f->fx_offset))
+               {
+                 fixS **pf;
+
+                 /* Move l->fixp before f.  */
+                 for (pf = &seginfo->fix_root;
+                      *pf != l->fixp;
+                      pf = &(*pf)->fx_next)
+                   assert (*pf != NULL);
+
+                 *pf = l->fixp->fx_next;
+
+                 l->fixp->fx_next = f;
+                 if (prev == NULL)
+                   seginfo->fix_root = l->fixp;
+                 else
+                   prev->fx_next = l->fixp;
+
+                 break;
+               }
+
+             prev = f;
+           }
+
+         if (f != NULL)
+           break;
+
+         if (pass == 1)
+           as_warn_where (l->fixp->fx_file, l->fixp->fx_line,
+                          "Unmatched %%hi reloc");
+       }
+    }
 }
 
 /* When generating embedded PIC code we need to use a special
@@ -5316,7 +6539,7 @@ md_apply_fix (fixP, valueP)
   unsigned char *buf;
   long insn, value;
 
-  assert (fixP->fx_size == 4);
+  assert (fixP->fx_size == 4 || fixP->fx_r_type == BFD_RELOC_16);
 
   value = *valueP;
   fixP->fx_addnumber = value;  /* Remember value for tc_gen_reloc */
@@ -5334,6 +6557,10 @@ md_apply_fix (fixP, valueP)
     case BFD_RELOC_MIPS_CALL16:
     case BFD_RELOC_MIPS_GOT16:
     case BFD_RELOC_MIPS_GPREL32:
+    case BFD_RELOC_MIPS_GOT_HI16:
+    case BFD_RELOC_MIPS_GOT_LO16:
+    case BFD_RELOC_MIPS_CALL_HI16:
+    case BFD_RELOC_MIPS_CALL_LO16:
       if (fixP->fx_pcrel)
        as_bad_where (fixP->fx_file, fixP->fx_line,
                      "Invalid PC relative reloc");
@@ -5383,6 +6610,15 @@ md_apply_fix (fixP, valueP)
                            value, 4);
       break;
 
+    case BFD_RELOC_16:
+      /* If we are deleting this reloc entry, we must fill in the
+         value now.  */
+      assert (fixP->fx_size == 2);
+      if (fixP->fx_done)
+       md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+                           value, 2);
+      break;
+
     case BFD_RELOC_LO16:
       /* When handling an embedded PIC switch statement, we can wind
         up deleting a LO16 reloc.  See the 'o' case in mips_ip.  */
@@ -5648,25 +6884,18 @@ s_align (x)
   demand_empty_rest_of_line ();
 }
 
-/* Handle .ascii and .asciiz.  This just calls stringer and forgets
-   that there was a previous instruction.  */
-
-static void
-s_stringer (append_zero)
-     int append_zero;
+void
+mips_flush_pending_output ()
 {
   mips_emit_delays ();
   insn_label = NULL;
-  stringer (append_zero);
 }
 
 static void
 s_change_sec (sec)
      int sec;
 {
-#ifdef GPOPT
   segT seg;
-#endif
 
   /* When generating embedded PIC code, we only use the .text, .lit8,
      .sdata and .sbss sections.  We change the .data and .rdata
@@ -5690,56 +6919,61 @@ s_change_sec (sec)
       break;
 
     case 'r':
-      seg = subseg_new (RDATA_SECTION_NAME,
-                       (subsegT) get_absolute_expression ());
-#ifdef OBJ_ELF
-      bfd_set_section_flags (stdoutput, seg,
-                            (SEC_ALLOC
-                             | SEC_LOAD
-                             | SEC_READONLY
-                             | SEC_RELOC
-                             | SEC_DATA));
-      bfd_set_section_alignment (stdoutput, seg, 4);
-#endif
-      demand_empty_rest_of_line ();
+      if (USE_GLOBAL_POINTER_OPT)
+       {
+         seg = subseg_new (RDATA_SECTION_NAME,
+                           (subsegT) get_absolute_expression ());
+         if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+           {
+             bfd_set_section_flags (stdoutput, seg,
+                                    (SEC_ALLOC
+                                     | SEC_LOAD
+                                     | SEC_READONLY
+                                     | SEC_RELOC
+                                     | SEC_DATA));
+             bfd_set_section_alignment (stdoutput, seg, 4);
+           }
+         demand_empty_rest_of_line ();
+       }
+      else
+       {
+         as_bad ("No read only data section in this object file format");
+         demand_empty_rest_of_line ();
+         return;
+       }
       break;
 
     case 's':
-#ifdef GPOPT
-      seg = subseg_new (".sdata", (subsegT) get_absolute_expression ());
-#ifdef OBJ_ELF
-      bfd_set_section_flags (stdoutput, seg,
-                            SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA);
-      bfd_set_section_alignment (stdoutput, seg, 4);
-#endif
-      demand_empty_rest_of_line ();
-      break;
-#else /* ! defined (GPOPT) */
-      as_bad ("Global pointers not supported; recompile -G 0");
-      demand_empty_rest_of_line ();
-      return;
-#endif /* ! defined (GPOPT) */
+      if (USE_GLOBAL_POINTER_OPT)
+       {
+         seg = subseg_new (".sdata", (subsegT) get_absolute_expression ());
+         if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+           {
+             bfd_set_section_flags (stdoutput, seg,
+                                    SEC_ALLOC | SEC_LOAD | SEC_RELOC
+                                    | SEC_DATA);
+             bfd_set_section_alignment (stdoutput, seg, 4);
+           }
+         demand_empty_rest_of_line ();
+         break;
+       }
+      else
+       {
+         as_bad ("Global pointers not supported; recompile -G 0");
+         demand_empty_rest_of_line ();
+         return;
+       }
     }
 
   auto_align = 1;
 }
 
-#ifdef OBJ_ELF
-
-/* Handle the ELF .section pseudo-op.  This is a wrapper around
-   obj_elf_section.  */
-
-static void
-s_elf_section (x)
-     int x;
+void
+mips_enable_auto_align ()
 {
-  mips_emit_delays ();
-  obj_elf_section (x);
   auto_align = 1;
 }
 
-#endif /* OBJ_ELF */
-
 static void
 s_cons (log_size)
      int log_size;
@@ -5754,31 +6988,6 @@ s_cons (log_size)
   cons (1 << log_size);
 }
 
-static void
-s_err (x)
-     int x;
-{
-  as_fatal ("Encountered `.err', aborting assembly");
-}
-
-static void
-s_extern (x)
-     int x;
-{
-  valueT size;
-  symbolS *symbolP;
-
-  symbolP = get_symbol ();
-  if (*input_line_pointer == ',')
-    input_line_pointer++;
-  size = get_absolute_expression ();
-  S_SET_EXTERNAL (symbolP);
-
-#ifdef ECOFF_DEBUGGING
-  symbolP->ecoff_extern_size = size;
-#endif
-}
-
 static void
 s_float_cons (type)
      int type;
@@ -5813,12 +7022,18 @@ s_mips_globl (x)
   char *name;
   int c;
   symbolS *symbolP;
+  flagword flag;
 
   name = input_line_pointer;
   c = get_symbol_end ();
   symbolP = symbol_find_or_make (name);
   *input_line_pointer = c;
   SKIP_WHITESPACE ();
+
+  /* On Irix 5, every global symbol that is not explicitly labelled as
+     being a function is apparently labelled as being an object.  */
+  flag = BSF_OBJECT;
+
   if (! is_end_of_line[(unsigned char) *input_line_pointer])
     {
       char *secname;
@@ -5832,9 +7047,11 @@ s_mips_globl (x)
       *input_line_pointer = c;
 
       if (sec != NULL && (sec->flags & SEC_CODE) != 0)
-       symbolP->bsym->flags |= BSF_FUNCTION;
+       flag = BSF_FUNCTION;
     }
 
+  symbolP->bsym->flags |= flag;
+
   S_SET_EXTERNAL (symbolP);
   demand_empty_rest_of_line ();
 }
@@ -5865,15 +7082,13 @@ s_option (x)
       else
        as_bad (".option pic%d not supported", i);
 
-#ifdef GPOPT
-      if (mips_pic == SVR4_PIC)
+      if (USE_GLOBAL_POINTER_OPT && mips_pic == SVR4_PIC)
        {
          if (g_switch_seen && g_switch_value != 0)
            as_warn ("-G may not be used with SVR4 PIC code");
          g_switch_value = 0;
          bfd_set_gp_size (stdoutput, 0);
        }
-#endif
     }
   else
     as_warn ("Unrecognized option \"%s\"", opt);
@@ -5951,7 +7166,7 @@ s_mipsset (x)
       isa = atoi (name + 4);
       if (isa == 0)
        mips_isa = file_mips_isa;
-      else if (isa < 1 || isa > 3)
+      else if (isa < 1 || isa > 4)
        as_bad ("unknown ISA level");
       else
        mips_isa = isa;
@@ -5964,18 +7179,6 @@ s_mipsset (x)
   demand_empty_rest_of_line ();
 }
 
-/* The same as the usual .space directive, except that we have to
-   forget about any previous instruction.  */
-
-static void
-s_mips_space (param)
-     int param;
-{
-  mips_emit_delays ();
-  insn_label = NULL;
-  s_space (param);
-}
-
 /* Handle the .abicalls pseudo-op.  I believe this is equivalent to
    .option pic2.  It means to generate SVR4 PIC calls.  */
 
@@ -5984,11 +7187,12 @@ s_abicalls (ignore)
      int ignore;
 {
   mips_pic = SVR4_PIC;
-#ifdef GPOPT
-  if (g_switch_seen && g_switch_value != 0)
-    as_warn ("-G may not be used with SVR4 PIC code");
-  g_switch_value = 0;
-#endif
+  if (USE_GLOBAL_POINTER_OPT)
+    {
+      if (g_switch_seen && g_switch_value != 0)
+       as_warn ("-G may not be used with SVR4 PIC code");
+      g_switch_value = 0;
+    }
   bfd_set_gp_size (stdoutput, 0);
   demand_empty_rest_of_line ();
 }
@@ -6026,6 +7230,9 @@ s_cpload (ignore)
   ex.X_op_symbol = NULL;
   ex.X_add_number = 0;
 
+  /* In ELF, this symbol is implicitly an STT_OBJECT symbol.  */
+  ex.X_add_symbol->bsym->flags |= BSF_OBJECT;
+
   macro_build_lui ((char *) NULL, &icnt, &ex, GP);
   macro_build ((char *) NULL, &icnt, &ex, "addiu", "t,r,j", GP, GP,
               (int) BFD_RELOC_LO16);
@@ -6188,32 +7395,40 @@ md_section_align (seg, addr)
 {
   int align = bfd_get_section_alignment (stdoutput, seg);
 
+#ifdef OBJ_ELF
+  /* We don't need to align ELF sections to the full alignment.
+     However, Irix 5 may prefer that we align them at least to a 16
+     byte boundary.  */
+  if (align > 16)
+    align = 16;
+#endif
+
   return ((addr + (1 << align) - 1) & (-1 << align));
 }
 
-/* Estimate the size of a frag before relaxing.  We are not really
-   relaxing here, and the final size is encoded in the subtype
-   information.  */
+/* Utility routine, called from above as well.  If called while the
+   input file is still being read, it's only an approximation.  (For
+   example, a symbol may later become defined which appeared to be
+   undefined earlier.)  */
 
-/*ARGSUSED*/
-int
-md_estimate_size_before_relax (fragp, segtype)
-     fragS *fragp;
-     asection *segtype;
+static int
+nopic_need_relax (sym)
+     symbolS *sym;
 {
-  int change;
+  if (sym == 0)
+    return 0;
 
-  if (mips_pic == NO_PIC)
+  if (USE_GLOBAL_POINTER_OPT)
     {
-#ifdef GPOPT
       const char *symname;
+      int change;
 
       /* Find out whether this symbol can be referenced off the GP
         register.  It can be if it is smaller than the -G size or if
         it is in the .sdata or .sbss section.  Certain symbols can
         not be referenced off the GP, although it appears as though
         they can.  */
-      symname = S_GET_NAME (fragp->fr_symbol);
+      symname = S_GET_NAME (sym);
       if (symname != (const char *) NULL
          && (strcmp (symname, "eprol") == 0
              || strcmp (symname, "etext") == 0
@@ -6225,26 +7440,47 @@ md_estimate_size_before_relax (fragp, segtype)
              || strcmp (symname, "end") == 0
              || strcmp (symname, "_gp_disp") == 0))
        change = 1;
-      else if (! S_IS_DEFINED (fragp->fr_symbol)
-              && ((fragp->fr_symbol->ecoff_extern_size != 0
-                   && fragp->fr_symbol->ecoff_extern_size <= g_switch_value)
-                  || (S_GET_VALUE (fragp->fr_symbol) != 0
-                      && S_GET_VALUE (fragp->fr_symbol) <= g_switch_value)))
+      else if (! S_IS_DEFINED (sym)
+              && (0
+#ifndef NO_ECOFF_DEBUGGING
+                  || (sym->ecoff_extern_size != 0
+                      && sym->ecoff_extern_size <= g_switch_value)
+#endif
+                  || (S_GET_VALUE (sym) != 0
+                      && S_GET_VALUE (sym) <= g_switch_value)))
        change = 0;
       else
        {
          const char *segname;
 
-         segname = segment_name (S_GET_SEGMENT (fragp->fr_symbol));
+         segname = segment_name (S_GET_SEGMENT (sym));
          assert (strcmp (segname, ".lit8") != 0
                  && strcmp (segname, ".lit4") != 0);
          change = (strcmp (segname, ".sdata") != 0
                    && strcmp (segname, ".sbss") != 0);
        }
-#else /* ! defined (GPOPT) */
-      /* We are not optimizing for the GP register.  */
-      change = 1;
-#endif /* ! defined (GPOPT) */  
+      return change;
+    }
+  else
+    /* We are not optimizing for the GP register.  */
+    return 1;
+}
+
+/* Estimate the size of a frag before relaxing.  We are not really
+   relaxing here, and the final size is encoded in the subtype
+   information.  */
+
+/*ARGSUSED*/
+int
+md_estimate_size_before_relax (fragp, segtype)
+     fragS *fragp;
+     asection *segtype;
+{
+  int change;
+
+  if (mips_pic == NO_PIC)
+    {
+      change = nopic_need_relax (fragp->fr_symbol);
     }
   else if (mips_pic == SVR4_PIC)
     {
@@ -6288,6 +7524,7 @@ tc_gen_reloc (section, fixp)
 {
   static arelent *retval[4];
   arelent *reloc;
+  bfd_reloc_code_real_type code;
 
   reloc = retval[0] = (arelent *) xmalloc (sizeof (arelent));
   retval[1] = NULL;
@@ -6302,9 +7539,8 @@ tc_gen_reloc (section, fixp)
         is actually the difference between the reloc address and the
         subtrahend.  */
       reloc->addend = reloc->address - S_GET_VALUE (fixp->fx_subsy);
-#ifndef OBJ_ECOFF
-      as_fatal ("Double check fx_r_type in tc-mips.c:tc_gen_reloc");
-#endif
+      if (OUTPUT_FLAVOR != bfd_target_ecoff_flavour)
+       as_fatal ("Double check fx_r_type in tc-mips.c:tc_gen_reloc");
       fixp->fx_r_type = BFD_RELOC_GPREL32;
     }
   else if (fixp->fx_r_type == BFD_RELOC_PCREL_LO16)
@@ -6335,13 +7571,12 @@ tc_gen_reloc (section, fixp)
     reloc->addend = fixp->fx_addnumber;
   else
     {
-#ifndef OBJ_AOUT
-      /* A gruesome hack which is a result of the gruesome gas reloc
-        handling.  */
-      reloc->addend = reloc->address;
-#else
-      reloc->addend = -reloc->address;
-#endif
+      if (OUTPUT_FLAVOR != bfd_target_aout_flavour)
+       /* A gruesome hack which is a result of the gruesome gas reloc
+          handling.  */
+       reloc->addend = reloc->address;
+      else
+       reloc->addend = -reloc->address;
     }
 
   /* If this is a variant frag, we may need to adjust the existing
@@ -6349,18 +7584,29 @@ tc_gen_reloc (section, fixp)
   if (fixp->fx_frag->fr_opcode != NULL
       && (fixp->fx_r_type == BFD_RELOC_MIPS_GPREL
          || fixp->fx_r_type == BFD_RELOC_MIPS_GOT16
-         || fixp->fx_r_type == BFD_RELOC_MIPS_CALL16))
+         || fixp->fx_r_type == BFD_RELOC_MIPS_CALL16
+         || fixp->fx_r_type == BFD_RELOC_MIPS_GOT_HI16
+         || fixp->fx_r_type == BFD_RELOC_MIPS_GOT_LO16
+         || fixp->fx_r_type == BFD_RELOC_MIPS_CALL_HI16
+         || fixp->fx_r_type == BFD_RELOC_MIPS_CALL_LO16))
     {
       arelent *reloc2;
 
       /* If this is not the last reloc in this frag, then we have two
-        GPREL relocs, both of which are being replaced.  Let the
-        second one handle all of them.  */
+        GPREL relocs, or a GOT_HI16/GOT_LO16 pair, or a
+        CALL_HI16/CALL_LO16, both of which are being replaced.  Let
+        the second one handle all of them.  */
       if (fixp->fx_next != NULL
          && fixp->fx_frag == fixp->fx_next->fx_frag)
        {
-         assert (fixp->fx_r_type == BFD_RELOC_MIPS_GPREL
-                 && fixp->fx_next->fx_r_type == BFD_RELOC_MIPS_GPREL);
+         assert ((fixp->fx_r_type == BFD_RELOC_MIPS_GPREL
+                  && fixp->fx_next->fx_r_type == BFD_RELOC_MIPS_GPREL)
+                 || (fixp->fx_r_type == BFD_RELOC_MIPS_GOT_HI16
+                     && (fixp->fx_next->fx_r_type
+                         == BFD_RELOC_MIPS_GOT_LO16))
+                 || (fixp->fx_r_type == BFD_RELOC_MIPS_CALL_HI16
+                     && (fixp->fx_next->fx_r_type
+                         == BFD_RELOC_MIPS_CALL_LO16)));
          retval[0] = NULL;
          return retval;
        }
@@ -6394,31 +7640,70 @@ tc_gen_reloc (section, fixp)
        }
       else if (mips_pic == SVR4_PIC)
        {
-         if (fixp->fx_r_type != BFD_RELOC_MIPS_GOT16)
+         switch (fixp->fx_r_type)
            {
-             assert (fixp->fx_r_type == BFD_RELOC_MIPS_CALL16);
+           default:
+             abort ();
+           case BFD_RELOC_MIPS_GOT16:
+             break;
+           case BFD_RELOC_MIPS_CALL16:
+           case BFD_RELOC_MIPS_GOT_LO16:
+           case BFD_RELOC_MIPS_CALL_LO16:
              fixp->fx_r_type = BFD_RELOC_MIPS_GOT16;
+             break;
            }
        }
       else
        abort ();
     }
 
+  /* Since DIFF_EXPR_OK is defined in tc-mips.h, it is possible that
+     fixup_segment converted a non-PC relative reloc into a PC
+     relative reloc.  In such a case, we need to convert the reloc
+     code.  */
+  code = fixp->fx_r_type;
+  if (fixp->fx_pcrel)
+    {
+      switch (code)
+       {
+       case BFD_RELOC_8:
+         code = BFD_RELOC_8_PCREL;
+         break;
+       case BFD_RELOC_16:
+         code = BFD_RELOC_16_PCREL;
+         break;
+       case BFD_RELOC_32:
+         code = BFD_RELOC_32_PCREL;
+         break;
+       case BFD_RELOC_8_PCREL:
+       case BFD_RELOC_16_PCREL:
+       case BFD_RELOC_32_PCREL:
+       case BFD_RELOC_16_PCREL_S2:
+       case BFD_RELOC_PCREL_HI16_S:
+       case BFD_RELOC_PCREL_LO16:
+         break;
+       default:
+         as_bad_where (fixp->fx_file, fixp->fx_line,
+                       "Cannot make %s relocation PC relative",
+                       bfd_get_reloc_code_name (code));
+       }
+    }
+
   /* To support a PC relative reloc when generating embedded PIC code
      for ECOFF, we use a Cygnus extension.  We check for that here to
      make sure that we don't let such a reloc escape normally.  */
-#ifdef OBJ_ECOFF
-  if (fixp->fx_r_type == BFD_RELOC_16_PCREL_S2
+  if (OUTPUT_FLAVOR == bfd_target_ecoff_flavour
+      && code == BFD_RELOC_16_PCREL_S2
       && mips_pic != EMBEDDED_PIC)
     reloc->howto = NULL;
   else
-#endif
-    reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+    reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
 
   if (reloc->howto == NULL)
     {
       as_bad_where (fixp->fx_file, fixp->fx_line,
-                   "Can not represent relocation in this object file format");
+                   "Can not represent %s relocation in this object file format",
+                   bfd_get_reloc_code_name (code));
       retval[0] = NULL;
     }
 
@@ -6459,27 +7744,73 @@ mips_define_label (sym)
 {
   insn_label = sym;
 }
+
+/* Decide whether a label is local.  This is called by LOCAL_LABEL.
+   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,
+   since they are useless.  */
+
+int
+mips_local_label (name)
+     const char *name;
+{
+#ifndef NO_ECOFF_DEBUGGING
+  if (ECOFF_DEBUGGING
+      && mips_debug != 0
+      && ! ecoff_debugging_seen)
+    {
+      /* We were called with -g, but we didn't see any debugging
+         information.  That may mean that gcc is smuggling debugging
+         information through to mips-tfile, in which case we must
+         generate all local labels.  */
+      return 0;
+    }
+#endif
+
+  /* Here it's OK to discard local labels.  */
+
+  return name[0] == '$';
+}
 \f
-#ifdef OBJ_ELF
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
 
 /* Some special processing for a MIPS ELF file.  */
 
 void
 mips_elf_final_processing ()
 {
-  Elf32_RegInfo s;
-
-  /* Write out the .reginfo section.  */
-  s.ri_gprmask = mips_gprmask;
-  s.ri_cprmask[0] = mips_cprmask[0];
-  s.ri_cprmask[1] = mips_cprmask[1];
-  s.ri_cprmask[2] = mips_cprmask[2];
-  s.ri_cprmask[3] = mips_cprmask[3];
-  /* The gp_value field is set by the MIPS ELF backend.  */
-
-  bfd_mips_elf32_swap_reginfo_out (stdoutput, &s,
-                                  ((Elf32_External_RegInfo *)
-                                   mips_regmask_frag));
+  /* Write out the register information.  */
+  if (! mips_64)
+    {
+      Elf32_RegInfo s;
+
+      s.ri_gprmask = mips_gprmask;
+      s.ri_cprmask[0] = mips_cprmask[0];
+      s.ri_cprmask[1] = mips_cprmask[1];
+      s.ri_cprmask[2] = mips_cprmask[2];
+      s.ri_cprmask[3] = mips_cprmask[3];
+      /* The gp_value field is set by the MIPS ELF backend.  */
+
+      bfd_mips_elf32_swap_reginfo_out (stdoutput, &s,
+                                      ((Elf32_External_RegInfo *)
+                                       mips_regmask_frag));
+    }
+  else
+    {
+      Elf64_Internal_RegInfo s;
+
+      s.ri_gprmask = mips_gprmask;
+      s.ri_pad = 0;
+      s.ri_cprmask[0] = mips_cprmask[0];
+      s.ri_cprmask[1] = mips_cprmask[1];
+      s.ri_cprmask[2] = mips_cprmask[2];
+      s.ri_cprmask[3] = mips_cprmask[3];
+      /* The gp_value field is set by the MIPS ELF backend.  */
+
+      bfd_mips_elf64_swap_reginfo_out (stdoutput, &s,
+                                      ((Elf64_External_RegInfo *)
+                                       mips_regmask_frag));
+    }
 
   /* Set the MIPS ELF flag bits.  FIXME: There should probably be some
      sort of BFD interface for this.  */
@@ -6489,10 +7820,8 @@ mips_elf_final_processing ()
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_PIC;
 }
 
-#endif /* OBJ_ELF */
+#endif /* OBJ_ELF || OBJ_MAYBE_ELF */
 \f
-#ifndef ECOFF_DEBUGGING
-
 /* These functions should really be defined by the object file format,
    since they are related to debugging information.  However, this
    code has to work for the a.out format, which does not define them,
@@ -6562,8 +7891,6 @@ md_obj_end ()
     as_warn ("missing `.end' at end of assembly");
 }
 
-extern char hex_value[];
-
 static long
 get_number ()
 {
@@ -6585,7 +7912,7 @@ get_number ()
          while (isxdigit (*input_line_pointer))
            {
              val <<= 4;
-             val |= hex_value[(int) *input_line_pointer++];
+             val |= hex_value (*input_line_pointer++);
            }
          return negative ? -val : val;
        }
@@ -6825,7 +8152,7 @@ s_loc (x)
   assert (now_seg == text_section);
 
   lineno = get_number ();
-  addroff = obstack_next_free (&frags) - frag_now->fr_literal;
+  addroff = frag_now_fix ();
 
   symbolP = symbol_new ("", N_SLINE, addroff, frag_now);
   S_SET_TYPE (symbolP, N_SLINE);
@@ -6834,5 +8161,3 @@ s_loc (x)
   symbolP->sy_segment = now_seg;
 }
 #endif
-
-#endif /* ! defined (ECOFF_DEBUGGING) */