* config/tc-mips.c (mips_arg_info): Replace allow_nonconst and
	lax_max with lax_match.
	(match_int_operand): Update accordingly.  Don't report an error
	for !lax_match-only cases.
	(match_insn): Replace more_alts with lax_match and use it to
	initialize the mips_arg_info field.  Add a complete_p parameter.
	Handle implicit VU0 suffixes here.
	(match_invalid_for_isa, match_insns, match_mips16_insns): New
	functions.
	(mips_ip, mips16_ip): Use them.
This commit is contained in:
Richard Sandiford
2013-08-19 19:42:50 +00:00
parent d436c1c2e8
commit 60f20e8ba8
2 changed files with 191 additions and 179 deletions

View File

@ -1,3 +1,16 @@
2013-08-19 Richard Sandiford <rdsandiford@googlemail.com>
* config/tc-mips.c (mips_arg_info): Replace allow_nonconst and
lax_max with lax_match.
(match_int_operand): Update accordingly. Don't report an error
for !lax_match-only cases.
(match_insn): Replace more_alts with lax_match and use it to
initialize the mips_arg_info field. Add a complete_p parameter.
Handle implicit VU0 suffixes here.
(match_invalid_for_isa, match_insns, match_mips16_insns): New
functions.
(mips_ip, mips16_ip): Use them.
2013-08-19 Richard Sandiford <rdsandiford@googlemail.com> 2013-08-19 Richard Sandiford <rdsandiford@googlemail.com>
* config/tc-mips.c (match_expression): Report uses of registers here. * config/tc-mips.c (match_expression): Report uses of registers here.

View File

@ -4271,14 +4271,11 @@ struct mips_arg_info
where it gives the lsb position. */ where it gives the lsb position. */
unsigned int last_op_int; unsigned int last_op_int;
/* If true, the OP_INT match routine should treat plain symbolic operands /* If true, match routines should assume that no later instruction
as if a relocation operator like %lo(...) had been used. This is only alternative matches and should therefore be as accomodating as
ever true if the operand can be relocated. */ possible. Match routines should not report errors if something
bfd_boolean allow_nonconst; is only invalid for !LAX_MATCH. */
bfd_boolean lax_match;
/* When true, the OP_INT match routine should allow unsigned N-bit
arguments to be used where a signed N-bit operand is expected. */
bfd_boolean lax_max;
/* True if a reference to the current AT register was seen. */ /* True if a reference to the current AT register was seen. */
bfd_boolean seen_at; bfd_boolean seen_at;
@ -4559,8 +4556,6 @@ match_int_operand (struct mips_arg_info *arg,
factor = 1 << operand->shift; factor = 1 << operand->shift;
min_val = mips_int_operand_min (operand); min_val = mips_int_operand_min (operand);
max_val = mips_int_operand_max (operand); max_val = mips_int_operand_max (operand);
if (arg->lax_max)
max_val = ((1 << operand_base->size) - 1) << operand->shift;
if (operand_base->lsb == 0 if (operand_base->lsb == 0
&& operand_base->size == 16 && operand_base->size == 16
@ -4580,13 +4575,10 @@ match_int_operand (struct mips_arg_info *arg,
if (offset_expr.X_op != O_constant) if (offset_expr.X_op != O_constant)
{ {
/* If non-constant operands are allowed then leave them for /* Accept non-constant operands if no later alternative matches,
the caller to process, otherwise fail the match. */ leaving it for the caller to process. */
if (!arg->allow_nonconst) if (!arg->lax_match)
{ return FALSE;
match_not_constant (arg);
return FALSE;
}
offset_reloc[0] = BFD_RELOC_LO16; offset_reloc[0] = BFD_RELOC_LO16;
return TRUE; return TRUE;
} }
@ -4595,6 +4587,16 @@ match_int_operand (struct mips_arg_info *arg,
ourselves. */ ourselves. */
sval = offset_expr.X_add_number; sval = offset_expr.X_add_number;
offset_expr.X_op = O_absent; offset_expr.X_op = O_absent;
/* For compatibility with older assemblers, we accept
0x8000-0xffff as signed 16-bit numbers when only
signed numbers are allowed. */
if (sval > max_val)
{
max_val = ((1 << operand_base->size) - 1) << operand->shift;
if (!arg->lax_match && sval <= max_val)
return FALSE;
}
} }
else else
{ {
@ -7047,7 +7049,7 @@ normalize_address_expr (expressionS *ex)
static bfd_boolean static bfd_boolean
match_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode, match_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
struct mips_operand_token *tokens, unsigned int opcode_extra, struct mips_operand_token *tokens, unsigned int opcode_extra,
bfd_boolean more_alts) bfd_boolean lax_match, bfd_boolean complete_p)
{ {
const char *args; const char *args;
struct mips_arg_info arg; struct mips_arg_info arg;
@ -7062,13 +7064,18 @@ match_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
offset_reloc[2] = BFD_RELOC_UNUSED; offset_reloc[2] = BFD_RELOC_UNUSED;
create_insn (insn, opcode); create_insn (insn, opcode);
insn->insn_opcode |= opcode_extra; /* When no opcode suffix is specified, assume ".xyzw". */
if ((opcode->pinfo2 & INSN2_VU0_CHANNEL_SUFFIX) != 0 && opcode_extra == 0)
insn->insn_opcode |= 0xf << mips_vu0_channel_mask.lsb;
else
insn->insn_opcode |= opcode_extra;
memset (&arg, 0, sizeof (arg)); memset (&arg, 0, sizeof (arg));
arg.insn = insn; arg.insn = insn;
arg.token = tokens; arg.token = tokens;
arg.argnum = 1; arg.argnum = 1;
arg.last_regno = ILLEGAL_REG; arg.last_regno = ILLEGAL_REG;
arg.dest_regno = ILLEGAL_REG; arg.dest_regno = ILLEGAL_REG;
arg.lax_match = lax_match;
for (args = opcode->args;; ++args) for (args = opcode->args;; ++args)
{ {
if (arg.token->type == OT_END) if (arg.token->type == OT_END)
@ -7107,6 +7114,8 @@ match_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
return FALSE; return FALSE;
/* Successful match. */ /* Successful match. */
if (!complete_p)
return TRUE;
clear_insn_error (); clear_insn_error ();
if (arg.dest_regno == arg.last_regno if (arg.dest_regno == arg.last_regno
&& strncmp (insn->insn_mo->name, "jalr", 4) == 0) && strncmp (insn->insn_mo->name, "jalr", 4) == 0)
@ -7148,7 +7157,6 @@ match_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
/* Handle special macro operands. Work out the properties of /* Handle special macro operands. Work out the properties of
other operands. */ other operands. */
arg.opnum += 1; arg.opnum += 1;
arg.lax_max = FALSE;
switch (*args) switch (*args)
{ {
case '+': case '+':
@ -7219,32 +7227,6 @@ match_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
return FALSE; return FALSE;
continue; continue;
/* ??? This is the traditional behavior, but is flaky if
there are alternative versions of the same instruction
for different subarchitectures. The next alternative
might not be suitable. */
case 'j':
/* For compatibility with older assemblers, we accept
0x8000-0xffff as signed 16-bit numbers when only
signed numbers are allowed. */
arg.lax_max = !more_alts;
case 'i':
/* Only accept non-constant operands if this is the
final alternative. Later alternatives might include
a macro implementation. */
arg.allow_nonconst = !more_alts;
break;
case 'u':
/* There are no macro implementations for out-of-range values. */
arg.allow_nonconst = TRUE;
break;
case 'o':
/* There should always be a macro implementation. */
arg.allow_nonconst = FALSE;
break;
case 'p': case 'p':
*offset_reloc = BFD_RELOC_16_PCREL_S2; *offset_reloc = BFD_RELOC_16_PCREL_S2;
break; break;
@ -7479,6 +7461,141 @@ match_mips16_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
} }
} }
/* Record that the current instruction is invalid for the current ISA. */
static void
match_invalid_for_isa (void)
{
set_insn_error_ss
(0, _("Opcode not supported on this processor: %s (%s)"),
mips_cpu_info_from_arch (mips_opts.arch)->name,
mips_cpu_info_from_isa (mips_opts.isa)->name);
}
/* Try to match TOKENS against a series of opcode entries, starting at FIRST.
Return true if a definite match or failure was found, storing any match
in INSN. OPCODE_EXTRA is a value that should be ORed into the opcode
(to handle things like VU0 suffixes). LAX_MATCH is true if we have already
tried and failed to match under normal conditions and now want to try a
more relaxed match. */
static bfd_boolean
match_insns (struct mips_cl_insn *insn, const struct mips_opcode *first,
const struct mips_opcode *past, struct mips_operand_token *tokens,
int opcode_extra, bfd_boolean lax_match)
{
const struct mips_opcode *opcode;
const struct mips_opcode *invalid_delay_slot;
bfd_boolean seen_valid_for_isa, seen_valid_for_size;
/* Search for a match, ignoring alternatives that don't satisfy the
current ISA or forced_length. */
invalid_delay_slot = 0;
seen_valid_for_isa = FALSE;
seen_valid_for_size = FALSE;
opcode = first;
do
{
gas_assert (strcmp (opcode->name, first->name) == 0);
if (is_opcode_valid (opcode))
{
seen_valid_for_isa = TRUE;
if (is_size_valid (opcode))
{
bfd_boolean delay_slot_ok;
seen_valid_for_size = TRUE;
delay_slot_ok = is_delay_slot_valid (opcode);
if (match_insn (insn, opcode, tokens, opcode_extra,
lax_match, delay_slot_ok))
{
if (!delay_slot_ok)
{
if (!invalid_delay_slot)
invalid_delay_slot = opcode;
}
else
return TRUE;
}
}
}
++opcode;
}
while (opcode < past && strcmp (opcode->name, first->name) == 0);
/* If the only matches we found had the wrong length for the delay slot,
pick the first such match. We'll issue an appropriate warning later. */
if (invalid_delay_slot)
{
if (match_insn (insn, invalid_delay_slot, tokens, opcode_extra,
lax_match, TRUE))
return TRUE;
abort ();
}
/* Handle the case where we didn't try to match an instruction because
all the alternatives were incompatible with the current ISA. */
if (!seen_valid_for_isa)
{
match_invalid_for_isa ();
return TRUE;
}
/* Handle the case where we didn't try to match an instruction because
all the alternatives were of the wrong size. */
if (!seen_valid_for_size)
{
if (mips_opts.insn32)
set_insn_error (0, _("Opcode not supported in the `insn32' mode"));
else
set_insn_error_i
(0, _("Unrecognized %d-bit version of microMIPS opcode"),
8 * forced_insn_length);
return TRUE;
}
return FALSE;
}
/* Like match_insns, but for MIPS16. */
static bfd_boolean
match_mips16_insns (struct mips_cl_insn *insn, const struct mips_opcode *first,
struct mips_operand_token *tokens)
{
const struct mips_opcode *opcode;
bfd_boolean seen_valid_for_isa;
/* Search for a match, ignoring alternatives that don't satisfy the
current ISA. There are no separate entries for extended forms so
we deal with forced_length later. */
seen_valid_for_isa = FALSE;
opcode = first;
do
{
gas_assert (strcmp (opcode->name, first->name) == 0);
if (is_opcode_valid_16 (opcode))
{
seen_valid_for_isa = TRUE;
if (match_mips16_insn (insn, opcode, tokens))
return TRUE;
}
++opcode;
}
while (opcode < &mips16_opcodes[bfd_mips16_num_opcodes]
&& strcmp (opcode->name, first->name) == 0);
/* Handle the case where we didn't try to match an instruction because
all the alternatives were incompatible with the current ISA. */
if (!seen_valid_for_isa)
{
match_invalid_for_isa ();
return TRUE;
}
return FALSE;
}
/* Set up global variables for the start of a new macro. */ /* Set up global variables for the start of a new macro. */
static void static void
@ -12952,14 +13069,10 @@ mips_lookup_insn (struct hash_control *hash, const char *start,
to offset_expr. */ to offset_expr. */
static void static void
mips_ip (char *str, struct mips_cl_insn *ip) mips_ip (char *str, struct mips_cl_insn *insn)
{ {
bfd_boolean wrong_delay_slot_insns = FALSE; const struct mips_opcode *first, *past;
bfd_boolean need_delay_slot_ok = TRUE;
struct mips_opcode *firstinsn = NULL;
const struct mips_opcode *past;
struct hash_control *hash; struct hash_control *hash;
struct mips_opcode *first, *insn;
char format; char format;
size_t end; size_t end;
struct mips_operand_token *tokens; struct mips_operand_token *tokens;
@ -12976,26 +13089,22 @@ mips_ip (char *str, struct mips_cl_insn *ip)
past = &mips_opcodes[NUMOPCODES]; past = &mips_opcodes[NUMOPCODES];
} }
forced_insn_length = 0; forced_insn_length = 0;
insn = NULL;
opcode_extra = 0; opcode_extra = 0;
/* We first try to match an instruction up to a space or to the end. */ /* We first try to match an instruction up to a space or to the end. */
for (end = 0; str[end] != '\0' && !ISSPACE (str[end]); end++) for (end = 0; str[end] != '\0' && !ISSPACE (str[end]); end++)
continue; continue;
first = insn = mips_lookup_insn (hash, str, end, &opcode_extra); first = mips_lookup_insn (hash, str, end, &opcode_extra);
if (insn == NULL) if (first == NULL)
{ {
set_insn_error (0, _("Unrecognized opcode")); set_insn_error (0, _("Unrecognized opcode"));
return; return;
} }
/* When no opcode suffix is specified, assume ".xyzw". */
if ((insn->pinfo2 & INSN2_VU0_CHANNEL_SUFFIX) != 0 && opcode_extra == 0)
opcode_extra = 0xf << mips_vu0_channel_mask.lsb;
if (strcmp (insn->name, "li.s") == 0) if (strcmp (first->name, "li.s") == 0)
format = 'f'; format = 'f';
else if (strcmp (insn->name, "li.d") == 0) else if (strcmp (first->name, "li.d") == 0)
format = 'd'; format = 'd';
else else
format = 0; format = 0;
@ -13003,84 +13112,10 @@ mips_ip (char *str, struct mips_cl_insn *ip)
if (!tokens) if (!tokens)
return; return;
/* For microMIPS instructions placed in a fixed-length branch delay slot if (!match_insns (insn, first, past, tokens, opcode_extra, FALSE)
we make up to two passes over the relevant fragment of the opcode && !match_insns (insn, first, past, tokens, opcode_extra, TRUE))
table. First we try instructions that meet the delay slot's length set_insn_error (0, _("Illegal operands"));
requirement. If none matched, then we retry with the remaining ones
and if one matches, then we use it and then issue an appropriate
warning later on. */
for (;;)
{
bfd_boolean delay_slot_ok;
bfd_boolean size_ok;
bfd_boolean ok;
bfd_boolean more_alts;
gas_assert (strcmp (insn->name, first->name) == 0);
ok = is_opcode_valid (insn);
size_ok = is_size_valid (insn);
delay_slot_ok = is_delay_slot_valid (insn);
if (!delay_slot_ok && !wrong_delay_slot_insns)
{
firstinsn = insn;
wrong_delay_slot_insns = TRUE;
}
more_alts = (insn + 1 < past
&& strcmp (insn[0].name, insn[1].name) == 0);
if (!ok || !size_ok || delay_slot_ok != need_delay_slot_ok)
{
if (more_alts)
{
++insn;
continue;
}
if (wrong_delay_slot_insns && need_delay_slot_ok)
{
gas_assert (firstinsn);
need_delay_slot_ok = FALSE;
past = insn + 1;
insn = firstinsn;
continue;
}
if (!ok)
set_insn_error_ss
(0, _("Opcode not supported on this processor: %s (%s)"),
mips_cpu_info_from_arch (mips_opts.arch)->name,
mips_cpu_info_from_isa (mips_opts.isa)->name);
else if (mips_opts.insn32)
set_insn_error
(0, _("Opcode not supported in the `insn32' mode"));
else
set_insn_error_i
(0, _("Unrecognized %d-bit version of microMIPS opcode"),
8 * forced_insn_length);
break;
}
if (match_insn (ip, insn, tokens, opcode_extra,
more_alts || (wrong_delay_slot_insns
&& need_delay_slot_ok)))
break;
/* Args don't match. */
set_insn_error (0, _("Illegal operands"));
if (more_alts)
{
++insn;
continue;
}
if (wrong_delay_slot_insns && need_delay_slot_ok)
{
gas_assert (firstinsn);
need_delay_slot_ok = FALSE;
past = insn + 1;
insn = firstinsn;
continue;
}
break;
}
obstack_free (&mips_operand_tokens, tokens); obstack_free (&mips_operand_tokens, tokens);
} }
@ -13089,10 +13124,10 @@ mips_ip (char *str, struct mips_cl_insn *ip)
bytes if the user explicitly requested a small or extended instruction. */ bytes if the user explicitly requested a small or extended instruction. */
static void static void
mips16_ip (char *str, struct mips_cl_insn *ip) mips16_ip (char *str, struct mips_cl_insn *insn)
{ {
char *end, *s, c; char *end, *s, c;
struct mips_opcode *insn, *first; struct mips_opcode *first;
struct mips_operand_token *tokens; struct mips_operand_token *tokens;
forced_insn_length = 0; forced_insn_length = 0;
@ -13133,10 +13168,10 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
forced_insn_length = 2; forced_insn_length = 2;
*end = 0; *end = 0;
first = insn = (struct mips_opcode *) hash_find (mips16_op_hash, str); first = (struct mips_opcode *) hash_find (mips16_op_hash, str);
*end = c; *end = c;
if (!insn) if (!first)
{ {
set_insn_error (0, _("Unrecognized opcode")); set_insn_error (0, _("Unrecognized opcode"));
return; return;
@ -13146,45 +13181,9 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
if (!tokens) if (!tokens)
return; return;
for (;;) if (!match_mips16_insns (insn, first, tokens))
{ set_insn_error (0, _("Illegal operands"));
bfd_boolean ok;
bfd_boolean more_alts;
gas_assert (strcmp (insn->name, first->name) == 0);
ok = is_opcode_valid_16 (insn);
more_alts = (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes]
&& strcmp (insn[0].name, insn[1].name) == 0);
if (! ok)
{
if (more_alts)
{
++insn;
continue;
}
else
{
set_insn_error_ss
(0, _("Opcode not supported on this processor: %s (%s)"),
mips_cpu_info_from_arch (mips_opts.arch)->name,
mips_cpu_info_from_isa (mips_opts.isa)->name);
break;
}
}
if (match_mips16_insn (ip, insn, tokens))
break;
/* Args don't match. */
set_insn_error (0, _("Illegal operands"));
if (more_alts)
{
++insn;
continue;
}
break;
}
obstack_free (&mips_operand_tokens, tokens); obstack_free (&mips_operand_tokens, tokens);
} }