@ -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 ( " E xpected comma after symbol-name" ) ;
as_bad ( " e xpected 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 ( " I gnoring attempt to re-define symbol" ) ;
as_bad ( " i gnoring 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 ( " E xpected comma after common length" ) ;
as_bad ( " e xpected 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