📄 tc-arc.c
字号:
/* tc-arc.c -- Assembler for the ARC Copyright 1994, 1995, 1997, 1999, 2000, 2001 Free Software Foundation, Inc. Contributed by Doug Evans (dje@cygnus.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) any later version. GAS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GAS; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <stdio.h>#include <ctype.h>#include "libiberty.h"#include "as.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 *));const struct suffix_classes { char *name; int len;} suffixclass[] = { { "SUFFIX_COND|SUFFIX_FLAG",23 }, { "SUFFIX_FLAG", 11 }, { "SUFFIX_COND", 11 }, { "SUFFIX_NONE", 11 }};#define MAXSUFFIXCLASS (sizeof (suffixclass) / sizeof (struct suffix_classes))const struct syntax_classes { char *name; int len; int class;} syntaxclass[] = { { "SYNTAX_3OP|OP1_MUST_BE_IMM", 26, SYNTAX_3OP|OP1_MUST_BE_IMM|SYNTAX_VALID }, { "OP1_MUST_BE_IMM|SYNTAX_3OP", 26, OP1_MUST_BE_IMM|SYNTAX_3OP|SYNTAX_VALID }, { "SYNTAX_2OP|OP1_IMM_IMPLIED", 26, SYNTAX_2OP|OP1_IMM_IMPLIED|SYNTAX_VALID }, { "OP1_IMM_IMPLIED|SYNTAX_2OP", 26, OP1_IMM_IMPLIED|SYNTAX_2OP|SYNTAX_VALID }, { "SYNTAX_3OP", 10, SYNTAX_3OP|SYNTAX_VALID }, { "SYNTAX_2OP", 10, SYNTAX_2OP|SYNTAX_VALID }};#define 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 }, { "file", dwarf2_directive_file, 0 }, { "loc", dwarf2_directive_loc, 0 }, { "extcondcode", arc_extoper, 0 }, { "extcoreregister", arc_extoper, 1 }, { "extauxregister", arc_extoper, 2 }, { "extinstruction", arc_extinst, 0 }, { NULL, 0, 0 },};/* This array holds the chars that always start a comment. If the pre-processor is disabled, these aren't very useful. */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. */const char line_comment_chars[] = "#";const char line_separator_chars[] = "";/* Chars that can be used to separate mant from exp in floating point nums. */const char EXP_CHARS[] = "eE";/* Chars that mean this number is a floating point constant As in 0f12.456 or 0d1.2345e12. */const char FLT_CHARS[] = "rRsSfFdD";/* Byte order. */extern int target_big_endian;const char *arc_target_format = DEFAULT_TARGET_FORMAT;static int byte_order = DEFAULT_BYTE_ORDER;static segT arcext_section;/* One of bfd_mach_arc_n. */static int arc_mach_type = bfd_mach_arc_6;/* Non-zero if the cpu type has been explicitly specified. */static int mach_type_specified_p = 0;/* Non-zero if opcode tables have been initialized. A .option command must appear before any instructions. */static int cpu_tables_init_p = 0;static struct hash_control *arc_suffix_hash = NULL;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 }};size_t md_longopts_size = sizeof (md_longopts);#define IS_SYMBOL_OPERAND(o) \ ((o) == 'b' || (o) == 'c' || (o) == 's' || (o) == 'o' || (o) == 'O')struct arc_operand_value *get_ext_suffix (char *s);/* Invocation line includes a switch not recognized by the base assembler. See if it's a processor-specific option. */intmd_parse_option (c, arg) int c; char *arg ATTRIBUTE_UNUSED;{ 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;}voidmd_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);}/* 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. */voidmd_begin (){ /* The endianness can be chosen "at the factory". */ target_big_endian = byte_order == BIG_ENDIAN; if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, arc_mach_type)) as_warn ("could not set architecture and machine"); /* 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));}/* Initialize the various opcode and operand tables. MACH is one of bfd_mach_arc_xxx. */static voidinit_opcode_tables (mach) int mach;{ int i; char *last; if ((arc_suffix_hash = hash_new ()) == NULL) as_fatal ("virtual memory exhausted"); if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach)) as_warn ("could not set architecture and machine"); /* 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)); /* Only put the first entry of each equivalently named suffix in the table. */ last = ""; for (i = 0; i < arc_suffixes_count; i++) { if (strcmp (arc_suffixes[i].name, last) != 0) hash_insert (arc_suffix_hash, arc_suffixes[i].name, (PTR) (arc_suffixes + i)); last = arc_suffixes[i].name; } /* 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. If the register name is already in the table, then the existing definition is assumed to be from an .ExtCoreRegister pseudo-op. */ for (i = 0; i < arc_reg_names_count; i++) { if (symbol_find (arc_reg_names[i].name)) continue; /* Use symbol_create here instead of symbol_new so we don't try to output registers into the object file's symbol table. */ symbol_table_insert (symbol_create (arc_reg_names[i].name, reg_section, (int) &arc_reg_names[i], &zero_address_frag)); } /* Tell `.option' it's too late. */ cpu_tables_init_p = 1;}/* Insert an operand value into an instruction. If REG is non-NULL, it is a register number and ignore VAL. */static arc_insnarc_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) { if ((operand->flags & ARC_OPERAND_SIGNOPT) != 0) max = (1 << operand->bits) - 1; else max = (1 << (operand->bits - 1)) - 1; min = - (1 << (operand->bits - 1)); } else { max = (1 << operand->bits) - 1; min = 0; } if ((operand->flags & ARC_OPERAND_NEGATIVE) != 0) test = - val; else test = val; if (test < (offsetT) min || test > (offsetT) max) { 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); } } if (operand->insert) { const char *errmsg; errmsg = NULL; insn = (*operand->insert) (insn, operand, mods, reg, (long) val, &errmsg); if (errmsg != (const char *) NULL) as_warn (errmsg); } else insn |= (((long) val & ((1 << operand->bits) - 1)) << operand->shift); return insn;}/* We need to keep a list of fixups. We can't simply generate them as we go, because that would require us to first create the frag, and that would screw up references to ``.''. */struct arc_fixup { /* index into `arc_operands' */ int opindex; expressionS exp;};#define MAX_FIXUPS 5#define MAX_SUFFIXES 5/* This routine is called for each instruction to be assembled. */voidmd_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) { init_opcode_tables (arc_mach_type); init_tables_p = 1; } /* Skip leading white space. */ while (isspace (*str)) str++; /* The instructions are stored in lists hashed by the first letter (though we needn't care how they're hashed). Get the first in the list. */ ext_opcode = arc_ext_opcodes; std_opcode = arc_opcode_lookup_asm (str); /* Keep looking until we find a match. */ start = str; for (opcode = (ext_opcode ? ext_opcode : std_opcode); opcode != NULL; opcode = (ARC_OPCODE_NEXT_ASM (opcode) ? ARC_OPCODE_NEXT_ASM (opcode) : (ext_opcode ? ext_opcode = NULL, std_opcode : NULL))) { int past_opcode_p, fc, num_suffixes; int fix_up_at = 0; char *syn; struct arc_fixup fixups[MAX_FIXUPS]; /* Used as a sanity check. If we need a limm reloc, make sure we ask for an extra 4 bytes from frag_more. */ int limm_reloc_p; int ext_suffix_p; const struct arc_operand_value *insn_suffixes[MAX_SUFFIXES]; /* Is this opcode supported by the selected cpu? */ if (! arc_opcode_supported (opcode)) continue; /* Scan the syntax string. If it doesn't match, try the next one. */ arc_opcode_init_insert (); insn = opcode->value; fc = 0; past_opcode_p = 0; num_suffixes = 0; limm_reloc_p = 0; ext_suffix_p = 0; /* We don't check for (*str != '\0') here because we want to parse any trailing fake arguments in the syntax string. */ for (str = start, syn = opcode->syntax; *syn != '\0';) { int mods; const struct arc_operand *operand; /* Non operand chars must match exactly. */ if (*syn != '%' || *++syn == '%') { /* 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; } /* We have an operand. Pick out any modifiers. */ mods = 0; while (ARC_MOD_P (arc_operands[arc_operand_map[(int) *syn]].flags)) { mods |= arc_operands[arc_operand_map[(int) *syn]].flags & ARC_MOD_BITS; ++syn; } operand = arc_operands + arc_operand_map[(int) *syn]; if (operand->fmt == 0) as_fatal ("unknown syntax format character `%c'", *syn); if (operand->flags & ARC_OPERAND_FAKE) { const char *errmsg = NULL; if (operand->insert) { insn = (*operand->insert) (insn, operand, mods, NULL, 0, &errmsg); if (errmsg != (const char *) NULL) { last_errmsg = errmsg; if (operand->flags & ARC_OPERAND_ERROR) { as_bad (errmsg); return; } else if (operand->flags & ARC_OPERAND_WARN) as_warn (errmsg); break; } if (limm_reloc_p && (operand->flags && operand->flags & ARC_OPERAND_LIMM) && (operand->flags & (ARC_OPERAND_ABSOLUTE_BRANCH | ARC_OPERAND_ADDRESS))) { fixups[fix_up_at].opindex = arc_operand_map[operand->fmt]; } } ++syn; } /* Are we finished with suffixes? */ else if (!past_opcode_p) { int found; char c; char *s, *t; const struct arc_operand_value *suf, *suffix_end; const struct arc_operand_value *suffix = NULL; if (!(operand->flags & ARC_OPERAND_SUFFIX)) abort (); /* If we're at a space in the input string, we want to skip the remaining suffixes. There may be some fake ones though, so
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -