You should have received a copy of the GNU General Public License
along with GAS; see the file COPYING. If not, write to the Free
- Software Foundation, 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA. */
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
#include "as.h"
#include "config.h"
#include "opcode/mips.h"
#include "itbl-ops.h"
#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
#ifdef DEBUG
#define DBG(x) printf x
equivalent to seeing no -g option at all. */
static int mips_debug = 0;
-/* The maximum number of NOPs needed to satisfy a hardware hazard
- or processor errata. */
-#define MAX_NOPS 2
+/* The maximum number of NOPs needed to avoid the VR4130 mflo/mfhi errata. */
+#define MAX_VR4130_NOPS 4
+
+/* The maximum number of NOPs needed to fill delay slots. */
+#define MAX_DELAY_NOPS 2
+
+/* The maximum number of NOPs needed for any purpose. */
+#define MAX_NOPS 4
/* A list of previous instructions, with index 0 being the most recent.
We need to look back MAX_NOPS instructions when filling delay slots
/* True if -mfix-vr4120 is in force. */
static int mips_fix_vr4120;
+/* ...likewise -mfix-vr4130. */
+static int mips_fix_vr4130;
+
/* 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
(((x) &~ (offsetT) 0x7fff) == 0 \
|| (((x) &~ (offsetT) 0x7fff) == ~ (offsetT) 0x7fff))
+/* Is the given value a zero-extended 32-bit value? Or a negated one? */
+#define IS_ZEXT_32BIT_NUM(x) \
+ (((x) &~ (offsetT) 0xffffffff) == 0 \
+ || (((x) &~ (offsetT) 0xffffffff) == ~ (offsetT) 0xffffffff))
+
/* Replace bits MASK << SHIFT of STRUCT with the equivalent bits in
VALUE << SHIFT. VALUE is evaluated exactly once. */
#define INSERT_BITS(STRUCT, VALUE, MASK, SHIFT) \
static void append_insn
(struct mips_cl_insn *ip, expressionS *p, bfd_reloc_code_real_type *r);
-static void mips_no_prev_insn (int);
+static void mips_no_prev_insn (void);
static void mips16_macro_build
(expressionS *, const char *, const char *, va_list);
static void load_register (int, expressionS *, int);
&zero_address_frag));
}
- mips_no_prev_insn (FALSE);
+ mips_no_prev_insn ();
mips_gprmask = 0;
mips_cprmask[0] = 0;
return 0;
}
+/* Move all labels in insn_labels to the current insertion point. */
+
+static void
+mips_move_labels (void)
+{
+ struct insn_label_list *l;
+ valueT val;
+
+ for (l = insn_labels; l != NULL; l = l->next)
+ {
+ assert (S_GET_SEGMENT (l->label) == now_seg);
+ symbol_set_frag (l->label, frag_now);
+ val = (valueT) frag_now_fix ();
+ /* mips16 text labels are stored as odd. */
+ if (mips_opts.mips16)
+ ++val;
+ S_SET_VALUE (l->label, val);
+ }
+}
+
/* Mark instruction labels in mips16 mode. This permits the linker to
handle them specially, such as generating jalx instructions when
needed. We also make them odd for the duration of the assembly, in
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 HISTORY. */
+
+static int
+nops_for_vr4130 (const struct mips_cl_insn *history,
+ const struct mips_cl_insn *insn)
+{
+ int i, j, reg;
+
+ /* Check if the instruction writes to HI or LO. MTHI and MTLO
+ are not affected by the errata. */
+ if (insn != 0
+ && ((insn->insn_mo->pinfo & (INSN_WRITE_HI | INSN_WRITE_LO)) == 0
+ || strcmp (insn->insn_mo->name, "mtlo") == 0
+ || strcmp (insn->insn_mo->name, "mthi") == 0))
+ return 0;
+
+ /* Search for the first MFLO or MFHI. */
+ for (i = 0; i < MAX_VR4130_NOPS; i++)
+ if (!history[i].noreorder_p && MF_HILO_INSN (history[i].insn_mo->pinfo))
+ {
+ /* Extract the destination register. */
+ if (mips_opts.mips16)
+ reg = mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, history[i])];
+ else
+ reg = EXTRACT_OPERAND (RD, history[i]);
+
+ /* No nops are needed if INSN reads that register. */
+ if (insn != NULL && insn_uses_reg (insn, reg, MIPS_GR_REG))
+ return 0;
+
+ /* ...or if any of the intervening instructions do. */
+ for (j = 0; j < i; j++)
+ if (insn_uses_reg (&history[j], reg, MIPS_GR_REG))
+ return 0;
+
+ return MAX_VR4130_NOPS - i;
+ }
+ return 0;
+}
+
/* Return the number of nops that would be needed if instruction INSN
immediately followed the MAX_NOPS instructions given by HISTORY,
where HISTORY[0] is the most recent instruction. If INSN is null,
int i, nops, tmp_nops;
nops = 0;
- for (i = 0; i < MAX_NOPS; i++)
+ for (i = 0; i < MAX_DELAY_NOPS; i++)
if (!history[i].noreorder_p)
{
tmp_nops = insns_between (history + i, insn) - i;
if (tmp_nops > nops)
nops = tmp_nops;
}
+
+ if (mips_fix_vr4130)
+ {
+ tmp_nops = nops_for_vr4130 (history, insn);
+ if (tmp_nops > nops)
+ nops = tmp_nops;
+ }
+
return nops;
}
fragS *old_frag;
unsigned long old_frag_offset;
int i;
- struct insn_label_list *l;
old_frag = frag_now;
old_frag_offset = frag_now_fix ();
frag_grow (40);
}
- for (l = insn_labels; l != NULL; l = l->next)
- {
- valueT val;
-
- assert (S_GET_SEGMENT (l->label) == now_seg);
- symbol_set_frag (l->label, frag_now);
- val = (valueT) frag_now_fix ();
- /* mips16 text labels are stored as odd. */
- if (mips_opts.mips16)
- ++val;
- S_SET_VALUE (l->label, val);
- }
+ mips_move_labels ();
#ifndef NO_ECOFF_DEBUGGING
if (ECOFF_DEBUGGING)
frags for different purposes. */
|| (! mips_opts.mips16
&& prev_insn_frag_type == rs_machine_dependent)
- /* If the branch reads the condition codes, we don't
- even try to swap, because in the sequence
- ctc1 $X,$31
- INSN
- INSN
- bc1t LABEL
- we can not swap, and I don't feel like handling that
- case. */
- || (! mips_opts.mips16
- && (pinfo & INSN_READ_COND_CODE)
- && ! cop_interlocks)
/* Check for conflicts between the branch and the instructions
before the candidate delay slot. */
|| nops_for_insn (history + 1, ip) > 0
/* If that was an unconditional branch, forget the previous
insn information. */
if (pinfo & INSN_UNCOND_BRANCH_DELAY)
- mips_no_prev_insn (FALSE);
+ mips_no_prev_insn ();
}
else if (pinfo & INSN_COND_BRANCH_LIKELY)
{
mips_clear_insn_labels ();
}
-/* This function forgets that there was any previous instruction or
- label. If PRESERVE is non-zero, it remembers enough information to
- know whether nops are needed before a noreorder section. */
+/* Forget that there was any previous instruction or label. */
static void
-mips_no_prev_insn (int preserve)
+mips_no_prev_insn (void)
{
- size_t i;
-
- if (! preserve)
- {
- prev_nop_frag = NULL;
- prev_nop_frag_holds = 0;
- prev_nop_frag_required = 0;
- prev_nop_frag_since = 0;
- for (i = 0; i < ARRAY_SIZE (history); i++)
- history[i] = (mips_opts.mips16 ? mips16_nop_insn : nop_insn);
- }
- else
- for (i = 0; i < ARRAY_SIZE (history); i++)
- {
- history[i].fixed_p = 1;
- history[i].noreorder_p = 0;
- history[i].mips16_absolute_jump_p = 0;
- }
+ prev_nop_frag = NULL;
+ insert_into_history (0, ARRAY_SIZE (history), NOP_INSN);
mips_clear_insn_labels ();
}
-/* This function must be called whenever we turn on noreorder or emit
- something other than instructions. It inserts any NOPS which might
- be needed by the previous instruction, and clears the information
- kept for the previous instructions. The INSNS parameter is true if
- instructions are to follow. */
+/* This function must be called before we emit something other than
+ instructions. It is like mips_no_prev_insn except that it inserts
+ any NOPS that might be needed by previous instructions. */
-static void
-mips_emit_delays (bfd_boolean insns)
+void
+mips_emit_delays (void)
{
if (! mips_opts.noreorder)
{
int nops = nops_for_insn (history, NULL);
if (nops > 0)
{
- struct insn_label_list *l;
+ while (nops-- > 0)
+ add_fixed_insn (NOP_INSN);
+ mips_move_labels ();
+ }
+ }
+ mips_no_prev_insn ();
+}
+
+/* Start a (possibly nested) noreorder block. */
+
+static void
+start_noreorder (void)
+{
+ if (mips_opts.noreorder == 0)
+ {
+ unsigned int i;
+ int nops;
- if (insns && mips_optimize != 0)
+ /* None of the instructions before the .set noreorder can be moved. */
+ for (i = 0; i < ARRAY_SIZE (history); i++)
+ history[i].fixed_p = 1;
+
+ /* 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);
+ if (nops > 0)
+ {
+ if (mips_optimize != 0)
{
/* Record the frag which holds the nop instructions, so
that we can remove them if we don't need them. */
for (; nops > 0; --nops)
add_fixed_insn (NOP_INSN);
- if (insns)
- {
- /* Move on to a new frag, so that it is safe to simply
- decrease the size of prev_nop_frag. */
- frag_wane (frag_now);
- frag_new (0);
- }
-
- for (l = insn_labels; l != NULL; l = l->next)
- {
- valueT val;
-
- assert (S_GET_SEGMENT (l->label) == now_seg);
- symbol_set_frag (l->label, frag_now);
- val = (valueT) frag_now_fix ();
- /* mips16 text labels are stored as odd. */
- if (mips_opts.mips16)
- ++val;
- S_SET_VALUE (l->label, val);
- }
+ /* Move on to a new frag, so that it is safe to simply
+ decrease the size of prev_nop_frag. */
+ frag_wane (frag_now);
+ frag_new (0);
+ mips_move_labels ();
}
+ mips16_mark_labels ();
+ mips_clear_insn_labels ();
}
+ mips_opts.noreorder++;
+ mips_any_noreorder = 1;
+}
- /* Mark instruction labels in mips16 mode. */
- if (insns)
- mips16_mark_labels ();
+/* End a nested noreorder block. */
- mips_no_prev_insn (insns);
+static void
+end_noreorder (void)
+{
+ mips_opts.noreorder--;
+ if (mips_opts.noreorder == 0 && prev_nop_frag != NULL)
+ {
+ /* Commit to inserting prev_nop_frag_required nops and go back to
+ handling nop insertion the .set reorder way. */
+ prev_nop_frag->fr_fix -= ((prev_nop_frag_holds - prev_nop_frag_required)
+ * (mips_opts.mips16 ? 2 : 4));
+ insert_into_history (prev_nop_frag_since,
+ prev_nop_frag_required, NOP_INSN);
+ prev_nop_frag = NULL;
+ }
}
/* Set up global variables for the start of a new macro. */
append_insn (&insn, ep, r);
}
+/*
+ * Sign-extend 32-bit mode constants that have bit 31 set and all
+ * higher bits unset.
+ */
+static void
+normalize_constant_expr (expressionS *ex)
+{
+ if (ex->X_op == O_constant
+ && IS_ZEXT_32BIT_NUM (ex->X_add_number))
+ ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
+ - 0x80000000);
+}
+
+/*
+ * Sign-extend 32-bit mode address offsets that have bit 31 set and
+ * all higher bits unset.
+ */
+static void
+normalize_address_expr (expressionS *ex)
+{
+ if (((ex->X_op == O_constant && HAVE_32BIT_ADDRESSES)
+ || (ex->X_op == O_symbol && HAVE_32BIT_SYMBOLS))
+ && IS_ZEXT_32BIT_NUM (ex->X_add_number))
+ ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
+ - 0x80000000);
+}
+
/*
* Generate a "jalr" instruction with a relocation hint to the called
* function. This occurs in NewABI PIC code.
assert (ep->X_op == O_constant);
/* Sign-extending 32-bit constants makes their handling easier. */
- if (! dbl && ! ((ep->X_add_number & ~((bfd_vma) 0x7fffffff))
- == ~((bfd_vma) 0x7fffffff)))
- {
- if (ep->X_add_number & ~((bfd_vma) 0xffffffff))
- as_bad (_("constant too large"));
-
- ep->X_add_number = (((ep->X_add_number & 0xffffffff) ^ 0x80000000)
- - 0x80000000);
- }
+ if (!dbl)
+ normalize_constant_expr (ep);
/* Right now, this routine can only handle signed 32-bit constants. */
if (! IS_SEXT_32BIT_NUM(ep->X_add_number + 0x8000))
}
}
-static void
-normalize_constant_expr (expressionS *ex)
-{
- if (ex->X_op == O_constant && HAVE_32BIT_GPRS)
- ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
- - 0x80000000);
-}
-
/* Warn if an expression is not a constant. */
static void
if (ex->X_op == O_big)
as_bad (_("unsupported large constant"));
else if (ex->X_op != O_constant)
- as_bad (_("Instruction %s requires absolute expression"), ip->insn_mo->name);
+ as_bad (_("Instruction %s requires absolute expression"),
+ ip->insn_mo->name);
- normalize_constant_expr (ex);
+ if (HAVE_32BIT_GPRS)
+ normalize_constant_expr (ex);
}
/* Count the leading zeroes by performing a binary chop. This is a
assert (ep->X_op == O_constant);
/* Sign-extending 32-bit constants makes their handling easier. */
- if (! dbl && ! ((ep->X_add_number & ~((bfd_vma) 0x7fffffff))
- == ~((bfd_vma) 0x7fffffff)))
- {
- if (ep->X_add_number & ~((bfd_vma) 0xffffffff))
- as_bad (_("constant too large"));
-
- ep->X_add_number = (((ep->X_add_number & 0xffffffff) ^ 0x80000000)
- - 0x80000000);
- }
+ if (!dbl)
+ normalize_constant_expr (ep);
if (IS_SEXT_16BIT_NUM (ep->X_add_number))
{
/* The value is larger than 32 bits. */
- if (HAVE_32BIT_GPRS)
+ if (!dbl || HAVE_32BIT_GPRS)
{
- as_bad (_("Number (0x%lx) larger than 32 bits"),
- (unsigned long) ep->X_add_number);
+ char value[32];
+
+ sprintf_vma (value, ep->X_add_number);
+ as_bad (_("Number (0x%s) larger than 32 bits"), value);
macro_build (ep, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
return;
}
sub v0,$zero,$a0
*/
- mips_emit_delays (TRUE);
- ++mips_opts.noreorder;
- mips_any_noreorder = 1;
+ start_noreorder ();
expr1.X_add_number = 8;
macro_build (&expr1, "bgez", "s,p", sreg);
move_register (dreg, sreg);
macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", dreg, 0, sreg);
- --mips_opts.noreorder;
+ end_noreorder ();
break;
case M_ADD_I:
break;
}
- mips_emit_delays (TRUE);
- ++mips_opts.noreorder;
- mips_any_noreorder = 1;
+ start_noreorder ();
if (mips_trap)
{
macro_build (NULL, "teq", "s,t,q", treg, 0, 7);
macro_build (NULL, "teq", "s,t,q", sreg, AT, 6);
/* We want to close the noreorder block as soon as possible, so
that later insns are available for delay slot filling. */
- --mips_opts.noreorder;
+ end_noreorder ();
}
else
{
/* We want to close the noreorder block as soon as possible, so
that later insns are available for delay slot filling. */
- --mips_opts.noreorder;
+ end_noreorder ();
macro_build (NULL, "break", "c", 6);
}
s = "ddivu";
s2 = "mfhi";
do_divu3:
- mips_emit_delays (TRUE);
- ++mips_opts.noreorder;
- mips_any_noreorder = 1;
+ start_noreorder ();
if (mips_trap)
{
macro_build (NULL, "teq", "s,t,q", treg, 0, 7);
macro_build (NULL, s, "z,s,t", sreg, treg);
/* We want to close the noreorder block as soon as possible, so
that later insns are available for delay slot filling. */
- --mips_opts.noreorder;
+ end_noreorder ();
}
else
{
/* We want to close the noreorder block as soon as possible, so
that later insns are available for delay slot filling. */
- --mips_opts.noreorder;
+ end_noreorder ();
macro_build (NULL, "break", "c", 7);
}
macro_build (NULL, s2, "d", dreg);
offset_expr.X_op = O_constant;
}
+ if (HAVE_32BIT_ADDRESSES
+ && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+ {
+ char value [32];
+
+ sprintf_vma (value, offset_expr.X_add_number);
+ as_bad (_("Number (0x%s) larger than 32 bits"), value);
+ }
+
/* A constant expression in PIC code can be handled just as it
is in non PIC code. */
if (offset_expr.X_op == O_constant)
{
- if (HAVE_32BIT_ADDRESSES
- && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
- as_bad (_("constant too large"));
-
expr1.X_add_number = ((offset_expr.X_add_number + 0x8000)
& ~(bfd_vma) 0xffff);
+ normalize_address_expr (&expr1);
load_register (tempreg, &expr1, HAVE_64BIT_ADDRESSES);
if (breg != 0)
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
offset_expr.X_op = O_constant;
}
+ if (HAVE_32BIT_ADDRESSES
+ && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+ {
+ char value [32];
+
+ sprintf_vma (value, offset_expr.X_add_number);
+ as_bad (_("Number (0x%s) larger than 32 bits"), value);
+ }
+
/* Even on a big endian machine $fn comes before $fn+1. We have
to adjust when loading from memory. We set coproc if we must
load $fn+1 first. */
If there is a base register, we add it to $at after the
lui instruction. If there is a constant, we always use
the last case. */
- if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+ if (offset_expr.X_op == O_symbol
+ && (valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
&& !nopic_need_relax (offset_expr.X_add_symbol, 1))
{
relax_start (offset_expr.X_add_symbol);
dbl = 1;
case M_MULO:
do_mulo:
- mips_emit_delays (TRUE);
- ++mips_opts.noreorder;
- mips_any_noreorder = 1;
+ start_noreorder ();
used_at = 1;
if (imm)
load_register (AT, &imm_expr, dbl);
macro_build (NULL, "nop", "", 0);
macro_build (NULL, "break", "c", 6);
}
- --mips_opts.noreorder;
+ end_noreorder ();
macro_build (NULL, "mflo", "d", dreg);
break;
dbl = 1;
case M_MULOU:
do_mulou:
- mips_emit_delays (TRUE);
- ++mips_opts.noreorder;
- mips_any_noreorder = 1;
+ start_noreorder ();
used_at = 1;
if (imm)
load_register (AT, &imm_expr, dbl);
macro_build (NULL, "nop", "", 0);
macro_build (NULL, "break", "c", 6);
}
- --mips_opts.noreorder;
+ end_noreorder ();
break;
case M_DROL:
* Is the double cfc1 instruction a bug in the mips assembler;
* or is there a reason for it?
*/
- mips_emit_delays (TRUE);
- ++mips_opts.noreorder;
- mips_any_noreorder = 1;
+ start_noreorder ();
macro_build (NULL, "cfc1", "t,G", treg, RA);
macro_build (NULL, "cfc1", "t,G", treg, RA);
macro_build (NULL, "nop", "");
dreg, sreg);
macro_build (NULL, "ctc1", "t,G", treg, RA);
macro_build (NULL, "nop", "");
- --mips_opts.noreorder;
+ end_noreorder ();
break;
case M_ULH:
case M_REM_3:
s = "mfhi";
do_div3:
- mips_emit_delays (TRUE);
- ++mips_opts.noreorder;
- mips_any_noreorder = 1;
+ start_noreorder ();
macro_build (NULL, dbl ? "ddiv" : "div", "0,x,y", xreg, yreg);
expr1.X_add_number = 2;
macro_build (&expr1, "bnez", "x,p", yreg);
since that causes an overflow. We should do that as well,
but I don't see how to do the comparisons without a temporary
register. */
- --mips_opts.noreorder;
+ end_noreorder ();
macro_build (NULL, s, "x", zreg);
break;
s = "ddivu";
s2 = "mfhi";
do_divu3:
- mips_emit_delays (TRUE);
- ++mips_opts.noreorder;
- mips_any_noreorder = 1;
+ start_noreorder ();
macro_build (NULL, s, "0,x,y", xreg, yreg);
expr1.X_add_number = 2;
macro_build (&expr1, "bnez", "x,p", yreg);
macro_build (NULL, "break", "6", 7);
- --mips_opts.noreorder;
+ end_noreorder ();
macro_build (NULL, s2, "x", zreg);
break;
if (imm2_expr.X_op != O_big
&& imm2_expr.X_op != O_constant)
insn_error = _("absolute expression required");
- normalize_constant_expr (&imm2_expr);
+ if (HAVE_32BIT_GPRS)
+ normalize_constant_expr (&imm2_expr);
s = expr_end;
continue;
if (imm_expr.X_op != O_big
&& imm_expr.X_op != O_constant)
insn_error = _("absolute expression required");
- normalize_constant_expr (&imm_expr);
+ if (HAVE_32BIT_GPRS)
+ normalize_constant_expr (&imm_expr);
s = expr_end;
continue;
case 'A':
my_getExpression (&offset_expr, s);
+ normalize_address_expr (&offset_expr);
*imm_reloc = BFD_RELOC_32;
s = expr_end;
continue;
#define OPTION_NO_FIX_VR4120 (OPTION_FIX_BASE + 3)
{"mfix-vr4120", no_argument, NULL, OPTION_FIX_VR4120},
{"mno-fix-vr4120", no_argument, NULL, OPTION_NO_FIX_VR4120},
+#define OPTION_FIX_VR4130 (OPTION_FIX_BASE + 4)
+#define OPTION_NO_FIX_VR4130 (OPTION_FIX_BASE + 5)
+ {"mfix-vr4130", no_argument, NULL, OPTION_FIX_VR4130},
+ {"mno-fix-vr4130", no_argument, NULL, OPTION_NO_FIX_VR4130},
/* Miscellaneous options. */
-#define OPTION_MISC_BASE (OPTION_FIX_BASE + 4)
+#define OPTION_MISC_BASE (OPTION_FIX_BASE + 6)
#define OPTION_TRAP (OPTION_MISC_BASE + 0)
{"trap", no_argument, NULL, OPTION_TRAP},
{"no-break", no_argument, NULL, OPTION_TRAP},
case OPTION_MIPS16:
mips_opts.mips16 = 1;
- mips_no_prev_insn (FALSE);
+ mips_no_prev_insn ();
break;
case OPTION_NO_MIPS16:
mips_opts.mips16 = 0;
- mips_no_prev_insn (FALSE);
+ mips_no_prev_insn ();
break;
case OPTION_MIPS3D:
mips_fix_vr4120 = 0;
break;
+ case OPTION_FIX_VR4130:
+ mips_fix_vr4130 = 1;
+ break;
+
+ case OPTION_NO_FIX_VR4130:
+ mips_fix_vr4130 = 0;
+ break;
+
case OPTION_RELAX_BRANCH:
mips_relax_branch = 1;
break;
}
/* Sort any unmatched HI16 and GOT16 relocs so that they immediately precede
- the corresponding LO16 reloc. This is called before md_apply_fix3 and
+ the corresponding LO16 reloc. This is called before md_apply_fix and
tc_gen_reloc. Unmatched relocs can only be generated by use of explicit
relocation operators.
if (*pos == l->fixp)
hi_pos = pos;
- if ((*pos)->fx_r_type == BFD_RELOC_LO16
+ if (((*pos)->fx_r_type == BFD_RELOC_LO16
+ || (*pos)->fx_r_type == BFD_RELOC_MIPS16_LO16)
&& (*pos)->fx_addsy == l->fixp->fx_addsy
&& (*pos)->fx_offset >= l->fixp->fx_offset
&& (lo_pos == NULL
/* Apply a fixup to the object file. */
void
-md_apply_fix3 (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
{
bfd_byte *buf;
long insn;
hiv = 0xffffffff;
else
hiv = 0;
- md_number_to_chars ((char *)(buf + target_big_endian ? 4 : 0),
+ md_number_to_chars ((char *)(buf + (target_big_endian ? 4 : 0)),
*valP, 4);
- md_number_to_chars ((char *)(buf + target_big_endian ? 0 : 4),
+ md_number_to_chars ((char *)(buf + (target_big_endian ? 0 : 4)),
hiv, 4);
}
}
static void
mips_align (int to, int fill, symbolS *label)
{
- mips_emit_delays (FALSE);
+ mips_emit_delays ();
frag_align (to, fill, 0);
record_alignment (now_seg, to);
if (label != NULL)
demand_empty_rest_of_line ();
}
-void
-mips_flush_pending_output (void)
-{
- mips_emit_delays (FALSE);
- mips_clear_insn_labels ();
-}
-
static void
s_change_sec (int sec)
{
obj_elf_section_change_hook ();
#endif
- mips_emit_delays (FALSE);
+ mips_emit_delays ();
switch (sec)
{
case 't':
symbolS *label;
label = insn_labels != NULL ? insn_labels->label : NULL;
- mips_emit_delays (FALSE);
+ mips_emit_delays ();
if (log_size > 0 && auto_align)
mips_align (log_size, 0, label);
mips_clear_insn_labels ();
label = insn_labels != NULL ? insn_labels->label : NULL;
- mips_emit_delays (FALSE);
+ mips_emit_delays ();
if (auto_align)
{
if (strcmp (name, "reorder") == 0)
{
- if (mips_opts.noreorder && prev_nop_frag != NULL)
- {
- /* If we still have pending nops, we can discard them. The
- usual nop handling will insert any that are still
- needed. */
- prev_nop_frag->fr_fix -= (prev_nop_frag_holds
- * (mips_opts.mips16 ? 2 : 4));
- prev_nop_frag = NULL;
- }
- mips_opts.noreorder = 0;
+ if (mips_opts.noreorder)
+ end_noreorder ();
}
else if (strcmp (name, "noreorder") == 0)
{
- mips_emit_delays (TRUE);
- mips_opts.noreorder = 1;
- mips_any_noreorder = 1;
+ if (!mips_opts.noreorder)
+ start_noreorder ();
}
else if (strcmp (name, "at") == 0)
{
/* If we're changing the reorder mode we need to handle
delay slots correctly. */
if (s->options.noreorder && ! mips_opts.noreorder)
- mips_emit_delays (TRUE);
+ start_noreorder ();
else if (! s->options.noreorder && mips_opts.noreorder)
- {
- if (prev_nop_frag != NULL)
- {
- prev_nop_frag->fr_fix -= (prev_nop_frag_holds
- * (mips_opts.mips16 ? 2 : 4));
- prev_nop_frag = NULL;
- }
- }
+ end_noreorder ();
mips_opts = s->options;
mips_opts_stack = s->next;
expressionS ex;
ex.X_op = O_symbol;
- ex.X_add_symbol = symbol_find_or_make ("_gp");
+ ex.X_add_symbol = symbol_find_or_make ("__gnu_local_gp");
ex.X_op_symbol = NULL;
ex.X_add_number = 0;
}
label = insn_labels != NULL ? insn_labels->label : NULL;
- mips_emit_delays (TRUE);
+ mips_emit_delays ();
if (auto_align)
mips_align (2, 0, label);
mips_clear_insn_labels ();
}
label = insn_labels != NULL ? insn_labels->label : NULL;
- mips_emit_delays (TRUE);
+ mips_emit_delays ();
if (auto_align)
mips_align (3, 0, label);
mips_clear_insn_labels ();
placed anywhere. Rather than break backwards compatibility by changing
this, it seems better not to force the issue, and instead keep the
original symbol. This will work with either linker behavior. */
- if ((fixp->fx_r_type == BFD_RELOC_LO16 || reloc_needs_lo_p (fixp->fx_r_type))
+ if ((fixp->fx_r_type == BFD_RELOC_LO16
+ || fixp->fx_r_type == BFD_RELOC_MIPS16_LO16
+ || reloc_needs_lo_p (fixp->fx_r_type))
&& HAVE_IN_PLACE_ADDENDS
&& (S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_MERGE) != 0)
return 0;
{ "4km", 0, ISA_MIPS32, CPU_MIPS32 },
{ "4kp", 0, ISA_MIPS32, CPU_MIPS32 },
+ /* MIPS32 Release 2 */
+ { "m4k", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24k", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kc", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kf", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kx", 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+
/* MIPS 64 */
{ "5kc", 0, ISA_MIPS64, CPU_MIPS64 },
{ "20kc", 0, ISA_MIPS64, CPU_MIPS64 },
-no-mips16 do not generate mips16 instructions\n"));
fprintf (stream, _("\
-mfix-vr4120 work around certain VR4120 errata\n\
+-mfix-vr4130 work around VR4130 mflo/mfhi errata\n\
-mgp32 use 32-bit GPRs, regardless of the chosen ISA\n\
-mfp32 use 32-bit FPRs, regardless of the chosen ISA\n\
-mno-shared optimize output for executables\n\
else
return 4;
}
+
+/* Standard calling conventions leave the CFA at SP on entry. */
+void
+mips_cfi_frame_initial_instructions (void)
+{
+ cfi_add_CFA_def_cfa_register (SP);
+}
+