This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[patch 2/4] nios2 disassembler update
- From: Sandra Loosemore <sandra at codesourcery dot com>
- To: "binutils at sourceware dot org" <binutils at sourceware dot org>
- Date: Wed, 22 Oct 2014 21:24:08 -0600
- Subject: [patch 2/4] nios2 disassembler update
- Authentication-results: sourceware.org; auth=none
This patch implements some code refactoring in the Nios II disassembler
intended to make it easier to add support for additional instruction
formats and encodings.
The major changes here are:
- Global state (hash tables, etc) specific to a particular encoding has
been encapsulated into an explicit state object. This will make it
easier to support e.g. disassembly of multiple objects using different
encodings from the same objdump -d command line.
- nios2_print_insn_arg has been updated to use the new accessor macros
introduced in part 1 of this series.
- Special handling for nop has been moved from nios2_disassemble to
nios2_find_opcode_hash.
-Sandra
2014-10-21 Sandra Loosemore <sandra@codesourcery.com>
opcodes/
* nios2-dis.c (INSNLEN): Update comment.
(nios2_hash_init, nios2_hash): Delete.
(OPCODE_HASH_SIZE): New.
(nios2_r1_extract_opcode): New.
(nios2_disassembler_state): New.
(nios2_r1_disassembler_state): New.
(nios2_init_opcode_hash): Add state parameter. Adjust to use it.
(nios2_find_opcode_hash): Use state object.
(bad_opcode): New.
(nios2_print_insn_arg): Add op parameter. Use it to access
format. Remove 'b' case.
(nios2_disassemble): Remove special case for nop. Remove
hard-coded instruction size.
diff --git a/opcodes/nios2-dis.c b/opcodes/nios2-dis.c
index b5c680f..00ee342 100644
--- a/opcodes/nios2-dis.c
+++ b/opcodes/nios2-dis.c
@@ -35,7 +35,7 @@
#include "elf/nios2.h"
#endif
-/* Length of Nios II instruction in bytes. */
+/* Default length of Nios II instruction in bytes. */
#define INSNLEN 4
/* Data structures used by the opcode hash table. */
@@ -45,36 +45,66 @@ typedef struct _nios2_opcode_hash
struct _nios2_opcode_hash *next;
} nios2_opcode_hash;
-static bfd_boolean nios2_hash_init = 0;
-static nios2_opcode_hash *nios2_hash[(OP_MASK_OP) + 1];
+/* Hash table size. */
+#define OPCODE_HASH_SIZE (IW_R1_OP_UNSHIFTED_MASK + 1)
-/* Separate hash table for pseudo-ops. */
-static nios2_opcode_hash *nios2_ps_hash[(OP_MASK_OP) + 1];
+/* Extract the opcode from an instruction word. */
+static unsigned int
+nios2_r1_extract_opcode (unsigned int x)
+{
+ return GET_IW_R1_OP (x);
+}
+
+/* Pseudo-ops are stored in a different table than regular instructions. */
+
+typedef struct _nios2_disassembler_state
+{
+ const struct nios2_opcode *opcodes;
+ const int *num_opcodes;
+ unsigned int (*extract_opcode) (unsigned int);
+ nios2_opcode_hash *hash[OPCODE_HASH_SIZE];
+ nios2_opcode_hash *ps_hash[OPCODE_HASH_SIZE];
+ const struct nios2_opcode *nop;
+ bfd_boolean init;
+} nios2_disassembler_state;
+
+static nios2_disassembler_state
+nios2_r1_disassembler_state = {
+ nios2_r1_opcodes,
+ &nios2_num_r1_opcodes,
+ nios2_r1_extract_opcode,
+ {},
+ {},
+ NULL,
+ 0
+};
/* Function to initialize the opcode hash table. */
static void
-nios2_init_opcode_hash (void)
+nios2_init_opcode_hash (nios2_disassembler_state *state)
{
unsigned int i;
register const struct nios2_opcode *op;
- for (i = 0; i <= OP_MASK_OP; ++i)
- nios2_hash[0] = NULL;
- for (i = 0; i <= OP_MASK_OP; i++)
- for (op = nios2_opcodes; op < &nios2_opcodes[NUMOPCODES]; op++)
+ for (i = 0; i < OPCODE_HASH_SIZE; i++)
+ for (op = state->opcodes; op < &state->opcodes[*(state->num_opcodes)]; op++)
{
nios2_opcode_hash *new_hash;
nios2_opcode_hash **bucket = NULL;
if ((op->pinfo & NIOS2_INSN_MACRO) == NIOS2_INSN_MACRO)
{
- if (i == ((op->match >> OP_SH_OP) & OP_MASK_OP)
+ if (i == state->extract_opcode (op->match)
&& (op->pinfo & (NIOS2_INSN_MACRO_MOV | NIOS2_INSN_MACRO_MOVI)
& 0x7fffffff))
- bucket = &(nios2_ps_hash[i]);
+ {
+ bucket = &(state->ps_hash[i]);
+ if (strcmp (op->name, "nop") == 0)
+ state->nop = op;
+ }
}
- else if (i == ((op->match >> OP_SH_OP) & OP_MASK_OP))
- bucket = &(nios2_hash[i]);
+ else if (i == state->extract_opcode (op->match))
+ bucket = &(state->hash[i]);
if (bucket)
{
@@ -93,11 +123,12 @@ nios2_init_opcode_hash (void)
*bucket = new_hash;
}
}
- nios2_hash_init = 1;
+ state->init = 1;
+
#ifdef DEBUG_HASHTABLE
- for (i = 0; i <= OP_MASK_OP; ++i)
+ for (i = 0; i < OPCODE_HASH_SIZE; ++i)
{
- nios2_opcode_hash *tmp_hash = nios2_hash[i];
+ nios2_opcode_hash *tmp_hash = state->hash[i];
printf ("index: 0x%02X ops: ", i);
while (tmp_hash != NULL)
{
@@ -107,9 +138,9 @@ nios2_init_opcode_hash (void)
printf ("\n");
}
- for (i = 0; i <= OP_MASK_OP; ++i)
+ for (i = 0; i < OPCODE_HASH_SIZE; ++i)
{
- nios2_opcode_hash *tmp_hash = nios2_ps_hash[i];
+ nios2_opcode_hash *tmp_hash = state->ps_hash[i];
printf ("index: 0x%02X ops: ", i);
while (tmp_hash != NULL)
{
@@ -127,19 +158,27 @@ const struct nios2_opcode *
nios2_find_opcode_hash (unsigned long opcode)
{
nios2_opcode_hash *entry;
+ nios2_disassembler_state *state;
+
+ state = &nios2_r1_disassembler_state;
/* Build a hash table to shorten the search time. */
- if (!nios2_hash_init)
- nios2_init_opcode_hash ();
+ if (!state->init)
+ nios2_init_opcode_hash (state);
+
+ /* Check for NOP first. Both NOP and MOV are macros that expand into
+ an ADD instruction, and we always want to give priority to NOP. */
+ if (state->nop->match == (opcode & state->nop->mask))
+ return state->nop;
/* First look in the pseudo-op hashtable. */
- for (entry = nios2_ps_hash[(opcode >> OP_SH_OP) & OP_MASK_OP];
+ for (entry = state->ps_hash[state->extract_opcode (opcode)];
entry; entry = entry->next)
if (entry->opcode->match == (opcode & entry->opcode->mask))
return entry->opcode;
/* Otherwise look in the main hashtable. */
- for (entry = nios2_hash[(opcode >> OP_SH_OP) & OP_MASK_OP];
+ for (entry = state->hash[state->extract_opcode (opcode)];
entry; entry = entry->next)
if (entry->opcode->match == (opcode & entry->opcode->mask))
return entry->opcode;
@@ -191,13 +230,23 @@ nios2_control_regs (void)
return cached;
}
+/* Helper routine to report internal errors. */
+static void
+bad_opcode (const struct nios2_opcode *op)
+{
+ fprintf (stderr, "Internal error: broken opcode descriptor for `%s %s'\n",
+ op->name, op->args);
+ abort ();
+}
+
/* The function nios2_print_insn_arg uses the character pointed
to by ARGPTR to determine how it print the next token or separator
character in the arguments to an instruction. */
static int
nios2_print_insn_arg (const char *argptr,
unsigned long opcode, bfd_vma address,
- disassemble_info *info)
+ disassemble_info *info,
+ const struct nios2_opcode *op)
{
unsigned long i = 0;
struct nios2_reg *reg_base;
@@ -209,98 +258,180 @@ nios2_print_insn_arg (const char *argptr,
case ')':
(*info->fprintf_func) (info->stream, "%c", *argptr);
break;
- case 'd':
- i = GET_INSN_FIELD (RRD, opcode);
-
- if (GET_INSN_FIELD (OP, opcode) == OP_MATCH_CUSTOM
- && GET_INSN_FIELD (CUSTOM_C, opcode) == 0)
- reg_base = nios2_coprocessor_regs ();
- else
- reg_base = nios2_regs;
+ case 'd':
+ switch (op->format)
+ {
+ case iw_r_type:
+ i = GET_IW_R_C (opcode);
+ reg_base = nios2_regs;
+ break;
+ case iw_custom_type:
+ i = GET_IW_CUSTOM_C (opcode);
+ if (GET_IW_CUSTOM_READC (opcode) == 0)
+ reg_base = nios2_coprocessor_regs ();
+ else
+ reg_base = nios2_regs;
+ break;
+ default:
+ bad_opcode (op);
+ }
if (i < NUMREGNAMES)
(*info->fprintf_func) (info->stream, "%s", reg_base[i].name);
else
(*info->fprintf_func) (info->stream, "unknown");
break;
- case 's':
- i = GET_INSN_FIELD (RRS, opcode);
-
- if (GET_INSN_FIELD (OP, opcode) == OP_MATCH_CUSTOM
- && GET_INSN_FIELD (CUSTOM_A, opcode) == 0)
- reg_base = nios2_coprocessor_regs ();
- else
- reg_base = nios2_regs;
+ case 's':
+ switch (op->format)
+ {
+ case iw_r_type:
+ i = GET_IW_R_A (opcode);
+ reg_base = nios2_regs;
+ break;
+ case iw_i_type:
+ i = GET_IW_I_A (opcode);
+ reg_base = nios2_regs;
+ break;
+ case iw_custom_type:
+ i = GET_IW_CUSTOM_A (opcode);
+ if (GET_IW_CUSTOM_READA (opcode) == 0)
+ reg_base = nios2_coprocessor_regs ();
+ else
+ reg_base = nios2_regs;
+ break;
+ default:
+ bad_opcode (op);
+ }
if (i < NUMREGNAMES)
(*info->fprintf_func) (info->stream, "%s", reg_base[i].name);
else
(*info->fprintf_func) (info->stream, "unknown");
break;
- case 't':
- i = GET_INSN_FIELD (RRT, opcode);
-
- if (GET_INSN_FIELD (OP, opcode) == OP_MATCH_CUSTOM
- && GET_INSN_FIELD (CUSTOM_B, opcode) == 0)
- reg_base = nios2_coprocessor_regs ();
- else
- reg_base = nios2_regs;
+ case 't':
+ switch (op->format)
+ {
+ case iw_r_type:
+ i = GET_IW_R_B (opcode);
+ reg_base = nios2_regs;
+ break;
+ case iw_i_type:
+ i = GET_IW_I_B (opcode);
+ reg_base = nios2_regs;
+ break;
+ case iw_custom_type:
+ i = GET_IW_CUSTOM_B (opcode);
+ if (GET_IW_CUSTOM_READB (opcode) == 0)
+ reg_base = nios2_coprocessor_regs ();
+ else
+ reg_base = nios2_regs;
+ break;
+ default:
+ bad_opcode (op);
+ }
if (i < NUMREGNAMES)
(*info->fprintf_func) (info->stream, "%s", reg_base[i].name);
else
(*info->fprintf_func) (info->stream, "unknown");
break;
+
case 'i':
/* 16-bit signed immediate. */
- i = (signed) (GET_INSN_FIELD (IMM16, opcode) << 16) >> 16;
+ switch (op->format)
+ {
+ case iw_i_type:
+ i = (signed) (GET_IW_I_IMM16 (opcode) << 16) >> 16;
+ break;
+ default:
+ bad_opcode (op);
+ }
(*info->fprintf_func) (info->stream, "%ld", i);
break;
+
case 'u':
/* 16-bit unsigned immediate. */
- i = GET_INSN_FIELD (IMM16, opcode);
+ switch (op->format)
+ {
+ case iw_i_type:
+ i = GET_IW_I_IMM16 (opcode);
+ break;
+ default:
+ bad_opcode (op);
+ }
(*info->fprintf_func) (info->stream, "%ld", i);
break;
+
case 'o':
/* 16-bit signed immediate address offset. */
- i = (signed) (GET_INSN_FIELD (IMM16, opcode) << 16) >> 16;
+ switch (op->format)
+ {
+ case iw_i_type:
+ i = (signed) (GET_IW_I_IMM16 (opcode) << 16) >> 16;
+ break;
+ default:
+ bad_opcode (op);
+ }
address = address + 4 + i;
(*info->print_address_func) (address, info);
break;
- case 'p':
- /* 5-bit unsigned immediate. */
- i = GET_INSN_FIELD (CACHE_OPX, opcode);
- (*info->fprintf_func) (info->stream, "%ld", i);
- break;
+
case 'j':
/* 5-bit unsigned immediate. */
- i = GET_INSN_FIELD (IMM5, opcode);
+ switch (op->format)
+ {
+ case iw_r_type:
+ i = GET_IW_R_IMM5 (opcode);
+ break;
+ default:
+ bad_opcode (op);
+ }
(*info->fprintf_func) (info->stream, "%ld", i);
break;
+
case 'l':
/* 8-bit unsigned immediate. */
- /* FIXME - not yet implemented */
- i = GET_INSN_FIELD (CUSTOM_N, opcode);
+ switch (op->format)
+ {
+ case iw_custom_type:
+ i = GET_IW_CUSTOM_N (opcode);
+ break;
+ default:
+ bad_opcode (op);
+ }
(*info->fprintf_func) (info->stream, "%lu", i);
break;
+
case 'm':
/* 26-bit unsigned immediate. */
- i = GET_INSN_FIELD (IMM26, opcode);
+ switch (op->format)
+ {
+ case iw_j_type:
+ i = GET_IW_J_IMM26 (opcode);
+ break;
+ default:
+ bad_opcode (op);
+ }
/* This translates to an address because it's only used in call
instructions. */
address = (address & 0xf0000000) | (i << 2);
(*info->print_address_func) (address, info);
break;
+
case 'c':
/* Control register index. */
- i = GET_INSN_FIELD (IMM5, opcode);
+ switch (op->format)
+ {
+ case iw_r_type:
+ i = GET_IW_R_IMM5 (opcode);
+ break;
+ default:
+ bad_opcode (op);
+ }
reg_base = nios2_control_regs ();
(*info->fprintf_func) (info->stream, "%s", reg_base[i].name);
break;
- case 'b':
- i = GET_INSN_FIELD (IMM5, opcode);
- (*info->fprintf_func) (info->stream, "%ld", i);
- break;
+
default:
(*info->fprintf_func) (info->stream, "unknown");
break;
@@ -332,46 +463,28 @@ nios2_disassemble (bfd_vma address, unsigned long opcode,
if (op != NULL)
{
- bfd_boolean is_nop = FALSE;
- if (op->pinfo == NIOS2_INSN_MACRO_MOV)
- {
- /* Check for mov r0, r0 and change to nop. */
- int dst, src;
- dst = GET_INSN_FIELD (RRD, opcode);
- src = GET_INSN_FIELD (RRS, opcode);
- if (dst == 0 && src == 0)
- {
- (*info->fprintf_func) (info->stream, "nop");
- is_nop = TRUE;
- }
- else
- (*info->fprintf_func) (info->stream, "%s", op->name);
- }
- else
- (*info->fprintf_func) (info->stream, "%s", op->name);
-
- if (!is_nop)
+ const char *argstr = op->args;
+ (*info->fprintf_func) (info->stream, "%s", op->name);
+ if (argstr != NULL && *argstr != '\0')
{
- const char *argstr = op->args;
- if (argstr != NULL && *argstr != '\0')
+ (*info->fprintf_func) (info->stream, "\t");
+ while (*argstr != '\0')
{
- (*info->fprintf_func) (info->stream, "\t");
- while (*argstr != '\0')
- {
- nios2_print_insn_arg (argstr, opcode, address, info);
- ++argstr;
- }
+ nios2_print_insn_arg (argstr, opcode, address, info, op);
+ ++argstr;
}
}
+ /* Tell the caller how far to advance the program counter. */
+ info->bytes_per_chunk = op->size;
+ return op->size;
}
else
{
/* Handle undefined instructions. */
info->insn_type = dis_noninsn;
(*info->fprintf_func) (info->stream, "0x%lx", opcode);
+ return INSNLEN;
}
- /* Tell the caller how far to advance the program counter. */
- return INSNLEN;
}