/* tc-arc.c -- Assembler for the ARC
- Copyright (C) 1994-2016 Free Software Foundation, Inc.
+ Copyright (C) 1994-2023 Free Software Foundation, Inc.
Contributor: Claudiu Zissulescu <claziss@synopsys.com>
#include "as.h"
#include "subsegs.h"
-#include "struc-symbol.h"
#include "dwarf2dbg.h"
#include "dw2gencfi.h"
#include "safe-ctype.h"
#include "opcode/arc.h"
+#include "opcode/arc-attrs.h"
#include "elf/arc.h"
#include "../opcodes/arc-ext.h"
#define LP_INSN(x) ((MAJOR_OPCODE (x) == 0x4) \
&& (SUB_OPCODE (x) == 0x28))
-/* Equal to MAX_PRECISION in atof-ieee.c. */
-#define MAX_LITTLENUMS 6
-
#ifndef TARGET_WITH_CPU
#define TARGET_WITH_CPU "arc700"
#endif /* TARGET_WITH_CPU */
+#define ARC_GET_FLAG(s) (*symbol_get_tc (s))
+#define ARC_SET_FLAG(s,v) (*symbol_get_tc (s) |= (v))
+#define streq(a, b) (strcmp (a, b) == 0)
+
/* Enum used to enumerate the relaxable ins operands. */
enum rlx_operand_type
{
#define is_spfp_p(op) (((sc) == SPX))
#define is_dpfp_p(op) (((sc) == DPX))
#define is_fpuda_p(op) (((sc) == DPA))
-#define is_br_jmp_insn_p(op) (((op)->insn_class == BRANCH \
- || (op)->insn_class == JUMP))
+#define is_br_jmp_insn_p(op) (((op)->insn_class == BRANCH \
+ || (op)->insn_class == JUMP \
+ || (op)->insn_class == BRCC \
+ || (op)->insn_class == BBIT0 \
+ || (op)->insn_class == BBIT1 \
+ || (op)->insn_class == BI \
+ || (op)->insn_class == EI \
+ || (op)->insn_class == ENTER \
+ || (op)->insn_class == JLI \
+ || (op)->insn_class == LOOP \
+ || (op)->insn_class == LEAVE \
+ ))
#define is_kernel_insn_p(op) (((op)->insn_class == KERNEL))
#define is_nps400_p(op) (((sc) == NPS400))
static void arc_extra_reloc (int);
static void arc_extinsn (int);
static void arc_extcorereg (int);
+static void arc_attribute (int);
const pseudo_typeS md_pseudo_table[] =
{
{ "lcommon", arc_lcomm, 0 },
{ "cpu", arc_option, 0 },
+ { "arc_attribute", arc_attribute, 0 },
{ "extinstruction", arc_extinsn, 0 },
{ "extcoreregister", arc_extcorereg, EXT_CORE_REGISTER },
{ "extauxregister", arc_extcorereg, EXT_AUX_REGISTER },
unsigned char pcrel;
/* TRUE if this fixup is for LIMM operand. */
- bfd_boolean islong;
+ bool islong;
};
struct arc_insn
{
- unsigned int insn;
+ unsigned long long int insn;
int nfixups;
struct arc_fixup fixups[MAX_INSN_FIXUPS];
long limm;
- bfd_boolean short_insn; /* Boolean value: TRUE if current insn is
- short. */
- bfd_boolean has_limm; /* Boolean value: TRUE if limm field is
- valid. */
- bfd_boolean relax; /* Boolean value: TRUE if needs
- relaxation. */
+ unsigned int len; /* Length of instruction in bytes. */
+ bool has_limm; /* Boolean value: TRUE if limm field is valid. */
+ bool relax; /* Boolean value: TRUE if needs relaxation. */
};
/* Structure to hold any last two instructions. */
const struct arc_opcode *opcode;
/* Boolean value: TRUE if current insn is short. */
- bfd_boolean has_limm;
+ bool has_limm;
/* Boolean value: TRUE if current insn has delay slot. */
- bfd_boolean has_delay_slot;
+ bool has_delay_slot;
} arc_last_insns[2];
/* Extension instruction suffix classes. */
(const struct arc_opcode *, const expressionS *, int,
const struct arc_flags *, int, struct arc_insn *);
-/* The cpu for which we are generating code. */
-static unsigned arc_target;
-static const char *arc_target_name;
-static unsigned arc_features;
-
-/* The default architecture. */
-static int arc_mach_type;
+/* The selection of the machine type can come from different sources. This
+ enum is used to track how the selection was made in order to perform
+ error checks. */
+enum mach_selection_type
+ {
+ MACH_SELECTION_NONE,
+ MACH_SELECTION_FROM_DEFAULT,
+ MACH_SELECTION_FROM_CPU_DIRECTIVE,
+ MACH_SELECTION_FROM_COMMAND_LINE
+ };
-/* TRUE if the cpu type has been explicitly specified. */
-static bfd_boolean mach_type_specified_p = FALSE;
+/* How the current machine type was selected. */
+static enum mach_selection_type mach_selection_mode = MACH_SELECTION_NONE;
/* The hash table of instruction opcodes. */
-static struct hash_control *arc_opcode_hash;
+static htab_t arc_opcode_hash;
/* The hash table of register symbols. */
-static struct hash_control *arc_reg_hash;
+static htab_t arc_reg_hash;
/* The hash table of aux register symbols. */
-static struct hash_control *arc_aux_hash;
+static htab_t arc_aux_hash;
/* The hash table of address types. */
-static struct hash_control *arc_addrtype_hash;
+static htab_t arc_addrtype_hash;
+
+#define ARC_CPU_TYPE_A6xx(NAME,EXTRA) \
+ { #NAME, ARC_OPCODE_ARC600, bfd_mach_arc_arc600, \
+ E_ARC_MACH_ARC600, EXTRA}
+#define ARC_CPU_TYPE_A7xx(NAME,EXTRA) \
+ { #NAME, ARC_OPCODE_ARC700, bfd_mach_arc_arc700, \
+ E_ARC_MACH_ARC700, EXTRA}
+#define ARC_CPU_TYPE_AV2EM(NAME,EXTRA) \
+ { #NAME, ARC_OPCODE_ARCv2EM, bfd_mach_arc_arcv2, \
+ EF_ARC_CPU_ARCV2EM, EXTRA}
+#define ARC_CPU_TYPE_AV2HS(NAME,EXTRA) \
+ { #NAME, ARC_OPCODE_ARCv2HS, bfd_mach_arc_arcv2, \
+ EF_ARC_CPU_ARCV2HS, EXTRA}
+#define ARC_CPU_TYPE_NONE \
+ { 0, 0, 0, 0, 0 }
/* A table of CPU names and opcode sets. */
static const struct cpu_type
}
cpu_types[] =
{
- { "arc600", ARC_OPCODE_ARC600, bfd_mach_arc_arc600,
- E_ARC_MACH_ARC600, 0x00},
- { "arc700", ARC_OPCODE_ARC700, bfd_mach_arc_arc700,
- E_ARC_MACH_ARC700, 0x00},
- { "nps400", ARC_OPCODE_ARC700 , bfd_mach_arc_arc700,
- E_ARC_MACH_ARC700, ARC_NPS400},
- { "arcem", ARC_OPCODE_ARCv2EM, bfd_mach_arc_arcv2,
- EF_ARC_CPU_ARCV2EM, 0x00},
- { "archs", ARC_OPCODE_ARCv2HS, bfd_mach_arc_arcv2,
- EF_ARC_CPU_ARCV2HS, ARC_CD},
- { 0, 0, 0, 0, 0 }
+ #include "elf/arc-cpu.def"
};
+/* Information about the cpu/variant we're assembling for. */
+static struct cpu_type selected_cpu = { 0, 0, 0, E_ARC_OSABI_CURRENT, 0 };
+
+/* TRUE if current assembly code uses RF16 only registers. */
+static bool rf16_only = true;
+
+/* MPY option. */
+static unsigned mpy_option = 0;
+
+/* Use PIC. */
+static unsigned pic_option = 0;
+
+/* Use small data. */
+static unsigned sda_option = 0;
+
+/* Use TLS. */
+static unsigned tls_option = 0;
+
+/* Command line given features. */
+static unsigned cl_features = 0;
+
/* Used by the arc_reloc_op table. Order is important. */
#define O_gotoff O_md1 /* @gotoff relocation. */
#define O_gotpc O_md2 /* @gotpc relocation. */
const unsigned arc_num_relaxable_ins = ARRAY_SIZE (arc_relaxable_insns);
-/* Flags to set in the elf header. */
-static flagword arc_eflag = 0x00;
-
/* Pre-defined "_GLOBAL_OFFSET_TABLE_". */
symbolS * GOT_symbol = 0;
/* Set to TRUE when we assemble instructions. */
-static bfd_boolean assembling_insn = FALSE;
+static bool assembling_insn = false;
+
+/* List with attributes set explicitly. */
+static bool attributes_set_explicitly[NUM_KNOWN_OBJ_ATTRIBUTES];
/* Functions implementation. */
{
const struct arc_opcode_hash_entry *entry;
- entry = hash_find (arc_opcode_hash, name);
+ entry = str_hash_find (arc_opcode_hash, name);
return entry;
}
static void
arc_insert_opcode (const struct arc_opcode *opcode)
{
- const char *name, *retval;
+ const char *name;
struct arc_opcode_hash_entry *entry;
name = opcode->name;
- entry = hash_find (arc_opcode_hash, name);
+ entry = str_hash_find (arc_opcode_hash, name);
if (entry == NULL)
{
entry = XNEW (struct arc_opcode_hash_entry);
entry->count = 0;
entry->opcode = NULL;
- retval = hash_insert (arc_opcode_hash, name, (void *) entry);
- if (retval)
- as_fatal (_("internal error: can't hash opcode '%s': %s"),
- name, retval);
+ if (str_hash_insert (arc_opcode_hash, name, entry, 0) != NULL)
+ as_fatal (_("duplicate %s"), name);
}
entry->opcode = XRESIZEVEC (const struct arc_opcode *, entry->opcode,
entry->count + 1);
- if (entry->opcode == NULL)
- as_fatal (_("Virtual memory exhausted"));
-
entry->opcode[entry->count] = opcode;
entry->count++;
}
+static void
+arc_opcode_free (void *elt)
+{
+ string_tuple_t *tuple = (string_tuple_t *) elt;
+ struct arc_opcode_hash_entry *entry = (void *) tuple->value;
+ free (entry->opcode);
+ free (entry);
+ free (tuple);
+}
-/* Like md_number_to_chars but used for limms. The 4-byte limm value,
- is encoded as 'middle-endian' for a little-endian target. FIXME!
- this function is used for regular 4 byte instructions as well. */
+/* Like md_number_to_chars but for middle-endian values. The 4-byte limm
+ value, is encoded as 'middle-endian' for a little-endian target. This
+ function is used for regular 4, 6, and 8 byte instructions as well. */
static void
-md_number_to_chars_midend (char *buf, valueT val, int n)
+md_number_to_chars_midend (char *buf, unsigned long long val, int n)
{
- if (n == 4)
+ switch (n)
{
+ case 2:
+ md_number_to_chars (buf, val, n);
+ break;
+ case 6:
+ md_number_to_chars (buf, (val & 0xffff00000000ull) >> 32, 2);
+ md_number_to_chars_midend (buf + 2, (val & 0xffffffff), 4);
+ break;
+ case 4:
md_number_to_chars (buf, (val & 0xffff0000) >> 16, 2);
md_number_to_chars (buf + 2, (val & 0xffff), 2);
- }
- else
- {
- md_number_to_chars (buf, val, n);
+ break;
+ case 8:
+ md_number_to_chars_midend (buf, (val & 0xffffffff00000000ull) >> 32, 4);
+ md_number_to_chars_midend (buf + 4, (val & 0xffffffff), 4);
+ break;
+ default:
+ abort ();
}
}
+/* Check if a feature is allowed for a specific CPU. */
+
+static void
+arc_check_feature (void)
+{
+ unsigned i;
+
+ if (!selected_cpu.features
+ || !selected_cpu.name)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE (feature_list); i++)
+ if ((selected_cpu.features & feature_list[i].feature)
+ && !(selected_cpu.flags & feature_list[i].cpus))
+ as_bad (_("invalid %s option for %s cpu"), feature_list[i].name,
+ selected_cpu.name);
+
+ for (i = 0; i < ARRAY_SIZE (conflict_list); i++)
+ if ((selected_cpu.features & conflict_list[i]) == conflict_list[i])
+ as_bad(_("conflicting ISA extension attributes."));
+}
+
/* Select an appropriate entry from CPU_TYPES based on ARG and initialise
- the relevant static global variables. */
+ the relevant static global variables. Parameter SEL describes where
+ this selection originated from. */
static void
-arc_select_cpu (const char *arg)
+arc_select_cpu (const char *arg, enum mach_selection_type sel)
{
- int cpu_flags = 0;
int i;
+ static struct cpu_type old_cpu = { 0, 0, 0, E_ARC_OSABI_CURRENT, 0 };
+
+ /* We should only set a default if we've not made a selection from some
+ other source. */
+ gas_assert (sel != MACH_SELECTION_FROM_DEFAULT
+ || mach_selection_mode == MACH_SELECTION_NONE);
+
+ if ((mach_selection_mode == MACH_SELECTION_FROM_CPU_DIRECTIVE)
+ && (sel == MACH_SELECTION_FROM_CPU_DIRECTIVE))
+ as_bad (_("Multiple .cpu directives found"));
+ /* Look for a matching entry in CPU_TYPES array. */
for (i = 0; cpu_types[i].name; ++i)
{
if (!strcasecmp (cpu_types[i].name, arg))
{
- arc_target = cpu_types[i].flags;
- arc_target_name = cpu_types[i].name;
- arc_features = cpu_types[i].features;
- arc_mach_type = cpu_types[i].mach;
- cpu_flags = cpu_types[i].eflags;
+ /* If a previous selection was made on the command line, then we
+ allow later selections on the command line to override earlier
+ ones. However, a selection from a '.cpu NAME' directive must
+ match the command line selection, or we give a warning. */
+ if (mach_selection_mode == MACH_SELECTION_FROM_COMMAND_LINE)
+ {
+ gas_assert (sel == MACH_SELECTION_FROM_COMMAND_LINE
+ || sel == MACH_SELECTION_FROM_CPU_DIRECTIVE);
+ if (sel == MACH_SELECTION_FROM_CPU_DIRECTIVE
+ && selected_cpu.mach != cpu_types[i].mach)
+ {
+ as_warn (_("Command-line value overrides \".cpu\" directive"));
+ }
+ return;
+ }
+ /* Initialise static global data about selected machine type. */
+ selected_cpu.flags = cpu_types[i].flags;
+ selected_cpu.name = cpu_types[i].name;
+ selected_cpu.features = cpu_types[i].features | cl_features;
+ selected_cpu.mach = cpu_types[i].mach;
+ selected_cpu.eflags = ((selected_cpu.eflags & ~EF_ARC_MACH_MSK)
+ | cpu_types[i].eflags);
break;
}
}
if (!cpu_types[i].name)
as_fatal (_("unknown architecture: %s\n"), arg);
- gas_assert (cpu_flags != 0);
- arc_eflag = (arc_eflag & ~EF_ARC_MACH_MSK) | cpu_flags;
+
+ /* Check if set features are compatible with the chosen CPU. */
+ arc_check_feature ();
+
+ /* If we change the CPU, we need to re-init the bfd. */
+ if (mach_selection_mode != MACH_SELECTION_NONE
+ && (old_cpu.mach != selected_cpu.mach))
+ {
+ bfd_find_target (arc_target_format, stdoutput);
+ if (! bfd_set_arch_mach (stdoutput, bfd_arch_arc, selected_cpu.mach))
+ as_warn (_("Could not set architecture and machine"));
+ }
+
+ mach_selection_mode = sel;
+ old_cpu = selected_cpu;
}
/* Here ends all the ARCompact extension instruction assembling
0, /* size: 1, 2, or 4 usually. */
sym, /* X_add_symbol. */
0, /* X_add_number. */
- FALSE, /* TRUE if PC-relative relocation. */
+ false, /* TRUE if PC-relative relocation. */
r_type /* Relocation type. */);
fixP->fx_subsy = lab;
}
static void
arc_option (int ignore ATTRIBUTE_UNUSED)
{
- int mach = -1;
char c;
char *cpu;
+ const char *cpu_name;
c = get_symbol_name (&cpu);
- mach = arc_get_mach (cpu);
-
- if (mach == -1)
- goto bad_cpu;
-
- if (!mach_type_specified_p)
- {
- if ((!strcmp ("ARC600", cpu))
- || (!strcmp ("ARC601", cpu))
- || (!strcmp ("A6", cpu)))
- {
- md_parse_option (OPTION_MCPU, "arc600");
- }
- else if ((!strcmp ("ARC700", cpu))
- || (!strcmp ("A7", cpu)))
- {
- md_parse_option (OPTION_MCPU, "arc700");
- }
- else if (!strcmp ("EM", cpu))
- {
- md_parse_option (OPTION_MCPU, "arcem");
- }
- else if (!strcmp ("HS", cpu))
- {
- md_parse_option (OPTION_MCPU, "archs");
- }
- else if (!strcmp ("NPS400", cpu))
- {
- md_parse_option (OPTION_MCPU, "nps400");
- }
- else
- as_fatal (_("could not find the architecture"));
-
- if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach))
- as_fatal (_("could not set architecture and machine"));
- /* Set elf header flags. */
- bfd_set_private_flags (stdoutput, arc_eflag);
- }
- else
- if (arc_mach_type != mach)
- as_warn (_("Command-line value overrides \".cpu\" directive"));
+ cpu_name = cpu;
+ if ((!strcmp ("ARC600", cpu))
+ || (!strcmp ("ARC601", cpu))
+ || (!strcmp ("A6", cpu)))
+ cpu_name = "arc600";
+ else if ((!strcmp ("ARC700", cpu))
+ || (!strcmp ("A7", cpu)))
+ cpu_name = "arc700";
+ else if (!strcmp ("EM", cpu))
+ cpu_name = "arcem";
+ else if (!strcmp ("HS", cpu))
+ cpu_name = "archs";
+ else if (!strcmp ("NPS400", cpu))
+ cpu_name = "nps400";
+
+ arc_select_cpu (cpu_name, MACH_SELECTION_FROM_CPU_DIRECTIVE);
restore_line_pointer (c);
demand_empty_rest_of_line ();
- return;
-
- bad_cpu:
- restore_line_pointer (c);
- as_bad (_("invalid identifier for \".cpu\""));
- ignore_rest_of_line ();
}
/* Smartly print an expression. */
fflush (stderr);
}
+/* Helper for parsing an argument, used for sorting out the relocation
+ type. */
+
+static void
+parse_reloc_symbol (expressionS *resultP)
+{
+ char *reloc_name, c, *sym_name;
+ size_t len;
+ int i;
+ const struct arc_reloc_op_tag *r;
+ expressionS right;
+ symbolS *base;
+
+ /* A relocation operand has the following form
+ @identifier@relocation_type. The identifier is already in
+ tok! */
+ if (resultP->X_op != O_symbol)
+ {
+ as_bad (_("No valid label relocation operand"));
+ resultP->X_op = O_illegal;
+ return;
+ }
+
+ /* Parse @relocation_type. */
+ input_line_pointer++;
+ c = get_symbol_name (&reloc_name);
+ len = input_line_pointer - reloc_name;
+ if (len == 0)
+ {
+ as_bad (_("No relocation operand"));
+ resultP->X_op = O_illegal;
+ return;
+ }
+
+ /* Go through known relocation and try to find a match. */
+ r = &arc_reloc_op[0];
+ for (i = arc_num_reloc_op - 1; i >= 0; i--, r++)
+ if (len == r->length
+ && memcmp (reloc_name, r->name, len) == 0)
+ break;
+ if (i < 0)
+ {
+ as_bad (_("Unknown relocation operand: @%s"), reloc_name);
+ resultP->X_op = O_illegal;
+ return;
+ }
+
+ *input_line_pointer = c;
+ SKIP_WHITESPACE_AFTER_NAME ();
+ /* Extra check for TLS: base. */
+ if (*input_line_pointer == '@')
+ {
+ if (resultP->X_op_symbol != NULL
+ || resultP->X_op != O_symbol)
+ {
+ as_bad (_("Unable to parse TLS base: %s"),
+ input_line_pointer);
+ resultP->X_op = O_illegal;
+ return;
+ }
+ input_line_pointer++;
+ c = get_symbol_name (&sym_name);
+ base = symbol_find_or_make (sym_name);
+ resultP->X_op = O_subtract;
+ resultP->X_op_symbol = base;
+ restore_line_pointer (c);
+ right.X_add_number = 0;
+ }
+
+ if ((*input_line_pointer != '+')
+ && (*input_line_pointer != '-'))
+ right.X_add_number = 0;
+ else
+ {
+ /* Parse the constant of a complex relocation expression
+ like @identifier@reloc +/- const. */
+ if (! r->complex_expr)
+ {
+ as_bad (_("@%s is not a complex relocation."), r->name);
+ resultP->X_op = O_illegal;
+ return;
+ }
+ expression (&right);
+ if (right.X_op != O_constant)
+ {
+ as_bad (_("Bad expression: @%s + %s."),
+ r->name, input_line_pointer);
+ resultP->X_op = O_illegal;
+ return;
+ }
+ }
+
+ resultP->X_md = r->op;
+ resultP->X_add_number = right.X_add_number;
+}
+
/* Parse the arguments to an opcode. */
static int
int ntok)
{
char *old_input_line_pointer;
- bfd_boolean saw_comma = FALSE;
- bfd_boolean saw_arg = FALSE;
+ bool saw_comma = false;
+ bool saw_arg = false;
int brk_lvl = 0;
int num_args = 0;
- int i;
- size_t len;
- const struct arc_reloc_op_tag *r;
- expressionS tmpE;
- char *reloc_name, c;
memset (tok, 0, sizeof (*tok) * ntok);
input_line_pointer++;
if (saw_comma || !saw_arg)
goto err;
- saw_comma = TRUE;
+ saw_comma = true;
break;
case '}':
if (!saw_arg || num_args == ntok)
goto err;
tok->X_op = O_colon;
- saw_arg = FALSE;
+ saw_arg = false;
++tok;
++num_args;
break;
goto err;
/* Parse @label. */
+ input_line_pointer++;
tok->X_op = O_symbol;
tok->X_md = O_absent;
expression (tok);
- if (*input_line_pointer != '@')
- goto normalsymbol; /* This is not a relocation. */
-
- relocationsym:
-
- /* A relocation opernad has the following form
- @identifier@relocation_type. The identifier is already
- in tok! */
- if (tok->X_op != O_symbol)
- {
- as_bad (_("No valid label relocation operand"));
- goto err;
- }
-
- /* Parse @relocation_type. */
- input_line_pointer++;
- c = get_symbol_name (&reloc_name);
- len = input_line_pointer - reloc_name;
- if (len == 0)
- {
- as_bad (_("No relocation operand"));
- goto err;
- }
- /* Go through known relocation and try to find a match. */
- r = &arc_reloc_op[0];
- for (i = arc_num_reloc_op - 1; i >= 0; i--, r++)
- if (len == r->length
- && memcmp (reloc_name, r->name, len) == 0)
- break;
- if (i < 0)
- {
- as_bad (_("Unknown relocation operand: @%s"), reloc_name);
- goto err;
- }
-
- *input_line_pointer = c;
- SKIP_WHITESPACE_AFTER_NAME ();
- /* Extra check for TLS: base. */
if (*input_line_pointer == '@')
- {
- symbolS *base;
- if (tok->X_op_symbol != NULL
- || tok->X_op != O_symbol)
- {
- as_bad (_("Unable to parse TLS base: %s"),
- input_line_pointer);
- goto err;
- }
- input_line_pointer++;
- char *sym_name;
- c = get_symbol_name (&sym_name);
- base = symbol_find_or_make (sym_name);
- tok->X_op = O_subtract;
- tok->X_op_symbol = base;
- restore_line_pointer (c);
- tmpE.X_add_number = 0;
- }
- else if ((*input_line_pointer != '+')
- && (*input_line_pointer != '-'))
- {
- tmpE.X_add_number = 0;
- }
- else
- {
- /* Parse the constant of a complex relocation expression
- like @identifier@reloc +/- const. */
- if (! r->complex_expr)
- {
- as_bad (_("@%s is not a complex relocation."), r->name);
- goto err;
- }
- expression (&tmpE);
- if (tmpE.X_op != O_constant)
- {
- as_bad (_("Bad expression: @%s + %s."),
- r->name, input_line_pointer);
- goto err;
- }
- }
-
- tok->X_md = r->op;
- tok->X_add_number = tmpE.X_add_number;
+ parse_reloc_symbol (tok);
debug_exp (tok);
- saw_comma = FALSE;
- saw_arg = TRUE;
+ if (tok->X_op == O_illegal
+ || tok->X_op == O_absent
+ || num_args == ntok)
+ goto err;
+
+ saw_comma = false;
+ saw_arg = true;
tok++;
num_args++;
break;
identifier@relocation_type, if it is the case parse the
relocation type as well. */
if (*input_line_pointer == '@')
- goto relocationsym;
+ parse_reloc_symbol (tok);
+ else
+ resolve_register (tok);
- normalsymbol:
debug_exp (tok);
if (tok->X_op == O_illegal
|| num_args == ntok)
goto err;
- saw_comma = FALSE;
- saw_arg = TRUE;
+ saw_comma = false;
+ saw_arg = true;
tok++;
num_args++;
break;
int nflg)
{
char *old_input_line_pointer;
- bfd_boolean saw_flg = FALSE;
- bfd_boolean saw_dot = FALSE;
+ bool saw_flg = false;
+ bool saw_dot = false;
int num_flags = 0;
size_t flgnamelen;
input_line_pointer++;
if (saw_dot)
goto err;
- saw_dot = TRUE;
- saw_flg = FALSE;
+ saw_dot = true;
+ saw_flg = false;
break;
default:
input_line_pointer += flgnamelen;
flags++;
- saw_dot = FALSE;
- saw_flg = TRUE;
+ saw_dot = false;
+ saw_flg = true;
num_flags++;
break;
}
/* FIXME! the reloc size is wrong in the BFD file.
When it is fixed please delete me. */
- size = (insn->short_insn && !fixup->islong) ? 2 : 4;
+ size = ((insn->len == 2) && !fixup->islong) ? 2 : 4;
if (fixup->islong)
- offset = (insn->short_insn) ? 2 : 4;
+ offset = insn->len;
/* Some fixups are only used internally, thus no howto. */
if ((int) fixup->reloc == 0)
{
/* FIXME! the reloc size is wrong in the BFD file.
When it is fixed please enable me.
- size = (insn->short_insn && !fixup->islong) ? 2 : 4; */
+ size = ((insn->len == 2 && !fixup->islong) ? 2 : 4; */
pcrel = fixup->pcrel;
}
else
/* Actually output an instruction with its fixup. */
static void
-emit_insn0 (struct arc_insn *insn, char *where, bfd_boolean relax)
+emit_insn0 (struct arc_insn *insn, char *where, bool relax)
{
char *f = where;
+ size_t total_len;
- pr_debug ("Emit insn : 0x%x\n", insn->insn);
- pr_debug ("\tShort : 0x%d\n", insn->short_insn);
+ pr_debug ("Emit insn : 0x%llx\n", insn->insn);
+ pr_debug ("\tLength : %d\n", insn->len);
pr_debug ("\tLong imm: 0x%lx\n", insn->limm);
/* Write out the instruction. */
- if (insn->short_insn)
- {
- if (insn->has_limm)
- {
- if (!relax)
- f = frag_more (6);
- md_number_to_chars (f, insn->insn, 2);
- md_number_to_chars_midend (f + 2, insn->limm, 4);
- dwarf2_emit_insn (6);
- }
- else
- {
- if (!relax)
- f = frag_more (2);
- md_number_to_chars (f, insn->insn, 2);
- dwarf2_emit_insn (2);
- }
- }
- else
- {
- if (insn->has_limm)
- {
- if (!relax)
- f = frag_more (8);
- md_number_to_chars_midend (f, insn->insn, 4);
- md_number_to_chars_midend (f + 4, insn->limm, 4);
- dwarf2_emit_insn (8);
- }
- else
- {
- if (!relax)
- f = frag_more (4);
- md_number_to_chars_midend (f, insn->insn, 4);
- dwarf2_emit_insn (4);
- }
- }
+ total_len = insn->len + (insn->has_limm ? 4 : 0);
+ if (!relax)
+ f = frag_more (total_len);
+
+ md_number_to_chars_midend(f, insn->insn, insn->len);
+
+ if (insn->has_limm)
+ md_number_to_chars_midend (f + insn->len, insn->limm, 4);
+ dwarf2_emit_insn (total_len);
if (!relax)
apply_fixups (insn, frag_now, (f - frag_now->fr_literal));
if (insn->relax)
emit_insn1 (insn);
else
- emit_insn0 (insn, NULL, FALSE);
+ emit_insn0 (insn, NULL, false);
}
/* Check whether a symbol involves a register. */
-static bfd_boolean
+static bool
contains_register (symbolS *sym)
{
if (sym)
&& !contains_register (ex->X_op_symbol));
}
- return FALSE;
+ return false;
}
/* Returns the register number within a symbol. */
/* Return true if a RELOC is generic. A generic reloc is PC-rel of a
simple ME relocation (e.g. RELOC_ARC_32_ME, BFD_RELOC_ARC_PC32. */
-static bfd_boolean
+static bool
generic_reloc_p (extended_bfd_reloc_code_real_type reloc)
{
if (!reloc)
- return FALSE;
+ return false;
switch (reloc)
{
case BFD_RELOC_ARC_SDA16_LD2:
case BFD_RELOC_ARC_SDA16_ST2:
case BFD_RELOC_ARC_SDA32_ME:
- return FALSE;
+ return false;
default:
- return TRUE;
+ return true;
}
}
return 0; /* No space left. */
if (cidx > ntok)
- return 0; /* Incorect args. */
+ return 0; /* Incorrect args. */
memcpy (&tok[ntok+1], &tok[ntok], sizeof (*tok));
/* Check if an particular ARC feature is enabled. */
-static bfd_boolean
+static bool
check_cpu_feature (insn_subclass_t sc)
{
- if (is_code_density_p (sc) && !(arc_features & ARC_CD))
- return FALSE;
+ if (is_code_density_p (sc) && !(selected_cpu.features & CD))
+ return false;
- if (is_spfp_p (sc) && !(arc_features & ARC_SPFP))
- return FALSE;
+ if (is_spfp_p (sc) && !(selected_cpu.features & SPX))
+ return false;
- if (is_dpfp_p (sc) && !(arc_features & ARC_DPFP))
- return FALSE;
+ if (is_dpfp_p (sc) && !(selected_cpu.features & DPX))
+ return false;
- if (is_fpuda_p (sc) && !(arc_features & ARC_FPUDA))
- return FALSE;
+ if (is_fpuda_p (sc) && !(selected_cpu.features & DPA))
+ return false;
- if (is_nps400_p (sc) && !(arc_features & ARC_NPS400))
- return FALSE;
+ if (is_nps400_p (sc) && !(selected_cpu.features & NPS400))
+ return false;
- return TRUE;
+ return true;
}
/* Parse the flags described by FIRST_PFLAG and NFLGS against the flag
returns FALSE, in which case the FIRST_PFLAG array may have been
modified. */
-static bfd_boolean
+static bool
parse_opcode_flags (const struct arc_opcode *opcode,
int nflgs,
struct arc_flags *first_pflag)
int cl_matches = 0;
struct arc_flags *pflag = NULL;
+ /* Check if opcode has implicit flag classes. */
+ if (cl_flags->flag_class & F_CLASS_IMPLICIT)
+ continue;
+
/* Check for extension conditional codes. */
if (ext_condcode.arc_ext_condcode
&& cl_flags->flag_class & F_CLASS_EXTEND)
if (!strcmp (pf->name, pflag->name))
{
if (pflag->flgp != NULL)
- return FALSE;
+ return false;
/* Found it. */
cl_matches++;
pflag->flgp = pf;
if (!strcmp (flg_operand->name, pflag->name))
{
if (pflag->flgp != NULL)
- return FALSE;
+ return false;
cl_matches++;
pflag->flgp = flg_operand;
lnflg--;
}
if ((cl_flags->flag_class & F_CLASS_REQUIRED) && cl_matches == 0)
- return FALSE;
+ return false;
if ((cl_flags->flag_class & F_CLASS_OPTIONAL) && cl_matches > 1)
- return FALSE;
+ return false;
}
/* Did I check all the parsed flags? */
- return lnflg ? FALSE : TRUE;
+ return lnflg == 0;
}
int *pntok,
struct arc_flags *first_pflag,
int nflgs,
- int *pcpumatch)
+ int *pcpumatch,
+ const char **errmsg)
{
const struct arc_opcode *opcode;
struct arc_opcode_hash_entry_iterator iter;
int ntok = *pntok;
int got_cpu_match = 0;
expressionS bktok[MAX_INSN_ARGS];
- int bkntok;
+ int bkntok, maxerridx = 0;
expressionS emptyE;
+ const char *tmpmsg = NULL;
arc_opcode_hash_entry_iterator_init (&iter);
memset (&emptyE, 0, sizeof (emptyE));
int tokidx = 0;
const expressionS *t = &emptyE;
- pr_debug ("%s:%d: find_opcode_match: trying opcode 0x%08X ",
+ pr_debug ("%s:%d: find_opcode_match: trying opcode 0x%08llX ",
frag_now->fr_file, frag_now->fr_line, opcode->opcode);
/* Don't match opcodes that don't exist on this
architecture. */
- if (!(opcode->cpu & arc_target))
+ if (!(opcode->cpu & selected_cpu.flags))
goto match_failed;
if (!check_cpu_feature (opcode->subclass))
switch (operand->flags & ARC_OPERAND_TYPECHECK_MASK)
{
case ARC_OPERAND_ADDRTYPE:
- /* Check to be an address type. */
- if (tok[tokidx].X_op != O_addrtype)
- goto match_failed;
+ {
+ tmpmsg = NULL;
+
+ /* Check to be an address type. */
+ if (tok[tokidx].X_op != O_addrtype)
+ goto match_failed;
+
+ /* All address type operands need to have an insert
+ method in order to check that we have the correct
+ address type. */
+ gas_assert (operand->insert != NULL);
+ (*operand->insert) (0, tok[tokidx].X_add_number,
+ &tmpmsg);
+ if (tmpmsg != NULL)
+ goto match_failed;
+ }
break;
case ARC_OPERAND_IR:
/* Special handling? */
if (operand->insert)
{
- const char *errmsg = NULL;
+ tmpmsg = NULL;
(*operand->insert)(0,
regno (tok[tokidx].X_add_number),
- &errmsg);
- if (errmsg)
+ &tmpmsg);
+ if (tmpmsg)
{
if (operand->flags & ARC_OPERAND_IGNORE)
{
case O_symbol:
{
const char *p;
+ char *tmpp, *pp;
const struct arc_aux_reg *auxr;
if (opcode->insn_class != AUXREG)
goto de_fault;
p = S_GET_NAME (tok[tokidx].X_add_symbol);
- auxr = hash_find (arc_aux_hash, p);
+ /* For compatibility reasons, an aux register can
+ be spelled with upper or lower case
+ letters. */
+ tmpp = strdup (p);
+ for (pp = tmpp; *pp; ++pp) *pp = TOLOWER (*pp);
+
+ auxr = str_hash_find (arc_aux_hash, tmpp);
if (auxr)
{
/* We modify the token array here, safe in the
tok[tokidx].X_add_number = auxr->address;
ARC_SET_FLAG (tok[tokidx].X_add_symbol, ARC_FLAG_AUX);
}
+ free (tmpp);
if (tok[tokidx].X_op != O_constant)
goto de_fault;
}
- /* Fall-through */
+ /* Fall through. */
case O_constant:
/* Check the range. */
if (operand->bits != 32
}
if (val < min || val > max)
- goto match_failed;
+ {
+ tmpmsg = _("immediate is out of bounds");
+ goto match_failed;
+ }
- /* Check alignmets. */
+ /* Check alignments. */
if ((operand->flags & ARC_OPERAND_ALIGNED32)
&& (val & 0x03))
- goto match_failed;
+ {
+ tmpmsg = _("immediate is not 32bit aligned");
+ goto match_failed;
+ }
if ((operand->flags & ARC_OPERAND_ALIGNED16)
&& (val & 0x01))
- goto match_failed;
+ {
+ tmpmsg = _("immediate is not 16bit aligned");
+ goto match_failed;
+ }
}
else if (operand->flags & ARC_OPERAND_NCHK)
{
if (operand->insert)
{
- const char *errmsg = NULL;
+ tmpmsg = NULL;
(*operand->insert)(0,
tok[tokidx].X_add_number,
- &errmsg);
- if (errmsg)
+ &tmpmsg);
+ if (tmpmsg)
goto match_failed;
}
else if (!(operand->flags & ARC_OPERAND_IGNORE))
regs |= get_register (tok[tokidx].X_op_symbol);
if (operand->insert)
{
- const char *errmsg = NULL;
+ tmpmsg = NULL;
(*operand->insert)(0,
regs,
- &errmsg);
- if (errmsg)
+ &tmpmsg);
+ if (tmpmsg)
goto match_failed;
}
else
goto match_failed;
break;
}
+ /* Fall through. */
default:
de_fault:
if (operand->default_reloc == 0)
case O_tlsie:
if (!(operand->flags & ARC_OPERAND_LIMM))
goto match_failed;
+ /* Fall through. */
case O_absent:
if (!generic_reloc_p (operand->default_reloc))
goto match_failed;
+ break;
default:
break;
}
|| t->X_op == O_absent
|| t->X_op == O_register
|| (t->X_add_number != tok[tokidx].X_add_number))
- goto match_failed;
+ {
+ tmpmsg = _("operand is not duplicate of the "
+ "previous one");
+ goto match_failed;
+ }
}
t = &tok[tokidx];
break;
/* Setup ready for flag parsing. */
if (!parse_opcode_flags (opcode, nflgs, first_pflag))
- goto match_failed;
+ {
+ tmpmsg = _("flag mismatch");
+ goto match_failed;
+ }
pr_debug ("flg");
/* Possible match -- did we use all of our input? */
pr_debug ("\n");
return opcode;
}
+ tmpmsg = _("too many arguments");
match_failed:;
pr_debug ("\n");
/* Restore the original parameters. */
memcpy (tok, bktok, MAX_INSN_ARGS * sizeof (*tok));
ntok = bkntok;
+ if (tokidx >= maxerridx
+ && tmpmsg)
+ {
+ maxerridx = tokidx;
+ *errmsg = tmpmsg;
+ }
}
if (*pcpumatch)
/* Check if *op matches *tok type.
Returns FALSE if they don't match, TRUE if they match. */
-static bfd_boolean
+static bool
pseudo_operand_match (const expressionS *tok,
const struct arc_operand_operation *op)
{
offsetT min, max, val;
- bfd_boolean ret;
+ bool ret;
const struct arc_operand *operand_real = &arc_operands[op->operand_idx];
- ret = FALSE;
+ ret = false;
switch (tok->X_op)
{
case O_constant:
min = 0;
}
if (min <= val && val <= max)
- ret = TRUE;
+ ret = true;
}
break;
if (operand_real->flags & ARC_OPERAND_LIMM
|| ((operand_real->flags & ARC_OPERAND_SIGNED)
&& operand_real->bits == 9))
- ret = TRUE;
+ ret = true;
break;
case O_register:
if (operand_real->flags & ARC_OPERAND_IR)
- ret = TRUE;
+ ret = true;
break;
case O_bracket:
if (operand_real->flags & ARC_OPERAND_BRAKET)
- ret = TRUE;
+ ret = true;
break;
default:
return NULL;
}
-/* The long instructions are not stored in a hash (there's not many of
- them) and so there's no arc_opcode_hash_entry structure to return. This
- helper function for find_special_case_long_opcode takes an arc_opcode
- result and places it into a fake arc_opcode_hash_entry that points to
- the single arc_opcode OPCODE, which is then returned. */
-
-static const struct arc_opcode_hash_entry *
-build_fake_opcode_hash_entry (const struct arc_opcode *opcode)
-{
- static struct arc_opcode_hash_entry entry;
- static struct arc_opcode tmp[2];
- static const struct arc_opcode *ptr[2];
-
- memcpy (&tmp[0], opcode, sizeof (struct arc_opcode));
- memset (&tmp[1], 0, sizeof (struct arc_opcode));
- entry.count = 1;
- entry.opcode = ptr;
- ptr[0] = tmp;
- ptr[1] = NULL;
- return &entry;
-}
-
-
-/* Used by the assembler to match the list of tokens against a long (48 or
- 64 bits) instruction. If a matching long instruction is found, then
- some of the tokens are consumed in this function and converted into a
- single LIMM value, which is then added to the end of the token list,
- where it will be consumed by a LIMM operand that exists in the base
- opcode of the long instruction. */
-
-static const struct arc_opcode_hash_entry *
-find_special_case_long_opcode (const char *opname,
- int *ntok ATTRIBUTE_UNUSED,
- expressionS *tok ATTRIBUTE_UNUSED,
- int *nflgs,
- struct arc_flags *pflags)
-{
- unsigned i;
-
- if (*ntok == MAX_INSN_ARGS)
- return NULL;
-
- for (i = 0; i < arc_num_long_opcodes; ++i)
- {
- struct arc_opcode fake_opcode;
- const struct arc_opcode *opcode;
- struct arc_insn insn;
- expressionS *limm_token;
-
- opcode = &arc_long_opcodes[i].base_opcode;
-
- if (!(opcode->cpu & arc_target))
- continue;
-
- if (!check_cpu_feature (opcode->subclass))
- continue;
-
- if (strcmp (opname, opcode->name) != 0)
- continue;
-
- /* Check that the flags are a match. */
- if (!parse_opcode_flags (opcode, *nflgs, pflags))
- continue;
-
- /* Parse the LIMM operands into the LIMM template. */
- memset (&fake_opcode, 0, sizeof (fake_opcode));
- fake_opcode.name = "fake limm";
- fake_opcode.opcode = arc_long_opcodes[i].limm_template;
- fake_opcode.mask = arc_long_opcodes[i].limm_mask;
- fake_opcode.cpu = opcode->cpu;
- fake_opcode.insn_class = opcode->insn_class;
- fake_opcode.subclass = opcode->subclass;
- memcpy (&fake_opcode.operands[0],
- &arc_long_opcodes[i].operands,
- MAX_INSN_ARGS);
- /* Leave fake_opcode.flags as zero. */
-
- pr_debug ("Calling assemble_insn to build fake limm value\n");
- assemble_insn (&fake_opcode, tok, *ntok,
- NULL, 0, &insn);
- pr_debug (" got limm value: 0x%x\n", insn.insn);
-
- /* Now create a new token at the end of the token array (We know this
- is safe as the token array is always created with enough space for
- MAX_INSN_ARGS, and we check at the start at the start of this
- function that we're not there yet). This new token will
- correspond to a LIMM operand that will be contained in the
- base_opcode of the arc_long_opcode. */
- limm_token = &tok[(*ntok)];
- (*ntok)++;
-
- /* Modify the LIMM token to hold the constant. */
- limm_token->X_op = O_constant;
- limm_token->X_add_number = insn.insn;
-
- /* Return the base opcode. */
- return build_fake_opcode_hash_entry (opcode);
- }
-
- return NULL;
-}
-
/* Used to find special case opcode. */
static const struct arc_opcode_hash_entry *
if (entry == NULL)
entry = find_special_case_flag (opname, nflgs, pflags);
- if (entry == NULL)
- entry = find_special_case_long_opcode (opname, ntok, tok, nflgs, pflags);
-
return entry;
}
+/* Autodetect cpu attribute list. */
+
+static void
+autodetect_attributes (const struct arc_opcode *opcode,
+ const expressionS *tok,
+ int ntok)
+{
+ unsigned i;
+ struct mpy_type
+ {
+ unsigned feature;
+ unsigned encoding;
+ } mpy_list[] = {{ MPY1E, 1 }, { MPY6E, 6 }, { MPY7E, 7 }, { MPY8E, 8 },
+ { MPY9E, 9 }};
+
+ for (i = 0; i < ARRAY_SIZE (feature_list); i++)
+ if (opcode->subclass == feature_list[i].feature)
+ selected_cpu.features |= feature_list[i].feature;
+
+ for (i = 0; i < ARRAY_SIZE (mpy_list); i++)
+ if (opcode->subclass == mpy_list[i].feature)
+ mpy_option = mpy_list[i].encoding;
+
+ for (i = 0; i < (unsigned) ntok; i++)
+ {
+ switch (tok[i].X_md)
+ {
+ case O_gotoff:
+ case O_gotpc:
+ case O_plt:
+ pic_option = 2;
+ break;
+ case O_sda:
+ sda_option = 2;
+ break;
+ case O_tlsgd:
+ case O_tlsie:
+ case O_tpoff9:
+ case O_tpoff:
+ case O_dtpoff9:
+ case O_dtpoff:
+ tls_option = 1;
+ break;
+ default:
+ break;
+ }
+
+ switch (tok[i].X_op)
+ {
+ case O_register:
+ if ((tok[i].X_add_number >= 4 && tok[i].X_add_number <= 9)
+ || (tok[i].X_add_number >= 16 && tok[i].X_add_number <= 25))
+ rf16_only = false;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
/* Given an opcode name, pre-tockenized set of argumenst and the
opcode flags, take it all the way through emission. */
struct arc_flags *pflags,
int nflgs)
{
- bfd_boolean found_something = FALSE;
+ bool found_something = false;
const struct arc_opcode_hash_entry *entry;
int cpumatch = 1;
+ const char *errmsg = NULL;
/* Search opcodes. */
entry = arc_find_opcode (opname);
pr_debug ("%s:%d: assemble_tokens: %s\n",
frag_now->fr_file, frag_now->fr_line, opname);
- found_something = TRUE;
+ found_something = true;
opcode = find_opcode_match (entry, tok, &ntok, pflags,
- nflgs, &cpumatch);
+ nflgs, &cpumatch, &errmsg);
if (opcode != NULL)
{
struct arc_insn insn;
+ autodetect_attributes (opcode, tok, ntok);
assemble_insn (opcode, tok, ntok, pflags, nflgs, &insn);
emit_insn (&insn);
return;
if (found_something)
{
if (cpumatch)
- as_bad (_("inappropriate arguments for opcode '%s'"), opname);
+ if (errmsg)
+ as_bad (_("%s for instruction '%s'"), errmsg, opname);
+ else
+ as_bad (_("inappropriate arguments for opcode '%s'"), opname);
else
as_bad (_("opcode '%s' not supported for target %s"), opname,
- arc_target_name);
+ selected_cpu.name);
}
else
as_bad (_("unknown opcode '%s'"), opname);
struct arc_flags flags[MAX_INSN_FLGS];
/* Split off the opcode. */
- opnamelen = strspn (str, "abcdefghijklmnopqrstuvwxyz_0123468");
+ opnamelen = strspn (str, "abcdefghijklmnopqrstuvwxyz_0123456789");
opname = xmemdup0 (str, opnamelen);
- /* Signalize we are assmbling the instructions. */
- assembling_insn = TRUE;
+ /* Signalize we are assembling the instructions. */
+ assembling_insn = true;
/* Tokenize the flags. */
if ((nflg = tokenize_flags (str + opnamelen, flags, MAX_INSN_FLGS)) == -1)
/* Finish it off. */
assemble_tokens (opname, tok, ntok, flags, nflg);
- assembling_insn = FALSE;
+ assembling_insn = false;
}
/* Callback to insert a register into the hash table. */
static void
declare_register (const char *name, int number)
{
- const char *err;
symbolS *regS = symbol_create (name, reg_section,
- number, &zero_address_frag);
+ &zero_address_frag, number);
- err = hash_insert (arc_reg_hash, S_GET_NAME (regS), (void *) regS);
- if (err)
- as_fatal (_("Inserting \"%s\" into register table failed: %s"),
- name, err);
+ if (str_hash_insert (arc_reg_hash, S_GET_NAME (regS), regS, 0) != NULL)
+ as_fatal (_("duplicate %s"), name);
}
/* Construct symbols for each of the general registers. */
int i;
for (i = 0; i < 64; ++i)
{
- char name[7];
+ char name[32];
sprintf (name, "r%d", i);
declare_register (name, i);
static void
declare_addrtype (const char *name, int number)
{
- const char *err;
symbolS *addrtypeS = symbol_create (name, undefined_section,
- number, &zero_address_frag);
+ &zero_address_frag, number);
- err = hash_insert (arc_addrtype_hash, S_GET_NAME (addrtypeS),
- (void *) addrtypeS);
- if (err)
- as_fatal (_("Inserting \"%s\" into address type table failed: %s"),
- name, err);
+ if (str_hash_insert (arc_addrtype_hash, S_GET_NAME (addrtypeS), addrtypeS, 0))
+ as_fatal (_("duplicate %s"), name);
}
/* Port-specific assembler initialization. This function is called
{
const struct arc_opcode *opcode = arc_opcodes;
- if (!mach_type_specified_p)
- arc_select_cpu (TARGET_WITH_CPU);
+ if (mach_selection_mode == MACH_SELECTION_NONE)
+ arc_select_cpu (TARGET_WITH_CPU, MACH_SELECTION_FROM_DEFAULT);
/* The endianness can be chosen "at the factory". */
target_big_endian = byte_order == BIG_ENDIAN;
- if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, arc_mach_type))
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, selected_cpu.mach))
as_warn (_("could not set architecture and machine"));
/* Set elf header flags. */
- bfd_set_private_flags (stdoutput, arc_eflag);
+ bfd_set_private_flags (stdoutput, selected_cpu.eflags);
/* Set up a hash table for the instructions. */
- arc_opcode_hash = hash_new ();
- if (arc_opcode_hash == NULL)
- as_fatal (_("Virtual memory exhausted"));
+ arc_opcode_hash = htab_create_alloc (16, hash_string_tuple, eq_string_tuple,
+ arc_opcode_free, xcalloc, free);
/* Initialize the hash table with the insns. */
do
}while (opcode->name);
/* Register declaration. */
- arc_reg_hash = hash_new ();
- if (arc_reg_hash == NULL)
- as_fatal (_("Virtual memory exhausted"));
+ arc_reg_hash = str_htab_create ();
declare_register_set ();
declare_register ("gp", 26);
memset (&arc_last_insns[0], 0, sizeof (arc_last_insns));
/* Aux register declaration. */
- arc_aux_hash = hash_new ();
- if (arc_aux_hash == NULL)
- as_fatal (_("Virtual memory exhausted"));
+ arc_aux_hash = str_htab_create ();
const struct arc_aux_reg *auxr = &arc_aux_regs[0];
unsigned int i;
for (i = 0; i < arc_num_aux_regs; i++, auxr++)
{
- const char *retval;
-
- if (!(auxr->cpu & arc_target))
+ if (!(auxr->cpu & selected_cpu.flags))
continue;
if ((auxr->subclass != NONE)
&& !check_cpu_feature (auxr->subclass))
continue;
- retval = hash_insert (arc_aux_hash, auxr->name, (void *) auxr);
- if (retval)
- as_fatal (_("internal error: can't hash aux register '%s': %s"),
- auxr->name, retval);
+ if (str_hash_insert (arc_aux_hash, auxr->name, auxr, 0) != 0)
+ as_fatal (_("duplicate %s"), auxr->name);
}
/* Address type declaration. */
- arc_addrtype_hash = hash_new ();
- if (arc_addrtype_hash == NULL)
- as_fatal (_("Virtual memory exhausted"));
+ arc_addrtype_hash = str_htab_create ();
declare_addrtype ("bd", ARC_NPS400_ADDRTYPE_BD);
declare_addrtype ("jid", ARC_NPS400_ADDRTYPE_JID);
declare_addrtype ("cxd", ARC_NPS400_ADDRTYPE_CXD);
}
+void
+arc_md_end (void)
+{
+ htab_delete (arc_opcode_hash);
+ htab_delete (arc_reg_hash);
+ htab_delete (arc_aux_hash);
+ htab_delete (arc_addrtype_hash);
+}
+
/* Write a value out to the object file, using the appropriate
endianness. */
md_section_align (segT segment,
valueT size)
{
- int align = bfd_get_section_alignment (stdoutput, segment);
+ int align = bfd_section_alignment (segment);
return ((size + (1 << align) - 1) & (-((valueT) 1 << align)));
}
/* The hardware calculates relative to the start of the
insn, but this relocation is relative to location of the
LIMM, compensate. The base always needs to be
- substracted by 4 as we do not support this type of PCrel
+ subtracted by 4 as we do not support this type of PCrel
relocation for short instructions. */
base -= 4;
/* Fall through. */
}
}
- pr_debug ("pcrel from %"BFD_VMA_FMT"x + %lx = %"BFD_VMA_FMT"x, "
- "symbol: %s (%"BFD_VMA_FMT"x)\n",
- fixP->fx_frag->fr_address, fixP->fx_where, base,
+ pr_debug ("pcrel from %" PRIx64 " + %lx = %" PRIx64 ", "
+ "symbol: %s (%" PRIx64 ")\n",
+ (uint64_t) fixP->fx_frag->fr_address, fixP->fx_where, (uint64_t) base,
fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : "(null)",
- fixP->fx_addsy ? S_GET_VALUE (fixP->fx_addsy) : 0);
+ fixP->fx_addsy ? (uint64_t) S_GET_VALUE (fixP->fx_addsy) : (uint64_t) 0);
return base;
}
-/* Given a BFD relocation find the coresponding operand. */
+/* Given a BFD relocation find the corresponding operand. */
static const struct arc_operand *
find_operand_for_reloc (extended_bfd_reloc_code_real_type reloc)
/* Insert an operand value into an instruction. */
-static unsigned
-insert_operand (unsigned insn,
+static unsigned long long
+insert_operand (unsigned long long insn,
const struct arc_operand *operand,
- offsetT val,
+ long long val,
const char *file,
unsigned line)
{
val, min, max, file, line);
}
- pr_debug ("insert field: %ld <= %ld <= %ld in 0x%08x\n",
+ pr_debug ("insert field: %ld <= %lld <= %ld in 0x%08llx\n",
min, val, max, insn);
if ((operand->flags & ARC_OPERAND_ALIGNED32)
}
else
{
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("can't resolve `%s' {%s section} - `%s' {%s section}"),
- fx_addsy ? S_GET_NAME (fx_addsy) : "0",
- segment_name (add_symbol_segment),
- S_GET_NAME (fx_subsy),
- segment_name (sub_symbol_segment));
+ as_bad_subtract (fixP);
return;
}
}
value += S_GET_VALUE (fx_addsy);
value -= md_pcrel_from_section (fixP, seg);
fx_addsy = NULL;
- fixP->fx_pcrel = FALSE;
+ fixP->fx_pcrel = false;
}
else if (add_symbol_segment == absolute_section)
{
value = fixP->fx_offset;
fx_offset += S_GET_VALUE (fixP->fx_addsy);
fx_addsy = NULL;
- fixP->fx_pcrel = FALSE;
+ fixP->fx_pcrel = false;
}
}
if (!fx_addsy)
- fixP->fx_done = TRUE;
+ fixP->fx_done = true;
if (fixP->fx_pcrel)
{
case BFD_RELOC_ARC_32_ME:
/* This is a pc-relative value in a LIMM. Adjust it to the
address of the instruction not to the address of the
- LIMM. Note: it is not anylonger valid this afirmation as
+ LIMM. Note: it is not any longer valid this affirmation as
the linker consider ARC_PC32 a fixup to entire 64 bit
insn. */
fixP->fx_offset += fixP->fx_frag->fr_address;
break;
default:
if ((int) fixP->fx_r_type < 0)
- as_fatal (_("PC relative relocation not allowed for (internal) type %d"),
- fixP->fx_r_type);
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PC relative relocation not allowed for (internal)"
+ " type %d"),
+ fixP->fx_r_type);
break;
}
}
return;
}
- /* Addjust the value if we have a constant. */
+ /* Adjust the value if we have a constant. */
value += fx_offset;
/* For hosts with longs bigger than 32-bits make sure that the top
case BFD_RELOC_ARC_TLS_LE_32:
gas_assert (!fixP->fx_addsy);
gas_assert (!fixP->fx_subsy);
+ /* Fall through. */
case BFD_RELOC_ARC_GOTOFF:
case BFD_RELOC_ARC_32_ME:
case BFD_RELOC_ARC_S21W_PCREL_PLT:
reloc = BFD_RELOC_ARC_S21W_PCREL;
+ /* Fall through. */
case BFD_RELOC_ARC_S25W_PCREL:
case BFD_RELOC_ARC_S21W_PCREL:
int size, fix;
struct arc_relax_type *relax_arg = &fragP->tc_frag_data;
- fix = (fragP->fr_fix < 0 ? 0 : fragP->fr_fix);
+ fix = fragP->fr_fix;
dest = fragP->fr_literal + fix;
table_entry = TC_GENERIC_RELAX_TABLE + fragP->fr_subtype;
pr_debug ("%s:%d: md_convert_frag, subtype: %d, fix: %d, "
- "var: %"BFD_VMA_FMT"d\n",
+ "var: %" PRId64 "\n",
fragP->fr_file, fragP->fr_line,
- fragP->fr_subtype, fix, fragP->fr_var);
+ fragP->fr_subtype, fix, (int64_t) fragP->fr_var);
if (fragP->fr_subtype <= 0
&& fragP->fr_subtype >= arc_num_relax_opcodes)
apply_fixups (&insn, fragP, fix);
- size = insn.short_insn ? (insn.has_limm ? 6 : 2) : (insn.has_limm ? 8 : 4);
+ size = insn.len + (insn.has_limm ? 4 : 0);
gas_assert (table_entry->rlx_length == size);
- emit_insn0 (&insn, dest, TRUE);
+ emit_insn0 (&insn, dest, true);
fragP->fr_fix += table_entry->rlx_length;
fragP->fr_var = 0;
GOTPC reference to _GLOBAL_OFFSET_TABLE_. */
if (((*name == '_')
&& (*(name+1) == 'G')
- && (strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0))
- || ((*name == '_')
- && (*(name+1) == 'D')
- && (strcmp (name, DYNAMIC_STRUCT_NAME) == 0)))
+ && (strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0)))
{
if (!GOT_symbol)
{
as_bad ("GOT already in symbol table");
GOT_symbol = symbol_new (GLOBAL_OFFSET_TABLE_NAME, undefined_section,
- (valueT) 0, &zero_address_frag);
+ &zero_address_frag, 0);
};
return GOT_symbol;
}
/* Called for any expression that can not be recognized. When the
function is called, `input_line_pointer' will point to the start of
- the expression. */
+ the expression. We use it when we have complex operations like
+ @label1 - @label2. */
void
-md_operand (expressionS *expressionP ATTRIBUTE_UNUSED)
+md_operand (expressionS *expressionP)
{
char *p = input_line_pointer;
if (*p == '@')
{
input_line_pointer++;
expressionP->X_op = O_symbol;
+ expressionP->X_md = O_absent;
expression (expressionP);
}
}
the expression with the identified register. It returns TRUE if
it is a register and FALSE otherwise. */
-bfd_boolean
+bool
arc_parse_name (const char *name,
struct expressionS *e)
{
struct symbol *sym;
if (!assembling_insn)
- return FALSE;
+ return false;
- /* Handle only registers and address types. */
- if (e->X_op != O_absent)
- return FALSE;
+ if (e->X_op == O_symbol
+ && e->X_md == O_absent)
+ return false;
- sym = hash_find (arc_reg_hash, name);
+ sym = str_hash_find (arc_reg_hash, name);
if (sym)
{
e->X_op = O_register;
e->X_add_number = S_GET_VALUE (sym);
- return TRUE;
+ return true;
}
- sym = hash_find (arc_addrtype_hash, name);
+ sym = str_hash_find (arc_addrtype_hash, name);
if (sym)
{
e->X_op = O_addrtype;
e->X_add_number = S_GET_VALUE (sym);
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
/* md_parse_option
case OPTION_MCPU:
{
- arc_select_cpu (arg);
- mach_type_specified_p = TRUE;
+ arc_select_cpu (arg, MACH_SELECTION_FROM_COMMAND_LINE);
break;
}
break;
case OPTION_CD:
- /* This option has an effect only on ARC EM. */
- if (arc_target & ARC_OPCODE_ARCv2EM)
- arc_features |= ARC_CD;
- else
- as_warn (_("Code density option invalid for selected CPU"));
+ selected_cpu.features |= CD;
+ cl_features |= CD;
+ arc_check_feature ();
break;
case OPTION_RELAX:
break;
case OPTION_NPS400:
- arc_features |= ARC_NPS400;
+ selected_cpu.features |= NPS400;
+ cl_features |= NPS400;
+ arc_check_feature ();
break;
case OPTION_SPFP:
- arc_features |= ARC_SPFP;
+ selected_cpu.features |= SPX;
+ cl_features |= SPX;
+ arc_check_feature ();
break;
case OPTION_DPFP:
- arc_features |= ARC_DPFP;
+ selected_cpu.features |= DPX;
+ cl_features |= DPX;
+ arc_check_feature ();
break;
case OPTION_FPUDA:
- /* This option has an effect only on ARC EM. */
- if (arc_target & ARC_OPCODE_ARCv2EM)
- arc_features |= ARC_FPUDA;
- else
- as_warn (_("FPUDA invalid for selected CPU"));
+ selected_cpu.features |= DPA;
+ cl_features |= DPA;
+ arc_check_feature ();
break;
/* Dummy options are accepted but have no effect. */
return 1;
}
+/* Display the list of cpu names for use in the help text. */
+
+static void
+arc_show_cpu_list (FILE *stream)
+{
+ int i, offset;
+ static const char *space_buf = " ";
+
+ fprintf (stream, "%s", space_buf);
+ offset = strlen (space_buf);
+ for (i = 0; cpu_types[i].name != NULL; ++i)
+ {
+ bool last = (cpu_types[i + 1].name == NULL);
+
+ /* If displaying the new cpu name string, and the ', ' (for all
+ but the last one) will take us past a target width of 80
+ characters, then it's time for a new line. */
+ if (offset + strlen (cpu_types[i].name) + (last ? 0 : 2) > 80)
+ {
+ fprintf (stream, "\n%s", space_buf);
+ offset = strlen (space_buf);
+ }
+
+ fprintf (stream, "%s%s", cpu_types[i].name, (last ? "\n" : ", "));
+ offset += strlen (cpu_types [i].name) + (last ? 0 : 2);
+ }
+}
+
void
md_show_usage (FILE *stream)
{
fprintf (stream, _("ARC-specific assembler options:\n"));
- fprintf (stream, " -mcpu=<cpu name>\t assemble for CPU <cpu name> "
- "(default: %s)\n", TARGET_WITH_CPU);
- fprintf (stream, " -mcpu=nps400\t\t same as -mcpu=arc700 -mnps400\n");
+ fprintf (stream, " -mcpu=<cpu name>\t (default: %s), assemble for"
+ " CPU <cpu name>, one of:\n", TARGET_WITH_CPU);
+ arc_show_cpu_list (stream);
+ fprintf (stream, "\n");
fprintf (stream, " -mA6/-mARC600/-mARC601 same as -mcpu=arc600\n");
fprintf (stream, " -mA7/-mARC700\t\t same as -mcpu=arc700\n");
fprintf (stream, " -mEM\t\t\t same as -mcpu=arcem\n");
fprintf (stream, " -mHS\t\t\t same as -mcpu=archs\n");
fprintf (stream, " -mnps400\t\t enable NPS-400 extended instructions\n");
- fprintf (stream, " -mspfp\t\t enable single-precision floating point instructions\n");
- fprintf (stream, " -mdpfp\t\t enable double-precision floating point instructions\n");
+ fprintf (stream, " -mspfp\t\t enable single-precision floating point"
+ " instructions\n");
+ fprintf (stream, " -mdpfp\t\t enable double-precision floating point"
+ " instructions\n");
fprintf (stream, " -mfpuda\t\t enable double-precision assist floating "
"point\n\t\t\t instructions for ARC EM\n");
{
unsigned int i;
int j;
- bfd_boolean found_flag, tmp;
+ bool found_flag, tmp;
extended_bfd_reloc_code_real_type ret = BFD_RELOC_UNUSED;
for (i = 0; i < arc_num_equiv_tab; i++)
{
if (!nflg)
continue;
- found_flag = FALSE;
+ found_flag = false;
unsigned * psflg = (unsigned *)r->flags;
do
{
- tmp = FALSE;
+ tmp = false;
for (j = 0; j < nflg; j++)
if (!strcmp (pflags[j].name,
arc_flag_operands[*psflg].name))
{
- tmp = TRUE;
+ tmp = true;
break;
}
if (!tmp)
{
- found_flag = FALSE;
+ found_flag = false;
break;
}
else
{
- found_flag = TRUE;
+ found_flag = true;
}
++ psflg;
} while (*psflg);
/* All the symbol types that are allowed to be used for
relaxation. */
-static bfd_boolean
+static bool
may_relax_expr (expressionS tok)
{
/* Check if we have unrelaxable relocs. */
default:
break;
case O_plt:
- return FALSE;
+ return false;
}
switch (tok.X_op)
break;
default:
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
/* Checks if flags are in line with relaxable insn. */
-static bfd_boolean
+static bool
relaxable_flag (const struct arc_relaxable_ins *ins,
const struct arc_flags *pflags,
int nflgs)
}
/* If counttrue == nflgs, then all flags have been found. */
- return (counttrue == nflgs ? TRUE : FALSE);
+ return counttrue == nflgs;
}
/* Checks if operands are in line with relaxable insn. */
-static bfd_boolean
+static bool
relaxable_operand (const struct arc_relaxable_ins *ins,
const expressionS *tok,
int ntok)
const expressionS *epr = &tok[i];
if (i != 0 && i >= ntok)
- return FALSE;
+ return false;
switch (*operand)
{
|| epr->X_op == O_add
|| epr->X_op == O_subtract
|| epr->X_op == O_symbol))
- return FALSE;
+ return false;
break;
case REGISTER_DUP:
if ((i <= 0)
|| (epr->X_add_number != tok[i - 1].X_add_number))
- return FALSE;
+ return false;
/* Fall through. */
case REGISTER:
if (epr->X_op != O_register)
- return FALSE;
+ return false;
break;
case REGISTER_S:
if (epr->X_op != O_register)
- return FALSE;
+ return false;
switch (epr->X_add_number)
{
case 12: case 13: case 14: case 15:
break;
default:
- return FALSE;
+ return false;
}
break;
case REGISTER_NO_GP:
if ((epr->X_op != O_register)
|| (epr->X_add_number == 26)) /* 26 is the gp register. */
- return FALSE;
+ return false;
break;
case BRACKET:
if (epr->X_op != O_bracket)
- return FALSE;
+ return false;
break;
default:
/* Don't understand, bail out. */
- return FALSE;
+ return false;
break;
}
operand = &ins->operands[i];
}
- return (i == ntok ? TRUE : FALSE);
+ return i == ntok;
}
/* Return TRUE if this OPDCODE is a candidate for relaxation. */
-static bfd_boolean
+static bool
relax_insn_p (const struct arc_opcode *opcode,
const expressionS *tok,
int ntok,
int nflg)
{
unsigned i;
- bfd_boolean rv = FALSE;
+ bool rv = false;
/* Check the relaxation table. */
for (i = 0; i < arc_num_relaxable_ins && relaxation_state; ++i)
&& relaxable_operand (arc_rlx_ins, tok, ntok)
&& relaxable_flag (arc_rlx_ins, pflags, nflg))
{
- rv = TRUE;
+ rv = true;
frag_now->fr_subtype = arc_relaxable_insns[i].subtype;
memcpy (&frag_now->tc_frag_data.tok, tok,
sizeof (expressionS) * ntok);
struct arc_insn *insn)
{
const expressionS *reloc_exp = NULL;
- unsigned image;
+ unsigned long long image;
const unsigned char *argidx;
int i;
int tokidx = 0;
unsigned char pcrel = 0;
- bfd_boolean needGOTSymbol;
- bfd_boolean has_delay_slot = FALSE;
+ bool needGOTSymbol;
+ bool has_delay_slot = false;
extended_bfd_reloc_code_real_type reloc = BFD_RELOC_UNUSED;
memset (insn, 0, sizeof (*insn));
image = opcode->opcode;
- pr_debug ("%s:%d: assemble_insn: %s using opcode %x\n",
+ pr_debug ("%s:%d: assemble_insn: %s using opcode %llx\n",
frag_now->fr_file, frag_now->fr_line, opcode->name,
opcode->opcode);
/* Regardless if we have a reloc or not mark the instruction
limm if it is the case. */
if (operand->flags & ARC_OPERAND_LIMM)
- insn->has_limm = TRUE;
+ insn->has_limm = true;
switch (t->X_op)
{
image = insert_operand (image, operand, regs, NULL, 0);
break;
}
+ /* Fall through. */
default:
/* This operand needs a relocation. */
- needGOTSymbol = FALSE;
+ needGOTSymbol = false;
switch (t->X_md)
{
case O_plt:
if (opcode->insn_class == JUMP)
- as_bad_where (frag_now->fr_file, frag_now->fr_line,
- _("Unable to use @plt relocatio for insn %s"),
- opcode->name);
- needGOTSymbol = TRUE;
+ as_bad (_("Unable to use @plt relocation for insn %s"),
+ opcode->name);
+ needGOTSymbol = true;
reloc = find_reloc ("plt", opcode->name,
pflags, nflg,
operand->default_reloc);
case O_gotoff:
case O_gotpc:
- needGOTSymbol = TRUE;
+ needGOTSymbol = true;
reloc = ARC_RELOC_TABLE (t->X_md)->reloc;
break;
case O_pcl:
- reloc = ARC_RELOC_TABLE (t->X_md)->reloc;
- if (ARC_SHORT (opcode->mask) || opcode->insn_class == JUMP)
- as_bad_where (frag_now->fr_file, frag_now->fr_line,
- _("Unable to use @pcl relocation for insn %s"),
- opcode->name);
+ if (operand->flags & ARC_OPERAND_LIMM)
+ {
+ reloc = ARC_RELOC_TABLE (t->X_md)->reloc;
+ if (arc_opcode_len (opcode) == 2
+ || opcode->insn_class == JUMP)
+ as_bad (_("Unable to use @pcl relocation for insn %s"),
+ opcode->name);
+ }
+ else
+ {
+ /* This is a relaxed operand which initially was
+ limm, choose whatever we have defined in the
+ opcode as reloc. */
+ reloc = operand->default_reloc;
+ }
break;
case O_sda:
reloc = find_reloc ("sda", opcode->name,
break;
case O_tlsgd:
case O_tlsie:
- needGOTSymbol = TRUE;
+ needGOTSymbol = true;
/* Fall-through. */
case O_tpoff:
fixup = &insn->fixups[insn->nfixups++];
fixup->exp = *t;
fixup->reloc = reloc;
- pcrel = (operand->flags & ARC_OPERAND_PCREL) ? 1 : 0;
+ if ((int) reloc < 0)
+ pcrel = (operand->flags & ARC_OPERAND_PCREL) ? 1 : 0;
+ else
+ {
+ reloc_howto_type *reloc_howto =
+ bfd_reloc_type_lookup (stdoutput,
+ (bfd_reloc_code_real_type) fixup->reloc);
+ pcrel = reloc_howto->pc_relative;
+ }
fixup->pcrel = pcrel;
- fixup->islong = (operand->flags & ARC_OPERAND_LIMM) ?
- TRUE : FALSE;
+ fixup->islong = (operand->flags & ARC_OPERAND_LIMM) != 0;
break;
}
}
/* Check if the instruction has a delay slot. */
if (!strcmp (flg_operand->name, "d"))
- has_delay_slot = TRUE;
-
- /* There is an exceptional case when we cannot insert a flag
- just as it is. The .T flag must be handled in relation with
- the relative address. */
- if (!strcmp (flg_operand->name, "t")
- || !strcmp (flg_operand->name, "nt"))
+ has_delay_slot = true;
+
+ /* There is an exceptional case when we cannot insert a flag just as
+ it is. On ARCv2 the '.t' and '.nt' flags must be handled in
+ relation with the relative address. Unfortunately, some of the
+ ARC700 extensions (NPS400) also have a '.nt' flag that should be
+ handled in the normal way.
+
+ Flag operands don't have an architecture field, so we can't
+ directly validate that FLAG_OPERAND is valid for the current
+ architecture, what we do instead is just validate that we're
+ assembling for an ARCv2 architecture. */
+ if ((selected_cpu.flags & ARC_OPCODE_ARCV2)
+ && (!strcmp (flg_operand->name, "t")
+ || !strcmp (flg_operand->name, "nt")))
{
unsigned bitYoperand = 0;
/* FIXME! move selection bbit/brcc in arc-opc.c. */
fixup->exp = *reloc_exp;
fixup->reloc = -bitYoperand;
fixup->pcrel = pcrel;
- fixup->islong = FALSE;
+ fixup->islong = false;
}
}
else
insn->relax = relax_insn_p (opcode, tok, ntok, pflags, nflg);
- /* Short instruction? */
- insn->short_insn = ARC_SHORT (opcode->mask) ? TRUE : FALSE;
+ /* Instruction length. */
+ insn->len = arc_opcode_len (opcode);
insn->insn = image;
/* Check if the current instruction is legally used. */
if (arc_last_insns[1].has_delay_slot
&& is_br_jmp_insn_p (arc_last_insns[0].opcode))
- as_bad_where (frag_now->fr_file, frag_now->fr_line,
- _("A jump/branch instruction in delay slot."));
+ as_bad (_("Insn %s has a jump/branch instruction %s in its delay slot."),
+ arc_last_insns[1].opcode->name,
+ arc_last_insns[0].opcode->name);
+ if (arc_last_insns[1].has_delay_slot
+ && arc_last_insns[0].has_limm)
+ as_bad (_("Insn %s has an instruction %s with limm in its delay slot."),
+ arc_last_insns[1].opcode->name,
+ arc_last_insns[0].opcode->name);
}
void
if (*r_type_p == BFD_RELOC_32
&& exp->X_op == O_subtract
&& exp->X_op_symbol != NULL
- && exp->X_op_symbol->bsym->section == now_seg)
+ && S_GET_SEGMENT (exp->X_op_symbol) == now_seg)
*r_type_p = BFD_RELOC_ARC_32_PCREL;
}
static void
check_zol (symbolS *s)
{
- switch (arc_mach_type)
+ switch (selected_cpu.mach)
{
case bfd_mach_arc_arcv2:
- if (arc_target & ARC_OPCODE_ARCv2EM)
+ if (selected_cpu.flags & ARC_OPCODE_ARCv2EM)
return;
if (is_br_jmp_insn_p (arc_last_insns[0].opcode)
int
arc_pcrel_adjust (fragS *fragP)
{
+ pr_debug ("arc_pcrel_adjust: address=%ld, fix=%ld, PCrel %s\n",
+ fragP->fr_address, fragP->fr_fix,
+ fragP->tc_frag_data.pcrel ? "Y" : "N");
+
if (!fragP->tc_frag_data.pcrel)
return fragP->fr_address + fragP->fr_fix;
- return 0;
+ /* Take into account the PCL rounding. */
+ return (fragP->fr_address + fragP->fr_fix) & 0x03;
}
/* Initialize the DWARF-2 unwind information for this procedure. */
{
struct symbol *sym;
- sym = hash_find (arc_reg_hash, regname);
+ sym = str_hash_find (arc_reg_hash, regname);
if (sym)
return S_GET_VALUE (sym);
insn_name = xstrdup (p);
restore_line_pointer (c);
+ /* Convert to lower case. */
+ for (p = insn_name; *p; ++p)
+ *p = TOLOWER (*p);
+
/* 2nd: get major opcode. */
if (*input_line_pointer != ',')
{
if (!arcext_section)
{
arcext_section = subseg_new (".arcextmap", 0);
- bfd_set_section_flags (stdoutput, arcext_section,
- SEC_READONLY | SEC_HAS_CONTENTS);
+ bfd_set_section_flags (arcext_section, SEC_READONLY | SEC_HAS_CONTENTS);
}
else
subseg_set (arcext_section, 0);
/* Check the opcode ranges. */
moplow = 0x05;
- mophigh = (arc_target & (ARC_OPCODE_ARCv2EM
- | ARC_OPCODE_ARCv2HS)) ? 0x07 : 0x0a;
+ mophigh = (selected_cpu.flags & (ARC_OPCODE_ARCv2EM
+ | ARC_OPCODE_ARCv2HS)) ? 0x07 : 0x0a;
if ((einsn.major > mophigh) || (einsn.major < moplow))
as_fatal (_("major opcode not in range [0x%02x - 0x%02x]"), moplow, mophigh);
break;
}
- arc_ext_opcodes = arcExtMap_genOpcode (&einsn, arc_target, &errmsg);
+ arc_ext_opcodes = arcExtMap_genOpcode (&einsn, selected_cpu.flags, &errmsg);
if (arc_ext_opcodes == NULL)
{
if (errmsg)
create_extinst_section (&einsn);
}
-static void
+static bool
tokenize_extregister (extRegister_t *ereg, int opertype)
{
char *name;
char c;
char *p;
int number, imode = 0;
- bfd_boolean isCore_p = (opertype == EXT_CORE_REGISTER) ? TRUE : FALSE;
- bfd_boolean isReg_p = (opertype == EXT_CORE_REGISTER
- || opertype == EXT_AUX_REGISTER) ? TRUE : FALSE;
+ bool isCore_p = opertype == EXT_CORE_REGISTER;
+ bool isReg_p = opertype == EXT_CORE_REGISTER || opertype == EXT_AUX_REGISTER;
/* 1st: get register name. */
SKIP_WHITESPACE ();
if (*input_line_pointer != ',')
{
- as_bad (_("expected comma after register name"));
+ as_bad (_("expected comma after name"));
ignore_rest_of_line ();
free (name);
- return;
+ return false;
}
input_line_pointer++;
number = get_absolute_expression ();
- if (number < 0)
+ if ((number < 0)
+ && (opertype != EXT_AUX_REGISTER))
{
- as_bad (_("negative operand number %d"), number);
+ as_bad (_("%s second argument cannot be a negative number %d"),
+ isCore_p ? "extCoreRegister's" : "extCondCode's",
+ number);
ignore_rest_of_line ();
free (name);
- return;
+ return false;
}
if (isReg_p)
as_bad (_("expected comma after register number"));
ignore_rest_of_line ();
free (name);
- return;
+ return false;
}
input_line_pointer++;
mode = input_line_pointer;
- if (!strncmp (mode, "r|w", 3))
+ if (startswith (mode, "r|w"))
{
imode = 0;
input_line_pointer += 3;
}
- else if (!strncmp (mode, "r", 1))
+ else if (startswith (mode, "r"))
{
imode = ARC_REGISTER_READONLY;
input_line_pointer += 1;
}
- else if (strncmp (mode, "w", 1))
+ else if (!startswith (mode, "w"))
{
as_bad (_("invalid mode"));
ignore_rest_of_line ();
free (name);
- return;
+ return false;
}
else
{
as_bad (_("expected comma after register mode"));
ignore_rest_of_line ();
free (name);
- return;
+ return false;
}
input_line_pointer++;
- if (!strncmp (input_line_pointer, "cannot_shortcut", 15))
+ if (startswith (input_line_pointer, "cannot_shortcut"))
{
imode |= ARC_REGISTER_NOSHORT_CUT;
input_line_pointer += 15;
}
- else if (strncmp (input_line_pointer, "can_shortcut", 12))
+ else if (!startswith (input_line_pointer, "can_shortcut"))
{
as_bad (_("shortcut designator invalid"));
ignore_rest_of_line ();
free (name);
- return;
+ return false;
}
else
{
ereg->name = name;
ereg->number = number;
ereg->imode = imode;
+ return true;
}
/* Create an extension register/condition description in the arc
[2]: Value.
[3]+ Name.
- For auxilirary registers:
+ For auxiliary registers:
[2..5]: Value.
[6]+ Name
{
extRegister_t ereg;
struct arc_aux_reg *auxr;
- const char *retval;
struct arc_flag_operand *ccode;
memset (&ereg, 0, sizeof (ereg));
- tokenize_extregister (&ereg, opertype);
+ if (!tokenize_extregister (&ereg, opertype))
+ return;
switch (opertype)
{
/* Auxiliary register. */
auxr = XNEW (struct arc_aux_reg);
auxr->name = ereg.name;
- auxr->cpu = arc_target;
+ auxr->cpu = selected_cpu.flags;
auxr->subclass = NONE;
auxr->address = ereg.number;
- retval = hash_insert (arc_aux_hash, auxr->name, (void *) auxr);
- if (retval)
- as_fatal (_("internal error: can't hash aux register '%s': %s"),
- auxr->name, retval);
+ if (str_hash_insert (arc_aux_hash, auxr->name, auxr, 0) != NULL)
+ as_bad (_("duplicate aux register %s"), auxr->name);
break;
case EXT_COND_CODE:
/* Condition code. */
ext_condcode.arc_ext_condcode =
XRESIZEVEC (struct arc_flag_operand, ext_condcode.arc_ext_condcode,
ext_condcode.size + 1);
- if (ext_condcode.arc_ext_condcode == NULL)
- as_fatal (_("Virtual memory exhausted"));
ccode = ext_condcode.arc_ext_condcode + ext_condcode.size - 1;
ccode->name = ereg.name;
create_extcore_section (&ereg, opertype);
}
+/* Parse a .arc_attribute directive. */
+
+static void
+arc_attribute (int ignored ATTRIBUTE_UNUSED)
+{
+ int tag = obj_elf_vendor_attribute (OBJ_ATTR_PROC);
+
+ if (tag < NUM_KNOWN_OBJ_ATTRIBUTES)
+ attributes_set_explicitly[tag] = true;
+}
+
+/* Set an attribute if it has not already been set by the user. */
+
+static void
+arc_set_attribute_int (int tag, int value)
+{
+ if (tag < 1
+ || tag >= NUM_KNOWN_OBJ_ATTRIBUTES
+ || !attributes_set_explicitly[tag])
+ bfd_elf_add_proc_attr_int (stdoutput, tag, value);
+}
+
+static void
+arc_set_attribute_string (int tag, const char *value)
+{
+ if (tag < 1
+ || tag >= NUM_KNOWN_OBJ_ATTRIBUTES
+ || !attributes_set_explicitly[tag])
+ bfd_elf_add_proc_attr_string (stdoutput, tag, value);
+}
+
+/* Allocate and concatenate two strings. s1 can be NULL but not
+ s2. s1 pointer is freed at end of this procedure. */
+
+static char *
+arc_stralloc (char * s1, const char * s2)
+{
+ char * p;
+ int len = 0;
+
+ if (s1)
+ len = strlen (s1) + 1;
+
+ /* Only s1 can be null. */
+ gas_assert (s2);
+ len += strlen (s2) + 1;
+
+ p = (char *) xmalloc (len);
+
+ if (s1)
+ {
+ strcpy (p, s1);
+ strcat (p, ",");
+ strcat (p, s2);
+ free (s1);
+ }
+ else
+ strcpy (p, s2);
+
+ return p;
+}
+
+/* Set the public ARC object attributes. */
+
+static void
+arc_set_public_attributes (void)
+{
+ int base = 0;
+ char *s = NULL;
+ unsigned int i;
+
+ /* Tag_ARC_CPU_name. */
+ arc_set_attribute_string (Tag_ARC_CPU_name, selected_cpu.name);
+
+ /* Tag_ARC_CPU_base. */
+ switch (selected_cpu.eflags & EF_ARC_MACH_MSK)
+ {
+ case E_ARC_MACH_ARC600:
+ case E_ARC_MACH_ARC601:
+ base = TAG_CPU_ARC6xx;
+ break;
+ case E_ARC_MACH_ARC700:
+ base = TAG_CPU_ARC7xx;
+ break;
+ case EF_ARC_CPU_ARCV2EM:
+ base = TAG_CPU_ARCEM;
+ break;
+ case EF_ARC_CPU_ARCV2HS:
+ base = TAG_CPU_ARCHS;
+ break;
+ default:
+ base = 0;
+ break;
+ }
+ if (attributes_set_explicitly[Tag_ARC_CPU_base]
+ && (base != bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_PROC,
+ Tag_ARC_CPU_base)))
+ as_warn (_("Overwrite explicitly set Tag_ARC_CPU_base"));
+ bfd_elf_add_proc_attr_int (stdoutput, Tag_ARC_CPU_base, base);
+
+ /* Tag_ARC_ABI_osver. */
+ if (attributes_set_explicitly[Tag_ARC_ABI_osver])
+ {
+ int val = bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_PROC,
+ Tag_ARC_ABI_osver);
+
+ selected_cpu.eflags = ((selected_cpu.eflags & ~EF_ARC_OSABI_MSK)
+ | (val & 0x0f << 8));
+ }
+ else
+ {
+ arc_set_attribute_int (Tag_ARC_ABI_osver, E_ARC_OSABI_CURRENT >> 8);
+ }
+
+ /* Tag_ARC_ISA_config. */
+ arc_check_feature();
+
+ for (i = 0; i < ARRAY_SIZE (feature_list); i++)
+ if (selected_cpu.features & feature_list[i].feature)
+ s = arc_stralloc (s, feature_list[i].attr);
+
+ if (s)
+ arc_set_attribute_string (Tag_ARC_ISA_config, s);
+
+ /* Tag_ARC_ISA_mpy_option. */
+ arc_set_attribute_int (Tag_ARC_ISA_mpy_option, mpy_option);
+
+ /* Tag_ARC_ABI_pic. */
+ arc_set_attribute_int (Tag_ARC_ABI_pic, pic_option);
+
+ /* Tag_ARC_ABI_sda. */
+ arc_set_attribute_int (Tag_ARC_ABI_sda, sda_option);
+
+ /* Tag_ARC_ABI_tls. */
+ arc_set_attribute_int (Tag_ARC_ABI_tls, tls_option);
+
+ /* Tag_ARC_ATR_version. */
+ arc_set_attribute_int (Tag_ARC_ATR_version, 1);
+
+ /* Tag_ARC_ABI_rf16. */
+ if (attributes_set_explicitly[Tag_ARC_ABI_rf16]
+ && bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_PROC,
+ Tag_ARC_ABI_rf16)
+ && !rf16_only)
+ {
+ as_warn (_("Overwrite explicitly set Tag_ARC_ABI_rf16 to full "
+ "register file"));
+ bfd_elf_add_proc_attr_int (stdoutput, Tag_ARC_ABI_rf16, 0);
+ }
+}
+
+/* Add the default contents for the .ARC.attributes section. */
+
+void
+arc_md_finish (void)
+{
+ arc_set_public_attributes ();
+
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, selected_cpu.mach))
+ as_fatal (_("could not set architecture and machine"));
+
+ bfd_set_private_flags (stdoutput, selected_cpu.eflags);
+}
+
+void arc_copy_symbol_attributes (symbolS *dest, symbolS *src)
+{
+ ARC_GET_FLAG (dest) = ARC_GET_FLAG (src);
+}
+
+int arc_convert_symbolic_attribute (const char *name)
+{
+ static const struct
+ {
+ const char * name;
+ const int tag;
+ }
+ attribute_table[] =
+ {
+#define T(tag) {#tag, tag}
+ T (Tag_ARC_PCS_config),
+ T (Tag_ARC_CPU_base),
+ T (Tag_ARC_CPU_variation),
+ T (Tag_ARC_CPU_name),
+ T (Tag_ARC_ABI_rf16),
+ T (Tag_ARC_ABI_osver),
+ T (Tag_ARC_ABI_sda),
+ T (Tag_ARC_ABI_pic),
+ T (Tag_ARC_ABI_tls),
+ T (Tag_ARC_ABI_enumsize),
+ T (Tag_ARC_ABI_exceptions),
+ T (Tag_ARC_ABI_double_size),
+ T (Tag_ARC_ISA_config),
+ T (Tag_ARC_ISA_apex),
+ T (Tag_ARC_ISA_mpy_option),
+ T (Tag_ARC_ATR_version)
+#undef T
+ };
+ unsigned int i;
+
+ if (name == NULL)
+ return -1;
+
+ for (i = 0; i < ARRAY_SIZE (attribute_table); i++)
+ if (streq (name, attribute_table[i].name))
+ return attribute_table[i].tag;
+
+ return -1;
+}
+
/* Local variables:
eval: (c-set-style "gnu")
indent-tabs-mode: t