diff options
Diffstat (limited to 'contrib/binutils/gas/config')
33 files changed, 22851 insertions, 3304 deletions
diff --git a/contrib/binutils/gas/config/atof-vax.c b/contrib/binutils/gas/config/atof-vax.c index 75756904..fe9f8b9 100644 --- a/contrib/binutils/gas/config/atof-vax.c +++ b/contrib/binutils/gas/config/atof-vax.c @@ -1,5 +1,5 @@ /* atof_vax.c - turn a Flonum into a VAX floating point number - Copyright 1987, 1992, 1993, 1995, 1997, 1999, 2000, 2005 + Copyright 1987, 1992, 1993, 1995, 1997, 1999, 2000, 2005, 2007 Free Software Foundation, Inc. This file is part of GAS, the GNU Assembler. @@ -35,7 +35,7 @@ int flonum_gen2vax (int, FLONUM_TYPE *, LITTLENUM_TYPE *); /* Number of chars in flonum type 'letter'. */ -static int +static unsigned int atof_vax_sizeof (int letter) { int return_value; @@ -395,7 +395,7 @@ md_atof (int what_statement_type, { LITTLENUM_TYPE words[MAXIMUM_NUMBER_OF_LITTLENUMS]; char kind_of_float; - int number_of_chars; + unsigned int number_of_chars; LITTLENUM_TYPE *littlenumP; switch (what_statement_type) diff --git a/contrib/binutils/gas/config/obj-coff.c b/contrib/binutils/gas/config/obj-coff.c index a5a76ff..1ac8a13 100644 --- a/contrib/binutils/gas/config/obj-coff.c +++ b/contrib/binutils/gas/config/obj-coff.c @@ -1026,7 +1026,7 @@ weak_name2altname (const char * name) } /* Return the name of the weak symbol corresponding to an - alterate symbol. */ + alternate symbol. */ static const char * weak_altname2name (const char * name) @@ -1579,7 +1579,7 @@ obj_coff_section (int ignore ATTRIBUTE_UNUSED) if (! load_removed) flags |= SEC_LOAD; /* Note - the READONLY flag is set here, even for the 'x' - attrbiute in order to be compatible with the MSVC + attribute in order to be compatible with the MSVC linker. */ if (! readonly_removed) flags |= SEC_READONLY; diff --git a/contrib/binutils/gas/config/obj-coff.h b/contrib/binutils/gas/config/obj-coff.h index 6fcbc9f..d2b2125 100644 --- a/contrib/binutils/gas/config/obj-coff.h +++ b/contrib/binutils/gas/config/obj-coff.h @@ -1,6 +1,6 @@ /* coff object file format Copyright 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005 + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GAS. @@ -27,8 +27,6 @@ #include "targ-cpu.h" -#include "bfd.h" - /* This internal_lineno crap is to stop namespace pollution from the bfd internal coff headerfile. */ #define internal_lineno bfd_internal_lineno @@ -57,16 +55,30 @@ #endif #ifdef TC_I386 +#ifndef TE_PEP +#include "coff/x86_64.h" +#else #include "coff/i386.h" +#endif #ifdef TE_PE +#ifdef TE_PEP +extern const char * x86_64_target_format (void); +#define TARGET_FORMAT x86_64_target_format () +#define COFF_TARGET_FORMAT "pe-x86-64" +#else #define TARGET_FORMAT "pe-i386" #endif +#endif #ifndef TARGET_FORMAT +#ifdef TE_PEP +#define TARGET_FORMAT "coff-x86-64" +#else #define TARGET_FORMAT "coff-i386" #endif #endif +#endif #ifdef TC_M68K #include "coff/m68k.h" diff --git a/contrib/binutils/gas/config/obj-elf.c b/contrib/binutils/gas/config/obj-elf.c index f922149..2f93990 100644 --- a/contrib/binutils/gas/config/obj-elf.c +++ b/contrib/binutils/gas/config/obj-elf.c @@ -1,6 +1,7 @@ /* ELF object file format Copyright 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, - 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. This file is part of GAS, the GNU Assembler. @@ -57,6 +58,10 @@ #include "elf/x86-64.h" #endif +#ifdef TC_MEP +#include "elf/mep.h" +#endif + static void obj_elf_line (int); static void obj_elf_size (int); static void obj_elf_type (int); @@ -632,6 +637,11 @@ obj_elf_change_section (const char *name, else if (attr == SHF_EXECINSTR && strcmp (name, ".note.GNU-stack") == 0) override = TRUE; +#ifdef TC_ALPHA + /* A section on Alpha may have SHF_ALPHA_GPREL. */ + else if ((attr & ~ssect->attr) == SHF_ALPHA_GPREL) + override = TRUE; +#endif else { if (group_name == NULL) @@ -1414,11 +1424,12 @@ obj_elf_version (int ignore ATTRIBUTE_UNUSED) Elf_Internal_Note i_note; Elf_External_Note e_note; asection *note_secp = NULL; - int len; SKIP_WHITESPACE (); if (*input_line_pointer == '\"') { + unsigned int len; + ++input_line_pointer; /* -> 1st char of string. */ name = input_line_pointer; @@ -1429,19 +1440,19 @@ obj_elf_version (int ignore ATTRIBUTE_UNUSED) *(input_line_pointer - 1) = '\0'; *input_line_pointer = c; - /* create the .note section */ - + /* Create the .note section. */ note_secp = subseg_new (".note", 0); bfd_set_section_flags (stdoutput, note_secp, SEC_HAS_CONTENTS | SEC_READONLY); - /* process the version string */ - - len = strlen (name); + /* Process the version string. */ + len = strlen (name) + 1; - i_note.namesz = ((len + 1) + 3) & ~3; /* round this to word boundary */ - i_note.descsz = 0; /* no description */ + /* PR 3456: Although the name field is padded out to an 4-byte + boundary, the namesz field should not be adjusted. */ + i_note.namesz = len; + i_note.descsz = 0; /* No description. */ i_note.type = NT_VERSION; p = frag_more (sizeof (e_note.namesz)); md_number_to_chars (p, i_note.namesz, sizeof (e_note.namesz)); @@ -1449,17 +1460,16 @@ obj_elf_version (int ignore ATTRIBUTE_UNUSED) md_number_to_chars (p, i_note.descsz, sizeof (e_note.descsz)); p = frag_more (sizeof (e_note.type)); md_number_to_chars (p, i_note.type, sizeof (e_note.type)); - p = frag_more (len + 1); - strcpy (p, name); + p = frag_more (len); + memcpy (p, name, len); frag_align (2, 0, 0); subseg_set (seg, subseg); } else - { - as_bad (_("expected quoted string")); - } + as_bad (_("expected quoted string")); + demand_empty_rest_of_line (); } @@ -1684,6 +1694,9 @@ adjust_stab_sections (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED) this at the moment, so we do it ourselves. We save the information in the symbol. */ +#ifdef OBJ_MAYBE_ELF +static +#endif void elf_ecoff_set_ext (symbolS *sym, struct ecoff_extr *ext) { @@ -1978,6 +1991,7 @@ elf_frob_file (void) bfd_set_section_size (stdoutput, s, size); s->contents = (unsigned char *) frag_more (size); frag_now->fr_fix = frag_now_fix_octets (); + frag_wane (frag_now); } #ifdef elf_tc_final_processing diff --git a/contrib/binutils/gas/config/obj-elf.h b/contrib/binutils/gas/config/obj-elf.h index 7ff9ef0..29356ab 100644 --- a/contrib/binutils/gas/config/obj-elf.h +++ b/contrib/binutils/gas/config/obj-elf.h @@ -1,6 +1,6 @@ /* ELF object file format. Copyright 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - 2002, 2003, 2004 Free Software Foundation, Inc. + 2002, 2003, 2004, 2006, 2007 Free Software Foundation, Inc. This file is part of GAS, the GNU Assembler. @@ -34,8 +34,6 @@ #define OUTPUT_FLAVOR bfd_target_elf_flavour #endif -#include "bfd.h" - #define BYTES_IN_WORD 4 /* for now */ #include "bfd/elf-bfd.h" @@ -83,15 +81,13 @@ struct elf_obj_sy #define OBJ_SYMFIELD_TYPE struct elf_obj_sy /* Symbol fields used by the ELF back end. */ -#define ELF_TARGET_SYMBOL_FIELDS int local:1; +#define ELF_TARGET_SYMBOL_FIELDS unsigned int local:1; /* Don't change this; change ELF_TARGET_SYMBOL_FIELDS instead. */ #ifndef TARGET_SYMBOL_FIELDS #define TARGET_SYMBOL_FIELDS ELF_TARGET_SYMBOL_FIELDS #endif -/* #include "targ-cpu.h" */ - #ifndef FALSE #define FALSE 0 #define TRUE !FALSE @@ -134,13 +130,6 @@ int elf_s_get_other (symbolS *); extern asection *gdb_section; -#ifndef obj_sec_set_private_data -#define obj_sec_set_private_data(B, S) \ - if (! BFD_SEND ((B), _new_section_hook, ((B), (S)))) \ - as_fatal (_("can't allocate ELF private section data: %s"), \ - bfd_errmsg (bfd_get_error ())) -#endif - #ifndef obj_frob_file #define obj_frob_file elf_frob_file #endif @@ -247,6 +236,7 @@ extern void elf_pop_insert (void); #endif #ifndef OBJ_MAYBE_ELF +/* If OBJ_MAYBE_ELF then obj-multi.h will define obj_ecoff_set_ext. */ #define obj_ecoff_set_ext elf_ecoff_set_ext struct ecoff_extr; extern void elf_ecoff_set_ext (symbolS *, struct ecoff_extr *); diff --git a/contrib/binutils/gas/config/obj-ieee.c b/contrib/binutils/gas/config/obj-ieee.c deleted file mode 100644 index bac4675..0000000 --- a/contrib/binutils/gas/config/obj-ieee.c +++ /dev/null @@ -1,613 +0,0 @@ -/* obj-format for ieee-695 records. - Copyright 1991, 1992, 1993, 1994, 1997, 2000, 2001, 2002, 2003, 2005 - Free Software Foundation, Inc. - - 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, 51 Franklin Street - Fifth Floor, Boston, MA - 02110-1301, USA. */ - -/* Created by Steve Chamberlain <steve@cygnus.com>. */ - -/* This will hopefully become the port through which bfd and gas talk, - for the moment, only ieee is known to work well. */ - -#include "bfd.h" -#include "as.h" -#include "subsegs.h" -#include "output-file.h" -#include "frags.h" - -bfd *abfd; - -/* How many addresses does the .align take? */ - -static relax_addressT -relax_align (address, alignment) - /* Address now. */ - register relax_addressT address; - - /* Alignment (binary). */ - register long alignment; -{ - relax_addressT mask; - relax_addressT new_address; - - mask = ~((~0) << alignment); - new_address = (address + mask) & (~mask); - return (new_address - address); -} - -/* Calculate the size of the frag chain - and create a bfd section to contain all of it. */ - -static void -size_section (abfd, idx) - bfd *abfd; - unsigned int idx; -{ - asection *sec; - unsigned int size = 0; - fragS *frag = segment_info[idx].frag_root; - - while (frag) - { - if (frag->fr_address != size) - { - printf (_("Out of step\n")); - size = frag->fr_address; - } - size += frag->fr_fix; - switch (frag->fr_type) - { - case rs_fill: - case rs_org: - size += frag->fr_offset * frag->fr_var; - break; - case rs_align: - case rs_align_code: - { - addressT off; - - off = relax_align (size, frag->fr_offset); - if (frag->fr_subtype != 0 && off > frag->fr_subtype) - off = 0; - size += off; - } - } - frag = frag->fr_next; - } - if (size) - { - char *name = segment_info[idx].name; - - if (name == (char *) NULL) - name = ".data"; - - segment_info[idx].user_stuff = - (char *) (sec = bfd_make_section (abfd, name)); - /* Make it output through itself. */ - sec->output_section = sec; - sec->flags |= SEC_HAS_CONTENTS; - bfd_set_section_size (abfd, sec, size); - } -} - -/* Run through a frag chain and write out the data to go with it. */ - -static void -fill_section (abfd, idx) - bfd *abfd; - unsigned int idx; -{ - asection *sec = segment_info[idx].user_stuff; - - if (sec) - { - fragS *frag = segment_info[idx].frag_root; - unsigned int offset = 0; - while (frag) - { - unsigned int fill_size; - unsigned int count; - switch (frag->fr_type) - { - case rs_fill: - case rs_align: - case rs_org: - if (frag->fr_fix) - { - bfd_set_section_contents (abfd, - sec, - frag->fr_literal, - frag->fr_address, - frag->fr_fix); - } - offset += frag->fr_fix; - fill_size = frag->fr_var; - if (fill_size) - { - unsigned int off = frag->fr_fix; - for (count = frag->fr_offset; count; count--) - { - bfd_set_section_contents (abfd, sec, - frag->fr_literal + - frag->fr_fix, - frag->fr_address + off, - fill_size); - off += fill_size; - } - } - break; - default: - abort (); - } - frag = frag->fr_next; - } - } -} - -/* Count the relocations in a chain. */ - -static unsigned int -count_entries_in_chain (idx) - unsigned int idx; -{ - unsigned int nrelocs; - fixS *fixup_ptr; - - /* Count the relocations. */ - fixup_ptr = segment_info[idx].fix_root; - nrelocs = 0; - while (fixup_ptr != (fixS *) NULL) - { - fixup_ptr = fixup_ptr->fx_next; - nrelocs++; - } - return nrelocs; -} - -/* Output all the relocations for a section. */ - -void -do_relocs_for (idx) - unsigned int idx; -{ - unsigned int nrelocs; - arelent **reloc_ptr_vector; - arelent *reloc_vector; - asymbol **ptrs; - asection *section = (asection *) (segment_info[idx].user_stuff); - unsigned int i; - fixS *from; - - if (section) - { - nrelocs = count_entries_in_chain (idx); - - reloc_ptr_vector = - (arelent **) malloc ((nrelocs + 1) * sizeof (arelent *)); - reloc_vector = (arelent *) malloc (nrelocs * sizeof (arelent)); - ptrs = (asymbol **) malloc (nrelocs * sizeof (asymbol *)); - from = segment_info[idx].fix_root; - for (i = 0; i < nrelocs; i++) - { - arelent *to = reloc_vector + i; - asymbol *s; - reloc_ptr_vector[i] = to; - to->howto = (reloc_howto_type *) (from->fx_r_type); - - s = &(from->fx_addsy->sy_symbol.sy); - to->address = ((char *) (from->fx_frag->fr_address + - from->fx_where)) - - ((char *) (&(from->fx_frag->fr_literal))); - to->addend = from->fx_offset; - /* If we know the symbol which we want to relocate to, turn - this reloaction into a section relative. - - If this relocation is pcrelative, and we know the - destination, we still want to keep the relocation - since - the linker might relax some of the bytes, but it stops - being pc relative and turns into an absolute relocation. */ - if (s) - { - if ((s->flags & BSF_UNDEFINED) == 0) - { - to->section = s->section; - - /* We can refer directly to the value field here, - rather than using S_GET_VALUE, because this is - only called after do_symbols, which sets up the - value field. */ - to->addend += s->value; - - to->sym_ptr_ptr = 0; - if (to->howto->pcrel_offset) - /* This is a pcrel relocation, the addend should - be adjusted. */ - to->addend -= to->address + 1; - } - else - { - to->section = 0; - *ptrs = &(from->fx_addsy->sy_symbol.sy); - to->sym_ptr_ptr = ptrs; - - if (to->howto->pcrel_offset) - /* This is a pcrel relocation, the addend should - be adjusted. */ - to->addend -= to->address - 1; - } - } - else - to->section = 0; - - ptrs++; - from = from->fx_next; - } - - /* Attach to the section. */ - section->orelocation = reloc_ptr_vector; - section->reloc_count = nrelocs; - section->flags |= SEC_LOAD; - } -} - -/* Do the symbols. */ - -static void -do_symbols (abfd) - bfd *abfd; -{ - extern symbolS *symbol_rootP; - symbolS *ptr; - asymbol **symbol_ptr_vec; - asymbol *symbol_vec; - unsigned int count = 0; - unsigned int index; - - for (ptr = symbol_rootP; - ptr != (symbolS *) NULL; - ptr = ptr->sy_next) - { - if (SEG_NORMAL (ptr->sy_symbol.seg)) - { - ptr->sy_symbol.sy.section = - (asection *) (segment_info[ptr->sy_symbol.seg].user_stuff); - S_SET_VALUE (ptr, S_GET_VALUE (ptr)); - if (ptr->sy_symbol.sy.flags == 0) - ptr->sy_symbol.sy.flags = BSF_LOCAL; - } - else - { - switch (ptr->sy_symbol.seg) - { - case SEG_ABSOLUTE: - ptr->sy_symbol.sy.flags |= BSF_ABSOLUTE; - ptr->sy_symbol.sy.section = 0; - break; - case SEG_UNKNOWN: - ptr->sy_symbol.sy.flags = BSF_UNDEFINED; - ptr->sy_symbol.sy.section = 0; - break; - default: - abort (); - } - } - ptr->sy_symbol.sy.value = S_GET_VALUE (ptr); - count++; - } - symbol_ptr_vec = (asymbol **) malloc ((count + 1) * sizeof (asymbol *)); - - index = 0; - for (ptr = symbol_rootP; - ptr != (symbolS *) NULL; - ptr = ptr->sy_next) - { - symbol_ptr_vec[index] = &(ptr->sy_symbol.sy); - index++; - } - symbol_ptr_vec[index] = 0; - abfd->outsymbols = symbol_ptr_vec; - abfd->symcount = count; -} - -/* The generic as->bfd converter. Other backends may have special case - code. */ - -void -bfd_as_write_hook () -{ - int i; - - for (i = SEG_E0; i < SEG_UNKNOWN; i++) - size_section (abfd, i); - - for (i = SEG_E0; i < SEG_UNKNOWN; i++) - fill_section (abfd, i); - - do_symbols (abfd); - - for (i = SEG_E0; i < SEG_UNKNOWN; i++) - do_relocs_for (i); -} - -S_SET_SEGMENT (x, y) - symbolS *x; - int y; -{ - x->sy_symbol.seg = y; -} - -S_IS_DEFINED (x) - symbolS *x; -{ - if (SEG_NORMAL (x->sy_symbol.seg)) - { - return 1; - } - switch (x->sy_symbol.seg) - { - case SEG_UNKNOWN: - return 0; - default: - abort (); - } -} - -S_IS_EXTERNAL (x) -{ - abort (); -} - -S_GET_DESC (x) -{ - abort (); -} - -S_GET_SEGMENT (x) - symbolS *x; -{ - return x->sy_symbol.seg; -} - -S_SET_EXTERNAL (x) - symbolS *x; -{ - x->sy_symbol.sy.flags |= BSF_GLOBAL | BSF_EXPORT; -} - -S_SET_NAME (x, y) - symbolS *x; - char *y; -{ - x->sy_symbol.sy.name = y; -} - -S_GET_OTHER (x) -{ - abort (); -} - -S_IS_DEBUG (x) -{ - abort (); -} - -#ifndef segment_name -char * -segment_name () -{ - abort (); -} -#endif - -void -obj_read_begin_hook () -{ -} - -static void -obj_ieee_section (ignore) - int ignore; -{ - extern char *input_line_pointer; - extern char is_end_of_line[]; - char *p = input_line_pointer; - char *s = p; - int i; - - /* Look up the name, if it doesn't exist, make it. */ - while (*p && *p != ' ' && *p != ',' && !is_end_of_line[*p]) - { - p++; - } - for (i = SEG_E0; i < SEG_UNKNOWN; i++) - { - if (segment_info[i].hadone) - { - if (strncmp (segment_info[i].name, s, p - s) == 0) - goto ok; - } - else - break; - } - if (i == SEG_UNKNOWN) - { - as_bad (_("too many sections")); - return; - } - - segment_info[i].hadone = 1; - segment_info[i].name = malloc (p - s + 1); - memcpy (segment_info[i].name, s, p - s); - segment_info[i].name[p - s] = 0; -ok: - subseg_set (i, 0); - while (!is_end_of_line[*p]) - p++; - input_line_pointer = p; -} - -const pseudo_typeS obj_pseudo_table[] = -{ - {"section", obj_ieee_section, 0}, - {"data.b" , cons , 1}, - {"data.w" , cons , 2}, - {"data.l" , cons , 4}, - {"export" , s_globl , 0}, - {"option" , s_ignore , 0}, - {"end" , s_ignore , 0}, - {"import" , s_ignore , 0}, - {"sdata" , stringer , 0}, - 0, -}; - -void -obj_symbol_new_hook (symbolP) - symbolS *symbolP; -{ - symbolP->sy_symbol.sy.the_bfd = abfd; -} - -#if 1 - -#ifndef SUB_SEGMENT_ALIGN -#ifdef HANDLE_ALIGN -/* The last subsegment gets an alignment corresponding to the alignment - of the section. This allows proper nop-filling at the end of - code-bearing sections. */ -#define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) \ - (!(FRCHAIN)->frch_next || (FRCHAIN)->frch_next->frch_seg != (SEG) \ - ? get_recorded_alignment (SEG) : 0) -#else -#define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) 2 -#endif -#endif - -extern void -write_object_file () -{ - int i; - struct frchain *frchain_ptr; - struct frag *frag_ptr; - - abfd = bfd_openw (out_file_name, "ieee"); - - if (abfd == 0) - { - as_perror (_("FATAL: Can't create %s"), out_file_name); - exit (EXIT_FAILURE); - } - bfd_set_format (abfd, bfd_object); - bfd_set_arch_mach (abfd, bfd_arch_h8300, 0); - subseg_set (1, 0); - subseg_set (2, 0); - subseg_set (3, 0); - - /* Run through all the sub-segments and align them up. Also - close any open frags. We tack a .fill onto the end of the - frag chain so that any .align's size can be worked by looking - at the next frag. */ - for (frchain_ptr = frchain_root; - frchain_ptr != (struct frchain *) NULL; - frchain_ptr = frchain_ptr->frch_next) - { - int alignment; - - subseg_set (frchain_ptr->frch_seg, frchain_ptr->frch_subseg); - - alignment = SUB_SEGMENT_ALIGN (now_seg, frchain_ptr) - -#ifdef md_do_align - md_do_align (alignment, (char *) NULL, 0, 0, alignment_done); -#endif - if (subseg_text_p (now_seg)) - frag_align_code (alignment, 0); - else - frag_align (alignment, 0, 0); - -#ifdef md_do_align - alignment_done: -#endif - - frag_wane (frag_now); - frag_now->fr_fix = 0; - know (frag_now->fr_next == NULL); - } - - /* Now build one big frag chain for each segment, linked through - fr_next. */ - for (i = SEG_E0; i < SEG_UNKNOWN; i++) - { - fragS **prev_frag_ptr_ptr; - struct frchain *next_frchain_ptr; - - segment_info[i].frag_root = segment_info[i].frchainP->frch_root; - } - - for (i = SEG_E0; i < SEG_UNKNOWN; i++) - relax_segment (segment_info[i].frag_root, i); - - /* Relaxation has completed. Freeze all syms. */ - finalize_syms = 1; - - /* Now the addresses of the frags are correct within the segment. */ - - bfd_as_write_hook (); - bfd_close (abfd); -} - -#endif - -H_SET_TEXT_SIZE (a, b) -{ - abort (); -} - -H_GET_TEXT_SIZE () -{ - abort (); -} - -H_SET_BSS_SIZE () -{ - abort (); -} - -H_SET_STRING_SIZE () -{ - abort (); -} - -H_SET_RELOCATION_SIZE () -{ - abort (); -} - -H_SET_MAGIC_NUMBER () -{ - abort (); -} - -H_GET_FILE_SIZE () -{ - abort (); -} - -H_GET_TEXT_RELOCATION_SIZE () -{ - abort (); -} diff --git a/contrib/binutils/gas/config/obj-ieee.h b/contrib/binutils/gas/config/obj-ieee.h deleted file mode 100644 index 2965429..0000000 --- a/contrib/binutils/gas/config/obj-ieee.h +++ /dev/null @@ -1,46 +0,0 @@ -/* This file is obj-ieee.h - Copyright 1987, 1988, 1989, 1990, 1991, 1992, 2000, 2002, 2003 - Free Software Foundation, Inc. - - 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, 51 Franklin Street - Fifth Floor, Boston, MA - 02110-1301, USA. */ - -#include "bfd.h" - -typedef struct -{ - asymbol sy; - int seg; -} -obj_symbol_type; - -#define S_GET_NAME(s) (((s)->sy_symbol.sy.name)) - -/* Return true for symbols that should not be reduced to section - symbols or eliminated from expressions, because they may be - overridden by the linker. */ -#define S_FORCE_RELOC(s, strict) (!SEG_NORMAL (x->sy_symbol.seg)) - -typedef struct - { - int x; - } -object_headers; - -int lineno_rootP; - -#define IEEE_STYLE diff --git a/contrib/binutils/gas/config/tc-alpha.c b/contrib/binutils/gas/config/tc-alpha.c index 3765b08..e8f0bad 100644 --- a/contrib/binutils/gas/config/tc-alpha.c +++ b/contrib/binutils/gas/config/tc-alpha.c @@ -1,6 +1,6 @@ /* tc-alpha.c - Processor-specific code for the DEC Alpha AXP CPU. Copyright 1989, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, - 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + 2001, 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc. Contributed by Carnegie Mellon University, 1993. Written by Alessandro Forin, based on earlier gas-1.38 target CPU files. Modified by Ken Raeburn for gas-2.x and ECOFF support. @@ -2383,15 +2383,15 @@ emit_ustX (const expressionS *tok, newtok[2] = newtok[0]; assemble_tokens ("or", newtok, 3, 1); - /* Emit "stq_u $t9, 0($at)". */ - set_tok_reg (newtok[0], AXP_REG_T9); - set_tok_const (newtok[1], 0); - set_tok_preg (newtok[2], AXP_REG_AT); - assemble_tokens ("stq_u", newtok, 3, 1); - /* Emit "stq_u $t10, size-1($at)". */ set_tok_reg (newtok[0], AXP_REG_T10); set_tok_const (newtok[1], (1 << lgsize) - 1); + set_tok_preg (newtok[2], AXP_REG_AT); + assemble_tokens ("stq_u", newtok, 3, 1); + + /* Emit "stq_u $t9, 0($at)". */ + set_tok_reg (newtok[0], AXP_REG_T9); + set_tok_const (newtok[1], 0); assemble_tokens ("stq_u", newtok, 3, 1); } diff --git a/contrib/binutils/gas/config/tc-alpha.h b/contrib/binutils/gas/config/tc-alpha.h index 42e004e..d28ab9f 100644 --- a/contrib/binutils/gas/config/tc-alpha.h +++ b/contrib/binutils/gas/config/tc-alpha.h @@ -1,6 +1,6 @@ /* This file is tc-alpha.h Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 - 2005 + 2005, 2006 Free Software Foundation, Inc. Written by Ken Raeburn <raeburn@cygnus.com>. @@ -180,4 +180,4 @@ extern void alpha_cfi_frame_initial_instructions (void); #define DWARF2_LINE_MIN_INSN_LENGTH 4 #define DWARF2_DEFAULT_RETURN_COLUMN 26 -#define DWARF2_CIE_DATA_ALIGNMENT -8 +#define DWARF2_CIE_DATA_ALIGNMENT (-8) diff --git a/contrib/binutils/gas/config/tc-arc.c b/contrib/binutils/gas/config/tc-arc.c index 525b540..490f327 100644 --- a/contrib/binutils/gas/config/tc-arc.c +++ b/contrib/binutils/gas/config/tc-arc.c @@ -20,8 +20,6 @@ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -#include <stdio.h> -#include "libiberty.h" #include "as.h" #include "struc-symbol.h" #include "safe-ctype.h" diff --git a/contrib/binutils/gas/config/tc-arm.c b/contrib/binutils/gas/config/tc-arm.c index ae420b3..683e6ee 100644 --- a/contrib/binutils/gas/config/tc-arm.c +++ b/contrib/binutils/gas/config/tc-arm.c @@ -1,6 +1,6 @@ /* tc-arm.c -- Assemble for the ARM Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, - 2004, 2005 + 2004, 2005, 2006 Free Software Foundation, Inc. Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org) Modified by David Taylor (dtaylor@armltd.co.uk) @@ -25,28 +25,24 @@ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -#include <string.h> +#include <limits.h> +#include <stdarg.h> #define NO_RELOC 0 #include "as.h" #include "safe-ctype.h" - -/* Need TARGET_CPU. */ -#include "config.h" #include "subsegs.h" #include "obstack.h" -#include "symbols.h" -#include "listing.h" #include "opcode/arm.h" #ifdef OBJ_ELF #include "elf/arm.h" -#include "dwarf2dbg.h" #include "dw2gencfi.h" #endif -/* XXX Set this to 1 after the next binutils release. */ -#define WARN_DEPRECATED 0 +#include "dwarf2dbg.h" + +#define WARN_DEPRECATED 1 #ifdef OBJ_ELF /* Must be at least the size of the largest unwind opcode (currently two). */ @@ -90,6 +86,15 @@ static unsigned int marked_pr_dependency = 0; #endif /* OBJ_ELF */ +/* Results from operand parsing worker functions. */ + +typedef enum +{ + PARSE_OPERAND_SUCCESS, + PARSE_OPERAND_FAIL, + PARSE_OPERAND_FAIL_NO_BACKTRACK +} parse_operand_result; + enum arm_float_abi { ARM_FLOAT_ABI_HARD, @@ -150,11 +155,14 @@ static const arm_feature_set *mcpu_fpu_opt = NULL; static const arm_feature_set *march_cpu_opt = NULL; static const arm_feature_set *march_fpu_opt = NULL; static const arm_feature_set *mfpu_opt = NULL; +static const arm_feature_set *object_arch = NULL; /* Constants for known architecture features. */ static const arm_feature_set fpu_default = FPU_DEFAULT; static const arm_feature_set fpu_arch_vfp_v1 = FPU_ARCH_VFP_V1; static const arm_feature_set fpu_arch_vfp_v2 = FPU_ARCH_VFP_V2; +static const arm_feature_set fpu_arch_vfp_v3 = FPU_ARCH_VFP_V3; +static const arm_feature_set fpu_arch_neon_v1 = FPU_ARCH_NEON_V1; static const arm_feature_set fpu_arch_fpa = FPU_ARCH_FPA; static const arm_feature_set fpu_any_hard = FPU_ANY_HARD; static const arm_feature_set fpu_arch_maverick = FPU_ARCH_MAVERICK; @@ -194,6 +202,8 @@ static const arm_feature_set arm_arch_full = ARM_FEATURE (-1, -1); static const arm_feature_set arm_arch_t2 = ARM_ARCH_THUMB2; static const arm_feature_set arm_arch_none = ARM_ARCH_NONE; +static const arm_feature_set arm_cext_iwmmxt2 = + ARM_FEATURE (0, ARM_CEXT_IWMMXT2); static const arm_feature_set arm_cext_iwmmxt = ARM_FEATURE (0, ARM_CEXT_IWMMXT); static const arm_feature_set arm_cext_xscale = @@ -206,6 +216,10 @@ static const arm_feature_set fpu_vfp_ext_v1xd = ARM_FEATURE (0, FPU_VFP_EXT_V1xD); static const arm_feature_set fpu_vfp_ext_v1 = ARM_FEATURE (0, FPU_VFP_EXT_V1); static const arm_feature_set fpu_vfp_ext_v2 = ARM_FEATURE (0, FPU_VFP_EXT_V2); +static const arm_feature_set fpu_vfp_ext_v3 = ARM_FEATURE (0, FPU_VFP_EXT_V3); +static const arm_feature_set fpu_neon_ext_v1 = ARM_FEATURE (0, FPU_NEON_EXT_V1); +static const arm_feature_set fpu_vfp_v3_or_neon_ext = + ARM_FEATURE (0, FPU_NEON_EXT_V1 | FPU_VFP_EXT_V3); static int mfloat_abi_opt = -1; /* Record user cpu selection for object attributes. */ @@ -218,6 +232,12 @@ static int meabi_flags = EABI_DEFAULT; # else static int meabi_flags = EF_ARM_EABI_UNKNOWN; # endif + +bfd_boolean +arm_is_eabi(void) +{ + return (EF_ARM_EABI_VERSION (meabi_flags) >= EF_ARM_EABI_VER4); +} #endif #ifdef OBJ_ELF @@ -256,6 +276,31 @@ static int thumb_mode = 0; static bfd_boolean unified_syntax = FALSE; +enum neon_el_type +{ + NT_invtype, + NT_untyped, + NT_integer, + NT_float, + NT_poly, + NT_signed, + NT_unsigned +}; + +struct neon_type_el +{ + enum neon_el_type type; + unsigned size; +}; + +#define NEON_MAX_TYPE_ELS 4 + +struct neon_type +{ + struct neon_type_el el[NEON_MAX_TYPE_ELS]; + unsigned elems; +}; + struct arm_it { const char * error; @@ -263,6 +308,11 @@ struct arm_it int size; int size_req; int cond; + /* "uncond_value" is set to the value in place of the conditional field in + unconditional versions of the instruction, or -1 if nothing is + appropriate. */ + int uncond_value; + struct neon_type vectype; /* Set to the opcode if the instruction needs relaxation. Zero if the instruction is not relaxed. */ unsigned long relax; @@ -277,9 +327,19 @@ struct arm_it { unsigned reg; signed int imm; + struct neon_type_el vectype; unsigned present : 1; /* Operand present. */ unsigned isreg : 1; /* Operand was a register. */ unsigned immisreg : 1; /* .imm field is a second register. */ + unsigned isscalar : 1; /* Operand is a (Neon) scalar. */ + unsigned immisalign : 1; /* Immediate is an alignment specifier. */ + unsigned immisfloat : 1; /* Immediate was parsed as a float. */ + /* Note: we abuse "regisimm" to mean "is Neon register" in VMOV + instructions. This allows us to disambiguate ARM <-> vector insns. */ + unsigned regisimm : 1; /* 64-bit immediate, reg forms high 32 bits. */ + unsigned isvec : 1; /* Is a single, double or quad VFP/Neon reg. */ + unsigned isquad : 1; /* Operand is Neon quad-precision register. */ + unsigned issingle : 1; /* Operand is VFP single-precision register. */ unsigned hasreloc : 1; /* Operand has relocation suffix. */ unsigned writeback : 1; /* Operand has trailing ! */ unsigned preind : 1; /* Preindexed address. */ @@ -355,9 +415,10 @@ struct reloc_entry bfd_reloc_code_real_type reloc; }; -enum vfp_sp_reg_pos +enum vfp_reg_pos { - VFP_REG_Sd, VFP_REG_Sm, VFP_REG_Sn + VFP_REG_Sd, VFP_REG_Sm, VFP_REG_Sn, + VFP_REG_Dd, VFP_REG_Dm, VFP_REG_Dn }; enum vfp_ldstm_type @@ -365,6 +426,17 @@ enum vfp_ldstm_type VFP_LDSTMIA, VFP_LDSTMDB, VFP_LDSTMIAX, VFP_LDSTMDBX }; +/* Bits for DEFINED field in neon_typed_alias. */ +#define NTA_HASTYPE 1 +#define NTA_HASINDEX 2 + +struct neon_typed_alias +{ + unsigned char defined; + unsigned char index; + struct neon_type_el eltype; +}; + /* ARM register categories. This includes coprocessor numbers and various architecture extensions' registers. */ enum arm_reg_type @@ -375,6 +447,10 @@ enum arm_reg_type REG_TYPE_FN, REG_TYPE_VFS, REG_TYPE_VFD, + REG_TYPE_NQ, + REG_TYPE_VFSD, + REG_TYPE_NDQ, + REG_TYPE_NSDQ, REG_TYPE_VFC, REG_TYPE_MVF, REG_TYPE_MVD, @@ -388,13 +464,17 @@ enum arm_reg_type REG_TYPE_XSCALE, }; -/* Structure for a hash table entry for a register. */ +/* Structure for a hash table entry for a register. + If TYPE is REG_TYPE_VFD or REG_TYPE_NQ, the NEON field can point to extra + information which states whether a vector type or index is specified (for a + register alias created with .dn or .qn). Otherwise NEON should be NULL. */ struct reg_entry { - const char *name; - unsigned char number; - unsigned char type; - unsigned char builtin; + const char *name; + unsigned char number; + unsigned char type; + unsigned char builtin; + struct neon_typed_alias *neon; }; /* Diagnostics used when we don't get a register of the expected type. */ @@ -405,7 +485,11 @@ const char *const reg_expected_msgs[] = N_("co-processor register expected"), N_("FPA register expected"), N_("VFP single precision register expected"), - N_("VFP double precision register expected"), + N_("VFP/Neon double precision register expected"), + N_("Neon quad precision register expected"), + N_("VFP single or double precision register expected"), + N_("Neon double or quad precision register expected"), + N_("VFP single, double or Neon quad precision register expected"), N_("VFP system register expected"), N_("Maverick MVF register expected"), N_("Maverick MVD register expected"), @@ -465,11 +549,14 @@ struct asm_opcode #define INDEX_UP 0x00800000 #define WRITE_BACK 0x00200000 #define LDM_TYPE_2_OR_3 0x00400000 +#define CPSI_MMOD 0x00020000 #define LITERAL_MASK 0xf000f000 #define OPCODE_MASK 0xfe1fffff #define V4_STR_BIT 0x00000020 +#define T2_SUBS_PC_LR 0xf3de8f00 + #define DATA_OP_SHIFT 21 #define T2_OPCODE_MASK 0xfe1fffff @@ -571,6 +658,7 @@ struct asm_opcode #define BAD_ADDR_MODE _("instruction does not accept this addressing mode"); #define BAD_BRANCH _("branch must be last instruction in IT block") #define BAD_NOT_IT _("instruction not allowed in IT block") +#define BAD_FPU _("selected FPU does not support instruction") static struct hash_control *arm_ops_hsh; static struct hash_control *arm_cond_hsh; @@ -691,6 +779,9 @@ static int in_my_get_expression = 0; #define GE_NO_PREFIX 0 #define GE_IMM_PREFIX 1 #define GE_OPT_PREFIX 2 +/* This is a bit of a hack. Use an optional prefix, and also allow big (64-bit) + immediates, as can be used in Neon VMVN and VMOV immediate instructions. */ +#define GE_OPT_PREFIX_BIG 3 static int my_get_expression (expressionS * ep, char ** str, int prefix_mode) @@ -700,7 +791,8 @@ my_get_expression (expressionS * ep, char ** str, int prefix_mode) /* In unified syntax, all prefixes are optional. */ if (unified_syntax) - prefix_mode = GE_OPT_PREFIX; + prefix_mode = (prefix_mode == GE_OPT_PREFIX_BIG) ? prefix_mode + : GE_OPT_PREFIX; switch (prefix_mode) { @@ -714,6 +806,7 @@ my_get_expression (expressionS * ep, char ** str, int prefix_mode) (*str)++; break; case GE_OPT_PREFIX: + case GE_OPT_PREFIX_BIG: if (is_immediate_prefix (**str)) (*str)++; break; @@ -755,11 +848,12 @@ my_get_expression (expressionS * ep, char ** str, int prefix_mode) /* Get rid of any bignums now, so that we don't generate an error for which we can't establish a line number later on. Big numbers are never valid in instructions, which is where this routine is always called. */ - if (ep->X_op == O_big - || (ep->X_add_symbol - && (walk_no_bignums (ep->X_add_symbol) - || (ep->X_op_symbol - && walk_no_bignums (ep->X_op_symbol))))) + if (prefix_mode != GE_OPT_PREFIX_BIG + && (ep->X_op == O_big + || (ep->X_add_symbol + && (walk_no_bignums (ep->X_add_symbol) + || (ep->X_op_symbol + && walk_no_bignums (ep->X_op_symbol)))))) { inst.error = _("invalid constant"); *str = input_line_pointer; @@ -939,18 +1033,10 @@ arm_reg_parse_multi (char **ccp) return reg; } -/* As above, but the register must be of type TYPE, and the return - value is the register number or FAIL. */ - static int -arm_reg_parse (char **ccp, enum arm_reg_type type) +arm_reg_alt_syntax (char **ccp, char *start, struct reg_entry *reg, + enum arm_reg_type type) { - char *start = *ccp; - struct reg_entry *reg = arm_reg_parse_multi (ccp); - - if (reg && reg->type == type) - return reg->number; - /* Alternative syntaxes are accepted for a few register classes. */ switch (type) { @@ -982,10 +1068,354 @@ arm_reg_parse (char **ccp, enum arm_reg_type type) break; } + return FAIL; +} + +/* As arm_reg_parse_multi, but the register must be of type TYPE, and the + return value is the register number or FAIL. */ + +static int +arm_reg_parse (char **ccp, enum arm_reg_type type) +{ + char *start = *ccp; + struct reg_entry *reg = arm_reg_parse_multi (ccp); + int ret; + + /* Do not allow a scalar (reg+index) to parse as a register. */ + if (reg && reg->neon && (reg->neon->defined & NTA_HASINDEX)) + return FAIL; + + if (reg && reg->type == type) + return reg->number; + + if ((ret = arm_reg_alt_syntax (ccp, start, reg, type)) != FAIL) + return ret; + *ccp = start; return FAIL; } +/* Parse a Neon type specifier. *STR should point at the leading '.' + character. Does no verification at this stage that the type fits the opcode + properly. E.g., + + .i32.i32.s16 + .s32.f32 + .u16 + + Can all be legally parsed by this function. + + Fills in neon_type struct pointer with parsed information, and updates STR + to point after the parsed type specifier. Returns SUCCESS if this was a legal + type, FAIL if not. */ + +static int +parse_neon_type (struct neon_type *type, char **str) +{ + char *ptr = *str; + + if (type) + type->elems = 0; + + while (type->elems < NEON_MAX_TYPE_ELS) + { + enum neon_el_type thistype = NT_untyped; + unsigned thissize = -1u; + + if (*ptr != '.') + break; + + ptr++; + + /* Just a size without an explicit type. */ + if (ISDIGIT (*ptr)) + goto parsesize; + + switch (TOLOWER (*ptr)) + { + case 'i': thistype = NT_integer; break; + case 'f': thistype = NT_float; break; + case 'p': thistype = NT_poly; break; + case 's': thistype = NT_signed; break; + case 'u': thistype = NT_unsigned; break; + case 'd': + thistype = NT_float; + thissize = 64; + ptr++; + goto done; + default: + as_bad (_("unexpected character `%c' in type specifier"), *ptr); + return FAIL; + } + + ptr++; + + /* .f is an abbreviation for .f32. */ + if (thistype == NT_float && !ISDIGIT (*ptr)) + thissize = 32; + else + { + parsesize: + thissize = strtoul (ptr, &ptr, 10); + + if (thissize != 8 && thissize != 16 && thissize != 32 + && thissize != 64) + { + as_bad (_("bad size %d in type specifier"), thissize); + return FAIL; + } + } + + done: + if (type) + { + type->el[type->elems].type = thistype; + type->el[type->elems].size = thissize; + type->elems++; + } + } + + /* Empty/missing type is not a successful parse. */ + if (type->elems == 0) + return FAIL; + + *str = ptr; + + return SUCCESS; +} + +/* Errors may be set multiple times during parsing or bit encoding + (particularly in the Neon bits), but usually the earliest error which is set + will be the most meaningful. Avoid overwriting it with later (cascading) + errors by calling this function. */ + +static void +first_error (const char *err) +{ + if (!inst.error) + inst.error = err; +} + +/* Parse a single type, e.g. ".s32", leading period included. */ +static int +parse_neon_operand_type (struct neon_type_el *vectype, char **ccp) +{ + char *str = *ccp; + struct neon_type optype; + + if (*str == '.') + { + if (parse_neon_type (&optype, &str) == SUCCESS) + { + if (optype.elems == 1) + *vectype = optype.el[0]; + else + { + first_error (_("only one type should be specified for operand")); + return FAIL; + } + } + else + { + first_error (_("vector type expected")); + return FAIL; + } + } + else + return FAIL; + + *ccp = str; + + return SUCCESS; +} + +/* Special meanings for indices (which have a range of 0-7), which will fit into + a 4-bit integer. */ + +#define NEON_ALL_LANES 15 +#define NEON_INTERLEAVE_LANES 14 + +/* Parse either a register or a scalar, with an optional type. Return the + register number, and optionally fill in the actual type of the register + when multiple alternatives were given (NEON_TYPE_NDQ) in *RTYPE, and + type/index information in *TYPEINFO. */ + +static int +parse_typed_reg_or_scalar (char **ccp, enum arm_reg_type type, + enum arm_reg_type *rtype, + struct neon_typed_alias *typeinfo) +{ + char *str = *ccp; + struct reg_entry *reg = arm_reg_parse_multi (&str); + struct neon_typed_alias atype; + struct neon_type_el parsetype; + + atype.defined = 0; + atype.index = -1; + atype.eltype.type = NT_invtype; + atype.eltype.size = -1; + + /* Try alternate syntax for some types of register. Note these are mutually + exclusive with the Neon syntax extensions. */ + if (reg == NULL) + { + int altreg = arm_reg_alt_syntax (&str, *ccp, reg, type); + if (altreg != FAIL) + *ccp = str; + if (typeinfo) + *typeinfo = atype; + return altreg; + } + + /* Undo polymorphism when a set of register types may be accepted. */ + if ((type == REG_TYPE_NDQ + && (reg->type == REG_TYPE_NQ || reg->type == REG_TYPE_VFD)) + || (type == REG_TYPE_VFSD + && (reg->type == REG_TYPE_VFS || reg->type == REG_TYPE_VFD)) + || (type == REG_TYPE_NSDQ + && (reg->type == REG_TYPE_VFS || reg->type == REG_TYPE_VFD + || reg->type == REG_TYPE_NQ)) + || (type == REG_TYPE_MMXWC + && (reg->type == REG_TYPE_MMXWCG))) + type = reg->type; + + if (type != reg->type) + return FAIL; + + if (reg->neon) + atype = *reg->neon; + + if (parse_neon_operand_type (&parsetype, &str) == SUCCESS) + { + if ((atype.defined & NTA_HASTYPE) != 0) + { + first_error (_("can't redefine type for operand")); + return FAIL; + } + atype.defined |= NTA_HASTYPE; + atype.eltype = parsetype; + } + + if (skip_past_char (&str, '[') == SUCCESS) + { + if (type != REG_TYPE_VFD) + { + first_error (_("only D registers may be indexed")); + return FAIL; + } + + if ((atype.defined & NTA_HASINDEX) != 0) + { + first_error (_("can't change index for operand")); + return FAIL; + } + + atype.defined |= NTA_HASINDEX; + + if (skip_past_char (&str, ']') == SUCCESS) + atype.index = NEON_ALL_LANES; + else + { + expressionS exp; + + my_get_expression (&exp, &str, GE_NO_PREFIX); + + if (exp.X_op != O_constant) + { + first_error (_("constant expression required")); + return FAIL; + } + + if (skip_past_char (&str, ']') == FAIL) + return FAIL; + + atype.index = exp.X_add_number; + } + } + + if (typeinfo) + *typeinfo = atype; + + if (rtype) + *rtype = type; + + *ccp = str; + + return reg->number; +} + +/* Like arm_reg_parse, but allow allow the following extra features: + - If RTYPE is non-zero, return the (possibly restricted) type of the + register (e.g. Neon double or quad reg when either has been requested). + - If this is a Neon vector type with additional type information, fill + in the struct pointed to by VECTYPE (if non-NULL). + This function will fault on encountering a scalar. +*/ + +static int +arm_typed_reg_parse (char **ccp, enum arm_reg_type type, + enum arm_reg_type *rtype, struct neon_type_el *vectype) +{ + struct neon_typed_alias atype; + char *str = *ccp; + int reg = parse_typed_reg_or_scalar (&str, type, rtype, &atype); + + if (reg == FAIL) + return FAIL; + + /* Do not allow a scalar (reg+index) to parse as a register. */ + if ((atype.defined & NTA_HASINDEX) != 0) + { + first_error (_("register operand expected, but got scalar")); + return FAIL; + } + + if (vectype) + *vectype = atype.eltype; + + *ccp = str; + + return reg; +} + +#define NEON_SCALAR_REG(X) ((X) >> 4) +#define NEON_SCALAR_INDEX(X) ((X) & 15) + +/* Parse a Neon scalar. Most of the time when we're parsing a scalar, we don't + have enough information to be able to do a good job bounds-checking. So, we + just do easy checks here, and do further checks later. */ + +static int +parse_scalar (char **ccp, int elsize, struct neon_type_el *type) +{ + int reg; + char *str = *ccp; + struct neon_typed_alias atype; + + reg = parse_typed_reg_or_scalar (&str, REG_TYPE_VFD, NULL, &atype); + + if (reg == FAIL || (atype.defined & NTA_HASINDEX) == 0) + return FAIL; + + if (atype.index == NEON_ALL_LANES) + { + first_error (_("scalar must have an index")); + return FAIL; + } + else if (atype.index >= 64 / elsize) + { + first_error (_("scalar index out of range")); + return FAIL; + } + + if (type) + *type = atype.eltype; + + *ccp = str; + + return reg * 16 + atype.index; +} + /* Parse an ARM register list. Returns the bitmask, or FAIL. */ static long parse_reg_list (char ** strp) @@ -1011,7 +1441,7 @@ parse_reg_list (char ** strp) if ((reg = arm_reg_parse (&str, REG_TYPE_RN)) == FAIL) { - inst.error = _(reg_expected_msgs[REG_TYPE_RN]); + first_error (_(reg_expected_msgs[REG_TYPE_RN])); return FAIL; } @@ -1021,7 +1451,7 @@ parse_reg_list (char ** strp) if (reg <= cur_reg) { - inst.error = _("bad range in register list"); + first_error (_("bad range in register list")); return FAIL; } @@ -1052,7 +1482,7 @@ parse_reg_list (char ** strp) if (*str++ != '}') { - inst.error = _("missing `}'"); + first_error (_("missing `}'")); return FAIL; } } @@ -1111,55 +1541,117 @@ parse_reg_list (char ** strp) return range; } +/* Types of registers in a list. */ + +enum reg_list_els +{ + REGLIST_VFP_S, + REGLIST_VFP_D, + REGLIST_NEON_D +}; + /* Parse a VFP register list. If the string is invalid return FAIL. Otherwise return the number of registers, and set PBASE to the first - register. Double precision registers are matched if DP is nonzero. */ + register. Parses registers of type ETYPE. + If REGLIST_NEON_D is used, several syntax enhancements are enabled: + - Q registers can be used to specify pairs of D registers + - { } can be omitted from around a singleton register list + FIXME: This is not implemented, as it would require backtracking in + some cases, e.g.: + vtbl.8 d3,d4,d5 + This could be done (the meaning isn't really ambiguous), but doesn't + fit in well with the current parsing framework. + - 32 D registers may be used (also true for VFPv3). + FIXME: Types are ignored in these register lists, which is probably a + bug. */ static int -parse_vfp_reg_list (char **str, unsigned int *pbase, int dp) +parse_vfp_reg_list (char **ccp, unsigned int *pbase, enum reg_list_els etype) { + char *str = *ccp; int base_reg; int new_base; - int regtype; - int max_regs; + enum arm_reg_type regtype = 0; + int max_regs = 0; int count = 0; int warned = 0; unsigned long mask = 0; int i; - if (**str != '{') - return FAIL; + if (*str != '{') + { + inst.error = _("expecting {"); + return FAIL; + } - (*str)++; + str++; - if (dp) + switch (etype) { + case REGLIST_VFP_S: + regtype = REG_TYPE_VFS; + max_regs = 32; + break; + + case REGLIST_VFP_D: regtype = REG_TYPE_VFD; - max_regs = 16; + break; + + case REGLIST_NEON_D: + regtype = REG_TYPE_NDQ; + break; } - else + + if (etype != REGLIST_VFP_S) { - regtype = REG_TYPE_VFS; - max_regs = 32; + /* VFPv3 allows 32 D registers. */ + if (ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v3)) + { + max_regs = 32; + if (thumb_mode) + ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used, + fpu_vfp_ext_v3); + else + ARM_MERGE_FEATURE_SETS (arm_arch_used, arm_arch_used, + fpu_vfp_ext_v3); + } + else + max_regs = 16; } base_reg = max_regs; do { - new_base = arm_reg_parse (str, regtype); + int setmask = 1, addregs = 1; + + new_base = arm_typed_reg_parse (&str, regtype, ®type, NULL); + if (new_base == FAIL) { - inst.error = gettext (reg_expected_msgs[regtype]); + first_error (_(reg_expected_msgs[regtype])); return FAIL; } + + if (new_base >= max_regs) + { + first_error (_("register out of range in list")); + return FAIL; + } + + /* Note: a value of 2 * n is returned for the register Q<n>. */ + if (regtype == REG_TYPE_NQ) + { + setmask = 3; + addregs = 2; + } if (new_base < base_reg) base_reg = new_base; - if (mask & (1 << new_base)) + if (mask & (setmask << new_base)) { - inst.error = _("invalid register list"); + first_error (_("invalid register list")); return FAIL; } @@ -1169,43 +1661,53 @@ parse_vfp_reg_list (char **str, unsigned int *pbase, int dp) warned = 1; } - mask |= 1 << new_base; - count++; + mask |= setmask << new_base; + count += addregs; - if (**str == '-') /* We have the start of a range expression */ + if (*str == '-') /* We have the start of a range expression */ { int high_range; - (*str)++; + str++; - if ((high_range = arm_reg_parse (str, regtype)) == FAIL) + if ((high_range = arm_typed_reg_parse (&str, regtype, NULL, NULL)) + == FAIL) { inst.error = gettext (reg_expected_msgs[regtype]); return FAIL; } + if (high_range >= max_regs) + { + first_error (_("register out of range in list")); + return FAIL; + } + + if (regtype == REG_TYPE_NQ) + high_range = high_range + 1; + if (high_range <= new_base) { inst.error = _("register range not in ascending order"); return FAIL; } - for (new_base++; new_base <= high_range; new_base++) + for (new_base += addregs; new_base <= high_range; new_base += addregs) { - if (mask & (1 << new_base)) + if (mask & (setmask << new_base)) { inst.error = _("invalid register list"); return FAIL; } - mask |= 1 << new_base; - count++; + mask |= setmask << new_base; + count += addregs; } } } - while (skip_past_comma (str) != FAIL); + while (skip_past_comma (&str) != FAIL); - (*str)++; + str++; /* Sanity check -- should have raised a parse error above. */ if (count == 0 || count > max_regs) @@ -1224,9 +1726,204 @@ parse_vfp_reg_list (char **str, unsigned int *pbase, int dp) } } + *ccp = str; + return count; } +/* True if two alias types are the same. */ + +static int +neon_alias_types_same (struct neon_typed_alias *a, struct neon_typed_alias *b) +{ + if (!a && !b) + return 1; + + if (!a || !b) + return 0; + + if (a->defined != b->defined) + return 0; + + if ((a->defined & NTA_HASTYPE) != 0 + && (a->eltype.type != b->eltype.type + || a->eltype.size != b->eltype.size)) + return 0; + + if ((a->defined & NTA_HASINDEX) != 0 + && (a->index != b->index)) + return 0; + + return 1; +} + +/* Parse element/structure lists for Neon VLD<n> and VST<n> instructions. + The base register is put in *PBASE. + The lane (or one of the NEON_*_LANES constants) is placed in bits [3:0] of + the return value. + The register stride (minus one) is put in bit 4 of the return value. + Bits [6:5] encode the list length (minus one). + The type of the list elements is put in *ELTYPE, if non-NULL. */ + +#define NEON_LANE(X) ((X) & 0xf) +#define NEON_REG_STRIDE(X) ((((X) >> 4) & 1) + 1) +#define NEON_REGLIST_LENGTH(X) ((((X) >> 5) & 3) + 1) + +static int +parse_neon_el_struct_list (char **str, unsigned *pbase, + struct neon_type_el *eltype) +{ + char *ptr = *str; + int base_reg = -1; + int reg_incr = -1; + int count = 0; + int lane = -1; + int leading_brace = 0; + enum arm_reg_type rtype = REG_TYPE_NDQ; + int addregs = 1; + const char *const incr_error = "register stride must be 1 or 2"; + const char *const type_error = "mismatched element/structure types in list"; + struct neon_typed_alias firsttype; + + if (skip_past_char (&ptr, '{') == SUCCESS) + leading_brace = 1; + + do + { + struct neon_typed_alias atype; + int getreg = parse_typed_reg_or_scalar (&ptr, rtype, &rtype, &atype); + + if (getreg == FAIL) + { + first_error (_(reg_expected_msgs[rtype])); + return FAIL; + } + + if (base_reg == -1) + { + base_reg = getreg; + if (rtype == REG_TYPE_NQ) + { + reg_incr = 1; + addregs = 2; + } + firsttype = atype; + } + else if (reg_incr == -1) + { + reg_incr = getreg - base_reg; + if (reg_incr < 1 || reg_incr > 2) + { + first_error (_(incr_error)); + return FAIL; + } + } + else if (getreg != base_reg + reg_incr * count) + { + first_error (_(incr_error)); + return FAIL; + } + + if (!neon_alias_types_same (&atype, &firsttype)) + { + first_error (_(type_error)); + return FAIL; + } + + /* Handle Dn-Dm or Qn-Qm syntax. Can only be used with non-indexed list + modes. */ + if (ptr[0] == '-') + { + struct neon_typed_alias htype; + int hireg, dregs = (rtype == REG_TYPE_NQ) ? 2 : 1; + if (lane == -1) + lane = NEON_INTERLEAVE_LANES; + else if (lane != NEON_INTERLEAVE_LANES) + { + first_error (_(type_error)); + return FAIL; + } + if (reg_incr == -1) + reg_incr = 1; + else if (reg_incr != 1) + { + first_error (_("don't use Rn-Rm syntax with non-unit stride")); + return FAIL; + } + ptr++; + hireg = parse_typed_reg_or_scalar (&ptr, rtype, NULL, &htype); + if (hireg == FAIL) + { + first_error (_(reg_expected_msgs[rtype])); + return FAIL; + } + if (!neon_alias_types_same (&htype, &firsttype)) + { + first_error (_(type_error)); + return FAIL; + } + count += hireg + dregs - getreg; + continue; + } + + /* If we're using Q registers, we can't use [] or [n] syntax. */ + if (rtype == REG_TYPE_NQ) + { + count += 2; + continue; + } + + if ((atype.defined & NTA_HASINDEX) != 0) + { + if (lane == -1) + lane = atype.index; + else if (lane != atype.index) + { + first_error (_(type_error)); + return FAIL; + } + } + else if (lane == -1) + lane = NEON_INTERLEAVE_LANES; + else if (lane != NEON_INTERLEAVE_LANES) + { + first_error (_(type_error)); + return FAIL; + } + count++; + } + while ((count != 1 || leading_brace) && skip_past_comma (&ptr) != FAIL); + + /* No lane set by [x]. We must be interleaving structures. */ + if (lane == -1) + lane = NEON_INTERLEAVE_LANES; + + /* Sanity check. */ + if (lane == -1 || base_reg == -1 || count < 1 || count > 4 + || (count > 1 && reg_incr == -1)) + { + first_error (_("error parsing element/structure list")); + return FAIL; + } + + if ((count > 1 || leading_brace) && skip_past_char (&ptr, '}') == FAIL) + { + first_error (_("expected }")); + return FAIL; + } + + if (reg_incr == -1) + reg_incr = 1; + + if (eltype) + *eltype = firsttype.eltype; + + *pbase = base_reg; + *str = ptr; + + return lane | ((reg_incr - 1) << 4) | ((count - 1) << 5); +} + /* Parse an explicit relocation suffix on an expression. This is either nothing, or a word in parentheses. Note that if !OBJ_ELF, arm_reloc_hsh contains no entries, so this function can only @@ -1258,7 +1955,7 @@ parse_reloc (char **str) /* Directives: register aliases. */ -static void +static struct reg_entry * insert_reg_alias (char *str, int number, int type) { struct reg_entry *new; @@ -1274,7 +1971,7 @@ insert_reg_alias (char *str, int number, int type) else if (new->number != number || new->type != type) as_warn (_("ignoring redefinition of register alias '%s'"), str); - return; + return 0; } name = xstrdup (str); @@ -1284,9 +1981,31 @@ insert_reg_alias (char *str, int number, int type) new->number = number; new->type = type; new->builtin = FALSE; + new->neon = NULL; if (hash_insert (arm_reg_hsh, name, (PTR) new)) abort (); + + return new; +} + +static void +insert_neon_reg_alias (char *str, int number, int type, + struct neon_typed_alias *atype) +{ + struct reg_entry *reg = insert_reg_alias (str, number, type); + + if (!reg) + { + first_error (_("attempt to redefine typed alias")); + return; + } + + if (atype) + { + reg->neon = xmalloc (sizeof (struct neon_typed_alias)); + *reg->neon = *atype; + } } /* Look for the .req directive. This is of the form: @@ -1354,6 +2073,148 @@ create_register_alias (char * newname, char *p) return 1; } +/* Create a Neon typed/indexed register alias using directives, e.g.: + X .dn d5.s32[1] + Y .qn 6.s16 + Z .dn d7 + T .dn Z[0] + These typed registers can be used instead of the types specified after the + Neon mnemonic, so long as all operands given have types. Types can also be + specified directly, e.g.: + vadd d0.s32, d1.s32, d2.s32 +*/ + +static int +create_neon_reg_alias (char *newname, char *p) +{ + enum arm_reg_type basetype; + struct reg_entry *basereg; + struct reg_entry mybasereg; + struct neon_type ntype; + struct neon_typed_alias typeinfo; + char *namebuf, *nameend; + int namelen; + + typeinfo.defined = 0; + typeinfo.eltype.type = NT_invtype; + typeinfo.eltype.size = -1; + typeinfo.index = -1; + + nameend = p; + + if (strncmp (p, " .dn ", 5) == 0) + basetype = REG_TYPE_VFD; + else if (strncmp (p, " .qn ", 5) == 0) + basetype = REG_TYPE_NQ; + else + return 0; + + p += 5; + + if (*p == '\0') + return 0; + + basereg = arm_reg_parse_multi (&p); + + if (basereg && basereg->type != basetype) + { + as_bad (_("bad type for register")); + return 0; + } + + if (basereg == NULL) + { + expressionS exp; + /* Try parsing as an integer. */ + my_get_expression (&exp, &p, GE_NO_PREFIX); + if (exp.X_op != O_constant) + { + as_bad (_("expression must be constant")); + return 0; + } + basereg = &mybasereg; + basereg->number = (basetype == REG_TYPE_NQ) ? exp.X_add_number * 2 + : exp.X_add_number; + basereg->neon = 0; + } + + if (basereg->neon) + typeinfo = *basereg->neon; + + if (parse_neon_type (&ntype, &p) == SUCCESS) + { + /* We got a type. */ + if (typeinfo.defined & NTA_HASTYPE) + { + as_bad (_("can't redefine the type of a register alias")); + return 0; + } + + typeinfo.defined |= NTA_HASTYPE; + if (ntype.elems != 1) + { + as_bad (_("you must specify a single type only")); + return 0; + } + typeinfo.eltype = ntype.el[0]; + } + + if (skip_past_char (&p, '[') == SUCCESS) + { + expressionS exp; + /* We got a scalar index. */ + + if (typeinfo.defined & NTA_HASINDEX) + { + as_bad (_("can't redefine the index of a scalar alias")); + return 0; + } + + my_get_expression (&exp, &p, GE_NO_PREFIX); + + if (exp.X_op != O_constant) + { + as_bad (_("scalar index must be constant")); + return 0; + } + + typeinfo.defined |= NTA_HASINDEX; + typeinfo.index = exp.X_add_number; + + if (skip_past_char (&p, ']') == FAIL) + { + as_bad (_("expecting ]")); + return 0; + } + } + + namelen = nameend - newname; + namebuf = alloca (namelen + 1); + strncpy (namebuf, newname, namelen); + namebuf[namelen] = '\0'; + + insert_neon_reg_alias (namebuf, basereg->number, basetype, + typeinfo.defined != 0 ? &typeinfo : NULL); + + /* Insert name in all uppercase. */ + for (p = namebuf; *p; p++) + *p = TOUPPER (*p); + + if (strncmp (namebuf, newname, namelen)) + insert_neon_reg_alias (namebuf, basereg->number, basetype, + typeinfo.defined != 0 ? &typeinfo : NULL); + + /* Insert name in all lowercase. */ + for (p = namebuf; *p; p++) + *p = TOLOWER (*p); + + if (strncmp (namebuf, newname, namelen)) + insert_neon_reg_alias (namebuf, basereg->number, basetype, + typeinfo.defined != 0 ? &typeinfo : NULL); + + return 1; +} + /* Should never be called, as .req goes between the alias and the register name, not at the beginning of the line. */ static void @@ -1362,6 +2223,18 @@ s_req (int a ATTRIBUTE_UNUSED) as_bad (_("invalid syntax for .req directive")); } +static void +s_dn (int a ATTRIBUTE_UNUSED) +{ + as_bad (_("invalid syntax for .dn directive")); +} + +static void +s_qn (int a ATTRIBUTE_UNUSED) +{ + as_bad (_("invalid syntax for .qn directive")); +} + /* The .unreq directive deletes an alias which was previously defined by .req. For example: @@ -1399,6 +2272,8 @@ s_unreq (int a ATTRIBUTE_UNUSED) { hash_delete (arm_reg_hsh, name); free ((char *) reg->name); + if (reg->neon) + free (reg->neon); free (reg); } } @@ -1417,7 +2292,7 @@ s_unreq (int a ATTRIBUTE_UNUSED) static enum mstate mapstate = MAP_UNDEFINED; -static void +void mapping_state (enum mstate state) { symbolS * symbolP; @@ -1738,6 +2613,7 @@ static void s_align (int unused ATTRIBUTE_UNUSED) { int temp; + bfd_boolean fill_p; long temp_fill; long max_alignment = 15; @@ -1754,16 +2630,25 @@ s_align (int unused ATTRIBUTE_UNUSED) { input_line_pointer++; temp_fill = get_absolute_expression (); + fill_p = TRUE; } else - temp_fill = 0; + { + fill_p = FALSE; + temp_fill = 0; + } if (!temp) temp = 2; /* Only make a frag if we HAVE to. */ if (temp && !need_pass_2) - frag_align (temp, (int) temp_fill, 0); + { + if (!fill_p && subseg_text_p (now_seg)) + frag_align_code (temp, 0); + else + frag_align (temp, (int) temp_fill, 0); + } demand_empty_rest_of_line (); record_alignment (now_seg, temp); @@ -2414,7 +3299,57 @@ s_arm_unwind_save_fpa (int reg) } -/* Parse a directive saving VFP registers. */ +/* Parse a directive saving VFP registers for ARMv6 and above. */ + +static void +s_arm_unwind_save_vfp_armv6 (void) +{ + int count; + unsigned int start; + valueT op; + int num_vfpv3_regs = 0; + int num_regs_below_16; + + count = parse_vfp_reg_list (&input_line_pointer, &start, REGLIST_VFP_D); + if (count == FAIL) + { + as_bad (_("expected register list")); + ignore_rest_of_line (); + return; + } + + demand_empty_rest_of_line (); + + /* We always generate FSTMD/FLDMD-style unwinding opcodes (rather + than FSTMX/FLDMX-style ones). */ + + /* Generate opcode for (VFPv3) registers numbered in the range 16 .. 31. */ + if (start >= 16) + num_vfpv3_regs = count; + else if (start + count > 16) + num_vfpv3_regs = start + count - 16; + + if (num_vfpv3_regs > 0) + { + int start_offset = start > 16 ? start - 16 : 0; + op = 0xc800 | (start_offset << 4) | (num_vfpv3_regs - 1); + add_unwind_opcode (op, 2); + } + + /* Generate opcode for registers numbered in the range 0 .. 15. */ + num_regs_below_16 = num_vfpv3_regs > 0 ? 16 - (int) start : count; + assert (num_regs_below_16 + num_vfpv3_regs == count); + if (num_regs_below_16 > 0) + { + op = 0xc900 | (start << 4) | (num_regs_below_16 - 1); + add_unwind_opcode (op, 2); + } + + unwind.frame_size += count * 8; +} + + +/* Parse a directive saving VFP registers for pre-ARMv6. */ static void s_arm_unwind_save_vfp (void) @@ -2423,7 +3358,7 @@ s_arm_unwind_save_vfp (void) unsigned int reg; valueT op; - count = parse_vfp_reg_list (&input_line_pointer, ®, 1); + count = parse_vfp_reg_list (&input_line_pointer, ®, REGLIST_VFP_D); if (count == FAIL) { as_bad (_("expected register list")); @@ -2502,7 +3437,7 @@ s_arm_unwind_save_mmxwr (void) demand_empty_rest_of_line (); - /* Generate any deferred opcodes becuuse we're going to be looking at + /* Generate any deferred opcodes because we're going to be looking at the list. */ flush_pending_unwind (); @@ -2538,7 +3473,7 @@ s_arm_unwind_save_mmxwr (void) op = 0xffff << (reg - 1); if (reg > 0 - || ((mask & op) == (1u << (reg - 1)))) + && ((mask & op) == (1u << (reg - 1)))) { op = (1 << (reg + i + 1)) - 1; op &= ~((1 << reg) - 1); @@ -2635,7 +3570,7 @@ s_arm_unwind_save_mmxwcg (void) demand_empty_rest_of_line (); - /* Generate any deferred opcodes becuuse we're going to be looking at + /* Generate any deferred opcodes because we're going to be looking at the list. */ flush_pending_unwind (); @@ -2652,10 +3587,11 @@ error: } -/* Parse an unwind_save directive. */ +/* Parse an unwind_save directive. + If the argument is non-zero, this is a .vsave directive. */ static void -s_arm_unwind_save (int ignored ATTRIBUTE_UNUSED) +s_arm_unwind_save (int arch_v6) { char *peek; struct reg_entry *reg; @@ -2692,7 +3628,12 @@ s_arm_unwind_save (int ignored ATTRIBUTE_UNUSED) return; case REG_TYPE_RN: s_arm_unwind_save_core (); return; - case REG_TYPE_VFD: s_arm_unwind_save_vfp (); return; + case REG_TYPE_VFD: + if (arch_v6) + s_arm_unwind_save_vfp_armv6 (); + else + s_arm_unwind_save_vfp (); + return; case REG_TYPE_MMXWR: s_arm_unwind_save_mmxwr (); return; case REG_TYPE_MMXWCG: s_arm_unwind_save_mmxwcg (); return; @@ -2710,6 +3651,7 @@ s_arm_unwind_movsp (int ignored ATTRIBUTE_UNUSED) { int reg; valueT op; + int offset; reg = arm_reg_parse (&input_line_pointer, REG_TYPE_RN); if (reg == FAIL) @@ -2718,6 +3660,16 @@ s_arm_unwind_movsp (int ignored ATTRIBUTE_UNUSED) ignore_rest_of_line (); return; } + + /* Optional constant. */ + if (skip_past_comma (&input_line_pointer) != FAIL) + { + if (immediate_for_directive (&offset) == FAIL) + return; + } + else + offset = 0; + demand_empty_rest_of_line (); if (reg == REG_SP || reg == REG_PC) @@ -2735,7 +3687,7 @@ s_arm_unwind_movsp (int ignored ATTRIBUTE_UNUSED) /* Record the information for later. */ unwind.fp_reg = reg; - unwind.fp_offset = unwind.frame_size; + unwind.fp_offset = unwind.frame_size - offset; unwind.sp_restored = 1; } @@ -2818,7 +3770,7 @@ static void s_arm_unwind_raw (int ignored ATTRIBUTE_UNUSED) { expressionS exp; - /* This is an arbitary limit. */ + /* This is an arbitrary limit. */ unsigned char op[16]; int count; @@ -2877,90 +3829,36 @@ s_arm_unwind_raw (int ignored ATTRIBUTE_UNUSED) static void s_arm_eabi_attribute (int ignored ATTRIBUTE_UNUSED) { - expressionS exp; - bfd_boolean is_string; - int tag; - unsigned int i = 0; - char *s = NULL; - char saved_char; + s_vendor_attribute (OBJ_ATTR_PROC); +} +#endif /* OBJ_ELF */ - expression (& exp); - if (exp.X_op != O_constant) - goto bad; +static void s_arm_arch (int); +static void s_arm_object_arch (int); +static void s_arm_cpu (int); +static void s_arm_fpu (int); - tag = exp.X_add_number; - if (tag == 4 || tag == 5 || tag == 32 || (tag > 32 && (tag & 1) != 0)) - is_string = 1; - else - is_string = 0; +#ifdef TE_PE - if (skip_past_comma (&input_line_pointer) == FAIL) - goto bad; - if (tag == 32 || !is_string) - { - expression (& exp); - if (exp.X_op != O_constant) - { - as_bad (_("expected numeric constant")); - ignore_rest_of_line (); - return; - } - i = exp.X_add_number; - } - if (tag == Tag_compatibility - && skip_past_comma (&input_line_pointer) == FAIL) - { - as_bad (_("expected comma")); - ignore_rest_of_line (); - return; - } - if (is_string) - { - skip_whitespace(input_line_pointer); - if (*input_line_pointer != '"') - goto bad_string; - input_line_pointer++; - s = input_line_pointer; - while (*input_line_pointer && *input_line_pointer != '"') - input_line_pointer++; - if (*input_line_pointer != '"') - goto bad_string; - saved_char = *input_line_pointer; - *input_line_pointer = 0; - } - else - { - s = NULL; - saved_char = 0; - } - - if (tag == Tag_compatibility) - elf32_arm_add_eabi_attr_compat (stdoutput, i, s); - else if (is_string) - elf32_arm_add_eabi_attr_string (stdoutput, tag, s); - else - elf32_arm_add_eabi_attr_int (stdoutput, tag, i); +static void +pe_directive_secrel (int dummy ATTRIBUTE_UNUSED) +{ + expressionS exp; - if (s) + do { - *input_line_pointer = saved_char; - input_line_pointer++; + expression (&exp); + if (exp.X_op == O_symbol) + exp.X_op = O_secrel; + + emit_expr (&exp, 4); } + while (*input_line_pointer++ == ','); + + input_line_pointer--; demand_empty_rest_of_line (); - return; -bad_string: - as_bad (_("bad string constant")); - ignore_rest_of_line (); - return; -bad: - as_bad (_("expected <tag> , <value>")); - ignore_rest_of_line (); } - -static void s_arm_arch (int); -static void s_arm_cpu (int); -static void s_arm_fpu (int); -#endif /* OBJ_ELF */ +#endif /* TE_PE */ /* This table describes all the machine specific pseudo-ops the assembler has to support. The fields are: @@ -2972,6 +3870,9 @@ const pseudo_typeS md_pseudo_table[] = { /* Never called because '.req' does not start a line. */ { "req", s_req, 0 }, + /* Following two are likewise never called. */ + { "dn", s_dn, 0 }, + { "qn", s_qn, 0 }, { "unreq", s_unreq, 0 }, { "bss", s_bss, 0 }, { "align", s_align, 0 }, @@ -2985,6 +3886,10 @@ const pseudo_typeS md_pseudo_table[] = { "ltorg", s_ltorg, 0 }, { "pool", s_ltorg, 0 }, { "syntax", s_syntax, 0 }, + { "cpu", s_arm_cpu, 0 }, + { "arch", s_arm_arch, 0 }, + { "object_arch", s_arm_object_arch, 0 }, + { "fpu", s_arm_fpu, 0 }, #ifdef OBJ_ELF { "word", s_arm_elf_cons, 4 }, { "long", s_arm_elf_cons, 4 }, @@ -2996,20 +3901,30 @@ const pseudo_typeS md_pseudo_table[] = { "personalityindex", s_arm_unwind_personalityindex, 0 }, { "handlerdata", s_arm_unwind_handlerdata, 0 }, { "save", s_arm_unwind_save, 0 }, + { "vsave", s_arm_unwind_save, 1 }, { "movsp", s_arm_unwind_movsp, 0 }, { "pad", s_arm_unwind_pad, 0 }, { "setfp", s_arm_unwind_setfp, 0 }, { "unwind_raw", s_arm_unwind_raw, 0 }, - { "cpu", s_arm_cpu, 0 }, - { "arch", s_arm_arch, 0 }, - { "fpu", s_arm_fpu, 0 }, { "eabi_attribute", s_arm_eabi_attribute, 0 }, #else { "word", cons, 4}, + + /* These are used for dwarf. */ + {"2byte", cons, 2}, + {"4byte", cons, 4}, + {"8byte", cons, 8}, + /* These are used for dwarf2. */ + { "file", (void (*) (int)) dwarf2_directive_file, 0 }, + { "loc", dwarf2_directive_loc, 0 }, + { "loc_mark_labels", dwarf2_directive_loc_mark_labels, 0 }, #endif { "extend", float_cons, 'x' }, { "ldouble", float_cons, 'x' }, { "packed", float_cons, 'p' }, +#ifdef TE_PE + {"secrel32", pe_directive_secrel, 0}, +#endif { 0, 0, 0 } }; @@ -3043,6 +3958,58 @@ parse_immediate (char **str, int *val, int min, int max, return SUCCESS; } +/* Less-generic immediate-value read function with the possibility of loading a + big (64-bit) immediate, as required by Neon VMOV, VMVN and logic immediate + instructions. Puts the result directly in inst.operands[i]. */ + +static int +parse_big_immediate (char **str, int i) +{ + expressionS exp; + char *ptr = *str; + + my_get_expression (&exp, &ptr, GE_OPT_PREFIX_BIG); + + if (exp.X_op == O_constant) + { + inst.operands[i].imm = exp.X_add_number & 0xffffffff; + /* If we're on a 64-bit host, then a 64-bit number can be returned using + O_constant. We have to be careful not to break compilation for + 32-bit X_add_number, though. */ + if ((exp.X_add_number & ~0xffffffffl) != 0) + { + /* X >> 32 is illegal if sizeof (exp.X_add_number) == 4. */ + inst.operands[i].reg = ((exp.X_add_number >> 16) >> 16) & 0xffffffff; + inst.operands[i].regisimm = 1; + } + } + else if (exp.X_op == O_big + && LITTLENUM_NUMBER_OF_BITS * exp.X_add_number > 32 + && LITTLENUM_NUMBER_OF_BITS * exp.X_add_number <= 64) + { + unsigned parts = 32 / LITTLENUM_NUMBER_OF_BITS, j, idx = 0; + /* Bignums have their least significant bits in + generic_bignum[0]. Make sure we put 32 bits in imm and + 32 bits in reg, in a (hopefully) portable way. */ + assert (parts != 0); + inst.operands[i].imm = 0; + for (j = 0; j < parts; j++, idx++) + inst.operands[i].imm |= generic_bignum[idx] + << (LITTLENUM_NUMBER_OF_BITS * j); + inst.operands[i].reg = 0; + for (j = 0; j < parts; j++, idx++) + inst.operands[i].reg |= generic_bignum[idx] + << (LITTLENUM_NUMBER_OF_BITS * j); + inst.operands[i].regisimm = 1; + } + else + return FAIL; + + *str = ptr; + + return SUCCESS; +} + /* Returns the pseudo-register number of an FPA immediate constant, or FAIL if there isn't a valid constant here. */ @@ -3134,6 +4101,80 @@ parse_fpa_immediate (char ** str) return FAIL; } +/* Returns 1 if a number has "quarter-precision" float format + 0baBbbbbbc defgh000 00000000 00000000. */ + +static int +is_quarter_float (unsigned imm) +{ + int bs = (imm & 0x20000000) ? 0x3e000000 : 0x40000000; + return (imm & 0x7ffff) == 0 && ((imm & 0x7e000000) ^ bs) == 0; +} + +/* Parse an 8-bit "quarter-precision" floating point number of the form: + 0baBbbbbbc defgh000 00000000 00000000. + The zero and minus-zero cases need special handling, since they can't be + encoded in the "quarter-precision" float format, but can nonetheless be + loaded as integer constants. */ + +static unsigned +parse_qfloat_immediate (char **ccp, int *immed) +{ + char *str = *ccp; + char *fpnum; + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + int found_fpchar = 0; + + skip_past_char (&str, '#'); + + /* We must not accidentally parse an integer as a floating-point number. Make + sure that the value we parse is not an integer by checking for special + characters '.' or 'e'. + FIXME: This is a horrible hack, but doing better is tricky because type + information isn't in a very usable state at parse time. */ + fpnum = str; + skip_whitespace (fpnum); + + if (strncmp (fpnum, "0x", 2) == 0) + return FAIL; + else + { + for (; *fpnum != '\0' && *fpnum != ' ' && *fpnum != '\n'; fpnum++) + if (*fpnum == '.' || *fpnum == 'e' || *fpnum == 'E') + { + found_fpchar = 1; + break; + } + + if (!found_fpchar) + return FAIL; + } + + if ((str = atof_ieee (str, 's', words)) != NULL) + { + unsigned fpword = 0; + int i; + + /* Our FP word must be 32 bits (single-precision FP). */ + for (i = 0; i < 32 / LITTLENUM_NUMBER_OF_BITS; i++) + { + fpword <<= LITTLENUM_NUMBER_OF_BITS; + fpword |= words[i]; + } + + if (is_quarter_float (fpword) || (fpword & 0x7fffffff) == 0) + *immed = fpword; + else + return FAIL; + + *ccp = str; + + return SUCCESS; + } + + return FAIL; +} + /* Shift operands. */ enum shift_kind { @@ -3317,6 +4358,168 @@ parse_shifter_operand (char **str, int i) return SUCCESS; } +/* Group relocation information. Each entry in the table contains the + textual name of the relocation as may appear in assembler source + and must end with a colon. + Along with this textual name are the relocation codes to be used if + the corresponding instruction is an ALU instruction (ADD or SUB only), + an LDR, an LDRS, or an LDC. */ + +struct group_reloc_table_entry +{ + const char *name; + int alu_code; + int ldr_code; + int ldrs_code; + int ldc_code; +}; + +typedef enum +{ + /* Varieties of non-ALU group relocation. */ + + GROUP_LDR, + GROUP_LDRS, + GROUP_LDC +} group_reloc_type; + +static struct group_reloc_table_entry group_reloc_table[] = + { /* Program counter relative: */ + { "pc_g0_nc", + BFD_RELOC_ARM_ALU_PC_G0_NC, /* ALU */ + 0, /* LDR */ + 0, /* LDRS */ + 0 }, /* LDC */ + { "pc_g0", + BFD_RELOC_ARM_ALU_PC_G0, /* ALU */ + BFD_RELOC_ARM_LDR_PC_G0, /* LDR */ + BFD_RELOC_ARM_LDRS_PC_G0, /* LDRS */ + BFD_RELOC_ARM_LDC_PC_G0 }, /* LDC */ + { "pc_g1_nc", + BFD_RELOC_ARM_ALU_PC_G1_NC, /* ALU */ + 0, /* LDR */ + 0, /* LDRS */ + 0 }, /* LDC */ + { "pc_g1", + BFD_RELOC_ARM_ALU_PC_G1, /* ALU */ + BFD_RELOC_ARM_LDR_PC_G1, /* LDR */ + BFD_RELOC_ARM_LDRS_PC_G1, /* LDRS */ + BFD_RELOC_ARM_LDC_PC_G1 }, /* LDC */ + { "pc_g2", + BFD_RELOC_ARM_ALU_PC_G2, /* ALU */ + BFD_RELOC_ARM_LDR_PC_G2, /* LDR */ + BFD_RELOC_ARM_LDRS_PC_G2, /* LDRS */ + BFD_RELOC_ARM_LDC_PC_G2 }, /* LDC */ + /* Section base relative */ + { "sb_g0_nc", + BFD_RELOC_ARM_ALU_SB_G0_NC, /* ALU */ + 0, /* LDR */ + 0, /* LDRS */ + 0 }, /* LDC */ + { "sb_g0", + BFD_RELOC_ARM_ALU_SB_G0, /* ALU */ + BFD_RELOC_ARM_LDR_SB_G0, /* LDR */ + BFD_RELOC_ARM_LDRS_SB_G0, /* LDRS */ + BFD_RELOC_ARM_LDC_SB_G0 }, /* LDC */ + { "sb_g1_nc", + BFD_RELOC_ARM_ALU_SB_G1_NC, /* ALU */ + 0, /* LDR */ + 0, /* LDRS */ + 0 }, /* LDC */ + { "sb_g1", + BFD_RELOC_ARM_ALU_SB_G1, /* ALU */ + BFD_RELOC_ARM_LDR_SB_G1, /* LDR */ + BFD_RELOC_ARM_LDRS_SB_G1, /* LDRS */ + BFD_RELOC_ARM_LDC_SB_G1 }, /* LDC */ + { "sb_g2", + BFD_RELOC_ARM_ALU_SB_G2, /* ALU */ + BFD_RELOC_ARM_LDR_SB_G2, /* LDR */ + BFD_RELOC_ARM_LDRS_SB_G2, /* LDRS */ + BFD_RELOC_ARM_LDC_SB_G2 } }; /* LDC */ + +/* Given the address of a pointer pointing to the textual name of a group + relocation as may appear in assembler source, attempt to find its details + in group_reloc_table. The pointer will be updated to the character after + the trailing colon. On failure, FAIL will be returned; SUCCESS + otherwise. On success, *entry will be updated to point at the relevant + group_reloc_table entry. */ + +static int +find_group_reloc_table_entry (char **str, struct group_reloc_table_entry **out) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE (group_reloc_table); i++) + { + int length = strlen (group_reloc_table[i].name); + + if (strncasecmp (group_reloc_table[i].name, *str, length) == 0 && + (*str)[length] == ':') + { + *out = &group_reloc_table[i]; + *str += (length + 1); + return SUCCESS; + } + } + + return FAIL; +} + +/* Parse a <shifter_operand> for an ARM data processing instruction + (as for parse_shifter_operand) where group relocations are allowed: + + #<immediate> + #<immediate>, <rotate> + #:<group_reloc>:<expression> + <Rm> + <Rm>, <shift> + + where <group_reloc> is one of the strings defined in group_reloc_table. + The hashes are optional. + + Everything else is as for parse_shifter_operand. */ + +static parse_operand_result +parse_shifter_operand_group_reloc (char **str, int i) +{ + /* Determine if we have the sequence of characters #: or just : + coming next. If we do, then we check for a group relocation. + If we don't, punt the whole lot to parse_shifter_operand. */ + + if (((*str)[0] == '#' && (*str)[1] == ':') + || (*str)[0] == ':') + { + struct group_reloc_table_entry *entry; + + if ((*str)[0] == '#') + (*str) += 2; + else + (*str)++; + + /* Try to parse a group relocation. Anything else is an error. */ + if (find_group_reloc_table_entry (str, &entry) == FAIL) + { + inst.error = _("unknown group relocation"); + return PARSE_OPERAND_FAIL_NO_BACKTRACK; + } + + /* We now have the group relocation table entry corresponding to + the name in the assembler source. Next, we parse the expression. */ + if (my_get_expression (&inst.reloc.exp, str, GE_NO_PREFIX)) + return PARSE_OPERAND_FAIL_NO_BACKTRACK; + + /* Record the relocation type (always the ALU variant here). */ + inst.reloc.type = entry->alu_code; + assert (inst.reloc.type != 0); + + return PARSE_OPERAND_SUCCESS; + } + else + return parse_shifter_operand (str, i) == SUCCESS + ? PARSE_OPERAND_SUCCESS : PARSE_OPERAND_FAIL; + + /* Never reached. */ +} + /* Parse all forms of an ARM address expression. Information is written to inst.operands[i] and/or inst.reloc. @@ -3349,8 +4552,9 @@ parse_shifter_operand (char **str, int i) It is the caller's responsibility to check for addressing modes not supported by the instruction, and to set inst.reloc.type. */ -static int -parse_address (char **str, int i) +static parse_operand_result +parse_address_main (char **str, int i, int group_relocations, + group_reloc_type group_type) { char *p = *str; int reg; @@ -3368,16 +4572,16 @@ parse_address (char **str, int i) /* else a load-constant pseudo op, no special treatment needed here */ if (my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX)) - return FAIL; + return PARSE_OPERAND_FAIL; *str = p; - return SUCCESS; + return PARSE_OPERAND_SUCCESS; } if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) == FAIL) { inst.error = _(reg_expected_msgs[REG_TYPE_RN]); - return FAIL; + return PARSE_OPERAND_FAIL; } inst.operands[i].reg = reg; inst.operands[i].isreg = 1; @@ -3396,8 +4600,25 @@ parse_address (char **str, int i) if (skip_past_comma (&p) == SUCCESS) if (parse_shift (&p, i, SHIFT_IMMEDIATE) == FAIL) - return FAIL; + return PARSE_OPERAND_FAIL; } + else if (skip_past_char (&p, ':') == SUCCESS) + { + /* FIXME: '@' should be used here, but it's filtered out by generic + code before we get to see it here. This may be subject to + change. */ + expressionS exp; + my_get_expression (&exp, &p, GE_NO_PREFIX); + if (exp.X_op != O_constant) + { + inst.error = _("alignment must be constant"); + return PARSE_OPERAND_FAIL; + } + inst.operands[i].imm = exp.X_add_number << 8; + inst.operands[i].immisalign = 1; + /* Alignments are not pre-indexes. */ + inst.operands[i].preind = 0; + } else { if (inst.operands[i].negative) @@ -3405,15 +4626,68 @@ parse_address (char **str, int i) inst.operands[i].negative = 0; p--; } - if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX)) - return FAIL; + + if (group_relocations && + ((*p == '#' && *(p + 1) == ':') || *p == ':')) + + { + struct group_reloc_table_entry *entry; + + /* Skip over the #: or : sequence. */ + if (*p == '#') + p += 2; + else + p++; + + /* Try to parse a group relocation. Anything else is an + error. */ + if (find_group_reloc_table_entry (&p, &entry) == FAIL) + { + inst.error = _("unknown group relocation"); + return PARSE_OPERAND_FAIL_NO_BACKTRACK; + } + + /* We now have the group relocation table entry corresponding to + the name in the assembler source. Next, we parse the + expression. */ + if (my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX)) + return PARSE_OPERAND_FAIL_NO_BACKTRACK; + + /* Record the relocation type. */ + switch (group_type) + { + case GROUP_LDR: + inst.reloc.type = entry->ldr_code; + break; + + case GROUP_LDRS: + inst.reloc.type = entry->ldrs_code; + break; + + case GROUP_LDC: + inst.reloc.type = entry->ldc_code; + break; + + default: + assert (0); + } + + if (inst.reloc.type == 0) + { + inst.error = _("this group relocation is not allowed on this instruction"); + return PARSE_OPERAND_FAIL_NO_BACKTRACK; + } + } + else + if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX)) + return PARSE_OPERAND_FAIL; } } if (skip_past_char (&p, ']') == FAIL) { inst.error = _("']' expected"); - return FAIL; + return PARSE_OPERAND_FAIL; } if (skip_past_char (&p, '!') == SUCCESS) @@ -3426,20 +4700,20 @@ parse_address (char **str, int i) /* [Rn], {expr} - unindexed, with option */ if (parse_immediate (&p, &inst.operands[i].imm, 0, 255, TRUE) == FAIL) - return FAIL; + return PARSE_OPERAND_FAIL; if (skip_past_char (&p, '}') == FAIL) { inst.error = _("'}' expected at end of 'option' field"); - return FAIL; + return PARSE_OPERAND_FAIL; } if (inst.operands[i].preind) { inst.error = _("cannot combine index with option"); - return FAIL; + return PARSE_OPERAND_FAIL; } *str = p; - return SUCCESS; + return PARSE_OPERAND_SUCCESS; } else { @@ -3449,7 +4723,7 @@ parse_address (char **str, int i) if (inst.operands[i].preind) { inst.error = _("cannot combine pre- and post-indexing"); - return FAIL; + return PARSE_OPERAND_FAIL; } if (*p == '+') p++; @@ -3457,12 +4731,17 @@ parse_address (char **str, int i) if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL) { - inst.operands[i].imm = reg; + /* We might be using the immediate for alignment already. If we + are, OR the register number into the low-order bits. */ + if (inst.operands[i].immisalign) + inst.operands[i].imm |= reg; + else + inst.operands[i].imm = reg; inst.operands[i].immisreg = 1; if (skip_past_comma (&p) == SUCCESS) if (parse_shift (&p, i, SHIFT_IMMEDIATE) == FAIL) - return FAIL; + return PARSE_OPERAND_FAIL; } else { @@ -3472,7 +4751,7 @@ parse_address (char **str, int i) p--; } if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX)) - return FAIL; + return PARSE_OPERAND_FAIL; } } } @@ -3486,6 +4765,59 @@ parse_address (char **str, int i) inst.reloc.exp.X_add_number = 0; } *str = p; + return PARSE_OPERAND_SUCCESS; +} + +static int +parse_address (char **str, int i) +{ + return parse_address_main (str, i, 0, 0) == PARSE_OPERAND_SUCCESS + ? SUCCESS : FAIL; +} + +static parse_operand_result +parse_address_group_reloc (char **str, int i, group_reloc_type type) +{ + return parse_address_main (str, i, 1, type); +} + +/* Parse an operand for a MOVW or MOVT instruction. */ +static int +parse_half (char **str) +{ + char * p; + + p = *str; + skip_past_char (&p, '#'); + if (strncasecmp (p, ":lower16:", 9) == 0) + inst.reloc.type = BFD_RELOC_ARM_MOVW; + else if (strncasecmp (p, ":upper16:", 9) == 0) + inst.reloc.type = BFD_RELOC_ARM_MOVT; + + if (inst.reloc.type != BFD_RELOC_UNUSED) + { + p += 9; + skip_whitespace(p); + } + + if (my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX)) + return FAIL; + + if (inst.reloc.type == BFD_RELOC_UNUSED) + { + if (inst.reloc.exp.X_op != O_constant) + { + inst.error = _("constant expression expected"); + return FAIL; + } + if (inst.reloc.exp.X_add_number < 0 + || inst.reloc.exp.X_add_number > 0xffff) + { + inst.error = _("immediate value out of range"); + return FAIL; + } + } + *str = p; return SUCCESS; } @@ -3752,6 +5084,228 @@ parse_tb (char **str) return SUCCESS; } +/* Parse the operands of a Neon VMOV instruction. See do_neon_mov for more + information on the types the operands can take and how they are encoded. + Up to four operands may be read; this function handles setting the + ".present" field for each read operand itself. + Updates STR and WHICH_OPERAND if parsing is successful and returns SUCCESS, + else returns FAIL. */ + +static int +parse_neon_mov (char **str, int *which_operand) +{ + int i = *which_operand, val; + enum arm_reg_type rtype; + char *ptr = *str; + struct neon_type_el optype; + + if ((val = parse_scalar (&ptr, 8, &optype)) != FAIL) + { + /* Case 4: VMOV<c><q>.<size> <Dn[x]>, <Rd>. */ + inst.operands[i].reg = val; + inst.operands[i].isscalar = 1; + inst.operands[i].vectype = optype; + inst.operands[i++].present = 1; + + if (skip_past_comma (&ptr) == FAIL) + goto wanted_comma; + + if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) == FAIL) + goto wanted_arm; + + inst.operands[i].reg = val; + inst.operands[i].isreg = 1; + inst.operands[i].present = 1; + } + else if ((val = arm_typed_reg_parse (&ptr, REG_TYPE_NSDQ, &rtype, &optype)) + != FAIL) + { + /* Cases 0, 1, 2, 3, 5 (D only). */ + if (skip_past_comma (&ptr) == FAIL) + goto wanted_comma; + + inst.operands[i].reg = val; + inst.operands[i].isreg = 1; + inst.operands[i].isquad = (rtype == REG_TYPE_NQ); + inst.operands[i].issingle = (rtype == REG_TYPE_VFS); + inst.operands[i].isvec = 1; + inst.operands[i].vectype = optype; + inst.operands[i++].present = 1; + + if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) != FAIL) + { + /* Case 5: VMOV<c><q> <Dm>, <Rd>, <Rn>. + Case 13: VMOV <Sd>, <Rm> */ + inst.operands[i].reg = val; + inst.operands[i].isreg = 1; + inst.operands[i].present = 1; + + if (rtype == REG_TYPE_NQ) + { + first_error (_("can't use Neon quad register here")); + return FAIL; + } + else if (rtype != REG_TYPE_VFS) + { + i++; + if (skip_past_comma (&ptr) == FAIL) + goto wanted_comma; + if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) == FAIL) + goto wanted_arm; + inst.operands[i].reg = val; + inst.operands[i].isreg = 1; + inst.operands[i].present = 1; + } + } + else if (parse_qfloat_immediate (&ptr, &inst.operands[i].imm) == SUCCESS) + /* Case 2: VMOV<c><q>.<dt> <Qd>, #<float-imm> + Case 3: VMOV<c><q>.<dt> <Dd>, #<float-imm> + Case 10: VMOV.F32 <Sd>, #<imm> + Case 11: VMOV.F64 <Dd>, #<imm> */ + inst.operands[i].immisfloat = 1; + else if (parse_big_immediate (&ptr, i) == SUCCESS) + /* Case 2: VMOV<c><q>.<dt> <Qd>, #<imm> + Case 3: VMOV<c><q>.<dt> <Dd>, #<imm> */ + ; + else if ((val = arm_typed_reg_parse (&ptr, REG_TYPE_NSDQ, &rtype, + &optype)) != FAIL) + { + /* Case 0: VMOV<c><q> <Qd>, <Qm> + Case 1: VMOV<c><q> <Dd>, <Dm> + Case 8: VMOV.F32 <Sd>, <Sm> + Case 15: VMOV <Sd>, <Se>, <Rn>, <Rm> */ + + inst.operands[i].reg = val; + inst.operands[i].isreg = 1; + inst.operands[i].isquad = (rtype == REG_TYPE_NQ); + inst.operands[i].issingle = (rtype == REG_TYPE_VFS); + inst.operands[i].isvec = 1; + inst.operands[i].vectype = optype; + inst.operands[i].present = 1; + + if (skip_past_comma (&ptr) == SUCCESS) + { + /* Case 15. */ + i++; + + if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) == FAIL) + goto wanted_arm; + + inst.operands[i].reg = val; + inst.operands[i].isreg = 1; + inst.operands[i++].present = 1; + + if (skip_past_comma (&ptr) == FAIL) + goto wanted_comma; + + if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) == FAIL) + goto wanted_arm; + + inst.operands[i].reg = val; + inst.operands[i].isreg = 1; + inst.operands[i++].present = 1; + } + } + else + { + first_error (_("expected <Rm> or <Dm> or <Qm> operand")); + return FAIL; + } + } + else if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) != FAIL) + { + /* Cases 6, 7. */ + inst.operands[i].reg = val; + inst.operands[i].isreg = 1; + inst.operands[i++].present = 1; + + if (skip_past_comma (&ptr) == FAIL) + goto wanted_comma; + + if ((val = parse_scalar (&ptr, 8, &optype)) != FAIL) + { + /* Case 6: VMOV<c><q>.<dt> <Rd>, <Dn[x]> */ + inst.operands[i].reg = val; + inst.operands[i].isscalar = 1; + inst.operands[i].present = 1; + inst.operands[i].vectype = optype; + } + else if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) != FAIL) + { + /* Case 7: VMOV<c><q> <Rd>, <Rn>, <Dm> */ + inst.operands[i].reg = val; + inst.operands[i].isreg = 1; + inst.operands[i++].present = 1; + + if (skip_past_comma (&ptr) == FAIL) + goto wanted_comma; + + if ((val = arm_typed_reg_parse (&ptr, REG_TYPE_VFSD, &rtype, &optype)) + == FAIL) + { + first_error (_(reg_expected_msgs[REG_TYPE_VFSD])); + return FAIL; + } + + inst.operands[i].reg = val; + inst.operands[i].isreg = 1; + inst.operands[i].isvec = 1; + inst.operands[i].issingle = (rtype == REG_TYPE_VFS); + inst.operands[i].vectype = optype; + inst.operands[i].present = 1; + + if (rtype == REG_TYPE_VFS) + { + /* Case 14. */ + i++; + if (skip_past_comma (&ptr) == FAIL) + goto wanted_comma; + if ((val = arm_typed_reg_parse (&ptr, REG_TYPE_VFS, NULL, + &optype)) == FAIL) + { + first_error (_(reg_expected_msgs[REG_TYPE_VFS])); + return FAIL; + } + inst.operands[i].reg = val; + inst.operands[i].isreg = 1; + inst.operands[i].isvec = 1; + inst.operands[i].issingle = 1; + inst.operands[i].vectype = optype; + inst.operands[i].present = 1; + } + } + else if ((val = arm_typed_reg_parse (&ptr, REG_TYPE_VFS, NULL, &optype)) + != FAIL) + { + /* Case 13. */ + inst.operands[i].reg = val; + inst.operands[i].isreg = 1; + inst.operands[i].isvec = 1; + inst.operands[i].issingle = 1; + inst.operands[i].vectype = optype; + inst.operands[i++].present = 1; + } + } + else + { + first_error (_("parse error")); + return FAIL; + } + + /* Successfully parsed the operands. Update args. */ + *which_operand = i; + *str = ptr; + return SUCCESS; + + wanted_comma: + first_error (_("expected comma")); + return FAIL; + + wanted_arm: + first_error (_(reg_expected_msgs[REG_TYPE_RN])); + return FAIL; +} + /* Matcher codes for parse_operands. */ enum operand_parse_code { @@ -3765,7 +5319,13 @@ enum operand_parse_code OP_RCN, /* Coprocessor register */ OP_RF, /* FPA register */ OP_RVS, /* VFP single precision register */ - OP_RVD, /* VFP double precision register */ + OP_RVD, /* VFP double precision register (0..15) */ + OP_RND, /* Neon double precision register (0..31) */ + OP_RNQ, /* Neon quad precision register */ + OP_RVSD, /* VFP single or double precision register */ + OP_RNDQ, /* Neon double or quad precision register */ + OP_RNSDQ, /* Neon single, double or quad precision register */ + OP_RNSC, /* Neon scalar D[X] */ OP_RVC, /* VFP control register */ OP_RMF, /* Maverick F register */ OP_RMD, /* Maverick D register */ @@ -3781,16 +5341,36 @@ enum operand_parse_code OP_REGLST, /* ARM register list */ OP_VRSLST, /* VFP single-precision register list */ OP_VRDLST, /* VFP double-precision register list */ - + OP_VRSDLST, /* VFP single or double-precision register list (& quad) */ + OP_NRDLST, /* Neon double-precision register list (d0-d31, qN aliases) */ + OP_NSTRLST, /* Neon element/structure list */ + + OP_NILO, /* Neon immediate/logic operands 2 or 2+3. (VBIC, VORR...) */ + OP_RNDQ_I0, /* Neon D or Q reg, or immediate zero. */ + OP_RVSD_I0, /* VFP S or D reg, or immediate zero. */ + OP_RR_RNSC, /* ARM reg or Neon scalar. */ + OP_RNSDQ_RNSC, /* Vector S, D or Q reg, or Neon scalar. */ + OP_RNDQ_RNSC, /* Neon D or Q reg, or Neon scalar. */ + OP_RND_RNSC, /* Neon D reg, or Neon scalar. */ + OP_VMOV, /* Neon VMOV operands. */ + OP_RNDQ_IMVNb,/* Neon D or Q reg, or immediate good for VMVN. */ + OP_RNDQ_I63b, /* Neon D or Q reg, or immediate for shift. */ + OP_RIWR_I32z, /* iWMMXt wR register, or immediate 0 .. 32 for iWMMXt2. */ + + OP_I0, /* immediate zero */ OP_I7, /* immediate value 0 .. 7 */ OP_I15, /* 0 .. 15 */ OP_I16, /* 1 .. 16 */ + OP_I16z, /* 0 .. 16 */ OP_I31, /* 0 .. 31 */ OP_I31w, /* 0 .. 31, optional trailing ! */ OP_I32, /* 1 .. 32 */ + OP_I32z, /* 0 .. 32 */ + OP_I63, /* 0 .. 63 */ OP_I63s, /* -64 .. 63 */ + OP_I64, /* 1 .. 64 */ + OP_I64z, /* 0 .. 64 */ OP_I255, /* 0 .. 255 */ - OP_Iffff, /* 0 .. 65535 */ OP_I4b, /* immediate, prefix optional, 1 .. 4 */ OP_I7b, /* 0 .. 7 */ @@ -3798,10 +5378,15 @@ enum operand_parse_code OP_I31b, /* 0 .. 31 */ OP_SH, /* shifter operand */ + OP_SHG, /* shifter operand with possible group relocation */ OP_ADDR, /* Memory address expression (any mode) */ + OP_ADDRGLDR, /* Mem addr expr (any mode) with possible LDR group reloc */ + OP_ADDRGLDRS, /* Mem addr expr (any mode) with possible LDRS group reloc */ + OP_ADDRGLDC, /* Mem addr expr (any mode) with possible LDC group reloc */ OP_EXP, /* arbitrary expression */ OP_EXPi, /* same, with optional immediate prefix */ OP_EXPr, /* same, with optional relocation suffix */ + OP_HALF, /* 0 .. 65535 or low/high reloc. */ OP_CPSF, /* CPS flags */ OP_ENDI, /* Endianness specifier */ @@ -3809,20 +5394,30 @@ enum operand_parse_code OP_COND, /* conditional code */ OP_TB, /* Table branch. */ + OP_RVC_PSR, /* CPSR/SPSR mask for msr, or VFP control register. */ + OP_APSR_RR, /* ARM register or "APSR_nzcv". */ + OP_RRnpc_I0, /* ARM register or literal 0 */ OP_RR_EXr, /* ARM register or expression with opt. reloc suff. */ OP_RR_EXi, /* ARM register or expression with imm prefix */ OP_RF_IF, /* FPA register or immediate */ OP_RIWR_RIWC, /* iWMMXt R or C reg */ + OP_RIWC_RIWG, /* iWMMXt wC or wCG reg */ /* Optional operands. */ OP_oI7b, /* immediate, prefix optional, 0 .. 7 */ OP_oI31b, /* 0 .. 31 */ + OP_oI32b, /* 1 .. 32 */ OP_oIffffb, /* 0 .. 65535 */ OP_oI255c, /* curly-brace enclosed, 0 .. 255 */ OP_oRR, /* ARM register */ OP_oRRnpc, /* ARM register, not the PC */ + OP_oRRw, /* ARM register, not r15, optional trailing ! */ + OP_oRND, /* Optional Neon double precision register */ + OP_oRNQ, /* Optional Neon quad precision register */ + OP_oRNDQ, /* Optional Neon double or quad precision register */ + OP_oRNSDQ, /* Optional single, double or quad precision vector register */ OP_oSHll, /* LSL immediate */ OP_oSHar, /* ASR immediate */ OP_oSHllar, /* LSL or ASR immediate */ @@ -3843,30 +5438,44 @@ parse_operands (char *str, const unsigned char *pattern) char *backtrack_pos = 0; const char *backtrack_error = 0; int i, val, backtrack_index = 0; + enum arm_reg_type rtype; + parse_operand_result result; #define po_char_or_fail(chr) do { \ if (skip_past_char (&str, chr) == FAIL) \ goto bad_args; \ } while (0) -#define po_reg_or_fail(regtype) do { \ - val = arm_reg_parse (&str, regtype); \ - if (val == FAIL) \ - { \ - inst.error = _(reg_expected_msgs[regtype]); \ - goto failure; \ - } \ - inst.operands[i].reg = val; \ - inst.operands[i].isreg = 1; \ +#define po_reg_or_fail(regtype) do { \ + val = arm_typed_reg_parse (&str, regtype, &rtype, \ + &inst.operands[i].vectype); \ + if (val == FAIL) \ + { \ + first_error (_(reg_expected_msgs[regtype])); \ + goto failure; \ + } \ + inst.operands[i].reg = val; \ + inst.operands[i].isreg = 1; \ + inst.operands[i].isquad = (rtype == REG_TYPE_NQ); \ + inst.operands[i].issingle = (rtype == REG_TYPE_VFS); \ + inst.operands[i].isvec = (rtype == REG_TYPE_VFS \ + || rtype == REG_TYPE_VFD \ + || rtype == REG_TYPE_NQ); \ } while (0) -#define po_reg_or_goto(regtype, label) do { \ - val = arm_reg_parse (&str, regtype); \ - if (val == FAIL) \ - goto label; \ - \ - inst.operands[i].reg = val; \ - inst.operands[i].isreg = 1; \ +#define po_reg_or_goto(regtype, label) do { \ + val = arm_typed_reg_parse (&str, regtype, &rtype, \ + &inst.operands[i].vectype); \ + if (val == FAIL) \ + goto label; \ + \ + inst.operands[i].reg = val; \ + inst.operands[i].isreg = 1; \ + inst.operands[i].isquad = (rtype == REG_TYPE_NQ); \ + inst.operands[i].issingle = (rtype == REG_TYPE_VFS); \ + inst.operands[i].isvec = (rtype == REG_TYPE_VFS \ + || rtype == REG_TYPE_VFD \ + || rtype == REG_TYPE_NQ); \ } while (0) #define po_imm_or_fail(min, max, popt) do { \ @@ -3875,11 +5484,27 @@ parse_operands (char *str, const unsigned char *pattern) inst.operands[i].imm = val; \ } while (0) +#define po_scalar_or_goto(elsz, label) do { \ + val = parse_scalar (&str, elsz, &inst.operands[i].vectype); \ + if (val == FAIL) \ + goto label; \ + inst.operands[i].reg = val; \ + inst.operands[i].isscalar = 1; \ +} while (0) + #define po_misc_or_fail(expr) do { \ if (expr) \ goto failure; \ } while (0) +#define po_misc_or_fail_no_backtrack(expr) do { \ + result = expr; \ + if (result == PARSE_OPERAND_FAIL_NO_BACKTRACK)\ + backtrack_pos = 0; \ + if (result != PARSE_OPERAND_SUCCESS) \ + goto failure; \ +} while (0) + skip_whitespace (str); for (i = 0; upat[i] != OP_stop; i++) @@ -3893,7 +5518,7 @@ parse_operands (char *str, const unsigned char *pattern) backtrack_index = i; } - if (i > 0) + if (i > 0 && (i > 1 || inst.operands[0].present)) po_char_or_fail (','); switch (upat[i]) @@ -3908,7 +5533,15 @@ parse_operands (char *str, const unsigned char *pattern) case OP_RF: po_reg_or_fail (REG_TYPE_FN); break; case OP_RVS: po_reg_or_fail (REG_TYPE_VFS); break; case OP_RVD: po_reg_or_fail (REG_TYPE_VFD); break; - case OP_RVC: po_reg_or_fail (REG_TYPE_VFC); break; + case OP_oRND: + case OP_RND: po_reg_or_fail (REG_TYPE_VFD); break; + case OP_RVC: + po_reg_or_goto (REG_TYPE_VFC, coproc_reg); + break; + /* Also accept generic coprocessor regs for unknown registers. */ + coproc_reg: + po_reg_or_fail (REG_TYPE_CN); + break; case OP_RMF: po_reg_or_fail (REG_TYPE_MVF); break; case OP_RMD: po_reg_or_fail (REG_TYPE_MVD); break; case OP_RMFX: po_reg_or_fail (REG_TYPE_MVFX); break; @@ -3919,6 +5552,126 @@ parse_operands (char *str, const unsigned char *pattern) case OP_RIWC: po_reg_or_fail (REG_TYPE_MMXWC); break; case OP_RIWG: po_reg_or_fail (REG_TYPE_MMXWCG); break; case OP_RXA: po_reg_or_fail (REG_TYPE_XSCALE); break; + case OP_oRNQ: + case OP_RNQ: po_reg_or_fail (REG_TYPE_NQ); break; + case OP_oRNDQ: + case OP_RNDQ: po_reg_or_fail (REG_TYPE_NDQ); break; + case OP_RVSD: po_reg_or_fail (REG_TYPE_VFSD); break; + case OP_oRNSDQ: + case OP_RNSDQ: po_reg_or_fail (REG_TYPE_NSDQ); break; + + /* Neon scalar. Using an element size of 8 means that some invalid + scalars are accepted here, so deal with those in later code. */ + case OP_RNSC: po_scalar_or_goto (8, failure); break; + + /* WARNING: We can expand to two operands here. This has the potential + to totally confuse the backtracking mechanism! It will be OK at + least as long as we don't try to use optional args as well, + though. */ + case OP_NILO: + { + po_reg_or_goto (REG_TYPE_NDQ, try_imm); + inst.operands[i].present = 1; + i++; + skip_past_comma (&str); + po_reg_or_goto (REG_TYPE_NDQ, one_reg_only); + break; + one_reg_only: + /* Optional register operand was omitted. Unfortunately, it's in + operands[i-1] and we need it to be in inst.operands[i]. Fix that + here (this is a bit grotty). */ + inst.operands[i] = inst.operands[i-1]; + inst.operands[i-1].present = 0; + break; + try_imm: + /* There's a possibility of getting a 64-bit immediate here, so + we need special handling. */ + if (parse_big_immediate (&str, i) == FAIL) + { + inst.error = _("immediate value is out of range"); + goto failure; + } + } + break; + + case OP_RNDQ_I0: + { + po_reg_or_goto (REG_TYPE_NDQ, try_imm0); + break; + try_imm0: + po_imm_or_fail (0, 0, TRUE); + } + break; + + case OP_RVSD_I0: + po_reg_or_goto (REG_TYPE_VFSD, try_imm0); + break; + + case OP_RR_RNSC: + { + po_scalar_or_goto (8, try_rr); + break; + try_rr: + po_reg_or_fail (REG_TYPE_RN); + } + break; + + case OP_RNSDQ_RNSC: + { + po_scalar_or_goto (8, try_nsdq); + break; + try_nsdq: + po_reg_or_fail (REG_TYPE_NSDQ); + } + break; + + case OP_RNDQ_RNSC: + { + po_scalar_or_goto (8, try_ndq); + break; + try_ndq: + po_reg_or_fail (REG_TYPE_NDQ); + } + break; + + case OP_RND_RNSC: + { + po_scalar_or_goto (8, try_vfd); + break; + try_vfd: + po_reg_or_fail (REG_TYPE_VFD); + } + break; + + case OP_VMOV: + /* WARNING: parse_neon_mov can move the operand counter, i. If we're + not careful then bad things might happen. */ + po_misc_or_fail (parse_neon_mov (&str, &i) == FAIL); + break; + + case OP_RNDQ_IMVNb: + { + po_reg_or_goto (REG_TYPE_NDQ, try_mvnimm); + break; + try_mvnimm: + /* There's a possibility of getting a 64-bit immediate here, so + we need special handling. */ + if (parse_big_immediate (&str, i) == FAIL) + { + inst.error = _("immediate value is out of range"); + goto failure; + } + } + break; + + case OP_RNDQ_I63b: + { + po_reg_or_goto (REG_TYPE_NDQ, try_shimm); + break; + try_shimm: + po_imm_or_fail (0, 63, TRUE); + } + break; case OP_RRnpcb: po_char_or_fail ('['); @@ -3927,6 +5680,7 @@ parse_operands (char *str, const unsigned char *pattern) break; case OP_RRw: + case OP_oRRw: po_reg_or_fail (REG_TYPE_RN); if (skip_past_char (&str, '!') == SUCCESS) inst.operands[i].writeback = 1; @@ -3936,11 +5690,15 @@ parse_operands (char *str, const unsigned char *pattern) case OP_I7: po_imm_or_fail ( 0, 7, FALSE); break; case OP_I15: po_imm_or_fail ( 0, 15, FALSE); break; case OP_I16: po_imm_or_fail ( 1, 16, FALSE); break; + case OP_I16z: po_imm_or_fail ( 0, 16, FALSE); break; case OP_I31: po_imm_or_fail ( 0, 31, FALSE); break; case OP_I32: po_imm_or_fail ( 1, 32, FALSE); break; + case OP_I32z: po_imm_or_fail ( 0, 32, FALSE); break; case OP_I63s: po_imm_or_fail (-64, 63, FALSE); break; + case OP_I63: po_imm_or_fail ( 0, 63, FALSE); break; + case OP_I64: po_imm_or_fail ( 1, 64, FALSE); break; + case OP_I64z: po_imm_or_fail ( 0, 64, FALSE); break; case OP_I255: po_imm_or_fail ( 0, 255, FALSE); break; - case OP_Iffff: po_imm_or_fail ( 0, 0xffff, FALSE); break; case OP_I4b: po_imm_or_fail ( 1, 4, TRUE); break; case OP_oI7b: @@ -3948,6 +5706,7 @@ parse_operands (char *str, const unsigned char *pattern) case OP_I15b: po_imm_or_fail ( 0, 15, TRUE); break; case OP_oI31b: case OP_I31b: po_imm_or_fail ( 0, 31, TRUE); break; + case OP_oI32b: po_imm_or_fail ( 1, 32, TRUE); break; case OP_oIffffb: po_imm_or_fail ( 0, 0xffff, TRUE); break; /* Immediate variants */ @@ -4005,6 +5764,11 @@ parse_operands (char *str, const unsigned char *pattern) } break; + /* Operand for MOVW or MOVT. */ + case OP_HALF: + po_misc_or_fail (parse_half (&str)); + break; + /* Register or expression */ case OP_RR_EXr: po_reg_or_goto (REG_TYPE_RN, EXPr); break; case OP_RR_EXi: po_reg_or_goto (REG_TYPE_RN, EXPi); break; @@ -4027,13 +5791,17 @@ parse_operands (char *str, const unsigned char *pattern) inst.operands[i].isreg = 1; break; + case OP_RIWR_I32z: po_reg_or_goto (REG_TYPE_MMXWR, I32z); break; + I32z: po_imm_or_fail (0, 32, FALSE); break; + /* Two kinds of register */ case OP_RIWR_RIWC: { struct reg_entry *rege = arm_reg_parse_multi (&str); - if (rege->type != REG_TYPE_MMXWR - && rege->type != REG_TYPE_MMXWC - && rege->type != REG_TYPE_MMXWCG) + if (!rege + || (rege->type != REG_TYPE_MMXWR + && rege->type != REG_TYPE_MMXWC + && rege->type != REG_TYPE_MMXWCG)) { inst.error = _("iWMMXt data or control register expected"); goto failure; @@ -4043,6 +5811,21 @@ parse_operands (char *str, const unsigned char *pattern) } break; + case OP_RIWC_RIWG: + { + struct reg_entry *rege = arm_reg_parse_multi (&str); + if (!rege + || (rege->type != REG_TYPE_MMXWC + && rege->type != REG_TYPE_MMXWCG)) + { + inst.error = _("iWMMXt control register expected"); + goto failure; + } + inst.operands[i].reg = rege->number; + inst.operands[i].isreg = 1; + } + break; + /* Misc */ case OP_CPSF: val = parse_cps_flags (&str); break; case OP_ENDI: val = parse_endian_specifier (&str); break; @@ -4051,6 +5834,41 @@ parse_operands (char *str, const unsigned char *pattern) case OP_COND: val = parse_cond (&str); break; case OP_oBARRIER:val = parse_barrier (&str); break; + case OP_RVC_PSR: + po_reg_or_goto (REG_TYPE_VFC, try_psr); + inst.operands[i].isvec = 1; /* Mark VFP control reg as vector. */ + break; + try_psr: + val = parse_psr (&str); + break; + + case OP_APSR_RR: + po_reg_or_goto (REG_TYPE_RN, try_apsr); + break; + try_apsr: + /* Parse "APSR_nvzc" operand (for FMSTAT-equivalent MRS + instruction). */ + if (strncasecmp (str, "APSR_", 5) == 0) + { + unsigned found = 0; + str += 5; + while (found < 15) + switch (*str++) + { + case 'c': found = (found & 1) ? 16 : found | 1; break; + case 'n': found = (found & 2) ? 16 : found | 2; break; + case 'z': found = (found & 4) ? 16 : found | 4; break; + case 'v': found = (found & 8) ? 16 : found | 8; break; + default: found = 16; + } + if (found != 15) + goto failure; + inst.operands[i].isvec = 1; + } + else + goto failure; + break; + case OP_TB: po_misc_or_fail (parse_tb (&str)); break; @@ -4066,22 +5884,65 @@ parse_operands (char *str, const unsigned char *pattern) break; case OP_VRSLST: - val = parse_vfp_reg_list (&str, &inst.operands[i].reg, 0); + val = parse_vfp_reg_list (&str, &inst.operands[i].reg, REGLIST_VFP_S); break; case OP_VRDLST: - val = parse_vfp_reg_list (&str, &inst.operands[i].reg, 1); + val = parse_vfp_reg_list (&str, &inst.operands[i].reg, REGLIST_VFP_D); break; + case OP_VRSDLST: + /* Allow Q registers too. */ + val = parse_vfp_reg_list (&str, &inst.operands[i].reg, + REGLIST_NEON_D); + if (val == FAIL) + { + inst.error = NULL; + val = parse_vfp_reg_list (&str, &inst.operands[i].reg, + REGLIST_VFP_S); + inst.operands[i].issingle = 1; + } + break; + + case OP_NRDLST: + val = parse_vfp_reg_list (&str, &inst.operands[i].reg, + REGLIST_NEON_D); + break; + + case OP_NSTRLST: + val = parse_neon_el_struct_list (&str, &inst.operands[i].reg, + &inst.operands[i].vectype); + break; + /* Addressing modes */ case OP_ADDR: po_misc_or_fail (parse_address (&str, i)); break; + case OP_ADDRGLDR: + po_misc_or_fail_no_backtrack ( + parse_address_group_reloc (&str, i, GROUP_LDR)); + break; + + case OP_ADDRGLDRS: + po_misc_or_fail_no_backtrack ( + parse_address_group_reloc (&str, i, GROUP_LDRS)); + break; + + case OP_ADDRGLDC: + po_misc_or_fail_no_backtrack ( + parse_address_group_reloc (&str, i, GROUP_LDC)); + break; + case OP_SH: po_misc_or_fail (parse_shifter_operand (&str, i)); break; + case OP_SHG: + po_misc_or_fail_no_backtrack ( + parse_shifter_operand_group_reloc (&str, i)); + break; + case OP_oSHll: po_misc_or_fail (parse_shift (&str, i, SHIFT_LSL_IMMEDIATE)); break; @@ -4107,6 +5968,7 @@ parse_operands (char *str, const unsigned char *pattern) case OP_RRnpc: case OP_RRnpcb: case OP_RRw: + case OP_oRRw: case OP_RRnpc_I0: if (inst.operands[i].isreg && inst.operands[i].reg == REG_PC) inst.error = BAD_PC; @@ -4116,11 +5978,15 @@ parse_operands (char *str, const unsigned char *pattern) case OP_ENDI: case OP_oROR: case OP_PSR: + case OP_RVC_PSR: case OP_COND: case OP_oBARRIER: case OP_REGLST: case OP_VRSLST: case OP_VRDLST: + case OP_VRSDLST: + case OP_NRDLST: + case OP_NSTRLST: if (val == FAIL) goto failure; inst.operands[i].imm = val; @@ -4178,6 +6044,7 @@ parse_operands (char *str, const unsigned char *pattern) #undef po_reg_or_fail #undef po_reg_or_goto #undef po_imm_or_fail +#undef po_scalar_or_fail /* Shorthand macro for instruction encoding functions issuing errors. */ #define constraint(expr, err) do { \ @@ -4236,11 +6103,30 @@ encode_thumb32_immediate (unsigned int val) return FAIL; } -/* Encode a VFP SP register number into inst.instruction. */ +/* Encode a VFP SP or DP register number into inst.instruction. */ static void -encode_arm_vfp_sp_reg (int reg, enum vfp_sp_reg_pos pos) -{ +encode_arm_vfp_reg (int reg, enum vfp_reg_pos pos) +{ + if ((pos == VFP_REG_Dd || pos == VFP_REG_Dn || pos == VFP_REG_Dm) + && reg > 15) + { + if (ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v3)) + { + if (thumb_mode) + ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used, + fpu_vfp_ext_v3); + else + ARM_MERGE_FEATURE_SETS (arm_arch_used, arm_arch_used, + fpu_vfp_ext_v3); + } + else + { + first_error (_("D register out of range for selected VFP version")); + return; + } + } + switch (pos) { case VFP_REG_Sd: @@ -4255,6 +6141,18 @@ encode_arm_vfp_sp_reg (int reg, enum vfp_sp_reg_pos pos) inst.instruction |= ((reg >> 1) << 0) | ((reg & 1) << 5); break; + case VFP_REG_Dd: + inst.instruction |= ((reg & 15) << 12) | ((reg >> 4) << 22); + break; + + case VFP_REG_Dn: + inst.instruction |= ((reg & 15) << 16) | ((reg >> 4) << 7); + break; + + case VFP_REG_Dm: + inst.instruction |= (reg & 15) | ((reg >> 4) << 5); + break; + default: abort (); } @@ -4399,7 +6297,8 @@ encode_arm_addr_mode_3 (int i, bfd_boolean is_t) into a coprocessor load/store instruction. If wb_ok is false, reject use of writeback; if unind_ok is false, reject use of unindexed addressing. If reloc_override is not 0, use it instead - of BFD_ARM_CP_OFF_IMM. */ + of BFD_ARM_CP_OFF_IMM, unless the initial relocation is a group one + (in which case it is preserved). */ static int encode_arm_cp_address (int i, int wb_ok, int unind_ok, int reloc_override) @@ -4441,10 +6340,16 @@ encode_arm_cp_address (int i, int wb_ok, int unind_ok, int reloc_override) if (reloc_override) inst.reloc.type = reloc_override; - else if (thumb_mode) - inst.reloc.type = BFD_RELOC_ARM_T32_CP_OFF_IMM; - else - inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM; + else if ((inst.reloc.type < BFD_RELOC_ARM_ALU_PC_G0_NC + || inst.reloc.type > BFD_RELOC_ARM_LDC_SB_G2) + && inst.reloc.type != BFD_RELOC_ARM_LDR_PC_G0) + { + if (thumb_mode) + inst.reloc.type = BFD_RELOC_ARM_T32_CP_OFF_IMM; + else + inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM; + } + return SUCCESS; } @@ -4570,7 +6475,7 @@ static void do_rd_rm_rn (void) { unsigned Rn = inst.operands[2].reg; - /* Enforce resutrictions on SWP instruction. */ + /* Enforce restrictions on SWP instruction. */ if ((inst.instruction & 0x0fbfffff) == 0x01000090) constraint (Rn == inst.operands[0].reg || Rn == inst.operands[1].reg, _("Rn must not overlap other operands")); @@ -4894,7 +6799,11 @@ static void do_cpsi (void) { inst.instruction |= inst.operands[0].imm << 6; - inst.instruction |= inst.operands[1].imm; + if (inst.operands[1].present) + { + inst.instruction |= CPSI_MMOD; + inst.instruction |= inst.operands[1].imm; + } } static void @@ -5112,17 +7021,16 @@ do_lstc (void) static void do_mlas (void) { - /* This restriction does not apply to mls (nor to mla in v6, but - that's hard to detect at present). */ + /* This restriction does not apply to mls (nor to mla in v6 or later). */ if (inst.operands[0].reg == inst.operands[1].reg + && !ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6) && !(inst.instruction & 0x00400000)) - as_tsktsk (_("rd and rm should be different in mla")); + as_tsktsk (_("Rd and Rm should be different in mla")); inst.instruction |= inst.operands[0].reg << 16; inst.instruction |= inst.operands[1].reg; inst.instruction |= inst.operands[2].reg << 8; inst.instruction |= inst.operands[3].reg << 12; - } static void @@ -5136,15 +7044,62 @@ do_mov (void) static void do_mov16 (void) { + bfd_vma imm; + bfd_boolean top; + + top = (inst.instruction & 0x00400000) != 0; + constraint (top && inst.reloc.type == BFD_RELOC_ARM_MOVW, + _(":lower16: not allowed this instruction")); + constraint (!top && inst.reloc.type == BFD_RELOC_ARM_MOVT, + _(":upper16: not allowed instruction")); inst.instruction |= inst.operands[0].reg << 12; - /* The value is in two pieces: 0:11, 16:19. */ - inst.instruction |= (inst.operands[1].imm & 0x00000fff); - inst.instruction |= (inst.operands[1].imm & 0x0000f000) << 4; + if (inst.reloc.type == BFD_RELOC_UNUSED) + { + imm = inst.reloc.exp.X_add_number; + /* The value is in two pieces: 0:11, 16:19. */ + inst.instruction |= (imm & 0x00000fff); + inst.instruction |= (imm & 0x0000f000) << 4; + } +} + +static void do_vfp_nsyn_opcode (const char *); + +static int +do_vfp_nsyn_mrs (void) +{ + if (inst.operands[0].isvec) + { + if (inst.operands[1].reg != 1) + first_error (_("operand 1 must be FPSCR")); + memset (&inst.operands[0], '\0', sizeof (inst.operands[0])); + memset (&inst.operands[1], '\0', sizeof (inst.operands[1])); + do_vfp_nsyn_opcode ("fmstat"); + } + else if (inst.operands[1].isvec) + do_vfp_nsyn_opcode ("fmrx"); + else + return FAIL; + + return SUCCESS; +} + +static int +do_vfp_nsyn_msr (void) +{ + if (inst.operands[0].isvec) + do_vfp_nsyn_opcode ("fmxr"); + else + return FAIL; + + return SUCCESS; } static void do_mrs (void) { + if (do_vfp_nsyn_mrs () == SUCCESS) + return; + /* mrs only accepts CPSR/SPSR/CPSR_all/SPSR_all. */ constraint ((inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f)) != (PSR_c|PSR_f), @@ -5160,6 +7115,9 @@ do_mrs (void) static void do_msr (void) { + if (do_vfp_nsyn_msr () == SUCCESS) + return; + inst.instruction |= inst.operands[0].imm; if (inst.operands[1].isreg) inst.instruction |= inst.operands[1].reg; @@ -5180,8 +7138,9 @@ do_mul (void) inst.instruction |= inst.operands[1].reg; inst.instruction |= inst.operands[2].reg << 8; - if (inst.operands[0].reg == inst.operands[1].reg) - as_tsktsk (_("rd and rm should be different in mul")); + if (inst.operands[0].reg == inst.operands[1].reg + && !ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6)) + as_tsktsk (_("Rd and Rm should be different in mul")); } /* Long Multiply Parser @@ -5447,13 +7406,25 @@ do_smul (void) inst.instruction |= inst.operands[2].reg << 8; } -/* ARM V6 srs (argument parse). */ +/* ARM V6 srs (argument parse). The variable fields in the encoding are + the same for both ARM and Thumb-2. */ static void do_srs (void) { - inst.instruction |= inst.operands[0].imm; - if (inst.operands[0].writeback) + int reg; + + if (inst.operands[0].present) + { + reg = inst.operands[0].reg; + constraint (reg != 13, _("SRS base register must be r13")); + } + else + reg = 13; + + inst.instruction |= reg << 16; + inst.instruction |= inst.operands[1].imm; + if (inst.operands[0].writeback || inst.operands[1].writeback) inst.instruction |= WRITE_BACK; } @@ -5542,43 +7513,43 @@ do_sxth (void) static void do_vfp_sp_monadic (void) { - encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sd); - encode_arm_vfp_sp_reg (inst.operands[1].reg, VFP_REG_Sm); + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd); + encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sm); } static void do_vfp_sp_dyadic (void) { - encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sd); - encode_arm_vfp_sp_reg (inst.operands[1].reg, VFP_REG_Sn); - encode_arm_vfp_sp_reg (inst.operands[2].reg, VFP_REG_Sm); + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd); + encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sn); + encode_arm_vfp_reg (inst.operands[2].reg, VFP_REG_Sm); } static void do_vfp_sp_compare_z (void) { - encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sd); + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd); } static void do_vfp_dp_sp_cvt (void) { - inst.instruction |= inst.operands[0].reg << 12; - encode_arm_vfp_sp_reg (inst.operands[1].reg, VFP_REG_Sm); + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd); + encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sm); } static void do_vfp_sp_dp_cvt (void) { - encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sd); - inst.instruction |= inst.operands[1].reg; + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd); + encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dm); } static void do_vfp_reg_from_sp (void) { inst.instruction |= inst.operands[0].reg << 12; - encode_arm_vfp_sp_reg (inst.operands[1].reg, VFP_REG_Sn); + encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sn); } static void @@ -5588,13 +7559,13 @@ do_vfp_reg2_from_sp2 (void) _("only two consecutive VFP SP registers allowed here")); inst.instruction |= inst.operands[0].reg << 12; inst.instruction |= inst.operands[1].reg << 16; - encode_arm_vfp_sp_reg (inst.operands[2].reg, VFP_REG_Sm); + encode_arm_vfp_reg (inst.operands[2].reg, VFP_REG_Sm); } static void do_vfp_sp_from_reg (void) { - encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sn); + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sn); inst.instruction |= inst.operands[1].reg << 12; } @@ -5603,7 +7574,7 @@ do_vfp_sp2_from_reg2 (void) { constraint (inst.operands[0].imm != 2, _("only two consecutive VFP SP registers allowed here")); - encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sm); + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sm); inst.instruction |= inst.operands[1].reg << 12; inst.instruction |= inst.operands[2].reg << 16; } @@ -5611,14 +7582,14 @@ do_vfp_sp2_from_reg2 (void) static void do_vfp_sp_ldst (void) { - encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sd); + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd); encode_arm_cp_address (1, FALSE, TRUE, 0); } static void do_vfp_dp_ldst (void) { - inst.instruction |= inst.operands[0].reg << 12; + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd); encode_arm_cp_address (1, FALSE, TRUE, 0); } @@ -5632,7 +7603,7 @@ vfp_sp_ldstm (enum vfp_ldstm_type ldstm_type) constraint (ldstm_type != VFP_LDSTMIA, _("this addressing mode requires base-register writeback")); inst.instruction |= inst.operands[0].reg << 16; - encode_arm_vfp_sp_reg (inst.operands[1].reg, VFP_REG_Sd); + encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sd); inst.instruction |= inst.operands[1].imm; } @@ -5648,7 +7619,7 @@ vfp_dp_ldstm (enum vfp_ldstm_type ldstm_type) _("this addressing mode requires base-register writeback")); inst.instruction |= inst.operands[0].reg << 16; - inst.instruction |= inst.operands[1].reg << 12; + encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dd); count = inst.operands[1].imm << 1; if (ldstm_type == VFP_LDSTMIAX || ldstm_type == VFP_LDSTMDBX) @@ -5692,6 +7663,103 @@ do_vfp_xp_ldstmdb (void) { vfp_dp_ldstm (VFP_LDSTMDBX); } + +static void +do_vfp_dp_rd_rm (void) +{ + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd); + encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dm); +} + +static void +do_vfp_dp_rn_rd (void) +{ + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dn); + encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dd); +} + +static void +do_vfp_dp_rd_rn (void) +{ + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd); + encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dn); +} + +static void +do_vfp_dp_rd_rn_rm (void) +{ + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd); + encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dn); + encode_arm_vfp_reg (inst.operands[2].reg, VFP_REG_Dm); +} + +static void +do_vfp_dp_rd (void) +{ + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd); +} + +static void +do_vfp_dp_rm_rd_rn (void) +{ + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dm); + encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dd); + encode_arm_vfp_reg (inst.operands[2].reg, VFP_REG_Dn); +} + +/* VFPv3 instructions. */ +static void +do_vfp_sp_const (void) +{ + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd); + inst.instruction |= (inst.operands[1].imm & 0xf0) << 12; + inst.instruction |= (inst.operands[1].imm & 0x0f); +} + +static void +do_vfp_dp_const (void) +{ + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd); + inst.instruction |= (inst.operands[1].imm & 0xf0) << 12; + inst.instruction |= (inst.operands[1].imm & 0x0f); +} + +static void +vfp_conv (int srcsize) +{ + unsigned immbits = srcsize - inst.operands[1].imm; + inst.instruction |= (immbits & 1) << 5; + inst.instruction |= (immbits >> 1); +} + +static void +do_vfp_sp_conv_16 (void) +{ + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd); + vfp_conv (16); +} + +static void +do_vfp_dp_conv_16 (void) +{ + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd); + vfp_conv (16); +} + +static void +do_vfp_sp_conv_32 (void) +{ + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd); + vfp_conv (32); +} + +static void +do_vfp_dp_conv_32 (void) +{ + encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd); + vfp_conv (32); +} + /* FPA instructions. Also in a logical order. */ @@ -5740,6 +7808,7 @@ do_fpa_ldmstm (void) encode_arm_cp_address (2, TRUE, TRUE, 0); } + /* iWMMXt instructions: strictly in alphabetical order. */ @@ -5790,6 +7859,15 @@ do_iwmmxt_waligni (void) } static void +do_iwmmxt_wmerge (void) +{ + inst.instruction |= inst.operands[0].reg << 12; + inst.instruction |= inst.operands[1].reg << 16; + inst.instruction |= inst.operands[2].reg; + inst.instruction |= inst.operands[3].imm << 21; +} + +static void do_iwmmxt_wmov (void) { /* WMOV rD, rN is an alias for WOR rD, rN, rN. */ @@ -5828,7 +7906,23 @@ static void do_iwmmxt_wldstd (void) { inst.instruction |= inst.operands[0].reg << 12; - encode_arm_cp_address (1, TRUE, FALSE, 0); + if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_iwmmxt2) + && inst.operands[1].immisreg) + { + inst.instruction &= ~0x1a000ff; + inst.instruction |= (0xf << 28); + if (inst.operands[1].preind) + inst.instruction |= PRE_INDEX; + if (!inst.operands[1].negative) + inst.instruction |= INDEX_UP; + if (inst.operands[1].writeback) + inst.instruction |= WRITE_BACK; + inst.instruction |= inst.operands[1].reg << 16; + inst.instruction |= inst.reloc.exp.X_add_number << 4; + inst.instruction |= inst.operands[1].imm; + } + else + encode_arm_cp_address (1, TRUE, FALSE, 0); } static void @@ -5848,6 +7942,56 @@ do_iwmmxt_wzero (void) inst.instruction |= inst.operands[0].reg << 12; inst.instruction |= inst.operands[0].reg << 16; } + +static void +do_iwmmxt_wrwrwr_or_imm5 (void) +{ + if (inst.operands[2].isreg) + do_rd_rn_rm (); + else { + constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_iwmmxt2), + _("immediate operand requires iWMMXt2")); + do_rd_rn (); + if (inst.operands[2].imm == 0) + { + switch ((inst.instruction >> 20) & 0xf) + { + case 4: + case 5: + case 6: + case 7: + /* w...h wrd, wrn, #0 -> wrorh wrd, wrn, #16. */ + inst.operands[2].imm = 16; + inst.instruction = (inst.instruction & 0xff0fffff) | (0x7 << 20); + break; + case 8: + case 9: + case 10: + case 11: + /* w...w wrd, wrn, #0 -> wrorw wrd, wrn, #32. */ + inst.operands[2].imm = 32; + inst.instruction = (inst.instruction & 0xff0fffff) | (0xb << 20); + break; + case 12: + case 13: + case 14: + case 15: + { + /* w...d wrd, wrn, #0 -> wor wrd, wrn, wrn. */ + unsigned long wrn; + wrn = (inst.instruction >> 16) & 0xf; + inst.instruction &= 0xff0fff0f; + inst.instruction |= wrn; + /* Bail out here; the instruction is now assembled. */ + return; + } + } + } + /* Map 32 -> 0, etc. */ + inst.operands[2].imm &= 0x1f; + inst.instruction |= (0xf << 28) | ((inst.operands[2].imm & 0x10) << 4) | (inst.operands[2].imm & 0xf); + } +} /* Cirrus Maverick instructions. Simple 2-, 3-, and 4-register operations first, then control, shift, and load/store. */ @@ -6082,7 +8226,7 @@ encode_thumb32_addr_mode (int i, bfd_boolean is_t, bfd_boolean is_d) X(cpsie, b660, f3af8400), \ X(cpsid, b670, f3af8600), \ X(cpy, 4600, ea4f0000), \ - X(dec_sp,80dd, f1bd0d00), \ + X(dec_sp,80dd, f1ad0d00), \ X(eor, 4040, ea800000), \ X(eors, 4040, ea900000), \ X(inc_sp,00dd, f10d0d00), \ @@ -6200,13 +8344,13 @@ do_t_add_sub (void) narrow = (current_it_mask != 0); if (!inst.operands[2].isreg) { + int add; + + add = (inst.instruction == T_MNEM_add + || inst.instruction == T_MNEM_adds); opcode = 0; if (inst.size_req != 4) { - int add; - - add = (inst.instruction == T_MNEM_add - || inst.instruction == T_MNEM_adds); /* Attempt to use a narrow opcode, with relaxation if appropriate. */ if (Rd == REG_SP && Rs == REG_SP && !flags) @@ -6236,12 +8380,38 @@ do_t_add_sub (void) if (inst.size_req == 4 || (inst.size_req != 2 && !opcode)) { - /* ??? Convert large immediates to addw/subw. */ - inst.instruction = THUMB_OP32 (inst.instruction); - inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000; - inst.instruction |= inst.operands[0].reg << 8; - inst.instruction |= inst.operands[1].reg << 16; - inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE; + if (Rd == REG_PC) + { + constraint (Rs != REG_LR || inst.instruction != T_MNEM_subs, + _("only SUBS PC, LR, #const allowed")); + constraint (inst.reloc.exp.X_op != O_constant, + _("expression too complex")); + constraint (inst.reloc.exp.X_add_number < 0 + || inst.reloc.exp.X_add_number > 0xff, + _("immediate value out of range")); + inst.instruction = T2_SUBS_PC_LR + | inst.reloc.exp.X_add_number; + inst.reloc.type = BFD_RELOC_UNUSED; + return; + } + else if (Rs == REG_PC) + { + /* Always use addw/subw. */ + inst.instruction = add ? 0xf20f0000 : 0xf2af0000; + inst.reloc.type = BFD_RELOC_ARM_T32_IMM12; + } + else + { + inst.instruction = THUMB_OP32 (inst.instruction); + inst.instruction = (inst.instruction & 0xe1ffffff) + | 0x10000000; + if (flags) + inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE; + else + inst.reloc.type = BFD_RELOC_ARM_T32_ADD_IMM; + } + inst.instruction |= Rd << 8; + inst.instruction |= Rs << 16; } } else @@ -6807,7 +8977,7 @@ do_t_cpy (void) } static void -do_t_czb (void) +do_t_cbz (void) { constraint (current_it_mask, BAD_NOT_IT); constraint (inst.operands[0].reg > 7, BAD_HIREG); @@ -6871,6 +9041,68 @@ do_t_it (void) inst.instruction |= cond << 4; } +/* Helper function used for both push/pop and ldm/stm. */ +static void +encode_thumb2_ldmstm (int base, unsigned mask, bfd_boolean writeback) +{ + bfd_boolean load; + + load = (inst.instruction & (1 << 20)) != 0; + + if (mask & (1 << 13)) + inst.error = _("SP not allowed in register list"); + if (load) + { + if (mask & (1 << 14) + && mask & (1 << 15)) + inst.error = _("LR and PC should not both be in register list"); + + if ((mask & (1 << base)) != 0 + && writeback) + as_warn (_("base register should not be in register list " + "when written back")); + } + else + { + if (mask & (1 << 15)) + inst.error = _("PC not allowed in register list"); + + if (mask & (1 << base)) + as_warn (_("value stored for r%d is UNPREDICTABLE"), base); + } + + if ((mask & (mask - 1)) == 0) + { + /* Single register transfers implemented as str/ldr. */ + if (writeback) + { + if (inst.instruction & (1 << 23)) + inst.instruction = 0x00000b04; /* ia! -> [base], #4 */ + else + inst.instruction = 0x00000d04; /* db! -> [base, #-4]! */ + } + else + { + if (inst.instruction & (1 << 23)) + inst.instruction = 0x00800000; /* ia -> [base] */ + else + inst.instruction = 0x00000c04; /* db -> [base, #-4] */ + } + + inst.instruction |= 0xf8400000; + if (load) + inst.instruction |= 0x00100000; + + mask = ffs(mask) - 1; + mask <<= 12; + } + else if (writeback) + inst.instruction |= WRITE_BACK; + + inst.instruction |= mask; + inst.instruction |= base << 16; +} + static void do_t_ldmstm (void) { @@ -6882,60 +9114,60 @@ do_t_ldmstm (void) if (unified_syntax) { + bfd_boolean narrow; + unsigned mask; + + narrow = FALSE; /* See if we can use a 16-bit instruction. */ if (inst.instruction < 0xffff /* not ldmdb/stmdb */ && inst.size_req != 4 - && inst.operands[0].reg <= 7 - && !(inst.operands[1].imm & ~0xff) - && (inst.instruction == T_MNEM_stmia - ? inst.operands[0].writeback - : (inst.operands[0].writeback - == !(inst.operands[1].imm & (1 << inst.operands[0].reg))))) - { - if (inst.instruction == T_MNEM_stmia - && (inst.operands[1].imm & (1 << inst.operands[0].reg)) - && (inst.operands[1].imm & ((1 << inst.operands[0].reg) - 1))) - as_warn (_("value stored for r%d is UNPREDICTABLE"), - inst.operands[0].reg); - - inst.instruction = THUMB_OP16 (inst.instruction); - inst.instruction |= inst.operands[0].reg << 8; - inst.instruction |= inst.operands[1].imm; - } - else + && !(inst.operands[1].imm & ~0xff)) { - if (inst.operands[1].imm & (1 << 13)) - as_warn (_("SP should not be in register list")); - if (inst.instruction == T_MNEM_stmia) + mask = 1 << inst.operands[0].reg; + + if (inst.operands[0].reg <= 7 + && (inst.instruction == T_MNEM_stmia + ? inst.operands[0].writeback + : (inst.operands[0].writeback + == !(inst.operands[1].imm & mask)))) { - if (inst.operands[1].imm & (1 << 15)) - as_warn (_("PC should not be in register list")); - if (inst.operands[1].imm & (1 << inst.operands[0].reg)) + if (inst.instruction == T_MNEM_stmia + && (inst.operands[1].imm & mask) + && (inst.operands[1].imm & (mask - 1))) as_warn (_("value stored for r%d is UNPREDICTABLE"), inst.operands[0].reg); + + inst.instruction = THUMB_OP16 (inst.instruction); + inst.instruction |= inst.operands[0].reg << 8; + inst.instruction |= inst.operands[1].imm; + narrow = TRUE; } - else + else if (inst.operands[0] .reg == REG_SP + && inst.operands[0].writeback) { - if (inst.operands[1].imm & (1 << 14) - && inst.operands[1].imm & (1 << 15)) - as_warn (_("LR and PC should not both be in register list")); - if ((inst.operands[1].imm & (1 << inst.operands[0].reg)) - && inst.operands[0].writeback) - as_warn (_("base register should not be in register list " - "when written back")); + inst.instruction = THUMB_OP16 (inst.instruction == T_MNEM_stmia + ? T_MNEM_push : T_MNEM_pop); + inst.instruction |= inst.operands[1].imm; + narrow = TRUE; } + } + + if (!narrow) + { if (inst.instruction < 0xffff) inst.instruction = THUMB_OP32 (inst.instruction); - inst.instruction |= inst.operands[0].reg << 16; - inst.instruction |= inst.operands[1].imm; - if (inst.operands[0].writeback) - inst.instruction |= WRITE_BACK; + + encode_thumb2_ldmstm(inst.operands[0].reg, inst.operands[1].imm, + inst.operands[0].writeback); } } else { constraint (inst.operands[0].reg > 7 || (inst.operands[1].imm & ~0xff), BAD_HIREG); + constraint (inst.instruction != T_MNEM_ldmia + && inst.instruction != T_MNEM_stmia, + _("Thumb-2 instruction only valid in unified syntax")); if (inst.instruction == T_MNEM_stmia) { if (!inst.operands[0].writeback) @@ -7209,6 +9441,16 @@ do_t_mov_cmp (void) || inst.operands[1].shifted) narrow = FALSE; + /* MOVS PC, LR is encoded as SUBS PC, LR, #0. */ + if (opcode == T_MNEM_movs && inst.operands[1].isreg + && !inst.operands[1].shifted + && inst.operands[0].reg == REG_PC + && inst.operands[1].reg == REG_LR) + { + inst.instruction = T2_SUBS_PC_LR; + return; + } + if (!inst.operands[1].isreg) { /* Immediate operand. */ @@ -7231,11 +9473,98 @@ do_t_mov_cmp (void) inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE; } } + else if (inst.operands[1].shifted && inst.operands[1].immisreg + && (inst.instruction == T_MNEM_mov + || inst.instruction == T_MNEM_movs)) + { + /* Register shifts are encoded as separate shift instructions. */ + bfd_boolean flags = (inst.instruction == T_MNEM_movs); + + if (current_it_mask) + narrow = !flags; + else + narrow = flags; + + if (inst.size_req == 4) + narrow = FALSE; + + if (!low_regs || inst.operands[1].imm > 7) + narrow = FALSE; + + if (inst.operands[0].reg != inst.operands[1].reg) + narrow = FALSE; + + switch (inst.operands[1].shift_kind) + { + case SHIFT_LSL: + opcode = narrow ? T_OPCODE_LSL_R : THUMB_OP32 (T_MNEM_lsl); + break; + case SHIFT_ASR: + opcode = narrow ? T_OPCODE_ASR_R : THUMB_OP32 (T_MNEM_asr); + break; + case SHIFT_LSR: + opcode = narrow ? T_OPCODE_LSR_R : THUMB_OP32 (T_MNEM_lsr); + break; + case SHIFT_ROR: + opcode = narrow ? T_OPCODE_ROR_R : THUMB_OP32 (T_MNEM_ror); + break; + default: + abort(); + } + + inst.instruction = opcode; + if (narrow) + { + inst.instruction |= inst.operands[0].reg; + inst.instruction |= inst.operands[1].imm << 3; + } + else + { + if (flags) + inst.instruction |= CONDS_BIT; + + inst.instruction |= inst.operands[0].reg << 8; + inst.instruction |= inst.operands[1].reg << 16; + inst.instruction |= inst.operands[1].imm; + } + } else if (!narrow) { - inst.instruction = THUMB_OP32 (inst.instruction); - inst.instruction |= inst.operands[0].reg << r0off; - encode_thumb32_shifted_operand (1); + /* Some mov with immediate shift have narrow variants. + Register shifts are handled above. */ + if (low_regs && inst.operands[1].shifted + && (inst.instruction == T_MNEM_mov + || inst.instruction == T_MNEM_movs)) + { + if (current_it_mask) + narrow = (inst.instruction == T_MNEM_mov); + else + narrow = (inst.instruction == T_MNEM_movs); + } + + if (narrow) + { + switch (inst.operands[1].shift_kind) + { + case SHIFT_LSL: inst.instruction = T_OPCODE_LSL_I; break; + case SHIFT_LSR: inst.instruction = T_OPCODE_LSR_I; break; + case SHIFT_ASR: inst.instruction = T_OPCODE_ASR_I; break; + default: narrow = FALSE; break; + } + } + + if (narrow) + { + inst.instruction |= inst.operands[0].reg; + inst.instruction |= inst.operands[1].reg << 3; + inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT; + } + else + { + inst.instruction = THUMB_OP32 (inst.instruction); + inst.instruction |= inst.operands[0].reg << r0off; + encode_thumb32_shifted_operand (1); + } } else switch (inst.instruction) @@ -7310,11 +9639,30 @@ do_t_mov_cmp (void) static void do_t_mov16 (void) { + bfd_vma imm; + bfd_boolean top; + + top = (inst.instruction & 0x00800000) != 0; + if (inst.reloc.type == BFD_RELOC_ARM_MOVW) + { + constraint (top, _(":lower16: not allowed this instruction")); + inst.reloc.type = BFD_RELOC_ARM_THUMB_MOVW; + } + else if (inst.reloc.type == BFD_RELOC_ARM_MOVT) + { + constraint (!top, _(":upper16: not allowed this instruction")); + inst.reloc.type = BFD_RELOC_ARM_THUMB_MOVT; + } + inst.instruction |= inst.operands[0].reg << 8; - inst.instruction |= (inst.operands[1].imm & 0xf000) << 4; - inst.instruction |= (inst.operands[1].imm & 0x0800) << 15; - inst.instruction |= (inst.operands[1].imm & 0x0700) << 4; - inst.instruction |= (inst.operands[1].imm & 0x00ff); + if (inst.reloc.type == BFD_RELOC_UNUSED) + { + imm = inst.reloc.exp.X_add_number; + inst.instruction |= (imm & 0xf000) << 4; + inst.instruction |= (imm & 0x0800) << 15; + inst.instruction |= (imm & 0x0700) << 4; + inst.instruction |= (imm & 0x00ff); + } } static void @@ -7388,6 +9736,10 @@ static void do_t_mrs (void) { int flags; + + if (do_vfp_nsyn_mrs () == SUCCESS) + return; + flags = inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f|SPSR_BIT); if (flags == 0) { @@ -7415,6 +9767,9 @@ do_t_msr (void) { int flags; + if (do_vfp_nsyn_msr () == SUCCESS) + return; + constraint (!inst.operands[1].isreg, _("Thumb encoding does not support an immediate here")); flags = inst.operands[0].imm; @@ -7589,7 +9944,7 @@ do_t_push_pop (void) mask = inst.operands[0].imm; if ((mask & ~0xff) == 0) - inst.instruction = THUMB_OP16 (inst.instruction); + inst.instruction = THUMB_OP16 (inst.instruction) | mask; else if ((inst.instruction == T_MNEM_push && (mask & ~0xff) == 1 << REG_LR) || (inst.instruction == T_MNEM_pop @@ -7597,43 +9952,18 @@ do_t_push_pop (void) { inst.instruction = THUMB_OP16 (inst.instruction); inst.instruction |= THUMB_PP_PC_LR; - mask &= 0xff; + inst.instruction |= mask & 0xff; } else if (unified_syntax) { - if (mask & (1 << 13)) - inst.error = _("SP not allowed in register list"); - if (inst.instruction == T_MNEM_push) - { - if (mask & (1 << 15)) - inst.error = _("PC not allowed in register list"); - } - else - { - if (mask & (1 << 14) - && mask & (1 << 15)) - inst.error = _("LR and PC should not both be in register list"); - } - if ((mask & (mask - 1)) == 0) - { - /* Single register push/pop implemented as str/ldr. */ - if (inst.instruction == T_MNEM_push) - inst.instruction = 0xf84d0d04; /* str reg, [sp, #-4]! */ - else - inst.instruction = 0xf85d0b04; /* ldr reg, [sp], #4 */ - mask = ffs(mask) - 1; - mask <<= 12; - } - else - inst.instruction = THUMB_OP32 (inst.instruction); + inst.instruction = THUMB_OP32 (inst.instruction); + encode_thumb2_ldmstm(13, mask, TRUE); } else { inst.error = _("invalid register list to push/pop instruction"); return; } - - inst.instruction |= mask; } static void @@ -7678,8 +10008,37 @@ do_t_rsb (void) inst.instruction |= Rs << 16; if (!inst.operands[2].isreg) { - inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000; - inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE; + bfd_boolean narrow; + + if ((inst.instruction & 0x00100000) != 0) + narrow = (current_it_mask == 0); + else + narrow = (current_it_mask != 0); + + if (Rd > 7 || Rs > 7) + narrow = FALSE; + + if (inst.size_req == 4 || !unified_syntax) + narrow = FALSE; + + if (inst.reloc.exp.X_op != O_constant + || inst.reloc.exp.X_add_number != 0) + narrow = FALSE; + + /* Turn rsb #0 into 16-bit neg. We should probably do this via + relaxation, but it doesn't seem worth the hassle. */ + if (narrow) + { + inst.reloc.type = BFD_RELOC_UNUSED; + inst.instruction = THUMB_OP16 (T_MNEM_negs); + inst.instruction |= Rs << 3; + inst.instruction |= Rd; + } + else + { + inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000; + inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE; + } } else encode_thumb32_shifted_operand (2); @@ -7997,6 +10356,3387 @@ do_t_usat16 (void) inst.instruction |= inst.operands[1].imm; inst.instruction |= inst.operands[2].reg << 16; } + +/* Neon instruction encoder helpers. */ + +/* Encodings for the different types for various Neon opcodes. */ + +/* An "invalid" code for the following tables. */ +#define N_INV -1u + +struct neon_tab_entry +{ + unsigned integer; + unsigned float_or_poly; + unsigned scalar_or_imm; +}; + +/* Map overloaded Neon opcodes to their respective encodings. */ +#define NEON_ENC_TAB \ + X(vabd, 0x0000700, 0x1200d00, N_INV), \ + X(vmax, 0x0000600, 0x0000f00, N_INV), \ + X(vmin, 0x0000610, 0x0200f00, N_INV), \ + X(vpadd, 0x0000b10, 0x1000d00, N_INV), \ + X(vpmax, 0x0000a00, 0x1000f00, N_INV), \ + X(vpmin, 0x0000a10, 0x1200f00, N_INV), \ + X(vadd, 0x0000800, 0x0000d00, N_INV), \ + X(vsub, 0x1000800, 0x0200d00, N_INV), \ + X(vceq, 0x1000810, 0x0000e00, 0x1b10100), \ + X(vcge, 0x0000310, 0x1000e00, 0x1b10080), \ + X(vcgt, 0x0000300, 0x1200e00, 0x1b10000), \ + /* Register variants of the following two instructions are encoded as + vcge / vcgt with the operands reversed. */ \ + X(vclt, 0x0000300, 0x1200e00, 0x1b10200), \ + X(vcle, 0x0000310, 0x1000e00, 0x1b10180), \ + X(vmla, 0x0000900, 0x0000d10, 0x0800040), \ + X(vmls, 0x1000900, 0x0200d10, 0x0800440), \ + X(vmul, 0x0000910, 0x1000d10, 0x0800840), \ + X(vmull, 0x0800c00, 0x0800e00, 0x0800a40), /* polynomial not float. */ \ + X(vmlal, 0x0800800, N_INV, 0x0800240), \ + X(vmlsl, 0x0800a00, N_INV, 0x0800640), \ + X(vqdmlal, 0x0800900, N_INV, 0x0800340), \ + X(vqdmlsl, 0x0800b00, N_INV, 0x0800740), \ + X(vqdmull, 0x0800d00, N_INV, 0x0800b40), \ + X(vqdmulh, 0x0000b00, N_INV, 0x0800c40), \ + X(vqrdmulh, 0x1000b00, N_INV, 0x0800d40), \ + X(vshl, 0x0000400, N_INV, 0x0800510), \ + X(vqshl, 0x0000410, N_INV, 0x0800710), \ + X(vand, 0x0000110, N_INV, 0x0800030), \ + X(vbic, 0x0100110, N_INV, 0x0800030), \ + X(veor, 0x1000110, N_INV, N_INV), \ + X(vorn, 0x0300110, N_INV, 0x0800010), \ + X(vorr, 0x0200110, N_INV, 0x0800010), \ + X(vmvn, 0x1b00580, N_INV, 0x0800030), \ + X(vshll, 0x1b20300, N_INV, 0x0800a10), /* max shift, immediate. */ \ + X(vcvt, 0x1b30600, N_INV, 0x0800e10), /* integer, fixed-point. */ \ + X(vdup, 0xe800b10, N_INV, 0x1b00c00), /* arm, scalar. */ \ + X(vld1, 0x0200000, 0x0a00000, 0x0a00c00), /* interlv, lane, dup. */ \ + X(vst1, 0x0000000, 0x0800000, N_INV), \ + X(vld2, 0x0200100, 0x0a00100, 0x0a00d00), \ + X(vst2, 0x0000100, 0x0800100, N_INV), \ + X(vld3, 0x0200200, 0x0a00200, 0x0a00e00), \ + X(vst3, 0x0000200, 0x0800200, N_INV), \ + X(vld4, 0x0200300, 0x0a00300, 0x0a00f00), \ + X(vst4, 0x0000300, 0x0800300, N_INV), \ + X(vmovn, 0x1b20200, N_INV, N_INV), \ + X(vtrn, 0x1b20080, N_INV, N_INV), \ + X(vqmovn, 0x1b20200, N_INV, N_INV), \ + X(vqmovun, 0x1b20240, N_INV, N_INV), \ + X(vnmul, 0xe200a40, 0xe200b40, N_INV), \ + X(vnmla, 0xe000a40, 0xe000b40, N_INV), \ + X(vnmls, 0xe100a40, 0xe100b40, N_INV), \ + X(vcmp, 0xeb40a40, 0xeb40b40, N_INV), \ + X(vcmpz, 0xeb50a40, 0xeb50b40, N_INV), \ + X(vcmpe, 0xeb40ac0, 0xeb40bc0, N_INV), \ + X(vcmpez, 0xeb50ac0, 0xeb50bc0, N_INV) + +enum neon_opc +{ +#define X(OPC,I,F,S) N_MNEM_##OPC +NEON_ENC_TAB +#undef X +}; + +static const struct neon_tab_entry neon_enc_tab[] = +{ +#define X(OPC,I,F,S) { (I), (F), (S) } +NEON_ENC_TAB +#undef X +}; + +#define NEON_ENC_INTEGER(X) (neon_enc_tab[(X) & 0x0fffffff].integer) +#define NEON_ENC_ARMREG(X) (neon_enc_tab[(X) & 0x0fffffff].integer) +#define NEON_ENC_POLY(X) (neon_enc_tab[(X) & 0x0fffffff].float_or_poly) +#define NEON_ENC_FLOAT(X) (neon_enc_tab[(X) & 0x0fffffff].float_or_poly) +#define NEON_ENC_SCALAR(X) (neon_enc_tab[(X) & 0x0fffffff].scalar_or_imm) +#define NEON_ENC_IMMED(X) (neon_enc_tab[(X) & 0x0fffffff].scalar_or_imm) +#define NEON_ENC_INTERLV(X) (neon_enc_tab[(X) & 0x0fffffff].integer) +#define NEON_ENC_LANE(X) (neon_enc_tab[(X) & 0x0fffffff].float_or_poly) +#define NEON_ENC_DUP(X) (neon_enc_tab[(X) & 0x0fffffff].scalar_or_imm) +#define NEON_ENC_SINGLE(X) \ + ((neon_enc_tab[(X) & 0x0fffffff].integer) | ((X) & 0xf0000000)) +#define NEON_ENC_DOUBLE(X) \ + ((neon_enc_tab[(X) & 0x0fffffff].float_or_poly) | ((X) & 0xf0000000)) + +/* Define shapes for instruction operands. The following mnemonic characters + are used in this table: + + F - VFP S<n> register + D - Neon D<n> register + Q - Neon Q<n> register + I - Immediate + S - Scalar + R - ARM register + L - D<n> register list + + This table is used to generate various data: + - enumerations of the form NS_DDR to be used as arguments to + neon_select_shape. + - a table classifying shapes into single, double, quad, mixed. + - a table used to drive neon_select_shape. +*/ + +#define NEON_SHAPE_DEF \ + X(3, (D, D, D), DOUBLE), \ + X(3, (Q, Q, Q), QUAD), \ + X(3, (D, D, I), DOUBLE), \ + X(3, (Q, Q, I), QUAD), \ + X(3, (D, D, S), DOUBLE), \ + X(3, (Q, Q, S), QUAD), \ + X(2, (D, D), DOUBLE), \ + X(2, (Q, Q), QUAD), \ + X(2, (D, S), DOUBLE), \ + X(2, (Q, S), QUAD), \ + X(2, (D, R), DOUBLE), \ + X(2, (Q, R), QUAD), \ + X(2, (D, I), DOUBLE), \ + X(2, (Q, I), QUAD), \ + X(3, (D, L, D), DOUBLE), \ + X(2, (D, Q), MIXED), \ + X(2, (Q, D), MIXED), \ + X(3, (D, Q, I), MIXED), \ + X(3, (Q, D, I), MIXED), \ + X(3, (Q, D, D), MIXED), \ + X(3, (D, Q, Q), MIXED), \ + X(3, (Q, Q, D), MIXED), \ + X(3, (Q, D, S), MIXED), \ + X(3, (D, Q, S), MIXED), \ + X(4, (D, D, D, I), DOUBLE), \ + X(4, (Q, Q, Q, I), QUAD), \ + X(2, (F, F), SINGLE), \ + X(3, (F, F, F), SINGLE), \ + X(2, (F, I), SINGLE), \ + X(2, (F, D), MIXED), \ + X(2, (D, F), MIXED), \ + X(3, (F, F, I), MIXED), \ + X(4, (R, R, F, F), SINGLE), \ + X(4, (F, F, R, R), SINGLE), \ + X(3, (D, R, R), DOUBLE), \ + X(3, (R, R, D), DOUBLE), \ + X(2, (S, R), SINGLE), \ + X(2, (R, S), SINGLE), \ + X(2, (F, R), SINGLE), \ + X(2, (R, F), SINGLE) + +#define S2(A,B) NS_##A##B +#define S3(A,B,C) NS_##A##B##C +#define S4(A,B,C,D) NS_##A##B##C##D + +#define X(N, L, C) S##N L + +enum neon_shape +{ + NEON_SHAPE_DEF, + NS_NULL +}; + +#undef X +#undef S2 +#undef S3 +#undef S4 + +enum neon_shape_class +{ + SC_SINGLE, + SC_DOUBLE, + SC_QUAD, + SC_MIXED +}; + +#define X(N, L, C) SC_##C + +static enum neon_shape_class neon_shape_class[] = +{ + NEON_SHAPE_DEF +}; + +#undef X + +enum neon_shape_el +{ + SE_F, + SE_D, + SE_Q, + SE_I, + SE_S, + SE_R, + SE_L +}; + +/* Register widths of above. */ +static unsigned neon_shape_el_size[] = +{ + 32, + 64, + 128, + 0, + 32, + 32, + 0 +}; + +struct neon_shape_info +{ + unsigned els; + enum neon_shape_el el[NEON_MAX_TYPE_ELS]; +}; + +#define S2(A,B) { SE_##A, SE_##B } +#define S3(A,B,C) { SE_##A, SE_##B, SE_##C } +#define S4(A,B,C,D) { SE_##A, SE_##B, SE_##C, SE_##D } + +#define X(N, L, C) { N, S##N L } + +static struct neon_shape_info neon_shape_tab[] = +{ + NEON_SHAPE_DEF +}; + +#undef X +#undef S2 +#undef S3 +#undef S4 + +/* Bit masks used in type checking given instructions. + 'N_EQK' means the type must be the same as (or based on in some way) the key + type, which itself is marked with the 'N_KEY' bit. If the 'N_EQK' bit is + set, various other bits can be set as well in order to modify the meaning of + the type constraint. */ + +enum neon_type_mask +{ + N_S8 = 0x000001, + N_S16 = 0x000002, + N_S32 = 0x000004, + N_S64 = 0x000008, + N_U8 = 0x000010, + N_U16 = 0x000020, + N_U32 = 0x000040, + N_U64 = 0x000080, + N_I8 = 0x000100, + N_I16 = 0x000200, + N_I32 = 0x000400, + N_I64 = 0x000800, + N_8 = 0x001000, + N_16 = 0x002000, + N_32 = 0x004000, + N_64 = 0x008000, + N_P8 = 0x010000, + N_P16 = 0x020000, + N_F32 = 0x040000, + N_F64 = 0x080000, + N_KEY = 0x100000, /* key element (main type specifier). */ + N_EQK = 0x200000, /* given operand has the same type & size as the key. */ + N_VFP = 0x400000, /* VFP mode: operand size must match register width. */ + N_DBL = 0x000001, /* if N_EQK, this operand is twice the size. */ + N_HLF = 0x000002, /* if N_EQK, this operand is half the size. */ + N_SGN = 0x000004, /* if N_EQK, this operand is forced to be signed. */ + N_UNS = 0x000008, /* if N_EQK, this operand is forced to be unsigned. */ + N_INT = 0x000010, /* if N_EQK, this operand is forced to be integer. */ + N_FLT = 0x000020, /* if N_EQK, this operand is forced to be float. */ + N_SIZ = 0x000040, /* if N_EQK, this operand is forced to be size-only. */ + N_UTYP = 0, + N_MAX_NONSPECIAL = N_F64 +}; + +#define N_ALLMODS (N_DBL | N_HLF | N_SGN | N_UNS | N_INT | N_FLT | N_SIZ) + +#define N_SU_ALL (N_S8 | N_S16 | N_S32 | N_S64 | N_U8 | N_U16 | N_U32 | N_U64) +#define N_SU_32 (N_S8 | N_S16 | N_S32 | N_U8 | N_U16 | N_U32) +#define N_SU_16_64 (N_S16 | N_S32 | N_S64 | N_U16 | N_U32 | N_U64) +#define N_SUF_32 (N_SU_32 | N_F32) +#define N_I_ALL (N_I8 | N_I16 | N_I32 | N_I64) +#define N_IF_32 (N_I8 | N_I16 | N_I32 | N_F32) + +/* Pass this as the first type argument to neon_check_type to ignore types + altogether. */ +#define N_IGNORE_TYPE (N_KEY | N_EQK) + +/* Select a "shape" for the current instruction (describing register types or + sizes) from a list of alternatives. Return NS_NULL if the current instruction + doesn't fit. For non-polymorphic shapes, checking is usually done as a + function of operand parsing, so this function doesn't need to be called. + Shapes should be listed in order of decreasing length. */ + +static enum neon_shape +neon_select_shape (enum neon_shape shape, ...) +{ + va_list ap; + enum neon_shape first_shape = shape; + + /* Fix missing optional operands. FIXME: we don't know at this point how + many arguments we should have, so this makes the assumption that we have + > 1. This is true of all current Neon opcodes, I think, but may not be + true in the future. */ + if (!inst.operands[1].present) + inst.operands[1] = inst.operands[0]; + + va_start (ap, shape); + + for (; shape != NS_NULL; shape = va_arg (ap, int)) + { + unsigned j; + int matches = 1; + + for (j = 0; j < neon_shape_tab[shape].els; j++) + { + if (!inst.operands[j].present) + { + matches = 0; + break; + } + + switch (neon_shape_tab[shape].el[j]) + { + case SE_F: + if (!(inst.operands[j].isreg + && inst.operands[j].isvec + && inst.operands[j].issingle + && !inst.operands[j].isquad)) + matches = 0; + break; + + case SE_D: + if (!(inst.operands[j].isreg + && inst.operands[j].isvec + && !inst.operands[j].isquad + && !inst.operands[j].issingle)) + matches = 0; + break; + + case SE_R: + if (!(inst.operands[j].isreg + && !inst.operands[j].isvec)) + matches = 0; + break; + + case SE_Q: + if (!(inst.operands[j].isreg + && inst.operands[j].isvec + && inst.operands[j].isquad + && !inst.operands[j].issingle)) + matches = 0; + break; + + case SE_I: + if (!(!inst.operands[j].isreg + && !inst.operands[j].isscalar)) + matches = 0; + break; + + case SE_S: + if (!(!inst.operands[j].isreg + && inst.operands[j].isscalar)) + matches = 0; + break; + + case SE_L: + break; + } + } + if (matches) + break; + } + + va_end (ap); + + if (shape == NS_NULL && first_shape != NS_NULL) + first_error (_("invalid instruction shape")); + + return shape; +} + +/* True if SHAPE is predominantly a quadword operation (most of the time, this + means the Q bit should be set). */ + +static int +neon_quad (enum neon_shape shape) +{ + return neon_shape_class[shape] == SC_QUAD; +} + +static void +neon_modify_type_size (unsigned typebits, enum neon_el_type *g_type, + unsigned *g_size) +{ + /* Allow modification to be made to types which are constrained to be + based on the key element, based on bits set alongside N_EQK. */ + if ((typebits & N_EQK) != 0) + { + if ((typebits & N_HLF) != 0) + *g_size /= 2; + else if ((typebits & N_DBL) != 0) + *g_size *= 2; + if ((typebits & N_SGN) != 0) + *g_type = NT_signed; + else if ((typebits & N_UNS) != 0) + *g_type = NT_unsigned; + else if ((typebits & N_INT) != 0) + *g_type = NT_integer; + else if ((typebits & N_FLT) != 0) + *g_type = NT_float; + else if ((typebits & N_SIZ) != 0) + *g_type = NT_untyped; + } +} + +/* Return operand OPNO promoted by bits set in THISARG. KEY should be the "key" + operand type, i.e. the single type specified in a Neon instruction when it + is the only one given. */ + +static struct neon_type_el +neon_type_promote (struct neon_type_el *key, unsigned thisarg) +{ + struct neon_type_el dest = *key; + + assert ((thisarg & N_EQK) != 0); + + neon_modify_type_size (thisarg, &dest.type, &dest.size); + + return dest; +} + +/* Convert Neon type and size into compact bitmask representation. */ + +static enum neon_type_mask +type_chk_of_el_type (enum neon_el_type type, unsigned size) +{ + switch (type) + { + case NT_untyped: + switch (size) + { + case 8: return N_8; + case 16: return N_16; + case 32: return N_32; + case 64: return N_64; + default: ; + } + break; + + case NT_integer: + switch (size) + { + case 8: return N_I8; + case 16: return N_I16; + case 32: return N_I32; + case 64: return N_I64; + default: ; + } + break; + + case NT_float: + switch (size) + { + case 32: return N_F32; + case 64: return N_F64; + default: ; + } + break; + + case NT_poly: + switch (size) + { + case 8: return N_P8; + case 16: return N_P16; + default: ; + } + break; + + case NT_signed: + switch (size) + { + case 8: return N_S8; + case 16: return N_S16; + case 32: return N_S32; + case 64: return N_S64; + default: ; + } + break; + + case NT_unsigned: + switch (size) + { + case 8: return N_U8; + case 16: return N_U16; + case 32: return N_U32; + case 64: return N_U64; + default: ; + } + break; + + default: ; + } + + return N_UTYP; +} + +/* Convert compact Neon bitmask type representation to a type and size. Only + handles the case where a single bit is set in the mask. */ + +static int +el_type_of_type_chk (enum neon_el_type *type, unsigned *size, + enum neon_type_mask mask) +{ + if ((mask & N_EQK) != 0) + return FAIL; + + if ((mask & (N_S8 | N_U8 | N_I8 | N_8 | N_P8)) != 0) + *size = 8; + else if ((mask & (N_S16 | N_U16 | N_I16 | N_16 | N_P16)) != 0) + *size = 16; + else if ((mask & (N_S32 | N_U32 | N_I32 | N_32 | N_F32)) != 0) + *size = 32; + else if ((mask & (N_S64 | N_U64 | N_I64 | N_64 | N_F64)) != 0) + *size = 64; + else + return FAIL; + + if ((mask & (N_S8 | N_S16 | N_S32 | N_S64)) != 0) + *type = NT_signed; + else if ((mask & (N_U8 | N_U16 | N_U32 | N_U64)) != 0) + *type = NT_unsigned; + else if ((mask & (N_I8 | N_I16 | N_I32 | N_I64)) != 0) + *type = NT_integer; + else if ((mask & (N_8 | N_16 | N_32 | N_64)) != 0) + *type = NT_untyped; + else if ((mask & (N_P8 | N_P16)) != 0) + *type = NT_poly; + else if ((mask & (N_F32 | N_F64)) != 0) + *type = NT_float; + else + return FAIL; + + return SUCCESS; +} + +/* Modify a bitmask of allowed types. This is only needed for type + relaxation. */ + +static unsigned +modify_types_allowed (unsigned allowed, unsigned mods) +{ + unsigned size; + enum neon_el_type type; + unsigned destmask; + int i; + + destmask = 0; + + for (i = 1; i <= N_MAX_NONSPECIAL; i <<= 1) + { + if (el_type_of_type_chk (&type, &size, allowed & i) == SUCCESS) + { + neon_modify_type_size (mods, &type, &size); + destmask |= type_chk_of_el_type (type, size); + } + } + + return destmask; +} + +/* Check type and return type classification. + The manual states (paraphrase): If one datatype is given, it indicates the + type given in: + - the second operand, if there is one + - the operand, if there is no second operand + - the result, if there are no operands. + This isn't quite good enough though, so we use a concept of a "key" datatype + which is set on a per-instruction basis, which is the one which matters when + only one data type is written. + Note: this function has side-effects (e.g. filling in missing operands). All + Neon instructions should call it before performing bit encoding. */ + +static struct neon_type_el +neon_check_type (unsigned els, enum neon_shape ns, ...) +{ + va_list ap; + unsigned i, pass, key_el = 0; + unsigned types[NEON_MAX_TYPE_ELS]; + enum neon_el_type k_type = NT_invtype; + unsigned k_size = -1u; + struct neon_type_el badtype = {NT_invtype, -1}; + unsigned key_allowed = 0; + + /* Optional registers in Neon instructions are always (not) in operand 1. + Fill in the missing operand here, if it was omitted. */ + if (els > 1 && !inst.operands[1].present) + inst.operands[1] = inst.operands[0]; + + /* Suck up all the varargs. */ + va_start (ap, ns); + for (i = 0; i < els; i++) + { + unsigned thisarg = va_arg (ap, unsigned); + if (thisarg == N_IGNORE_TYPE) + { + va_end (ap); + return badtype; + } + types[i] = thisarg; + if ((thisarg & N_KEY) != 0) + key_el = i; + } + va_end (ap); + + if (inst.vectype.elems > 0) + for (i = 0; i < els; i++) + if (inst.operands[i].vectype.type != NT_invtype) + { + first_error (_("types specified in both the mnemonic and operands")); + return badtype; + } + + /* Duplicate inst.vectype elements here as necessary. + FIXME: No idea if this is exactly the same as the ARM assembler, + particularly when an insn takes one register and one non-register + operand. */ + if (inst.vectype.elems == 1 && els > 1) + { + unsigned j; + inst.vectype.elems = els; + inst.vectype.el[key_el] = inst.vectype.el[0]; + for (j = 0; j < els; j++) + if (j != key_el) + inst.vectype.el[j] = neon_type_promote (&inst.vectype.el[key_el], + types[j]); + } + else if (inst.vectype.elems == 0 && els > 0) + { + unsigned j; + /* No types were given after the mnemonic, so look for types specified + after each operand. We allow some flexibility here; as long as the + "key" operand has a type, we can infer the others. */ + for (j = 0; j < els; j++) + if (inst.operands[j].vectype.type != NT_invtype) + inst.vectype.el[j] = inst.operands[j].vectype; + + if (inst.operands[key_el].vectype.type != NT_invtype) + { + for (j = 0; j < els; j++) + if (inst.operands[j].vectype.type == NT_invtype) + inst.vectype.el[j] = neon_type_promote (&inst.vectype.el[key_el], + types[j]); + } + else + { + first_error (_("operand types can't be inferred")); + return badtype; + } + } + else if (inst.vectype.elems != els) + { + first_error (_("type specifier has the wrong number of parts")); + return badtype; + } + + for (pass = 0; pass < 2; pass++) + { + for (i = 0; i < els; i++) + { + unsigned thisarg = types[i]; + unsigned types_allowed = ((thisarg & N_EQK) != 0 && pass != 0) + ? modify_types_allowed (key_allowed, thisarg) : thisarg; + enum neon_el_type g_type = inst.vectype.el[i].type; + unsigned g_size = inst.vectype.el[i].size; + + /* Decay more-specific signed & unsigned types to sign-insensitive + integer types if sign-specific variants are unavailable. */ + if ((g_type == NT_signed || g_type == NT_unsigned) + && (types_allowed & N_SU_ALL) == 0) + g_type = NT_integer; + + /* If only untyped args are allowed, decay any more specific types to + them. Some instructions only care about signs for some element + sizes, so handle that properly. */ + if ((g_size == 8 && (types_allowed & N_8) != 0) + || (g_size == 16 && (types_allowed & N_16) != 0) + || (g_size == 32 && (types_allowed & N_32) != 0) + || (g_size == 64 && (types_allowed & N_64) != 0)) + g_type = NT_untyped; + + if (pass == 0) + { + if ((thisarg & N_KEY) != 0) + { + k_type = g_type; + k_size = g_size; + key_allowed = thisarg & ~N_KEY; + } + } + else + { + if ((thisarg & N_VFP) != 0) + { + enum neon_shape_el regshape = neon_shape_tab[ns].el[i]; + unsigned regwidth = neon_shape_el_size[regshape], match; + + /* In VFP mode, operands must match register widths. If we + have a key operand, use its width, else use the width of + the current operand. */ + if (k_size != -1u) + match = k_size; + else + match = g_size; + + if (regwidth != match) + { + first_error (_("operand size must match register width")); + return badtype; + } + } + + if ((thisarg & N_EQK) == 0) + { + unsigned given_type = type_chk_of_el_type (g_type, g_size); + + if ((given_type & types_allowed) == 0) + { + first_error (_("bad type in Neon instruction")); + return badtype; + } + } + else + { + enum neon_el_type mod_k_type = k_type; + unsigned mod_k_size = k_size; + neon_modify_type_size (thisarg, &mod_k_type, &mod_k_size); + if (g_type != mod_k_type || g_size != mod_k_size) + { + first_error (_("inconsistent types in Neon instruction")); + return badtype; + } + } + } + } + } + + return inst.vectype.el[key_el]; +} + +/* Neon-style VFP instruction forwarding. */ + +/* Thumb VFP instructions have 0xE in the condition field. */ + +static void +do_vfp_cond_or_thumb (void) +{ + if (thumb_mode) + inst.instruction |= 0xe0000000; + else + inst.instruction |= inst.cond << 28; +} + +/* Look up and encode a simple mnemonic, for use as a helper function for the + Neon-style VFP syntax. This avoids duplication of bits of the insns table, + etc. It is assumed that operand parsing has already been done, and that the + operands are in the form expected by the given opcode (this isn't necessarily + the same as the form in which they were parsed, hence some massaging must + take place before this function is called). + Checks current arch version against that in the looked-up opcode. */ + +static void +do_vfp_nsyn_opcode (const char *opname) +{ + const struct asm_opcode *opcode; + + opcode = hash_find (arm_ops_hsh, opname); + + if (!opcode) + abort (); + + constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, + thumb_mode ? *opcode->tvariant : *opcode->avariant), + _(BAD_FPU)); + + if (thumb_mode) + { + inst.instruction = opcode->tvalue; + opcode->tencode (); + } + else + { + inst.instruction = (inst.cond << 28) | opcode->avalue; + opcode->aencode (); + } +} + +static void +do_vfp_nsyn_add_sub (enum neon_shape rs) +{ + int is_add = (inst.instruction & 0x0fffffff) == N_MNEM_vadd; + + if (rs == NS_FFF) + { + if (is_add) + do_vfp_nsyn_opcode ("fadds"); + else + do_vfp_nsyn_opcode ("fsubs"); + } + else + { + if (is_add) + do_vfp_nsyn_opcode ("faddd"); + else + do_vfp_nsyn_opcode ("fsubd"); + } +} + +/* Check operand types to see if this is a VFP instruction, and if so call + PFN (). */ + +static int +try_vfp_nsyn (int args, void (*pfn) (enum neon_shape)) +{ + enum neon_shape rs; + struct neon_type_el et; + + switch (args) + { + case 2: + rs = neon_select_shape (NS_FF, NS_DD, NS_NULL); + et = neon_check_type (2, rs, + N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP); + break; + + case 3: + rs = neon_select_shape (NS_FFF, NS_DDD, NS_NULL); + et = neon_check_type (3, rs, + N_EQK | N_VFP, N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP); + break; + + default: + abort (); + } + + if (et.type != NT_invtype) + { + pfn (rs); + return SUCCESS; + } + else + inst.error = NULL; + + return FAIL; +} + +static void +do_vfp_nsyn_mla_mls (enum neon_shape rs) +{ + int is_mla = (inst.instruction & 0x0fffffff) == N_MNEM_vmla; + + if (rs == NS_FFF) + { + if (is_mla) + do_vfp_nsyn_opcode ("fmacs"); + else + do_vfp_nsyn_opcode ("fmscs"); + } + else + { + if (is_mla) + do_vfp_nsyn_opcode ("fmacd"); + else + do_vfp_nsyn_opcode ("fmscd"); + } +} + +static void +do_vfp_nsyn_mul (enum neon_shape rs) +{ + if (rs == NS_FFF) + do_vfp_nsyn_opcode ("fmuls"); + else + do_vfp_nsyn_opcode ("fmuld"); +} + +static void +do_vfp_nsyn_abs_neg (enum neon_shape rs) +{ + int is_neg = (inst.instruction & 0x80) != 0; + neon_check_type (2, rs, N_EQK | N_VFP, N_F32 | N_F64 | N_VFP | N_KEY); + + if (rs == NS_FF) + { + if (is_neg) + do_vfp_nsyn_opcode ("fnegs"); + else + do_vfp_nsyn_opcode ("fabss"); + } + else + { + if (is_neg) + do_vfp_nsyn_opcode ("fnegd"); + else + do_vfp_nsyn_opcode ("fabsd"); + } +} + +/* Encode single-precision (only!) VFP fldm/fstm instructions. Double precision + insns belong to Neon, and are handled elsewhere. */ + +static void +do_vfp_nsyn_ldm_stm (int is_dbmode) +{ + int is_ldm = (inst.instruction & (1 << 20)) != 0; + if (is_ldm) + { + if (is_dbmode) + do_vfp_nsyn_opcode ("fldmdbs"); + else + do_vfp_nsyn_opcode ("fldmias"); + } + else + { + if (is_dbmode) + do_vfp_nsyn_opcode ("fstmdbs"); + else + do_vfp_nsyn_opcode ("fstmias"); + } +} + +static void +do_vfp_nsyn_sqrt (void) +{ + enum neon_shape rs = neon_select_shape (NS_FF, NS_DD, NS_NULL); + neon_check_type (2, rs, N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP); + + if (rs == NS_FF) + do_vfp_nsyn_opcode ("fsqrts"); + else + do_vfp_nsyn_opcode ("fsqrtd"); +} + +static void +do_vfp_nsyn_div (void) +{ + enum neon_shape rs = neon_select_shape (NS_FFF, NS_DDD, NS_NULL); + neon_check_type (3, rs, N_EQK | N_VFP, N_EQK | N_VFP, + N_F32 | N_F64 | N_KEY | N_VFP); + + if (rs == NS_FFF) + do_vfp_nsyn_opcode ("fdivs"); + else + do_vfp_nsyn_opcode ("fdivd"); +} + +static void +do_vfp_nsyn_nmul (void) +{ + enum neon_shape rs = neon_select_shape (NS_FFF, NS_DDD, NS_NULL); + neon_check_type (3, rs, N_EQK | N_VFP, N_EQK | N_VFP, + N_F32 | N_F64 | N_KEY | N_VFP); + + if (rs == NS_FFF) + { + inst.instruction = NEON_ENC_SINGLE (inst.instruction); + do_vfp_sp_dyadic (); + } + else + { + inst.instruction = NEON_ENC_DOUBLE (inst.instruction); + do_vfp_dp_rd_rn_rm (); + } + do_vfp_cond_or_thumb (); +} + +static void +do_vfp_nsyn_cmp (void) +{ + if (inst.operands[1].isreg) + { + enum neon_shape rs = neon_select_shape (NS_FF, NS_DD, NS_NULL); + neon_check_type (2, rs, N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP); + + if (rs == NS_FF) + { + inst.instruction = NEON_ENC_SINGLE (inst.instruction); + do_vfp_sp_monadic (); + } + else + { + inst.instruction = NEON_ENC_DOUBLE (inst.instruction); + do_vfp_dp_rd_rm (); + } + } + else + { + enum neon_shape rs = neon_select_shape (NS_FI, NS_DI, NS_NULL); + neon_check_type (2, rs, N_F32 | N_F64 | N_KEY | N_VFP, N_EQK); + + switch (inst.instruction & 0x0fffffff) + { + case N_MNEM_vcmp: + inst.instruction += N_MNEM_vcmpz - N_MNEM_vcmp; + break; + case N_MNEM_vcmpe: + inst.instruction += N_MNEM_vcmpez - N_MNEM_vcmpe; + break; + default: + abort (); + } + + if (rs == NS_FI) + { + inst.instruction = NEON_ENC_SINGLE (inst.instruction); + do_vfp_sp_compare_z (); + } + else + { + inst.instruction = NEON_ENC_DOUBLE (inst.instruction); + do_vfp_dp_rd (); + } + } + do_vfp_cond_or_thumb (); +} + +static void +nsyn_insert_sp (void) +{ + inst.operands[1] = inst.operands[0]; + memset (&inst.operands[0], '\0', sizeof (inst.operands[0])); + inst.operands[0].reg = 13; + inst.operands[0].isreg = 1; + inst.operands[0].writeback = 1; + inst.operands[0].present = 1; +} + +static void +do_vfp_nsyn_push (void) +{ + nsyn_insert_sp (); + if (inst.operands[1].issingle) + do_vfp_nsyn_opcode ("fstmdbs"); + else + do_vfp_nsyn_opcode ("fstmdbd"); +} + +static void +do_vfp_nsyn_pop (void) +{ + nsyn_insert_sp (); + if (inst.operands[1].issingle) + do_vfp_nsyn_opcode ("fldmias"); + else + do_vfp_nsyn_opcode ("fldmiad"); +} + +/* Fix up Neon data-processing instructions, ORing in the correct bits for + ARM mode or Thumb mode and moving the encoded bit 24 to bit 28. */ + +static unsigned +neon_dp_fixup (unsigned i) +{ + if (thumb_mode) + { + /* The U bit is at bit 24 by default. Move to bit 28 in Thumb mode. */ + if (i & (1 << 24)) + i |= 1 << 28; + + i &= ~(1 << 24); + + i |= 0xef000000; + } + else + i |= 0xf2000000; + + return i; +} + +/* Turn a size (8, 16, 32, 64) into the respective bit number minus 3 + (0, 1, 2, 3). */ + +static unsigned +neon_logbits (unsigned x) +{ + return ffs (x) - 4; +} + +#define LOW4(R) ((R) & 0xf) +#define HI1(R) (((R) >> 4) & 1) + +/* Encode insns with bit pattern: + + |28/24|23|22 |21 20|19 16|15 12|11 8|7|6|5|4|3 0| + | U |x |D |size | Rn | Rd |x x x x|N|Q|M|x| Rm | + + SIZE is passed in bits. -1 means size field isn't changed, in case it has a + different meaning for some instruction. */ + +static void +neon_three_same (int isquad, int ubit, int size) +{ + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= LOW4 (inst.operands[1].reg) << 16; + inst.instruction |= HI1 (inst.operands[1].reg) << 7; + inst.instruction |= LOW4 (inst.operands[2].reg); + inst.instruction |= HI1 (inst.operands[2].reg) << 5; + inst.instruction |= (isquad != 0) << 6; + inst.instruction |= (ubit != 0) << 24; + if (size != -1) + inst.instruction |= neon_logbits (size) << 20; + + inst.instruction = neon_dp_fixup (inst.instruction); +} + +/* Encode instructions of the form: + + |28/24|23|22|21 20|19 18|17 16|15 12|11 7|6|5|4|3 0| + | U |x |D |x x |size |x x | Rd |x x x x x|Q|M|x| Rm | + + Don't write size if SIZE == -1. */ + +static void +neon_two_same (int qbit, int ubit, int size) +{ + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= LOW4 (inst.operands[1].reg); + inst.instruction |= HI1 (inst.operands[1].reg) << 5; + inst.instruction |= (qbit != 0) << 6; + inst.instruction |= (ubit != 0) << 24; + + if (size != -1) + inst.instruction |= neon_logbits (size) << 18; + + inst.instruction = neon_dp_fixup (inst.instruction); +} + +/* Neon instruction encoders, in approximate order of appearance. */ + +static void +do_neon_dyadic_i_su (void) +{ + enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL); + struct neon_type_el et = neon_check_type (3, rs, + N_EQK, N_EQK, N_SU_32 | N_KEY); + neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size); +} + +static void +do_neon_dyadic_i64_su (void) +{ + enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL); + struct neon_type_el et = neon_check_type (3, rs, + N_EQK, N_EQK, N_SU_ALL | N_KEY); + neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size); +} + +static void +neon_imm_shift (int write_ubit, int uval, int isquad, struct neon_type_el et, + unsigned immbits) +{ + unsigned size = et.size >> 3; + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= LOW4 (inst.operands[1].reg); + inst.instruction |= HI1 (inst.operands[1].reg) << 5; + inst.instruction |= (isquad != 0) << 6; + inst.instruction |= immbits << 16; + inst.instruction |= (size >> 3) << 7; + inst.instruction |= (size & 0x7) << 19; + if (write_ubit) + inst.instruction |= (uval != 0) << 24; + + inst.instruction = neon_dp_fixup (inst.instruction); +} + +static void +do_neon_shl_imm (void) +{ + if (!inst.operands[2].isreg) + { + enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_NULL); + struct neon_type_el et = neon_check_type (2, rs, N_EQK, N_KEY | N_I_ALL); + inst.instruction = NEON_ENC_IMMED (inst.instruction); + neon_imm_shift (FALSE, 0, neon_quad (rs), et, inst.operands[2].imm); + } + else + { + enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL); + struct neon_type_el et = neon_check_type (3, rs, + N_EQK, N_SU_ALL | N_KEY, N_EQK | N_SGN); + unsigned int tmp; + + /* VSHL/VQSHL 3-register variants have syntax such as: + vshl.xx Dd, Dm, Dn + whereas other 3-register operations encoded by neon_three_same have + syntax like: + vadd.xx Dd, Dn, Dm + (i.e. with Dn & Dm reversed). Swap operands[1].reg and operands[2].reg + here. */ + tmp = inst.operands[2].reg; + inst.operands[2].reg = inst.operands[1].reg; + inst.operands[1].reg = tmp; + inst.instruction = NEON_ENC_INTEGER (inst.instruction); + neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size); + } +} + +static void +do_neon_qshl_imm (void) +{ + if (!inst.operands[2].isreg) + { + enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_NULL); + struct neon_type_el et = neon_check_type (2, rs, N_EQK, N_SU_ALL | N_KEY); + + inst.instruction = NEON_ENC_IMMED (inst.instruction); + neon_imm_shift (TRUE, et.type == NT_unsigned, neon_quad (rs), et, + inst.operands[2].imm); + } + else + { + enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL); + struct neon_type_el et = neon_check_type (3, rs, + N_EQK, N_SU_ALL | N_KEY, N_EQK | N_SGN); + unsigned int tmp; + + /* See note in do_neon_shl_imm. */ + tmp = inst.operands[2].reg; + inst.operands[2].reg = inst.operands[1].reg; + inst.operands[1].reg = tmp; + inst.instruction = NEON_ENC_INTEGER (inst.instruction); + neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size); + } +} + +static void +do_neon_rshl (void) +{ + enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL); + struct neon_type_el et = neon_check_type (3, rs, + N_EQK, N_EQK, N_SU_ALL | N_KEY); + unsigned int tmp; + + tmp = inst.operands[2].reg; + inst.operands[2].reg = inst.operands[1].reg; + inst.operands[1].reg = tmp; + neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size); +} + +static int +neon_cmode_for_logic_imm (unsigned immediate, unsigned *immbits, int size) +{ + /* Handle .I8 pseudo-instructions. */ + if (size == 8) + { + /* Unfortunately, this will make everything apart from zero out-of-range. + FIXME is this the intended semantics? There doesn't seem much point in + accepting .I8 if so. */ + immediate |= immediate << 8; + size = 16; + } + + if (size >= 32) + { + if (immediate == (immediate & 0x000000ff)) + { + *immbits = immediate; + return 0x1; + } + else if (immediate == (immediate & 0x0000ff00)) + { + *immbits = immediate >> 8; + return 0x3; + } + else if (immediate == (immediate & 0x00ff0000)) + { + *immbits = immediate >> 16; + return 0x5; + } + else if (immediate == (immediate & 0xff000000)) + { + *immbits = immediate >> 24; + return 0x7; + } + if ((immediate & 0xffff) != (immediate >> 16)) + goto bad_immediate; + immediate &= 0xffff; + } + + if (immediate == (immediate & 0x000000ff)) + { + *immbits = immediate; + return 0x9; + } + else if (immediate == (immediate & 0x0000ff00)) + { + *immbits = immediate >> 8; + return 0xb; + } + + bad_immediate: + first_error (_("immediate value out of range")); + return FAIL; +} + +/* True if IMM has form 0bAAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD for bits + A, B, C, D. */ + +static int +neon_bits_same_in_bytes (unsigned imm) +{ + return ((imm & 0x000000ff) == 0 || (imm & 0x000000ff) == 0x000000ff) + && ((imm & 0x0000ff00) == 0 || (imm & 0x0000ff00) == 0x0000ff00) + && ((imm & 0x00ff0000) == 0 || (imm & 0x00ff0000) == 0x00ff0000) + && ((imm & 0xff000000) == 0 || (imm & 0xff000000) == 0xff000000); +} + +/* For immediate of above form, return 0bABCD. */ + +static unsigned +neon_squash_bits (unsigned imm) +{ + return (imm & 0x01) | ((imm & 0x0100) >> 7) | ((imm & 0x010000) >> 14) + | ((imm & 0x01000000) >> 21); +} + +/* Compress quarter-float representation to 0b...000 abcdefgh. */ + +static unsigned +neon_qfloat_bits (unsigned imm) +{ + return ((imm >> 19) & 0x7f) | ((imm >> 24) & 0x80); +} + +/* Returns CMODE. IMMBITS [7:0] is set to bits suitable for inserting into + the instruction. *OP is passed as the initial value of the op field, and + may be set to a different value depending on the constant (i.e. + "MOV I64, 0bAAAAAAAABBBB..." which uses OP = 1 despite being MOV not + MVN). If the immediate looks like a repeated parttern then also + try smaller element sizes. */ + +static int +neon_cmode_for_move_imm (unsigned immlo, unsigned immhi, int float_p, + unsigned *immbits, int *op, int size, + enum neon_el_type type) +{ + /* Only permit float immediates (including 0.0/-0.0) if the operand type is + float. */ + if (type == NT_float && !float_p) + return FAIL; + + if (type == NT_float && is_quarter_float (immlo) && immhi == 0) + { + if (size != 32 || *op == 1) + return FAIL; + *immbits = neon_qfloat_bits (immlo); + return 0xf; + } + + if (size == 64) + { + if (neon_bits_same_in_bytes (immhi) + && neon_bits_same_in_bytes (immlo)) + { + if (*op == 1) + return FAIL; + *immbits = (neon_squash_bits (immhi) << 4) + | neon_squash_bits (immlo); + *op = 1; + return 0xe; + } + + if (immhi != immlo) + return FAIL; + } + + if (size >= 32) + { + if (immlo == (immlo & 0x000000ff)) + { + *immbits = immlo; + return 0x0; + } + else if (immlo == (immlo & 0x0000ff00)) + { + *immbits = immlo >> 8; + return 0x2; + } + else if (immlo == (immlo & 0x00ff0000)) + { + *immbits = immlo >> 16; + return 0x4; + } + else if (immlo == (immlo & 0xff000000)) + { + *immbits = immlo >> 24; + return 0x6; + } + else if (immlo == ((immlo & 0x0000ff00) | 0x000000ff)) + { + *immbits = (immlo >> 8) & 0xff; + return 0xc; + } + else if (immlo == ((immlo & 0x00ff0000) | 0x0000ffff)) + { + *immbits = (immlo >> 16) & 0xff; + return 0xd; + } + + if ((immlo & 0xffff) != (immlo >> 16)) + return FAIL; + immlo &= 0xffff; + } + + if (size >= 16) + { + if (immlo == (immlo & 0x000000ff)) + { + *immbits = immlo; + return 0x8; + } + else if (immlo == (immlo & 0x0000ff00)) + { + *immbits = immlo >> 8; + return 0xa; + } + + if ((immlo & 0xff) != (immlo >> 8)) + return FAIL; + immlo &= 0xff; + } + + if (immlo == (immlo & 0x000000ff)) + { + /* Don't allow MVN with 8-bit immediate. */ + if (*op == 1) + return FAIL; + *immbits = immlo; + return 0xe; + } + + return FAIL; +} + +/* Write immediate bits [7:0] to the following locations: + + |28/24|23 19|18 16|15 4|3 0| + | a |x x x x x|b c d|x x x x x x x x x x x x|e f g h| + + This function is used by VMOV/VMVN/VORR/VBIC. */ + +static void +neon_write_immbits (unsigned immbits) +{ + inst.instruction |= immbits & 0xf; + inst.instruction |= ((immbits >> 4) & 0x7) << 16; + inst.instruction |= ((immbits >> 7) & 0x1) << 24; +} + +/* Invert low-order SIZE bits of XHI:XLO. */ + +static void +neon_invert_size (unsigned *xlo, unsigned *xhi, int size) +{ + unsigned immlo = xlo ? *xlo : 0; + unsigned immhi = xhi ? *xhi : 0; + + switch (size) + { + case 8: + immlo = (~immlo) & 0xff; + break; + + case 16: + immlo = (~immlo) & 0xffff; + break; + + case 64: + immhi = (~immhi) & 0xffffffff; + /* fall through. */ + + case 32: + immlo = (~immlo) & 0xffffffff; + break; + + default: + abort (); + } + + if (xlo) + *xlo = immlo; + + if (xhi) + *xhi = immhi; +} + +static void +do_neon_logic (void) +{ + if (inst.operands[2].present && inst.operands[2].isreg) + { + enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL); + neon_check_type (3, rs, N_IGNORE_TYPE); + /* U bit and size field were set as part of the bitmask. */ + inst.instruction = NEON_ENC_INTEGER (inst.instruction); + neon_three_same (neon_quad (rs), 0, -1); + } + else + { + enum neon_shape rs = neon_select_shape (NS_DI, NS_QI, NS_NULL); + struct neon_type_el et = neon_check_type (2, rs, + N_I8 | N_I16 | N_I32 | N_I64 | N_F32 | N_KEY, N_EQK); + enum neon_opc opcode = inst.instruction & 0x0fffffff; + unsigned immbits; + int cmode; + + if (et.type == NT_invtype) + return; + + inst.instruction = NEON_ENC_IMMED (inst.instruction); + + immbits = inst.operands[1].imm; + if (et.size == 64) + { + /* .i64 is a pseudo-op, so the immediate must be a repeating + pattern. */ + if (immbits != (inst.operands[1].regisimm ? + inst.operands[1].reg : 0)) + { + /* Set immbits to an invalid constant. */ + immbits = 0xdeadbeef; + } + } + + switch (opcode) + { + case N_MNEM_vbic: + cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size); + break; + + case N_MNEM_vorr: + cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size); + break; + + case N_MNEM_vand: + /* Pseudo-instruction for VBIC. */ + neon_invert_size (&immbits, 0, et.size); + cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size); + break; + + case N_MNEM_vorn: + /* Pseudo-instruction for VORR. */ + neon_invert_size (&immbits, 0, et.size); + cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size); + break; + + default: + abort (); + } + + if (cmode == FAIL) + return; + + inst.instruction |= neon_quad (rs) << 6; + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= cmode << 8; + neon_write_immbits (immbits); + + inst.instruction = neon_dp_fixup (inst.instruction); + } +} + +static void +do_neon_bitfield (void) +{ + enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL); + neon_check_type (3, rs, N_IGNORE_TYPE); + neon_three_same (neon_quad (rs), 0, -1); +} + +static void +neon_dyadic_misc (enum neon_el_type ubit_meaning, unsigned types, + unsigned destbits) +{ + enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL); + struct neon_type_el et = neon_check_type (3, rs, N_EQK | destbits, N_EQK, + types | N_KEY); + if (et.type == NT_float) + { + inst.instruction = NEON_ENC_FLOAT (inst.instruction); + neon_three_same (neon_quad (rs), 0, -1); + } + else + { + inst.instruction = NEON_ENC_INTEGER (inst.instruction); + neon_three_same (neon_quad (rs), et.type == ubit_meaning, et.size); + } +} + +static void +do_neon_dyadic_if_su (void) +{ + neon_dyadic_misc (NT_unsigned, N_SUF_32, 0); +} + +static void +do_neon_dyadic_if_su_d (void) +{ + /* This version only allow D registers, but that constraint is enforced during + operand parsing so we don't need to do anything extra here. */ + neon_dyadic_misc (NT_unsigned, N_SUF_32, 0); +} + +static void +do_neon_dyadic_if_i_d (void) +{ + /* The "untyped" case can't happen. Do this to stop the "U" bit being + affected if we specify unsigned args. */ + neon_dyadic_misc (NT_untyped, N_IF_32, 0); +} + +enum vfp_or_neon_is_neon_bits +{ + NEON_CHECK_CC = 1, + NEON_CHECK_ARCH = 2 +}; + +/* Call this function if an instruction which may have belonged to the VFP or + Neon instruction sets, but turned out to be a Neon instruction (due to the + operand types involved, etc.). We have to check and/or fix-up a couple of + things: + + - Make sure the user hasn't attempted to make a Neon instruction + conditional. + - Alter the value in the condition code field if necessary. + - Make sure that the arch supports Neon instructions. + + Which of these operations take place depends on bits from enum + vfp_or_neon_is_neon_bits. + + WARNING: This function has side effects! If NEON_CHECK_CC is used and the + current instruction's condition is COND_ALWAYS, the condition field is + changed to inst.uncond_value. This is necessary because instructions shared + between VFP and Neon may be conditional for the VFP variants only, and the + unconditional Neon version must have, e.g., 0xF in the condition field. */ + +static int +vfp_or_neon_is_neon (unsigned check) +{ + /* Conditions are always legal in Thumb mode (IT blocks). */ + if (!thumb_mode && (check & NEON_CHECK_CC)) + { + if (inst.cond != COND_ALWAYS) + { + first_error (_(BAD_COND)); + return FAIL; + } + if (inst.uncond_value != -1) + inst.instruction |= inst.uncond_value << 28; + } + + if ((check & NEON_CHECK_ARCH) + && !ARM_CPU_HAS_FEATURE (cpu_variant, fpu_neon_ext_v1)) + { + first_error (_(BAD_FPU)); + return FAIL; + } + + return SUCCESS; +} + +static void +do_neon_addsub_if_i (void) +{ + if (try_vfp_nsyn (3, do_vfp_nsyn_add_sub) == SUCCESS) + return; + + if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL) + return; + + /* The "untyped" case can't happen. Do this to stop the "U" bit being + affected if we specify unsigned args. */ + neon_dyadic_misc (NT_untyped, N_IF_32 | N_I64, 0); +} + +/* Swaps operands 1 and 2. If operand 1 (optional arg) was omitted, we want the + result to be: + V<op> A,B (A is operand 0, B is operand 2) + to mean: + V<op> A,B,A + not: + V<op> A,B,B + so handle that case specially. */ + +static void +neon_exchange_operands (void) +{ + void *scratch = alloca (sizeof (inst.operands[0])); + if (inst.operands[1].present) + { + /* Swap operands[1] and operands[2]. */ + memcpy (scratch, &inst.operands[1], sizeof (inst.operands[0])); + inst.operands[1] = inst.operands[2]; + memcpy (&inst.operands[2], scratch, sizeof (inst.operands[0])); + } + else + { + inst.operands[1] = inst.operands[2]; + inst.operands[2] = inst.operands[0]; + } +} + +static void +neon_compare (unsigned regtypes, unsigned immtypes, int invert) +{ + if (inst.operands[2].isreg) + { + if (invert) + neon_exchange_operands (); + neon_dyadic_misc (NT_unsigned, regtypes, N_SIZ); + } + else + { + enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_NULL); + struct neon_type_el et = neon_check_type (2, rs, + N_EQK | N_SIZ, immtypes | N_KEY); + + inst.instruction = NEON_ENC_IMMED (inst.instruction); + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= LOW4 (inst.operands[1].reg); + inst.instruction |= HI1 (inst.operands[1].reg) << 5; + inst.instruction |= neon_quad (rs) << 6; + inst.instruction |= (et.type == NT_float) << 10; + inst.instruction |= neon_logbits (et.size) << 18; + + inst.instruction = neon_dp_fixup (inst.instruction); + } +} + +static void +do_neon_cmp (void) +{ + neon_compare (N_SUF_32, N_S8 | N_S16 | N_S32 | N_F32, FALSE); +} + +static void +do_neon_cmp_inv (void) +{ + neon_compare (N_SUF_32, N_S8 | N_S16 | N_S32 | N_F32, TRUE); +} + +static void +do_neon_ceq (void) +{ + neon_compare (N_IF_32, N_IF_32, FALSE); +} + +/* For multiply instructions, we have the possibility of 16-bit or 32-bit + scalars, which are encoded in 5 bits, M : Rm. + For 16-bit scalars, the register is encoded in Rm[2:0] and the index in + M:Rm[3], and for 32-bit scalars, the register is encoded in Rm[3:0] and the + index in M. */ + +static unsigned +neon_scalar_for_mul (unsigned scalar, unsigned elsize) +{ + unsigned regno = NEON_SCALAR_REG (scalar); + unsigned elno = NEON_SCALAR_INDEX (scalar); + + switch (elsize) + { + case 16: + if (regno > 7 || elno > 3) + goto bad_scalar; + return regno | (elno << 3); + + case 32: + if (regno > 15 || elno > 1) + goto bad_scalar; + return regno | (elno << 4); + + default: + bad_scalar: + first_error (_("scalar out of range for multiply instruction")); + } + + return 0; +} + +/* Encode multiply / multiply-accumulate scalar instructions. */ + +static void +neon_mul_mac (struct neon_type_el et, int ubit) +{ + unsigned scalar; + + /* Give a more helpful error message if we have an invalid type. */ + if (et.type == NT_invtype) + return; + + scalar = neon_scalar_for_mul (inst.operands[2].reg, et.size); + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= LOW4 (inst.operands[1].reg) << 16; + inst.instruction |= HI1 (inst.operands[1].reg) << 7; + inst.instruction |= LOW4 (scalar); + inst.instruction |= HI1 (scalar) << 5; + inst.instruction |= (et.type == NT_float) << 8; + inst.instruction |= neon_logbits (et.size) << 20; + inst.instruction |= (ubit != 0) << 24; + + inst.instruction = neon_dp_fixup (inst.instruction); +} + +static void +do_neon_mac_maybe_scalar (void) +{ + if (try_vfp_nsyn (3, do_vfp_nsyn_mla_mls) == SUCCESS) + return; + + if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL) + return; + + if (inst.operands[2].isscalar) + { + enum neon_shape rs = neon_select_shape (NS_DDS, NS_QQS, NS_NULL); + struct neon_type_el et = neon_check_type (3, rs, + N_EQK, N_EQK, N_I16 | N_I32 | N_F32 | N_KEY); + inst.instruction = NEON_ENC_SCALAR (inst.instruction); + neon_mul_mac (et, neon_quad (rs)); + } + else + { + /* The "untyped" case can't happen. Do this to stop the "U" bit being + affected if we specify unsigned args. */ + neon_dyadic_misc (NT_untyped, N_IF_32, 0); + } +} + +static void +do_neon_tst (void) +{ + enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL); + struct neon_type_el et = neon_check_type (3, rs, + N_EQK, N_EQK, N_8 | N_16 | N_32 | N_KEY); + neon_three_same (neon_quad (rs), 0, et.size); +} + +/* VMUL with 3 registers allows the P8 type. The scalar version supports the + same types as the MAC equivalents. The polynomial type for this instruction + is encoded the same as the integer type. */ + +static void +do_neon_mul (void) +{ + if (try_vfp_nsyn (3, do_vfp_nsyn_mul) == SUCCESS) + return; + + if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL) + return; + + if (inst.operands[2].isscalar) + do_neon_mac_maybe_scalar (); + else + neon_dyadic_misc (NT_poly, N_I8 | N_I16 | N_I32 | N_F32 | N_P8, 0); +} + +static void +do_neon_qdmulh (void) +{ + if (inst.operands[2].isscalar) + { + enum neon_shape rs = neon_select_shape (NS_DDS, NS_QQS, NS_NULL); + struct neon_type_el et = neon_check_type (3, rs, + N_EQK, N_EQK, N_S16 | N_S32 | N_KEY); + inst.instruction = NEON_ENC_SCALAR (inst.instruction); + neon_mul_mac (et, neon_quad (rs)); + } + else + { + enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL); + struct neon_type_el et = neon_check_type (3, rs, + N_EQK, N_EQK, N_S16 | N_S32 | N_KEY); + inst.instruction = NEON_ENC_INTEGER (inst.instruction); + /* The U bit (rounding) comes from bit mask. */ + neon_three_same (neon_quad (rs), 0, et.size); + } +} + +static void +do_neon_fcmp_absolute (void) +{ + enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL); + neon_check_type (3, rs, N_EQK, N_EQK, N_F32 | N_KEY); + /* Size field comes from bit mask. */ + neon_three_same (neon_quad (rs), 1, -1); +} + +static void +do_neon_fcmp_absolute_inv (void) +{ + neon_exchange_operands (); + do_neon_fcmp_absolute (); +} + +static void +do_neon_step (void) +{ + enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL); + neon_check_type (3, rs, N_EQK, N_EQK, N_F32 | N_KEY); + neon_three_same (neon_quad (rs), 0, -1); +} + +static void +do_neon_abs_neg (void) +{ + enum neon_shape rs; + struct neon_type_el et; + + if (try_vfp_nsyn (2, do_vfp_nsyn_abs_neg) == SUCCESS) + return; + + if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL) + return; + + rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL); + et = neon_check_type (2, rs, N_EQK, N_S8 | N_S16 | N_S32 | N_F32 | N_KEY); + + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= LOW4 (inst.operands[1].reg); + inst.instruction |= HI1 (inst.operands[1].reg) << 5; + inst.instruction |= neon_quad (rs) << 6; + inst.instruction |= (et.type == NT_float) << 10; + inst.instruction |= neon_logbits (et.size) << 18; + + inst.instruction = neon_dp_fixup (inst.instruction); +} + +static void +do_neon_sli (void) +{ + enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_NULL); + struct neon_type_el et = neon_check_type (2, rs, + N_EQK, N_8 | N_16 | N_32 | N_64 | N_KEY); + int imm = inst.operands[2].imm; + constraint (imm < 0 || (unsigned)imm >= et.size, + _("immediate out of range for insert")); + neon_imm_shift (FALSE, 0, neon_quad (rs), et, imm); +} + +static void +do_neon_sri (void) +{ + enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_NULL); + struct neon_type_el et = neon_check_type (2, rs, + N_EQK, N_8 | N_16 | N_32 | N_64 | N_KEY); + int imm = inst.operands[2].imm; + constraint (imm < 1 || (unsigned)imm > et.size, + _("immediate out of range for insert")); + neon_imm_shift (FALSE, 0, neon_quad (rs), et, et.size - imm); +} + +static void +do_neon_qshlu_imm (void) +{ + enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_NULL); + struct neon_type_el et = neon_check_type (2, rs, + N_EQK | N_UNS, N_S8 | N_S16 | N_S32 | N_S64 | N_KEY); + int imm = inst.operands[2].imm; + constraint (imm < 0 || (unsigned)imm >= et.size, + _("immediate out of range for shift")); + /* Only encodes the 'U present' variant of the instruction. + In this case, signed types have OP (bit 8) set to 0. + Unsigned types have OP set to 1. */ + inst.instruction |= (et.type == NT_unsigned) << 8; + /* The rest of the bits are the same as other immediate shifts. */ + neon_imm_shift (FALSE, 0, neon_quad (rs), et, imm); +} + +static void +do_neon_qmovn (void) +{ + struct neon_type_el et = neon_check_type (2, NS_DQ, + N_EQK | N_HLF, N_SU_16_64 | N_KEY); + /* Saturating move where operands can be signed or unsigned, and the + destination has the same signedness. */ + inst.instruction = NEON_ENC_INTEGER (inst.instruction); + if (et.type == NT_unsigned) + inst.instruction |= 0xc0; + else + inst.instruction |= 0x80; + neon_two_same (0, 1, et.size / 2); +} + +static void +do_neon_qmovun (void) +{ + struct neon_type_el et = neon_check_type (2, NS_DQ, + N_EQK | N_HLF | N_UNS, N_S16 | N_S32 | N_S64 | N_KEY); + /* Saturating move with unsigned results. Operands must be signed. */ + inst.instruction = NEON_ENC_INTEGER (inst.instruction); + neon_two_same (0, 1, et.size / 2); +} + +static void +do_neon_rshift_sat_narrow (void) +{ + /* FIXME: Types for narrowing. If operands are signed, results can be signed + or unsigned. If operands are unsigned, results must also be unsigned. */ + struct neon_type_el et = neon_check_type (2, NS_DQI, + N_EQK | N_HLF, N_SU_16_64 | N_KEY); + int imm = inst.operands[2].imm; + /* This gets the bounds check, size encoding and immediate bits calculation + right. */ + et.size /= 2; + + /* VQ{R}SHRN.I<size> <Dd>, <Qm>, #0 is a synonym for + VQMOVN.I<size> <Dd>, <Qm>. */ + if (imm == 0) + { + inst.operands[2].present = 0; + inst.instruction = N_MNEM_vqmovn; + do_neon_qmovn (); + return; + } + + constraint (imm < 1 || (unsigned)imm > et.size, + _("immediate out of range")); + neon_imm_shift (TRUE, et.type == NT_unsigned, 0, et, et.size - imm); +} + +static void +do_neon_rshift_sat_narrow_u (void) +{ + /* FIXME: Types for narrowing. If operands are signed, results can be signed + or unsigned. If operands are unsigned, results must also be unsigned. */ + struct neon_type_el et = neon_check_type (2, NS_DQI, + N_EQK | N_HLF | N_UNS, N_S16 | N_S32 | N_S64 | N_KEY); + int imm = inst.operands[2].imm; + /* This gets the bounds check, size encoding and immediate bits calculation + right. */ + et.size /= 2; + + /* VQSHRUN.I<size> <Dd>, <Qm>, #0 is a synonym for + VQMOVUN.I<size> <Dd>, <Qm>. */ + if (imm == 0) + { + inst.operands[2].present = 0; + inst.instruction = N_MNEM_vqmovun; + do_neon_qmovun (); + return; + } + + constraint (imm < 1 || (unsigned)imm > et.size, + _("immediate out of range")); + /* FIXME: The manual is kind of unclear about what value U should have in + VQ{R}SHRUN instructions, but U=0, op=0 definitely encodes VRSHR, so it + must be 1. */ + neon_imm_shift (TRUE, 1, 0, et, et.size - imm); +} + +static void +do_neon_movn (void) +{ + struct neon_type_el et = neon_check_type (2, NS_DQ, + N_EQK | N_HLF, N_I16 | N_I32 | N_I64 | N_KEY); + inst.instruction = NEON_ENC_INTEGER (inst.instruction); + neon_two_same (0, 1, et.size / 2); +} + +static void +do_neon_rshift_narrow (void) +{ + struct neon_type_el et = neon_check_type (2, NS_DQI, + N_EQK | N_HLF, N_I16 | N_I32 | N_I64 | N_KEY); + int imm = inst.operands[2].imm; + /* This gets the bounds check, size encoding and immediate bits calculation + right. */ + et.size /= 2; + + /* If immediate is zero then we are a pseudo-instruction for + VMOVN.I<size> <Dd>, <Qm> */ + if (imm == 0) + { + inst.operands[2].present = 0; + inst.instruction = N_MNEM_vmovn; + do_neon_movn (); + return; + } + + constraint (imm < 1 || (unsigned)imm > et.size, + _("immediate out of range for narrowing operation")); + neon_imm_shift (FALSE, 0, 0, et, et.size - imm); +} + +static void +do_neon_shll (void) +{ + /* FIXME: Type checking when lengthening. */ + struct neon_type_el et = neon_check_type (2, NS_QDI, + N_EQK | N_DBL, N_I8 | N_I16 | N_I32 | N_KEY); + unsigned imm = inst.operands[2].imm; + + if (imm == et.size) + { + /* Maximum shift variant. */ + inst.instruction = NEON_ENC_INTEGER (inst.instruction); + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= LOW4 (inst.operands[1].reg); + inst.instruction |= HI1 (inst.operands[1].reg) << 5; + inst.instruction |= neon_logbits (et.size) << 18; + + inst.instruction = neon_dp_fixup (inst.instruction); + } + else + { + /* A more-specific type check for non-max versions. */ + et = neon_check_type (2, NS_QDI, + N_EQK | N_DBL, N_SU_32 | N_KEY); + inst.instruction = NEON_ENC_IMMED (inst.instruction); + neon_imm_shift (TRUE, et.type == NT_unsigned, 0, et, imm); + } +} + +/* Check the various types for the VCVT instruction, and return which version + the current instruction is. */ + +static int +neon_cvt_flavour (enum neon_shape rs) +{ +#define CVT_VAR(C,X,Y) \ + et = neon_check_type (2, rs, whole_reg | (X), whole_reg | (Y)); \ + if (et.type != NT_invtype) \ + { \ + inst.error = NULL; \ + return (C); \ + } + struct neon_type_el et; + unsigned whole_reg = (rs == NS_FFI || rs == NS_FD || rs == NS_DF + || rs == NS_FF) ? N_VFP : 0; + /* The instruction versions which take an immediate take one register + argument, which is extended to the width of the full register. Thus the + "source" and "destination" registers must have the same width. Hack that + here by making the size equal to the key (wider, in this case) operand. */ + unsigned key = (rs == NS_QQI || rs == NS_DDI || rs == NS_FFI) ? N_KEY : 0; + + CVT_VAR (0, N_S32, N_F32); + CVT_VAR (1, N_U32, N_F32); + CVT_VAR (2, N_F32, N_S32); + CVT_VAR (3, N_F32, N_U32); + + whole_reg = N_VFP; + + /* VFP instructions. */ + CVT_VAR (4, N_F32, N_F64); + CVT_VAR (5, N_F64, N_F32); + CVT_VAR (6, N_S32, N_F64 | key); + CVT_VAR (7, N_U32, N_F64 | key); + CVT_VAR (8, N_F64 | key, N_S32); + CVT_VAR (9, N_F64 | key, N_U32); + /* VFP instructions with bitshift. */ + CVT_VAR (10, N_F32 | key, N_S16); + CVT_VAR (11, N_F32 | key, N_U16); + CVT_VAR (12, N_F64 | key, N_S16); + CVT_VAR (13, N_F64 | key, N_U16); + CVT_VAR (14, N_S16, N_F32 | key); + CVT_VAR (15, N_U16, N_F32 | key); + CVT_VAR (16, N_S16, N_F64 | key); + CVT_VAR (17, N_U16, N_F64 | key); + + return -1; +#undef CVT_VAR +} + +/* Neon-syntax VFP conversions. */ + +static void +do_vfp_nsyn_cvt (enum neon_shape rs, int flavour) +{ + const char *opname = 0; + + if (rs == NS_DDI || rs == NS_QQI || rs == NS_FFI) + { + /* Conversions with immediate bitshift. */ + const char *enc[] = + { + "ftosls", + "ftouls", + "fsltos", + "fultos", + NULL, + NULL, + "ftosld", + "ftould", + "fsltod", + "fultod", + "fshtos", + "fuhtos", + "fshtod", + "fuhtod", + "ftoshs", + "ftouhs", + "ftoshd", + "ftouhd" + }; + + if (flavour >= 0 && flavour < (int) ARRAY_SIZE (enc)) + { + opname = enc[flavour]; + constraint (inst.operands[0].reg != inst.operands[1].reg, + _("operands 0 and 1 must be the same register")); + inst.operands[1] = inst.operands[2]; + memset (&inst.operands[2], '\0', sizeof (inst.operands[2])); + } + } + else + { + /* Conversions without bitshift. */ + const char *enc[] = + { + "ftosis", + "ftouis", + "fsitos", + "fuitos", + "fcvtsd", + "fcvtds", + "ftosid", + "ftouid", + "fsitod", + "fuitod" + }; + + if (flavour >= 0 && flavour < (int) ARRAY_SIZE (enc)) + opname = enc[flavour]; + } + + if (opname) + do_vfp_nsyn_opcode (opname); +} + +static void +do_vfp_nsyn_cvtz (void) +{ + enum neon_shape rs = neon_select_shape (NS_FF, NS_FD, NS_NULL); + int flavour = neon_cvt_flavour (rs); + const char *enc[] = + { + "ftosizs", + "ftouizs", + NULL, + NULL, + NULL, + NULL, + "ftosizd", + "ftouizd" + }; + + if (flavour >= 0 && flavour < (int) ARRAY_SIZE (enc) && enc[flavour]) + do_vfp_nsyn_opcode (enc[flavour]); +} + +static void +do_neon_cvt (void) +{ + enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_FFI, NS_DD, NS_QQ, + NS_FD, NS_DF, NS_FF, NS_NULL); + int flavour = neon_cvt_flavour (rs); + + /* VFP rather than Neon conversions. */ + if (flavour >= 4) + { + do_vfp_nsyn_cvt (rs, flavour); + return; + } + + switch (rs) + { + case NS_DDI: + case NS_QQI: + { + if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL) + return; + + /* Fixed-point conversion with #0 immediate is encoded as an + integer conversion. */ + if (inst.operands[2].present && inst.operands[2].imm == 0) + goto int_encode; + unsigned immbits = 32 - inst.operands[2].imm; + unsigned enctab[] = { 0x0000100, 0x1000100, 0x0, 0x1000000 }; + inst.instruction = NEON_ENC_IMMED (inst.instruction); + if (flavour != -1) + inst.instruction |= enctab[flavour]; + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= LOW4 (inst.operands[1].reg); + inst.instruction |= HI1 (inst.operands[1].reg) << 5; + inst.instruction |= neon_quad (rs) << 6; + inst.instruction |= 1 << 21; + inst.instruction |= immbits << 16; + + inst.instruction = neon_dp_fixup (inst.instruction); + } + break; + + case NS_DD: + case NS_QQ: + int_encode: + { + unsigned enctab[] = { 0x100, 0x180, 0x0, 0x080 }; + + inst.instruction = NEON_ENC_INTEGER (inst.instruction); + + if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL) + return; + + if (flavour != -1) + inst.instruction |= enctab[flavour]; + + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= LOW4 (inst.operands[1].reg); + inst.instruction |= HI1 (inst.operands[1].reg) << 5; + inst.instruction |= neon_quad (rs) << 6; + inst.instruction |= 2 << 18; + + inst.instruction = neon_dp_fixup (inst.instruction); + } + break; + + default: + /* Some VFP conversions go here (s32 <-> f32, u32 <-> f32). */ + do_vfp_nsyn_cvt (rs, flavour); + } +} + +static void +neon_move_immediate (void) +{ + enum neon_shape rs = neon_select_shape (NS_DI, NS_QI, NS_NULL); + struct neon_type_el et = neon_check_type (2, rs, + N_I8 | N_I16 | N_I32 | N_I64 | N_F32 | N_KEY, N_EQK); + unsigned immlo, immhi = 0, immbits; + int op, cmode, float_p; + + constraint (et.type == NT_invtype, + _("operand size must be specified for immediate VMOV")); + + /* We start out as an MVN instruction if OP = 1, MOV otherwise. */ + op = (inst.instruction & (1 << 5)) != 0; + + immlo = inst.operands[1].imm; + if (inst.operands[1].regisimm) + immhi = inst.operands[1].reg; + + constraint (et.size < 32 && (immlo & ~((1 << et.size) - 1)) != 0, + _("immediate has bits set outside the operand size")); + + float_p = inst.operands[1].immisfloat; + + if ((cmode = neon_cmode_for_move_imm (immlo, immhi, float_p, &immbits, &op, + et.size, et.type)) == FAIL) + { + /* Invert relevant bits only. */ + neon_invert_size (&immlo, &immhi, et.size); + /* Flip from VMOV/VMVN to VMVN/VMOV. Some immediate types are unavailable + with one or the other; those cases are caught by + neon_cmode_for_move_imm. */ + op = !op; + if ((cmode = neon_cmode_for_move_imm (immlo, immhi, float_p, &immbits, + &op, et.size, et.type)) == FAIL) + { + first_error (_("immediate out of range")); + return; + } + } + + inst.instruction &= ~(1 << 5); + inst.instruction |= op << 5; + + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= neon_quad (rs) << 6; + inst.instruction |= cmode << 8; + + neon_write_immbits (immbits); +} + +static void +do_neon_mvn (void) +{ + if (inst.operands[1].isreg) + { + enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL); + + inst.instruction = NEON_ENC_INTEGER (inst.instruction); + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= LOW4 (inst.operands[1].reg); + inst.instruction |= HI1 (inst.operands[1].reg) << 5; + inst.instruction |= neon_quad (rs) << 6; + } + else + { + inst.instruction = NEON_ENC_IMMED (inst.instruction); + neon_move_immediate (); + } + + inst.instruction = neon_dp_fixup (inst.instruction); +} + +/* Encode instructions of form: + + |28/24|23|22|21 20|19 16|15 12|11 8|7|6|5|4|3 0| + | U |x |D |size | Rn | Rd |x x x x|N|x|M|x| Rm | + +*/ + +static void +neon_mixed_length (struct neon_type_el et, unsigned size) +{ + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= LOW4 (inst.operands[1].reg) << 16; + inst.instruction |= HI1 (inst.operands[1].reg) << 7; + inst.instruction |= LOW4 (inst.operands[2].reg); + inst.instruction |= HI1 (inst.operands[2].reg) << 5; + inst.instruction |= (et.type == NT_unsigned) << 24; + inst.instruction |= neon_logbits (size) << 20; + + inst.instruction = neon_dp_fixup (inst.instruction); +} + +static void +do_neon_dyadic_long (void) +{ + /* FIXME: Type checking for lengthening op. */ + struct neon_type_el et = neon_check_type (3, NS_QDD, + N_EQK | N_DBL, N_EQK, N_SU_32 | N_KEY); + neon_mixed_length (et, et.size); +} + +static void +do_neon_abal (void) +{ + struct neon_type_el et = neon_check_type (3, NS_QDD, + N_EQK | N_INT | N_DBL, N_EQK, N_SU_32 | N_KEY); + neon_mixed_length (et, et.size); +} + +static void +neon_mac_reg_scalar_long (unsigned regtypes, unsigned scalartypes) +{ + if (inst.operands[2].isscalar) + { + struct neon_type_el et = neon_check_type (3, NS_QDS, + N_EQK | N_DBL, N_EQK, regtypes | N_KEY); + inst.instruction = NEON_ENC_SCALAR (inst.instruction); + neon_mul_mac (et, et.type == NT_unsigned); + } + else + { + struct neon_type_el et = neon_check_type (3, NS_QDD, + N_EQK | N_DBL, N_EQK, scalartypes | N_KEY); + inst.instruction = NEON_ENC_INTEGER (inst.instruction); + neon_mixed_length (et, et.size); + } +} + +static void +do_neon_mac_maybe_scalar_long (void) +{ + neon_mac_reg_scalar_long (N_S16 | N_S32 | N_U16 | N_U32, N_SU_32); +} + +static void +do_neon_dyadic_wide (void) +{ + struct neon_type_el et = neon_check_type (3, NS_QQD, + N_EQK | N_DBL, N_EQK | N_DBL, N_SU_32 | N_KEY); + neon_mixed_length (et, et.size); +} + +static void +do_neon_dyadic_narrow (void) +{ + struct neon_type_el et = neon_check_type (3, NS_QDD, + N_EQK | N_DBL, N_EQK, N_I16 | N_I32 | N_I64 | N_KEY); + /* Operand sign is unimportant, and the U bit is part of the opcode, + so force the operand type to integer. */ + et.type = NT_integer; + neon_mixed_length (et, et.size / 2); +} + +static void +do_neon_mul_sat_scalar_long (void) +{ + neon_mac_reg_scalar_long (N_S16 | N_S32, N_S16 | N_S32); +} + +static void +do_neon_vmull (void) +{ + if (inst.operands[2].isscalar) + do_neon_mac_maybe_scalar_long (); + else + { + struct neon_type_el et = neon_check_type (3, NS_QDD, + N_EQK | N_DBL, N_EQK, N_SU_32 | N_P8 | N_KEY); + if (et.type == NT_poly) + inst.instruction = NEON_ENC_POLY (inst.instruction); + else + inst.instruction = NEON_ENC_INTEGER (inst.instruction); + /* For polynomial encoding, size field must be 0b00 and the U bit must be + zero. Should be OK as-is. */ + neon_mixed_length (et, et.size); + } +} + +static void +do_neon_ext (void) +{ + enum neon_shape rs = neon_select_shape (NS_DDDI, NS_QQQI, NS_NULL); + struct neon_type_el et = neon_check_type (3, rs, + N_EQK, N_EQK, N_8 | N_16 | N_32 | N_64 | N_KEY); + unsigned imm = (inst.operands[3].imm * et.size) / 8; + constraint (imm >= (neon_quad (rs) ? 16 : 8), _("shift out of range")); + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= LOW4 (inst.operands[1].reg) << 16; + inst.instruction |= HI1 (inst.operands[1].reg) << 7; + inst.instruction |= LOW4 (inst.operands[2].reg); + inst.instruction |= HI1 (inst.operands[2].reg) << 5; + inst.instruction |= neon_quad (rs) << 6; + inst.instruction |= imm << 8; + + inst.instruction = neon_dp_fixup (inst.instruction); +} + +static void +do_neon_rev (void) +{ + enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL); + struct neon_type_el et = neon_check_type (2, rs, + N_EQK, N_8 | N_16 | N_32 | N_KEY); + unsigned op = (inst.instruction >> 7) & 3; + /* N (width of reversed regions) is encoded as part of the bitmask. We + extract it here to check the elements to be reversed are smaller. + Otherwise we'd get a reserved instruction. */ + unsigned elsize = (op == 2) ? 16 : (op == 1) ? 32 : (op == 0) ? 64 : 0; + assert (elsize != 0); + constraint (et.size >= elsize, + _("elements must be smaller than reversal region")); + neon_two_same (neon_quad (rs), 1, et.size); +} + +static void +do_neon_dup (void) +{ + if (inst.operands[1].isscalar) + { + enum neon_shape rs = neon_select_shape (NS_DS, NS_QS, NS_NULL); + struct neon_type_el et = neon_check_type (2, rs, + N_EQK, N_8 | N_16 | N_32 | N_KEY); + unsigned sizebits = et.size >> 3; + unsigned dm = NEON_SCALAR_REG (inst.operands[1].reg); + int logsize = neon_logbits (et.size); + unsigned x = NEON_SCALAR_INDEX (inst.operands[1].reg) << logsize; + + if (vfp_or_neon_is_neon (NEON_CHECK_CC) == FAIL) + return; + + inst.instruction = NEON_ENC_SCALAR (inst.instruction); + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= LOW4 (dm); + inst.instruction |= HI1 (dm) << 5; + inst.instruction |= neon_quad (rs) << 6; + inst.instruction |= x << 17; + inst.instruction |= sizebits << 16; + + inst.instruction = neon_dp_fixup (inst.instruction); + } + else + { + enum neon_shape rs = neon_select_shape (NS_DR, NS_QR, NS_NULL); + struct neon_type_el et = neon_check_type (2, rs, + N_8 | N_16 | N_32 | N_KEY, N_EQK); + /* Duplicate ARM register to lanes of vector. */ + inst.instruction = NEON_ENC_ARMREG (inst.instruction); + switch (et.size) + { + case 8: inst.instruction |= 0x400000; break; + case 16: inst.instruction |= 0x000020; break; + case 32: inst.instruction |= 0x000000; break; + default: break; + } + inst.instruction |= LOW4 (inst.operands[1].reg) << 12; + inst.instruction |= LOW4 (inst.operands[0].reg) << 16; + inst.instruction |= HI1 (inst.operands[0].reg) << 7; + inst.instruction |= neon_quad (rs) << 21; + /* The encoding for this instruction is identical for the ARM and Thumb + variants, except for the condition field. */ + do_vfp_cond_or_thumb (); + } +} + +/* VMOV has particularly many variations. It can be one of: + 0. VMOV<c><q> <Qd>, <Qm> + 1. VMOV<c><q> <Dd>, <Dm> + (Register operations, which are VORR with Rm = Rn.) + 2. VMOV<c><q>.<dt> <Qd>, #<imm> + 3. VMOV<c><q>.<dt> <Dd>, #<imm> + (Immediate loads.) + 4. VMOV<c><q>.<size> <Dn[x]>, <Rd> + (ARM register to scalar.) + 5. VMOV<c><q> <Dm>, <Rd>, <Rn> + (Two ARM registers to vector.) + 6. VMOV<c><q>.<dt> <Rd>, <Dn[x]> + (Scalar to ARM register.) + 7. VMOV<c><q> <Rd>, <Rn>, <Dm> + (Vector to two ARM registers.) + 8. VMOV.F32 <Sd>, <Sm> + 9. VMOV.F64 <Dd>, <Dm> + (VFP register moves.) + 10. VMOV.F32 <Sd>, #imm + 11. VMOV.F64 <Dd>, #imm + (VFP float immediate load.) + 12. VMOV <Rd>, <Sm> + (VFP single to ARM reg.) + 13. VMOV <Sd>, <Rm> + (ARM reg to VFP single.) + 14. VMOV <Rd>, <Re>, <Sn>, <Sm> + (Two ARM regs to two VFP singles.) + 15. VMOV <Sd>, <Se>, <Rn>, <Rm> + (Two VFP singles to two ARM regs.) + + These cases can be disambiguated using neon_select_shape, except cases 1/9 + and 3/11 which depend on the operand type too. + + All the encoded bits are hardcoded by this function. + + Cases 4, 6 may be used with VFPv1 and above (only 32-bit transfers!). + Cases 5, 7 may be used with VFPv2 and above. + + FIXME: Some of the checking may be a bit sloppy (in a couple of cases you + can specify a type where it doesn't make sense to, and is ignored). +*/ + +static void +do_neon_mov (void) +{ + enum neon_shape rs = neon_select_shape (NS_RRFF, NS_FFRR, NS_DRR, NS_RRD, + NS_QQ, NS_DD, NS_QI, NS_DI, NS_SR, NS_RS, NS_FF, NS_FI, NS_RF, NS_FR, + NS_NULL); + struct neon_type_el et; + const char *ldconst = 0; + + switch (rs) + { + case NS_DD: /* case 1/9. */ + et = neon_check_type (2, rs, N_EQK, N_F64 | N_KEY); + /* It is not an error here if no type is given. */ + inst.error = NULL; + if (et.type == NT_float && et.size == 64) + { + do_vfp_nsyn_opcode ("fcpyd"); + break; + } + /* fall through. */ + + case NS_QQ: /* case 0/1. */ + { + if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL) + return; + /* The architecture manual I have doesn't explicitly state which + value the U bit should have for register->register moves, but + the equivalent VORR instruction has U = 0, so do that. */ + inst.instruction = 0x0200110; + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= LOW4 (inst.operands[1].reg); + inst.instruction |= HI1 (inst.operands[1].reg) << 5; + inst.instruction |= LOW4 (inst.operands[1].reg) << 16; + inst.instruction |= HI1 (inst.operands[1].reg) << 7; + inst.instruction |= neon_quad (rs) << 6; + + inst.instruction = neon_dp_fixup (inst.instruction); + } + break; + + case NS_DI: /* case 3/11. */ + et = neon_check_type (2, rs, N_EQK, N_F64 | N_KEY); + inst.error = NULL; + if (et.type == NT_float && et.size == 64) + { + /* case 11 (fconstd). */ + ldconst = "fconstd"; + goto encode_fconstd; + } + /* fall through. */ + + case NS_QI: /* case 2/3. */ + if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL) + return; + inst.instruction = 0x0800010; + neon_move_immediate (); + inst.instruction = neon_dp_fixup (inst.instruction); + break; + + case NS_SR: /* case 4. */ + { + unsigned bcdebits = 0; + struct neon_type_el et = neon_check_type (2, NS_NULL, + N_8 | N_16 | N_32 | N_KEY, N_EQK); + int logsize = neon_logbits (et.size); + unsigned dn = NEON_SCALAR_REG (inst.operands[0].reg); + unsigned x = NEON_SCALAR_INDEX (inst.operands[0].reg); + + constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v1), + _(BAD_FPU)); + constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_neon_ext_v1) + && et.size != 32, _(BAD_FPU)); + constraint (et.type == NT_invtype, _("bad type for scalar")); + constraint (x >= 64 / et.size, _("scalar index out of range")); + + switch (et.size) + { + case 8: bcdebits = 0x8; break; + case 16: bcdebits = 0x1; break; + case 32: bcdebits = 0x0; break; + default: ; + } + + bcdebits |= x << logsize; + + inst.instruction = 0xe000b10; + do_vfp_cond_or_thumb (); + inst.instruction |= LOW4 (dn) << 16; + inst.instruction |= HI1 (dn) << 7; + inst.instruction |= inst.operands[1].reg << 12; + inst.instruction |= (bcdebits & 3) << 5; + inst.instruction |= (bcdebits >> 2) << 21; + } + break; + + case NS_DRR: /* case 5 (fmdrr). */ + constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v2), + _(BAD_FPU)); + + inst.instruction = 0xc400b10; + do_vfp_cond_or_thumb (); + inst.instruction |= LOW4 (inst.operands[0].reg); + inst.instruction |= HI1 (inst.operands[0].reg) << 5; + inst.instruction |= inst.operands[1].reg << 12; + inst.instruction |= inst.operands[2].reg << 16; + break; + + case NS_RS: /* case 6. */ + { + struct neon_type_el et = neon_check_type (2, NS_NULL, + N_EQK, N_S8 | N_S16 | N_U8 | N_U16 | N_32 | N_KEY); + unsigned logsize = neon_logbits (et.size); + unsigned dn = NEON_SCALAR_REG (inst.operands[1].reg); + unsigned x = NEON_SCALAR_INDEX (inst.operands[1].reg); + unsigned abcdebits = 0; + + constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v1), + _(BAD_FPU)); + constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_neon_ext_v1) + && et.size != 32, _(BAD_FPU)); + constraint (et.type == NT_invtype, _("bad type for scalar")); + constraint (x >= 64 / et.size, _("scalar index out of range")); + + switch (et.size) + { + case 8: abcdebits = (et.type == NT_signed) ? 0x08 : 0x18; break; + case 16: abcdebits = (et.type == NT_signed) ? 0x01 : 0x11; break; + case 32: abcdebits = 0x00; break; + default: ; + } + + abcdebits |= x << logsize; + inst.instruction = 0xe100b10; + do_vfp_cond_or_thumb (); + inst.instruction |= LOW4 (dn) << 16; + inst.instruction |= HI1 (dn) << 7; + inst.instruction |= inst.operands[0].reg << 12; + inst.instruction |= (abcdebits & 3) << 5; + inst.instruction |= (abcdebits >> 2) << 21; + } + break; + + case NS_RRD: /* case 7 (fmrrd). */ + constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v2), + _(BAD_FPU)); + + inst.instruction = 0xc500b10; + do_vfp_cond_or_thumb (); + inst.instruction |= inst.operands[0].reg << 12; + inst.instruction |= inst.operands[1].reg << 16; + inst.instruction |= LOW4 (inst.operands[2].reg); + inst.instruction |= HI1 (inst.operands[2].reg) << 5; + break; + + case NS_FF: /* case 8 (fcpys). */ + do_vfp_nsyn_opcode ("fcpys"); + break; + + case NS_FI: /* case 10 (fconsts). */ + ldconst = "fconsts"; + encode_fconstd: + if (is_quarter_float (inst.operands[1].imm)) + { + inst.operands[1].imm = neon_qfloat_bits (inst.operands[1].imm); + do_vfp_nsyn_opcode (ldconst); + } + else + first_error (_("immediate out of range")); + break; + + case NS_RF: /* case 12 (fmrs). */ + do_vfp_nsyn_opcode ("fmrs"); + break; + + case NS_FR: /* case 13 (fmsr). */ + do_vfp_nsyn_opcode ("fmsr"); + break; + + /* The encoders for the fmrrs and fmsrr instructions expect three operands + (one of which is a list), but we have parsed four. Do some fiddling to + make the operands what do_vfp_reg2_from_sp2 and do_vfp_sp2_from_reg2 + expect. */ + case NS_RRFF: /* case 14 (fmrrs). */ + constraint (inst.operands[3].reg != inst.operands[2].reg + 1, + _("VFP registers must be adjacent")); + inst.operands[2].imm = 2; + memset (&inst.operands[3], '\0', sizeof (inst.operands[3])); + do_vfp_nsyn_opcode ("fmrrs"); + break; + + case NS_FFRR: /* case 15 (fmsrr). */ + constraint (inst.operands[1].reg != inst.operands[0].reg + 1, + _("VFP registers must be adjacent")); + inst.operands[1] = inst.operands[2]; + inst.operands[2] = inst.operands[3]; + inst.operands[0].imm = 2; + memset (&inst.operands[3], '\0', sizeof (inst.operands[3])); + do_vfp_nsyn_opcode ("fmsrr"); + break; + + default: + abort (); + } +} + +static void +do_neon_rshift_round_imm (void) +{ + enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_NULL); + struct neon_type_el et = neon_check_type (2, rs, N_EQK, N_SU_ALL | N_KEY); + int imm = inst.operands[2].imm; + + /* imm == 0 case is encoded as VMOV for V{R}SHR. */ + if (imm == 0) + { + inst.operands[2].present = 0; + do_neon_mov (); + return; + } + + constraint (imm < 1 || (unsigned)imm > et.size, + _("immediate out of range for shift")); + neon_imm_shift (TRUE, et.type == NT_unsigned, neon_quad (rs), et, + et.size - imm); +} + +static void +do_neon_movl (void) +{ + struct neon_type_el et = neon_check_type (2, NS_QD, + N_EQK | N_DBL, N_SU_32 | N_KEY); + unsigned sizebits = et.size >> 3; + inst.instruction |= sizebits << 19; + neon_two_same (0, et.type == NT_unsigned, -1); +} + +static void +do_neon_trn (void) +{ + enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL); + struct neon_type_el et = neon_check_type (2, rs, + N_EQK, N_8 | N_16 | N_32 | N_KEY); + inst.instruction = NEON_ENC_INTEGER (inst.instruction); + neon_two_same (neon_quad (rs), 1, et.size); +} + +static void +do_neon_zip_uzp (void) +{ + enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL); + struct neon_type_el et = neon_check_type (2, rs, + N_EQK, N_8 | N_16 | N_32 | N_KEY); + if (rs == NS_DD && et.size == 32) + { + /* Special case: encode as VTRN.32 <Dd>, <Dm>. */ + inst.instruction = N_MNEM_vtrn; + do_neon_trn (); + return; + } + neon_two_same (neon_quad (rs), 1, et.size); +} + +static void +do_neon_sat_abs_neg (void) +{ + enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL); + struct neon_type_el et = neon_check_type (2, rs, + N_EQK, N_S8 | N_S16 | N_S32 | N_KEY); + neon_two_same (neon_quad (rs), 1, et.size); +} + +static void +do_neon_pair_long (void) +{ + enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL); + struct neon_type_el et = neon_check_type (2, rs, N_EQK, N_SU_32 | N_KEY); + /* Unsigned is encoded in OP field (bit 7) for these instruction. */ + inst.instruction |= (et.type == NT_unsigned) << 7; + neon_two_same (neon_quad (rs), 1, et.size); +} + +static void +do_neon_recip_est (void) +{ + enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL); + struct neon_type_el et = neon_check_type (2, rs, + N_EQK | N_FLT, N_F32 | N_U32 | N_KEY); + inst.instruction |= (et.type == NT_float) << 8; + neon_two_same (neon_quad (rs), 1, et.size); +} + +static void +do_neon_cls (void) +{ + enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL); + struct neon_type_el et = neon_check_type (2, rs, + N_EQK, N_S8 | N_S16 | N_S32 | N_KEY); + neon_two_same (neon_quad (rs), 1, et.size); +} + +static void +do_neon_clz (void) +{ + enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL); + struct neon_type_el et = neon_check_type (2, rs, + N_EQK, N_I8 | N_I16 | N_I32 | N_KEY); + neon_two_same (neon_quad (rs), 1, et.size); +} + +static void +do_neon_cnt (void) +{ + enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL); + struct neon_type_el et = neon_check_type (2, rs, + N_EQK | N_INT, N_8 | N_KEY); + neon_two_same (neon_quad (rs), 1, et.size); +} + +static void +do_neon_swp (void) +{ + enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL); + neon_two_same (neon_quad (rs), 1, -1); +} + +static void +do_neon_tbl_tbx (void) +{ + unsigned listlenbits; + neon_check_type (3, NS_DLD, N_EQK, N_EQK, N_8 | N_KEY); + + if (inst.operands[1].imm < 1 || inst.operands[1].imm > 4) + { + first_error (_("bad list length for table lookup")); + return; + } + + listlenbits = inst.operands[1].imm - 1; + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= LOW4 (inst.operands[1].reg) << 16; + inst.instruction |= HI1 (inst.operands[1].reg) << 7; + inst.instruction |= LOW4 (inst.operands[2].reg); + inst.instruction |= HI1 (inst.operands[2].reg) << 5; + inst.instruction |= listlenbits << 8; + + inst.instruction = neon_dp_fixup (inst.instruction); +} + +static void +do_neon_ldm_stm (void) +{ + /* P, U and L bits are part of bitmask. */ + int is_dbmode = (inst.instruction & (1 << 24)) != 0; + unsigned offsetbits = inst.operands[1].imm * 2; + + if (inst.operands[1].issingle) + { + do_vfp_nsyn_ldm_stm (is_dbmode); + return; + } + + constraint (is_dbmode && !inst.operands[0].writeback, + _("writeback (!) must be used for VLDMDB and VSTMDB")); + + constraint (inst.operands[1].imm < 1 || inst.operands[1].imm > 16, + _("register list must contain at least 1 and at most 16 " + "registers")); + + inst.instruction |= inst.operands[0].reg << 16; + inst.instruction |= inst.operands[0].writeback << 21; + inst.instruction |= LOW4 (inst.operands[1].reg) << 12; + inst.instruction |= HI1 (inst.operands[1].reg) << 22; + + inst.instruction |= offsetbits; + + do_vfp_cond_or_thumb (); +} + +static void +do_neon_ldr_str (void) +{ + int is_ldr = (inst.instruction & (1 << 20)) != 0; + + if (inst.operands[0].issingle) + { + if (is_ldr) + do_vfp_nsyn_opcode ("flds"); + else + do_vfp_nsyn_opcode ("fsts"); + } + else + { + if (is_ldr) + do_vfp_nsyn_opcode ("fldd"); + else + do_vfp_nsyn_opcode ("fstd"); + } +} + +/* "interleave" version also handles non-interleaving register VLD1/VST1 + instructions. */ + +static void +do_neon_ld_st_interleave (void) +{ + struct neon_type_el et = neon_check_type (1, NS_NULL, + N_8 | N_16 | N_32 | N_64); + unsigned alignbits = 0; + unsigned idx; + /* The bits in this table go: + 0: register stride of one (0) or two (1) + 1,2: register list length, minus one (1, 2, 3, 4). + 3,4: <n> in instruction type, minus one (VLD<n> / VST<n>). + We use -1 for invalid entries. */ + const int typetable[] = + { + 0x7, -1, 0xa, -1, 0x6, -1, 0x2, -1, /* VLD1 / VST1. */ + -1, -1, 0x8, 0x9, -1, -1, 0x3, -1, /* VLD2 / VST2. */ + -1, -1, -1, -1, 0x4, 0x5, -1, -1, /* VLD3 / VST3. */ + -1, -1, -1, -1, -1, -1, 0x0, 0x1 /* VLD4 / VST4. */ + }; + int typebits; + + if (et.type == NT_invtype) + return; + + if (inst.operands[1].immisalign) + switch (inst.operands[1].imm >> 8) + { + case 64: alignbits = 1; break; + case 128: + if (NEON_REGLIST_LENGTH (inst.operands[0].imm) == 3) + goto bad_alignment; + alignbits = 2; + break; + case 256: + if (NEON_REGLIST_LENGTH (inst.operands[0].imm) == 3) + goto bad_alignment; + alignbits = 3; + break; + default: + bad_alignment: + first_error (_("bad alignment")); + return; + } + + inst.instruction |= alignbits << 4; + inst.instruction |= neon_logbits (et.size) << 6; + + /* Bits [4:6] of the immediate in a list specifier encode register stride + (minus 1) in bit 4, and list length in bits [5:6]. We put the <n> of + VLD<n>/VST<n> in bits [9:8] of the initial bitmask. Suck it out here, look + up the right value for "type" in a table based on this value and the given + list style, then stick it back. */ + idx = ((inst.operands[0].imm >> 4) & 7) + | (((inst.instruction >> 8) & 3) << 3); + + typebits = typetable[idx]; + + constraint (typebits == -1, _("bad list type for instruction")); + + inst.instruction &= ~0xf00; + inst.instruction |= typebits << 8; +} + +/* Check alignment is valid for do_neon_ld_st_lane and do_neon_ld_dup. + *DO_ALIGN is set to 1 if the relevant alignment bit should be set, 0 + otherwise. The variable arguments are a list of pairs of legal (size, align) + values, terminated with -1. */ + +static int +neon_alignment_bit (int size, int align, int *do_align, ...) +{ + va_list ap; + int result = FAIL, thissize, thisalign; + + if (!inst.operands[1].immisalign) + { + *do_align = 0; + return SUCCESS; + } + + va_start (ap, do_align); + + do + { + thissize = va_arg (ap, int); + if (thissize == -1) + break; + thisalign = va_arg (ap, int); + + if (size == thissize && align == thisalign) + result = SUCCESS; + } + while (result != SUCCESS); + + va_end (ap); + + if (result == SUCCESS) + *do_align = 1; + else + first_error (_("unsupported alignment for instruction")); + + return result; +} + +static void +do_neon_ld_st_lane (void) +{ + struct neon_type_el et = neon_check_type (1, NS_NULL, N_8 | N_16 | N_32); + int align_good, do_align = 0; + int logsize = neon_logbits (et.size); + int align = inst.operands[1].imm >> 8; + int n = (inst.instruction >> 8) & 3; + int max_el = 64 / et.size; + + if (et.type == NT_invtype) + return; + + constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != n + 1, + _("bad list length")); + constraint (NEON_LANE (inst.operands[0].imm) >= max_el, + _("scalar index out of range")); + constraint (n != 0 && NEON_REG_STRIDE (inst.operands[0].imm) == 2 + && et.size == 8, + _("stride of 2 unavailable when element size is 8")); + + switch (n) + { + case 0: /* VLD1 / VST1. */ + align_good = neon_alignment_bit (et.size, align, &do_align, 16, 16, + 32, 32, -1); + if (align_good == FAIL) + return; + if (do_align) + { + unsigned alignbits = 0; + switch (et.size) + { + case 16: alignbits = 0x1; break; + case 32: alignbits = 0x3; break; + default: ; + } + inst.instruction |= alignbits << 4; + } + break; + + case 1: /* VLD2 / VST2. */ + align_good = neon_alignment_bit (et.size, align, &do_align, 8, 16, 16, 32, + 32, 64, -1); + if (align_good == FAIL) + return; + if (do_align) + inst.instruction |= 1 << 4; + break; + + case 2: /* VLD3 / VST3. */ + constraint (inst.operands[1].immisalign, + _("can't use alignment with this instruction")); + break; + + case 3: /* VLD4 / VST4. */ + align_good = neon_alignment_bit (et.size, align, &do_align, 8, 32, + 16, 64, 32, 64, 32, 128, -1); + if (align_good == FAIL) + return; + if (do_align) + { + unsigned alignbits = 0; + switch (et.size) + { + case 8: alignbits = 0x1; break; + case 16: alignbits = 0x1; break; + case 32: alignbits = (align == 64) ? 0x1 : 0x2; break; + default: ; + } + inst.instruction |= alignbits << 4; + } + break; + + default: ; + } + + /* Reg stride of 2 is encoded in bit 5 when size==16, bit 6 when size==32. */ + if (n != 0 && NEON_REG_STRIDE (inst.operands[0].imm) == 2) + inst.instruction |= 1 << (4 + logsize); + + inst.instruction |= NEON_LANE (inst.operands[0].imm) << (logsize + 5); + inst.instruction |= logsize << 10; +} + +/* Encode single n-element structure to all lanes VLD<n> instructions. */ + +static void +do_neon_ld_dup (void) +{ + struct neon_type_el et = neon_check_type (1, NS_NULL, N_8 | N_16 | N_32); + int align_good, do_align = 0; + + if (et.type == NT_invtype) + return; + + switch ((inst.instruction >> 8) & 3) + { + case 0: /* VLD1. */ + assert (NEON_REG_STRIDE (inst.operands[0].imm) != 2); + align_good = neon_alignment_bit (et.size, inst.operands[1].imm >> 8, + &do_align, 16, 16, 32, 32, -1); + if (align_good == FAIL) + return; + switch (NEON_REGLIST_LENGTH (inst.operands[0].imm)) + { + case 1: break; + case 2: inst.instruction |= 1 << 5; break; + default: first_error (_("bad list length")); return; + } + inst.instruction |= neon_logbits (et.size) << 6; + break; + + case 1: /* VLD2. */ + align_good = neon_alignment_bit (et.size, inst.operands[1].imm >> 8, + &do_align, 8, 16, 16, 32, 32, 64, -1); + if (align_good == FAIL) + return; + constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 2, + _("bad list length")); + if (NEON_REG_STRIDE (inst.operands[0].imm) == 2) + inst.instruction |= 1 << 5; + inst.instruction |= neon_logbits (et.size) << 6; + break; + + case 2: /* VLD3. */ + constraint (inst.operands[1].immisalign, + _("can't use alignment with this instruction")); + constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 3, + _("bad list length")); + if (NEON_REG_STRIDE (inst.operands[0].imm) == 2) + inst.instruction |= 1 << 5; + inst.instruction |= neon_logbits (et.size) << 6; + break; + + case 3: /* VLD4. */ + { + int align = inst.operands[1].imm >> 8; + align_good = neon_alignment_bit (et.size, align, &do_align, 8, 32, + 16, 64, 32, 64, 32, 128, -1); + if (align_good == FAIL) + return; + constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 4, + _("bad list length")); + if (NEON_REG_STRIDE (inst.operands[0].imm) == 2) + inst.instruction |= 1 << 5; + if (et.size == 32 && align == 128) + inst.instruction |= 0x3 << 6; + else + inst.instruction |= neon_logbits (et.size) << 6; + } + break; + + default: ; + } + + inst.instruction |= do_align << 4; +} + +/* Disambiguate VLD<n> and VST<n> instructions, and fill in common bits (those + apart from bits [11:4]. */ + +static void +do_neon_ldx_stx (void) +{ + switch (NEON_LANE (inst.operands[0].imm)) + { + case NEON_INTERLEAVE_LANES: + inst.instruction = NEON_ENC_INTERLV (inst.instruction); + do_neon_ld_st_interleave (); + break; + + case NEON_ALL_LANES: + inst.instruction = NEON_ENC_DUP (inst.instruction); + do_neon_ld_dup (); + break; + + default: + inst.instruction = NEON_ENC_LANE (inst.instruction); + do_neon_ld_st_lane (); + } + + /* L bit comes from bit mask. */ + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= inst.operands[1].reg << 16; + + if (inst.operands[1].postind) + { + int postreg = inst.operands[1].imm & 0xf; + constraint (!inst.operands[1].immisreg, + _("post-index must be a register")); + constraint (postreg == 0xd || postreg == 0xf, + _("bad register for post-index")); + inst.instruction |= postreg; + } + else if (inst.operands[1].writeback) + { + inst.instruction |= 0xd; + } + else + inst.instruction |= 0xf; + + if (thumb_mode) + inst.instruction |= 0xf9000000; + else + inst.instruction |= 0xf4000000; +} + /* Overall per-instruction processing. */ @@ -8045,11 +13785,9 @@ output_relax_insn (void) symbolS *sym; int offset; -#ifdef OBJ_ELF /* The size of the instruction is unknown, so tie the debug info to the start of the instruction. */ dwarf2_emit_insn (0); -#endif switch (inst.reloc.exp.X_op) { @@ -8117,9 +13855,7 @@ output_inst (const char * str) inst.size, & inst.reloc.exp, inst.reloc.pc_rel, inst.reloc.type); -#ifdef OBJ_ELF dwarf2_emit_insn (inst.size); -#endif } /* Tag values used in struct asm_opcode's tag field. */ @@ -8130,9 +13866,14 @@ enum opcode_tag OT_unconditionalF, /* Instruction cannot be conditionalized and carries 0xF in its ARM condition field. */ OT_csuffix, /* Instruction takes a conditional suffix. */ + OT_csuffixF, /* Some forms of the instruction take a conditional + suffix, others place 0xF where the condition field + would be. */ OT_cinfix3, /* Instruction takes a conditional infix, beginning at character index 3. (In unified mode, it becomes a suffix.) */ + OT_cinfix3_deprecated, /* The same as OT_cinfix3. This is used for + tsts, cmps, cmns, and teqs. */ OT_cinfix3_legacy, /* Legacy instruction takes a conditional infix at character index 3, even in unified mode. Used for legacy instructions where suffix and infix forms @@ -8211,27 +13952,46 @@ opcode_lookup (char **str) const struct asm_opcode *opcode; const struct asm_cond *cond; char save[2]; + bfd_boolean neon_supported; + + neon_supported = ARM_CPU_HAS_FEATURE (cpu_variant, fpu_neon_ext_v1); /* Scan up to the end of the mnemonic, which must end in white space, - '.' (in unified mode only), or end of string. */ + '.' (in unified mode, or for Neon instructions), or end of string. */ for (base = end = *str; *end != '\0'; end++) - if (*end == ' ' || (unified_syntax && *end == '.')) + if (*end == ' ' || ((unified_syntax || neon_supported) && *end == '.')) break; if (end == base) return 0; - /* Handle a possible width suffix. */ + /* Handle a possible width suffix and/or Neon type suffix. */ if (end[0] == '.') { - if (end[1] == 'w' && (end[2] == ' ' || end[2] == '\0')) + int offset = 2; + + /* The .w and .n suffixes are only valid if the unified syntax is in + use. */ + if (unified_syntax && end[1] == 'w') inst.size_req = 4; - else if (end[1] == 'n' && (end[2] == ' ' || end[2] == '\0')) + else if (unified_syntax && end[1] == 'n') inst.size_req = 2; else - return 0; + offset = 0; + + inst.vectype.elems = 0; - *str = end + 2; + *str = end + offset; + + if (end[offset] == '.') + { + /* See if we have a Neon type suffix (possible in either unified or + non-unified ARM syntax mode). */ + if (parse_neon_type (&inst.vectype, str) == FAIL) + return 0; + } + else if (end[offset] != '\0' && end[offset] != ' ') + return 0; } else *str = end; @@ -8276,12 +14036,14 @@ opcode_lookup (char **str) break; case OT_cinfix3: + case OT_cinfix3_deprecated: case OT_odd_infix_unc: if (!unified_syntax) return 0; /* else fall through */ case OT_csuffix: + case OT_csuffixF: case OT_csuf_or_in3: inst.cond = cond->value; return opcode; @@ -8322,11 +14084,16 @@ opcode_lookup (char **str) memmove (affix + 2, affix, (end - affix) - 2); memcpy (affix, save, 2); - if (opcode && (opcode->tag == OT_cinfix3 || opcode->tag == OT_csuf_or_in3 - || opcode->tag == OT_cinfix3_legacy)) + if (opcode + && (opcode->tag == OT_cinfix3 + || opcode->tag == OT_cinfix3_deprecated + || opcode->tag == OT_csuf_or_in3 + || opcode->tag == OT_cinfix3_legacy)) { /* step CM */ - if (unified_syntax && opcode->tag == OT_cinfix3) + if (unified_syntax + && (opcode->tag == OT_cinfix3 + || opcode->tag == OT_cinfix3_deprecated)) as_warn (_("conditional infixes are deprecated in unified syntax")); inst.cond = cond->value; @@ -8357,13 +14124,21 @@ md_assemble (char *str) if (!opcode) { /* It wasn't an instruction, but it might be a register alias of - the form alias .req reg. */ - if (!create_register_alias (str, p)) + the form alias .req reg, or a Neon .dn/.qn directive. */ + if (!create_register_alias (str, p) + && !create_neon_reg_alias (str, p)) as_bad (_("bad instruction `%s'"), str); return; } + if (opcode->tag == OT_cinfix3_deprecated) + as_warn (_("s suffix on comparison instruction is deprecated")); + + /* The value which unconditional instructions should have in place of the + condition field. */ + inst.uncond_value = (opcode->tag == OT_csuffixF) ? 0xf : -1; + if (thumb_mode) { arm_feature_set variant; @@ -8387,6 +14162,14 @@ md_assemble (char *str) return; } + if (!ARM_CPU_HAS_FEATURE (variant, arm_ext_v6t2) && !inst.size_req) + { + /* Implicit require narrow instructions on Thumb-1. This avoids + relaxation accidentally introducing Thumb-2 instructions. */ + if (opcode->tencode != do_t_blx && opcode->tencode != do_t_branch23) + inst.size_req = 2; + } + /* Check conditional suffixes. */ if (current_it_mask) { @@ -8428,10 +14211,15 @@ md_assemble (char *str) return; } } + + /* Something has gone badly wrong if we try to relax a fixed size + instruction. */ + assert (inst.size_req == 0 || !inst.relax); + ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used, *opcode->tvariant); /* Many Thumb-2 instructions also have Thumb-1 variants, so explicitly - set those bits when Thumb-2 32-bit instuctions are seen. ie. + set those bits when Thumb-2 32-bit instructions are seen. ie. anything other than bl/blx. This is overly pessimistic for relaxable instructions. */ if ((inst.size == 4 && (inst.instruction & 0xf800e800) != 0xf000e800) @@ -8439,7 +14227,7 @@ md_assemble (char *str) ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used, arm_ext_v6t2); } - else + else if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v1)) { /* Check that this instruction is supported for this CPU. */ if (!opcode->avariant || @@ -8472,6 +14260,12 @@ md_assemble (char *str) ARM_MERGE_FEATURE_SETS (arm_arch_used, arm_arch_used, *opcode->avariant); } + else + { + as_bad (_("attempt to use an ARM instruction on a Thumb-only processor " + "-- `%s'"), str); + return; + } output_inst (str); } @@ -8537,9 +14331,7 @@ arm_frob_label (symbolS * sym) label_is_thumb_function_name = FALSE; } -#ifdef OBJ_ELF dwarf2_emit_label (sym); -#endif } int @@ -8573,13 +14365,24 @@ arm_canonicalize_symbol_name (char * name) should appear in both upper and lowercase variants. Some registers also have mixed-case names. */ -#define REGDEF(s,n,t) { #s, n, REG_TYPE_##t, TRUE } +#define REGDEF(s,n,t) { #s, n, REG_TYPE_##t, TRUE, 0 } #define REGNUM(p,n,t) REGDEF(p##n, n, t) +#define REGNUM2(p,n,t) REGDEF(p##n, 2 * n, t) #define REGSET(p,t) \ REGNUM(p, 0,t), REGNUM(p, 1,t), REGNUM(p, 2,t), REGNUM(p, 3,t), \ REGNUM(p, 4,t), REGNUM(p, 5,t), REGNUM(p, 6,t), REGNUM(p, 7,t), \ REGNUM(p, 8,t), REGNUM(p, 9,t), REGNUM(p,10,t), REGNUM(p,11,t), \ REGNUM(p,12,t), REGNUM(p,13,t), REGNUM(p,14,t), REGNUM(p,15,t) +#define REGSETH(p,t) \ + REGNUM(p,16,t), REGNUM(p,17,t), REGNUM(p,18,t), REGNUM(p,19,t), \ + REGNUM(p,20,t), REGNUM(p,21,t), REGNUM(p,22,t), REGNUM(p,23,t), \ + REGNUM(p,24,t), REGNUM(p,25,t), REGNUM(p,26,t), REGNUM(p,27,t), \ + REGNUM(p,28,t), REGNUM(p,29,t), REGNUM(p,30,t), REGNUM(p,31,t) +#define REGSET2(p,t) \ + REGNUM2(p, 0,t), REGNUM2(p, 1,t), REGNUM2(p, 2,t), REGNUM2(p, 3,t), \ + REGNUM2(p, 4,t), REGNUM2(p, 5,t), REGNUM2(p, 6,t), REGNUM2(p, 7,t), \ + REGNUM2(p, 8,t), REGNUM2(p, 9,t), REGNUM2(p,10,t), REGNUM2(p,11,t), \ + REGNUM2(p,12,t), REGNUM2(p,13,t), REGNUM2(p,14,t), REGNUM2(p,15,t) static const struct reg_entry reg_names[] = { @@ -8618,24 +14421,24 @@ static const struct reg_entry reg_names[] = REGNUM(F,4,FN), REGNUM(F,5,FN), REGNUM(F,6,FN), REGNUM(F,7, FN), /* VFP SP registers. */ - REGSET(s,VFS), - REGNUM(s,16,VFS), REGNUM(s,17,VFS), REGNUM(s,18,VFS), REGNUM(s,19,VFS), - REGNUM(s,20,VFS), REGNUM(s,21,VFS), REGNUM(s,22,VFS), REGNUM(s,23,VFS), - REGNUM(s,24,VFS), REGNUM(s,25,VFS), REGNUM(s,26,VFS), REGNUM(s,27,VFS), - REGNUM(s,28,VFS), REGNUM(s,29,VFS), REGNUM(s,30,VFS), REGNUM(s,31,VFS), - - REGSET(S,VFS), - REGNUM(S,16,VFS), REGNUM(S,17,VFS), REGNUM(S,18,VFS), REGNUM(S,19,VFS), - REGNUM(S,20,VFS), REGNUM(S,21,VFS), REGNUM(S,22,VFS), REGNUM(S,23,VFS), - REGNUM(S,24,VFS), REGNUM(S,25,VFS), REGNUM(S,26,VFS), REGNUM(S,27,VFS), - REGNUM(S,28,VFS), REGNUM(S,29,VFS), REGNUM(S,30,VFS), REGNUM(S,31,VFS), + REGSET(s,VFS), REGSET(S,VFS), + REGSETH(s,VFS), REGSETH(S,VFS), /* VFP DP Registers. */ - REGSET(d,VFD), REGSET(D,VFS), + REGSET(d,VFD), REGSET(D,VFD), + /* Extra Neon DP registers. */ + REGSETH(d,VFD), REGSETH(D,VFD), + + /* Neon QP registers. */ + REGSET2(q,NQ), REGSET2(Q,NQ), /* VFP control registers. */ REGDEF(fpsid,0,VFC), REGDEF(fpscr,1,VFC), REGDEF(fpexc,8,VFC), REGDEF(FPSID,0,VFC), REGDEF(FPSCR,1,VFC), REGDEF(FPEXC,8,VFC), + REGDEF(fpinst,9,VFC), REGDEF(fpinst2,10,VFC), + REGDEF(FPINST,9,VFC), REGDEF(FPINST2,10,VFC), + REGDEF(mvfr0,7,VFC), REGDEF(mvfr1,6,VFC), + REGDEF(MVFR0,7,VFC), REGDEF(MVFR1,6,VFC), /* Maverick DSP coprocessor registers. */ REGSET(mvf,MVF), REGSET(mvd,MVD), REGSET(mvfx,MVFX), REGSET(mvdx,MVDX), @@ -8752,20 +14555,21 @@ static const struct asm_psr psrs[] = /* Table of V7M psr names. */ static const struct asm_psr v7m_psrs[] = { - {"apsr", 0 }, - {"iapsr", 1 }, - {"eapsr", 2 }, - {"psr", 3 }, - {"ipsr", 5 }, - {"epsr", 6 }, - {"iepsr", 7 }, - {"msp", 8 }, - {"psp", 9 }, - {"primask", 16}, - {"basepri", 17}, - {"basepri_max", 18}, - {"faultmask", 19}, - {"control", 20} + {"apsr", 0 }, {"APSR", 0 }, + {"iapsr", 1 }, {"IAPSR", 1 }, + {"eapsr", 2 }, {"EAPSR", 2 }, + {"psr", 3 }, {"PSR", 3 }, + {"xpsr", 3 }, {"XPSR", 3 }, {"xPSR", 3 }, + {"ipsr", 5 }, {"IPSR", 5 }, + {"epsr", 6 }, {"EPSR", 6 }, + {"iepsr", 7 }, {"IEPSR", 7 }, + {"msp", 8 }, {"MSP", 8 }, + {"psp", 9 }, {"PSP", 9 }, + {"primask", 16}, {"PRIMASK", 16}, + {"basepri", 17}, {"BASEPRI", 17}, + {"basepri_max", 18}, {"BASEPRI_MAX", 18}, + {"faultmask", 19}, {"FAULTMASK", 19}, + {"control", 20}, {"CONTROL", 20} }; /* Table of all shift-in-operand names. */ @@ -8858,10 +14662,17 @@ static struct asm_barrier_opt barrier_opt_names[] = #define TxC3(mnem, op, top, nops, ops, ae, te) \ { #mnem, OPS##nops ops, OT_cinfix3, 0x##op, top, ARM_VARIANT, \ THUMB_VARIANT, do_##ae, do_##te } +#define TxC3w(mnem, op, top, nops, ops, ae, te) \ + { #mnem, OPS##nops ops, OT_cinfix3_deprecated, 0x##op, top, ARM_VARIANT, \ + THUMB_VARIANT, do_##ae, do_##te } #define TC3(mnem, aop, top, nops, ops, ae, te) \ TxC3(mnem, aop, 0x##top, nops, ops, ae, te) +#define TC3w(mnem, aop, top, nops, ops, ae, te) \ + TxC3w(mnem, aop, 0x##top, nops, ops, ae, te) #define tC3(mnem, aop, top, nops, ops, ae, te) \ TxC3(mnem, aop, T_MNEM_##top, nops, ops, ae, te) +#define tC3w(mnem, aop, top, nops, ops, ae, te) \ + TxC3w(mnem, aop, T_MNEM_##top, nops, ops, ae, te) /* Mnemonic with a conditional infix in an unusual place. Each and every variant has to appear in the condition table. */ @@ -8970,6 +14781,42 @@ static struct asm_barrier_opt barrier_opt_names[] = #define UF(mnem, op, nops, ops, ae) \ { #mnem, OPS##nops ops, OT_unconditionalF, 0x##op, 0, ARM_VARIANT, 0, do_##ae, NULL } +/* Neon data-processing. ARM versions are unconditional with cond=0xf. + The Thumb and ARM variants are mostly the same (bits 0-23 and 24/28), so we + use the same encoding function for each. */ +#define NUF(mnem, op, nops, ops, enc) \ + { #mnem, OPS##nops ops, OT_unconditionalF, 0x##op, 0x##op, \ + ARM_VARIANT, THUMB_VARIANT, do_##enc, do_##enc } + +/* Neon data processing, version which indirects through neon_enc_tab for + the various overloaded versions of opcodes. */ +#define nUF(mnem, op, nops, ops, enc) \ + { #mnem, OPS##nops ops, OT_unconditionalF, N_MNEM_##op, N_MNEM_##op, \ + ARM_VARIANT, THUMB_VARIANT, do_##enc, do_##enc } + +/* Neon insn with conditional suffix for the ARM version, non-overloaded + version. */ +#define NCE_tag(mnem, op, nops, ops, enc, tag) \ + { #mnem, OPS##nops ops, tag, 0x##op, 0x##op, ARM_VARIANT, \ + THUMB_VARIANT, do_##enc, do_##enc } + +#define NCE(mnem, op, nops, ops, enc) \ + NCE_tag(mnem, op, nops, ops, enc, OT_csuffix) + +#define NCEF(mnem, op, nops, ops, enc) \ + NCE_tag(mnem, op, nops, ops, enc, OT_csuffixF) + +/* Neon insn with conditional suffix for the ARM version, overloaded types. */ +#define nCE_tag(mnem, op, nops, ops, enc, tag) \ + { #mnem, OPS##nops ops, tag, N_MNEM_##op, N_MNEM_##op, \ + ARM_VARIANT, THUMB_VARIANT, do_##enc, do_##enc } + +#define nCE(mnem, op, nops, ops, enc) \ + nCE_tag(mnem, op, nops, ops, enc, OT_csuffix) + +#define nCEF(mnem, op, nops, ops, enc) \ + nCE_tag(mnem, op, nops, ops, enc, OT_csuffixF) + #define do_0 0 /* Thumb-only, unconditional. */ @@ -8985,8 +14832,8 @@ static const struct asm_opcode insns[] = tC3(eors, 0300000, eors, 3, (RR, oRR, SH), arit, t_arit3c), tCE(sub, 0400000, sub, 3, (RR, oRR, SH), arit, t_add_sub), tC3(subs, 0500000, subs, 3, (RR, oRR, SH), arit, t_add_sub), - tCE(add, 0800000, add, 3, (RR, oRR, SH), arit, t_add_sub), - tC3(adds, 0900000, adds, 3, (RR, oRR, SH), arit, t_add_sub), + tCE(add, 0800000, add, 3, (RR, oRR, SHG), arit, t_add_sub), + tC3(adds, 0900000, adds, 3, (RR, oRR, SHG), arit, t_add_sub), tCE(adc, 0a00000, adc, 3, (RR, oRR, SH), arit, t_arit3c), tC3(adcs, 0b00000, adcs, 3, (RR, oRR, SH), arit, t_arit3c), tCE(sbc, 0c00000, sbc, 3, (RR, oRR, SH), arit, t_arit3), @@ -9000,13 +14847,13 @@ static const struct asm_opcode insns[] = for setting PSR flag bits. They are obsolete in V6 and do not have Thumb equivalents. */ tCE(tst, 1100000, tst, 2, (RR, SH), cmp, t_mvn_tst), - tC3(tsts, 1100000, tst, 2, (RR, SH), cmp, t_mvn_tst), + tC3w(tsts, 1100000, tst, 2, (RR, SH), cmp, t_mvn_tst), CL(tstp, 110f000, 2, (RR, SH), cmp), tCE(cmp, 1500000, cmp, 2, (RR, SH), cmp, t_mov_cmp), - tC3(cmps, 1500000, cmp, 2, (RR, SH), cmp, t_mov_cmp), + tC3w(cmps, 1500000, cmp, 2, (RR, SH), cmp, t_mov_cmp), CL(cmpp, 150f000, 2, (RR, SH), cmp), tCE(cmn, 1700000, cmn, 2, (RR, SH), cmp, t_mvn_tst), - tC3(cmns, 1700000, cmn, 2, (RR, SH), cmp, t_mvn_tst), + tC3w(cmns, 1700000, cmn, 2, (RR, SH), cmp, t_mvn_tst), CL(cmnp, 170f000, 2, (RR, SH), cmp), tCE(mov, 1a00000, mov, 2, (RR, SH), mov, t_mov_cmp), @@ -9014,10 +14861,10 @@ static const struct asm_opcode insns[] = tCE(mvn, 1e00000, mvn, 2, (RR, SH), mov, t_mvn_tst), tC3(mvns, 1f00000, mvns, 2, (RR, SH), mov, t_mvn_tst), - tCE(ldr, 4100000, ldr, 2, (RR, ADDR), ldst, t_ldst), - tC3(ldrb, 4500000, ldrb, 2, (RR, ADDR), ldst, t_ldst), - tCE(str, 4000000, str, 2, (RR, ADDR), ldst, t_ldst), - tC3(strb, 4400000, strb, 2, (RR, ADDR), ldst, t_ldst), + tCE(ldr, 4100000, ldr, 2, (RR, ADDRGLDR),ldst, t_ldst), + tC3(ldrb, 4500000, ldrb, 2, (RR, ADDRGLDR),ldst, t_ldst), + tCE(str, 4000000, str, 2, (RR, ADDRGLDR),ldst, t_ldst), + tC3(strb, 4400000, strb, 2, (RR, ADDRGLDR),ldst, t_ldst), tCE(stm, 8800000, stmia, 2, (RRw, REGLST), ldmstm, t_ldmstm), tC3(stmia, 8800000, stmia, 2, (RRw, REGLST), ldmstm, t_ldmstm), @@ -9050,6 +14897,10 @@ static const struct asm_opcode insns[] = tCE(push, 92d0000, push, 1, (REGLST), push_pop, t_push_pop), tCE(pop, 8bd0000, pop, 1, (REGLST), push_pop, t_push_pop), + /* These may simplify to neg. */ + TCE(rsb, 0600000, ebc00000, 3, (RR, oRR, SH), arit, t_rsb), + TC3(rsbs, 0700000, ebd00000, 3, (RR, oRR, SH), arit, t_rsb), + #undef THUMB_VARIANT #define THUMB_VARIANT &arm_ext_v6 TCE(cpy, 1a00000, 4600, 2, (RR, RR), rd_rm, t_cpy), @@ -9057,10 +14908,8 @@ static const struct asm_opcode insns[] = /* V1 instructions with no Thumb analogue prior to V6T2. */ #undef THUMB_VARIANT #define THUMB_VARIANT &arm_ext_v6t2 - TCE(rsb, 0600000, ebc00000, 3, (RR, oRR, SH), arit, t_rsb), - TC3(rsbs, 0700000, ebd00000, 3, (RR, oRR, SH), arit, t_rsb), TCE(teq, 1300000, ea900f00, 2, (RR, SH), cmp, t_mvn_tst), - TC3(teqs, 1300000, ea900f00, 2, (RR, SH), cmp, t_mvn_tst), + TC3w(teqs, 1300000, ea900f00, 2, (RR, SH), cmp, t_mvn_tst), CL(teqp, 130f000, 2, (RR, SH), cmp), TC3(ldrt, 4300000, f8500e00, 2, (RR, ADDR), ldstt, t_ldstt), @@ -9101,10 +14950,10 @@ static const struct asm_opcode insns[] = /* Generic coprocessor instructions. */ TCE(cdp, e000000, ee000000, 6, (RCP, I15b, RCN, RCN, RCN, oI7b), cdp, cdp), - TCE(ldc, c100000, ec100000, 3, (RCP, RCN, ADDR), lstc, lstc), - TC3(ldcl, c500000, ec500000, 3, (RCP, RCN, ADDR), lstc, lstc), - TCE(stc, c000000, ec000000, 3, (RCP, RCN, ADDR), lstc, lstc), - TC3(stcl, c400000, ec400000, 3, (RCP, RCN, ADDR), lstc, lstc), + TCE(ldc, c100000, ec100000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc), + TC3(ldcl, c500000, ec500000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc), + TCE(stc, c000000, ec000000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc), + TC3(stcl, c400000, ec400000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc), TCE(mcr, e000010, ee000010, 6, (RCP, I7b, RR, RCN, RCN, oI7b), co_reg, co_reg), TCE(mrc, e100010, ee100010, 6, (RCP, I7b, RR, RCN, RCN, oI7b), co_reg, co_reg), @@ -9115,8 +14964,8 @@ static const struct asm_opcode insns[] = #undef ARM_VARIANT #define ARM_VARIANT &arm_ext_v3 /* ARM 6 Status register instructions. */ - TCE(mrs, 10f0000, f3ef8000, 2, (RR, PSR), mrs, t_mrs), - TCE(msr, 120f000, f3808000, 2, (PSR, RR_EXi), msr, t_msr), + TCE(mrs, 10f0000, f3ef8000, 2, (APSR_RR, RVC_PSR), mrs, t_mrs), + TCE(msr, 120f000, f3808000, 2, (RVC_PSR, RR_EXi), msr, t_msr), #undef ARM_VARIANT #define ARM_VARIANT &arm_ext_v3m /* ARM 7M long multiplies. */ @@ -9133,12 +14982,12 @@ static const struct asm_opcode insns[] = #define ARM_VARIANT &arm_ext_v4 /* ARM Architecture 4. */ #undef THUMB_VARIANT #define THUMB_VARIANT &arm_ext_v4t - tC3(ldrh, 01000b0, ldrh, 2, (RR, ADDR), ldstv4, t_ldst), - tC3(strh, 00000b0, strh, 2, (RR, ADDR), ldstv4, t_ldst), - tC3(ldrsh, 01000f0, ldrsh, 2, (RR, ADDR), ldstv4, t_ldst), - tC3(ldrsb, 01000d0, ldrsb, 2, (RR, ADDR), ldstv4, t_ldst), - tCM(ld,sh, 01000f0, ldrsh, 2, (RR, ADDR), ldstv4, t_ldst), - tCM(ld,sb, 01000d0, ldrsb, 2, (RR, ADDR), ldstv4, t_ldst), + tC3(ldrh, 01000b0, ldrh, 2, (RR, ADDRGLDRS), ldstv4, t_ldst), + tC3(strh, 00000b0, strh, 2, (RR, ADDRGLDRS), ldstv4, t_ldst), + tC3(ldrsh, 01000f0, ldrsh, 2, (RR, ADDRGLDRS), ldstv4, t_ldst), + tC3(ldrsb, 01000d0, ldrsb, 2, (RR, ADDRGLDRS), ldstv4, t_ldst), + tCM(ld,sh, 01000f0, ldrsh, 2, (RR, ADDRGLDRS), ldstv4, t_ldst), + tCM(ld,sb, 01000d0, ldrsb, 2, (RR, ADDRGLDRS), ldstv4, t_ldst), #undef ARM_VARIANT #define ARM_VARIANT &arm_ext_v4t_5 @@ -9159,10 +15008,10 @@ static const struct asm_opcode insns[] = #undef THUMB_VARIANT #define THUMB_VARIANT &arm_ext_v6t2 TCE(clz, 16f0f10, fab0f080, 2, (RRnpc, RRnpc), rd_rm, t_clz), - TUF(ldc2, c100000, fc100000, 3, (RCP, RCN, ADDR), lstc, lstc), - TUF(ldc2l, c500000, fc500000, 3, (RCP, RCN, ADDR), lstc, lstc), - TUF(stc2, c000000, fc000000, 3, (RCP, RCN, ADDR), lstc, lstc), - TUF(stc2l, c400000, fc400000, 3, (RCP, RCN, ADDR), lstc, lstc), + TUF(ldc2, c100000, fc100000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc), + TUF(ldc2l, c500000, fc500000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc), + TUF(stc2, c000000, fc000000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc), + TUF(stc2l, c400000, fc400000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc), TUF(cdp2, e000000, fe000000, 6, (RCP, I15b, RCN, RCN, RCN, oI7b), cdp, cdp), TUF(mcr2, e000010, fe000010, 6, (RCP, I7b, RR, RCN, RCN, oI7b), co_reg, co_reg), TUF(mrc2, e100010, fe100010, 6, (RCP, I7b, RR, RCN, RCN, oI7b), co_reg, co_reg), @@ -9198,8 +15047,8 @@ static const struct asm_opcode insns[] = #undef ARM_VARIANT #define ARM_VARIANT &arm_ext_v5e /* ARM Architecture 5TE. */ TUF(pld, 450f000, f810f000, 1, (ADDR), pld, t_pld), - TC3(ldrd, 00000d0, e9500000, 3, (RRnpc, oRRnpc, ADDR), ldrd, t_ldstd), - TC3(strd, 00000f0, e9400000, 3, (RRnpc, oRRnpc, ADDR), ldrd, t_ldstd), + TC3(ldrd, 00000d0, e8500000, 3, (RRnpc, oRRnpc, ADDRGLDRS), ldrd, t_ldstd), + TC3(strd, 00000f0, e8400000, 3, (RRnpc, oRRnpc, ADDRGLDRS), ldrd, t_ldstd), TCE(mcrr, c400000, ec400000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c), TCE(mrrc, c500000, ec500000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c), @@ -9226,6 +15075,7 @@ static const struct asm_opcode insns[] = #undef THUMB_VARIANT #define THUMB_VARIANT &arm_ext_v6t2 TCE(ldrex, 1900f9f, e8500f00, 2, (RRnpc, ADDR), ldrex, t_ldrex), + TCE(strex, 1800f90, e8400000, 3, (RRnpc, RRnpc, ADDR), strex, t_strex), TUF(mcrr2, c400000, fc400000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c), TUF(mrrc2, c500000, fc500000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c), @@ -9309,12 +15159,11 @@ static const struct asm_opcode insns[] = TCE(smuadx, 700f030, fb20f010, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd), TCE(smusd, 700f050, fb40f000, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd), TCE(smusdx, 700f070, fb40f010, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd), - TUF(srsia, 8cd0500, e980c000, 1, (I31w), srs, srs), - UF(srsib, 9cd0500, 1, (I31w), srs), - UF(srsda, 84d0500, 1, (I31w), srs), - TUF(srsdb, 94d0500, e800c000, 1, (I31w), srs, srs), + TUF(srsia, 8c00500, e980c000, 2, (oRRw, I31w), srs, srs), + UF(srsib, 9c00500, 2, (oRRw, I31w), srs), + UF(srsda, 8400500, 2, (oRRw, I31w), srs), + TUF(srsdb, 9400500, e800c000, 2, (oRRw, I31w), srs, srs), TCE(ssat16, 6a00f30, f3200000, 3, (RRnpc, I16, RRnpc), ssat16, t_ssat16), - TCE(strex, 1800f90, e8400000, 3, (RRnpc, RRnpc, ADDR), strex, t_strex), TCE(umaal, 0400090, fbe00060, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smlal, t_mlal), TCE(usad8, 780f010, fb70f000, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd), TCE(usada8, 7800010, fb700000, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla), @@ -9354,18 +15203,20 @@ static const struct asm_opcode insns[] = TCE(ubfx, 7e00050, f3c00000, 4, (RR, RR, I31, I32), bfx, t_bfx), TCE(mls, 0600090, fb000010, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mlas, t_mla), - TCE(movw, 3000000, f2400000, 2, (RRnpc, Iffff), mov16, t_mov16), - TCE(movt, 3400000, f2c00000, 2, (RRnpc, Iffff), mov16, t_mov16), - TCE(rbit, 3ff0f30, fa90f0a0, 2, (RR, RR), rd_rm, t_rbit), + TCE(movw, 3000000, f2400000, 2, (RRnpc, HALF), mov16, t_mov16), + TCE(movt, 3400000, f2c00000, 2, (RRnpc, HALF), mov16, t_mov16), + TCE(rbit, 6ff0f30, fa90f0a0, 2, (RR, RR), rd_rm, t_rbit), TC3(ldrht, 03000b0, f8300e00, 2, (RR, ADDR), ldsttv4, t_ldstt), TC3(ldrsht, 03000f0, f9300e00, 2, (RR, ADDR), ldsttv4, t_ldstt), TC3(ldrsbt, 03000d0, f9100e00, 2, (RR, ADDR), ldsttv4, t_ldstt), TC3(strht, 02000b0, f8200e00, 2, (RR, ADDR), ldsttv4, t_ldstt), - UT(cbnz, b900, 2, (RR, EXP), t_czb), - UT(cbz, b100, 2, (RR, EXP), t_czb), - /* ARM does not really have an IT instruction. */ + UT(cbnz, b900, 2, (RR, EXP), t_cbz), + UT(cbz, b100, 2, (RR, EXP), t_cbz), + /* ARM does not really have an IT instruction, so always allow it. */ +#undef ARM_VARIANT +#define ARM_VARIANT &arm_ext_v1 TUE(it, 0, bf08, 1, (COND), it, t_it), TUE(itt, 0, bf0c, 1, (COND), it, t_it), TUE(ite, 0, bf04, 1, (COND), it, t_it), @@ -9415,15 +15266,15 @@ static const struct asm_opcode insns[] = cCE(wfc, e400110, 1, (RR), rd), cCE(rfc, e500110, 1, (RR), rd), - cCL(ldfs, c100100, 2, (RF, ADDR), rd_cpaddr), - cCL(ldfd, c108100, 2, (RF, ADDR), rd_cpaddr), - cCL(ldfe, c500100, 2, (RF, ADDR), rd_cpaddr), - cCL(ldfp, c508100, 2, (RF, ADDR), rd_cpaddr), + cCL(ldfs, c100100, 2, (RF, ADDRGLDC), rd_cpaddr), + cCL(ldfd, c108100, 2, (RF, ADDRGLDC), rd_cpaddr), + cCL(ldfe, c500100, 2, (RF, ADDRGLDC), rd_cpaddr), + cCL(ldfp, c508100, 2, (RF, ADDRGLDC), rd_cpaddr), - cCL(stfs, c000100, 2, (RF, ADDR), rd_cpaddr), - cCL(stfd, c008100, 2, (RF, ADDR), rd_cpaddr), - cCL(stfe, c400100, 2, (RF, ADDR), rd_cpaddr), - cCL(stfp, c408100, 2, (RF, ADDR), rd_cpaddr), + cCL(stfs, c000100, 2, (RF, ADDRGLDC), rd_cpaddr), + cCL(stfd, c008100, 2, (RF, ADDRGLDC), rd_cpaddr), + cCL(stfe, c400100, 2, (RF, ADDRGLDC), rd_cpaddr), + cCL(stfp, c408100, 2, (RF, ADDRGLDC), rd_cpaddr), cCL(mvfs, e008100, 2, (RF, RF_IF), rd_rm), cCL(mvfsp, e008120, 2, (RF, RF_IF), rd_rm), @@ -9866,8 +15717,8 @@ static const struct asm_opcode insns[] = cCE(fmxr, ee00a10, 2, (RVC, RR), rn_rd), /* Memory operations. */ - cCE(flds, d100a00, 2, (RVS, ADDR), vfp_sp_ldst), - cCE(fsts, d000a00, 2, (RVS, ADDR), vfp_sp_ldst), + cCE(flds, d100a00, 2, (RVS, ADDRGLDC), vfp_sp_ldst), + cCE(fsts, d000a00, 2, (RVS, ADDRGLDC), vfp_sp_ldst), cCE(fldmias, c900a00, 2, (RRw, VRSLST), vfp_sp_ldstmia), cCE(fldmfds, c900a00, 2, (RRw, VRSLST), vfp_sp_ldstmia), cCE(fldmdbs, d300a00, 2, (RRw, VRSLST), vfp_sp_ldstmdb), @@ -9910,13 +15761,13 @@ static const struct asm_opcode insns[] = #undef ARM_VARIANT #define ARM_VARIANT &fpu_vfp_ext_v1 /* VFP V1 (Double precision). */ /* Moves and type conversions. */ - cCE(fcpyd, eb00b40, 2, (RVD, RVD), rd_rm), + cCE(fcpyd, eb00b40, 2, (RVD, RVD), vfp_dp_rd_rm), cCE(fcvtds, eb70ac0, 2, (RVD, RVS), vfp_dp_sp_cvt), cCE(fcvtsd, eb70bc0, 2, (RVS, RVD), vfp_sp_dp_cvt), - cCE(fmdhr, e200b10, 2, (RVD, RR), rn_rd), - cCE(fmdlr, e000b10, 2, (RVD, RR), rn_rd), - cCE(fmrdh, e300b10, 2, (RR, RVD), rd_rn), - cCE(fmrdl, e100b10, 2, (RR, RVD), rd_rn), + cCE(fmdhr, e200b10, 2, (RVD, RR), vfp_dp_rn_rd), + cCE(fmdlr, e000b10, 2, (RVD, RR), vfp_dp_rn_rd), + cCE(fmrdh, e300b10, 2, (RR, RVD), vfp_dp_rd_rn), + cCE(fmrdl, e100b10, 2, (RR, RVD), vfp_dp_rd_rn), cCE(fsitod, eb80bc0, 2, (RVD, RVS), vfp_dp_sp_cvt), cCE(fuitod, eb80b40, 2, (RVD, RVS), vfp_dp_sp_cvt), cCE(ftosid, ebd0b40, 2, (RVS, RVD), vfp_sp_dp_cvt), @@ -9925,8 +15776,8 @@ static const struct asm_opcode insns[] = cCE(ftouizd, ebc0bc0, 2, (RVS, RVD), vfp_sp_dp_cvt), /* Memory operations. */ - cCE(fldd, d100b00, 2, (RVD, ADDR), vfp_dp_ldst), - cCE(fstd, d000b00, 2, (RVD, ADDR), vfp_dp_ldst), + cCE(fldd, d100b00, 2, (RVD, ADDRGLDC), vfp_dp_ldst), + cCE(fstd, d000b00, 2, (RVD, ADDRGLDC), vfp_dp_ldst), cCE(fldmiad, c900b00, 2, (RRw, VRDLST), vfp_dp_ldstmia), cCE(fldmfdd, c900b00, 2, (RRw, VRDLST), vfp_dp_ldstmia), cCE(fldmdbd, d300b00, 2, (RRw, VRDLST), vfp_dp_ldstmdb), @@ -9937,34 +15788,347 @@ static const struct asm_opcode insns[] = cCE(fstmfdd, d200b00, 2, (RRw, VRDLST), vfp_dp_ldstmdb), /* Monadic operations. */ - cCE(fabsd, eb00bc0, 2, (RVD, RVD), rd_rm), - cCE(fnegd, eb10b40, 2, (RVD, RVD), rd_rm), - cCE(fsqrtd, eb10bc0, 2, (RVD, RVD), rd_rm), + cCE(fabsd, eb00bc0, 2, (RVD, RVD), vfp_dp_rd_rm), + cCE(fnegd, eb10b40, 2, (RVD, RVD), vfp_dp_rd_rm), + cCE(fsqrtd, eb10bc0, 2, (RVD, RVD), vfp_dp_rd_rm), /* Dyadic operations. */ - cCE(faddd, e300b00, 3, (RVD, RVD, RVD), rd_rn_rm), - cCE(fsubd, e300b40, 3, (RVD, RVD, RVD), rd_rn_rm), - cCE(fmuld, e200b00, 3, (RVD, RVD, RVD), rd_rn_rm), - cCE(fdivd, e800b00, 3, (RVD, RVD, RVD), rd_rn_rm), - cCE(fmacd, e000b00, 3, (RVD, RVD, RVD), rd_rn_rm), - cCE(fmscd, e100b00, 3, (RVD, RVD, RVD), rd_rn_rm), - cCE(fnmuld, e200b40, 3, (RVD, RVD, RVD), rd_rn_rm), - cCE(fnmacd, e000b40, 3, (RVD, RVD, RVD), rd_rn_rm), - cCE(fnmscd, e100b40, 3, (RVD, RVD, RVD), rd_rn_rm), + cCE(faddd, e300b00, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm), + cCE(fsubd, e300b40, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm), + cCE(fmuld, e200b00, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm), + cCE(fdivd, e800b00, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm), + cCE(fmacd, e000b00, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm), + cCE(fmscd, e100b00, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm), + cCE(fnmuld, e200b40, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm), + cCE(fnmacd, e000b40, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm), + cCE(fnmscd, e100b40, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm), /* Comparisons. */ - cCE(fcmpd, eb40b40, 2, (RVD, RVD), rd_rm), - cCE(fcmpzd, eb50b40, 1, (RVD), rd), - cCE(fcmped, eb40bc0, 2, (RVD, RVD), rd_rm), - cCE(fcmpezd, eb50bc0, 1, (RVD), rd), + cCE(fcmpd, eb40b40, 2, (RVD, RVD), vfp_dp_rd_rm), + cCE(fcmpzd, eb50b40, 1, (RVD), vfp_dp_rd), + cCE(fcmped, eb40bc0, 2, (RVD, RVD), vfp_dp_rd_rm), + cCE(fcmpezd, eb50bc0, 1, (RVD), vfp_dp_rd), #undef ARM_VARIANT #define ARM_VARIANT &fpu_vfp_ext_v2 cCE(fmsrr, c400a10, 3, (VRSLST, RR, RR), vfp_sp2_from_reg2), cCE(fmrrs, c500a10, 3, (RR, RR, VRSLST), vfp_reg2_from_sp2), - cCE(fmdrr, c400b10, 3, (RVD, RR, RR), rm_rd_rn), - cCE(fmrrd, c500b10, 3, (RR, RR, RVD), rd_rn_rm), + cCE(fmdrr, c400b10, 3, (RVD, RR, RR), vfp_dp_rm_rd_rn), + cCE(fmrrd, c500b10, 3, (RR, RR, RVD), vfp_dp_rd_rn_rm), + +/* Instructions which may belong to either the Neon or VFP instruction sets. + Individual encoder functions perform additional architecture checks. */ +#undef ARM_VARIANT +#define ARM_VARIANT &fpu_vfp_ext_v1xd +#undef THUMB_VARIANT +#define THUMB_VARIANT &fpu_vfp_ext_v1xd + /* These mnemonics are unique to VFP. */ + NCE(vsqrt, 0, 2, (RVSD, RVSD), vfp_nsyn_sqrt), + NCE(vdiv, 0, 3, (RVSD, RVSD, RVSD), vfp_nsyn_div), + nCE(vnmul, vnmul, 3, (RVSD, RVSD, RVSD), vfp_nsyn_nmul), + nCE(vnmla, vnmla, 3, (RVSD, RVSD, RVSD), vfp_nsyn_nmul), + nCE(vnmls, vnmls, 3, (RVSD, RVSD, RVSD), vfp_nsyn_nmul), + nCE(vcmp, vcmp, 2, (RVSD, RVSD_I0), vfp_nsyn_cmp), + nCE(vcmpe, vcmpe, 2, (RVSD, RVSD_I0), vfp_nsyn_cmp), + NCE(vpush, 0, 1, (VRSDLST), vfp_nsyn_push), + NCE(vpop, 0, 1, (VRSDLST), vfp_nsyn_pop), + NCE(vcvtz, 0, 2, (RVSD, RVSD), vfp_nsyn_cvtz), + + /* Mnemonics shared by Neon and VFP. */ + nCEF(vmul, vmul, 3, (RNSDQ, oRNSDQ, RNSDQ_RNSC), neon_mul), + nCEF(vmla, vmla, 3, (RNSDQ, oRNSDQ, RNSDQ_RNSC), neon_mac_maybe_scalar), + nCEF(vmls, vmls, 3, (RNSDQ, oRNSDQ, RNSDQ_RNSC), neon_mac_maybe_scalar), + + nCEF(vadd, vadd, 3, (RNSDQ, oRNSDQ, RNSDQ), neon_addsub_if_i), + nCEF(vsub, vsub, 3, (RNSDQ, oRNSDQ, RNSDQ), neon_addsub_if_i), + + NCEF(vabs, 1b10300, 2, (RNSDQ, RNSDQ), neon_abs_neg), + NCEF(vneg, 1b10380, 2, (RNSDQ, RNSDQ), neon_abs_neg), + + NCE(vldm, c900b00, 2, (RRw, VRSDLST), neon_ldm_stm), + NCE(vldmia, c900b00, 2, (RRw, VRSDLST), neon_ldm_stm), + NCE(vldmdb, d100b00, 2, (RRw, VRSDLST), neon_ldm_stm), + NCE(vstm, c800b00, 2, (RRw, VRSDLST), neon_ldm_stm), + NCE(vstmia, c800b00, 2, (RRw, VRSDLST), neon_ldm_stm), + NCE(vstmdb, d000b00, 2, (RRw, VRSDLST), neon_ldm_stm), + NCE(vldr, d100b00, 2, (RVSD, ADDRGLDC), neon_ldr_str), + NCE(vstr, d000b00, 2, (RVSD, ADDRGLDC), neon_ldr_str), + + nCEF(vcvt, vcvt, 3, (RNSDQ, RNSDQ, oI32b), neon_cvt), + + /* NOTE: All VMOV encoding is special-cased! */ + NCE(vmov, 0, 1, (VMOV), neon_mov), + NCE(vmovq, 0, 1, (VMOV), neon_mov), +#undef THUMB_VARIANT +#define THUMB_VARIANT &fpu_neon_ext_v1 +#undef ARM_VARIANT +#define ARM_VARIANT &fpu_neon_ext_v1 + /* Data processing with three registers of the same length. */ + /* integer ops, valid types S8 S16 S32 U8 U16 U32. */ + NUF(vaba, 0000710, 3, (RNDQ, RNDQ, RNDQ), neon_dyadic_i_su), + NUF(vabaq, 0000710, 3, (RNQ, RNQ, RNQ), neon_dyadic_i_su), + NUF(vhadd, 0000000, 3, (RNDQ, oRNDQ, RNDQ), neon_dyadic_i_su), + NUF(vhaddq, 0000000, 3, (RNQ, oRNQ, RNQ), neon_dyadic_i_su), + NUF(vrhadd, 0000100, 3, (RNDQ, oRNDQ, RNDQ), neon_dyadic_i_su), + NUF(vrhaddq, 0000100, 3, (RNQ, oRNQ, RNQ), neon_dyadic_i_su), + NUF(vhsub, 0000200, 3, (RNDQ, oRNDQ, RNDQ), neon_dyadic_i_su), + NUF(vhsubq, 0000200, 3, (RNQ, oRNQ, RNQ), neon_dyadic_i_su), + /* integer ops, valid types S8 S16 S32 S64 U8 U16 U32 U64. */ + NUF(vqadd, 0000010, 3, (RNDQ, oRNDQ, RNDQ), neon_dyadic_i64_su), + NUF(vqaddq, 0000010, 3, (RNQ, oRNQ, RNQ), neon_dyadic_i64_su), + NUF(vqsub, 0000210, 3, (RNDQ, oRNDQ, RNDQ), neon_dyadic_i64_su), + NUF(vqsubq, 0000210, 3, (RNQ, oRNQ, RNQ), neon_dyadic_i64_su), + NUF(vrshl, 0000500, 3, (RNDQ, oRNDQ, RNDQ), neon_rshl), + NUF(vrshlq, 0000500, 3, (RNQ, oRNQ, RNQ), neon_rshl), + NUF(vqrshl, 0000510, 3, (RNDQ, oRNDQ, RNDQ), neon_rshl), + NUF(vqrshlq, 0000510, 3, (RNQ, oRNQ, RNQ), neon_rshl), + /* If not immediate, fall back to neon_dyadic_i64_su. + shl_imm should accept I8 I16 I32 I64, + qshl_imm should accept S8 S16 S32 S64 U8 U16 U32 U64. */ + nUF(vshl, vshl, 3, (RNDQ, oRNDQ, RNDQ_I63b), neon_shl_imm), + nUF(vshlq, vshl, 3, (RNQ, oRNQ, RNDQ_I63b), neon_shl_imm), + nUF(vqshl, vqshl, 3, (RNDQ, oRNDQ, RNDQ_I63b), neon_qshl_imm), + nUF(vqshlq, vqshl, 3, (RNQ, oRNQ, RNDQ_I63b), neon_qshl_imm), + /* Logic ops, types optional & ignored. */ + nUF(vand, vand, 2, (RNDQ, NILO), neon_logic), + nUF(vandq, vand, 2, (RNQ, NILO), neon_logic), + nUF(vbic, vbic, 2, (RNDQ, NILO), neon_logic), + nUF(vbicq, vbic, 2, (RNQ, NILO), neon_logic), + nUF(vorr, vorr, 2, (RNDQ, NILO), neon_logic), + nUF(vorrq, vorr, 2, (RNQ, NILO), neon_logic), + nUF(vorn, vorn, 2, (RNDQ, NILO), neon_logic), + nUF(vornq, vorn, 2, (RNQ, NILO), neon_logic), + nUF(veor, veor, 3, (RNDQ, oRNDQ, RNDQ), neon_logic), + nUF(veorq, veor, 3, (RNQ, oRNQ, RNQ), neon_logic), + /* Bitfield ops, untyped. */ + NUF(vbsl, 1100110, 3, (RNDQ, RNDQ, RNDQ), neon_bitfield), + NUF(vbslq, 1100110, 3, (RNQ, RNQ, RNQ), neon_bitfield), + NUF(vbit, 1200110, 3, (RNDQ, RNDQ, RNDQ), neon_bitfield), + NUF(vbitq, 1200110, 3, (RNQ, RNQ, RNQ), neon_bitfield), + NUF(vbif, 1300110, 3, (RNDQ, RNDQ, RNDQ), neon_bitfield), + NUF(vbifq, 1300110, 3, (RNQ, RNQ, RNQ), neon_bitfield), + /* Int and float variants, types S8 S16 S32 U8 U16 U32 F32. */ + nUF(vabd, vabd, 3, (RNDQ, oRNDQ, RNDQ), neon_dyadic_if_su), + nUF(vabdq, vabd, 3, (RNQ, oRNQ, RNQ), neon_dyadic_if_su), + nUF(vmax, vmax, 3, (RNDQ, oRNDQ, RNDQ), neon_dyadic_if_su), + nUF(vmaxq, vmax, 3, (RNQ, oRNQ, RNQ), neon_dyadic_if_su), + nUF(vmin, vmin, 3, (RNDQ, oRNDQ, RNDQ), neon_dyadic_if_su), + nUF(vminq, vmin, 3, (RNQ, oRNQ, RNQ), neon_dyadic_if_su), + /* Comparisons. Types S8 S16 S32 U8 U16 U32 F32. Non-immediate versions fall + back to neon_dyadic_if_su. */ + nUF(vcge, vcge, 3, (RNDQ, oRNDQ, RNDQ_I0), neon_cmp), + nUF(vcgeq, vcge, 3, (RNQ, oRNQ, RNDQ_I0), neon_cmp), + nUF(vcgt, vcgt, 3, (RNDQ, oRNDQ, RNDQ_I0), neon_cmp), + nUF(vcgtq, vcgt, 3, (RNQ, oRNQ, RNDQ_I0), neon_cmp), + nUF(vclt, vclt, 3, (RNDQ, oRNDQ, RNDQ_I0), neon_cmp_inv), + nUF(vcltq, vclt, 3, (RNQ, oRNQ, RNDQ_I0), neon_cmp_inv), + nUF(vcle, vcle, 3, (RNDQ, oRNDQ, RNDQ_I0), neon_cmp_inv), + nUF(vcleq, vcle, 3, (RNQ, oRNQ, RNDQ_I0), neon_cmp_inv), + /* Comparison. Type I8 I16 I32 F32. */ + nUF(vceq, vceq, 3, (RNDQ, oRNDQ, RNDQ_I0), neon_ceq), + nUF(vceqq, vceq, 3, (RNQ, oRNQ, RNDQ_I0), neon_ceq), + /* As above, D registers only. */ + nUF(vpmax, vpmax, 3, (RND, oRND, RND), neon_dyadic_if_su_d), + nUF(vpmin, vpmin, 3, (RND, oRND, RND), neon_dyadic_if_su_d), + /* Int and float variants, signedness unimportant. */ + nUF(vmlaq, vmla, 3, (RNQ, oRNQ, RNDQ_RNSC), neon_mac_maybe_scalar), + nUF(vmlsq, vmls, 3, (RNQ, oRNQ, RNDQ_RNSC), neon_mac_maybe_scalar), + nUF(vpadd, vpadd, 3, (RND, oRND, RND), neon_dyadic_if_i_d), + /* Add/sub take types I8 I16 I32 I64 F32. */ + nUF(vaddq, vadd, 3, (RNQ, oRNQ, RNQ), neon_addsub_if_i), + nUF(vsubq, vsub, 3, (RNQ, oRNQ, RNQ), neon_addsub_if_i), + /* vtst takes sizes 8, 16, 32. */ + NUF(vtst, 0000810, 3, (RNDQ, oRNDQ, RNDQ), neon_tst), + NUF(vtstq, 0000810, 3, (RNQ, oRNQ, RNQ), neon_tst), + /* VMUL takes I8 I16 I32 F32 P8. */ + nUF(vmulq, vmul, 3, (RNQ, oRNQ, RNDQ_RNSC), neon_mul), + /* VQD{R}MULH takes S16 S32. */ + nUF(vqdmulh, vqdmulh, 3, (RNDQ, oRNDQ, RNDQ_RNSC), neon_qdmulh), + nUF(vqdmulhq, vqdmulh, 3, (RNQ, oRNQ, RNDQ_RNSC), neon_qdmulh), + nUF(vqrdmulh, vqrdmulh, 3, (RNDQ, oRNDQ, RNDQ_RNSC), neon_qdmulh), + nUF(vqrdmulhq, vqrdmulh, 3, (RNQ, oRNQ, RNDQ_RNSC), neon_qdmulh), + NUF(vacge, 0000e10, 3, (RNDQ, oRNDQ, RNDQ), neon_fcmp_absolute), + NUF(vacgeq, 0000e10, 3, (RNQ, oRNQ, RNQ), neon_fcmp_absolute), + NUF(vacgt, 0200e10, 3, (RNDQ, oRNDQ, RNDQ), neon_fcmp_absolute), + NUF(vacgtq, 0200e10, 3, (RNQ, oRNQ, RNQ), neon_fcmp_absolute), + NUF(vaclt, 0200e10, 3, (RNDQ, oRNDQ, RNDQ), neon_fcmp_absolute_inv), + NUF(vacltq, 0200e10, 3, (RNQ, oRNQ, RNQ), neon_fcmp_absolute_inv), + NUF(vacle, 0000e10, 3, (RNDQ, oRNDQ, RNDQ), neon_fcmp_absolute_inv), + NUF(vacleq, 0000e10, 3, (RNQ, oRNQ, RNQ), neon_fcmp_absolute_inv), + NUF(vrecps, 0000f10, 3, (RNDQ, oRNDQ, RNDQ), neon_step), + NUF(vrecpsq, 0000f10, 3, (RNQ, oRNQ, RNQ), neon_step), + NUF(vrsqrts, 0200f10, 3, (RNDQ, oRNDQ, RNDQ), neon_step), + NUF(vrsqrtsq, 0200f10, 3, (RNQ, oRNQ, RNQ), neon_step), + + /* Two address, int/float. Types S8 S16 S32 F32. */ + NUF(vabsq, 1b10300, 2, (RNQ, RNQ), neon_abs_neg), + NUF(vnegq, 1b10380, 2, (RNQ, RNQ), neon_abs_neg), + + /* Data processing with two registers and a shift amount. */ + /* Right shifts, and variants with rounding. + Types accepted S8 S16 S32 S64 U8 U16 U32 U64. */ + NUF(vshr, 0800010, 3, (RNDQ, oRNDQ, I64z), neon_rshift_round_imm), + NUF(vshrq, 0800010, 3, (RNQ, oRNQ, I64z), neon_rshift_round_imm), + NUF(vrshr, 0800210, 3, (RNDQ, oRNDQ, I64z), neon_rshift_round_imm), + NUF(vrshrq, 0800210, 3, (RNQ, oRNQ, I64z), neon_rshift_round_imm), + NUF(vsra, 0800110, 3, (RNDQ, oRNDQ, I64), neon_rshift_round_imm), + NUF(vsraq, 0800110, 3, (RNQ, oRNQ, I64), neon_rshift_round_imm), + NUF(vrsra, 0800310, 3, (RNDQ, oRNDQ, I64), neon_rshift_round_imm), + NUF(vrsraq, 0800310, 3, (RNQ, oRNQ, I64), neon_rshift_round_imm), + /* Shift and insert. Sizes accepted 8 16 32 64. */ + NUF(vsli, 1800510, 3, (RNDQ, oRNDQ, I63), neon_sli), + NUF(vsliq, 1800510, 3, (RNQ, oRNQ, I63), neon_sli), + NUF(vsri, 1800410, 3, (RNDQ, oRNDQ, I64), neon_sri), + NUF(vsriq, 1800410, 3, (RNQ, oRNQ, I64), neon_sri), + /* QSHL{U} immediate accepts S8 S16 S32 S64 U8 U16 U32 U64. */ + NUF(vqshlu, 1800610, 3, (RNDQ, oRNDQ, I63), neon_qshlu_imm), + NUF(vqshluq, 1800610, 3, (RNQ, oRNQ, I63), neon_qshlu_imm), + /* Right shift immediate, saturating & narrowing, with rounding variants. + Types accepted S16 S32 S64 U16 U32 U64. */ + NUF(vqshrn, 0800910, 3, (RND, RNQ, I32z), neon_rshift_sat_narrow), + NUF(vqrshrn, 0800950, 3, (RND, RNQ, I32z), neon_rshift_sat_narrow), + /* As above, unsigned. Types accepted S16 S32 S64. */ + NUF(vqshrun, 0800810, 3, (RND, RNQ, I32z), neon_rshift_sat_narrow_u), + NUF(vqrshrun, 0800850, 3, (RND, RNQ, I32z), neon_rshift_sat_narrow_u), + /* Right shift narrowing. Types accepted I16 I32 I64. */ + NUF(vshrn, 0800810, 3, (RND, RNQ, I32z), neon_rshift_narrow), + NUF(vrshrn, 0800850, 3, (RND, RNQ, I32z), neon_rshift_narrow), + /* Special case. Types S8 S16 S32 U8 U16 U32. Handles max shift variant. */ + nUF(vshll, vshll, 3, (RNQ, RND, I32), neon_shll), + /* CVT with optional immediate for fixed-point variant. */ + nUF(vcvtq, vcvt, 3, (RNQ, RNQ, oI32b), neon_cvt), + + nUF(vmvn, vmvn, 2, (RNDQ, RNDQ_IMVNb), neon_mvn), + nUF(vmvnq, vmvn, 2, (RNQ, RNDQ_IMVNb), neon_mvn), + + /* Data processing, three registers of different lengths. */ + /* Dyadic, long insns. Types S8 S16 S32 U8 U16 U32. */ + NUF(vabal, 0800500, 3, (RNQ, RND, RND), neon_abal), + NUF(vabdl, 0800700, 3, (RNQ, RND, RND), neon_dyadic_long), + NUF(vaddl, 0800000, 3, (RNQ, RND, RND), neon_dyadic_long), + NUF(vsubl, 0800200, 3, (RNQ, RND, RND), neon_dyadic_long), + /* If not scalar, fall back to neon_dyadic_long. + Vector types as above, scalar types S16 S32 U16 U32. */ + nUF(vmlal, vmlal, 3, (RNQ, RND, RND_RNSC), neon_mac_maybe_scalar_long), + nUF(vmlsl, vmlsl, 3, (RNQ, RND, RND_RNSC), neon_mac_maybe_scalar_long), + /* Dyadic, widening insns. Types S8 S16 S32 U8 U16 U32. */ + NUF(vaddw, 0800100, 3, (RNQ, oRNQ, RND), neon_dyadic_wide), + NUF(vsubw, 0800300, 3, (RNQ, oRNQ, RND), neon_dyadic_wide), + /* Dyadic, narrowing insns. Types I16 I32 I64. */ + NUF(vaddhn, 0800400, 3, (RND, RNQ, RNQ), neon_dyadic_narrow), + NUF(vraddhn, 1800400, 3, (RND, RNQ, RNQ), neon_dyadic_narrow), + NUF(vsubhn, 0800600, 3, (RND, RNQ, RNQ), neon_dyadic_narrow), + NUF(vrsubhn, 1800600, 3, (RND, RNQ, RNQ), neon_dyadic_narrow), + /* Saturating doubling multiplies. Types S16 S32. */ + nUF(vqdmlal, vqdmlal, 3, (RNQ, RND, RND_RNSC), neon_mul_sat_scalar_long), + nUF(vqdmlsl, vqdmlsl, 3, (RNQ, RND, RND_RNSC), neon_mul_sat_scalar_long), + nUF(vqdmull, vqdmull, 3, (RNQ, RND, RND_RNSC), neon_mul_sat_scalar_long), + /* VMULL. Vector types S8 S16 S32 U8 U16 U32 P8, scalar types + S16 S32 U16 U32. */ + nUF(vmull, vmull, 3, (RNQ, RND, RND_RNSC), neon_vmull), + + /* Extract. Size 8. */ + NUF(vext, 0b00000, 4, (RNDQ, oRNDQ, RNDQ, I15), neon_ext), + NUF(vextq, 0b00000, 4, (RNQ, oRNQ, RNQ, I15), neon_ext), + + /* Two registers, miscellaneous. */ + /* Reverse. Sizes 8 16 32 (must be < size in opcode). */ + NUF(vrev64, 1b00000, 2, (RNDQ, RNDQ), neon_rev), + NUF(vrev64q, 1b00000, 2, (RNQ, RNQ), neon_rev), + NUF(vrev32, 1b00080, 2, (RNDQ, RNDQ), neon_rev), + NUF(vrev32q, 1b00080, 2, (RNQ, RNQ), neon_rev), + NUF(vrev16, 1b00100, 2, (RNDQ, RNDQ), neon_rev), + NUF(vrev16q, 1b00100, 2, (RNQ, RNQ), neon_rev), + /* Vector replicate. Sizes 8 16 32. */ + nCE(vdup, vdup, 2, (RNDQ, RR_RNSC), neon_dup), + nCE(vdupq, vdup, 2, (RNQ, RR_RNSC), neon_dup), + /* VMOVL. Types S8 S16 S32 U8 U16 U32. */ + NUF(vmovl, 0800a10, 2, (RNQ, RND), neon_movl), + /* VMOVN. Types I16 I32 I64. */ + nUF(vmovn, vmovn, 2, (RND, RNQ), neon_movn), + /* VQMOVN. Types S16 S32 S64 U16 U32 U64. */ + nUF(vqmovn, vqmovn, 2, (RND, RNQ), neon_qmovn), + /* VQMOVUN. Types S16 S32 S64. */ + nUF(vqmovun, vqmovun, 2, (RND, RNQ), neon_qmovun), + /* VZIP / VUZP. Sizes 8 16 32. */ + NUF(vzip, 1b20180, 2, (RNDQ, RNDQ), neon_zip_uzp), + NUF(vzipq, 1b20180, 2, (RNQ, RNQ), neon_zip_uzp), + NUF(vuzp, 1b20100, 2, (RNDQ, RNDQ), neon_zip_uzp), + NUF(vuzpq, 1b20100, 2, (RNQ, RNQ), neon_zip_uzp), + /* VQABS / VQNEG. Types S8 S16 S32. */ + NUF(vqabs, 1b00700, 2, (RNDQ, RNDQ), neon_sat_abs_neg), + NUF(vqabsq, 1b00700, 2, (RNQ, RNQ), neon_sat_abs_neg), + NUF(vqneg, 1b00780, 2, (RNDQ, RNDQ), neon_sat_abs_neg), + NUF(vqnegq, 1b00780, 2, (RNQ, RNQ), neon_sat_abs_neg), + /* Pairwise, lengthening. Types S8 S16 S32 U8 U16 U32. */ + NUF(vpadal, 1b00600, 2, (RNDQ, RNDQ), neon_pair_long), + NUF(vpadalq, 1b00600, 2, (RNQ, RNQ), neon_pair_long), + NUF(vpaddl, 1b00200, 2, (RNDQ, RNDQ), neon_pair_long), + NUF(vpaddlq, 1b00200, 2, (RNQ, RNQ), neon_pair_long), + /* Reciprocal estimates. Types U32 F32. */ + NUF(vrecpe, 1b30400, 2, (RNDQ, RNDQ), neon_recip_est), + NUF(vrecpeq, 1b30400, 2, (RNQ, RNQ), neon_recip_est), + NUF(vrsqrte, 1b30480, 2, (RNDQ, RNDQ), neon_recip_est), + NUF(vrsqrteq, 1b30480, 2, (RNQ, RNQ), neon_recip_est), + /* VCLS. Types S8 S16 S32. */ + NUF(vcls, 1b00400, 2, (RNDQ, RNDQ), neon_cls), + NUF(vclsq, 1b00400, 2, (RNQ, RNQ), neon_cls), + /* VCLZ. Types I8 I16 I32. */ + NUF(vclz, 1b00480, 2, (RNDQ, RNDQ), neon_clz), + NUF(vclzq, 1b00480, 2, (RNQ, RNQ), neon_clz), + /* VCNT. Size 8. */ + NUF(vcnt, 1b00500, 2, (RNDQ, RNDQ), neon_cnt), + NUF(vcntq, 1b00500, 2, (RNQ, RNQ), neon_cnt), + /* Two address, untyped. */ + NUF(vswp, 1b20000, 2, (RNDQ, RNDQ), neon_swp), + NUF(vswpq, 1b20000, 2, (RNQ, RNQ), neon_swp), + /* VTRN. Sizes 8 16 32. */ + nUF(vtrn, vtrn, 2, (RNDQ, RNDQ), neon_trn), + nUF(vtrnq, vtrn, 2, (RNQ, RNQ), neon_trn), + + /* Table lookup. Size 8. */ + NUF(vtbl, 1b00800, 3, (RND, NRDLST, RND), neon_tbl_tbx), + NUF(vtbx, 1b00840, 3, (RND, NRDLST, RND), neon_tbl_tbx), + +#undef THUMB_VARIANT +#define THUMB_VARIANT &fpu_vfp_v3_or_neon_ext +#undef ARM_VARIANT +#define ARM_VARIANT &fpu_vfp_v3_or_neon_ext + /* Neon element/structure load/store. */ + nUF(vld1, vld1, 2, (NSTRLST, ADDR), neon_ldx_stx), + nUF(vst1, vst1, 2, (NSTRLST, ADDR), neon_ldx_stx), + nUF(vld2, vld2, 2, (NSTRLST, ADDR), neon_ldx_stx), + nUF(vst2, vst2, 2, (NSTRLST, ADDR), neon_ldx_stx), + nUF(vld3, vld3, 2, (NSTRLST, ADDR), neon_ldx_stx), + nUF(vst3, vst3, 2, (NSTRLST, ADDR), neon_ldx_stx), + nUF(vld4, vld4, 2, (NSTRLST, ADDR), neon_ldx_stx), + nUF(vst4, vst4, 2, (NSTRLST, ADDR), neon_ldx_stx), + +#undef THUMB_VARIANT +#define THUMB_VARIANT &fpu_vfp_ext_v3 +#undef ARM_VARIANT +#define ARM_VARIANT &fpu_vfp_ext_v3 + cCE(fconsts, eb00a00, 2, (RVS, I255), vfp_sp_const), + cCE(fconstd, eb00b00, 2, (RVD, I255), vfp_dp_const), + cCE(fshtos, eba0a40, 2, (RVS, I16z), vfp_sp_conv_16), + cCE(fshtod, eba0b40, 2, (RVD, I16z), vfp_dp_conv_16), + cCE(fsltos, eba0ac0, 2, (RVS, I32), vfp_sp_conv_32), + cCE(fsltod, eba0bc0, 2, (RVD, I32), vfp_dp_conv_32), + cCE(fuhtos, ebb0a40, 2, (RVS, I16z), vfp_sp_conv_16), + cCE(fuhtod, ebb0b40, 2, (RVD, I16z), vfp_dp_conv_16), + cCE(fultos, ebb0ac0, 2, (RVS, I32), vfp_sp_conv_32), + cCE(fultod, ebb0bc0, 2, (RVD, I32), vfp_dp_conv_32), + cCE(ftoshs, ebe0a40, 2, (RVS, I16z), vfp_sp_conv_16), + cCE(ftoshd, ebe0b40, 2, (RVD, I16z), vfp_dp_conv_16), + cCE(ftosls, ebe0ac0, 2, (RVS, I32), vfp_sp_conv_32), + cCE(ftosld, ebe0bc0, 2, (RVD, I32), vfp_dp_conv_32), + cCE(ftouhs, ebf0a40, 2, (RVS, I16z), vfp_sp_conv_16), + cCE(ftouhd, ebf0b40, 2, (RVD, I16z), vfp_dp_conv_16), + cCE(ftouls, ebf0ac0, 2, (RVS, I32), vfp_sp_conv_32), + cCE(ftould, ebf0bc0, 2, (RVD, I32), vfp_dp_conv_32), + +#undef THUMB_VARIANT #undef ARM_VARIANT #define ARM_VARIANT &arm_cext_xscale /* Intel XScale extensions. */ cCE(mia, e200010, 3, (RXA, RRnpc, RRnpc), xsc_mia), @@ -9996,7 +16160,7 @@ static const struct asm_opcode insns[] = cCE(tinsrb, e600010, 3, (RIWR, RR, I7), iwmmxt_tinsr), cCE(tinsrh, e600050, 3, (RIWR, RR, I7), iwmmxt_tinsr), cCE(tinsrw, e600090, 3, (RIWR, RR, I7), iwmmxt_tinsr), - cCE(tmcr, e000110, 2, (RIWC, RR), rn_rd), + cCE(tmcr, e000110, 2, (RIWC_RIWG, RR), rn_rd), cCE(tmcrr, c400000, 3, (RIWR, RR, RR), rm_rd_rn), cCE(tmia, e200010, 3, (RIWR, RR, RR), iwmmxt_tmia), cCE(tmiaph, e280010, 3, (RIWR, RR, RR), iwmmxt_tmia), @@ -10007,7 +16171,7 @@ static const struct asm_opcode insns[] = cCE(tmovmskb, e100030, 2, (RR, RIWR), rd_rn), cCE(tmovmskh, e500030, 2, (RR, RIWR), rd_rn), cCE(tmovmskw, e900030, 2, (RR, RIWR), rd_rn), - cCE(tmrc, e100110, 2, (RR, RIWC), rd_rn), + cCE(tmrc, e100110, 2, (RR, RIWC_RIWG), rd_rn), cCE(tmrrc, c500000, 3, (RR, RR, RIWR), rd_rn_rm), cCE(torcb, e13f150, 1, (RR), iwmmxt_tandorc), cCE(torch, e53f150, 1, (RR), iwmmxt_tandorc), @@ -10078,34 +16242,34 @@ static const struct asm_opcode insns[] = cCE(wpackwus, e900080, 3, (RIWR, RIWR, RIWR), rd_rn_rm), cCE(wpackdss, ef00080, 3, (RIWR, RIWR, RIWR), rd_rn_rm), cCE(wpackdus, ed00080, 3, (RIWR, RIWR, RIWR), rd_rn_rm), - cCE(wrorh, e700040, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wrorh, e700040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5), cCE(wrorhg, e700148, 3, (RIWR, RIWR, RIWG), rd_rn_rm), - cCE(wrorw, eb00040, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wrorw, eb00040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5), cCE(wrorwg, eb00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm), - cCE(wrord, ef00040, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wrord, ef00040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5), cCE(wrordg, ef00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm), cCE(wsadb, e000120, 3, (RIWR, RIWR, RIWR), rd_rn_rm), cCE(wsadbz, e100120, 3, (RIWR, RIWR, RIWR), rd_rn_rm), cCE(wsadh, e400120, 3, (RIWR, RIWR, RIWR), rd_rn_rm), cCE(wsadhz, e500120, 3, (RIWR, RIWR, RIWR), rd_rn_rm), cCE(wshufh, e0001e0, 3, (RIWR, RIWR, I255), iwmmxt_wshufh), - cCE(wsllh, e500040, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wsllh, e500040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5), cCE(wsllhg, e500148, 3, (RIWR, RIWR, RIWG), rd_rn_rm), - cCE(wsllw, e900040, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wsllw, e900040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5), cCE(wsllwg, e900148, 3, (RIWR, RIWR, RIWG), rd_rn_rm), - cCE(wslld, ed00040, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wslld, ed00040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5), cCE(wslldg, ed00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm), - cCE(wsrah, e400040, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wsrah, e400040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5), cCE(wsrahg, e400148, 3, (RIWR, RIWR, RIWG), rd_rn_rm), - cCE(wsraw, e800040, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wsraw, e800040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5), cCE(wsrawg, e800148, 3, (RIWR, RIWR, RIWG), rd_rn_rm), - cCE(wsrad, ec00040, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wsrad, ec00040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5), cCE(wsradg, ec00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm), - cCE(wsrlh, e600040, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wsrlh, e600040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5), cCE(wsrlhg, e600148, 3, (RIWR, RIWR, RIWG), rd_rn_rm), - cCE(wsrlw, ea00040, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wsrlw, ea00040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5), cCE(wsrlwg, ea00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm), - cCE(wsrld, ee00040, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wsrld, ee00040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5), cCE(wsrldg, ee00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm), cCE(wstrb, c000000, 2, (RIWR, ADDR), iwmmxt_wldstbh), cCE(wstrh, c400000, 2, (RIWR, ADDR), iwmmxt_wldstbh), @@ -10142,15 +16306,75 @@ static const struct asm_opcode insns[] = cCE(wzero, e300000, 1, (RIWR), iwmmxt_wzero), #undef ARM_VARIANT +#define ARM_VARIANT &arm_cext_iwmmxt2 /* Intel Wireless MMX technology, version 2. */ + cCE(torvscb, e13f190, 1, (RR), iwmmxt_tandorc), + cCE(torvsch, e53f190, 1, (RR), iwmmxt_tandorc), + cCE(torvscw, e93f190, 1, (RR), iwmmxt_tandorc), + cCE(wabsb, e2001c0, 2, (RIWR, RIWR), rd_rn), + cCE(wabsh, e6001c0, 2, (RIWR, RIWR), rd_rn), + cCE(wabsw, ea001c0, 2, (RIWR, RIWR), rd_rn), + cCE(wabsdiffb, e1001c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wabsdiffh, e5001c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wabsdiffw, e9001c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(waddbhusl, e2001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(waddbhusm, e6001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(waddhc, e600180, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(waddwc, ea00180, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(waddsubhx, ea001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wavg4, e400000, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wavg4r, e500000, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmaddsn, ee00100, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmaddsx, eb00100, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmaddun, ec00100, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmaddux, e900100, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmerge, e000080, 4, (RIWR, RIWR, RIWR, I7), iwmmxt_wmerge), + cCE(wmiabb, e0000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmiabt, e1000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmiatb, e2000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmiatt, e3000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmiabbn, e4000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmiabtn, e5000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmiatbn, e6000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmiattn, e7000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmiawbb, e800120, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmiawbt, e900120, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmiawtb, ea00120, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmiawtt, eb00120, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmiawbbn, ec00120, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmiawbtn, ed00120, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmiawtbn, ee00120, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmiawttn, ef00120, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmulsmr, ef00100, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmulumr, ed00100, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmulwumr, ec000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmulwsmr, ee000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmulwum, ed000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmulwsm, ef000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wmulwl, eb000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wqmiabb, e8000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wqmiabt, e9000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wqmiatb, ea000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wqmiatt, eb000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wqmiabbn, ec000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wqmiabtn, ed000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wqmiatbn, ee000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wqmiattn, ef000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wqmulm, e100080, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wqmulmr, e300080, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wqmulwm, ec000e0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wqmulwmr, ee000e0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + cCE(wsubaddhx, ed001c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm), + +#undef ARM_VARIANT #define ARM_VARIANT &arm_cext_maverick /* Cirrus Maverick instructions. */ - cCE(cfldrs, c100400, 2, (RMF, ADDR), rd_cpaddr), - cCE(cfldrd, c500400, 2, (RMD, ADDR), rd_cpaddr), - cCE(cfldr32, c100500, 2, (RMFX, ADDR), rd_cpaddr), - cCE(cfldr64, c500500, 2, (RMDX, ADDR), rd_cpaddr), - cCE(cfstrs, c000400, 2, (RMF, ADDR), rd_cpaddr), - cCE(cfstrd, c400400, 2, (RMD, ADDR), rd_cpaddr), - cCE(cfstr32, c000500, 2, (RMFX, ADDR), rd_cpaddr), - cCE(cfstr64, c400500, 2, (RMDX, ADDR), rd_cpaddr), + cCE(cfldrs, c100400, 2, (RMF, ADDRGLDC), rd_cpaddr), + cCE(cfldrd, c500400, 2, (RMD, ADDRGLDC), rd_cpaddr), + cCE(cfldr32, c100500, 2, (RMFX, ADDRGLDC), rd_cpaddr), + cCE(cfldr64, c500500, 2, (RMDX, ADDRGLDC), rd_cpaddr), + cCE(cfstrs, c000400, 2, (RMF, ADDRGLDC), rd_cpaddr), + cCE(cfstrd, c400400, 2, (RMD, ADDRGLDC), rd_cpaddr), + cCE(cfstr32, c000500, 2, (RMFX, ADDRGLDC), rd_cpaddr), + cCE(cfstr64, c400500, 2, (RMDX, ADDRGLDC), rd_cpaddr), cCE(cfmvsr, e000450, 2, (RMF, RR), rn_rd), cCE(cfmvrs, e100450, 2, (RR, RMF), rd_rn), cCE(cfmvdlr, e000410, 2, (RMD, RR), rn_rd), @@ -10235,6 +16459,10 @@ static const struct asm_opcode insns[] = #undef UE #undef UF #undef UT +#undef NUF +#undef nUF +#undef NCE +#undef nCE #undef OPS0 #undef OPS1 #undef OPS2 @@ -10428,7 +16656,10 @@ md_convert_frag (bfd *abfd, segT asec ATTRIBUTE_UNUSED, fragS *fragp) insn = THUMB_OP32 (opcode); insn |= (old_op & 0xf0) << 4; put_thumb32_insn (buf, insn); - reloc_type = BFD_RELOC_ARM_T32_IMMEDIATE; + if (opcode == T_MNEM_add_pc) + reloc_type = BFD_RELOC_ARM_T32_IMM12; + else + reloc_type = BFD_RELOC_ARM_T32_ADD_IMM; } else reloc_type = BFD_RELOC_ARM_THUMB_ADD; @@ -10445,7 +16676,10 @@ md_convert_frag (bfd *abfd, segT asec ATTRIBUTE_UNUSED, fragS *fragp) insn |= (old_op & 0xf0) << 4; insn |= (old_op & 0xf) << 16; put_thumb32_insn (buf, insn); - reloc_type = BFD_RELOC_ARM_T32_IMMEDIATE; + if (insn & (1 << 20)) + reloc_type = BFD_RELOC_ARM_T32_ADD_IMM; + else + reloc_type = BFD_RELOC_ARM_T32_IMMEDIATE; } else reloc_type = BFD_RELOC_ARM_THUMB_ADD; @@ -10479,16 +16713,42 @@ relax_immediate (fragS *fragp, int size, int shift) offset = fragp->fr_offset; /* Force misaligned offsets to 32-bit variant. */ if (offset & low) - return -4; + return 4; if (offset & ~mask) return 4; return 2; } +/* Get the address of a symbol during relaxation. */ +static addressT +relaxed_symbol_addr(fragS *fragp, long stretch) +{ + fragS *sym_frag; + addressT addr; + symbolS *sym; + + sym = fragp->fr_symbol; + sym_frag = symbol_get_frag (sym); + know (S_GET_SEGMENT (sym) != absolute_section + || sym_frag == &zero_address_frag); + addr = S_GET_VALUE (sym) + fragp->fr_offset; + + /* If frag has yet to be reached on this pass, assume it will + move by STRETCH just as we did. If this is not so, it will + be because some frag between grows, and that will force + another pass. */ + + if (stretch != 0 + && sym_frag->relax_marker != fragp->relax_marker) + addr += stretch; + + return addr; +} + /* Return the size of a relaxable adr pseudo-instruction or PC-relative load. */ static int -relax_adr (fragS *fragp, asection *sec) +relax_adr (fragS *fragp, asection *sec, long stretch) { addressT addr; offsetT val; @@ -10498,14 +16758,12 @@ relax_adr (fragS *fragp, asection *sec) || sec != S_GET_SEGMENT (fragp->fr_symbol)) return 4; - val = S_GET_VALUE(fragp->fr_symbol) + fragp->fr_offset; + val = relaxed_symbol_addr(fragp, stretch); addr = fragp->fr_address + fragp->fr_fix; addr = (addr + 4) & ~3; - /* Fix the insn as the 4-byte version if the target address is not - sufficiently aligned. This is prevents an infinite loop when two - instructions have contradictory range/alignment requirements. */ + /* Force misaligned targets to 32-bit variant. */ if (val & 3) - return -4; + return 4; val -= addr; if (val < 0 || val > 1020) return 4; @@ -10532,7 +16790,7 @@ relax_addsub (fragS *fragp, asection *sec) size of the offset field in the narrow instruction. */ static int -relax_branch (fragS *fragp, asection *sec, int bits) +relax_branch (fragS *fragp, asection *sec, int bits, long stretch) { addressT addr; offsetT val; @@ -10543,7 +16801,7 @@ relax_branch (fragS *fragp, asection *sec, int bits) || sec != S_GET_SEGMENT (fragp->fr_symbol)) return 4; - val = S_GET_VALUE(fragp->fr_symbol) + fragp->fr_offset; + val = relaxed_symbol_addr(fragp, stretch); addr = fragp->fr_address + fragp->fr_fix + 4; val -= addr; @@ -10559,7 +16817,7 @@ relax_branch (fragS *fragp, asection *sec, int bits) the current size of the frag should change. */ int -arm_relax_frag (asection *sec, fragS *fragp, long stretch ATTRIBUTE_UNUSED) +arm_relax_frag (asection *sec, fragS *fragp, long stretch) { int oldsize; int newsize; @@ -10568,7 +16826,7 @@ arm_relax_frag (asection *sec, fragS *fragp, long stretch ATTRIBUTE_UNUSED) switch (fragp->fr_subtype) { case T_MNEM_ldr_pc2: - newsize = relax_adr(fragp, sec); + newsize = relax_adr(fragp, sec, stretch); break; case T_MNEM_ldr_pc: case T_MNEM_ldr_sp: @@ -10588,7 +16846,7 @@ arm_relax_frag (asection *sec, fragS *fragp, long stretch ATTRIBUTE_UNUSED) newsize = relax_immediate(fragp, 5, 0); break; case T_MNEM_adr: - newsize = relax_adr(fragp, sec); + newsize = relax_adr(fragp, sec, stretch); break; case T_MNEM_mov: case T_MNEM_movs: @@ -10597,10 +16855,10 @@ arm_relax_frag (asection *sec, fragS *fragp, long stretch ATTRIBUTE_UNUSED) newsize = relax_immediate(fragp, 8, 0); break; case T_MNEM_b: - newsize = relax_branch(fragp, sec, 11); + newsize = relax_branch(fragp, sec, 11, stretch); break; case T_MNEM_bcond: - newsize = relax_branch(fragp, sec, 8); + newsize = relax_branch(fragp, sec, 8, stretch); break; case T_MNEM_add_sp: case T_MNEM_add_pc: @@ -10619,14 +16877,18 @@ arm_relax_frag (asection *sec, fragS *fragp, long stretch ATTRIBUTE_UNUSED) default: abort(); } - if (newsize < 0) + + fragp->fr_var = newsize; + /* Freeze wide instructions that are at or before the same location as + in the previous pass. This avoids infinite loops. + Don't freeze them unconditionally because targets may be artificialy + misaligned by the expansion of preceeding frags. */ + if (stretch <= 0 && newsize > 2) { - fragp->fr_var = -newsize; md_convert_frag (sec->owner, sec, fragp); frag_wane(fragp); - return -(newsize + oldsize); } - fragp->fr_var = newsize; + return newsize - oldsize; } @@ -10636,12 +16898,22 @@ valueT md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size) { -#ifdef OBJ_ELF - return size; -#else - /* Round all sects to multiple of 4. */ - return (size + 3) & ~3; +#if (defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT)) + if (OUTPUT_FLAVOR == bfd_target_aout_flavour) + { + /* For a.out, force the section size to be aligned. If we don't do + this, BFD will align it for us, but it will not write out the + final bytes of the section. This may be a bug in BFD, but it is + easier to fix it here since that is how the other a.out targets + work. */ + int align; + + align = bfd_get_section_alignment (stdoutput, segment); + size = ((size + (1 << align) - 1) & ((valueT) -1 << align)); + } #endif + + return size; } /* This is called from HANDLE_ALIGN in write.c. Fill in the contents @@ -10893,7 +17165,7 @@ finish_unwind_opcodes (void) if (unwind.fp_used) { - /* Adjust sp as neccessary. */ + /* Adjust sp as necessary. */ unwind.pending_offset += unwind.fp_offset - unwind.frame_size; flush_pending_unwind (); @@ -11141,12 +17413,22 @@ create_unwind_entry (int have_data) return 0; } + +/* Initialize the DWARF-2 unwind information for this procedure. */ + +void +tc_arm_frame_initial_instructions (void) +{ + cfi_add_CFA_def_cfa (REG_SP, 0); +} +#endif /* OBJ_ELF */ + /* Convert REGNAME to a DWARF-2 register number. */ int -tc_arm_regname_to_dw2regnum (const char *regname) +tc_arm_regname_to_dw2regnum (char *regname) { - int reg = arm_reg_parse ((char **) ®name, REG_TYPE_RN); + int reg = arm_reg_parse (®name, REG_TYPE_RN); if (reg == FAIL) return -1; @@ -11154,15 +17436,18 @@ tc_arm_regname_to_dw2regnum (const char *regname) return reg; } -/* Initialize the DWARF-2 unwind information for this procedure. */ - +#ifdef TE_PE void -tc_arm_frame_initial_instructions (void) +tc_pe_dwarf2_emit_offset (symbolS *symbol, unsigned int size) { - cfi_add_CFA_def_cfa (REG_SP, 0); -} -#endif /* OBJ_ELF */ + expressionS expr; + expr.X_op = O_secrel; + expr.X_add_symbol = symbol; + expr.X_add_number = 0; + emit_expr (&expr, size); +} +#endif /* MD interface: Symbol and relocation handling. */ @@ -11179,10 +17464,16 @@ md_pcrel_from_section (fixS * fixP, segT seg) /* If this is pc-relative and we are going to emit a relocation then we just want to put out any pipeline compensation that the linker - will need. Otherwise we want to use the calculated base. */ + will need. Otherwise we want to use the calculated base. + For WinCE we skip the bias for externals as well, since this + is how the MS ARM-CE assembler behaves and we want to be compatible. */ if (fixP->fx_pcrel && ((fixP->fx_addsy && S_GET_SEGMENT (fixP->fx_addsy) != seg) - || arm_force_relocation (fixP))) + || (arm_force_relocation (fixP) +#ifdef TE_WINCE + && !S_IS_EXTERNAL (fixP->fx_addsy) +#endif + ))) base = 0; switch (fixP->fx_r_type) @@ -11219,6 +17510,17 @@ md_pcrel_from_section (fixS * fixP, segT seg) case BFD_RELOC_ARM_PCREL_BLX: case BFD_RELOC_ARM_PLT32: #ifdef TE_WINCE + /* When handling fixups immediately, because we have already + discovered the value of a symbol, or the address of the frag involved + we must account for the offset by +8, as the OS loader will never see the reloc. + see fixup_segment() in write.c + The S_IS_EXTERNAL test handles the case of global symbols. + Those need the calculated base, not just the pipe compensation the linker will need. */ + if (fixP->fx_pcrel + && fixP->fx_addsy != NULL + && (S_GET_SEGMENT (fixP->fx_addsy) == seg) + && (S_IS_EXTERNAL (fixP->fx_addsy) || !arm_force_relocation (fixP))) + return base + 8; return base; #else return base + 8; @@ -11406,11 +17708,11 @@ negate_data_op (unsigned long * instruction, /* Like negate_data_op, but for Thumb-2. */ static unsigned int -thumb32_negate_data_op (offsetT *instruction, offsetT value) +thumb32_negate_data_op (offsetT *instruction, unsigned int value) { int op, new_inst; int rd; - offsetT negated, inverted; + unsigned int negated, inverted; negated = encode_thumb32_immediate (-value); inverted = encode_thumb32_immediate (~value); @@ -11471,7 +17773,7 @@ thumb32_negate_data_op (offsetT *instruction, offsetT value) return FAIL; } - if (value == FAIL) + if (value == (unsigned int)FAIL) return FAIL; *instruction &= T2_OPCODE_MASK; @@ -11528,6 +17830,7 @@ md_apply_fix (fixS * fixP, assert (fixP->fx_r_type <= BFD_RELOC_UNUSED); /* Note whether this will delete the relocation. */ + if (fixP->fx_addsy == 0 && !fixP->fx_pcrel) fixP->fx_done = 1; @@ -11676,7 +17979,7 @@ md_apply_fix (fixS * fixP, as_bad_where (fixP->fx_file, fixP->fx_line, _("invalid literal constant: pool needs to be closer")); else - as_bad (_("bad immediate value for half-word offset (%ld)"), + as_bad (_("bad immediate value for 8-bit offset (%ld)"), (long) value); break; } @@ -11835,6 +18138,7 @@ md_apply_fix (fixS * fixP, break; case BFD_RELOC_ARM_T32_IMMEDIATE: + case BFD_RELOC_ARM_T32_ADD_IMM: case BFD_RELOC_ARM_T32_IMM12: case BFD_RELOC_ARM_T32_ADD_PC12: /* We claim that this fixup has been processed here, @@ -11855,15 +18159,21 @@ md_apply_fix (fixS * fixP, newval <<= 16; newval |= md_chars_to_number (buf+2, THUMB_SIZE); - /* FUTURE: Implement analogue of negate_data_op for T32. */ - if (fixP->fx_r_type == BFD_RELOC_ARM_T32_IMMEDIATE) + newimm = FAIL; + if (fixP->fx_r_type == BFD_RELOC_ARM_T32_IMMEDIATE + || fixP->fx_r_type == BFD_RELOC_ARM_T32_ADD_IMM) { newimm = encode_thumb32_immediate (value); if (newimm == (unsigned int) FAIL) newimm = thumb32_negate_data_op (&newval, value); } - else + if (fixP->fx_r_type != BFD_RELOC_ARM_T32_IMMEDIATE + && newimm == (unsigned int) FAIL) { + /* Turn add/sum into addw/subw. */ + if (fixP->fx_r_type == BFD_RELOC_ARM_T32_ADD_IMM) + newval = (newval & 0xfeffffff) | 0x02000000; + /* 12 bit immediate for addw/subw. */ if (value < 0) { @@ -11977,18 +18287,34 @@ md_apply_fix (fixS * fixP, } break; - case BFD_RELOC_THUMB_PCREL_BRANCH7: /* CZB */ - /* CZB can only branch forward. */ - if (value & ~0x7e) - as_bad_where (fixP->fx_file, fixP->fx_line, - _("branch out of range")); + case BFD_RELOC_THUMB_PCREL_BRANCH7: /* CBZ */ + /* CBZ can only branch forward. */ - if (fixP->fx_done || !seg->use_rela_p) + /* Attempts to use CBZ to branch to the next instruction + (which, strictly speaking, are prohibited) will be turned into + no-ops. + + FIXME: It may be better to remove the instruction completely and + perform relaxation. */ + if (value == -2) { newval = md_chars_to_number (buf, THUMB_SIZE); - newval |= ((value & 0x3e) << 2) | ((value & 0x40) << 3); + newval = 0xbf00; /* NOP encoding T1 */ md_number_to_chars (buf, newval, THUMB_SIZE); } + else + { + if (value & ~0x7e) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("branch out of range")); + + if (fixP->fx_done || !seg->use_rela_p) + { + newval = md_chars_to_number (buf, THUMB_SIZE); + newval |= ((value & 0x3e) << 2) | ((value & 0x40) << 3); + md_number_to_chars (buf, newval, THUMB_SIZE); + } + } break; case BFD_RELOC_THUMB_PCREL_BRANCH9: /* Conditional branch. */ @@ -12129,8 +18455,15 @@ md_apply_fix (fixS * fixP, case BFD_RELOC_ARM_ROSEGREL32: case BFD_RELOC_ARM_SBREL32: case BFD_RELOC_32_PCREL: +#ifdef TE_PE + case BFD_RELOC_32_SECREL: +#endif if (fixP->fx_done || !seg->use_rela_p) - md_number_to_chars (buf, value, 4); +#ifdef TE_WINCE + /* For WinCE we only do this for pcrel fixups. */ + if (fixP->fx_done || fixP->fx_pcrel) +#endif + md_number_to_chars (buf, value, 4); break; #ifdef OBJ_ELF @@ -12165,8 +18498,6 @@ md_apply_fix (fixS * fixP, newval = get_thumb32_insn (buf); newval &= 0xff7fff00; newval |= (value >> 2) | (sign ? INDEX_UP : 0); - if (value == 0) - newval &= ~WRITE_BACK; if (fixP->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM || fixP->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM_S2) md_number_to_chars (buf, newval, INSN_SIZE); @@ -12358,6 +18689,216 @@ md_apply_fix (fixS * fixP, fixP->fx_done = 0; return; + case BFD_RELOC_ARM_MOVW: + case BFD_RELOC_ARM_MOVT: + case BFD_RELOC_ARM_THUMB_MOVW: + case BFD_RELOC_ARM_THUMB_MOVT: + if (fixP->fx_done || !seg->use_rela_p) + { + /* REL format relocations are limited to a 16-bit addend. */ + if (!fixP->fx_done) + { + if (value < -0x1000 || value > 0xffff) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("offset too big")); + } + else if (fixP->fx_r_type == BFD_RELOC_ARM_MOVT + || fixP->fx_r_type == BFD_RELOC_ARM_THUMB_MOVT) + { + value >>= 16; + } + + if (fixP->fx_r_type == BFD_RELOC_ARM_THUMB_MOVW + || fixP->fx_r_type == BFD_RELOC_ARM_THUMB_MOVT) + { + newval = get_thumb32_insn (buf); + newval &= 0xfbf08f00; + newval |= (value & 0xf000) << 4; + newval |= (value & 0x0800) << 15; + newval |= (value & 0x0700) << 4; + newval |= (value & 0x00ff); + put_thumb32_insn (buf, newval); + } + else + { + newval = md_chars_to_number (buf, 4); + newval &= 0xfff0f000; + newval |= value & 0x0fff; + newval |= (value & 0xf000) << 4; + md_number_to_chars (buf, newval, 4); + } + } + return; + + case BFD_RELOC_ARM_ALU_PC_G0_NC: + case BFD_RELOC_ARM_ALU_PC_G0: + case BFD_RELOC_ARM_ALU_PC_G1_NC: + case BFD_RELOC_ARM_ALU_PC_G1: + case BFD_RELOC_ARM_ALU_PC_G2: + case BFD_RELOC_ARM_ALU_SB_G0_NC: + case BFD_RELOC_ARM_ALU_SB_G0: + case BFD_RELOC_ARM_ALU_SB_G1_NC: + case BFD_RELOC_ARM_ALU_SB_G1: + case BFD_RELOC_ARM_ALU_SB_G2: + assert (!fixP->fx_done); + if (!seg->use_rela_p) + { + bfd_vma insn; + bfd_vma encoded_addend; + bfd_vma addend_abs = abs (value); + + /* Check that the absolute value of the addend can be + expressed as an 8-bit constant plus a rotation. */ + encoded_addend = encode_arm_immediate (addend_abs); + if (encoded_addend == (unsigned int) FAIL) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("the offset 0x%08lX is not representable"), + (unsigned long) addend_abs); + + /* Extract the instruction. */ + insn = md_chars_to_number (buf, INSN_SIZE); + + /* If the addend is positive, use an ADD instruction. + Otherwise use a SUB. Take care not to destroy the S bit. */ + insn &= 0xff1fffff; + if (value < 0) + insn |= 1 << 22; + else + insn |= 1 << 23; + + /* Place the encoded addend into the first 12 bits of the + instruction. */ + insn &= 0xfffff000; + insn |= encoded_addend; + + /* Update the instruction. */ + md_number_to_chars (buf, insn, INSN_SIZE); + } + break; + + case BFD_RELOC_ARM_LDR_PC_G0: + case BFD_RELOC_ARM_LDR_PC_G1: + case BFD_RELOC_ARM_LDR_PC_G2: + case BFD_RELOC_ARM_LDR_SB_G0: + case BFD_RELOC_ARM_LDR_SB_G1: + case BFD_RELOC_ARM_LDR_SB_G2: + assert (!fixP->fx_done); + if (!seg->use_rela_p) + { + bfd_vma insn; + bfd_vma addend_abs = abs (value); + + /* Check that the absolute value of the addend can be + encoded in 12 bits. */ + if (addend_abs >= 0x1000) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("bad offset 0x%08lX (only 12 bits available for the magnitude)"), + (unsigned long) addend_abs); + + /* Extract the instruction. */ + insn = md_chars_to_number (buf, INSN_SIZE); + + /* If the addend is negative, clear bit 23 of the instruction. + Otherwise set it. */ + if (value < 0) + insn &= ~(1 << 23); + else + insn |= 1 << 23; + + /* Place the absolute value of the addend into the first 12 bits + of the instruction. */ + insn &= 0xfffff000; + insn |= addend_abs; + + /* Update the instruction. */ + md_number_to_chars (buf, insn, INSN_SIZE); + } + break; + + case BFD_RELOC_ARM_LDRS_PC_G0: + case BFD_RELOC_ARM_LDRS_PC_G1: + case BFD_RELOC_ARM_LDRS_PC_G2: + case BFD_RELOC_ARM_LDRS_SB_G0: + case BFD_RELOC_ARM_LDRS_SB_G1: + case BFD_RELOC_ARM_LDRS_SB_G2: + assert (!fixP->fx_done); + if (!seg->use_rela_p) + { + bfd_vma insn; + bfd_vma addend_abs = abs (value); + + /* Check that the absolute value of the addend can be + encoded in 8 bits. */ + if (addend_abs >= 0x100) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("bad offset 0x%08lX (only 8 bits available for the magnitude)"), + (unsigned long) addend_abs); + + /* Extract the instruction. */ + insn = md_chars_to_number (buf, INSN_SIZE); + + /* If the addend is negative, clear bit 23 of the instruction. + Otherwise set it. */ + if (value < 0) + insn &= ~(1 << 23); + else + insn |= 1 << 23; + + /* Place the first four bits of the absolute value of the addend + into the first 4 bits of the instruction, and the remaining + four into bits 8 .. 11. */ + insn &= 0xfffff0f0; + insn |= (addend_abs & 0xf) | ((addend_abs & 0xf0) << 4); + + /* Update the instruction. */ + md_number_to_chars (buf, insn, INSN_SIZE); + } + break; + + case BFD_RELOC_ARM_LDC_PC_G0: + case BFD_RELOC_ARM_LDC_PC_G1: + case BFD_RELOC_ARM_LDC_PC_G2: + case BFD_RELOC_ARM_LDC_SB_G0: + case BFD_RELOC_ARM_LDC_SB_G1: + case BFD_RELOC_ARM_LDC_SB_G2: + assert (!fixP->fx_done); + if (!seg->use_rela_p) + { + bfd_vma insn; + bfd_vma addend_abs = abs (value); + + /* Check that the absolute value of the addend is a multiple of + four and, when divided by four, fits in 8 bits. */ + if (addend_abs & 0x3) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("bad offset 0x%08lX (must be word-aligned)"), + (unsigned long) addend_abs); + + if ((addend_abs >> 2) > 0xff) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("bad offset 0x%08lX (must be an 8-bit number of words)"), + (unsigned long) addend_abs); + + /* Extract the instruction. */ + insn = md_chars_to_number (buf, INSN_SIZE); + + /* If the addend is negative, clear bit 23 of the instruction. + Otherwise set it. */ + if (value < 0) + insn &= ~(1 << 23); + else + insn |= 1 << 23; + + /* Place the addend (divided by four) into the first eight + bits of the instruction. */ + insn &= 0xfffffff0; + insn |= addend_abs >> 2; + + /* Update the instruction. */ + md_number_to_chars (buf, insn, INSN_SIZE); + } + break; + case BFD_RELOC_UNUSED: default: as_bad_where (fixP->fx_file, fixP->fx_line, @@ -12412,6 +18953,34 @@ tc_gen_reloc (asection *section, fixS *fixp) break; } + case BFD_RELOC_ARM_MOVW: + if (fixp->fx_pcrel) + { + code = BFD_RELOC_ARM_MOVW_PCREL; + break; + } + + case BFD_RELOC_ARM_MOVT: + if (fixp->fx_pcrel) + { + code = BFD_RELOC_ARM_MOVT_PCREL; + break; + } + + case BFD_RELOC_ARM_THUMB_MOVW: + if (fixp->fx_pcrel) + { + code = BFD_RELOC_ARM_THUMB_MOVW_PCREL; + break; + } + + case BFD_RELOC_ARM_THUMB_MOVT: + if (fixp->fx_pcrel) + { + code = BFD_RELOC_ARM_THUMB_MOVT_PCREL; + break; + } + case BFD_RELOC_NONE: case BFD_RELOC_ARM_PCREL_BRANCH: case BFD_RELOC_ARM_PCREL_BLX: @@ -12425,6 +18994,9 @@ tc_gen_reloc (asection *section, fixS *fixp) case BFD_RELOC_THUMB_PCREL_BLX: case BFD_RELOC_VTABLE_ENTRY: case BFD_RELOC_VTABLE_INHERIT: +#ifdef TE_PE + case BFD_RELOC_32_SECREL: +#endif code = fixp->fx_r_type; break; @@ -12449,6 +19021,34 @@ tc_gen_reloc (asection *section, fixS *fixp) case BFD_RELOC_ARM_TLS_LDO32: case BFD_RELOC_ARM_PCREL_CALL: case BFD_RELOC_ARM_PCREL_JUMP: + case BFD_RELOC_ARM_ALU_PC_G0_NC: + case BFD_RELOC_ARM_ALU_PC_G0: + case BFD_RELOC_ARM_ALU_PC_G1_NC: + case BFD_RELOC_ARM_ALU_PC_G1: + case BFD_RELOC_ARM_ALU_PC_G2: + case BFD_RELOC_ARM_LDR_PC_G0: + case BFD_RELOC_ARM_LDR_PC_G1: + case BFD_RELOC_ARM_LDR_PC_G2: + case BFD_RELOC_ARM_LDRS_PC_G0: + case BFD_RELOC_ARM_LDRS_PC_G1: + case BFD_RELOC_ARM_LDRS_PC_G2: + case BFD_RELOC_ARM_LDC_PC_G0: + case BFD_RELOC_ARM_LDC_PC_G1: + case BFD_RELOC_ARM_LDC_PC_G2: + case BFD_RELOC_ARM_ALU_SB_G0_NC: + case BFD_RELOC_ARM_ALU_SB_G0: + case BFD_RELOC_ARM_ALU_SB_G1_NC: + case BFD_RELOC_ARM_ALU_SB_G1: + case BFD_RELOC_ARM_ALU_SB_G2: + case BFD_RELOC_ARM_LDR_SB_G0: + case BFD_RELOC_ARM_LDR_SB_G1: + case BFD_RELOC_ARM_LDR_SB_G2: + case BFD_RELOC_ARM_LDRS_SB_G0: + case BFD_RELOC_ARM_LDRS_SB_G1: + case BFD_RELOC_ARM_LDRS_SB_G2: + case BFD_RELOC_ARM_LDC_SB_G0: + case BFD_RELOC_ARM_LDC_SB_G1: + case BFD_RELOC_ARM_LDC_SB_G2: code = fixp->fx_r_type; break; @@ -12579,6 +19179,14 @@ cons_fix_new_arm (fragS * frag, break; } +#ifdef TE_PE + if (exp->X_op == O_secrel) + { + exp->X_op = O_symbol; + type = BFD_RELOC_32_SECREL; + } +#endif + fix_new_exp (frag, where, (int) size, exp, pcrel, type); } @@ -12612,34 +19220,31 @@ arm_force_relocation (struct fix * fixp) if (fixp->fx_r_type == BFD_RELOC_ARM_IMMEDIATE || fixp->fx_r_type == BFD_RELOC_ARM_OFFSET_IMM || fixp->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE + || fixp->fx_r_type == BFD_RELOC_ARM_T32_ADD_IMM || fixp->fx_r_type == BFD_RELOC_ARM_T32_IMMEDIATE || fixp->fx_r_type == BFD_RELOC_ARM_T32_IMM12 || fixp->fx_r_type == BFD_RELOC_ARM_T32_ADD_PC12) return 0; - return generic_force_reloc (fixp); -} - -#ifdef OBJ_COFF -/* This is a little hack to help the gas/arm/adrl.s test. It prevents - local labels from being added to the output symbol table when they - are used with the ADRL pseudo op. The ADRL relocation should always - be resolved before the binbary is emitted, so it is safe to say that - it is adjustable. */ + /* Always leave these relocations for the linker. */ + if ((fixp->fx_r_type >= BFD_RELOC_ARM_ALU_PC_G0_NC + && fixp->fx_r_type <= BFD_RELOC_ARM_LDC_SB_G2) + || fixp->fx_r_type == BFD_RELOC_ARM_LDR_PC_G0) + return 1; -bfd_boolean -arm_fix_adjustable (fixS * fixP) -{ - if (fixP->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE) + /* Always generate relocations against function symbols. */ + if (fixp->fx_r_type == BFD_RELOC_32 + && fixp->fx_addsy + && (symbol_get_bfdsym (fixp->fx_addsy)->flags & BSF_FUNCTION)) return 1; - return 0; + + return generic_force_reloc (fixp); } -#endif -#ifdef OBJ_ELF -/* Relocations against Thumb function names must be left unadjusted, - so that the linker can use this information to correctly set the - bottom bit of their addresses. The MIPS version of this function +#if defined (OBJ_ELF) || defined (OBJ_COFF) +/* Relocations against function names must be left unadjusted, + so that the linker can use this information to generate interworking + stubs. The MIPS version of this function also prevents relocations that are mips-16 specific, but I do not know why it does this. @@ -12656,6 +19261,10 @@ arm_fix_adjustable (fixS * fixP) if (fixP->fx_addsy == NULL) return 1; + /* Preserve relocations against symbols with function type. */ + if (symbol_get_bfdsym (fixP->fx_addsy)->flags & BSF_FUNCTION) + return 0; + if (THUMB_IS_FUNC (fixP->fx_addsy) && fixP->fx_subsy == NULL) return 0; @@ -12677,8 +19286,17 @@ arm_fix_adjustable (fixS * fixP) || fixP->fx_r_type == BFD_RELOC_ARM_TARGET2) return 0; + /* Similarly for group relocations. */ + if ((fixP->fx_r_type >= BFD_RELOC_ARM_ALU_PC_G0_NC + && fixP->fx_r_type <= BFD_RELOC_ARM_LDC_SB_G2) + || fixP->fx_r_type == BFD_RELOC_ARM_LDR_PC_G0) + return 0; + return 1; } +#endif /* defined (OBJ_ELF) || defined (OBJ_COFF) */ + +#ifdef OBJ_ELF const char * elf32_arm_target_format (void) @@ -12789,14 +19407,15 @@ arm_adjust_symtab (void) elf_sym = elf_symbol (symbol_get_bfdsym (sym)); bind = ELF_ST_BIND (elf_sym->internal_elf_sym.st_info); - if (! bfd_is_arm_mapping_symbol_name (elf_sym->symbol.name)) + if (! bfd_is_arm_special_symbol_name (elf_sym->symbol.name, + BFD_ARM_SPECIAL_SYM_TYPE_ANY)) { /* If it's a .thumb_func, declare it as so, otherwise tag label as .code 16. */ if (THUMB_IS_FUNC (sym)) elf_sym->internal_elf_sym.st_info = ELF_ST_INFO (bind, STT_ARM_TFUNC); - else + else if (EF_ARM_EABI_VERSION (meabi_flags) < EF_ARM_EABI_VER4) elf_sym->internal_elf_sym.st_info = ELF_ST_INFO (bind, STT_ARM_16BIT); } @@ -12817,6 +19436,16 @@ set_constant_flonums (void) abort (); } +/* Auto-select Thumb mode if it's the only available instruction set for the + given architecture. */ + +static void +autoselect_thumb_from_cpu_variant (void) +{ + if (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v1)) + opcode_select (16); +} + void md_begin (void) { @@ -12894,9 +19523,9 @@ md_begin (void) if (!mfpu_opt) { - if (!mcpu_cpu_opt) + if (mcpu_cpu_opt != NULL) mfpu_opt = &fpu_default; - else if (ARM_CPU_HAS_FEATURE (*mcpu_fpu_opt, arm_ext_v5)) + else if (mcpu_fpu_opt != NULL && ARM_CPU_HAS_FEATURE (*mcpu_fpu_opt, arm_ext_v5)) mfpu_opt = &fpu_arch_vfp_v2; else mfpu_opt = &fpu_arch_fpa; @@ -12917,6 +19546,8 @@ md_begin (void) ARM_MERGE_FEATURE_SETS (cpu_variant, *mcpu_cpu_opt, *mfpu_opt); + autoselect_thumb_from_cpu_variant (); + arm_arch_used = thumb_arch_used = arm_arch_none; #if defined OBJ_COFF || defined OBJ_ELF @@ -12992,7 +19623,9 @@ md_begin (void) #endif /* Record the CPU type as well. */ - if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_iwmmxt)) + if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_iwmmxt2)) + mach = bfd_mach_arm_iWMMXt2; + else if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_iwmmxt)) mach = bfd_mach_arm_iWMMXt; else if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_xscale)) mach = bfd_mach_arm_XScale; @@ -13362,13 +19995,16 @@ static const struct arm_cpu_option_table arm_cpus[] = {"arm1156t2f-s", ARM_ARCH_V6T2, FPU_ARCH_VFP_V2, NULL}, {"arm1176jz-s", ARM_ARCH_V6ZK, FPU_NONE, NULL}, {"arm1176jzf-s", ARM_ARCH_V6ZK, FPU_ARCH_VFP_V2, NULL}, - {"cortex-a8", ARM_ARCH_V7A, FPU_ARCH_VFP_V2, NULL}, + {"cortex-a8", ARM_ARCH_V7A, ARM_FEATURE(0, FPU_VFP_V3 + | FPU_NEON_EXT_V1), + NULL}, {"cortex-r4", ARM_ARCH_V7R, FPU_NONE, NULL}, {"cortex-m3", ARM_ARCH_V7M, FPU_NONE, NULL}, /* ??? XSCALE is really an architecture. */ {"xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2, NULL}, /* ??? iwmmxt is not a processor. */ {"iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP_V2, NULL}, + {"iwmmxt2", ARM_ARCH_IWMMXT2,FPU_ARCH_VFP_V2, NULL}, {"i80200", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2, NULL}, /* Maverick */ {"ep9312", ARM_FEATURE(ARM_AEXT_V4T, ARM_CEXT_MAVERICK), FPU_ARCH_MAVERICK, "ARM920T"}, @@ -13413,11 +20049,17 @@ static const struct arm_arch_option_table arm_archs[] = {"armv6zt2", ARM_ARCH_V6ZT2, FPU_ARCH_VFP}, {"armv6zkt2", ARM_ARCH_V6ZKT2, FPU_ARCH_VFP}, {"armv7", ARM_ARCH_V7, FPU_ARCH_VFP}, + /* The official spelling of the ARMv7 profile variants is the dashed form. + Accept the non-dashed form for compatibility with old toolchains. */ {"armv7a", ARM_ARCH_V7A, FPU_ARCH_VFP}, {"armv7r", ARM_ARCH_V7R, FPU_ARCH_VFP}, {"armv7m", ARM_ARCH_V7M, FPU_ARCH_VFP}, + {"armv7-a", ARM_ARCH_V7A, FPU_ARCH_VFP}, + {"armv7-r", ARM_ARCH_V7R, FPU_ARCH_VFP}, + {"armv7-m", ARM_ARCH_V7M, FPU_ARCH_VFP}, {"xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP}, {"iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP}, + {"iwmmxt2", ARM_ARCH_IWMMXT2,FPU_ARCH_VFP}, {NULL, ARM_ARCH_NONE, ARM_ARCH_NONE} }; @@ -13433,6 +20075,7 @@ static const struct arm_option_cpu_value_table arm_extensions[] = {"maverick", ARM_FEATURE (0, ARM_CEXT_MAVERICK)}, {"xscale", ARM_FEATURE (0, ARM_CEXT_XSCALE)}, {"iwmmxt", ARM_FEATURE (0, ARM_CEXT_IWMMXT)}, + {"iwmmxt2", ARM_FEATURE (0, ARM_CEXT_IWMMXT2)}, {NULL, ARM_ARCH_NONE} }; @@ -13452,6 +20095,7 @@ static const struct arm_option_cpu_value_table arm_fpus[] = {"softvfp+vfp", FPU_ARCH_VFP_V2}, {"vfp", FPU_ARCH_VFP_V2}, {"vfp9", FPU_ARCH_VFP_V2}, + {"vfp3", FPU_ARCH_VFP_V3}, {"vfp10", FPU_ARCH_VFP_V2}, {"vfp10-r0", FPU_ARCH_VFP_V1}, {"vfpxd", FPU_ARCH_VFP_V1xD}, @@ -13460,6 +20104,7 @@ static const struct arm_option_cpu_value_table arm_fpus[] = {"arm1136jfs", FPU_ARCH_VFP_V2}, {"arm1136jf-s", FPU_ARCH_VFP_V2}, {"maverick", FPU_ARCH_MAVERICK}, + {"neon", FPU_ARCH_VFP_V3_PLUS_NEON_V1}, {NULL, ARM_ARCH_NONE} }; @@ -13855,7 +20500,13 @@ aeabi_set_public_attributes (void) ARM_MERGE_FEATURE_SETS (flags, arm_arch_used, thumb_arch_used); ARM_MERGE_FEATURE_SETS (flags, flags, *mfpu_opt); ARM_MERGE_FEATURE_SETS (flags, flags, selected_cpu); - + /*Allow the user to override the reported architecture. */ + if (object_arch) + { + ARM_CLEAR_FEATURE (flags, flags, arm_arch_any); + ARM_MERGE_FEATURE_SETS (flags, flags, *object_arch); + } + tmp = flags; arch = 0; for (p = cpu_arch_ver; p->val; p++) @@ -13881,57 +20532,56 @@ aeabi_set_public_attributes (void) for (i = 0; p[i]; i++) p[i] = TOUPPER (p[i]); } - elf32_arm_add_eabi_attr_string (stdoutput, 5, p); + bfd_elf_add_proc_attr_string (stdoutput, 5, p); } /* Tag_CPU_arch. */ - elf32_arm_add_eabi_attr_int (stdoutput, 6, arch); + bfd_elf_add_proc_attr_int (stdoutput, 6, arch); /* Tag_CPU_arch_profile. */ if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v7a)) - elf32_arm_add_eabi_attr_int (stdoutput, 7, 'A'); + bfd_elf_add_proc_attr_int (stdoutput, 7, 'A'); else if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v7r)) - elf32_arm_add_eabi_attr_int (stdoutput, 7, 'R'); + bfd_elf_add_proc_attr_int (stdoutput, 7, 'R'); else if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v7m)) - elf32_arm_add_eabi_attr_int (stdoutput, 7, 'M'); + bfd_elf_add_proc_attr_int (stdoutput, 7, 'M'); /* Tag_ARM_ISA_use. */ if (ARM_CPU_HAS_FEATURE (arm_arch_used, arm_arch_full)) - elf32_arm_add_eabi_attr_int (stdoutput, 8, 1); + bfd_elf_add_proc_attr_int (stdoutput, 8, 1); /* Tag_THUMB_ISA_use. */ if (ARM_CPU_HAS_FEATURE (thumb_arch_used, arm_arch_full)) - elf32_arm_add_eabi_attr_int (stdoutput, 9, + bfd_elf_add_proc_attr_int (stdoutput, 9, ARM_CPU_HAS_FEATURE (thumb_arch_used, arm_arch_t2) ? 2 : 1); /* Tag_VFP_arch. */ - if (ARM_CPU_HAS_FEATURE (thumb_arch_used, fpu_arch_vfp_v2) - || ARM_CPU_HAS_FEATURE (arm_arch_used, fpu_arch_vfp_v2)) - elf32_arm_add_eabi_attr_int (stdoutput, 10, 2); - else if (ARM_CPU_HAS_FEATURE (thumb_arch_used, fpu_arch_vfp_v1) - || ARM_CPU_HAS_FEATURE (arm_arch_used, fpu_arch_vfp_v1)) - elf32_arm_add_eabi_attr_int (stdoutput, 10, 1); + if (ARM_CPU_HAS_FEATURE (thumb_arch_used, fpu_vfp_ext_v3) + || ARM_CPU_HAS_FEATURE (arm_arch_used, fpu_vfp_ext_v3)) + bfd_elf_add_proc_attr_int (stdoutput, 10, 3); + else if (ARM_CPU_HAS_FEATURE (thumb_arch_used, fpu_vfp_ext_v2) + || ARM_CPU_HAS_FEATURE (arm_arch_used, fpu_vfp_ext_v2)) + bfd_elf_add_proc_attr_int (stdoutput, 10, 2); + else if (ARM_CPU_HAS_FEATURE (thumb_arch_used, fpu_vfp_ext_v1) + || ARM_CPU_HAS_FEATURE (arm_arch_used, fpu_vfp_ext_v1) + || ARM_CPU_HAS_FEATURE (thumb_arch_used, fpu_vfp_ext_v1xd) + || ARM_CPU_HAS_FEATURE (arm_arch_used, fpu_vfp_ext_v1xd)) + bfd_elf_add_proc_attr_int (stdoutput, 10, 1); /* Tag_WMMX_arch. */ if (ARM_CPU_HAS_FEATURE (thumb_arch_used, arm_cext_iwmmxt) || ARM_CPU_HAS_FEATURE (arm_arch_used, arm_cext_iwmmxt)) - elf32_arm_add_eabi_attr_int (stdoutput, 11, 1); + bfd_elf_add_proc_attr_int (stdoutput, 11, 1); + /* Tag_NEON_arch. */ + if (ARM_CPU_HAS_FEATURE (thumb_arch_used, fpu_neon_ext_v1) + || ARM_CPU_HAS_FEATURE (arm_arch_used, fpu_neon_ext_v1)) + bfd_elf_add_proc_attr_int (stdoutput, 12, 1); } -/* Add the .ARM.attributes section. */ +/* Add the default contents for the .ARM.attributes section. */ void arm_md_end (void) { - segT s; - char *p; - addressT addr; - offsetT size; - if (EF_ARM_EABI_VERSION (meabi_flags) < EF_ARM_EABI_VER4) return; aeabi_set_public_attributes (); - size = elf32_arm_eabi_attr_size (stdoutput); - s = subseg_new (".ARM.attributes", 0); - bfd_set_section_flags (stdoutput, s, SEC_READONLY | SEC_DATA); - addr = frag_now_fix (); - p = frag_more (size); - elf32_arm_set_eabi_attr_contents (stdoutput, (bfd_byte *)p, size); } +#endif /* OBJ_ELF */ /* Parse a .cpu directive. */ @@ -14009,6 +20659,37 @@ s_arm_arch (int ignored ATTRIBUTE_UNUSED) } +/* Parse a .object_arch directive. */ + +static void +s_arm_object_arch (int ignored ATTRIBUTE_UNUSED) +{ + const struct arm_arch_option_table *opt; + char saved_char; + char *name; + + name = input_line_pointer; + while (*input_line_pointer && !ISSPACE(*input_line_pointer)) + input_line_pointer++; + saved_char = *input_line_pointer; + *input_line_pointer = 0; + + /* Skip the first "all" entry. */ + for (opt = arm_archs + 1; opt->name != NULL; opt++) + if (streq (opt->name, name)) + { + object_arch = &opt->value; + *input_line_pointer = saved_char; + demand_empty_rest_of_line (); + return; + } + + as_bad (_("unknown architecture `%s'\n"), name); + *input_line_pointer = saved_char; + ignore_rest_of_line (); +} + + /* Parse a .fpu directive. */ static void @@ -14038,5 +20719,10 @@ s_arm_fpu (int ignored ATTRIBUTE_UNUSED) *input_line_pointer = saved_char; ignore_rest_of_line (); } -#endif /* OBJ_ELF */ +/* Copy symbol information. */ +void +arm_copy_symbol_attributes (symbolS *dest, symbolS *src) +{ + ARM_GET_FLAG (dest) = ARM_GET_FLAG (src); +} diff --git a/contrib/binutils/gas/config/tc-arm.h b/contrib/binutils/gas/config/tc-arm.h index f261577..d6dee9b 100644 --- a/contrib/binutils/gas/config/tc-arm.h +++ b/contrib/binutils/gas/config/tc-arm.h @@ -1,6 +1,6 @@ /* This file is tc-arm.h Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, - 2004 Free Software Foundation, Inc. + 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org) Modified by David Taylor (dtaylor@armltd.co.uk) @@ -66,6 +66,8 @@ struct fix; # if defined TE_PE # if defined TE_EPOC # define TARGET_FORMAT (target_big_endian ? "epoc-pe-arm-big" : "epoc-pe-arm-little") +# elif defined TE_WINCE +# define TARGET_FORMAT (target_big_endian ? "pe-arm-wince-big" : "pe-arm-wince-little") # else # define TARGET_FORMAT (target_big_endian ? "pe-arm-big" : "pe-arm-little") # endif @@ -98,6 +100,7 @@ extern int arm_optimize_expr (expressionS *, operatorT, expressionS *); #ifdef OBJ_ELF #define md_end arm_md_end extern void arm_md_end (void); +bfd_boolean arm_is_eabi (void); #endif /* NOTE: The fake label creation in stabs.c:s_stab_generic() has @@ -120,12 +123,30 @@ extern void arm_md_end (void); #define ARM_IS_THUMB(s) (ARM_GET_FLAG (s) & ARM_FLAG_THUMB) #define ARM_IS_INTERWORK(s) (ARM_GET_FLAG (s) & ARM_FLAG_INTERWORK) +#ifdef OBJ_ELF + +/* For ELF objects THUMB_IS_FUNC is inferred from + ARM_IS_TUMB and the function type. */ +#define THUMB_IS_FUNC(s) \ + ((arm_is_eabi () \ + && (ARM_IS_THUMB (s)) \ + && (symbol_get_bfdsym (s)->flags & BSF_FUNCTION)) \ + || (ARM_GET_FLAG (s) & THUMB_FLAG_FUNC)) + +#else #define THUMB_IS_FUNC(s) (ARM_GET_FLAG (s) & THUMB_FLAG_FUNC) +#endif #define ARM_SET_THUMB(s,t) ((t) ? ARM_SET_FLAG (s, ARM_FLAG_THUMB) : ARM_RESET_FLAG (s, ARM_FLAG_THUMB)) #define ARM_SET_INTERWORK(s,t) ((t) ? ARM_SET_FLAG (s, ARM_FLAG_INTERWORK) : ARM_RESET_FLAG (s, ARM_FLAG_INTERWORK)) #define THUMB_SET_FUNC(s,t) ((t) ? ARM_SET_FLAG (s, THUMB_FLAG_FUNC) : ARM_RESET_FLAG (s, THUMB_FLAG_FUNC)) +void arm_copy_symbol_attributes (symbolS *, symbolS *); +#ifndef TC_COPY_SYMBOL_ATTRIBUTES +#define TC_COPY_SYMBOL_ATTRIBUTES(DEST, SRC) \ + (arm_copy_symbol_attributes (DEST, SRC)) +#endif + #define TC_START_LABEL(C,STR) (c == ':' || (c == '/' && arm_data_in_code ())) #define tc_canonicalize_symbol_name(str) arm_canonicalize_symbol_name (str); #define obj_adjust_symtab() arm_adjust_symtab () @@ -146,7 +167,6 @@ extern void arm_md_end (void); #define TC_FORCE_RELOCATION_LOCAL(FIX) \ (!(FIX)->fx_pcrel \ - || (FIX)->fx_plt \ || (FIX)->fx_r_type == BFD_RELOC_ARM_GOT32 \ || (FIX)->fx_r_type == BFD_RELOC_32 \ || TC_FORCE_RELOCATION (FIX)) @@ -175,14 +195,27 @@ extern void arm_md_end (void); goto LABEL; \ } +#define DWARF2_LINE_MIN_INSN_LENGTH 2 + +/* The lr register is r14. */ +#define DWARF2_DEFAULT_RETURN_COLUMN 14 + +/* Registers are generally saved at negative offsets to the CFA. */ +#define DWARF2_CIE_DATA_ALIGNMENT (-4) + #ifdef OBJ_ELF -# define DWARF2_LINE_MIN_INSN_LENGTH 2 # define obj_frob_symbol(sym, punt) armelf_frob_symbol ((sym), & (punt)) # define md_elf_section_change_hook() arm_elf_change_section () # define md_elf_section_type(str, len) arm_elf_section_type (str, len) # define GLOBAL_OFFSET_TABLE_NAME "_GLOBAL_OFFSET_TABLE_" # define TC_SEGMENT_INFO_TYPE struct arm_segment_info_type +/* This is not really an alignment operation, but it's something we + need to do at the same time: whenever we are figuring out the + alignment for data, we should check whether a $d symbol is + necessary. */ +# define md_cons_align(nbytes) mapping_state (MAP_DATA) + enum mstate { MAP_UNDEFINED = 0, /* Must be zero, for seginfo in new sections. */ @@ -191,6 +224,8 @@ enum mstate MAP_THUMB }; +void mapping_state (enum mstate); + struct arm_segment_info_type { enum mstate mapstate; @@ -200,12 +235,6 @@ struct arm_segment_info_type /* We want .cfi_* pseudo-ops for generating unwind info. */ #define TARGET_USE_CFIPOP 1 -/* The lr register is r14. */ -#define DWARF2_DEFAULT_RETURN_COLUMN 14 - -/* Registers are generally saved at negative offsets to the CFA. */ -#define DWARF2_CIE_DATA_ALIGNMENT -4 - /* CFI hooks. */ #define tc_regname_to_dw2regnum tc_arm_regname_to_dw2regnum #define tc_cfi_frame_initial_instructions tc_arm_frame_initial_instructions @@ -244,5 +273,14 @@ extern void arm_init_frag (struct frag *); extern void arm_handle_align (struct frag *); extern bfd_boolean arm_fix_adjustable (struct fix *); extern int arm_elf_section_type (const char *, size_t); -extern int tc_arm_regname_to_dw2regnum (const char *regname); +extern int tc_arm_regname_to_dw2regnum (char *regname); extern void tc_arm_frame_initial_instructions (void); + +#ifdef TE_PE + +#define O_secrel O_md1 + +#define TC_DWARF2_EMIT_OFFSET tc_pe_dwarf2_emit_offset +void tc_pe_dwarf2_emit_offset (symbolS *, unsigned int); + +#endif /* TE_PE */ diff --git a/contrib/binutils/gas/config/tc-cr16.c b/contrib/binutils/gas/config/tc-cr16.c new file mode 100644 index 0000000..2c4c6a4 --- /dev/null +++ b/contrib/binutils/gas/config/tc-cr16.c @@ -0,0 +1,2444 @@ +/* tc-cr16.c -- Assembler code for the CR16 CPU core. + Copyright 2007 Free Software Foundation, Inc. + + Contributed by M R Swami Reddy <MR.Swami.Reddy@nsc.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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "as.h" +#include "safe-ctype.h" +#include "dwarf2dbg.h" +#include "opcode/cr16.h" +#include "elf/cr16.h" + + +/* Word is considered here as a 16-bit unsigned short int. */ +#define WORD_SHIFT 16 + +/* Register is 2-byte size. */ +#define REG_SIZE 2 + +/* Maximum size of a single instruction (in words). */ +#define INSN_MAX_SIZE 3 + +/* Maximum bits which may be set in a `mask16' operand. */ +#define MAX_REGS_IN_MASK16 8 + +/* Assign a number NUM, shifted by SHIFT bytes, into a location + pointed by index BYTE of array 'output_opcode'. */ +#define CR16_PRINT(BYTE, NUM, SHIFT) output_opcode[BYTE] |= (NUM << SHIFT) + +/* Operand errors. */ +typedef enum + { + OP_LEGAL = 0, /* Legal operand. */ + OP_OUT_OF_RANGE, /* Operand not within permitted range. */ + OP_NOT_EVEN /* Operand is Odd number, should be even. */ + } +op_err; + +/* Opcode mnemonics hash table. */ +static struct hash_control *cr16_inst_hash; +/* CR16 registers hash table. */ +static struct hash_control *reg_hash; +/* CR16 register pair hash table. */ +static struct hash_control *regp_hash; +/* CR16 processor registers hash table. */ +static struct hash_control *preg_hash; +/* CR16 processor registers 32 bit hash table. */ +static struct hash_control *pregp_hash; +/* Current instruction we're assembling. */ +const inst *instruction; + + +static int code_label = 0; + +/* Global variables. */ + +/* Array to hold an instruction encoding. */ +long output_opcode[2]; + +/* Nonzero means a relocatable symbol. */ +int relocatable; + +/* A copy of the original instruction (used in error messages). */ +char ins_parse[MAX_INST_LEN]; + +/* The current processed argument number. */ +int cur_arg_num; + +/* Generic assembler global variables which must be defined by all targets. */ + +/* Characters which always start a comment. */ +const char comment_chars[] = "#"; + +/* Characters which start a comment at the beginning of a line. */ +const char line_comment_chars[] = "#"; + +/* This array holds machine specific line separator characters. */ +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 */ +const char FLT_CHARS[] = "f'"; + +/* Target-specific multicharacter options, not const-declared at usage. */ +const char *md_shortopts = ""; +struct option md_longopts[] = +{ + {NULL, no_argument, NULL, 0} +}; +size_t md_longopts_size = sizeof (md_longopts); + +static void +l_cons (int nbytes) +{ + int c; + expressionS exp; + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + + if (is_it_end_of_statement ()) + { + demand_empty_rest_of_line (); + return; + } + +#ifdef TC_ADDRESS_BYTES + if (nbytes == 0) + nbytes = TC_ADDRESS_BYTES (); +#endif + +#ifdef md_cons_align + md_cons_align (nbytes); +#endif + + c = 0; + do + { + unsigned int bits_available = BITS_PER_CHAR * nbytes; + char *hold = input_line_pointer; + + expression (&exp); + + if (*input_line_pointer == ':') + { + /* Bitfields. */ + long value = 0; + + for (;;) + { + unsigned long width; + + if (*input_line_pointer != ':') + { + input_line_pointer = hold; + break; + } + if (exp.X_op == O_absent) + { + as_warn (_("using a bit field width of zero")); + exp.X_add_number = 0; + exp.X_op = O_constant; + } + + if (exp.X_op != O_constant) + { + *input_line_pointer = '\0'; + as_bad (_("field width \"%s\" too complex for a bitfield"), hold); + *input_line_pointer = ':'; + demand_empty_rest_of_line (); + return; + } + + if ((width = exp.X_add_number) > + (unsigned int)(BITS_PER_CHAR * nbytes)) + { + as_warn (_("field width %lu too big to fit in %d bytes: truncated to %d bits"), width, nbytes, (BITS_PER_CHAR * nbytes)); + width = BITS_PER_CHAR * nbytes; + } /* Too big. */ + + + if (width > bits_available) + { + /* FIXME-SOMEDAY: backing up and reparsing is wasteful. */ + input_line_pointer = hold; + exp.X_add_number = value; + break; + } + + /* Skip ':'. */ + hold = ++input_line_pointer; + + expression (&exp); + if (exp.X_op != O_constant) + { + char cache = *input_line_pointer; + + *input_line_pointer = '\0'; + as_bad (_("field value \"%s\" too complex for a bitfield"), hold); + *input_line_pointer = cache; + demand_empty_rest_of_line (); + return; + } + + value |= ((~(-1 << width) & exp.X_add_number) + << ((BITS_PER_CHAR * nbytes) - bits_available)); + + if ((bits_available -= width) == 0 + || is_it_end_of_statement () + || *input_line_pointer != ',') + break; + + hold = ++input_line_pointer; + expression (&exp); + } + + exp.X_add_number = value; + exp.X_op = O_constant; + exp.X_unsigned = 1; + } + + if ((*(input_line_pointer) == '@') && (*(input_line_pointer +1) == 'c')) + code_label = 1; + emit_expr (&exp, (unsigned int) nbytes); + ++c; + if ((*(input_line_pointer) == '@') && (*(input_line_pointer +1) == 'c')) + { + input_line_pointer +=3; + break; + } + } + while ((*input_line_pointer++ == ',')); + + /* Put terminator back into stream. */ + input_line_pointer--; + + demand_empty_rest_of_line (); +} + + +/* This table describes all the machine specific pseudo-ops + the assembler has to support. The fields are: + *** Pseudo-op name without dot. + *** Function to call to execute this pseudo-op. + *** Integer arg to pass to the function. */ + +const pseudo_typeS md_pseudo_table[] = +{ + /* In CR16 machine, align is in bytes (not a ptwo boundary). */ + {"align", s_align_bytes, 0}, + {"long", l_cons, 4 }, + {0, 0, 0} +}; + +/* CR16 relaxation table. */ +const relax_typeS md_relax_table[] = +{ + /* bCC */ + {0xfa, -0x100, 2, 1}, /* 8 */ + {0xfffe, -0x10000, 4, 2}, /* 16 */ + {0xfffffe, -0x1000000, 6, 0}, /* 24 */ +}; + +/* Return the bit size for a given operand. */ + +static int +get_opbits (operand_type op) +{ + if (op < MAX_OPRD) + return cr16_optab[op].bit_size; + + return 0; +} + +/* Return the argument type of a given operand. */ + +static argtype +get_optype (operand_type op) +{ + if (op < MAX_OPRD) + return cr16_optab[op].arg_type; + else + return nullargs; +} + +/* Return the flags of a given operand. */ + +static int +get_opflags (operand_type op) +{ + if (op < MAX_OPRD) + return cr16_optab[op].flags; + + return 0; +} + +/* Get the cc code. */ + +static int +get_cc (char *cc_name) +{ + unsigned int i; + + for (i = 0; i < cr16_num_cc; i++) + if (strcmp (cc_name, cr16_b_cond_tab[i]) == 0) + return i; + + return -1; +} + +/* Get the core processor register 'reg_name'. */ + +static reg +get_register (char *reg_name) +{ + const reg_entry *reg; + + reg = (const reg_entry *) hash_find (reg_hash, reg_name); + + if (reg != NULL) + return reg->value.reg_val; + + return nullregister; +} +/* Get the core processor register-pair 'reg_name'. */ + +static reg +get_register_pair (char *reg_name) +{ + const reg_entry *reg; + char tmp_rp[16]="\0"; + + /* Add '(' and ')' to the reg pair, if its not present. */ + if (reg_name[0] != '(') + { + tmp_rp[0] = '('; + strcat (tmp_rp, reg_name); + strcat (tmp_rp,")"); + reg = (const reg_entry *) hash_find (regp_hash, tmp_rp); + } + else + reg = (const reg_entry *) hash_find (regp_hash, reg_name); + + if (reg != NULL) + return reg->value.reg_val; + + return nullregister; +} + +/* Get the index register 'reg_name'. */ + +static reg +get_index_register (char *reg_name) +{ + const reg_entry *reg; + + reg = (const reg_entry *) hash_find (reg_hash, reg_name); + + if ((reg != NULL) + && ((reg->value.reg_val == 12) || (reg->value.reg_val == 13))) + return reg->value.reg_val; + + return nullregister; +} +/* Get the core processor index register-pair 'reg_name'. */ + +static reg +get_index_register_pair (char *reg_name) +{ + const reg_entry *reg; + + reg = (const reg_entry *) hash_find (regp_hash, reg_name); + + if (reg != NULL) + { + if ((reg->value.reg_val != 1) || (reg->value.reg_val != 7) + || (reg->value.reg_val != 9) || (reg->value.reg_val > 10)) + return reg->value.reg_val; + + as_bad (_("Unknown register pair - index relative mode: `%d'"), reg->value.reg_val); + } + + return nullregister; +} + +/* Get the processor register 'preg_name'. */ + +static preg +get_pregister (char *preg_name) +{ + const reg_entry *preg; + + preg = (const reg_entry *) hash_find (preg_hash, preg_name); + + if (preg != NULL) + return preg->value.preg_val; + + return nullpregister; +} + +/* Get the processor register 'preg_name 32 bit'. */ + +static preg +get_pregisterp (char *preg_name) +{ + const reg_entry *preg; + + preg = (const reg_entry *) hash_find (pregp_hash, preg_name); + + if (preg != NULL) + return preg->value.preg_val; + + return nullpregister; +} + + +/* Round up a section size to the appropriate boundary. */ + +valueT +md_section_align (segT seg, valueT val) +{ + /* Round .text section to a multiple of 2. */ + if (seg == text_section) + return (val + 1) & ~1; + return val; +} + +/* Parse an operand that is machine-specific (remove '*'). */ + +void +md_operand (expressionS * exp) +{ + char c = *input_line_pointer; + + switch (c) + { + case '*': + input_line_pointer++; + expression (exp); + break; + default: + break; + } +} + +/* Reset global variables before parsing a new instruction. */ + +static void +reset_vars (char *op) +{ + cur_arg_num = relocatable = 0; + memset (& output_opcode, '\0', sizeof (output_opcode)); + + /* Save a copy of the original OP (used in error messages). */ + strncpy (ins_parse, op, sizeof ins_parse - 1); + ins_parse [sizeof ins_parse - 1] = 0; +} + +/* This macro decides whether a particular reloc is an entry in a + switch table. It is used when relaxing, because the linker needs + to know about all such entries so that it can adjust them if + necessary. */ + +#define SWITCH_TABLE(fix) \ + ( (fix)->fx_addsy != NULL \ + && (fix)->fx_subsy != NULL \ + && S_GET_SEGMENT ((fix)->fx_addsy) == \ + S_GET_SEGMENT ((fix)->fx_subsy) \ + && S_GET_SEGMENT (fix->fx_addsy) != undefined_section \ + && ( (fix)->fx_r_type == BFD_RELOC_CR16_NUM8 \ + || (fix)->fx_r_type == BFD_RELOC_CR16_NUM16 \ + || (fix)->fx_r_type == BFD_RELOC_CR16_NUM32 \ + || (fix)->fx_r_type == BFD_RELOC_CR16_NUM32a)) + +/* See whether we need to force a relocation into the output file. + This is used to force out switch and PC relative relocations when + relaxing. */ + +int +cr16_force_relocation (fixS *fix) +{ + /* REVISIT: Check if the "SWITCH_TABLE (fix)" should be added + if (generic_force_reloc (fix) || SWITCH_TABLE (fix)) */ + if (generic_force_reloc (fix)) + return 1; + + return 0; +} + +/* Record a fixup for a cons expression. */ + +void +cr16_cons_fix_new (fragS *frag, int offset, int len, expressionS *exp) +{ + int rtype; + switch (len) + { + default: rtype = BFD_RELOC_NONE; break; + case 1: rtype = BFD_RELOC_CR16_NUM8 ; break; + case 2: rtype = BFD_RELOC_CR16_NUM16; break; + case 4: + if (code_label) + { + rtype = BFD_RELOC_CR16_NUM32a; + code_label = 0; + } + else + rtype = BFD_RELOC_CR16_NUM32; + break; + } + + fix_new_exp (frag, offset, len, exp, 0, rtype); +} + +/* Generate a relocation entry for a fixup. */ + +arelent * +tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS * fixP) +{ + arelent * reloc; + + reloc = xmalloc (sizeof (arelent)); + reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *)); + *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy); + reloc->address = fixP->fx_frag->fr_address + fixP->fx_where; + reloc->addend = fixP->fx_offset; + + if (fixP->fx_subsy != NULL) + { + if (SWITCH_TABLE (fixP)) + { + /* Keep the current difference in the addend. */ + reloc->addend = (S_GET_VALUE (fixP->fx_addsy) + - S_GET_VALUE (fixP->fx_subsy) + fixP->fx_offset); + + switch (fixP->fx_r_type) + { + case BFD_RELOC_CR16_NUM8: + fixP->fx_r_type = BFD_RELOC_CR16_NUM8; + break; + case BFD_RELOC_CR16_NUM16: + fixP->fx_r_type = BFD_RELOC_CR16_NUM16; + break; + case BFD_RELOC_CR16_NUM32: + fixP->fx_r_type = BFD_RELOC_CR16_NUM32; + break; + case BFD_RELOC_CR16_NUM32a: + fixP->fx_r_type = BFD_RELOC_CR16_NUM32a; + break; + default: + abort (); + break; + } + } + else + { + /* We only resolve difference expressions in the same section. */ + as_bad_where (fixP->fx_file, fixP->fx_line, + _("can't resolve `%s' {%s section} - `%s' {%s section}"), + fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : "0", + segment_name (fixP->fx_addsy + ? S_GET_SEGMENT (fixP->fx_addsy) + : absolute_section), + S_GET_NAME (fixP->fx_subsy), + segment_name (S_GET_SEGMENT (fixP->fx_addsy))); + } + } + + assert ((int) fixP->fx_r_type > 0); + reloc->howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type); + + if (reloc->howto == NULL) + { + as_bad_where (fixP->fx_file, fixP->fx_line, + _("internal error: reloc %d (`%s') not supported by object file format"), + fixP->fx_r_type, + bfd_get_reloc_code_name (fixP->fx_r_type)); + return NULL; + } + assert (!fixP->fx_pcrel == !reloc->howto->pc_relative); + + return reloc; +} + +/* Prepare machine-dependent frags for relaxation. */ + +int +md_estimate_size_before_relax (fragS *fragp, asection *seg) +{ + /* If symbol is undefined or located in a different section, + select the largest supported relocation. */ + relax_substateT subtype; + relax_substateT rlx_state[] = {0, 2}; + + for (subtype = 0; subtype < ARRAY_SIZE (rlx_state); subtype += 2) + { + if (fragp->fr_subtype == rlx_state[subtype] + && (!S_IS_DEFINED (fragp->fr_symbol) + || seg != S_GET_SEGMENT (fragp->fr_symbol))) + { + fragp->fr_subtype = rlx_state[subtype + 1]; + break; + } + } + + if (fragp->fr_subtype >= ARRAY_SIZE (md_relax_table)) + abort (); + + return md_relax_table[fragp->fr_subtype].rlx_length; +} + +void +md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, fragS *fragP) +{ + /* 'opcode' points to the start of the instruction, whether + we need to change the instruction's fixed encoding. */ + bfd_reloc_code_real_type reloc = BFD_RELOC_NONE; + + subseg_change (sec, 0); + + fix_new (fragP, fragP->fr_fix, + bfd_get_reloc_size (bfd_reloc_type_lookup (stdoutput, reloc)), + fragP->fr_symbol, fragP->fr_offset, 1, reloc); + fragP->fr_var = 0; + fragP->fr_fix += md_relax_table[fragP->fr_subtype].rlx_length; +} + +/* Process machine-dependent command line options. Called once for + each option on the command line that the machine-independent part of + GAS does not understand. */ + +int +md_parse_option (int c ATTRIBUTE_UNUSED, char *arg ATTRIBUTE_UNUSED) +{ + return 0; +} + +/* Machine-dependent usage-output. */ + +void +md_show_usage (FILE *stream ATTRIBUTE_UNUSED) +{ + return; +} + +/* 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) +{ + int prec; + int i; + LITTLENUM_TYPE words[4]; + char *t; + + switch (type) + { + case 'f': + prec = 2; + break; + + case 'd': + prec = 4; + break; + + default: + *sizeP = 0; + return _("bad call to md_atof"); + } + + t = atof_ieee (input_line_pointer, type, words); + if (t) + input_line_pointer = t; + + *sizeP = prec * 2; + + if (! target_big_endian) + { + for (i = prec - 1; i >= 0; i--) + { + md_number_to_chars (litP, (valueT) words[i], 2); + litP += 2; + } + } + else + { + for (i = 0; i < prec; i++) + { + md_number_to_chars (litP, (valueT) words[i], 2); + litP += 2; + } + } + + return NULL; +} + +/* Apply a fixS (fixup of an instruction or data that we didn't have + enough info to complete immediately) to the data in a frag. + Since linkrelax is nonzero and TC_LINKRELAX_FIXUP is defined to disable + relaxation of debug sections, this function is called only when + fixuping relocations of debug sections. */ + +void +md_apply_fix (fixS *fixP, valueT *valP, segT seg) +{ + valueT val = * valP; + char *buf = fixP->fx_frag->fr_literal + fixP->fx_where; + fixP->fx_offset = 0; + + switch (fixP->fx_r_type) + { + case BFD_RELOC_CR16_NUM8: + bfd_put_8 (stdoutput, (unsigned char) val, buf); + break; + case BFD_RELOC_CR16_NUM16: + bfd_put_16 (stdoutput, val, buf); + break; + case BFD_RELOC_CR16_NUM32: + bfd_put_32 (stdoutput, val, buf); + break; + case BFD_RELOC_CR16_NUM32a: + bfd_put_32 (stdoutput, val, buf); + break; + default: + /* We shouldn't ever get here because linkrelax is nonzero. */ + abort (); + break; + } + + fixP->fx_done = 0; + + if (fixP->fx_addsy == NULL + && fixP->fx_pcrel == 0) + fixP->fx_done = 1; + + if (fixP->fx_pcrel == 1 + && fixP->fx_addsy != NULL + && S_GET_SEGMENT (fixP->fx_addsy) == seg) + fixP->fx_done = 1; +} + +/* The location from which a PC relative jump should be calculated, + given a PC relative reloc. */ + +long +md_pcrel_from (fixS *fixp) +{ + return fixp->fx_frag->fr_address + fixp->fx_where; +} + +static void +initialise_reg_hash_table (struct hash_control ** hash_table, + const reg_entry * register_table, + const unsigned int num_entries) +{ + const reg_entry * reg; + const char *hashret; + + if ((* hash_table = hash_new ()) == NULL) + as_fatal (_("Virtual memory exhausted")); + + for (reg = register_table; + reg < (register_table + num_entries); + reg++) + { + hashret = hash_insert (* hash_table, reg->name, (char *) reg); + if (hashret) + as_fatal (_("Internal Error: Can't hash %s: %s"), + reg->name, hashret); + } +} + +/* This function is called once, at assembler startup time. This should + set up all the tables, etc that the MD part of the assembler needs. */ + +void +md_begin (void) +{ + int i = 0; + + /* Set up a hash table for the instructions. */ + if ((cr16_inst_hash = hash_new ()) == NULL) + as_fatal (_("Virtual memory exhausted")); + + while (cr16_instruction[i].mnemonic != NULL) + { + const char *hashret; + const char *mnemonic = cr16_instruction[i].mnemonic; + + hashret = hash_insert (cr16_inst_hash, mnemonic, + (char *)(cr16_instruction + i)); + + if (hashret != NULL && *hashret != '\0') + as_fatal (_("Can't hash `%s': %s\n"), cr16_instruction[i].mnemonic, + *hashret == 0 ? _("(unknown reason)") : hashret); + + /* Insert unique names into hash table. The CR16 instruction set + has many identical opcode names that have different opcodes based + on the operands. This hash table then provides a quick index to + the first opcode with a particular name in the opcode table. */ + do + { + ++i; + } + while (cr16_instruction[i].mnemonic != NULL + && streq (cr16_instruction[i].mnemonic, mnemonic)); + } + + /* Initialize reg_hash hash table. */ + initialise_reg_hash_table (& reg_hash, cr16_regtab, NUMREGS); + /* Initialize regp_hash hash table. */ + initialise_reg_hash_table (& regp_hash, cr16_regptab, NUMREGPS); + /* Initialize preg_hash hash table. */ + initialise_reg_hash_table (& preg_hash, cr16_pregtab, NUMPREGS); + /* Initialize pregp_hash hash table. */ + initialise_reg_hash_table (& pregp_hash, cr16_pregptab, NUMPREGPS); + + /* Set linkrelax here to avoid fixups in most sections. */ + linkrelax = 1; +} + +/* Process constants (immediate/absolute) + and labels (jump targets/Memory locations). */ + +static void +process_label_constant (char *str, ins * cr16_ins) +{ + char *saved_input_line_pointer; + int symbol_with_at = 0; + int symbol_with_s = 0; + int symbol_with_m = 0; + int symbol_with_l = 0; + argument *cur_arg = cr16_ins->arg + cur_arg_num; /* Current argument. */ + + saved_input_line_pointer = input_line_pointer; + input_line_pointer = str; + + expression (&cr16_ins->exp); + + switch (cr16_ins->exp.X_op) + { + case O_big: + case O_absent: + /* Missing or bad expr becomes absolute 0. */ + as_bad (_("missing or invalid displacement expression `%s' taken as 0"), + str); + cr16_ins->exp.X_op = O_constant; + cr16_ins->exp.X_add_number = 0; + cr16_ins->exp.X_add_symbol = NULL; + cr16_ins->exp.X_op_symbol = NULL; + /* Fall through. */ + + case O_constant: + cur_arg->X_op = O_constant; + cur_arg->constant = cr16_ins->exp.X_add_number; + break; + + case O_symbol: + case O_subtract: + case O_add: + cur_arg->X_op = O_symbol; + cr16_ins->rtype = BFD_RELOC_NONE; + relocatable = 1; + + if (strneq (input_line_pointer, "@c", 2)) + symbol_with_at = 1; + + if (strneq (input_line_pointer, "@l", 2) + || strneq (input_line_pointer, ":l", 2)) + symbol_with_l = 1; + + if (strneq (input_line_pointer, "@m", 2) + || strneq (input_line_pointer, ":m", 2)) + symbol_with_m = 1; + + if (strneq (input_line_pointer, "@s", 2) + || strneq (input_line_pointer, ":s", 2)) + symbol_with_s = 1; + + switch (cur_arg->type) + { + case arg_cr: + if (IS_INSN_TYPE (LD_STOR_INS) || IS_INSN_TYPE (CSTBIT_INS)) + { + if (cur_arg->size == 20) + cr16_ins->rtype = BFD_RELOC_CR16_REGREL20; + else + cr16_ins->rtype = BFD_RELOC_CR16_REGREL20a; + } + break; + + case arg_crp: + if (IS_INSN_TYPE (LD_STOR_INS) || IS_INSN_TYPE (CSTBIT_INS)) + switch (instruction->size) + { + case 1: + switch (cur_arg->size) + { + case 0: + cr16_ins->rtype = BFD_RELOC_CR16_REGREL0; + break; + case 4: + if (IS_INSN_MNEMONIC ("loadb") || IS_INSN_MNEMONIC ("storb")) + cr16_ins->rtype = BFD_RELOC_CR16_REGREL4; + else + cr16_ins->rtype = BFD_RELOC_CR16_REGREL4a; + break; + default: break; + } + break; + case 2: + cr16_ins->rtype = BFD_RELOC_CR16_REGREL16; + break; + case 3: + if (cur_arg->size == 20) + cr16_ins->rtype = BFD_RELOC_CR16_REGREL20; + else + cr16_ins->rtype = BFD_RELOC_CR16_REGREL20a; + break; + default: + break; + } + break; + + case arg_idxr: + if (IS_INSN_TYPE (LD_STOR_INS) || IS_INSN_TYPE (CSTBIT_INS)) + cr16_ins->rtype = BFD_RELOC_CR16_REGREL20; + break; + + case arg_idxrp: + if (IS_INSN_TYPE (LD_STOR_INS) || IS_INSN_TYPE (CSTBIT_INS)) + switch (instruction->size) + { + case 1: cr16_ins->rtype = BFD_RELOC_CR16_REGREL0; break; + case 2: cr16_ins->rtype = BFD_RELOC_CR16_REGREL14; break; + case 3: cr16_ins->rtype = BFD_RELOC_CR16_REGREL20; break; + default: break; + } + break; + + case arg_c: + if (IS_INSN_MNEMONIC ("bal")) + cr16_ins->rtype = BFD_RELOC_CR16_DISP24; + else if (IS_INSN_TYPE (BRANCH_INS)) + { + if (symbol_with_s) + cr16_ins->rtype = BFD_RELOC_CR16_DISP8; + else if (symbol_with_m) + cr16_ins->rtype = BFD_RELOC_CR16_DISP16; + else + cr16_ins->rtype = BFD_RELOC_CR16_DISP24; + } + else if (IS_INSN_TYPE (STOR_IMM_INS) || IS_INSN_TYPE (LD_STOR_INS) + || IS_INSN_TYPE (CSTBIT_INS)) + { + if (symbol_with_s) + as_bad (_("operand %d: illegal use expression: `%s`"), cur_arg_num + 1, str); + if (symbol_with_m) + cr16_ins->rtype = BFD_RELOC_CR16_ABS20; + else /* Default to (symbol_with_l) */ + cr16_ins->rtype = BFD_RELOC_CR16_ABS24; + } + else if (IS_INSN_TYPE (BRANCH_NEQ_INS)) + cr16_ins->rtype = BFD_RELOC_CR16_DISP4; + break; + + case arg_ic: + if (IS_INSN_TYPE (ARITH_INS)) + { + if (symbol_with_s) + cr16_ins->rtype = BFD_RELOC_CR16_IMM4; + else if (symbol_with_m) + cr16_ins->rtype = BFD_RELOC_CR16_IMM20; + else if (symbol_with_at) + cr16_ins->rtype = BFD_RELOC_CR16_IMM32a; + else /* Default to (symbol_with_l) */ + cr16_ins->rtype = BFD_RELOC_CR16_IMM32; + } + else if (IS_INSN_TYPE (ARITH_BYTE_INS)) + { + cr16_ins->rtype = BFD_RELOC_CR16_IMM16; + } + break; + default: + break; + } + break; + + default: + cur_arg->X_op = cr16_ins->exp.X_op; + break; + } + + input_line_pointer = saved_input_line_pointer; + return; +} + +/* Retrieve the opcode image of a given register. + If the register is illegal for the current instruction, + issue an error. */ + +static int +getreg_image (reg r) +{ + const reg_entry *reg; + char *reg_name; + int is_procreg = 0; /* Nonzero means argument should be processor reg. */ + + /* Check whether the register is in registers table. */ + if (r < MAX_REG) + reg = cr16_regtab + r; + else /* Register not found. */ + { + as_bad (_("Unknown register: `%d'"), r); + return 0; + } + + reg_name = reg->name; + +/* Issue a error message when register is illegal. */ +#define IMAGE_ERR \ + as_bad (_("Illegal register (`%s') in Instruction: `%s'"), \ + reg_name, ins_parse); \ + break; + + switch (reg->type) + { + case CR16_R_REGTYPE: + if (! is_procreg) + return reg->image; + else + IMAGE_ERR; + + case CR16_P_REGTYPE: + return reg->image; + break; + + default: + IMAGE_ERR; + } + + return 0; +} + +/* Parsing different types of operands + -> constants Immediate/Absolute/Relative numbers + -> Labels Relocatable symbols + -> (reg pair base) Register pair base + -> (rbase) Register base + -> disp(rbase) Register relative + -> [rinx]disp(reg pair) Register index with reg pair mode + -> disp(rbase,ridx,scl) Register index mode. */ + +static void +set_operand (char *operand, ins * cr16_ins) +{ + char *operandS; /* Pointer to start of sub-opearand. */ + char *operandE; /* Pointer to end of sub-opearand. */ + + argument *cur_arg = &cr16_ins->arg[cur_arg_num]; /* Current argument. */ + + /* Initialize pointers. */ + operandS = operandE = operand; + + switch (cur_arg->type) + { + case arg_ic: /* Case $0x18. */ + operandS++; + case arg_c: /* Case 0x18. */ + /* Set constant. */ + process_label_constant (operandS, cr16_ins); + + if (cur_arg->type != arg_ic) + cur_arg->type = arg_c; + break; + + case arg_icr: /* Case $0x18(r1). */ + operandS++; + case arg_cr: /* Case 0x18(r1). */ + /* Set displacement constant. */ + while (*operandE != '(') + operandE++; + *operandE = '\0'; + process_label_constant (operandS, cr16_ins); + operandS = operandE; + case arg_rbase: /* Case (r1) or (r1,r0). */ + operandS++; + /* Set register base. */ + while (*operandE != ')') + operandE++; + *operandE = '\0'; + if ((cur_arg->r = get_register (operandS)) == nullregister) + as_bad (_("Illegal register `%s' in Instruction `%s'"), + operandS, ins_parse); + + /* set the arg->rp, if reg is "r12" or "r13" or "14" or "15" */ + if ((cur_arg->type != arg_rbase) + && ((getreg_image (cur_arg->r) == 12) + || (getreg_image (cur_arg->r) == 13) + || (getreg_image (cur_arg->r) == 14) + || (getreg_image (cur_arg->r) == 15))) + { + cur_arg->type = arg_crp; + cur_arg->rp = cur_arg->r; + } + break; + + case arg_crp: /* Case 0x18(r1,r0). */ + /* Set displacement constant. */ + while (*operandE != '(') + operandE++; + *operandE = '\0'; + process_label_constant (operandS, cr16_ins); + operandS = operandE; + operandS++; + /* Set register pair base. */ + while (*operandE != ')') + operandE++; + *operandE = '\0'; + if ((cur_arg->rp = get_register_pair (operandS)) == nullregister) + as_bad (_("Illegal register pair `%s' in Instruction `%s'"), + operandS, ins_parse); + break; + + case arg_idxr: + /* Set register pair base. */ + if ((strchr (operandS,'(') != NULL)) + { + while ((*operandE != '(') && (! ISSPACE (*operandE))) + operandE++; + if ((cur_arg->rp = get_index_register_pair (operandE)) == nullregister) + as_bad (_("Illegal register pair `%s' in Instruction `%s'"), + operandS, ins_parse); + *operandE++ = '\0'; + cur_arg->type = arg_idxrp; + } + else + cur_arg->rp = -1; + + operandE = operandS; + /* Set displacement constant. */ + while (*operandE != ']') + operandE++; + process_label_constant (++operandE, cr16_ins); + *operandE++ = '\0'; + operandE = operandS; + + /* Set index register . */ + operandS = strchr (operandE,'['); + if (operandS != NULL) + { /* Eliminate '[', detach from rest of operand. */ + *operandS++ = '\0'; + + operandE = strchr (operandS, ']'); + + if (operandE == NULL) + as_bad (_("unmatched '['")); + else + { /* Eliminate ']' and make sure it was the last thing + in the string. */ + *operandE = '\0'; + if (*(operandE + 1) != '\0') + as_bad (_("garbage after index spec ignored")); + } + } + + if ((cur_arg->i_r = get_index_register (operandS)) == nullregister) + as_bad (_("Illegal register `%s' in Instruction `%s'"), + operandS, ins_parse); + *operandE = '\0'; + *operandS = '\0'; + break; + + default: + break; + } +} + +/* Parse a single operand. + operand - Current operand to parse. + cr16_ins - Current assembled instruction. */ + +static void +parse_operand (char *operand, ins * cr16_ins) +{ + int ret_val; + argument *cur_arg = cr16_ins->arg + cur_arg_num; /* Current argument. */ + + /* Initialize the type to NULL before parsing. */ + cur_arg->type = nullargs; + + /* Check whether this is a condition code . */ + if ((IS_INSN_MNEMONIC ("b")) && ((ret_val = get_cc (operand)) != -1)) + { + cur_arg->type = arg_cc; + cur_arg->cc = ret_val; + cur_arg->X_op = O_register; + return; + } + + /* Check whether this is a general processor register. */ + if ((ret_val = get_register (operand)) != nullregister) + { + cur_arg->type = arg_r; + cur_arg->r = ret_val; + cur_arg->X_op = 0; + return; + } + + /* Check whether this is a general processor register pair. */ + if ((operand[0] == '(') + && ((ret_val = get_register_pair (operand)) != nullregister)) + { + cur_arg->type = arg_rp; + cur_arg->rp = ret_val; + cur_arg->X_op = O_register; + return; + } + + /* Check whether the operand is a processor register. + For "lprd" and "sprd" instruction, only 32 bit + processor registers used. */ + if (!(IS_INSN_MNEMONIC ("lprd") || (IS_INSN_MNEMONIC ("sprd"))) + && ((ret_val = get_pregister (operand)) != nullpregister)) + { + cur_arg->type = arg_pr; + cur_arg->pr = ret_val; + cur_arg->X_op = O_register; + return; + } + + /* Check whether this is a processor register - 32 bit. */ + if ((ret_val = get_pregisterp (operand)) != nullpregister) + { + cur_arg->type = arg_prp; + cur_arg->prp = ret_val; + cur_arg->X_op = O_register; + return; + } + + /* Deal with special characters. */ + switch (operand[0]) + { + case '$': + if (strchr (operand, '(') != NULL) + cur_arg->type = arg_icr; + else + cur_arg->type = arg_ic; + goto set_params; + break; + + case '(': + cur_arg->type = arg_rbase; + goto set_params; + break; + + case '[': + cur_arg->type = arg_idxr; + goto set_params; + break; + + default: + break; + } + + if (strchr (operand, '(') != NULL) + { + if (strchr (operand, ',') != NULL + && (strchr (operand, ',') > strchr (operand, '('))) + cur_arg->type = arg_crp; + else + cur_arg->type = arg_cr; + } + else + cur_arg->type = arg_c; + +/* Parse an operand according to its type. */ + set_params: + cur_arg->constant = 0; + set_operand (operand, cr16_ins); +} + +/* Parse the various operands. Each operand is then analyzed to fillup + the fields in the cr16_ins data structure. */ + +static void +parse_operands (ins * cr16_ins, char *operands) +{ + char *operandS; /* Operands string. */ + char *operandH, *operandT; /* Single operand head/tail pointers. */ + int allocated = 0; /* Indicates a new operands string was allocated.*/ + char *operand[MAX_OPERANDS];/* Separating the operands. */ + int op_num = 0; /* Current operand number we are parsing. */ + int bracket_flag = 0; /* Indicates a bracket '(' was found. */ + int sq_bracket_flag = 0; /* Indicates a square bracket '[' was found. */ + + /* Preprocess the list of registers, if necessary. */ + operandS = operandH = operandT = operands; + + while (*operandT != '\0') + { + if (*operandT == ',' && bracket_flag != 1 && sq_bracket_flag != 1) + { + *operandT++ = '\0'; + operand[op_num++] = strdup (operandH); + operandH = operandT; + continue; + } + + if (*operandT == ' ') + as_bad (_("Illegal operands (whitespace): `%s'"), ins_parse); + + if (*operandT == '(') + bracket_flag = 1; + else if (*operandT == '[') + sq_bracket_flag = 1; + + if (*operandT == ')') + { + if (bracket_flag) + bracket_flag = 0; + else + as_fatal (_("Missing matching brackets : `%s'"), ins_parse); + } + else if (*operandT == ']') + { + if (sq_bracket_flag) + sq_bracket_flag = 0; + else + as_fatal (_("Missing matching brackets : `%s'"), ins_parse); + } + + if (bracket_flag == 1 && *operandT == ')') + bracket_flag = 0; + else if (sq_bracket_flag == 1 && *operandT == ']') + sq_bracket_flag = 0; + + operandT++; + } + + /* Adding the last operand. */ + operand[op_num++] = strdup (operandH); + cr16_ins->nargs = op_num; + + /* Verifying correct syntax of operands (all brackets should be closed). */ + if (bracket_flag || sq_bracket_flag) + as_fatal (_("Missing matching brackets : `%s'"), ins_parse); + + /* Now we parse each operand separately. */ + for (op_num = 0; op_num < cr16_ins->nargs; op_num++) + { + cur_arg_num = op_num; + parse_operand (operand[op_num], cr16_ins); + free (operand[op_num]); + } + + if (allocated) + free (operandS); +} + +/* Get the trap index in dispatch table, given its name. + This routine is used by assembling the 'excp' instruction. */ + +static int +gettrap (char *s) +{ + const trap_entry *trap; + + for (trap = cr16_traps; trap < (cr16_traps + NUMTRAPS); trap++) + if (strcasecmp (trap->name, s) == 0) + return trap->entry; + + /* To make compatable with CR16 4.1 tools, the below 3-lines of + * code added. Refer: Development Tracker item #123 */ + for (trap = cr16_traps; trap < (cr16_traps + NUMTRAPS); trap++) + if (trap->entry == (unsigned int) atoi (s)) + return trap->entry; + + as_bad (_("Unknown exception: `%s'"), s); + return 0; +} + +/* Top level module where instruction parsing starts. + cr16_ins - data structure holds some information. + operands - holds the operands part of the whole instruction. */ + +static void +parse_insn (ins *insn, char *operands) +{ + int i; + + /* Handle instructions with no operands. */ + for (i = 0; cr16_no_op_insn[i] != NULL; i++) + { + if (streq (cr16_no_op_insn[i], instruction->mnemonic)) + { + insn->nargs = 0; + return; + } + } + + /* Handle 'excp' instructions. */ + if (IS_INSN_MNEMONIC ("excp")) + { + insn->nargs = 1; + insn->arg[0].type = arg_ic; + insn->arg[0].constant = gettrap (operands); + insn->arg[0].X_op = O_constant; + return; + } + + if (operands != NULL) + parse_operands (insn, operands); +} + +/* bCC instruction requires special handling. */ +static char * +get_b_cc (char * op) +{ + unsigned int i; + char op1[5]; + + for (i = 1; i < strlen (op); i++) + op1[i-1] = op[i]; + + op1[i-1] = '\0'; + + for (i = 0; i < cr16_num_cc ; i++) + if (streq (op1, cr16_b_cond_tab[i])) + return (char *) cr16_b_cond_tab[i]; + + return NULL; +} + +/* bCC instruction requires special handling. */ +static int +is_bcc_insn (char * op) +{ + if (!(streq (op, "bal") || streq (op, "beq0b") || streq (op, "bnq0b") + || streq (op, "beq0w") || streq (op, "bnq0w"))) + if ((op[0] == 'b') && (get_b_cc (op) != NULL)) + return 1; + return 0; +} + +/* Cinv instruction requires special handling. */ + +static int +check_cinv_options (char * operand) +{ + char *p = operand; + int i_used = 0, u_used = 0, d_used = 0; + + while (*++p != ']') + { + if (*p == ',' || *p == ' ') + continue; + + else if (*p == 'i') + i_used = 1; + else if (*p == 'u') + u_used = 1; + else if (*p == 'd') + d_used = 1; + else + as_bad (_("Illegal `cinv' parameter: `%c'"), *p); + } + + return 0; +} + +/* Retrieve the opcode image of a given register pair. + If the register is illegal for the current instruction, + issue an error. */ + +static int +getregp_image (reg r) +{ + const reg_entry *reg; + char *reg_name; + + /* Check whether the register is in registers table. */ + if (r < MAX_REG) + reg = cr16_regptab + r; + /* Register not found. */ + else + { + as_bad (_("Unknown register pair: `%d'"), r); + return 0; + } + + reg_name = reg->name; + +/* Issue a error message when register pair is illegal. */ +#define RPAIR_IMAGE_ERR \ + as_bad (_("Illegal register pair (`%s') in Instruction: `%s'"), \ + reg_name, ins_parse); \ + break; + + switch (reg->type) + { + case CR16_RP_REGTYPE: + return reg->image; + default: + RPAIR_IMAGE_ERR; + } + + return 0; +} + +/* Retrieve the opcode image of a given index register pair. + If the register is illegal for the current instruction, + issue an error. */ + +static int +getidxregp_image (reg r) +{ + const reg_entry *reg; + char *reg_name; + + /* Check whether the register is in registers table. */ + if (r < MAX_REG) + reg = cr16_regptab + r; + /* Register not found. */ + else + { + as_bad (_("Unknown register pair: `%d'"), r); + return 0; + } + + reg_name = reg->name; + +/* Issue a error message when register pair is illegal. */ +#define IDX_RPAIR_IMAGE_ERR \ + as_bad (_("Illegal index register pair (`%s') in Instruction: `%s'"), \ + reg_name, ins_parse); \ + + if (reg->type == CR16_RP_REGTYPE) + { + switch (reg->image) + { + case 0: return 0; break; + case 2: return 1; break; + case 4: return 2; break; + case 6: return 3; break; + case 8: return 4; break; + case 10: return 5; break; + case 3: return 6; break; + case 5: return 7; break; + default: + break; + } + } + + IDX_RPAIR_IMAGE_ERR; + return 0; +} + +/* Retrieve the opcode image of a given processort register. + If the register is illegal for the current instruction, + issue an error. */ +static int +getprocreg_image (reg r) +{ + const reg_entry *reg; + char *reg_name; + + /* Check whether the register is in registers table. */ + if (r < MAX_PREG) + reg = &cr16_pregtab[r - MAX_REG]; + /* Register not found. */ + else + { + as_bad (_("Unknown processor register : `%d'"), r); + return 0; + } + + reg_name = reg->name; + +/* Issue a error message when register pair is illegal. */ +#define PROCREG_IMAGE_ERR \ + as_bad (_("Illegal processor register (`%s') in Instruction: `%s'"), \ + reg_name, ins_parse); \ + break; + + switch (reg->type) + { + case CR16_P_REGTYPE: + return reg->image; + default: + PROCREG_IMAGE_ERR; + } + + return 0; +} + +/* Retrieve the opcode image of a given processort register. + If the register is illegal for the current instruction, + issue an error. */ +static int +getprocregp_image (reg r) +{ + const reg_entry *reg; + char *reg_name; + int pregptab_disp = 0; + + /* Check whether the register is in registers table. */ + if (r < MAX_PREG) + { + r = r - MAX_REG; + switch (r) + { + case 4: pregptab_disp = 1; break; + case 6: pregptab_disp = 2; break; + case 8: + case 9: + case 10: + pregptab_disp = 3; break; + case 12: + pregptab_disp = 4; break; + case 14: + pregptab_disp = 5; break; + default: break; + } + reg = &cr16_pregptab[r - pregptab_disp]; + } + /* Register not found. */ + else + { + as_bad (_("Unknown processor register (32 bit) : `%d'"), r); + return 0; + } + + reg_name = reg->name; + +/* Issue a error message when register pair is illegal. */ +#define PROCREGP_IMAGE_ERR \ + as_bad (_("Illegal 32 bit - processor register (`%s') in Instruction: `%s'"),\ + reg_name, ins_parse); \ + break; + + switch (reg->type) + { + case CR16_P_REGTYPE: + return reg->image; + default: + PROCREGP_IMAGE_ERR; + } + + return 0; +} + +/* Routine used to represent integer X using NBITS bits. */ + +static long +getconstant (long x, int nbits) +{ + /* The following expression avoids overflow if + 'nbits' is the number of bits in 'bfd_vma'. */ + return (x & ((((1 << (nbits - 1)) - 1) << 1) | 1)); +} + +/* Print a constant value to 'output_opcode': + ARG holds the operand's type and value. + SHIFT represents the location of the operand to be print into. + NBITS determines the size (in bits) of the constant. */ + +static void +print_constant (int nbits, int shift, argument *arg) +{ + unsigned long mask = 0; + + long constant = getconstant (arg->constant, nbits); + + switch (nbits) + { + case 32: + case 28: + /* mask the upper part of the constant, that is, the bits + going to the lowest byte of output_opcode[0]. + The upper part of output_opcode[1] is always filled, + therefore it is always masked with 0xFFFF. */ + mask = (1 << (nbits - 16)) - 1; + /* Divide the constant between two consecutive words : + 0 1 2 3 + +---------+---------+---------+---------+ + | | X X X X | x X x X | | + +---------+---------+---------+---------+ + output_opcode[0] output_opcode[1] */ + + CR16_PRINT (0, (constant >> WORD_SHIFT) & mask, 0); + CR16_PRINT (1, (constant & 0xFFFF), WORD_SHIFT); + break; + + case 21: + if ((nbits == 21) && (IS_INSN_TYPE (LD_STOR_INS))) nbits = 20; + case 24: + case 22: + case 20: + /* mask the upper part of the constant, that is, the bits + going to the lowest byte of output_opcode[0]. + The upper part of output_opcode[1] is always filled, + therefore it is always masked with 0xFFFF. */ + mask = (1 << (nbits - 16)) - 1; + /* Divide the constant between two consecutive words : + 0 1 2 3 + +---------+---------+---------+---------+ + | | X X X X | - X - X | | + +---------+---------+---------+---------+ + output_opcode[0] output_opcode[1] */ + + if ((instruction->size > 2) && (shift == WORD_SHIFT)) + { + if (arg->type == arg_idxrp) + { + CR16_PRINT (0, ((constant >> WORD_SHIFT) & mask) << 8, 0); + CR16_PRINT (1, (constant & 0xFFFF), WORD_SHIFT); + } + else + { + CR16_PRINT (0, (((((constant >> WORD_SHIFT) & mask) << 8) & 0x0f00) | ((((constant >> WORD_SHIFT) & mask) >> 4) & 0xf)),0); + CR16_PRINT (1, (constant & 0xFFFF), WORD_SHIFT); + } + } + else + CR16_PRINT (0, constant, shift); + break; + + case 14: + if (arg->type == arg_idxrp) + { + if (instruction->size == 2) + { + CR16_PRINT (0, ((constant)&0xf), shift); // 0-3 bits + CR16_PRINT (0, ((constant>>4)&0x3), (shift+20)); // 4-5 bits + CR16_PRINT (0, ((constant>>6)&0x3), (shift+14)); // 6-7 bits + CR16_PRINT (0, ((constant>>8)&0x3f), (shift+8)); // 8-13 bits + } + else + CR16_PRINT (0, constant, shift); + } + break; + + case 16: + case 12: + /* When instruction size is 3 and 'shift' is 16, a 16-bit constant is + always filling the upper part of output_opcode[1]. If we mistakenly + write it to output_opcode[0], the constant prefix (that is, 'match') + will be overriden. + 0 1 2 3 + +---------+---------+---------+---------+ + | 'match' | | X X X X | | + +---------+---------+---------+---------+ + output_opcode[0] output_opcode[1] */ + + if ((instruction->size > 2) && (shift == WORD_SHIFT)) + CR16_PRINT (1, constant, WORD_SHIFT); + else + CR16_PRINT (0, constant, shift); + break; + + case 8: + CR16_PRINT (0, ((constant/2)&0xf), shift); + CR16_PRINT (0, ((constant/2)>>4), (shift+8)); + break; + + default: + CR16_PRINT (0, constant, shift); + break; + } +} + +/* Print an operand to 'output_opcode', which later on will be + printed to the object file: + ARG holds the operand's type, size and value. + SHIFT represents the printing location of operand. + NBITS determines the size (in bits) of a constant operand. */ + +static void +print_operand (int nbits, int shift, argument *arg) +{ + switch (arg->type) + { + case arg_cc: + CR16_PRINT (0, arg->cc, shift); + break; + + case arg_r: + CR16_PRINT (0, getreg_image (arg->r), shift); + break; + + case arg_rp: + CR16_PRINT (0, getregp_image (arg->rp), shift); + break; + + case arg_pr: + CR16_PRINT (0, getprocreg_image (arg->pr), shift); + break; + + case arg_prp: + CR16_PRINT (0, getprocregp_image (arg->prp), shift); + break; + + case arg_idxrp: + /* 16 12 8 6 0 + +-----------------------------+ + | r_index | disp | rp_base | + +-----------------------------+ */ + + if (instruction->size == 3) + { + CR16_PRINT (0, getidxregp_image (arg->rp), 0); + if (getreg_image (arg->i_r) == 12) + CR16_PRINT (0, 0, 3); + else + CR16_PRINT (0, 1, 3); + } + else + { + CR16_PRINT (0, getidxregp_image (arg->rp), 16); + if (getreg_image (arg->i_r) == 12) + CR16_PRINT (0, 0, 19); + else + CR16_PRINT (0, 1, 19); + } + print_constant (nbits, shift, arg); + break; + + case arg_idxr: + if (getreg_image (arg->i_r) == 12) + if (IS_INSN_MNEMONIC ("cbitb") || IS_INSN_MNEMONIC ("sbitb") + || IS_INSN_MNEMONIC ("tbitb")) + CR16_PRINT (0, 0, 23); + else CR16_PRINT (0, 0, 24); + else + if (IS_INSN_MNEMONIC ("cbitb") || IS_INSN_MNEMONIC ("sbitb") + || IS_INSN_MNEMONIC ("tbitb")) + CR16_PRINT (0, 1, 23); + else CR16_PRINT (0, 1, 24); + + print_constant (nbits, shift, arg); + break; + + case arg_ic: + case arg_c: + print_constant (nbits, shift, arg); + break; + + case arg_rbase: + CR16_PRINT (0, getreg_image (arg->r), shift); + break; + + case arg_cr: + print_constant (nbits, shift , arg); + /* Add the register argument to the output_opcode. */ + CR16_PRINT (0, getreg_image (arg->r), (shift+16)); + break; + + case arg_crp: + print_constant (nbits, shift , arg); + if (instruction->size > 1) + CR16_PRINT (0, getregp_image (arg->rp), (shift + 16)); + else if (IS_INSN_TYPE (LD_STOR_INS) || (IS_INSN_TYPE (CSTBIT_INS))) + { + if (instruction->size == 2) + CR16_PRINT (0, getregp_image (arg->rp), (shift - 8)); + else if (instruction->size == 1) + CR16_PRINT (0, getregp_image (arg->rp), 16); + } + else + CR16_PRINT (0, getregp_image (arg->rp), shift); + break; + + default: + break; + } +} + +/* Retrieve the number of operands for the current assembled instruction. */ + +static int +get_number_of_operands (void) +{ + int i; + + for (i = 0; instruction->operands[i].op_type && i < MAX_OPERANDS; i++) + ; + return i; +} + +/* Verify that the number NUM can be represented in BITS bits (that is, + within its permitted range), based on the instruction's FLAGS. + If UPDATE is nonzero, update the value of NUM if necessary. + Return OP_LEGAL upon success, actual error type upon failure. */ + +static op_err +check_range (long *num, int bits, int unsigned flags, int update) +{ + long min, max; + int retval = OP_LEGAL; + long value = *num; + + if (bits == 0 && value > 0) return OP_OUT_OF_RANGE; + + /* For hosts witah 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); + + + /* Verify operand value is even. */ + if (flags & OP_EVEN) + { + if (value % 2) + return OP_NOT_EVEN; + } + + if (flags & OP_DEC) + { + value -= 1; + if (update) + *num = value; + } + + if (flags & OP_SHIFT) + { + value >>= 1; + if (update) + *num = value; + } + else if (flags & OP_SHIFT_DEC) + { + value = (value >> 1) - 1; + if (update) + *num = value; + } + + if (flags & OP_ABS20) + { + if (value > 0xEFFFF) + return OP_OUT_OF_RANGE; + } + + if (flags & OP_ESC) + { + if (value == 0xB || value == 0x9) + return OP_OUT_OF_RANGE; + else if (value == -1) + { + if (update) + *num = 9; + return retval; + } + } + + if (flags & OP_ESC1) + { + if (value > 13) + return OP_OUT_OF_RANGE; + } + + if (flags & OP_SIGNED) + { + max = (1 << (bits - 1)) - 1; + min = - (1 << (bits - 1)); + if ((value > max) || (value < min)) + retval = OP_OUT_OF_RANGE; + } + else if (flags & OP_UNSIGNED) + { + max = ((((1 << (bits - 1)) - 1) << 1) | 1); + min = 0; + if (((unsigned long) value > (unsigned long) max) + || ((unsigned long) value < (unsigned long) min)) + retval = OP_OUT_OF_RANGE; + } + else if (flags & OP_NEG) + { + max = - 1; + min = - ((1 << (bits - 1))-1); + if ((value > max) || (value < min)) + retval = OP_OUT_OF_RANGE; + } + return retval; +} + +/* Bunch of error checkings. + The checks are made after a matching instruction was found. */ + +static void +warn_if_needed (ins *insn) +{ + /* If the post-increment address mode is used and the load/store + source register is the same as rbase, the result of the + instruction is undefined. */ + if (IS_INSN_TYPE (LD_STOR_INS_INC)) + { + /* Enough to verify that one of the arguments is a simple reg. */ + if ((insn->arg[0].type == arg_r) || (insn->arg[1].type == arg_r)) + if (insn->arg[0].r == insn->arg[1].r) + as_bad (_("Same src/dest register is used (`r%d'), result is undefined"), insn->arg[0].r); + } + + if (IS_INSN_MNEMONIC ("pop") + || IS_INSN_MNEMONIC ("push") + || IS_INSN_MNEMONIC ("popret")) + { + unsigned int count = insn->arg[0].constant, reg_val; + + /* Check if count operand caused to save/retrive the RA twice + to generate warning message. */ + if (insn->nargs > 2) + { + reg_val = getreg_image (insn->arg[1].r); + + if ( ((reg_val == 9) && (count > 7)) + || ((reg_val == 10) && (count > 6)) + || ((reg_val == 11) && (count > 5)) + || ((reg_val == 12) && (count > 4)) + || ((reg_val == 13) && (count > 2)) + || ((reg_val == 14) && (count > 0))) + as_warn (_("RA register is saved twice.")); + + /* Check if the third operand is "RA" or "ra" */ + if (!(((insn->arg[2].r) == ra) || ((insn->arg[2].r) == RA))) + as_bad (_("`%s' Illegal use of registers."), ins_parse); + } + + if (insn->nargs > 1) + { + reg_val = getreg_image (insn->arg[1].r); + + /* If register is a register pair ie r12/r13/r14 in operand1, then + the count constant should be validated. */ + if (((reg_val == 11) && (count > 7)) + || ((reg_val == 12) && (count > 6)) + || ((reg_val == 13) && (count > 4)) + || ((reg_val == 14) && (count > 2)) + || ((reg_val == 15) && (count > 0))) + as_bad (_("`%s' Illegal count-register combination."), ins_parse); + } + else + { + /* Check if the operand is "RA" or "ra" */ + if (!(((insn->arg[0].r) == ra) || ((insn->arg[0].r) == RA))) + as_bad (_("`%s' Illegal use of register."), ins_parse); + } + } + + /* Some instruction assume the stack pointer as rptr operand. + Issue an error when the register to be loaded is also SP. */ + if (instruction->flags & NO_SP) + { + if (getreg_image (insn->arg[1].r) == getreg_image (sp)) + as_bad (_("`%s' has undefined result"), ins_parse); + } + + /* If the rptr register is specified as one of the registers to be loaded, + the final contents of rptr are undefined. Thus, we issue an error. */ + if (instruction->flags & NO_RPTR) + { + if ((1 << getreg_image (insn->arg[0].r)) & insn->arg[1].constant) + as_bad (_("Same src/dest register is used (`r%d'),result is undefined"), + getreg_image (insn->arg[0].r)); + } +} + +/* In some cases, we need to adjust the instruction pointer although a + match was already found. Here, we gather all these cases. + Returns 1 if instruction pointer was adjusted, otherwise 0. */ + +static int +adjust_if_needed (ins *insn ATTRIBUTE_UNUSED) +{ + int ret_value = 0; + + if ((IS_INSN_TYPE (CSTBIT_INS)) || (IS_INSN_TYPE (LD_STOR_INS))) + { + if ((instruction->operands[0].op_type == abs24) + && ((insn->arg[0].constant) > 0xF00000)) + { + insn->arg[0].constant &= 0xFFFFF; + instruction--; + ret_value = 1; + } + } + + return ret_value; +} + +/* Assemble a single instruction: + INSN is already parsed (that is, all operand values and types are set). + For instruction to be assembled, we need to find an appropriate template in + the instruction table, meeting the following conditions: + 1: Has the same number of operands. + 2: Has the same operand types. + 3: Each operand size is sufficient to represent the instruction's values. + Returns 1 upon success, 0 upon failure. */ + +static int +assemble_insn (char *mnemonic, ins *insn) +{ + /* Type of each operand in the current template. */ + argtype cur_type[MAX_OPERANDS]; + /* Size (in bits) of each operand in the current template. */ + unsigned int cur_size[MAX_OPERANDS]; + /* Flags of each operand in the current template. */ + unsigned int cur_flags[MAX_OPERANDS]; + /* Instruction type to match. */ + unsigned int ins_type; + /* Boolean flag to mark whether a match was found. */ + int match = 0; + int i; + /* Nonzero if an instruction with same number of operands was found. */ + int found_same_number_of_operands = 0; + /* Nonzero if an instruction with same argument types was found. */ + int found_same_argument_types = 0; + /* Nonzero if a constant was found within the required range. */ + int found_const_within_range = 0; + /* Argument number of an operand with invalid type. */ + int invalid_optype = -1; + /* Argument number of an operand with invalid constant value. */ + int invalid_const = -1; + /* Operand error (used for issuing various constant error messages). */ + op_err op_error, const_err = OP_LEGAL; + +/* Retrieve data (based on FUNC) for each operand of a given instruction. */ +#define GET_CURRENT_DATA(FUNC, ARRAY) \ + for (i = 0; i < insn->nargs; i++) \ + ARRAY[i] = FUNC (instruction->operands[i].op_type) + +#define GET_CURRENT_TYPE GET_CURRENT_DATA (get_optype, cur_type) +#define GET_CURRENT_SIZE GET_CURRENT_DATA (get_opbits, cur_size) +#define GET_CURRENT_FLAGS GET_CURRENT_DATA (get_opflags, cur_flags) + + /* Instruction has no operands -> only copy the constant opcode. */ + if (insn->nargs == 0) + { + output_opcode[0] = BIN (instruction->match, instruction->match_bits); + return 1; + } + + /* In some case, same mnemonic can appear with different instruction types. + For example, 'storb' is supported with 3 different types : + LD_STOR_INS, LD_STOR_INS_INC, STOR_IMM_INS. + We assume that when reaching this point, the instruction type was + pre-determined. We need to make sure that the type stays the same + during a search for matching instruction. */ + ins_type = CR16_INS_TYPE (instruction->flags); + + while (/* Check that match is still not found. */ + match != 1 + /* Check we didn't get to end of table. */ + && instruction->mnemonic != NULL + /* Check that the actual mnemonic is still available. */ + && IS_INSN_MNEMONIC (mnemonic) + /* Check that the instruction type wasn't changed. */ + && IS_INSN_TYPE (ins_type)) + { + /* Check whether number of arguments is legal. */ + if (get_number_of_operands () != insn->nargs) + goto next_insn; + found_same_number_of_operands = 1; + + /* Initialize arrays with data of each operand in current template. */ + GET_CURRENT_TYPE; + GET_CURRENT_SIZE; + GET_CURRENT_FLAGS; + + /* Check for type compatibility. */ + for (i = 0; i < insn->nargs; i++) + { + if (cur_type[i] != insn->arg[i].type) + { + if (invalid_optype == -1) + invalid_optype = i + 1; + goto next_insn; + } + } + found_same_argument_types = 1; + + for (i = 0; i < insn->nargs; i++) + { + /* If 'bal' instruction size is '2' and reg operand is not 'ra' + then goto next instruction. */ + if (IS_INSN_MNEMONIC ("bal") && (i == 0) + && (instruction->size == 2) && (insn->arg[i].rp != 14)) + goto next_insn; + + /* If 'storb' instruction with 'sp' reg and 16-bit disp of + * reg-pair, leads to undifined trap, so this should use + * 20-bit disp of reg-pair. */ + if (IS_INSN_MNEMONIC ("storb") && (instruction->size == 2) + && (insn->arg[i].r == 15) && (insn->arg[i + 1].type == arg_crp)) + goto next_insn; + + /* Only check range - don't update the constant's value, since the + current instruction may not be the last we try to match. + The constant's value will be updated later, right before printing + it to the object file. */ + if ((insn->arg[i].X_op == O_constant) + && (op_error = check_range (&insn->arg[i].constant, cur_size[i], + cur_flags[i], 0))) + { + if (invalid_const == -1) + { + invalid_const = i + 1; + const_err = op_error; + } + goto next_insn; + } + /* For symbols, we make sure the relocation size (which was already + determined) is sufficient. */ + else if ((insn->arg[i].X_op == O_symbol) + && ((bfd_reloc_type_lookup (stdoutput, insn->rtype))->bitsize + > cur_size[i])) + goto next_insn; + } + found_const_within_range = 1; + + /* If we got till here -> Full match is found. */ + match = 1; + break; + +/* Try again with next instruction. */ +next_insn: + instruction++; + } + + if (!match) + { + /* We haven't found a match - instruction can't be assembled. */ + if (!found_same_number_of_operands) + as_bad (_("Incorrect number of operands")); + else if (!found_same_argument_types) + as_bad (_("Illegal type of operand (arg %d)"), invalid_optype); + else if (!found_const_within_range) + { + switch (const_err) + { + case OP_OUT_OF_RANGE: + as_bad (_("Operand out of range (arg %d)"), invalid_const); + break; + case OP_NOT_EVEN: + as_bad (_("Operand has odd displacement (arg %d)"), invalid_const); + break; + default: + as_bad (_("Illegal operand (arg %d)"), invalid_const); + break; + } + } + + return 0; + } + else + /* Full match - print the encoding to output file. */ + { + /* Make further checkings (such that couldn't be made earlier). + Warn the user if necessary. */ + warn_if_needed (insn); + + /* Check whether we need to adjust the instruction pointer. */ + if (adjust_if_needed (insn)) + /* If instruction pointer was adjusted, we need to update + the size of the current template operands. */ + GET_CURRENT_SIZE; + + for (i = 0; i < insn->nargs; i++) + { + int j = instruction->flags & REVERSE_MATCH ? + i == 0 ? 1 : + i == 1 ? 0 : i : + i; + + /* This time, update constant value before printing it. */ + if ((insn->arg[j].X_op == O_constant) + && (check_range (&insn->arg[j].constant, cur_size[j], + cur_flags[j], 1) != OP_LEGAL)) + as_fatal (_("Illegal operand (arg %d)"), j+1); + } + + /* First, copy the instruction's opcode. */ + output_opcode[0] = BIN (instruction->match, instruction->match_bits); + + for (i = 0; i < insn->nargs; i++) + { + /* For BAL (ra),disp17 instuction only. And also set the + DISP24a relocation type. */ + if (IS_INSN_MNEMONIC ("bal") && (instruction->size == 2) && i == 0) + { + insn->rtype = BFD_RELOC_CR16_DISP24a; + continue; + } + cur_arg_num = i; + print_operand (cur_size[i], instruction->operands[i].shift, + &insn->arg[i]); + } + } + + return 1; +} + +/* Print the instruction. + Handle also cases where the instruction is relaxable/relocatable. */ + +static void +print_insn (ins *insn) +{ + unsigned int i, j, insn_size; + char *this_frag; + unsigned short words[4]; + int addr_mod; + + /* Arrange the insn encodings in a WORD size array. */ + for (i = 0, j = 0; i < 2; i++) + { + words[j++] = (output_opcode[i] >> 16) & 0xFFFF; + words[j++] = output_opcode[i] & 0xFFFF; + } + + insn_size = instruction->size; + this_frag = frag_more (insn_size * 2); + + /* Handle relocation. */ + if ((relocatable) && (insn->rtype != BFD_RELOC_NONE)) + { + reloc_howto_type *reloc_howto; + int size; + + reloc_howto = bfd_reloc_type_lookup (stdoutput, insn->rtype); + + if (!reloc_howto) + abort (); + + size = bfd_get_reloc_size (reloc_howto); + + if (size < 1 || size > 4) + abort (); + + fix_new_exp (frag_now, this_frag - frag_now->fr_literal, + size, &insn->exp, reloc_howto->pc_relative, + insn->rtype); + } + + /* Verify a 2-byte code alignment. */ + addr_mod = frag_now_fix () & 1; + if (frag_now->has_code && frag_now->insn_addr != addr_mod) + as_bad (_("instruction address is not a multiple of 2")); + frag_now->insn_addr = addr_mod; + frag_now->has_code = 1; + + /* Write the instruction encoding to frag. */ + for (i = 0; i < insn_size; i++) + { + md_number_to_chars (this_frag, (valueT) words[i], 2); + this_frag += 2; + } +} + +/* This is the guts of the machine-dependent assembler. OP points to a + machine dependent instruction. This function is supposed to emit + the frags/bytes it assembles to. */ + +void +md_assemble (char *op) +{ + ins cr16_ins; + char *param, param1[32]; + char c; + + /* Reset global variables for a new instruction. */ + reset_vars (op); + + /* Strip the mnemonic. */ + for (param = op; *param != 0 && !ISSPACE (*param); param++) + ; + c = *param; + *param++ = '\0'; + + /* bCC instuctions and adjust the mnemonic by adding extra white spaces. */ + if (is_bcc_insn (op)) + { + strcpy (param1, get_b_cc (op)); + op = "b"; + strcat (param1,","); + strcat (param1, param); + param = (char *) ¶m1; + } + + /* Checking the cinv options and adjust the mnemonic by removing the + extra white spaces. */ + if (streq ("cinv", op)) + { + /* Validate the cinv options. */ + check_cinv_options (param); + strcat (op, param); + } + + /* MAPPING - SHIFT INSN, if imm4/imm16 positive values + lsh[b/w] imm4/imm6, reg ==> ashu[b/w] imm4/imm16, reg + as CR16 core doesn't support lsh[b/w] right shift operaions. */ + if ((streq ("lshb", op) || streq ("lshw", op) || streq ("lshd", op)) + && (param [0] == '$')) + { + strcpy (param1, param); + /* Find the instruction. */ + instruction = (const inst *) hash_find (cr16_inst_hash, op); + parse_operands (&cr16_ins, param1); + if (((&cr16_ins)->arg[0].type == arg_ic) + && ((&cr16_ins)->arg[0].constant >= 0)) + { + if (streq ("lshb", op)) + op = "ashub"; + else if (streq ("lshd", op)) + op = "ashud"; + else + op = "ashuw"; + } + } + + /* Find the instruction. */ + instruction = (const inst *) hash_find (cr16_inst_hash, op); + if (instruction == NULL) + { + as_bad (_("Unknown opcode: `%s'"), op); + return; + } + + /* Tie dwarf2 debug info to the address at the start of the insn. */ + dwarf2_emit_insn (0); + + /* Parse the instruction's operands. */ + parse_insn (&cr16_ins, param); + + /* Assemble the instruction - return upon failure. */ + if (assemble_insn (op, &cr16_ins) == 0) + return; + + /* Print the instruction. */ + print_insn (&cr16_ins); +} diff --git a/contrib/binutils/gas/config/tc-cr16.h b/contrib/binutils/gas/config/tc-cr16.h new file mode 100644 index 0000000..6d06151 --- /dev/null +++ b/contrib/binutils/gas/config/tc-cr16.h @@ -0,0 +1,73 @@ +/* tc-cr16.h -- Header file for tc-cr16.c, the CR16 GAS port. + Copyright 2007 Free Software Foundation, Inc. + + Contributed by M R Swami Reddy <MR.Swami.Reddy@nsc.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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef TC_CR16_H +#define TC_CR16_H + +#define TC_CR16 1 + +#define TARGET_BYTES_BIG_ENDIAN 0 + +#define TARGET_FORMAT "elf32-cr16" +#define TARGET_ARCH bfd_arch_cr16 + +#define WORKING_DOT_WORD +#define LOCAL_LABEL_PREFIX '.' + +#define md_undefined_symbol(s) 0 +#define md_number_to_chars number_to_chars_littleendian + +/* We do relaxing in the assembler as well as the linker. */ +extern const struct relax_type md_relax_table[]; +#define TC_GENERIC_RELAX_TABLE md_relax_table + +/* We do not want to adjust any relocations to make implementation of + linker relaxations easier. */ +#define tc_fix_adjustable(fixP) 0 + +/* We need to force out some relocations when relaxing. */ +#define TC_FORCE_RELOCATION(FIXP) cr16_force_relocation (FIXP) +extern int cr16_force_relocation (struct fix *); + +/* Fixup debug sections since we will never relax them. */ +#define TC_LINKRELAX_FIXUP(seg) (seg->flags & SEC_ALLOC) + +/* CR16 instructions, with operands included, are a multiple + of two bytes long. */ +#define DWARF2_LINE_MIN_INSN_LENGTH 2 + +extern void cr16_cons_fix_new (struct frag *, int, int, struct expressionS *); +/* This is called by emit_expr when creating a reloc for a cons. + We could use the definition there, except that we want to handle + the CR16 reloc type specially, rather than the BFD_RELOC type. */ +#define TC_CONS_FIX_NEW(FRAG, OFF, LEN, EXP) \ + cr16_cons_fix_new (FRAG, OFF, LEN, EXP) + +/* Give an error if a frag containing code is not aligned to a 2-byte + boundary. */ +#define md_frag_check(FRAGP) \ + if ((FRAGP)->has_code \ + && (((FRAGP)->fr_address + (FRAGP)->insn_addr) & 1) != 0) \ + as_bad_where ((FRAGP)->fr_file, (FRAGP)->fr_line, \ + _("instruction address is not a multiple of 2")); + +#endif /* TC_CR16_H */ diff --git a/contrib/binutils/gas/config/tc-i386.c b/contrib/binutils/gas/config/tc-i386.c index be384bc..296fdcd 100644 --- a/contrib/binutils/gas/config/tc-i386.c +++ b/contrib/binutils/gas/config/tc-i386.c @@ -1,6 +1,6 @@ -/* i386.c -- Assemble code for the Intel 80386 +/* tc-i386.c -- Assemble code for the Intel 80386 Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, - 2000, 2001, 2002, 2003, 2004, 2005, 2006 + 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GAS, the GNU Assembler. @@ -32,7 +32,6 @@ #include "subsegs.h" #include "dwarf2dbg.h" #include "dw2gencfi.h" -#include "opcode/i386.h" #include "elf/x86-64.h" #ifndef REGISTER_WARNINGS @@ -63,54 +62,39 @@ #endif #endif -static INLINE unsigned int mode_from_disp_size PARAMS ((unsigned int)); -static INLINE int fits_in_signed_byte PARAMS ((offsetT)); -static INLINE int fits_in_unsigned_byte PARAMS ((offsetT)); -static INLINE int fits_in_unsigned_word PARAMS ((offsetT)); -static INLINE int fits_in_signed_word PARAMS ((offsetT)); -static INLINE int fits_in_unsigned_long PARAMS ((offsetT)); -static INLINE int fits_in_signed_long PARAMS ((offsetT)); -static int smallest_imm_type PARAMS ((offsetT)); -static offsetT offset_in_range PARAMS ((offsetT, int)); -static int add_prefix PARAMS ((unsigned int)); -static void set_code_flag PARAMS ((int)); -static void set_16bit_gcc_code_flag PARAMS ((int)); -static void set_intel_syntax PARAMS ((int)); -static void set_cpu_arch PARAMS ((int)); +static void set_code_flag (int); +static void set_16bit_gcc_code_flag (int); +static void set_intel_syntax (int); +static void set_cpu_arch (int); #ifdef TE_PE -static void pe_directive_secrel PARAMS ((int)); +static void pe_directive_secrel (int); #endif -static void signed_cons PARAMS ((int)); -static char *output_invalid PARAMS ((int c)); -static int i386_operand PARAMS ((char *operand_string)); -static int i386_intel_operand PARAMS ((char *operand_string, int got_a_float)); -static const reg_entry *parse_register PARAMS ((char *reg_string, - char **end_op)); -static char *parse_insn PARAMS ((char *, char *)); -static char *parse_operands PARAMS ((char *, const char *)); -static void swap_operands PARAMS ((void)); -static void optimize_imm PARAMS ((void)); -static void optimize_disp PARAMS ((void)); -static int match_template PARAMS ((void)); -static int check_string PARAMS ((void)); -static int process_suffix PARAMS ((void)); -static int check_byte_reg PARAMS ((void)); -static int check_long_reg PARAMS ((void)); -static int check_qword_reg PARAMS ((void)); -static int check_word_reg PARAMS ((void)); -static int finalize_imm PARAMS ((void)); -static int process_operands PARAMS ((void)); -static const seg_entry *build_modrm_byte PARAMS ((void)); -static void output_insn PARAMS ((void)); -static void output_branch PARAMS ((void)); -static void output_jump PARAMS ((void)); -static void output_interseg_jump PARAMS ((void)); -static void output_imm PARAMS ((fragS *insn_start_frag, - offsetT insn_start_off)); -static void output_disp PARAMS ((fragS *insn_start_frag, - offsetT insn_start_off)); +static void signed_cons (int); +static char *output_invalid (int c); +static int i386_operand (char *); +static int i386_intel_operand (char *, int); +static const reg_entry *parse_register (char *, char **); +static char *parse_insn (char *, char *); +static char *parse_operands (char *, const char *); +static void swap_operands (void); +static void swap_2_operands (int, int); +static void optimize_imm (void); +static void optimize_disp (void); +static int match_template (void); +static int check_string (void); +static int process_suffix (void); +static int check_byte_reg (void); +static int check_long_reg (void); +static int check_qword_reg (void); +static int check_word_reg (void); +static int finalize_imm (void); +static int process_operands (void); +static const seg_entry *build_modrm_byte (void); +static void output_insn (void); +static void output_imm (fragS *, offsetT); +static void output_disp (fragS *, offsetT); #ifndef I386COFF -static void s_bss PARAMS ((int)); +static void s_bss (int); #endif #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) static void handle_large_common (int small ATTRIBUTE_UNUSED); @@ -271,8 +255,9 @@ static i386_insn i; /* Possible templates for current insn. */ static const templates *current_templates; -/* Per instruction expressionS buffers: 2 displacements & 2 immediate max. */ -static expressionS disp_expressions[2], im_expressions[2]; +/* Per instruction expressionS buffers: max displacements & immediates. */ +static expressionS disp_expressions[MAX_MEMORY_OPERANDS]; +static expressionS im_expressions[MAX_IMMEDIATE_OPERANDS]; /* Current operand we are working on. */ static int this_operand; @@ -305,6 +290,9 @@ static int intel_syntax = 0; /* 1 if register prefix % not required. */ static int allow_naked_reg = 0; +/* Register prefix used for error message. */ +static const char *register_prefix = "%"; + /* Used in 16 bit gcc mode to add an l suffix to call, ret, enter, leave, push, and pop instructions so that gcc has the same stack frame as in 32 bit mode. */ @@ -323,6 +311,21 @@ static const char *cpu_sub_arch_name = NULL; /* CPU feature flags. */ static unsigned int cpu_arch_flags = CpuUnknownFlags | CpuNo64; +/* If we have selected a cpu we are generating instructions for. */ +static int cpu_arch_tune_set = 0; + +/* Cpu we are generating instructions for. */ +static enum processor_type cpu_arch_tune = PROCESSOR_UNKNOWN; + +/* CPU feature flags of cpu we are generating instructions for. */ +static unsigned int cpu_arch_tune_flags = 0; + +/* CPU instruction set architecture used. */ +static enum processor_type cpu_arch_isa = PROCESSOR_UNKNOWN; + +/* CPU feature flags of instruction set architecture used. */ +static unsigned int cpu_arch_isa_flags = 0; + /* If set, conditional jumps are not automatically promoted to handle larger than a byte offset. */ static unsigned int no_cond_jump_promotion = 0; @@ -415,35 +418,106 @@ const relax_typeS md_relax_table[] = {0, 0, 4, 0} }; -static const arch_entry cpu_arch[] = { - {"i8086", Cpu086 }, - {"i186", Cpu086|Cpu186 }, - {"i286", Cpu086|Cpu186|Cpu286 }, - {"i386", Cpu086|Cpu186|Cpu286|Cpu386 }, - {"i486", Cpu086|Cpu186|Cpu286|Cpu386|Cpu486 }, - {"i586", Cpu086|Cpu186|Cpu286|Cpu386|Cpu486|Cpu586 }, - {"i686", Cpu086|Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686 }, - {"pentium", Cpu086|Cpu186|Cpu286|Cpu386|Cpu486|Cpu586 }, - {"pentiumpro",Cpu086|Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686 }, - {"pentiumii", Cpu086|Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuMMX }, - {"pentiumiii",Cpu086|Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuMMX|CpuMMX2|CpuSSE }, - {"pentium4", Cpu086|Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuP4|CpuMMX|CpuMMX2|CpuSSE|CpuSSE2 }, - {"prescott", Cpu086|Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuP4|CpuMMX|CpuMMX2|CpuSSE|CpuSSE2|CpuPNI }, - {"k6", Cpu086|Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|CpuK6|CpuMMX }, - {"k6_2", Cpu086|Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|CpuK6|CpuMMX|Cpu3dnow }, - {"athlon", Cpu086|Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuK6|CpuAthlon|CpuMMX|CpuMMX2|Cpu3dnow|Cpu3dnowA }, - {"sledgehammer",Cpu086|Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuK6|CpuAthlon|CpuSledgehammer|CpuMMX|CpuMMX2|Cpu3dnow|Cpu3dnowA|CpuSSE|CpuSSE2 }, - {"opteron", Cpu086|Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuK6|CpuAthlon|CpuSledgehammer|CpuMMX|CpuMMX2|Cpu3dnow|Cpu3dnowA|CpuSSE|CpuSSE2 }, - {".mmx", CpuMMX }, - {".sse", CpuMMX|CpuMMX2|CpuSSE }, - {".sse2", CpuMMX|CpuMMX2|CpuSSE|CpuSSE2 }, - {".sse3", CpuMMX|CpuMMX2|CpuSSE|CpuSSE2|CpuSSE3 }, - {".3dnow", CpuMMX|Cpu3dnow }, - {".3dnowa", CpuMMX|CpuMMX2|Cpu3dnow|Cpu3dnowA }, - {".padlock", CpuPadLock }, - {".pacifica", CpuSVME }, - {".svme", CpuSVME }, - {NULL, 0 } +static const arch_entry cpu_arch[] = +{ + {"generic32", PROCESSOR_GENERIC32, + Cpu186|Cpu286|Cpu386}, + {"generic64", PROCESSOR_GENERIC64, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuP4|CpuMMX + |CpuMMX2|CpuSSE|CpuSSE2}, + {"i8086", PROCESSOR_UNKNOWN, + 0}, + {"i186", PROCESSOR_UNKNOWN, + Cpu186}, + {"i286", PROCESSOR_UNKNOWN, + Cpu186|Cpu286}, + {"i386", PROCESSOR_GENERIC32, + Cpu186|Cpu286|Cpu386}, + {"i486", PROCESSOR_I486, + Cpu186|Cpu286|Cpu386|Cpu486}, + {"i586", PROCESSOR_PENTIUM, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586}, + {"i686", PROCESSOR_PENTIUMPRO, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686}, + {"pentium", PROCESSOR_PENTIUM, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586}, + {"pentiumpro",PROCESSOR_PENTIUMPRO, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686}, + {"pentiumii", PROCESSOR_PENTIUMPRO, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuMMX}, + {"pentiumiii",PROCESSOR_PENTIUMPRO, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuMMX|CpuMMX2|CpuSSE}, + {"pentium4", PROCESSOR_PENTIUM4, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuP4|CpuMMX + |CpuMMX2|CpuSSE|CpuSSE2}, + {"prescott", PROCESSOR_NOCONA, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuP4|CpuMMX + |CpuMMX2|CpuSSE|CpuSSE2|CpuSSE3}, + {"nocona", PROCESSOR_NOCONA, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuP4|CpuMMX + |CpuMMX2|CpuSSE|CpuSSE2|CpuSSE3}, + {"yonah", PROCESSOR_CORE, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuP4|CpuMMX + |CpuMMX2|CpuSSE|CpuSSE2|CpuSSE3}, + {"core", PROCESSOR_CORE, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuP4|CpuMMX + |CpuMMX2|CpuSSE|CpuSSE2|CpuSSE3}, + {"merom", PROCESSOR_CORE2, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuP4|CpuMMX + |CpuMMX2|CpuSSE|CpuSSE2|CpuSSE3|CpuSSSE3}, + {"core2", PROCESSOR_CORE2, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuP4|CpuMMX + |CpuMMX2|CpuSSE|CpuSSE2|CpuSSE3|CpuSSSE3}, + {"k6", PROCESSOR_K6, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|CpuK6|CpuMMX}, + {"k6_2", PROCESSOR_K6, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|CpuK6|CpuMMX|Cpu3dnow}, + {"athlon", PROCESSOR_ATHLON, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuK6 + |CpuMMX|CpuMMX2|Cpu3dnow|Cpu3dnowA}, + {"sledgehammer", PROCESSOR_K8, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuK6 + |CpuSledgehammer|CpuMMX|CpuMMX2|Cpu3dnow|Cpu3dnowA|CpuSSE|CpuSSE2}, + {"opteron", PROCESSOR_K8, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuK6 + |CpuSledgehammer|CpuMMX|CpuMMX2|Cpu3dnow|Cpu3dnowA|CpuSSE|CpuSSE2}, + {"k8", PROCESSOR_K8, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuK6 + |CpuSledgehammer|CpuMMX|CpuMMX2|Cpu3dnow|Cpu3dnowA|CpuSSE|CpuSSE2}, + {"amdfam10", PROCESSOR_AMDFAM10, + Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuK6|CpuSledgehammer + |CpuMMX|CpuMMX2|Cpu3dnow|Cpu3dnowA|CpuSSE|CpuSSE2|CpuSSE3|CpuSSE4a + |CpuABM}, + {".mmx", PROCESSOR_UNKNOWN, + CpuMMX}, + {".sse", PROCESSOR_UNKNOWN, + CpuMMX|CpuMMX2|CpuSSE}, + {".sse2", PROCESSOR_UNKNOWN, + CpuMMX|CpuMMX2|CpuSSE|CpuSSE2}, + {".sse3", PROCESSOR_UNKNOWN, + CpuMMX|CpuMMX2|CpuSSE|CpuSSE2|CpuSSE3}, + {".ssse3", PROCESSOR_UNKNOWN, + CpuMMX|CpuMMX2|CpuSSE|CpuSSE2|CpuSSE3|CpuSSSE3}, + {".sse4.1", PROCESSOR_UNKNOWN, + CpuMMX|CpuMMX2|CpuSSE|CpuSSE2|CpuSSE3|CpuSSSE3|CpuSSE4_1}, + {".sse4.2", PROCESSOR_UNKNOWN, + CpuMMX|CpuMMX2|CpuSSE|CpuSSE2|CpuSSE3|CpuSSSE3|CpuSSE4}, + {".sse4", PROCESSOR_UNKNOWN, + CpuMMX|CpuMMX2|CpuSSE|CpuSSE2|CpuSSE3|CpuSSSE3|CpuSSE4}, + {".3dnow", PROCESSOR_UNKNOWN, + CpuMMX|Cpu3dnow}, + {".3dnowa", PROCESSOR_UNKNOWN, + CpuMMX|CpuMMX2|Cpu3dnow|Cpu3dnowA}, + {".padlock", PROCESSOR_UNKNOWN, + CpuPadLock}, + {".pacifica", PROCESSOR_UNKNOWN, + CpuSVME}, + {".svme", PROCESSOR_UNKNOWN, + CpuSVME}, + {".sse4a", PROCESSOR_UNKNOWN, + CpuMMX|CpuMMX2|CpuSSE|CpuSSE2|CpuSSE3|CpuSSE4a}, + {".abm", PROCESSOR_UNKNOWN, + CpuABM} }; const pseudo_typeS md_pseudo_table[] = @@ -473,7 +547,7 @@ const pseudo_typeS md_pseudo_table[] = #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) {"largecomm", handle_large_common, 0}, #else - {"file", (void (*) PARAMS ((int))) dwarf2_directive_file, 0}, + {"file", (void (*) (int)) dwarf2_directive_file, 0}, {"loc", dwarf2_directive_loc, 0}, {"loc_mark_labels", dwarf2_directive_loc_mark_labels, 0}, #endif @@ -493,9 +567,7 @@ static struct hash_control *op_hash; static struct hash_control *reg_hash; void -i386_align_code (fragP, count) - fragS *fragP; - int count; +i386_align_code (fragS *fragP, int count) { /* Various efficient no-op patterns for aligning code labels. Note: Don't try to assemble the instructions in the comments. @@ -503,7 +575,7 @@ i386_align_code (fragP, count) static const char f32_1[] = {0x90}; /* nop */ static const char f32_2[] = - {0x89,0xf6}; /* movl %esi,%esi */ + {0x66,0x90}; /* xchg %ax,%ax */ static const char f32_3[] = {0x8d,0x76,0x00}; /* leal 0(%esi),%esi */ static const char f32_4[] = @@ -563,13 +635,141 @@ i386_align_code (fragP, count) f32_1, f32_2, f16_3, f16_4, f16_5, f16_6, f16_7, f16_8, f32_15, f32_15, f32_15, f32_15, f32_15, f32_15, f32_15 }; + /* nopl (%[re]ax) */ + static const char alt_3[] = + {0x0f,0x1f,0x00}; + /* nopl 0(%[re]ax) */ + static const char alt_4[] = + {0x0f,0x1f,0x40,0x00}; + /* nopl 0(%[re]ax,%[re]ax,1) */ + static const char alt_5[] = + {0x0f,0x1f,0x44,0x00,0x00}; + /* nopw 0(%[re]ax,%[re]ax,1) */ + static const char alt_6[] = + {0x66,0x0f,0x1f,0x44,0x00,0x00}; + /* nopl 0L(%[re]ax) */ + static const char alt_7[] = + {0x0f,0x1f,0x80,0x00,0x00,0x00,0x00}; + /* nopl 0L(%[re]ax,%[re]ax,1) */ + static const char alt_8[] = + {0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; + /* nopw 0L(%[re]ax,%[re]ax,1) */ + static const char alt_9[] = + {0x66,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; + /* nopw %cs:0L(%[re]ax,%[re]ax,1) */ + static const char alt_10[] = + {0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; + /* data16 + nopw %cs:0L(%[re]ax,%[re]ax,1) */ + static const char alt_long_11[] = + {0x66, + 0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; + /* data16 + data16 + nopw %cs:0L(%[re]ax,%[re]ax,1) */ + static const char alt_long_12[] = + {0x66, + 0x66, + 0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; + /* data16 + data16 + data16 + nopw %cs:0L(%[re]ax,%[re]ax,1) */ + static const char alt_long_13[] = + {0x66, + 0x66, + 0x66, + 0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; + /* data16 + data16 + data16 + data16 + nopw %cs:0L(%[re]ax,%[re]ax,1) */ + static const char alt_long_14[] = + {0x66, + 0x66, + 0x66, + 0x66, + 0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; + /* data16 + data16 + data16 + data16 + data16 + nopw %cs:0L(%[re]ax,%[re]ax,1) */ + static const char alt_long_15[] = + {0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; + /* nopl 0(%[re]ax,%[re]ax,1) + nopw 0(%[re]ax,%[re]ax,1) */ + static const char alt_short_11[] = + {0x0f,0x1f,0x44,0x00,0x00, + 0x66,0x0f,0x1f,0x44,0x00,0x00}; + /* nopw 0(%[re]ax,%[re]ax,1) + nopw 0(%[re]ax,%[re]ax,1) */ + static const char alt_short_12[] = + {0x66,0x0f,0x1f,0x44,0x00,0x00, + 0x66,0x0f,0x1f,0x44,0x00,0x00}; + /* nopw 0(%[re]ax,%[re]ax,1) + nopl 0L(%[re]ax) */ + static const char alt_short_13[] = + {0x66,0x0f,0x1f,0x44,0x00,0x00, + 0x0f,0x1f,0x80,0x00,0x00,0x00,0x00}; + /* nopl 0L(%[re]ax) + nopl 0L(%[re]ax) */ + static const char alt_short_14[] = + {0x0f,0x1f,0x80,0x00,0x00,0x00,0x00, + 0x0f,0x1f,0x80,0x00,0x00,0x00,0x00}; + /* nopl 0L(%[re]ax) + nopl 0L(%[re]ax,%[re]ax,1) */ + static const char alt_short_15[] = + {0x0f,0x1f,0x80,0x00,0x00,0x00,0x00, + 0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; + static const char *const alt_short_patt[] = { + f32_1, f32_2, alt_3, alt_4, alt_5, alt_6, alt_7, alt_8, + alt_9, alt_10, alt_short_11, alt_short_12, alt_short_13, + alt_short_14, alt_short_15 + }; + static const char *const alt_long_patt[] = { + f32_1, f32_2, alt_3, alt_4, alt_5, alt_6, alt_7, alt_8, + alt_9, alt_10, alt_long_11, alt_long_12, alt_long_13, + alt_long_14, alt_long_15 + }; if (count <= 0 || count > 15) return; - /* The recommended way to pad 64bit code is to use NOPs preceded by - maximally four 0x66 prefixes. Balance the size of nops. */ - if (flag_code == CODE_64BIT) + /* We need to decide which NOP sequence to use for 32bit and + 64bit. When -mtune= is used: + + 1. For PROCESSOR_I486, PROCESSOR_PENTIUM and PROCESSOR_GENERIC32, + f32_patt will be used. + 2. For PROCESSOR_K8 and PROCESSOR_AMDFAM10 in 64bit, NOPs with + 0x66 prefix will be used. + 3. For PROCESSOR_CORE2, alt_long_patt will be used. + 4. For PROCESSOR_PENTIUMPRO, PROCESSOR_PENTIUM4, PROCESSOR_NOCONA, + PROCESSOR_CORE, PROCESSOR_CORE2, PROCESSOR_K6, PROCESSOR_ATHLON + and PROCESSOR_GENERIC64, alt_short_patt will be used. + + When -mtune= isn't used, alt_short_patt will be used if + cpu_arch_isa_flags has Cpu686. Otherwise, f32_patt will be used. + + When -march= or .arch is used, we can't use anything beyond + cpu_arch_isa_flags. */ + + if (flag_code == CODE_16BIT) + { + memcpy (fragP->fr_literal + fragP->fr_fix, + f16_patt[count - 1], count); + if (count > 8) + /* Adjust jump offset. */ + fragP->fr_literal[fragP->fr_fix + 1] = count - 2; + } + else if (flag_code == CODE_64BIT && cpu_arch_tune == PROCESSOR_K8) { int i; int nnops = (count + 3) / 4; @@ -577,6 +777,8 @@ i386_align_code (fragP, count) int remains = count - nnops * len; int pos = 0; + /* The recommended way to pad 64bit code is to use NOPs preceded + by maximally four 0x66 prefixes. Balance the size of nops. */ for (i = 0; i < remains; i++) { memset (fragP->fr_literal + fragP->fr_fix + pos, 0x66, len); @@ -591,57 +793,121 @@ i386_align_code (fragP, count) } } else - if (flag_code == CODE_16BIT) - { - memcpy (fragP->fr_literal + fragP->fr_fix, - f16_patt[count - 1], count); - if (count > 8) - /* Adjust jump offset. */ - fragP->fr_literal[fragP->fr_fix + 1] = count - 2; - } - else + { + const char *const *patt = NULL; + + if (cpu_arch_isa == PROCESSOR_UNKNOWN) + { + /* PROCESSOR_UNKNOWN means that all ISAs may be used. */ + switch (cpu_arch_tune) + { + case PROCESSOR_UNKNOWN: + /* We use cpu_arch_isa_flags to check if we SHOULD + optimize for Cpu686. */ + if ((cpu_arch_isa_flags & Cpu686) != 0) + patt = alt_short_patt; + else + patt = f32_patt; + break; + case PROCESSOR_CORE2: + patt = alt_long_patt; + break; + case PROCESSOR_PENTIUMPRO: + case PROCESSOR_PENTIUM4: + case PROCESSOR_NOCONA: + case PROCESSOR_CORE: + case PROCESSOR_K6: + case PROCESSOR_ATHLON: + case PROCESSOR_K8: + case PROCESSOR_GENERIC64: + case PROCESSOR_AMDFAM10: + patt = alt_short_patt; + break; + case PROCESSOR_I486: + case PROCESSOR_PENTIUM: + case PROCESSOR_GENERIC32: + patt = f32_patt; + break; + } + } + else + { + switch (cpu_arch_tune) + { + case PROCESSOR_UNKNOWN: + /* When cpu_arch_isa is net, cpu_arch_tune shouldn't be + PROCESSOR_UNKNOWN. */ + abort (); + break; + + case PROCESSOR_I486: + case PROCESSOR_PENTIUM: + case PROCESSOR_PENTIUMPRO: + case PROCESSOR_PENTIUM4: + case PROCESSOR_NOCONA: + case PROCESSOR_CORE: + case PROCESSOR_K6: + case PROCESSOR_ATHLON: + case PROCESSOR_K8: + case PROCESSOR_AMDFAM10: + case PROCESSOR_GENERIC32: + /* We use cpu_arch_isa_flags to check if we CAN optimize + for Cpu686. */ + if ((cpu_arch_isa_flags & Cpu686) != 0) + patt = alt_short_patt; + else + patt = f32_patt; + break; + case PROCESSOR_CORE2: + if ((cpu_arch_isa_flags & Cpu686) != 0) + patt = alt_long_patt; + else + patt = f32_patt; + break; + case PROCESSOR_GENERIC64: + patt = alt_short_patt; + break; + } + } + memcpy (fragP->fr_literal + fragP->fr_fix, - f32_patt[count - 1], count); + patt[count - 1], count); + } fragP->fr_var = count; } static INLINE unsigned int -mode_from_disp_size (t) - unsigned int t; +mode_from_disp_size (unsigned int t) { return (t & Disp8) ? 1 : (t & (Disp16 | Disp32 | Disp32S)) ? 2 : 0; } static INLINE int -fits_in_signed_byte (num) - offsetT num; +fits_in_signed_byte (offsetT num) { return (num >= -128) && (num <= 127); } static INLINE int -fits_in_unsigned_byte (num) - offsetT num; +fits_in_unsigned_byte (offsetT num) { return (num & 0xff) == num; } static INLINE int -fits_in_unsigned_word (num) - offsetT num; +fits_in_unsigned_word (offsetT num) { return (num & 0xffff) == num; } static INLINE int -fits_in_signed_word (num) - offsetT num; +fits_in_signed_word (offsetT num) { return (-32768 <= num) && (num <= 32767); } + static INLINE int -fits_in_signed_long (num) - offsetT num ATTRIBUTE_UNUSED; +fits_in_signed_long (offsetT num ATTRIBUTE_UNUSED) { #ifndef BFD64 return 1; @@ -650,9 +916,9 @@ fits_in_signed_long (num) || (((offsetT) -1 << 31) & num) == ((offsetT) -1 << 31)); #endif } /* fits_in_signed_long() */ + static INLINE int -fits_in_unsigned_long (num) - offsetT num ATTRIBUTE_UNUSED; +fits_in_unsigned_long (offsetT num ATTRIBUTE_UNUSED) { #ifndef BFD64 return 1; @@ -661,11 +927,10 @@ fits_in_unsigned_long (num) #endif } /* fits_in_unsigned_long() */ -static int -smallest_imm_type (num) - offsetT num; +static unsigned int +smallest_imm_type (offsetT num) { - if (cpu_arch_flags != (Cpu086 | Cpu186 | Cpu286 | Cpu386 | Cpu486 | CpuNo64)) + if (cpu_arch_flags != (Cpu186 | Cpu286 | Cpu386 | Cpu486 | CpuNo64)) { /* This code is disabled on the 486 because all the Imm1 forms in the opcode table are slower on the i486. They're the @@ -689,9 +954,7 @@ smallest_imm_type (num) } static offsetT -offset_in_range (val, size) - offsetT val; - int size; +offset_in_range (offsetT val, int size) { addressT mask; @@ -726,8 +989,7 @@ offset_in_range (val, size) class already exists, 1 if non rep/repne added, 2 if rep/repne added. */ static int -add_prefix (prefix) - unsigned int prefix; +add_prefix (unsigned int prefix) { int ret = 1; unsigned int q; @@ -735,9 +997,9 @@ add_prefix (prefix) if (prefix >= REX_OPCODE && prefix < REX_OPCODE + 16 && flag_code == CODE_64BIT) { - if ((i.prefix[REX_PREFIX] & prefix & REX_MODE64) - || ((i.prefix[REX_PREFIX] & (REX_EXTX | REX_EXTY | REX_EXTZ)) - && (prefix & (REX_EXTX | REX_EXTY | REX_EXTZ)))) + if ((i.prefix[REX_PREFIX] & prefix & REX_W) + || ((i.prefix[REX_PREFIX] & (REX_R | REX_X | REX_B)) + && (prefix & (REX_R | REX_X | REX_B)))) ret = 0; q = REX_PREFIX; } @@ -794,8 +1056,7 @@ add_prefix (prefix) } static void -set_code_flag (value) - int value; +set_code_flag (int value) { flag_code = value; cpu_arch_flags &= ~(Cpu64 | CpuNo64); @@ -812,8 +1073,7 @@ set_code_flag (value) } static void -set_16bit_gcc_code_flag (new_code_flag) - int new_code_flag; +set_16bit_gcc_code_flag (int new_code_flag) { flag_code = new_code_flag; cpu_arch_flags &= ~(Cpu64 | CpuNo64); @@ -822,8 +1082,7 @@ set_16bit_gcc_code_flag (new_code_flag) } static void -set_intel_syntax (syntax_flag) - int syntax_flag; +set_intel_syntax (int syntax_flag) { /* Find out if register prefixing is specified. */ int ask_naked_reg = 0; @@ -854,11 +1113,11 @@ set_intel_syntax (syntax_flag) identifier_chars['%'] = intel_syntax && allow_naked_reg ? '%' : 0; identifier_chars['$'] = intel_syntax ? '$' : 0; + register_prefix = allow_naked_reg ? "" : "%"; } static void -set_cpu_arch (dummy) - int dummy ATTRIBUTE_UNUSED; +set_cpu_arch (int dummy ATTRIBUTE_UNUSED) { SKIP_WHITESPACE (); @@ -866,9 +1125,9 @@ set_cpu_arch (dummy) { char *string = input_line_pointer; int e = get_symbol_end (); - int i; + unsigned int i; - for (i = 0; cpu_arch[i].name; i++) + for (i = 0; i < ARRAY_SIZE (cpu_arch); i++) { if (strcmp (string, cpu_arch[i].name) == 0) { @@ -877,7 +1136,15 @@ set_cpu_arch (dummy) cpu_arch_name = cpu_arch[i].name; cpu_sub_arch_name = NULL; cpu_arch_flags = (cpu_arch[i].flags - | (flag_code == CODE_64BIT ? Cpu64 : CpuNo64)); + | (flag_code == CODE_64BIT + ? Cpu64 : CpuNo64)); + cpu_arch_isa = cpu_arch[i].type; + cpu_arch_isa_flags = cpu_arch[i].flags; + if (!cpu_arch_tune_set) + { + cpu_arch_tune = cpu_arch_isa; + cpu_arch_tune_flags = cpu_arch_isa_flags; + } break; } if ((cpu_arch_flags | cpu_arch[i].flags) != cpu_arch_flags) @@ -890,7 +1157,7 @@ set_cpu_arch (dummy) return; } } - if (!cpu_arch[i].name) + if (i >= ARRAY_SIZE (cpu_arch)) as_bad (_("no such architecture: `%s'"), string); *input_line_pointer = e; @@ -976,10 +1243,9 @@ md_begin () reg_hash = hash_new (); { const reg_entry *regtab; + unsigned int regtab_size = i386_regtab_size; - for (regtab = i386_regtab; - regtab < i386_regtab + sizeof (i386_regtab) / sizeof (i386_regtab[0]); - regtab++) + for (regtab = i386_regtab; regtab_size--; regtab++) { hash_err = hash_insert (reg_hash, regtab->reg_name, (PTR) regtab); if (hash_err) @@ -1034,6 +1300,7 @@ md_begin () #endif digit_chars['-'] = '-'; mnemonic_chars['-'] = '-'; + mnemonic_chars['.'] = '.'; identifier_chars['_'] = '_'; identifier_chars['.'] = '.'; @@ -1063,8 +1330,7 @@ md_begin () } void -i386_print_statistics (file) - FILE *file; +i386_print_statistics (FILE *file) { hash_print_statistics (file, "i386 opcode", op_hash); hash_print_statistics (file, "i386 register", reg_hash); @@ -1073,16 +1339,13 @@ i386_print_statistics (file) #ifdef DEBUG386 /* Debugging routines for md_assemble. */ -static void pi PARAMS ((char *, i386_insn *)); -static void pte PARAMS ((template *)); -static void pt PARAMS ((unsigned int)); -static void pe PARAMS ((expressionS *)); -static void ps PARAMS ((symbolS *)); +static void pte (template *); +static void pt (unsigned int); +static void pe (expressionS *); +static void ps (symbolS *); static void -pi (line, x) - char *line; - i386_insn *x; +pi (char *line, i386_insn *x) { unsigned int i; @@ -1097,10 +1360,10 @@ pi (line, x) fprintf (stdout, " sib: base %x index %x scale %x\n", x->sib.base, x->sib.index, x->sib.scale); fprintf (stdout, " rex: 64bit %x extX %x extY %x extZ %x\n", - (x->rex & REX_MODE64) != 0, - (x->rex & REX_EXTX) != 0, - (x->rex & REX_EXTY) != 0, - (x->rex & REX_EXTZ) != 0); + (x->rex & REX_W) != 0, + (x->rex & REX_R) != 0, + (x->rex & REX_X) != 0, + (x->rex & REX_B) != 0); for (i = 0; i < x->operands; i++) { fprintf (stdout, " #%d: ", i + 1); @@ -1117,8 +1380,7 @@ pi (line, x) } static void -pte (t) - template *t; +pte (template *t) { unsigned int i; fprintf (stdout, " %d operands ", t->operands); @@ -1139,8 +1401,7 @@ pte (t) } static void -pe (e) - expressionS *e; +pe (expressionS *e) { fprintf (stdout, " operation %d\n", e->X_op); fprintf (stdout, " add_number %ld (%lx)\n", @@ -1160,8 +1421,7 @@ pe (e) } static void -ps (s) - symbolS *s; +ps (symbolS *s) { fprintf (stdout, "%s type %s%s", S_GET_NAME (s), @@ -1226,9 +1486,9 @@ pt (t) static bfd_reloc_code_real_type reloc (unsigned int size, - int pcrel, - int sign, - bfd_reloc_code_real_type other) + int pcrel, + int sign, + bfd_reloc_code_real_type other) { if (other != NO_RELOC) { @@ -1237,26 +1497,26 @@ reloc (unsigned int size, if (size == 8) switch (other) { - case BFD_RELOC_X86_64_GOT32: - return BFD_RELOC_X86_64_GOT64; - break; - case BFD_RELOC_X86_64_PLTOFF64: - return BFD_RELOC_X86_64_PLTOFF64; - break; - case BFD_RELOC_X86_64_GOTPC32: - other = BFD_RELOC_X86_64_GOTPC64; - break; - case BFD_RELOC_X86_64_GOTPCREL: - other = BFD_RELOC_X86_64_GOTPCREL64; - break; - case BFD_RELOC_X86_64_TPOFF32: - other = BFD_RELOC_X86_64_TPOFF64; - break; - case BFD_RELOC_X86_64_DTPOFF32: - other = BFD_RELOC_X86_64_DTPOFF64; - break; - default: - break; + case BFD_RELOC_X86_64_GOT32: + return BFD_RELOC_X86_64_GOT64; + break; + case BFD_RELOC_X86_64_PLTOFF64: + return BFD_RELOC_X86_64_PLTOFF64; + break; + case BFD_RELOC_X86_64_GOTPC32: + other = BFD_RELOC_X86_64_GOTPC64; + break; + case BFD_RELOC_X86_64_GOTPCREL: + other = BFD_RELOC_X86_64_GOTPCREL64; + break; + case BFD_RELOC_X86_64_TPOFF32: + other = BFD_RELOC_X86_64_TPOFF64; + break; + case BFD_RELOC_X86_64_DTPOFF32: + other = BFD_RELOC_X86_64_DTPOFF64; + break; + default: + break; } /* Sign-checking 4-byte relocations in 16-/32-bit code is pointless. */ @@ -1275,7 +1535,7 @@ reloc (unsigned int size, else if ((reloc->complain_on_overflow == complain_overflow_signed && !sign) || (reloc->complain_on_overflow == complain_overflow_unsigned - && sign > 0)) + && sign > 0)) as_bad (_("relocated field and relocation type differ in signedness")); else return other; @@ -1324,8 +1584,7 @@ reloc (unsigned int size, some cases we force the original symbol to be used. */ int -tc_i386_fix_adjustable (fixP) - fixS *fixP ATTRIBUTE_UNUSED; +tc_i386_fix_adjustable (fixS *fixP ATTRIBUTE_UNUSED) { #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) if (!IS_ELF) @@ -1378,11 +1637,8 @@ tc_i386_fix_adjustable (fixP) return 1; } -static int intel_float_operand PARAMS ((const char *mnemonic)); - static int -intel_float_operand (mnemonic) - const char *mnemonic; +intel_float_operand (const char *mnemonic) { /* Note that the value returned is meaningful only for opcodes with (memory) operands, hence the code here is free to improperly handle opcodes that @@ -1465,16 +1721,29 @@ md_assemble (line) if (line == NULL) return; + /* The order of the immediates should be reversed + for 2 immediates extrq and insertq instructions */ + if ((i.imm_operands == 2) + && ((strcmp (mnemonic, "extrq") == 0) + || (strcmp (mnemonic, "insertq") == 0))) + { + swap_2_operands (0, 1); + /* "extrq" and insertq" are the only two instructions whose operands + have to be reversed even though they have two immediate operands. + */ + if (intel_syntax) + swap_operands (); + } + /* Now we've parsed the mnemonic into a set of templates, and have the operands at hand. */ /* All intel opcodes have reversed operands except for "bound" and "enter". We also don't reverse intersegment "jmp" and "call" instructions with 2 immediate operands so that the immediate segment - precedes the offset, as it does when in AT&T mode. "enter" and the - intersegment "jmp" and "call" instructions are the only ones that - have two immediate operands. */ - if (intel_syntax && i.operands > 1 + precedes the offset, as it does when in AT&T mode. */ + if (intel_syntax + && i.operands > 1 && (strcmp (mnemonic, "bound") != 0) && (strcmp (mnemonic, "invlpga") != 0) && !((i.types[0] & Imm) && (i.types[1] & Imm))) @@ -1502,7 +1771,7 @@ md_assemble (line) /* Undo SYSV386_COMPAT brokenness when in Intel mode. See i386.h */ if (SYSV386_COMPAT && (i.tm.base_opcode & 0xfffffde0) == 0xdce0) - i.tm.base_opcode ^= FloatR; + i.tm.base_opcode ^= Opcode_FloatR; /* Zap movzx and movsx suffix. The suffix may have been set from "word ptr" or "byte ptr" on the source operand, but we'll use @@ -1556,9 +1825,9 @@ md_assemble (line) { expressionS *exp; - if ((i.tm.cpu_flags & CpuPNI) && i.operands > 0) + if ((i.tm.cpu_flags & CpuSSE3) && i.operands > 0) { - /* These Intel Prescott New Instructions have the fixed + /* Streaming SIMD extensions 3 Instructions have the fixed operands with an opcode suffix which is coded in the same place as an 8-bit immediate field would be. Here we check those operands and remove them afterwards. */ @@ -1566,8 +1835,11 @@ md_assemble (line) for (x = 0; x < i.operands; x++) if (i.op[x].regs->reg_num != x) - as_bad (_("can't use register '%%%s' as operand %d in '%s'."), - i.op[x].regs->reg_name, x + 1, i.tm.name); + as_bad (_("can't use register '%s%s' as operand %d in '%s'."), + register_prefix, + i.op[x].regs->reg_name, + x + 1, + i.tm.name); i.operands = 0; } @@ -1616,7 +1888,7 @@ md_assemble (line) } if ((i.tm.opcode_modifier & Rex64) != 0) - i.rex |= REX_MODE64; + i.rex |= REX_W; /* For 8 bit registers we need an empty rex prefix. Also if the instruction already has a prefix, we need to convert old @@ -1640,8 +1912,9 @@ md_assemble (line) { /* In case it is "hi" register, give up. */ if (i.op[x].regs->reg_num > 3) - as_bad (_("can't encode register '%%%s' in an instruction requiring REX prefix."), - i.op[x].regs->reg_name); + as_bad (_("can't encode register '%s%s' in an " + "instruction requiring REX prefix."), + register_prefix, i.op[x].regs->reg_name); /* Otherwise it is equivalent to the extended register. Since the encoding doesn't change this is merely @@ -1660,9 +1933,7 @@ md_assemble (line) } static char * -parse_insn (line, mnemonic) - char *line; - char *mnemonic; +parse_insn (char *line, char *mnemonic) { char *l = line; char *token_start = l; @@ -1832,9 +2103,9 @@ parse_insn (line, mnemonic) { if (!((t->cpu_flags & ~(Cpu64 | CpuNo64)) & ~(cpu_arch_flags & ~(Cpu64 | CpuNo64)))) - supported |= 1; + supported |= 1; if (!(t->cpu_flags & (flag_code == CODE_64BIT ? CpuNo64 : Cpu64))) - supported |= 2; + supported |= 2; } if (!(supported & 2)) { @@ -1867,7 +2138,7 @@ parse_insn (line, mnemonic) if (t >= current_templates->end) { as_bad (_("expecting string instruction after `%s'"), - expecting_string_instruction); + expecting_string_instruction); return NULL; } for (override.start = t; t < current_templates->end; ++t) @@ -1881,9 +2152,7 @@ parse_insn (line, mnemonic) } static char * -parse_operands (l, mnemonic) - char *l; - const char *mnemonic; +parse_operands (char *l, const char *mnemonic) { char *token_start; @@ -2001,24 +2270,12 @@ parse_operands (l, mnemonic) } static void -swap_operands () +swap_2_operands (int xchg1, int xchg2) { union i386_op temp_op; unsigned int temp_type; enum bfd_reloc_code_real temp_reloc; - int xchg1 = 0; - int xchg2 = 0; - if (i.operands == 2) - { - xchg1 = 0; - xchg2 = 1; - } - else if (i.operands == 3) - { - xchg1 = 0; - xchg2 = 2; - } temp_type = i.types[xchg2]; i.types[xchg2] = i.types[xchg1]; i.types[xchg1] = temp_type; @@ -2028,6 +2285,22 @@ swap_operands () temp_reloc = i.reloc[xchg2]; i.reloc[xchg2] = i.reloc[xchg1]; i.reloc[xchg1] = temp_reloc; +} + +static void +swap_operands (void) +{ + switch (i.operands) + { + case 4: + swap_2_operands (1, i.operands - 2); + case 3: + case 2: + swap_2_operands (0, i.operands - 1); + break; + default: + abort (); + } if (i.mem_operands == 2) { @@ -2041,7 +2314,7 @@ swap_operands () /* Try to ensure constant immediates are represented in the smallest opcode possible. */ static void -optimize_imm () +optimize_imm (void) { char guess_suffix = 0; int op; @@ -2131,8 +2404,10 @@ optimize_imm () unsigned int mask, allowed = 0; const template *t; - for (t = current_templates->start; t < current_templates->end; ++t) - allowed |= t->operand_types[op]; + for (t = current_templates->start; + t < current_templates->end; + ++t) + allowed |= t->operand_types[op]; switch (guess_suffix) { case QWORD_MNEM_SUFFIX: @@ -2151,8 +2426,8 @@ optimize_imm () mask = 0; break; } - if (mask & allowed) - i.types[op] &= mask; + if (mask & allowed) + i.types[op] &= mask; } break; } @@ -2161,7 +2436,7 @@ optimize_imm () /* Try to use the smallest displacement type too. */ static void -optimize_disp () +optimize_disp (void) { int op; @@ -2225,13 +2500,20 @@ optimize_disp () } static int -match_template () +match_template (void) { /* Points to template once we've found it. */ const template *t; - unsigned int overlap0, overlap1, overlap2; + unsigned int overlap0, overlap1, overlap2, overlap3; unsigned int found_reverse_match; int suffix_check; + unsigned int operand_types [MAX_OPERANDS]; + int addr_prefix_disp; + unsigned int j; + +#if MAX_OPERANDS != 4 +# error "MAX_OPERANDS must be 4." +#endif #define MATCH(overlap, given, template) \ ((overlap & ~JumpAbsolute) \ @@ -2249,7 +2531,11 @@ match_template () overlap0 = 0; overlap1 = 0; overlap2 = 0; + overlap3 = 0; found_reverse_match = 0; + for (j = 0; j < MAX_OPERANDS; j++) + operand_types [j] = 0; + addr_prefix_disp = -1; suffix_check = (i.suffix == BYTE_MNEM_SUFFIX ? No_bSuf : (i.suffix == WORD_MNEM_SUFFIX @@ -2265,6 +2551,8 @@ match_template () for (t = current_templates->start; t < current_templates->end; t++) { + addr_prefix_disp = -1; + /* Must have right number of operands. */ if (i.operands != t->operands) continue; @@ -2275,6 +2563,9 @@ match_template () && (t->opcode_modifier & IgnoreSize))) continue; + for (j = 0; j < MAX_OPERANDS; j++) + operand_types [j] = t->operand_types [j]; + /* In general, don't allow 64-bit operands in 32-bit mode. */ if (i.suffix == QWORD_MNEM_SUFFIX && flag_code != CODE_64BIT @@ -2282,8 +2573,8 @@ match_template () ? (!(t->opcode_modifier & IgnoreSize) && !intel_float_operand (t->name)) : intel_float_operand (t->name) != 2) - && (!(t->operand_types[0] & (RegMMX | RegXMM)) - || !(t->operand_types[t->operands > 1] & (RegMMX | RegXMM))) + && (!(operand_types[0] & (RegMMX | RegXMM)) + || !(operand_types[t->operands > 1] & (RegMMX | RegXMM))) && (t->base_opcode != 0x0fc7 || t->extension_opcode != 1 /* cmpxchg8b */)) continue; @@ -2297,66 +2588,142 @@ match_template () break; } - overlap0 = i.types[0] & t->operand_types[0]; + /* Address size prefix will turn Disp64/Disp32/Disp16 operand + into Disp32/Disp16/Disp32 operand. */ + if (i.prefix[ADDR_PREFIX] != 0) + { + unsigned int DispOn = 0, DispOff = 0; + + switch (flag_code) + { + case CODE_16BIT: + DispOn = Disp32; + DispOff = Disp16; + break; + case CODE_32BIT: + DispOn = Disp16; + DispOff = Disp32; + break; + case CODE_64BIT: + DispOn = Disp32; + DispOff = Disp64; + break; + } + + for (j = 0; j < MAX_OPERANDS; j++) + { + /* There should be only one Disp operand. */ + if ((operand_types[j] & DispOff)) + { + addr_prefix_disp = j; + operand_types[j] |= DispOn; + operand_types[j] &= ~DispOff; + break; + } + } + } + + overlap0 = i.types[0] & operand_types[0]; switch (t->operands) { case 1: - if (!MATCH (overlap0, i.types[0], t->operand_types[0])) + if (!MATCH (overlap0, i.types[0], operand_types[0])) continue; break; case 2: + /* xchg %eax, %eax is a special case. It is an aliase for nop + only in 32bit mode and we can use opcode 0x90. In 64bit + mode, we can't use 0x90 for xchg %eax, %eax since it should + zero-extend %eax to %rax. */ + if (flag_code == CODE_64BIT + && t->base_opcode == 0x90 + && i.types [0] == (Acc | Reg32) + && i.types [1] == (Acc | Reg32)) + continue; case 3: - overlap1 = i.types[1] & t->operand_types[1]; - if (!MATCH (overlap0, i.types[0], t->operand_types[0]) - || !MATCH (overlap1, i.types[1], t->operand_types[1]) + case 4: + overlap1 = i.types[1] & operand_types[1]; + if (!MATCH (overlap0, i.types[0], operand_types[0]) + || !MATCH (overlap1, i.types[1], operand_types[1]) /* monitor in SSE3 is a very special case. The first - register and the second register may have differnet - sizes. */ + register and the second register may have different + sizes. The same applies to crc32 in SSE4.2. */ || !((t->base_opcode == 0x0f01 && t->extension_opcode == 0xc8) + || t->base_opcode == 0xf20f38f1 || CONSISTENT_REGISTER_MATCH (overlap0, i.types[0], - t->operand_types[0], + operand_types[0], overlap1, i.types[1], - t->operand_types[1]))) + operand_types[1]))) { /* Check if other direction is valid ... */ if ((t->opcode_modifier & (D | FloatD)) == 0) continue; /* Try reversing direction of operands. */ - overlap0 = i.types[0] & t->operand_types[1]; - overlap1 = i.types[1] & t->operand_types[0]; - if (!MATCH (overlap0, i.types[0], t->operand_types[1]) - || !MATCH (overlap1, i.types[1], t->operand_types[0]) + overlap0 = i.types[0] & operand_types[1]; + overlap1 = i.types[1] & operand_types[0]; + if (!MATCH (overlap0, i.types[0], operand_types[1]) + || !MATCH (overlap1, i.types[1], operand_types[0]) || !CONSISTENT_REGISTER_MATCH (overlap0, i.types[0], - t->operand_types[1], + operand_types[1], overlap1, i.types[1], - t->operand_types[0])) + operand_types[0])) { /* Does not match either direction. */ continue; } /* found_reverse_match holds which of D or FloatDR we've found. */ - found_reverse_match = t->opcode_modifier & (D | FloatDR); + if ((t->opcode_modifier & D)) + found_reverse_match = Opcode_D; + else if ((t->opcode_modifier & FloatD)) + found_reverse_match = Opcode_FloatD; + else + found_reverse_match = 0; + if ((t->opcode_modifier & FloatR)) + found_reverse_match |= Opcode_FloatR; } - /* Found a forward 2 operand match here. */ - else if (t->operands == 3) + else { - /* Here we make use of the fact that there are no - reverse match 3 operand instructions, and all 3 - operand instructions only need to be checked for - register consistency between operands 2 and 3. */ - overlap2 = i.types[2] & t->operand_types[2]; - if (!MATCH (overlap2, i.types[2], t->operand_types[2]) - || !CONSISTENT_REGISTER_MATCH (overlap1, i.types[1], - t->operand_types[1], - overlap2, i.types[2], - t->operand_types[2])) + /* Found a forward 2 operand match here. */ + switch (t->operands) + { + case 4: + overlap3 = i.types[3] & operand_types[3]; + case 3: + overlap2 = i.types[2] & operand_types[2]; + break; + } - continue; + switch (t->operands) + { + case 4: + if (!MATCH (overlap3, i.types[3], operand_types[3]) + || !CONSISTENT_REGISTER_MATCH (overlap2, + i.types[2], + operand_types[2], + overlap3, + i.types[3], + operand_types[3])) + continue; + case 3: + /* Here we make use of the fact that there are no + reverse match 3 operand instructions, and all 3 + operand instructions only need to be checked for + register consistency between operands 2 and 3. */ + if (!MATCH (overlap2, i.types[2], operand_types[2]) + || !CONSISTENT_REGISTER_MATCH (overlap1, + i.types[1], + operand_types[1], + overlap2, + i.types[2], + operand_types[2])) + continue; + break; + } } - /* Found either forward/reverse 2 or 3 operand match here: + /* Found either forward/reverse 2, 3 or 4 operand match here: slip through to break. */ } if (t->cpu_flags & ~cpu_arch_flags) @@ -2380,7 +2747,7 @@ match_template () { if (!intel_syntax && ((i.types[0] & JumpAbsolute) - != (t->operand_types[0] & JumpAbsolute))) + != (operand_types[0] & JumpAbsolute))) { as_warn (_("indirect %s without `*'"), t->name); } @@ -2396,6 +2763,11 @@ match_template () /* Copy the template we found. */ i.tm = *t; + + if (addr_prefix_disp != -1) + i.tm.operand_types[addr_prefix_disp] + = operand_types[addr_prefix_disp]; + if (found_reverse_match) { /* If we found a reverse match we must alter the opcode @@ -2404,15 +2776,15 @@ match_template () i.tm.base_opcode ^= found_reverse_match; - i.tm.operand_types[0] = t->operand_types[1]; - i.tm.operand_types[1] = t->operand_types[0]; + i.tm.operand_types[0] = operand_types[1]; + i.tm.operand_types[1] = operand_types[0]; } return 1; } static int -check_string () +check_string (void) { int mem_op = (i.types[0] & AnyMem) ? 0 : 1; if ((i.tm.operand_types[mem_op] & EsSeg) != 0) @@ -2465,19 +2837,44 @@ process_suffix (void) { /* We take i.suffix from the last register operand specified, Destination register type is more significant than source - register type. */ - int op; - - for (op = i.operands; --op >= 0;) - if ((i.types[op] & Reg) - && !(i.tm.operand_types[op] & InOutPortReg)) - { - i.suffix = ((i.types[op] & Reg8) ? BYTE_MNEM_SUFFIX : - (i.types[op] & Reg16) ? WORD_MNEM_SUFFIX : - (i.types[op] & Reg64) ? QWORD_MNEM_SUFFIX : + register type. crc32 in SSE4.2 prefers source register + type. */ + if (i.tm.base_opcode == 0xf20f38f1) + { + if ((i.types[0] & Reg)) + i.suffix = ((i.types[0] & Reg16) ? WORD_MNEM_SUFFIX : LONG_MNEM_SUFFIX); - break; - } + } + else if (i.tm.base_opcode == 0xf20f38f0) + { + if ((i.types[0] & Reg8)) + i.suffix = BYTE_MNEM_SUFFIX; + } + + if (!i.suffix) + { + int op; + + if (i.tm.base_opcode == 0xf20f38f1 + || i.tm.base_opcode == 0xf20f38f0) + { + /* We have to know the operand size for crc32. */ + as_bad (_("ambiguous memory operand size for `%s`"), + i.tm.name); + return 0; + } + + for (op = i.operands; --op >= 0;) + if ((i.types[op] & Reg) + && !(i.tm.operand_types[op] & InOutPortReg)) + { + i.suffix = ((i.types[op] & Reg8) ? BYTE_MNEM_SUFFIX : + (i.types[op] & Reg16) ? WORD_MNEM_SUFFIX : + (i.types[op] & Reg64) ? QWORD_MNEM_SUFFIX : + LONG_MNEM_SUFFIX); + break; + } + } } else if (i.suffix == BYTE_MNEM_SUFFIX) { @@ -2515,9 +2912,9 @@ process_suffix (void) else if (intel_syntax && !i.suffix && ((i.tm.operand_types[0] & JumpAbsolute) - || (i.tm.opcode_modifier & (JumpByte|JumpInterSegment)) - || (i.tm.base_opcode == 0x0f01 /* [ls][gi]dt */ - && i.tm.extension_opcode <= 3))) + || (i.tm.opcode_modifier & (JumpByte|JumpInterSegment)) + || (i.tm.base_opcode == 0x0f01 /* [ls][gi]dt */ + && i.tm.extension_opcode <= 3))) { switch (flag_code) { @@ -2544,19 +2941,20 @@ process_suffix (void) { if (i.tm.opcode_modifier & W) { - as_bad (_("no instruction mnemonic suffix given and no register operands; can't size instruction")); + as_bad (_("no instruction mnemonic suffix given and " + "no register operands; can't size instruction")); return 0; } } else { - unsigned int suffixes = ~i.tm.opcode_modifier - & (No_bSuf - | No_wSuf - | No_lSuf - | No_sSuf - | No_xSuf - | No_qSuf); + unsigned int suffixes = (~i.tm.opcode_modifier + & (No_bSuf + | No_wSuf + | No_lSuf + | No_sSuf + | No_xSuf + | No_qSuf)); if ((i.tm.opcode_modifier & W) || ((suffixes & (suffixes - 1)) @@ -2615,7 +3013,15 @@ process_suffix (void) if (i.suffix == QWORD_MNEM_SUFFIX && flag_code == CODE_64BIT && (i.tm.opcode_modifier & NoRex64) == 0) - i.rex |= REX_MODE64; + { + /* Special case for xchg %rax,%rax. It is NOP and doesn't + need rex64. */ + if (i.operands != 2 + || i.types [0] != (Acc | Reg64) + || i.types [1] != (Acc | Reg64) + || i.tm.base_opcode != 0x90) + i.rex |= REX_W; + } /* Size floating point instruction. */ if (i.suffix == LONG_MNEM_SUFFIX) @@ -2648,6 +3054,10 @@ check_byte_reg (void) || i.tm.base_opcode == 0xfbf)) continue; + /* crc32 doesn't generate this warning. */ + if (i.tm.base_opcode == 0xf20f38f0) + continue; + if ((i.types[op] & WordReg) && i.op[op].regs->reg_num < 4) { /* Prohibit these changes in the 64bit mode, since the @@ -2655,18 +3065,20 @@ check_byte_reg (void) if (flag_code == CODE_64BIT && (i.tm.operand_types[op] & InOutPortReg) == 0) { - as_bad (_("Incorrect register `%%%s' used with `%c' suffix"), - i.op[op].regs->reg_name, + as_bad (_("Incorrect register `%s%s' used with `%c' suffix"), + register_prefix, i.op[op].regs->reg_name, i.suffix); return 0; } #if REGISTER_WARNINGS if (!quiet_warnings && (i.tm.operand_types[op] & InOutPortReg) == 0) - as_warn (_("using `%%%s' instead of `%%%s' due to `%c' suffix"), + as_warn (_("using `%s%s' instead of `%s%s' due to `%c' suffix"), + register_prefix, (i.op[op].regs + (i.types[op] & Reg16 ? REGNAM_AL - REGNAM_AX : REGNAM_AL - REGNAM_EAX))->reg_name, + register_prefix, i.op[op].regs->reg_name, i.suffix); #endif @@ -2678,7 +3090,8 @@ check_byte_reg (void) | Control | Debug | Test | FloatReg | FloatAcc)) { - as_bad (_("`%%%s' not allowed with `%s%c'"), + as_bad (_("`%s%s' not allowed with `%s%c'"), + register_prefix, i.op[op].regs->reg_name, i.tm.name, i.suffix); @@ -2689,7 +3102,7 @@ check_byte_reg (void) } static int -check_long_reg () +check_long_reg (void) { int op; @@ -2699,7 +3112,8 @@ check_long_reg () if ((i.types[op] & Reg8) != 0 && (i.tm.operand_types[op] & (Reg16 | Reg32 | Acc)) != 0) { - as_bad (_("`%%%s' not allowed with `%s%c'"), + as_bad (_("`%s%s' not allowed with `%s%c'"), + register_prefix, i.op[op].regs->reg_name, i.tm.name, i.suffix); @@ -2714,15 +3128,17 @@ check_long_reg () lowering is more complicated. */ if (flag_code == CODE_64BIT) { - as_bad (_("Incorrect register `%%%s' used with `%c' suffix"), - i.op[op].regs->reg_name, + as_bad (_("Incorrect register `%s%s' used with `%c' suffix"), + register_prefix, i.op[op].regs->reg_name, i.suffix); return 0; } #if REGISTER_WARNINGS else - as_warn (_("using `%%%s' instead of `%%%s' due to `%c' suffix"), + as_warn (_("using `%s%s' instead of `%s%s' due to `%c' suffix"), + register_prefix, (i.op[op].regs + REGNAM_EAX - REGNAM_AX)->reg_name, + register_prefix, i.op[op].regs->reg_name, i.suffix); #endif @@ -2731,8 +3147,8 @@ check_long_reg () else if ((i.types[op] & Reg64) != 0 && (i.tm.operand_types[op] & (Reg32 | Acc)) != 0) { - as_bad (_("Incorrect register `%%%s' used with `%c' suffix"), - i.op[op].regs->reg_name, + as_bad (_("Incorrect register `%s%s' used with `%c' suffix"), + register_prefix, i.op[op].regs->reg_name, i.suffix); return 0; } @@ -2740,7 +3156,7 @@ check_long_reg () } static int -check_qword_reg () +check_qword_reg (void) { int op; @@ -2750,7 +3166,8 @@ check_qword_reg () if ((i.types[op] & Reg8) != 0 && (i.tm.operand_types[op] & (Reg16 | Reg32 | Acc)) != 0) { - as_bad (_("`%%%s' not allowed with `%s%c'"), + as_bad (_("`%s%s' not allowed with `%s%c'"), + register_prefix, i.op[op].regs->reg_name, i.tm.name, i.suffix); @@ -2763,8 +3180,8 @@ check_qword_reg () { /* Prohibit these changes in the 64bit mode, since the lowering is more complicated. */ - as_bad (_("Incorrect register `%%%s' used with `%c' suffix"), - i.op[op].regs->reg_name, + as_bad (_("Incorrect register `%s%s' used with `%c' suffix"), + register_prefix, i.op[op].regs->reg_name, i.suffix); return 0; } @@ -2772,7 +3189,7 @@ check_qword_reg () } static int -check_word_reg () +check_word_reg (void) { int op; for (op = i.operands; --op >= 0;) @@ -2781,7 +3198,8 @@ check_word_reg () if ((i.types[op] & Reg8) != 0 && (i.tm.operand_types[op] & (Reg16 | Reg32 | Acc)) != 0) { - as_bad (_("`%%%s' not allowed with `%s%c'"), + as_bad (_("`%s%s' not allowed with `%s%c'"), + register_prefix, i.op[op].regs->reg_name, i.tm.name, i.suffix); @@ -2796,15 +3214,17 @@ check_word_reg () lowering is more complicated. */ if (flag_code == CODE_64BIT) { - as_bad (_("Incorrect register `%%%s' used with `%c' suffix"), - i.op[op].regs->reg_name, + as_bad (_("Incorrect register `%s%s' used with `%c' suffix"), + register_prefix, i.op[op].regs->reg_name, i.suffix); return 0; } else #if REGISTER_WARNINGS - as_warn (_("using `%%%s' instead of `%%%s' due to `%c' suffix"), + as_warn (_("using `%s%s' instead of `%s%s' due to `%c' suffix"), + register_prefix, (i.op[op].regs + REGNAM_AX - REGNAM_EAX)->reg_name, + register_prefix, i.op[op].regs->reg_name, i.suffix); #endif @@ -2813,7 +3233,7 @@ check_word_reg () } static int -finalize_imm () +finalize_imm (void) { unsigned int overlap0, overlap1, overlap2; @@ -2844,7 +3264,8 @@ finalize_imm () && overlap0 != Imm16 && overlap0 != Imm32S && overlap0 != Imm32 && overlap0 != Imm64) { - as_bad (_("no instruction mnemonic suffix given; can't determine immediate size")); + as_bad (_("no instruction mnemonic suffix given; " + "can't determine immediate size")); return 0; } } @@ -2877,7 +3298,9 @@ finalize_imm () && overlap1 != Imm16 && overlap1 != Imm32S && overlap1 != Imm32 && overlap1 != Imm64) { - as_bad (_("no instruction mnemonic suffix given; can't determine immediate size %x %c"),overlap1, i.suffix); + as_bad (_("no instruction mnemonic suffix given; " + "can't determine immediate size %x %c"), + overlap1, i.suffix); return 0; } } @@ -2891,7 +3314,7 @@ finalize_imm () } static int -process_operands () +process_operands (void) { /* Default segment register this instruction will use for memory accesses. 0 means unknown. This is only for optimizing out @@ -2901,40 +3324,90 @@ process_operands () /* The imul $imm, %reg instruction is converted into imul $imm, %reg, %reg, and the clr %reg instruction is converted into xor %reg, %reg. */ - if (i.tm.opcode_modifier & regKludge) - { - unsigned int first_reg_op = (i.types[0] & Reg) ? 0 : 1; - /* Pretend we saw the extra register operand. */ - assert (i.op[first_reg_op + 1].regs == 0); - i.op[first_reg_op + 1].regs = i.op[first_reg_op].regs; - i.types[first_reg_op + 1] = i.types[first_reg_op]; - i.reg_operands = 2; + if (i.tm.opcode_modifier & RegKludge) + { + if ((i.tm.cpu_flags & CpuSSE4_1)) + { + /* The first operand in instruction blendvpd, blendvps and + pblendvb in SSE4.1 is implicit and must be xmm0. */ + assert (i.operands == 3 + && i.reg_operands >= 2 + && i.types[0] == RegXMM); + if (i.op[0].regs->reg_num != 0) + { + if (intel_syntax) + as_bad (_("the last operand of `%s' must be `%sxmm0'"), + i.tm.name, register_prefix); + else + as_bad (_("the first operand of `%s' must be `%sxmm0'"), + i.tm.name, register_prefix); + return 0; + } + i.op[0] = i.op[1]; + i.op[1] = i.op[2]; + i.types[0] = i.types[1]; + i.types[1] = i.types[2]; + i.operands--; + i.reg_operands--; + + /* We need to adjust fields in i.tm since they are used by + build_modrm_byte. */ + i.tm.operand_types [0] = i.tm.operand_types [1]; + i.tm.operand_types [1] = i.tm.operand_types [2]; + i.tm.operands--; + } + else + { + unsigned int first_reg_op = (i.types[0] & Reg) ? 0 : 1; + /* Pretend we saw the extra register operand. */ + assert (i.reg_operands == 1 + && i.op[first_reg_op + 1].regs == 0); + i.op[first_reg_op + 1].regs = i.op[first_reg_op].regs; + i.types[first_reg_op + 1] = i.types[first_reg_op]; + i.operands++; + i.reg_operands++; + } } if (i.tm.opcode_modifier & ShortForm) { - /* The register or float register operand is in operand 0 or 1. */ - unsigned int op = (i.types[0] & (Reg | FloatReg)) ? 0 : 1; - /* Register goes in low 3 bits of opcode. */ - i.tm.base_opcode |= i.op[op].regs->reg_num; - if ((i.op[op].regs->reg_flags & RegRex) != 0) - i.rex |= REX_EXTZ; - if (!quiet_warnings && (i.tm.opcode_modifier & Ugh) != 0) + if (i.types[0] & (SReg2 | SReg3)) { - /* Warn about some common errors, but press on regardless. - The first case can be generated by gcc (<= 2.8.1). */ - if (i.operands == 2) + if (i.tm.base_opcode == POP_SEG_SHORT + && i.op[0].regs->reg_num == 1) { - /* Reversed arguments on faddp, fsubp, etc. */ - as_warn (_("translating to `%s %%%s,%%%s'"), i.tm.name, - i.op[1].regs->reg_name, - i.op[0].regs->reg_name); + as_bad (_("you can't `pop %%cs'")); + return 0; } - else + i.tm.base_opcode |= (i.op[0].regs->reg_num << 3); + if ((i.op[0].regs->reg_flags & RegRex) != 0) + i.rex |= REX_B; + } + else + { + /* The register or float register operand is in operand 0 or 1. */ + unsigned int op = (i.types[0] & (Reg | FloatReg)) ? 0 : 1; + /* Register goes in low 3 bits of opcode. */ + i.tm.base_opcode |= i.op[op].regs->reg_num; + if ((i.op[op].regs->reg_flags & RegRex) != 0) + i.rex |= REX_B; + if (!quiet_warnings && (i.tm.opcode_modifier & Ugh) != 0) { - /* Extraneous `l' suffix on fp insn. */ - as_warn (_("translating to `%s %%%s'"), i.tm.name, - i.op[0].regs->reg_name); + /* Warn about some common errors, but press on regardless. + The first case can be generated by gcc (<= 2.8.1). */ + if (i.operands == 2) + { + /* Reversed arguments on faddp, fsubp, etc. */ + as_warn (_("translating to `%s %s%s,%s%s'"), i.tm.name, + register_prefix, i.op[1].regs->reg_name, + register_prefix, i.op[0].regs->reg_name); + } + else + { + /* Extraneous `l' suffix on fp insn. */ + as_warn (_("translating to `%s %s%s'"), i.tm.name, + register_prefix, i.op[0].regs->reg_name); + } } } } @@ -2946,19 +3419,7 @@ process_operands () default_seg = build_modrm_byte (); } - else if (i.tm.opcode_modifier & (Seg2ShortForm | Seg3ShortForm)) - { - if (i.tm.base_opcode == POP_SEG_SHORT - && i.op[0].regs->reg_num == 1) - { - as_bad (_("you can't `pop %%cs'")); - return 0; - } - i.tm.base_opcode |= (i.op[0].regs->reg_num << 3); - if ((i.op[0].regs->reg_flags & RegRex) != 0) - i.rex |= REX_EXTZ; - } - else if ((i.tm.base_opcode & ~(D | W)) == MOV_AX_DISP32) + else if ((i.tm.base_opcode & ~0x3) == MOV_AX_DISP32) { default_seg = &ds; } @@ -2988,7 +3449,7 @@ process_operands () } static const seg_entry * -build_modrm_byte () +build_modrm_byte (void) { const seg_entry *default_seg = 0; @@ -2997,11 +3458,33 @@ build_modrm_byte () if (i.reg_operands == 2) { unsigned int source, dest; - source = ((i.types[0] - & (Reg | RegMMX | RegXMM - | SReg2 | SReg3 - | Control | Debug | Test)) - ? 0 : 1); + + switch (i.operands) + { + case 2: + source = 0; + break; + case 3: + /* When there are 3 operands, one of them may be immediate, + which may be the first or the last operand. Otherwise, + the first operand must be shift count register (cl). */ + assert (i.imm_operands == 1 + || (i.imm_operands == 0 + && (i.types[0] & ShiftCount))); + source = (i.types[0] & (Imm | ShiftCount)) ? 1 : 0; + break; + case 4: + /* When there are 4 operands, the first two must be immediate + operands. The source operand will be the 3rd one. */ + assert (i.imm_operands == 2 + && (i.types[0] & Imm) + && (i.types[1] & Imm)); + source = 2; + break; + default: + abort (); + } + dest = source + 1; i.rm.mode = 3; @@ -3011,29 +3494,29 @@ build_modrm_byte () destination operand, then we assume the source operand may sometimes be a memory operand and so we need to store the destination in the i.rm.reg field. */ - if ((i.tm.operand_types[dest] & AnyMem) == 0) + if ((i.tm.operand_types[dest] & (AnyMem | RegMem)) == 0) { i.rm.reg = i.op[dest].regs->reg_num; i.rm.regmem = i.op[source].regs->reg_num; if ((i.op[dest].regs->reg_flags & RegRex) != 0) - i.rex |= REX_EXTX; + i.rex |= REX_R; if ((i.op[source].regs->reg_flags & RegRex) != 0) - i.rex |= REX_EXTZ; + i.rex |= REX_B; } else { i.rm.reg = i.op[source].regs->reg_num; i.rm.regmem = i.op[dest].regs->reg_num; if ((i.op[dest].regs->reg_flags & RegRex) != 0) - i.rex |= REX_EXTZ; + i.rex |= REX_B; if ((i.op[source].regs->reg_flags & RegRex) != 0) - i.rex |= REX_EXTX; + i.rex |= REX_R; } - if (flag_code != CODE_64BIT && (i.rex & (REX_EXTX | REX_EXTZ))) + if (flag_code != CODE_64BIT && (i.rex & (REX_R | REX_B))) { if (!((i.types[0] | i.types[1]) & Control)) abort (); - i.rex &= ~(REX_EXTX | REX_EXTZ); + i.rex &= ~(REX_R | REX_B); add_prefix (LOCK_PREFIX_OPCODE); } } @@ -3042,9 +3525,12 @@ build_modrm_byte () if (i.mem_operands) { unsigned int fake_zero_displacement = 0; - unsigned int op = ((i.types[0] & AnyMem) - ? 0 - : (i.types[1] & AnyMem) ? 1 : 2); + unsigned int op; + + for (op = 0; op < i.operands; op++) + if ((i.types[op] & AnyMem)) + break; + assert (op < i.operands); default_seg = &ds; @@ -3065,9 +3551,11 @@ build_modrm_byte () i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING; i.sib.base = NO_BASE_REGISTER; i.sib.index = NO_INDEX_REGISTER; - i.types[op] = ((i.prefix[ADDR_PREFIX] == 0) ? Disp32S : Disp32); + i.types[op] = ((i.prefix[ADDR_PREFIX] == 0) + ? Disp32S : Disp32); } - else if ((flag_code == CODE_16BIT) ^ (i.prefix[ADDR_PREFIX] != 0)) + else if ((flag_code == CODE_16BIT) + ^ (i.prefix[ADDR_PREFIX] != 0)) { i.rm.regmem = NO_BASE_REGISTER_16; i.types[op] = Disp16; @@ -3090,7 +3578,7 @@ build_modrm_byte () else i.types[op] |= Disp32S; if ((i.index_reg->reg_flags & RegRex) != 0) - i.rex |= REX_EXTY; + i.rex |= REX_X; } } /* RIP addressing for 64bit mode. */ @@ -3099,7 +3587,7 @@ build_modrm_byte () i.rm.regmem = NO_BASE_REGISTER; i.types[op] &= ~ Disp; i.types[op] |= Disp32S; - i.flags[op] = Operand_PCrel; + i.flags[op] |= Operand_PCrel; if (! i.disp_operands) fake_zero_displacement = 1; } @@ -3137,11 +3625,13 @@ build_modrm_byte () { if (flag_code == CODE_64BIT && (i.types[op] & Disp)) - i.types[op] = (i.types[op] & Disp8) | (i.prefix[ADDR_PREFIX] == 0 ? Disp32S : Disp32); + i.types[op] = ((i.types[op] & Disp8) + | (i.prefix[ADDR_PREFIX] == 0 + ? Disp32S : Disp32)); i.rm.regmem = i.base_reg->reg_num; if ((i.base_reg->reg_flags & RegRex) != 0) - i.rex |= REX_EXTZ; + i.rex |= REX_B; i.sib.base = i.base_reg->reg_num; /* x86-64 ignores REX prefix bit here to avoid decoder complications. */ @@ -3178,7 +3668,7 @@ build_modrm_byte () i.sib.index = i.index_reg->reg_num; i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING; if ((i.index_reg->reg_flags & RegRex) != 0) - i.rex |= REX_EXTY; + i.rex |= REX_X; } if (i.disp_operands @@ -3211,31 +3701,28 @@ build_modrm_byte () registers are coded into the i.rm.reg field. */ if (i.reg_operands) { - unsigned int op = - ((i.types[0] - & (Reg | RegMMX | RegXMM - | SReg2 | SReg3 - | Control | Debug | Test)) - ? 0 - : ((i.types[1] - & (Reg | RegMMX | RegXMM - | SReg2 | SReg3 - | Control | Debug | Test)) - ? 1 - : 2)); + unsigned int op; + + for (op = 0; op < i.operands; op++) + if ((i.types[op] & (Reg | RegMMX | RegXMM + | SReg2 | SReg3 + | Control | Debug | Test))) + break; + assert (op < i.operands); + /* If there is an extension opcode to put here, the register number must be put into the regmem field. */ if (i.tm.extension_opcode != None) { i.rm.regmem = i.op[op].regs->reg_num; if ((i.op[op].regs->reg_flags & RegRex) != 0) - i.rex |= REX_EXTZ; + i.rex |= REX_B; } else { i.rm.reg = i.op[op].regs->reg_num; if ((i.op[op].regs->reg_flags & RegRex) != 0) - i.rex |= REX_EXTX; + i.rex |= REX_R; } /* Now, if no memory operand has set i.rm.mode = 0, 1, 2 we @@ -3253,7 +3740,7 @@ build_modrm_byte () } static void -output_branch () +output_branch (void) { char *p; int code16; @@ -3331,7 +3818,7 @@ output_branch () } static void -output_jump () +output_jump (void) { char *p; int size; @@ -3397,7 +3884,7 @@ output_jump () } static void -output_interseg_jump () +output_interseg_jump (void) { char *p; int size; @@ -3461,7 +3948,7 @@ output_interseg_jump () } static void -output_insn () +output_insn (void) { fragS *insn_start_frag; offsetT insn_start_off; @@ -3488,10 +3975,12 @@ output_insn () unsigned char *q; unsigned int prefix; - /* All opcodes on i386 have either 1 or 2 bytes. Merom New - Instructions have 3 bytes. We may use one more higher byte - to specify a prefix the instruction requires. */ - if ((i.tm.cpu_flags & CpuMNI) != 0) + /* All opcodes on i386 have either 1 or 2 bytes. SSSE3 and + SSE4 instructions have 3 bytes. We may use one more higher + byte to specify a prefix the instruction requires. Exclude + instructions which are in both SSE4 and ABM. */ + if ((i.tm.cpu_flags & (CpuSSSE3 | CpuSSE4)) != 0 + && (i.tm.cpu_flags & CpuABM) == 0) { if (i.tm.base_opcode & 0xff000000) { @@ -3504,7 +3993,7 @@ output_insn () prefix = (i.tm.base_opcode >> 16) & 0xff; if ((i.tm.cpu_flags & CpuPadLock) != 0) { -check_prefix: + check_prefix: if (prefix != REPE_PREFIX_OPCODE || i.prefix[LOCKREP_PREFIX] != REPE_PREFIX_OPCODE) add_prefix (prefix); @@ -3532,7 +4021,8 @@ check_prefix: } else { - if ((i.tm.cpu_flags & CpuMNI) != 0) + if ((i.tm.cpu_flags & (CpuSSSE3 | CpuSSE4)) != 0 + && (i.tm.cpu_flags & CpuABM) == 0) { p = frag_more (3); *p++ = (i.tm.base_opcode >> 16) & 0xff; @@ -3586,10 +4076,42 @@ check_prefix: #endif /* DEBUG386 */ } +/* Return the size of the displacement operand N. */ + +static int +disp_size (unsigned int n) +{ + int size = 4; + if (i.types[n] & (Disp8 | Disp16 | Disp64)) + { + size = 2; + if (i.types[n] & Disp8) + size = 1; + if (i.types[n] & Disp64) + size = 8; + } + return size; +} + +/* Return the size of the immediate operand N. */ + +static int +imm_size (unsigned int n) +{ + int size = 4; + if (i.types[n] & (Imm8 | Imm8S | Imm16 | Imm64)) + { + size = 2; + if (i.types[n] & (Imm8 | Imm8S)) + size = 1; + if (i.types[n] & Imm64) + size = 8; + } + return size; +} + static void -output_disp (insn_start_frag, insn_start_off) - fragS *insn_start_frag; - offsetT insn_start_off; +output_disp (fragS *insn_start_frag, offsetT insn_start_off) { char *p; unsigned int n; @@ -3600,18 +4122,9 @@ output_disp (insn_start_frag, insn_start_off) { if (i.op[n].disps->X_op == O_constant) { - int size; + int size = disp_size (n); offsetT val; - size = 4; - if (i.types[n] & (Disp8 | Disp16 | Disp64)) - { - size = 2; - if (i.types[n] & Disp8) - size = 1; - if (i.types[n] & Disp64) - size = 8; - } val = offset_in_range (i.op[n].disps->X_add_number, size); p = frag_more (size); @@ -3620,45 +4133,32 @@ output_disp (insn_start_frag, insn_start_off) else { enum bfd_reloc_code_real reloc_type; - int size = 4; - int sign = 0; + int size = disp_size (n); + int sign = (i.types[n] & Disp32S) != 0; int pcrel = (i.flags[n] & Operand_PCrel) != 0; + /* We can't have 8 bit displacement here. */ + assert ((i.types[n] & Disp8) == 0); + /* The PC relative address is computed relative to the instruction boundary, so in case immediate fields follows, we need to adjust the value. */ if (pcrel && i.imm_operands) { - int imm_size = 4; unsigned int n1; + int sz = 0; for (n1 = 0; n1 < i.operands; n1++) if (i.types[n1] & Imm) { - if (i.types[n1] & (Imm8 | Imm8S | Imm16 | Imm64)) - { - imm_size = 2; - if (i.types[n1] & (Imm8 | Imm8S)) - imm_size = 1; - if (i.types[n1] & Imm64) - imm_size = 8; - } - break; + /* Only one immediate is allowed for PC + relative address. */ + assert (sz == 0); + sz = imm_size (n1); + i.op[n].disps->X_add_number -= sz; } /* We should find the immediate. */ - if (n1 == i.operands) - abort (); - i.op[n].disps->X_add_number -= imm_size; - } - - if (i.types[n] & Disp32S) - sign = 1; - - if (i.types[n] & (Disp16 | Disp64)) - { - size = 2; - if (i.types[n] & Disp64) - size = 8; + assert (sz != 0); } p = frag_more (size); @@ -3712,9 +4212,7 @@ output_disp (insn_start_frag, insn_start_off) } static void -output_imm (insn_start_frag, insn_start_off) - fragS *insn_start_frag; - offsetT insn_start_off; +output_imm (fragS *insn_start_frag, offsetT insn_start_off) { char *p; unsigned int n; @@ -3725,18 +4223,9 @@ output_imm (insn_start_frag, insn_start_off) { if (i.op[n].imms->X_op == O_constant) { - int size; + int size = imm_size (n); offsetT val; - size = 4; - if (i.types[n] & (Imm8 | Imm8S | Imm16 | Imm64)) - { - size = 2; - if (i.types[n] & (Imm8 | Imm8S)) - size = 1; - else if (i.types[n] & Imm64) - size = 8; - } val = offset_in_range (i.op[n].imms->X_add_number, size); p = frag_more (size); @@ -3749,21 +4238,15 @@ output_imm (insn_start_frag, insn_start_off) non-absolute imms). Try to support other sizes ... */ enum bfd_reloc_code_real reloc_type; - int size = 4; - int sign = 0; + int size = imm_size (n); + int sign; if ((i.types[n] & (Imm32S)) && (i.suffix == QWORD_MNEM_SUFFIX || (!i.suffix && (i.tm.opcode_modifier & No_lSuf)))) sign = 1; - if (i.types[n] & (Imm8 | Imm8S | Imm16 | Imm64)) - { - size = 2; - if (i.types[n] & (Imm8 | Imm8S)) - size = 1; - if (i.types[n] & Imm64) - size = 8; - } + else + sign = 0; p = frag_more (size); reloc_type = reloc (size, 0, sign, i.reloc[n]); @@ -3857,10 +4340,8 @@ static enum bfd_reloc_code_real got_reloc = NO_RELOC; static int cons_sign = -1; void -x86_cons_fix_new (fragS *frag, - unsigned int off, - unsigned int len, - expressionS *exp) +x86_cons_fix_new (fragS *frag, unsigned int off, unsigned int len, + expressionS *exp) { enum bfd_reloc_code_real r = reloc (len, 0, cons_sign, got_reloc); @@ -3891,8 +4372,8 @@ x86_cons_fix_new (fragS *frag, input line. Otherwise return NULL. */ static char * lex_got (enum bfd_reloc_code_real *reloc, - int *adjust, - unsigned int *types) + int *adjust, + unsigned int *types) { /* Some of the relocations depend on the size of what field is to be relocated. But in our callers i386_immediate and i386_displacement @@ -3904,23 +4385,57 @@ lex_got (enum bfd_reloc_code_real *reloc, const enum bfd_reloc_code_real rel[2]; const unsigned int types64; } gotrel[] = { - { "PLTOFF", { 0, BFD_RELOC_X86_64_PLTOFF64 }, Imm64 }, - { "PLT", { BFD_RELOC_386_PLT32, BFD_RELOC_X86_64_PLT32 }, Imm32|Imm32S|Disp32 }, - { "GOTPLT", { 0, BFD_RELOC_X86_64_GOTPLT64 }, Imm64|Disp64 }, - { "GOTOFF", { BFD_RELOC_386_GOTOFF, BFD_RELOC_X86_64_GOTOFF64 }, Imm64|Disp64 }, - { "GOTPCREL", { 0, BFD_RELOC_X86_64_GOTPCREL }, Imm32|Imm32S|Disp32 }, - { "TLSGD", { BFD_RELOC_386_TLS_GD, BFD_RELOC_X86_64_TLSGD }, Imm32|Imm32S|Disp32 }, - { "TLSLDM", { BFD_RELOC_386_TLS_LDM, 0 }, 0 }, - { "TLSLD", { 0, BFD_RELOC_X86_64_TLSLD }, Imm32|Imm32S|Disp32 }, - { "GOTTPOFF", { BFD_RELOC_386_TLS_IE_32, BFD_RELOC_X86_64_GOTTPOFF }, Imm32|Imm32S|Disp32 }, - { "TPOFF", { BFD_RELOC_386_TLS_LE_32, BFD_RELOC_X86_64_TPOFF32 }, Imm32|Imm32S|Imm64|Disp32|Disp64 }, - { "NTPOFF", { BFD_RELOC_386_TLS_LE, 0 }, 0 }, - { "DTPOFF", { BFD_RELOC_386_TLS_LDO_32, BFD_RELOC_X86_64_DTPOFF32 }, Imm32|Imm32S|Imm64|Disp32|Disp64 }, - { "GOTNTPOFF",{ BFD_RELOC_386_TLS_GOTIE, 0 }, 0 }, - { "INDNTPOFF",{ BFD_RELOC_386_TLS_IE, 0 }, 0 }, - { "GOT", { BFD_RELOC_386_GOT32, BFD_RELOC_X86_64_GOT32 }, Imm32|Imm32S|Disp32|Imm64 }, - { "TLSDESC", { BFD_RELOC_386_TLS_GOTDESC, BFD_RELOC_X86_64_GOTPC32_TLSDESC }, Imm32|Imm32S|Disp32 }, - { "TLSCALL", { BFD_RELOC_386_TLS_DESC_CALL, BFD_RELOC_X86_64_TLSDESC_CALL }, Imm32|Imm32S|Disp32 } + { "PLTOFF", { 0, + BFD_RELOC_X86_64_PLTOFF64 }, + Imm64 }, + { "PLT", { BFD_RELOC_386_PLT32, + BFD_RELOC_X86_64_PLT32 }, + Imm32 | Imm32S | Disp32 }, + { "GOTPLT", { 0, + BFD_RELOC_X86_64_GOTPLT64 }, + Imm64 | Disp64 }, + { "GOTOFF", { BFD_RELOC_386_GOTOFF, + BFD_RELOC_X86_64_GOTOFF64 }, + Imm64 | Disp64 }, + { "GOTPCREL", { 0, + BFD_RELOC_X86_64_GOTPCREL }, + Imm32 | Imm32S | Disp32 }, + { "TLSGD", { BFD_RELOC_386_TLS_GD, + BFD_RELOC_X86_64_TLSGD }, + Imm32 | Imm32S | Disp32 }, + { "TLSLDM", { BFD_RELOC_386_TLS_LDM, + 0 }, + 0 }, + { "TLSLD", { 0, + BFD_RELOC_X86_64_TLSLD }, + Imm32 | Imm32S | Disp32 }, + { "GOTTPOFF", { BFD_RELOC_386_TLS_IE_32, + BFD_RELOC_X86_64_GOTTPOFF }, + Imm32 | Imm32S | Disp32 }, + { "TPOFF", { BFD_RELOC_386_TLS_LE_32, + BFD_RELOC_X86_64_TPOFF32 }, + Imm32 | Imm32S | Imm64 | Disp32 | Disp64 }, + { "NTPOFF", { BFD_RELOC_386_TLS_LE, + 0 }, + 0 }, + { "DTPOFF", { BFD_RELOC_386_TLS_LDO_32, + BFD_RELOC_X86_64_DTPOFF32 }, + Imm32 | Imm32S | Imm64 | Disp32 | Disp64 }, + { "GOTNTPOFF",{ BFD_RELOC_386_TLS_GOTIE, + 0 }, + 0 }, + { "INDNTPOFF",{ BFD_RELOC_386_TLS_IE, + 0 }, + 0 }, + { "GOT", { BFD_RELOC_386_GOT32, + BFD_RELOC_X86_64_GOT32 }, + Imm32 | Imm32S | Disp32 | Imm64 }, + { "TLSDESC", { BFD_RELOC_386_TLS_GOTDESC, + BFD_RELOC_X86_64_GOTPC32_TLSDESC }, + Imm32 | Imm32S | Disp32 }, + { "TLSCALL", { BFD_RELOC_386_TLS_DESC_CALL, + BFD_RELOC_X86_64_TLSDESC_CALL }, + Imm32 | Imm32S | Disp32 } }; char *cp; unsigned int j; @@ -3951,7 +4466,7 @@ lex_got (enum bfd_reloc_code_real *reloc, if (types) { if (flag_code != CODE_64BIT) - *types = Imm32|Disp32; + *types = Imm32 | Disp32; else *types = gotrel[j].types64; } @@ -3959,9 +4474,6 @@ lex_got (enum bfd_reloc_code_real *reloc, if (GOT_symbol == NULL) GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME); - /* Replace the relocation token with ' ', so that - errors like foo@GOTOFF1 will be detected. */ - /* The length of the first part of our input line. */ first = cp - input_line_pointer; @@ -3977,9 +4489,12 @@ lex_got (enum bfd_reloc_code_real *reloc, be necessary, but be safe. */ tmpbuf = xmalloc (first + second + 2); memcpy (tmpbuf, input_line_pointer, first); - tmpbuf[first] = ' '; - memcpy (tmpbuf + first + 1, past_reloc, second); - tmpbuf[first + second + 1] = '\0'; + if (second != 0 && *past_reloc != ' ') + /* Replace the relocation token with ' ', so that + errors like foo@GOTOFF1 will be detected. */ + tmpbuf[first++] = ' '; + memcpy (tmpbuf + first, past_reloc, second); + tmpbuf[first + second] = '\0'; return tmpbuf; } @@ -3994,9 +4509,7 @@ lex_got (enum bfd_reloc_code_real *reloc, } void -x86_cons (exp, size) - expressionS *exp; - int size; +x86_cons (expressionS *exp, int size) { if (size == 4 || (object_64bit && size == 8)) { @@ -4058,11 +4571,8 @@ pe_directive_secrel (dummy) } #endif -static int i386_immediate PARAMS ((char *)); - static int -i386_immediate (imm_start) - char *imm_start; +i386_immediate (char *imm_start) { char *save_input_line_pointer; char *gotfree_input_line; @@ -4072,7 +4582,8 @@ i386_immediate (imm_start) if (i.imm_operands == MAX_IMMEDIATE_OPERANDS) { - as_bad (_("only 1 or 2 immediate operands are allowed")); + as_bad (_("at most %d immediate operands are allowed"), + MAX_IMMEDIATE_OPERANDS); return 0; } @@ -4114,9 +4625,10 @@ i386_immediate (imm_start) /* Size it properly later. */ i.types[this_operand] |= Imm64; /* If BFD64, sign extend val. */ - if (!use_rela_relocations) - if ((exp->X_add_number & ~(((addressT) 2 << 31) - 1)) == 0) - exp->X_add_number = (exp->X_add_number ^ ((addressT) 1 << 31)) - ((addressT) 1 << 31); + if (!use_rela_relocations + && (exp->X_add_number & ~(((addressT) 2 << 31) - 1)) == 0) + exp->X_add_number + = (exp->X_add_number ^ ((addressT) 1 << 31)) - ((addressT) 1 << 31); } #if (defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT)) else if (OUTPUT_FLAVOR == bfd_target_aout_flavour @@ -4131,6 +4643,11 @@ i386_immediate (imm_start) return 0; } #endif + else if (!intel_syntax && exp->X_op == O_register) + { + as_bad (_("illegal immediate register operand %s"), imm_start); + return 0; + } else { /* This is an address. The size of the address will be @@ -4143,11 +4660,8 @@ i386_immediate (imm_start) return 1; } -static char *i386_scale PARAMS ((char *)); - static char * -i386_scale (scale) - char *scale; +i386_scale (char *scale) { offsetT val; char *save = input_line_pointer; @@ -4194,12 +4708,8 @@ i386_scale (scale) return scale; } -static int i386_displacement PARAMS ((char *, char *)); - static int -i386_displacement (disp_start, disp_end) - char *disp_start; - char *disp_end; +i386_displacement (char *disp_start, char *disp_end) { expressionS *exp; segT exp_seg = 0; @@ -4208,6 +4718,13 @@ i386_displacement (disp_start, disp_end) int bigdisp, override; unsigned int types = Disp; + if (i.disp_operands == MAX_MEMORY_OPERANDS) + { + as_bad (_("at most %d displacement operands are allowed"), + MAX_MEMORY_OPERANDS); + return 0; + } + if ((i.types[this_operand] & JumpAbsolute) || !(current_templates->start->opcode_modifier & (Jump | JumpDword))) { @@ -4224,9 +4741,9 @@ i386_displacement (disp_start, disp_end) if (flag_code == CODE_64BIT) { if (!bigdisp) - bigdisp = (override || i.suffix == WORD_MNEM_SUFFIX) - ? Disp16 - : Disp32S | Disp32; + bigdisp = ((override || i.suffix == WORD_MNEM_SUFFIX) + ? Disp16 + : Disp32S | Disp32); else if (!override) bigdisp = Disp64 | Disp32S | Disp32; } @@ -4374,14 +4891,11 @@ i386_displacement (disp_start, disp_end) return 1; } -static int i386_index_check PARAMS ((const char *)); - /* Make sure the memory operand we've been dealt is valid. Return 1 on success, 0 on a failure. */ static int -i386_index_check (operand_string) - const char *operand_string; +i386_index_check (const char *operand_string) { int ok; #if INFER_ADDR_PREFIX @@ -4403,9 +4917,9 @@ i386_index_check (operand_string) else if (flag_code == CODE_64BIT) RegXX = i.prefix[ADDR_PREFIX] == 0 ? Reg64 : Reg32; else - RegXX = (flag_code == CODE_16BIT) ^ (i.prefix[ADDR_PREFIX] != 0) - ? Reg16 - : Reg32; + RegXX = ((flag_code == CODE_16BIT) ^ (i.prefix[ADDR_PREFIX] != 0) + ? Reg16 + : Reg32); if (!i.base_reg || !(i.base_reg->reg_type & Acc) || !(i.base_reg->reg_type & RegXX) @@ -4414,17 +4928,17 @@ i386_index_check (operand_string) ok = 0; } else if (flag_code == CODE_64BIT) - { - unsigned RegXX = (i.prefix[ADDR_PREFIX] == 0 ? Reg64 : Reg32); - - if ((i.base_reg - && ((i.base_reg->reg_type & RegXX) == 0) - && (i.base_reg->reg_type != BaseIndex - || i.index_reg)) - || (i.index_reg - && ((i.index_reg->reg_type & (RegXX | BaseIndex)) - != (RegXX | BaseIndex)))) - ok = 0; + { + unsigned RegXX = (i.prefix[ADDR_PREFIX] == 0 ? Reg64 : Reg32); + + if ((i.base_reg + && ((i.base_reg->reg_type & RegXX) == 0) + && (i.base_reg->reg_type != BaseIndex + || i.index_reg)) + || (i.index_reg + && ((i.index_reg->reg_type & (RegXX | BaseIndex)) + != (RegXX | BaseIndex)))) + ok = 0; } else { @@ -4466,8 +4980,9 @@ i386_index_check (operand_string) FIXME. There doesn't seem to be any real need for separate Disp16 and Disp32 flags. The same goes for Imm16 and Imm32. Removing them would probably clean up the code quite a lot. */ - if (flag_code != CODE_64BIT && (i.types[this_operand] & (Disp16 | Disp32))) - i.types[this_operand] ^= (Disp16 | Disp32); + if (flag_code != CODE_64BIT + && (i.types[this_operand] & (Disp16 | Disp32))) + i.types[this_operand] ^= (Disp16 | Disp32); fudged = 1; goto tryprefix; } @@ -4487,8 +5002,7 @@ i386_index_check (operand_string) on error. */ static int -i386_operand (operand_string) - char *operand_string; +i386_operand (char *operand_string) { const reg_entry *r; char *end_op; @@ -4646,7 +5160,8 @@ i386_operand (operand_string) ++base_string; if (*base_string == ',' - || ((i.base_reg = parse_register (base_string, &end_op)) != NULL)) + || ((i.base_reg = parse_register (base_string, &end_op)) + != NULL)) { displacement_string_end = temp_string; @@ -4666,7 +5181,8 @@ i386_operand (operand_string) if (is_space_char (*base_string)) ++base_string; - if ((i.index_reg = parse_register (base_string, &end_op)) != NULL) + if ((i.index_reg = parse_register (base_string, &end_op)) + != NULL) { base_string = end_op; if (is_space_char (*base_string)) @@ -4679,7 +5195,8 @@ i386_operand (operand_string) } else if (*base_string != ')') { - as_bad (_("expecting `,' or `)' after index register in `%s'"), + as_bad (_("expecting `,' or `)' " + "after index register in `%s'"), operand_string); return 0; } @@ -4703,21 +5220,24 @@ i386_operand (operand_string) ++base_string; if (*base_string != ')') { - as_bad (_("expecting `)' after scale factor in `%s'"), + as_bad (_("expecting `)' " + "after scale factor in `%s'"), operand_string); return 0; } } else if (!i.index_reg) { - as_bad (_("expecting index register or scale factor after `,'; got '%c'"), + as_bad (_("expecting index register or scale factor " + "after `,'; got '%c'"), *base_string); return 0; } } else if (*base_string != ')') { - as_bad (_("expecting `,' or `)' after base register in `%s'"), + as_bad (_("expecting `,' or `)' " + "after base register in `%s'"), operand_string); return 0; } @@ -4933,7 +5453,8 @@ md_convert_frag (abfd, sec, fragP) { if (no_cond_jump_promotion && TYPE_FROM_RELAX_STATE (fragP->fr_subtype) != UNCOND_JUMP) - as_warn_where (fragP->fr_file, fragP->fr_line, _("long jump required")); + as_warn_where (fragP->fr_file, fragP->fr_line, + _("long jump required")); switch (fragP->fr_subtype) { @@ -4984,8 +5505,8 @@ md_convert_frag (abfd, sec, fragP) if (DISP_SIZE_FROM_RELAX_STATE (fragP->fr_subtype) == 4 && object_64bit && ((addressT) (displacement_from_opcode_start - extension - + ((addressT) 1 << 31)) - > (((addressT) 2 << 31) - 1))) + + ((addressT) 1 << 31)) + > (((addressT) 2 << 31) - 1))) { as_bad_where (fragP->fr_file, fragP->fr_line, _("jump target out of range")); @@ -5250,16 +5771,17 @@ md_atof (type, litP, sizeP) return 0; } -static char output_invalid_buf[8]; +static char output_invalid_buf[sizeof (unsigned char) * 2 + 6]; static char * -output_invalid (c) - int c; +output_invalid (int c) { if (ISPRINT (c)) - sprintf (output_invalid_buf, "'%c'", c); + snprintf (output_invalid_buf, sizeof (output_invalid_buf), + "'%c'", c); else - sprintf (output_invalid_buf, "(0x%x)", (unsigned) c); + snprintf (output_invalid_buf, sizeof (output_invalid_buf), + "(0x%x)", (unsigned char) c); return output_invalid_buf; } @@ -5310,14 +5832,16 @@ parse_real_register (char *reg_string, char **end_op) ++s; if (*s >= '0' && *s <= '7') { - r = &i386_float_regtab[*s - '0']; + int fpr = *s - '0'; ++s; if (is_space_char (*s)) ++s; if (*s == ')') { *end_op = s + 1; - return r; + r = hash_find (reg_hash, "st(0)"); + know (r); + return r + fpr; } } /* We have "%st(" then garbage. */ @@ -5359,7 +5883,8 @@ parse_register (char *reg_string, char **end_op) const expressionS *e = symbol_get_value_expression (symbolP); know (e->X_op == O_register); - know (e->X_add_number >= 0 && (valueT) e->X_add_number < ARRAY_SIZE (i386_regtab)); + know (e->X_add_number >= 0 + && (valueT) e->X_add_number < i386_regtab_size); r = i386_regtab + e->X_add_number; *end_op = input_line_pointer; } @@ -5417,22 +5942,27 @@ const char *md_shortopts = "qn"; #define OPTION_32 (OPTION_MD_BASE + 0) #define OPTION_64 (OPTION_MD_BASE + 1) #define OPTION_DIVIDE (OPTION_MD_BASE + 2) +#define OPTION_MARCH (OPTION_MD_BASE + 3) +#define OPTION_MTUNE (OPTION_MD_BASE + 4) -struct option md_longopts[] = { +struct option md_longopts[] = +{ {"32", no_argument, NULL, OPTION_32}, -#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) || defined(TE_PEP) {"64", no_argument, NULL, OPTION_64}, #endif {"divide", no_argument, NULL, OPTION_DIVIDE}, + {"march", required_argument, NULL, OPTION_MARCH}, + {"mtune", required_argument, NULL, OPTION_MTUNE}, {NULL, no_argument, NULL, 0} }; size_t md_longopts_size = sizeof (md_longopts); int -md_parse_option (c, arg) - int c; - char *arg ATTRIBUTE_UNUSED; +md_parse_option (int c, char *arg) { + unsigned int i; + switch (c) { case 'n': @@ -5462,14 +5992,18 @@ md_parse_option (c, arg) /* -s: On i386 Solaris, this tells the native assembler to use .stab instead of .stab.excl. We always use .stab anyhow. */ break; - +#endif +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) || defined(TE_PEP) case OPTION_64: { const char **list, **l; list = bfd_target_list (); for (l = list; *l != NULL; l++) - if (strcmp (*l, "elf64-x86-64") == 0) + if (CONST_STRNEQ (*l, "elf64-x86-64") + || strcmp (*l, "coff-x86-64") == 0 + || strcmp (*l, "pe-x86-64") == 0 + || strcmp (*l, "pei-x86-64") == 0) { default_arch = "x86_64"; break; @@ -5502,6 +6036,44 @@ md_parse_option (c, arg) #endif break; + case OPTION_MARCH: + if (*arg == '.') + as_fatal (_("Invalid -march= option: `%s'"), arg); + for (i = 0; i < ARRAY_SIZE (cpu_arch); i++) + { + if (strcmp (arg, cpu_arch [i].name) == 0) + { + cpu_arch_isa = cpu_arch[i].type; + cpu_arch_isa_flags = cpu_arch[i].flags; + if (!cpu_arch_tune_set) + { + cpu_arch_tune = cpu_arch_isa; + cpu_arch_tune_flags = cpu_arch_isa_flags; + } + break; + } + } + if (i >= ARRAY_SIZE (cpu_arch)) + as_fatal (_("Invalid -march= option: `%s'"), arg); + break; + + case OPTION_MTUNE: + if (*arg == '.') + as_fatal (_("Invalid -mtune= option: `%s'"), arg); + for (i = 0; i < ARRAY_SIZE (cpu_arch); i++) + { + if (strcmp (arg, cpu_arch [i].name) == 0) + { + cpu_arch_tune_set = 1; + cpu_arch_tune = cpu_arch [i].type; + cpu_arch_tune_flags = cpu_arch[i].flags; + break; + } + } + if (i >= ARRAY_SIZE (cpu_arch)) + as_fatal (_("Invalid -mtune= option: `%s'"), arg); + break; + default: return 0; } @@ -5525,6 +6097,10 @@ md_show_usage (stream) fprintf (stream, _("\ -s ignored\n")); #endif +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) || defined(TE_PEP) + fprintf (stream, _("\ + --32/--64 generate 32bit/64bit code\n")); +#endif #ifdef SVR4_COMMENT_CHARS fprintf (stream, _("\ --divide do not treat `/' as a comment character\n")); @@ -5532,7 +6108,32 @@ md_show_usage (stream) fprintf (stream, _("\ --divide ignored\n")); #endif + fprintf (stream, _("\ + -march=CPU/-mtune=CPU generate code/optimize for CPU, where CPU is one of:\n\ + i386, i486, pentium, pentiumpro, pentium4, nocona,\n\ + core, core2, k6, athlon, k8, generic32, generic64\n")); + +} + +#if defined(TE_PEP) +const char * +x86_64_target_format (void) +{ + if (strcmp (default_arch, "x86_64") == 0) + { + set_code_flag (CODE_64BIT); + return COFF_TARGET_FORMAT; + } + else if (strcmp (default_arch, "i386") == 0) + { + set_code_flag (CODE_32BIT); + return "coff-i386"; + } + + as_fatal (_("Unknown architecture")); + return NULL; } +#endif #if ((defined (OBJ_MAYBE_COFF) && defined (OBJ_MAYBE_AOUT)) \ || defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)) @@ -5540,12 +6141,28 @@ md_show_usage (stream) /* Pick the target format to use. */ const char * -i386_target_format () +i386_target_format (void) { if (!strcmp (default_arch, "x86_64")) - set_code_flag (CODE_64BIT); + { + set_code_flag (CODE_64BIT); + if (cpu_arch_isa_flags == 0) + cpu_arch_isa_flags = Cpu186|Cpu286|Cpu386|Cpu486 + |Cpu586|Cpu686|CpuP4|CpuMMX|CpuMMX2 + |CpuSSE|CpuSSE2; + if (cpu_arch_tune_flags == 0) + cpu_arch_tune_flags = Cpu186|Cpu286|Cpu386|Cpu486 + |Cpu586|Cpu686|CpuP4|CpuMMX|CpuMMX2 + |CpuSSE|CpuSSE2; + } else if (!strcmp (default_arch, "i386")) - set_code_flag (CODE_32BIT); + { + set_code_flag (CODE_32BIT); + if (cpu_arch_isa_flags == 0) + cpu_arch_isa_flags = Cpu186|Cpu286|Cpu386; + if (cpu_arch_tune_flags == 0) + cpu_arch_tune_flags = Cpu186|Cpu286|Cpu386; + } else as_fatal (_("Unknown architecture")); switch (OUTPUT_FLAVOR) @@ -5566,7 +6183,7 @@ i386_target_format () object_64bit = 1; use_rela_relocations = 1; } - return flag_code == CODE_64BIT ? "elf64-x86-64" : ELF_TARGET_FORMAT; + return flag_code == CODE_64BIT ? ELF_TARGET_FORMAT64 : ELF_TARGET_FORMAT; } #endif default: @@ -5578,7 +6195,8 @@ i386_target_format () #endif /* OBJ_MAYBE_ more than one */ #if (defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)) -void i386_elf_emit_arch_note () +void +i386_elf_emit_arch_note (void) { if (IS_ELF && cpu_arch_name != NULL) { @@ -5669,8 +6287,7 @@ md_section_align (segment, size) size, since the offset is always the last part of the insn. */ long -md_pcrel_from (fixP) - fixS *fixP; +md_pcrel_from (fixS *fixP) { return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address; } @@ -5678,8 +6295,7 @@ md_pcrel_from (fixP) #ifndef I386COFF static void -s_bss (ignore) - int ignore ATTRIBUTE_UNUSED; +s_bss (int ignore ATTRIBUTE_UNUSED) { int temp; @@ -5695,8 +6311,7 @@ s_bss (ignore) #endif void -i386_validate_fix (fixp) - fixS *fixp; +i386_validate_fix (fixS *fixp) { if (fixp->fx_subsy && fixp->fx_subsy == GOT_symbol) { @@ -6045,8 +6660,8 @@ struct intel_parser_s int got_a_float; /* Whether the operand is a float. */ int op_modifier; /* Operand modifier. */ int is_mem; /* 1 if operand is memory reference. */ - int in_offset; /* >=1 if parsing operand of offset. */ - int in_bracket; /* >=1 if parsing operand in brackets. */ + int in_offset; /* >=1 if parsing operand of offset. */ + int in_bracket; /* >=1 if parsing operand in brackets. */ const reg_entry *reg; /* Last register reference found. */ char *disp; /* Displacement string being built. */ char *next_operand; /* Resume point when splitting operands. */ @@ -6085,22 +6700,19 @@ static struct intel_token cur_token, prev_token; #define T_SHR 15 /* Prototypes for intel parser functions. */ -static int intel_match_token PARAMS ((int code)); -static void intel_get_token PARAMS ((void)); -static void intel_putback_token PARAMS ((void)); -static int intel_expr PARAMS ((void)); -static int intel_e04 PARAMS ((void)); -static int intel_e05 PARAMS ((void)); -static int intel_e06 PARAMS ((void)); -static int intel_e09 PARAMS ((void)); -static int intel_bracket_expr PARAMS ((void)); -static int intel_e10 PARAMS ((void)); -static int intel_e11 PARAMS ((void)); +static int intel_match_token (int); +static void intel_putback_token (void); +static void intel_get_token (void); +static int intel_expr (void); +static int intel_e04 (void); +static int intel_e05 (void); +static int intel_e06 (void); +static int intel_e09 (void); +static int intel_e10 (void); +static int intel_e11 (void); static int -i386_intel_operand (operand_string, got_a_float) - char *operand_string; - int got_a_float; +i386_intel_operand (char *operand_string, int got_a_float) { int ret; char *p; @@ -6190,7 +6802,7 @@ i386_intel_operand (operand_string, got_a_float) ret = i386_immediate (intel_parser.disp); if (intel_parser.next_operand && this_operand >= MAX_OPERANDS - 1) - ret = 0; + ret = 0; if (!ret || !intel_parser.next_operand) break; intel_parser.op_string = intel_parser.next_operand; @@ -6210,7 +6822,7 @@ i386_intel_operand (operand_string, got_a_float) expr' cmpOp e04 expr' | Empty */ static int -intel_expr () +intel_expr (void) { /* XXX Implement the comparison operators. */ return intel_e04 (); @@ -6221,7 +6833,7 @@ intel_expr () e04' addOp e05 e04' | Empty */ static int -intel_e04 () +intel_e04 (void) { int nregs = -1; @@ -6250,7 +6862,7 @@ intel_e04 () e05' binOp e06 e05' | Empty */ static int -intel_e05 () +intel_e05 (void) { int nregs = ~NUM_ADDRESS_REGS; @@ -6259,7 +6871,9 @@ intel_e05 () if (!intel_e06()) return 0; - if (cur_token.code == '&' || cur_token.code == '|' || cur_token.code == '^') + if (cur_token.code == '&' + || cur_token.code == '|' + || cur_token.code == '^') { char str[2]; @@ -6285,7 +6899,7 @@ intel_e05 () e06' mulOp e09 e06' | Empty */ static int -intel_e06 () +intel_e06 (void) { int nregs = ~NUM_ADDRESS_REGS; @@ -6294,7 +6908,9 @@ intel_e06 () if (!intel_e09()) return 0; - if (cur_token.code == '*' || cur_token.code == '/' || cur_token.code == '%') + if (cur_token.code == '*' + || cur_token.code == '/' + || cur_token.code == '%') { char str[2]; @@ -6309,7 +6925,7 @@ intel_e06 () else break; - intel_match_token (cur_token.code); + intel_match_token (cur_token.code); if (nregs < 0) nregs = ~nregs; @@ -6331,7 +6947,7 @@ intel_e06 () | : e10 e09' | Empty */ static int -intel_e09 () +intel_e09 (void) { int nregs = ~NUM_ADDRESS_REGS; int in_offset = 0; @@ -6545,7 +7161,7 @@ intel_e09 () } static int -intel_bracket_expr () +intel_bracket_expr (void) { int was_offset = intel_parser.op_modifier & (1 << T_OFFSET); const char *start = intel_parser.op_string; @@ -6581,7 +7197,7 @@ intel_bracket_expr () intel_parser.op_modifier &= ~was_offset; } else - strcat (intel_parser.disp, "["); + strcat (intel_parser.disp, "["); /* Add a '+' to the displacement string if necessary. */ if (*intel_parser.disp != '\0' @@ -6606,7 +7222,8 @@ intel_bracket_expr () /* Defer the warning until all of the operand was parsed. */ intel_parser.is_mem = -1; else if (!quiet_warnings) - as_warn (_("`[%.*s]' taken to mean just `%.*s'"), len, start, len, start); + as_warn (_("`[%.*s]' taken to mean just `%.*s'"), + len, start, len, start); } } intel_parser.op_modifier |= was_offset; @@ -6621,7 +7238,7 @@ intel_bracket_expr () e10' [ expr ] e10' | Empty */ static int -intel_e10 () +intel_e10 (void) { if (!intel_e11 ()) return 0; @@ -6651,7 +7268,7 @@ intel_e10 () | id | constant */ static int -intel_e11 () +intel_e11 (void) { switch (cur_token.code) { @@ -6696,7 +7313,8 @@ intel_e11 () { if (!(reg->reg_type & (SReg2 | SReg3))) { - as_bad (_("`%s' is not a valid segment register"), reg->reg_name); + as_bad (_("`%s' is not a valid segment register"), + reg->reg_name); return 0; } else if (i.seg[i.mem_operands]) @@ -6886,7 +7504,8 @@ intel_e11 () /* Get the next token to check for register scaling. */ intel_match_token (cur_token.code); - /* Check if this constant is a scaling factor for an index register. */ + /* Check if this constant is a scaling factor for an + index register. */ if (cur_token.code == '*') { if (intel_match_token ('*') && cur_token.code == T_REG) @@ -6895,14 +7514,17 @@ intel_e11 () if (!intel_parser.in_bracket) { - as_bad (_("Register scaling only allowed in memory operands")); + as_bad (_("Register scaling only allowed " + "in memory operands")); return 0; } - if (reg->reg_type & Reg16) /* Disallow things like [1*si]. */ - reg = i386_regtab + REGNAM_AX + 4; /* sp is invalid as index */ + /* Disallow things like [1*si]. + sp and esp are invalid as index. */ + if (reg->reg_type & Reg16) + reg = i386_regtab + REGNAM_AX + 4; else if (i.index_reg) - reg = i386_regtab + REGNAM_EAX + 4; /* esp is invalid as index */ + reg = i386_regtab + REGNAM_EAX + 4; /* The constant is followed by `* reg', so it must be a valid scale. */ @@ -6952,8 +7574,7 @@ intel_e11 () /* Match the given token against cur_token. If they match, read the next token from the operand string. */ static int -intel_match_token (code) - int code; +intel_match_token (int code) { if (cur_token.code == code) { @@ -6969,7 +7590,7 @@ intel_match_token (code) /* Read a new token from intel_parser.op_string and store it in cur_token. */ static void -intel_get_token () +intel_get_token (void) { char *end_op; const reg_entry *reg; @@ -7154,7 +7775,7 @@ intel_get_token () /* Put cur_token back into the token stream and make cur_token point to prev_token. */ static void -intel_putback_token () +intel_putback_token (void) { if (cur_token.code != T_NIL) { @@ -7170,7 +7791,7 @@ intel_putback_token () } int -tc_x86_regname_to_dw2regnum (const char *regname) +tc_x86_regname_to_dw2regnum (char *regname) { unsigned int regnum; unsigned int regnames_count; @@ -7281,16 +7902,16 @@ x86_64_section_letter (int letter, char **ptr_msg) return SHF_X86_64_LARGE; *ptr_msg = _("Bad .section directive: want a,l,w,x,M,S,G,T in string"); - } + } else - *ptr_msg = _("Bad .section directive: want a,w,x,M,S,G,T in string"); + *ptr_msg = _("Bad .section directive: want a,w,x,M,S,G,T in string"); return -1; } int x86_64_section_word (char *str, size_t len) { - if (len == 5 && flag_code == CODE_64BIT && strncmp (str, "large", 5) == 0) + if (len == 5 && flag_code == CODE_64BIT && CONST_STRNEQ (str, "large")) return SHF_X86_64_LARGE; return -1; diff --git a/contrib/binutils/gas/config/tc-i386.h b/contrib/binutils/gas/config/tc-i386.h index 9851704..51638b0 100644 --- a/contrib/binutils/gas/config/tc-i386.h +++ b/contrib/binutils/gas/config/tc-i386.h @@ -1,6 +1,6 @@ /* tc-i386.h -- Header file for tc-i386.c Copyright 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, - 2001, 2002, 2003, 2004, 2005 + 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GAS, the GNU Assembler. @@ -23,6 +23,8 @@ #ifndef TC_I386 #define TC_I386 1 +#include "opcodes/i386-opc.h" + struct fix; #define TARGET_BYTES_BIG_ENDIAN 0 @@ -55,6 +57,7 @@ extern unsigned long i386_mach (void); #ifdef TE_FreeBSD #define ELF_TARGET_FORMAT "elf32-i386-freebsd" +#define ELF_TARGET_FORMAT64 "elf64-x86-64-freebsd" #elif defined (TE_VXWORKS) #define ELF_TARGET_FORMAT "elf32-i386-vxworks" #endif @@ -63,9 +66,13 @@ extern unsigned long i386_mach (void); #define ELF_TARGET_FORMAT "elf32-i386" #endif +#ifndef ELF_TARGET_FORMAT64 +#define ELF_TARGET_FORMAT64 "elf64-x86-64" +#endif + #if ((defined (OBJ_MAYBE_COFF) && defined (OBJ_MAYBE_AOUT)) \ || defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)) -extern const char *i386_target_format PARAMS ((void)); +extern const char *i386_target_format (void); #define TARGET_FORMAT i386_target_format () #else #ifdef OBJ_ELF @@ -78,7 +85,7 @@ extern const char *i386_target_format PARAMS ((void)); #if (defined (OBJ_MAYBE_ELF) || defined (OBJ_ELF)) #define md_end i386_elf_emit_arch_note -extern void i386_elf_emit_arch_note PARAMS ((void)); +extern void i386_elf_emit_arch_note (void); #endif #define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) 0 @@ -91,18 +98,16 @@ extern const char extra_symbol_chars[]; extern const char *i386_comment_chars; #define tc_comment_chars i386_comment_chars -#define MAX_OPERANDS 3 /* max operands per insn */ -#define MAX_IMMEDIATE_OPERANDS 2/* max immediates per insn (lcall, ljmp) */ -#define MAX_MEMORY_OPERANDS 2 /* max memory refs per insn (string ops) */ - /* Prefixes will be emitted in the order defined below. WAIT_PREFIX must be the first prefix since FWAIT is really is an - instruction, and so must come before any prefixes. */ + instruction, and so must come before any prefixes. + The preferred prefix order is SEG_PREFIX, ADDR_PREFIX, DATA_PREFIX, + LOCKREP_PREFIX. */ #define WAIT_PREFIX 0 -#define LOCKREP_PREFIX 1 +#define SEG_PREFIX 1 #define ADDR_PREFIX 2 #define DATA_PREFIX 3 -#define SEG_PREFIX 4 +#define LOCKREP_PREFIX 4 #define REX_PREFIX 5 /* must come last. */ #define MAX_PREFIXES 6 /* max prefixes per opcode */ @@ -111,21 +116,6 @@ extern const char *i386_comment_chars; #define IMMEDIATE_PREFIX '$' #define ABSOLUTE_PREFIX '*' -#define TWO_BYTE_OPCODE_ESCAPE 0x0f -#define NOP_OPCODE (char) 0x90 - -/* register numbers */ -#define EBP_REG_NUM 5 -#define ESP_REG_NUM 4 - -/* modrm_byte.regmem for twobyte escape */ -#define ESCAPE_TO_TWO_BYTE_ADDRESSING ESP_REG_NUM -/* index_base_byte.index for no index register addressing */ -#define NO_INDEX_REGISTER ESP_REG_NUM -/* index_base_byte.base for no base register addressing */ -#define NO_BASE_REGISTER EBP_REG_NUM -#define NO_BASE_REGISTER_16 6 - /* these are the instruction mnemonic suffixes. */ #define WORD_MNEM_SUFFIX 'w' #define BYTE_MNEM_SUFFIX 'b' @@ -135,184 +125,8 @@ extern const char *i386_comment_chars; /* Intel Syntax */ #define LONG_DOUBLE_MNEM_SUFFIX 'x' -/* modrm.mode = REGMEM_FIELD_HAS_REG when a register is in there */ -#define REGMEM_FIELD_HAS_REG 0x3/* always = 0x3 */ -#define REGMEM_FIELD_HAS_MEM (~REGMEM_FIELD_HAS_REG) - #define END_OF_INSN '\0' -typedef struct -{ - /* instruction name sans width suffix ("mov" for movl insns) */ - char *name; - - /* how many operands */ - unsigned int operands; - - /* base_opcode is the fundamental opcode byte without optional - prefix(es). */ - unsigned int base_opcode; - - /* extension_opcode is the 3 bit extension for group <n> insns. - This field is also used to store the 8-bit opcode suffix for the - AMD 3DNow! instructions. - If this template has no extension opcode (the usual case) use None */ - unsigned int extension_opcode; -#define None 0xffff /* If no extension_opcode is possible. */ - - /* cpu feature flags */ - unsigned int cpu_flags; -#define Cpu086 0x1 /* Any old cpu will do, 0 does the same */ -#define Cpu186 0x2 /* i186 or better required */ -#define Cpu286 0x4 /* i286 or better required */ -#define Cpu386 0x8 /* i386 or better required */ -#define Cpu486 0x10 /* i486 or better required */ -#define Cpu586 0x20 /* i585 or better required */ -#define Cpu686 0x40 /* i686 or better required */ -#define CpuP4 0x80 /* Pentium4 or better required */ -#define CpuK6 0x100 /* AMD K6 or better required*/ -#define CpuAthlon 0x200 /* AMD Athlon or better required*/ -#define CpuSledgehammer 0x400 /* Sledgehammer or better required */ -#define CpuMMX 0x800 /* MMX support required */ -#define CpuMMX2 0x1000 /* extended MMX support (with SSE or 3DNow!Ext) required */ -#define CpuSSE 0x2000 /* Streaming SIMD extensions required */ -#define CpuSSE2 0x4000 /* Streaming SIMD extensions 2 required */ -#define Cpu3dnow 0x8000 /* 3dnow! support required */ -#define Cpu3dnowA 0x10000 /* 3dnow!Extensions support required */ -#define CpuSSE3 0x20000 /* Streaming SIMD extensions 3 required */ -#define CpuPNI CpuSSE3 /* Prescott New Instructions required */ -#define CpuPadLock 0x40000 /* VIA PadLock required */ -#define CpuSVME 0x80000 /* AMD Secure Virtual Machine Ext-s required */ -#define CpuVMX 0x100000 /* VMX Instructions required */ -#define CpuMNI 0x200000 /* Merom New Instructions required */ - - /* These flags are set by gas depending on the flag_code. */ -#define Cpu64 0x4000000 /* 64bit support required */ -#define CpuNo64 0x8000000 /* Not supported in the 64bit mode */ - - /* The default value for unknown CPUs - enable all features to avoid problems. */ -#define CpuUnknownFlags (Cpu086|Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686 \ - |CpuP4|CpuSledgehammer|CpuMMX|CpuMMX2|CpuSSE|CpuSSE2|CpuPNI|CpuVMX \ - |Cpu3dnow|Cpu3dnowA|CpuK6|CpuAthlon|CpuPadLock|CpuSVME|CpuMNI) - - /* the bits in opcode_modifier are used to generate the final opcode from - the base_opcode. These bits also are used to detect alternate forms of - the same instruction */ - unsigned int opcode_modifier; - - /* opcode_modifier bits: */ -#define W 0x1 /* set if operands can be words or dwords - encoded the canonical way */ -#define D 0x2 /* D = 0 if Reg --> Regmem; - D = 1 if Regmem --> Reg: MUST BE 0x2 */ -#define Modrm 0x4 -#define FloatR 0x8 /* src/dest swap for floats: MUST BE 0x8 */ -#define ShortForm 0x10 /* register is in low 3 bits of opcode */ -#define FloatMF 0x20 /* FP insn memory format bit, sized by 0x4 */ -#define Jump 0x40 /* special case for jump insns. */ -#define JumpDword 0x80 /* call and jump */ -#define JumpByte 0x100 /* loop and jecxz */ -#define JumpInterSegment 0x200 /* special case for intersegment leaps/calls */ -#define FloatD 0x400 /* direction for float insns: MUST BE 0x400 */ -#define Seg2ShortForm 0x800 /* encoding of load segment reg insns */ -#define Seg3ShortForm 0x1000 /* fs/gs segment register insns. */ -#define Size16 0x2000 /* needs size prefix if in 32-bit mode */ -#define Size32 0x4000 /* needs size prefix if in 16-bit mode */ -#define Size64 0x8000 /* needs size prefix if in 16-bit mode */ -#define IgnoreSize 0x10000 /* instruction ignores operand size prefix */ -#define DefaultSize 0x20000 /* default insn size depends on mode */ -#define No_bSuf 0x40000 /* b suffix on instruction illegal */ -#define No_wSuf 0x80000 /* w suffix on instruction illegal */ -#define No_lSuf 0x100000 /* l suffix on instruction illegal */ -#define No_sSuf 0x200000 /* s suffix on instruction illegal */ -#define No_qSuf 0x400000 /* q suffix on instruction illegal */ -#define No_xSuf 0x800000 /* x suffix on instruction illegal */ -#define FWait 0x1000000 /* instruction needs FWAIT */ -#define IsString 0x2000000 /* quick test for string instructions */ -#define regKludge 0x4000000 /* fake an extra reg operand for clr, imul */ -#define IsPrefix 0x8000000 /* opcode is a prefix */ -#define ImmExt 0x10000000 /* instruction has extension in 8 bit imm */ -#define NoRex64 0x20000000 /* instruction don't need Rex64 prefix. */ -#define Rex64 0x40000000 /* instruction require Rex64 prefix. */ -#define Ugh 0x80000000 /* deprecated fp insn, gets a warning */ - - /* operand_types[i] describes the type of operand i. This is made - by OR'ing together all of the possible type masks. (e.g. - 'operand_types[i] = Reg|Imm' specifies that operand i can be - either a register or an immediate operand. */ - unsigned int operand_types[3]; - - /* operand_types[i] bits */ - /* register */ -#define Reg8 0x1 /* 8 bit reg */ -#define Reg16 0x2 /* 16 bit reg */ -#define Reg32 0x4 /* 32 bit reg */ -#define Reg64 0x8 /* 64 bit reg */ - /* immediate */ -#define Imm8 0x10 /* 8 bit immediate */ -#define Imm8S 0x20 /* 8 bit immediate sign extended */ -#define Imm16 0x40 /* 16 bit immediate */ -#define Imm32 0x80 /* 32 bit immediate */ -#define Imm32S 0x100 /* 32 bit immediate sign extended */ -#define Imm64 0x200 /* 64 bit immediate */ -#define Imm1 0x400 /* 1 bit immediate */ - /* memory */ -#define BaseIndex 0x800 - /* Disp8,16,32 are used in different ways, depending on the - instruction. For jumps, they specify the size of the PC relative - displacement, for baseindex type instructions, they specify the - size of the offset relative to the base register, and for memory - offset instructions such as `mov 1234,%al' they specify the size of - the offset relative to the segment base. */ -#define Disp8 0x1000 /* 8 bit displacement */ -#define Disp16 0x2000 /* 16 bit displacement */ -#define Disp32 0x4000 /* 32 bit displacement */ -#define Disp32S 0x8000 /* 32 bit signed displacement */ -#define Disp64 0x10000 /* 64 bit displacement */ - /* specials */ -#define InOutPortReg 0x20000 /* register to hold in/out port addr = dx */ -#define ShiftCount 0x40000 /* register to hold shift cound = cl */ -#define Control 0x80000 /* Control register */ -#define Debug 0x100000 /* Debug register */ -#define Test 0x200000 /* Test register */ -#define FloatReg 0x400000 /* Float register */ -#define FloatAcc 0x800000 /* Float stack top %st(0) */ -#define SReg2 0x1000000 /* 2 bit segment register */ -#define SReg3 0x2000000 /* 3 bit segment register */ -#define Acc 0x4000000 /* Accumulator %al or %ax or %eax */ -#define JumpAbsolute 0x8000000 -#define RegMMX 0x10000000 /* MMX register */ -#define RegXMM 0x20000000 /* XMM registers in PIII */ -#define EsSeg 0x40000000 /* String insn operand with fixed es segment */ - - /* InvMem is for instructions with a modrm byte that only allow a - general register encoding in the i.tm.mode and i.tm.regmem fields, - eg. control reg moves. They really ought to support a memory form, - but don't, so we add an InvMem flag to the register operand to - indicate that it should be encoded in the i.tm.regmem field. */ -#define InvMem 0x80000000 - -#define Reg (Reg8|Reg16|Reg32|Reg64) /* gen'l register */ -#define WordReg (Reg16|Reg32|Reg64) -#define ImplicitRegister (InOutPortReg|ShiftCount|Acc|FloatAcc) -#define Imm (Imm8|Imm8S|Imm16|Imm32S|Imm32|Imm64) /* gen'l immediate */ -#define EncImm (Imm8|Imm16|Imm32|Imm32S) /* Encodable gen'l immediate */ -#define Disp (Disp8|Disp16|Disp32|Disp32S|Disp64) /* General displacement */ -#define AnyMem (Disp8|Disp16|Disp32|Disp32S|BaseIndex|InvMem) /* General memory */ - /* The following aliases are defined because the opcode table - carefully specifies the allowed memory types for each instruction. - At the moment we can only tell a memory reference size by the - instruction suffix, so there's not much point in defining Mem8, - Mem16, Mem32 and Mem64 opcode modifiers - We might as well just use - the suffix directly to check memory operands. */ -#define LLongMem AnyMem /* 64 bits (or more) */ -#define LongMem AnyMem /* 32 bit memory ref */ -#define ShortMem AnyMem /* 16 bit memory ref */ -#define WordMem AnyMem /* 16 or 32 bit memory ref */ -#define ByteMem AnyMem /* 8 bit memory ref */ -} -template; - /* 'templates' is for grouping together 'template' structures for opcodes of the same name. This is only used for storing the insns in the grand @@ -327,25 +141,6 @@ typedef struct } templates; -/* these are for register name --> number & type hash lookup */ -typedef struct -{ - char *reg_name; - unsigned int reg_type; - unsigned int reg_flags; -#define RegRex 0x1 /* Extended register. */ -#define RegRex64 0x2 /* Extended 8 bit register. */ - unsigned int reg_num; -} -reg_entry; - -typedef struct -{ - char *seg_name; - unsigned int seg_prefix; -} -seg_entry; - /* 386 operand encoding bytes: see 386 book for details of this. */ typedef struct { @@ -357,16 +152,6 @@ modrm_byte; /* x86-64 extension prefix. */ typedef int rex_byte; -#define REX_OPCODE 0x40 - -/* Indicates 64 bit operand size. */ -#define REX_MODE64 8 -/* High extension to reg field of modrm byte. */ -#define REX_EXTX 4 -/* High extension to SIB index field. */ -#define REX_EXTY 2 -/* High extension to base field of modrm or SIB, or reg field of opcode. */ -#define REX_EXTZ 1 /* 386 opcode byte to code indirect addressing. */ typedef struct @@ -377,11 +162,30 @@ typedef struct } sib_byte; -/* x86 arch names and features */ +enum processor_type +{ + PROCESSOR_UNKNOWN, + PROCESSOR_I486, + PROCESSOR_PENTIUM, + PROCESSOR_PENTIUMPRO, + PROCESSOR_PENTIUM4, + PROCESSOR_NOCONA, + PROCESSOR_CORE, + PROCESSOR_CORE2, + PROCESSOR_K6, + PROCESSOR_ATHLON, + PROCESSOR_K8, + PROCESSOR_GENERIC32, + PROCESSOR_GENERIC64, + PROCESSOR_AMDFAM10 +}; + +/* x86 arch names, types and features */ typedef struct { - const char *name; /* arch name */ - unsigned int flags; /* cpu feature flags */ + const char *name; /* arch name */ + enum processor_type type; /* arch type */ + unsigned int flags; /* cpu feature flags */ } arch_entry; @@ -393,22 +197,22 @@ arch_entry; #if (defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)) && !defined (LEX_AT) #define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) x86_cons (EXP, NBYTES) -extern void x86_cons PARAMS ((expressionS *, int)); +extern void x86_cons (expressionS *, int); #endif #define TC_CONS_FIX_NEW(FRAG,OFF,LEN,EXP) x86_cons_fix_new(FRAG, OFF, LEN, EXP) extern void x86_cons_fix_new - PARAMS ((fragS *, unsigned int, unsigned int, expressionS *)); + (fragS *, unsigned int, unsigned int, expressionS *); #define DIFF_EXPR_OK /* foo-. gets turned into PC relative relocs */ #define NO_RELOC BFD_RELOC_NONE -void i386_validate_fix PARAMS ((struct fix *)); +void i386_validate_fix (struct fix *); #define TC_VALIDATE_FIX(FIX,SEGTYPE,SKIP) i386_validate_fix(FIX) #define tc_fix_adjustable(X) tc_i386_fix_adjustable(X) -extern int tc_i386_fix_adjustable PARAMS ((struct fix *)); +extern int tc_i386_fix_adjustable (struct fix *); /* Values passed to md_apply_fix don't include the symbol value. */ #define MD_APPLY_SYM_VALUE(FIX) 0 @@ -432,7 +236,6 @@ extern int tc_i386_fix_adjustable PARAMS ((struct fix *)); #define TC_FORCE_RELOCATION_LOCAL(FIX) \ (!(FIX)->fx_pcrel \ - || (FIX)->fx_plt \ || (FIX)->fx_r_type == BFD_RELOC_386_PLT32 \ || (FIX)->fx_r_type == BFD_RELOC_386_GOT32 \ || (FIX)->fx_r_type == BFD_RELOC_386_GOTPC \ @@ -460,7 +263,7 @@ if ((n) \ #define MAX_MEM_FOR_RS_ALIGN_CODE 15 -extern void i386_align_code PARAMS ((fragS *, int)); +extern void i386_align_code (fragS *, int); #define HANDLE_ALIGN(fragP) \ if (fragP->fr_type == rs_align_code) \ @@ -468,16 +271,18 @@ if (fragP->fr_type == rs_align_code) \ - fragP->fr_address \ - fragP->fr_fix)); -void i386_print_statistics PARAMS ((FILE *)); +void i386_print_statistics (FILE *); #define tc_print_statistics i386_print_statistics #define md_number_to_chars number_to_chars_littleendian #ifdef SCO_ELF #define tc_init_after_args() sco_id () -extern void sco_id PARAMS ((void)); +extern void sco_id (void); #endif +#define WORKING_DOT_WORD 1 + /* We want .cfi_* pseudo-ops for generating unwind info. */ #define TARGET_USE_CFIPOP 1 @@ -488,17 +293,17 @@ extern int x86_cie_data_alignment; #define DWARF2_CIE_DATA_ALIGNMENT x86_cie_data_alignment #define tc_regname_to_dw2regnum tc_x86_regname_to_dw2regnum -extern int tc_x86_regname_to_dw2regnum PARAMS ((const char *regname)); +extern int tc_x86_regname_to_dw2regnum (char *); #define tc_cfi_frame_initial_instructions tc_x86_frame_initial_instructions -extern void tc_x86_frame_initial_instructions PARAMS ((void)); +extern void tc_x86_frame_initial_instructions (void); #define md_elf_section_type(str,len) i386_elf_section_type (str, len) -extern int i386_elf_section_type PARAMS ((const char *, size_t len)); +extern int i386_elf_section_type (const char *, size_t); /* Support for SHF_X86_64_LARGE */ -extern int x86_64_section_word PARAMS ((char *, size_t)); -extern int x86_64_section_letter PARAMS ((int letter, char **ptr_msg)); +extern int x86_64_section_word (char *, size_t); +extern int x86_64_section_letter (int, char **); #define md_elf_section_letter(LETTER, PTR_MSG) x86_64_section_letter (LETTER, PTR_MSG) #define md_elf_section_word(STR, LEN) x86_64_section_word (STR, LEN) diff --git a/contrib/binutils/gas/config/tc-ia64.c b/contrib/binutils/gas/config/tc-ia64.c index 426b60f..5ed9ba8 100644 --- a/contrib/binutils/gas/config/tc-ia64.c +++ b/contrib/binutils/gas/config/tc-ia64.c @@ -1,5 +1,5 @@ /* tc-ia64.c -- Assembler for the HP/Intel IA-64 architecture. - Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by David Mosberger-Tang <davidm@hpl.hp.com> @@ -5634,7 +5634,7 @@ declare_register_set (prefix, num_regs, base_regnum) for (i = 0; i < num_regs; ++i) { - sprintf (name, "%s%u", prefix, i); + snprintf (name, sizeof (name), "%s%u", prefix, i); declare_register (name, base_regnum + i); } } @@ -6691,7 +6691,7 @@ emit_one_bundle () int addr_mod; first = (md.curr_slot + NUM_SLOTS - md.num_slots_in_use) % NUM_SLOTS; - know (first >= 0 & first < NUM_SLOTS); + know (first >= 0 && first < NUM_SLOTS); n = MIN (3, md.num_slots_in_use); /* Determine template: user user_template if specified, best match @@ -6971,7 +6971,8 @@ emit_one_bundle () else as_fatal ("emit_one_bundle: unexpected dynamic op"); - sprintf (mnemonic, "%s.%c", idesc->name, "?imbfxx"[insn_unit]); + snprintf (mnemonic, sizeof (mnemonic), "%s.%c", + idesc->name, "?imbfxx"[insn_unit]); opnd1 = idesc->operands[0]; opnd2 = idesc->operands[1]; ia64_free_opcode (idesc); @@ -7066,7 +7067,6 @@ emit_one_bundle () fix = fix_new_exp (frag_now, frag_now_fix () - 16 + i, 8, &ifix->expr, ifix->is_pcrel, ifix->code); fix->tc_fix_data.opnd = ifix->opnd; - fix->fx_plt = (fix->fx_r_type == BFD_RELOC_IA64_PLTOFF22); fix->fx_file = md.slot[curr].src_file; fix->fx_line = md.slot[curr].src_line; } @@ -10544,12 +10544,15 @@ check_dependencies (idesc) int certain = (matchtype == 1 && CURR_SLOT.qp_regno == 0); if (path != 0) - sprintf (pathmsg, " when entry is at label '%s'", + snprintf (pathmsg, sizeof (pathmsg), + " when entry is at label '%s'", md.entry_labels[path - 1]); if (matchtype == 1 && rs->index >= 0) - sprintf (indexmsg, ", specific resource number is %d", + snprintf (indexmsg, sizeof (indexmsg), + ", specific resource number is %d", rs->index); - sprintf (msg, "Use of '%s' %s %s dependency '%s' (%s)%s%s", + snprintf (msg, sizeof (msg), + "Use of '%s' %s %s dependency '%s' (%s)%s%s", idesc->name, (certain ? "violates" : "may violate"), dv_mode[dep->mode], dep->name, @@ -11862,7 +11865,7 @@ struct alias { char *file; /* The file where the directive is seen. */ unsigned int line; /* The line number the directive is at. */ - const char *name; /* The orignale name of the symbol. */ + const char *name; /* The original name of the symbol. */ }; /* Called for .alias and .secalias directives. If SECTION is 1, it is diff --git a/contrib/binutils/gas/config/tc-ia64.h b/contrib/binutils/gas/config/tc-ia64.h index c17494b..b851b9f 100644 --- a/contrib/binutils/gas/config/tc-ia64.h +++ b/contrib/binutils/gas/config/tc-ia64.h @@ -1,5 +1,5 @@ /* tc-ia64.h -- Header file for tc-ia64.c. - Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc. Contributed by David Mosberger-Tang <davidm@hpl.hp.com> @@ -315,5 +315,5 @@ typedef struct unwind_record #define TC_FORCE_RELOCATION_LOCAL(FIX) \ ((FIX)->fx_r_type != BFD_RELOC_UNUSED \ && (!(FIX)->fx_pcrel \ - || (FIX)->fx_plt \ + || (FIX)->fx_r_type == BFD_RELOC_IA64_PLTOFF22 \ || TC_FORCE_RELOCATION (FIX))) diff --git a/contrib/binutils/gas/config/tc-mep.c b/contrib/binutils/gas/config/tc-mep.c new file mode 100644 index 0000000..b3b17d3 --- /dev/null +++ b/contrib/binutils/gas/config/tc-mep.c @@ -0,0 +1,1886 @@ +/* tc-mep.c -- Assembler for the Toshiba Media Processor. + Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation. + + 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, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include <stdio.h> +#include "as.h" +#include "dwarf2dbg.h" +#include "subsegs.h" +#include "symcat.h" +#include "opcodes/mep-desc.h" +#include "opcodes/mep-opc.h" +#include "cgen.h" +#include "elf/common.h" +#include "elf/mep.h" +#include "libbfd.h" +#include "xregex.h" + +/* Structure to hold all of the different components describing + an individual instruction. */ +typedef struct +{ + const CGEN_INSN * insn; + const CGEN_INSN * orig_insn; + CGEN_FIELDS fields; +#if CGEN_INT_INSN_P + CGEN_INSN_INT buffer [1]; +#define INSN_VALUE(buf) (*(buf)) +#else + unsigned char buffer [CGEN_MAX_INSN_SIZE]; +#define INSN_VALUE(buf) (buf) +#endif + char * addr; + fragS * frag; + int num_fixups; + fixS * fixups [GAS_CGEN_MAX_FIXUPS]; + int indices [MAX_OPERAND_INSTANCES]; +} mep_insn; + +static int mode = CORE; /* Start in core mode. */ +static int pluspresent = 0; +static int allow_disabled_registers = 0; +static int library_flag = 0; + +/* We're going to need to store all of the instructions along with + their fixups so that we can parallelization grouping rules. */ + +static mep_insn saved_insns[MAX_SAVED_FIXUP_CHAINS]; +static int num_insns_saved = 0; + +const char comment_chars[] = "#"; +const char line_comment_chars[] = ";#"; +const char line_separator_chars[] = ";"; +const char EXP_CHARS[] = "eE"; +const char FLT_CHARS[] = "dD"; + +static void mep_switch_to_vliw_mode (int); +static void mep_switch_to_core_mode (int); +static void mep_s_vtext (int); +static void mep_noregerr (int); + +/* The target specific pseudo-ops which we support. */ +const pseudo_typeS md_pseudo_table[] = +{ + { "word", cons, 4 }, + { "file", (void (*) (int)) dwarf2_directive_file, 0 }, + { "loc", dwarf2_directive_loc, 0 }, + { "vliw", mep_switch_to_vliw_mode, 0 }, + { "core", mep_switch_to_core_mode, 0 }, + { "vtext", mep_s_vtext, 0 }, + { "noregerr", mep_noregerr, 0 }, + { NULL, NULL, 0 } +}; + +/* Relocations against symbols are done in two + parts, with a HI relocation and a LO relocation. Each relocation + has only 16 bits of space to store an addend. This means that in + order for the linker to handle carries correctly, it must be able + to locate both the HI and the LO relocation. This means that the + relocations must appear in order in the relocation table. + + In order to implement this, we keep track of each unmatched HI + relocation. We then sort them so that they immediately precede the + corresponding LO relocation. */ + +struct mep_hi_fixup +{ + struct mep_hi_fixup * next; /* Next HI fixup. */ + fixS * fixp; /* This fixup. */ + segT seg; /* The section this fixup is in. */ +}; + +/* The list of unmatched HI relocs. */ +static struct mep_hi_fixup * mep_hi_fixup_list; + + +#define OPTION_EB (OPTION_MD_BASE + 0) +#define OPTION_EL (OPTION_MD_BASE + 1) +#define OPTION_CONFIG (OPTION_MD_BASE + 2) +#define OPTION_AVERAGE (OPTION_MD_BASE + 3) +#define OPTION_NOAVERAGE (OPTION_MD_BASE + 4) +#define OPTION_MULT (OPTION_MD_BASE + 5) +#define OPTION_NOMULT (OPTION_MD_BASE + 6) +#define OPTION_DIV (OPTION_MD_BASE + 7) +#define OPTION_NODIV (OPTION_MD_BASE + 8) +#define OPTION_BITOPS (OPTION_MD_BASE + 9) +#define OPTION_NOBITOPS (OPTION_MD_BASE + 10) +#define OPTION_LEADZ (OPTION_MD_BASE + 11) +#define OPTION_NOLEADZ (OPTION_MD_BASE + 12) +#define OPTION_ABSDIFF (OPTION_MD_BASE + 13) +#define OPTION_NOABSDIFF (OPTION_MD_BASE + 14) +#define OPTION_MINMAX (OPTION_MD_BASE + 15) +#define OPTION_NOMINMAX (OPTION_MD_BASE + 16) +#define OPTION_CLIP (OPTION_MD_BASE + 17) +#define OPTION_NOCLIP (OPTION_MD_BASE + 18) +#define OPTION_SATUR (OPTION_MD_BASE + 19) +#define OPTION_NOSATUR (OPTION_MD_BASE + 20) +#define OPTION_COP32 (OPTION_MD_BASE + 21) +#define OPTION_REPEAT (OPTION_MD_BASE + 25) +#define OPTION_NOREPEAT (OPTION_MD_BASE + 26) +#define OPTION_DEBUG (OPTION_MD_BASE + 27) +#define OPTION_NODEBUG (OPTION_MD_BASE + 28) +#define OPTION_LIBRARY (OPTION_MD_BASE + 29) + +struct option md_longopts[] = { + { "EB", no_argument, NULL, OPTION_EB}, + { "EL", no_argument, NULL, OPTION_EL}, + { "mconfig", required_argument, NULL, OPTION_CONFIG}, + { "maverage", no_argument, NULL, OPTION_AVERAGE}, + { "mno-average", no_argument, NULL, OPTION_NOAVERAGE}, + { "mmult", no_argument, NULL, OPTION_MULT}, + { "mno-mult", no_argument, NULL, OPTION_NOMULT}, + { "mdiv", no_argument, NULL, OPTION_DIV}, + { "mno-div", no_argument, NULL, OPTION_NODIV}, + { "mbitops", no_argument, NULL, OPTION_BITOPS}, + { "mno-bitops", no_argument, NULL, OPTION_NOBITOPS}, + { "mleadz", no_argument, NULL, OPTION_LEADZ}, + { "mno-leadz", no_argument, NULL, OPTION_NOLEADZ}, + { "mabsdiff", no_argument, NULL, OPTION_ABSDIFF}, + { "mno-absdiff", no_argument, NULL, OPTION_NOABSDIFF}, + { "mminmax", no_argument, NULL, OPTION_MINMAX}, + { "mno-minmax", no_argument, NULL, OPTION_NOMINMAX}, + { "mclip", no_argument, NULL, OPTION_CLIP}, + { "mno-clip", no_argument, NULL, OPTION_NOCLIP}, + { "msatur", no_argument, NULL, OPTION_SATUR}, + { "mno-satur", no_argument, NULL, OPTION_NOSATUR}, + { "mcop32", no_argument, NULL, OPTION_COP32}, + { "mdebug", no_argument, NULL, OPTION_DEBUG}, + { "mno-debug", no_argument, NULL, OPTION_NODEBUG}, + { "mlibrary", no_argument, NULL, OPTION_LIBRARY}, + { NULL, 0, NULL, 0 } }; +size_t md_longopts_size = sizeof (md_longopts); + +const char * md_shortopts = ""; +static int optbits = 0; +static int optbitset = 0; + +int +md_parse_option (int c, char *arg ATTRIBUTE_UNUSED) +{ + int i, idx; + switch (c) + { + case OPTION_EB: + target_big_endian = 1; + break; + case OPTION_EL: + target_big_endian = 0; + break; + case OPTION_CONFIG: + idx = 0; + for (i=1; mep_config_map[i].name; i++) + if (strcmp (mep_config_map[i].name, arg) == 0) + { + idx = i; + break; + } + if (!idx) + { + fprintf (stderr, "Error: unknown configuration %s\n", arg); + return 0; + } + mep_config_index = idx; + target_big_endian = mep_config_map[idx].big_endian; + break; + case OPTION_AVERAGE: + optbits |= 1 << CGEN_INSN_OPTIONAL_AVE_INSN; + optbitset |= 1 << CGEN_INSN_OPTIONAL_AVE_INSN; + break; + case OPTION_NOAVERAGE: + optbits &= ~(1 << CGEN_INSN_OPTIONAL_AVE_INSN); + optbitset |= 1 << CGEN_INSN_OPTIONAL_AVE_INSN; + break; + case OPTION_MULT: + optbits |= 1 << CGEN_INSN_OPTIONAL_MUL_INSN; + optbitset |= 1 << CGEN_INSN_OPTIONAL_MUL_INSN; + break; + case OPTION_NOMULT: + optbits &= ~(1 << CGEN_INSN_OPTIONAL_MUL_INSN); + optbitset |= 1 << CGEN_INSN_OPTIONAL_MUL_INSN; + break; + case OPTION_DIV: + optbits |= 1 << CGEN_INSN_OPTIONAL_DIV_INSN; + optbitset |= 1 << CGEN_INSN_OPTIONAL_DIV_INSN; + break; + case OPTION_NODIV: + optbits &= ~(1 << CGEN_INSN_OPTIONAL_DIV_INSN); + optbitset |= 1 << CGEN_INSN_OPTIONAL_DIV_INSN; + break; + case OPTION_BITOPS: + optbits |= 1 << CGEN_INSN_OPTIONAL_BIT_INSN; + optbitset |= 1 << CGEN_INSN_OPTIONAL_BIT_INSN; + break; + case OPTION_NOBITOPS: + optbits &= ~(1 << CGEN_INSN_OPTIONAL_BIT_INSN); + optbitset |= 1 << CGEN_INSN_OPTIONAL_BIT_INSN; + break; + case OPTION_LEADZ: + optbits |= 1 << CGEN_INSN_OPTIONAL_LDZ_INSN; + optbitset |= 1 << CGEN_INSN_OPTIONAL_LDZ_INSN; + break; + case OPTION_NOLEADZ: + optbits &= ~(1 << CGEN_INSN_OPTIONAL_LDZ_INSN); + optbitset |= 1 << CGEN_INSN_OPTIONAL_LDZ_INSN; + break; + case OPTION_ABSDIFF: + optbits |= 1 << CGEN_INSN_OPTIONAL_ABS_INSN; + optbitset |= 1 << CGEN_INSN_OPTIONAL_ABS_INSN; + break; + case OPTION_NOABSDIFF: + optbits &= ~(1 << CGEN_INSN_OPTIONAL_ABS_INSN); + optbitset |= 1 << CGEN_INSN_OPTIONAL_ABS_INSN; + break; + case OPTION_MINMAX: + optbits |= 1 << CGEN_INSN_OPTIONAL_MINMAX_INSN; + optbitset |= 1 << CGEN_INSN_OPTIONAL_MINMAX_INSN; + break; + case OPTION_NOMINMAX: + optbits &= ~(1 << CGEN_INSN_OPTIONAL_MINMAX_INSN); + optbitset |= 1 << CGEN_INSN_OPTIONAL_MINMAX_INSN; + break; + case OPTION_CLIP: + optbits |= 1 << CGEN_INSN_OPTIONAL_CLIP_INSN; + optbitset |= 1 << CGEN_INSN_OPTIONAL_CLIP_INSN; + break; + case OPTION_NOCLIP: + optbits &= ~(1 << CGEN_INSN_OPTIONAL_CLIP_INSN); + optbitset |= 1 << CGEN_INSN_OPTIONAL_CLIP_INSN; + break; + case OPTION_SATUR: + optbits |= 1 << CGEN_INSN_OPTIONAL_SAT_INSN; + optbitset |= 1 << CGEN_INSN_OPTIONAL_SAT_INSN; + break; + case OPTION_NOSATUR: + optbits &= ~(1 << CGEN_INSN_OPTIONAL_SAT_INSN); + optbitset |= 1 << CGEN_INSN_OPTIONAL_SAT_INSN; + break; + case OPTION_COP32: + optbits |= 1 << CGEN_INSN_OPTIONAL_CP_INSN; + optbitset |= 1 << CGEN_INSN_OPTIONAL_CP_INSN; + break; + case OPTION_DEBUG: + optbits |= 1 << CGEN_INSN_OPTIONAL_DEBUG_INSN; + optbitset |= 1 << CGEN_INSN_OPTIONAL_DEBUG_INSN; + break; + case OPTION_NODEBUG: + optbits &= ~(1 << CGEN_INSN_OPTIONAL_DEBUG_INSN); + optbitset |= 1 << CGEN_INSN_OPTIONAL_DEBUG_INSN; + break; + case OPTION_LIBRARY: + library_flag = EF_MEP_LIBRARY; + break; + case OPTION_REPEAT: + case OPTION_NOREPEAT: + break; + default: + return 0; + } + return 1; +} + +void +md_show_usage (FILE *stream) +{ + fprintf (stream, _("MeP specific command line options:\n\ + -EB assemble for a big endian system (default)\n\ + -EL assemble for a little endian system\n\ + -mconfig=<name> specify a chip configuration to use\n\ + -maverage -mno-average -mmult -mno-mult -mdiv -mno-div\n\ + -mbitops -mno-bitops -mleadz -mno-leadz -mabsdiff -mno-absdiff\n\ + -mminmax -mno-minmax -mclip -mno-clip -msatur -mno-satur -mcop32\n\ + enable/disable the given opcodes\n\ +\n\ + If -mconfig is given, the other -m options modify it. Otherwise,\n\ + if no -m options are given, all core opcodes are enabled;\n\ + if any enabling -m options are given, only those are enabled;\n\ + if only disabling -m options are given, only those are disabled.\n\ +")); + if (mep_config_map[1].name) + { + int i; + fprintf (stream, " -mconfig=STR specify the configuration to use\n"); + fprintf (stream, " Configurations:"); + for (i=0; mep_config_map[i].name; i++) + fprintf (stream, " %s", mep_config_map[i].name); + fprintf (stream, "\n"); + } +} + + + +static void +mep_check_for_disabled_registers (mep_insn *insn) +{ + static int initted = 0; + static int has_mul_div = 0; + static int has_cop = 0; + static int has_debug = 0; + unsigned int b, r; + + if (allow_disabled_registers) + return; + +#if !CGEN_INT_INSN_P + if (target_big_endian) + b = insn->buffer[0] * 256 + insn->buffer[1]; + else + b = insn->buffer[1] * 256 + insn->buffer[0]; +#else + b = insn->buffer[0]; +#endif + + if ((b & 0xfffff00e) == 0x7008 /* stc */ + || (b & 0xfffff00e) == 0x700a /* ldc */) + { + if (!initted) + { + initted = 1; + if ((MEP_OMASK & (1 << CGEN_INSN_OPTIONAL_MUL_INSN)) + || (MEP_OMASK & (1 << CGEN_INSN_OPTIONAL_DIV_INSN))) + has_mul_div = 1; + if (MEP_OMASK & (1 << CGEN_INSN_OPTIONAL_DEBUG_INSN)) + has_debug = 1; + if (MEP_OMASK & (1 << CGEN_INSN_OPTIONAL_CP_INSN)) + has_cop = 1; + } + + r = ((b & 0x00f0) >> 4) | ((b & 0x0001) << 4); + switch (r) + { + case 7: /* $hi */ + case 8: /* $lo */ + if (!has_mul_div) + as_bad ("$hi and $lo are disabled when MUL and DIV are off"); + break; + case 12: /* $mb0 */ + case 13: /* $me0 */ + case 14: /* $mb1 */ + case 15: /* $me1 */ + if (!has_cop) + as_bad ("$mb0, $me0, $mb1, and $me1 are disabled when COP is off"); + break; + case 24: /* $dbg */ + case 25: /* $depc */ + if (!has_debug) + as_bad ("$dbg and $depc are disabled when DEBUG is off"); + break; + } + } +} + +static int +mep_machine (void) +{ + switch (MEP_CPU) + { + default: break; + case EF_MEP_CPU_C2: return bfd_mach_mep; + case EF_MEP_CPU_C3: return bfd_mach_mep; + case EF_MEP_CPU_C4: return bfd_mach_mep; + case EF_MEP_CPU_H1: return bfd_mach_mep_h1; + } + + return bfd_mach_mep; +} + +/* The MeP version of the cgen parse_operand function. The only difference + from the standard version is that we want to avoid treating '$foo' and + '($foo...)' as references to a symbol called '$foo'. The chances are + that '$foo' is really a misspelt register. */ + +static const char * +mep_parse_operand (CGEN_CPU_DESC cd, enum cgen_parse_operand_type want, + const char **strP, int opindex, int opinfo, + enum cgen_parse_operand_result *resultP, bfd_vma *valueP) +{ + if (want == CGEN_PARSE_OPERAND_INTEGER || want == CGEN_PARSE_OPERAND_ADDRESS) + { + const char *next; + + next = *strP; + while (*next == '(') + next++; + if (*next == '$') + return "Not a valid literal"; + } + return gas_cgen_parse_operand (cd, want, strP, opindex, opinfo, + resultP, valueP); +} + +void +md_begin () +{ + /* Initialize the `cgen' interface. */ + + /* If the user specifies no options, we default to allowing + everything. If the user specifies any enabling options, we + default to allowing only what is specified. If the user + specifies only disabling options, we only disable what is + specified. If the user specifies options and a config, the + options modify the config. */ + if (optbits && mep_config_index == 0) + MEP_OMASK = optbits; + else + MEP_OMASK = (MEP_OMASK & ~optbitset) | optbits; + + /* Set the machine number and endian. */ + gas_cgen_cpu_desc = mep_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, 0, + CGEN_CPU_OPEN_ENDIAN, + target_big_endian + ? CGEN_ENDIAN_BIG + : CGEN_ENDIAN_LITTLE, + CGEN_CPU_OPEN_ISAS, 0, + CGEN_CPU_OPEN_END); + mep_cgen_init_asm (gas_cgen_cpu_desc); + + /* This is a callback from cgen to gas to parse operands. */ + cgen_set_parse_operand_fn (gas_cgen_cpu_desc, mep_parse_operand); + + /* Identify the architecture. */ + bfd_default_set_arch_mach (stdoutput, bfd_arch_mep, mep_machine ()); + + /* Store the configuration number and core. */ + bfd_set_private_flags (stdoutput, MEP_CPU | MEP_CONFIG | library_flag); + + /* Initialize the array we'll be using to store fixups. */ + gas_cgen_initialize_saved_fixups_array(); +} + +/* Variant of mep_cgen_assemble_insn. Assemble insn STR of cpu CD as a + coprocessor instruction, if possible, into FIELDS, BUF, and INSN. */ + +static const CGEN_INSN * +mep_cgen_assemble_cop_insn (CGEN_CPU_DESC cd, + const char *str, + CGEN_FIELDS *fields, + CGEN_INSN_BYTES_PTR buf, + const struct cgen_insn *pinsn) +{ + const char *start; + CGEN_INSN_LIST *ilist; + const char *errmsg = NULL; + + /* The instructions are stored in hashed lists. */ + ilist = CGEN_ASM_LOOKUP_INSN (gas_cgen_cpu_desc, + CGEN_INSN_MNEMONIC (pinsn)); + + start = str; + for ( ; ilist != NULL ; ilist = CGEN_ASM_NEXT_INSN (ilist)) + { + const CGEN_INSN *insn = ilist->insn; + if (strcmp (CGEN_INSN_MNEMONIC (ilist->insn), + CGEN_INSN_MNEMONIC (pinsn)) == 0 + && MEP_INSN_COP_P (ilist->insn) + && mep_cgen_insn_supported (cd, insn)) + { + str = start; + + /* skip this insn if str doesn't look right lexically */ + if (CGEN_INSN_RX (insn) != NULL && + regexec ((regex_t *) CGEN_INSN_RX (insn), str, 0, NULL, 0) == REG_NOMATCH) + continue; + + /* Allow parse/insert handlers to obtain length of insn. */ + CGEN_FIELDS_BITSIZE (fields) = CGEN_INSN_BITSIZE (insn); + + errmsg = CGEN_PARSE_FN (cd, insn) (cd, insn, & str, fields); + if (errmsg != NULL) + continue; + + errmsg = CGEN_INSERT_FN (cd, insn) (cd, insn, fields, buf, + (bfd_vma) 0); + if (errmsg != NULL) + continue; + + return insn; + } + } + return pinsn; +} + +static void +mep_save_insn (mep_insn insn) +{ + /* Consider change MAX_SAVED_FIXUP_CHAINS to MAX_PARALLEL_INSNS. */ + if (num_insns_saved < 0 || num_insns_saved >= MAX_SAVED_FIXUP_CHAINS) + { + as_fatal("index into saved_insns[] out of bounds."); + return; + } + saved_insns[num_insns_saved] = insn; + gas_cgen_save_fixups(num_insns_saved); + num_insns_saved++; +} + +static void +mep_check_parallel32_scheduling (void) +{ + int insn0iscopro, insn1iscopro, insn0length, insn1length; + + /* More than two instructions means that either someone is referring to + an internally parallel core or an internally parallel coprocessor, + neither of which are supported at this time. */ + if ( num_insns_saved > 2 ) + as_fatal("Internally paralled cores and coprocessors not supported."); + + /* If there are no insns saved, that's ok. Just return. This will + happen when mep_process_saved_insns is called when the end of the + source file is reached and there are no insns left to be processed. */ + if (num_insns_saved == 0) + return; + + /* Check some of the attributes of the first insn. */ + insn0iscopro = MEP_INSN_COP_P (saved_insns[0].insn); + insn0length = CGEN_FIELDS_BITSIZE (& saved_insns[0].fields); + + if (num_insns_saved == 2) + { + /* Check some of the attributes of the first insn. */ + insn1iscopro = MEP_INSN_COP_P (saved_insns[1].insn); + insn1length = CGEN_FIELDS_BITSIZE (& saved_insns[1].fields); + + if ((insn0iscopro && !insn1iscopro) + || (insn1iscopro && !insn0iscopro)) + { + /* We have one core and one copro insn. If their sizes + add up to 32, then the combination is valid. */ + if (insn0length + insn1length == 32) + return; + else + as_bad ("core and copro insn lengths must total 32 bits."); + } + else + as_bad ("vliw group must consist of 1 core and 1 copro insn."); + } + else + { + /* If we arrive here, we have one saved instruction. There are a + number of possible cases: + + 1. The instruction is a 32 bit core or coprocessor insn and + can be executed by itself. Valid. + + 2. The instrucion is a core instruction for which a cop nop + exists. In this case, insert the cop nop into the saved + insn array after the core insn and return. Valid. + + 3. The instruction is a coprocessor insn for which a core nop + exists. In this case, move the coprocessor insn to the + second element of the array and put the nop in the first + element then return. Valid. + + 4. The instruction is a core or coprocessor instruction for + which there is no matching coprocessor or core nop to use + to form a valid vliw insn combination. In this case, we + we have to abort. */ + + if (insn0length > 32) + as_fatal ("Cannot use 48- or 64-bit insns with a 32 bit datapath."); + + if (insn0length == 32) + return; + + /* Insn is smaller than datapath. If there are no matching + nops for this insn, then terminate assembly. */ + if (CGEN_INSN_ATTR_VALUE (saved_insns[0].insn, + CGEN_INSN_VLIW32_NO_MATCHING_NOP)) + as_fatal ("No valid nop."); + + /* At this point we know that we have a single 16-bit insn that has + a matching nop. We have to assemble it and put it into the saved + insn and fixup chain arrays. */ + + if (insn0iscopro) + { + char *errmsg; + mep_insn insn; + + /* Move the insn and it's fixups to the second element of the + saved insns arrary and insert a 16 bit core nope into the + first element. */ + insn.insn = mep_cgen_assemble_insn (gas_cgen_cpu_desc, "nop", + &insn.fields, insn.buffer, + &errmsg); + if (!insn.insn) + { + as_bad ("%s", errmsg); + return; + } + + /* Move the insn in element 0 to element 1 and insert the + nop into element 0. Move the fixups in element 0 to + element 1 and save the current fixups to element 0. + Really there aren't any fixups at this point because we're + inserting a nop but we might as well be general so that + if there's ever a need to insert a general insn, we'll + have an example. */ + saved_insns[1] = saved_insns[0]; + saved_insns[0] = insn; + num_insns_saved++; + gas_cgen_swap_fixups (0); + gas_cgen_save_fixups (1); + } + else + { + char * errmsg; + mep_insn insn; + int insn_num = saved_insns[0].insn->base->num; + + /* Use 32 bit branches and skip the nop. */ + if (insn_num == MEP_INSN_BSR12 + || insn_num == MEP_INSN_BEQZ + || insn_num == MEP_INSN_BNEZ) + return; + + /* Insert a 16-bit coprocessor nop. Note that at the time */ + /* this was done, no 16-bit coprocessor nop was defined. */ + insn.insn = mep_cgen_assemble_insn (gas_cgen_cpu_desc, "cpnop16", + &insn.fields, insn.buffer, + &errmsg); + if (!insn.insn) + { + as_bad ("%s", errmsg); + return; + } + + /* Now put the insn and fixups into the arrays. */ + mep_save_insn (insn); + } + } +} + +static void +mep_check_parallel64_scheduling (void) +{ + int insn0iscopro, insn1iscopro, insn0length, insn1length; + + /* More than two instructions means that someone is referring to an + internally parallel core or an internally parallel coprocessor. */ + /* These are not currently supported. */ + if (num_insns_saved > 2) + as_fatal ("Internally parallel cores of coprocessors not supported."); + + /* If there are no insns saved, that's ok. Just return. This will + happen when mep_process_saved_insns is called when the end of the + source file is reached and there are no insns left to be processed. */ + if (num_insns_saved == 0) + return; + + /* Check some of the attributes of the first insn. */ + insn0iscopro = MEP_INSN_COP_P (saved_insns[0].insn); + insn0length = CGEN_FIELDS_BITSIZE (& saved_insns[0].fields); + + if (num_insns_saved == 2) + { + /* Check some of the attributes of the first insn. */ + insn1iscopro = MEP_INSN_COP_P (saved_insns[1].insn); + insn1length = CGEN_FIELDS_BITSIZE (& saved_insns[1].fields); + + if ((insn0iscopro && !insn1iscopro) + || (insn1iscopro && !insn0iscopro)) + { + /* We have one core and one copro insn. If their sizes + add up to 64, then the combination is valid. */ + if (insn0length + insn1length == 64) + return; + else + as_bad ("core and copro insn lengths must total 64 bits."); + } + else + as_bad ("vliw group must consist of 1 core and 1 copro insn."); + } + else + { + /* If we arrive here, we have one saved instruction. There are a + number of possible cases: + + 1. The instruction is a 64 bit coprocessor insn and can be + executed by itself. Valid. + + 2. The instrucion is a core instruction for which a cop nop + exists. In this case, insert the cop nop into the saved + insn array after the core insn and return. Valid. + + 3. The instruction is a coprocessor insn for which a core nop + exists. In this case, move the coprocessor insn to the + second element of the array and put the nop in the first + element then return. Valid. + + 4. The instruction is a core or coprocessor instruction for + which there is no matching coprocessor or core nop to use + to form a valid vliw insn combination. In this case, we + we have to abort. */ + + /* If the insn is 64 bits long, it can run alone. The size check + is done indepependantly of whether the insn is core or copro + in case 64 bit coprocessor insns are added later. */ + if (insn0length == 64) + return; + + /* Insn is smaller than datapath. If there are no matching + nops for this insn, then terminate assembly. */ + if (CGEN_INSN_ATTR_VALUE (saved_insns[0].insn, + CGEN_INSN_VLIW64_NO_MATCHING_NOP)) + as_fatal ("No valid nop."); + + if (insn0iscopro) + { + char *errmsg; + mep_insn insn; + int i; + + /* Initialize the insn buffer. */ + for (i = 0; i < 64; i++) + insn.buffer[i] = '\0'; + + /* We have a coprocessor insn. At this point in time there + are is 32-bit core nop. There is only a 16-bit core + nop. The idea is to allow for a relatively arbitrary + coprocessor to be specified. We aren't looking at + trying to cover future changes in the core at this time + since it is assumed that the core will remain fairly + static. If there ever are 32 or 48 bit core nops added, + they will require entries below. */ + + if (insn0length == 48) + { + /* Move the insn and fixups to the second element of the + arrays then assemble and insert a 16 bit core nop. */ + insn.insn = mep_cgen_assemble_insn (gas_cgen_cpu_desc, "nop", + & insn.fields, insn.buffer, + & errmsg); + } + else + { + /* If this is reached, then we have a single coprocessor + insn that is not 48 bits long, but for which the assembler + thinks there is a matching core nop. If a 32-bit core + nop has been added, then make the necessary changes and + handle its assembly and insertion here. Otherwise, + go figure out why either: + + 1. The assembler thinks that there is a 32-bit core nop + to match a 32-bit coprocessor insn, or + 2. The assembler thinks that there is a 48-bit core nop + to match a 16-bit coprocessor insn. */ + + as_fatal ("Assembler expects a non-existent core nop."); + } + + if (!insn.insn) + { + as_bad ("%s", errmsg); + return; + } + + /* Move the insn in element 0 to element 1 and insert the + nop into element 0. Move the fixups in element 0 to + element 1 and save the current fixups to element 0. + Really there aren't any fixups at this point because we're + inserting a nop but we might as well be general so that + if there's ever a need to insert a general insn, we'll + have an example. */ + + saved_insns[1] = saved_insns[0]; + saved_insns[0] = insn; + num_insns_saved++; + gas_cgen_swap_fixups(0); + gas_cgen_save_fixups(1); + + } + else + { + char * errmsg; + mep_insn insn; + int i; + + /* Initialize the insn buffer */ + for (i = 0; i < 64; i++) + insn.buffer[i] = '\0'; + + /* We have a core insn. We have to handle all possible nop + lengths. If a coprocessor doesn't have a nop of a certain + length but there exists core insns that when combined with + a nop of that length would fill the datapath, those core + insns will be flagged with the VLIW_NO_CORRESPONDING_NOP + attribute. That will ensure that when used in a way that + requires a nop to be inserted, assembly will terminate + before reaching this section of code. This guarantees + that cases below which would result in the attempted + insertion of nop that doesn't exist will never be entered. */ + if (insn0length == 16) + { + /* Insert 48 bit coprocessor nop. */ + /* Assemble it and put it into the arrays. */ + insn.insn = mep_cgen_assemble_insn (gas_cgen_cpu_desc, "cpnop48", + &insn.fields, insn.buffer, + &errmsg); + } + else if (insn0length == 32) + { + /* Insert 32 bit coprocessor nop. */ + insn.insn = mep_cgen_assemble_insn (gas_cgen_cpu_desc, "cpnop32", + &insn.fields, insn.buffer, + &errmsg); + } + else if (insn0length == 48) + { + /* Insert 16 bit coprocessor nop. */ + insn.insn = mep_cgen_assemble_insn (gas_cgen_cpu_desc, "cpnop16", + &insn.fields, insn.buffer, + &errmsg); + } + else + /* Core insn has an invalid length. Something has gone wrong. */ + as_fatal ("Core insn has invalid length! Something is wrong!"); + + if (!insn.insn) + { + as_bad ("%s", errmsg); + return; + } + + /* Now put the insn and fixups into the arrays. */ + mep_save_insn (insn); + } + } +} + +/* The scheduling functions are just filters for invalid combinations. + If there is a violation, they terminate assembly. Otherise they + just fall through. Succesful combinations cause no side effects + other than valid nop insertion. */ + +static void +mep_check_parallel_scheduling (void) +{ + /* This is where we will eventually read the config information + and choose which scheduling checking function to call. */ + if (MEP_VLIW64) + mep_check_parallel64_scheduling (); + else + mep_check_parallel32_scheduling (); +} + +static void +mep_process_saved_insns (void) +{ + int i; + + gas_cgen_save_fixups (MAX_SAVED_FIXUP_CHAINS - 1); + + /* We have to check for valid scheduling here. */ + mep_check_parallel_scheduling (); + + /* If the last call didn't cause assembly to terminate, we have + a valid vliw insn/insn pair saved. Restore this instructions' + fixups and process the insns. */ + for (i = 0;i<num_insns_saved;i++) + { + gas_cgen_restore_fixups (i); + gas_cgen_finish_insn (saved_insns[i].insn, saved_insns[i].buffer, + CGEN_FIELDS_BITSIZE (& saved_insns[i].fields), + 1, NULL); + } + gas_cgen_restore_fixups (MAX_SAVED_FIXUP_CHAINS - 1); + + /* Clear the fixups and reset the number insn saved to 0. */ + gas_cgen_initialize_saved_fixups_array (); + num_insns_saved = 0; + listing_prev_line (); +} + +void +md_assemble (char * str) +{ + static CGEN_BITSET* isas = NULL; + char * errmsg; + + /* Initialize GAS's cgen interface for a new instruction. */ + gas_cgen_init_parse (); + + /* There are two possible modes: core and vliw. We have to assemble + differently for each. + + Core Mode: We assemble normally. All instructions are on a + single line and are made up of one mnemonic and one + set of operands. + VLIW Mode: Vliw combinations are indicated as follows: + + core insn + + copro insn + + We want to handle the general case where more than + one instruction can be preceeded by a +. This will + happen later if we add support for internally parallel + coprocessors. We'll make the parsing nice and general + so that it can handle an arbitrary number of insns + with leading +'s. The actual checking for valid + combinations is done elsewhere. */ + + /* Initialize the isa to refer to the core. */ + if (isas == NULL) + isas = cgen_bitset_copy (& MEP_CORE_ISA); + else + { + cgen_bitset_clear (isas); + cgen_bitset_union (isas, & MEP_CORE_ISA, isas); + } + gas_cgen_cpu_desc->isas = isas; + + if (mode == VLIW) + { + /* VLIW mode. */ + + int thisInsnIsCopro = 0; + mep_insn insn; + int i; + + /* Initialize the insn buffer */ + + if (! CGEN_INT_INSN_P) + for (i=0; i < CGEN_MAX_INSN_SIZE; i++) + insn.buffer[i]='\0'; + + /* Can't tell core / copro insns apart at parse time! */ + cgen_bitset_union (isas, & MEP_COP_ISA, isas); + + /* Assemble the insn so we can examine its attributes. */ + insn.insn = mep_cgen_assemble_insn (gas_cgen_cpu_desc, str, + &insn.fields, insn.buffer, + &errmsg); + if (!insn.insn) + { + as_bad ("%s", errmsg); + return; + } + mep_check_for_disabled_registers (&insn); + + /* Check to see if it's a coprocessor instruction. */ + thisInsnIsCopro = MEP_INSN_COP_P (insn.insn); + + if (!thisInsnIsCopro) + { + insn.insn = mep_cgen_assemble_cop_insn (gas_cgen_cpu_desc, str, + &insn.fields, insn.buffer, + insn.insn); + thisInsnIsCopro = MEP_INSN_COP_P (insn.insn); + mep_check_for_disabled_registers (&insn); + } + + if (pluspresent) + { + /* A plus was present. */ + /* Check for a + with a core insn and abort if found. */ + if (!thisInsnIsCopro) + { + as_fatal("A core insn cannot be preceeded by a +.\n"); + return; + } + + if (num_insns_saved > 0) + { + /* There are insns in the queue. Add this one. */ + mep_save_insn (insn); + } + else + { + /* There are no insns in the queue and a plus is present. + This is a syntax error. Let's not tolerate this. + We can relax this later if necessary. */ + as_bad (_("Invalid use of parallelization operator.")); + return; + } + } + else + { + /* No plus was present. */ + if (num_insns_saved > 0) + { + /* There are insns saved and we came across an insn without a + leading +. That's the signal to process the saved insns + before proceeding then treat the current insn as the first + in a new vliw group. */ + mep_process_saved_insns (); + num_insns_saved = 0; + /* mep_save_insn (insn); */ + } + mep_save_insn (insn); +#if 0 + else + { + + /* Core Insn. Add it to the beginning of the queue. */ + mep_save_insn (insn); + /* gas_cgen_save_fixups(num_insns_saved); */ + } +#endif + } + + pluspresent = 0; + } + else + { + /* Core mode. */ + + /* Only single instructions are assembled in core mode. */ + mep_insn insn; + + /* If a leading '+' was present, issue an error. + That's not allowed in core mode. */ + if (pluspresent) + { + as_bad (_("Leading plus sign not allowed in core mode")); + return; + } + + insn.insn = mep_cgen_assemble_insn + (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg); + + if (!insn.insn) + { + as_bad ("%s", errmsg); + return; + } + gas_cgen_finish_insn (insn.insn, insn.buffer, + CGEN_FIELDS_BITSIZE (& insn.fields), 1, NULL); + mep_check_for_disabled_registers (&insn); + } +} + +valueT +md_section_align (segT segment, valueT size) +{ + int align = bfd_get_section_alignment (stdoutput, segment); + return ((size + (1 << align) - 1) & (-1 << align)); +} + + +symbolS * +md_undefined_symbol (char *name ATTRIBUTE_UNUSED) +{ + return 0; +} + +/* Interface to relax_segment. */ + + +const relax_typeS md_relax_table[] = +{ + /* The fields are: + 1) most positive reach of this state, + 2) most negative reach of this state, + 3) how many bytes this mode will have in the variable part of the frag + 4) which index into the table to try if we can't fit into this one. */ + /* Note that we use "beq" because "jmp" has a peculiarity - it cannot + jump to addresses with any bits 27..24 set. So, we use beq as a + 17-bit pc-relative branch to avoid using jmp, just in case. */ + + /* 0 */ { 0, 0, 0, 0 }, /* unused */ + /* 1 */ { 0, 0, 0, 0 }, /* marker for "don't know yet" */ + + /* 2 */ { 2047, -2048, 0, 3 }, /* bsr12 */ + /* 3 */ { 0, 0, 2, 0 }, /* bsr16 */ + + /* 4 */ { 2047, -2048, 0, 5 }, /* bra */ + /* 5 */ { 65535, -65536, 2, 6 }, /* beq $0,$0 */ + /* 6 */ { 0, 0, 2, 0 }, /* jmp24 */ + + /* 7 */ { 65535, -65536, 0, 8 }, /* beqi */ + /* 8 */ { 0, 0, 4, 0 }, /* bnei/jmp */ + + /* 9 */ { 127, -128, 0, 10 }, /* beqz */ + /* 10 */ { 65535, -65536, 2, 11 }, /* beqi */ + /* 11 */ { 0, 0, 4, 0 }, /* bnei/jmp */ + + /* 12 */ { 65535, -65536, 0, 13 }, /* bnei */ + /* 13 */ { 0, 0, 4, 0 }, /* beqi/jmp */ + + /* 14 */ { 127, -128, 0, 15 }, /* bnez */ + /* 15 */ { 65535, -65536, 2, 16 }, /* bnei */ + /* 16 */ { 0, 0, 4, 0 }, /* beqi/jmp */ + + /* 17 */ { 65535, -65536, 0, 13 }, /* bgei */ + /* 18 */ { 0, 0, 4, 0 }, + /* 19 */ { 65535, -65536, 0, 13 }, /* blti */ + /* 20 */ { 0, 0, 4, 0 }, + /* 19 */ { 65535, -65536, 0, 13 }, /* bcpeq */ + /* 20 */ { 0, 0, 4, 0 }, + /* 19 */ { 65535, -65536, 0, 13 }, /* bcpne */ + /* 20 */ { 0, 0, 4, 0 }, + /* 19 */ { 65535, -65536, 0, 13 }, /* bcpat */ + /* 20 */ { 0, 0, 4, 0 }, + /* 19 */ { 65535, -65536, 0, 13 }, /* bcpaf */ + /* 20 */ { 0, 0, 4, 0 } +}; + +/* Pseudo-values for 64 bit "insns" which are combinations of two 32 + bit insns. */ +typedef enum { + MEP_PSEUDO64_NONE, + MEP_PSEUDO64_16BITCC, + MEP_PSEUDO64_32BITCC, +} MepPseudo64Values; + +static struct { + int insn; + int growth; + int insn_for_extern; +} subtype_mappings[] = { + { 0, 0, 0 }, + { 0, 0, 0 }, + { MEP_INSN_BSR12, 0, MEP_INSN_BSR24 }, + { MEP_INSN_BSR24, 2, MEP_INSN_BSR24 }, + { MEP_INSN_BRA, 0, MEP_INSN_BRA }, + { MEP_INSN_BEQ, 2, MEP_INSN_BEQ }, + { MEP_INSN_JMP, 2, MEP_INSN_JMP }, + { MEP_INSN_BEQI, 0, MEP_INSN_BEQI }, + { -1, 4, MEP_PSEUDO64_32BITCC }, + { MEP_INSN_BEQZ, 0, MEP_INSN_BEQZ }, + { MEP_INSN_BEQI, 2, MEP_INSN_BEQI }, + { -1, 4, MEP_PSEUDO64_16BITCC }, + { MEP_INSN_BNEI, 0, MEP_INSN_BNEI }, + { -1, 4, MEP_PSEUDO64_32BITCC }, + { MEP_INSN_BNEZ, 0, MEP_INSN_BNEZ }, + { MEP_INSN_BNEI, 2, MEP_INSN_BNEI }, + { -1, 4, MEP_PSEUDO64_16BITCC }, + { MEP_INSN_BGEI, 0, MEP_INSN_BGEI }, + { -1, 4, MEP_PSEUDO64_32BITCC }, + { MEP_INSN_BLTI, 0, MEP_INSN_BLTI }, + { -1, 4, MEP_PSEUDO64_32BITCC }, + { MEP_INSN_BCPEQ, 0, MEP_INSN_BCPEQ }, + { -1, 4, MEP_PSEUDO64_32BITCC }, + { MEP_INSN_BCPNE, 0, MEP_INSN_BCPNE }, + { -1, 4, MEP_PSEUDO64_32BITCC }, + { MEP_INSN_BCPAT, 0, MEP_INSN_BCPAT }, + { -1, 4, MEP_PSEUDO64_32BITCC }, + { MEP_INSN_BCPAF, 0, MEP_INSN_BCPAF }, + { -1, 4, MEP_PSEUDO64_32BITCC } +}; +#define NUM_MAPPINGS (sizeof (subtype_mappings) / sizeof (subtype_mappings[0])) + +void +mep_prepare_relax_scan (fragS *fragP, offsetT *aim, relax_substateT this_state) +{ + symbolS *symbolP = fragP->fr_symbol; + if (symbolP && !S_IS_DEFINED (symbolP)) + *aim = 0; + /* Adjust for MeP pcrel not being relative to the next opcode. */ + *aim += 2 + md_relax_table[this_state].rlx_length; +} + +static int +insn_to_subtype (int insn) +{ + unsigned int i; + for (i=0; i<NUM_MAPPINGS; i++) + if (insn == subtype_mappings[i].insn) + return i; + abort (); +} + +/* Return an initial guess of the length by which a fragment must grow + to hold a branch to reach its destination. Also updates fr_type + and fr_subtype as necessary. + + Called just before doing relaxation. Any symbol that is now + undefined will not become defined. 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 0 value. */ + +int +md_estimate_size_before_relax (fragS * fragP, segT segment) +{ + if (fragP->fr_subtype == 1) + fragP->fr_subtype = insn_to_subtype (fragP->fr_cgen.insn->base->num); + + if (S_GET_SEGMENT (fragP->fr_symbol) != segment) + { + int new_insn; + + new_insn = subtype_mappings[fragP->fr_subtype].insn_for_extern; + fragP->fr_subtype = insn_to_subtype (new_insn); + } + + if (MEP_VLIW && ! MEP_VLIW64 + && (bfd_get_section_flags (stdoutput, segment) & SEC_MEP_VLIW)) + { + /* Use 32 bit branches for vliw32 so the vliw word is not split. */ + switch (fragP->fr_cgen.insn->base->num) + { + case MEP_INSN_BSR12: + fragP->fr_subtype = insn_to_subtype + (subtype_mappings[fragP->fr_subtype].insn_for_extern); + break; + case MEP_INSN_BEQZ: + fragP->fr_subtype ++; + break; + case MEP_INSN_BNEZ: + fragP->fr_subtype ++; + break; + } + } + + if (fragP->fr_cgen.insn->base + && fragP->fr_cgen.insn->base->num + != subtype_mappings[fragP->fr_subtype].insn) + { + int new_insn= subtype_mappings[fragP->fr_subtype].insn; + if (new_insn != -1) + { + fragP->fr_cgen.insn = (fragP->fr_cgen.insn + - fragP->fr_cgen.insn->base->num + + new_insn); + } + } + + return subtype_mappings[fragP->fr_subtype].growth; +} + +/* *fragP has been relaxed to its final size, and now needs to have + the bytes inside it modified to conform to the new size. + + Called after relaxation is finished. + fragP->fr_type == rs_machine_dependent. + fragP->fr_subtype is the subtype of what the address relaxed to. */ + +static int +target_address_for (fragS *frag) +{ + int rv = frag->fr_offset; + symbolS *sym = frag->fr_symbol; + + if (sym) + rv += S_GET_VALUE (sym); + + return rv; +} + +void +md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, + segT sec ATTRIBUTE_UNUSED, + fragS *fragP) +{ + int addend, rn, bit = 0; + int operand; + int where = fragP->fr_opcode - fragP->fr_literal; + int e = target_big_endian ? 0 : 1; + + addend = target_address_for (fragP) - (fragP->fr_address + where); + + if (subtype_mappings[fragP->fr_subtype].insn == -1) + { + fragP->fr_fix += subtype_mappings[fragP->fr_subtype].growth; + switch (subtype_mappings[fragP->fr_subtype].insn_for_extern) + { + case MEP_PSEUDO64_16BITCC: + fragP->fr_opcode[1^e] = ((fragP->fr_opcode[1^e] & 1) ^ 1) | 0x06; + fragP->fr_opcode[2^e] = 0xd8; + fragP->fr_opcode[3^e] = 0x08; + fragP->fr_opcode[4^e] = 0; + fragP->fr_opcode[5^e] = 0; + where += 2; + break; + case MEP_PSEUDO64_32BITCC: + if (fragP->fr_opcode[0^e] & 0x10) + fragP->fr_opcode[1^e] ^= 0x01; + else + fragP->fr_opcode[1^e] ^= 0x04; + fragP->fr_opcode[2^e] = 0; + fragP->fr_opcode[3^e] = 4; + fragP->fr_opcode[4^e] = 0xd8; + fragP->fr_opcode[5^e] = 0x08; + fragP->fr_opcode[6^e] = 0; + fragP->fr_opcode[7^e] = 0; + where += 4; + break; + default: + abort (); + } + fragP->fr_cgen.insn = (fragP->fr_cgen.insn + - fragP->fr_cgen.insn->base->num + + MEP_INSN_JMP); + operand = MEP_OPERAND_PCABS24A2; + } + else + switch (fragP->fr_cgen.insn->base->num) + { + case MEP_INSN_BSR12: + fragP->fr_opcode[0^e] = 0xb0 | ((addend >> 8) & 0x0f); + fragP->fr_opcode[1^e] = 0x01 | (addend & 0xfe); + operand = MEP_OPERAND_PCREL12A2; + break; + + case MEP_INSN_BSR24: + fragP->fr_fix += 2; + fragP->fr_opcode[0^e] = 0xd8 | ((addend >> 5) & 0x07); + fragP->fr_opcode[1^e] = 0x09 | ((addend << 3) & 0xf0); + fragP->fr_opcode[2^e] = 0x00 | ((addend >>16) & 0xff); + fragP->fr_opcode[3^e] = 0x00 | ((addend >> 8) & 0xff); + operand = MEP_OPERAND_PCREL24A2; + break; + + case MEP_INSN_BRA: + fragP->fr_opcode[0^e] = 0xb0 | ((addend >> 8) & 0x0f); + fragP->fr_opcode[1^e] = 0x00 | (addend & 0xfe); + operand = MEP_OPERAND_PCREL12A2; + break; + + case MEP_INSN_BEQ: + /* The default relax_frag doesn't change the state if there is no + growth, so we must manually handle converting out-of-range BEQ + instructions to JMP. */ + if (addend <= 65535 && addend >= -65536) + { + fragP->fr_fix += 2; + fragP->fr_opcode[0^e] = 0xe0; + fragP->fr_opcode[1^e] = 0x01; + fragP->fr_opcode[2^e] = 0x00 | ((addend >> 9) & 0xff); + fragP->fr_opcode[3^e] = 0x00 | ((addend >> 1) & 0xff); + operand = MEP_OPERAND_PCREL17A2; + break; + } + /* ...FALLTHROUGH... */ + + case MEP_INSN_JMP: + addend = target_address_for (fragP); + fragP->fr_fix += 2; + fragP->fr_opcode[0^e] = 0xd8 | ((addend >> 5) & 0x07); + fragP->fr_opcode[1^e] = 0x08 | ((addend << 3) & 0xf0); + fragP->fr_opcode[2^e] = 0x00 | ((addend >>16) & 0xff); + fragP->fr_opcode[3^e] = 0x00 | ((addend >> 8) & 0xff); + operand = MEP_OPERAND_PCABS24A2; + break; + + case MEP_INSN_BNEZ: + bit = 1; + case MEP_INSN_BEQZ: + fragP->fr_opcode[1^e] = bit | (addend & 0xfe); + operand = MEP_OPERAND_PCREL8A2; + break; + + case MEP_INSN_BNEI: + bit = 4; + case MEP_INSN_BEQI: + if (subtype_mappings[fragP->fr_subtype].growth) + { + fragP->fr_fix += subtype_mappings[fragP->fr_subtype].growth; + rn = fragP->fr_opcode[0^e] & 0x0f; + fragP->fr_opcode[0^e] = 0xe0 | rn; + fragP->fr_opcode[1^e] = bit; + } + fragP->fr_opcode[2^e] = 0x00 | ((addend >> 9) & 0xff); + fragP->fr_opcode[3^e] = 0x00 | ((addend >> 1) & 0xff); + operand = MEP_OPERAND_PCREL17A2; + break; + + case MEP_INSN_BLTI: + case MEP_INSN_BGEI: + case MEP_INSN_BCPEQ: + case MEP_INSN_BCPNE: + case MEP_INSN_BCPAT: + case MEP_INSN_BCPAF: + /* No opcode change needed, just operand. */ + fragP->fr_opcode[2^e] = (addend >> 9) & 0xff; + fragP->fr_opcode[3^e] = (addend >> 1) & 0xff; + operand = MEP_OPERAND_PCREL17A2; + break; + + default: + abort (); + } + + if (S_GET_SEGMENT (fragP->fr_symbol) != sec + || operand == MEP_OPERAND_PCABS24A2) + { + assert (fragP->fr_cgen.insn != 0); + gas_cgen_record_fixup (fragP, + where, + fragP->fr_cgen.insn, + (fragP->fr_fix - where) * 8, + cgen_operand_lookup_by_num (gas_cgen_cpu_desc, + operand), + fragP->fr_cgen.opinfo, + fragP->fr_symbol, fragP->fr_offset); + } +} + + +/* Functions concerning relocs. */ + +void +mep_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) +{ + /* If we already know the fixup value, adjust it in the same + way that the linker would have done. */ + if (fixP->fx_addsy == 0) + switch (fixP->fx_cgen.opinfo) + { + case BFD_RELOC_MEP_LOW16: + *valP = ((long)(*valP & 0xffff)) << 16 >> 16; + break; + case BFD_RELOC_MEP_HI16U: + *valP >>= 16; + break; + case BFD_RELOC_MEP_HI16S: + *valP = (*valP + 0x8000) >> 16; + break; + } + + /* Now call cgen's md_aply_fix. */ + gas_cgen_md_apply_fix (fixP, valP, seg); +} + +long +md_pcrel_from_section (fixS *fixP, segT sec) +{ + if (fixP->fx_addsy != (symbolS *) NULL + && (! S_IS_DEFINED (fixP->fx_addsy) + || S_GET_SEGMENT (fixP->fx_addsy) != sec)) + /* The symbol is undefined (or is defined but not in this section). + Let the linker figure it out. */ + return 0; + + /* Return the address of the opcode - cgen adjusts for opcode size + itself, to be consistent with the disassembler, which must do + so. */ + return fixP->fx_where + fixP->fx_frag->fr_address; +} + +/* Return the bfd reloc type for OPERAND of INSN at fixup FIXP. + Returns BFD_RELOC_NONE if no reloc type can be found. + *FIXP may be modified if desired. */ + +#if defined (__STDC__) || defined (ALMOST_STDC) || defined (HAVE_STRINGIZE) +#define MAP(n) case MEP_OPERAND_##n: return BFD_RELOC_MEP_##n; +#else +#define MAP(n) case MEP_OPERAND_/**/n: return BFD_RELOC_MEP_/**/n; +#endif + +bfd_reloc_code_real_type +md_cgen_lookup_reloc (const CGEN_INSN *insn ATTRIBUTE_UNUSED, + const CGEN_OPERAND *operand, + fixS *fixP) +{ + enum bfd_reloc_code_real reloc = fixP->fx_cgen.opinfo; + static char printed[MEP_OPERAND_MAX] = { 0 }; + + /* If there's a reloc here, it's because the parser saw a %foo() and + is giving us the correct reloc to use, or because we converted to + a different size reloc below and want to avoid "converting" more + than once. */ + if (reloc && reloc != BFD_RELOC_NONE) + return reloc; + + switch (operand->type) + { + MAP (PCREL8A2); /* beqz */ + MAP (PCREL12A2); /* bsr16 */ + MAP (PCREL17A2); /* beqi */ + MAP (PCREL24A2); /* bsr24 */ + MAP (PCABS24A2); /* jmp */ + MAP (UIMM24); /* mov */ + MAP (ADDR24A4); /* sw/lw */ + + /* The rest of the relocs should be generated by the parser, + for things such as %tprel(), etc. */ + case MEP_OPERAND_SIMM16: +#ifdef OBJ_COMPLEX_RELC + /* coalescing this into RELOC_MEP_16 is actually a bug, + since it's a signed operand. let the relc code handle it. */ + return BFD_RELOC_RELC; +#endif + + case MEP_OPERAND_UIMM16: + case MEP_OPERAND_SDISP16: + case MEP_OPERAND_CODE16: + fixP->fx_where += 2; + /* to avoid doing the above add twice */ + fixP->fx_cgen.opinfo = BFD_RELOC_MEP_16; + return BFD_RELOC_MEP_16; + + default: +#ifdef OBJ_COMPLEX_RELC + /* this is not an error, yet. + pass it to the linker. */ + return BFD_RELOC_RELC; +#endif + if (printed[operand->type]) + return BFD_RELOC_NONE; + printed[operand->type] = 1; + + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Don't know how to relocate plain operands of type %s"), + operand->name); + + /* Print some helpful hints for the user. */ + switch (operand->type) + { + case MEP_OPERAND_UDISP7: + case MEP_OPERAND_UDISP7A2: + case MEP_OPERAND_UDISP7A4: + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Perhaps you are missing %%tpoff()?")); + break; + default: + break; + } + return BFD_RELOC_NONE; + } +} + +/* Called while parsing an instruction to create a fixup. + We need to check for HI16 relocs and queue them up for later sorting. */ + +fixS * +mep_cgen_record_fixup_exp (fragS *frag, + int where, + const CGEN_INSN *insn, + int length, + const CGEN_OPERAND *operand, + int opinfo, + expressionS *exp) +{ + fixS * fixP = gas_cgen_record_fixup_exp (frag, where, insn, length, + operand, opinfo, exp); + return fixP; +} + +/* Return BFD reloc type from opinfo field in a fixS. + It's tricky using fx_r_type in mep_frob_file because the values + are BFD_RELOC_UNUSED + operand number. */ +#define FX_OPINFO_R_TYPE(f) ((f)->fx_cgen.opinfo) + +/* Sort any unmatched HI16 relocs so that they immediately precede + the corresponding LO16 reloc. This is called before md_apply_fix and + tc_gen_reloc. */ + +void +mep_frob_file () +{ + struct mep_hi_fixup * l; + + for (l = mep_hi_fixup_list; l != NULL; l = l->next) + { + segment_info_type * seginfo; + int pass; + + assert (FX_OPINFO_R_TYPE (l->fixp) == BFD_RELOC_HI16 + || FX_OPINFO_R_TYPE (l->fixp) == BFD_RELOC_LO16); + + /* Check quickly whether the next fixup happens to be a matching low. */ + if (l->fixp->fx_next != NULL + && FX_OPINFO_R_TYPE (l->fixp->fx_next) == BFD_RELOC_LO16 + && l->fixp->fx_addsy == l->fixp->fx_next->fx_addsy + && l->fixp->fx_offset == l->fixp->fx_next->fx_offset) + continue; + + /* Look through the fixups for this segment for a matching + `low'. When we find one, move the high just in front of it. + We do this in two passes. In the first pass, we try to find + a unique `low'. In the second pass, we permit multiple + high's relocs for a single `low'. */ + seginfo = seg_info (l->seg); + for (pass = 0; pass < 2; pass++) + { + fixS * f; + fixS * prev; + + prev = NULL; + for (f = seginfo->fix_root; f != NULL; f = f->fx_next) + { + /* Check whether this is a `low' fixup which matches l->fixp. */ + if (FX_OPINFO_R_TYPE (f) == BFD_RELOC_LO16 + && f->fx_addsy == l->fixp->fx_addsy + && f->fx_offset == l->fixp->fx_offset + && (pass == 1 + || prev == NULL + || (FX_OPINFO_R_TYPE (prev) != BFD_RELOC_HI16) + || prev->fx_addsy != f->fx_addsy + || prev->fx_offset != f->fx_offset)) + { + fixS ** pf; + + /* Move l->fixp before f. */ + for (pf = &seginfo->fix_root; + * pf != l->fixp; + pf = & (* pf)->fx_next) + assert (* pf != NULL); + + * pf = l->fixp->fx_next; + + l->fixp->fx_next = f; + if (prev == NULL) + seginfo->fix_root = l->fixp; + else + prev->fx_next = l->fixp; + + break; + } + + prev = f; + } + + if (f != NULL) + break; + + if (pass == 1) + as_warn_where (l->fixp->fx_file, l->fixp->fx_line, + _("Unmatched high relocation")); + } + } +} + +/* See whether we need to force a relocation into the output file. */ + +int +mep_force_relocation (fixS *fixp) +{ + if ( fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT + || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + return 1; + + /* Allow branches to global symbols to be resolved at assembly time. + This is consistent with way relaxable branches are handled, since + branches to both global and local symbols are relaxed. It also + corresponds to the assumptions made in md_pcrel_from_section. */ + return S_FORCE_RELOC (fixp->fx_addsy, !fixp->fx_pcrel); +} + +/* 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); +} + +/* 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 + +char * +md_atof (int type, char *litP, int *sizeP) +{ + int i; + int prec; + LITTLENUM_TYPE words [MAX_LITTLENUMS]; + char * t; + + switch (type) + { + case 'f': + case 'F': + case 's': + case 'S': + prec = 2; + break; + + case 'd': + case 'D': + case 'r': + case 'R': + prec = 4; + break; + + /* FIXME: Some targets allow other format chars for bigger sizes here. */ + default: + *sizeP = 0; + return _("Bad call to md_atof()"); + } + + t = atof_ieee (input_line_pointer, type, words); + if (t) + input_line_pointer = t; + * sizeP = prec * sizeof (LITTLENUM_TYPE); + + for (i = 0; i < prec; i++) + { + md_number_to_chars (litP, (valueT) words[i], + sizeof (LITTLENUM_TYPE)); + litP += sizeof (LITTLENUM_TYPE); + } + + return 0; +} + + +bfd_boolean +mep_fix_adjustable (fixS *fixP) +{ + bfd_reloc_code_real_type reloc_type; + + if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED) + { + const CGEN_INSN *insn = NULL; + int opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED; + const CGEN_OPERAND *operand + = cgen_operand_lookup_by_num(gas_cgen_cpu_desc, opindex); + reloc_type = md_cgen_lookup_reloc (insn, operand, fixP); + } + else + reloc_type = fixP->fx_r_type; + + if (fixP->fx_addsy == NULL) + return 1; + + /* Prevent all adjustments to global symbols. */ + if (S_IS_EXTERNAL (fixP->fx_addsy)) + return 0; + + if (S_IS_WEAK (fixP->fx_addsy)) + return 0; + + /* We need the symbol name for the VTABLE entries */ + if (reloc_type == BFD_RELOC_VTABLE_INHERIT + || reloc_type == BFD_RELOC_VTABLE_ENTRY) + return 0; + + return 1; +} + +int +mep_elf_section_letter (int letter, char **ptrmsg) +{ + if (letter == 'v') + return SHF_MEP_VLIW; + + *ptrmsg = _("Bad .section directive: want a,v,w,x,M,S in string"); + return 0; +} + +flagword +mep_elf_section_flags (flagword flags, int attr, int type ATTRIBUTE_UNUSED) +{ + if (attr & SHF_MEP_VLIW) + flags |= SEC_MEP_VLIW; + return flags; +} + +/* In vliw mode, the default section is .vtext. We have to be able + to switch into .vtext using only the .vtext directive. */ + +static segT +mep_vtext_section (void) +{ + static segT vtext_section; + + if (! vtext_section) + { + flagword applicable = bfd_applicable_section_flags (stdoutput); + vtext_section = subseg_new (VTEXT_SECTION_NAME, 0); + bfd_set_section_flags (stdoutput, vtext_section, + applicable & (SEC_ALLOC | SEC_LOAD | SEC_RELOC + | SEC_CODE | SEC_READONLY + | SEC_MEP_VLIW)); + } + + return vtext_section; +} + +static void +mep_s_vtext (int ignore ATTRIBUTE_UNUSED) +{ + int temp; + + /* Record previous_section and previous_subsection. */ + obj_elf_section_change_hook (); + + temp = get_absolute_expression (); + subseg_set (mep_vtext_section (), (subsegT) temp); + demand_empty_rest_of_line (); +} + +static void +mep_switch_to_core_mode (int dummy ATTRIBUTE_UNUSED) +{ + mep_process_saved_insns (); + pluspresent = 0; + mode = CORE; +} + +static void +mep_switch_to_vliw_mode (int dummy ATTRIBUTE_UNUSED) +{ + if (! MEP_VLIW) + as_bad (_(".vliw unavailable when VLIW is disabled.")); + mode = VLIW; + /* Switch into .vtext here too. */ + /* mep_s_vtext(); */ +} + +/* This is an undocumented pseudo-op used to disable gas's + "disabled_registers" check. Used for code which checks for those + registers at runtime. */ +static void +mep_noregerr (int i ATTRIBUTE_UNUSED) +{ + allow_disabled_registers = 1; +} + +/* mep_unrecognized_line: This is called when a line that can't be parsed + is encountered. We use it to check for a leading '+' sign which indicates + that the current instruction is a coprocessor instruction that is to be + parallelized with a previous core insn. This function accepts the '+' and + rejects all other characters that might indicate garbage at the beginning + of the line. The '+' character gets lost as the calling loop continues, + so we need to indicate that we saw it. */ + +int +mep_unrecognized_line (int ch) +{ + switch (ch) + { + case '+': + pluspresent = 1; + return 1; /* '+' indicates an instruction to be parallelized. */ + default: + return 0; /* If it's not a '+', the line can't be parsed. */ + } +} + +void +mep_cleanup (void) +{ + /* Take care of any insns left to be parallelized when the file ends. + This is mainly here to handle the case where the file ends with an + insn preceeded by a + or the file ends unexpectedly. */ + if (mode == VLIW) + mep_process_saved_insns (); +} + +int +mep_flush_pending_output (void) +{ + if (mode == VLIW) + { + mep_process_saved_insns (); + pluspresent = 0; + } + + return 1; +} diff --git a/contrib/binutils/gas/config/tc-mep.h b/contrib/binutils/gas/config/tc-mep.h new file mode 100644 index 0000000..1d48bd4 --- /dev/null +++ b/contrib/binutils/gas/config/tc-mep.h @@ -0,0 +1,119 @@ +/* tc-mep.h -- Header file for tc-mep.c. + Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. + + 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, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#define TC_MEP + +/* Support computed relocations. */ +#define OBJ_COMPLEX_RELC + +/* Support many operands per instruction. */ +#define GAS_CGEN_MAX_FIXUPS 10 + +#define LISTING_HEADER "MEP GAS " + +/* The target BFD architecture. */ +#define TARGET_ARCH bfd_arch_mep + +#define TARGET_FORMAT (target_big_endian ? "elf32-mep" : "elf32-mep-little") + +/* This is the default. */ +#define TARGET_BYTES_BIG_ENDIAN 1 + +/* Permit temporary numeric labels. */ +#define LOCAL_LABELS_FB 1 + +/* .-foo gets turned into PC relative relocs. */ +#define DIFF_EXPR_OK + +/* We don't need to handle .word strangely. */ +#define WORKING_DOT_WORD + +/* Values passed to md_apply_fix don't include the symbol value. */ +#define MD_APPLY_SYM_VALUE(FIX) 0 + +#define MD_APPLY_FIX +#define md_apply_fix mep_apply_fix +extern void mep_apply_fix (struct fix *, valueT *, segT); + +/* Call md_pcrel_from_section(), not md_pcrel_from(). */ +#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section (FIXP, SEC) +extern long md_pcrel_from_section (struct fix *, segT); + +#define tc_frob_file() mep_frob_file () +extern void mep_frob_file (void); + +#define tc_fix_adjustable(fixP) mep_fix_adjustable (fixP) +extern bfd_boolean mep_fix_adjustable (struct fix *); + +/* After creating a fixup for an instruction operand, we need + to check for HI16 relocs and queue them up for later sorting. */ +#define md_cgen_record_fixup_exp mep_cgen_record_fixup_exp + +/* When relaxing, we need to emit various relocs we otherwise wouldn't. */ +#define TC_FORCE_RELOCATION(fix) mep_force_relocation (fix) +extern int mep_force_relocation (struct fix *); + +#define tc_gen_reloc gas_cgen_tc_gen_reloc + +extern void gas_cgen_md_operand (expressionS *); +#define md_operand(x) gas_cgen_md_operand (x) + +#define md_flush_pending_output() mep_flush_pending_output() +extern int mep_flush_pending_output(void); + +extern const struct relax_type md_relax_table[]; +#define TC_GENERIC_RELAX_TABLE md_relax_table + +/* Account for inserting a jmp after the insn. */ +#define TC_CGEN_MAX_RELAX(insn, len) ((len) + 4) + +extern void mep_prepare_relax_scan (fragS *, offsetT *, relax_substateT); +#define md_prepare_relax_scan(FRAGP, ADDR, AIM, STATE, TYPE) \ + mep_prepare_relax_scan (FRAGP, &AIM, STATE) + +#define skip_whitespace(str) while (*(str) == ' ') ++(str) + +/* Support for core/vliw mode switching. */ +#define CORE 0 +#define VLIW 1 +#define MAX_PARALLEL_INSNS 56 /* From email from Toshiba. */ +#define VTEXT_SECTION_NAME ".vtext" + +/* Needed to process pending instructions when a label is encountered. */ +#define TC_START_LABEL(ch, ptr) ((ch == ':') && mep_flush_pending_output ()) + +#define tc_unrecognized_line(c) mep_unrecognized_line (c) +extern int mep_unrecognized_line (int); +#define md_cleanup mep_cleanup +extern void mep_cleanup (void); + +#define md_elf_section_letter mep_elf_section_letter +extern int mep_elf_section_letter (int, char **); +#define md_elf_section_flags mep_elf_section_flags +extern flagword mep_elf_section_flags (flagword, int, int); + +#define ELF_TC_SPECIAL_SECTIONS \ + { VTEXT_SECTION_NAME, SHT_PROGBITS, SHF_ALLOC|SHF_EXECINSTR|SHF_MEP_VLIW }, + +/* The values of the following enum are for use with parinsnum, which + is a variable in md_assemble that keeps track of whether or not the + next instruction is expected to be the first or second instrucion in + a parallelization group. */ +typedef enum exp_par_insn_{FIRST, SECOND} EXP_PAR_INSN; diff --git a/contrib/binutils/gas/config/tc-mips.c b/contrib/binutils/gas/config/tc-mips.c index dc5ff68..e97193f 100644 --- a/contrib/binutils/gas/config/tc-mips.c +++ b/contrib/binutils/gas/config/tc-mips.c @@ -1,6 +1,6 @@ /* tc-mips.c -- assemble code for a MIPS chip. Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, - 2003, 2004, 2005 Free Software Foundation, Inc. + 2003, 2004, 2005, 2006 Free Software Foundation, Inc. Contributed by the OSF and Ralph Campbell. Written by Keith Knowles and Ralph Campbell, working independently. Modified for ECOFF and R4000 support by Ian Lance Taylor of Cygnus @@ -28,8 +28,6 @@ #include "subsegs.h" #include "safe-ctype.h" -#include <stdarg.h> - #include "opcode/mips.h" #include "itbl-ops.h" #include "dwarf2dbg.h" @@ -169,7 +167,7 @@ struct mips_cl_insn /* True if this entry cannot be moved from its current position. */ unsigned int fixed_p : 1; - /* True if this instruction occured in a .set noreorder block. */ + /* True if this instruction occurred in a .set noreorder block. */ unsigned int noreorder_p : 1; /* True for mips16 instructions that jump to an absolute address. */ @@ -212,7 +210,9 @@ struct mips_set_options command line options, and based on the default architecture. */ int ase_mips3d; int ase_mdmx; + int ase_smartmips; int ase_dsp; + int ase_dspr2; int ase_mt; /* Whether we are assembling for the mips16 processor. 0 if we are not, 1 if we are, and -1 if the value has not been initialized. @@ -264,7 +264,7 @@ static int file_mips_fp32 = -1; static struct mips_set_options mips_opts = { - ISA_UNKNOWN, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, CPU_UNKNOWN, FALSE + ISA_UNKNOWN, -1, -1, 0, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, CPU_UNKNOWN, FALSE }; /* These variables are filled in with the masks of registers used. @@ -280,6 +280,11 @@ static int file_mips_isa = ISA_UNKNOWN; command line (e.g., by -march). */ static int file_ase_mips16; +#define ISA_SUPPORTS_MIPS16E (mips_opts.isa == ISA_MIPS32 \ + || mips_opts.isa == ISA_MIPS32R2 \ + || mips_opts.isa == ISA_MIPS64 \ + || mips_opts.isa == ISA_MIPS64R2) + /* True if -mips3d was passed or implied by arguments passed on the command line (e.g., by -march). */ static int file_ase_mips3d; @@ -288,14 +293,36 @@ static int file_ase_mips3d; command line (e.g., by -march). */ static int file_ase_mdmx; +/* True if -msmartmips was passed or implied by arguments passed on the + command line (e.g., by -march). */ +static int file_ase_smartmips; + +#define ISA_SUPPORTS_SMARTMIPS (mips_opts.isa == ISA_MIPS32 \ + || mips_opts.isa == ISA_MIPS32R2) + /* True if -mdsp was passed or implied by arguments passed on the command line (e.g., by -march). */ static int file_ase_dsp; +#define ISA_SUPPORTS_DSP_ASE (mips_opts.isa == ISA_MIPS32R2 \ + || mips_opts.isa == ISA_MIPS64R2) + +#define ISA_SUPPORTS_DSP64_ASE (mips_opts.isa == ISA_MIPS64R2) + +/* True if -mdspr2 was passed or implied by arguments passed on the + command line (e.g., by -march). */ +static int file_ase_dspr2; + +#define ISA_SUPPORTS_DSPR2_ASE (mips_opts.isa == ISA_MIPS32R2 \ + || mips_opts.isa == ISA_MIPS64R2) + /* True if -mmt was passed or implied by arguments passed on the command line (e.g., by -march). */ static int file_ase_mt; +#define ISA_SUPPORTS_MT_ASE (mips_opts.isa == ISA_MIPS32R2 \ + || mips_opts.isa == ISA_MIPS64R2) + /* The argument of the -march= flag. The architecture we are assembling. */ static int file_mips_arch = CPU_UNKNOWN; static const char *mips_arch_string; @@ -312,47 +339,66 @@ static int mips_32bitmode = 0; #define ABI_NEEDS_32BIT_REGS(ABI) ((ABI) == O32_ABI) /* Likewise 64-bit registers. */ -#define ABI_NEEDS_64BIT_REGS(ABI) \ - ((ABI) == N32_ABI \ - || (ABI) == N64_ABI \ +#define ABI_NEEDS_64BIT_REGS(ABI) \ + ((ABI) == N32_ABI \ + || (ABI) == N64_ABI \ || (ABI) == O64_ABI) -/* Return true if ISA supports 64 bit gp register instructions. */ -#define ISA_HAS_64BIT_REGS(ISA) ( \ - (ISA) == ISA_MIPS3 \ - || (ISA) == ISA_MIPS4 \ - || (ISA) == ISA_MIPS5 \ - || (ISA) == ISA_MIPS64 \ - || (ISA) == ISA_MIPS64R2 \ - ) +/* Return true if ISA supports 64 bit wide gp registers. */ +#define ISA_HAS_64BIT_REGS(ISA) \ + ((ISA) == ISA_MIPS3 \ + || (ISA) == ISA_MIPS4 \ + || (ISA) == ISA_MIPS5 \ + || (ISA) == ISA_MIPS64 \ + || (ISA) == ISA_MIPS64R2) + +/* Return true if ISA supports 64 bit wide float registers. */ +#define ISA_HAS_64BIT_FPRS(ISA) \ + ((ISA) == ISA_MIPS3 \ + || (ISA) == ISA_MIPS4 \ + || (ISA) == ISA_MIPS5 \ + || (ISA) == ISA_MIPS32R2 \ + || (ISA) == ISA_MIPS64 \ + || (ISA) == ISA_MIPS64R2) /* Return true if ISA supports 64-bit right rotate (dror et al.) instructions. */ -#define ISA_HAS_DROR(ISA) ( \ - (ISA) == ISA_MIPS64R2 \ - ) +#define ISA_HAS_DROR(ISA) \ + ((ISA) == ISA_MIPS64R2) /* Return true if ISA supports 32-bit right rotate (ror et al.) instructions. */ -#define ISA_HAS_ROR(ISA) ( \ - (ISA) == ISA_MIPS32R2 \ - || (ISA) == ISA_MIPS64R2 \ - ) +#define ISA_HAS_ROR(ISA) \ + ((ISA) == ISA_MIPS32R2 \ + || (ISA) == ISA_MIPS64R2 \ + || mips_opts.ase_smartmips) /* Return true if ISA supports ins instructions. */ -#define ISA_HAS_INS(ISA) ( \ - (ISA) == ISA_MIPS32R2 \ - || (ISA) == ISA_MIPS64R2 \ - ) +#define ISA_HAS_INS(ISA) \ + ((ISA) == ISA_MIPS32R2 \ + || (ISA) == ISA_MIPS64R2) + +/* Return true if ISA supports single-precision floats in odd registers. */ +#define ISA_HAS_ODD_SINGLE_FPR(ISA) \ + ((ISA) == ISA_MIPS32 \ + || (ISA) == ISA_MIPS32R2 \ + || (ISA) == ISA_MIPS64 \ + || (ISA) == ISA_MIPS64R2) + +/* Return true if ISA supports move to/from high part of a 64-bit + floating-point register. */ +#define ISA_HAS_MXHC1(ISA) \ + ((ISA) == ISA_MIPS32R2 \ + || (ISA) == ISA_MIPS64R2) #define HAVE_32BIT_GPRS \ - (mips_opts.gp32 || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + (mips_opts.gp32 || !ISA_HAS_64BIT_REGS (mips_opts.isa)) #define HAVE_32BIT_FPRS \ - (mips_opts.fp32 || ! ISA_HAS_64BIT_REGS (mips_opts.isa)) + (mips_opts.fp32 || !ISA_HAS_64BIT_FPRS (mips_opts.isa)) -#define HAVE_64BIT_GPRS (! HAVE_32BIT_GPRS) -#define HAVE_64BIT_FPRS (! HAVE_32BIT_FPRS) +#define HAVE_64BIT_GPRS (!HAVE_32BIT_GPRS) +#define HAVE_64BIT_FPRS (!HAVE_32BIT_FPRS) #define HAVE_NEWABI (mips_abi == N32_ABI || mips_abi == N64_ABI) @@ -392,22 +438,6 @@ static int mips_32bitmode = 0; (strncmp (TARGET_CPU, "mips16", sizeof ("mips16") - 1) == 0 \ || strncmp (TARGET_CANONICAL, "mips-lsi-elf", sizeof ("mips-lsi-elf") - 1) == 0) -/* Return true if the given CPU supports the MIPS3D ASE. */ -#define CPU_HAS_MIPS3D(cpu) ((cpu) == CPU_SB1 \ - ) - -/* Return true if the given CPU supports the MDMX ASE. */ -#define CPU_HAS_MDMX(cpu) (FALSE \ - ) - -/* Return true if the given CPU supports the DSP ASE. */ -#define CPU_HAS_DSP(cpu) (FALSE \ - ) - -/* Return true if the given CPU supports the MT ASE. */ -#define CPU_HAS_MT(cpu) (FALSE \ - ) - /* True if CPU has a dror instruction. */ #define CPU_HAS_DROR(CPU) ((CPU) == CPU_VR5400 || (CPU) == CPU_VR5500) @@ -501,7 +531,7 @@ static int mips_any_noreorder; an mfhi/mflo instruction is read in the next two instructions. */ static int mips_7000_hilo_fix; -/* The size of the small data section. */ +/* The size of objects in the small data section. */ static unsigned int g_switch_value = 8; /* Whether the -G option was used. */ static int g_switch_seen = 0; @@ -1018,6 +1048,8 @@ static void s_cpsetup (int); static void s_cplocal (int); static void s_cprestore (int); static void s_cpreturn (int); +static void s_dtprelword (int); +static void s_dtpreldword (int); static void s_gpvalue (int); static void s_gpword (int); static void s_gpdword (int); @@ -1043,11 +1075,19 @@ static int validate_mips_insn (const struct mips_opcode *); struct mips_cpu_info { const char *name; /* CPU or ISA name. */ - int is_isa; /* Is this an ISA? (If 0, a CPU.) */ + int flags; /* ASEs available, or ISA flag. */ int isa; /* ISA level. */ int cpu; /* CPU number (default CPU if ISA). */ }; +#define MIPS_CPU_IS_ISA 0x0001 /* Is this an ISA? (If 0, a CPU.) */ +#define MIPS_CPU_ASE_SMARTMIPS 0x0002 /* CPU implements SmartMIPS ASE */ +#define MIPS_CPU_ASE_DSP 0x0004 /* CPU implements DSP ASE */ +#define MIPS_CPU_ASE_MT 0x0008 /* CPU implements MT ASE */ +#define MIPS_CPU_ASE_MIPS3D 0x0010 /* CPU implements MIPS-3D ASE */ +#define MIPS_CPU_ASE_MDMX 0x0020 /* CPU implements MDMX ASE */ +#define MIPS_CPU_ASE_DSPR2 0x0040 /* CPU implements DSP R2 ASE */ + static const struct mips_cpu_info *mips_parse_cpu (const char *, const char *); static const struct mips_cpu_info *mips_cpu_info_from_isa (int); static const struct mips_cpu_info *mips_cpu_info_from_arch (int); @@ -1067,8 +1107,7 @@ static const struct mips_cpu_info *mips_cpu_info_from_arch (int); The following pseudo-ops from the Kane and Heinrich MIPS book are not MIPS CPU specific, but are also not specific to the object file format. This file is probably the best place to define them, but - they are not currently supported: .asm0, .endr, .lab, .repeat, - .struct. */ + they are not currently supported: .asm0, .endr, .lab, .struct. */ static const pseudo_typeS mips_pseudo_table[] = { @@ -1084,6 +1123,8 @@ static const pseudo_typeS mips_pseudo_table[] = {"cplocal", s_cplocal, 0}, {"cprestore", s_cprestore, 0}, {"cpreturn", s_cpreturn, 0}, + {"dtprelword", s_dtprelword, 0}, + {"dtpreldword", s_dtpreldword, 0}, {"gpvalue", s_gpvalue, 0}, {"gpword", s_gpword, 0}, {"gpdword", s_gpdword, 0}, @@ -1098,6 +1139,8 @@ static const pseudo_typeS mips_pseudo_table[] = {"half", s_cons, 1}, {"dword", s_cons, 3}, {"weakext", s_mips_weakext, 0}, + {"origin", s_org, 0}, + {"repeat", s_rept, 0}, /* These pseudo-ops are defined in read.c, but must be overridden here for one reason or another. */ @@ -1161,8 +1204,8 @@ struct insn_label_list symbolS *label; }; -static struct insn_label_list *insn_labels; static struct insn_label_list *free_insn_labels; +#define label_list tc_segment_info_data static void mips_clear_insn_labels (void); @@ -1170,12 +1213,19 @@ static inline void mips_clear_insn_labels (void) { register struct insn_label_list **pl; + segment_info_type *si; - for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next) - ; - *pl = insn_labels; - insn_labels = NULL; + if (now_seg) + { + for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next) + ; + + si = seg_info (now_seg); + *pl = si->label_list; + si->label_list = NULL; + } } + static char *expr_end; @@ -1418,13 +1468,302 @@ init_vr4120_conflicts (void) #undef CONFLICT } -/* 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. */ +struct regname { + const char *name; + unsigned int num; +}; + +#define RTYPE_MASK 0x1ff00 +#define RTYPE_NUM 0x00100 +#define RTYPE_FPU 0x00200 +#define RTYPE_FCC 0x00400 +#define RTYPE_VEC 0x00800 +#define RTYPE_GP 0x01000 +#define RTYPE_CP0 0x02000 +#define RTYPE_PC 0x04000 +#define RTYPE_ACC 0x08000 +#define RTYPE_CCC 0x10000 +#define RNUM_MASK 0x000ff +#define RWARN 0x80000 + +#define GENERIC_REGISTER_NUMBERS \ + {"$0", RTYPE_NUM | 0}, \ + {"$1", RTYPE_NUM | 1}, \ + {"$2", RTYPE_NUM | 2}, \ + {"$3", RTYPE_NUM | 3}, \ + {"$4", RTYPE_NUM | 4}, \ + {"$5", RTYPE_NUM | 5}, \ + {"$6", RTYPE_NUM | 6}, \ + {"$7", RTYPE_NUM | 7}, \ + {"$8", RTYPE_NUM | 8}, \ + {"$9", RTYPE_NUM | 9}, \ + {"$10", RTYPE_NUM | 10}, \ + {"$11", RTYPE_NUM | 11}, \ + {"$12", RTYPE_NUM | 12}, \ + {"$13", RTYPE_NUM | 13}, \ + {"$14", RTYPE_NUM | 14}, \ + {"$15", RTYPE_NUM | 15}, \ + {"$16", RTYPE_NUM | 16}, \ + {"$17", RTYPE_NUM | 17}, \ + {"$18", RTYPE_NUM | 18}, \ + {"$19", RTYPE_NUM | 19}, \ + {"$20", RTYPE_NUM | 20}, \ + {"$21", RTYPE_NUM | 21}, \ + {"$22", RTYPE_NUM | 22}, \ + {"$23", RTYPE_NUM | 23}, \ + {"$24", RTYPE_NUM | 24}, \ + {"$25", RTYPE_NUM | 25}, \ + {"$26", RTYPE_NUM | 26}, \ + {"$27", RTYPE_NUM | 27}, \ + {"$28", RTYPE_NUM | 28}, \ + {"$29", RTYPE_NUM | 29}, \ + {"$30", RTYPE_NUM | 30}, \ + {"$31", RTYPE_NUM | 31} + +#define FPU_REGISTER_NAMES \ + {"$f0", RTYPE_FPU | 0}, \ + {"$f1", RTYPE_FPU | 1}, \ + {"$f2", RTYPE_FPU | 2}, \ + {"$f3", RTYPE_FPU | 3}, \ + {"$f4", RTYPE_FPU | 4}, \ + {"$f5", RTYPE_FPU | 5}, \ + {"$f6", RTYPE_FPU | 6}, \ + {"$f7", RTYPE_FPU | 7}, \ + {"$f8", RTYPE_FPU | 8}, \ + {"$f9", RTYPE_FPU | 9}, \ + {"$f10", RTYPE_FPU | 10}, \ + {"$f11", RTYPE_FPU | 11}, \ + {"$f12", RTYPE_FPU | 12}, \ + {"$f13", RTYPE_FPU | 13}, \ + {"$f14", RTYPE_FPU | 14}, \ + {"$f15", RTYPE_FPU | 15}, \ + {"$f16", RTYPE_FPU | 16}, \ + {"$f17", RTYPE_FPU | 17}, \ + {"$f18", RTYPE_FPU | 18}, \ + {"$f19", RTYPE_FPU | 19}, \ + {"$f20", RTYPE_FPU | 20}, \ + {"$f21", RTYPE_FPU | 21}, \ + {"$f22", RTYPE_FPU | 22}, \ + {"$f23", RTYPE_FPU | 23}, \ + {"$f24", RTYPE_FPU | 24}, \ + {"$f25", RTYPE_FPU | 25}, \ + {"$f26", RTYPE_FPU | 26}, \ + {"$f27", RTYPE_FPU | 27}, \ + {"$f28", RTYPE_FPU | 28}, \ + {"$f29", RTYPE_FPU | 29}, \ + {"$f30", RTYPE_FPU | 30}, \ + {"$f31", RTYPE_FPU | 31} + +#define FPU_CONDITION_CODE_NAMES \ + {"$fcc0", RTYPE_FCC | 0}, \ + {"$fcc1", RTYPE_FCC | 1}, \ + {"$fcc2", RTYPE_FCC | 2}, \ + {"$fcc3", RTYPE_FCC | 3}, \ + {"$fcc4", RTYPE_FCC | 4}, \ + {"$fcc5", RTYPE_FCC | 5}, \ + {"$fcc6", RTYPE_FCC | 6}, \ + {"$fcc7", RTYPE_FCC | 7} + +#define COPROC_CONDITION_CODE_NAMES \ + {"$cc0", RTYPE_FCC | RTYPE_CCC | 0}, \ + {"$cc1", RTYPE_FCC | RTYPE_CCC | 1}, \ + {"$cc2", RTYPE_FCC | RTYPE_CCC | 2}, \ + {"$cc3", RTYPE_FCC | RTYPE_CCC | 3}, \ + {"$cc4", RTYPE_FCC | RTYPE_CCC | 4}, \ + {"$cc5", RTYPE_FCC | RTYPE_CCC | 5}, \ + {"$cc6", RTYPE_FCC | RTYPE_CCC | 6}, \ + {"$cc7", RTYPE_FCC | RTYPE_CCC | 7} + +#define N32N64_SYMBOLIC_REGISTER_NAMES \ + {"$a4", RTYPE_GP | 8}, \ + {"$a5", RTYPE_GP | 9}, \ + {"$a6", RTYPE_GP | 10}, \ + {"$a7", RTYPE_GP | 11}, \ + {"$ta0", RTYPE_GP | 8}, /* alias for $a4 */ \ + {"$ta1", RTYPE_GP | 9}, /* alias for $a5 */ \ + {"$ta2", RTYPE_GP | 10}, /* alias for $a6 */ \ + {"$ta3", RTYPE_GP | 11}, /* alias for $a7 */ \ + {"$t0", RTYPE_GP | 12}, \ + {"$t1", RTYPE_GP | 13}, \ + {"$t2", RTYPE_GP | 14}, \ + {"$t3", RTYPE_GP | 15} + +#define O32_SYMBOLIC_REGISTER_NAMES \ + {"$t0", RTYPE_GP | 8}, \ + {"$t1", RTYPE_GP | 9}, \ + {"$t2", RTYPE_GP | 10}, \ + {"$t3", RTYPE_GP | 11}, \ + {"$t4", RTYPE_GP | 12}, \ + {"$t5", RTYPE_GP | 13}, \ + {"$t6", RTYPE_GP | 14}, \ + {"$t7", RTYPE_GP | 15}, \ + {"$ta0", RTYPE_GP | 12}, /* alias for $t4 */ \ + {"$ta1", RTYPE_GP | 13}, /* alias for $t5 */ \ + {"$ta2", RTYPE_GP | 14}, /* alias for $t6 */ \ + {"$ta3", RTYPE_GP | 15} /* alias for $t7 */ + +/* Remaining symbolic register names */ +#define SYMBOLIC_REGISTER_NAMES \ + {"$zero", RTYPE_GP | 0}, \ + {"$at", RTYPE_GP | 1}, \ + {"$AT", RTYPE_GP | 1}, \ + {"$v0", RTYPE_GP | 2}, \ + {"$v1", RTYPE_GP | 3}, \ + {"$a0", RTYPE_GP | 4}, \ + {"$a1", RTYPE_GP | 5}, \ + {"$a2", RTYPE_GP | 6}, \ + {"$a3", RTYPE_GP | 7}, \ + {"$s0", RTYPE_GP | 16}, \ + {"$s1", RTYPE_GP | 17}, \ + {"$s2", RTYPE_GP | 18}, \ + {"$s3", RTYPE_GP | 19}, \ + {"$s4", RTYPE_GP | 20}, \ + {"$s5", RTYPE_GP | 21}, \ + {"$s6", RTYPE_GP | 22}, \ + {"$s7", RTYPE_GP | 23}, \ + {"$t8", RTYPE_GP | 24}, \ + {"$t9", RTYPE_GP | 25}, \ + {"$k0", RTYPE_GP | 26}, \ + {"$kt0", RTYPE_GP | 26}, \ + {"$k1", RTYPE_GP | 27}, \ + {"$kt1", RTYPE_GP | 27}, \ + {"$gp", RTYPE_GP | 28}, \ + {"$sp", RTYPE_GP | 29}, \ + {"$s8", RTYPE_GP | 30}, \ + {"$fp", RTYPE_GP | 30}, \ + {"$ra", RTYPE_GP | 31} + +#define MIPS16_SPECIAL_REGISTER_NAMES \ + {"$pc", RTYPE_PC | 0} + +#define MDMX_VECTOR_REGISTER_NAMES \ + /* {"$v0", RTYPE_VEC | 0}, clash with REG 2 above */ \ + /* {"$v1", RTYPE_VEC | 1}, clash with REG 3 above */ \ + {"$v2", RTYPE_VEC | 2}, \ + {"$v3", RTYPE_VEC | 3}, \ + {"$v4", RTYPE_VEC | 4}, \ + {"$v5", RTYPE_VEC | 5}, \ + {"$v6", RTYPE_VEC | 6}, \ + {"$v7", RTYPE_VEC | 7}, \ + {"$v8", RTYPE_VEC | 8}, \ + {"$v9", RTYPE_VEC | 9}, \ + {"$v10", RTYPE_VEC | 10}, \ + {"$v11", RTYPE_VEC | 11}, \ + {"$v12", RTYPE_VEC | 12}, \ + {"$v13", RTYPE_VEC | 13}, \ + {"$v14", RTYPE_VEC | 14}, \ + {"$v15", RTYPE_VEC | 15}, \ + {"$v16", RTYPE_VEC | 16}, \ + {"$v17", RTYPE_VEC | 17}, \ + {"$v18", RTYPE_VEC | 18}, \ + {"$v19", RTYPE_VEC | 19}, \ + {"$v20", RTYPE_VEC | 20}, \ + {"$v21", RTYPE_VEC | 21}, \ + {"$v22", RTYPE_VEC | 22}, \ + {"$v23", RTYPE_VEC | 23}, \ + {"$v24", RTYPE_VEC | 24}, \ + {"$v25", RTYPE_VEC | 25}, \ + {"$v26", RTYPE_VEC | 26}, \ + {"$v27", RTYPE_VEC | 27}, \ + {"$v28", RTYPE_VEC | 28}, \ + {"$v29", RTYPE_VEC | 29}, \ + {"$v30", RTYPE_VEC | 30}, \ + {"$v31", RTYPE_VEC | 31} + +#define MIPS_DSP_ACCUMULATOR_NAMES \ + {"$ac0", RTYPE_ACC | 0}, \ + {"$ac1", RTYPE_ACC | 1}, \ + {"$ac2", RTYPE_ACC | 2}, \ + {"$ac3", RTYPE_ACC | 3} + +static const struct regname reg_names[] = { + GENERIC_REGISTER_NUMBERS, + FPU_REGISTER_NAMES, + FPU_CONDITION_CODE_NAMES, + COPROC_CONDITION_CODE_NAMES, + + /* The $txx registers depends on the abi, + these will be added later into the symbol table from + one of the tables below once mips_abi is set after + parsing of arguments from the command line. */ + SYMBOLIC_REGISTER_NAMES, + + MIPS16_SPECIAL_REGISTER_NAMES, + MDMX_VECTOR_REGISTER_NAMES, + MIPS_DSP_ACCUMULATOR_NAMES, + {0, 0} +}; + +static const struct regname reg_names_o32[] = { + O32_SYMBOLIC_REGISTER_NAMES, + {0, 0} +}; + +static const struct regname reg_names_n32n64[] = { + N32N64_SYMBOLIC_REGISTER_NAMES, + {0, 0} +}; + +static int +reg_lookup (char **s, unsigned int types, unsigned int *regnop) +{ + symbolS *symbolP; + char *e; + char save_c; + int reg = -1; + + /* Find end of name. */ + e = *s; + if (is_name_beginner (*e)) + ++e; + while (is_part_of_name (*e)) + ++e; + + /* Terminate name. */ + save_c = *e; + *e = '\0'; + + /* Look for a register symbol. */ + if ((symbolP = symbol_find (*s)) && S_GET_SEGMENT (symbolP) == reg_section) + { + int r = S_GET_VALUE (symbolP); + if (r & types) + reg = r & RNUM_MASK; + else if ((types & RTYPE_VEC) && (r & ~1) == (RTYPE_GP | 2)) + /* Convert GP reg $v0/1 to MDMX reg $v0/1! */ + reg = (r & RNUM_MASK) - 2; + } + /* Else see if this is a register defined in an itbl entry. */ + else if ((types & RTYPE_GP) && itbl_have_entries) + { + char *n = *s; + unsigned long r; + + if (*n == '$') + ++n; + if (itbl_get_reg_val (n, &r)) + reg = r & RNUM_MASK; + } + + /* Advance to next token if a register was recognised. */ + if (reg >= 0) + *s = e; + else if (types & RWARN) + as_warn ("Unrecognized register name `%s'", *s); + + *e = save_c; + if (regnop) + *regnop = reg; + return reg >= 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. */ void md_begin (void) { - register const char *retval = NULL; + const char *retval = NULL; int i = 0; int broken = 0; @@ -1506,46 +1845,20 @@ md_begin (void) /* We add all the general register names to the symbol table. This helps us detect invalid uses of them. */ - for (i = 0; i < 32; i++) - { - char buf[5]; - - sprintf (buf, "$%d", i); - symbol_table_insert (symbol_new (buf, reg_section, i, + for (i = 0; reg_names[i].name; i++) + symbol_table_insert (symbol_new (reg_names[i].name, reg_section, + reg_names[i].num, // & RNUM_MASK, + &zero_address_frag)); + if (HAVE_NEWABI) + for (i = 0; reg_names_n32n64[i].name; i++) + symbol_table_insert (symbol_new (reg_names_n32n64[i].name, reg_section, + reg_names_n32n64[i].num, // & RNUM_MASK, &zero_address_frag)); - } - symbol_table_insert (symbol_new ("$ra", reg_section, RA, - &zero_address_frag)); - symbol_table_insert (symbol_new ("$fp", reg_section, FP, - &zero_address_frag)); - symbol_table_insert (symbol_new ("$sp", reg_section, SP, - &zero_address_frag)); - symbol_table_insert (symbol_new ("$gp", reg_section, GP, - &zero_address_frag)); - symbol_table_insert (symbol_new ("$at", reg_section, AT, - &zero_address_frag)); - symbol_table_insert (symbol_new ("$kt0", reg_section, KT0, - &zero_address_frag)); - symbol_table_insert (symbol_new ("$kt1", reg_section, KT1, - &zero_address_frag)); - symbol_table_insert (symbol_new ("$zero", reg_section, ZERO, - &zero_address_frag)); - symbol_table_insert (symbol_new ("$pc", reg_section, -1, - &zero_address_frag)); - - /* If we don't add these register names to the symbol table, they - may end up being added as regular symbols by operand(), and then - make it to the object file as undefined in case they're not - regarded as local symbols. They're local in o32, since `$' is a - local symbol prefix, but not in n32 or n64. */ - for (i = 0; i < 8; i++) - { - char buf[6]; - - sprintf (buf, "$fcc%i", i); - symbol_table_insert (symbol_new (buf, reg_section, -1, + else + for (i = 0; reg_names_o32[i].name; i++) + symbol_table_insert (symbol_new (reg_names_o32[i].name, reg_section, + reg_names_o32[i].num, // & RNUM_MASK, &zero_address_frag)); - } mips_no_prev_insn (); @@ -1560,7 +1873,8 @@ md_begin (void) bfd_set_gp_size (stdoutput, g_switch_value); - if (OUTPUT_FLAVOR == bfd_target_elf_flavour) +#ifdef OBJ_ELF + if (IS_ELF) { /* On a native system other than VxWorks, sections must be aligned to 16 byte boundaries. When configured for an embedded ELF @@ -1598,9 +1912,7 @@ md_begin (void) bfd_set_section_flags (stdoutput, sec, flags); bfd_set_section_alignment (stdoutput, sec, HAVE_NEWABI ? 3 : 2); -#ifdef OBJ_ELF mips_regmask_frag = frag_more (sizeof (Elf32_External_RegInfo)); -#endif } else { @@ -1610,7 +1922,6 @@ md_begin (void) bfd_set_section_flags (stdoutput, sec, flags); bfd_set_section_alignment (stdoutput, sec, 3); -#ifdef OBJ_ELF /* Set up the option header. */ { Elf_Internal_Options opthdr; @@ -1627,7 +1938,6 @@ md_begin (void) mips_regmask_frag = frag_more (sizeof (Elf64_External_RegInfo)); } -#endif } if (ECOFF_DEBUGGING) @@ -1637,8 +1947,7 @@ md_begin (void) SEC_HAS_CONTENTS | SEC_READONLY); (void) bfd_set_section_alignment (stdoutput, sec, 2); } -#ifdef OBJ_ELF - else if (OUTPUT_FLAVOR == bfd_target_elf_flavour && mips_flag_pdr) + else if (mips_flag_pdr) { pdr_seg = subseg_new (".pdr", (subsegT) 0); (void) bfd_set_section_flags (stdoutput, pdr_seg, @@ -1646,11 +1955,11 @@ md_begin (void) | SEC_DEBUGGING); (void) bfd_set_section_alignment (stdoutput, pdr_seg, 2); } -#endif subseg_set (seg, subseg); } } +#endif /* OBJ_ELF */ if (! ECOFF_DEBUGGING) md_obj_begin (); @@ -1849,10 +2158,11 @@ reg_needs_delay (unsigned int reg) static void mips_move_labels (void) { + segment_info_type *si = seg_info (now_seg); struct insn_label_list *l; valueT val; - for (l = insn_labels; l != NULL; l = l->next) + for (l = si->label_list; l != NULL; l = l->next) { assert (S_GET_SEGMENT (l->label) == now_seg); symbol_set_frag (l->label, frag_now); @@ -1864,6 +2174,28 @@ mips_move_labels (void) } } +static bfd_boolean +s_is_linkonce (symbolS *sym, segT from_seg) +{ + bfd_boolean linkonce = FALSE; + segT symseg = S_GET_SEGMENT (sym); + + if (symseg != from_seg && !S_IS_LOCAL (sym)) + { + if ((bfd_get_section_flags (stdoutput, symseg) & SEC_LINK_ONCE)) + linkonce = TRUE; +#ifdef OBJ_ELF + /* The GNU toolchain uses an extension for ELF: a section + beginning with the magic string .gnu.linkonce is a + linkonce section. */ + if (strncmp (segment_name (symseg), ".gnu.linkonce", + sizeof ".gnu.linkonce" - 1) == 0) + linkonce = TRUE; +#endif + } + return linkonce; +} + /* Mark instruction labels in mips16 mode. This permits the linker to handle them specially, such as generating jalx instructions when needed. We also make them odd for the duration of the assembly, in @@ -1875,21 +2207,29 @@ mips_move_labels (void) static void mips16_mark_labels (void) { - if (mips_opts.mips16) - { - struct insn_label_list *l; - valueT val; + segment_info_type *si = seg_info (now_seg); + struct insn_label_list *l; - for (l = insn_labels; l != NULL; l = l->next) - { -#ifdef OBJ_ELF - if (OUTPUT_FLAVOR == bfd_target_elf_flavour) - S_SET_OTHER (l->label, STO_MIPS16); + if (!mips_opts.mips16) + return; + + for (l = si->label_list; l != NULL; l = l->next) + { + symbolS *label = l->label; + +#if defined(OBJ_ELF) || defined(OBJ_MAYBE_ELF) + if (IS_ELF) + S_SET_OTHER (label, STO_MIPS16); #endif - val = S_GET_VALUE (l->label); - if ((val & 1) == 0) - S_SET_VALUE (l->label, val + 1); - } + if ((S_GET_VALUE (label) & 1) == 0 + /* Don't adjust the address if the label is global or weak, or + in a link-once section, since we'll be emitting symbol reloc + references to it which will be patched up by the linker, and + the final value of the symbol may or may not be MIPS16. */ + && ! S_IS_WEAK (label) + && ! S_IS_EXTERNAL (label) + && ! s_is_linkonce (label, now_seg)) + S_SET_VALUE (label, S_GET_VALUE (label) | 1); } } @@ -2214,9 +2554,10 @@ static void append_insn (struct mips_cl_insn *ip, expressionS *address_expr, bfd_reloc_code_real_type *reloc_type) { - register unsigned long prev_pinfo, pinfo; + unsigned long prev_pinfo, pinfo; relax_stateT prev_insn_frag_type = 0; bfd_boolean relaxed_branch = FALSE; + segment_info_type *si = seg_info (now_seg); /* Mark instruction labels in mips16 mode. */ mips16_mark_labels (); @@ -2479,6 +2820,11 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr, reloc_type[0] == BFD_RELOC_16_PCREL_S2, reloc_type[0]); + /* Tag symbols that have a R_MIPS16_26 relocation against them. */ + if (reloc_type[0] == BFD_RELOC_MIPS16_JMP + && ip->fixp[0]->fx_addsy) + *symbol_get_tc (ip->fixp[0]->fx_addsy) = 1; + /* These relocations can have an addend that won't fit in 4 octets for 64bit assembly. */ if (HAVE_64BIT_GPRS @@ -2633,7 +2979,7 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr, whether there is a label on this instruction. If there are any branches to anything other than a label, users must use .set noreorder. */ - || insn_labels != NULL + || si->label_list != NULL /* If the previous instruction is in a variant frag other than this branch's one, we cannot do the swap. This does not apply to the mips16, which uses variant @@ -2740,10 +3086,7 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr, if (mips_opts.mips16 && (pinfo & INSN_UNCOND_BRANCH_DELAY) && (pinfo & (MIPS16_INSN_READ_X | MIPS16_INSN_READ_31)) - && (mips_opts.isa == ISA_MIPS32 - || mips_opts.isa == ISA_MIPS32R2 - || mips_opts.isa == ISA_MIPS64 - || mips_opts.isa == ISA_MIPS64R2)) + && ISA_SUPPORTS_MIPS16E) { /* Convert MIPS16 jr/jalr into a "compact" jump. */ ip->insn_opcode |= 0x0080; @@ -3025,16 +3368,24 @@ macro_build (expressionS *ep, const char *name, const char *fmt, ...) assert (mo); assert (strcmp (name, mo->name) == 0); - /* Search until we get a match for NAME. It is assumed here that - macros will never generate MDMX or MIPS-3D instructions. */ - while (strcmp (fmt, mo->args) != 0 - || mo->pinfo == INSN_MACRO - || !OPCODE_IS_MEMBER (mo, + while (1) + { + /* Search until we get a match for NAME. It is assumed here that + macros will never generate MDMX, MIPS-3D, or MT instructions. */ + if (strcmp (fmt, mo->args) == 0 + && mo->pinfo != INSN_MACRO + && OPCODE_IS_MEMBER (mo, (mips_opts.isa - | (file_ase_mips16 ? INSN_MIPS16 : 0)), + | (mips_opts.mips16 ? INSN_MIPS16 : 0) + | (mips_opts.ase_dsp ? INSN_DSP : 0) + | ((mips_opts.ase_dsp && ISA_SUPPORTS_DSP64_ASE) + ? INSN_DSP64 : 0) + | (mips_opts.ase_dspr2 ? INSN_DSPR2 : 0) + | (mips_opts.ase_smartmips ? INSN_SMARTMIPS : 0)), mips_opts.arch) - || (mips_opts.arch == CPU_R4650 && (mo->pinfo & FP_D) != 0)) - { + && (mips_opts.arch != CPU_R4650 || (mo->pinfo & FP_D) == 0)) + break; + ++mo; assert (mo->name); assert (strcmp (name, mo->name) == 0); @@ -3085,6 +3436,10 @@ macro_build (expressionS *ep, const char *name, const char *fmt, ...) } continue; + case '2': + INSERT_OPERAND (BP, insn, va_arg (args, int)); + continue; + case 't': case 'w': case 'E': @@ -3212,7 +3567,11 @@ macro_build (expressionS *ep, const char *name, const char *fmt, ...) continue; case 'C': - insn.insn_opcode |= va_arg (args, unsigned long); + INSERT_OPERAND (COPZ, insn, va_arg (args, unsigned long)); + continue; + + case 'k': + INSERT_OPERAND (CACHE, insn, va_arg (args, unsigned long)); continue; default: @@ -3296,7 +3655,7 @@ mips16_macro_build (expressionS *ep, const char *name, const char *fmt, regno = va_arg (args, int); regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3); - insn.insn_opcode |= regno << MIPS16OP_SH_REG32R; + MIPS16_INSERT_OPERAND (REG32R, insn, regno); } continue; @@ -3412,7 +3771,7 @@ macro_build_lui (expressionS *ep, int regnum) if (high_expr.X_op == O_constant) { - /* we can compute the instruction now without a relocation entry */ + /* We can compute the instruction now without a relocation entry. */ high_expr.X_add_number = ((high_expr.X_add_number + 0x8000) >> 16) & 0xffff; *r = BFD_RELOC_UNUSED; @@ -4189,7 +4548,7 @@ add_got_offset_hilo (int dest, expressionS *local, int tmp) static void macro (struct mips_cl_insn *ip) { - register int treg, sreg, dreg, breg; + int treg, sreg, dreg, breg; int tempreg; int mask; int used_at = 0; @@ -4309,6 +4668,22 @@ macro (struct mips_cl_insn *ip) macro_build (NULL, s2, "d,v,t", treg, sreg, AT); break; + case M_BALIGN: + switch (imm_expr.X_add_number) + { + case 0: + macro_build (NULL, "nop", ""); + break; + case 2: + macro_build (NULL, "packrl.ph", "d,s,t", treg, treg, sreg); + break; + default: + macro_build (NULL, "balign", "t,s,2", treg, sreg, + (int)imm_expr.X_add_number); + break; + } + break; + case M_BEQ_I: s = "beq"; goto beq_i; @@ -5849,6 +6224,9 @@ macro (struct mips_cl_insn *ip) case M_SCD_AB: s = "scd"; goto st; + case M_CACHE_AB: + s = "cache"; + goto st; case M_SDC1_AB: if (mips_opts.arch == CPU_R4650) { @@ -5895,6 +6273,8 @@ macro (struct mips_cl_insn *ip) || mask == M_L_DAB || mask == M_S_DAB) fmt = "T,o(b)"; + else if (mask == M_CACHE_AB) + fmt = "k,o(b)"; else if (coproc) fmt = "E,o(b)"; else @@ -6854,7 +7234,7 @@ macro (struct mips_cl_insn *ip) static void macro2 (struct mips_cl_insn *ip) { - register int treg, sreg, dreg, breg; + int treg, sreg, dreg, breg; int tempreg; int mask; int used_at; @@ -7990,6 +8370,10 @@ validate_mips_insn (const struct mips_opcode *opc) case '+': switch (c = *p++) { + case '1': USE_BITS (OP_MASK_UDI1, OP_SH_UDI1); break; + case '2': USE_BITS (OP_MASK_UDI2, OP_SH_UDI2); break; + case '3': USE_BITS (OP_MASK_UDI3, OP_SH_UDI3); break; + case '4': USE_BITS (OP_MASK_UDI4, OP_SH_UDI4); break; case 'A': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break; case 'B': USE_BITS (OP_MASK_INSMSB, OP_SH_INSMSB); break; case 'C': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break; @@ -8065,6 +8449,7 @@ validate_mips_insn (const struct mips_opcode *opc) case '%': USE_BITS (OP_MASK_VECALIGN, OP_SH_VECALIGN); break; case '[': break; case ']': break; + case '2': USE_BITS (OP_MASK_BP, OP_SH_BP); break; case '3': USE_BITS (OP_MASK_SA3, OP_SH_SA3); break; case '4': USE_BITS (OP_MASK_SA4, OP_SH_SA4); break; case '5': USE_BITS (OP_MASK_IMM8, OP_SH_IMM8); break; @@ -8096,6 +8481,62 @@ validate_mips_insn (const struct mips_opcode *opc) return 1; } +/* UDI immediates. */ +struct mips_immed { + char type; + unsigned int shift; + unsigned long mask; + const char * desc; +}; + +static const struct mips_immed mips_immed[] = { + { '1', OP_SH_UDI1, OP_MASK_UDI1, 0}, + { '2', OP_SH_UDI2, OP_MASK_UDI2, 0}, + { '3', OP_SH_UDI3, OP_MASK_UDI3, 0}, + { '4', OP_SH_UDI4, OP_MASK_UDI4, 0}, + { 0,0,0,0 } +}; + +/* Check whether an odd floating-point register is allowed. */ +static int +mips_oddfpreg_ok (const struct mips_opcode *insn, int argnum) +{ + const char *s = insn->name; + + if (insn->pinfo == INSN_MACRO) + /* Let a macro pass, we'll catch it later when it is expanded. */ + return 1; + + if (ISA_HAS_ODD_SINGLE_FPR (mips_opts.isa)) + { + /* Allow odd registers for single-precision ops. */ + switch (insn->pinfo & (FP_S | FP_D)) + { + case FP_S: + case 0: + return 1; /* both single precision - ok */ + case FP_D: + return 0; /* both double precision - fail */ + default: + break; + } + + /* Cvt.w.x and cvt.x.w allow an odd register for a 'w' or 's' operand. */ + s = strchr (insn->name, '.'); + if (argnum == 2) + s = s != NULL ? strchr (s + 1, '.') : NULL; + return (s != NULL && (s[1] == 'w' || s[1] == 's')); + } + + /* Single-precision coprocessor loads and moves are OK too. */ + if ((insn->pinfo & FP_S) + && (insn->pinfo & (INSN_COPROC_MEMORY_DELAY | INSN_STORE_MEMORY + | INSN_LOAD_COPROC_DELAY | INSN_COPROC_MOVE_DELAY))) + return 1; + + return 0; +} + /* This routine assembles an instruction into its binary format. As a side effect, it sets one of the global variables imm_reloc or offset_reloc to the type of relocation to do if one of the operands @@ -8116,6 +8557,8 @@ mips_ip (char *str, struct mips_cl_insn *ip) char *s_reset; char save_c = 0; offsetT min_range, max_range; + int argnum; + unsigned int rtype; insn_error = NULL; @@ -8176,11 +8619,18 @@ mips_ip (char *str, struct mips_cl_insn *ip) if (OPCODE_IS_MEMBER (insn, (mips_opts.isa + /* We don't check for mips_opts.mips16 here since + we want to allow jalx if -mips16 was specified + on the command line. */ | (file_ase_mips16 ? INSN_MIPS16 : 0) | (mips_opts.ase_mdmx ? INSN_MDMX : 0) | (mips_opts.ase_dsp ? INSN_DSP : 0) + | ((mips_opts.ase_dsp && ISA_SUPPORTS_DSP64_ASE) + ? INSN_DSP64 : 0) + | (mips_opts.ase_dspr2 ? INSN_DSPR2 : 0) | (mips_opts.ase_mt ? INSN_MT : 0) - | (mips_opts.ase_mips3d ? INSN_MIPS3D : 0)), + | (mips_opts.ase_mips3d ? INSN_MIPS3D : 0) + | (mips_opts.ase_smartmips ? INSN_SMARTMIPS : 0)), mips_opts.arch)) ok = TRUE; else @@ -8295,6 +8745,7 @@ mips_ip (char *str, struct mips_cl_insn *ip) create_insn (ip, insn); insn_error = NULL; + argnum = 1; for (args = insn->args;; ++args) { int is_mdmx; @@ -8308,16 +8759,29 @@ mips_ip (char *str, struct mips_cl_insn *ip) return; break; + case '2': /* dsp 2-bit unsigned immediate in bit 11 */ + my_getExpression (&imm_expr, s); + check_absolute_expr (ip, &imm_expr); + if ((unsigned long) imm_expr.X_add_number != 1 + && (unsigned long) imm_expr.X_add_number != 3) + { + as_bad (_("BALIGN immediate not 1 or 3 (%lu)"), + (unsigned long) imm_expr.X_add_number); + } + INSERT_OPERAND (BP, *ip, imm_expr.X_add_number); + imm_expr.X_op = O_absent; + s = expr_end; + continue; + case '3': /* dsp 3-bit unsigned immediate in bit 21 */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if (imm_expr.X_add_number & ~OP_MASK_SA3) { - as_warn (_("DSP immediate not in range 0..%d (%lu)"), - OP_MASK_SA3, (unsigned long) imm_expr.X_add_number); - imm_expr.X_add_number &= OP_MASK_SA3; + as_bad (_("DSP immediate not in range 0..%d (%lu)"), + OP_MASK_SA3, (unsigned long) imm_expr.X_add_number); } - ip->insn_opcode |= imm_expr.X_add_number << OP_SH_SA3; + INSERT_OPERAND (SA3, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; @@ -8327,11 +8791,10 @@ mips_ip (char *str, struct mips_cl_insn *ip) check_absolute_expr (ip, &imm_expr); if (imm_expr.X_add_number & ~OP_MASK_SA4) { - as_warn (_("DSP immediate not in range 0..%d (%lu)"), - OP_MASK_SA4, (unsigned long) imm_expr.X_add_number); - imm_expr.X_add_number &= OP_MASK_SA4; + as_bad (_("DSP immediate not in range 0..%d (%lu)"), + OP_MASK_SA4, (unsigned long) imm_expr.X_add_number); } - ip->insn_opcode |= imm_expr.X_add_number << OP_SH_SA4; + INSERT_OPERAND (SA4, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; @@ -8341,11 +8804,10 @@ mips_ip (char *str, struct mips_cl_insn *ip) check_absolute_expr (ip, &imm_expr); if (imm_expr.X_add_number & ~OP_MASK_IMM8) { - as_warn (_("DSP immediate not in range 0..%d (%lu)"), - OP_MASK_IMM8, (unsigned long) imm_expr.X_add_number); - imm_expr.X_add_number &= OP_MASK_IMM8; + as_bad (_("DSP immediate not in range 0..%d (%lu)"), + OP_MASK_IMM8, (unsigned long) imm_expr.X_add_number); } - ip->insn_opcode |= imm_expr.X_add_number << OP_SH_IMM8; + INSERT_OPERAND (IMM8, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; @@ -8355,11 +8817,10 @@ mips_ip (char *str, struct mips_cl_insn *ip) check_absolute_expr (ip, &imm_expr); if (imm_expr.X_add_number & ~OP_MASK_RS) { - as_warn (_("DSP immediate not in range 0..%d (%lu)"), - OP_MASK_RS, (unsigned long) imm_expr.X_add_number); - imm_expr.X_add_number &= OP_MASK_RS; + as_bad (_("DSP immediate not in range 0..%d (%lu)"), + OP_MASK_RS, (unsigned long) imm_expr.X_add_number); } - ip->insn_opcode |= imm_expr.X_add_number << OP_SH_RS; + INSERT_OPERAND (RS, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; @@ -8370,7 +8831,7 @@ mips_ip (char *str, struct mips_cl_insn *ip) { regno = s[3] - '0'; s += 4; - ip->insn_opcode |= regno << OP_SH_DSPACC; + INSERT_OPERAND (DSPACC, *ip, regno); continue; } else @@ -8382,12 +8843,11 @@ mips_ip (char *str, struct mips_cl_insn *ip) check_absolute_expr (ip, &imm_expr); if (imm_expr.X_add_number & ~OP_MASK_WRDSP) { - as_warn (_("DSP immediate not in range 0..%d (%lu)"), - OP_MASK_WRDSP, - (unsigned long) imm_expr.X_add_number); - imm_expr.X_add_number &= OP_MASK_WRDSP; + as_bad (_("DSP immediate not in range 0..%d (%lu)"), + OP_MASK_WRDSP, + (unsigned long) imm_expr.X_add_number); } - ip->insn_opcode |= imm_expr.X_add_number << OP_SH_WRDSP; + INSERT_OPERAND (WRDSP, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; @@ -8398,7 +8858,7 @@ mips_ip (char *str, struct mips_cl_insn *ip) { regno = s[3] - '0'; s += 4; - ip->insn_opcode |= regno << OP_SH_DSPACC_S; + INSERT_OPERAND (DSPACC_S, *ip, regno); continue; } else @@ -8413,13 +8873,11 @@ mips_ip (char *str, struct mips_cl_insn *ip) if (imm_expr.X_add_number < min_range || imm_expr.X_add_number > max_range) { - as_warn (_("DSP immediate not in range %ld..%ld (%ld)"), - (long) min_range, (long) max_range, - (long) imm_expr.X_add_number); + as_bad (_("DSP immediate not in range %ld..%ld (%ld)"), + (long) min_range, (long) max_range, + (long) imm_expr.X_add_number); } - imm_expr.X_add_number &= OP_MASK_DSPSFT; - ip->insn_opcode |= ((unsigned long) imm_expr.X_add_number - << OP_SH_DSPSFT); + INSERT_OPERAND (DSPSFT, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; @@ -8429,12 +8887,11 @@ mips_ip (char *str, struct mips_cl_insn *ip) check_absolute_expr (ip, &imm_expr); if (imm_expr.X_add_number & ~OP_MASK_RDDSP) { - as_warn (_("DSP immediate not in range 0..%d (%lu)"), - OP_MASK_RDDSP, - (unsigned long) imm_expr.X_add_number); - imm_expr.X_add_number &= OP_MASK_RDDSP; + as_bad (_("DSP immediate not in range 0..%d (%lu)"), + OP_MASK_RDDSP, + (unsigned long) imm_expr.X_add_number); } - ip->insn_opcode |= imm_expr.X_add_number << OP_SH_RDDSP; + INSERT_OPERAND (RDDSP, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; @@ -8447,13 +8904,11 @@ mips_ip (char *str, struct mips_cl_insn *ip) if (imm_expr.X_add_number < min_range || imm_expr.X_add_number > max_range) { - as_warn (_("DSP immediate not in range %ld..%ld (%ld)"), - (long) min_range, (long) max_range, - (long) imm_expr.X_add_number); + as_bad (_("DSP immediate not in range %ld..%ld (%ld)"), + (long) min_range, (long) max_range, + (long) imm_expr.X_add_number); } - imm_expr.X_add_number &= OP_MASK_DSPSFT_7; - ip->insn_opcode |= ((unsigned long) imm_expr.X_add_number - << OP_SH_DSPSFT_7); + INSERT_OPERAND (DSPSFT_7, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; @@ -8466,41 +8921,33 @@ mips_ip (char *str, struct mips_cl_insn *ip) if (imm_expr.X_add_number < min_range || imm_expr.X_add_number > max_range) { - as_warn (_("DSP immediate not in range %ld..%ld (%ld)"), - (long) min_range, (long) max_range, - (long) imm_expr.X_add_number); + as_bad (_("DSP immediate not in range %ld..%ld (%ld)"), + (long) min_range, (long) max_range, + (long) imm_expr.X_add_number); } - imm_expr.X_add_number &= OP_MASK_IMM10; - ip->insn_opcode |= ((unsigned long) imm_expr.X_add_number - << OP_SH_IMM10); + INSERT_OPERAND (IMM10, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; - case '!': /* mt 1-bit unsigned immediate in bit 5 */ + case '!': /* MT usermode flag bit. */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if (imm_expr.X_add_number & ~OP_MASK_MT_U) - { - as_warn (_("MT immediate not in range 0..%d (%lu)"), - OP_MASK_MT_U, (unsigned long) imm_expr.X_add_number); - imm_expr.X_add_number &= OP_MASK_MT_U; - } - ip->insn_opcode |= imm_expr.X_add_number << OP_SH_MT_U; + as_bad (_("MT usermode bit not 0 or 1 (%lu)"), + (unsigned long) imm_expr.X_add_number); + INSERT_OPERAND (MT_U, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; - case '$': /* mt 1-bit unsigned immediate in bit 4 */ + case '$': /* MT load high flag bit. */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if (imm_expr.X_add_number & ~OP_MASK_MT_H) - { - as_warn (_("MT immediate not in range 0..%d (%lu)"), - OP_MASK_MT_H, (unsigned long) imm_expr.X_add_number); - imm_expr.X_add_number &= OP_MASK_MT_H; - } - ip->insn_opcode |= imm_expr.X_add_number << OP_SH_MT_H; + as_bad (_("MT load high bit not 0 or 1 (%lu)"), + (unsigned long) imm_expr.X_add_number); + INSERT_OPERAND (MT_H, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; @@ -8511,7 +8958,7 @@ mips_ip (char *str, struct mips_cl_insn *ip) { regno = s[3] - '0'; s += 4; - ip->insn_opcode |= regno << OP_SH_MTACC_T; + INSERT_OPERAND (MTACC_T, *ip, regno); continue; } else @@ -8524,7 +8971,7 @@ mips_ip (char *str, struct mips_cl_insn *ip) { regno = s[3] - '0'; s += 4; - ip->insn_opcode |= regno << OP_SH_MTACC_D; + INSERT_OPERAND (MTACC_D, *ip, regno); continue; } else @@ -8532,6 +8979,7 @@ mips_ip (char *str, struct mips_cl_insn *ip) break; case ',': + ++argnum; if (*s++ == *args) continue; s--; @@ -8577,6 +9025,34 @@ mips_ip (char *str, struct mips_cl_insn *ip) case '+': /* Opcode extension character. */ switch (*++args) { + case '1': /* UDI immediates. */ + case '2': + case '3': + case '4': + { + const struct mips_immed *imm = mips_immed; + + while (imm->type && imm->type != *args) + ++imm; + if (! imm->type) + internalError (); + my_getExpression (&imm_expr, s); + check_absolute_expr (ip, &imm_expr); + if ((unsigned long) imm_expr.X_add_number & ~imm->mask) + { + as_warn (_("Illegal %s number (%lu, 0x%lx)"), + imm->desc ? imm->desc : ip->insn_mo->name, + (unsigned long) imm_expr.X_add_number, + (unsigned long) imm_expr.X_add_number); + imm_expr.X_add_number &= imm->mask; + } + ip->insn_opcode |= ((unsigned long) imm_expr.X_add_number + << imm->shift); + imm_expr.X_op = O_absent; + s = expr_end; + } + continue; + case 'A': /* ins/ext position, becomes LSB. */ limlo = 0; limhi = 31; @@ -8683,11 +9159,11 @@ do_msbd: s = expr_end; continue; - case 'T': /* Coprocessor register */ + case 'T': /* Coprocessor register. */ /* +T is for disassembly only; never match. */ break; - case 't': /* Coprocessor register number */ + case 't': /* Coprocessor register number. */ if (s[0] == '$' && ISDIGIT (s[1])) { ++s; @@ -8703,7 +9179,7 @@ do_msbd: as_bad (_("Invalid register number (%d)"), regno); else { - ip->insn_opcode |= regno << OP_SH_RT; + INSERT_OPERAND (RT, *ip, regno); continue; } } @@ -8794,8 +9270,9 @@ do_msbd: case 'c': /* break code */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); - if ((unsigned long) imm_expr.X_add_number > 1023) - as_warn (_("Illegal break code (%lu)"), + if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE) + as_warn (_("Code for %s not in range 0..1023 (%lu)"), + ip->insn_mo->name, (unsigned long) imm_expr.X_add_number); INSERT_OPERAND (CODE, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; @@ -8805,8 +9282,9 @@ do_msbd: case 'q': /* lower break code */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); - if ((unsigned long) imm_expr.X_add_number > 1023) - as_warn (_("Illegal lower break code (%lu)"), + if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE2) + as_warn (_("Lower code for %s not in range 0..1023 (%lu)"), + ip->insn_mo->name, (unsigned long) imm_expr.X_add_number); INSERT_OPERAND (CODE2, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; @@ -8834,7 +9312,8 @@ do_msbd: my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE20) - as_warn (_("Illegal 20-bit code (%lu)"), + as_warn (_("Code for %s not in range 0..1048575 (%lu)"), + ip->insn_mo->name, (unsigned long) imm_expr.X_add_number); INSERT_OPERAND (CODE20, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; @@ -8844,13 +9323,13 @@ do_msbd: case 'C': /* Coprocessor code */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); - if ((unsigned long) imm_expr.X_add_number >= (1 << 25)) + if ((unsigned long) imm_expr.X_add_number > OP_MASK_COPZ) { as_warn (_("Coproccesor code > 25 bits (%lu)"), (unsigned long) imm_expr.X_add_number); - imm_expr.X_add_number &= ((1 << 25) - 1); + imm_expr.X_add_number &= OP_MASK_COPZ; } - ip->insn_opcode |= imm_expr.X_add_number; + INSERT_OPERAND (COPZ, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; @@ -8859,14 +9338,17 @@ do_msbd: my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE19) - as_warn (_("Illegal 19-bit code (%lu)"), - (unsigned long) imm_expr.X_add_number); + { + as_warn (_("Illegal 19-bit code (%lu)"), + (unsigned long) imm_expr.X_add_number); + imm_expr.X_add_number &= OP_MASK_CODE19; + } INSERT_OPERAND (CODE19, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; continue; - case 'P': /* Performance register */ + case 'P': /* Performance register. */ my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if (imm_expr.X_add_number != 0 && imm_expr.X_add_number != 1) @@ -8877,6 +9359,20 @@ do_msbd: s = expr_end; continue; + case 'G': /* Coprocessor destination register. */ + if (((ip->insn_opcode >> OP_SH_OP) & OP_MASK_OP) == OP_OP_COP0) + ok = reg_lookup (&s, RTYPE_NUM | RTYPE_CP0, ®no); + else + ok = reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no); + INSERT_OPERAND (RD, *ip, regno); + if (ok) + { + lastregno = regno; + continue; + } + else + break; + case 'b': /* base register */ case 'd': /* destination register */ case 's': /* source register */ @@ -8885,106 +9381,22 @@ do_msbd: case 'v': /* both dest and source */ case 'w': /* both dest and target */ case 'E': /* coprocessor target register */ - case 'G': /* coprocessor destination register */ case 'K': /* 'rdhwr' destination register */ case 'x': /* ignore register name */ case 'z': /* must be zero register */ case 'U': /* destination register (clo/clz). */ case 'g': /* coprocessor destination register */ - s_reset = s; - if (s[0] == '$') + s_reset = s; + if (*args == 'E' || *args == 'K') + ok = reg_lookup (&s, RTYPE_NUM, ®no); + else + { + ok = reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no); + if (regno == AT && ! mips_opts.noat) + as_warn ("Used $at without \".set noat\""); + } + if (ok) { - if (ISDIGIT (s[1])) - { - ++s; - regno = 0; - do - { - regno *= 10; - regno += *s - '0'; - ++s; - } - while (ISDIGIT (*s)); - if (regno > 31) - as_bad (_("Invalid register number (%d)"), regno); - } - else if (*args == 'E' || *args == 'G' || *args == 'K') - goto notreg; - else - { - if (s[1] == 'r' && s[2] == 'a') - { - s += 3; - regno = RA; - } - else if (s[1] == 'f' && s[2] == 'p') - { - s += 3; - regno = FP; - } - else if (s[1] == 's' && s[2] == 'p') - { - s += 3; - regno = SP; - } - else if (s[1] == 'g' && s[2] == 'p') - { - s += 3; - regno = GP; - } - else if (s[1] == 'a' && s[2] == 't') - { - s += 3; - regno = AT; - } - else if (s[1] == 'k' && s[2] == 't' && s[3] == '0') - { - s += 4; - regno = KT0; - } - else if (s[1] == 'k' && s[2] == 't' && s[3] == '1') - { - s += 4; - regno = KT1; - } - else if (s[1] == 'z' && s[2] == 'e' && s[3] == 'r' && s[4] == 'o') - { - s += 5; - regno = ZERO; - } - else if (itbl_have_entries) - { - char *p, *n; - unsigned long r; - - p = s + 1; /* advance past '$' */ - n = itbl_get_field (&p); /* n is name */ - - /* See if this is a register defined in an - itbl entry. */ - if (itbl_get_reg_val (n, &r)) - { - /* Get_field advances to the start of - the next field, so we need to back - rack to the end of the last field. */ - if (p) - s = p - 1; - else - s = strchr (s, '\0'); - regno = r; - } - else - goto notreg; - } - else - goto notreg; - } - if (regno == AT - && ! mips_opts.noat - && *args != 'E' - && *args != 'G' - && *args != 'K') - as_warn (_("Used $at without \".set noat\"")); c = *args; if (*s == ' ') ++s; @@ -9050,7 +9462,6 @@ do_msbd: lastregno = regno; continue; } - notreg: switch (*args++) { case 'r': @@ -9103,45 +9514,27 @@ do_msbd: case 'R': /* floating point source register */ case 'V': case 'W': + rtype = RTYPE_FPU; + if (is_mdmx + || (mips_opts.ase_mdmx + && (ip->insn_mo->pinfo & FP_D) + && (ip->insn_mo->pinfo & (INSN_COPROC_MOVE_DELAY + | INSN_COPROC_MEMORY_DELAY + | INSN_LOAD_COPROC_DELAY + | INSN_LOAD_MEMORY_DELAY + | INSN_STORE_MEMORY)))) + rtype |= RTYPE_VEC; s_reset = s; - if (mips_opts.arch == CPU_OCTEON && octeon_error_on_unsupported) - { - insn_error = "opcode not implemented in Octeon"; - return; - } - /* Accept $fN for FP and MDMX register numbers, and in - addition accept $vN for MDMX register numbers. */ - if ((s[0] == '$' && s[1] == 'f' && ISDIGIT (s[2])) - || (is_mdmx != 0 && s[0] == '$' && s[1] == 'v' - && ISDIGIT (s[2]))) + if (mips_opts.arch == CPU_OCTEON && octeon_error_on_unsupported) + { + insn_error = "opcode not implemented in Octeon"; + return; + } + if (reg_lookup (&s, rtype, ®no)) { - s += 2; - regno = 0; - do - { - regno *= 10; - regno += *s - '0'; - ++s; - } - while (ISDIGIT (*s)); - - if (regno > 31) - as_bad (_("Invalid float register number (%d)"), regno); - if ((regno & 1) != 0 && HAVE_32BIT_FPRS - && ! (strcmp (str, "mtc1") == 0 - || strcmp (str, "mfc1") == 0 - || strcmp (str, "lwc1") == 0 - || strcmp (str, "swc1") == 0 - || strcmp (str, "l.s") == 0 - || strcmp (str, "s.s") == 0 - || strcmp (str, "mftc1") == 0 - || strcmp (str, "mfthc1") == 0 - || strcmp (str, "cftc1") == 0 - || strcmp (str, "mttc1") == 0 - || strcmp (str, "mtthc1") == 0 - || strcmp (str, "cttc1") == 0)) + && ! mips_oddfpreg_ok (ip->insn_mo, argnum)) as_warn (_("Float register should be even, was %d"), regno); @@ -9403,15 +9796,14 @@ do_msbd: break; } new_seg = subseg_new (newname, (subsegT) 0); - if (OUTPUT_FLAVOR == bfd_target_elf_flavour) + if (IS_ELF) bfd_set_section_flags (stdoutput, new_seg, (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA)); frag_align (*args == 'l' ? 2 : 3, 0, 0); - if (OUTPUT_FLAVOR == bfd_target_elf_flavour - && strcmp (TARGET_OS, "elf") != 0) + if (IS_ELF && strcmp (TARGET_OS, "elf") != 0) record_alignment (new_seg, 4); else record_alignment (new_seg, *args == 'l' ? 2 : 3); @@ -9529,19 +9921,11 @@ do_msbd: case 'N': /* 3 bit branch condition code */ case 'M': /* 3 bit compare condition code */ - if (strncmp (s, "$fcc", 4) != 0) + rtype = RTYPE_CCC; + if (ip->insn_mo->pinfo & (FP_D| FP_S)) + rtype |= RTYPE_FCC; + if (!reg_lookup (&s, rtype, ®no)) break; - s += 4; - regno = 0; - do - { - regno *= 10; - regno += *s - '0'; - ++s; - } - while (ISDIGIT (*s)); - if (regno > 7) - as_bad (_("Invalid condition code register $fcc%d"), regno); if ((strcmp(str + strlen(str) - 3, ".ps") == 0 || strcmp(str + strlen(str) - 5, "any2f") == 0 || strcmp(str + strlen(str) - 5, "any2t") == 0) @@ -9708,8 +10092,38 @@ mips16_ip (char *str, struct mips_cl_insn *ip) argsstart = s; for (;;) { + bfd_boolean ok; + assert (strcmp (insn->name, str) == 0); + if (OPCODE_IS_MEMBER (insn, mips_opts.isa, mips_opts.arch)) + ok = TRUE; + else + ok = FALSE; + + if (! ok) + { + if (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes] + && strcmp (insn->name, insn[1].name) == 0) + { + ++insn; + continue; + } + else + { + if (!insn_error) + { + static char buf[100]; + sprintf (buf, + _("opcode not supported on this processor: %s (%s)"), + mips_cpu_info_from_arch (mips_opts.arch)->name, + mips_cpu_info_from_isa (mips_opts.isa)->name); + insn_error = buf; + } + return; + } + } + create_insn (ip, insn); imm_expr.X_op = O_absent; imm_reloc[0] = BFD_RELOC_UNUSED; @@ -9822,70 +10236,19 @@ mips16_ip (char *str, struct mips_cl_insn *ip) case 'R': case 'X': case 'Y': - if (s[0] != '$') - break; - s_reset = s; - if (ISDIGIT (s[1])) - { - ++s; - regno = 0; - do - { - regno *= 10; - regno += *s - '0'; - ++s; - } - while (ISDIGIT (*s)); - if (regno > 31) - { - as_bad (_("invalid register number (%d)"), regno); - regno = 2; - } - } - else + s_reset = s; + if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no)) { - if (s[1] == 'r' && s[2] == 'a') - { - s += 3; - regno = RA; - } - else if (s[1] == 'f' && s[2] == 'p') - { - s += 3; - regno = FP; - } - else if (s[1] == 's' && s[2] == 'p') - { - s += 3; - regno = SP; - } - else if (s[1] == 'g' && s[2] == 'p') - { - s += 3; - regno = GP; - } - else if (s[1] == 'a' && s[2] == 't') - { - s += 3; - regno = AT; - } - else if (s[1] == 'k' && s[2] == 't' && s[3] == '0') - { - s += 4; - regno = KT0; - } - else if (s[1] == 'k' && s[2] == 't' && s[3] == '1') - { - s += 4; - regno = KT1; - } - else if (s[1] == 'z' && s[2] == 'e' && s[3] == 'r' && s[4] == 'o') + if (c == 'v' || c == 'w') { - s += 5; - regno = ZERO; + if (c == 'v') + MIPS16_INSERT_OPERAND (RX, *ip, lastregno); + else + MIPS16_INSERT_OPERAND (RY, *ip, lastregno); + ++args; + continue; } - else - break; + break; } if (*s == ' ') @@ -10091,29 +10454,18 @@ mips16_ip (char *str, struct mips_cl_insn *ip) mask = 7 << 3; while (*s != '\0') { - int freg, reg1, reg2; + unsigned int freg, reg1, reg2; while (*s == ' ' || *s == ',') ++s; - if (*s != '$') - { - as_bad (_("can't parse register list")); - break; - } - ++s; - if (*s != 'f') + if (reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®1)) freg = 0; + else if (reg_lookup (&s, RTYPE_FPU, ®1)) + freg = 1; else { - freg = 1; - ++s; - } - reg1 = 0; - while (ISDIGIT (*s)) - { - reg1 *= 10; - reg1 += *s - '0'; - ++s; + as_bad (_("can't parse register list")); + break; } if (*s == ' ') ++s; @@ -10122,25 +10474,11 @@ mips16_ip (char *str, struct mips_cl_insn *ip) else { ++s; - if (*s != '$') - break; - ++s; - if (freg) - { - if (*s == 'f') - ++s; - else - { - as_bad (_("invalid register list")); - break; - } - } - reg2 = 0; - while (ISDIGIT (*s)) + if (!reg_lookup (&s, freg ? RTYPE_FPU + : (RTYPE_GP | RTYPE_NUM), ®2)) { - reg2 *= 10; - reg2 += *s - '0'; - ++s; + as_bad (_("invalid register list")); + break; } } if (freg && reg1 == 0 && reg2 == 0 && c == 'L') @@ -10205,46 +10543,33 @@ mips16_ip (char *str, struct mips_cl_insn *ip) continue; } - if (*s != '$') + if (! reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®1)) { as_bad (_("can't parse register list")); break; } - ++s; - reg1 = 0; - while (ISDIGIT (*s)) - { - reg1 *= 10; - reg1 += *s - '0'; - ++s; - } - SKIP_SPACE_TABS (s); + while (*s == ' ') + ++s; + if (*s != '-') reg2 = reg1; else { ++s; - if (*s != '$') + if (! reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®2) + || reg2 < reg1) { as_bad (_("can't parse register list")); break; } - ++s; - reg2 = 0; - while (ISDIGIT (*s)) - { - reg2 *= 10; - reg2 += *s - '0'; - ++s; - } } while (reg1 <= reg2) { if (reg1 >= 4 && reg1 <= 7) { - if (c == 'm' && !seen_framesz) + if (!seen_framesz) /* args $a0-$a3 */ args |= 1 << (reg1 - 4); else @@ -10456,7 +10781,7 @@ mips16_immed (char *file, unsigned int line, int type, offsetT val, unsigned long *insn, bfd_boolean *use_extend, unsigned short *extend) { - register const struct mips16_immed_operand *op; + const struct mips16_immed_operand *op; int mintiny, maxtiny; bfd_boolean needext; @@ -10866,9 +11191,17 @@ struct option md_longopts[] = {"mmt", no_argument, NULL, OPTION_MT}, #define OPTION_NO_MT (OPTION_ASE_BASE + 9) {"mno-mt", no_argument, NULL, OPTION_NO_MT}, +#define OPTION_SMARTMIPS (OPTION_ASE_BASE + 10) + {"msmartmips", no_argument, NULL, OPTION_SMARTMIPS}, +#define OPTION_NO_SMARTMIPS (OPTION_ASE_BASE + 11) + {"mno-smartmips", no_argument, NULL, OPTION_NO_SMARTMIPS}, +#define OPTION_DSPR2 (OPTION_ASE_BASE + 12) + {"mdspr2", no_argument, NULL, OPTION_DSPR2}, +#define OPTION_NO_DSPR2 (OPTION_ASE_BASE + 13) + {"mno-dspr2", no_argument, NULL, OPTION_NO_DSPR2}, /* Old-style architecture options. Don't add more of these. */ -#define OPTION_COMPAT_ARCH_BASE (OPTION_ASE_BASE + 10) +#define OPTION_COMPAT_ARCH_BASE (OPTION_ASE_BASE + 14) #define OPTION_M4650 (OPTION_COMPAT_ARCH_BASE + 0) {"m4650", no_argument, NULL, OPTION_M4650}, #define OPTION_NO_M4650 (OPTION_COMPAT_ARCH_BASE + 1) @@ -11046,7 +11379,7 @@ md_parse_option (int c, char *arg) break; case 'O': - if (arg && arg[1] == '0') + if (arg && arg[0] == '0') mips_optimize = 1; else mips_optimize = 2; @@ -11057,11 +11390,6 @@ md_parse_option (int c, char *arg) mips_debug = 2; else mips_debug = atoi (arg); - /* When the MIPS assembler sees -g or -g2, it does not do - optimizations which limit full symbolic debugging. We take - that to be equivalent to -O0. */ - if (mips_debug == 2) - mips_optimize = 1; break; case OPTION_MIPS1: @@ -11150,10 +11478,22 @@ md_parse_option (int c, char *arg) case OPTION_DSP: mips_opts.ase_dsp = 1; + mips_opts.ase_dspr2 = 0; break; case OPTION_NO_DSP: mips_opts.ase_dsp = 0; + mips_opts.ase_dspr2 = 0; + break; + + case OPTION_DSPR2: + mips_opts.ase_dspr2 = 1; + mips_opts.ase_dsp = 1; + break; + + case OPTION_NO_DSPR2: + mips_opts.ase_dspr2 = 0; + mips_opts.ase_dsp = 0; break; case OPTION_MT: @@ -11182,6 +11522,14 @@ md_parse_option (int c, char *arg) mips_opts.ase_mips3d = 0; break; + case OPTION_SMARTMIPS: + mips_opts.ase_smartmips = 1; + break; + + case OPTION_NO_SMARTMIPS: + mips_opts.ase_smartmips = 0; + break; + case OPTION_FIX_VR4120: mips_fix_vr4120 = 1; break; @@ -11227,7 +11575,7 @@ md_parse_option (int c, char *arg) select SVR4_PIC, and -non_shared to select no PIC. This is intended to be compatible with Irix 5. */ case OPTION_CALL_SHARED: - if (OUTPUT_FLAVOR != bfd_target_elf_flavour) + if (!IS_ELF) { as_bad (_("-call_shared is supported only for ELF format")); return 0; @@ -11237,7 +11585,7 @@ md_parse_option (int c, char *arg) break; case OPTION_NON_SHARED: - if (OUTPUT_FLAVOR != bfd_target_elf_flavour) + if (!IS_ELF) { as_bad (_("-non_shared is supported only for ELF format")); return 0; @@ -11263,7 +11611,7 @@ md_parse_option (int c, char *arg) /* The -32, -n32 and -64 options are shortcuts for -mabi=32, -mabi=n32 and -mabi=64. */ case OPTION_32: - if (OUTPUT_FLAVOR != bfd_target_elf_flavour) + if (!IS_ELF) { as_bad (_("-32 is supported for ELF format only")); return 0; @@ -11272,7 +11620,7 @@ md_parse_option (int c, char *arg) break; case OPTION_N32: - if (OUTPUT_FLAVOR != bfd_target_elf_flavour) + if (!IS_ELF) { as_bad (_("-n32 is supported for ELF format only")); return 0; @@ -11281,13 +11629,13 @@ md_parse_option (int c, char *arg) break; case OPTION_64: - if (OUTPUT_FLAVOR != bfd_target_elf_flavour) + if (!IS_ELF) { as_bad (_("-64 is supported for ELF format only")); return 0; } mips_abi = N64_ABI; - if (! support_64bit_objects()) + if (!support_64bit_objects()) as_fatal (_("No compiled in support for 64 bit object file format")); break; #endif /* OBJ_ELF */ @@ -11310,7 +11658,7 @@ md_parse_option (int c, char *arg) #ifdef OBJ_ELF case OPTION_MABI: - if (OUTPUT_FLAVOR != bfd_target_elf_flavour) + if (!IS_ELF) { as_bad (_("-mabi is supported for ELF format only")); return 0; @@ -11482,14 +11830,43 @@ mips_after_parse_args (void) || !ISA_HAS_64BIT_REGS (mips_opts.isa)); } - /* ??? GAS treats single-float processors as though they had 64-bit - float registers (although it complains when double-precision - instructions are used). As things stand, saying they have 32-bit - registers would lead to spurious "register must be even" messages. - So here we assume float registers are always the same size as - integer ones, unless the user says otherwise. */ - if (file_mips_fp32 < 0) - file_mips_fp32 = file_mips_gp32; + switch (file_mips_fp32) + { + default: + case -1: + /* No user specified float register size. + ??? GAS treats single-float processors as though they had 64-bit + float registers (although it complains when double-precision + instructions are used). As things stand, saying they have 32-bit + registers would lead to spurious "register must be even" messages. + So here we assume float registers are never smaller than the + integer ones. */ + if (file_mips_gp32 == 0) + /* 64-bit integer registers implies 64-bit float registers. */ + file_mips_fp32 = 0; + else if ((mips_opts.ase_mips3d > 0 || mips_opts.ase_mdmx > 0) + && ISA_HAS_64BIT_FPRS (mips_opts.isa)) + /* -mips3d and -mdmx imply 64-bit float registers, if possible. */ + file_mips_fp32 = 0; + else + /* 32-bit float registers. */ + file_mips_fp32 = 1; + break; + + /* The user specified the size of the float registers. Check if it + agrees with the ABI and ISA. */ + case 0: + if (!ISA_HAS_64BIT_FPRS (mips_opts.isa)) + as_bad (_("-mfp64 used with a 32-bit fpu")); + else if (ABI_NEEDS_32BIT_REGS (mips_abi) + && !ISA_HAS_MXHC1 (mips_opts.isa)) + as_warn (_("-mfp64 used with a 32-bit ABI")); + break; + case 1: + if (ABI_NEEDS_64BIT_REGS (mips_abi)) + as_warn (_("-mfp32 used with a 64-bit ABI")); + break; + } /* End of GCC-shared inference code. */ @@ -11508,19 +11885,51 @@ mips_after_parse_args (void) if (mips_opts.mips16 == -1) mips_opts.mips16 = (CPU_HAS_MIPS16 (file_mips_arch)) ? 1 : 0; if (mips_opts.ase_mips3d == -1) - mips_opts.ase_mips3d = (CPU_HAS_MIPS3D (file_mips_arch)) ? 1 : 0; + mips_opts.ase_mips3d = ((arch_info->flags & MIPS_CPU_ASE_MIPS3D) + && file_mips_fp32 == 0) ? 1 : 0; + if (mips_opts.ase_mips3d && file_mips_fp32 == 1) + as_bad (_("-mfp32 used with -mips3d")); + if (mips_opts.ase_mdmx == -1) - mips_opts.ase_mdmx = (CPU_HAS_MDMX (file_mips_arch)) ? 1 : 0; + mips_opts.ase_mdmx = ((arch_info->flags & MIPS_CPU_ASE_MDMX) + && file_mips_fp32 == 0) ? 1 : 0; + if (mips_opts.ase_mdmx && file_mips_fp32 == 1) + as_bad (_("-mfp32 used with -mdmx")); + + if (mips_opts.ase_smartmips == -1) + mips_opts.ase_smartmips = (arch_info->flags & MIPS_CPU_ASE_SMARTMIPS) ? 1 : 0; + if (mips_opts.ase_smartmips && !ISA_SUPPORTS_SMARTMIPS) + as_warn ("%s ISA does not support SmartMIPS", + mips_cpu_info_from_isa (mips_opts.isa)->name); + if (mips_opts.ase_dsp == -1) - mips_opts.ase_dsp = (CPU_HAS_DSP (file_mips_arch)) ? 1 : 0; + mips_opts.ase_dsp = (arch_info->flags & MIPS_CPU_ASE_DSP) ? 1 : 0; + if (mips_opts.ase_dsp && !ISA_SUPPORTS_DSP_ASE) + as_warn ("%s ISA does not support DSP ASE", + mips_cpu_info_from_isa (mips_opts.isa)->name); + + if (mips_opts.ase_dspr2 == -1) + { + mips_opts.ase_dspr2 = (arch_info->flags & MIPS_CPU_ASE_DSPR2) ? 1 : 0; + mips_opts.ase_dsp = (arch_info->flags & MIPS_CPU_ASE_DSP) ? 1 : 0; + } + if (mips_opts.ase_dspr2 && !ISA_SUPPORTS_DSPR2_ASE) + as_warn ("%s ISA does not support DSP R2 ASE", + mips_cpu_info_from_isa (mips_opts.isa)->name); + if (mips_opts.ase_mt == -1) - mips_opts.ase_mt = (CPU_HAS_MT (file_mips_arch)) ? 1 : 0; + mips_opts.ase_mt = (arch_info->flags & MIPS_CPU_ASE_MT) ? 1 : 0; + if (mips_opts.ase_mt && !ISA_SUPPORTS_MT_ASE) + as_warn ("%s ISA does not support MT ASE", + mips_cpu_info_from_isa (mips_opts.isa)->name); file_mips_isa = mips_opts.isa; file_ase_mips16 = mips_opts.mips16; file_ase_mips3d = mips_opts.ase_mips3d; file_ase_mdmx = mips_opts.ase_mdmx; + file_ase_smartmips = mips_opts.ase_smartmips; file_ase_dsp = mips_opts.ase_dsp; + file_ase_dspr2 = mips_opts.ase_dspr2; file_ase_mt = mips_opts.ase_mt; mips_opts.gp32 = file_mips_gp32; mips_opts.fp32 = file_mips_fp32; @@ -11555,6 +11964,10 @@ md_pcrel_from (fixS *fixP) /* Return the address of the delay slot. */ return addr + 4; default: + /* We have no relocation type for PC relative MIPS16 instructions. */ + if (fixP->fx_addsy && S_GET_SEGMENT (fixP->fx_addsy) != now_seg) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("PC relative MIPS16 instruction references a different section")); return addr; } } @@ -11737,11 +12150,12 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) || fixP->fx_r_type == BFD_RELOC_CTOR || fixP->fx_r_type == BFD_RELOC_MIPS_SUB || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT - || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY); + || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY + || fixP->fx_r_type == BFD_RELOC_MIPS_TLS_DTPREL64); buf = (bfd_byte *) (fixP->fx_frag->fr_literal + fixP->fx_where); - assert (! fixP->fx_pcrel || fixP->fx_r_type == BFD_RELOC_16_PCREL_S2); + assert (!fixP->fx_pcrel || fixP->fx_r_type == BFD_RELOC_16_PCREL_S2); /* Don't treat parts of a composite relocation as done. There are two reasons for this: @@ -11753,13 +12167,15 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) constants. The easiest way of dealing with the pathological exceptions is to generate a relocation against STN_UNDEF and leave everything up to the linker. */ - if (fixP->fx_addsy == NULL && ! fixP->fx_pcrel && fixP->fx_tcbit == 0) + if (fixP->fx_addsy == NULL && !fixP->fx_pcrel && fixP->fx_tcbit == 0) fixP->fx_done = 1; switch (fixP->fx_r_type) { case BFD_RELOC_MIPS_TLS_GD: case BFD_RELOC_MIPS_TLS_LDM: + case BFD_RELOC_MIPS_TLS_DTPREL32: + case BFD_RELOC_MIPS_TLS_DTPREL64: case BFD_RELOC_MIPS_TLS_DTPREL_HI16: case BFD_RELOC_MIPS_TLS_DTPREL_LO16: case BFD_RELOC_MIPS_TLS_GOTTPREL: @@ -11798,14 +12214,8 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) case BFD_RELOC_MIPS16_GPREL: case BFD_RELOC_MIPS16_HI16: case BFD_RELOC_MIPS16_HI16_S: - /* Nothing needed to do. The value comes from the reloc entry */ - break; - case BFD_RELOC_MIPS16_JMP: - /* We currently always generate a reloc against a symbol, which - means that we don't want an addend even if the symbol is - defined. */ - *valP = 0; + /* Nothing needed to do. The value comes from the reloc entry. */ break; case BFD_RELOC_64: @@ -11833,18 +12243,12 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) case BFD_RELOC_RVA: case BFD_RELOC_32: - /* If we are deleting this reloc entry, we must fill in the - value now. This can happen if we have a .word which is not - resolved when it appears but is later defined. */ - if (fixP->fx_done) - md_number_to_chars ((char *) buf, *valP, 4); - break; - case BFD_RELOC_16: /* If we are deleting this reloc entry, we must fill in the - value now. */ + value now. This can happen if we have a .word which is not + resolved when it appears but is later defined. */ if (fixP->fx_done) - md_number_to_chars ((char *) buf, *valP, 2); + md_number_to_chars ((char *) buf, *valP, fixP->fx_size); break; case BFD_RELOC_LO16: @@ -11869,15 +12273,13 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) as_bad_where (fixP->fx_file, fixP->fx_line, _("Branch to misaligned address (%lx)"), (long) *valP); - /* - * We need to save the bits in the instruction since fixup_segment() - * might be deleting the relocation entry (i.e., a branch within - * the current segment). - */ + /* We need to save the bits in the instruction since fixup_segment() + might be deleting the relocation entry (i.e., a branch within + the current segment). */ if (! fixP->fx_done) break; - /* update old instruction data */ + /* Update old instruction data. */ if (target_big_endian) insn = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; else @@ -11977,21 +12379,17 @@ mips_align (int to, int fill, symbolS *label) static void s_align (int x ATTRIBUTE_UNUSED) { - register int temp; - register long temp_fill; + int temp; + long temp_fill; long max_alignment = 15; - /* - - o Note that the assembler pulls down any immediately preceding label + /* o Note that the assembler pulls down any immediately preceding label to the aligned address. - o It's not documented but auto alignment is reinstated by + o It's not documented but auto alignment is reinstated by a .align pseudo instruction. - o Note also that after auto alignment is turned off the mips assembler + o Note also that after auto alignment is turned off the mips assembler issues an error on attempt to assemble an improperly aligned data item. - We don't. - - */ + We don't. */ temp = get_absolute_expression (); if (temp > max_alignment) @@ -12010,9 +12408,11 @@ s_align (int x ATTRIBUTE_UNUSED) temp_fill = 0; if (temp) { + segment_info_type *si = seg_info (now_seg); + struct insn_label_list *l = si->label_list; + /* Auto alignment should be switched on by next section change. */ auto_align = 1; - mips_align (temp, (int) temp_fill, - insn_labels != NULL ? insn_labels->label : NULL); + mips_align (temp, (int) temp_fill, l != NULL ? l->label : NULL); } else { @@ -12034,7 +12434,8 @@ s_change_sec (int sec) as it would not be appropriate to use it in the section changing functions in read.c, since obj-elf.c intercepts those. FIXME: This should be cleaner, somehow. */ - obj_elf_section_change_hook (); + if (IS_ELF) + obj_elf_section_change_hook (); #endif mips_emit_delays (); @@ -12054,7 +12455,7 @@ s_change_sec (int sec) case 'r': seg = subseg_new (RDATA_SECTION_NAME, (subsegT) get_absolute_expression ()); - if (OUTPUT_FLAVOR == bfd_target_elf_flavour) + if (IS_ELF) { bfd_set_section_flags (stdoutput, seg, (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_RELOC @@ -12067,7 +12468,7 @@ s_change_sec (int sec) case 's': seg = subseg_new (".sdata", (subsegT) get_absolute_expression ()); - if (OUTPUT_FLAVOR == bfd_target_elf_flavour) + if (IS_ELF) { bfd_set_section_flags (stdoutput, seg, SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA); @@ -12093,7 +12494,7 @@ s_change_section (int ignore ATTRIBUTE_UNUSED) int section_entry_size; int section_alignment; - if (OUTPUT_FLAVOR != bfd_target_elf_flavour) + if (!IS_ELF) return; section_name = input_line_pointer; @@ -12138,7 +12539,7 @@ s_change_section (int ignore ATTRIBUTE_UNUSED) There's nothing really harmful in this, since bfd will correct SHT_PROGBITS to SHT_MIPS_DWARF before writing out the file. But it - means that, for backwards compatibiltiy, the special_section entries + means that, for backwards compatibility, the special_section entries for dwarf sections must use SHT_PROGBITS rather than SHT_MIPS_DWARF. Even so, we shouldn't force users of the MIPS .section syntax to @@ -12165,9 +12566,11 @@ mips_enable_auto_align (void) static void s_cons (int log_size) { + segment_info_type *si = seg_info (now_seg); + struct insn_label_list *l = si->label_list; symbolS *label; - label = insn_labels != NULL ? insn_labels->label : NULL; + label = l != NULL ? l->label : NULL; mips_emit_delays (); if (log_size > 0 && auto_align) mips_align (log_size, 0, label); @@ -12178,9 +12581,11 @@ s_cons (int log_size) static void s_float_cons (int type) { + segment_info_type *si = seg_info (now_seg); + struct insn_label_list *l = si->label_list; symbolS *label; - label = insn_labels != NULL ? insn_labels->label : NULL; + label = l != NULL ? l->label : NULL; mips_emit_delays (); @@ -12367,12 +12772,43 @@ s_mipsset (int x ATTRIBUTE_UNUSED) { mips_opts.nobopt = 1; } + else if (strcmp (name, "gp=default") == 0) + mips_opts.gp32 = file_mips_gp32; + else if (strcmp (name, "gp=32") == 0) + mips_opts.gp32 = 1; + else if (strcmp (name, "gp=64") == 0) + { + if (!ISA_HAS_64BIT_REGS (mips_opts.isa)) + as_warn ("%s isa does not support 64-bit registers", + mips_cpu_info_from_isa (mips_opts.isa)->name); + mips_opts.gp32 = 0; + } + else if (strcmp (name, "fp=default") == 0) + mips_opts.fp32 = file_mips_fp32; + else if (strcmp (name, "fp=32") == 0) + mips_opts.fp32 = 1; + else if (strcmp (name, "fp=64") == 0) + { + if (!ISA_HAS_64BIT_FPRS (mips_opts.isa)) + as_warn ("%s isa does not support 64-bit floating point registers", + mips_cpu_info_from_isa (mips_opts.isa)->name); + mips_opts.fp32 = 0; + } else if (strcmp (name, "mips16") == 0 || strcmp (name, "MIPS-16") == 0) mips_opts.mips16 = 1; else if (strcmp (name, "nomips16") == 0 || strcmp (name, "noMIPS-16") == 0) mips_opts.mips16 = 0; + else if (strcmp (name, "smartmips") == 0) + { + if (!ISA_SUPPORTS_SMARTMIPS) + as_warn ("%s ISA does not support SmartMIPS ASE", + mips_cpu_info_from_isa (mips_opts.isa)->name); + mips_opts.ase_smartmips = 1; + } + else if (strcmp (name, "nosmartmips") == 0) + mips_opts.ase_smartmips = 0; else if (strcmp (name, "mips3d") == 0) mips_opts.ase_mips3d = 1; else if (strcmp (name, "nomips3d") == 0) @@ -12382,11 +12818,38 @@ s_mipsset (int x ATTRIBUTE_UNUSED) else if (strcmp (name, "nomdmx") == 0) mips_opts.ase_mdmx = 0; else if (strcmp (name, "dsp") == 0) - mips_opts.ase_dsp = 1; + { + if (!ISA_SUPPORTS_DSP_ASE) + as_warn ("%s ISA does not support DSP ASE", + mips_cpu_info_from_isa (mips_opts.isa)->name); + mips_opts.ase_dsp = 1; + mips_opts.ase_dspr2 = 0; + } else if (strcmp (name, "nodsp") == 0) - mips_opts.ase_dsp = 0; + { + mips_opts.ase_dsp = 0; + mips_opts.ase_dspr2 = 0; + } + else if (strcmp (name, "dspr2") == 0) + { + if (!ISA_SUPPORTS_DSPR2_ASE) + as_warn ("%s ISA does not support DSP R2 ASE", + mips_cpu_info_from_isa (mips_opts.isa)->name); + mips_opts.ase_dspr2 = 1; + mips_opts.ase_dsp = 1; + } + else if (strcmp (name, "nodspr2") == 0) + { + mips_opts.ase_dspr2 = 0; + mips_opts.ase_dsp = 0; + } else if (strcmp (name, "mt") == 0) - mips_opts.ase_mt = 1; + { + if (!ISA_SUPPORTS_MT_ASE) + as_warn ("%s ISA does not support MT ASE", + mips_cpu_info_from_isa (mips_opts.isa)->name); + mips_opts.ase_mt = 1; + } else if (strcmp (name, "nomt") == 0) mips_opts.ase_mt = 0; else if (strncmp (name, "mips", 4) == 0 || strncmp (name, "arch=", 5) == 0) @@ -12497,6 +12960,14 @@ s_mipsset (int x ATTRIBUTE_UNUSED) mips_opts.sym32 = TRUE; else if (strcmp (name, "nosym32") == 0) mips_opts.sym32 = FALSE; + else if (strchr (name, ',')) + { + /* Generic ".set" directive; use the generic handler. */ + *input_line_pointer = ch; + input_line_pointer = name; + s_set (0); + return; + } else { as_warn (_("Tried to set unrecognized symbol: %s\n"), name); @@ -12604,8 +13075,7 @@ s_cpload (int ignore ATTRIBUTE_UNUSED) The -mno-shared option replaces the last three instructions with lui $gp,%hi(_gp) - addiu $gp,$gp,%lo(_gp) - */ + addiu $gp,$gp,%lo(_gp) */ static void s_cpsetup (int ignore ATTRIBUTE_UNUSED) @@ -12707,7 +13177,7 @@ static void s_cplocal (int ignore ATTRIBUTE_UNUSED) { /* If we are not generating SVR4 PIC code, or if this is not NewABI code, - .cplocal is ignored. */ + .cplocal is ignored. */ if (mips_pic != SVR4_PIC || ! HAVE_NEWABI) { s_ignore (0); @@ -12756,8 +13226,8 @@ s_cprestore (int ignore ATTRIBUTE_UNUSED) ld $gp, offset($sp) If a register $reg2 was given there, it results in: - daddu $gp, $reg2, $0 - */ + daddu $gp, $reg2, $0 */ + static void s_cpreturn (int ignore ATTRIBUTE_UNUSED) { @@ -12789,6 +13259,52 @@ s_cpreturn (int ignore ATTRIBUTE_UNUSED) demand_empty_rest_of_line (); } +/* Handle the .dtprelword and .dtpreldword pseudo-ops. They generate + a 32-bit or 64-bit DTP-relative relocation (BYTES says which) for + use in DWARF debug information. */ + +static void +s_dtprel_internal (size_t bytes) +{ + expressionS ex; + char *p; + + expression (&ex); + + if (ex.X_op != O_symbol) + { + as_bad (_("Unsupported use of %s"), (bytes == 8 + ? ".dtpreldword" + : ".dtprelword")); + ignore_rest_of_line (); + } + + p = frag_more (bytes); + md_number_to_chars (p, 0, bytes); + fix_new_exp (frag_now, p - frag_now->fr_literal, bytes, &ex, FALSE, + (bytes == 8 + ? BFD_RELOC_MIPS_TLS_DTPREL64 + : BFD_RELOC_MIPS_TLS_DTPREL32)); + + demand_empty_rest_of_line (); +} + +/* Handle .dtprelword. */ + +static void +s_dtprelword (int ignore ATTRIBUTE_UNUSED) +{ + s_dtprel_internal (4); +} + +/* Handle .dtpreldword. */ + +static void +s_dtpreldword (int ignore ATTRIBUTE_UNUSED) +{ + s_dtprel_internal (8); +} + /* Handle the .gpvalue pseudo-op. This is used when generating NewABI PIC code. It sets the offset to use in gp_rel relocations. */ @@ -12814,6 +13330,8 @@ s_gpvalue (int ignore ATTRIBUTE_UNUSED) static void s_gpword (int ignore ATTRIBUTE_UNUSED) { + segment_info_type *si; + struct insn_label_list *l; symbolS *label; expressionS ex; char *p; @@ -12825,7 +13343,9 @@ s_gpword (int ignore ATTRIBUTE_UNUSED) return; } - label = insn_labels != NULL ? insn_labels->label : NULL; + si = seg_info (now_seg); + l = si->label_list; + label = l != NULL ? l->label : NULL; mips_emit_delays (); if (auto_align) mips_align (2, 0, label); @@ -12850,6 +13370,8 @@ s_gpword (int ignore ATTRIBUTE_UNUSED) static void s_gpdword (int ignore ATTRIBUTE_UNUSED) { + segment_info_type *si; + struct insn_label_list *l; symbolS *label; expressionS ex; char *p; @@ -12861,7 +13383,9 @@ s_gpdword (int ignore ATTRIBUTE_UNUSED) return; } - label = insn_labels != NULL ? insn_labels->label : NULL; + si = seg_info (now_seg); + l = si->label_list; + label = l != NULL ? l->label : NULL; mips_emit_delays (); if (auto_align) mips_align (3, 0, label); @@ -12943,8 +13467,7 @@ s_mips_stab (int type) s_stab (type); } -/* Handle the .weakext pseudo-op as defined in Kane and Heinrich. - */ +/* Handle the .weakext pseudo-op as defined in Kane and Heinrich. */ static void s_mips_weakext (int ignore ATTRIBUTE_UNUSED) @@ -12998,73 +13521,11 @@ s_mips_weakext (int ignore ATTRIBUTE_UNUSED) int tc_get_register (int frame) { - int reg; + unsigned int reg; SKIP_WHITESPACE (); - if (*input_line_pointer++ != '$') - { - as_warn (_("expected `$'")); - reg = ZERO; - } - else if (ISDIGIT (*input_line_pointer)) - { - reg = get_absolute_expression (); - if (reg < 0 || reg >= 32) - { - as_warn (_("Bad register number")); - reg = ZERO; - } - } - else - { - if (strncmp (input_line_pointer, "ra", 2) == 0) - { - reg = RA; - input_line_pointer += 2; - } - else if (strncmp (input_line_pointer, "fp", 2) == 0) - { - reg = FP; - input_line_pointer += 2; - } - else if (strncmp (input_line_pointer, "sp", 2) == 0) - { - reg = SP; - input_line_pointer += 2; - } - else if (strncmp (input_line_pointer, "gp", 2) == 0) - { - reg = GP; - input_line_pointer += 2; - } - else if (strncmp (input_line_pointer, "at", 2) == 0) - { - reg = AT; - input_line_pointer += 2; - } - else if (strncmp (input_line_pointer, "kt0", 3) == 0) - { - reg = KT0; - input_line_pointer += 3; - } - else if (strncmp (input_line_pointer, "kt1", 3) == 0) - { - reg = KT1; - input_line_pointer += 3; - } - else if (strncmp (input_line_pointer, "zero", 4) == 0) - { - reg = ZERO; - input_line_pointer += 4; - } - else - { - as_warn (_("Unrecognized register name")); - reg = ZERO; - while (ISALNUM(*input_line_pointer)) - input_line_pointer++; - } - } + if (! reg_lookup (&input_line_pointer, RWARN | RTYPE_NUM | RTYPE_GP, ®)) + reg = 0; if (frame) { mips_frame_reg = reg != 0 ? reg : SP; @@ -13079,16 +13540,17 @@ md_section_align (asection *seg, valueT addr) { int align = bfd_get_section_alignment (stdoutput, seg); -#ifdef OBJ_ELF - /* We don't need to align ELF sections to the full alignment. - However, Irix 5 may prefer that we align them at least to a 16 - byte boundary. We don't bother to align the sections if we are - targeted for an embedded system. */ - if (strcmp (TARGET_OS, "elf") == 0) - return addr; - if (align > 4) - align = 4; -#endif + if (IS_ELF) + { + /* We don't need to align ELF sections to the full alignment. + However, Irix 5 may prefer that we align them at least to a 16 + byte boundary. We don't bother to align the sections if we + are targeted for an embedded system. */ + if (strcmp (TARGET_OS, "elf") == 0) + return addr; + if (align > 4) + align = 4; + } return ((addr + (1 << align) - 1) & (-1 << align)); } @@ -13154,6 +13616,8 @@ nopic_need_relax (symbolS *sym, int before_relaxing) change = (strcmp (segname, ".sdata") != 0 && strcmp (segname, ".sbss") != 0 && strncmp (segname, ".sdata.", 7) != 0 + && strncmp (segname, ".sbss.", 6) != 0 + && strncmp (segname, ".gnu.linkonce.sb.", 17) != 0 && strncmp (segname, ".gnu.linkonce.s.", 16) != 0); } return change; @@ -13170,48 +13634,32 @@ static bfd_boolean pic_need_relax (symbolS *sym, asection *segtype) { asection *symsec; - bfd_boolean linkonce; /* Handle the case of a symbol equated to another symbol. */ while (symbol_equated_reloc_p (sym)) { symbolS *n; - /* It's possible to get a loop here in a badly written - program. */ + /* It's possible to get a loop here in a badly written program. */ n = symbol_get_value_expression (sym)->X_add_symbol; if (n == sym) break; sym = n; } - symsec = S_GET_SEGMENT (sym); - - /* duplicate the test for LINK_ONCE sections as in adjust_reloc_syms */ - linkonce = FALSE; - if (symsec != segtype && ! S_IS_LOCAL (sym)) - { - if ((bfd_get_section_flags (stdoutput, symsec) & SEC_LINK_ONCE) - != 0) - linkonce = TRUE; + if (symbol_section_p (sym)) + return TRUE; - /* The GNU toolchain uses an extension for ELF: a section - beginning with the magic string .gnu.linkonce is a linkonce - section. */ - if (strncmp (segment_name (symsec), ".gnu.linkonce", - sizeof ".gnu.linkonce" - 1) == 0) - linkonce = TRUE; - } + symsec = S_GET_SEGMENT (sym); /* This must duplicate the test in adjust_reloc_syms. */ return (symsec != &bfd_und_section && symsec != &bfd_abs_section - && ! bfd_is_com_section (symsec) - && !linkonce + && !bfd_is_com_section (symsec) + && !s_is_linkonce (sym, segtype) #ifdef OBJ_ELF /* A global or weak symbol is treated as external. */ - && (OUTPUT_FLAVOR != bfd_target_elf_flavour - || (! S_IS_WEAK (sym) && ! S_IS_EXTERNAL (sym))) + && (!IS_ELF || (! S_IS_WEAK (sym) && ! S_IS_EXTERNAL (sym))) #endif ); } @@ -13224,7 +13672,7 @@ static int mips16_extended_frag (fragS *fragp, asection *sec, long stretch) { int type; - register const struct mips16_immed_operand *op; + const struct mips16_immed_operand *op; offsetT val; int mintiny, maxtiny; segT symsec; @@ -13527,11 +13975,6 @@ md_estimate_size_before_relax (fragS *fragp, asection *segtype) int mips_fix_adjustable (fixS *fixp) { - /* Don't adjust MIPS16 jump relocations, so we don't have to worry - about the format of the offset in the .o file. */ - if (fixp->fx_r_type == BFD_RELOC_MIPS16_JMP) - return 0; - if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) return 0; @@ -13561,11 +14004,50 @@ mips_fix_adjustable (fixS *fixp) return 0; #ifdef OBJ_ELF - /* Don't adjust relocations against mips16 symbols, so that the linker - can find them if it needs to set up a stub. */ - if (OUTPUT_FLAVOR == bfd_target_elf_flavour - && S_GET_OTHER (fixp->fx_addsy) == STO_MIPS16 - && fixp->fx_subsy == NULL) + /* R_MIPS16_26 relocations against non-MIPS16 functions might resolve + to a floating-point stub. The same is true for non-R_MIPS16_26 + relocations against MIPS16 functions; in this case, the stub becomes + the function's canonical address. + + Floating-point stubs are stored in unique .mips16.call.* or + .mips16.fn.* sections. If a stub T for function F is in section S, + the first relocation in section S must be against F; this is how the + linker determines the target function. All relocations that might + resolve to T must also be against F. We therefore have the following + restrictions, which are given in an intentionally-redundant way: + + 1. We cannot reduce R_MIPS16_26 relocations against non-MIPS16 + symbols. + + 2. We cannot reduce a stub's relocations against non-MIPS16 symbols + if that stub might be used. + + 3. We cannot reduce non-R_MIPS16_26 relocations against MIPS16 + symbols. + + 4. We cannot reduce a stub's relocations against MIPS16 symbols if + that stub might be used. + + There is a further restriction: + + 5. We cannot reduce R_MIPS16_26 relocations against MIPS16 symbols + on targets with in-place addends; the relocation field cannot + encode the low bit. + + For simplicity, we deal with (3)-(5) by not reducing _any_ relocation + against a MIPS16 symbol. + + We deal with (1)-(2) by saying that, if there's a R_MIPS16_26 + relocation against some symbol R, no relocation against R may be + reduced. (Note that this deals with (2) as well as (1) because + relocations against global symbols will never be reduced on ELF + targets.) This approach is a little simpler than trying to detect + stub sections, and gives the "all or nothing" per-symbol consistency + that we have for MIPS16 symbols. */ + if (IS_ELF + && fixp->fx_subsy == NULL + && (S_GET_OTHER (fixp->fx_addsy) == STO_MIPS16 + || *symbol_get_tc (fixp->fx_addsy))) return 0; #endif @@ -13595,7 +14077,7 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp) /* At this point, fx_addnumber is "symbol offset - pcrel address". Relocations want only the symbol offset. */ reloc->addend = fixp->fx_addnumber + reloc->address; - if (OUTPUT_FLAVOR != bfd_target_elf_flavour) + if (!IS_ELF) { /* A gruesome hack which is a result of the gruesome gas reloc handling. What's worse, for COFF (as opposed to @@ -13694,7 +14176,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp) exp.X_add_number = fragp->fr_offset; fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal, - 4, &exp, 1, BFD_RELOC_16_PCREL_S2); + 4, &exp, TRUE, BFD_RELOC_16_PCREL_S2); fixp->fx_file = fragp->fr_file; fixp->fx_line = fragp->fr_line; @@ -13726,14 +14208,14 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp) case 0: /* bltz 0x04000000 bgez 0x04010000 - bltzal 0x04100000 bgezal 0x04110000 */ + bltzal 0x04100000 bgezal 0x04110000 */ assert ((insn & 0xfc0e0000) == 0x04000000); insn ^= 0x00010000; break; case 1: /* beq 0x10000000 bne 0x14000000 - blez 0x18000000 bgtz 0x1c000000 */ + blez 0x18000000 bgtz 0x1c000000 */ insn ^= 0x04000000; break; @@ -13747,8 +14229,8 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp) /* Clear the and-link bit. */ assert ((insn & 0xfc1c0000) == 0x04100000); - /* bltzal 0x04100000 bgezal 0x04110000 - bltzall 0x04120000 bgezall 0x04130000 */ + /* bltzal 0x04100000 bgezal 0x04110000 + bltzall 0x04120000 bgezall 0x04130000 */ insn &= ~0x00100000; } @@ -13773,7 +14255,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp) md_number_to_chars ((char *) buf, insn, 4); buf += 4; - /* Nop */ + /* nop */ md_number_to_chars ((char *) buf, 0, 4); buf += 4; @@ -13811,7 +14293,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp) exp.X_add_number = fragp->fr_offset; fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal, - 4, &exp, 0, BFD_RELOC_MIPS_JMP); + 4, &exp, FALSE, BFD_RELOC_MIPS_JMP); fixp->fx_file = fragp->fr_file; fixp->fx_line = fragp->fr_line; @@ -13833,7 +14315,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp) } fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal, - 4, &exp, 0, BFD_RELOC_MIPS_GOT16); + 4, &exp, FALSE, BFD_RELOC_MIPS_GOT16); fixp->fx_file = fragp->fr_file; fixp->fx_line = fragp->fr_line; @@ -13851,7 +14333,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp) insn = HAVE_64BIT_ADDRESSES ? 0x64210000 : 0x24210000; fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal, - 4, &exp, 0, BFD_RELOC_LO16); + 4, &exp, FALSE, BFD_RELOC_LO16); fixp->fx_file = fragp->fr_file; fixp->fx_line = fragp->fr_line; @@ -13880,7 +14362,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp) if (RELAX_MIPS16_P (fragp->fr_subtype)) { int type; - register const struct mips16_immed_operand *op; + const struct mips16_immed_operand *op; bfd_boolean small, ext; offsetT val; bfd_byte *buf; @@ -14034,7 +14516,7 @@ mips_frob_file_after_relocs (void) asymbol **syms; unsigned int count, i; - if (OUTPUT_FLAVOR != bfd_target_elf_flavour) + if (!IS_ELF) return; syms = bfd_get_outsymbols (stdoutput); @@ -14062,6 +14544,7 @@ mips_frob_file_after_relocs (void) void mips_define_label (symbolS *sym) { + segment_info_type *si = seg_info (now_seg); struct insn_label_list *l; if (free_insn_labels == NULL) @@ -14073,8 +14556,8 @@ mips_define_label (symbolS *sym) } l->label = sym; - l->next = insn_labels; - insn_labels = l; + l->next = si->label_list; + si->label_list = l; #ifdef OBJ_ELF dwarf2_emit_label (sym); @@ -14136,6 +14619,7 @@ mips_elf_final_processing (void) /* Set MIPS ELF flags for ASEs. */ /* We may need to define a new flag for DSP ASE, and set this flag when file_ase_dsp is true. */ + /* Same for DSP R2. */ /* We may need to define a new flag for MT ASE, and set this flag when file_ase_mt is true. */ if (file_ase_mips16) @@ -14166,6 +14650,12 @@ mips_elf_final_processing (void) if (mips_32bitmode) elf_elfheader (stdoutput)->e_flags |= EF_MIPS_32BITMODE; + +#if 0 /* XXX FIXME */ + /* 32 bit code with 64 bit FP registers. */ + if (!file_mips_fp32 && ABI_NEEDS_32BIT_REGS (mips_abi)) + elf_elfheader (stdoutput)->e_flags |= ???; +#endif } #endif /* OBJ_ELF || OBJ_MAYBE_ELF */ @@ -14226,7 +14716,7 @@ md_obj_begin (void) static void md_obj_end (void) { - /* check for premature end, nesting errors, etc */ + /* Check for premature end, nesting errors, etc. */ if (cur_proc_ptr) as_warn (_("missing .end at end of assembly")); } @@ -14383,8 +14873,7 @@ s_mips_end (int x ATTRIBUTE_UNUSED) } /* Generate a .pdr section. */ - if (OUTPUT_FLAVOR == bfd_target_elf_flavour && ! ECOFF_DEBUGGING - && mips_flag_pdr) + if (IS_ELF && !ECOFF_DEBUGGING && mips_flag_pdr) { segT saved_seg = now_seg; subsegT saved_subseg = now_subseg; @@ -14478,7 +14967,7 @@ static void s_mips_frame (int ignore ATTRIBUTE_UNUSED) { #ifdef OBJ_ELF - if (OUTPUT_FLAVOR == bfd_target_elf_flavour && ! ECOFF_DEBUGGING) + if (IS_ELF && !ECOFF_DEBUGGING) { long val; @@ -14521,7 +15010,7 @@ static void s_mips_mask (int reg_type) { #ifdef OBJ_ELF - if (OUTPUT_FLAVOR == bfd_target_elf_flavour && ! ECOFF_DEBUGGING) + if (IS_ELF && !ECOFF_DEBUGGING) { long mask, off; @@ -14568,72 +15057,102 @@ s_mips_mask (int reg_type) static const struct mips_cpu_info mips_cpu_info_table[] = { /* Entries for generic ISAs */ - { "mips1", 1, ISA_MIPS1, CPU_R3000 }, - { "mips2", 1, ISA_MIPS2, CPU_R6000 }, - { "mips3", 1, ISA_MIPS3, CPU_R4000 }, - { "mips4", 1, ISA_MIPS4, CPU_R8000 }, - { "mips5", 1, ISA_MIPS5, CPU_MIPS5 }, - { "mips32", 1, ISA_MIPS32, CPU_MIPS32 }, - { "mips32r2", 1, ISA_MIPS32R2, CPU_MIPS32R2 }, - { "mips64", 1, ISA_MIPS64, CPU_MIPS64 }, - { "mips64r2", 1, ISA_MIPS64R2, CPU_MIPS64R2 }, + { "mips1", MIPS_CPU_IS_ISA, ISA_MIPS1, CPU_R3000 }, + { "mips2", MIPS_CPU_IS_ISA, ISA_MIPS2, CPU_R6000 }, + { "mips3", MIPS_CPU_IS_ISA, ISA_MIPS3, CPU_R4000 }, + { "mips4", MIPS_CPU_IS_ISA, ISA_MIPS4, CPU_R8000 }, + { "mips5", MIPS_CPU_IS_ISA, ISA_MIPS5, CPU_MIPS5 }, + { "mips32", MIPS_CPU_IS_ISA, ISA_MIPS32, CPU_MIPS32 }, + { "mips32r2", MIPS_CPU_IS_ISA, ISA_MIPS32R2, CPU_MIPS32R2 }, + { "mips64", MIPS_CPU_IS_ISA, ISA_MIPS64, CPU_MIPS64 }, + { "mips64r2", MIPS_CPU_IS_ISA, ISA_MIPS64R2, CPU_MIPS64R2 }, /* MIPS I */ - { "r3000", 0, ISA_MIPS1, CPU_R3000 }, - { "r2000", 0, ISA_MIPS1, CPU_R3000 }, - { "r3900", 0, ISA_MIPS1, CPU_R3900 }, + { "r3000", 0, ISA_MIPS1, CPU_R3000 }, + { "r2000", 0, ISA_MIPS1, CPU_R3000 }, + { "r3900", 0, ISA_MIPS1, CPU_R3900 }, /* MIPS II */ - { "r6000", 0, ISA_MIPS2, CPU_R6000 }, + { "r6000", 0, ISA_MIPS2, CPU_R6000 }, /* MIPS III */ - { "r4000", 0, ISA_MIPS3, CPU_R4000 }, - { "r4010", 0, ISA_MIPS2, CPU_R4010 }, - { "vr4100", 0, ISA_MIPS3, CPU_VR4100 }, - { "vr4111", 0, ISA_MIPS3, CPU_R4111 }, - { "vr4120", 0, ISA_MIPS3, CPU_VR4120 }, - { "vr4130", 0, ISA_MIPS3, CPU_VR4120 }, - { "vr4181", 0, ISA_MIPS3, CPU_R4111 }, - { "vr4300", 0, ISA_MIPS3, CPU_R4300 }, - { "r4400", 0, ISA_MIPS3, CPU_R4400 }, - { "r4600", 0, ISA_MIPS3, CPU_R4600 }, - { "orion", 0, ISA_MIPS3, CPU_R4600 }, - { "r4650", 0, ISA_MIPS3, CPU_R4650 }, + { "r4000", 0, ISA_MIPS3, CPU_R4000 }, + { "r4010", 0, ISA_MIPS2, CPU_R4010 }, + { "vr4100", 0, ISA_MIPS3, CPU_VR4100 }, + { "vr4111", 0, ISA_MIPS3, CPU_R4111 }, + { "vr4120", 0, ISA_MIPS3, CPU_VR4120 }, + { "vr4130", 0, ISA_MIPS3, CPU_VR4120 }, + { "vr4181", 0, ISA_MIPS3, CPU_R4111 }, + { "vr4300", 0, ISA_MIPS3, CPU_R4300 }, + { "r4400", 0, ISA_MIPS3, CPU_R4400 }, + { "r4600", 0, ISA_MIPS3, CPU_R4600 }, + { "orion", 0, ISA_MIPS3, CPU_R4600 }, + { "r4650", 0, ISA_MIPS3, CPU_R4650 }, /* MIPS IV */ - { "r8000", 0, ISA_MIPS4, CPU_R8000 }, - { "r10000", 0, ISA_MIPS4, CPU_R10000 }, - { "r12000", 0, ISA_MIPS4, CPU_R12000 }, - { "vr5000", 0, ISA_MIPS4, CPU_R5000 }, - { "vr5400", 0, ISA_MIPS4, CPU_VR5400 }, - { "vr5500", 0, ISA_MIPS4, CPU_VR5500 }, - { "rm5200", 0, ISA_MIPS4, CPU_R5000 }, - { "rm5230", 0, ISA_MIPS4, CPU_R5000 }, - { "rm5231", 0, ISA_MIPS4, CPU_R5000 }, - { "rm5261", 0, ISA_MIPS4, CPU_R5000 }, - { "rm5721", 0, ISA_MIPS4, CPU_R5000 }, - { "rm7000", 0, ISA_MIPS4, CPU_RM7000 }, - { "rm9000", 0, ISA_MIPS4, CPU_RM9000 }, + { "r8000", 0, ISA_MIPS4, CPU_R8000 }, + { "r10000", 0, ISA_MIPS4, CPU_R10000 }, + { "r12000", 0, ISA_MIPS4, CPU_R12000 }, + { "vr5000", 0, ISA_MIPS4, CPU_R5000 }, + { "vr5400", 0, ISA_MIPS4, CPU_VR5400 }, + { "vr5500", 0, ISA_MIPS4, CPU_VR5500 }, + { "rm5200", 0, ISA_MIPS4, CPU_R5000 }, + { "rm5230", 0, ISA_MIPS4, CPU_R5000 }, + { "rm5231", 0, ISA_MIPS4, CPU_R5000 }, + { "rm5261", 0, ISA_MIPS4, CPU_R5000 }, + { "rm5721", 0, ISA_MIPS4, CPU_R5000 }, + { "rm7000", 0, ISA_MIPS4, CPU_RM7000 }, + { "rm9000", 0, ISA_MIPS4, CPU_RM9000 }, /* MIPS 32 */ - { "4kc", 0, ISA_MIPS32, CPU_MIPS32 }, - { "4km", 0, ISA_MIPS32, CPU_MIPS32 }, - { "4kp", 0, ISA_MIPS32, CPU_MIPS32 }, - - /* MIPS32 Release 2 */ - { "m4k", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, - { "24k", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, - { "24kc", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, - { "24kf", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, - { "24kx", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, + { "4kc", 0, ISA_MIPS32, CPU_MIPS32 }, + { "4km", 0, ISA_MIPS32, CPU_MIPS32 }, + { "4kp", 0, ISA_MIPS32, CPU_MIPS32 }, + { "4ksc", MIPS_CPU_ASE_SMARTMIPS, ISA_MIPS32, CPU_MIPS32 }, + + /* MIPS 32 Release 2 */ + { "4kec", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, + { "4kem", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, + { "4kep", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, + { "4ksd", MIPS_CPU_ASE_SMARTMIPS, ISA_MIPS32R2, CPU_MIPS32R2 }, + { "m4k", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, + { "m4kp", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, + { "24kc", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, + { "24kf", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, + { "24kx", 0, ISA_MIPS32R2, CPU_MIPS32R2 }, + /* 24KE is a 24K with DSP ASE, other ASEs are optional. */ + { "24kec", MIPS_CPU_ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 }, + { "24kef", MIPS_CPU_ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 }, + { "24kex", MIPS_CPU_ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 }, + /* 34K is a 24K with DSP and MT ASE, other ASEs are optional. */ + { "34kc", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT, + ISA_MIPS32R2, CPU_MIPS32R2 }, + { "34kf", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT, + ISA_MIPS32R2, CPU_MIPS32R2 }, + { "34kx", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_MT, + ISA_MIPS32R2, CPU_MIPS32R2 }, + /* 74K with DSP and DSPR2 ASE, other ASEs are optional. */ + { "74kc", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2, + ISA_MIPS32R2, CPU_MIPS32R2 }, + { "74kf", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2, + ISA_MIPS32R2, CPU_MIPS32R2 }, + { "74kx", MIPS_CPU_ASE_DSP | MIPS_CPU_ASE_DSPR2, + ISA_MIPS32R2, CPU_MIPS32R2 }, /* MIPS 64 */ - { "5kc", 0, ISA_MIPS64, CPU_MIPS64 }, - { "5kf", 0, ISA_MIPS64, CPU_MIPS64 }, - { "20kc", 0, ISA_MIPS64, CPU_MIPS64 }, + { "5kc", 0, ISA_MIPS64, CPU_MIPS64 }, + { "5kf", 0, ISA_MIPS64, CPU_MIPS64 }, + { "20kc", MIPS_CPU_ASE_MIPS3D, ISA_MIPS64, CPU_MIPS64 }, + { "25kf", MIPS_CPU_ASE_MIPS3D, ISA_MIPS64, CPU_MIPS64 }, + + /* MIPS 64 Release 2 */ /* Broadcom SB-1 CPU core */ - { "sb1", 0, ISA_MIPS64, CPU_SB1 }, + { "sb1", MIPS_CPU_ASE_MIPS3D | MIPS_CPU_ASE_MDMX, + ISA_MIPS64, CPU_SB1 }, + /* Broadcom SB-1A CPU core */ + { "sb1a", MIPS_CPU_ASE_MIPS3D | MIPS_CPU_ASE_MDMX, + ISA_MIPS64, CPU_SB1 }, /* Cavium Networks Octeon CPU core */ { "octeon", 0, ISA_MIPS64R2, CPU_OCTEON }, @@ -14751,7 +15270,7 @@ mips_cpu_info_from_isa (int isa) int i; for (i = 0; mips_cpu_info_table[i].name != NULL; i++) - if (mips_cpu_info_table[i].is_isa + if ((mips_cpu_info_table[i].flags & MIPS_CPU_IS_ISA) && isa == mips_cpu_info_table[i].isa) return (&mips_cpu_info_table[i]); @@ -14845,9 +15364,15 @@ MIPS options:\n\ -mips16 generate mips16 instructions\n\ -no-mips16 do not generate mips16 instructions\n")); fprintf (stream, _("\ +-msmartmips generate smartmips instructions\n\ +-mno-smartmips do not generate smartmips instructions\n")); + fprintf (stream, _("\ -mdsp generate DSP instructions\n\ -mno-dsp do not generate DSP instructions\n")); fprintf (stream, _("\ +-mdspr2 generate DSP R2 instructions\n\ +-mno-dspr2 do not generate DSP R2 instructions\n")); + fprintf (stream, _("\ -mmt generate MT instructions\n\ -mno-mt do not generate MT instructions\n")); fprintf (stream, _("\ @@ -14855,7 +15380,6 @@ MIPS options:\n\ -mfix-vr4130 work around VR4130 mflo/mfhi errata\n\ -mgp32 use 32-bit GPRs, regardless of the chosen ISA\n\ -mfp32 use 32-bit FPRs, regardless of the chosen ISA\n\ --mno-shared optimize output for executables\n\ -msym32 assume all symbols have 32-bit values\n\ -O0 remove unneeded NOPs, do not swap branches\n\ -O remove unneeded NOPs and swap branches\n\ @@ -14865,11 +15389,12 @@ MIPS options:\n\ #ifdef OBJ_ELF fprintf (stream, _("\ -KPIC, -call_shared generate SVR4 position independent code\n\ +-mvxworks-pic generate VxWorks position independent code\n\ -non_shared do not generate position independent code\n\ -xgot assume a 32 bit GOT\n\ -mpdr, -mno-pdr enable/disable creation of .pdr sections\n\ -mshared, -mno-shared disable/enable .cpload optimization for\n\ - non-shared code\n\ + position dependent (non shared) code\n\ -mabi=ABI create ABI conformant object file for:\n")); first = 1; @@ -14926,3 +15451,14 @@ mips_cfi_frame_initial_instructions (void) cfi_add_CFA_def_cfa_register (SP); } +int +tc_mips_regname_to_dw2regnum (char *regname) +{ + unsigned int regnum = -1; + unsigned int reg; + + if (reg_lookup (®name, RTYPE_GP | RTYPE_NUM, ®)) + regnum = reg; + + return regnum; +} diff --git a/contrib/binutils/gas/config/tc-mips.h b/contrib/binutils/gas/config/tc-mips.h index 5665d3d..117417c 100644 --- a/contrib/binutils/gas/config/tc-mips.h +++ b/contrib/binutils/gas/config/tc-mips.h @@ -1,6 +1,6 @@ /* tc-mips.h -- header file for tc-mips.c. - Copyright 1993, 1994, 1995, 1996, 1997, 2000, 2001, 2002, 2003, 2004 - Free Software Foundation, Inc. + Copyright 1993, 1994, 1995, 1996, 1997, 2000, 2001, 2002, 2003, 2004, + 2005, 2006 Free Software Foundation, Inc. Contributed by the OSF and Ralph Campbell. Written by Keith Knowles and Ralph Campbell, working independently. Modified for ECOFF support by Ian Lance Taylor of Cygnus Support. @@ -58,6 +58,12 @@ extern void mips_handle_align (struct frag *); #define MAX_MEM_FOR_RS_ALIGN_CODE (1 + 2) +struct insn_label_list; +#define TC_SEGMENT_INFO_TYPE struct insn_label_list * + +/* This field is nonzero if the symbol is the target of a MIPS16 jump. */ +#define TC_SYMFIELD_TYPE int + /* Tell assembler that we have an itbl_mips.h header file to include. */ #define HAVE_ITBL_CPU @@ -149,6 +155,7 @@ extern void mips_emit_delays (void); extern void mips_enable_auto_align (void); #define md_elf_section_change_hook() mips_enable_auto_align() +enum dwarf2_format; extern enum dwarf2_format mips_dwarf2_format (void); #define DWARF2_FORMAT() mips_dwarf2_format () @@ -160,7 +167,10 @@ extern int mips_dwarf2_addr_size (void); #define tc_cfi_frame_initial_instructions mips_cfi_frame_initial_instructions extern void mips_cfi_frame_initial_instructions (void); +#define tc_regname_to_dw2regnum tc_mips_regname_to_dw2regnum +extern int tc_mips_regname_to_dw2regnum (char *regname); + #define DWARF2_DEFAULT_RETURN_COLUMN 31 -#define DWARF2_CIE_DATA_ALIGNMENT -4 +#define DWARF2_CIE_DATA_ALIGNMENT (-4) #endif /* TC_MIPS */ diff --git a/contrib/binutils/gas/config/tc-ppc.c b/contrib/binutils/gas/config/tc-ppc.c index d5bdb9e..a86a3be 100644 --- a/contrib/binutils/gas/config/tc-ppc.c +++ b/contrib/binutils/gas/config/tc-ppc.c @@ -1,6 +1,6 @@ /* tc-ppc.c -- Assemble for the PowerPC or POWER (RS/6000) Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, - 2004, 2005 Free Software Foundation, Inc. + 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Written by Ian Lance Taylor, Cygnus Support. This file is part of GAS, the GNU Assembler. @@ -20,7 +20,6 @@ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -#include <stdio.h> #include "as.h" #include "safe-ctype.h" #include "subsegs.h" @@ -85,67 +84,57 @@ static int set_target_endian = 0; static bfd_boolean reg_names_p = TARGET_REG_NAMES_P; -static bfd_boolean register_name PARAMS ((expressionS *)); -static void ppc_set_cpu PARAMS ((void)); -static unsigned long ppc_insert_operand - PARAMS ((unsigned long insn, const struct powerpc_operand *operand, - offsetT val, char *file, unsigned int line)); -static void ppc_macro PARAMS ((char *str, const struct powerpc_macro *macro)); -static void ppc_byte PARAMS ((int)); +static void ppc_macro (char *, const struct powerpc_macro *); +static void ppc_byte (int); #if defined (OBJ_XCOFF) || defined (OBJ_ELF) -static int ppc_is_toc_sym PARAMS ((symbolS *sym)); -static void ppc_tc PARAMS ((int)); -static void ppc_machine PARAMS ((int)); +static void ppc_tc (int); +static void ppc_machine (int); #endif #ifdef OBJ_XCOFF -static void ppc_comm PARAMS ((int)); -static void ppc_bb PARAMS ((int)); -static void ppc_bc PARAMS ((int)); -static void ppc_bf PARAMS ((int)); -static void ppc_biei PARAMS ((int)); -static void ppc_bs PARAMS ((int)); -static void ppc_eb PARAMS ((int)); -static void ppc_ec PARAMS ((int)); -static void ppc_ef PARAMS ((int)); -static void ppc_es PARAMS ((int)); -static void ppc_csect PARAMS ((int)); -static void ppc_change_csect PARAMS ((symbolS *, offsetT)); -static void ppc_function PARAMS ((int)); -static void ppc_extern PARAMS ((int)); -static void ppc_lglobl PARAMS ((int)); -static void ppc_section PARAMS ((int)); -static void ppc_named_section PARAMS ((int)); -static void ppc_stabx PARAMS ((int)); -static void ppc_rename PARAMS ((int)); -static void ppc_toc PARAMS ((int)); -static void ppc_xcoff_cons PARAMS ((int)); -static void ppc_vbyte PARAMS ((int)); +static void ppc_comm (int); +static void ppc_bb (int); +static void ppc_bc (int); +static void ppc_bf (int); +static void ppc_biei (int); +static void ppc_bs (int); +static void ppc_eb (int); +static void ppc_ec (int); +static void ppc_ef (int); +static void ppc_es (int); +static void ppc_csect (int); +static void ppc_change_csect (symbolS *, offsetT); +static void ppc_function (int); +static void ppc_extern (int); +static void ppc_lglobl (int); +static void ppc_section (int); +static void ppc_named_section (int); +static void ppc_stabx (int); +static void ppc_rename (int); +static void ppc_toc (int); +static void ppc_xcoff_cons (int); +static void ppc_vbyte (int); #endif #ifdef OBJ_ELF -static bfd_reloc_code_real_type ppc_elf_suffix PARAMS ((char **, expressionS *)); -static void ppc_elf_cons PARAMS ((int)); -static void ppc_elf_rdata PARAMS ((int)); -static void ppc_elf_lcomm PARAMS ((int)); -static void ppc_elf_validate_fix PARAMS ((fixS *, segT)); -static void ppc_apuinfo_section_add PARAMS ((unsigned int apu, unsigned int version)); +static void ppc_elf_cons (int); +static void ppc_elf_rdata (int); +static void ppc_elf_lcomm (int); #endif #ifdef TE_PE -static void ppc_set_current_section PARAMS ((segT)); -static void ppc_previous PARAMS ((int)); -static void ppc_pdata PARAMS ((int)); -static void ppc_ydata PARAMS ((int)); -static void ppc_reldata PARAMS ((int)); -static void ppc_rdata PARAMS ((int)); -static void ppc_ualong PARAMS ((int)); -static void ppc_znop PARAMS ((int)); -static void ppc_pe_comm PARAMS ((int)); -static void ppc_pe_section PARAMS ((int)); -static void ppc_pe_function PARAMS ((int)); -static void ppc_pe_tocd PARAMS ((int)); +static void ppc_previous (int); +static void ppc_pdata (int); +static void ppc_ydata (int); +static void ppc_reldata (int); +static void ppc_rdata (int); +static void ppc_ualong (int); +static void ppc_znop (int); +static void ppc_pe_comm (int); +static void ppc_pe_section (int); +static void ppc_pe_function (int); +static void ppc_pe_tocd (int); #endif /* Generic assembler global variables which must be defined by all @@ -183,11 +172,9 @@ const char EXP_CHARS[] = "eE"; as in 0d1.0. */ const char FLT_CHARS[] = "dD"; -/* '+' and '-' can be used as postfix predicate predictors for conditional - branches. So they need to be accepted as symbol characters. - Also, anything that can start an operand needs to be mentioned here, +/* Anything that can start an operand needs to be mentioned here, to stop the input scrubber eating whitespace. */ -const char ppc_symbol_chars[] = "+-%["; +const char ppc_symbol_chars[] = "%["; /* The dwarf2 data alignment, adjusted for 32 or 64 bit. */ int ppc_cie_data_alignment; @@ -567,14 +554,8 @@ static const struct pd_reg pre_defined_registers[] = /* Given NAME, find the register number associated with that name, return the integer value associated with the given name or -1 on failure. */ -static int reg_name_search - PARAMS ((const struct pd_reg *, int, const char * name)); - static int -reg_name_search (regs, regcount, name) - const struct pd_reg *regs; - int regcount; - const char *name; +reg_name_search (const struct pd_reg *regs, int regcount, const char *name) { int middle, low, high; int cmp; @@ -611,8 +592,7 @@ reg_name_search (regs, regcount, name) */ static bfd_boolean -register_name (expressionP) - expressionS *expressionP; +register_name (expressionS *expressionP) { int reg_number; char *name; @@ -679,9 +659,7 @@ static const struct pd_reg cr_names[] = expression. */ int -ppc_parse_name (name, expr) - const char *name; - expressionS *expr; +ppc_parse_name (const char *name, expressionS *expr) { int val; @@ -917,6 +895,18 @@ parse_cpu (const char *arg) | PPC_OPCODE_64 | PPC_OPCODE_POWER4 | PPC_OPCODE_POWER5); } + else if (strcmp (arg, "power6") == 0) + { + ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC + | PPC_OPCODE_64 | PPC_OPCODE_POWER4 + | PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6); + } + else if (strcmp (arg, "cell") == 0) + { + ppc_cpu = (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC + | PPC_OPCODE_64 | PPC_OPCODE_POWER4 + | PPC_OPCODE_CELL); + } /* -mcom means assemble for the common intersection between Power and PowerPC. At present, we just allow the union, rather than the intersection. */ @@ -932,9 +922,7 @@ parse_cpu (const char *arg) } int -md_parse_option (c, arg) - int c; - char *arg; +md_parse_option (int c, char *arg) { switch (c) { @@ -1088,8 +1076,7 @@ md_parse_option (c, arg) } void -md_show_usage (stream) - FILE *stream; +md_show_usage (FILE *stream) { fprintf (stream, _("\ PowerPC options:\n\ @@ -1112,6 +1099,8 @@ PowerPC options:\n\ -mbooke, mbooke32 generate code for 32-bit PowerPC BookE\n\ -mpower4 generate code for Power4 architecture\n\ -mpower5 generate code for Power5 architecture\n\ +-mpower6 generate code for Power6 architecture\n\ +-mcell generate code for Cell Broadband Engine architecture\n\ -mcom generate code Power/PowerPC common instructions\n\ -many generate code for any architecture (PWR/PWRX/PPC)\n")); fprintf (stream, _("\ @@ -1140,7 +1129,7 @@ PowerPC options:\n\ /* Set ppc_cpu if it is not already set. */ static void -ppc_set_cpu () +ppc_set_cpu (void) { const char *default_os = TARGET_OS; const char *default_cpu = TARGET_CPU; @@ -1168,7 +1157,7 @@ ppc_set_cpu () are called well before md_begin, when the output file is opened. */ enum bfd_architecture -ppc_arch () +ppc_arch (void) { const char *default_cpu = TARGET_CPU; ppc_set_cpu (); @@ -1190,7 +1179,7 @@ ppc_arch () } unsigned long -ppc_mach () +ppc_mach (void) { if (ppc_obj64) return bfd_mach_ppc64; @@ -1201,7 +1190,7 @@ ppc_mach () } extern char* -ppc_target_format () +ppc_target_format (void) { #ifdef OBJ_COFF #ifdef TE_PE @@ -1233,11 +1222,11 @@ ppc_target_format () static void ppc_setup_opcodes (void) { - register const struct powerpc_opcode *op; + const struct powerpc_opcode *op; const struct powerpc_opcode *op_end; const struct powerpc_macro *macro; const struct powerpc_macro *macro_end; - bfd_boolean dup_insn = FALSE; + bfd_boolean bad_insn = FALSE; if (ppc_hash != NULL) hash_die (ppc_hash); @@ -1247,10 +1236,77 @@ ppc_setup_opcodes (void) /* Insert the opcodes into a hash table. */ ppc_hash = hash_new (); + if (ENABLE_CHECKING) + { + unsigned int i; + + /* Check operand masks. Code here and in the disassembler assumes + all the 1's in the mask are contiguous. */ + for (i = 0; i < num_powerpc_operands; ++i) + { + unsigned long mask = powerpc_operands[i].bitm; + unsigned long right_bit; + unsigned int j; + + right_bit = mask & -mask; + mask += right_bit; + right_bit = mask & -mask; + if (mask != right_bit) + { + as_bad (_("powerpc_operands[%d].bitm invalid"), i); + bad_insn = TRUE; + } + for (j = i + 1; j < num_powerpc_operands; ++j) + if (memcmp (&powerpc_operands[i], &powerpc_operands[j], + sizeof (powerpc_operands[0])) == 0) + { + as_bad (_("powerpc_operands[%d] duplicates powerpc_operands[%d]"), + j, i); + bad_insn = TRUE; + } + } + } + op_end = powerpc_opcodes + powerpc_num_opcodes; for (op = powerpc_opcodes; op < op_end; op++) { - know ((op->opcode & op->mask) == op->opcode); + if (ENABLE_CHECKING) + { + const unsigned char *o; + unsigned long omask = op->mask; + + /* The mask had better not trim off opcode bits. */ + if ((op->opcode & omask) != op->opcode) + { + as_bad (_("mask trims opcode bits for %s"), + op->name); + bad_insn = TRUE; + } + + /* The operands must not overlap the opcode or each other. */ + for (o = op->operands; *o; ++o) + if (*o >= num_powerpc_operands) + { + as_bad (_("operand index error for %s"), + op->name); + bad_insn = TRUE; + } + else + { + const struct powerpc_operand *operand = &powerpc_operands[*o]; + if (operand->shift >= 0) + { + unsigned long mask = operand->bitm << operand->shift; + if (omask & mask) + { + as_bad (_("operand %d overlap in %s"), + (int) (o - op->operands), op->name); + bad_insn = TRUE; + } + omask |= mask; + } + } + } if ((op->flags & ppc_cpu & ~(PPC_OPCODE_32 | PPC_OPCODE_64)) != 0 && ((op->flags & (PPC_OPCODE_32 | PPC_OPCODE_64)) == 0 @@ -1270,11 +1326,14 @@ ppc_setup_opcodes (void) == (ppc_cpu & PPC_OPCODE_POWER4))) && ((op->flags & PPC_OPCODE_POWER5) == 0 || ((op->flags & PPC_OPCODE_POWER5) - == (ppc_cpu & PPC_OPCODE_POWER5)))) + == (ppc_cpu & PPC_OPCODE_POWER5))) + && ((op->flags & PPC_OPCODE_POWER6) == 0 + || ((op->flags & PPC_OPCODE_POWER6) + == (ppc_cpu & PPC_OPCODE_POWER6)))) { const char *retval; - retval = hash_insert (ppc_hash, op->name, (PTR) op); + retval = hash_insert (ppc_hash, op->name, (void *) op); if (retval != NULL) { /* Ignore Power duplicates for -m601. */ @@ -1282,16 +1341,16 @@ ppc_setup_opcodes (void) && (op->flags & PPC_OPCODE_POWER) != 0) continue; - as_bad (_("Internal assembler error for instruction %s"), + as_bad (_("duplicate instruction %s"), op->name); - dup_insn = TRUE; + bad_insn = TRUE; } } } if ((ppc_cpu & PPC_OPCODE_ANY) != 0) for (op = powerpc_opcodes; op < op_end; op++) - hash_insert (ppc_hash, op->name, (PTR) op); + hash_insert (ppc_hash, op->name, (void *) op); /* Insert the macros into a hash table. */ ppc_macro_hash = hash_new (); @@ -1303,16 +1362,16 @@ ppc_setup_opcodes (void) { const char *retval; - retval = hash_insert (ppc_macro_hash, macro->name, (PTR) macro); + retval = hash_insert (ppc_macro_hash, macro->name, (void *) macro); if (retval != (const char *) NULL) { - as_bad (_("Internal assembler error for macro %s"), macro->name); - dup_insn = TRUE; + as_bad (_("duplicate macro %s"), macro->name); + bad_insn = TRUE; } } } - if (dup_insn) + if (bad_insn) abort (); } @@ -1321,7 +1380,7 @@ ppc_setup_opcodes (void) opened. */ void -md_begin () +md_begin (void) { ppc_set_cpu (); @@ -1364,7 +1423,7 @@ md_begin () } void -ppc_cleanup () +ppc_cleanup (void) { #ifdef OBJ_ELF if (ppc_apuinfo_list == NULL) @@ -1426,54 +1485,60 @@ ppc_cleanup () /* Insert an operand value into an instruction. */ static unsigned long -ppc_insert_operand (insn, operand, val, file, line) - unsigned long insn; - const struct powerpc_operand *operand; - offsetT val; - char *file; - unsigned int line; +ppc_insert_operand (unsigned long insn, + const struct powerpc_operand *operand, + offsetT val, + char *file, + unsigned int line) { - if (operand->bits != 32) + long min, max, right; + + max = operand->bitm; + right = max & -max; + min = 0; + + if ((operand->flags & PPC_OPERAND_SIGNED) != 0) { - long min, max; - offsetT test; + if ((operand->flags & PPC_OPERAND_SIGNOPT) == 0) + max = (max >> 1) & -right; + min = ~max & -right; + } - if ((operand->flags & PPC_OPERAND_SIGNED) != 0) - { - if ((operand->flags & PPC_OPERAND_SIGNOPT) != 0) - max = (1 << operand->bits) - 1; - else - max = (1 << (operand->bits - 1)) - 1; - min = - (1 << (operand->bits - 1)); + if ((operand->flags & PPC_OPERAND_PLUS1) != 0) + max++; - if (!ppc_obj64) - { - /* Some people write 32 bit hex constants with the sign - extension done by hand. This shouldn't really be - valid, but, to permit this code to assemble on a 64 - bit host, we sign extend the 32 bit value. */ - if (val > 0 - && (val & (offsetT) 0x80000000) != 0 - && (val & (offsetT) 0xffffffff) == val) - { - val -= 0x80000000; - val -= 0x80000000; - } - } - } - else - { - max = (1 << operand->bits) - 1; - min = 0; - } + if ((operand->flags & PPC_OPERAND_NEGATIVE) != 0) + { + long tmp = min; + min = -max; + max = -tmp; + } - if ((operand->flags & PPC_OPERAND_NEGATIVE) != 0) - test = - val; - else - test = val; + if (min <= max) + { + /* Some people write constants with the sign extension done by + hand but only up to 32 bits. This shouldn't really be valid, + but, to permit this code to assemble on a 64-bit host, we + sign extend the 32-bit value to 64 bits if so doing makes the + value valid. */ + if (val > max + && (offsetT) (val - 0x80000000 - 0x80000000) >= min + && (offsetT) (val - 0x80000000 - 0x80000000) <= max + && ((val - 0x80000000 - 0x80000000) & (right - 1)) == 0) + val = val - 0x80000000 - 0x80000000; - if (test < (offsetT) min || test > (offsetT) max) - as_bad_value_out_of_range (_("operand"), test, (offsetT) min, (offsetT) max, file, line); + /* Similarly, people write expressions like ~(1<<15), and expect + this to be OK for a 32-bit unsigned value. */ + else if (val < min + && (offsetT) (val + 0x80000000 + 0x80000000) >= min + && (offsetT) (val + 0x80000000 + 0x80000000) <= max + && ((val + 0x80000000 + 0x80000000) & (right - 1)) == 0) + val = val + 0x80000000 + 0x80000000; + + else if (val < min + || val > max + || (val & (right - 1)) != 0) + as_bad_value_out_of_range (_("operand"), val, min, max, file, line); } if (operand->insert) @@ -1486,8 +1551,7 @@ ppc_insert_operand (insn, operand, val, file, line) as_bad_where (file, line, errmsg); } else - insn |= (((long) val & ((1 << operand->bits) - 1)) - << operand->shift); + insn |= ((long) val & operand->bitm) << operand->shift; return insn; } @@ -1496,9 +1560,7 @@ ppc_insert_operand (insn, operand, val, file, line) #ifdef OBJ_ELF /* Parse @got, etc. and return the desired relocation. */ static bfd_reloc_code_real_type -ppc_elf_suffix (str_p, exp_p) - char **str_p; - expressionS *exp_p; +ppc_elf_suffix (char **str_p, expressionS *exp_p) { struct map_bfd { char *string; @@ -1676,8 +1738,7 @@ ppc_elf_suffix (str_p, exp_p) /* Like normal .long/.short/.word, except support @got, etc. Clobbers input_line_pointer, checks end-of-line. */ static void -ppc_elf_cons (nbytes) - register int nbytes; /* 1=.byte, 2=.word, 4=.long, 8=.llong. */ +ppc_elf_cons (int nbytes /* 1=.byte, 2=.word, 4=.long, 8=.llong */) { expressionS exp; bfd_reloc_code_real_type reloc; @@ -1732,8 +1793,7 @@ ppc_elf_cons (nbytes) /* Solaris pseduo op to change to the .rodata section. */ static void -ppc_elf_rdata (xxx) - int xxx; +ppc_elf_rdata (int xxx) { char *save_line = input_line_pointer; static char section[] = ".rodata\n"; @@ -1747,14 +1807,13 @@ ppc_elf_rdata (xxx) /* Pseudo op to make file scope bss items. */ static void -ppc_elf_lcomm (xxx) - int xxx ATTRIBUTE_UNUSED; +ppc_elf_lcomm (int xxx ATTRIBUTE_UNUSED) { - register char *name; - register char c; - register char *p; + char *name; + char c; + char *p; offsetT size; - register symbolS *symbolP; + symbolS *symbolP; offsetT align; segT old_sec; int old_subsec; @@ -1857,9 +1916,7 @@ ppc_elf_lcomm (xxx) fixups for word relocations in writable segments, so we can adjust them at runtime. */ static void -ppc_elf_validate_fix (fixp, seg) - fixS *fixp; - segT seg; +ppc_elf_validate_fix (fixS *fixp, segT seg) { if (fixp->fx_done || fixp->fx_pcrel) return; @@ -1904,7 +1961,7 @@ ppc_elf_validate_fix (fixp, seg) function descriptor sym if the corresponding code sym is used. */ void -ppc_frob_file_before_adjust () +ppc_frob_file_before_adjust (void) { symbolS *symp; asection *toc; @@ -1987,8 +2044,7 @@ enum toc_size_qualifier }; static int -parse_toc_entry (toc_kind) - enum toc_size_qualifier *toc_kind; +parse_toc_entry (enum toc_size_qualifier *toc_kind) { char *start; char *toc_spec; @@ -2052,8 +2108,7 @@ parse_toc_entry (toc_kind) #ifdef OBJ_ELF #define APUID(a,v) ((((a) & 0xffff) << 16) | ((v) & 0xffff)) static void -ppc_apuinfo_section_add (apu, version) - unsigned int apu, version; +ppc_apuinfo_section_add (unsigned int apu, unsigned int version) { unsigned int i; @@ -2099,8 +2154,7 @@ struct ppc_fixup /* This routine is called for each instruction to be assembled. */ void -md_assemble (str) - char *str; +md_assemble (char *str) { char *s; const struct powerpc_opcode *opcode; @@ -2724,16 +2778,14 @@ md_assemble (str) around operands here. */ static void -ppc_macro (str, macro) - char *str; - const struct powerpc_macro *macro; +ppc_macro (char *str, const struct powerpc_macro *macro) { char *operands[10]; unsigned int count; char *s; unsigned int len; const char *format; - int arg; + unsigned int arg; char *send; char *complete; @@ -2771,7 +2823,7 @@ ppc_macro (str, macro) else { arg = strtol (format + 1, &send, 10); - know (send != format && arg >= 0 && arg < count); + know (send != format && arg < count); len += strlen (operands[arg]); format = send; } @@ -2802,9 +2854,7 @@ ppc_macro (str, macro) /* For ELF, add support for SHF_EXCLUDE and SHT_ORDERED. */ int -ppc_section_letter (letter, ptr_msg) - int letter; - char **ptr_msg; +ppc_section_letter (int letter, char **ptr_msg) { if (letter == 'e') return SHF_EXCLUDE; @@ -2814,9 +2864,7 @@ ppc_section_letter (letter, ptr_msg) } int -ppc_section_word (str, len) - char *str; - size_t len; +ppc_section_word (char *str, size_t len) { if (len == 7 && strncmp (str, "exclude", 7) == 0) return SHF_EXCLUDE; @@ -2825,9 +2873,7 @@ ppc_section_word (str, len) } int -ppc_section_type (str, len) - char *str; - size_t len; +ppc_section_type (char *str, size_t len) { if (len == 7 && strncmp (str, "ordered", 7) == 0) return SHT_ORDERED; @@ -2836,10 +2882,7 @@ ppc_section_type (str, len) } int -ppc_section_flags (flags, attr, type) - int flags; - int attr; - int type; +ppc_section_flags (int flags, int attr, int type) { if (type == SHT_ORDERED) flags |= SEC_ALLOC | SEC_LOAD | SEC_SORT_ENTRIES; @@ -2858,8 +2901,7 @@ ppc_section_flags (flags, attr, type) pseudo-op, but it can also take a single ASCII string. */ static void -ppc_byte (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_byte (int ignore ATTRIBUTE_UNUSED) { if (*input_line_pointer != '\"') { @@ -2903,8 +2945,7 @@ static bfd_boolean ppc_stab_symbol; aligns .comm and .lcomm to 4 bytes. */ static void -ppc_comm (lcomm) - int lcomm; +ppc_comm (int lcomm) { asection *current_seg = now_seg; subsegT current_subseg = now_subseg; @@ -3058,8 +3099,7 @@ ppc_comm (lcomm) optional second argument is the alignment (the default is 2). */ static void -ppc_csect (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_csect (int ignore ATTRIBUTE_UNUSED) { char *name; char endc; @@ -3094,9 +3134,7 @@ ppc_csect (ignore) /* Change to a different csect. */ static void -ppc_change_csect (sym, align) - symbolS *sym; - offsetT align; +ppc_change_csect (symbolS *sym, offsetT align) { if (S_IS_DEFINED (sym)) subseg_set (S_GET_SEGMENT (sym), symbol_get_tc (sym)->subseg); @@ -3196,8 +3234,7 @@ ppc_change_csect (sym, align) convenience of people who aren't used to XCOFF. */ static void -ppc_section (type) - int type; +ppc_section (int type) { const char *name; symbolS *sym; @@ -3221,8 +3258,7 @@ ppc_section (type) we do permit the user to name the text or data section. */ static void -ppc_named_section (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_named_section (int ignore ATTRIBUTE_UNUSED) { char *user_name; const char *real_name; @@ -3256,8 +3292,7 @@ ppc_named_section (ignore) /* The .extern pseudo-op. We create an undefined symbol. */ static void -ppc_extern (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_extern (int ignore ATTRIBUTE_UNUSED) { char *name; char endc; @@ -3275,8 +3310,7 @@ ppc_extern (ignore) /* The .lglobl pseudo-op. Keep the symbol in the symbol table. */ static void -ppc_lglobl (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_lglobl (int ignore ATTRIBUTE_UNUSED) { char *name; char endc; @@ -3298,8 +3332,7 @@ ppc_lglobl (ignore) although I don't know why it bothers. */ static void -ppc_rename (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_rename (int ignore ATTRIBUTE_UNUSED) { char *name; char endc; @@ -3334,8 +3367,7 @@ ppc_rename (ignore) always zero, and I am assuming it is the type. */ static void -ppc_stabx (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_stabx (int ignore ATTRIBUTE_UNUSED) { char *name; int len; @@ -3461,8 +3493,7 @@ ppc_stabx (ignore) gets an aux entry like that used for a csect. */ static void -ppc_function (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_function (int ignore ATTRIBUTE_UNUSED) { char *name; char endc; @@ -3555,8 +3586,7 @@ ppc_function (ignore) static symbolS *saved_bi_sym = 0; static void -ppc_bf (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_bf (int ignore ATTRIBUTE_UNUSED) { symbolS *sym; @@ -3591,8 +3621,7 @@ ppc_bf (ignore) most recent ".bf" symbol. */ static void -ppc_ef (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_ef (int ignore ATTRIBUTE_UNUSED) { symbolS *sym; @@ -3616,8 +3645,7 @@ ppc_ef (ignore) is encountered. */ static void -ppc_biei (ei) - int ei; +ppc_biei (int ei) { static symbolS *last_biei; @@ -3671,8 +3699,7 @@ ppc_biei (ei) .bs symbol is the index of this csect symbol. */ static void -ppc_bs (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_bs (int ignore ATTRIBUTE_UNUSED) { char *name; char endc; @@ -3707,8 +3734,7 @@ ppc_bs (ignore) /* The .es pseudo-op. Generate a C_ESTART symbol named .es. */ static void -ppc_es (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_es (int ignore ATTRIBUTE_UNUSED) { symbolS *sym; @@ -3732,8 +3758,7 @@ ppc_es (ignore) line number. */ static void -ppc_bb (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_bb (int ignore ATTRIBUTE_UNUSED) { symbolS *sym; @@ -3759,8 +3784,7 @@ ppc_bb (ignore) line number. */ static void -ppc_eb (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_eb (int ignore ATTRIBUTE_UNUSED) { symbolS *sym; @@ -3784,8 +3808,7 @@ ppc_eb (ignore) specified name. */ static void -ppc_bc (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_bc (int ignore ATTRIBUTE_UNUSED) { char *name; int len; @@ -3807,8 +3830,7 @@ ppc_bc (ignore) /* The .ec pseudo-op. This just creates a C_ECOMM symbol. */ static void -ppc_ec (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_ec (int ignore ATTRIBUTE_UNUSED) { symbolS *sym; @@ -3827,8 +3849,7 @@ ppc_ec (ignore) /* The .toc pseudo-op. Switch to the .toc subsegment. */ static void -ppc_toc (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_toc (int ignore ATTRIBUTE_UNUSED) { if (ppc_toc_csect != (symbolS *) NULL) subseg_set (data_section, symbol_get_tc (ppc_toc_csect)->subseg); @@ -3874,8 +3895,7 @@ ppc_toc (ignore) .short pseudo-op, and we want to be compatible. */ static void -ppc_xcoff_cons (log_size) - int log_size; +ppc_xcoff_cons (int log_size) { frag_align (log_size, 0, 0); record_alignment (now_seg, log_size); @@ -3883,8 +3903,7 @@ ppc_xcoff_cons (log_size) } static void -ppc_vbyte (dummy) - int dummy ATTRIBUTE_UNUSED; +ppc_vbyte (int dummy ATTRIBUTE_UNUSED) { expressionS exp; int byte_count; @@ -3927,8 +3946,7 @@ ppc_vbyte (dummy) the first argument is simply ignored. */ static void -ppc_tc (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_tc (int ignore ATTRIBUTE_UNUSED) { #ifdef OBJ_XCOFF @@ -4014,8 +4032,7 @@ ppc_tc (ignore) /* Pseudo-op .machine. */ static void -ppc_machine (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_machine (int ignore ATTRIBUTE_UNUSED) { char *cpu_string; #define MAX_HISTORY 100 @@ -4078,8 +4095,7 @@ ppc_machine (ignore) /* See whether a symbol is in the TOC section. */ static int -ppc_is_toc_sym (sym) - symbolS *sym; +ppc_is_toc_sym (symbolS *sym) { #ifdef OBJ_XCOFF return symbol_get_tc (sym)->class == XMC_TC; @@ -4100,8 +4116,7 @@ ppc_is_toc_sym (sym) /* Set the current section. */ static void -ppc_set_current_section (new) - segT new; +ppc_set_current_section (segT new) { ppc_previous_section = ppc_current_section; ppc_current_section = new; @@ -4113,8 +4128,7 @@ ppc_set_current_section (new) warnings: "No previous section" */ static void -ppc_previous (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_previous (int ignore ATTRIBUTE_UNUSED) { symbolS *tmp; @@ -4145,8 +4159,7 @@ ppc_previous (ignore) handling, debugging, etc. */ static void -ppc_pdata (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_pdata (int ignore ATTRIBUTE_UNUSED) { if (pdata_section == 0) { @@ -4180,8 +4193,7 @@ ppc_pdata (ignore) debugging, etc. */ static void -ppc_ydata (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_ydata (int ignore ATTRIBUTE_UNUSED) { if (ydata_section == 0) { @@ -4217,8 +4229,7 @@ ppc_ydata (ignore) function descriptors, etc. */ static void -ppc_reldata (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_reldata (int ignore ATTRIBUTE_UNUSED) { if (reldata_section == 0) { @@ -4248,8 +4259,7 @@ ppc_reldata (ignore) 3 - double word aligned (that would be 4 byte boundary) */ static void -ppc_rdata (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_rdata (int ignore ATTRIBUTE_UNUSED) { if (rdata_section == 0) { @@ -4275,8 +4285,7 @@ ppc_rdata (ignore) warnings: None */ static void -ppc_ualong (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_ualong (int ignore ATTRIBUTE_UNUSED) { /* Try for long. */ cons (4); @@ -4290,8 +4299,7 @@ ppc_ualong (ignore) warnings: Missing symbol name */ static void -ppc_znop (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_znop (int ignore ATTRIBUTE_UNUSED) { unsigned long insn; const struct powerpc_opcode *opcode; @@ -4343,14 +4351,13 @@ ppc_znop (ignore) warnings: */ static void -ppc_pe_comm (lcomm) - int lcomm; +ppc_pe_comm (int lcomm) { - register char *name; - register char c; - register char *p; + char *name; + char c; + char *p; offsetT temp; - register symbolS *symbolP; + symbolS *symbolP; offsetT align; name = input_line_pointer; @@ -4473,8 +4480,7 @@ ppc_pe_comm (lcomm) */ void -ppc_pe_section (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_pe_section (int ignore ATTRIBUTE_UNUSED) { /* Strip out the section name. */ char *section_name; @@ -4562,7 +4568,7 @@ ppc_pe_section (ignore) case 'R': /* Remove section at link time */ flags |= SEC_NEVER_LOAD; break; - +#if IFLICT_BRAIN_DAMAGE /* Section Protection */ case 'r': /* section is readable */ flags |= IMAGE_SCN_MEM_READ; @@ -4606,7 +4612,7 @@ ppc_pe_section (ignore) flags |= IMAGE_SCN_ALIGN_64BYTES; align = 6; break; - +#endif default: as_bad (_("unknown section attribute '%c'"), *input_line_pointer); @@ -4632,12 +4638,10 @@ ppc_pe_section (ignore) } bfd_set_section_alignment (stdoutput, sec, align); - } static void -ppc_pe_function (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_pe_function (int ignore ATTRIBUTE_UNUSED) { char *name; char endc; @@ -4659,8 +4663,7 @@ ppc_pe_function (ignore) } static void -ppc_pe_tocd (ignore) - int ignore ATTRIBUTE_UNUSED; +ppc_pe_tocd (int ignore ATTRIBUTE_UNUSED) { if (tocdata_section == 0) { @@ -4685,8 +4688,7 @@ ppc_pe_tocd (ignore) /* Don't adjust TOC relocs to use the section symbol. */ int -ppc_pe_fix_adjustable (fix) - fixS *fix; +ppc_pe_fix_adjustable (fixS *fix) { return fix->fx_r_type != BFD_RELOC_PPC_TOC16; } @@ -4701,8 +4703,7 @@ ppc_pe_fix_adjustable (fix) any, to use square brackets, and to be in upper case. */ char * -ppc_canonicalize_symbol_name (name) - char *name; +ppc_canonicalize_symbol_name (char *name) { char *s; @@ -4739,8 +4740,7 @@ ppc_canonicalize_symbol_name (name) called whenever a new symbol is created. */ void -ppc_symbol_new_hook (sym) - symbolS *sym; +ppc_symbol_new_hook (symbolS *sym) { struct ppc_tc_sy *tc; const char *s; @@ -4828,8 +4828,7 @@ ppc_symbol_new_hook (sym) follows the csect symbol. */ void -ppc_frob_label (sym) - symbolS *sym; +ppc_frob_label (symbolS *sym) { if (ppc_current_csect != (symbolS *) NULL) { @@ -4859,8 +4858,7 @@ static bfd_boolean ppc_saw_abs; symbol table. */ int -ppc_frob_symbol (sym) - symbolS *sym; +ppc_frob_symbol (symbolS *sym) { static symbolS *ppc_last_function; static symbolS *set_end; @@ -5123,7 +5121,7 @@ ppc_frob_symbol (sym) absolute symbols. */ void -ppc_adjust_symtab () +ppc_adjust_symtab (void) { symbolS *sym; @@ -5169,8 +5167,7 @@ ppc_adjust_symtab () turn. */ void -ppc_frob_section (sec) - asection *sec; +ppc_frob_section (asection *sec) { static bfd_vma vma = 0; @@ -5187,10 +5184,7 @@ ppc_frob_section (sec) returned, or NULL on OK. */ char * -md_atof (type, litp, sizep) - int type; - char *litp; - int *sizep; +md_atof (int type, char *litp, int *sizep) { int prec; LITTLENUM_TYPE words[4]; @@ -5242,10 +5236,7 @@ md_atof (type, litp, sizep) endianness. */ void -md_number_to_chars (buf, val, n) - char *buf; - valueT val; - int n; +md_number_to_chars (char *buf, valueT val, int n) { if (target_big_endian) number_to_chars_bigendian (buf, val, n); @@ -5256,21 +5247,22 @@ md_number_to_chars (buf, val, n) /* Align a section (I don't know why this is machine dependent). */ valueT -md_section_align (seg, addr) - asection *seg; - valueT addr; +md_section_align (asection *seg ATTRIBUTE_UNUSED, valueT addr) { +#ifdef OBJ_ELF + return addr; +#else int align = bfd_get_section_alignment (stdoutput, seg); return ((addr + (1 << align) - 1) & (-1 << align)); +#endif } /* We don't have any form of relaxing. */ int -md_estimate_size_before_relax (fragp, seg) - fragS *fragp ATTRIBUTE_UNUSED; - asection *seg ATTRIBUTE_UNUSED; +md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED, + asection *seg ATTRIBUTE_UNUSED) { abort (); return 0; @@ -5279,10 +5271,9 @@ md_estimate_size_before_relax (fragp, seg) /* Convert a machine dependent frag. We never generate these. */ void -md_convert_frag (abfd, sec, fragp) - bfd *abfd ATTRIBUTE_UNUSED; - asection *sec ATTRIBUTE_UNUSED; - fragS *fragp ATTRIBUTE_UNUSED; +md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, + asection *sec ATTRIBUTE_UNUSED, + fragS *fragp ATTRIBUTE_UNUSED) { abort (); } @@ -5290,8 +5281,7 @@ md_convert_frag (abfd, sec, fragp) /* We have no need to default values of symbols. */ symbolS * -md_undefined_symbol (name) - char *name ATTRIBUTE_UNUSED; +md_undefined_symbol (char *name ATTRIBUTE_UNUSED) { return 0; } @@ -5302,9 +5292,7 @@ md_undefined_symbol (name) given a PC relative reloc. */ long -md_pcrel_from_section (fixp, sec) - fixS *fixp; - segT sec ATTRIBUTE_UNUSED; +md_pcrel_from_section (fixS *fixp, segT sec ATTRIBUTE_UNUSED) { return fixp->fx_frag->fr_address + fixp->fx_where; } @@ -5317,8 +5305,7 @@ md_pcrel_from_section (fixp, sec) corresponding .tc symbol. */ int -ppc_fix_adjustable (fix) - fixS *fix; +ppc_fix_adjustable (fixS *fix) { valueT val = resolve_symbol_value (fix->fx_addsy); segT symseg = S_GET_SEGMENT (fix->fx_addsy); @@ -5443,8 +5430,7 @@ ppc_fix_adjustable (fix) between two csects in the same section. */ int -ppc_force_relocation (fix) - fixS *fix; +ppc_force_relocation (fixS *fix) { /* At this point fix->fx_addsy should already have been converted to a csect symbol. If the csect does not include the fragment, then @@ -5469,8 +5455,7 @@ ppc_force_relocation (fix) will be emitted for a fixup. */ int -ppc_force_relocation (fix) - fixS *fix; +ppc_force_relocation (fixS *fix) { /* Branch prediction relocations must force a relocation, as must the vtable description relocs. */ @@ -5495,8 +5480,7 @@ ppc_force_relocation (fix) } int -ppc_fix_adjustable (fix) - fixS *fix; +ppc_fix_adjustable (fixS *fix) { return (fix->fx_r_type != BFD_RELOC_16_GOTOFF && fix->fx_r_type != BFD_RELOC_LO16_GOTOFF @@ -5510,6 +5494,47 @@ ppc_fix_adjustable (fix) } #endif +/* Implement HANDLE_ALIGN. This writes the NOP pattern into an + rs_align_code frag. */ + +void +ppc_handle_align (struct frag *fragP) +{ + valueT count = (fragP->fr_next->fr_address + - (fragP->fr_address + fragP->fr_fix)); + + if (count != 0 && (count & 3) == 0) + { + char *dest = fragP->fr_literal + fragP->fr_fix; + + fragP->fr_var = 4; + md_number_to_chars (dest, 0x60000000, 4); + + if ((ppc_cpu & PPC_OPCODE_POWER6) != 0) + { + /* For power6, we want the last nop to be a group terminating + one, "ori 1,1,0". Do this by inserting an rs_fill frag + immediately after this one, with its address set to the last + nop location. This will automatically reduce the number of + nops in the current frag by one. */ + if (count > 4) + { + struct frag *group_nop = xmalloc (SIZEOF_STRUCT_FRAG + 4); + + memcpy (group_nop, fragP, SIZEOF_STRUCT_FRAG); + group_nop->fr_address = group_nop->fr_next->fr_address - 4; + group_nop->fr_fix = 0; + group_nop->fr_offset = 1; + group_nop->fr_type = rs_fill; + fragP->fr_next = group_nop; + dest = group_nop->fr_literal; + } + + md_number_to_chars (dest, 0x60210000, 4); + } + } +} + /* 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 @@ -5520,10 +5545,7 @@ ppc_fix_adjustable (fix) fixup. */ void -md_apply_fix (fixP, valP, seg) - fixS *fixP; - valueT * valP; - segT seg ATTRIBUTE_UNUSED; +md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) { valueT value = * valP; @@ -5580,7 +5602,7 @@ md_apply_fix (fixP, valP, seg) csect. Other usages, such as `.long sym', generate relocs. This is the documented behaviour of non-TOC symbols. */ if ((operand->flags & PPC_OPERAND_PARENS) != 0 - && operand->bits == 16 + && (operand->bitm & 0xfff0) == 0xfff0 && operand->shift == 0 && (operand->insert == NULL || ppc_obj64) && fixP->fx_addsy != NULL @@ -5618,11 +5640,11 @@ md_apply_fix (fixP, valP, seg) We are only prepared to turn a few of the operands into relocs. */ if ((operand->flags & PPC_OPERAND_RELATIVE) != 0 - && operand->bits == 26 + && operand->bitm == 0x3fffffc && operand->shift == 0) fixP->fx_r_type = BFD_RELOC_PPC_B26; else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0 - && operand->bits == 16 + && operand->bitm == 0xfffc && operand->shift == 0) { fixP->fx_r_type = BFD_RELOC_PPC_B16; @@ -5633,11 +5655,11 @@ md_apply_fix (fixP, valP, seg) #endif } else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0 - && operand->bits == 26 + && operand->bitm == 0x3fffffc && operand->shift == 0) fixP->fx_r_type = BFD_RELOC_PPC_BA26; else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0 - && operand->bits == 16 + && operand->bitm == 0xfffc && operand->shift == 0) { fixP->fx_r_type = BFD_RELOC_PPC_BA16; @@ -5649,7 +5671,7 @@ md_apply_fix (fixP, valP, seg) } #if defined (OBJ_XCOFF) || defined (OBJ_ELF) else if ((operand->flags & PPC_OPERAND_PARENS) != 0 - && operand->bits == 16 + && (operand->bitm & 0xfff0) == 0xfff0 && operand->shift == 0) { if (ppc_is_toc_sym (fixP->fx_addsy)) @@ -6047,9 +6069,7 @@ md_apply_fix (fixP, valP, seg) /* Generate a reloc for a fixup. */ arelent * -tc_gen_reloc (seg, fixp) - asection *seg ATTRIBUTE_UNUSED; - fixS *fixp; +tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp) { arelent *reloc; @@ -6072,13 +6092,13 @@ tc_gen_reloc (seg, fixp) } void -ppc_cfi_frame_initial_instructions () +ppc_cfi_frame_initial_instructions (void) { cfi_add_CFA_def_cfa (1, 0); } int -tc_ppc_regname_to_dw2regnum (const char *regname) +tc_ppc_regname_to_dw2regnum (char *regname) { unsigned int regnum = -1; unsigned int i; diff --git a/contrib/binutils/gas/config/tc-ppc.h b/contrib/binutils/gas/config/tc-ppc.h index f7c2da6..1e85649 100644 --- a/contrib/binutils/gas/config/tc-ppc.h +++ b/contrib/binutils/gas/config/tc-ppc.h @@ -1,6 +1,6 @@ /* tc-ppc.h -- Header file for tc-ppc.c. Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, - 2004, 2005 Free Software Foundation, Inc. + 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Written by Ian Lance Taylor, Cygnus Support. This file is part of GAS, the GNU Assembler. @@ -42,15 +42,15 @@ struct fix; /* The target BFD architecture. */ #define TARGET_ARCH (ppc_arch ()) #define TARGET_MACH (ppc_mach ()) -extern enum bfd_architecture ppc_arch PARAMS ((void)); -extern unsigned long ppc_mach PARAMS ((void)); +extern enum bfd_architecture ppc_arch (void); +extern unsigned long ppc_mach (void); /* Whether or not the target is big endian */ extern int target_big_endian; /* The target BFD format. */ #define TARGET_FORMAT (ppc_target_format ()) -extern char *ppc_target_format PARAMS ((void)); +extern char *ppc_target_format (void); /* Permit temporary numeric labels. */ #define LOCAL_LABELS_FB 1 @@ -78,31 +78,12 @@ extern char *ppc_target_format PARAMS ((void)); #define MAX_MEM_FOR_RS_ALIGN_CODE 4 #define HANDLE_ALIGN(FRAGP) \ - if ((FRAGP)->fr_type == rs_align_code) \ - { \ - valueT count = ((FRAGP)->fr_next->fr_address \ - - ((FRAGP)->fr_address + (FRAGP)->fr_fix)); \ - if (count != 0 && (count & 3) == 0) \ - { \ - char *dest = (FRAGP)->fr_literal + (FRAGP)->fr_fix; \ - \ - (FRAGP)->fr_var = 4; \ - if (target_big_endian) \ - { \ - *dest++ = 0x60; \ - *dest++ = 0; \ - *dest++ = 0; \ - *dest++ = 0; \ - } \ - else \ - { \ - *dest++ = 0; \ - *dest++ = 0; \ - *dest++ = 0; \ - *dest++ = 0x60; \ - } \ - } \ - } + if ((FRAGP)->fr_type == rs_align_code) \ + ppc_handle_align (FRAGP); + +extern void ppc_handle_align (struct frag *); + +#define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) 0 #define md_frag_check(FRAGP) \ if ((FRAGP)->has_code \ @@ -117,7 +98,7 @@ extern char *ppc_target_format PARAMS ((void)); /* Don't adjust TOC relocs. */ #define tc_fix_adjustable(FIX) ppc_pe_fix_adjustable (FIX) -extern int ppc_pe_fix_adjustable PARAMS ((struct fix *)); +extern int ppc_pe_fix_adjustable (struct fix *); #endif @@ -165,31 +146,31 @@ struct ppc_tc_sy /* Canonicalize the symbol name. */ #define tc_canonicalize_symbol_name(name) ppc_canonicalize_symbol_name (name) -extern char *ppc_canonicalize_symbol_name PARAMS ((char *)); +extern char *ppc_canonicalize_symbol_name (char *); /* Get the symbol class from the name. */ #define tc_symbol_new_hook(sym) ppc_symbol_new_hook (sym) -extern void ppc_symbol_new_hook PARAMS ((symbolS *)); +extern void ppc_symbol_new_hook (symbolS *); /* Set the symbol class of a label based on the csect. */ #define tc_frob_label(sym) ppc_frob_label (sym) -extern void ppc_frob_label PARAMS ((symbolS *)); +extern void ppc_frob_label (symbolS *); /* TOC relocs requires special handling. */ #define tc_fix_adjustable(FIX) ppc_fix_adjustable (FIX) -extern int ppc_fix_adjustable PARAMS ((struct fix *)); +extern int ppc_fix_adjustable (struct fix *); /* We need to set the section VMA. */ #define tc_frob_section(sec) ppc_frob_section (sec) -extern void ppc_frob_section PARAMS ((asection *)); +extern void ppc_frob_section (asection *); /* Finish up the symbol. */ #define tc_frob_symbol(sym, punt) punt = ppc_frob_symbol (sym) -extern int ppc_frob_symbol PARAMS ((symbolS *)); +extern int ppc_frob_symbol (symbolS *); /* Finish up the entire symtab. */ #define tc_adjust_symtab() ppc_adjust_symtab () -extern void ppc_adjust_symtab PARAMS ((void)); +extern void ppc_adjust_symtab (void); /* We also need to copy, in particular, the class of the symbol, over what obj-coff would otherwise have copied. */ @@ -211,10 +192,10 @@ extern const char ppc_symbol_chars[]; #ifdef OBJ_ELF /* Support for SHF_EXCLUDE and SHT_ORDERED */ -extern int ppc_section_letter PARAMS ((int, char **)); -extern int ppc_section_type PARAMS ((char *, size_t)); -extern int ppc_section_word PARAMS ((char *, size_t)); -extern int ppc_section_flags PARAMS ((int, int, int)); +extern int ppc_section_letter (int, char **); +extern int ppc_section_type (char *, size_t); +extern int ppc_section_word (char *, size_t); +extern int ppc_section_flags (int, int, int); #define md_elf_section_letter(LETTER, PTR_MSG) ppc_section_letter (LETTER, PTR_MSG) #define md_elf_section_type(STR, LEN) ppc_section_type (STR, LEN) @@ -226,40 +207,40 @@ extern const char *ppc_comment_chars; /* Keep relocations relative to the GOT, or non-PC relative. */ #define tc_fix_adjustable(FIX) ppc_fix_adjustable (FIX) -extern int ppc_fix_adjustable PARAMS ((struct fix *)); +extern int ppc_fix_adjustable (struct fix *); /* Values passed to md_apply_fix don't include symbol values. */ #define MD_APPLY_SYM_VALUE(FIX) 0 #define tc_frob_file_before_adjust ppc_frob_file_before_adjust -extern void ppc_frob_file_before_adjust PARAMS ((void)); +extern void ppc_frob_file_before_adjust (void); #endif /* OBJ_ELF */ #if defined (OBJ_ELF) || defined (OBJ_XCOFF) #define TC_FORCE_RELOCATION(FIX) ppc_force_relocation (FIX) -extern int ppc_force_relocation PARAMS ((struct fix *)); +extern int ppc_force_relocation (struct fix *); #endif /* call md_pcrel_from_section, not md_pcrel_from */ #define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section(FIX, SEC) -extern long md_pcrel_from_section PARAMS ((struct fix *, segT)); +extern long md_pcrel_from_section (struct fix *, segT); #define md_parse_name(name, exp, mode, c) ppc_parse_name (name, exp) -extern int ppc_parse_name PARAMS ((const char *, struct expressionS *)); +extern int ppc_parse_name (const char *, struct expressionS *); #define md_operand(x) #define md_cleanup() ppc_cleanup () - extern void ppc_cleanup PARAMS ((void)); +extern void ppc_cleanup (void); #define TARGET_USE_CFIPOP 1 #define tc_cfi_frame_initial_instructions ppc_cfi_frame_initial_instructions -extern void ppc_cfi_frame_initial_instructions PARAMS ((void)); +extern void ppc_cfi_frame_initial_instructions (void); #define tc_regname_to_dw2regnum tc_ppc_regname_to_dw2regnum -extern int tc_ppc_regname_to_dw2regnum PARAMS ((const char *regname)); +extern int tc_ppc_regname_to_dw2regnum (char *); extern int ppc_cie_data_alignment; diff --git a/contrib/binutils/gas/config/tc-s390.c b/contrib/binutils/gas/config/tc-s390.c index 56b5b25..6ca31f7b 100644 --- a/contrib/binutils/gas/config/tc-s390.c +++ b/contrib/binutils/gas/config/tc-s390.c @@ -1,5 +1,5 @@ /* tc-s390.c -- Assemble for the S390 - Copyright 2000, 2001, 2002, 2003, 2004, 2005 + Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com). @@ -20,7 +20,6 @@ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -#include <stdio.h> #include "as.h" #include "safe-ctype.h" #include "subsegs.h" @@ -412,6 +411,8 @@ md_parse_option (c, arg) current_cpu = S390_OPCODE_Z990; else if (strcmp (arg + 5, "z9-109") == 0) current_cpu = S390_OPCODE_Z9_109; + else if (strcmp (arg + 5, "z9-ec") == 0) + current_cpu = S390_OPCODE_Z9_EC; else { as_bad (_("invalid switch -m%s"), arg); @@ -2322,7 +2323,7 @@ s390_cfi_frame_initial_instructions () } int -tc_s390_regname_to_dw2regnum (const char *regname) +tc_s390_regname_to_dw2regnum (char *regname) { int regnum = -1; diff --git a/contrib/binutils/gas/config/tc-s390.h b/contrib/binutils/gas/config/tc-s390.h index dbecfef..b387c3b 100644 --- a/contrib/binutils/gas/config/tc-s390.h +++ b/contrib/binutils/gas/config/tc-s390.h @@ -88,7 +88,7 @@ extern void s390_md_end PARAMS ((void)); extern void s390_cfi_frame_initial_instructions PARAMS ((void)); #define tc_regname_to_dw2regnum tc_s390_regname_to_dw2regnum -extern int tc_s390_regname_to_dw2regnum PARAMS ((const char *regname)); +extern int tc_s390_regname_to_dw2regnum PARAMS ((char *regname)); extern int s390_cie_data_alignment; diff --git a/contrib/binutils/gas/config/tc-score.c b/contrib/binutils/gas/config/tc-score.c new file mode 100644 index 0000000..fe45b00 --- /dev/null +++ b/contrib/binutils/gas/config/tc-score.c @@ -0,0 +1,6661 @@ +/* tc-score.c -- Assembler for Score + Copyright 2006 Free Software Foundation, Inc. + Contributed by: + Mei Ligang (ligang@sunnorth.com.cn) + Pei-Lin Tsai (pltsai@sunplus.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, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "as.h" +#include "config.h" +#include "subsegs.h" +#include "safe-ctype.h" +#include "opcode/score-inst.h" +#include "opcode/score-datadep.h" +#include "struc-symbol.h" + +#ifdef OBJ_ELF +#include "elf/score.h" +#include "dwarf2dbg.h" +#endif + +#define GP 28 +#define PIC_CALL_REG 29 +#define MAX_LITERAL_POOL_SIZE 1024 +#define FAIL 0x80000000 +#define SUCCESS 0 +#define INSN_SIZE 4 +#define INSN16_SIZE 2 +#define RELAX_INST_NUM 3 + +/* For score5u : div/mul will pop warning message, mmu/alw/asw will pop error message. */ +#define BAD_ARGS _("bad arguments to instruction") +#define BAD_PC _("r15 not allowed here") +#define BAD_COND _("instruction is not conditional") +#define ERR_NO_ACCUM _("acc0 expected") +#define ERR_FOR_SCORE5U_MUL_DIV _("div / mul are reserved instructions") +#define ERR_FOR_SCORE5U_MMU _("This architecture doesn't support mmu") +#define ERR_FOR_SCORE5U_ATOMIC _("This architecture doesn't support atomic instruction") +#define LONG_LABEL_LEN _("the label length is longer than 1024"); +#define BAD_SKIP_COMMA BAD_ARGS +#define BAD_GARBAGE _("garbage following instruction"); + +#define skip_whitespace(str) while (*(str) == ' ') ++(str) + +/* The name of the readonly data section. */ +#define RDATA_SECTION_NAME (OUTPUT_FLAVOR == bfd_target_aout_flavour \ + ? ".data" \ + : OUTPUT_FLAVOR == bfd_target_ecoff_flavour \ + ? ".rdata" \ + : OUTPUT_FLAVOR == bfd_target_coff_flavour \ + ? ".rdata" \ + : OUTPUT_FLAVOR == bfd_target_elf_flavour \ + ? ".rodata" \ + : (abort (), "")) + +#define RELAX_ENCODE(old, new, type, reloc1, reloc2, opt) \ + ((relax_substateT) \ + (((old) << 23) \ + | ((new) << 16) \ + | ((type) << 9) \ + | ((reloc1) << 5) \ + | ((reloc2) << 1) \ + | ((opt) ? 1 : 0))) + +#define RELAX_OLD(i) (((i) >> 23) & 0x7f) +#define RELAX_NEW(i) (((i) >> 16) & 0x7f) +#define RELAX_TYPE(i) (((i) >> 9) & 0x7f) +#define RELAX_RELOC1(i) ((valueT) ((i) >> 5) & 0xf) +#define RELAX_RELOC2(i) ((valueT) ((i) >> 1) & 0xf) +#define RELAX_OPT(i) ((i) & 1) +#define RELAX_OPT_CLEAR(i) ((i) & ~1) + +#define SET_INSN_ERROR(s) (inst.error = (s)) +#define INSN_IS_PCE_P(s) (strstr (str, "||") != NULL) + +#define GET_INSN_CLASS(type) (get_insn_class_from_type (type)) + +#define GET_INSN_SIZE(type) ((GET_INSN_CLASS (type) == INSN_CLASS_16) \ + ? INSN16_SIZE : INSN_SIZE) + +/* 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 line_comment_chars[] = "#"; +const char line_separator_chars[] = ";"; + +/* Chars that can be used to separate mant from exp in floating point numbers. */ +const char EXP_CHARS[] = "eE"; +const char FLT_CHARS[] = "rRsSfFdDxXeEpP"; + +/* Used to contain constructed error messages. */ +static char err_msg[255]; + +fragS *score_fragp = 0; +static int fix_data_dependency = 0; +static int warn_fix_data_dependency = 1; +static int score7 = 1; +static int university_version = 0; + +static int in_my_get_expression = 0; + +#define USE_GLOBAL_POINTER_OPT 1 +#define SCORE_BI_ENDIAN + +/* Default, pop warning message when using r1. */ +static int nor1 = 1; + +/* Default will do instruction relax, -O0 will set g_opt = 0. */ +static unsigned int g_opt = 1; + +/* The size of the small data section. */ +static unsigned int g_switch_value = 8; + +#ifdef OBJ_ELF +/* Pre-defined "_GLOBAL_OFFSET_TABLE_" */ +symbolS *GOT_symbol; +#endif +static segT pdr_seg; + +enum score_pic_level score_pic = NO_PIC; + +#define INSN_NAME_LEN 16 +struct score_it +{ + char name[INSN_NAME_LEN]; + unsigned long instruction; + unsigned long relax_inst; + int size; + int relax_size; + enum score_insn_type type; + char str[MAX_LITERAL_POOL_SIZE]; + const char *error; + int bwarn; + char reg[INSN_NAME_LEN]; + struct + { + bfd_reloc_code_real_type type; + expressionS exp; + int pc_rel; + }reloc; +}; +struct score_it inst; + +typedef struct proc +{ + symbolS *isym; + unsigned long reg_mask; + unsigned long reg_offset; + unsigned long fpreg_mask; + unsigned long leaf; + unsigned long frame_offset; + unsigned long frame_reg; + unsigned long pc_reg; +} +procS; + +static procS cur_proc; +static procS *cur_proc_ptr; +static int numprocs; + +#define SCORE7_PIPELINE 7 +#define SCORE5_PIPELINE 5 +static int vector_size = SCORE7_PIPELINE; +struct score_it dependency_vector[SCORE7_PIPELINE]; + +/* Relax will need some padding for alignment. */ +#define RELAX_PAD_BYTE 3 + +/* Number of littlenums required to hold an extended precision number. For md_atof. */ +#define NUM_FLOAT_VALS 8 +#define MAX_LITTLENUMS 6 +LITTLENUM_TYPE fp_values[NUM_FLOAT_VALS][MAX_LITTLENUMS]; + +/* Structure for a hash table entry for a register. */ +struct reg_entry +{ + const char *name; + int number; +}; + +static const struct reg_entry score_rn_table[] = +{ + {"r0", 0}, {"r1", 1}, {"r2", 2}, {"r3", 3}, + {"r4", 4}, {"r5", 5}, {"r6", 6}, {"r7", 7}, + {"r8", 8}, {"r9", 9}, {"r10", 10}, {"r11", 11}, + {"r12", 12}, {"r13", 13}, {"r14", 14}, {"r15", 15}, + {"r16", 16}, {"r17", 17}, {"r18", 18}, {"r19", 19}, + {"r20", 20}, {"r21", 21}, {"r22", 22}, {"r23", 23}, + {"r24", 24}, {"r25", 25}, {"r26", 26}, {"r27", 27}, + {"r28", 28}, {"r29", 29}, {"r30", 30}, {"r31", 31}, + {NULL, 0} +}; + +static const struct reg_entry score_srn_table[] = +{ + {"sr0", 0}, {"sr1", 1}, {"sr2", 2}, + {NULL, 0} +}; + +static const struct reg_entry score_crn_table[] = +{ + {"cr0", 0}, {"cr1", 1}, {"cr2", 2}, {"cr3", 3}, + {"cr4", 4}, {"cr5", 5}, {"cr6", 6}, {"cr7", 7}, + {"cr8", 8}, {"cr9", 9}, {"cr10", 10}, {"cr11", 11}, + {"cr12", 12}, {"cr13", 13}, {"cr14", 14}, {"cr15", 15}, + {"cr16", 16}, {"cr17", 17}, {"cr18", 18}, {"cr19", 19}, + {"cr20", 20}, {"cr21", 21}, {"cr22", 22}, {"cr23", 23}, + {"cr24", 24}, {"cr25", 25}, {"cr26", 26}, {"cr27", 27}, + {"cr28", 28}, {"cr29", 29}, {"cr30", 30}, {"cr31", 31}, + {NULL, 0} +}; + +struct reg_map +{ + const struct reg_entry *names; + int max_regno; + struct hash_control *htab; + const char *expected; +}; + +struct reg_map all_reg_maps[] = +{ + {score_rn_table, 31, NULL, N_("S+core register expected")}, + {score_srn_table, 2, NULL, N_("S+core special-register expected")}, + {score_crn_table, 31, NULL, N_("S+core co-processor register expected")}, +}; + +static struct hash_control *score_ops_hsh = NULL; + +static struct hash_control *dependency_insn_hsh = NULL; + +/* Enumeration matching entries in table above. */ +enum score_reg_type +{ + REG_TYPE_SCORE = 0, +#define REG_TYPE_FIRST REG_TYPE_SCORE + REG_TYPE_SCORE_SR = 1, + REG_TYPE_SCORE_CR = 2, + REG_TYPE_MAX = 3 +}; + +typedef struct literalS +{ + struct expressionS exp; + struct score_it *inst; +} +literalT; + +literalT literals[MAX_LITERAL_POOL_SIZE]; + +static void do_ldst_insn (char *); +static void do_crdcrscrsimm5 (char *); +static void do_ldst_unalign (char *); +static void do_ldst_atomic (char *); +static void do_ldst_cop (char *); +static void do_macro_li_rdi32 (char *); +static void do_macro_la_rdi32 (char *); +static void do_macro_rdi32hi (char *); +static void do_macro_rdi32lo (char *); +static void do_macro_mul_rdrsrs (char *); +static void do_macro_ldst_label (char *); +static void do_branch (char *); +static void do_jump (char *); +static void do_empty (char *); +static void do_rdrsrs (char *); +static void do_rdsi16 (char *); +static void do_rdrssi14 (char *); +static void do_sub_rdsi16 (char *); +static void do_sub_rdrssi14 (char *); +static void do_rdrsi5 (char *); +static void do_rdrsi14 (char *); +static void do_rdi16 (char *); +static void do_xrsi5 (char *); +static void do_rdrs (char *); +static void do_rdxrs (char *); +static void do_rsrs (char *); +static void do_rdcrs (char *); +static void do_rdsrs (char *); +static void do_rd (char *); +static void do_rs (char *); +static void do_i15 (char *); +static void do_xi5x (char *); +static void do_ceinst (char *); +static void do_cache (char *); +static void do16_rdrs (char *); +static void do16_rs (char *); +static void do16_xrs (char *); +static void do16_mv_rdrs (char *); +static void do16_hrdrs (char *); +static void do16_rdhrs (char *); +static void do16_rdi4 (char *); +static void do16_rdi5 (char *); +static void do16_xi5 (char *); +static void do16_ldst_insn (char *); +static void do16_ldst_imm_insn (char *); +static void do16_push_pop (char *); +static void do16_branch (char *); +static void do16_jump (char *); +static void do_rdi16_pic (char *); +static void do_addi_s_pic (char *); +static void do_addi_u_pic (char *); +static void do_lw_pic (char *); + +static const struct asm_opcode score_ldst_insns[] = +{ + {"lw", 0x20000000, 0x3e000000, 0x2008, Rd_rvalueRs_SI15, do_ldst_insn}, + {"lw", 0x06000000, 0x3e000007, 0x8000, Rd_rvalueRs_preSI12, do_ldst_insn}, + {"lw", 0x0e000000, 0x3e000007, 0x200a, Rd_rvalueRs_postSI12, do_ldst_insn}, + {"lh", 0x22000000, 0x3e000000, 0x2009, Rd_rvalueRs_SI15, do_ldst_insn}, + {"lh", 0x06000001, 0x3e000007, 0x8000, Rd_rvalueRs_preSI12, do_ldst_insn}, + {"lh", 0x0e000001, 0x3e000007, 0x8000, Rd_rvalueRs_postSI12, do_ldst_insn}, + {"lhu", 0x24000000, 0x3e000000, 0x8000, Rd_rvalueRs_SI15, do_ldst_insn}, + {"lhu", 0x06000002, 0x3e000007, 0x8000, Rd_rvalueRs_preSI12, do_ldst_insn}, + {"lhu", 0x0e000002, 0x3e000007, 0x8000, Rd_rvalueRs_postSI12, do_ldst_insn}, + {"lb", 0x26000000, 0x3e000000, 0x8000, Rd_rvalueRs_SI15, do_ldst_insn}, + {"lb", 0x06000003, 0x3e000007, 0x8000, Rd_rvalueRs_preSI12, do_ldst_insn}, + {"lb", 0x0e000003, 0x3e000007, 0x8000, Rd_rvalueRs_postSI12, do_ldst_insn}, + {"sw", 0x28000000, 0x3e000000, 0x200c, Rd_lvalueRs_SI15, do_ldst_insn}, + {"sw", 0x06000004, 0x3e000007, 0x200e, Rd_lvalueRs_preSI12, do_ldst_insn}, + {"sw", 0x0e000004, 0x3e000007, 0x8000, Rd_lvalueRs_postSI12, do_ldst_insn}, + {"sh", 0x2a000000, 0x3e000000, 0x200d, Rd_lvalueRs_SI15, do_ldst_insn}, + {"sh", 0x06000005, 0x3e000007, 0x8000, Rd_lvalueRs_preSI12, do_ldst_insn}, + {"sh", 0x0e000005, 0x3e000007, 0x8000, Rd_lvalueRs_postSI12, do_ldst_insn}, + {"lbu", 0x2c000000, 0x3e000000, 0x200b, Rd_rvalueRs_SI15, do_ldst_insn}, + {"lbu", 0x06000006, 0x3e000007, 0x8000, Rd_rvalueRs_preSI12, do_ldst_insn}, + {"lbu", 0x0e000006, 0x3e000007, 0x8000, Rd_rvalueRs_postSI12, do_ldst_insn}, + {"sb", 0x2e000000, 0x3e000000, 0x200f, Rd_lvalueRs_SI15, do_ldst_insn}, + {"sb", 0x06000007, 0x3e000007, 0x8000, Rd_lvalueRs_preSI12, do_ldst_insn}, + {"sb", 0x0e000007, 0x3e000007, 0x8000, Rd_lvalueRs_postSI12, do_ldst_insn}, +}; + +static const struct asm_opcode score_insns[] = +{ + {"abs", 0x3800000a, 0x3e007fff, 0x8000, Rd_Rs_x, do_rdrs}, + {"abs.s", 0x3800004b, 0x3e007fff, 0x8000, Rd_Rs_x, do_rdrs}, + {"add", 0x00000010, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"add.c", 0x00000011, 0x3e0003ff, 0x2000, Rd_Rs_Rs, do_rdrsrs}, + {"add.s", 0x38000048, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"addc", 0x00000012, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"addc.c", 0x00000013, 0x3e0003ff, 0x0009, Rd_Rs_Rs, do_rdrsrs}, + {"addi", 0x02000000, 0x3e0e0001, 0x8000, Rd_SI16, do_rdsi16}, + {"addi.c", 0x02000001, 0x3e0e0001, 0x8000, Rd_SI16, do_rdsi16}, + {"addis", 0x0a000000, 0x3e0e0001, 0x8000, Rd_SI16, do_rdi16}, + {"addis.c", 0x0a000001, 0x3e0e0001, 0x8000, Rd_SI16, do_rdi16}, + {"addri", 0x10000000, 0x3e000001, 0x8000, Rd_Rs_SI14, do_rdrssi14}, + {"addri.c", 0x10000001, 0x3e000001, 0x8000, Rd_Rs_SI14, do_rdrssi14}, + {"addc!", 0x0009, 0x700f, 0x00000013, Rd_Rs, do16_rdrs}, + {"add!", 0x2000, 0x700f, 0x00000011, Rd_Rs, do16_rdrs}, + {"addei!", 0x6000 , 0x7087, 0x02000001, Rd_I4, do16_rdi4}, + {"subi", 0x02000000, 0x3e0e0001, 0x8000, Rd_SI16, do_sub_rdsi16}, + {"subi.c", 0x02000001, 0x3e0e0001, 0x8000, Rd_SI16, do_sub_rdsi16}, + {"subri", 0x10000000, 0x3e000001, 0x8000, Rd_Rs_SI14, do_sub_rdrssi14}, + {"subri.c", 0x10000001, 0x3e000001, 0x8000, Rd_Rs_SI14, do_sub_rdrssi14}, + {"and", 0x00000020, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"and.c", 0x00000021, 0x3e0003ff, 0x2004, Rd_Rs_Rs, do_rdrsrs}, + {"andi", 0x02080000, 0x3e0e0001, 0x8000, Rd_I16, do_rdi16}, + {"andi.c", 0x02080001, 0x3e0e0001, 0x8000, Rd_I16, do_rdi16}, + {"andis", 0x0a080000, 0x3e0e0001, 0x8000, Rd_I16, do_rdi16}, + {"andis.c", 0x0a080001, 0x3e0e0001, 0x8000, Rd_I16, do_rdi16}, + {"andri", 0x18000000, 0x3e000001, 0x8000, Rd_Rs_I14, do_rdrsi14}, + {"andri.c", 0x18000001, 0x3e000001, 0x8000, Rd_Rs_I14, do_rdrsi14}, + {"and!", 0x2004, 0x700f, 0x00000021, Rd_Rs, do16_rdrs}, + {"bcs", 0x08000000, 0x3e007c01, 0x4000, PC_DISP19div2, do_branch}, + {"bcc", 0x08000400, 0x3e007c01, 0x4000, PC_DISP19div2, do_branch}, + {"bcnz", 0x08003800, 0x3e007c01, 0x4000, PC_DISP19div2, do_branch}, + {"bcsl", 0x08000001, 0x3e007c01, 0x8000, PC_DISP19div2, do_branch}, + {"bccl", 0x08000401, 0x3e007c01, 0x8000, PC_DISP19div2, do_branch}, + {"bcnzl", 0x08003801, 0x3e007c01, 0x8000, PC_DISP19div2, do_branch}, + {"bcs!", 0x4000, 0x7f00, 0x08000000, PC_DISP8div2, do16_branch}, + {"bcc!", 0x4100, 0x7f00, 0x08000400, PC_DISP8div2, do16_branch}, + {"bcnz!", 0x4e00, 0x7f00, 0x08003800, PC_DISP8div2, do16_branch}, + {"beq", 0x08001000, 0x3e007c01, 0x4000, PC_DISP19div2, do_branch}, + {"beql", 0x08001001, 0x3e007c01, 0x8000, PC_DISP19div2, do_branch}, + {"beq!", 0x4400, 0x7f00, 0x08001000, PC_DISP8div2, do16_branch}, + {"bgtu", 0x08000800, 0x3e007c01, 0x4000, PC_DISP19div2, do_branch}, + {"bgt", 0x08001800, 0x3e007c01, 0x4000, PC_DISP19div2, do_branch}, + {"bge", 0x08002000, 0x3e007c01, 0x4000, PC_DISP19div2, do_branch}, + {"bgtul", 0x08000801, 0x3e007c01, 0x8000, PC_DISP19div2, do_branch}, + {"bgtl", 0x08001801, 0x3e007c01, 0x8000, PC_DISP19div2, do_branch}, + {"bgel", 0x08002001, 0x3e007c01, 0x8000, PC_DISP19div2, do_branch}, + {"bgtu!", 0x4200, 0x7f00, 0x08000800, PC_DISP8div2, do16_branch}, + {"bgt!", 0x4600, 0x7f00, 0x08001800, PC_DISP8div2, do16_branch}, + {"bge!", 0x4800, 0x7f00, 0x08002000, PC_DISP8div2, do16_branch}, + {"bitclr.c", 0x00000029, 0x3e0003ff, 0x6004, Rd_Rs_I5, do_rdrsi5}, + {"bitrev", 0x3800000c, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"bitset.c", 0x0000002b, 0x3e0003ff, 0x6005, Rd_Rs_I5, do_rdrsi5}, + {"bittst.c", 0x0000002d, 0x3e0003ff, 0x6006, x_Rs_I5, do_xrsi5}, + {"bittgl.c", 0x0000002f, 0x3e0003ff, 0x6007, Rd_Rs_I5, do_rdrsi5}, + {"bitclr!", 0x6004, 0x7007, 0x00000029, Rd_I5, do16_rdi5}, + {"bitset!", 0x6005, 0x7007, 0x0000002b, Rd_I5, do16_rdi5}, + {"bittst!", 0x6006, 0x7007, 0x0000002d, Rd_I5, do16_rdi5}, + {"bittgl!", 0x6007, 0x7007, 0x0000002f, Rd_I5, do16_rdi5}, + {"bleu", 0x08000c00, 0x3e007c01, 0x4000, PC_DISP19div2, do_branch}, + {"ble", 0x08001c00, 0x3e007c01, 0x4000, PC_DISP19div2, do_branch}, + {"blt", 0x08002400, 0x3e007c01, 0x4000, PC_DISP19div2, do_branch}, + {"bleul", 0x08000c01, 0x3e007c01, 0x8000, PC_DISP19div2, do_branch}, + {"blel", 0x08001c01, 0x3e007c01, 0x8000, PC_DISP19div2, do_branch}, + {"bltl", 0x08002401, 0x3e007c01, 0x8000, PC_DISP19div2, do_branch}, + {"bl", 0x08003c01, 0x3e007c01, 0x8000, PC_DISP19div2, do_branch}, + {"bleu!", 0x4300, 0x7f00, 0x08000c00, PC_DISP8div2, do16_branch}, + {"ble!", 0x4700, 0x7f00, 0x08001c00, PC_DISP8div2, do16_branch}, + {"blt!", 0x4900, 0x7f00, 0x08002400, PC_DISP8div2, do16_branch}, + {"bmi", 0x08002800, 0x3e007c01, 0x4000, PC_DISP19div2, do_branch}, + {"bmil", 0x08002801, 0x3e007c01, 0x8000, PC_DISP19div2, do_branch}, + {"bmi!", 0x00004a00, 0x00007f00, 0x08002800, PC_DISP8div2, do16_branch}, + {"bne", 0x08001400, 0x3e007c01, 0x4000, PC_DISP19div2, do_branch}, + {"bnel", 0x08001401, 0x3e007c01, 0x8000, PC_DISP19div2, do_branch}, + {"bne!", 0x4500, 0x7f00, 0x08001400, PC_DISP8div2, do16_branch}, + {"bpl", 0x08002c00, 0x3e007c01, 0x4000, PC_DISP19div2, do_branch}, + {"bpll", 0x08002c01, 0x3e007c01, 0x8000, PC_DISP19div2, do_branch}, + {"bpl!", 0x4b00, 0x7f00, 0x08002c00, PC_DISP8div2, do16_branch}, + {"brcs", 0x00000008, 0x3e007fff, 0x0004, x_Rs_x, do_rs}, + {"brcc", 0x00000408, 0x3e007fff, 0x0104, x_Rs_x, do_rs}, + {"brgtu", 0x00000808, 0x3e007fff, 0x0204, x_Rs_x, do_rs}, + {"brleu", 0x00000c08, 0x3e007fff, 0x0304, x_Rs_x, do_rs}, + {"breq", 0x00001008, 0x3e007fff, 0x0404, x_Rs_x, do_rs}, + {"brne", 0x00001408, 0x3e007fff, 0x0504, x_Rs_x, do_rs}, + {"brgt", 0x00001808, 0x3e007fff, 0x0604, x_Rs_x, do_rs}, + {"brle", 0x00001c08, 0x3e007fff, 0x0704, x_Rs_x, do_rs}, + {"brge", 0x00002008, 0x3e007fff, 0x0804, x_Rs_x, do_rs}, + {"brlt", 0x00002408, 0x3e007fff, 0x0904, x_Rs_x, do_rs}, + {"brmi", 0x00002808, 0x3e007fff, 0x0a04, x_Rs_x, do_rs}, + {"brpl", 0x00002c08, 0x3e007fff, 0x0b04, x_Rs_x, do_rs}, + {"brvs", 0x00003008, 0x3e007fff, 0x0c04, x_Rs_x, do_rs}, + {"brvc", 0x00003408, 0x3e007fff, 0x0d04, x_Rs_x, do_rs}, + {"brcnz", 0x00003808, 0x3e007fff, 0x0e04, x_Rs_x, do_rs}, + {"br", 0x00003c08, 0x3e007fff, 0x0f04, x_Rs_x, do_rs}, + {"brcsl", 0x00000009, 0x3e007fff, 0x000c, x_Rs_x, do_rs}, + {"brccl", 0x00000409, 0x3e007fff, 0x010c, x_Rs_x, do_rs}, + {"brgtul", 0x00000809, 0x3e007fff, 0x020c, x_Rs_x, do_rs}, + {"brleul", 0x00000c09, 0x3e007fff, 0x030c, x_Rs_x, do_rs}, + {"breql", 0x00001009, 0x3e007fff, 0x040c, x_Rs_x, do_rs}, + {"brnel", 0x00001409, 0x3e007fff, 0x050c, x_Rs_x, do_rs}, + {"brgtl", 0x00001809, 0x3e007fff, 0x060c, x_Rs_x, do_rs}, + {"brlel", 0x00001c09, 0x3e007fff, 0x070c, x_Rs_x, do_rs}, + {"brgel", 0x00002009, 0x3e007fff, 0x080c, x_Rs_x, do_rs}, + {"brltl", 0x00002409, 0x3e007fff, 0x090c, x_Rs_x, do_rs}, + {"brmil", 0x00002809, 0x3e007fff, 0x0a0c, x_Rs_x, do_rs}, + {"brpll", 0x00002c09, 0x3e007fff, 0x0b0c, x_Rs_x, do_rs}, + {"brvsl", 0x00003009, 0x3e007fff, 0x0c0c, x_Rs_x, do_rs}, + {"brvcl", 0x00003409, 0x3e007fff, 0x0d0c, x_Rs_x, do_rs}, + {"brcnzl", 0x00003809, 0x3e007fff, 0x0e0c, x_Rs_x, do_rs}, + {"brl", 0x00003c09, 0x3e007fff, 0x0f0c, x_Rs_x, do_rs}, + {"brcs!", 0x0004, 0x7f0f, 0x00000008, x_Rs, do16_xrs}, + {"brcc!", 0x0104, 0x7f0f, 0x00000408, x_Rs, do16_xrs}, + {"brgtu!", 0x0204, 0x7f0f, 0x00000808, x_Rs, do16_xrs}, + {"brleu!", 0x0304, 0x7f0f, 0x00000c08, x_Rs, do16_xrs}, + {"breq!", 0x0404, 0x7f0f, 0x00001008, x_Rs, do16_xrs}, + {"brne!", 0x0504, 0x7f0f, 0x00001408, x_Rs, do16_xrs}, + {"brgt!", 0x0604, 0x7f0f, 0x00001808, x_Rs, do16_xrs}, + {"brle!", 0x0704, 0x7f0f, 0x00001c08, x_Rs, do16_xrs}, + {"brge!", 0x0804, 0x7f0f, 0x00002008, x_Rs, do16_xrs}, + {"brlt!", 0x0904, 0x7f0f, 0x00002408, x_Rs, do16_xrs}, + {"brmi!", 0x0a04, 0x7f0f, 0x00002808, x_Rs, do16_xrs}, + {"brpl!", 0x0b04, 0x7f0f, 0x00002c08, x_Rs, do16_xrs}, + {"brvs!", 0x0c04, 0x7f0f, 0x00003008, x_Rs, do16_xrs}, + {"brvc!", 0x0d04, 0x7f0f, 0x00003408, x_Rs, do16_xrs}, + {"brcnz!", 0x0e04, 0x7f0f, 0x00003808, x_Rs, do16_xrs}, + {"br!", 0x0f04, 0x7f0f, 0x00003c08, x_Rs, do16_xrs}, + {"brcsl!", 0x000c, 0x7f0f, 0x00000009, x_Rs, do16_xrs}, + {"brccl!", 0x010c, 0x7f0f, 0x00000409, x_Rs, do16_xrs}, + {"brgtul!", 0x020c, 0x7f0f, 0x00000809, x_Rs, do16_xrs}, + {"brleul!", 0x030c, 0x7f0f, 0x00000c09, x_Rs, do16_xrs}, + {"breql!", 0x040c, 0x7f0f, 0x00001009, x_Rs, do16_xrs}, + {"brnel!", 0x050c, 0x7f0f, 0x00001409, x_Rs, do16_xrs}, + {"brgtl!", 0x060c, 0x7f0f, 0x00001809, x_Rs, do16_xrs}, + {"brlel!", 0x070c, 0x7f0f, 0x00001c09, x_Rs, do16_xrs}, + {"brgel!", 0x080c, 0x7f0f, 0x00002009, x_Rs, do16_xrs}, + {"brltl!", 0x090c, 0x7f0f, 0x00002409, x_Rs, do16_xrs}, + {"brmil!", 0x0a0c, 0x7f0f, 0x00002809, x_Rs, do16_xrs}, + {"brpll!", 0x0b0c, 0x7f0f, 0x00002c09, x_Rs, do16_xrs}, + {"brvsl!", 0x0c0c, 0x7f0f, 0x00003009, x_Rs, do16_xrs}, + {"brvcl!", 0x0d0c, 0x7f0f, 0x00003409, x_Rs, do16_xrs}, + {"brcnzl!", 0x0e0c, 0x7f0f, 0x00003809, x_Rs, do16_xrs}, + {"brl!", 0x0f0c, 0x7f0f, 0x00003c09, x_Rs, do16_xrs}, + {"bvs", 0x08003000, 0x3e007c01, 0x4000, PC_DISP19div2, do_branch}, + {"bvc", 0x08003400, 0x3e007c01, 0x4000, PC_DISP19div2, do_branch}, + {"bvsl", 0x08003001, 0x3e007c01, 0x8000, PC_DISP19div2, do_branch}, + {"bvcl", 0x08003401, 0x3e007c01, 0x8000, PC_DISP19div2, do_branch}, + {"bvs!", 0x4c00, 0x7f00, 0x08003000, PC_DISP8div2, do16_branch}, + {"bvc!", 0x4d00, 0x7f00, 0x08003400, PC_DISP8div2, do16_branch}, + {"b!", 0x4f00, 0x7f00, 0x08003c00, PC_DISP8div2, do16_branch}, + {"b", 0x08003c00, 0x3e007c01, 0x4000, PC_DISP19div2, do_branch}, + {"cache", 0x30000000, 0x3ff00000, 0x8000, OP5_rvalueRs_SI15, do_cache}, + {"ceinst", 0x38000000, 0x3e000000, 0x8000, I5_Rs_Rs_I5_OP5, do_ceinst}, + {"clz", 0x3800000d, 0x3e007fff, 0x8000, Rd_Rs_x, do_rdrs}, + {"cmpteq.c", 0x00000019, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"cmptmi.c", 0x00100019, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"cmp.c", 0x00300019, 0x3ff003ff, 0x2003, x_Rs_Rs, do_rsrs}, + {"cmpzteq.c", 0x0000001b, 0x3ff07fff, 0x8000, x_Rs_x, do_rs}, + {"cmpztmi.c", 0x0010001b, 0x3ff07fff, 0x8000, x_Rs_x, do_rs}, + {"cmpz.c", 0x0030001b, 0x3ff07fff, 0x8000, x_Rs_x, do_rs}, + {"cmpi.c", 0x02040001, 0x3e0e0001, 0x8000, Rd_SI16, do_rdsi16}, + {"cmp!", 0x2003, 0x700f, 0x00300019, Rd_Rs, do16_rdrs}, + {"cop1", 0x0c00000c, 0x3e00001f, 0x8000, Rd_Rs_Rs_imm, do_crdcrscrsimm5}, + {"cop2", 0x0c000014, 0x3e00001f, 0x8000, Rd_Rs_Rs_imm, do_crdcrscrsimm5}, + {"cop3", 0x0c00001c, 0x3e00001f, 0x8000, Rd_Rs_Rs_imm, do_crdcrscrsimm5}, + {"drte", 0x0c0000a4, 0x3e0003ff, 0x8000, NO_OPD, do_empty}, + {"extsb", 0x00000058, 0x3e0003ff, 0x8000, Rd_Rs_x, do_rdrs}, + {"extsb.c", 0x00000059, 0x3e0003ff, 0x8000, Rd_Rs_x, do_rdrs}, + {"extsh", 0x0000005a, 0x3e0003ff, 0x8000, Rd_Rs_x, do_rdrs}, + {"extsh.c", 0x0000005b, 0x3e0003ff, 0x8000, Rd_Rs_x, do_rdrs}, + {"extzb", 0x0000005c, 0x3e0003ff, 0x8000, Rd_Rs_x, do_rdrs}, + {"extzb.c", 0x0000005d, 0x3e0003ff, 0x8000, Rd_Rs_x, do_rdrs}, + {"extzh", 0x0000005e, 0x3e0003ff, 0x8000, Rd_Rs_x, do_rdrs}, + {"extzh.c", 0x0000005f, 0x3e0003ff, 0x8000, Rd_Rs_x, do_rdrs}, + {"jl", 0x04000001, 0x3e000001, 0x8000, PC_DISP24div2, do_jump}, + {"jl!", 0x3001, 0x7001, 0x04000001, PC_DISP11div2, do16_jump}, + {"j!", 0x3000, 0x7001, 0x04000000, PC_DISP11div2, do16_jump}, + {"j", 0x04000000, 0x3e000001, 0x8000, PC_DISP24div2, do_jump}, + {"lbu!", 0x200b, 0x0000700f, 0x2c000000, Rd_rvalueRs, do16_ldst_insn}, + {"lbup!", 0x7003, 0x7007, 0x2c000000, Rd_rvalueBP_I5, do16_ldst_imm_insn}, + {"alw", 0x0000000c, 0x3e0003ff, 0x8000, Rd_rvalue32Rs, do_ldst_atomic}, + {"lcb", 0x00000060, 0x3e0003ff, 0x8000, x_rvalueRs_post4, do_ldst_unalign}, + {"lcw", 0x00000062, 0x3e0003ff, 0x8000, Rd_rvalueRs_post4, do_ldst_unalign}, + {"lce", 0x00000066, 0x3e0003ff, 0x8000, Rd_rvalueRs_post4, do_ldst_unalign}, + {"ldc1", 0x0c00000a, 0x3e00001f, 0x8000, Rd_rvalueRs_SI10, do_ldst_cop}, + {"ldc2", 0x0c000012, 0x3e00001f, 0x8000, Rd_rvalueRs_SI10, do_ldst_cop}, + {"ldc3", 0x0c00001a, 0x3e00001f, 0x8000, Rd_rvalueRs_SI10, do_ldst_cop}, + {"lh!", 0x2009, 0x700f, 0x22000000, Rd_rvalueRs, do16_ldst_insn}, + {"lhp!", 0x7001, 0x7007, 0x22000000, Rd_rvalueBP_I5, do16_ldst_imm_insn}, + {"ldi", 0x020c0000, 0x3e0e0000, 0x5000, Rd_SI16, do_rdsi16}, + {"ldis", 0x0a0c0000, 0x3e0e0000, 0x8000, Rd_I16, do_rdi16}, + {"ldiu!", 0x5000, 0x7000, 0x020c0000, Rd_I8, do16_ldst_imm_insn}, + {"lw!", 0x2008, 0x700f, 0x20000000, Rd_rvalueRs, do16_ldst_insn}, + {"lwp!", 0x7000, 0x7007, 0x20000000, Rd_rvalueBP_I5, do16_ldst_imm_insn}, + {"mfcel", 0x00000448, 0x3e007fff, 0x8000, Rd_x_x, do_rd}, + {"mfcel!", 0x1001, 0x7f0f, 0x00000448, x_Rs, do16_rs}, + {"mad", 0x38000000, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"mad.f!", 0x1004, 0x700f, 0x38000080, Rd_Rs, do16_rdrs}, + {"madh", 0x38000203, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"madh.fs", 0x380002c3, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"madh.fs!", 0x100b, 0x700f, 0x380002c3, Rd_Rs, do16_rdrs}, + {"madl", 0x38000002, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"madl.fs", 0x380000c2, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"madl.fs!", 0x100a, 0x700f, 0x380000c2, Rd_Rs, do16_rdrs}, + {"madu", 0x38000020, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"madu!", 0x1005, 0x700f, 0x38000020, Rd_Rs, do16_rdrs}, + {"mad.f", 0x38000080, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"max", 0x38000007, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"mazh", 0x38000303, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"mazh.f", 0x38000383, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"mazh.f!", 0x1009, 0x700f, 0x3800038c, Rd_Rs, do16_rdrs}, + {"mazl", 0x38000102, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"mazl.f", 0x38000182, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"mazl.f!", 0x1008, 0x700f, 0x38000182, Rd_Rs, do16_rdrs}, + {"mfceh", 0x00000848, 0x3e007fff, 0x8000, Rd_x_x, do_rd}, + {"mfceh!", 0x1101, 0x7f0f, 0x00000848, x_Rs, do16_rs}, + {"mfcehl", 0x00000c48, 0x3e007fff, 0x8000, Rd_Rs_x, do_rdrs}, + {"mfsr", 0x00000050, 0x3e0003ff, 0x8000, Rd_x_I5, do_rdsrs}, + {"mfcr", 0x0c000001, 0x3e00001f, 0x8000, Rd_Rs_x, do_rdcrs}, + {"mfc1", 0x0c000009, 0x3e00001f, 0x8000, Rd_Rs_x, do_rdcrs}, + {"mfc2", 0x0c000011, 0x3e00001f, 0x8000, Rd_Rs_x, do_rdcrs}, + {"mfc3", 0x0c000019, 0x3e00001f, 0x8000, Rd_Rs_x, do_rdcrs}, + {"mfcc1", 0x0c00000f, 0x3e00001f, 0x8000, Rd_Rs_x, do_rdcrs}, + {"mfcc2", 0x0c000017, 0x3e00001f, 0x8000, Rd_Rs_x, do_rdcrs}, + {"mfcc3", 0x0c00001f, 0x3e00001f, 0x8000, Rd_Rs_x, do_rdcrs}, + {"mhfl!", 0x0002, 0x700f, 0x00003c56, Rd_LowRs, do16_hrdrs}, + {"min", 0x38000006, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"mlfh!", 0x0001, 0x700f, 0x00003c56, Rd_HighRs, do16_rdhrs}, + {"msb", 0x38000001, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"msb.f!", 0x1006, 0x700f, 0x38000081, Rd_Rs, do16_rdrs}, + {"msbh", 0x38000205, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"msbh.fs", 0x380002c5, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"msbh.fs!", 0x100f, 0x700f, 0x380002c5, Rd_Rs, do16_rdrs}, + {"msbl", 0x38000004, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"msbl.fs", 0x380000c4, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"msbl.fs!", 0x100e, 0x700f, 0x380000c4, Rd_Rs, do16_rdrs}, + {"msbu", 0x38000021, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"msbu!", 0x1007, 0x700f, 0x38000021, Rd_Rs, do16_rdrs}, + {"msb.f", 0x38000081, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"mszh", 0x38000305, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"mszh.f", 0x38000385, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"mszh.f!", 0x100d, 0x700f, 0x38000385, Rd_Rs, do16_rdrs}, + {"mszl", 0x38000104, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"mszl.f", 0x38000184, 0x3ff003ff, 0x8000, x_Rs_Rs, do_rsrs}, + {"mszl.f!", 0x100c, 0x700f, 0x38000184, Rd_Rs, do16_rdrs}, + {"mtcel!", 0x1000, 0x7f0f, 0x0000044a, x_Rs, do16_rs}, + {"mtcel", 0x0000044a, 0x3e007fff, 0x8000, Rd_x_x, do_rd}, + {"mtceh", 0x0000084a, 0x3e007fff, 0x8000, Rd_x_x, do_rd}, + {"mtceh!", 0x1100, 0x7f0f, 0x0000084a, x_Rs, do16_rs}, + {"mtcehl", 0x00000c4a, 0x3e007fff, 0x8000, Rd_Rs_x, do_rdrs}, + {"mtsr", 0x00000052, 0x3e0003ff, 0x8000, x_Rs_I5, do_rdsrs}, + {"mtcr", 0x0c000000, 0x3e00001f, 0x8000, Rd_Rs_x, do_rdcrs}, + {"mtc1", 0x0c000008, 0x3e00001f, 0x8000, Rd_Rs_x, do_rdcrs}, + {"mtc2", 0x0c000010, 0x3e00001f, 0x8000, Rd_Rs_x, do_rdcrs}, + {"mtc3", 0x0c000018, 0x3e00001f, 0x8000, Rd_Rs_x, do_rdcrs}, + {"mtcc1", 0x0c00000e, 0x3e00001f, 0x8000, Rd_Rs_x, do_rdcrs}, + {"mtcc2", 0x0c000016, 0x3e00001f, 0x8000, Rd_Rs_x, do_rdcrs}, + {"mtcc3", 0x0c00001e, 0x3e00001f, 0x8000, Rd_Rs_x, do_rdcrs}, + {"mul.f!", 0x1002, 0x700f, 0x00000041, Rd_Rs, do16_rdrs}, + {"mulu!", 0x1003, 0x700f, 0x00000042, Rd_Rs, do16_rdrs}, + {"mvcs", 0x00000056, 0x3e007fff, 0x8000, Rd_Rs_x, do_rdrs}, + {"mvcc", 0x00000456, 0x3e007fff, 0x8000, Rd_Rs_x, do_rdrs}, + {"mvgtu", 0x00000856, 0x3e007fff, 0x8000, Rd_Rs_x, do_rdrs}, + {"mvleu", 0x00000c56, 0x3e007fff, 0x8000, Rd_Rs_x, do_rdrs}, + {"mveq", 0x00001056, 0x3e007fff, 0x8000, Rd_Rs_x, do_rdrs}, + {"mvne", 0x00001456, 0x3e007fff, 0x8000, Rd_Rs_x, do_rdrs}, + {"mvgt", 0x00001856, 0x3e007fff, 0x8000, Rd_Rs_x, do_rdrs}, + {"mvle", 0x00001c56, 0x3e007fff, 0x8000, Rd_Rs_x, do_rdrs}, + {"mvge", 0x00002056, 0x3e007fff, 0x8000, Rd_Rs_x, do_rdrs}, + {"mvlt", 0x00002456, 0x3e007fff, 0x8000, Rd_Rs_x, do_rdrs}, + {"mvmi", 0x00002856, 0x3e007fff, 0x8000, Rd_Rs_x, do_rdrs}, + {"mvpl", 0x00002c56, 0x3e007fff, 0x8000, Rd_Rs_x, do_rdrs}, + {"mvvs", 0x00003056, 0x3e007fff, 0x8000, Rd_Rs_x, do_rdrs}, + {"mvvc", 0x00003456, 0x3e007fff, 0x8000, Rd_Rs_x, do_rdrs}, + {"mv", 0x00003c56, 0x3e007fff, 0x0003, Rd_Rs_x, do_rdrs}, + {"mv!", 0x0003, 0x700f, 0x00003c56, Rd_Rs, do16_mv_rdrs}, + {"neg", 0x0000001e, 0x3e0003ff, 0x8000, Rd_x_Rs, do_rdxrs}, + {"neg.c", 0x0000001f, 0x3e0003ff, 0x2002, Rd_x_Rs, do_rdxrs}, + {"neg!", 0x2002, 0x700f, 0x0000001f, Rd_Rs, do16_rdrs}, + {"nop", 0x00000000, 0x3e0003ff, 0x0000, NO_OPD, do_empty}, + {"not", 0x00000024, 0x3e0003ff, 0x8000, Rd_Rs_x, do_rdrs}, + {"not.c", 0x00000025, 0x3e0003ff, 0x2006, Rd_Rs_x, do_rdrs}, + {"nop!", 0x0000, 0x700f, 0x00000000, NO16_OPD, do_empty}, + {"not!", 0x2006, 0x700f, 0x00000025, Rd_Rs, do16_rdrs}, + {"or", 0x00000022, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"or.c", 0x00000023, 0x3e0003ff, 0x2005, Rd_Rs_Rs, do_rdrsrs}, + {"ori", 0x020a0000, 0x3e0e0001, 0x8000, Rd_I16, do_rdi16}, + {"ori.c", 0x020a0001, 0x3e0e0001, 0x8000, Rd_I16, do_rdi16}, + {"oris", 0x0a0a0000, 0x3e0e0001, 0x8000, Rd_I16, do_rdi16}, + {"oris.c", 0x0a0a0001, 0x3e0e0001, 0x8000, Rd_I16, do_rdi16}, + {"orri", 0x1a000000, 0x3e000001, 0x8000, Rd_Rs_I14, do_rdrsi14}, + {"orri.c", 0x1a000001, 0x3e000001, 0x8000, Rd_Rs_I14, do_rdrsi14}, + {"or!", 0x2005, 0x700f, 0x00000023, Rd_Rs, do16_rdrs}, + {"pflush", 0x0000000a, 0x3e0003ff, 0x8000, NO_OPD, do_empty}, + {"pop!", 0x200a, 0x700f, 0x0e000000, Rd_rvalueRs, do16_push_pop}, + {"push!", 0x200e, 0x700f, 0x06000004, Rd_lvalueRs, do16_push_pop}, + {"ror", 0x00000038, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"ror.c", 0x00000039, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"rorc.c", 0x0000003b, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"rol", 0x0000003c, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"rol.c", 0x0000003d, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"rolc.c", 0x0000003f, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"rori", 0x00000078, 0x3e0003ff, 0x8000, Rd_Rs_I5, do_rdrsi5}, + {"rori.c", 0x00000079, 0x3e0003ff, 0x8000, Rd_Rs_I5, do_rdrsi5}, + {"roric.c", 0x0000007b, 0x3e0003ff, 0x8000, Rd_Rs_I5, do_rdrsi5}, + {"roli", 0x0000007c, 0x3e0003ff, 0x8000, Rd_Rs_I5, do_rdrsi5}, + {"roli.c", 0x0000007d, 0x3e0003ff, 0x8000, Rd_Rs_I5, do_rdrsi5}, + {"rolic.c", 0x0000007f, 0x3e0003ff, 0x8000, Rd_Rs_I5, do_rdrsi5}, + {"rte", 0x0c000084, 0x3e0003ff, 0x8000, NO_OPD, do_empty}, + {"sb!", 0x200f, 0x700f, 0x2e000000, Rd_lvalueRs, do16_ldst_insn}, + {"sbp!", 0x7007, 0x7007, 0x2e000000, Rd_lvalueBP_I5, do16_ldst_imm_insn}, + {"asw", 0x0000000e, 0x3e0003ff, 0x8000, Rd_lvalue32Rs, do_ldst_atomic}, + {"scb", 0x00000068, 0x3e0003ff, 0x8000, Rd_lvalueRs_post4, do_ldst_unalign}, + {"scw", 0x0000006a, 0x3e0003ff, 0x8000, Rd_lvalueRs_post4, do_ldst_unalign}, + {"sce", 0x0000006e, 0x3e0003ff, 0x8000, x_lvalueRs_post4, do_ldst_unalign}, + {"sdbbp", 0x00000006, 0x3e0003ff, 0x6002, x_I5_x, do_xi5x}, + {"sdbbp!", 0x6002, 0x7007, 0x00000006, Rd_I5, do16_xi5}, + {"sh!", 0x200d, 0x700f, 0x2a000000, Rd_lvalueRs, do16_ldst_insn}, + {"shp!", 0x7005, 0x7007, 0x2a000000, Rd_lvalueBP_I5, do16_ldst_imm_insn}, + {"sleep", 0x0c0000c4, 0x3e0003ff, 0x8000, NO_OPD, do_empty}, + {"sll", 0x00000030, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"sll.c", 0x00000031, 0x3e0003ff, 0x0008, Rd_Rs_Rs, do_rdrsrs}, + {"sll.s", 0x3800004e, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"slli", 0x00000070, 0x3e0003ff, 0x8000, Rd_Rs_I5, do_rdrsi5}, + {"slli.c", 0x00000071, 0x3e0003ff, 0x6001, Rd_Rs_I5, do_rdrsi5}, + {"sll!", 0x0008, 0x700f, 0x00000031, Rd_Rs, do16_rdrs}, + {"slli!", 0x6001, 0x7007, 0x00000071, Rd_I5, do16_rdi5}, + {"srl", 0x00000034, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"srl.c", 0x00000035, 0x3e0003ff, 0x000a, Rd_Rs_Rs, do_rdrsrs}, + {"sra", 0x00000036, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"sra.c", 0x00000037, 0x3e0003ff, 0x000b, Rd_Rs_Rs, do_rdrsrs}, + {"srli", 0x00000074, 0x3e0003ff, 0x8000, Rd_Rs_I5, do_rdrsi5}, + {"srli.c", 0x00000075, 0x3e0003ff, 0x6003, Rd_Rs_I5, do_rdrsi5}, + {"srai", 0x00000076, 0x3e0003ff, 0x8000, Rd_Rs_I5, do_rdrsi5}, + {"srai.c", 0x00000077, 0x3e0003ff, 0x8000, Rd_Rs_I5, do_rdrsi5}, + {"srl!", 0x000a, 0x700f, 0x00000035, Rd_Rs, do16_rdrs}, + {"sra!", 0x000b, 0x700f, 0x00000037, Rd_Rs, do16_rdrs}, + {"srli!", 0x6003, 0x7007, 0x00000075, Rd_Rs, do16_rdi5}, + {"stc1", 0x0c00000b, 0x3e00001f, 0x8000, Rd_lvalueRs_SI10, do_ldst_cop}, + {"stc2", 0x0c000013, 0x3e00001f, 0x8000, Rd_lvalueRs_SI10, do_ldst_cop}, + {"stc3", 0x0c00001b, 0x3e00001f, 0x8000, Rd_lvalueRs_SI10, do_ldst_cop}, + {"sub", 0x00000014, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"sub.c", 0x00000015, 0x3e0003ff, 0x2001, Rd_Rs_Rs, do_rdrsrs}, + {"sub.s", 0x38000049, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"subc", 0x00000016, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"subc.c", 0x00000017, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"sub!", 0x2001, 0x700f, 0x00000015, Rd_Rs, do16_rdrs}, + {"subei!", 0x6080, 0x7087, 0x02000001, Rd_I4, do16_rdi4}, + {"sw!", 0x200c, 0x700f, 0x28000000, Rd_lvalueRs, do16_ldst_insn}, + {"swp!", 0x7004, 0x7007, 0x28000000, Rd_lvalueBP_I5, do16_ldst_imm_insn}, + {"syscall", 0x00000002, 0x3e0003ff, 0x8000, I15, do_i15}, + {"tcs", 0x00000054, 0x3e007fff, 0x0005, NO_OPD, do_empty}, + {"tcc", 0x00000454, 0x3e007fff, 0x0105, NO_OPD, do_empty}, + {"tcnz", 0x00003854, 0x3e007fff, 0x0e05, NO_OPD, do_empty}, + {"tcs!", 0x0005, 0x7f0f, 0x00000054, NO16_OPD, do_empty}, + {"tcc!", 0x0105, 0x7f0f, 0x00000454, NO16_OPD, do_empty}, + {"tcnz!", 0x0e05, 0x7f0f, 0x00003854, NO16_OPD, do_empty}, + {"teq", 0x00001054, 0x3e007fff, 0x0405, NO_OPD, do_empty}, + {"teq!", 0x0405, 0x7f0f, 0x00001054, NO16_OPD, do_empty}, + {"tgtu", 0x00000854, 0x3e007fff, 0x0205, NO_OPD, do_empty}, + {"tgt", 0x00001854, 0x3e007fff, 0x0605, NO_OPD, do_empty}, + {"tge", 0x00002054, 0x3e007fff, 0x0805, NO_OPD, do_empty}, + {"tgtu!", 0x0205, 0x7f0f, 0x00000854, NO16_OPD, do_empty}, + {"tgt!", 0x0605, 0x7f0f, 0x00001854, NO16_OPD, do_empty}, + {"tge!", 0x0805, 0x7f0f, 0x00002054, NO16_OPD, do_empty}, + {"tleu", 0x00000c54, 0x3e007fff, 0x0305, NO_OPD, do_empty}, + {"tle", 0x00001c54, 0x3e007fff, 0x0705, NO_OPD, do_empty}, + {"tlt", 0x00002454, 0x3e007fff, 0x0905, NO_OPD, do_empty}, + {"stlb", 0x0c000004, 0x3e0003ff, 0x8000, NO_OPD, do_empty}, + {"mftlb", 0x0c000024, 0x3e0003ff, 0x8000, NO_OPD, do_empty}, + {"mtptlb", 0x0c000044, 0x3e0003ff, 0x8000, NO_OPD, do_empty}, + {"mtrtlb", 0x0c000064, 0x3e0003ff, 0x8000, NO_OPD, do_empty}, + {"tleu!", 0x0305, 0x7f0f, 0x00000c54, NO16_OPD, do_empty}, + {"tle!", 0x0705, 0x7f0f, 0x00001c54, NO16_OPD, do_empty}, + {"tlt!", 0x0905, 0x7f0f, 0x00002454, NO16_OPD, do_empty}, + {"tmi", 0x00002854, 0x3e007fff, 0x0a05, NO_OPD, do_empty}, + {"tmi!", 0x0a05, 0x7f0f, 0x00002854, NO16_OPD, do_empty}, + {"tne", 0x00001454, 0x3e007fff, 0x0505, NO_OPD, do_empty}, + {"tne!", 0x0505, 0x7f0f, 0x00001454, NO16_OPD, do_empty}, + {"tpl", 0x00002c54, 0x3e007fff, 0x0b05, NO_OPD, do_empty}, + {"tpl!", 0x0b05, 0x7f0f, 0x00002c54, NO16_OPD, do_empty}, + {"trapcs", 0x00000004, 0x3e007fff, 0x8000, x_I5_x, do_xi5x}, + {"trapcc", 0x00000404, 0x3e007fff, 0x8000, x_I5_x, do_xi5x}, + {"trapgtu", 0x00000804, 0x3e007fff, 0x8000, x_I5_x, do_xi5x}, + {"trapleu", 0x00000c04, 0x3e007fff, 0x8000, x_I5_x, do_xi5x}, + {"trapeq", 0x00001004, 0x3e007fff, 0x8000, x_I5_x, do_xi5x}, + {"trapne", 0x00001404, 0x3e007fff, 0x8000, x_I5_x, do_xi5x}, + {"trapgt", 0x00001804, 0x3e007fff, 0x8000, x_I5_x, do_xi5x}, + {"traple", 0x00001c04, 0x3e007fff, 0x8000, x_I5_x, do_xi5x}, + {"trapge", 0x00002004, 0x3e007fff, 0x8000, x_I5_x, do_xi5x}, + {"traplt", 0x00002404, 0x3e007fff, 0x8000, x_I5_x, do_xi5x}, + {"trapmi", 0x00002804, 0x3e007fff, 0x8000, x_I5_x, do_xi5x}, + {"trappl", 0x00002c04, 0x3e007fff, 0x8000, x_I5_x, do_xi5x}, + {"trapvs", 0x00003004, 0x3e007fff, 0x8000, x_I5_x, do_xi5x}, + {"trapvc", 0x00003404, 0x3e007fff, 0x8000, x_I5_x, do_xi5x}, + {"trap", 0x00003c04, 0x3e007fff, 0x8000, x_I5_x, do_xi5x}, + {"tset", 0x00003c54, 0x3e007fff, 0x0f05, NO_OPD, do_empty}, + {"tset!", 0x0f05, 0x00007f0f, 0x00003c54, NO16_OPD, do_empty}, + {"tvs", 0x00003054, 0x3e007fff, 0x0c05, NO_OPD, do_empty}, + {"tvc", 0x00003454, 0x3e007fff, 0x0d05, NO_OPD, do_empty}, + {"tvs!", 0x0c05, 0x7f0f, 0x00003054, NO16_OPD, do_empty}, + {"tvc!", 0x0d05, 0x7f0f, 0x00003454, NO16_OPD, do_empty}, + {"xor", 0x00000026, 0x3e0003ff, 0x8000, Rd_Rs_Rs, do_rdrsrs}, + {"xor.c", 0x00000027, 0x3e0003ff, 0x2007, Rd_Rs_Rs, do_rdrsrs}, + {"xor!", 0x2007, 0x700f, 0x00000027, Rd_Rs, do16_rdrs}, + /* Macro instruction. */ + {"li", 0x020c0000, 0x3e0e0000, 0x8000, Insn_Type_SYN, do_macro_li_rdi32}, + /* la reg, imm32 -->(1) ldi reg, simm16 + (2) ldis reg, %HI(imm32) + ori reg, %LO(imm32) + + la reg, symbol -->(1) lis reg, %HI(imm32) + ori reg, %LO(imm32) */ + {"la", 0x020c0000, 0x3e0e0000, 0x8000, Insn_Type_SYN, do_macro_la_rdi32}, + {"div", 0x00000044, 0x3e0003ff, 0x8000, Insn_Type_SYN, do_macro_mul_rdrsrs}, + {"divu", 0x00000046, 0x3e0003ff, 0x8000, Insn_Type_SYN, do_macro_mul_rdrsrs}, + {"rem", 0x00000044, 0x3e0003ff, 0x8000, Insn_Type_SYN, do_macro_mul_rdrsrs}, + {"remu", 0x00000046, 0x3e0003ff, 0x8000, Insn_Type_SYN, do_macro_mul_rdrsrs}, + {"mul", 0x00000040, 0x3e0003ff, 0x8000, Insn_Type_SYN, do_macro_mul_rdrsrs}, + {"mulu", 0x00000042, 0x3e0003ff, 0x8000, Insn_Type_SYN, do_macro_mul_rdrsrs}, + {"maz", 0x00000040, 0x3e0003ff, 0x8000, Insn_Type_SYN, do_macro_mul_rdrsrs}, + {"mazu", 0x00000042, 0x3e0003ff, 0x8000, Insn_Type_SYN, do_macro_mul_rdrsrs}, + {"mul.f", 0x00000041, 0x3e0003ff, 0x8000, Insn_Type_SYN, do_macro_mul_rdrsrs}, + {"maz.f", 0x00000041, 0x3e0003ff, 0x8000, Insn_Type_SYN, do_macro_mul_rdrsrs}, + {"lb", INSN_LB, 0x00000000, 0x8000, Insn_Type_SYN, do_macro_ldst_label}, + {"lbu", INSN_LBU, 0x00000000, 0x200b, Insn_Type_SYN, do_macro_ldst_label}, + {"lh", INSN_LH, 0x00000000, 0x2009, Insn_Type_SYN, do_macro_ldst_label}, + {"lhu", INSN_LHU, 0x00000000, 0x8000, Insn_Type_SYN, do_macro_ldst_label}, + {"lw", INSN_LW, 0x00000000, 0x2008, Insn_Type_SYN, do_macro_ldst_label}, + {"sb", INSN_SB, 0x00000000, 0x200f, Insn_Type_SYN, do_macro_ldst_label}, + {"sh", INSN_SH, 0x00000000, 0x200d, Insn_Type_SYN, do_macro_ldst_label}, + {"sw", INSN_SW, 0x00000000, 0x200c, Insn_Type_SYN, do_macro_ldst_label}, + /* Assembler use internal. */ + {"ld_i32hi", 0x0a0c0000, 0x3e0e0000, 0x8000, Insn_internal, do_macro_rdi32hi}, + {"ld_i32lo", 0x020a0000, 0x3e0e0001, 0x8000, Insn_internal, do_macro_rdi32lo}, + {"ldis_pic", 0x0a0c0000, 0x3e0e0000, 0x5000, Insn_internal, do_rdi16_pic}, + {"addi_s_pic",0x02000000, 0x3e0e0001, 0x8000, Insn_internal, do_addi_s_pic}, + {"addi_u_pic",0x02000000, 0x3e0e0001, 0x8000, Insn_internal, do_addi_u_pic}, + {"lw_pic", 0x20000000, 0x3e000000, 0x8000, Insn_internal, do_lw_pic}, +}; + +/* Next free entry in the pool. */ +int next_literal_pool_place = 0; + +/* Next literal pool number. */ +int lit_pool_num = 1; +symbolS *current_poolP = NULL; + + +static int +end_of_line (char *str) +{ + int retval = SUCCESS; + + skip_whitespace (str); + if (*str != '\0') + { + retval = (int) FAIL; + + if (!inst.error) + inst.error = BAD_GARBAGE; + } + + return retval; +} + +static int +score_reg_parse (char **ccp, struct hash_control *htab) +{ + char *start = *ccp; + char c; + char *p; + struct reg_entry *reg; + + p = start; + if (!ISALPHA (*p) || !is_name_beginner (*p)) + return (int) FAIL; + + c = *p++; + + while (ISALPHA (c) || ISDIGIT (c) || c == '_') + c = *p++; + + *--p = 0; + reg = (struct reg_entry *) hash_find (htab, start); + *p = c; + + if (reg) + { + *ccp = p; + return reg->number; + } + return (int) FAIL; +} + +/* If shift <= 0, only return reg. */ + +static int +reg_required_here (char **str, int shift, enum score_reg_type reg_type) +{ + static char buff[MAX_LITERAL_POOL_SIZE]; + int reg = (int) FAIL; + char *start = *str; + + if ((reg = score_reg_parse (str, all_reg_maps[reg_type].htab)) != (int) FAIL) + { + if (reg_type == REG_TYPE_SCORE) + { + if ((reg == 1) && (nor1 == 1) && (inst.bwarn == 0)) + { + as_warn (_("Using temp register(r1)")); + inst.bwarn = 1; + } + } + if (shift >= 0) + { + if (reg_type == REG_TYPE_SCORE_CR) + strcpy (inst.reg, score_crn_table[reg].name); + else if (reg_type == REG_TYPE_SCORE_SR) + strcpy (inst.reg, score_srn_table[reg].name); + else + strcpy (inst.reg, ""); + + inst.instruction |= reg << shift; + } + } + else + { + *str = start; + sprintf (buff, _("register expected, not '%.100s'"), start); + inst.error = buff; + } + + return reg; +} + +static int +skip_past_comma (char **str) +{ + char *p = *str; + char c; + int comma = 0; + + while ((c = *p) == ' ' || c == ',') + { + p++; + if (c == ',' && comma++) + { + inst.error = BAD_SKIP_COMMA; + return (int) FAIL; + } + } + + if ((c == '\0') || (comma == 0)) + { + inst.error = BAD_SKIP_COMMA; + return (int) FAIL; + } + + *str = p; + return comma ? SUCCESS : (int) FAIL; +} + +static void +do_rdrsrs (char *str) +{ + skip_whitespace (str); + + if (reg_required_here (&str, 20, REG_TYPE_SCORE) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL + || reg_required_here (&str, 15, REG_TYPE_SCORE) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL + || reg_required_here (&str, 10, REG_TYPE_SCORE) == (int) FAIL + || end_of_line (str) == (int) FAIL) + { + return; + } + else + { + if ((((inst.instruction >> 15) & 0x10) == 0) + && (((inst.instruction >> 10) & 0x10) == 0) + && (((inst.instruction >> 20) & 0x10) == 0) + && (inst.relax_inst != 0x8000) + && (((inst.instruction >> 20) & 0xf) == ((inst.instruction >> 15) & 0xf))) + { + inst.relax_inst |= (((inst.instruction >> 10) & 0xf) << 4) + | (((inst.instruction >> 15) & 0xf) << 8); + inst.relax_size = 2; + } + else + { + inst.relax_inst = 0x8000; + } + } +} + +static int +walk_no_bignums (symbolS * sp) +{ + if (symbol_get_value_expression (sp)->X_op == O_big) + return 1; + + if (symbol_get_value_expression (sp)->X_add_symbol) + return (walk_no_bignums (symbol_get_value_expression (sp)->X_add_symbol) + || (symbol_get_value_expression (sp)->X_op_symbol + && walk_no_bignums (symbol_get_value_expression (sp)->X_op_symbol))); + + return 0; +} + +static int +my_get_expression (expressionS * ep, char **str) +{ + char *save_in; + segT seg; + + save_in = input_line_pointer; + input_line_pointer = *str; + in_my_get_expression = 1; + seg = expression (ep); + in_my_get_expression = 0; + + if (ep->X_op == O_illegal) + { + *str = input_line_pointer; + input_line_pointer = save_in; + inst.error = _("illegal expression"); + return (int) FAIL; + } + /* Get rid of any bignums now, so that we don't generate an error for which + we can't establish a line number later on. Big numbers are never valid + in instructions, which is where this routine is always called. */ + if (ep->X_op == O_big + || (ep->X_add_symbol + && (walk_no_bignums (ep->X_add_symbol) + || (ep->X_op_symbol && walk_no_bignums (ep->X_op_symbol))))) + { + inst.error = _("invalid constant"); + *str = input_line_pointer; + input_line_pointer = save_in; + return (int) FAIL; + } + + if ((ep->X_add_symbol != NULL) + && (inst.type != PC_DISP19div2) + && (inst.type != PC_DISP8div2) + && (inst.type != PC_DISP24div2) + && (inst.type != PC_DISP11div2) + && (inst.type != Insn_Type_SYN) + && (inst.type != Rd_rvalueRs_SI15) + && (inst.type != Rd_lvalueRs_SI15) + && (inst.type != Insn_internal)) + { + inst.error = BAD_ARGS; + *str = input_line_pointer; + input_line_pointer = save_in; + return (int) FAIL; + } + + *str = input_line_pointer; + input_line_pointer = save_in; + return SUCCESS; +} + +/* Check if an immediate is valid. If so, convert it to the right format. */ + +static int +validate_immediate (int val, unsigned int data_type, int hex_p) +{ + switch (data_type) + { + case _VALUE_HI16: + { + int val_hi = ((val & 0xffff0000) >> 16); + + if (score_df_range[data_type].range[0] <= val_hi + && val_hi <= score_df_range[data_type].range[1]) + return val_hi; + } + break; + + case _VALUE_LO16: + { + int val_lo = (val & 0xffff); + + if (score_df_range[data_type].range[0] <= val_lo + && val_lo <= score_df_range[data_type].range[1]) + return val_lo; + } + break; + + case _VALUE: + return val; + break; + + case _SIMM14: + if (hex_p == 1) + { + if (!(val >= -0x2000 && val <= 0x3fff)) + { + return (int) FAIL; + } + } + else + { + if (!(val >= -8192 && val <= 8191)) + { + return (int) FAIL; + } + } + + return val; + break; + + case _SIMM16_NEG: + if (hex_p == 1) + { + if (!(val >= -0x7fff && val <= 0xffff && val != 0x8000)) + { + return (int) FAIL; + } + } + else + { + if (!(val >= -32767 && val <= 32768)) + { + return (int) FAIL; + } + } + + val = -val; + return val; + break; + + default: + if (data_type == _SIMM14_NEG || data_type == _IMM16_NEG) + val = -val; + + if (score_df_range[data_type].range[0] <= val + && val <= score_df_range[data_type].range[1]) + return val; + + break; + } + + return (int) FAIL; +} + +static int +data_op2 (char **str, int shift, enum score_data_type data_type) +{ + int value; + char data_exp[MAX_LITERAL_POOL_SIZE]; + char *dataptr; + int cnt = 0; + char *pp = NULL; + + skip_whitespace (*str); + inst.error = NULL; + dataptr = * str; + + /* Set hex_p to zero. */ + int hex_p = 0; + + while ((*dataptr != '\0') && (*dataptr != '|') && (cnt <= MAX_LITERAL_POOL_SIZE)) /* 0x7c = ='|' */ + { + data_exp[cnt] = *dataptr; + dataptr++; + cnt++; + } + + data_exp[cnt] = '\0'; + pp = (char *)&data_exp; + + if (*dataptr == '|') /* process PCE */ + { + if (my_get_expression (&inst.reloc.exp, &pp) == (int) FAIL) + return (int) FAIL; + end_of_line (pp); + if (inst.error != 0) + return (int) FAIL; /* to ouptut_inst to printf out the error */ + *str = dataptr; + } + else /* process 16 bit */ + { + if (my_get_expression (&inst.reloc.exp, str) == (int) FAIL) + { + return (int) FAIL; + } + + dataptr = (char *)data_exp; + for (; *dataptr != '\0'; dataptr++) + { + *dataptr = TOLOWER (*dataptr); + if (*dataptr == '!' || *dataptr == ' ') + break; + } + dataptr = (char *)data_exp; + + if ((dataptr != NULL) + && (((strstr (dataptr, "0x")) != NULL) + || ((strstr (dataptr, "0X")) != NULL))) + { + hex_p = 1; + if ((data_type != _SIMM16_LA) + && (data_type != _VALUE_HI16) + && (data_type != _VALUE_LO16) + && (data_type != _IMM16) + && (data_type != _IMM15) + && (data_type != _IMM14) + && (data_type != _IMM4) + && (data_type != _IMM5) + && (data_type != _IMM8) + && (data_type != _IMM5_RSHIFT_1) + && (data_type != _IMM5_RSHIFT_2) + && (data_type != _SIMM14) + && (data_type != _SIMM14_NEG) + && (data_type != _SIMM16_NEG) + && (data_type != _IMM10_RSHIFT_2) + && (data_type != _GP_IMM15)) + { + data_type += 24; + } + } + + if ((inst.reloc.exp.X_add_number == 0) + && (inst.type != Insn_Type_SYN) + && (inst.type != Rd_rvalueRs_SI15) + && (inst.type != Rd_lvalueRs_SI15) + && (inst.type != Insn_internal) + && (((*dataptr >= 'a') && (*dataptr <= 'z')) + || ((*dataptr == '0') && (*(dataptr + 1) == 'x') && (*(dataptr + 2) != '0')) + || ((*dataptr == '+') && (*(dataptr + 1) != '0')) + || ((*dataptr == '-') && (*(dataptr + 1) != '0')))) + { + inst.error = BAD_ARGS; + return (int) FAIL; + } + } + + if ((inst.reloc.exp.X_add_symbol) + && ((data_type == _SIMM16) + || (data_type == _SIMM16_NEG) + || (data_type == _IMM16_NEG) + || (data_type == _SIMM14) + || (data_type == _SIMM14_NEG) + || (data_type == _IMM5) + || (data_type == _IMM14) + || (data_type == _IMM20) + || (data_type == _IMM16) + || (data_type == _IMM15) + || (data_type == _IMM4))) + { + inst.error = BAD_ARGS; + return (int) FAIL; + } + + if (inst.reloc.exp.X_add_symbol) + { + switch (data_type) + { + case _SIMM16_LA: + return (int) FAIL; + case _VALUE_HI16: + inst.reloc.type = BFD_RELOC_HI16_S; + inst.reloc.pc_rel = 0; + break; + case _VALUE_LO16: + inst.reloc.type = BFD_RELOC_LO16; + inst.reloc.pc_rel = 0; + break; + case _GP_IMM15: + inst.reloc.type = BFD_RELOC_SCORE_GPREL15; + inst.reloc.pc_rel = 0; + break; + case _SIMM16_pic: + case _IMM16_LO16_pic: + inst.reloc.type = BFD_RELOC_SCORE_GOT_LO16; + inst.reloc.pc_rel = 0; + break; + default: + inst.reloc.type = BFD_RELOC_32; + inst.reloc.pc_rel = 0; + break; + } + } + else + { + if (data_type == _IMM16_pic) + { + inst.reloc.type = BFD_RELOC_SCORE_DUMMY_HI16; + inst.reloc.pc_rel = 0; + } + + if (data_type == _SIMM16_LA && inst.reloc.exp.X_unsigned == 1) + { + value = validate_immediate (inst.reloc.exp.X_add_number, _SIMM16_LA_POS, hex_p); + if (value == (int) FAIL) /* for advance to check if this is ldis */ + if ((inst.reloc.exp.X_add_number & 0xffff) == 0) + { + inst.instruction |= 0x8000000; + inst.instruction |= ((inst.reloc.exp.X_add_number >> 16) << 1) & 0x1fffe; + return SUCCESS; + } + } + else + { + value = validate_immediate (inst.reloc.exp.X_add_number, data_type, hex_p); + } + + if (value == (int) FAIL) + { + if ((data_type != _SIMM14_NEG) && (data_type != _SIMM16_NEG) && (data_type != _IMM16_NEG)) + { + sprintf (err_msg, + _("invalid constant: %d bit expression not in range %d..%d"), + score_df_range[data_type].bits, + score_df_range[data_type].range[0], score_df_range[data_type].range[1]); + } + else + { + sprintf (err_msg, + _("invalid constant: %d bit expression not in range %d..%d"), + score_df_range[data_type].bits, + -score_df_range[data_type].range[1], -score_df_range[data_type].range[0]); + } + + inst.error = err_msg; + return (int) FAIL; + } + + if ((score_df_range[data_type].range[0] != 0) || (data_type == _IMM5_RANGE_8_31)) + { + value &= (1 << score_df_range[data_type].bits) - 1; + } + + inst.instruction |= value << shift; + } + + if ((inst.instruction & 0xf0000000) == 0x30000000) + { + if ((((inst.instruction >> 20) & 0x1F) != 0) + && (((inst.instruction >> 20) & 0x1F) != 1) + && (((inst.instruction >> 20) & 0x1F) != 2) + && (((inst.instruction >> 20) & 0x1F) != 3) + && (((inst.instruction >> 20) & 0x1F) != 4) + && (((inst.instruction >> 20) & 0x1F) != 8) + && (((inst.instruction >> 20) & 0x1F) != 9) + && (((inst.instruction >> 20) & 0x1F) != 0xa) + && (((inst.instruction >> 20) & 0x1F) != 0xb) + && (((inst.instruction >> 20) & 0x1F) != 0xc) + && (((inst.instruction >> 20) & 0x1F) != 0xd) + && (((inst.instruction >> 20) & 0x1F) != 0xe) + && (((inst.instruction >> 20) & 0x1F) != 0x10) + && (((inst.instruction >> 20) & 0x1F) != 0x11) + && (((inst.instruction >> 20) & 0x1F) != 0x18) + && (((inst.instruction >> 20) & 0x1F) != 0x1A) + && (((inst.instruction >> 20) & 0x1F) != 0x1B) + && (((inst.instruction >> 20) & 0x1F) != 0x1d) + && (((inst.instruction >> 20) & 0x1F) != 0x1e) + && (((inst.instruction >> 20) & 0x1F) != 0x1f)) + { + inst.error = _("invalid constant: bit expression not defined"); + return (int) FAIL; + } + } + + return SUCCESS; +} + +/* Handle addi/addi.c/addis.c/cmpi.c/addis.c/ldi. */ + +static void +do_rdsi16 (char *str) +{ + skip_whitespace (str); + + if (reg_required_here (&str, 20, REG_TYPE_SCORE) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL + || data_op2 (&str, 1, _SIMM16) == (int) FAIL + || end_of_line (str) == (int) FAIL) + return; + + /* ldi. */ + if ((inst.instruction & 0x20c0000) == 0x20c0000) + { + if ((((inst.instruction >> 20) & 0x10) == 0x10) || ((inst.instruction & 0x1fe00) != 0)) + { + inst.relax_inst = 0x8000; + } + else + { + inst.relax_inst |= (inst.instruction >> 1) & 0xff; + inst.relax_inst |= (((inst.instruction >> 20) & 0xf) << 8); + inst.relax_size = 2; + } + } + else if (((inst.instruction >> 20) & 0x10) == 0x10) + { + inst.relax_inst = 0x8000; + } +} + +/* Handle subi/subi.c. */ + +static void +do_sub_rdsi16 (char *str) +{ + skip_whitespace (str); + + if (reg_required_here (&str, 20, REG_TYPE_SCORE) != (int) FAIL + && skip_past_comma (&str) != (int) FAIL + && data_op2 (&str, 1, _SIMM16_NEG) != (int) FAIL) + end_of_line (str); +} + +/* Handle addri/addri.c. */ + +static void +do_rdrssi14 (char *str) /* -(2^13)~((2^13)-1) */ +{ + skip_whitespace (str); + + if (reg_required_here (&str, 20, REG_TYPE_SCORE) != (int) FAIL + && skip_past_comma (&str) != (int) FAIL + && reg_required_here (&str, 15, REG_TYPE_SCORE) != (int) FAIL + && skip_past_comma (&str) != (int) FAIL) + data_op2 (&str, 1, _SIMM14); +} + +/* Handle subri.c/subri. */ +static void +do_sub_rdrssi14 (char *str) /* -(2^13)~((2^13)-1) */ +{ + skip_whitespace (str); + + if (reg_required_here (&str, 20, REG_TYPE_SCORE) != (int) FAIL + && skip_past_comma (&str) != (int) FAIL + && reg_required_here (&str, 15, REG_TYPE_SCORE) != (int) FAIL + && skip_past_comma (&str) != (int) FAIL + && data_op2 (&str, 1, _SIMM14_NEG) != (int) FAIL) + end_of_line (str); +} + +/* Handle bitclr.c/bitset.c/bittgl.c/slli.c/srai.c/srli.c/roli.c/rori.c/rolic.c. */ +static void +do_rdrsi5 (char *str) /* 0~((2^14)-1) */ +{ + skip_whitespace (str); + + if (reg_required_here (&str, 20, REG_TYPE_SCORE) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL + || reg_required_here (&str, 15, REG_TYPE_SCORE) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL + || data_op2 (&str, 10, _IMM5) == (int) FAIL + || end_of_line (str) == (int) FAIL) + return; + + if ((((inst.instruction >> 20) & 0x1f) == ((inst.instruction >> 15) & 0x1f)) + && (inst.relax_inst != 0x8000) && (((inst.instruction >> 15) & 0x10) == 0)) + { + inst.relax_inst |= (((inst.instruction >> 10) & 0x1f) << 3) | (((inst.instruction >> 15) & 0xf) << 8); + inst.relax_size = 2; + } + else + inst.relax_inst = 0x8000; +} + +/* Handle andri/orri/andri.c/orri.c. */ + +static void +do_rdrsi14 (char *str) /* 0 ~ ((2^14)-1) */ +{ + skip_whitespace (str); + + if (reg_required_here (&str, 20, REG_TYPE_SCORE) != (int) FAIL + && skip_past_comma (&str) != (int) FAIL + && reg_required_here (&str, 15, REG_TYPE_SCORE) != (int) FAIL + && skip_past_comma (&str) != (int) FAIL + && data_op2 (&str, 1, _IMM14) != (int) FAIL) + end_of_line (str); +} + +/* Handle bittst.c. */ +static void +do_xrsi5 (char *str) +{ + skip_whitespace (str); + + if (reg_required_here (&str, 15, REG_TYPE_SCORE) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL + || data_op2 (&str, 10, _IMM5) == (int) FAIL + || end_of_line (str) == (int) FAIL) + return; + + if ((inst.relax_inst != 0x8000) && (((inst.instruction >> 15) & 0x10) == 0)) + { + inst.relax_inst |= (((inst.instruction >> 10) & 0x1f) << 3) | (((inst.instruction >> 15) & 0xf) << 8); + inst.relax_size = 2; + } + else + inst.relax_inst = 0x8000; +} + +/* Handle addis/andi/ori/andis/oris/ldis. */ +static void +do_rdi16 (char *str) +{ + skip_whitespace (str); + + if (reg_required_here (&str, 20, REG_TYPE_SCORE) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL + || data_op2 (&str, 1, _IMM16) == (int) FAIL + || end_of_line (str) == (int) FAIL) + return; + /* + if (((inst.instruction & 0xa0dfffe) != 0xa0c0000) || ((((inst.instruction >> 20) & 0x1f) & 0x10) == 0x10)) + inst.relax_inst = 0x8000; + else + inst.relax_size = 2; + */ +} + +static void +do_macro_rdi32hi (char *str) +{ + skip_whitespace (str); + + /* Do not handle end_of_line(). */ + if (reg_required_here (&str, 20, REG_TYPE_SCORE) != (int) FAIL + && skip_past_comma (&str) != (int) FAIL) + data_op2 (&str, 1, _VALUE_HI16); +} + +static void +do_macro_rdi32lo (char *str) +{ + skip_whitespace (str); + + /* Do not handle end_of_line(). */ + if (reg_required_here (&str, 20, REG_TYPE_SCORE) != (int) FAIL + && skip_past_comma (&str) != (int) FAIL) + data_op2 (&str, 1, _VALUE_LO16); +} + +/* Handle ldis_pic. */ + +static void +do_rdi16_pic (char *str) +{ + skip_whitespace (str); + + if (reg_required_here (&str, 20, REG_TYPE_SCORE) != (int) FAIL + && skip_past_comma (&str) != (int) FAIL + && data_op2 (&str, 1, _IMM16_pic) != (int) FAIL) + end_of_line (str); +} + +/* Handle addi_s_pic to generate R_SCORE_GOT_LO16 . */ + +static void +do_addi_s_pic (char *str) +{ + skip_whitespace (str); + + if (reg_required_here (&str, 20, REG_TYPE_SCORE) != (int) FAIL + && skip_past_comma (&str) != (int) FAIL + && data_op2 (&str, 1, _SIMM16_pic) != (int) FAIL) + end_of_line (str); +} + +/* Handle addi_u_pic to generate R_SCORE_GOT_LO16 . */ + +static void +do_addi_u_pic (char *str) +{ + skip_whitespace (str); + + if (reg_required_here (&str, 20, REG_TYPE_SCORE) != (int) FAIL + && skip_past_comma (&str) != (int) FAIL + && data_op2 (&str, 1, _IMM16_LO16_pic) != (int) FAIL) + end_of_line (str); +} + +/* Handle mfceh/mfcel/mtceh/mtchl. */ + +static void +do_rd (char *str) +{ + skip_whitespace (str); + + if (reg_required_here (&str, 20, REG_TYPE_SCORE) != (int) FAIL) + end_of_line (str); +} + +static void +do_rs (char *str) +{ + skip_whitespace (str); + + if (reg_required_here (&str, 15, REG_TYPE_SCORE) == (int) FAIL + || end_of_line (str) == (int) FAIL) + return; + + if ((inst.relax_inst != 0x8000) && (((inst.instruction >> 15) & 0x10) == 0)) + { + inst.relax_inst |= (((inst.instruction >> 10) & 0xf) << 8) | (((inst.instruction >> 15) & 0xf) << 4); + inst.relax_size = 2; + } + else + inst.relax_inst = 0x8000; +} + +static void +do_i15 (char *str) +{ + skip_whitespace (str); + + if (data_op2 (&str, 10, _IMM15) != (int) FAIL) + end_of_line (str); +} + +static void +do_xi5x (char *str) +{ + skip_whitespace (str); + + if (data_op2 (&str, 15, _IMM5) == (int) FAIL || end_of_line (str) == (int) FAIL) + return; + + if (inst.relax_inst != 0x8000) + { + inst.relax_inst |= (((inst.instruction >> 15) & 0x1f) << 3); + inst.relax_size = 2; + } +} + +static void +do_rdrs (char *str) +{ + skip_whitespace (str); + + if (reg_required_here (&str, 20, REG_TYPE_SCORE) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL + || reg_required_here (&str, 15, REG_TYPE_SCORE) == (int) FAIL + || end_of_line (str) == (int) FAIL) + return; + + if (inst.relax_inst != 0x8000) + { + if (((inst.instruction & 0x7f) == 0x56)) /* adjust mv -> mv! / mlfh! / mhfl! */ + { + /* mlfh */ + if ((((inst.instruction >> 15) & 0x10) != 0x0) && (((inst.instruction >> 20) & 0x10) == 0)) + { + inst.relax_inst = 0x00000001 | (((inst.instruction >> 15) & 0xf) << 4) + | (((inst.instruction >> 20) & 0xf) << 8); + inst.relax_size = 2; + } + /* mhfl */ + else if ((((inst.instruction >> 15) & 0x10) == 0x0) && ((inst.instruction >> 20) & 0x10) != 0) + { + inst.relax_inst = 0x00000002 | (((inst.instruction >> 15) & 0xf) << 4) + | (((inst.instruction >> 20) & 0xf) << 8); + inst.relax_size = 2; + } + else if ((((inst.instruction >> 15) & 0x10) == 0x0) && (((inst.instruction >> 20) & 0x10) == 0)) + { + inst.relax_inst |= (((inst.instruction >> 15) & 0xf) << 4) + | (((inst.instruction >> 20) & 0xf) << 8); + inst.relax_size = 2; + } + else + { + inst.relax_inst = 0x8000; + } + } + else if ((((inst.instruction >> 15) & 0x10) == 0x0) && (((inst.instruction >> 20) & 0x10) == 0)) + { + inst.relax_inst |= (((inst.instruction >> 15) & 0xf) << 4) + | (((inst.instruction >> 20) & 0xf) << 8); + inst.relax_size = 2; + } + else + { + inst.relax_inst = 0x8000; + } + } +} + +/* Handle mfcr/mtcr. */ +static void +do_rdcrs (char *str) +{ + skip_whitespace (str); + + if (reg_required_here (&str, 20, REG_TYPE_SCORE) != (int) FAIL + && skip_past_comma (&str) != (int) FAIL + && reg_required_here (&str, 15, REG_TYPE_SCORE_CR) != (int) FAIL) + end_of_line (str); +} + +/* Handle mfsr/mtsr. */ + +static void +do_rdsrs (char *str) +{ + skip_whitespace (str); + + /* mfsr */ + if ((inst.instruction & 0xff) == 0x50) + { + if (reg_required_here (&str, 20, REG_TYPE_SCORE) != (int) FAIL + && skip_past_comma (&str) != (int) FAIL + && reg_required_here (&str, 10, REG_TYPE_SCORE_SR) != (int) FAIL) + end_of_line (str); + } + else + { + if (reg_required_here (&str, 15, REG_TYPE_SCORE) != (int) FAIL + && skip_past_comma (&str) != (int) FAIL) + reg_required_here (&str, 10, REG_TYPE_SCORE_SR); + } +} + +/* Handle neg. */ + +static void +do_rdxrs (char *str) +{ + skip_whitespace (str); + + if (reg_required_here (&str, 20, REG_TYPE_SCORE) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL + || reg_required_here (&str, 10, REG_TYPE_SCORE) == (int) FAIL + || end_of_line (str) == (int) FAIL) + return; + + if ((inst.relax_inst != 0x8000) && (((inst.instruction >> 10) & 0x10) == 0) + && (((inst.instruction >> 20) & 0x10) == 0)) + { + inst.relax_inst |= (((inst.instruction >> 10) & 0xf) << 4) | (((inst.instruction >> 20) & 0xf) << 8); + inst.relax_size = 2; + } + else + inst.relax_inst = 0x8000; +} + +/* Handle cmp.c/cmp<cond>. */ +static void +do_rsrs (char *str) +{ + skip_whitespace (str); + + if (reg_required_here (&str, 15, REG_TYPE_SCORE) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL + || reg_required_here (&str, 10, REG_TYPE_SCORE) == (int) FAIL + || end_of_line (str) == (int) FAIL) + return; + + if ((inst.relax_inst != 0x8000) && (((inst.instruction >> 20) & 0x1f) == 3) + && (((inst.instruction >> 10) & 0x10) == 0) && (((inst.instruction >> 15) & 0x10) == 0)) + { + inst.relax_inst |= (((inst.instruction >> 10) & 0xf) << 4) | (((inst.instruction >> 15) & 0xf) << 8); + inst.relax_size = 2; + } + else + inst.relax_inst = 0x8000; +} + +static void +do_ceinst (char *str) +{ + char *strbak; + + strbak = str; + skip_whitespace (str); + + if (data_op2 (&str, 20, _IMM5) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL + || reg_required_here (&str, 15, REG_TYPE_SCORE) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL + || reg_required_here (&str, 10, REG_TYPE_SCORE) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL + || data_op2 (&str, 5, _IMM5) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL + || data_op2 (&str, 0, _IMM5) == (int) FAIL + || end_of_line (str) == (int) FAIL) + { + return; + } + else + { + str = strbak; + if (data_op2 (&str, 0, _IMM25) == (int) FAIL) + return; + } +} + +static int +reglow_required_here (char **str, int shift) +{ + static char buff[MAX_LITERAL_POOL_SIZE]; + int reg; + char *start = *str; + + if ((reg = score_reg_parse (str, all_reg_maps[REG_TYPE_SCORE].htab)) != (int) FAIL) + { + if ((reg == 1) && (nor1 == 1) && (inst.bwarn == 0)) + { + as_warn (_("Using temp register(r1)")); + inst.bwarn = 1; + } + if (reg < 16) + { + if (shift >= 0) + inst.instruction |= reg << shift; + + return reg; + } + } + + /* Restore the start point, we may have got a reg of the wrong class. */ + *str = start; + sprintf (buff, _("low register(r0-r15)expected, not '%.100s'"), start); + inst.error = buff; + return (int) FAIL; +} + +/* Handle addc!/add!/and!/cmp!/neg!/not!/or!/sll!/srl!/sra!/xor!/sub!. */ +static void +do16_rdrs (char *str) +{ + skip_whitespace (str); + + if (reglow_required_here (&str, 8) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL + || reglow_required_here (&str, 4) == (int) FAIL + || end_of_line (str) == (int) FAIL) + { + return; + } + else + { + if ((inst.instruction & 0x700f) == 0x2003) /* cmp! */ + { + inst.relax_inst |= (((inst.instruction >> 8) & 0xf) << 15) + | (((inst.instruction >> 4) & 0xf) << 10); + } + else if ((inst.instruction & 0x700f) == 0x2006) /* not! */ + { + inst.relax_inst |= (((inst.instruction >> 8) & 0xf) << 20) + | (((inst.instruction >> 4) & 0xf) << 15); + } + else + { + inst.relax_inst |= (((inst.instruction >> 8) & 0xf) << 20) + | (((inst.instruction >> 8) & 0xf) << 15) | (((inst.instruction >> 4) & 0xf) << 10); + } + inst.relax_size = 4; + } +} + +static void +do16_rs (char *str) +{ + int rd = 0; + + skip_whitespace (str); + + if ((rd = reglow_required_here (&str, 4)) == (int) FAIL + || end_of_line (str) == (int) FAIL) + { + return; + } + else + { + inst.relax_inst |= rd << 20; + inst.relax_size = 4; + } +} + +/* Handle br!/brl!. */ +static void +do16_xrs (char *str) +{ + skip_whitespace (str); + + if (reglow_required_here (&str, 4) == (int) FAIL || end_of_line (str) == (int) FAIL) + { + return; + } + else + { + inst.relax_inst |= (((inst.instruction >> 8) & 0xf) << 10) + | (((inst.instruction >> 4) & 0xf) << 15); + inst.relax_size = 4; + } +} + +static int +reghigh_required_here (char **str, int shift) +{ + static char buff[MAX_LITERAL_POOL_SIZE]; + int reg; + char *start = *str; + + if ((reg = score_reg_parse (str, all_reg_maps[REG_TYPE_SCORE].htab)) != (int) FAIL) + { + if (15 < reg && reg < 32) + { + if (shift >= 0) + inst.instruction |= (reg & 0xf) << shift; + + return reg; + } + } + + *str = start; + sprintf (buff, _("high register(r16-r31)expected, not '%.100s'"), start); + inst.error = buff; + return (int) FAIL; +} + +/* Handle mhfl!. */ +static void +do16_hrdrs (char *str) +{ + skip_whitespace (str); + + if (reghigh_required_here (&str, 8) != (int) FAIL + && skip_past_comma (&str) != (int) FAIL + && reglow_required_here (&str, 4) != (int) FAIL + && end_of_line (str) != (int) FAIL) + { + inst.relax_inst |= ((((inst.instruction >> 8) & 0xf) | 0x10) << 20) + | (((inst.instruction >> 4) & 0xf) << 15) | (0xf << 10); + inst.relax_size = 4; + } +} + +/* Handle mlfh!. */ +static void +do16_rdhrs (char *str) +{ + skip_whitespace (str); + + if (reglow_required_here (&str, 8) != (int) FAIL + && skip_past_comma (&str) != (int) FAIL + && reghigh_required_here (&str, 4) != (int) FAIL + && end_of_line (str) != (int) FAIL) + { + inst.relax_inst |= (((inst.instruction >> 8) & 0xf) << 20) + | ((((inst.instruction >> 4) & 0xf) | 0x10) << 15) | (0xf << 10); + inst.relax_size = 4; + } +} + +/* We need to be able to fix up arbitrary expressions in some statements. + This is so that we can handle symbols that are an arbitrary distance from + the pc. The most common cases are of the form ((+/-sym -/+ . - 8) & mask), + which returns part of an address in a form which will be valid for + a data instruction. We do this by pushing the expression into a symbol + in the expr_section, and creating a fix for that. */ +static fixS * +fix_new_score (fragS * frag, int where, short int size, expressionS * exp, int pc_rel, int reloc) +{ + fixS *new_fix; + + switch (exp->X_op) + { + case O_constant: + case O_symbol: + case O_add: + case O_subtract: + new_fix = fix_new_exp (frag, where, size, exp, pc_rel, reloc); + break; + default: + new_fix = fix_new (frag, where, size, make_expr_symbol (exp), 0, pc_rel, reloc); + break; + } + return new_fix; +} + +static void +init_dependency_vector (void) +{ + int i; + + for (i = 0; i < vector_size; i++) + memset (&dependency_vector[i], '\0', sizeof (dependency_vector[i])); + + return; +} + +static enum insn_type_for_dependency +dependency_type_from_insn (char *insn_name) +{ + char name[INSN_NAME_LEN]; + const struct insn_to_dependency *tmp; + + strcpy (name, insn_name); + tmp = (const struct insn_to_dependency *) hash_find (dependency_insn_hsh, name); + + if (tmp) + return tmp->type; + + return D_all_insn; +} + +static int +check_dependency (char *pre_insn, char *pre_reg, + char *cur_insn, char *cur_reg, int *warn_or_error) +{ + int bubbles = 0; + unsigned int i; + enum insn_type_for_dependency pre_insn_type; + enum insn_type_for_dependency cur_insn_type; + + pre_insn_type = dependency_type_from_insn (pre_insn); + cur_insn_type = dependency_type_from_insn (cur_insn); + + for (i = 0; i < sizeof (data_dependency_table) / sizeof (data_dependency_table[0]); i++) + { + if ((pre_insn_type == data_dependency_table[i].pre_insn_type) + && (D_all_insn == data_dependency_table[i].cur_insn_type + || cur_insn_type == data_dependency_table[i].cur_insn_type) + && (strcmp (data_dependency_table[i].pre_reg, "") == 0 + || strcmp (data_dependency_table[i].pre_reg, pre_reg) == 0) + && (strcmp (data_dependency_table[i].cur_reg, "") == 0 + || strcmp (data_dependency_table[i].cur_reg, cur_reg) == 0)) + { + bubbles = (score7) ? data_dependency_table[i].bubblenum_7 : data_dependency_table[i].bubblenum_5; + *warn_or_error = data_dependency_table[i].warn_or_error; + break; + } + } + + return bubbles; +} + +static void +build_one_frag (struct score_it one_inst) +{ + char *p; + int relaxable_p = g_opt; + int relax_size = 0; + + /* Start a new frag if frag_now is not empty. */ + if (frag_now_fix () != 0) + { + if (!frag_now->tc_frag_data.is_insn) + frag_wane (frag_now); + + frag_new (0); + } + frag_grow (20); + + p = frag_more (one_inst.size); + md_number_to_chars (p, one_inst.instruction, one_inst.size); + +#ifdef OBJ_ELF + dwarf2_emit_insn (one_inst.size); +#endif + + relaxable_p &= (one_inst.relax_size != 0); + relax_size = relaxable_p ? one_inst.relax_size : 0; + + p = frag_var (rs_machine_dependent, relax_size + RELAX_PAD_BYTE, 0, + RELAX_ENCODE (one_inst.size, one_inst.relax_size, + one_inst.type, 0, 0, relaxable_p), + NULL, 0, NULL); + + if (relaxable_p) + md_number_to_chars (p, one_inst.relax_inst, relax_size); +} + +static void +handle_dependency (struct score_it *theinst) +{ + int i; + int warn_or_error = 0; /* warn - 0; error - 1 */ + int bubbles = 0; + int remainder_bubbles = 0; + char cur_insn[INSN_NAME_LEN]; + char pre_insn[INSN_NAME_LEN]; + struct score_it nop_inst; + struct score_it pflush_inst; + + nop_inst.instruction = 0x0000; + nop_inst.size = 2; + nop_inst.relax_inst = 0x80008000; + nop_inst.relax_size = 4; + nop_inst.type = NO16_OPD; + + pflush_inst.instruction = 0x8000800a; + pflush_inst.size = 4; + pflush_inst.relax_inst = 0x8000; + pflush_inst.relax_size = 0; + pflush_inst.type = NO_OPD; + + /* pflush will clear all data dependency. */ + if (strcmp (theinst->name, "pflush") == 0) + { + init_dependency_vector (); + return; + } + + /* Push current instruction to dependency_vector[0]. */ + for (i = vector_size - 1; i > 0; i--) + memcpy (&dependency_vector[i], &dependency_vector[i - 1], sizeof (dependency_vector[i])); + + memcpy (&dependency_vector[0], theinst, sizeof (dependency_vector[i])); + + /* There is no dependency between nop and any instruction. */ + if (strcmp (dependency_vector[0].name, "nop") == 0 + || strcmp (dependency_vector[0].name, "nop!") == 0) + return; + + /* "pce" is defined in insn_to_dependency_table. */ +#define PCE_NAME "pce" + + if (dependency_vector[0].type == Insn_Type_PCE) + strcpy (cur_insn, PCE_NAME); + else + strcpy (cur_insn, dependency_vector[0].name); + + for (i = 1; i < vector_size; i++) + { + /* The element of dependency_vector is NULL. */ + if (dependency_vector[i].name[0] == '\0') + continue; + + if (dependency_vector[i].type == Insn_Type_PCE) + strcpy (pre_insn, PCE_NAME); + else + strcpy (pre_insn, dependency_vector[i].name); + + bubbles = check_dependency (pre_insn, dependency_vector[i].reg, + cur_insn, dependency_vector[0].reg, &warn_or_error); + remainder_bubbles = bubbles - i + 1; + + if (remainder_bubbles > 0) + { + int j; + + if (fix_data_dependency == 1) + { + if (remainder_bubbles <= 2) + { + if (warn_fix_data_dependency) + as_warn (_("Fix data dependency: %s %s -- %s %s (insert %d nop!/%d)"), + dependency_vector[i].name, dependency_vector[i].reg, + dependency_vector[0].name, dependency_vector[0].reg, + remainder_bubbles, bubbles); + + for (j = (vector_size - 1); (j - remainder_bubbles) > 0; j--) + memcpy (&dependency_vector[j], &dependency_vector[j - remainder_bubbles], + sizeof (dependency_vector[j])); + + for (j = 1; j <= remainder_bubbles; j++) + { + memset (&dependency_vector[j], '\0', sizeof (dependency_vector[j])); + /* Insert nop!. */ + build_one_frag (nop_inst); + } + } + else + { + if (warn_fix_data_dependency) + as_warn (_("Fix data dependency: %s %s -- %s %s (insert 1 pflush/%d)"), + dependency_vector[i].name, dependency_vector[i].reg, + dependency_vector[0].name, dependency_vector[0].reg, + bubbles); + + for (j = 1; j < vector_size; j++) + memset (&dependency_vector[j], '\0', sizeof (dependency_vector[j])); + + /* Insert pflush. */ + build_one_frag (pflush_inst); + } + } + else + { + if (warn_or_error) + { + as_bad (_("data dependency: %s %s -- %s %s (%d/%d bubble)"), + dependency_vector[i].name, dependency_vector[i].reg, + dependency_vector[0].name, dependency_vector[0].reg, + remainder_bubbles, bubbles); + } + else + { + as_warn (_("data dependency: %s %s -- %s %s (%d/%d bubble)"), + dependency_vector[i].name, dependency_vector[i].reg, + dependency_vector[0].name, dependency_vector[0].reg, + remainder_bubbles, bubbles); + } + } + } + } +} + +static enum insn_class +get_insn_class_from_type (enum score_insn_type type) +{ + enum insn_class retval = (int) FAIL; + + switch (type) + { + case Rd_I4: + case Rd_I5: + case Rd_rvalueBP_I5: + case Rd_lvalueBP_I5: + case Rd_I8: + case PC_DISP8div2: + case PC_DISP11div2: + case Rd_Rs: + case Rd_HighRs: + case Rd_lvalueRs: + case Rd_rvalueRs: + case x_Rs: + case Rd_LowRs: + case NO16_OPD: + retval = INSN_CLASS_16; + break; + case Rd_Rs_I5: + case x_Rs_I5: + case x_I5_x: + case Rd_Rs_I14: + case I15: + case Rd_I16: + case Rd_SI16: + case Rd_rvalueRs_SI10: + case Rd_lvalueRs_SI10: + case Rd_rvalueRs_preSI12: + case Rd_rvalueRs_postSI12: + case Rd_lvalueRs_preSI12: + case Rd_lvalueRs_postSI12: + case Rd_Rs_SI14: + case Rd_rvalueRs_SI15: + case Rd_lvalueRs_SI15: + case PC_DISP19div2: + case PC_DISP24div2: + case Rd_Rs_Rs: + case x_Rs_x: + case x_Rs_Rs: + case Rd_Rs_x: + case Rd_x_Rs: + case Rd_x_x: + case OP5_rvalueRs_SI15: + case I5_Rs_Rs_I5_OP5: + case x_rvalueRs_post4: + case Rd_rvalueRs_post4: + case Rd_x_I5: + case Rd_lvalueRs_post4: + case x_lvalueRs_post4: + case Rd_Rs_Rs_imm: + case NO_OPD: + case Rd_lvalue32Rs: + case Rd_rvalue32Rs: + case Insn_GP: + case Insn_PIC: + case Insn_internal: + retval = INSN_CLASS_32; + break; + case Insn_Type_PCE: + retval = INSN_CLASS_PCE; + break; + case Insn_Type_SYN: + retval = INSN_CLASS_SYN; + break; + default: + abort (); + break; + } + return retval; +} + +static unsigned long +adjust_paritybit (unsigned long m_code, enum insn_class class) +{ + unsigned long result = 0; + unsigned long m_code_high = 0; + unsigned long m_code_low = 0; + unsigned long pb_high = 0; + unsigned long pb_low = 0; + + if (class == INSN_CLASS_32) + { + pb_high = 0x80000000; + pb_low = 0x00008000; + } + else if (class == INSN_CLASS_16) + { + pb_high = 0; + pb_low = 0; + } + else if (class == INSN_CLASS_PCE) + { + pb_high = 0; + pb_low = 0x00008000; + } + else if (class == INSN_CLASS_SYN) + { + /* FIXME. at this time, INSN_CLASS_SYN must be 32 bit, but, instruction type should + be changed if macro instruction has been expanded. */ + pb_high = 0x80000000; + pb_low = 0x00008000; + } + else + { + abort (); + } + + m_code_high = m_code & 0x3fff8000; + m_code_low = m_code & 0x00007fff; + result = pb_high | (m_code_high << 1) | pb_low | m_code_low; + return result; + +} + +static void +gen_insn_frag (struct score_it *part_1, struct score_it *part_2) +{ + char *p; + bfd_boolean pce_p = FALSE; + int relaxable_p = g_opt; + int relax_size = 0; + struct score_it *inst1 = part_1; + struct score_it *inst2 = part_2; + struct score_it backup_inst1; + + pce_p = (inst2) ? TRUE : FALSE; + memcpy (&backup_inst1, inst1, sizeof (struct score_it)); + + /* Adjust instruction opcode and to be relaxed instruction opcode. */ + if (pce_p) + { + backup_inst1.instruction = ((backup_inst1.instruction & 0x7FFF) << 15) + | (inst2->instruction & 0x7FFF); + backup_inst1.instruction = adjust_paritybit (backup_inst1.instruction, INSN_CLASS_PCE); + backup_inst1.relax_inst = 0x8000; + backup_inst1.size = INSN_SIZE; + backup_inst1.relax_size = 0; + backup_inst1.type = Insn_Type_PCE; + } + else + { + backup_inst1.instruction = adjust_paritybit (backup_inst1.instruction, + GET_INSN_CLASS (backup_inst1.type)); + } + + if (backup_inst1.relax_size != 0) + { + enum insn_class tmp; + + tmp = (backup_inst1.size == INSN_SIZE) ? INSN_CLASS_16 : INSN_CLASS_32; + backup_inst1.relax_inst = adjust_paritybit (backup_inst1.relax_inst, tmp); + } + + /* Check data dependency. */ + handle_dependency (&backup_inst1); + + /* Start a new frag if frag_now is not empty and is not instruction frag, maybe it contains + data produced by .ascii etc. Doing this is to make one instruction per frag. */ + if (frag_now_fix () != 0) + { + if (!frag_now->tc_frag_data.is_insn) + frag_wane (frag_now); + + frag_new (0); + } + + /* Here, we must call frag_grow in order to keep the instruction frag type is + rs_machine_dependent. + For, frag_var may change frag_now->fr_type to rs_fill by calling frag_grow which + acturally will call frag_wane. + Calling frag_grow first will create a new frag_now which free size is 20 that is enough + for frag_var. */ + frag_grow (20); + + p = frag_more (backup_inst1.size); + md_number_to_chars (p, backup_inst1.instruction, backup_inst1.size); + +#ifdef OBJ_ELF + dwarf2_emit_insn (backup_inst1.size); +#endif + + /* Generate fixup structure. */ + if (pce_p) + { + if (inst1->reloc.type != BFD_RELOC_NONE) + fix_new_score (frag_now, p - frag_now->fr_literal, + inst1->size, &inst1->reloc.exp, + inst1->reloc.pc_rel, inst1->reloc.type); + + if (inst2->reloc.type != BFD_RELOC_NONE) + fix_new_score (frag_now, p - frag_now->fr_literal + 2, + inst2->size, &inst2->reloc.exp, inst2->reloc.pc_rel, inst2->reloc.type); + } + else + { + if (backup_inst1.reloc.type != BFD_RELOC_NONE) + fix_new_score (frag_now, p - frag_now->fr_literal, + backup_inst1.size, &backup_inst1.reloc.exp, + backup_inst1.reloc.pc_rel, backup_inst1.reloc.type); + } + + /* relax_size may be 2, 4, 12 or 0, 0 indicates no relaxation. */ + relaxable_p &= (backup_inst1.relax_size != 0); + relax_size = relaxable_p ? backup_inst1.relax_size : 0; + + p = frag_var (rs_machine_dependent, relax_size + RELAX_PAD_BYTE, 0, + RELAX_ENCODE (backup_inst1.size, backup_inst1.relax_size, + backup_inst1.type, 0, 0, relaxable_p), + backup_inst1.reloc.exp.X_add_symbol, 0, NULL); + + if (relaxable_p) + md_number_to_chars (p, backup_inst1.relax_inst, relax_size); + + memcpy (inst1, &backup_inst1, sizeof (struct score_it)); +} + +static void +parse_16_32_inst (char *insnstr, bfd_boolean gen_frag_p) +{ + char c; + char *p; + char *operator = insnstr; + const struct asm_opcode *opcode; + + /* Parse operator and operands. */ + skip_whitespace (operator); + + for (p = operator; *p != '\0'; p++) + if ((*p == ' ') || (*p == '!')) + break; + + if (*p == '!') + p++; + + c = *p; + *p = '\0'; + + opcode = (const struct asm_opcode *) hash_find (score_ops_hsh, operator); + *p = c; + + memset (&inst, '\0', sizeof (inst)); + sprintf (inst.str, "%s", insnstr); + if (opcode) + { + inst.instruction = opcode->value; + inst.relax_inst = opcode->relax_value; + inst.type = opcode->type; + inst.size = GET_INSN_SIZE (inst.type); + inst.relax_size = 0; + inst.bwarn = 0; + sprintf (inst.name, "%s", opcode->template); + strcpy (inst.reg, ""); + inst.error = NULL; + inst.reloc.type = BFD_RELOC_NONE; + + (*opcode->parms) (p); + + /* It indicates current instruction is a macro instruction if inst.bwarn equals -1. */ + if ((inst.bwarn != -1) && (!inst.error) && (gen_frag_p)) + gen_insn_frag (&inst, NULL); + } + else + inst.error = _("unrecognized opcode"); +} + +static int +append_insn (char *str, bfd_boolean gen_frag_p) +{ + int retval = SUCCESS; + + parse_16_32_inst (str, gen_frag_p); + + if (inst.error) + { + retval = (int) FAIL; + as_bad (_("%s -- `%s'"), inst.error, inst.str); + inst.error = NULL; + } + + return retval; +} + +/* Handle mv! reg_high, reg_low; + mv! reg_low, reg_high; + mv! reg_low, reg_low; */ +static void +do16_mv_rdrs (char *str) +{ + int reg_rd; + int reg_rs; + char *backupstr = NULL; + + backupstr = str; + skip_whitespace (str); + + if ((reg_rd = reg_required_here (&str, 8, REG_TYPE_SCORE)) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL + || (reg_rs = reg_required_here (&str, 4, REG_TYPE_SCORE)) == (int) FAIL + || end_of_line (str) == (int) FAIL) + { + return; + } + else + { + /* Case 1 : mv! or mlfh!. */ + if (reg_rd < 16) + { + if (reg_rs < 16) + { + inst.relax_inst |= (((inst.instruction >> 8) & 0xf) << 20) + | (((inst.instruction >> 4) & 0xf) << 15) | (0xf << 10); + inst.relax_size = 4; + } + else + { + char append_str[MAX_LITERAL_POOL_SIZE]; + + sprintf (append_str, "mlfh! %s", backupstr); + if (append_insn (append_str, TRUE) == (int) FAIL) + return; + /* Set bwarn as -1, so macro instruction itself will not be generated frag. */ + inst.bwarn = -1; + } + } + /* Case 2 : mhfl!. */ + else + { + if (reg_rs > 16) + { + SET_INSN_ERROR (BAD_ARGS); + return; + } + else + { + char append_str[MAX_LITERAL_POOL_SIZE]; + + sprintf (append_str, "mhfl! %s", backupstr); + if (append_insn (append_str, TRUE) == (int) FAIL) + return; + + /* Set bwarn as -1, so macro instruction itself will not be generated frag. */ + inst.bwarn = -1; + } + } + } +} + +static void +do16_rdi4 (char *str) +{ + skip_whitespace (str); + + if (reglow_required_here (&str, 8) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL + || data_op2 (&str, 3, _IMM4) == (int) FAIL + || end_of_line (str) == (int) FAIL) + { + return; + } + else + { + if (((inst.instruction >> 3) & 0x10) == 0) /* for judge is addei or subei : bit 5 =0 : addei */ + { + if (((inst.instruction >> 3) & 0xf) != 0xf) + { + inst.relax_inst |= (((inst.instruction >> 8) & 0xf) << 20) + | ((1 << ((inst.instruction >> 3) & 0xf)) << 1); + inst.relax_size = 4; + } + else + { + inst.relax_inst = 0x8000; + } + } + else + { + if (((inst.instruction >> 3) & 0xf) != 0xf) + { + inst.relax_inst |= (((inst.instruction >> 8) & 0xf) << 20) + | (((-(1 << ((inst.instruction >> 3) & 0xf))) & 0xffff) << 1); + inst.relax_size = 4; + } + else + { + inst.relax_inst = 0x8000; + } + } + } +} + +static void +do16_rdi5 (char *str) +{ + skip_whitespace (str); + + if (reglow_required_here (&str, 8) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL + || data_op2 (&str, 3, _IMM5) == (int) FAIL + || end_of_line (str) == (int) FAIL) + return; + else + { + inst.relax_inst |= (((inst.instruction >> 8) & 0xf) << 20) + | (((inst.instruction >> 8) & 0xf) << 15) | (((inst.instruction >> 3) & 0x1f) << 10); + inst.relax_size = 4; + } +} + +/* Handle sdbbp. */ +static void +do16_xi5 (char *str) +{ + skip_whitespace (str); + + if (data_op2 (&str, 3, _IMM5) == (int) FAIL || end_of_line (str) == (int) FAIL) + return; + else + { + inst.relax_inst |= (((inst.instruction >> 3) & 0x1f) << 15); + inst.relax_size = 4; + } +} + +/* Check that an immediate is word alignment or half word alignment. + If so, convert it to the right format. */ +static int +validate_immediate_align (int val, unsigned int data_type) +{ + if (data_type == _IMM5_RSHIFT_1) + { + if (val % 2) + { + inst.error = _("address offset must be half word alignment"); + return (int) FAIL; + } + } + else if ((data_type == _IMM5_RSHIFT_2) || (data_type == _IMM10_RSHIFT_2)) + { + if (val % 4) + { + inst.error = _("address offset must be word alignment"); + return (int) FAIL; + } + } + + return SUCCESS; +} + +static int +exp_ldst_offset (char **str, int shift, unsigned int data_type) +{ + char *dataptr; + + dataptr = * str; + + if ((*dataptr == '0') && (*(dataptr + 1) == 'x') + && (data_type != _SIMM16_LA) + && (data_type != _VALUE_HI16) + && (data_type != _VALUE_LO16) + && (data_type != _IMM16) + && (data_type != _IMM15) + && (data_type != _IMM14) + && (data_type != _IMM4) + && (data_type != _IMM5) + && (data_type != _IMM8) + && (data_type != _IMM5_RSHIFT_1) + && (data_type != _IMM5_RSHIFT_2) + && (data_type != _SIMM14_NEG) + && (data_type != _IMM10_RSHIFT_2)) + { + data_type += 24; + } + + if (my_get_expression (&inst.reloc.exp, str) == (int) FAIL) + return (int) FAIL; + + if (inst.reloc.exp.X_op == O_constant) + { + /* Need to check the immediate align. */ + int value = validate_immediate_align (inst.reloc.exp.X_add_number, data_type); + + if (value == (int) FAIL) + return (int) FAIL; + + value = validate_immediate (inst.reloc.exp.X_add_number, data_type, 0); + if (value == (int) FAIL) + { + if (data_type < 30) + sprintf (err_msg, + _("invalid constant: %d bit expression not in range %d..%d"), + score_df_range[data_type].bits, + score_df_range[data_type].range[0], score_df_range[data_type].range[1]); + else + sprintf (err_msg, + _("invalid constant: %d bit expression not in range %d..%d"), + score_df_range[data_type - 24].bits, + score_df_range[data_type - 24].range[0], score_df_range[data_type - 24].range[1]); + inst.error = err_msg; + return (int) FAIL; + } + + if (data_type == _IMM5_RSHIFT_1) + { + value >>= 1; + } + else if ((data_type == _IMM5_RSHIFT_2) || (data_type == _IMM10_RSHIFT_2)) + { + value >>= 2; + } + + if (score_df_range[data_type].range[0] != 0) + { + value &= (1 << score_df_range[data_type].bits) - 1; + } + + inst.instruction |= value << shift; + } + else + { + inst.reloc.pc_rel = 0; + } + + return SUCCESS; +} + +static void +do_ldst_insn (char *str) +{ + int pre_inc = 0; + int conflict_reg; + int value; + char * temp; + char *strbak; + char *dataptr; + int reg; + int ldst_idx = 0; + + strbak = str; + skip_whitespace (str); + + if (((conflict_reg = reg_required_here (&str, 20, REG_TYPE_SCORE)) == (int) FAIL) + || (skip_past_comma (&str) == (int) FAIL)) + return; + + /* ld/sw rD, [rA, simm15] ld/sw rD, [rA]+, simm12 ld/sw rD, [rA, simm12]+. */ + if (*str == '[') + { + str++; + skip_whitespace (str); + + if ((reg = reg_required_here (&str, 15, REG_TYPE_SCORE)) == (int) FAIL) + return; + + /* Conflicts can occur on stores as well as loads. */ + conflict_reg = (conflict_reg == reg); + skip_whitespace (str); + temp = str + 1; /* The latter will process decimal/hex expression. */ + + /* ld/sw rD, [rA]+, simm12 ld/sw rD, [rA]+. */ + if (*str == ']') + { + str++; + if (*str == '+') + { + str++; + /* ld/sw rD, [rA]+, simm12. */ + if (skip_past_comma (&str) == SUCCESS) + { + if ((exp_ldst_offset (&str, 3, _SIMM12) == (int) FAIL) + || (end_of_line (str) == (int) FAIL)) + return; + + if (conflict_reg) + { + unsigned int ldst_func = inst.instruction & OPC_PSEUDOLDST_MASK; + + if ((ldst_func == INSN_LH) + || (ldst_func == INSN_LHU) + || (ldst_func == INSN_LW) + || (ldst_func == INSN_LB) + || (ldst_func == INSN_LBU)) + { + inst.error = _("register same as write-back base"); + return; + } + } + + ldst_idx = inst.instruction & OPC_PSEUDOLDST_MASK; + inst.instruction &= ~OPC_PSEUDOLDST_MASK; + inst.instruction |= score_ldst_insns[ldst_idx * 3 + LDST_POST].value; + + /* lw rD, [rA]+, 4 convert to pop rD, [rA]. */ + if ((inst.instruction & 0x3e000007) == 0x0e000000) + { + /* rs = r0-r7, offset = 4 */ + if ((((inst.instruction >> 15) & 0x18) == 0) + && (((inst.instruction >> 3) & 0xfff) == 4)) + { + /* Relax to pophi. */ + if ((((inst.instruction >> 20) & 0x10) == 0x10)) + { + inst.relax_inst = 0x0000200a | (((inst.instruction >> 20) & 0xf) + << 8) | 1 << 7 | + (((inst.instruction >> 15) & 0x7) << 4); + } + /* Relax to pop. */ + else + { + inst.relax_inst = 0x0000200a | (((inst.instruction >> 20) & 0xf) + << 8) | 0 << 7 | + (((inst.instruction >> 15) & 0x7) << 4); + } + inst.relax_size = 2; + } + } + return; + } + /* ld/sw rD, [rA]+ convert to ld/sw rD, [rA, 0]+. */ + else + { + SET_INSN_ERROR (NULL); + if (end_of_line (str) == (int) FAIL) + { + return; + } + + pre_inc = 1; + value = validate_immediate (inst.reloc.exp.X_add_number, _SIMM12, 0); + value &= (1 << score_df_range[_SIMM12].bits) - 1; + ldst_idx = inst.instruction & OPC_PSEUDOLDST_MASK; + inst.instruction &= ~OPC_PSEUDOLDST_MASK; + inst.instruction |= score_ldst_insns[ldst_idx * 3 + pre_inc].value; + inst.instruction |= value << 3; + inst.relax_inst = 0x8000; + return; + } + } + /* ld/sw rD, [rA] convert to ld/sw rD, [rA, simm15]. */ + else + { + if (end_of_line (str) == (int) FAIL) + return; + + ldst_idx = inst.instruction & OPC_PSEUDOLDST_MASK; + inst.instruction &= ~OPC_PSEUDOLDST_MASK; + inst.instruction |= score_ldst_insns[ldst_idx * 3 + LDST_NOUPDATE].value; + + /* lbu rd, [rs] -> lbu! rd, [rs] */ + if (ldst_idx == INSN_LBU) + { + inst.relax_inst = INSN16_LBU; + } + else if (ldst_idx == INSN_LH) + { + inst.relax_inst = INSN16_LH; + } + else if (ldst_idx == INSN_LW) + { + inst.relax_inst = INSN16_LW; + } + else if (ldst_idx == INSN_SB) + { + inst.relax_inst = INSN16_SB; + } + else if (ldst_idx == INSN_SH) + { + inst.relax_inst = INSN16_SH; + } + else if (ldst_idx == INSN_SW) + { + inst.relax_inst = INSN16_SW; + } + else + { + inst.relax_inst = 0x8000; + } + + /* lw/lh/lbu/sw/sh/sb, offset = 0, relax to 16 bit instruction. */ + if ((ldst_idx == INSN_LBU) + || (ldst_idx == INSN_LH) + || (ldst_idx == INSN_LW) + || (ldst_idx == INSN_SB) || (ldst_idx == INSN_SH) || (ldst_idx == INSN_SW)) + { + if ((((inst.instruction >> 15) & 0x10) == 0) && (((inst.instruction >> 20) & 0x10) == 0)) + { + inst.relax_inst |= (2 << 12) | (((inst.instruction >> 20) & 0xf) << 8) | + (((inst.instruction >> 15) & 0xf) << 4); + inst.relax_size = 2; + } + } + + return; + } + } + /* ld/sw rD, [rA, simm15] ld/sw rD, [rA, simm12]+. */ + else + { + if (skip_past_comma (&str) == (int) FAIL) + { + inst.error = _("pre-indexed expression expected"); + return; + } + + if (my_get_expression (&inst.reloc.exp, &str) == (int) FAIL) + return; + + skip_whitespace (str); + if (*str++ != ']') + { + inst.error = _("missing ]"); + return; + } + + skip_whitespace (str); + /* ld/sw rD, [rA, simm12]+. */ + if (*str == '+') + { + str++; + pre_inc = 1; + if (conflict_reg) + { + unsigned int ldst_func = inst.instruction & OPC_PSEUDOLDST_MASK; + + if ((ldst_func == INSN_LH) + || (ldst_func == INSN_LHU) + || (ldst_func == INSN_LW) + || (ldst_func == INSN_LB) + || (ldst_func == INSN_LBU)) + { + inst.error = _("register same as write-back base"); + return; + } + } + } + + if (end_of_line (str) == (int) FAIL) + return; + + if (inst.reloc.exp.X_op == O_constant) + { + int value; + unsigned int data_type; + + if (pre_inc == 1) + data_type = _SIMM12; + else + data_type = _SIMM15; + dataptr = temp; + + if ((*dataptr == '0') && (*(dataptr + 1) == 'x') + && (data_type != _SIMM16_LA) + && (data_type != _VALUE_HI16) + && (data_type != _VALUE_LO16) + && (data_type != _IMM16) + && (data_type != _IMM15) + && (data_type != _IMM14) + && (data_type != _IMM4) + && (data_type != _IMM5) + && (data_type != _IMM8) + && (data_type != _IMM5_RSHIFT_1) + && (data_type != _IMM5_RSHIFT_2) + && (data_type != _SIMM14_NEG) + && (data_type != _IMM10_RSHIFT_2)) + { + data_type += 24; + } + + value = validate_immediate (inst.reloc.exp.X_add_number, data_type, 0); + if (value == (int) FAIL) + { + if (data_type < 30) + sprintf (err_msg, + _("invalid constant: %d bit expression not in range %d..%d"), + score_df_range[data_type].bits, + score_df_range[data_type].range[0], score_df_range[data_type].range[1]); + else + sprintf (err_msg, + _("invalid constant: %d bit expression not in range %d..%d"), + score_df_range[data_type - 24].bits, + score_df_range[data_type - 24].range[0], + score_df_range[data_type - 24].range[1]); + inst.error = err_msg; + return; + } + + value &= (1 << score_df_range[data_type].bits) - 1; + ldst_idx = inst.instruction & OPC_PSEUDOLDST_MASK; + inst.instruction &= ~OPC_PSEUDOLDST_MASK; + inst.instruction |= score_ldst_insns[ldst_idx * 3 + pre_inc].value; + if (pre_inc == 1) + inst.instruction |= value << 3; + else + inst.instruction |= value; + + /* lw rD, [rA, simm15] */ + if ((inst.instruction & 0x3e000000) == 0x20000000) + { + /* Both rD and rA are in [r0 - r15]. */ + if ((((inst.instruction >> 15) & 0x10) == 0) + && (((inst.instruction >> 20) & 0x10) == 0)) + { + /* simm15 = 0, lw -> lw!. */ + if ((inst.instruction & 0x7fff) == 0) + { + inst.relax_inst |= (((inst.instruction >> 15) & 0xf) << 4) + | (((inst.instruction >> 20) & 0xf) << 8); + inst.relax_size = 2; + } + /* rA = r2, lw -> lwp!. */ + else if ((((inst.instruction >> 15) & 0xf) == 2) + && ((inst.instruction & 0x3) == 0) + && ((inst.instruction & 0x7fff) < 128)) + { + inst.relax_inst = 0x7000 | (((inst.instruction >> 20) & 0xf) << 8) + | (((inst.instruction & 0x7fff) >> 2) << 3); + inst.relax_size = 2; + } + else + { + inst.relax_inst = 0x8000; + } + } + else + { + inst.relax_inst = 0x8000; + } + } + /* sw rD, [rA, simm15] */ + else if ((inst.instruction & 0x3e000000) == 0x28000000) + { + /* Both rD and rA are in [r0 - r15]. */ + if ((((inst.instruction >> 15) & 0x10) == 0) && (((inst.instruction >> 20) & 0x10) == 0)) + { + /* simm15 = 0, sw -> sw!. */ + if ((inst.instruction & 0x7fff) == 0) + { + inst.relax_inst |= (((inst.instruction >> 15) & 0xf) << 4) + | (((inst.instruction >> 20) & 0xf) << 8); + inst.relax_size = 2; + } + /* rA = r2, sw -> swp!. */ + else if ((((inst.instruction >> 15) & 0xf) == 2) + && ((inst.instruction & 0x3) == 0) + && ((inst.instruction & 0x7fff) < 128)) + { + inst.relax_inst = 0x7004 | (((inst.instruction >> 20) & 0xf) << 8) + | (((inst.instruction & 0x7fff) >> 2) << 3); + inst.relax_size = 2; + } + else + { + inst.relax_inst = 0x8000; + } + } + else + { + inst.relax_inst = 0x8000; + } + } + /* sw rD, [rA, simm15]+ sw pre. */ + else if ((inst.instruction & 0x3e000007) == 0x06000004) + { + /* rA is in [r0 - r7], and simm15 = -4. */ + if ((((inst.instruction >> 15) & 0x18) == 0) + && (((inst.instruction >> 3) & 0xfff) == 0xffc)) + { + /* sw -> pushhi!. */ + if ((((inst.instruction >> 20) & 0x10) == 0x10)) + { + inst.relax_inst = 0x0000200e | (((inst.instruction >> 20) & 0xf) << 8) + | 1 << 7 | (((inst.instruction >> 15) & 0x7) << 4); + inst.relax_size = 2; + } + /* sw -> push!. */ + else + { + inst.relax_inst = 0x0000200e | (((inst.instruction >> 20) & 0xf) << 8) + | 0 << 7 | (((inst.instruction >> 15) & 0x7) << 4); + inst.relax_size = 2; + } + } + else + { + inst.relax_inst = 0x8000; + } + } + /* lh rD, [rA, simm15] */ + else if ((inst.instruction & 0x3e000000) == 0x22000000) + { + /* Both rD and rA are in [r0 - r15]. */ + if ((((inst.instruction >> 15) & 0x10) == 0) && (((inst.instruction >> 20) & 0x10) == 0)) + { + /* simm15 = 0, lh -> lh!. */ + if ((inst.instruction & 0x7fff) == 0) + { + inst.relax_inst |= (((inst.instruction >> 15) & 0xf) << 4) + | (((inst.instruction >> 20) & 0xf) << 8); + inst.relax_size = 2; + } + /* rA = r2, lh -> lhp!. */ + else if ((((inst.instruction >> 15) & 0xf) == 2) + && ((inst.instruction & 0x1) == 0) + && ((inst.instruction & 0x7fff) < 64)) + { + inst.relax_inst = 0x7001 | (((inst.instruction >> 20) & 0xf) << 8) + | (((inst.instruction & 0x7fff) >> 1) << 3); + inst.relax_size = 2; + } + else + { + inst.relax_inst = 0x8000; + } + } + else + { + inst.relax_inst = 0x8000; + } + } + /* sh rD, [rA, simm15] */ + else if ((inst.instruction & 0x3e000000) == 0x2a000000) + { + /* Both rD and rA are in [r0 - r15]. */ + if ((((inst.instruction >> 15) & 0x10) == 0) && (((inst.instruction >> 20) & 0x10) == 0)) + { + /* simm15 = 0, sh -> sh!. */ + if ((inst.instruction & 0x7fff) == 0) + { + inst.relax_inst |= (((inst.instruction >> 15) & 0xf) << 4) + | (((inst.instruction >> 20) & 0xf) << 8); + inst.relax_size = 2; + } + /* rA = r2, sh -> shp!. */ + else if ((((inst.instruction >> 15) & 0xf) == 2) + && ((inst.instruction & 0x1) == 0) + && ((inst.instruction & 0x7fff) < 64)) + { + inst.relax_inst = 0x7005 | (((inst.instruction >> 20) & 0xf) << 8) + | (((inst.instruction & 0x7fff) >> 1) << 3); + inst.relax_size = 2; + } + else + { + inst.relax_inst = 0x8000; + } + } + else + { + inst.relax_inst = 0x8000; + } + } + /* lbu rD, [rA, simm15] */ + else if ((inst.instruction & 0x3e000000) == 0x2c000000) + { + /* Both rD and rA are in [r0 - r15]. */ + if ((((inst.instruction >> 15) & 0x10) == 0) && (((inst.instruction >> 20) & 0x10) == 0)) + { + /* simm15 = 0, lbu -> lbu!. */ + if ((inst.instruction & 0x7fff) == 0) + { + inst.relax_inst |= (((inst.instruction >> 15) & 0xf) << 4) + | (((inst.instruction >> 20) & 0xf) << 8); + inst.relax_size = 2; + } + /* rA = r2, lbu -> lbup!. */ + else if ((((inst.instruction >> 15) & 0xf) == 2) + && ((inst.instruction & 0x7fff) < 32)) + { + inst.relax_inst = 0x7003 | (((inst.instruction >> 20) & 0xf) << 8) + | ((inst.instruction & 0x7fff) << 3); + inst.relax_size = 2; + } + else + { + inst.relax_inst = 0x8000; + } + } + else + { + inst.relax_inst = 0x8000; + } + } + /* sb rD, [rA, simm15] */ + else if ((inst.instruction & 0x3e000000) == 0x2e000000) + { + /* Both rD and rA are in [r0 - r15]. */ + if ((((inst.instruction >> 15) & 0x10) == 0) && (((inst.instruction >> 20) & 0x10) == 0)) + { + /* simm15 = 0, sb -> sb!. */ + if ((inst.instruction & 0x7fff) == 0) + { + inst.relax_inst |= (((inst.instruction >> 15) & 0xf) << 4) + | (((inst.instruction >> 20) & 0xf) << 8); + inst.relax_size = 2; + } + /* rA = r2, sb -> sb!. */ + else if ((((inst.instruction >> 15) & 0xf) == 2) + && ((inst.instruction & 0x7fff) < 32)) + { + inst.relax_inst = 0x7007 | (((inst.instruction >> 20) & 0xf) << 8) + | ((inst.instruction & 0x7fff) << 3); + inst.relax_size = 2; + } + else + { + inst.relax_inst = 0x8000; + } + } + else + { + inst.relax_inst = 0x8000; + } + } + else + { + inst.relax_inst = 0x8000; + } + + return; + } + else + { + /* FIXME: may set error, for there is no ld/sw rD, [rA, label] */ + inst.reloc.pc_rel = 0; + } + } + } + else + { + inst.error = BAD_ARGS; + } +} + +/* Handle cache. */ + +static void +do_cache (char *str) +{ + skip_whitespace (str); + + if ((data_op2 (&str, 20, _IMM5) == (int) FAIL) || (skip_past_comma (&str) == (int) FAIL)) + { + return; + } + else + { + int cache_op; + + cache_op = (inst.instruction >> 20) & 0x1F; + sprintf (inst.name, "cache %d", cache_op); + } + + if (*str == '[') + { + str++; + skip_whitespace (str); + + if (reg_required_here (&str, 15, REG_TYPE_SCORE) == (int) FAIL) + return; + + skip_whitespace (str); + + /* cache op, [rA] */ + if (skip_past_comma (&str) == (int) FAIL) + { + SET_INSN_ERROR (NULL); + if (*str != ']') + { + inst.error = _("missing ]"); + return; + } + str++; + } + /* cache op, [rA, simm15] */ + else + { + if (exp_ldst_offset (&str, 0, _SIMM15) == (int) FAIL) + { + return; + } + + skip_whitespace (str); + if (*str++ != ']') + { + inst.error = _("missing ]"); + return; + } + } + + if (end_of_line (str) == (int) FAIL) + return; + } + else + { + inst.error = BAD_ARGS; + } +} + +static void +do_crdcrscrsimm5 (char *str) +{ + char *strbak; + + strbak = str; + skip_whitespace (str); + + if (reg_required_here (&str, 20, REG_TYPE_SCORE_CR) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL + || reg_required_here (&str, 15, REG_TYPE_SCORE_CR) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL + || reg_required_here (&str, 10, REG_TYPE_SCORE_CR) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL) + { + str = strbak; + /* cop1 cop_code20. */ + if (data_op2 (&str, 5, _IMM20) == (int) FAIL) + return; + } + else + { + if (data_op2 (&str, 5, _IMM5) == (int) FAIL) + return; + } + + end_of_line (str); +} + +/* Handle ldc/stc. */ +static void +do_ldst_cop (char *str) +{ + skip_whitespace (str); + + if ((reg_required_here (&str, 15, REG_TYPE_SCORE_CR) == (int) FAIL) + || (skip_past_comma (&str) == (int) FAIL)) + return; + + if (*str == '[') + { + str++; + skip_whitespace (str); + + if (reg_required_here (&str, 20, REG_TYPE_SCORE) == (int) FAIL) + return; + + skip_whitespace (str); + + if (*str++ != ']') + { + if (exp_ldst_offset (&str, 5, _IMM10_RSHIFT_2) == (int) FAIL) + return; + + skip_whitespace (str); + if (*str++ != ']') + { + inst.error = _("missing ]"); + return; + } + } + + end_of_line (str); + } + else + inst.error = BAD_ARGS; +} + +static void +do16_ldst_insn (char *str) +{ + skip_whitespace (str); + + if ((reglow_required_here (&str, 8) == (int) FAIL) || (skip_past_comma (&str) == (int) FAIL)) + return; + + if (*str == '[') + { + int reg; + + str++; + skip_whitespace (str); + + if ((reg = reglow_required_here (&str, 4)) == (int) FAIL) + return; + + skip_whitespace (str); + if (*str++ == ']') + { + if (end_of_line (str) == (int) FAIL) + return; + else + { + inst.relax_inst |= (((inst.instruction >> 8) & 0xf) << 20) + | (((inst.instruction >> 4) & 0xf) << 15); + inst.relax_size = 4; + } + } + else + { + inst.error = _("missing ]"); + } + } + else + { + inst.error = BAD_ARGS; + } +} + +/* Handle lbup!/lhp!/ldiu!/lwp!/sbp!/shp!/swp!. */ +static void +do16_ldst_imm_insn (char *str) +{ + char data_exp[MAX_LITERAL_POOL_SIZE]; + int reg_rd; + char *dataptr = NULL, *pp = NULL; + int cnt = 0; + int assign_data = (int) FAIL; + unsigned int ldst_func; + + skip_whitespace (str); + + if (((reg_rd = reglow_required_here (&str, 8)) == (int) FAIL) + || (skip_past_comma (&str) == (int) FAIL)) + return; + + skip_whitespace (str); + dataptr = str; + + while ((*dataptr != '\0') && (*dataptr != '|') && (cnt <= MAX_LITERAL_POOL_SIZE)) + { + data_exp[cnt] = *dataptr; + dataptr++; + cnt++; + } + + data_exp[cnt] = '\0'; + pp = &data_exp[0]; + + str = dataptr; + + ldst_func = inst.instruction & LDST16_RI_MASK; + if (ldst_func == N16_LIU) + assign_data = exp_ldst_offset (&pp, 0, _IMM8); + else if (ldst_func == N16_LHP || ldst_func == N16_SHP) + assign_data = exp_ldst_offset (&pp, 3, _IMM5_RSHIFT_1); + else if (ldst_func == N16_LWP || ldst_func == N16_SWP) + assign_data = exp_ldst_offset (&pp, 3, _IMM5_RSHIFT_2); + else + assign_data = exp_ldst_offset (&pp, 3, _IMM5); + + if ((assign_data == (int) FAIL) || (end_of_line (pp) == (int) FAIL)) + return; + else + { + if ((inst.instruction & 0x7000) == N16_LIU) + { + inst.relax_inst |= ((inst.instruction >> 8) & 0xf) << 20 + | ((inst.instruction & 0xff) << 1); + } + else if (((inst.instruction & 0x7007) == N16_LHP) + || ((inst.instruction & 0x7007) == N16_SHP)) + { + inst.relax_inst |= ((inst.instruction >> 8) & 0xf) << 20 | 2 << 15 + | (((inst.instruction >> 3) & 0x1f) << 1); + } + else if (((inst.instruction & 0x7007) == N16_LWP) + || ((inst.instruction & 0x7007) == N16_SWP)) + { + inst.relax_inst |= ((inst.instruction >> 8) & 0xf) << 20 | 2 << 15 + | (((inst.instruction >> 3) & 0x1f) << 2); + } + else if (((inst.instruction & 0x7007) == N16_LBUP) + || ((inst.instruction & 0x7007) == N16_SBP)) + { + inst.relax_inst |= ((inst.instruction >> 8) & 0xf) << 20 | 2 << 15 + | (((inst.instruction >> 3) & 0x1f)); + } + + inst.relax_size = 4; + } +} + +static void +do16_push_pop (char *str) +{ + int reg_rd; + int H_bit_mask = 0; + + skip_whitespace (str); + if (((reg_rd = reg_required_here (&str, 8, REG_TYPE_SCORE)) == (int) FAIL) + || (skip_past_comma (&str) == (int) FAIL)) + return; + + if (reg_rd >= 16) + H_bit_mask = 1; + + /* reg_required_here will change bit 12 of opcode, so we must restore bit 12. */ + inst.instruction &= ~(1 << 12); + + inst.instruction |= H_bit_mask << 7; + + if (*str == '[') + { + int reg; + + str++; + skip_whitespace (str); + if ((reg = reg_required_here (&str, 4, REG_TYPE_SCORE)) == (int) FAIL) + return; + else if (reg > 7) + { + if (!inst.error) + inst.error = _("base register nums are over 3 bit"); + + return; + } + + skip_whitespace (str); + if ((*str++ != ']') || (end_of_line (str) == (int) FAIL)) + { + if (!inst.error) + inst.error = _("missing ]"); + + return; + } + + /* pop! */ + if ((inst.instruction & 0xf) == 0xa) + { + if (H_bit_mask) + { + inst.relax_inst |= ((((inst.instruction >> 8) & 0xf) | 0x10) << 20) + | (((inst.instruction >> 4) & 0x7) << 15) | (4 << 3); + } + else + { + inst.relax_inst |= (((inst.instruction >> 8) & 0xf) << 20) + | (((inst.instruction >> 4) & 0x7) << 15) | (4 << 3); + } + } + /* push! */ + else + { + if (H_bit_mask) + { + inst.relax_inst |= ((((inst.instruction >> 8) & 0xf) | 0x10) << 20) + | (((inst.instruction >> 4) & 0x7) << 15) | (((-4) & 0xfff) << 3); + } + else + { + inst.relax_inst |= (((inst.instruction >> 8) & 0xf) << 20) + | (((inst.instruction >> 4) & 0x7) << 15) | (((-4) & 0xfff) << 3); + } + } + inst.relax_size = 4; + } + else + { + inst.error = BAD_ARGS; + } +} + +/* Handle lcb/lcw/lce/scb/scw/sce. */ +static void +do_ldst_unalign (char *str) +{ + int conflict_reg; + + if (university_version == 1) + { + inst.error = ERR_FOR_SCORE5U_ATOMIC; + return; + } + + skip_whitespace (str); + + /* lcb/scb [rA]+. */ + if (*str == '[') + { + str++; + skip_whitespace (str); + + if (reg_required_here (&str, 15, REG_TYPE_SCORE) == (int) FAIL) + return; + + if (*str++ == ']') + { + if (*str++ != '+') + { + inst.error = _("missing +"); + return; + } + } + else + { + inst.error = _("missing ]"); + return; + } + + if (end_of_line (str) == (int) FAIL) + return; + } + /* lcw/lce/scb/sce rD, [rA]+. */ + else + { + if (((conflict_reg = reg_required_here (&str, 20, REG_TYPE_SCORE)) == (int) FAIL) + || (skip_past_comma (&str) == (int) FAIL)) + { + return; + } + + skip_whitespace (str); + if (*str++ == '[') + { + int reg; + + skip_whitespace (str); + if ((reg = reg_required_here (&str, 15, REG_TYPE_SCORE)) == (int) FAIL) + { + return; + } + + /* Conflicts can occur on stores as well as loads. */ + conflict_reg = (conflict_reg == reg); + skip_whitespace (str); + if (*str++ == ']') + { + unsigned int ldst_func = inst.instruction & LDST_UNALIGN_MASK; + + if (*str++ == '+') + { + if (conflict_reg) + { + as_warn (_("%s register same as write-back base"), + ((ldst_func & UA_LCE) || (ldst_func & UA_LCW) + ? _("destination") : _("source"))); + } + } + else + { + inst.error = _("missing +"); + return; + } + + if (end_of_line (str) == (int) FAIL) + return; + } + else + { + inst.error = _("missing ]"); + return; + } + } + else + { + inst.error = BAD_ARGS; + return; + } + } +} + +/* Handle alw/asw. */ +static void +do_ldst_atomic (char *str) +{ + if (university_version == 1) + { + inst.error = ERR_FOR_SCORE5U_ATOMIC; + return; + } + + skip_whitespace (str); + + if ((reg_required_here (&str, 20, REG_TYPE_SCORE) == (int) FAIL) + || (skip_past_comma (&str) == (int) FAIL)) + { + return; + } + else + { + + skip_whitespace (str); + if (*str++ == '[') + { + int reg; + + skip_whitespace (str); + if ((reg = reg_required_here (&str, 15, REG_TYPE_SCORE)) == (int) FAIL) + { + return; + } + + skip_whitespace (str); + if (*str++ != ']') + { + inst.error = _("missing ]"); + return; + } + + end_of_line (str); + } + else + inst.error = BAD_ARGS; + } +} + +static void +build_relax_frag (struct score_it fix_insts[RELAX_INST_NUM], int fix_num ATTRIBUTE_UNUSED, + struct score_it var_insts[RELAX_INST_NUM], int var_num, + symbolS *add_symbol) +{ + int i; + char *p; + fixS *fixp = NULL; + fixS *cur_fixp = NULL; + long where; + struct score_it inst_main; + + memcpy (&inst_main, &fix_insts[0], sizeof (struct score_it)); + + /* Adjust instruction opcode and to be relaxed instruction opcode. */ + inst_main.instruction = adjust_paritybit (inst_main.instruction, GET_INSN_CLASS (inst_main.type)); + inst_main.type = Insn_PIC; + + for (i = 0; i < var_num; i++) + { + inst_main.relax_size += var_insts[i].size; + var_insts[i].instruction = adjust_paritybit (var_insts[i].instruction, + GET_INSN_CLASS (var_insts[i].type)); + } + + /* Check data dependency. */ + handle_dependency (&inst_main); + + /* Start a new frag if frag_now is not empty. */ + if (frag_now_fix () != 0) + { + if (!frag_now->tc_frag_data.is_insn) + { + frag_wane (frag_now); + } + frag_new (0); + } + frag_grow (20); + + /* Write fr_fix part. */ + p = frag_more (inst_main.size); + md_number_to_chars (p, inst_main.instruction, inst_main.size); + + if (inst_main.reloc.type != BFD_RELOC_NONE) + fixp = fix_new_score (frag_now, p - frag_now->fr_literal, inst_main.size, + &inst_main.reloc.exp, inst_main.reloc.pc_rel, inst_main.reloc.type); + + frag_now->tc_frag_data.fixp = fixp; + cur_fixp = frag_now->tc_frag_data.fixp; + +#ifdef OBJ_ELF + dwarf2_emit_insn (inst_main.size); +#endif + + where = p - frag_now->fr_literal + inst_main.size; + for (i = 0; i < var_num; i++) + { + if (i > 0) + where += var_insts[i - 1].size; + + if (var_insts[i].reloc.type != BFD_RELOC_NONE) + { + fixp = fix_new_score (frag_now, where, var_insts[i].size, + &var_insts[i].reloc.exp, var_insts[i].reloc.pc_rel, + var_insts[i].reloc.type); + if (fixp) + { + if (cur_fixp) + { + cur_fixp->fx_next = fixp; + cur_fixp = cur_fixp->fx_next; + } + else + { + frag_now->tc_frag_data.fixp = fixp; + cur_fixp = frag_now->tc_frag_data.fixp; + } + } + } + } + + p = frag_var (rs_machine_dependent, inst_main.relax_size + RELAX_PAD_BYTE, 0, + RELAX_ENCODE (inst_main.size, inst_main.relax_size, inst_main.type, + 0, inst_main.size, 0), add_symbol, 0, NULL); + + /* Write fr_var part. + no calling gen_insn_frag, no fixS will be generated. */ + for (i = 0; i < var_num; i++) + { + md_number_to_chars (p, var_insts[i].instruction, var_insts[i].size); + p += var_insts[i].size; + } + /* Set bwarn as -1, so macro instruction itself will not be generated frag. */ + inst.bwarn = -1; +} + +/* Build a relax frag for la instruction when generating PIC, + external symbol first and local symbol second. */ + +static void +build_la_pic (int reg_rd, expressionS exp) +{ + symbolS *add_symbol = exp.X_add_symbol; + offsetT add_number = exp.X_add_number; + struct score_it fix_insts[RELAX_INST_NUM]; + struct score_it var_insts[RELAX_INST_NUM]; + int fix_num = 0; + int var_num = 0; + char tmp[MAX_LITERAL_POOL_SIZE]; + int r1_bak; + + r1_bak = nor1; + nor1 = 0; + + if (add_number == 0) + { + fix_num = 1; + var_num = 2; + + /* For an external symbol, only one insn is generated; + For a local symbol, two insns are generated. */ + /* Fix part + For an external symbol: lw rD, <sym>($gp) + (BFD_RELOC_SCORE_GOT15 or BFD_RELOC_SCORE_CALL15) */ + sprintf (tmp, "lw_pic r%d, %s", reg_rd, add_symbol->bsym->name); + if (append_insn (tmp, FALSE) == (int) FAIL) + return; + + if (reg_rd == PIC_CALL_REG) + inst.reloc.type = BFD_RELOC_SCORE_CALL15; + memcpy (&fix_insts[0], &inst, sizeof (struct score_it)); + + /* Var part + For a local symbol : + lw rD, <sym>($gp) (BFD_RELOC_SCORE_GOT15) + addi rD, <sym> (BFD_RELOC_GOT_LO16) */ + inst.reloc.type = BFD_RELOC_SCORE_GOT15; + memcpy (&var_insts[0], &inst, sizeof (struct score_it)); + sprintf (tmp, "addi_s_pic r%d, %s", reg_rd, add_symbol->bsym->name); + if (append_insn (tmp, FALSE) == (int) FAIL) + return; + + memcpy (&var_insts[1], &inst, sizeof (struct score_it)); + build_relax_frag (fix_insts, fix_num, var_insts, var_num, add_symbol); + } + else if (add_number >= -0x8000 && add_number <= 0x7fff) + { + /* Insn 1: lw rD, <sym>($gp) (BFD_RELOC_SCORE_GOT15) */ + sprintf (tmp, "lw_pic r%d, %s", reg_rd, add_symbol->bsym->name); + if (append_insn (tmp, TRUE) == (int) FAIL) + return; + + /* Insn 2 */ + fix_num = 1; + var_num = 1; + /* Fix part + For an external symbol: addi rD, <constant> */ + sprintf (tmp, "addi r%d, %d", reg_rd, (int)add_number); + if (append_insn (tmp, FALSE) == (int) FAIL) + return; + + memcpy (&fix_insts[0], &inst, sizeof (struct score_it)); + + /* Var part + For a local symbol: addi rD, <sym>+<constant> (BFD_RELOC_GOT_LO16) */ + sprintf (tmp, "addi_s_pic r%d, %s + %d", reg_rd, add_symbol->bsym->name, (int)add_number); + if (append_insn (tmp, FALSE) == (int) FAIL) + return; + + memcpy (&var_insts[0], &inst, sizeof (struct score_it)); + build_relax_frag (fix_insts, fix_num, var_insts, var_num, add_symbol); + } + else + { + int hi = (add_number >> 16) & 0x0000FFFF; + int lo = add_number & 0x0000FFFF; + + /* Insn 1: lw rD, <sym>($gp) (BFD_RELOC_SCORE_GOT15) */ + sprintf (tmp, "lw_pic r%d, %s", reg_rd, add_symbol->bsym->name); + if (append_insn (tmp, TRUE) == (int) FAIL) + return; + + /* Insn 2 */ + fix_num = 1; + var_num = 1; + /* Fix part + For an external symbol: ldis r1, HI%<constant> */ + sprintf (tmp, "ldis r1, %d", hi); + if (append_insn (tmp, FALSE) == (int) FAIL) + return; + + memcpy (&fix_insts[0], &inst, sizeof (struct score_it)); + + /* Var part + For a local symbol: ldis r1, HI%<constant> + but, if lo is outof 16 bit, make hi plus 1 */ + if ((lo < -0x8000) || (lo > 0x7fff)) + { + hi += 1; + } + sprintf (tmp, "ldis_pic r1, %d", hi); + if (append_insn (tmp, FALSE) == (int) FAIL) + return; + + memcpy (&var_insts[0], &inst, sizeof (struct score_it)); + build_relax_frag (fix_insts, fix_num, var_insts, var_num, add_symbol); + + /* Insn 3 */ + fix_num = 1; + var_num = 1; + /* Fix part + For an external symbol: ori r1, LO%<constant> */ + sprintf (tmp, "ori r1, %d", lo); + if (append_insn (tmp, FALSE) == (int) FAIL) + return; + + memcpy (&fix_insts[0], &inst, sizeof (struct score_it)); + + /* Var part + For a local symbol: addi r1, <sym>+LO%<constant> (BFD_RELOC_GOT_LO16) */ + sprintf (tmp, "addi_u_pic r1, %s + %d", add_symbol->bsym->name, lo); + if (append_insn (tmp, FALSE) == (int) FAIL) + return; + + memcpy (&var_insts[0], &inst, sizeof (struct score_it)); + build_relax_frag (fix_insts, fix_num, var_insts, var_num, add_symbol); + + /* Insn 4: add rD, rD, r1 */ + sprintf (tmp, "add r%d, r%d, r1", reg_rd, reg_rd); + if (append_insn (tmp, TRUE) == (int) FAIL) + return; + + /* Set bwarn as -1, so macro instruction itself will not be generated frag. */ + inst.bwarn = -1; + } + + nor1 = r1_bak; +} + +/* Handle la. */ +static void +do_macro_la_rdi32 (char *str) +{ + int reg_rd; + + skip_whitespace (str); + if ((reg_rd = reg_required_here (&str, 20, REG_TYPE_SCORE)) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL) + { + return; + } + else + { + char append_str[MAX_LITERAL_POOL_SIZE]; + char *keep_data = str; + + /* la rd, simm16. */ + if (data_op2 (&str, 1, _SIMM16_LA) != (int) FAIL) + { + end_of_line (str); + return; + } + /* la rd, imm32 or la rd, label. */ + else + { + SET_INSN_ERROR (NULL); + str = keep_data; + if ((data_op2 (&str, 1, _VALUE_HI16) == (int) FAIL) + || (end_of_line (str) == (int) FAIL)) + { + return; + } + else + { + if ((score_pic == NO_PIC) || (!inst.reloc.exp.X_add_symbol)) + { + sprintf (append_str, "ld_i32hi r%d, %s", reg_rd, keep_data); + if (append_insn (append_str, TRUE) == (int) FAIL) + return; + + sprintf (append_str, "ld_i32lo r%d, %s", reg_rd, keep_data); + if (append_insn (append_str, TRUE) == (int) FAIL) + return; + } + else + { + assert (inst.reloc.exp.X_add_symbol); + build_la_pic (reg_rd, inst.reloc.exp); + } + + /* Set bwarn as -1, so macro instruction itself will not be generated frag. */ + inst.bwarn = -1; + } + } + } +} + +/* Handle li. */ +static void +do_macro_li_rdi32 (char *str){ + + int reg_rd; + + skip_whitespace (str); + if ((reg_rd = reg_required_here (&str, 20, REG_TYPE_SCORE)) == (int) FAIL + || skip_past_comma (&str) == (int) FAIL) + { + return; + } + else + { + char *keep_data = str; + + /* li rd, simm16. */ + if (data_op2 (&str, 1, _SIMM16_LA) != (int) FAIL) + { + end_of_line (str); + return; + } + /* li rd, imm32. */ + else + { + char append_str[MAX_LITERAL_POOL_SIZE]; + + str = keep_data; + + if ((data_op2 (&str, 1, _VALUE_HI16) == (int) FAIL) + || (end_of_line (str) == (int) FAIL)) + { + return; + } + else if (inst.reloc.exp.X_add_symbol) + { + inst.error = _("li rd label isn't correct instruction form"); + return; + } + else + { + sprintf (append_str, "ld_i32hi r%d, %s", reg_rd, keep_data); + + if (append_insn (append_str, TRUE) == (int) FAIL) + return; + else + { + sprintf (append_str, "ld_i32lo r%d, %s", reg_rd, keep_data); + if (append_insn (append_str, TRUE) == (int) FAIL) + return; + + /* Set bwarn as -1, so macro instruction itself will not be generated frag. */ + inst.bwarn = -1; + } + } + } + } +} + +/* Handle mul/mulu/div/divu/rem/remu. */ +static void +do_macro_mul_rdrsrs (char *str) +{ + int reg_rd; + int reg_rs1; + int reg_rs2; + char *backupstr; + char append_str[MAX_LITERAL_POOL_SIZE]; + + if (university_version == 1) + as_warn ("%s", ERR_FOR_SCORE5U_MUL_DIV); + + strcpy (append_str, str); + backupstr = append_str; + skip_whitespace (backupstr); + if (((reg_rd = reg_required_here (&backupstr, -1, REG_TYPE_SCORE)) == (int) FAIL) + || (skip_past_comma (&backupstr) == (int) FAIL) + || ((reg_rs1 = reg_required_here (&backupstr, -1, REG_TYPE_SCORE)) == (int) FAIL)) + { + inst.error = BAD_ARGS; + return; + } + + if (skip_past_comma (&backupstr) == (int) FAIL) + { + /* rem/remu rA, rB is error format. */ + if (strcmp (inst.name, "rem") == 0 || strcmp (inst.name, "remu") == 0) + { + SET_INSN_ERROR (BAD_ARGS); + } + else + { + SET_INSN_ERROR (NULL); + do_rsrs (str); + } + return; + } + else + { + SET_INSN_ERROR (NULL); + if (((reg_rs2 = reg_required_here (&backupstr, -1, REG_TYPE_SCORE)) == (int) FAIL) + || (end_of_line (backupstr) == (int) FAIL)) + { + return; + } + else + { + char append_str1[MAX_LITERAL_POOL_SIZE]; + + if (strcmp (inst.name, "rem") == 0) + { + sprintf (append_str, "mul r%d, r%d", reg_rs1, reg_rs2); + sprintf (append_str1, "mfceh r%d", reg_rd); + } + else if (strcmp (inst.name, "remu") == 0) + { + sprintf (append_str, "mulu r%d, r%d", reg_rs1, reg_rs2); + sprintf (append_str1, "mfceh r%d", reg_rd); + } + else + { + sprintf (append_str, "%s r%d, r%d", inst.name, reg_rs1, reg_rs2); + sprintf (append_str1, "mfcel r%d", reg_rd); + } + + /* Output mul/mulu or div/divu or rem/remu. */ + if (append_insn (append_str, TRUE) == (int) FAIL) + return; + + /* Output mfcel or mfceh. */ + if (append_insn (append_str1, TRUE) == (int) FAIL) + return; + + /* Set bwarn as -1, so macro instruction itself will not be generated frag. */ + inst.bwarn = -1; + } + } +} + +static void +exp_macro_ldst_abs (char *str) +{ + int reg_rd; + char *backupstr, *tmp; + char append_str[MAX_LITERAL_POOL_SIZE]; + char verifystr[MAX_LITERAL_POOL_SIZE]; + struct score_it inst_backup; + int r1_bak = 0; + + r1_bak = nor1; + nor1 = 0; + memcpy (&inst_backup, &inst, sizeof (struct score_it)); + + strcpy (verifystr, str); + backupstr = verifystr; + skip_whitespace (backupstr); + if ((reg_rd = reg_required_here (&backupstr, -1, REG_TYPE_SCORE)) == (int) FAIL) + return; + + tmp = backupstr; + if (skip_past_comma (&backupstr) == (int) FAIL) + return; + + backupstr = tmp; + sprintf (append_str, "li r1 %s", backupstr); + append_insn (append_str, TRUE); + + memcpy (&inst, &inst_backup, sizeof (struct score_it)); + sprintf (append_str, " r%d, [r1,0]", reg_rd); + do_ldst_insn (append_str); + + nor1 = r1_bak; +} + +static int +nopic_need_relax (symbolS * sym, int before_relaxing) +{ + if (sym == NULL) + return 0; + else if (USE_GLOBAL_POINTER_OPT && g_switch_value > 0) + { + const char *symname; + const char *segname; + + /* Find out whether this symbol can be referenced off the $gp + register. It can be if it is smaller than the -G size or if + it is in the .sdata or .sbss section. Certain symbols can + not be referenced off the $gp, although it appears as though + they can. */ + symname = S_GET_NAME (sym); + if (symname != (const char *)NULL + && (strcmp (symname, "eprol") == 0 + || strcmp (symname, "etext") == 0 + || strcmp (symname, "_gp") == 0 + || strcmp (symname, "edata") == 0 + || strcmp (symname, "_fbss") == 0 + || strcmp (symname, "_fdata") == 0 + || strcmp (symname, "_ftext") == 0 + || strcmp (symname, "end") == 0 + || strcmp (symname, GP_DISP_LABEL) == 0)) + { + return 1; + } + else if ((!S_IS_DEFINED (sym) || S_IS_COMMON (sym)) && (0 + /* We must defer this decision until after the whole file has been read, + since there might be a .extern after the first use of this symbol. */ + || (before_relaxing + && S_GET_VALUE (sym) == 0) + || (S_GET_VALUE (sym) != 0 + && S_GET_VALUE (sym) <= g_switch_value))) + { + return 0; + } + + segname = segment_name (S_GET_SEGMENT (sym)); + return (strcmp (segname, ".sdata") != 0 + && strcmp (segname, ".sbss") != 0 + && strncmp (segname, ".sdata.", 7) != 0 + && strncmp (segname, ".gnu.linkonce.s.", 16) != 0); + } + /* We are not optimizing for the $gp register. */ + else + return 1; +} + +/* Build a relax frag for lw/st instruction when generating PIC, + external symbol first and local symbol second. */ + +static void +build_lwst_pic (int reg_rd, expressionS exp, const char *insn_name) +{ + symbolS *add_symbol = exp.X_add_symbol; + int add_number = exp.X_add_number; + struct score_it fix_insts[RELAX_INST_NUM]; + struct score_it var_insts[RELAX_INST_NUM]; + int fix_num = 0; + int var_num = 0; + char tmp[MAX_LITERAL_POOL_SIZE]; + int r1_bak; + + r1_bak = nor1; + nor1 = 0; + + if ((add_number == 0) || (add_number >= -0x8000 && add_number <= 0x7fff)) + { + fix_num = 1; + var_num = 2; + + /* For an external symbol, two insns are generated; + For a local symbol, three insns are generated. */ + /* Fix part + For an external symbol: lw rD, <sym>($gp) + (BFD_RELOC_SCORE_GOT15) */ + sprintf (tmp, "lw_pic r1, %s", add_symbol->bsym->name); + if (append_insn (tmp, FALSE) == (int) FAIL) + return; + + memcpy (&fix_insts[0], &inst, sizeof (struct score_it)); + + /* Var part + For a local symbol : + lw rD, <sym>($gp) (BFD_RELOC_SCORE_GOT15) + addi rD, <sym> (BFD_RELOC_GOT_LO16) */ + inst.reloc.type = BFD_RELOC_SCORE_GOT15; + memcpy (&var_insts[0], &inst, sizeof (struct score_it)); + sprintf (tmp, "addi_s_pic r1, %s", add_symbol->bsym->name); + if (append_insn (tmp, FALSE) == (int) FAIL) + return; + + memcpy (&var_insts[1], &inst, sizeof (struct score_it)); + build_relax_frag (fix_insts, fix_num, var_insts, var_num, add_symbol); + + /* Insn 2 or Insn 3: lw/st rD, [r1, constant] */ + sprintf (tmp, "%s r%d, [r1, %d]", insn_name, reg_rd, add_number); + if (append_insn (tmp, TRUE) == (int) FAIL) + return; + + /* Set bwarn as -1, so macro instruction itself will not be generated frag. */ + inst.bwarn = -1; + } + else + { + inst.error = _("PIC code offset overflow (max 16 signed bits)"); + return; + } + + nor1 = r1_bak; +} + +static void +do_macro_ldst_label (char *str) +{ + int i; + int ldst_gp_p = 0; + int reg_rd; + int r1_bak; + char *backup_str; + char *label_str; + char *absolute_value; + char append_str[3][MAX_LITERAL_POOL_SIZE]; + char verifystr[MAX_LITERAL_POOL_SIZE]; + struct score_it inst_backup; + struct score_it inst_expand[3]; + struct score_it inst_main; + + memcpy (&inst_backup, &inst, sizeof (struct score_it)); + strcpy (verifystr, str); + backup_str = verifystr; + + skip_whitespace (backup_str); + if ((reg_rd = reg_required_here (&backup_str, -1, REG_TYPE_SCORE)) == (int) FAIL) + return; + + if (skip_past_comma (&backup_str) == (int) FAIL) + return; + + label_str = backup_str; + + /* Ld/st rD, [rA, imm] ld/st rD, [rA]+, imm ld/st rD, [rA, imm]+. */ + if (*backup_str == '[') + { + inst.type = Rd_rvalueRs_preSI12; + do_ldst_insn (str); + return; + } + + /* Ld/st rD, imm. */ + absolute_value = backup_str; + inst.type = Rd_rvalueRs_SI15; + if ((my_get_expression (&inst.reloc.exp, &backup_str) == (int) FAIL) + || (validate_immediate (inst.reloc.exp.X_add_number, _VALUE, 0) == (int) FAIL) + || (end_of_line (backup_str) == (int) FAIL)) + { + return; + } + else + { + if (inst.reloc.exp.X_add_symbol == 0) + { + memcpy (&inst, &inst_backup, sizeof (struct score_it)); + exp_macro_ldst_abs (str); + return; + } + } + + /* Ld/st rD, label. */ + inst.type = Rd_rvalueRs_SI15; + backup_str = absolute_value; + if ((data_op2 (&backup_str, 1, _GP_IMM15) == (int) FAIL) + || (end_of_line (backup_str) == (int) FAIL)) + { + return; + } + else + { + if (inst.reloc.exp.X_add_symbol == 0) + { + if (!inst.error) + inst.error = BAD_ARGS; + + return; + } + + if (score_pic == PIC) + { + int ldst_idx = 0; + ldst_idx = inst.instruction & OPC_PSEUDOLDST_MASK; + build_lwst_pic (reg_rd, inst.reloc.exp, score_ldst_insns[ldst_idx * 3 + 0].template); + return; + } + else + { + if ((inst.reloc.exp.X_add_number <= 0x3fff) + && (inst.reloc.exp.X_add_number >= -0x4000) + && (!nopic_need_relax (inst.reloc.exp.X_add_symbol, 1))) + { + int ldst_idx = 0; + + /* Assign the real opcode. */ + ldst_idx = inst.instruction & OPC_PSEUDOLDST_MASK; + inst.instruction &= ~OPC_PSEUDOLDST_MASK; + inst.instruction |= score_ldst_insns[ldst_idx * 3 + 0].value; + inst.instruction |= reg_rd << 20; + inst.instruction |= GP << 15; + inst.relax_inst = 0x8000; + inst.relax_size = 0; + ldst_gp_p = 1; + } + } + } + + /* Backup inst. */ + memcpy (&inst_main, &inst, sizeof (struct score_it)); + r1_bak = nor1; + nor1 = 0; + + /* Determine which instructions should be output. */ + sprintf (append_str[0], "ld_i32hi r1, %s", label_str); + sprintf (append_str[1], "ld_i32lo r1, %s", label_str); + sprintf (append_str[2], "%s r%d, [r1, 0]", inst_backup.name, reg_rd); + + /* Generate three instructions. + la r1, label + ld/st rd, [r1, 0] */ + for (i = 0; i < 3; i++) + { + if (append_insn (append_str[i], FALSE) == (int) FAIL) + return; + + memcpy (&inst_expand[i], &inst, sizeof (struct score_it)); + } + + if (ldst_gp_p) + { + char *p; + + /* Adjust instruction opcode and to be relaxed instruction opcode. */ + inst_main.instruction = adjust_paritybit (inst_main.instruction, GET_INSN_CLASS (inst_main.type)); + inst_main.relax_size = inst_expand[0].size + inst_expand[1].size + inst_expand[2].size; + inst_main.type = Insn_GP; + + for (i = 0; i < 3; i++) + inst_expand[i].instruction = adjust_paritybit (inst_expand[i].instruction + , GET_INSN_CLASS (inst_expand[i].type)); + + /* Check data dependency. */ + handle_dependency (&inst_main); + + /* Start a new frag if frag_now is not empty. */ + if (frag_now_fix () != 0) + { + if (!frag_now->tc_frag_data.is_insn) + frag_wane (frag_now); + + frag_new (0); + } + frag_grow (20); + + /* Write fr_fix part. */ + p = frag_more (inst_main.size); + md_number_to_chars (p, inst_main.instruction, inst_main.size); + + if (inst_main.reloc.type != BFD_RELOC_NONE) + { + fix_new_score (frag_now, p - frag_now->fr_literal, inst_main.size, + &inst_main.reloc.exp, inst_main.reloc.pc_rel, inst_main.reloc.type); + } + +#ifdef OBJ_ELF + dwarf2_emit_insn (inst_main.size); +#endif + + /* GP instruction can not do optimization, only can do relax between + 1 instruction and 3 instructions. */ + p = frag_var (rs_machine_dependent, inst_main.relax_size + RELAX_PAD_BYTE, 0, + RELAX_ENCODE (inst_main.size, inst_main.relax_size, inst_main.type, 0, 4, 0), + inst_main.reloc.exp.X_add_symbol, 0, NULL); + + /* Write fr_var part. + no calling gen_insn_frag, no fixS will be generated. */ + md_number_to_chars (p, inst_expand[0].instruction, inst_expand[0].size); + p += inst_expand[0].size; + md_number_to_chars (p, inst_expand[1].instruction, inst_expand[1].size); + p += inst_expand[1].size; + md_number_to_chars (p, inst_expand[2].instruction, inst_expand[2].size); + } + else + { + gen_insn_frag (&inst_expand[0], NULL); + gen_insn_frag (&inst_expand[1], NULL); + gen_insn_frag (&inst_expand[2], NULL); + } + nor1 = r1_bak; + + /* Set bwarn as -1, so macro instruction itself will not be generated frag. */ + inst.bwarn = -1; +} + +static void +do_lw_pic (char *str) +{ + int reg_rd; + + skip_whitespace (str); + if (((reg_rd = reg_required_here (&str, 20, REG_TYPE_SCORE)) == (int) FAIL) + || (skip_past_comma (&str) == (int) FAIL) + || (my_get_expression (&inst.reloc.exp, &str) == (int) FAIL) + || (end_of_line (str) == (int) FAIL)) + { + return; + } + else + { + if (inst.reloc.exp.X_add_symbol == 0) + { + if (!inst.error) + inst.error = BAD_ARGS; + + return; + } + + inst.instruction |= GP << 15; + inst.reloc.type = BFD_RELOC_SCORE_GOT15; + } +} + +static void +do_empty (char *str) +{ + str = str; + if (university_version == 1) + { + if (((inst.instruction & 0x3e0003ff) == 0x0c000004) + || ((inst.instruction & 0x3e0003ff) == 0x0c000024) + || ((inst.instruction & 0x3e0003ff) == 0x0c000044) + || ((inst.instruction & 0x3e0003ff) == 0x0c000064)) + { + inst.error = ERR_FOR_SCORE5U_MMU; + return; + } + } + if (end_of_line (str) == (int) FAIL) + return; + + if (inst.relax_inst != 0x8000) + { + if (inst.type == NO_OPD) + { + inst.relax_size = 2; + } + else + { + inst.relax_size = 4; + } + } +} + +static void +do_jump (char *str) +{ + char *save_in; + + skip_whitespace (str); + if (my_get_expression (&inst.reloc.exp, &str) == (int) FAIL + || end_of_line (str) == (int) FAIL) + return; + + if (inst.reloc.exp.X_add_symbol == 0) + { + inst.error = _("lacking label "); + return; + } + + if (((inst.reloc.exp.X_add_number & 0xff000000) != 0) + && ((inst.reloc.exp.X_add_number & 0xff000000) != 0xff000000)) + { + inst.error = _("invalid constant: 25 bit expression not in range -2^24..2^24"); + return; + } + + save_in = input_line_pointer; + input_line_pointer = str; + inst.reloc.type = BFD_RELOC_SCORE_JMP; + inst.reloc.pc_rel = 1; + input_line_pointer = save_in; +} + +static void +do16_jump (char *str) +{ + skip_whitespace (str); + if (my_get_expression (&inst.reloc.exp, &str) == (int) FAIL + || end_of_line (str) == (int) FAIL) + { + return; + } + else if (inst.reloc.exp.X_add_symbol == 0) + { + inst.error = _("lacking label "); + return; + } + else if (((inst.reloc.exp.X_add_number & 0xfffff800) != 0) + && ((inst.reloc.exp.X_add_number & 0xfffff800) != 0xfffff800)) + { + inst.error = _("invalid constant: 12 bit expression not in range -2^11..2^11"); + return; + } + + inst.reloc.type = BFD_RELOC_SCORE16_JMP; + inst.reloc.pc_rel = 1; +} + +static void +do_branch (char *str) +{ + unsigned long abs_value = 0; + + if (my_get_expression (&inst.reloc.exp, &str) == (int) FAIL + || end_of_line (str) == (int) FAIL) + { + return; + } + else if (inst.reloc.exp.X_add_symbol == 0) + { + inst.error = _("lacking label "); + return; + } + else if (((inst.reloc.exp.X_add_number & 0xff000000) != 0) + && ((inst.reloc.exp.X_add_number & 0xff000000) != 0xff000000)) + { + inst.error = _("invalid constant: 20 bit expression not in range -2^19..2^19"); + return; + } + + inst.reloc.type = BFD_RELOC_SCORE_BRANCH; + inst.reloc.pc_rel = 1; + + /* Branch 32 offset field : 20 bit, 16 bit branch offset field : 8 bit. */ + inst.instruction |= (inst.reloc.exp.X_add_number & 0x3fe) | ((inst.reloc.exp.X_add_number & 0xffc00) << 5); + + /* Compute 16 bit branch instruction. */ + if ((inst.relax_inst != 0x8000) && (abs_value & 0xfffffe00) == 0) + { + inst.relax_inst |= (((inst.instruction >> 10) & 0xf) << 8); + inst.relax_inst |= ((inst.reloc.exp.X_add_number >> 1) & 0xff); + inst.relax_size = 2; + } + else + { + inst.relax_inst = 0x8000; + } +} + +static void +do16_branch (char *str) +{ + if ((my_get_expression (&inst.reloc.exp, &str) == (int) FAIL + || end_of_line (str) == (int) FAIL)) + { + ; + } + else if (inst.reloc.exp.X_add_symbol == 0) + { + inst.error = _("lacking label"); + } + else if (((inst.reloc.exp.X_add_number & 0xffffff00) != 0) + && ((inst.reloc.exp.X_add_number & 0xffffff00) != 0xffffff00)) + { + inst.error = _("invalid constant: 9 bit expression not in range -2^8..2^8"); + } + else + { + inst.reloc.type = BFD_RELOC_SCORE16_BRANCH; + inst.reloc.pc_rel = 1; + inst.instruction |= ((inst.reloc.exp.X_add_number >> 1) & 0xff); + } +} + +/* Iterate over the base tables to create the instruction patterns. */ +static void +build_score_ops_hsh (void) +{ + unsigned int i; + static struct obstack insn_obstack; + + obstack_begin (&insn_obstack, 4000); + for (i = 0; i < sizeof (score_insns) / sizeof (struct asm_opcode); i++) + { + const struct asm_opcode *insn = score_insns + i; + unsigned len = strlen (insn->template); + struct asm_opcode *new; + char *template; + new = obstack_alloc (&insn_obstack, sizeof (struct asm_opcode)); + template = obstack_alloc (&insn_obstack, len + 1); + + strcpy (template, insn->template); + new->template = template; + new->parms = insn->parms; + new->value = insn->value; + new->relax_value = insn->relax_value; + new->type = insn->type; + new->bitmask = insn->bitmask; + hash_insert (score_ops_hsh, new->template, (void *) new); + } +} + +static void +build_dependency_insn_hsh (void) +{ + unsigned int i; + static struct obstack dependency_obstack; + + obstack_begin (&dependency_obstack, 4000); + for (i = 0; i < sizeof (insn_to_dependency_table) / sizeof (insn_to_dependency_table[0]); i++) + { + const struct insn_to_dependency *tmp = insn_to_dependency_table + i; + unsigned len = strlen (tmp->insn_name); + struct insn_to_dependency *new; + + new = obstack_alloc (&dependency_obstack, sizeof (struct insn_to_dependency)); + new->insn_name = obstack_alloc (&dependency_obstack, len + 1); + + strcpy (new->insn_name, tmp->insn_name); + new->type = tmp->type; + hash_insert (dependency_insn_hsh, new->insn_name, (void *) new); + } +} + +/* Turn an integer of n bytes (in val) into a stream of bytes appropriate + for use in the a.out file, and stores them in the array pointed to by buf. + This knows about the endian-ness of the target machine and does + THE RIGHT THING, whatever it is. Possible values for n are 1 (byte) + 2 (short) and 4 (long) Floating numbers are put out as a series of + LITTLENUMS (shorts, here at least). */ + +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); +} + +static valueT +md_chars_to_number (char *buf, int n) +{ + valueT result = 0; + unsigned char *where = (unsigned char *)buf; + + if (target_big_endian) + { + while (n--) + { + result <<= 8; + result |= (*where++ & 255); + } + } + else + { + while (n--) + { + result <<= 8; + result |= (where[n] & 255); + } + } + + return result; +} + +/* 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. + + Note that fp constants aren't represent in the normal way on the ARM. + In big endian mode, things are as expected. However, in little endian + mode fp constants are big-endian word-wise, and little-endian byte-wise + within the words. For example, (double) 1.1 in big endian mode is + the byte sequence 3f f1 99 99 99 99 99 9a, and in little endian mode is + the byte sequence 99 99 f1 3f 9a 99 99 99. */ + +char * +md_atof (int type, char *litP, int *sizeP) +{ + int prec; + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + char *t; + int i; + + switch (type) + { + case 'f': + case 'F': + case 's': + case 'S': + prec = 2; + break; + case 'd': + case 'D': + case 'r': + case 'R': + prec = 4; + break; + case 'x': + case 'X': + case 'p': + case 'P': + prec = 6; + break; + default: + *sizeP = 0; + return _("bad call to MD_ATOF()"); + } + + t = atof_ieee (input_line_pointer, type, words); + if (t) + input_line_pointer = t; + *sizeP = prec * 2; + + if (target_big_endian) + { + for (i = 0; i < prec; i++) + { + md_number_to_chars (litP, (valueT) words[i], 2); + litP += 2; + } + } + else + { + for (i = 0; i < prec; i += 2) + { + md_number_to_chars (litP, (valueT) words[i + 1], 2); + md_number_to_chars (litP + 2, (valueT) words[i], 2); + litP += 4; + } + } + + return 0; +} + +/* Return true if the given symbol should be considered local for PIC. */ + +static bfd_boolean +pic_need_relax (symbolS *sym, asection *segtype) +{ + asection *symsec; + bfd_boolean linkonce; + + /* Handle the case of a symbol equated to another symbol. */ + while (symbol_equated_reloc_p (sym)) + { + symbolS *n; + + /* It's possible to get a loop here in a badly written + program. */ + n = symbol_get_value_expression (sym)->X_add_symbol; + if (n == sym) + break; + sym = n; + } + + symsec = S_GET_SEGMENT (sym); + + /* duplicate the test for LINK_ONCE sections as in adjust_reloc_syms */ + linkonce = FALSE; + if (symsec != segtype && ! S_IS_LOCAL (sym)) + { + if ((bfd_get_section_flags (stdoutput, symsec) & SEC_LINK_ONCE) != 0) + linkonce = TRUE; + + /* The GNU toolchain uses an extension for ELF: a section + beginning with the magic string .gnu.linkonce is a linkonce + section. */ + if (strncmp (segment_name (symsec), ".gnu.linkonce", + sizeof ".gnu.linkonce" - 1) == 0) + linkonce = TRUE; + } + + /* This must duplicate the test in adjust_reloc_syms. */ + return (symsec != &bfd_und_section + && symsec != &bfd_abs_section + && ! bfd_is_com_section (symsec) + && !linkonce +#ifdef OBJ_ELF + /* A global or weak symbol is treated as external. */ + && (OUTPUT_FLAVOR != bfd_target_elf_flavour + || (! S_IS_WEAK (sym) && ! S_IS_EXTERNAL (sym))) +#endif + ); +} + +static int +judge_size_before_relax (fragS * fragp, asection *sec) +{ + int change = 0; + + if (score_pic == NO_PIC) + change = nopic_need_relax (fragp->fr_symbol, 0); + else + change = pic_need_relax (fragp->fr_symbol, sec); + + if (change == 1) + { + /* Only at the first time determining whether GP instruction relax should be done, + return the difference between insntruction size and instruction relax size. */ + if (fragp->fr_opcode == NULL) + { + fragp->fr_fix = RELAX_NEW (fragp->fr_subtype); + fragp->fr_opcode = fragp->fr_literal + RELAX_RELOC1 (fragp->fr_subtype); + return RELAX_NEW (fragp->fr_subtype) - RELAX_OLD (fragp->fr_subtype); + } + } + + return 0; +} + +/* In this function, we determine whether GP instruction should do relaxation, + for the label being against was known now. + Doing this here but not in md_relax_frag() can induce iteration times + in stage of doing relax. */ +int +md_estimate_size_before_relax (fragS * fragp, asection * sec ATTRIBUTE_UNUSED) +{ + if ((RELAX_TYPE (fragp->fr_subtype) == Insn_GP) + || (RELAX_TYPE (fragp->fr_subtype) == Insn_PIC)) + return judge_size_before_relax (fragp, sec); + + return 0; +} + +static int +b32_relax_to_b16 (fragS * fragp) +{ + int grows = 0; + int relaxable_p = 0; + int old; + int new; + int frag_addr = fragp->fr_address + fragp->insn_addr; + + addressT symbol_address = 0; + symbolS *s; + offsetT offset; + unsigned long value; + unsigned long abs_value; + + /* FIXME : here may be able to modify better . + I don't know how to get the fragp's section , + so in relax stage , it may be wrong to calculate the symbol's offset when the frag's section + is different from the symbol's. */ + + old = RELAX_OLD (fragp->fr_subtype); + new = RELAX_NEW (fragp->fr_subtype); + relaxable_p = RELAX_OPT (fragp->fr_subtype); + + s = fragp->fr_symbol; + /* b/bl immediate */ + if (s == NULL) + frag_addr = 0; + else + { + if (s->bsym != 0) + symbol_address = (addressT) s->sy_frag->fr_address; + } + + value = md_chars_to_number (fragp->fr_literal, INSN_SIZE); + + /* b 32's offset : 20 bit, b 16's tolerate field : 0xff. */ + offset = ((value & 0x3ff0000) >> 6) | (value & 0x3fe); + if ((offset & 0x80000) == 0x80000) + offset |= 0xfff00000; + + abs_value = offset + symbol_address - frag_addr; + if ((abs_value & 0x80000000) == 0x80000000) + abs_value = 0xffffffff - abs_value + 1; + + /* Relax branch 32 to branch 16. */ + if (relaxable_p && (s->bsym != NULL) && ((abs_value & 0xffffff00) == 0) + && (S_IS_DEFINED (s) && !S_IS_COMMON (s) && !S_IS_EXTERNAL (s))) + { + /* do nothing. */ + } + else + { + /* Branch 32 can not be relaxed to b 16, so clear OPT bit. */ + fragp->fr_opcode = NULL; + fragp->fr_subtype = RELAX_OPT_CLEAR (fragp->fr_subtype); + } + + return grows; +} + +/* Main purpose is to determine whether one frag should do relax. + frag->fr_opcode indicates this point. */ + +int +score_relax_frag (asection * sec ATTRIBUTE_UNUSED, fragS * fragp, long stretch ATTRIBUTE_UNUSED) +{ + int grows = 0; + int insn_size; + int insn_relax_size; + int do_relax_p = 0; /* Indicate doing relaxation for this frag. */ + int relaxable_p = 0; + bfd_boolean word_align_p = FALSE; + fragS *next_fragp; + + /* If the instruction address is odd, make it half word align first. */ + if ((fragp->fr_address) % 2 != 0) + { + if ((fragp->fr_address + fragp->insn_addr) % 2 != 0) + { + fragp->insn_addr = 1; + grows += 1; + } + } + + word_align_p = ((fragp->fr_address + fragp->insn_addr) % 4 == 0) ? TRUE : FALSE; + + /* Get instruction size and relax size after the last relaxation. */ + if (fragp->fr_opcode) + { + insn_size = RELAX_NEW (fragp->fr_subtype); + insn_relax_size = RELAX_OLD (fragp->fr_subtype); + } + else + { + insn_size = RELAX_OLD (fragp->fr_subtype); + insn_relax_size = RELAX_NEW (fragp->fr_subtype); + } + + /* Handle specially for GP instruction. for, judge_size_before_relax() has already determine + whether the GP instruction should do relax. */ + if ((RELAX_TYPE (fragp->fr_subtype) == Insn_GP) + || (RELAX_TYPE (fragp->fr_subtype) == Insn_PIC)) + { + if (!word_align_p) + { + if (fragp->insn_addr < 2) + { + fragp->insn_addr += 2; + grows += 2; + } + else + { + fragp->insn_addr -= 2; + grows -= 2; + } + } + + if (fragp->fr_opcode) + fragp->fr_fix = RELAX_NEW (fragp->fr_subtype) + fragp->insn_addr; + else + fragp->fr_fix = RELAX_OLD (fragp->fr_subtype) + fragp->insn_addr; + } + else + { + if (RELAX_TYPE (fragp->fr_subtype) == PC_DISP19div2) + b32_relax_to_b16 (fragp); + + relaxable_p = RELAX_OPT (fragp->fr_subtype); + next_fragp = fragp->fr_next; + while ((next_fragp) && (next_fragp->fr_type != rs_machine_dependent)) + { + next_fragp = next_fragp->fr_next; + } + + if (next_fragp) + { + int n_insn_size; + int n_relaxable_p = 0; + + if (next_fragp->fr_opcode) + { + n_insn_size = RELAX_NEW (next_fragp->fr_subtype); + } + else + { + n_insn_size = RELAX_OLD (next_fragp->fr_subtype); + } + + if (RELAX_TYPE (next_fragp->fr_subtype) == PC_DISP19div2) + b32_relax_to_b16 (next_fragp); + n_relaxable_p = RELAX_OPT (next_fragp->fr_subtype); + + if (word_align_p) + { + if (insn_size == 4) + { + /* 32 -> 16. */ + if (relaxable_p && ((n_insn_size == 2) || n_relaxable_p)) + { + grows -= 2; + do_relax_p = 1; + } + } + else if (insn_size == 2) + { + /* 16 -> 32. */ + if (relaxable_p && (((n_insn_size == 4) && !n_relaxable_p) || (n_insn_size > 4))) + { + grows += 2; + do_relax_p = 1; + } + } + else + { + abort (); + } + } + else + { + if (insn_size == 4) + { + /* 32 -> 16. */ + if (relaxable_p) + { + grows -= 2; + do_relax_p = 1; + } + /* Make the 32 bit insturction word align. */ + else + { + fragp->insn_addr += 2; + grows += 2; + } + } + else if (insn_size == 2) + { + /* Do nothing. */ + } + else + { + abort (); + } + } + } + else + { + /* Here, try best to do relax regardless fragp->fr_next->fr_type. */ + if (word_align_p == FALSE) + { + if (insn_size % 4 == 0) + { + /* 32 -> 16. */ + if (relaxable_p) + { + grows -= 2; + do_relax_p = 1; + } + else + { + fragp->insn_addr += 2; + grows += 2; + } + } + } + else + { + /* Do nothing. */ + } + } + + /* fragp->fr_opcode indicates whether this frag should be relaxed. */ + if (do_relax_p) + { + if (fragp->fr_opcode) + { + fragp->fr_opcode = NULL; + /* Guarantee estimate stage is correct. */ + fragp->fr_fix = RELAX_OLD (fragp->fr_subtype); + fragp->fr_fix += fragp->insn_addr; + } + else + { + fragp->fr_opcode = fragp->fr_literal + RELAX_RELOC1 (fragp->fr_subtype); + /* Guarantee estimate stage is correct. */ + fragp->fr_fix = RELAX_NEW (fragp->fr_subtype); + fragp->fr_fix += fragp->insn_addr; + } + } + else + { + if (fragp->fr_opcode) + { + /* Guarantee estimate stage is correct. */ + fragp->fr_fix = RELAX_NEW (fragp->fr_subtype); + fragp->fr_fix += fragp->insn_addr; + } + else + { + /* Guarantee estimate stage is correct. */ + fragp->fr_fix = RELAX_OLD (fragp->fr_subtype); + fragp->fr_fix += fragp->insn_addr; + } + } + } + + return grows; +} + +void +md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED, segT sec ATTRIBUTE_UNUSED, fragS * fragp) +{ + int old; + int new; + char backup[20]; + fixS *fixp; + + old = RELAX_OLD (fragp->fr_subtype); + new = RELAX_NEW (fragp->fr_subtype); + + /* fragp->fr_opcode indicates whether this frag should be relaxed. */ + if (fragp->fr_opcode == NULL) + { + memcpy (backup, fragp->fr_literal, old); + fragp->fr_fix = old; + } + else + { + memcpy (backup, fragp->fr_literal + old, new); + fragp->fr_fix = new; + } + + fixp = fragp->tc_frag_data.fixp; + while (fixp && fixp->fx_frag == fragp && fixp->fx_where < old) + { + if (fragp->fr_opcode) + fixp->fx_done = 1; + fixp = fixp->fx_next; + } + while (fixp && fixp->fx_frag == fragp) + { + if (fragp->fr_opcode) + fixp->fx_where -= old + fragp->insn_addr; + else + fixp->fx_done = 1; + fixp = fixp->fx_next; + } + + if (fragp->insn_addr) + { + md_number_to_chars (fragp->fr_literal, 0x0, fragp->insn_addr); + } + memcpy (fragp->fr_literal + fragp->insn_addr, backup, fragp->fr_fix); + fragp->fr_fix += fragp->insn_addr; +} + +/* Implementation of md_frag_check. + Called after md_convert_frag(). */ + +void +score_frag_check (fragS * fragp ATTRIBUTE_UNUSED) +{ + know (fragp->insn_addr <= RELAX_PAD_BYTE); +} + +bfd_boolean +score_fix_adjustable (fixS * fixP) +{ + if (fixP->fx_addsy == NULL) + { + return 1; + } + else if (OUTPUT_FLAVOR == bfd_target_elf_flavour + && (S_IS_EXTERNAL (fixP->fx_addsy) || S_IS_WEAK (fixP->fx_addsy))) + { + return 0; + } + else if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT + || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + { + return 0; + } + + return 1; +} + +/* Implementation of TC_VALIDATE_FIX. + Called before md_apply_fix() and after md_convert_frag(). */ +void +score_validate_fix (fixS *fixP) +{ + fixP->fx_where += fixP->fx_frag->insn_addr; +} + +long +md_pcrel_from (fixS * fixP) +{ + long retval = 0; + + if (fixP->fx_addsy + && (S_GET_SEGMENT (fixP->fx_addsy) == undefined_section) + && (fixP->fx_subsy == NULL)) + { + retval = 0; + } + else + { + retval = fixP->fx_where + fixP->fx_frag->fr_address; + } + + return retval; +} + +int +score_force_relocation (struct fix *fixp) +{ + int retval = 0; + + if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT + || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY + || fixp->fx_r_type == BFD_RELOC_SCORE_JMP + || fixp->fx_r_type == BFD_RELOC_SCORE_BRANCH + || fixp->fx_r_type == BFD_RELOC_SCORE16_JMP + || fixp->fx_r_type == BFD_RELOC_SCORE16_BRANCH) + { + retval = 1; + } + + return retval; +} + +/* Round up a section size to the appropriate boundary. */ +valueT +md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size) +{ + int align = bfd_get_section_alignment (stdoutput, segment); + + return ((size + (1 << align) - 1) & (-1 << align)); +} + +void +md_apply_fix (fixS *fixP, valueT *valP, segT seg) +{ + offsetT value = *valP; + offsetT abs_value = 0; + offsetT newval; + offsetT content; + unsigned short HI, LO; + + char *buf = fixP->fx_frag->fr_literal + fixP->fx_where; + + assert (fixP->fx_r_type < BFD_RELOC_UNUSED); + if (fixP->fx_addsy == 0 && !fixP->fx_pcrel) + { + if (fixP->fx_r_type != BFD_RELOC_SCORE_DUMMY_HI16) + fixP->fx_done = 1; + } + + /* If this symbol is in a different section then we need to leave it for + the linker to deal with. Unfortunately, md_pcrel_from can't tell, + so we have to undo it's effects here. */ + if (fixP->fx_pcrel) + { + if (fixP->fx_addsy != NULL + && S_IS_DEFINED (fixP->fx_addsy) + && S_GET_SEGMENT (fixP->fx_addsy) != seg) + value += md_pcrel_from (fixP); + } + + /* Remember value for emit_reloc. */ + fixP->fx_addnumber = value; + + switch (fixP->fx_r_type) + { + case BFD_RELOC_HI16_S: + if (fixP->fx_done) + { /* For la rd, imm32. */ + newval = md_chars_to_number (buf, INSN_SIZE); + HI = (value) >> 16; /* mul to 2, then take the hi 16 bit. */ + newval |= (HI & 0x3fff) << 1; + newval |= ((HI >> 14) & 0x3) << 16; + md_number_to_chars (buf, newval, INSN_SIZE); + } + break; + case BFD_RELOC_LO16: + if (fixP->fx_done) /* For la rd, imm32. */ + { + newval = md_chars_to_number (buf, INSN_SIZE); + LO = (value) & 0xffff; + newval |= (LO & 0x3fff) << 1; /* 16 bit: imm -> 14 bit in lo, 2 bit in hi. */ + newval |= ((LO >> 14) & 0x3) << 16; + md_number_to_chars (buf, newval, INSN_SIZE); + } + break; + case BFD_RELOC_SCORE_JMP: + { + content = md_chars_to_number (buf, INSN_SIZE); + value = fixP->fx_offset; + content = (content & ~0x3ff7ffe) | ((value << 1) & 0x3ff0000) | (value & 0x7fff); + md_number_to_chars (buf, content, INSN_SIZE); + } + break; + case BFD_RELOC_SCORE_BRANCH: + if ((S_GET_SEGMENT (fixP->fx_addsy) != seg) || (fixP->fx_addsy != NULL && S_IS_EXTERNAL (fixP->fx_addsy))) + value = fixP->fx_offset; + else + fixP->fx_done = 1; + + content = md_chars_to_number (buf, INSN_SIZE); + if ((fixP->fx_frag->fr_opcode != 0) && ((content & 0x80008000) != 0x80008000)) + { + if ((value & 0x80000000) == 0x80000000) + abs_value = 0xffffffff - value + 1; + if ((abs_value & 0xffffff00) != 0) + { + as_bad_where (fixP->fx_file, fixP->fx_line, + _(" branch relocation truncate (0x%x) [-2^8 ~ 2^8]"), (unsigned int)value); + return; + } + content = md_chars_to_number (buf, INSN16_SIZE); + content &= 0xff00; + content = (content & 0xff00) | ((value >> 1) & 0xff); + md_number_to_chars (buf, content, INSN16_SIZE); + fixP->fx_r_type = BFD_RELOC_SCORE16_BRANCH; + fixP->fx_size = 2; + } + else + { + if ((value & 0x80000000) == 0x80000000) + abs_value = 0xffffffff - value + 1; + if ((abs_value & 0xfff80000) != 0) + { + as_bad_where (fixP->fx_file, fixP->fx_line, + _(" branch relocation truncate (0x%x) [-2^19 ~ 2^19]"), (unsigned int)value); + return; + } + content = md_chars_to_number (buf, INSN_SIZE); + content &= 0xfc00fc01; + content = (content & 0xfc00fc01) | (value & 0x3fe) | ((value << 6) & 0x3ff0000); + md_number_to_chars (buf, content, INSN_SIZE); + } + break; + case BFD_RELOC_SCORE16_JMP: + content = md_chars_to_number (buf, INSN16_SIZE); + content &= 0xf001; + value = fixP->fx_offset & 0xfff; + content = (content & 0xfc01) | (value & 0xffe); + md_number_to_chars (buf, content, INSN16_SIZE); + break; + case BFD_RELOC_SCORE16_BRANCH: + content = md_chars_to_number (buf, INSN_SIZE); + if ((fixP->fx_frag->fr_opcode != 0) && ((content & 0x80008000) == 0x80008000)) + { + if ((S_GET_SEGMENT (fixP->fx_addsy) != seg) || + (fixP->fx_addsy != NULL && S_IS_EXTERNAL (fixP->fx_addsy))) + value = fixP->fx_offset; + else + fixP->fx_done = 1; + if ((value & 0x80000000) == 0x80000000) + abs_value = 0xffffffff - value + 1; + if ((abs_value & 0xfff80000) != 0) + { + as_bad_where (fixP->fx_file, fixP->fx_line, + _(" branch relocation truncate (0x%x) [-2^19 ~ 2^19]"), (unsigned int)value); + return; + } + content = md_chars_to_number (buf, INSN_SIZE); + content = (content & 0xfc00fc01) | (value & 0x3fe) | ((value << 6) & 0x3ff0000); + md_number_to_chars (buf, content, INSN_SIZE); + fixP->fx_r_type = BFD_RELOC_SCORE_BRANCH; + fixP->fx_size = 4; + break; + } + else + { + /* In differnt section. */ + if ((S_GET_SEGMENT (fixP->fx_addsy) != seg) || + (fixP->fx_addsy != NULL && S_IS_EXTERNAL (fixP->fx_addsy))) + value = fixP->fx_offset; + else + fixP->fx_done = 1; + + if ((value & 0x80000000) == 0x80000000) + abs_value = 0xffffffff - value + 1; + if ((abs_value & 0xffffff00) != 0) + { + as_bad_where (fixP->fx_file, fixP->fx_line, + _(" branch relocation truncate (0x%x) [-2^8 ~ 2^8]"), (unsigned int)value); + return; + } + content = md_chars_to_number (buf, INSN16_SIZE); + content = (content & 0xff00) | ((value >> 1) & 0xff); + md_number_to_chars (buf, content, INSN16_SIZE); + break; + } + case BFD_RELOC_8: + if (fixP->fx_done || fixP->fx_pcrel) + md_number_to_chars (buf, value, 1); +#ifdef OBJ_ELF + else + { + value = fixP->fx_offset; + md_number_to_chars (buf, value, 1); + } +#endif + break; + + case BFD_RELOC_16: + if (fixP->fx_done || fixP->fx_pcrel) + md_number_to_chars (buf, value, 2); +#ifdef OBJ_ELF + else + { + value = fixP->fx_offset; + md_number_to_chars (buf, value, 2); + } +#endif + break; + case BFD_RELOC_RVA: + case BFD_RELOC_32: + if (fixP->fx_done || fixP->fx_pcrel) + md_number_to_chars (buf, value, 4); +#ifdef OBJ_ELF + else + { + value = fixP->fx_offset; + md_number_to_chars (buf, value, 4); + } +#endif + break; + case BFD_RELOC_VTABLE_INHERIT: + fixP->fx_done = 0; + if (fixP->fx_addsy && !S_IS_DEFINED (fixP->fx_addsy) && !S_IS_WEAK (fixP->fx_addsy)) + S_SET_WEAK (fixP->fx_addsy); + break; + case BFD_RELOC_VTABLE_ENTRY: + fixP->fx_done = 0; + break; + case BFD_RELOC_SCORE_GPREL15: + content = md_chars_to_number (buf, INSN_SIZE); + if ((fixP->fx_frag->fr_opcode != 0) && ((content & 0xfc1c8000) != 0x94188000)) + fixP->fx_r_type = BFD_RELOC_NONE; + fixP->fx_done = 0; + break; + case BFD_RELOC_SCORE_GOT15: + case BFD_RELOC_SCORE_DUMMY_HI16: + case BFD_RELOC_SCORE_GOT_LO16: + case BFD_RELOC_SCORE_CALL15: + case BFD_RELOC_GPREL32: + break; + case BFD_RELOC_NONE: + default: + as_bad_where (fixP->fx_file, fixP->fx_line, _("bad relocation fixup type (%d)"), fixP->fx_r_type); + } +} + +/* Translate internal representation of relocation info to BFD target format. */ +arelent ** +tc_gen_reloc (asection * section ATTRIBUTE_UNUSED, fixS * fixp) +{ + static arelent *retval[MAX_RELOC_EXPANSION + 1]; /* MAX_RELOC_EXPANSION equals 2. */ + arelent *reloc; + bfd_reloc_code_real_type code; + char *type; + fragS *f; + symbolS *s; + expressionS e; + + reloc = retval[0] = xmalloc (sizeof (arelent)); + retval[1] = NULL; + + reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *)); + *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); + reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; + reloc->addend = fixp->fx_offset; + + /* If this is a variant frag, we may need to adjust the existing + reloc and generate a new one. */ + if (fixp->fx_frag->fr_opcode != NULL && (fixp->fx_r_type == BFD_RELOC_SCORE_GPREL15)) + { + /* Update instruction imm bit. */ + offsetT newval; + unsigned short off; + char *buf; + + buf = fixp->fx_frag->fr_literal + fixp->fx_frag->insn_addr; + newval = md_chars_to_number (buf, INSN_SIZE); + off = fixp->fx_offset >> 16; + newval |= (off & 0x3fff) << 1; + newval |= ((off >> 14) & 0x3) << 16; + md_number_to_chars (buf, newval, INSN_SIZE); + + buf += INSN_SIZE; + newval = md_chars_to_number (buf, INSN_SIZE); + off = fixp->fx_offset & 0xffff; + newval |= ((off & 0x3fff) << 1); + newval |= (((off >> 14) & 0x3) << 16); + md_number_to_chars (buf, newval, INSN_SIZE); + + retval[1] = xmalloc (sizeof (arelent)); + retval[2] = NULL; + retval[1]->sym_ptr_ptr = xmalloc (sizeof (asymbol *)); + *retval[1]->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); + retval[1]->address = (reloc->address + RELAX_RELOC2 (fixp->fx_frag->fr_subtype)); + + f = fixp->fx_frag; + s = f->fr_symbol; + e = s->sy_value; + + retval[1]->addend = 0; + retval[1]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_LO16); + assert (retval[1]->howto != NULL); + + fixp->fx_r_type = BFD_RELOC_HI16_S; + } + + code = fixp->fx_r_type; + switch (fixp->fx_r_type) + { + case BFD_RELOC_32: + if (fixp->fx_pcrel) + { + code = BFD_RELOC_32_PCREL; + break; + } + case BFD_RELOC_HI16_S: + case BFD_RELOC_LO16: + case BFD_RELOC_SCORE_JMP: + case BFD_RELOC_SCORE_BRANCH: + case BFD_RELOC_SCORE16_JMP: + case BFD_RELOC_SCORE16_BRANCH: + case BFD_RELOC_VTABLE_ENTRY: + case BFD_RELOC_VTABLE_INHERIT: + case BFD_RELOC_SCORE_GPREL15: + case BFD_RELOC_SCORE_GOT15: + case BFD_RELOC_SCORE_DUMMY_HI16: + case BFD_RELOC_SCORE_GOT_LO16: + case BFD_RELOC_SCORE_CALL15: + case BFD_RELOC_GPREL32: + case BFD_RELOC_NONE: + code = fixp->fx_r_type; + break; + default: + type = _("<unknown>"); + as_bad_where (fixp->fx_file, fixp->fx_line, + _("cannot represent %s relocation in this object file format"), type); + return NULL; + } + + reloc->howto = bfd_reloc_type_lookup (stdoutput, code); + if (reloc->howto == NULL) + { + as_bad_where (fixp->fx_file, fixp->fx_line, + _("cannot represent %s relocation in this object file format1"), + bfd_get_reloc_code_name (code)); + return NULL; + } + /* HACK: Since arm ELF uses Rel instead of Rela, encode the + vtable entry to be used in the relocation's section offset. */ + if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + reloc->address = fixp->fx_offset; + + return retval; +} + +void +score_elf_final_processing (void) +{ + if (fix_data_dependency == 1) + { + elf_elfheader (stdoutput)->e_flags |= EF_SCORE_FIXDEP; + } + if (score_pic == PIC) + { + elf_elfheader (stdoutput)->e_flags |= EF_SCORE_PIC; + } +} + +static void +parse_pce_inst (char *insnstr) +{ + char c; + char *p; + char first[MAX_LITERAL_POOL_SIZE]; + char second[MAX_LITERAL_POOL_SIZE]; + struct score_it pec_part_1; + + /* Get first part string of PCE. */ + p = strstr (insnstr, "||"); + c = *p; + *p = '\0'; + sprintf (first, "%s", insnstr); + + /* Get second part string of PCE. */ + *p = c; + p += 2; + sprintf (second, "%s", p); + + parse_16_32_inst (first, FALSE); + if (inst.error) + return; + + memcpy (&pec_part_1, &inst, sizeof (inst)); + + parse_16_32_inst (second, FALSE); + if (inst.error) + return; + + if ( ((pec_part_1.size == INSN_SIZE) && (inst.size == INSN_SIZE)) + || ((pec_part_1.size == INSN_SIZE) && (inst.size == INSN16_SIZE)) + || ((pec_part_1.size == INSN16_SIZE) && (inst.size == INSN_SIZE))) + { + inst.error = _("pce instruction error (16 bit || 16 bit)'"); + sprintf (inst.str, insnstr); + return; + } + + if (!inst.error) + gen_insn_frag (&pec_part_1, &inst); +} + +void +md_assemble (char *str) +{ + know (str); + know (strlen (str) < MAX_LITERAL_POOL_SIZE); + + memset (&inst, '\0', sizeof (inst)); + if (INSN_IS_PCE_P (str)) + parse_pce_inst (str); + else + parse_16_32_inst (str, TRUE); + + if (inst.error) + as_bad (_("%s -- `%s'"), inst.error, inst.str); +} + +/* We handle all bad expressions here, so that we can report the faulty + instruction in the error message. */ +void +md_operand (expressionS * expr) +{ + if (in_my_get_expression) + { + expr->X_op = O_illegal; + if (inst.error == NULL) + { + inst.error = _("bad expression"); + } + } +} + +const char *md_shortopts = "nO::g::G:"; + +#ifdef SCORE_BI_ENDIAN +#define OPTION_EB (OPTION_MD_BASE + 0) +#define OPTION_EL (OPTION_MD_BASE + 1) +#else +#if TARGET_BYTES_BIG_ENDIAN +#define OPTION_EB (OPTION_MD_BASE + 0) +#else +#define OPTION_EL (OPTION_MD_BASE + 1) +#endif +#endif +#define OPTION_FIXDD (OPTION_MD_BASE + 2) +#define OPTION_NWARN (OPTION_MD_BASE + 3) +#define OPTION_SCORE5 (OPTION_MD_BASE + 4) +#define OPTION_SCORE5U (OPTION_MD_BASE + 5) +#define OPTION_SCORE7 (OPTION_MD_BASE + 6) +#define OPTION_R1 (OPTION_MD_BASE + 7) +#define OPTION_O0 (OPTION_MD_BASE + 8) +#define OPTION_SCORE_VERSION (OPTION_MD_BASE + 9) +#define OPTION_PIC (OPTION_MD_BASE + 10) + +struct option md_longopts[] = +{ +#ifdef OPTION_EB + {"EB" , no_argument, NULL, OPTION_EB}, +#endif +#ifdef OPTION_EL + {"EL" , no_argument, NULL, OPTION_EL}, +#endif + {"FIXDD" , no_argument, NULL, OPTION_FIXDD}, + {"NWARN" , no_argument, NULL, OPTION_NWARN}, + {"SCORE5" , no_argument, NULL, OPTION_SCORE5}, + {"SCORE5U", no_argument, NULL, OPTION_SCORE5U}, + {"SCORE7" , no_argument, NULL, OPTION_SCORE7}, + {"USE_R1" , no_argument, NULL, OPTION_R1}, + {"O0" , no_argument, NULL, OPTION_O0}, + {"V" , no_argument, NULL, OPTION_SCORE_VERSION}, + {"KPIC" , no_argument, NULL, OPTION_PIC}, + {NULL , no_argument, NULL, 0} +}; + +size_t md_longopts_size = sizeof (md_longopts); + +int +md_parse_option (int c, char *arg) +{ + switch (c) + { +#ifdef OPTION_EB + case OPTION_EB: + target_big_endian = 1; + break; +#endif +#ifdef OPTION_EL + case OPTION_EL: + target_big_endian = 0; + break; +#endif + case OPTION_FIXDD: + fix_data_dependency = 1; + break; + case OPTION_NWARN: + warn_fix_data_dependency = 0; + break; + case OPTION_SCORE5: + score7 = 0; + university_version = 0; + vector_size = SCORE5_PIPELINE; + break; + case OPTION_SCORE5U: + score7 = 0; + university_version = 1; + vector_size = SCORE5_PIPELINE; + break; + case OPTION_SCORE7: + score7 = 1; + university_version = 0; + vector_size = SCORE7_PIPELINE; + break; + case OPTION_R1: + nor1 = 0; + break; + case 'G': + g_switch_value = atoi (arg); + break; + case OPTION_O0: + g_opt = 0; + break; + case OPTION_SCORE_VERSION: + printf (_("Sunplus-v2-0-0-20060510\n")); + break; + case OPTION_PIC: + score_pic = PIC; + g_switch_value = 0; /* Must set -G num as 0 to generate PIC code. */ + break; + default: + /* as_bad (_("unrecognized option `-%c%s'"), c, arg ? arg : ""); */ + return 0; + } + return 1; +} + +void +md_show_usage (FILE * fp) +{ + fprintf (fp, _(" Score-specific assembler options:\n")); +#ifdef OPTION_EB + fprintf (fp, _("\ + -EB\t\tassemble code for a big-endian cpu\n")); +#endif + +#ifdef OPTION_EL + fprintf (fp, _("\ + -EL\t\tassemble code for a little-endian cpu\n")); +#endif + + fprintf (fp, _("\ + -FIXDD\t\tassemble code for fix data dependency\n")); + fprintf (fp, _("\ + -NWARN\t\tassemble code for no warning message for fix data dependency\n")); + fprintf (fp, _("\ + -SCORE5\t\tassemble code for target is SCORE5\n")); + fprintf (fp, _("\ + -SCORE5U\tassemble code for target is SCORE5U\n")); + fprintf (fp, _("\ + -SCORE7\t\tassemble code for target is SCORE7, this is default setting\n")); + fprintf (fp, _("\ + -USE_R1\t\tassemble code for no warning message when using temp register r1\n")); + fprintf (fp, _("\ + -KPIC\t\tassemble code for PIC\n")); + fprintf (fp, _("\ + -O0\t\tassembler will not perform any optimizations\n")); + fprintf (fp, _("\ + -G gpnum\tassemble code for setting gpsize and default is 8 byte\n")); + fprintf (fp, _("\ + -V \t\tSunplus release version \n")); +} + + +/* Pesudo handling functions. */ + +/* If we change section we must dump the literal pool first. */ +static void +s_score_bss (int ignore ATTRIBUTE_UNUSED) +{ + subseg_set (bss_section, (subsegT) get_absolute_expression ()); + demand_empty_rest_of_line (); +} + +static void +s_score_text (int ignore) +{ + obj_elf_text (ignore); + record_alignment (now_seg, 2); +} + +static void +score_s_section (int ignore) +{ + obj_elf_section (ignore); + if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0) + record_alignment (now_seg, 2); + +} + +static void +s_change_sec (int sec) +{ + segT seg; + +#ifdef OBJ_ELF + /* The ELF backend needs to know that we are changing sections, so + that .previous works correctly. We could do something like check + for an obj_section_change_hook macro, but that might be confusing + as it would not be appropriate to use it in the section changing + functions in read.c, since obj-elf.c intercepts those. FIXME: + This should be cleaner, somehow. */ + obj_elf_section_change_hook (); +#endif + switch (sec) + { + case 'r': + seg = subseg_new (RDATA_SECTION_NAME, (subsegT) get_absolute_expression ()); + bfd_set_section_flags (stdoutput, seg, (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_RELOC | SEC_DATA)); + if (strcmp (TARGET_OS, "elf") != 0) + record_alignment (seg, 4); + demand_empty_rest_of_line (); + break; + case 's': + seg = subseg_new (".sdata", (subsegT) get_absolute_expression ()); + bfd_set_section_flags (stdoutput, seg, SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA); + if (strcmp (TARGET_OS, "elf") != 0) + record_alignment (seg, 4); + demand_empty_rest_of_line (); + break; + } +} + +static void +s_score_mask (int reg_type ATTRIBUTE_UNUSED) +{ + long mask, off; + + if (cur_proc_ptr == (procS *) NULL) + { + as_warn (_(".mask outside of .ent")); + demand_empty_rest_of_line (); + return; + } + if (get_absolute_expression_and_terminator (&mask) != ',') + { + as_warn (_("Bad .mask directive")); + --input_line_pointer; + demand_empty_rest_of_line (); + return; + } + off = get_absolute_expression (); + cur_proc_ptr->reg_mask = mask; + cur_proc_ptr->reg_offset = off; + demand_empty_rest_of_line (); +} + +static symbolS * +get_symbol (void) +{ + int c; + char *name; + symbolS *p; + + name = input_line_pointer; + c = get_symbol_end (); + p = (symbolS *) symbol_find_or_make (name); + *input_line_pointer = c; + return p; +} + +static long +get_number (void) +{ + int negative = 0; + long val = 0; + + if (*input_line_pointer == '-') + { + ++input_line_pointer; + negative = 1; + } + if (!ISDIGIT (*input_line_pointer)) + as_bad (_("expected simple number")); + if (input_line_pointer[0] == '0') + { + if (input_line_pointer[1] == 'x') + { + input_line_pointer += 2; + while (ISXDIGIT (*input_line_pointer)) + { + val <<= 4; + val |= hex_value (*input_line_pointer++); + } + return negative ? -val : val; + } + else + { + ++input_line_pointer; + while (ISDIGIT (*input_line_pointer)) + { + val <<= 3; + val |= *input_line_pointer++ - '0'; + } + return negative ? -val : val; + } + } + if (!ISDIGIT (*input_line_pointer)) + { + printf (_(" *input_line_pointer == '%c' 0x%02x\n"), *input_line_pointer, *input_line_pointer); + as_warn (_("invalid number")); + return -1; + } + while (ISDIGIT (*input_line_pointer)) + { + val *= 10; + val += *input_line_pointer++ - '0'; + } + return negative ? -val : val; +} + +/* The .aent and .ent directives. */ + +static void +s_score_ent (int aent) +{ + symbolS *symbolP; + int maybe_text; + + symbolP = get_symbol (); + if (*input_line_pointer == ',') + ++input_line_pointer; + SKIP_WHITESPACE (); + if (ISDIGIT (*input_line_pointer) || *input_line_pointer == '-') + get_number (); + +#ifdef BFD_ASSEMBLER + if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0) + maybe_text = 1; + else + maybe_text = 0; +#else + if (now_seg != data_section && now_seg != bss_section) + maybe_text = 1; + else + maybe_text = 0; +#endif + if (!maybe_text) + as_warn (_(".ent or .aent not in text section.")); + if (!aent && cur_proc_ptr) + as_warn (_("missing .end")); + if (!aent) + { + cur_proc_ptr = &cur_proc; + cur_proc_ptr->reg_mask = 0xdeadbeaf; + cur_proc_ptr->reg_offset = 0xdeadbeaf; + cur_proc_ptr->fpreg_mask = 0xdeafbeaf; + cur_proc_ptr->leaf = 0xdeafbeaf; + cur_proc_ptr->frame_offset = 0xdeafbeaf; + cur_proc_ptr->frame_reg = 0xdeafbeaf; + cur_proc_ptr->pc_reg = 0xdeafbeaf; + cur_proc_ptr->isym = symbolP; + symbol_get_bfdsym (symbolP)->flags |= BSF_FUNCTION; + ++numprocs; + if (debug_type == DEBUG_STABS) + stabs_generate_asm_func (S_GET_NAME (symbolP), S_GET_NAME (symbolP)); + } + demand_empty_rest_of_line (); +} + +static void +s_score_frame (int ignore ATTRIBUTE_UNUSED) +{ + char *backupstr; + char str[30]; + long val; + int i = 0; + + backupstr = input_line_pointer; + +#ifdef OBJ_ELF + if (cur_proc_ptr == (procS *) NULL) + { + as_warn (_(".frame outside of .ent")); + demand_empty_rest_of_line (); + return; + } + cur_proc_ptr->frame_reg = reg_required_here ((&backupstr), 0, REG_TYPE_SCORE); + SKIP_WHITESPACE (); + skip_past_comma (&backupstr); + while (*backupstr != ',') + { + str[i] = *backupstr; + i++; + backupstr++; + } + str[i] = '\0'; + val = atoi (str); + + SKIP_WHITESPACE (); + skip_past_comma (&backupstr); + cur_proc_ptr->frame_offset = val; + cur_proc_ptr->pc_reg = reg_required_here ((&backupstr), 0, REG_TYPE_SCORE); + + SKIP_WHITESPACE (); + skip_past_comma (&backupstr); + i = 0; + while (*backupstr != '\n') + { + str[i] = *backupstr; + i++; + backupstr++; + } + str[i] = '\0'; + val = atoi (str); + cur_proc_ptr->leaf = val; + SKIP_WHITESPACE (); + skip_past_comma (&backupstr); + +#endif /* OBJ_ELF */ + while (input_line_pointer != backupstr) + input_line_pointer++; +} + +/* The .end directive. */ +static void +s_score_end (int x ATTRIBUTE_UNUSED) +{ + symbolS *p; + int maybe_text; + + /* Generate a .pdr section. */ + segT saved_seg = now_seg; + subsegT saved_subseg = now_subseg; + valueT dot; + expressionS exp; + char *fragp; + + if (!is_end_of_line[(unsigned char)*input_line_pointer]) + { + p = get_symbol (); + demand_empty_rest_of_line (); + } + else + p = NULL; + +#ifdef BFD_ASSEMBLER + if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0) + maybe_text = 1; + else + maybe_text = 0; +#else + if (now_seg != data_section && now_seg != bss_section) + maybe_text = 1; + else + maybe_text = 0; +#endif + + if (!maybe_text) + as_warn (_(".end not in text section")); + if (!cur_proc_ptr) + { + as_warn (_(".end directive without a preceding .ent directive.")); + demand_empty_rest_of_line (); + return; + } + if (p != NULL) + { + assert (S_GET_NAME (p)); + if (strcmp (S_GET_NAME (p), S_GET_NAME (cur_proc_ptr->isym))) + as_warn (_(".end symbol does not match .ent symbol.")); + if (debug_type == DEBUG_STABS) + stabs_generate_asm_endfunc (S_GET_NAME (p), S_GET_NAME (p)); + } + else + as_warn (_(".end directive missing or unknown symbol")); + + if ((cur_proc_ptr->reg_mask == 0xdeadbeaf) || + (cur_proc_ptr->reg_offset == 0xdeadbeaf) || + (cur_proc_ptr->leaf == 0xdeafbeaf) || + (cur_proc_ptr->frame_offset == 0xdeafbeaf) || + (cur_proc_ptr->frame_reg == 0xdeafbeaf) || (cur_proc_ptr->pc_reg == 0xdeafbeaf)); + + else + { + dot = frag_now_fix (); + assert (pdr_seg); + subseg_set (pdr_seg, 0); + /* Write the symbol. */ + exp.X_op = O_symbol; + exp.X_add_symbol = p; + exp.X_add_number = 0; + emit_expr (&exp, 4); + fragp = frag_more (7 * 4); + md_number_to_chars (fragp, (valueT) cur_proc_ptr->reg_mask, 4); + md_number_to_chars (fragp + 4, (valueT) cur_proc_ptr->reg_offset, 4); + md_number_to_chars (fragp + 8, (valueT) cur_proc_ptr->fpreg_mask, 4); + md_number_to_chars (fragp + 12, (valueT) cur_proc_ptr->leaf, 4); + md_number_to_chars (fragp + 16, (valueT) cur_proc_ptr->frame_offset, 4); + md_number_to_chars (fragp + 20, (valueT) cur_proc_ptr->frame_reg, 4); + md_number_to_chars (fragp + 24, (valueT) cur_proc_ptr->pc_reg, 4); + subseg_set (saved_seg, saved_subseg); + + } + cur_proc_ptr = NULL; +} + +/* Handle the .set pseudo-op. */ +static void +s_score_set (int x ATTRIBUTE_UNUSED) +{ + int i = 0; + char name[MAX_LITERAL_POOL_SIZE]; + char * orig_ilp = input_line_pointer; + + while (!is_end_of_line[(unsigned char)*input_line_pointer]) + { + name[i] = (char) * input_line_pointer; + i++; + ++input_line_pointer; + } + + name[i] = '\0'; + + if (strcmp (name, "nwarn") == 0) + { + warn_fix_data_dependency = 0; + } + else if (strcmp (name, "fixdd") == 0) + { + fix_data_dependency = 1; + } + else if (strcmp (name, "nofixdd") == 0) + { + fix_data_dependency = 0; + } + else if (strcmp (name, "r1") == 0) + { + nor1 = 0; + } + else if (strcmp (name, "nor1") == 0) + { + nor1 = 1; + } + else if (strcmp (name, "optimize") == 0) + { + g_opt = 1; + } + else if (strcmp (name, "volatile") == 0) + { + g_opt = 0; + } + else if (strcmp (name, "pic") == 0) + { + score_pic = PIC; + } + else + { + input_line_pointer = orig_ilp; + s_set (0); + } +} + +/* Handle the .cpload pseudo-op. This is used when generating PIC code. It sets the + $gp register for the function based on the function address, which is in the register + named in the argument. This uses a relocation against GP_DISP_LABEL, which is handled + specially by the linker. The result is: + ldis gp, %hi(GP_DISP_LABEL) + ori gp, %low(GP_DISP_LABEL) + add gp, gp, .cpload argument + The .cpload argument is normally r29. */ + +static void +s_score_cpload (int ignore ATTRIBUTE_UNUSED) +{ + int reg; + char insn_str[MAX_LITERAL_POOL_SIZE]; + + /* If we are not generating PIC code, .cpload is ignored. */ + if (score_pic == NO_PIC) + { + s_ignore (0); + return; + } + + if ((reg = reg_required_here (&input_line_pointer, -1, REG_TYPE_SCORE)) == (int) FAIL) + return; + + demand_empty_rest_of_line (); + + sprintf (insn_str, "ld_i32hi r%d, %s", GP, GP_DISP_LABEL); + if (append_insn (insn_str, TRUE) == (int) FAIL) + return; + + sprintf (insn_str, "ld_i32lo r%d, %s", GP, GP_DISP_LABEL); + if (append_insn (insn_str, TRUE) == (int) FAIL) + return; + + sprintf (insn_str, "add r%d, r%d, r%d", GP, GP, reg); + if (append_insn (insn_str, TRUE) == (int) FAIL) + return; +} + +/* Handle the .cprestore pseudo-op. This stores $gp into a given + offset from $sp. The offset is remembered, and after making a PIC + call $gp is restored from that location. */ + +static void +s_score_cprestore (int ignore ATTRIBUTE_UNUSED) +{ + int reg; + int cprestore_offset; + char insn_str[MAX_LITERAL_POOL_SIZE]; + + /* If we are not generating PIC code, .cprestore is ignored. */ + if (score_pic == NO_PIC) + { + s_ignore (0); + return; + } + + if ((reg = reg_required_here (&input_line_pointer, -1, REG_TYPE_SCORE)) == (int) FAIL + || skip_past_comma (&input_line_pointer) == (int) FAIL) + { + return; + } + + cprestore_offset = get_absolute_expression (); + + if (cprestore_offset <= 0x3fff) + { + sprintf (insn_str, "sw r%d, [r%d, %d]", GP, reg, cprestore_offset); + if (append_insn (insn_str, TRUE) == (int) FAIL) + return; + } + else + { + int r1_bak; + + r1_bak = nor1; + nor1 = 0; + + sprintf (insn_str, "li r1, %d", cprestore_offset); + if (append_insn (insn_str, TRUE) == (int) FAIL) + return; + + sprintf (insn_str, "add r1, r1, r%d", reg); + if (append_insn (insn_str, TRUE) == (int) FAIL) + return; + + sprintf (insn_str, "sw r%d, [r1]", GP); + if (append_insn (insn_str, TRUE) == (int) FAIL) + return; + + nor1 = r1_bak; + } + + demand_empty_rest_of_line (); +} + +/* Handle the .gpword pseudo-op. This is used when generating PIC + code. It generates a 32 bit GP relative reloc. */ +static void +s_score_gpword (int ignore ATTRIBUTE_UNUSED) +{ + expressionS ex; + char *p; + + /* When not generating PIC code, this is treated as .word. */ + if (score_pic == NO_PIC) + { + cons (4); + return; + } + expression (&ex); + if (ex.X_op != O_symbol || ex.X_add_number != 0) + { + as_bad (_("Unsupported use of .gpword")); + ignore_rest_of_line (); + } + p = frag_more (4); + md_number_to_chars (p, (valueT) 0, 4); + fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, FALSE, BFD_RELOC_GPREL32); + demand_empty_rest_of_line (); +} + +/* Handle the .cpadd pseudo-op. This is used when dealing with switch + tables in PIC code. */ + +static void +s_score_cpadd (int ignore ATTRIBUTE_UNUSED) +{ + int reg; + char insn_str[MAX_LITERAL_POOL_SIZE]; + + /* If we are not generating PIC code, .cpload is ignored. */ + if (score_pic == NO_PIC) + { + s_ignore (0); + return; + } + + if ((reg = reg_required_here (&input_line_pointer, -1, REG_TYPE_SCORE)) == (int) FAIL) + { + return; + } + demand_empty_rest_of_line (); + + /* Add $gp to the register named as an argument. */ + sprintf (insn_str, "add r%d, r%d, r%d", reg, reg, GP); + if (append_insn (insn_str, TRUE) == (int) FAIL) + return; +} + +#ifndef TC_IMPLICIT_LCOMM_ALIGNMENT +#define TC_IMPLICIT_LCOMM_ALIGNMENT(SIZE, P2VAR) \ + do \ + { \ + if ((SIZE) >= 8) \ + (P2VAR) = 3; \ + else if ((SIZE) >= 4) \ + (P2VAR) = 2; \ + else if ((SIZE) >= 2) \ + (P2VAR) = 1; \ + else \ + (P2VAR) = 0; \ + } \ + while (0) +#endif + +static void +s_score_lcomm (int bytes_p) +{ + char *name; + char c; + char *p; + int temp; + symbolS *symbolP; + segT current_seg = now_seg; + subsegT current_subseg = now_subseg; + const int max_alignment = 15; + int align = 0; + segT bss_seg = bss_section; + int needs_align = 0; + + name = input_line_pointer; + c = get_symbol_end (); + p = input_line_pointer; + *p = c; + + if (name == p) + { + as_bad (_("expected symbol name")); + discard_rest_of_line (); + return; + } + + SKIP_WHITESPACE (); + + /* Accept an optional comma after the name. The comma used to be + required, but Irix 5 cc does not generate it. */ + if (*input_line_pointer == ',') + { + ++input_line_pointer; + SKIP_WHITESPACE (); + } + + if (is_end_of_line[(unsigned char)*input_line_pointer]) + { + as_bad (_("missing size expression")); + return; + } + + if ((temp = get_absolute_expression ()) < 0) + { + as_warn (_("BSS length (%d) < 0 ignored"), temp); + ignore_rest_of_line (); + return; + } + +#if defined (TC_SCORE) + if (OUTPUT_FLAVOR == bfd_target_ecoff_flavour || OUTPUT_FLAVOR == bfd_target_elf_flavour) + { + /* For Score and Alpha ECOFF or ELF, small objects are put in .sbss. */ + if ((unsigned)temp <= bfd_get_gp_size (stdoutput)) + { + bss_seg = subseg_new (".sbss", 1); + seg_info (bss_seg)->bss = 1; +#ifdef BFD_ASSEMBLER + if (!bfd_set_section_flags (stdoutput, bss_seg, SEC_ALLOC)) + as_warn (_("error setting flags for \".sbss\": %s"), bfd_errmsg (bfd_get_error ())); +#endif + } + } +#endif + + SKIP_WHITESPACE (); + if (*input_line_pointer == ',') + { + ++input_line_pointer; + SKIP_WHITESPACE (); + + if (is_end_of_line[(unsigned char)*input_line_pointer]) + { + as_bad (_("missing alignment")); + return; + } + else + { + align = get_absolute_expression (); + needs_align = 1; + } + } + + if (!needs_align) + { + TC_IMPLICIT_LCOMM_ALIGNMENT (temp, align); + + /* Still zero unless TC_IMPLICIT_LCOMM_ALIGNMENT set it. */ + if (align) + record_alignment (bss_seg, align); + } + + if (needs_align) + { + if (bytes_p) + { + /* Convert to a power of 2. */ + if (align != 0) + { + unsigned int i; + + for (i = 0; align != 0; align >>= 1, ++i) + ; + align = i - 1; + } + } + + if (align > max_alignment) + { + align = max_alignment; + as_warn (_("alignment too large; %d assumed"), align); + } + else if (align < 0) + { + align = 0; + as_warn (_("alignment negative; 0 assumed")); + } + + record_alignment (bss_seg, align); + } + else + { + /* Assume some objects may require alignment on some systems. */ +#if defined (TC_ALPHA) && ! defined (VMS) + if (temp > 1) + { + align = ffs (temp) - 1; + if (temp % (1 << align)) + abort (); + } +#endif + } + + *p = 0; + symbolP = symbol_find_or_make (name); + *p = c; + + if ( +#if (defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT) \ + || defined (OBJ_BOUT) || defined (OBJ_MAYBE_BOUT)) +#ifdef BFD_ASSEMBLER + (OUTPUT_FLAVOR != bfd_target_aout_flavour + || (S_GET_OTHER (symbolP) == 0 && S_GET_DESC (symbolP) == 0)) && +#else + (S_GET_OTHER (symbolP) == 0 && S_GET_DESC (symbolP) == 0) && +#endif +#endif + (S_GET_SEGMENT (symbolP) == bss_seg || (!S_IS_DEFINED (symbolP) && S_GET_VALUE (symbolP) == 0))) + { + char *pfrag; + + subseg_set (bss_seg, 1); + + if (align) + frag_align (align, 0, 0); + + /* Detach from old frag. */ + if (S_GET_SEGMENT (symbolP) == bss_seg) + symbol_get_frag (symbolP)->fr_symbol = NULL; + + symbol_set_frag (symbolP, frag_now); + pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP, (offsetT) temp, NULL); + *pfrag = 0; + + + S_SET_SEGMENT (symbolP, bss_seg); + +#ifdef OBJ_COFF + /* The symbol may already have been created with a preceding + ".globl" directive -- be careful not to step on storage class + in that case. Otherwise, set it to static. */ + if (S_GET_STORAGE_CLASS (symbolP) != C_EXT) + { + S_SET_STORAGE_CLASS (symbolP, C_STAT); + } +#endif /* OBJ_COFF */ + +#ifdef S_SET_SIZE + S_SET_SIZE (symbolP, temp); +#endif + } + else + as_bad (_("symbol `%s' is already defined"), S_GET_NAME (symbolP)); + + subseg_set (current_seg, current_subseg); + + demand_empty_rest_of_line (); +} + +static void +insert_reg (const struct reg_entry *r, struct hash_control *htab) +{ + int i = 0; + int len = strlen (r->name) + 2; + char *buf = xmalloc (len); + char *buf2 = xmalloc (len); + + strcpy (buf + i, r->name); + for (i = 0; buf[i]; i++) + { + buf2[i] = TOUPPER (buf[i]); + } + buf2[i] = '\0'; + + hash_insert (htab, buf, (void *) r); + hash_insert (htab, buf2, (void *) r); +} + +static void +build_reg_hsh (struct reg_map *map) +{ + const struct reg_entry *r; + + if ((map->htab = hash_new ()) == NULL) + { + as_fatal (_("virtual memory exhausted")); + } + for (r = map->names; r->name != NULL; r++) + { + insert_reg (r, map->htab); + } +} + +void +md_begin (void) +{ + unsigned int i; + segT seg; + subsegT subseg; + + if ((score_ops_hsh = hash_new ()) == NULL) + as_fatal (_("virtual memory exhausted")); + + build_score_ops_hsh (); + + if ((dependency_insn_hsh = hash_new ()) == NULL) + as_fatal (_("virtual memory exhausted")); + + build_dependency_insn_hsh (); + + for (i = (int)REG_TYPE_FIRST; i < (int)REG_TYPE_MAX; i++) + build_reg_hsh (all_reg_maps + i); + + /* Initialize dependency vector. */ + init_dependency_vector (); + + bfd_set_arch_mach (stdoutput, TARGET_ARCH, 0); + seg = now_seg; + subseg = now_subseg; + pdr_seg = subseg_new (".pdr", (subsegT) 0); + (void)bfd_set_section_flags (stdoutput, pdr_seg, SEC_READONLY | SEC_RELOC | SEC_DEBUGGING); + (void)bfd_set_section_alignment (stdoutput, pdr_seg, 2); + subseg_set (seg, subseg); + + if (USE_GLOBAL_POINTER_OPT) + bfd_set_gp_size (stdoutput, g_switch_value); +} + + +const pseudo_typeS md_pseudo_table[] = +{ + {"bss", s_score_bss, 0}, + {"text", s_score_text, 0}, + {"word", cons, 4}, + {"long", cons, 4}, + {"extend", float_cons, 'x'}, + {"ldouble", float_cons, 'x'}, + {"packed", float_cons, 'p'}, + {"end", s_score_end, 0}, + {"ent", s_score_ent, 0}, + {"frame", s_score_frame, 0}, + {"rdata", s_change_sec, 'r'}, + {"sdata", s_change_sec, 's'}, + {"set", s_score_set, 0}, + {"mask", s_score_mask, 'R'}, + {"dword", cons, 8}, + {"lcomm", s_score_lcomm, 1}, + {"section", score_s_section, 0}, + {"cpload", s_score_cpload, 0}, + {"cprestore", s_score_cprestore, 0}, + {"gpword", s_score_gpword, 0}, + {"cpadd", s_score_cpadd, 0}, + {0, 0, 0} +}; + diff --git a/contrib/binutils/gas/config/tc-score.h b/contrib/binutils/gas/config/tc-score.h new file mode 100644 index 0000000..2cd4039 --- /dev/null +++ b/contrib/binutils/gas/config/tc-score.h @@ -0,0 +1,83 @@ +/* tc-score.h -- Score specific file for assembler + Copyright 2006 Free Software Foundation, Inc. + Contributed by: + Mei Ligang (ligang@sunnorth.com.cn) + Pei-Lin Tsai (pltsai@sunplus.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, Inc., 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef TC_SCORE +#define TC_SCORE + +#define TARGET_ARCH bfd_arch_score +#define WORKING_DOT_WORD +#define DIFF_EXPR_OK +#define RELOC_EXPANSION_POSSIBLE +#define MAX_RELOC_EXPANSION 2 +#define MAX_MEM_FOR_RS_ALIGN_CODE (3 + 4) + +#define md_undefined_symbol(name) NULL + +#define TARGET_FORMAT (target_big_endian ? "elf32-bigscore" : "elf32-littlescore") + +#define md_relax_frag(segment, fragp, stretch) score_relax_frag (segment, fragp, stretch) +extern int score_relax_frag (asection *, struct frag *, long); + +#define md_frag_check(fragp) score_frag_check (fragp) +extern void score_frag_check (fragS *); + +#define TC_VALIDATE_FIX(FIXP, SEGTYPE, SKIP) score_validate_fix (FIXP) +extern void score_validate_fix (struct fix *); + +#define TC_FORCE_RELOCATION(FIXP) score_force_relocation (FIXP) +extern int score_force_relocation (struct fix *); + +#define tc_fix_adjustable(fixp) score_fix_adjustable (fixp) +extern bfd_boolean score_fix_adjustable (struct fix *); + +#define elf_tc_final_processing score_elf_final_processing +extern void score_elf_final_processing (void); + +struct score_tc_frag_data +{ + unsigned int is_insn; + struct fix *fixp; +}; + +#define TC_FRAG_TYPE struct score_tc_frag_data + +#define TC_FRAG_INIT(FRAGP) \ + do \ + { \ + (FRAGP)->tc_frag_data.is_insn = (((FRAGP)->fr_type == rs_machine_dependent) ? 1 : 0); \ + } \ + while (0) + +#ifdef OBJ_ELF +#define GLOBAL_OFFSET_TABLE_NAME "_GLOBAL_OFFSET_TABLE_" +#else +#define GLOBAL_OFFSET_TABLE_NAME "__GLOBAL_OFFSET_TABLE_" +#endif + +enum score_pic_level +{ + NO_PIC, + PIC +}; + +#endif /*TC_SCORE */ diff --git a/contrib/binutils/gas/config/tc-sparc.c b/contrib/binutils/gas/config/tc-sparc.c index 10a1411..d4a409f 100644 --- a/contrib/binutils/gas/config/tc-sparc.c +++ b/contrib/binutils/gas/config/tc-sparc.c @@ -1,6 +1,6 @@ /* tc-sparc.c -- Assemble for the SPARC Copyright 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005 + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GAS, the GNU Assembler. @@ -19,8 +19,6 @@ to the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -#include <stdio.h> - #include "as.h" #include "safe-ctype.h" #include "subsegs.h" @@ -339,7 +337,7 @@ sparc_target_format () #endif #ifdef OBJ_ELF - return sparc_arch_size == 64 ? "elf64-sparc" : "elf32-sparc"; + return sparc_arch_size == 64 ? ELF64_TARGET_FORMAT : ELF_TARGET_FORMAT; #endif abort (); @@ -547,12 +545,12 @@ md_parse_option (c, arg) { if (sparc_arch_size == 32) { - if (strcmp (*l, "elf32-sparc") == 0) + if (CONST_STRNEQ (*l, "elf32-sparc")) break; } else { - if (strcmp (*l, "elf64-sparc") == 0) + if (CONST_STRNEQ (*l, "elf64-sparc")) break; } } @@ -1862,7 +1860,8 @@ sparc_ip (str, pinsn) case '\0': /* End of args. */ if (s[0] == ',' && s[1] == '%') { - static const struct tls_ops { + static const struct tls_ops + { /* The name as it appears in assembler. */ char *name; /* strlen (name), precomputed for speed */ @@ -1871,7 +1870,9 @@ sparc_ip (str, pinsn) int reloc; /* 1 if call. */ int call; - } tls_ops[] = { + } + tls_ops[] = + { { "tgd_add", 7, BFD_RELOC_SPARC_TLS_GD_ADD, 0 }, { "tgd_call", 8, BFD_RELOC_SPARC_TLS_GD_CALL, 1 }, { "tldm_add", 8, BFD_RELOC_SPARC_TLS_LDM_ADD, 0 }, @@ -1879,7 +1880,8 @@ sparc_ip (str, pinsn) { "tldo_add", 8, BFD_RELOC_SPARC_TLS_LDO_ADD, 0 }, { "tie_ldx", 7, BFD_RELOC_SPARC_TLS_IE_LDX, 0 }, { "tie_ld", 6, BFD_RELOC_SPARC_TLS_IE_LD, 0 }, - { "tie_add", 7, BFD_RELOC_SPARC_TLS_IE_ADD, 0 } + { "tie_add", 7, BFD_RELOC_SPARC_TLS_IE_ADD, 0 }, + { NULL, 0, 0, 0 } }; const struct tls_ops *o; char *s1; @@ -3311,9 +3313,9 @@ md_apply_fix (fixP, valP, segment) break; case BFD_RELOC_SPARC_WDISP16: - /* FIXME: simplify. */ - if (((val > 0) && (val & ~0x3fffc)) - || ((val < 0) && (~(val - 1) & ~0x3fffc))) + if ((val & 3) + || val >= 0x1fffc + || val <= -(offsetT) 0x20008) as_bad_where (fixP->fx_file, fixP->fx_line, _("relocation overflow")); /* FIXME: The +1 deserves a comment. */ @@ -3322,9 +3324,9 @@ md_apply_fix (fixP, valP, segment) break; case BFD_RELOC_SPARC_WDISP19: - /* FIXME: simplify. */ - if (((val > 0) && (val & ~0x1ffffc)) - || ((val < 0) && (~(val - 1) & ~0x1ffffc))) + if ((val & 3) + || val >= 0xffffc + || val <= -(offsetT) 0x100008) as_bad_where (fixP->fx_file, fixP->fx_line, _("relocation overflow")); /* FIXME: The +1 deserves a comment. */ @@ -3439,7 +3441,7 @@ md_apply_fix (fixP, valP, segment) arelent ** tc_gen_reloc (section, fixp) - asection *section ATTRIBUTE_UNUSED; + asection *section; fixS *fixp; { static arelent *relocs[3]; @@ -3582,6 +3584,16 @@ tc_gen_reloc (section, fixp) } #endif /* defined (OBJ_ELF) || defined (OBJ_AOUT) */ + /* Nothing is aligned in DWARF debugging sections. */ + if (bfd_get_section_flags (stdoutput, section) & SEC_DEBUGGING) + switch (code) + { + case BFD_RELOC_16: code = BFD_RELOC_SPARC_UA16; break; + case BFD_RELOC_32: code = BFD_RELOC_SPARC_UA32; break; + case BFD_RELOC_64: code = BFD_RELOC_SPARC_UA64; break; + default: break; + } + if (code == BFD_RELOC_SPARC_OLO10) reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_LO10); else @@ -4602,7 +4614,7 @@ sparc_cfi_frame_initial_instructions () } int -sparc_regname_to_dw2regnum (const char *regname) +sparc_regname_to_dw2regnum (char *regname) { char *p, *q; diff --git a/contrib/binutils/gas/config/tc-sparc.h b/contrib/binutils/gas/config/tc-sparc.h index 14da16a..90c0e95 100644 --- a/contrib/binutils/gas/config/tc-sparc.h +++ b/contrib/binutils/gas/config/tc-sparc.h @@ -1,6 +1,7 @@ /* tc-sparc.h - Macros and type defines for the sparc. Copyright 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc. + 1999, 2000, 2001, 2002, 2003, 2005, 2007 + Free Software Foundation, Inc. This file is part of GAS, the GNU Assembler. @@ -31,6 +32,19 @@ struct frag; #define TARGET_ARCH bfd_arch_sparc +#ifdef TE_FreeBSD +#define ELF_TARGET_FORMAT "elf32-sparc-freebsd" +#define ELF64_TARGET_FORMAT "elf64-sparc-freebsd" +#endif + +#ifndef ELF_TARGET_FORMAT +#define ELF_TARGET_FORMAT "elf32-sparc" +#endif + +#ifndef ELF64_TARGET_FORMAT +#define ELF64_TARGET_FORMAT "elf64-sparc" +#endif + extern const char *sparc_target_format PARAMS ((void)); #define TARGET_FORMAT sparc_target_format () @@ -78,7 +92,6 @@ extern void sparc_handle_align PARAMS ((struct frag *)); #define TC_FORCE_RELOCATION_LOCAL(FIX) \ (!(FIX)->fx_pcrel \ - || (FIX)->fx_plt \ || (sparc_pic_code \ && S_IS_EXTERNAL ((FIX)->fx_addsy)) \ || TC_FORCE_RELOCATION (FIX)) @@ -171,7 +184,7 @@ extern void cons_fix_new_sparc extern void sparc_cfi_frame_initial_instructions PARAMS ((void)); #define tc_regname_to_dw2regnum sparc_regname_to_dw2regnum -extern int sparc_regname_to_dw2regnum PARAMS ((const char *regname)); +extern int sparc_regname_to_dw2regnum PARAMS ((char *regname)); #define tc_cfi_emit_pcrel_expr sparc_cfi_emit_pcrel_expr extern void sparc_cfi_emit_pcrel_expr PARAMS ((expressionS *, unsigned int)); diff --git a/contrib/binutils/gas/config/tc-spu.c b/contrib/binutils/gas/config/tc-spu.c new file mode 100644 index 0000000..ef801df --- /dev/null +++ b/contrib/binutils/gas/config/tc-spu.c @@ -0,0 +1,1083 @@ +/* spu.c -- Assembler for the IBM Synergistic Processing Unit (SPU) + + Copyright 2006, 2007 Free Software Foundation, Inc. + + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "as.h" +#include "safe-ctype.h" +#include "subsegs.h" +#include "dwarf2dbg.h" + +const struct spu_opcode spu_opcodes[] = { +#define APUOP(TAG,MACFORMAT,OPCODE,MNEMONIC,ASMFORMAT,DEP,PIPE) \ + { MACFORMAT, (OPCODE) << (32-11), MNEMONIC, ASMFORMAT }, +#define APUOPFB(TAG,MACFORMAT,OPCODE,FB,MNEMONIC,ASMFORMAT,DEP,PIPE) \ + { MACFORMAT, ((OPCODE) << (32-11)) | ((FB) << (32-18)), MNEMONIC, ASMFORMAT }, +#include "opcode/spu-insns.h" +#undef APUOP +#undef APUOPFB +}; + +static const int spu_num_opcodes = + sizeof (spu_opcodes) / sizeof (spu_opcodes[0]); + +#define MAX_RELOCS 2 + +struct spu_insn +{ + unsigned int opcode; + expressionS exp[MAX_RELOCS]; + int reloc_arg[MAX_RELOCS]; + int flag[MAX_RELOCS]; + enum spu_insns tag; +}; + +static const char *get_imm (const char *param, struct spu_insn *insn, int arg); +static const char *get_reg (const char *param, struct spu_insn *insn, int arg, + int accept_expr); +static int calcop (struct spu_opcode *format, const char *param, + struct spu_insn *insn); +static void spu_cons (int); + +extern char *myname; +static struct hash_control *op_hash = NULL; + +/* These bits should be turned off in the first address of every segment */ +int md_seg_align = 7; + +/* These chars start a comment anywhere in a source file (except inside + another comment */ +const char comment_chars[] = "#"; + +/* These chars only start a comment at the beginning of a line. */ +const char line_comment_chars[] = "#"; + +/* gods own line continuation char */ +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 0f123.456 */ +/* or 0H1.234E-12 (see exp chars above) */ +const char FLT_CHARS[] = "dDfF"; + +const pseudo_typeS md_pseudo_table[] = +{ + {"align", s_align_ptwo, 4}, + {"bss", s_lcomm_bytes, 1}, + {"def", s_set, 0}, + {"dfloat", float_cons, 'd'}, + {"ffloat", float_cons, 'f'}, + {"global", s_globl, 0}, + {"half", cons, 2}, + {"int", spu_cons, 4}, + {"long", spu_cons, 4}, + {"quad", spu_cons, 8}, + {"string", stringer, 1}, + {"word", spu_cons, 4}, + /* Force set to be treated as an instruction. */ + {"set", NULL, 0}, + {".set", s_set, 0}, + /* Likewise for eqv. */ + {"eqv", NULL, 0}, + {".eqv", s_set, -1}, + {"file", (void (*) PARAMS ((int))) dwarf2_directive_file, 0 }, + {"loc", dwarf2_directive_loc, 0}, + {0,0,0} +}; + +void +md_begin (void) +{ + const char *retval = NULL; + int i; + + /* initialize hash table */ + + op_hash = hash_new (); + + /* loop until you see the end of the list */ + + for (i = 0; i < spu_num_opcodes; i++) + { + /* hash each mnemonic and record its position */ + + retval = hash_insert (op_hash, spu_opcodes[i].mnemonic, (PTR)&spu_opcodes[i]); + + if (retval != NULL && strcmp (retval, "exists") != 0) + as_fatal (_("Can't hash instruction '%s':%s"), + spu_opcodes[i].mnemonic, retval); + } +} + +const char *md_shortopts = ""; +struct option md_longopts[] = { +#define OPTION_APUASM (OPTION_MD_BASE) + {"apuasm", no_argument, NULL, OPTION_APUASM}, +#define OPTION_DD2 (OPTION_MD_BASE+1) + {"mdd2.0", no_argument, NULL, OPTION_DD2}, +#define OPTION_DD1 (OPTION_MD_BASE+2) + {"mdd1.0", no_argument, NULL, OPTION_DD1}, +#define OPTION_DD3 (OPTION_MD_BASE+3) + {"mdd3.0", no_argument, NULL, OPTION_DD3}, + { NULL, no_argument, NULL, 0 } +}; +size_t md_longopts_size = sizeof (md_longopts); + +/* When set (by -apuasm) our assembler emulates the behaviour of apuasm. + * e.g. don't add bias to float conversion and don't right shift + * immediate values. */ +static int emulate_apuasm; + +/* Use the dd2.0 instructions set. The only differences are some new + * register names and the orx insn */ +static int use_dd2 = 1; + +int +md_parse_option (int c, char *arg ATTRIBUTE_UNUSED) +{ + switch (c) + { + case OPTION_APUASM: + emulate_apuasm = 1; + break; + case OPTION_DD3: + use_dd2 = 1; + break; + case OPTION_DD2: + use_dd2 = 1; + break; + case OPTION_DD1: + use_dd2 = 0; + break; + default: + return 0; + } + return 1; +} + +void +md_show_usage (FILE *stream) +{ + fputs (_("\ +SPU options:\n\ + --apuasm emulate behaviour of apuasm\n"), + stream); +} + + +struct arg_encode { + int size; + int pos; + int rshift; + int lo, hi; + int wlo, whi; + bfd_reloc_code_real_type reloc; +}; + +static struct arg_encode arg_encode[A_MAX] = { + { 7, 0, 0, 0, 127, 0, -1, 0 }, /* A_T */ + { 7, 7, 0, 0, 127, 0, -1, 0 }, /* A_A */ + { 7, 14, 0, 0, 127, 0, -1, 0 }, /* A_B */ + { 7, 21, 0, 0, 127, 0, -1, 0 }, /* A_C */ + { 7, 7, 0, 0, 127, 0, -1, 0 }, /* A_S */ + { 7, 7, 0, 0, 127, 0, -1, 0 }, /* A_H */ + { 0, 0, 0, 0, -1, 0, -1, 0 }, /* A_P */ + { 7, 14, 0, 0, -1, 0, -1, BFD_RELOC_SPU_IMM7 }, /* A_S3 */ + { 7, 14, 0, -32, 31, -31, 0, BFD_RELOC_SPU_IMM7 }, /* A_S6 */ + { 7, 14, 0, 0, -1, 0, -1, BFD_RELOC_SPU_IMM7 }, /* A_S7N */ + { 7, 14, 0, -64, 63, -63, 0, BFD_RELOC_SPU_IMM7 }, /* A_S7 */ + { 8, 14, 0, 0, 127, 0, -1, BFD_RELOC_SPU_IMM8 }, /* A_U7A */ + { 8, 14, 0, 0, 127, 0, -1, BFD_RELOC_SPU_IMM8 }, /* A_U7B */ + { 10, 14, 0, -512, 511, -128, 255, BFD_RELOC_SPU_IMM10 }, /* A_S10B */ + { 10, 14, 0, -512, 511, 0, -1, BFD_RELOC_SPU_IMM10 }, /* A_S10 */ + { 2, 23, 9, -1024, 1023, 0, -1, BFD_RELOC_SPU_PCREL9a }, /* A_S11 */ + { 2, 14, 9, -1024, 1023, 0, -1, BFD_RELOC_SPU_PCREL9b }, /* A_S11I */ + { 10, 14, 4, -8192, 8191, 0, -1, BFD_RELOC_SPU_IMM10W }, /* A_S14 */ + { 16, 7, 0, -32768, 32767, 0, -1, BFD_RELOC_SPU_IMM16 }, /* A_S16 */ + { 16, 7, 2, -131072, 262143, 0, -1, BFD_RELOC_SPU_IMM16W }, /* A_S18 */ + { 16, 7, 2, -262144, 262143, 0, -1, BFD_RELOC_SPU_PCREL16 }, /* A_R18 */ + { 7, 14, 0, 0, -1, 0, -1, BFD_RELOC_SPU_IMM7 }, /* A_U3 */ + { 7, 14, 0, 0, 127, 0, 31, BFD_RELOC_SPU_IMM7 }, /* A_U5 */ + { 7, 14, 0, 0, 127, 0, 63, BFD_RELOC_SPU_IMM7 }, /* A_U6 */ + { 7, 14, 0, 0, -1, 0, -1, BFD_RELOC_SPU_IMM7 }, /* A_U7 */ + { 14, 0, 0, 0, 16383, 0, -1, 0 }, /* A_U14 */ + { 16, 7, 0, -32768, 65535, 0, -1, BFD_RELOC_SPU_IMM16 }, /* A_X16 */ + { 18, 7, 0, 0, 262143, 0, -1, BFD_RELOC_SPU_IMM18 }, /* A_U18 */ +}; + +/* Some flags for handling errors. This is very hackish and added after + * the fact. */ +static int syntax_error_arg; +static const char *syntax_error_param; +static int syntax_reg; + +static char * +insn_fmt_string (struct spu_opcode *format) +{ + static char buf[64]; + int len = 0; + int i; + + len += sprintf (&buf[len], "%s\t", format->mnemonic); + for (i = 1; i <= format->arg[0]; i++) + { + int arg = format->arg[i]; + char *exp; + if (i > 1 && arg != A_P && format->arg[i-1] != A_P) + buf[len++] = ','; + if (arg == A_P) + exp = "("; + else if (arg < A_P) + exp = i == syntax_error_arg ? "REG" : "reg"; + else + exp = i == syntax_error_arg ? "IMM" : "imm"; + len += sprintf (&buf[len], "%s", exp); + if (i > 1 && format->arg[i-1] == A_P) + buf[len++] = ')'; + } + buf[len] = 0; + return buf; +} + +void +md_assemble (char *op) +{ + char *param, *thisfrag; + char c; + struct spu_opcode *format; + struct spu_insn insn; + int i; + + assert (op); + + /* skip over instruction to find parameters */ + + for (param = op; *param != 0 && !ISSPACE (*param); param++) + ; + c = *param; + *param = 0; + + if (c != 0 && c != '\n') + param++; + + /* try to find the instruction in the hash table */ + + if ((format = (struct spu_opcode *) hash_find (op_hash, op)) == NULL) + { + as_bad (_("Invalid mnemonic '%s'"), op); + return; + } + + if (!use_dd2 && strcmp (format->mnemonic, "orx") == 0) + { + as_bad (_("'%s' is only available in DD2.0 or higher."), op); + return; + } + + while (1) + { + /* try parsing this instruction into insn */ + for (i = 0; i < MAX_RELOCS; i++) + { + insn.exp[i].X_add_symbol = 0; + insn.exp[i].X_op_symbol = 0; + insn.exp[i].X_add_number = 0; + insn.exp[i].X_op = O_illegal; + insn.reloc_arg[i] = -1; + insn.flag[i] = 0; + } + insn.opcode = format->opcode; + insn.tag = (enum spu_insns) (format - spu_opcodes); + + syntax_error_arg = 0; + syntax_error_param = 0; + syntax_reg = 0; + if (calcop (format, param, &insn)) + break; + + /* if it doesn't parse try the next instruction */ + if (!strcmp (format[0].mnemonic, format[1].mnemonic)) + format++; + else + { + int parg = format[0].arg[syntax_error_arg-1]; + + as_fatal (_("Error in argument %d. Expecting: \"%s\""), + syntax_error_arg - (parg == A_P), + insn_fmt_string (format)); + return; + } + } + + if ((syntax_reg & 4) + && ! (insn.tag == M_RDCH + || insn.tag == M_RCHCNT + || insn.tag == M_WRCH)) + as_warn (_("Mixing register syntax, with and without '$'.")); + if (syntax_error_param) + { + const char *d = syntax_error_param; + while (*d != '$') + d--; + as_warn (_("Treating '%-*s' as a symbol."), (int)(syntax_error_param - d), d); + } + + /* grow the current frag and plop in the opcode */ + + thisfrag = frag_more (4); + md_number_to_chars (thisfrag, insn.opcode, 4); + + /* if this instruction requires labels mark it for later */ + + for (i = 0; i < MAX_RELOCS; i++) + if (insn.reloc_arg[i] >= 0) + { + fixS *fixP; + bfd_reloc_code_real_type reloc = arg_encode[insn.reloc_arg[i]].reloc; + int pcrel = 0; + + if (reloc == BFD_RELOC_SPU_PCREL9a + || reloc == BFD_RELOC_SPU_PCREL9b + || reloc == BFD_RELOC_SPU_PCREL16) + pcrel = 1; + if (insn.flag[i] == 1) + reloc = BFD_RELOC_SPU_HI16; + else if (insn.flag[i] == 2) + reloc = BFD_RELOC_SPU_LO16; + fixP = fix_new_exp (frag_now, + thisfrag - frag_now->fr_literal, + 4, + &insn.exp[i], + pcrel, + reloc); + fixP->tc_fix_data.arg_format = insn.reloc_arg[i]; + fixP->tc_fix_data.insn_tag = insn.tag; + } + dwarf2_emit_insn (4); +} + +static int +calcop (struct spu_opcode *format, const char *param, struct spu_insn *insn) +{ + int i; + int paren = 0; + int arg; + + for (i = 1; i <= format->arg[0]; i++) + { + arg = format->arg[i]; + syntax_error_arg = i; + + while (ISSPACE (*param)) + param++; + if (*param == 0 || *param == ',') + return 0; + if (arg < A_P) + param = get_reg (param, insn, arg, 1); + else if (arg > A_P) + param = get_imm (param, insn, arg); + else if (arg == A_P) + { + paren++; + if ('(' != *param++) + return 0; + } + + if (!param) + return 0; + + while (ISSPACE (*param)) + param++; + + if (arg != A_P && paren) + { + paren--; + if (')' != *param++) + return 0; + } + else if (i < format->arg[0] + && format->arg[i] != A_P + && format->arg[i+1] != A_P) + { + if (',' != *param++) + { + syntax_error_arg++; + return 0; + } + } + } + while (ISSPACE (*param)) + param++; + return !paren && (*param == 0 || *param == '\n'); +} + +struct reg_name { + unsigned int regno; + unsigned int length; + char name[32]; +}; + +#define REG_NAME(NO,NM) { NO, sizeof (NM) - 1, NM } + +static struct reg_name reg_name[] = { + REG_NAME (0, "lr"), /* link register */ + REG_NAME (1, "sp"), /* stack pointer */ + REG_NAME (0, "rp"), /* link register */ + REG_NAME (127, "fp"), /* frame pointer */ +}; + +static struct reg_name sp_reg_name[] = { +}; + +static struct reg_name ch_reg_name[] = { + REG_NAME ( 0, "SPU_RdEventStat"), + REG_NAME ( 1, "SPU_WrEventMask"), + REG_NAME ( 2, "SPU_WrEventAck"), + REG_NAME ( 3, "SPU_RdSigNotify1"), + REG_NAME ( 4, "SPU_RdSigNotify2"), + REG_NAME ( 7, "SPU_WrDec"), + REG_NAME ( 8, "SPU_RdDec"), + REG_NAME ( 11, "SPU_RdEventMask"), /* DD2.0 only */ + REG_NAME ( 13, "SPU_RdMachStat"), + REG_NAME ( 14, "SPU_WrSRR0"), + REG_NAME ( 15, "SPU_RdSRR0"), + REG_NAME ( 28, "SPU_WrOutMbox"), + REG_NAME ( 29, "SPU_RdInMbox"), + REG_NAME ( 30, "SPU_WrOutIntrMbox"), + REG_NAME ( 9, "MFC_WrMSSyncReq"), + REG_NAME ( 12, "MFC_RdTagMask"), /* DD2.0 only */ + REG_NAME ( 16, "MFC_LSA"), + REG_NAME ( 17, "MFC_EAH"), + REG_NAME ( 18, "MFC_EAL"), + REG_NAME ( 19, "MFC_Size"), + REG_NAME ( 20, "MFC_TagID"), + REG_NAME ( 21, "MFC_Cmd"), + REG_NAME ( 22, "MFC_WrTagMask"), + REG_NAME ( 23, "MFC_WrTagUpdate"), + REG_NAME ( 24, "MFC_RdTagStat"), + REG_NAME ( 25, "MFC_RdListStallStat"), + REG_NAME ( 26, "MFC_WrListStallAck"), + REG_NAME ( 27, "MFC_RdAtomicStat"), +}; +#undef REG_NAME + +static const char * +get_reg (const char *param, struct spu_insn *insn, int arg, int accept_expr) +{ + unsigned regno; + int saw_prefix = 0; + + if (*param == '$') + { + saw_prefix = 1; + param++; + } + + if (arg == A_H) /* Channel */ + { + if ((param[0] == 'c' || param[0] == 'C') + && (param[1] == 'h' || param[1] == 'H') + && ISDIGIT (param[2])) + param += 2; + } + else if (arg == A_S) /* Special purpose register */ + { + if ((param[0] == 's' || param[0] == 'S') + && (param[1] == 'p' || param[1] == 'P') + && ISDIGIT (param[2])) + param += 2; + } + + if (ISDIGIT (*param)) + { + regno = 0; + while (ISDIGIT (*param)) + regno = regno * 10 + *param++ - '0'; + } + else + { + struct reg_name *rn; + unsigned int i, n, l = 0; + + if (arg == A_H) /* Channel */ + { + rn = ch_reg_name; + n = sizeof (ch_reg_name) / sizeof (*ch_reg_name); + } + else if (arg == A_S) /* Special purpose register */ + { + rn = sp_reg_name; + n = sizeof (sp_reg_name) / sizeof (*sp_reg_name); + } + else + { + rn = reg_name; + n = sizeof (reg_name) / sizeof (*reg_name); + } + regno = 128; + for (i = 0; i < n; i++) + if (rn[i].length > l + && 0 == strncasecmp (param, rn[i].name, rn[i].length)) + { + l = rn[i].length; + regno = rn[i].regno; + } + param += l; + } + + if (!use_dd2 + && arg == A_H) + { + if (regno == 11) + as_bad (_("'SPU_RdEventMask' (channel 11) is only available in DD2.0 or higher.")); + else if (regno == 12) + as_bad (_("'MFC_RdTagMask' (channel 12) is only available in DD2.0 or higher.")); + } + + if (regno < 128) + { + insn->opcode |= regno << arg_encode[arg].pos; + if ((!saw_prefix && syntax_reg == 1) + || (saw_prefix && syntax_reg == 2)) + syntax_reg |= 4; + syntax_reg |= saw_prefix ? 1 : 2; + return param; + } + + if (accept_expr) + { + char *save_ptr; + expressionS ex; + save_ptr = input_line_pointer; + input_line_pointer = (char *)param; + expression (&ex); + param = input_line_pointer; + input_line_pointer = save_ptr; + if (ex.X_op == O_register || ex.X_op == O_constant) + { + insn->opcode |= ex.X_add_number << arg_encode[arg].pos; + return param; + } + } + return 0; +} + +static const char * +get_imm (const char *param, struct spu_insn *insn, int arg) +{ + int val; + char *save_ptr; + int low = 0, high = 0; + int reloc_i = insn->reloc_arg[0] >= 0 ? 1 : 0; + + if (strncasecmp (param, "%lo(", 4) == 0) + { + param += 3; + low = 1; + as_warn (_("Using old style, %%lo(expr), please change to PPC style, expr@l.")); + } + else if (strncasecmp (param, "%hi(", 4) == 0) + { + param += 3; + high = 1; + as_warn (_("Using old style, %%hi(expr), please change to PPC style, expr@h.")); + } + else if (strncasecmp (param, "%pic(", 5) == 0) + { + /* Currently we expect %pic(expr) == expr, so do nothing here. + i.e. for code loaded at address 0 $toc will be 0. */ + param += 4; + } + + if (*param == '$') + { + /* Symbols can start with $, but if this symbol matches a register + name, it's probably a mistake. The only way to avoid this + warning is to rename the symbol. */ + struct spu_insn tmp_insn; + const char *np = get_reg (param, &tmp_insn, arg, 0); + + if (np) + syntax_error_param = np; + } + + save_ptr = input_line_pointer; + input_line_pointer = (char *) param; + expression (&insn->exp[reloc_i]); + param = input_line_pointer; + input_line_pointer = save_ptr; + + /* Similar to ppc_elf_suffix in tc-ppc.c. We have so few cases to + handle we do it inlined here. */ + if (param[0] == '@' && !ISALNUM (param[2]) && param[2] != '@') + { + if (param[1] == 'h' || param[1] == 'H') + { + high = 1; + param += 2; + } + else if (param[1] == 'l' || param[1] == 'L') + { + low = 1; + param += 2; + } + } + + if (insn->exp[reloc_i].X_op == O_constant) + { + val = insn->exp[reloc_i].X_add_number; + + if (emulate_apuasm) + { + /* Convert the value to a format we expect. */ + val <<= arg_encode[arg].rshift; + if (arg == A_U7A) + val = 173 - val; + else if (arg == A_U7B) + val = 155 - val; + } + + if (high) + val = val >> 16; + else if (low) + val = val & 0xffff; + + /* Warn about out of range expressions. */ + { + int hi = arg_encode[arg].hi; + int lo = arg_encode[arg].lo; + int whi = arg_encode[arg].whi; + int wlo = arg_encode[arg].wlo; + + if (hi > lo && (val < lo || val > hi)) + as_fatal (_("Constant expression %d out of range, [%d, %d]."), + val, lo, hi); + else if (whi > wlo && (val < wlo || val > whi)) + as_warn (_("Constant expression %d out of range, [%d, %d]."), + val, wlo, whi); + } + + if (arg == A_U7A) + val = 173 - val; + else if (arg == A_U7B) + val = 155 - val; + + /* Branch hints have a split encoding. Do the bottom part. */ + if (arg == A_S11 || arg == A_S11I) + insn->opcode |= ((val >> 2) & 0x7f); + + insn->opcode |= (((val >> arg_encode[arg].rshift) + & ((1 << arg_encode[arg].size) - 1)) + << arg_encode[arg].pos); + insn->reloc_arg[reloc_i] = -1; + insn->flag[reloc_i] = 0; + } + else + { + insn->reloc_arg[reloc_i] = arg; + if (high) + insn->flag[reloc_i] = 1; + else if (low) + insn->flag[reloc_i] = 2; + } + + return param; +} + +#define MAX_LITTLENUMS 6 + +/* 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) +{ + int prec; + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + LITTLENUM_TYPE *wordP; + char *t; + + switch (type) + { + case 'f': + case 'F': + case 's': + case 'S': + prec = 2; + break; + + case 'd': + case 'D': + case 'r': + case 'R': + prec = 4; + break; + + case 'x': + case 'X': + prec = 6; + break; + + case 'p': + case 'P': + prec = 6; + break; + + default: + *sizeP = 0; + return _("Bad call to MD_ATOF()"); + } + t = atof_ieee (input_line_pointer, type, words); + if (t) + input_line_pointer = t; + + *sizeP = prec * sizeof (LITTLENUM_TYPE); + for (wordP = words; prec--;) + { + md_number_to_chars (litP, (long) (*wordP++), sizeof (LITTLENUM_TYPE)); + litP += sizeof (LITTLENUM_TYPE); + } + return 0; +} + +#ifndef WORKING_DOT_WORD +int md_short_jump_size = 4; + +void +md_create_short_jump (char *ptr, + addressT from_addr ATTRIBUTE_UNUSED, + addressT to_addr ATTRIBUTE_UNUSED, + fragS *frag, + symbolS *to_symbol) +{ + ptr[0] = (char) 0xc0; + ptr[1] = 0x00; + ptr[2] = 0x00; + ptr[3] = 0x00; + fix_new (frag, + ptr - frag->fr_literal, + 4, + to_symbol, + (offsetT) 0, + 0, + BFD_RELOC_SPU_PCREL16); +} + +int md_long_jump_size = 4; + +void +md_create_long_jump (char *ptr, + addressT from_addr ATTRIBUTE_UNUSED, + addressT to_addr ATTRIBUTE_UNUSED, + fragS *frag, + symbolS *to_symbol) +{ + ptr[0] = (char) 0xc0; + ptr[1] = 0x00; + ptr[2] = 0x00; + ptr[3] = 0x00; + fix_new (frag, + ptr - frag->fr_literal, + 4, + to_symbol, + (offsetT) 0, + 0, + BFD_RELOC_SPU_PCREL16); +} +#endif + +/* Support @ppu on symbols referenced in .int/.long/.word/.quad. */ +static void +spu_cons (int nbytes) +{ + expressionS exp; + + if (is_it_end_of_statement ()) + { + demand_empty_rest_of_line (); + return; + } + + do + { + deferred_expression (&exp); + if ((exp.X_op == O_symbol + || exp.X_op == O_constant) + && strncasecmp (input_line_pointer, "@ppu", 4) == 0) + { + char *p = frag_more (nbytes); + enum bfd_reloc_code_real reloc; + + /* Check for identifier@suffix+constant. */ + input_line_pointer += 4; + if (*input_line_pointer == '-' || *input_line_pointer == '+') + { + expressionS new_exp; + + expression (&new_exp); + if (new_exp.X_op == O_constant) + exp.X_add_number += new_exp.X_add_number; + } + + reloc = nbytes == 4 ? BFD_RELOC_SPU_PPU32 : BFD_RELOC_SPU_PPU64; + fix_new_exp (frag_now, p - frag_now->fr_literal, nbytes, + &exp, 0, reloc); + } + else + emit_expr (&exp, nbytes); + } + while (*input_line_pointer++ == ','); + + /* Put terminator back into stream. */ + input_line_pointer--; + demand_empty_rest_of_line (); +} + +int +md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED, + segT segment_type ATTRIBUTE_UNUSED) +{ + as_fatal (_("Relaxation should never occur")); + return -1; +} + +/* If while processing a fixup, a reloc really needs to be created, + then it is done here. */ + +arelent * +tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp) +{ + arelent *reloc; + reloc = (arelent *) xmalloc (sizeof (arelent)); + reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + if (fixp->fx_addsy) + *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); + else if (fixp->fx_subsy) + *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy); + else + abort (); + 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) + { + as_bad_where (fixp->fx_file, fixp->fx_line, + _("reloc %d not supported by object file format"), + (int) fixp->fx_r_type); + return NULL; + } + reloc->addend = fixp->fx_addnumber; + return reloc; +} + +/* Round up a section's size to the appropriate boundary. */ + +valueT +md_section_align (segT seg, valueT size) +{ + int align = bfd_get_section_alignment (stdoutput, seg); + valueT mask = ((valueT) 1 << align) - 1; + + return (size + mask) & ~mask; +} + +/* Where a PC relative offset is calculated from. On the spu they + are calculated from the beginning of the branch instruction. */ + +long +md_pcrel_from (fixS *fixp) +{ + return fixp->fx_frag->fr_address + fixp->fx_where; +} + +/* Fill in rs_align_code fragments. */ + +void +spu_handle_align (fragS *fragp) +{ + static const unsigned char nop_pattern[8] = { + 0x40, 0x20, 0x00, 0x00, /* even nop */ + 0x00, 0x20, 0x00, 0x00, /* odd nop */ + }; + + int bytes; + char *p; + + if (fragp->fr_type != rs_align_code) + return; + + bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix; + p = fragp->fr_literal + fragp->fr_fix; + + if (bytes & 3) + { + int fix = bytes & 3; + memset (p, 0, fix); + p += fix; + bytes -= fix; + fragp->fr_fix += fix; + } + if (bytes & 4) + { + memcpy (p, &nop_pattern[4], 4); + p += 4; + bytes -= 4; + fragp->fr_fix += 4; + } + + memcpy (p, nop_pattern, 8); + fragp->fr_var = 8; +} + +void +md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) +{ + unsigned int res; + valueT val = *valP; + char *place = fixP->fx_where + fixP->fx_frag->fr_literal; + + if (fixP->fx_subsy != (symbolS *) NULL) + { + /* We can't actually support subtracting a symbol. */ + as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex")); + } + + if (fixP->fx_addsy != NULL) + { + if (fixP->fx_pcrel) + { + /* Hack around bfd_install_relocation brain damage. */ + val += fixP->fx_frag->fr_address + fixP->fx_where; + + switch (fixP->fx_r_type) + { + case BFD_RELOC_32: + fixP->fx_r_type = BFD_RELOC_32_PCREL; + break; + + case BFD_RELOC_SPU_PCREL16: + case BFD_RELOC_SPU_PCREL9a: + case BFD_RELOC_SPU_PCREL9b: + case BFD_RELOC_32_PCREL: + break; + + default: + as_bad_where (fixP->fx_file, fixP->fx_line, + _("expression too complex")); + break; + } + } + } + + fixP->fx_addnumber = val; + + if (fixP->fx_r_type == BFD_RELOC_SPU_PPU32 + || fixP->fx_r_type == BFD_RELOC_SPU_PPU64) + return; + + if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0) + { + fixP->fx_done = 1; + res = 0; + if (fixP->tc_fix_data.arg_format > A_P) + { + int hi = arg_encode[fixP->tc_fix_data.arg_format].hi; + int lo = arg_encode[fixP->tc_fix_data.arg_format].lo; + if (hi > lo && ((offsetT) val < lo || (offsetT) val > hi)) + as_bad_where (fixP->fx_file, fixP->fx_line, + "Relocation doesn't fit. (relocation value = 0x%lx)", + (long) val); + } + + switch (fixP->fx_r_type) + { + case BFD_RELOC_8: + md_number_to_chars (place, val, 1); + return; + + case BFD_RELOC_16: + md_number_to_chars (place, val, 2); + return; + + case BFD_RELOC_32: + md_number_to_chars (place, val, 4); + return; + + case BFD_RELOC_64: + md_number_to_chars (place, val, 8); + return; + + case BFD_RELOC_SPU_IMM7: + res = (val & 0x7f) << 14; + break; + + case BFD_RELOC_SPU_IMM8: + res = (val & 0xff) << 14; + break; + + case BFD_RELOC_SPU_IMM10: + res = (val & 0x3ff) << 14; + break; + + case BFD_RELOC_SPU_IMM10W: + res = (val & 0x3ff0) << 10; + break; + + case BFD_RELOC_SPU_IMM16: + res = (val & 0xffff) << 7; + break; + + case BFD_RELOC_SPU_IMM16W: + res = (val & 0x3fffc) << 5; + break; + + case BFD_RELOC_SPU_IMM18: + res = (val & 0x3ffff) << 7; + break; + + case BFD_RELOC_SPU_PCREL9a: + res = ((val & 0x1fc) >> 2) | ((val & 0x600) << 14); + break; + + case BFD_RELOC_SPU_PCREL9b: + res = ((val & 0x1fc) >> 2) | ((val & 0x600) << 5); + break; + + case BFD_RELOC_SPU_PCREL16: + res = (val & 0x3fffc) << 5; + break; + + default: + as_bad_where (fixP->fx_file, fixP->fx_line, + _("reloc %d not supported by object file format"), + (int) fixP->fx_r_type); + } + + if (res != 0) + { + place[0] |= (res >> 24) & 0xff; + place[1] |= (res >> 16) & 0xff; + place[2] |= (res >> 8) & 0xff; + place[3] |= (res) & 0xff; + } + } +} diff --git a/contrib/binutils/gas/config/tc-spu.h b/contrib/binutils/gas/config/tc-spu.h new file mode 100644 index 0000000..4c6c2d4 --- /dev/null +++ b/contrib/binutils/gas/config/tc-spu.h @@ -0,0 +1,107 @@ +/* spu.h -- Assembler for spu + + Copyright 2006, 2007 Free Software Foundation, Inc. + + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef TC_SPU +#define TC_SPU 1 + +#include "opcode/spu.h" + +#define TARGET_FORMAT "elf32-spu" +#define TARGET_ARCH bfd_arch_spu +#define TARGET_NAME "elf32-spu" + +#define TARGET_BYTES_BIG_ENDIAN 1 + +struct tc_fix_info { + unsigned short arg_format; + unsigned short insn_tag; +}; + +/* fixS will have a member named tc_fix_data of this type. */ +#define TC_FIX_TYPE struct tc_fix_info +#define TC_INIT_FIX_DATA(FIXP) \ + do \ + { \ + (FIXP)->tc_fix_data.arg_format = 0; \ + (FIXP)->tc_fix_data.insn_tag = 0; \ + } \ + while (0) + +/* Don't reduce function symbols to section symbols, and don't adjust + references to PPU symbols. */ +#define tc_fix_adjustable(FIXP) \ + (!(S_IS_FUNCTION ((FIXP)->fx_addsy) \ + || (FIXP)->fx_r_type == BFD_RELOC_SPU_PPU32 \ + || (FIXP)->fx_r_type == BFD_RELOC_SPU_PPU64)) + +/* Keep relocs on calls. Branches to function symbols are tail or + sibling calls. */ +#define TC_FORCE_RELOCATION(FIXP) \ + ((FIXP)->tc_fix_data.insn_tag == M_BRSL \ + || (FIXP)->tc_fix_data.insn_tag == M_BRASL \ + || (((FIXP)->tc_fix_data.insn_tag == M_BR \ + || (FIXP)->tc_fix_data.insn_tag == M_BRA) \ + && (FIXP)->fx_addsy != NULL \ + && S_IS_FUNCTION ((FIXP)->fx_addsy)) \ + || (FIXP)->fx_r_type == BFD_RELOC_SPU_PPU32 \ + || (FIXP)->fx_r_type == BFD_RELOC_SPU_PPU64 \ + || generic_force_reloc (FIXP)) + +/* Values passed to md_apply_fix don't include symbol values. */ +#define MD_APPLY_SYM_VALUE(FIX) 0 + +/* The spu uses pseudo-ops with no leading period. */ +#define NO_PSEUDO_DOT 1 + +/* Don't warn on word overflow; it happens on %hi relocs. */ +#undef WARN_SIGNED_OVERFLOW_WORD + +#define DIFF_EXPR_OK + +#define WORKING_DOT_WORD + +#define md_number_to_chars number_to_chars_bigendian + +#define md_convert_frag(b,s,f) {as_fatal (_("spu convert_frag\n"));} + +/* We don't need to do anything special for undefined symbols. */ +#define md_undefined_symbol(s) 0 + +extern symbolS *section_symbol (asection *); +#define md_operand(e) \ + do { \ + if (strncasecmp (input_line_pointer, "@ppu", 4) == 0) \ + { \ + e->X_op = O_symbol; \ + if (abs_section_sym == NULL) \ + abs_section_sym = section_symbol (absolute_section); \ + e->X_add_symbol = abs_section_sym; \ + e->X_add_number = 0; \ + } \ + } while (0) + +/* Fill in rs_align_code fragments. */ +extern void spu_handle_align PARAMS ((fragS *)); +#define HANDLE_ALIGN(frag) spu_handle_align (frag) + +#define MAX_MEM_FOR_RS_ALIGN_CODE (7 + 8) + +#endif /* TC_SPU */ diff --git a/contrib/binutils/gas/config/te-pep.h b/contrib/binutils/gas/config/te-pep.h new file mode 100644 index 0000000..164b22d --- /dev/null +++ b/contrib/binutils/gas/config/te-pep.h @@ -0,0 +1,10 @@ +#define TE_PEP +#define COFF_WITH_pex64 + +#define TE_PE +#define LEX_AT (LEX_BEGIN_NAME | LEX_NAME) /* Can have @'s inside labels. */ + +/* The PE format supports long section names. */ +#define COFF_LONG_SECTION_NAMES + +#include "obj-format.h" |