* 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.
This commit is contained in:
David Edelsohn 1995-02-10 02:01:01 +00:00
parent 2db01c3d52
commit 31add5f0c9

View File

@ -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);
as_bad ("only one .cpu command allowed");
ignore_rest_of_line ();
return;
}
if (strncmp (input_line_pointer, "\"data\"", 6) == 0)
seen_p = 1;
/* If an instruction has already been seen, it's too late. */
if (cpu_tables_init_p)
{
input_line_pointer += 6;
s_data (0);
as_bad (".cpu command must appear before any instructions");
ignore_rest_of_line ();
return;
}
if (strncmp (input_line_pointer, "\"data1\"", 7) == 0)
{
input_line_pointer += 7;
s_data1 ();
return;
}
if (strncmp (input_line_pointer, "\"bss\"", 5) == 0)
{
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;
}
as_bad ("Unknown segment type");
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
{
arc_mach_type = mach;
if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach))
as_warn ("could not set architecture and machine");
}
return;
bad_cpu:
as_bad ("bad .cpu op");
ignore_rest_of_line ();
}
static void
s_data1 ()
{
subseg_set (data_section, 1);
demand_empty_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