]> Git Repo - binutils.git/blobdiff - gas/config/tc-mips.c
include/opcode/
[binutils.git] / gas / config / tc-mips.c
index c84952cbd0e30bc3f3e6382045ed141e14378908..afa8b3876a0cd452e36b728723d83c2e4339e1a2 100644 (file)
@@ -121,6 +121,21 @@ extern int target_big_endian;
                            ? ".rodata" \
                            : (abort (), ""))
 
+/* Ways in which an instruction can be "appended" to the output.  */
+enum append_method {
+  /* Just add it normally.  */
+  APPEND_ADD,
+
+  /* Add it normally and then add a nop.  */
+  APPEND_ADD_WITH_NOP,
+
+  /* Turn an instruction with a delay slot into a "compact" version.  */
+  APPEND_ADD_COMPACT,
+
+  /* Insert the instruction before the last one.  */
+  APPEND_SWAP
+};
+
 /* Information about an instruction, including its format, operands
    and fixups.  */
 struct mips_cl_insn
@@ -156,6 +171,9 @@ struct mips_cl_insn
 
   /* True for mips16 instructions that jump to an absolute address.  */
   unsigned int mips16_absolute_jump_p : 1;
+
+  /* True if this instruction is complete.  */
+  unsigned int complete_p : 1;
 };
 
 /* The ABI to use.  */
@@ -918,18 +936,20 @@ static int mips_relax_branch;
 
 
    but it's not clear that it would actually improve performance.  */
-#define RELAX_BRANCH_ENCODE(uncond, likely, link, toofar) \
-  ((relax_substateT) \
-   (0xc0000000 \
-    | ((toofar) ? 1 : 0) \
-    | ((link) ? 2 : 0) \
-    | ((likely) ? 4 : 0) \
-    | ((uncond) ? 8 : 0)))
+#define RELAX_BRANCH_ENCODE(at, uncond, likely, link, toofar)  \
+  ((relax_substateT)                                           \
+   (0xc0000000                                                 \
+    | ((at) & 0x1f)                                            \
+    | ((toofar) ? 0x20 : 0)                                    \
+    | ((link) ? 0x40 : 0)                                      \
+    | ((likely) ? 0x80 : 0)                                    \
+    | ((uncond) ? 0x100 : 0)))
 #define RELAX_BRANCH_P(i) (((i) & 0xf0000000) == 0xc0000000)
-#define RELAX_BRANCH_UNCOND(i) (((i) & 8) != 0)
-#define RELAX_BRANCH_LIKELY(i) (((i) & 4) != 0)
-#define RELAX_BRANCH_LINK(i) (((i) & 2) != 0)
-#define RELAX_BRANCH_TOOFAR(i) (((i) & 1) != 0)
+#define RELAX_BRANCH_UNCOND(i) (((i) & 0x100) != 0)
+#define RELAX_BRANCH_LIKELY(i) (((i) & 0x80) != 0)
+#define RELAX_BRANCH_LINK(i) (((i) & 0x40) != 0)
+#define RELAX_BRANCH_TOOFAR(i) (((i) & 0x20) != 0)
+#define RELAX_BRANCH_AT(i) ((i) & 0x1f)
 
 /* For mips16 code, we use an entirely different form of relaxation.
    mips16 supports two versions of most instructions which take
@@ -1382,6 +1402,7 @@ create_insn (struct mips_cl_insn *insn, const struct mips_opcode *mo)
   insn->fixed_p = (mips_opts.noreorder > 0);
   insn->noreorder_p = (mips_opts.noreorder > 0);
   insn->mips16_absolute_jump_p = 0;
+  insn->complete_p = 0;
 }
 
 /* Record the current MIPS16 mode in now_seg.  */
@@ -2105,6 +2126,7 @@ md_begin (void)
 void
 md_mips_end (void)
 {
+  mips_emit_delays ();
   if (! ECOFF_DEBUGGING)
     md_obj_end ();
 }
@@ -2236,88 +2258,6 @@ fixup_has_matching_lo_p (fixS *fixp)
          && fixp->fx_offset == fixp->fx_next->fx_offset);
 }
 
-/* See whether instruction IP reads register REG.  CLASS is the type
-   of register.  */
-
-static int
-insn_uses_reg (const struct mips_cl_insn *ip, unsigned int reg,
-              enum mips_regclass regclass)
-{
-  if (regclass == MIPS16_REG)
-    {
-      gas_assert (mips_opts.mips16);
-      reg = mips16_to_32_reg_map[reg];
-      regclass = MIPS_GR_REG;
-    }
-
-  /* Don't report on general register ZERO, since it never changes.  */
-  if (regclass == MIPS_GR_REG && reg == ZERO)
-    return 0;
-
-  if (regclass == MIPS_FP_REG)
-    {
-      gas_assert (! mips_opts.mips16);
-      /* If we are called with either $f0 or $f1, we must check $f0.
-        This is not optimal, because it will introduce an unnecessary
-        NOP between "lwc1 $f0" and "swc1 $f1".  To fix this we would
-        need to distinguish reading both $f0 and $f1 or just one of
-        them.  Note that we don't have to check the other way,
-        because there is no instruction that sets both $f0 and $f1
-        and requires a delay.  */
-      if ((ip->insn_mo->pinfo & INSN_READ_FPR_S)
-         && ((EXTRACT_OPERAND (FS, *ip) & ~(unsigned) 1)
-             == (reg &~ (unsigned) 1)))
-       return 1;
-      if ((ip->insn_mo->pinfo & INSN_READ_FPR_T)
-         && ((EXTRACT_OPERAND (FT, *ip) & ~(unsigned) 1)
-             == (reg &~ (unsigned) 1)))
-       return 1;
-      if ((ip->insn_mo->pinfo2 & INSN2_READ_FPR_Z)
-         && ((EXTRACT_OPERAND (FZ, *ip) & ~(unsigned) 1)
-             == (reg &~ (unsigned) 1)))
-       return 1;
-    }
-  else if (! mips_opts.mips16)
-    {
-      if ((ip->insn_mo->pinfo & INSN_READ_GPR_S)
-         && EXTRACT_OPERAND (RS, *ip) == reg)
-       return 1;
-      if ((ip->insn_mo->pinfo & INSN_READ_GPR_T)
-         && EXTRACT_OPERAND (RT, *ip) == reg)
-       return 1;
-      if ((ip->insn_mo->pinfo2 & INSN2_READ_GPR_D)
-         && EXTRACT_OPERAND (RD, *ip) == reg)
-       return 1;
-      if ((ip->insn_mo->pinfo2 & INSN2_READ_GPR_Z)
-         && EXTRACT_OPERAND (RZ, *ip) == reg)
-       return 1;
-    }
-  else
-    {
-      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_X)
-         && mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, *ip)] == reg)
-       return 1;
-      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Y)
-         && mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RY, *ip)] == reg)
-       return 1;
-      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Z)
-         && (mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip)]
-             == reg))
-       return 1;
-      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_T) && reg == TREG)
-       return 1;
-      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_SP) && reg == SP)
-       return 1;
-      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_31) && reg == RA)
-       return 1;
-      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_GPR_X)
-         && MIPS16_EXTRACT_OPERAND (REGR32, *ip) == reg)
-       return 1;
-    }
-
-  return 0;
-}
-
 /* This function returns true if modifying a register requires a
    delay.  */
 
@@ -2470,6 +2410,151 @@ relax_end (void)
   mips_relax.sequence = 0;
 }
 
+/* Return the mask of core registers that IP reads.  */
+
+static unsigned int
+gpr_read_mask (const struct mips_cl_insn *ip)
+{
+  unsigned long pinfo, pinfo2;
+  unsigned int mask;
+
+  mask = 0;
+  pinfo = ip->insn_mo->pinfo;
+  pinfo2 = ip->insn_mo->pinfo2;
+  if (mips_opts.mips16)
+    {
+      if (pinfo & MIPS16_INSN_READ_X)
+       mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, *ip)];
+      if (pinfo & MIPS16_INSN_READ_Y)
+       mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RY, *ip)];
+      if (pinfo & MIPS16_INSN_READ_T)
+       mask |= 1 << TREG;
+      if (pinfo & MIPS16_INSN_READ_SP)
+       mask |= 1 << SP;
+      if (pinfo & MIPS16_INSN_READ_31)
+       mask |= 1 << RA;
+      if (pinfo & MIPS16_INSN_READ_Z)
+       mask |= 1 << (mips16_to_32_reg_map
+                     [MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip)]);
+      if (pinfo & MIPS16_INSN_READ_GPR_X)
+       mask |= 1 << MIPS16_EXTRACT_OPERAND (REGR32, *ip);
+    }
+  else
+    {
+      if (pinfo2 & INSN2_READ_GPR_D)
+       mask |= 1 << EXTRACT_OPERAND (RD, *ip);
+      if (pinfo & INSN_READ_GPR_T)
+       mask |= 1 << EXTRACT_OPERAND (RT, *ip);
+      if (pinfo & INSN_READ_GPR_S)
+       mask |= 1 << EXTRACT_OPERAND (RS, *ip);
+      if (pinfo2 & INSN2_READ_GPR_Z)
+       mask |= 1 << EXTRACT_OPERAND (RZ, *ip);
+    }
+  /* Don't include register 0.  */
+  return mask & ~1;
+}
+
+/* Return the mask of core registers that IP writes.  */
+
+static unsigned int
+gpr_write_mask (const struct mips_cl_insn *ip)
+{
+  unsigned long pinfo, pinfo2;
+  unsigned int mask;
+
+  mask = 0;
+  pinfo = ip->insn_mo->pinfo;
+  pinfo2 = ip->insn_mo->pinfo2;
+  if (mips_opts.mips16)
+    {
+      if (pinfo & MIPS16_INSN_WRITE_X)
+       mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, *ip)];
+      if (pinfo & MIPS16_INSN_WRITE_Y)
+       mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RY, *ip)];
+      if (pinfo & MIPS16_INSN_WRITE_Z)
+       mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RZ, *ip)];
+      if (pinfo & MIPS16_INSN_WRITE_T)
+       mask |= 1 << TREG;
+      if (pinfo & MIPS16_INSN_WRITE_SP)
+       mask |= 1 << SP;
+      if (pinfo & MIPS16_INSN_WRITE_31)
+       mask |= 1 << RA;
+      if (pinfo & MIPS16_INSN_WRITE_GPR_Y)
+       mask |= 1 << MIPS16OP_EXTRACT_REG32R (ip->insn_opcode);
+    }
+  else
+    {
+      if (pinfo & INSN_WRITE_GPR_D)
+       mask |= 1 << EXTRACT_OPERAND (RD, *ip);
+      if (pinfo & INSN_WRITE_GPR_T)
+       mask |= 1 << EXTRACT_OPERAND (RT, *ip);
+      if (pinfo & INSN_WRITE_GPR_31)
+       mask |= 1 << RA;
+      if (pinfo2 & INSN2_WRITE_GPR_Z)
+       mask |= 1 << EXTRACT_OPERAND (RZ, *ip);
+    }
+  /* Don't include register 0.  */
+  return mask & ~1;
+}
+
+/* Return the mask of floating-point registers that IP reads.  */
+
+static unsigned int
+fpr_read_mask (const struct mips_cl_insn *ip)
+{
+  unsigned long pinfo, pinfo2;
+  unsigned int mask;
+
+  mask = 0;
+  pinfo = ip->insn_mo->pinfo;
+  pinfo2 = ip->insn_mo->pinfo2;
+  if (!mips_opts.mips16)
+    {
+      if (pinfo & INSN_READ_FPR_S)
+       mask |= 1 << EXTRACT_OPERAND (FS, *ip);
+      if (pinfo & INSN_READ_FPR_T)
+       mask |= 1 << EXTRACT_OPERAND (FT, *ip);
+      if (pinfo & INSN_READ_FPR_R)
+       mask |= 1 << EXTRACT_OPERAND (FR, *ip);
+      if (pinfo2 & INSN2_READ_FPR_Z)
+       mask |= 1 << EXTRACT_OPERAND (FZ, *ip);
+    }
+  /* Conservatively treat all operands to an FP_D instruction are doubles.
+     (This is overly pessimistic for things like cvt.d.s.)  */
+  if (HAVE_32BIT_FPRS && (pinfo & FP_D))
+    mask |= mask << 1;
+  return mask;
+}
+
+/* Return the mask of floating-point registers that IP writes.  */
+
+static unsigned int
+fpr_write_mask (const struct mips_cl_insn *ip)
+{
+  unsigned long pinfo, pinfo2;
+  unsigned int mask;
+
+  mask = 0;
+  pinfo = ip->insn_mo->pinfo;
+  pinfo2 = ip->insn_mo->pinfo2;
+  if (!mips_opts.mips16)
+    {
+      if (pinfo & INSN_WRITE_FPR_D)
+       mask |= 1 << EXTRACT_OPERAND (FD, *ip);
+      if (pinfo & INSN_WRITE_FPR_S)
+       mask |= 1 << EXTRACT_OPERAND (FS, *ip);
+      if (pinfo & INSN_WRITE_FPR_T)
+       mask |= 1 << EXTRACT_OPERAND (FT, *ip);
+      if (pinfo2 & INSN2_WRITE_FPR_Z)
+       mask |= 1 << EXTRACT_OPERAND (FZ, *ip);
+    }
+  /* Conservatively treat all operands to an FP_D instruction are doubles.
+     (This is overly pessimistic for things like cvt.s.d.)  */
+  if (HAVE_32BIT_FPRS && (pinfo & FP_D))
+    mask |= mask << 1;
+  return mask;
+}
+
 /* Classify an instruction according to the FIX_VR4120_* enumeration.
    Return NUM_FIX_VR4120_CLASSES if the instruction isn't affected
    by VR4120 errata.  */
@@ -2504,16 +2589,17 @@ insns_between (const struct mips_cl_insn *insn1,
               const struct mips_cl_insn *insn2)
 {
   unsigned long pinfo1, pinfo2;
+  unsigned int mask;
 
   /* This function needs to know which pinfo flags are set for INSN2
      and which registers INSN2 uses.  The former is stored in PINFO2 and
-     the latter is tested via INSN2_USES_REG.  If INSN2 is null, PINFO2
-     will have every flag set and INSN2_USES_REG will always return true.  */
+     the latter is tested via INSN2_USES_GPR.  If INSN2 is null, PINFO2
+     will have every flag set and INSN2_USES_GPR will always return true.  */
   pinfo1 = insn1->insn_mo->pinfo;
   pinfo2 = insn2 ? insn2->insn_mo->pinfo : ~0U;
 
-#define INSN2_USES_REG(REG, CLASS) \
-   (insn2 == NULL || insn_uses_reg (insn2, REG, CLASS))
+#define INSN2_USES_GPR(REG) \
+  (insn2 == NULL || (gpr_read_mask (insn2) & (1U << (REG))) != 0)
 
   /* For most targets, write-after-read dependencies on the HI and LO
      registers must be separated by at least two instructions.  */
@@ -2529,7 +2615,7 @@ insns_between (const struct mips_cl_insn *insn1,
      between an mfhi or mflo and any instruction that uses the result.  */
   if (mips_7000_hilo_fix
       && MF_HILO_INSN (pinfo1)
-      && INSN2_USES_REG (EXTRACT_OPERAND (RD, *insn1), MIPS_GR_REG))
+      && INSN2_USES_GPR (EXTRACT_OPERAND (RD, *insn1)))
     return 2;
 
   /* If we're working around 24K errata, one instruction is required
@@ -2576,7 +2662,7 @@ insns_between (const struct mips_cl_insn *insn1,
          || (!cop_interlocks && (pinfo1 & INSN_LOAD_COPROC_DELAY)))
        {
          know (pinfo1 & INSN_WRITE_GPR_T);
-         if (INSN2_USES_REG (EXTRACT_OPERAND (RT, *insn1), MIPS_GR_REG))
+         if (INSN2_USES_GPR (EXTRACT_OPERAND (RT, *insn1)))
            return 1;
        }
 
@@ -2594,14 +2680,10 @@ insns_between (const struct mips_cl_insn *insn1,
          /* Handle cases where INSN1 writes to a known general coprocessor
             register.  There must be a one instruction delay before INSN2
             if INSN2 reads that register, otherwise no delay is needed.  */
-         if (pinfo1 & INSN_WRITE_FPR_T)
+         mask = fpr_write_mask (insn1);
+         if (mask != 0)
            {
-             if (INSN2_USES_REG (EXTRACT_OPERAND (FT, *insn1), MIPS_FP_REG))
-               return 1;
-           }
-         else if (pinfo1 & INSN_WRITE_FPR_S)
-           {
-             if (INSN2_USES_REG (EXTRACT_OPERAND (FS, *insn1), MIPS_FP_REG))
+             if (!insn2 || (mask & fpr_read_mask (insn2)) != 0)
                return 1;
            }
          else
@@ -2631,20 +2713,22 @@ insns_between (const struct mips_cl_insn *insn1,
        return 1;
     }
 
-#undef INSN2_USES_REG
+#undef INSN2_USES_GPR
 
   return 0;
 }
 
 /* Return the number of nops that would be needed to work around the
    VR4130 mflo/mfhi errata if instruction INSN immediately followed
-   the MAX_VR4130_NOPS instructions described by HIST.  */
+   the MAX_VR4130_NOPS instructions described by HIST.  Ignore hazards
+   that are contained within the first IGNORE instructions of HIST.  */
 
 static int
-nops_for_vr4130 (const struct mips_cl_insn *hist,
+nops_for_vr4130 (int ignore, const struct mips_cl_insn *hist,
                 const struct mips_cl_insn *insn)
 {
-  int i, j, reg;
+  int i, j;
+  unsigned int mask;
 
   /* Check if the instruction writes to HI or LO.  MTHI and MTLO
      are not affected by the errata.  */
@@ -2659,38 +2743,227 @@ nops_for_vr4130 (const struct mips_cl_insn *hist,
     if (MF_HILO_INSN (hist[i].insn_mo->pinfo))
       {
        /* Extract the destination register.  */
-       if (mips_opts.mips16)
-         reg = mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, hist[i])];
-       else
-         reg = EXTRACT_OPERAND (RD, hist[i]);
+       mask = gpr_write_mask (&hist[i]);
 
        /* No nops are needed if INSN reads that register.  */
-       if (insn != NULL && insn_uses_reg (insn, reg, MIPS_GR_REG))
+       if (insn != NULL && (gpr_read_mask (insn) & mask) != 0)
          return 0;
 
        /* ...or if any of the intervening instructions do.  */
        for (j = 0; j < i; j++)
-         if (insn_uses_reg (&hist[j], reg, MIPS_GR_REG))
+         if (gpr_read_mask (&hist[j]) & mask)
            return 0;
 
-       return MAX_VR4130_NOPS - i;
+       if (i >= ignore)
+         return MAX_VR4130_NOPS - i;
       }
   return 0;
 }
 
+#define BASE_REG_EQ(INSN1, INSN2)      \
+  ((((INSN1) >> OP_SH_RS) & OP_MASK_RS) \
+      == (((INSN2) >> OP_SH_RS) & OP_MASK_RS))
+
+/* Return the minimum alignment for this store instruction.  */
+
+static int
+fix_24k_align_to (const struct mips_opcode *mo)
+{
+  if (strcmp (mo->name, "sh") == 0)
+    return 2;
+
+  if (strcmp (mo->name, "swc1") == 0
+      || strcmp (mo->name, "swc2") == 0
+      || strcmp (mo->name, "sw") == 0
+      || strcmp (mo->name, "sc") == 0
+      || strcmp (mo->name, "s.s") == 0)
+    return 4;
+
+  if (strcmp (mo->name, "sdc1") == 0
+      || strcmp (mo->name, "sdc2") == 0
+      || strcmp (mo->name, "s.d") == 0)
+    return 8;
+
+  /* sb, swl, swr */
+  return 1;
+}
+
+struct fix_24k_store_info
+  {
+    /* Immediate offset, if any, for this store instruction.  */
+    short off;
+    /* Alignment required by this store instruction.  */
+    int align_to;
+    /* True for register offsets.  */
+    int register_offset;
+  };
+
+/* Comparison function used by qsort.  */
+
+static int
+fix_24k_sort (const void *a, const void *b)
+{
+  const struct fix_24k_store_info *pos1 = a;
+  const struct fix_24k_store_info *pos2 = b;
+
+  return (pos1->off - pos2->off);
+}
+
+/* INSN is a store instruction.  Try to record the store information
+   in STINFO.  Return false if the information isn't known.  */
+
+static bfd_boolean
+fix_24k_record_store_info (struct fix_24k_store_info *stinfo,
+                          const struct mips_cl_insn *insn)
+{
+  /* The instruction must have a known offset.  */
+  if (!insn->complete_p || !strstr (insn->insn_mo->args, "o("))
+    return FALSE;
+
+  stinfo->off = (insn->insn_opcode >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE;
+  stinfo->align_to = fix_24k_align_to (insn->insn_mo);
+  return TRUE;
+}
+
+/* Return the number of nops that would be needed to work around the 24k
+   "lost data on stores during refill" errata if instruction INSN
+   immediately followed the 2 instructions described by HIST.
+   Ignore hazards that are contained within the first IGNORE
+   instructions of HIST.
+
+   Problem: The FSB (fetch store buffer) acts as an intermediate buffer
+   for the data cache refills and store data. The following describes
+   the scenario where the store data could be lost.
+
+   * A data cache miss, due to either a load or a store, causing fill
+     data to be supplied by the memory subsystem
+   * The first three doublewords of fill data are returned and written
+     into the cache
+   * A sequence of four stores occurs in consecutive cycles around the
+     final doubleword of the fill:
+   * Store A
+   * Store B
+   * Store C
+   * Zero, One or more instructions
+   * Store D
+
+   The four stores A-D must be to different doublewords of the line that
+   is being filled. The fourth instruction in the sequence above permits
+   the fill of the final doubleword to be transferred from the FSB into
+   the cache. In the sequence above, the stores may be either integer
+   (sb, sh, sw, swr, swl, sc) or coprocessor (swc1/swc2, sdc1/sdc2,
+   swxc1, sdxc1, suxc1) stores, as long as the four stores are to
+   different doublewords on the line. If the floating point unit is
+   running in 1:2 mode, it is not possible to create the sequence above
+   using only floating point store instructions.
+
+   In this case, the cache line being filled is incorrectly marked
+   invalid, thereby losing the data from any store to the line that
+   occurs between the original miss and the completion of the five
+   cycle sequence shown above.
+
+   The workarounds are:
+
+   * Run the data cache in write-through mode.
+   * Insert a non-store instruction between
+     Store A and Store B or Store B and Store C.  */
+  
+static int
+nops_for_24k (int ignore, const struct mips_cl_insn *hist,
+             const struct mips_cl_insn *insn)
+{
+  struct fix_24k_store_info pos[3];
+  int align, i, base_offset;
+
+  if (ignore >= 2)
+    return 0;
+
+  /* If the previous instruction wasn't a store, there's nothing to
+     worry about.  */
+  if ((hist[0].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
+    return 0;
+
+  /* If the instructions after the previous one are unknown, we have
+     to assume the worst.  */
+  if (!insn)
+    return 1;
+
+  /* Check whether we are dealing with three consecutive stores.  */
+  if ((insn->insn_mo->pinfo & INSN_STORE_MEMORY) == 0
+      || (hist[1].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
+    return 0;
+
+  /* If we don't know the relationship between the store addresses,
+     assume the worst.  */
+  if (!BASE_REG_EQ (insn->insn_opcode, hist[0].insn_opcode)
+      || !BASE_REG_EQ (insn->insn_opcode, hist[1].insn_opcode))
+    return 1;
+
+  if (!fix_24k_record_store_info (&pos[0], insn)
+      || !fix_24k_record_store_info (&pos[1], &hist[0])
+      || !fix_24k_record_store_info (&pos[2], &hist[1]))
+    return 1;
+
+  qsort (&pos, 3, sizeof (struct fix_24k_store_info), fix_24k_sort);
+
+  /* Pick a value of ALIGN and X such that all offsets are adjusted by
+     X bytes and such that the base register + X is known to be aligned
+     to align bytes.  */
+
+  if (((insn->insn_opcode >> OP_SH_RS) & OP_MASK_RS) == SP)
+    align = 8;
+  else
+    {
+      align = pos[0].align_to;
+      base_offset = pos[0].off;
+      for (i = 1; i < 3; i++)
+       if (align < pos[i].align_to)
+         {
+           align = pos[i].align_to;
+           base_offset = pos[i].off;
+         }
+      for (i = 0; i < 3; i++)
+       pos[i].off -= base_offset;
+    }
+
+  pos[0].off &= ~align + 1;
+  pos[1].off &= ~align + 1;
+  pos[2].off &= ~align + 1;
+
+  /* If any two stores write to the same chunk, they also write to the
+     same doubleword.  The offsets are still sorted at this point.  */
+  if (pos[0].off == pos[1].off || pos[1].off == pos[2].off)
+    return 0;
+
+  /* A range of at least 9 bytes is needed for the stores to be in
+     non-overlapping doublewords.  */
+  if (pos[2].off - pos[0].off <= 8)
+    return 0;
+
+  if (pos[2].off - pos[1].off >= 24
+      || pos[1].off - pos[0].off >= 24
+      || pos[2].off - pos[0].off >= 32)
+    return 0;
+
+  return 1;
+}
+
 /* Return the number of nops that would be needed if instruction INSN
    immediately followed the MAX_NOPS instructions given by HIST,
-   where HIST[0] is the most recent instruction.  If INSN is null,
-   return the worse-case number of nops for any instruction.  */
+   where HIST[0] is the most recent instruction.  Ignore hazards
+   between INSN and the first IGNORE instructions in HIST.
+
+   If INSN is null, return the worse-case number of nops for any
+   instruction.  */
 
 static int
-nops_for_insn (const struct mips_cl_insn *hist,
+nops_for_insn (int ignore, const struct mips_cl_insn *hist,
               const struct mips_cl_insn *insn)
 {
   int i, nops, tmp_nops;
 
   nops = 0;
-  for (i = 0; i < MAX_DELAY_NOPS; i++)
+  for (i = ignore; i < MAX_DELAY_NOPS; i++)
     {
       tmp_nops = insns_between (hist + i, insn) - i;
       if (tmp_nops > nops)
@@ -2699,7 +2972,14 @@ nops_for_insn (const struct mips_cl_insn *hist,
 
   if (mips_fix_vr4130)
     {
-      tmp_nops = nops_for_vr4130 (hist, insn);
+      tmp_nops = nops_for_vr4130 (ignore, hist, insn);
+      if (tmp_nops > nops)
+       nops = tmp_nops;
+    }
+
+  if (mips_fix_24k)
+    {
+      tmp_nops = nops_for_24k (ignore, hist, insn);
       if (tmp_nops > nops)
        nops = tmp_nops;
     }
@@ -2709,10 +2989,12 @@ nops_for_insn (const struct mips_cl_insn *hist,
 
 /* The variable arguments provide NUM_INSNS extra instructions that
    might be added to HIST.  Return the largest number of nops that
-   would be needed after the extended sequence.  */
+   would be needed after the extended sequence, ignoring hazards
+   in the first IGNORE instructions.  */
 
 static int
-nops_for_sequence (int num_insns, const struct mips_cl_insn *hist, ...)
+nops_for_sequence (int num_insns, int ignore,
+                  const struct mips_cl_insn *hist, ...)
 {
   va_list args;
   struct mips_cl_insn buffer[MAX_NOPS];
@@ -2725,7 +3007,7 @@ nops_for_sequence (int num_insns, const struct mips_cl_insn *hist, ...)
   while (cursor > buffer)
     *--cursor = *va_arg (args, const struct mips_cl_insn *);
 
-  nops = nops_for_insn (buffer, NULL);
+  nops = nops_for_insn (ignore, buffer, NULL);
   va_end (args);
   return nops;
 }
@@ -2734,17 +3016,18 @@ nops_for_sequence (int num_insns, const struct mips_cl_insn *hist, ...)
    worst-case delay for the branch target.  */
 
 static int
-nops_for_insn_or_target (const struct mips_cl_insn *hist,
+nops_for_insn_or_target (int ignore, const struct mips_cl_insn *hist,
                         const struct mips_cl_insn *insn)
 {
   int nops, tmp_nops;
 
-  nops = nops_for_insn (hist, insn);
+  nops = nops_for_insn (ignore, hist, insn);
   if (insn->insn_mo->pinfo & (INSN_UNCOND_BRANCH_DELAY
                              | INSN_COND_BRANCH_DELAY
                              | INSN_COND_BRANCH_LIKELY))
     {
-      tmp_nops = nops_for_sequence (2, hist, insn, NOP_INSN);
+      tmp_nops = nops_for_sequence (2, ignore ? ignore + 2 : 0,
+                                   hist, insn, NOP_INSN);
       if (tmp_nops > nops)
        nops = tmp_nops;
     }
@@ -2752,7 +3035,7 @@ nops_for_insn_or_target (const struct mips_cl_insn *hist,
           && (insn->insn_mo->pinfo & (MIPS16_INSN_UNCOND_BRANCH
                                       | MIPS16_INSN_COND_BRANCH)))
     {
-      tmp_nops = nops_for_sequence (1, hist, insn);
+      tmp_nops = nops_for_sequence (1, ignore ? ignore + 1 : 0, hist, insn);
       if (tmp_nops > nops)
        nops = tmp_nops;
     }
@@ -2807,6 +3090,178 @@ fix_loongson2f (struct mips_cl_insn * ip)
     fix_loongson2f_jump (ip);
 }
 
+/* IP is a branch that has a delay slot, and we need to fill it
+   automatically.   Return true if we can do that by swapping IP
+   with the previous instruction.  */
+
+static bfd_boolean
+can_swap_branch_p (struct mips_cl_insn *ip)
+{
+  unsigned long pinfo, prev_pinfo;
+  unsigned int gpr_read, gpr_write, prev_gpr_read, prev_gpr_write;
+
+  /* -O2 and above is required for this optimization.  */
+  if (mips_optimize < 2)
+    return FALSE;
+
+  /* If we have seen .set volatile or .set nomove, don't optimize.  */
+  if (mips_opts.nomove)
+    return FALSE;
+
+  /* We can't swap if the previous instruction's position is fixed.  */
+  if (history[0].fixed_p)
+    return FALSE;
+
+  /* If the previous previous insn was in a .set noreorder, we can't
+     swap.  Actually, the MIPS assembler will swap in this situation.
+     However, gcc configured -with-gnu-as will generate code like
+
+       .set    noreorder
+       lw      $4,XXX
+       .set    reorder
+       INSN
+       bne     $4,$0,foo
+
+     in which we can not swap the bne and INSN.  If gcc is not configured
+     -with-gnu-as, it does not output the .set pseudo-ops.  */
+  if (history[1].noreorder_p)
+    return FALSE;
+
+  /* If the previous instruction had a fixup in mips16 mode, we can not
+     swap.  This normally means that the previous instruction was a 4
+     byte branch anyhow.  */
+  if (mips_opts.mips16 && history[0].fixp[0])
+    return FALSE;
+
+  /* If the branch is itself the target of a branch, we can not swap.
+     We cheat on this; all we check for is whether there is a label on
+     this instruction.  If there are any branches to anything other than
+     a label, users must use .set noreorder.  */
+  if (seg_info (now_seg)->label_list)
+    return FALSE;
+
+  /* If the previous instruction is in a variant frag other than this
+     branch's one, we cannot do the swap.  This does not apply to the
+     mips16, which uses variant frags for different purposes.  */
+  if (!mips_opts.mips16
+      && history[0].frag
+      && history[0].frag->fr_type == rs_machine_dependent)
+    return FALSE;
+
+  /* We do not swap with instructions that cannot architecturally
+     be placed in a branch delay slot, such as SYNC or ERET.  We
+     also refrain from swapping with a trap instruction, since it
+     complicates trap handlers to have the trap instruction be in
+     a delay slot.  */
+  prev_pinfo = history[0].insn_mo->pinfo;
+  if (prev_pinfo & INSN_NO_DELAY_SLOT)
+    return FALSE;
+
+  /* Check for conflicts between the branch and the instructions
+     before the candidate delay slot.  */
+  if (nops_for_insn (0, history + 1, ip) > 0)
+    return FALSE;
+
+  /* Check for conflicts between the swapped sequence and the
+     target of the branch.  */
+  if (nops_for_sequence (2, 0, history + 1, ip, history) > 0)
+    return FALSE;
+
+  /* If the branch reads a register that the previous
+     instruction sets, we can not swap.  */
+  gpr_read = gpr_read_mask (ip);
+  prev_gpr_write = gpr_write_mask (&history[0]);
+  if (gpr_read & prev_gpr_write)
+    return FALSE;
+
+  /* If the branch writes a register that the previous
+     instruction sets, we can not swap.  */
+  gpr_write = gpr_write_mask (ip);
+  if (gpr_write & prev_gpr_write)
+    return FALSE;
+
+  /* If the branch writes a register that the previous
+     instruction reads, we can not swap.  */
+  prev_gpr_read = gpr_read_mask (&history[0]);
+  if (gpr_write & prev_gpr_read)
+    return FALSE;
+
+  /* If one instruction sets a condition code and the
+     other one uses a condition code, we can not swap.  */
+  pinfo = ip->insn_mo->pinfo;
+  if ((pinfo & INSN_READ_COND_CODE)
+      && (prev_pinfo & INSN_WRITE_COND_CODE))
+    return FALSE;
+  if ((pinfo & INSN_WRITE_COND_CODE)
+      && (prev_pinfo & INSN_READ_COND_CODE))
+    return FALSE;
+
+  /* If the previous instruction uses the PC, we can not swap.  */
+  if (mips_opts.mips16 && (prev_pinfo & MIPS16_INSN_READ_PC))
+    return FALSE;
+
+  return TRUE;
+}
+
+/* Decide how we should add IP to the instruction stream.  */
+
+static enum append_method
+get_append_method (struct mips_cl_insn *ip)
+{
+  unsigned long pinfo;
+
+  /* The relaxed version of a macro sequence must be inherently
+     hazard-free.  */
+  if (mips_relax.sequence == 2)
+    return APPEND_ADD;
+
+  /* We must not dabble with instructions in a ".set norerorder" block.  */
+  if (mips_opts.noreorder)
+    return APPEND_ADD;
+
+  /* Otherwise, it's our responsibility to fill branch delay slots.  */
+  pinfo = ip->insn_mo->pinfo;
+  if ((pinfo & INSN_UNCOND_BRANCH_DELAY)
+      || (pinfo & INSN_COND_BRANCH_DELAY))
+    {
+      if (can_swap_branch_p (ip))
+       return APPEND_SWAP;
+
+      if (mips_opts.mips16
+         && ISA_SUPPORTS_MIPS16E
+         && (pinfo & INSN_UNCOND_BRANCH_DELAY)
+         && (pinfo & (MIPS16_INSN_READ_X | MIPS16_INSN_READ_31)))
+       return APPEND_ADD_COMPACT;
+
+      return APPEND_ADD_WITH_NOP;
+    }
+
+  /* We don't bother trying to track the target of branches, so there's
+     nothing we can use to fill a branch-likely slot.  */
+  if (pinfo & INSN_COND_BRANCH_LIKELY)
+    return APPEND_ADD_WITH_NOP;
+
+  return APPEND_ADD;
+}
+
+/* IP is a MIPS16 instruction whose opcode we have just changed.
+   Point IP->insn_mo to the new opcode's definition.  */
+
+static void
+find_altered_mips16_opcode (struct mips_cl_insn *ip)
+{
+  const struct mips_opcode *mo, *end;
+
+  end = &mips16_opcodes[bfd_mips16_num_opcodes];
+  for (mo = ip->insn_mo; mo < end; mo++)
+    if ((ip->insn_opcode & mo->mask) == mo->match)
+      {
+       ip->insn_mo = mo;
+       return;
+      }
+  abort ();
+}
+
 /* Output an instruction.  IP is the instruction information.
    ADDRESS_EXPR is an operand of the instruction to be used with
    RELOC_TYPE.  */
@@ -2816,10 +3271,8 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
             bfd_reloc_code_real_type *reloc_type)
 {
   unsigned long prev_pinfo, pinfo;
-  unsigned long prev_pinfo2, pinfo2;
-  relax_stateT prev_insn_frag_type = 0;
   bfd_boolean relaxed_branch = FALSE;
-  segment_info_type *si = seg_info (now_seg);
+  enum append_method method;
 
   if (mips_fix_loongson2f)
     fix_loongson2f (ip);
@@ -2830,9 +3283,84 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
   file_ase_mips16 |= mips_opts.mips16;
 
   prev_pinfo = history[0].insn_mo->pinfo;
-  prev_pinfo2 = history[0].insn_mo->pinfo2;
   pinfo = ip->insn_mo->pinfo;
-  pinfo2 = ip->insn_mo->pinfo2;
+
+  if (address_expr == NULL)
+    ip->complete_p = 1;
+  else if (*reloc_type <= BFD_RELOC_UNUSED
+          && address_expr->X_op == O_constant)
+    {
+      unsigned int tmp;
+
+      ip->complete_p = 1;
+      switch (*reloc_type)
+       {
+       case BFD_RELOC_32:
+         ip->insn_opcode |= address_expr->X_add_number;
+         break;
+
+       case BFD_RELOC_MIPS_HIGHEST:
+         tmp = (address_expr->X_add_number + 0x800080008000ull) >> 48;
+         ip->insn_opcode |= tmp & 0xffff;
+         break;
+
+       case BFD_RELOC_MIPS_HIGHER:
+         tmp = (address_expr->X_add_number + 0x80008000ull) >> 32;
+         ip->insn_opcode |= tmp & 0xffff;
+         break;
+
+       case BFD_RELOC_HI16_S:
+         tmp = (address_expr->X_add_number + 0x8000) >> 16;
+         ip->insn_opcode |= tmp & 0xffff;
+         break;
+
+       case BFD_RELOC_HI16:
+         ip->insn_opcode |= (address_expr->X_add_number >> 16) & 0xffff;
+         break;
+
+       case BFD_RELOC_UNUSED:
+       case BFD_RELOC_LO16:
+       case BFD_RELOC_MIPS_GOT_DISP:
+         ip->insn_opcode |= address_expr->X_add_number & 0xffff;
+         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;
+         ip->complete_p = 0;
+         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)
+              | ((address_expr->X_add_number & 0x3fffc) >> 2));
+         ip->complete_p = 0;
+         break;
+
+       case BFD_RELOC_16_PCREL_S2:
+         if ((address_expr->X_add_number & 3) != 0)
+           as_bad (_("branch to misaligned address (0x%lx)"),
+                   (unsigned long) address_expr->X_add_number);
+         if (!mips_relax_branch)
+           {
+             if ((address_expr->X_add_number + 0x20000) & ~0x3ffff)
+               as_bad (_("branch address range overflow (0x%lx)"),
+                       (unsigned long) address_expr->X_add_number);
+             ip->insn_opcode |= (address_expr->X_add_number >> 2) & 0xffff;
+           }
+         ip->complete_p = 0;
+         break;
+
+       default:
+         internalError ();
+       }       
+    }
 
   if (mips_relax.sequence != 2 && !mips_opts.noreorder)
     {
@@ -2844,8 +3372,8 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
         benefit hand written assembly code, and does not seem worth
         it.  */
       int nops = (mips_optimize == 0
-                 ? nops_for_insn (history, NULL)
-                 : nops_for_insn_or_target (history, ip));
+                 ? nops_for_insn (0, history, NULL)
+                 : nops_for_insn_or_target (0, history, ip));
       if (nops > 0)
        {
          fragS *old_frag;
@@ -2882,8 +3410,12 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
     }
   else if (mips_relax.sequence != 2 && prev_nop_frag != NULL)
     {
-      /* Work out how many nops in prev_nop_frag are needed by IP.  */
-      int nops = nops_for_insn_or_target (history, ip);
+      int nops;
+
+      /* Work out how many nops in prev_nop_frag are needed by IP,
+        ignoring hazards generated by the first prev_nop_frag_since
+        instructions.  */
+      nops = nops_for_insn_or_target (prev_nop_frag_since, history, ip);
       gas_assert (nops <= prev_nop_frag_holds);
 
       /* Enforce NOPS as a minimum.  */
@@ -2908,19 +3440,27 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
        }
     }
 
+  method = get_append_method (ip);
+
 #ifdef OBJ_ELF
   /* The value passed to dwarf2_emit_insn is the distance between
      the beginning of the current instruction and the address that
-     should be recorded in the debug tables.  For MIPS16 debug info
-     we want to use ISA-encoded addresses, so we pass -1 for an
-     address higher by one than the current.  */
-  dwarf2_emit_insn (mips_opts.mips16 ? -1 : 0);
+     should be recorded in the debug tables.  This is normally the
+     current address.
+
+     For MIPS16 debug info we want to use ISA-encoded addresses,
+     so we use -1 for an address higher by one than the current one.
+
+     If the instruction produced is a branch that we will swap with
+     the preceding instruction, then we add the displacement by which
+     the branch will be moved backwards.  This is more appropriate
+     and for MIPS16 code also prevents a debugger from placing a
+     breakpoint in the middle of the branch (and corrupting code if
+     software breakpoints are used).  */
+  dwarf2_emit_insn ((mips_opts.mips16 ? -1 : 0)
+                   + (method == APPEND_SWAP ? insn_length (history) : 0));
 #endif
 
-  /* Record the frag type before frag_var.  */
-  if (history[0].frag)
-    prev_insn_frag_type = history[0].frag->fr_type;
-
   if (address_expr
       && *reloc_type == BFD_RELOC_16_PCREL_S2
       && (pinfo & INSN_UNCOND_BRANCH_DELAY || pinfo & INSN_COND_BRANCH_DELAY
@@ -2931,6 +3471,8 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
         out that the branch was out-of-range, we'll get an error.  */
       && !mips_opts.warn_about_macros
       && (mips_opts.at || mips_pic == NO_PIC)
+      /* Don't relax BPOSGE32/64 as they have no complementing branches.  */
+      && !(ip->insn_mo->membership & (INSN_DSP64 | INSN_DSP))
       && !mips_opts.mips16)
     {
       relaxed_branch = TRUE;
@@ -2940,7 +3482,8 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
                              : (pinfo & INSN_COND_BRANCH_LIKELY) ? 1
                              : 0)), 4,
                        RELAX_BRANCH_ENCODE
-                       (pinfo & INSN_UNCOND_BRANCH_DELAY,
+                       (AT,
+                        pinfo & INSN_UNCOND_BRANCH_DELAY,
                         pinfo & INSN_COND_BRANCH_LIKELY,
                         pinfo & INSN_WRITE_GPR_31,
                         0),
@@ -3000,452 +3543,166 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
       add_fixed_insn (ip);
     }
 
-  if (address_expr != NULL && *reloc_type <= BFD_RELOC_UNUSED)
+  if (!ip->complete_p && *reloc_type < BFD_RELOC_UNUSED)
     {
-      if (address_expr->X_op == O_constant)
-       {
-         unsigned int tmp;
-
-         switch (*reloc_type)
-           {
-           case BFD_RELOC_32:
-             ip->insn_opcode |= address_expr->X_add_number;
-             break;
-
-           case BFD_RELOC_MIPS_HIGHEST:
-             tmp = (address_expr->X_add_number + 0x800080008000ull) >> 48;
-             ip->insn_opcode |= tmp & 0xffff;
-             break;
-
-           case BFD_RELOC_MIPS_HIGHER:
-             tmp = (address_expr->X_add_number + 0x80008000ull) >> 32;
-             ip->insn_opcode |= tmp & 0xffff;
-             break;
-
-           case BFD_RELOC_HI16_S:
-             tmp = (address_expr->X_add_number + 0x8000) >> 16;
-             ip->insn_opcode |= tmp & 0xffff;
-             break;
-
-           case BFD_RELOC_HI16:
-             ip->insn_opcode |= (address_expr->X_add_number >> 16) & 0xffff;
-             break;
-
-           case BFD_RELOC_UNUSED:
-           case BFD_RELOC_LO16:
-           case BFD_RELOC_MIPS_GOT_DISP:
-             ip->insn_opcode |= address_expr->X_add_number & 0xffff;
-             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;
+      reloc_howto_type *howto;
+      int i;
 
-           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)
-                | ((address_expr->X_add_number & 0x3fffc) >> 2));
-             break;
+      /* In a compound relocation, it is the final (outermost)
+        operator that determines the relocated field.  */
+      for (i = 1; i < 3; i++)
+       if (reloc_type[i] == BFD_RELOC_UNUSED)
+         break;
 
-           case BFD_RELOC_16_PCREL_S2:
-             if ((address_expr->X_add_number & 3) != 0)
-               as_bad (_("branch to misaligned address (0x%lx)"),
-                       (unsigned long) address_expr->X_add_number);
-             if (mips_relax_branch)
-               goto need_reloc;
-             if ((address_expr->X_add_number + 0x20000) & ~0x3ffff)
-               as_bad (_("branch address range overflow (0x%lx)"),
-                       (unsigned long) address_expr->X_add_number);
-             ip->insn_opcode |= (address_expr->X_add_number >> 2) & 0xffff;
-             break;
+      howto = bfd_reloc_type_lookup (stdoutput, reloc_type[i - 1]);
+      if (howto == NULL)
+       {
+         /* To reproduce this failure try assembling gas/testsuites/
+            gas/mips/mips16-intermix.s with a mips-ecoff targeted
+            assembler.  */
+         as_bad (_("Unsupported MIPS relocation number %d"), reloc_type[i - 1]);
+         howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_16);
+       }
+         
+      ip->fixp[0] = fix_new_exp (ip->frag, ip->where,
+                                bfd_get_reloc_size (howto),
+                                address_expr,
+                                reloc_type[0] == BFD_RELOC_16_PCREL_S2,
+                                reloc_type[0]);
+
+      /* Tag symbols that have a R_MIPS16_26 relocation against them.  */
+      if (reloc_type[0] == BFD_RELOC_MIPS16_JMP
+         && ip->fixp[0]->fx_addsy)
+       *symbol_get_tc (ip->fixp[0]->fx_addsy) = 1;
+
+      /* These relocations can have an addend that won't fit in
+        4 octets for 64bit assembly.  */
+      if (HAVE_64BIT_GPRS
+         && ! howto->partial_inplace
+         && (reloc_type[0] == BFD_RELOC_16
+             || reloc_type[0] == BFD_RELOC_32
+             || reloc_type[0] == BFD_RELOC_MIPS_JMP
+             || reloc_type[0] == BFD_RELOC_GPREL16
+             || reloc_type[0] == BFD_RELOC_MIPS_LITERAL
+             || reloc_type[0] == BFD_RELOC_GPREL32
+             || reloc_type[0] == BFD_RELOC_64
+             || reloc_type[0] == BFD_RELOC_CTOR
+             || reloc_type[0] == BFD_RELOC_MIPS_SUB
+             || reloc_type[0] == BFD_RELOC_MIPS_HIGHEST
+             || reloc_type[0] == BFD_RELOC_MIPS_HIGHER
+             || reloc_type[0] == BFD_RELOC_MIPS_SCN_DISP
+             || reloc_type[0] == BFD_RELOC_MIPS_REL16
+             || reloc_type[0] == BFD_RELOC_MIPS_RELGOT
+             || reloc_type[0] == BFD_RELOC_MIPS16_GPREL
+             || hi16_reloc_p (reloc_type[0])
+             || lo16_reloc_p (reloc_type[0])))
+       ip->fixp[0]->fx_no_overflow = 1;
 
-           default:
-             internalError ();
-           }
+      if (mips_relax.sequence)
+       {
+         if (mips_relax.first_fixup == 0)
+           mips_relax.first_fixup = ip->fixp[0];
        }
-      else if (*reloc_type < BFD_RELOC_UNUSED)
-       need_reloc:
+      else if (reloc_needs_lo_p (*reloc_type))
        {
-         reloc_howto_type *howto;
-         int i;
-
-         /* In a compound relocation, it is the final (outermost)
-            operator that determines the relocated field.  */
-         for (i = 1; i < 3; i++)
-           if (reloc_type[i] == BFD_RELOC_UNUSED)
-             break;
+         struct mips_hi_fixup *hi_fixup;
 
-         howto = bfd_reloc_type_lookup (stdoutput, reloc_type[i - 1]);
-         if (howto == NULL)
+         /* Reuse the last entry if it already has a matching %lo.  */
+         hi_fixup = mips_hi_fixup_list;
+         if (hi_fixup == 0
+             || !fixup_has_matching_lo_p (hi_fixup->fixp))
            {
-             /* To reproduce this failure try assembling gas/testsuites/
-                gas/mips/mips16-intermix.s with a mips-ecoff targeted
-                assembler.  */
-             as_bad (_("Unsupported MIPS relocation number %d"), reloc_type[i - 1]);
-             howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_16);
-           }
-         
-         ip->fixp[0] = fix_new_exp (ip->frag, ip->where,
-                                    bfd_get_reloc_size (howto),
-                                    address_expr,
-                                    reloc_type[0] == BFD_RELOC_16_PCREL_S2,
-                                    reloc_type[0]);
-
-         /* Tag symbols that have a R_MIPS16_26 relocation against them.  */
-         if (reloc_type[0] == BFD_RELOC_MIPS16_JMP
-             && ip->fixp[0]->fx_addsy)
-           *symbol_get_tc (ip->fixp[0]->fx_addsy) = 1;
-
-         /* These relocations can have an addend that won't fit in
-            4 octets for 64bit assembly.  */
-         if (HAVE_64BIT_GPRS
-             && ! howto->partial_inplace
-             && (reloc_type[0] == BFD_RELOC_16
-                 || reloc_type[0] == BFD_RELOC_32
-                 || reloc_type[0] == BFD_RELOC_MIPS_JMP
-                 || reloc_type[0] == BFD_RELOC_GPREL16
-                 || reloc_type[0] == BFD_RELOC_MIPS_LITERAL
-                 || reloc_type[0] == BFD_RELOC_GPREL32
-                 || reloc_type[0] == BFD_RELOC_64
-                 || reloc_type[0] == BFD_RELOC_CTOR
-                 || reloc_type[0] == BFD_RELOC_MIPS_SUB
-                 || reloc_type[0] == BFD_RELOC_MIPS_HIGHEST
-                 || reloc_type[0] == BFD_RELOC_MIPS_HIGHER
-                 || reloc_type[0] == BFD_RELOC_MIPS_SCN_DISP
-                 || reloc_type[0] == BFD_RELOC_MIPS_REL16
-                 || reloc_type[0] == BFD_RELOC_MIPS_RELGOT
-                 || reloc_type[0] == BFD_RELOC_MIPS16_GPREL
-                 || hi16_reloc_p (reloc_type[0])
-                 || lo16_reloc_p (reloc_type[0])))
-           ip->fixp[0]->fx_no_overflow = 1;
-
-         if (mips_relax.sequence)
-           {
-             if (mips_relax.first_fixup == 0)
-               mips_relax.first_fixup = ip->fixp[0];
-           }
-         else if (reloc_needs_lo_p (*reloc_type))
-           {
-             struct mips_hi_fixup *hi_fixup;
-
-             /* Reuse the last entry if it already has a matching %lo.  */
-             hi_fixup = mips_hi_fixup_list;
-             if (hi_fixup == 0
-                 || !fixup_has_matching_lo_p (hi_fixup->fixp))
-               {
-                 hi_fixup = ((struct mips_hi_fixup *)
-                             xmalloc (sizeof (struct mips_hi_fixup)));
-                 hi_fixup->next = mips_hi_fixup_list;
-                 mips_hi_fixup_list = hi_fixup;
-               }
-             hi_fixup->fixp = ip->fixp[0];
-             hi_fixup->seg = now_seg;
+             hi_fixup = ((struct mips_hi_fixup *)
+                         xmalloc (sizeof (struct mips_hi_fixup)));
+             hi_fixup->next = mips_hi_fixup_list;
+             mips_hi_fixup_list = hi_fixup;
            }
+         hi_fixup->fixp = ip->fixp[0];
+         hi_fixup->seg = now_seg;
+       }
 
-         /* Add fixups for the second and third relocations, if given.
-            Note that the ABI allows the second relocation to be
-            against RSS_UNDEF, RSS_GP, RSS_GP0 or RSS_LOC.  At the
-            moment we only use RSS_UNDEF, but we could add support
-            for the others if it ever becomes necessary.  */
-         for (i = 1; i < 3; i++)
-           if (reloc_type[i] != BFD_RELOC_UNUSED)
-             {
-               ip->fixp[i] = fix_new (ip->frag, ip->where,
-                                      ip->fixp[0]->fx_size, NULL, 0,
-                                      FALSE, reloc_type[i]);
+      /* Add fixups for the second and third relocations, if given.
+        Note that the ABI allows the second relocation to be
+        against RSS_UNDEF, RSS_GP, RSS_GP0 or RSS_LOC.  At the
+        moment we only use RSS_UNDEF, but we could add support
+        for the others if it ever becomes necessary.  */
+      for (i = 1; i < 3; i++)
+       if (reloc_type[i] != BFD_RELOC_UNUSED)
+         {
+           ip->fixp[i] = fix_new (ip->frag, ip->where,
+                                  ip->fixp[0]->fx_size, NULL, 0,
+                                  FALSE, reloc_type[i]);
 
-               /* Use fx_tcbit to mark compound relocs.  */
-               ip->fixp[0]->fx_tcbit = 1;
-               ip->fixp[i]->fx_tcbit = 1;
-             }
-       }
+           /* Use fx_tcbit to mark compound relocs.  */
+           ip->fixp[0]->fx_tcbit = 1;
+           ip->fixp[i]->fx_tcbit = 1;
+         }
     }
   install_insn (ip);
 
   /* Update the register mask information.  */
-  if (! mips_opts.mips16)
-    {
-      if ((pinfo & INSN_WRITE_GPR_D) || (pinfo2 & INSN2_READ_GPR_D))
-       mips_gprmask |= 1 << EXTRACT_OPERAND (RD, *ip);
-      if ((pinfo & (INSN_WRITE_GPR_T | INSN_READ_GPR_T)) != 0)
-       mips_gprmask |= 1 << EXTRACT_OPERAND (RT, *ip);
-      if (pinfo & INSN_READ_GPR_S)
-       mips_gprmask |= 1 << EXTRACT_OPERAND (RS, *ip);
-      if (pinfo & INSN_WRITE_GPR_31)
-       mips_gprmask |= 1 << RA;
-      if (pinfo2 & (INSN2_WRITE_GPR_Z | INSN2_READ_GPR_Z))
-       mips_gprmask |= 1 << EXTRACT_OPERAND (RZ, *ip);
-      if (pinfo & INSN_WRITE_FPR_D)
-       mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FD, *ip);
-      if ((pinfo & (INSN_WRITE_FPR_S | INSN_READ_FPR_S)) != 0)
-       mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FS, *ip);
-      if ((pinfo & (INSN_WRITE_FPR_T | INSN_READ_FPR_T)) != 0)
-       mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FT, *ip);
-      if ((pinfo & INSN_READ_FPR_R) != 0)
-       mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FR, *ip);
-      if (pinfo2 & (INSN2_WRITE_FPR_Z | INSN2_READ_FPR_Z))
-       mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FZ, *ip);
-      if (pinfo & INSN_COP)
-       {
-         /* 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;
-    }
-  else
-    {
-      if (pinfo & (MIPS16_INSN_WRITE_X | MIPS16_INSN_READ_X))
-       mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RX, *ip);
-      if (pinfo & (MIPS16_INSN_WRITE_Y | MIPS16_INSN_READ_Y))
-       mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RY, *ip);
-      if (pinfo & MIPS16_INSN_WRITE_Z)
-       mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RZ, *ip);
-      if (pinfo & (MIPS16_INSN_WRITE_T | MIPS16_INSN_READ_T))
-       mips_gprmask |= 1 << TREG;
-      if (pinfo & (MIPS16_INSN_WRITE_SP | MIPS16_INSN_READ_SP))
-       mips_gprmask |= 1 << SP;
-      if (pinfo & (MIPS16_INSN_WRITE_31 | MIPS16_INSN_READ_31))
-       mips_gprmask |= 1 << RA;
-      if (pinfo & MIPS16_INSN_WRITE_GPR_Y)
-       mips_gprmask |= 1 << MIPS16OP_EXTRACT_REG32R (ip->insn_opcode);
-      if (pinfo & MIPS16_INSN_READ_Z)
-       mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip);
-      if (pinfo & MIPS16_INSN_READ_GPR_X)
-       mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (REGR32, *ip);
-    }
+  mips_gprmask |= gpr_read_mask (ip) | gpr_write_mask (ip);
+  mips_cprmask[1] |= fpr_read_mask (ip) | fpr_write_mask (ip);
 
-  if (mips_relax.sequence != 2 && !mips_opts.noreorder)
+  switch (method)
     {
-      /* Filling the branch delay slot is more complex.  We try to
-        switch the branch with the previous instruction, which we can
-        do if the previous instruction does not set up a condition
-        that the branch tests and if the branch is not itself the
-        target of any branch.  */
-      if ((pinfo & INSN_UNCOND_BRANCH_DELAY)
-         || (pinfo & INSN_COND_BRANCH_DELAY))
-       {
-         if (mips_optimize < 2
-             /* If we have seen .set volatile or .set nomove, don't
-                optimize.  */
-             || mips_opts.nomove != 0
-             /* We can't swap if the previous instruction's position
-                is fixed.  */
-             || history[0].fixed_p
-             /* If the previous previous insn was in a .set
-                noreorder, we can't swap.  Actually, the MIPS
-                assembler will swap in this situation.  However, gcc
-                configured -with-gnu-as will generate code like
-                  .set noreorder
-                  lw   $4,XXX
-                  .set reorder
-                  INSN
-                  bne  $4,$0,foo
-                in which we can not swap the bne and INSN.  If gcc is
-                not configured -with-gnu-as, it does not output the
-                .set pseudo-ops.  */
-             || history[1].noreorder_p
-             /* If the branch is itself the target of a branch, we
-                can not swap.  We cheat on this; all we check for is
-                whether there is a label on this instruction.  If
-                there are any branches to anything other than a
-                label, users must use .set noreorder.  */
-             || si->label_list != NULL
-             /* If the previous instruction is in a variant frag
-                other than this branch's one, we cannot do the swap.
-                This does not apply to the mips16, which uses variant
-                frags for different purposes.  */
-             || (! mips_opts.mips16
-                 && prev_insn_frag_type == rs_machine_dependent)
-             /* Check for conflicts between the branch and the instructions
-                before the candidate delay slot.  */
-             || nops_for_insn (history + 1, ip) > 0
-             /* Check for conflicts between the swapped sequence and the
-                target of the branch.  */
-             || nops_for_sequence (2, history + 1, ip, history) > 0
-             /* We do not swap with a trap instruction, since it
-                complicates trap handlers to have the trap
-                instruction be in a delay slot.  */
-             || (prev_pinfo & INSN_TRAP)
-             /* If the branch reads a register that the previous
-                instruction sets, we can not swap.  */
-             || (! mips_opts.mips16
-                 && (prev_pinfo & INSN_WRITE_GPR_T)
-                 && insn_uses_reg (ip, EXTRACT_OPERAND (RT, history[0]),
-                                   MIPS_GR_REG))
-             || (! mips_opts.mips16
-                 && (prev_pinfo & INSN_WRITE_GPR_D)
-                 && insn_uses_reg (ip, EXTRACT_OPERAND (RD, history[0]),
-                                   MIPS_GR_REG))
-             || (! mips_opts.mips16
-                 && (prev_pinfo2 & INSN2_WRITE_GPR_Z)
-                 && insn_uses_reg (ip, EXTRACT_OPERAND (RZ, history[0]),
-                                   MIPS_GR_REG))
-             || (mips_opts.mips16
-                 && (((prev_pinfo & MIPS16_INSN_WRITE_X)
-                      && (insn_uses_reg
-                          (ip, MIPS16_EXTRACT_OPERAND (RX, history[0]),
-                           MIPS16_REG)))
-                     || ((prev_pinfo & MIPS16_INSN_WRITE_Y)
-                         && (insn_uses_reg
-                             (ip, MIPS16_EXTRACT_OPERAND (RY, history[0]),
-                              MIPS16_REG)))
-                     || ((prev_pinfo & MIPS16_INSN_WRITE_Z)
-                         && (insn_uses_reg
-                             (ip, MIPS16_EXTRACT_OPERAND (RZ, history[0]),
-                              MIPS16_REG)))
-                     || ((prev_pinfo & MIPS16_INSN_WRITE_T)
-                         && insn_uses_reg (ip, TREG, MIPS_GR_REG))
-                     || ((prev_pinfo & MIPS16_INSN_WRITE_31)
-                         && insn_uses_reg (ip, RA, MIPS_GR_REG))
-                     || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y)
-                         && insn_uses_reg (ip,
-                                           MIPS16OP_EXTRACT_REG32R
-                                             (history[0].insn_opcode),
-                                           MIPS_GR_REG))))
-             /* If the branch writes a register that the previous
-                instruction sets, we can not swap (we know that
-                branches write only to RD or to $31).  */
-             || (! mips_opts.mips16
-                 && (prev_pinfo & INSN_WRITE_GPR_T)
-                 && (((pinfo & INSN_WRITE_GPR_D)
-                      && (EXTRACT_OPERAND (RT, history[0])
-                          == EXTRACT_OPERAND (RD, *ip)))
-                     || ((pinfo & INSN_WRITE_GPR_31)
-                         && EXTRACT_OPERAND (RT, history[0]) == RA)))
-             || (! mips_opts.mips16
-                 && (prev_pinfo & INSN_WRITE_GPR_D)
-                 && (((pinfo & INSN_WRITE_GPR_D)
-                      && (EXTRACT_OPERAND (RD, history[0])
-                          == EXTRACT_OPERAND (RD, *ip)))
-                     || ((pinfo & INSN_WRITE_GPR_31)
-                         && EXTRACT_OPERAND (RD, history[0]) == RA)))
-             || (mips_opts.mips16
-                 && (pinfo & MIPS16_INSN_WRITE_31)
-                 && ((prev_pinfo & MIPS16_INSN_WRITE_31)
-                     || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y)
-                         && (MIPS16OP_EXTRACT_REG32R (history[0].insn_opcode)
-                             == RA))))
-             /* If the branch writes a register that the previous
-                instruction reads, we can not swap (we know that
-                branches only write to RD or to $31).  */
-             || (! mips_opts.mips16
-                 && (pinfo & INSN_WRITE_GPR_D)
-                 && insn_uses_reg (&history[0],
-                                   EXTRACT_OPERAND (RD, *ip),
-                                   MIPS_GR_REG))
-             || (! mips_opts.mips16
-                 && (pinfo & INSN_WRITE_GPR_31)
-                 && insn_uses_reg (&history[0], RA, MIPS_GR_REG))
-             || (mips_opts.mips16
-                 && (pinfo & MIPS16_INSN_WRITE_31)
-                 && insn_uses_reg (&history[0], RA, MIPS_GR_REG))
-             /* If one instruction sets a condition code and the
-                 other one uses a condition code, we can not swap.  */
-             || ((pinfo & INSN_READ_COND_CODE)
-                 && (prev_pinfo & INSN_WRITE_COND_CODE))
-             || ((pinfo & INSN_WRITE_COND_CODE)
-                 && (prev_pinfo & INSN_READ_COND_CODE))
-             /* If the previous instruction uses the PC, we can not
-                 swap.  */
-             || (mips_opts.mips16
-                 && (prev_pinfo & MIPS16_INSN_READ_PC))
-             /* If the previous instruction had a fixup in mips16
-                 mode, we can not swap.  This normally means that the
-                 previous instruction was a 4 byte branch anyhow.  */
-             || (mips_opts.mips16 && history[0].fixp[0])
-             /* If the previous instruction is a sync, sync.l, or
-                sync.p, we can not swap.  */
-             || (prev_pinfo & INSN_SYNC)
-             /* If the previous instruction is an ERET or
-                DERET, avoid the swap.  */
-              || (history[0].insn_opcode == INSN_ERET)
-              || (history[0].insn_opcode == INSN_DERET))
-           {
-             if (mips_opts.mips16
-                 && (pinfo & INSN_UNCOND_BRANCH_DELAY)
-                 && (pinfo & (MIPS16_INSN_READ_X | MIPS16_INSN_READ_31))
-                 && ISA_SUPPORTS_MIPS16E)
-               {
-                 /* Convert MIPS16 jr/jalr into a "compact" jump.  */
-                 ip->insn_opcode |= 0x0080;
-                 install_insn (ip);
-                 insert_into_history (0, 1, ip);
-               } 
-             else
-               {
-                 /* We could do even better for unconditional branches to
-                    portions of this object file; we could pick up the
-                    instruction at the destination, put it in the delay
-                    slot, and bump the destination address.  */
-                 insert_into_history (0, 1, ip);
-                 emit_nop ();
-               }
-               
-             if (mips_relax.sequence)
-               mips_relax.sizes[mips_relax.sequence - 1] += 4;
-           }
-         else
-           {
-             /* It looks like we can actually do the swap.  */
-             struct mips_cl_insn delay = history[0];
-             if (mips_opts.mips16)
-               {
-                 know (delay.frag == ip->frag);
-                  move_insn (ip, delay.frag, delay.where);
-                 move_insn (&delay, ip->frag, ip->where + insn_length (ip));
-               }
-             else if (relaxed_branch)
-               {
-                 /* Add the delay slot instruction to the end of the
-                    current frag and shrink the fixed part of the
-                    original frag.  If the branch occupies the tail of
-                    the latter, move it backwards to cover the gap.  */
-                 delay.frag->fr_fix -= 4;
-                 if (delay.frag == ip->frag)
-                   move_insn (ip, ip->frag, ip->where - 4);
-                 add_fixed_insn (&delay);
-               }
-             else
-               {
-                 move_insn (&delay, ip->frag, ip->where);
-                 move_insn (ip, history[0].frag, history[0].where);
-               }
-             history[0] = *ip;
-             delay.fixed_p = 1;
-             insert_into_history (0, 1, &delay);
-           }
+    case APPEND_ADD:
+      insert_into_history (0, 1, ip);
+      break;
 
-         /* If that was an unconditional branch, forget the previous
-            insn information.  */
-         if (pinfo & INSN_UNCOND_BRANCH_DELAY)
-           {
-             mips_no_prev_insn ();
-           }
-       }
-      else if (pinfo & INSN_COND_BRANCH_LIKELY)
-       {
-         /* We don't yet optimize a branch likely.  What we should do
-            is look at the target, copy the instruction found there
-            into the delay slot, and increment the branch to jump to
-            the next instruction.  */
-         insert_into_history (0, 1, ip);
-         emit_nop ();
-       }
-      else
-       insert_into_history (0, 1, ip);
+    case APPEND_ADD_WITH_NOP:
+      insert_into_history (0, 1, ip);
+      emit_nop ();
+      if (mips_relax.sequence)
+       mips_relax.sizes[mips_relax.sequence - 1] += 4;
+      break;
+
+    case APPEND_ADD_COMPACT:
+      /* Convert MIPS16 jr/jalr into a "compact" jump.  */
+      gas_assert (mips_opts.mips16);
+      ip->insn_opcode |= 0x0080;
+      find_altered_mips16_opcode (ip);
+      install_insn (ip);
+      insert_into_history (0, 1, ip);
+      break;
+
+    case APPEND_SWAP:
+      {
+       struct mips_cl_insn delay = history[0];
+       if (mips_opts.mips16)
+         {
+           know (delay.frag == ip->frag);
+           move_insn (ip, delay.frag, delay.where);
+           move_insn (&delay, ip->frag, ip->where + insn_length (ip));
+         }
+       else if (relaxed_branch)
+         {
+           /* Add the delay slot instruction to the end of the
+              current frag and shrink the fixed part of the
+              original frag.  If the branch occupies the tail of
+              the latter, move it backwards to cover the gap.  */
+           delay.frag->fr_fix -= 4;
+           if (delay.frag == ip->frag)
+             move_insn (ip, ip->frag, ip->where - 4);
+           add_fixed_insn (&delay);
+         }
+       else
+         {
+           move_insn (&delay, ip->frag, ip->where);
+           move_insn (ip, history[0].frag, history[0].where);
+         }
+       history[0] = *ip;
+       delay.fixed_p = 1;
+       insert_into_history (0, 1, &delay);
+      }
+      break;
     }
-  else
-    insert_into_history (0, 1, ip);
+
+  /* If we have just completed an unconditional branch, clear the history.  */
+  if ((history[1].insn_mo->pinfo & INSN_UNCOND_BRANCH_DELAY)
+      || (mips_opts.mips16
+         && (history[0].insn_mo->pinfo & MIPS16_INSN_UNCOND_BRANCH)))
+    mips_no_prev_insn ();
 
   /* We just output an insn, so the next one doesn't have a label.  */
   mips_clear_insn_labels ();
@@ -3470,7 +3727,7 @@ mips_emit_delays (void)
 {
   if (! mips_opts.noreorder)
     {
-      int nops = nops_for_insn (history, NULL);
+      int nops = nops_for_insn (0, history, NULL);
       if (nops > 0)
        {
          while (nops-- > 0)
@@ -3498,7 +3755,7 @@ start_noreorder (void)
       /* Insert any nops that might be needed between the .set noreorder
         block and the previous instructions.  We will later remove any
         nops that turn out not to be needed.  */
-      nops = nops_for_insn (history, NULL);
+      nops = nops_for_insn (0, history, NULL);
       if (nops > 0)
        {
          if (mips_optimize != 0)
@@ -6523,6 +6780,9 @@ macro (struct mips_cl_insn *ip)
     case M_CACHE_AB:
       s = "cache";
       goto st;
+    case M_PREF_AB:
+      s = "pref";
+      goto st;
     case M_SDC1_AB:
       s = "sdc1";
       coproc = 1;
@@ -6564,7 +6824,7 @@ macro (struct mips_cl_insn *ip)
          || mask == M_L_DAB
          || mask == M_S_DAB)
        fmt = "T,o(b)";
-      else if (mask == M_CACHE_AB)
+      else if (mask == M_CACHE_AB || mask == M_PREF_AB)
        fmt = "k,o(b)";
       else if (coproc)
        fmt = "E,o(b)";
@@ -14145,7 +14405,8 @@ relaxed_branch_length (fragS *fragp, asection *sec, int update)
 
   if (fragp && update && toofar != RELAX_BRANCH_TOOFAR (fragp->fr_subtype))
     fragp->fr_subtype
-      = RELAX_BRANCH_ENCODE (RELAX_BRANCH_UNCOND (fragp->fr_subtype),
+      = RELAX_BRANCH_ENCODE (RELAX_BRANCH_AT (fragp->fr_subtype),
+                            RELAX_BRANCH_UNCOND (fragp->fr_subtype),
                             RELAX_BRANCH_LIKELY (fragp->fr_subtype),
                             RELAX_BRANCH_LINK (fragp->fr_subtype),
                             toofar);
@@ -14248,8 +14509,12 @@ mips_fix_adjustable (fixS *fixp)
       && (S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_MERGE) != 0)
     return 0;
 
-  /* There is no place to store an in-place offset for JALR relocations.  */
-  if (fixp->fx_r_type == BFD_RELOC_MIPS_JALR && HAVE_IN_PLACE_ADDENDS)
+  /* There is no place to store an in-place offset for JALR relocations.
+     Likewise an in-range offset of PC-relative relocations may overflow
+     the in-place relocatable field if recalculated against the start
+     address of the symbol's containing section.  */
+  if (HAVE_IN_PLACE_ADDENDS
+      && (fixp->fx_pcrel || fixp->fx_r_type == BFD_RELOC_MIPS_JALR))
     return 0;
 
 #ifdef OBJ_ELF
@@ -14437,7 +14702,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
          int i;
 
          as_warn_where (fragp->fr_file, fragp->fr_line,
-                        _("relaxed out-of-range branch into a jump"));
+                        _("Relaxed out-of-range branch into a jump"));
 
          if (RELAX_BRANCH_UNCOND (fragp->fr_subtype))
            goto uncond;
@@ -14551,8 +14816,11 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
            }
          else
            {
+             unsigned long at = RELAX_BRANCH_AT (fragp->fr_subtype);
+
              /* lw/ld $at, <sym>($gp)  R_MIPS_GOT16 */
-             insn = HAVE_64BIT_ADDRESSES ? 0xdf810000 : 0x8f810000;
+             insn = HAVE_64BIT_ADDRESSES ? 0xdf800000 : 0x8f800000;
+             insn |= at << OP_SH_RT;
              exp.X_op = O_symbol;
              exp.X_add_symbol = fragp->fr_symbol;
              exp.X_add_number = fragp->fr_offset;
@@ -14579,7 +14847,8 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
                }
 
              /* d/addiu $at, $at, <sym>  R_MIPS_LO16 */
-             insn = HAVE_64BIT_ADDRESSES ? 0x64210000 : 0x24210000;
+             insn = HAVE_64BIT_ADDRESSES ? 0x64000000 : 0x24000000;
+             insn |= at << OP_SH_RS | at << OP_SH_RT;
 
              fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
                                  4, &exp, FALSE, BFD_RELOC_LO16);
@@ -14591,9 +14860,10 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
 
              /* j(al)r $at.  */
              if (RELAX_BRANCH_LINK (fragp->fr_subtype))
-               insn = 0x0020f809;
+               insn = 0x0000f809;
              else
-               insn = 0x00200008;
+               insn = 0x00000008;
+             insn |= at << OP_SH_RS;
 
              md_number_to_chars ((char *) buf, insn, 4);
              buf += 4;
This page took 0.104457 seconds and 4 git commands to generate.