Thu Mar 20 13:42:01 1997 H.J. Lu <hjl@lucon.org>
[binutils-gdb.git] / gas / config / tc-mips.c
index a788dcced7ceb2bdeae1a369dbe6546c27ae7282..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")
@@ -148,12 +162,18 @@ static int mips_4010 = -1;
 /* Whether the 4100 MADD16 and DMADD16 are permitted. */
 static int mips_4100 = -1;
 
+/* start-sanitize-r5900 */
+/* Whether Toshiba r5900 instructions are permitted. */
+static int mips_5900 = -1;
+/* end-sanitize-r5900 */
+
 /* Whether the processor uses hardware interlocks, and thus does not
    require nops to be inserted.  */
 static int interlocks = -1;
 
 /* As with "interlocks" this is used by hardware that has FP
    (co-processor) interlocks.  */
+/* Itbl support may require additional care here. */
 static int cop_interlocks = -1;
 
 /* MIPS PIC level.  */
@@ -319,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
@@ -498,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,
@@ -543,6 +579,7 @@ static void s_cpload PARAMS ((int));
 static void s_cprestore PARAMS ((int));
 static void s_gpword PARAMS ((int));
 static void s_cpadd PARAMS ((int));
+static void s_insn PARAMS ((int));
 static void md_obj_begin PARAMS ((void));
 static void md_obj_end PARAMS ((void));
 static long get_number PARAMS ((void));
@@ -582,6 +619,7 @@ static const pseudo_typeS mips_pseudo_table[] =
   {"cprestore", s_cprestore, 0},
   {"gpword", s_gpword, 0},
   {"cpadd", s_cpadd, 0},
+  {"insn", s_insn, 0},
 
  /* Relatively generic pseudo-ops that happen to be used on MIPS
      chips.  */
@@ -780,6 +818,18 @@ md_begin ()
          if (mips_cpu == -1)
            mips_cpu = 5000;
        }
+      /* start-sanitize-r5900 */
+      else if (strcmp (cpu, "r5900") == 0
+              || strcmp (cpu, "mips64vr5900") == 0
+               || strcmp (cpu, "mips64vr5900el") == 0)
+       {
+         mips_isa = 3;
+         if (mips_cpu == -1)
+           mips_cpu = 5900;
+          if (mips_5900 == -1)
+            mips_5900 = 1;
+       }
+      /* end-sanitize-r5900 */
       else if (strcmp (cpu, "r8000") == 0
               || strcmp (cpu, "mips4") == 0)
        {
@@ -827,11 +877,17 @@ md_begin ()
   if (mips_4100 < 0)
     mips_4100 = 0;
 
-  if (mips_4650 || mips_4010 || mips_4100 || mips_cpu == 4300)
+  /* start-sanitize-r5900 */
+  if (mips_5900 < 0)
+    mips_5900 = 0;
+  /* end-sanitize-r5900 */
+
+  if (mips_4010 || mips_4100 || mips_cpu == 4300)
     interlocks = 1;
   else
     interlocks = 0;
 
+  /* Itbl support may require additional care here. */
   if (mips_cpu == 4300)
     cop_interlocks = 1;
   else
@@ -912,7 +968,32 @@ md_begin ()
             && strcmp (mips16_opcodes[i].name, name) == 0);
     }
 
-  mips_no_prev_insn ();
+  /* We add all the general register names to the symbol table.  This
+     helps us detect invalid uses of them.  */
+  for (i = 0; i < 32; i++)
+    {
+      char buf[5];
+
+      sprintf (buf, "$%d", i);
+      symbol_table_insert (symbol_new (buf, reg_section, i,
+                                      &zero_address_frag));
+    }
+  symbol_table_insert (symbol_new ("$fp", reg_section, FP,
+                                  &zero_address_frag));
+  symbol_table_insert (symbol_new ("$sp", reg_section, SP,
+                                  &zero_address_frag));
+  symbol_table_insert (symbol_new ("$gp", reg_section, GP,
+                                  &zero_address_frag));
+  symbol_table_insert (symbol_new ("$at", reg_section, AT,
+                                  &zero_address_frag));
+  symbol_table_insert (symbol_new ("$kt0", reg_section, KT0,
+                                  &zero_address_frag));
+  symbol_table_insert (symbol_new ("$kt1", reg_section, KT1,
+                                  &zero_address_frag));
+  symbol_table_insert (symbol_new ("$pc", reg_section, -1,
+                                  &zero_address_frag));
+
+  mips_no_prev_insn (false);
 
   mips_gprmask = 0;
   mips_cprmask[0] = 0;
@@ -943,20 +1024,25 @@ md_begin ()
       {
        segT seg;
        subsegT subseg;
+       flagword flags;
        segT sec;
 
        seg = now_seg;
        subseg = now_subseg;
 
+       /* The ABI says this section should be loaded so that the
+          running program can access it.  However, we don't load it
+          if we are configured for an embedded target */
+       flags = SEC_READONLY | SEC_DATA;
+       if (strcmp (TARGET_OS, "elf") != 0)
+         flags |= SEC_ALLOC | SEC_LOAD;
+
        if (! mips_64)
          {
            sec = subseg_new (".reginfo", (subsegT) 0);
 
-           /* The ABI says this section should be loaded so that the
-              running program can access it.  */
-           (void) bfd_set_section_flags (stdoutput, sec,
-                                         (SEC_ALLOC | SEC_LOAD
-                                          | SEC_READONLY | SEC_DATA));
+
+           (void) bfd_set_section_flags (stdoutput, sec, flags);
            (void) bfd_set_section_alignment (stdoutput, sec, 2);
        
 #ifdef OBJ_ELF
@@ -968,9 +1054,7 @@ md_begin ()
            /* The 64-bit ABI uses a .MIPS.options section rather than
                .reginfo section.  */
            sec = subseg_new (".MIPS.options", (subsegT) 0);
-           (void) bfd_set_section_flags (stdoutput, sec,
-                                         (SEC_ALLOC | SEC_LOAD
-                                          | SEC_READONLY | SEC_DATA));
+           (void) bfd_set_section_flags (stdoutput, sec, flags);
            (void) bfd_set_section_alignment (stdoutput, sec, 3);
 
 #ifdef OBJ_ELF
@@ -1031,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)
     {
@@ -1153,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;
@@ -1203,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.
@@ -1244,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,
@@ -1273,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
@@ -1299,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)))
@@ -1317,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;
@@ -1342,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
@@ -1350,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;
@@ -1415,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.  */
@@ -1428,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)
@@ -1441,7 +1590,15 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
       f = frag_more (2);
     }
   else
-    f = frag_more (4);
+    {
+      if (mips16
+         && mips_noreorder
+         && (prev_pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
+       as_warn ("extended instruction in delay slot");
+
+      f = frag_more (4);
+    }
+
   fixp = NULL;
   if (address_expr != NULL && reloc_type < BFD_RELOC_UNUSED)
     {
@@ -1458,10 +1615,16 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
              break;
 
            case BFD_RELOC_MIPS_JMP:
+             if ((address_expr->X_add_number & 3) != 0)
+               as_bad ("jump to misaligned address (0x%lx)",
+                       (unsigned long) address_expr->X_add_number);
              ip->insn_opcode |= (address_expr->X_add_number >> 2) & 0x3ffffff;
              break;
 
            case BFD_RELOC_MIPS16_JMP:
+             if ((address_expr->X_add_number & 3) != 0)
+               as_bad ("jump to misaligned address (0x%lx)",
+                       (unsigned long) address_expr->X_add_number);
              ip->insn_opcode |=
                (((address_expr->X_add_number & 0x7c0000) << 3)
                 | ((address_expr->X_add_number & 0xf800000) >> 7)
@@ -1502,8 +1665,13 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
        }
     }
 
-  if (! mips16 || reloc_type == BFD_RELOC_MIPS16_JMP)
+  if (! mips16)
     md_number_to_chars (f, ip->insn_opcode, 4);
+  else if (reloc_type == BFD_RELOC_MIPS16_JMP)
+    {
+      md_number_to_chars (f, ip->insn_opcode >> 16, 2);
+      md_number_to_chars (f + 2, ip->insn_opcode & 0xffff, 2);
+    }
   else
     {
       if (ip->use_extend)
@@ -1535,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;
@@ -1636,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)))
@@ -1647,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
@@ -1751,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
@@ -1809,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;
@@ -1838,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);
@@ -1915,8 +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_prev_insn_unreordered = prev_insn_unreordered;
+      prev_insn_unreordered = 1;
     }
 
   /* We just output an insn, so the next one doesn't have a label.  */
@@ -1924,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;
@@ -1952,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
@@ -1972,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
@@ -1980,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
@@ -1989,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);
@@ -2029,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
@@ -2102,7 +2302,12 @@ macro_build (place, counter, ep, name, fmt, va_alist)
         || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_4010
             && ! mips_4010)
         || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_4100
-            && ! mips_4100))
+            && ! mips_4100)
+        /* start-sanitize-r5900 */
+         || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_5900
+            && ! mips_5900)
+         /* end-sanitize-r5900 */
+         )
     {
       ++insn.insn_mo;
       assert (insn.insn_mo->name);
@@ -2552,8 +2757,8 @@ load_register (counter, reg, ep, dbl)
      expressionS *ep;
      int dbl;
 {
-  int shift, freg;
-  expressionS hi32, lo32, tmp;
+  int freg;
+  expressionS hi32, lo32;
 
   if (ep->X_op != O_big)
     {
@@ -2587,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,
@@ -2613,11 +2822,9 @@ load_register (counter, reg, ep, dbl)
   if (ep->X_op != O_big)
     {
       hi32 = *ep;
-      shift = 32;
-      hi32.X_add_number >>= shift;
+      hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
+      hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
       hi32.X_add_number &= 0xffffffff;
-      if ((hi32.X_add_number & 0x80000000) != 0)
-       hi32.X_add_number |= ~ (offsetT) 0xffffffff;
       lo32 = *ep;
       lo32.X_add_number &= 0xffffffff;
     }
@@ -2638,76 +2845,130 @@ load_register (counter, reg, ep, dbl)
     freg = 0;
   else
     {
+      int shift, bit;
+      unsigned long hi, lo;
+
       if (hi32.X_add_number == 0xffffffff)
         {
           if ((lo32.X_add_number & 0xffff8000) == 0xffff8000)
             {
-              macro_build ((char *) NULL, counter, &lo32, "addiu", "t,r,j", reg, 0,
-                           (int) BFD_RELOC_LO16);
+              macro_build ((char *) NULL, counter, &lo32, "addiu", "t,r,j",
+                          reg, 0, (int) BFD_RELOC_LO16);
               return;
             }
           if (lo32.X_add_number & 0x80000000)
             {
               macro_build ((char *) NULL, counter, &lo32, "lui", "t,u", reg,
                            (int) BFD_RELOC_HI16);
-              macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i", reg, reg,
-                           (int) BFD_RELOC_LO16);
+             if (lo32.X_add_number & 0xffff)
+               macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i",
+                            reg, reg, (int) BFD_RELOC_LO16);
               return;
             }
         }
 
-      /* Check for 16bit shifted constant: */
-      shift = 32;
-      tmp.X_add_number = hi32.X_add_number << shift | lo32.X_add_number;
-      /* We know that hi32 is non-zero, so start the mask on the first
-         bit of the hi32 value: */
+      /* Check for 16bit shifted constant.  We know that hi32 is
+         non-zero, so start the mask on the first bit of the hi32
+         value.  */
       shift = 17;
       do
        {
-         if ((tmp.X_add_number & ~((offsetT)0xffff << shift)) == 0)
-          {
-            tmp.X_op = O_constant;
-            tmp.X_add_number >>= shift;
-            macro_build ((char *) NULL, counter, &tmp, "ori", "t,r,i", reg, 0,
-                         (int) BFD_RELOC_LO16);
-            macro_build ((char *) NULL, counter, NULL,
-                         (shift >= 32) ? "dsll32" : "dsll",
-                         "d,w,<", reg, reg, (shift >= 32) ? shift - 32 : shift);
-            return;
-          }
+        unsigned long himask, lomask;
+
+        if (shift < 32)
+          {
+            himask = 0xffff >> (32 - shift);
+            lomask = (0xffff << shift) & 0xffffffff;
+          }
+        else
+          {
+            himask = 0xffff << (shift - 32);
+            lomask = 0;
+          }
+        if ((hi32.X_add_number & ~ (offsetT) himask) == 0
+            && (lo32.X_add_number & ~ (offsetT) lomask) == 0)
+          {
+            expressionS tmp;
+
+            tmp.X_op = O_constant;
+            if (shift < 32)
+              tmp.X_add_number = ((hi32.X_add_number << (32 - shift))
+                                  | (lo32.X_add_number >> shift));
+            else
+              tmp.X_add_number = hi32.X_add_number >> (shift - 32);
+            macro_build ((char *) NULL, counter, &tmp, "ori", "t,r,i", reg, 0,
+                         (int) BFD_RELOC_LO16);
+            macro_build ((char *) NULL, counter, NULL,
+                         (shift >= 32) ? "dsll32" : "dsll",
+                         "d,w,<", reg, reg,
+                         (shift >= 32) ? shift - 32 : shift);
+            return;
+          }
          shift++;
        } while (shift <= (64 - 16));
 
-      freg = 0;
-      shift = 32;
-      tmp.X_add_number = hi32.X_add_number << shift | lo32.X_add_number;
-      while ((tmp.X_add_number & 1) == 0)
-        {
-          tmp.X_add_number >>= 1;
-          freg++;
-        }
-      if (((tmp.X_add_number + 1) & tmp.X_add_number) == 0) /* (power-of-2 - 1) */
+      /* Find the bit number of the lowest one bit, and store the
+         shifted value in hi/lo.  */
+      hi = (unsigned long) (hi32.X_add_number & 0xffffffff);
+      lo = (unsigned long) (lo32.X_add_number & 0xffffffff);
+      if (lo != 0)
+       {
+         bit = 0;
+         while ((lo & 1) == 0)
+           {
+             lo >>= 1;
+             ++bit;
+           }
+         lo |= (hi & (((unsigned long) 1 << bit) - 1)) << (32 - bit);
+         hi >>= bit;
+       }
+      else
+       {
+         bit = 32;
+         while ((hi & 1) == 0)
+           {
+             hi >>= 1;
+             ++bit;
+           }
+         lo = hi;
+         hi = 0;
+       }
+
+      /* Optimize if the shifted value is a (power of 2) - 1.  */
+      if ((hi == 0 && ((lo + 1) & lo) == 0)
+         || (lo == 0xffffffff && ((hi + 1) & hi) == 0))
         {
-          shift = COUNT_TOP_ZEROES((unsigned int)hi32.X_add_number);
+          shift = COUNT_TOP_ZEROES ((unsigned int) hi32.X_add_number);
          if (shift != 0)
             {
+             expressionS tmp;
+
+             /* This instruction will set the register to be all
+                 ones.  */
               tmp.X_op = O_constant;
-              tmp.X_add_number = (offsetT)-1;
-              macro_build ((char *) NULL, counter, &tmp, "addiu", "t,r,j", reg, 0,
-                           (int) BFD_RELOC_LO16); /* set all ones */
-              if (freg != 0)
+              tmp.X_add_number = (offsetT) -1;
+              macro_build ((char *) NULL, counter, &tmp, "addiu", "t,r,j",
+                          reg, 0, (int) BFD_RELOC_LO16);
+              if (bit != 0)
                 {
-                  freg += shift;
+                  bit += shift;
                   macro_build ((char *) NULL, counter, NULL,
-                               (freg >= 32) ? "dsll32" : "dsll",
+                               (bit >= 32) ? "dsll32" : "dsll",
                                "d,w,<", reg, reg,
-                               (freg >= 32) ? freg - 32 : freg);
+                               (bit >= 32) ? bit - 32 : bit);
                 }
-              macro_build ((char *) NULL, counter, NULL, (shift >= 32) ? "dsrl32" : "dsrl",
-                           "d,w,<", reg, reg, (shift >= 32) ? shift - 32 : shift);
+              macro_build ((char *) NULL, counter, NULL,
+                          (shift >= 32) ? "dsrl32" : "dsrl",
+                           "d,w,<", reg, reg,
+                          (shift >= 32) ? shift - 32 : shift);
               return;
             }
         }
+
+      /* Sign extend hi32 before calling load_register, because we can
+         generally get better code when we load a sign extended value.  */
+      if ((hi32.X_add_number & 0x80000000) != 0)
+       hi32.X_add_number |= ~ (offsetT) 0xffffffff;
       load_register (counter, reg, &hi32, 0);
       freg = reg;
     }
@@ -2794,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)
@@ -2823,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);
@@ -2870,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
@@ -3665,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);
@@ -3729,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)
                {
@@ -3753,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
@@ -3796,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;
            }
@@ -3879,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
@@ -3896,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
@@ -3947,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;
@@ -4113,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
            {
@@ -4137,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", "");
@@ -4200,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:
@@ -4224,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:
@@ -4273,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:
@@ -4302,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:
@@ -4320,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
@@ -4376,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;
                }
@@ -4401,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);
@@ -4446,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",
@@ -4500,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", "");
@@ -4718,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)
        {
@@ -4738,6 +5017,7 @@ macro (ip)
 
       s = "swc1";
       fmt = "T,o(b)";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto ldd_std;
 
@@ -4773,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;
 
@@ -4821,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);
@@ -4830,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);
@@ -4838,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
@@ -4863,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);
@@ -4870,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);
@@ -4910,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);
@@ -4919,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);
@@ -4926,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)
@@ -4976,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);
@@ -4985,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);
@@ -4994,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)
            {
@@ -5014,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);
@@ -5024,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);
@@ -5053,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);
@@ -5082,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;
     }
@@ -5746,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;
     }
@@ -6006,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;
+  if (isspace (*s))
+    *s++ = '\0';
 
-    case ' ':
-      *s++ = '\0';
-      break;
-
-    default:
-      as_fatal ("Unknown opcode: `%s'", str);
-    }
   if ((insn = (struct mips_opcode *) hash_find (op_hash, str)) == NULL)
     {
       insn_error = "unrecognized opcode";
@@ -6044,12 +6381,18 @@ mips_ip (str, ip)
        insn_isa = 1;
 
       if (insn_isa > mips_isa
-         || ((insn->pinfo & INSN_ISA) == INSN_4650
-             && ! mips_4650)
-         || ((insn->pinfo & INSN_ISA) == INSN_4010
-             && ! mips_4010)
-         || ((insn->pinfo & INSN_ISA) == INSN_4100
-             && ! mips_4100))
+         || (insn->pinfo != INSN_MACRO
+             && (((insn->pinfo & INSN_ISA) == INSN_4650
+                  && ! mips_4650)
+                 || ((insn->pinfo & INSN_ISA) == INSN_4010
+                     && ! mips_4010)
+                 || ((insn->pinfo & INSN_ISA) == INSN_4100
+                     && ! mips_4100)
+                 /* start-sanitize-r5900 */
+                 || ((insn->pinfo & INSN_ISA) == INSN_5900
+                     && ! mips_5900)
+                 /* end-sanitize-r5900 */
+                 )))
        {
          if (insn + 1 < &mips_opcodes[NUMOPCODES]
              && strcmp (insn->name, insn[1].name) == 0)
@@ -6267,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;
                    }
@@ -6290,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':
@@ -6322,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;
@@ -6540,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);
@@ -7077,23 +7454,52 @@ mips16_ip (str, ip)
            case 'U':
            case 'k':
            case 'K':
-             if (s[0] == '$' && isdigit (s[1]))
+             if (s[0] == '%'
+                 && strncmp (s + 1, "gprel(", sizeof "gprel(" - 1) == 0)
                {
-                 /* Looks like a register name.  */
-                 break;
+                 /* This is %gprel(SYMBOL).  We need to read SYMBOL,
+                     and generate the appropriate reloc.  If the text
+                     inside %gprel is not a symbol name with an
+                     optional offset, then we generate a normal reloc
+                     and will probably fail later.  */
+                 my_getExpression (&imm_expr, s + sizeof "%gprel" - 1);
+                 if (imm_expr.X_op == O_symbol)
+                   {
+                     mips16_ext = true;
+                     imm_reloc = BFD_RELOC_MIPS16_GPREL;
+                     s = expr_end;
+                     ip->use_extend = true;
+                     ip->extend = 0;
+                     continue;
+                   }
+               }
+             else
+               {
+                 /* Just pick up a normal expression.  */
+                 my_getExpression (&imm_expr, s);
                }
 
-             if (s[0] == '('
-                 && args[1] == '('
-                 && s[1] == '$')
+             if (imm_expr.X_op == O_register)
                {
-                 /* It looks like the expression was omitted before a
-                     register indirection, which means that the
-                     expression is implicitly zero.  */
-                 continue;
+                 /* What we thought was an expression turned out to
+                     be a register.  */
+
+                 if (s[0] == '(' && args[1] == '(')
+                   {
+                     /* It looks like the expression was omitted
+                        before a register indirection, which means
+                        that the expression is implicitly zero.  We
+                        still set up imm_expr, so that we handle
+                        explicit extensions correctly.  */
+                     imm_expr.X_op = O_constant;
+                     imm_expr.X_add_number = 0;
+                     imm_reloc = (int) BFD_RELOC_UNUSED + c;
+                     continue;
+                   }
+
+                 break;
                }
 
-             my_getExpression (&imm_expr, s);
              /* We need to relax this instruction.  */
              imm_reloc = (int) BFD_RELOC_UNUSED + c;
              s = expr_end;
@@ -7107,12 +7513,11 @@ mips16_ip (str, ip)
              /* We use offset_reloc rather than imm_reloc for the PC
                  relative operands.  This lets macros with both
                  immediate and address operands work correctly.  */
-             if (s[0] == '$' && isdigit (s[1]))
-               {
-                 /* Looks like a register name.  */
-                 break;
-               }
              my_getExpression (&offset_expr, s);
+
+             if (offset_expr.X_op == O_register)
+               break;
+
              /* We need to relax this instruction.  */
              offset_reloc = (int) BFD_RELOC_UNUSED + c;
              s = expr_end;
@@ -7151,7 +7556,7 @@ mips16_ip (str, ip)
                  mask = 7 << 3;
                while (*s != '\0')
                  {
-                   int reg1, reg2;
+                   int freg, reg1, reg2;
 
                    while (*s == ' ' || *s == ',')
                      ++s;
@@ -7161,6 +7566,13 @@ mips16_ip (str, ip)
                        break;
                      }
                    ++s;
+                   if (*s != 'f')
+                     freg = 0;
+                   else
+                     {
+                       freg = 1;
+                       ++s;
+                     }
                    reg1 = 0;
                    while (isdigit (*s))
                      {
@@ -7178,6 +7590,16 @@ mips16_ip (str, ip)
                        if (*s != '$')
                          break;
                        ++s;
+                       if (freg)
+                         {
+                           if (*s == 'f')
+                             ++s;
+                           else
+                             {
+                               as_bad ("invalid register list");
+                               break;
+                             }
+                         }
                        reg2 = 0;
                        while (isdigit (*s))
                          {
@@ -7186,19 +7608,51 @@ mips16_ip (str, ip)
                            ++s;
                          }
                      }
-                   if (reg1 == 4 && reg2 >= 4 && reg2 <= 7 && c != 'L')
+                   if (freg && reg1 == 0 && reg2 == 0 && c == 'L')
+                     {
+                       mask &= ~ (7 << 3);
+                       mask |= 5 << 3;
+                     }
+                   else if (freg && reg1 == 0 && reg2 == 1 && c == 'L')
+                     {
+                       mask &= ~ (7 << 3);
+                       mask |= 6 << 3;
+                     }
+                   else if (reg1 == 4 && reg2 >= 4 && reg2 <= 7 && c != 'L')
                      mask |= (reg2 - 3) << 3;
                    else if (reg1 == 16 && reg2 >= 16 && reg2 <= 17)
                      mask |= (reg2 - 15) << 1;
                    else if (reg1 == 31 && reg2 == 31)
                      mask |= 1;
                    else
-                     as_bad ("invalid register list");
+                     {
+                       as_bad ("invalid register list");
+                       break;
+                     }
                  }
+               /* The mask is filled in in the opcode table for the
+                   benefit of the disassembler.  We remove it before
+                   applying the actual mask.  */
+               ip->insn_opcode &= ~ ((7 << 3) << MIPS16OP_SH_IMM6);
                ip->insn_opcode |= mask << MIPS16OP_SH_IMM6;
              }
            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 ();
            }
@@ -7495,6 +7949,19 @@ my_getExpression (ep, str)
   expression (ep);
   expr_end = input_line_pointer;
   input_line_pointer = save_in;
+
+  /* If we are in mips16 mode, and this is an expression based on `.',
+     then we bump the value of the symbol by 1 since that is how other
+     text symbols are handled.  We don't bother to handle complex
+     expressions, just `.' plus or minus a constant.  */
+  if (mips16
+      && ep->X_op == O_symbol
+      && strcmp (S_GET_NAME (ep->X_add_symbol), FAKE_LABEL_NAME) == 0
+      && S_GET_SEGMENT (ep->X_add_symbol) == now_seg
+      && ep->X_add_symbol->sy_frag == frag_now
+      && ep->X_add_symbol->sy_value.X_op == O_constant
+      && ep->X_add_symbol->sy_value.X_add_number == frag_now_fix ())
+    ++ep->X_add_symbol->sy_value.X_add_number;
 }
 
 /* Turn a string in input_line_pointer into a floating point constant
@@ -7608,6 +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-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-r5900 */
 
 #define OPTION_CALL_SHARED (OPTION_MD_BASE + 7)
 #define OPTION_NON_SHARED (OPTION_MD_BASE + 8)
@@ -7666,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:
@@ -7777,6 +8250,10 @@ md_parse_option (c, arg)
                    || strcmp (p, "5k") == 0
                    || strcmp (p, "5K") == 0)
                  mips_cpu = 5000;
+                /* start-sanitize-r5900 */
+                else if (strcmp (p, "5900") == 0)
+                  mips_cpu = 5900;
+                /* end-sanitize-r5900 */
                break;
 
              case '6':
@@ -7838,14 +8315,24 @@ md_parse_option (c, arg)
       mips_4100 = 0;
       break;
 
+      /* start-sanitize-r5900 */
+    case OPTION_M5900:
+      mips_5900 = 1;
+      break;
+
+    case OPTION_NO_M5900:
+      mips_5900 = 0;
+      break;
+      /* end-sanitize-r5900 */
+
     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:
@@ -7980,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;
@@ -8028,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
@@ -8151,6 +8664,25 @@ md_apply_fix (fixP, valueP)
          || fixP->fx_r_type == BFD_RELOC_64);
 
   value = *valueP;
+
+  /* If we aren't adjusting this fixup to be against the section
+     symbol, we need to adjust the value.  */
+#ifdef S_GET_OTHER
+  if (fixP->fx_addsy != NULL
+      && OUTPUT_FLAVOR == bfd_target_elf_flavour
+      && S_GET_OTHER (fixP->fx_addsy) == STO_MIPS16)
+    {
+      value -= S_GET_VALUE (fixP->fx_addsy);
+      if (value != 0 && ! fixP->fx_pcrel)
+       {
+         /* In this case, the bfd_install_relocation routine will
+             incorrectly add the symbol value back in.  We just want
+             the addend to appear in the object file.  */
+         value -= S_GET_VALUE (fixP->fx_addsy);
+       }
+    }
+#endif
+
   fixP->fx_addnumber = value;  /* Remember value for tc_gen_reloc */
 
   if (fixP->fx_addsy == NULL && ! fixP->fx_pcrel)
@@ -8170,6 +8702,7 @@ md_apply_fix (fixP, valueP)
     case BFD_RELOC_MIPS_GOT_LO16:
     case BFD_RELOC_MIPS_CALL_HI16:
     case BFD_RELOC_MIPS_CALL_LO16:
+    case BFD_RELOC_MIPS16_GPREL:
       if (fixP->fx_pcrel)
        as_bad_where (fixP->fx_file, fixP->fx_line,
                      "Invalid PC relative reloc");
@@ -8185,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
@@ -8205,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;
@@ -8286,8 +8819,8 @@ md_apply_fix (fixP, valueP)
        * the current segment).
        */
       if ((value & 0x3) != 0)
-       as_warn_where (fixP->fx_file, fixP->fx_line,
-                      "Branch to odd address (%lx)", value);
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     "Branch to odd address (%lx)", value);
       value >>= 2;
 
       /* update old instruction data */
@@ -8332,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");
            }
        }
 
@@ -8458,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)
     {
@@ -8748,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;
     }
@@ -8989,6 +9525,38 @@ s_cpadd (ignore)
   demand_empty_rest_of_line ();  
 }
 
+/* Handle the .insn pseudo-op.  This marks instruction labels in
+   mips16 mode.  This permits the linker to handle them specially,
+   such as generating jalx instructions when needed.  We also make
+   them odd for the duration of the assembly, in order to generate the
+   right sort of code.  We will make them even in the adjust_symtab
+   routine, while leaving them marked.  This is convenient for the
+   debugger and the disassembler.  The linker knows to make them odd
+   again.  */
+
+static void
+s_insn (ignore)
+     int ignore;
+{
+  if (mips16)
+    {
+      struct insn_label_list *l;
+
+      for (l = insn_labels; l != NULL; l = l->next)
+       {
+#ifdef S_SET_OTHER
+         if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+           S_SET_OTHER (l->label, STO_MIPS16);
+#endif
+         ++l->label->sy_value.X_add_number;
+       }
+
+      mips_clear_insn_labels ();
+    }
+
+  demand_empty_rest_of_line ();
+}
+
 /* Parse a register string into a number.  Called from the ECOFF code
    to parse .frame.  The argument is non-zero if this is the frame
    register, so that we can record it in mips_frame_reg.  */
@@ -9226,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)
@@ -9241,7 +9815,8 @@ mips16_extended_frag (fragp, sec, stretch)
                    break;
                }
            }
-         val += stretch;
+         if (f != NULL)
+           val += stretch;
        }
 
       addr = fragp->fr_address + fragp->fr_fix;
@@ -9249,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)
@@ -9265,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.  */
@@ -9353,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
@@ -9383,6 +9978,30 @@ md_estimate_size_before_relax (fragp, segtype)
     return RELAX_NEW (fragp->fr_subtype) - RELAX_OLD (fragp->fr_subtype);
 }
 
+/* This is called to see whether a reloc against a defined symbol
+   should be converted into a reloc against a section.  Don't adjust
+   MIPS16 jump relocations, so we don't have to worry about the format
+   of the offset in the .o file.  Don't adjust relocations against
+   mips16 symbols, so that the linker can find them if it needs to set
+   up a stub.  */
+
+int
+mips_fix_adjustable (fixp)
+     fixS *fixp;
+{
+  if (fixp->fx_r_type == BFD_RELOC_MIPS16_JMP)
+    return 0;
+  if (fixp->fx_addsy == NULL)
+    return 1;
+#ifdef S_GET_OTHER
+  if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+      && S_GET_OTHER (fixp->fx_addsy) == STO_MIPS16
+      && fixp->fx_subsy == NULL)
+    return 0;
+#endif
+  return 1;
+}
+
 /* Translate internal representation of relocation info to BFD target
    format.  */
 
@@ -9665,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)
@@ -9675,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;
 
@@ -9686,6 +10305,12 @@ md_convert_frag (abfd, asec, fragp)
            record_alignment (asec, op->shift);
        }
 
+      if (ext
+         && (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype)
+             || RELAX_MIPS16_DSLOT (fragp->fr_subtype)))
+       as_warn_where (fragp->fr_file, fragp->fr_line,
+                      "extended instruction in delay slot");
+
       buf = (bfd_byte *) (fragp->fr_literal + fragp->fr_fix);
 
       if (target_big_endian)
@@ -9693,8 +10318,9 @@ md_convert_frag (abfd, asec, fragp)
       else
        insn = bfd_getl16 (buf);
 
-      mips16_immed (fragp->fr_file, fragp->fr_line, type, val, false, small,
-                   ext, &insn, &use_extend, &extend);
+      mips16_immed (fragp->fr_file, fragp->fr_line, type, val,
+                   RELAX_MIPS16_USER_EXT (fragp->fr_subtype),
+                   small, ext, &insn, &use_extend, &extend);
 
       if (use_extend)
        {
@@ -9778,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)