+
+/* 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_hash_entry *entry,
+ expressionS *tok,
+ int *pntok,
+ struct arc_flags *first_pflag,
+ int nflgs,
+ 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;
+ expressionS emptyE;
+
+ arc_opcode_hash_entry_iterator_init (&iter);
+ memset (&emptyE, 0, sizeof (emptyE));
+ memcpy (bktok, tok, MAX_INSN_ARGS * sizeof (*tok));
+ bkntok = ntok;
+
+ 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;
+ int tokidx = 0;
+ const expressionS *t = &emptyE;
+
+ 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 & selected_cpu.flags))
+ goto match_failed;
+
+ if (!check_cpu_feature (opcode->subclass))
+ goto match_failed;
+
+ got_cpu_match = 1;
+ pr_debug ("cpu ");
+
+ /* Check the operands. */
+ for (opidx = opcode->operands; *opidx; ++opidx)
+ {
+ const struct arc_operand *operand = &arc_operands[*opidx];
+
+ /* Only take input from real operands. */
+ if (ARC_OPERAND_IS_FAKE (operand))
+ continue;
+
+ /* When we expect input, make sure we have it. */
+ if (tokidx >= ntok)
+ goto match_failed;
+
+ /* Match operand type with expression type. */
+ switch (operand->flags & ARC_OPERAND_TYPECHECK_MASK)
+ {
+ case ARC_OPERAND_ADDRTYPE:
+ {
+ *errmsg = 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,
+ errmsg);
+ if (*errmsg != NULL)
+ goto match_failed;
+ }
+ break;
+
+ case ARC_OPERAND_IR:
+ /* Check to be a register. */
+ if ((tok[tokidx].X_op != O_register
+ || !is_ir_num (tok[tokidx].X_add_number))
+ && !(operand->flags & ARC_OPERAND_IGNORE))
+ goto match_failed;
+
+ /* If expect duplicate, make sure it is duplicate. */
+ if (operand->flags & ARC_OPERAND_DUPLICATE)
+ {
+ /* Check for duplicate. */
+ if (t->X_op != O_register
+ || !is_ir_num (t->X_add_number)
+ || (regno (t->X_add_number) !=
+ regno (tok[tokidx].X_add_number)))
+ goto match_failed;
+ }
+
+ /* Special handling? */
+ if (operand->insert)
+ {
+ *errmsg = NULL;
+ (*operand->insert)(0,
+ regno (tok[tokidx].X_add_number),
+ errmsg);
+ if (*errmsg)
+ {
+ if (operand->flags & ARC_OPERAND_IGNORE)
+ {
+ /* Missing argument, create one. */
+ if (!allocate_tok (tok, ntok - 1, tokidx))
+ goto match_failed;
+
+ tok[tokidx].X_op = O_absent;
+ ++ntok;
+ }
+ else
+ goto match_failed;
+ }
+ }
+
+ t = &tok[tokidx];
+ break;
+
+ case ARC_OPERAND_BRAKET:
+ /* Check if bracket is also in opcode table as
+ operand. */
+ if (tok[tokidx].X_op != O_bracket)
+ goto match_failed;
+ break;
+
+ case ARC_OPERAND_COLON:
+ /* Check if colon is also in opcode table as operand. */
+ if (tok[tokidx].X_op != O_colon)
+ goto match_failed;
+ break;
+
+ case ARC_OPERAND_LIMM:
+ case ARC_OPERAND_SIGNED:
+ case ARC_OPERAND_UNSIGNED:
+ switch (tok[tokidx].X_op)
+ {
+ case O_illegal:
+ case O_absent:
+ case O_register:
+ goto match_failed;
+
+ case O_bracket:
+ /* Got an (too) early bracket, check if it is an
+ ignored operand. N.B. This procedure works only
+ when bracket is the last operand! */
+ if (!(operand->flags & ARC_OPERAND_IGNORE))
+ goto match_failed;
+ /* Insert the missing operand. */
+ if (!allocate_tok (tok, ntok - 1, tokidx))
+ goto match_failed;
+
+ tok[tokidx].X_op = O_absent;
+ ++ntok;
+ break;
+
+ 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);
+
+ /* 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 = hash_find (arc_aux_hash, tmpp);
+ if (auxr)
+ {
+ /* 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);
+ }
+ free (tmpp);
+
+ if (tok[tokidx].X_op != O_constant)
+ goto de_fault;
+ }
+ /* Fall through. */
+ case O_constant:
+ /* Check the range. */
+ if (operand->bits != 32
+ && !(operand->flags & ARC_OPERAND_NCHK))
+ {
+ offsetT min, max, val;
+ val = tok[tokidx].X_add_number;
+
+ if (operand->flags & ARC_OPERAND_SIGNED)
+ {
+ max = (1 << (operand->bits - 1)) - 1;
+ min = -(1 << (operand->bits - 1));
+ }
+ else
+ {
+ max = (1 << operand->bits) - 1;
+ min = 0;
+ }
+
+ if (val < min || val > max)
+ goto match_failed;
+
+ /* Check alignments. */
+ if ((operand->flags & ARC_OPERAND_ALIGNED32)
+ && (val & 0x03))
+ goto match_failed;
+
+ if ((operand->flags & ARC_OPERAND_ALIGNED16)
+ && (val & 0x01))
+ goto match_failed;
+ }
+ else if (operand->flags & ARC_OPERAND_NCHK)
+ {
+ if (operand->insert)
+ {
+ *errmsg = NULL;
+ (*operand->insert)(0,
+ tok[tokidx].X_add_number,
+ errmsg);
+ if (*errmsg)
+ goto match_failed;
+ }
+ else if (!(operand->flags & ARC_OPERAND_IGNORE))
+ goto match_failed;
+ }
+ break;
+
+ case O_subtract:
+ /* Check if it is register range. */
+ if ((tok[tokidx].X_add_number == 0)
+ && contains_register (tok[tokidx].X_add_symbol)
+ && contains_register (tok[tokidx].X_op_symbol))
+ {
+ int regs;
+
+ regs = get_register (tok[tokidx].X_add_symbol);
+ regs <<= 16;
+ regs |= get_register (tok[tokidx].X_op_symbol);
+ if (operand->insert)
+ {
+ *errmsg = NULL;
+ (*operand->insert)(0,
+ regs,
+ errmsg);
+ if (*errmsg)
+ goto match_failed;
+ }
+ else
+ goto match_failed;
+ break;
+ }
+ /* Fall through. */
+ default:
+ de_fault:
+ if (operand->default_reloc == 0)
+ goto match_failed; /* The operand needs relocation. */
+
+ /* Relocs requiring long immediate. FIXME! make it
+ generic and move it to a function. */
+ switch (tok[tokidx].X_md)
+ {
+ case O_gotoff:
+ case O_gotpc:
+ case O_pcl:
+ case O_tpoff:
+ case O_dtpoff:
+ case O_tlsgd:
+ 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;
+ }
+ break;
+ }
+ /* If expect duplicate, make sure it is duplicate. */
+ if (operand->flags & ARC_OPERAND_DUPLICATE)
+ {
+ if (t->X_op == O_illegal
+ || t->X_op == O_absent
+ || t->X_op == O_register
+ || (t->X_add_number != tok[tokidx].X_add_number))
+ goto match_failed;
+ }
+ t = &tok[tokidx];
+ break;
+
+ default:
+ /* Everything else should have been fake. */
+ abort ();
+ }
+
+ ++tokidx;
+ }
+ pr_debug ("opr ");
+
+ /* Setup ready for flag parsing. */
+ if (!parse_opcode_flags (opcode, nflgs, first_pflag))
+ goto match_failed;
+
+ pr_debug ("flg");
+ /* Possible match -- did we use all of our input? */
+ if (tokidx == ntok)
+ {
+ *pntok = ntok;
+ pr_debug ("\n");
+ return opcode;
+ }
+
+ match_failed:;
+ pr_debug ("\n");
+ /* Restore the original parameters. */
+ memcpy (tok, bktok, MAX_INSN_ARGS * sizeof (*tok));
+ ntok = bkntok;
+ }
+
+ if (*pcpumatch)
+ *pcpumatch = got_cpu_match;
+
+ return NULL;
+}
+
+/* Swap operand tokens. */
+
+static void
+swap_operand (expressionS *operand_array,
+ unsigned source,
+ unsigned destination)
+{
+ expressionS cpy_operand;
+ expressionS *src_operand;
+ expressionS *dst_operand;
+ size_t size;
+
+ if (source == destination)
+ return;
+
+ src_operand = &operand_array[source];
+ dst_operand = &operand_array[destination];
+ size = sizeof (expressionS);
+
+ /* Make copy of operand to swap with and swap. */
+ memcpy (&cpy_operand, dst_operand, size);
+ memcpy (dst_operand, src_operand, size);
+ memcpy (src_operand, &cpy_operand, size);
+}
+
+/* Check if *op matches *tok type.
+ Returns FALSE if they don't match, TRUE if they match. */
+
+static bfd_boolean
+pseudo_operand_match (const expressionS *tok,
+ const struct arc_operand_operation *op)
+{
+ offsetT min, max, val;
+ bfd_boolean ret;
+ const struct arc_operand *operand_real = &arc_operands[op->operand_idx];
+
+ ret = FALSE;
+ switch (tok->X_op)
+ {
+ case O_constant:
+ if (operand_real->bits == 32 && (operand_real->flags & ARC_OPERAND_LIMM))
+ ret = 1;
+ else if (!(operand_real->flags & ARC_OPERAND_IR))
+ {
+ val = tok->X_add_number + op->count;
+ if (operand_real->flags & ARC_OPERAND_SIGNED)
+ {
+ max = (1 << (operand_real->bits - 1)) - 1;
+ min = -(1 << (operand_real->bits - 1));
+ }
+ else
+ {
+ max = (1 << operand_real->bits) - 1;
+ min = 0;
+ }
+ if (min <= val && val <= max)
+ ret = TRUE;
+ }
+ break;
+
+ case O_symbol:
+ /* Handle all symbols as long immediates or signed 9. */
+ if (operand_real->flags & ARC_OPERAND_LIMM
+ || ((operand_real->flags & ARC_OPERAND_SIGNED)
+ && operand_real->bits == 9))
+ ret = TRUE;
+ break;
+
+ case O_register:
+ if (operand_real->flags & ARC_OPERAND_IR)
+ ret = TRUE;
+ break;
+
+ case O_bracket:
+ if (operand_real->flags & ARC_OPERAND_BRAKET)
+ ret = TRUE;
+ break;
+
+ default:
+ /* Unknown. */
+ break;
+ }
+ return ret;
+}
+
+/* Find pseudo instruction in array. */
+
+static const struct arc_pseudo_insn *
+find_pseudo_insn (const char *opname,
+ int ntok,
+ const expressionS *tok)
+{
+ const struct arc_pseudo_insn *pseudo_insn = NULL;
+ const struct arc_operand_operation *op;
+ unsigned int i;
+ int j;
+
+ for (i = 0; i < arc_num_pseudo_insn; ++i)
+ {
+ pseudo_insn = &arc_pseudo_insns[i];
+ if (strcmp (pseudo_insn->mnemonic_p, opname) == 0)
+ {
+ op = pseudo_insn->operand;
+ for (j = 0; j < ntok; ++j)
+ if (!pseudo_operand_match (&tok[j], &op[j]))
+ break;
+
+ /* Found the right instruction. */
+ if (j == ntok)
+ return pseudo_insn;
+ }
+ }
+ return NULL;
+}
+
+/* Assumes the expressionS *tok is of sufficient size. */
+
+static const struct arc_opcode_hash_entry *
+find_special_case_pseudo (const char *opname,
+ int *ntok,
+ expressionS *tok,
+ int *nflgs,
+ struct arc_flags *pflags)
+{
+ const struct arc_pseudo_insn *pseudo_insn = NULL;
+ const struct arc_operand_operation *operand_pseudo;
+ const struct arc_operand *operand_real;
+ unsigned i;
+ char construct_operand[MAX_CONSTR_STR];
+
+ /* Find whether opname is in pseudo instruction array. */
+ pseudo_insn = find_pseudo_insn (opname, *ntok, tok);
+
+ if (pseudo_insn == NULL)
+ return NULL;
+
+ /* Handle flag, Limited to one flag at the moment. */
+ if (pseudo_insn->flag_r != NULL)
+ *nflgs += tokenize_flags (pseudo_insn->flag_r, &pflags[*nflgs],
+ MAX_INSN_FLGS - *nflgs);
+
+ /* Handle operand operations. */
+ for (i = 0; i < pseudo_insn->operand_cnt; ++i)
+ {
+ operand_pseudo = &pseudo_insn->operand[i];
+ operand_real = &arc_operands[operand_pseudo->operand_idx];
+
+ if (operand_real->flags & ARC_OPERAND_BRAKET
+ && !operand_pseudo->needs_insert)
+ continue;
+
+ /* Has to be inserted (i.e. this token does not exist yet). */
+ if (operand_pseudo->needs_insert)
+ {
+ if (operand_real->flags & ARC_OPERAND_BRAKET)
+ {
+ tok[i].X_op = O_bracket;
+ ++(*ntok);
+ continue;
+ }
+
+ /* Check if operand is a register or constant and handle it
+ by type. */
+ if (operand_real->flags & ARC_OPERAND_IR)
+ snprintf (construct_operand, MAX_CONSTR_STR, "r%d",
+ operand_pseudo->count);
+ else
+ snprintf (construct_operand, MAX_CONSTR_STR, "%d",
+ operand_pseudo->count);
+
+ tokenize_arguments (construct_operand, &tok[i], 1);
+ ++(*ntok);
+ }
+
+ else if (operand_pseudo->count)
+ {
+ /* Operand number has to be adjusted accordingly (by operand
+ type). */
+ switch (tok[i].X_op)
+ {
+ case O_constant:
+ tok[i].X_add_number += operand_pseudo->count;
+ break;
+
+ case O_symbol:
+ break;
+
+ default:
+ /* Ignored. */
+ break;
+ }
+ }
+ }
+
+ /* Swap operands if necessary. Only supports one swap at the
+ moment. */
+ for (i = 0; i < pseudo_insn->operand_cnt; ++i)
+ {
+ operand_pseudo = &pseudo_insn->operand[i];
+
+ if (operand_pseudo->swap_operand_idx == i)
+ continue;
+
+ swap_operand (tok, i, operand_pseudo->swap_operand_idx);
+
+ /* Prevent a swap back later by breaking out. */
+ break;
+ }
+
+ return arc_find_opcode (pseudo_insn->mnemonic_r);
+}
+
+static const struct arc_opcode_hash_entry *
+find_special_case_flag (const char *opname,
+ int *nflgs,
+ struct arc_flags *pflags)
+{
+ unsigned int i;
+ const char *flagnm;
+ unsigned flag_idx, flag_arr_idx;
+ size_t flaglen, oplen;
+ const struct arc_flag_special *arc_flag_special_opcode;
+ const struct arc_opcode_hash_entry *entry;
+
+ /* Search for special case instruction. */
+ for (i = 0; i < arc_num_flag_special; i++)
+ {
+ arc_flag_special_opcode = &arc_flag_special_cases[i];
+ oplen = strlen (arc_flag_special_opcode->name);
+
+ if (strncmp (opname, arc_flag_special_opcode->name, oplen) != 0)
+ continue;
+
+ /* Found a potential special case instruction, now test for
+ flags. */
+ for (flag_arr_idx = 0;; ++flag_arr_idx)
+ {
+ flag_idx = arc_flag_special_opcode->flags[flag_arr_idx];
+ if (flag_idx == 0)
+ break; /* End of array, nothing found. */
+
+ flagnm = arc_flag_operands[flag_idx].name;
+ flaglen = strlen (flagnm);
+ if (strcmp (opname + oplen, flagnm) == 0)
+ {
+ 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 entry;
+ }
+ }
+ }
+ return NULL;
+}
+
+/* Used to find special case 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_hash_entry *entry;
+
+ entry = find_special_case_pseudo (opname, ntok, tok, nflgs, pflags);
+
+ if (entry == NULL)
+ entry = find_special_case_flag (opname, 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. */
+
+static void
+assemble_tokens (const char *opname,
+ expressionS *tok,
+ int ntok,
+ struct arc_flags *pflags,
+ int nflgs)
+{
+ bfd_boolean found_something = FALSE;
+ const struct arc_opcode_hash_entry *entry;
+ int cpumatch = 1;
+ const char *errmsg = NULL;
+
+ /* Search opcodes. */
+ entry = arc_find_opcode (opname);
+
+ /* Couldn't find opcode conventional way, try special cases. */
+ if (entry == NULL)
+ entry = find_special_case (opname, &nflgs, pflags, tok, &ntok);
+
+ if (entry != NULL)
+ {
+ 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 (entry, tok, &ntok, pflags,
+ 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)
+ 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,
+ selected_cpu.name);
+ }
+ else
+ as_bad (_("unknown opcode '%s'"), opname);
+}
+
+/* The public interface to the instruction assembler. */
+
+void
+md_assemble (char *str)
+{
+ char *opname;
+ expressionS tok[MAX_INSN_ARGS];
+ int ntok, nflg;
+ size_t opnamelen;
+ struct arc_flags flags[MAX_INSN_FLGS];
+
+ /* Split off the opcode. */
+ opnamelen = strspn (str, "abcdefghijklmnopqrstuvwxyz_0123468");
+ opname = xmemdup0 (str, opnamelen);
+
+ /* Signalize we are assembling the instructions. */
+ assembling_insn = TRUE;
+
+ /* Tokenize the flags. */
+ if ((nflg = tokenize_flags (str + opnamelen, flags, MAX_INSN_FLGS)) == -1)
+ {
+ as_bad (_("syntax error"));
+ return;
+ }
+
+ /* Scan up to the end of the mnemonic which must end in space or end
+ of string. */
+ str += opnamelen;
+ for (; *str != '\0'; str++)
+ if (*str == ' ')
+ break;
+
+ /* Tokenize the rest of the line. */
+ if ((ntok = tokenize_arguments (str, tok, MAX_INSN_ARGS)) < 0)
+ {
+ as_bad (_("syntax error"));
+ return;
+ }
+
+ /* Finish it off. */
+ assemble_tokens (opname, tok, ntok, flags, nflg);
+ 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);
+
+ 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);
+}
+
+/* Construct symbols for each of the general registers. */
+
+static void
+declare_register_set (void)
+{
+ int i;
+ for (i = 0; i < 64; ++i)
+ {
+ char name[7];
+
+ sprintf (name, "r%d", i);
+ declare_register (name, i);
+ if ((i & 0x01) == 0)
+ {
+ sprintf (name, "r%dr%d", i, i+1);
+ declare_register (name, i);
+ }
+ }
+}
+
+/* Construct a symbol for an address type. */
+
+static void
+declare_addrtype (const char *name, int number)
+{
+ const char *err;
+ symbolS *addrtypeS = symbol_create (name, undefined_section,
+ number, &zero_address_frag);
+
+ 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);
+}
+
+/* Port-specific assembler initialization. This function is called
+ once, at assembler startup time. */
+
+void
+md_begin (void)
+{
+ const struct arc_opcode *opcode = arc_opcodes;
+
+ 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, selected_cpu.mach))
+ as_warn (_("could not set architecture and machine"));
+
+ /* Set elf header flags. */
+ 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"));
+
+ /* Initialize the hash table with the insns. */
+ do
+ {
+ const char *name = opcode->name;
+
+ arc_insert_opcode (opcode);
+
+ while (++opcode && opcode->name
+ && (opcode->name == name
+ || !strcmp (opcode->name, name)))
+ continue;
+ }while (opcode->name);
+
+ /* Register declaration. */
+ arc_reg_hash = hash_new ();
+ if (arc_reg_hash == NULL)
+ as_fatal (_("Virtual memory exhausted"));
+
+ declare_register_set ();
+ declare_register ("gp", 26);
+ declare_register ("fp", 27);
+ declare_register ("sp", 28);
+ declare_register ("ilink", 29);
+ declare_register ("ilink1", 29);
+ declare_register ("ilink2", 30);
+ declare_register ("blink", 31);
+
+ /* XY memory registers. */
+ declare_register ("x0_u0", 32);
+ declare_register ("x0_u1", 33);
+ declare_register ("x1_u0", 34);
+ declare_register ("x1_u1", 35);
+ declare_register ("x2_u0", 36);
+ declare_register ("x2_u1", 37);
+ declare_register ("x3_u0", 38);
+ declare_register ("x3_u1", 39);
+ declare_register ("y0_u0", 40);
+ declare_register ("y0_u1", 41);
+ declare_register ("y1_u0", 42);
+ declare_register ("y1_u1", 43);
+ declare_register ("y2_u0", 44);
+ declare_register ("y2_u1", 45);
+ declare_register ("y3_u0", 46);
+ declare_register ("y3_u1", 47);
+ declare_register ("x0_nu", 48);
+ declare_register ("x1_nu", 49);
+ declare_register ("x2_nu", 50);
+ declare_register ("x3_nu", 51);
+ declare_register ("y0_nu", 52);
+ declare_register ("y1_nu", 53);
+ declare_register ("y2_nu", 54);
+ declare_register ("y3_nu", 55);
+
+ declare_register ("mlo", 57);
+ declare_register ("mmid", 58);
+ declare_register ("mhi", 59);
+
+ declare_register ("acc1", 56);
+ declare_register ("acc2", 57);
+
+ declare_register ("lp_count", 60);
+ declare_register ("pcl", 63);
+
+ /* Initialize the last instructions. */
+ 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"));
+
+ 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 & 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);
+ }
+
+ /* Address type declaration. */
+ arc_addrtype_hash = hash_new ();
+ if (arc_addrtype_hash == NULL)
+ as_fatal (_("Virtual memory exhausted"));
+
+ declare_addrtype ("bd", ARC_NPS400_ADDRTYPE_BD);
+ declare_addrtype ("jid", ARC_NPS400_ADDRTYPE_JID);
+ declare_addrtype ("lbd", ARC_NPS400_ADDRTYPE_LBD);
+ declare_addrtype ("mbd", ARC_NPS400_ADDRTYPE_MBD);
+ declare_addrtype ("sd", ARC_NPS400_ADDRTYPE_SD);
+ declare_addrtype ("sm", ARC_NPS400_ADDRTYPE_SM);
+ declare_addrtype ("xa", ARC_NPS400_ADDRTYPE_XA);
+ declare_addrtype ("xd", ARC_NPS400_ADDRTYPE_XD);
+ declare_addrtype ("cd", ARC_NPS400_ADDRTYPE_CD);
+ declare_addrtype ("cbd", ARC_NPS400_ADDRTYPE_CBD);
+ declare_addrtype ("cjid", ARC_NPS400_ADDRTYPE_CJID);
+ declare_addrtype ("clbd", ARC_NPS400_ADDRTYPE_CLBD);
+ declare_addrtype ("cm", ARC_NPS400_ADDRTYPE_CM);
+ declare_addrtype ("csd", ARC_NPS400_ADDRTYPE_CSD);
+ declare_addrtype ("cxa", ARC_NPS400_ADDRTYPE_CXA);
+ declare_addrtype ("cxd", ARC_NPS400_ADDRTYPE_CXD);
+}
+
+/* Write a value out to the object file, using the appropriate
+ endianness. */
+
+void
+md_number_to_chars (char *buf,
+ valueT val,
+ int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+/* Round up a section size to the appropriate boundary. */
+
+valueT
+md_section_align (segT segment,
+ valueT size)
+{
+ int align = bfd_get_section_alignment (stdoutput, segment);
+
+ return ((size + (1 << align) - 1) & (-((valueT) 1 << align)));
+}
+
+/* The location from which a PC relative jump should be calculated,
+ given a PC relative reloc. */
+
+long
+md_pcrel_from_section (fixS *fixP,
+ segT sec)
+{
+ offsetT base = fixP->fx_where + fixP->fx_frag->fr_address;
+
+ pr_debug ("pcrel_from_section, fx_offset = %d\n", (int) fixP->fx_offset);
+
+ if (fixP->fx_addsy != (symbolS *) NULL
+ && (!S_IS_DEFINED (fixP->fx_addsy)
+ || S_GET_SEGMENT (fixP->fx_addsy) != sec))
+ {
+ pr_debug ("Unknown pcrel symbol: %s\n", S_GET_NAME (fixP->fx_addsy));
+
+ /* The symbol is undefined (or is defined but not in this section).
+ Let the linker figure it out. */
+ return 0;
+ }
+
+ if ((int) fixP->fx_r_type < 0)
+ {
+ /* These are the "internal" relocations. Align them to
+ 32 bit boundary (PCL), for the moment. */
+ base &= ~3;
+ }
+ else
+ {
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_ARC_PC32:
+ /* 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
+ subtracted by 4 as we do not support this type of PCrel
+ relocation for short instructions. */
+ base -= 4;
+ /* Fall through. */
+ case BFD_RELOC_ARC_PLT32:
+ case BFD_RELOC_ARC_S25H_PCREL_PLT:
+ case BFD_RELOC_ARC_S21H_PCREL_PLT:
+ case BFD_RELOC_ARC_S25W_PCREL_PLT:
+ case BFD_RELOC_ARC_S21W_PCREL_PLT:
+
+ case BFD_RELOC_ARC_S21H_PCREL:
+ case BFD_RELOC_ARC_S25H_PCREL:
+ case BFD_RELOC_ARC_S13_PCREL:
+ case BFD_RELOC_ARC_S21W_PCREL:
+ case BFD_RELOC_ARC_S25W_PCREL:
+ base &= ~3;
+ break;
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unhandled reloc %s in md_pcrel_from_section"),
+ bfd_get_reloc_code_name (fixP->fx_r_type));
+ break;
+ }
+ }
+
+ 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,
+ fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : "(null)",
+ fixP->fx_addsy ? S_GET_VALUE (fixP->fx_addsy) : 0);
+
+ return base;
+}
+
+/* Given a BFD relocation find the corresponding operand. */
+
+static const struct arc_operand *
+find_operand_for_reloc (extended_bfd_reloc_code_real_type reloc)
+{
+ unsigned i;
+
+ for (i = 0; i < arc_num_operands; i++)
+ if (arc_operands[i].default_reloc == reloc)
+ return &arc_operands[i];
+ return NULL;
+}
+
+/* Insert an operand value into an instruction. */
+
+static unsigned long long
+insert_operand (unsigned long long insn,
+ const struct arc_operand *operand,
+ long long val,
+ const char *file,
+ unsigned line)
+{
+ offsetT min = 0, max = 0;
+
+ if (operand->bits != 32
+ && !(operand->flags & ARC_OPERAND_NCHK)
+ && !(operand->flags & ARC_OPERAND_FAKE))
+ {
+ if (operand->flags & ARC_OPERAND_SIGNED)
+ {
+ max = (1 << (operand->bits - 1)) - 1;
+ min = -(1 << (operand->bits - 1));
+ }
+ else
+ {
+ max = (1 << operand->bits) - 1;
+ min = 0;
+ }
+
+ if (val < min || val > max)
+ as_bad_value_out_of_range (_("operand"),
+ val, min, max, file, line);
+ }
+
+ pr_debug ("insert field: %ld <= %lld <= %ld in 0x%08llx\n",
+ min, val, max, insn);
+
+ if ((operand->flags & ARC_OPERAND_ALIGNED32)
+ && (val & 0x03))
+ as_bad_where (file, line,
+ _("Unaligned operand. Needs to be 32bit aligned"));
+
+ if ((operand->flags & ARC_OPERAND_ALIGNED16)
+ && (val & 0x01))
+ as_bad_where (file, line,
+ _("Unaligned operand. Needs to be 16bit aligned"));
+
+ if (operand->insert)
+ {
+ const char *errmsg = NULL;
+
+ insn = (*operand->insert) (insn, val, &errmsg);
+ if (errmsg)
+ as_warn_where (file, line, "%s", errmsg);
+ }
+ else
+ {
+ if (operand->flags & ARC_OPERAND_TRUNCATE)
+ {
+ if (operand->flags & ARC_OPERAND_ALIGNED32)
+ val >>= 2;
+ if (operand->flags & ARC_OPERAND_ALIGNED16)
+ val >>= 1;
+ }
+ insn |= ((val & ((1 << operand->bits) - 1)) << operand->shift);
+ }
+ return insn;
+}
+
+/* Apply a fixup to the object code. At this point all symbol values
+ should be fully resolved, and we attempt to completely resolve the
+ reloc. If we can not do that, we determine the correct reloc code
+ and put it back in the fixup. To indicate that a fixup has been
+ eliminated, set fixP->fx_done. */
+
+void
+md_apply_fix (fixS *fixP,
+ valueT *valP,
+ segT seg)
+{
+ char * const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
+ valueT value = *valP;
+ unsigned insn = 0;
+ symbolS *fx_addsy, *fx_subsy;
+ offsetT fx_offset;
+ segT add_symbol_segment = absolute_section;
+ segT sub_symbol_segment = absolute_section;
+ const struct arc_operand *operand = NULL;
+ extended_bfd_reloc_code_real_type reloc;
+
+ pr_debug ("%s:%u: apply_fix: r_type=%d (%s) value=0x%lX offset=0x%lX\n",
+ fixP->fx_file, fixP->fx_line, fixP->fx_r_type,
+ ((int) fixP->fx_r_type < 0) ? "Internal":
+ bfd_get_reloc_code_name (fixP->fx_r_type), value,
+ fixP->fx_offset);
+
+ fx_addsy = fixP->fx_addsy;
+ fx_subsy = fixP->fx_subsy;
+ fx_offset = 0;
+
+ if (fx_addsy)
+ {
+ add_symbol_segment = S_GET_SEGMENT (fx_addsy);
+ }
+
+ if (fx_subsy
+ && fixP->fx_r_type != BFD_RELOC_ARC_TLS_DTPOFF
+ && fixP->fx_r_type != BFD_RELOC_ARC_TLS_DTPOFF_S9
+ && fixP->fx_r_type != BFD_RELOC_ARC_TLS_GD_LD)
+ {
+ resolve_symbol_value (fx_subsy);
+ sub_symbol_segment = S_GET_SEGMENT (fx_subsy);
+
+ if (sub_symbol_segment == absolute_section)
+ {
+ /* The symbol is really a constant. */
+ fx_offset -= S_GET_VALUE (fx_subsy);
+ fx_subsy = NULL;
+ }
+ 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",