aboutsummaryrefslogtreecommitdiff
path: root/gas/config
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@airs.com>1993-07-16 16:26:41 +0000
committerIan Lance Taylor <ian@airs.com>1993-07-16 16:26:41 +0000
commitbecfe05e6138362a050fd8003c7adaf4c9c39c57 (patch)
tree802d5cd408cdbe2a72ec1b9b28e4b2539ab494dc /gas/config
parent1e9cf5658626de55a962fbd239dcc38854981b3d (diff)
downloadfsf-binutils-gdb-becfe05e6138362a050fd8003c7adaf4c9c39c57.zip
fsf-binutils-gdb-becfe05e6138362a050fd8003c7adaf4c9c39c57.tar.gz
fsf-binutils-gdb-becfe05e6138362a050fd8003c7adaf4c9c39c57.tar.bz2
* 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.
Diffstat (limited to 'gas/config')
-rw-r--r--gas/config/tc-hppa.h2
-rw-r--r--gas/config/tc-mips.c681
2 files changed, 560 insertions, 123 deletions
diff --git a/gas/config/tc-hppa.h b/gas/config/tc-hppa.h
index 308863f..821ec3d 100644
--- a/gas/config/tc-hppa.h
+++ b/gas/config/tc-hppa.h
@@ -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 */
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
index 18c1bcf..9075b26 100644
--- a/gas/config/tc-mips.c
+++ b/gas/config/tc-mips.c
@@ -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))
+ {
+ /* 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];
+
+ /* 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
{
- f = frag_more (4);
- md_number_to_chars (f, 0, 4);
- };
+ /* 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;
+ }
- /* One extra nop */
- if (ip->insn_mo->pinfo & (INSN_READ_HI | INSN_READ_LO))
+ /* 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)
{
- f = frag_more (4);
- md_number_to_chars (f, 0, 4);
+ 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