2011-04-20 Catherine Moore <clm@codesourcery.com>
authorCatherine Moore <clm@redhat.com>
Wed, 20 Apr 2011 16:45:35 +0000 (16:45 +0000)
committerCatherine Moore <clm@redhat.com>
Wed, 20 Apr 2011 16:45:35 +0000 (16:45 +0000)
            David Ung <davidu@mips.com>

* config/mips.c (mips_cl_insn): Add new field complete_p.
(create_insn): Initialize complete_p to zero.
(BASE_REG_EQ): New.
(fix_24k_align_to): New.
(fix_24k_store_info): Declare.
(fix_24k_sort): New.
(fix_24k_record_store_info): New.
(nops_for_24k): New.
(nops_for_insn): Call nops_for_24k.
(append_insn): Move O_constant expression handling.

gas/ChangeLog
gas/config/tc-mips.c

index 6a21ff3..9721288 100644 (file)
@@ -1,3 +1,17 @@
+2011-04-20  Catherine Moore  <clm@codesourcery.com>
+            David Ung <davidu@mips.com>
+
+       * config/mips.c (mips_cl_insn): Add new field complete_p.
+       (create_insn): Initialize complete_p to zero.
+       (BASE_REG_EQ): New.
+       (fix_24k_align_to): New.
+       (fix_24k_store_info): Declare.
+       (fix_24k_sort): New.
+       (fix_24k_record_store_info): New.
+       (nops_for_24k): New.
+       (nops_for_insn): Call nops_for_24k.
+       (append_insn): Move O_constant expression handling.
+       
 2011-04-20  Alan Modra  <amodra@gmail.com>
 
        * hash.c (set_gas_hash_table_size): Use bfd_hash_set_default_size.
index 4374ed9..33d8896 100644 (file)
@@ -156,6 +156,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.  */
@@ -1384,6 +1387,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.  */
@@ -2680,6 +2684,189 @@ nops_for_vr4130 (const struct mips_cl_insn *hist,
   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;
+}
+
+/* 24K Errata: Lost Data on Stores During Refill. 
+  
+  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 (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 INSN is definitely not a store, there's nothing to worry about.  */
+  if (insn && (insn->insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
+    return 0;
+
+  /* Likewise, the previous instruction wasn't a store.  */
+  if ((hist[0].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
+    return 0;
+
+  /* If we don't know what came before, assume the worst.  */
+  if (hist[1].frag == NULL)
+    return 1;
+
+  /* If the instruction was not a store, there's nothing to worry about.  */
+  if ((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 (insn == NULL
+      || !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,
@@ -2706,6 +2893,13 @@ nops_for_insn (const struct mips_cl_insn *hist,
        nops = tmp_nops;
     }
 
+  if (mips_fix_24k)
+    {
+      tmp_nops = nops_for_24k (hist, insn);
+      if (tmp_nops > nops)
+       nops = tmp_nops;
+    }
+
   return nops;
 }
 
@@ -2836,6 +3030,82 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
   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)
+           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;
+         ip->complete_p = 0;
+         break;
+
+       default:
+         internalError ();
+       }       
+    }
+
   if (mips_relax.sequence != 2 && !mips_opts.noreorder)
     {
       /* There are a lot of optimizations we could do that we don't.
@@ -3007,75 +3277,8 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
 
   if (address_expr != NULL && *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;
-
-           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;
-
-           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;
-
-           default:
-             internalError ();
-           }
-       }
-      else if (*reloc_type < BFD_RELOC_UNUSED)
+      if (!ip->complete_p
+          && *reloc_type < BFD_RELOC_UNUSED)
        need_reloc:
        {
          reloc_howto_type *howto;