noreorder. */
static int prev_prev_insn_unreordered;
+/* If this is set, it points to a frag holding nop instructions which
+ were inserted before the start of a noreorder section. If those
+ nops turn out to be unnecessary, the size of the frag can be
+ decreased. */
+static fragS *prev_nop_frag;
+
+/* The number of nop instructions we created in prev_nop_frag. */
+static int prev_nop_frag_holds;
+
+/* The number of nop instructions that we know we need in
+ prev_nop_frag. */
+static int prev_nop_frag_required;
+
+/* The number of instructions we've seen since prev_nop_frag. */
+static int prev_nop_frag_since;
+
/* For ECOFF and ELF, relocations against symbols are done in two
parts, with a HI relocation and a LO relocation. Each relocation
has only 16 bits of space to store an addend. This means that in
expressionS * p,
bfd_reloc_code_real_type r,
boolean));
-static void mips_no_prev_insn PARAMS ((void));
+static void mips_no_prev_insn PARAMS ((int));
static void mips_emit_delays PARAMS ((boolean));
#ifdef USE_STDARG
static void macro_build PARAMS ((char *place, int *counter, expressionS * ep,
symbol_table_insert (symbol_new ("$pc", reg_section, -1,
&zero_address_frag));
- mips_no_prev_insn ();
+ mips_no_prev_insn (false);
mips_gprmask = 0;
mips_cprmask[0] = 0;
prev_pinfo = prev_insn.insn_mo->pinfo;
pinfo = ip->insn_mo->pinfo;
- if (place == NULL && ! mips_noreorder)
+ if (place == NULL && (! mips_noreorder || prev_nop_frag != NULL))
{
+ int prev_prev_nop;
+
/* If the previous insn required any delay slots, see if we need
to insert a NOP or two. There are eight kinds of possible
hazards, of which an instruction can have at most one type.
nops += 2;
}
+ /* If the previous instruction was in a noreorder section, then
+ we don't want to insert the nop after all. */
+ if (prev_insn_unreordered)
+ nops = 0;
+
/* There are two cases which require two intervening
instructions: 1) setting the condition codes using a move to
coprocessor instruction which requires a general coprocessor
which have interlocks). If we are not already emitting a NOP
instruction, we must check for these cases compared to the
instruction previous to the previous instruction. */
- if (nops == 0
- && ((! mips16
- && mips_isa < 4
- && (prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY)
- && (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
- && (pinfo & INSN_READ_COND_CODE)
- && ! cop_interlocks)
- || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)
- && (pinfo & INSN_WRITE_LO)
- && ! interlocks)
- || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
- && (pinfo & INSN_WRITE_HI)
- && ! interlocks)))
+ if ((! mips16
+ && mips_isa < 4
+ && (prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY)
+ && (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
+ && (pinfo & INSN_READ_COND_CODE)
+ && ! cop_interlocks)
+ || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)
+ && (pinfo & INSN_WRITE_LO)
+ && ! interlocks)
+ || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
+ && (pinfo & INSN_WRITE_HI)
+ && ! interlocks))
+ prev_prev_nop = 1;
+ else
+ prev_prev_nop = 0;
+
+ if (prev_prev_insn_unreordered)
+ prev_prev_nop = 0;
+
+ if (prev_prev_nop && nops == 0)
++nops;
/* If we are being given a nop instruction, don't bother with
one of the nops we would otherwise output. This will only
happen when a nop instruction is used with mips_optimize set
to 0. */
- if (nops > 0 && ip->insn_opcode == (mips16 ? 0x6500 : 0))
+ if (nops > 0
+ && ! mips_noreorder
+ && ip->insn_opcode == (mips16 ? 0x6500 : 0))
--nops;
/* Now emit the right number of NOP instructions. */
- if (nops > 0)
+ if (nops > 0 && ! mips_noreorder)
{
fragS *old_frag;
unsigned long old_frag_offset;
ecoff_fix_loc (old_frag, old_frag_offset);
#endif
}
+ else if (prev_nop_frag != NULL)
+ {
+ /* We have a frag holding nops we may be able to remove. If
+ we don't need any nops, we can decrease the size of
+ prev_nop_frag by the size of one instruction. If we do
+ need some nops, we count them in prev_nops_required. */
+ if (prev_nop_frag_since == 0)
+ {
+ if (nops == 0)
+ {
+ prev_nop_frag->fr_fix -= mips16 ? 2 : 4;
+ --prev_nop_frag_holds;
+ }
+ else
+ prev_nop_frag_required += nops;
+ }
+ else
+ {
+ if (prev_prev_nop == 0)
+ {
+ prev_nop_frag->fr_fix -= mips16 ? 2 : 4;
+ --prev_nop_frag_holds;
+ }
+ else
+ ++prev_nop_frag_required;
+ }
+
+ if (prev_nop_frag_holds <= prev_nop_frag_required)
+ prev_nop_frag = NULL;
+
+ ++prev_nop_frag_since;
+
+ /* Sanity check: by the time we reach the second instruction
+ after prev_nop_frag, we should have used up all the nops
+ one way or another. */
+ assert (prev_nop_frag_since <= 1 || prev_nop_frag == NULL);
+ }
}
-
+
if (reloc_type > BFD_RELOC_UNUSED)
{
/* We need to set up a variant frag. */
f = frag_more (2);
}
else
- f = frag_more (4);
+ {
+ if (mips16
+ && mips_noreorder
+ && (prev_pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
+ as_warn ("extended instruction in delay slot");
+
+ f = frag_more (4);
+ }
+
fixp = NULL;
if (address_expr != NULL && reloc_type < BFD_RELOC_UNUSED)
{
fixp->fx_where = prev_insn_where;
}
}
- else if (reloc_type > BFD_RELOC_UNUSED)
- {
- char *prev_f;
- char temp[2];
-
- /* We are in mips16 mode, and we have just created a
- variant frag. We need to extract the old
- instruction from the end of the previous frag,
- and add it to a new frag. */
- prev_f = prev_insn_frag->fr_literal + prev_insn_where;
- memcpy (temp, prev_f, 2);
- prev_insn_frag->fr_fix -= 2;
- if (prev_insn_frag->fr_type == rs_machine_dependent)
- {
- assert (prev_insn_where == prev_insn_frag->fr_fix);
- memcpy (prev_f, prev_f + 2, 2);
- }
- memcpy (frag_more (2), temp, 2);
- }
else
{
char *prev_f;
memcpy (temp, prev_f, 2);
memcpy (prev_f, f, 2);
if (reloc_type != BFD_RELOC_MIPS16_JMP)
- memcpy (f, temp, 2);
+ {
+ assert (reloc_type == BFD_RELOC_UNUSED);
+ memcpy (f, temp, 2);
+ }
else
{
memcpy (f, f + 2, 2);
/* We need to record a bit of information even when we are not
reordering, in order to determine the base address for mips16
PC relative relocs. */
+ prev_prev_insn = prev_insn;
prev_insn = *ip;
prev_insn_reloc_type = reloc_type;
+ prev_prev_insn_unreordered = prev_insn_unreordered;
+ prev_insn_unreordered = 1;
}
/* We just output an insn, so the next one doesn't have a label. */
}
/* This function forgets that there was any previous instruction or
- label. */
+ label. If PRESERVE is non-zero, it remembers enough information to
+ know whether nops are needed before a noreorder section. */
static void
-mips_no_prev_insn ()
+mips_no_prev_insn (preserve)
+ int preserve;
{
- prev_insn.insn_mo = &dummy_opcode;
- prev_prev_insn.insn_mo = &dummy_opcode;
+ if (! preserve)
+ {
+ prev_insn.insn_mo = &dummy_opcode;
+ prev_prev_insn.insn_mo = &dummy_opcode;
+ prev_nop_frag = NULL;
+ prev_nop_frag_holds = 0;
+ prev_nop_frag_required = 0;
+ prev_nop_frag_since = 0;
+ }
prev_insn_valid = 0;
prev_insn_is_delay_slot = 0;
prev_insn_unreordered = 0;
{
if (! mips_noreorder)
{
- int nop;
+ int nops;
- nop = 0;
+ nops = 0;
if ((! mips16
&& mips_isa < 4
&& (! cop_interlocks
& (INSN_LOAD_MEMORY_DELAY
| INSN_COPROC_MEMORY_DELAY))))
{
- nop = 1;
+ ++nops;
if ((! mips16
&& mips_isa < 4
&& (! cop_interlocks
|| (! interlocks
&& ((prev_insn.insn_mo->pinfo & INSN_READ_HI)
|| (prev_insn.insn_mo->pinfo & INSN_READ_LO))))
- emit_nop ();
+ ++nops;
+
+ if (prev_insn_unreordered)
+ nops = 0;
}
else if ((! mips16
&& mips_isa < 4
|| (! interlocks
&& ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
|| (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO))))
- nop = 1;
- if (nop)
+ {
+ if (! prev_prev_insn_unreordered)
+ ++nops;
+ }
+
+ if (nops > 0)
{
struct insn_label_list *l;
- emit_nop ();
+ if (insns)
+ {
+ /* Record the frag which holds the nop instructions, so
+ that we can remove them if we don't need them. */
+ frag_grow (mips16 ? nops * 2 : nops * 4);
+ prev_nop_frag = frag_now;
+ prev_nop_frag_holds = nops;
+ prev_nop_frag_required = 0;
+ prev_nop_frag_since = 0;
+ }
+
+ for (; nops > 0; --nops)
+ emit_nop ();
+
+ 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)
{
assert (S_GET_SEGMENT (l->label) == now_seg);
}
}
- mips_no_prev_insn ();
+ mips_no_prev_insn (insns);
}
/* Build an instruction created by a macro expansion. This is passed
}
continue;
+ case 'e': /* extend code */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number > 0x7ff)
+ {
+ as_warn ("Invalid value for `%s' (%lu)",
+ ip->insn_mo->name,
+ (unsigned long) imm_expr.X_add_number);
+ imm_expr.X_add_number &= 0x7ff;
+ }
+ ip->insn_opcode |= imm_expr.X_add_number;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
default:
internalError ();
}
expression (ep);
expr_end = input_line_pointer;
input_line_pointer = save_in;
+
+ /* If we are in mips16 mode, and this is an expression based on `.',
+ then we bump the value of the symbol by 1 since that is how other
+ text symbols are handled. We don't bother to handle complex
+ expressions, just `.' plus or minus a constant. */
+ if (mips16
+ && ep->X_op == O_symbol
+ && strcmp (S_GET_NAME (ep->X_add_symbol), FAKE_LABEL_NAME) == 0
+ && S_GET_SEGMENT (ep->X_add_symbol) == now_seg
+ && ep->X_add_symbol->sy_frag == frag_now
+ && ep->X_add_symbol->sy_value.X_op == O_constant
+ && ep->X_add_symbol->sy_value.X_add_number == frag_now_fix ())
+ ++ep->X_add_symbol->sy_value.X_add_number;
}
/* Turn a string in input_line_pointer into a floating point constant
optimizations which limit full symbolic debugging. We take
that to be equivalent to -O0. */
if (mips_debug == 2)
- mips_optimize = 0;
+ mips_optimize = 1;
break;
case OPTION_MIPS1:
case OPTION_MIPS16:
mips16 = 1;
- mips_no_prev_insn ();
+ mips_no_prev_insn (false);
break;
case OPTION_NO_MIPS16:
mips16 = 0;
- mips_no_prev_insn ();
+ mips_no_prev_insn (false);
break;
case OPTION_MEMBEDDED_PIC:
if (strcmp (name, "reorder") == 0)
{
- if (mips_noreorder)
+ if (mips_noreorder && prev_nop_frag != NULL)
{
- prev_insn_unreordered = 1;
- prev_prev_insn_unreordered = 1;
+ /* 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 * (mips16 ? 2 : 4);
+ prev_nop_frag = NULL;
}
mips_noreorder = 0;
}
/* The base address rules are complicated. The base address of
a branch is the following instruction. The base address of a
PC relative load or add is the instruction itself, but if it
- is extended add 2, and if it is in a delay slot (in which
- case it can not be extended) use the address of the
- instruction whose delay slot it is in. */
+ is in a delay slot (in which case it can not be extended) use
+ the address of the instruction whose delay slot it is in. */
if (type == 'p' || type == 'q')
{
addr += 2;
+
+ /* If we are currently assuming that this frag should be
+ extended, then, the current address is two bytes
+ higher. */
+ if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
+ addr += 2;
+
/* Ignore the low bit in the target, since it will be set
for a text label. */
if ((val & 1) != 0)
else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype))
addr -= 2;
- /* If we are currently assuming that this frag should be
- extended, then the current address is two bytes higher. */
- if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
- addr += 2;
-
val -= addr & ~ ((1 << op->shift) - 1);
/* Branch offsets have an implicit 0 in the lowest bit. */
if (type == 'p' || type == 'q')
{
addr += 2;
+ if (ext)
+ addr += 2;
/* Ignore the low bit in the target, since it will be
set for a text label. */
if ((val & 1) != 0)
else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype))
addr -= 2;
- if (ext)
- addr += 2;
addr &= ~ (addressT) ((1 << op->shift) - 1);
val -= addr;
record_alignment (asec, op->shift);
}
+ if (ext
+ && (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype)
+ || RELAX_MIPS16_DSLOT (fragp->fr_subtype)))
+ as_warn_where (fragp->fr_file, fragp->fr_line,
+ "extended instruction in delay slot");
+
buf = (bfd_byte *) (fragp->fr_literal + fragp->fr_fix);
if (target_big_endian)