* config/tc-mips.c: Check ECOFF_DEBUGGING rather than
authorIan Lance Taylor <ian@airs.com>
Thu, 18 Nov 1993 18:56:31 +0000 (18:56 +0000)
committerIan Lance Taylor <ian@airs.com>
Thu, 18 Nov 1993 18:56:31 +0000 (18:56 +0000)
OBJ_ECOFF in many cases.
(mips_any_noreorder): New variable.
(mips_cprestore_offset): Initialize to -1.
(mips_frame_reg): New variable.
(RELAX_ENCODE, RELAX_OLD, RELAX_NEW, RELAX_RELOC1,
RELAX_RELOC2, RELAX_RELOC3, RELAX_WARN): New macros.
(md_pseudo_table): Handle "gpword" and "cpadd".
(md_begin): Initialize ok to false.  If OBJ_ELF, set alignment
of text, data and bss sections to 4.  Set alignment of
.reginfo section to 2.  If ECOFF_DEBUGGING, create .mdebug
section.
(ALIGN_ERR, ALIGN_ERR2): Removed unused and useless alignment
check.
(append_insn, macro_build, macro_build_lui): Take place
argument.  Changed all callers.
(append_insn): If appending a nop, don't emit one.
(macro_build): Changed assertion for 'i', 'j', 'o' case.
(gp_reference): Removed.
(load_address): New function.
(macro): If mips_noreorder is used, set mips_any_noreorder.
Extensive changes to handle GP and PIC symbols differently.
Build both possible code choices using a variant frag, and
make a final decision at the end of assembly when all
information is known.  Added PIC support for all symbol
references.
(mips_ip): Don't permit anything but a number after $ for a
coprocessor register.  Don't use .lit4 or .lit8 sections when
generating PIC code.  If OBJ_ELF, set alignment of .lit4 or
.lit8 section to 4.
(md_apply_fix): Accept and ignore GOT16 and GPREL32 relocs.
(s_change_sec): Set alignment of ELF .rodata or .sdata section
to 4.
(s_mipsset): If .set noreorder, set mips_any_noreorder.
(s_cpload): Ignore .cpload if not generating PIC code.  Warn
if .cpload is not in noreorder section.
(s_cprestore): Ignore .cprestore if not generating PIC code.
(s_gpword, s_cpadd): New functions.
(tc_get_register): Added frame argument; if true, set
mips_frame_reg to return value.  Changed all callers.
(md_estimate_size_before_relax): Don't error out, but instead
determine how much a frag should grow.
(tc_gen_reloc): Return multiple relocs if appropriate, as
determined by md_estimate_size_before_relax.
(md_convert_frag): New function.
(mips_elf_final_processing): Set ELF header flags based on
mips_any_noreorder and mips_pic.
* config/tc-mips.h (RELOC_EXPANSION_POSSIBLE): Define.
(MAX_RELOC_EXPANSION): Define to be 3.
(md_relax_frag): Define to be 0.
(md_convert_frag): Don't define.
(tc_get_register): Changed declaration.

gas/config/tc-mips.c

index 0e7cc3d085a62720eeb97835bcc8e88a2e1156a6..a826256a9832a5373c5a32ae8ea3d4c792ea2bb6 100644 (file)
@@ -75,12 +75,12 @@ unsigned long mips_cprmask[4];
 static int mips_isa = -1;
 
 /* MIPS PIC level.  0 is normal, non-PIC code.  2 means to generate
-   SVR4 ABI PIC calls.  FIXME: What does 1 mean?  I'm using 2 because
-   Irix 5 cc outputs .option pic2.  */
+   SVR4 ABI PIC calls.  1 doesn't mean anything.  */
 static int mips_pic;
 
 static int mips_warn_about_macros;
 static int mips_noreorder;
+static int mips_any_noreorder;
 static int mips_nomove;
 static int mips_noat;
 static int mips_nobopt;
@@ -138,7 +138,11 @@ static symbolS *insn_label;
    offset in the stack frame from which to restore the $gp register.
    This is set by the .cprestore pseudo-op, and saved in this
    variable.  */
-static offsetT mips_cprestore_offset;
+static offsetT mips_cprestore_offset = -1;
+
+/* This is the register which holds the stack frame, as set by the
+   .frame pseudo-op.  This is needed to implement .cprestore.  */
+static int mips_frame_reg = SP;
 
 /* To output NOP instructions correctly, we need to keep information
    about the previous two instructions.  */
@@ -181,6 +185,82 @@ static int prev_insn_unreordered;
    noreorder.  */
 static int prev_prev_insn_unreordered;
 \f
+/* Since the MIPS does not have multiple forms of PC relative
+   instructions, we do not have to do relaxing as is done on other
+   platforms.  However, we do have to handle GP relative addressing
+   correctly, which turns out to be a similar problem.
+
+   Every macro that refers to a symbol can occur in (at least) two
+   forms, one with GP relative addressing and one without.  For
+   example, loading a global variable into a register generally uses
+   an macroinstruction like this:
+     lw $4,i
+   If i can be addressed off the GP register (this is true if it is in
+   the .sbss or .sdata section, or if it is known to be smaller than
+   the -G argument) this will generate the following instruction:
+     lw $4,i($gp)
+   This instruction will use a GPREL reloc.  If i can not be addressed
+   off the GP register, the following instruction sequence will be used:
+     lui $at,i
+     lw $4,i($at)
+   In this case the first instruction will have a HI16 reloc, and the
+   second reloc will have a LO16 reloc.  Both relocs will be against
+   the symbol i.
+
+   The issue here is that we may not know whether i is GP addressable
+   until after we see the instruction that uses it.  Therefore, we
+   want to be able to choose the final instruction sequence only at
+   the end of the assembly.  This is similar to the way other
+   platforms choose the form 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.
+
+   We handle these issues by actually generating both possible
+   instruction sequences.  The longer one is put in a frag_var with
+   type rs_machine_dependent.  We encode what to do with the frag in
+   the subtype field.  We encode (1) the number of existing bytes to
+   replace, (2) the number of new bytes to use, (3) the offset from
+   the start of the existing bytes to the first reloc we must generate
+   (that is, the offset is applied from the start of the existing
+   bytes after they are replaced by the new bytes, if any), (4) the
+   offset from the start of the existing bytes to the second reloc,
+   (5) whether a third reloc is needed (the third reloc is always four
+   bytes after the second reloc), and (6) whether to warn if this
+   variant is used (this is sometimes needed if .set nomacro or .set
+   noat is in effect).  All these numbers are reasonably small.
+
+   Generating two instruction sequences must be handled carefully to
+   ensure that delay slots are handled correctly.  Fortunately, 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.
+
+   These variant frags are then handled in functions called by the
+   machine independent code.  md_estimate_size_before_relax returns
+   the final size of the frag.  md_convert_frag sets up the final form
+   of the frag.  tc_gen_reloc adjust the first reloc and adds a second
+   one if needed.  */
+#define RELAX_ENCODE(old, new, reloc1, reloc2, reloc3, warn) \
+  ((relax_substateT) \
+   (((old) << 24) \
+    | ((new) << 16) \
+    | (((reloc1) + 64) << 9) \
+    | (((reloc2) + 64) << 2) \
+    | ((reloc3) ? (1 << 1) : 0) \
+    | ((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_RELOC3(i) (((i) >> 1) & 1)
+#define RELAX_WARN(i) ((i) & 1)
+\f
 /* Prototypes for static functions.  */
 
 #ifdef __STDC__
@@ -192,22 +272,22 @@ static int prev_prev_insn_unreordered;
 
 static int insn_uses_reg PARAMS ((struct mips_cl_insn *ip,
                                  unsigned int reg, int fpr));
-static void append_insn PARAMS ((struct mips_cl_insn * ip,
+static void append_insn PARAMS ((char *place,
+                                struct mips_cl_insn * ip,
                                 expressionS * p,
                                 bfd_reloc_code_real_type r));
 static void mips_no_prev_insn PARAMS ((void));
 static void mips_emit_delays PARAMS ((void));
-static int gp_reference PARAMS ((expressionS * ep));
-static void macro_build PARAMS ((int *counter, expressionS * ep,
+static void macro_build PARAMS ((char *place, int *counter, expressionS * ep,
                                 const char *name, const char *fmt,
                                 ...));
-static void macro_build_lui PARAMS ((int *counter, expressionS * ep,
-                                    int regnum));
+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 *counter, int reg, expressionS * ep));
+static void load_address PARAMS ((int *counter, int reg, expressionS *ep));
 static void macro PARAMS ((struct mips_cl_insn * ip));
 static void mips_ip PARAMS ((char *str, struct mips_cl_insn * ip));
 static int my_getSmallExpression PARAMS ((expressionS * ep, char *str));
@@ -227,7 +307,9 @@ 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));
-#ifndef OBJ_ECOFF
+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));
@@ -270,6 +352,8 @@ const pseudo_typeS md_pseudo_table[] =
   { "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.  */
@@ -293,10 +377,9 @@ const pseudo_typeS md_pseudo_table[] =
   {"text", s_change_sec, 't'},
   {"word", s_cons, 2},
 
-#ifndef OBJ_ECOFF
+#ifndef ECOFF_DEBUGGING
  /* These pseudo-ops should be defined by the object file format.
-     However, ECOFF is the only format which currently defines them,
-     so we have versions here for a.out.  */
+    However, a.out doesn't support them, so we have versions here.  */
   {"aent", s_ent, 1},
   {"bgnb", s_ignore, 0},
   {"end", s_mipsend, 0},
@@ -337,7 +420,7 @@ extern int target_big_endian;
 void
 md_begin ()
 {
-  boolean ok;
+  boolean ok = false;
   register const char *retval = NULL;
   register unsigned int i = 0;
 
@@ -420,28 +503,43 @@ md_begin ()
 #endif
 
 #ifdef OBJ_ELF
-  /* Create a .reginfo section for register masks.  */
+  /* 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 regsec;
+    segT sec;
 
     seg = now_seg;
     subseg = now_subseg;
-    regsec = subseg_new (".reginfo", (subsegT) 0);
+    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.  */
-    bfd_set_section_flags (stdoutput, regsec,
-                          SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA);
+    (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);
+#endif
+
     subseg_set (seg, subseg);
   }
 #endif /* OBJ_ELF */
 
-#ifndef OBJ_ECOFF
+#ifndef ECOFF_DEBUGGING
   md_obj_begin ();
 #endif
 }
@@ -449,7 +547,7 @@ md_begin ()
 void
 md_end ()
 {
-#ifndef OBJ_ECOFF
+#ifndef ECOFF_DEBUGGING
   md_obj_end ();
 #endif
 }
@@ -476,11 +574,11 @@ md_assemble (str)
   else
     {
       if (imm_expr.X_op != O_absent)
-       append_insn (&insn, &imm_expr, imm_reloc);
+       append_insn ((char *) NULL, &insn, &imm_expr, imm_reloc);
       else if (offset_expr.X_op != O_absent)
-       append_insn (&insn, &offset_expr, offset_reloc);
+       append_insn ((char *) NULL, &insn, &offset_expr, offset_reloc);
       else
-       append_insn (&insn, NULL, BFD_RELOC_UNUSED);
+       append_insn ((char *) NULL, &insn, NULL, BFD_RELOC_UNUSED);
     }
 }
 
@@ -528,15 +626,14 @@ insn_uses_reg (ip, reg, fpr)
   return 0;
 }
 
-#define ALIGN_ERR "Attempt to assemble instruction onto non word boundary."
-#define ALIGN_ERR2 "GAS doesn't do implicit alignment; use .align directive."
+/* 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.  */
 
-/*
- *                                append insn
- * Output an instruction.
- */
 static void
-append_insn (ip, address_expr, reloc_type)
+append_insn (place, ip, address_expr, reloc_type)
+     char *place;
      struct mips_cl_insn *ip;
      expressionS *address_expr;
      bfd_reloc_code_real_type reloc_type;
@@ -549,7 +646,7 @@ append_insn (ip, address_expr, reloc_type)
   prev_pinfo = prev_insn.insn_mo->pinfo;
   pinfo = ip->insn_mo->pinfo;
 
-  if (! mips_noreorder)
+  if (place == NULL && ! mips_noreorder)
     {
       /* If the previous insn required any delay slots, see if we need
         to insert a NOP or two.  There are eight kinds of possible
@@ -692,6 +789,13 @@ append_insn (ip, address_expr, reloc_type)
                  && (pinfo & INSN_WRITE_HI))))
        ++nops;
 
+      /* If we are being given a nop instruction, don't bother with
+        one of the nops we would otherwise output.  This will only
+        happen when a nop instruction is used with mips_optimize set
+        to 0.  */
+      if (nops > 0 && ip->insn_opcode == 0)
+       --nops;
+
       /* Now emit the right number of NOP instructions.  */
       if (nops > 0)
        {
@@ -707,16 +811,10 @@ append_insn (ip, address_expr, reloc_type)
        }
     }
   
-  f = frag_more (4);
-#if 0
-  /* This is testing the address of the frag, not the alignment of
-     the instruction in the current section.  */
-  if ((int) f & 3)
-    {
-      as_bad (ALIGN_ERR);
-      as_bad (ALIGN_ERR2);
-    }
-#endif
+  if (place == NULL)
+    f = frag_more (4);
+  else
+    f = place;
   fixp = NULL;
   if (address_expr != NULL)
     {
@@ -744,10 +842,13 @@ append_insn (ip, address_expr, reloc_type)
        {
          assert (reloc_type != BFD_RELOC_UNUSED);
        need_reloc:
-         fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 4,
-                             address_expr,
-                             reloc_type == BFD_RELOC_16_PCREL_S2,
-                             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);
        }
     }
 
@@ -775,7 +876,7 @@ append_insn (ip, address_expr, reloc_type)
   /* Never set the bit for $0, which is always zero.  */
   mips_gprmask &=~ 1 << 0;
 
-  if (! mips_noreorder)
+  if (place == NULL && ! mips_noreorder)
     {
       /* Filling the branch delay slot is more complex.  We try to
         switch the branch with the previous instruction, which we can
@@ -1051,51 +1152,6 @@ mips_emit_delays ()
     }
 }
 
-/* Return 1 if an expression can be accessed via the GP register.  */
-
-static int
-gp_reference (ep)
-     expressionS *ep;
-{
-#ifdef GPOPT
-  symbolS *sym;
-  const char *symname;
-  const char *segname;
-
-  sym = ep->X_add_symbol;
-  if (sym == (symbolS *) NULL
-      || ep->X_op_symbol != (symbolS *) NULL)
-    return 0;
-
-  /* Certain symbols can not be referenced off the GP, although it
-     appears as though they can.  */
-  symname = S_GET_NAME (sym);
-  if (symname != (const char *) NULL
-      && (strcmp (symname, "eprol") == 0
-         || strcmp (symname, "etext") == 0
-         || strcmp (symname, "_gp") == 0
-         || strcmp (symname, "edata") == 0
-         || strcmp (symname, "_fbss") == 0
-         || strcmp (symname, "_fdata") == 0
-         || strcmp (symname, "_ftext") == 0
-         || strcmp (symname, "end") == 0
-         || strcmp (symname, "_gp_disp") == 0))
-    return 0;
-  if (! S_IS_DEFINED (sym)
-      && S_GET_VALUE (sym) != 0
-      && S_GET_VALUE (sym) <= g_switch_value)
-    return 1;
-  segname = segment_name (S_GET_SEGMENT (ep->X_add_symbol));
-  return (strcmp (segname, ".sdata") == 0
-         || strcmp (segname, ".sbss") == 0
-         || strcmp (segname, ".lit8") == 0
-         || strcmp (segname, ".lit4") == 0);
-#else /* ! defined (GPOPT) */
-  /* We are not optimizing for the GP register.  */
-  return 0;
-#endif /* ! defined (GPOPT) */  
-}
-
 /* Build an instruction created by a macro expansion.  This is passed
    a pointer to the count of instructions created so far, an
    expression, the name of the instruction to build, an operand format
@@ -1103,14 +1159,16 @@ gp_reference (ep)
 
 #ifndef NO_STDARG
 static void
-macro_build (int *counter,
+macro_build (char *place,
+            int *counter,
             expressionS * ep,
             const char *name,
             const char *fmt,
             ...)
 #else /* ! defined (NO_STDARG) */
 static void
-macro_build (counter, ep, name, fmt, va_alist)
+macro_build (place, counter, ep, name, fmt, va_alist)
+     char *place;
      int *counter;
      expressionS *ep;
      const char *name;
@@ -1133,10 +1191,11 @@ macro_build (counter, ep, name, fmt, va_alist)
    * print a warning if needed. We need to pass ip as a parameter
    * to generate a better warning message here...
    */
-  if (mips_warn_about_macros && *counter == 1)
+  if (mips_warn_about_macros && place == NULL && *counter == 1)
     as_warn ("Macro instruction expanded into multiple instructions");
 
-  *counter += 1;               /* bump instruction counter */
+  if (place == NULL)
+    *counter += 1;             /* bump instruction counter */
 
   r = BFD_RELOC_UNUSED;
   insn.insn_mo = (struct mips_opcode *) hash_find (op_hash, name);
@@ -1210,11 +1269,12 @@ macro_build (counter, ep, name, fmt, va_alist)
        case 'i':
        case 'j':
        case 'o':
-         assert (ep != NULL);
          r = (bfd_reloc_code_real_type) va_arg (args, int);
-         assert (ep->X_op == O_constant || ! gp_reference (ep)
-                 ? r == BFD_RELOC_LO16 || r == BFD_RELOC_MIPS_CALL16
-                 : r == BFD_RELOC_MIPS_GPREL || r == BFD_RELOC_MIPS_LITERAL);
+         assert (r == BFD_RELOC_MIPS_GPREL
+                 || r == BFD_RELOC_MIPS_LITERAL
+                 || r == BFD_RELOC_LO16
+                 || r == BFD_RELOC_MIPS_GOT16
+                 || r== BFD_RELOC_MIPS_CALL16);
          continue;
 
        case 'u':
@@ -1254,14 +1314,15 @@ macro_build (counter, ep, name, fmt, va_alist)
   va_end (args);
   assert (r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
 
-  append_insn (&insn, ep, r);
+  append_insn (place, &insn, ep, r);
 }
 
 /*
  * Generate a "lui" instruction.
  */
 static void
-macro_build_lui (counter, ep, regnum)
+macro_build_lui (place, counter, ep, regnum)
+     char *place;
      int *counter;
      expressionS *ep;
      int regnum;
@@ -1272,7 +1333,13 @@ macro_build_lui (counter, ep, regnum)
   CONST char *name = "lui";
   CONST char *fmt = "t,u";
 
-  high_expr = *ep;
+  if (place == NULL)
+    high_expr = *ep;
+  else
+    {
+      high_expr.X_op = O_constant;
+      high_expr.X_add_number = 0;
+    }
 
   if (high_expr.X_op == O_constant)
     {
@@ -1284,31 +1351,38 @@ macro_build_lui (counter, ep, regnum)
       r = BFD_RELOC_UNUSED;
     }
   else
-    r = BFD_RELOC_HI16_S;
+    {
+      assert (ep->X_op == O_symbol);
+      /* _gp_disp is a special case, used from s_cpload.  */
+      assert (mips_pic == 0
+             || strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0);
+      r = BFD_RELOC_HI16_S;
+    }
 
   /*
    * If the macro is about to expand into a second instruction,
    * print a warning if needed. We need to pass ip as a parameter
    * to generate a better warning message here...
    */
-  if (mips_warn_about_macros && *counter == 1)
+  if (mips_warn_about_macros && place == NULL && *counter == 1)
     as_warn ("Macro instruction expanded into multiple instructions");
 
-  *counter += 1;               /* bump instruction counter */
+  if (place == NULL)
+    *counter += 1;             /* bump instruction counter */
 
   insn.insn_mo = (struct mips_opcode *) hash_find (op_hash, name);
   assert (insn.insn_mo);
   assert (strcmp (name, insn.insn_mo->name) == 0);
   assert (strcmp (fmt, insn.insn_mo->args) == 0);
 
-  insn.insn_opcode = insn.insn_mo->match | (regnum << 16);
+  insn.insn_opcode = insn.insn_mo->match | (regnum << OP_SH_RT);
   if (r == BFD_RELOC_UNUSED)
     {
       insn.insn_opcode |= high_expr.X_add_number;
-      append_insn (&insn, NULL, r);
+      append_insn (place, &insn, NULL, r);
     }
   else
-    append_insn (&insn, &high_expr, r);
+    append_insn (place, &insn, &high_expr, r);
 }
 
 /*                     set_at()
@@ -1322,13 +1396,13 @@ set_at (counter, reg, unsignedp)
      int unsignedp;
 {
   if (imm_expr.X_add_number >= -0x8000 && imm_expr.X_add_number < 0x8000)
-    macro_build (counter, &imm_expr,
+    macro_build ((char *) NULL, counter, &imm_expr,
                 unsignedp ? "sltiu" : "slti",
                 "t,r,j", AT, reg, (int) BFD_RELOC_LO16);
   else
     {
       load_register (counter, AT, &imm_expr);
-      macro_build (counter, NULL,
+      macro_build ((char *) NULL, counter, NULL,
                   unsignedp ? "sltu" : "slt",
                   "d,v,t", AT, reg, AT);
     }
@@ -1357,24 +1431,25 @@ load_register (counter, reg, ep)
 {
   assert (ep->X_op == O_constant);
   if (ep->X_add_number >= -0x8000 && ep->X_add_number < 0x8000)
-    macro_build (counter, ep,
+    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 (counter, ep, "ori", "t,r,i", reg, 0, (int) BFD_RELOC_LO16);
+    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))
     {
-      macro_build (counter, ep, "lui", "t,u", reg);
+      macro_build ((char *) NULL, counter, ep, "lui", "t,u", reg);
       if ((ep->X_add_number & 0xffff) != 0)
-       macro_build (counter, ep, "ori", "t,r,i", reg, reg,
+       macro_build ((char *) NULL, counter, ep, "ori", "t,r,i", reg, reg,
                     (int) BFD_RELOC_LO16);
     }
   else if (mips_isa < 3)
     {
       as_bad ("Number larger than 32 bits");
-      macro_build (counter, ep, "addiu", "t,r,j", reg, 0,
+      macro_build ((char *) NULL, counter, ep, "addiu", "t,r,j", reg, 0,
                   (int) BFD_RELOC_LO16);
     }
   else
@@ -1392,24 +1467,109 @@ load_register (counter, reg, ep)
       lo32 = *ep;
       lo32.X_add_number &= 0xffffffff;
       if ((lo32.X_add_number & 0xffff0000) == 0)
-       macro_build (counter, NULL, "dsll32", "d,w,<", reg, reg, 0);
+       macro_build ((char *) NULL, counter, NULL, "dsll32", "d,w,<", reg,
+                    reg, 0);
       else
        {
          expressionS mid16;
 
-         macro_build (counter, NULL, "dsll", "d,w,<", reg, reg, 16);
+         macro_build ((char *) NULL, counter, NULL, "dsll", "d,w,<", reg,
+                      reg, 16);
          mid16 = lo32;
          mid16.X_add_number >>= 16;
-         macro_build (counter, &mid16, "ori", "t,r,i", reg, reg,
-                      (int) BFD_RELOC_LO16);
-         macro_build (counter, NULL, "dsll", "d,w,<", reg, reg, 16);
+         macro_build ((char *) NULL, counter, &mid16, "ori", "t,r,i", reg,
+                      reg, (int) BFD_RELOC_LO16);
+         macro_build ((char *) NULL, counter, NULL, "dsll", "d,w,<", reg,
+                      reg, 16);
        }
       if ((lo32.X_add_number & 0xffff) != 0)
-       macro_build (counter, &lo32, "ori", "t,r,i", reg, reg,
+       macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i", reg, reg,
                     (int) BFD_RELOC_LO16);
     }
 }
 
+/* Load an address into a register.  */
+
+static void
+load_address (counter, reg, ep)
+     int *counter;
+     int reg;
+     expressionS *ep;
+{
+  char *p;
+
+  if (ep->X_op != O_constant
+      && ep->X_op != O_symbol)
+    {
+      as_bad ("expression too complex");
+      ep->X_op = O_constant;
+    }
+
+  if (ep->X_op == O_constant)
+    load_register (counter, reg, ep);
+  else if (mips_pic == 0)
+    {
+      /* 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)
+          addiu        $reg,$reg,<sym>         (BFD_RELOC_LO16)
+        If we have an addend, we always use the latter form.  */
+      if (ep->X_add_number != 0)
+       p = NULL;
+      else
+       {
+         frag_grow (12);
+         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),
+                       ep->X_add_symbol, (long) 0, (char *) NULL);
+       }
+      macro_build_lui (p, counter, ep, reg);
+      if (p != NULL)
+       p += 4;
+      macro_build (p, counter, ep,
+                  mips_isa < 3 ? "addiu" : "daddiu",
+                  "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+    }
+  else
+    {
+      expressionS ex;
+
+      /* If this is a reference to an external symbol, we want
+          lw           $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT16)
+        Otherwise 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 afterward.  */
+      ex.X_add_number = ep->X_add_number;
+      ep->X_add_number = 0;
+      frag_grow (12);
+      macro_build ((char *) NULL, counter, ep,
+                  mips_isa < 3 ? "lw" : "ld",
+                  "t,o(b)", reg, (int) BFD_RELOC_MIPS_GOT16, GP);
+      macro_build ((char *) NULL, counter, (expressionS *) NULL, "nop", "");
+      p = frag_var (rs_machine_dependent, 4, 0,
+                   RELAX_ENCODE (0, 4, -8, 0, 0, mips_warn_about_macros),
+                   ep->X_add_symbol, (long) 0, (char *) NULL);
+      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 (p, 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
@@ -1446,6 +1606,7 @@ macro (ip)
   int coproc = 0;
   offsetT maxnum;
   bfd_reloc_code_real_type r;
+  char *p;
 
   treg = (ip->insn_opcode >> 16) & 0x1f;
   dreg = (ip->insn_opcode >> 11) & 0x1f;
@@ -1469,14 +1630,15 @@ macro (ip)
 
       mips_emit_delays ();
       ++mips_noreorder;
+      mips_any_noreorder = 1;
 
       expr1.X_add_number = 8;
-      macro_build (&icnt, &expr1, "bgez", "s,p", sreg);
+      macro_build ((char *) NULL, &icnt, &expr1, "bgez", "s,p", sreg);
       if (dreg == sreg)
-       macro_build (&icnt, NULL, "nop", "", 0);
+       macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
       else
-       macro_build (&icnt, NULL, "move", "d,s", dreg, sreg, 0);
-      macro_build (&icnt, NULL,
+       macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", dreg, sreg, 0);
+      macro_build ((char *) NULL, &icnt, NULL,
                   dbl ? "dsub" : "sub",
                   "d,v,t", dreg, 0, sreg);
 
@@ -1503,12 +1665,12 @@ macro (ip)
     do_addi:
       if (imm_expr.X_add_number >= -0x8000 && imm_expr.X_add_number < 0x8000)
        {
-         macro_build (&icnt, &imm_expr, s, "t,r,j", treg, sreg,
+         macro_build ((char *) NULL, &icnt, &imm_expr, s, "t,r,j", treg, sreg,
                       (int) BFD_RELOC_LO16);
          return;
        }
       load_register (&icnt, AT, &imm_expr);
-      macro_build (&icnt, NULL, s2, "d,v,t", treg, sreg, AT);
+      macro_build ((char *) NULL, &icnt, NULL, s2, "d,v,t", treg, sreg, AT);
       break;
 
     case M_AND_I:
@@ -1530,19 +1692,20 @@ macro (ip)
       if (imm_expr.X_add_number >= 0 && imm_expr.X_add_number < 0x10000)
        {
          if (mask != M_NOR_I)
-           macro_build (&icnt, &imm_expr, s, "t,r,i", treg, sreg,
-                        (int) BFD_RELOC_LO16);
+           macro_build ((char *) NULL, &icnt, &imm_expr, s, "t,r,i", treg,
+                        sreg, (int) BFD_RELOC_LO16);
          else
            {
-             macro_build (&icnt, &imm_expr, "ori", "t,r,i", treg, sreg,
-                          (int) BFD_RELOC_LO16);
-             macro_build (&icnt, &imm_expr, "nor", "d,v,t", treg, treg, 0);
+             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",
+                          treg, treg, 0);
            }
          return;
        }
 
       load_register (&icnt, AT, &imm_expr);
-      macro_build (&icnt, NULL, s2, "d,v,t", treg, sreg, AT);
+      macro_build ((char *) NULL, &icnt, NULL, s2, "d,v,t", treg, sreg, AT);
       break;
 
     case M_BEQ_I:
@@ -1561,11 +1724,12 @@ macro (ip)
     beq_i:
       if (imm_expr.X_add_number == 0)
        {
-         macro_build (&icnt, &offset_expr, s, "s,t,p", sreg, 0);
+         macro_build ((char *) NULL, &icnt, &offset_expr, s, "s,t,p", sreg,
+                      0);
          return;
        }
       load_register (&icnt, AT, &imm_expr);
-      macro_build (&icnt, &offset_expr, s, "s,t,p", sreg, AT);
+      macro_build ((char *) NULL, &icnt, &offset_expr, s, "s,t,p", sreg, AT);
       break;
 
     case M_BGEL:
@@ -1573,20 +1737,20 @@ macro (ip)
     case M_BGE:
       if (treg == 0)
        {
-         macro_build (&icnt, &offset_expr,
+         macro_build ((char *) NULL, &icnt, &offset_expr,
                       likely ? "bgezl" : "bgez",
                       "s,p", sreg);
          return;
        }
       if (sreg == 0)
        {
-         macro_build (&icnt, &offset_expr,
+         macro_build ((char *) NULL, &icnt, &offset_expr,
                       likely ? "blezl" : "blez",
                       "s,p", treg);
          return;
        }
-      macro_build (&icnt, NULL, "slt", "d,v,t", AT, sreg, treg);
-      macro_build (&icnt, &offset_expr,
+      macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", AT, sreg, treg);
+      macro_build ((char *) NULL, &icnt, &offset_expr,
                   likely ? "beql" : "beq",
                   "s,t,p", AT, 0);
       break;
@@ -1610,12 +1774,13 @@ macro (ip)
          if (! likely)
            {
              as_warn ("Branch %s is always false (nop)", ip->insn_mo->name);
-             macro_build (&icnt, NULL, "nop", "", 0);
+             macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
            }
          else
            {
              as_warn ("Branch likely %s is always false", ip->insn_mo->name);
-             macro_build (&icnt, &offset_expr, "bnel", "s,t,p", 0, 0);
+             macro_build ((char *) NULL, &icnt, &offset_expr, "bnel",
+                          "s,t,p", 0, 0);
            }
          return;
        }
@@ -1627,14 +1792,14 @@ macro (ip)
        likely = 1;
       if (imm_expr.X_add_number == 0)
        {
-         macro_build (&icnt, &offset_expr,
+         macro_build ((char *) NULL, &icnt, &offset_expr,
                       likely ? "bgezl" : "bgez",
                       "s,p", sreg);
          return;
        }
       if (imm_expr.X_add_number == 1)
        {
-         macro_build (&icnt, &offset_expr,
+         macro_build ((char *) NULL, &icnt, &offset_expr,
                       likely ? "bgtzl" : "bgtz",
                       "s,p", sreg);
          return;
@@ -1653,11 +1818,11 @@ macro (ip)
        do_true:
          /* result is always true */
          as_warn ("Branch %s is always true", ip->insn_mo->name);
-         macro_build (&icnt, &offset_expr, "b", "p");
+         macro_build ((char *) NULL, &icnt, &offset_expr, "b", "p");
          return;
        }
       set_at (&icnt, sreg, 0);
-      macro_build (&icnt, &offset_expr,
+      macro_build ((char *) NULL, &icnt, &offset_expr,
                   likely ? "beql" : "beq",
                   "s,t,p", AT, 0);
       break;
@@ -1669,13 +1834,14 @@ macro (ip)
        goto do_true;
       if (sreg == 0)
        {
-         macro_build (&icnt, &offset_expr,
+         macro_build ((char *) NULL, &icnt, &offset_expr,
                       likely ? "beql" : "beq",
                       "s,t,p", 0, treg);
          return;
        }
-      macro_build (&icnt, NULL, "sltu", "d,v,t", AT, sreg, treg);
-      macro_build (&icnt, &offset_expr,
+      macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", AT, sreg,
+                  treg);
+      macro_build ((char *) NULL, &icnt, &offset_expr,
                   likely ? "beql" : "beq",
                   "s,t,p", AT, 0);
       break;
@@ -1695,13 +1861,13 @@ macro (ip)
        goto do_true;
       if (imm_expr.X_add_number == 1)
        {
-         macro_build (&icnt, &offset_expr,
+         macro_build ((char *) NULL, &icnt, &offset_expr,
                       likely ? "bnel" : "bne",
                       "s,t,p", sreg, 0);
          return;
        }
       set_at (&icnt, sreg, 1);
-      macro_build (&icnt, &offset_expr,
+      macro_build ((char *) NULL, &icnt, &offset_expr,
                   likely ? "beql" : "beq",
                   "s,t,p", AT, 0);
       break;
@@ -1711,20 +1877,20 @@ macro (ip)
     case M_BGT:
       if (treg == 0)
        {
-         macro_build (&icnt, &offset_expr,
+         macro_build ((char *) NULL, &icnt, &offset_expr,
                       likely ? "bgtzl" : "bgtz",
                       "s,p", sreg);
          return;
        }
       if (sreg == 0)
        {
-         macro_build (&icnt, &offset_expr,
+         macro_build ((char *) NULL, &icnt, &offset_expr,
                       likely ? "bltzl" : "bltz",
                       "s,p", treg);
          return;
        }
-      macro_build (&icnt, NULL, "slt", "d,v,t", AT, treg, sreg);
-      macro_build (&icnt, &offset_expr,
+      macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", AT, treg, sreg);
+      macro_build ((char *) NULL, &icnt, &offset_expr,
                   likely ? "bnel" : "bne",
                   "s,t,p", AT, 0);
       break;
@@ -1734,15 +1900,16 @@ macro (ip)
     case M_BGTU:
       if (treg == 0)
        {
-         macro_build (&icnt, &offset_expr,
+         macro_build ((char *) NULL, &icnt, &offset_expr,
                       likely ? "bnel" : "bne",
                       "s,t,p", sreg, 0);
          return;
        }
       if (sreg == 0)
        goto do_false;
-      macro_build (&icnt, NULL, "sltu", "d,v,t", AT, treg, sreg);
-      macro_build (&icnt, &offset_expr,
+      macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", AT, treg,
+                  sreg);
+      macro_build ((char *) NULL, &icnt, &offset_expr,
                   likely ? "bnel" : "bne",
                   "s,t,p", AT, 0);
       break;
@@ -1752,20 +1919,20 @@ macro (ip)
     case M_BLE:
       if (treg == 0)
        {
-         macro_build (&icnt, &offset_expr,
+         macro_build ((char *) NULL, &icnt, &offset_expr,
                       likely ? "blezl" : "blez",
                       "s,p", sreg);
          return;
        }
       if (sreg == 0)
        {
-         macro_build (&icnt, &offset_expr,
+         macro_build ((char *) NULL, &icnt, &offset_expr,
                       likely ? "bgezl" : "bgez",
                       "s,p", treg);
          return;
        }
-      macro_build (&icnt, NULL, "slt", "d,v,t", AT, treg, sreg);
-      macro_build (&icnt, &offset_expr,
+      macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", AT, treg, sreg);
+      macro_build ((char *) NULL, &icnt, &offset_expr,
                   likely ? "beql" : "beq",
                   "s,t,p", AT, 0);
       break;
@@ -1791,20 +1958,20 @@ macro (ip)
        likely = 1;
       if (imm_expr.X_add_number == 0)
        {
-         macro_build (&icnt, &offset_expr,
+         macro_build ((char *) NULL, &icnt, &offset_expr,
                       likely ? "bltzl" : "bltz",
                       "s,p", sreg);
          return;
        }
       if (imm_expr.X_add_number == 1)
        {
-         macro_build (&icnt, &offset_expr,
+         macro_build ((char *) NULL, &icnt, &offset_expr,
                       likely ? "blezl" : "blez",
                       "s,p", sreg);
          return;
        }
       set_at (&icnt, sreg, 0);
-      macro_build (&icnt, &offset_expr,
+      macro_build ((char *) NULL, &icnt, &offset_expr,
                   likely ? "bnel" : "bne",
                   "s,t,p", AT, 0);
       break;
@@ -1814,15 +1981,16 @@ macro (ip)
     case M_BLEU:
       if (treg == 0)
        {
-         macro_build (&icnt, &offset_expr,
+         macro_build ((char *) NULL, &icnt, &offset_expr,
                       likely ? "beql" : "beq",
                       "s,t,p", sreg, 0);
          return;
        }
       if (sreg == 0)
        goto do_true;
-      macro_build (&icnt, NULL, "sltu", "d,v,t", AT, treg, sreg);
-      macro_build (&icnt, &offset_expr,
+      macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", AT, treg,
+                  sreg);
+      macro_build ((char *) NULL, &icnt, &offset_expr,
                   likely ? "beql" : "beq",
                   "s,t,p", AT, 0);
       break;
@@ -1842,13 +2010,13 @@ macro (ip)
        goto do_false;
       if (imm_expr.X_add_number == 1)
        {
-         macro_build (&icnt, &offset_expr,
+         macro_build ((char *) NULL, &icnt, &offset_expr,
                       likely ? "beql" : "beq",
                       "s,t,p", sreg, 0);
          return;
        }
       set_at (&icnt, sreg, 1);
-      macro_build (&icnt, &offset_expr,
+      macro_build ((char *) NULL, &icnt, &offset_expr,
                   likely ? "bnel" : "bne",
                   "s,t,p", AT, 0);
       break;
@@ -1858,20 +2026,20 @@ macro (ip)
     case M_BLT:
       if (treg == 0)
        {
-         macro_build (&icnt, &offset_expr,
+         macro_build ((char *) NULL, &icnt, &offset_expr,
                       likely ? "bltzl" : "bltz",
                       "s,p", sreg);
          return;
        }
       if (sreg == 0)
        {
-         macro_build (&icnt, &offset_expr,
+         macro_build ((char *) NULL, &icnt, &offset_expr,
                       likely ? "bgtzl" : "bgtz",
                       "s,p", treg);
          return;
        }
-      macro_build (&icnt, NULL, "slt", "d,v,t", AT, sreg, treg);
-      macro_build (&icnt, &offset_expr,
+      macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", AT, sreg, treg);
+      macro_build ((char *) NULL, &icnt, &offset_expr,
                   likely ? "bnel" : "bne",
                   "s,t,p", AT, 0);
       break;
@@ -1883,13 +2051,14 @@ macro (ip)
        goto do_false;
       if (sreg == 0)
        {
-         macro_build (&icnt, &offset_expr,
+         macro_build ((char *) NULL, &icnt, &offset_expr,
                       likely ? "bnel" : "bne",
                       "s,t,p", 0, treg);
          return;
        }
-      macro_build (&icnt, NULL, "sltu", "d,v,t", AT, sreg, treg);
-      macro_build (&icnt, &offset_expr,
+      macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", AT, sreg,
+                  treg);
+      macro_build ((char *) NULL, &icnt, &offset_expr,
                   likely ? "bnel" : "bne",
                   "s,t,p", AT, 0);
       break;
@@ -1907,43 +2076,45 @@ macro (ip)
       if (treg == 0)
        {
          as_warn ("Divide by zero.");
-         macro_build (&icnt, NULL, "break", "c", 7);
+         macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7);
          return;
        }
 
       mips_emit_delays ();
       ++mips_noreorder;
-      macro_build (&icnt, NULL,
+      mips_any_noreorder = 1;
+      macro_build ((char *) NULL, &icnt, NULL,
                   dbl ? "ddiv" : "div",
                   "z,s,t", sreg, treg);
       expr1.X_add_number = 8;
-      macro_build (&icnt, &expr1, "bne", "s,t,p", treg, 0);
-      macro_build (&icnt, NULL, "nop", "", 0);
-      macro_build (&icnt, NULL, "break", "c", 7);
+      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 (&icnt, &expr1,
+      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;
-      macro_build (&icnt, &expr1, "bne", "s,t,p", treg, AT);
+      macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", treg, AT);
       if (dbl)
        {
          expr1.X_add_number = 1;
-         macro_build (&icnt, &expr1, "daddiu", "t,r,j", AT, 0,
+         macro_build ((char *) NULL, &icnt, &expr1, "daddiu", "t,r,j", AT, 0,
                       (int) BFD_RELOC_LO16);
-         macro_build (&icnt, NULL, "dsll32", "d,w,<", AT, AT, 31);
+         macro_build ((char *) NULL, &icnt, NULL, "dsll32", "d,w,<", AT, AT,
+                      31);
        }
       else
        {
          expr1.X_add_number = 0x80000000;
-         macro_build (&icnt, &expr1, "lui", "t,u", AT);
+         macro_build ((char *) NULL, &icnt, &expr1, "lui", "t,u", AT);
        }
       expr1.X_add_number = 8;
-      macro_build (&icnt, &expr1, "bne", "s,t,p", sreg, AT);
-      macro_build (&icnt, NULL, "nop", "", 0);
-      macro_build (&icnt, NULL, "break", "c", 6);
+      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 (&icnt, NULL, s, "d", dreg);
+      macro_build ((char *) NULL, &icnt, NULL, s, "d", dreg);
       break;
 
     case M_DIV_3I:
@@ -1985,15 +2156,16 @@ macro (ip)
       if (imm_expr.X_add_number == 0)
        {
          as_warn ("Divide by zero.");
-         macro_build (&icnt, NULL, "break", "c", 7);
+         macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7);
          return;
        }
       if (imm_expr.X_add_number == 1)
        {
          if (strcmp (s2, "mflo") == 0)
-           macro_build (&icnt, NULL, "move", "d,s", dreg, sreg);
+           macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", dreg,
+                        sreg);
          else
-           macro_build (&icnt, NULL, "move", "d,s", dreg, 0);
+           macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", dreg, 0);
          return;
        }
       if (imm_expr.X_add_number == -1
@@ -2002,18 +2174,20 @@ macro (ip)
          if (strcmp (s2, "mflo") == 0)
            {
              if (dbl)
-               macro_build (&icnt, NULL, "dneg", "d,w", dreg, sreg);
+               macro_build ((char *) NULL, &icnt, NULL, "dneg", "d,w", dreg,
+                            sreg);
              else
-               macro_build (&icnt, NULL, "neg", "d,w", dreg, sreg);
+               macro_build ((char *) NULL, &icnt, NULL, "neg", "d,w", dreg,
+                            sreg);
            }
          else
-           macro_build (&icnt, NULL, "move", "d,s", dreg, 0);
+           macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", dreg, 0);
          return;
        }
 
       load_register (&icnt, AT, &imm_expr);
-      macro_build (&icnt, NULL, s, "z,s,t", sreg, AT);
-      macro_build (&icnt, NULL, s2, "d", dreg);
+      macro_build ((char *) NULL, &icnt, NULL, s, "z,s,t", sreg, AT);
+      macro_build ((char *) NULL, &icnt, NULL, s2, "d", dreg);
       break;
 
     case M_DIVU_3:
@@ -2034,56 +2208,208 @@ macro (ip)
     do_divu3:
       mips_emit_delays ();
       ++mips_noreorder;
-      macro_build (&icnt, NULL, s, "z,s,t", sreg, treg);
+      mips_any_noreorder = 1;
+      macro_build ((char *) NULL, &icnt, NULL, s, "z,s,t", sreg, treg);
       expr1.X_add_number = 8;
-      macro_build (&icnt, &expr1, "bne", "s,t,p", treg, 0);
-      macro_build (&icnt, NULL, "nop", "", 0);
-      macro_build (&icnt, NULL, "break", "c", 7);
+      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 (&icnt, NULL, s2, "d", dreg);
+      macro_build ((char *) NULL, &icnt, NULL, s2, "d", dreg);
       return;
 
-    case M_LA:
-      if (offset_expr.X_op == O_constant)
+    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.  */
+      if (offset_expr.X_op != O_symbol
+         && offset_expr.X_op != O_constant)
        {
-         load_register (&icnt, treg, &offset_expr);
-         return;
+         as_bad ("expression too complex");
+         offset_expr.X_op = O_constant;
+       }
+
+      if (treg == breg)
+       {
+         tempreg = AT;
+         used_at = 1;
        }
-      if (gp_reference (&offset_expr))
-       macro_build (&icnt, &offset_expr,
-                    mips_isa < 3 ? "addiu" : "daddiu",
-                    "t,r,j", treg, GP, (int) BFD_RELOC_MIPS_GPREL);
       else
        {
-         /* FIXME: This won't work for a 64 bit address.  */
-         macro_build_lui (&icnt, &offset_expr, treg);
-         macro_build (&icnt, &offset_expr,
-                      mips_isa < 3 ? "addiu" : "daddiu",
-                      "t,r,j", treg, treg, (int) BFD_RELOC_LO16);
+         tempreg = treg;
+         used_at = 0;
        }
-      return;
 
-    case M_LA_AB:
-      tempreg = (breg == treg) ? AT : treg;
       if (offset_expr.X_op == O_constant)
        load_register (&icnt, tempreg, &offset_expr);
-      else if (gp_reference (&offset_expr))
-       macro_build (&icnt, &offset_expr,
-                    mips_isa < 3 ? "addiu" : "daddiu",
-                    "t,r,j", tempreg, GP, (int) BFD_RELOC_MIPS_GPREL);
-      else
+      else if (mips_pic == 0)
        {
-         /* FIXME: This won't work for a 64 bit address.  */
-         macro_build_lui (&icnt, &offset_expr, tempreg);
-         macro_build (&icnt, &offset_expr,
+         /* If this is a reference to an GP relative symbol, we want
+              addiu    $tempreg,$gp,<sym>      (BFD_RELOC_MIPS_GPREL)
+            Otherwise we want
+              lui      $tempreg,<sym>          (BFD_RELOC_HI16_S)
+              addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+            If we have a constant, we need two instructions anyhow,
+            so we may as well always use the latter form.  */
+         if (offset_expr.X_add_number != 0)
+           p = NULL;
+         else
+           {
+             frag_grow (12);
+             macro_build ((char *) NULL, &icnt, &offset_expr,
+                          mips_isa < 3 ? "addiu" : "daddiu",
+                          "t,r,j", tempreg, GP, (int) BFD_RELOC_MIPS_GPREL);
+             p = frag_var (rs_machine_dependent, 8, 0,
+                           RELAX_ENCODE (4, 8, 0, 4, 0,
+                                         mips_warn_about_macros),
+                           offset_expr.X_add_symbol, (long) 0,
+                           (char *) NULL);
+           }
+         macro_build_lui (p, &icnt, &offset_expr, tempreg);
+         if (p != NULL)
+           p += 4;
+         macro_build (p, &icnt, &offset_expr,
                       mips_isa < 3 ? "addiu" : "daddiu",
-                      "t,r,j", tempreg, tempreg,
-                      (int) BFD_RELOC_LO16);
+                      "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
+       }
+      else
+       {
+         /* If this is a reference to an external symbol, and there
+            is no constant, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+            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
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              nop
+              addiu    $tempreg,$tempreg,<constant>
+            For a local symbol, we want the same instruction
+            sequence, but we output a BFD_RELOC_LO16 reloc on the
+            addiu instruction.
+
+            If we have a large constant, and this is a reference to
+            an external symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              lui      $at,<hiconstant>
+              addiu    $at,$at,<loconstant>
+              addu     $tempreg,$tempreg,$at
+            For a local symbol, we want the same instruction
+            sequence, but we output a BFD_RELOC_LO16 reloc on the
+            addiu instruction.  */
+         expr1.X_add_number = offset_expr.X_add_number;
+         offset_expr.X_add_number = 0;
+         frag_grow (24);
+         macro_build ((char *) NULL, &icnt, &offset_expr,
+                      mips_isa < 3 ? "lw" : "ld",
+                      "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP);
+         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, 8 - off, 0,
+                           RELAX_ENCODE (0, 8 - off, -4 - off, 4 - off, 0,
+                                         (breg == 0
+                                          ? mips_warn_about_macros
+                                          : 0)),
+                           offset_expr.X_add_symbol, (long) 0,
+                           (char *) NULL);
+             if (breg == 0)
+               {
+                 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 breg == 0, and the next instruction uses
+                $tempreg, then if this variant case is used an extra
+                nop will be generated.  */
+           }
+         else if (expr1.X_add_number >= -0x8000
+                  && expr1.X_add_number < 0x8000)
+           {
+             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);
+             (void) frag_var (rs_machine_dependent, 0, 0,
+                              RELAX_ENCODE (0, 0, -12, -4, 0, 0),
+                              offset_expr.X_add_symbol, (long) 0,
+                              (char *) NULL);
+           }
+         else
+           {
+             int off1;
+
+             /* 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)
+               off1 = 0;
+             else
+               {
+                 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);
+                 breg = 0;
+                 tempreg = treg;
+                 off1 = -8;
+               }
+
+             macro_build_lui ((char *) NULL, &icnt, &expr1, AT);
+             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", tempreg, tempreg, AT);
+             (void) frag_var (rs_machine_dependent, 0, 0,
+                              RELAX_ENCODE (0, 0, -16 + off1, -8, 0, 0),
+                              offset_expr.X_add_symbol, (long) 0,
+                              (char *) NULL);
+             used_at = 1;
+           }
        }
+
       if (breg != 0)
-       macro_build (&icnt, NULL, "addu", "d,v,t", treg, tempreg, breg);
-      if (breg == treg)
-       break;
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                    mips_isa < 3 ? "addu" : "daddu",
+                    "d,v,t", treg, tempreg, breg);
+
+      if (! used_at)
+       return;
+
+      break;
+
+    case M_J_A:
+      /* The j instruction may not be used in PIC code, since it
+        requires an absolute address.  We convert it to a b
+        instruction.  */
+      if (mips_pic == 0)
+       macro_build ((char *) NULL, &icnt, &offset_expr, "j", "a");
+      else
+       macro_build ((char *) NULL, &icnt, &offset_expr, "b", "p");
       return;
 
       /* The jal instructions must be handled as macros because when
@@ -2095,50 +2421,81 @@ macro (ip)
     case M_JAL_2:
       if (mips_pic == 0)
        {
-         macro_build (&icnt, (expressionS *) NULL, "jalr", "d,s",
-                      dreg, sreg);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "jalr",
+                      "d,s", dreg, sreg);
          return;
        }
 
       /* I only know how to handle pic2.  */
       assert (mips_pic == 2);
 
-      if (dreg != PIC_CALL_REG)
+      if (sreg != PIC_CALL_REG)
        as_warn ("MIPS PIC call to register other than $25");
       
-      macro_build (&icnt, (expressionS *) NULL, "jalr", "d,s",
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "jalr", "d,s",
                   dreg, sreg);
-      expr1.X_add_number = mips_cprestore_offset;
-      macro_build (&icnt, &expr1,
-                  mips_isa < 3 ? "lw" : "ld",
-                  "t,o(b)", GP, (int) BFD_RELOC_LO16, SP);
+      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);
+       }
       return;
 
     case M_JAL_A:
       if (mips_pic == 0)
        {
-         macro_build (&icnt, &offset_expr, "jal", "a");
+         macro_build ((char *) NULL, &icnt, &offset_expr, "jal", "a");
          return;
        }
 
       /* I only know how to handle pic2.  */
       assert (mips_pic == 2);
 
-      /* We turn this into
-          lw   $25,%call16($gp)
-          jalr $25
-          lw   $gp,cprestore($sp)
-        The %call16 generates the R_MIPS_CALL16 reloc.  See the MIPS
-        ABI.  The cprestore value is set using the .cprestore
-        pseudo-op.  */
-      macro_build (&icnt, &offset_expr,
+      /* 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 (&icnt, (expressionS *) NULL, "jalr", "s", PIC_CALL_REG);
-      expr1.X_add_number = mips_cprestore_offset;
-      macro_build (&icnt, &expr1,
-                  mips_isa < 3 ? "lw" : "ld",
-                  "t,o(b)", GP, (int) BFD_RELOC_LO16, SP);
+      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 (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);
+       }
       return;
 
     case M_LB_AB:
@@ -2161,7 +2518,6 @@ macro (ip)
       coproc = 1;
       goto ld;
     case M_LWC1_AB:
-    case M_LI_SS:
       s = "lwc1";
       coproc = 1;
       goto ld;
@@ -2277,81 +2633,236 @@ macro (ip)
     ld_st:
       if (mask == M_LWC1_AB
          || mask == M_SWC1_AB
-         || mask == M_LI_SS
          || mask == M_LDC1_AB
-         || mask == M_SDC1_AB)
+         || mask == M_SDC1_AB
+         || mask == M_L_DAB
+         || mask == M_S_DAB)
        fmt = "T,o(b)";
       else if (coproc)
        fmt = "E,o(b)";
       else
        fmt = "t,o(b)";
-      if (gp_reference (&offset_expr))
+
+      if (offset_expr.X_op != O_constant
+         && offset_expr.X_op != O_symbol)
+       {
+         as_bad ("expression too complex");
+         offset_expr.X_op = O_constant;
+       }
+
+      /* A constant expression in PIC code can be handled just as it
+        is in non PIC code.  */
+      if (mips_pic == 0
+         || 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
+              lui      $tempreg,<sym>          (BFD_RELOC_HI16_S)
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+            If we have a constant, we need two instructions anyhow,
+            so we always use the latter form.
+
+            If we have a base register, and this is a reference to a
+            GP relative symbol, we want
+              addu     $tempreg,$breg,$gp
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_MIPS_GPREL)
+            Otherwise we want
+              lui      $tempreg,<sym>          (BFD_RELOC_HI16_S)
+              addu     $tempreg,$tempreg,$breg
+              <op>     $treg,<sym>($tempreg)   (BFD_RELOC_LO16)
+            With a constant we always use the latter case.  */
          if (breg == 0)
            {
-             macro_build (&icnt, &offset_expr, s, fmt, treg,
-                          (int) BFD_RELOC_MIPS_GPREL, GP);
-             return;
+             if (offset_expr.X_add_number != 0)
+               p = NULL;
+             else
+               {
+                 frag_grow (12);
+                 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),
+                               offset_expr.X_add_symbol, (long) 0,
+                               (char *) NULL);
+               }
+             macro_build_lui (p, &icnt, &offset_expr, tempreg);
+             if (p != NULL)
+               p += 4;
+             macro_build (p, &icnt, &offset_expr, s, fmt, treg,
+                          (int) BFD_RELOC_LO16, tempreg);
+           }
+         else
+           {
+             if (offset_expr.X_add_number != 0)
+               p = NULL;
+             else
+               {
+                 frag_grow (20);
+                 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);
+                 p = frag_var (rs_machine_dependent, 12, 0,
+                               RELAX_ENCODE (8, 12, 0, 8, 0, 0),
+                               offset_expr.X_add_symbol, (long) 0,
+                               (char *) NULL);
+               }
+             macro_build_lui (p, &icnt, &offset_expr, tempreg);
+             if (p != NULL)
+               p += 4;
+             macro_build (p, &icnt, (expressionS *) NULL,
+                          mips_isa < 3 ? "addu" : "daddu",
+                          "d,v,t", tempreg, tempreg, breg);
+             if (p != NULL)
+               p += 4;
+             macro_build (p, &icnt, &offset_expr, s, fmt, treg,
+                          (int) BFD_RELOC_LO16, tempreg);
            }
-         macro_build (&icnt, (expressionS *) NULL,
-                      mips_isa < 3 ? "addu" : "daddu",
-                      "d,v,t", tempreg, breg, GP);
-         r = BFD_RELOC_MIPS_GPREL;
        }
       else
        {
-         /* FIXME: This won't work for a 64 bit address.  */
-         macro_build_lui (&icnt, &offset_expr, tempreg);
+         /* If this is a reference to an external symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              nop
+              <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)");
+         frag_grow (12);
+         macro_build ((char *) NULL, &icnt, &offset_expr,
+                      mips_isa < 3 ? "lw" : "ld",
+                      "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
+         p = frag_var (rs_machine_dependent, 4, 0, 
+                       RELAX_ENCODE (0, 4, -8, 0, 0, 0),
+                       offset_expr.X_add_symbol, (long) 0,
+                       (char *) NULL);
+         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 (&icnt, NULL,
+           macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                         mips_isa < 3 ? "addu" : "daddu",
                         "d,v,t", tempreg, tempreg, breg);
-         r = BFD_RELOC_LO16;
+         macro_build ((char *) NULL, &icnt, &expr1, s, fmt, treg,
+                      (int) BFD_RELOC_LO16, tempreg);
        }
-      macro_build (&icnt, &offset_expr, s, fmt, treg, (int) r, tempreg);
-      if (used_at)
-       break;
-      return;
+
+      if (! used_at)
+       return;
+
+      break;
 
     case M_LI:
     case M_LI_S:
       load_register (&icnt, treg, &imm_expr);
       return;
 
+    case M_LI_SS:
+      if (mips_pic == 0)
+       {
+         assert (offset_expr.X_op == O_symbol
+                 && strcmp (segment_name (S_GET_SEGMENT
+                                          (offset_expr.X_add_symbol)),
+                            ".lit4") == 0
+                 && offset_expr.X_add_number == 0);
+         macro_build ((char *) NULL, &icnt, &offset_expr, "lwc1", "T,o(b)",
+                      treg, (int) BFD_RELOC_MIPS_LITERAL, GP);
+       }
+      else
+       {
+         assert (imm_expr.X_op == O_constant);
+         load_register (&icnt, treg, &imm_expr);
+       }
+      return;
+
     case M_LI_D:
-      /*  lui $at,%hi(foo)
-         lw $v0,%lo(foo)($at)
-         lw $v1,%lo(foo+4)($at)
-         .rdata
-        foo:
-         .double 3.133435
-        */
-      /* FIXME: This won't work for a 64 bit address.  */
-      macro_build_lui (&icnt, &offset_expr, AT);
+      /* We know that sym is in the .rdata instruction.  First we get
+        the upper 16 bits of the address.  */
+      if (mips_pic == 0)
+       {
+         /* FIXME: This won't work for a 64 bit address.  */
+         macro_build_lui ((char *) NULL, &icnt, &offset_expr, AT);
+       }
+      else
+       {
+         macro_build ((char *) NULL, &icnt, &offset_expr,
+                      mips_isa < 3 ? "lw" : "ld",
+                      "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16, GP);
+       }
+      /* Now we load the register(s).  */
       if (mips_isa >= 3)
-       macro_build (&icnt, &offset_expr, "ld", "t,o(b)", treg,
-                    (int) BFD_RELOC_LO16, AT);
+       macro_build ((char *) NULL, &icnt, &offset_expr, "ld", "t,o(b)",
+                    treg, (int) BFD_RELOC_LO16, AT);
       else
        {
-         macro_build (&icnt, &offset_expr, "lw", "t,o(b)", treg,
-                      (int) BFD_RELOC_LO16, AT);
-         offset_expr.X_add_number += 4;
-         macro_build (&icnt, &offset_expr, "lw", "t,o(b)", treg + 1,
-                      (int) BFD_RELOC_LO16, AT);
+         macro_build ((char *) NULL, &icnt, &offset_expr, "lw", "t,o(b)",
+                      treg, (int) BFD_RELOC_LO16, AT);
+         if (treg != 31)
+           {
+             /* FIXME: How in the world do we deal with the possible
+                overflow here?  */
+             offset_expr.X_add_number += 4;
+             macro_build ((char *) NULL, &icnt, &offset_expr, "lw", "t,o(b)",
+                          treg + 1, (int) BFD_RELOC_LO16, AT);
+           }
        }
+              
       break;
 
     case M_LI_DD:
-      /* Load a floating point number from the .lit8 section.  */
-      if (mips_isa >= 2)
+      if (mips_pic == 0)
        {
-         macro_build (&icnt, &offset_expr, "ldc1", "T,o(b)", treg,
-                      (int) BFD_RELOC_MIPS_LITERAL, GP);
-         return;
+         /* 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",
+                          "T,o(b)", treg, (int) BFD_RELOC_MIPS_LITERAL, GP);
+             return;
+           }
+         breg = GP;
+         r = BFD_RELOC_MIPS_LITERAL;
+         goto dob;
+       }
+      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);
+         if (mips_isa >= 2)
+           {
+             macro_build ((char *) NULL, &icnt, &offset_expr, "ldc1",
+                          "T,o(b)", treg, (int) BFD_RELOC_LO16, GP);
+             break;
+           }
+         breg = AT;
+         r = BFD_RELOC_LO16;
+         goto dob;
        }
-      breg = GP;
-      r = BFD_RELOC_MIPS_LITERAL;
-      goto dob;
 
     case M_L_DOB:
       /* Even on a big endian machine $fn comes before $fn+1.  We have
@@ -2359,14 +2870,18 @@ macro (ip)
       r = BFD_RELOC_LO16;
     dob:
       assert (mips_isa < 2);
-      macro_build (&icnt, &offset_expr, "lwc1", "T,o(b)",
+      macro_build ((char *) NULL, &icnt, &offset_expr, "lwc1", "T,o(b)",
                   byte_order == LITTLE_ENDIAN ? treg : treg + 1,
                   (int) r, breg);
+      /* FIXME: A possible overflow which I don't know how to deal
+        with.  */
       offset_expr.X_add_number += 4;
-      macro_build (&icnt, &offset_expr, "lwc1", "T,o(b)",
+      macro_build ((char *) NULL, &icnt, &offset_expr, "lwc1", "T,o(b)",
                   byte_order == LITTLE_ENDIAN ? treg + 1 : treg,
                   (int) r, breg);
-      return;
+      if (breg != AT)
+       return;
+      break;
 
     case M_L_DAB:
       /*
@@ -2380,128 +2895,220 @@ macro (ip)
        * But, the resulting address is the same after relocation so why
        * generate the extra instruction?
        */
-      if (gp_reference (&offset_expr))
+      if (mips_isa >= 2)
        {
-         if (breg == 0)
-           tempreg = GP;
-         else
-           {
-             macro_build (&icnt, &offset_expr,
-                          mips_isa < 3 ? "addu" : "daddu",
-                          "d,v,t", AT, breg, GP);
-             tempreg = AT;
-           }
-         r = BFD_RELOC_MIPS_GPREL;
-       }
-      else
-       {
-         /* FIXME: This won't work for a 64 bit address.  */
-         macro_build_lui (&icnt, &offset_expr, AT);
-         if (breg != 0)
-           macro_build (&icnt, NULL,
-                        mips_isa < 3 ? "addu" : "daddu",
-                        "d,v,t", AT, AT, breg);
-         tempreg = AT;
-         r = BFD_RELOC_LO16;
+         s = "ldc1";
+         goto ld;
        }
+
+      s = "lwc1";
+      fmt = "T,o(b)";
+      coproc = 1;
+      goto ldd_std;
+
+    case M_S_DAB:
       if (mips_isa >= 2)
-       macro_build (&icnt, &offset_expr, "ldc1", "T,o(b)", treg,
-                    (int) r, tempreg);
-      else
        {
-         /* Even on a big endian machine $fn comes before $fn+1.  We
-            have to adjust when loading from memory.  */
-         macro_build (&icnt, &offset_expr, "lwc1", "T,o(b)",
-                      byte_order == LITTLE_ENDIAN ? treg : treg + 1,
-                      (int) r, tempreg);
-         offset_expr.X_add_number += 4;
-         macro_build (&icnt, &offset_expr, "lwc1", "T,o(b)",
-                      byte_order == LITTLE_ENDIAN ? treg + 1 : treg,
-                      (int) r, tempreg);
+         s = "sdc1";
+         goto st;
        }
-      if (tempreg == AT)
-       break;
-      return;
 
-    case M_LD_OB:
-      s = "lw";
-      goto sd_ob;
-    case M_SD_OB:
-      s = "sw";
-    sd_ob:
-      assert (mips_isa < 3);
-      macro_build (&icnt, &offset_expr, s, "t,o(b)", treg,
-                  (int) BFD_RELOC_LO16, breg);
-      offset_expr.X_add_number += 4;
-      macro_build (&icnt, &offset_expr, s, "t,o(b)", treg + 1,
-                  (int) BFD_RELOC_LO16, breg);
-      return;
+      s = "swc1";
+      fmt = "T,o(b)";
+      coproc = 1;
+      goto ldd_std;
 
     case M_LD_AB:
-      s = "lw";
-      s2 = "ld";
-      if (breg == treg)
+      if (mips_isa >= 3)
        {
-         tempreg = AT;
-         used_at = 1;
+         s = "ld";
+         goto ld;
        }
-      else
+
+      s = "lw";
+      fmt = "t,o(b)";
+      goto ldd_std;
+
+    case M_SD_AB:
+      if (mips_isa >= 3)
        {
-         tempreg = treg;
-         used_at = 0;
+         s = "sd";
+         goto st;
        }
-      goto sd_ab;
-    case M_SD_AB:
+
       s = "sw";
-      s2 = "sd";
-      tempreg = AT;
-      used_at = 1;
-    sd_ab:
-      if (gp_reference (&offset_expr))
+      fmt = "t,o(b)";
+
+    ldd_std:
+      if (offset_expr.X_op != O_symbol
+         && offset_expr.X_op != O_constant)
        {
-         if (breg == 0)
+         as_bad ("expression too complex");
+         offset_expr.X_op = O_constant;
+       }
+
+      /* Even on a big endian machine $fn comes before $fn+1.  We have
+        to adjust when loading from memory.  We set coproc if we must
+        load $fn+1 first.  */
+      if (byte_order == LITTLE_ENDIAN)
+       coproc = 0;
+
+      if (mips_pic == 0
+         || offset_expr.X_op == O_constant)
+       {
+         /* If this is a reference to a GP relative symbol, we want
+              <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 this
+              addu     $at,$breg,$gp
+              <op>     $treg,<sym>($at)        (BFD_RELOC_MIPS_GPREL)
+              <op>     $treg+1,<sym>+4($at)    (BFD_RELOC_MIPS_GPREL)
+            If this is not a GP relative symbol, we want
+              lui      $at,<sym>               (BFD_RELOC_HI16_S)
+              <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 after the
+            lui instruction.  If there is a constant, we always use
+            the last case.  */
+         if (offset_expr.X_add_number != 0)
            {
-             tempreg = GP;
-             used_at = 0;
+             p = NULL;
+             used_at = 1;
            }
          else
-           macro_build (&icnt, (expressionS *) NULL,
-                        mips_isa < 3 ? "addu" : "daddu",
-                        "d,v,t", tempreg, breg, GP);
-         r = BFD_RELOC_MIPS_GPREL;
-       }
+           {
+             int off;
+
+             if (breg == 0)
+               {
+                 frag_grow (20);
+                 tempreg = GP;
+                 off = 0;
+                 used_at = 0;
+               }
+             else
+               {
+                 frag_grow (28);
+                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                              mips_isa < 3 ? "addu" : "daddu",
+                              "d,v,t", AT, breg, GP);
+                 tempreg = AT;
+                 off = 4;
+                 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);
+             p = frag_var (rs_machine_dependent, 12 + off, 0,
+                           RELAX_ENCODE (8 + off, 12 + off, 0, 4 + off, 1,
+                                         ! used_at && mips_noat),
+                           offset_expr.X_add_symbol, (long) 0,
+                           (char *) NULL);
+             offset_expr.X_add_number -= 4;
+             offset_expr.X_op = O_constant;
+           }
+         macro_build_lui (p, &icnt, &offset_expr, AT);
+         if (p != NULL)
+           p += 4;
+         if (breg != 0)
+           {
+             macro_build (p, &icnt, (expressionS *) NULL,
+                          mips_isa < 3 ? "addu" : "daddu",
+                          "d,v,t", AT, breg, AT);
+             if (p != NULL)
+               p += 4;
+           }
+         macro_build (p, &icnt, &offset_expr, s, fmt,
+                      coproc ? treg + 1 : treg,
+                      (int) BFD_RELOC_LO16, AT);
+         if (p != NULL)
+           p += 4;
+         /* FIXME: How do we handle overflow here?  */
+         offset_expr.X_add_number += 4;
+         macro_build (p, &icnt, &offset_expr, s, fmt,
+                      coproc ? treg : treg + 1,
+                      (int) BFD_RELOC_LO16, AT);
+       }         
       else
        {
-         /* FIXME: This won't work for a 64 bit address.  */
-         macro_build_lui (&icnt, &offset_expr, tempreg);
+         int off;
+
+         /* If this is a reference to an external symbol, we want
+              lw       $at,<sym>($gp)          (BFD_RELOC_MIPS_GOT16)
+              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 (breg == 0)
+           off = 0;
+         else
+           off = 4;
+         frag_grow (16 + off);
+         macro_build ((char *) NULL, &icnt, &offset_expr,
+                      mips_isa < 3 ? "lw" : "ld",
+                      "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16, GP);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
          if (breg != 0)
-           macro_build (&icnt, NULL,
+           macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                         mips_isa < 3 ? "addu" : "daddu",
-                        "d,v,t", tempreg, tempreg, breg);
-         r = BFD_RELOC_LO16;
-       }
-      if (mips_isa >= 3)
-       macro_build (&icnt, &offset_expr, s2, "t,o(b)", treg,
-                    (int) r, tempreg);
-      else
-       {
-         macro_build (&icnt, &offset_expr, s, "t,o(b)", treg,
-                      (int) r, tempreg);
-         offset_expr.X_add_number += 4;
-         macro_build (&icnt, &offset_expr, s, "t,o(b)", treg + 1,
-                      (int) r, tempreg);
+                        "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;
+         macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
+                      coproc ? treg : treg + 1,
+                      (int) BFD_RELOC_LO16, AT);
+         (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);
        }
-      if (used_at)
-       break;
+
+      if (! used_at)
+       return;
+
+      break;
+
+    case M_LD_OB:
+      s = "lw";
+      goto sd_ob;
+    case M_SD_OB:
+      s = "sw";
+    sd_ob:
+      assert (mips_isa < 3);
+      macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
+                  (int) BFD_RELOC_LO16, breg);
+      offset_expr.X_add_number += 4;
+      macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg + 1,
+                  (int) BFD_RELOC_LO16, breg);
       return;
 
     case M_DMUL:
       dbl = 1;
     case M_MUL:
-      macro_build (&icnt, NULL,
+      macro_build ((char *) NULL, &icnt, NULL,
                   dbl ? "dmultu" : "multu",
                   "s,t", sreg, treg);
-      macro_build (&icnt, NULL, "mflo", "d", dreg);
+      macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg);
       return;
 
     case M_DMUL_I:
@@ -2511,10 +3118,10 @@ macro (ip)
         not trying to be that fancy. GCC should do this for us
         anyway.  */
       load_register (&icnt, AT, &imm_expr);
-      macro_build (&icnt, NULL,
+      macro_build ((char *) NULL, &icnt, NULL,
                   dbl ? "dmult" : "mult",
                   "s,t", sreg, AT);
-      macro_build (&icnt, NULL, "mflo", "d", dreg);
+      macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg);
       break;
 
     case M_DMULO:
@@ -2522,20 +3129,21 @@ macro (ip)
     case M_MULO:
       mips_emit_delays ();
       ++mips_noreorder;
-      macro_build (&icnt, NULL,
+      mips_any_noreorder = 1;
+      macro_build ((char *) NULL, &icnt, NULL,
                   dbl ? "dmult" : "mult",
                   "s,t", sreg, treg);
-      macro_build (&icnt, NULL, "mflo", "d", dreg);
-      macro_build (&icnt, NULL,
+      macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg);
+      macro_build ((char *) NULL, &icnt, NULL,
                   dbl ? "dsra32" : "sra",
                   "d,w,<", dreg, dreg, 31);
-      macro_build (&icnt, NULL, "mfhi", "d", AT);
+      macro_build ((char *) NULL, &icnt, NULL, "mfhi", "d", AT);
       expr1.X_add_number = 8;
-      macro_build (&icnt, &expr1, "beq", "s,t,p", dreg, AT);
-      macro_build (&icnt, NULL, "nop", "", 0);
-      macro_build (&icnt, NULL, "break", "c", 6);
+      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 (&icnt, NULL, "mflo", "d", dreg);
+      macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg);
       break;
 
     case M_DMULOU:
@@ -2543,144 +3151,104 @@ macro (ip)
     case M_MULOU:
       mips_emit_delays ();
       ++mips_noreorder;
-      macro_build (&icnt, NULL,
+      mips_any_noreorder = 1;
+      macro_build ((char *) NULL, &icnt, NULL,
                   dbl ? "dmultu" : "multu",
                   "s,t", sreg, treg);
-      macro_build (&icnt, NULL, "mfhi", "d", AT);
-      macro_build (&icnt, NULL, "mflo", "d", dreg);
+      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 (&icnt, &expr1, "beq", "s,t,p", AT, 0);
-      macro_build (&icnt, NULL, "nop", "", 0);
-      macro_build (&icnt, NULL, "break", "c", 6);
+      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;
 
     case M_ROL:
-      macro_build (&icnt, NULL, "subu", "d,v,t", AT, 0, treg);
-      macro_build (&icnt, NULL, "srlv", "d,t,s", AT, sreg, AT);
-      macro_build (&icnt, NULL, "sllv", "d,t,s", dreg, sreg, treg);
-      macro_build (&icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
+      macro_build ((char *) NULL, &icnt, NULL, "subu", "d,v,t", AT, 0, treg);
+      macro_build ((char *) NULL, &icnt, NULL, "srlv", "d,t,s", AT, sreg, AT);
+      macro_build ((char *) NULL, &icnt, NULL, "sllv", "d,t,s", dreg, sreg,
+                  treg);
+      macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
       break;
 
     case M_ROL_I:
-      macro_build (&icnt, NULL, "sll", "d,w,<", AT, sreg,
+      macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", AT, sreg,
                   imm_expr.X_add_number & 0x1f);
-      macro_build (&icnt, NULL, "srl", "d,w,<", dreg, sreg,
+      macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", dreg, sreg,
                   (0 - imm_expr.X_add_number) & 0x1f);
-      macro_build (&icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
+      macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
       break;
 
     case M_ROR:
-      macro_build (&icnt, NULL, "subu", "d,v,t", AT, 0, treg);
-      macro_build (&icnt, NULL, "sllv", "d,t,s", AT, sreg, AT);
-      macro_build (&icnt, NULL, "srlv", "d,t,s", dreg, sreg, treg);
-      macro_build (&icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
+      macro_build ((char *) NULL, &icnt, NULL, "subu", "d,v,t", AT, 0, treg);
+      macro_build ((char *) NULL, &icnt, NULL, "sllv", "d,t,s", AT, sreg, AT);
+      macro_build ((char *) NULL, &icnt, NULL, "srlv", "d,t,s", dreg, sreg,
+                  treg);
+      macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
       break;
 
     case M_ROR_I:
-      macro_build (&icnt, NULL, "srl", "d,w,<", AT, sreg,
+      macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", AT, sreg,
                   imm_expr.X_add_number & 0x1f);
-      macro_build (&icnt, NULL, "sll", "d,w,<", dreg, sreg,
+      macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", dreg, sreg,
                   (0 - imm_expr.X_add_number) & 0x1f);
-      macro_build (&icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
+      macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
       break;
 
     case M_S_DOB:
       assert (mips_isa < 2);
       /* Even on a big endian machine $fn comes before $fn+1.  We have
         to adjust when storing to memory.  */
-      macro_build (&icnt, &offset_expr, "swc1", "T,o(b)",
+      macro_build ((char *) NULL, &icnt, &offset_expr, "swc1", "T,o(b)",
                   byte_order == LITTLE_ENDIAN ? treg : treg + 1,
                   (int) BFD_RELOC_LO16, breg);
       offset_expr.X_add_number += 4;
-      macro_build (&icnt, &offset_expr, "swc1", "T,o(b)",
+      macro_build ((char *) NULL, &icnt, &offset_expr, "swc1", "T,o(b)",
                   byte_order == LITTLE_ENDIAN ? treg + 1 : treg,
                   (int) BFD_RELOC_LO16, breg);
       return;
 
-    case M_S_DAB:
-      if (gp_reference (&offset_expr))
-       {
-         if (breg == 0)
-           tempreg = GP;
-         else
-           {
-             macro_build (&icnt, (expressionS *) NULL,
-                          mips_isa < 3 ? "addu" : "daddu",
-                          "d,v,t", AT, breg, GP);
-             tempreg = AT;
-           }
-         r = BFD_RELOC_MIPS_GPREL;
-       }
-      else
-       {
-         /* FIXME: This won't work for a 64 bit address.  */
-         macro_build_lui (&icnt, &offset_expr, AT);
-         if (breg != 0)
-           macro_build (&icnt, NULL,
-                        mips_isa < 3 ? "addu" : "daddu",
-                        "d,v,t", AT, AT, breg);
-         tempreg = AT;
-         r = BFD_RELOC_LO16;
-       }
-      if (mips_isa >= 2)
-       macro_build (&icnt, &offset_expr, "sdc1", "T,o(b)", treg,
-                    (int) r, tempreg);
-      else
-       {
-         /* Even on a big endian machine $fn comes before $fn+1.  We
-            have to adjust when storing to memory.  */
-         macro_build (&icnt, &offset_expr, "swc1", "T,o(b)",
-                      byte_order == LITTLE_ENDIAN ? treg : treg + 1,
-                      (int) r, tempreg);
-         offset_expr.X_add_number += 4;
-         macro_build (&icnt, &offset_expr, "swc1", "T,o(b)",
-                      byte_order == LITTLE_ENDIAN ? treg + 1 : treg,
-                      (int) r, tempreg);
-       }
-      if (tempreg == AT)
-       break;
-      return;
-
     case M_SEQ:
       if (sreg == 0)
-       macro_build (&icnt, &expr1, "sltiu", "t,r,j", dreg, treg,
-                    (int) BFD_RELOC_LO16);
+       macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg,
+                    treg, (int) BFD_RELOC_LO16);
       else if (treg == 0)
-       macro_build (&icnt, &expr1, "sltiu", "t,r,j", dreg, sreg,
-                    (int) BFD_RELOC_LO16);
+       macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg,
+                    sreg, (int) BFD_RELOC_LO16);
       else
        {
-         macro_build (&icnt, NULL, "xor", "d,v,t", dreg, sreg, treg);
-         macro_build (&icnt, &expr1, "sltiu", "t,r,j", dreg, dreg,
-                      (int) BFD_RELOC_LO16);
+         macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg,
+                      sreg, treg);
+         macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg,
+                      dreg, (int) BFD_RELOC_LO16);
        }
       return;
 
     case M_SEQ_I:
       if (imm_expr.X_add_number == 0)
        {
-         macro_build (&icnt, &expr1, "sltiu", "t,r,j", dreg, sreg,
-                      (int) BFD_RELOC_LO16);
+         macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg,
+                      sreg, (int) BFD_RELOC_LO16);
          return;
        }
       if (sreg == 0)
        {
          as_warn ("Instruction %s: result is always false",
                   ip->insn_mo->name);
-         macro_build (&icnt, NULL, "move", "d,s", dreg, 0);
+         macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", dreg, 0);
          return;
        }
       if (imm_expr.X_add_number >= 0 && imm_expr.X_add_number < 0x10000)
        {
-         macro_build (&icnt, &imm_expr, "xori", "t,r,i", dreg, sreg,
-                      (int) BFD_RELOC_LO16);
+         macro_build ((char *) NULL, &icnt, &imm_expr, "xori", "t,r,i", dreg,
+                      sreg, (int) BFD_RELOC_LO16);
          used_at = 0;
        }
       else if (imm_expr.X_add_number > -0x8000 && imm_expr.X_add_number < 0)
        {
          imm_expr.X_add_number = -imm_expr.X_add_number;
-         macro_build (&icnt, &imm_expr,
+         macro_build ((char *) NULL, &icnt, &imm_expr,
                       mips_isa < 3 ? "addiu" : "daddiu",
                       "t,r,j", dreg, sreg,
                       (int) BFD_RELOC_LO16);
@@ -2689,10 +3257,11 @@ macro (ip)
       else
        {
          load_register (&icnt, AT, &imm_expr);
-         macro_build (&icnt, NULL, "xor", "d,v,t", dreg, sreg, AT);
+         macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg,
+                      sreg, AT);
          used_at = 1;
        }
-      macro_build (&icnt, &expr1, "sltiu", "t,r,j", dreg, dreg,
+      macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg, dreg,
                   (int) BFD_RELOC_LO16);
       if (used_at)
        break;
@@ -2704,8 +3273,8 @@ macro (ip)
     case M_SGEU:
       s = "sltu";
     sge:
-      macro_build (&icnt, NULL, s, "d,v,t", dreg, sreg, treg);
-      macro_build (&icnt, &expr1, "xori", "t,r,i", dreg, dreg,
+      macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, sreg, treg);
+      macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg,
                   (int) BFD_RELOC_LO16);
       return;
 
@@ -2713,7 +3282,7 @@ macro (ip)
     case M_SGEU_I:
       if (imm_expr.X_add_number >= -0x8000 && imm_expr.X_add_number < 0x8000)
        {
-         macro_build (&icnt, &expr1,
+         macro_build ((char *) NULL, &icnt, &expr1,
                       mask == M_SGE_I ? "slti" : "sltiu",
                       "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16);
          used_at = 0;
@@ -2721,12 +3290,12 @@ macro (ip)
       else
        {
          load_register (&icnt, AT, &imm_expr);
-         macro_build (&icnt, NULL,
+         macro_build ((char *) NULL, &icnt, NULL,
                       mask == M_SGE_I ? "slt" : "sltu",
                       "d,v,t", dreg, sreg, AT);
          used_at = 1;
        }
-      macro_build (&icnt, &expr1, "xori", "t,r,i", dreg, dreg,
+      macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg,
                   (int) BFD_RELOC_LO16);
       if (used_at)
        break;
@@ -2738,7 +3307,7 @@ macro (ip)
     case M_SGTU:
       s = "sltu";
     sgt:
-      macro_build (&icnt, NULL, s, "d,v,t", dreg, treg, sreg);
+      macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, treg, sreg);
       return;
 
     case M_SGT_I:              /* sreg > I  <==>  I < sreg */
@@ -2748,7 +3317,7 @@ macro (ip)
       s = "sltu";
     sgti:
       load_register (&icnt, AT, &imm_expr);
-      macro_build (&icnt, NULL, s, "d,v,t", dreg, AT, sreg);
+      macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, AT, sreg);
       break;
 
     case M_SLE:                /* sreg <= treg  <==>  treg >= sreg  <==>  not (treg < sreg) */
@@ -2757,8 +3326,8 @@ macro (ip)
     case M_SLEU:
       s = "sltu";
     sle:
-      macro_build (&icnt, NULL, s, "d,v,t", dreg, treg, sreg);
-      macro_build (&icnt, &expr1, "xori", "t,r,i", dreg, dreg,
+      macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, treg, sreg);
+      macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg,
                   (int) BFD_RELOC_LO16);
       return;
 
@@ -2769,70 +3338,76 @@ macro (ip)
       s = "sltu";
     slei:
       load_register (&icnt, AT, &imm_expr);
-      macro_build (&icnt, NULL, s, "d,v,t", dreg, AT, sreg);
-      macro_build (&icnt, &expr1, "xori", "t,r,i", dreg, dreg,
+      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);
       break;
 
     case M_SLT_I:
       if (imm_expr.X_add_number >= -0x8000 && imm_expr.X_add_number < 0x8000)
        {
-         macro_build (&icnt, &imm_expr, "slti", "t,r,j", dreg, sreg,
-                      (int) BFD_RELOC_LO16);
+         macro_build ((char *) NULL, &icnt, &imm_expr, "slti", "t,r,j",
+                      dreg, sreg, (int) BFD_RELOC_LO16);
          return;
        }
       load_register (&icnt, AT, &imm_expr);
-      macro_build (&icnt, NULL, "slt", "d,v,t", dreg, sreg, AT);
+      macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", dreg, sreg, AT);
       break;
 
     case M_SLTU_I:
       if (imm_expr.X_add_number >= -0x8000 && imm_expr.X_add_number < 0x8000)
        {
-         macro_build (&icnt, &imm_expr, "sltiu", "t,r,j", dreg, sreg,
-                      (int) BFD_RELOC_LO16);
+         macro_build ((char *) NULL, &icnt, &imm_expr, "sltiu", "t,r,j",
+                      dreg, sreg, (int) BFD_RELOC_LO16);
          return;
        }
       load_register (&icnt, AT, &imm_expr);
-      macro_build (&icnt, NULL, "sltu", "d,v,t", dreg, sreg, AT);
+      macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, sreg,
+                  AT);
       break;
 
     case M_SNE:
       if (sreg == 0)
-       macro_build (&icnt, NULL, "sltu", "d,v,t", dreg, 0, treg);
+       macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, 0,
+                    treg);
       else if (treg == 0)
-       macro_build (&icnt, NULL, "sltu", "d,v,t", dreg, 0, sreg);
+       macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, 0,
+                    sreg);
       else
        {
-         macro_build (&icnt, NULL, "xor", "d,v,t", dreg, sreg, treg);
-         macro_build (&icnt, NULL, "sltu", "d,v,t", dreg, 0, dreg);
+         macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg,
+                      sreg, treg);
+         macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, 0,
+                      dreg);
        }
       return;
 
     case M_SNE_I:
       if (imm_expr.X_add_number == 0)
        {
-         macro_build (&icnt, NULL, "sltu", "d,v,t", dreg, 0, sreg);
+         macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, 0,
+                      sreg);
          return;
        }
       if (sreg == 0)
        {
          as_warn ("Instruction %s: result is always true",
                   ip->insn_mo->name);
-         macro_build (&icnt, &expr1,
+         macro_build ((char *) NULL, &icnt, &expr1,
                       mips_isa < 3 ? "addiu" : "daddiu",
                       "t,r,j", dreg, 0, (int) BFD_RELOC_LO16);
          return;
        }
       if (imm_expr.X_add_number >= 0 && imm_expr.X_add_number < 0x10000)
        {
-         macro_build (&icnt, &imm_expr, "xori", "t,r,i", dreg, sreg,
-                      (int) BFD_RELOC_LO16);
+         macro_build ((char *) NULL, &icnt, &imm_expr, "xori", "t,r,i",
+                      dreg, sreg, (int) BFD_RELOC_LO16);
          used_at = 0;
        }
       else if (imm_expr.X_add_number > -0x8000 && imm_expr.X_add_number < 0)
        {
          imm_expr.X_add_number = -imm_expr.X_add_number;
-         macro_build (&icnt, &imm_expr,
+         macro_build ((char *) NULL, &icnt, &imm_expr,
                       mips_isa < 3 ? "addiu" : "daddiu",
                       "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16);
          used_at = 0;
@@ -2840,10 +3415,11 @@ macro (ip)
       else
        {
          load_register (&icnt, AT, &imm_expr);
-         macro_build (&icnt, NULL, "xor", "d,v,t", dreg, sreg, AT);
+         macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg,
+                      sreg, AT);
          used_at = 1;
        }
-      macro_build (&icnt, NULL, "sltu", "d,v,t", dreg, 0, dreg);
+      macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, 0, dreg);
       if (used_at)
        break;
       return;
@@ -2854,13 +3430,13 @@ macro (ip)
       if (imm_expr.X_add_number > -0x8000 && imm_expr.X_add_number <= 0x8000)
        {
          imm_expr.X_add_number = -imm_expr.X_add_number;
-         macro_build (&icnt, &imm_expr,
+         macro_build ((char *) NULL, &icnt, &imm_expr,
                       dbl ? "daddi" : "addi",
                       "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16);
          return;
        }
       load_register (&icnt, AT, &imm_expr);
-      macro_build (&icnt, NULL,
+      macro_build ((char *) NULL, &icnt, NULL,
                   dbl ? "dsub" : "sub",
                   "d,v,t", dreg, sreg, AT);
       break;
@@ -2871,13 +3447,13 @@ macro (ip)
       if (imm_expr.X_add_number > -0x8000 && imm_expr.X_add_number <= 0x8000)
        {
          imm_expr.X_add_number = -imm_expr.X_add_number;
-         macro_build (&icnt, &imm_expr,
+         macro_build ((char *) NULL, &icnt, &imm_expr,
                       dbl ? "daddiu" : "addiu",
                       "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16);
          return;
        }
       load_register (&icnt, AT, &imm_expr);
-      macro_build (&icnt, NULL,
+      macro_build ((char *) NULL, &icnt, NULL,
                   dbl ? "dsubu" : "subu",
                   "d,v,t", dreg, sreg, AT);
       break;
@@ -2901,7 +3477,7 @@ macro (ip)
       s = "tne";
     trap:
       load_register (&icnt, AT, &imm_expr);
-      macro_build (&icnt, NULL, s, "s,t", sreg, AT);
+      macro_build ((char *) NULL, &icnt, NULL, s, "s,t", sreg, AT);
       break;
 
     case M_TRUNCWD:
@@ -2916,21 +3492,22 @@ macro (ip)
        */
       mips_emit_delays ();
       ++mips_noreorder;
-      macro_build (&icnt, NULL, "cfc1", "t,G", treg, 31);
-      macro_build (&icnt, NULL, "cfc1", "t,G", treg, 31);
-      macro_build (&icnt, NULL, "nop", "");
+      mips_any_noreorder = 1;
+      macro_build ((char *) NULL, &icnt, NULL, "cfc1", "t,G", treg, 31);
+      macro_build ((char *) NULL, &icnt, NULL, "cfc1", "t,G", treg, 31);
+      macro_build ((char *) NULL, &icnt, NULL, "nop", "");
       expr1.X_add_number = 3;
-      macro_build (&icnt, &expr1, "ori", "t,r,i", AT, treg,
+      macro_build ((char *) NULL, &icnt, &expr1, "ori", "t,r,i", AT, treg,
                   (int) BFD_RELOC_LO16);
       expr1.X_add_number = 2;
-      macro_build (&icnt, &expr1, "xori", "t,r,i", AT, AT,
+      macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", AT, AT,
                     (int) BFD_RELOC_LO16);
-      macro_build (&icnt, NULL, "ctc1", "t,G", AT, 31);
-      macro_build (&icnt, NULL, "nop", "");
-      macro_build (&icnt, NULL,
+      macro_build ((char *) NULL, &icnt, NULL, "ctc1", "t,G", AT, 31);
+      macro_build ((char *) NULL, &icnt, NULL, "nop", "");
+      macro_build ((char *) NULL, &icnt, NULL,
              mask == M_TRUNCWD ? "cvt.w.d" : "cvt.w.s", "D,S", dreg, sreg);
-      macro_build (&icnt, NULL, "ctc1", "t,G", treg, 31);
-      macro_build (&icnt, NULL, "nop", "");
+      macro_build ((char *) NULL, &icnt, NULL, "ctc1", "t,G", treg, 31);
+      macro_build ((char *) NULL, &icnt, NULL, "nop", "");
       --mips_noreorder;
       break;
 
@@ -2942,121 +3519,100 @@ macro (ip)
     ulh:
       /* avoid load delay */
       offset_expr.X_add_number += 1;
-      macro_build (&icnt, &offset_expr, s, "t,o(b)", treg,
+      macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
       offset_expr.X_add_number -= 1;
-      macro_build (&icnt, &offset_expr, "lbu", "t,o(b)", AT,
+      macro_build ((char *) NULL, &icnt, &offset_expr, "lbu", "t,o(b)", AT,
                   (int) BFD_RELOC_LO16, breg);
-      macro_build (&icnt, NULL, "sll", "d,w,<", treg, treg, 8);
-      macro_build (&icnt, NULL, "or", "d,v,t", treg, treg, 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_ULW:
       /* does this work on a big endian machine? */
       offset_expr.X_add_number += 3;
-      macro_build (&icnt, &offset_expr, "lwl", "t,o(b)", treg,
+      macro_build ((char *) NULL, &icnt, &offset_expr, "lwl", "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
       offset_expr.X_add_number -= 3;
-      macro_build (&icnt, &offset_expr, "lwr", "t,o(b)", treg,
+      macro_build ((char *) NULL, &icnt, &offset_expr, "lwr", "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
       return;
 
     case M_ULH_A:
     case M_ULHU_A:
     case M_ULW_A:
-      if (offset_expr.X_op == O_constant)
-       load_register (&icnt, AT, &offset_expr);
-      else if (gp_reference (&offset_expr))
-       macro_build (&icnt, &offset_expr,
-                    mips_isa < 3 ? "addiu" : "daddiu",
-                    "t,r,j", AT, GP, (int) BFD_RELOC_MIPS_GPREL);
-      else
-       {
-         /* FIXME: This won't work for a 64 bit address.  */
-         macro_build_lui (&icnt, &offset_expr, AT);
-         macro_build (&icnt, &offset_expr,
-                      mips_isa < 3 ? "addiu" : "daddiu",
-                      "t,r,j", AT, AT, (int) BFD_RELOC_LO16);
-       }
+      load_address (&icnt, AT, &offset_expr);
       if (mask == M_ULW_A)
        {
          expr1.X_add_number = 3;
-         macro_build (&icnt, &expr1, "lwl", "t,o(b)", treg,
+         macro_build ((char *) NULL, &icnt, &expr1, "lwl", "t,o(b)", treg,
                       (int) BFD_RELOC_LO16, AT);
-         imm_expr.X_add_number = 0;
-         macro_build (&icnt, &expr1, "lwr", "t,o(b)", treg,
+         expr1.X_add_number = 0;
+         macro_build ((char *) NULL, &icnt, &expr1, "lwr", "t,o(b)", treg,
                       (int) BFD_RELOC_LO16, AT);
        }
       else
        {
-         macro_build (&icnt, &expr1,
+         macro_build ((char *) NULL, &icnt, &expr1,
                       mask == M_ULH_A ? "lb" : "lbu", "t,o(b)", treg,
                       (int) BFD_RELOC_LO16, AT);
-         imm_expr.X_add_number = 0;
-         macro_build (&icnt, &expr1, "lbu", "t,o(b)", AT,
+         expr1.X_add_number = 0;
+         macro_build ((char *) NULL, &icnt, &expr1, "lbu", "t,o(b)", AT,
                       (int) BFD_RELOC_LO16, AT);
-         macro_build (&icnt, NULL, "sll", "d,w,<", treg, treg, 8);
-         macro_build (&icnt, NULL, "or", "d,v,t", treg, treg, 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:
-      macro_build (&icnt, &offset_expr, "sb", "t,o(b)", treg,
+      macro_build ((char *) NULL, &icnt, &offset_expr, "sb", "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
-      macro_build (&icnt, NULL, "srl", "d,w,<", AT, treg, 8);
+      macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", AT, treg, 8);
       offset_expr.X_add_number += 1;
-      macro_build (&icnt, &offset_expr, "sb", "t,o(b)", AT,
+      macro_build ((char *) NULL, &icnt, &offset_expr, "sb", "t,o(b)", AT,
                   (int) BFD_RELOC_LO16, breg);
       break;
 
     case M_USW:
       offset_expr.X_add_number += 3;
-      macro_build (&icnt, &offset_expr, "swl", "t,o(b)", treg,
+      macro_build ((char *) NULL, &icnt, &offset_expr, "swl", "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
       offset_expr.X_add_number -= 3;
-      macro_build (&icnt, &offset_expr, "swr", "t,o(b)", treg,
+      macro_build ((char *) NULL, &icnt, &offset_expr, "swr", "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
       return;
 
     case M_USH_A:
     case M_USW_A:
-      if (offset_expr.X_op == O_constant)
-       load_register (&icnt, AT, &offset_expr);
-      else if (gp_reference (&offset_expr))
-       macro_build (&icnt, &offset_expr,
-                    mips_isa < 3 ? "addiu" : "daddiu",
-                    "t,r,j", AT, GP, (int) BFD_RELOC_MIPS_GPREL);
-      else
-       {
-         /* FIXME: This won't work for a 64 bit address.  */
-         macro_build_lui (&icnt, &offset_expr, AT);
-         macro_build (&icnt, &offset_expr,
-                      mips_isa < 3 ? "addiu" : "daddiu",
-                      "t,r,j", AT, AT, (int) BFD_RELOC_LO16);
-       }
+      load_address (&icnt, AT, &offset_expr);
       if (mask == M_USW_A)
        {
          expr1.X_add_number = 3;
-         macro_build (&icnt, &expr1, "swl", "t,o(b)", treg,
+         macro_build ((char *) NULL, &icnt, &expr1, "swl", "t,o(b)", treg,
                       (int) BFD_RELOC_LO16, AT);
          expr1.X_add_number = 0;
-         macro_build (&icnt, &expr1, "swr", "t,o(b)", treg,
+         macro_build ((char *) NULL, &icnt, &expr1, "swr", "t,o(b)", treg,
                       (int) BFD_RELOC_LO16, AT);
        }
       else
        {
          expr1.X_add_number = 0;
-         macro_build (&icnt, &expr1, "sb", "t,o(b)", treg,
+         macro_build ((char *) NULL, &icnt, &expr1, "sb", "t,o(b)", treg,
                       (int) BFD_RELOC_LO16, AT);
-         macro_build (&icnt, NULL, "srl", "d,w,<", treg, treg, 8);
+         macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", treg,
+                      treg, 8);
          expr1.X_add_number = 1;
-         macro_build (&icnt, &expr1, "sb", "t,o(b)", treg,
+         macro_build ((char *) NULL, &icnt, &expr1, "sb", "t,o(b)", treg,
                       (int) BFD_RELOC_LO16, AT);
          expr1.X_add_number = 0;
-         macro_build (&icnt, &expr1, "lbu", "t,o(b)", AT,
+         macro_build ((char *) NULL, &icnt, &expr1, "lbu", "t,o(b)", AT,
                       (int) BFD_RELOC_LO16, AT);
-         macro_build (&icnt, NULL, "sll", "d,w,<", treg, treg, 8);
-         macro_build (&icnt, NULL, "or", "d,v,t", treg, treg, 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;
 
@@ -3298,7 +3854,9 @@ mips_ip (str, ip)
                      if (regno > 31)
                        as_bad ("Invalid register number (%d)", regno);
                    }
-                 else if (*args != 'E' && *args != 'G')
+                 else if (*args == 'E' || *args == 'G')
+                   goto notreg;
+                 else
                    {
                      if (s[1] == 'f' && s[2] == 'p')
                        {
@@ -3500,7 +4058,10 @@ mips_ip (str, ip)
                    L -- .lit8
                    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.  */
 
                f64 = *args == 'F' || *args == 'L';
 
@@ -3519,7 +4080,8 @@ mips_ip (str, ip)
 
                assert (length == (f64 ? 8 : 4));
 
-               if (*args == 'f')
+               if (*args == 'f'
+                   || (mips_pic != 0 && *args == 'l'))
                  {
                    imm_expr.X_op = O_constant;
                    if (byte_order == LITTLE_ENDIAN)
@@ -3537,21 +4099,30 @@ mips_ip (str, ip)
                  }
                else
                  {
+                   const char *newname;
+                   segT new_seg;
+
                    /* Switch to the right section.  */
                    seg = now_seg;
                    subseg = now_subseg;
                    switch (*args)
                      {
-                     case 'F':
-                       subseg_new (".rdata", (subsegT) 0);
-                       break;
+                     default: /* unused default case avoids warnings.  */
                      case 'L':
-                       subseg_new (".lit8", (subsegT) 0);
+                       newname = (mips_pic == 0 ? ".lit8" : ".rdata");
+                       break;
+                     case 'F':
+                       newname = ".rdata";
                        break;
                      case 'l':
-                       subseg_new (".lit4", (subsegT) 0);
+                       assert (mips_pic == 0);
+                       newname = ".lit4";
                        break;
                      }
+                   new_seg = subseg_new (newname, (subsegT) 0);
+#ifdef OBJ_ELF
+                   bfd_set_section_alignment (stdoutput, new_seg, 4);
+#endif
                    if (seg == now_seg)
                      as_bad ("Can't use floating point insn in this section");
 
@@ -4119,6 +4690,8 @@ md_apply_fix (fixP, valueP)
     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:
       /* Nothing needed to do. The value comes from the reloc entry */
       return 1;
 
@@ -4381,6 +4954,7 @@ s_change_sec (sec)
                              | SEC_READONLY
                              | SEC_RELOC
                              | SEC_DATA));
+      bfd_set_section_alignment (stdoutput, seg, 4);
       demand_empty_rest_of_line ();
       break;
 #else /* ! defined (OBJ_ELF) */
@@ -4395,6 +4969,7 @@ s_change_sec (sec)
 #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;
@@ -4440,7 +5015,7 @@ s_extern (x)
   S_SET_VALUE (symbolP, size);
   S_SET_EXTERNAL (symbolP);
 
-#ifdef OBJ_ECOFF
+#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;
@@ -4474,7 +5049,6 @@ s_option (x)
   opt = input_line_pointer;
   c = get_symbol_end ();
 
-  /* FIXME: What do these options mean?  */
   if (*opt == 'O')
     {
       /* FIXME: What does this mean?  */
@@ -4482,7 +5056,7 @@ s_option (x)
   else if (strncmp (opt, "pic", 3) == 0)
     {
       mips_pic = atoi (opt + 3);
-      /* FIXME: I don't know what other values mean.  */
+      /* Supposedly no other values are used.  */
       assert (mips_pic == 0 || mips_pic == 2);
     }
   else
@@ -4516,6 +5090,7 @@ s_mipsset (x)
     {
       mips_emit_delays ();
       mips_noreorder = 1;
+      mips_any_noreorder = 1;
     }
   else if (strcmp (name, "at") == 0)
     {
@@ -4590,7 +5165,7 @@ s_abicalls (ignore)
        lui     $gp,%hi(_gp_disp)
        addiu   $gp,$gp,%lo(_gp_disp)
        addu    $gp,$gp,.cpload argument
-   The .cpload argument is normally $25 or $t9.  */
+   The .cpload argument is normally $25 == $t9.  */
 
 static void
 s_cpload (ignore)
@@ -4599,17 +5174,28 @@ s_cpload (ignore)
   expressionS ex;
   int icnt = 0;
 
+  /* If we are not generating PIC code, .cpload is ignored.  */
+  if (mips_pic == 0)
+    {
+      s_ignore (0);
+      return;
+    }
+
+  /* .cpload should be a in .set noreorder section.  */
+  if (mips_noreorder == 0)
+    as_warn (".cpload not in noreorder section");
+
   ex.X_op = O_symbol;
   ex.X_add_symbol = symbol_find_or_make ("_gp_disp");
   ex.X_op_symbol = NULL;
   ex.X_add_number = 0;
 
-  macro_build_lui (&icnt, &ex, GP);
-  macro_build (&icnt, &ex, "addiu", "t,r,j", GP, GP,
+  macro_build_lui ((char *) NULL, &icnt, &ex, GP);
+  macro_build ((char *) NULL, &icnt, &ex, "addiu", "t,r,j", GP, GP,
               (int) BFD_RELOC_LO16);
 
-  macro_build (&icnt, (expressionS *) NULL, "addu", "d,v,t", GP, GP,
-              tc_get_register ());
+  macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "addu", "d,v,t",
+              GP, GP, tc_get_register (0));
 
   demand_empty_rest_of_line ();
 }
@@ -4625,6 +5211,13 @@ s_cprestore (ignore)
   expressionS ex;
   int icnt = 0;
 
+  /* If we are not generating PIC code, .cprestore is ignored.  */
+  if (mips_pic == 0)
+    {
+      s_ignore (0);
+      return;
+    }
+
   mips_cprestore_offset = get_absolute_expression ();
 
   ex.X_op = O_constant;
@@ -4632,18 +5225,84 @@ s_cprestore (ignore)
   ex.X_op_symbol = NULL;
   ex.X_add_number = mips_cprestore_offset;
 
-  macro_build (&icnt, &ex,
+  macro_build ((char *) NULL, &icnt, &ex,
               mips_isa < 3 ? "sw" : "sd",
               "t,o(b)", GP, (int) BFD_RELOC_LO16, SP);
 
   demand_empty_rest_of_line ();
 }
 
+/* Handle the .gpword pseudo-op.  This is used when generating PIC
+   code.  It generates a 32 bit GP relative reloc.  */
+
+static void
+s_gpword (ignore)
+     int ignore;
+{
+  expressionS ex;
+  char *p;
+
+  /* When not generating PIC code, this is treated as .word.  */
+  if (mips_pic == 0)
+    {
+      s_cons (2);
+      return;
+    }
+
+  mips_emit_delays ();
+  if (auto_align)
+    mips_align (2, 0);
+  insn_label = NULL;
+
+  expression (&ex);
+
+  if (ex.X_op != O_symbol || ex.X_add_number != 0)
+    {
+      as_bad ("Unsupported use of .gpword");
+      ignore_rest_of_line ();
+    }
+
+  p = frag_more (4);
+  md_number_to_chars (p, (valueT) 0, 4);
+  fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, 0,
+              BFD_RELOC_MIPS_GPREL32);
+
+  demand_empty_rest_of_line ();
+}
+
+/* Handle the .cpadd pseudo-op.  This is used when dealing with switch
+   tables in SVR4 PIC code.  */
+
+static void
+s_cpadd (ignore)
+     int ignore;
+{
+  int icnt = 0;
+  int reg;
+
+  /* This is ignored when not generating SVR4 PIC code.  */
+  if (mips_pic == 0)
+    {
+      s_ignore (0);
+      return;
+    }
+
+  /* Add $gp to the register named as an argument.  */
+  reg = tc_get_register (0);
+  macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+              mips_isa < 3 ? "addu" : "daddu",
+              "d,v,t", reg, reg, GP);
+
+  demand_empty_rest_of_line ();  
+}
+
 /* Parse a register string into a number.  Called from the ECOFF code
-   to parse .frame.  */
+   to parse .frame.  The argument is non-zero if this is the frame
+   register, so that we can record it in mips_frame_reg.  */
 
 int
-tc_get_register ()
+tc_get_register (frame)
+     int frame;
 {
   int reg;
 
@@ -4651,9 +5310,9 @@ tc_get_register ()
   if (*input_line_pointer++ != '$')
     {
       as_warn ("expected `$'");
-      return 0;
+      reg = 0;
     }
-  if (isdigit ((unsigned char) *input_line_pointer))
+  else if (isdigit ((unsigned char) *input_line_pointer))
     {
       reg = get_absolute_expression ();
       if (reg < 0 || reg >= 32)
@@ -4675,25 +5334,121 @@ tc_get_register ()
       else
        {
          as_warn ("Unrecognized register name");
-         return 0;
+         reg = 0;
        }
       input_line_pointer += 2;
     }
+  if (frame)
+    mips_frame_reg = reg != 0 ? reg : SP;
   return reg;
 }
 
-/*
- * Translate internal representation of relocation info to BFD target format.
- */
-arelent *
+valueT
+md_section_align (seg, addr)
+     asection *seg;
+     valueT addr;
+{
+  int align = bfd_get_section_alignment (stdoutput, seg);
+
+  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.  */
+
+/*ARGSUSED*/
+int
+md_estimate_size_before_relax (fragp, segtype)
+     fragS *fragp;
+     asection *segtype;
+{
+  int change;
+
+  if (mips_pic == 0)
+    {
+#ifdef GPOPT
+      const char *symname;
+
+      /* 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);
+      if (symname != (const char *) NULL
+         && (strcmp (symname, "eprol") == 0
+             || strcmp (symname, "etext") == 0
+             || strcmp (symname, "_gp") == 0
+             || strcmp (symname, "edata") == 0
+             || strcmp (symname, "_fbss") == 0
+             || strcmp (symname, "_fdata") == 0
+             || strcmp (symname, "_ftext") == 0
+             || 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)
+       change = 0;
+      else
+       {
+         const char *segname;
+
+         segname = segment_name (S_GET_SEGMENT (fragp->fr_symbol));
+         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) */  
+    }
+  else
+    {
+      asection *symsec = fragp->fr_symbol->bsym->section;
+
+      /* This must duplicate the test in adjust_reloc_syms.  */
+      change = (symsec != &bfd_und_section
+               && symsec != &bfd_abs_section
+               && ! bfd_is_com_section (symsec));
+    }
+
+  if (change)
+    {
+      /* Record the offset to the first reloc in the fr_opcode field.
+        This lets md_convert_frag and tc_gen_reloc know that the code
+        must be expanded.  */
+      fragp->fr_opcode = (fragp->fr_literal
+                         + fragp->fr_fix
+                         - RELAX_OLD (fragp->fr_subtype)
+                         + RELAX_RELOC1 (fragp->fr_subtype));
+      /* FIXME: This really needs as_warn_where.  */
+      if (RELAX_WARN (fragp->fr_subtype))
+       as_warn ("AT used after \".set noat\" or macro used after \".set nomacro\"");
+    }
+
+  if (! change)
+    return 0;
+  else
+    return RELAX_NEW (fragp->fr_subtype) - RELAX_OLD (fragp->fr_subtype);
+}
+
+/* Translate internal representation of relocation info to BFD target
+   format.  */
+
+arelent **
 tc_gen_reloc (section, fixp)
      asection *section;
      fixS *fixp;
 {
+  static arelent *retval[4];
   arelent *reloc;
 
-  reloc = (arelent *) xmalloc (sizeof (arelent));
-  assert (reloc != 0);
+  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;
@@ -4705,36 +5460,100 @@ tc_gen_reloc (section, fixp)
 #else
     reloc->addend = -reloc->address;
 #endif
+
+  /* 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))
+    {
+      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.  */
+      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);
+         retval[0] = NULL;
+         return retval;
+       }
+
+      fixp->fx_where = fixp->fx_frag->fr_opcode - fixp->fx_frag->fr_literal;
+      reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+      reloc2 = retval[1] = (arelent *) xmalloc (sizeof (arelent));
+      retval[2] = NULL;
+      reloc2->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+      reloc2->address = (reloc->address
+                        + (RELAX_RELOC2 (fixp->fx_frag->fr_subtype)
+                           - RELAX_RELOC1 (fixp->fx_frag->fr_subtype)));
+      reloc2->addend = fixp->fx_addnumber;
+      reloc2->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_LO16);
+      assert (reloc2->howto != NULL);
+
+      if (RELAX_RELOC3 (fixp->fx_frag->fr_subtype))
+       {
+         arelent *reloc3;
+
+         reloc3 = retval[2] = (arelent *) xmalloc (sizeof (arelent));
+         retval[3] = NULL;
+         *reloc3 = *reloc2;
+         reloc3->address += 4;
+       }
+
+      if (mips_pic == 0)
+       {
+         assert (fixp->fx_r_type == BFD_RELOC_MIPS_GPREL);
+         fixp->fx_r_type = BFD_RELOC_HI16_S;
+       }
+      else
+       {
+         if (fixp->fx_r_type != BFD_RELOC_MIPS_GOT16)
+           {
+             assert (fixp->fx_r_type == BFD_RELOC_MIPS_CALL16);
+             fixp->fx_r_type = BFD_RELOC_MIPS_GOT16;
+           }
+       }
+    }
+
   reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+
   if (reloc->howto == NULL)
     {
       as_bad_where (fixp->fx_file, fixp->fx_line,
                    "Can not represent relocation in this object file format");
-      return NULL;
+      retval[0] = NULL;
     }
 
-  return reloc;
+  return retval;
 }
 
-/* should never be called */
-valueT
-md_section_align (seg, addr)
-     asection *seg;
-     valueT addr;
+/* Convert a machine dependent frag.  */
+
+void
+md_convert_frag (abfd, asec, fragp)
+     bfd *abfd;
+     segT asec;
+     fragS *fragp;
 {
-  int align = bfd_get_section_alignment (stdoutput, seg);
+  int old, new;
+  char *fixptr;
 
-  return ((addr + (1 << align) - 1) & (-1 << align));
-}
+  if (fragp->fr_opcode == NULL)
+    return;
 
-int
-md_estimate_size_before_relax (fragP, segtype)
-     fragS *fragP;
-     asection *segtype;
-{
-  as_fatal ("md_estimate_size_before_relax");
-  return (1);
-}                              /* md_estimate_size_before_relax() */
+  old = RELAX_OLD (fragp->fr_subtype);
+  new = RELAX_NEW (fragp->fr_subtype);
+  fixptr = fragp->fr_literal + fragp->fr_fix;
+
+  if (new > 0)
+    memcpy (fixptr - old, fixptr, new);
+
+  fragp->fr_fix += new - old;
+}
 
 /* This function is called whenever a label is defined.  It is used
    when handling branch delays; if a branch has a label, we assume we
@@ -4749,13 +5568,14 @@ mips_define_label (sym)
 \f
 #ifdef OBJ_ELF
 
-/* Write out the .reginfo section for a MIPS ELF file.  */
+/* 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];
@@ -4766,11 +5586,18 @@ mips_elf_final_processing ()
   bfd_mips_elf32_swap_reginfo_out (stdoutput, &s,
                                   ((Elf32_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)
+    elf_elfheader (stdoutput)->e_flags |= EF_MIPS_PIC;
 }
 
 #endif /* OBJ_ELF */
 \f
-#ifndef OBJ_ECOFF
+#ifndef ECOFF_DEBUGGING
 
 /* These functions should really be defined by the object file format,
    since they are related to debugging information.  However, this
@@ -4999,13 +5826,13 @@ s_frame (x)
   int frame_off;
   int pcreg;
 
-  frame_reg = tc_get_register ();
+  frame_reg = tc_get_register (1);
   if (*input_line_pointer == ',')
     input_line_pointer++;
   frame_off = get_absolute_expression ();
   if (*input_line_pointer == ',')
     input_line_pointer++;
-  pcreg = tc_get_register ();
+  pcreg = tc_get_register (0);
 
   /* bob third eye */
   assert (proc_rootP);
@@ -5114,4 +5941,4 @@ s_loc (x)
 }
 #endif
 
-#endif /* ! defined (OBJ_ECOFF) */
+#endif /* ! defined (ECOFF_DEBUGGING) */