X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=opcodes%2Fcgen-dis.in;h=91d51739660b542ad128dea7f8043cba2c289d72;hb=c1d66d5f24eb54a6453b3a813cbc7a7e0b5d15fe;hp=06e22eb72f69782594d2b48b416e6a06f78eb585;hpb=fa803dc60f0bf01297674c41d001798e18ade4dc;p=binutils-gdb.git diff --git a/opcodes/cgen-dis.in b/opcodes/cgen-dis.in index 06e22eb72f6..91d51739660 100644 --- a/opcodes/cgen-dis.in +++ b/opcodes/cgen-dis.in @@ -1,135 +1,118 @@ /* Disassembler interface for targets using CGEN. -*- C -*- CGEN: Cpu tools GENerator -This file is used to generate @arch@-dis.c. + THIS FILE IS MACHINE GENERATED WITH CGEN. + - the resultant file is machine generated, cgen-dis.in isn't -Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Copyright (C) 1996-2021 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 -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. + 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 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. */ + 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., + 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* ??? Eventually more and more of this stuff can go to cpu-independent files. + Keep that in mind. */ #include "sysdep.h" #include #include "ansidecl.h" -#include "dis-asm.h" +#include "disassemble.h" #include "bfd.h" #include "symcat.h" -#include "@arch@-opc.h" - -/* ??? The layout of this stuff is still work in progress. - For speed in assembly/disassembly, we use inline functions. That of course - will only work for GCC. When this stuff is finished, we can decide whether - to keep the inline functions (and only get the performance increase when - compiled with GCC), or switch to macros, or use something else. -*/ +#include "libiberty.h" +#include "@prefix@-desc.h" +#include "@prefix@-opc.h" +#include "opintl.h" /* Default text to print if an instruction isn't recognized. */ -#define UNKNOWN_INSN_MSG "*unknown*" - -/* FIXME: Machine generate. */ -#ifndef CGEN_PCREL_OFFSET -#define CGEN_PCREL_OFFSET 0 -#endif - -static int print_insn PARAMS ((bfd_vma, disassemble_info *, char *, int)); - -static int extract_insn_normal - PARAMS ((const CGEN_INSN *, void *, cgen_insn_t, CGEN_FIELDS *)); +#define UNKNOWN_INSN_MSG _("*unknown*") + +static void print_normal + (CGEN_CPU_DESC, void *, long, unsigned int, bfd_vma, int); +static void print_address + (CGEN_CPU_DESC, void *, bfd_vma, unsigned int, bfd_vma, int) ATTRIBUTE_UNUSED; +static void print_keyword + (CGEN_CPU_DESC, void *, CGEN_KEYWORD *, long, unsigned int) ATTRIBUTE_UNUSED; static void print_insn_normal - PARAMS ((void *, const CGEN_INSN *, CGEN_FIELDS *, bfd_vma, int)); + (CGEN_CPU_DESC, void *, const CGEN_INSN *, CGEN_FIELDS *, bfd_vma, int); +static int print_insn + (CGEN_CPU_DESC, bfd_vma, disassemble_info *, bfd_byte *, unsigned); +static int default_print_insn + (CGEN_CPU_DESC, bfd_vma, disassemble_info *) ATTRIBUTE_UNUSED; +static int read_insn + (CGEN_CPU_DESC, bfd_vma, disassemble_info *, bfd_byte *, int, CGEN_EXTRACT_INFO *, + unsigned long *); -/* Default extraction routine. - - ATTRS is a mask of the boolean attributes. We only need `unsigned', - but for generality we take a bitmask of all of them. */ +/* -- disassembler routines inserted here. */ + +/* Default print handler. */ -static int -extract_normal (buf_ctrl, insn_value, attrs, start, length, shift, total_length, valuep) - PTR buf_ctrl; - cgen_insn_t insn_value; - unsigned int attrs; - int start, length, shift, total_length; - long *valuep; +static void +print_normal (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, + void *dis_info, + long value, + unsigned int attrs, + bfd_vma pc ATTRIBUTE_UNUSED, + int length ATTRIBUTE_UNUSED) { - long value; + disassemble_info *info = (disassemble_info *) dis_info; -#ifdef CGEN_INT_INSN -#if 0 - value = ((insn_value >> (CGEN_BASE_INSN_BITSIZE - (start + length))) - & ((1 << length) - 1)); -#else - value = ((insn_value >> (total_length - (start + length))) - & ((1 << length) - 1)); -#endif - if (! (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_UNSIGNED)) - && (value & (1 << (length - 1)))) - value -= 1 << length; -#else - /* FIXME: unfinished */ -#endif - - /* This is backwards as we undo the effects of insert_normal. */ - if (shift < 0) - value >>= -shift; + /* Print the operand as directed by the attributes. */ + if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY)) + ; /* nothing to do */ + else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED)) + (*info->fprintf_func) (info->stream, "%ld", value); else - value <<= shift; - - *valuep = value; - - /* FIXME: for now */ - return 1; + (*info->fprintf_func) (info->stream, "0x%lx", value); } -/* Default print handler. */ +/* Default address handler. */ static void -print_normal (dis_info, value, attrs, pc, length) - PTR dis_info; - long value; - unsigned int attrs; - unsigned long pc; /* FIXME: should be bfd_vma */ - int length; +print_address (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, + void *dis_info, + bfd_vma value, + unsigned int attrs, + bfd_vma pc ATTRIBUTE_UNUSED, + int length ATTRIBUTE_UNUSED) { - disassemble_info *info = dis_info; + disassemble_info *info = (disassemble_info *) dis_info; /* Print the operand as directed by the attributes. */ - if (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_FAKE)) - ; /* nothing to do (??? at least not yet) */ - else if (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_PCREL_ADDR)) - (*info->print_address_func) (pc + CGEN_PCREL_OFFSET + value, info); - /* ??? Not all cases of this are currently caught. */ - else if (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_ABS_ADDR)) - /* FIXME: Why & 0xffffffff? */ - (*info->print_address_func) ((bfd_vma) value & 0xffffffff, info); - else if (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_UNSIGNED)) - (*info->fprintf_func) (info->stream, "0x%lx", value); + if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY)) + ; /* Nothing to do. */ + else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_PCREL_ADDR)) + (*info->print_address_func) (value, info); + else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_ABS_ADDR)) + (*info->print_address_func) (value, info); + else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED)) + (*info->fprintf_func) (info->stream, "%ld", (long) value); else - (*info->fprintf_func) (info->stream, "%ld", value); + (*info->fprintf_func) (info->stream, "0x%lx", (long) value); } /* Keyword print handler. */ static void -print_keyword (dis_info, keyword_table, value, attrs) - PTR dis_info; - CGEN_KEYWORD *keyword_table; - long value; - CGEN_ATTR *attrs; +print_keyword (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, + void *dis_info, + CGEN_KEYWORD *keyword_table, + long value, + unsigned int attrs ATTRIBUTE_UNUSED) { - disassemble_info *info = dis_info; + disassemble_info *info = (disassemble_info *) dis_info; const CGEN_KEYWORD_ENTRY *ke; ke = cgen_keyword_lookup_value (keyword_table, value); @@ -139,64 +122,24 @@ print_keyword (dis_info, keyword_table, value, attrs) (*info->fprintf_func) (info->stream, "???"); } -/* -- disassembler routines inserted here */ - -/* Default insn extractor. - - The extracted fields are stored in DIS_FLDS. - BUF_CTRL is used to handle reading variable length insns (FIXME: not done). - Return the length of the insn in bits, or 0 if no match. */ - -static int -extract_insn_normal (insn, buf_ctrl, insn_value, fields) - const CGEN_INSN *insn; - PTR buf_ctrl; - cgen_insn_t insn_value; - CGEN_FIELDS *fields; -{ - const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn); - const unsigned char *syn; - - CGEN_FIELDS_BITSIZE (fields) = CGEN_INSN_BITSIZE (insn); - - CGEN_INIT_EXTRACT (); - - for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn) - { - int length; - - if (CGEN_SYNTAX_CHAR_P (*syn)) - continue; - - length = @arch@_cgen_extract_operand (CGEN_SYNTAX_FIELD (*syn), - buf_ctrl, insn_value, fields); - if (length == 0) - return 0; - } - - /* We recognized and successfully extracted this insn. */ - return CGEN_INSN_BITSIZE (insn); -} - /* Default insn printer. - DIS_INFO is defined as `PTR' so the disassembler needn't know anything - about disassemble_info. -*/ + DIS_INFO is defined as `void *' so the disassembler needn't know anything + about disassemble_info. */ static void -print_insn_normal (dis_info, insn, fields, pc, length) - PTR dis_info; - const CGEN_INSN *insn; - CGEN_FIELDS *fields; - bfd_vma pc; - int length; +print_insn_normal (CGEN_CPU_DESC cd, + void *dis_info, + const CGEN_INSN *insn, + CGEN_FIELDS *fields, + bfd_vma pc, + int length) { const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn); - disassemble_info *info = dis_info; - const unsigned char *syn; + disassemble_info *info = (disassemble_info *) dis_info; + const CGEN_SYNTAX_CHAR_TYPE *syn; - CGEN_INIT_PRINT (); + CGEN_INIT_PRINT (cd); for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn) { @@ -212,76 +155,137 @@ print_insn_normal (dis_info, insn, fields, pc, length) } /* We have an operand. */ - @arch@_cgen_print_operand (CGEN_SYNTAX_FIELD (*syn), info, - fields, CGEN_INSN_ATTRS (insn), pc, length); + @arch@_cgen_print_operand (cd, CGEN_SYNTAX_FIELD (*syn), info, + fields, CGEN_INSN_ATTRS (insn), pc, length); } } -/* Default value for CGEN_PRINT_INSN. - Given BUFLEN bits (target byte order) read into BUF, look up the - insn in the instruction table and disassemble it. +/* Subroutine of print_insn. Reads an insn into the given buffers and updates + the extract info. + Returns 0 if all is well, non-zero otherwise. */ - The result is the size of the insn in bytes. */ +static int +read_insn (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, + bfd_vma pc, + disassemble_info *info, + bfd_byte *buf, + int buflen, + CGEN_EXTRACT_INFO *ex_info, + unsigned long *insn_value) +{ + int status = (*info->read_memory_func) (pc, buf, buflen, info); -#ifndef CGEN_PRINT_INSN -#define CGEN_PRINT_INSN print_insn -#endif + if (status != 0) + { + (*info->memory_error_func) (status, pc, info); + return -1; + } + + ex_info->dis_info = info; + ex_info->valid = (1 << buflen) - 1; + ex_info->insn_bytes = buf; + + *insn_value = bfd_get_bits (buf, buflen * 8, info->endian == BFD_ENDIAN_BIG); + return 0; +} + +/* Utility to print an insn. + BUF is the base part of the insn, target byte order, BUFLEN bytes long. + The result is the size of the insn in bytes or zero for an unknown insn + or -1 if an error occurs fetching data (memory_error_func will have + been called). */ static int -print_insn (pc, info, buf, buflen) - bfd_vma pc; - disassemble_info *info; - char *buf; - int buflen; +print_insn (CGEN_CPU_DESC cd, + bfd_vma pc, + disassemble_info *info, + bfd_byte *buf, + unsigned int buflen) { - unsigned long insn_value; + CGEN_INSN_INT insn_value; const CGEN_INSN_LIST *insn_list; + CGEN_EXTRACT_INFO ex_info; + int basesize; - switch (buflen) - { - case 8: - insn_value = buf[0]; - break; - case 16: - insn_value = info->endian == BFD_ENDIAN_BIG ? bfd_getb16 (buf) : bfd_getl16 (buf); - break; - case 32: - insn_value = info->endian == BFD_ENDIAN_BIG ? bfd_getb32 (buf) : bfd_getl32 (buf); - break; - default: - abort (); - } + /* Extract base part of instruction, just in case CGEN_DIS_* uses it. */ + basesize = cd->base_insn_bitsize < buflen * 8 ? + cd->base_insn_bitsize : buflen * 8; + insn_value = cgen_get_insn_value (cd, buf, basesize, cd->insn_endian); + + + /* Fill in ex_info fields like read_insn would. Don't actually call + read_insn, since the incoming buffer is already read (and possibly + modified a la m32r). */ + ex_info.valid = (1 << buflen) - 1; + ex_info.dis_info = info; + ex_info.insn_bytes = buf; /* 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 (buf, insn_value); + insn_list = CGEN_DIS_LOOKUP_INSN (cd, (char *) buf, insn_value); while (insn_list != NULL) { const CGEN_INSN *insn = insn_list->insn; CGEN_FIELDS fields; int length; + unsigned long insn_value_cropped; -#if 0 /* not needed as insn shouldn't be in hash lists if not supported */ +#ifdef CGEN_VALIDATE_INSN_SUPPORTED + /* Not needed as insn shouldn't be in hash lists if not supported. */ /* Supported by this cpu? */ - if (! @arch@_cgen_insn_supported (insn)) - continue; + if (! @arch@_cgen_insn_supported (cd, insn)) + { + insn_list = CGEN_DIS_NEXT_INSN (insn_list); + continue; + } #endif /* Basic bit mask must be correct. */ /* ??? May wish to allow target to defer this check until the extract handler. */ - if ((insn_value & CGEN_INSN_MASK (insn)) == CGEN_INSN_VALUE (insn)) + + /* Base size may exceed this instruction's size. Extract the + relevant part from the buffer. */ + if ((unsigned) (CGEN_INSN_BITSIZE (insn) / 8) < buflen && + (unsigned) (CGEN_INSN_BITSIZE (insn) / 8) <= sizeof (unsigned long)) + insn_value_cropped = bfd_get_bits (buf, CGEN_INSN_BITSIZE (insn), + info->endian == BFD_ENDIAN_BIG); + else + insn_value_cropped = insn_value; + + if ((insn_value_cropped & CGEN_INSN_BASE_MASK (insn)) + == CGEN_INSN_BASE_VALUE (insn)) { /* Printing is handled in two passes. The first pass parses the machine insn and extracts the fields. The second pass prints them. */ - length = (*CGEN_EXTRACT_FN (insn)) (insn, NULL, insn_value, &fields); + /* Make sure the entire insn is loaded into insn_value, if it + can fit. */ + if (((unsigned) CGEN_INSN_BITSIZE (insn) > cd->base_insn_bitsize) && + (unsigned) (CGEN_INSN_BITSIZE (insn) / 8) <= sizeof (unsigned long)) + { + unsigned long full_insn_value; + int rc = read_insn (cd, pc, info, buf, + CGEN_INSN_BITSIZE (insn) / 8, + & ex_info, & full_insn_value); + if (rc != 0) + return rc; + length = CGEN_EXTRACT_FN (cd, insn) + (cd, insn, &ex_info, full_insn_value, &fields, pc); + } + else + length = CGEN_EXTRACT_FN (cd, insn) + (cd, insn, &ex_info, insn_value_cropped, &fields, pc); + + /* Length < 0 -> error. */ + if (length < 0) + return length; if (length > 0) { - (*CGEN_PRINT_FN (insn)) (info, insn, &fields, pc, length); - /* length is in bits, result is in bytes */ + CGEN_PRINT_FN (cd, insn) (cd, info, insn, &fields, pc, length); + /* Length is in bits, result is in bytes. */ return length / 8; } } @@ -292,39 +296,158 @@ print_insn (pc, info, buf, buflen) return 0; } +/* Default value for CGEN_PRINT_INSN. + The result is the size of the insn in bytes or zero for an unknown insn + or -1 if an error occured fetching bytes. */ + +#ifndef CGEN_PRINT_INSN +#define CGEN_PRINT_INSN default_print_insn +#endif + +static int +default_print_insn (CGEN_CPU_DESC cd, bfd_vma pc, disassemble_info *info) +{ + bfd_byte buf[CGEN_MAX_INSN_SIZE]; + int buflen; + int status; + + /* Attempt to read the base part of the insn. */ + buflen = cd->base_insn_bitsize / 8; + status = (*info->read_memory_func) (pc, buf, buflen, info); + + /* Try again with the minimum part, if min < base. */ + if (status != 0 && (cd->min_insn_bitsize < cd->base_insn_bitsize)) + { + buflen = cd->min_insn_bitsize / 8; + status = (*info->read_memory_func) (pc, buf, buflen, info); + } + + if (status != 0) + { + (*info->memory_error_func) (status, pc, info); + return -1; + } + + return print_insn (cd, pc, info, buf, buflen); +} + /* Main entry point. Print one instruction from PC on INFO->STREAM. Return the size of the instruction (in bytes). */ +typedef struct cpu_desc_list +{ + struct cpu_desc_list *next; + CGEN_BITSET *isa; + int mach; + int endian; + int insn_endian; + CGEN_CPU_DESC cd; +} cpu_desc_list; + int -print_insn_@arch@ (pc, info) - bfd_vma pc; - disassemble_info *info; +print_insn_@arch@ (bfd_vma pc, disassemble_info *info) { - char buffer[CGEN_MAX_INSN_SIZE]; - int status, length; - static int initialized = 0; - static int current_mach = 0; - static int current_big_p = 0; - int mach = info->mach; - int big_p = info->endian == BFD_ENDIAN_BIG; - - /* If we haven't initialized yet, or if we've switched cpu's, initialize. */ - if (!initialized || mach != current_mach || big_p != current_big_p) + static cpu_desc_list *cd_list = 0; + cpu_desc_list *cl = 0; + static CGEN_CPU_DESC cd = 0; + static CGEN_BITSET *prev_isa; + static int prev_mach; + static int prev_endian; + static int prev_insn_endian; + int length; + CGEN_BITSET *isa; + int mach; + int endian = (info->endian == BFD_ENDIAN_BIG + ? CGEN_ENDIAN_BIG + : CGEN_ENDIAN_LITTLE); + int insn_endian = (info->endian_code == BFD_ENDIAN_BIG + ? CGEN_ENDIAN_BIG + : CGEN_ENDIAN_LITTLE); + enum bfd_architecture arch; + + /* ??? gdb will set mach but leave the architecture as "unknown" */ +#ifndef CGEN_BFD_ARCH +#define CGEN_BFD_ARCH bfd_arch_@arch@ +#endif + arch = info->arch; + if (arch == bfd_arch_unknown) + arch = CGEN_BFD_ARCH; + + /* There's no standard way to compute the machine or isa number + so we leave it to the target. */ +#ifdef CGEN_COMPUTE_MACH + mach = CGEN_COMPUTE_MACH (info); +#else + mach = info->mach; +#endif + +#ifdef CGEN_COMPUTE_ISA + { + static CGEN_BITSET *permanent_isa; + + if (!permanent_isa) + permanent_isa = cgen_bitset_create (MAX_ISAS); + isa = permanent_isa; + cgen_bitset_clear (isa); + cgen_bitset_add (isa, CGEN_COMPUTE_ISA (info)); + } +#else + isa = info->private_data; +#endif + + /* If we've switched cpu's, try to find a handle we've used before */ + if (cd + && (cgen_bitset_compare (isa, prev_isa) != 0 + || mach != prev_mach + || endian != prev_endian)) { - initialized = 1; - current_mach = mach; - current_big_p = big_p; - @arch@_cgen_init_dis (mach, big_p ? CGEN_ENDIAN_BIG : CGEN_ENDIAN_LITTLE); + cd = 0; + for (cl = cd_list; cl; cl = cl->next) + { + if (cgen_bitset_compare (cl->isa, isa) == 0 && + cl->mach == mach && + cl->endian == endian) + { + cd = cl->cd; + prev_isa = cd->isas; + break; + } + } } - /* Read enough of the insn so we can look it up in the hash lists. */ - - status = (*info->read_memory_func) (pc, buffer, CGEN_BASE_INSN_SIZE, info); - if (status != 0) + /* If we haven't initialized yet, initialize the opcode table. */ + if (! cd) { - (*info->memory_error_func) (status, pc, info); - return -1; + const bfd_arch_info_type *arch_type = bfd_lookup_arch (arch, mach); + const char *mach_name; + + if (!arch_type) + abort (); + mach_name = arch_type->printable_name; + + prev_isa = cgen_bitset_copy (isa); + prev_mach = mach; + prev_endian = endian; + prev_insn_endian = insn_endian; + cd = @arch@_cgen_cpu_open (CGEN_CPU_OPEN_ISAS, prev_isa, + CGEN_CPU_OPEN_BFDMACH, mach_name, + CGEN_CPU_OPEN_ENDIAN, prev_endian, + CGEN_CPU_OPEN_INSN_ENDIAN, prev_insn_endian, + CGEN_CPU_OPEN_END); + if (!cd) + abort (); + + /* Save this away for future reference. */ + cl = xmalloc (sizeof (struct cpu_desc_list)); + cl->cd = cd; + cl->isa = prev_isa; + cl->mach = mach; + cl->endian = endian; + cl->next = cd_list; + cd_list = cl; + + @arch@_cgen_init_dis (cd); } /* We try to have as much common code as possible. @@ -332,10 +455,12 @@ print_insn_@arch@ (pc, info) /* ??? Some targets may need a hook elsewhere. Try to avoid this, but if not possible try to move this hook elsewhere rather than have two hooks. */ - length = CGEN_PRINT_INSN (pc, info, buffer, CGEN_BASE_INSN_BITSIZE); - if (length) + length = CGEN_PRINT_INSN (cd, pc, info); + if (length > 0) return length; + if (length < 0) + return -1; (*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG); - return CGEN_DEFAULT_INSN_SIZE; + return cd->default_insn_bitsize / 8; }