386BSD 0.0 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Fri, 8 Sep 1989 03:18:33 +0000 (19:18 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Fri, 8 Sep 1989 03:18:33 +0000 (19:18 -0800)
Work on file usr/src/usr.bin/gcc/cc1/genpeep.c
Work on file usr/src/usr.bin/gcc/cc1/genrecog.c

Co-Authored-By: Lynne Greer Jolitz <ljolitz@cardio.ucsf.edu>
Synthesized-from: 386BSD-0.0/src

usr/src/usr.bin/gcc/cc1/genpeep.c [new file with mode: 0644]
usr/src/usr.bin/gcc/cc1/genrecog.c [new file with mode: 0644]

diff --git a/usr/src/usr.bin/gcc/cc1/genpeep.c b/usr/src/usr.bin/gcc/cc1/genpeep.c
new file mode 100644 (file)
index 0000000..7553485
--- /dev/null
@@ -0,0 +1,437 @@
+/* Generate code from machine description to perform peephole optimizations.
+   Copyright (C) 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GNU CC 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 a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+
+#include <stdio.h>
+#include "config.h"
+#include "rtl.h"
+#include "obstack.h"
+
+struct obstack obstack;
+struct obstack *rtl_obstack = &obstack;
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+extern int xmalloc ();
+extern void free ();
+
+/* While tree-walking an instruction pattern, we keep a chain
+   of these `struct link's to record how to get down to the
+   current position.  In each one, POS is the operand number,
+   and if the operand is a vector VEC is the element number.
+   VEC is -1 if the operand is not a vector.  */
+
+struct link
+{
+  struct link *next;
+  int pos;
+  int vecelt;
+};
+
+void match_rtx ();
+void gen_exp ();
+void fatal ();
+void fancy_abort ();
+
+int max_opno;
+
+/* Number of operands used in current peephole definition.  */
+
+int n_operands;
+
+/* Peephole optimizations get insn codes just like insn patterns.
+   Count them so we know the code of the define_peephole we are handling.  */
+
+int insn_code_number = 0;
+
+void print_path ();
+void print_code ();
+\f
+void
+gen_peephole (peep)
+     rtx peep;
+{
+  int ninsns = XVECLEN (peep, 0);
+  int i;
+
+  n_operands = 0;
+
+  printf ("  insn = ins1;\n");
+#if 0
+  printf ("  want_jump = 0;\n");
+#endif
+
+  for (i = 0; i < ninsns; i++)
+    {
+      if (i > 0)
+       {
+         printf ("  do { insn = NEXT_INSN (insn);\n");
+         printf ("       if (insn == 0) goto L%d; }\n",
+                 insn_code_number);
+         printf ("  while (GET_CODE (insn) == NOTE);\n");
+
+         printf ("  if (GET_CODE (insn) == CODE_LABEL\n\
+      || GET_CODE (insn) == BARRIER)\n    goto L%d;\n",
+                 insn_code_number);
+       }
+
+#if 0
+      printf ("  if (GET_CODE (insn) == JUMP_INSN)\n");
+      printf ("    want_jump = JUMP_LABEL (insn);\n");
+#endif
+
+      printf ("  pat = PATTERN (insn);\n");
+
+      /* Walk the insn's pattern, remembering at all times the path
+        down to the walking point.  */
+
+      match_rtx (XVECEXP (peep, 0, i), 0, insn_code_number);
+    }
+
+  /* We get this far if the pattern matches.
+     Now test the extra condition.  */
+
+  if (XSTR (peep, 1) && XSTR (peep, 1)[0])
+    printf ("  if (! (%s)) goto L%d;\n",
+           XSTR (peep, 1), insn_code_number);
+
+  /* If that matches, construct new pattern and put it in the first insn.
+     This new pattern will never be matched.
+     It exists only so that insn-extract can get the operands back.
+     So use a simple regular form: a PARALLEL containing a vector
+     of all the operands.  */
+
+  printf ("  PATTERN (ins1) = gen_rtx (PARALLEL, VOIDmode, gen_rtvec_v (%d, operands));\n", n_operands);
+
+#if 0
+  printf ("  if (want_jump && GET_CODE (ins1) != JUMP_INSN)\n");
+  printf ("    {\n");
+  printf ("      rtx insn2 = emit_jump_insn_before (PATTERN (ins1), ins1);\n");
+  printf ("      delete_insn (ins1);\n");
+  printf ("      ins1 = ins2;\n");
+  printf ("    }\n");
+#endif
+
+  /* Record this define_peephole's insn code in the insn,
+     as if it had been recognized to match this.  */
+  printf ("  INSN_CODE (ins1) = %d;\n",
+         insn_code_number);
+
+  /* Delete the remaining insns.  */
+  if (ninsns > 1)
+    printf ("  delete_for_peephole (NEXT_INSN (ins1), insn);\n");
+
+  /* See reload1.c for insertion of NOTE which guarantees that this
+     cannot be zero.  */
+  printf ("  return NEXT_INSN (insn);\n");
+
+  printf (" L%d:\n\n", insn_code_number);
+}
+\f
+void
+match_rtx (x, path, fail_label)
+     rtx x;
+     struct link *path;
+     int fail_label;
+{
+  register RTX_CODE code;
+  register int i;
+  register int len;
+  register char *fmt;
+  struct link link;
+
+  if (x == 0)
+    return;
+
+
+  code = GET_CODE (x);
+
+  switch (code)
+    {
+    case MATCH_OPERAND:
+      if (XINT (x, 0) > max_opno)
+       max_opno = XINT (x, 0);
+      if (XINT (x, 0) >= n_operands)
+       n_operands = 1 + XINT (x, 0);
+
+      printf ("  x = ");
+      print_path (path);
+      printf (";\n");
+
+      printf ("  operands[%d] = x;\n", XINT (x, 0));
+      if (XSTR (x, 1) && XSTR (x, 1)[0])
+       printf ("  if (! %s (x, %smode)) goto L%d;\n",
+               XSTR (x, 1), GET_MODE_NAME (GET_MODE (x)), fail_label);
+      return;
+
+    case MATCH_DUP:
+      printf ("  x = ");
+      print_path (path);
+      printf (";\n");
+
+      printf ("  if (!rtx_equal_p (operands[%d], x)) goto L%d;\n",
+             XINT (x, 0), fail_label);
+      return;
+
+    case MATCH_OPERATOR:
+      if (XINT (x, 0) > max_opno)
+       max_opno = XINT (x, 0);
+      if (XINT (x, 0) >= n_operands)
+       n_operands = 1 + XINT (x, 0);
+
+      printf ("  x = (rtx)");
+      print_path (path);
+      printf (";\n");
+
+      printf ("  operands[%d] = x;\n", XINT (x, 0));
+      if (XSTR (x, 1) && XSTR (x, 1)[0])
+       printf ("  if (! %s (x, %smode)) goto L%d;\n",
+               XSTR (x, 1), GET_MODE_NAME (GET_MODE (x)), fail_label);
+      link.next = path;
+      link.vecelt = -1;
+      for (i = 0; i < XVECLEN (x, 2); i++)
+       {
+         link.pos = i;
+         match_rtx (XVECEXP (x, 2, i), &link, fail_label);
+       }
+      return;
+
+    case ADDRESS:
+      match_rtx (XEXP (x, 0), path, fail_label);
+      return;
+    }
+
+  printf ("  x = ");
+  print_path (path);
+  printf (";\n");
+
+  printf ("  if (GET_CODE (x) != ");
+  print_code (code);
+  printf (") goto L%d;\n", fail_label);
+
+  if (GET_MODE (x) != VOIDmode)
+    {
+      printf ("  if (GET_MODE (x) != %smode) goto L%d;\n",
+             GET_MODE_NAME (GET_MODE (x)), fail_label);
+    }
+
+  link.next = path;
+  link.vecelt = -1;
+  fmt = GET_RTX_FORMAT (code);
+  len = GET_RTX_LENGTH (code);
+  for (i = 0; i < len; i++)
+    {
+      link.pos = i;
+      if (fmt[i] == 'e' || fmt[i] == 'u')
+       match_rtx (XEXP (x, i), &link, fail_label);
+      else if (fmt[i] == 'E')
+       {
+         int j;
+         printf ("  if (XVECLEN (x, %d) != %d) goto L%d;\n",
+                 i, XVECLEN (x, i), fail_label);
+         for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+           {
+             link.vecelt = j;
+             match_rtx (XVECEXP (x, i, j), &link, fail_label);
+           }
+       }
+      else if (fmt[i] == 'i')
+       {
+         /* Make sure that at run time `x' is the RTX we want to test.  */
+         if (i != 0)
+           {
+             printf ("  x = ");
+             print_path (path);
+             printf (";\n");
+           }
+
+         printf ("  if (XINT (x, %d) != %d) goto L%d;\n",
+                 i, XINT (x, i), fail_label);
+       }
+      else if (fmt[i] == 's')
+       {
+         /* Make sure that at run time `x' is the RTX we want to test.  */
+         if (i != 0)
+           {
+             printf ("  x = ");
+             print_path (path);
+             printf (";\n");
+           }
+
+         printf ("  if (strcmp (XSTR (x, %d), \"%s\")) goto L%d;\n",
+                 i, XSTR (x, i), fail_label);
+       }
+    }
+}
+
+/* Given a PATH, representing a path down the instruction's
+   pattern from the root to a certain point, output code to
+   evaluate to the rtx at that point.  */
+
+void
+print_path (path)
+     struct link *path;
+{
+  if (path == 0)
+    printf ("pat");
+  else if (path->vecelt >= 0)
+    {
+      printf ("XVECEXP (");
+      print_path (path->next);
+      printf (", %d, %d)", path->pos, path->vecelt);
+    }
+  else
+    {
+      printf ("XEXP (");
+      print_path (path->next);
+      printf (", %d)", path->pos);
+    }
+}
+\f
+void
+print_code (code)
+     RTX_CODE code;
+{
+  register char *p1;
+  for (p1 = GET_RTX_NAME (code); *p1; p1++)
+    {
+      if (*p1 >= 'a' && *p1 <= 'z')
+       putchar (*p1 + 'A' - 'a');
+      else
+       putchar (*p1);
+    }
+}
+\f
+int
+xmalloc (size)
+{
+  register int val = malloc (size);
+
+  if (val == 0)
+    fatal ("virtual memory exhausted");
+  return val;
+}
+
+int
+xrealloc (ptr, size)
+     char *ptr;
+     int size;
+{
+  int result = realloc (ptr, size);
+  if (!result)
+    fatal ("virtual memory exhausted");
+  return result;
+}
+
+void
+fatal (s, a1, a2)
+     char *s;
+{
+  fprintf (stderr, "genpeep: ");
+  fprintf (stderr, s, a1, a2);
+  fprintf (stderr, "\n");
+  exit (FATAL_EXIT_CODE);
+}
+
+/* More 'friendly' abort that prints the line and file.
+   config.h can #define abort fancy_abort if you like that sort of thing.  */
+
+void
+fancy_abort ()
+{
+  fatal ("Internal gcc abort.");
+}
+\f
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  rtx desc;
+  FILE *infile;
+  extern rtx read_rtx ();
+  register int c;
+
+  max_opno = -1;
+
+  obstack_init (rtl_obstack);
+
+  if (argc <= 1)
+    fatal ("No input file name.");
+
+  infile = fopen (argv[1], "r");
+  if (infile == 0)
+    {
+      perror (argv[1]);
+      exit (FATAL_EXIT_CODE);
+    }
+
+  init_rtl ();
+
+  printf ("/* Generated automatically by the program `genpeep'\n\
+from the machine description file `md'.  */\n\n");
+
+  printf ("#include \"config.h\"\n");
+  printf ("#include \"rtl.h\"\n");
+  printf ("#include \"regs.h\"\n");
+  printf ("#include \"real.h\"\n\n");
+
+  printf ("extern rtx peep_operand[];\n\n");
+  printf ("#define operands peep_operand\n\n");
+
+  printf ("rtx\npeephole (ins1)\n     rtx ins1;\n{\n");
+  printf ("  rtx insn, x, pat;\n");
+  printf ("  int i;\n\n");
+
+  /* Early out: no peepholes for insns followed by barriers.  */
+  printf ("  if (NEXT_INSN (ins1)\n");
+  printf ("      && GET_CODE (NEXT_INSN (ins1)) == BARRIER)\n");
+  printf ("    return 0;\n\n");
+
+  /* Read the machine description.  */
+
+  while (1)
+    {
+      c = read_skip_spaces (infile);
+      if (c == EOF)
+       break;
+      ungetc (c, infile);
+
+      desc = read_rtx (infile);
+      if (GET_CODE (desc) == DEFINE_PEEPHOLE)
+       {
+         gen_peephole (desc);
+         insn_code_number++;
+       }
+      if (GET_CODE (desc) == DEFINE_INSN || GET_CODE (desc) == DEFINE_EXPAND)
+       {
+         insn_code_number++;
+       }
+    }
+
+  printf ("  return 0;\n}\n\n");
+
+  if (max_opno == -1)
+    max_opno = 1;
+
+  printf ("rtx peep_operand[%d];\n", max_opno + 1);
+
+  fflush (stdout);
+  exit (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
+}
diff --git a/usr/src/usr.bin/gcc/cc1/genrecog.c b/usr/src/usr.bin/gcc/cc1/genrecog.c
new file mode 100644 (file)
index 0000000..be759ba
--- /dev/null
@@ -0,0 +1,1095 @@
+/* Generate code from machine description to emit insns as rtl.
+   Copyright (C) 1987,1988 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GNU CC 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 a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+
+/* This program is used to produce insn-recog.c, which contains
+   a function called `recog' plus its subroutines.
+   These functions contain a decision tree
+   that recognizes whether an rtx, the argument given to recog,
+   is a valid instruction.
+
+   recog returns -1 if the rtx is not valid.
+   If the rtx is valid, recog returns a nonnegative number
+   which is the insn code number for the pattern that matched.
+   This is the same as the order in the machine description of the
+   entry that matched.  This number can be used as an index into
+   insn_templates and insn_n_operands (found in insn-output.c)
+   or as an argument to output_insn_hairy (also in insn-output.c).  */
+
+#include <stdio.h>
+#include "config.h"
+#include "rtl.h"
+#include "obstack.h"
+
+struct obstack obstack;
+struct obstack *rtl_obstack = &obstack;
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+extern int xmalloc ();
+extern void free ();
+
+/* Data structure for decision tree for recognizing
+   legitimate instructions.  */
+
+struct decision
+{
+  int number;
+  char *position;
+  RTX_CODE code;
+  char *exact;
+  enum machine_mode mode;
+  char *tests;
+  int insn_code_number;
+  struct decision *next;
+  struct decision *success;
+  int opno;
+  int dupno;
+  int dupcount;
+  int test_elt_zero_int;
+  int elt_zero_int;
+  int test_elt_one_int;
+  int elt_one_int;
+  int ignmode;
+  struct decision *afterward;
+  int label_needed;
+  char *c_test;
+  char *reg_class;
+  char enforce_mode;
+  int veclen;
+  int subroutine_number;
+};
+
+#define SUBROUTINE_THRESHOLD 50
+
+int next_subroutine_number;
+
+/*
+recognize (top)
+{
+ staten:
+  x = XVECEXP (top, 0, 3);
+  if (test_code (GET_CODE (x))
+      && test_mode (MODE (x))
+      && whatever_else)
+    goto statep;
+  else if (next one...)
+    goto statem:
+  goto stater;
+
+ statep:
+  actions...;
+  return 1;
+
+ statem:
+  x = stack[depth--];
+  more tests...;
+
+ stateq:
+  stack[++depth] = x;
+  x = XEXP (stack[depth], 0);
+  more tests...;
+
+ stater:
+  x = XEXP (stack[depth], 1);
+}
+
+*/
+
+int next_number;
+
+int next_insn_code;
+
+/* Number of MATCH_DUP's seen so far in this instruction.  */
+int dupcount;
+
+struct decision *add_to_sequence ();
+struct decision *try_merge_2 ();
+void write_subroutine ();
+void print_code ();
+void clear_codes ();
+void clear_modes ();
+void change_state ();
+void write_tree ();
+char *copystr ();
+char *concat ();
+void fatal ();
+void fancy_abort ();
+void mybzero ();
+\f
+struct decision *first;
+
+/* Construct and return a sequence of decisions
+   that will recognize INSN.  */
+
+struct decision *
+make_insn_sequence (insn)
+     rtx insn;
+{
+  rtx x;
+  char *c_test = XSTR (insn, 2);
+  struct decision *last;
+
+  dupcount = 0;
+
+  if (XVECLEN (insn, 1) == 1)
+    x = XVECEXP (insn, 1, 0);
+  else
+    {
+      x = rtx_alloc (PARALLEL);
+      XVEC (x, 0) = XVEC (insn, 1);
+      PUT_MODE (x, VOIDmode);
+    }
+
+  last = add_to_sequence (x, 0, "");
+
+  if (c_test[0])
+    last->c_test = c_test;
+  last->insn_code_number = next_insn_code++;
+
+  return first;
+}
+
+struct decision *
+add_to_sequence (pattern, last, position)
+     rtx pattern;
+     struct decision *last;
+     char *position;
+{
+  register RTX_CODE code;
+  register struct decision *new
+    = (struct decision *) xmalloc (sizeof (struct decision));
+  struct decision *this;
+  char *newpos;
+  register char *fmt;
+  register int i;
+  int depth;
+  int len;
+
+  new->number = next_number++;
+  new->position = copystr (position);
+  new->exact = 0;
+  new->next = 0;
+  new->success = 0;
+  new->insn_code_number = -1;
+  new->tests = 0;
+  new->opno = -1;
+  new->dupno = -1;
+  new->dupcount = -1;
+  new->test_elt_zero_int = 0;
+  new->test_elt_one_int = 0;
+  new->elt_zero_int = 0;
+  new->elt_one_int = 0;
+  new->enforce_mode = 0;
+  new->ignmode = 0;
+  new->afterward = 0;
+  new->label_needed = 0;
+  new->c_test = 0;
+  new->reg_class = 0;
+  new->veclen = 0;
+  new->subroutine_number = 0;
+
+  this = new;
+
+  if (last == 0)
+    first = new;
+  else
+    last->success = new;
+
+  depth = strlen (position);
+  newpos = (char *) alloca (depth + 2);
+  strcpy (newpos, position);
+  newpos[depth + 1] = 0;
+
+ restart:
+
+  if (pattern == 0)
+    {
+      new->exact = "0";
+      new->code = UNKNOWN;
+      new->mode = VOIDmode;
+      return new;
+    }
+
+  switch (GET_MODE (pattern))
+    {
+    case 0:
+      new->mode = VOIDmode;
+      break;
+
+    default:
+      new->mode = GET_MODE (pattern);
+      break;
+    }
+
+  new->code = code = GET_CODE (pattern);
+
+  switch (code)
+    {
+    case MATCH_OPERAND:
+      new->opno = XINT (pattern, 0);
+      new->code = UNKNOWN;
+      new->tests = XSTR (pattern, 1);
+      if (*new->tests == 0)
+       new->tests = 0;
+      new->reg_class = XSTR (pattern, 2);
+      if (*new->reg_class == 0)
+       new->reg_class = 0;
+      return new;
+
+    case MATCH_OPERATOR:
+      new->opno = XINT (pattern, 0);
+      new->code = UNKNOWN;
+      new->tests = XSTR (pattern, 1);
+      if (*new->tests == 0)
+       new->tests = 0;
+      for (i = 0; i < XVECLEN (pattern, 2); i++)
+       {
+         newpos[depth] = i + '0';
+         new = add_to_sequence (XVECEXP (pattern, 2, i), new, newpos);
+       }
+      this->success->enforce_mode = 0;
+      return new;
+
+    case MATCH_DUP:
+      new->dupno = XINT (pattern, 0);
+      new->dupcount = dupcount++;
+      new->code = UNKNOWN;
+      return new;
+
+    case ADDRESS:
+      pattern = XEXP (pattern, 0);
+      goto restart;
+
+    case PC:
+      new->exact = "pc_rtx";
+      return new;
+
+    case CC0:
+      new->exact = "cc0_rtx";
+      return new;
+
+    case CONST_INT:
+      if (INTVAL (pattern) == 0)
+       {
+         new->exact = "const0_rtx";
+         return new;
+       }
+      if (INTVAL (pattern) == 1)
+       {
+         new->exact = "const1_rtx";
+         return new;
+       }
+      break;
+
+    case SET:
+      newpos[depth] = '0';
+      new = add_to_sequence (SET_DEST (pattern), new, newpos);
+      this->success->enforce_mode = 1;
+      newpos[depth] = '1';
+      new = add_to_sequence (SET_SRC (pattern), new, newpos);
+      return new;
+
+    case STRICT_LOW_PART:
+      newpos[depth] = '0';
+      new = add_to_sequence (XEXP (pattern, 0), new, newpos);
+      this->success->enforce_mode = 1;
+      return new;
+
+    case SUBREG:
+      this->test_elt_one_int = 1;
+      this->elt_one_int = XINT (pattern, 1);
+      newpos[depth] = '0';
+      new = add_to_sequence (XEXP (pattern, 0), new, newpos);
+      this->success->enforce_mode = 1;
+      return new;
+
+    case ZERO_EXTRACT:
+    case SIGN_EXTRACT:
+      newpos[depth] = '0';
+      new = add_to_sequence (XEXP (pattern, 0), new, newpos);
+      this->success->enforce_mode = 1;
+      newpos[depth] = '1';
+      new = add_to_sequence (XEXP (pattern, 1), new, newpos);
+      newpos[depth] = '2';
+      new = add_to_sequence (XEXP (pattern, 2), new, newpos);
+      return new;
+    }
+
+  fmt = GET_RTX_FORMAT (code);
+  len = GET_RTX_LENGTH (code);
+  for (i = 0; i < len; i++)
+    {
+      newpos[depth] = '0' + i;
+      if (fmt[i] == 'e' || fmt[i] == 'u')
+       new = add_to_sequence (XEXP (pattern, i), new, newpos);
+      else if (fmt[i] == 'i' && i == 0)
+       {
+         this->test_elt_zero_int = 1;
+         this->elt_zero_int = XINT (pattern, i);
+       }
+      else if (fmt[i] == 'i' && i == 1)
+       {
+         this->test_elt_one_int = 1;
+         this->elt_one_int = XINT (pattern, i);
+       }
+      else if (fmt[i] == 'E')
+       {
+         register int j;
+         /* We do not handle a vector appearing as other than
+            the first item, just because nothing uses them
+            and by handling only the special case
+            we can use one element in newpos for either
+            the item number of a subexpression
+            or the element number in a vector.  */
+         if (i != 0)
+           abort ();
+         this->veclen = XVECLEN (pattern, i);
+         for (j = 0; j < XVECLEN (pattern, i); j++)
+           {
+             newpos[depth] = 'a' + j;
+             new = add_to_sequence (XVECEXP (pattern, i, j),
+                                    new, newpos);
+           }
+       }
+      else if (fmt[i] != '0')
+       abort ();
+    }
+  return new;
+}
+
+/* Merge two decision trees OLD and ADD,
+   modifying OLD destructively,
+   and return the merged tree.  */
+
+struct decision *
+merge_trees (old, add)
+     register struct decision *old, *add;
+{
+  while (add)
+    {
+      register struct decision *next = add->next;
+      add->next = 0;
+      if (!try_merge_1 (old, add))
+       old = try_merge_2 (old, add);
+      add = next;
+    }
+  return old;
+}
+
+/* Merge ADD into the next-chain starting with OLD
+   only if it overlaps a condition already tested in OLD.
+   Returns 1 if successful (OLD is modified),
+   0 if nothing has been done.  */
+
+int
+try_merge_1 (old, add)
+     register struct decision *old, *add;
+{
+  while (old)
+    {
+      if ((old->position == add->position
+          || (old->position && add->position
+              && !strcmp (old->position, add->position)))
+         && (old->tests == add->tests
+             || (old->tests && add->tests && !strcmp (old->tests, add->tests)))
+         && (old->c_test == add->c_test
+             || (old->c_test && add->c_test && !strcmp (old->c_test, add->c_test)))
+         && old->test_elt_zero_int == add->test_elt_zero_int
+         && old->elt_zero_int == add->elt_zero_int
+         && old->test_elt_one_int == add->test_elt_one_int
+         && old->elt_one_int == add->elt_one_int
+         && old->veclen == add->veclen
+         && old->dupno == add->dupno
+         && old->opno == add->opno
+         && (old->tests == 0
+             || (add->enforce_mode ? no_same_mode (old) : old->next == 0))
+         && old->code == add->code
+         && old->mode == add->mode)
+       {
+         old->success = merge_trees (old->success, add->success);
+         if (old->insn_code_number >= 0 && add->insn_code_number >= 0)
+           fatal ("Two actions at one point in tree.");
+         if (old->insn_code_number == -1)
+           old->insn_code_number = add->insn_code_number;
+         return 1;
+       }
+      old = old->next;
+    }
+  return 0;
+}
+
+/* Merge ADD into the next-chain that starts with OLD,
+   preferably after something that tests the same place
+   that ADD does.
+   The next-chain of ADD itself is ignored, and it is set
+   up for entering ADD into the new chain.
+   Returns the new chain.  */
+
+struct decision *
+try_merge_2 (old, add)
+     struct decision *old, *add;
+{
+  register struct decision *p;
+  struct decision *last = 0;
+  struct decision *last_same_place = 0;
+
+  /* Put this in after the others that test the same place,
+     if there are any.  If not, find the last chain element
+     and insert there.
+
+     One modification: if this one is NOT a MATCH_OPERAND,
+     put it before any MATCH_OPERANDS that test the same place.
+
+     Another: if enforce_mode (i.e. this is first operand of a SET),
+     put this after the last thing that tests the same place for
+     the same mode.  */
+
+  int operand = 0 != add->tests;
+
+  for (p = old; p; p = p->next)
+    {
+      if (p->position == add->position
+         || (p->position && add->position
+             && !strcmp (p->position, add->position)))
+       {
+         last_same_place = p;
+         /* If enforce_mode, segregate the modes in numerical order.  */
+         if (p->enforce_mode && (int) add->mode < (int) p->mode)
+           break;
+#if 0
+         /* Keep explicit decompositions before those that test predicates.
+            If enforce_mode, do this separately within each mode.  */
+         if (! p->enforce_mode || p->mode == add->mode)
+           if (!operand && p->tests)
+             break;
+#endif
+       }
+      /* If this is past the end of the decisions at the same place as ADD,
+        stop looking now; add ADD before here.  */
+      else if (last_same_place)
+       break;
+      last = p;
+    }
+
+  /* Insert before P, which means after LAST.  */
+
+  if (last)
+    {
+      add->next = last->next;
+      last->next = add;
+      return old;
+    }
+
+  add->next = old;
+  return add;
+}
+
+int
+no_same_mode (node)
+     struct decision *node;
+{
+  register struct decision *p;
+  register enum machine_mode mode = node->mode;
+
+  for (p = node->next; p; p = p->next)
+    if (p->mode == mode)
+      return 0;
+
+  return 1;
+}
+\f
+/* Count the number of subnodes of node NODE, assumed to be the start
+   of a next-chain.  If the number is high enough, make NODE start
+   a separate subroutine in the C code that is generated.  */
+
+int
+break_out_subroutines (node)
+     struct decision *node;
+{
+  int size = 0;
+  struct decision *sub;
+  for (sub = node; sub; sub = sub->next)
+    size += 1 + break_out_subroutines (sub->success);
+  if (size > SUBROUTINE_THRESHOLD)
+    {
+      node->subroutine_number = ++next_subroutine_number;
+      write_subroutine (node);
+      size = 1;
+    }
+  return size;
+}
+
+void
+write_subroutine (tree)
+     struct decision *tree;
+{
+  printf ("int\nrecog_%d (x0, insn)\n     register rtx x0;\n     rtx insn;\n{\n",
+         tree->subroutine_number);
+  printf ("  register rtx x1, x2, x3, x4, x5;\n  rtx x6, x7, x8, x9, x10, x11;\n");
+  printf ("  int tem;\n");
+  write_tree (tree, "", 0, "", 1);
+  printf (" ret0: return -1;\n}\n\n");
+}
+\f
+/* Write out C code to perform the decisions in the tree.  */
+
+void
+write_tree (tree, prevpos, afterward, afterpos, initial)
+     struct decision *tree;
+     char *prevpos;
+     int afterward;
+     char *afterpos;
+     int initial;
+{
+  register struct decision *p, *p1;
+  char *pos;
+  register int depth;
+  int ignmode;
+  enum anon1 { NO_SWITCH, CODE_SWITCH, MODE_SWITCH } in_switch = NO_SWITCH;
+  char modemap[NUM_MACHINE_MODES];
+  char codemap[NUM_RTX_CODE];
+
+  pos = prevpos;
+
+  if (tree->subroutine_number > 0 && ! initial)
+    {
+      printf (" L%d:\n", tree->number);
+
+      if (afterward)
+       {
+         printf ("  tem = recog_%d (x0, insn);\n",
+                 tree->subroutine_number);
+         printf ("  if (tem >= 0) return tem;\n");
+         change_state (pos, afterpos);
+         printf ("  goto L%d;\n", afterward);
+       }
+      else
+       printf ("  return recog_%d (x0, insn);\n",
+               tree->subroutine_number);
+      return;
+    }
+
+  tree->label_needed = 1;
+  for (p = tree; p; p = p->next)
+    {
+      /* Find the next alternative to p
+        that might be true when p is true.
+        Test that one next if p's successors fail.
+        Note that when the `tests' field is nonzero
+        it is up to the specified test-function to compare machine modes
+        and some (such as general_operand) don't always do so.
+        But when inside a switch-on-modes we ignore this and
+        consider all modes mutually exclusive.  */
+      for (p1 = p->next; p1; p1 = p1->next)
+       if (((p->code == UNKNOWN || p1->code == UNKNOWN || p->code == p1->code)
+            && (p->mode == VOIDmode || p1->mode == VOIDmode
+                || p->mode == p1->mode
+                || (in_switch != MODE_SWITCH && (p->tests || p1->tests))))
+           || strcmp (p1->position, p->position))
+         break;
+      p->afterward = p1;
+      if (p1) p1->label_needed = 1;
+
+      if (in_switch == MODE_SWITCH
+         && (p->mode == VOIDmode || (! p->enforce_mode && p->tests != 0)))
+       {
+         in_switch = NO_SWITCH;
+         printf ("  }\n");
+       }
+      if (in_switch == CODE_SWITCH && p->code == UNKNOWN)
+       {
+         in_switch = NO_SWITCH;
+         printf ("  }\n");
+       }
+
+      if (p->label_needed)
+       printf (" L%d:\n", p->number);
+
+      if (p->success == 0 && p->insn_code_number < 0)
+       abort ();
+
+      change_state (pos, p->position);
+      pos = p->position;
+      depth = strlen (pos);
+
+      ignmode = p->ignmode || pos[depth - 1] == '*' || p->tests;
+
+      if (in_switch == NO_SWITCH)
+       {
+         /* If p and its alternatives all want the same mode,
+            reject all others at once, first, then ignore the mode.  */
+         if (!ignmode && p->mode != VOIDmode && p->next && same_modes (p, p->mode))
+           {
+             printf ("  if (GET_MODE (x%d) != %smode)\n",
+                     depth, GET_MODE_NAME (p->mode));
+             if (afterward)
+               {
+                 printf ("    {\n    ");
+                 change_state (pos, afterpos);
+                 printf ("      goto L%d;\n    }\n", afterward);
+               }
+             else
+               printf ("    goto ret0;\n");
+             clear_modes (p);
+             ignmode = 1;
+           }
+
+         /* If p and its alternatives all want the same code,
+            reject all others at once, first, then ignore the code.  */
+         if (p->code != UNKNOWN && p->next && same_codes (p, p->code))
+           {
+             printf ("  if (GET_CODE (x%d) != ", depth);
+             print_code (p->code);
+             printf (")\n");
+             if (afterward)
+               {
+                 printf ("    {");
+                 change_state (pos, afterpos);
+                 printf ("    goto L%d; }\n", afterward);
+               }
+             else
+               printf ("    goto ret0;\n");
+             clear_codes (p);
+           }
+       }
+
+      /* If p and its alternatives all have different modes
+        and there are at least 4 of them, make a switch.  */
+      if (in_switch == NO_SWITCH && pos[depth-1] != '*')
+       {
+         register int i;
+         int lose = 0;
+
+         mybzero (modemap, sizeof modemap);
+         for (p1 = p, i = 0;
+              (p1 && p1->mode != VOIDmode
+               && (p1->tests == 0 || p1->enforce_mode));
+              p1 = p1->next, i++)
+           {
+             if (! p->enforce_mode && modemap[(int) p1->mode])
+               {
+                 lose = 1;
+                 break;
+               }
+             modemap[(int) p1->mode] = 1;
+           }
+         if (!lose && i >= 4)
+           {
+             in_switch = MODE_SWITCH;
+             printf (" switch (GET_MODE (x%d))\n  {\n", depth);
+           }
+       }
+
+      if (in_switch == NO_SWITCH)
+       {
+         register int i;
+         mybzero (codemap, sizeof codemap);
+         for (p1 = p, i = 0; p1 && p1->code != UNKNOWN; p1 = p1->next, i++)
+           {
+             if (codemap[(int) p1->code])
+               break;
+             codemap[(int) p1->code] = 1;
+           }
+         if ((p1 == 0 || p1->code == UNKNOWN) && i >= 4)
+           {
+             in_switch = CODE_SWITCH;
+             printf (" switch (GET_CODE (x%d))\n  {\n", depth);
+           }
+       }
+
+      if (in_switch == MODE_SWITCH)
+       {
+         if (modemap[(int) p->mode])
+           {
+             printf ("  case %smode:\n", GET_MODE_NAME (p->mode));
+             modemap[(int) p->mode] = 0;
+           }
+       }
+      if (in_switch == CODE_SWITCH)
+       {
+         if (codemap[(int) p->code])
+           {
+             printf ("  case ");
+             print_code (p->code);
+             printf (":\n");
+             codemap[(int) p->code] = 0;
+           }
+       }
+
+      printf ("  if (");
+      if (p->exact || (p->code != UNKNOWN && in_switch != CODE_SWITCH))
+       {
+         if (p->exact)
+           printf ("x%d == %s", depth, p->exact);
+         else
+           {
+             printf ("GET_CODE (x%d) == ", depth);
+             print_code (p->code);
+           }
+         printf (" && ");
+       }
+      if (p->mode != VOIDmode && !ignmode && in_switch != MODE_SWITCH)
+       printf ("GET_MODE (x%d) == %smode && ",
+               depth, GET_MODE_NAME (p->mode));
+      if (p->test_elt_zero_int)
+       printf ("XINT (x%d, 0) == %d && ", depth, p->elt_zero_int);
+      if (p->veclen)
+       printf ("XVECLEN (x%d, 0) == %d && ", depth, p->veclen);
+      if (p->test_elt_one_int)
+       printf ("XINT (x%d, 1) == %d && ", depth, p->elt_one_int);
+      if (p->dupno >= 0)
+       printf ("rtx_equal_p (x%d, recog_operand[%d]) && ", depth, p->dupno);
+      if (p->tests)
+       printf ("%s (x%d, %smode)", p->tests, depth,
+               GET_MODE_NAME (p->mode));
+      else
+       printf ("1");
+
+      if (p->opno >= 0)
+       printf (")\n    { recog_operand[%d] = x%d; ",
+               p->opno, depth);
+      else
+       printf (")\n    ");
+
+      if (p->c_test)
+       printf ("if (%s) ", p->c_test);
+
+      if (p->insn_code_number >= 0)
+       printf ("return %d;", p->insn_code_number);
+      else
+       printf ("goto L%d;", p->success->number);
+
+      if (p->opno >= 0)
+       printf (" }\n");
+      else
+       printf ("\n");
+
+      /* Now, if inside a switch, branch to next switch member
+        that might also need to be tested if this one fails.  */
+
+      if (in_switch == CODE_SWITCH)
+       {
+         /* Find the next alternative to p
+            that might be applicable if p was applicable.  */
+         for (p1 = p->next; p1; p1 = p1->next)
+           if (p1->code == UNKNOWN || p->code == p1->code)
+             break;
+         if (p1 == 0 || p1->code == UNKNOWN)
+           printf ("  break;\n");
+         else if (p1 != p->next)
+           {
+             printf (" goto L%d;\n", p1->number);
+             p1->label_needed = 1;
+           }
+       }
+
+      if (in_switch == MODE_SWITCH)
+       {
+         /* Find the next alternative to p
+            that might be applicable if p was applicable.  */
+         for (p1 = p->next; p1; p1 = p1->next)
+           if (p1->mode == VOIDmode || p->mode == p1->mode)
+             break;
+         if (p1 == 0 || p1->mode == VOIDmode)
+           printf ("  break;\n");
+         else if (p1 != p->next)
+           {
+             printf (" goto L%d;\n", p1->number);
+             p1->label_needed = 1;
+           }
+       }
+    }
+
+  if (in_switch != NO_SWITCH)
+    printf ("  }\n");
+
+  if (afterward)
+    {
+      change_state (pos, afterpos);
+      printf ("  goto L%d;\n", afterward);
+    }
+  else
+    printf ("  goto ret0;\n");
+
+  for (p = tree; p; p = p->next)
+    if (p->success)
+      {
+         {
+           pos = p->position;
+           write_tree (p->success, pos,
+                       p->afterward ? p->afterward->number : afterward,
+                       p->afterward ? pos : afterpos,
+                       0);
+         }
+      }
+}
+
+void
+print_code (code)
+     RTX_CODE code;
+{
+  register char *p1;
+  for (p1 = GET_RTX_NAME (code); *p1; p1++)
+    {
+      if (*p1 >= 'a' && *p1 <= 'z')
+       putchar (*p1 + 'A' - 'a');
+      else
+       putchar (*p1);
+    }
+}
+
+int
+same_codes (p, code)
+     register struct decision *p;
+     register RTX_CODE code;
+{
+  for (; p; p = p->next)
+    if (p->code != code)
+      return 0;
+
+  return 1;
+}
+
+void
+clear_codes (p)
+     register struct decision *p;
+{
+  for (; p; p = p->next)
+    p->code = UNKNOWN;
+}
+
+int
+same_modes (p, mode)
+     register struct decision *p;
+     register enum machine_mode mode;
+{
+  for (; p; p = p->next)
+    if (p->mode != mode || p->tests)
+      return 0;
+
+  return 1;
+}
+
+void
+clear_modes (p)
+     register struct decision *p;
+{
+  for (; p; p = p->next)
+    p->ignmode = 1;
+}
+\f
+void
+change_state (oldpos, newpos)
+     char *oldpos;
+     char *newpos;
+{
+  int odepth = strlen (oldpos);
+  int depth = odepth;
+  int ndepth = strlen (newpos);
+
+  /* Pop up as many levels as necessary.  */
+
+  while (strncmp (oldpos, newpos, depth))
+    --depth;
+
+  /* Go down to desired level.  */
+
+  while (depth < ndepth)
+    {
+      if (newpos[depth] == '*')
+       printf ("  x%d = recog_addr_dummy;\n  XEXP (x%d, 0) = x%d;\n",
+               depth + 1, depth + 1, depth);
+      else if (newpos[depth] >= 'a' && newpos[depth] <= 'z')
+       printf ("  x%d = XVECEXP (x%d, 0, %d);\n",
+               depth + 1, depth, newpos[depth] - 'a');
+      else
+       printf ("  x%d = XEXP (x%d, %c);\n",
+               depth + 1, depth, newpos[depth]);
+      ++depth;
+    }
+}
+\f
+char *
+copystr (s1)
+     char *s1;
+{
+  register char *tem;
+
+  if (s1 == 0)
+    return 0;
+
+  tem = (char *) xmalloc (strlen (s1) + 1);
+  strcpy (tem, s1);
+
+  return tem;
+}
+
+void
+mybzero (b, length)
+     register char *b;
+     register int length;
+{
+  while (length-- > 0)
+    *b++ = 0;
+}
+
+char *
+concat (s1, s2)
+     char *s1, *s2;
+{
+  register char *tem;
+
+  if (s1 == 0)
+    return s2;
+  if (s2 == 0)
+    return s1;
+
+  tem = (char *) xmalloc (strlen (s1) + strlen (s2) + 2);
+  strcpy (tem, s1);
+  strcat (tem, " ");
+  strcat (tem, s2);
+
+  return tem;
+}
+
+int
+xrealloc (ptr, size)
+     char *ptr;
+     int size;
+{
+  int result = realloc (ptr, size);
+  if (!result)
+    fatal ("virtual memory exhausted");
+  return result;
+}
+
+int
+xmalloc (size)
+{
+  register int val = malloc (size);
+
+  if (val == 0)
+    fatal ("virtual memory exhausted");
+  return val;
+}
+
+void
+fatal (s, a1, a2)
+     char *s;
+{
+  fprintf (stderr, "genrecog: ");
+  fprintf (stderr, s, a1, a2);
+  fprintf (stderr, "\n");
+  fprintf (stderr, "after %d instruction definitions\n",
+          next_insn_code);
+  exit (FATAL_EXIT_CODE);
+}
+
+/* More 'friendly' abort that prints the line and file.
+   config.h can #define abort fancy_abort if you like that sort of thing.  */
+
+void
+fancy_abort ()
+{
+  fatal ("Internal gcc abort.");
+}
+\f
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  rtx desc;
+  struct decision *tree = 0;
+  FILE *infile;
+  extern rtx read_rtx ();
+  register int c;
+
+  obstack_init (rtl_obstack);
+
+  if (argc <= 1)
+    fatal ("No input file name.");
+
+  infile = fopen (argv[1], "r");
+  if (infile == 0)
+    {
+      perror (argv[1]);
+      exit (FATAL_EXIT_CODE);
+    }
+
+  init_rtl ();
+  next_insn_code = 0;
+
+  printf ("/* Generated automatically by the program `genrecog'\n\
+from the machine description file `md'.  */\n\n");
+
+  /* Read the machine description.  */
+
+  while (1)
+    {
+      c = read_skip_spaces (infile);
+      if (c == EOF)
+       break;
+      ungetc (c, infile);
+
+      desc = read_rtx (infile);
+      if (GET_CODE (desc) == DEFINE_INSN)
+       tree = merge_trees (tree, make_insn_sequence (desc));
+      if (GET_CODE (desc) == DEFINE_PEEPHOLE
+         || GET_CODE (desc) == DEFINE_EXPAND)
+       next_insn_code++;
+    }
+
+  printf ("#include \"config.h\"\n");
+  printf ("#include \"rtl.h\"\n");
+  printf ("#include \"insn-config.h\"\n");
+  printf ("#include \"recog.h\"\n");
+  printf ("#include \"real.h\"\n");
+  printf ("\n\
+/* `recog' contains a decision tree\n\
+   that recognizes whether the rtx X0 is a valid instruction.\n\
+\n\
+   recog returns -1 if the rtx is not valid.\n\
+   If the rtx is valid, recog returns a nonnegative number\n\
+   which is the insn code number for the pattern that matched.\n");
+  printf ("   This is the same as the order in the machine description of\n\
+   the entry that matched.  This number can be used as an index into\n\
+   insn_templates and insn_n_operands (found in insn-output.c)\n\
+   or as an argument to output_insn_hairy (also in insn-output.c).  */\n\n");
+
+  printf ("rtx recog_operand[MAX_RECOG_OPERANDS];\n\n");
+  printf ("rtx *recog_operand_loc[MAX_RECOG_OPERANDS];\n\n");
+  printf ("rtx *recog_dup_loc[MAX_DUP_OPERANDS];\n\n");
+  printf ("char recog_dup_num[MAX_DUP_OPERANDS];\n\n");
+  printf ("extern rtx recog_addr_dummy;\n\n");
+  printf ("#define operands recog_operand\n\n");
+
+  break_out_subroutines (tree);
+
+  printf ("int\nrecog (x0, insn)\n     register rtx x0;\n     rtx insn;\n{\n");
+  printf ("  register rtx x1, x2, x3, x4, x5;\n  rtx x6, x7, x8, x9, x10, x11;\n");
+  printf ("  int tem;\n");
+
+  write_tree (tree, "", 0, "", 1);
+  printf (" ret0: return -1;\n}\n");
+
+  fflush (stdout);
+  exit (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
+}