OSDN Git Service

Initial revision
authorkenner <kenner@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 24 Nov 1991 19:57:16 +0000 (19:57 +0000)
committerkenner <kenner@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 24 Nov 1991 19:57:16 +0000 (19:57 +0000)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@71 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/genrecog.c [new file with mode: 0644]

diff --git a/gcc/genrecog.c b/gcc/genrecog.c
new file mode 100644 (file)
index 0000000..db4bb41
--- /dev/null
@@ -0,0 +1,1339 @@
+/* Generate code from machine description to recognize rtl as insns.
+   Copyright (C) 1987-1991 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 2, 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 various
+   insn_* tables, such as insn_templates, insn_outfun, and insn_n_operands
+   (found in insn-output.c).
+
+   The third argument to recog is an optional pointer to an int.
+   If present, recog will accept a pattern if it matches except for
+   missing CLOBBER expressions at the end.  In that case, the value
+   pointed to by the optional pointer will be set to the number of
+   CLOBBERs that need to be added (it should be initialized to zero by
+   the caller).  If it is set nonzero, the caller should allocate a
+   PARALLEL of the appropriate size, copy the initial entries, and call
+   add_clobbers (found in insn-emit.c) to fill in the CLOBBERs.
+
+   This program also generates the function `split_insns',
+   which returns 0 if the rtl could not be split, or
+   it returns the split rtl in a SEQUENCE.  */
+
+#include <stdio.h>
+#include "config.h"
+#include "rtl.h"
+#include "obstack.h"
+
+static struct obstack obstack;
+struct obstack *rtl_obstack = &obstack;
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+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;
+  int num_clobbers_to_add;
+  struct decision *next;
+  struct decision *success;
+  int opno;
+  int dupno;
+  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 enforce_mode;
+  int veclen;
+  int subroutine_number;
+  /* Used for DEFINE_SPLITs.  */
+  char *c_hook;
+  rtx split_sequence;
+};
+
+#define SUBROUTINE_THRESHOLD 50
+
+static int next_subroutine_number;
+
+/* We can write two types of subroutines: One for insn recognition and
+   one to split insns.  This defines which type is being written.  */
+
+enum routine_type {RECOG, SPLIT};
+
+static int try_merge_1 ();
+static int no_same_mode ();
+static int same_codes ();
+static int same_modes ();
+
+/*
+static int
+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);
+}
+
+*/
+
+static int next_number;
+
+static int next_insn_code;
+
+static int next_index;
+
+char *xmalloc ();
+static struct decision *add_to_sequence ();
+static struct decision *merge_trees ();
+static struct decision *try_merge_2 ();
+static void write_subroutine ();
+static void print_code ();
+static void clear_codes ();
+static void clear_modes ();
+static void change_state ();
+static void write_tree ();
+static char *copystr ();
+static char *concat ();
+static void fatal ();
+void fancy_abort ();
+static void mybzero ();
+\f
+static struct decision *first;
+
+/* Construct and return a sequence of decisions
+   that will recognize INSN.  */
+
+static struct decision *
+make_insn_sequence (insn)
+     rtx insn;
+{
+  rtx x;
+  char *c_test = XSTR (insn, 2);
+  struct decision *last;
+
+  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;
+  last->num_clobbers_to_add = 0;
+
+  /* If X is a PARALLEL, see if it ends with a group of CLOBBERs of (hard)
+     registers or MATCH_SCRATCHes.  If so, set up to recognize the pattern
+     without these CLOBBERs.  */
+
+  if (GET_CODE (x) == PARALLEL)
+    {
+      int i;
+
+      for (i = XVECLEN (x, 0); i > 0; i--)
+       if (GET_CODE (XVECEXP (x, 0, i - 1)) != CLOBBER
+           || (GET_CODE (XEXP (XVECEXP (x, 0, i - 1), 0)) != REG
+               && GET_CODE (XEXP (XVECEXP (x, 0, i - 1), 0)) != MATCH_SCRATCH))
+         break;
+
+      if (i != XVECLEN (x, 0))
+       {
+         rtx new;
+         struct decision *previous_first = first;
+
+         if (i == 1)
+           new = XVECEXP (x, 0, 0);
+         else
+           {
+             int j;
+
+             new = rtx_alloc (PARALLEL);
+             XVEC (new, 0) = rtvec_alloc (i);
+             for (j = i - 1; j >= 0; j--)
+               XVECEXP (new, 0, j) = XVECEXP (x, 0, j);
+           }
+
+         last = add_to_sequence (new, 0, "");
+
+         if (c_test[0])
+           last->c_test = c_test;
+         last->insn_code_number = next_insn_code;
+         last->num_clobbers_to_add = XVECLEN (x, 0) - i;
+         first = merge_trees (previous_first, first);
+       }
+    }
+
+  next_insn_code++;
+  return first;
+}
+
+static struct decision *
+make_split_sequence (insn)
+     rtx insn;
+{
+  rtx x;
+  char *c_test = XSTR (insn, 1);
+  char *c_hook = XSTR (insn, 3);
+  struct decision *last;
+
+  if (XVECLEN (insn, 0) == 1)
+    x = XVECEXP (insn, 0, 0);
+  else
+    {
+      x = rtx_alloc (PARALLEL);
+      XVEC (x, 0) = XVEC (insn, 0);
+      PUT_MODE (x, VOIDmode);
+    }
+
+  last = add_to_sequence (x, 0, "");
+
+  if (c_test[0])
+    last->c_test = c_test;
+  if (c_hook != 0 && c_hook[0] != 0)
+    last->c_hook = c_hook;
+  last->split_sequence = XEXP (insn, 2);
+  last->insn_code_number = next_insn_code++;
+
+  /* Define the subroutine we will call below and emit in genemit.  */
+  printf ("extern rtx gen_split_%d ();\n", last->insn_code_number);
+
+  return first;
+}
+
+static 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->num_clobbers_to_add = 0;
+  new->tests = 0;
+  new->opno = -1;
+  new->dupno = -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->c_hook = 0;
+  new->split_sequence = 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;
+    }
+
+  new->mode = GET_MODE (pattern);
+  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;
+      return new;
+
+    case MATCH_SCRATCH:
+      new->opno = XINT (pattern, 0);
+      new->code = UNKNOWN;
+      new->tests = "scratch_operand";
+      if (*new->tests == 0)
+       new->tests = 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_PARALLEL:
+      new->opno = XINT (pattern, 0);
+      new->code = PARALLEL;
+      new->tests = XSTR (pattern, 1);
+      if (*new->tests == 0)
+       new->tests = 0;
+      for (i = 0; i < XVECLEN (pattern, 2); i++)
+       {
+         newpos[depth] = i + 'a';
+         new = add_to_sequence (XVECEXP (pattern, 2, i), new, newpos);
+       }
+      this->success->enforce_mode = 0;
+      return new;
+
+    case MATCH_OP_DUP:
+      new->opno = XINT (pattern, 0);
+      new->dupno = XINT (pattern, 0);
+      new->code = UNKNOWN;
+      new->tests = 0;
+      for (i = 0; i < XVECLEN (pattern, 1); i++)
+       {
+         newpos[depth] = i + '0';
+         new = add_to_sequence (XVECEXP (pattern, 1, i), new, newpos);
+       }
+      this->success->enforce_mode = 0;
+      return new;
+
+    case MATCH_DUP:
+      new->dupno = XINT (pattern, 0);
+      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;
+       }
+      if (INTVAL (pattern) == -1)
+       {
+         new->exact = "constm1_rtx";
+         return new;
+       }
+      if (INTVAL (pattern) == STORE_FLAG_VALUE)
+       {
+         new->exact = "const_true_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.  */
+
+static 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.  */
+
+static 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->c_hook == add->c_hook
+             || (old->c_hook && add->c_hook && !strcmp (old->c_hook, add->c_hook)))
+         && 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
+/* In a collection of nodes that don't have predicates,
+   we can always merge a new one with any node that matches it.
+   This is because we know that two different nodes can't possibly match
+   the same RTL object.  So we can reorder the tests to simplify the
+   whole collection of them.
+
+   But when predicates are involved, we have to preserve the order of
+   testing them.  This means that a new node can only be merged with the
+   last existing node.
+
+   enforce_mode indicates that at this level each of the nodes
+   requires a particular mode.  When this is true, then we know
+   that two nodes with different modes can't possibly both match.
+   Therefore, it is ok to merge a new node with the last node
+   that wants the same mode, even if other nodes for different modes
+   appear after it.  no_same_mode tests for this condition.  */
+         && (old->tests == 0
+             || (add->enforce_mode ? no_same_mode (old) : old->next == 0))
+         && old->code == add->code
+         && old->mode == add->mode
+         && (old->exact == add->exact
+             || (old->exact && add->exact && ! strcmp (old->exact, add->exact))))
+       {
+         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.  */
+
+static 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.  */
+
+#if 0
+  int operand = 0 != add->tests;
+#endif
+
+  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;
+}
+
+static 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.
+
+   TYPE gives the type of routine we are writing.  */
+
+static int
+break_out_subroutines (node, type)
+     struct decision *node;
+     enum routine_type type;
+{
+  int size = 0;
+  struct decision *sub;
+  for (sub = node; sub; sub = sub->next)
+    size += 1 + break_out_subroutines (sub->success, type);
+  if (size > SUBROUTINE_THRESHOLD)
+    {
+      node->subroutine_number = ++next_subroutine_number;
+      write_subroutine (node, type);
+      size = 1;
+    }
+  return size;
+}
+
+static void
+write_subroutine (tree, type)
+     struct decision *tree;
+     enum routine_type type;
+{
+  char *return_type = (type == SPLIT ? "rtx" : "int");
+
+  if (type == SPLIT)
+    {
+      printf ("rtx\nsplit_%d (x0, insn)\n", tree->subroutine_number);
+      printf ("     register rtx x0;\n     rtx insn;\n");
+    }
+  else
+    {
+      printf ("int\nrecog_%d (x0, insn, pnum_clobbers)\n",
+             tree->subroutine_number);
+      printf ("     register rtx x0;\n     rtx insn;\n");
+      printf ("     int *pnum_clobbers;\n");
+    }
+
+  printf ("{\n");
+  printf ("  register rtx *ro = &recog_operand[0];\n");
+  printf ("  register rtx x1, x2, x3, x4, x5;\n  rtx x6, x7, x8, x9, x10, x11;\n");
+  printf ("  %s tem;\n", return_type);
+  write_tree (tree, "", 0, "", 1, type);
+  printf (" ret0: return %d;\n}\n\n", type == SPLIT ? 0 : -1);
+}
+\f
+/* Write out C code to perform the decisions in the tree.  */
+
+static char *
+write_tree_1 (tree, prevpos, afterward, afterpos, initial, type)
+     struct decision *tree;
+     char *prevpos;
+     int afterward;
+     char *afterpos;
+     int initial;
+     enum routine_type type;
+{
+  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;
+
+  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 || 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)
+       {
+         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, ro[%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    { ro[%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)
+       {
+         if (type == SPLIT)
+           printf ("return gen_split_%d (operands);", p->insn_code_number);
+         else
+           {
+             if (p->num_clobbers_to_add)
+               {
+                 printf ("\n      {\n");
+                 printf ("\tif (pnum_clobbers == 0) goto ret0; ");
+                 printf ("*pnum_clobbers = %d; ", p->num_clobbers_to_add);
+                 printf ("return %d;\n      }", p->insn_code_number);
+               }
+             else
+               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");
+  return pos;
+}
+
+static void
+write_tree (tree, prevpos, afterward, afterpos, initial, type)
+     struct decision *tree;
+     char *prevpos;
+     int afterward;
+     char *afterpos;
+     int initial;
+     enum routine_type type;
+{
+  register struct decision *p;
+  char *pos = prevpos;
+  char *name_prefix = (type == SPLIT ? "split" : "recog");
+  char *call_suffix = (type == SPLIT ? "" : ", pnum_clobbers");
+
+  if (tree->subroutine_number > 0 && ! initial)
+    {
+      printf (" L%d:\n", tree->number);
+
+      if (afterward)
+       {
+         printf ("  tem = %s_%d (x0, insn%s);\n",
+                 name_prefix, tree->subroutine_number, call_suffix);
+         printf ("  if (tem >= 0) return tem;\n");
+         change_state (pos, afterpos);
+         printf ("  goto L%d;\n", afterward);
+       }
+      else
+       printf ("  return %s_%d (x0, insn%s);\n",
+               name_prefix, tree->subroutine_number, call_suffix);
+      return;
+    }
+
+  pos = write_tree_1 (tree, prevpos, afterward, afterpos, initial, type);
+
+  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, type);
+      }
+}
+
+static 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);
+    }
+}
+
+static 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;
+}
+
+static void
+clear_codes (p)
+     register struct decision *p;
+{
+  for (; p; p = p->next)
+    p->code = UNKNOWN;
+}
+
+static 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;
+}
+
+static void
+clear_modes (p)
+     register struct decision *p;
+{
+  for (; p; p = p->next)
+    p->ignmode = 1;
+}
+\f
+static 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] >= '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
+static char *
+copystr (s1)
+     char *s1;
+{
+  register char *tem;
+
+  if (s1 == 0)
+    return 0;
+
+  tem = (char *) xmalloc (strlen (s1) + 1);
+  strcpy (tem, s1);
+
+  return tem;
+}
+
+static void
+mybzero (b, length)
+     register char *b;
+     register unsigned length;
+{
+  while (length-- > 0)
+    *b++ = 0;
+}
+
+static 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;
+}
+
+char *
+xrealloc (ptr, size)
+     char *ptr;
+     unsigned size;
+{
+  char *result = (char *) realloc (ptr, size);
+  if (!result)
+    fatal ("virtual memory exhausted");
+  return result;
+}
+
+char *
+xmalloc (size)
+     unsigned size;
+{
+  register char *val = (char *) malloc (size);
+
+  if (val == 0)
+    fatal ("virtual memory exhausted");
+  return val;
+}
+
+static 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_index);
+  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;
+  struct decision *split_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;
+  next_index = 0;
+
+  printf ("/* Generated automatically by the program `genrecog'\n\
+from the machine description file `md'.  */\n\n");
+
+  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 ("#include \"output.h\"\n");
+  printf ("#include \"flags.h\"\n");
+  printf ("\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));
+      else if (GET_CODE (desc) == DEFINE_SPLIT)
+       split_tree = merge_trees (split_tree, make_split_sequence (desc));
+      if (GET_CODE (desc) == DEFINE_PEEPHOLE
+         || GET_CODE (desc) == DEFINE_EXPAND)
+       next_insn_code++;
+      next_index++;
+    }
+
+  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\
+   entry that matched.  This number can be used as an index into various\n\
+   insn_* tables, such as insn_templates, insn_outfun, and insn_n_operands\n\
+   (found in insn-output.c).\n\n");
+  printf ("   The third argument to recog is an optional pointer to an int.\n\
+   If present, recog will accept a pattern if it matches except for\n\
+   missing CLOBBER expressions at the end.  In that case, the value\n\
+   pointed to by the optional pointer will be set to the number of\n\
+   CLOBBERs that need to be added (it should be initialized to zero by\n\
+   the caller).  If it is set nonzero, the caller should allocate a\n\
+   PARALLEL of the appropriate size, copy the initial entries, and call\n\
+   add_clobbers (found in insn-emit.c) to fill in the CLOBBERs.");
+
+  if (split_tree)
+    printf ("\n\n   The function split_insns returns 0 if the rtl could not\n\
+   be split or the split rtl in a SEQUENCE if it can be.");
+
+  printf ("*/\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 ("#define operands recog_operand\n\n");
+
+  next_subroutine_number = 0;
+  break_out_subroutines (tree, RECOG);
+
+  printf ("int\nrecog (x0, insn, pnum_clobbers)\n");
+  printf ("     register rtx x0;\n     rtx insn;\n");
+  printf ("     int *pnum_clobbers;\n{\n");
+  printf ("  register rtx *ro = &recog_operand[0];\n");
+  printf ("  register rtx x1, x2, x3, x4, x5;\n  rtx x6, x7, x8, x9, x10, x11;\n");
+  printf ("  int tem;\n");
+
+  if (tree)
+    write_tree (tree, "", 0, "", 1, RECOG);
+  printf (" ret0: return -1;\n}\n");
+
+  next_subroutine_number = 0;
+  break_out_subroutines (split_tree, SPLIT);
+
+  printf ("rtx\nsplit_insns (x0, insn)\n     register rtx x0;\n     rtx insn;\n{\n");
+  printf ("  register rtx *ro = &recog_operand[0];\n");
+  printf ("  register rtx x1, x2, x3, x4, x5;\n  rtx x6, x7, x8, x9, x10, x11;\n");
+  printf ("  rtx tem;\n");
+
+  if (split_tree)
+    write_tree (split_tree, "", 0, "", 1, SPLIT);
+  printf (" ret0: return 0;\n}\n");
+
+  fflush (stdout);
+  exit (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
+  /* NOTREACHED */
+  return 0;
+}