From 31add5f0c96ac11ac534dbb74abf9cd989270b8e Mon Sep 17 00:00:00 2001 From: David Edelsohn Date: Fri, 10 Feb 1995 02:01:01 +0000 Subject: [PATCH] * config/tc-arc.c (md_pseudo_table): Add .cpu. (comment_chars): Add ';'. (arc_mach_type, mach_type_specified, cpu_tables_init_p): New globals. (md_parse_option): Delete support for -mmult. Add -mcpu=xxx. (md_begin): Current ARCs are little endian. Call bfd_set_arch_mach to set the cpu type. (init_opcode_tables): New function. (md_begin): Ignore suffixes and registers not supported by cpu. (md_assemble): Initialize opcode tables here. Ignore opcodes not supported by selected cpu. Always ask for more memory in one piece. (arc_cpu): New function. (md_numbers_to_chars): Support both endians (will probably be needed eventually anyway). (md_apply_fix): Likewise. --- gas/config/tc-arc.c | 467 ++++++++++++++++++++++---------------------- 1 file changed, 230 insertions(+), 237 deletions(-) diff --git a/gas/config/tc-arc.c b/gas/config/tc-arc.c index b229398a5a8..aa455920526 100644 --- a/gas/config/tc-arc.c +++ b/gas/config/tc-arc.c @@ -1,5 +1,5 @@ /* tc-arc.c -- Assembler for the ARC - Copyright (C) 1994 Free Software Foundation, Inc. + Copyright (C) 1994, 1995 Free Software Foundation, Inc. Contributed by Doug Evans (dje@cygnus.com). This file is part of GAS, the GNU Assembler. @@ -23,8 +23,10 @@ #include "as.h" #include "subsegs.h" #include "opcode/arc.h" +#include "elf/arc.h" extern int target_big_endian; +extern int arc_get_mach PARAMS ((char *)); static arc_insn arc_insert_operand PARAMS ((arc_insn insn, const struct arc_operand *operand, @@ -33,31 +35,22 @@ static arc_insn arc_insert_operand PARAMS ((arc_insn insn, offsetT val, char *file, unsigned int line)); -static void s_data1 PARAMS ((void)); -static void s_seg PARAMS ((int)); -static void s_proc PARAMS ((int)); -static void s_reserve PARAMS ((int)); -static void s_common PARAMS ((int)); +static void arc_common PARAMS ((int)); +static void arc_cpu PARAMS ((int)); +/*static void arc_rename PARAMS ((int));*/ + +static int find_mach PARAMS ((char *)); const pseudo_typeS md_pseudo_table[] = { - {"align", s_align_bytes, 0}, /* Defaulting is invalid (0) */ - {"common", s_common, 0}, - {"global", s_globl, 0}, - {"half", cons, 2}, - {"optim", s_ignore, 0}, - {"proc", s_proc, 0}, - {"reserve", s_reserve, 0}, - {"seg", s_seg, 0}, - {"skip", s_space, 0}, - {"word", cons, 4}, - {"xword", cons, 8}, - {"pushsection", obj_elf_section, 0}, - {"popsection", obj_elf_previous, 0}, - {"uahalf", cons, 2}, - {"uaword", cons, 4}, - {"uaxword", cons, 8}, - {NULL, 0, 0}, + { "align", s_align_bytes, 0 }, /* Defaulting is invalid (0) */ + { "common", arc_common, 0 }, +/*{ "hword", cons, 2 }, - already exists */ + { "word", cons, 4 }, + { "xword", cons, 8 }, + { "cpu", arc_cpu, 0 }, +/*{ "rename", arc_rename, 0 },*/ + { NULL, 0, 0 }, }; const int md_short_jump_size = 4; @@ -66,7 +59,7 @@ const int md_reloc_size = 12; /* Size of relocation record */ /* 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[] = "#"; +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' @@ -88,6 +81,16 @@ const char EXP_CHARS[] = "eE"; /* or 0d1.2345e12 */ const char FLT_CHARS[] = "rRsSfFdDxXpP"; +/* One of bfd_mach_arc_xxx. */ +static int arc_mach_type = bfd_mach_arc_base; + +/* Non-zero if the cpu type was specified on the command line. */ +static int mach_type_specified = 0; + +/* Non-zero if opcode tables have been initialized. + A .cpu command must appear before any instructions. */ +static int cpu_tables_init_p = 0; + static const char *arc_condition_codes[] = { "al", "eq", "ne", "p", "n", "c", "nc", "v", @@ -99,13 +102,11 @@ static struct hash_control *arc_suffix_hash = NULL; static struct hash_control *arc_reg_hash = NULL; const char *md_shortopts = "m:"; -struct option md_longopts[] = { - {NULL, no_argument, NULL, 0} +struct option md_longopts[] = +{ + { NULL, no_argument, NULL, 0 } }; -size_t md_longopts_size = sizeof(md_longopts); - -/* Non-zero if we accept the mul/mulu and variable shift insns. */ -static int have_mult = 0; +size_t md_longopts_size = sizeof (md_longopts); /* * md_parse_option @@ -122,14 +123,19 @@ md_parse_option (c, arg) switch (c) { case 'm': - if (strcmp (arg, "mult") == 0) - have_mult = 1; - else + if (strncmp (arg, "cpu=", 4) == 0) { - as_bad ("invalid architecture -m%s", arg); - return 0; + int mach = arc_get_mach (arg + 4); + + if (mach != -1) + { + arc_mach_type = mach; + mach_type_specified = 1; + break; + } } - break; + as_bad ("invalid architecture -m%s", arg); + return 0; default: return 0; @@ -144,24 +150,55 @@ md_show_usage (stream) { fprintf (stream, "\ ARC options:\n\ --mmult recognize the mul/mulu and variable shift instructions\n"); +-mcpu={base,host,graphics,audio} select cpu type\n"); } /* This function is called once, at assembler startup time. It should - set up all the tables, etc. that the MD part of the assembler will need. */ + set up all the tables, etc. that the MD part of the assembler will need. + Opcode selection is defered until later because we might see a .cpu + command. */ + void md_begin () { - register unsigned int i = 0; - char *last; + /* The endianness can be chosen "at the factory". One day we may have + to be bi-endian. */ + target_big_endian = 0; - target_big_endian = 1; + if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, arc_mach_type)) + as_warn ("could not set architecture and machine"); +} + +/* Initialize the various opcode and operand tables. + MACH is one of bfd_mach_arc_xxx. */ + +static void +init_opcode_tables (mach) + int mach; +{ + register unsigned int i; + char *last; + /* Indexed by bfd_mach_arc_xxx. */ + static int cpu_type_map[] = + { + ARC_CPU_BASE, + ARC_CPU_HOST, + ARC_CPU_GRAPHICS, + ARC_CPU_AUDIO, + }; if ((arc_ops_hash = hash_new ()) == NULL || (arc_suffix_hash = hash_new ()) == NULL || (arc_reg_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 (cpu_type_map[mach]); + #if 0 for (i = 0; i < arc_opcodes_count; i++) hash_insert (arc_ops_hash, arc_opcodes[i].name, (PTR) (arc_opcodes + i)); @@ -172,6 +209,8 @@ md_begin () last = ""; for (i = 0; i < arc_suffixes_count; i++) { + if (! arc_opval_supported (&arc_suffixes[i])) + continue; 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; @@ -179,10 +218,14 @@ md_begin () /* ??? This is the simple version. See tc-arm.c for something snazzier. */ for (i = 0; i < arc_reg_names_count; i++) - hash_insert (arc_reg_hash, arc_reg_names[i].name, (PTR) (arc_reg_names + i)); + { + if (! arc_opval_supported (&arc_reg_names[i])) + continue; + hash_insert (arc_reg_hash, arc_reg_names[i].name, (PTR) (arc_reg_names + i)); + } - /* This initializes a few things in arc-opc.c that we need. */ - arc_opcode_init_tables (have_mult ? ARC_HAVE_MULT_SHIFT : 0); + /* Tell `s_cpu' it's too late. */ + cpu_tables_init_p = 1; } /* Insert an operand value into an instruction. @@ -275,6 +318,15 @@ md_assemble (str) char *start; arc_insn insn; bfd_reloc_code_real_type reloc; + static int init_tables_p = 0; + + /* Opcode table initialization is deferred until here because we have to + wait for a possible .cpu command. */ + if (!init_tables_p) + { + init_opcode_tables (arc_mach_type); + init_tables_p = 1; + } /* Skip leading white space. */ while (isspace (*str)) @@ -294,6 +346,7 @@ md_assemble (str) /* Keep looking until we find a match. If we haven't found a match, and the first character no longer matches, we needn't look any further. */ + start = str; for ( ; opcode < opcode_end && *opcode->syntax == *start; ++opcode) { @@ -302,6 +355,10 @@ md_assemble (str) struct arc_fixup fixups[MAX_INSN_FIXUPS]; int fc,limm_reloc_p; + /* 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 (); @@ -583,18 +640,26 @@ md_assemble (str) if (*str != '\0') as_bad ("junk at end of line: `%s'", str); - /* Write out the instruction. */ - f = frag_more (4); - md_number_to_chars (f, insn, 4); - + /* 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 (arc_opcode_limm_p (&limm)) { - char *f2 = frag_more (4); - md_number_to_chars (f2, limm, 4); + f = frag_more (8); + md_number_to_chars (f, insn, 4); + md_number_to_chars (f + 4, limm, 4); } else if (limm_reloc_p) - /* Ahh! We need a limm reloc, but the tables think we don't. */ - abort (); + { + /* 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); + } /* Create any fixups. At this point we do not use a bfd_reloc_code_real_type, but instead just use the operand index. @@ -625,150 +690,8 @@ md_assemble (str) as_bad ("bad instruction `%s'", start); } -/* - * sort of like s_lcomm - * - */ -#ifndef OBJ_ELF -static int max_alignment = 15; -#endif - -static void -s_reserve (ignore) - int ignore; -{ - char *name; - char *p; - char c; - int align; - int size; - int temp; - symbolS *symbolP; - - name = input_line_pointer; - c = get_symbol_end (); - p = input_line_pointer; - *p = c; - SKIP_WHITESPACE (); - - if (*input_line_pointer != ',') - { - as_bad ("Expected comma after name"); - ignore_rest_of_line (); - return; - } - - ++input_line_pointer; - - if ((size = get_absolute_expression ()) < 0) - { - as_bad ("BSS length (%d.) <0! Ignored.", size); - ignore_rest_of_line (); - return; - } /* bad length */ - - *p = 0; - symbolP = symbol_find_or_make (name); - *p = c; - - if (strncmp (input_line_pointer, ",\"bss\"", 6) != 0 - && strncmp (input_line_pointer, ",\".bss\"", 7) != 0) - { - as_bad ("bad .reserve segment -- expected BSS segment"); - return; - } - - if (input_line_pointer[2] == '.') - input_line_pointer += 7; - else - input_line_pointer += 6; - SKIP_WHITESPACE (); - - if (*input_line_pointer == ',') - { - ++input_line_pointer; - - SKIP_WHITESPACE (); - if (*input_line_pointer == '\n') - { - as_bad ("Missing alignment"); - return; - } - - align = get_absolute_expression (); -#ifndef OBJ_ELF - if (align > max_alignment) - { - align = max_alignment; - as_warn ("Alignment too large: %d. assumed.", align); - } -#endif - if (align < 0) - { - align = 0; - as_warn ("Alignment negative. 0 assumed."); - } - - record_alignment (bss_section, align); - - /* convert to a power of 2 alignment */ - for (temp = 0; (align & 1) == 0; align >>= 1, ++temp);; - - if (align != 1) - { - as_bad ("Alignment not a power of 2"); - ignore_rest_of_line (); - return; - } /* not a power of two */ - - align = temp; - } /* if has optional alignment */ - else - align = 0; - - if ((S_GET_SEGMENT (symbolP) == bss_section - || !S_IS_DEFINED (symbolP)) -#ifdef OBJ_AOUT - && S_GET_OTHER (symbolP) == 0 - && S_GET_DESC (symbolP) == 0 -#endif - ) - { - if (! need_pass_2) - { - char *pfrag; - segT current_seg = now_seg; - subsegT current_subseg = now_subseg; - - subseg_set (bss_section, 1); /* switch to bss */ - - if (align) - frag_align (align, 0); /* do alignment */ - - /* 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, - size, (char *)0); - *pfrag = 0; - - S_SET_SEGMENT (symbolP, bss_section); - - subseg_set (current_seg, current_subseg); - } - } - else - { - as_warn("Ignoring attempt to re-define symbol %s.", name); - } /* if not redefining */ - - demand_empty_rest_of_line (); -} - static void -s_common (ignore) +arc_common (ignore) int ignore; { char *name; @@ -785,7 +708,7 @@ s_common (ignore) SKIP_WHITESPACE (); if (*input_line_pointer != ',') { - as_bad ("Expected comma after symbol-name"); + as_bad ("expected comma after symbol-name"); ignore_rest_of_line (); return; } @@ -802,7 +725,7 @@ s_common (ignore) *p = c; if (S_IS_DEFINED (symbolP)) { - as_bad ("Ignoring attempt to re-define symbol"); + as_bad ("ignoring attempt to re-define symbol"); ignore_rest_of_line (); return; } @@ -824,7 +747,7 @@ s_common (ignore) assert (symbolP->sy_frag == &zero_address_frag); if (*input_line_pointer != ',') { - as_bad ("Expected comma after common length"); + as_bad ("expected comma after common length"); ignore_rest_of_line (); return; } @@ -921,59 +844,105 @@ s_common (ignore) } } +/* Select the cpu we're assembling for. */ + static void -s_seg (ignore) +arc_cpu (ignore) int ignore; { + int mach; + char c; + char *cpu; + static int seen_p = 0; - if (strncmp (input_line_pointer, "\"text\"", 6) == 0) + /* Allow only one .cpu. */ + if (seen_p) { - input_line_pointer += 6; - s_text (0); - return; - } - if (strncmp (input_line_pointer, "\"data\"", 6) == 0) - { - input_line_pointer += 6; - s_data (0); + as_bad ("only one .cpu command allowed"); + ignore_rest_of_line (); return; } - if (strncmp (input_line_pointer, "\"data1\"", 7) == 0) + seen_p = 1; + + /* If an instruction has already been seen, it's too late. */ + if (cpu_tables_init_p) { - input_line_pointer += 7; - s_data1 (); + as_bad (".cpu command must appear before any instructions"); + ignore_rest_of_line (); return; } - if (strncmp (input_line_pointer, "\"bss\"", 5) == 0) + + cpu = input_line_pointer; + c = get_symbol_end (); + mach = arc_get_mach (cpu); + *input_line_pointer = c; + if (mach == -1) + goto bad_cpu; + + /* Kind of overkill but what the heck. */ + demand_empty_rest_of_line (); + + /* The cpu may have been selected on the command line. + The choices must match. */ + if (mach_type_specified && mach != arc_mach_type) + as_bad (".cpu conflicts with -mcpu flag"); + else { - input_line_pointer += 5; - /* We only support 2 segments -- text and data -- for now, so - things in the "bss segment" will have to go into data for now. - You can still allocate SEG_BSS stuff with .lcomm or .reserve. */ - subseg_set (data_section, 255); /* FIXME-SOMEDAY */ - return; + arc_mach_type = mach; + if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach)) + as_warn ("could not set architecture and machine"); } - as_bad ("Unknown segment type"); - demand_empty_rest_of_line (); -} + return; -static void -s_data1 () -{ - subseg_set (data_section, 1); - demand_empty_rest_of_line (); + bad_cpu: + as_bad ("bad .cpu op"); + ignore_rest_of_line (); } +#if 0 +/* The .rename pseudo-op. This is used by gcc to implement + -mmangle-cpu-libgcc. */ + static void -s_proc (ignore) +arc_rename (ignore) int ignore; { - while (!is_end_of_line[(unsigned char) *input_line_pointer]) + char *name,*new; + char c; + symbolS *sym; + int len; + + name = input_line_pointer; + c = get_symbol_end (); + sym = symbol_find_or_make (name); + *input_line_pointer = c; + + if (*input_line_pointer != ',') { - ++input_line_pointer; + as_bad ("missing rename string"); + ignore_rest_of_line (); + return; } ++input_line_pointer; + SKIP_WHITESPACE (); + + name = input_line_pointer; + c = get_symbol_end (); + if (*name == '\0') + { + *input_line_pointer = c; + as_bad ("invalid symbol to rename to"); + ignore_rest_of_line (); + return; + } + new = (char *) xmalloc (strlen (name) + 1); + strcpy (new, name); + *input_line_pointer = c; + sym->sy_tc.real_name = new; + + demand_empty_rest_of_line (); } +#endif /* 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 @@ -1034,7 +1003,11 @@ md_number_to_chars (buf, val, n) valueT val; int n; { - number_to_chars_bigendian (buf, val, n); + /* The ARC isn't bi-endian. Yet. */ + 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. */ @@ -1106,11 +1079,8 @@ md_pcrel_from (fixP) { if (fixP->fx_addsy != (symbolS *) NULL && ! S_IS_DEFINED (fixP->fx_addsy)) - { - /* This makes a branch to an undefined symbol be a branch to the - current location. */ - return 4; - } + /* Return offset from PC to delay slot. Offsets are from there. */ + return 4; /* Return the address of the delay slot. */ return fixP->fx_frag->fr_address + fixP->fx_where + fixP->fx_size; @@ -1181,10 +1151,16 @@ md_apply_fix (fixP, valueP) /* Fetch the instruction, insert the fully resolved operand value, and stuff the instruction back again. */ where = fixP->fx_frag->fr_literal + fixP->fx_where; - insn = bfd_getb32 ((unsigned char *) 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); - bfd_putb32 ((bfd_vma) insn, (unsigned char *) where); + if (target_big_endian) + bfd_putb32 ((bfd_vma) insn, (unsigned char *) where); + else + bfd_putl32 ((bfd_vma) insn, (unsigned char *) where); if (fixP->fx_done) { @@ -1270,3 +1246,20 @@ tc_gen_reloc (section, fixP) return reloc; } + +/* Frobbers. */ + +#if 0 +/* Set the real name if the .rename pseudo-op was used. + Return 1 if the symbol should not be included in the symbol table. */ + +int +arc_frob_symbol (sym) + symbolS *sym; +{ + if (sym->sy_tc.real_name != (char *) NULL) + S_SET_NAME (sym, sym->sy_tc.real_name); + + return 0; +} +#endif -- 2.30.2