+
+/* 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",
+ segment_name (add_symbol_segment),
+ S_GET_NAME (fx_subsy),
+ segment_name (sub_symbol_segment));
+ return;
+ }
+ }
+
+ if (fx_addsy
+ && !S_IS_WEAK (fx_addsy))
+ {
+ if (add_symbol_segment == seg
+ && fixP->fx_pcrel)
+ {
+ value += S_GET_VALUE (fx_addsy);
+ value -= md_pcrel_from_section (fixP, seg);
+ fx_addsy = NULL;
+ 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;
+ }
+ }
+
+ if (!fx_addsy)
+ fixP->fx_done = TRUE;
+
+ if (fixP->fx_pcrel)
+ {
+ if (fx_addsy
+ && ((S_IS_DEFINED (fx_addsy)
+ && S_GET_SEGMENT (fx_addsy) != seg)
+ || S_IS_WEAK (fx_addsy)))
+ value += md_pcrel_from_section (fixP, seg);
+
+ switch (fixP->fx_r_type)
+ {
+ 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 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;
+ /* Fall through. */
+ case BFD_RELOC_32:
+ fixP->fx_r_type = BFD_RELOC_ARC_PC32;
+ /* Fall through. */
+ case BFD_RELOC_ARC_PC32:
+ /* fixP->fx_offset += fixP->fx_where - fixP->fx_dot_value; */
+ break;
+ default:
+ if ((int) fixP->fx_r_type < 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PC relative relocation not allowed for (internal)"
+ " type %d"),
+ fixP->fx_r_type);
+ break;
+ }
+ }
+
+ 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);
+
+
+ /* Now check for TLS relocations. */
+ reloc = fixP->fx_r_type;
+ switch (reloc)
+ {
+ case BFD_RELOC_ARC_TLS_DTPOFF:
+ case BFD_RELOC_ARC_TLS_LE_32:
+ if (fixP->fx_done)
+ break;
+ /* Fall through. */
+ case BFD_RELOC_ARC_TLS_GD_GOT:
+ case BFD_RELOC_ARC_TLS_IE_GOT:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ break;
+
+ case BFD_RELOC_ARC_TLS_GD_LD:
+ gas_assert (!fixP->fx_offset);
+ if (fixP->fx_subsy)
+ fixP->fx_offset
+ = (S_GET_VALUE (fixP->fx_subsy)
+ - fixP->fx_frag->fr_address- fixP->fx_where);
+ fixP->fx_subsy = NULL;
+ /* Fall through. */
+ case BFD_RELOC_ARC_TLS_GD_CALL:
+ /* These two relocs are there just to allow ld to change the tls
+ model for this symbol, by patching the code. The offset -
+ and scale, if any - will be installed by the linker. */
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ break;
+
+ case BFD_RELOC_ARC_TLS_LE_S9:
+ case BFD_RELOC_ARC_TLS_DTPOFF_S9:
+ as_bad (_("TLS_*_S9 relocs are not supported yet"));
+ break;
+
+ default:
+ break;
+ }
+
+ if (!fixP->fx_done)
+ {
+ return;
+ }
+
+ /* Adjust the value if we have a constant. */
+ value += fx_offset;
+
+ /* For hosts with longs bigger than 32-bits make sure that the top
+ bits of a 32-bit negative value read in by the parser are set,
+ so that the correct comparisons are made. */
+ if (value & 0x80000000)
+ value |= (-1UL << 31);
+
+ reloc = fixP->fx_r_type;
+ switch (reloc)
+ {
+ case BFD_RELOC_8:
+ case BFD_RELOC_16:
+ case BFD_RELOC_24:
+ case BFD_RELOC_32:
+ case BFD_RELOC_64:
+ case BFD_RELOC_ARC_32_PCREL:
+ md_number_to_chars (fixpos, value, fixP->fx_size);
+ return;
+
+ case BFD_RELOC_ARC_GOTPC32:
+ /* I cannot fix an GOTPC relocation because I need to relax it
+ from ld rx,[pcl,@sym@gotpc] to add rx,pcl,@sym@gotpc. */
+ as_bad (_("Unsupported operation on reloc"));
+ return;
+
+ case BFD_RELOC_ARC_TLS_DTPOFF:
+ 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_PC32:
+ md_number_to_chars_midend (fixpos, value, fixP->fx_size);
+ return;
+
+ case BFD_RELOC_ARC_PLT32:
+ md_number_to_chars_midend (fixpos, value, fixP->fx_size);
+ return;
+
+ case BFD_RELOC_ARC_S25H_PCREL_PLT:
+ reloc = BFD_RELOC_ARC_S25W_PCREL;
+ goto solve_plt;
+
+ case BFD_RELOC_ARC_S21H_PCREL_PLT:
+ reloc = BFD_RELOC_ARC_S21H_PCREL;
+ goto solve_plt;
+
+ case BFD_RELOC_ARC_S25W_PCREL_PLT:
+ reloc = BFD_RELOC_ARC_S25W_PCREL;
+ goto solve_plt;
+
+ 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:
+ case BFD_RELOC_ARC_S21H_PCREL:
+ case BFD_RELOC_ARC_S25H_PCREL:
+ case BFD_RELOC_ARC_S13_PCREL:
+ solve_plt:
+ operand = find_operand_for_reloc (reloc);
+ gas_assert (operand);
+ break;
+
+ default:
+ {
+ if ((int) fixP->fx_r_type >= 0)
+ as_fatal (_("unhandled relocation type %s"),
+ bfd_get_reloc_code_name (fixP->fx_r_type));
+
+ /* The rest of these fixups needs to be completely resolved as
+ constants. */
+ if (fixP->fx_addsy != 0
+ && S_GET_SEGMENT (fixP->fx_addsy) != absolute_section)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("non-absolute expression in constant field"));
+
+ gas_assert (-(int) fixP->fx_r_type < (int) arc_num_operands);
+ operand = &arc_operands[-(int) fixP->fx_r_type];
+ break;
+ }
+ }
+
+ if (target_big_endian)
+ {
+ switch (fixP->fx_size)
+ {
+ case 4:
+ insn = bfd_getb32 (fixpos);
+ break;
+ case 2:
+ insn = bfd_getb16 (fixpos);
+ break;
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unknown fixup size"));
+ }
+ }
+ else
+ {
+ insn = 0;
+ switch (fixP->fx_size)
+ {
+ case 4:
+ insn = bfd_getl16 (fixpos) << 16 | bfd_getl16 (fixpos + 2);
+ break;
+ case 2:
+ insn = bfd_getl16 (fixpos);
+ break;
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unknown fixup size"));
+ }
+ }
+
+ insn = insert_operand (insn, operand, (offsetT) value,
+ fixP->fx_file, fixP->fx_line);
+
+ md_number_to_chars_midend (fixpos, insn, fixP->fx_size);
+}
+
+/* Prepare machine-dependent frags for relaxation.
+
+ Called just before relaxation starts. Any symbol that is now undefined
+ will not become defined.
+
+ Return the correct fr_subtype in the frag.
+
+ Return the initial "guess for fr_var" to caller. The guess for fr_var
+ is *actually* the growth beyond fr_fix. Whatever we do to grow fr_fix
+ or fr_var contributes to our returned value.
+
+ Although it may not be explicit in the frag, pretend
+ fr_var starts with a value. */
+
+int
+md_estimate_size_before_relax (fragS *fragP,
+ segT segment)
+{
+ int growth;
+
+ /* If the symbol is not located within the same section AND it's not
+ an absolute section, use the maximum. OR if the symbol is a
+ constant AND the insn is by nature not pc-rel, use the maximum.
+ OR if the symbol is being equated against another symbol, use the
+ maximum. OR if the symbol is weak use the maximum. */
+ if ((S_GET_SEGMENT (fragP->fr_symbol) != segment
+ && S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
+ || (symbol_constant_p (fragP->fr_symbol)
+ && !fragP->tc_frag_data.pcrel)
+ || symbol_equated_p (fragP->fr_symbol)
+ || S_IS_WEAK (fragP->fr_symbol))
+ {
+ while (md_relax_table[fragP->fr_subtype].rlx_more != ARC_RLX_NONE)
+ ++fragP->fr_subtype;
+ }
+
+ growth = md_relax_table[fragP->fr_subtype].rlx_length;
+ fragP->fr_var = growth;
+
+ pr_debug ("%s:%d: md_estimate_size_before_relax: %d\n",
+ fragP->fr_file, fragP->fr_line, growth);
+
+ return growth;
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format. */
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED,
+ fixS *fixP)
+{
+ arelent *reloc;
+ bfd_reloc_code_real_type code;
+
+ reloc = XNEW (arelent);
+ reloc->sym_ptr_ptr = XNEW (asymbol *);
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
+ reloc->address = fixP->fx_frag->fr_address + fixP->fx_where;
+
+ /* Make sure none of our internal relocations make it this far.
+ They'd better have been fully resolved by this point. */
+ gas_assert ((int) fixP->fx_r_type > 0);
+
+ code = fixP->fx_r_type;
+
+ /* if we have something like add gp, pcl,
+ _GLOBAL_OFFSET_TABLE_@gotpc. */
+ if (code == BFD_RELOC_ARC_GOTPC32
+ && GOT_symbol
+ && fixP->fx_addsy == GOT_symbol)
+ code = BFD_RELOC_ARC_GOTPC;
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("cannot represent `%s' relocation in object file"),
+ bfd_get_reloc_code_name (code));
+ return NULL;
+ }
+
+ if (!fixP->fx_pcrel != !reloc->howto->pc_relative)
+ as_fatal (_("internal error? cannot generate `%s' relocation"),
+ bfd_get_reloc_code_name (code));
+
+ gas_assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
+
+ reloc->addend = fixP->fx_offset;
+
+ return reloc;
+}
+
+/* Perform post-processing of machine-dependent frags after relaxation.
+ Called after relaxation is finished.
+ In: Address of frag.
+ fr_type == rs_machine_dependent.
+ fr_subtype is what the address relaxed to.
+
+ Out: Any fixS:s and constants are set up. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ segT segment ATTRIBUTE_UNUSED,
+ fragS *fragP)
+{
+ const relax_typeS *table_entry;
+ char *dest;
+ const struct arc_opcode *opcode;
+ struct arc_insn insn;
+ int size, fix;
+ struct arc_relax_type *relax_arg = &fragP->tc_frag_data;
+
+ 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",
+ fragP->fr_file, fragP->fr_line,
+ fragP->fr_subtype, fix, fragP->fr_var);
+
+ if (fragP->fr_subtype <= 0
+ && fragP->fr_subtype >= arc_num_relax_opcodes)
+ as_fatal (_("no relaxation found for this instruction."));
+
+ opcode = &arc_relax_opcodes[fragP->fr_subtype];
+
+ assemble_insn (opcode, relax_arg->tok, relax_arg->ntok, relax_arg->pflags,
+ relax_arg->nflg, &insn);
+
+ apply_fixups (&insn, fragP, fix);
+
+ size = insn.len + (insn.has_limm ? 4 : 0);
+ gas_assert (table_entry->rlx_length == size);
+ emit_insn0 (&insn, dest, TRUE);
+
+ fragP->fr_fix += table_entry->rlx_length;
+ fragP->fr_var = 0;
+}
+
+/* We have no need to default values of symbols. We could catch
+ register names here, but that is handled by inserting them all in
+ the symbol table to begin with. */
+
+symbolS *
+md_undefined_symbol (char *name)
+{
+ /* The arc abi demands that a GOT[0] should be referencible as
+ [pc+_DYNAMIC@gotpc]. Hence we convert a _DYNAMIC@gotpc to a
+ GOTPC reference to _GLOBAL_OFFSET_TABLE_. */
+ if (((*name == '_')
+ && (*(name+1) == 'G')
+ && (strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0)))
+ {
+ if (!GOT_symbol)
+ {
+ if (symbol_find (name))
+ as_bad ("GOT already in symbol table");
+
+ GOT_symbol = symbol_new (GLOBAL_OFFSET_TABLE_NAME, undefined_section,
+ (valueT) 0, &zero_address_frag);
+ };
+ return GOT_symbol;
+ }
+ return NULL;
+}
+
+/* Turn a string in input_line_pointer into a floating point constant
+ of type type, and store the appropriate bytes in *litP. The number
+ of LITTLENUMS emitted is stored in *sizeP. An error message is
+ returned, or NULL on OK. */
+
+const char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, target_big_endian);
+}
+
+/* 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. We use it when we have complex operations like
+ @label1 - @label2. */
+
+void
+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);
+ }
+}
+
+/* This function is called from the function 'expression', it attempts
+ to parse special names (in our case register names). It fills in
+ the expression with the identified register. It returns TRUE if
+ it is a register and FALSE otherwise. */
+
+bfd_boolean
+arc_parse_name (const char *name,
+ struct expressionS *e)
+{
+ struct symbol *sym;
+
+ if (!assembling_insn)
+ return FALSE;
+
+ if (e->X_op == O_symbol
+ && e->X_md == O_absent)
+ return FALSE;
+
+ sym = hash_find (arc_reg_hash, name);
+ if (sym)
+ {
+ e->X_op = O_register;
+ e->X_add_number = S_GET_VALUE (sym);
+ return TRUE;
+ }
+
+ sym = hash_find (arc_addrtype_hash, name);
+ if (sym)
+ {
+ e->X_op = O_addrtype;
+ e->X_add_number = S_GET_VALUE (sym);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* md_parse_option
+ Invocation line includes a switch not recognized by the base assembler.
+ See if it's a processor-specific option.
+
+ New options (supported) are:
+
+ -mcpu=<cpu name> Assemble for selected processor
+ -EB/-mbig-endian Big-endian
+ -EL/-mlittle-endian Little-endian
+ -mrelax Enable relaxation
+
+ The following CPU names are recognized:
+ arc600, arc700, arcem, archs, nps400. */
+
+int
+md_parse_option (int c, const char *arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ case OPTION_ARC600:
+ case OPTION_ARC601:
+ return md_parse_option (OPTION_MCPU, "arc600");
+
+ case OPTION_ARC700:
+ return md_parse_option (OPTION_MCPU, "arc700");
+
+ case OPTION_ARCEM:
+ return md_parse_option (OPTION_MCPU, "arcem");
+
+ case OPTION_ARCHS:
+ return md_parse_option (OPTION_MCPU, "archs");
+
+ case OPTION_MCPU:
+ {
+ arc_select_cpu (arg, MACH_SELECTION_FROM_COMMAND_LINE);
+ break;
+ }
+
+ case OPTION_EB:
+ arc_target_format = "elf32-bigarc";
+ byte_order = BIG_ENDIAN;
+ break;
+
+ case OPTION_EL:
+ arc_target_format = "elf32-littlearc";
+ byte_order = LITTLE_ENDIAN;
+ break;
+
+ case OPTION_CD:
+ selected_cpu.features |= CD;
+ cl_features |= CD;
+ arc_check_feature ();
+ break;
+
+ case OPTION_RELAX:
+ relaxation_state = 1;
+ break;
+
+ case OPTION_NPS400:
+ selected_cpu.features |= NPS400;
+ cl_features |= NPS400;
+ arc_check_feature ();
+ break;
+
+ case OPTION_SPFP:
+ selected_cpu.features |= SPX;
+ cl_features |= SPX;
+ arc_check_feature ();
+ break;
+
+ case OPTION_DPFP:
+ selected_cpu.features |= DPX;
+ cl_features |= DPX;
+ arc_check_feature ();
+ break;
+
+ case OPTION_FPUDA:
+ selected_cpu.features |= DPA;
+ cl_features |= DPA;
+ arc_check_feature ();
+ break;
+
+ /* Dummy options are accepted but have no effect. */
+ case OPTION_USER_MODE:
+ case OPTION_LD_EXT_MASK:
+ case OPTION_SWAP:
+ case OPTION_NORM:
+ case OPTION_BARREL_SHIFT:
+ case OPTION_MIN_MAX:
+ case OPTION_NO_MPY:
+ case OPTION_EA:
+ case OPTION_MUL64:
+ case OPTION_SIMD:
+ case OPTION_XMAC_D16:
+ case OPTION_XMAC_24:
+ case OPTION_DSP_PACKA:
+ case OPTION_CRC:
+ case OPTION_DVBF:
+ case OPTION_TELEPHONY:
+ case OPTION_XYMEMORY:
+ case OPTION_LOCK:
+ case OPTION_SWAPE:
+ case OPTION_RTSC:
+ break;
+
+ default:
+ return 0;
+ }
+
+ 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)
+ {
+ bfd_boolean 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 (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, " -mfpuda\t\t enable double-precision assist floating "
+ "point\n\t\t\t instructions for ARC EM\n");
+
+ fprintf (stream,
+ " -mcode-density\t enable code density option for ARC EM\n");
+
+ fprintf (stream, _("\
+ -EB assemble code for a big-endian cpu\n"));
+ fprintf (stream, _("\
+ -EL assemble code for a little-endian cpu\n"));
+ fprintf (stream, _("\
+ -mrelax enable relaxation\n"));
+
+ fprintf (stream, _("The following ARC-specific assembler options are "
+ "deprecated and are accepted\nfor compatibility only:\n"));
+
+ fprintf (stream, _(" -mEA\n"
+ " -mbarrel-shifter\n"
+ " -mbarrel_shifter\n"
+ " -mcrc\n"
+ " -mdsp-packa\n"
+ " -mdsp_packa\n"
+ " -mdvbf\n"
+ " -mld-extension-reg-mask\n"
+ " -mlock\n"
+ " -mmac-24\n"
+ " -mmac-d16\n"
+ " -mmac_24\n"
+ " -mmac_d16\n"
+ " -mmin-max\n"
+ " -mmin_max\n"
+ " -mmul64\n"
+ " -mno-mpy\n"
+ " -mnorm\n"
+ " -mrtsc\n"
+ " -msimd\n"
+ " -mswap\n"
+ " -mswape\n"
+ " -mtelephony\n"
+ " -muser-mode-only\n"
+ " -mxy\n"));
+}
+
+/* Find the proper relocation for the given opcode. */
+
+static extended_bfd_reloc_code_real_type
+find_reloc (const char *name,
+ const char *opcodename,
+ const struct arc_flags *pflags,
+ int nflg,
+ extended_bfd_reloc_code_real_type reloc)
+{
+ unsigned int i;
+ int j;
+ bfd_boolean found_flag, tmp;
+ extended_bfd_reloc_code_real_type ret = BFD_RELOC_UNUSED;
+
+ for (i = 0; i < arc_num_equiv_tab; i++)
+ {
+ const struct arc_reloc_equiv_tab *r = &arc_reloc_equiv[i];
+
+ /* Find the entry. */
+ if (strcmp (name, r->name))
+ continue;
+ if (r->mnemonic && (strcmp (r->mnemonic, opcodename)))
+ continue;
+ if (r->flags[0])
+ {
+ if (!nflg)
+ continue;
+ found_flag = FALSE;
+ unsigned * psflg = (unsigned *)r->flags;
+ do
+ {
+ tmp = FALSE;
+ for (j = 0; j < nflg; j++)
+ if (!strcmp (pflags[j].name,
+ arc_flag_operands[*psflg].name))
+ {
+ tmp = TRUE;
+ break;
+ }
+ if (!tmp)
+ {
+ found_flag = FALSE;
+ break;
+ }
+ else
+ {
+ found_flag = TRUE;
+ }
+ ++ psflg;
+ } while (*psflg);
+
+ if (!found_flag)
+ continue;
+ }
+
+ if (reloc != r->oldreloc)
+ continue;
+ /* Found it. */
+ ret = r->newreloc;
+ break;
+ }
+
+ if (ret == BFD_RELOC_UNUSED)
+ as_bad (_("Unable to find %s relocation for instruction %s"),
+ name, opcodename);
+ return ret;
+}
+
+/* All the symbol types that are allowed to be used for
+ relaxation. */
+
+static bfd_boolean
+may_relax_expr (expressionS tok)
+{
+ /* Check if we have unrelaxable relocs. */
+ switch (tok.X_md)
+ {
+ default:
+ break;
+ case O_plt:
+ return FALSE;
+ }
+
+ switch (tok.X_op)
+ {
+ case O_symbol:
+ case O_multiply:
+ case O_divide:
+ case O_modulus:
+ case O_add:
+ case O_subtract:
+ break;
+
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Checks if flags are in line with relaxable insn. */
+
+static bfd_boolean
+relaxable_flag (const struct arc_relaxable_ins *ins,
+ const struct arc_flags *pflags,
+ int nflgs)
+{
+ unsigned flag_class,
+ flag,
+ flag_class_idx = 0,
+ flag_idx = 0;
+
+ const struct arc_flag_operand *flag_opand;
+ int i, counttrue = 0;
+
+ /* Iterate through flags classes. */
+ while ((flag_class = ins->flag_classes[flag_class_idx]) != 0)
+ {
+ /* Iterate through flags in flag class. */
+ while ((flag = arc_flag_classes[flag_class].flags[flag_idx])
+ != 0)
+ {
+ flag_opand = &arc_flag_operands[flag];
+ /* Iterate through flags in ins to compare. */
+ for (i = 0; i < nflgs; ++i)
+ {
+ if (strcmp (flag_opand->name, pflags[i].name) == 0)
+ ++counttrue;
+ }
+
+ ++flag_idx;
+ }
+
+ ++flag_class_idx;
+ flag_idx = 0;
+ }
+
+ /* If counttrue == nflgs, then all flags have been found. */
+ return (counttrue == nflgs ? TRUE : FALSE);
+}
+
+/* Checks if operands are in line with relaxable insn. */
+
+static bfd_boolean
+relaxable_operand (const struct arc_relaxable_ins *ins,
+ const expressionS *tok,
+ int ntok)
+{
+ const enum rlx_operand_type *operand = &ins->operands[0];
+ int i = 0;
+
+ while (*operand != EMPTY)
+ {
+ const expressionS *epr = &tok[i];
+
+ if (i != 0 && i >= ntok)
+ return FALSE;
+
+ switch (*operand)
+ {
+ case IMMEDIATE:
+ if (!(epr->X_op == O_multiply
+ || epr->X_op == O_divide
+ || epr->X_op == O_modulus
+ || epr->X_op == O_add
+ || epr->X_op == O_subtract
+ || epr->X_op == O_symbol))
+ return FALSE;
+ break;
+
+ case REGISTER_DUP:
+ if ((i <= 0)
+ || (epr->X_add_number != tok[i - 1].X_add_number))
+ return FALSE;
+ /* Fall through. */
+ case REGISTER:
+ if (epr->X_op != O_register)
+ return FALSE;
+ break;
+
+ case REGISTER_S:
+ if (epr->X_op != O_register)
+ return FALSE;
+
+ switch (epr->X_add_number)
+ {
+ case 0: case 1: case 2: case 3:
+ case 12: case 13: case 14: case 15:
+ break;
+ default:
+ 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;
+ break;
+
+ case BRACKET:
+ if (epr->X_op != O_bracket)
+ return FALSE;
+ break;
+
+ default:
+ /* Don't understand, bail out. */
+ return FALSE;
+ break;
+ }
+
+ ++i;
+ operand = &ins->operands[i];
+ }
+
+ return (i == ntok ? TRUE : FALSE);
+}
+
+/* Return TRUE if this OPDCODE is a candidate for relaxation. */
+
+static bfd_boolean
+relax_insn_p (const struct arc_opcode *opcode,
+ const expressionS *tok,
+ int ntok,
+ const struct arc_flags *pflags,
+ int nflg)
+{
+ unsigned i;
+ bfd_boolean rv = FALSE;
+
+ /* Check the relaxation table. */
+ for (i = 0; i < arc_num_relaxable_ins && relaxation_state; ++i)
+ {
+ const struct arc_relaxable_ins *arc_rlx_ins = &arc_relaxable_insns[i];
+
+ if ((strcmp (opcode->name, arc_rlx_ins->mnemonic_r) == 0)
+ && may_relax_expr (tok[arc_rlx_ins->opcheckidx])
+ && relaxable_operand (arc_rlx_ins, tok, ntok)
+ && relaxable_flag (arc_rlx_ins, pflags, nflg))
+ {
+ rv = TRUE;
+ frag_now->fr_subtype = arc_relaxable_insns[i].subtype;
+ memcpy (&frag_now->tc_frag_data.tok, tok,
+ sizeof (expressionS) * ntok);
+ memcpy (&frag_now->tc_frag_data.pflags, pflags,
+ sizeof (struct arc_flags) * nflg);
+ frag_now->tc_frag_data.nflg = nflg;
+ frag_now->tc_frag_data.ntok = ntok;
+ break;
+ }
+ }
+
+ return rv;
+}
+
+/* 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);
+ 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
+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
+ && S_GET_SEGMENT (exp->X_op_symbol) == 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);
+
+ /* Convert to lower case. */
+ for (p = insn_name; *p; ++p)
+ *p = TOLOWER (*p);
+
+ /* 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 (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);
+
+ /* 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_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),
+ 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
+ End: */