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]

Re: RFA: performance improvement for PowerPC disassembly


On Wed, Mar 14, 2012 at 12:09:58PM -0400, James Lemke wrote:
> The VLE port caused me to look at performance here.

Thanks for doing that.  I've moved your code around a little, and made
some other changes I've been meaning to do for a while.  I also spent
a little time reading some VLE docs, to better understand the VLE
patch previously posted.  Here are some thoughts:

VLE replaces large chunks of the usual powerpc opcode space to allow
16 bit insn encoding.  VLE also replaces a number of common powerpc
opcodes with its own 32 bit insn encodings, eg. mulli, addic, addic.,
addi, etc.  So many are changed that it's almost like a new
architecture.  Which leads me to conclude
a) -Many shouldn't select VLE insns for disassembly,
b) nor should -many allow VLE insns in the assembler, and
c) we'd be better off putting the VLE insns in a separate opcode
table.  Certainly the 16 bit insns would be better moved out of
powerpc_opcodes, in order to lose all that masking and whatnot in the
current opcode lookup loop.  The middle of print_insn_powerpc should
then look like

  opcode = NULL;
  if ((dialect & PPC_OPCODE_VLE) != 0)
    opcode = lookup_vle (insn);
  if (opcode == NULL)
    opcode = lookup_powerpc (insn, dialect);
  if (opcode == NULL
      && (dialect & PPC_OPCODE_ANY) != 0)
    opcode = lookup_powerpc (insn,
			     ~(ppc_cpu_t) (PPC_OPCODE_ANY | PPC_OPCODE_VLE));
  if (opcode != NULL)
    {
      /* The instruction is valid.  */
      current code following this comment
    }    

lookup_powerpc is basically the current code from
  /* Get the major opcode of the instruction.  */
to
      /* The instruction is valid.  */
extracted to a function.

The following is what I'm about to commit.

include/
	* dis-asm.h (disassemble_init_powerpc): Declare.
opcodes/
	* disassemble.c (disassemble_init_for_target): Handle ppc init.
	* ppc-dis.c (private): New var.
	(powerpc_init_dialect): Don't return calloc failure, instead use
	private.
	(PPC_OPCD_SEGS, PPC_OP_TO_SEG): Define.
	(powerpc_opcd_indices): New array.
	(disassemble_init_powerpc): New function.
	(print_insn_big_powerpc): Don't init dialect here.
	(print_insn_little_powerpc): Likewise.
	(print_insn_powerpc): Start search using powerpc_opcd_indices.

Index: include/dis-asm.h
===================================================================
RCS file: /cvs/src/src/include/dis-asm.h,v
retrieving revision 1.83
diff -u -p -r1.83 dis-asm.h
--- include/dis-asm.h	2 Nov 2011 03:09:06 -0000	1.83
+++ include/dis-asm.h	15 Mar 2012 10:05:43 -0000
@@ -1,7 +1,7 @@
 /* Interface between the opcode library and its callers.
 
    Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2010,
-   2011  Free Software Foundation, Inc.
+   2011, 2012 Free Software Foundation, Inc.
 
    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
@@ -314,6 +314,7 @@ extern int  get_arm_regname_num_options 
 extern int  set_arm_regname_option (int);
 extern int  get_arm_regnames (int, const char **, const char **, const char *const **);
 extern bfd_boolean arm_symbol_is_valid (asymbol *, struct disassemble_info *);
+extern void disassemble_init_powerpc (struct disassemble_info *);
 
 /* Fetch the disassembler for a given BFD, if that support is available.  */
 extern disassembler_ftype disassembler (bfd *);
Index: opcodes/disassemble.c
===================================================================
RCS file: /cvs/src/src/opcodes/disassemble.c,v
retrieving revision 1.83
diff -u -p -r1.83 disassemble.c
--- opcodes/disassemble.c	2 Nov 2011 03:09:11 -0000	1.83
+++ opcodes/disassemble.c	15 Mar 2012 10:05:43 -0000
@@ -1,6 +1,7 @@
 /* Select disassembly routine for specified architecture.
    Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
-   2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+   2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
+   Free Software Foundation, Inc.
 
    This file is part of the GNU opcodes library.
 
@@ -566,6 +567,16 @@ disassemble_init_for_target (struct disa
 	}
       break;
 #endif
+#ifdef ARCH_powerpc
+    case bfd_arch_powerpc:
+#endif
+#ifdef ARCH_rs6000
+    case bfd_arch_rs6000:
+#endif
+#if defined (ARCH_powerpc) || defined (ARCH_rs6000)
+      disassemble_init_powerpc (info);
+      break;
+#endif
     default:
       break;
     }
Index: opcodes/ppc-dis.c
===================================================================
RCS file: /cvs/src/src/opcodes/ppc-dis.c,v
retrieving revision 1.52
diff -u -p -r1.52 ppc-dis.c
--- opcodes/ppc-dis.c	9 Mar 2012 23:39:02 -0000	1.52
+++ opcodes/ppc-dis.c	15 Mar 2012 10:05:43 -0000
@@ -38,7 +38,7 @@ struct dis_private
 {
   /* Stash the result of parsing disassembler_options here.  */
   ppc_cpu_t dialect;
-};
+} private;
 
 #define POWERPC_DIALECT(INFO) \
   (((struct dis_private *) ((INFO)->private_data))->dialect)
@@ -217,7 +217,7 @@ ppc_parse_cpu (ppc_cpu_t ppc_cpu, const 
 
 /* Determine which set of machines to disassemble for.  */
 
-static int
+static void
 powerpc_init_dialect (struct disassemble_info *info)
 {
   ppc_cpu_t dialect = 0;
@@ -225,7 +225,7 @@ powerpc_init_dialect (struct disassemble
   struct dis_private *priv = calloc (sizeof (*priv), 1);
 
   if (priv == NULL)
-    return FALSE;
+    priv = &private;
 
   arg = info->disassembler_options;
   while (arg != NULL)
@@ -263,8 +263,34 @@ powerpc_init_dialect (struct disassemble
 
   info->private_data = priv;
   POWERPC_DIALECT(info) = dialect;
+}
+
+#define PPC_OPCD_SEGS 64
+#define PPC_OP_TO_SEG(i) (i)
+static unsigned short powerpc_opcd_indices[PPC_OPCD_SEGS];
+
+/* Calculate opcode table indices to speed up disassembly,
+   and init dialect.  */
+
+void
+disassemble_init_powerpc (struct disassemble_info *info)
+{
+  int i;
+
+  for (i = 0; i < PPC_OPCD_SEGS; ++i)
+    powerpc_opcd_indices[i] = powerpc_num_opcodes;
 
-  return TRUE;
+  i = powerpc_num_opcodes;
+  while (--i >= 0)
+    {
+      unsigned op = PPC_OP (powerpc_opcodes[i].opcode);
+      unsigned seg = PPC_OP_TO_SEG (op);
+
+      powerpc_opcd_indices[seg] = i;
+    }
+
+  if (info->arch == bfd_arch_powerpc)
+    powerpc_init_dialect (info);
 }
 
 /* Print a big endian PowerPC instruction.  */
@@ -272,8 +298,6 @@ powerpc_init_dialect (struct disassemble
 int
 print_insn_big_powerpc (bfd_vma memaddr, struct disassemble_info *info)
 {
-  if (info->private_data == NULL && !powerpc_init_dialect (info))
-    return -1;
   return print_insn_powerpc (memaddr, info, 1, POWERPC_DIALECT(info));
 }
 
@@ -282,8 +306,6 @@ print_insn_big_powerpc (bfd_vma memaddr,
 int
 print_insn_little_powerpc (bfd_vma memaddr, struct disassemble_info *info)
 {
-  if (info->private_data == NULL && !powerpc_init_dialect (info))
-    return -1;
   return print_insn_powerpc (memaddr, info, 0, POWERPC_DIALECT(info));
 }
 
@@ -375,11 +397,14 @@ print_insn_powerpc (bfd_vma memaddr,
   /* Get the major opcode of the instruction.  */
   op = PPC_OP (insn);
 
-  /* Find the first match in the opcode table.  We could speed this up
-     a bit by doing a binary search on the major opcode.  */
+  /* Find the first match in the opcode table.
+     We speed this up by segmenting the opcode table and starting the search
+     at one of the segment boundaries.  */
   opcode_end = powerpc_opcodes + powerpc_num_opcodes;
  again:
-  for (opcode = powerpc_opcodes; opcode < opcode_end; opcode++)
+  for (opcode = powerpc_opcodes + powerpc_opcd_indices[PPC_OP_TO_SEG (op)];
+       opcode < opcode_end;
+       ++opcode)
     {
       unsigned long table_op;
       const unsigned char *opindex;
@@ -390,10 +415,10 @@ print_insn_powerpc (bfd_vma memaddr,
       int skip_optional;
 
       table_op = PPC_OP (opcode->opcode);
-      if (op < table_op)
-	break;
       if (op > table_op)
 	continue;
+      if (op < table_op)
+	break;
 
       if ((insn & opcode->mask) != opcode->opcode
 	  || (opcode->flags & dialect) == 0


-- 
Alan Modra
Australia Development Lab, IBM


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