/* tc-arc.c -- Assembler for the ARC
- Copyright 1994, 1995, 1997, 1999, 2000, 2001, 2002
- Free Software Foundation, Inc.
- Contributed by Doug Evans (dje@cygnus.com).
+ Copyright (C) 1994-2016 Free Software Foundation, Inc.
+
+ Contributor: Claudiu Zissulescu <claziss@synopsys.com>
This file is part of GAS, the GNU Assembler.
GAS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
+ the Free Software Foundation; either version 3, or (at your option)
any later version.
GAS is distributed in the hope that it will be useful,
You should have received a copy of the GNU General Public License
along with GAS; see the file COPYING. If not, write to the Free
- Software Foundation, 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA. */
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
-#include <stdio.h>
-#include "libiberty.h"
#include "as.h"
+#include "subsegs.h"
#include "struc-symbol.h"
+#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
#include "safe-ctype.h"
-#include "subsegs.h"
+
#include "opcode/arc.h"
-#include "../opcodes/arc-ext.h"
#include "elf/arc.h"
-#include "dwarf2dbg.h"
-extern int arc_get_mach PARAMS ((char *));
-extern int arc_operand_type PARAMS ((int));
-extern int arc_insn_not_jl PARAMS ((arc_insn));
-extern int arc_limm_fixup_adjust PARAMS ((arc_insn));
-extern int arc_get_noshortcut_flag PARAMS ((void));
-extern int arc_set_ext_seg PARAMS ((void));
-extern void arc_code_symbol PARAMS ((expressionS *));
-
-static arc_insn arc_insert_operand PARAMS ((arc_insn,
- const struct arc_operand *, int,
- const struct arc_operand_value *,
- offsetT, char *, unsigned int));
-static void arc_common PARAMS ((int));
-static void arc_extinst PARAMS ((int));
-static void arc_extoper PARAMS ((int));
-static void arc_option PARAMS ((int));
-static int get_arc_exp_reloc_type PARAMS ((int, int, expressionS *,
- expressionS *));
-
-static void init_opcode_tables PARAMS ((int));
-
-const struct suffix_classes {
- char *name;
- int len;
-} suffixclass[] = {
- { "SUFFIX_COND|SUFFIX_FLAG",23 },
- { "SUFFIX_FLAG", 11 },
- { "SUFFIX_COND", 11 },
- { "SUFFIX_NONE", 11 }
-};
+/* Defines section. */
+
+#define MAX_INSN_FIXUPS 2
+#define MAX_CONSTR_STR 20
+#define FRAG_MAX_GROWTH 8
-#define MAXSUFFIXCLASS (sizeof (suffixclass) / sizeof (struct suffix_classes))
-
-const struct syntax_classes {
- char *name;
- int len;
- int class;
-} syntaxclass[] = {
- { "SYNTAX_3OP|OP1_MUST_BE_IMM", 26, SYNTAX_3OP|OP1_MUST_BE_IMM|SYNTAX_VALID },
- { "OP1_MUST_BE_IMM|SYNTAX_3OP", 26, OP1_MUST_BE_IMM|SYNTAX_3OP|SYNTAX_VALID },
- { "SYNTAX_2OP|OP1_IMM_IMPLIED", 26, SYNTAX_2OP|OP1_IMM_IMPLIED|SYNTAX_VALID },
- { "OP1_IMM_IMPLIED|SYNTAX_2OP", 26, OP1_IMM_IMPLIED|SYNTAX_2OP|SYNTAX_VALID },
- { "SYNTAX_3OP", 10, SYNTAX_3OP|SYNTAX_VALID },
- { "SYNTAX_2OP", 10, SYNTAX_2OP|SYNTAX_VALID }
+#ifdef DEBUG
+# define pr_debug(fmt, args...) fprintf (stderr, fmt, ##args)
+#else
+# define pr_debug(fmt, args...)
+#endif
+
+#define MAJOR_OPCODE(x) (((x) & 0xF8000000) >> 27)
+#define SUB_OPCODE(x) (((x) & 0x003F0000) >> 16)
+#define LP_INSN(x) ((MAJOR_OPCODE (x) == 0x4) && \
+ (SUB_OPCODE (x) == 0x28))
+
+/* Equal to MAX_PRECISION in atof-ieee.c. */
+#define MAX_LITTLENUMS 6
+
+/* Enum used to enumerate the relaxable ins operands. */
+enum rlx_operand_type
+{
+ EMPTY = 0,
+ REGISTER,
+ REGISTER_S, /* Register for short instruction(s). */
+ REGISTER_NO_GP, /* Is a register but not gp register specifically. */
+ REGISTER_DUP, /* Duplication of previous operand of type register. */
+ IMMEDIATE,
+ BRACKET
};
-#define MAXSYNTAXCLASS (sizeof (syntaxclass) / sizeof (struct syntax_classes))
-
-const pseudo_typeS md_pseudo_table[] = {
- { "align", s_align_bytes, 0 }, /* Defaulting is invalid (0). */
- { "comm", arc_common, 0 },
- { "common", arc_common, 0 },
- { "lcomm", arc_common, 1 },
- { "lcommon", arc_common, 1 },
- { "2byte", cons, 2 },
- { "half", cons, 2 },
- { "short", cons, 2 },
- { "3byte", cons, 3 },
- { "4byte", cons, 4 },
- { "word", cons, 4 },
- { "option", arc_option, 0 },
- { "cpu", arc_option, 0 },
- { "block", s_space, 0 },
- { "extcondcode", arc_extoper, 0 },
- { "extcoreregister", arc_extoper, 1 },
- { "extauxregister", arc_extoper, 2 },
- { "extinstruction", arc_extinst, 0 },
- { NULL, 0, 0 },
+enum arc_rlx_types
+{
+ ARC_RLX_NONE = 0,
+ ARC_RLX_BL_S,
+ ARC_RLX_BL,
+ ARC_RLX_B_S,
+ ARC_RLX_B,
+ ARC_RLX_ADD_U3,
+ ARC_RLX_ADD_U6,
+ ARC_RLX_ADD_LIMM,
+ ARC_RLX_LD_U7,
+ ARC_RLX_LD_S9,
+ ARC_RLX_LD_LIMM,
+ ARC_RLX_MOV_U8,
+ ARC_RLX_MOV_S12,
+ ARC_RLX_MOV_LIMM,
+ ARC_RLX_SUB_U3,
+ ARC_RLX_SUB_U6,
+ ARC_RLX_SUB_LIMM,
+ ARC_RLX_MPY_U6,
+ ARC_RLX_MPY_LIMM,
+ ARC_RLX_MOV_RU6,
+ ARC_RLX_MOV_RLIMM,
+ ARC_RLX_ADD_RRU6,
+ ARC_RLX_ADD_RRLIMM,
};
-/* This array holds the chars that always start a comment. If the
- pre-processor is disabled, these aren't very useful. */
+/* Macros section. */
+
+#define regno(x) ((x) & 0x3F)
+#define is_ir_num(x) (((x) & ~0x3F) == 0)
+#define is_code_density_p(op) (((op)->subclass == CD1 || (op)->subclass == CD2))
+#define is_br_jmp_insn_p(op) (((op)->class == BRANCH || (op)->class == JUMP))
+#define is_kernel_insn_p(op) (((op)->class == KERNEL))
+
+/* Generic assembler global variables which must be defined by all
+ targets. */
+
+/* Characters which always start a comment. */
const char comment_chars[] = "#;";
-/* This array holds the chars that only start a comment at the beginning of
- a line. If the line seems to have the form '# 123 filename'
- .line and .file directives will appear in the pre-processed output */
-/* Note that input_file.c hand checks for '#' at the beginning of the
- first line of the input file. This is because the compiler outputs
- #NO_APP at the beginning of its output. */
-/* Also note that comments started like this one will always
- work if '/' isn't otherwise defined. */
+/* Characters which start a comment at the beginning of a line. */
const char line_comment_chars[] = "#";
-const char line_separator_chars[] = "";
+/* Characters which may be used to separate multiple commands on a
+ single line. */
+const char line_separator_chars[] = "`";
-/* Chars that can be used to separate mant from exp in floating point nums. */
+/* Characters which are used to indicate an exponent in a floating
+ point number. */
const char EXP_CHARS[] = "eE";
/* Chars that mean this number is a floating point constant
const char *arc_target_format = DEFAULT_TARGET_FORMAT;
static int byte_order = DEFAULT_BYTE_ORDER;
-static segT arcext_section;
+/* By default relaxation is disabled. */
+static int relaxation_state = 0;
-/* One of bfd_mach_arc_n. */
-static int arc_mach_type = bfd_mach_arc_6;
+extern int arc_get_mach (char *);
-/* Non-zero if the cpu type has been explicitly specified. */
-static int mach_type_specified_p = 0;
+/* Forward declarations. */
+static void arc_lcomm (int);
+static void arc_option (int);
+static void arc_extra_reloc (int);
+
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ /* Make sure that .word is 32 bits. */
+ { "word", cons, 4 },
+
+ { "align", s_align_bytes, 0 }, /* Defaulting is invalid (0). */
+ { "lcomm", arc_lcomm, 0 },
+ { "lcommon", arc_lcomm, 0 },
+ { "cpu", arc_option, 0 },
-/* Non-zero if opcode tables have been initialized.
- A .option command must appear before any instructions. */
-static int cpu_tables_init_p = 0;
+ { "tls_gd_ld", arc_extra_reloc, BFD_RELOC_ARC_TLS_GD_LD },
+ { "tls_gd_call", arc_extra_reloc, BFD_RELOC_ARC_TLS_GD_CALL },
+
+ { NULL, NULL, 0 }
+};
-static struct hash_control *arc_suffix_hash = NULL;
-\f
const char *md_shortopts = "";
-struct option md_longopts[] = {
-#define OPTION_EB (OPTION_MD_BASE + 0)
- { "EB", no_argument, NULL, OPTION_EB },
-#define OPTION_EL (OPTION_MD_BASE + 1)
- { "EL", no_argument, NULL, OPTION_EL },
-#define OPTION_ARC5 (OPTION_MD_BASE + 2)
- { "marc5", no_argument, NULL, OPTION_ARC5 },
- { "pre-v6", no_argument, NULL, OPTION_ARC5 },
-#define OPTION_ARC6 (OPTION_MD_BASE + 3)
- { "marc6", no_argument, NULL, OPTION_ARC6 },
-#define OPTION_ARC7 (OPTION_MD_BASE + 4)
- { "marc7", no_argument, NULL, OPTION_ARC7 },
-#define OPTION_ARC8 (OPTION_MD_BASE + 5)
- { "marc8", no_argument, NULL, OPTION_ARC8 },
-#define OPTION_ARC (OPTION_MD_BASE + 6)
- { "marc", no_argument, NULL, OPTION_ARC },
- { NULL, no_argument, NULL, 0 }
+
+enum options
+{
+ OPTION_EB = OPTION_MD_BASE,
+ OPTION_EL,
+
+ OPTION_ARC600,
+ OPTION_ARC601,
+ OPTION_ARC700,
+ OPTION_ARCEM,
+ OPTION_ARCHS,
+
+ OPTION_MCPU,
+ OPTION_CD,
+ OPTION_RELAX,
+
+ /* The following options are deprecated and provided here only for
+ compatibility reasons. */
+ OPTION_USER_MODE,
+ OPTION_LD_EXT_MASK,
+ OPTION_SWAP,
+ OPTION_NORM,
+ OPTION_BARREL_SHIFT,
+ OPTION_MIN_MAX,
+ OPTION_NO_MPY,
+ OPTION_EA,
+ OPTION_MUL64,
+ OPTION_SIMD,
+ OPTION_SPFP,
+ OPTION_DPFP,
+ OPTION_XMAC_D16,
+ OPTION_XMAC_24,
+ OPTION_DSP_PACKA,
+ OPTION_CRC,
+ OPTION_DVBF,
+ OPTION_TELEPHONY,
+ OPTION_XYMEMORY,
+ OPTION_LOCK,
+ OPTION_SWAPE,
+ OPTION_RTSC,
+ OPTION_FPUDA
+};
+
+struct option md_longopts[] =
+{
+ { "EB", no_argument, NULL, OPTION_EB },
+ { "EL", no_argument, NULL, OPTION_EL },
+ { "mcpu", required_argument, NULL, OPTION_MCPU },
+ { "mA6", no_argument, NULL, OPTION_ARC600 },
+ { "mARC600", no_argument, NULL, OPTION_ARC600 },
+ { "mARC601", no_argument, NULL, OPTION_ARC601 },
+ { "mARC700", no_argument, NULL, OPTION_ARC700 },
+ { "mA7", no_argument, NULL, OPTION_ARC700 },
+ { "mEM", no_argument, NULL, OPTION_ARCEM },
+ { "mHS", no_argument, NULL, OPTION_ARCHS },
+ { "mcode-density", no_argument, NULL, OPTION_CD },
+ { "mrelax", no_argument, NULL, OPTION_RELAX },
+
+ /* The following options are deprecated and provided here only for
+ compatibility reasons. */
+ { "mav2em", no_argument, NULL, OPTION_ARCEM },
+ { "mav2hs", no_argument, NULL, OPTION_ARCHS },
+ { "muser-mode-only", no_argument, NULL, OPTION_USER_MODE },
+ { "mld-extension-reg-mask", required_argument, NULL, OPTION_LD_EXT_MASK },
+ { "mswap", no_argument, NULL, OPTION_SWAP },
+ { "mnorm", no_argument, NULL, OPTION_NORM },
+ { "mbarrel-shifter", no_argument, NULL, OPTION_BARREL_SHIFT },
+ { "mbarrel_shifter", no_argument, NULL, OPTION_BARREL_SHIFT },
+ { "mmin-max", no_argument, NULL, OPTION_MIN_MAX },
+ { "mmin_max", no_argument, NULL, OPTION_MIN_MAX },
+ { "mno-mpy", no_argument, NULL, OPTION_NO_MPY },
+ { "mea", no_argument, NULL, OPTION_EA },
+ { "mEA", no_argument, NULL, OPTION_EA },
+ { "mmul64", no_argument, NULL, OPTION_MUL64 },
+ { "msimd", no_argument, NULL, OPTION_SIMD},
+ { "mspfp", no_argument, NULL, OPTION_SPFP},
+ { "mspfp-compact", no_argument, NULL, OPTION_SPFP},
+ { "mspfp_compact", no_argument, NULL, OPTION_SPFP},
+ { "mspfp-fast", no_argument, NULL, OPTION_SPFP},
+ { "mspfp_fast", no_argument, NULL, OPTION_SPFP},
+ { "mdpfp", no_argument, NULL, OPTION_DPFP},
+ { "mdpfp-compact", no_argument, NULL, OPTION_DPFP},
+ { "mdpfp_compact", no_argument, NULL, OPTION_DPFP},
+ { "mdpfp-fast", no_argument, NULL, OPTION_DPFP},
+ { "mdpfp_fast", no_argument, NULL, OPTION_DPFP},
+ { "mmac-d16", no_argument, NULL, OPTION_XMAC_D16},
+ { "mmac_d16", no_argument, NULL, OPTION_XMAC_D16},
+ { "mmac-24", no_argument, NULL, OPTION_XMAC_24},
+ { "mmac_24", no_argument, NULL, OPTION_XMAC_24},
+ { "mdsp-packa", no_argument, NULL, OPTION_DSP_PACKA},
+ { "mdsp_packa", no_argument, NULL, OPTION_DSP_PACKA},
+ { "mcrc", no_argument, NULL, OPTION_CRC},
+ { "mdvbf", no_argument, NULL, OPTION_DVBF},
+ { "mtelephony", no_argument, NULL, OPTION_TELEPHONY},
+ { "mxy", no_argument, NULL, OPTION_XYMEMORY},
+ { "mlock", no_argument, NULL, OPTION_LOCK},
+ { "mswape", no_argument, NULL, OPTION_SWAPE},
+ { "mrtsc", no_argument, NULL, OPTION_RTSC},
+ { "mfpuda", no_argument, NULL, OPTION_FPUDA},
+
+ { NULL, no_argument, NULL, 0 }
};
+
size_t md_longopts_size = sizeof (md_longopts);
-#define IS_SYMBOL_OPERAND(o) \
- ((o) == 'b' || (o) == 'c' || (o) == 's' || (o) == 'o' || (o) == 'O')
+/* Local data and data types. */
+
+/* Used since new relocation types are introduced in this
+ file (DUMMY_RELOC_LITUSE_*). */
+typedef int extended_bfd_reloc_code_real_type;
+
+struct arc_fixup
+{
+ expressionS exp;
-struct arc_operand_value *get_ext_suffix (char *s);
+ extended_bfd_reloc_code_real_type reloc;
-/* Invocation line includes a switch not recognized by the base assembler.
- See if it's a processor-specific option. */
+ /* index into arc_operands. */
+ unsigned int opindex;
-int
-md_parse_option (c, arg)
- int c;
- char *arg ATTRIBUTE_UNUSED;
+ /* PC-relative, used by internals fixups. */
+ unsigned char pcrel;
+
+ /* TRUE if this fixup is for LIMM operand. */
+ bfd_boolean islong;
+};
+
+struct arc_insn
{
- switch (c)
- {
- case OPTION_ARC5:
- arc_mach_type = bfd_mach_arc_5;
- break;
- case OPTION_ARC:
- case OPTION_ARC6:
- arc_mach_type = bfd_mach_arc_6;
- break;
- case OPTION_ARC7:
- arc_mach_type = bfd_mach_arc_7;
- break;
- case OPTION_ARC8:
- arc_mach_type = bfd_mach_arc_8;
- break;
- case OPTION_EB:
- byte_order = BIG_ENDIAN;
- arc_target_format = "elf32-bigarc";
- break;
- case OPTION_EL:
- byte_order = LITTLE_ENDIAN;
- arc_target_format = "elf32-littlearc";
- break;
- default:
- return 0;
- }
- return 1;
+ unsigned int insn;
+ int nfixups;
+ struct arc_fixup fixups[MAX_INSN_FIXUPS];
+ long limm;
+ bfd_boolean short_insn; /* Boolean value: TRUE if current insn is
+ short. */
+ bfd_boolean has_limm; /* Boolean value: TRUE if limm field is
+ valid. */
+ bfd_boolean relax; /* Boolean value: TRUE if needs
+ relaxation. */
+};
+
+/* Structure to hold any last two instructions. */
+static struct arc_last_insn
+{
+ /* Saved instruction opcode. */
+ const struct arc_opcode *opcode;
+
+ /* Boolean value: TRUE if current insn is short. */
+ bfd_boolean has_limm;
+
+ /* Boolean value: TRUE if current insn has delay slot. */
+ bfd_boolean has_delay_slot;
+} arc_last_insns[2];
+
+/* Forward declaration. */
+static void assemble_insn
+ (const struct arc_opcode *, const expressionS *, int,
+ const struct arc_flags *, int, struct arc_insn *);
+
+/* The cpu for which we are generating code. */
+static unsigned arc_target = ARC_OPCODE_BASE;
+static const char *arc_target_name = "<all>";
+static unsigned arc_features = 0x00;
+
+/* The default architecture. */
+static int arc_mach_type = bfd_mach_arc_arcv2;
+
+/* Non-zero if the cpu type has been explicitly specified. */
+static int mach_type_specified_p = 0;
+
+/* The hash table of instruction opcodes. */
+static struct hash_control *arc_opcode_hash;
+
+/* The hash table of register symbols. */
+static struct hash_control *arc_reg_hash;
+
+/* A table of CPU names and opcode sets. */
+static const struct cpu_type
+{
+ const char *name;
+ unsigned flags;
+ int mach;
+ unsigned eflags;
+ unsigned features;
}
+ cpu_types[] =
+{
+ { "arc600", ARC_OPCODE_ARC600, bfd_mach_arc_arc600,
+ E_ARC_MACH_ARC600, 0x00},
+ { "arc700", ARC_OPCODE_ARC700, bfd_mach_arc_arc700,
+ E_ARC_MACH_ARC700, 0x00},
+ { "arcem", ARC_OPCODE_ARCv2EM, bfd_mach_arc_arcv2,
+ EF_ARC_CPU_ARCV2EM, 0x00},
+ { "archs", ARC_OPCODE_ARCv2HS, bfd_mach_arc_arcv2,
+ EF_ARC_CPU_ARCV2HS, ARC_CD},
+ { "all", ARC_OPCODE_BASE, bfd_mach_arc_arcv2,
+ 0x00, 0x00 },
+ { 0, 0, 0, 0, 0 }
+};
-void
-md_show_usage (stream)
- FILE *stream;
-{
- fprintf (stream, "\
-ARC Options:\n\
- -marc[5|6|7|8] select processor variant (default arc%d)\n\
- -EB assemble code for a big endian cpu\n\
- -EL assemble code for a little endian cpu\n", arc_mach_type + 5);
+/* Used by the arc_reloc_op table. Order is important. */
+#define O_gotoff O_md1 /* @gotoff relocation. */
+#define O_gotpc O_md2 /* @gotpc relocation. */
+#define O_plt O_md3 /* @plt relocation. */
+#define O_sda O_md4 /* @sda relocation. */
+#define O_pcl O_md5 /* @pcl relocation. */
+#define O_tlsgd O_md6 /* @tlsgd relocation. */
+#define O_tlsie O_md7 /* @tlsie relocation. */
+#define O_tpoff9 O_md8 /* @tpoff9 relocation. */
+#define O_tpoff O_md9 /* @tpoff relocation. */
+#define O_dtpoff9 O_md10 /* @dtpoff9 relocation. */
+#define O_dtpoff O_md11 /* @dtpoff relocation. */
+#define O_last O_dtpoff
+
+/* Used to define a bracket as operand in tokens. */
+#define O_bracket O_md32
+
+/* Dummy relocation, to be sorted out. */
+#define DUMMY_RELOC_ARC_ENTRY (BFD_RELOC_UNUSED + 1)
+
+#define USER_RELOC_P(R) ((R) >= O_gotoff && (R) <= O_last)
+
+/* A table to map the spelling of a relocation operand into an appropriate
+ bfd_reloc_code_real_type type. The table is assumed to be ordered such
+ that op-O_literal indexes into it. */
+#define ARC_RELOC_TABLE(op) \
+ (&arc_reloc_op[ ((!USER_RELOC_P (op)) \
+ ? (abort (), 0) \
+ : (int) (op) - (int) O_gotoff) ])
+
+#define DEF(NAME, RELOC, REQ) \
+ { #NAME, sizeof (#NAME)-1, O_##NAME, RELOC, REQ}
+
+static const struct arc_reloc_op_tag
+{
+ /* String to lookup. */
+ const char *name;
+ /* Size of the string. */
+ size_t length;
+ /* Which operator to use. */
+ operatorT op;
+ extended_bfd_reloc_code_real_type reloc;
+ /* Allows complex relocation expression like identifier@reloc +
+ const. */
+ unsigned int complex_expr : 1;
}
+ arc_reloc_op[] =
+{
+ DEF (gotoff, BFD_RELOC_ARC_GOTOFF, 1),
+ DEF (gotpc, BFD_RELOC_ARC_GOTPC32, 0),
+ DEF (plt, BFD_RELOC_ARC_PLT32, 0),
+ DEF (sda, DUMMY_RELOC_ARC_ENTRY, 1),
+ DEF (pcl, BFD_RELOC_ARC_PC32, 1),
+ DEF (tlsgd, BFD_RELOC_ARC_TLS_GD_GOT, 0),
+ DEF (tlsie, BFD_RELOC_ARC_TLS_IE_GOT, 0),
+ DEF (tpoff9, BFD_RELOC_ARC_TLS_LE_S9, 0),
+ DEF (tpoff, BFD_RELOC_ARC_TLS_LE_32, 1),
+ DEF (dtpoff9, BFD_RELOC_ARC_TLS_DTPOFF_S9, 0),
+ DEF (dtpoff, BFD_RELOC_ARC_TLS_DTPOFF, 0),
+};
-/* This function is called once, at assembler startup time. It should
- set up all the tables, etc. that the MD part of the assembler will need.
- Opcode selection is deferred until later because we might see a .option
- command. */
+static const int arc_num_reloc_op
+= sizeof (arc_reloc_op) / sizeof (*arc_reloc_op);
-void
-md_begin ()
+/* Structure for relaxable instruction that have to be swapped with a
+ smaller alternative instruction. */
+struct arc_relaxable_ins
{
- /* The endianness can be chosen "at the factory". */
- target_big_endian = byte_order == BIG_ENDIAN;
+ /* Mnemonic that should be checked. */
+ const char *mnemonic_r;
- if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, arc_mach_type))
- as_warn ("could not set architecture and machine");
+ /* Operands that should be checked.
+ Indexes of operands from operand array. */
+ enum rlx_operand_type operands[6];
- /* This call is necessary because we need to initialize `arc_operand_map'
- which may be needed before we see the first insn. */
- arc_opcode_init_tables (arc_get_opcode_mach (arc_mach_type,
- target_big_endian));
-}
+ /* Flags that should be checked. */
+ unsigned flag_classes[5];
-/* Initialize the various opcode and operand tables.
- MACH is one of bfd_mach_arc_xxx. */
-static void
-init_opcode_tables (mach)
- int mach;
+ /* Mnemonic (smaller) alternative to be used later for relaxation. */
+ const char *mnemonic_alt;
+
+ /* Index of operand that generic relaxation has to check. */
+ unsigned opcheckidx;
+
+ /* Base subtype index used. */
+ enum arc_rlx_types subtype;
+};
+
+#define RELAX_TABLE_ENTRY(BITS, ISSIGNED, SIZE, NEXT) \
+ { (ISSIGNED) ? ((1 << ((BITS) - 1)) - 1) : ((1 << (BITS)) - 1), \
+ (ISSIGNED) ? -(1 << ((BITS) - 1)) : 0, \
+ (SIZE), \
+ (NEXT) } \
+
+#define RELAX_TABLE_ENTRY_MAX(ISSIGNED, SIZE, NEXT) \
+ { (ISSIGNED) ? 0x7FFFFFFF : 0xFFFFFFFF, \
+ (ISSIGNED) ? -(0x7FFFFFFF) : 0, \
+ (SIZE), \
+ (NEXT) } \
+
+
+/* ARC relaxation table. */
+const relax_typeS md_relax_table[] =
{
- int i;
- char *last;
+ /* Fake entry. */
+ {0, 0, 0, 0},
+
+ /* BL_S s13 ->
+ BL s25. */
+ RELAX_TABLE_ENTRY(13, 1, 2, ARC_RLX_BL),
+ RELAX_TABLE_ENTRY(25, 1, 4, ARC_RLX_NONE),
+
+ /* B_S s10 ->
+ B s25. */
+ RELAX_TABLE_ENTRY(10, 1, 2, ARC_RLX_B),
+ RELAX_TABLE_ENTRY(25, 1, 4, ARC_RLX_NONE),
+
+ /* ADD_S c,b, u3 ->
+ ADD<.f> a,b,u6 ->
+ ADD<.f> a,b,limm. */
+ RELAX_TABLE_ENTRY(3, 0, 2, ARC_RLX_ADD_U6),
+ RELAX_TABLE_ENTRY(6, 0, 4, ARC_RLX_ADD_LIMM),
+ RELAX_TABLE_ENTRY_MAX(0, 8, ARC_RLX_NONE),
+
+ /* LD_S a, [b, u7] ->
+ LD<zz><.x><.aa><.di> a, [b, s9] ->
+ LD<zz><.x><.aa><.di> a, [b, limm] */
+ RELAX_TABLE_ENTRY(7, 0, 2, ARC_RLX_LD_S9),
+ RELAX_TABLE_ENTRY(9, 1, 4, ARC_RLX_LD_LIMM),
+ RELAX_TABLE_ENTRY_MAX(1, 8, ARC_RLX_NONE),
+
+ /* MOV_S b, u8 ->
+ MOV<.f> b, s12 ->
+ MOV<.f> b, limm. */
+ RELAX_TABLE_ENTRY(8, 0, 2, ARC_RLX_MOV_S12),
+ RELAX_TABLE_ENTRY(8, 0, 4, ARC_RLX_MOV_LIMM),
+ RELAX_TABLE_ENTRY_MAX(0, 8, ARC_RLX_NONE),
+
+ /* SUB_S c, b, u3 ->
+ SUB<.f> a, b, u6 ->
+ SUB<.f> a, b, limm. */
+ RELAX_TABLE_ENTRY(3, 0, 2, ARC_RLX_SUB_U6),
+ RELAX_TABLE_ENTRY(6, 0, 4, ARC_RLX_SUB_LIMM),
+ RELAX_TABLE_ENTRY_MAX(0, 8, ARC_RLX_NONE),
+
+ /* MPY<.f> a, b, u6 ->
+ MPY<.f> a, b, limm. */
+ RELAX_TABLE_ENTRY(6, 0, 4, ARC_RLX_MPY_LIMM),
+ RELAX_TABLE_ENTRY_MAX(0, 8, ARC_RLX_NONE),
+
+ /* MOV<.f><.cc> b, u6 ->
+ MOV<.f><.cc> b, limm. */
+ RELAX_TABLE_ENTRY(6, 0, 4, ARC_RLX_MOV_RLIMM),
+ RELAX_TABLE_ENTRY_MAX(0, 8, ARC_RLX_NONE),
+
+ /* ADD<.f><.cc> b, b, u6 ->
+ ADD<.f><.cc> b, b, limm. */
+ RELAX_TABLE_ENTRY(6, 0, 4, ARC_RLX_ADD_RRLIMM),
+ RELAX_TABLE_ENTRY_MAX(0, 8, ARC_RLX_NONE),
+};
+
+/* Order of this table's entries matters! */
+const struct arc_relaxable_ins arc_relaxable_insns[] =
+{
+ { "bl", { IMMEDIATE }, { 0 }, "bl_s", 0, ARC_RLX_BL_S },
+ { "b", { IMMEDIATE }, { 0 }, "b_s", 0, ARC_RLX_B_S },
+ { "add", { REGISTER, REGISTER_DUP, IMMEDIATE }, { 5, 1, 0 }, "add",
+ 2, ARC_RLX_ADD_RRU6},
+ { "add", { REGISTER_S, REGISTER_S, IMMEDIATE }, { 0 }, "add_s", 2,
+ ARC_RLX_ADD_U3 },
+ { "add", { REGISTER, REGISTER, IMMEDIATE }, { 5, 0 }, "add", 2,
+ ARC_RLX_ADD_U6 },
+ { "ld", { REGISTER_S, BRACKET, REGISTER_S, IMMEDIATE, BRACKET },
+ { 0 }, "ld_s", 3, ARC_RLX_LD_U7 },
+ { "ld", { REGISTER, BRACKET, REGISTER_NO_GP, IMMEDIATE, BRACKET },
+ { 11, 4, 14, 17, 0 }, "ld", 3, ARC_RLX_LD_S9 },
+ { "mov", { REGISTER_S, IMMEDIATE }, { 0 }, "mov_s", 1, ARC_RLX_MOV_U8 },
+ { "mov", { REGISTER, IMMEDIATE }, { 5, 0 }, "mov", 1, ARC_RLX_MOV_S12 },
+ { "mov", { REGISTER, IMMEDIATE }, { 5, 1, 0 },"mov", 1, ARC_RLX_MOV_RU6 },
+ { "sub", { REGISTER_S, REGISTER_S, IMMEDIATE }, { 0 }, "sub_s", 2,
+ ARC_RLX_SUB_U3 },
+ { "sub", { REGISTER, REGISTER, IMMEDIATE }, { 5, 0 }, "sub", 2,
+ ARC_RLX_SUB_U6 },
+ { "mpy", { REGISTER, REGISTER, IMMEDIATE }, { 5, 0 }, "mpy", 2,
+ ARC_RLX_MPY_U6 },
+};
+
+const unsigned arc_num_relaxable_ins = ARRAY_SIZE (arc_relaxable_insns);
+
+/* Flags to set in the elf header. */
+static flagword arc_eflag = 0x00;
+
+/* Pre-defined "_GLOBAL_OFFSET_TABLE_". */
+symbolS * GOT_symbol = 0;
- if ((arc_suffix_hash = hash_new ()) == NULL)
- as_fatal ("virtual memory exhausted");
+/* Set to TRUE when we assemble instructions. */
+static bfd_boolean assembling_insn = FALSE;
- if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach))
- as_warn ("could not set architecture and machine");
+/* Functions implementation. */
+
+/* Like md_number_to_chars but used for limms. The 4-byte limm value,
+ is encoded as 'middle-endian' for a little-endian target. FIXME!
+ this function is used for regular 4 byte instructions as well. */
+
+static void
+md_number_to_chars_midend (char *buf, valueT val, int n)
+{
+ if (n == 4)
+ {
+ md_number_to_chars (buf, (val & 0xffff0000) >> 16, 2);
+ md_number_to_chars (buf + 2, (val & 0xffff), 2);
+ }
+ else
+ {
+ md_number_to_chars (buf, val, n);
+ }
+}
- /* This initializes a few things in arc-opc.c that we need.
- This must be called before the various arc_xxx_supported fns. */
- arc_opcode_init_tables (arc_get_opcode_mach (mach, target_big_endian));
+/* Here ends all the ARCompact extension instruction assembling
+ stuff. */
- /* Only put the first entry of each equivalently named suffix in the
- table. */
- last = "";
- for (i = 0; i < arc_suffixes_count; i++)
+static void
+arc_extra_reloc (int r_type)
+{
+ char *sym_name, c;
+ symbolS *sym, *lab = NULL;
+
+ if (*input_line_pointer == '@')
+ input_line_pointer++;
+ c = get_symbol_name (&sym_name);
+ sym = symbol_find_or_make (sym_name);
+ restore_line_pointer (c);
+ if (c == ',' && r_type == BFD_RELOC_ARC_TLS_GD_LD)
{
- if (strcmp (arc_suffixes[i].name, last) != 0)
- hash_insert (arc_suffix_hash, arc_suffixes[i].name, (PTR) (arc_suffixes + i));
- last = arc_suffixes[i].name;
+ ++input_line_pointer;
+ char *lab_name;
+ c = get_symbol_name (&lab_name);
+ lab = symbol_find_or_make (lab_name);
+ restore_line_pointer (c);
}
+ fixS *fixP
+ = fix_new (frag_now, /* Which frag? */
+ frag_now_fix (), /* Where in that frag? */
+ 2, /* size: 1, 2, or 4 usually. */
+ sym, /* X_add_symbol. */
+ 0, /* X_add_number. */
+ FALSE, /* TRUE if PC-relative relocation. */
+ r_type /* Relocation type. */);
+ fixP->fx_subsy = lab;
+}
- /* Since registers don't have a prefix, we put them in the symbol table so
- they can't be used as symbols. This also simplifies argument parsing as
- we can let gas parse registers for us. The recorded register number is
- the address of the register's entry in arc_reg_names.
+static symbolS *
+arc_lcomm_internal (int ignore ATTRIBUTE_UNUSED,
+ symbolS *symbolP, addressT size)
+{
+ addressT align = 0;
+ SKIP_WHITESPACE ();
- If the register name is already in the table, then the existing
- definition is assumed to be from an .ExtCoreRegister pseudo-op. */
+ if (*input_line_pointer == ',')
+ {
+ align = parse_align (1);
- for (i = 0; i < arc_reg_names_count; i++)
+ if (align == (addressT) -1)
+ return NULL;
+ }
+ else
{
- if (symbol_find (arc_reg_names[i].name))
- continue;
- /* Use symbol_create here instead of symbol_new so we don't try to
- output registers into the object file's symbol table. */
- symbol_table_insert (symbol_create (arc_reg_names[i].name,
- reg_section,
- (int) &arc_reg_names[i],
- &zero_address_frag));
+ if (size >= 8)
+ align = 3;
+ else if (size >= 4)
+ align = 2;
+ else if (size >= 2)
+ align = 1;
+ else
+ align = 0;
}
- /* Tell `.option' it's too late. */
- cpu_tables_init_p = 1;
+ bss_alloc (symbolP, size, align);
+ S_CLEAR_EXTERNAL (symbolP);
+
+ return symbolP;
}
-\f
-/* Insert an operand value into an instruction.
- If REG is non-NULL, it is a register number and ignore VAL. */
-
-static arc_insn
-arc_insert_operand (insn, operand, mods, reg, val, file, line)
- arc_insn insn;
- const struct arc_operand *operand;
- int mods;
- const struct arc_operand_value *reg;
- offsetT val;
- char *file;
- unsigned int line;
-{
- if (operand->bits != 32)
- {
- long min, max;
- offsetT test;
- if ((operand->flags & ARC_OPERAND_SIGNED) != 0)
+static void
+arc_lcomm (int ignore)
+{
+ symbolS *symbolP = s_comm_internal (ignore, arc_lcomm_internal);
+
+ if (symbolP)
+ symbol_get_bfdsym (symbolP)->flags |= BSF_OBJECT;
+}
+
+/* Select the cpu we're assembling for. */
+
+static void
+arc_option (int ignore ATTRIBUTE_UNUSED)
+{
+ int mach = -1;
+ char c;
+ char *cpu;
+
+ c = get_symbol_name (&cpu);
+ mach = arc_get_mach (cpu);
+
+ if (mach == -1)
+ goto bad_cpu;
+
+ if (!mach_type_specified_p)
+ {
+ if ((!strcmp ("ARC600", cpu))
+ || (!strcmp ("ARC601", cpu))
+ || (!strcmp ("A6", cpu)))
{
- if ((operand->flags & ARC_OPERAND_SIGNOPT) != 0)
- max = (1 << operand->bits) - 1;
- else
- max = (1 << (operand->bits - 1)) - 1;
- min = - (1 << (operand->bits - 1));
+ md_parse_option (OPTION_MCPU, "arc600");
}
- else
+ else if ((!strcmp ("ARC700", cpu))
+ || (!strcmp ("A7", cpu)))
{
- max = (1 << operand->bits) - 1;
- min = 0;
+ md_parse_option (OPTION_MCPU, "arc700");
}
-
- if ((operand->flags & ARC_OPERAND_NEGATIVE) != 0)
- test = - val;
- else
- test = val;
-
- if (test < (offsetT) min || test > (offsetT) max)
+ else if (!strcmp ("EM", cpu))
{
- const char *err =
- "operand out of range (%s not between %ld and %ld)";
- char buf[100];
-
- sprint_value (buf, test);
- if (file == (char *) NULL)
- as_warn (err, buf, min, max);
- else
- as_warn_where (file, line, err, buf, min, max);
+ md_parse_option (OPTION_MCPU, "arcem");
}
- }
-
- if (operand->insert)
- {
- const char *errmsg;
+ else if (!strcmp ("HS", cpu))
+ {
+ md_parse_option (OPTION_MCPU, "archs");
+ }
+ else
+ as_fatal ("could not find the architecture");
- errmsg = NULL;
- insn = (*operand->insert) (insn, operand, mods, reg, (long) val, &errmsg);
- if (errmsg != (const char *) NULL)
- as_warn (errmsg);
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach))
+ as_fatal ("could not set architecture and machine");
}
else
- insn |= (((long) val & ((1 << operand->bits) - 1))
- << operand->shift);
+ if (arc_mach_type != mach)
+ as_warn ("Command-line value overrides \".cpu\" directive");
- return insn;
-}
+ restore_line_pointer (c);
+ demand_empty_rest_of_line ();
+ return;
-/* We need to keep a list of fixups. We can't simply generate them as
- we go, because that would require us to first create the frag, and
- that would screw up references to ``.''. */
+ bad_cpu:
+ restore_line_pointer (c);
+ as_bad ("invalid identifier for \".cpu\"");
+ ignore_rest_of_line ();
+}
-struct arc_fixup {
- /* index into `arc_operands' */
- int opindex;
- expressionS exp;
-};
+/* Smartly print an expression. */
-#define MAX_FIXUPS 5
+static void
+debug_exp (expressionS *t)
+{
+ const char *name ATTRIBUTE_UNUSED;
+ const char *namemd ATTRIBUTE_UNUSED;
-#define MAX_SUFFIXES 5
+ pr_debug ("debug_exp: ");
-/* This routine is called for each instruction to be assembled. */
+ switch (t->X_op)
+ {
+ default: name = "unknown"; break;
+ case O_illegal: name = "O_illegal"; break;
+ case O_absent: name = "O_absent"; break;
+ case O_constant: name = "O_constant"; break;
+ case O_symbol: name = "O_symbol"; break;
+ case O_symbol_rva: name = "O_symbol_rva"; break;
+ case O_register: name = "O_register"; break;
+ case O_big: name = "O_big"; break;
+ case O_uminus: name = "O_uminus"; break;
+ case O_bit_not: name = "O_bit_not"; break;
+ case O_logical_not: name = "O_logical_not"; break;
+ case O_multiply: name = "O_multiply"; break;
+ case O_divide: name = "O_divide"; break;
+ case O_modulus: name = "O_modulus"; break;
+ case O_left_shift: name = "O_left_shift"; break;
+ case O_right_shift: name = "O_right_shift"; break;
+ case O_bit_inclusive_or: name = "O_bit_inclusive_or"; break;
+ case O_bit_or_not: name = "O_bit_or_not"; break;
+ case O_bit_exclusive_or: name = "O_bit_exclusive_or"; break;
+ case O_bit_and: name = "O_bit_and"; break;
+ case O_add: name = "O_add"; break;
+ case O_subtract: name = "O_subtract"; break;
+ case O_eq: name = "O_eq"; break;
+ case O_ne: name = "O_ne"; break;
+ case O_lt: name = "O_lt"; break;
+ case O_le: name = "O_le"; break;
+ case O_ge: name = "O_ge"; break;
+ case O_gt: name = "O_gt"; break;
+ case O_logical_and: name = "O_logical_and"; break;
+ case O_logical_or: name = "O_logical_or"; break;
+ case O_index: name = "O_index"; break;
+ case O_bracket: name = "O_bracket"; break;
+ }
-void
-md_assemble (str)
- char *str;
-{
- const struct arc_opcode *opcode;
- const struct arc_opcode *std_opcode;
- struct arc_opcode *ext_opcode;
- char *start;
- const char *last_errmsg = 0;
- arc_insn insn;
- static int init_tables_p = 0;
-
- /* Opcode table initialization is deferred until here because we have to
- wait for a possible .option command. */
- if (!init_tables_p)
+ switch (t->X_md)
{
- init_opcode_tables (arc_mach_type);
- init_tables_p = 1;
+ default: namemd = "unknown"; break;
+ case O_gotoff: namemd = "O_gotoff"; break;
+ case O_gotpc: namemd = "O_gotpc"; break;
+ case O_plt: namemd = "O_plt"; break;
+ case O_sda: namemd = "O_sda"; break;
+ case O_pcl: namemd = "O_pcl"; break;
+ case O_tlsgd: namemd = "O_tlsgd"; break;
+ case O_tlsie: namemd = "O_tlsie"; break;
+ case O_tpoff9: namemd = "O_tpoff9"; break;
+ case O_tpoff: namemd = "O_tpoff"; break;
+ case O_dtpoff9: namemd = "O_dtpoff9"; break;
+ case O_dtpoff: namemd = "O_dtpoff"; break;
}
- /* Skip leading white space. */
- while (ISSPACE (*str))
- str++;
+ pr_debug ("%s (%s, %s, %d, %s)", name,
+ (t->X_add_symbol) ? S_GET_NAME (t->X_add_symbol) : "--",
+ (t->X_op_symbol) ? S_GET_NAME (t->X_op_symbol) : "--",
+ (int) t->X_add_number,
+ (t->X_md) ? namemd : "--");
+ pr_debug ("\n");
+ fflush (stderr);
+}
+
+/* Parse the arguments to an opcode. */
- /* 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. */
+static int
+tokenize_arguments (char *str,
+ expressionS *tok,
+ int ntok)
+{
+ char *old_input_line_pointer;
+ bfd_boolean saw_comma = FALSE;
+ bfd_boolean saw_arg = FALSE;
+ int brk_lvl = 0;
+ int num_args = 0;
+ int i;
+ size_t len;
+ const struct arc_reloc_op_tag *r;
+ expressionS tmpE;
+ char *reloc_name, c;
- ext_opcode = arc_ext_opcodes;
- std_opcode = arc_opcode_lookup_asm (str);
+ memset (tok, 0, sizeof (*tok) * ntok);
- /* Keep looking until we find a match. */
+ /* Save and restore input_line_pointer around this function. */
+ old_input_line_pointer = input_line_pointer;
+ input_line_pointer = str;
- start = str;
- for (opcode = (ext_opcode ? ext_opcode : std_opcode);
- opcode != NULL;
- opcode = (ARC_OPCODE_NEXT_ASM (opcode)
- ? ARC_OPCODE_NEXT_ASM (opcode)
- : (ext_opcode ? ext_opcode = NULL, std_opcode : NULL)))
+ while (*input_line_pointer)
{
- int past_opcode_p, fc, num_suffixes;
- int fix_up_at = 0;
- char *syn;
- struct arc_fixup fixups[MAX_FIXUPS];
- /* Used as a sanity check. If we need a limm reloc, make sure we ask
- for an extra 4 bytes from frag_more. */
- int limm_reloc_p;
- int ext_suffix_p;
- const struct arc_operand_value *insn_suffixes[MAX_SUFFIXES];
-
- /* Is this opcode supported by the selected cpu? */
- if (! arc_opcode_supported (opcode))
- continue;
+ SKIP_WHITESPACE ();
+ switch (*input_line_pointer)
+ {
+ case '\0':
+ goto fini;
+
+ case ',':
+ input_line_pointer++;
+ if (saw_comma || !saw_arg)
+ goto err;
+ saw_comma = TRUE;
+ break;
- /* Scan the syntax string. If it doesn't match, try the next one. */
+ case '}':
+ case ']':
+ ++input_line_pointer;
+ --brk_lvl;
+ if (!saw_arg)
+ goto err;
+ tok->X_op = O_bracket;
+ ++tok;
+ ++num_args;
+ break;
- arc_opcode_init_insert ();
- insn = opcode->value;
- fc = 0;
- past_opcode_p = 0;
- num_suffixes = 0;
- limm_reloc_p = 0;
- ext_suffix_p = 0;
+ case '{':
+ case '[':
+ input_line_pointer++;
+ if (brk_lvl)
+ goto err;
+ ++brk_lvl;
+ tok->X_op = O_bracket;
+ ++tok;
+ ++num_args;
+ break;
- /* We don't check for (*str != '\0') here because we want to parse
- any trailing fake arguments in the syntax string. */
- for (str = start, syn = opcode->syntax; *syn != '\0';)
- {
- int mods;
- const struct arc_operand *operand;
+ case '@':
+ /* We have labels, function names and relocations, all
+ starting with @ symbol. Sort them out. */
+ if (saw_arg && !saw_comma)
+ goto err;
+
+ /* Parse @label. */
+ tok->X_op = O_symbol;
+ tok->X_md = O_absent;
+ expression (tok);
+ if (*input_line_pointer != '@')
+ goto normalsymbol; /* This is not a relocation. */
+
+ relocationsym:
+
+ /* A relocation opernad has the following form
+ @identifier@relocation_type. The identifier is already
+ in tok! */
+ if (tok->X_op != O_symbol)
+ {
+ as_bad (_("No valid label relocation operand"));
+ goto err;
+ }
- /* Non operand chars must match exactly. */
- if (*syn != '%' || *++syn == '%')
+ /* Parse @relocation_type. */
+ input_line_pointer++;
+ c = get_symbol_name (&reloc_name);
+ len = input_line_pointer - reloc_name;
+ if (len == 0)
{
- /* Handle '+' specially as we want to allow "ld r0,[sp-4]". */
- /* ??? The syntax has changed to [sp,-4]. */
- if (0 && *syn == '+' && *str == '-')
- {
- /* Skip over syn's +, but leave str's - alone.
- That makes the case identical to "ld r0,[sp+-4]". */
- ++syn;
- }
- else if (*str == *syn)
- {
- if (*syn == ' ')
- past_opcode_p = 1;
- ++syn;
- ++str;
- }
- else
- break;
- continue;
+ as_bad (_("No relocation operand"));
+ goto err;
}
- /* We have an operand. Pick out any modifiers. */
- mods = 0;
- while (ARC_MOD_P (arc_operands[arc_operand_map[(int) *syn]].flags))
+ /* Go through known relocation and try to find a match. */
+ r = &arc_reloc_op[0];
+ for (i = arc_num_reloc_op - 1; i >= 0; i--, r++)
+ if (len == r->length
+ && memcmp (reloc_name, r->name, len) == 0)
+ break;
+ if (i < 0)
{
- mods |= arc_operands[arc_operand_map[(int) *syn]].flags & ARC_MOD_BITS;
- ++syn;
+ as_bad (_("Unknown relocation operand: @%s"), reloc_name);
+ goto err;
}
- operand = arc_operands + arc_operand_map[(int) *syn];
- if (operand->fmt == 0)
- as_fatal ("unknown syntax format character `%c'", *syn);
- if (operand->flags & ARC_OPERAND_FAKE)
+ *input_line_pointer = c;
+ SKIP_WHITESPACE_AFTER_NAME ();
+ /* Extra check for TLS: base. */
+ if (*input_line_pointer == '@')
{
- const char *errmsg = NULL;
- if (operand->insert)
+ symbolS *base;
+ if (tok->X_op_symbol != NULL
+ || tok->X_op != O_symbol)
{
- insn = (*operand->insert) (insn, operand, mods, NULL, 0, &errmsg);
- if (errmsg != (const char *) NULL)
- {
- last_errmsg = errmsg;
- if (operand->flags & ARC_OPERAND_ERROR)
- {
- as_bad (errmsg);
- return;
- }
- else if (operand->flags & ARC_OPERAND_WARN)
- as_warn (errmsg);
- break;
- }
- if (limm_reloc_p
- && (operand->flags && operand->flags & ARC_OPERAND_LIMM)
- && (operand->flags &
- (ARC_OPERAND_ABSOLUTE_BRANCH | ARC_OPERAND_ADDRESS)))
- {
- fixups[fix_up_at].opindex = arc_operand_map[operand->fmt];
- }
+ as_bad (_("Unable to parse TLS base: %s"),
+ input_line_pointer);
+ goto err;
}
- ++syn;
+ input_line_pointer++;
+ char *sym_name;
+ c = get_symbol_name (&sym_name);
+ base = symbol_find_or_make (sym_name);
+ tok->X_op = O_subtract;
+ tok->X_op_symbol = base;
+ restore_line_pointer (c);
+ tmpE.X_add_number = 0;
}
- /* Are we finished with suffixes? */
- else if (!past_opcode_p)
+ else if ((*input_line_pointer != '+')
+ && (*input_line_pointer != '-'))
{
- int found;
- char c;
- char *s, *t;
- const struct arc_operand_value *suf, *suffix_end;
- const struct arc_operand_value *suffix = NULL;
-
- if (!(operand->flags & ARC_OPERAND_SUFFIX))
- abort ();
-
- /* If we're at a space in the input string, we want to skip the
- remaining suffixes. There may be some fake ones though, so
- just go on to try the next one. */
- if (*str == ' ')
- {
- ++syn;
- continue;
- }
-
- s = str;
- if (mods & ARC_MOD_DOT)
- {
- if (*s != '.')
- break;
- ++s;
- }
- else
- {
- /* This can happen in "b.nd foo" and we're currently looking
- for "%q" (ie: a condition code suffix). */
- if (*s == '.')
- {
- ++syn;
- continue;
- }
- }
-
- /* Pick the suffix out and look it up via the hash table. */
- for (t = s; *t && ISALNUM (*t); ++t)
- continue;
- c = *t;
- *t = '\0';
- if ((suf = get_ext_suffix (s)))
- ext_suffix_p = 1;
- else
- suf = hash_find (arc_suffix_hash, s);
- if (!suf)
- {
- /* This can happen in "blle foo" and we're currently using
- the template "b%q%.n %j". The "bl" insn occurs later in
- the table so "lle" isn't an illegal suffix. */
- *t = c;
- break;
- }
-
- /* Is it the right type? Note that the same character is used
- several times, so we have to examine all of them. This is
- relatively efficient as equivalent entries are kept
- together. If it's not the right type, don't increment `str'
- so we try the next one in the series. */
- found = 0;
- if (ext_suffix_p && arc_operands[suf->type].fmt == *syn)
- {
- /* Insert the suffix's value into the insn. */
- *t = c;
- if (operand->insert)
- insn = (*operand->insert) (insn, operand,
- mods, NULL, suf->value,
- NULL);
- else
- insn |= suf->value << operand->shift;
-
- str = t;
- found = 1;
- }
- else
- {
- *t = c;
- suffix_end = arc_suffixes + arc_suffixes_count;
- for (suffix = suf;
- suffix < suffix_end && strcmp (suffix->name, suf->name) == 0;
- ++suffix)
- {
- if (arc_operands[suffix->type].fmt == *syn)
- {
- /* Insert the suffix's value into the insn. */
- if (operand->insert)
- insn = (*operand->insert) (insn, operand,
- mods, NULL, suffix->value,
- NULL);
- else
- insn |= suffix->value << operand->shift;
-
- str = t;
- found = 1;
- break;
- }
- }
- }
- ++syn;
- if (!found)
- /* Wrong type. Just go on to try next insn entry. */
- ;
- else
- {
- if (num_suffixes == MAX_SUFFIXES)
- as_bad ("too many suffixes");
- else
- insn_suffixes[num_suffixes++] = suffix;
- }
+ tmpE.X_add_number = 0;
}
else
- /* This is either a register or an expression of some kind. */
{
- char *hold;
- const struct arc_operand_value *reg = NULL;
- long value = 0;
- expressionS exp;
-
- if (operand->flags & ARC_OPERAND_SUFFIX)
- abort ();
-
- /* Is there anything left to parse?
- We don't check for this at the top because we want to parse
- any trailing fake arguments in the syntax string. */
- if (is_end_of_line[(unsigned char) *str])
- break;
-
- /* Parse the operand. */
- hold = input_line_pointer;
- input_line_pointer = str;
- expression (&exp);
- str = input_line_pointer;
- input_line_pointer = hold;
-
- if (exp.X_op == O_illegal)
- as_bad ("illegal operand");
- else if (exp.X_op == O_absent)
- as_bad ("missing operand");
- else if (exp.X_op == O_constant)
- {
- value = exp.X_add_number;
- }
- else if (exp.X_op == O_register)
+ /* Parse the constant of a complex relocation expression
+ like @identifier@reloc +/- const. */
+ if (! r->complex_expr)
{
- reg = (struct arc_operand_value *) exp.X_add_number;
+ as_bad (_("@%s is not a complex relocation."), r->name);
+ goto err;
}
-#define IS_REG_DEST_OPERAND(o) ((o) == 'a')
- else if (IS_REG_DEST_OPERAND (*syn))
- as_bad ("symbol as destination register");
- else
+ expression (&tmpE);
+ if (tmpE.X_op != O_constant)
{
- if (!strncmp (str, "@h30", 4))
- {
- arc_code_symbol (&exp);
- str += 4;
- }
- /* We need to generate a fixup for this expression. */
- if (fc >= MAX_FIXUPS)
- as_fatal ("too many fixups");
- fixups[fc].exp = exp;
- /* We don't support shimm relocs. break here to force
- the assembler to output a limm. */
-#define IS_REG_SHIMM_OFFSET(o) ((o) == 'd')
- if (IS_REG_SHIMM_OFFSET (*syn))
- break;
- /* If this is a register constant (IE: one whose
- register value gets stored as 61-63) then this
- must be a limm. */
- /* ??? This bit could use some cleaning up.
- Referencing the format chars like this goes
- against style. */
- if (IS_SYMBOL_OPERAND (*syn))
- {
- const char *junk;
- limm_reloc_p = 1;
- /* Save this, we don't yet know what reloc to use. */
- fix_up_at = fc;
- /* Tell insert_reg we need a limm. This is
- needed because the value at this point is
- zero, a shimm. */
- /* ??? We need a cleaner interface than this. */
- (*arc_operands[arc_operand_map['Q']].insert)
- (insn, operand, mods, reg, 0L, &junk);
- }
- else
- fixups[fc].opindex = arc_operand_map[(int) *syn];
- ++fc;
- value = 0;
+ as_bad (_("Bad expression: @%s + %s."),
+ r->name, input_line_pointer);
+ goto err;
}
+ }
- /* Insert the register or expression into the instruction. */
- if (operand->insert)
- {
- const char *errmsg = NULL;
- insn = (*operand->insert) (insn, operand, mods,
- reg, (long) value, &errmsg);
- if (errmsg != (const char *) NULL)
- {
- last_errmsg = errmsg;
- if (operand->flags & ARC_OPERAND_ERROR)
- {
- as_bad (errmsg);
- return;
- }
- else if (operand->flags & ARC_OPERAND_WARN)
- as_warn (errmsg);
- break;
- }
- }
- else
- insn |= (value & ((1 << operand->bits) - 1)) << operand->shift;
-
- ++syn;
- }
- }
-
- /* If we're at the end of the syntax string, we're done. */
- /* FIXME: try to move this to a separate function. */
- if (*syn == '\0')
- {
- int i;
- char *f;
- long limm, limm_p;
-
- /* For the moment we assume a valid `str' can only contain blanks
- now. IE: We needn't try again with a longer version of the
- insn and it is assumed that longer versions of insns appear
- before shorter ones (eg: lsr r2,r3,1 vs lsr r2,r3). */
+ tok->X_md = r->op;
+ tok->X_add_number = tmpE.X_add_number;
- while (ISSPACE (*str))
- ++str;
+ debug_exp (tok);
- if (!is_end_of_line[(unsigned char) *str])
- as_bad ("junk at end of line: `%s'", str);
+ saw_comma = FALSE;
+ saw_arg = TRUE;
+ tok++;
+ num_args++;
+ break;
- /* Is there a limm value? */
- limm_p = arc_opcode_limm_p (&limm);
+ case '%':
+ /* Can be a register. */
+ ++input_line_pointer;
+ /* Fall through. */
+ default:
- /* Perform various error and warning tests. */
+ if (saw_arg && !saw_comma)
+ goto err;
- {
- static int in_delay_slot_p = 0;
- static int prev_insn_needs_cc_nop_p = 0;
- /* delay slot type seen */
- int delay_slot_type = ARC_DELAY_NONE;
- /* conditional execution flag seen */
- int conditional = 0;
- /* 1 if condition codes are being set */
- int cc_set_p = 0;
- /* 1 if conditional branch, including `b' "branch always" */
- int cond_branch_p = opcode->flags & ARC_OPCODE_COND_BRANCH;
-
- for (i = 0; i < num_suffixes; ++i)
- {
- switch (arc_operands[insn_suffixes[i]->type].fmt)
- {
- case 'n':
- delay_slot_type = insn_suffixes[i]->value;
- break;
- case 'q':
- conditional = insn_suffixes[i]->value;
- break;
- case 'f':
- cc_set_p = 1;
- break;
- }
- }
+ tok->X_op = O_absent;
+ tok->X_md = O_absent;
+ expression (tok);
- /* Putting an insn with a limm value in a delay slot is supposed to
- be legal, but let's warn the user anyway. Ditto for 8 byte
- jumps with delay slots. */
- if (in_delay_slot_p && limm_p)
- as_warn ("8 byte instruction in delay slot");
- if (delay_slot_type != ARC_DELAY_NONE
- && limm_p && arc_insn_not_jl (insn)) /* except for jl addr */
- as_warn ("8 byte jump instruction with delay slot");
- in_delay_slot_p = (delay_slot_type != ARC_DELAY_NONE) && !limm_p;
-
- /* Warn when a conditional branch immediately follows a set of
- the condition codes. Note that this needn't be done if the
- insn that sets the condition codes uses a limm. */
- if (cond_branch_p && conditional != 0 /* 0 = "always" */
- && prev_insn_needs_cc_nop_p && arc_mach_type == bfd_mach_arc_5)
- as_warn ("conditional branch follows set of flags");
- prev_insn_needs_cc_nop_p =
- /* FIXME: ??? not required:
- (delay_slot_type != ARC_DELAY_NONE) && */
- cc_set_p && !limm_p;
- }
+ /* Legacy: There are cases when we have
+ identifier@relocation_type, if it is the case parse the
+ relocation type as well. */
+ if (*input_line_pointer == '@')
+ goto relocationsym;
- /* Write out the instruction.
- It is important to fetch enough space in one call to `frag_more'.
- We use (f - frag_now->fr_literal) to compute where we are and we
- don't want frag_now to change between calls. */
- if (limm_p)
- {
- f = frag_more (8);
- md_number_to_chars (f, insn, 4);
- md_number_to_chars (f + 4, limm, 4);
- dwarf2_emit_insn (8);
- }
- else if (limm_reloc_p)
- {
- /* We need a limm reloc, but the tables think we don't. */
- abort ();
- }
- else
- {
- f = frag_more (4);
- md_number_to_chars (f, insn, 4);
- dwarf2_emit_insn (4);
- }
+ normalsymbol:
+ debug_exp (tok);
- /* Create any fixups. */
- for (i = 0; i < fc; ++i)
- {
- int op_type, reloc_type;
- expressionS exptmp;
- const struct arc_operand *operand;
-
- /* Create a fixup for this operand.
- At this point we do not use a bfd_reloc_code_real_type for
- operands residing in the insn, but instead just use the
- operand index. This lets us easily handle fixups for any
- operand type, although that is admittedly not a very exciting
- feature. We pick a BFD reloc type in md_apply_fix3.
-
- Limm values (4 byte immediate "constants") must be treated
- normally because they're not part of the actual insn word
- and thus the insertion routines don't handle them. */
-
- if (arc_operands[fixups[i].opindex].flags & ARC_OPERAND_LIMM)
- {
- /* Modify the fixup addend as required by the cpu. */
- fixups[i].exp.X_add_number += arc_limm_fixup_adjust (insn);
- op_type = fixups[i].opindex;
- /* FIXME: can we add this data to the operand table? */
- if (op_type == arc_operand_map['L']
- || op_type == arc_operand_map['s']
- || op_type == arc_operand_map['o']
- || op_type == arc_operand_map['O'])
- reloc_type = BFD_RELOC_32;
- else if (op_type == arc_operand_map['J'])
- reloc_type = BFD_RELOC_ARC_B26;
- else
- abort ();
- reloc_type = get_arc_exp_reloc_type (1, reloc_type,
- &fixups[i].exp,
- &exptmp);
- }
- else
- {
- op_type = get_arc_exp_reloc_type (0, fixups[i].opindex,
- &fixups[i].exp, &exptmp);
- reloc_type = op_type + (int) BFD_RELOC_UNUSED;
- }
- operand = &arc_operands[op_type];
- fix_new_exp (frag_now,
- ((f - frag_now->fr_literal)
- + (operand->flags & ARC_OPERAND_LIMM ? 4 : 0)), 4,
- &exptmp,
- (operand->flags & ARC_OPERAND_RELATIVE_BRANCH) != 0,
- (bfd_reloc_code_real_type) reloc_type);
- }
+ if (tok->X_op == O_illegal || tok->X_op == O_absent)
+ goto err;
- /* All done. */
- return;
+ saw_comma = FALSE;
+ saw_arg = TRUE;
+ tok++;
+ num_args++;
+ break;
}
-
- /* Try the next entry. */
}
- if (NULL == last_errmsg)
- as_bad ("bad instruction `%s'", start);
+ fini:
+ if (saw_comma || brk_lvl)
+ goto err;
+ input_line_pointer = old_input_line_pointer;
+
+ return num_args;
+
+ err:
+ if (brk_lvl)
+ as_bad (_("Brackets in operand field incorrect"));
+ else if (saw_comma)
+ as_bad (_("extra comma"));
+ else if (!saw_arg)
+ as_bad (_("missing argument"));
else
- as_bad (last_errmsg);
+ as_bad (_("missing comma or colon"));
+ input_line_pointer = old_input_line_pointer;
+ return -1;
}
-\f
-static void
-arc_extoper (opertype)
- int opertype;
+
+/* Parse the flags to a structure. */
+
+static int
+tokenize_flags (const char *str,
+ struct arc_flags flags[],
+ int nflg)
{
- char *name;
- char *mode;
- char c;
- char *p;
- int imode = 0;
- int number;
- struct arc_ext_operand_value *ext_oper;
- symbolS *symbolP;
+ char *old_input_line_pointer;
+ bfd_boolean saw_flg = FALSE;
+ bfd_boolean saw_dot = FALSE;
+ int num_flags = 0;
+ size_t flgnamelen;
- segT old_sec;
- int old_subsec;
+ memset (flags, 0, sizeof (*flags) * nflg);
- name = input_line_pointer;
- c = get_symbol_end ();
- name = xstrdup (name);
+ /* Save and restore input_line_pointer around this function. */
+ old_input_line_pointer = input_line_pointer;
+ input_line_pointer = (char *) str;
- p = name;
- while (*p)
+ while (*input_line_pointer)
{
- *p = TOLOWER (*p);
- p++;
- }
+ switch (*input_line_pointer)
+ {
+ case ' ':
+ case '\0':
+ goto fini;
+
+ case '.':
+ input_line_pointer++;
+ if (saw_dot)
+ goto err;
+ saw_dot = TRUE;
+ saw_flg = FALSE;
+ break;
- /* just after name is now '\0' */
- p = input_line_pointer;
- *p = c;
- SKIP_WHITESPACE ();
+ default:
+ if (saw_flg && !saw_dot)
+ goto err;
- if (*input_line_pointer != ',')
- {
- as_bad ("expected comma after operand name");
- ignore_rest_of_line ();
- free (name);
- return;
- }
+ if (num_flags >= nflg)
+ goto err;
- input_line_pointer++; /* skip ',' */
- number = get_absolute_expression ();
+ flgnamelen = strspn (input_line_pointer, "abcdefghilmnopqrstvwxz");
+ if (flgnamelen > MAX_FLAG_NAME_LENGHT)
+ goto err;
- if (number < 0)
- {
- as_bad ("negative operand number %d", number);
- ignore_rest_of_line ();
- free (name);
- return;
+ memcpy (flags->name, input_line_pointer, flgnamelen);
+
+ input_line_pointer += flgnamelen;
+ flags++;
+ saw_dot = FALSE;
+ saw_flg = TRUE;
+ num_flags++;
+ break;
+ }
}
- if (opertype)
+ fini:
+ input_line_pointer = old_input_line_pointer;
+ return num_flags;
+
+ err:
+ if (saw_dot)
+ as_bad (_("extra dot"));
+ else if (!saw_flg)
+ as_bad (_("unrecognized flag"));
+ else
+ as_bad (_("failed to parse flags"));
+ input_line_pointer = old_input_line_pointer;
+ return -1;
+}
+
+/* Apply the fixups in order. */
+
+static void
+apply_fixups (struct arc_insn *insn, fragS *fragP, int fix)
+{
+ int i;
+
+ for (i = 0; i < insn->nfixups; i++)
{
- SKIP_WHITESPACE ();
+ struct arc_fixup *fixup = &insn->fixups[i];
+ int size, pcrel, offset = 0;
- if (*input_line_pointer != ',')
- {
- as_bad ("expected comma after register-number");
- ignore_rest_of_line ();
- free (name);
- return;
- }
+ /* FIXME! the reloc size is wrong in the BFD file.
+ When it is fixed please delete me. */
+ size = (insn->short_insn && !fixup->islong) ? 2 : 4;
- input_line_pointer++; /* skip ',' */
- mode = input_line_pointer;
+ if (fixup->islong)
+ offset = (insn->short_insn) ? 2 : 4;
- if (!strncmp (mode, "r|w", 3))
+ /* Some fixups are only used internally, thus no howto. */
+ if ((int) fixup->reloc == 0)
+ as_fatal (_("Unhandled reloc type"));
+
+ if ((int) fixup->reloc < 0)
{
- imode = 0;
- input_line_pointer += 3;
+ /* FIXME! the reloc size is wrong in the BFD file.
+ When it is fixed please enable me.
+ size = (insn->short_insn && !fixup->islong) ? 2 : 4; */
+ pcrel = fixup->pcrel;
}
else
{
- if (!strncmp (mode, "r", 1))
- {
- imode = ARC_REGISTER_READONLY;
- input_line_pointer += 1;
- }
- else
- {
- if (strncmp (mode, "w", 1))
- {
- as_bad ("invalid mode");
- ignore_rest_of_line ();
- free (name);
- return;
- }
- else
- {
- imode = ARC_REGISTER_WRITEONLY;
- input_line_pointer += 1;
- }
- }
+ reloc_howto_type *reloc_howto =
+ bfd_reloc_type_lookup (stdoutput,
+ (bfd_reloc_code_real_type) fixup->reloc);
+ gas_assert (reloc_howto);
+
+ /* FIXME! the reloc size is wrong in the BFD file.
+ When it is fixed please enable me.
+ size = bfd_get_reloc_size (reloc_howto); */
+ pcrel = reloc_howto->pc_relative;
}
- SKIP_WHITESPACE ();
- if (1 == opertype)
- {
- if (*input_line_pointer != ',')
- {
- as_bad ("expected comma after register-mode");
- ignore_rest_of_line ();
- free (name);
- return;
- }
- input_line_pointer++; /* skip ',' */
-
- if (!strncmp (input_line_pointer, "cannot_shortcut", 15))
- {
- imode |= arc_get_noshortcut_flag ();
- input_line_pointer += 15;
- }
- else
- {
- if (strncmp (input_line_pointer, "can_shortcut", 12))
- {
- as_bad ("shortcut designator invalid");
- ignore_rest_of_line ();
- free (name);
- return;
- }
- else
- {
- input_line_pointer += 12;
- }
- }
+ pr_debug ("%s:%d: apply_fixups: new %s fixup (PCrel:%s) of size %d @ \
+offset %d + %d\n",
+ fragP->fr_file, fragP->fr_line,
+ (fixup->reloc < 0) ? "Internal" :
+ bfd_get_reloc_code_name (fixup->reloc),
+ pcrel ? "Y" : "N",
+ size, fix, offset);
+ fix_new_exp (fragP, fix + offset,
+ size, &fixup->exp, pcrel, fixup->reloc);
+
+ /* Check for ZOLs, and update symbol info if any. */
+ if (LP_INSN (insn->insn))
+ {
+ gas_assert (fixup->exp.X_add_symbol);
+ ARC_SET_FLAG (fixup->exp.X_add_symbol, ARC_FLAG_ZOL);
}
}
+}
- if ((opertype == 1) && number > 60)
- {
- as_bad ("core register value (%d) too large", number);
- ignore_rest_of_line ();
- free (name);
- return;
- }
+/* Actually output an instruction with its fixup. */
- if ((opertype == 0) && number > 31)
- {
- as_bad ("condition code value (%d) too large", number);
- ignore_rest_of_line ();
- free (name);
- return;
- }
+static void
+emit_insn0 (struct arc_insn *insn, char *where, bfd_boolean relax)
+{
+ char *f = where;
- ext_oper = (struct arc_ext_operand_value *) \
- xmalloc (sizeof (struct arc_ext_operand_value));
+ pr_debug ("Emit insn : 0x%x\n", insn->insn);
+ pr_debug ("\tShort : 0x%d\n", insn->short_insn);
+ pr_debug ("\tLong imm: 0x%lx\n", insn->limm);
- if (opertype)
+ /* Write out the instruction. */
+ if (insn->short_insn)
{
- /* If the symbol already exists, point it at the new definition. */
- if ((symbolP = symbol_find (name)))
+ if (insn->has_limm)
{
- if (S_GET_SEGMENT (symbolP) == reg_section)
- S_SET_VALUE (symbolP, (int) &ext_oper->operand);
- else
- {
- as_bad ("attempt to override symbol: %s", name);
- ignore_rest_of_line ();
- free (name);
- free (ext_oper);
- return;
- }
+ if (!relax)
+ f = frag_more (6);
+ md_number_to_chars (f, insn->insn, 2);
+ md_number_to_chars_midend (f + 2, insn->limm, 4);
+ dwarf2_emit_insn (6);
}
else
{
- /* If its not there, add it. */
- symbol_table_insert (symbol_create (name, reg_section,
- (int) &ext_oper->operand, &zero_address_frag));
+ if (!relax)
+ f = frag_more (2);
+ md_number_to_chars (f, insn->insn, 2);
+ dwarf2_emit_insn (2);
}
}
-
- ext_oper->operand.name = name;
- ext_oper->operand.value = number;
- ext_oper->operand.type = arc_operand_type (opertype);
- ext_oper->operand.flags = imode;
-
- ext_oper->next = arc_ext_operands;
- arc_ext_operands = ext_oper;
-
- /* OK, now that we know what this operand is, put a description in
- the arc extension section of the output file. */
-
- old_sec = now_seg;
- old_subsec = now_subseg;
-
- arc_set_ext_seg ();
-
- switch (opertype)
+ else
{
- case 0:
- p = frag_more (1);
- *p = 3 + strlen (name) + 1;
- p = frag_more (1);
- *p = EXT_COND_CODE;
- p = frag_more (1);
- *p = number;
- p = frag_more (strlen (name) + 1);
- strcpy (p, name);
- break;
- case 1:
- p = frag_more (1);
- *p = 3 + strlen (name) + 1;
- p = frag_more (1);
- *p = EXT_CORE_REGISTER;
- p = frag_more (1);
- *p = number;
- p = frag_more (strlen (name) + 1);
- strcpy (p, name);
- break;
- case 2:
- p = frag_more (1);
- *p = 6 + strlen (name) + 1;
- p = frag_more (1);
- *p = EXT_AUX_REGISTER;
- p = frag_more (1);
- *p = number >> 24 & 0xff;
- p = frag_more (1);
- *p = number >> 16 & 0xff;
- p = frag_more (1);
- *p = number >> 8 & 0xff;
- p = frag_more (1);
- *p = number & 0xff;
- p = frag_more (strlen (name) + 1);
- strcpy (p, name);
- break;
- default:
- as_bad ("invalid opertype");
- ignore_rest_of_line ();
- free (name);
- return;
- break;
+ if (insn->has_limm)
+ {
+ if (!relax)
+ f = frag_more (8);
+ md_number_to_chars_midend (f, insn->insn, 4);
+ md_number_to_chars_midend (f + 4, insn->limm, 4);
+ dwarf2_emit_insn (8);
+ }
+ else
+ {
+ if (!relax)
+ f = frag_more (4);
+ md_number_to_chars_midend (f, insn->insn, 4);
+ dwarf2_emit_insn (4);
+ }
}
- subseg_set (old_sec, old_subsec);
-
- /* Enter all registers into the symbol table. */
-
- demand_empty_rest_of_line ();
+ if (!relax)
+ apply_fixups (insn, frag_now, (f - frag_now->fr_literal));
}
static void
-arc_extinst (ignore)
- int ignore ATTRIBUTE_UNUSED;
+emit_insn1 (struct arc_insn *insn)
{
- unsigned char syntax[129];
- char *name;
- char *p;
- char c;
- int suffixcode = -1;
- int opcode, subopcode;
- int i;
- int class = 0;
- int name_len;
- struct arc_opcode *ext_op;
+ /* How frag_var's args are currently configured:
+ - rs_machine_dependent, to dictate it's a relaxation frag.
+ - FRAG_MAX_GROWTH, maximum size of instruction
+ - 0, variable size that might grow...unused by generic relaxation.
+ - frag_now->fr_subtype, fr_subtype starting value, set previously.
+ - s, opand expression.
+ - 0, offset but it's unused.
+ - 0, opcode but it's unused. */
+ symbolS *s = make_expr_symbol (&insn->fixups[0].exp);
+ frag_now->tc_frag_data.pcrel = insn->fixups[0].pcrel;
+
+ if (frag_room () < FRAG_MAX_GROWTH)
+ {
+ /* Handle differently when frag literal memory is exhausted.
+ This is used because when there's not enough memory left in
+ the current frag, a new frag is created and the information
+ we put into frag_now->tc_frag_data is disregarded. */
- segT old_sec;
- int old_subsec;
+ struct arc_relax_type relax_info_copy;
+ relax_substateT subtype = frag_now->fr_subtype;
- name = input_line_pointer;
- c = get_symbol_end ();
- name = xstrdup (name);
- strcpy (syntax, name);
- name_len = strlen (name);
+ memcpy (&relax_info_copy, &frag_now->tc_frag_data,
+ sizeof (struct arc_relax_type));
- /* just after name is now '\0' */
- p = input_line_pointer;
- *p = c;
+ frag_wane (frag_now);
+ frag_grow (FRAG_MAX_GROWTH);
- SKIP_WHITESPACE ();
+ memcpy (&frag_now->tc_frag_data, &relax_info_copy,
+ sizeof (struct arc_relax_type));
- if (*input_line_pointer != ',')
- {
- as_bad ("expected comma after operand name");
- ignore_rest_of_line ();
- return;
+ frag_var (rs_machine_dependent, FRAG_MAX_GROWTH, 0,
+ subtype, s, 0, 0);
}
+ else
+ frag_var (rs_machine_dependent, FRAG_MAX_GROWTH, 0,
+ frag_now->fr_subtype, s, 0, 0);
+}
- input_line_pointer++; /* skip ',' */
- opcode = get_absolute_expression ();
-
- SKIP_WHITESPACE ();
-
- if (*input_line_pointer != ',')
- {
- as_bad ("expected comma after opcode");
- ignore_rest_of_line ();
- return;
- }
+static void
+emit_insn (struct arc_insn *insn)
+{
+ if (insn->relax)
+ emit_insn1 (insn);
+ else
+ emit_insn0 (insn, NULL, FALSE);
+}
- input_line_pointer++; /* skip ',' */
- subopcode = get_absolute_expression ();
+/* Check whether a symbol involves a register. */
- if (subopcode < 0)
+static bfd_boolean
+contains_register (symbolS *sym)
+{
+ if (sym)
{
- as_bad ("negative subopcode %d", subopcode);
- ignore_rest_of_line ();
- return;
- }
+ expressionS *ex = symbol_get_value_expression (sym);
- if (subopcode)
- {
- if (3 != opcode)
- {
- as_bad ("subcode value found when opcode not equal 0x03");
- ignore_rest_of_line ();
- return;
- }
- else
- {
- if (subopcode < 0x09 || subopcode == 0x3f)
- {
- as_bad ("invalid subopcode %d", subopcode);
- ignore_rest_of_line ();
- return;
- }
- }
+ return ((O_register == ex->X_op)
+ && !contains_register (ex->X_add_symbol)
+ && !contains_register (ex->X_op_symbol));
}
- SKIP_WHITESPACE ();
+ return FALSE;
+}
- if (*input_line_pointer != ',')
- {
- as_bad ("expected comma after subopcode");
- ignore_rest_of_line ();
- return;
- }
+/* Returns the register number within a symbol. */
- input_line_pointer++; /* skip ',' */
+static int
+get_register (symbolS *sym)
+{
+ if (!contains_register (sym))
+ return -1;
- for (i = 0; i < (int) MAXSUFFIXCLASS; i++)
- {
- if (!strncmp (suffixclass[i].name,input_line_pointer, suffixclass[i].len))
- {
- suffixcode = i;
- input_line_pointer += suffixclass[i].len;
- break;
- }
- }
+ expressionS *ex = symbol_get_value_expression (sym);
+ return regno (ex->X_add_number);
+}
- if (-1 == suffixcode)
- {
- as_bad ("invalid suffix class");
- ignore_rest_of_line ();
- return;
- }
+/* Return true if a RELOC is generic. A generic reloc is PC-rel of a
+ simple ME relocation (e.g. RELOC_ARC_32_ME, BFD_RELOC_ARC_PC32. */
- SKIP_WHITESPACE ();
+static bfd_boolean
+generic_reloc_p (extended_bfd_reloc_code_real_type reloc)
+{
+ if (!reloc)
+ return FALSE;
- if (*input_line_pointer != ',')
+ switch (reloc)
{
- as_bad ("expected comma after suffix class");
- ignore_rest_of_line ();
- return;
+ case BFD_RELOC_ARC_SDA_LDST:
+ case BFD_RELOC_ARC_SDA_LDST1:
+ case BFD_RELOC_ARC_SDA_LDST2:
+ case BFD_RELOC_ARC_SDA16_LD:
+ case BFD_RELOC_ARC_SDA16_LD1:
+ case BFD_RELOC_ARC_SDA16_LD2:
+ case BFD_RELOC_ARC_SDA16_ST2:
+ case BFD_RELOC_ARC_SDA32_ME:
+ return FALSE;
+ default:
+ return TRUE;
}
+}
- input_line_pointer++; /* skip ',' */
-
- for (i = 0; i < (int) MAXSYNTAXCLASS; i++)
- {
- if (!strncmp (syntaxclass[i].name,input_line_pointer, syntaxclass[i].len))
- {
- class = syntaxclass[i].class;
- input_line_pointer += syntaxclass[i].len;
- break;
- }
- }
+/* Allocates a tok entry. */
- if (0 == (SYNTAX_VALID & class))
- {
- as_bad ("invalid syntax class");
- ignore_rest_of_line ();
- return;
- }
+static int
+allocate_tok (expressionS *tok, int ntok, int cidx)
+{
+ if (ntok > MAX_INSN_ARGS - 2)
+ return 0; /* No space left. */
- if ((0x3 == opcode) & (class & SYNTAX_3OP))
- {
- as_bad ("opcode 0x3 and SYNTAX_3OP invalid");
- ignore_rest_of_line ();
- return;
- }
+ if (cidx > ntok)
+ return 0; /* Incorect args. */
- switch (suffixcode)
- {
- case 0:
- strcat (syntax, "%.q%.f ");
- break;
- case 1:
- strcat (syntax, "%.f ");
- break;
- case 2:
- strcat (syntax, "%.q ");
- break;
- case 3:
- strcat (syntax, " ");
- break;
- default:
- as_bad ("unknown suffix class");
- ignore_rest_of_line ();
- return;
- break;
- };
-
- strcat (syntax, ((opcode == 0x3) ? "%a,%b" : ((class & SYNTAX_3OP) ? "%a,%b,%c" : "%b,%c")));
- if (suffixcode < 2)
- strcat (syntax, "%F");
- strcat (syntax, "%S%L");
-
- ext_op = (struct arc_opcode *) xmalloc (sizeof (struct arc_opcode));
- ext_op->syntax = xstrdup (syntax);
-
- ext_op->mask = I (-1) | ((0x3 == opcode) ? C (-1) : 0);
- ext_op->value = I (opcode) | ((0x3 == opcode) ? C (subopcode) : 0);
- ext_op->flags = class;
- ext_op->next_asm = arc_ext_opcodes;
- ext_op->next_dis = arc_ext_opcodes;
- arc_ext_opcodes = ext_op;
-
- /* OK, now that we know what this inst is, put a description in the
- arc extension section of the output file. */
-
- old_sec = now_seg;
- old_subsec = now_subseg;
-
- arc_set_ext_seg ();
-
- p = frag_more (1);
- *p = 5 + name_len + 1;
- p = frag_more (1);
- *p = EXT_INSTRUCTION;
- p = frag_more (1);
- *p = opcode;
- p = frag_more (1);
- *p = subopcode;
- p = frag_more (1);
- *p = (class & (OP1_MUST_BE_IMM | OP1_IMM_IMPLIED) ? IGNORE_FIRST_OPD : 0);
- p = frag_more (name_len);
- strncpy (p, syntax, name_len);
- p = frag_more (1);
- *p = '\0';
-
- subseg_set (old_sec, old_subsec);
+ memcpy (&tok[ntok+1], &tok[ntok], sizeof (*tok));
- demand_empty_rest_of_line ();
+ if (cidx == ntok)
+ return 1; /* Success. */
+ return allocate_tok (tok, ntok - 1, cidx);
}
-int
-arc_set_ext_seg ()
-{
- if (!arcext_section)
- {
- arcext_section = subseg_new (".arcextmap", 0);
- bfd_set_section_flags (stdoutput, arcext_section,
- SEC_READONLY | SEC_HAS_CONTENTS);
- }
- else
- subseg_set (arcext_section, 0);
- return 1;
-}
+/* Search forward through all variants of an opcode looking for a
+ syntax match. */
-static void
-arc_common (localScope)
- int localScope;
+static const struct arc_opcode *
+find_opcode_match (const struct arc_opcode *first_opcode,
+ expressionS *tok,
+ int *pntok,
+ struct arc_flags *first_pflag,
+ int nflgs,
+ int *pcpumatch)
{
- char *name;
- char c;
- char *p;
- int align, size;
- symbolS *symbolP;
-
- name = input_line_pointer;
- c = get_symbol_end ();
- /* just after name is now '\0' */
- p = input_line_pointer;
- *p = c;
- SKIP_WHITESPACE ();
-
- if (*input_line_pointer != ',')
+ const struct arc_opcode *opcode = first_opcode;
+ int ntok = *pntok;
+ int got_cpu_match = 0;
+ expressionS bktok[MAX_INSN_ARGS];
+ int bkntok;
+ expressionS emptyE;
+
+ memset (&emptyE, 0, sizeof (emptyE));
+ memcpy (bktok, tok, MAX_INSN_ARGS * sizeof (*tok));
+ bkntok = ntok;
+
+ do
{
- as_bad ("expected comma after symbol name");
- ignore_rest_of_line ();
- return;
- }
+ const unsigned char *opidx;
+ const unsigned char *flgidx;
+ int tokidx = 0;
+ const expressionS *t = &emptyE;
- input_line_pointer++; /* skip ',' */
- size = get_absolute_expression ();
+ pr_debug ("%s:%d: find_opcode_match: trying opcode 0x%08X ",
+ frag_now->fr_file, frag_now->fr_line, opcode->opcode);
- if (size < 0)
- {
- as_bad ("negative symbol length");
- ignore_rest_of_line ();
- return;
- }
+ /* Don't match opcodes that don't exist on this
+ architecture. */
+ if (!(opcode->cpu & arc_target))
+ goto match_failed;
- *p = 0;
- symbolP = symbol_find_or_make (name);
- *p = c;
+ if (is_code_density_p (opcode) && !(arc_features & ARC_CD))
+ goto match_failed;
- if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
- {
- as_bad ("ignoring attempt to re-define symbol");
- ignore_rest_of_line ();
- return;
- }
- if (((int) S_GET_VALUE (symbolP) != 0) \
- && ((int) S_GET_VALUE (symbolP) != size))
- {
- as_warn ("length of symbol \"%s\" already %ld, ignoring %d",
- S_GET_NAME (symbolP), (long) S_GET_VALUE (symbolP), size);
- }
- assert (symbolP->sy_frag == &zero_address_frag);
+ got_cpu_match = 1;
+ pr_debug ("cpu ");
- /* Now parse the alignment field. This field is optional for
- local and global symbols. Default alignment is zero. */
- if (*input_line_pointer == ',')
- {
- input_line_pointer++;
- align = get_absolute_expression ();
- if (align < 0)
+ /* Check the operands. */
+ for (opidx = opcode->operands; *opidx; ++opidx)
{
- align = 0;
- as_warn ("assuming symbol alignment of zero");
- }
- }
- else
- align = 0;
-
- if (localScope != 0)
- {
- segT old_sec;
- int old_subsec;
- char *pfrag;
-
- old_sec = now_seg;
- old_subsec = now_subseg;
- record_alignment (bss_section, align);
- subseg_set (bss_section, 0); /* ??? subseg_set (bss_section, 1); ??? */
-
- if (align)
- /* Do alignment. */
- frag_align (align, 0, 0);
-
- /* Detach from old frag. */
- if (S_GET_SEGMENT (symbolP) == bss_section)
- symbolP->sy_frag->fr_symbol = NULL;
-
- symbolP->sy_frag = frag_now;
- pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
- (offsetT) size, (char *) 0);
- *pfrag = 0;
-
- S_SET_SIZE (symbolP, size);
- S_SET_SEGMENT (symbolP, bss_section);
- S_CLEAR_EXTERNAL (symbolP);
- symbolP->local = 1;
- subseg_set (old_sec, old_subsec);
- }
- else
- {
- S_SET_VALUE (symbolP, (valueT) size);
- S_SET_ALIGN (symbolP, align);
- S_SET_EXTERNAL (symbolP);
- S_SET_SEGMENT (symbolP, bfd_com_section_ptr);
- }
+ const struct arc_operand *operand = &arc_operands[*opidx];
- symbolP->bsym->flags |= BSF_OBJECT;
+ /* Only take input from real operands. */
+ if ((operand->flags & ARC_OPERAND_FAKE)
+ && !(operand->flags & ARC_OPERAND_BRAKET))
+ continue;
- demand_empty_rest_of_line ();
-}
-\f
-/* Select the cpu we're assembling for. */
+ /* When we expect input, make sure we have it. */
+ if (tokidx >= ntok)
+ goto match_failed;
-static void
-arc_option (ignore)
- int ignore ATTRIBUTE_UNUSED;
-{
- int mach;
- char c;
- char *cpu;
+ /* Match operand type with expression type. */
+ switch (operand->flags & ARC_OPERAND_TYPECHECK_MASK)
+ {
+ case ARC_OPERAND_IR:
+ /* Check to be a register. */
+ if ((tok[tokidx].X_op != O_register
+ || !is_ir_num (tok[tokidx].X_add_number))
+ && !(operand->flags & ARC_OPERAND_IGNORE))
+ goto match_failed;
+
+ /* If expect duplicate, make sure it is duplicate. */
+ if (operand->flags & ARC_OPERAND_DUPLICATE)
+ {
+ /* Check for duplicate. */
+ if (t->X_op != O_register
+ || !is_ir_num (t->X_add_number)
+ || (regno (t->X_add_number) !=
+ regno (tok[tokidx].X_add_number)))
+ goto match_failed;
+ }
- cpu = input_line_pointer;
- c = get_symbol_end ();
- mach = arc_get_mach (cpu);
- *input_line_pointer = c;
+ /* Special handling? */
+ if (operand->insert)
+ {
+ const char *errmsg = NULL;
+ (*operand->insert)(0,
+ regno (tok[tokidx].X_add_number),
+ &errmsg);
+ if (errmsg)
+ {
+ if (operand->flags & ARC_OPERAND_IGNORE)
+ {
+ /* Missing argument, create one. */
+ if (!allocate_tok (tok, ntok - 1, tokidx))
+ goto match_failed;
- /* If an instruction has already been seen, it's too late. */
- if (cpu_tables_init_p)
- {
- as_bad ("\".option\" directive must appear before any instructions");
- ignore_rest_of_line ();
- return;
- }
+ tok[tokidx].X_op = O_absent;
+ ++ntok;
+ }
+ else
+ goto match_failed;
+ }
+ }
- if (mach == -1)
- goto bad_cpu;
+ t = &tok[tokidx];
+ break;
- if (mach_type_specified_p && mach != arc_mach_type)
- {
- as_bad ("\".option\" directive conflicts with initial definition");
- ignore_rest_of_line ();
- return;
- }
- else
- {
- /* The cpu may have been selected on the command line. */
- if (mach != arc_mach_type)
- as_warn ("\".option\" directive overrides command-line (default) value");
- arc_mach_type = mach;
- if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach))
- as_fatal ("could not set architecture and machine");
- mach_type_specified_p = 1;
+ case ARC_OPERAND_BRAKET:
+ /* Check if bracket is also in opcode table as
+ operand. */
+ if (tok[tokidx].X_op != O_bracket)
+ goto match_failed;
+ break;
+
+ case ARC_OPERAND_LIMM:
+ case ARC_OPERAND_SIGNED:
+ case ARC_OPERAND_UNSIGNED:
+ switch (tok[tokidx].X_op)
+ {
+ case O_illegal:
+ case O_absent:
+ case O_register:
+ goto match_failed;
+
+ case O_bracket:
+ /* Got an (too) early bracket, check if it is an
+ ignored operand. N.B. This procedure works only
+ when bracket is the last operand! */
+ if (!(operand->flags & ARC_OPERAND_IGNORE))
+ goto match_failed;
+ /* Insert the missing operand. */
+ if (!allocate_tok (tok, ntok - 1, tokidx))
+ goto match_failed;
+
+ tok[tokidx].X_op = O_absent;
+ ++ntok;
+ break;
+
+ case O_constant:
+ /* Check the range. */
+ if (operand->bits != 32
+ && !(operand->flags & ARC_OPERAND_NCHK))
+ {
+ offsetT min, max, val;
+ val = tok[tokidx].X_add_number;
+
+ if (operand->flags & ARC_OPERAND_SIGNED)
+ {
+ max = (1 << (operand->bits - 1)) - 1;
+ min = -(1 << (operand->bits - 1));
+ }
+ else
+ {
+ max = (1 << operand->bits) - 1;
+ min = 0;
+ }
+
+ if (val < min || val > max)
+ goto match_failed;
+
+ /* Check alignmets. */
+ if ((operand->flags & ARC_OPERAND_ALIGNED32)
+ && (val & 0x03))
+ goto match_failed;
+
+ if ((operand->flags & ARC_OPERAND_ALIGNED16)
+ && (val & 0x01))
+ goto match_failed;
+ }
+ else if (operand->flags & ARC_OPERAND_NCHK)
+ {
+ if (operand->insert)
+ {
+ const char *errmsg = NULL;
+ (*operand->insert)(0,
+ tok[tokidx].X_add_number,
+ &errmsg);
+ if (errmsg)
+ goto match_failed;
+ }
+ else
+ goto match_failed;
+ }
+ break;
+
+ case O_subtract:
+ /* Check if it is register range. */
+ if ((tok[tokidx].X_add_number == 0)
+ && contains_register (tok[tokidx].X_add_symbol)
+ && contains_register (tok[tokidx].X_op_symbol))
+ {
+ int regs;
+
+ regs = get_register (tok[tokidx].X_add_symbol);
+ regs <<= 16;
+ regs |= get_register (tok[tokidx].X_op_symbol);
+ if (operand->insert)
+ {
+ const char *errmsg = NULL;
+ (*operand->insert)(0,
+ regs,
+ &errmsg);
+ if (errmsg)
+ goto match_failed;
+ }
+ else
+ goto match_failed;
+ break;
+ }
+ default:
+ if (operand->default_reloc == 0)
+ goto match_failed; /* The operand needs relocation. */
+
+ /* Relocs requiring long immediate. FIXME! make it
+ generic and move it to a function. */
+ switch (tok[tokidx].X_md)
+ {
+ case O_gotoff:
+ case O_gotpc:
+ case O_pcl:
+ case O_tpoff:
+ case O_dtpoff:
+ case O_tlsgd:
+ case O_tlsie:
+ if (!(operand->flags & ARC_OPERAND_LIMM))
+ goto match_failed;
+ case O_absent:
+ if (!generic_reloc_p (operand->default_reloc))
+ goto match_failed;
+ default:
+ break;
+ }
+ break;
+ }
+ /* If expect duplicate, make sure it is duplicate. */
+ if (operand->flags & ARC_OPERAND_DUPLICATE)
+ {
+ if (t->X_op == O_illegal
+ || t->X_op == O_absent
+ || t->X_op == O_register
+ || (t->X_add_number != tok[tokidx].X_add_number))
+ goto match_failed;
+ }
+ t = &tok[tokidx];
+ break;
+
+ default:
+ /* Everything else should have been fake. */
+ abort ();
+ }
+
+ ++tokidx;
+ }
+ pr_debug ("opr ");
+
+ /* Check the flags. Iterate over the valid flag classes. */
+ int lnflg = nflgs;
+
+ for (flgidx = opcode->flags; *flgidx && lnflg; ++flgidx)
+ {
+ /* Get a valid flag class. */
+ const struct arc_flag_class *cl_flags = &arc_flag_classes[*flgidx];
+ const unsigned *flgopridx;
+
+ for (flgopridx = cl_flags->flags; *flgopridx; ++flgopridx)
+ {
+ const struct arc_flag_operand *flg_operand;
+ struct arc_flags *pflag = first_pflag;
+ int i;
+
+ flg_operand = &arc_flag_operands[*flgopridx];
+ for (i = 0; i < nflgs; i++, pflag++)
+ {
+ /* Match against the parsed flags. */
+ if (!strcmp (flg_operand->name, pflag->name))
+ {
+ /*TODO: Check if it is duplicated. */
+ pflag->code = *flgopridx;
+ lnflg--;
+ break; /* goto next flag class and parsed flag. */
+ }
+ }
+ }
+ }
+ /* Did I check all the parsed flags? */
+ if (lnflg)
+ goto match_failed;
+
+ pr_debug ("flg");
+ /* Possible match -- did we use all of our input? */
+ if (tokidx == ntok)
+ {
+ *pntok = ntok;
+ pr_debug ("\n");
+ return opcode;
+ }
+
+ match_failed:;
+ pr_debug ("\n");
+ /* Restore the original parameters. */
+ memcpy (tok, bktok, MAX_INSN_ARGS * sizeof (*tok));
+ ntok = bkntok;
}
- demand_empty_rest_of_line ();
- return;
+ while (++opcode - arc_opcodes < (int) arc_num_opcodes
+ && !strcmp (opcode->name, first_opcode->name));
- bad_cpu:
- as_bad ("invalid identifier for \".option\"");
- ignore_rest_of_line ();
+ if (*pcpumatch)
+ *pcpumatch = got_cpu_match;
+
+ return NULL;
}
-\f
-/* Turn a string in input_line_pointer into a floating point constant
- of type TYPE, and store the appropriate bytes in *LITP. The number
- of LITTLENUMS emitted is stored in *SIZEP. An error message is
- returned, or NULL on OK. */
-/* Equal to MAX_PRECISION in atof-ieee.c */
-#define MAX_LITTLENUMS 6
+/* Swap operand tokens. */
-char *
-md_atof (type, litP, sizeP)
- int type;
- char *litP;
- int *sizeP;
+static void
+swap_operand (expressionS *operand_array,
+ unsigned source,
+ unsigned destination)
+{
+ expressionS cpy_operand;
+ expressionS *src_operand;
+ expressionS *dst_operand;
+ size_t size;
+
+ if (source == destination)
+ return;
+
+ src_operand = &operand_array[source];
+ dst_operand = &operand_array[destination];
+ size = sizeof (expressionS);
+
+ /* Make copy of operand to swap with and swap. */
+ memcpy (&cpy_operand, dst_operand, size);
+ memcpy (dst_operand, src_operand, size);
+ memcpy (src_operand, &cpy_operand, size);
+}
+
+/* Check if *op matches *tok type.
+ Returns FALSE if they don't match, TRUE if they match. */
+
+static bfd_boolean
+pseudo_operand_match (const expressionS *tok,
+ const struct arc_operand_operation *op)
{
- int prec;
- LITTLENUM_TYPE words[MAX_LITTLENUMS];
- LITTLENUM_TYPE *wordP;
- char *t;
+ offsetT min, max, val;
+ bfd_boolean ret;
+ const struct arc_operand *operand_real = &arc_operands[op->operand_idx];
- switch (type)
+ ret = FALSE;
+ switch (tok->X_op)
{
- case 'f':
- case 'F':
- prec = 2;
+ case O_constant:
+ if (operand_real->bits == 32 && (operand_real->flags & ARC_OPERAND_LIMM))
+ ret = 1;
+ else if (!(operand_real->flags & ARC_OPERAND_IR))
+ {
+ val = tok->X_add_number + op->count;
+ if (operand_real->flags & ARC_OPERAND_SIGNED)
+ {
+ max = (1 << (operand_real->bits - 1)) - 1;
+ min = -(1 << (operand_real->bits - 1));
+ }
+ else
+ {
+ max = (1 << operand_real->bits) - 1;
+ min = 0;
+ }
+ if (min <= val && val <= max)
+ ret = TRUE;
+ }
+ break;
+
+ case O_symbol:
+ /* Handle all symbols as long immediates or signed 9. */
+ if (operand_real->flags & ARC_OPERAND_LIMM ||
+ ((operand_real->flags & ARC_OPERAND_SIGNED) && operand_real->bits == 9))
+ ret = TRUE;
+ break;
+
+ case O_register:
+ if (operand_real->flags & ARC_OPERAND_IR)
+ ret = TRUE;
break;
- case 'd':
- case 'D':
- prec = 4;
+ case O_bracket:
+ if (operand_real->flags & ARC_OPERAND_BRAKET)
+ ret = TRUE;
break;
default:
- *sizeP = 0;
- return "bad call to md_atof";
+ /* Unknown. */
+ break;
}
+ return ret;
+}
+
+/* Find pseudo instruction in array. */
- t = atof_ieee (input_line_pointer, type, words);
- if (t)
- input_line_pointer = t;
- *sizeP = prec * sizeof (LITTLENUM_TYPE);
- for (wordP = words; prec--;)
+static const struct arc_pseudo_insn *
+find_pseudo_insn (const char *opname,
+ int ntok,
+ const expressionS *tok)
+{
+ const struct arc_pseudo_insn *pseudo_insn = NULL;
+ const struct arc_operand_operation *op;
+ unsigned int i;
+ int j;
+
+ for (i = 0; i < arc_num_pseudo_insn; ++i)
{
- md_number_to_chars (litP, (valueT) (*wordP++), sizeof (LITTLENUM_TYPE));
- litP += sizeof (LITTLENUM_TYPE);
+ pseudo_insn = &arc_pseudo_insns[i];
+ if (strcmp (pseudo_insn->mnemonic_p, opname) == 0)
+ {
+ op = pseudo_insn->operand;
+ for (j = 0; j < ntok; ++j)
+ if (!pseudo_operand_match (&tok[j], &op[j]))
+ break;
+
+ /* Found the right instruction. */
+ if (j == ntok)
+ return pseudo_insn;
+ }
}
-
return NULL;
}
-/* Write a value out to the object file, using the appropriate
- endianness. */
+/* Assumes the expressionS *tok is of sufficient size. */
-void
-md_number_to_chars (buf, val, n)
- char *buf;
- valueT val;
- int n;
+static const struct arc_opcode *
+find_special_case_pseudo (const char *opname,
+ int *ntok,
+ expressionS *tok,
+ int *nflgs,
+ struct arc_flags *pflags)
{
- if (target_big_endian)
- number_to_chars_bigendian (buf, val, n);
- else
- number_to_chars_littleendian (buf, val, n);
-}
+ const struct arc_pseudo_insn *pseudo_insn = NULL;
+ const struct arc_operand_operation *operand_pseudo;
+ const struct arc_operand *operand_real;
+ unsigned i;
+ char construct_operand[MAX_CONSTR_STR];
-/* Round up a section size to the appropriate boundary. */
+ /* Find whether opname is in pseudo instruction array. */
+ pseudo_insn = find_pseudo_insn (opname, *ntok, tok);
-valueT
-md_section_align (segment, size)
- segT segment;
- valueT size;
-{
- int align = bfd_get_section_alignment (stdoutput, segment);
+ if (pseudo_insn == NULL)
+ return NULL;
- return ((size + (1 << align) - 1) & (-1 << align));
-}
+ /* Handle flag, Limited to one flag at the moment. */
+ if (pseudo_insn->flag_r != NULL)
+ *nflgs += tokenize_flags (pseudo_insn->flag_r, &pflags[*nflgs],
+ MAX_INSN_FLGS - *nflgs);
-/* We don't have any form of relaxing. */
+ /* Handle operand operations. */
+ for (i = 0; i < pseudo_insn->operand_cnt; ++i)
+ {
+ operand_pseudo = &pseudo_insn->operand[i];
+ operand_real = &arc_operands[operand_pseudo->operand_idx];
-int
-md_estimate_size_before_relax (fragp, seg)
- fragS *fragp ATTRIBUTE_UNUSED;
- asection *seg ATTRIBUTE_UNUSED;
-{
- as_fatal (_("md_estimate_size_before_relax\n"));
- return 1;
-}
+ if (operand_real->flags & ARC_OPERAND_BRAKET &&
+ !operand_pseudo->needs_insert)
+ continue;
-/* Convert a machine dependent frag. We never generate these. */
+ /* Has to be inserted (i.e. this token does not exist yet). */
+ if (operand_pseudo->needs_insert)
+ {
+ if (operand_real->flags & ARC_OPERAND_BRAKET)
+ {
+ tok[i].X_op = O_bracket;
+ ++(*ntok);
+ continue;
+ }
-void
-md_convert_frag (abfd, sec, fragp)
- bfd *abfd ATTRIBUTE_UNUSED;
- asection *sec ATTRIBUTE_UNUSED;
- fragS *fragp ATTRIBUTE_UNUSED;
-{
- as_fatal (_("md_convert_frag\n"));
-}
+ /* Check if operand is a register or constant and handle it
+ by type. */
+ if (operand_real->flags & ARC_OPERAND_IR)
+ snprintf (construct_operand, MAX_CONSTR_STR, "r%d",
+ operand_pseudo->count);
+ else
+ snprintf (construct_operand, MAX_CONSTR_STR, "%d",
+ operand_pseudo->count);
-void
-arc_code_symbol (expressionP)
- expressionS *expressionP;
-{
- if (expressionP->X_op == O_symbol && expressionP->X_add_number == 0)
- {
- expressionS two;
- expressionP->X_op = O_right_shift;
- expressionP->X_add_symbol->sy_value.X_op = O_constant;
- two.X_op = O_constant;
- two.X_add_symbol = two.X_op_symbol = NULL;
- two.X_add_number = 2;
- expressionP->X_op_symbol = make_expr_symbol (&two);
- }
- /* Allow %st(sym1-sym2) */
- else if (expressionP->X_op == O_subtract
- && expressionP->X_add_symbol != NULL
- && expressionP->X_op_symbol != NULL
- && expressionP->X_add_number == 0)
- {
- expressionS two;
- expressionP->X_add_symbol = make_expr_symbol (expressionP);
- expressionP->X_op = O_right_shift;
- two.X_op = O_constant;
- two.X_add_symbol = two.X_op_symbol = NULL;
- two.X_add_number = 2;
- expressionP->X_op_symbol = make_expr_symbol (&two);
+ tokenize_arguments (construct_operand, &tok[i], 1);
+ ++(*ntok);
+ }
+
+ else if (operand_pseudo->count)
+ {
+ /* Operand number has to be adjusted accordingly (by operand
+ type). */
+ switch (tok[i].X_op)
+ {
+ case O_constant:
+ tok[i].X_add_number += operand_pseudo->count;
+ break;
+
+ case O_symbol:
+ break;
+
+ default:
+ /* Ignored. */
+ break;
+ }
+ }
}
- else
+
+ /* Swap operands if necessary. Only supports one swap at the
+ moment. */
+ for (i = 0; i < pseudo_insn->operand_cnt; ++i)
{
- as_bad ("expression too complex code symbol");
- return;
+ operand_pseudo = &pseudo_insn->operand[i];
+
+ if (operand_pseudo->swap_operand_idx == i)
+ continue;
+
+ swap_operand (tok, i, operand_pseudo->swap_operand_idx);
+
+ /* Prevent a swap back later by breaking out. */
+ break;
}
+
+ return (const struct arc_opcode *)
+ hash_find (arc_opcode_hash, pseudo_insn->mnemonic_r);
}
-/* Parse an operand that is machine-specific.
+static const struct arc_opcode *
+find_special_case_flag (const char *opname,
+ int *nflgs,
+ struct arc_flags *pflags)
+{
+ unsigned int i;
+ const char *flagnm;
+ unsigned flag_idx, flag_arr_idx;
+ size_t flaglen, oplen;
+ const struct arc_flag_special *arc_flag_special_opcode;
+ const struct arc_opcode *opcode;
- The ARC has a special %-op to adjust addresses so they're usable in
- branches. The "st" is short for the STatus register.
- ??? Later expand this to take a flags value too.
+ /* Search for special case instruction. */
+ for (i = 0; i < arc_num_flag_special; i++)
+ {
+ arc_flag_special_opcode = &arc_flag_special_cases[i];
+ oplen = strlen (arc_flag_special_opcode->name);
- ??? We can't create new expression types so we map the %-op's onto the
- existing syntax. This means that the user could use the chosen syntax
- to achieve the same effect. */
+ if (strncmp (opname, arc_flag_special_opcode->name, oplen) != 0)
+ continue;
-void
-md_operand (expressionP)
- expressionS *expressionP;
-{
- char *p = input_line_pointer;
+ /* Found a potential special case instruction, now test for
+ flags. */
+ for (flag_arr_idx = 0;; ++flag_arr_idx)
+ {
+ flag_idx = arc_flag_special_opcode->flags[flag_arr_idx];
+ if (flag_idx == 0)
+ break; /* End of array, nothing found. */
- if (*p == '%')
- if (strncmp (p, "%st(", 4) == 0)
- {
- input_line_pointer += 4;
- expression (expressionP);
- if (*input_line_pointer != ')')
- {
- as_bad ("missing ')' in %%-op");
- return;
- }
- ++input_line_pointer;
- arc_code_symbol (expressionP);
- }
- else
- {
- /* It could be a register. */
- int i, l;
- struct arc_ext_operand_value *ext_oper = arc_ext_operands;
- p++;
+ flagnm = arc_flag_operands[flag_idx].name;
+ flaglen = strlen (flagnm);
+ if (strcmp (opname + oplen, flagnm) == 0)
+ {
+ opcode = (const struct arc_opcode *)
+ hash_find (arc_opcode_hash,
+ arc_flag_special_opcode->name);
- while (ext_oper)
- {
- l = strlen (ext_oper->operand.name);
- if (!strncmp (p, ext_oper->operand.name, l) && !ISALNUM (*(p + l)))
- {
- input_line_pointer += l + 1;
- expressionP->X_op = O_register;
- expressionP->X_add_number = (int) &ext_oper->operand;
- return;
- }
- ext_oper = ext_oper->next;
- }
- for (i = 0; i < arc_reg_names_count; i++)
- {
- l = strlen (arc_reg_names[i].name);
- if (!strncmp (p, arc_reg_names[i].name, l) && !ISALNUM (*(p + l)))
- {
- input_line_pointer += l + 1;
- expressionP->X_op = O_register;
- expressionP->X_add_number = (int) &arc_reg_names[i];
+ if (*nflgs + 1 > MAX_INSN_FLGS)
break;
- }
- }
- }
+ memcpy (pflags[*nflgs].name, flagnm, flaglen);
+ pflags[*nflgs].name[flaglen] = '\0';
+ (*nflgs)++;
+ return opcode;
+ }
+ }
+ }
+ return NULL;
}
-/* We have no need to default values of symbols.
- We could catch register names here, but that is handled by inserting
- them all in the symbol table to begin with. */
+/* Used to find special case opcode. */
-symbolS *
-md_undefined_symbol (name)
- char *name ATTRIBUTE_UNUSED;
+static const struct arc_opcode *
+find_special_case (const char *opname,
+ int *nflgs,
+ struct arc_flags *pflags,
+ expressionS *tok,
+ int *ntok)
{
- return 0;
-}
-\f
-/* Functions concerning expressions. */
+ const struct arc_opcode *opcode;
-/* Parse a .byte, .word, etc. expression.
+ opcode = find_special_case_pseudo (opname, ntok, tok, nflgs, pflags);
- Values for the status register are specified with %st(label).
- `label' will be right shifted by 2. */
+ if (opcode == NULL)
+ opcode = find_special_case_flag (opname, nflgs, pflags);
-void
-arc_parse_cons_expression (exp, nbytes)
- expressionS *exp;
- unsigned int nbytes ATTRIBUTE_UNUSED;
+ return opcode;
+}
+
+static void
+preprocess_operands (const struct arc_opcode *opcode,
+ expressionS *tok,
+ int ntok)
{
- char *p = input_line_pointer;
- int code_symbol_fix = 0;
+ int i;
+ size_t len;
+ const char *p;
+ unsigned j;
+ const struct arc_aux_reg *auxr;
- for (; ! is_end_of_line[(unsigned char) *p]; p++)
- if (*p == '@' && !strncmp (p, "@h30", 4))
- {
- code_symbol_fix = 1;
- strcpy (p, "; ");
- }
- expr (0, exp);
- if (code_symbol_fix)
+ for (i = 0; i < ntok; i++)
{
- arc_code_symbol (exp);
- input_line_pointer = p;
+ switch (tok[i].X_op)
+ {
+ case O_illegal:
+ case O_absent:
+ break; /* Throw and error. */
+
+ case O_symbol:
+ if (opcode->class != AUXREG)
+ break;
+ /* Convert the symbol to a constant if possible. */
+ p = S_GET_NAME (tok[i].X_add_symbol);
+ len = strlen (p);
+
+ auxr = &arc_aux_regs[0];
+ for (j = 0; j < arc_num_aux_regs; j++, auxr++)
+ if (len == auxr->length
+ && strcasecmp (auxr->name, p) == 0)
+ {
+ tok[i].X_op = O_constant;
+ tok[i].X_add_number = auxr->address;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
}
}
-/* Record a fixup for a cons expression. */
+/* Given an opcode name, pre-tockenized set of argumenst and the
+ opcode flags, take it all the way through emission. */
-void
-arc_cons_fix_new (frag, where, nbytes, exp)
- fragS *frag;
- int where;
- int nbytes;
- expressionS *exp;
+static void
+assemble_tokens (const char *opname,
+ expressionS *tok,
+ int ntok,
+ struct arc_flags *pflags,
+ int nflgs)
{
- if (nbytes == 4)
+ bfd_boolean found_something = FALSE;
+ const struct arc_opcode *opcode;
+ int cpumatch = 1;
+
+ /* Search opcodes. */
+ opcode = (const struct arc_opcode *) hash_find (arc_opcode_hash, opname);
+
+ /* Couldn't find opcode conventional way, try special cases. */
+ if (!opcode)
+ opcode = find_special_case (opname, &nflgs, pflags, tok, &ntok);
+
+ if (opcode)
{
- int reloc_type;
- expressionS exptmp;
+ pr_debug ("%s:%d: assemble_tokens: %s trying opcode 0x%08X\n",
+ frag_now->fr_file, frag_now->fr_line, opcode->name,
+ opcode->opcode);
+
+ preprocess_operands (opcode, tok, ntok);
- /* This may be a special ARC reloc (eg: %st()). */
- reloc_type = get_arc_exp_reloc_type (1, BFD_RELOC_32, exp, &exptmp);
- fix_new_exp (frag, where, nbytes, &exptmp, 0, reloc_type);
+ found_something = TRUE;
+ opcode = find_opcode_match (opcode, tok, &ntok, pflags, nflgs, &cpumatch);
+ if (opcode)
+ {
+ struct arc_insn insn;
+ assemble_insn (opcode, tok, ntok, pflags, nflgs, &insn);
+ emit_insn (&insn);
+ return;
+ }
}
- else
+
+ if (found_something)
{
- fix_new_exp (frag, where, nbytes, exp, 0,
- nbytes == 2 ? BFD_RELOC_16
- : nbytes == 8 ? BFD_RELOC_64
- : BFD_RELOC_32);
+ if (cpumatch)
+ as_bad (_("inappropriate arguments for opcode '%s'"), opname);
+ else
+ as_bad (_("opcode '%s' not supported for target %s"), opname,
+ arc_target_name);
}
+ else
+ as_bad (_("unknown opcode '%s'"), opname);
}
-\f
-/* Functions concerning relocs. */
-/* The location from which a PC relative jump should be calculated,
- given a PC relative reloc. */
+/* The public interface to the instruction assembler. */
-long
-md_pcrel_from (fixP)
- fixS *fixP;
+void
+md_assemble (char *str)
{
- /* Return the address of the delay slot. */
- return fixP->fx_frag->fr_address + fixP->fx_where + fixP->fx_size;
-}
+ char *opname;
+ expressionS tok[MAX_INSN_ARGS];
+ int ntok, nflg;
+ size_t opnamelen;
+ struct arc_flags flags[MAX_INSN_FLGS];
+
+ /* Split off the opcode. */
+ opnamelen = strspn (str, "abcdefghijklmnopqrstuvwxyz_0123468");
+ opname = xmalloc (opnamelen + 1);
+ memcpy (opname, str, opnamelen);
+ opname[opnamelen] = '\0';
+
+ /* Signalize we are assmbling the instructions. */
+ assembling_insn = TRUE;
+
+ /* Tokenize the flags. */
+ if ((nflg = tokenize_flags (str + opnamelen, flags, MAX_INSN_FLGS)) == -1)
+ {
+ as_bad (_("syntax error"));
+ return;
+ }
-/* Compute the reloc type of an expression.
- The possibly modified expression is stored in EXPNEW.
+ /* Scan up to the end of the mnemonic which must end in space or end
+ of string. */
+ str += opnamelen;
+ for (; *str != '\0'; str++)
+ if (*str == ' ')
+ break;
- This is used to convert the expressions generated by the %-op's into
- the appropriate operand type. It is called for both data in instructions
- (operands) and data outside instructions (variables, debugging info, etc.).
+ /* Tokenize the rest of the line. */
+ if ((ntok = tokenize_arguments (str, tok, MAX_INSN_ARGS)) < 0)
+ {
+ as_bad (_("syntax error"));
+ return;
+ }
- Currently supported %-ops:
+ /* Finish it off. */
+ assemble_tokens (opname, tok, ntok, flags, nflg);
+ assembling_insn = FALSE;
+}
- %st(symbol): represented as "symbol >> 2"
- "st" is short for STatus as in the status register (pc)
+/* Callback to insert a register into the hash table. */
- DEFAULT_TYPE is the type to use if no special processing is required.
+static void
+declare_register (char *name, int number)
+{
+ const char *err;
+ symbolS *regS = symbol_create (name, reg_section,
+ number, &zero_address_frag);
+
+ err = hash_insert (arc_reg_hash, S_GET_NAME (regS), (void *) regS);
+ if (err)
+ as_fatal ("Inserting \"%s\" into register table failed: %s",
+ name, err);
+}
- DATA_P is non-zero for data or limm values, zero for insn operands.
- Remember that the opcode "insertion fns" cannot be used on data, they're
- only for inserting operands into insns. They also can't be used for limm
- values as the insertion routines don't handle limm values. When called for
- insns we return fudged reloc types (real_value - BFD_RELOC_UNUSED). When
- called for data or limm values we use real reloc types. */
+/* Construct symbols for each of the general registers. */
-static int
-get_arc_exp_reloc_type (data_p, default_type, exp, expnew)
- int data_p;
- int default_type;
- expressionS *exp;
- expressionS *expnew;
-{
- /* If the expression is "symbol >> 2" we must change it to just "symbol",
- as fix_new_exp can't handle it. Similarly for (symbol - symbol) >> 2.
- That's ok though. What's really going on here is that we're using
- ">> 2" as a special syntax for specifying BFD_RELOC_ARC_B26. */
-
- if (exp->X_op == O_right_shift
- && exp->X_op_symbol != NULL
- && exp->X_op_symbol->sy_value.X_op == O_constant
- && exp->X_op_symbol->sy_value.X_add_number == 2
- && exp->X_add_number == 0)
+static void
+declare_register_set (void)
+{
+ int i;
+ for (i = 0; i < 64; ++i)
{
- if (exp->X_add_symbol != NULL
- && (exp->X_add_symbol->sy_value.X_op == O_constant
- || exp->X_add_symbol->sy_value.X_op == O_symbol))
- {
- *expnew = *exp;
- expnew->X_op = O_symbol;
- expnew->X_op_symbol = NULL;
- return data_p ? BFD_RELOC_ARC_B26 : arc_operand_map['J'];
- }
- else if (exp->X_add_symbol != NULL
- && exp->X_add_symbol->sy_value.X_op == O_subtract)
+ char name[7];
+
+ sprintf (name, "r%d", i);
+ declare_register (name, i);
+ if ((i & 0x01) == 0)
{
- *expnew = exp->X_add_symbol->sy_value;
- return data_p ? BFD_RELOC_ARC_B26 : arc_operand_map['J'];
+ sprintf (name, "r%dr%d", i, i+1);
+ declare_register (name, i);
}
}
-
- *expnew = *exp;
- return default_type;
}
-/* Apply a fixup to the object code. This is called for all the
- fixups we generated by the call to fix_new_exp, above. In the call
- above we used a reloc code which was the largest legal reloc code
- plus the operand index. Here we undo that to recover the operand
- index. At this point all symbol values should be fully resolved,
- and we attempt to completely resolve the reloc. If we can not do
- that, we determine the correct reloc code and put it back in the fixup. */
+/* Port-specific assembler initialization. This function is called
+ once, at assembler startup time. */
void
-md_apply_fix3 (fixP, valP, seg)
- fixS *fixP;
- valueT * valP;
- segT seg;
+md_begin (void)
{
-#if 0
- char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
-#endif
- valueT value = * valP;
+ unsigned int i;
+
+ /* The endianness can be chosen "at the factory". */
+ target_big_endian = byte_order == BIG_ENDIAN;
- if (fixP->fx_addsy == (symbolS *) NULL)
- fixP->fx_done = 1;
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, arc_mach_type))
+ as_warn (_("could not set architecture and machine"));
- else if (fixP->fx_pcrel)
- {
- /* Hack around bfd_install_relocation brain damage. */
- if (S_GET_SEGMENT (fixP->fx_addsy) != seg)
- value += md_pcrel_from (fixP);
- }
+ /* Set elf header flags. */
+ bfd_set_private_flags (stdoutput, arc_eflag);
- /* We can't actually support subtracting a symbol. */
- if (fixP->fx_subsy != NULL)
- as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+ /* Set up a hash table for the instructions. */
+ arc_opcode_hash = hash_new ();
+ if (arc_opcode_hash == NULL)
+ as_fatal (_("Virtual memory exhausted"));
- if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
+ /* Initialize the hash table with the insns. */
+ for (i = 0; i < arc_num_opcodes;)
{
- int opindex;
- const struct arc_operand *operand;
- char *where;
- arc_insn insn;
+ const char *name, *retval;
- opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
+ name = arc_opcodes[i].name;
+ retval = hash_insert (arc_opcode_hash, name, (void *) &arc_opcodes[i]);
+ if (retval)
+ as_fatal (_("internal error: can't hash opcode '%s': %s"),
+ name, retval);
- operand = &arc_operands[opindex];
+ while (++i < arc_num_opcodes
+ && (arc_opcodes[i].name == name
+ || !strcmp (arc_opcodes[i].name, name)))
+ continue;
+ }
- /* Fetch the instruction, insert the fully resolved operand
- value, and stuff the instruction back again. */
- where = fixP->fx_frag->fr_literal + fixP->fx_where;
- if (target_big_endian)
- insn = bfd_getb32 ((unsigned char *) where);
- else
- insn = bfd_getl32 ((unsigned char *) where);
- insn = arc_insert_operand (insn, operand, -1, NULL, (offsetT) value,
- fixP->fx_file, fixP->fx_line);
- if (target_big_endian)
- bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
- else
- bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
+ /* Register declaration. */
+ arc_reg_hash = hash_new ();
+ if (arc_reg_hash == NULL)
+ as_fatal (_("Virtual memory exhausted"));
- if (fixP->fx_done)
+ declare_register_set ();
+ declare_register ("gp", 26);
+ declare_register ("fp", 27);
+ declare_register ("sp", 28);
+ declare_register ("ilink", 29);
+ declare_register ("ilink1", 29);
+ declare_register ("ilink2", 30);
+ declare_register ("blink", 31);
+
+ declare_register ("mlo", 57);
+ declare_register ("mmid", 58);
+ declare_register ("mhi", 59);
+
+ declare_register ("acc1", 56);
+ declare_register ("acc2", 57);
+
+ declare_register ("lp_count", 60);
+ declare_register ("pcl", 63);
+
+ /* Initialize the last instructions. */
+ memset (&arc_last_insns[0], 0, sizeof (arc_last_insns));
+}
+
+/* Write a value out to the object file, using the appropriate
+ endianness. */
+
+void
+md_number_to_chars (char *buf,
+ valueT val,
+ int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+/* Round up a section size to the appropriate boundary. */
+
+valueT
+md_section_align (segT segment,
+ valueT size)
+{
+ int align = bfd_get_section_alignment (stdoutput, segment);
+
+ return ((size + (1 << align) - 1) & (-((valueT) 1 << align)));
+}
+
+/* The location from which a PC relative jump should be calculated,
+ given a PC relative reloc. */
+
+long
+md_pcrel_from_section (fixS *fixP,
+ segT sec)
+{
+ offsetT base = fixP->fx_where + fixP->fx_frag->fr_address;
+
+ pr_debug ("pcrel_from_section, fx_offset = %d\n", (int) fixP->fx_offset);
+
+ if (fixP->fx_addsy != (symbolS *) NULL
+ && (!S_IS_DEFINED (fixP->fx_addsy)
+ || S_GET_SEGMENT (fixP->fx_addsy) != sec))
+ {
+ pr_debug ("Unknown pcrel symbol: %s\n", S_GET_NAME (fixP->fx_addsy));
+
+ /* The symbol is undefined (or is defined but not in this section).
+ Let the linker figure it out. */
+ return 0;
+ }
+
+ if ((int) fixP->fx_r_type < 0)
+ {
+ /* These are the "internal" relocations. Align them to
+ 32 bit boundary (PCL), for the moment. */
+ base &= ~3;
+ }
+ else
+ {
+ switch (fixP->fx_r_type)
{
- /* Nothing else to do here. */
- return;
+ case BFD_RELOC_ARC_PC32:
+ /* The hardware calculates relative to the start of the
+ insn, but this relocation is relative to location of the
+ LIMM, compensate. The base always needs to be
+ substracted by 4 as we do not support this type of PCrel
+ relocation for short instructions. */
+ base -= 4;
+ /* Fall through. */
+ case BFD_RELOC_ARC_PLT32:
+ case BFD_RELOC_ARC_S25H_PCREL_PLT:
+ case BFD_RELOC_ARC_S21H_PCREL_PLT:
+ case BFD_RELOC_ARC_S25W_PCREL_PLT:
+ case BFD_RELOC_ARC_S21W_PCREL_PLT:
+
+ case BFD_RELOC_ARC_S21H_PCREL:
+ case BFD_RELOC_ARC_S25H_PCREL:
+ case BFD_RELOC_ARC_S13_PCREL:
+ case BFD_RELOC_ARC_S21W_PCREL:
+ case BFD_RELOC_ARC_S25W_PCREL:
+ base &= ~3;
+ break;
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unhandled reloc %s in md_pcrel_from_section"),
+ bfd_get_reloc_code_name (fixP->fx_r_type));
+ break;
}
+ }
+
+ pr_debug ("pcrel from %x + %lx = %x, symbol: %s (%x)\n",
+ fixP->fx_frag->fr_address, fixP->fx_where, base,
+ fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : "(null)",
+ fixP->fx_addsy ? S_GET_VALUE (fixP->fx_addsy) : 0);
+
+ return base;
+}
+
+/* Given a BFD relocation find the coresponding operand. */
+
+static const struct arc_operand *
+find_operand_for_reloc (extended_bfd_reloc_code_real_type reloc)
+{
+ unsigned i;
+
+ for (i = 0; i < arc_num_operands; i++)
+ if (arc_operands[i].default_reloc == reloc)
+ return &arc_operands[i];
+ return NULL;
+}
+
+/* Insert an operand value into an instruction. */
+
+static unsigned
+insert_operand (unsigned insn,
+ const struct arc_operand *operand,
+ offsetT val,
+ const char *file,
+ unsigned line)
+{
+ offsetT min = 0, max = 0;
- /* Determine a BFD reloc value based on the operand information.
- We are only prepared to turn a few of the operands into relocs.
- !!! Note that we can't handle limm values here. Since we're using
- implicit addends the addend must be inserted into the instruction,
- however, the opcode insertion routines currently do nothing with
- limm values. */
- if (operand->fmt == 'B')
+ if (operand->bits != 32
+ && !(operand->flags & ARC_OPERAND_NCHK)
+ && !(operand->flags & ARC_OPERAND_FAKE))
+ {
+ if (operand->flags & ARC_OPERAND_SIGNED)
+ {
+ max = (1 << (operand->bits - 1)) - 1;
+ min = -(1 << (operand->bits - 1));
+ }
+ else
{
- assert ((operand->flags & ARC_OPERAND_RELATIVE_BRANCH) != 0
- && operand->bits == 20
- && operand->shift == 7);
- fixP->fx_r_type = BFD_RELOC_ARC_B22_PCREL;
+ max = (1 << operand->bits) - 1;
+ min = 0;
}
- else if (operand->fmt == 'J')
+
+ if (val < min || val > max)
+ as_bad_value_out_of_range (_("operand"),
+ val, min, max, file, line);
+ }
+
+ pr_debug ("insert field: %ld <= %ld <= %ld in 0x%08x\n",
+ min, val, max, insn);
+
+ if ((operand->flags & ARC_OPERAND_ALIGNED32)
+ && (val & 0x03))
+ as_bad_where (file, line,
+ _("Unaligned operand. Needs to be 32bit aligned"));
+
+ if ((operand->flags & ARC_OPERAND_ALIGNED16)
+ && (val & 0x01))
+ as_bad_where (file, line,
+ _("Unaligned operand. Needs to be 16bit aligned"));
+
+ if (operand->insert)
+ {
+ const char *errmsg = NULL;
+
+ insn = (*operand->insert) (insn, val, &errmsg);
+ if (errmsg)
+ as_warn_where (file, line, "%s", errmsg);
+ }
+ else
+ {
+ if (operand->flags & ARC_OPERAND_TRUNCATE)
{
- assert ((operand->flags & ARC_OPERAND_ABSOLUTE_BRANCH) != 0
- && operand->bits == 24
- && operand->shift == 32);
- fixP->fx_r_type = BFD_RELOC_ARC_B26;
+ if (operand->flags & ARC_OPERAND_ALIGNED32)
+ val >>= 2;
+ if (operand->flags & ARC_OPERAND_ALIGNED16)
+ val >>= 1;
}
- else if (operand->fmt == 'L')
+ insn |= ((val & ((1 << operand->bits) - 1)) << operand->shift);
+ }
+ return insn;
+}
+
+/* Apply a fixup to the object code. At this point all symbol values
+ should be fully resolved, and we attempt to completely resolve the
+ reloc. If we can not do that, we determine the correct reloc code
+ and put it back in the fixup. To indicate that a fixup has been
+ eliminated, set fixP->fx_done. */
+
+void
+md_apply_fix (fixS *fixP,
+ valueT *valP,
+ segT seg)
+{
+ char * const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
+ valueT value = *valP;
+ unsigned insn = 0;
+ symbolS *fx_addsy, *fx_subsy;
+ offsetT fx_offset;
+ segT add_symbol_segment = absolute_section;
+ segT sub_symbol_segment = absolute_section;
+ const struct arc_operand *operand = NULL;
+ extended_bfd_reloc_code_real_type reloc;
+
+ pr_debug ("%s:%u: apply_fix: r_type=%d (%s) value=0x%lX offset=0x%lX\n",
+ fixP->fx_file, fixP->fx_line, fixP->fx_r_type,
+ ((int) fixP->fx_r_type < 0) ? "Internal":
+ bfd_get_reloc_code_name (fixP->fx_r_type), value,
+ fixP->fx_offset);
+
+ fx_addsy = fixP->fx_addsy;
+ fx_subsy = fixP->fx_subsy;
+ fx_offset = 0;
+
+ if (fx_addsy)
+ {
+ add_symbol_segment = S_GET_SEGMENT (fx_addsy);
+ }
+
+ if (fx_subsy
+ && fixP->fx_r_type != BFD_RELOC_ARC_TLS_DTPOFF
+ && fixP->fx_r_type != BFD_RELOC_ARC_TLS_DTPOFF_S9
+ && fixP->fx_r_type != BFD_RELOC_ARC_TLS_GD_LD)
+ {
+ resolve_symbol_value (fx_subsy);
+ sub_symbol_segment = S_GET_SEGMENT (fx_subsy);
+
+ if (sub_symbol_segment == absolute_section)
{
- assert ((operand->flags & ARC_OPERAND_LIMM) != 0
- && operand->bits == 32
- && operand->shift == 32);
- fixP->fx_r_type = BFD_RELOC_32;
+ /* The symbol is really a constant. */
+ fx_offset -= S_GET_VALUE (fx_subsy);
+ fx_subsy = NULL;
}
else
{
as_bad_where (fixP->fx_file, fixP->fx_line,
- "unresolved expression that must be resolved");
- fixP->fx_done = 1;
+ _("can't resolve `%s' {%s section} - `%s' {%s section}"),
+ fx_addsy ? S_GET_NAME (fx_addsy) : "0",
+ segment_name (add_symbol_segment),
+ S_GET_NAME (fx_subsy),
+ segment_name (sub_symbol_segment));
return;
}
}
- else
+
+ if (fx_addsy
+ && !S_IS_WEAK (fx_addsy))
+ {
+ if (add_symbol_segment == seg
+ && fixP->fx_pcrel)
+ {
+ value += S_GET_VALUE (fx_addsy);
+ value -= md_pcrel_from_section (fixP, seg);
+ fx_addsy = NULL;
+ fixP->fx_pcrel = FALSE;
+ }
+ else if (add_symbol_segment == absolute_section)
+ {
+ value = fixP->fx_offset;
+ fx_offset += S_GET_VALUE (fixP->fx_addsy);
+ fx_addsy = NULL;
+ fixP->fx_pcrel = FALSE;
+ }
+ }
+
+ if (!fx_addsy)
+ fixP->fx_done = TRUE;
+
+ if (fixP->fx_pcrel)
{
+ if (fx_addsy
+ && ((S_IS_DEFINED (fx_addsy)
+ && S_GET_SEGMENT (fx_addsy) != seg)
+ || S_IS_WEAK (fx_addsy)))
+ value += md_pcrel_from_section (fixP, seg);
+
switch (fixP->fx_r_type)
{
- case BFD_RELOC_8:
- md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
- value, 1);
+ case BFD_RELOC_ARC_32_ME:
+ /* This is a pc-relative value in a LIMM. Adjust it to the
+ address of the instruction not to the address of the
+ LIMM. Note: it is not anylonger valid this afirmation as
+ the linker consider ARC_PC32 a fixup to entire 64 bit
+ insn. */
+ fixP->fx_offset += fixP->fx_frag->fr_address;
+ /* Fall through. */
+ case BFD_RELOC_32:
+ fixP->fx_r_type = BFD_RELOC_ARC_PC32;
+ /* Fall through. */
+ case BFD_RELOC_ARC_PC32:
+ /* fixP->fx_offset += fixP->fx_where - fixP->fx_dot_value; */
break;
- case BFD_RELOC_16:
- md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
- value, 2);
+ default:
+ if ((int) fixP->fx_r_type < 0)
+ as_fatal (_("PC relative relocation not allowed for (internal) type %d"),
+ fixP->fx_r_type);
break;
- case BFD_RELOC_32:
- md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
- value, 4);
+ }
+ }
+
+ pr_debug ("%s:%u: apply_fix: r_type=%d (%s) value=0x%lX offset=0x%lX\n",
+ fixP->fx_file, fixP->fx_line, fixP->fx_r_type,
+ ((int) fixP->fx_r_type < 0) ? "Internal":
+ bfd_get_reloc_code_name (fixP->fx_r_type), value,
+ fixP->fx_offset);
+
+
+ /* Now check for TLS relocations. */
+ reloc = fixP->fx_r_type;
+ switch (reloc)
+ {
+ case BFD_RELOC_ARC_TLS_DTPOFF:
+ case BFD_RELOC_ARC_TLS_LE_32:
+ if (fixP->fx_done)
+ break;
+ /* Fall through. */
+ case BFD_RELOC_ARC_TLS_GD_GOT:
+ case BFD_RELOC_ARC_TLS_IE_GOT:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ break;
+
+ case BFD_RELOC_ARC_TLS_GD_LD:
+ gas_assert (!fixP->fx_offset);
+ if (fixP->fx_subsy)
+ fixP->fx_offset
+ = (S_GET_VALUE (fixP->fx_subsy)
+ - fixP->fx_frag->fr_address- fixP->fx_where);
+ fixP->fx_subsy = NULL;
+ /* Fall through. */
+ case BFD_RELOC_ARC_TLS_GD_CALL:
+ /* These two relocs are there just to allow ld to change the tls
+ model for this symbol, by patching the code. The offset -
+ and scale, if any - will be installed by the linker. */
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ break;
+
+ case BFD_RELOC_ARC_TLS_LE_S9:
+ case BFD_RELOC_ARC_TLS_DTPOFF_S9:
+ as_bad (_("TLS_*_S9 relocs are not supported yet"));
+ break;
+
+ default:
+ break;
+ }
+
+ if (!fixP->fx_done)
+ {
+ return;
+ }
+
+ /* Addjust the value if we have a constant. */
+ value += fx_offset;
+
+ /* For hosts with longs bigger than 32-bits make sure that the top
+ bits of a 32-bit negative value read in by the parser are set,
+ so that the correct comparisons are made. */
+ if (value & 0x80000000)
+ value |= (-1L << 31);
+
+ reloc = fixP->fx_r_type;
+ switch (reloc)
+ {
+ case BFD_RELOC_8:
+ case BFD_RELOC_16:
+ case BFD_RELOC_24:
+ case BFD_RELOC_32:
+ case BFD_RELOC_64:
+ case BFD_RELOC_ARC_32_PCREL:
+ md_number_to_chars (fixpos, value, fixP->fx_size);
+ return;
+
+ case BFD_RELOC_ARC_GOTPC32:
+ /* I cannot fix an GOTPC relocation because I need to relax it
+ from ld rx,[pcl,@sym@gotpc] to add rx,pcl,@sym@gotpc. */
+ as_bad (_("Unsupported operation on reloc"));
+ return;
+
+ case BFD_RELOC_ARC_TLS_DTPOFF:
+ case BFD_RELOC_ARC_TLS_LE_32:
+ gas_assert (!fixP->fx_addsy);
+ gas_assert (!fixP->fx_subsy);
+
+ case BFD_RELOC_ARC_GOTOFF:
+ case BFD_RELOC_ARC_32_ME:
+ case BFD_RELOC_ARC_PC32:
+ md_number_to_chars_midend (fixpos, value, fixP->fx_size);
+ return;
+
+ case BFD_RELOC_ARC_PLT32:
+ md_number_to_chars_midend (fixpos, value, fixP->fx_size);
+ return;
+
+ case BFD_RELOC_ARC_S25H_PCREL_PLT:
+ reloc = BFD_RELOC_ARC_S25W_PCREL;
+ goto solve_plt;
+
+ case BFD_RELOC_ARC_S21H_PCREL_PLT:
+ reloc = BFD_RELOC_ARC_S21H_PCREL;
+ goto solve_plt;
+
+ case BFD_RELOC_ARC_S25W_PCREL_PLT:
+ reloc = BFD_RELOC_ARC_S25W_PCREL;
+ goto solve_plt;
+
+ case BFD_RELOC_ARC_S21W_PCREL_PLT:
+ reloc = BFD_RELOC_ARC_S21W_PCREL;
+
+ case BFD_RELOC_ARC_S25W_PCREL:
+ case BFD_RELOC_ARC_S21W_PCREL:
+ case BFD_RELOC_ARC_S21H_PCREL:
+ case BFD_RELOC_ARC_S25H_PCREL:
+ case BFD_RELOC_ARC_S13_PCREL:
+ solve_plt:
+ operand = find_operand_for_reloc (reloc);
+ gas_assert (operand);
+ break;
+
+ default:
+ {
+ if ((int) fixP->fx_r_type >= 0)
+ as_fatal (_("unhandled relocation type %s"),
+ bfd_get_reloc_code_name (fixP->fx_r_type));
+
+ /* The rest of these fixups needs to be completely resolved as
+ constants. */
+ if (fixP->fx_addsy != 0
+ && S_GET_SEGMENT (fixP->fx_addsy) != absolute_section)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("non-absolute expression in constant field"));
+
+ gas_assert (-(int) fixP->fx_r_type < (int) arc_num_operands);
+ operand = &arc_operands[-(int) fixP->fx_r_type];
+ break;
+ }
+ }
+
+ if (target_big_endian)
+ {
+ switch (fixP->fx_size)
+ {
+ case 4:
+ insn = bfd_getb32 (fixpos);
break;
-#if 0
- case BFD_RELOC_64:
- md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
- value, 8);
+ case 2:
+ insn = bfd_getb16 (fixpos);
break;
-#endif
- case BFD_RELOC_ARC_B26:
- /* If !fixP->fx_done then `value' is an implicit addend.
- We must shift it right by 2 in this case as well because the
- linker performs the relocation and then adds this in (as opposed
- to adding this in and then shifting right by 2). */
- value >>= 2;
- md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
- value, 4);
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unknown fixup size"));
+ }
+ }
+ else
+ {
+ insn = 0;
+ switch (fixP->fx_size)
+ {
+ case 4:
+ insn = bfd_getl16 (fixpos) << 16 | bfd_getl16 (fixpos + 2);
+ break;
+ case 2:
+ insn = bfd_getl16 (fixpos);
break;
default:
- abort ();
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unknown fixup size"));
}
}
+
+ insn = insert_operand (insn, operand, (offsetT) value,
+ fixP->fx_file, fixP->fx_line);
+
+ md_number_to_chars_midend (fixpos, insn, fixP->fx_size);
+}
+
+/* Prepare machine-dependent frags for relaxation.
+
+ Called just before relaxation starts. Any symbol that is now undefined
+ will not become defined.
+
+ Return the correct fr_subtype in the frag.
+
+ Return the initial "guess for fr_var" to caller. The guess for fr_var
+ is *actually* the growth beyond fr_fix. Whatever we do to grow fr_fix
+ or fr_var contributes to our returned value.
+
+ Although it may not be explicit in the frag, pretend
+ fr_var starts with a value. */
+
+int
+md_estimate_size_before_relax (fragS *fragP,
+ segT segment)
+{
+ int growth;
+
+ /* If the symbol is not located within the same section AND it's not
+ an absolute section, use the maximum. OR if the symbol is a
+ constant AND the insn is by nature not pc-rel, use the maximum.
+ OR if the symbol is being equated against another symbol, use the
+ maximum. OR if the symbol is weak use the maximum. */
+ if ((S_GET_SEGMENT (fragP->fr_symbol) != segment
+ && S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
+ || (symbol_constant_p (fragP->fr_symbol)
+ && !fragP->tc_frag_data.pcrel)
+ || symbol_equated_p (fragP->fr_symbol)
+ || S_IS_WEAK (fragP->fr_symbol))
+ {
+ while (md_relax_table[fragP->fr_subtype].rlx_more != ARC_RLX_NONE)
+ ++fragP->fr_subtype;
+ }
+
+ growth = md_relax_table[fragP->fr_subtype].rlx_length;
+ fragP->fr_var = growth;
+
+ pr_debug ("%s:%d: md_estimate_size_before_relax: %d\n",
+ fragP->fr_file, fragP->fr_line, growth);
+
+ return growth;
}
/* Translate internal representation of relocation info to BFD target
format. */
arelent *
-tc_gen_reloc (section, fixP)
- asection *section ATTRIBUTE_UNUSED;
- fixS *fixP;
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED,
+ fixS *fixP)
{
arelent *reloc;
+ bfd_reloc_code_real_type code;
- reloc = (arelent *) xmalloc (sizeof (arelent));
-
- reloc->sym_ptr_ptr = &fixP->fx_addsy->bsym;
+ reloc = (arelent *) xmalloc (sizeof (* reloc));
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
reloc->address = fixP->fx_frag->fr_address + fixP->fx_where;
- reloc->howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
- if (reloc->howto == (reloc_howto_type *) NULL)
+
+ /* Make sure none of our internal relocations make it this far.
+ They'd better have been fully resolved by this point. */
+ gas_assert ((int) fixP->fx_r_type > 0);
+
+ code = fixP->fx_r_type;
+
+ /* if we have something like add gp, pcl,
+ _GLOBAL_OFFSET_TABLE_@gotpc. */
+ if (code == BFD_RELOC_ARC_GOTPC32
+ && GOT_symbol
+ && fixP->fx_addsy == GOT_symbol)
+ code = BFD_RELOC_ARC_GOTPC;
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+ if (reloc->howto == NULL)
{
as_bad_where (fixP->fx_file, fixP->fx_line,
- "internal error: can't export reloc type %d (`%s')",
- fixP->fx_r_type,
- bfd_get_reloc_code_name (fixP->fx_r_type));
+ _("cannot represent `%s' relocation in object file"),
+ bfd_get_reloc_code_name (code));
return NULL;
}
- assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
+ if (!fixP->fx_pcrel != !reloc->howto->pc_relative)
+ as_fatal (_("internal error? cannot generate `%s' relocation"),
+ bfd_get_reloc_code_name (code));
- /* Set addend to account for PC being advanced one insn before the
- target address is computed. */
+ gas_assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
- reloc->addend = (fixP->fx_pcrel ? -4 : 0);
+ if (code == BFD_RELOC_ARC_TLS_DTPOFF
+ || code == BFD_RELOC_ARC_TLS_DTPOFF_S9)
+ {
+ asymbol *sym
+ = fixP->fx_subsy ? symbol_get_bfdsym (fixP->fx_subsy) : NULL;
+ /* We just want to store a 24 bit index, but we have to wait
+ till after write_contents has been called via
+ bfd_map_over_sections before we can get the index from
+ _bfd_elf_symbol_from_bfd_symbol. Thus, the write_relocs
+ function is elf32-arc.c has to pick up the slack.
+ Unfortunately, this leads to problems with hosts that have
+ pointers wider than long (bfd_vma). There would be various
+ ways to handle this, all error-prone :-( */
+ reloc->addend = (bfd_vma) sym;
+ if ((asymbol *) reloc->addend != sym)
+ {
+ as_bad ("Can't store pointer\n");
+ return NULL;
+ }
+ }
+ else
+ reloc->addend = fixP->fx_offset;
return reloc;
}
+
+/* Perform post-processing of machine-dependent frags after relaxation.
+ Called after relaxation is finished.
+ In: Address of frag.
+ fr_type == rs_machine_dependent.
+ fr_subtype is what the address relaxed to.
+
+ Out: Any fixS:s and constants are set up. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ segT segment ATTRIBUTE_UNUSED,
+ fragS *fragP)
+{
+ const relax_typeS *table_entry;
+ char *dest;
+ const struct arc_opcode *opcode;
+ struct arc_insn insn;
+ int size, fix;
+ struct arc_relax_type *relax_arg = &fragP->tc_frag_data;
+
+ fix = (fragP->fr_fix < 0 ? 0 : fragP->fr_fix);
+ dest = fragP->fr_literal + fix;
+ table_entry = TC_GENERIC_RELAX_TABLE + fragP->fr_subtype;
+
+ pr_debug ("%s:%d: md_convert_frag, subtype: %d, fix: %d, var: %d\n",
+ fragP->fr_file, fragP->fr_line,
+ fragP->fr_subtype, fix, fragP->fr_var);
+
+ if (fragP->fr_subtype <= 0
+ && fragP->fr_subtype >= arc_num_relax_opcodes)
+ as_fatal (_("no relaxation found for this instruction."));
+
+ opcode = &arc_relax_opcodes[fragP->fr_subtype];
+
+ assemble_insn (opcode, relax_arg->tok, relax_arg->ntok, relax_arg->pflags,
+ relax_arg->nflg, &insn);
+
+ apply_fixups (&insn, fragP, fix);
+
+ size = insn.short_insn ? (insn.has_limm ? 6 : 2) : (insn.has_limm ? 8 : 4);
+ gas_assert (table_entry->rlx_length == size);
+ emit_insn0 (&insn, dest, TRUE);
+
+ fragP->fr_fix += table_entry->rlx_length;
+ fragP->fr_var = 0;
+}
+
+/* We have no need to default values of symbols. We could catch
+ register names here, but that is handled by inserting them all in
+ the symbol table to begin with. */
+
+symbolS *
+md_undefined_symbol (char *name)
+{
+ /* The arc abi demands that a GOT[0] should be referencible as
+ [pc+_DYNAMIC@gotpc]. Hence we convert a _DYNAMIC@gotpc to a
+ GOTPC reference to _GLOBAL_OFFSET_TABLE_. */
+ if (((*name == '_')
+ && (*(name+1) == 'G')
+ && (strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0))
+ || ((*name == '_')
+ && (*(name+1) == 'D')
+ && (strcmp (name, DYNAMIC_STRUCT_NAME) == 0)))
+ {
+ if (!GOT_symbol)
+ {
+ if (symbol_find (name))
+ as_bad ("GOT already in symbol table");
+
+ GOT_symbol = symbol_new (GLOBAL_OFFSET_TABLE_NAME, undefined_section,
+ (valueT) 0, &zero_address_frag);
+ };
+ return GOT_symbol;
+ }
+ return NULL;
+}
+
+/* Turn a string in input_line_pointer into a floating point constant
+ of type type, and store the appropriate bytes in *litP. The number
+ of LITTLENUMS emitted is stored in *sizeP. An error message is
+ returned, or NULL on OK. */
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, target_big_endian);
+}
+
+/* Called for any expression that can not be recognized. When the
+ function is called, `input_line_pointer' will point to the start of
+ the expression. */
+
+void
+md_operand (expressionS *expressionP ATTRIBUTE_UNUSED)
+{
+ char *p = input_line_pointer;
+ if (*p == '@')
+ {
+ input_line_pointer++;
+ expressionP->X_op = O_symbol;
+ expression (expressionP);
+ }
+}
+
+/* This function is called from the function 'expression', it attempts
+ to parse special names (in our case register names). It fills in
+ the expression with the identified register. It returns TRUE if
+ it is a register and FALSE otherwise. */
+
+bfd_boolean
+arc_parse_name (const char *name,
+ struct expressionS *e)
+{
+ struct symbol *sym;
+
+ if (!assembling_insn)
+ return FALSE;
+
+ /* Handle only registers. */
+ if (e->X_op != O_absent)
+ return FALSE;
+
+ sym = hash_find (arc_reg_hash, name);
+ if (sym)
+ {
+ e->X_op = O_register;
+ e->X_add_number = S_GET_VALUE (sym);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* md_parse_option
+ Invocation line includes a switch not recognized by the base assembler.
+ See if it's a processor-specific option.
+
+ New options (supported) are:
+
+ -mcpu=<cpu name> Assemble for selected processor
+ -EB/-mbig-endian Big-endian
+ -EL/-mlittle-endian Little-endian
+ -mrelax Enable relaxation
+
+ The following CPU names are recognized:
+ arc700, av2em, av2hs. */
+
+int
+md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+{
+ int cpu_flags = EF_ARC_CPU_GENERIC;
+
+ switch (c)
+ {
+ case OPTION_ARC600:
+ case OPTION_ARC601:
+ return md_parse_option (OPTION_MCPU, "arc600");
+
+ case OPTION_ARC700:
+ return md_parse_option (OPTION_MCPU, "arc700");
+
+ case OPTION_ARCEM:
+ return md_parse_option (OPTION_MCPU, "arcem");
+
+ case OPTION_ARCHS:
+ return md_parse_option (OPTION_MCPU, "archs");
+
+ case OPTION_MCPU:
+ {
+ int i;
+ char *s = alloca (strlen (arg) + 1);
+
+ {
+ char *t = s;
+ char *arg1 = arg;
+
+ do
+ *t = TOLOWER (*arg1++);
+ while (*t++);
+ }
+
+ for (i = 0; cpu_types[i].name; ++i)
+ {
+ if (!strcmp (cpu_types[i].name, s))
+ {
+ arc_target = cpu_types[i].flags;
+ arc_target_name = cpu_types[i].name;
+ arc_features = cpu_types[i].features;
+ arc_mach_type = cpu_types[i].mach;
+ cpu_flags = cpu_types[i].eflags;
+
+ mach_type_specified_p = 1;
+ break;
+ }
+ }
+
+ if (!cpu_types[i].name)
+ {
+ as_fatal (_("unknown architecture: %s\n"), arg);
+ }
+ break;
+ }
+
+ case OPTION_EB:
+ arc_target_format = "elf32-bigarc";
+ byte_order = BIG_ENDIAN;
+ break;
+
+ case OPTION_EL:
+ arc_target_format = "elf32-littlearc";
+ byte_order = LITTLE_ENDIAN;
+ break;
+
+ case OPTION_CD:
+ /* This option has an effect only on ARC EM. */
+ if (arc_target & ARC_OPCODE_ARCv2EM)
+ arc_features |= ARC_CD;
+ break;
+
+ case OPTION_RELAX:
+ relaxation_state = 1;
+ break;
+
+ case OPTION_USER_MODE:
+ case OPTION_LD_EXT_MASK:
+ case OPTION_SWAP:
+ case OPTION_NORM:
+ case OPTION_BARREL_SHIFT:
+ case OPTION_MIN_MAX:
+ case OPTION_NO_MPY:
+ case OPTION_EA:
+ case OPTION_MUL64:
+ case OPTION_SIMD:
+ case OPTION_SPFP:
+ case OPTION_DPFP:
+ case OPTION_XMAC_D16:
+ case OPTION_XMAC_24:
+ case OPTION_DSP_PACKA:
+ case OPTION_CRC:
+ case OPTION_DVBF:
+ case OPTION_TELEPHONY:
+ case OPTION_XYMEMORY:
+ case OPTION_LOCK:
+ case OPTION_SWAPE:
+ case OPTION_RTSC:
+ case OPTION_FPUDA:
+ /* Dummy options are accepted but have no effect. */
+ break;
+
+ default:
+ return 0;
+ }
+
+ if (cpu_flags != EF_ARC_CPU_GENERIC)
+ arc_eflag = (arc_eflag & ~EF_ARC_MACH_MSK) | cpu_flags;
+
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _("ARC-specific assembler options:\n"));
+
+ fprintf (stream, " -mcpu=<cpu name>\t assemble for CPU <cpu name>\n");
+ fprintf (stream,
+ " -mcode-density\t enable code density option for ARC EM\n");
+
+ fprintf (stream, _("\
+ -EB assemble code for a big-endian cpu\n"));
+ fprintf (stream, _("\
+ -EL assemble code for a little-endian cpu\n"));
+ fprintf (stream, _("\
+ -mrelax Enable relaxation\n"));
+
+}
+
+/* Find the proper relocation for the given opcode. */
+
+static extended_bfd_reloc_code_real_type
+find_reloc (const char *name,
+ const char *opcodename,
+ const struct arc_flags *pflags,
+ int nflg,
+ extended_bfd_reloc_code_real_type reloc)
+{
+ unsigned int i;
+ int j;
+ bfd_boolean found_flag, tmp;
+ extended_bfd_reloc_code_real_type ret = BFD_RELOC_UNUSED;
+
+ for (i = 0; i < arc_num_equiv_tab; i++)
+ {
+ const struct arc_reloc_equiv_tab *r = &arc_reloc_equiv[i];
+
+ /* Find the entry. */
+ if (strcmp (name, r->name))
+ continue;
+ if (r->mnemonic && (strcmp (r->mnemonic, opcodename)))
+ continue;
+ if (r->flags[0])
+ {
+ if (!nflg)
+ continue;
+ found_flag = FALSE;
+ unsigned * psflg = (unsigned *)r->flags;
+ do
+ {
+ tmp = FALSE;
+ for (j = 0; j < nflg; j++)
+ if (!strcmp (pflags[j].name,
+ arc_flag_operands[*psflg].name))
+ {
+ tmp = TRUE;
+ break;
+ }
+ if (!tmp)
+ {
+ found_flag = FALSE;
+ break;
+ }
+ else
+ {
+ found_flag = TRUE;
+ }
+ ++ psflg;
+ } while (*psflg);
+
+ if (!found_flag)
+ continue;
+ }
+
+ if (reloc != r->oldreloc)
+ continue;
+ /* Found it. */
+ ret = r->newreloc;
+ break;
+ }
+
+ if (ret == BFD_RELOC_UNUSED)
+ as_bad (_("Unable to find %s relocation for instruction %s"),
+ name, opcodename);
+ return ret;
+}
+
+/* All the symbol types that are allowed to be used for
+ relaxation. */
+
+static bfd_boolean
+may_relax_expr (expressionS tok)
+{
+ /* Check if we have unrelaxable relocs. */
+ switch (tok.X_md)
+ {
+ default:
+ break;
+ case O_plt:
+ return FALSE;
+ }
+
+ switch (tok.X_op)
+ {
+ case O_symbol:
+ case O_multiply:
+ case O_divide:
+ case O_modulus:
+ case O_add:
+ case O_subtract:
+ break;
+
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Checks if flags are in line with relaxable insn. */
+
+static bfd_boolean
+relaxable_flag (const struct arc_relaxable_ins *ins,
+ const struct arc_flags *pflags,
+ int nflgs)
+{
+ unsigned flag_class,
+ flag,
+ flag_class_idx = 0,
+ flag_idx = 0;
+
+ const struct arc_flag_operand *flag_opand;
+ int i, counttrue = 0;
+
+ /* Iterate through flags classes. */
+ while ((flag_class = ins->flag_classes[flag_class_idx]) != 0)
+ {
+ /* Iterate through flags in flag class. */
+ while ((flag = arc_flag_classes[flag_class].flags[flag_idx])
+ != 0)
+ {
+ flag_opand = &arc_flag_operands[flag];
+ /* Iterate through flags in ins to compare. */
+ for (i = 0; i < nflgs; ++i)
+ {
+ if (strcmp (flag_opand->name, pflags[i].name) == 0)
+ ++counttrue;
+ }
+
+ ++flag_idx;
+ }
+
+ ++flag_class_idx;
+ flag_idx = 0;
+ }
+
+ /* If counttrue == nflgs, then all flags have been found. */
+ return (counttrue == nflgs ? TRUE : FALSE);
+}
+
+/* Checks if operands are in line with relaxable insn. */
+
+static bfd_boolean
+relaxable_operand (const struct arc_relaxable_ins *ins,
+ const expressionS *tok,
+ int ntok)
+{
+ const enum rlx_operand_type *operand = &ins->operands[0];
+ int i = 0;
+
+ while (*operand != EMPTY)
+ {
+ const expressionS *epr = &tok[i];
+
+ if (i != 0 && i >= ntok)
+ return FALSE;
+
+ switch (*operand)
+ {
+ case IMMEDIATE:
+ if (!(epr->X_op == O_multiply
+ || epr->X_op == O_divide
+ || epr->X_op == O_modulus
+ || epr->X_op == O_add
+ || epr->X_op == O_subtract
+ || epr->X_op == O_symbol))
+ return FALSE;
+ break;
+
+ case REGISTER_DUP:
+ if ((i <= 0)
+ || (epr->X_add_number != tok[i - 1].X_add_number))
+ return FALSE;
+ /* Fall through. */
+ case REGISTER:
+ if (epr->X_op != O_register)
+ return FALSE;
+ break;
+
+ case REGISTER_S:
+ if (epr->X_op != O_register)
+ return FALSE;
+
+ switch (epr->X_add_number)
+ {
+ case 0: case 1: case 2: case 3:
+ case 12: case 13: case 14: case 15:
+ break;
+ default:
+ return FALSE;
+ }
+ break;
+
+ case REGISTER_NO_GP:
+ if ((epr->X_op != O_register)
+ || (epr->X_add_number == 26)) /* 26 is the gp register. */
+ return FALSE;
+ break;
+
+ case BRACKET:
+ if (epr->X_op != O_bracket)
+ return FALSE;
+ break;
+
+ default:
+ /* Don't understand, bail out. */
+ return FALSE;
+ break;
+ }
+
+ ++i;
+ operand = &ins->operands[i];
+ }
+
+ return (i == ntok ? TRUE : FALSE);
+}
+
+/* Return TRUE if this OPDCODE is a candidate for relaxation. */
+
+static bfd_boolean
+relax_insn_p (const struct arc_opcode *opcode,
+ const expressionS *tok,
+ int ntok,
+ const struct arc_flags *pflags,
+ int nflg)
+{
+ unsigned i;
+ bfd_boolean rv = FALSE;
+
+ /* Check the relaxation table. */
+ for (i = 0; i < arc_num_relaxable_ins && relaxation_state; ++i)
+ {
+ const struct arc_relaxable_ins *arc_rlx_ins = &arc_relaxable_insns[i];
+
+ if ((strcmp (opcode->name, arc_rlx_ins->mnemonic_r) == 0)
+ && may_relax_expr (tok[arc_rlx_ins->opcheckidx])
+ && relaxable_operand (arc_rlx_ins, tok, ntok)
+ && relaxable_flag (arc_rlx_ins, pflags, nflg))
+ {
+ rv = TRUE;
+ frag_now->fr_subtype = arc_relaxable_insns[i].subtype;
+ memcpy (&frag_now->tc_frag_data.tok, tok,
+ sizeof (expressionS) * ntok);
+ memcpy (&frag_now->tc_frag_data.pflags, pflags,
+ sizeof (struct arc_flags) * nflg);
+ frag_now->tc_frag_data.nflg = nflg;
+ frag_now->tc_frag_data.ntok = ntok;
+ break;
+ }
+ }
+
+ return rv;
+}
+
+/* Turn an opcode description and a set of arguments into
+ an instruction and a fixup. */
+
+static void
+assemble_insn (const struct arc_opcode *opcode,
+ const expressionS *tok,
+ int ntok,
+ const struct arc_flags *pflags,
+ int nflg,
+ struct arc_insn *insn)
+{
+ const expressionS *reloc_exp = NULL;
+ unsigned image;
+ const unsigned char *argidx;
+ int i;
+ int tokidx = 0;
+ unsigned char pcrel = 0;
+ bfd_boolean needGOTSymbol;
+ bfd_boolean has_delay_slot = FALSE;
+ extended_bfd_reloc_code_real_type reloc = BFD_RELOC_UNUSED;
+
+ memset (insn, 0, sizeof (*insn));
+ image = opcode->opcode;
+
+ pr_debug ("%s:%d: assemble_insn: %s using opcode %x\n",
+ frag_now->fr_file, frag_now->fr_line, opcode->name,
+ opcode->opcode);
+
+ /* Handle operands. */
+ for (argidx = opcode->operands; *argidx; ++argidx)
+ {
+ const struct arc_operand *operand = &arc_operands[*argidx];
+ const expressionS *t = (const expressionS *) 0;
+
+ if ((operand->flags & ARC_OPERAND_FAKE)
+ && !(operand->flags & ARC_OPERAND_BRAKET))
+ continue;
+
+ if (operand->flags & ARC_OPERAND_DUPLICATE)
+ {
+ /* Duplicate operand, already inserted. */
+ tokidx ++;
+ continue;
+ }
+
+ if (tokidx >= ntok)
+ {
+ abort ();
+ }
+ else
+ t = &tok[tokidx++];
+
+ /* Regardless if we have a reloc or not mark the instruction
+ limm if it is the case. */
+ if (operand->flags & ARC_OPERAND_LIMM)
+ insn->has_limm = TRUE;
+
+ switch (t->X_op)
+ {
+ case O_register:
+ image = insert_operand (image, operand, regno (t->X_add_number),
+ NULL, 0);
+ break;
+
+ case O_constant:
+ image = insert_operand (image, operand, t->X_add_number, NULL, 0);
+ reloc_exp = t;
+ if (operand->flags & ARC_OPERAND_LIMM)
+ insn->limm = t->X_add_number;
+ break;
+
+ case O_bracket:
+ /* Ignore brackets. */
+ break;
+
+ case O_absent:
+ gas_assert (operand->flags & ARC_OPERAND_IGNORE);
+ break;
+
+ case O_subtract:
+ /* Maybe register range. */
+ if ((t->X_add_number == 0)
+ && contains_register (t->X_add_symbol)
+ && contains_register (t->X_op_symbol))
+ {
+ int regs;
+
+ regs = get_register (t->X_add_symbol);
+ regs <<= 16;
+ regs |= get_register (t->X_op_symbol);
+ image = insert_operand (image, operand, regs, NULL, 0);
+ break;
+ }
+
+ default:
+ /* This operand needs a relocation. */
+ needGOTSymbol = FALSE;
+
+ switch (t->X_md)
+ {
+ case O_plt:
+ needGOTSymbol = TRUE;
+ reloc = find_reloc ("plt", opcode->name,
+ pflags, nflg,
+ operand->default_reloc);
+ break;
+
+ case O_gotoff:
+ case O_gotpc:
+ needGOTSymbol = TRUE;
+ reloc = ARC_RELOC_TABLE (t->X_md)->reloc;
+ break;
+ case O_pcl:
+ reloc = ARC_RELOC_TABLE (t->X_md)->reloc;
+ if (ARC_SHORT (opcode->mask))
+ as_bad_where (frag_now->fr_file, frag_now->fr_line,
+ _("Unable to use @pcl relocation for insn %s"),
+ opcode->name);
+ break;
+ case O_sda:
+ reloc = find_reloc ("sda", opcode->name,
+ pflags, nflg,
+ operand->default_reloc);
+ break;
+ case O_tlsgd:
+ case O_tlsie:
+ needGOTSymbol = TRUE;
+ /* Fall-through. */
+
+ case O_tpoff:
+ case O_dtpoff:
+ reloc = ARC_RELOC_TABLE (t->X_md)->reloc;
+ break;
+
+ case O_tpoff9: /*FIXME! Check for the conditionality of
+ the insn. */
+ case O_dtpoff9: /*FIXME! Check for the conditionality of
+ the insn. */
+ as_bad (_("TLS_*_S9 relocs are not supported yet"));
+ break;
+
+ default:
+ /* Just consider the default relocation. */
+ reloc = operand->default_reloc;
+ break;
+ }
+
+ if (needGOTSymbol && (GOT_symbol == NULL))
+ GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
+
+ reloc_exp = t;
+
+#if 0
+ if (reloc > 0)
+ {
+ /* sanity checks. */
+ reloc_howto_type *reloc_howto
+ = bfd_reloc_type_lookup (stdoutput,
+ (bfd_reloc_code_real_type) reloc);
+ unsigned reloc_bitsize = reloc_howto->bitsize;
+ if (reloc_howto->rightshift)
+ reloc_bitsize -= reloc_howto->rightshift;
+ if (reloc_bitsize != operand->bits)
+ {
+ as_bad (_("invalid relocation %s for field"),
+ bfd_get_reloc_code_name (reloc));
+ return;
+ }
+ }
+#endif
+ if (insn->nfixups >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ struct arc_fixup *fixup;
+ fixup = &insn->fixups[insn->nfixups++];
+ fixup->exp = *t;
+ fixup->reloc = reloc;
+ pcrel = (operand->flags & ARC_OPERAND_PCREL) ? 1 : 0;
+ fixup->pcrel = pcrel;
+ fixup->islong = (operand->flags & ARC_OPERAND_LIMM) ?
+ TRUE : FALSE;
+ break;
+ }
+ }
+
+ /* Handle flags. */
+ for (i = 0; i < nflg; i++)
+ {
+ const struct arc_flag_operand *flg_operand =
+ &arc_flag_operands[pflags[i].code];
+
+ /* Check if the instruction has a delay slot. */
+ if (!strcmp (flg_operand->name, "d"))
+ has_delay_slot = TRUE;
+
+ /* There is an exceptional case when we cannot insert a flag
+ just as it is. The .T flag must be handled in relation with
+ the relative address. */
+ if (!strcmp (flg_operand->name, "t")
+ || !strcmp (flg_operand->name, "nt"))
+ {
+ unsigned bitYoperand = 0;
+ /* FIXME! move selection bbit/brcc in arc-opc.c. */
+ if (!strcmp (flg_operand->name, "t"))
+ if (!strcmp (opcode->name, "bbit0")
+ || !strcmp (opcode->name, "bbit1"))
+ bitYoperand = arc_NToperand;
+ else
+ bitYoperand = arc_Toperand;
+ else
+ if (!strcmp (opcode->name, "bbit0")
+ || !strcmp (opcode->name, "bbit1"))
+ bitYoperand = arc_Toperand;
+ else
+ bitYoperand = arc_NToperand;
+
+ gas_assert (reloc_exp != NULL);
+ if (reloc_exp->X_op == O_constant)
+ {
+ /* Check if we have a constant and solved it
+ immediately. */
+ offsetT val = reloc_exp->X_add_number;
+ image |= insert_operand (image, &arc_operands[bitYoperand],
+ val, NULL, 0);
+ }
+ else
+ {
+ struct arc_fixup *fixup;
+
+ if (insn->nfixups >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ fixup = &insn->fixups[insn->nfixups++];
+ fixup->exp = *reloc_exp;
+ fixup->reloc = -bitYoperand;
+ fixup->pcrel = pcrel;
+ fixup->islong = FALSE;
+ }
+ }
+ else
+ image |= (flg_operand->code & ((1 << flg_operand->bits) - 1))
+ << flg_operand->shift;
+ }
+
+ insn->relax = relax_insn_p (opcode, tok, ntok, pflags, nflg);
+
+ /* Short instruction? */
+ insn->short_insn = ARC_SHORT (opcode->mask) ? TRUE : FALSE;
+
+ insn->insn = image;
+
+ /* Update last insn status. */
+ arc_last_insns[1] = arc_last_insns[0];
+ arc_last_insns[0].opcode = opcode;
+ arc_last_insns[0].has_limm = insn->has_limm;
+ arc_last_insns[0].has_delay_slot = has_delay_slot;
+
+ /* Check if the current instruction is legally used. */
+ if (arc_last_insns[1].has_delay_slot
+ && is_br_jmp_insn_p (arc_last_insns[0].opcode))
+ as_bad_where (frag_now->fr_file, frag_now->fr_line,
+ _("A jump/branch instruction in delay slot."));
+}
+
+void
+arc_handle_align (fragS* fragP)
+{
+ if ((fragP)->fr_type == rs_align_code)
+ {
+ char *dest = (fragP)->fr_literal + (fragP)->fr_fix;
+ valueT count = ((fragP)->fr_next->fr_address
+ - (fragP)->fr_address - (fragP)->fr_fix);
+
+ (fragP)->fr_var = 2;
+
+ if (count & 1)/* Padding in the gap till the next 2-byte
+ boundary with 0s. */
+ {
+ (fragP)->fr_fix++;
+ *dest++ = 0;
+ }
+ /* Writing nop_s. */
+ md_number_to_chars (dest, NOP_OPCODE_S, 2);
+ }
+}
+
+/* Here we decide which fixups can be adjusted to make them relative
+ to the beginning of the section instead of the symbol. Basically
+ we need to make sure that the dynamic relocations are done
+ correctly, so in some cases we force the original symbol to be
+ used. */
+
+int
+tc_arc_fix_adjustable (fixS *fixP)
+{
+
+ /* Prevent all adjustments to global symbols. */
+ if (S_IS_EXTERNAL (fixP->fx_addsy))
+ return 0;
+ if (S_IS_WEAK (fixP->fx_addsy))
+ return 0;
+
+ /* Adjust_reloc_syms doesn't know about the GOT. */
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_ARC_GOTPC32:
+ case BFD_RELOC_ARC_PLT32:
+ case BFD_RELOC_ARC_S25H_PCREL_PLT:
+ case BFD_RELOC_ARC_S21H_PCREL_PLT:
+ case BFD_RELOC_ARC_S25W_PCREL_PLT:
+ case BFD_RELOC_ARC_S21W_PCREL_PLT:
+ return 0;
+
+ default:
+ break;
+ }
+
+ return 0; /* FIXME! return 1, fix it in the linker. */
+}
+
+/* Compute the reloc type of an expression EXP. */
+
+static void
+arc_check_reloc (expressionS *exp,
+ bfd_reloc_code_real_type *r_type_p)
+{
+ if (*r_type_p == BFD_RELOC_32
+ && exp->X_op == O_subtract
+ && exp->X_op_symbol != NULL
+ && exp->X_op_symbol->bsym->section == now_seg)
+ *r_type_p = BFD_RELOC_ARC_32_PCREL;
+}
+
+
+/* Add expression EXP of SIZE bytes to offset OFF of fragment FRAG. */
+
+void
+arc_cons_fix_new (fragS *frag,
+ int off,
+ int size,
+ expressionS *exp,
+ bfd_reloc_code_real_type r_type)
+{
+ r_type = BFD_RELOC_UNUSED;
+
+ switch (size)
+ {
+ case 1:
+ r_type = BFD_RELOC_8;
+ break;
+
+ case 2:
+ r_type = BFD_RELOC_16;
+ break;
+
+ case 3:
+ r_type = BFD_RELOC_24;
+ break;
+
+ case 4:
+ r_type = BFD_RELOC_32;
+ arc_check_reloc (exp, &r_type);
+ break;
+
+ case 8:
+ r_type = BFD_RELOC_64;
+ break;
+
+ default:
+ as_bad (_("unsupported BFD relocation size %u"), size);
+ r_type = BFD_RELOC_UNUSED;
+ }
+
+ fix_new_exp (frag, off, size, exp, 0, r_type);
+}
+
+/* The actual routine that checks the ZOL conditions. */
+
+static void
+check_zol (symbolS *s)
+{
+ switch (arc_mach_type)
+ {
+ case bfd_mach_arc_arcv2:
+ if (arc_target & ARC_OPCODE_ARCv2EM)
+ return;
+
+ if (is_br_jmp_insn_p (arc_last_insns[0].opcode)
+ || arc_last_insns[1].has_delay_slot)
+ as_bad (_("Jump/Branch instruction detected at the end of the ZOL label @%s"),
+ S_GET_NAME (s));
+
+ break;
+ case bfd_mach_arc_arc600:
+
+ if (is_kernel_insn_p (arc_last_insns[0].opcode))
+ as_bad (_("Kernel instruction detected at the end of the ZOL label @%s"),
+ S_GET_NAME (s));
+
+ if (arc_last_insns[0].has_limm
+ && is_br_jmp_insn_p (arc_last_insns[0].opcode))
+ as_bad (_("A jump instruction with long immediate detected at the \
+end of the ZOL label @%s"), S_GET_NAME (s));
+
+ /* Fall through. */
+ case bfd_mach_arc_arc700:
+ if (arc_last_insns[0].has_delay_slot)
+ as_bad (_("An illegal use of delay slot detected at the end of the ZOL label @%s"),
+ S_GET_NAME (s));
+
+ break;
+ default:
+ break;
+ }
+}
+
+/* If ZOL end check the last two instruction for illegals. */
+void
+arc_frob_label (symbolS * sym)
+{
+ if (ARC_GET_FLAG (sym) & ARC_FLAG_ZOL)
+ check_zol (sym);
+
+ dwarf2_emit_label (sym);
+}
+
+/* Used because generic relaxation assumes a pc-rel value whilst we
+ also relax instructions that use an absolute value resolved out of
+ relative values (if that makes any sense). An example: 'add r1,
+ r2, @.L2 - .' The symbols . and @.L2 are relative to the section
+ but if they're in the same section we can subtract the section
+ offset relocation which ends up in a resolved value. So if @.L2 is
+ .text + 0x50 and . is .text + 0x10, we can say that .text + 0x50 -
+ .text + 0x40 = 0x10. */
+int
+arc_pcrel_adjust (fragS *fragP)
+{
+ if (!fragP->tc_frag_data.pcrel)
+ return fragP->fr_address + fragP->fr_fix;
+
+ return 0;
+}
+
+/* Initialize the DWARF-2 unwind information for this procedure. */
+
+void
+tc_arc_frame_initial_instructions (void)
+{
+ /* Stack pointer is register 28. */
+ cfi_add_CFA_def_cfa_register (28);
+}
+
+int
+tc_arc_regname_to_dw2regnum (char *regname)
+{
+ struct symbol *sym;
+
+ sym = hash_find (arc_reg_hash, regname);
+ if (sym)
+ return S_GET_VALUE (sym);
+
+ return -1;
+}