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;
/* 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)
{
- if (insns && mips_optimize != 0)
+ 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;
+
+ /* 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);
- }
-
+ /* 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. */
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);
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;
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:
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;
}
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 ();