Remove unneeded AUX register symbols.
[deliverable/binutils-gdb.git] / gas / config / tc-arc.c
index 20e123012a899c86c08fcaf25dd587ef01b456a2..98cac6a3f423cd961b6c3e3dac2b38028fa544d5 100644 (file)
@@ -1,12 +1,13 @@
 /* tc-arc.c -- Assembler for the ARC
-   Copyright (C) 1994, 1995, 1997, 2000 Free Software Foundation, Inc.
-   Contributed by Doug Evans (dje@cygnus.com).
+   Copyright (C) 1994-2016 Free Software Foundation, Inc.
+
+   Contributor: Claudiu Zissulescu <claziss@synopsys.com>
 
    This file is part of GAS, the GNU Assembler.
 
    GAS is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
+   the Free Software Foundation; either version 3, or (at your option)
    any later version.
 
    GAS is distributed in the hope that it will be useful,
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with GAS; see the file COPYING.  If not, write to
-   the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   along with GAS; see the file COPYING.  If not, write to the Free
+   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
 
-#include <stdio.h>
-#include <ctype.h>
-#include "libiberty.h"
 #include "as.h"
 #include "subsegs.h"
+#include "struc-symbol.h"
+#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
+#include "safe-ctype.h"
+
 #include "opcode/arc.h"
-#include "../opcodes/arc-ext.h"
 #include "elf/arc.h"
 
-extern int arc_get_mach PARAMS ((char *));
-extern int arc_operand_type PARAMS ((int));
-extern int arc_insn_not_jl PARAMS ((arc_insn));
-extern int arc_limm_fixup_adjust PARAMS ((arc_insn));
-extern int arc_get_noshortcut_flag PARAMS ((void));
-extern int arc_set_ext_seg PARAMS ((void));
-extern void arc_code_symbol PARAMS((expressionS *));
-
-static arc_insn arc_insert_operand PARAMS ((arc_insn,
-                                           const struct arc_operand *, int,
-                                           const struct arc_operand_value *,
-                                           offsetT, char *, unsigned int));
-static void arc_common PARAMS ((int));
-static void arc_extinst PARAMS ((int));
-static void arc_extoper PARAMS ((int));
-static void arc_option PARAMS ((int));
-static int get_arc_exp_reloc_type PARAMS ((int, int, expressionS *,
-                                          expressionS *));
-
-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 MAXSUFFIXCLASS (sizeof (suffixclass) / sizeof (struct suffix_classes))
-
-const struct syntax_classes {
-  char *name;
-  int  len;
-  int  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 }
-};
+#define MAX_INSN_FIXUPS      2
+#define MAX_CONSTR_STR       20
+#define FRAG_MAX_GROWTH      8
 
-#define MAXSYNTAXCLASS (sizeof (syntaxclass) / sizeof (struct syntax_classes))
+#ifdef DEBUG
+# define pr_debug(fmt, args...) fprintf (stderr, fmt, ##args)
+#else
+# define pr_debug(fmt, args...)
+#endif
 
-const pseudo_typeS md_pseudo_table[] =
+#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))
+
+/* Equal to MAX_PRECISION in atof-ieee.c.  */
+#define MAX_LITTLENUMS 6
+
+/* Enum used to enumerate the relaxable ins operands.  */
+enum rlx_operand_type
 {
-  { "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 },
-  { "block", s_space, 0 },
-  { "extcondcode", arc_extoper, 0 },
-  { "extcoreregister", arc_extoper, 1 },
-  { "extauxregister", arc_extoper, 2 },
-  { "extinstruction", arc_extinst, 0 },
-  { NULL, 0, 0 },
+  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
+};
+
+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)->class == BRANCH || (op)->class == JUMP))
+#define is_kernel_insn_p(op)    (((op)->class == KERNEL))
+
+/* 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 */
-/* As in 0f12.456 */
-/* or    0d1.2345e12 */
+/* Chars that mean this number is a floating point constant
+   As in 0f12.456 or 0d1.2345e12.  */
 const char FLT_CHARS[] = "rRsSfFdD";
 
 /* Byte order.  */
@@ -124,1949 +126,3754 @@ extern int target_big_endian;
 const char *arc_target_format = DEFAULT_TARGET_FORMAT;
 static int byte_order = DEFAULT_BYTE_ORDER;
 
-static segT arcext_section;
+/* By default relaxation is disabled.  */
+static int relaxation_state = 0;
 
-/* One of bfd_mach_arc_n.  */
-static int arc_mach_type = bfd_mach_arc_5;
+extern int arc_get_mach (char *);
 
-/* Non-zero if the cpu type has been explicitly specified.  */
-static int mach_type_specified_p = 0;
+/* Forward declarations.  */
+static void arc_lcomm (int);
+static void arc_option (int);
+static void arc_extra_reloc (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 opcode tables have been initialized.
-   A .cpu 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_ARC600,
+  OPTION_ARC601,
+  OPTION_ARC700,
+  OPTION_ARCEM,
+  OPTION_ARCHS,
+
+  OPTION_MCPU,
+  OPTION_CD,
+  OPTION_RELAX,
+
+  /* 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_SPFP,
+  OPTION_DPFP,
+  OPTION_XMAC_D16,
+  OPTION_XMAC_24,
+  OPTION_DSP_PACKA,
+  OPTION_CRC,
+  OPTION_DVBF,
+  OPTION_TELEPHONY,
+  OPTION_XYMEMORY,
+  OPTION_LOCK,
+  OPTION_SWAPE,
+  OPTION_RTSC,
+  OPTION_FPUDA
+};
+
 struct option md_longopts[] =
 {
-#define OPTION_EB (OPTION_MD_BASE + 0)
-  {"EB", no_argument, NULL, OPTION_EB},
-#define OPTION_EL (OPTION_MD_BASE + 1)
-  {"EL", no_argument, NULL, OPTION_EL},
-#define OPTION_ARC5 (OPTION_MD_BASE + 2)
-  {"marc5", no_argument, NULL, OPTION_ARC5},
-#define OPTION_ARC6 (OPTION_MD_BASE + 3)
-  {"marc6", no_argument, NULL, OPTION_ARC6},
-#define OPTION_ARC7 (OPTION_MD_BASE + 4)
-  {"marc7", no_argument, NULL, OPTION_ARC7},
-#define OPTION_ARC8 (OPTION_MD_BASE + 5)
-  {"marc8", no_argument, NULL, OPTION_ARC8},
-#define OPTION_ARC (OPTION_MD_BASE + 6)
-  {"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 },
+
+  /* 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},
+  { "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},
+  { "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},
+  { "mfpuda", no_argument, NULL, OPTION_FPUDA},
+
+  { 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.  */
+
+/* Used since new relocation types are introduced in this
+   file (DUMMY_RELOC_LITUSE_*).  */
+typedef int extended_bfd_reloc_code_real_type;
 
-struct arc_operand_value *get_ext_suffix(char *s);
+struct arc_fixup
+{
+  expressionS exp;
 
-/*
- * md_parse_option
- *
- * Invocation line includes a switch not recognized by the base assembler.
- * See if it's a processor-specific option.
- */
+  extended_bfd_reloc_code_real_type reloc;
 
-int
-md_parse_option (c, arg)
-     int c;
-     char *arg ATTRIBUTE_UNUSED;
+  /* 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_ARC:
-    case OPTION_ARC5:
-      arc_mach_type = bfd_mach_arc_5;
-      break;
-    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 int insn;
+  int nfixups;
+  struct arc_fixup fixups[MAX_INSN_FIXUPS];
+  long limm;
+  bfd_boolean short_insn; /* Boolean value: TRUE if current insn is
+                            short.  */
+  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];
+
+/* 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 cpu for which we are generating code.  */
+static unsigned arc_target;
+static const char *arc_target_name;
+static unsigned arc_features;
+
+/* The default architecture.  */
+static int arc_mach_type;
+
+/* Non-zero if the cpu type has been explicitly specified.  */
+static int mach_type_specified_p = 0;
+
+/* 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;
+
+/* 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[] =
+{
+  { "arc600", ARC_OPCODE_ARC600,  bfd_mach_arc_arc600,
+    E_ARC_MACH_ARC600,  0x00},
+  { "arc700", ARC_OPCODE_ARC700,  bfd_mach_arc_arc700,
+    E_ARC_MACH_ARC700,  0x00},
+  { "nps400", ARC_OPCODE_ARC700 | ARC_OPCODE_NPS400, bfd_mach_arc_nps400,
+    E_ARC_MACH_NPS400,  0x00},
+  { "arcem",  ARC_OPCODE_ARCv2EM, bfd_mach_arc_arcv2,
+    EF_ARC_CPU_ARCV2EM, ARC_CD},
+  { "archs",  ARC_OPCODE_ARCv2HS, bfd_mach_arc_arcv2,
+    EF_ARC_CPU_ARCV2HS, ARC_CD},
+  { 0, 0, 0, 0, 0 }
+};
 
-void
-md_show_usage (stream)
-     FILE *stream;
+/* 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
+
+/* 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,      0),
+};
 
-/* 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 defered until later because we might see a .cpu
-   command.  */
+static const int arc_num_reloc_op
+= sizeof (arc_reloc_op) / sizeof (*arc_reloc_op);
 
-void
-md_begin ()
+/* 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 (mach)
-     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),
+};
+
+/* 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 ((arc_suffix_hash = hash_new ()) == NULL)
-    as_fatal ("virtual memory exhausted");
+const unsigned arc_num_relaxable_ins = ARRAY_SIZE (arc_relaxable_insns);
 
-  if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach))
-    as_warn ("could not set architecture and machine");
+/* Flags to set in the elf header.  */
+static flagword arc_eflag = 0x00;
 
-  /* 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, (PTR) (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.
+/* Functions implementation.  */
 
-     If the register name is already in the table, then the existing
-     definition is assumed to be from an .ExtCoreRegister pseudo-op.  */
+/* 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.  */
 
-  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,
-                           (int) &arc_reg_names[i], &zero_address_frag));
-    }
+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;
 }
-\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 (insn, operand, mods, reg, val, file, line)
-     arc_insn insn;
-     const struct arc_operand *operand;
-     int mods;
-     const struct arc_operand_value *reg;
-     offsetT val;
-     char *file;
-     unsigned int line;
+/* Initialise the iterator ITER.  */
+
+static void
+arc_opcode_hash_entry_iterator_init (struct arc_opcode_hash_entry_iterator *iter)
+{
+  iter->index = 0;
+  iter->opcode = NULL;
+}
+
+/* Return the next ARC_OPCODE from ENTRY, using ITER to hold state between
+   calls to this function.  Return NULL when all ARC_OPCODE entries have
+   been returned.  */
+
+static const struct arc_opcode *
+arc_opcode_hash_entry_iterator_next (const struct arc_opcode_hash_entry *entry,
+                                    struct arc_opcode_hash_entry_iterator *iter)
 {
-  if (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 - arc_opcodes >= (int) arc_num_opcodes)
+         || (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)
-       {
-         const char *err =
-           "operand out of range (%s not between %ld and %ld)";
-         char buf[100];
+/* Like md_number_to_chars but used for limms.  The 4-byte limm value,
+   is encoded as 'middle-endian' for a little-endian target.  FIXME!
+   this function is used for regular 4 byte instructions as well.  */
 
-         sprint_value (buf, test);
-         if (file == (char *) NULL)
-           as_warn (err, buf, min, max);
-         else
-           as_warn_where (file, line, err, buf, min, max);
-       }
+static void
+md_number_to_chars_midend (char *buf, valueT val, int n)
+{
+  if (n == 4)
+    {
+      md_number_to_chars (buf,     (val & 0xffff0000) >> 16, 2);
+      md_number_to_chars (buf + 2, (val & 0xffff), 2);
+    }
+  else
+    {
+      md_number_to_chars (buf, val, n);
     }
+}
 
-  if (operand->insert)
+/* Select an appropriate entry from CPU_TYPES based on ARG and initialise
+   the relevant static global variables.  */
+
+static void
+arc_select_cpu (const char *arg)
+{
+  int cpu_flags = 0;
+  int i;
+
+  for (i = 0; cpu_types[i].name; ++i)
+    {
+      if (!strcasecmp (cpu_types[i].name, arg))
+        {
+          arc_target = cpu_types[i].flags;
+          arc_target_name = cpu_types[i].name;
+          arc_features = cpu_types[i].features;
+          arc_mach_type = cpu_types[i].mach;
+          cpu_flags = cpu_types[i].eflags;
+          break;
+        }
+    }
+
+  if (!cpu_types[i].name)
+    as_fatal (_("unknown architecture: %s\n"), arg);
+  gas_assert (cpu_flags != 0);
+  arc_eflag = (arc_eflag & ~EF_ARC_MACH_MSK) | cpu_flags;
+}
+
+/* Here ends all the ARCompact extension instruction assembling
+   stuff.  */
+
+static void
+arc_extra_reloc (int r_type)
+{
+  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)
+    {
+      ++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 == ',')
     {
-      const char *errmsg;
+      align = parse_align (1);
 
-      errmsg = NULL;
-      insn = (*operand->insert) (insn, operand, mods, reg, (long) val, &errmsg);
-      if (errmsg != (const char *) NULL)
-       as_warn (errmsg);
+      if (align == (addressT) -1)
+       return NULL;
     }
   else
-    insn |= (((long) val & ((1 << operand->bits) - 1))
-            << operand->shift);
+    {
+      if (size >= 8)
+       align = 3;
+      else if (size >= 4)
+       align = 2;
+      else if (size >= 2)
+       align = 1;
+      else
+       align = 0;
+    }
 
-  return insn;
+  bss_alloc (symbolP, size, align);
+  S_CLEAR_EXTERNAL (symbolP);
+
+  return symbolP;
+}
+
+static void
+arc_lcomm (int ignore)
+{
+  symbolS *symbolP = s_comm_internal (ignore, arc_lcomm_internal);
+
+  if (symbolP)
+    symbol_get_bfdsym (symbolP)->flags |= BSF_OBJECT;
 }
 
-/* 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 ``.''.  */
+/* Select the cpu we're assembling for.  */
 
-struct arc_fixup
+static void
+arc_option (int ignore ATTRIBUTE_UNUSED)
 {
-  /* index into `arc_operands' */
-  int opindex;
-  expressionS exp;
-};
+  int mach = -1;
+  char c;
+  char *cpu;
 
-#define MAX_FIXUPS 5
+  c = get_symbol_name (&cpu);
+  mach = arc_get_mach (cpu);
 
-#define MAX_SUFFIXES 5
+  if (mach == -1)
+    goto bad_cpu;
 
-/* This routine is called for each instruction to be assembled.  */
+  if (!mach_type_specified_p)
+    {
+      if ((!strcmp ("ARC600", cpu))
+         || (!strcmp ("ARC601", cpu))
+         || (!strcmp ("A6", cpu)))
+       {
+         md_parse_option (OPTION_MCPU, "arc600");
+       }
+      else if ((!strcmp ("ARC700", cpu))
+              || (!strcmp ("A7", cpu)))
+       {
+         md_parse_option (OPTION_MCPU, "arc700");
+       }
+      else if (!strcmp ("EM", cpu))
+       {
+         md_parse_option (OPTION_MCPU, "arcem");
+       }
+      else if (!strcmp ("HS", cpu))
+       {
+         md_parse_option (OPTION_MCPU, "archs");
+       }
+      else
+       as_fatal (_("could not find the architecture"));
 
-void
-md_assemble (str)
-     char *str;
+      if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach))
+       as_fatal (_("could not set architecture and machine"));
+    }
+  else
+    if (arc_mach_type != mach)
+      as_warn (_("Command-line value overrides \".cpu\" directive"));
+
+  restore_line_pointer (c);
+  demand_empty_rest_of_line ();
+  return;
+
+ bad_cpu:
+  restore_line_pointer (c);
+  as_bad (_("invalid identifier for \".cpu\""));
+  ignore_rest_of_line ();
+}
+
+/* Smartly print an expression.  */
+
+static void
+debug_exp (expressionS *t)
 {
-  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;
+  const char *name ATTRIBUTE_UNUSED;
+  const char *namemd ATTRIBUTE_UNUSED;
 
-  /* Opcode table initialization is deferred until here because we have to
-     wait for a possible .cpu command.  */
-  if (!init_tables_p)
+  pr_debug ("debug_exp: ");
+
+  switch (t->X_op)
     {
-      init_opcode_tables (arc_mach_type);
-      init_tables_p = 1;
+    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;
     }
 
-  /* Skip leading white space.  */
-  while (isspace (*str))
-    str++;
-
-  /* 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.  */
+  switch (t->X_md)
+    {
+    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;
+    }
 
-  ext_opcode = arc_ext_opcodes;
-  std_opcode = arc_opcode_lookup_asm (str);
+  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);
+}
 
-  /* Keep looking until we find a match.  */
+/* Parse the arguments to an opcode.  */
 
-  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;
+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;
+  int i;
+  size_t len;
+  const struct arc_reloc_op_tag *r;
+  expressionS tmpE;
+  char *reloc_name, c;
 
-      /* Scan the syntax string.  If it doesn't match, try the next one.  */
+  memset (tok, 0, sizeof (*tok) * ntok);
 
-      arc_opcode_init_insert ();
-      insn = opcode->value;
-      fc = 0;
-      past_opcode_p = 0;
-      num_suffixes = 0;
-      limm_reloc_p = 0;
-      ext_suffix_p = 0;
+  /* Save and restore input_line_pointer around this function.  */
+  old_input_line_pointer = input_line_pointer;
+  input_line_pointer = str;
 
-      /* 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'; )
+  while (*input_line_pointer)
+    {
+      SKIP_WHITESPACE ();
+      switch (*input_line_pointer)
        {
-         int mods;
-         const struct arc_operand *operand;
+       case '\0':
+         goto fini;
+
+       case ',':
+         input_line_pointer++;
+         if (saw_comma || !saw_arg)
+           goto err;
+         saw_comma = TRUE;
+         break;
+
+       case '}':
+       case ']':
+         ++input_line_pointer;
+         --brk_lvl;
+         if (!saw_arg)
+           goto err;
+         tok->X_op = O_bracket;
+         ++tok;
+         ++num_args;
+         break;
 
-         /* Non operand chars must match exactly.  */
-         if (*syn != '%' || *++syn == '%')
+       case '{':
+       case '[':
+         input_line_pointer++;
+         if (brk_lvl)
+           goto err;
+         ++brk_lvl;
+         tok->X_op = O_bracket;
+         ++tok;
+         ++num_args;
+         break;
+
+       case '@':
+         /* We have labels, function names and relocations, all
+            starting with @ symbol.  Sort them out.  */
+         if (saw_arg && !saw_comma)
+           goto err;
+
+         /* Parse @label.  */
+         tok->X_op = O_symbol;
+         tok->X_md = O_absent;
+         expression (tok);
+         if (*input_line_pointer != '@')
+           goto normalsymbol; /* This is not a relocation.  */
+
+       relocationsym:
+
+         /* A relocation opernad has the following form
+            @identifier@relocation_type.  The identifier is already
+            in tok!  */
+         if (tok->X_op != O_symbol)
            {
-             /* Handle '+' specially as we want to allow "ld r0,[sp-4]".  */
-             /* ??? The syntax has changed to [sp,-4].  */
-             if (0 && *syn == '+' && *str == '-')
-               {
-                 /* Skip over syn's +, but leave str's - alone.
-                    That makes the case identical to "ld r0,[sp+-4]".  */
-                 ++syn;
-               }
-             else if (*str == *syn)
-               {
-                 if (*syn == ' ')
-                   past_opcode_p = 1;
-                 ++syn;
-                 ++str;
-               }
-             else
-               break;
-             continue;
+             as_bad (_("No valid label relocation operand"));
+             goto err;
            }
 
-         /* We have an operand.  Pick out any modifiers.  */
-         mods = 0;
-         while (ARC_MOD_P (arc_operands[arc_operand_map[(int) *syn]].flags))
+         /* Parse @relocation_type.  */
+         input_line_pointer++;
+         c = get_symbol_name (&reloc_name);
+         len = input_line_pointer - reloc_name;
+         if (len == 0)
            {
-             mods |= arc_operands[arc_operand_map[(int) *syn]].flags & ARC_MOD_BITS;
-             ++syn;
+             as_bad (_("No relocation operand"));
+             goto err;
            }
-         operand = arc_operands + arc_operand_map[(int) *syn];
-         if (operand->fmt == 0)
-           as_fatal ("unknown syntax format character `%c'", *syn);
 
-         if (operand->flags & ARC_OPERAND_FAKE)
+         /* 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)
            {
-             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 (errmsg);
-                         return;
-                       }
-                     else if (operand->flags & ARC_OPERAND_WARN)
-                       as_warn (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;
+             as_bad (_("Unknown relocation operand: @%s"), reloc_name);
+             goto err;
            }
-         /* Are we finished with suffixes?  */
-         else if (!past_opcode_p)
+
+         *input_line_pointer = c;
+         SKIP_WHITESPACE_AFTER_NAME ();
+         /* Extra check for TLS: base.  */
+         if (*input_line_pointer == '@')
            {
-             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 == ' ')
+             symbolS *base;
+             if (tok->X_op_symbol != NULL
+                 || tok->X_op != O_symbol)
                {
-                 ++syn;
-                 continue;
+                 as_bad (_("Unable to parse TLS base: %s"),
+                         input_line_pointer);
+                 goto err;
                }
-
-             s = str;
-             if (mods & ARC_MOD_DOT)
+             input_line_pointer++;
+             char *sym_name;
+             c = get_symbol_name (&sym_name);
+             base = symbol_find_or_make (sym_name);
+             tok->X_op = O_subtract;
+             tok->X_op_symbol = base;
+             restore_line_pointer (c);
+             tmpE.X_add_number = 0;
+           }
+         else if ((*input_line_pointer != '+')
+                  && (*input_line_pointer != '-'))
+           {
+             tmpE.X_add_number = 0;
+           }
+         else
+           {
+             /* Parse the constant of a complex relocation expression
+                like @identifier@reloc +/- const.  */
+             if (! r->complex_expr)
                {
-                 if (*s != '.')
-                   break;
-                 ++s;
+                 as_bad (_("@%s is not a complex relocation."), r->name);
+                 goto err;
                }
-             else
+             expression (&tmpE);
+             if (tmpE.X_op != O_constant)
                {
-                 /* This can happen in "b.nd foo" and we're currently looking
-                    for "%q" (ie: a condition code suffix).  */
-                 if (*s == '.')
-                   {
-                     ++syn;
-                     continue;
-                   }
+                 as_bad (_("Bad expression: @%s + %s."),
+                         r->name, input_line_pointer);
+                 goto err;
                }
+           }
 
-             /* 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 = 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;
-               }
+         tok->X_md = r->op;
+         tok->X_add_number = tmpE.X_add_number;
 
-             /* 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;
-
-                 str = t;
-                 found = 1;
+         debug_exp (tok);
+
+         saw_comma = FALSE;
+         saw_arg = TRUE;
+         tok++;
+         num_args++;
+         break;
+
+       case '%':
+         /* Can be a register.  */
+         ++input_line_pointer;
+         /* Fall through.  */
+       default:
+
+         if (saw_arg && !saw_comma)
+           goto err;
+
+         tok->X_op = O_absent;
+         tok->X_md = O_absent;
+         expression (tok);
+
+         /* 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 == '@')
+           goto relocationsym;
+
+       normalsymbol:
+         debug_exp (tok);
+
+         if (tok->X_op == O_illegal || tok->X_op == O_absent)
+           goto err;
+
+         saw_comma = FALSE;
+         saw_arg = TRUE;
+         tok++;
+         num_args++;
+         break;
+       }
+    }
+
+ fini:
+  if (saw_comma || brk_lvl)
+    goto err;
+  input_line_pointer = old_input_line_pointer;
+
+  return num_args;
+
+ 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;
+}
+
+/* Parse the flags to a structure.  */
+
+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;
+
+  while (*input_line_pointer)
+    {
+      switch (*input_line_pointer)
+       {
+       case ' ':
+       case '\0':
+         goto fini;
+
+       case '.':
+         input_line_pointer++;
+         if (saw_dot)
+           goto err;
+         saw_dot = TRUE;
+         saw_flg = FALSE;
+         break;
+
+       default:
+         if (saw_flg && !saw_dot)
+           goto err;
+
+         if (num_flags >= nflg)
+           goto err;
+
+         flgnamelen = strspn (input_line_pointer,
+                              "abcdefghijklmnopqrstuvwxyz0123456789");
+         if (flgnamelen > MAX_FLAG_NAME_LENGTH)
+           goto err;
+
+         memcpy (flags->name, input_line_pointer, flgnamelen);
+
+         input_line_pointer += flgnamelen;
+         flags++;
+         saw_dot = FALSE;
+         saw_flg = TRUE;
+         num_flags++;
+         break;
+       }
+    }
+
+ fini:
+  input_line_pointer = old_input_line_pointer;
+  return num_flags;
+
+ 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;
+}
+
+/* Apply the fixups in order.  */
+
+static void
+apply_fixups (struct arc_insn *insn, fragS *fragP, int fix)
+{
+  int i;
+
+  for (i = 0; i < insn->nfixups; i++)
+    {
+      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->short_insn && !fixup->islong) ? 2 : 4;
+
+      if (fixup->islong)
+       offset = (insn->short_insn) ? 2 : 4;
+
+      /* Some fixups are only used internally, thus no howto.  */
+      if ((int) fixup->reloc == 0)
+       as_fatal (_("Unhandled reloc type"));
+
+      if ((int) fixup->reloc < 0)
+       {
+         /* FIXME! the reloc size is wrong in the BFD file.
+            When it is fixed please enable me.
+            size = (insn->short_insn && !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;
+       }
+
+      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);
+       }
+    }
+}
+
+/* Actually output an instruction with its fixup.  */
+
+static void
+emit_insn0 (struct arc_insn *insn, char *where, bfd_boolean relax)
+{
+  char *f = where;
+
+  pr_debug ("Emit insn : 0x%x\n", insn->insn);
+  pr_debug ("\tShort   : 0x%d\n", insn->short_insn);
+  pr_debug ("\tLong imm: 0x%lx\n", insn->limm);
+
+  /* Write out the instruction.  */
+  if (insn->short_insn)
+    {
+      if (insn->has_limm)
+       {
+         if (!relax)
+           f = frag_more (6);
+         md_number_to_chars (f, insn->insn, 2);
+         md_number_to_chars_midend (f + 2, insn->limm, 4);
+         dwarf2_emit_insn (6);
+       }
+      else
+       {
+         if (!relax)
+           f = frag_more (2);
+         md_number_to_chars (f, insn->insn, 2);
+         dwarf2_emit_insn (2);
+       }
+    }
+  else
+    {
+      if (insn->has_limm)
+       {
+         if (!relax)
+           f = frag_more (8);
+         md_number_to_chars_midend (f, insn->insn, 4);
+         md_number_to_chars_midend (f + 4, insn->limm, 4);
+         dwarf2_emit_insn (8);
+       }
+      else
+       {
+         if (!relax)
+           f = frag_more (4);
+         md_number_to_chars_midend (f, insn->insn, 4);
+         dwarf2_emit_insn (4);
+       }
+    }
+
+  if (!relax)
+    apply_fixups (insn, frag_now, (f - frag_now->fr_literal));
+}
+
+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.  */
+
+      struct arc_relax_type relax_info_copy;
+      relax_substateT subtype = frag_now->fr_subtype;
+
+      memcpy (&relax_info_copy, &frag_now->tc_frag_data,
+             sizeof (struct arc_relax_type));
+
+      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
+emit_insn (struct arc_insn *insn)
+{
+  if (insn->relax)
+    emit_insn1 (insn);
+  else
+    emit_insn0 (insn, NULL, FALSE);
+}
+
+/* Check whether a symbol involves a register.  */
+
+static bfd_boolean
+contains_register (symbolS *sym)
+{
+  if (sym)
+    {
+      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));
+    }
+
+  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;
+
+  switch (reloc)
+    {
+    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;
+    }
+}
+
+/* Allocates a tok entry.  */
+
+static int
+allocate_tok (expressionS *tok, int ntok, int cidx)
+{
+  if (ntok > MAX_INSN_ARGS - 2)
+    return 0; /* No space left.  */
+
+  if (cidx > ntok)
+    return 0; /* Incorect args.  */
+
+  memcpy (&tok[ntok+1], &tok[ntok], sizeof (*tok));
+
+  if (cidx == ntok)
+    return 1; /* Success.  */
+  return allocate_tok (tok, ntok - 1, cidx);
+}
+
+/* Check if an particular ARC feature is enabled.  */
+
+static bfd_boolean
+check_cpu_feature (insn_subclass_t sc)
+{
+  if (!(arc_features & ARC_CD)
+      && is_code_density_p (sc))
+    return FALSE;
+
+  if (!(arc_features & ARC_SPFP)
+      && is_spfp_p (sc))
+    return FALSE;
+
+  if (!(arc_features & ARC_DPFP)
+      && is_dpfp_p (sc))
+    return FALSE;
+
+  if (!(arc_features & ARC_FPUDA)
+      && is_fpuda_p (sc))
+    return FALSE;
+
+  return TRUE;
+}
+
+/* Search forward through all variants of an opcode looking for a
+   syntax match.  */
+
+static const struct arc_opcode *
+find_opcode_match (const struct arc_opcode_hash_entry *entry,
+                  expressionS *tok,
+                  int *pntok,
+                  struct arc_flags *first_pflag,
+                  int nflgs,
+                  int *pcpumatch)
+{
+  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;
+      const unsigned char *flgidx;
+      int tokidx = 0, lnflg, i;
+      const expressionS *t = &emptyE;
+
+      pr_debug ("%s:%d: find_opcode_match: trying opcode 0x%08X ",
+               frag_now->fr_file, frag_now->fr_line, opcode->opcode);
+
+      /* Don't match opcodes that don't exist on this
+        architecture.  */
+      if (!(opcode->cpu & arc_target))
+       goto match_failed;
+
+      if (!check_cpu_feature (opcode->subclass))
+       goto match_failed;
+
+      got_cpu_match = 1;
+      pr_debug ("cpu ");
+
+      /* Check the operands.  */
+      for (opidx = opcode->operands; *opidx; ++opidx)
+       {
+         const struct arc_operand *operand = &arc_operands[*opidx];
+
+         /* Only take input from real operands.  */
+         if ((operand->flags & ARC_OPERAND_FAKE)
+             && !(operand->flags & ARC_OPERAND_BRAKET))
+           continue;
+
+         /* When we expect input, make sure we have it.  */
+         if (tokidx >= ntok)
+           goto match_failed;
+
+         /* Match operand type with expression type.  */
+         switch (operand->flags & ARC_OPERAND_TYPECHECK_MASK)
+           {
+           case ARC_OPERAND_IR:
+             /* Check to be a register.  */
+             if ((tok[tokidx].X_op != O_register
+                  || !is_ir_num (tok[tokidx].X_add_number))
+                 && !(operand->flags & ARC_OPERAND_IGNORE))
+               goto match_failed;
+
+             /* If expect duplicate, make sure it is duplicate.  */
+             if (operand->flags & ARC_OPERAND_DUPLICATE)
+               {
+                 /* Check for duplicate.  */
+                 if (t->X_op != O_register
+                     || !is_ir_num (t->X_add_number)
+                     || (regno (t->X_add_number) !=
+                         regno (tok[tokidx].X_add_number)))
+                   goto match_failed;
                }
-             else
+
+             /* Special handling?  */
+             if (operand->insert)
                {
-                 *t = c;
-                 suffix_end = arc_suffixes + arc_suffixes_count;
-                 for (suffix = suf;
-                      suffix < suffix_end && strcmp (suffix->name, suf->name) == 0;
-                      ++suffix)
+                 const char *errmsg = NULL;
+                 (*operand->insert)(0,
+                                    regno (tok[tokidx].X_add_number),
+                                    &errmsg);
+                 if (errmsg)
                    {
-                     if (arc_operands[suffix->type].fmt == *syn)
+                     if (operand->flags & ARC_OPERAND_IGNORE)
                        {
-                         /* 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;
+                         /* Missing argument, create one.  */
+                         if (!allocate_tok (tok, ntok - 1, tokidx))
+                           goto match_failed;
+
+                         tok[tokidx].X_op = O_absent;
+                         ++ntok;
                        }
+                     else
+                       goto match_failed;
                    }
                }
-             ++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 (*str == '\0')
-               break;
 
-             /* 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
+             t = &tok[tokidx];
+             break;
+
+           case ARC_OPERAND_BRAKET:
+             /* Check if bracket is also in opcode table as
+                operand.  */
+             if (tok[tokidx].X_op != O_bracket)
+               goto match_failed;
+             break;
+
+           case ARC_OPERAND_LIMM:
+           case ARC_OPERAND_SIGNED:
+           case ARC_OPERAND_UNSIGNED:
+             switch (tok[tokidx].X_op)
                {
-                 if(!strncmp(str,"@h30",4))
+               case O_illegal:
+               case O_absent:
+               case O_register:
+                 goto match_failed;
+
+               case O_bracket:
+                 /* Got an (too) early bracket, check if it is an
+                    ignored operand.  N.B. This procedure works only
+                    when bracket is the last operand!  */
+                 if (!(operand->flags & ARC_OPERAND_IGNORE))
+                   goto match_failed;
+                 /* Insert the missing operand.  */
+                 if (!allocate_tok (tok, ntok - 1, tokidx))
+                   goto match_failed;
+
+                 tok[tokidx].X_op = O_absent;
+                 ++ntok;
+                 break;
+
+               case O_symbol:
+                 {
+                   const char *p;
+                   size_t len;
+                   const struct arc_aux_reg *auxr;
+                   unsigned j;
+
+                   if (opcode->class != AUXREG)
+                     goto de_fault;
+                   p = S_GET_NAME (tok[tokidx].X_add_symbol);
+                   len = strlen (p);
+
+                   auxr = &arc_aux_regs[0];
+                   for (j = 0; j < arc_num_aux_regs; j++, auxr++)
+                     if (len == auxr->length
+                         && strcasecmp (auxr->name, p) == 0
+                          && ((auxr->subclass == NONE)
+                              || check_cpu_feature (auxr->subclass)))
+                       {
+                         /* We modify the token array here, safe in the
+                            knowledge, that if this was the wrong choice
+                            then the original contents will be restored
+                            from BKTOK.  */
+                         tok[tokidx].X_op = O_constant;
+                         tok[tokidx].X_add_number = auxr->address;
+                         ARC_SET_FLAG (tok[i].X_add_symbol, ARC_FLAG_AUX);
+                         break;
+                       }
+
+                   if (tok[tokidx].X_op != O_constant)
+                     goto de_fault;
+                 }
+                 /* Fall-through */
+               case O_constant:
+                 /* Check the range.  */
+                 if (operand->bits != 32
+                     && !(operand->flags & ARC_OPERAND_NCHK))
                    {
-                     arc_code_symbol(&exp);
-                     str += 4;
+                     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 alignmets.  */
+                     if ((operand->flags & ARC_OPERAND_ALIGNED32)
+                         && (val & 0x03))
+                       goto match_failed;
+
+                     if ((operand->flags & ARC_OPERAND_ALIGNED16)
+                         && (val & 0x01))
+                       goto match_failed;
                    }
-                 /* 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))
+                 else if (operand->flags & ARC_OPERAND_NCHK)
                    {
-                     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);
+                     if (operand->insert)
+                       {
+                         const char *errmsg = NULL;
+                         (*operand->insert)(0,
+                                            tok[tokidx].X_add_number,
+                                            &errmsg);
+                         if (errmsg)
+                           goto match_failed;
+                       }
+                     else
+                       goto match_failed;
                    }
-                 else
-                   fixups[fc].opindex = arc_operand_map[(int) *syn];
-                 ++fc;
-                 value = 0;
-               }
+                 break;
 
-             /* 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)
+               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))
                    {
-                     last_errmsg = errmsg;
-                     if (operand->flags & ARC_OPERAND_ERROR)
+                     int regs;
+
+                     regs = get_register (tok[tokidx].X_add_symbol);
+                     regs <<= 16;
+                     regs |= get_register (tok[tokidx].X_op_symbol);
+                     if (operand->insert)
                        {
-                         as_bad (errmsg);
-                         return;
+                         const char *errmsg = NULL;
+                         (*operand->insert)(0,
+                                            regs,
+                                            &errmsg);
+                         if (errmsg)
+                           goto match_failed;
                        }
-                     else if (operand->flags & ARC_OPERAND_WARN)
-                       as_warn (errmsg);
+                     else
+                       goto match_failed;
+                     break;
+                   }
+               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;
+                   case O_absent:
+                     if (!generic_reloc_p (operand->default_reloc))
+                       goto match_failed;
+                   default:
                      break;
                    }
+                 break;
                }
-             else
-               insn |= (value & ((1 << operand->bits) - 1)) << operand->shift;
+             /* If expect duplicate, make sure it is duplicate.  */
+             if (operand->flags & ARC_OPERAND_DUPLICATE)
+               {
+                 if (t->X_op == O_illegal
+                     || t->X_op == O_absent
+                     || t->X_op == O_register
+                     || (t->X_add_number != tok[tokidx].X_add_number))
+                   goto match_failed;
+               }
+             t = &tok[tokidx];
+             break;
+
+           default:
+             /* Everything else should have been fake.  */
+             abort ();
+           }
+
+         ++tokidx;
+       }
+      pr_debug ("opr ");
+
+      /* Setup ready for flag parsing.  */
+      lnflg = nflgs;
+      for (i = 0; i < nflgs; i++)
+        first_pflag [i].code = 0;
+
+      /* Check the flags.  Iterate over the valid flag classes.  */
+      for (flgidx = opcode->flags; *flgidx; ++flgidx)
+       {
+         /* Get a valid flag class.  */
+         const struct arc_flag_class *cl_flags = &arc_flag_classes[*flgidx];
+         const unsigned *flgopridx;
+         int cl_matches = 0;
+
+         for (flgopridx = cl_flags->flags; *flgopridx; ++flgopridx)
+           {
+             const struct arc_flag_operand *flg_operand;
+             struct arc_flags *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->code != 0)
+                       goto match_failed;
+                     cl_matches++;
+                     pflag->code = *flgopridx;
+                     lnflg--;
+                     break; /* goto next flag class and parsed flag.  */
+                   }
+               }
+           }
+
+         if (cl_flags->class == F_CLASS_REQUIRED && cl_matches == 0)
+           goto match_failed;
+         if (cl_flags->class == F_CLASS_OPTIONAL && cl_matches > 1)
+           goto match_failed;
+       }
+      /* Did I check all the parsed flags?  */
+      if (lnflg)
+       goto match_failed;
+
+      pr_debug ("flg");
+      /* Possible match -- did we use all of our input?  */
+      if (tokidx == ntok)
+       {
+         *pntok = ntok;
+         pr_debug ("\n");
+         return opcode;
+       }
+
+    match_failed:;
+      pr_debug ("\n");
+      /* Restore the original parameters.  */
+      memcpy (tok, bktok, MAX_INSN_ARGS * sizeof (*tok));
+      ntok = bkntok;
+    }
+
+  if (*pcpumatch)
+    *pcpumatch = got_cpu_match;
+
+  return NULL;
+}
+
+/* Swap operand tokens.  */
+
+static void
+swap_operand (expressionS *operand_array,
+             unsigned source,
+             unsigned destination)
+{
+  expressionS cpy_operand;
+  expressionS *src_operand;
+  expressionS *dst_operand;
+  size_t size;
+
+  if (source == destination)
+    return;
+
+  src_operand = &operand_array[source];
+  dst_operand = &operand_array[destination];
+  size = sizeof (expressionS);
+
+  /* Make copy of operand to swap with and swap.  */
+  memcpy (&cpy_operand, dst_operand, size);
+  memcpy (dst_operand, src_operand, size);
+  memcpy (src_operand, &cpy_operand, size);
+}
+
+/* Check if *op matches *tok type.
+   Returns FALSE if they don't match, TRUE if they match.  */
+
+static bfd_boolean
+pseudo_operand_match (const expressionS *tok,
+                     const struct arc_operand_operation *op)
+{
+  offsetT min, max, val;
+  bfd_boolean ret;
+  const struct arc_operand *operand_real = &arc_operands[op->operand_idx];
+
+  ret = FALSE;
+  switch (tok->X_op)
+    {
+    case O_constant:
+      if (operand_real->bits == 32 && (operand_real->flags & ARC_OPERAND_LIMM))
+       ret = 1;
+      else if (!(operand_real->flags & ARC_OPERAND_IR))
+       {
+         val = tok->X_add_number + op->count;
+         if (operand_real->flags & ARC_OPERAND_SIGNED)
+           {
+             max = (1 << (operand_real->bits - 1)) - 1;
+             min = -(1 << (operand_real->bits - 1));
+           }
+         else
+           {
+             max = (1 << operand_real->bits) - 1;
+             min = 0;
+           }
+         if (min <= val && val <= max)
+           ret = TRUE;
+       }
+      break;
+
+    case O_symbol:
+      /* Handle all symbols as long immediates or signed 9.  */
+      if (operand_real->flags & ARC_OPERAND_LIMM ||
+         ((operand_real->flags & ARC_OPERAND_SIGNED) && operand_real->bits == 9))
+       ret = TRUE;
+      break;
+
+    case O_register:
+      if (operand_real->flags & ARC_OPERAND_IR)
+       ret = TRUE;
+      break;
+
+    case O_bracket:
+      if (operand_real->flags & ARC_OPERAND_BRAKET)
+       ret = TRUE;
+      break;
+
+    default:
+      /* Unknown.  */
+      break;
+    }
+  return ret;
+}
+
+/* Find pseudo instruction in array.  */
+
+static const struct arc_pseudo_insn *
+find_pseudo_insn (const char *opname,
+                 int ntok,
+                 const expressionS *tok)
+{
+  const struct arc_pseudo_insn *pseudo_insn = NULL;
+  const struct arc_operand_operation *op;
+  unsigned int i;
+  int j;
+
+  for (i = 0; i < arc_num_pseudo_insn; ++i)
+    {
+      pseudo_insn = &arc_pseudo_insns[i];
+      if (strcmp (pseudo_insn->mnemonic_p, opname) == 0)
+       {
+         op = pseudo_insn->operand;
+         for (j = 0; j < ntok; ++j)
+           if (!pseudo_operand_match (&tok[j], &op[j]))
+             break;
+
+         /* Found the right instruction.  */
+         if (j == ntok)
+           return pseudo_insn;
+       }
+    }
+  return NULL;
+}
 
-             ++syn;
-           }
-       }
+/* Assumes the expressionS *tok is of sufficient size.  */
+
+static const struct arc_opcode_hash_entry *
+find_special_case_pseudo (const char *opname,
+                         int *ntok,
+                         expressionS *tok,
+                         int *nflgs,
+                         struct arc_flags *pflags)
+{
+  const struct arc_pseudo_insn *pseudo_insn = NULL;
+  const struct arc_operand_operation *operand_pseudo;
+  const struct arc_operand *operand_real;
+  unsigned i;
+  char construct_operand[MAX_CONSTR_STR];
+
+  /* Find whether opname is in pseudo instruction array.  */
+  pseudo_insn = find_pseudo_insn (opname, *ntok, tok);
+
+  if (pseudo_insn == NULL)
+    return NULL;
+
+  /* Handle flag, Limited to one flag at the moment.  */
+  if (pseudo_insn->flag_r != NULL)
+    *nflgs += tokenize_flags (pseudo_insn->flag_r, &pflags[*nflgs],
+                             MAX_INSN_FLGS - *nflgs);
+
+  /* Handle operand operations.  */
+  for (i = 0; i < pseudo_insn->operand_cnt; ++i)
+    {
+      operand_pseudo = &pseudo_insn->operand[i];
+      operand_real = &arc_operands[operand_pseudo->operand_idx];
+
+      if (operand_real->flags & ARC_OPERAND_BRAKET &&
+         !operand_pseudo->needs_insert)
+       continue;
 
-      /* 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')
+      /* Has to be inserted (i.e. this token does not exist yet).  */
+      if (operand_pseudo->needs_insert)
        {
-         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 (*str != '\0')
-           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;
-                 }
-             }
-
-           /* 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)
+         if (operand_real->flags & ARC_OPERAND_BRAKET)
            {
-             f = frag_more (8);
-             md_number_to_chars (f, insn, 4);
-             md_number_to_chars (f + 4, limm, 4);
-           }
-         else if (limm_reloc_p)
-           {
-             /* We need a limm reloc, but the tables think we don't.  */
-             abort ();
+             tok[i].X_op = O_bracket;
+             ++(*ntok);
+             continue;
            }
+
+         /* Check if operand is a register or constant and handle it
+            by type.  */
+         if (operand_real->flags & ARC_OPERAND_IR)
+           snprintf (construct_operand, MAX_CONSTR_STR, "r%d",
+                     operand_pseudo->count);
          else
+           snprintf (construct_operand, MAX_CONSTR_STR, "%d",
+                     operand_pseudo->count);
+
+         tokenize_arguments (construct_operand, &tok[i], 1);
+         ++(*ntok);
+       }
+
+      else if (operand_pseudo->count)
+       {
+         /* Operand number has to be adjusted accordingly (by operand
+            type).  */
+         switch (tok[i].X_op)
            {
-             f = frag_more (4);
-             md_number_to_chars (f, insn, 4);
+           case O_constant:
+             tok[i].X_add_number += operand_pseudo->count;
+             break;
+
+           case O_symbol:
+             break;
+
+           default:
+             /* Ignored.  */
+             break;
            }
+       }
+    }
+
+  /* Swap operands if necessary.  Only supports one swap at the
+     moment.  */
+  for (i = 0; i < pseudo_insn->operand_cnt; ++i)
+    {
+      operand_pseudo = &pseudo_insn->operand[i];
+
+      if (operand_pseudo->swap_operand_idx == i)
+       continue;
+
+      swap_operand (tok, i, operand_pseudo->swap_operand_idx);
+
+      /* Prevent a swap back later by breaking out.  */
+      break;
+    }
+
+  return arc_find_opcode (pseudo_insn->mnemonic_r);
+}
+
+static const struct arc_opcode_hash_entry *
+find_special_case_flag (const char *opname,
+                       int *nflgs,
+                       struct arc_flags *pflags)
+{
+  unsigned int i;
+  const char *flagnm;
+  unsigned flag_idx, flag_arr_idx;
+  size_t flaglen, oplen;
+  const struct arc_flag_special *arc_flag_special_opcode;
+  const struct arc_opcode_hash_entry *entry;
+
+  /* Search for special case instruction.  */
+  for (i = 0; i < arc_num_flag_special; i++)
+    {
+      arc_flag_special_opcode = &arc_flag_special_cases[i];
+      oplen = strlen (arc_flag_special_opcode->name);
+
+      if (strncmp (opname, arc_flag_special_opcode->name, oplen) != 0)
+       continue;
 
-         /* Create any fixups.  */
-         for (i = 0; i < fc; ++i)
+      /* 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)
            {
-             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);
+              entry = arc_find_opcode (arc_flag_special_opcode->name);
+
+             if (*nflgs + 1 > MAX_INSN_FLGS)
+               break;
+             memcpy (pflags[*nflgs].name, flagnm, flaglen);
+             pflags[*nflgs].name[flaglen] = '\0';
+             (*nflgs)++;
+             return entry;
            }
+       }
+    }
+  return NULL;
+}
+
+/* Used to find special case opcode.  */
+
+static const struct arc_opcode_hash_entry *
+find_special_case (const char *opname,
+                  int *nflgs,
+                  struct arc_flags *pflags,
+                  expressionS *tok,
+                  int *ntok)
+{
+  const struct arc_opcode_hash_entry *entry;
+
+  entry = find_special_case_pseudo (opname, ntok, tok, nflgs, pflags);
+
+  if (entry == NULL)
+    entry = find_special_case_flag (opname, nflgs, pflags);
+
+  return entry;
+}
 
-         /* All done.  */
+/* 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;
+
+  /* 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);
+      if (opcode != NULL)
+       {
+         struct arc_insn insn;
+
+         assemble_insn (opcode, tok, ntok, pflags, nflgs, &insn);
+         emit_insn (&insn);
          return;
        }
-
-      /* Try the next entry.  */
     }
 
-  if(NULL == last_errmsg)
-    as_bad ("bad instruction `%s'", start);
+  if (found_something)
+    {
+      if (cpumatch)
+       as_bad (_("inappropriate arguments for opcode '%s'"), opname);
+      else
+       as_bad (_("opcode '%s' not supported for target %s"), opname,
+               arc_target_name);
+    }
   else
-    as_bad (last_errmsg);
+    as_bad (_("unknown opcode '%s'"), opname);
 }
 
-\f
+/* The public interface to the instruction assembler.  */
 
-static void
-arc_extoper (opertype)
-     int opertype;
+void
+md_assemble (char *str)
 {
-  char *name;
-  char *mode;
-  char c;
-  char *p;
-  int imode = 0;
-  int number;
-  struct arc_ext_operand_value *ext_oper;
-  symbolS *symbolP;
+  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 = xmalloc (opnamelen + 1);
+  memcpy (opname, str, opnamelen);
+  opname[opnamelen] = '\0';
+
+  /* Signalize we are assmbling the instructions.  */
+  assembling_insn = TRUE;
+
+  /* Tokenize the flags.  */
+  if ((nflg = tokenize_flags (str + opnamelen, flags, MAX_INSN_FLGS)) == -1)
+    {
+      as_bad (_("syntax error"));
+      return;
+    }
 
-  segT old_sec;
-  int old_subsec;
+  /* 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;
 
-  name = input_line_pointer;
-  c = get_symbol_end ();
-  name = xstrdup(name);
-  if (NULL == name)
+  /* Tokenize the rest of the line.  */
+  if ((ntok = tokenize_arguments (str, tok, MAX_INSN_ARGS)) < 0)
     {
-      ignore_rest_of_line();
+      as_bad (_("syntax error"));
       return;
     }
 
-  p = name;
-  while (*p)
+  /* Finish it off.  */
+  assemble_tokens (opname, tok, ntok, flags, nflg);
+  assembling_insn = FALSE;
+}
+
+/* Callback to insert a register into the hash table.  */
+
+static void
+declare_register (const char *name, int number)
+{
+  const char *err;
+  symbolS *regS = symbol_create (name, reg_section,
+                                number, &zero_address_frag);
+
+  err = hash_insert (arc_reg_hash, S_GET_NAME (regS), (void *) regS);
+  if (err)
+    as_fatal (_("Inserting \"%s\" into register table failed: %s"),
+             name, err);
+}
+
+/* Construct symbols for each of the general registers.  */
+
+static void
+declare_register_set (void)
+{
+  int i;
+  for (i = 0; i < 64; ++i)
     {
-      if (isupper(*p))
-       *p = tolower(*p);
-      p++;
+      char name[7];
+
+      sprintf (name, "r%d", i);
+      declare_register (name, i);
+      if ((i & 0x01) == 0)
+       {
+         sprintf (name, "r%dr%d", i, i+1);
+         declare_register (name, i);
+       }
     }
+}
 
-  /* just after name is now '\0' */
-  p = input_line_pointer;
-  *p = c;
-  SKIP_WHITESPACE ();
+/* Port-specific assembler initialization.  This function is called
+   once, at assembler startup time.  */
+
+void
+md_begin (void)
+{
+  unsigned int i;
+
+  if (!mach_type_specified_p)
+    arc_select_cpu ("arc700");
 
-  if (*input_line_pointer != ',')
+  /* The endianness can be chosen "at the factory".  */
+  target_big_endian = byte_order == BIG_ENDIAN;
+
+  if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, arc_mach_type))
+    as_warn (_("could not set architecture and machine"));
+
+  /* Set elf header flags.  */
+  bfd_set_private_flags (stdoutput, arc_eflag);
+
+  /* Set up a hash table for the instructions.  */
+  arc_opcode_hash = hash_new ();
+  if (arc_opcode_hash == NULL)
+    as_fatal (_("Virtual memory exhausted"));
+
+  /* Initialize the hash table with the insns.  */
+  for (i = 0; i < arc_num_opcodes;)
     {
-      as_bad ("expected comma after operand name");
-      ignore_rest_of_line ();
-      free(name);
-      return;
+      const char *name, *retval;
+      struct arc_opcode_hash_entry *entry;
+
+      name = arc_opcodes[i].name;
+
+      entry = hash_find (arc_opcode_hash, name);
+      if (entry == NULL)
+        {
+          entry = xmalloc (sizeof (*entry));
+          entry->count = 0;
+          entry->opcode = NULL;
+
+          retval = hash_insert (arc_opcode_hash, name, (void *) entry);
+          if (retval)
+            as_fatal (_("internal error: can't hash opcode '%s': %s"),
+                      name, retval);
+        }
+
+      entry->opcode = xrealloc (entry->opcode,
+                                sizeof (const struct arc_opcode *)
+                                * entry->count + 1);
+      entry->opcode [entry->count] = &arc_opcodes[i];
+      entry->count++;
+
+      while (++i < arc_num_opcodes
+            && (arc_opcodes[i].name == name
+                || !strcmp (arc_opcodes[i].name, name)))
+       continue;
+    }
+
+  /* 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);
+
+  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));
+}
+
+/* Write a value out to the object file, using the appropriate
+   endianness.  */
+
+void
+md_number_to_chars (char *buf,
+                   valueT val,
+                   int n)
+{
+  if (target_big_endian)
+    number_to_chars_bigendian (buf, val, n);
+  else
+    number_to_chars_littleendian (buf, val, n);
+}
+
+/* Round up a section size to the appropriate boundary.  */
+
+valueT
+md_section_align (segT segment,
+                 valueT size)
+{
+  int align = bfd_get_section_alignment (stdoutput, segment);
+
+  return ((size + (1 << align) - 1) & (-((valueT) 1 << align)));
+}
+
+/* The location from which a PC relative jump should be calculated,
+   given a PC relative reloc.  */
+
+long
+md_pcrel_from_section (fixS *fixP,
+                      segT sec)
+{
+  offsetT base = fixP->fx_where + fixP->fx_frag->fr_address;
+
+  pr_debug ("pcrel_from_section, fx_offset = %d\n", (int) fixP->fx_offset);
+
+  if (fixP->fx_addsy != (symbolS *) NULL
+      && (!S_IS_DEFINED (fixP->fx_addsy)
+         || S_GET_SEGMENT (fixP->fx_addsy) != sec))
+    {
+      pr_debug ("Unknown pcrel symbol: %s\n", S_GET_NAME (fixP->fx_addsy));
+
+      /* The symbol is undefined (or is defined but not in this section).
+        Let the linker figure it out.  */
+      return 0;
+    }
+
+  if ((int) fixP->fx_r_type < 0)
+    {
+      /* These are the "internal" relocations.  Align them to
+        32 bit boundary (PCL), for the moment.  */
+      base &= ~3;
+    }
+  else
+    {
+      switch (fixP->fx_r_type)
+       {
+       case BFD_RELOC_ARC_PC32:
+         /* The hardware calculates relative to the start of the
+            insn, but this relocation is relative to location of the
+            LIMM, compensate.  The base always needs to be
+            substracted 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;
+       }
     }
 
-  input_line_pointer++;                /* skip ',' */
-  number = get_absolute_expression ();
+  pr_debug ("pcrel from %x + %lx = %x, symbol: %s (%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 coresponding 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
+insert_operand (unsigned insn,
+               const struct arc_operand *operand,
+               offsetT val,
+               const char *file,
+               unsigned line)
+{
+  offsetT min = 0, max = 0;
 
-  if(number < 0)
+  if (operand->bits != 32
+      && !(operand->flags & ARC_OPERAND_NCHK)
+      && !(operand->flags & ARC_OPERAND_FAKE))
     {
-      as_bad ("negative operand number %d",number);
-      ignore_rest_of_line();
-      free(name);
-      return;
+      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);
     }
 
-  if (opertype)
+  pr_debug ("insert field: %ld <= %ld <= %ld in 0x%08x\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)
     {
-      SKIP_WHITESPACE();
+      const char *errmsg = NULL;
 
-      if (*input_line_pointer != ',')
+      insn = (*operand->insert) (insn, val, &errmsg);
+      if (errmsg)
+       as_warn_where (file, line, "%s", errmsg);
+    }
+  else
+    {
+      if (operand->flags & ARC_OPERAND_TRUNCATE)
        {
-         as_bad ("expected comma after register-number");
-         ignore_rest_of_line ();
-         free(name);
-         return;
+         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);
+    }
 
-      input_line_pointer++;            /* skip ',' */
-      mode = input_line_pointer;
+  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 (!strncmp(mode, "r|w",3))
+      if (sub_symbol_segment == absolute_section)
        {
-         imode = 0;
-         input_line_pointer += 3;
+         /* The symbol is really a constant.  */
+         fx_offset -= S_GET_VALUE (fx_subsy);
+         fx_subsy = NULL;
        }
       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;
-               }
-           }
+         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;
        }
-      SKIP_WHITESPACE();
-      if (1 == opertype)
+    }
+
+  if (fx_addsy
+      && !S_IS_WEAK (fx_addsy))
+    {
+      if (add_symbol_segment == seg
+         && fixP->fx_pcrel)
        {
-         if (*input_line_pointer != ',')
-           {
-             as_bad ("expected comma after register-mode");
-             ignore_rest_of_line ();
-             free(name);
-             return;
-           }
+         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;
+       }
+    }
 
-         input_line_pointer++;             /* skip ',' */
+  if (!fx_addsy)
+    fixP->fx_done = TRUE;
 
-         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;
-               }
-           }
+  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 anylonger valid this afirmation 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_fatal (_("PC relative relocation not allowed for (internal) type %d"),
+                     fixP->fx_r_type);
+         break;
        }
     }
 
-  if ((opertype == 1) && number > 60)
+  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)
     {
-      as_bad("core register value (%d) too large", number);
-      ignore_rest_of_line();
-      free(name);
       return;
     }
 
-  if ((opertype == 0) && number > 31)
+  /* Addjust 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 |= (-1L << 31);
+
+  reloc = fixP->fx_r_type;
+  switch (reloc)
     {
-      as_bad("condition code value (%d) too large", number);
-      ignore_rest_of_line();
-      free(name);
+    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;
-    }
 
-  ext_oper = (struct arc_ext_operand_value *) \
-    xmalloc(sizeof (struct arc_ext_operand_value));
+    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);
+
+    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;
+
+    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(opertype)
+  if (target_big_endian)
     {
-      /* if the symbol already exists, point it at the new definition */
-      if ((symbolP = symbol_find (name)))
+      switch (fixP->fx_size)
        {
-         if (S_GET_SEGMENT(symbolP) == reg_section)
-           S_SET_VALUE(symbolP,(int)&ext_oper->operand);
-         else
-           {
-             as_bad("attempt to override symbol: %s",name);
-             ignore_rest_of_line();
-             free(name);
-             free(ext_oper);
-             return;
-           }
+       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
+    }
+  else
+    {
+      insn = 0;
+      switch (fixP->fx_size)
        {
-         /* If its not there, add it */
-         symbol_table_insert (symbol_create (name, reg_section,
-                                             (int) &ext_oper->operand, &zero_address_frag));
+       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"));
        }
     }
 
-  ext_oper->operand.name  = name;
-  ext_oper->operand.value = number;
-  ext_oper->operand.type  = arc_operand_type(opertype);
-  ext_oper->operand.flags = imode;
+  insn = insert_operand (insn, operand, (offsetT) value,
+                        fixP->fx_file, fixP->fx_line);
 
-  ext_oper->next = arc_ext_operands;
-  arc_ext_operands = ext_oper;
+  md_number_to_chars_midend (fixpos, insn, fixP->fx_size);
+}
+
+/* Prepare machine-dependent frags for relaxation.
 
-  /* ok, now that we know what this operand is, put a description
-     in the arc extension section of the output file */
+   Called just before relaxation starts.  Any symbol that is now undefined
+   will not become defined.
 
-  old_sec    = now_seg;
-  old_subsec = now_subseg;
+   Return the correct fr_subtype in the frag.
 
-  arc_set_ext_seg();
+   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.
 
-  switch (opertype)
+   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))
     {
-    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;
+      while (md_relax_table[fragP->fr_subtype].rlx_more != ARC_RLX_NONE)
+       ++fragP->fr_subtype;
     }
 
-  subseg_set (old_sec, old_subsec);
+  growth = md_relax_table[fragP->fr_subtype].rlx_length;
+  fragP->fr_var = growth;
 
-  /* enter all registers into the symbol table */
+  pr_debug ("%s:%d: md_estimate_size_before_relax: %d\n",
+          fragP->fr_file, fragP->fr_line, growth);
 
-  demand_empty_rest_of_line();
+  return growth;
 }
 
-static void
-arc_extinst (ignore)
-    int ignore ATTRIBUTE_UNUSED;
+/* Translate internal representation of relocation info to BFD target
+   format.  */
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED,
+             fixS *fixP)
 {
-  unsigned char syntax[129];
-  char *name;
-  char *p;
-  char c;
-  int suffixcode = -1;
-  int opcode,subopcode;
-  int i;
-  int class = 0;
-  int name_len;
-  struct arc_opcode *ext_op;
+  arelent *reloc;
+  bfd_reloc_code_real_type code;
 
-  segT old_sec;
-  int old_subsec;
+  reloc = (arelent *) xmalloc (sizeof (* reloc));
+  reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
+  reloc->address = fixP->fx_frag->fr_address + fixP->fx_where;
 
-  name = input_line_pointer;
-  c = get_symbol_end ();
-  name = xstrdup(name);
-  if (NULL == name)
-    {
-      ignore_rest_of_line();
-      return;
-    }
-  strcpy(syntax,name);
-  name_len = strlen(name);
+  /* 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);
 
-  /* just after name is now '\0' */
-  p = input_line_pointer;
-  *p = c;
+  code = fixP->fx_r_type;
 
-  SKIP_WHITESPACE ();
+  /* 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;
 
-  if (*input_line_pointer != ',')
+  reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+  if (reloc->howto == NULL)
     {
-      as_bad ("expected comma after operand name");
-      ignore_rest_of_line ();
-      return;
+      as_bad_where (fixP->fx_file, fixP->fx_line,
+                   _("cannot represent `%s' relocation in object file"),
+                   bfd_get_reloc_code_name (code));
+      return NULL;
     }
 
-  input_line_pointer++;                /* skip ',' */
-  opcode = get_absolute_expression ();
+  if (!fixP->fx_pcrel != !reloc->howto->pc_relative)
+    as_fatal (_("internal error? cannot generate `%s' relocation"),
+             bfd_get_reloc_code_name (code));
 
-  SKIP_WHITESPACE ();
+  gas_assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
 
-  if (*input_line_pointer != ',')
+  if (code == BFD_RELOC_ARC_TLS_DTPOFF
+      || code ==  BFD_RELOC_ARC_TLS_DTPOFF_S9)
     {
-      as_bad ("expected comma after opcode");
-      ignore_rest_of_line ();
-      return;
+      asymbol *sym
+       = fixP->fx_subsy ? symbol_get_bfdsym (fixP->fx_subsy) : NULL;
+      /* We just want to store a 24 bit index, but we have to wait
+        till after write_contents has been called via
+        bfd_map_over_sections before we can get the index from
+        _bfd_elf_symbol_from_bfd_symbol.  Thus, the write_relocs
+        function is elf32-arc.c has to pick up the slack.
+        Unfortunately, this leads to problems with hosts that have
+        pointers wider than long (bfd_vma).  There would be various
+        ways to handle this, all error-prone :-(  */
+      reloc->addend = (bfd_vma) sym;
+      if ((asymbol *) reloc->addend != sym)
+       {
+         as_bad ("Can't store pointer\n");
+         return NULL;
+       }
     }
+  else
+    reloc->addend = fixP->fx_offset;
 
-  input_line_pointer++;                /* skip ',' */
-  subopcode = get_absolute_expression ();
+  return reloc;
+}
 
-  if(subopcode < 0)
-    {
-      as_bad ("negative subopcode %d",subopcode);
-      ignore_rest_of_line();
-      return;
-    }
+/* 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.
 
-  if(subopcode)
-    {
-      if(3 != opcode)
-       {
-         as_bad ("subcode value found when opcode not equal 0x03");
-         ignore_rest_of_line();
-         return;
-       }
-      else
-       {
-         if (subopcode < 0x09 || subopcode == 0x3f)
-           {
-             as_bad ("invalid subopcode %d", subopcode);
-             ignore_rest_of_line();
-             return;
-           }
-       }
-    }
+   Out: Any fixS:s and constants are set up.  */
 
-  SKIP_WHITESPACE ();
+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;
 
-  if (*input_line_pointer != ',')
-    {
-      as_bad ("expected comma after subopcode");
-      ignore_rest_of_line ();
-      return;
-    }
+  fix = (fragP->fr_fix < 0 ? 0 : fragP->fr_fix);
+  dest = fragP->fr_literal + fix;
+  table_entry = TC_GENERIC_RELAX_TABLE + fragP->fr_subtype;
 
-  input_line_pointer++;         /* skip ',' */
+  pr_debug ("%s:%d: md_convert_frag, subtype: %d, fix: %d, var: %d\n",
+           fragP->fr_file, fragP->fr_line,
+           fragP->fr_subtype, fix, fragP->fr_var);
 
-  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;
-         break;
-       }
-    }
+  if (fragP->fr_subtype <= 0
+      && fragP->fr_subtype >= arc_num_relax_opcodes)
+    as_fatal (_("no relaxation found for this instruction."));
 
-  if(-1 == suffixcode)
-    {
-      as_bad ("invalid suffix class");
-      ignore_rest_of_line ();
-      return;
-    }
+  opcode = &arc_relax_opcodes[fragP->fr_subtype];
 
-  SKIP_WHITESPACE ();
+  assemble_insn (opcode, relax_arg->tok, relax_arg->ntok, relax_arg->pflags,
+       relax_arg->nflg, &insn);
 
-  if (*input_line_pointer != ',')
-    {
-      as_bad ("expected comma after suffix class");
-      ignore_rest_of_line ();
-      return;
-    }
+  apply_fixups (&insn, fragP, fix);
 
-  input_line_pointer++;         /* skip ',' */
+  size = insn.short_insn ? (insn.has_limm ? 6 : 2) : (insn.has_limm ? 8 : 4);
+  gas_assert (table_entry->rlx_length == size);
+  emit_insn0 (&insn, dest, TRUE);
 
-  for(i = 0; i < (int) MAXSYNTAXCLASS; i++)
+  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))
+      || ((*name == '_')
+         && (*(name+1) == 'D')
+         && (strcmp (name, DYNAMIC_STRUCT_NAME) == 0)))
     {
-      if(!strncmp(syntaxclass[i].name,input_line_pointer, syntaxclass[i].len))
+      if (!GOT_symbol)
        {
-         class = syntaxclass[i].class;
-         input_line_pointer += syntaxclass[i].len;
-         break;
-       }
+         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);
+}
 
-  if(0 == (SYNTAX_VALID & class))
+/* 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.  */
+
+void
+md_operand (expressionS *expressionP ATTRIBUTE_UNUSED)
+{
+  char *p = input_line_pointer;
+  if (*p == '@')
     {
-      as_bad ("invalid syntax class");
-      ignore_rest_of_line ();
-      return;
+      input_line_pointer++;
+      expressionP->X_op = O_symbol;
+      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 ((0x3 == opcode) & (class &  SYNTAX_3OP))
+  if (!assembling_insn)
+    return FALSE;
+
+  /* Handle only registers.  */
+  if (e->X_op != O_absent)
+    return FALSE;
+
+  sym = hash_find (arc_reg_hash, name);
+  if (sym)
     {
-      as_bad ("opcode 0x3 and SYNTAX_3OP invalid");
-      ignore_rest_of_line ();
-      return;
+      e->X_op = O_register;
+      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:
+   arc700, av2em, av2hs.  */
 
-  switch (suffixcode)
+int
+md_parse_option (int c, const char *arg ATTRIBUTE_UNUSED)
+{
+  switch (c)
     {
-    case 0:
-      strcat(syntax,"%.q%.f ");
+    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_type_specified_p = 1;
+       break;
+      }
+
+    case OPTION_EB:
+      arc_target_format = "elf32-bigarc";
+      byte_order = BIG_ENDIAN;
       break;
-    case 1:
-      strcat(syntax,"%.f ");
+
+    case OPTION_EL:
+      arc_target_format = "elf32-littlearc";
+      byte_order = LITTLE_ENDIAN;
       break;
-    case 2:
-      strcat(syntax,"%.q ");
+
+    case OPTION_CD:
+      /* This option has an effect only on ARC EM.  */
+      if (arc_target & ARC_OPCODE_ARCv2EM)
+       arc_features |= ARC_CD;
+      else
+       as_warn (_("Code density option invalid for selected CPU"));
       break;
-    case 3:
-      strcat(syntax," ");
+
+    case OPTION_RELAX:
+      relaxation_state = 1;
       break;
-    default:
-      as_bad("unknown suffix class");
-      ignore_rest_of_line();
-      return;
+
+    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:
+      /* Dummy options are accepted but have no effect.  */
       break;
-    };
 
-  strcat(syntax,((opcode == 0x3) ? "%a,%b" : ((class & SYNTAX_3OP) ? "%a,%b,%c" : "%b,%c")));
-  if(suffixcode < 2)
-    strcat(syntax,"%F");
-  strcat(syntax,"%S%L");
+    case OPTION_SPFP:
+      arc_features |= ARC_SPFP;
+      break;
 
-  ext_op = (struct arc_opcode *) xmalloc(sizeof (struct arc_opcode));
-  if(NULL == ext_op)
-    {
-      ignore_rest_of_line ();
-      return;
-    }
+    case OPTION_DPFP:
+      arc_features |= ARC_DPFP;
+      break;
 
-  ext_op->syntax = xstrdup(syntax);
-  if (NULL == ext_op->syntax)
-    {
-      ignore_rest_of_line ();
-      return;
+    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:
+      /* Dummy options are accepted but have no effect.  */
+      break;
+
+    case OPTION_FPUDA:
+      /* This option has an effect only on ARC EM.  */
+      if (arc_target & ARC_OPCODE_ARCv2EM)
+       arc_features |= ARC_FPUDA;
+      else
+       as_warn (_("FPUDA invalid for selected CPU"));
+      break;
+
+    default:
+      return 0;
     }
 
-  ext_op->mask  = I(-1) | ((0x3 == opcode) ? C(-1) : 0 );
-  ext_op->value = I(opcode) | ((0x3 == opcode) ? C(subopcode) : 0 );
-  ext_op->flags = class;
-  ext_op->next_asm = arc_ext_opcodes;
-  ext_op->next_dis = arc_ext_opcodes;
-  arc_ext_opcodes = ext_op;
+  return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+  fprintf (stream, _("ARC-specific assembler options:\n"));
+
+  fprintf (stream, "  -mcpu=<cpu name>\t  assemble for CPU <cpu name>\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"));
+
+}
+
+/* Find the proper relocation for the given opcode.  */
 
-  /* ok, now that we know what this inst is, put a description in
-     the arc extension section of the output file */
+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;
 
-  old_sec    = now_seg;
-  old_subsec = now_subseg;
+  for (i = 0; i < arc_num_equiv_tab; i++)
+    {
+      const struct arc_reloc_equiv_tab *r = &arc_reloc_equiv[i];
 
-  arc_set_ext_seg();
+      /* 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);
 
-  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 = (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';
+         if (!found_flag)
+           continue;
+       }
 
-  subseg_set (old_sec, old_subsec);
+      if (reloc != r->oldreloc)
+       continue;
+      /* Found it.  */
+      ret = r->newreloc;
+      break;
+    }
 
-  demand_empty_rest_of_line();
+  if (ret == BFD_RELOC_UNUSED)
+    as_bad (_("Unable to find %s relocation for instruction %s"),
+           name, opcodename);
+  return ret;
 }
 
-int
-arc_set_ext_seg()
+/* All the symbol types that are allowed to be used for
+   relaxation.  */
+
+static bfd_boolean
+may_relax_expr (expressionS tok)
 {
-  if (!arcext_section)
+  /* Check if we have unrelaxable relocs.  */
+  switch (tok.X_md)
+    {
+    default:
+      break;
+    case O_plt:
+      return FALSE;
+    }
+
+  switch (tok.X_op)
     {
-      arcext_section = subseg_new (".arcextmap", 0);
-      bfd_set_section_flags (stdoutput, arcext_section,
-                             SEC_READONLY | SEC_HAS_CONTENTS);
+    case O_symbol:
+    case O_multiply:
+    case O_divide:
+    case O_modulus:
+    case O_add:
+    case O_subtract:
+      break;
+
+    default:
+      return FALSE;
     }
-  else
-    subseg_set (arcext_section, 0);
-  return 1;
+  return TRUE;
 }
 
-static void
-arc_common (localScope)
-     int localScope;
+/* 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)
 {
-  char *name;
-  char c;
-  char *p;
-  int  align, size;
-  symbolS *symbolP;
-
-  name = input_line_pointer;
-  c = get_symbol_end ();
-  /* just after name is now '\0' */
-  p = input_line_pointer;
-  *p = c;
-  SKIP_WHITESPACE ();
+  unsigned flag_class,
+    flag,
+    flag_class_idx = 0,
+    flag_idx = 0;
+
+  const struct arc_flag_operand *flag_opand;
+  int i, counttrue = 0;
 
-  if (*input_line_pointer != ',')
+  /* Iterate through flags classes.  */
+  while ((flag_class = ins->flag_classes[flag_class_idx]) != 0)
     {
-      as_bad ("expected comma after symbol name");
-      ignore_rest_of_line ();
-      return;
+      /* 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;
     }
 
-  input_line_pointer++;                /* skip ',' */
-  size = get_absolute_expression ();
+  /* 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;
 
-  if (size < 0)
+  while (*operand != EMPTY)
     {
-      as_bad ("negative symbol length");
-      ignore_rest_of_line ();
-      return;
+      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];
     }
 
-  *p = 0;
-  symbolP = symbol_find_or_make (name);
-  *p = c;
+  return (i == ntok ? TRUE : FALSE);
+}
+
+/* Return TRUE if this OPDCODE is a candidate for relaxation.  */
 
-  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);
-    }
-  assert (symbolP->sy_frag == &zero_address_frag);
+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;
 
-  /* Now parse the alignment field.  This field is optional for
-     local and global symbols. Default alignment is zero.  */
-  if (*input_line_pointer == ',')
+  /* Check the relaxation table.  */
+  for (i = 0; i < arc_num_relaxable_ins && relaxation_state; ++i)
     {
-      input_line_pointer++;
-      align = get_absolute_expression ();
-      if (align < 0)
+      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))
        {
-         align = 0;
-         as_warn ("assuming symbol alignment of zero");
+         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;
        }
     }
-  else
-    align = 0;
 
-  if (localScope != 0)
-    {
-      segT old_sec;
-      int old_subsec;
-      char *pfrag;
+  return rv;
+}
 
-      old_sec    = now_seg;
-      old_subsec = now_subseg;
-      record_alignment (bss_section, align);
-      subseg_set (bss_section, 0);  /* ??? subseg_set (bss_section, 1); ??? */
+/* Turn an opcode description and a set of arguments into
+   an instruction and a fixup.  */
 
-      if (align)
-       frag_align (align, 0, 0); /* do alignment */
+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 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;
 
-      /* detach from old frag */
-      if (S_GET_SEGMENT (symbolP) == bss_section)
-       symbolP->sy_frag->fr_symbol = NULL;
+  memset (insn, 0, sizeof (*insn));
+  image = opcode->opcode;
 
-      symbolP->sy_frag = frag_now;
-      pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
-                       (offsetT) size, (char *) 0);
-      *pfrag = 0;
+  pr_debug ("%s:%d: assemble_insn: %s using opcode %x\n",
+           frag_now->fr_file, frag_now->fr_line, opcode->name,
+           opcode->opcode);
 
-      S_SET_SIZE       (symbolP, size);
-      S_SET_SEGMENT    (symbolP, bss_section);
-      S_CLEAR_EXTERNAL (symbolP);
-      symbolP->local = 1;
-      subseg_set (old_sec, old_subsec);
-    }
-  else
+  /* Handle operands.  */
+  for (argidx = opcode->operands; *argidx; ++argidx)
     {
-      S_SET_VALUE    (symbolP, (valueT) size);
-      S_SET_ALIGN    (symbolP, align);
-      S_SET_EXTERNAL (symbolP);
-      S_SET_SEGMENT  (symbolP, bfd_com_section_ptr);
-    }
-
-  symbolP->bsym->flags |= BSF_OBJECT;
+      const struct arc_operand *operand = &arc_operands[*argidx];
+      const expressionS *t = (const expressionS *) 0;
 
-  demand_empty_rest_of_line ();
-  return;
-}
+      if ((operand->flags & ARC_OPERAND_FAKE)
+         && !(operand->flags & ARC_OPERAND_BRAKET))
+       continue;
 
-\f
+      if (operand->flags & ARC_OPERAND_DUPLICATE)
+       {
+         /* Duplicate operand, already inserted.  */
+         tokidx ++;
+         continue;
+       }
 
-/* Select the cpu we're assembling for.  */
+      if (tokidx >= ntok)
+       {
+         abort ();
+       }
+      else
+       t = &tok[tokidx++];
 
-static void
-arc_option (ignore)
-    int ignore ATTRIBUTE_UNUSED;
-{
-  int mach;
-  char c;
-  char *cpu;
+      /* 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;
 
-  cpu = input_line_pointer;
-  c = get_symbol_end ();
-  mach = arc_get_mach (cpu);
-  *input_line_pointer = c;
+      switch (t->X_op)
+       {
+       case O_register:
+         image = insert_operand (image, operand, regno (t->X_add_number),
+                                 NULL, 0);
+         break;
 
-  /* If an instruction has already been seen, it's too late.  */
-  if (cpu_tables_init_p)
-    {
-      as_bad ("\".option\" directive must appear before any instructions");
-      ignore_rest_of_line ();
-      return;
-    }
+       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;
 
-  if (mach == -1)
-    goto bad_cpu;
+       case O_bracket:
+         /* Ignore brackets.  */
+         break;
 
-  if (mach_type_specified_p && mach != arc_mach_type)
-    {
-      as_bad ("\".option\" directive conflicts with initial definition");
-      ignore_rest_of_line ();
-      return;
-    }
-  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;
+       case O_absent:
+         gas_assert (operand->flags & ARC_OPERAND_IGNORE);
+         break;
 
- bad_cpu:
-  as_bad ("invalid identifier for \".option\"");
-  ignore_rest_of_line ();
-}
+       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;
 
-\f
-/* 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.  */
+             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;
+           }
 
-/* Equal to MAX_PRECISION in atof-ieee.c  */
-#define MAX_LITTLENUMS 6
+       default:
+         /* This operand needs a relocation.  */
+         needGOTSymbol = FALSE;
 
-char *
-md_atof (type, litP, sizeP)
-  char type;
-  char *litP;
-  int *sizeP;
-{
-  int prec;
-  LITTLENUM_TYPE words[MAX_LITTLENUMS];
-  LITTLENUM_TYPE *wordP;
-  char *t;
-  char *atof_ieee ();
+         switch (t->X_md)
+           {
+           case O_plt:
+             if (opcode->class == JUMP)
+               as_bad_where (frag_now->fr_file, frag_now->fr_line,
+                             _("Unable to use @plt relocatio for insn %s"),
+                             opcode->name);
+             needGOTSymbol = TRUE;
+             reloc = find_reloc ("plt", opcode->name,
+                                 pflags, nflg,
+                                 operand->default_reloc);
+             break;
+
+           case O_gotoff:
+           case O_gotpc:
+             needGOTSymbol = TRUE;
+             reloc = ARC_RELOC_TABLE (t->X_md)->reloc;
+             break;
+           case O_pcl:
+             reloc = ARC_RELOC_TABLE (t->X_md)->reloc;
+             if (ARC_SHORT (opcode->mask) || opcode->class == JUMP)
+               as_bad_where (frag_now->fr_file, frag_now->fr_line,
+                             _("Unable to use @pcl relocation for insn %s"),
+                             opcode->name);
+             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;
+           }
 
-  switch (type)
-    {
-    case 'f':
-    case 'F':
-      prec = 2;
-      break;
+         if (needGOTSymbol && (GOT_symbol == NULL))
+           GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
 
-    case 'd':
-    case 'D':
-      prec = 4;
-      break;
+         reloc_exp = t;
 
-    default:
-      *sizeP = 0;
-      return "bad call to md_atof";
+#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;
+         pcrel = (operand->flags & ARC_OPERAND_PCREL) ? 1 : 0;
+         fixup->pcrel = pcrel;
+         fixup->islong = (operand->flags & ARC_OPERAND_LIMM) ?
+           TRUE : FALSE;
+         break;
+       }
     }
 
-  t = atof_ieee (input_line_pointer, type, words);
-  if (t)
-    input_line_pointer = t;
-  *sizeP = prec * sizeof (LITTLENUM_TYPE);
-  for (wordP = words; prec--;)
+  /* Handle flags.  */
+  for (i = 0; i < nflg; i++)
     {
-      md_number_to_chars (litP, (valueT) (*wordP++), sizeof (LITTLENUM_TYPE));
-      litP += sizeof (LITTLENUM_TYPE);
-    }
-
-  return NULL;
-}
+      const struct arc_flag_operand *flg_operand =
+       &arc_flag_operands[pflags[i].code];
+
+      /* 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.  The .T flag must be handled in relation with
+        the relative address.  */
+      if (!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;
 
-/* Write a value out to the object file, using the appropriate
-   endianness.  */
+             if (insn->nfixups >= MAX_INSN_FIXUPS)
+               as_fatal (_("too many fixups"));
 
-void
-md_number_to_chars (buf, val, n)
-     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);
-}
+             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;
+    }
 
-/* Round up a section size to the appropriate boundary.  */
+  insn->relax = relax_insn_p (opcode, tok, ntok, pflags, nflg);
 
-valueT
-md_section_align (segment, size)
-     segT segment;
-     valueT size;
-{
-  int align = bfd_get_section_alignment (stdoutput, segment);
+  /* Short instruction?  */
+  insn->short_insn = ARC_SHORT (opcode->mask) ? TRUE : FALSE;
 
-  return ((size + (1 << align) - 1) & (-1 << align));
-}
+  insn->insn = image;
 
-/* We don't have any form of relaxing.  */
+  /* 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;
 
-int
-md_estimate_size_before_relax (fragp, seg)
-     fragS *fragp ATTRIBUTE_UNUSED;
-     asection *seg ATTRIBUTE_UNUSED;
-{
-  as_fatal (_("md_estimate_size_before_relax\n"));
-  return 1;
+  /* 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_where (frag_now->fr_file, frag_now->fr_line,
+                 _("A jump/branch instruction in delay slot."));
 }
 
-/* Convert a machine dependent frag.  We never generate these.  */
-
 void
-md_convert_frag (abfd, sec, fragp)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     asection *sec ATTRIBUTE_UNUSED;
-     fragS *fragp ATTRIBUTE_UNUSED;
+arc_handle_align (fragS* fragP)
 {
-  as_fatal (_("md_convert_frag\n"));
-}
-
-void
-arc_code_symbol(expressionP)
-     expressionS *expressionP;
-{
-  if (expressionP->X_op == O_symbol && expressionP->X_add_number == 0
-      /* I think this test is unnecessary but just as a sanity check...  */
-      && expressionP->X_op_symbol == NULL)
-    {
-      expressionS two;
-      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);
-    }
-  /* 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;
-      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
+  if ((fragP)->fr_type == rs_align_code)
     {
-      as_bad ("expression too complex code symbol");
-      return;
+      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);
     }
 }
 
-/* Parse an operand that is machine-specific.
+/* 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.  */
 
-   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.
+int
+tc_arc_fix_adjustable (fixS *fixP)
+{
 
-   ??? 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.  */
+  /* Prevent all adjustments to global symbols.  */
+  if (S_IS_EXTERNAL (fixP->fx_addsy))
+    return 0;
+  if (S_IS_WEAK (fixP->fx_addsy))
+    return 0;
 
-void
-md_operand (expressionP)
-     expressionS *expressionP;
-{
-  char *p = input_line_pointer;
+  /* 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;
 
-  if (*p == '%')
-    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++;
-
-       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 = (int) &ext_oper->operand;
-               return;
-             }
-           ext_oper = ext_oper->next;
-         }
-       for (i = 0; i < arc_reg_names_count; i++)
-         {
-           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 = (int)  &arc_reg_names[i];
-               break;
-             }
-         }
-      }
+    default:
+      break;
+    }
+
+  return 1;
 }
 
-/* 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.  */
+/* Compute the reloc type of an expression EXP.  */
 
-symbolS *
-md_undefined_symbol (name)
-     char *name ATTRIBUTE_UNUSED;
+static void
+arc_check_reloc (expressionS *exp,
+                bfd_reloc_code_real_type *r_type_p)
 {
-  return 0;
+  if (*r_type_p == BFD_RELOC_32
+      && exp->X_op == O_subtract
+      && exp->X_op_symbol != NULL
+      && exp->X_op_symbol->bsym->section == now_seg)
+    *r_type_p = BFD_RELOC_ARC_32_PCREL;
 }
-\f
-/* Functions concerning expressions.  */
 
-/* Parse a .byte, .word, etc. expression.
 
-   Values for the status register are specified with %st(label).
-   `label' will be right shifted by 2.  */
+/* Add expression EXP of SIZE bytes to offset OFF of fragment FRAG.  */
 
 void
-arc_parse_cons_expression (exp, nbytes)
-  expressionS *exp;
-  unsigned int nbytes ATTRIBUTE_UNUSED;
+arc_cons_fix_new (fragS *frag,
+                 int off,
+                 int size,
+                 expressionS *exp,
+                 bfd_reloc_code_real_type r_type)
 {
-  char *p = input_line_pointer;
-  int code_symbol_fix = 0;
+  r_type = BFD_RELOC_UNUSED;
 
-  for (;! is_end_of_line[(unsigned char) *p]; p++)
-    if (*p == '@' && !strncmp(p,"@h30",4))
-      {
-       code_symbol_fix = 1;
-       strcpy(p,";   ");
-      }
-  expr (0, exp);
-  if (code_symbol_fix)
+  switch (size)
     {
-      arc_code_symbol(exp);
-      input_line_pointer = p;
-    }
-}
+    case 1:
+      r_type = BFD_RELOC_8;
+      break;
 
-/* Record a fixup for a cons expression.  */
+    case 2:
+      r_type = BFD_RELOC_16;
+      break;
 
-void
-arc_cons_fix_new (frag, where, nbytes, exp)
-     fragS *frag;
-     int where;
-     int nbytes;
-     expressionS *exp;
-{
-  if (nbytes == 4)
-    {
-      int reloc_type;
-      expressionS exptmp;
+    case 3:
+      r_type = BFD_RELOC_24;
+      break;
 
-      /* 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, reloc_type);
-    }
-  else
-    {
-      fix_new_exp (frag, where, nbytes, exp, 0,
-                  nbytes == 2 ? BFD_RELOC_16
-                  : nbytes == 8 ? BFD_RELOC_64
-                  : BFD_RELOC_32);
-    }
-}
-\f
-/* Functions concerning relocs.  */
+    case 4:
+      r_type = BFD_RELOC_32;
+      arc_check_reloc (exp, &r_type);
+      break;
 
-/* The location from which a PC relative jump should be calculated,
-   given a PC relative reloc.  */
+    case 8:
+      r_type = BFD_RELOC_64;
+      break;
 
-long
-md_pcrel_from (fixP)
-     fixS *fixP;
-{
-  if (fixP->fx_addsy != (symbolS *) NULL
-      && ! S_IS_DEFINED (fixP->fx_addsy))
-    {
-      /* The symbol is undefined.  Let the linker figure it out.  */
-      return 0;
+    default:
+      as_bad (_("unsupported BFD relocation size %u"), size);
+      r_type = BFD_RELOC_UNUSED;
     }
 
-  /* Return the address of the delay slot.  */
-  return fixP->fx_frag->fr_address + fixP->fx_where + fixP->fx_size;
+  fix_new_exp (frag, off, size, exp, 0, r_type);
 }
 
-/* Compute the reloc type of an expression.
-   The possibly modified expression is stored in EXPNEW.
+/* The actual routine that checks the ZOL conditions.  */
 
-   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.).
+static void
+check_zol (symbolS *s)
+{
+  switch (arc_mach_type)
+    {
+    case bfd_mach_arc_arcv2:
+      if (arc_target & ARC_OPCODE_ARCv2EM)
+       return;
 
-   Currently supported %-ops:
+      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));
 
-   %st(symbol): represented as "symbol >> 2"
-                "st" is short for STatus as in the status register (pc)
+      break;
+    case bfd_mach_arc_arc600:
 
-   DEFAULT_TYPE is the type to use if no special processing is required.
+      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));
 
-   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.  */
+      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));
 
-static int
-get_arc_exp_reloc_type (data_p, default_type, exp, expnew)
-     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.  Similarily 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 (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)
-    {
-      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'];
-       }
-    }
+      /* Fall through.  */
+    case bfd_mach_arc_nps400:
+    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));
 
-  *expnew = *exp;
-  return default_type;
+      break;
+    default:
+      break;
+    }
 }
 
-/* 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.  */
-
-int
-md_apply_fix3 (fixP, valueP, seg)
-     fixS *fixP;
-     valueT *valueP;
-     segT seg;
-{
-  /*char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;*/
-  valueT value;
-
-  /* FIXME FIXME FIXME: The value we are passed in *valueP includes
-     the symbol values.  Since we are using BFD_ASSEMBLER, if we are
-     doing this relocation the code in write.c is going to call
-     bfd_perform_relocation, which is also going to use the symbol
-     value.  That means that if the reloc is fully resolved we want to
-     use *valueP since bfd_perform_relocation is not being used.
-     However, if the reloc is not fully resolved we do not want to use
-     *valueP, and must use fx_offset instead.  However, if the reloc
-     is PC relative, we do want to use *valueP since it includes the
-     result of md_pcrel_from.  This is confusing.  */
-
-  if (fixP->fx_addsy == (symbolS *) NULL)
-    {
-      value = *valueP;
-      fixP->fx_done = 1;
-    }
-  else if (fixP->fx_pcrel)
-    {
-      value = *valueP;
-      /* ELF relocations are against symbols.
-        If this symbol is in a different section then we need to leave it for
-        the linker to deal with.  Unfortunately, md_pcrel_from can't tell,
-        so we have to undo it's effects here.  */
-      if (S_IS_DEFINED (fixP->fx_addsy)
-         && S_GET_SEGMENT (fixP->fx_addsy) != seg)
-       value += md_pcrel_from (fixP);
-    }
-  else
-    {
-      value = fixP->fx_offset;
-      if (fixP->fx_subsy != (symbolS *) NULL)
-       {
-         if (S_GET_SEGMENT (fixP->fx_subsy) == absolute_section)
-           value -= S_GET_VALUE (fixP->fx_subsy);
-         else
-           {
-             /* We can't actually support subtracting a symbol.  */
-             as_bad_where (fixP->fx_file, fixP->fx_line,
-                           "expression too complex");
-           }
-       }
-    }
+/* 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);
 
-  if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
-    {
-      int opindex;
-      const struct arc_operand *operand;
-      char *where;
-      arc_insn insn;
+  dwarf2_emit_label (sym);
+}
 
-      opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
+/* 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)
+{
+  if (!fragP->tc_frag_data.pcrel)
+    return fragP->fr_address + fragP->fr_fix;
 
-      operand = &arc_operands[opindex];
+  return 0;
+}
 
-      /* 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);
+/* Initialize the DWARF-2 unwind information for this procedure.  */
 
-      if (fixP->fx_done)
-       {
-         /* Nothing else to do here.  */
-         return 1;
-       }
+void
+tc_arc_frame_initial_instructions (void)
+{
+  /* Stack pointer is register 28.  */
+  cfi_add_CFA_def_cfa_register (28);
+}
 
-      /* 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')
-       {
-         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')
-       {
-         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')
-       {
-         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 1;
-       }
-    }
-  else
-    {
-      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;
-#if 0
-       case BFD_RELOC_64:
-         md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                             value, 8);
-         break;
-#endif
-       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 ();
-       }
-    }
+int
+tc_arc_regname_to_dw2regnum (char *regname)
+{
+  struct symbol *sym;
 
-  fixP->fx_addnumber = value;
+  sym = hash_find (arc_reg_hash, regname);
+  if (sym)
+    return S_GET_VALUE (sym);
 
-  return 1;
+  return -1;
 }
 
-/* Translate internal representation of relocation info to BFD target
-   format.  */
+/* Adjust the symbol table.  Delete found AUX register symbols.  */
 
-arelent *
-tc_gen_reloc (section, fixP)
-     asection *section ATTRIBUTE_UNUSED;
-     fixS *fixP;
+void
+arc_adjust_symtab (void)
 {
-  arelent *reloc;
+  symbolS * sym;
 
-  reloc = (arelent *) xmalloc (sizeof (arelent));
-
-  reloc->sym_ptr_ptr = &fixP->fx_addsy->bsym;
-  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)
+  for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
     {
-      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;
+      /* 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);
     }
 
-  assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
-
-/* set addend to account for PC being advanced one insn before the target
-   address is computed, drop fx_addnumber as it is handled elsewhere  mlm */
-
-  reloc->addend = ( fixP->fx_pcrel ? -4 : 0 );
-
-  return reloc;
+  /* Now do generic ELF adjustments.  */
+  elf_adjust_symtab ();
 }
This page took 0.079551 seconds and 4 git commands to generate.