aboutsummaryrefslogtreecommitdiff
path: root/gas/config
diff options
context:
space:
mode:
authorCatherine Moore <clm@redhat.com>2009-04-09 15:55:59 +0000
committerCatherine Moore <clm@redhat.com>2009-04-09 15:55:59 +0000
commit6a32d87432882fa1e7bcfca440b080d6667792c3 (patch)
tree25d73571a34619a8af0ffe46d21c9ad64ae63226 /gas/config
parentef699244c2601d69942baa0d913875f3eda3dce6 (diff)
downloadgdb-6a32d87432882fa1e7bcfca440b080d6667792c3.zip
gdb-6a32d87432882fa1e7bcfca440b080d6667792c3.tar.gz
gdb-6a32d87432882fa1e7bcfca440b080d6667792c3.tar.bz2
2009-04-09 Catherine Moore <clm@codesourcery.com>
* config/tc-mips.c (mips_fix_24k): Declare. (check_for_24k_errata): New. (mips_cleanup): Call check_for_24k_errata. (start_noreorder): Likewise. (md_mips_end): Likewise. (s_change_sec): Likewise. (s_change_section): Likewise. (append_insn): Call check_for_24k_errata. Prevent ERET/DERET instructions from being moved into delay slots. (OPTION_FIX_24K): New. (OPTION_NO_FIX_24k) New. (md_longopts): Add "mfix-24k" and "mno-fix-24k". (md_parse_option): Handle fix-24k options. (md_show_usage): Display fix-24k options. * doc/c-mips.texi: Document. * testsuite/gas/mips/mips.exp: Run new tests. * testsuite/gas/mips/eret.s: New test. * testsuite/gas/mips/eret.d: New test output. * testsuite/gas/mips/eret.l: New test output.
Diffstat (limited to 'gas/config')
-rw-r--r--gas/config/tc-mips.c134
1 files changed, 131 insertions, 3 deletions
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
index 62d4e4f..5c85845 100644
--- a/gas/config/tc-mips.c
+++ b/gas/config/tc-mips.c
@@ -760,6 +760,9 @@ static int mips_fix_vr4120;
/* ...likewise -mfix-vr4130. */
static int mips_fix_vr4130;
+/* ...likewise -mfix-24k. */
+static int mips_fix_24k;
+
/* We don't relax branches by default, since this causes us to expand
`la .l2 - .l1' if there's a branch between .l1 and .l2, because we
fail to compute the offset before expanding the macro to the most
@@ -1789,6 +1792,85 @@ reg_lookup (char **s, unsigned int types, unsigned int *regnop)
return reg >= 0;
}
+#define INSN_ERET 0x42000018
+#define INSN_DERET 0x4200001f
+
+/* Implement the ERET/DERET Errata for MIPS 24k.
+
+ If an ERET/DERET is encountered in a noreorder block,
+ warn if the ERET/DERET is followed by a branch instruction.
+ Also warn if the ERET/DERET is the last instruction in the
+ noreorder block.
+
+ IF an ERET/DERET is in a reorder block and is followed by a
+ branch instruction, insert a nop. */
+
+static void
+check_for_24k_errata (struct mips_cl_insn *insn, int eret_ndx)
+{
+ bfd_boolean next_insn_is_branch = FALSE;
+
+ /* eret_ndx will be -1 for the last instruction in a section
+ and the ERET/DERET will be in insn, not history. */
+ if (insn
+ && eret_ndx == -1
+ && (insn->insn_opcode == INSN_ERET
+ || insn->insn_opcode == INSN_DERET)
+ && insn->noreorder_p)
+ {
+ as_warn (_("ERET and DERET must be followed by a NOP on the 24K."));
+ return;
+ }
+
+ if (history[eret_ndx].insn_opcode != INSN_ERET
+ && history[eret_ndx].insn_opcode != INSN_DERET)
+ return;
+
+ if (!insn)
+ {
+ if (history[eret_ndx].noreorder_p)
+ as_warn (_("ERET and DERET must be followed by a NOP on the 24K."));
+ return;
+ }
+
+ next_insn_is_branch = ((insn->insn_opcode == INSN_ERET)
+ || (insn->insn_opcode == INSN_DERET)
+ || (insn->insn_mo->pinfo
+ & (INSN_UNCOND_BRANCH_DELAY
+ | INSN_COND_BRANCH_DELAY
+ | INSN_COND_BRANCH_LIKELY)));
+
+ if (next_insn_is_branch && history[eret_ndx].noreorder_p)
+ {
+ as_warn (_("ERET and DERET must be followed by a NOP on the 24K."));
+ return;
+ }
+
+ /* Emit nop if the next instruction is a branch. */
+ if (next_insn_is_branch)
+ {
+ long nop_where, br_where;
+ struct frag *nop_frag, *br_frag;
+ struct mips_cl_insn br_insn, nop_insn;
+
+ emit_nop ();
+
+ nop_insn = history[eret_ndx - 1];
+ nop_frag = history[eret_ndx - 1].frag;
+ nop_where = history[eret_ndx - 1].where;
+
+ br_insn = history[eret_ndx];
+ br_frag = history[eret_ndx].frag;
+ br_where = history[eret_ndx].where;
+
+ move_insn (&nop_insn, br_frag, br_where);
+ move_insn (&br_insn, nop_frag, nop_where);
+
+ history[eret_ndx-1] = br_insn;
+ history[eret_ndx] = nop_insn;
+ }
+}
+
/* Return TRUE if opcode MO is valid on the currently selected ISA and
architecture. If EXPANSIONP is TRUE then this check is done while
expanding a macro. Use is_opcode_valid_16 for MIPS16 opcodes. */
@@ -2074,6 +2156,9 @@ md_begin (void)
void
md_mips_end (void)
{
+ if (mips_fix_24k)
+ check_for_24k_errata ((struct mips_cl_insn *) &history[0], -1);
+
if (! ECOFF_DEBUGGING)
md_obj_end ();
}
@@ -2705,6 +2790,7 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
bfd_reloc_code_real_type *reloc_type)
{
unsigned long prev_pinfo, pinfo;
+ int hndx_24k = 0;
relax_stateT prev_insn_frag_type = 0;
bfd_boolean relaxed_branch = FALSE;
segment_info_type *si = seg_info (now_seg);
@@ -3238,7 +3324,11 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
|| (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))
+ || (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)
@@ -3258,6 +3348,8 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
slot, and bump the destination address. */
insert_into_history (0, 1, ip);
emit_nop ();
+ if (mips_fix_24k)
+ hndx_24k++;
}
if (mips_relax.sequence)
@@ -3297,7 +3389,14 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
/* If that was an unconditional branch, forget the previous
insn information. */
if (pinfo & INSN_UNCOND_BRANCH_DELAY)
- mips_no_prev_insn ();
+ {
+ /* Check for eret/deret before clearing history. */
+ if (mips_fix_24k)
+ check_for_24k_errata (
+ (struct mips_cl_insn *) &history[hndx_24k],
+ hndx_24k+1);
+ mips_no_prev_insn ();
+ }
}
else if (pinfo & INSN_COND_BRANCH_LIKELY)
{
@@ -3307,6 +3406,8 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
the next instruction. */
insert_into_history (0, 1, ip);
emit_nop ();
+ if (mips_fix_24k)
+ hndx_24k++;
}
else
insert_into_history (0, 1, ip);
@@ -3314,6 +3415,10 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
else
insert_into_history (0, 1, ip);
+ if (mips_fix_24k)
+ check_for_24k_errata ((struct mips_cl_insn *) &history[hndx_24k],
+ hndx_24k+1);
+
/* We just output an insn, so the next one doesn't have a label. */
mips_clear_insn_labels ();
}
@@ -3400,6 +3505,9 @@ start_noreorder (void)
static void
end_noreorder (void)
{
+ if (mips_fix_24k)
+ check_for_24k_errata (NULL, 0);
+
mips_opts.noreorder--;
if (mips_opts.noreorder == 0 && prev_nop_frag != NULL)
{
@@ -11175,7 +11283,9 @@ enum options
OPTION_M3900,
OPTION_NO_M3900,
OPTION_M7000_HILO_FIX,
- OPTION_MNO_7000_HILO_FIX,
+ OPTION_MNO_7000_HILO_FIX,
+ OPTION_FIX_24K,
+ OPTION_NO_FIX_24K,
OPTION_FIX_VR4120,
OPTION_NO_FIX_VR4120,
OPTION_FIX_VR4130,
@@ -11268,6 +11378,8 @@ struct option md_longopts[] =
{"mno-fix-vr4120", no_argument, NULL, OPTION_NO_FIX_VR4120},
{"mfix-vr4130", no_argument, NULL, OPTION_FIX_VR4130},
{"mno-fix-vr4130", no_argument, NULL, OPTION_NO_FIX_VR4130},
+ {"mfix-24k", no_argument, NULL, OPTION_FIX_24K},
+ {"mno-fix-24k", no_argument, NULL, OPTION_NO_FIX_24K},
/* Miscellaneous options. */
{"trap", no_argument, NULL, OPTION_TRAP},
@@ -11521,6 +11633,14 @@ md_parse_option (int c, char *arg)
mips_opts.ase_smartmips = 0;
break;
+ case OPTION_FIX_24K:
+ mips_fix_24k = 1;
+ break;
+
+ case OPTION_NO_FIX_24K:
+ mips_fix_24k = 0;
+ break;
+
case OPTION_FIX_VR4120:
mips_fix_vr4120 = 1;
break;
@@ -12468,6 +12588,10 @@ s_change_sec (int sec)
#endif
mips_emit_delays ();
+
+ if (mips_fix_24k)
+ check_for_24k_errata ((struct mips_cl_insn *) &history[0], -1);
+
switch (sec)
{
case 't':
@@ -12526,6 +12650,9 @@ s_change_section (int ignore ATTRIBUTE_UNUSED)
if (!IS_ELF)
return;
+ if (mips_fix_24k)
+ check_for_24k_errata ((struct mips_cl_insn *) &history[0], -1);
+
section_name = input_line_pointer;
c = get_symbol_end ();
if (c)
@@ -15457,6 +15584,7 @@ MIPS options:\n\
fprintf (stream, _("\
-mfix-vr4120 work around certain VR4120 errata\n\
-mfix-vr4130 work around VR4130 mflo/mfhi errata\n\
+-mfix-24k insert a nop after ERET and DERET instructions\n\
-mgp32 use 32-bit GPRs, regardless of the chosen ISA\n\
-mfp32 use 32-bit FPRs, regardless of the chosen ISA\n\
-msym32 assume all symbols have 32-bit values\n\