+
+/* Turn an opcode description and a set of arguments into
+ an instruction and a fixup. */
+
+static void
+assemble_insn (const struct arc_opcode *opcode,
+ const expressionS *tok,
+ int ntok,
+ const struct arc_flags *pflags,
+ int nflg,
+ struct arc_insn *insn)
+{
+ const expressionS *reloc_exp = NULL;
+ 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;
+ 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 %llx\n",
+ frag_now->fr_file, frag_now->fr_line, opcode->name,
+ opcode->opcode);
+
+ /* Handle operands. */
+ for (argidx = opcode->operands; *argidx; ++argidx)
+ {
+ const struct arc_operand *operand = &arc_operands[*argidx];
+ const expressionS *t = (const expressionS *) 0;
+
+ if (ARC_OPERAND_IS_FAKE (operand))
+ continue;
+
+ if (operand->flags & ARC_OPERAND_DUPLICATE)
+ {
+ /* Duplicate operand, already inserted. */
+ tokidx ++;
+ continue;
+ }
+
+ if (tokidx >= ntok)
+ {
+ abort ();
+ }
+ else
+ t = &tok[tokidx++];
+
+ /* 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;
+
+ switch (t->X_op)
+ {
+ case O_register:
+ image = insert_operand (image, operand, regno (t->X_add_number),
+ NULL, 0);
+ break;
+
+ case O_constant:
+ image = insert_operand (image, operand, t->X_add_number, NULL, 0);
+ reloc_exp = t;
+ if (operand->flags & ARC_OPERAND_LIMM)
+ insn->limm = t->X_add_number;
+ break;
+
+ case O_bracket:
+ case O_colon:
+ case O_addrtype:
+ /* Ignore brackets, colons, and address types. */
+ break;
+
+ case O_absent:
+ gas_assert (operand->flags & ARC_OPERAND_IGNORE);
+ break;
+
+ case O_subtract:
+ /* Maybe register range. */
+ if ((t->X_add_number == 0)
+ && contains_register (t->X_add_symbol)
+ && contains_register (t->X_op_symbol))
+ {
+ int regs;
+
+ regs = get_register (t->X_add_symbol);
+ regs <<= 16;
+ regs |= get_register (t->X_op_symbol);
+ image = insert_operand (image, operand, regs, NULL, 0);
+ break;
+ }
+ /* Fall through. */
+
+ default:
+ /* This operand needs a relocation. */
+ needGOTSymbol = FALSE;
+
+ switch (t->X_md)
+ {
+ case O_plt:
+ if (opcode->insn_class == JUMP)
+ 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);
+ break;
+
+ case O_gotoff:
+ case O_gotpc:
+ needGOTSymbol = TRUE;
+ reloc = ARC_RELOC_TABLE (t->X_md)->reloc;
+ break;
+ case O_pcl:
+ 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,
+ pflags, nflg,
+ operand->default_reloc);
+ break;
+ case O_tlsgd:
+ case O_tlsie:
+ needGOTSymbol = TRUE;
+ /* Fall-through. */
+
+ case O_tpoff:
+ case O_dtpoff:
+ reloc = ARC_RELOC_TABLE (t->X_md)->reloc;
+ break;
+
+ case O_tpoff9: /*FIXME! Check for the conditionality of
+ the insn. */
+ case O_dtpoff9: /*FIXME! Check for the conditionality of
+ the insn. */
+ as_bad (_("TLS_*_S9 relocs are not supported yet"));
+ break;
+
+ default:
+ /* Just consider the default relocation. */
+ reloc = operand->default_reloc;
+ break;
+ }
+
+ if (needGOTSymbol && (GOT_symbol == NULL))
+ GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
+
+ reloc_exp = t;
+
+#if 0
+ if (reloc > 0)
+ {
+ /* sanity checks. */
+ reloc_howto_type *reloc_howto
+ = bfd_reloc_type_lookup (stdoutput,
+ (bfd_reloc_code_real_type) reloc);
+ unsigned reloc_bitsize = reloc_howto->bitsize;
+ if (reloc_howto->rightshift)
+ reloc_bitsize -= reloc_howto->rightshift;
+ if (reloc_bitsize != operand->bits)
+ {
+ as_bad (_("invalid relocation %s for field"),
+ bfd_get_reloc_code_name (reloc));
+ return;
+ }
+ }
+#endif
+ if (insn->nfixups >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ struct arc_fixup *fixup;
+ fixup = &insn->fixups[insn->nfixups++];
+ fixup->exp = *t;
+ fixup->reloc = reloc;
+ 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;
+ break;
+ }
+ }
+
+ /* Handle flags. */
+ for (i = 0; i < nflg; i++)
+ {
+ const struct arc_flag_operand *flg_operand = pflags[i].flgp;
+
+ /* 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. 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. */
+ if (!strcmp (flg_operand->name, "t"))
+ if (!strcmp (opcode->name, "bbit0")
+ || !strcmp (opcode->name, "bbit1"))
+ bitYoperand = arc_NToperand;
+ else
+ bitYoperand = arc_Toperand;
+ else
+ if (!strcmp (opcode->name, "bbit0")
+ || !strcmp (opcode->name, "bbit1"))
+ bitYoperand = arc_Toperand;
+ else
+ bitYoperand = arc_NToperand;
+
+ gas_assert (reloc_exp != NULL);
+ if (reloc_exp->X_op == O_constant)
+ {
+ /* Check if we have a constant and solved it
+ immediately. */
+ offsetT val = reloc_exp->X_add_number;
+ image |= insert_operand (image, &arc_operands[bitYoperand],
+ val, NULL, 0);
+ }
+ else
+ {
+ struct arc_fixup *fixup;
+
+ if (insn->nfixups >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ fixup = &insn->fixups[insn->nfixups++];
+ fixup->exp = *reloc_exp;
+ fixup->reloc = -bitYoperand;
+ fixup->pcrel = pcrel;
+ fixup->islong = FALSE;
+ }
+ }
+ else
+ image |= (flg_operand->code & ((1 << flg_operand->bits) - 1))
+ << flg_operand->shift;
+ }
+
+ insn->relax = relax_insn_p (opcode, tok, ntok, pflags, nflg);
+
+ /* Instruction length. */
+ insn->len = arc_opcode_len (opcode);
+
+ insn->insn = image;
+
+ /* Update last insn status. */
+ arc_last_insns[1] = arc_last_insns[0];
+ arc_last_insns[0].opcode = opcode;
+ arc_last_insns[0].has_limm = insn->has_limm;
+ arc_last_insns[0].has_delay_slot = has_delay_slot;
+
+ /* 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 (_("Insn %s has a jump/branch instruction %s in its delay slot."),
+ arc_last_insns[1].opcode->name,
+ arc_last_insns[0].opcode->name);
+}
+
+void
+arc_handle_align (fragS* fragP)
+{
+ if ((fragP)->fr_type == rs_align_code)
+ {
+ char *dest = (fragP)->fr_literal + (fragP)->fr_fix;
+ valueT count = ((fragP)->fr_next->fr_address
+ - (fragP)->fr_address - (fragP)->fr_fix);
+
+ (fragP)->fr_var = 2;
+
+ if (count & 1)/* Padding in the gap till the next 2-byte
+ boundary with 0s. */
+ {
+ (fragP)->fr_fix++;
+ *dest++ = 0;
+ }
+ /* Writing nop_s. */
+ md_number_to_chars (dest, NOP_OPCODE_S, 2);
+ }
+}
+
+/* Here we decide which fixups can be adjusted to make them relative
+ to the beginning of the section instead of the symbol. Basically
+ we need to make sure that the dynamic relocations are done
+ correctly, so in some cases we force the original symbol to be
+ used. */
+
+int
+tc_arc_fix_adjustable (fixS *fixP)
+{
+
+ /* Prevent all adjustments to global symbols. */
+ if (S_IS_EXTERNAL (fixP->fx_addsy))
+ return 0;
+ if (S_IS_WEAK (fixP->fx_addsy))
+ return 0;
+
+ /* Adjust_reloc_syms doesn't know about the GOT. */
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_ARC_GOTPC32:
+ 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:
+ return 0;
+
+ default:
+ break;
+ }
+
+ return 1;
+}
+
+/* Compute the reloc type of an expression EXP. */
+
+static void
+arc_check_reloc (expressionS *exp,
+ bfd_reloc_code_real_type *r_type_p)
+{
+ 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)
+ *r_type_p = BFD_RELOC_ARC_32_PCREL;
+}
+
+
+/* Add expression EXP of SIZE bytes to offset OFF of fragment FRAG. */
+
+void
+arc_cons_fix_new (fragS *frag,
+ int off,
+ int size,
+ expressionS *exp,
+ bfd_reloc_code_real_type r_type)
+{
+ r_type = BFD_RELOC_UNUSED;
+
+ switch (size)
+ {
+ case 1:
+ r_type = BFD_RELOC_8;
+ break;
+
+ case 2:
+ r_type = BFD_RELOC_16;
+ break;
+
+ case 3:
+ r_type = BFD_RELOC_24;
+ break;
+
+ case 4:
+ r_type = BFD_RELOC_32;
+ arc_check_reloc (exp, &r_type);
+ break;
+
+ case 8:
+ r_type = BFD_RELOC_64;
+ break;
+
+ default:
+ as_bad (_("unsupported BFD relocation size %u"), size);
+ r_type = BFD_RELOC_UNUSED;
+ }
+
+ fix_new_exp (frag, off, size, exp, 0, r_type);
+}
+
+/* The actual routine that checks the ZOL conditions. */
+
+static void
+check_zol (symbolS *s)
+{
+ switch (selected_cpu.mach)
+ {
+ case bfd_mach_arc_arcv2:
+ if (selected_cpu.flags & ARC_OPCODE_ARCv2EM)
+ return;
+
+ if (is_br_jmp_insn_p (arc_last_insns[0].opcode)
+ || arc_last_insns[1].has_delay_slot)
+ as_bad (_("Jump/Branch instruction detected at the end of the ZOL label @%s"),
+ S_GET_NAME (s));
+
+ break;
+ case bfd_mach_arc_arc600:
+
+ if (is_kernel_insn_p (arc_last_insns[0].opcode))
+ as_bad (_("Kernel instruction detected at the end of the ZOL label @%s"),
+ S_GET_NAME (s));
+
+ if (arc_last_insns[0].has_limm
+ && is_br_jmp_insn_p (arc_last_insns[0].opcode))
+ as_bad (_("A jump instruction with long immediate detected at the \
+end of the ZOL label @%s"), S_GET_NAME (s));
+
+ /* Fall through. */
+ case bfd_mach_arc_arc700:
+ if (arc_last_insns[0].has_delay_slot)
+ as_bad (_("An illegal use of delay slot detected at the end of the ZOL label @%s"),
+ S_GET_NAME (s));
+
+ break;
+ default:
+ break;
+ }
+}
+
+/* If ZOL end check the last two instruction for illegals. */
+void
+arc_frob_label (symbolS * sym)
+{
+ if (ARC_GET_FLAG (sym) & ARC_FLAG_ZOL)
+ check_zol (sym);
+
+ dwarf2_emit_label (sym);
+}
+
+/* Used because generic relaxation assumes a pc-rel value whilst we
+ also relax instructions that use an absolute value resolved out of
+ relative values (if that makes any sense). An example: 'add r1,
+ r2, @.L2 - .' The symbols . and @.L2 are relative to the section
+ but if they're in the same section we can subtract the section
+ offset relocation which ends up in a resolved value. So if @.L2 is
+ .text + 0x50 and . is .text + 0x10, we can say that .text + 0x50 -
+ .text + 0x40 = 0x10. */
+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;
+
+ /* Take into account the PCL rounding. */
+ return (fragP->fr_address + fragP->fr_fix) & 0x03;
+}
+
+/* Initialize the DWARF-2 unwind information for this procedure. */
+
+void
+tc_arc_frame_initial_instructions (void)
+{
+ /* Stack pointer is register 28. */
+ cfi_add_CFA_def_cfa (28, 0);
+}
+
+int
+tc_arc_regname_to_dw2regnum (char *regname)
+{
+ struct symbol *sym;
+
+ sym = hash_find (arc_reg_hash, regname);
+ if (sym)
+ return S_GET_VALUE (sym);
+
+ 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].attr_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].attr_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].attr_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 = (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);
+
+ 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_MASK)
+ {
+ case ARC_SYNTAX_3OP:
+ if (einsn.modsyn & ARC_OP1_IMM_IMPLIED)
+ as_fatal (_("Improper use of OP1_IMM_IMPLIED"));
+ break;
+ case ARC_SYNTAX_2OP:
+ case ARC_SYNTAX_1OP:
+ case ARC_SYNTAX_NOP:
+ 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, selected_cpu.flags, &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);
+}
+
+static bfd_boolean
+tokenize_extregister (extRegister_t *ereg, int opertype)
+{
+ char *name;
+ char *mode;
+ 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;
+
+ /* 1st: get register name. */
+ SKIP_WHITESPACE ();
+ p = input_line_pointer;
+ c = get_symbol_name (&p);
+
+ name = xstrdup (p);
+ restore_line_pointer (c);
+
+ /* 2nd: get register number. */
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after name"));
+ ignore_rest_of_line ();
+ free (name);
+ return FALSE;
+ }
+ input_line_pointer++;
+ number = get_absolute_expression ();
+
+ if ((number < 0)
+ && (opertype != EXT_AUX_REGISTER))
+ {
+ 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 FALSE;
+ }
+
+ if (isReg_p)
+ {
+ /* 3rd: get register mode. */
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after register number"));
+ ignore_rest_of_line ();
+ free (name);
+ return FALSE;
+ }
+
+ input_line_pointer++;
+ mode = input_line_pointer;
+
+ if (!strncmp (mode, "r|w", 3))
+ {
+ imode = 0;
+ input_line_pointer += 3;
+ }
+ else if (!strncmp (mode, "r", 1))
+ {
+ imode = ARC_REGISTER_READONLY;
+ input_line_pointer += 1;
+ }
+ else if (strncmp (mode, "w", 1))
+ {
+ as_bad (_("invalid mode"));
+ ignore_rest_of_line ();
+ free (name);
+ return FALSE;
+ }
+ else
+ {
+ imode = ARC_REGISTER_WRITEONLY;
+ input_line_pointer += 1;
+ }
+ }
+
+ if (isCore_p)
+ {
+ /* 4th: get core register shortcut. */
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after register mode"));
+ ignore_rest_of_line ();
+ free (name);
+ return FALSE;
+ }
+
+ input_line_pointer++;
+
+ if (!strncmp (input_line_pointer, "cannot_shortcut", 15))
+ {
+ imode |= ARC_REGISTER_NOSHORT_CUT;
+ input_line_pointer += 15;
+ }
+ else if (strncmp (input_line_pointer, "can_shortcut", 12))
+ {
+ as_bad (_("shortcut designator invalid"));
+ ignore_rest_of_line ();
+ free (name);
+ return FALSE;
+ }
+ else
+ {
+ input_line_pointer += 12;
+ }
+ }
+ demand_empty_rest_of_line ();
+
+ ereg->name = name;
+ ereg->number = number;
+ ereg->imode = imode;
+ return TRUE;
+}
+
+/* Create an extension register/condition 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.
+
+ For core regs and condition codes:
+ [2]: Value.
+ [3]+ Name.
+
+ For auxiliary registers:
+ [2..5]: Value.
+ [6]+ Name
+
+ The sequence is terminated by an empty entry. */
+
+static void
+create_extcore_section (extRegister_t *ereg, int opertype)
+{
+ segT old_sec = now_seg;
+ int old_subsec = now_subseg;
+ char *p;
+ int name_len = strlen (ereg->name);
+
+ arc_set_ext_seg ();
+
+ switch (opertype)
+ {
+ case EXT_COND_CODE:
+ case EXT_CORE_REGISTER:
+ p = frag_more (1);
+ *p = 3 + name_len + 1;
+ p = frag_more (1);
+ *p = opertype;
+ p = frag_more (1);
+ *p = ereg->number;
+ break;
+ case EXT_AUX_REGISTER:
+ p = frag_more (1);
+ *p = 6 + name_len + 1;
+ p = frag_more (1);
+ *p = EXT_AUX_REGISTER;
+ p = frag_more (1);
+ *p = (ereg->number >> 24) & 0xff;
+ p = frag_more (1);
+ *p = (ereg->number >> 16) & 0xff;
+ p = frag_more (1);
+ *p = (ereg->number >> 8) & 0xff;
+ p = frag_more (1);
+ *p = (ereg->number) & 0xff;
+ break;
+ default:
+ break;
+ }
+
+ p = frag_more (name_len + 1);
+ strcpy (p, ereg->name);
+
+ subseg_set (old_sec, old_subsec);
+}
+
+/* Handler .extCoreRegister pseudo-op. */
+
+static void
+arc_extcorereg (int opertype)
+{
+ extRegister_t ereg;
+ struct arc_aux_reg *auxr;
+ const char *retval;
+ struct arc_flag_operand *ccode;
+
+ memset (&ereg, 0, sizeof (ereg));
+ if (!tokenize_extregister (&ereg, opertype))
+ return;
+
+ switch (opertype)
+ {
+ case EXT_CORE_REGISTER:
+ /* Core register. */
+ if (ereg.number > 60)
+ as_bad (_("core register %s value (%d) too large"), ereg.name,
+ ereg.number);
+ declare_register (ereg.name, ereg.number);
+ break;
+ case EXT_AUX_REGISTER:
+ /* Auxiliary register. */
+ auxr = XNEW (struct arc_aux_reg);
+ auxr->name = ereg.name;
+ 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);
+ break;
+ case EXT_COND_CODE:
+ /* Condition code. */
+ if (ereg.number > 31)
+ as_bad (_("condition code %s value (%d) too large"), ereg.name,
+ ereg.number);
+ ext_condcode.size ++;
+ 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;
+ ccode->code = ereg.number;
+ ccode->bits = 5;
+ ccode->shift = 0;
+ ccode->favail = 0; /* not used. */
+ ccode++;
+ memset (ccode, 0, sizeof (struct arc_flag_operand));
+ break;
+ default:
+ as_bad (_("Unknown extension"));
+ break;
+ }
+ 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 (p == NULL)
+ as_fatal (_("Virtual memory exhausted"));
+
+ 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);
+}
+
+/* Add the default contents for the .ARC.attributes section. */
+
+void
+arc_md_end (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)
+#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
+ End: */