aboutsummaryrefslogtreecommitdiff
path: root/gas/config/tc-ft32.c
diff options
context:
space:
mode:
authorJames Bowman <james.bowman@ftdichip.com>2017-11-01 15:33:24 +0000
committerNick Clifton <nickc@redhat.com>2017-11-01 15:33:24 +0000
commit81b42bcab16f4462eb4c91db2cf09cd76e17402e (patch)
treef46b9942608fd108bf16b1b10eea645c82e7142e /gas/config/tc-ft32.c
parenta67d66eb97e7613a38ffe6622d837303b3ecd31d (diff)
downloadgdb-81b42bcab16f4462eb4c91db2cf09cd76e17402e.zip
gdb-81b42bcab16f4462eb4c91db2cf09cd76e17402e.tar.gz
gdb-81b42bcab16f4462eb4c91db2cf09cd76e17402e.tar.bz2
FT32B is a new FT32 family member. It has a code compression scheme, which requires the use of linker relaxations. The change is quite large, so submission is in several parts.
Part 2 adds a relaxation pass, which actually implements the code compression scheme. bfd * archures.c: Add bfd_mach_ft32b. * cpu-ft32.c: Add arch_info_struct. * elf32-ft32.c: Add R_FT32_RELAX, SC0, SC1, DIFF32. (ft32_elf_relocate_section): Add clauses for R_FT32_SC0, SC1, DIFF32. (ft32_reloc_shortable, elf32_ft32_is_diff_reloc, elf32_ft32_adjust_diff_reloc_value, elf32_ft32_adjust_reloc_if_spans_insn, elf32_ft32_relax_delete_bytes, elf32_ft32_relax_is_branch_target, ft32_elf_relax_section): New function. * reloc.c: Add BFD_RELOC_FT32_RELAX, SC0, SC1, DIFF32. * bfd-in2.h: Regenerate. * libbfd.h: Regenerate. gas * config/tc-ft32.c (md_assemble): add relaxation reloc BFD_RELOC_FT32_RELAX. (md_longopts): Add "norelax" and "no-relax". (md_apply_fix): Add reloc BFD_RELOC_FT32_DIFF32. (relaxable_section, ft32_validate_fix_sub, ft32_force_relocation, ft32_allow_local_subtract): New function. * config/tc-ft32.h: remove unused MD_PCREL_FROM_SECTION. * testsuite/gas/ft32/insnsc.s: New test exercising all FT32B shortcodes. include * elf/ft32.h: Add R_FT32_RELAX, SC0, SC1, DIFF32.
Diffstat (limited to 'gas/config/tc-ft32.c')
-rw-r--r--gas/config/tc-ft32.c387
1 files changed, 280 insertions, 107 deletions
diff --git a/gas/config/tc-ft32.c b/gas/config/tc-ft32.c
index 2878e14..f401547 100644
--- a/gas/config/tc-ft32.c
+++ b/gas/config/tc-ft32.c
@@ -26,6 +26,9 @@
extern const ft32_opc_info_t ft32_opc_info[128];
+/* See md_parse_option() for meanings of these options. */
+static char norelax; /* True if -norelax switch seen. */
+
const char comment_chars[] = "#";
const char line_separator_chars[] = ";";
const char line_comment_chars[] = "#";
@@ -58,6 +61,8 @@ md_begin (void)
hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
bfd_set_arch_mach (stdoutput, TARGET_ARCH, 0);
+ if (!norelax)
+ linkrelax = 1;
}
/* Parse an expression and then restore the input line pointer. */
@@ -78,10 +83,13 @@ static int
parse_condition (char **ptr)
{
char *s = *ptr;
- static const struct {
+ static const struct
+ {
const char *name;
int bits;
- } ccs[] = {
+ }
+ ccs[] =
+ {
{ "gt," , (2 << FT32_FLD_CR_BIT) | (5 << FT32_FLD_CB_BIT) | (1 << FT32_FLD_CV_BIT)},
{ "gte," , (2 << FT32_FLD_CR_BIT) | (4 << FT32_FLD_CB_BIT) | (1 << FT32_FLD_CV_BIT)},
{ "lt," , (2 << FT32_FLD_CR_BIT) | (4 << FT32_FLD_CB_BIT) | (0 << FT32_FLD_CV_BIT)},
@@ -191,18 +199,17 @@ md_assemble (char *str)
{
char *op_start;
char *op_end;
-
ft32_opc_info_t *opcode;
char *output;
int idx = 0;
char pend;
-
int nlen = 0;
-
unsigned int b;
int f;
-
expressionS arg;
+ bfd_boolean fixed = FALSE;
+ unsigned int sc;
+ bfd_boolean can_sc;
/* Drop leading whitespace. */
while (*str == ' ')
@@ -211,7 +218,10 @@ md_assemble (char *str)
/* Find the op code end. */
op_start = str;
for (op_end = str;
- *op_end && !is_end_of_line[*op_end & 0xff] && *op_end != ' ' && *op_end != '.';
+ *op_end
+ && !is_end_of_line[*op_end & 0xff]
+ && *op_end != ' '
+ && *op_end != '.';
op_end++)
nlen++;
@@ -236,6 +246,7 @@ md_assemble (char *str)
if (opcode->dw)
{
int dw;
+
if (*op_end == '.')
{
switch (op_end[1])
@@ -270,106 +281,115 @@ md_assemble (char *str)
while (f)
{
int lobit = f & -f;
+
if (f & lobit)
{
switch (lobit)
- {
- case FT32_FLD_CBCRCV:
- b |= parse_condition( &op_end);
- break;
- case FT32_FLD_CB:
- b |= parse_decimal (&op_end) << FT32_FLD_CB_BIT;
- break;
- case FT32_FLD_R_D:
- b |= parse_register_operand (&op_end) << FT32_FLD_R_D_BIT;
- break;
- case FT32_FLD_CR:
- b |= (parse_register_operand (&op_end) - 28) << FT32_FLD_CR_BIT;
- break;
- case FT32_FLD_CV:
- b |= parse_decimal (&op_end) << FT32_FLD_CV_BIT;
- break;
- case FT32_FLD_R_1:
- b |= parse_register_operand (&op_end) << FT32_FLD_R_1_BIT;
- break;
- case FT32_FLD_RIMM:
- if (*op_end == '$')
- {
- b |= parse_register_operand (&op_end) << FT32_FLD_RIMM_BIT;
- }
- else
- {
- b |= 0x400 << FT32_FLD_RIMM_BIT;
- op_end = parse_exp_save_ilp (op_end, &arg);
- fix_new_exp (frag_now,
- (output - frag_now->fr_literal),
- 2,
- &arg,
- 0,
- BFD_RELOC_FT32_10);
- }
- break;
- case FT32_FLD_R_2:
- b |= parse_register_operand (&op_end) << FT32_FLD_R_2_BIT;
- break;
- case FT32_FLD_K20:
- op_end = parse_exp_save_ilp (op_end, &arg);
- fix_new_exp (frag_now,
- (output - frag_now->fr_literal),
- 3,
- &arg,
- 0,
- BFD_RELOC_FT32_20);
- break;
- case FT32_FLD_PA:
- op_end = parse_exp_save_ilp (op_end, &arg);
- fix_new_exp (frag_now,
- (output - frag_now->fr_literal),
- 3,
- &arg,
- 0,
- BFD_RELOC_FT32_18);
- break;
- case FT32_FLD_AA:
- op_end = parse_exp_save_ilp (op_end, &arg);
- fix_new_exp (frag_now,
- (output - frag_now->fr_literal),
- 3,
- &arg,
- 0,
- BFD_RELOC_FT32_17);
- break;
- case FT32_FLD_K16:
- op_end = parse_exp_save_ilp (op_end, &arg);
- fix_new_exp (frag_now,
- (output - frag_now->fr_literal),
- 2,
- &arg,
- 0,
- BFD_RELOC_16);
- break;
- case FT32_FLD_K15:
- op_end = parse_exp_save_ilp (op_end, &arg);
- if (arg.X_add_number & 0x80)
- arg.X_add_number ^= 0x7f00;
- fix_new_exp (frag_now,
- (output - frag_now->fr_literal),
- 2,
- &arg,
- 0,
- BFD_RELOC_FT32_15);
- break;
- case FT32_FLD_R_D_POST:
- b |= parse_register_operand (&op_end) << FT32_FLD_R_D_BIT;
- break;
- case FT32_FLD_R_1_POST:
- b |= parse_register_operand (&op_end) << FT32_FLD_R_1_BIT;
- break;
- default:
- as_bad (_("internal error in argument parsing"));
- break;
- }
+ {
+ case FT32_FLD_CBCRCV:
+ b |= parse_condition( &op_end);
+ break;
+ case FT32_FLD_CB:
+ b |= parse_decimal (&op_end) << FT32_FLD_CB_BIT;
+ break;
+ case FT32_FLD_R_D:
+ b |= parse_register_operand (&op_end) << FT32_FLD_R_D_BIT;
+ break;
+ case FT32_FLD_CR:
+ b |= (parse_register_operand (&op_end) - 28) << FT32_FLD_CR_BIT;
+ break;
+ case FT32_FLD_CV:
+ b |= parse_decimal (&op_end) << FT32_FLD_CV_BIT;
+ break;
+ case FT32_FLD_R_1:
+ b |= parse_register_operand (&op_end) << FT32_FLD_R_1_BIT;
+ break;
+ case FT32_FLD_RIMM:
+ if (*op_end == '$')
+ {
+ b |= parse_register_operand (&op_end) << FT32_FLD_RIMM_BIT;
+ }
+ else
+ {
+ b |= 0x400 << FT32_FLD_RIMM_BIT;
+ op_end = parse_exp_save_ilp (op_end, &arg);
+ fixed = TRUE;
+ fix_new_exp (frag_now,
+ (output - frag_now->fr_literal),
+ 2,
+ &arg,
+ 0,
+ BFD_RELOC_FT32_10);
+ }
+ break;
+ case FT32_FLD_R_2:
+ b |= parse_register_operand (&op_end) << FT32_FLD_R_2_BIT;
+ break;
+ case FT32_FLD_K20:
+ op_end = parse_exp_save_ilp (op_end, &arg);
+ fixed = TRUE;
+ fix_new_exp (frag_now,
+ (output - frag_now->fr_literal),
+ 3,
+ &arg,
+ 0,
+ BFD_RELOC_FT32_20);
+ break;
+ case FT32_FLD_PA:
+ op_end = parse_exp_save_ilp (op_end, &arg);
+ fixed = TRUE;
+ fix_new_exp (frag_now,
+ (output - frag_now->fr_literal),
+ 3,
+ &arg,
+ 0,
+ BFD_RELOC_FT32_18);
+ break;
+ case FT32_FLD_AA:
+ op_end = parse_exp_save_ilp (op_end, &arg);
+ fixed = TRUE;
+ fix_new_exp (frag_now,
+ (output - frag_now->fr_literal),
+ 3,
+ &arg,
+ 0,
+ BFD_RELOC_FT32_17);
+ break;
+ case FT32_FLD_K16:
+ op_end = parse_exp_save_ilp (op_end, &arg);
+ fixed = TRUE;
+ fix_new_exp (frag_now,
+ (output - frag_now->fr_literal),
+ 2,
+ &arg,
+ 0,
+ BFD_RELOC_16);
+ break;
+ case FT32_FLD_K15:
+ op_end = parse_exp_save_ilp (op_end, &arg);
+ if (arg.X_add_number & 0x80)
+ arg.X_add_number ^= 0x7f00;
+ fixed = TRUE;
+ fix_new_exp (frag_now,
+ (output - frag_now->fr_literal),
+ 2,
+ &arg,
+ 0,
+ BFD_RELOC_FT32_15);
+ break;
+ case FT32_FLD_R_D_POST:
+ b |= parse_register_operand (&op_end) << FT32_FLD_R_D_BIT;
+ break;
+ case FT32_FLD_R_1_POST:
+ b |= parse_register_operand (&op_end) << FT32_FLD_R_1_BIT;
+ break;
+ default:
+ as_bad (_("internal error in argument parsing"));
+ break;
+ }
+
f &= ~lobit;
+
if (f)
{
while (ISSPACE (*op_end))
@@ -387,9 +407,26 @@ md_assemble (char *str)
}
}
}
+
if (*op_end != 0)
as_warn (_("extra stuff on line ignored"));
+ can_sc = ft32_shortcode (b, &sc);
+
+ if (!fixed && can_sc)
+ {
+ arg.X_op = O_constant;
+ arg.X_add_number = 0;
+ arg.X_add_symbol = NULL;
+ arg.X_op_symbol = NULL;
+ fix_new_exp (frag_now,
+ (output - frag_now->fr_literal),
+ 2,
+ &arg,
+ 0,
+ BFD_RELOC_FT32_RELAX);
+ }
+
output[idx++] = 0xff & (b >> 0);
output[idx++] = 0xff & (b >> 8);
output[idx++] = 0xff & (b >> 16);
@@ -454,6 +491,9 @@ const char *md_shortopts = "";
struct option md_longopts[] =
{
+#define OPTION_NORELAX (OPTION_MD_BASE)
+ {"norelax", no_argument, NULL, OPTION_NORELAX},
+ {"no-relax", no_argument, NULL, OPTION_NORELAX},
{NULL, no_argument, NULL, 0}
};
size_t md_longopts_size = sizeof (md_longopts);
@@ -463,12 +503,26 @@ size_t md_longopts_size = sizeof (md_longopts);
int
md_parse_option (int c ATTRIBUTE_UNUSED, const char *arg ATTRIBUTE_UNUSED)
{
- return 0;
+ switch (c)
+ {
+ case OPTION_NORELAX:
+ norelax = 1;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
}
void
md_show_usage (FILE *stream ATTRIBUTE_UNUSED)
{
+ fprintf (stream, _("FT32 options:\n"));
+ fprintf (stream, _("\n\
+-no-relax don't relax relocations\n\
+ \n"));
}
/* Convert from target byte order to host byte order. */
@@ -487,6 +541,7 @@ md_chars_to_number (char * buf, int n)
return result;
}
+
/* Apply a fixup to the object file. */
void
@@ -497,8 +552,43 @@ md_apply_fix (fixS *fixP ATTRIBUTE_UNUSED,
long val = *valP;
long newval;
+ if (linkrelax && fixP->fx_subsy)
+ {
+ /* For a subtraction relocation expression, generate one
+ of the DIFF relocs, with the value being the difference.
+ Note that a sym1 - sym2 expression is adjusted into a
+ section_start_sym + sym4_offset_from_section_start - sym1
+ expression. fixP->fx_addsy holds the section start symbol,
+ fixP->fx_offset holds sym2's offset, and fixP->fx_subsy
+ holds sym1. Calculate the current difference and write value,
+ but leave fx_offset as is - during relaxation,
+ fx_offset - value gives sym1's value. */
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_32:
+ fixP->fx_r_type = BFD_RELOC_FT32_DIFF32;
+ break;
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("expression too complex"));
+ break;
+ }
+
+ val = S_GET_VALUE (fixP->fx_addsy) +
+ fixP->fx_offset - S_GET_VALUE (fixP->fx_subsy);
+ *valP = val;
+
+ fixP->fx_subsy = NULL;
+ }
+
+ /* We don't actually support subtracting a symbol. */
+ if (fixP->fx_subsy != (symbolS *) NULL)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+
switch (fixP->fx_r_type)
{
+ case BFD_RELOC_FT32_DIFF32:
case BFD_RELOC_32:
buf[3] = val >> 24;
buf[2] = val >> 16;
@@ -555,13 +645,15 @@ md_apply_fix (fixS *fixP ATTRIBUTE_UNUSED,
md_number_to_chars (buf, newval, 4);
break;
+ case BFD_RELOC_FT32_RELAX:
+ break;
+
default:
abort ();
}
if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
fixP->fx_done = 1;
- // printf("fx_addsy=%p fixP->fx_pcrel=%d fx_done=%d\n", fixP->fx_addsy, fixP->fx_pcrel, fixP->fx_done);
}
void
@@ -571,6 +663,7 @@ md_number_to_chars (char *ptr, valueT use, int nbytes)
}
/* Generate a machine-dependent relocation. */
+
arelent *
tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP)
{
@@ -587,12 +680,15 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP)
case BFD_RELOC_FT32_15:
case BFD_RELOC_FT32_17:
case BFD_RELOC_FT32_18:
+ case BFD_RELOC_FT32_RELAX:
+ case BFD_RELOC_FT32_DIFF32:
code = fixP->fx_r_type;
break;
default:
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("Semantics error. This type of operand can not be relocated, it must be an assembly-time constant"));
- return 0;
+ _("Semantics error. This type of operand can not be "
+ "relocated, it must be an assembly-time constant"));
+ return NULL;
}
relP = XNEW (arelent);
@@ -617,3 +713,80 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP)
return relP;
}
+
+/* TC_FORCE_RELOCATION hook */
+
+static bfd_boolean
+relaxable_section (asection *sec)
+{
+ return ((sec->flags & SEC_DEBUGGING) == 0
+ && (sec->flags & SEC_CODE) != 0
+ && (sec->flags & SEC_ALLOC) != 0);
+}
+
+/* Does whatever the xtensa port does. */
+
+int
+ft32_validate_fix_sub (fixS *fix)
+{
+ segT add_symbol_segment, sub_symbol_segment;
+
+ /* The difference of two symbols should be resolved by the assembler when
+ linkrelax is not set. If the linker may relax the section containing
+ the symbols, then an Xtensa DIFF relocation must be generated so that
+ the linker knows to adjust the difference value. */
+ if (!linkrelax || fix->fx_addsy == NULL)
+ return 0;
+
+ /* Make sure both symbols are in the same segment, and that segment is
+ "normal" and relaxable. If the segment is not "normal", then the
+ fix is not valid. If the segment is not "relaxable", then the fix
+ should have been handled earlier. */
+ add_symbol_segment = S_GET_SEGMENT (fix->fx_addsy);
+ if (! SEG_NORMAL (add_symbol_segment) ||
+ ! relaxable_section (add_symbol_segment))
+ return 0;
+
+ sub_symbol_segment = S_GET_SEGMENT (fix->fx_subsy);
+ return (sub_symbol_segment == add_symbol_segment);
+}
+
+/* TC_FORCE_RELOCATION hook */
+
+/* If linkrelax is turned on, and the symbol to relocate
+ against is in a relaxable segment, don't compute the value -
+ generate a relocation instead. */
+
+int
+ft32_force_relocation (fixS *fix)
+{
+ if (linkrelax && fix->fx_addsy
+ && relaxable_section (S_GET_SEGMENT (fix->fx_addsy)))
+ {
+ return 1;
+ }
+
+ return generic_force_reloc (fix);
+}
+
+bfd_boolean
+ft32_allow_local_subtract (expressionS * left,
+ expressionS * right,
+ segT section)
+{
+ /* If we are not in relaxation mode, subtraction is OK. */
+ if (!linkrelax)
+ return TRUE;
+
+ /* If the symbols are not in a code section then they are OK. */
+ if ((section->flags & SEC_CODE) == 0)
+ return TRUE;
+
+ if (left->X_add_symbol == right->X_add_symbol)
+ return TRUE;
+
+ /* We have to assume that there may be instructions between the
+ two symbols and that relaxation may increase the distance between
+ them. */
+ return FALSE;
+}