X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=opcodes%2Fcgen-opc.c;h=f3cfa9d9c38e35442ec6296f642729f4a128acc9;hb=6edbfd3beb15105dfe5c59ee3b22e3daefaea509;hp=56840a6a6f9f100706312e9d243313368504962a;hpb=30727aa6d12fb866494020c0b62ab265a2bdcdfe;p=deliverable%2Fbinutils-gdb.git diff --git a/opcodes/cgen-opc.c b/opcodes/cgen-opc.c index 56840a6a6f..f3cfa9d9c3 100644 --- a/opcodes/cgen-opc.c +++ b/opcodes/cgen-opc.c @@ -1,38 +1,39 @@ /* CGEN generic opcode support. - Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + Copyright (C) 1996-2020 Free Software Foundation, Inc. - This file is part of the GNU Binutils and GDB, the GNU debugger. + This file is part of libopcodes. - This program is free software; you can redistribute it and/or modify + This library 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) + the Free Software Foundation; either version 3, or (at your option) any later version. - This program 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. + It 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 this program; if not, write to the Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sysdep.h" -#include +#include "alloca-conf.h" #include #include "ansidecl.h" #include "libiberty.h" +#include "safe-ctype.h" #include "bfd.h" #include "symcat.h" #include "opcode/cgen.h" static unsigned int hash_keyword_name - PARAMS ((const CGEN_KEYWORD *, const char *, int)); + (const CGEN_KEYWORD *, const char *, int); static unsigned int hash_keyword_value - PARAMS ((const CGEN_KEYWORD *, unsigned int)); + (const CGEN_KEYWORD *, unsigned int); static void build_keyword_hash_tables - PARAMS ((CGEN_KEYWORD *)); + (CGEN_KEYWORD *); /* Return number of hash table entries to use for N elements. */ #define KEYWORD_HASH_SIZE(n) ((n) <= 31 ? 17 : 31) @@ -41,9 +42,7 @@ static void build_keyword_hash_tables The result is the keyword entry or NULL if not found. */ const CGEN_KEYWORD_ENTRY * -cgen_keyword_lookup_name (kt, name) - CGEN_KEYWORD *kt; - const char *name; +cgen_keyword_lookup_name (CGEN_KEYWORD *kt, const char *name) { const CGEN_KEYWORD_ENTRY *ke; const char *p,*n; @@ -64,9 +63,7 @@ cgen_keyword_lookup_name (kt, name) while (*p && (*p == *n - || (isalpha ((unsigned char) *p) - && (tolower ((unsigned char) *p) - == tolower ((unsigned char) *n))))) + || (ISALPHA (*p) && (TOLOWER (*p) == TOLOWER (*n))))) ++n, ++p; if (!*p && !*n) @@ -84,9 +81,7 @@ cgen_keyword_lookup_name (kt, name) The result is the keyword entry or NULL if not found. */ const CGEN_KEYWORD_ENTRY * -cgen_keyword_lookup_value (kt, value) - CGEN_KEYWORD *kt; - int value; +cgen_keyword_lookup_value (CGEN_KEYWORD *kt, int value) { const CGEN_KEYWORD_ENTRY *ke; @@ -108,11 +103,10 @@ cgen_keyword_lookup_value (kt, value) /* Add an entry to a keyword table. */ void -cgen_keyword_add (kt, ke) - CGEN_KEYWORD *kt; - CGEN_KEYWORD_ENTRY *ke; +cgen_keyword_add (CGEN_KEYWORD *kt, CGEN_KEYWORD_ENTRY *ke) { unsigned int hash; + size_t i; if (kt->name_hash_table == NULL) build_keyword_hash_tables (kt); @@ -127,6 +121,21 @@ cgen_keyword_add (kt, ke) if (ke->name[0] == 0) kt->null_entry = ke; + + for (i = 1; i < strlen (ke->name); i++) + if (! ISALNUM (ke->name[i]) + && ! strchr (kt->nonalpha_chars, ke->name[i])) + { + size_t idx = strlen (kt->nonalpha_chars); + + /* If you hit this limit, please don't just + increase the size of the field, instead + look for a better algorithm. */ + if (idx >= sizeof (kt->nonalpha_chars) - 1) + abort (); + kt->nonalpha_chars[idx] = ke->name[i]; + kt->nonalpha_chars[idx+1] = 0; + } } /* FIXME: Need function to return count of keywords. */ @@ -140,13 +149,11 @@ cgen_keyword_add (kt, ke) It is passed to each call to cgen_keyword_search_next. */ CGEN_KEYWORD_SEARCH -cgen_keyword_search_init (kt, spec) - CGEN_KEYWORD *kt; - const char *spec; +cgen_keyword_search_init (CGEN_KEYWORD *kt, const char *spec) { CGEN_KEYWORD_SEARCH search; - /* FIXME: Need to specify format of PARAMS. */ + /* FIXME: Need to specify format of params. */ if (spec != NULL) abort (); @@ -164,8 +171,7 @@ cgen_keyword_search_init (kt, spec) The result is the next entry or NULL if there are no more. */ const CGEN_KEYWORD_ENTRY * -cgen_keyword_search_next (search) - CGEN_KEYWORD_SEARCH *search; +cgen_keyword_search_next (CGEN_KEYWORD_SEARCH *search) { /* Has search finished? */ if (search->current_hash == search->table->hash_table_size) @@ -199,10 +205,9 @@ cgen_keyword_search_next (search) If CASE_SENSITIVE_P is non-zero, return a case sensitive hash. */ static unsigned int -hash_keyword_name (kt, name, case_sensitive_p) - const CGEN_KEYWORD *kt; - const char *name; - int case_sensitive_p; +hash_keyword_name (const CGEN_KEYWORD *kt, + const char *name, + int case_sensitive_p) { unsigned int hash; @@ -211,16 +216,14 @@ hash_keyword_name (kt, name, case_sensitive_p) hash = (hash * 97) + (unsigned char) *name; else for (hash = 0; *name; ++name) - hash = (hash * 97) + (unsigned char) tolower (*name); + hash = (hash * 97) + (unsigned char) TOLOWER (*name); return hash % kt->hash_table_size; } /* Return first entry in hash chain for VALUE. */ static unsigned int -hash_keyword_value (kt, value) - const CGEN_KEYWORD *kt; - unsigned int value; +hash_keyword_value (const CGEN_KEYWORD *kt, unsigned int value) { return value % kt->hash_table_size; } @@ -230,8 +233,7 @@ hash_keyword_value (kt, value) we're using the disassembler, but we keep things simple. */ static void -build_keyword_hash_tables (kt) - CGEN_KEYWORD *kt; +build_keyword_hash_tables (CGEN_KEYWORD *kt) { int i; /* Use the number of compiled in entries as an estimate for the @@ -259,11 +261,9 @@ build_keyword_hash_tables (kt) mach/isa. */ const CGEN_HW_ENTRY * -cgen_hw_lookup_by_name (cd, name) - CGEN_CPU_DESC cd; - const char *name; +cgen_hw_lookup_by_name (CGEN_CPU_DESC cd, const char *name) { - int i; + unsigned int i; const CGEN_HW_ENTRY **hw = cd->hw_table.entries; for (i = 0; i < cd->hw_table.num_entries; ++i) @@ -279,11 +279,9 @@ cgen_hw_lookup_by_name (cd, name) Returns NULL if HWNUM is not supported by the currently selected mach. */ const CGEN_HW_ENTRY * -cgen_hw_lookup_by_num (cd, hwnum) - CGEN_CPU_DESC cd; - int hwnum; +cgen_hw_lookup_by_num (CGEN_CPU_DESC cd, unsigned int hwnum) { - int i; + unsigned int i; const CGEN_HW_ENTRY **hw = cd->hw_table.entries; /* ??? This can be speeded up. */ @@ -301,11 +299,9 @@ cgen_hw_lookup_by_num (cd, hwnum) mach/isa. */ const CGEN_OPERAND * -cgen_operand_lookup_by_name (cd, name) - CGEN_CPU_DESC cd; - const char *name; +cgen_operand_lookup_by_name (CGEN_CPU_DESC cd, const char *name) { - int i; + unsigned int i; const CGEN_OPERAND **op = cd->operand_table.entries; for (i = 0; i < cd->operand_table.num_entries; ++i) @@ -322,9 +318,7 @@ cgen_operand_lookup_by_name (cd, name) mach/isa. */ const CGEN_OPERAND * -cgen_operand_lookup_by_num (cd, opnum) - CGEN_CPU_DESC cd; - int opnum; +cgen_operand_lookup_by_num (CGEN_CPU_DESC cd, int opnum) { return cd->operand_table.entries[opnum]; } @@ -334,8 +328,7 @@ cgen_operand_lookup_by_num (cd, opnum) /* Return number of instructions. This includes any added at runtime. */ int -cgen_insn_count (cd) - CGEN_CPU_DESC cd; +cgen_insn_count (CGEN_CPU_DESC cd) { int count = cd->insn_table.num_init_entries; CGEN_INSN_LIST *rt_insns = cd->insn_table.new_entries; @@ -350,8 +343,7 @@ cgen_insn_count (cd) This includes any added at runtime. */ int -cgen_macro_insn_count (cd) - CGEN_CPU_DESC cd; +cgen_macro_insn_count (CGEN_CPU_DESC cd) { int count = cd->macro_insn_table.num_init_entries; CGEN_INSN_LIST *rt_insns = cd->macro_insn_table.new_entries; @@ -365,32 +357,35 @@ cgen_macro_insn_count (cd) /* Cover function to read and properly byteswap an insn value. */ CGEN_INSN_INT -cgen_get_insn_value (cd, buf, length) - CGEN_CPU_DESC cd; - unsigned char *buf; - int length; +cgen_get_insn_value (CGEN_CPU_DESC cd, unsigned char *buf, int length) { - CGEN_INSN_INT value; + int big_p = (cd->insn_endian == CGEN_ENDIAN_BIG); + int insn_chunk_bitsize = cd->insn_chunk_bitsize; + CGEN_INSN_INT value = 0; - switch (length) + if (insn_chunk_bitsize != 0 && insn_chunk_bitsize < length) { - case 8: - value = *buf; - break; - case 16: - if (cd->insn_endian == CGEN_ENDIAN_BIG) - value = bfd_getb16 (buf); - else - value = bfd_getl16 (buf); - break; - case 32: - if (cd->insn_endian == CGEN_ENDIAN_BIG) - value = bfd_getb32 (buf); - else - value = bfd_getl32 (buf); - break; - default: - abort (); + /* We need to divide up the incoming value into insn_chunk_bitsize-length + segments, and endian-convert them, one at a time. */ + int i; + + /* Enforce divisibility. */ + if ((length % insn_chunk_bitsize) != 0) + abort (); + + for (i = 0; i < length; i += insn_chunk_bitsize) /* NB: i == bits */ + { + int bit_index; + bfd_vma this_value; + + bit_index = i; /* NB: not dependent on endianness; opposite of cgen_put_insn_value! */ + this_value = bfd_get_bits (& buf[bit_index / 8], insn_chunk_bitsize, big_p); + value = (value << insn_chunk_bitsize) | this_value; + } + } + else + { + value = bfd_get_bits (buf, length, cd->insn_endian == CGEN_ENDIAN_BIG); } return value; @@ -399,31 +394,36 @@ cgen_get_insn_value (cd, buf, length) /* Cover function to store an insn value properly byteswapped. */ void -cgen_put_insn_value (cd, buf, length, value) - CGEN_CPU_DESC cd; - unsigned char *buf; - int length; - CGEN_INSN_INT value; +cgen_put_insn_value (CGEN_CPU_DESC cd, + unsigned char *buf, + int length, + CGEN_INSN_INT value) { - switch (length) + int big_p = (cd->insn_endian == CGEN_ENDIAN_BIG); + int insn_chunk_bitsize = cd->insn_chunk_bitsize; + + if (insn_chunk_bitsize != 0 && insn_chunk_bitsize < length) { - case 8: - buf[0] = value; - break; - case 16: - if (cd->insn_endian == CGEN_ENDIAN_BIG) - bfd_putb16 (value, buf); - else - bfd_putl16 (value, buf); - break; - case 32: - if (cd->insn_endian == CGEN_ENDIAN_BIG) - bfd_putb32 (value, buf); - else - bfd_putl32 (value, buf); - break; - default: - abort (); + /* We need to divide up the incoming value into insn_chunk_bitsize-length + segments, and endian-convert them, one at a time. */ + int i; + + /* Enforce divisibility. */ + if ((length % insn_chunk_bitsize) != 0) + abort (); + + for (i = 0; i < length; i += insn_chunk_bitsize) /* NB: i == bits */ + { + int bit_index; + + bit_index = (length - insn_chunk_bitsize - i); /* NB: not dependent on endianness! */ + bfd_put_bits ((bfd_vma) value, & buf[bit_index / 8], insn_chunk_bitsize, big_p); + value >>= insn_chunk_bitsize; + } + } + else + { + bfd_put_bits ((bfd_vma) value, buf, length, big_p); } } @@ -443,28 +443,23 @@ cgen_put_insn_value (cd, buf, length, value) /* ??? Will need to be revisited for VLIW architectures. */ const CGEN_INSN * -cgen_lookup_insn (cd, insn, insn_int_value, insn_bytes_value, length, fields, - alias_p) - CGEN_CPU_DESC cd; - const CGEN_INSN *insn; - CGEN_INSN_INT insn_int_value; - /* ??? CGEN_INSN_BYTES would be a nice type name to use here. */ - unsigned char *insn_bytes_value; - int length; - CGEN_FIELDS *fields; - int alias_p; +cgen_lookup_insn (CGEN_CPU_DESC cd, + const CGEN_INSN *insn, + CGEN_INSN_INT insn_int_value, + /* ??? CGEN_INSN_BYTES would be a nice type name to use here. */ + unsigned char *insn_bytes_value, + int length, + CGEN_FIELDS *fields, + int alias_p) { - unsigned char *buf; - CGEN_INSN_INT base_insn; CGEN_EXTRACT_INFO ex_info; CGEN_EXTRACT_INFO *info; if (cd->int_insn_p) { info = NULL; - buf = (unsigned char *) alloca (cd->max_insn_bitsize / 8); - cgen_put_insn_value (cd, buf, length, insn_int_value); - base_insn = insn_int_value; + insn_bytes_value = (unsigned char *) xmalloc (cd->max_insn_bitsize / 8); + cgen_put_insn_value (cd, insn_bytes_value, length, insn_int_value); } else { @@ -472,8 +467,7 @@ cgen_lookup_insn (cd, insn, insn_int_value, insn_bytes_value, length, fields, ex_info.dis_info = NULL; ex_info.insn_bytes = insn_bytes_value; ex_info.valid = -1; - buf = insn_bytes_value; - base_insn = cgen_get_insn_value (cd, buf, length); + insn_int_value = cgen_get_insn_value (cd, insn_bytes_value, length); } if (!insn) @@ -483,7 +477,8 @@ cgen_lookup_insn (cd, insn, insn_int_value, insn_bytes_value, length, fields, /* The instructions are stored in hash lists. Pick the first one and keep trying until we find the right one. */ - insn_list = cgen_dis_lookup_insn (cd, buf, base_insn); + insn_list = cgen_dis_lookup_insn (cd, (char *) insn_bytes_value, + insn_int_value); while (insn_list != NULL) { insn = insn_list->insn; @@ -495,18 +490,18 @@ cgen_lookup_insn (cd, insn, insn_int_value, insn_bytes_value, length, fields, /* Basic bit mask must be correct. */ /* ??? May wish to allow target to defer this check until the extract handler. */ - if ((base_insn & CGEN_INSN_BASE_MASK (insn)) + if ((insn_int_value & CGEN_INSN_BASE_MASK (insn)) == CGEN_INSN_BASE_VALUE (insn)) { /* ??? 0 is passed for `pc' */ int elength = CGEN_EXTRACT_FN (cd, insn) - (cd, insn, info, base_insn, fields, (bfd_vma) 0); + (cd, insn, info, insn_int_value, fields, (bfd_vma) 0); if (elength > 0) { /* sanity check */ if (length != 0 && length != elength) abort (); - return insn; + break; } } } @@ -526,15 +521,17 @@ cgen_lookup_insn (cd, insn, insn_int_value, insn_bytes_value, length, fields, /* ??? 0 is passed for `pc' */ length = CGEN_EXTRACT_FN (cd, insn) - (cd, insn, info, base_insn, fields, (bfd_vma) 0); + (cd, insn, info, insn_int_value, fields, (bfd_vma) 0); /* Sanity check: must succeed. Could relax this later if it ever proves useful. */ if (length == 0) abort (); - return insn; } - return NULL; + if (cd->int_insn_p) + free (insn_bytes_value); + + return insn; } /* Fill in the operand instances used by INSN whose operands are FIELDS. @@ -542,11 +539,10 @@ cgen_lookup_insn (cd, insn, insn_int_value, insn_bytes_value, length, fields, in. */ void -cgen_get_insn_operands (cd, insn, fields, indices) - CGEN_CPU_DESC cd; - const CGEN_INSN *insn; - const CGEN_FIELDS *fields; - int *indices; +cgen_get_insn_operands (CGEN_CPU_DESC cd, + const CGEN_INSN *insn, + const CGEN_FIELDS *fields, + int *indices) { const CGEN_OPINST *opinst; int i; @@ -574,16 +570,14 @@ cgen_get_insn_operands (cd, insn, fields, indices) recognized. */ const CGEN_INSN * -cgen_lookup_get_insn_operands (cd, insn, insn_int_value, insn_bytes_value, - length, indices, fields) - CGEN_CPU_DESC cd; - const CGEN_INSN *insn; - CGEN_INSN_INT insn_int_value; - /* ??? CGEN_INSN_BYTES would be a nice type name to use here. */ - unsigned char *insn_bytes_value; - int length; - int *indices; - CGEN_FIELDS *fields; +cgen_lookup_get_insn_operands (CGEN_CPU_DESC cd, + const CGEN_INSN *insn, + CGEN_INSN_INT insn_int_value, + /* ??? CGEN_INSN_BYTES would be a nice type name to use here. */ + unsigned char *insn_bytes_value, + int length, + int *indices, + CGEN_FIELDS *fields) { /* Pass non-zero for ALIAS_P only if INSN != NULL. If INSN == NULL, we want a real insn. */ @@ -595,3 +589,24 @@ cgen_lookup_get_insn_operands (cd, insn, insn_int_value, insn_bytes_value, cgen_get_insn_operands (cd, insn, fields, indices); return insn; } + +/* Allow signed overflow of instruction fields. */ +void +cgen_set_signed_overflow_ok (CGEN_CPU_DESC cd) +{ + cd->signed_overflow_ok_p = 1; +} + +/* Generate an error message if a signed field in an instruction overflows. */ +void +cgen_clear_signed_overflow_ok (CGEN_CPU_DESC cd) +{ + cd->signed_overflow_ok_p = 0; +} + +/* Will an error message be generated if a signed field in an instruction overflows ? */ +unsigned int +cgen_signed_overflow_ok_p (CGEN_CPU_DESC cd) +{ + return cd->signed_overflow_ok_p; +}