/* tc-ft32.c -- Assemble code for ft32
- Copyright (C) 2008-2016 Free Software Foundation, Inc.
+ Copyright (C) 2008-2023 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
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[] = "#";
static int pending_reloc;
-static struct hash_control *opcode_hash_control;
+static htab_t opcode_hash_control;
static valueT md_chars_to_number (char * buf, int n);
md_begin (void)
{
const ft32_opc_info_t *opcode;
- opcode_hash_control = hash_new ();
+ opcode_hash_control = str_htab_create ();
/* Insert names into hash table. */
for (opcode = ft32_opc_info; opcode->name; opcode++)
- hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
+ str_hash_insert (opcode_hash_control, opcode->name, opcode, 0);
bfd_set_arch_mach (stdoutput, TARGET_ARCH, 0);
+ if (!norelax)
+ linkrelax = 1;
}
/* Parse an expression and then restore the input line pointer. */
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)},
{
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;
+ bool fixed = false;
+ unsigned int sc;
+ bool can_sc;
/* Drop leading whitespace. */
while (*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++;
if (nlen == 0)
as_bad (_("can't find opcode "));
- opcode = (ft32_opc_info_t *) hash_find (opcode_hash_control, op_start);
+ opcode = (ft32_opc_info_t *) str_hash_find (opcode_hash_control, op_start);
*op_end = pend;
if (opcode == NULL)
if (opcode->dw)
{
int dw;
+
if (*op_end == '.')
{
switch (op_end[1])
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_K8:
- op_end = parse_exp_save_ilp (op_end, &arg);
- fix_new_exp (frag_now,
- (output - frag_now->fr_literal),
- 1,
- &arg,
- 0,
- BFD_RELOC_8);
- 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))
}
}
}
+
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);
output[idx++] = 0xff & (b >> 24);
+ dwarf2_emit_insn (4);
+
while (ISSPACE (*op_end))
op_end++;
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);
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. */
return result;
}
+
/* Apply a fixup to the object file. */
void
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_subtract (fixP);
+ 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_subtract (fixP);
+
switch (fixP->fx_r_type)
{
+ case BFD_RELOC_FT32_DIFF32:
case BFD_RELOC_32:
buf[3] = val >> 24;
buf[2] = val >> 16;
md_number_to_chars (buf, newval, 3);
break;
+ case BFD_RELOC_FT32_15:
+ if (!val)
+ break;
+ newval = md_chars_to_number (buf, 2);
+ newval |= val & ((1 << 15) - 1);
+ md_number_to_chars (buf, newval, 2);
+ break;
+
case BFD_RELOC_FT32_17:
if (!val)
break;
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
}
/* Generate a machine-dependent relocation. */
+
arelent *
tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP)
{
case BFD_RELOC_8:
case BFD_RELOC_FT32_10:
case BFD_RELOC_FT32_20:
+ 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 = xmalloc (sizeof (arelent));
+ relP = XNEW (arelent);
gas_assert (relP != 0);
- relP->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ relP->sym_ptr_ptr = XNEW (asymbol *);
*relP->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
relP->address = fixP->fx_frag->fr_address + fixP->fx_where;
return relP;
}
+
+/* TC_FORCE_RELOCATION hook */
+
+static bool
+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);
+}
+
+bool
+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;
+}