OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-old/] [binutils-2.18.50/] [gas/] [config/] [tc-arc.c] - Diff between revs 156 and 816

Go to most recent revision | Only display areas with differences | Details | Blame | View Log

Rev 156 Rev 816
/* tc-arc.c -- Assembler for the ARC
/* tc-arc.c -- Assembler for the ARC
   Copyright 1994, 1995, 1997, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
   Copyright 1994, 1995, 1997, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
   2006, 2007  Free Software Foundation, Inc.
   2006, 2007  Free Software Foundation, Inc.
   Contributed by Doug Evans (dje@cygnus.com).
   Contributed by Doug Evans (dje@cygnus.com).
 
 
   This file is part of GAS, the GNU Assembler.
   This file is part of GAS, the GNU Assembler.
 
 
   GAS is free software; you can redistribute it and/or modify
   GAS is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   the Free Software Foundation; either version 3, or (at your option)
   any later version.
   any later version.
 
 
   GAS is distributed in the hope that it will be useful,
   GAS is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   GNU General Public License for more details.
 
 
   You should have received a copy of the GNU General Public License
   You should have received a copy of the GNU General Public License
   along with GAS; see the file COPYING.  If not, write to the Free
   along with GAS; see the file COPYING.  If not, write to the Free
   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
   02110-1301, USA.  */
   02110-1301, USA.  */
 
 
#include "as.h"
#include "as.h"
#include "struc-symbol.h"
#include "struc-symbol.h"
#include "safe-ctype.h"
#include "safe-ctype.h"
#include "subsegs.h"
#include "subsegs.h"
#include "opcode/arc.h"
#include "opcode/arc.h"
#include "../opcodes/arc-ext.h"
#include "../opcodes/arc-ext.h"
#include "elf/arc.h"
#include "elf/arc.h"
#include "dwarf2dbg.h"
#include "dwarf2dbg.h"
 
 
const struct suffix_classes
const struct suffix_classes
{
{
  char *name;
  char *name;
  int  len;
  int  len;
} suffixclass[] =
} suffixclass[] =
{
{
  { "SUFFIX_COND|SUFFIX_FLAG",23 },
  { "SUFFIX_COND|SUFFIX_FLAG",23 },
  { "SUFFIX_FLAG", 11 },
  { "SUFFIX_FLAG", 11 },
  { "SUFFIX_COND", 11 },
  { "SUFFIX_COND", 11 },
  { "SUFFIX_NONE", 11 }
  { "SUFFIX_NONE", 11 }
};
};
 
 
#define MAXSUFFIXCLASS (sizeof (suffixclass) / sizeof (struct suffix_classes))
#define MAXSUFFIXCLASS (sizeof (suffixclass) / sizeof (struct suffix_classes))
 
 
const struct syntax_classes
const struct syntax_classes
{
{
  char *name;
  char *name;
  int  len;
  int  len;
  int  class;
  int  class;
} syntaxclass[] =
} syntaxclass[] =
{
{
  { "SYNTAX_3OP|OP1_MUST_BE_IMM", 26, SYNTAX_3OP|OP1_MUST_BE_IMM|SYNTAX_VALID },
  { "SYNTAX_3OP|OP1_MUST_BE_IMM", 26, SYNTAX_3OP|OP1_MUST_BE_IMM|SYNTAX_VALID },
  { "OP1_MUST_BE_IMM|SYNTAX_3OP", 26, OP1_MUST_BE_IMM|SYNTAX_3OP|SYNTAX_VALID },
  { "OP1_MUST_BE_IMM|SYNTAX_3OP", 26, OP1_MUST_BE_IMM|SYNTAX_3OP|SYNTAX_VALID },
  { "SYNTAX_2OP|OP1_IMM_IMPLIED", 26, SYNTAX_2OP|OP1_IMM_IMPLIED|SYNTAX_VALID },
  { "SYNTAX_2OP|OP1_IMM_IMPLIED", 26, SYNTAX_2OP|OP1_IMM_IMPLIED|SYNTAX_VALID },
  { "OP1_IMM_IMPLIED|SYNTAX_2OP", 26, OP1_IMM_IMPLIED|SYNTAX_2OP|SYNTAX_VALID },
  { "OP1_IMM_IMPLIED|SYNTAX_2OP", 26, OP1_IMM_IMPLIED|SYNTAX_2OP|SYNTAX_VALID },
  { "SYNTAX_3OP",                 10, SYNTAX_3OP|SYNTAX_VALID },
  { "SYNTAX_3OP",                 10, SYNTAX_3OP|SYNTAX_VALID },
  { "SYNTAX_2OP",                 10, SYNTAX_2OP|SYNTAX_VALID }
  { "SYNTAX_2OP",                 10, SYNTAX_2OP|SYNTAX_VALID }
};
};
 
 
#define MAXSYNTAXCLASS (sizeof (syntaxclass) / sizeof (struct syntax_classes))
#define MAXSYNTAXCLASS (sizeof (syntaxclass) / sizeof (struct syntax_classes))
 
 
/* This array holds the chars that always start a comment.  If the
/* This array holds the chars that always start a comment.  If the
   pre-processor is disabled, these aren't very useful.  */
   pre-processor is disabled, these aren't very useful.  */
const char comment_chars[] = "#;";
const char comment_chars[] = "#;";
 
 
/* This array holds the chars that only start a comment at the beginning of
/* This array holds the chars that only start a comment at the beginning of
   a line.  If the line seems to have the form '# 123 filename'
   a line.  If the line seems to have the form '# 123 filename'
   .line and .file directives will appear in the pre-processed output */
   .line and .file directives will appear in the pre-processed output */
/* Note that input_file.c hand checks for '#' at the beginning of the
/* Note that input_file.c hand checks for '#' at the beginning of the
   first line of the input file.  This is because the compiler outputs
   first line of the input file.  This is because the compiler outputs
   #NO_APP at the beginning of its output.  */
   #NO_APP at the beginning of its output.  */
/* Also note that comments started like this one will always
/* Also note that comments started like this one will always
   work if '/' isn't otherwise defined.  */
   work if '/' isn't otherwise defined.  */
const char line_comment_chars[] = "#";
const char line_comment_chars[] = "#";
 
 
const char line_separator_chars[] = "";
const char line_separator_chars[] = "";
 
 
/* Chars that can be used to separate mant from exp in floating point nums.  */
/* Chars that can be used to separate mant from exp in floating point nums.  */
const char EXP_CHARS[] = "eE";
const char EXP_CHARS[] = "eE";
 
 
/* Chars that mean this number is a floating point constant
/* Chars that mean this number is a floating point constant
   As in 0f12.456 or 0d1.2345e12.  */
   As in 0f12.456 or 0d1.2345e12.  */
const char FLT_CHARS[] = "rRsSfFdD";
const char FLT_CHARS[] = "rRsSfFdD";
 
 
/* Byte order.  */
/* Byte order.  */
extern int target_big_endian;
extern int target_big_endian;
const char *arc_target_format = DEFAULT_TARGET_FORMAT;
const char *arc_target_format = DEFAULT_TARGET_FORMAT;
static int byte_order = DEFAULT_BYTE_ORDER;
static int byte_order = DEFAULT_BYTE_ORDER;
 
 
static segT arcext_section;
static segT arcext_section;
 
 
/* One of bfd_mach_arc_n.  */
/* One of bfd_mach_arc_n.  */
static int arc_mach_type = bfd_mach_arc_6;
static int arc_mach_type = bfd_mach_arc_6;
 
 
/* Non-zero if the cpu type has been explicitly specified.  */
/* Non-zero if the cpu type has been explicitly specified.  */
static int mach_type_specified_p = 0;
static int mach_type_specified_p = 0;
 
 
/* Non-zero if opcode tables have been initialized.
/* Non-zero if opcode tables have been initialized.
   A .option command must appear before any instructions.  */
   A .option command must appear before any instructions.  */
static int cpu_tables_init_p = 0;
static int cpu_tables_init_p = 0;
 
 
static struct hash_control *arc_suffix_hash = NULL;
static struct hash_control *arc_suffix_hash = NULL;


const char *md_shortopts = "";
const char *md_shortopts = "";
 
 
enum options
enum options
{
{
  OPTION_EB = OPTION_MD_BASE,
  OPTION_EB = OPTION_MD_BASE,
  OPTION_EL,
  OPTION_EL,
  OPTION_ARC5,
  OPTION_ARC5,
  OPTION_ARC6,
  OPTION_ARC6,
  OPTION_ARC7,
  OPTION_ARC7,
  OPTION_ARC8,
  OPTION_ARC8,
  OPTION_ARC
  OPTION_ARC
};
};
 
 
struct option md_longopts[] =
struct option md_longopts[] =
{
{
  { "EB", no_argument, NULL, OPTION_EB },
  { "EB", no_argument, NULL, OPTION_EB },
  { "EL", no_argument, NULL, OPTION_EL },
  { "EL", no_argument, NULL, OPTION_EL },
  { "marc5", no_argument, NULL, OPTION_ARC5 },
  { "marc5", no_argument, NULL, OPTION_ARC5 },
  { "pre-v6", no_argument, NULL, OPTION_ARC5 },
  { "pre-v6", no_argument, NULL, OPTION_ARC5 },
  { "marc6", no_argument, NULL, OPTION_ARC6 },
  { "marc6", no_argument, NULL, OPTION_ARC6 },
  { "marc7", no_argument, NULL, OPTION_ARC7 },
  { "marc7", no_argument, NULL, OPTION_ARC7 },
  { "marc8", no_argument, NULL, OPTION_ARC8 },
  { "marc8", no_argument, NULL, OPTION_ARC8 },
  { "marc", no_argument, NULL, OPTION_ARC },
  { "marc", no_argument, NULL, OPTION_ARC },
  { NULL, no_argument, NULL, 0 }
  { NULL, no_argument, NULL, 0 }
};
};
size_t md_longopts_size = sizeof (md_longopts);
size_t md_longopts_size = sizeof (md_longopts);
 
 
#define IS_SYMBOL_OPERAND(o) \
#define IS_SYMBOL_OPERAND(o) \
 ((o) == 'b' || (o) == 'c' || (o) == 's' || (o) == 'o' || (o) == 'O')
 ((o) == 'b' || (o) == 'c' || (o) == 's' || (o) == 'o' || (o) == 'O')
 
 
struct arc_operand_value *get_ext_suffix (char *s);
struct arc_operand_value *get_ext_suffix (char *s);
 
 
/* Invocation line includes a switch not recognized by the base assembler.
/* Invocation line includes a switch not recognized by the base assembler.
   See if it's a processor-specific option.  */
   See if it's a processor-specific option.  */
 
 
int
int
md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
{
{
  switch (c)
  switch (c)
    {
    {
    case OPTION_ARC5:
    case OPTION_ARC5:
      arc_mach_type = bfd_mach_arc_5;
      arc_mach_type = bfd_mach_arc_5;
      break;
      break;
    case OPTION_ARC:
    case OPTION_ARC:
    case OPTION_ARC6:
    case OPTION_ARC6:
      arc_mach_type = bfd_mach_arc_6;
      arc_mach_type = bfd_mach_arc_6;
      break;
      break;
    case OPTION_ARC7:
    case OPTION_ARC7:
      arc_mach_type = bfd_mach_arc_7;
      arc_mach_type = bfd_mach_arc_7;
      break;
      break;
    case OPTION_ARC8:
    case OPTION_ARC8:
      arc_mach_type = bfd_mach_arc_8;
      arc_mach_type = bfd_mach_arc_8;
      break;
      break;
    case OPTION_EB:
    case OPTION_EB:
      byte_order = BIG_ENDIAN;
      byte_order = BIG_ENDIAN;
      arc_target_format = "elf32-bigarc";
      arc_target_format = "elf32-bigarc";
      break;
      break;
    case OPTION_EL:
    case OPTION_EL:
      byte_order = LITTLE_ENDIAN;
      byte_order = LITTLE_ENDIAN;
      arc_target_format = "elf32-littlearc";
      arc_target_format = "elf32-littlearc";
      break;
      break;
    default:
    default:
      return 0;
      return 0;
    }
    }
  return 1;
  return 1;
}
}
 
 
void
void
md_show_usage (FILE *stream)
md_show_usage (FILE *stream)
{
{
  fprintf (stream, "\
  fprintf (stream, "\
ARC Options:\n\
ARC Options:\n\
  -marc[5|6|7|8]          select processor variant (default arc%d)\n\
  -marc[5|6|7|8]          select processor variant (default arc%d)\n\
  -EB                     assemble code for a big endian cpu\n\
  -EB                     assemble code for a big endian cpu\n\
  -EL                     assemble code for a little endian cpu\n", arc_mach_type + 5);
  -EL                     assemble code for a little endian cpu\n", arc_mach_type + 5);
}
}
 
 
/* This function is called once, at assembler startup time.  It should
/* This function is called once, at assembler startup time.  It should
   set up all the tables, etc. that the MD part of the assembler will need.
   set up all the tables, etc. that the MD part of the assembler will need.
   Opcode selection is deferred until later because we might see a .option
   Opcode selection is deferred until later because we might see a .option
   command.  */
   command.  */
 
 
void
void
md_begin (void)
md_begin (void)
{
{
  /* The endianness can be chosen "at the factory".  */
  /* The endianness can be chosen "at the factory".  */
  target_big_endian = byte_order == BIG_ENDIAN;
  target_big_endian = byte_order == BIG_ENDIAN;
 
 
  if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, arc_mach_type))
  if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, arc_mach_type))
    as_warn (_("could not set architecture and machine"));
    as_warn (_("could not set architecture and machine"));
 
 
  /* This call is necessary because we need to initialize `arc_operand_map'
  /* This call is necessary because we need to initialize `arc_operand_map'
     which may be needed before we see the first insn.  */
     which may be needed before we see the first insn.  */
  arc_opcode_init_tables (arc_get_opcode_mach (arc_mach_type,
  arc_opcode_init_tables (arc_get_opcode_mach (arc_mach_type,
                                               target_big_endian));
                                               target_big_endian));
}
}
 
 
/* Initialize the various opcode and operand tables.
/* Initialize the various opcode and operand tables.
   MACH is one of bfd_mach_arc_xxx.  */
   MACH is one of bfd_mach_arc_xxx.  */
 
 
static void
static void
init_opcode_tables (int mach)
init_opcode_tables (int mach)
{
{
  int i;
  int i;
  char *last;
  char *last;
 
 
  if ((arc_suffix_hash = hash_new ()) == NULL)
  if ((arc_suffix_hash = hash_new ()) == NULL)
    as_fatal (_("virtual memory exhausted"));
    as_fatal (_("virtual memory exhausted"));
 
 
  if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach))
  if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach))
    as_warn (_("could not set architecture and machine"));
    as_warn (_("could not set architecture and machine"));
 
 
  /* This initializes a few things in arc-opc.c that we need.
  /* This initializes a few things in arc-opc.c that we need.
     This must be called before the various arc_xxx_supported fns.  */
     This must be called before the various arc_xxx_supported fns.  */
  arc_opcode_init_tables (arc_get_opcode_mach (mach, target_big_endian));
  arc_opcode_init_tables (arc_get_opcode_mach (mach, target_big_endian));
 
 
  /* Only put the first entry of each equivalently named suffix in the
  /* Only put the first entry of each equivalently named suffix in the
     table.  */
     table.  */
  last = "";
  last = "";
  for (i = 0; i < arc_suffixes_count; i++)
  for (i = 0; i < arc_suffixes_count; i++)
    {
    {
      if (strcmp (arc_suffixes[i].name, last) != 0)
      if (strcmp (arc_suffixes[i].name, last) != 0)
        hash_insert (arc_suffix_hash, arc_suffixes[i].name, (void *) (arc_suffixes + i));
        hash_insert (arc_suffix_hash, arc_suffixes[i].name, (void *) (arc_suffixes + i));
      last = arc_suffixes[i].name;
      last = arc_suffixes[i].name;
    }
    }
 
 
  /* Since registers don't have a prefix, we put them in the symbol table so
  /* Since registers don't have a prefix, we put them in the symbol table so
     they can't be used as symbols.  This also simplifies argument parsing as
     they can't be used as symbols.  This also simplifies argument parsing as
     we can let gas parse registers for us.  The recorded register number is
     we can let gas parse registers for us.  The recorded register number is
     the address of the register's entry in arc_reg_names.
     the address of the register's entry in arc_reg_names.
 
 
     If the register name is already in the table, then the existing
     If the register name is already in the table, then the existing
     definition is assumed to be from an .ExtCoreRegister pseudo-op.  */
     definition is assumed to be from an .ExtCoreRegister pseudo-op.  */
 
 
  for (i = 0; i < arc_reg_names_count; i++)
  for (i = 0; i < arc_reg_names_count; i++)
    {
    {
      if (symbol_find (arc_reg_names[i].name))
      if (symbol_find (arc_reg_names[i].name))
        continue;
        continue;
      /* Use symbol_create here instead of symbol_new so we don't try to
      /* Use symbol_create here instead of symbol_new so we don't try to
         output registers into the object file's symbol table.  */
         output registers into the object file's symbol table.  */
      symbol_table_insert (symbol_create (arc_reg_names[i].name,
      symbol_table_insert (symbol_create (arc_reg_names[i].name,
                                          reg_section,
                                          reg_section,
                                          (valueT) &arc_reg_names[i],
                                          (valueT) &arc_reg_names[i],
                                          &zero_address_frag));
                                          &zero_address_frag));
    }
    }
 
 
  /* Tell `.option' it's too late.  */
  /* Tell `.option' it's too late.  */
  cpu_tables_init_p = 1;
  cpu_tables_init_p = 1;
}
}


/* Insert an operand value into an instruction.
/* Insert an operand value into an instruction.
   If REG is non-NULL, it is a register number and ignore VAL.  */
   If REG is non-NULL, it is a register number and ignore VAL.  */
 
 
static arc_insn
static arc_insn
arc_insert_operand (arc_insn insn,
arc_insert_operand (arc_insn insn,
                    const struct arc_operand *operand,
                    const struct arc_operand *operand,
                    int mods,
                    int mods,
                    const struct arc_operand_value *reg,
                    const struct arc_operand_value *reg,
                    offsetT val,
                    offsetT val,
                    char *file,
                    char *file,
                    unsigned int line)
                    unsigned int line)
{
{
  if (operand->bits != 32)
  if (operand->bits != 32)
    {
    {
      long min, max;
      long min, max;
      offsetT test;
      offsetT test;
 
 
      if ((operand->flags & ARC_OPERAND_SIGNED) != 0)
      if ((operand->flags & ARC_OPERAND_SIGNED) != 0)
        {
        {
          if ((operand->flags & ARC_OPERAND_SIGNOPT) != 0)
          if ((operand->flags & ARC_OPERAND_SIGNOPT) != 0)
            max = (1 << operand->bits) - 1;
            max = (1 << operand->bits) - 1;
          else
          else
            max = (1 << (operand->bits - 1)) - 1;
            max = (1 << (operand->bits - 1)) - 1;
          min = - (1 << (operand->bits - 1));
          min = - (1 << (operand->bits - 1));
        }
        }
      else
      else
        {
        {
          max = (1 << operand->bits) - 1;
          max = (1 << operand->bits) - 1;
          min = 0;
          min = 0;
        }
        }
 
 
      if ((operand->flags & ARC_OPERAND_NEGATIVE) != 0)
      if ((operand->flags & ARC_OPERAND_NEGATIVE) != 0)
        test = - val;
        test = - val;
      else
      else
        test = val;
        test = val;
 
 
      if (test < (offsetT) min || test > (offsetT) max)
      if (test < (offsetT) min || test > (offsetT) max)
        as_warn_value_out_of_range (_("operand"), test, (offsetT) min, (offsetT) max, file, line);
        as_warn_value_out_of_range (_("operand"), test, (offsetT) min, (offsetT) max, file, line);
    }
    }
 
 
  if (operand->insert)
  if (operand->insert)
    {
    {
      const char *errmsg;
      const char *errmsg;
 
 
      errmsg = NULL;
      errmsg = NULL;
      insn = (*operand->insert) (insn, operand, mods, reg, (long) val, &errmsg);
      insn = (*operand->insert) (insn, operand, mods, reg, (long) val, &errmsg);
      if (errmsg != (const char *) NULL)
      if (errmsg != (const char *) NULL)
        as_warn (errmsg);
        as_warn (errmsg);
    }
    }
  else
  else
    insn |= (((long) val & ((1 << operand->bits) - 1))
    insn |= (((long) val & ((1 << operand->bits) - 1))
             << operand->shift);
             << operand->shift);
 
 
  return insn;
  return insn;
}
}
 
 
/* We need to keep a list of fixups.  We can't simply generate them as
/* We need to keep a list of fixups.  We can't simply generate them as
   we go, because that would require us to first create the frag, and
   we go, because that would require us to first create the frag, and
   that would screw up references to ``.''.  */
   that would screw up references to ``.''.  */
 
 
struct arc_fixup
struct arc_fixup
{
{
  /* index into `arc_operands'  */
  /* index into `arc_operands'  */
  int opindex;
  int opindex;
  expressionS exp;
  expressionS exp;
};
};
 
 
#define MAX_FIXUPS 5
#define MAX_FIXUPS 5
 
 
#define MAX_SUFFIXES 5
#define MAX_SUFFIXES 5
 
 
/* Compute the reloc type of an expression.
/* Compute the reloc type of an expression.
   The possibly modified expression is stored in EXPNEW.
   The possibly modified expression is stored in EXPNEW.
 
 
   This is used to convert the expressions generated by the %-op's into
   This is used to convert the expressions generated by the %-op's into
   the appropriate operand type.  It is called for both data in instructions
   the appropriate operand type.  It is called for both data in instructions
   (operands) and data outside instructions (variables, debugging info, etc.).
   (operands) and data outside instructions (variables, debugging info, etc.).
 
 
   Currently supported %-ops:
   Currently supported %-ops:
 
 
   %st(symbol): represented as "symbol >> 2"
   %st(symbol): represented as "symbol >> 2"
                "st" is short for STatus as in the status register (pc)
                "st" is short for STatus as in the status register (pc)
 
 
   DEFAULT_TYPE is the type to use if no special processing is required.
   DEFAULT_TYPE is the type to use if no special processing is required.
 
 
   DATA_P is non-zero for data or limm values, zero for insn operands.
   DATA_P is non-zero for data or limm values, zero for insn operands.
   Remember that the opcode "insertion fns" cannot be used on data, they're
   Remember that the opcode "insertion fns" cannot be used on data, they're
   only for inserting operands into insns.  They also can't be used for limm
   only for inserting operands into insns.  They also can't be used for limm
   values as the insertion routines don't handle limm values.  When called for
   values as the insertion routines don't handle limm values.  When called for
   insns we return fudged reloc types (real_value - BFD_RELOC_UNUSED).  When
   insns we return fudged reloc types (real_value - BFD_RELOC_UNUSED).  When
   called for data or limm values we use real reloc types.  */
   called for data or limm values we use real reloc types.  */
 
 
static int
static int
get_arc_exp_reloc_type (int data_p,
get_arc_exp_reloc_type (int data_p,
                        int default_type,
                        int default_type,
                        expressionS *exp,
                        expressionS *exp,
                        expressionS *expnew)
                        expressionS *expnew)
{
{
  /* If the expression is "symbol >> 2" we must change it to just "symbol",
  /* If the expression is "symbol >> 2" we must change it to just "symbol",
     as fix_new_exp can't handle it.  Similarly for (symbol - symbol) >> 2.
     as fix_new_exp can't handle it.  Similarly for (symbol - symbol) >> 2.
     That's ok though.  What's really going on here is that we're using
     That's ok though.  What's really going on here is that we're using
     ">> 2" as a special syntax for specifying BFD_RELOC_ARC_B26.  */
     ">> 2" as a special syntax for specifying BFD_RELOC_ARC_B26.  */
 
 
  if (exp->X_op == O_right_shift
  if (exp->X_op == O_right_shift
      && exp->X_op_symbol != NULL
      && exp->X_op_symbol != NULL
      && exp->X_op_symbol->sy_value.X_op == O_constant
      && exp->X_op_symbol->sy_value.X_op == O_constant
      && exp->X_op_symbol->sy_value.X_add_number == 2
      && exp->X_op_symbol->sy_value.X_add_number == 2
      && exp->X_add_number == 0)
      && exp->X_add_number == 0)
    {
    {
      if (exp->X_add_symbol != NULL
      if (exp->X_add_symbol != NULL
          && (exp->X_add_symbol->sy_value.X_op == O_constant
          && (exp->X_add_symbol->sy_value.X_op == O_constant
              || exp->X_add_symbol->sy_value.X_op == O_symbol))
              || exp->X_add_symbol->sy_value.X_op == O_symbol))
        {
        {
          *expnew = *exp;
          *expnew = *exp;
          expnew->X_op = O_symbol;
          expnew->X_op = O_symbol;
          expnew->X_op_symbol = NULL;
          expnew->X_op_symbol = NULL;
          return data_p ? BFD_RELOC_ARC_B26 : arc_operand_map['J'];
          return data_p ? BFD_RELOC_ARC_B26 : arc_operand_map['J'];
        }
        }
      else if (exp->X_add_symbol != NULL
      else if (exp->X_add_symbol != NULL
               && exp->X_add_symbol->sy_value.X_op == O_subtract)
               && exp->X_add_symbol->sy_value.X_op == O_subtract)
        {
        {
          *expnew = exp->X_add_symbol->sy_value;
          *expnew = exp->X_add_symbol->sy_value;
          return data_p ? BFD_RELOC_ARC_B26 : arc_operand_map['J'];
          return data_p ? BFD_RELOC_ARC_B26 : arc_operand_map['J'];
        }
        }
    }
    }
 
 
  *expnew = *exp;
  *expnew = *exp;
  return default_type;
  return default_type;
}
}


static int
static int
arc_set_ext_seg (void)
arc_set_ext_seg (void)
{
{
  if (!arcext_section)
  if (!arcext_section)
    {
    {
      arcext_section = subseg_new (".arcextmap", 0);
      arcext_section = subseg_new (".arcextmap", 0);
      bfd_set_section_flags (stdoutput, arcext_section,
      bfd_set_section_flags (stdoutput, arcext_section,
                             SEC_READONLY | SEC_HAS_CONTENTS);
                             SEC_READONLY | SEC_HAS_CONTENTS);
    }
    }
  else
  else
    subseg_set (arcext_section, 0);
    subseg_set (arcext_section, 0);
  return 1;
  return 1;
}
}
 
 
static void
static void
arc_extoper (int opertype)
arc_extoper (int opertype)
{
{
  char *name;
  char *name;
  char *mode;
  char *mode;
  char c;
  char c;
  char *p;
  char *p;
  int imode = 0;
  int imode = 0;
  int number;
  int number;
  struct arc_ext_operand_value *ext_oper;
  struct arc_ext_operand_value *ext_oper;
  symbolS *symbolP;
  symbolS *symbolP;
 
 
  segT old_sec;
  segT old_sec;
  int old_subsec;
  int old_subsec;
 
 
  name = input_line_pointer;
  name = input_line_pointer;
  c = get_symbol_end ();
  c = get_symbol_end ();
  name = xstrdup (name);
  name = xstrdup (name);
 
 
  p = name;
  p = name;
  while (*p)
  while (*p)
    {
    {
      *p = TOLOWER (*p);
      *p = TOLOWER (*p);
      p++;
      p++;
    }
    }
 
 
  /* just after name is now '\0'  */
  /* just after name is now '\0'  */
  p = input_line_pointer;
  p = input_line_pointer;
  *p = c;
  *p = c;
  SKIP_WHITESPACE ();
  SKIP_WHITESPACE ();
 
 
  if (*input_line_pointer != ',')
  if (*input_line_pointer != ',')
    {
    {
      as_bad (_("expected comma after operand name"));
      as_bad (_("expected comma after operand name"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      free (name);
      free (name);
      return;
      return;
    }
    }
 
 
  input_line_pointer++;         /* skip ','  */
  input_line_pointer++;         /* skip ','  */
  number = get_absolute_expression ();
  number = get_absolute_expression ();
 
 
  if (number < 0)
  if (number < 0)
    {
    {
      as_bad (_("negative operand number %d"), number);
      as_bad (_("negative operand number %d"), number);
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      free (name);
      free (name);
      return;
      return;
    }
    }
 
 
  if (opertype)
  if (opertype)
    {
    {
      SKIP_WHITESPACE ();
      SKIP_WHITESPACE ();
 
 
      if (*input_line_pointer != ',')
      if (*input_line_pointer != ',')
        {
        {
          as_bad (_("expected comma after register-number"));
          as_bad (_("expected comma after register-number"));
          ignore_rest_of_line ();
          ignore_rest_of_line ();
          free (name);
          free (name);
          return;
          return;
        }
        }
 
 
      input_line_pointer++;             /* skip ','  */
      input_line_pointer++;             /* skip ','  */
      mode = input_line_pointer;
      mode = input_line_pointer;
 
 
      if (!strncmp (mode, "r|w", 3))
      if (!strncmp (mode, "r|w", 3))
        {
        {
          imode = 0;
          imode = 0;
          input_line_pointer += 3;
          input_line_pointer += 3;
        }
        }
      else
      else
        {
        {
          if (!strncmp (mode, "r", 1))
          if (!strncmp (mode, "r", 1))
            {
            {
              imode = ARC_REGISTER_READONLY;
              imode = ARC_REGISTER_READONLY;
              input_line_pointer += 1;
              input_line_pointer += 1;
            }
            }
          else
          else
            {
            {
              if (strncmp (mode, "w", 1))
              if (strncmp (mode, "w", 1))
                {
                {
                  as_bad (_("invalid mode"));
                  as_bad (_("invalid mode"));
                  ignore_rest_of_line ();
                  ignore_rest_of_line ();
                  free (name);
                  free (name);
                  return;
                  return;
                }
                }
              else
              else
                {
                {
                  imode = ARC_REGISTER_WRITEONLY;
                  imode = ARC_REGISTER_WRITEONLY;
                  input_line_pointer += 1;
                  input_line_pointer += 1;
                }
                }
            }
            }
        }
        }
      SKIP_WHITESPACE ();
      SKIP_WHITESPACE ();
      if (1 == opertype)
      if (1 == opertype)
        {
        {
          if (*input_line_pointer != ',')
          if (*input_line_pointer != ',')
            {
            {
              as_bad (_("expected comma after register-mode"));
              as_bad (_("expected comma after register-mode"));
              ignore_rest_of_line ();
              ignore_rest_of_line ();
              free (name);
              free (name);
              return;
              return;
            }
            }
 
 
          input_line_pointer++;         /* skip ','  */
          input_line_pointer++;         /* skip ','  */
 
 
          if (!strncmp (input_line_pointer, "cannot_shortcut", 15))
          if (!strncmp (input_line_pointer, "cannot_shortcut", 15))
            {
            {
              imode |= arc_get_noshortcut_flag ();
              imode |= arc_get_noshortcut_flag ();
              input_line_pointer += 15;
              input_line_pointer += 15;
            }
            }
          else
          else
            {
            {
              if (strncmp (input_line_pointer, "can_shortcut", 12))
              if (strncmp (input_line_pointer, "can_shortcut", 12))
                {
                {
                  as_bad (_("shortcut designator invalid"));
                  as_bad (_("shortcut designator invalid"));
                  ignore_rest_of_line ();
                  ignore_rest_of_line ();
                  free (name);
                  free (name);
                  return;
                  return;
                }
                }
              else
              else
                {
                {
                  input_line_pointer += 12;
                  input_line_pointer += 12;
                }
                }
            }
            }
        }
        }
    }
    }
 
 
  if ((opertype == 1) && number > 60)
  if ((opertype == 1) && number > 60)
    {
    {
      as_bad (_("core register value (%d) too large"), number);
      as_bad (_("core register value (%d) too large"), number);
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      free (name);
      free (name);
      return;
      return;
    }
    }
 
 
  if ((opertype == 0) && number > 31)
  if ((opertype == 0) && number > 31)
    {
    {
      as_bad (_("condition code value (%d) too large"), number);
      as_bad (_("condition code value (%d) too large"), number);
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      free (name);
      free (name);
      return;
      return;
    }
    }
 
 
  ext_oper = xmalloc (sizeof (struct arc_ext_operand_value));
  ext_oper = xmalloc (sizeof (struct arc_ext_operand_value));
 
 
  if (opertype)
  if (opertype)
    {
    {
      /* If the symbol already exists, point it at the new definition.  */
      /* If the symbol already exists, point it at the new definition.  */
      if ((symbolP = symbol_find (name)))
      if ((symbolP = symbol_find (name)))
        {
        {
          if (S_GET_SEGMENT (symbolP) == reg_section)
          if (S_GET_SEGMENT (symbolP) == reg_section)
            S_SET_VALUE (symbolP, (valueT) &ext_oper->operand);
            S_SET_VALUE (symbolP, (valueT) &ext_oper->operand);
          else
          else
            {
            {
              as_bad (_("attempt to override symbol: %s"), name);
              as_bad (_("attempt to override symbol: %s"), name);
              ignore_rest_of_line ();
              ignore_rest_of_line ();
              free (name);
              free (name);
              free (ext_oper);
              free (ext_oper);
              return;
              return;
            }
            }
        }
        }
      else
      else
        {
        {
          /* If its not there, add it.  */
          /* If its not there, add it.  */
          symbol_table_insert (symbol_create (name, reg_section,
          symbol_table_insert (symbol_create (name, reg_section,
                                              (valueT) &ext_oper->operand,
                                              (valueT) &ext_oper->operand,
                                              &zero_address_frag));
                                              &zero_address_frag));
        }
        }
    }
    }
 
 
  ext_oper->operand.name  = name;
  ext_oper->operand.name  = name;
  ext_oper->operand.value = number;
  ext_oper->operand.value = number;
  ext_oper->operand.type  = arc_operand_type (opertype);
  ext_oper->operand.type  = arc_operand_type (opertype);
  ext_oper->operand.flags = imode;
  ext_oper->operand.flags = imode;
 
 
  ext_oper->next = arc_ext_operands;
  ext_oper->next = arc_ext_operands;
  arc_ext_operands = ext_oper;
  arc_ext_operands = ext_oper;
 
 
  /* OK, now that we know what this operand is, put a description in
  /* OK, now that we know what this operand is, put a description in
     the arc extension section of the output file.  */
     the arc extension section of the output file.  */
 
 
  old_sec    = now_seg;
  old_sec    = now_seg;
  old_subsec = now_subseg;
  old_subsec = now_subseg;
 
 
  arc_set_ext_seg ();
  arc_set_ext_seg ();
 
 
  switch (opertype)
  switch (opertype)
    {
    {
    case 0:
    case 0:
      p = frag_more (1);
      p = frag_more (1);
      *p = 3 + strlen (name) + 1;
      *p = 3 + strlen (name) + 1;
      p = frag_more (1);
      p = frag_more (1);
      *p = EXT_COND_CODE;
      *p = EXT_COND_CODE;
      p = frag_more (1);
      p = frag_more (1);
      *p = number;
      *p = number;
      p = frag_more (strlen (name) + 1);
      p = frag_more (strlen (name) + 1);
      strcpy (p, name);
      strcpy (p, name);
      break;
      break;
    case 1:
    case 1:
      p = frag_more (1);
      p = frag_more (1);
      *p = 3 + strlen (name) + 1;
      *p = 3 + strlen (name) + 1;
      p = frag_more (1);
      p = frag_more (1);
      *p = EXT_CORE_REGISTER;
      *p = EXT_CORE_REGISTER;
      p = frag_more (1);
      p = frag_more (1);
      *p = number;
      *p = number;
      p = frag_more (strlen (name) + 1);
      p = frag_more (strlen (name) + 1);
      strcpy (p, name);
      strcpy (p, name);
      break;
      break;
    case 2:
    case 2:
      p = frag_more (1);
      p = frag_more (1);
      *p = 6 + strlen (name) + 1;
      *p = 6 + strlen (name) + 1;
      p = frag_more (1);
      p = frag_more (1);
      *p = EXT_AUX_REGISTER;
      *p = EXT_AUX_REGISTER;
      p = frag_more (1);
      p = frag_more (1);
      *p = number >> 24 & 0xff;
      *p = number >> 24 & 0xff;
      p = frag_more (1);
      p = frag_more (1);
      *p = number >> 16 & 0xff;
      *p = number >> 16 & 0xff;
      p = frag_more (1);
      p = frag_more (1);
      *p = number >>  8 & 0xff;
      *p = number >>  8 & 0xff;
      p = frag_more (1);
      p = frag_more (1);
      *p = number       & 0xff;
      *p = number       & 0xff;
      p = frag_more (strlen (name) + 1);
      p = frag_more (strlen (name) + 1);
      strcpy (p, name);
      strcpy (p, name);
      break;
      break;
    default:
    default:
      as_bad (_("invalid opertype"));
      as_bad (_("invalid opertype"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      free (name);
      free (name);
      return;
      return;
      break;
      break;
    }
    }
 
 
  subseg_set (old_sec, old_subsec);
  subseg_set (old_sec, old_subsec);
 
 
  /* Enter all registers into the symbol table.  */
  /* Enter all registers into the symbol table.  */
 
 
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
static void
static void
arc_extinst (int ignore ATTRIBUTE_UNUSED)
arc_extinst (int ignore ATTRIBUTE_UNUSED)
{
{
  char syntax[129];
  char syntax[129];
  char *name;
  char *name;
  char *p;
  char *p;
  char c;
  char c;
  int suffixcode = -1;
  int suffixcode = -1;
  int opcode, subopcode;
  int opcode, subopcode;
  int i;
  int i;
  int class = 0;
  int class = 0;
  int name_len;
  int name_len;
  struct arc_opcode *ext_op;
  struct arc_opcode *ext_op;
 
 
  segT old_sec;
  segT old_sec;
  int old_subsec;
  int old_subsec;
 
 
  name = input_line_pointer;
  name = input_line_pointer;
  c = get_symbol_end ();
  c = get_symbol_end ();
  name = xstrdup (name);
  name = xstrdup (name);
  strcpy (syntax, name);
  strcpy (syntax, name);
  name_len = strlen (name);
  name_len = strlen (name);
 
 
  /* just after name is now '\0'  */
  /* just after name is now '\0'  */
  p = input_line_pointer;
  p = input_line_pointer;
  *p = c;
  *p = c;
 
 
  SKIP_WHITESPACE ();
  SKIP_WHITESPACE ();
 
 
  if (*input_line_pointer != ',')
  if (*input_line_pointer != ',')
    {
    {
      as_bad (_("expected comma after operand name"));
      as_bad (_("expected comma after operand name"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  input_line_pointer++;         /* skip ','  */
  input_line_pointer++;         /* skip ','  */
  opcode = get_absolute_expression ();
  opcode = get_absolute_expression ();
 
 
  SKIP_WHITESPACE ();
  SKIP_WHITESPACE ();
 
 
  if (*input_line_pointer != ',')
  if (*input_line_pointer != ',')
    {
    {
      as_bad (_("expected comma after opcode"));
      as_bad (_("expected comma after opcode"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  input_line_pointer++;         /* skip ','  */
  input_line_pointer++;         /* skip ','  */
  subopcode = get_absolute_expression ();
  subopcode = get_absolute_expression ();
 
 
  if (subopcode < 0)
  if (subopcode < 0)
    {
    {
      as_bad (_("negative subopcode %d"), subopcode);
      as_bad (_("negative subopcode %d"), subopcode);
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  if (subopcode)
  if (subopcode)
    {
    {
      if (3 != opcode)
      if (3 != opcode)
        {
        {
          as_bad (_("subcode value found when opcode not equal 0x03"));
          as_bad (_("subcode value found when opcode not equal 0x03"));
          ignore_rest_of_line ();
          ignore_rest_of_line ();
          return;
          return;
        }
        }
      else
      else
        {
        {
          if (subopcode < 0x09 || subopcode == 0x3f)
          if (subopcode < 0x09 || subopcode == 0x3f)
            {
            {
              as_bad (_("invalid subopcode %d"), subopcode);
              as_bad (_("invalid subopcode %d"), subopcode);
              ignore_rest_of_line ();
              ignore_rest_of_line ();
              return;
              return;
            }
            }
        }
        }
    }
    }
 
 
  SKIP_WHITESPACE ();
  SKIP_WHITESPACE ();
 
 
  if (*input_line_pointer != ',')
  if (*input_line_pointer != ',')
    {
    {
      as_bad (_("expected comma after subopcode"));
      as_bad (_("expected comma after subopcode"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  input_line_pointer++;         /* skip ','  */
  input_line_pointer++;         /* skip ','  */
 
 
  for (i = 0; i < (int) MAXSUFFIXCLASS; i++)
  for (i = 0; i < (int) MAXSUFFIXCLASS; i++)
    {
    {
      if (!strncmp (suffixclass[i].name,input_line_pointer, suffixclass[i].len))
      if (!strncmp (suffixclass[i].name,input_line_pointer, suffixclass[i].len))
        {
        {
          suffixcode = i;
          suffixcode = i;
          input_line_pointer += suffixclass[i].len;
          input_line_pointer += suffixclass[i].len;
          break;
          break;
        }
        }
    }
    }
 
 
  if (-1 == suffixcode)
  if (-1 == suffixcode)
    {
    {
      as_bad (_("invalid suffix class"));
      as_bad (_("invalid suffix class"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  SKIP_WHITESPACE ();
  SKIP_WHITESPACE ();
 
 
  if (*input_line_pointer != ',')
  if (*input_line_pointer != ',')
    {
    {
      as_bad (_("expected comma after suffix class"));
      as_bad (_("expected comma after suffix class"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  input_line_pointer++;         /* skip ','  */
  input_line_pointer++;         /* skip ','  */
 
 
  for (i = 0; i < (int) MAXSYNTAXCLASS; i++)
  for (i = 0; i < (int) MAXSYNTAXCLASS; i++)
    {
    {
      if (!strncmp (syntaxclass[i].name,input_line_pointer, syntaxclass[i].len))
      if (!strncmp (syntaxclass[i].name,input_line_pointer, syntaxclass[i].len))
        {
        {
          class = syntaxclass[i].class;
          class = syntaxclass[i].class;
          input_line_pointer += syntaxclass[i].len;
          input_line_pointer += syntaxclass[i].len;
          break;
          break;
        }
        }
    }
    }
 
 
  if (0 == (SYNTAX_VALID & class))
  if (0 == (SYNTAX_VALID & class))
    {
    {
      as_bad (_("invalid syntax class"));
      as_bad (_("invalid syntax class"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  if ((0x3 == opcode) & (class & SYNTAX_3OP))
  if ((0x3 == opcode) & (class & SYNTAX_3OP))
    {
    {
      as_bad (_("opcode 0x3 and SYNTAX_3OP invalid"));
      as_bad (_("opcode 0x3 and SYNTAX_3OP invalid"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  switch (suffixcode)
  switch (suffixcode)
    {
    {
    case 0:
    case 0:
      strcat (syntax, "%.q%.f ");
      strcat (syntax, "%.q%.f ");
      break;
      break;
    case 1:
    case 1:
      strcat (syntax, "%.f ");
      strcat (syntax, "%.f ");
      break;
      break;
    case 2:
    case 2:
      strcat (syntax, "%.q ");
      strcat (syntax, "%.q ");
      break;
      break;
    case 3:
    case 3:
      strcat (syntax, " ");
      strcat (syntax, " ");
      break;
      break;
    default:
    default:
      as_bad (_("unknown suffix class"));
      as_bad (_("unknown suffix class"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
      break;
      break;
    };
    };
 
 
  strcat (syntax, ((opcode == 0x3) ? "%a,%b" : ((class & SYNTAX_3OP) ? "%a,%b,%c" : "%b,%c")));
  strcat (syntax, ((opcode == 0x3) ? "%a,%b" : ((class & SYNTAX_3OP) ? "%a,%b,%c" : "%b,%c")));
  if (suffixcode < 2)
  if (suffixcode < 2)
    strcat (syntax, "%F");
    strcat (syntax, "%F");
  strcat (syntax, "%S%L");
  strcat (syntax, "%S%L");
 
 
  ext_op = xmalloc (sizeof (struct arc_opcode));
  ext_op = xmalloc (sizeof (struct arc_opcode));
  ext_op->syntax = xstrdup (syntax);
  ext_op->syntax = xstrdup (syntax);
 
 
  ext_op->mask  = I (-1) | ((0x3 == opcode) ? C (-1) : 0);
  ext_op->mask  = I (-1) | ((0x3 == opcode) ? C (-1) : 0);
  ext_op->value = I (opcode) | ((0x3 == opcode) ? C (subopcode) : 0);
  ext_op->value = I (opcode) | ((0x3 == opcode) ? C (subopcode) : 0);
  ext_op->flags = class;
  ext_op->flags = class;
  ext_op->next_asm = arc_ext_opcodes;
  ext_op->next_asm = arc_ext_opcodes;
  ext_op->next_dis = arc_ext_opcodes;
  ext_op->next_dis = arc_ext_opcodes;
  arc_ext_opcodes = ext_op;
  arc_ext_opcodes = ext_op;
 
 
  /* OK, now that we know what this inst is, put a description in the
  /* OK, now that we know what this inst is, put a description in the
     arc extension section of the output file.  */
     arc extension section of the output file.  */
 
 
  old_sec    = now_seg;
  old_sec    = now_seg;
  old_subsec = now_subseg;
  old_subsec = now_subseg;
 
 
  arc_set_ext_seg ();
  arc_set_ext_seg ();
 
 
  p = frag_more (1);
  p = frag_more (1);
  *p = 5 + name_len + 1;
  *p = 5 + name_len + 1;
  p = frag_more (1);
  p = frag_more (1);
  *p = EXT_INSTRUCTION;
  *p = EXT_INSTRUCTION;
  p = frag_more (1);
  p = frag_more (1);
  *p = opcode;
  *p = opcode;
  p = frag_more (1);
  p = frag_more (1);
  *p = subopcode;
  *p = subopcode;
  p = frag_more (1);
  p = frag_more (1);
  *p = (class & (OP1_MUST_BE_IMM | OP1_IMM_IMPLIED) ? IGNORE_FIRST_OPD : 0);
  *p = (class & (OP1_MUST_BE_IMM | OP1_IMM_IMPLIED) ? IGNORE_FIRST_OPD : 0);
  p = frag_more (name_len);
  p = frag_more (name_len);
  strncpy (p, syntax, name_len);
  strncpy (p, syntax, name_len);
  p = frag_more (1);
  p = frag_more (1);
  *p = '\0';
  *p = '\0';
 
 
  subseg_set (old_sec, old_subsec);
  subseg_set (old_sec, old_subsec);
 
 
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
static void
static void
arc_common (int localScope)
arc_common (int localScope)
{
{
  char *name;
  char *name;
  char c;
  char c;
  char *p;
  char *p;
  int align, size;
  int align, size;
  symbolS *symbolP;
  symbolS *symbolP;
 
 
  name = input_line_pointer;
  name = input_line_pointer;
  c = get_symbol_end ();
  c = get_symbol_end ();
  /* just after name is now '\0'  */
  /* just after name is now '\0'  */
  p = input_line_pointer;
  p = input_line_pointer;
  *p = c;
  *p = c;
  SKIP_WHITESPACE ();
  SKIP_WHITESPACE ();
 
 
  if (*input_line_pointer != ',')
  if (*input_line_pointer != ',')
    {
    {
      as_bad (_("expected comma after symbol name"));
      as_bad (_("expected comma after symbol name"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  input_line_pointer++;         /* skip ','  */
  input_line_pointer++;         /* skip ','  */
  size = get_absolute_expression ();
  size = get_absolute_expression ();
 
 
  if (size < 0)
  if (size < 0)
    {
    {
      as_bad (_("negative symbol length"));
      as_bad (_("negative symbol length"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  *p = 0;
  *p = 0;
  symbolP = symbol_find_or_make (name);
  symbolP = symbol_find_or_make (name);
  *p = c;
  *p = c;
 
 
  if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
  if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
    {
    {
      as_bad (_("ignoring attempt to re-define symbol"));
      as_bad (_("ignoring attempt to re-define symbol"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
  if (((int) S_GET_VALUE (symbolP) != 0) \
  if (((int) S_GET_VALUE (symbolP) != 0) \
      && ((int) S_GET_VALUE (symbolP) != size))
      && ((int) S_GET_VALUE (symbolP) != size))
    {
    {
      as_warn (_("length of symbol \"%s\" already %ld, ignoring %d"),
      as_warn (_("length of symbol \"%s\" already %ld, ignoring %d"),
               S_GET_NAME (symbolP), (long) S_GET_VALUE (symbolP), size);
               S_GET_NAME (symbolP), (long) S_GET_VALUE (symbolP), size);
    }
    }
  assert (symbolP->sy_frag == &zero_address_frag);
  assert (symbolP->sy_frag == &zero_address_frag);
 
 
  /* Now parse the alignment field.  This field is optional for
  /* Now parse the alignment field.  This field is optional for
     local and global symbols. Default alignment is zero.  */
     local and global symbols. Default alignment is zero.  */
  if (*input_line_pointer == ',')
  if (*input_line_pointer == ',')
    {
    {
      input_line_pointer++;
      input_line_pointer++;
      align = get_absolute_expression ();
      align = get_absolute_expression ();
      if (align < 0)
      if (align < 0)
        {
        {
          align = 0;
          align = 0;
          as_warn (_("assuming symbol alignment of zero"));
          as_warn (_("assuming symbol alignment of zero"));
        }
        }
    }
    }
  else
  else
    align = 0;
    align = 0;
 
 
  if (localScope != 0)
  if (localScope != 0)
    {
    {
      segT old_sec;
      segT old_sec;
      int old_subsec;
      int old_subsec;
      char *pfrag;
      char *pfrag;
 
 
      old_sec    = now_seg;
      old_sec    = now_seg;
      old_subsec = now_subseg;
      old_subsec = now_subseg;
      record_alignment (bss_section, align);
      record_alignment (bss_section, align);
      subseg_set (bss_section, 0);  /* ??? subseg_set (bss_section, 1); ???  */
      subseg_set (bss_section, 0);  /* ??? subseg_set (bss_section, 1); ???  */
 
 
      if (align)
      if (align)
        /* Do alignment.  */
        /* Do alignment.  */
        frag_align (align, 0, 0);
        frag_align (align, 0, 0);
 
 
      /* Detach from old frag.  */
      /* Detach from old frag.  */
      if (S_GET_SEGMENT (symbolP) == bss_section)
      if (S_GET_SEGMENT (symbolP) == bss_section)
        symbolP->sy_frag->fr_symbol = NULL;
        symbolP->sy_frag->fr_symbol = NULL;
 
 
      symbolP->sy_frag = frag_now;
      symbolP->sy_frag = frag_now;
      pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
      pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
                        (offsetT) size, (char *) 0);
                        (offsetT) size, (char *) 0);
      *pfrag = 0;
      *pfrag = 0;
 
 
      S_SET_SIZE       (symbolP, size);
      S_SET_SIZE       (symbolP, size);
      S_SET_SEGMENT    (symbolP, bss_section);
      S_SET_SEGMENT    (symbolP, bss_section);
      S_CLEAR_EXTERNAL (symbolP);
      S_CLEAR_EXTERNAL (symbolP);
      symbolP->local = 1;
      symbolP->local = 1;
      subseg_set (old_sec, old_subsec);
      subseg_set (old_sec, old_subsec);
    }
    }
  else
  else
    {
    {
      S_SET_VALUE    (symbolP, (valueT) size);
      S_SET_VALUE    (symbolP, (valueT) size);
      S_SET_ALIGN    (symbolP, align);
      S_SET_ALIGN    (symbolP, align);
      S_SET_EXTERNAL (symbolP);
      S_SET_EXTERNAL (symbolP);
      S_SET_SEGMENT  (symbolP, bfd_com_section_ptr);
      S_SET_SEGMENT  (symbolP, bfd_com_section_ptr);
    }
    }
 
 
  symbolP->bsym->flags |= BSF_OBJECT;
  symbolP->bsym->flags |= BSF_OBJECT;
 
 
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}


/* Select the cpu we're assembling for.  */
/* Select the cpu we're assembling for.  */
 
 
static void
static void
arc_option (int ignore ATTRIBUTE_UNUSED)
arc_option (int ignore ATTRIBUTE_UNUSED)
{
{
  extern int arc_get_mach (char *);
  extern int arc_get_mach (char *);
  int mach;
  int mach;
  char c;
  char c;
  char *cpu;
  char *cpu;
 
 
  cpu = input_line_pointer;
  cpu = input_line_pointer;
  c = get_symbol_end ();
  c = get_symbol_end ();
  mach = arc_get_mach (cpu);
  mach = arc_get_mach (cpu);
  *input_line_pointer = c;
  *input_line_pointer = c;
 
 
  /* If an instruction has already been seen, it's too late.  */
  /* If an instruction has already been seen, it's too late.  */
  if (cpu_tables_init_p)
  if (cpu_tables_init_p)
    {
    {
      as_bad (_("\".option\" directive must appear before any instructions"));
      as_bad (_("\".option\" directive must appear before any instructions"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  if (mach == -1)
  if (mach == -1)
    goto bad_cpu;
    goto bad_cpu;
 
 
  if (mach_type_specified_p && mach != arc_mach_type)
  if (mach_type_specified_p && mach != arc_mach_type)
    {
    {
      as_bad (_("\".option\" directive conflicts with initial definition"));
      as_bad (_("\".option\" directive conflicts with initial definition"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
  else
  else
    {
    {
      /* The cpu may have been selected on the command line.  */
      /* The cpu may have been selected on the command line.  */
      if (mach != arc_mach_type)
      if (mach != arc_mach_type)
        as_warn (_("\".option\" directive overrides command-line (default) value"));
        as_warn (_("\".option\" directive overrides command-line (default) value"));
      arc_mach_type = mach;
      arc_mach_type = mach;
      if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach))
      if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach))
        as_fatal (_("could not set architecture and machine"));
        as_fatal (_("could not set architecture and machine"));
      mach_type_specified_p = 1;
      mach_type_specified_p = 1;
    }
    }
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
  return;
  return;
 
 
 bad_cpu:
 bad_cpu:
  as_bad (_("invalid identifier for \".option\""));
  as_bad (_("invalid identifier for \".option\""));
  ignore_rest_of_line ();
  ignore_rest_of_line ();
}
}


char *
char *
md_atof (int type, char *litP, int *sizeP)
md_atof (int type, char *litP, int *sizeP)
{
{
  return ieee_md_atof (type, litP, sizeP, TRUE);
  return ieee_md_atof (type, litP, sizeP, TRUE);
}
}
 
 
/* Write a value out to the object file, using the appropriate
/* Write a value out to the object file, using the appropriate
   endianness.  */
   endianness.  */
 
 
void
void
md_number_to_chars (char *buf, valueT val, int n)
md_number_to_chars (char *buf, valueT val, int n)
{
{
  if (target_big_endian)
  if (target_big_endian)
    number_to_chars_bigendian (buf, val, n);
    number_to_chars_bigendian (buf, val, n);
  else
  else
    number_to_chars_littleendian (buf, val, n);
    number_to_chars_littleendian (buf, val, n);
}
}
 
 
/* Round up a section size to the appropriate boundary.  */
/* Round up a section size to the appropriate boundary.  */
 
 
valueT
valueT
md_section_align (segT segment, valueT size)
md_section_align (segT segment, valueT size)
{
{
  int align = bfd_get_section_alignment (stdoutput, segment);
  int align = bfd_get_section_alignment (stdoutput, segment);
 
 
  return ((size + (1 << align) - 1) & (-1 << align));
  return ((size + (1 << align) - 1) & (-1 << align));
}
}
 
 
/* We don't have any form of relaxing.  */
/* We don't have any form of relaxing.  */
 
 
int
int
md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
                               asection *seg ATTRIBUTE_UNUSED)
                               asection *seg ATTRIBUTE_UNUSED)
{
{
  as_fatal (_("relaxation not supported\n"));
  as_fatal (_("relaxation not supported\n"));
  return 1;
  return 1;
}
}
 
 
/* Convert a machine dependent frag.  We never generate these.  */
/* Convert a machine dependent frag.  We never generate these.  */
 
 
void
void
md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
                 asection *sec ATTRIBUTE_UNUSED,
                 asection *sec ATTRIBUTE_UNUSED,
                 fragS *fragp ATTRIBUTE_UNUSED)
                 fragS *fragp ATTRIBUTE_UNUSED)
{
{
  abort ();
  abort ();
}
}
 
 
static void
static void
arc_code_symbol (expressionS *expressionP)
arc_code_symbol (expressionS *expressionP)
{
{
  if (expressionP->X_op == O_symbol && expressionP->X_add_number == 0)
  if (expressionP->X_op == O_symbol && expressionP->X_add_number == 0)
    {
    {
      expressionS two;
      expressionS two;
 
 
      expressionP->X_op = O_right_shift;
      expressionP->X_op = O_right_shift;
      expressionP->X_add_symbol->sy_value.X_op = O_constant;
      expressionP->X_add_symbol->sy_value.X_op = O_constant;
      two.X_op = O_constant;
      two.X_op = O_constant;
      two.X_add_symbol = two.X_op_symbol = NULL;
      two.X_add_symbol = two.X_op_symbol = NULL;
      two.X_add_number = 2;
      two.X_add_number = 2;
      expressionP->X_op_symbol = make_expr_symbol (&two);
      expressionP->X_op_symbol = make_expr_symbol (&two);
    }
    }
  /* Allow %st(sym1-sym2)  */
  /* Allow %st(sym1-sym2)  */
  else if (expressionP->X_op == O_subtract
  else if (expressionP->X_op == O_subtract
           && expressionP->X_add_symbol != NULL
           && expressionP->X_add_symbol != NULL
           && expressionP->X_op_symbol != NULL
           && expressionP->X_op_symbol != NULL
           && expressionP->X_add_number == 0)
           && expressionP->X_add_number == 0)
    {
    {
      expressionS two;
      expressionS two;
 
 
      expressionP->X_add_symbol = make_expr_symbol (expressionP);
      expressionP->X_add_symbol = make_expr_symbol (expressionP);
      expressionP->X_op = O_right_shift;
      expressionP->X_op = O_right_shift;
      two.X_op = O_constant;
      two.X_op = O_constant;
      two.X_add_symbol = two.X_op_symbol = NULL;
      two.X_add_symbol = two.X_op_symbol = NULL;
      two.X_add_number = 2;
      two.X_add_number = 2;
      expressionP->X_op_symbol = make_expr_symbol (&two);
      expressionP->X_op_symbol = make_expr_symbol (&two);
    }
    }
  else
  else
    as_bad (_("expression too complex code symbol"));
    as_bad (_("expression too complex code symbol"));
}
}
 
 
/* Parse an operand that is machine-specific.
/* Parse an operand that is machine-specific.
 
 
   The ARC has a special %-op to adjust addresses so they're usable in
   The ARC has a special %-op to adjust addresses so they're usable in
   branches.  The "st" is short for the STatus register.
   branches.  The "st" is short for the STatus register.
   ??? Later expand this to take a flags value too.
   ??? Later expand this to take a flags value too.
 
 
   ??? We can't create new expression types so we map the %-op's onto the
   ??? We can't create new expression types so we map the %-op's onto the
   existing syntax.  This means that the user could use the chosen syntax
   existing syntax.  This means that the user could use the chosen syntax
   to achieve the same effect.  */
   to achieve the same effect.  */
 
 
void
void
md_operand (expressionS *expressionP)
md_operand (expressionS *expressionP)
{
{
  char *p = input_line_pointer;
  char *p = input_line_pointer;
 
 
  if (*p != '%')
  if (*p != '%')
    return;
    return;
 
 
  if (strncmp (p, "%st(", 4) == 0)
  if (strncmp (p, "%st(", 4) == 0)
    {
    {
      input_line_pointer += 4;
      input_line_pointer += 4;
      expression (expressionP);
      expression (expressionP);
      if (*input_line_pointer != ')')
      if (*input_line_pointer != ')')
        {
        {
          as_bad (_("missing ')' in %%-op"));
          as_bad (_("missing ')' in %%-op"));
          return;
          return;
        }
        }
      ++input_line_pointer;
      ++input_line_pointer;
      arc_code_symbol (expressionP);
      arc_code_symbol (expressionP);
    }
    }
  else
  else
    {
    {
      /* It could be a register.  */
      /* It could be a register.  */
      int i, l;
      int i, l;
      struct arc_ext_operand_value *ext_oper = arc_ext_operands;
      struct arc_ext_operand_value *ext_oper = arc_ext_operands;
      p++;
      p++;
 
 
      while (ext_oper)
      while (ext_oper)
        {
        {
          l = strlen (ext_oper->operand.name);
          l = strlen (ext_oper->operand.name);
          if (!strncmp (p, ext_oper->operand.name, l) && !ISALNUM (*(p + l)))
          if (!strncmp (p, ext_oper->operand.name, l) && !ISALNUM (*(p + l)))
            {
            {
              input_line_pointer += l + 1;
              input_line_pointer += l + 1;
              expressionP->X_op = O_register;
              expressionP->X_op = O_register;
              expressionP->X_add_number = (offsetT) &ext_oper->operand;
              expressionP->X_add_number = (offsetT) &ext_oper->operand;
              return;
              return;
            }
            }
          ext_oper = ext_oper->next;
          ext_oper = ext_oper->next;
        }
        }
      for (i = 0; i < arc_reg_names_count; i++)
      for (i = 0; i < arc_reg_names_count; i++)
        {
        {
          l = strlen (arc_reg_names[i].name);
          l = strlen (arc_reg_names[i].name);
          if (!strncmp (p, arc_reg_names[i].name, l) && !ISALNUM (*(p + l)))
          if (!strncmp (p, arc_reg_names[i].name, l) && !ISALNUM (*(p + l)))
            {
            {
              input_line_pointer += l + 1;
              input_line_pointer += l + 1;
              expressionP->X_op = O_register;
              expressionP->X_op = O_register;
              expressionP->X_add_number = (offsetT) &arc_reg_names[i];
              expressionP->X_add_number = (offsetT) &arc_reg_names[i];
              break;
              break;
            }
            }
        }
        }
    }
    }
}
}
 
 
/* We have no need to default values of symbols.
/* We have no need to default values of symbols.
   We could catch register names here, but that is handled by inserting
   We could catch register names here, but that is handled by inserting
   them all in the symbol table to begin with.  */
   them all in the symbol table to begin with.  */
 
 
symbolS *
symbolS *
md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
{
{
  return 0;
  return 0;
}
}


/* Functions concerning expressions.  */
/* Functions concerning expressions.  */
 
 
/* Parse a .byte, .word, etc. expression.
/* Parse a .byte, .word, etc. expression.
 
 
   Values for the status register are specified with %st(label).
   Values for the status register are specified with %st(label).
   `label' will be right shifted by 2.  */
   `label' will be right shifted by 2.  */
 
 
void
void
arc_parse_cons_expression (expressionS *exp,
arc_parse_cons_expression (expressionS *exp,
                           unsigned int nbytes ATTRIBUTE_UNUSED)
                           unsigned int nbytes ATTRIBUTE_UNUSED)
{
{
  char *p = input_line_pointer;
  char *p = input_line_pointer;
  int code_symbol_fix = 0;
  int code_symbol_fix = 0;
 
 
  for (; ! is_end_of_line[(unsigned char) *p]; p++)
  for (; ! is_end_of_line[(unsigned char) *p]; p++)
    if (*p == '@' && !strncmp (p, "@h30", 4))
    if (*p == '@' && !strncmp (p, "@h30", 4))
      {
      {
        code_symbol_fix = 1;
        code_symbol_fix = 1;
        strcpy (p, ";   ");
        strcpy (p, ";   ");
      }
      }
  expression_and_evaluate (exp);
  expression_and_evaluate (exp);
  if (code_symbol_fix)
  if (code_symbol_fix)
    {
    {
      arc_code_symbol (exp);
      arc_code_symbol (exp);
      input_line_pointer = p;
      input_line_pointer = p;
    }
    }
}
}
 
 
/* Record a fixup for a cons expression.  */
/* Record a fixup for a cons expression.  */
 
 
void
void
arc_cons_fix_new (fragS *frag,
arc_cons_fix_new (fragS *frag,
                  int where,
                  int where,
                  int nbytes,
                  int nbytes,
                  expressionS *exp)
                  expressionS *exp)
{
{
  if (nbytes == 4)
  if (nbytes == 4)
    {
    {
      int reloc_type;
      int reloc_type;
      expressionS exptmp;
      expressionS exptmp;
 
 
      /* This may be a special ARC reloc (eg: %st()).  */
      /* This may be a special ARC reloc (eg: %st()).  */
      reloc_type = get_arc_exp_reloc_type (1, BFD_RELOC_32, exp, &exptmp);
      reloc_type = get_arc_exp_reloc_type (1, BFD_RELOC_32, exp, &exptmp);
      fix_new_exp (frag, where, nbytes, &exptmp, 0, reloc_type);
      fix_new_exp (frag, where, nbytes, &exptmp, 0, reloc_type);
    }
    }
  else
  else
    {
    {
      fix_new_exp (frag, where, nbytes, exp, 0,
      fix_new_exp (frag, where, nbytes, exp, 0,
                   nbytes == 2 ? BFD_RELOC_16
                   nbytes == 2 ? BFD_RELOC_16
                   : nbytes == 8 ? BFD_RELOC_64
                   : nbytes == 8 ? BFD_RELOC_64
                   : BFD_RELOC_32);
                   : BFD_RELOC_32);
    }
    }
}
}


/* Functions concerning relocs.  */
/* Functions concerning relocs.  */
 
 
/* The location from which a PC relative jump should be calculated,
/* The location from which a PC relative jump should be calculated,
   given a PC relative reloc.  */
   given a PC relative reloc.  */
 
 
long
long
md_pcrel_from (fixS *fixP)
md_pcrel_from (fixS *fixP)
{
{
  /* Return the address of the delay slot.  */
  /* Return the address of the delay slot.  */
  return fixP->fx_frag->fr_address + fixP->fx_where + fixP->fx_size;
  return fixP->fx_frag->fr_address + fixP->fx_where + fixP->fx_size;
}
}
 
 
/* Apply a fixup to the object code.  This is called for all the
/* Apply a fixup to the object code.  This is called for all the
   fixups we generated by the call to fix_new_exp, above.  In the call
   fixups we generated by the call to fix_new_exp, above.  In the call
   above we used a reloc code which was the largest legal reloc code
   above we used a reloc code which was the largest legal reloc code
   plus the operand index.  Here we undo that to recover the operand
   plus the operand index.  Here we undo that to recover the operand
   index.  At this point all symbol values should be fully resolved,
   index.  At this point all symbol values should be fully resolved,
   and we attempt to completely resolve the reloc.  If we can not do
   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.  */
   that, we determine the correct reloc code and put it back in the fixup.  */
 
 
void
void
md_apply_fix (fixS *fixP, valueT * valP, segT seg)
md_apply_fix (fixS *fixP, valueT * valP, segT seg)
{
{
  valueT value = * valP;
  valueT value = * valP;
 
 
  if (fixP->fx_addsy == (symbolS *) NULL)
  if (fixP->fx_addsy == (symbolS *) NULL)
    fixP->fx_done = 1;
    fixP->fx_done = 1;
 
 
  else if (fixP->fx_pcrel)
  else if (fixP->fx_pcrel)
    {
    {
      /* Hack around bfd_install_relocation brain damage.  */
      /* Hack around bfd_install_relocation brain damage.  */
      if (S_GET_SEGMENT (fixP->fx_addsy) != seg)
      if (S_GET_SEGMENT (fixP->fx_addsy) != seg)
        value += md_pcrel_from (fixP);
        value += md_pcrel_from (fixP);
    }
    }
 
 
  /* We can't actually support subtracting a symbol.  */
  /* We can't actually support subtracting a symbol.  */
  if (fixP->fx_subsy != NULL)
  if (fixP->fx_subsy != NULL)
    as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
    as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
 
 
  if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
  if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
    {
    {
      int opindex;
      int opindex;
      const struct arc_operand *operand;
      const struct arc_operand *operand;
      char *where;
      char *where;
      arc_insn insn;
      arc_insn insn;
 
 
      opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
      opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
 
 
      operand = &arc_operands[opindex];
      operand = &arc_operands[opindex];
 
 
      /* Fetch the instruction, insert the fully resolved operand
      /* Fetch the instruction, insert the fully resolved operand
         value, and stuff the instruction back again.  */
         value, and stuff the instruction back again.  */
      where = fixP->fx_frag->fr_literal + fixP->fx_where;
      where = fixP->fx_frag->fr_literal + fixP->fx_where;
      if (target_big_endian)
      if (target_big_endian)
        insn = bfd_getb32 ((unsigned char *) where);
        insn = bfd_getb32 ((unsigned char *) where);
      else
      else
        insn = bfd_getl32 ((unsigned char *) where);
        insn = bfd_getl32 ((unsigned char *) where);
      insn = arc_insert_operand (insn, operand, -1, NULL, (offsetT) value,
      insn = arc_insert_operand (insn, operand, -1, NULL, (offsetT) value,
                                 fixP->fx_file, fixP->fx_line);
                                 fixP->fx_file, fixP->fx_line);
      if (target_big_endian)
      if (target_big_endian)
        bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
        bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
      else
      else
        bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
        bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
 
 
      if (fixP->fx_done)
      if (fixP->fx_done)
        /* Nothing else to do here.  */
        /* Nothing else to do here.  */
        return;
        return;
 
 
      /* Determine a BFD reloc value based on the operand information.
      /* Determine a BFD reloc value based on the operand information.
         We are only prepared to turn a few of the operands into relocs.
         We are only prepared to turn a few of the operands into relocs.
         !!! Note that we can't handle limm values here.  Since we're using
         !!! Note that we can't handle limm values here.  Since we're using
         implicit addends the addend must be inserted into the instruction,
         implicit addends the addend must be inserted into the instruction,
         however, the opcode insertion routines currently do nothing with
         however, the opcode insertion routines currently do nothing with
         limm values.  */
         limm values.  */
      if (operand->fmt == 'B')
      if (operand->fmt == 'B')
        {
        {
          assert ((operand->flags & ARC_OPERAND_RELATIVE_BRANCH) != 0
          assert ((operand->flags & ARC_OPERAND_RELATIVE_BRANCH) != 0
                  && operand->bits == 20
                  && operand->bits == 20
                  && operand->shift == 7);
                  && operand->shift == 7);
          fixP->fx_r_type = BFD_RELOC_ARC_B22_PCREL;
          fixP->fx_r_type = BFD_RELOC_ARC_B22_PCREL;
        }
        }
      else if (operand->fmt == 'J')
      else if (operand->fmt == 'J')
        {
        {
          assert ((operand->flags & ARC_OPERAND_ABSOLUTE_BRANCH) != 0
          assert ((operand->flags & ARC_OPERAND_ABSOLUTE_BRANCH) != 0
                  && operand->bits == 24
                  && operand->bits == 24
                  && operand->shift == 32);
                  && operand->shift == 32);
          fixP->fx_r_type = BFD_RELOC_ARC_B26;
          fixP->fx_r_type = BFD_RELOC_ARC_B26;
        }
        }
      else if (operand->fmt == 'L')
      else if (operand->fmt == 'L')
        {
        {
          assert ((operand->flags & ARC_OPERAND_LIMM) != 0
          assert ((operand->flags & ARC_OPERAND_LIMM) != 0
                  && operand->bits == 32
                  && operand->bits == 32
                  && operand->shift == 32);
                  && operand->shift == 32);
          fixP->fx_r_type = BFD_RELOC_32;
          fixP->fx_r_type = BFD_RELOC_32;
        }
        }
      else
      else
        {
        {
          as_bad_where (fixP->fx_file, fixP->fx_line,
          as_bad_where (fixP->fx_file, fixP->fx_line,
                        _("unresolved expression that must be resolved"));
                        _("unresolved expression that must be resolved"));
          fixP->fx_done = 1;
          fixP->fx_done = 1;
          return;
          return;
        }
        }
    }
    }
  else
  else
    {
    {
      switch (fixP->fx_r_type)
      switch (fixP->fx_r_type)
        {
        {
        case BFD_RELOC_8:
        case BFD_RELOC_8:
          md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
          md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
                              value, 1);
                              value, 1);
          break;
          break;
        case BFD_RELOC_16:
        case BFD_RELOC_16:
          md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
          md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
                              value, 2);
                              value, 2);
          break;
          break;
        case BFD_RELOC_32:
        case BFD_RELOC_32:
          md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
          md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
                              value, 4);
                              value, 4);
          break;
          break;
        case BFD_RELOC_ARC_B26:
        case BFD_RELOC_ARC_B26:
          /* If !fixP->fx_done then `value' is an implicit addend.
          /* If !fixP->fx_done then `value' is an implicit addend.
             We must shift it right by 2 in this case as well because the
             We must shift it right by 2 in this case as well because the
             linker performs the relocation and then adds this in (as opposed
             linker performs the relocation and then adds this in (as opposed
             to adding this in and then shifting right by 2).  */
             to adding this in and then shifting right by 2).  */
          value >>= 2;
          value >>= 2;
          md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
          md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
                              value, 4);
                              value, 4);
          break;
          break;
        default:
        default:
          abort ();
          abort ();
        }
        }
    }
    }
}
}
 
 
/* Translate internal representation of relocation info to BFD target
/* Translate internal representation of relocation info to BFD target
   format.  */
   format.  */
 
 
arelent *
arelent *
tc_gen_reloc (asection *section ATTRIBUTE_UNUSED,
tc_gen_reloc (asection *section ATTRIBUTE_UNUSED,
              fixS *fixP)
              fixS *fixP)
{
{
  arelent *reloc;
  arelent *reloc;
 
 
  reloc = xmalloc (sizeof (arelent));
  reloc = xmalloc (sizeof (arelent));
  reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
  reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
 
 
  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
  reloc->address = fixP->fx_frag->fr_address + fixP->fx_where;
  reloc->address = fixP->fx_frag->fr_address + fixP->fx_where;
  reloc->howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
  reloc->howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
  if (reloc->howto == (reloc_howto_type *) NULL)
  if (reloc->howto == (reloc_howto_type *) NULL)
    {
    {
      as_bad_where (fixP->fx_file, fixP->fx_line,
      as_bad_where (fixP->fx_file, fixP->fx_line,
                    _("internal error: can't export reloc type %d (`%s')"),
                    _("internal error: can't export reloc type %d (`%s')"),
                    fixP->fx_r_type,
                    fixP->fx_r_type,
                    bfd_get_reloc_code_name (fixP->fx_r_type));
                    bfd_get_reloc_code_name (fixP->fx_r_type));
      return NULL;
      return NULL;
    }
    }
 
 
  assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
  assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
 
 
  /* Set addend to account for PC being advanced one insn before the
  /* Set addend to account for PC being advanced one insn before the
     target address is computed.  */
     target address is computed.  */
 
 
  reloc->addend = (fixP->fx_pcrel ? -4 : 0);
  reloc->addend = (fixP->fx_pcrel ? -4 : 0);
 
 
  return reloc;
  return reloc;
}
}
 
 
const pseudo_typeS md_pseudo_table[] =
const pseudo_typeS md_pseudo_table[] =
{
{
  { "align", s_align_bytes, 0 }, /* Defaulting is invalid (0).  */
  { "align", s_align_bytes, 0 }, /* Defaulting is invalid (0).  */
  { "comm", arc_common, 0 },
  { "comm", arc_common, 0 },
  { "common", arc_common, 0 },
  { "common", arc_common, 0 },
  { "lcomm", arc_common, 1 },
  { "lcomm", arc_common, 1 },
  { "lcommon", arc_common, 1 },
  { "lcommon", arc_common, 1 },
  { "2byte", cons, 2 },
  { "2byte", cons, 2 },
  { "half", cons, 2 },
  { "half", cons, 2 },
  { "short", cons, 2 },
  { "short", cons, 2 },
  { "3byte", cons, 3 },
  { "3byte", cons, 3 },
  { "4byte", cons, 4 },
  { "4byte", cons, 4 },
  { "word", cons, 4 },
  { "word", cons, 4 },
  { "option", arc_option, 0 },
  { "option", arc_option, 0 },
  { "cpu", arc_option, 0 },
  { "cpu", arc_option, 0 },
  { "block", s_space, 0 },
  { "block", s_space, 0 },
  { "extcondcode", arc_extoper, 0 },
  { "extcondcode", arc_extoper, 0 },
  { "extcoreregister", arc_extoper, 1 },
  { "extcoreregister", arc_extoper, 1 },
  { "extauxregister", arc_extoper, 2 },
  { "extauxregister", arc_extoper, 2 },
  { "extinstruction", arc_extinst, 0 },
  { "extinstruction", arc_extinst, 0 },
  { NULL, 0, 0 },
  { NULL, 0, 0 },
};
};
 
 
/* This routine is called for each instruction to be assembled.  */
/* This routine is called for each instruction to be assembled.  */
 
 
void
void
md_assemble (char *str)
md_assemble (char *str)
{
{
  const struct arc_opcode *opcode;
  const struct arc_opcode *opcode;
  const struct arc_opcode *std_opcode;
  const struct arc_opcode *std_opcode;
  struct arc_opcode *ext_opcode;
  struct arc_opcode *ext_opcode;
  char *start;
  char *start;
  const char *last_errmsg = 0;
  const char *last_errmsg = 0;
  arc_insn insn;
  arc_insn insn;
  static int init_tables_p = 0;
  static int init_tables_p = 0;
 
 
  /* Opcode table initialization is deferred until here because we have to
  /* Opcode table initialization is deferred until here because we have to
     wait for a possible .option command.  */
     wait for a possible .option command.  */
  if (!init_tables_p)
  if (!init_tables_p)
    {
    {
      init_opcode_tables (arc_mach_type);
      init_opcode_tables (arc_mach_type);
      init_tables_p = 1;
      init_tables_p = 1;
    }
    }
 
 
  /* Skip leading white space.  */
  /* Skip leading white space.  */
  while (ISSPACE (*str))
  while (ISSPACE (*str))
    str++;
    str++;
 
 
  /* The instructions are stored in lists hashed by the first letter (though
  /* The instructions are stored in lists hashed by the first letter (though
     we needn't care how they're hashed).  Get the first in the list.  */
     we needn't care how they're hashed).  Get the first in the list.  */
 
 
  ext_opcode = arc_ext_opcodes;
  ext_opcode = arc_ext_opcodes;
  std_opcode = arc_opcode_lookup_asm (str);
  std_opcode = arc_opcode_lookup_asm (str);
 
 
  /* Keep looking until we find a match.  */
  /* Keep looking until we find a match.  */
  start = str;
  start = str;
  for (opcode = (ext_opcode ? ext_opcode : std_opcode);
  for (opcode = (ext_opcode ? ext_opcode : std_opcode);
       opcode != NULL;
       opcode != NULL;
       opcode = (ARC_OPCODE_NEXT_ASM (opcode)
       opcode = (ARC_OPCODE_NEXT_ASM (opcode)
                 ? ARC_OPCODE_NEXT_ASM (opcode)
                 ? ARC_OPCODE_NEXT_ASM (opcode)
                 : (ext_opcode ? ext_opcode = NULL, std_opcode : NULL)))
                 : (ext_opcode ? ext_opcode = NULL, std_opcode : NULL)))
    {
    {
      int past_opcode_p, fc, num_suffixes;
      int past_opcode_p, fc, num_suffixes;
      int fix_up_at = 0;
      int fix_up_at = 0;
      char *syn;
      char *syn;
      struct arc_fixup fixups[MAX_FIXUPS];
      struct arc_fixup fixups[MAX_FIXUPS];
      /* Used as a sanity check.  If we need a limm reloc, make sure we ask
      /* Used as a sanity check.  If we need a limm reloc, make sure we ask
         for an extra 4 bytes from frag_more.  */
         for an extra 4 bytes from frag_more.  */
      int limm_reloc_p;
      int limm_reloc_p;
      int ext_suffix_p;
      int ext_suffix_p;
      const struct arc_operand_value *insn_suffixes[MAX_SUFFIXES];
      const struct arc_operand_value *insn_suffixes[MAX_SUFFIXES];
 
 
      /* Is this opcode supported by the selected cpu?  */
      /* Is this opcode supported by the selected cpu?  */
      if (! arc_opcode_supported (opcode))
      if (! arc_opcode_supported (opcode))
        continue;
        continue;
 
 
      /* Scan the syntax string.  If it doesn't match, try the next one.  */
      /* Scan the syntax string.  If it doesn't match, try the next one.  */
      arc_opcode_init_insert ();
      arc_opcode_init_insert ();
      insn = opcode->value;
      insn = opcode->value;
      fc = 0;
      fc = 0;
      past_opcode_p = 0;
      past_opcode_p = 0;
      num_suffixes = 0;
      num_suffixes = 0;
      limm_reloc_p = 0;
      limm_reloc_p = 0;
      ext_suffix_p = 0;
      ext_suffix_p = 0;
 
 
      /* We don't check for (*str != '\0') here because we want to parse
      /* We don't check for (*str != '\0') here because we want to parse
         any trailing fake arguments in the syntax string.  */
         any trailing fake arguments in the syntax string.  */
      for (str = start, syn = opcode->syntax; *syn != '\0';)
      for (str = start, syn = opcode->syntax; *syn != '\0';)
        {
        {
          int mods;
          int mods;
          const struct arc_operand *operand;
          const struct arc_operand *operand;
 
 
          /* Non operand chars must match exactly.  */
          /* Non operand chars must match exactly.  */
          if (*syn != '%' || *++syn == '%')
          if (*syn != '%' || *++syn == '%')
            {
            {
             if (*str == *syn)
             if (*str == *syn)
                {
                {
                  if (*syn == ' ')
                  if (*syn == ' ')
                    past_opcode_p = 1;
                    past_opcode_p = 1;
                  ++syn;
                  ++syn;
                  ++str;
                  ++str;
                }
                }
              else
              else
                break;
                break;
              continue;
              continue;
            }
            }
 
 
          /* We have an operand.  Pick out any modifiers.  */
          /* We have an operand.  Pick out any modifiers.  */
          mods = 0;
          mods = 0;
          while (ARC_MOD_P (arc_operands[arc_operand_map[(int) *syn]].flags))
          while (ARC_MOD_P (arc_operands[arc_operand_map[(int) *syn]].flags))
            {
            {
              mods |= arc_operands[arc_operand_map[(int) *syn]].flags & ARC_MOD_BITS;
              mods |= arc_operands[arc_operand_map[(int) *syn]].flags & ARC_MOD_BITS;
              ++syn;
              ++syn;
            }
            }
          operand = arc_operands + arc_operand_map[(int) *syn];
          operand = arc_operands + arc_operand_map[(int) *syn];
          if (operand->fmt == 0)
          if (operand->fmt == 0)
            as_fatal (_("unknown syntax format character `%c'"), *syn);
            as_fatal (_("unknown syntax format character `%c'"), *syn);
 
 
          if (operand->flags & ARC_OPERAND_FAKE)
          if (operand->flags & ARC_OPERAND_FAKE)
            {
            {
              const char *errmsg = NULL;
              const char *errmsg = NULL;
              if (operand->insert)
              if (operand->insert)
                {
                {
                  insn = (*operand->insert) (insn, operand, mods, NULL, 0, &errmsg);
                  insn = (*operand->insert) (insn, operand, mods, NULL, 0, &errmsg);
                  if (errmsg != (const char *) NULL)
                  if (errmsg != (const char *) NULL)
                    {
                    {
                      last_errmsg = errmsg;
                      last_errmsg = errmsg;
                      if (operand->flags & ARC_OPERAND_ERROR)
                      if (operand->flags & ARC_OPERAND_ERROR)
                        {
                        {
                          as_bad (errmsg);
                          as_bad (errmsg);
                          return;
                          return;
                        }
                        }
                      else if (operand->flags & ARC_OPERAND_WARN)
                      else if (operand->flags & ARC_OPERAND_WARN)
                        as_warn (errmsg);
                        as_warn (errmsg);
                      break;
                      break;
                    }
                    }
                  if (limm_reloc_p
                  if (limm_reloc_p
                      && (operand->flags && operand->flags & ARC_OPERAND_LIMM)
                      && (operand->flags && operand->flags & ARC_OPERAND_LIMM)
                      && (operand->flags &
                      && (operand->flags &
                          (ARC_OPERAND_ABSOLUTE_BRANCH | ARC_OPERAND_ADDRESS)))
                          (ARC_OPERAND_ABSOLUTE_BRANCH | ARC_OPERAND_ADDRESS)))
                    {
                    {
                      fixups[fix_up_at].opindex = arc_operand_map[operand->fmt];
                      fixups[fix_up_at].opindex = arc_operand_map[operand->fmt];
                    }
                    }
                }
                }
              ++syn;
              ++syn;
            }
            }
          /* Are we finished with suffixes?  */
          /* Are we finished with suffixes?  */
          else if (!past_opcode_p)
          else if (!past_opcode_p)
            {
            {
              int found;
              int found;
              char c;
              char c;
              char *s, *t;
              char *s, *t;
              const struct arc_operand_value *suf, *suffix_end;
              const struct arc_operand_value *suf, *suffix_end;
              const struct arc_operand_value *suffix = NULL;
              const struct arc_operand_value *suffix = NULL;
 
 
              if (!(operand->flags & ARC_OPERAND_SUFFIX))
              if (!(operand->flags & ARC_OPERAND_SUFFIX))
                abort ();
                abort ();
 
 
              /* If we're at a space in the input string, we want to skip the
              /* If we're at a space in the input string, we want to skip the
                 remaining suffixes.  There may be some fake ones though, so
                 remaining suffixes.  There may be some fake ones though, so
                 just go on to try the next one.  */
                 just go on to try the next one.  */
              if (*str == ' ')
              if (*str == ' ')
                {
                {
                  ++syn;
                  ++syn;
                  continue;
                  continue;
                }
                }
 
 
              s = str;
              s = str;
              if (mods & ARC_MOD_DOT)
              if (mods & ARC_MOD_DOT)
                {
                {
                  if (*s != '.')
                  if (*s != '.')
                    break;
                    break;
                  ++s;
                  ++s;
                }
                }
              else
              else
                {
                {
                  /* This can happen in "b.nd foo" and we're currently looking
                  /* This can happen in "b.nd foo" and we're currently looking
                     for "%q" (ie: a condition code suffix).  */
                     for "%q" (ie: a condition code suffix).  */
                  if (*s == '.')
                  if (*s == '.')
                    {
                    {
                      ++syn;
                      ++syn;
                      continue;
                      continue;
                    }
                    }
                }
                }
 
 
              /* Pick the suffix out and look it up via the hash table.  */
              /* Pick the suffix out and look it up via the hash table.  */
              for (t = s; *t && ISALNUM (*t); ++t)
              for (t = s; *t && ISALNUM (*t); ++t)
                continue;
                continue;
              c = *t;
              c = *t;
              *t = '\0';
              *t = '\0';
              if ((suf = get_ext_suffix (s)))
              if ((suf = get_ext_suffix (s)))
                ext_suffix_p = 1;
                ext_suffix_p = 1;
              else
              else
                suf = hash_find (arc_suffix_hash, s);
                suf = hash_find (arc_suffix_hash, s);
              if (!suf)
              if (!suf)
                {
                {
                  /* This can happen in "blle foo" and we're currently using
                  /* This can happen in "blle foo" and we're currently using
                     the template "b%q%.n %j".  The "bl" insn occurs later in
                     the template "b%q%.n %j".  The "bl" insn occurs later in
                     the table so "lle" isn't an illegal suffix.  */
                     the table so "lle" isn't an illegal suffix.  */
                  *t = c;
                  *t = c;
                  break;
                  break;
                }
                }
 
 
              /* Is it the right type?  Note that the same character is used
              /* Is it the right type?  Note that the same character is used
                 several times, so we have to examine all of them.  This is
                 several times, so we have to examine all of them.  This is
                 relatively efficient as equivalent entries are kept
                 relatively efficient as equivalent entries are kept
                 together.  If it's not the right type, don't increment `str'
                 together.  If it's not the right type, don't increment `str'
                 so we try the next one in the series.  */
                 so we try the next one in the series.  */
              found = 0;
              found = 0;
              if (ext_suffix_p && arc_operands[suf->type].fmt == *syn)
              if (ext_suffix_p && arc_operands[suf->type].fmt == *syn)
                {
                {
                  /* Insert the suffix's value into the insn.  */
                  /* Insert the suffix's value into the insn.  */
                  *t = c;
                  *t = c;
                  if (operand->insert)
                  if (operand->insert)
                    insn = (*operand->insert) (insn, operand,
                    insn = (*operand->insert) (insn, operand,
                                               mods, NULL, suf->value,
                                               mods, NULL, suf->value,
                                               NULL);
                                               NULL);
                  else
                  else
                    insn |= suf->value << operand->shift;
                    insn |= suf->value << operand->shift;
                  suffix = suf;
                  suffix = suf;
                  str = t;
                  str = t;
                  found = 1;
                  found = 1;
                }
                }
              else
              else
                {
                {
                  *t = c;
                  *t = c;
                  suffix_end = arc_suffixes + arc_suffixes_count;
                  suffix_end = arc_suffixes + arc_suffixes_count;
                  for (suffix = suf;
                  for (suffix = suf;
                       suffix < suffix_end && strcmp (suffix->name, suf->name) == 0;
                       suffix < suffix_end && strcmp (suffix->name, suf->name) == 0;
                       ++suffix)
                       ++suffix)
                    {
                    {
                      if (arc_operands[suffix->type].fmt == *syn)
                      if (arc_operands[suffix->type].fmt == *syn)
                        {
                        {
                          /* Insert the suffix's value into the insn.  */
                          /* Insert the suffix's value into the insn.  */
                          if (operand->insert)
                          if (operand->insert)
                            insn = (*operand->insert) (insn, operand,
                            insn = (*operand->insert) (insn, operand,
                                                       mods, NULL, suffix->value,
                                                       mods, NULL, suffix->value,
                                                       NULL);
                                                       NULL);
                          else
                          else
                            insn |= suffix->value << operand->shift;
                            insn |= suffix->value << operand->shift;
 
 
                          str = t;
                          str = t;
                          found = 1;
                          found = 1;
                          break;
                          break;
                        }
                        }
                    }
                    }
                }
                }
              ++syn;
              ++syn;
              if (!found)
              if (!found)
                /* Wrong type.  Just go on to try next insn entry.  */
                /* Wrong type.  Just go on to try next insn entry.  */
                ;
                ;
              else
              else
                {
                {
                  if (num_suffixes == MAX_SUFFIXES)
                  if (num_suffixes == MAX_SUFFIXES)
                    as_bad (_("too many suffixes"));
                    as_bad (_("too many suffixes"));
                  else
                  else
                    insn_suffixes[num_suffixes++] = suffix;
                    insn_suffixes[num_suffixes++] = suffix;
                }
                }
            }
            }
          else
          else
            /* This is either a register or an expression of some kind.  */
            /* This is either a register or an expression of some kind.  */
            {
            {
              char *hold;
              char *hold;
              const struct arc_operand_value *reg = NULL;
              const struct arc_operand_value *reg = NULL;
              long value = 0;
              long value = 0;
              expressionS exp;
              expressionS exp;
 
 
              if (operand->flags & ARC_OPERAND_SUFFIX)
              if (operand->flags & ARC_OPERAND_SUFFIX)
                abort ();
                abort ();
 
 
              /* Is there anything left to parse?
              /* Is there anything left to parse?
                 We don't check for this at the top because we want to parse
                 We don't check for this at the top because we want to parse
                 any trailing fake arguments in the syntax string.  */
                 any trailing fake arguments in the syntax string.  */
              if (is_end_of_line[(unsigned char) *str])
              if (is_end_of_line[(unsigned char) *str])
                break;
                break;
 
 
              /* Parse the operand.  */
              /* Parse the operand.  */
              hold = input_line_pointer;
              hold = input_line_pointer;
              input_line_pointer = str;
              input_line_pointer = str;
              expression (&exp);
              expression (&exp);
              str = input_line_pointer;
              str = input_line_pointer;
              input_line_pointer = hold;
              input_line_pointer = hold;
 
 
              if (exp.X_op == O_illegal)
              if (exp.X_op == O_illegal)
                as_bad (_("illegal operand"));
                as_bad (_("illegal operand"));
              else if (exp.X_op == O_absent)
              else if (exp.X_op == O_absent)
                as_bad (_("missing operand"));
                as_bad (_("missing operand"));
              else if (exp.X_op == O_constant)
              else if (exp.X_op == O_constant)
                value = exp.X_add_number;
                value = exp.X_add_number;
              else if (exp.X_op == O_register)
              else if (exp.X_op == O_register)
                reg = (struct arc_operand_value *) exp.X_add_number;
                reg = (struct arc_operand_value *) exp.X_add_number;
#define IS_REG_DEST_OPERAND(o) ((o) == 'a')
#define IS_REG_DEST_OPERAND(o) ((o) == 'a')
              else if (IS_REG_DEST_OPERAND (*syn))
              else if (IS_REG_DEST_OPERAND (*syn))
                as_bad (_("symbol as destination register"));
                as_bad (_("symbol as destination register"));
              else
              else
                {
                {
                  if (!strncmp (str, "@h30", 4))
                  if (!strncmp (str, "@h30", 4))
                    {
                    {
                      arc_code_symbol (&exp);
                      arc_code_symbol (&exp);
                      str += 4;
                      str += 4;
                    }
                    }
                  /* We need to generate a fixup for this expression.  */
                  /* We need to generate a fixup for this expression.  */
                  if (fc >= MAX_FIXUPS)
                  if (fc >= MAX_FIXUPS)
                    as_fatal (_("too many fixups"));
                    as_fatal (_("too many fixups"));
                  fixups[fc].exp = exp;
                  fixups[fc].exp = exp;
                  /* We don't support shimm relocs. break here to force
                  /* We don't support shimm relocs. break here to force
                     the assembler to output a limm.  */
                     the assembler to output a limm.  */
#define IS_REG_SHIMM_OFFSET(o) ((o) == 'd')
#define IS_REG_SHIMM_OFFSET(o) ((o) == 'd')
                  if (IS_REG_SHIMM_OFFSET (*syn))
                  if (IS_REG_SHIMM_OFFSET (*syn))
                    break;
                    break;
                  /* If this is a register constant (IE: one whose
                  /* If this is a register constant (IE: one whose
                     register value gets stored as 61-63) then this
                     register value gets stored as 61-63) then this
                     must be a limm.  */
                     must be a limm.  */
                  /* ??? This bit could use some cleaning up.
                  /* ??? This bit could use some cleaning up.
                     Referencing the format chars like this goes
                     Referencing the format chars like this goes
                     against style.  */
                     against style.  */
                  if (IS_SYMBOL_OPERAND (*syn))
                  if (IS_SYMBOL_OPERAND (*syn))
                    {
                    {
                      const char *junk;
                      const char *junk;
                      limm_reloc_p = 1;
                      limm_reloc_p = 1;
                      /* Save this, we don't yet know what reloc to use.  */
                      /* Save this, we don't yet know what reloc to use.  */
                      fix_up_at = fc;
                      fix_up_at = fc;
                      /* Tell insert_reg we need a limm.  This is
                      /* Tell insert_reg we need a limm.  This is
                         needed because the value at this point is
                         needed because the value at this point is
                         zero, a shimm.  */
                         zero, a shimm.  */
                      /* ??? We need a cleaner interface than this.  */
                      /* ??? We need a cleaner interface than this.  */
                      (*arc_operands[arc_operand_map['Q']].insert)
                      (*arc_operands[arc_operand_map['Q']].insert)
                        (insn, operand, mods, reg, 0L, &junk);
                        (insn, operand, mods, reg, 0L, &junk);
                    }
                    }
                  else
                  else
                    fixups[fc].opindex = arc_operand_map[(int) *syn];
                    fixups[fc].opindex = arc_operand_map[(int) *syn];
                  ++fc;
                  ++fc;
                  value = 0;
                  value = 0;
                }
                }
 
 
              /* Insert the register or expression into the instruction.  */
              /* Insert the register or expression into the instruction.  */
              if (operand->insert)
              if (operand->insert)
                {
                {
                  const char *errmsg = NULL;
                  const char *errmsg = NULL;
                  insn = (*operand->insert) (insn, operand, mods,
                  insn = (*operand->insert) (insn, operand, mods,
                                             reg, (long) value, &errmsg);
                                             reg, (long) value, &errmsg);
                  if (errmsg != (const char *) NULL)
                  if (errmsg != (const char *) NULL)
                    {
                    {
                      last_errmsg = errmsg;
                      last_errmsg = errmsg;
                      if (operand->flags & ARC_OPERAND_ERROR)
                      if (operand->flags & ARC_OPERAND_ERROR)
                        {
                        {
                          as_bad (errmsg);
                          as_bad (errmsg);
                          return;
                          return;
                        }
                        }
                      else if (operand->flags & ARC_OPERAND_WARN)
                      else if (operand->flags & ARC_OPERAND_WARN)
                        as_warn (errmsg);
                        as_warn (errmsg);
                      break;
                      break;
                    }
                    }
                }
                }
              else
              else
                insn |= (value & ((1 << operand->bits) - 1)) << operand->shift;
                insn |= (value & ((1 << operand->bits) - 1)) << operand->shift;
 
 
              ++syn;
              ++syn;
            }
            }
        }
        }
 
 
      /* If we're at the end of the syntax string, we're done.  */
      /* If we're at the end of the syntax string, we're done.  */
      /* FIXME: try to move this to a separate function.  */
      /* FIXME: try to move this to a separate function.  */
      if (*syn == '\0')
      if (*syn == '\0')
        {
        {
          int i;
          int i;
          char *f;
          char *f;
          long limm, limm_p;
          long limm, limm_p;
 
 
          /* For the moment we assume a valid `str' can only contain blanks
          /* For the moment we assume a valid `str' can only contain blanks
             now.  IE: We needn't try again with a longer version of the
             now.  IE: We needn't try again with a longer version of the
             insn and it is assumed that longer versions of insns appear
             insn and it is assumed that longer versions of insns appear
             before shorter ones (eg: lsr r2,r3,1 vs lsr r2,r3).  */
             before shorter ones (eg: lsr r2,r3,1 vs lsr r2,r3).  */
 
 
          while (ISSPACE (*str))
          while (ISSPACE (*str))
            ++str;
            ++str;
 
 
          if (!is_end_of_line[(unsigned char) *str])
          if (!is_end_of_line[(unsigned char) *str])
            as_bad (_("junk at end of line: `%s'"), str);
            as_bad (_("junk at end of line: `%s'"), str);
 
 
          /* Is there a limm value?  */
          /* Is there a limm value?  */
          limm_p = arc_opcode_limm_p (&limm);
          limm_p = arc_opcode_limm_p (&limm);
 
 
          /* Perform various error and warning tests.  */
          /* Perform various error and warning tests.  */
 
 
          {
          {
            static int in_delay_slot_p = 0;
            static int in_delay_slot_p = 0;
            static int prev_insn_needs_cc_nop_p = 0;
            static int prev_insn_needs_cc_nop_p = 0;
            /* delay slot type seen */
            /* delay slot type seen */
            int delay_slot_type = ARC_DELAY_NONE;
            int delay_slot_type = ARC_DELAY_NONE;
            /* conditional execution flag seen */
            /* conditional execution flag seen */
            int conditional = 0;
            int conditional = 0;
            /* 1 if condition codes are being set */
            /* 1 if condition codes are being set */
            int cc_set_p = 0;
            int cc_set_p = 0;
            /* 1 if conditional branch, including `b' "branch always" */
            /* 1 if conditional branch, including `b' "branch always" */
            int cond_branch_p = opcode->flags & ARC_OPCODE_COND_BRANCH;
            int cond_branch_p = opcode->flags & ARC_OPCODE_COND_BRANCH;
 
 
            for (i = 0; i < num_suffixes; ++i)
            for (i = 0; i < num_suffixes; ++i)
              {
              {
                switch (arc_operands[insn_suffixes[i]->type].fmt)
                switch (arc_operands[insn_suffixes[i]->type].fmt)
                  {
                  {
                  case 'n':
                  case 'n':
                    delay_slot_type = insn_suffixes[i]->value;
                    delay_slot_type = insn_suffixes[i]->value;
                    break;
                    break;
                  case 'q':
                  case 'q':
                    conditional = insn_suffixes[i]->value;
                    conditional = insn_suffixes[i]->value;
                    break;
                    break;
                  case 'f':
                  case 'f':
                    cc_set_p = 1;
                    cc_set_p = 1;
                    break;
                    break;
                  }
                  }
              }
              }
 
 
            /* Putting an insn with a limm value in a delay slot is supposed to
            /* Putting an insn with a limm value in a delay slot is supposed to
               be legal, but let's warn the user anyway.  Ditto for 8 byte
               be legal, but let's warn the user anyway.  Ditto for 8 byte
               jumps with delay slots.  */
               jumps with delay slots.  */
            if (in_delay_slot_p && limm_p)
            if (in_delay_slot_p && limm_p)
              as_warn (_("8 byte instruction in delay slot"));
              as_warn (_("8 byte instruction in delay slot"));
            if (delay_slot_type != ARC_DELAY_NONE
            if (delay_slot_type != ARC_DELAY_NONE
                && limm_p && arc_insn_not_jl (insn)) /* except for jl  addr */
                && limm_p && arc_insn_not_jl (insn)) /* except for jl  addr */
              as_warn (_("8 byte jump instruction with delay slot"));
              as_warn (_("8 byte jump instruction with delay slot"));
            in_delay_slot_p = (delay_slot_type != ARC_DELAY_NONE) && !limm_p;
            in_delay_slot_p = (delay_slot_type != ARC_DELAY_NONE) && !limm_p;
 
 
            /* Warn when a conditional branch immediately follows a set of
            /* Warn when a conditional branch immediately follows a set of
               the condition codes.  Note that this needn't be done if the
               the condition codes.  Note that this needn't be done if the
               insn that sets the condition codes uses a limm.  */
               insn that sets the condition codes uses a limm.  */
            if (cond_branch_p && conditional != 0 /* 0 = "always" */
            if (cond_branch_p && conditional != 0 /* 0 = "always" */
                && prev_insn_needs_cc_nop_p && arc_mach_type == bfd_mach_arc_5)
                && prev_insn_needs_cc_nop_p && arc_mach_type == bfd_mach_arc_5)
              as_warn (_("conditional branch follows set of flags"));
              as_warn (_("conditional branch follows set of flags"));
            prev_insn_needs_cc_nop_p =
            prev_insn_needs_cc_nop_p =
              /* FIXME: ??? not required:
              /* FIXME: ??? not required:
                 (delay_slot_type != ARC_DELAY_NONE) &&  */
                 (delay_slot_type != ARC_DELAY_NONE) &&  */
              cc_set_p && !limm_p;
              cc_set_p && !limm_p;
          }
          }
 
 
          /* Write out the instruction.
          /* Write out the instruction.
             It is important to fetch enough space in one call to `frag_more'.
             It is important to fetch enough space in one call to `frag_more'.
             We use (f - frag_now->fr_literal) to compute where we are and we
             We use (f - frag_now->fr_literal) to compute where we are and we
             don't want frag_now to change between calls.  */
             don't want frag_now to change between calls.  */
          if (limm_p)
          if (limm_p)
            {
            {
              f = frag_more (8);
              f = frag_more (8);
              md_number_to_chars (f, insn, 4);
              md_number_to_chars (f, insn, 4);
              md_number_to_chars (f + 4, limm, 4);
              md_number_to_chars (f + 4, limm, 4);
              dwarf2_emit_insn (8);
              dwarf2_emit_insn (8);
            }
            }
          else if (limm_reloc_p)
          else if (limm_reloc_p)
            /* We need a limm reloc, but the tables think we don't.  */
            /* We need a limm reloc, but the tables think we don't.  */
            abort ();
            abort ();
          else
          else
            {
            {
              f = frag_more (4);
              f = frag_more (4);
              md_number_to_chars (f, insn, 4);
              md_number_to_chars (f, insn, 4);
              dwarf2_emit_insn (4);
              dwarf2_emit_insn (4);
            }
            }
 
 
          /* Create any fixups.  */
          /* Create any fixups.  */
          for (i = 0; i < fc; ++i)
          for (i = 0; i < fc; ++i)
            {
            {
              int op_type, reloc_type;
              int op_type, reloc_type;
              expressionS exptmp;
              expressionS exptmp;
              const struct arc_operand *operand;
              const struct arc_operand *operand;
 
 
              /* Create a fixup for this operand.
              /* Create a fixup for this operand.
                 At this point we do not use a bfd_reloc_code_real_type for
                 At this point we do not use a bfd_reloc_code_real_type for
                 operands residing in the insn, but instead just use the
                 operands residing in the insn, but instead just use the
                 operand index.  This lets us easily handle fixups for any
                 operand index.  This lets us easily handle fixups for any
                 operand type, although that is admittedly not a very exciting
                 operand type, although that is admittedly not a very exciting
                 feature.  We pick a BFD reloc type in md_apply_fix.
                 feature.  We pick a BFD reloc type in md_apply_fix.
 
 
                 Limm values (4 byte immediate "constants") must be treated
                 Limm values (4 byte immediate "constants") must be treated
                 normally because they're not part of the actual insn word
                 normally because they're not part of the actual insn word
                 and thus the insertion routines don't handle them.  */
                 and thus the insertion routines don't handle them.  */
 
 
              if (arc_operands[fixups[i].opindex].flags & ARC_OPERAND_LIMM)
              if (arc_operands[fixups[i].opindex].flags & ARC_OPERAND_LIMM)
                {
                {
                  /* Modify the fixup addend as required by the cpu.  */
                  /* Modify the fixup addend as required by the cpu.  */
                  fixups[i].exp.X_add_number += arc_limm_fixup_adjust (insn);
                  fixups[i].exp.X_add_number += arc_limm_fixup_adjust (insn);
                  op_type = fixups[i].opindex;
                  op_type = fixups[i].opindex;
                  /* FIXME: can we add this data to the operand table?  */
                  /* FIXME: can we add this data to the operand table?  */
                  if (op_type == arc_operand_map['L']
                  if (op_type == arc_operand_map['L']
                      || op_type == arc_operand_map['s']
                      || op_type == arc_operand_map['s']
                      || op_type == arc_operand_map['o']
                      || op_type == arc_operand_map['o']
                      || op_type == arc_operand_map['O'])
                      || op_type == arc_operand_map['O'])
                    reloc_type = BFD_RELOC_32;
                    reloc_type = BFD_RELOC_32;
                  else if (op_type == arc_operand_map['J'])
                  else if (op_type == arc_operand_map['J'])
                    reloc_type = BFD_RELOC_ARC_B26;
                    reloc_type = BFD_RELOC_ARC_B26;
                  else
                  else
                    abort ();
                    abort ();
                  reloc_type = get_arc_exp_reloc_type (1, reloc_type,
                  reloc_type = get_arc_exp_reloc_type (1, reloc_type,
                                                       &fixups[i].exp,
                                                       &fixups[i].exp,
                                                       &exptmp);
                                                       &exptmp);
                }
                }
              else
              else
                {
                {
                  op_type = get_arc_exp_reloc_type (0, fixups[i].opindex,
                  op_type = get_arc_exp_reloc_type (0, fixups[i].opindex,
                                                    &fixups[i].exp, &exptmp);
                                                    &fixups[i].exp, &exptmp);
                  reloc_type = op_type + (int) BFD_RELOC_UNUSED;
                  reloc_type = op_type + (int) BFD_RELOC_UNUSED;
                }
                }
              operand = &arc_operands[op_type];
              operand = &arc_operands[op_type];
              fix_new_exp (frag_now,
              fix_new_exp (frag_now,
                           ((f - frag_now->fr_literal)
                           ((f - frag_now->fr_literal)
                            + (operand->flags & ARC_OPERAND_LIMM ? 4 : 0)), 4,
                            + (operand->flags & ARC_OPERAND_LIMM ? 4 : 0)), 4,
                           &exptmp,
                           &exptmp,
                           (operand->flags & ARC_OPERAND_RELATIVE_BRANCH) != 0,
                           (operand->flags & ARC_OPERAND_RELATIVE_BRANCH) != 0,
                           (bfd_reloc_code_real_type) reloc_type);
                           (bfd_reloc_code_real_type) reloc_type);
            }
            }
          return;
          return;
        }
        }
    }
    }
 
 
  if (NULL == last_errmsg)
  if (NULL == last_errmsg)
    as_bad (_("bad instruction `%s'"), start);
    as_bad (_("bad instruction `%s'"), start);
  else
  else
    as_bad (last_errmsg);
    as_bad (last_errmsg);
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.