#include "opcode/arc.h"
#include "elf/arc.h"
+#include "../opcodes/arc-ext.h"
/* Defines section. */
#define regno(x) ((x) & 0x3F)
#define is_ir_num(x) (((x) & ~0x3F) == 0)
-#define is_code_density_p(op) (((op)->subclass == CD1 || (op)->subclass == CD2))
+#define is_code_density_p(sc) (((sc) == CD1 || (sc) == CD2))
+#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)->class == BRANCH || (op)->class == JUMP))
#define is_kernel_insn_p(op) (((op)->class == KERNEL))
const char *arc_target_format = DEFAULT_TARGET_FORMAT;
static int byte_order = DEFAULT_BYTE_ORDER;
+/* Arc extension section. */
+static segT arcext_section;
+
/* By default relaxation is disabled. */
static int relaxation_state = 0;
static void arc_lcomm (int);
static void arc_option (int);
static void arc_extra_reloc (int);
-
+static void arc_extinsn (int);
const pseudo_typeS md_pseudo_table[] =
{
{ "lcommon", arc_lcomm, 0 },
{ "cpu", arc_option, 0 },
+ { "extinstruction", arc_extinsn, 0 },
+
{ "tls_gd_ld", arc_extra_reloc, BFD_RELOC_ARC_TLS_GD_LD },
{ "tls_gd_call", arc_extra_reloc, BFD_RELOC_ARC_TLS_GD_CALL },
bfd_boolean has_delay_slot;
} arc_last_insns[2];
+/* Extension instruction suffix classes. */
+typedef struct
+{
+ const char *name;
+ int len;
+ int class;
+} attributes_t;
+
+static const attributes_t suffixclass[] =
+{
+ { "SUFFIX_FLAG", 11, ARC_SUFFIX_FLAG },
+ { "SUFFIX_COND", 11, ARC_SUFFIX_COND },
+ { "SUFFIX_NONE", 11, ARC_SUFFIX_NONE }
+};
+
+/* Extension instruction syntax classes. */
+static const attributes_t syntaxclass[] =
+{
+ { "SYNTAX_3OP", 10, ARC_SYNTAX_3OP },
+ { "SYNTAX_2OP", 10, ARC_SYNTAX_2OP }
+};
+
+/* Extension instruction syntax classes modifiers. */
+static const attributes_t syntaxclassmod[] =
+{
+ { "OP1_IMM_IMPLIED" , 15, ARC_OP1_IMM_IMPLIED },
+ { "OP1_MUST_BE_IMM" , 15, ARC_OP1_MUST_BE_IMM }
+};
+
+/* Structure to hold an entry in ARC_OPCODE_HASH. */
+struct arc_opcode_hash_entry
+{
+ /* The number of pointers in the OPCODE list. */
+ size_t count;
+
+ /* Points to a list of opcode pointers. */
+ const struct arc_opcode **opcode;
+};
+
+/* Structure used for iterating through an arc_opcode_hash_entry. */
+struct arc_opcode_hash_entry_iterator
+{
+ /* Index into the OPCODE element of the arc_opcode_hash_entry. */
+ size_t index;
+
+ /* The specific ARC_OPCODE from the ARC_OPCODES table that was last
+ returned by this iterator. */
+ const struct arc_opcode *opcode;
+};
+
/* Forward declaration. */
static void assemble_insn
(const struct arc_opcode *, const expressionS *, int,
/* Functions implementation. */
+/* Return a pointer to ARC_OPCODE_HASH_ENTRY that identifies all
+ ARC_OPCODE entries in ARC_OPCODE_HASH that match NAME, or NULL if there
+ are no matching entries in ARC_OPCODE_HASH. */
+
+static const struct arc_opcode_hash_entry *
+arc_find_opcode (const char *name)
+{
+ const struct arc_opcode_hash_entry *entry;
+
+ entry = hash_find (arc_opcode_hash, name);
+ return entry;
+}
+
+/* Initialise the iterator ITER. */
+
+static void
+arc_opcode_hash_entry_iterator_init (struct arc_opcode_hash_entry_iterator *iter)
+{
+ iter->index = 0;
+ iter->opcode = NULL;
+}
+
+/* Return the next ARC_OPCODE from ENTRY, using ITER to hold state between
+ calls to this function. Return NULL when all ARC_OPCODE entries have
+ been returned. */
+
+static const struct arc_opcode *
+arc_opcode_hash_entry_iterator_next (const struct arc_opcode_hash_entry *entry,
+ struct arc_opcode_hash_entry_iterator *iter)
+{
+ if (iter->opcode == NULL && iter->index == 0)
+ {
+ gas_assert (entry->count > 0);
+ iter->opcode = entry->opcode[iter->index];
+ }
+ else if (iter->opcode != NULL)
+ {
+ const char *old_name = iter->opcode->name;
+
+ iter->opcode++;
+ if (iter->opcode->name
+ && (strcmp (old_name, iter->opcode->name) != 0))
+ {
+ iter->index++;
+ if (iter->index == entry->count)
+ iter->opcode = NULL;
+ else
+ iter->opcode = entry->opcode[iter->index];
+ }
+ }
+
+ return iter->opcode;
+}
+
+/* Insert an opcode into opcode hash structure. */
+
+static void
+arc_insert_opcode (const struct arc_opcode *opcode)
+{
+ const char *name, *retval;
+ struct arc_opcode_hash_entry *entry;
+ name = opcode->name;
+
+ entry = hash_find (arc_opcode_hash, name);
+ if (entry == NULL)
+ {
+ entry = xmalloc (sizeof (*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);
+ }
+
+ entry->opcode = xrealloc (entry->opcode,
+ sizeof (const struct arc_opcode *)
+ * (entry->count + 1));
+
+ if (entry->opcode == NULL)
+ as_fatal (_("Virtual memory exhausted"));
+
+ entry->opcode[entry->count] = opcode;
+ entry->count++;
+}
+
+
/* 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. */
md_parse_option (OPTION_MCPU, "archs");
}
else
- as_fatal ("could not find the architecture");
+ 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");
+ as_fatal (_("could not set architecture and machine"));
}
else
if (arc_mach_type != mach)
- as_warn ("Command-line value overrides \".cpu\" directive");
+ as_warn (_("Command-line value overrides \".cpu\" directive"));
restore_line_pointer (c);
demand_empty_rest_of_line ();
bad_cpu:
restore_line_pointer (c);
- as_bad ("invalid identifier for \".cpu\"");
+ as_bad (_("invalid identifier for \".cpu\""));
ignore_rest_of_line ();
}
if (num_flags >= nflg)
goto err;
- flgnamelen = strspn (input_line_pointer, "abcdefghilmnopqrstvwxz");
- if (flgnamelen > MAX_FLAG_NAME_LENGHT)
+ flgnamelen = strspn (input_line_pointer,
+ "abcdefghijklmnopqrstuvwxyz0123456789");
+ if (flgnamelen > MAX_FLAG_NAME_LENGTH)
goto err;
memcpy (flags->name, input_line_pointer, flgnamelen);
return allocate_tok (tok, ntok - 1, cidx);
}
+/* Check if an particular ARC feature is enabled. */
+
+static bfd_boolean
+check_cpu_feature (insn_subclass_t sc)
+{
+ if (!(arc_features & ARC_CD)
+ && is_code_density_p (sc))
+ return FALSE;
+
+ if (!(arc_features & ARC_SPFP)
+ && is_spfp_p (sc))
+ return FALSE;
+
+ if (!(arc_features & ARC_DPFP)
+ && is_dpfp_p (sc))
+ return FALSE;
+
+ if (!(arc_features & ARC_FPUDA)
+ && is_fpuda_p (sc))
+ return FALSE;
+
+ return TRUE;
+}
+
/* Search forward through all variants of an opcode looking for a
syntax match. */
static const struct arc_opcode *
-find_opcode_match (const struct arc_opcode *first_opcode,
+find_opcode_match (const struct arc_opcode_hash_entry *entry,
expressionS *tok,
int *pntok,
struct arc_flags *first_pflag,
int nflgs,
int *pcpumatch)
{
- const struct arc_opcode *opcode = first_opcode;
+ 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;
expressionS emptyE;
+ arc_opcode_hash_entry_iterator_init (&iter);
memset (&emptyE, 0, sizeof (emptyE));
memcpy (bktok, tok, MAX_INSN_ARGS * sizeof (*tok));
bkntok = ntok;
- do
+ for (opcode = arc_opcode_hash_entry_iterator_next (entry, &iter);
+ opcode != NULL;
+ opcode = arc_opcode_hash_entry_iterator_next (entry, &iter))
{
const unsigned char *opidx;
const unsigned char *flgidx;
if (!(opcode->cpu & arc_target))
goto match_failed;
- if (is_code_density_p (opcode) && !(arc_features & ARC_CD))
+ if (!check_cpu_feature (opcode->subclass))
goto match_failed;
got_cpu_match = 1;
++ntok;
break;
+ case O_symbol:
+ {
+ const char *p;
+ size_t len;
+ const struct arc_aux_reg *auxr;
+ unsigned j;
+
+ if (opcode->class != AUXREG)
+ goto de_fault;
+ p = S_GET_NAME (tok[tokidx].X_add_symbol);
+ len = strlen (p);
+
+ auxr = &arc_aux_regs[0];
+ for (j = 0; j < arc_num_aux_regs; j++, auxr++)
+ if (len == auxr->length
+ && strcasecmp (auxr->name, p) == 0
+ && ((auxr->subclass == NONE)
+ || check_cpu_feature (auxr->subclass)))
+ {
+ /* We modify the token array here, safe in the
+ knowledge, that if this was the wrong choice
+ then the original contents will be restored
+ from BKTOK. */
+ tok[tokidx].X_op = O_constant;
+ tok[tokidx].X_add_number = auxr->address;
+ ARC_SET_FLAG (tok[tokidx].X_add_symbol, ARC_FLAG_AUX);
+ break;
+ }
+
+ if (tok[tokidx].X_op != O_constant)
+ goto de_fault;
+ }
+ /* Fall-through */
case O_constant:
/* Check the range. */
if (operand->bits != 32
break;
}
default:
+ de_fault:
if (operand->default_reloc == 0)
goto match_failed; /* The operand needs relocation. */
memcpy (tok, bktok, MAX_INSN_ARGS * sizeof (*tok));
ntok = bkntok;
}
- while (++opcode - arc_opcodes < (int) arc_num_opcodes
- && !strcmp (opcode->name, first_opcode->name));
if (*pcpumatch)
*pcpumatch = got_cpu_match;
/* Assumes the expressionS *tok is of sufficient size. */
-static const struct arc_opcode *
+static const struct arc_opcode_hash_entry *
find_special_case_pseudo (const char *opname,
int *ntok,
expressionS *tok,
break;
}
- return (const struct arc_opcode *)
- hash_find (arc_opcode_hash, pseudo_insn->mnemonic_r);
+ return arc_find_opcode (pseudo_insn->mnemonic_r);
}
-static const struct arc_opcode *
+static const struct arc_opcode_hash_entry *
find_special_case_flag (const char *opname,
int *nflgs,
struct arc_flags *pflags)
unsigned flag_idx, flag_arr_idx;
size_t flaglen, oplen;
const struct arc_flag_special *arc_flag_special_opcode;
- const struct arc_opcode *opcode;
+ const struct arc_opcode_hash_entry *entry;
/* Search for special case instruction. */
for (i = 0; i < arc_num_flag_special; i++)
flaglen = strlen (flagnm);
if (strcmp (opname + oplen, flagnm) == 0)
{
- opcode = (const struct arc_opcode *)
- hash_find (arc_opcode_hash,
- arc_flag_special_opcode->name);
+ entry = arc_find_opcode (arc_flag_special_opcode->name);
if (*nflgs + 1 > MAX_INSN_FLGS)
break;
memcpy (pflags[*nflgs].name, flagnm, flaglen);
pflags[*nflgs].name[flaglen] = '\0';
(*nflgs)++;
- return opcode;
+ return entry;
}
}
}
/* Used to find special case opcode. */
-static const struct arc_opcode *
+static const struct arc_opcode_hash_entry *
find_special_case (const char *opname,
int *nflgs,
struct arc_flags *pflags,
expressionS *tok,
int *ntok)
{
- const struct arc_opcode *opcode;
+ const struct arc_opcode_hash_entry *entry;
- opcode = find_special_case_pseudo (opname, ntok, tok, nflgs, pflags);
+ entry = find_special_case_pseudo (opname, ntok, tok, nflgs, pflags);
- if (opcode == NULL)
- opcode = find_special_case_flag (opname, nflgs, pflags);
+ if (entry == NULL)
+ entry = find_special_case_flag (opname, nflgs, pflags);
- return opcode;
-}
-
-static void
-preprocess_operands (const struct arc_opcode *opcode,
- expressionS *tok,
- int ntok)
-{
- int i;
- size_t len;
- const char *p;
- unsigned j;
- const struct arc_aux_reg *auxr;
-
- for (i = 0; i < ntok; i++)
- {
- switch (tok[i].X_op)
- {
- case O_illegal:
- case O_absent:
- break; /* Throw and error. */
-
- case O_symbol:
- if (opcode->class != AUXREG)
- break;
- /* Convert the symbol to a constant if possible. */
- p = S_GET_NAME (tok[i].X_add_symbol);
- len = strlen (p);
-
- auxr = &arc_aux_regs[0];
- for (j = 0; j < arc_num_aux_regs; j++, auxr++)
- if (len == auxr->length
- && strcasecmp (auxr->name, p) == 0)
- {
- tok[i].X_op = O_constant;
- tok[i].X_add_number = auxr->address;
- break;
- }
- break;
- default:
- break;
- }
- }
+ return entry;
}
/* Given an opcode name, pre-tockenized set of argumenst and the
int nflgs)
{
bfd_boolean found_something = FALSE;
- const struct arc_opcode *opcode;
+ const struct arc_opcode_hash_entry *entry;
int cpumatch = 1;
/* Search opcodes. */
- opcode = (const struct arc_opcode *) hash_find (arc_opcode_hash, opname);
+ entry = arc_find_opcode (opname);
/* Couldn't find opcode conventional way, try special cases. */
- if (!opcode)
- opcode = find_special_case (opname, &nflgs, pflags, tok, &ntok);
+ if (entry == NULL)
+ entry = find_special_case (opname, &nflgs, pflags, tok, &ntok);
- if (opcode)
+ if (entry != NULL)
{
- pr_debug ("%s:%d: assemble_tokens: %s trying opcode 0x%08X\n",
- frag_now->fr_file, frag_now->fr_line, opcode->name,
- opcode->opcode);
-
- preprocess_operands (opcode, tok, ntok);
+ const struct arc_opcode *opcode;
+ pr_debug ("%s:%d: assemble_tokens: %s\n",
+ frag_now->fr_file, frag_now->fr_line, opname);
found_something = TRUE;
- opcode = find_opcode_match (opcode, tok, &ntok, pflags, nflgs, &cpumatch);
- if (opcode)
+ opcode = find_opcode_match (entry, tok, &ntok, pflags,
+ nflgs, &cpumatch);
+ if (opcode != NULL)
{
struct arc_insn insn;
+
assemble_insn (opcode, tok, ntok, pflags, nflgs, &insn);
emit_insn (&insn);
return;
err = hash_insert (arc_reg_hash, S_GET_NAME (regS), (void *) regS);
if (err)
- as_fatal ("Inserting \"%s\" into register table failed: %s",
+ as_fatal (_("Inserting \"%s\" into register table failed: %s"),
name, err);
}
void
md_begin (void)
{
- unsigned int i;
+ const struct arc_opcode *opcode = arc_opcodes;
if (!mach_type_specified_p)
arc_select_cpu ("arc700");
as_fatal (_("Virtual memory exhausted"));
/* Initialize the hash table with the insns. */
- for (i = 0; i < arc_num_opcodes;)
+ do
{
- const char *name, *retval;
+ const char *name = opcode->name;
- name = arc_opcodes[i].name;
- retval = hash_insert (arc_opcode_hash, name, (void *) &arc_opcodes[i]);
- if (retval)
- as_fatal (_("internal error: can't hash opcode '%s': %s"),
- name, retval);
+ arc_insert_opcode (opcode);
- while (++i < arc_num_opcodes
- && (arc_opcodes[i].name == name
- || !strcmp (arc_opcodes[i].name, name)))
+ while (++opcode && opcode->name
+ && (opcode->name == name
+ || !strcmp (opcode->name, name)))
continue;
- }
+ }while (opcode->name);
/* Register declaration. */
arc_reg_hash = hash_new ();
of LITTLENUMS emitted is stored in *sizeP. An error message is
returned, or NULL on OK. */
-char *
+const char *
md_atof (int type, char *litP, int *sizeP)
{
return ieee_md_atof (type, litP, sizeP, target_big_endian);
arc700, av2em, av2hs. */
int
-md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+md_parse_option (int c, const char *arg ATTRIBUTE_UNUSED)
{
switch (c)
{
/* 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"));
break;
case OPTION_RELAX:
case OPTION_EA:
case OPTION_MUL64:
case OPTION_SIMD:
+ /* Dummy options are accepted but have no effect. */
+ break;
+
case OPTION_SPFP:
+ arc_features |= ARC_SPFP;
+ break;
+
case OPTION_DPFP:
+ arc_features |= ARC_DPFP;
+ break;
+
case OPTION_XMAC_D16:
case OPTION_XMAC_24:
case OPTION_DSP_PACKA:
case OPTION_LOCK:
case OPTION_SWAPE:
case OPTION_RTSC:
- case OPTION_FPUDA:
/* Dummy options are accepted but have no effect. */
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"));
+ break;
+
default:
return 0;
}
switch (t->X_md)
{
case O_plt:
+ if (opcode->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;
reloc = find_reloc ("plt", opcode->name,
pflags, nflg,
break;
case O_pcl:
reloc = ARC_RELOC_TABLE (t->X_md)->reloc;
- if (ARC_SHORT (opcode->mask))
+ if (ARC_SHORT (opcode->mask) || opcode->class == JUMP)
as_bad_where (frag_now->fr_file, frag_now->fr_line,
_("Unable to use @pcl relocation for insn %s"),
opcode->name);
return -1;
}
+
+/* Adjust the symbol table. Delete found AUX register symbols. */
+
+void
+arc_adjust_symtab (void)
+{
+ symbolS * sym;
+
+ for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
+ {
+ /* I've created a symbol during parsing process. Now, remove
+ the symbol as it is found to be an AUX register. */
+ if (ARC_GET_FLAG (sym) & ARC_FLAG_AUX)
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ }
+
+ /* Now do generic ELF adjustments. */
+ elf_adjust_symtab ();
+}
+
+static void
+tokenize_extinsn (extInstruction_t *einsn)
+{
+ char *p, c;
+ char *insn_name;
+ unsigned char major_opcode;
+ unsigned char sub_opcode;
+ unsigned char syntax_class = 0;
+ unsigned char syntax_class_modifiers = 0;
+ unsigned char suffix_class = 0;
+ unsigned int i;
+
+ SKIP_WHITESPACE ();
+
+ /* 1st: get instruction name. */
+ p = input_line_pointer;
+ c = get_symbol_name (&p);
+
+ insn_name = xstrdup (p);
+ restore_line_pointer (c);
+
+ /* 2nd: get major opcode. */
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after instruction name"));
+ ignore_rest_of_line ();
+ return;
+ }
+ input_line_pointer++;
+ major_opcode = get_absolute_expression ();
+
+ /* 3rd: get sub-opcode. */
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after major opcode"));
+ ignore_rest_of_line ();
+ return;
+ }
+ input_line_pointer++;
+ sub_opcode = get_absolute_expression ();
+
+ /* 4th: get suffix class. */
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad ("expected comma after sub opcode");
+ ignore_rest_of_line ();
+ return;
+ }
+ input_line_pointer++;
+
+ while (1)
+ {
+ SKIP_WHITESPACE ();
+
+ for (i = 0; i < ARRAY_SIZE (suffixclass); i++)
+ {
+ if (!strncmp (suffixclass[i].name, input_line_pointer,
+ suffixclass[i].len))
+ {
+ suffix_class |= suffixclass[i].class;
+ input_line_pointer += suffixclass[i].len;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE (suffixclass))
+ {
+ as_bad ("invalid suffix class");
+ ignore_rest_of_line ();
+ return;
+ }
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer == '|')
+ input_line_pointer++;
+ else
+ break;
+ }
+
+ /* 5th: get syntax class and syntax class modifiers. */
+ if (*input_line_pointer != ',')
+ {
+ as_bad ("expected comma after suffix class");
+ ignore_rest_of_line ();
+ return;
+ }
+ input_line_pointer++;
+
+ while (1)
+ {
+ SKIP_WHITESPACE ();
+
+ for (i = 0; i < ARRAY_SIZE (syntaxclassmod); i++)
+ {
+ if (!strncmp (syntaxclassmod[i].name,
+ input_line_pointer,
+ syntaxclassmod[i].len))
+ {
+ syntax_class_modifiers |= syntaxclassmod[i].class;
+ input_line_pointer += syntaxclassmod[i].len;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE (syntaxclassmod))
+ {
+ for (i = 0; i < ARRAY_SIZE (syntaxclass); i++)
+ {
+ if (!strncmp (syntaxclass[i].name,
+ input_line_pointer,
+ syntaxclass[i].len))
+ {
+ syntax_class |= syntaxclass[i].class;
+ input_line_pointer += syntaxclass[i].len;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE (syntaxclass))
+ {
+ as_bad ("missing syntax class");
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer == '|')
+ input_line_pointer++;
+ else
+ break;
+ }
+
+ demand_empty_rest_of_line ();
+
+ einsn->name = insn_name;
+ einsn->major = major_opcode;
+ einsn->minor = sub_opcode;
+ einsn->syntax = syntax_class;
+ einsn->modsyn = syntax_class_modifiers;
+ einsn->suffix = suffix_class;
+ einsn->flags = syntax_class
+ | (syntax_class_modifiers & ARC_OP1_IMM_IMPLIED ? 0x10 : 0);
+}
+
+/* Generate an extension section. */
+
+static int
+arc_set_ext_seg (void)
+{
+ if (!arcext_section)
+ {
+ arcext_section = subseg_new (".arcextmap", 0);
+ bfd_set_section_flags (stdoutput, arcext_section,
+ SEC_READONLY | SEC_HAS_CONTENTS);
+ }
+ else
+ subseg_set (arcext_section, 0);
+ return 1;
+}
+
+/* Create an extension instruction description in the arc extension
+ section of the output file.
+ The structure for an instruction is like this:
+ [0]: Length of the record.
+ [1]: Type of the record.
+
+ [2]: Major opcode.
+ [3]: Sub-opcode.
+ [4]: Syntax (flags).
+ [5]+ Name instruction.
+
+ The sequence is terminated by an empty entry. */
+
+static void
+create_extinst_section (extInstruction_t *einsn)
+{
+
+ segT old_sec = now_seg;
+ int old_subsec = now_subseg;
+ char *p;
+ int name_len = strlen (einsn->name);
+
+ arc_set_ext_seg ();
+
+ p = frag_more (1);
+ *p = 5 + name_len + 1;
+ p = frag_more (1);
+ *p = EXT_INSTRUCTION;
+ p = frag_more (1);
+ *p = einsn->major;
+ p = frag_more (1);
+ *p = einsn->minor;
+ p = frag_more (1);
+ *p = einsn->flags;
+ p = frag_more (name_len + 1);
+ strcpy (p, einsn->name);
+
+ subseg_set (old_sec, old_subsec);
+}
+
+/* Handler .extinstruction pseudo-op. */
+
+static void
+arc_extinsn (int ignore ATTRIBUTE_UNUSED)
+{
+ extInstruction_t einsn;
+ struct arc_opcode *arc_ext_opcodes;
+ const char *errmsg = NULL;
+ unsigned char moplow, mophigh;
+
+ memset (&einsn, 0, sizeof (einsn));
+ tokenize_extinsn (&einsn);
+
+ /* Check if the name is already used. */
+ if (arc_find_opcode (einsn.name))
+ as_warn (_("Pseudocode already used %s"), einsn.name);
+
+ /* Check the opcode ranges. */
+ moplow = 0x05;
+ mophigh = (arc_target & (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);
+
+ if ((einsn.minor > 0x3f) && (einsn.major != 0x0a)
+ && (einsn.major != 5) && (einsn.major != 9))
+ as_fatal (_("minor opcode not in range [0x00 - 0x3f]"));
+
+ switch (einsn.syntax & (ARC_SYNTAX_3OP | ARC_SYNTAX_2OP))
+ {
+ case ARC_SYNTAX_3OP:
+ if (einsn.modsyn & ARC_OP1_IMM_IMPLIED)
+ as_fatal (_("Improper use of OP1_IMM_IMPLIED"));
+ break;
+ case ARC_SYNTAX_2OP:
+ if (einsn.modsyn & ARC_OP1_MUST_BE_IMM)
+ as_fatal (_("Improper use of OP1_MUST_BE_IMM"));
+ break;
+ default:
+ break;
+ }
+
+ arc_ext_opcodes = arcExtMap_genOpcode (&einsn, arc_target, &errmsg);
+ if (arc_ext_opcodes == NULL)
+ {
+ if (errmsg)
+ as_fatal ("%s", errmsg);
+ else
+ as_fatal (_("Couldn't generate extension instruction opcodes"));
+ }
+ else if (errmsg)
+ as_warn ("%s", errmsg);
+
+ /* Insert the extension instruction. */
+ arc_insert_opcode ((const struct arc_opcode *) arc_ext_opcodes);
+
+ create_extinst_section (&einsn);
+}
+
+/* Local variables:
+ eval: (c-set-style "gnu")
+ indent-tabs-mode: t
+ End: */