Thu Mar 20 13:42:01 1997 H.J. Lu <hjl@lucon.org>
[binutils-gdb.git] / gas / config / tc-mips.c
index c3ced95168fc33b10c62246e46b2c457cd48aff7..5a92c2490adb266e233a5fb7b0e45ac24994465d 100644 (file)
 #endif
 
 #include "opcode/mips.h"
+#include "itbl-ops.h"
+
+#ifdef DEBUG
+#define DBG(x) printf x
+#else
+#define DBG(x)
+#endif
 
 #ifdef OBJ_MAYBE_ELF
 /* Clean up namespace so we can include obj-elf.h too.  */
+static int mips_output_flavor PARAMS ((void));
 static int mips_output_flavor () { return OUTPUT_FLAVOR; }
 #undef OBJ_PROCESS_STAB
 #undef OUTPUT_FLAVOR
@@ -86,6 +94,12 @@ static char *mips_regmask_frag;
 
 #define ILLEGAL_REG (32)
 
+/* Allow override of standard little-endian ECOFF format.  */
+
+#ifndef ECOFF_LITTLE_FORMAT
+#define ECOFF_LITTLE_FORMAT "ecoff-littlemips"
+#endif
+
 extern int target_big_endian;
 
 /* 1 is we should use the 64 bit MIPS ELF ABI, 0 if we should use the
@@ -101,7 +115,7 @@ mips_target_format ()
     case bfd_target_aout_flavour:
       return target_big_endian ? "a.out-mips-big" : "a.out-mips-little";
     case bfd_target_ecoff_flavour:
-      return target_big_endian ? "ecoff-bigmips" : "ecoff-littlemips";
+      return target_big_endian ? "ecoff-bigmips" : ECOFF_LITTLE_FORMAT;
     case bfd_target_elf_flavour:
       return (target_big_endian
              ? (mips_64 ? "elf64-bigmips" : "elf32-bigmips")
@@ -159,6 +173,7 @@ static int interlocks = -1;
 
 /* As with "interlocks" this is used by hardware that has FP
    (co-processor) interlocks.  */
+/* Itbl support may require additional care here. */
 static int cop_interlocks = -1;
 
 /* MIPS PIC level.  */
@@ -324,6 +339,22 @@ static int prev_insn_extended;
    noreorder.  */
 static int prev_prev_insn_unreordered;
 
+/* If this is set, it points to a frag holding nop instructions which
+   were inserted before the start of a noreorder section.  If those
+   nops turn out to be unnecessary, the size of the frag can be
+   decreased.  */
+static fragS *prev_nop_frag;
+
+/* The number of nop instructions we created in prev_nop_frag.  */
+static int prev_nop_frag_holds;
+
+/* The number of nop instructions that we know we need in
+   prev_nop_frag. */
+static int prev_nop_frag_required;
+
+/* The number of instructions we've seen since prev_nop_frag.  */
+static int prev_nop_frag_since;
+
 /* For ECOFF and ELF, relocations against symbols are done in two
    parts, with a HI relocation and a LO relocation.  Each relocation
    has only 16 bits of space to store an addend.  This means that in
@@ -503,7 +534,7 @@ static void append_insn PARAMS ((char *place,
                                 expressionS * p,
                                 bfd_reloc_code_real_type r,
                                 boolean));
-static void mips_no_prev_insn PARAMS ((void));
+static void mips_no_prev_insn PARAMS ((int));
 static void mips_emit_delays PARAMS ((boolean));
 #ifdef USE_STDARG
 static void macro_build PARAMS ((char *place, int *counter, expressionS * ep,
@@ -856,6 +887,7 @@ md_begin ()
   else
     interlocks = 0;
 
+  /* Itbl support may require additional care here. */
   if (mips_cpu == 4300)
     cop_interlocks = 1;
   else
@@ -961,7 +993,7 @@ md_begin ()
   symbol_table_insert (symbol_new ("$pc", reg_section, -1,
                                   &zero_address_frag));
 
-  mips_no_prev_insn ();
+  mips_no_prev_insn (false);
 
   mips_gprmask = 0;
   mips_cprmask[0] = 0;
@@ -1083,7 +1115,11 @@ md_assemble (str)
   if (mips16)
     mips16_ip (str, &insn);
   else
-    mips_ip (str, &insn);
+    {
+      mips_ip (str, &insn);
+      DBG(("returned from mips_ip(%s) insn_opcode = 0x%x\n", 
+               str, insn.insn_opcode));
+    }
 
   if (insn_error)
     {
@@ -1205,6 +1241,7 @@ reg_needs_delay (reg)
         delays delay the use of general register rt for one
         instruction on the r3000.  The r6000 and r4000 use
         interlocks.  */
+      /* Itbl support may require additional care here. */
       know (prev_pinfo & INSN_WRITE_GPR_T);
       if (reg == ((prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT))
        return 1;
@@ -1255,8 +1292,10 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
   prev_pinfo = prev_insn.insn_mo->pinfo;
   pinfo = ip->insn_mo->pinfo;
 
-  if (place == NULL && ! mips_noreorder)
+  if (place == NULL && (! mips_noreorder || prev_nop_frag != NULL))
     {
+      int prev_prev_nop;
+
       /* If the previous insn required any delay slots, see if we need
         to insert a NOP or two.  There are eight kinds of possible
         hazards, of which an instruction can have at most one type.
@@ -1296,6 +1335,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
             delays delay the use of general register rt for one
             instruction on the r3000.  The r6000 and r4000 use
             interlocks.  */
+          /* Itbl support may require additional care here. */
          know (prev_pinfo & INSN_WRITE_GPR_T);
          if (mips_optimize == 0
              || insn_uses_reg (ip,
@@ -1325,6 +1365,9 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
             knowledge of CP0 handling, and the coprocessors other
             than the floating point unit are not distinguished at
             all.  */
+          /* Itbl support may require additional care here. FIXME!
+             Need to modify this to include knowledge about 
+             user specified delays!  */
          if (prev_pinfo & INSN_WRITE_FPR_T)
            {
              if (mips_optimize == 0
@@ -1351,6 +1394,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                 instruction may set the condition codes, and the
                 current instruction uses them, we must insert two
                 NOPS.  */
+              /* Itbl support may require additional care here. */
              if (mips_optimize == 0
                  || ((prev_pinfo & INSN_WRITE_COND_CODE)
                      && (pinfo & INSN_READ_COND_CODE)))
@@ -1369,6 +1413,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
             (this means it is a floating point comparison
             instruction).  If this instruction uses the condition
             codes, we need to insert a single NOP.  */
+          /* Itbl support may require additional care here. */
          if (mips_optimize == 0
              || (pinfo & INSN_READ_COND_CODE))
            ++nops;
@@ -1394,6 +1439,12 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
            nops += 2;
        }
 
+      /* If the previous instruction was in a noreorder section, then
+         we don't want to insert the nop after all.  */
+      /* Itbl support may require additional care here. */
+      if (prev_insn_unreordered)
+       nops = 0;
+
       /* There are two cases which require two intervening
         instructions: 1) setting the condition codes using a move to
         coprocessor instruction which requires a general coprocessor
@@ -1402,30 +1453,39 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
         which have interlocks).  If we are not already emitting a NOP
         instruction, we must check for these cases compared to the
         instruction previous to the previous instruction.  */
-      if (nops == 0
-         && ((! mips16
-              && mips_isa < 4
-              && (prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY)
-              && (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
-              && (pinfo & INSN_READ_COND_CODE)
-               && ! cop_interlocks)
-             || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)
-                 && (pinfo & INSN_WRITE_LO)
-                 && ! interlocks)
-             || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
-                 && (pinfo & INSN_WRITE_HI)
-                 && ! interlocks)))
+      if ((! mips16
+          && mips_isa < 4
+          && (prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY)
+          && (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
+          && (pinfo & INSN_READ_COND_CODE)
+          && ! cop_interlocks)
+         || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)
+             && (pinfo & INSN_WRITE_LO)
+             && ! interlocks)
+         || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
+             && (pinfo & INSN_WRITE_HI)
+             && ! interlocks))
+       prev_prev_nop = 1;
+      else
+       prev_prev_nop = 0;
+
+      if (prev_prev_insn_unreordered)
+       prev_prev_nop = 0;
+
+      if (prev_prev_nop && nops == 0)
        ++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 == (mips16 ? 0x6500 : 0))
+      if (nops > 0
+         && ! mips_noreorder
+         && ip->insn_opcode == (mips16 ? 0x6500 : 0))
        --nops;
 
       /* Now emit the right number of NOP instructions.  */
-      if (nops > 0)
+      if (nops > 0 && ! mips_noreorder)
        {
          fragS *old_frag;
          unsigned long old_frag_offset;
@@ -1467,8 +1527,45 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
            ecoff_fix_loc (old_frag, old_frag_offset);
 #endif
        }
+      else if (prev_nop_frag != NULL)
+       {
+         /* We have a frag holding nops we may be able to remove.  If
+             we don't need any nops, we can decrease the size of
+             prev_nop_frag by the size of one instruction.  If we do
+             need some nops, we count them in prev_nops_required. */
+         if (prev_nop_frag_since == 0)
+           {
+             if (nops == 0)
+               {
+                 prev_nop_frag->fr_fix -= mips16 ? 2 : 4;
+                 --prev_nop_frag_holds;
+               }
+             else
+               prev_nop_frag_required += nops;
+           }
+         else
+           {
+             if (prev_prev_nop == 0)
+               {
+                 prev_nop_frag->fr_fix -= mips16 ? 2 : 4;
+                 --prev_nop_frag_holds;
+               }
+             else
+               ++prev_nop_frag_required;
+           }
+
+         if (prev_nop_frag_holds <= prev_nop_frag_required)
+           prev_nop_frag = NULL;
+
+         ++prev_nop_frag_since;
+
+         /* Sanity check: by the time we reach the second instruction
+             after prev_nop_frag, we should have used up all the nops
+             one way or another.  */
+         assert (prev_nop_frag_since <= 1 || prev_nop_frag == NULL);
+       }
     }
-  
+
   if (reloc_type > BFD_RELOC_UNUSED)
     {
       /* We need to set up a variant frag.  */
@@ -1480,7 +1577,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                                          & INSN_UNCOND_BRANCH_DELAY),
                                         (prev_insn_reloc_type
                                          == BFD_RELOC_MIPS16_JMP)),
-                   make_expr_symbol (address_expr), (long) 0,
+                   make_expr_symbol (address_expr), (offsetT) 0,
                    (char *) NULL);
     }
   else if (place != NULL)
@@ -1606,7 +1703,10 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
        mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FR) & OP_MASK_FR);
       if (pinfo & INSN_COP)
        {
-         /* We don't keep enough information to sort these cases out.  */
+         /* We don't keep enough information to sort these cases out. 
+            The itbl support does keep this information however, although 
+            we currently don't support itbl fprmats as part of the cop 
+            instruction.  May want to add this support in the future. */
        }
       /* Never set the bit for $0, which is always zero.  */
       mips_gprmask &=~ 1 << 0;
@@ -1707,6 +1807,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
              || (! mips16
                  && mips_isa < 4
                  && (prev_pinfo
+              /* Itbl support may require additional care here. */
                      & (INSN_LOAD_COPROC_DELAY
                         | INSN_COPROC_MOVE_DELAY
                         | INSN_WRITE_COND_CODE)))
@@ -1718,6 +1819,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                  && mips_isa < 2
                  && (prev_pinfo
                      & (INSN_LOAD_MEMORY_DELAY
+              /* Itbl support may require additional care here. */
                         | INSN_COPROC_MEMORY_DELAY)))
              /* We can not swap with a branch instruction.  */
              || (prev_pinfo
@@ -1822,6 +1924,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                 can not swap.  */
              || (! mips16
                  && mips_isa < 4
+              /* Itbl support may require additional care here. */
                  && ((prev_prev_insn.insn_mo->pinfo & INSN_LOAD_COPROC_DELAY)
                      || (mips_isa < 2
                          && (prev_prev_insn.insn_mo->pinfo
@@ -1880,25 +1983,6 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                      fixp->fx_where = prev_insn_where;
                    }
                }
-             else if (reloc_type > BFD_RELOC_UNUSED)
-               {
-                 char *prev_f;
-                 char temp[2];
-
-                 /* We are in mips16 mode, and we have just created a
-                     variant frag.  We need to extract the old
-                     instruction from the end of the previous frag,
-                     and add it to a new frag.  */
-                 prev_f = prev_insn_frag->fr_literal + prev_insn_where;
-                 memcpy (temp, prev_f, 2);
-                 prev_insn_frag->fr_fix -= 2;
-                 if (prev_insn_frag->fr_type == rs_machine_dependent)
-                   {
-                     assert (prev_insn_where == prev_insn_frag->fr_fix);
-                     memcpy (prev_f, prev_f + 2, 2);
-                   }
-                 memcpy (frag_more (2), temp, 2);
-               }
              else
                {
                  char *prev_f;
@@ -1909,7 +1993,10 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                  memcpy (temp, prev_f, 2);
                  memcpy (prev_f, f, 2);
                  if (reloc_type != BFD_RELOC_MIPS16_JMP)
-                   memcpy (f, temp, 2);
+                   {
+                     assert (reloc_type == BFD_RELOC_UNUSED);
+                     memcpy (f, temp, 2);
+                   }
                  else
                    {
                      memcpy (f, f + 2, 2);
@@ -1986,9 +2073,11 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
       /* We need to record a bit of information even when we are not
          reordering, in order to determine the base address for mips16
          PC relative relocs.  */
+      prev_prev_insn = prev_insn;
       prev_insn = *ip;
       prev_insn_reloc_type = reloc_type;
-      prev_insn_valid = 1;
+      prev_prev_insn_unreordered = prev_insn_unreordered;
+      prev_insn_unreordered = 1;
     }
 
   /* We just output an insn, so the next one doesn't have a label.  */
@@ -1996,13 +2085,22 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
 }
 
 /* This function forgets that there was any previous instruction or
-   label.  */
+   label.  If PRESERVE is non-zero, it remembers enough information to
+   know whether nops are needed before a noreorder section. */
 
 static void
-mips_no_prev_insn ()
+mips_no_prev_insn (preserve)
+     int preserve;
 {
-  prev_insn.insn_mo = &dummy_opcode;
-  prev_prev_insn.insn_mo = &dummy_opcode;
+  if (! preserve)
+    {
+      prev_insn.insn_mo = &dummy_opcode;
+      prev_prev_insn.insn_mo = &dummy_opcode;
+      prev_nop_frag = NULL;
+      prev_nop_frag_holds = 0;
+      prev_nop_frag_required = 0;
+      prev_nop_frag_since = 0;
+    }
   prev_insn_valid = 0;
   prev_insn_is_delay_slot = 0;
   prev_insn_unreordered = 0;
@@ -2024,9 +2122,9 @@ mips_emit_delays (insns)
 {
   if (! mips_noreorder)
     {
-      int nop;
+      int nops;
 
-      nop = 0;
+      nops = 0;
       if ((! mips16
           && mips_isa < 4
           && (! cop_interlocks
@@ -2044,7 +2142,8 @@ mips_emit_delays (insns)
                  & (INSN_LOAD_MEMORY_DELAY
                     | INSN_COPROC_MEMORY_DELAY))))
        {
-         nop = 1;
+          /* Itbl support may require additional care here. */
+         ++nops;
          if ((! mips16
               && mips_isa < 4
               && (! cop_interlocks
@@ -2052,7 +2151,10 @@ mips_emit_delays (insns)
              || (! interlocks
                  && ((prev_insn.insn_mo->pinfo & INSN_READ_HI)
                      || (prev_insn.insn_mo->pinfo & INSN_READ_LO))))
-           emit_nop ();
+           ++nops;
+
+         if (prev_insn_unreordered)
+           nops = 0;
        }
       else if ((! mips16
                && mips_isa < 4
@@ -2061,12 +2163,38 @@ mips_emit_delays (insns)
               || (! interlocks
                   && ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
                       || (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO))))
-       nop = 1;
-      if (nop)
+       {
+          /* Itbl support may require additional care here. */
+         if (! prev_prev_insn_unreordered)
+           ++nops;
+       }
+
+      if (nops > 0)
        {
          struct insn_label_list *l;
 
-         emit_nop ();
+         if (insns)
+           {
+             /* Record the frag which holds the nop instructions, so
+                 that we can remove them if we don't need them.  */
+             frag_grow (mips16 ? nops * 2 : nops * 4);
+             prev_nop_frag = frag_now;
+             prev_nop_frag_holds = nops;
+             prev_nop_frag_required = 0;
+             prev_nop_frag_since = 0;
+           }
+
+         for (; nops > 0; --nops)
+           emit_nop ();
+
+         if (insns)
+           {
+             /* Move on to a new frag, so that it is safe to simply
+                 decrease the size of prev_nop_frag. */
+             frag_wane (frag_now);
+             frag_new (0);
+           }
+
          for (l = insn_labels; l != NULL; l = l->next)
            {
              assert (S_GET_SEGMENT (l->label) == now_seg);
@@ -2101,7 +2229,7 @@ mips_emit_delays (insns)
        }
     }
 
-  mips_no_prev_insn ();
+  mips_no_prev_insn (insns);
 }
 
 /* Build an instruction created by a macro expansion.  This is passed
@@ -2664,8 +2792,12 @@ load_register (counter, reg, ep, dbl)
                    || ! ep->X_unsigned
                    || sizeof (ep->X_add_number) > 4
                    || (ep->X_add_number & 0x80000000) == 0))
-              || ((mips_isa < 3 || !dbl)
-                  && (ep->X_add_number &~ (offsetT) 0xffffffff) == 0))
+              || ((mips_isa < 3 || ! dbl)
+                  && (ep->X_add_number &~ (offsetT) 0xffffffff) == 0)
+              || (mips_isa < 3
+                  && ! dbl
+                  && ((ep->X_add_number &~ (offsetT) 0xffffffff)
+                      == ~ (offsetT) 0xffffffff)))
        {
          /* 32 bit values require an lui.  */
          macro_build ((char *) NULL, counter, ep, "lui", "t,u", reg,
@@ -2923,7 +3055,7 @@ load_address (counter, reg, ep)
                       "t,r,j", reg, GP, (int) BFD_RELOC_MIPS_GPREL);
          p = frag_var (rs_machine_dependent, 8, 0,
                        RELAX_ENCODE (4, 8, 0, 4, 0, mips_warn_about_macros),
-                       ep->X_add_symbol, (long) 0, (char *) NULL);
+                       ep->X_add_symbol, (offsetT) 0, (char *) NULL);
        }
       macro_build_lui (p, counter, ep, reg);
       if (p != NULL)
@@ -2952,7 +3084,7 @@ load_address (counter, reg, ep)
       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);
+                   ep->X_add_symbol, (offsetT) 0, (char *) NULL);
       macro_build (p, counter, ep,
                   mips_isa < 3 ? "addiu" : "daddiu",
                   "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
@@ -2999,7 +3131,7 @@ load_address (counter, reg, ep)
       p = frag_var (rs_machine_dependent, 12 + off, 0,
                    RELAX_ENCODE (12, 12 + off, off, 8 + off, 0,
                                  mips_warn_about_macros),
-                   ep->X_add_symbol, (long) 0, (char *) NULL);
+                   ep->X_add_symbol, (offsetT) 0, (char *) NULL);
       if (off > 0)
        {
          /* We need a nop before loading from $gp.  This special
@@ -3794,7 +3926,7 @@ macro (ip)
              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,
+                           offset_expr.X_add_symbol, (offsetT) 0,
                            (char *) NULL);
            }
          macro_build_lui (p, &icnt, &offset_expr, tempreg);
@@ -3858,7 +3990,7 @@ macro (ip)
                                          (breg == 0
                                           ? mips_warn_about_macros
                                           : 0)),
-                           offset_expr.X_add_symbol, (long) 0,
+                           offset_expr.X_add_symbol, (offsetT) 0,
                            (char *) NULL);
              if (breg == 0)
                {
@@ -3882,7 +4014,7 @@ macro (ip)
                           "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,
+                              offset_expr.X_add_symbol, (offsetT) 0,
                               (char *) NULL);
            }
          else
@@ -3925,7 +4057,7 @@ macro (ip)
                           "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,
+                              offset_expr.X_add_symbol, (offsetT) 0,
                               (char *) NULL);
              used_at = 1;
            }
@@ -4008,7 +4140,7 @@ macro (ip)
                                          (breg == 0
                                           ? mips_warn_about_macros
                                           : 0)),
-                           offset_expr.X_add_symbol, (long) 0,
+                           offset_expr.X_add_symbol, (offsetT) 0,
                            (char *) NULL);
            }
          else if (expr1.X_add_number >= -0x8000
@@ -4025,7 +4157,7 @@ macro (ip)
                                          (breg == 0
                                           ? mips_warn_about_macros
                                           : 0)),
-                           offset_expr.X_add_symbol, (long) 0,
+                           offset_expr.X_add_symbol, (offsetT) 0,
                            (char *) NULL);
            }
          else
@@ -4076,7 +4208,7 @@ macro (ip)
                                          (breg == 0
                                           ? mips_warn_about_macros
                                           : 0)),
-                           offset_expr.X_add_symbol, (long) 0,
+                           offset_expr.X_add_symbol, (offsetT) 0,
                            (char *) NULL);
 
              used_at = 1;
@@ -4242,7 +4374,8 @@ macro (ip)
                           "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);
+                           offset_expr.X_add_symbol, (offsetT) 0,
+                           (char *) NULL);
            }
          else
            {
@@ -4266,7 +4399,8 @@ macro (ip)
              p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
                            RELAX_ENCODE (16, 12 + gpdel, gpdel, 8 + gpdel,
                                          0, 0),
-                           offset_expr.X_add_symbol, (long) 0, (char *) NULL);
+                           offset_expr.X_add_symbol, (offsetT) 0,
+                           (char *) NULL);
              if (gpdel > 0)
                {
                  macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
@@ -4329,18 +4463,22 @@ macro (ip)
       goto ld;
     case M_LWC0_AB:
       s = "lwc0";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto ld;
     case M_LWC1_AB:
       s = "lwc1";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto ld;
     case M_LWC2_AB:
       s = "lwc2";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto ld;
     case M_LWC3_AB:
       s = "lwc3";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto ld;
     case M_LWL_AB:
@@ -4353,14 +4491,17 @@ macro (ip)
       goto ld;
     case M_LDC1_AB:
       s = "ldc1";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto ld;
     case M_LDC2_AB:
       s = "ldc2";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto ld;
     case M_LDC3_AB:
       s = "ldc3";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto ld;
     case M_LDL_AB:
@@ -4402,18 +4543,22 @@ macro (ip)
       goto st;
     case M_SWC0_AB:
       s = "swc0";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto st;
     case M_SWC1_AB:
       s = "swc1";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto st;
     case M_SWC2_AB:
       s = "swc2";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto st;
     case M_SWC3_AB:
       s = "swc3";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto st;
     case M_SWL_AB:
@@ -4431,13 +4576,16 @@ macro (ip)
     case M_SDC1_AB:
       s = "sdc1";
       coproc = 1;
+      /* Itbl support may require additional care here. */
       goto st;
     case M_SDC2_AB:
       s = "sdc2";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto st;
     case M_SDC3_AB:
       s = "sdc3";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto st;
     case M_SDL_AB:
@@ -4449,6 +4597,7 @@ macro (ip)
       tempreg = AT;
       used_at = 1;
     ld_st:
+      /* Itbl support may require additional care here. */
       if (mask == M_LWC1_AB
          || mask == M_SWC1_AB
          || mask == M_LDC1_AB
@@ -4505,7 +4654,7 @@ macro (ip)
                                RELAX_ENCODE (4, 8, 0, 4, 0,
                                              (mips_warn_about_macros
                                               || (used_at && mips_noat))),
-                               offset_expr.X_add_symbol, (long) 0,
+                               offset_expr.X_add_symbol, (offsetT) 0,
                                (char *) NULL);
                  used_at = 0;
                }
@@ -4530,7 +4679,7 @@ macro (ip)
                               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,
+                               offset_expr.X_add_symbol, (offsetT) 0,
                                (char *) NULL);
                }
              macro_build_lui (p, &icnt, &offset_expr, tempreg);
@@ -4575,7 +4724,7 @@ macro (ip)
          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,
+                       offset_expr.X_add_symbol, (offsetT) 0,
                        (char *) NULL);
          macro_build (p, &icnt, &offset_expr,
                       mips_isa < 3 ? "addiu" : "daddiu",
@@ -4629,7 +4778,7 @@ macro (ip)
                       tempreg);
          p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
                        RELAX_ENCODE (12, 12 + gpdel, gpdel, 8 + gpdel, 0, 0),
-                       offset_expr.X_add_symbol, (long) 0, (char *) NULL);
+                       offset_expr.X_add_symbol, (offsetT) 0, (char *) NULL);
          if (gpdel > 0)
            {
              macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
@@ -4847,6 +4996,7 @@ macro (ip)
        * But, the resulting address is the same after relocation so why
        * generate the extra instruction?
        */
+      /* Itbl support may require additional care here. */
       coproc = 1;
       if (mips_isa >= 2)
        {
@@ -4867,6 +5017,7 @@ macro (ip)
 
       s = "swc1";
       fmt = "T,o(b)";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto ldd_std;
 
@@ -4902,6 +5053,7 @@ macro (ip)
       /* 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.  */
+      /* Itbl support may require additional care here. */
       if (! target_big_endian)
        coproc = 0;
 
@@ -4950,6 +5102,7 @@ macro (ip)
                  used_at = 1;
                }
 
+              /* Itbl support may require additional care here. */
              macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
                           coproc ? treg + 1 : treg,
                           (int) BFD_RELOC_MIPS_GPREL, tempreg);
@@ -4959,6 +5112,7 @@ macro (ip)
                  undesired nop.  */
              hold_mips_optimize = mips_optimize;
              mips_optimize = 2;
+              /* Itbl support may require additional care here. */
              macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
                           coproc ? treg : treg + 1,
                           (int) BFD_RELOC_MIPS_GPREL, tempreg);
@@ -4967,7 +5121,7 @@ macro (ip)
              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,
+                           offset_expr.X_add_symbol, (offsetT) 0,
                            (char *) NULL);
 
              /* We just generated two relocs.  When tc_gen_reloc
@@ -4992,6 +5146,7 @@ macro (ip)
              if (p != NULL)
                p += 4;
            }
+          /* Itbl support may require additional care here. */
          macro_build (p, &icnt, &offset_expr, s, fmt,
                       coproc ? treg + 1 : treg,
                       (int) BFD_RELOC_LO16, AT);
@@ -4999,6 +5154,7 @@ macro (ip)
            p += 4;
          /* FIXME: How do we handle overflow here?  */
          offset_expr.X_add_number += 4;
+          /* Itbl support may require additional care here. */
          macro_build (p, &icnt, &offset_expr, s, fmt,
                       coproc ? treg : treg + 1,
                       (int) BFD_RELOC_LO16, AT);
@@ -5039,6 +5195,7 @@ macro (ip)
            macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                         mips_isa < 3 ? "addu" : "daddu",
                         "d,v,t", AT, breg, AT);
+          /* Itbl support may require additional care here. */
          macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
                       coproc ? treg + 1 : treg,
                       (int) BFD_RELOC_LO16, AT);
@@ -5048,6 +5205,7 @@ macro (ip)
              nop.  */
          hold_mips_optimize = mips_optimize;
          mips_optimize = 2;
+          /* Itbl support may require additional care here. */
          macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
                       coproc ? treg : treg + 1,
                       (int) BFD_RELOC_LO16, AT);
@@ -5055,7 +5213,7 @@ macro (ip)
 
          (void) frag_var (rs_machine_dependent, 0, 0,
                           RELAX_ENCODE (0, 0, -16 - off, -8, 1, 0),
-                          offset_expr.X_add_symbol, (long) 0,
+                          offset_expr.X_add_symbol, (offsetT) 0,
                           (char *) NULL);
        }
       else if (mips_pic == SVR4_PIC)
@@ -5105,6 +5263,7 @@ macro (ip)
            macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                         mips_isa < 3 ? "addu" : "daddu",
                         "d,v,t", AT, breg, AT);
+          /* Itbl support may require additional care here. */
          macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
                       coproc ? treg + 1 : treg,
                       (int) BFD_RELOC_LO16, AT);
@@ -5114,6 +5273,7 @@ macro (ip)
              nop.  */
          hold_mips_optimize = mips_optimize;
          mips_optimize = 2;
+          /* Itbl support may require additional care here. */
          macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
                       coproc ? treg : treg + 1,
                       (int) BFD_RELOC_LO16, AT);
@@ -5123,7 +5283,7 @@ macro (ip)
          p = frag_var (rs_machine_dependent, 16 + gpdel + off, 0,
                        RELAX_ENCODE (24 + off, 16 + gpdel + off, gpdel,
                                      8 + gpdel + off, 1, 0),
-                       offset_expr.X_add_symbol, (long) 0,
+                       offset_expr.X_add_symbol, (offsetT) 0,
                        (char *) NULL);
          if (gpdel > 0)
            {
@@ -5143,6 +5303,7 @@ macro (ip)
                           "d,v,t", AT, breg, AT);
              p += 4;
            }
+          /* Itbl support may require additional care here. */
          macro_build (p, &icnt, &expr1, s, fmt,
                       coproc ? treg + 1 : treg,
                       (int) BFD_RELOC_LO16, AT);
@@ -5153,6 +5314,7 @@ macro (ip)
              nop.  */
          hold_mips_optimize = mips_optimize;
          mips_optimize = 2;
+          /* Itbl support may require additional care here. */
          macro_build (p, &icnt, &expr1, s, fmt,
                       coproc ? treg : treg + 1,
                       (int) BFD_RELOC_LO16, AT);
@@ -5182,10 +5344,12 @@ macro (ip)
              used_at = 1;
            }
 
+          /* Itbl support may require additional care here. */
          macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
                       coproc ? treg + 1 : treg,
                       (int) BFD_RELOC_MIPS_GPREL, tempreg);
          offset_expr.X_add_number += 4;
+          /* Itbl support may require additional care here. */
          macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
                       coproc ? treg : treg + 1,
                       (int) BFD_RELOC_MIPS_GPREL, tempreg);
@@ -5211,8 +5375,59 @@ macro (ip)
       macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg + 1,
                   (int) BFD_RELOC_LO16, breg);
       return;
+
+   /* New code added to support COPZ instructions.
+      This code builds table entries out of the macros in mip_opcodes.
+      R4000 uses interlocks to handle coproc delays.
+      Other chips (like the R3000) require nops to be inserted for delays.
+
+      FIXME: Currently, we require that the user handle delays.
+      In order to fill delay slots for non-interlocked chips,
+      we must have a way to specify delays based on the coprocessor.
+      Eg. 4 cycles if load coproc reg from memory, 1 if in cache, etc.
+      What are the side-effects of the cop instruction?
+      What cache support might we have and what are its effects?
+      Both coprocessor & memory require delays. how long???
+      What registers are read/set/modified? 
+
+      If an itbl is provided to interpret cop instructions,
+      this knowledge can be encoded in the itbl spec. */
+
+    case M_COP0:
+      s = "cop0";
+      goto copz;
+    case M_COP1:
+      s = "cop1";
+      goto copz;
+    case M_COP2:
+      s = "cop2";
+      goto copz;
+    case M_COP3:
+      s = "cop3";
+    copz:
+      /* For now we just do C (same as Cz). */
+      macro_build ((char *) NULL, &icnt, &offset_expr, s, "C");
+      return;
+
 #ifdef LOSING_COMPILER
     default:
+      /* Try and see if this is a new itbl instruction.
+         This code builds table entries out of the macros in mip_opcodes.
+         FIXME: For now we just assemble the expression and pass it's
+         value along as a 32-bit immediate.
+         We may want to have the assembler assemble this value, 
+         so that we gain the assembler's knowledge of delay slots,
+         symbols, etc.
+         Would it be more efficient to use mask (id) here? */
+      if (itbl_have_entries 
+         && (immed_expr = itbl_assemble (ip->insn_mo->name, "")))
+        {
+         s = ip->insn_mo->name;
+         s2 = "cop3";
+         coproc = ITBL_DECODE_PNUM (immed_expr);;
+         macro_build ((char *) NULL, &icnt, &immed_expr, s, "C");
+         return;
+        }
       macro2 (ip);
       return;
     }
@@ -5875,6 +6090,8 @@ macro2 (ip)
       break;
 
     default:
+      /* FIXME: Check if this is one of the itbl macros, since they
+        are added dynamically. */
       as_bad ("Macro %s not implemented yet", ip->insn_mo->name);
       break;
     }
@@ -6135,20 +6352,11 @@ mips_ip (str, ip)
 
   insn_error = NULL;
 
-  for (s = str; islower (*s) || (*s >= '0' && *s <= '3') || *s == '6' || *s == '.'; ++s)
+  for (s = str; *s != '\0' && !isspace(*s); ++s)
     continue;
-  switch (*s)
-    {
-    case '\0':
-      break;
-
-    case ' ':
-      *s++ = '\0';
-      break;
+  if (isspace (*s))
+    *s++ = '\0';
 
-    default:
-      as_fatal ("Unknown opcode: `%s'", str);
-    }
   if ((insn = (struct mips_opcode *) hash_find (op_hash, str)) == NULL)
     {
       insn_error = "unrecognized opcode";
@@ -6402,6 +6610,31 @@ mips_ip (str, ip)
                          s += 4;
                          regno = KT1;
                        }
+                     else if (itbl_have_entries)
+                       {
+                         char *p, *n;
+                         int r;
+
+                         p = s+1;      /* advance past '$' */
+                         n = itbl_get_field (&p);  /* n is name */
+
+                         /* See if this is a register defined in an 
+                            itbl entry */
+                         r = itbl_get_reg_val (n);
+                         if (r)
+                           {
+                             /* Get_field advances to the start of
+                                the next field, so we need to back
+                                rack to the end of the last field. */
+                             if (p) 
+                               s = p - 1;
+                             else 
+                               s = strchr (s,'\0');
+                             regno = r;
+                           }
+                         else
+                           goto notreg;
+                         }
                      else
                        goto notreg;
                    }
@@ -6425,6 +6658,9 @@ mips_ip (str, ip)
                  /* 'z' only matches $0.  */
                  if (c == 'z' && regno != 0)
                    break;
+
+       /* Now that we have assembled one operand, we use the args string 
+        * to figure out where it goes in the instruction. */
                  switch (c)
                    {
                    case 'r':
@@ -6457,6 +6693,11 @@ mips_ip (str, ip)
                         is $0.  This only matches $0, and is checked
                         outside the switch.  */
                      break;
+                   case 'D':
+                     /* Itbl operand; not yet implemented. FIXME ?? */
+                     break;
+                     /* What about all other operands like 'i', which
+                        can be specified in the opcode table? */
                    }
                  lastregno = regno;
                  continue;
@@ -6675,8 +6916,9 @@ mips_ip (str, ip)
                                              | SEC_LOAD
                                              | SEC_READONLY
                                              | SEC_DATA));
-                   frag_align (*args == 'l' ? 2 : 3, 0);
-                   if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+                   frag_align (*args == 'l' ? 2 : 3, 0, 0);
+                   if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+                       && strcmp (TARGET_OS, "elf") != 0)
                      record_alignment (new_seg, 4);
                    else
                      record_alignment (new_seg, *args == 'l' ? 2 : 3);
@@ -7396,6 +7638,21 @@ mips16_ip (str, ip)
              }
            continue;
 
+           case 'e':           /* extend code */
+             my_getExpression (&imm_expr, s);
+             check_absolute_expr (ip, &imm_expr);
+             if ((unsigned long) imm_expr.X_add_number > 0x7ff)
+               {
+                 as_warn ("Invalid value for `%s' (%lu)",
+                          ip->insn_mo->name,
+                          (unsigned long) imm_expr.X_add_number);
+                 imm_expr.X_add_number &= 0x7ff;
+               }
+             ip->insn_opcode |= imm_expr.X_add_number;
+             imm_expr.X_op = O_absent;
+             s = expr_end;
+             continue;
+
            default:
              internalError ();
            }
@@ -7818,12 +8075,12 @@ struct option md_longopts[] = {
   {"mips16", no_argument, NULL, OPTION_MIPS16},
 #define OPTION_NO_MIPS16 (OPTION_MD_BASE + 23)
   {"no-mips16", no_argument, NULL, OPTION_NO_MIPS16},
-  /* start-sanitize-5900 */
+  /* start-sanitize-r5900 */
 #define OPTION_M5900 (OPTION_MD_BASE + 24)
   {"m5900", no_argument, NULL, OPTION_M5900},
 #define OPTION_NO_M5900 (OPTION_MD_BASE + 25)
   {"no-m5900", no_argument, NULL, OPTION_NO_M5900},
-  /* end-sanitize-5900 */
+  /* end-sanitize-r5900 */
 
 #define OPTION_CALL_SHARED (OPTION_MD_BASE + 7)
 #define OPTION_NON_SHARED (OPTION_MD_BASE + 8)
@@ -7882,7 +8139,7 @@ md_parse_option (c, arg)
          optimizations which limit full symbolic debugging.  We take
          that to be equivalent to -O0.  */
       if (mips_debug == 2)
-       mips_optimize = 0;
+       mips_optimize = 1;
       break;
 
     case OPTION_MIPS1:
@@ -8070,12 +8327,12 @@ md_parse_option (c, arg)
 
     case OPTION_MIPS16:
       mips16 = 1;
-      mips_no_prev_insn ();
+      mips_no_prev_insn (false);
       break;
 
     case OPTION_NO_MIPS16:
       mips16 = 0;
-      mips_no_prev_insn ();
+      mips_no_prev_insn (false);
       break;
 
     case OPTION_MEMBEDDED_PIC:
@@ -8210,6 +8467,14 @@ MIPS options:\n\
 #endif
 }
 \f
+void
+mips_init_after_args ()
+{
+  /* initialize opcodes */
+  bfd_mips_num_opcodes = bfd_mips_num_builtin_opcodes;
+  mips_opcodes = (struct mips_opcode*) mips_builtin_opcodes;
+}
+
 long
 md_pcrel_from (fixP)
      fixS *fixP;
@@ -8258,6 +8523,24 @@ cons_fix_new_mips (frag, where, nbytes, exp)
                : (nbytes == 4 ? BFD_RELOC_32 : BFD_RELOC_64)));
 }
 
+/* This is called before the symbol table is processed.  In order to
+   work with gcc when using mips-tfile, we must keep all local labels.
+   However, in other cases, we want to discard them.  If we were
+   called with -g, but we didn't see any debugging information, it may
+   mean that gcc is smuggling debugging information through to
+   mips-tfile, in which case we must generate all local labels.  */
+
+void
+mips_frob_file_before_adjust ()
+{
+#ifndef NO_ECOFF_DEBUGGING
+  if (ECOFF_DEBUGGING
+      && mips_debug != 0
+      && ! ecoff_debugging_seen)
+    flag_keep_locals = 1;
+#endif
+}
+
 /* Sort any unmatched HI16_S relocs so that they immediately precede
    the corresponding LO reloc.  This is called before md_apply_fix and
    tc_gen_reloc.  Unmatched HI16_S relocs can only be generated by
@@ -8435,7 +8718,7 @@ md_apply_fix (fixP, valueP)
 
     case BFD_RELOC_PCREL_HI16_S:
       /* The addend for this is tricky if it is internal, so we just
-        do everything here rather than in bfd_perform_relocation.  */
+        do everything here rather than in bfd_install_relocation.  */
       if ((fixP->fx_addsy->bsym->flags & BSF_SECTION_SYM) == 0)
        {
          /* For an external symbol adjust by the address to make it
@@ -8455,7 +8738,7 @@ md_apply_fix (fixP, valueP)
 
     case BFD_RELOC_PCREL_LO16:
       /* The addend for this is tricky if it is internal, so we just
-        do everything here rather than in bfd_perform_relocation.  */
+        do everything here rather than in bfd_install_relocation.  */
       if ((fixP->fx_addsy->bsym->flags & BSF_SECTION_SYM) == 0)
        value += fixP->fx_frag->fr_address + fixP->fx_where;
       buf = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
@@ -8582,7 +8865,7 @@ md_apply_fix (fixP, valueP)
                  handle these cases, but it appears to do it
                  incorrectly.  */
              as_bad_where (fixP->fx_file, fixP->fx_line,
-                           "Relocation overflow");
+                           "Branch out of range");
            }
        }
 
@@ -8708,7 +8991,7 @@ mips_align (to, fill, label)
      symbolS *label;
 {
   mips_emit_delays (false);
-  frag_align (to, fill);
+  frag_align (to, fill, 0);
   record_alignment (now_seg, to);
   if (label != NULL)
     {
@@ -8998,10 +9281,13 @@ s_mipsset (x)
 
   if (strcmp (name, "reorder") == 0)
     {
-      if (mips_noreorder)
+      if (mips_noreorder && prev_nop_frag != NULL)
        {
-         prev_insn_unreordered = 1;
-         prev_prev_insn_unreordered = 1;
+         /* If we still have pending nops, we can discard them.  The
+            usual nop handling will insert any that are still
+            needed. */
+         prev_nop_frag->fr_fix -= prev_nop_frag_holds * (mips16 ? 2 : 4);
+         prev_nop_frag = NULL;
        }
       mips_noreorder = 0;
     }
@@ -9508,10 +9794,16 @@ mips16_extended_frag (fragp, sec, stretch)
        {
          fragS *f;
 
-         /* Adjust stretch for any alignment frag.  */
-         for (f = fragp; f != fragp->fr_symbol->sy_frag; f = f->fr_next)
+         /* Adjust stretch for any alignment frag.  Note that if have
+             been expanding the earlier code, the symbol may be
+             defined in what appears to be an earlier frag.  FIXME:
+             This doesn't handle the fr_subtype field, which specifies
+             a maximum number of bytes to skip when doing an
+             alignment.  */
+         for (f = fragp;
+              f != NULL && f != fragp->fr_symbol->sy_frag;
+              f = f->fr_next)
            {
-             assert (f != NULL);
              if (f->fr_type == rs_align || f->fr_type == rs_align_code)
                {
                  if (stretch < 0)
@@ -9523,7 +9815,8 @@ mips16_extended_frag (fragp, sec, stretch)
                    break;
                }
            }
-         val += stretch;
+         if (f != NULL)
+           val += stretch;
        }
 
       addr = fragp->fr_address + fragp->fr_fix;
@@ -9531,12 +9824,18 @@ mips16_extended_frag (fragp, sec, stretch)
       /* The base address rules are complicated.  The base address of
          a branch is the following instruction.  The base address of a
          PC relative load or add is the instruction itself, but if it
-         is extended add 2, and if it is in a delay slot (in which
-         case it can not be extended) use the address of the
-         instruction whose delay slot it is in.  */
+         is in a delay slot (in which case it can not be extended) use
+         the address of the instruction whose delay slot it is in.  */
       if (type == 'p' || type == 'q')
        {
          addr += 2;
+
+         /* If we are currently assuming that this frag should be
+            extended, then, the current address is two bytes
+            higher. */
+         if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
+           addr += 2;
+
          /* Ignore the low bit in the target, since it will be set
              for a text label.  */
          if ((val & 1) != 0)
@@ -9547,11 +9846,6 @@ mips16_extended_frag (fragp, sec, stretch)
       else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype))
        addr -= 2;
 
-      /* If we are currently assuming that this frag should be
-         extended, then the current address is two bytes higher.  */
-      if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
-       addr += 2;
-
       val -= addr & ~ ((1 << op->shift) - 1);
 
       /* Branch offsets have an implicit 0 in the lowest bit.  */
@@ -9635,7 +9929,26 @@ md_estimate_size_before_relax (fragp, segtype)
     }
   else if (mips_pic == SVR4_PIC)
     {
-      asection *symsec = fragp->fr_symbol->bsym->section;
+      symbolS *sym;
+      asection *symsec;
+
+      sym = fragp->fr_symbol;
+
+      /* Handle the case of a symbol equated to another symbol.  */
+      while (sym->sy_value.X_op == O_symbol
+            && (! S_IS_DEFINED (sym) || S_IS_COMMON (sym)))
+       {
+         symbolS *n;
+
+         /* It's possible to get a loop here in a badly written
+             program.  */
+         n = sym->sy_value.X_add_symbol;
+         if (n == sym)
+           break;
+         sym = n;
+       }
+
+      symsec = S_GET_SEGMENT (sym);
 
       /* This must duplicate the test in adjust_reloc_syms.  */
       change = (symsec != &bfd_und_section
@@ -9682,7 +9995,8 @@ mips_fix_adjustable (fixp)
     return 1;
 #ifdef S_GET_OTHER
   if (OUTPUT_FLAVOR == bfd_target_elf_flavour
-      && S_GET_OTHER (fixp->fx_addsy) == STO_MIPS16)
+      && S_GET_OTHER (fixp->fx_addsy) == STO_MIPS16
+      && fixp->fx_subsy == NULL)
     return 0;
 #endif
   return 1;
@@ -9970,6 +10284,8 @@ md_convert_frag (abfd, asec, fragp)
          if (type == 'p' || type == 'q')
            {
              addr += 2;
+             if (ext)
+               addr += 2;
              /* Ignore the low bit in the target, since it will be
                  set for a text label.  */
              if ((val & 1) != 0)
@@ -9980,8 +10296,6 @@ md_convert_frag (abfd, asec, fragp)
          else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype))
            addr -= 2;
 
-         if (ext)
-           addr += 2;
          addr &= ~ (addressT) ((1 << op->shift) - 1);
          val -= addr;
 
@@ -10090,33 +10404,6 @@ mips_define_label (sym)
   l->next = insn_labels;
   insn_labels = l;
 }
-
-/* Decide whether a label is local.  This is called by LOCAL_LABEL.
-   In order to work with gcc when using mips-tfile, we must keep all
-   local labels.  However, in other cases, we want to discard them,
-   since they are useless.  */
-
-int
-mips_local_label (name)
-     const char *name;
-{
-#ifndef NO_ECOFF_DEBUGGING
-  if (ECOFF_DEBUGGING
-      && mips_debug != 0
-      && ! ecoff_debugging_seen)
-    {
-      /* We were called with -g, but we didn't see any debugging
-         information.  That may mean that gcc is smuggling debugging
-         information through to mips-tfile, in which case we must
-         generate all local labels.  */
-      return 0;
-    }
-#endif
-
-  /* Here it's OK to discard local labels.  */
-
-  return name[0] == '$';
-}
 \f
 #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)