diff options
author | John Darrington <john@darrington.wattle.id.au> | 2019-02-01 17:42:54 +0100 |
---|---|---|
committer | John Darrington <john@darrington.wattle.id.au> | 2019-02-01 18:08:08 +0100 |
commit | c6f14c0d2ca4d68308f967c446277789047a980a (patch) | |
tree | c07f41f2cd76f9b11458e80f73568fca72a06b2c /gas/config | |
parent | 91bae99160e9d81cfe340b937f9613d43dc90293 (diff) | |
download | gdb-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.c | 114 |
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; |