This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 1/3] opcodes/arc: Compute insn lengths in disassembler


Change the mechanism by which the instruction length is figured out in
the arc disassembler.  Previously we maintained a micro-decoder that
contained enough knowledge to figure out the instruction length.  The
problem is that as instructions are added for different arc
architectures, more and more special cases need to be added to this
micro disassembler.

This commit changes to a table based lookup for the instruction length,
the table maps from the major opcode of the instruction to the
instruction length.  The table is filled at run-time, the first time
that the table is needed.

The table is self-checking while being built, it only contains the
instructions for the current architecture being disassembled, and if
different instructions with the same major opcode have different
lengths, this will cause an error while the table is being filled.

opcodes/ChangeLog:

	* arc-dis.c (arc_insn_length): New function.
	(print_insn_arc): Use arc_insn_length.
---
 opcodes/ChangeLog |  5 ++++
 opcodes/arc-dis.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 63 insertions(+), 11 deletions(-)

diff --git a/opcodes/arc-dis.c b/opcodes/arc-dis.c
index 5f8fa42..f7cff5f 100644
--- a/opcodes/arc-dis.c
+++ b/opcodes/arc-dis.c
@@ -102,6 +102,53 @@ special_flag_p (const char *opname,
   return 0;
 }
 
+/* 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 int
+arc_insn_length (unsigned isa_mask, bfd_byte msb,
+                 bfd_byte lsb ATTRIBUTE_UNUSED)
+{
+  static int tbl [32]; /* 0x00 -> 0x1f */
+  static int tbl_init = 0;
+#define MAJOR_OPCODE_VALUE(BYTE) (BYTE >> 3)
+
+  if (!tbl_init)
+    {
+      unsigned i;
+
+      tbl_init = 1;
+      memset (tbl, 0, sizeof (tbl));
+      for (i = 0; i < arc_num_opcodes; i++)
+        {
+          const struct arc_opcode *opcode;
+          bfd_byte b;
+          int is_short, len;
+          unsigned value;
+
+          opcode = &arc_opcodes[i];
+          if (!(opcode->cpu & isa_mask))
+            continue;
+
+          value = opcode->opcode;
+          is_short = ARC_SHORT (opcode->mask);
+          b = MAJOR_OPCODE_VALUE (value >> (is_short ? 8 : 24));
+          len = (is_short ? 2 : 4);
+          assert (tbl [b] == 0 || tbl [b] == len);
+          tbl [b] = len;
+        }
+    }
+
+  return tbl [MAJOR_OPCODE_VALUE (msb)];
+
+#undef MAJOR_OPCODE_VALUE
+}
+
 /* Disassemble ARC instructions.  */
 
 static int
@@ -218,20 +265,19 @@ 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.  */
-      )
+  insnLen = arc_insn_length (isa_mask, buffer[lowbyte], buffer[highbyte]);
+  switch (insnLen)
     {
-      /* This is a short instruction.  */
-      insnLen = 2;
+    case 2:
       insn[0] = (buffer[lowbyte] << 8) | buffer[highbyte];
-    }
-  else
-    {
-      insnLen = 4;
+      break;
 
+    default:
+      /* An unknown instruction is treated as being length 4.  This is
+         possibly not the best solution, but matches the behaviour that was
+         in place before the table based instruction length look-up was
+         introduced.  */
+    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)
@@ -240,6 +286,7 @@ print_insn_arc (bfd_vma memaddr,
 	  return -1;
 	}
       insn[0] = ARRANGE_ENDIAN (info, buffer);
+      break;
     }
 
   /* Set some defaults for the insn info.  */
-- 
2.5.1


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]