This is the mail archive of the binutils-cvs@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[binutils-gdb] Add support for ARC instruction relaxation in the assembler.


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=4670103e86f59a80259fd593a6949d693382e536

commit 4670103e86f59a80259fd593a6949d693382e536
Author: Claudiu Zissulescu <claziss@synopsys.com>
Date:   Wed Feb 10 12:09:01 2016 +0000

    Add support for ARC instruction relaxation in the assembler.
    
    gas/
    2016-01-26  Claudiu Zissulescu  <claziss@synopsys.com>
    	    Janek van Oirschot <jvanoirs@synopsys.com>
    
            * config/tc-arc.h (TC_FRAG_TYPE, TC_PCREL_ADJUST, MAX_INSN_ARGS)
            (MAX_INSN_FLGS, MAX_FLAG_NAME_LENGHT, TC_GENERIC_RELAX_TABLE):
            Define.
            (arc_flags, arc_relax_type): New structure.
            * config/tc-arc.c (FRAG_MAX_GROWTH, RELAX_TABLE_ENTRY)
    	(RELAX_TABLE_ENTRY_MAX): New define.
            (relaxation_state, md_relax_table, arc_relaxable_insns)
    	(arc_num_relaxable_ins): New variable.
    	(rlx_operand_type, arc_rlx_types): New enums.
    	(arc_relaxable_ins): New structure.
            (OPTION_RELAX): New option.
            (arc_insn): New relax member.
            (arc_flags): Remove.
            (relax_insn_p): New function.
            (apply_fixups): Likewise.
            (relaxable_operand): Likewise.
            (may_relax_expr): Likewise.
            (relaxable_flag): Likewise.
            (arc_pcrel_adjust): Likewise.
            (md_estimate_size_before_relax): Implement.
            (md_convert_frag): Likewise.
            (md_parse_option): Handle new mrelax option.
            (md_show_usage): Likewise.
            (assemble_insn): Set relax member.
            (emit_insn0): New function.
            (emit_insn1): Likewise.
            (emit_insn): Handle relaxation case.
    	* NEWS: Mention the new relaxation option.
    	* doc/c-arc.texi (ARC Options): Document new mrelax option.
    
    gas/testsuite
    2016-01-26  Claudiu Zissulescu  <claziss@synopsys.com>
    
            * gas/arc/relax-avoid1.d: New file.
            * gas/arc/relax-avoid1.s: Likewise.
            * gas/arc/relax-avoid2.d: Likewise.
            * gas/arc/relax-avoid2.s: Likewise.
            * gas/arc/relax-avoid3.d: Likewise.
            * gas/arc/relax-avoid3.s: Likewise.
    	* gas/arc/relax-b.d: Likewise.
            * gas/arc/relax-b.s: Likewise.
    
    include/opcode/
    2016-01-26  Claudiu Zissulescu  <claziss@synopsys.com>
    	    Janek van Oirschot  <jvanoirs@synopsys.com>
    
            * arc.h (arc_opcode arc_relax_opcodes, arc_num_relax_opcodes):
            Declare.
    
    opcodes/
    2016-01-26  Claudiu Zissulescu  <claziss@synopsys.com>
    	    Janek van Oirschot  <jvanoirs@synopsys.com>
    
            * arc-opc.c (arc_relax_opcodes, arc_num_relax_opcodes): New
            variable.

Diff:
---
 gas/ChangeLog                        |   42 +
 gas/NEWS                             |    2 +
 gas/config/tc-arc.c                  | 3537 +++++++++++++++++++---------------
 gas/config/tc-arc.h                  |   56 +
 gas/doc/as.texinfo                   |    1 +
 gas/doc/c-arc.texi                   |    6 +
 gas/testsuite/gas/arc/relax-avoid1.d |   13 +
 gas/testsuite/gas/arc/relax-avoid1.s |   11 +
 gas/testsuite/gas/arc/relax-avoid2.d |   14 +
 gas/testsuite/gas/arc/relax-avoid2.s |    4 +
 gas/testsuite/gas/arc/relax-avoid3.d |   14 +
 gas/testsuite/gas/arc/relax-avoid3.s |    5 +
 gas/testsuite/gas/arc/relax-b.d      |   19 +
 gas/testsuite/gas/arc/relax-b.s      |   11 +
 include/ChangeLog                    |    6 +
 include/opcode/arc.h                 |    8 +
 opcodes/ChangeLog                    |    6 +
 opcodes/arc-opc.c                    |  126 ++
 18 files changed, 2346 insertions(+), 1535 deletions(-)

diff --git a/gas/ChangeLog b/gas/ChangeLog
index 34c301d..1d6a178 100644
--- a/gas/ChangeLog
+++ b/gas/ChangeLog
@@ -1,3 +1,45 @@
+2016-02-10  Claudiu Zissulescu  <claziss@synopsys.com>
+	    Janek van Oirschot <jvanoirs@synopsys.com>
+
+	* config/tc-arc.h (TC_FRAG_TYPE, TC_PCREL_ADJUST, MAX_INSN_ARGS)
+	(MAX_INSN_FLGS, MAX_FLAG_NAME_LENGHT, TC_GENERIC_RELAX_TABLE):
+	Define.
+	(arc_flags, arc_relax_type): New structure.
+	* config/tc-arc.c (FRAG_MAX_GROWTH, RELAX_TABLE_ENTRY)
+	(RELAX_TABLE_ENTRY_MAX): New define.
+	(relaxation_state, md_relax_table, arc_relaxable_insns)
+	(arc_num_relaxable_ins): New variable.
+	(rlx_operand_type, arc_rlx_types): New enums.
+	(arc_relaxable_ins): New structure.
+	(OPTION_RELAX): New option.
+	(arc_insn): New relax member.
+	(arc_flags): Remove.
+	(relax_insn_p): New function.
+	(apply_fixups): Likewise.
+	(relaxable_operand): Likewise.
+	(may_relax_expr): Likewise.
+	(relaxable_flag): Likewise.
+	(arc_pcrel_adjust): Likewise.
+	(md_estimate_size_before_relax): Implement.
+	(md_convert_frag): Likewise.
+	(md_parse_option): Handle new mrelax option.
+	(md_show_usage): Likewise.
+	(assemble_insn): Set relax member.
+	(emit_insn0): New function.
+	(emit_insn1): Likewise.
+	(emit_insn): Handle relaxation case.
+	* NEWS: Mention the new relaxation option.
+	* doc/c-arc.texi (ARC Options): Document new mrelax option.
+	* doc/as.texinfo (Target ARC Options): Likewise.
+	* testsuite/gas/arc/relax-avoid1.d: New file.
+	* testsuite/gas/arc/relax-avoid1.s: Likewise.
+	* testsuite/gas/arc/relax-avoid2.d: Likewise.
+	* testsuite/gas/arc/relax-avoid2.s: Likewise.
+	* testsuite/gas/arc/relax-avoid3.d: Likewise.
+	* testsuite/gas/arc/relax-avoid3.s: Likewise.
+	* testsuite/gas/arc/relax-b.d: Likewise.
+	* testsuite/gas/arc/relax-b.s: Likewise.
+
 2016-02-08  Nick Clifton  <nickc@redhat.com>
 
 	* config/tc-ia64.c (dot_prologue): Fix formatting.
diff --git a/gas/NEWS b/gas/NEWS
index 7175ef0..87bdbd9 100644
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -10,6 +10,8 @@
 * New command line option -mfence-as-lock-add=yes for x86 target to encode
   lfence, mfence and sfence as "lock addl $0x0, (%[re]sp)".
 
+* Add assembly-time relaxation option for ARC cpus.
+
 Changes in 2.26:
 
 * Add a configure option --enable-compressed-debug-sections={all,gas} to
diff --git a/gas/config/tc-arc.c b/gas/config/tc-arc.c
index 20a48f7..f2d3a0e 100644
--- a/gas/config/tc-arc.c
+++ b/gas/config/tc-arc.c
@@ -31,9 +31,9 @@
 
 /* Defines section.  */
 
-#define MAX_FLAG_NAME_LENGHT 3
 #define MAX_INSN_FIXUPS      2
 #define MAX_CONSTR_STR       20
+#define FRAG_MAX_GROWTH      8
 
 #ifdef DEBUG
 # define pr_debug(fmt, args...) fprintf (stderr, fmt, ##args)
@@ -43,12 +43,51 @@
 
 #define MAJOR_OPCODE(x)  (((x) & 0xF8000000) >> 27)
 #define SUB_OPCODE(x)	 (((x) & 0x003F0000) >> 16)
-#define LP_INSN(x)	 ((MAJOR_OPCODE (x) == 0x4) &&	\
+#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
+{
+  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,
+};
+
 /* Macros section.  */
 
 #define regno(x)		((x) & 0x3F)
@@ -83,13 +122,17 @@ extern int target_big_endian;
 const char *arc_target_format = DEFAULT_TARGET_FORMAT;
 static int byte_order = DEFAULT_BYTE_ORDER;
 
+/* By default relaxation is disabled.  */
+static int relaxation_state = 0;
+
 extern int arc_get_mach (char *);
 
-/* Forward declaration.  */
+/* 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.  */
@@ -121,6 +164,7 @@ enum options
 
   OPTION_MCPU,
   OPTION_CD,
+  OPTION_RELAX,
 
   /* The following options are deprecated and provided here only for
      compatibility reasons.  */
@@ -162,6 +206,7 @@ struct option md_longopts[] =
   { "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.  */
@@ -242,6 +287,8 @@ struct arc_insn
 			     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.  */
@@ -257,6 +304,11 @@ static struct arc_last_insn
   bfd_boolean has_delay_slot;
 } arc_last_insns[2];
 
+/* 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 = ARC_OPCODE_BASE;
 static const char *arc_target_name = "<all>";
@@ -298,15 +350,6 @@ static const struct cpu_type
   { 0, 0, 0, 0, 0 }
 };
 
-struct arc_flags
-{
-  /* Name of the parsed flag.  */
-  char name[MAX_FLAG_NAME_LENGHT+1];
-
-  /* The code of the parsed flag.  Valid when is not zero.  */
-  unsigned char code;
-};
-
 /* Used by the arc_reloc_op table.  Order is important.  */
 #define O_gotoff  O_md1     /* @gotoff relocation.  */
 #define O_gotpc   O_md2     /* @gotpc relocation.  */
@@ -371,6 +414,131 @@ static const struct arc_reloc_op_tag
 static const int arc_num_reloc_op
 = sizeof (arc_reloc_op) / sizeof (*arc_reloc_op);
 
+/* Structure for relaxable instruction that have to be swapped with a
+   smaller alternative instruction.  */
+struct arc_relaxable_ins
+{
+  /* Mnemonic that should be checked.  */
+  const char *mnemonic_r;
+
+  /* Operands that should be checked.
+     Indexes of operands from operand array.  */
+  enum rlx_operand_type operands[6];
+
+  /* Flags that should be checked.  */
+  unsigned flag_classes[5];
+
+  /* Mnemonic (smaller) alternative to be used later for relaxation.  */
+  const char *mnemonic_alt;
+
+  /* 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[] =
+{
+  /* 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 },
+};
+
+const unsigned arc_num_relaxable_ins = ARRAY_SIZE (arc_relaxable_insns);
+
 /* Flags to set in the elf header.  */
 static flagword arc_eflag = 0x00;
 
@@ -380,33 +548,6 @@ symbolS * GOT_symbol = 0;
 /* Set to TRUE when we assemble instructions.  */
 static bfd_boolean assembling_insn = FALSE;
 
-/* Functions declaration.  */
-
-static void assemble_tokens (const char *, expressionS *, int,
-			     struct arc_flags *, int);
-static const struct arc_opcode *find_opcode_match (const struct arc_opcode *,
-						   expressionS *, int *,
-						   struct arc_flags *,
-						   int, int *);
-static void assemble_insn (const struct arc_opcode *, const expressionS *,
-			   int, const struct arc_flags *, int,
-			   struct arc_insn *);
-static void emit_insn (struct arc_insn *);
-static unsigned insert_operand (unsigned, const struct arc_operand *,
-				offsetT, char *, unsigned);
-static const struct arc_opcode *find_special_case_flag (const char *,
-							int *,
-							struct arc_flags *);
-static const struct arc_opcode *find_special_case (const char *,
-						   int *,
-						   struct arc_flags *,
-						   expressionS *, int *);
-static const struct arc_opcode *find_special_case_pseudo (const char *,
-							  int *,
-							  expressionS *,
-							  int *,
-							  struct arc_flags *);
-
 /* Functions implementation.  */
 
 /* Like md_number_to_chars but used for limms.  The 4-byte limm value,
@@ -916,1642 +1057,1926 @@ tokenize_flags (const char *str,
   return -1;
 }
 
-/* The public interface to the instruction assembler.  */
+/* Apply the fixups in order.  */
 
-void
-md_assemble (char *str)
+static void
+apply_fixups (struct arc_insn *insn, fragS *fragP, int fix)
 {
-  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;
-    }
-
-  /* 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;
+  int i;
 
-  /* Tokenize the rest of the line.  */
-  if ((ntok = tokenize_arguments (str, tok, MAX_INSN_ARGS)) < 0)
+  for (i = 0; i < insn->nfixups; i++)
     {
-      as_bad (_("syntax error"));
-      return;
-    }
+      struct arc_fixup *fixup = &insn->fixups[i];
+      int size, pcrel, offset = 0;
 
-  /* Finish it off.  */
-  assemble_tokens (opname, tok, ntok, flags, nflg);
-  assembling_insn = FALSE;
-}
+      /* 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;
 
-/* Callback to insert a register into the hash table.  */
+      if (fixup->islong)
+	offset = (insn->short_insn) ? 2 : 4;
 
-static void
-declare_register (char *name, int number)
-{
-  const char *err;
-  symbolS *regS = symbol_create (name, reg_section,
-				 number, &zero_address_frag);
+      /* Some fixups are only used internally, thus no howto.  */
+      if ((int) fixup->reloc == 0)
+	as_fatal (_("Unhandled reloc type"));
 
-  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);
-}
+      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);
 
-/* Construct symbols for each of the general registers.  */
+	  /* 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;
+	}
 
-static void
-declare_register_set (void)
-{
-  int i;
-  for (i = 0; i < 64; ++i)
-    {
-      char name[7];
+      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);
 
-      sprintf (name, "r%d", i);
-      declare_register (name, i);
-      if ((i & 0x01) == 0)
+      /* Check for ZOLs, and update symbol info if any.  */
+      if (LP_INSN (insn->insn))
 	{
-	  sprintf (name, "r%dr%d", i, i+1);
-	  declare_register (name, i);
+	  gas_assert (fixup->exp.X_add_symbol);
+	  ARC_SET_FLAG (fixup->exp.X_add_symbol, ARC_FLAG_ZOL);
 	}
     }
 }
 
-/* Port-specific assembler initialization.  This function is called
-   once, at assembler startup time.  */
+/* Actually output an instruction with its fixup.  */
 
-void
-md_begin (void)
+static void
+emit_insn0 (struct arc_insn *insn, char *where, bfd_boolean relax)
 {
-  unsigned int i;
-
-  /* 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);
+  char *f = where;
 
-  /* Set up a hash table for the instructions.  */
-  arc_opcode_hash = hash_new ();
-  if (arc_opcode_hash == NULL)
-    as_fatal (_("Virtual memory exhausted"));
+  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);
 
-  /* Initialize the hash table with the insns.  */
-  for (i = 0; i < arc_num_opcodes;)
+  /* Write out the instruction.  */
+  if (insn->short_insn)
     {
-      const char *name, *retval;
-
-      name = arc_opcodes[i].name;
-      retval = hash_insert (arc_opcode_hash, name, (void *) &arc_opcodes[i]);
-      if (retval)
-	as_fatal (_("internal error: can't hash opcode '%s': %s"),
-		  name, retval);
-
-      while (++i < arc_num_opcodes
-	     && (arc_opcodes[i].name == name
-		 || !strcmp (arc_opcodes[i].name, name)))
-	continue;
+      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);
+	}
     }
 
-  /* Register declaration.  */
-  arc_reg_hash = hash_new ();
-  if (arc_reg_hash == NULL)
-    as_fatal (_("Virtual memory exhausted"));
+  if (!relax)
+    apply_fixups (insn, frag_now, (f - frag_now->fr_literal));
+}
 
-  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);
+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.  */
 
-  declare_register ("mlo", 57);
-  declare_register ("mmid", 58);
-  declare_register ("mhi", 59);
+      struct arc_relax_type relax_info_copy;
+      relax_substateT subtype = frag_now->fr_subtype;
 
-  declare_register ("acc1", 56);
-  declare_register ("acc2", 57);
+      memcpy (&relax_info_copy, &frag_now->tc_frag_data,
+	      sizeof (struct arc_relax_type));
 
-  declare_register ("lp_count", 60);
-  declare_register ("pcl", 63);
+      frag_wane (frag_now);
+      frag_grow (FRAG_MAX_GROWTH);
 
-  /* Initialize the last instructions.  */
-  memset (&arc_last_insns[0], 0, sizeof (arc_last_insns));
-}
+      memcpy (&frag_now->tc_frag_data, &relax_info_copy,
+	      sizeof (struct arc_relax_type));
 
-/* Write a value out to the object file, using the appropriate
-   endianness.  */
+      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);
+}
 
-void
-md_number_to_chars (char *buf,
-		    valueT val,
-		    int n)
+static void
+emit_insn (struct arc_insn *insn)
 {
-  if (target_big_endian)
-    number_to_chars_bigendian (buf, val, n);
+  if (insn->relax)
+    emit_insn1 (insn);
   else
-    number_to_chars_littleendian (buf, val, n);
+    emit_insn0 (insn, NULL, FALSE);
 }
 
-/* Round up a section size to the appropriate boundary.  */
+/* Check whether a symbol involves a register.  */
 
-valueT
-md_section_align (segT segment,
-		  valueT size)
+static bfd_boolean
+contains_register (symbolS *sym)
 {
-  int align = bfd_get_section_alignment (stdoutput, segment);
+  if (sym)
+    {
+      expressionS *ex = symbol_get_value_expression (sym);
 
-  return ((size + (1 << align) - 1) & (-((valueT) 1 << align)));
+      return ((O_register == ex->X_op)
+	      && !contains_register (ex->X_add_symbol)
+	      && !contains_register (ex->X_op_symbol));
+    }
+
+  return FALSE;
 }
 
-/* The location from which a PC relative jump should be calculated,
-   given a PC relative reloc.  */
+/* Returns the register number within a symbol.  */
 
-long
-md_pcrel_from_section (fixS *fixP,
-		       segT sec)
+static int
+get_register (symbolS *sym)
 {
-  offsetT base = fixP->fx_where + fixP->fx_frag->fr_address;
-
-  pr_debug ("pcrel_from_section, fx_offset = %d\n", (int) fixP->fx_offset);
+  if (!contains_register (sym))
+    return -1;
 
-  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));
+  expressionS *ex = symbol_get_value_expression (sym);
+  return regno (ex->X_add_number);
+}
 
-      /* The symbol is undefined (or is defined but not in this section).
-	 Let the linker figure it out.  */
-      return 0;
-    }
+/* 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.  */
 
-  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:
+static bfd_boolean
+generic_reloc_p (extended_bfd_reloc_code_real_type reloc)
+{
+  if (!reloc)
+    return FALSE;
 
-	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;
-	}
+  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;
     }
-
-  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.  */
+/* Allocates a tok entry.  */
 
-static const struct arc_operand *
-find_operand_for_reloc (extended_bfd_reloc_code_real_type reloc)
+static int
+allocate_tok (expressionS *tok, int ntok, int cidx)
 {
-  unsigned i;
+  if (ntok > MAX_INSN_ARGS - 2)
+    return 0; /* No space left.  */
 
-  for (i = 0; i < arc_num_operands; i++)
-    if (arc_operands[i].default_reloc == reloc)
-      return  &arc_operands[i];
-  return NULL;
-}
+  if (cidx > ntok)
+    return 0; /* Incorect args.  */
 
-/* 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.  */
+  memcpy (&tok[ntok+1], &tok[ntok], sizeof (*tok));
 
-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;
+  if (cidx == ntok)
+    return 1; /* Success.  */
+  return allocate_tok (tok, ntok - 1, cidx);
+}
 
-  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);
+/* Search forward through all variants of an opcode looking for a
+   syntax match.  */
 
-  fx_addsy = fixP->fx_addsy;
-  fx_subsy = fixP->fx_subsy;
-  fx_offset = 0;
+static const struct arc_opcode *
+find_opcode_match (const struct arc_opcode *first_opcode,
+		   expressionS *tok,
+		   int *pntok,
+		   struct arc_flags *first_pflag,
+		   int nflgs,
+		   int *pcpumatch)
+{
+  const struct arc_opcode *opcode = first_opcode;
+  int ntok = *pntok;
+  int got_cpu_match = 0;
+  expressionS bktok[MAX_INSN_ARGS];
+  int bkntok;
+  expressionS emptyE;
 
-  if (fx_addsy)
-    {
-      add_symbol_segment = S_GET_SEGMENT (fx_addsy);
-    }
+  memset (&emptyE, 0, sizeof (emptyE));
+  memcpy (bktok, tok, MAX_INSN_ARGS * sizeof (*tok));
+  bkntok = ntok;
 
-  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)
+  do
     {
-      resolve_symbol_value (fx_subsy);
-      sub_symbol_segment = S_GET_SEGMENT (fx_subsy);
+      const unsigned char *opidx;
+      const unsigned char *flgidx;
+      int tokidx = 0;
+      const expressionS *t = &emptyE;
 
-      if (sub_symbol_segment == absolute_section)
-	{
-	  /* The symbol is really a constant.  */
-	  fx_offset -= S_GET_VALUE (fx_subsy);
-	  fx_subsy = NULL;
-	}
-      else
-	{
-	  as_bad_where (fixP->fx_file, fixP->fx_line,
-			_("can't resolve `%s' {%s section} - `%s' {%s section}"),
-			fx_addsy ? S_GET_NAME (fx_addsy) : "0",
-			segment_name (add_symbol_segment),
-			S_GET_NAME (fx_subsy),
-			segment_name (sub_symbol_segment));
-	  return;
-	}
-    }
+      pr_debug ("%s:%d: find_opcode_match: trying opcode 0x%08X ",
+		frag_now->fr_file, frag_now->fr_line, opcode->opcode);
 
-  if (fx_addsy
-      && !S_IS_WEAK (fx_addsy))
-    {
-      if (add_symbol_segment == seg
-	  && fixP->fx_pcrel)
-	{
-	  value += S_GET_VALUE (fx_addsy);
-	  value -= md_pcrel_from_section (fixP, seg);
-	  fx_addsy = NULL;
-	  fixP->fx_pcrel = FALSE;
-	}
-      else if (add_symbol_segment == absolute_section)
-	{
-	  value = fixP->fx_offset;
-	  fx_offset += S_GET_VALUE (fixP->fx_addsy);
-	  fx_addsy = NULL;
-	  fixP->fx_pcrel = FALSE;
-	}
-    }
+      /* Don't match opcodes that don't exist on this
+	 architecture.  */
+      if (!(opcode->cpu & arc_target))
+	goto match_failed;
 
-  if (!fx_addsy)
-    fixP->fx_done = TRUE;
+      if (is_code_density_p (opcode) && !(arc_features & ARC_CD))
+	goto match_failed;
 
-  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);
+      got_cpu_match = 1;
+      pr_debug ("cpu ");
 
-      switch (fixP->fx_r_type)
+      /* Check the operands.  */
+      for (opidx = opcode->operands; *opidx; ++opidx)
 	{
-	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;
-	}
-    }
+	  const struct arc_operand *operand = &arc_operands[*opidx];
 
-  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);
+	  /* 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;
 
-  /* 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);
+	  /* 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;
+		}
+
+	      /* Special handling?  */
+	      if (operand->insert)
+		{
+		  const char *errmsg = NULL;
+		  (*operand->insert)(0,
+				     regno (tok[tokidx].X_add_number),
+				     &errmsg);
+		  if (errmsg)
+		    {
+		      if (operand->flags & ARC_OPERAND_IGNORE)
+			{
+			  /* Missing argument, create one.  */
+			  if (!allocate_tok (tok, ntok - 1, tokidx))
+			    goto match_failed;
+
+			  tok[tokidx].X_op = O_absent;
+			  ++ntok;
+			}
+		      else
+			goto match_failed;
+		    }
+		}
+
+	      t = &tok[tokidx];
+	      break;
+
+	    case ARC_OPERAND_BRAKET:
+	      /* Check if bracket is also in opcode table as
+		 operand.  */
+	      if (tok[tokidx].X_op != O_bracket)
+		goto match_failed;
+	      break;
+
+	    case ARC_OPERAND_LIMM:
+	    case ARC_OPERAND_SIGNED:
+	    case ARC_OPERAND_UNSIGNED:
+	      switch (tok[tokidx].X_op)
+		{
+		case O_illegal:
+		case O_absent:
+		case O_register:
+		  goto match_failed;
+
+		case O_bracket:
+		  /* Got an (too) early bracket, check if it is an
+		     ignored operand.  N.B. This procedure works only
+		     when bracket is the last operand!  */
+		  if (!(operand->flags & ARC_OPERAND_IGNORE))
+		    goto match_failed;
+		  /* Insert the missing operand.  */
+		  if (!allocate_tok (tok, ntok - 1, tokidx))
+		    goto match_failed;
+
+		  tok[tokidx].X_op = O_absent;
+		  ++ntok;
+		  break;
+
+		case O_constant:
+		  /* Check the range.  */
+		  if (operand->bits != 32
+		      && !(operand->flags & ARC_OPERAND_NCHK))
+		    {
+		      offsetT min, max, val;
+		      val = tok[tokidx].X_add_number;
+
+		      if (operand->flags & ARC_OPERAND_SIGNED)
+			{
+			  max = (1 << (operand->bits - 1)) - 1;
+			  min = -(1 << (operand->bits - 1));
+			}
+		      else
+			{
+			  max = (1 << operand->bits) - 1;
+			  min = 0;
+			}
+
+		      if (val < min || val > max)
+			goto match_failed;
+
+		      /* Check alignmets.  */
+		      if ((operand->flags & ARC_OPERAND_ALIGNED32)
+			  && (val & 0x03))
+			goto match_failed;
+
+		      if ((operand->flags & ARC_OPERAND_ALIGNED16)
+			  && (val & 0x01))
+			goto match_failed;
+		    }
+		  else if (operand->flags & ARC_OPERAND_NCHK)
+		    {
+		      if (operand->insert)
+			{
+			  const char *errmsg = NULL;
+			  (*operand->insert)(0,
+					     tok[tokidx].X_add_number,
+					     &errmsg);
+			  if (errmsg)
+			    goto match_failed;
+			}
+		      else
+			goto match_failed;
+		    }
+		  break;
+
+		case O_subtract:
+		  /* Check if it is register range.  */
+		  if ((tok[tokidx].X_add_number == 0)
+		      && contains_register (tok[tokidx].X_add_symbol)
+		      && contains_register (tok[tokidx].X_op_symbol))
+		    {
+		      int regs;
+
+		      regs = get_register (tok[tokidx].X_add_symbol);
+		      regs <<= 16;
+		      regs |= get_register (tok[tokidx].X_op_symbol);
+		      if (operand->insert)
+			{
+			  const char *errmsg = NULL;
+			  (*operand->insert)(0,
+					     regs,
+					     &errmsg);
+			  if (errmsg)
+			    goto match_failed;
+			}
+		      else
+			goto match_failed;
+		      break;
+		    }
+		default:
+		  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;
+		}
+	      /* 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 ");
+
+      /* Check the flags.  Iterate over the valid flag classes.  */
+      int lnflg = nflgs;
+
+      for (flgidx = opcode->flags; *flgidx && lnflg; ++flgidx)
+	{
+	  /* Get a valid flag class.  */
+	  const struct arc_flag_class *cl_flags = &arc_flag_classes[*flgidx];
+	  const unsigned *flgopridx;
+
+	  for (flgopridx = cl_flags->flags; *flgopridx; ++flgopridx)
+	    {
+	      const struct arc_flag_operand *flg_operand;
+	      struct arc_flags *pflag = first_pflag;
+	      int i;
+
+	      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))
+		    {
+		      /*TODO: Check if it is duplicated.  */
+		      pflag->code = *flgopridx;
+		      lnflg--;
+		      break; /* goto next flag class and parsed flag.  */
+		    }
+		}
+	    }
+	}
+      /* 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;
+    }
+  while (++opcode - arc_opcodes < (int) arc_num_opcodes
+	 && !strcmp (opcode->name, first_opcode->name));
+
+  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 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);
+    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 BFD_RELOC_ARC_TLS_LE_S9:
-    case BFD_RELOC_ARC_TLS_DTPOFF_S9:
-      as_bad (_("TLS_*_S9 relocs are not supported yet"));
+    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;
+}
 
-  if (!fixP->fx_done)
+/* 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)
     {
-      return;
+      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;
+}
 
-  /* Addjust the value if we have a constant.  */
-  value += fx_offset;
+/* Assumes the expressionS *tok is of sufficient size.  */
 
-  /* 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);
+static const struct arc_opcode *
+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];
 
-  reloc = fixP->fx_r_type;
-  switch (reloc)
+  /* 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)
     {
-    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;
+      operand_pseudo = &pseudo_insn->operand[i];
+      operand_real = &arc_operands[operand_pseudo->operand_idx];
 
-    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;
+      if (operand_real->flags & ARC_OPERAND_BRAKET &&
+	  !operand_pseudo->needs_insert)
+	continue;
 
-    case BFD_RELOC_ARC_TLS_DTPOFF:
-    case BFD_RELOC_ARC_TLS_LE_32:
-      gas_assert (!fixP->fx_addsy);
-      gas_assert (!fixP->fx_subsy);
+      /* Has to be inserted (i.e. this token does not exist yet).  */
+      if (operand_pseudo->needs_insert)
+	{
+	  if (operand_real->flags & ARC_OPERAND_BRAKET)
+	    {
+	      tok[i].X_op = O_bracket;
+	      ++(*ntok);
+	      continue;
+	    }
 
-    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;
+	  /* 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);
 
-    case BFD_RELOC_ARC_PLT32:
-      md_number_to_chars_midend (fixpos, value, fixP->fx_size);
-      return;
+	  tokenize_arguments (construct_operand, &tok[i], 1);
+	  ++(*ntok);
+	}
+
+      else if (operand_pseudo->count)
+	{
+	  /* Operand number has to be adjusted accordingly (by operand
+	     type).  */
+	  switch (tok[i].X_op)
+	    {
+	    case O_constant:
+	      tok[i].X_add_number += operand_pseudo->count;
+	      break;
+
+	    case O_symbol:
+	      break;
+
+	    default:
+	      /* Ignored.  */
+	      break;
+	    }
+	}
+    }
+
+  /* Swap operands if necessary.  Only supports one swap at the
+     moment.  */
+  for (i = 0; i < pseudo_insn->operand_cnt; ++i)
+    {
+      operand_pseudo = &pseudo_insn->operand[i];
+
+      if (operand_pseudo->swap_operand_idx == i)
+	continue;
+
+      swap_operand (tok, i, operand_pseudo->swap_operand_idx);
+
+      /* Prevent a swap back later by breaking out.  */
+      break;
+    }
+
+  return (const struct arc_opcode *)
+    hash_find (arc_opcode_hash,	pseudo_insn->mnemonic_r);
+}
+
+static const struct arc_opcode *
+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 *opcode;
+
+  /* Search for special case instruction.  */
+  for (i = 0; i < arc_num_flag_special; i++)
+    {
+      arc_flag_special_opcode = &arc_flag_special_cases[i];
+      oplen = strlen (arc_flag_special_opcode->name);
+
+      if (strncmp (opname, arc_flag_special_opcode->name, oplen) != 0)
+	continue;
+
+      /* Found a potential special case instruction, now test for
+	 flags.  */
+      for (flag_arr_idx = 0;; ++flag_arr_idx)
+	{
+	  flag_idx = arc_flag_special_opcode->flags[flag_arr_idx];
+	  if (flag_idx == 0)
+	    break;  /* End of array, nothing found.  */
 
-    case BFD_RELOC_ARC_S25H_PCREL_PLT:
-      reloc = BFD_RELOC_ARC_S25W_PCREL;
-      goto solve_plt;
+	  flagnm = arc_flag_operands[flag_idx].name;
+	  flaglen = strlen (flagnm);
+	  if (strcmp (opname + oplen, flagnm) == 0)
+	    {
+	      opcode = (const struct arc_opcode *)
+		hash_find (arc_opcode_hash,
+			   arc_flag_special_opcode->name);
 
-    case BFD_RELOC_ARC_S21H_PCREL_PLT:
-      reloc = BFD_RELOC_ARC_S21H_PCREL;
-      goto solve_plt;
+	      if (*nflgs + 1 > MAX_INSN_FLGS)
+		break;
+	      memcpy (pflags[*nflgs].name, flagnm, flaglen);
+	      pflags[*nflgs].name[flaglen] = '\0';
+	      (*nflgs)++;
+	      return opcode;
+	    }
+	}
+    }
+  return NULL;
+}
 
-    case BFD_RELOC_ARC_S25W_PCREL_PLT:
-      reloc = BFD_RELOC_ARC_S25W_PCREL;
-      goto solve_plt;
+/* Used to find special case opcode.  */
 
-    case BFD_RELOC_ARC_S21W_PCREL_PLT:
-      reloc = BFD_RELOC_ARC_S21W_PCREL;
+static const struct arc_opcode *
+find_special_case (const char *opname,
+		   int *nflgs,
+		   struct arc_flags *pflags,
+		   expressionS *tok,
+		   int *ntok)
+{
+  const struct arc_opcode *opcode;
 
-    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;
+  opcode = find_special_case_pseudo (opname, ntok, tok, nflgs, pflags);
 
-    default:
-      {
-	if ((int) fixP->fx_r_type >= 0)
-	  as_fatal (_("unhandled relocation type %s"),
-		    bfd_get_reloc_code_name (fixP->fx_r_type));
+  if (opcode == NULL)
+    opcode = find_special_case_flag (opname, nflgs, pflags);
 
-	/* 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"));
+  return opcode;
+}
 
-	gas_assert (-(int) fixP->fx_r_type < (int) arc_num_operands);
-	operand = &arc_operands[-(int) fixP->fx_r_type];
-	break;
-      }
-    }
+static void
+preprocess_operands (const struct arc_opcode *opcode,
+		     expressionS *tok,
+		     int ntok)
+{
+  int i;
+  size_t len;
+  const char *p;
+  unsigned j;
+  const struct arc_aux_reg *auxr;
 
-  if (target_big_endian)
+  for (i = 0; i < ntok; i++)
     {
-      switch (fixP->fx_size)
+      switch (tok[i].X_op)
 	{
-	case 4:
-	  insn = bfd_getb32 (fixpos);
-	  break;
-	case 2:
-	  insn = bfd_getb16 (fixpos);
+	case O_illegal:
+	case O_absent:
+	  break; /* Throw and error.  */
+
+	case O_symbol:
+	  if (opcode->class != AUXREG)
+	    break;
+	  /* Convert the symbol to a constant if possible.  */
+	  p = S_GET_NAME (tok[i].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)
+	      {
+		tok[i].X_op = O_constant;
+		tok[i].X_add_number = auxr->address;
+		break;
+	      }
 	  break;
 	default:
-	  as_bad_where (fixP->fx_file, fixP->fx_line,
-			_("unknown fixup size"));
-	}
-    }
-  else
-    {
-      insn = 0;
-      switch (fixP->fx_size)
-	{
-	case 4:
-	  insn = bfd_getl16 (fixpos) << 16 | bfd_getl16 (fixpos + 2);
-	  break;
-	case 2:
-	  insn = bfd_getl16 (fixpos);
 	  break;
-	default:
-	  as_bad_where (fixP->fx_file, fixP->fx_line,
-			_("unknown fixup size"));
 	}
     }
-
-  insn = insert_operand (insn, operand, (offsetT) value,
-			 fixP->fx_file, fixP->fx_line);
-
-  md_number_to_chars_midend (fixpos, insn, fixP->fx_size);
 }
 
-/* Prepare machine-dependent frags for relaxation.
+/* Given an opcode name, pre-tockenized set of argumenst and the
+   opcode flags, take it all the way through emission.  */
 
-   Called just before relaxation starts.  Any symbol that is now undefined
-   will not become defined.
+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 *opcode;
+  int cpumatch = 1;
 
-   Return the correct fr_subtype in the frag.
+  /* Search opcodes.  */
+  opcode = (const struct arc_opcode *) hash_find (arc_opcode_hash, opname);
 
-   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.
+  /* Couldn't find opcode conventional way, try special cases.  */
+  if (!opcode)
+    opcode = find_special_case (opname, &nflgs, pflags, tok, &ntok);
 
-   Although it may not be explicit in the frag, pretend
-   fr_var starts with a value.  */
+  if (opcode)
+    {
+      pr_debug ("%s:%d: assemble_tokens: %s trying opcode 0x%08X\n",
+		frag_now->fr_file, frag_now->fr_line, opcode->name,
+		opcode->opcode);
 
-int
-md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED,
-			       segT segment ATTRIBUTE_UNUSED)
-{
-  int growth = 4;
+      preprocess_operands (opcode, tok, ntok);
 
-  fragP->fr_var = 4;
-  pr_debug ("%s:%d: md_estimate_size_before_relax: %d\n",
-	   fragP->fr_file, fragP->fr_line, growth);
+      found_something = TRUE;
+      opcode = find_opcode_match (opcode, tok, &ntok, pflags, nflgs, &cpumatch);
+      if (opcode)
+	{
+	  struct arc_insn insn;
+	  assemble_insn (opcode, tok, ntok, pflags, nflgs, &insn);
+	  emit_insn (&insn);
+	  return;
+	}
+    }
 
-  as_fatal (_("md_estimate_size_before_relax\n"));
-  return growth;
+  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 (_("unknown opcode '%s'"), opname);
 }
 
-/* Translate internal representation of relocation info to BFD target
-   format.  */
+/* The public interface to the instruction assembler.  */
 
-arelent *
-tc_gen_reloc (asection *section ATTRIBUTE_UNUSED,
-	      fixS *fixP)
+void
+md_assemble (char *str)
 {
-  arelent *reloc;
-  bfd_reloc_code_real_type code;
+  char *opname;
+  expressionS tok[MAX_INSN_ARGS];
+  int ntok, nflg;
+  size_t opnamelen;
+  struct arc_flags flags[MAX_INSN_FLGS];
 
-  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;
+  /* Split off the opcode.  */
+  opnamelen = strspn (str, "abcdefghijklmnopqrstuvwxyz_0123468");
+  opname = xmalloc (opnamelen + 1);
+  memcpy (opname, str, opnamelen);
+  opname[opnamelen] = '\0';
 
-  /* 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);
+  /* Signalize we are assmbling the instructions.  */
+  assembling_insn = TRUE;
 
-  code = fixP->fx_r_type;
+  /* Tokenize the flags.  */
+  if ((nflg = tokenize_flags (str + opnamelen, flags, MAX_INSN_FLGS)) == -1)
+    {
+      as_bad (_("syntax error"));
+      return;
+    }
 
-  /* 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;
+  /* 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;
 
-  reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
-  if (reloc->howto == NULL)
+  /* Tokenize the rest of the line.  */
+  if ((ntok = tokenize_arguments (str, tok, MAX_INSN_ARGS)) < 0)
     {
-      as_bad_where (fixP->fx_file, fixP->fx_line,
-		    _("cannot represent `%s' relocation in object file"),
-		    bfd_get_reloc_code_name (code));
-      return NULL;
+      as_bad (_("syntax error"));
+      return;
     }
 
-  if (!fixP->fx_pcrel != !reloc->howto->pc_relative)
-    as_fatal (_("internal error? cannot generate `%s' relocation"),
-	      bfd_get_reloc_code_name (code));
+  /* 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 (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);
+}
 
-  gas_assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
+/* Construct symbols for each of the general registers.  */
 
-  if (code == BFD_RELOC_ARC_TLS_DTPOFF
-      || code ==  BFD_RELOC_ARC_TLS_DTPOFF_S9)
+static void
+declare_register_set (void)
+{
+  int i;
+  for (i = 0; i < 64; ++i)
     {
-      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)
+      char name[7];
+
+      sprintf (name, "r%d", i);
+      declare_register (name, i);
+      if ((i & 0x01) == 0)
 	{
-	  as_bad ("Can't store pointer\n");
-	  return NULL;
+	  sprintf (name, "r%dr%d", i, i+1);
+	  declare_register (name, i);
 	}
     }
-  else
-    reloc->addend = fixP->fx_offset;
-
-  return reloc;
 }
 
-/* Perform post-processing of machine-dependent frags after relaxation.
-   Called after relaxation is finished.
-   In:	Address of frag.
-   fr_type == rs_machine_dependent.
-   fr_subtype is what the address relaxed to.
-
-   Out: Any fixS:s and constants are set up.  */
+/* Port-specific assembler initialization.  This function is called
+   once, at assembler startup time.  */
 
 void
-md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
-		 segT segment ATTRIBUTE_UNUSED,
-		 fragS *fragP ATTRIBUTE_UNUSED)
+md_begin (void)
 {
-  pr_debug ("%s:%d: md_convert_frag, subtype: %d, fix: %d, var: %d\n",
-	    fragP->fr_file, fragP->fr_line,
-	    fragP->fr_subtype, fragP->fr_fix, fragP->fr_var);
-  abort ();
-}
+  unsigned int i;
 
-/* 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.  */
+  /* The endianness can be chosen "at the factory".  */
+  target_big_endian = byte_order == BIG_ENDIAN;
 
-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 (!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;)
     {
-      if (!GOT_symbol)
-	{
-	  if (symbol_find (name))
-	    as_bad ("GOT already in symbol table");
+      const char *name, *retval;
 
-	  GOT_symbol = symbol_new (GLOBAL_OFFSET_TABLE_NAME, undefined_section,
-				   (valueT) 0, &zero_address_frag);
-	};
-      return GOT_symbol;
+      name = arc_opcodes[i].name;
+      retval = hash_insert (arc_opcode_hash, name, (void *) &arc_opcodes[i]);
+      if (retval)
+	as_fatal (_("internal error: can't hash opcode '%s': %s"),
+		  name, retval);
+
+      while (++i < arc_num_opcodes
+	     && (arc_opcodes[i].name == name
+		 || !strcmp (arc_opcodes[i].name, name)))
+	continue;
     }
-  return NULL;
+
+  /* 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));
 }
 
-/* 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.  */
+/* Write a value out to the object file, using the appropriate
+   endianness.  */
 
-char *
-md_atof (int type, char *litP, int *sizeP)
+void
+md_number_to_chars (char *buf,
+		    valueT val,
+		    int n)
 {
-  return ieee_md_atof (type, litP, sizeP, target_big_endian);
+  if (target_big_endian)
+    number_to_chars_bigendian (buf, val, n);
+  else
+    number_to_chars_littleendian (buf, val, n);
 }
 
-/* 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.  */
+/* Round up a section size to the appropriate boundary.  */
 
-void
-md_operand (expressionS *expressionP ATTRIBUTE_UNUSED)
+valueT
+md_section_align (segT segment,
+		  valueT size)
 {
-  char *p = input_line_pointer;
-  if (*p == '@')
-    {
-      input_line_pointer++;
-      expressionP->X_op = O_symbol;
-      expression (expressionP);
-    }
+  int align = bfd_get_section_alignment (stdoutput, segment);
+
+  return ((size + (1 << align) - 1) & (-((valueT) 1 << align)));
 }
 
-/* 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.  */
+/* The location from which a PC relative jump should be calculated,
+   given a PC relative reloc.  */
 
-bfd_boolean
-arc_parse_name (const char *name,
-		struct expressionS *e)
+long
+md_pcrel_from_section (fixS *fixP,
+		       segT sec)
 {
-  struct symbol *sym;
+  offsetT base = fixP->fx_where + fixP->fx_frag->fr_address;
 
-  if (!assembling_insn)
-    return FALSE;
+  pr_debug ("pcrel_from_section, fx_offset = %d\n", (int) fixP->fx_offset);
 
-  /* Handle only registers.  */
-  if (e->X_op != O_absent)
-    return FALSE;
+  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));
 
-  sym = hash_find (arc_reg_hash, name);
-  if (sym)
+      /* 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)
     {
-      e->X_op = O_register;
-      e->X_add_number = S_GET_VALUE (sym);
-      return TRUE;
+      /* These are the "internal" relocations.  Align them to
+	 32 bit boundary (PCL), for the moment.  */
+      base &= ~3;
     }
-  return FALSE;
+  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;
+	}
+    }
+
+  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;
 }
 
-/* md_parse_option
-   Invocation line includes a switch not recognized by the base assembler.
-   See if it's a processor-specific option.
+/* Given a BFD relocation find the coresponding operand.  */
 
-   New options (supported) are:
+static const struct arc_operand *
+find_operand_for_reloc (extended_bfd_reloc_code_real_type reloc)
+{
+  unsigned i;
 
-   -mcpu=<cpu name>		 Assemble for selected processor
-   -EB/-mbig-endian		 Big-endian
-   -EL/-mlittle-endian		 Little-endian
+  for (i = 0; i < arc_num_operands; i++)
+    if (arc_operands[i].default_reloc == reloc)
+      return  &arc_operands[i];
+  return NULL;
+}
 
-   The following CPU names are recognized:
-   arc700, av2em, av2hs.  */
+/* Insert an operand value into an instruction.  */
 
-int
-md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+static unsigned
+insert_operand (unsigned insn,
+		const struct arc_operand *operand,
+		offsetT val,
+		char *file,
+		unsigned line)
 {
-  int cpu_flags = EF_ARC_CPU_GENERIC;
+  offsetT min = 0, max = 0;
 
-  switch (c)
+  if (operand->bits != 32
+      && !(operand->flags & ARC_OPERAND_NCHK)
+      && !(operand->flags & ARC_OPERAND_FAKE))
     {
-    case OPTION_ARC600:
-    case OPTION_ARC601:
-      return md_parse_option (OPTION_MCPU, "arc600");
+      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;
+	}
 
-    case OPTION_ARC700:
-      return md_parse_option (OPTION_MCPU, "arc700");
+      if (val < min || val > max)
+	as_bad_value_out_of_range (_("operand"),
+				   val, min, max, file, line);
+    }
 
-    case OPTION_ARCEM:
-      return md_parse_option (OPTION_MCPU, "arcem");
+  pr_debug ("insert field: %ld <= %ld <= %ld in 0x%08x\n",
+	    min, val, max, insn);
 
-    case OPTION_ARCHS:
-      return md_parse_option (OPTION_MCPU, "archs");
+  if ((operand->flags & ARC_OPERAND_ALIGNED32)
+      && (val & 0x03))
+    as_bad_where (file, line,
+		  _("Unaligned operand. Needs to be 32bit aligned"));
 
-    case OPTION_MCPU:
-      {
-	int i;
-	char *s = alloca (strlen (arg) + 1);
+  if ((operand->flags & ARC_OPERAND_ALIGNED16)
+      && (val & 0x01))
+    as_bad_where (file, line,
+		  _("Unaligned operand. Needs to be 16bit aligned"));
 
-	{
-	  char *t = s;
-	  char *arg1 = arg;
+  if (operand->insert)
+    {
+      const char *errmsg = NULL;
 
-	  do
-	    *t = TOLOWER (*arg1++);
-	  while (*t++);
+      insn = (*operand->insert) (insn, val, &errmsg);
+      if (errmsg)
+	as_warn_where (file, line, "%s", errmsg);
+    }
+  else
+    {
+      if (operand->flags & ARC_OPERAND_TRUNCATE)
+	{
+	  if (operand->flags & ARC_OPERAND_ALIGNED32)
+	    val >>= 2;
+	  if (operand->flags & ARC_OPERAND_ALIGNED16)
+	    val >>= 1;
 	}
+      insn |= ((val & ((1 << operand->bits) - 1)) << operand->shift);
+    }
+  return insn;
+}
 
-	for (i = 0; cpu_types[i].name; ++i)
-	  {
-	    if (!strcmp (cpu_types[i].name, s))
-	      {
-		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;
-
-		mach_type_specified_p = 1;
-		break;
-	      }
-	  }
-
-	if (!cpu_types[i].name)
-	  {
-	    as_fatal (_("unknown architecture: %s\n"), arg);
-	  }
-	break;
-      }
-
-    case OPTION_EB:
-      arc_target_format = "elf32-bigarc";
-      byte_order = BIG_ENDIAN;
-      break;
+/* 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.  */
 
-    case OPTION_EL:
-      arc_target_format = "elf32-littlearc";
-      byte_order = LITTLE_ENDIAN;
-      break;
+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;
 
-    case OPTION_CD:
-      /* This option has an effect only on ARC EM.  */
-      if (arc_target & ARC_OPCODE_ARCv2EM)
-	arc_features |= ARC_CD;
-      break;
+  pr_debug ("%s:%u: apply_fix: r_type=%d (%s) value=0x%lX offset=0x%lX\n",
+	    fixP->fx_file, fixP->fx_line, fixP->fx_r_type,
+	    ((int) fixP->fx_r_type < 0) ? "Internal":
+	    bfd_get_reloc_code_name (fixP->fx_r_type), value,
+	    fixP->fx_offset);
 
-    case OPTION_USER_MODE:
-    case OPTION_LD_EXT_MASK:
-    case OPTION_SWAP:
-    case OPTION_NORM:
-    case OPTION_BARREL_SHIFT:
-    case OPTION_MIN_MAX:
-    case OPTION_NO_MPY:
-    case OPTION_EA:
-    case OPTION_MUL64:
-    case OPTION_SIMD:
-    case OPTION_SPFP:
-    case OPTION_DPFP:
-    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:
-    case OPTION_FPUDA:
-      /* Dummy options are accepted but have no effect.  */
-      break;
+  fx_addsy = fixP->fx_addsy;
+  fx_subsy = fixP->fx_subsy;
+  fx_offset = 0;
 
-    default:
-      return 0;
+  if (fx_addsy)
+    {
+      add_symbol_segment = S_GET_SEGMENT (fx_addsy);
     }
 
-  if (cpu_flags != EF_ARC_CPU_GENERIC)
-    arc_eflag = (arc_eflag & ~EF_ARC_MACH_MSK) | cpu_flags;
-
-  return 1;
-}
-
-void
-md_show_usage (FILE *stream)
-{
-  fprintf (stream, _("ARC-specific assembler options:\n"));
+  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);
 
-  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");
+      if (sub_symbol_segment == absolute_section)
+	{
+	  /* The symbol is really a constant.  */
+	  fx_offset -= S_GET_VALUE (fx_subsy);
+	  fx_subsy = NULL;
+	}
+      else
+	{
+	  as_bad_where (fixP->fx_file, fixP->fx_line,
+			_("can't resolve `%s' {%s section} - `%s' {%s section}"),
+			fx_addsy ? S_GET_NAME (fx_addsy) : "0",
+			segment_name (add_symbol_segment),
+			S_GET_NAME (fx_subsy),
+			segment_name (sub_symbol_segment));
+	  return;
+	}
+    }
 
-  fprintf (stream, _("\
-  -EB                     assemble code for a big-endian cpu\n"));
-  fprintf (stream, _("\
-  -EL                     assemble code for a little-endian cpu\n"));
-}
+  if (fx_addsy
+      && !S_IS_WEAK (fx_addsy))
+    {
+      if (add_symbol_segment == seg
+	  && fixP->fx_pcrel)
+	{
+	  value += S_GET_VALUE (fx_addsy);
+	  value -= md_pcrel_from_section (fixP, seg);
+	  fx_addsy = NULL;
+	  fixP->fx_pcrel = FALSE;
+	}
+      else if (add_symbol_segment == absolute_section)
+	{
+	  value = fixP->fx_offset;
+	  fx_offset += S_GET_VALUE (fixP->fx_addsy);
+	  fx_addsy = NULL;
+	  fixP->fx_pcrel = FALSE;
+	}
+    }
 
-static void
-preprocess_operands (const struct arc_opcode *opcode,
-		     expressionS *tok,
-		     int ntok)
-{
-  int i;
-  size_t len;
-  const char *p;
-  unsigned j;
-  const struct arc_aux_reg *auxr;
+  if (!fx_addsy)
+    fixP->fx_done = TRUE;
 
-  for (i = 0; i < ntok; i++)
+  if (fixP->fx_pcrel)
     {
-      switch (tok[i].X_op)
-	{
-	case O_illegal:
-	case O_absent:
-	  break; /* Throw and error.  */
-
-	case O_symbol:
-	  if (opcode->class != AUXREG)
-	    break;
-	  /* Convert the symbol to a constant if possible.  */
-	  p = S_GET_NAME (tok[i].X_add_symbol);
-	  len = strlen (p);
+      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);
 
-	  auxr = &arc_aux_regs[0];
-	  for (j = 0; j < arc_num_aux_regs; j++, auxr++)
-	    if (len == auxr->length
-		&& strcasecmp (auxr->name, p) == 0)
-	      {
-		tok[i].X_op = O_constant;
-		tok[i].X_add_number = auxr->address;
-		break;
-	      }
+      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;
 	}
     }
-}
 
-/* 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 *opcode;
-  int cpumatch = 1;
-
-  /* Search opcodes.  */
-  opcode = (const struct arc_opcode *) hash_find (arc_opcode_hash, opname);
+  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);
 
-  /* Couldn't find opcode conventional way, try special cases.  */
-  if (!opcode)
-    opcode = find_special_case (opname, &nflgs, pflags, tok, &ntok);
 
-  if (opcode)
+  /* Now check for TLS relocations.  */
+  reloc = fixP->fx_r_type;
+  switch (reloc)
     {
-      pr_debug ("%s:%d: assemble_tokens: %s trying opcode 0x%08X\n",
-		frag_now->fr_file, frag_now->fr_line, opcode->name,
-		opcode->opcode);
+    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;
 
-      preprocess_operands (opcode, tok, ntok);
+    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;
 
-      found_something = TRUE;
-      opcode = find_opcode_match (opcode, tok, &ntok, pflags, nflgs, &cpumatch);
-      if (opcode)
-	{
-	  struct arc_insn insn;
-	  assemble_insn (opcode, tok, ntok, pflags, nflgs, &insn);
-	  emit_insn (&insn);
-	  return;
-	}
+    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 (found_something)
+  if (!fixP->fx_done)
     {
-      if (cpumatch)
-	as_bad (_("inappropriate arguments for opcode '%s'"), opname);
-      else
-	as_bad (_("opcode '%s' not supported for target %s"), opname,
-		arc_target_name);
+      return;
     }
-  else
-    as_bad (_("unknown opcode '%s'"), opname);
-}
-
-/* Used to find special case opcode.  */
-
-static const struct arc_opcode *
-find_special_case (const char *opname,
-		   int *nflgs,
-		   struct arc_flags *pflags,
-		   expressionS *tok,
-		   int *ntok)
-{
-  const struct arc_opcode *opcode;
-
-  opcode = find_special_case_pseudo (opname, ntok, tok, nflgs, pflags);
-
-  if (opcode == NULL)
-    opcode = find_special_case_flag (opname, nflgs, pflags);
 
-  return opcode;
-}
+  /* Addjust the value if we have a constant.  */
+  value += fx_offset;
 
-/* Swap operand tokens.  */
+  /* 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);
 
-static void
-swap_operand (expressionS *operand_array,
-	      unsigned source,
-	      unsigned destination)
-{
-  expressionS cpy_operand;
-  expressionS *src_operand;
-  expressionS *dst_operand;
-  size_t size;
+  reloc = fixP->fx_r_type;
+  switch (reloc)
+    {
+    case BFD_RELOC_8:
+    case BFD_RELOC_16:
+    case BFD_RELOC_24:
+    case BFD_RELOC_32:
+    case BFD_RELOC_64:
+    case BFD_RELOC_ARC_32_PCREL:
+      md_number_to_chars (fixpos, value, fixP->fx_size);
+      return;
 
-  if (source == destination)
-    return;
+    case BFD_RELOC_ARC_GOTPC32:
+      /* I cannot fix an GOTPC relocation because I need to relax it
+	 from ld rx,[pcl,@sym@gotpc] to add rx,pcl,@sym@gotpc.  */
+      as_bad (_("Unsupported operation on reloc"));
+      return;
 
-  src_operand = &operand_array[source];
-  dst_operand = &operand_array[destination];
-  size = sizeof (expressionS);
+    case BFD_RELOC_ARC_TLS_DTPOFF:
+    case BFD_RELOC_ARC_TLS_LE_32:
+      gas_assert (!fixP->fx_addsy);
+      gas_assert (!fixP->fx_subsy);
 
-  /* 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);
-}
+    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;
 
-/* Check if *op matches *tok type.
-   Returns FALSE if they don't match, TRUE if they match.  */
+    case BFD_RELOC_ARC_PLT32:
+      md_number_to_chars_midend (fixpos, value, fixP->fx_size);
+      return;
 
-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];
+    case BFD_RELOC_ARC_S25H_PCREL_PLT:
+      reloc = BFD_RELOC_ARC_S25W_PCREL;
+      goto solve_plt;
 
-  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 BFD_RELOC_ARC_S21H_PCREL_PLT:
+      reloc = BFD_RELOC_ARC_S21H_PCREL;
+      goto solve_plt;
 
-    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 BFD_RELOC_ARC_S25W_PCREL_PLT:
+      reloc = BFD_RELOC_ARC_S25W_PCREL;
+      goto solve_plt;
 
-    case O_register:
-      if (operand_real->flags & ARC_OPERAND_IR)
-	ret = TRUE;
-      break;
+    case BFD_RELOC_ARC_S21W_PCREL_PLT:
+      reloc = BFD_RELOC_ARC_S21W_PCREL;
 
-    case O_bracket:
-      if (operand_real->flags & ARC_OPERAND_BRAKET)
-	ret = TRUE;
+    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:
-      /* Unknown.  */
-      break;
-    }
-  return ret;
-}
+      {
+	if ((int) fixP->fx_r_type >= 0)
+	  as_fatal (_("unhandled relocation type %s"),
+		    bfd_get_reloc_code_name (fixP->fx_r_type));
 
-/* Find pseudo instruction in array.  */
+	/* 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"));
 
-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;
+	gas_assert (-(int) fixP->fx_r_type < (int) arc_num_operands);
+	operand = &arc_operands[-(int) fixP->fx_r_type];
+	break;
+      }
+    }
 
-  for (i = 0; i < arc_num_pseudo_insn; ++i)
+  if (target_big_endian)
     {
-      pseudo_insn = &arc_pseudo_insns[i];
-      if (strcmp (pseudo_insn->mnemonic_p, opname) == 0)
+      switch (fixP->fx_size)
 	{
-	  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;
+	case 4:
+	  insn = bfd_getb32 (fixpos);
+	  break;
+	case 2:
+	  insn = bfd_getb16 (fixpos);
+	  break;
+	default:
+	  as_bad_where (fixP->fx_file, fixP->fx_line,
+			_("unknown fixup size"));
+	}
+    }
+  else
+    {
+      insn = 0;
+      switch (fixP->fx_size)
+	{
+	case 4:
+	  insn = bfd_getl16 (fixpos) << 16 | bfd_getl16 (fixpos + 2);
+	  break;
+	case 2:
+	  insn = bfd_getl16 (fixpos);
+	  break;
+	default:
+	  as_bad_where (fixP->fx_file, fixP->fx_line,
+			_("unknown fixup size"));
 	}
     }
-  return NULL;
-}
-
-/* Assumes the expressionS *tok is of sufficient size.  */
-
-static const struct arc_opcode *
-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);
+  insn = insert_operand (insn, operand, (offsetT) value,
+			 fixP->fx_file, fixP->fx_line);
 
-  if (pseudo_insn == NULL)
-    return NULL;
+  md_number_to_chars_midend (fixpos, insn, fixP->fx_size);
+}
 
-  /* 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);
+/* Prepare machine-dependent frags for relaxation.
 
-  /* 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];
+   Called just before relaxation starts.  Any symbol that is now undefined
+   will not become defined.
 
-      if (operand_real->flags & ARC_OPERAND_BRAKET &&
-	  !operand_pseudo->needs_insert)
-	continue;
+   Return the correct fr_subtype in the frag.
 
-      /* Has to be inserted (i.e. this token does not exist yet).  */
-      if (operand_pseudo->needs_insert)
-	{
-	  if (operand_real->flags & ARC_OPERAND_BRAKET)
-	    {
-	      tok[i].X_op = O_bracket;
-	      ++(*ntok);
-	      continue;
-	    }
+   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.
 
-	  /* 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);
+   Although it may not be explicit in the frag, pretend
+   fr_var starts with a value.  */
 
-	  tokenize_arguments (construct_operand, &tok[i], 1);
-	  ++(*ntok);
-	}
+int
+md_estimate_size_before_relax (fragS *fragP,
+			       segT segment)
+{
+  int growth;
+
+  /* If the symbol is not located within the same section AND it's not
+     an absolute section, use the maximum.  OR if the symbol is a
+     constant AND the insn is by nature not pc-rel, use the maximum.
+     OR if the symbol is being equated against another symbol, use the
+     maximum.  OR if the symbol is weak use the maximum.  */
+  if ((S_GET_SEGMENT (fragP->fr_symbol) != segment
+       && S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
+      || (symbol_constant_p (fragP->fr_symbol)
+	  && !fragP->tc_frag_data.pcrel)
+      || symbol_equated_p (fragP->fr_symbol)
+      || S_IS_WEAK (fragP->fr_symbol))
+    {
+      while (md_relax_table[fragP->fr_subtype].rlx_more != ARC_RLX_NONE)
+	++fragP->fr_subtype;
+    }
 
-      else if (operand_pseudo->count)
-	{
-	  /* Operand number has to be adjusted accordingly (by operand
-	     type).  */
-	  switch (tok[i].X_op)
-	    {
-	    case O_constant:
-	      tok[i].X_add_number += operand_pseudo->count;
-	      break;
+  growth = md_relax_table[fragP->fr_subtype].rlx_length;
+  fragP->fr_var = growth;
 
-	    case O_symbol:
-	      break;
+  pr_debug ("%s:%d: md_estimate_size_before_relax: %d\n",
+	   fragP->fr_file, fragP->fr_line, growth);
 
-	    default:
-	      /* Ignored.  */
-	      break;
-	    }
-	}
-    }
+  return growth;
+}
 
-  /* 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];
+/* Translate internal representation of relocation info to BFD target
+   format.  */
 
-      if (operand_pseudo->swap_operand_idx == i)
-	continue;
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED,
+	      fixS *fixP)
+{
+  arelent *reloc;
+  bfd_reloc_code_real_type code;
 
-      swap_operand (tok, i, operand_pseudo->swap_operand_idx);
+  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;
 
-      /* Prevent a swap back later by breaking out.  */
-      break;
-    }
+  /* 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);
 
-  return (const struct arc_opcode *)
-    hash_find (arc_opcode_hash,	pseudo_insn->mnemonic_r);
-}
+  code = fixP->fx_r_type;
 
-static const struct arc_opcode *
-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 *opcode;
+  /* 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;
 
-  /* Search for special case instruction.  */
-  for (i = 0; i < arc_num_flag_special; i++)
+  reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+  if (reloc->howto == NULL)
     {
-      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;
+      as_bad_where (fixP->fx_file, fixP->fx_line,
+		    _("cannot represent `%s' relocation in object file"),
+		    bfd_get_reloc_code_name (code));
+      return NULL;
+    }
 
-      /* Found a potential special case instruction, now test for
-	 flags.  */
-      for (flag_arr_idx = 0;; ++flag_arr_idx)
-	{
-	  flag_idx = arc_flag_special_opcode->flags[flag_arr_idx];
-	  if (flag_idx == 0)
-	    break;  /* End of array, nothing found.  */
+  if (!fixP->fx_pcrel != !reloc->howto->pc_relative)
+    as_fatal (_("internal error? cannot generate `%s' relocation"),
+	      bfd_get_reloc_code_name (code));
 
-	  flagnm = arc_flag_operands[flag_idx].name;
-	  flaglen = strlen (flagnm);
-	  if (strcmp (opname + oplen, flagnm) == 0)
-	    {
-	      opcode = (const struct arc_opcode *)
-		hash_find (arc_opcode_hash,
-			   arc_flag_special_opcode->name);
+  gas_assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
 
-	      if (*nflgs + 1 > MAX_INSN_FLGS)
-		break;
-	      memcpy (pflags[*nflgs].name, flagnm, flaglen);
-	      pflags[*nflgs].name[flaglen] = '\0';
-	      (*nflgs)++;
-	      return opcode;
-	    }
+  if (code == BFD_RELOC_ARC_TLS_DTPOFF
+      || code ==  BFD_RELOC_ARC_TLS_DTPOFF_S9)
+    {
+      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;
 	}
     }
-  return NULL;
+  else
+    reloc->addend = fixP->fx_offset;
+
+  return reloc;
 }
 
-/* Check whether a symbol involves a register.  */
+/* 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.
 
-static int
-contains_register (symbolS *sym)
+   Out: Any fixS:s and constants are set up.  */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+		 segT segment ATTRIBUTE_UNUSED,
+		 fragS *fragP)
 {
-  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));
-  }
-  else
-    return 0;
-}
+  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;
 
-/* Returns the register number within a symbol.  */
+  fix = (fragP->fr_fix < 0 ? 0 : fragP->fr_fix);
+  dest = fragP->fr_literal + fix;
+  table_entry = TC_GENERIC_RELAX_TABLE + fragP->fr_subtype;
 
-static int
-get_register (symbolS *sym)
-{
-  if (!contains_register (sym))
-    return -1;
+  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);
 
-  expressionS *ex = symbol_get_value_expression (sym);
-  return regno (ex->X_add_number);
-}
+  if (fragP->fr_subtype <= 0
+      && fragP->fr_subtype >= arc_num_relax_opcodes)
+    as_fatal (_("no relaxation found for this instruction."));
 
-/* Allocates a tok entry.  */
+  opcode = &arc_relax_opcodes[fragP->fr_subtype];
 
-static int
-allocate_tok (expressionS *tok, int ntok, int cidx)
-{
-  if (ntok > MAX_INSN_ARGS - 2)
-    return 0; /* No space left.  */
+  assemble_insn (opcode, relax_arg->tok, relax_arg->ntok, relax_arg->pflags,
+	relax_arg->nflg, &insn);
 
-  if (cidx > ntok)
-    return 0; /* Incorect args.  */
+  apply_fixups (&insn, fragP, fix);
 
-  memcpy (&tok[ntok+1], &tok[ntok], sizeof (*tok));
+  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);
 
-  if (cidx == ntok)
-    return 1; /* Success.  */
-  return allocate_tok (tok, ntok - 1, cidx);
+  fragP->fr_fix += table_entry->rlx_length;
+  fragP->fr_var = 0;
 }
 
-/* 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.  */
+/* 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.  */
 
-static bfd_boolean
-generic_reloc_p (extended_bfd_reloc_code_real_type reloc)
+symbolS *
+md_undefined_symbol (char *name)
 {
-  if (!reloc)
-    return FALSE;
-
-  switch (reloc)
+  /* 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)))
     {
-    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:
-      break;
+      if (!GOT_symbol)
+	{
+	  if (symbol_find (name))
+	    as_bad ("GOT already in symbol table");
+
+	  GOT_symbol = symbol_new (GLOBAL_OFFSET_TABLE_NAME, undefined_section,
+				   (valueT) 0, &zero_address_frag);
+	};
+      return GOT_symbol;
     }
-  return TRUE;
+  return NULL;
 }
 
-/* Search forward through all variants of an opcode looking for a
-   syntax match.  */
+/* 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.  */
 
-static const struct arc_opcode *
-find_opcode_match (const struct arc_opcode *first_opcode,
-		   expressionS *tok,
-		   int *pntok,
-		   struct arc_flags *first_pflag,
-		   int nflgs,
-		   int *pcpumatch)
+char *
+md_atof (int type, char *litP, int *sizeP)
 {
-  const struct arc_opcode *opcode = first_opcode;
-  int ntok = *pntok;
-  int got_cpu_match = 0;
-  expressionS bktok[MAX_INSN_ARGS];
-  int bkntok;
-  expressionS emptyE;
+  return ieee_md_atof (type, litP, sizeP, target_big_endian);
+}
 
-  memset (&emptyE, 0, sizeof (emptyE));
-  memcpy (bktok, tok, MAX_INSN_ARGS * sizeof (*tok));
-  bkntok = ntok;
+/* 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.  */
 
-  do
+void
+md_operand (expressionS *expressionP ATTRIBUTE_UNUSED)
+{
+  char *p = input_line_pointer;
+  if (*p == '@')
     {
-      const unsigned char *opidx;
-      const unsigned char *flgidx;
-      int tokidx = 0;
-      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 (is_code_density_p (opcode) && !(arc_features & ARC_CD))
-	goto match_failed;
+      input_line_pointer++;
+      expressionP->X_op = O_symbol;
+      expression (expressionP);
+    }
+}
 
-      got_cpu_match = 1;
-      pr_debug ("cpu ");
+/* 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.  */
 
-      /* Check the operands.  */
-      for (opidx = opcode->operands; *opidx; ++opidx)
-	{
-	  const struct arc_operand *operand = &arc_operands[*opidx];
+bfd_boolean
+arc_parse_name (const char *name,
+		struct expressionS *e)
+{
+  struct symbol *sym;
 
-	  /* Only take input from real operands.  */
-	  if ((operand->flags & ARC_OPERAND_FAKE)
-	      && !(operand->flags & ARC_OPERAND_BRAKET))
-	    continue;
+  if (!assembling_insn)
+    return FALSE;
 
-	  /* When we expect input, make sure we have it.  */
-	  if (tokidx >= ntok)
-	    goto match_failed;
+  /* Handle only registers.  */
+  if (e->X_op != O_absent)
+    return FALSE;
 
-	  /* 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;
+  sym = hash_find (arc_reg_hash, name);
+  if (sym)
+    {
+      e->X_op = O_register;
+      e->X_add_number = S_GET_VALUE (sym);
+      return TRUE;
+    }
+  return FALSE;
+}
 
-	      /* 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;
-		}
+/* md_parse_option
+   Invocation line includes a switch not recognized by the base assembler.
+   See if it's a processor-specific option.
 
-	      /* Special handling?  */
-	      if (operand->insert)
-		{
-		  const char *errmsg = NULL;
-		  (*operand->insert)(0,
-				     regno (tok[tokidx].X_add_number),
-				     &errmsg);
-		  if (errmsg)
-		    {
-		      if (operand->flags & ARC_OPERAND_IGNORE)
-			{
-			  /* Missing argument, create one.  */
-			  if (!allocate_tok (tok, ntok - 1, tokidx))
-			    goto match_failed;
+   New options (supported) are:
 
-			  tok[tokidx].X_op = O_absent;
-			  ++ntok;
-			}
-		      else
-			goto match_failed;
-		    }
-		}
+   -mcpu=<cpu name>		 Assemble for selected processor
+   -EB/-mbig-endian		 Big-endian
+   -EL/-mlittle-endian		 Little-endian
+   -mrelax                       Enable relaxation
 
-	      t = &tok[tokidx];
-	      break;
+   The following CPU names are recognized:
+   arc700, av2em, av2hs.  */
 
-	    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;
+int
+md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+{
+  int cpu_flags = EF_ARC_CPU_GENERIC;
 
-	    case ARC_OPERAND_LIMM:
-	    case ARC_OPERAND_SIGNED:
-	    case ARC_OPERAND_UNSIGNED:
-	      switch (tok[tokidx].X_op)
-		{
-		case O_illegal:
-		case O_absent:
-		case O_register:
-		  goto match_failed;
+  switch (c)
+    {
+    case OPTION_ARC600:
+    case OPTION_ARC601:
+      return md_parse_option (OPTION_MCPU, "arc600");
 
-		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;
+    case OPTION_ARC700:
+      return md_parse_option (OPTION_MCPU, "arc700");
 
-		  tok[tokidx].X_op = O_absent;
-		  ++ntok;
-		  break;
+    case OPTION_ARCEM:
+      return md_parse_option (OPTION_MCPU, "arcem");
 
-		case O_constant:
-		  /* Check the range.  */
-		  if (operand->bits != 32
-		      && !(operand->flags & ARC_OPERAND_NCHK))
-		    {
-		      offsetT min, max, val;
-		      val = tok[tokidx].X_add_number;
+    case OPTION_ARCHS:
+      return md_parse_option (OPTION_MCPU, "archs");
 
-		      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;
-			}
+    case OPTION_MCPU:
+      {
+	int i;
+	c[...]

[diff truncated at 100000 bytes]


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]