* config/tc-arc.c (delay_slot_type): New function.

(md_assemble): Use hashed list of insns.
	Print warning if 8 byte insn appears in delay slot.
	(md_operand): Handle %st(sym1-sym2).
	(get_arc_exp_reloc_type): Likewise.
	(md_apply_fix, case BFD_RELOC_ARC_B26): Now using implicit addends
	so must store them here.
This commit is contained in:
David Edelsohn 1995-04-12 14:41:21 +00:00
parent 5d57a66b5a
commit fa7a56f8dc
1 changed files with 96 additions and 39 deletions

View File

@ -31,6 +31,7 @@ static arc_insn arc_insert_operand PARAMS ((arc_insn,
const struct arc_operand *, int,
const struct arc_operand_value *,
offsetT, char *, unsigned int));
static int delay_slot_type PARAMS ((arc_insn));
static void arc_common PARAMS ((int));
static void arc_cpu PARAMS ((int));
/*static void arc_rename PARAMS ((int));*/
@ -277,6 +278,18 @@ arc_insert_operand (insn, operand, mods, reg, val, file, line)
return insn;
}
/* Return the delay slot type for INSN (or -1 if it isn't a branch). */
/* ??? This is a quick hack. May wish to later generalize this concept
to record all suffixes the insn contains (for various warnings, etc.). */
static int
delay_slot_type (arc_insn insn)
{
if ((insn >> 27) >= 4 && (insn >> 27) <= 7)
return (insn & 0x60) >> 5;
return -1;
}
/* We need to keep a list of fixups. We can't simply generate them as
we go, because that would require us to first create the frag, and
that would screw up references to ``.''. */
@ -296,7 +309,7 @@ void
md_assemble (str)
char *str;
{
const struct arc_opcode *opcode,*opcode_end;
const struct arc_opcode *opcode;
char *start;
arc_insn insn;
static int init_tables_p = 0;
@ -313,23 +326,15 @@ md_assemble (str)
while (isspace (*str))
str++;
/* The instructions are sorted by the first letter. Scan the opcode table
until we find the right one. */
opcode_end = arc_opcodes + arc_opcodes_count;
for (opcode = arc_opcodes; opcode < opcode_end; opcode++)
if (*opcode->syntax == *str)
break;
if (opcode == opcode_end)
{
as_bad ("bad instruction `%s'", str);
return;
}
/* The instructions are stored in lists hashed by the first letter (though
we needn't care how they're hashed). Get the first in the list. */
/* Keep looking until we find a match. If we haven't found a match, and the
first character no longer matches, we needn't look any further. */
opcode = arc_opcode_asm_list (str);
/* Keep looking until we find a match. */
start = str;
for ( ; opcode < opcode_end && *opcode->syntax == *start; ++opcode)
for ( ; opcode != NULL; opcode = ARC_OPCODE_NEXT_ASM (opcode))
{
int past_opcode_p;
char *syn;
@ -604,11 +609,12 @@ md_assemble (str)
{
int i;
char *f;
long limm;
long limm, limm_p;
/* ??? For the moment we assume a valid `str' can only contain blanks
/* For the moment we assume a valid `str' can only contain blanks
now. IE: We needn't try again with a longer version of the
insn. */
insn and it is assumed that longer versions of insns appear
before shorter ones (eg: lsr r2,r3,1 vs lsr r2,r3). */
while (isspace (*str))
++str;
@ -616,11 +622,28 @@ md_assemble (str)
if (*str != '\0')
as_bad ("junk at end of line: `%s'", str);
/* Is there a limm value? */
limm_p = arc_opcode_limm_p (&limm);
/* Putting an insn with a limm value in a delay slot is supposed to
be legal, but let's warn the user anyway. Ditto for 8 byte jumps
with delay slots. */
{
static int in_delay_slot_p = 0;
int this_insn_delay_slot_type = delay_slot_type (insn);
if (in_delay_slot_p && limm_p)
as_warn ("8 byte instruction in delay slot");
if (this_insn_delay_slot_type > 0 && limm_p)
as_warn ("8 byte jump instruction with delay slot");
in_delay_slot_p = (this_insn_delay_slot_type > 0) && !limm_p;
}
/* Write out the instruction.
It is important to fetch enough space in one call to `frag_more'.
We use (f - frag_now->fr_literal) to compute where we are and we
don't want frag_now to change between calls. */
if (arc_opcode_limm_p (&limm))
if (limm_p)
{
f = frag_more (8);
md_number_to_chars (f, insn, 4);
@ -1036,8 +1059,6 @@ md_operand (expressionP)
if (*p == '%' && strncmp (p, "%st(", 4) == 0)
{
expressionS two;
input_line_pointer += 4;
expression (expressionP);
if (*input_line_pointer != ')')
@ -1046,17 +1067,39 @@ md_operand (expressionP)
return;
}
++input_line_pointer;
if (expressionP->X_op != O_symbol
|| expressionP->X_add_number != 0)
if (expressionP->X_op == O_symbol
&& expressionP->X_add_number == 0
/* I think this test is unnecessary but just as a sanity check... */
&& expressionP->X_op_symbol == NULL)
{
as_bad ("expression too complex for %st");
expressionS two;
expressionP->X_op = O_right_shift;
two.X_op = O_constant;
two.X_add_symbol = two.X_op_symbol = NULL;
two.X_add_number = 2;
expressionP->X_op_symbol = make_expr_symbol (&two);
}
/* allow %st(sym1-sym2) */
else if (expressionP->X_op == O_subtract
&& expressionP->X_add_symbol != NULL
&& expressionP->X_op_symbol != NULL
&& expressionP->X_add_number == 0)
{
expressionS two;
expressionP->X_add_symbol = make_expr_symbol (expressionP);
expressionP->X_op = O_right_shift;
two.X_op = O_constant;
two.X_add_symbol = two.X_op_symbol = NULL;
two.X_add_number = 2;
expressionP->X_op_symbol = make_expr_symbol (&two);
}
else
{
as_bad ("expression too complex for %%st");
return;
}
expressionP->X_op = O_right_shift;
two.X_op = O_constant;
two.X_add_symbol = two.X_op_symbol = NULL;
two.X_add_number = 2;
expressionP->X_op_symbol = make_expr_symbol (&two);
}
}
@ -1160,25 +1203,31 @@ get_arc_exp_reloc_type (data_p, default_type, exp, expnew)
expressionS *expnew;
{
/* If the expression is "symbol >> 2" we must change it to just "symbol",
as fix_new_exp can't handle it. That's ok though. What's really going
on here is that we're using ">> 2" as a special syntax for specifying
BFD_RELOC_ARC_B26. */
as fix_new_exp can't handle it. Similarily for (symbol - symbol) >> 2.
That's ok though. What's really going on here is that we're using
">> 2" as a special syntax for specifying BFD_RELOC_ARC_B26. */
if (exp->X_op == O_right_shift)
if (exp->X_op == O_right_shift
&& exp->X_op_symbol != NULL
&& exp->X_op_symbol->sy_value.X_op == O_constant
&& exp->X_op_symbol->sy_value.X_add_number == 2
&& exp->X_add_number == 0)
{
if (exp->X_add_symbol != NULL
&& (exp->X_add_symbol->sy_value.X_op == O_constant
|| exp->X_add_symbol->sy_value.X_op == O_symbol)
&& exp->X_op_symbol != NULL
&& exp->X_op_symbol->sy_value.X_op == O_constant
&& exp->X_op_symbol->sy_value.X_add_number == 2
&& exp->X_add_number == 0)
|| exp->X_add_symbol->sy_value.X_op == O_symbol))
{
*expnew = *exp;
expnew->X_op = O_symbol;
expnew->X_op_symbol = NULL;
return data_p ? BFD_RELOC_ARC_B26 : arc_operand_map['J'];
}
else if (exp->X_add_symbol != NULL
&& exp->X_add_symbol->sy_value.X_op == O_subtract)
{
*expnew = exp->X_add_symbol->sy_value;
return data_p ? BFD_RELOC_ARC_B26 : arc_operand_map['J'];
}
}
*expnew = *exp;
@ -1314,7 +1363,6 @@ md_apply_fix (fixP, valueP)
value, 2);
break;
case BFD_RELOC_32:
case BFD_RELOC_ARC_B26:
md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
value, 4);
break;
@ -1324,6 +1372,15 @@ md_apply_fix (fixP, valueP)
value, 8);
break;
#endif
case BFD_RELOC_ARC_B26:
/* If !fixP->fx_done then `value' is an implicit addend.
We must shift it right by 2 in this case as well because the
linker performs the relocation and then adds this in (as opposed
to adding this in and then shifting right by 2). */
value >>= 2;
md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
value, 4);
break;
default:
abort ();
}