mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-06-24 12:23:31 +08:00
gas/
* config/tc-mips.c (mips32_to_16_reg_map): Delete. (match_entry_exit_operand): New function. (match_save_restore_list_operand): Likewise. (match_operand): Use them. (check_absolute_expr): Delete. (mips16_ip): Rewrite main parsing loop to use mips_operands.
This commit is contained in:
@ -1,3 +1,12 @@
|
|||||||
|
2013-07-14 Richard Sandiford <rdsandiford@googlemail.com>
|
||||||
|
|
||||||
|
* config/tc-mips.c (mips32_to_16_reg_map): Delete.
|
||||||
|
(match_entry_exit_operand): New function.
|
||||||
|
(match_save_restore_list_operand): Likewise.
|
||||||
|
(match_operand): Use them.
|
||||||
|
(check_absolute_expr): Delete.
|
||||||
|
(mips16_ip): Rewrite main parsing loop to use mips_operands.
|
||||||
|
|
||||||
2013-07-14 Richard Sandiford <rdsandiford@googlemail.com>
|
2013-07-14 Richard Sandiford <rdsandiford@googlemail.com>
|
||||||
|
|
||||||
* config/tc-mips.c: Enable functions commented out in previous patch.
|
* config/tc-mips.c: Enable functions commented out in previous patch.
|
||||||
|
@ -755,18 +755,6 @@ static struct mips_hi_fixup *mips_hi_fixup_list;
|
|||||||
|
|
||||||
static fragS *prev_reloc_op_frag;
|
static fragS *prev_reloc_op_frag;
|
||||||
|
|
||||||
/* Map normal MIPS register numbers to mips16 register numbers. */
|
|
||||||
|
|
||||||
#define X ILLEGAL_REG
|
|
||||||
static const int mips32_to_16_reg_map[] =
|
|
||||||
{
|
|
||||||
X, X, 2, 3, 4, 5, 6, 7,
|
|
||||||
X, X, X, X, X, X, X, X,
|
|
||||||
0, 1, X, X, X, X, X, X,
|
|
||||||
X, X, X, X, X, X, X, X
|
|
||||||
};
|
|
||||||
#undef X
|
|
||||||
|
|
||||||
/* Map mips16 register numbers to normal MIPS register numbers. */
|
/* Map mips16 register numbers to normal MIPS register numbers. */
|
||||||
|
|
||||||
static const unsigned int mips16_to_32_reg_map[] =
|
static const unsigned int mips16_to_32_reg_map[] =
|
||||||
@ -4280,6 +4268,236 @@ match_lwm_swm_list_operand (struct mips_arg_info *arg,
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* OP_ENTRY_EXIT_LIST matcher. */
|
||||||
|
|
||||||
|
static char *
|
||||||
|
match_entry_exit_operand (struct mips_arg_info *arg,
|
||||||
|
const struct mips_operand *operand, char *s)
|
||||||
|
{
|
||||||
|
unsigned int mask;
|
||||||
|
bfd_boolean is_exit;
|
||||||
|
|
||||||
|
/* The format is the same for both ENTRY and EXIT, but the constraints
|
||||||
|
are different. */
|
||||||
|
is_exit = strcmp (arg->insn->insn_mo->name, "exit") == 0;
|
||||||
|
mask = (is_exit ? 7 << 3 : 0);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
unsigned int regno1, regno2;
|
||||||
|
bfd_boolean is_freg;
|
||||||
|
|
||||||
|
if (reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®no1))
|
||||||
|
is_freg = FALSE;
|
||||||
|
else if (reg_lookup (&s, RTYPE_FPU, ®no1))
|
||||||
|
is_freg = TRUE;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
SKIP_SPACE_TABS (s);
|
||||||
|
if (*s == '-')
|
||||||
|
{
|
||||||
|
++s;
|
||||||
|
SKIP_SPACE_TABS (s);
|
||||||
|
if (!reg_lookup (&s, (is_freg ? RTYPE_FPU
|
||||||
|
: RTYPE_GP | RTYPE_NUM), ®no2))
|
||||||
|
return 0;
|
||||||
|
SKIP_SPACE_TABS (s);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
regno2 = regno1;
|
||||||
|
|
||||||
|
if (is_exit && is_freg && regno1 == 0 && regno2 < 2)
|
||||||
|
{
|
||||||
|
mask &= ~(7 << 3);
|
||||||
|
mask |= (5 + regno2) << 3;
|
||||||
|
}
|
||||||
|
else if (!is_exit && regno1 == 4 && regno2 >= 4 && regno2 <= 7)
|
||||||
|
mask |= (regno2 - 3) << 3;
|
||||||
|
else if (regno1 == 16 && regno2 >= 16 && regno2 <= 17)
|
||||||
|
mask |= (regno2 - 15) << 1;
|
||||||
|
else if (regno1 == RA && regno2 == RA)
|
||||||
|
mask |= 1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!*s)
|
||||||
|
break;
|
||||||
|
if (*s != ',')
|
||||||
|
return 0;
|
||||||
|
arg->argnum += 1;
|
||||||
|
++s;
|
||||||
|
SKIP_SPACE_TABS (s);
|
||||||
|
}
|
||||||
|
insn_insert_operand (arg->insn, operand, mask);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OP_SAVE_RESTORE_LIST matcher. */
|
||||||
|
|
||||||
|
static char *
|
||||||
|
match_save_restore_list_operand (struct mips_arg_info *arg, char *s)
|
||||||
|
{
|
||||||
|
unsigned int opcode, args, statics, sregs;
|
||||||
|
unsigned int num_frame_sizes, num_args, num_statics, num_sregs;
|
||||||
|
expressionS value;
|
||||||
|
offsetT frame_size;
|
||||||
|
const char *error;
|
||||||
|
|
||||||
|
error = 0;
|
||||||
|
opcode = arg->insn->insn_opcode;
|
||||||
|
frame_size = 0;
|
||||||
|
num_frame_sizes = 0;
|
||||||
|
args = 0;
|
||||||
|
statics = 0;
|
||||||
|
sregs = 0;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
unsigned int regno1, regno2;
|
||||||
|
|
||||||
|
my_getExpression (&value, s);
|
||||||
|
if (value.X_op == O_constant)
|
||||||
|
{
|
||||||
|
/* Handle the frame size. */
|
||||||
|
num_frame_sizes += 1;
|
||||||
|
frame_size = value.X_add_number;
|
||||||
|
s = expr_end;
|
||||||
|
SKIP_SPACE_TABS (s);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®no1))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
SKIP_SPACE_TABS (s);
|
||||||
|
if (*s == '-')
|
||||||
|
{
|
||||||
|
++s;
|
||||||
|
SKIP_SPACE_TABS (s);
|
||||||
|
if (!reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®no2)
|
||||||
|
|| regno2 < regno1)
|
||||||
|
return 0;
|
||||||
|
SKIP_SPACE_TABS (s);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
regno2 = regno1;
|
||||||
|
|
||||||
|
while (regno1 <= regno2)
|
||||||
|
{
|
||||||
|
if (regno1 >= 4 && regno1 <= 7)
|
||||||
|
{
|
||||||
|
if (num_frame_sizes == 0)
|
||||||
|
/* args $a0-$a3 */
|
||||||
|
args |= 1 << (regno1 - 4);
|
||||||
|
else
|
||||||
|
/* statics $a0-$a3 */
|
||||||
|
statics |= 1 << (regno1 - 4);
|
||||||
|
}
|
||||||
|
else if (regno1 >= 16 && regno1 <= 23)
|
||||||
|
/* $s0-$s7 */
|
||||||
|
sregs |= 1 << (regno1 - 16);
|
||||||
|
else if (regno1 == 30)
|
||||||
|
/* $s8 */
|
||||||
|
sregs |= 1 << 8;
|
||||||
|
else if (regno1 == 31)
|
||||||
|
/* Add $ra to insn. */
|
||||||
|
opcode |= 0x40;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
regno1 += 1;
|
||||||
|
if (regno1 == 24)
|
||||||
|
regno1 = 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!*s)
|
||||||
|
break;
|
||||||
|
if (*s != ',')
|
||||||
|
return 0;
|
||||||
|
arg->argnum += 1;
|
||||||
|
++s;
|
||||||
|
SKIP_SPACE_TABS (s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encode args/statics combination. */
|
||||||
|
if (args & statics)
|
||||||
|
return 0;
|
||||||
|
else if (args == 0xf)
|
||||||
|
/* All $a0-$a3 are args. */
|
||||||
|
opcode |= MIPS16_ALL_ARGS << 16;
|
||||||
|
else if (statics == 0xf)
|
||||||
|
/* All $a0-$a3 are statics. */
|
||||||
|
opcode |= MIPS16_ALL_STATICS << 16;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Count arg registers. */
|
||||||
|
num_args = 0;
|
||||||
|
while (args & 0x1)
|
||||||
|
{
|
||||||
|
args >>= 1;
|
||||||
|
num_args += 1;
|
||||||
|
}
|
||||||
|
if (args != 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Count static registers. */
|
||||||
|
num_statics = 0;
|
||||||
|
while (statics & 0x8)
|
||||||
|
{
|
||||||
|
statics = (statics << 1) & 0xf;
|
||||||
|
num_statics += 1;
|
||||||
|
}
|
||||||
|
if (statics != 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Encode args/statics. */
|
||||||
|
opcode |= ((num_args << 2) | num_statics) << 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encode $s0/$s1. */
|
||||||
|
if (sregs & (1 << 0)) /* $s0 */
|
||||||
|
opcode |= 0x20;
|
||||||
|
if (sregs & (1 << 1)) /* $s1 */
|
||||||
|
opcode |= 0x10;
|
||||||
|
sregs >>= 2;
|
||||||
|
|
||||||
|
/* Encode $s2-$s8. */
|
||||||
|
num_sregs = 0;
|
||||||
|
while (sregs & 1)
|
||||||
|
{
|
||||||
|
sregs >>= 1;
|
||||||
|
num_sregs += 1;
|
||||||
|
}
|
||||||
|
if (sregs != 0)
|
||||||
|
return 0;
|
||||||
|
opcode |= num_sregs << 24;
|
||||||
|
|
||||||
|
/* Encode frame size. */
|
||||||
|
if (num_frame_sizes == 0)
|
||||||
|
error = _("Missing frame size");
|
||||||
|
else if (num_frame_sizes > 1)
|
||||||
|
error = _("Frame size specified twice");
|
||||||
|
else if ((frame_size & 7) != 0 || frame_size < 0 || frame_size > 0xff * 8)
|
||||||
|
error = _("Invalid frame size");
|
||||||
|
else if (frame_size != 128 || (opcode >> 16) != 0)
|
||||||
|
{
|
||||||
|
frame_size /= 8;
|
||||||
|
opcode |= (((frame_size & 0xf0) << 16)
|
||||||
|
| (frame_size & 0x0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
if (arg->soft_match)
|
||||||
|
return 0;
|
||||||
|
as_bad (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finally build the instruction. */
|
||||||
|
if ((opcode >> 16) != 0 || frame_size == 0)
|
||||||
|
opcode |= MIPS16_EXTEND;
|
||||||
|
arg->insn->insn_opcode = opcode;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
/* OP_MDMX_IMM_REG matcher. */
|
/* OP_MDMX_IMM_REG matcher. */
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
@ -4454,8 +4672,10 @@ match_operand (struct mips_arg_info *arg,
|
|||||||
return match_lwm_swm_list_operand (arg, operand, s);
|
return match_lwm_swm_list_operand (arg, operand, s);
|
||||||
|
|
||||||
case OP_ENTRY_EXIT_LIST:
|
case OP_ENTRY_EXIT_LIST:
|
||||||
|
return match_entry_exit_operand (arg, operand, s);
|
||||||
|
|
||||||
case OP_SAVE_RESTORE_LIST:
|
case OP_SAVE_RESTORE_LIST:
|
||||||
abort ();
|
return match_save_restore_list_operand (arg, s);
|
||||||
|
|
||||||
case OP_MDMX_IMM_REG:
|
case OP_MDMX_IMM_REG:
|
||||||
return match_mdmx_imm_reg_operand (arg, operand, s);
|
return match_mdmx_imm_reg_operand (arg, operand, s);
|
||||||
@ -6573,21 +6793,6 @@ set_at (int reg, int unsignedp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Warn if an expression is not a constant. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
check_absolute_expr (struct mips_cl_insn *ip, expressionS *ex)
|
|
||||||
{
|
|
||||||
if (ex->X_op == O_big)
|
|
||||||
as_bad (_("unsupported large constant"));
|
|
||||||
else if (ex->X_op != O_constant)
|
|
||||||
as_bad (_("Instruction %s requires absolute expression"),
|
|
||||||
ip->insn_mo->name);
|
|
||||||
|
|
||||||
if (HAVE_32BIT_GPRS)
|
|
||||||
normalize_constant_expr (ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Count the leading zeroes by performing a binary chop. This is a
|
/* Count the leading zeroes by performing a binary chop. This is a
|
||||||
bulky bit of source, but performance is a LOT better for the
|
bulky bit of source, but performance is a LOT better for the
|
||||||
majority of values than a simple loop to count the bits:
|
majority of values than a simple loop to count the bits:
|
||||||
@ -12064,10 +12269,10 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
|
|||||||
const char *args;
|
const char *args;
|
||||||
struct mips_opcode *insn;
|
struct mips_opcode *insn;
|
||||||
char *argsstart;
|
char *argsstart;
|
||||||
unsigned int regno;
|
|
||||||
unsigned int lastregno = 0;
|
|
||||||
char *s_reset;
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
const struct mips_operand *operand;
|
||||||
|
const struct mips_operand *ext_operand;
|
||||||
|
struct mips_arg_info arg;
|
||||||
|
|
||||||
insn_error = NULL;
|
insn_error = NULL;
|
||||||
|
|
||||||
@ -12118,15 +12323,17 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
|
|||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
bfd_boolean ok;
|
bfd_boolean ok;
|
||||||
|
bfd_boolean more_alts;
|
||||||
char relax_char;
|
char relax_char;
|
||||||
|
|
||||||
gas_assert (strcmp (insn->name, str) == 0);
|
gas_assert (strcmp (insn->name, str) == 0);
|
||||||
|
|
||||||
ok = is_opcode_valid_16 (insn);
|
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 (! ok)
|
||||||
{
|
{
|
||||||
if (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes]
|
if (more_alts)
|
||||||
&& strcmp (insn->name, insn[1].name) == 0)
|
|
||||||
{
|
{
|
||||||
++insn;
|
++insn;
|
||||||
continue;
|
continue;
|
||||||
@ -12154,26 +12361,41 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
|
|||||||
offset_reloc[1] = BFD_RELOC_UNUSED;
|
offset_reloc[1] = BFD_RELOC_UNUSED;
|
||||||
offset_reloc[2] = BFD_RELOC_UNUSED;
|
offset_reloc[2] = BFD_RELOC_UNUSED;
|
||||||
relax_char = 0;
|
relax_char = 0;
|
||||||
|
|
||||||
|
memset (&arg, 0, sizeof (arg));
|
||||||
|
arg.insn = ip;
|
||||||
|
arg.argnum = 1;
|
||||||
|
arg.last_regno = ILLEGAL_REG;
|
||||||
|
arg.dest_regno = ILLEGAL_REG;
|
||||||
|
arg.soft_match = more_alts;
|
||||||
|
relax_char = 0;
|
||||||
for (args = insn->args; 1; ++args)
|
for (args = insn->args; 1; ++args)
|
||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
if (*s == ' ')
|
SKIP_SPACE_TABS (s);
|
||||||
++s;
|
if (*s == 0)
|
||||||
|
|
||||||
/* In this switch statement we call break if we did not find
|
|
||||||
a match, continue if we did find a match, or return if we
|
|
||||||
are done. */
|
|
||||||
|
|
||||||
c = *args;
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case '\0':
|
|
||||||
if (*s == '\0')
|
|
||||||
{
|
{
|
||||||
offsetT value;
|
offsetT value;
|
||||||
|
|
||||||
/* Stuff the immediate value in now, if we can. */
|
/* Handle unary instructions in which only one operand is given.
|
||||||
|
The source is then the same as the destination. */
|
||||||
|
if (arg.opnum == 1 && *args == ',')
|
||||||
|
switch (args[1])
|
||||||
|
{
|
||||||
|
case 'v':
|
||||||
|
case 'w':
|
||||||
|
arg.argnum = 1;
|
||||||
|
s = argsstart;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fail the match if there were too few operands. */
|
||||||
|
if (*args)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Successful match. Stuff the immediate value in now, if
|
||||||
|
we can. */
|
||||||
if (insn->pinfo == INSN_MACRO)
|
if (insn->pinfo == INSN_MACRO)
|
||||||
{
|
{
|
||||||
gas_assert (relax_char == 0);
|
gas_assert (relax_char == 0);
|
||||||
@ -12200,246 +12422,42 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
|
|||||||
else if (relax_char)
|
else if (relax_char)
|
||||||
*offset_reloc = (int) BFD_RELOC_UNUSED + relax_char;
|
*offset_reloc = (int) BFD_RELOC_UNUSED + relax_char;
|
||||||
|
|
||||||
|
check_completed_insn (&arg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fail the match if the line has too many operands. */
|
||||||
|
if (*args == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ',':
|
/* Handle characters that need to match exactly. */
|
||||||
if (*s++ == c)
|
if (*args == '(' || *args == ')' || *args == ',')
|
||||||
continue;
|
|
||||||
s--;
|
|
||||||
switch (*++args)
|
|
||||||
{
|
{
|
||||||
case 'v':
|
if (*s != *args)
|
||||||
MIPS16_INSERT_OPERAND (RX, *ip, lastregno);
|
|
||||||
continue;
|
|
||||||
case 'w':
|
|
||||||
MIPS16_INSERT_OPERAND (RY, *ip, lastregno);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
if (*s == ',')
|
||||||
case '(':
|
arg.argnum += 1;
|
||||||
case ')':
|
|
||||||
if (*s++ == c)
|
|
||||||
continue;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'v':
|
|
||||||
case 'w':
|
|
||||||
if (s[0] != '$')
|
|
||||||
{
|
|
||||||
if (c == 'v')
|
|
||||||
MIPS16_INSERT_OPERAND (RX, *ip, lastregno);
|
|
||||||
else
|
|
||||||
MIPS16_INSERT_OPERAND (RY, *ip, lastregno);
|
|
||||||
++args;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* Fall through. */
|
|
||||||
case 'x':
|
|
||||||
case 'y':
|
|
||||||
case 'z':
|
|
||||||
case 'Z':
|
|
||||||
case '0':
|
|
||||||
case 'S':
|
|
||||||
case 'R':
|
|
||||||
case 'X':
|
|
||||||
case 'Y':
|
|
||||||
s_reset = s;
|
|
||||||
if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no))
|
|
||||||
{
|
|
||||||
if (c == 'v' || c == 'w')
|
|
||||||
{
|
|
||||||
if (c == 'v')
|
|
||||||
MIPS16_INSERT_OPERAND (RX, *ip, lastregno);
|
|
||||||
else
|
|
||||||
MIPS16_INSERT_OPERAND (RY, *ip, lastregno);
|
|
||||||
++args;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*s == ' ')
|
|
||||||
++s;
|
++s;
|
||||||
if (args[1] != *s)
|
continue;
|
||||||
{
|
|
||||||
if (c == 'v' || c == 'w')
|
|
||||||
{
|
|
||||||
regno = mips16_to_32_reg_map[lastregno];
|
|
||||||
s = s_reset;
|
|
||||||
++args;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
arg.opnum += 1;
|
||||||
|
arg.optional_reg = FALSE;
|
||||||
|
c = *args;
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
case 'x':
|
|
||||||
case 'y':
|
|
||||||
case 'z':
|
|
||||||
case 'v':
|
case 'v':
|
||||||
case 'w':
|
case 'w':
|
||||||
case 'Z':
|
arg.optional_reg = (args[1] == ',');
|
||||||
regno = mips32_to_16_reg_map[regno];
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '0':
|
|
||||||
if (regno != 0)
|
|
||||||
regno = ILLEGAL_REG;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'S':
|
|
||||||
if (regno != SP)
|
|
||||||
regno = ILLEGAL_REG;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'R':
|
|
||||||
if (regno != RA)
|
|
||||||
regno = ILLEGAL_REG;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'X':
|
|
||||||
case 'Y':
|
|
||||||
if (regno == AT && mips_opts.at)
|
|
||||||
{
|
|
||||||
if (mips_opts.at == ATREG)
|
|
||||||
as_warn (_("used $at without \".set noat\""));
|
|
||||||
else
|
|
||||||
as_warn (_("used $%u with \".set at=$%u\""),
|
|
||||||
regno, mips_opts.at);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
abort ();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (regno == ILLEGAL_REG)
|
|
||||||
break;
|
|
||||||
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case 'x':
|
|
||||||
case 'v':
|
|
||||||
MIPS16_INSERT_OPERAND (RX, *ip, regno);
|
|
||||||
break;
|
|
||||||
case 'y':
|
|
||||||
case 'w':
|
|
||||||
MIPS16_INSERT_OPERAND (RY, *ip, regno);
|
|
||||||
break;
|
|
||||||
case 'z':
|
|
||||||
MIPS16_INSERT_OPERAND (RZ, *ip, regno);
|
|
||||||
break;
|
|
||||||
case 'Z':
|
|
||||||
MIPS16_INSERT_OPERAND (MOVE32Z, *ip, regno);
|
|
||||||
case '0':
|
|
||||||
case 'S':
|
|
||||||
case 'R':
|
|
||||||
break;
|
|
||||||
case 'X':
|
|
||||||
MIPS16_INSERT_OPERAND (REGR32, *ip, regno);
|
|
||||||
break;
|
|
||||||
case 'Y':
|
|
||||||
regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3);
|
|
||||||
MIPS16_INSERT_OPERAND (REG32R, *ip, regno);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
abort ();
|
|
||||||
}
|
|
||||||
|
|
||||||
lastregno = regno;
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case 'P':
|
|
||||||
if (strncmp (s, "$pc", 3) == 0)
|
|
||||||
{
|
|
||||||
s += 3;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '5':
|
|
||||||
case 'H':
|
|
||||||
case 'W':
|
|
||||||
case 'D':
|
|
||||||
case 'j':
|
|
||||||
case 'V':
|
|
||||||
case 'C':
|
|
||||||
case 'U':
|
|
||||||
case 'k':
|
|
||||||
case 'K':
|
|
||||||
i = my_getSmallExpression (&offset_expr, offset_reloc, s);
|
|
||||||
if (i > 0)
|
|
||||||
{
|
|
||||||
relax_char = c;
|
|
||||||
s = expr_end;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
*offset_reloc = BFD_RELOC_UNUSED;
|
|
||||||
/* Fall through. */
|
|
||||||
case '<':
|
|
||||||
case '>':
|
|
||||||
case '[':
|
|
||||||
case ']':
|
|
||||||
case '4':
|
|
||||||
case '8':
|
|
||||||
my_getExpression (&offset_expr, s);
|
|
||||||
if (offset_expr.X_op == O_register)
|
|
||||||
{
|
|
||||||
/* What we thought was an expression turned out to
|
|
||||||
be a register. */
|
|
||||||
|
|
||||||
if (s[0] == '(' && args[1] == '(')
|
|
||||||
{
|
|
||||||
/* It looks like the expression was omitted
|
|
||||||
before a register indirection, which means
|
|
||||||
that the expression is implicitly zero. We
|
|
||||||
still set up offset_expr, so that we handle
|
|
||||||
explicit extensions correctly. */
|
|
||||||
offset_expr.X_op = O_constant;
|
|
||||||
offset_expr.X_add_number = 0;
|
|
||||||
relax_char = c;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We need to relax this instruction. */
|
|
||||||
relax_char = c;
|
|
||||||
s = expr_end;
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case 'p':
|
case 'p':
|
||||||
case 'q':
|
case 'q':
|
||||||
case 'A':
|
case 'A':
|
||||||
case 'B':
|
case 'B':
|
||||||
case 'E':
|
case 'E':
|
||||||
/* We use offset_reloc rather than imm_reloc for the PC
|
|
||||||
relative operands. This lets macros with both
|
|
||||||
immediate and address operands work correctly. */
|
|
||||||
my_getExpression (&offset_expr, s);
|
|
||||||
|
|
||||||
if (offset_expr.X_op == O_register)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* We need to relax this instruction. */
|
|
||||||
relax_char = c;
|
relax_char = c;
|
||||||
s = expr_end;
|
break;
|
||||||
continue;
|
|
||||||
|
|
||||||
case '6': /* break code */
|
|
||||||
my_getExpression (&imm_expr, s);
|
|
||||||
check_absolute_expr (ip, &imm_expr);
|
|
||||||
if ((unsigned long) imm_expr.X_add_number > 63)
|
|
||||||
as_warn (_("Invalid value for `%s' (%lu)"),
|
|
||||||
ip->insn_mo->name,
|
|
||||||
(unsigned long) imm_expr.X_add_number);
|
|
||||||
MIPS16_INSERT_OPERAND (IMM6, *ip, imm_expr.X_add_number);
|
|
||||||
imm_expr.X_op = O_absent;
|
|
||||||
s = expr_end;
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case 'I':
|
case 'I':
|
||||||
my_getExpression (&imm_expr, s);
|
my_getExpression (&imm_expr, s);
|
||||||
@ -12451,268 +12469,73 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
|
|||||||
s = expr_end;
|
s = expr_end;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case 'a': /* 26 bit address */
|
case 'a':
|
||||||
case 'i':
|
case 'i':
|
||||||
my_getExpression (&offset_expr, s);
|
|
||||||
s = expr_end;
|
|
||||||
*offset_reloc = BFD_RELOC_MIPS16_JMP;
|
*offset_reloc = BFD_RELOC_MIPS16_JMP;
|
||||||
ip->insn_opcode <<= 16;
|
ip->insn_opcode <<= 16;
|
||||||
continue;
|
|
||||||
|
|
||||||
case 'l': /* register list for entry macro */
|
|
||||||
case 'L': /* register list for exit macro */
|
|
||||||
{
|
|
||||||
int mask;
|
|
||||||
|
|
||||||
if (c == 'l')
|
|
||||||
mask = 0;
|
|
||||||
else
|
|
||||||
mask = 7 << 3;
|
|
||||||
while (*s != '\0')
|
|
||||||
{
|
|
||||||
unsigned int freg, reg1, reg2;
|
|
||||||
|
|
||||||
while (*s == ' ' || *s == ',')
|
|
||||||
++s;
|
|
||||||
if (reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®1))
|
|
||||||
freg = 0;
|
|
||||||
else if (reg_lookup (&s, RTYPE_FPU, ®1))
|
|
||||||
freg = 1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
as_bad (_("can't parse register list"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (*s == ' ')
|
|
||||||
++s;
|
|
||||||
if (*s != '-')
|
|
||||||
reg2 = reg1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++s;
|
|
||||||
if (!reg_lookup (&s, freg ? RTYPE_FPU
|
|
||||||
: (RTYPE_GP | RTYPE_NUM), ®2))
|
|
||||||
{
|
|
||||||
as_bad (_("invalid register list"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (freg && reg1 == 0 && reg2 == 0 && c == 'L')
|
|
||||||
{
|
|
||||||
mask &= ~ (7 << 3);
|
|
||||||
mask |= 5 << 3;
|
|
||||||
}
|
|
||||||
else if (freg && reg1 == 0 && reg2 == 1 && c == 'L')
|
|
||||||
{
|
|
||||||
mask &= ~ (7 << 3);
|
|
||||||
mask |= 6 << 3;
|
|
||||||
}
|
|
||||||
else if (reg1 == 4 && reg2 >= 4 && reg2 <= 7 && c != 'L')
|
|
||||||
mask |= (reg2 - 3) << 3;
|
|
||||||
else if (reg1 == 16 && reg2 >= 16 && reg2 <= 17)
|
|
||||||
mask |= (reg2 - 15) << 1;
|
|
||||||
else if (reg1 == RA && reg2 == RA)
|
|
||||||
mask |= 1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
as_bad (_("invalid register list"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* The mask is filled in in the opcode table for the
|
|
||||||
benefit of the disassembler. We remove it before
|
|
||||||
applying the actual mask. */
|
|
||||||
ip->insn_opcode &= ~ ((7 << 3) << MIPS16OP_SH_IMM6);
|
|
||||||
ip->insn_opcode |= mask << MIPS16OP_SH_IMM6;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case 'm': /* Register list for save insn. */
|
|
||||||
case 'M': /* Register list for restore insn. */
|
|
||||||
{
|
|
||||||
int opcode = ip->insn_opcode;
|
|
||||||
int framesz = 0, seen_framesz = 0;
|
|
||||||
int nargs = 0, statics = 0, sregs = 0;
|
|
||||||
|
|
||||||
while (*s != '\0')
|
|
||||||
{
|
|
||||||
unsigned int reg1, reg2;
|
|
||||||
|
|
||||||
SKIP_SPACE_TABS (s);
|
|
||||||
while (*s == ',')
|
|
||||||
++s;
|
|
||||||
SKIP_SPACE_TABS (s);
|
|
||||||
|
|
||||||
my_getExpression (&imm_expr, s);
|
|
||||||
if (imm_expr.X_op == O_constant)
|
|
||||||
{
|
|
||||||
/* Handle the frame size. */
|
|
||||||
if (seen_framesz)
|
|
||||||
{
|
|
||||||
as_bad (_("more than one frame size in list"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
seen_framesz = 1;
|
|
||||||
framesz = imm_expr.X_add_number;
|
|
||||||
imm_expr.X_op = O_absent;
|
|
||||||
s = expr_end;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®1))
|
|
||||||
{
|
|
||||||
as_bad (_("can't parse register list"));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (*s == ' ')
|
operand = decode_mips16_operand (c, FALSE);
|
||||||
++s;
|
if (!operand)
|
||||||
|
|
||||||
if (*s != '-')
|
|
||||||
reg2 = reg1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++s;
|
|
||||||
if (! reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®2)
|
|
||||||
|| reg2 < reg1)
|
|
||||||
{
|
|
||||||
as_bad (_("can't parse register list"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (reg1 <= reg2)
|
|
||||||
{
|
|
||||||
if (reg1 >= 4 && reg1 <= 7)
|
|
||||||
{
|
|
||||||
if (!seen_framesz)
|
|
||||||
/* args $a0-$a3 */
|
|
||||||
nargs |= 1 << (reg1 - 4);
|
|
||||||
else
|
|
||||||
/* statics $a0-$a3 */
|
|
||||||
statics |= 1 << (reg1 - 4);
|
|
||||||
}
|
|
||||||
else if ((reg1 >= 16 && reg1 <= 23) || reg1 == 30)
|
|
||||||
{
|
|
||||||
/* $s0-$s8 */
|
|
||||||
sregs |= 1 << ((reg1 == 30) ? 8 : (reg1 - 16));
|
|
||||||
}
|
|
||||||
else if (reg1 == 31)
|
|
||||||
{
|
|
||||||
/* Add $ra to insn. */
|
|
||||||
opcode |= 0x40;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
as_bad (_("unexpected register in list"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (++reg1 == 24)
|
|
||||||
reg1 = 30;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Encode args/statics combination. */
|
|
||||||
if (nargs & statics)
|
|
||||||
as_bad (_("arg/static registers overlap"));
|
|
||||||
else if (nargs == 0xf)
|
|
||||||
/* All $a0-$a3 are args. */
|
|
||||||
opcode |= MIPS16_ALL_ARGS << 16;
|
|
||||||
else if (statics == 0xf)
|
|
||||||
/* All $a0-$a3 are statics. */
|
|
||||||
opcode |= MIPS16_ALL_STATICS << 16;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int narg = 0, nstat = 0;
|
|
||||||
|
|
||||||
/* Count arg registers. */
|
|
||||||
while (nargs & 0x1)
|
|
||||||
{
|
|
||||||
nargs >>= 1;
|
|
||||||
narg++;
|
|
||||||
}
|
|
||||||
if (nargs != 0)
|
|
||||||
as_bad (_("invalid arg register list"));
|
|
||||||
|
|
||||||
/* Count static registers. */
|
|
||||||
while (statics & 0x8)
|
|
||||||
{
|
|
||||||
statics = (statics << 1) & 0xf;
|
|
||||||
nstat++;
|
|
||||||
}
|
|
||||||
if (statics != 0)
|
|
||||||
as_bad (_("invalid static register list"));
|
|
||||||
|
|
||||||
/* Encode args/statics. */
|
|
||||||
opcode |= ((narg << 2) | nstat) << 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Encode $s0/$s1. */
|
|
||||||
if (sregs & (1 << 0)) /* $s0 */
|
|
||||||
opcode |= 0x20;
|
|
||||||
if (sregs & (1 << 1)) /* $s1 */
|
|
||||||
opcode |= 0x10;
|
|
||||||
sregs >>= 2;
|
|
||||||
|
|
||||||
if (sregs != 0)
|
|
||||||
{
|
|
||||||
/* Count regs $s2-$s8. */
|
|
||||||
int nsreg = 0;
|
|
||||||
while (sregs & 1)
|
|
||||||
{
|
|
||||||
sregs >>= 1;
|
|
||||||
nsreg++;
|
|
||||||
}
|
|
||||||
if (sregs != 0)
|
|
||||||
as_bad (_("invalid static register list"));
|
|
||||||
/* Encode $s2-$s8. */
|
|
||||||
opcode |= nsreg << 24;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Encode frame size. */
|
|
||||||
if (!seen_framesz)
|
|
||||||
as_bad (_("missing frame size"));
|
|
||||||
else if ((framesz & 7) != 0 || framesz < 0
|
|
||||||
|| framesz > 0xff * 8)
|
|
||||||
as_bad (_("invalid frame size"));
|
|
||||||
else if (framesz != 128 || (opcode >> 16) != 0)
|
|
||||||
{
|
|
||||||
framesz /= 8;
|
|
||||||
opcode |= (((framesz & 0xf0) << 16)
|
|
||||||
| (framesz & 0x0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Finally build the instruction. */
|
|
||||||
if ((opcode >> 16) != 0 || framesz == 0)
|
|
||||||
opcode |= MIPS16_EXTEND;
|
|
||||||
ip->insn_opcode = opcode;
|
|
||||||
}
|
|
||||||
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:
|
|
||||||
abort ();
|
abort ();
|
||||||
|
|
||||||
|
/* '6' is a special case. It is used for BREAK and SDBBP,
|
||||||
|
whose operands are only meaningful to the software that decodes
|
||||||
|
them. This means that there is no architectural reason why
|
||||||
|
they cannot be prefixed by EXTEND, but in practice,
|
||||||
|
exception handlers will only look at the instruction
|
||||||
|
itself. We therefore allow '6' to be extended when
|
||||||
|
disassembling but not when assembling. */
|
||||||
|
if (operand->type != OP_PCREL && c != '6')
|
||||||
|
{
|
||||||
|
ext_operand = decode_mips16_operand (c, TRUE);
|
||||||
|
if (operand != ext_operand)
|
||||||
|
{
|
||||||
|
/* Parse the expression, allowing relocation operators. */
|
||||||
|
i = my_getSmallExpression (&offset_expr, offset_reloc, s);
|
||||||
|
s = expr_end;
|
||||||
|
|
||||||
|
if (offset_expr.X_op == O_register)
|
||||||
|
{
|
||||||
|
/* Handle elided offsets, which are equivalent to 0. */
|
||||||
|
if (*s == '(')
|
||||||
|
{
|
||||||
|
offset_expr.X_op = O_constant;
|
||||||
|
offset_expr.X_add_number = 0;
|
||||||
|
relax_char = c;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
/* Fail the match. */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
/* '8' is used for SLTI(U) and has traditionally not
|
||||||
|
been allowed to take relocation operators. */
|
||||||
|
if (i > 0 && (ext_operand->size != 16 || c == '8'))
|
||||||
|
break;
|
||||||
|
relax_char = c;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s = match_operand (&arg, operand, s);
|
||||||
|
if (!s && arg.optional_reg)
|
||||||
|
{
|
||||||
|
/* Assume that the register has been elided and is the
|
||||||
|
same as the first operand. */
|
||||||
|
arg.optional_reg = FALSE;
|
||||||
|
arg.argnum = 1;
|
||||||
|
s = argsstart;
|
||||||
|
SKIP_SPACE_TABS (s);
|
||||||
|
s = match_operand (&arg, operand, s);
|
||||||
|
}
|
||||||
|
if (!s)
|
||||||
|
break;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* Args don't match. */
|
/* Args don't match. */
|
||||||
if (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes] &&
|
if (more_alts)
|
||||||
strcmp (insn->name, insn[1].name) == 0)
|
|
||||||
{
|
{
|
||||||
++insn;
|
++insn;
|
||||||
s = argsstart;
|
s = argsstart;
|
||||||
|
Reference in New Issue
Block a user