diff options
author | James Bowman <james.bowman@ftdichip.com> | 2017-11-01 15:33:24 +0000 |
---|---|---|
committer | Nick Clifton <nickc@redhat.com> | 2017-11-01 15:33:24 +0000 |
commit | 81b42bcab16f4462eb4c91db2cf09cd76e17402e (patch) | |
tree | f46b9942608fd108bf16b1b10eea645c82e7142e /gas/config/tc-ft32.c | |
parent | a67d66eb97e7613a38ffe6622d837303b3ecd31d (diff) | |
download | gdb-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.c | 387 |
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; +} |