diff options
author | Catherine Moore <clm@redhat.com> | 2009-04-09 15:55:59 +0000 |
---|---|---|
committer | Catherine Moore <clm@redhat.com> | 2009-04-09 15:55:59 +0000 |
commit | 6a32d87432882fa1e7bcfca440b080d6667792c3 (patch) | |
tree | 25d73571a34619a8af0ffe46d21c9ad64ae63226 /gas/config | |
parent | ef699244c2601d69942baa0d913875f3eda3dce6 (diff) | |
download | gdb-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.c | 134 |
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\ |