* config/tc-hppa.h (tc_frob_label): Define.
* config/tc-mips.c: Many changes to support simple assembler optimization. (insn_label, prev_insn, prev_prev_insn, dummy_opcode, prev_insn_valid, prev_insn_frag, prev_insn_where, prev_insn_fixp, prev_insn_is_delay_slot): New static variables. (insn_uses_reg, mips_no_prev_insn, mips_emit_delays, mips_align, s_stringer, s_mips_space): New static functions. (mips_define_label): New global function. (md_pseudo_table): For "ascii", "asciz", "asciiz", call s_stringer. Changed argument to float_cons from 0 or 1 to 'f' or 'd'. For "space" call s_mips_space. (md_begin): Call mips_no_prev_insn. (append_insn): Only insert necessary NOP instructions. (macro): Call mips_emit_delays before setting mips_noreorder. Increment and decrement mips_noreorder rather than using save_reorder_condition. Don't bother to use noreorder in M_L_DOB and M_L_DAB, since append_insn will not insert a NOP. (md_atof): Handle floating point numbers correctly for both big and little endian targets. (s_align, s_cons): Call mips_align rather than frag_align. (s_change_seg, s_cons): Call mips_emit_delays. (s_float_cons): Let float_cons do the work. (s_mipsset): Call mips_emit_delays when setting noreorder. * config/tc-mips.h (tc_frob_label): Define to be mips_define_label.
This commit is contained in:
parent
1e9cf56586
commit
becfe05e61
2 changed files with 561 additions and 124 deletions
|
@ -468,6 +468,8 @@ extern struct default_space_dict pa_def_spaces[];
|
|||
space.
|
||||
*/
|
||||
|
||||
#define tc_frob_label(sym) pa_define_label (sym)
|
||||
|
||||
typedef struct label_symbol_struct
|
||||
{
|
||||
symbolS *lss_label; /* the label symbol */
|
||||
|
|
|
@ -95,6 +95,37 @@ static char *insn_error;
|
|||
static int byte_order = BYTE_ORDER;
|
||||
|
||||
static int auto_align = 1;
|
||||
|
||||
/* Symbol labelling the current insn. */
|
||||
static symbolS *insn_label;
|
||||
|
||||
/* To output NOP instructions correctly, we need to keep information
|
||||
about the previous two instructions. */
|
||||
|
||||
/* The previous instruction. */
|
||||
static struct mips_cl_insn prev_insn;
|
||||
|
||||
/* The instruction before prev_insn. */
|
||||
static struct mips_cl_insn prev_prev_insn;
|
||||
|
||||
/* If we don't want information for prev_insn or prev_prev_insn, we
|
||||
point the insn_mo field at this dummy integer. */
|
||||
static const struct mips_opcode dummy_opcode = { 0 };
|
||||
|
||||
/* Non-zero if prev_insn is valid. */
|
||||
static int prev_insn_valid;
|
||||
|
||||
/* The frag for the previous instruction. */
|
||||
static struct frag *prev_insn_frag;
|
||||
|
||||
/* The offset into prev_insn_frag for the previous instruction. */
|
||||
static long prev_insn_where;
|
||||
|
||||
/* The reloc for the previous instruction, if any. */
|
||||
static fixS *prev_insn_fixp;
|
||||
|
||||
/* Non-zero if the previous instruction was in a delay slot. */
|
||||
static int prev_insn_is_delay_slot;
|
||||
|
||||
/* Prototypes for static functions. */
|
||||
|
||||
|
@ -105,9 +136,13 @@ static int auto_align = 1;
|
|||
#define internalError() as_fatal ("MIPS internal Error");
|
||||
#endif
|
||||
|
||||
static int insn_uses_reg PARAMS ((struct mips_cl_insn *ip,
|
||||
int reg, int fpr));
|
||||
static void append_insn PARAMS ((struct mips_cl_insn * ip,
|
||||
expressionS * p,
|
||||
bfd_reloc_code_real_type r));
|
||||
static void mips_no_prev_insn PARAMS ((void));
|
||||
static void mips_emit_delays PARAMS ((void));
|
||||
static int gp_reference PARAMS ((expressionS * ep));
|
||||
static void macro_build PARAMS ((int *counter, expressionS * ep,
|
||||
const char *name, const char *fmt,
|
||||
|
@ -127,7 +162,9 @@ static int my_getSmallExpression PARAMS ((expressionS * ep, char *str));
|
|||
static void my_getExpression PARAMS ((expressionS * ep, char *str));
|
||||
static symbolS *get_symbol PARAMS ((void));
|
||||
static long get_optional_absolute_expression PARAMS ((void));
|
||||
static void mips_align PARAMS ((int to, int fill));
|
||||
static void s_align PARAMS ((int));
|
||||
static void s_stringer PARAMS ((int));
|
||||
static void s_change_sec PARAMS ((int));
|
||||
static void s_cons PARAMS ((int));
|
||||
static void s_err PARAMS ((int));
|
||||
|
@ -135,6 +172,7 @@ static void s_extern PARAMS ((int));
|
|||
static void s_float_cons PARAMS ((int));
|
||||
static void s_option PARAMS ((int));
|
||||
static void s_mipsset PARAMS ((int));
|
||||
static void s_mips_space PARAMS ((int));
|
||||
#ifndef OBJ_ECOFF
|
||||
static void md_obj_begin PARAMS ((void));
|
||||
static void md_obj_end PARAMS ((void));
|
||||
|
@ -175,7 +213,7 @@ const pseudo_typeS md_pseudo_table[] =
|
|||
|
||||
/* Relatively generic pseudo-ops that happen to be used on MIPS
|
||||
chips. */
|
||||
{"asciiz", stringer, 1},
|
||||
{"asciiz", s_stringer, 1},
|
||||
{"bss", s_change_sec, 'b'},
|
||||
{"err", s_err, 0},
|
||||
{"half", s_cons, 1},
|
||||
|
@ -183,11 +221,14 @@ const pseudo_typeS md_pseudo_table[] =
|
|||
/* These pseudo-ops are defined in read.c, but must be overridden
|
||||
here for one reason or another. */
|
||||
{"align", s_align, 0},
|
||||
{"ascii", s_stringer, 0},
|
||||
{"asciz", s_stringer, 1},
|
||||
{"byte", s_cons, 0},
|
||||
{"data", s_change_sec, 'd'},
|
||||
{"double", s_float_cons, 1},
|
||||
{"double", s_float_cons, 'd'},
|
||||
{"extern", s_extern, 0},
|
||||
{"float", s_float_cons, 0},
|
||||
{"float", s_float_cons, 'f'},
|
||||
{"space", s_mips_space, 0},
|
||||
{"text", s_change_sec, 't'},
|
||||
{"word", s_cons, 2},
|
||||
|
||||
|
@ -262,6 +303,8 @@ md_begin ()
|
|||
while ((i < NUMOPCODES) && !strcmp (mips_opcodes[i].name, name));
|
||||
}
|
||||
|
||||
mips_no_prev_insn ();
|
||||
|
||||
#ifndef OBJ_ECOFF
|
||||
md_obj_begin ();
|
||||
#endif
|
||||
|
@ -317,6 +360,48 @@ md_assemble (str)
|
|||
}
|
||||
}
|
||||
|
||||
/* See whether instruction IP reads register REG. If FPR is non-zero,
|
||||
REG is a floating point register. */
|
||||
|
||||
static int
|
||||
insn_uses_reg (ip, reg, fpr)
|
||||
struct mips_cl_insn *ip;
|
||||
int reg;
|
||||
int fpr;
|
||||
{
|
||||
/* Don't report on general register 0, since it never changes. */
|
||||
if (! fpr && reg == 0)
|
||||
return 0;
|
||||
|
||||
if (fpr)
|
||||
{
|
||||
/* If we are called with either $f0 or $f1, we must check $f0.
|
||||
This is not optimal, because it will introduce an unnecessary
|
||||
NOP between "lwc1 $f0" and "swc1 $f1". To fix this we would
|
||||
need to distinguish reading both $f0 and $f1 or just one of
|
||||
them. Note that we don't have to check the other way,
|
||||
because there is no instruction that sets both $f0 and $f1
|
||||
and requires a delay. */
|
||||
if ((ip->insn_mo->pinfo & INSN_READ_FPR_S)
|
||||
&& ((ip->insn_opcode >> OP_SH_FS) & OP_MASK_FS) == (reg &~ 1))
|
||||
return 1;
|
||||
if ((ip->insn_mo->pinfo & INSN_READ_FPR_T)
|
||||
&& ((ip->insn_opcode >> OP_SH_FT) & OP_MASK_FT) == (reg &~ 1))
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((ip->insn_mo->pinfo & INSN_READ_GPR_S)
|
||||
&& ((ip->insn_opcode >> OP_SH_RS) & OP_MASK_RS) == reg)
|
||||
return 1;
|
||||
if ((ip->insn_mo->pinfo & INSN_READ_GPR_T)
|
||||
&& ((ip->insn_opcode >> OP_SH_RT) & OP_MASK_RT) == reg)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ALIGN_ERR "Attempt to assemble instruction onto non word boundary."
|
||||
#define ALIGN_ERR2 "GAS doesn't do implicit alignment; use .align directive."
|
||||
|
||||
|
@ -331,20 +416,161 @@ append_insn (ip, address_expr, reloc_type)
|
|||
bfd_reloc_code_real_type reloc_type;
|
||||
{
|
||||
char *f;
|
||||
fixS *fixp;
|
||||
int nops = 0;
|
||||
|
||||
if (! mips_noreorder)
|
||||
{
|
||||
/* If the previous insn required any delay slots, see if we need
|
||||
to insert a NOP or two. There are six kinds of possible
|
||||
hazards, of which an instruction can have at most one type.
|
||||
(1) a load delay
|
||||
(2) an unconditional branch delay
|
||||
(3) a conditional branch delay
|
||||
(4) a generic coprocessor delay
|
||||
(5) a coprocessor condition code delay
|
||||
(6) a HI/LO special register delay
|
||||
|
||||
There are a lot of optimizations we could do that we don't.
|
||||
In particular, we do not, in general, reorder instructions.
|
||||
If you use gcc with optimization, it will reorder
|
||||
instructions and generally do much more optimization then we
|
||||
do here; repeating all that work in the assembler would only
|
||||
benefit hand written assembly code, and does not seem worth
|
||||
it. */
|
||||
|
||||
/* This is how a NOP is emitted. */
|
||||
#define emit_nop() md_number_to_chars (frag_more (4), 0, 4)
|
||||
|
||||
/* The previous insn might require a delay slot, depending upon
|
||||
the contents of the current insn. */
|
||||
if (prev_insn.insn_mo->pinfo & INSN_LOAD_DELAY)
|
||||
{
|
||||
/* A load delay. All load delays delay the use of general
|
||||
register rt for one instruction. */
|
||||
know (prev_insn.insn_mo->pinfo & INSN_WRITE_GPR_T);
|
||||
if (insn_uses_reg (ip,
|
||||
(prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT,
|
||||
0))
|
||||
++nops;
|
||||
}
|
||||
else if (prev_insn.insn_mo->pinfo & INSN_COPROC_DELAY)
|
||||
{
|
||||
/* A generic coprocessor delay. The previous instruction
|
||||
modified a coprocessor general or control register. If
|
||||
it modified a control register, we need to avoid any
|
||||
coprocessor instruction (this is probably not always
|
||||
required, but it sometimes is). If it modified a general
|
||||
register, we avoid using that register.
|
||||
|
||||
This case is not handled very well. There is no special
|
||||
knowledge of CP0 handling, and the coprocessors other
|
||||
than the floating point unit are not distinguished at
|
||||
all. */
|
||||
if (prev_insn.insn_mo->pinfo & INSN_WRITE_FPR_T)
|
||||
{
|
||||
if (insn_uses_reg (ip,
|
||||
((prev_insn.insn_opcode >> OP_SH_RT)
|
||||
& OP_MASK_RT),
|
||||
1))
|
||||
++nops;
|
||||
}
|
||||
else if (prev_insn.insn_mo->pinfo & INSN_WRITE_FPR_D)
|
||||
{
|
||||
if (insn_uses_reg (ip,
|
||||
((prev_insn.insn_opcode >> OP_SH_RD)
|
||||
& OP_MASK_RD),
|
||||
1))
|
||||
++nops;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We don't know exactly what the previous instruction
|
||||
does. If the current instruction uses a coprocessor
|
||||
register, we must insert a NOP. If previous
|
||||
instruction may set the condition codes, and the
|
||||
current instruction uses them, we must insert two
|
||||
NOPS. */
|
||||
if ((prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
|
||||
&& (ip->insn_mo->pinfo & INSN_READ_COND_CODE))
|
||||
nops += 2;
|
||||
else if (ip->insn_mo->pinfo & INSN_COP)
|
||||
++nops;
|
||||
}
|
||||
}
|
||||
else if (prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
|
||||
{
|
||||
/* The previous instruction sets the coprocessor condition
|
||||
codes, but does not require a general coprocessor delay
|
||||
(this means it is a floating point comparison
|
||||
instruction). If this instruction uses the condition
|
||||
codes, we need to insert a single NOP. */
|
||||
if (ip->insn_mo->pinfo & INSN_READ_COND_CODE)
|
||||
++nops;
|
||||
}
|
||||
else if (prev_insn.insn_mo->pinfo & INSN_READ_LO)
|
||||
{
|
||||
/* The previous instruction reads the LO register; if the
|
||||
current instruction writes to the LO register, we must
|
||||
insert two NOPS. */
|
||||
if (ip->insn_mo->pinfo & INSN_WRITE_LO)
|
||||
nops += 2;
|
||||
}
|
||||
else if (prev_insn.insn_mo->pinfo & INSN_READ_HI)
|
||||
{
|
||||
/* The previous instruction reads the HI register; if the
|
||||
current instruction writes to the HI register, we must
|
||||
insert a NOP. */
|
||||
if (ip->insn_mo->pinfo & INSN_WRITE_HI)
|
||||
nops += 2;
|
||||
}
|
||||
|
||||
/* 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
|
||||
delay and then reading the condition codes 2) reading the HI
|
||||
or LO register and then writing to it. 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
|
||||
&& (((prev_prev_insn.insn_mo->pinfo & INSN_COPROC_DELAY)
|
||||
&& (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
|
||||
&& (ip->insn_mo->pinfo & INSN_READ_COND_CODE))
|
||||
|| ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)
|
||||
&& (ip->insn_mo->pinfo & INSN_WRITE_LO))
|
||||
|| ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
|
||||
&& (ip->insn_mo->pinfo & INSN_WRITE_HI))))
|
||||
++nops;
|
||||
|
||||
/* Now emit the right number of NOP instructions. */
|
||||
if (nops > 0)
|
||||
{
|
||||
emit_nop ();
|
||||
if (nops > 1)
|
||||
emit_nop ();
|
||||
if (insn_label != NULL)
|
||||
{
|
||||
assert (S_GET_SEGMENT (insn_label) == now_seg);
|
||||
insn_label->sy_frag = frag_now;
|
||||
S_SET_VALUE (insn_label, frag_now_fix ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f = frag_more (4);
|
||||
#if 0 /* This is testing the address of the frag, not the alignment of
|
||||
the instruction in the current section. */
|
||||
#if 0
|
||||
/* This is testing the address of the frag, not the alignment of
|
||||
the instruction in the current section. */
|
||||
if ((int) f & 3)
|
||||
{
|
||||
as_bad (ALIGN_ERR);
|
||||
as_bad (ALIGN_ERR2);
|
||||
}
|
||||
#endif
|
||||
fixp = NULL;
|
||||
if (address_expr != NULL)
|
||||
{
|
||||
fixS *fixP;
|
||||
|
||||
if (address_expr->X_seg == &bfd_abs_section)
|
||||
{
|
||||
switch (reloc_type)
|
||||
|
@ -369,7 +595,7 @@ append_insn (ip, address_expr, reloc_type)
|
|||
{
|
||||
assert (reloc_type != BFD_RELOC_UNUSED);
|
||||
need_reloc:
|
||||
fixP = fix_new (frag_now, f - frag_now->fr_literal, 4,
|
||||
fixp = fix_new (frag_now, f - frag_now->fr_literal, 4,
|
||||
address_expr->X_add_symbol,
|
||||
address_expr->X_subtract_symbol,
|
||||
address_expr->X_add_number,
|
||||
|
@ -377,25 +603,203 @@ append_insn (ip, address_expr, reloc_type)
|
|||
reloc_type);
|
||||
}
|
||||
}
|
||||
|
||||
md_number_to_chars (f, ip->insn_opcode, 4);
|
||||
|
||||
/*
|
||||
* Fill all delay slots with nops.
|
||||
*/
|
||||
if (!mips_noreorder)
|
||||
if (! mips_noreorder)
|
||||
{
|
||||
if (ip->insn_mo->pinfo & ANY_DELAY)
|
||||
/* Filling the branch delay slot is more complex. We try to
|
||||
switch the branch with the previous instruction, which we can
|
||||
do if the previous instruction does not set up a condition
|
||||
that the branch tests and if the branch is not itself the
|
||||
target of any branch. */
|
||||
if ((ip->insn_mo->pinfo & INSN_UNCOND_BRANCH_DELAY)
|
||||
|| (ip->insn_mo->pinfo & INSN_COND_BRANCH_DELAY))
|
||||
{
|
||||
f = frag_more (4);
|
||||
md_number_to_chars (f, 0, 4);
|
||||
};
|
||||
/* If we had to emit any NOP instructions, then we already
|
||||
know we can not swap. */
|
||||
if (nops != 0
|
||||
/* If we don't even know the previous insn, we can not
|
||||
swap. */
|
||||
|| ! prev_insn_valid
|
||||
/* If the previous insn is already in a branch delay
|
||||
slot, then we can not swap. */
|
||||
|| prev_insn_is_delay_slot
|
||||
/* If the branch is itself the target of a branch, we
|
||||
can not swap. We cheat on this; all we check for is
|
||||
whether there is a label on this instruction. If
|
||||
there are any branches to anything other than a
|
||||
label, users must use .set noreorder. */
|
||||
|| insn_label != NULL
|
||||
/* 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. */
|
||||
|| (ip->insn_mo->pinfo & INSN_READ_COND_CODE)
|
||||
/* We can not swap with an instruction that requires a
|
||||
delay slot, becase the target of the branch might
|
||||
interfere with that instruction. */
|
||||
|| (prev_insn.insn_mo->pinfo
|
||||
& (INSN_LOAD_DELAY
|
||||
| INSN_COPROC_DELAY
|
||||
| INSN_WRITE_COND_CODE
|
||||
| INSN_READ_LO
|
||||
| INSN_READ_HI))
|
||||
/* We can not swap with a branch instruction. */
|
||||
|| (prev_insn.insn_mo->pinfo
|
||||
& (INSN_UNCOND_BRANCH_DELAY | INSN_COND_BRANCH_DELAY))
|
||||
/* If the branch reads a register that the previous
|
||||
instruction sets, we can not swap. */
|
||||
|| ((prev_insn.insn_mo->pinfo & INSN_WRITE_GPR_T)
|
||||
&& insn_uses_reg (ip,
|
||||
((prev_insn.insn_opcode >> OP_SH_RT)
|
||||
& OP_MASK_RT),
|
||||
0))
|
||||
|| ((prev_insn.insn_mo->pinfo & INSN_WRITE_GPR_D)
|
||||
&& insn_uses_reg (ip,
|
||||
((prev_insn.insn_opcode >> OP_SH_RD)
|
||||
& OP_MASK_RD),
|
||||
0))
|
||||
/* If the branch writes a register that the previous
|
||||
instruction reads, we can not swap (we know that
|
||||
branches only write to RD or to $31). */
|
||||
|| ((ip->insn_mo->pinfo & INSN_WRITE_GPR_D)
|
||||
&& insn_uses_reg (&prev_insn,
|
||||
((ip->insn_opcode >> OP_SH_RD)
|
||||
& OP_MASK_RD),
|
||||
0))
|
||||
|| ((ip->insn_mo->pinfo & INSN_WRITE_GPR_31)
|
||||
&& insn_uses_reg (&prev_insn, 31, 0))
|
||||
/* If the previous previous instruction has a load
|
||||
delay, and sets a register that the branch reads, we
|
||||
can not swap. */
|
||||
|| ((prev_prev_insn.insn_mo->pinfo & INSN_LOAD_DELAY)
|
||||
&& insn_uses_reg (ip,
|
||||
((prev_prev_insn.insn_opcode >> OP_SH_RT)
|
||||
& OP_MASK_RT),
|
||||
0)))
|
||||
{
|
||||
/* We could do even better for unconditional branches to
|
||||
portions of this object file; we could pick up the
|
||||
instruction at the destination, put it in the delay
|
||||
slot, and bump the destination address. */
|
||||
emit_nop ();
|
||||
/* Update the previous insn information. */
|
||||
prev_prev_insn = *ip;
|
||||
prev_insn.insn_mo = &dummy_opcode;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *prev_f;
|
||||
char temp[4];
|
||||
|
||||
/* One extra nop */
|
||||
if (ip->insn_mo->pinfo & (INSN_READ_HI | INSN_READ_LO))
|
||||
{
|
||||
f = frag_more (4);
|
||||
md_number_to_chars (f, 0, 4);
|
||||
/* It looks like we can actually do the swap. */
|
||||
prev_f = prev_insn_frag->fr_literal + prev_insn_where;
|
||||
memcpy (temp, prev_f, 4);
|
||||
memcpy (prev_f, f, 4);
|
||||
memcpy (f, temp, 4);
|
||||
if (prev_insn_fixp)
|
||||
{
|
||||
prev_insn_fixp->fx_frag = frag_now;
|
||||
prev_insn_fixp->fx_where = f - frag_now->fr_literal;
|
||||
}
|
||||
if (fixp)
|
||||
{
|
||||
fixp->fx_frag = prev_insn_frag;
|
||||
fixp->fx_where = prev_insn_where;
|
||||
}
|
||||
/* Update the previous insn information; leave prev_insn
|
||||
unchanged. */
|
||||
prev_prev_insn = *ip;
|
||||
}
|
||||
prev_insn_is_delay_slot = 1;
|
||||
|
||||
/* If that was an unconditional branch, forget the previous
|
||||
insn information. */
|
||||
if (ip->insn_mo->pinfo & INSN_UNCOND_BRANCH_DELAY)
|
||||
{
|
||||
prev_prev_insn.insn_mo = &dummy_opcode;
|
||||
prev_insn.insn_mo = &dummy_opcode;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Update the previous insn information. */
|
||||
if (nops > 0)
|
||||
prev_prev_insn.insn_mo = &dummy_opcode;
|
||||
else
|
||||
prev_prev_insn = prev_insn;
|
||||
prev_insn = *ip;
|
||||
|
||||
/* Any time we see a branch, we always fill the delay slot
|
||||
immediately; since this insn is not a branch, we know it
|
||||
is not in a delay slot. */
|
||||
prev_insn_is_delay_slot = 0;
|
||||
}
|
||||
|
||||
prev_insn_frag = frag_now;
|
||||
prev_insn_where = f - frag_now->fr_literal;
|
||||
prev_insn_fixp = fixp;
|
||||
prev_insn_valid = 1;
|
||||
}
|
||||
|
||||
/* We just output an insn, so the next one doesn't have a label. */
|
||||
insn_label = NULL;
|
||||
}
|
||||
|
||||
/* This function forgets that there was any previous instruction or
|
||||
label. */
|
||||
|
||||
static void
|
||||
mips_no_prev_insn ()
|
||||
{
|
||||
prev_insn.insn_mo = &dummy_opcode;
|
||||
prev_prev_insn.insn_mo = &dummy_opcode;
|
||||
prev_insn_valid = 0;
|
||||
prev_insn_is_delay_slot = 0;
|
||||
insn_label = NULL;
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
|
||||
static void
|
||||
mips_emit_delays ()
|
||||
{
|
||||
if (! mips_noreorder)
|
||||
{
|
||||
int nop;
|
||||
|
||||
nop = 0;
|
||||
if (prev_insn.insn_mo->pinfo & ANY_DELAY)
|
||||
{
|
||||
nop = 1;
|
||||
if ((prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
|
||||
|| (prev_insn.insn_mo->pinfo & INSN_READ_HI)
|
||||
|| (prev_insn.insn_mo->pinfo & INSN_READ_LO))
|
||||
emit_nop ();
|
||||
}
|
||||
else if ((prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
|
||||
|| (prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
|
||||
|| (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO))
|
||||
nop = 1;
|
||||
if (nop)
|
||||
{
|
||||
emit_nop ();
|
||||
if (insn_label != NULL)
|
||||
{
|
||||
assert (S_GET_SEGMENT (insn_label) == now_seg);
|
||||
insn_label->sy_frag = frag_now;
|
||||
S_SET_VALUE (insn_label, frag_now_fix ());
|
||||
}
|
||||
}
|
||||
mips_no_prev_insn ();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -769,7 +1173,6 @@ macro (ip)
|
|||
int mask;
|
||||
int icnt = 0;
|
||||
int used_at;
|
||||
int save_reorder_condition;
|
||||
expressionS expr1;
|
||||
const char *s;
|
||||
const char *fmt;
|
||||
|
@ -796,8 +1199,8 @@ macro (ip)
|
|||
<main+12>: nop
|
||||
*/
|
||||
|
||||
save_reorder_condition = mips_noreorder;
|
||||
mips_noreorder = 1;
|
||||
mips_emit_delays ();
|
||||
++mips_noreorder;
|
||||
|
||||
expr1.X_add_number = 8;
|
||||
macro_build (&icnt, &expr1, "bgez", "s,p", sreg);
|
||||
|
@ -805,7 +1208,7 @@ macro (ip)
|
|||
macro_build (&icnt, NULL, mask == M_ABS ? "sub" : "subu", "d,v,t",
|
||||
dreg, 0, sreg);
|
||||
|
||||
mips_noreorder = save_reorder_condition;
|
||||
--mips_noreorder;
|
||||
return;
|
||||
|
||||
case M_ADD_I:
|
||||
|
@ -1110,8 +1513,8 @@ macro (ip)
|
|||
return;
|
||||
}
|
||||
|
||||
save_reorder_condition = mips_noreorder;
|
||||
mips_noreorder = 1;
|
||||
mips_emit_delays ();
|
||||
++mips_noreorder;
|
||||
macro_build (&icnt, NULL, "div", "s,t", sreg, treg);
|
||||
expr1.X_add_number = 8;
|
||||
macro_build (&icnt, &expr1, "bne", "s,t,p", treg, 0);
|
||||
|
@ -1127,7 +1530,7 @@ macro (ip)
|
|||
macro_build (&icnt, &expr1, "bne", "s,t,p", sreg, AT);
|
||||
macro_build (&icnt, NULL, "nop", "", 0);
|
||||
macro_build (&icnt, NULL, "break", "c", 6);
|
||||
mips_noreorder = save_reorder_condition;
|
||||
--mips_noreorder;
|
||||
macro_build (&icnt, NULL, mask == M_DIV_3 ? "mflo" : "mfhi", "d", dreg);
|
||||
/* with reorder on there will be two implicit nop instructions here. */
|
||||
break;
|
||||
|
@ -1166,14 +1569,14 @@ macro (ip)
|
|||
|
||||
case M_DIVU_3:
|
||||
case M_REMU_3:
|
||||
save_reorder_condition = mips_noreorder;
|
||||
mips_noreorder = 1;
|
||||
mips_emit_delays ();
|
||||
++mips_noreorder;
|
||||
macro_build (&icnt, NULL, "divu", "s,t", sreg, treg);
|
||||
expr1.X_add_number = 8;
|
||||
macro_build (&icnt, &expr1, "bne", "s,t,p", treg, 0);
|
||||
macro_build (&icnt, NULL, "nop", "", 0);
|
||||
macro_build (&icnt, NULL, "break", "c", 7);
|
||||
mips_noreorder = save_reorder_condition;
|
||||
--mips_noreorder;
|
||||
macro_build (&icnt, NULL, mask == M_DIVU_3 ? "mflo" : "mfhi", "d", dreg);
|
||||
/* with reorder on there will be two implicit nop instructions here. */
|
||||
return;
|
||||
|
@ -1347,13 +1750,9 @@ macro (ip)
|
|||
case M_L_DOB:
|
||||
/* Even on a big endian machine $fn comes before $fn+1. We have
|
||||
to adjust when loading from memory. */
|
||||
save_reorder_condition = mips_noreorder;
|
||||
mips_noreorder = 1;
|
||||
macro_build (&icnt, &offset_expr, "lwc1", "T,o(b)",
|
||||
byte_order == LITTLE_ENDIAN ? treg : treg + 1,
|
||||
breg);
|
||||
/* unecessary implicit nop */
|
||||
mips_noreorder = save_reorder_condition;
|
||||
offset_expr.X_add_number += 4;
|
||||
macro_build (&icnt, &offset_expr, "lwc1", "T,o(b)",
|
||||
byte_order == LITTLE_ENDIAN ? treg + 1 : treg,
|
||||
|
@ -1391,13 +1790,9 @@ macro (ip)
|
|||
}
|
||||
/* Even on a big endian machine $fn comes before $fn+1. We have
|
||||
to adjust when loading from memory. */
|
||||
save_reorder_condition = mips_noreorder;
|
||||
mips_noreorder = 1;
|
||||
macro_build (&icnt, &offset_expr, "lwc1", "T,o(b)",
|
||||
byte_order == LITTLE_ENDIAN ? treg : treg + 1,
|
||||
tempreg);
|
||||
/* unecessary implicit nop */
|
||||
mips_noreorder = save_reorder_condition;
|
||||
offset_expr.X_add_number += 4;
|
||||
macro_build (&icnt, &offset_expr, "lwc1", "T,o(b)",
|
||||
byte_order == LITTLE_ENDIAN ? treg + 1 : treg,
|
||||
|
@ -1782,8 +2177,8 @@ macro (ip)
|
|||
* Is the double cfc1 instruction a bug in the mips assembler;
|
||||
* or is there a reason for it?
|
||||
*/
|
||||
save_reorder_condition = mips_noreorder;
|
||||
mips_noreorder = 1;
|
||||
mips_emit_delays ();
|
||||
++mips_noreorder;
|
||||
macro_build (&icnt, NULL, "cfc1", "t,G", treg, 31);
|
||||
macro_build (&icnt, NULL, "cfc1", "t,G", treg, 31);
|
||||
macro_build (&icnt, NULL, "nop", "");
|
||||
|
@ -1797,7 +2192,7 @@ macro (ip)
|
|||
mask == M_TRUNCWD ? "cvt.w.d" : "cvt.w.s", "D,S", dreg, sreg);
|
||||
macro_build (&icnt, NULL, "ctc1", "t,G", treg, 31);
|
||||
macro_build (&icnt, NULL, "nop", "");
|
||||
mips_noreorder = save_reorder_condition;
|
||||
--mips_noreorder;
|
||||
break;
|
||||
|
||||
case M_ULH:
|
||||
|
@ -2443,13 +2838,60 @@ my_getExpression (ep, str)
|
|||
input_line_pointer = save_in;
|
||||
}
|
||||
|
||||
/* Turn a string in input_line_pointer into a floating point constant
|
||||
of type type, and store the appropriate bytes in *litP. The number
|
||||
of LITTLENUMS emitted is stored in *sizeP . An error message is
|
||||
returned, or NULL on OK. */
|
||||
|
||||
char *
|
||||
md_atof (type, litP, sizeP)
|
||||
char type;
|
||||
int type;
|
||||
char *litP;
|
||||
int *sizeP;
|
||||
{
|
||||
internalError ();
|
||||
int prec;
|
||||
LITTLENUM_TYPE words[4];
|
||||
char *t;
|
||||
int i;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case 'f':
|
||||
prec = 2;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
prec = 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
*sizeP = 0;
|
||||
return "bad call to md_atof";
|
||||
}
|
||||
|
||||
t = atof_ieee (input_line_pointer, type, words);
|
||||
if (t)
|
||||
input_line_pointer = t;
|
||||
|
||||
*sizeP = prec * 2;
|
||||
|
||||
if (byte_order == LITTLE_ENDIAN)
|
||||
{
|
||||
for (i = prec - 1; i >= 0; i--)
|
||||
{
|
||||
md_number_to_chars (litP, (valueT) words[i], 2);
|
||||
litP += 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < prec; i++)
|
||||
{
|
||||
md_number_to_chars (litP, (valueT) words[i], 2);
|
||||
litP += 2;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2729,6 +3171,28 @@ get_optional_absolute_expression ()
|
|||
return exp.X_add_number;
|
||||
}
|
||||
|
||||
/* Align the current frag to a given power of two. The MIPS assembler
|
||||
also automatically adjusts any preceding label. */
|
||||
|
||||
static void
|
||||
mips_align (to, fill)
|
||||
int to;
|
||||
int fill;
|
||||
{
|
||||
mips_emit_delays ();
|
||||
frag_align (to, fill);
|
||||
record_alignment (now_seg, to);
|
||||
if (insn_label != NULL)
|
||||
{
|
||||
assert (S_GET_SEGMENT (insn_label) == now_seg);
|
||||
insn_label->sy_frag = frag_now;
|
||||
S_SET_VALUE (insn_label, frag_now_fix ());
|
||||
}
|
||||
}
|
||||
|
||||
/* Align to a given power of two. .align 0 turns off the automatic
|
||||
alignment used by the data creating pseudo-ops. */
|
||||
|
||||
static void
|
||||
s_align (x)
|
||||
int x;
|
||||
|
@ -2767,23 +3231,35 @@ s_align (x)
|
|||
if (temp)
|
||||
{
|
||||
auto_align = 1;
|
||||
if (!need_pass_2)
|
||||
frag_align (temp, (int) temp_fill);
|
||||
mips_align (temp, (int) temp_fill);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto_align = 0;
|
||||
}
|
||||
|
||||
record_alignment (now_seg, temp);
|
||||
|
||||
demand_empty_rest_of_line ();
|
||||
}
|
||||
|
||||
/* Handle .ascii and .asciiz. This just calls stringer and forgets
|
||||
that there was a previous instruction. */
|
||||
|
||||
static void
|
||||
s_stringer (append_zero)
|
||||
int append_zero;
|
||||
{
|
||||
mips_emit_delays ();
|
||||
stringer (append_zero);
|
||||
}
|
||||
|
||||
static void
|
||||
s_change_sec (sec)
|
||||
int sec;
|
||||
{
|
||||
segT segment;
|
||||
|
||||
mips_emit_delays ();
|
||||
segment = now_seg;
|
||||
switch (sec)
|
||||
{
|
||||
case 't':
|
||||
|
@ -2792,6 +3268,7 @@ s_change_sec (sec)
|
|||
case 'r':
|
||||
#ifdef OBJ_ECOFF
|
||||
subseg_new (".rdata", (subsegT) get_absolute_expression ());
|
||||
demand_empty_rest_of_line ();
|
||||
break;
|
||||
#else
|
||||
/* Fall through. */
|
||||
|
@ -2810,9 +3287,11 @@ s_change_sec (sec)
|
|||
case 's':
|
||||
#ifdef OBJ_ECOFF
|
||||
subseg_new (".sdata", (subsegT) get_absolute_expression ());
|
||||
demand_empty_rest_of_line ();
|
||||
break;
|
||||
#else
|
||||
as_bad ("Global pointers not supported; recompile -G 0");
|
||||
demand_empty_rest_of_line ();
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
@ -2823,9 +3302,9 @@ static void
|
|||
s_cons (log_size)
|
||||
int log_size;
|
||||
{
|
||||
|
||||
mips_emit_delays ();
|
||||
if (log_size > 0 && auto_align)
|
||||
frag_align (log_size, 0);
|
||||
mips_align (log_size, 0);
|
||||
cons (1 << log_size);
|
||||
}
|
||||
|
||||
|
@ -2858,85 +3337,18 @@ s_extern (x)
|
|||
}
|
||||
|
||||
static void
|
||||
s_float_cons (is_double)
|
||||
int is_double;
|
||||
s_float_cons (type)
|
||||
int type;
|
||||
{
|
||||
char *f;
|
||||
short words[4];
|
||||
int error_code, repeat;
|
||||
extern FLONUM_TYPE generic_floating_point_number;
|
||||
mips_emit_delays ();
|
||||
|
||||
if (auto_align)
|
||||
if (is_double)
|
||||
frag_align (3, 0);
|
||||
if (type == 'd')
|
||||
mips_align (3, 0);
|
||||
else
|
||||
frag_align (2, 0);
|
||||
mips_align (2, 0);
|
||||
|
||||
SKIP_WHITESPACE ();
|
||||
if (!is_end_of_line[(unsigned char) *input_line_pointer])
|
||||
{
|
||||
do
|
||||
{
|
||||
error_code = atof_generic (&input_line_pointer, ".", EXP_CHARS,
|
||||
&generic_floating_point_number);
|
||||
if (error_code)
|
||||
{
|
||||
if (error_code == ERROR_EXPONENT_OVERFLOW)
|
||||
as_warn ("Bad floating-point constant: exponent overflow");
|
||||
else
|
||||
as_warn ("Bad floating-point constant: unknown error code=%d.", error_code);
|
||||
}
|
||||
|
||||
if (is_double)
|
||||
{
|
||||
gen_to_words ((LITTLENUM_TYPE *) words,
|
||||
4 /* precision */ ,
|
||||
11 /* exponent_bits */ );
|
||||
}
|
||||
else
|
||||
{
|
||||
gen_to_words ((LITTLENUM_TYPE *) words,
|
||||
2 /* precision */ ,
|
||||
8 /* exponent_bits */ );
|
||||
}
|
||||
if (*input_line_pointer == ':')
|
||||
{
|
||||
input_line_pointer++;
|
||||
repeat = get_absolute_expression ();
|
||||
}
|
||||
else
|
||||
{
|
||||
repeat = 1;
|
||||
}
|
||||
if (is_double)
|
||||
{
|
||||
f = frag_more (repeat * 8);
|
||||
for (; repeat--; f += 8)
|
||||
{
|
||||
md_number_to_chars (f + 6, words[0], 2);
|
||||
md_number_to_chars (f + 4, words[1], 2);
|
||||
md_number_to_chars (f + 2, words[2], 2);
|
||||
md_number_to_chars (f, words[3], 2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
f = frag_more (repeat * 4);
|
||||
for (; repeat--; f += 4)
|
||||
{
|
||||
md_number_to_chars (f + 2, words[0], 2);
|
||||
md_number_to_chars (f, words[1], 2);
|
||||
}
|
||||
}
|
||||
SKIP_WHITESPACE ();
|
||||
if (*input_line_pointer != ',')
|
||||
break;
|
||||
input_line_pointer++;
|
||||
SKIP_WHITESPACE ();
|
||||
}
|
||||
while (1);
|
||||
}
|
||||
demand_empty_rest_of_line ();
|
||||
float_cons (type);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -2966,6 +3378,7 @@ s_mipsset (x)
|
|||
}
|
||||
else if (strcmp (name, "noreorder") == 0)
|
||||
{
|
||||
mips_emit_delays ();
|
||||
mips_noreorder = 1;
|
||||
}
|
||||
else if (strcmp (name, "at") == 0)
|
||||
|
@ -3010,6 +3423,17 @@ s_mipsset (x)
|
|||
demand_empty_rest_of_line ();
|
||||
}
|
||||
|
||||
/* The same as the usual .space directive, except that we have to
|
||||
forget about any previous instruction. */
|
||||
|
||||
static void
|
||||
s_mips_space (param)
|
||||
int param;
|
||||
{
|
||||
mips_emit_delays ();
|
||||
s_space (param);
|
||||
}
|
||||
|
||||
int
|
||||
tc_get_register ()
|
||||
{
|
||||
|
@ -3098,6 +3522,17 @@ md_estimate_size_before_relax (fragP, segtype)
|
|||
as_fatal ("md_estimate_size_before_relax");
|
||||
return (1);
|
||||
} /* md_estimate_size_before_relax() */
|
||||
|
||||
/* This function is called whenever a label is defined. It is used
|
||||
when handling branch delays; if a branch has a label, we assume we
|
||||
can not move it. */
|
||||
|
||||
void
|
||||
mips_define_label (sym)
|
||||
symbolS *sym;
|
||||
{
|
||||
insn_label = sym;
|
||||
}
|
||||
|
||||
#ifndef OBJ_ECOFF
|
||||
|
||||
|
|
Loading…
Reference in a new issue