* config/tc-mips.c (md_section_align): Do align if OBJ_ELF, but
[binutils-gdb.git] / gas / config / tc-mips.c
index 24805dedd9d752decff7f65bd52e2a5f0b68ef96..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;
+
+/* 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;
+
+/* 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.  */
+#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
@@ -71,12 +123,63 @@ static char *mips_regmask_frag;
 unsigned long mips_gprmask;
 unsigned long mips_cprmask[4];
 
-/* MIPS ISA (Instruction Set Architecture) level.  */
+/* MIPS ISA (Instruction Set Architecture) level (may be changed
+   temporarily using .set mipsN).  */
 static int mips_isa = -1;
 
-/* MIPS PIC level.  0 is normal, non-PIC code.  2 means to generate
-   SVR4 ABI PIC calls.  1 doesn't mean anything.  */
-static int mips_pic;
+/* MIPS ISA we are using for this output file.  */
+static int file_mips_isa;
+
+/* The CPU type as a number: 2000, 3000, 4000, 4400, etc.  */
+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.  */
+
+enum mips_pic_level
+{
+  /* Do not generate PIC code.  */
+  NO_PIC,
+
+  /* Generate PIC code as in Irix 4.  This is not implemented, and I'm
+     not sure what it is supposed to do.  */
+  IRIX4_PIC,
+
+  /* Generate PIC code as in the SVR4 MIPS ABI.  */
+  SVR4_PIC,
+
+  /* Generate PIC code without using a global offset table: the data
+     segment has a maximum size of 64K, all data references are off
+     the $gp register, and all text references are PC relative.  This
+     is used on some embedded systems.  */
+  EMBEDDED_PIC
+};
+
+static enum mips_pic_level mips_pic;
+
+/* 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;
 
 static int mips_warn_about_macros;
 static int mips_noreorder;
@@ -85,14 +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;
-#endif
+/* Whether the -G option was used.  */
+static int g_switch_seen = 0;
 
 #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;
 
@@ -127,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;
 
@@ -153,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;
 
@@ -184,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
@@ -193,7 +338,7 @@ static int prev_prev_insn_unreordered;
    Every macro that refers to a symbol can occur in (at least) two
    forms, one with GP relative addressing and one without.  For
    example, loading a global variable into a register generally uses
-   an macroinstruction like this:
+   a macro instruction like this:
      lw $4,i
    If i can be addressed off the GP register (this is true if it is in
    the .sbss or .sdata section, or if it is known to be smaller than
@@ -211,12 +356,12 @@ static int prev_prev_insn_unreordered;
    until after we see the instruction that uses it.  Therefore, we
    want to be able to choose the final instruction sequence only at
    the end of the assembly.  This is similar to the way other
-   platforms choose the form of a PC relative instruction only at the
+   platforms choose the size of a PC relative instruction only at the
    end of assembly.
 
    When generating position independent code we do not use GP
-   addressing in the same way, but the issue still arises as external
-   symbols and local symbols must be handled differently.
+   addressing in quite the same way, but the issue still arises as
+   external symbols and local symbols must be handled differently.
 
    We handle these issues by actually generating both possible
    instruction sequences.  The longer one is put in a frag_var with
@@ -233,13 +378,13 @@ static int prev_prev_insn_unreordered;
    noat is in effect).  All these numbers are reasonably small.
 
    Generating two instruction sequences must be handled carefully to
-   ensure that delay slots are handled correctly.  Fortunately, the
-   issue only arises in a restricted number of cases.  When the second
-   instruction sequence is generated, append_insn is directed to
-   maintain the existing delay slot information, so it continues to
-   apply to any code after the second instruction sequence.  This
-   means that the second instruction sequence must not impose any
-   requirements not required by the first instruction sequence.
+   ensure that delay slots are handled correctly.  Fortunately, there
+   are a limited number of cases.  When the second instruction
+   sequence is generated, append_insn is directed to maintain the
+   existing delay slot information, so it continues to apply to any
+   code after the second instruction sequence.  This means that the
+   second instruction sequence must not impose any requirements not
+   required by the first instruction sequence.
 
    These variant frags are then handled in functions called by the
    machine independent code.  md_estimate_size_before_relax returns
@@ -256,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
@@ -272,56 +417,55 @@ 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
+static void macro2 PARAMS ((struct mips_cl_insn * ip));
+#endif
 static void mips_ip PARAMS ((char *str, struct mips_cl_insn * ip));
 static int my_getSmallExpression PARAMS ((expressionS * ep, char *str));
 static void my_getExpression PARAMS ((expressionS * ep, char *str));
 static symbolS *get_symbol PARAMS ((void));
-static void mips_align PARAMS ((int to, int fill));
+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
 \f
 /* Pseudo-op table.
 
@@ -341,7 +485,7 @@ static void s_mask PARAMS ((char));
    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},
@@ -349,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},
@@ -366,18 +510,25 @@ 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'},
-  {"space", s_mips_space, 0},
+  {"globl", s_mips_globl, 0},
+  {"global", s_mips_globl, 0},
+  {"hword", s_cons, 1},
+  {"int", s_cons, 2},
+  {"long", s_cons, 2},
+  {"octa", s_cons, 4},
+  {"quad", s_cons, 3},
+  {"short", s_cons, 1},
+  {"single", s_float_cons, 'f'},
   {"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},
@@ -391,27 +542,35 @@ const pseudo_typeS md_pseudo_table[] =
   {"loc", s_ignore, 0},
   {"mask", s_ignore, 'R'},
   {"verstamp", s_ignore, 0},
-#endif
-
- /* Sentinel.  */
-  {NULL}
-};
-\f
-const relax_typeS md_relax_table[] =
-{
-  { 0 }
+  { 0 },
 };
 
+extern void pop_insert PARAMS ((const pseudo_typeS *));
 
+void
+mips_pop_insert ()
+{
+  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
@@ -426,19 +585,128 @@ md_begin ()
 
   if (mips_isa == -1)
     {
-      if (strcmp (TARGET_CPU, "mips") == 0)
-       mips_isa = 1;
-      else if (strcmp (TARGET_CPU, "r6000") == 0
-              || strcmp (TARGET_CPU, "mips2") == 0)
-       mips_isa = 2;
-      else if (strcmp (TARGET_CPU, "mips64") == 0
-              || strcmp (TARGET_CPU, "r4000") == 0
-              || strcmp (TARGET_CPU, "mips3") == 0)
-       mips_isa = 3;
+      const char *cpu;
+      char *a = NULL;
+
+      cpu = TARGET_CPU;
+      if (strcmp (cpu + (sizeof TARGET_CPU) - 3, "el") == 0)
+       {
+         a = xmalloc (sizeof TARGET_CPU);
+         strcpy (a, TARGET_CPU);
+         a[(sizeof TARGET_CPU) - 3] = '\0';
+         cpu = a;
+       }
+
+      if (strcmp (cpu, "mips") == 0)
+       {
+         mips_isa = 1;
+         if (mips_cpu == -1)
+           mips_cpu = 3000;
+       }
+      else if (strcmp (cpu, "r6000") == 0
+              || strcmp (cpu, "mips2") == 0)
+       {
+         mips_isa = 2;
+         if (mips_cpu == -1)
+           mips_cpu = 6000;
+       }
+      else if (strcmp (cpu, "mips64") == 0
+              || strcmp (cpu, "r4000") == 0
+              || strcmp (cpu, "mips3") == 0)
+       {
+         mips_isa = 3;
+         if (mips_cpu == -1)
+           mips_cpu = 4000;
+       }
+      else if (strcmp (cpu, "r4400") == 0)
+       {
+         mips_isa = 3;
+         if (mips_cpu == -1)
+           mips_cpu = 4400;
+       }
+      else if (strcmp (cpu, "mips64orion") == 0
+              || strcmp (cpu, "r4600") == 0)
+       {
+         mips_isa = 3;
+         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_isa = 1;
+         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");
+
   switch (mips_isa)
     {
     case 1:
@@ -450,10 +718,15 @@ 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");
 
+  file_mips_isa = mips_isa;
+
   op_hash = hash_new ();
 
   for (i = 0; i < NUMOPCODES;)
@@ -493,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);
+  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
+           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);
 
 #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);
-
-  /* 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);
-
-    /* I don't know why this section should be loaded, but the ABI
-       says that SHF_ALLOC should be set.  */
-    (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);
+           /* 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
+         }
+
+       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);
+         }
+
+       subseg_set (seg, subseg);
+      }
+    }
 
-    subseg_set (seg, subseg);
-  }
-#endif /* OBJ_ELF */
-
-#ifndef ECOFF_DEBUGGING
-  md_obj_begin ();
-#endif
+  if (! ECOFF_DEBUGGING)
+    md_obj_begin ();
 }
 
 void
 md_mips_end ()
 {
-#ifndef ECOFF_DEBUGGING
-  md_obj_end ();
-#endif
+  if (! ECOFF_DEBUGGING)
+    md_obj_end ();
 }
 
 void
@@ -557,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)
@@ -572,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);
     }
 }
 
@@ -624,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;
@@ -671,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
@@ -687,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
@@ -739,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
@@ -754,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;
        }
 
@@ -773,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
@@ -797,15 +1149,41 @@ append_insn (place, ip, address_expr, reloc_type)
       /* Now emit the right number of NOP instructions.  */
       if (nops > 0)
        {
-         emit_nop ();
-         if (nops > 1)
+         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 ();
+             /* 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
        }
     }
   
@@ -843,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;
+               }
+           }
        }
     }
 
@@ -867,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.  */
@@ -885,8 +1279,6 @@ append_insn (place, ip, address_expr, reloc_type)
          || (pinfo & INSN_COND_BRANCH_DELAY))
        {
          if (mips_optimize < 2
-             /* If we have seen .set nobopt, don't optimize.  */
-             || mips_nobopt != 0
              /* If we have seen .set volatile or .set nomove, don't
                 optimize.  */
              || mips_nomove != 0
@@ -933,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
@@ -997,13 +1393,19 @@ append_insn (place, ip, address_expr, reloc_type)
                                    0))
              || ((pinfo & INSN_WRITE_GPR_31)
                  && insn_uses_reg (&prev_insn, 31, 0))
+             /* If we are generating embedded PIC code, the branch
+                might be expanded into a sequence which uses $at, so
+                we can't swap with an instruction which reads it.  */
+             || (mips_pic == EMBEDDED_PIC
+                 && insn_uses_reg (&prev_insn, AT, 0))
              /* 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),
@@ -1118,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)
        {
@@ -1149,8 +1561,9 @@ mips_emit_delays ()
              S_SET_VALUE (insn_label, (valueT) frag_now_fix ());
            }
        }
-      mips_no_prev_insn ();
     }
+
+  mips_no_prev_insn ();
 }
 
 /* Build an instruction created by a macro expansion.  This is passed
@@ -1158,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,
@@ -1166,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;
@@ -1175,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);
@@ -1204,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);
@@ -1275,13 +1700,32 @@ macro_build (place, counter, ep, name, fmt, va_alist)
                  || r == BFD_RELOC_MIPS_LITERAL
                  || r == BFD_RELOC_LO16
                  || r == BFD_RELOC_MIPS_GOT16
-                 || r== BFD_RELOC_MIPS_CALL16);
+                 || 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
+                     && r == BFD_RELOC_PCREL_LO16));
          continue;
 
        case 'u':
-         assert (ep != NULL && ep->X_op == O_constant);
-         insn.insn_opcode |= (ep->X_add_number >> 16) & 0xffff;
-         ep = NULL;
+         r = (bfd_reloc_code_real_type) va_arg (args, int);
+         assert (ep != NULL
+                 && (ep->X_op == O_constant
+                     || (ep->X_op == O_symbol
+                         && (r == BFD_RELOC_HI16_S
+                             || r == BFD_RELOC_HI16
+                             || r == BFD_RELOC_MIPS_GOT_HI16
+                             || r == BFD_RELOC_MIPS_CALL_HI16))
+                     || (ep->X_op == O_subtract
+                         && now_seg == text_section
+                         && r == BFD_RELOC_PCREL_HI16_S)));
+         if (ep->X_op == O_constant)
+           {
+             insn.insn_opcode |= (ep->X_add_number >> 16) & 0xffff;
+             ep = NULL;
+             r = BFD_RELOC_UNUSED;
+           }
          continue;
 
        case 'p':
@@ -1315,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);
 }
 
 /*
@@ -1339,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)
@@ -1355,7 +1799,7 @@ macro_build_lui (place, counter, ep, regnum)
     {
       assert (ep->X_op == O_symbol);
       /* _gp_disp is a special case, used from s_cpload.  */
-      assert (mips_pic == 0
+      assert (mips_pic == NO_PIC
              || strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0);
       r = BFD_RELOC_HI16_S;
     }
@@ -1380,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()
@@ -1402,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);
@@ -1425,68 +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)
-    macro_build ((char *) NULL, counter, ep,
-                mips_isa < 3 ? "addiu" : "daddiu",
-                "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);
-      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
-       {
-         expressionS mid16;
+    }
+  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)
+       {
+         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);
+                      freg, 16);
+         freg = reg;
        }
-      if ((lo32.X_add_number & 0xffff) != 0)
-       macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i", reg, reg,
-                    (int) BFD_RELOC_LO16);
+      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.  */
@@ -1507,25 +2053,30 @@ load_address (counter, reg, ep)
     }
 
   if (ep->X_op == O_constant)
-    load_register (counter, reg, ep);
-  else if (mips_pic == 0)
+    {
+      load_register (counter, reg, ep, 0);
+      return;
+    }
+
+  if (mips_pic == NO_PIC)
     {
       /* If this is a reference to a GP relative symbol, we want
           addiu        $reg,$gp,<sym>          (BFD_RELOC_MIPS_GPREL)
         Otherwise we want
-          lui          $reg,$gp,<sym>          (BFD_RELOC_HI16_S)
+          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
        {
-         frag_grow (12);
+         frag_grow (20);
          macro_build ((char *) NULL, counter, 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);
@@ -1535,7 +2086,7 @@ load_address (counter, reg, ep)
                   mips_isa < 3 ? "addiu" : "daddiu",
                   "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
     }
-  else
+  else if (mips_pic == SVR4_PIC && ! mips_big_got)
     {
       expressionS ex;
 
@@ -1545,10 +2096,10 @@ load_address (counter, reg, ep)
           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 afterward.  */
+        If there is a constant, it must be added in after.  */
       ex.X_add_number = ep->X_add_number;
       ep->X_add_number = 0;
-      frag_grow (12);
+      frag_grow (20);
       macro_build ((char *) NULL, counter, ep,
                   mips_isa < 3 ? "lw" : "ld",
                   "t,o(b)", reg, (int) BFD_RELOC_MIPS_GOT16, GP);
@@ -1564,50 +2115,126 @@ 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);
        }
-    }                 
-}
-
-/*
- *                     Build macros
- *   This routine implements the seemingly endless macro or synthesized
- * instructions and addressing modes in the mips assembly language. Many
- * of these macros are simple and are similar to each other. These could
- * probably be handled by some kind of table or grammer aproach instead of
- * this verbose method. Others are not simple macros but are more like
- * optimizing code generation.
- *   One interesting optimization is when several store macros appear
- * consecutivly that would load AT with the upper half of the same address.
- * The ensuing load upper instructions are ommited. This implies some kind
- * of global optimization. We currently only optimize within a single macro.
- *   For many of the load and store macros if the address is specified as a
- * constant expression in the first 64k of memory (ie ld $2,0x4000c) we
- * first load register 'at' with zero and use it as the base register. The
- * mips assembler simply uses register $zero. Just one tiny optimization
- * we're missing.
- */
-static void
-macro (ip)
-     struct mips_cl_insn *ip;
-{
-  register int treg, sreg, dreg, breg;
-  int tempreg;
-  int mask;
-  int icnt = 0;
-  int used_at;
-  expressionS expr1;
+    }
+  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);
+       }
+    }
+  else if (mips_pic == EMBEDDED_PIC)
+    {
+      /* We always do
+          addiu        $reg,$gp,<sym>          (BFD_RELOC_MIPS_GPREL)
+        */
+      macro_build ((char *) NULL, counter, ep,
+                  mips_isa < 3 ? "addiu" : "daddiu",
+                  "t,r,j", reg, GP, (int) BFD_RELOC_MIPS_GPREL);
+    }
+  else
+    abort ();
+}
+
+/*
+ *                     Build macros
+ *   This routine implements the seemingly endless macro or synthesized
+ * instructions and addressing modes in the mips assembly language. Many
+ * of these macros are simple and are similar to each other. These could
+ * probably be handled by some kind of table or grammer aproach instead of
+ * this verbose method. Others are not simple macros but are more like
+ * optimizing code generation.
+ *   One interesting optimization is when several store macros appear
+ * consecutivly that would load AT with the upper half of the same address.
+ * The ensuing load upper instructions are ommited. This implies some kind
+ * of global optimization. We currently only optimize within a single macro.
+ *   For many of the load and store macros if the address is specified as a
+ * constant expression in the first 64k of memory (ie ld $2,0x4000c) we
+ * first load register 'at' with zero and use it as the base register. The
+ * mips assembler simply uses register $zero. Just one tiny optimization
+ * we're missing.
+ */
+static void
+macro (ip)
+     struct mips_cl_insn *ip;
+{
+  register int treg, sreg, dreg, breg;
+  int tempreg;
+  int mask;
+  int icnt = 0;
+  int used_at;
+  expressionS expr1;
   const char *s;
   const char *s2;
   const char *fmt;
   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;
 
   treg = (ip->insn_opcode >> 16) & 0x1f;
   dreg = (ip->insn_opcode >> 11) & 0x1f;
@@ -1670,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;
 
@@ -1699,13 +2326,13 @@ macro (ip)
            {
              macro_build ((char *) NULL, &icnt, &imm_expr, "ori", "t,r,i",
                           treg, sreg, (int) BFD_RELOC_LO16);
-             macro_build ((char *) NULL, &icnt, &imm_expr, "nor", "d,v,t",
+             macro_build ((char *) NULL, &icnt, NULL, "nor", "d,v,t",
                           treg, treg, 0);
            }
          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;
 
@@ -1729,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;
 
@@ -1768,7 +2395,8 @@ macro (ip)
          maxnum <<= 16;
          maxnum |= 0xffff;
        }
-      if (imm_expr.X_add_number >= maxnum)
+      if (imm_expr.X_add_number >= maxnum
+         && (mips_isa < 3 || sizeof (maxnum) > 4))
        {
        do_false:
          /* result is always false */
@@ -1814,7 +2442,8 @@ macro (ip)
          maxnum |= 0xffff;
        }
       maxnum = - maxnum - 1;
-      if (imm_expr.X_add_number <= maxnum)
+      if (imm_expr.X_add_number <= maxnum
+         && (mips_isa < 3 || sizeof (maxnum) > 4))
        {
        do_true:
          /* result is always true */
@@ -1949,7 +2578,8 @@ macro (ip)
          maxnum <<= 16;
          maxnum |= 0xffff;
        }
-      if (imm_expr.X_add_number >= maxnum)
+      if (imm_expr.X_add_number >= maxnum
+         && (mips_isa < 3 || sizeof (maxnum) > 4))
        goto do_true;
       imm_expr.X_add_number++;
       /* FALLTHROUGH */
@@ -2077,7 +2707,10 @@ macro (ip)
       if (treg == 0)
        {
          as_warn ("Divide by zero.");
-         macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7);
+         if (mips_trap)
+           macro_build ((char *) NULL, &icnt, NULL, "teq", "s,t", 0, 0);
+         else
+           macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7);
          return;
        }
 
@@ -2087,15 +2720,20 @@ macro (ip)
       macro_build ((char *) NULL, &icnt, NULL,
                   dbl ? "ddiv" : "div",
                   "z,s,t", sreg, treg);
-      expr1.X_add_number = 8;
-      macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", treg, 0);
-      macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
-      macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7);
+      if (mips_trap)
+       macro_build ((char *) NULL, &icnt, NULL, "teq", "s,t", treg, 0);
+      else
+       {
+         expr1.X_add_number = 8;
+         macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", treg, 0);
+         macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
+         macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7);
+       }
       expr1.X_add_number = -1;
       macro_build ((char *) NULL, &icnt, &expr1,
                   dbl ? "daddiu" : "addiu",
                   "t,r,j", AT, 0, (int) BFD_RELOC_LO16);
-      expr1.X_add_number = dbl ? 20 : 16;
+      expr1.X_add_number = mips_trap ? (dbl ? 12 : 8) : (dbl ? 20 : 16);
       macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", treg, AT);
       if (dbl)
        {
@@ -2108,12 +2746,18 @@ macro (ip)
       else
        {
          expr1.X_add_number = 0x80000000;
-         macro_build ((char *) NULL, &icnt, &expr1, "lui", "t,u", AT);
+         macro_build ((char *) NULL, &icnt, &expr1, "lui", "t,u", AT,
+                      (int) BFD_RELOC_HI16);
+       }
+      if (mips_trap)
+       macro_build ((char *) NULL, &icnt, NULL, "teq", "s,t", sreg, AT);
+      else
+       {
+         expr1.X_add_number = 8;
+         macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", sreg, AT);
+         macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
+         macro_build ((char *) NULL, &icnt, NULL, "break", "c", 6);
        }
-      expr1.X_add_number = 8;
-      macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", sreg, AT);
-      macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
-      macro_build ((char *) NULL, &icnt, NULL, "break", "c", 6);
       --mips_noreorder;
       macro_build ((char *) NULL, &icnt, NULL, s, "d", dreg);
       break;
@@ -2157,7 +2801,10 @@ macro (ip)
       if (imm_expr.X_add_number == 0)
        {
          as_warn ("Divide by zero.");
-         macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7);
+         if (mips_trap)
+           macro_build ((char *) NULL, &icnt, NULL, "teq", "s,t", 0, 0);
+         else
+           macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7);
          return;
        }
       if (imm_expr.X_add_number == 1)
@@ -2186,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;
@@ -2211,17 +2858,53 @@ macro (ip)
       ++mips_noreorder;
       mips_any_noreorder = 1;
       macro_build ((char *) NULL, &icnt, NULL, s, "z,s,t", sreg, treg);
-      expr1.X_add_number = 8;
-      macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", treg, 0);
-      macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
-      macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7);
+      if (mips_trap)
+       macro_build ((char *) NULL, &icnt, NULL, "teq", "s,t", treg, 0);
+      else
+       {
+         expr1.X_add_number = 8;
+         macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", treg, 0);
+         macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
+         macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7);
+       }
       --mips_noreorder;
       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 M_LA_AB, we
-        then add a base register to it.  */
+      /* Load the address of a symbol into a register.  If breg is not
+        zero, we then add a base register to it.  */
+
+      /* When generating embedded PIC code, we permit expressions of
+        the form
+          la   $4,foo-bar
+        where bar is an address in the .text section.  These are used
+        when getting the addresses of functions.  We don't permit
+        X_add_number to be non-zero, because if the symbol is
+        external the relaxing code needs to know that any addend is
+        purely the offset to X_op_symbol.  */
+      if (mips_pic == EMBEDDED_PIC
+         && offset_expr.X_op == O_subtract
+         && now_seg == 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)
+       {
+         macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
+                      treg, (int) BFD_RELOC_PCREL_HI16_S);
+         macro_build ((char *) NULL, &icnt, &offset_expr,
+                      mips_isa < 3 ? "addiu" : "daddiu",
+                      "t,r,j", treg, treg, (int) BFD_RELOC_PCREL_LO16);
+         return;
+       }
+
       if (offset_expr.X_op != O_symbol
          && offset_expr.X_op != O_constant)
        {
@@ -2241,8 +2924,8 @@ macro (ip)
        }
 
       if (offset_expr.X_op == O_constant)
-       load_register (&icnt, tempreg, &offset_expr);
-      else if (mips_pic == 0)
+       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
               addiu    $tempreg,$gp,<sym>      (BFD_RELOC_MIPS_GPREL)
@@ -2251,11 +2934,12 @@ 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
            {
-             frag_grow (12);
+             frag_grow (20);
              macro_build ((char *) NULL, &icnt, &offset_expr,
                           mips_isa < 3 ? "addiu" : "daddiu",
                           "t,r,j", tempreg, GP, (int) BFD_RELOC_MIPS_GPREL);
@@ -2272,7 +2956,7 @@ macro (ip)
                       mips_isa < 3 ? "addiu" : "daddiu",
                       "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
        }
-      else
+      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
@@ -2302,9 +2986,9 @@ macro (ip)
             addiu instruction.  */
          expr1.X_add_number = offset_expr.X_add_number;
          offset_expr.X_add_number = 0;
-         frag_grow (24);
+         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)
            {
@@ -2378,7 +3062,13 @@ macro (ip)
                  off1 = -8;
                }
 
+             /* Set mips_optimize around the lui instruction to avoid
+                inserting an unnecessary nop after the lw.  */
+             hold_mips_optimize = mips_optimize;
+             mips_optimize = 2;
              macro_build_lui ((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);
@@ -2392,6 +3082,225 @@ macro (ip)
              used_at = 1;
            }
        }
+      else if (mips_pic == SVR4_PIC)
+       {
+         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);
+       }
+      else
+       abort ();
 
       if (breg != 0)
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
@@ -2407,7 +3316,7 @@ macro (ip)
       /* The j instruction may not be used in PIC code, since it
         requires an absolute address.  We convert it to a b
         instruction.  */
-      if (mips_pic == 0)
+      if (mips_pic == NO_PIC)
        macro_build ((char *) NULL, &icnt, &offset_expr, "j", "a");
       else
        macro_build ((char *) NULL, &icnt, &offset_expr, "b", "p");
@@ -2420,83 +3329,139 @@ macro (ip)
       dreg = RA;
       /* Fall through.  */
     case M_JAL_2:
-      if (mips_pic == 0)
+      if (mips_pic == NO_PIC
+         || mips_pic == EMBEDDED_PIC)
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "jalr",
+                    "d,s", dreg, sreg);
+      else if (mips_pic == SVR4_PIC)
        {
+         if (sreg != PIC_CALL_REG)
+           as_warn ("MIPS PIC call to register other than $25");
+      
          macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "jalr",
                       "d,s", dreg, sreg);
-         return;
+         if (mips_cprestore_offset < 0)
+           as_warn ("No .cprestore pseudo-op used in PIC code");
+         else
+           {
+             expr1.X_add_number = mips_cprestore_offset;
+             macro_build ((char *) NULL, &icnt, &expr1,
+                          mips_isa < 3 ? "lw" : "ld",
+                          "t,o(b)", GP, (int) BFD_RELOC_LO16, mips_frame_reg);
+           }
        }
-
-      /* I only know how to handle pic2.  */
-      assert (mips_pic == 2);
-
-      if (sreg != PIC_CALL_REG)
-       as_warn ("MIPS PIC call to register other than $25");
-      
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "jalr", "d,s",
-                  dreg, sreg);
-      if (mips_cprestore_offset < 0)
-       as_warn ("No .cprestore pseudo-op used in PIC code");
       else
-       {
-         expr1.X_add_number = mips_cprestore_offset;
-         macro_build ((char *) NULL, &icnt, &expr1,
-                      mips_isa < 3 ? "lw" : "ld",
-                      "t,o(b)", GP, (int) BFD_RELOC_LO16, mips_frame_reg);
-       }
+       abort ();
+
       return;
 
     case M_JAL_A:
-      if (mips_pic == 0)
+      if (mips_pic == NO_PIC)
+       macro_build ((char *) NULL, &icnt, &offset_expr, "jal", "a");
+      else if (mips_pic == SVR4_PIC)
        {
-         macro_build ((char *) NULL, &icnt, &offset_expr, "jal", "a");
-         return;
-       }
-
-      /* I only know how to handle pic2.  */
-      assert (mips_pic == 2);
-
-      /* If this is a reference to an external symbol, 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
-          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 (12);
-      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);
-      macro_build (p, &icnt, &offset_expr,
-                  mips_isa < 3 ? "addiu" : "daddiu",
-                  "t,r,j", PIC_CALL_REG, PIC_CALL_REG,
-                  (int) BFD_RELOC_LO16);
-      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "jalr", "s",
-                  PIC_CALL_REG);
-      if (mips_cprestore_offset < 0)
-       as_warn ("No .cprestore pseudo-op used in PIC code");
-      else
+         /* If 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 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 (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,
+                      (int) BFD_RELOC_LO16);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                      "jalr", "s", PIC_CALL_REG);
+         if (mips_cprestore_offset < 0)
+           as_warn ("No .cprestore pseudo-op used in PIC code");
+         else
+           {
+             if (mips_noreorder)
+               macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                            "nop", "");
+             expr1.X_add_number = mips_cprestore_offset;
+             macro_build ((char *) NULL, &icnt, &expr1,
+                          mips_isa < 3 ? "lw" : "ld",
+                          "t,o(b)", GP, (int) BFD_RELOC_LO16,
+                          mips_frame_reg);
+           }
+       }
+      else if (mips_pic == EMBEDDED_PIC)
        {
-         if (mips_noreorder)
-           macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                        "nop", "");
-         expr1.X_add_number = mips_cprestore_offset;
-         macro_build ((char *) NULL, &icnt, &expr1,
-                      mips_isa < 3 ? "lw" : "ld",
-                      "t,o(b)", GP, (int) BFD_RELOC_LO16, mips_frame_reg);
+         macro_build ((char *) NULL, &icnt, &offset_expr, "bal", "p");
+         /* The linker may expand the call to a longer sequence which
+            uses $at, so we must break rather than return.  */
+         break;
        }
+      else
+       abort ();
+
       return;
 
     case M_LB_AB:
@@ -2532,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";
@@ -2550,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";
@@ -2563,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;
@@ -2653,13 +3622,13 @@ macro (ip)
 
       /* A constant expression in PIC code can be handled just as it
         is in non PIC code.  */
-      if (mips_pic == 0
+      if (mips_pic == NO_PIC
          || offset_expr.X_op == O_constant)
        {
          /* If this is a reference to a GP relative symbol, and there
             is no base register, we want
               <op>     $treg,<sym>($gp)        (BFD_RELOC_MIPS_GPREL)
-            Otherwise we want
+            Otherwise, if there is no base register, we want
               lui      $tempreg,<sym>          (BFD_RELOC_HI16_S)
               <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
             If we have a constant, we need two instructions anyhow,
@@ -2676,18 +3645,21 @@ 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
                {
-                 frag_grow (12);
+                 frag_grow (20);
                  macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
                               treg, (int) BFD_RELOC_MIPS_GPREL, GP);
                  p = frag_var (rs_machine_dependent, 8, 0,
                                RELAX_ENCODE (4, 8, 0, 4, 0,
-                                             mips_warn_about_macros),
+                                             (mips_warn_about_macros
+                                              || (used_at && mips_noat))),
                                offset_expr.X_add_symbol, (long) 0,
                                (char *) NULL);
+                 used_at = 0;
                }
              macro_build_lui (p, &icnt, &offset_expr, tempreg);
              if (p != NULL)
@@ -2697,11 +3669,12 @@ 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
                {
-                 frag_grow (20);
+                 frag_grow (28);
                  macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                               mips_isa < 3 ? "addu" : "daddu",
                               "d,v,t", tempreg, breg, GP);
@@ -2724,7 +3697,7 @@ macro (ip)
                           (int) BFD_RELOC_LO16, tempreg);
            }
        }
-      else
+      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)
@@ -2747,7 +3720,7 @@ macro (ip)
          if (expr1.X_add_number < -0x8000
              || expr1.X_add_number >= 0x8000)
            as_bad ("PIC code offset overflow (max 16 signed bits)");
-         frag_grow (12);
+         frag_grow (20);
          macro_build ((char *) NULL, &icnt, &offset_expr,
                       mips_isa < 3 ? "lw" : "ld",
                       "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP);
@@ -2766,6 +3739,96 @@ 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
+              <op>     $treg,<sym>($gp)        (BFD_RELOC_MIPS_GPREL)
+            If there is a base register, we want
+              addu     $tempreg,$breg,$gp
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_MIPS_GPREL)
+            */
+         assert (offset_expr.X_op == O_symbol);
+         if (breg == 0)
+           {
+             macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
+                          treg, (int) BFD_RELOC_MIPS_GPREL, GP);
+             used_at = 0;
+           }
+         else
+           {
+             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                          mips_isa < 3 ? "addu" : "daddu",
+                          "d,v,t", tempreg, breg, GP);
+             macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
+                          treg, (int) BFD_RELOC_MIPS_GPREL, tempreg);
+           }
+       }
+      else
+       abort ();
 
       if (! used_at)
        return;
@@ -2774,11 +3837,22 @@ 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 (mips_pic == 0)
+      if (imm_expr.X_op == O_constant)
+       {
+         load_register (&icnt, AT, &imm_expr, 0);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                      "mtc1", "t,G", AT, treg);
+         break;
+       }
+      else
        {
          assert (offset_expr.X_op == O_symbol
                  && strcmp (segment_name (S_GET_SEGMENT
@@ -2787,28 +3861,36 @@ macro (ip)
                  && offset_expr.X_add_number == 0);
          macro_build ((char *) NULL, &icnt, &offset_expr, "lwc1", "T,o(b)",
                       treg, (int) BFD_RELOC_MIPS_LITERAL, GP);
+         return;
        }
-      else
-       {
-         assert (imm_expr.X_op == O_constant);
-         load_register (&icnt, treg, &imm_expr);
-       }
-      return;
 
     case M_LI_D:
-      /* We know that sym is in the .rdata instruction.  First we get
-        the upper 16 bits of the address.  */
-      if (mips_pic == 0)
+      /* We know that sym is in the .rdata section.  First we get the
+        upper 16 bits of the address.  */
+      if (mips_pic == NO_PIC)
        {
          /* FIXME: This won't work for a 64 bit address.  */
          macro_build_lui ((char *) NULL, &icnt, &offset_expr, AT);
        }
-      else
+      else if (mips_pic == SVR4_PIC)
        {
          macro_build ((char *) NULL, &icnt, &offset_expr,
                       mips_isa < 3 ? "lw" : "ld",
                       "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16, GP);
        }
+      else if (mips_pic == EMBEDDED_PIC)
+       {
+         /* For embedded PIC we pick up the entire address off $gp in
+            a single instruction.  */
+         macro_build ((char *) NULL, &icnt, &offset_expr,
+                      mips_isa < 3 ? "addiu" : "daddiu",
+                      "t,r,j", AT, GP, (int) BFD_RELOC_MIPS_GPREL);
+         offset_expr.X_op = O_constant;
+         offset_expr.X_add_number = 0;
+       }
+      else
+       abort ();
+       
       /* Now we load the register(s).  */
       if (mips_isa >= 3)
        macro_build ((char *) NULL, &icnt, &offset_expr, "ld", "t,o(b)",
@@ -2826,18 +3908,20 @@ macro (ip)
                           treg + 1, (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;
 
     case M_LI_DD:
-      if (mips_pic == 0)
+      assert (offset_expr.X_op == O_symbol
+             && offset_expr.X_add_number == 0);
+      s = segment_name (S_GET_SEGMENT (offset_expr.X_add_symbol));
+      if (strcmp (s, ".lit8") == 0)
        {
-         /* Load a floating point number from the .lit8 section.  */
-         assert (offset_expr.X_op == O_symbol
-                 && strcmp (segment_name (S_GET_SEGMENT
-                                          (offset_expr.X_add_symbol)),
-                            ".lit8") == 0
-                 && offset_expr.X_add_number == 0);
          if (mips_isa >= 2)
            {
              macro_build ((char *) NULL, &icnt, &offset_expr, "ldc1",
@@ -2850,14 +3934,27 @@ macro (ip)
        }
       else
        {
-         /* Load the double from the .rdata section.  */
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      mips_isa < 3 ? "lw" : "ld",
-                      "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16, GP);
+         assert (strcmp (s, RDATA_SECTION_NAME) == 0);
+         if (mips_pic == SVR4_PIC)
+           macro_build ((char *) NULL, &icnt, &offset_expr,
+                        mips_isa < 3 ? "lw" : "ld",
+                        "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16, GP);
+         else
+           {
+             /* FIXME: This won't work for a 64 bit address.  */
+             macro_build_lui ((char *) NULL, &icnt, &offset_expr, AT);
+           }
+             
          if (mips_isa >= 2)
            {
              macro_build ((char *) NULL, &icnt, &offset_expr, "ldc1",
-                          "T,o(b)", treg, (int) BFD_RELOC_LO16, GP);
+                          "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;
@@ -2880,6 +3977,12 @@ macro (ip)
       macro_build ((char *) NULL, &icnt, &offset_expr, "lwc1", "T,o(b)",
                   byte_order == LITTLE_ENDIAN ? treg + 1 : treg,
                   (int) r, breg);
+
+      /* To avoid confusion in tc_gen_reloc, we must ensure that this
+        does not become a variant frag.  */
+      frag_wane (frag_now);
+      frag_new (0);
+
       if (breg != AT)
        return;
       break;
@@ -2954,7 +4057,7 @@ macro (ip)
       if (byte_order == LITTLE_ENDIAN)
        coproc = 0;
 
-      if (mips_pic == 0
+      if (mips_pic == NO_PIC
          || offset_expr.X_op == O_constant)
        {
          /* If this is a reference to a GP relative symbol, we want
@@ -2971,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;
@@ -2982,14 +4086,14 @@ macro (ip)
 
              if (breg == 0)
                {
-                 frag_grow (20);
+                 frag_grow (28);
                  tempreg = GP;
                  off = 0;
                  used_at = 0;
                }
              else
                {
-                 frag_grow (28);
+                 frag_grow (36);
                  macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                               mips_isa < 3 ? "addu" : "daddu",
                               "d,v,t", AT, breg, GP);
@@ -3002,12 +4106,19 @@ macro (ip)
                           coproc ? treg + 1 : treg,
                           (int) BFD_RELOC_MIPS_GPREL, tempreg);
              offset_expr.X_add_number += 4;
+
+             /* Set mips_optimize to 2 to avoid inserting an
+                 undesired nop.  */
+             hold_mips_optimize = mips_optimize;
+             mips_optimize = 2;
              macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
                           coproc ? treg : treg + 1,
                           (int) BFD_RELOC_MIPS_GPREL, tempreg);
+             mips_optimize = hold_mips_optimize;
+
              p = frag_var (rs_machine_dependent, 12 + off, 0,
                            RELAX_ENCODE (8 + off, 12 + off, 0, 4 + off, 1,
-                                         used_at && mips_noat),
+                                         used_at && mips_noat),
                            offset_expr.X_add_symbol, (long) 0,
                            (char *) NULL);
 
@@ -3044,7 +4155,7 @@ macro (ip)
                       coproc ? treg : treg + 1,
                       (int) BFD_RELOC_LO16, AT);
        }         
-      else
+      else if (mips_pic == SVR4_PIC && ! mips_big_got)
        {
          int off;
 
@@ -3071,7 +4182,7 @@ macro (ip)
            off = 0;
          else
            off = 4;
-         frag_grow (16 + off);
+         frag_grow (24 + off);
          macro_build ((char *) NULL, &icnt, &offset_expr,
                       mips_isa < 3 ? "lw" : "ld",
                       "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16, GP);
@@ -3084,14 +4195,155 @@ macro (ip)
                       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;
+
          (void) frag_var (rs_machine_dependent, 0, 0,
                           RELAX_ENCODE (0, 0, -16 - off, -8, 1, 0),
                           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
+              <op>     $treg,<sym>($gp)        (BFD_RELOC_MIPS_GPREL)
+              <op>     $treg+1,<sym>+4($gp)    (BFD_RELOC_MIPS_GPREL)
+            If we have a base register, we use
+              addu     $at,$breg,$gp
+              <op>     $treg,<sym>($at)        (BFD_RELOC_MIPS_GPREL)
+              <op>     $treg+1,<sym>+4($at)    (BFD_RELOC_MIPS_GPREL)
+            */
+         if (breg == 0)
+           {
+             tempreg = GP;
+             used_at = 0;
+           }
+         else
+           {
+             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                          mips_isa < 3 ? "addu" : "daddu",
+                          "d,v,t", AT, breg, GP);
+             tempreg = AT;
+             used_at = 1;
+           }
+
+         macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
+                      coproc ? treg + 1 : treg,
+                      (int) BFD_RELOC_MIPS_GPREL, tempreg);
+         offset_expr.X_add_number += 4;
+         macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
+                      coproc ? treg : treg + 1,
+                      (int) BFD_RELOC_MIPS_GPREL, tempreg);
+       }
+      else
+       abort ();
 
       if (! used_at)
        return;
@@ -3111,6 +4363,50 @@ macro (ip)
       macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg + 1,
                   (int) BFD_RELOC_LO16, breg);
       return;
+#ifdef LOSING_COMPILER
+    default:
+      macro2 (ip);
+      return;
+    }
+  if (mips_noat)
+    as_warn ("Macro used $at after \".set noat\"");
+}
+          
+static void
+macro2 (ip)
+     struct mips_cl_insn *ip;
+{
+  register int treg, sreg, dreg, breg;
+  int tempreg;
+  int mask;
+  int icnt = 0;
+  int used_at;
+  expressionS expr1;
+  const char *s;
+  const char *s2;
+  const char *fmt;
+  int likely = 0;
+  int dbl = 0;
+  int coproc = 0;
+  int lr = 0;
+  int off;
+  offsetT maxnum;
+  bfd_reloc_code_real_type r;
+  char *p;
+          
+  treg = (ip->insn_opcode >> 16) & 0x1f;
+  dreg = (ip->insn_opcode >> 11) & 0x1f;
+  sreg = breg = (ip->insn_opcode >> 21) & 0x1f;
+  mask = ip->insn_mo->mask;
+          
+  expr1.X_op = O_constant;
+  expr1.X_op_symbol = NULL;
+  expr1.X_add_symbol = NULL;
+  expr1.X_add_number = 1;
+          
+  switch (mask)
+    {
+#endif /* LOSING_COMPILER */
 
     case M_DMUL:
       dbl = 1;
@@ -3127,7 +4423,7 @@ macro (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);
@@ -3148,10 +4444,15 @@ macro (ip)
                   dbl ? "dsra32" : "sra",
                   "d,w,<", dreg, dreg, 31);
       macro_build ((char *) NULL, &icnt, NULL, "mfhi", "d", AT);
-      expr1.X_add_number = 8;
-      macro_build ((char *) NULL, &icnt, &expr1, "beq", "s,t,p", dreg, AT);
-      macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
-      macro_build ((char *) NULL, &icnt, NULL, "break", "c", 6);
+      if (mips_trap)
+       macro_build ((char *) NULL, &icnt, NULL, "tne", "s,t", dreg, AT);
+      else
+       {
+         expr1.X_add_number = 8;
+         macro_build ((char *) NULL, &icnt, &expr1, "beq", "s,t,p", dreg, AT);
+         macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
+         macro_build ((char *) NULL, &icnt, NULL, "break", "c", 6);
+       }
       --mips_noreorder;
       macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg);
       break;
@@ -3167,10 +4468,15 @@ macro (ip)
                   "s,t", sreg, treg);
       macro_build ((char *) NULL, &icnt, NULL, "mfhi", "d", AT);
       macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg);
-      expr1.X_add_number = 8;
-      macro_build ((char *) NULL, &icnt, &expr1, "beq", "s,t,p", AT, 0);
-      macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
-      macro_build ((char *) NULL, &icnt, NULL, "break", "c", 6);
+      if (mips_trap)
+       macro_build ((char *) NULL, &icnt, NULL, "tne", "s,t", AT, 0);
+      else
+       {
+         expr1.X_add_number = 8;
+         macro_build ((char *) NULL, &icnt, &expr1, "beq", "s,t,p", AT, 0);
+         macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
+         macro_build ((char *) NULL, &icnt, NULL, "break", "c", 6);
+       }
       --mips_noreorder;
       break;
 
@@ -3184,9 +4490,9 @@ macro (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;
 
@@ -3200,9 +4506,9 @@ macro (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;
 
@@ -3266,7 +4572,7 @@ macro (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;
@@ -3299,7 +4605,7 @@ macro (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);
@@ -3326,7 +4632,7 @@ macro (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;
 
@@ -3347,7 +4653,7 @@ macro (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);
@@ -3360,7 +4666,7 @@ macro (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;
 
@@ -3371,7 +4677,7 @@ macro (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;
@@ -3424,7 +4730,7 @@ macro (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;
@@ -3445,7 +4751,7 @@ macro (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);
@@ -3462,7 +4768,7 @@ macro (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);
@@ -3486,7 +4792,7 @@ macro (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;
 
@@ -3527,103 +4833,197 @@ macro (ip)
     case M_ULHU:
       s = "lbu";
     ulh:
+      if (offset_expr.X_add_number >= 0x7fff)
+       as_bad ("operand overflow");
       /* avoid load delay */
-      offset_expr.X_add_number += 1;
+      if (byte_order == LITTLE_ENDIAN)
+       offset_expr.X_add_number += 1;
       macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
-      offset_expr.X_add_number -= 1;
+      if (byte_order == LITTLE_ENDIAN)
+       offset_expr.X_add_number -= 1;
+      else
+       offset_expr.X_add_number += 1;
       macro_build ((char *) NULL, &icnt, &offset_expr, "lbu", "t,o(b)", AT,
                   (int) BFD_RELOC_LO16, breg);
       macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", treg, treg, 8);
       macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", treg, treg, AT);
       break;
 
+    case M_ULD:
+      s = "ldl";
+      s2 = "ldr";
+      off = 7;
+      goto ulw;
     case M_ULW:
-      /* does this work on a big endian machine? */
-      offset_expr.X_add_number += 3;
-      macro_build ((char *) NULL, &icnt, &offset_expr, "lwl", "t,o(b)", treg,
+      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 += off;
+      macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
-      offset_expr.X_add_number -= 3;
-      macro_build ((char *) NULL, &icnt, &offset_expr, "lwr", "t,o(b)", treg,
+      if (byte_order == LITTLE_ENDIAN)
+       offset_expr.X_add_number -= off;
+      else
+       offset_expr.X_add_number += off;
+      macro_build ((char *) NULL, &icnt, &offset_expr, s2, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
       return;
 
+    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)
-       {
-         expr1.X_add_number = 3;
-         macro_build ((char *) NULL, &icnt, &expr1, "lwl", "t,o(b)", treg,
-                      (int) BFD_RELOC_LO16, AT);
-         expr1.X_add_number = 0;
-         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
-       {
-         macro_build ((char *) NULL, &icnt, &expr1,
-                      mask == M_ULH_A ? "lb" : "lbu", "t,o(b)", treg,
-                      (int) BFD_RELOC_LO16, 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);
-       }
+       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:
+      if (offset_expr.X_add_number >= 0x7fff)
+       as_bad ("operand overflow");
+      if (byte_order == BIG_ENDIAN)
+       offset_expr.X_add_number += 1;
       macro_build ((char *) NULL, &icnt, &offset_expr, "sb", "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
       macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", AT, treg, 8);
-      offset_expr.X_add_number += 1;
+      if (byte_order == BIG_ENDIAN)
+       offset_expr.X_add_number -= 1;
+      else
+       offset_expr.X_add_number += 1;
       macro_build ((char *) NULL, &icnt, &offset_expr, "sb", "t,o(b)", AT,
                   (int) BFD_RELOC_LO16, breg);
       break;
 
+    case M_USD:
+      s = "sdl";
+      s2 = "sdr";
+      off = 7;
+      goto usw;
     case M_USW:
-      offset_expr.X_add_number += 3;
-      macro_build ((char *) NULL, &icnt, &offset_expr, "swl", "t,o(b)", treg,
+      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 += off;
+      macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
-      offset_expr.X_add_number -= 3;
-      macro_build ((char *) NULL, &icnt, &offset_expr, "swr", "t,o(b)", treg,
+      if (byte_order == LITTLE_ENDIAN)
+       offset_expr.X_add_number -= off;
+      else
+       offset_expr.X_add_number += off;
+      macro_build ((char *) NULL, &icnt, &offset_expr, s2, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
       return;
 
-    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)
-       {
-         expr1.X_add_number = 3;
-         macro_build ((char *) NULL, &icnt, &expr1, "swl", "t,o(b)", treg,
-                      (int) BFD_RELOC_LO16, AT);
-         expr1.X_add_number = 0;
-         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
-       {
-         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);
-         expr1.X_add_number = 1;
-         macro_build ((char *) NULL, &icnt, &expr1, "sb", "t,o(b)", treg,
-                      (int) BFD_RELOC_LO16, 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);
-       }
+       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:
@@ -3634,12 +5034,11 @@ macro (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;
@@ -3656,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)
     {
@@ -3668,13 +5067,11 @@ mips_ip (str, ip)
       break;
 
     default:
-      as_warn ("Unknown opcode: `%s'", str);
-      exit (1);
+      as_fatal ("Unknown opcode: `%s'", str);
     }
   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;
@@ -3686,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)
@@ -3701,7 +5106,7 @@ mips_ip (str, ip)
              ++insn;
              continue;
            }
-         insn_error = "ERROR: instruction not supported on this processor";
+         insn_error = "opcode not supported on this processor";
          return;
        }
 
@@ -3787,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;
@@ -3888,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 == ' ')
@@ -3960,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;
@@ -4011,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;
@@ -4028,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;
 
@@ -4069,9 +5499,15 @@ mips_ip (str, ip)
                    f -- immediate value
                    l -- .lit4
 
-                   When generating PIC code, we do not use the .lit8
-                   or .lit4 sections at all, in order to reserve the
-                   entire global offset table.  */
+                   The .lit4 and .lit8 sections are only used if
+                   permitted by the -G argument.
+
+                   When generating embedded PIC code, we use the
+                   .lit8 section but not the .lit4 section (we can do
+                   .lit4 inline easily; we need to put .lit8
+                   somewhere in the data segment, and using .lit8
+                   permits the linker to eventually combine identical
+                   .lit8 entries).  */
 
                f64 = *args == 'F' || *args == 'L';
 
@@ -4091,7 +5527,11 @@ mips_ip (str, ip)
                assert (length == (f64 ? 8 : 4));
 
                if (*args == 'f'
-                   || (mips_pic != 0 && *args == 'l'))
+                   || (*args == 'l'
+                       && (! USE_GLOBAL_POINTER_OPT
+                           || mips_pic == EMBEDDED_PIC
+                           || g_switch_value < 4)
+                       ))
                  {
                    imm_expr.X_op = O_constant;
                    if (byte_order == LITTLE_ENDIAN)
@@ -4119,20 +5559,31 @@ mips_ip (str, ip)
                      {
                      default: /* unused default case avoids warnings.  */
                      case 'L':
-                       newname = (mips_pic == 0 ? ".lit8" : ".rdata");
+                       newname = RDATA_SECTION_NAME;
+                       if (USE_GLOBAL_POINTER_OPT && g_switch_value >= 8)
+                         newname = ".lit8";
                        break;
                      case 'F':
-                       newname = ".rdata";
+                       newname = RDATA_SECTION_NAME;
                        break;
                      case 'l':
-                       assert (mips_pic == 0);
+                       assert (!USE_GLOBAL_POINTER_OPT
+                               || g_switch_value >= 4);
                        newname = ".lit4";
                        break;
                      }
                    new_seg = subseg_new (newname, (subsegT) 0);
-#ifdef OBJ_ELF
-                   bfd_set_section_alignment (stdoutput, new_seg, 4);
-#endif
+                   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);
+                   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");
 
@@ -4158,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')
                    {
@@ -4166,33 +5617,68 @@ 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
                {
-                 if (imm_expr.X_add_number < -0x8000 ||
-                     imm_expr.X_add_number >= 0x8000)
+                 int more;
+                 offsetT max;
+
+                 /* The upper bound should be 0x8000, but
+                    unfortunately the MIPS assembler accepts numbers
+                    from 0x8000 to 0xffff and sign extends them, and
+                    we want to be compatible.  We only permit this
+                    extended range for an instruction which does not
+                    provide any further alternates, since those
+                    alternates may handle other cases.  People should
+                    use the numbers they mean, rather than relying on
+                    a mysterious sign extension.  */
+                 more = (insn + 1 < &mips_opcodes[NUMOPCODES] &&
+                         strcmp (insn->name, insn[1].name) == 0);
+                 if (more)
+                   max = 0x8000;
+                 else
+                   max = 0x10000;
+                 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 (insn + 1 < &mips_opcodes[NUMOPCODES] &&
-                         !strcmp (insn->name, insn[1].name))
+                     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;
@@ -4200,14 +5686,27 @@ mips_ip (str, ip)
 
            case 'o':           /* 16 bit offset */
              c = my_getSmallExpression (&offset_expr, s);
-             /*
-              * If this value won't fit into a 16 bit offset, then
-              * go find a macro that will generate the 32 bit offset
-              * code pattern.
-              */
-             if (offset_expr.X_op != O_constant
-                 || offset_expr.X_add_number >= 0x8000
-                 || offset_expr.X_add_number < -0x8000)
+
+             /* If this value won't fit into a 16 bit offset, then go
+                find a macro that will generate the 32 bit offset
+                code pattern.  As a special hack, we accept the
+                difference of two local symbols as a constant.  This
+                is required to suppose embedded PIC switches, which
+                use an instruction which looks like
+                    lw $4,$L12-$LS12($4)
+                The problem with handling this in a more general
+                fashion is that the macro function doesn't expect to
+                see anything which can be handled in a single
+                constant instruction.  */
+             if (c == 0
+                 && (offset_expr.X_op != O_constant
+                     || offset_expr.X_add_number >= 0x8000
+                     || offset_expr.X_add_number < -0x8000)
+                 && (mips_pic != EMBEDDED_PIC
+                     || offset_expr.X_op != O_subtract
+                     || now_seg != text_section
+                     || (S_GET_SEGMENT (offset_expr.X_op_symbol)
+                         != text_section)))
                break;
 
              offset_reloc = BFD_RELOC_LO16;
@@ -4228,9 +5727,9 @@ mips_ip (str, ip)
 
            case 'u':           /* upper 16 bits */
              c = my_getSmallExpression (&imm_expr, s);
-             if (imm_expr.X_op != O_constant
-                 || imm_expr.X_add_number < 0
-                 || imm_expr.X_add_number >= 0x10000)
+             if (imm_expr.X_op == O_constant
+                 && (imm_expr.X_add_number < 0
+                     || imm_expr.X_add_number >= 0x10000))
                as_bad ("lui expression not in range 0..65535");
              imm_reloc = BFD_RELOC_LO16;
              if (c)
@@ -4241,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;
                    }
@@ -4255,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 ();
@@ -4269,7 +5789,7 @@ mips_ip (str, ip)
          s = argsStart;
          continue;
        }
-      insn_error = "ERROR: Illegal operands";
+      insn_error = "illegal operands";
       return;
     }
 }
@@ -4455,143 +5975,419 @@ md_number_to_chars (buf, val, n)
       internalError ();
     }
 }
+\f
+CONST char *md_shortopts = "O::g::G:";
+
+struct option md_longopts[] = {
+#define OPTION_MIPS1 (OPTION_MD_BASE + 1)
+  {"mips0", no_argument, NULL, OPTION_MIPS1},
+  {"mips1", no_argument, NULL, OPTION_MIPS1},
+#define OPTION_MIPS2 (OPTION_MD_BASE + 2)
+  {"mips2", no_argument, NULL, OPTION_MIPS2},
+#define OPTION_MIPS3 (OPTION_MD_BASE + 3)
+  {"mips3", no_argument, NULL, OPTION_MIPS3},
+#define OPTION_MIPS4 (OPTION_MD_BASE + 4)
+  {"mips4", no_argument, NULL, OPTION_MIPS4},
+#define OPTION_MCPU (OPTION_MD_BASE + 5)
+  {"mcpu", required_argument, NULL, OPTION_MCPU},
+#define OPTION_MEMBEDDED_PIC (OPTION_MD_BASE + 6)
+  {"membedded-pic", no_argument, NULL, OPTION_MEMBEDDED_PIC},
+#define OPTION_TRAP (OPTION_MD_BASE + 9)
+  {"trap", no_argument, NULL, OPTION_TRAP},
+  {"no-break", no_argument, NULL, OPTION_TRAP},
+#define OPTION_BREAK (OPTION_MD_BASE + 10)
+  {"break", no_argument, NULL, OPTION_BREAK},
+  {"no-trap", no_argument, NULL, OPTION_BREAK},
+#define OPTION_EB (OPTION_MD_BASE + 11)
+  {"EB", no_argument, NULL, OPTION_EB},
+#define OPTION_EL (OPTION_MD_BASE + 12)
+  {"EL", no_argument, NULL, OPTION_EL},
+#define OPTION_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
+  {"KPIC", no_argument, NULL, OPTION_CALL_SHARED},
+  {"xgot", no_argument, NULL, OPTION_XGOT},
+  {"call_shared", no_argument, NULL, OPTION_CALL_SHARED},
+  {"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}
+};
+size_t md_longopts_size = sizeof(md_longopts);
 
 int
-md_parse_option (argP, cntP, vecP)
-     char **argP;
-     int *cntP;
-     char ***vecP;
+md_parse_option (c, arg)
+     int c;
+     char *arg;
 {
-  /* Accept -nocpp but ignore it. */
-  if (strcmp (*argP, "nocpp") == 0)
+  switch (c)
     {
-      *argP += 5;
-      return 1;
-    }
+    case OPTION_TRAP:
+      mips_trap = 1;
+      break;
 
-  if (strcmp (*argP, "EL") == 0
-      || strcmp (*argP, "EB") == 0)
-    {
-      /* FIXME: This breaks -L -EL.  */
-      flagseen['L'] = 0;
-      *argP = "";
-      return 1;
-    }
+    case OPTION_BREAK:
+      mips_trap = 0;
+      break;
 
-  if (**argP == 'O')
-    {
-      if ((*argP)[1] == '0')
+    case OPTION_EB:
+      target_big_endian = 1;
+      break;
+
+    case OPTION_EL:
+      target_big_endian = 0;
+      break;
+
+    case 'O':
+      if (arg && arg[1] == '0')
        mips_optimize = 1;
       else
        mips_optimize = 2;
-      return 1;
-    }
+      break;
 
-  if (**argP == 'g')
-    {
-      if ((*argP)[1] == '\0' || (*argP)[1] == '2')
+    case 'g':
+      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;
-      return 1;
-    }
+      break;
 
-  if (strncmp (*argP, "mips", 4) == 0)
-    {
-      mips_isa = atol (*argP + 4);
-      if (mips_isa == 0)
-       mips_isa = 1;
-      else if (mips_isa < 1 || mips_isa > 3)
-       {
-         as_bad ("-mips%d not supported", mips_isa);
-         mips_isa = 1;
-       }
-      *argP = "";
-      return 1;
-    }
+    case OPTION_MIPS1:
+      mips_isa = 1;
+      if (mips_cpu == -1)
+       mips_cpu = 3000;
+      break;
 
-  if (strncmp (*argP, "mcpu=", 5) == 0)
-    {
-      char *p;
+    case OPTION_MIPS2:
+      mips_isa = 2;
+      if (mips_cpu == -1)
+       mips_cpu = 6000;
+      break;
 
-      /* Identify the processor type */
-      p = *argP + 5;
-      if (strcmp (p, "default") == 0
-         || strcmp (p, "DEFAULT") == 0)
-       mips_isa = -1;
-      else
-       {
-         if (*p == 'r' || *p == 'R')
-           p++;
+    case OPTION_MIPS3:
+      mips_isa = 3;
+      if (mips_cpu == -1)
+       mips_cpu = 4000;
+      break;
 
-         mips_isa = -1;
-         switch (*p)
-           {
-           case '2':
-             if (strcmp (p, "2000") == 0
-                 || strcmp (p, "2k") == 0
-                 || strcmp (p, "2K") == 0)
-               mips_isa = 1;
-             break;
+    case OPTION_MIPS4:
+      mips_isa = 4;
+      if (mips_cpu == -1)
+       mips_cpu = 8000;
+      break;
 
-           case '3':
-             if (strcmp (p, "3000") == 0
-                 || strcmp (p, "3k") == 0
-                 || strcmp (p, "3K") == 0)
-               mips_isa = 1;
-             break;
+    case OPTION_MCPU:
+      {
+       char *p;
+
+       /* Identify the processor type */
+       p = arg;
+       if (strcmp (p, "default") == 0
+           || strcmp (p, "DEFAULT") == 0)
+         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++;
+             }
 
-           case '4':
-             if (strcmp (p, "4000") == 0
-                 || strcmp (p, "4k") == 0
-                 || strcmp (p, "4K") == 0)
-               mips_isa = 3;
-             break;
+           if (*p == 'r' || *p == 'R')
+             p++;
 
-           case '6':
-             if (strcmp (p, "6000") == 0
-                 || strcmp (p, "6k") == 0
-                 || strcmp (p, "6K") == 0)
-               mips_isa = 2;
-             break;
-           }
+           mips_cpu = -1;
+           switch (*p)
+             {
+             case '1':
+               if (strcmp (p, "10000") == 0
+                   || strcmp (p, "10k") == 0
+                   || strcmp (p, "10K") == 0)
+                 mips_cpu = 10000;
+               break;
 
-         if (mips_isa == -1)
-           {
-             as_bad ("bad value (%s) for -mcpu= switch", *argP + 5);
-             mips_isa = 1;
-           }
+             case '2':
+               if (strcmp (p, "2000") == 0
+                   || strcmp (p, "2k") == 0
+                   || strcmp (p, "2K") == 0)
+                 mips_cpu = 2000;
+               break;
+
+             case '3':
+               if (strcmp (p, "3000") == 0
+                   || strcmp (p, "3k") == 0
+                   || strcmp (p, "3K") == 0)
+                 mips_cpu = 3000;
+               break;
+
+             case '4':
+               if (strcmp (p, "4000") == 0
+                   || strcmp (p, "4k") == 0
+                   || strcmp (p, "4K") == 0)
+                 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_cpu = 4650;
+                   if (mips_4650 < 0)
+                     mips_4650 = 1;
+                 }
+               else if (strcmp (p, "4010") == 0)
+                 {
+                   mips_cpu = 4010;
+                   if (mips_4010 < 0)
+                     mips_4010 = 1;
+                 }
+               break;
+
+             case '6':
+               if (strcmp (p, "6000") == 0
+                   || strcmp (p, "6k") == 0
+                   || strcmp (p, "6K") == 0)
+                 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_cpu = 4600;
+               break;
+             }
+
+           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;
+             }
+         }
+      }
+      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;
+      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;
+      break;
 
-      *argP = "";
-      return 1;
-    }
+      /* 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)
+       {
+         as_bad ("-G may not be used with SVR4 PIC code");
+         return 0;
+       }
+      g_switch_value = 0;
+      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;
 
+      /* 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;
 
-#ifdef GPOPT
-  if (**argP == 'G')
-    {
-      if ((*argP)[1] != '\0')
-       g_switch_value = atoi (*argP + 1);
-      else if (*cntP)
+    case 'G':
+      if (! USE_GLOBAL_POINTER_OPT)
        {
-         **vecP = (char *) NULL;
-         (*cntP)--;
-         (*vecP)++;
-         g_switch_value = atoi (**vecP);
+         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;
        }
       else
-       as_warn ("Number expected after -G");
-      *argP = "";
-      return 1;
+       g_switch_value = atoi (arg);
+      g_switch_seen = 1;
+      break;
+
+      /* 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;
     }
-#endif
 
-  return 1;                    /* pretend you parsed the character */
+  return 1;
+}
+
+void
+md_show_usage (stream)
+     FILE *stream;
+{
+  fprintf(stream, "\
+MIPS options:\n\
+-membedded-pic         generate embedded position independent code\n\
+-EB                    generate big endian output\n\
+-EL                    generate little endian output\n\
+-g, -g2                        do not remove uneeded NOPs or swap branches\n\
+-G NUM                 allow referencing objects up to NUM bytes\n\
+                       implicitly with the gp register [default 8]\n");
+  fprintf(stream, "\
+-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\
+--break, --no-trap     break exception on div by 0 and mult overflow\n");
+#ifdef OBJ_ELF
+  fprintf(stream, "\
+-KPIC, -call_shared    generate SVR4 position independent code\n\
+-non_shared            do not generate position independent code\n\
+-xgot                  assume a 32 bit GOT\n\
+-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;
 {
+  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;
+    }
+
   /* return the address of the delay slot */
   return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
 }
@@ -4608,23 +6404,133 @@ 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
+   relocation to represent the difference of two symbols in the .text
+   section (switch tables use a difference of this sort).  See
+   include/coff/mips.h for details.  This macro checks whether this
+   fixup requires the special reloc.  */
+#define SWITCH_TABLE(fixp) \
+  ((fixp)->fx_r_type == BFD_RELOC_32 \
+   && (fixp)->fx_addsy != NULL \
+   && (fixp)->fx_subsy != NULL \
+   && S_GET_SEGMENT ((fixp)->fx_addsy) == text_section \
+   && S_GET_SEGMENT ((fixp)->fx_subsy) == text_section)
+
+/* When generating embedded PIC code we must keep all PC relative
+   relocations, in case the linker has to relax a call.  We also need
+   to keep relocations for switch table entries.  */
+
+/*ARGSUSED*/
+int
+mips_force_relocation (fixp)
+     fixS *fixp;
+{
+  return (mips_pic == EMBEDDED_PIC
+         && (fixp->fx_pcrel
+             || SWITCH_TABLE (fixp)
+             || fixp->fx_r_type == BFD_RELOC_PCREL_HI16_S
+             || fixp->fx_r_type == BFD_RELOC_PCREL_LO16));
 }
 
+/* Apply a fixup to the object file.  */
+
 int
 md_apply_fix (fixP, valueP)
      fixS *fixP;
@@ -4633,25 +6539,100 @@ 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 */
 
+  if (fixP->fx_addsy == NULL && ! fixP->fx_pcrel)
+    fixP->fx_done = 1;
+
   switch (fixP->fx_r_type)
     {
-    case BFD_RELOC_32:
     case BFD_RELOC_MIPS_JMP:
     case BFD_RELOC_HI16:
     case BFD_RELOC_HI16_S:
-    case BFD_RELOC_LO16:
     case BFD_RELOC_MIPS_GPREL:
     case BFD_RELOC_MIPS_LITERAL:
     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");
       /* Nothing needed to do. The value comes from the reloc entry */
-      return 1;
+      break;
+
+    case BFD_RELOC_PCREL_HI16_S:
+      /* The addend for this is tricky if it is internal, so we just
+        do everything here rather than in bfd_perform_relocation.  */
+      if ((fixP->fx_addsy->bsym->flags & BSF_SECTION_SYM) == 0)
+       {
+         /* For an external symbol adjust by the address to make it
+            pcrel_offset.  We use the address of the RELLO reloc
+            which follows this one.  */
+         value += (fixP->fx_next->fx_frag->fr_address
+                   + fixP->fx_next->fx_where);
+       }
+      if (value & 0x8000)
+       value += 0x10000;
+      value >>= 16;
+      buf = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
+      if (byte_order == BIG_ENDIAN)
+       buf += 2;
+      md_number_to_chars (buf, value, 2);
+      break;
+
+    case BFD_RELOC_PCREL_LO16:
+      /* The addend for this is tricky if it is internal, so we just
+        do everything here rather than in bfd_perform_relocation.  */
+      if ((fixP->fx_addsy->bsym->flags & BSF_SECTION_SYM) == 0)
+       value += fixP->fx_frag->fr_address + fixP->fx_where;
+      buf = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
+      if (byte_order == BIG_ENDIAN)
+       buf += 2;
+      md_number_to_chars (buf, value, 2);
+      break;
+
+    case BFD_RELOC_32:
+      /* If we are deleting this reloc entry, we must fill in the
+        value now.  This can happen if we have a .word which is not
+        resolved when it appears but is later defined.  We also need
+        to fill in the value if this is an embedded PIC switch table
+        entry.  */
+      if (fixP->fx_done
+         || (mips_pic == EMBEDDED_PIC && SWITCH_TABLE (fixP)))
+       md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+                           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.  */
+      if (fixP->fx_done)
+       {
+         if (value < -0x8000 || value > 0x7fff)
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         "relocation overflow");
+         buf = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
+         if (byte_order == BIG_ENDIAN)
+           buf += 2;
+         md_number_to_chars (buf, value, 2);
+       }
+      break;
 
     case BFD_RELOC_16_PCREL_S2:
       /*
@@ -4660,10 +6641,9 @@ md_apply_fix (fixP, valueP)
        * the current segment).
        */
       if (value & 0x3)
-       as_warn ("Branch to odd address (%lx)", value);
+       as_warn_where (fixP->fx_file, fixP->fx_line,
+                      "Branch to odd address (%lx)", value);
       value >>= 2;
-      if ((value & ~0xFFFF) && (value & ~0xFFFF) != (-1 & ~0xFFFF))
-       as_bad ("Relocation overflow");
 
       /* update old instruction data */
       buf = (unsigned char *) (fixP->fx_where + fixP->fx_frag->fr_literal);
@@ -4681,13 +6661,53 @@ md_apply_fix (fixP, valueP)
          internalError ();
          return 0;
        }
-      insn |= value & 0xFFFF;
+
+      if (value >= -0x8000 && value < 0x8000)
+       insn |= value & 0xffff;
+      else
+       {
+         /* The branch offset is too large.  If this is an
+             unconditional branch, and we are not generating PIC code,
+             we can convert it to an absolute jump instruction.  */
+         if (mips_pic == NO_PIC
+             && fixP->fx_done
+             && fixP->fx_frag->fr_address >= text_section->vma
+             && (fixP->fx_frag->fr_address
+                 < text_section->vma + text_section->_raw_size)
+             && ((insn & 0xffff0000) == 0x10000000      /* beq $0,$0 */
+                 || (insn & 0xffff0000) == 0x04010000   /* bgez $0 */
+                 || (insn & 0xffff0000) == 0x04110000)) /* bgezal $0 */
+           {
+             if ((insn & 0xffff0000) == 0x04110000)     /* bgezal $0 */
+               insn = 0x0c000000;      /* jal */
+             else
+               insn = 0x08000000;      /* j */
+             fixP->fx_r_type = BFD_RELOC_MIPS_JMP;
+             fixP->fx_done = 0;
+             fixP->fx_addsy = section_symbol (text_section);
+             fixP->fx_addnumber = (value << 2) + md_pcrel_from (fixP);
+           }
+         else
+           {
+             /* FIXME.  It would be possible in principle to handle
+                 conditional branches which overflow.  They could be
+                 transformed into a branch around a jump.  This would
+                 require setting up variant frags for each different
+                 branch type.  The native MIPS assembler attempts to
+                 handle these cases, but it appears to do it
+                 incorrectly.  */
+             as_bad_where (fixP->fx_file, fixP->fx_line,
+                           "Relocation overflow");
+           }
+       }
+
       md_number_to_chars ((char *) buf, (valueT) insn, 4);
       break;
 
     default:
       internalError ();
     }
+
   return 1;
 }
 
@@ -4797,19 +6817,19 @@ get_symbol ()
    also automatically adjusts any preceding label.  */
 
 static void
-mips_align (to, fill)
+mips_align (to, fill, label)
      int to;
      int fill;
+     symbolS *label;
 {
   mips_emit_delays ();
   frag_align (to, fill);
   record_alignment (now_seg, to);
-  if (insn_label != NULL)
+  if (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 ());
-      insn_label = NULL;
+      assert (S_GET_SEGMENT (label) == now_seg);
+      label->sy_frag = frag_now;
+      S_SET_VALUE (label, (valueT) frag_now_fix ());
     }
 }
 
@@ -4854,7 +6874,7 @@ s_align (x)
   if (temp)
     {
       auto_align = 1;
-      mips_align (temp, (int) temp_fill);
+      mips_align (temp, (int) temp_fill, insn_label);
     }
   else
     {
@@ -4864,25 +6884,25 @@ 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
+     pseudo-ops to use .sdata.  */
+  if (mips_pic == EMBEDDED_PIC
+      && (sec == 'd' || sec == 'r'))
+    sec = 's';
 
   mips_emit_delays ();
   switch (sec)
@@ -4899,104 +6919,143 @@ s_change_sec (sec)
       break;
 
     case 'r':
-#ifdef OBJ_ECOFF
-      subseg_new (".rdata", (subsegT) get_absolute_expression ());
-      demand_empty_rest_of_line ();
-      break;
-#else /* ! defined (OBJ_ECOFF) */
-#ifdef OBJ_ELF
-      seg = subseg_new (".rodata", (subsegT) get_absolute_expression ());
-      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 ();
-      break;
-#else /* ! defined (OBJ_ELF) */
-      s_data (0);
+      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;
-#endif /* ! defined (OBJ_ELF) */
-#endif /* ! defined (OBJ_ECOFF) */
 
     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;
 }
 
+void
+mips_enable_auto_align ()
+{
+  auto_align = 1;
+}
+
 static void
 s_cons (log_size)
      int log_size;
 {
+  symbolS *label;
+
+  label = insn_label;
   mips_emit_delays ();
   if (log_size > 0 && auto_align)
-    mips_align (log_size, 0);
+    mips_align (log_size, 0, label);
   insn_label = NULL;
   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_VALUE (symbolP, size);
-  S_SET_EXTERNAL (symbolP);
-
-#ifdef ECOFF_DEBUGGING
-  /* ECOFF needs to distinguish a .comm symbol from a .extern symbol,
-     so we use an additional ECOFF specific field.  */
-  symbolP->ecoff_undefined = 1;
-#endif
-}
-
 static void
 s_float_cons (type)
      int type;
 {
+  symbolS *label;
+
+  label = insn_label;
+
   mips_emit_delays ();
 
   if (auto_align)
     if (type == 'd')
-      mips_align (3, 0);
+      mips_align (3, 0, label);
     else
-      mips_align (2, 0);
+      mips_align (2, 0, label);
 
   insn_label = NULL;
 
   float_cons (type);
 }
 
+/* Handle .globl.  We need to override it because on Irix 5 you are
+   permitted to say
+       .globl foo .text
+   where foo is an undefined symbol, to mean that foo should be
+   considered to be the address of a function.  */
+
+static void
+s_mips_globl (x)
+     int 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;
+      asection *sec;
+
+      secname = input_line_pointer;
+      c = get_symbol_end ();
+      sec = bfd_get_section_by_name (stdoutput, secname);
+      if (sec == NULL)
+       as_bad ("%s: no such section", secname);
+      *input_line_pointer = c;
+
+      if (sec != NULL && (sec->flags & SEC_CODE) != 0)
+       flag = BSF_FUNCTION;
+    }
+
+  symbolP->bsym->flags |= flag;
+
+  S_SET_EXTERNAL (symbolP);
+  demand_empty_rest_of_line ();
+}
+
 static void
 s_option (x)
      int x;
@@ -5013,9 +7072,23 @@ s_option (x)
     }
   else if (strncmp (opt, "pic", 3) == 0)
     {
-      mips_pic = atoi (opt + 3);
-      /* Supposedly no other values are used.  */
-      assert (mips_pic == 0 || mips_pic == 2);
+      int i;
+
+      i = atoi (opt + 3);
+      if (i == 0)
+       mips_pic = NO_PIC;
+      else if (i == 2)
+       mips_pic = SVR4_PIC;
+      else
+       as_bad (".option pic%d not supported", i);
+
+      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);
+       }
     }
   else
     as_warn ("Unrecognized option \"%s\"", opt);
@@ -5084,6 +7157,20 @@ s_mipsset (x)
     {
       mips_nobopt = 1;
     }
+  else if (strncmp (name, "mips", 4) == 0)
+    {
+      int isa;
+
+      /* Permit the user to change the ISA on the fly.  Needless to
+        say, misuse can cause serious problems.  */
+      isa = atoi (name + 4);
+      if (isa == 0)
+       mips_isa = file_mips_isa;
+      else if (isa < 1 || isa > 4)
+       as_bad ("unknown ISA level");
+      else
+       mips_isa = isa;
+    }
   else
     {
       as_warn ("Tried to set unrecognized symbol: %s\n", name);
@@ -5092,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.  */
 
@@ -5111,7 +7186,14 @@ static void
 s_abicalls (ignore)
      int ignore;
 {
-  mips_pic = 2;
+  mips_pic = SVR4_PIC;
+  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 ();
 }
 
@@ -5132,8 +7214,8 @@ s_cpload (ignore)
   expressionS ex;
   int icnt = 0;
 
-  /* If we are not generating PIC code, .cpload is ignored.  */
-  if (mips_pic == 0)
+  /* If we are not generating SVR4 PIC code, .cpload is ignored.  */
+  if (mips_pic != SVR4_PIC)
     {
       s_ignore (0);
       return;
@@ -5148,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);
@@ -5169,8 +7254,8 @@ s_cprestore (ignore)
   expressionS ex;
   int icnt = 0;
 
-  /* If we are not generating PIC code, .cprestore is ignored.  */
-  if (mips_pic == 0)
+  /* If we are not generating SVR4 PIC code, .cprestore is ignored.  */
+  if (mips_pic != SVR4_PIC)
     {
       s_ignore (0);
       return;
@@ -5197,19 +7282,21 @@ static void
 s_gpword (ignore)
      int ignore;
 {
+  symbolS *label;
   expressionS ex;
   char *p;
 
   /* When not generating PIC code, this is treated as .word.  */
-  if (mips_pic == 0)
+  if (mips_pic != SVR4_PIC)
     {
       s_cons (2);
       return;
     }
 
+  label = insn_label;
   mips_emit_delays ();
   if (auto_align)
-    mips_align (2, 0);
+    mips_align (2, 0, label);
   insn_label = NULL;
 
   expression (&ex);
@@ -5239,7 +7326,7 @@ s_cpadd (ignore)
   int reg;
 
   /* This is ignored when not generating SVR4 PIC code.  */
-  if (mips_pic == 0)
+  if (mips_pic != SVR4_PIC)
     {
       s_ignore (0);
       return;
@@ -5308,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 == 0)
+  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
@@ -5345,26 +7440,49 @@ 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)
-              && 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)
     {
       asection *symsec = fragp->fr_symbol->bsym->section;
 
@@ -5373,6 +7491,8 @@ md_estimate_size_before_relax (fragp, segtype)
                && symsec != &bfd_abs_section
                && ! bfd_is_com_section (symsec));
     }
+  else
+    abort ();
 
   if (change)
     {
@@ -5404,38 +7524,89 @@ 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;
 
   reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
   reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
-  if (fixp->fx_pcrel == 0)
+
+  if (mips_pic == EMBEDDED_PIC
+      && SWITCH_TABLE (fixp))
+    {
+      /* For a switch table entry we use a special reloc.  The addend
+        is actually the difference between the reloc address and the
+        subtrahend.  */
+      reloc->addend = reloc->address - S_GET_VALUE (fixp->fx_subsy);
+      if (OUTPUT_FLAVOR != bfd_target_ecoff_flavour)
+       as_fatal ("Double check fx_r_type in tc-mips.c:tc_gen_reloc");
+      fixp->fx_r_type = BFD_RELOC_GPREL32;
+    }
+  else if (fixp->fx_r_type == BFD_RELOC_PCREL_LO16)
+    {
+      /* We use a special addend for an internal RELLO reloc.  */
+      if (fixp->fx_addsy->bsym->flags & BSF_SECTION_SYM)
+       reloc->addend = reloc->address - S_GET_VALUE (fixp->fx_subsy);
+      else
+       reloc->addend = fixp->fx_addnumber + reloc->address;
+    }
+  else if (fixp->fx_r_type == BFD_RELOC_PCREL_HI16_S)
+    {
+      assert (fixp->fx_next != NULL
+             && fixp->fx_next->fx_r_type == BFD_RELOC_PCREL_LO16);
+      /* We use a special addend for an internal RELHI reloc.  The
+        reloc is relative to the RELLO; adjust the addend
+        accordingly.  */
+      if (fixp->fx_addsy->bsym->flags & BSF_SECTION_SYM)
+       reloc->addend = (fixp->fx_next->fx_frag->fr_address
+                        + fixp->fx_next->fx_where
+                        - S_GET_VALUE (fixp->fx_subsy));
+      else
+       reloc->addend = (fixp->fx_addnumber
+                        + fixp->fx_next->fx_frag->fr_address
+                        + fixp->fx_next->fx_where);
+    }
+  else if (fixp->fx_pcrel == 0)
     reloc->addend = fixp->fx_addnumber;
   else
-#ifdef OBJ_ELF
-    reloc->addend = 0;
-#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
      reloc and generate a new one.  */
   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;
        }
@@ -5462,27 +7633,77 @@ tc_gen_reloc (section, fixp)
          reloc3->address += 4;
        }
 
-      if (mips_pic == 0)
+      if (mips_pic == NO_PIC)
        {
          assert (fixp->fx_r_type == BFD_RELOC_MIPS_GPREL);
          fixp->fx_r_type = BFD_RELOC_HI16_S;
        }
-      else
+      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));
+       }
     }
 
-  reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+  /* 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.  */
+  if (OUTPUT_FLAVOR == bfd_target_ecoff_flavour
+      && code == BFD_RELOC_16_PCREL_S2
+      && mips_pic != EMBEDDED_PIC)
+    reloc->howto = NULL;
+  else
+    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;
     }
 
@@ -5523,40 +7744,84 @@ 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.  */
   if (mips_any_noreorder)
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_NOREORDER;
-  if (mips_pic != 0)
+  if (mips_pic != NO_PIC)
     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,
@@ -5626,8 +7891,6 @@ md_obj_end ()
     as_warn ("missing `.end' at end of assembly");
 }
 
-extern char hex_value[];
-
 static long
 get_number ()
 {
@@ -5649,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;
        }
@@ -5889,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);
@@ -5898,5 +8161,3 @@ s_loc (x)
   symbolP->sy_segment = now_seg;
 }
 #endif
-
-#endif /* ! defined (ECOFF_DEBUGGING) */