aboutsummaryrefslogtreecommitdiff
path: root/gas/config
diff options
context:
space:
mode:
authorJohn Darrington <john@darrington.wattle.id.au>2019-02-01 17:42:54 +0100
committerJohn Darrington <john@darrington.wattle.id.au>2019-02-01 18:08:08 +0100
commitc6f14c0d2ca4d68308f967c446277789047a980a (patch)
treec07f41f2cd76f9b11458e80f73568fca72a06b2c /gas/config
parent91bae99160e9d81cfe340b937f9613d43dc90293 (diff)
downloadgdb-c6f14c0d2ca4d68308f967c446277789047a980a.zip
gdb-c6f14c0d2ca4d68308f967c446277789047a980a.tar.gz
gdb-c6f14c0d2ca4d68308f967c446277789047a980a.tar.bz2
S12Z: GAS: Allow #_symbol operands as mov source
mov.l, mov.p and mov.w (but not mov.b) when called with an immediate source operand should be accepted a relocatable expression. This change makes that possible. gas/ * config/tc-s12z.c (lex_imm): Add new argument exp_o. (emit_reloc): New function. (md_apply_fix): [BFD_RELOC_S12Z_OPR] Recognise that it can be either 2 bytes or 3 bytes long. * testsuite/gas/s12z/mov-imm-reloc.d: New file. * testsuite/gas/s12z/mov-imm-reloc.s: New file. * testsuite/gas/s12z/s12z.exp: Add them.
Diffstat (limited to 'gas/config')
-rw-r--r--gas/config/tc-s12z.c114
1 files changed, 79 insertions, 35 deletions
diff --git a/gas/config/tc-s12z.c b/gas/config/tc-s12z.c
index 8b56b68..bc1bb5c 100644
--- a/gas/config/tc-s12z.c
+++ b/gas/config/tc-s12z.c
@@ -227,9 +227,12 @@ lex_expression (expressionS *exp)
return 0;
}
-/* immediate operand */
+/* Immediate operand.
+ If EXP_O is non-null, then a symbolic expression is permitted,
+ in which case, EXP_O will be populated with the parsed expression.
+ */
static int
-lex_imm (long *v)
+lex_imm (long *v, expressionS *exp_o)
{
char *ilp = input_line_pointer;
@@ -242,7 +245,12 @@ lex_imm (long *v)
goto fail;
if (exp.X_op != O_constant)
- goto fail;
+ {
+ if (!exp_o)
+ as_bad (_("A non-constant expression is not permitted here"));
+ else
+ *exp_o = exp;
+ }
*v = exp.X_add_number;
return 1;
@@ -258,7 +266,7 @@ static int
lex_imm_e4 (long *val)
{
char *ilp = input_line_pointer;
- if ((lex_imm (val)))
+ if ((lex_imm (val, NULL)))
{
if ((*val == -1) || (*val > 0 && *val <= 15))
{
@@ -731,26 +739,35 @@ no_operands (const struct instruction *insn)
return 1;
}
-/* Emit the code for an OPR address mode operand */
-static char *
-emit_opr (char *f, const uint8_t *buffer, int n_bytes, expressionS *exp)
+
+static void
+emit_reloc (expressionS *exp, char *f, int size, enum bfd_reloc_code_real reloc)
{
- int i;
- number_to_chars_bigendian (f++, buffer[0], 1);
if (exp->X_op != O_absent && exp->X_op != O_constant)
{
fixS *fix = fix_new_exp (frag_now,
f - frag_now->fr_literal,
- 3,
+ size,
exp,
FALSE,
- BFD_RELOC_S12Z_OPR);
+ reloc);
/* Some third party tools seem to use the lower bits
- of this addend for flags. They don't get added
- to the final location. The purpose of these flags
- is not known. We simply set it to zero. */
+ of this addend for flags. They don't get added
+ to the final location. The purpose of these flags
+ is not known. We simply set it to zero. */
fix->fx_addnumber = 0x00;
}
+}
+
+/* Emit the code for an OPR address mode operand */
+static char *
+emit_opr (char *f, const uint8_t *buffer, int n_bytes, expressionS *exp)
+{
+ int i;
+ number_to_chars_bigendian (f++, buffer[0], 1);
+
+ emit_reloc (exp, f, 3, BFD_RELOC_S12Z_OPR);
+
for (i = 1; i < n_bytes; ++i)
number_to_chars_bigendian (f++, buffer[i], 1);
@@ -1037,7 +1054,7 @@ mul_reg_reg_imm (const struct instruction *insn)
goto fail;
long imm;
- if (!lex_imm (&imm))
+ if (!lex_imm (&imm, NULL))
goto fail;
@@ -1349,7 +1366,7 @@ static int
imm8 (const struct instruction *insn)
{
long imm;
- if (! lex_imm (&imm))
+ if (! lex_imm (&imm, NULL))
return 0;
if (imm > 127 || imm < -128)
{
@@ -1374,7 +1391,7 @@ reg_imm (const struct instruction *insn, int allowed_reg)
if (!lex_force_match (','))
goto fail;
long imm;
- if (! lex_imm (&imm))
+ if (! lex_imm (&imm, NULL))
goto fail;
short size = registers[reg].bytes;
@@ -1417,7 +1434,7 @@ static int
trap_imm (const struct instruction *insn ATTRIBUTE_UNUSED)
{
long imm = -1;
- if (! lex_imm (&imm))
+ if (! lex_imm (&imm, NULL))
goto fail;
if (imm < 0x92 || imm > 0xFF ||
@@ -1619,7 +1636,19 @@ imm_opr (const struct instruction *insn)
{
char *ilp = input_line_pointer;
long imm;
- if (!lex_imm (&imm))
+ expressionS exp0;
+ int size = size_from_suffix (insn, 0);
+ exp0.X_op = O_absent;
+
+ /* Note: The ternary expression below means that "MOV.x #symbol,
+ mem-expr" is accepted when x is a member of {'w', 'p', 'l'} but
+ not when it is 'b'.
+ The Freescale assembler accepts "MOV.b #symbol, mem-expr" but
+ produces obviously incorrect code. Since such an instruction
+ would require an 8-bit reloc (which we don't have) and some
+ non-optimal kludges in the OPR encoding, it seems sensible that
+ such instructions should be rejected. */
+ if (!lex_imm (&imm, size > 1 ? &exp0 : NULL))
goto fail;
if (!lex_match (','))
@@ -1627,19 +1656,20 @@ imm_opr (const struct instruction *insn)
uint8_t buffer[4];
int n_bytes;
- expressionS exp;
- if (!lex_opr (buffer, &n_bytes, &exp, false))
+ expressionS exp1;
+ if (!lex_opr (buffer, &n_bytes, &exp1, false))
goto fail;
- int size = size_from_suffix (insn, 0);
char *f = s12z_new_insn (1 + n_bytes + size);
number_to_chars_bigendian (f++, insn->opc, 1);
+ emit_reloc (&exp0, f, size, size == 4 ? BFD_RELOC_32 : BFD_RELOC_S12Z_OPR);
+
int i;
for (i = 0; i < size; ++i)
number_to_chars_bigendian (f++, imm >> (CHAR_BIT * (size - i - 1)), 1);
- emit_opr (f, buffer, n_bytes, &exp);
+ emit_opr (f, buffer, n_bytes, &exp1);
return 1;
@@ -1771,7 +1801,7 @@ lex_shift_reg_imm1 (const struct instruction *insn, short type, short dir)
goto fail;
long imm = -1;
- if (!lex_imm (&imm))
+ if (!lex_imm (&imm, NULL))
goto fail;
if (imm != 1 && imm != 2)
@@ -1847,7 +1877,7 @@ lex_shift_reg (const struct instruction *insn, short type, short dir)
return 1;
}
- else if (lex_imm (&imm))
+ else if (lex_imm (&imm, NULL))
{
if (imm < 0 || imm > 31)
{
@@ -1942,7 +1972,7 @@ shift_two_operand (const struct instruction *insn)
goto fail;
long imm = -1;
- if (!lex_imm (&imm))
+ if (!lex_imm (&imm, NULL))
goto fail;
if (imm != 1 && imm != 2)
@@ -1999,7 +2029,7 @@ shift_opr_imm (const struct instruction *insn)
expressionS exp2;
long imm;
bool immediate = false;
- if (lex_imm (&imm))
+ if (lex_imm (&imm, NULL))
{
immediate = true;
}
@@ -2087,7 +2117,7 @@ bm_regd_imm (const struct instruction *insn)
goto fail;
long imm;
- if (!lex_imm (&imm))
+ if (!lex_imm (&imm, NULL))
goto fail;
@@ -2162,7 +2192,7 @@ bm_opr_imm (const struct instruction *insn)
long imm;
- if (!lex_imm (&imm))
+ if (!lex_imm (&imm, NULL))
goto fail;
int size = size_from_suffix (insn, 0);
@@ -2255,7 +2285,7 @@ bf_reg_opr_imm (const struct instruction *insn, short ie)
goto fail;
long width;
- if (!lex_imm (&width))
+ if (!lex_imm (&width, NULL))
goto fail;
if (width < 0 || width > 31)
@@ -2324,7 +2354,7 @@ bf_opr_reg_imm (const struct instruction *insn, short ie)
goto fail;
long width;
- if (!lex_imm (&width))
+ if (!lex_imm (&width, NULL))
goto fail;
if (width < 0 || width > 31)
@@ -2392,7 +2422,7 @@ bf_reg_reg_imm (const struct instruction *insn, short ie)
goto fail;
long width;
- if (!lex_imm (&width))
+ if (!lex_imm (&width, NULL))
goto fail;
if (width < 0 || width > 31)
@@ -2909,7 +2939,7 @@ test_br_opr_imm_rel (const struct instruction *insn)
goto fail;
long imm;
- if (!lex_imm (&imm))
+ if (!lex_imm (&imm, NULL))
goto fail;
if (imm < 0 || imm > 31)
@@ -2962,7 +2992,7 @@ test_br_reg_imm_rel (const struct instruction *insn)
goto fail;
long imm;
- if (!lex_imm (&imm))
+ if (!lex_imm (&imm, NULL))
goto fail;
if (imm < 0 || imm > 31)
@@ -3846,9 +3876,23 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
bfd_putb16 ((bfd_vma) value, (unsigned char *) where);
break;
case BFD_RELOC_24:
- case BFD_RELOC_S12Z_OPR:
bfd_putb24 ((bfd_vma) value, (unsigned char *) where);
break;
+ case BFD_RELOC_S12Z_OPR:
+ {
+ switch (fixP->fx_size)
+ {
+ case 3:
+ bfd_putb24 ((bfd_vma) value, (unsigned char *) where);
+ break;
+ case 2:
+ bfd_putb16 ((bfd_vma) value, (unsigned char *) where);
+ break;
+ default:
+ abort ();
+ }
+ }
+ break;
case BFD_RELOC_32:
bfd_putb32 ((bfd_vma) value, (unsigned char *) where);
break;