[ARC] Improve parsing instruction operands.
[deliverable/binutils-gdb.git] / gas / config / tc-arc.c
index df3ed1f0d22358ad89b87feb995e11a94cc95adc..ed1e3fb5dd5c9504c66e1f377f3d85a4f322a33b 100644 (file)
@@ -1,6 +1,7 @@
 /* tc-arc.c -- Assembler for the ARC
-   Copyright (C) 1994-2015 Free Software Foundation, Inc.
-   Contributed by Doug Evans (dje@cygnus.com).
+   Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+   Contributor: Claudiu Zissulescu <claziss@synopsys.com>
 
    This file is part of GAS, the GNU Assembler.
 
    02110-1301, USA.  */
 
 #include "as.h"
-#include "struc-symbol.h"
-#include "safe-ctype.h"
 #include "subsegs.h"
+#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
+#include "safe-ctype.h"
+
 #include "opcode/arc.h"
-#include "../opcodes/arc-ext.h"
+#include "opcode/arc-attrs.h"
 #include "elf/arc.h"
-#include "dwarf2dbg.h"
+#include "../opcodes/arc-ext.h"
 
-const struct suffix_classes
-{
-  char *name;
-  int  len;
-} suffixclass[] =
-{
-  { "SUFFIX_COND|SUFFIX_FLAG",23 },
-  { "SUFFIX_FLAG", 11 },
-  { "SUFFIX_COND", 11 },
-  { "SUFFIX_NONE", 11 }
-};
+/* Defines section.  */
+
+#define MAX_INSN_FIXUPS      2
+#define MAX_CONSTR_STR       20
+#define FRAG_MAX_GROWTH      8
+
+#ifdef DEBUG
+# define pr_debug(fmt, args...) fprintf (stderr, fmt, ##args)
+#else
+# define pr_debug(fmt, args...)
+#endif
+
+#define MAJOR_OPCODE(x)  (((x) & 0xF8000000) >> 27)
+#define SUB_OPCODE(x)   (((x) & 0x003F0000) >> 16)
+#define LP_INSN(x)      ((MAJOR_OPCODE (x) == 0x4) \
+                         && (SUB_OPCODE (x) == 0x28))
 
-#define MAXSUFFIXCLASS (sizeof (suffixclass) / sizeof (struct suffix_classes))
+#ifndef TARGET_WITH_CPU
+#define TARGET_WITH_CPU "arc700"
+#endif /* TARGET_WITH_CPU */
 
-const struct syntax_classes
+#define ARC_GET_FLAG(s)        (*symbol_get_tc (s))
+#define ARC_SET_FLAG(s,v)      (*symbol_get_tc (s) |= (v))
+#define streq(a, b)          (strcmp (a, b) == 0)
+
+/* Enum used to enumerate the relaxable ins operands.  */
+enum rlx_operand_type
 {
-  char *name;
-  int  len;
-  int  s_class;
-} syntaxclass[] =
-{
-  { "SYNTAX_3OP|OP1_MUST_BE_IMM", 26, SYNTAX_3OP|OP1_MUST_BE_IMM|SYNTAX_VALID },
-  { "OP1_MUST_BE_IMM|SYNTAX_3OP", 26, OP1_MUST_BE_IMM|SYNTAX_3OP|SYNTAX_VALID },
-  { "SYNTAX_2OP|OP1_IMM_IMPLIED", 26, SYNTAX_2OP|OP1_IMM_IMPLIED|SYNTAX_VALID },
-  { "OP1_IMM_IMPLIED|SYNTAX_2OP", 26, OP1_IMM_IMPLIED|SYNTAX_2OP|SYNTAX_VALID },
-  { "SYNTAX_3OP",                 10, SYNTAX_3OP|SYNTAX_VALID },
-  { "SYNTAX_2OP",                 10, SYNTAX_2OP|SYNTAX_VALID }
+  EMPTY = 0,
+  REGISTER,
+  REGISTER_S,     /* Register for short instruction(s).  */
+  REGISTER_NO_GP, /* Is a register but not gp register specifically.  */
+  REGISTER_DUP,   /* Duplication of previous operand of type register.  */
+  IMMEDIATE,
+  BRACKET
 };
 
-#define MAXSYNTAXCLASS (sizeof (syntaxclass) / sizeof (struct syntax_classes))
+enum arc_rlx_types
+{
+  ARC_RLX_NONE = 0,
+  ARC_RLX_BL_S,
+  ARC_RLX_BL,
+  ARC_RLX_B_S,
+  ARC_RLX_B,
+  ARC_RLX_ADD_U3,
+  ARC_RLX_ADD_U6,
+  ARC_RLX_ADD_LIMM,
+  ARC_RLX_LD_U7,
+  ARC_RLX_LD_S9,
+  ARC_RLX_LD_LIMM,
+  ARC_RLX_MOV_U8,
+  ARC_RLX_MOV_S12,
+  ARC_RLX_MOV_LIMM,
+  ARC_RLX_SUB_U3,
+  ARC_RLX_SUB_U6,
+  ARC_RLX_SUB_LIMM,
+  ARC_RLX_MPY_U6,
+  ARC_RLX_MPY_LIMM,
+  ARC_RLX_MOV_RU6,
+  ARC_RLX_MOV_RLIMM,
+  ARC_RLX_ADD_RRU6,
+  ARC_RLX_ADD_RRLIMM,
+};
 
-/* This array holds the chars that always start a comment.  If the
-   pre-processor is disabled, these aren't very useful.  */
+/* Macros section.  */
+
+#define regno(x)               ((x) & 0x3F)
+#define is_ir_num(x)           (((x) & ~0x3F) == 0)
+#define is_code_density_p(sc)   (((sc) == CD1 || (sc) == CD2))
+#define is_spfp_p(op)           (((sc) == SPX))
+#define is_dpfp_p(op)           (((sc) == DPX))
+#define is_fpuda_p(op)          (((sc) == DPA))
+#define is_br_jmp_insn_p(op)    (((op)->insn_class == BRANCH           \
+                                 || (op)->insn_class == JUMP           \
+                                 || (op)->insn_class == BRCC           \
+                                 || (op)->insn_class == BBIT0          \
+                                 || (op)->insn_class == BBIT1          \
+                                 || (op)->insn_class == BI             \
+                                 || (op)->insn_class == EI             \
+                                 || (op)->insn_class == ENTER          \
+                                 || (op)->insn_class == JLI            \
+                                 || (op)->insn_class == LOOP           \
+                                 || (op)->insn_class == LEAVE          \
+                                 ))
+#define is_kernel_insn_p(op)    (((op)->insn_class == KERNEL))
+#define is_nps400_p(op)         (((sc) == NPS400))
+
+/* Generic assembler global variables which must be defined by all
+   targets.  */
+
+/* Characters which always start a comment.  */
 const char comment_chars[] = "#;";
 
-/* This array holds the chars that only start a comment at the beginning of
-   a line.  If the line seems to have the form '# 123 filename'
-   .line and .file directives will appear in the pre-processed output */
-/* Note that input_file.c hand checks for '#' at the beginning of the
-   first line of the input file.  This is because the compiler outputs
-   #NO_APP at the beginning of its output.  */
-/* Also note that comments started like this one will always
-   work if '/' isn't otherwise defined.  */
+/* Characters which start a comment at the beginning of a line.  */
 const char line_comment_chars[] = "#";
 
-const char line_separator_chars[] = "";
+/* Characters which may be used to separate multiple commands on a
+   single line.  */
+const char line_separator_chars[] = "`";
 
-/* Chars that can be used to separate mant from exp in floating point nums.  */
+/* Characters which are used to indicate an exponent in a floating
+   point number.  */
 const char EXP_CHARS[] = "eE";
 
 /* Chars that mean this number is a floating point constant
@@ -87,1811 +144,4979 @@ extern int target_big_endian;
 const char *arc_target_format = DEFAULT_TARGET_FORMAT;
 static int byte_order = DEFAULT_BYTE_ORDER;
 
+/* Arc extension section.  */
 static segT arcext_section;
 
-/* One of bfd_mach_arc_n.  */
-static int arc_mach_type = bfd_mach_arc_6;
+/* By default relaxation is disabled.  */
+static int relaxation_state = 0;
+
+extern int arc_get_mach (char *);
+
+/* Forward declarations.  */
+static void arc_lcomm (int);
+static void arc_option (int);
+static void arc_extra_reloc (int);
+static void arc_extinsn (int);
+static void arc_extcorereg (int);
+static void arc_attribute (int);
+
+const pseudo_typeS md_pseudo_table[] =
+{
+  /* Make sure that .word is 32 bits.  */
+  { "word", cons, 4 },
+
+  { "align",   s_align_bytes, 0 }, /* Defaulting is invalid (0).  */
+  { "lcomm",   arc_lcomm, 0 },
+  { "lcommon", arc_lcomm, 0 },
+  { "cpu",     arc_option, 0 },
 
-/* Non-zero if the cpu type has been explicitly specified.  */
-static int mach_type_specified_p = 0;
+  { "arc_attribute",   arc_attribute, 0 },
+  { "extinstruction",  arc_extinsn, 0 },
+  { "extcoreregister", arc_extcorereg, EXT_CORE_REGISTER },
+  { "extauxregister",  arc_extcorereg, EXT_AUX_REGISTER },
+  { "extcondcode",     arc_extcorereg, EXT_COND_CODE },
 
-/* Non-zero if opcode tables have been initialized.
-   A .option command must appear before any instructions.  */
-static int cpu_tables_init_p = 0;
+  { "tls_gd_ld",   arc_extra_reloc, BFD_RELOC_ARC_TLS_GD_LD },
+  { "tls_gd_call", arc_extra_reloc, BFD_RELOC_ARC_TLS_GD_CALL },
+
+  { NULL, NULL, 0 }
+};
 
-static struct hash_control *arc_suffix_hash = NULL;
-\f
 const char *md_shortopts = "";
 
 enum options
 {
   OPTION_EB = OPTION_MD_BASE,
   OPTION_EL,
-  OPTION_ARC5,
-  OPTION_ARC6,
-  OPTION_ARC7,
-  OPTION_ARC8,
-  OPTION_ARC
+
+  OPTION_ARC600,
+  OPTION_ARC601,
+  OPTION_ARC700,
+  OPTION_ARCEM,
+  OPTION_ARCHS,
+
+  OPTION_MCPU,
+  OPTION_CD,
+  OPTION_RELAX,
+  OPTION_NPS400,
+
+  OPTION_SPFP,
+  OPTION_DPFP,
+  OPTION_FPUDA,
+
+  /* The following options are deprecated and provided here only for
+     compatibility reasons.  */
+  OPTION_USER_MODE,
+  OPTION_LD_EXT_MASK,
+  OPTION_SWAP,
+  OPTION_NORM,
+  OPTION_BARREL_SHIFT,
+  OPTION_MIN_MAX,
+  OPTION_NO_MPY,
+  OPTION_EA,
+  OPTION_MUL64,
+  OPTION_SIMD,
+  OPTION_XMAC_D16,
+  OPTION_XMAC_24,
+  OPTION_DSP_PACKA,
+  OPTION_CRC,
+  OPTION_DVBF,
+  OPTION_TELEPHONY,
+  OPTION_XYMEMORY,
+  OPTION_LOCK,
+  OPTION_SWAPE,
+  OPTION_RTSC
 };
 
 struct option md_longopts[] =
 {
-  { "EB", no_argument, NULL, OPTION_EB },
-  { "EL", no_argument, NULL, OPTION_EL },
-  { "marc5", no_argument, NULL, OPTION_ARC5 },
-  { "pre-v6", no_argument, NULL, OPTION_ARC5 },
-  { "marc6", no_argument, NULL, OPTION_ARC6 },
-  { "marc7", no_argument, NULL, OPTION_ARC7 },
-  { "marc8", no_argument, NULL, OPTION_ARC8 },
-  { "marc", no_argument, NULL, OPTION_ARC },
-  { NULL, no_argument, NULL, 0 }
+  { "EB",              no_argument,       NULL, OPTION_EB },
+  { "EL",              no_argument,       NULL, OPTION_EL },
+  { "mcpu",            required_argument, NULL, OPTION_MCPU },
+  { "mA6",             no_argument,       NULL, OPTION_ARC600 },
+  { "mARC600",         no_argument,       NULL, OPTION_ARC600 },
+  { "mARC601",         no_argument,       NULL, OPTION_ARC601 },
+  { "mARC700",         no_argument,       NULL, OPTION_ARC700 },
+  { "mA7",             no_argument,       NULL, OPTION_ARC700 },
+  { "mEM",             no_argument,       NULL, OPTION_ARCEM },
+  { "mHS",             no_argument,       NULL, OPTION_ARCHS },
+  { "mcode-density",   no_argument,       NULL, OPTION_CD },
+  { "mrelax",           no_argument,       NULL, OPTION_RELAX },
+  { "mnps400",          no_argument,       NULL, OPTION_NPS400 },
+
+  /* Floating point options */
+  { "mspfp", no_argument, NULL, OPTION_SPFP},
+  { "mspfp-compact", no_argument, NULL, OPTION_SPFP},
+  { "mspfp_compact", no_argument, NULL, OPTION_SPFP},
+  { "mspfp-fast", no_argument, NULL, OPTION_SPFP},
+  { "mspfp_fast", no_argument, NULL, OPTION_SPFP},
+  { "mdpfp", no_argument, NULL, OPTION_DPFP},
+  { "mdpfp-compact", no_argument, NULL, OPTION_DPFP},
+  { "mdpfp_compact", no_argument, NULL, OPTION_DPFP},
+  { "mdpfp-fast", no_argument, NULL, OPTION_DPFP},
+  { "mdpfp_fast", no_argument, NULL, OPTION_DPFP},
+  { "mfpuda", no_argument, NULL, OPTION_FPUDA},
+
+  /* The following options are deprecated and provided here only for
+     compatibility reasons.  */
+  { "mav2em", no_argument, NULL, OPTION_ARCEM },
+  { "mav2hs", no_argument, NULL, OPTION_ARCHS },
+  { "muser-mode-only", no_argument, NULL, OPTION_USER_MODE },
+  { "mld-extension-reg-mask", required_argument, NULL, OPTION_LD_EXT_MASK },
+  { "mswap", no_argument, NULL, OPTION_SWAP },
+  { "mnorm", no_argument, NULL, OPTION_NORM },
+  { "mbarrel-shifter", no_argument, NULL, OPTION_BARREL_SHIFT },
+  { "mbarrel_shifter", no_argument, NULL, OPTION_BARREL_SHIFT },
+  { "mmin-max", no_argument, NULL, OPTION_MIN_MAX },
+  { "mmin_max", no_argument, NULL, OPTION_MIN_MAX },
+  { "mno-mpy", no_argument, NULL, OPTION_NO_MPY },
+  { "mea", no_argument, NULL, OPTION_EA },
+  { "mEA", no_argument, NULL, OPTION_EA },
+  { "mmul64", no_argument, NULL, OPTION_MUL64 },
+  { "msimd", no_argument, NULL, OPTION_SIMD},
+  { "mmac-d16", no_argument, NULL, OPTION_XMAC_D16},
+  { "mmac_d16", no_argument, NULL, OPTION_XMAC_D16},
+  { "mmac-24", no_argument, NULL, OPTION_XMAC_24},
+  { "mmac_24", no_argument, NULL, OPTION_XMAC_24},
+  { "mdsp-packa", no_argument, NULL, OPTION_DSP_PACKA},
+  { "mdsp_packa", no_argument, NULL, OPTION_DSP_PACKA},
+  { "mcrc", no_argument, NULL, OPTION_CRC},
+  { "mdvbf", no_argument, NULL, OPTION_DVBF},
+  { "mtelephony", no_argument, NULL, OPTION_TELEPHONY},
+  { "mxy", no_argument, NULL, OPTION_XYMEMORY},
+  { "mlock", no_argument, NULL, OPTION_LOCK},
+  { "mswape", no_argument, NULL, OPTION_SWAPE},
+  { "mrtsc", no_argument, NULL, OPTION_RTSC},
+
+  { NULL,              no_argument, NULL, 0 }
 };
+
 size_t md_longopts_size = sizeof (md_longopts);
 
-#define IS_SYMBOL_OPERAND(o) \
- ((o) == 'b' || (o) == 'c' || (o) == 's' || (o) == 'o' || (o) == 'O')
+/* Local data and data types.  */
 
-struct arc_operand_value *get_ext_suffix (char *s);
+/* Used since new relocation types are introduced in this
+   file (DUMMY_RELOC_LITUSE_*).  */
+typedef int extended_bfd_reloc_code_real_type;
 
-/* Invocation line includes a switch not recognized by the base assembler.
-   See if it's a processor-specific option.  */
+struct arc_fixup
+{
+  expressionS exp;
 
-int
-md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+  extended_bfd_reloc_code_real_type reloc;
+
+  /* index into arc_operands.  */
+  unsigned int opindex;
+
+  /* PC-relative, used by internals fixups.  */
+  unsigned char pcrel;
+
+  /* TRUE if this fixup is for LIMM operand.  */
+  bfd_boolean islong;
+};
+
+struct arc_insn
 {
-  switch (c)
-    {
-    case OPTION_ARC5:
-      arc_mach_type = bfd_mach_arc_5;
-      break;
-    case OPTION_ARC:
-    case OPTION_ARC6:
-      arc_mach_type = bfd_mach_arc_6;
-      break;
-    case OPTION_ARC7:
-      arc_mach_type = bfd_mach_arc_7;
-      break;
-    case OPTION_ARC8:
-      arc_mach_type = bfd_mach_arc_8;
-      break;
-    case OPTION_EB:
-      byte_order = BIG_ENDIAN;
-      arc_target_format = "elf32-bigarc";
-      break;
-    case OPTION_EL:
-      byte_order = LITTLE_ENDIAN;
-      arc_target_format = "elf32-littlearc";
-      break;
-    default:
-      return 0;
-    }
-  return 1;
+  unsigned long long int insn;
+  int nfixups;
+  struct arc_fixup fixups[MAX_INSN_FIXUPS];
+  long limm;
+  unsigned int len;       /* Length of instruction in bytes.  */
+  bfd_boolean has_limm;   /* Boolean value: TRUE if limm field is
+                            valid.  */
+  bfd_boolean relax;     /* Boolean value: TRUE if needs
+                            relaxation.  */
+};
+
+/* Structure to hold any last two instructions.  */
+static struct arc_last_insn
+{
+  /* Saved instruction opcode.  */
+  const struct arc_opcode *opcode;
+
+  /* Boolean value: TRUE if current insn is short.  */
+  bfd_boolean has_limm;
+
+  /* Boolean value: TRUE if current insn has delay slot.  */
+  bfd_boolean has_delay_slot;
+} arc_last_insns[2];
+
+/* Extension instruction suffix classes.  */
+typedef struct
+{
+  const char *name;
+  int  len;
+  int  attr_class;
+} attributes_t;
+
+static const attributes_t suffixclass[] =
+{
+  { "SUFFIX_FLAG", 11, ARC_SUFFIX_FLAG },
+  { "SUFFIX_COND", 11, ARC_SUFFIX_COND },
+  { "SUFFIX_NONE", 11, ARC_SUFFIX_NONE }
+};
+
+/* Extension instruction syntax classes.  */
+static const attributes_t syntaxclass[] =
+{
+  { "SYNTAX_3OP", 10, ARC_SYNTAX_3OP },
+  { "SYNTAX_2OP", 10, ARC_SYNTAX_2OP },
+  { "SYNTAX_1OP", 10, ARC_SYNTAX_1OP },
+  { "SYNTAX_NOP", 10, ARC_SYNTAX_NOP }
+};
+
+/* Extension instruction syntax classes modifiers.  */
+static const attributes_t syntaxclassmod[] =
+{
+  { "OP1_IMM_IMPLIED" , 15, ARC_OP1_IMM_IMPLIED },
+  { "OP1_MUST_BE_IMM" , 15, ARC_OP1_MUST_BE_IMM }
+};
+
+/* Extension register type.  */
+typedef struct
+{
+  char *name;
+  int  number;
+  int  imode;
+} extRegister_t;
+
+/* A structure to hold the additional conditional codes.  */
+static struct
+{
+  struct arc_flag_operand *arc_ext_condcode;
+  int size;
+} ext_condcode = { NULL, 0 };
+
+/* Structure to hold an entry in ARC_OPCODE_HASH.  */
+struct arc_opcode_hash_entry
+{
+  /* The number of pointers in the OPCODE list.  */
+  size_t count;
+
+  /* Points to a list of opcode pointers.  */
+  const struct arc_opcode **opcode;
+};
+
+/* Structure used for iterating through an arc_opcode_hash_entry.  */
+struct arc_opcode_hash_entry_iterator
+{
+  /* Index into the OPCODE element of the arc_opcode_hash_entry.  */
+  size_t index;
+
+  /* The specific ARC_OPCODE from the ARC_OPCODES table that was last
+     returned by this iterator.  */
+  const struct arc_opcode *opcode;
+};
+
+/* Forward declaration.  */
+static void assemble_insn
+  (const struct arc_opcode *, const expressionS *, int,
+   const struct arc_flags *, int, struct arc_insn *);
+
+/* The selection of the machine type can come from different sources.  This
+   enum is used to track how the selection was made in order to perform
+   error checks.  */
+enum mach_selection_type
+  {
+    MACH_SELECTION_NONE,
+    MACH_SELECTION_FROM_DEFAULT,
+    MACH_SELECTION_FROM_CPU_DIRECTIVE,
+    MACH_SELECTION_FROM_COMMAND_LINE
+  };
+
+/* How the current machine type was selected.  */
+static enum mach_selection_type mach_selection_mode = MACH_SELECTION_NONE;
+
+/* The hash table of instruction opcodes.  */
+static struct hash_control *arc_opcode_hash;
+
+/* The hash table of register symbols.  */
+static struct hash_control *arc_reg_hash;
+
+/* The hash table of aux register symbols.  */
+static struct hash_control *arc_aux_hash;
+
+/* The hash table of address types.  */
+static struct hash_control *arc_addrtype_hash;
+
+#define ARC_CPU_TYPE_A6xx(NAME,EXTRA)                  \
+  { #NAME, ARC_OPCODE_ARC600, bfd_mach_arc_arc600,     \
+      E_ARC_MACH_ARC600, EXTRA}
+#define ARC_CPU_TYPE_A7xx(NAME,EXTRA)                  \
+  { #NAME, ARC_OPCODE_ARC700,  bfd_mach_arc_arc700,    \
+      E_ARC_MACH_ARC700, EXTRA}
+#define ARC_CPU_TYPE_AV2EM(NAME,EXTRA)                 \
+  { #NAME,  ARC_OPCODE_ARCv2EM, bfd_mach_arc_arcv2,    \
+      EF_ARC_CPU_ARCV2EM, EXTRA}
+#define ARC_CPU_TYPE_AV2HS(NAME,EXTRA)                 \
+  { #NAME,  ARC_OPCODE_ARCv2HS, bfd_mach_arc_arcv2,    \
+      EF_ARC_CPU_ARCV2HS, EXTRA}
+#define ARC_CPU_TYPE_NONE                              \
+  { 0, 0, 0, 0, 0 }
+
+/* A table of CPU names and opcode sets.  */
+static const struct cpu_type
+{
+  const char *name;
+  unsigned flags;
+  int mach;
+  unsigned eflags;
+  unsigned features;
 }
+  cpu_types[] =
+{
+  #include "elf/arc-cpu.def"
+};
 
-void
-md_show_usage (FILE *stream)
+/* Information about the cpu/variant we're assembling for.  */
+static struct cpu_type selected_cpu = { 0, 0, 0, E_ARC_OSABI_CURRENT, 0 };
+
+/* TRUE if current assembly code uses RF16 only registers.  */
+static bfd_boolean rf16_only = TRUE;
+
+/* MPY option.  */
+static unsigned mpy_option = 0;
+
+/* Use PIC. */
+static unsigned pic_option = 0;
+
+/* Use small data.  */
+static unsigned sda_option = 0;
+
+/* Use TLS.  */
+static unsigned tls_option = 0;
+
+/* Command line given features.  */
+static unsigned cl_features = 0;
+
+/* Used by the arc_reloc_op table.  Order is important.  */
+#define O_gotoff  O_md1     /* @gotoff relocation.  */
+#define O_gotpc   O_md2     /* @gotpc relocation.  */
+#define O_plt     O_md3     /* @plt relocation.  */
+#define O_sda     O_md4     /* @sda relocation.  */
+#define O_pcl     O_md5     /* @pcl relocation.  */
+#define O_tlsgd   O_md6     /* @tlsgd relocation.  */
+#define O_tlsie   O_md7     /* @tlsie relocation.  */
+#define O_tpoff9  O_md8     /* @tpoff9 relocation.  */
+#define O_tpoff   O_md9     /* @tpoff relocation.  */
+#define O_dtpoff9 O_md10    /* @dtpoff9 relocation.  */
+#define O_dtpoff  O_md11    /* @dtpoff relocation.  */
+#define O_last    O_dtpoff
+
+/* Used to define a bracket as operand in tokens.  */
+#define O_bracket O_md32
+
+/* Used to define a colon as an operand in tokens.  */
+#define O_colon O_md31
+
+/* Used to define address types in nps400.  */
+#define O_addrtype O_md30
+
+/* Dummy relocation, to be sorted out.  */
+#define DUMMY_RELOC_ARC_ENTRY     (BFD_RELOC_UNUSED + 1)
+
+#define USER_RELOC_P(R) ((R) >= O_gotoff && (R) <= O_last)
+
+/* A table to map the spelling of a relocation operand into an appropriate
+   bfd_reloc_code_real_type type.  The table is assumed to be ordered such
+   that op-O_literal indexes into it.  */
+#define ARC_RELOC_TABLE(op)                            \
+  (&arc_reloc_op[ ((!USER_RELOC_P (op))                        \
+                  ? (abort (), 0)                      \
+                  : (int) (op) - (int) O_gotoff) ])
+
+#define DEF(NAME, RELOC, REQ)                          \
+  { #NAME, sizeof (#NAME)-1, O_##NAME, RELOC, REQ}
+
+static const struct arc_reloc_op_tag
 {
-  fprintf (stream, "\
-ARC Options:\n\
-  -marc[5|6|7|8]          select processor variant (default arc%d)\n\
-  -EB                     assemble code for a big endian cpu\n\
-  -EL                     assemble code for a little endian cpu\n", arc_mach_type + 5);
+  /* String to lookup.  */
+  const char *name;
+  /* Size of the string.  */
+  size_t length;
+  /* Which operator to use.  */
+  operatorT op;
+  extended_bfd_reloc_code_real_type reloc;
+  /* Allows complex relocation expression like identifier@reloc +
+     const.  */
+  unsigned int complex_expr : 1;
 }
+  arc_reloc_op[] =
+{
+  DEF (gotoff,  BFD_RELOC_ARC_GOTOFF,          1),
+  DEF (gotpc,   BFD_RELOC_ARC_GOTPC32,         0),
+  DEF (plt,    BFD_RELOC_ARC_PLT32,            0),
+  DEF (sda,    DUMMY_RELOC_ARC_ENTRY,          1),
+  DEF (pcl,    BFD_RELOC_ARC_PC32,             1),
+  DEF (tlsgd,   BFD_RELOC_ARC_TLS_GD_GOT,      0),
+  DEF (tlsie,   BFD_RELOC_ARC_TLS_IE_GOT,      0),
+  DEF (tpoff9,  BFD_RELOC_ARC_TLS_LE_S9,       0),
+  DEF (tpoff,   BFD_RELOC_ARC_TLS_LE_32,       1),
+  DEF (dtpoff9, BFD_RELOC_ARC_TLS_DTPOFF_S9,   0),
+  DEF (dtpoff,  BFD_RELOC_ARC_TLS_DTPOFF,      1),
+};
 
-/* This function is called once, at assembler startup time.  It should
-   set up all the tables, etc. that the MD part of the assembler will need.
-   Opcode selection is deferred until later because we might see a .option
-   command.  */
+static const int arc_num_reloc_op
+= sizeof (arc_reloc_op) / sizeof (*arc_reloc_op);
 
-void
-md_begin (void)
+/* Structure for relaxable instruction that have to be swapped with a
+   smaller alternative instruction.  */
+struct arc_relaxable_ins
 {
-  /* The endianness can be chosen "at the factory".  */
-  target_big_endian = byte_order == BIG_ENDIAN;
+  /* Mnemonic that should be checked.  */
+  const char *mnemonic_r;
 
-  if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, arc_mach_type))
-    as_warn (_("could not set architecture and machine"));
+  /* Operands that should be checked.
+     Indexes of operands from operand array.  */
+  enum rlx_operand_type operands[6];
 
-  /* This call is necessary because we need to initialize `arc_operand_map'
-     which may be needed before we see the first insn.  */
-  arc_opcode_init_tables (arc_get_opcode_mach (arc_mach_type,
-                                              target_big_endian));
-}
+  /* Flags that should be checked.  */
+  unsigned flag_classes[5];
 
-/* Initialize the various opcode and operand tables.
-   MACH is one of bfd_mach_arc_xxx.  */
+  /* Mnemonic (smaller) alternative to be used later for relaxation.  */
+  const char *mnemonic_alt;
 
-static void
-init_opcode_tables (int mach)
+  /* Index of operand that generic relaxation has to check.  */
+  unsigned opcheckidx;
+
+  /* Base subtype index used.  */
+  enum arc_rlx_types subtype;
+};
+
+#define RELAX_TABLE_ENTRY(BITS, ISSIGNED, SIZE, NEXT)                  \
+  { (ISSIGNED) ? ((1 << ((BITS) - 1)) - 1) : ((1 << (BITS)) - 1),      \
+      (ISSIGNED) ? -(1 << ((BITS) - 1)) : 0,                           \
+      (SIZE),                                                          \
+      (NEXT) }                                                         \
+
+#define RELAX_TABLE_ENTRY_MAX(ISSIGNED, SIZE, NEXT)    \
+  { (ISSIGNED) ? 0x7FFFFFFF : 0xFFFFFFFF,              \
+      (ISSIGNED) ? -(0x7FFFFFFF) : 0,                   \
+      (SIZE),                                           \
+      (NEXT) }                                          \
+
+
+/* ARC relaxation table.  */
+const relax_typeS md_relax_table[] =
 {
-  int i;
-  char *last;
+  /* Fake entry.  */
+  {0, 0, 0, 0},
+
+  /* BL_S s13 ->
+     BL s25.  */
+  RELAX_TABLE_ENTRY (13, 1, 2, ARC_RLX_BL),
+  RELAX_TABLE_ENTRY (25, 1, 4, ARC_RLX_NONE),
+
+  /* B_S s10 ->
+     B s25.  */
+  RELAX_TABLE_ENTRY (10, 1, 2, ARC_RLX_B),
+  RELAX_TABLE_ENTRY (25, 1, 4, ARC_RLX_NONE),
+
+  /* ADD_S c,b, u3 ->
+     ADD<.f> a,b,u6 ->
+     ADD<.f> a,b,limm.  */
+  RELAX_TABLE_ENTRY (3, 0, 2, ARC_RLX_ADD_U6),
+  RELAX_TABLE_ENTRY (6, 0, 4, ARC_RLX_ADD_LIMM),
+  RELAX_TABLE_ENTRY_MAX (0, 8, ARC_RLX_NONE),
+
+  /* LD_S a, [b, u7] ->
+     LD<zz><.x><.aa><.di> a, [b, s9] ->
+     LD<zz><.x><.aa><.di> a, [b, limm] */
+  RELAX_TABLE_ENTRY (7, 0, 2, ARC_RLX_LD_S9),
+  RELAX_TABLE_ENTRY (9, 1, 4, ARC_RLX_LD_LIMM),
+  RELAX_TABLE_ENTRY_MAX (1, 8, ARC_RLX_NONE),
+
+  /* MOV_S b, u8 ->
+     MOV<.f> b, s12 ->
+     MOV<.f> b, limm.  */
+  RELAX_TABLE_ENTRY (8, 0, 2, ARC_RLX_MOV_S12),
+  RELAX_TABLE_ENTRY (8, 0, 4, ARC_RLX_MOV_LIMM),
+  RELAX_TABLE_ENTRY_MAX (0, 8, ARC_RLX_NONE),
+
+  /* SUB_S c, b, u3 ->
+     SUB<.f> a, b, u6 ->
+     SUB<.f> a, b, limm.  */
+  RELAX_TABLE_ENTRY (3, 0, 2, ARC_RLX_SUB_U6),
+  RELAX_TABLE_ENTRY (6, 0, 4, ARC_RLX_SUB_LIMM),
+  RELAX_TABLE_ENTRY_MAX (0, 8, ARC_RLX_NONE),
+
+  /* MPY<.f> a, b, u6 ->
+     MPY<.f> a, b, limm.  */
+  RELAX_TABLE_ENTRY (6, 0, 4, ARC_RLX_MPY_LIMM),
+  RELAX_TABLE_ENTRY_MAX (0, 8, ARC_RLX_NONE),
+
+  /* MOV<.f><.cc> b, u6 ->
+     MOV<.f><.cc> b, limm.  */
+  RELAX_TABLE_ENTRY (6, 0, 4, ARC_RLX_MOV_RLIMM),
+  RELAX_TABLE_ENTRY_MAX (0, 8, ARC_RLX_NONE),
+
+  /* ADD<.f><.cc> b, b, u6 ->
+     ADD<.f><.cc> b, b, limm.  */
+  RELAX_TABLE_ENTRY (6, 0, 4, ARC_RLX_ADD_RRLIMM),
+  RELAX_TABLE_ENTRY_MAX (0, 8, ARC_RLX_NONE),
+};
 
-  if ((arc_suffix_hash = hash_new ()) == NULL)
-    as_fatal (_("virtual memory exhausted"));
+/* Order of this table's entries matters!  */
+const struct arc_relaxable_ins arc_relaxable_insns[] =
+{
+  { "bl", { IMMEDIATE }, { 0 }, "bl_s", 0, ARC_RLX_BL_S },
+  { "b", { IMMEDIATE }, { 0 }, "b_s", 0, ARC_RLX_B_S },
+  { "add", { REGISTER, REGISTER_DUP, IMMEDIATE }, { 5, 1, 0 }, "add",
+    2, ARC_RLX_ADD_RRU6},
+  { "add", { REGISTER_S, REGISTER_S, IMMEDIATE }, { 0 }, "add_s", 2,
+    ARC_RLX_ADD_U3 },
+  { "add", { REGISTER, REGISTER, IMMEDIATE }, { 5, 0 }, "add", 2,
+    ARC_RLX_ADD_U6 },
+  { "ld", { REGISTER_S, BRACKET, REGISTER_S, IMMEDIATE, BRACKET },
+    { 0 }, "ld_s", 3, ARC_RLX_LD_U7 },
+  { "ld", { REGISTER, BRACKET, REGISTER_NO_GP, IMMEDIATE, BRACKET },
+    { 11, 4, 14, 17, 0 }, "ld", 3, ARC_RLX_LD_S9 },
+  { "mov", { REGISTER_S, IMMEDIATE }, { 0 }, "mov_s", 1, ARC_RLX_MOV_U8 },
+  { "mov", { REGISTER, IMMEDIATE }, { 5, 0 }, "mov", 1, ARC_RLX_MOV_S12 },
+  { "mov", { REGISTER, IMMEDIATE }, { 5, 1, 0 },"mov", 1, ARC_RLX_MOV_RU6 },
+  { "sub", { REGISTER_S, REGISTER_S, IMMEDIATE }, { 0 }, "sub_s", 2,
+    ARC_RLX_SUB_U3 },
+  { "sub", { REGISTER, REGISTER, IMMEDIATE }, { 5, 0 }, "sub", 2,
+    ARC_RLX_SUB_U6 },
+  { "mpy", { REGISTER, REGISTER, IMMEDIATE }, { 5, 0 }, "mpy", 2,
+    ARC_RLX_MPY_U6 },
+};
 
-  if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach))
-    as_warn (_("could not set architecture and machine"));
+const unsigned arc_num_relaxable_ins = ARRAY_SIZE (arc_relaxable_insns);
 
-  /* This initializes a few things in arc-opc.c that we need.
-     This must be called before the various arc_xxx_supported fns.  */
-  arc_opcode_init_tables (arc_get_opcode_mach (mach, target_big_endian));
+/* Pre-defined "_GLOBAL_OFFSET_TABLE_".  */
+symbolS * GOT_symbol = 0;
 
-  /* Only put the first entry of each equivalently named suffix in the
-     table.  */
-  last = "";
-  for (i = 0; i < arc_suffixes_count; i++)
-    {
-      if (strcmp (arc_suffixes[i].name, last) != 0)
-       hash_insert (arc_suffix_hash, arc_suffixes[i].name, (void *) (arc_suffixes + i));
-      last = arc_suffixes[i].name;
-    }
+/* Set to TRUE when we assemble instructions.  */
+static bfd_boolean assembling_insn = FALSE;
 
-  /* Since registers don't have a prefix, we put them in the symbol table so
-     they can't be used as symbols.  This also simplifies argument parsing as
-     we can let gas parse registers for us.  The recorded register number is
-     the address of the register's entry in arc_reg_names.
+/* List with attributes set explicitly.  */
+static bfd_boolean attributes_set_explicitly[NUM_KNOWN_OBJ_ATTRIBUTES];
 
-     If the register name is already in the table, then the existing
-     definition is assumed to be from an .ExtCoreRegister pseudo-op.  */
+/* Functions implementation.  */
 
-  for (i = 0; i < arc_reg_names_count; i++)
-    {
-      if (symbol_find (arc_reg_names[i].name))
-       continue;
-      /* Use symbol_create here instead of symbol_new so we don't try to
-        output registers into the object file's symbol table.  */
-      symbol_table_insert (symbol_create (arc_reg_names[i].name,
-                                         reg_section,
-                                         (valueT) &arc_reg_names[i],
-                                         &zero_address_frag));
-    }
+/* Return a pointer to ARC_OPCODE_HASH_ENTRY that identifies all
+   ARC_OPCODE entries in ARC_OPCODE_HASH that match NAME, or NULL if there
+   are no matching entries in ARC_OPCODE_HASH.  */
+
+static const struct arc_opcode_hash_entry *
+arc_find_opcode (const char *name)
+{
+  const struct arc_opcode_hash_entry *entry;
 
-  /* Tell `.option' it's too late.  */
-  cpu_tables_init_p = 1;
+  entry = hash_find (arc_opcode_hash, name);
+  return entry;
+}
+
+/* Initialise the iterator ITER.  */
+
+static void
+arc_opcode_hash_entry_iterator_init (struct arc_opcode_hash_entry_iterator *iter)
+{
+  iter->index = 0;
+  iter->opcode = NULL;
 }
-\f
-/* Insert an operand value into an instruction.
-   If REG is non-NULL, it is a register number and ignore VAL.  */
 
-static arc_insn
-arc_insert_operand (arc_insn insn,
-                   const struct arc_operand *operand,
-                   int mods,
-                   const struct arc_operand_value *reg,
-                   offsetT val,
-                   char *file,
-                   unsigned int line)
+/* Return the next ARC_OPCODE from ENTRY, using ITER to hold state between
+   calls to this function.  Return NULL when all ARC_OPCODE entries have
+   been returned.  */
+
+static const struct arc_opcode *
+arc_opcode_hash_entry_iterator_next (const struct arc_opcode_hash_entry *entry,
+                                    struct arc_opcode_hash_entry_iterator *iter)
 {
-  if (operand->bits != 32)
+  if (iter->opcode == NULL && iter->index == 0)
+    {
+      gas_assert (entry->count > 0);
+      iter->opcode = entry->opcode[iter->index];
+    }
+  else if (iter->opcode != NULL)
     {
-      long min, max;
-      offsetT test;
+      const char *old_name = iter->opcode->name;
 
-      if ((operand->flags & ARC_OPERAND_SIGNED) != 0)
+      iter->opcode++;
+      if (iter->opcode->name == NULL
+         || strcmp (old_name, iter->opcode->name) != 0)
        {
-         if ((operand->flags & ARC_OPERAND_SIGNOPT) != 0)
-           max = (1 << operand->bits) - 1;
+         iter->index++;
+         if (iter->index == entry->count)
+           iter->opcode = NULL;
          else
-           max = (1 << (operand->bits - 1)) - 1;
-         min = - (1 << (operand->bits - 1));
-       }
-      else
-       {
-         max = (1 << operand->bits) - 1;
-         min = 0;
+           iter->opcode = entry->opcode[iter->index];
        }
+    }
 
-      if ((operand->flags & ARC_OPERAND_NEGATIVE) != 0)
-       test = - val;
-      else
-       test = val;
+  return iter->opcode;
+}
 
-      if (test < (offsetT) min || test > (offsetT) max)
-       as_warn_value_out_of_range (_("operand"), test, (offsetT) min, (offsetT) max, file, line);
-    }
+/* Insert an opcode into opcode hash structure.  */
 
-  if (operand->insert)
-    {
-      const char *errmsg;
+static void
+arc_insert_opcode (const struct arc_opcode *opcode)
+{
+  const char *name, *retval;
+  struct arc_opcode_hash_entry *entry;
+  name = opcode->name;
 
-      errmsg = NULL;
-      insn = (*operand->insert) (insn, operand, mods, reg, (long) val, &errmsg);
-      if (errmsg != (const char *) NULL)
-       as_warn ("%s", errmsg);
+  entry = hash_find (arc_opcode_hash, name);
+  if (entry == NULL)
+    {
+      entry = XNEW (struct arc_opcode_hash_entry);
+      entry->count = 0;
+      entry->opcode = NULL;
+
+      retval = hash_insert (arc_opcode_hash, name, (void *) entry);
+      if (retval)
+       as_fatal (_("internal error: can't hash opcode '%s': %s"),
+                 name, retval);
     }
-  else
-    insn |= (((long) val & ((1 << operand->bits) - 1))
-            << operand->shift);
 
-  return insn;
+  entry->opcode = XRESIZEVEC (const struct arc_opcode *, entry->opcode,
+                             entry->count + 1);
+
+  if (entry->opcode == NULL)
+    as_fatal (_("Virtual memory exhausted"));
+
+  entry->opcode[entry->count] = opcode;
+  entry->count++;
 }
 
-/* We need to keep a list of fixups.  We can't simply generate them as
-   we go, because that would require us to first create the frag, and
-   that would screw up references to ``.''.  */
 
-struct arc_fixup
+/* Like md_number_to_chars but for middle-endian values.  The 4-byte limm
+   value, is encoded as 'middle-endian' for a little-endian target.  This
+   function is used for regular 4, 6, and 8 byte instructions as well.  */
+
+static void
+md_number_to_chars_midend (char *buf, unsigned long long val, int n)
 {
-  /* index into `arc_operands'  */
-  int opindex;
-  expressionS exp;
-};
+  switch (n)
+    {
+    case 2:
+      md_number_to_chars (buf, val, n);
+      break;
+    case 6:
+      md_number_to_chars (buf, (val & 0xffff00000000ull) >> 32, 2);
+      md_number_to_chars_midend (buf + 2, (val & 0xffffffff), 4);
+      break;
+    case 4:
+      md_number_to_chars (buf,     (val & 0xffff0000) >> 16, 2);
+      md_number_to_chars (buf + 2, (val & 0xffff), 2);
+      break;
+    case 8:
+      md_number_to_chars_midend (buf, (val & 0xffffffff00000000ull) >> 32, 4);
+      md_number_to_chars_midend (buf + 4, (val & 0xffffffff), 4);
+      break;
+    default:
+      abort ();
+    }
+}
 
-#define MAX_FIXUPS 5
+/* Check if a feature is allowed for a specific CPU.  */
 
-#define MAX_SUFFIXES 5
+static void
+arc_check_feature (void)
+{
+  unsigned i;
 
-/* Compute the reloc type of an expression.
-   The possibly modified expression is stored in EXPNEW.
+  if (!selected_cpu.features
+      || !selected_cpu.name)
+    return;
 
-   This is used to convert the expressions generated by the %-op's into
-   the appropriate operand type.  It is called for both data in instructions
-   (operands) and data outside instructions (variables, debugging info, etc.).
+  for (i = 0; i < ARRAY_SIZE (feature_list); i++)
+    if ((selected_cpu.features & feature_list[i].feature)
+       && !(selected_cpu.flags & feature_list[i].cpus))
+      as_bad (_("invalid %s option for %s cpu"), feature_list[i].name,
+             selected_cpu.name);
 
-   Currently supported %-ops:
+  for (i = 0; i < ARRAY_SIZE (conflict_list); i++)
+    if ((selected_cpu.features & conflict_list[i]) == conflict_list[i])
+      as_bad(_("conflicting ISA extension attributes."));
+}
 
-   %st(symbol): represented as "symbol >> 2"
-                "st" is short for STatus as in the status register (pc)
+/* Select an appropriate entry from CPU_TYPES based on ARG and initialise
+   the relevant static global variables.  Parameter SEL describes where
+   this selection originated from.  */
 
-   DEFAULT_TYPE is the type to use if no special processing is required.
+static void
+arc_select_cpu (const char *arg, enum mach_selection_type sel)
+{
+  int i;
 
-   DATA_P is non-zero for data or limm values, zero for insn operands.
-   Remember that the opcode "insertion fns" cannot be used on data, they're
-   only for inserting operands into insns.  They also can't be used for limm
-   values as the insertion routines don't handle limm values.  When called for
-   insns we return fudged reloc types (real_value - BFD_RELOC_UNUSED).  When
-   called for data or limm values we use real reloc types.  */
+  /* We should only set a default if we've not made a selection from some
+     other source.  */
+  gas_assert (sel != MACH_SELECTION_FROM_DEFAULT
+              || mach_selection_mode == MACH_SELECTION_NONE);
 
-static int
-get_arc_exp_reloc_type (int data_p,
-                       int default_type,
-                       expressionS *exp,
-                       expressionS *expnew)
-{
-  /* If the expression is "symbol >> 2" we must change it to just "symbol",
-     as fix_new_exp can't handle it.  Similarly for (symbol - symbol) >> 2.
-     That's ok though.  What's really going on here is that we're using
-     ">> 2" as a special syntax for specifying BFD_RELOC_ARC_B26.  */
+  if ((mach_selection_mode == MACH_SELECTION_FROM_CPU_DIRECTIVE)
+      && (sel == MACH_SELECTION_FROM_CPU_DIRECTIVE))
+    as_bad (_("Multiple .cpu directives found"));
 
-  if (exp->X_op == O_right_shift
-      && exp->X_op_symbol != NULL
-      && exp->X_op_symbol->sy_value.X_op == O_constant
-      && exp->X_op_symbol->sy_value.X_add_number == 2
-      && exp->X_add_number == 0)
+  /* Look for a matching entry in CPU_TYPES array.  */
+  for (i = 0; cpu_types[i].name; ++i)
     {
-      if (exp->X_add_symbol != NULL
-         && (exp->X_add_symbol->sy_value.X_op == O_constant
-             || exp->X_add_symbol->sy_value.X_op == O_symbol))
-       {
-         *expnew = *exp;
-         expnew->X_op = O_symbol;
-         expnew->X_op_symbol = NULL;
-         return data_p ? BFD_RELOC_ARC_B26 : arc_operand_map['J'];
-       }
-      else if (exp->X_add_symbol != NULL
-              && exp->X_add_symbol->sy_value.X_op == O_subtract)
-       {
-         *expnew = exp->X_add_symbol->sy_value;
-         return data_p ? BFD_RELOC_ARC_B26 : arc_operand_map['J'];
-       }
+      if (!strcasecmp (cpu_types[i].name, arg))
+        {
+          /* If a previous selection was made on the command line, then we
+             allow later selections on the command line to override earlier
+             ones.  However, a selection from a '.cpu NAME' directive must
+             match the command line selection, or we give a warning.  */
+          if (mach_selection_mode == MACH_SELECTION_FROM_COMMAND_LINE)
+            {
+              gas_assert (sel == MACH_SELECTION_FROM_COMMAND_LINE
+                          || sel == MACH_SELECTION_FROM_CPU_DIRECTIVE);
+              if (sel == MACH_SELECTION_FROM_CPU_DIRECTIVE
+                  && selected_cpu.mach != cpu_types[i].mach)
+                {
+                  as_warn (_("Command-line value overrides \".cpu\" directive"));
+                }
+             return;
+            }
+
+         /* Initialise static global data about selected machine type.  */
+         selected_cpu.flags = cpu_types[i].flags;
+         selected_cpu.name = cpu_types[i].name;
+         selected_cpu.features = cpu_types[i].features | cl_features;
+         selected_cpu.mach = cpu_types[i].mach;
+         selected_cpu.eflags = ((selected_cpu.eflags & ~EF_ARC_MACH_MSK)
+                                | cpu_types[i].eflags);
+          break;
+        }
     }
 
-  *expnew = *exp;
-  return default_type;
+  if (!cpu_types[i].name)
+    as_fatal (_("unknown architecture: %s\n"), arg);
+
+  /* Check if set features are compatible with the chosen CPU.  */
+  arc_check_feature ();
+
+  mach_selection_mode = sel;
 }
-\f
-static int
-arc_set_ext_seg (void)
+
+/* Here ends all the ARCompact extension instruction assembling
+   stuff.  */
+
+static void
+arc_extra_reloc (int r_type)
 {
-  if (!arcext_section)
+  char *sym_name, c;
+  symbolS *sym, *lab = NULL;
+
+  if (*input_line_pointer == '@')
+    input_line_pointer++;
+  c = get_symbol_name (&sym_name);
+  sym = symbol_find_or_make (sym_name);
+  restore_line_pointer (c);
+  if (c == ',' && r_type == BFD_RELOC_ARC_TLS_GD_LD)
     {
-      arcext_section = subseg_new (".arcextmap", 0);
-      bfd_set_section_flags (stdoutput, arcext_section,
-                            SEC_READONLY | SEC_HAS_CONTENTS);
+      ++input_line_pointer;
+      char *lab_name;
+      c = get_symbol_name (&lab_name);
+      lab = symbol_find_or_make (lab_name);
+      restore_line_pointer (c);
+    }
+
+  /* These relocations exist as a mechanism for the compiler to tell the
+     linker how to patch the code if the tls model is optimised.  However,
+     the relocation itself does not require any space within the assembler
+     fragment, and so we pass a size of 0.
+
+     The lines that generate these relocations look like this:
+
+         .tls_gd_ld @.tdata`bl __tls_get_addr@plt
+
+     The '.tls_gd_ld @.tdata' is processed first and generates the
+     additional relocation, while the 'bl __tls_get_addr@plt' is processed
+     second and generates the additional branch.
+
+     It is possible that the additional relocation generated by the
+     '.tls_gd_ld @.tdata' will be attached at the very end of one fragment,
+     while the 'bl __tls_get_addr@plt' will be generated as the first thing
+     in the next fragment.  This will be fine; both relocations will still
+     appear to be at the same address in the generated object file.
+     However, this only works as the additional relocation is generated
+     with size of 0 bytes.  */
+  fixS *fixP
+    = fix_new (frag_now,       /* Which frag?  */
+              frag_now_fix (), /* Where in that frag?  */
+              0,               /* size: 1, 2, or 4 usually.  */
+              sym,             /* X_add_symbol.  */
+              0,               /* X_add_number.  */
+              FALSE,           /* TRUE if PC-relative relocation.  */
+              r_type           /* Relocation type.  */);
+  fixP->fx_subsy = lab;
+}
+
+static symbolS *
+arc_lcomm_internal (int ignore ATTRIBUTE_UNUSED,
+                   symbolS *symbolP, addressT size)
+{
+  addressT align = 0;
+  SKIP_WHITESPACE ();
+
+  if (*input_line_pointer == ',')
+    {
+      align = parse_align (1);
+
+      if (align == (addressT) -1)
+       return NULL;
     }
   else
-    subseg_set (arcext_section, 0);
-  return 1;
+    {
+      if (size >= 8)
+       align = 3;
+      else if (size >= 4)
+       align = 2;
+      else if (size >= 2)
+       align = 1;
+      else
+       align = 0;
+    }
+
+  bss_alloc (symbolP, size, align);
+  S_CLEAR_EXTERNAL (symbolP);
+
+  return symbolP;
 }
 
 static void
-arc_extoper (int opertype)
+arc_lcomm (int ignore)
+{
+  symbolS *symbolP = s_comm_internal (ignore, arc_lcomm_internal);
+
+  if (symbolP)
+    symbol_get_bfdsym (symbolP)->flags |= BSF_OBJECT;
+}
+
+/* Select the cpu we're assembling for.  */
+
+static void
+arc_option (int ignore ATTRIBUTE_UNUSED)
 {
-  char *name;
-  char *mode;
   char c;
-  char *p;
-  int imode = 0;
-  int number;
-  struct arc_ext_operand_value *ext_oper;
-  symbolS *symbolP;
+  char *cpu;
+  const char *cpu_name;
+
+  c = get_symbol_name (&cpu);
+
+  cpu_name = cpu;
+  if ((!strcmp ("ARC600", cpu))
+      || (!strcmp ("ARC601", cpu))
+      || (!strcmp ("A6", cpu)))
+    cpu_name = "arc600";
+  else if ((!strcmp ("ARC700", cpu))
+           || (!strcmp ("A7", cpu)))
+    cpu_name = "arc700";
+  else if (!strcmp ("EM", cpu))
+    cpu_name = "arcem";
+  else if (!strcmp ("HS", cpu))
+    cpu_name = "archs";
+  else if (!strcmp ("NPS400", cpu))
+    cpu_name = "nps400";
+
+  arc_select_cpu (cpu_name, MACH_SELECTION_FROM_CPU_DIRECTIVE);
+
+  restore_line_pointer (c);
+  demand_empty_rest_of_line ();
+}
 
-  segT old_sec;
-  int old_subsec;
+/* Smartly print an expression.  */
 
-  name = input_line_pointer;
-  c = get_symbol_end ();
-  name = xstrdup (name);
+static void
+debug_exp (expressionS *t)
+{
+  const char *name ATTRIBUTE_UNUSED;
+  const char *namemd ATTRIBUTE_UNUSED;
 
-  p = name;
-  while (*p)
+  pr_debug ("debug_exp: ");
+
+  switch (t->X_op)
     {
-      *p = TOLOWER (*p);
-      p++;
+    default:                   name = "unknown";               break;
+    case O_illegal:            name = "O_illegal";             break;
+    case O_absent:             name = "O_absent";              break;
+    case O_constant:           name = "O_constant";            break;
+    case O_symbol:             name = "O_symbol";              break;
+    case O_symbol_rva:         name = "O_symbol_rva";          break;
+    case O_register:           name = "O_register";            break;
+    case O_big:                        name = "O_big";                 break;
+    case O_uminus:             name = "O_uminus";              break;
+    case O_bit_not:            name = "O_bit_not";             break;
+    case O_logical_not:                name = "O_logical_not";         break;
+    case O_multiply:           name = "O_multiply";            break;
+    case O_divide:             name = "O_divide";              break;
+    case O_modulus:            name = "O_modulus";             break;
+    case O_left_shift:         name = "O_left_shift";          break;
+    case O_right_shift:                name = "O_right_shift";         break;
+    case O_bit_inclusive_or:   name = "O_bit_inclusive_or";    break;
+    case O_bit_or_not:         name = "O_bit_or_not";          break;
+    case O_bit_exclusive_or:   name = "O_bit_exclusive_or";    break;
+    case O_bit_and:            name = "O_bit_and";             break;
+    case O_add:                        name = "O_add";                 break;
+    case O_subtract:           name = "O_subtract";            break;
+    case O_eq:                 name = "O_eq";                  break;
+    case O_ne:                 name = "O_ne";                  break;
+    case O_lt:                 name = "O_lt";                  break;
+    case O_le:                 name = "O_le";                  break;
+    case O_ge:                 name = "O_ge";                  break;
+    case O_gt:                 name = "O_gt";                  break;
+    case O_logical_and:                name = "O_logical_and";         break;
+    case O_logical_or:         name = "O_logical_or";          break;
+    case O_index:              name = "O_index";               break;
+    case O_bracket:            name = "O_bracket";             break;
+    case O_colon:              name = "O_colon";               break;
+    case O_addrtype:           name = "O_addrtype";            break;
     }
 
-  /* just after name is now '\0'  */
-  p = input_line_pointer;
-  *p = c;
-  SKIP_WHITESPACE ();
-
-  if (*input_line_pointer != ',')
+  switch (t->X_md)
     {
-      as_bad (_("expected comma after operand name"));
-      ignore_rest_of_line ();
-      free (name);
-      return;
+    default:                   namemd = "unknown";             break;
+    case O_gotoff:             namemd = "O_gotoff";            break;
+    case O_gotpc:              namemd = "O_gotpc";             break;
+    case O_plt:                        namemd = "O_plt";               break;
+    case O_sda:                        namemd = "O_sda";               break;
+    case O_pcl:                        namemd = "O_pcl";               break;
+    case O_tlsgd:              namemd = "O_tlsgd";             break;
+    case O_tlsie:              namemd = "O_tlsie";             break;
+    case O_tpoff9:             namemd = "O_tpoff9";            break;
+    case O_tpoff:              namemd = "O_tpoff";             break;
+    case O_dtpoff9:            namemd = "O_dtpoff9";           break;
+    case O_dtpoff:             namemd = "O_dtpoff";            break;
     }
 
-  input_line_pointer++;                /* skip ','  */
-  number = get_absolute_expression ();
+  pr_debug ("%s (%s, %s, %d, %s)", name,
+           (t->X_add_symbol) ? S_GET_NAME (t->X_add_symbol) : "--",
+           (t->X_op_symbol) ? S_GET_NAME (t->X_op_symbol) : "--",
+           (int) t->X_add_number,
+           (t->X_md) ? namemd : "--");
+  pr_debug ("\n");
+  fflush (stderr);
+}
 
-  if (number < 0)
+/* Helper for parsing an argument, used for sorting out the relocation
+   type.  */
+
+static void
+parse_reloc_symbol (expressionS *resultP)
+{
+  char *reloc_name, c, *sym_name;
+  size_t len;
+  int i;
+  const struct arc_reloc_op_tag *r;
+  expressionS right;
+  symbolS *base;
+
+  /* A relocation operand has the following form
+     @identifier@relocation_type.  The identifier is already in
+     tok!  */
+  if (resultP->X_op != O_symbol)
     {
-      as_bad (_("negative operand number %d"), number);
-      ignore_rest_of_line ();
-      free (name);
+      as_bad (_("No valid label relocation operand"));
+      resultP->X_op = O_illegal;
       return;
     }
 
-  if (opertype)
+  /* Parse @relocation_type.  */
+  input_line_pointer++;
+  c = get_symbol_name (&reloc_name);
+  len = input_line_pointer - reloc_name;
+  if (len == 0)
     {
-      SKIP_WHITESPACE ();
-
-      if (*input_line_pointer != ',')
-       {
-         as_bad (_("expected comma after register-number"));
-         ignore_rest_of_line ();
-         free (name);
-         return;
-       }
-
-      input_line_pointer++;            /* skip ','  */
-      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;
-               }
-             else
-               {
-                 imode = ARC_REGISTER_WRITEONLY;
-                 input_line_pointer += 1;
-               }
-           }
-       }
-      SKIP_WHITESPACE ();
-      if (1 == opertype)
-       {
-         if (*input_line_pointer != ',')
-           {
-             as_bad (_("expected comma after register-mode"));
-             ignore_rest_of_line ();
-             free (name);
-             return;
-           }
-
-         input_line_pointer++;         /* skip ','  */
-
-         if (!strncmp (input_line_pointer, "cannot_shortcut", 15))
-           {
-             imode |= arc_get_noshortcut_flag ();
-             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;
-               }
-             else
-               {
-                 input_line_pointer += 12;
-               }
-           }
-       }
+      as_bad (_("No relocation operand"));
+      resultP->X_op = O_illegal;
+      return;
     }
 
-  if ((opertype == 1) && number > 60)
+  /* Go through known relocation and try to find a match.  */
+  r = &arc_reloc_op[0];
+  for (i = arc_num_reloc_op - 1; i >= 0; i--, r++)
+    if (len == r->length
+       && memcmp (reloc_name, r->name, len) == 0)
+      break;
+  if (i < 0)
     {
-      as_bad (_("core register value (%d) too large"), number);
-      ignore_rest_of_line ();
-      free (name);
+      as_bad (_("Unknown relocation operand: @%s"), reloc_name);
+      resultP->X_op = O_illegal;
       return;
     }
 
-  if ((opertype == 0) && number > 31)
+  *input_line_pointer = c;
+  SKIP_WHITESPACE_AFTER_NAME ();
+  /* Extra check for TLS: base.  */
+  if (*input_line_pointer == '@')
     {
-      as_bad (_("condition code value (%d) too large"), number);
-      ignore_rest_of_line ();
-      free (name);
-      return;
+      if (resultP->X_op_symbol != NULL
+         || resultP->X_op != O_symbol)
+       {
+         as_bad (_("Unable to parse TLS base: %s"),
+                 input_line_pointer);
+         resultP->X_op = O_illegal;
+         return;
+       }
+      input_line_pointer++;
+      c = get_symbol_name (&sym_name);
+      base = symbol_find_or_make (sym_name);
+      resultP->X_op = O_subtract;
+      resultP->X_op_symbol = base;
+      restore_line_pointer (c);
+      right.X_add_number = 0;
     }
 
-  ext_oper = (struct arc_ext_operand_value *)
-      xmalloc (sizeof (struct arc_ext_operand_value));
-
-  if (opertype)
+  if ((*input_line_pointer != '+')
+      && (*input_line_pointer != '-'))
+    right.X_add_number = 0;
+  else
     {
-      /* If the symbol already exists, point it at the new definition.  */
-      if ((symbolP = symbol_find (name)))
+      /* Parse the constant of a complex relocation expression
+        like @identifier@reloc +/- const.  */
+      if (! r->complex_expr)
        {
-         if (S_GET_SEGMENT (symbolP) == reg_section)
-           S_SET_VALUE (symbolP, (valueT) &ext_oper->operand);
-         else
-           {
-             as_bad (_("attempt to override symbol: %s"), name);
-             ignore_rest_of_line ();
-             free (name);
-             free (ext_oper);
-             return;
-           }
+         as_bad (_("@%s is not a complex relocation."), r->name);
+         resultP->X_op = O_illegal;
+         return;
        }
-      else
+      expression (&right);
+      if (right.X_op != O_constant)
        {
-         /* If its not there, add it.  */
-         symbol_table_insert (symbol_create (name, reg_section,
-                                             (valueT) &ext_oper->operand,
-                                             &zero_address_frag));
+         as_bad (_("Bad expression: @%s + %s."),
+                 r->name, input_line_pointer);
+         resultP->X_op = O_illegal;
+         return;
        }
     }
 
-  ext_oper->operand.name  = name;
-  ext_oper->operand.value = number;
-  ext_oper->operand.type  = arc_operand_type (opertype);
-  ext_oper->operand.flags = imode;
+  resultP->X_md = r->op;
+  resultP->X_add_number = right.X_add_number;
+}
 
-  ext_oper->next = arc_ext_operands;
-  arc_ext_operands = ext_oper;
+/* Parse the arguments to an opcode.  */
 
-  /* OK, now that we know what this operand is, put a description in
-     the arc extension section of the output file.  */
+static int
+tokenize_arguments (char *str,
+                   expressionS *tok,
+                   int ntok)
+{
+  char *old_input_line_pointer;
+  bfd_boolean saw_comma = FALSE;
+  bfd_boolean saw_arg = FALSE;
+  int brk_lvl = 0;
+  int num_args = 0;
 
-  old_sec    = now_seg;
-  old_subsec = now_subseg;
+  memset (tok, 0, sizeof (*tok) * ntok);
 
-  arc_set_ext_seg ();
+  /* Save and restore input_line_pointer around this function.  */
+  old_input_line_pointer = input_line_pointer;
+  input_line_pointer = str;
 
-  switch (opertype)
+  while (*input_line_pointer)
     {
-    case 0:
-      p = frag_more (1);
-      *p = 3 + strlen (name) + 1;
-      p = frag_more (1);
-      *p = EXT_COND_CODE;
-      p = frag_more (1);
-      *p = number;
-      p = frag_more (strlen (name) + 1);
-      strcpy (p, name);
-      break;
-    case 1:
-      p = frag_more (1);
-      *p = 3 + strlen (name) + 1;
-      p = frag_more (1);
-      *p = EXT_CORE_REGISTER;
-      p = frag_more (1);
-      *p = number;
-      p = frag_more (strlen (name) + 1);
-      strcpy (p, name);
-      break;
-    case 2:
-      p = frag_more (1);
-      *p = 6 + strlen (name) + 1;
-      p = frag_more (1);
-      *p = EXT_AUX_REGISTER;
-      p = frag_more (1);
-      *p = number >> 24 & 0xff;
-      p = frag_more (1);
-      *p = number >> 16 & 0xff;
-      p = frag_more (1);
-      *p = number >>  8 & 0xff;
-      p = frag_more (1);
-      *p = number       & 0xff;
-      p = frag_more (strlen (name) + 1);
-      strcpy (p, name);
-      break;
-    default:
-      as_bad (_("invalid opertype"));
-      ignore_rest_of_line ();
-      free (name);
-      return;
-      break;
-    }
+      SKIP_WHITESPACE ();
+      switch (*input_line_pointer)
+       {
+       case '\0':
+         goto fini;
+
+       case ',':
+         input_line_pointer++;
+         if (saw_comma || !saw_arg)
+           goto err;
+         saw_comma = TRUE;
+         break;
 
-  subseg_set (old_sec, old_subsec);
+       case '}':
+       case ']':
+         ++input_line_pointer;
+         --brk_lvl;
+         if (!saw_arg || num_args == ntok)
+           goto err;
+         tok->X_op = O_bracket;
+         ++tok;
+         ++num_args;
+         break;
 
-  /* Enter all registers into the symbol table.  */
+       case '{':
+       case '[':
+         input_line_pointer++;
+         if (brk_lvl || num_args == ntok)
+           goto err;
+         ++brk_lvl;
+         tok->X_op = O_bracket;
+         ++tok;
+         ++num_args;
+         break;
 
-  demand_empty_rest_of_line ();
-}
+        case ':':
+          input_line_pointer++;
+          if (!saw_arg || num_args == ntok)
+            goto err;
+          tok->X_op = O_colon;
+          saw_arg = FALSE;
+          ++tok;
+          ++num_args;
+          break;
+
+       case '@':
+         /* We have labels, function names and relocations, all
+            starting with @ symbol.  Sort them out.  */
+         if ((saw_arg && !saw_comma) || num_args == ntok)
+           goto err;
+
+         /* Parse @label.  */
+         input_line_pointer++;
+         tok->X_op = O_symbol;
+         tok->X_md = O_absent;
+         expression (tok);
+
+         if (*input_line_pointer == '@')
+           parse_reloc_symbol (tok);
+
+         debug_exp (tok);
+
+         if (tok->X_op == O_illegal
+              || tok->X_op == O_absent
+              || num_args == ntok)
+           goto err;
+
+         saw_comma = FALSE;
+         saw_arg = TRUE;
+         tok++;
+         num_args++;
+         break;
 
-static void
-arc_extinst (int ignore ATTRIBUTE_UNUSED)
-{
-  char syntax[129];
-  char *name;
-  char *p;
-  char c;
-  int suffixcode = -1;
-  int opcode, subopcode;
-  int i;
-  int s_class = 0;
-  int name_len;
-  struct arc_opcode *ext_op;
+       case '%':
+         /* Can be a register.  */
+         ++input_line_pointer;
+         /* Fall through.  */
+       default:
 
-  segT old_sec;
-  int old_subsec;
+         if ((saw_arg && !saw_comma) || num_args == ntok)
+           goto err;
 
-  name = input_line_pointer;
-  c = get_symbol_end ();
-  name = xstrdup (name);
-  strcpy (syntax, name);
-  name_len = strlen (name);
+         tok->X_op = O_absent;
+         tok->X_md = O_absent;
+         expression (tok);
 
-  /* just after name is now '\0'  */
-  p = input_line_pointer;
-  *p = c;
+         /* Legacy: There are cases when we have
+            identifier@relocation_type, if it is the case parse the
+            relocation type as well.  */
+         if (*input_line_pointer == '@')
+           parse_reloc_symbol (tok);
 
-  SKIP_WHITESPACE ();
+         debug_exp (tok);
 
-  if (*input_line_pointer != ',')
-    {
-      as_bad (_("expected comma after operand name"));
-      ignore_rest_of_line ();
-      return;
+         if (tok->X_op == O_illegal
+              || tok->X_op == O_absent
+              || num_args == ntok)
+           goto err;
+
+         saw_comma = FALSE;
+         saw_arg = TRUE;
+         tok++;
+         num_args++;
+         break;
+       }
     }
 
-  input_line_pointer++;                /* skip ','  */
-  opcode = get_absolute_expression ();
+ fini:
+  if (saw_comma || brk_lvl)
+    goto err;
+  input_line_pointer = old_input_line_pointer;
 
-  SKIP_WHITESPACE ();
+  return num_args;
 
-  if (*input_line_pointer != ',')
-    {
-      as_bad (_("expected comma after opcode"));
-      ignore_rest_of_line ();
-      return;
-    }
+ err:
+  if (brk_lvl)
+    as_bad (_("Brackets in operand field incorrect"));
+  else if (saw_comma)
+    as_bad (_("extra comma"));
+  else if (!saw_arg)
+    as_bad (_("missing argument"));
+  else
+    as_bad (_("missing comma or colon"));
+  input_line_pointer = old_input_line_pointer;
+  return -1;
+}
 
-  input_line_pointer++;                /* skip ','  */
-  subopcode = get_absolute_expression ();
+/* Parse the flags to a structure.  */
 
-  if (subopcode < 0)
-    {
-      as_bad (_("negative subopcode %d"), subopcode);
-      ignore_rest_of_line ();
-      return;
-    }
+static int
+tokenize_flags (const char *str,
+               struct arc_flags flags[],
+               int nflg)
+{
+  char *old_input_line_pointer;
+  bfd_boolean saw_flg = FALSE;
+  bfd_boolean saw_dot = FALSE;
+  int num_flags  = 0;
+  size_t flgnamelen;
+
+  memset (flags, 0, sizeof (*flags) * nflg);
+
+  /* Save and restore input_line_pointer around this function.  */
+  old_input_line_pointer = input_line_pointer;
+  input_line_pointer = (char *) str;
 
-  if (subopcode)
+  while (*input_line_pointer)
     {
-      if (3 != opcode)
-       {
-         as_bad (_("subcode value found when opcode not equal 0x03"));
-         ignore_rest_of_line ();
-         return;
-       }
-      else
+      switch (*input_line_pointer)
        {
-         if (subopcode < 0x09 || subopcode == 0x3f)
-           {
-             as_bad (_("invalid subopcode %d"), subopcode);
-             ignore_rest_of_line ();
-             return;
-           }
-       }
-    }
+       case ' ':
+       case '\0':
+         goto fini;
+
+       case '.':
+         input_line_pointer++;
+         if (saw_dot)
+           goto err;
+         saw_dot = TRUE;
+         saw_flg = FALSE;
+         break;
 
-  SKIP_WHITESPACE ();
+       default:
+         if (saw_flg && !saw_dot)
+           goto err;
 
-  if (*input_line_pointer != ',')
-    {
-      as_bad (_("expected comma after subopcode"));
-      ignore_rest_of_line ();
-      return;
-    }
+         if (num_flags >= nflg)
+           goto err;
 
-  input_line_pointer++;                /* skip ','  */
+         flgnamelen = strspn (input_line_pointer,
+                              "abcdefghijklmnopqrstuvwxyz0123456789");
+         if (flgnamelen > MAX_FLAG_NAME_LENGTH)
+           goto err;
 
-  for (i = 0; i < (int) MAXSUFFIXCLASS; i++)
-    {
-      if (!strncmp (suffixclass[i].name,input_line_pointer, suffixclass[i].len))
-       {
-         suffixcode = i;
-         input_line_pointer += suffixclass[i].len;
+         memcpy (flags->name, input_line_pointer, flgnamelen);
+
+         input_line_pointer += flgnamelen;
+         flags++;
+         saw_dot = FALSE;
+         saw_flg = TRUE;
+         num_flags++;
          break;
        }
     }
 
-  if (-1 == suffixcode)
-    {
-      as_bad (_("invalid suffix class"));
-      ignore_rest_of_line ();
-      return;
-    }
+ fini:
+  input_line_pointer = old_input_line_pointer;
+  return num_flags;
 
-  SKIP_WHITESPACE ();
+ err:
+  if (saw_dot)
+    as_bad (_("extra dot"));
+  else if (!saw_flg)
+    as_bad (_("unrecognized flag"));
+  else
+    as_bad (_("failed to parse flags"));
+  input_line_pointer = old_input_line_pointer;
+  return -1;
+}
 
-  if (*input_line_pointer != ',')
-    {
-      as_bad (_("expected comma after suffix class"));
-      ignore_rest_of_line ();
-      return;
-    }
+/* Apply the fixups in order.  */
 
-  input_line_pointer++;                /* skip ','  */
+static void
+apply_fixups (struct arc_insn *insn, fragS *fragP, int fix)
+{
+  int i;
 
-  for (i = 0; i < (int) MAXSYNTAXCLASS; i++)
+  for (i = 0; i < insn->nfixups; i++)
     {
-      if (!strncmp (syntaxclass[i].name,input_line_pointer, syntaxclass[i].len))
+      struct arc_fixup *fixup = &insn->fixups[i];
+      int size, pcrel, offset = 0;
+
+      /* FIXME! the reloc size is wrong in the BFD file.
+        When it is fixed please delete me.  */
+      size = ((insn->len == 2) && !fixup->islong) ? 2 : 4;
+
+      if (fixup->islong)
+       offset = insn->len;
+
+      /* Some fixups are only used internally, thus no howto.  */
+      if ((int) fixup->reloc == 0)
+       as_fatal (_("Unhandled reloc type"));
+
+      if ((int) fixup->reloc < 0)
        {
-         s_class = syntaxclass[i].s_class;
-         input_line_pointer += syntaxclass[i].len;
-         break;
+         /* FIXME! the reloc size is wrong in the BFD file.
+            When it is fixed please enable me.
+            size = ((insn->len == 2 && !fixup->islong) ? 2 : 4; */
+         pcrel = fixup->pcrel;
+       }
+      else
+       {
+         reloc_howto_type *reloc_howto =
+           bfd_reloc_type_lookup (stdoutput,
+                                  (bfd_reloc_code_real_type) fixup->reloc);
+         gas_assert (reloc_howto);
+
+         /* FIXME! the reloc size is wrong in the BFD file.
+            When it is fixed please enable me.
+            size = bfd_get_reloc_size (reloc_howto); */
+         pcrel = reloc_howto->pc_relative;
        }
-    }
 
-  if (0 == (SYNTAX_VALID & s_class))
-    {
-      as_bad (_("invalid syntax class"));
-      ignore_rest_of_line ();
-      return;
+      pr_debug ("%s:%d: apply_fixups: new %s fixup (PCrel:%s) of size %d @ \
+offset %d + %d\n",
+               fragP->fr_file, fragP->fr_line,
+               (fixup->reloc < 0) ? "Internal" :
+               bfd_get_reloc_code_name (fixup->reloc),
+               pcrel ? "Y" : "N",
+               size, fix, offset);
+      fix_new_exp (fragP, fix + offset,
+                  size, &fixup->exp, pcrel, fixup->reloc);
+
+      /* Check for ZOLs, and update symbol info if any.  */
+      if (LP_INSN (insn->insn))
+       {
+         gas_assert (fixup->exp.X_add_symbol);
+         ARC_SET_FLAG (fixup->exp.X_add_symbol, ARC_FLAG_ZOL);
+       }
     }
+}
 
-  if ((0x3 == opcode) & (s_class & SYNTAX_3OP))
-    {
-      as_bad (_("opcode 0x3 and SYNTAX_3OP invalid"));
-      ignore_rest_of_line ();
-      return;
-    }
+/* Actually output an instruction with its fixup.  */
 
-  switch (suffixcode)
-    {
-    case 0:
-      strcat (syntax, "%.q%.f ");
-      break;
-    case 1:
-      strcat (syntax, "%.f ");
-      break;
-    case 2:
-      strcat (syntax, "%.q ");
-      break;
-    case 3:
-      strcat (syntax, " ");
-      break;
-    default:
-      as_bad (_("unknown suffix class"));
-      ignore_rest_of_line ();
-      return;
-      break;
-    };
+static void
+emit_insn0 (struct arc_insn *insn, char *where, bfd_boolean relax)
+{
+  char *f = where;
+  size_t total_len;
 
-  strcat (syntax, ((opcode == 0x3) ? "%a,%b" : ((s_class & SYNTAX_3OP) ? "%a,%b,%c" : "%b,%c")));
-  if (suffixcode < 2)
-    strcat (syntax, "%F");
-  strcat (syntax, "%S%L");
+  pr_debug ("Emit insn : 0x%llx\n", insn->insn);
+  pr_debug ("\tLength  : 0x%d\n", insn->len);
+  pr_debug ("\tLong imm: 0x%lx\n", insn->limm);
 
-  ext_op = (struct arc_opcode *) xmalloc (sizeof (struct arc_opcode));
-  ext_op->syntax = xstrdup (syntax);
+  /* Write out the instruction.  */
+  total_len = insn->len + (insn->has_limm ? 4 : 0);
+  if (!relax)
+    f = frag_more (total_len);
 
-  ext_op->mask  = I (-1) | ((0x3 == opcode) ? C (-1) : 0);
-  ext_op->value = I (opcode) | ((0x3 == opcode) ? C (subopcode) : 0);
-  ext_op->flags = s_class;
-  ext_op->next_asm = arc_ext_opcodes;
-  ext_op->next_dis = arc_ext_opcodes;
-  arc_ext_opcodes = ext_op;
+  md_number_to_chars_midend(f, insn->insn, insn->len);
 
-  /* OK, now that we know what this inst is, put a description in the
-     arc extension section of the output file.  */
+  if (insn->has_limm)
+    md_number_to_chars_midend (f + insn->len, insn->limm, 4);
+  dwarf2_emit_insn (total_len);
 
-  old_sec    = now_seg;
-  old_subsec = now_subseg;
+  if (!relax)
+    apply_fixups (insn, frag_now, (f - frag_now->fr_literal));
+}
 
-  arc_set_ext_seg ();
+static void
+emit_insn1 (struct arc_insn *insn)
+{
+  /* How frag_var's args are currently configured:
+     - rs_machine_dependent, to dictate it's a relaxation frag.
+     - FRAG_MAX_GROWTH, maximum size of instruction
+     - 0, variable size that might grow...unused by generic relaxation.
+     - frag_now->fr_subtype, fr_subtype starting value, set previously.
+     - s, opand expression.
+     - 0, offset but it's unused.
+     - 0, opcode but it's unused.  */
+  symbolS *s = make_expr_symbol (&insn->fixups[0].exp);
+  frag_now->tc_frag_data.pcrel = insn->fixups[0].pcrel;
+
+  if (frag_room () < FRAG_MAX_GROWTH)
+    {
+      /* Handle differently when frag literal memory is exhausted.
+        This is used because when there's not enough memory left in
+        the current frag, a new frag is created and the information
+        we put into frag_now->tc_frag_data is disregarded.  */
 
-  p = frag_more (1);
-  *p = 5 + name_len + 1;
-  p = frag_more (1);
-  *p = EXT_INSTRUCTION;
-  p = frag_more (1);
-  *p = opcode;
-  p = frag_more (1);
-  *p = subopcode;
-  p = frag_more (1);
-  *p = (s_class & (OP1_MUST_BE_IMM | OP1_IMM_IMPLIED) ? IGNORE_FIRST_OPD : 0);
-  p = frag_more (name_len);
-  strncpy (p, syntax, name_len);
-  p = frag_more (1);
-  *p = '\0';
+      struct arc_relax_type relax_info_copy;
+      relax_substateT subtype = frag_now->fr_subtype;
 
-  subseg_set (old_sec, old_subsec);
+      memcpy (&relax_info_copy, &frag_now->tc_frag_data,
+             sizeof (struct arc_relax_type));
 
-  demand_empty_rest_of_line ();
+      frag_wane (frag_now);
+      frag_grow (FRAG_MAX_GROWTH);
+
+      memcpy (&frag_now->tc_frag_data, &relax_info_copy,
+             sizeof (struct arc_relax_type));
+
+      frag_var (rs_machine_dependent, FRAG_MAX_GROWTH, 0,
+               subtype, s, 0, 0);
+    }
+  else
+    frag_var (rs_machine_dependent, FRAG_MAX_GROWTH, 0,
+             frag_now->fr_subtype, s, 0, 0);
 }
 
 static void
-arc_common (int localScope)
+emit_insn (struct arc_insn *insn)
 {
-  char *name;
-  char c;
-  char *p;
-  int align, size;
-  symbolS *symbolP;
+  if (insn->relax)
+    emit_insn1 (insn);
+  else
+    emit_insn0 (insn, NULL, FALSE);
+}
 
-  name = input_line_pointer;
-  c = get_symbol_end ();
-  /* just after name is now '\0'  */
-  p = input_line_pointer;
-  *p = c;
-  SKIP_WHITESPACE ();
+/* Check whether a symbol involves a register.  */
 
-  if (*input_line_pointer != ',')
+static bfd_boolean
+contains_register (symbolS *sym)
+{
+  if (sym)
     {
-      as_bad (_("expected comma after symbol name"));
-      ignore_rest_of_line ();
-      return;
+      expressionS *ex = symbol_get_value_expression (sym);
+
+      return ((O_register == ex->X_op)
+             && !contains_register (ex->X_add_symbol)
+             && !contains_register (ex->X_op_symbol));
     }
 
-  input_line_pointer++;                /* skip ','  */
-  size = get_absolute_expression ();
+  return FALSE;
+}
+
+/* Returns the register number within a symbol.  */
+
+static int
+get_register (symbolS *sym)
+{
+  if (!contains_register (sym))
+    return -1;
+
+  expressionS *ex = symbol_get_value_expression (sym);
+  return regno (ex->X_add_number);
+}
+
+/* Return true if a RELOC is generic.  A generic reloc is PC-rel of a
+   simple ME relocation (e.g. RELOC_ARC_32_ME, BFD_RELOC_ARC_PC32.  */
+
+static bfd_boolean
+generic_reloc_p (extended_bfd_reloc_code_real_type reloc)
+{
+  if (!reloc)
+    return FALSE;
 
-  if (size < 0)
+  switch (reloc)
     {
-      as_bad (_("negative symbol length"));
-      ignore_rest_of_line ();
-      return;
+    case BFD_RELOC_ARC_SDA_LDST:
+    case BFD_RELOC_ARC_SDA_LDST1:
+    case BFD_RELOC_ARC_SDA_LDST2:
+    case BFD_RELOC_ARC_SDA16_LD:
+    case BFD_RELOC_ARC_SDA16_LD1:
+    case BFD_RELOC_ARC_SDA16_LD2:
+    case BFD_RELOC_ARC_SDA16_ST2:
+    case BFD_RELOC_ARC_SDA32_ME:
+      return FALSE;
+    default:
+      return TRUE;
     }
+}
 
-  *p = 0;
-  symbolP = symbol_find_or_make (name);
-  *p = c;
+/* Allocates a tok entry.  */
 
-  if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
-    {
-      as_bad (_("ignoring attempt to re-define symbol"));
-      ignore_rest_of_line ();
-      return;
-    }
-  if (((int) S_GET_VALUE (symbolP) != 0) \
-      && ((int) S_GET_VALUE (symbolP) != size))
-    {
-      as_warn (_("length of symbol \"%s\" already %ld, ignoring %d"),
-              S_GET_NAME (symbolP), (long) S_GET_VALUE (symbolP), size);
-    }
-  gas_assert (symbolP->sy_frag == &zero_address_frag);
+static int
+allocate_tok (expressionS *tok, int ntok, int cidx)
+{
+  if (ntok > MAX_INSN_ARGS - 2)
+    return 0; /* No space left.  */
 
-  /* Now parse the alignment field.  This field is optional for
-     local and global symbols. Default alignment is zero.  */
-  if (*input_line_pointer == ',')
-    {
-      input_line_pointer++;
-      align = get_absolute_expression ();
-      if (align < 0)
-       {
-         align = 0;
-         as_warn (_("assuming symbol alignment of zero"));
-       }
-    }
-  else
-    align = 0;
+  if (cidx > ntok)
+    return 0; /* Incorrect args.  */
 
-  if (localScope != 0)
-    {
-      segT old_sec;
-      int old_subsec;
-      char *pfrag;
+  memcpy (&tok[ntok+1], &tok[ntok], sizeof (*tok));
 
-      old_sec    = now_seg;
-      old_subsec = now_subseg;
-      record_alignment (bss_section, align);
-      subseg_set (bss_section, 0);  /* ??? subseg_set (bss_section, 1); ???  */
+  if (cidx == ntok)
+    return 1; /* Success.  */
+  return allocate_tok (tok, ntok - 1, cidx);
+}
 
-      if (align)
-       /* Do alignment.  */
-       frag_align (align, 0, 0);
+/* Check if an particular ARC feature is enabled.  */
 
-      /* Detach from old frag.  */
-      if (S_GET_SEGMENT (symbolP) == bss_section)
-       symbolP->sy_frag->fr_symbol = NULL;
+static bfd_boolean
+check_cpu_feature (insn_subclass_t sc)
+{
+  if (is_code_density_p (sc) && !(selected_cpu.features & CD))
+    return FALSE;
 
-      symbolP->sy_frag = frag_now;
-      pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
-                       (offsetT) size, (char *) 0);
-      *pfrag = 0;
+  if (is_spfp_p (sc) && !(selected_cpu.features & SPX))
+    return FALSE;
 
-      S_SET_SIZE       (symbolP, size);
-      S_SET_SEGMENT    (symbolP, bss_section);
-      S_CLEAR_EXTERNAL (symbolP);
-      symbol_get_obj (symbolP)->local = 1;
-      subseg_set (old_sec, old_subsec);
-    }
-  else
-    {
-      S_SET_VALUE    (symbolP, (valueT) size);
-      S_SET_ALIGN    (symbolP, align);
-      S_SET_EXTERNAL (symbolP);
-      S_SET_SEGMENT  (symbolP, bfd_com_section_ptr);
-    }
+  if (is_dpfp_p (sc) && !(selected_cpu.features & DPX))
+    return FALSE;
 
-  symbolP->bsym->flags |= BSF_OBJECT;
+  if (is_fpuda_p (sc) && !(selected_cpu.features & DPA))
+    return FALSE;
 
-  demand_empty_rest_of_line ();
+  if (is_nps400_p (sc) && !(selected_cpu.features & NPS400))
+    return FALSE;
+
+  return TRUE;
 }
-\f
-/* Select the cpu we're assembling for.  */
 
-static void
-arc_option (int ignore ATTRIBUTE_UNUSED)
+/* Parse the flags described by FIRST_PFLAG and NFLGS against the flag
+   operands in OPCODE.  Stores the matching OPCODES into the FIRST_PFLAG
+   array and returns TRUE if the flag operands all match, otherwise,
+   returns FALSE, in which case the FIRST_PFLAG array may have been
+   modified.  */
+
+static bfd_boolean
+parse_opcode_flags (const struct arc_opcode *opcode,
+                    int nflgs,
+                    struct arc_flags *first_pflag)
 {
-  extern int arc_get_mach (char *);
-  int mach;
-  char c;
-  char *cpu;
+  int lnflg, i;
+  const unsigned char *flgidx;
 
-  cpu = input_line_pointer;
-  c = get_symbol_end ();
-  mach = arc_get_mach (cpu);
-  *input_line_pointer = c;
+  lnflg = nflgs;
+  for (i = 0; i < nflgs; i++)
+    first_pflag[i].flgp = NULL;
 
-  /* If an instruction has already been seen, it's too late.  */
-  if (cpu_tables_init_p)
+  /* Check the flags.  Iterate over the valid flag classes.  */
+  for (flgidx = opcode->flags; *flgidx; ++flgidx)
     {
-      as_bad (_("\".option\" directive must appear before any instructions"));
-      ignore_rest_of_line ();
-      return;
-    }
-
-  if (mach == -1)
-    goto bad_cpu;
+      /* Get a valid flag class.  */
+      const struct arc_flag_class *cl_flags = &arc_flag_classes[*flgidx];
+      const unsigned *flgopridx;
+      int cl_matches = 0;
+      struct arc_flags *pflag = NULL;
+
+      /* Check if opcode has implicit flag classes.  */
+      if (cl_flags->flag_class & F_CLASS_IMPLICIT)
+       continue;
 
-  if (mach_type_specified_p && mach != arc_mach_type)
-    {
-      as_bad (_("\".option\" directive conflicts with initial definition"));
-      ignore_rest_of_line ();
-      return;
+      /* Check for extension conditional codes.  */
+      if (ext_condcode.arc_ext_condcode
+          && cl_flags->flag_class & F_CLASS_EXTEND)
+        {
+          struct arc_flag_operand *pf = ext_condcode.arc_ext_condcode;
+          while (pf->name)
+            {
+              pflag = first_pflag;
+              for (i = 0; i < nflgs; i++, pflag++)
+                {
+                  if (!strcmp (pf->name, pflag->name))
+                    {
+                      if (pflag->flgp != NULL)
+                        return FALSE;
+                      /* Found it.  */
+                      cl_matches++;
+                      pflag->flgp = pf;
+                      lnflg--;
+                      break;
+                    }
+                }
+              pf++;
+            }
+        }
+
+      for (flgopridx = cl_flags->flags; *flgopridx; ++flgopridx)
+        {
+          const struct arc_flag_operand *flg_operand;
+
+          pflag = first_pflag;
+          flg_operand = &arc_flag_operands[*flgopridx];
+          for (i = 0; i < nflgs; i++, pflag++)
+            {
+              /* Match against the parsed flags.  */
+              if (!strcmp (flg_operand->name, pflag->name))
+                {
+                  if (pflag->flgp != NULL)
+                    return FALSE;
+                  cl_matches++;
+                  pflag->flgp = flg_operand;
+                  lnflg--;
+                  break; /* goto next flag class and parsed flag.  */
+                }
+            }
+        }
+
+      if ((cl_flags->flag_class & F_CLASS_REQUIRED) && cl_matches == 0)
+        return FALSE;
+      if ((cl_flags->flag_class & F_CLASS_OPTIONAL) && cl_matches > 1)
+        return FALSE;
     }
-  else
-    {
-      /* The cpu may have been selected on the command line.  */
-      if (mach != arc_mach_type)
-       as_warn (_("\".option\" directive overrides command-line (default) value"));
-      arc_mach_type = mach;
-      if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach))
-       as_fatal (_("could not set architecture and machine"));
-      mach_type_specified_p = 1;
-    }
-  demand_empty_rest_of_line ();
-  return;
 
- bad_cpu:
-  as_bad (_("invalid identifier for \".option\""));
-  ignore_rest_of_line ();
-}
-\f
-char *
-md_atof (int type, char *litP, int *sizeP)
-{
-  return ieee_md_atof (type, litP, sizeP, TRUE);
+  /* Did I check all the parsed flags?  */
+  return lnflg ? FALSE : TRUE;
 }
 
-/* Write a value out to the object file, using the appropriate
-   endianness.  */
 
-void
-md_number_to_chars (char *buf, valueT val, int n)
+/* 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)
 {
-  if (target_big_endian)
-    number_to_chars_bigendian (buf, val, n);
-  else
-    number_to_chars_littleendian (buf, val, n);
-}
+  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;
 
-/* Round up a section size to the appropriate boundary.  */
+      pr_debug ("%s:%d: find_opcode_match: trying opcode 0x%08llX ",
+               frag_now->fr_file, frag_now->fr_line, opcode->opcode);
 
-valueT
-md_section_align (segT segment, valueT size)
-{
-  int align = bfd_get_section_alignment (stdoutput, segment);
+      /* Don't match opcodes that don't exist on this
+        architecture.  */
+      if (!(opcode->cpu & selected_cpu.flags))
+       goto match_failed;
 
-  return ((size + (1 << align) - 1) & (-1 << align));
-}
+      if (!check_cpu_feature (opcode->subclass))
+       goto match_failed;
 
-/* We don't have any form of relaxing.  */
+      got_cpu_match = 1;
+      pr_debug ("cpu ");
 
-int
-md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
-                              asection *seg ATTRIBUTE_UNUSED)
-{
-  as_fatal (_("relaxation not supported\n"));
-  return 1;
-}
+      /* Check the operands.  */
+      for (opidx = opcode->operands; *opidx; ++opidx)
+       {
+         const struct arc_operand *operand = &arc_operands[*opidx];
 
-/* Convert a machine dependent frag.  We never generate these.  */
+         /* Only take input from real operands.  */
+         if (ARC_OPERAND_IS_FAKE (operand))
+           continue;
 
-void
-md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
-                asection *sec ATTRIBUTE_UNUSED,
-                fragS *fragp ATTRIBUTE_UNUSED)
-{
-  abort ();
-}
+         /* When we expect input, make sure we have it.  */
+         if (tokidx >= ntok)
+           goto match_failed;
 
-static void
-arc_code_symbol (expressionS *expressionP)
-{
-  if (expressionP->X_op == O_symbol && expressionP->X_add_number == 0)
-    {
-      expressionS two;
+         /* 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;
 
-      expressionP->X_op = O_right_shift;
-      expressionP->X_add_symbol->sy_value.X_op = O_constant;
-      two.X_op = O_constant;
-      two.X_add_symbol = two.X_op_symbol = NULL;
-      two.X_add_number = 2;
-      expressionP->X_op_symbol = make_expr_symbol (&two);
-    }
-  /* Allow %st(sym1-sym2)  */
-  else if (expressionP->X_op == O_subtract
-          && expressionP->X_add_symbol != NULL
-          && expressionP->X_op_symbol != NULL
-          && expressionP->X_add_number == 0)
-    {
-      expressionS two;
+           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;
 
-      expressionP->X_add_symbol = make_expr_symbol (expressionP);
-      expressionP->X_op = O_right_shift;
-      two.X_op = O_constant;
-      two.X_add_symbol = two.X_op_symbol = NULL;
-      two.X_add_number = 2;
-      expressionP->X_op_symbol = make_expr_symbol (&two);
-    }
-  else
-    as_bad (_("expression too complex code symbol"));
-}
+             /* 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;
+               }
 
-/* Parse an operand that is machine-specific.
+             /* 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;
 
-   The ARC has a special %-op to adjust addresses so they're usable in
-   branches.  The "st" is short for the STatus register.
-   ??? Later expand this to take a flags value too.
+                         tok[tokidx].X_op = O_absent;
+                         ++ntok;
+                       }
+                     else
+                       goto match_failed;
+                   }
+               }
 
-   ??? We can't create new expression types so we map the %-op's onto the
-   existing syntax.  This means that the user could use the chosen syntax
-   to achieve the same effect.  */
+             t = &tok[tokidx];
+             break;
 
-void
-md_operand (expressionS *expressionP)
-{
-  char *p = input_line_pointer;
+           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;
 
-  if (*p != '%')
-    return;
+            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;
 
-  if (strncmp (p, "%st(", 4) == 0)
-    {
-      input_line_pointer += 4;
-      expression (expressionP);
-      if (*input_line_pointer != ')')
-       {
-         as_bad (_("missing ')' in %%-op"));
-         return;
-       }
-      ++input_line_pointer;
-      arc_code_symbol (expressionP);
-    }
-  else
-    {
-      /* It could be a register.  */
-      int i, l;
-      struct arc_ext_operand_value *ext_oper = arc_ext_operands;
-      p++;
+           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;
 
-      while (ext_oper)
-       {
-         l = strlen (ext_oper->operand.name);
-         if (!strncmp (p, ext_oper->operand.name, l) && !ISALNUM (*(p + l)))
-           {
-             input_line_pointer += l + 1;
-             expressionP->X_op = O_register;
-             expressionP->X_add_number = (offsetT) &ext_oper->operand;
-             return;
+               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 ();
            }
-         ext_oper = ext_oper->next;
+
+         ++tokidx;
        }
-      for (i = 0; i < arc_reg_names_count; i++)
+      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)
        {
-         l = strlen (arc_reg_names[i].name);
-         if (!strncmp (p, arc_reg_names[i].name, l) && !ISALNUM (*(p + l)))
-           {
-             input_line_pointer += l + 1;
-             expressionP->X_op = O_register;
-             expressionP->X_add_number = (offsetT) &arc_reg_names[i];
-             break;
-           }
+         *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;
 }
 
-/* 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.  */
+/* Swap operand tokens.  */
 
-symbolS *
-md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+static void
+swap_operand (expressionS *operand_array,
+             unsigned source,
+             unsigned destination)
 {
-  return 0;
-}
-\f
-/* Functions concerning expressions.  */
+  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);
 
-/* Parse a .byte, .word, etc. expression.
+  /* 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);
+}
 
-   Values for the status register are specified with %st(label).
-   `label' will be right shifted by 2.  */
+/* Check if *op matches *tok type.
+   Returns FALSE if they don't match, TRUE if they match.  */
 
-bfd_reloc_code_real_type
-arc_parse_cons_expression (expressionS *exp,
-                          unsigned int nbytes ATTRIBUTE_UNUSED)
+static bfd_boolean
+pseudo_operand_match (const expressionS *tok,
+                     const struct arc_operand_operation *op)
 {
-  char *p = input_line_pointer;
-  int code_symbol_fix = 0;
+  offsetT min, max, val;
+  bfd_boolean ret;
+  const struct arc_operand *operand_real = &arc_operands[op->operand_idx];
 
-  for (; ! is_end_of_line[(unsigned char) *p]; p++)
-    if (*p == '@' && !strncmp (p, "@h30", 4))
-      {
-       code_symbol_fix = 1;
-       strcpy (p, ";   ");
-      }
-  expression_and_evaluate (exp);
-  if (code_symbol_fix)
+  ret = FALSE;
+  switch (tok->X_op)
     {
-      arc_code_symbol (exp);
-      input_line_pointer = p;
+    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 BFD_RELOC_NONE;
+  return ret;
 }
 
-/* Record a fixup for a cons expression.  */
+/* Find pseudo instruction in array.  */
 
-void
-arc_cons_fix_new (fragS *frag,
-                 int where,
-                 int nbytes,
-                 expressionS *exp,
-                 bfd_reloc_code_real_type r ATTRIBUTE_UNUSED)
+static const struct arc_pseudo_insn *
+find_pseudo_insn (const char *opname,
+                 int ntok,
+                 const expressionS *tok)
 {
-  if (nbytes == 4)
-    {
-      int reloc_type;
-      expressionS exptmp;
+  const struct arc_pseudo_insn *pseudo_insn = NULL;
+  const struct arc_operand_operation *op;
+  unsigned int i;
+  int j;
 
-      /* This may be a special ARC reloc (eg: %st()).  */
-      reloc_type = get_arc_exp_reloc_type (1, BFD_RELOC_32, exp, &exptmp);
-      fix_new_exp (frag, where, nbytes, &exptmp, 0,
-                   (enum bfd_reloc_code_real) reloc_type);
-    }
-  else
+  for (i = 0; i < arc_num_pseudo_insn; ++i)
     {
-      fix_new_exp (frag, where, nbytes, exp, 0,
-                  nbytes == 2 ? BFD_RELOC_16
-                  : nbytes == 8 ? BFD_RELOC_64
-                  : BFD_RELOC_32);
+      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;
 }
-\f
-/* Functions concerning relocs.  */
 
-/* The location from which a PC relative jump should be calculated,
-   given a PC relative reloc.  */
+/* Assumes the expressionS *tok is of sufficient size.  */
 
-long
-md_pcrel_from (fixS *fixP)
+static const struct arc_opcode_hash_entry *
+find_special_case_pseudo (const char *opname,
+                         int *ntok,
+                         expressionS *tok,
+                         int *nflgs,
+                         struct arc_flags *pflags)
 {
-  /* Return the address of the delay slot.  */
-  return fixP->fx_frag->fr_address + fixP->fx_where + fixP->fx_size;
-}
+  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];
 
-/* Apply a fixup to the object code.  This is called for all the
-   fixups we generated by the call to fix_new_exp, above.  In the call
-   above we used a reloc code which was the largest legal reloc code
-   plus the operand index.  Here we undo that to recover the operand
-   index.  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.  */
+  /* Find whether opname is in pseudo instruction array.  */
+  pseudo_insn = find_pseudo_insn (opname, *ntok, tok);
 
-void
-md_apply_fix (fixS *fixP, valueT * valP, segT seg)
-{
-  valueT value = * valP;
+  if (pseudo_insn == NULL)
+    return NULL;
 
-  if (fixP->fx_addsy == (symbolS *) NULL)
-    fixP->fx_done = 1;
+  /* 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);
 
-  else if (fixP->fx_pcrel)
+  /* Handle operand operations.  */
+  for (i = 0; i < pseudo_insn->operand_cnt; ++i)
     {
-      /* Hack around bfd_install_relocation brain damage.  */
-      if (S_GET_SEGMENT (fixP->fx_addsy) != seg)
-       value += md_pcrel_from (fixP);
-    }
+      operand_pseudo = &pseudo_insn->operand[i];
+      operand_real = &arc_operands[operand_pseudo->operand_idx];
 
-  /* We can't actually support subtracting a symbol.  */
-  if (fixP->fx_subsy != NULL)
-    as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+      if (operand_real->flags & ARC_OPERAND_BRAKET
+         && !operand_pseudo->needs_insert)
+       continue;
 
-  if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
-    {
-      int opindex;
-      const struct arc_operand *operand;
-      char *where;
-      arc_insn insn;
+      /* 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;
+           }
 
-      opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
+         /* 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);
 
-      operand = &arc_operands[opindex];
+         tokenize_arguments (construct_operand, &tok[i], 1);
+         ++(*ntok);
+       }
 
-      /* Fetch the instruction, insert the fully resolved operand
-        value, and stuff the instruction back again.  */
-      where = fixP->fx_frag->fr_literal + fixP->fx_where;
-      if (target_big_endian)
-       insn = bfd_getb32 ((unsigned char *) where);
-      else
-       insn = bfd_getl32 ((unsigned char *) where);
-      insn = arc_insert_operand (insn, operand, -1, NULL, (offsetT) value,
-                                fixP->fx_file, fixP->fx_line);
-      if (target_big_endian)
-       bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
-      else
-       bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
+      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;
 
-      if (fixP->fx_done)
-       /* Nothing else to do here.  */
-       return;
+           case O_symbol:
+             break;
 
-      /* Determine a BFD reloc value based on the operand information.
-        We are only prepared to turn a few of the operands into relocs.
-        !!! Note that we can't handle limm values here.  Since we're using
-        implicit addends the addend must be inserted into the instruction,
-        however, the opcode insertion routines currently do nothing with
-        limm values.  */
-      if (operand->fmt == 'B')
-       {
-         gas_assert ((operand->flags & ARC_OPERAND_RELATIVE_BRANCH) != 0
-                 && operand->bits == 20
-                 && operand->shift == 7);
-         fixP->fx_r_type = BFD_RELOC_ARC_B22_PCREL;
-       }
-      else if (operand->fmt == 'J')
-       {
-         gas_assert ((operand->flags & ARC_OPERAND_ABSOLUTE_BRANCH) != 0
-                 && operand->bits == 24
-                 && operand->shift == 32);
-         fixP->fx_r_type = BFD_RELOC_ARC_B26;
-       }
-      else if (operand->fmt == 'L')
-       {
-         gas_assert ((operand->flags & ARC_OPERAND_LIMM) != 0
-                 && operand->bits == 32
-                 && operand->shift == 32);
-         fixP->fx_r_type = BFD_RELOC_32;
-       }
-      else
-       {
-         as_bad_where (fixP->fx_file, fixP->fx_line,
-                       _("unresolved expression that must be resolved"));
-         fixP->fx_done = 1;
-         return;
+           default:
+             /* Ignored.  */
+             break;
+           }
        }
     }
-  else
+
+  /* Swap operands if necessary.  Only supports one swap at the
+     moment.  */
+  for (i = 0; i < pseudo_insn->operand_cnt; ++i)
     {
-      switch (fixP->fx_r_type)
-       {
-       case BFD_RELOC_8:
-         md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                             value, 1);
-         break;
-       case BFD_RELOC_16:
-         md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                             value, 2);
-         break;
-       case BFD_RELOC_32:
-         md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                             value, 4);
-         break;
-       case BFD_RELOC_ARC_B26:
-         /* If !fixP->fx_done then `value' is an implicit addend.
-            We must shift it right by 2 in this case as well because the
-            linker performs the relocation and then adds this in (as opposed
-            to adding this in and then shifting right by 2).  */
-         value >>= 2;
-         md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                             value, 4);
-         break;
-       default:
-         abort ();
-       }
+      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;
     }
-}
 
-/* Translate internal representation of relocation info to BFD target
-   format.  */
+  return arc_find_opcode (pseudo_insn->mnemonic_r);
+}
 
-arelent *
-tc_gen_reloc (asection *section ATTRIBUTE_UNUSED,
-             fixS *fixP)
+static const struct arc_opcode_hash_entry *
+find_special_case_flag (const char *opname,
+                       int *nflgs,
+                       struct arc_flags *pflags)
 {
-  arelent *reloc;
+  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);
 
-  reloc = (arelent *) xmalloc (sizeof (arelent));
-  reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+      if (strncmp (opname, arc_flag_special_opcode->name, oplen) != 0)
+       continue;
 
-  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
-  reloc->address = fixP->fx_frag->fr_address + fixP->fx_where;
-  reloc->howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
-  if (reloc->howto == (reloc_howto_type *) NULL)
-    {
-      as_bad_where (fixP->fx_file, fixP->fx_line,
-                   _("internal error: can't export reloc type %d (`%s')"),
-                   fixP->fx_r_type,
-                   bfd_get_reloc_code_name (fixP->fx_r_type));
-      return NULL;
+      /* 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;
+}
 
-  gas_assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
+/* Used to find special case opcode.  */
 
-  /* Set addend to account for PC being advanced one insn before the
-     target address is computed.  */
+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;
 
-  reloc->addend = (fixP->fx_pcrel ? -4 : 0);
+  entry = find_special_case_pseudo (opname, ntok, tok, nflgs, pflags);
 
-  return reloc;
+  if (entry == NULL)
+    entry = find_special_case_flag (opname, nflgs, pflags);
+
+  return entry;
 }
 
-const pseudo_typeS md_pseudo_table[] =
+/* Autodetect cpu attribute list.  */
+
+static void
+autodetect_attributes (const struct arc_opcode *opcode,
+                        const expressionS *tok,
+                        int ntok)
 {
-  { "align", s_align_bytes, 0 }, /* Defaulting is invalid (0).  */
-  { "comm", arc_common, 0 },
-  { "common", arc_common, 0 },
-  { "lcomm", arc_common, 1 },
-  { "lcommon", arc_common, 1 },
-  { "2byte", cons, 2 },
-  { "half", cons, 2 },
-  { "short", cons, 2 },
-  { "3byte", cons, 3 },
-  { "4byte", cons, 4 },
-  { "word", cons, 4 },
-  { "option", arc_option, 0 },
-  { "cpu", arc_option, 0 },
-  { "block", s_space, 0 },
-  { "extcondcode", arc_extoper, 0 },
-  { "extcoreregister", arc_extoper, 1 },
-  { "extauxregister", arc_extoper, 2 },
-  { "extinstruction", arc_extinst, 0 },
-  { NULL, 0, 0 },
-};
+  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);
+}
 
-/* This routine is called for each instruction to be assembled.  */
+/* The public interface to the instruction assembler.  */
 
 void
 md_assemble (char *str)
 {
-  const struct arc_opcode *opcode;
-  const struct arc_opcode *std_opcode;
-  struct arc_opcode *ext_opcode;
-  char *start;
-  const char *last_errmsg = 0;
-  arc_insn insn;
-  static int init_tables_p = 0;
+  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;
+    }
 
-  /* Opcode table initialization is deferred until here because we have to
-     wait for a possible .option command.  */
-  if (!init_tables_p)
+  /* 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)
     {
-      init_opcode_tables (arc_mach_type);
-      init_tables_p = 1;
+      as_bad (_("syntax error"));
+      return;
     }
 
-  /* Skip leading white space.  */
-  while (ISSPACE (*str))
-    str++;
+  /* Finish it off.  */
+  assemble_tokens (opname, tok, ntok, flags, nflg);
+  assembling_insn = FALSE;
+}
 
-  /* The instructions are stored in lists hashed by the first letter (though
-     we needn't care how they're hashed).  Get the first in the list.  */
+/* Callback to insert a register into the hash table.  */
 
-  ext_opcode = arc_ext_opcodes;
-  std_opcode = arc_opcode_lookup_asm (str);
+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);
+}
 
-  /* Keep looking until we find a match.  */
-  start = str;
-  for (opcode = (ext_opcode ? ext_opcode : std_opcode);
-       opcode != NULL;
-       opcode = (ARC_OPCODE_NEXT_ASM (opcode)
-                ? ARC_OPCODE_NEXT_ASM (opcode)
-                : (ext_opcode ? ext_opcode = NULL, std_opcode : NULL)))
-    {
-      int past_opcode_p, fc, num_suffixes;
-      int fix_up_at = 0;
-      char *syn;
-      struct arc_fixup fixups[MAX_FIXUPS];
-      /* Used as a sanity check.  If we need a limm reloc, make sure we ask
-        for an extra 4 bytes from frag_more.  */
-      int limm_reloc_p;
-      int ext_suffix_p;
-      const struct arc_operand_value *insn_suffixes[MAX_SUFFIXES];
-
-      /* Is this opcode supported by the selected cpu?  */
-      if (! arc_opcode_supported (opcode))
-       continue;
+/* 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];
 
-      /* Scan the syntax string.  If it doesn't match, try the next one.  */
-      arc_opcode_init_insert ();
-      insn = opcode->value;
-      fc = 0;
-      past_opcode_p = 0;
-      num_suffixes = 0;
-      limm_reloc_p = 0;
-      ext_suffix_p = 0;
-
-      /* We don't check for (*str != '\0') here because we want to parse
-        any trailing fake arguments in the syntax string.  */
-      for (str = start, syn = opcode->syntax; *syn != '\0';)
+      sprintf (name, "r%d", i);
+      declare_register (name, i);
+      if ((i & 0x01) == 0)
        {
-         int mods;
-         const struct arc_operand *operand;
+         sprintf (name, "r%dr%d", i, i+1);
+         declare_register (name, i);
+       }
+    }
+}
 
-         /* Non operand chars must match exactly.  */
-         if (*syn != '%' || *++syn == '%')
-           {
-            if (*str == *syn)
-               {
-                 if (*syn == ' ')
-                   past_opcode_p = 1;
-                 ++syn;
-                 ++str;
-               }
-             else
-               break;
-             continue;
-           }
+/* Construct a symbol for an address type.  */
 
-         /* We have an operand.  Pick out any modifiers.  */
-         mods = 0;
-         while (ARC_MOD_P (arc_operands[arc_operand_map[(int) *syn]].flags))
-           {
-             mods |= arc_operands[arc_operand_map[(int) *syn]].flags & ARC_MOD_BITS;
-             ++syn;
-           }
-         operand = arc_operands + arc_operand_map[(int) *syn];
-         if (operand->fmt == 0)
-           as_fatal (_("unknown syntax format character `%c'"), *syn);
+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);
+}
 
-         if (operand->flags & ARC_OPERAND_FAKE)
-           {
-             const char *errmsg = NULL;
-             if (operand->insert)
-               {
-                 insn = (*operand->insert) (insn, operand, mods, NULL, 0, &errmsg);
-                 if (errmsg != (const char *) NULL)
-                   {
-                     last_errmsg = errmsg;
-                     if (operand->flags & ARC_OPERAND_ERROR)
-                       {
-                         as_bad ("%s", errmsg);
-                         return;
-                       }
-                     else if (operand->flags & ARC_OPERAND_WARN)
-                       as_warn ("%s", errmsg);
-                     break;
-                   }
-                 if (limm_reloc_p
-                     && (operand->flags && operand->flags & ARC_OPERAND_LIMM)
-                     && (operand->flags &
-                         (ARC_OPERAND_ABSOLUTE_BRANCH | ARC_OPERAND_ADDRESS)))
-                   {
-                     fixups[fix_up_at].opindex = arc_operand_map[operand->fmt];
-                   }
-               }
-             ++syn;
-           }
-         /* Are we finished with suffixes?  */
-         else if (!past_opcode_p)
-           {
-             int found;
-             char c;
-             char *s, *t;
-             const struct arc_operand_value *suf, *suffix_end;
-             const struct arc_operand_value *suffix = NULL;
-
-             if (!(operand->flags & ARC_OPERAND_SUFFIX))
-               abort ();
-
-             /* If we're at a space in the input string, we want to skip the
-                remaining suffixes.  There may be some fake ones though, so
-                just go on to try the next one.  */
-             if (*str == ' ')
-               {
-                 ++syn;
-                 continue;
-               }
+/* Port-specific assembler initialization.  This function is called
+   once, at assembler startup time.  */
 
-             s = str;
-             if (mods & ARC_MOD_DOT)
-               {
-                 if (*s != '.')
-                   break;
-                 ++s;
-               }
-             else
-               {
-                 /* This can happen in "b.nd foo" and we're currently looking
-                    for "%q" (ie: a condition code suffix).  */
-                 if (*s == '.')
-                   {
-                     ++syn;
-                     continue;
-                   }
-               }
+void
+md_begin (void)
+{
+  const struct arc_opcode *opcode = arc_opcodes;
 
-             /* Pick the suffix out and look it up via the hash table.  */
-             for (t = s; *t && ISALNUM (*t); ++t)
-               continue;
-             c = *t;
-             *t = '\0';
-             if ((suf = get_ext_suffix (s)))
-               ext_suffix_p = 1;
-             else
-               suf = (const struct arc_operand_value *)
-                    hash_find (arc_suffix_hash, s);
-             if (!suf)
-               {
-                 /* This can happen in "blle foo" and we're currently using
-                    the template "b%q%.n %j".  The "bl" insn occurs later in
-                    the table so "lle" isn't an illegal suffix.  */
-                 *t = c;
-                 break;
-               }
+  if (mach_selection_mode == MACH_SELECTION_NONE)
+    arc_select_cpu (TARGET_WITH_CPU, MACH_SELECTION_FROM_DEFAULT);
 
-             /* Is it the right type?  Note that the same character is used
-                several times, so we have to examine all of them.  This is
-                relatively efficient as equivalent entries are kept
-                together.  If it's not the right type, don't increment `str'
-                so we try the next one in the series.  */
-             found = 0;
-             if (ext_suffix_p && arc_operands[suf->type].fmt == *syn)
-               {
-                 /* Insert the suffix's value into the insn.  */
-                 *t = c;
-                 if (operand->insert)
-                   insn = (*operand->insert) (insn, operand,
-                                              mods, NULL, suf->value,
-                                              NULL);
-                 else
-                   insn |= suf->value << operand->shift;
-                 suffix = suf;
-                 str = t;
-                 found = 1;
-               }
-             else
-               {
-                 *t = c;
-                 suffix_end = arc_suffixes + arc_suffixes_count;
-                 for (suffix = suf;
-                      suffix < suffix_end && strcmp (suffix->name, suf->name) == 0;
-                      ++suffix)
-                   {
-                     if (arc_operands[suffix->type].fmt == *syn)
-                       {
-                         /* Insert the suffix's value into the insn.  */
-                         if (operand->insert)
-                           insn = (*operand->insert) (insn, operand,
-                                                      mods, NULL, suffix->value,
-                                                      NULL);
-                         else
-                           insn |= suffix->value << operand->shift;
-
-                         str = t;
-                         found = 1;
-                         break;
-                       }
-                   }
-               }
-             ++syn;
-             if (!found)
-               /* Wrong type.  Just go on to try next insn entry.  */
-               ;
-             else
-               {
-                 if (num_suffixes == MAX_SUFFIXES)
-                   as_bad (_("too many suffixes"));
-                 else
-                   insn_suffixes[num_suffixes++] = suffix;
-               }
-           }
-         else
-           /* This is either a register or an expression of some kind.  */
-           {
-             char *hold;
-             const struct arc_operand_value *reg = NULL;
-             long value = 0;
-             expressionS exp;
-
-             if (operand->flags & ARC_OPERAND_SUFFIX)
-               abort ();
-
-             /* Is there anything left to parse?
-                We don't check for this at the top because we want to parse
-                any trailing fake arguments in the syntax string.  */
-             if (is_end_of_line[(unsigned char) *str])
-               break;
+  /* The endianness can be chosen "at the factory".  */
+  target_big_endian = byte_order == BIG_ENDIAN;
 
-             /* Parse the operand.  */
-             hold = input_line_pointer;
-             input_line_pointer = str;
-             expression (&exp);
-             str = input_line_pointer;
-             input_line_pointer = hold;
-
-             if (exp.X_op == O_illegal)
-               as_bad (_("illegal operand"));
-             else if (exp.X_op == O_absent)
-               as_bad (_("missing operand"));
-             else if (exp.X_op == O_constant)
-               value = exp.X_add_number;
-             else if (exp.X_op == O_register)
-               reg = (struct arc_operand_value *) exp.X_add_number;
-#define IS_REG_DEST_OPERAND(o) ((o) == 'a')
-             else if (IS_REG_DEST_OPERAND (*syn))
-               as_bad (_("symbol as destination register"));
-             else
-               {
-                 if (!strncmp (str, "@h30", 4))
-                   {
-                     arc_code_symbol (&exp);
-                     str += 4;
-                   }
-                 /* We need to generate a fixup for this expression.  */
-                 if (fc >= MAX_FIXUPS)
-                   as_fatal (_("too many fixups"));
-                 fixups[fc].exp = exp;
-                 /* We don't support shimm relocs. break here to force
-                    the assembler to output a limm.  */
-#define IS_REG_SHIMM_OFFSET(o) ((o) == 'd')
-                 if (IS_REG_SHIMM_OFFSET (*syn))
-                   break;
-                 /* If this is a register constant (IE: one whose
-                    register value gets stored as 61-63) then this
-                    must be a limm.  */
-                 /* ??? This bit could use some cleaning up.
-                    Referencing the format chars like this goes
-                    against style.  */
-                 if (IS_SYMBOL_OPERAND (*syn))
-                   {
-                     const char *junk;
-                     limm_reloc_p = 1;
-                     /* Save this, we don't yet know what reloc to use.  */
-                     fix_up_at = fc;
-                     /* Tell insert_reg we need a limm.  This is
-                        needed because the value at this point is
-                        zero, a shimm.  */
-                     /* ??? We need a cleaner interface than this.  */
-                     (*arc_operands[arc_operand_map['Q']].insert)
-                       (insn, operand, mods, reg, 0L, &junk);
-                   }
-                 else
-                   fixups[fc].opindex = arc_operand_map[(int) *syn];
-                 ++fc;
-                 value = 0;
-               }
+  if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, selected_cpu.mach))
+    as_warn (_("could not set architecture and machine"));
 
-             /* Insert the register or expression into the instruction.  */
-             if (operand->insert)
-               {
-                 const char *errmsg = NULL;
-                 insn = (*operand->insert) (insn, operand, mods,
-                                            reg, (long) value, &errmsg);
-                 if (errmsg != (const char *) NULL)
-                   {
-                     last_errmsg = errmsg;
-                     if (operand->flags & ARC_OPERAND_ERROR)
-                       {
-                         as_bad ("%s", errmsg);
-                         return;
-                       }
-                     else if (operand->flags & ARC_OPERAND_WARN)
-                       as_warn ("%s", errmsg);
-                     break;
-                   }
-               }
-             else
-               insn |= (value & ((1 << operand->bits) - 1)) << operand->shift;
+  /* Set elf header flags.  */
+  bfd_set_private_flags (stdoutput, selected_cpu.eflags);
 
-             ++syn;
-           }
-       }
+  /* Set up a hash table for the instructions.  */
+  arc_opcode_hash = hash_new ();
+  if (arc_opcode_hash == NULL)
+    as_fatal (_("Virtual memory exhausted"));
 
-      /* If we're at the end of the syntax string, we're done.  */
-      /* FIXME: try to move this to a separate function.  */
-      if (*syn == '\0')
-       {
-         int i;
-         char *f;
-         long limm, limm_p;
-
-         /* For the moment we assume a valid `str' can only contain blanks
-            now.  IE: We needn't try again with a longer version of the
-            insn and it is assumed that longer versions of insns appear
-            before shorter ones (eg: lsr r2,r3,1 vs lsr r2,r3).  */
-
-         while (ISSPACE (*str))
-           ++str;
-
-         if (!is_end_of_line[(unsigned char) *str])
-           as_bad (_("junk at end of line: `%s'"), str);
-
-         /* Is there a limm value?  */
-         limm_p = arc_opcode_limm_p (&limm);
-
-         /* Perform various error and warning tests.  */
-
-         {
-           static int in_delay_slot_p = 0;
-           static int prev_insn_needs_cc_nop_p = 0;
-           /* delay slot type seen */
-           int delay_slot_type = ARC_DELAY_NONE;
-           /* conditional execution flag seen */
-           int conditional = 0;
-           /* 1 if condition codes are being set */
-           int cc_set_p = 0;
-           /* 1 if conditional branch, including `b' "branch always" */
-           int cond_branch_p = opcode->flags & ARC_OPCODE_COND_BRANCH;
-
-           for (i = 0; i < num_suffixes; ++i)
-             {
-               switch (arc_operands[insn_suffixes[i]->type].fmt)
-                 {
-                 case 'n':
-                   delay_slot_type = insn_suffixes[i]->value;
-                   break;
-                 case 'q':
-                   conditional = insn_suffixes[i]->value;
-                   break;
-                 case 'f':
-                   cc_set_p = 1;
-                   break;
-                 }
-             }
+  /* Initialize the hash table with the insns.  */
+  do
+    {
+      const char *name = opcode->name;
 
-           /* Putting an insn with a limm value in a delay slot is supposed to
-              be legal, but let's warn the user anyway.  Ditto for 8 byte
-              jumps with delay slots.  */
-           if (in_delay_slot_p && limm_p)
-             as_warn (_("8 byte instruction in delay slot"));
-           if (delay_slot_type != ARC_DELAY_NONE
-               && limm_p && arc_insn_not_jl (insn)) /* except for jl  addr */
-             as_warn (_("8 byte jump instruction with delay slot"));
-           in_delay_slot_p = (delay_slot_type != ARC_DELAY_NONE) && !limm_p;
-
-           /* Warn when a conditional branch immediately follows a set of
-              the condition codes.  Note that this needn't be done if the
-              insn that sets the condition codes uses a limm.  */
-           if (cond_branch_p && conditional != 0 /* 0 = "always" */
-               && prev_insn_needs_cc_nop_p && arc_mach_type == bfd_mach_arc_5)
-             as_warn (_("conditional branch follows set of flags"));
-           prev_insn_needs_cc_nop_p =
-             /* FIXME: ??? not required:
-                (delay_slot_type != ARC_DELAY_NONE) &&  */
-             cc_set_p && !limm_p;
-         }
-
-         /* Write out the instruction.
-            It is important to fetch enough space in one call to `frag_more'.
-            We use (f - frag_now->fr_literal) to compute where we are and we
-            don't want frag_now to change between calls.  */
-         if (limm_p)
-           {
-             f = frag_more (8);
-             md_number_to_chars (f, insn, 4);
-             md_number_to_chars (f + 4, limm, 4);
-             dwarf2_emit_insn (8);
-           }
-         else if (limm_reloc_p)
-           /* We need a limm reloc, but the tables think we don't.  */
-           abort ();
-         else
-           {
-             f = frag_more (4);
-             md_number_to_chars (f, insn, 4);
-             dwarf2_emit_insn (4);
-           }
+      arc_insert_opcode (opcode);
 
-         /* Create any fixups.  */
-         for (i = 0; i < fc; ++i)
-           {
-             int op_type, reloc_type;
-             expressionS exptmp;
-             const struct arc_operand *operand;
-
-             /* Create a fixup for this operand.
-                At this point we do not use a bfd_reloc_code_real_type for
-                operands residing in the insn, but instead just use the
-                operand index.  This lets us easily handle fixups for any
-                operand type, although that is admittedly not a very exciting
-                feature.  We pick a BFD reloc type in md_apply_fix.
-
-                Limm values (4 byte immediate "constants") must be treated
-                normally because they're not part of the actual insn word
-                and thus the insertion routines don't handle them.  */
-
-             if (arc_operands[fixups[i].opindex].flags & ARC_OPERAND_LIMM)
-               {
-                 /* Modify the fixup addend as required by the cpu.  */
-                 fixups[i].exp.X_add_number += arc_limm_fixup_adjust (insn);
-                 op_type = fixups[i].opindex;
-                 /* FIXME: can we add this data to the operand table?  */
-                 if (op_type == arc_operand_map['L']
-                     || op_type == arc_operand_map['s']
-                     || op_type == arc_operand_map['o']
-                     || op_type == arc_operand_map['O'])
-                   reloc_type = BFD_RELOC_32;
-                 else if (op_type == arc_operand_map['J'])
-                   reloc_type = BFD_RELOC_ARC_B26;
-                 else
-                   abort ();
-                 reloc_type = get_arc_exp_reloc_type (1, reloc_type,
-                                                      &fixups[i].exp,
-                                                      &exptmp);
-               }
-             else
-               {
-                 op_type = get_arc_exp_reloc_type (0, fixups[i].opindex,
-                                                   &fixups[i].exp, &exptmp);
-                 reloc_type = op_type + (int) BFD_RELOC_UNUSED;
-               }
-             operand = &arc_operands[op_type];
-             fix_new_exp (frag_now,
-                          ((f - frag_now->fr_literal)
-                           + (operand->flags & ARC_OPERAND_LIMM ? 4 : 0)), 4,
-                          &exptmp,
-                          (operand->flags & ARC_OPERAND_RELATIVE_BRANCH) != 0,
-                          (bfd_reloc_code_real_type) reloc_type);
-           }
-         return;
-       }
+      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_section_alignment (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 (NULL == last_errmsg)
-    as_bad (_("bad instruction `%s'"), start);
+  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
-    as_bad ("%s", last_errmsg);
+    {
+      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",
+                       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:  */
This page took 0.126378 seconds and 4 git commands to generate.