Squashed 'third_party/elfutils/' content from commit 555e15e

Change-Id: I61cde98949e47e5c8c09c33260de17f30921be79
git-subtree-dir: third_party/elfutils
git-subtree-split: 555e15ebe8bf1eb33d00747173cfc80cc65648a4
diff --git a/libcpu/i386_parse.y b/libcpu/i386_parse.y
new file mode 100644
index 0000000..5fc0682
--- /dev/null
+++ b/libcpu/i386_parse.y
@@ -0,0 +1,1687 @@
+%{
+/* Parser for i386 CPU description.
+   Copyright (C) 2004, 2005, 2007, 2008, 2009 Red Hat, Inc.
+   Written by Ulrich Drepper <drepper@redhat.com>, 2004.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils 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 copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <error.h>
+#include <inttypes.h>
+#include <libintl.h>
+#include <math.h>
+#include <obstack.h>
+#include <search.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libeu.h>
+#include <system.h>
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+/* The error handler.  */
+static void yyerror (const char *s);
+
+extern int yylex (void);
+extern int i386_lineno;
+extern char *infname;
+
+
+struct known_bitfield
+{
+  char *name;
+  unsigned long int bits;
+  int tmp;
+};
+
+
+struct bitvalue
+{
+  enum bittype { zeroone, field, failure } type;
+  union
+  {
+    unsigned int value;
+    struct known_bitfield *field;
+  };
+  struct bitvalue *next;
+};
+
+
+struct argname
+{
+  enum nametype { string, nfield } type;
+  union
+  {
+    char *str;
+    struct known_bitfield *field;
+  };
+  struct argname *next;
+};
+
+
+struct argument
+{
+  struct argname *name;
+  struct argument *next;
+};
+
+
+struct instruction
+{
+  /* The byte encoding.  */
+  struct bitvalue *bytes;
+
+  /* Prefix possible.  */
+  int repe;
+  int rep;
+
+  /* Mnemonic.  */
+  char *mnemonic;
+
+  /* Suffix.  */
+  enum { suffix_none = 0, suffix_w, suffix_w0, suffix_W, suffix_tttn,
+	 suffix_w1, suffix_W1, suffix_D } suffix;
+
+  /* Flag set if modr/m is used.  */
+  int modrm;
+
+  /* Operands.  */
+  struct operand
+  {
+    char *fct;
+    char *str;
+    int off1;
+    int off2;
+    int off3;
+  } operands[3];
+
+  struct instruction *next;
+};
+
+
+struct synonym
+{
+  char *from;
+  char *to;
+};
+
+
+struct suffix
+{
+  char *name;
+  int idx;
+};
+
+
+struct argstring
+{
+  char *str;
+  int idx;
+  int off;
+};
+
+
+static struct known_bitfield ax_reg =
+  {
+    .name = "ax", .bits = 0, .tmp = 0
+  };
+
+static struct known_bitfield dx_reg =
+  {
+    .name = "dx", .bits = 0, .tmp = 0
+  };
+
+static struct known_bitfield di_reg =
+  {
+    .name = "es_di", .bits = 0, .tmp = 0
+  };
+
+static struct known_bitfield si_reg =
+  {
+    .name = "ds_si", .bits = 0, .tmp = 0
+  };
+
+static struct known_bitfield bx_reg =
+  {
+    .name = "ds_bx", .bits = 0, .tmp = 0
+  };
+
+
+static int bitfield_compare (const void *p1, const void *p2);
+static void new_bitfield (char *name, unsigned long int num);
+static void check_bits (struct bitvalue *value);
+static int check_duplicates (struct bitvalue *val);
+static int check_argsdef (struct bitvalue *bitval, struct argument *args);
+static int check_bitsused (struct bitvalue *bitval,
+			   struct known_bitfield *suffix,
+			   struct argument *args);
+static struct argname *combine (struct argname *name);
+static void fillin_arg (struct bitvalue *bytes, struct argname *name,
+			struct instruction *instr, int n);
+static void find_numbers (void);
+static int compare_syn (const void *p1, const void *p2);
+static int compare_suf (const void *p1, const void *p2);
+static void instrtable_out (void);
+#if 0
+static void create_mnemonic_table (void);
+#endif
+
+static void *bitfields;
+static struct instruction *instructions;
+static size_t ninstructions;
+static void *synonyms;
+static void *suffixes;
+static int nsuffixes;
+static void *mnemonics;
+size_t nmnemonics;
+extern FILE *outfile;
+
+/* Number of bits used mnemonics.  */
+#if 0
+static size_t best_mnemonic_bits;
+#endif
+%}
+
+%union {
+  unsigned long int num;
+  char *str;
+  char ch;
+  struct known_bitfield *field;
+  struct bitvalue *bit;
+  struct argname *name;
+  struct argument *arg;
+}
+
+%token kMASK
+%token kPREFIX
+%token kSUFFIX
+%token kSYNONYM
+%token <str> kID
+%token <num> kNUMBER
+%token kPERCPERC
+%token <str> kBITFIELD
+%token <ch> kCHAR
+%token kSPACE
+
+%type <bit> bit byte bytes
+%type <field> bitfieldopt
+%type <name> argcomp arg
+%type <arg> args optargs
+
+%defines
+
+%%
+
+spec:		  masks kPERCPERC '\n' instrs
+		    {
+		      if (error_message_count != 0)
+			error (EXIT_FAILURE, 0,
+			       "terminated due to previous error");
+
+		      instrtable_out ();
+		    }
+		;
+
+masks:		  masks '\n' mask
+		| mask
+		;
+
+mask:		  kMASK kBITFIELD kNUMBER
+		    { new_bitfield ($2, $3); }
+		| kPREFIX kBITFIELD
+		    { new_bitfield ($2, -1); }
+		| kSUFFIX kBITFIELD
+		    { new_bitfield ($2, -2); }
+		| kSYNONYM kBITFIELD kBITFIELD
+		    {
+		      struct synonym *newp = xmalloc (sizeof (*newp));
+		      newp->from = $2;
+		      newp->to = $3;
+		      if (tfind (newp, &synonyms, compare_syn) != NULL)
+			error (0, 0,
+			       "%d: duplicate definition for synonym '%s'",
+			       i386_lineno, $2);
+		      else if (tsearch ( newp, &synonyms, compare_syn) == NULL)
+			error (EXIT_FAILURE, 0, "tsearch");
+		    }
+		|
+		;
+
+instrs:		  instrs '\n' instr
+		| instr
+		;
+
+instr:		  bytes ':' bitfieldopt kID bitfieldopt optargs
+		    {
+		      if ($3 != NULL && strcmp ($3->name, "RE") != 0
+			  && strcmp ($3->name, "R") != 0)
+			{
+			  error (0, 0, "%d: only 'R' and 'RE' prefix allowed",
+				 i386_lineno - 1);
+			}
+		      if (check_duplicates ($1) == 0
+			  && check_argsdef ($1, $6) == 0
+			  && check_bitsused ($1, $5, $6) == 0)
+			{
+			  struct instruction *newp = xcalloc (sizeof (*newp),
+							      1);
+			  if ($3 != NULL)
+			    {
+			      if (strcmp ($3->name, "RE") == 0)
+				newp->repe = 1;
+			      else if (strcmp ($3->name, "R") == 0)
+				newp->rep = 1;
+			    }
+
+			  newp->bytes = $1;
+			  newp->mnemonic = $4;
+			  if (newp->mnemonic != (void *) -1l
+			      && tfind ($4, &mnemonics,
+					(int (*)(const void *, const void *)) strcmp) == NULL)
+			    {
+			      if (tsearch ($4, &mnemonics,
+					   (int (*)(const void *, const void *)) strcmp) == NULL)
+				error (EXIT_FAILURE, errno, "tsearch");
+			      ++nmnemonics;
+			    }
+
+			  if ($5 != NULL)
+			    {
+			      if (strcmp ($5->name, "w") == 0)
+				newp->suffix = suffix_w;
+			      else if (strcmp ($5->name, "w0") == 0)
+				newp->suffix = suffix_w0;
+			      else if (strcmp ($5->name, "tttn") == 0)
+				newp->suffix = suffix_tttn;
+			      else if (strcmp ($5->name, "w1") == 0)
+				newp->suffix = suffix_w1;
+			      else if (strcmp ($5->name, "W") == 0)
+				newp->suffix = suffix_W;
+			      else if (strcmp ($5->name, "W1") == 0)
+				newp->suffix = suffix_W1;
+			      else if (strcmp ($5->name, "D") == 0)
+				newp->suffix = suffix_D;
+			      else
+				error (EXIT_FAILURE, 0,
+				       "%s: %d: unknown suffix '%s'",
+				       infname, i386_lineno - 1, $5->name);
+
+			      struct suffix search = { .name = $5->name };
+			      if (tfind (&search, &suffixes, compare_suf)
+				  == NULL)
+				{
+				  struct suffix *ns = xmalloc (sizeof (*ns));
+				  ns->name = $5->name;
+				  ns->idx = ++nsuffixes;
+				  if (tsearch (ns, &suffixes, compare_suf)
+				      == NULL)
+				    error (EXIT_FAILURE, errno, "tsearch");
+				}
+			    }
+
+			  struct argument *args = $6;
+			  int n = 0;
+			  while (args != NULL)
+			    {
+			      fillin_arg ($1, args->name, newp, n);
+
+			      args = args->next;
+			      ++n;
+			    }
+
+			  newp->next = instructions;
+			  instructions = newp;
+			  ++ninstructions;
+			}
+		    }
+		|
+		;
+
+bitfieldopt:	  kBITFIELD
+		    {
+		      struct known_bitfield search;
+		      search.name = $1;
+		      struct known_bitfield **res;
+		      res = tfind (&search, &bitfields, bitfield_compare);
+		      if (res == NULL)
+			{
+			  error (0, 0, "%d: unknown bitfield '%s'",
+				 i386_lineno, search.name);
+			  $$ = NULL;
+			}
+		      else
+			$$ = *res;
+		    }
+		|
+		    { $$ = NULL; }
+		;
+
+bytes:		  bytes ',' byte
+		    {
+		      check_bits ($3);
+
+		      struct bitvalue *runp = $1;
+		      while (runp->next != NULL)
+			runp = runp->next;
+		      runp->next = $3;
+		      $$ = $1;
+		    }
+		| byte
+		    {
+		      check_bits ($1);
+		      $$ = $1;
+		    }
+		;
+
+byte:		  byte bit
+		    {
+		      struct bitvalue *runp = $1;
+		      while (runp->next != NULL)
+			runp = runp->next;
+		      runp->next = $2;
+		      $$ = $1;
+		    }
+		| bit
+		    { $$ = $1; }
+		;
+
+bit:		  '0'
+		    {
+		      $$ = xmalloc (sizeof (struct bitvalue));
+		      $$->type = zeroone;
+		      $$->value = 0;
+		      $$->next = NULL;
+		    }
+		| '1'
+		    {
+		      $$ = xmalloc (sizeof (struct bitvalue));
+		      $$->type = zeroone;
+		      $$->value = 1;
+		      $$->next = NULL;
+		    }
+		| kBITFIELD
+		    {
+		      $$ = xmalloc (sizeof (struct bitvalue));
+		      struct known_bitfield search;
+		      search.name = $1;
+		      struct known_bitfield **res;
+		      res = tfind (&search, &bitfields, bitfield_compare);
+		      if (res == NULL)
+			{
+			  error (0, 0, "%d: unknown bitfield '%s'",
+				 i386_lineno, search.name);
+			  $$->type = failure;
+			}
+		      else
+			{
+			  $$->type = field;
+			  $$->field = *res;
+			}
+		      $$->next = NULL;
+		    }
+		;
+
+optargs:	  kSPACE args
+		    { $$ = $2; }
+		|
+		    { $$ = NULL; }
+		;
+
+args:		  args ',' arg
+		    {
+		      struct argument *runp = $1;
+		      while (runp->next != NULL)
+			runp = runp->next;
+		      runp->next = xmalloc (sizeof (struct argument));
+		      runp->next->name = combine ($3);
+		      runp->next->next = NULL;
+		      $$ = $1;
+		    }
+		| arg
+		    {
+		      $$ = xmalloc (sizeof (struct argument));
+		      $$->name = combine ($1);
+		      $$->next = NULL;
+		    }
+		;
+
+arg:		  arg argcomp
+		    {
+		      struct argname *runp = $1;
+		      while (runp->next != NULL)
+			runp = runp->next;
+		      runp->next = $2;
+		      $$ = $1;
+		    }
+		| argcomp
+		    { $$ = $1; }
+		;
+argcomp:	  kBITFIELD
+		    {
+		      $$ = xmalloc (sizeof (struct argname));
+		      $$->type = nfield;
+		      $$->next = NULL;
+
+		      struct known_bitfield search;
+		      search.name = $1;
+		      struct known_bitfield **res;
+		      res = tfind (&search, &bitfields, bitfield_compare);
+		      if (res == NULL)
+			{
+			  if (strcmp ($1, "ax") == 0)
+			    $$->field = &ax_reg;
+			  else if (strcmp ($1, "dx") == 0)
+			    $$->field = &dx_reg;
+			  else if (strcmp ($1, "es_di") == 0)
+			    $$->field = &di_reg;
+			  else if (strcmp ($1, "ds_si") == 0)
+			    $$->field = &si_reg;
+			  else if (strcmp ($1, "ds_bx") == 0)
+			    $$->field = &bx_reg;
+			  else
+			    {
+			      error (0, 0, "%d: unknown bitfield '%s'",
+				     i386_lineno, search.name);
+			      $$->field = NULL;
+			    }
+			}
+		      else
+			$$->field = *res;
+		    }
+		| kCHAR
+		    {
+		      $$ = xmalloc (sizeof (struct argname));
+		      $$->type = string;
+		      $$->next = NULL;
+		      $$->str = xmalloc (2);
+		      $$->str[0] = $1;
+		      $$->str[1] = '\0';
+		    }
+		| kID
+		    {
+		      $$ = xmalloc (sizeof (struct argname));
+		      $$->type = string;
+		      $$->next = NULL;
+		      $$->str = $1;
+		    }
+		| ':'
+		    {
+		      $$ = xmalloc (sizeof (struct argname));
+		      $$->type = string;
+		      $$->next = NULL;
+		      $$->str = xmalloc (2);
+		      $$->str[0] = ':';
+		      $$->str[1] = '\0';
+		    }
+		;
+
+%%
+
+static void
+yyerror (const char *s)
+{
+  error (0, 0, gettext ("while reading i386 CPU description: %s at line %d"),
+         gettext (s), i386_lineno);
+}
+
+
+static int
+bitfield_compare (const void *p1, const void *p2)
+{
+  struct known_bitfield *f1 = (struct known_bitfield *) p1;
+  struct known_bitfield *f2 = (struct known_bitfield *) p2;
+
+  return strcmp (f1->name, f2->name);
+}
+
+
+static void
+new_bitfield (char *name, unsigned long int num)
+{
+  struct known_bitfield *newp = xmalloc (sizeof (struct known_bitfield));
+  newp->name = name;
+  newp->bits = num;
+  newp->tmp = 0;
+
+  if (tfind (newp, &bitfields, bitfield_compare) != NULL)
+    {
+      error (0, 0, "%d: duplicated definition of bitfield '%s'",
+	     i386_lineno, name);
+      free (name);
+      return;
+    }
+
+  if (tsearch (newp, &bitfields, bitfield_compare) == NULL)
+    error (EXIT_FAILURE, errno, "%d: cannot insert new bitfield '%s'",
+	   i386_lineno, name);
+}
+
+
+/* Check that the number of bits is a multiple of 8.  */
+static void
+check_bits (struct bitvalue *val)
+{
+  struct bitvalue *runp = val;
+  unsigned int total = 0;
+
+  while (runp != NULL)
+    {
+      if (runp->type == zeroone)
+	++total;
+      else if (runp->field == NULL)
+	/* No sense doing anything, the field is not known.  */
+	return;
+      else
+	total += runp->field->bits;
+
+      runp = runp->next;
+    }
+
+  if (total % 8 != 0)
+    {
+      struct obstack os;
+      obstack_init (&os);
+
+      while (val != NULL)
+	{
+	  if (val->type == zeroone)
+	    obstack_printf (&os, "%u", val->value);
+	  else
+	    obstack_printf (&os, "{%s}", val->field->name);
+	  val = val->next;
+	}
+      obstack_1grow (&os, '\0');
+
+      error (0, 0, "%d: field '%s' not a multiple of 8 bits in size",
+	     i386_lineno, (char *) obstack_finish (&os));
+
+      obstack_free (&os, NULL);
+    }
+}
+
+
+static int
+check_duplicates (struct bitvalue *val)
+{
+  static int testcnt;
+  ++testcnt;
+
+  int result = 0;
+  while (val != NULL)
+    {
+      if (val->type == field && val->field != NULL)
+	{
+	  if (val->field->tmp == testcnt)
+	    {
+	      error (0, 0, "%d: bitfield '%s' used more than once",
+		     i386_lineno - 1, val->field->name);
+	      result = 1;
+	    }
+	  val->field->tmp = testcnt;
+	}
+
+      val = val->next;
+    }
+
+  return result;
+}
+
+
+static int
+check_argsdef (struct bitvalue *bitval, struct argument *args)
+{
+  int result = 0;
+
+  while (args != NULL)
+    {
+      for (struct argname *name = args->name; name != NULL; name = name->next)
+	if (name->type == nfield && name->field != NULL
+	    && name->field != &ax_reg && name->field != &dx_reg
+	    && name->field != &di_reg && name->field != &si_reg
+	    && name->field != &bx_reg)
+	  {
+	    struct bitvalue *runp = bitval;
+
+	    while (runp != NULL)
+	      if (runp->type == field && runp->field == name->field)
+		break;
+	      else
+		runp = runp->next;
+
+	    if (runp == NULL)
+	      {
+		error (0, 0, "%d: unknown bitfield '%s' used in output format",
+		       i386_lineno - 1, name->field->name);
+		result = 1;
+	      }
+	  }
+
+      args = args->next;
+    }
+
+  return result;
+}
+
+
+static int
+check_bitsused (struct bitvalue *bitval, struct known_bitfield *suffix,
+		struct argument *args)
+{
+  int result = 0;
+
+  while (bitval != NULL)
+    {
+      if (bitval->type == field && bitval->field != NULL
+	  && bitval->field != suffix
+	  /* {w} is handled special.  */
+	  && strcmp (bitval->field->name, "w") != 0)
+	{
+	  struct argument *runp;
+	  for (runp = args; runp != NULL; runp = runp->next)
+	    {
+	      struct argname *name = runp->name;
+
+	      while (name != NULL)
+		if (name->type == nfield && name->field == bitval->field)
+		  break;
+		else
+		  name = name->next;
+
+	      if (name != NULL)
+		break;
+	    }
+
+#if 0
+	  if (runp == NULL)
+	    {
+	      error (0, 0, "%d: bitfield '%s' not used",
+		     i386_lineno - 1, bitval->field->name);
+	      result = 1;
+	    }
+#endif
+	}
+
+      bitval = bitval->next;
+    }
+
+  return result;
+}
+
+
+static struct argname *
+combine (struct argname *name)
+{
+  struct argname *last_str = NULL;
+  for (struct argname *runp = name; runp != NULL; runp = runp->next)
+    {
+      if (runp->type == string)
+	{
+	  if (last_str == NULL)
+	    last_str = runp;
+	  else
+	    {
+	      last_str->str = xrealloc (last_str->str,
+					strlen (last_str->str)
+					+ strlen (runp->str) + 1);
+	      strcat (last_str->str, runp->str);
+	      last_str->next = runp->next;
+	    }
+	}
+      else
+	last_str = NULL;
+    }
+  return name;
+}
+
+
+#define obstack_grow_str(ob, str) obstack_grow (ob, str, strlen (str))
+
+
+static void
+fillin_arg (struct bitvalue *bytes, struct argname *name,
+	    struct instruction *instr, int n)
+{
+  static struct obstack ob;
+  static int initialized;
+  if (! initialized)
+    {
+      initialized = 1;
+      obstack_init (&ob);
+    }
+
+  struct argname *runp = name;
+  int cnt = 0;
+  while (runp != NULL)
+    {
+      /* We ignore strings in the function name.  */
+      if (runp->type == string)
+	{
+	  if (instr->operands[n].str != NULL)
+	    error (EXIT_FAILURE, 0,
+		   "%d: cannot have more than one string parameter",
+		   i386_lineno - 1);
+
+	  instr->operands[n].str = runp->str;
+	}
+      else
+	{
+	  assert (runp->type == nfield);
+
+	  /* Construct the function name.  */
+	  if (cnt++ > 0)
+	    obstack_1grow (&ob, '$');
+
+	  if (runp->field == NULL)
+	    /* Add some string which contains invalid characters.  */
+	    obstack_grow_str (&ob, "!!!INVALID!!!");
+	  else
+	    {
+	      char *fieldname = runp->field->name;
+
+	      struct synonym search = { .from = fieldname };
+
+	      struct synonym **res = tfind (&search, &synonyms, compare_syn);
+	      if (res != NULL)
+		fieldname = (*res)->to;
+
+	      obstack_grow_str (&ob, fieldname);
+	    }
+
+	  /* Now compute the bit offset of the field.  */
+	  struct bitvalue *b = bytes;
+	  int bitoff = 0;
+	  if (runp->field != NULL)
+	    while (b != NULL)
+	      {
+		if (b->type == field && b->field != NULL)
+		  {
+		    if (strcmp (b->field->name, runp->field->name) == 0)
+		      break;
+		    bitoff += b->field->bits;
+		  }
+		else
+		  ++bitoff;
+
+		b = b->next;
+	      }
+	  if (instr->operands[n].off1 == 0)
+	    instr->operands[n].off1 = bitoff;
+	  else if (instr->operands[n].off2 == 0)
+	    instr->operands[n].off2 = bitoff;
+	  else if (instr->operands[n].off3 == 0)
+	    instr->operands[n].off3 = bitoff;
+	  else
+	    error (EXIT_FAILURE, 0,
+		   "%d: cannot have more than three fields in parameter",
+		   i386_lineno - 1);
+
+	  if  (runp->field != NULL
+	       && strncasecmp (runp->field->name, "mod", 3) == 0)
+	    instr->modrm = 1;
+	}
+
+      runp = runp->next;
+    }
+  if (obstack_object_size (&ob) == 0)
+    obstack_grow_str (&ob, "string");
+  obstack_1grow (&ob, '\0');
+  char *fct = obstack_finish (&ob);
+
+  instr->operands[n].fct = fct;
+}
+
+
+#if 0
+static void
+nameout (const void *nodep, VISIT value, int level)
+{
+  if (value == leaf || value == postorder)
+    printf ("  %s\n", *(const char **) nodep);
+}
+#endif
+
+
+static int
+compare_argstring (const void *p1, const void *p2)
+{
+  const struct argstring *a1 = (const struct argstring *) p1;
+  const struct argstring *a2 = (const struct argstring *) p2;
+
+  return strcmp (a1->str, a2->str);
+}
+
+
+static int maxoff[3][3];
+static int minoff[3][3] = { { 1000, 1000, 1000 },
+			    { 1000, 1000, 1000 },
+			    { 1000, 1000, 1000 } };
+static int nbitoff[3][3];
+static void *fct_names[3];
+static int nbitfct[3];
+static int nbitsuf;
+static void *strs[3];
+static int nbitstr[3];
+static int total_bits = 2;	// Already counted the rep/repe bits.
+
+static void
+find_numbers (void)
+{
+  int nfct_names[3] = { 0, 0, 0 };
+  int nstrs[3] = { 0, 0, 0 };
+
+  /* We reverse the order of the instruction list while processing it.
+     Later phases need it in the order in which the input file has
+     them.  */
+  struct instruction *reversed = NULL;
+
+  struct instruction *runp = instructions;
+  while (runp != NULL)
+    {
+      for (int i = 0; i < 3; ++i)
+	if (runp->operands[i].fct != NULL)
+	  {
+	    struct argstring search = { .str = runp->operands[i].fct };
+	    if (tfind (&search, &fct_names[i], compare_argstring) == NULL)
+	      {
+		struct argstring *newp = xmalloc (sizeof (*newp));
+		newp->str = runp->operands[i].fct;
+		newp->idx = 0;
+		if (tsearch (newp, &fct_names[i], compare_argstring) == NULL)
+		  error (EXIT_FAILURE, errno, "tsearch");
+		++nfct_names[i];
+	      }
+
+	    if (runp->operands[i].str != NULL)
+	      {
+		search.str = runp->operands[i].str;
+		if (tfind (&search, &strs[i], compare_argstring) == NULL)
+		  {
+		    struct argstring *newp = xmalloc (sizeof (*newp));
+		    newp->str = runp->operands[i].str;
+		    newp->idx = 0;
+		    if (tsearch (newp, &strs[i], compare_argstring) == NULL)
+		      error (EXIT_FAILURE, errno, "tsearch");
+		    ++nstrs[i];
+		  }
+	      }
+
+	    maxoff[i][0] = MAX (maxoff[i][0], runp->operands[i].off1);
+	    maxoff[i][1] = MAX (maxoff[i][1], runp->operands[i].off2);
+	    maxoff[i][2] = MAX (maxoff[i][2], runp->operands[i].off3);
+
+	    if (runp->operands[i].off1 > 0)
+	      minoff[i][0] = MIN (minoff[i][0], runp->operands[i].off1);
+	    if (runp->operands[i].off2 > 0)
+	      minoff[i][1] = MIN (minoff[i][1], runp->operands[i].off2);
+	    if (runp->operands[i].off3 > 0)
+	      minoff[i][2] = MIN (minoff[i][2], runp->operands[i].off3);
+	  }
+
+      struct instruction *old = runp;
+      runp = runp->next;
+
+      old->next = reversed;
+      reversed = old;
+    }
+  instructions = reversed;
+
+  int d;
+  int c;
+  for (int i = 0; i < 3; ++i)
+    {
+      // printf ("min1 = %d, min2 = %d, min3 = %d\n", minoff[i][0], minoff[i][1], minoff[i][2]);
+      // printf ("max1 = %d, max2 = %d, max3 = %d\n", maxoff[i][0], maxoff[i][1], maxoff[i][2]);
+
+      if (minoff[i][0] == 1000)
+	nbitoff[i][0] = 0;
+      else
+	{
+	  nbitoff[i][0] = 1;
+	  d = maxoff[i][0] - minoff[i][0];
+	  c = 1;
+	  while (c < d)
+	    {
+	      ++nbitoff[i][0];
+	      c *= 2;
+	    }
+	  total_bits += nbitoff[i][0];
+	}
+
+      if (minoff[i][1] == 1000)
+	nbitoff[i][1] = 0;
+      else
+	{
+	  nbitoff[i][1] = 1;
+	  d = maxoff[i][1] - minoff[i][1];
+	  c = 1;
+	  while (c < d)
+	    {
+	      ++nbitoff[i][1];
+	      c *= 2;
+	    }
+	  total_bits += nbitoff[i][1];
+	}
+
+      if (minoff[i][2] == 1000)
+	nbitoff[i][2] = 0;
+      else
+	{
+	  nbitoff[i][2] = 1;
+	  d = maxoff[i][2] - minoff[i][2];
+	  c = 1;
+	  while (c < d)
+	    {
+	      ++nbitoff[i][2];
+	      c *= 2;
+	    }
+	  total_bits += nbitoff[i][2];
+	}
+      // printf ("off1 = %d, off2 = %d, off3 = %d\n", nbitoff[i][0], nbitoff[i][1], nbitoff[i][2]);
+
+      nbitfct[i] = 1;
+      d = nfct_names[i];
+      c = 1;
+      while (c < d)
+	{
+	  ++nbitfct[i];
+	  c *= 2;
+	}
+      total_bits += nbitfct[i];
+      // printf ("%d fct[%d], %d bits\n", nfct_names[i], i, nbitfct[i]);
+
+      if (nstrs[i] != 0)
+	{
+	  nbitstr[i] = 1;
+	  d = nstrs[i];
+	  c = 1;
+	  while (c < d)
+	    {
+	      ++nbitstr[i];
+	      c *= 2;
+	    }
+	  total_bits += nbitstr[i];
+	}
+
+      // twalk (fct_names[i], nameout);
+    }
+
+  nbitsuf = 0;
+  d = nsuffixes;
+  c = 1;
+  while (c < d)
+    {
+      ++nbitsuf;
+      c *= 2;
+    }
+  total_bits += nbitsuf;
+  // printf ("%d suffixes, %d bits\n", nsuffixes, nbitsuf);
+}
+
+
+static int
+compare_syn (const void *p1, const void *p2)
+{
+  const struct synonym *s1 = (const struct synonym *) p1;
+  const struct synonym *s2 = (const struct synonym *) p2;
+
+  return strcmp (s1->from, s2->from);
+}
+
+
+static int
+compare_suf (const void *p1, const void *p2)
+{
+  const struct suffix *s1 = (const struct suffix *) p1;
+  const struct suffix *s2 = (const struct suffix *) p2;
+
+  return strcmp (s1->name, s2->name);
+}
+
+
+static int count_op_str;
+static int off_op_str;
+static void
+print_op_str (const void *nodep, VISIT value,
+	      int level __attribute__ ((unused)))
+{
+  if (value == leaf || value == postorder)
+    {
+      const char *str = (*(struct argstring **) nodep)->str;
+      fprintf (outfile, "%s\n  \"%s",
+	       count_op_str == 0 ? "" : "\\0\"", str);
+      (*(struct argstring **) nodep)->idx = ++count_op_str;
+      (*(struct argstring **) nodep)->off = off_op_str;
+      off_op_str += strlen (str) + 1;
+    }
+}
+
+
+static void
+print_op_str_idx (const void *nodep, VISIT value,
+		  int level __attribute__ ((unused)))
+{
+  if (value == leaf || value == postorder)
+    printf ("  %d,\n", (*(struct argstring **) nodep)->off);
+}
+
+
+static void
+print_op_fct (const void *nodep, VISIT value,
+	      int level __attribute__ ((unused)))
+{
+  if (value == leaf || value == postorder)
+    {
+      fprintf (outfile, "  FCT_%s,\n", (*(struct argstring **) nodep)->str);
+      (*(struct argstring **) nodep)->idx = ++count_op_str;
+    }
+}
+
+
+#if NMNES < 2
+# error "bogus NMNES value"
+#endif
+
+static void
+instrtable_out (void)
+{
+  find_numbers ();
+
+#if 0
+  create_mnemonic_table ();
+
+  fprintf (outfile, "#define MNEMONIC_BITS %zu\n", best_mnemonic_bits);
+#else
+  fprintf (outfile, "#define MNEMONIC_BITS %ld\n",
+	   lrint (ceil (log2 (NMNES))));
+#endif
+  fprintf (outfile, "#define SUFFIX_BITS %d\n", nbitsuf);
+  for (int i = 0; i < 3; ++i)
+    {
+      fprintf (outfile, "#define FCT%d_BITS %d\n", i + 1, nbitfct[i]);
+      if (nbitstr[i] != 0)
+	fprintf (outfile, "#define STR%d_BITS %d\n", i + 1, nbitstr[i]);
+      fprintf (outfile, "#define OFF%d_1_BITS %d\n", i + 1, nbitoff[i][0]);
+      fprintf (outfile, "#define OFF%d_1_BIAS %d\n", i + 1, minoff[i][0]);
+      if (nbitoff[i][1] != 0)
+	{
+	  fprintf (outfile, "#define OFF%d_2_BITS %d\n", i + 1, nbitoff[i][1]);
+	  fprintf (outfile, "#define OFF%d_2_BIAS %d\n", i + 1, minoff[i][1]);
+	}
+      if (nbitoff[i][2] != 0)
+	{
+	  fprintf (outfile, "#define OFF%d_3_BITS %d\n", i + 1, nbitoff[i][2]);
+	  fprintf (outfile, "#define OFF%d_3_BIAS %d\n", i + 1, minoff[i][2]);
+	}
+    }
+
+  fputs ("\n#include <i386_data.h>\n\n", outfile);
+
+
+#define APPEND(a, b) APPEND_ (a, b)
+#define APPEND_(a, b) a##b
+#define EMIT_SUFFIX(suf) \
+  fprintf (outfile, "#define suffix_%s %d\n", #suf, APPEND (suffix_, suf))
+  EMIT_SUFFIX (none);
+  EMIT_SUFFIX (w);
+  EMIT_SUFFIX (w0);
+  EMIT_SUFFIX (W);
+  EMIT_SUFFIX (tttn);
+  EMIT_SUFFIX (D);
+  EMIT_SUFFIX (w1);
+  EMIT_SUFFIX (W1);
+
+  fputc_unlocked ('\n', outfile);
+
+  for (int i = 0; i < 3; ++i)
+    {
+      /* Functions.  */
+      count_op_str = 0;
+      fprintf (outfile, "static const opfct_t op%d_fct[] =\n{\n  NULL,\n",
+	       i + 1);
+      twalk (fct_names[i], print_op_fct);
+      fputs ("};\n", outfile);
+
+      /* The operand strings.  */
+      if (nbitstr[i] != 0)
+	{
+	  count_op_str = 0;
+	  off_op_str = 0;
+	  fprintf (outfile, "static const char op%d_str[] =", i + 1);
+	  twalk (strs[i], print_op_str);
+	  fputs ("\";\n", outfile);
+
+	  fprintf (outfile, "static const uint8_t op%d_str_idx[] = {\n",
+		   i + 1);
+	  twalk (strs[i], print_op_str_idx);
+	  fputs ("};\n", outfile);
+	}
+    }
+
+
+  fputs ("static const struct instr_enc instrtab[] =\n{\n", outfile);
+  struct instruction *instr;
+  for (instr = instructions; instr != NULL; instr = instr->next)
+    {
+      fputs ("  {", outfile);
+      if (instr->mnemonic == (void *) -1l)
+	fputs (" .mnemonic = MNE_INVALID,", outfile);
+      else
+	fprintf (outfile, " .mnemonic = MNE_%s,", instr->mnemonic);
+      fprintf (outfile, " .rep = %d,", instr->rep);
+      fprintf (outfile, " .repe = %d,", instr->repe);
+      fprintf (outfile, " .suffix = %d,", instr->suffix);
+      fprintf (outfile, " .modrm = %d,", instr->modrm);
+
+      for (int i = 0; i < 3; ++i)
+	{
+	  int idx = 0;
+	  if (instr->operands[i].fct != NULL)
+	    {
+	      struct argstring search = { .str = instr->operands[i].fct };
+	      struct argstring **res = tfind (&search, &fct_names[i],
+					      compare_argstring);
+	      assert (res != NULL);
+	      idx = (*res)->idx;
+	    }
+	  fprintf (outfile, " .fct%d = %d,", i + 1, idx);
+
+	  idx = 0;
+	  if (instr->operands[i].str != NULL)
+	    {
+	      struct argstring search = { .str = instr->operands[i].str };
+	      struct argstring **res = tfind (&search, &strs[i],
+					      compare_argstring);
+	      assert (res != NULL);
+	      idx = (*res)->idx;
+	    }
+	  if (nbitstr[i] != 0)
+	    fprintf (outfile, " .str%d = %d,", i + 1, idx);
+
+	  fprintf (outfile, " .off%d_1 = %d,", i + 1,
+		   MAX (0, instr->operands[i].off1 - minoff[i][0]));
+
+	  if (nbitoff[i][1] != 0)
+	    fprintf (outfile, " .off%d_2 = %d,", i + 1,
+		     MAX (0, instr->operands[i].off2 - minoff[i][1]));
+
+	  if (nbitoff[i][2] != 0)
+	    fprintf (outfile, " .off%d_3 = %d,", i + 1,
+		     MAX (0, instr->operands[i].off3 - minoff[i][2]));
+	}
+
+      fputs (" },\n", outfile);
+    }
+  fputs ("};\n", outfile);
+
+  fputs ("static const uint8_t match_data[] =\n{\n", outfile);
+  size_t cnt = 0;
+  for (instr = instructions; instr != NULL; instr = instr->next, ++cnt)
+    {
+      /* First count the number of bytes.  */
+      size_t totalbits = 0;
+      size_t zerobits = 0;
+      bool leading_p = true;
+      size_t leadingbits = 0;
+      struct bitvalue *b = instr->bytes;
+      while (b != NULL)
+	{
+	  if (b->type == zeroone)
+	    {
+	      ++totalbits;
+	      zerobits = 0;
+	      if (leading_p)
+		++leadingbits;
+	    }
+	  else
+	    {
+	      totalbits += b->field->bits;
+	      /* We must always count the mod/rm byte.  */
+	      if (strncasecmp (b->field->name, "mod", 3) == 0)
+		zerobits = 0;
+	      else
+		zerobits += b->field->bits;
+	      leading_p = false;
+	    }
+	  b = b->next;
+	}
+      size_t nbytes = (totalbits - zerobits + 7) / 8;
+      assert (nbytes > 0);
+      size_t leadingbytes = leadingbits / 8;
+
+      fprintf (outfile, "  %#zx,", nbytes | (leadingbytes << 4));
+
+      /* Now create the mask and byte values.  */
+      uint8_t byte = 0;
+      uint8_t mask = 0;
+      int nbits = 0;
+      b = instr->bytes;
+      while (b != NULL)
+	{
+	  if (b->type == zeroone)
+	    {
+	      byte = (byte << 1) | b->value;
+	      mask = (mask << 1) | 1;
+	      if (++nbits == 8)
+		{
+		  if (leadingbytes > 0)
+		    {
+		      assert (mask == 0xff);
+		      fprintf (outfile, " %#" PRIx8 ",", byte);
+		      --leadingbytes;
+		    }
+		  else
+		    fprintf (outfile, " %#" PRIx8 ", %#" PRIx8 ",",
+			     mask, byte);
+		  byte = mask = nbits = 0;
+		  if (--nbytes == 0)
+		    break;
+		}
+	    }
+	  else
+	    {
+	      assert (leadingbytes == 0);
+
+	      unsigned long int remaining = b->field->bits;
+	      while (nbits + remaining > 8)
+		{
+		  fprintf (outfile, " %#" PRIx8 ", %#" PRIx8 ",",
+			   mask << (8 - nbits), byte << (8 - nbits));
+		  remaining = nbits + remaining - 8;
+		  byte = mask = nbits = 0;
+		  if (--nbytes == 0)
+		    break;
+		}
+	      byte <<= remaining;
+	      mask <<= remaining;
+	      nbits += remaining;
+	      if (nbits == 8)
+		{
+		  fprintf (outfile, " %#" PRIx8 ", %#" PRIx8 ",", mask, byte);
+		  byte = mask = nbits = 0;
+		  if (--nbytes == 0)
+		    break;
+		}
+	    }
+	  b = b->next;
+	}
+
+      fputc_unlocked ('\n', outfile);
+    }
+  fputs ("};\n", outfile);
+}
+
+
+#if 0
+static size_t mnemonic_maxlen;
+static size_t mnemonic_minlen;
+static size_t
+which_chars (const char *str[], size_t nstr)
+{
+  char used_char[256];
+  memset (used_char, '\0', sizeof (used_char));
+  mnemonic_maxlen = 0;
+  mnemonic_minlen = 10000;
+  for (size_t cnt = 0; cnt < nstr; ++cnt)
+    {
+      const unsigned char *cp = (const unsigned char *) str[cnt];
+      mnemonic_maxlen = MAX (mnemonic_maxlen, strlen ((char *) cp));
+      mnemonic_minlen = MIN (mnemonic_minlen, strlen ((char *) cp));
+      do
+        used_char[*cp++] = 1;
+      while (*cp != '\0');
+    }
+  size_t nused_char = 0;
+  for (size_t cnt = 0; cnt < 256; ++cnt)
+    if (used_char[cnt] != 0)
+      ++nused_char;
+  return nused_char;
+}
+
+
+static const char **mnemonic_strs;
+static size_t nmnemonic_strs;
+static void
+add_mnemonics (const void *nodep, VISIT value,
+	       int level __attribute__ ((unused)))
+{
+  if (value == leaf || value == postorder)
+    mnemonic_strs[nmnemonic_strs++] = *(const char **) nodep;
+}
+
+
+struct charfreq
+{
+  char ch;
+  int freq;
+};
+static struct charfreq pfxfreq[256];
+static struct charfreq sfxfreq[256];
+
+
+static int
+compare_freq (const void *p1, const void *p2)
+{
+  const struct charfreq *c1 = (const struct charfreq *) p1;
+  const struct charfreq *c2 = (const struct charfreq *) p2;
+
+  if (c1->freq > c2->freq)
+    return -1;
+  if (c1->freq < c2->freq)
+    return 1;
+  return 0;
+}
+
+
+static size_t
+compute_pfxfreq (const char *str[], size_t nstr)
+{
+  memset (pfxfreq, '\0', sizeof (pfxfreq));
+
+  for (size_t i = 0; i < nstr; ++i)
+    pfxfreq[i].ch = i;
+
+  for (size_t i = 0; i < nstr; ++i)
+    ++pfxfreq[*((const unsigned char *) str[i])].freq;
+
+  qsort (pfxfreq, 256, sizeof (struct charfreq), compare_freq);
+
+  size_t n = 0;
+  while (n < 256 && pfxfreq[n].freq != 0)
+    ++n;
+  return n;
+}
+
+
+struct strsnlen
+{
+  const char *str;
+  size_t len;
+};
+
+static size_t
+compute_sfxfreq (size_t nstr, struct strsnlen *strsnlen)
+{
+  memset (sfxfreq, '\0', sizeof (sfxfreq));
+
+  for (size_t i = 0; i < nstr; ++i)
+    sfxfreq[i].ch = i;
+
+  for (size_t i = 0; i < nstr; ++i)
+    ++sfxfreq[((const unsigned char *) strchrnul (strsnlen[i].str, '\0'))[-1]].freq;
+
+  qsort (sfxfreq, 256, sizeof (struct charfreq), compare_freq);
+
+  size_t n = 0;
+  while (n < 256 && sfxfreq[n].freq != 0)
+    ++n;
+  return n;
+}
+
+
+static void
+create_mnemonic_table (void)
+{
+  mnemonic_strs = xmalloc (nmnemonics * sizeof (char *));
+
+  twalk (mnemonics, add_mnemonics);
+
+  (void) which_chars (mnemonic_strs, nmnemonic_strs);
+
+  size_t best_so_far = 100000000;
+  char *best_prefix = NULL;
+  char *best_suffix = NULL;
+  char *best_table = NULL;
+  size_t best_table_size = 0;
+  size_t best_table_bits = 0;
+  size_t best_prefix_bits = 0;
+
+  /* We can precompute the prefix characters.  */
+  size_t npfx_char = compute_pfxfreq (mnemonic_strs, nmnemonic_strs);
+
+  /* Compute best size for string representation including explicit NUL.  */
+  for (size_t pfxbits = 0; (1u << pfxbits) < 2 * npfx_char; ++pfxbits)
+    {
+      char prefix[1 << pfxbits];
+      size_t i;
+      for (i = 0; i < (1u << pfxbits) - 1; ++i)
+	prefix[i] = pfxfreq[i].ch;
+      prefix[i] = '\0';
+
+      struct strsnlen strsnlen[nmnemonic_strs];
+
+      for (i = 0; i < nmnemonic_strs; ++i)
+	{
+	  if (strchr (prefix, *mnemonic_strs[i]) != NULL)
+	    strsnlen[i].str = mnemonic_strs[i] + 1;
+	  else
+	    strsnlen[i].str = mnemonic_strs[i];
+	  strsnlen[i].len = strlen (strsnlen[i].str);
+	}
+
+      /* With the prefixes gone, try to combine strings.  */
+      size_t nstrsnlen = 1;
+      for (i = 1; i < nmnemonic_strs; ++i)
+	{
+	  size_t j;
+	  for (j = 0; j < nstrsnlen; ++j)
+	    if (strsnlen[i].len > strsnlen[j].len
+		&& strcmp (strsnlen[j].str,
+			   strsnlen[i].str + (strsnlen[i].len
+					      - strsnlen[j].len)) == 0)
+	      {
+		strsnlen[j] = strsnlen[i];
+		break;
+	      }
+	    else if (strsnlen[i].len < strsnlen[j].len
+		     && strcmp (strsnlen[i].str,
+				strsnlen[j].str + (strsnlen[j].len
+						   - strsnlen[i].len)) == 0)
+	      break;
+;
+	  if (j == nstrsnlen)
+	      strsnlen[nstrsnlen++] = strsnlen[i];
+	}
+
+      size_t nsfx_char = compute_sfxfreq (nstrsnlen, strsnlen);
+
+      for (size_t sfxbits = 0; (1u << sfxbits) < 2 * nsfx_char; ++sfxbits)
+	{
+	  char suffix[1 << sfxbits];
+
+	  for (i = 0; i < (1u << sfxbits) - 1; ++i)
+	    suffix[i] = sfxfreq[i].ch;
+	  suffix[i] = '\0';
+
+	  size_t newlen[nstrsnlen];
+
+	  for (i = 0; i < nstrsnlen; ++i)
+	    if (strchr (suffix, strsnlen[i].str[strsnlen[i].len - 1]) != NULL)
+	      newlen[i] = strsnlen[i].len - 1;
+	    else
+	      newlen[i] = strsnlen[i].len;
+
+	  char charused[256];
+	  memset (charused, '\0', sizeof (charused));
+	  size_t ncharused = 0;
+
+	  const char *tablestr[nstrsnlen];
+	  size_t ntablestr = 1;
+	  tablestr[0] = strsnlen[0].str;
+	  size_t table = newlen[0] + 1;
+	  for (i = 1; i < nstrsnlen; ++i)
+	    {
+	      size_t j;
+	      for (j = 0; j < ntablestr; ++j)
+		if (newlen[i] > newlen[j]
+		    && memcmp (tablestr[j],
+			       strsnlen[i].str + (newlen[i] - newlen[j]),
+			       newlen[j]) == 0)
+		  {
+		    table += newlen[i] - newlen[j];
+		    tablestr[j] = strsnlen[i].str;
+		    newlen[j] = newlen[i];
+		    break;
+		  }
+		else if (newlen[i] < newlen[j]
+		     && memcmp (strsnlen[i].str,
+				tablestr[j] + (newlen[j] - newlen[i]),
+				newlen[i]) == 0)
+		  break;
+
+	      if (j == ntablestr)
+		{
+		  table += newlen[i] + 1;
+		  tablestr[ntablestr] = strsnlen[i].str;
+		  newlen[ntablestr] = newlen[i];
+
+		  ++ntablestr;
+		}
+
+	      for (size_t x = 0; x < newlen[j]; ++x)
+		if (charused[((const unsigned char *) tablestr[j])[x]]++ == 0)
+		  ++ncharused;
+	    }
+
+	  size_t ncharused_bits = 0;
+	  i = 1;
+	  while (i < ncharused)
+	    {
+	      i *= 2;
+	      ++ncharused_bits;
+	    }
+
+	  size_t table_bits = 0;
+	  i = 1;
+	  while (i < table)
+	    {
+	      i *= 2;
+	      ++table_bits;
+	    }
+
+	  size_t mnemonic_bits = table_bits + pfxbits + sfxbits;
+	  size_t new_total = (((table + 7) / 8) * ncharused_bits + ncharused
+			      + (pfxbits == 0 ? 0 : (1 << pfxbits) - 1)
+			      + (sfxbits == 0 ? 0 : (1 << sfxbits) - 1)
+			      + (((total_bits + mnemonic_bits + 7) / 8)
+				 * ninstructions));
+
+	  if (new_total < best_so_far)
+	    {
+	      best_so_far = new_total;
+	      best_mnemonic_bits = mnemonic_bits;
+
+	      free (best_suffix);
+	      best_suffix = xstrdup (suffix);
+
+	      free (best_prefix);
+	      best_prefix = xstrdup (prefix);
+	      best_prefix_bits = pfxbits;
+
+	      best_table_size = table;
+	      best_table_bits = table_bits;
+	      char *cp = best_table = xrealloc (best_table, table);
+	      for (i = 0; i < ntablestr; ++i)
+		{
+		  assert (cp + newlen[i] + 1 <= best_table + table);
+		  cp = mempcpy (cp, tablestr[i], newlen[i]);
+		  *cp++ = '\0';
+		}
+	      assert (cp == best_table + table);
+	    }
+	}
+    }
+
+  fputs ("static const char mnemonic_table[] =\n\"", outfile);
+  for (size_t i = 0; i < best_table_size; ++i)
+    {
+      if (((i + 1) % 60) == 0)
+	fputs ("\"\n\"", outfile);
+      if (!isascii (best_table[i]) || !isprint (best_table[i]))
+	fprintf (outfile, "\\%03o", best_table[i]);
+      else
+	fputc (best_table[i], outfile);
+    }
+  fputs ("\";\n", outfile);
+
+  if (best_prefix[0] != '\0')
+    fprintf (outfile,
+	     "static const char prefix[%zu] = \"%s\";\n"
+	     "#define PREFIXCHAR_BITS %zu\n",
+	     strlen (best_prefix), best_prefix, best_prefix_bits);
+  else
+    fputs ("#define NO_PREFIX\n", outfile);
+
+  if (best_suffix[0] != '\0')
+    fprintf (outfile, "static const char suffix[%zu] = \"%s\";\n",
+	     strlen (best_suffix), best_suffix);
+  else
+    fputs ("#define NO_SUFFIX\n", outfile);
+
+  for (size_t i = 0; i < nmnemonic_strs; ++i)
+    {
+      const char *mne = mnemonic_strs[i];
+
+      size_t pfxval = 0;
+      char *cp = strchr (best_prefix, *mne);
+      if (cp != NULL)
+	{
+	  pfxval = 1 + (cp - best_prefix);
+	  ++mne;
+	}
+
+      size_t l = strlen (mne);
+
+      size_t sfxval = 0;
+      cp = strchr (best_suffix, mne[l - 1]);
+      if (cp != NULL)
+	{
+	  sfxval = 1 + (cp - best_suffix);
+	  --l;
+	}
+
+      char *off = memmem (best_table, best_table_size, mne, l);
+      while (off[l] != '\0')
+	{
+	  off = memmem (off + 1, best_table_size, mne, l);
+	  assert (off != NULL);
+	}
+
+      fprintf (outfile, "#define MNE_%s %#zx\n",
+	       mnemonic_strs[i],
+	       (off - best_table)
+	       + ((pfxval + (sfxval << best_prefix_bits)) << best_table_bits));
+    }
+}
+#endif