X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=opcodes%2Farc-dis.c;h=7541b74d23034a10fa79bc00925a0d022d805323;hb=d85bf2ba86fea1cf918f065d179b98eb4ddbde86;hp=eb8bd670dbe7271581d6b25be4eafe851f164a15;hpb=f36e33dac1a97cca8f79ca8b20cf0fb05f1e25f4;p=deliverable%2Fbinutils-gdb.git diff --git a/opcodes/arc-dis.c b/opcodes/arc-dis.c index eb8bd670db..7541b74d23 100644 --- a/opcodes/arc-dis.c +++ b/opcodes/arc-dis.c @@ -1,5 +1,5 @@ /* Instruction printing code for the ARC. - Copyright (C) 1994-2016 Free Software Foundation, Inc. + Copyright (C) 1994-2017 Free Software Foundation, Inc. Contributed by Claudiu Zissulescu (claziss@synopsys.com) @@ -25,9 +25,58 @@ #include #include "dis-asm.h" #include "opcode/arc.h" +#include "elf/arc.h" #include "arc-dis.h" #include "arc-ext.h" +#include "elf-bfd.h" +#include "libiberty.h" +#include "opintl.h" +/* Structure used to iterate over, and extract the values for, operands of + an opcode. */ + +struct arc_operand_iterator +{ + /* The complete instruction value to extract operands from. */ + unsigned long long insn; + + /* The LIMM if this is being tracked separately. This field is only + valid if we find the LIMM operand in the operand list. */ + unsigned limm; + + /* The opcode this iterator is operating on. */ + const struct arc_opcode *opcode; + + /* The index into the opcodes operand index list. */ + const unsigned char *opidx; +}; + +/* A private data used by ARC decoder. */ +struct arc_disassemble_info +{ + /* The current disassembled arc opcode. */ + const struct arc_opcode *opcode; + + /* Instruction length w/o limm field. */ + unsigned insn_len; + + /* TRUE if we have limm. */ + bfd_boolean limm_p; + + /* LIMM value, if exists. */ + unsigned limm; + + /* Condition code, if exists. */ + unsigned condition_code; + + /* Writeback mode. */ + unsigned writeback_mode; + + /* Number of operands. */ + unsigned operands_count; + + struct arc_insn_operand operands[MAX_INSN_ARGS]; +}; /* Globals variables. */ @@ -44,6 +93,38 @@ static const char * const regnames[64] = "r56", "r57", "ACCL", "ACCH", "lp_count", "rezerved", "LIMM", "pcl" }; +static const char * const addrtypenames[ARC_NUM_ADDRTYPES] = +{ + "bd", "jid", "lbd", "mbd", "sd", "sm", "xa", "xd", + "cd", "cbd", "cjid", "clbd", "cm", "csd", "cxa", "cxd" +}; + +static int addrtypenames_max = ARC_NUM_ADDRTYPES - 1; + +static const char * const addrtypeunknown = "unknown"; + +/* This structure keeps track which instruction class(es) + should be ignored durring disassembling. */ + +typedef struct skipclass +{ + insn_class_t insn_class; + insn_subclass_t subclass; + struct skipclass *nxt; +} skipclass_t, *linkclass; + +/* Intial classes of instructions to be consider first when + disassembling. */ +static linkclass decodelist = NULL; + +/* ISA mask value enforced via disassembler info options. ARC_OPCODE_NONE + value means that no CPU is enforced. */ + +static unsigned enforced_isa_mask = ARC_OPCODE_NONE; + +/* True if we want to print using only hex numbers. */ +static bfd_boolean print_hex = FALSE; + /* Macros section. */ #ifdef DEBUG @@ -58,12 +139,73 @@ static const char * const regnames[64] = #define BITS(word,s,e) (((word) << (sizeof (word) * 8 - 1 - e)) >> \ (s + (sizeof (word) * 8 - 1 - e))) -#define OPCODE(word) (BITS ((word), 27, 31)) - -#define OPCODE_AC(word) (BITS ((word), 11, 15)) +#define OPCODE_32BIT_INSN(word) (BITS ((word), 27, 31)) /* Functions implementation. */ +/* Initialize private data. */ +static bfd_boolean +init_arc_disasm_info (struct disassemble_info *info) +{ + struct arc_disassemble_info *arc_infop + = calloc (sizeof (*arc_infop), 1); + + if (arc_infop == NULL) + return FALSE; + + info->private_data = arc_infop; + return TRUE; +} + +/* Add a new element to the decode list. */ + +static void +add_to_decodelist (insn_class_t insn_class, + insn_subclass_t subclass) +{ + linkclass t = (linkclass) xmalloc (sizeof (skipclass_t)); + + t->insn_class = insn_class; + t->subclass = subclass; + t->nxt = decodelist; + decodelist = t; +} + +/* Return TRUE if we need to skip the opcode from being + disassembled. */ + +static bfd_boolean +skip_this_opcode (const struct arc_opcode *opcode) +{ + linkclass t = decodelist; + + /* Check opcode for major 0x06, return if it is not in. */ + if (arc_opcode_len (opcode) == 4 + && OPCODE_32BIT_INSN (opcode->opcode) != 0x06) + return FALSE; + + /* or not a known truble class. */ + switch (opcode->insn_class) + { + case FLOAT: + case DSP: + case ARITH: + break; + default: + return FALSE; + } + + while (t != NULL) + { + if ((t->insn_class == opcode->insn_class) + && (t->subclass == opcode->subclass)) + return FALSE; + t = t->nxt; + } + + return TRUE; +} + static bfd_vma bfd_getm32 (unsigned int data) { @@ -74,7 +216,7 @@ bfd_getm32 (unsigned int data) return value; } -static int +static bfd_boolean special_flag_p (const char *opname, const char *flgname) { @@ -96,127 +238,262 @@ special_flag_p (const char *opname, break; /* End of the array. */ if (strcmp (flgname, arc_flag_operands[flgidx].name) == 0) - return 1; + return TRUE; } } - return 0; + return FALSE; } -/* Find proper format for the given opcode. */ +/* Find opcode from ARC_TABLE given the instruction described by INSN and + INSNLEN. The ISA_MASK restricts the possible matches in ARC_TABLE. */ + static const struct arc_opcode * -find_format (const struct arc_opcode *arc_table, - unsigned *insn, int insnLen, - unsigned isa_mask) +find_format_from_table (struct disassemble_info *info, + const struct arc_opcode *arc_table, + unsigned long long insn, + unsigned int insn_len, + unsigned isa_mask, + bfd_boolean *has_limm, + bfd_boolean overlaps) { unsigned int i = 0; const struct arc_opcode *opcode = NULL; + const struct arc_opcode *t_op = NULL; const unsigned char *opidx; const unsigned char *flgidx; + bfd_boolean warn_p = FALSE; - do { - bfd_boolean invalid = FALSE; + do + { + bfd_boolean invalid = FALSE; - opcode = &arc_table[i++]; + opcode = &arc_table[i++]; - if (ARC_SHORT (opcode->mask) && (insnLen == 2)) - { - if (OPCODE_AC (opcode->opcode) != OPCODE_AC (insn[0])) - continue; - } - else if (!ARC_SHORT (opcode->mask) && (insnLen == 4)) - { - if (OPCODE (opcode->opcode) != OPCODE (insn[0])) - continue; - } - else - continue; + if (!(opcode->cpu & isa_mask)) + continue; - if ((insn[0] ^ opcode->opcode) & opcode->mask) - continue; + if (arc_opcode_len (opcode) != (int) insn_len) + continue; - if (!(opcode->cpu & isa_mask)) - continue; + if ((insn & opcode->mask) != opcode->opcode) + continue; - /* Possible candidate, check the operands. */ - for (opidx = opcode->operands; *opidx; opidx++) - { - int value; - const struct arc_operand *operand = &arc_operands[*opidx]; + *has_limm = FALSE; - if (operand->flags & ARC_OPERAND_FAKE) - continue; + /* Possible candidate, check the operands. */ + for (opidx = opcode->operands; *opidx; opidx++) + { + int value, limmind; + const struct arc_operand *operand = &arc_operands[*opidx]; - if (operand->extract) - value = (*operand->extract) (insn[0], &invalid); - else - value = (insn[0] >> operand->shift) & ((1 << operand->bits) - 1); + if (operand->flags & ARC_OPERAND_FAKE) + continue; - /* Check for LIMM indicator. If it is there, then make sure - we pick the right format. */ - if (operand->flags & ARC_OPERAND_IR - && !(operand->flags & ARC_OPERAND_LIMM)) - { - if ((value == 0x3E && insnLen == 4) - || (value == 0x1E && insnLen == 2)) - { - invalid = TRUE; - break; - } - } - } + if (operand->extract) + value = (*operand->extract) (insn, &invalid); + else + value = (insn >> operand->shift) & ((1 << operand->bits) - 1); - /* Check the flags. */ - for (flgidx = opcode->flags; *flgidx; flgidx++) - { - /* Get a valid flag class. */ - const struct arc_flag_class *cl_flags = &arc_flag_classes[*flgidx]; - const unsigned *flgopridx; - int foundA = 0, foundB = 0; - unsigned int value; - - /* Check first the extensions. */ - if (cl_flags->class & F_CLASS_EXTEND) - { - value = (insn[0] & 0x1F); - if (arcExtMap_condCodeName (value)) - continue; - } - for (flgopridx = cl_flags->flags; *flgopridx; ++flgopridx) - { - const struct arc_flag_operand *flg_operand = - &arc_flag_operands[*flgopridx]; - - value = (insn[0] >> flg_operand->shift) - & ((1 << flg_operand->bits) - 1); - if (value == flg_operand->code) - foundA = 1; - if (value) - foundB = 1; - } - if (!foundA && foundB) - { - invalid = TRUE; - break; - } - } + /* Check for LIMM indicator. If it is there, then make sure + we pick the right format. */ + limmind = (isa_mask & ARC_OPCODE_ARCV2) ? 0x1E : 0x3E; + if (operand->flags & ARC_OPERAND_IR + && !(operand->flags & ARC_OPERAND_LIMM)) + { + if ((value == 0x3E && insn_len == 4) + || (value == limmind && insn_len == 2)) + { + invalid = TRUE; + break; + } + } - if (invalid) - continue; + if (operand->flags & ARC_OPERAND_LIMM + && !(operand->flags & ARC_OPERAND_DUPLICATE)) + *has_limm = TRUE; + } - /* The instruction is valid. */ - return opcode; - } while (opcode->mask); + /* Check the flags. */ + for (flgidx = opcode->flags; *flgidx; flgidx++) + { + /* Get a valid flag class. */ + const struct arc_flag_class *cl_flags = &arc_flag_classes[*flgidx]; + const unsigned *flgopridx; + int foundA = 0, foundB = 0; + unsigned int value; + + /* Check first the extensions. */ + if (cl_flags->flag_class & F_CLASS_EXTEND) + { + value = (insn & 0x1F); + if (arcExtMap_condCodeName (value)) + continue; + } + + /* Check for the implicit flags. */ + if (cl_flags->flag_class & F_CLASS_IMPLICIT) + continue; + + for (flgopridx = cl_flags->flags; *flgopridx; ++flgopridx) + { + const struct arc_flag_operand *flg_operand = + &arc_flag_operands[*flgopridx]; + + value = (insn >> flg_operand->shift) + & ((1 << flg_operand->bits) - 1); + if (value == flg_operand->code) + foundA = 1; + if (value) + foundB = 1; + } + + if (!foundA && foundB) + { + invalid = TRUE; + break; + } + } + + if (invalid) + continue; + + if (insn_len == 4 + && overlaps) + { + warn_p = TRUE; + t_op = opcode; + if (skip_this_opcode (opcode)) + continue; + } + + /* The instruction is valid. */ + return opcode; + } + while (opcode->mask); + + if (warn_p) + { + info->fprintf_func (info->stream, + _("\nWarning: disassembly may be wrong due to " + "guessed opcode class choice.\n" + "Use -M to select the correct " + "opcode class(es).\n\t\t\t\t")); + return t_op; + } return NULL; } +/* Find opcode for INSN, trying various different sources. The instruction + length in INSN_LEN will be updated if the instruction requires a LIMM + extension. + + A pointer to the opcode is placed into OPCODE_RESULT, and ITER is + initialised, ready to iterate over the operands of the found opcode. If + the found opcode requires a LIMM then the LIMM value will be loaded into a + field of ITER. + + This function returns TRUE in almost all cases, FALSE is reserved to + indicate an error (failing to find an opcode is not an error) a returned + result of FALSE would indicate that the disassembler can't continue. + + If no matching opcode is found then the returned result will be TRUE, the + value placed into OPCODE_RESULT will be NULL, ITER will be undefined, and + INSN_LEN will be unchanged. + + If a matching opcode is found, then the returned result will be TRUE, the + opcode pointer is placed into OPCODE_RESULT, INSN_LEN will be increased by + 4 if the instruction requires a LIMM, and the LIMM value will have been + loaded into a field of ITER. Finally, ITER will have been initialised so + that calls to OPERAND_ITERATOR_NEXT will iterate over the opcode's + operands. */ + +static bfd_boolean +find_format (bfd_vma memaddr, + unsigned long long insn, + unsigned int * insn_len, + unsigned isa_mask, + struct disassemble_info * info, + const struct arc_opcode ** opcode_result, + struct arc_operand_iterator * iter) +{ + const struct arc_opcode *opcode = NULL; + bfd_boolean needs_limm; + const extInstruction_t *einsn, *i; + unsigned limm = 0; + struct arc_disassemble_info *arc_infop = info->private_data; + + /* First, try the extension instructions. */ + if (*insn_len == 4) + { + einsn = arcExtMap_insn (OPCODE_32BIT_INSN (insn), insn); + for (i = einsn; (i != NULL) && (opcode == NULL); i = i->next) + { + const char *errmsg = NULL; + + opcode = arcExtMap_genOpcode (i, isa_mask, &errmsg); + if (opcode == NULL) + { + (*info->fprintf_func) (info->stream, "\ +An error occured while generating the extension instruction operations"); + *opcode_result = NULL; + return FALSE; + } + + opcode = find_format_from_table (info, opcode, insn, *insn_len, + isa_mask, &needs_limm, FALSE); + } + } + + /* Then, try finding the first match in the opcode table. */ + if (opcode == NULL) + opcode = find_format_from_table (info, arc_opcodes, insn, *insn_len, + isa_mask, &needs_limm, TRUE); + + if (needs_limm && opcode != NULL) + { + bfd_byte buffer[4]; + int status; + + status = (*info->read_memory_func) (memaddr + *insn_len, buffer, + 4, info); + if (status != 0) + { + opcode = NULL; + } + else + { + limm = ARRANGE_ENDIAN (info, buffer); + *insn_len += 4; + } + } + + if (opcode != NULL) + { + iter->insn = insn; + iter->limm = limm; + iter->opcode = opcode; + iter->opidx = opcode->operands; + } + + *opcode_result = opcode; + + /* Update private data. */ + arc_infop->opcode = opcode; + arc_infop->limm = (needs_limm) ? limm : 0; + arc_infop->limm_p = needs_limm; + + return TRUE; +} + static void print_flags (const struct arc_opcode *opcode, - unsigned *insn, + unsigned long long *insn, struct disassemble_info *info) { const unsigned char *flgidx; unsigned int value; + struct arc_disassemble_info *arc_infop = info->private_data; /* Now extract and print the flags. */ for (flgidx = opcode->flags; *flgidx; flgidx++) @@ -226,7 +503,7 @@ print_flags (const struct arc_opcode *opcode, const unsigned *flgopridx; /* Check first the extensions. */ - if (cl_flags->class & F_CLASS_EXTEND) + if (cl_flags->flag_class & F_CLASS_EXTEND) { const char *name; value = (insn[0] & 0x1F); @@ -244,6 +521,18 @@ print_flags (const struct arc_opcode *opcode, const struct arc_flag_operand *flg_operand = &arc_flag_operands[*flgopridx]; + /* Implicit flags are only used for the insn decoder. */ + if (cl_flags->flag_class & F_CLASS_IMPLICIT) + { + if (cl_flags->flag_class & F_CLASS_COND) + arc_infop->condition_code = flg_operand->code; + else if (cl_flags->flag_class & F_CLASS_WB) + arc_infop->writeback_mode = flg_operand->code; + else if (cl_flags->flag_class & F_CLASS_ZZ) + info->data_size = flg_operand->code; + continue; + } + if (!flg_operand->favail) continue; @@ -270,12 +559,26 @@ print_flags (const struct arc_opcode *opcode, break; } } + if (flg_operand->name[0] == 'd' + && flg_operand->name[1] == 0) + info->branch_delay_insns = 1; + + /* Check if it is a conditional flag. */ + if (cl_flags->flag_class & F_CLASS_COND) + { + if (info->insn_type == dis_jsr) + info->insn_type = dis_condjsr; + else if (info->insn_type == dis_branch) + info->insn_type = dis_condbranch; + arc_infop->condition_code = flg_operand->code; + } + + /* Check for the write back modes. */ + if (cl_flags->flag_class & F_CLASS_WB) + arc_infop->writeback_mode = flg_operand->code; + (*info->fprintf_func) (info->stream, "%s", flg_operand->name); } - - if (flg_operand->name[0] == 'd' - && flg_operand->name[1] == 0) - info->branch_delay_insns = 1; } } } @@ -289,7 +592,7 @@ get_auxreg (const struct arc_opcode *opcode, unsigned int i; const struct arc_aux_reg *auxr = &arc_aux_regs[0]; - if (opcode->class != AUXREG) + if (opcode->insn_class != AUXREG) return NULL; name = arcExtMap_auxRegName (value); @@ -309,46 +612,374 @@ get_auxreg (const struct arc_opcode *opcode, } return NULL; } + +/* Convert a value representing an address type to a string used to refer to + the address type in assembly code. */ + +static const char * +get_addrtype (int value) +{ + if (value < 0 || value > addrtypenames_max) + return addrtypeunknown; + + return addrtypenames[value]; +} + +/* Calculate the instruction length for an instruction starting with MSB + and LSB, the most and least significant byte. The ISA_MASK is used to + filter the instructions considered to only those that are part of the + current architecture. + + The instruction lengths are calculated from the ARC_OPCODE table, and + cached for later use. */ + +static unsigned int +arc_insn_length (bfd_byte msb, bfd_byte lsb, struct disassemble_info *info) +{ + bfd_byte major_opcode = msb >> 3; + + switch (info->mach) + { + case bfd_mach_arc_arc700: + /* The nps400 extension set requires this special casing of the + instruction length calculation. Right now this is not causing any + problems as none of the known extensions overlap in opcode space, + but, if they ever do then we might need to start carrying + information around in the elf about which extensions are in use. */ + if (major_opcode == 0xb) + { + bfd_byte minor_opcode = lsb & 0x1f; + + if (minor_opcode < 4) + return 6; + else if (minor_opcode == 0x10 || minor_opcode == 0x11) + return 8; + } + if (major_opcode == 0xa) + { + return 8; + } + /* Fall through. */ + case bfd_mach_arc_arc600: + return (major_opcode > 0xb) ? 2 : 4; + break; + + case bfd_mach_arc_arcv2: + return (major_opcode > 0x7) ? 2 : 4; + break; + + default: + abort (); + } +} + +/* Extract and return the value of OPERAND from the instruction whose value + is held in the array INSN. */ + +static int +extract_operand_value (const struct arc_operand *operand, + unsigned long long insn, + unsigned limm) +{ + int value; + + /* Read the limm operand, if required. */ + if (operand->flags & ARC_OPERAND_LIMM) + /* The second part of the instruction value will have been loaded as + part of the find_format call made earlier. */ + value = limm; + else + { + if (operand->extract) + value = (*operand->extract) (insn, (int *) NULL); + else + { + if (operand->flags & ARC_OPERAND_ALIGNED32) + { + value = (insn >> operand->shift) + & ((1 << (operand->bits - 2)) - 1); + value = value << 2; + } + else + { + value = (insn >> operand->shift) & ((1 << operand->bits) - 1); + } + if (operand->flags & ARC_OPERAND_SIGNED) + { + int signbit = 1 << (operand->bits - 1); + value = (value ^ signbit) - signbit; + } + } + } + + return value; +} + +/* Find the next operand, and the operands value from ITER. Return TRUE if + there is another operand, otherwise return FALSE. If there is an + operand returned then the operand is placed into OPERAND, and the value + into VALUE. If there is no operand returned then OPERAND and VALUE are + unchanged. */ + +static bfd_boolean +operand_iterator_next (struct arc_operand_iterator *iter, + const struct arc_operand **operand, + int *value) +{ + if (*iter->opidx == 0) + { + *operand = NULL; + return FALSE; + } + + *operand = &arc_operands[*iter->opidx]; + *value = extract_operand_value (*operand, iter->insn, iter->limm); + iter->opidx++; + + return TRUE; +} + +/* Helper for parsing the options. */ + +static void +parse_option (const char *option) +{ + if (disassembler_options_cmp (option, "dsp") == 0) + add_to_decodelist (DSP, NONE); + + else if (disassembler_options_cmp (option, "spfp") == 0) + add_to_decodelist (FLOAT, SPX); + + else if (disassembler_options_cmp (option, "dpfp") == 0) + add_to_decodelist (FLOAT, DPX); + + else if (disassembler_options_cmp (option, "quarkse_em") == 0) + { + add_to_decodelist (FLOAT, DPX); + add_to_decodelist (FLOAT, SPX); + add_to_decodelist (FLOAT, QUARKSE1); + add_to_decodelist (FLOAT, QUARKSE2); + } + + else if (disassembler_options_cmp (option, "fpuda") == 0) + add_to_decodelist (FLOAT, DPA); + + else if (disassembler_options_cmp (option, "fpus") == 0) + { + add_to_decodelist (FLOAT, SP); + add_to_decodelist (FLOAT, CVT); + } + + else if (disassembler_options_cmp (option, "fpud") == 0) + { + add_to_decodelist (FLOAT, DP); + add_to_decodelist (FLOAT, CVT); + } + else if (CONST_STRNEQ (option, "hex")) + print_hex = TRUE; + else + fprintf (stderr, _("Unrecognised disassembler option: %s\n"), option); +} + +#define ARC_CPU_TYPE_A6xx(NAME,EXTRA) \ + { #NAME, ARC_OPCODE_ARC600, "ARC600" } +#define ARC_CPU_TYPE_A7xx(NAME,EXTRA) \ + { #NAME, ARC_OPCODE_ARC700, "ARC700" } +#define ARC_CPU_TYPE_AV2EM(NAME,EXTRA) \ + { #NAME, ARC_OPCODE_ARCv2EM, "ARC EM" } +#define ARC_CPU_TYPE_AV2HS(NAME,EXTRA) \ + { #NAME, ARC_OPCODE_ARCv2HS, "ARC HS" } +#define ARC_CPU_TYPE_NONE \ + { 0, 0, 0 } + +/* A table of CPU names and opcode sets. */ +static const struct cpu_type +{ + const char *name; + unsigned flags; + const char *isa; +} + cpu_types[] = +{ + #include "elf/arc-cpu.def" +}; + +/* Helper for parsing the CPU options. Accept any of the ARC architectures + values. OPTION should be a value passed to cpu=. */ + +static unsigned +parse_cpu_option (const char *option) +{ + int i; + + for (i = 0; cpu_types[i].name; ++i) + { + if (!disassembler_options_cmp (cpu_types[i].name, option)) + { + return cpu_types[i].flags; + } + } + + fprintf (stderr, _("Unrecognised disassembler CPU option: %s\n"), option); + return ARC_OPCODE_NONE; +} + +/* Go over the options list and parse it. */ + +static void +parse_disassembler_options (const char *options) +{ + const char *option; + + if (options == NULL) + return; + + /* Disassembler might be reused for difference CPU's, and cpu option set for + the first one shouldn't be applied to second (which might not have + explicit cpu in its options. Therefore it is required to reset enforced + CPU when new options are being parsed. */ + enforced_isa_mask = ARC_OPCODE_NONE; + + FOR_EACH_DISASSEMBLER_OPTION (option, options) + { + /* A CPU option? Cannot use STRING_COMMA_LEN because strncmp is also a + preprocessor macro. */ + if (strncmp (option, "cpu=", 4) == 0) + /* Strip leading `cpu=`. */ + enforced_isa_mask = parse_cpu_option (option + 4); + else + parse_option (option); + } +} + +/* Return the instruction type for an instruction described by OPCODE. */ + +static enum dis_insn_type +arc_opcode_to_insn_type (const struct arc_opcode *opcode) +{ + enum dis_insn_type insn_type; + + switch (opcode->insn_class) + { + case BRANCH: + case BBIT0: + case BBIT1: + case BI: + case BIH: + case BRCC: + case EI: + case JLI: + case JUMP: + case LOOP: + if (!strncmp (opcode->name, "bl", 2) + || !strncmp (opcode->name, "jl", 2)) + { + if (opcode->subclass == COND) + insn_type = dis_condjsr; + else + insn_type = dis_jsr; + } + else + { + if (opcode->subclass == COND) + insn_type = dis_condbranch; + else + insn_type = dis_branch; + } + break; + case LOAD: + case STORE: + case MEMORY: + case ENTER: + case PUSH: + case POP: + insn_type = dis_dref; + break; + case LEAVE: + insn_type = dis_branch; + break; + default: + insn_type = dis_nonbranch; + break; + } + + return insn_type; +} + /* Disassemble ARC instructions. */ static int print_insn_arc (bfd_vma memaddr, struct disassemble_info *info) { - bfd_byte buffer[4]; - unsigned int lowbyte, highbyte; + bfd_byte buffer[8]; + unsigned int highbyte, lowbyte; int status; - int insnLen = 0; - unsigned insn[2] = { 0, 0 }; - unsigned isa_mask; - const unsigned char *opidx; + unsigned int insn_len; + unsigned long long insn = 0; + unsigned isa_mask = ARC_OPCODE_NONE; const struct arc_opcode *opcode; - const extInstruction_t *einsn; bfd_boolean need_comma; bfd_boolean open_braket; int size; + const struct arc_operand *operand; + int value; + struct arc_operand_iterator iter; + struct arc_disassemble_info *arc_infop; - lowbyte = ((info->endian == BFD_ENDIAN_LITTLE) ? 1 : 0); - highbyte = ((info->endian == BFD_ENDIAN_LITTLE) ? 0 : 1); + if (info->disassembler_options) + { + parse_disassembler_options (info->disassembler_options); - switch (info->mach) + /* Avoid repeated parsing of the options. */ + info->disassembler_options = NULL; + } + + if (info->private_data == NULL && !init_arc_disasm_info (info)) + return -1; + + memset (&iter, 0, sizeof (iter)); + highbyte = ((info->endian == BFD_ENDIAN_LITTLE) ? 1 : 0); + lowbyte = ((info->endian == BFD_ENDIAN_LITTLE) ? 0 : 1); + + /* Figure out CPU type, unless it was enforced via disassembler options. */ + if (enforced_isa_mask == ARC_OPCODE_NONE) { - case bfd_mach_arc_nps400: - isa_mask = ARC_OPCODE_ARC700 | ARC_OPCODE_NPS400; - break; + Elf_Internal_Ehdr *header = NULL; - case bfd_mach_arc_arc700: - isa_mask = ARC_OPCODE_ARC700; - break; + if (info->section && info->section->owner) + header = elf_elfheader (info->section->owner); - case bfd_mach_arc_arc600: - isa_mask = ARC_OPCODE_ARC600; - break; + switch (info->mach) + { + case bfd_mach_arc_arc700: + isa_mask = ARC_OPCODE_ARC700; + break; - case bfd_mach_arc_arcv2: - default: - isa_mask = ARC_OPCODE_ARCv2HS | ARC_OPCODE_ARCv2EM; - break; + case bfd_mach_arc_arc600: + isa_mask = ARC_OPCODE_ARC600; + break; + + case bfd_mach_arc_arcv2: + default: + isa_mask = ARC_OPCODE_ARCv2EM; + /* TODO: Perhaps remove definition of header since it is only used at + this location. */ + if (header != NULL + && (header->e_flags & EF_ARC_MACH_MSK) == EF_ARC_CPU_ARCV2HS) + isa_mask = ARC_OPCODE_ARCv2HS; + break; + } + } + else + isa_mask = enforced_isa_mask; + + if (isa_mask == ARC_OPCODE_ARCv2HS) + { + /* FPU instructions are not extensions for HS. */ + add_to_decodelist (FLOAT, SP); + add_to_decodelist (FLOAT, DP); + add_to_decodelist (FLOAT, CVT); } /* This variable may be set by the instruction decoder. It suggests @@ -422,34 +1053,69 @@ print_insn_arc (bfd_vma memaddr, return size; } - if ((((buffer[lowbyte] & 0xf8) > 0x38) - && ((buffer[lowbyte] & 0xf8) != 0x48)) - || ((info->mach == bfd_mach_arc_arcv2) - && ((buffer[lowbyte] & 0xF8) == 0x48)) /* FIXME! ugly. */ - ) - { - /* This is a short instruction. */ - insnLen = 2; - insn[0] = (buffer[lowbyte] << 8) | buffer[highbyte]; - } - else + insn_len = arc_insn_length (buffer[highbyte], buffer[lowbyte], info); + pr_debug ("instruction length = %d bytes\n", insn_len); + arc_infop = info->private_data; + arc_infop->insn_len = insn_len; + + switch (insn_len) { - insnLen = 4; + case 2: + insn = (buffer[highbyte] << 8) | buffer[lowbyte]; + break; - /* This is a long instruction: Read the remaning 2 bytes. */ - status = (*info->read_memory_func) (memaddr + 2, &buffer[2], 2, info); - if (status != 0) - { - (*info->memory_error_func) (status, memaddr + 2, info); - return -1; - } - insn[0] = ARRANGE_ENDIAN (info, buffer); + case 4: + { + /* This is a long instruction: Read the remaning 2 bytes. */ + status = (*info->read_memory_func) (memaddr + 2, &buffer[2], 2, info); + if (status != 0) + { + (*info->memory_error_func) (status, memaddr + 2, info); + return -1; + } + insn = (unsigned long long) ARRANGE_ENDIAN (info, buffer); + } + break; + + case 6: + { + status = (*info->read_memory_func) (memaddr + 2, &buffer[2], 4, info); + if (status != 0) + { + (*info->memory_error_func) (status, memaddr + 2, info); + return -1; + } + insn = (unsigned long long) ARRANGE_ENDIAN (info, &buffer[2]); + insn |= ((unsigned long long) buffer[highbyte] << 40) + | ((unsigned long long) buffer[lowbyte] << 32); + } + break; + + case 8: + { + status = (*info->read_memory_func) (memaddr + 2, &buffer[2], 6, info); + if (status != 0) + { + (*info->memory_error_func) (status, memaddr + 2, info); + return -1; + } + insn = + ((((unsigned long long) ARRANGE_ENDIAN (info, buffer)) << 32) + | ((unsigned long long) ARRANGE_ENDIAN (info, &buffer[4]))); + } + break; + + default: + /* There is no instruction whose length is not 2, 4, 6, or 8. */ + abort (); } + pr_debug ("instruction value = %llx\n", insn); + /* Set some defaults for the insn info. */ info->insn_info_valid = 1; info->branch_delay_insns = 0; - info->data_size = 0; + info->data_size = 4; info->insn_type = dis_nonbranch; info->target = 0; info->target2 = 0; @@ -458,78 +1124,62 @@ print_insn_arc (bfd_vma memaddr, info->disassembler_needs_relocs = TRUE; /* Find the first match in the opcode table. */ - opcode = find_format (arc_opcodes, insn, insnLen, isa_mask); + if (!find_format (memaddr, insn, &insn_len, isa_mask, info, &opcode, &iter)) + return -1; if (!opcode) { - /* No instruction found. Try the extensions. */ - einsn = arcExtMap_insn (OPCODE (insn[0]), insn[0]); - if (einsn) + switch (insn_len) { - const char *errmsg = NULL; - opcode = arcExtMap_genOpcode (einsn, isa_mask, &errmsg); - if (opcode == NULL) - { - (*info->fprintf_func) (info->stream, - "An error occured while " - "generating the extension instruction " - "operations"); - return -1; - } - - opcode = find_format (opcode, insn, insnLen, isa_mask); - assert (opcode != NULL); + case 2: + (*info->fprintf_func) (info->stream, ".long %#04llx", + insn & 0xffff); + break; + case 4: + (*info->fprintf_func) (info->stream, ".long %#08llx", + insn & 0xffffffff); + break; + case 6: + (*info->fprintf_func) (info->stream, ".long %#08llx", + insn & 0xffffffff); + (*info->fprintf_func) (info->stream, ".long %#04llx", + (insn >> 32) & 0xffff); + break; + case 8: + (*info->fprintf_func) (info->stream, ".long %#08llx", + insn & 0xffffffff); + (*info->fprintf_func) (info->stream, ".long %#08llx", + insn >> 32); + break; + default: + abort (); } - else - { - if (insnLen == 2) - (*info->fprintf_func) (info->stream, ".long %#04x", insn[0]); - else - (*info->fprintf_func) (info->stream, ".long %#08x", insn[0]); - info->insn_type = dis_noninsn; - return insnLen; - } + info->insn_type = dis_noninsn; + return insn_len; } /* Print the mnemonic. */ (*info->fprintf_func) (info->stream, "%s", opcode->name); /* Preselect the insn class. */ - switch (opcode->class) - { - case BRANCH: - case JUMP: - if (!strncmp (opcode->name, "bl", 2) - || !strncmp (opcode->name, "jl", 2)) - info->insn_type = dis_jsr; - else - info->insn_type = dis_branch; - break; - case MEMORY: - info->insn_type = dis_dref; /* FIXME! DB indicates mov as memory! */ - break; - default: - info->insn_type = dis_nonbranch; - break; - } + info->insn_type = arc_opcode_to_insn_type (opcode); - pr_debug ("%s: 0x%08x\n", opcode->name, opcode->opcode); + pr_debug ("%s: 0x%08llx\n", opcode->name, opcode->opcode); - print_flags (opcode, insn, info); + print_flags (opcode, &insn, info); if (opcode->operands[0] != 0) (*info->fprintf_func) (info->stream, "\t"); need_comma = FALSE; open_braket = FALSE; + arc_infop->operands_count = 0; /* Now extract and print the operands. */ - for (opidx = opcode->operands; *opidx; opidx++) + operand = NULL; + while (operand_iterator_next (&iter, &operand, &value)) { - const struct arc_operand *operand = &arc_operands[*opidx]; - int value; - if (open_braket && (operand->flags & ARC_OPERAND_BRAKET)) { (*info->fprintf_func) (info->stream, "]"); @@ -538,36 +1188,20 @@ print_insn_arc (bfd_vma memaddr, } /* Only take input from real operands. */ - if ((operand->flags & ARC_OPERAND_FAKE) - && !(operand->flags & ARC_OPERAND_BRAKET)) + if (ARC_OPERAND_IS_FAKE (operand)) continue; - if (operand->extract) - value = (*operand->extract) (insn[0], (int *) NULL); - else + if ((operand->flags & ARC_OPERAND_IGNORE) + && (operand->flags & ARC_OPERAND_IR) + && value == -1) + continue; + + if (operand->flags & ARC_OPERAND_COLON) { - if (operand->flags & ARC_OPERAND_ALIGNED32) - { - value = (insn[0] >> operand->shift) - & ((1 << (operand->bits - 2)) - 1); - value = value << 2; - } - else - { - value = (insn[0] >> operand->shift) & ((1 << operand->bits) - 1); - } - if (operand->flags & ARC_OPERAND_SIGNED) - { - int signbit = 1 << (operand->bits - 1); - value = (value ^ signbit) - signbit; - } + (*info->fprintf_func) (info->stream, ":"); + continue; } - if (operand->flags & ARC_OPERAND_IGNORE - && (operand->flags & ARC_OPERAND_IR - && value == -1)) - continue; - if (need_comma) (*info->fprintf_func) (info->stream, ","); @@ -579,19 +1213,7 @@ print_insn_arc (bfd_vma memaddr, continue; } - /* Read the limm operand, if required. */ - if (operand->flags & ARC_OPERAND_LIMM - && !(operand->flags & ARC_OPERAND_DUPLICATE)) - { - status = (*info->read_memory_func) (memaddr + insnLen, buffer, - 4, info); - if (status != 0) - { - (*info->memory_error_func) (status, memaddr + insnLen, info); - return -1; - } - insn[1] = ARRANGE_ENDIAN (info, buffer); - } + need_comma = TRUE; /* Print the operand as directed by the flags. */ if (operand->flags & ARC_OPERAND_IR) @@ -613,15 +1235,16 @@ print_insn_arc (bfd_vma memaddr, } else if (operand->flags & ARC_OPERAND_LIMM) { - const char *rname = get_auxreg (opcode, insn[1], isa_mask); + const char *rname = get_auxreg (opcode, value, isa_mask); + if (rname && open_braket) (*info->fprintf_func) (info->stream, "%s", rname); else { - (*info->fprintf_func) (info->stream, "%#x", insn[1]); + (*info->fprintf_func) (info->stream, "%#x", value); if (info->insn_type == dis_branch || info->insn_type == dis_jsr) - info->target = (bfd_vma) insn[1]; + info->target = (bfd_vma) value; } } else if (operand->flags & ARC_OPERAND_PCREL) @@ -639,16 +1262,41 @@ print_insn_arc (bfd_vma memaddr, if (rname && open_braket) (*info->fprintf_func) (info->stream, "%s", rname); else - (*info->fprintf_func) (info->stream, "%d", value); + { + if (print_hex) + (*info->fprintf_func) (info->stream, "%#x", value); + else + (*info->fprintf_func) (info->stream, "%d", value); + } + } + else if (operand->flags & ARC_OPERAND_ADDRTYPE) + { + const char *addrtype = get_addrtype (value); + (*info->fprintf_func) (info->stream, "%s", addrtype); + /* A colon follow an address type. */ + need_comma = FALSE; } else { if (operand->flags & ARC_OPERAND_TRUNCATE && !(operand->flags & ARC_OPERAND_ALIGNED32) && !(operand->flags & ARC_OPERAND_ALIGNED16) - && value > 0 && value <= 14) - (*info->fprintf_func) (info->stream, "r13-%s", - regnames[13 + value - 1]); + && value >= 0 && value <= 14) + { + switch (value) + { + case 0: + need_comma = FALSE; + break; + case 1: + (*info->fprintf_func) (info->stream, "r13"); + break; + default: + (*info->fprintf_func) (info->stream, "r13-%s", + regnames[13 + value - 1]); + break; + } + } else { const char *rname = get_auxreg (opcode, value, isa_mask); @@ -659,52 +1307,132 @@ print_insn_arc (bfd_vma memaddr, } } - need_comma = TRUE; - - /* Adjust insn len. */ - if (operand->flags & ARC_OPERAND_LIMM - && !(operand->flags & ARC_OPERAND_DUPLICATE)) - insnLen += 4; + if (operand->flags & ARC_OPERAND_LIMM) + { + arc_infop->operands[arc_infop->operands_count].kind + = ARC_OPERAND_KIND_LIMM; + /* It is not important to have exactly the LIMM indicator + here. */ + arc_infop->operands[arc_infop->operands_count].value = 63; + } + else + { + arc_infop->operands[arc_infop->operands_count].value = value; + arc_infop->operands[arc_infop->operands_count].kind + = (operand->flags & ARC_OPERAND_IR + ? ARC_OPERAND_KIND_REG + : ARC_OPERAND_KIND_SHIMM); + } + arc_infop->operands_count ++; } - return insnLen; + return insn_len; } disassembler_ftype arc_get_disassembler (bfd *abfd) { - /* Read the extenssion insns and registers, if any. */ - build_ARC_extmap (abfd); + /* BFD my be absent, if opcodes is invoked from the debugger that + has connected to remote target and doesn't have an ELF file. */ + if (abfd != NULL) + { + /* Read the extension insns and registers, if any. */ + build_ARC_extmap (abfd); #ifdef DEBUG - dump_ARC_extmap (); + dump_ARC_extmap (); #endif + } return print_insn_arc; } -/* Disassemble ARC instructions. Used by debugger. */ +void +print_arc_disassembler_options (FILE *stream) +{ + int i; + + fprintf (stream, _("\n\ +The following ARC specific disassembler options are supported for use \n\ +with -M switch (multiple options should be separated by commas):\n")); + + /* cpu=... options. */ + for (i = 0; cpu_types[i].name; ++i) + { + /* As of now all value CPU values are less than 16 characters. */ + fprintf (stream, " cpu=%-16s\tEnforce %s ISA.\n", + cpu_types[i].name, cpu_types[i].isa); + } -struct arcDisState -arcAnalyzeInstr (bfd_vma memaddr, - struct disassemble_info *info) + fprintf (stream, _("\ + dsp Recognize DSP instructions.\n")); + fprintf (stream, _("\ + spfp Recognize FPX SP instructions.\n")); + fprintf (stream, _("\ + dpfp Recognize FPX DP instructions.\n")); + fprintf (stream, _("\ + quarkse_em Recognize FPU QuarkSE-EM instructions.\n")); + fprintf (stream, _("\ + fpuda Recognize double assist FPU instructions.\n")); + fprintf (stream, _("\ + fpus Recognize single precision FPU instructions.\n")); + fprintf (stream, _("\ + fpud Recognize double precision FPU instructions.\n")); + fprintf (stream, _("\ + hex Use only hexadecimal number to print immediates.\n")); +} + +void arc_insn_decode (bfd_vma addr, + struct disassemble_info *info, + disassembler_ftype disasm_func, + struct arc_instruction *insn) { - struct arcDisState ret; - memset (&ret, 0, sizeof (struct arcDisState)); - - ret.instructionLen = print_insn_arc (memaddr, info); - -#if 0 - ret.words[0] = insn[0]; - ret.words[1] = insn[1]; - ret._this = &ret; - ret.coreRegName = _coreRegName; - ret.auxRegName = _auxRegName; - ret.condCodeName = _condCodeName; - ret.instName = _instName; -#endif + const struct arc_opcode *opcode; + struct arc_disassemble_info *arc_infop; + + /* Ensure that insn would be in the reset state. */ + memset (insn, 0, sizeof (struct arc_instruction)); + + /* There was an error when disassembling, for example memory read error. */ + if (disasm_func (addr, info) < 0) + { + insn->valid = FALSE; + return; + } + + assert (info->private_data != NULL); + arc_infop = info->private_data; + + insn->length = arc_infop->insn_len;; + insn->address = addr; + + /* Quick exit if memory at this address is not an instruction. */ + if (info->insn_type == dis_noninsn) + { + insn->valid = FALSE; + return; + } - return ret; + insn->valid = TRUE; + + opcode = (const struct arc_opcode *) arc_infop->opcode; + insn->insn_class = opcode->insn_class; + insn->limm_value = arc_infop->limm; + insn->limm_p = arc_infop->limm_p; + + insn->is_control_flow = (info->insn_type == dis_branch + || info->insn_type == dis_condbranch + || info->insn_type == dis_jsr + || info->insn_type == dis_condjsr); + + insn->has_delay_slot = info->branch_delay_insns; + insn->writeback_mode + = (enum arc_ldst_writeback_mode) arc_infop->writeback_mode; + insn->data_size_mode = info->data_size; + insn->condition_code = arc_infop->condition_code; + memcpy (insn->operands, arc_infop->operands, + sizeof (struct arc_insn_operand) * MAX_INSN_ARGS); + insn->operands_count = arc_infop->operands_count; } /* Local variables: