OSDN Git Service

* tradcif.c: Remove.
[pf3gnuchains/gcc-fork.git] / gcc / genrecog.c
index 371e5b2..b955eb0 100644 (file)
@@ -1,5 +1,6 @@
 /* Generate code from machine description to recognize rtl as insns.
-   Copyright (C) 1987, 88, 92-95, 97-98, 1999 Free Software Foundation, Inc.
+   Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1997, 1998,
+   1999, 2000 Free Software Foundation, Inc.
 
    This file is part of GNU CC.
 
 #include "hconfig.h"
 #include "system.h"
 #include "rtl.h"
-#include "obstack.h"
 #include "errors.h"
+#include "gensupport.h"
+
 
 #define OUTPUT_LABEL(INDENT_STRING, LABEL_NUMBER) \
   printf("%sL%d: ATTRIBUTE_UNUSED_LABEL\n", (INDENT_STRING), (LABEL_NUMBER))
 
-static struct obstack obstack;
-struct obstack *rtl_obstack = &obstack;
-
-#define obstack_chunk_alloc xmalloc
-#define obstack_chunk_free free
-
 /* Holds an array of names indexed by insn_code_number.  */
 static char **insn_name_ptr = 0;
 static int insn_name_ptr_size = 0;
@@ -115,6 +111,7 @@ struct decision_test
 
     struct {
       int code_number;         /* Insn number matched.  */
+      int lineno;              /* Line number of the insn.  */
       int num_clobbers_to_add; /* Number of CLOBBERs to be added.  */
     } insn;
   } u;
@@ -170,6 +167,12 @@ static int next_index;
    allocate in each subroutine we make.  */
 
 static int max_depth;
+
+/* The line number of the start of the pattern currently being processed.  */
+static int pattern_lineno;
+
+/* Count of errors.  */
+static int error_count;
 \f
 /* This table contains a list of the rtl codes that can possibly match a
    predicate defined in recog.c.  The function `maybe_both_true' uses it to
@@ -190,6 +193,7 @@ static struct pred_table
   {"address_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
                       LABEL_REF, SUBREG, REG, MEM, PLUS, MINUS, MULT}},
   {"register_operand", {SUBREG, REG}},
+  {"pmode_register_operand", {SUBREG, REG}},
   {"scratch_operand", {SCRATCH, REG}},
   {"immediate_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
                         LABEL_REF}},
@@ -202,87 +206,107 @@ static struct pred_table
   {"pop_operand", {MEM}},
   {"memory_operand", {SUBREG, MEM}},
   {"indirect_operand", {SUBREG, MEM}},
-  {"comparison_operator", {EQ, NE, LE, LT, GE, GT, LEU, LTU, GEU, GTU}},
+  {"comparison_operator", {EQ, NE, LE, LT, GE, GT, LEU, LTU, GEU, GTU,
+                          UNORDERED, ORDERED, UNEQ, UNGE, UNGT, UNLE,
+                          UNLT, LTGT}},
   {"mode_independent_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
                                LABEL_REF, SUBREG, REG, MEM}}
 };
 
 #define NUM_KNOWN_PREDS (sizeof preds / sizeof preds[0])
 
+static const char * special_mode_pred_table[] = {
+#ifdef SPECIAL_MODE_PREDICATES
+  SPECIAL_MODE_PREDICATES
+#endif
+  "pmode_register_operand"
+};
+
+#define NUM_SPECIAL_MODE_PREDS \
+  (sizeof (special_mode_pred_table) / sizeof (special_mode_pred_table[0]))
+
 static struct decision *new_decision
-  PROTO((const char *, struct decision_head *));
+  PARAMS ((const char *, struct decision_head *));
 static struct decision_test *new_decision_test
-  PROTO((enum decision_type, struct decision_test ***));
+  PARAMS ((enum decision_type, struct decision_test ***));
+static rtx find_operand
+  PARAMS ((rtx, int));
+static void validate_pattern
+  PARAMS ((rtx, rtx, rtx));
 static struct decision *add_to_sequence
-  PROTO((rtx, struct decision_head *, const char *, enum routine_type, int));
+  PARAMS ((rtx, struct decision_head *, const char *, enum routine_type, int));
 
 static int maybe_both_true_2
-  PROTO((struct decision_test *, struct decision_test *));
+  PARAMS ((struct decision_test *, struct decision_test *));
 static int maybe_both_true_1
-  PROTO((struct decision_test *, struct decision_test *));
+  PARAMS ((struct decision_test *, struct decision_test *));
 static int maybe_both_true
-  PROTO((struct decision *, struct decision *, int));
+  PARAMS ((struct decision *, struct decision *, int));
 
 static int nodes_identical_1
-  PROTO((struct decision_test *, struct decision_test *));
+  PARAMS ((struct decision_test *, struct decision_test *));
 static int nodes_identical
-  PROTO((struct decision *, struct decision *));
+  PARAMS ((struct decision *, struct decision *));
 static void merge_accept_insn
-  PROTO((struct decision *, struct decision *));
+  PARAMS ((struct decision *, struct decision *));
 static void merge_trees
-  PROTO((struct decision_head *, struct decision_head *));
+  PARAMS ((struct decision_head *, struct decision_head *));
 
 static void factor_tests
-  PROTO((struct decision_head *));
+  PARAMS ((struct decision_head *));
 static void simplify_tests
-  PROTO((struct decision_head *));
+  PARAMS ((struct decision_head *));
 static int break_out_subroutines
-  PROTO((struct decision_head *, int));
+  PARAMS ((struct decision_head *, int));
 static void find_afterward
-  PROTO((struct decision_head *, struct decision *));
+  PARAMS ((struct decision_head *, struct decision *));
 
 static void change_state
-  PROTO((const char *, const char *, struct decision *, const char *));
+  PARAMS ((const char *, const char *, struct decision *, const char *));
 static void print_code
-  PROTO((enum rtx_code));
+  PARAMS ((enum rtx_code));
 static void write_afterward
-  PROTO((struct decision *, struct decision *, const char *));
+  PARAMS ((struct decision *, struct decision *, const char *));
 static struct decision *write_switch
-  PROTO((struct decision *, int));
+  PARAMS ((struct decision *, int));
 static void write_cond
-  PROTO((struct decision_test *, int, enum routine_type));
+  PARAMS ((struct decision_test *, int, enum routine_type));
 static void write_action
-  PROTO((struct decision_test *, int, int, struct decision *,
-        enum routine_type));
+  PARAMS ((struct decision *, struct decision_test *, int, int,
+          struct decision *, enum routine_type));
 static int is_unconditional
-  PROTO((struct decision_test *, enum routine_type));
+  PARAMS ((struct decision_test *, enum routine_type));
 static int write_node
-  PROTO((struct decision *, int, enum routine_type));
+  PARAMS ((struct decision *, int, enum routine_type));
 static void write_tree_1
-  PROTO((struct decision_head *, int, enum routine_type));
+  PARAMS ((struct decision_head *, int, enum routine_type));
 static void write_tree
-  PROTO((struct decision_head *, const char *, enum routine_type, int));
+  PARAMS ((struct decision_head *, const char *, enum routine_type, int));
 static void write_subroutine
-  PROTO((struct decision_head *, enum routine_type));
+  PARAMS ((struct decision_head *, enum routine_type));
 static void write_subroutines
-  PROTO((struct decision_head *, enum routine_type));
+  PARAMS ((struct decision_head *, enum routine_type));
 static void write_header
-  PROTO((void));
+  PARAMS ((void));
 
 static struct decision_head make_insn_sequence
-  PROTO((rtx, enum routine_type));
+  PARAMS ((rtx, enum routine_type));
 static void process_tree
-  PROTO((struct decision_head *, enum routine_type));
+  PARAMS ((struct decision_head *, enum routine_type));
   
 static void record_insn_name
-  PROTO((int, const char *));
+  PARAMS ((int, const char *));
 
+static void debug_decision_0
+  PARAMS ((struct decision *, int, int));
 static void debug_decision_1
-  PROTO((struct decision *, int));
+  PARAMS ((struct decision *, int));
 static void debug_decision_2
-  PROTO((struct decision_test *));
+  PARAMS ((struct decision_test *));
 extern void debug_decision
-  PROTO((struct decision *));
+  PARAMS ((struct decision *));
+extern void debug_decision_list
+  PARAMS ((struct decision *));
 \f
 /* Create a new node in sequence after LAST.  */
 
@@ -324,6 +348,300 @@ new_decision_test (type, pplace)
   return test;
 }
 
+/* Search for and return operand N.  */
+
+static rtx
+find_operand (pattern, n)
+     rtx pattern;
+     int n;
+{
+  const char *fmt;
+  RTX_CODE code;
+  int i, j, len;
+  rtx r;
+
+  code = GET_CODE (pattern);
+  if ((code == MATCH_SCRATCH
+       || code == MATCH_INSN
+       || code == MATCH_OPERAND
+       || code == MATCH_OPERATOR
+       || code == MATCH_PARALLEL)
+      && XINT (pattern, 0) == n)
+    return pattern;
+
+  fmt = GET_RTX_FORMAT (code);
+  len = GET_RTX_LENGTH (code);
+  for (i = 0; i < len; i++)
+    {
+      switch (fmt[i])
+       {
+       case 'e': case 'u':
+         if ((r = find_operand (XEXP (pattern, i), n)) != NULL_RTX)
+           return r;
+         break;
+
+       case 'E':
+         for (j = 0; j < XVECLEN (pattern, i); j++)
+           if ((r = find_operand (XVECEXP (pattern, i, j), n)) != NULL_RTX)
+             return r;
+         break;
+
+       case 'i': case 'w': case '0': case 's':
+         break;
+
+       default:
+         abort ();
+       }
+    }
+
+  return NULL;
+}
+
+/* Check for various errors in patterns.  SET is nonnull for a destination,
+   and is the complete set pattern.  */
+
+static void
+validate_pattern (pattern, insn, set)
+     rtx pattern;
+     rtx insn;
+     rtx set;
+{
+  const char *fmt;
+  RTX_CODE code;
+  size_t i, len;
+  int j;
+
+  code = GET_CODE (pattern);
+  switch (code)
+    {
+    case MATCH_SCRATCH:
+      return;
+
+    case MATCH_INSN:
+    case MATCH_OPERAND:
+    case MATCH_OPERATOR:
+      {
+       const char *pred_name = XSTR (pattern, 1);
+       int allows_non_lvalue = 1, allows_non_const = 1;
+       int special_mode_pred = 0;
+       const char *c_test;
+
+       if (GET_CODE (insn) == DEFINE_INSN)
+         c_test = XSTR (insn, 2);
+       else
+         c_test = XSTR (insn, 1);
+
+       if (pred_name[0] != 0)
+         {
+           for (i = 0; i < NUM_KNOWN_PREDS; i++)
+             if (! strcmp (preds[i].name, pred_name))
+               break;
+
+           if (i < NUM_KNOWN_PREDS)
+             {
+               int j;
+
+               allows_non_lvalue = allows_non_const = 0;
+               for (j = 0; preds[i].codes[j] != 0; j++)
+                 {
+                   RTX_CODE c = preds[i].codes[j];
+                   if (c != LABEL_REF
+                       && c != SYMBOL_REF
+                       && c != CONST_INT
+                       && c != CONST_DOUBLE
+                       && c != CONST
+                       && c != HIGH
+                       && c != CONSTANT_P_RTX)
+                     allows_non_const = 1;
+
+                   if (c != REG
+                       && c != SUBREG
+                       && c != MEM
+                       && c != CONCAT
+                       && c != PARALLEL
+                       && c != STRICT_LOW_PART)
+                     allows_non_lvalue = 1;
+                 }
+             }
+           else
+             {
+#ifdef PREDICATE_CODES
+               /* If the port has a list of the predicates it uses but
+                  omits one, warn.  */
+               message_with_line (pattern_lineno,
+                                  "warning: `%s' not in PREDICATE_CODES",
+                                  pred_name);
+#endif
+             }
+
+           for (i = 0; i < NUM_SPECIAL_MODE_PREDS; ++i)
+             if (strcmp (pred_name, special_mode_pred_table[i]) == 0)
+               {
+                 special_mode_pred = 1;
+                 break;
+               }
+         }
+
+       /* A MATCH_OPERAND that is a SET should have an output reload.  */
+       if (set
+           && code == MATCH_OPERAND
+           && XSTR (pattern, 2)[0] != '\0'
+           && XSTR (pattern, 2)[0] != '='
+           && XSTR (pattern, 2)[0] != '+')
+         {
+           message_with_line (pattern_lineno,
+                              "operand %d missing output reload", 
+                              XINT (pattern, 0));
+           error_count++;
+         }
+
+       /* Allowing non-lvalues in destinations -- particularly CONST_INT --
+          while not likely to occur at runtime, results in less efficient
+          code from insn-recog.c.  */
+       if (set
+           && pred_name[0] != '\0'
+           && allows_non_lvalue)
+         {
+           message_with_line (pattern_lineno,
+                       "warning: destination operand %d allows non-lvalue",
+                       XINT (pattern, 0));
+         }
+
+       /* A modeless MATCH_OPERAND can be handy when we can
+          check for multiple modes in the c_test.  In most other cases,
+          it is a mistake.  Only DEFINE_INSN is eligible, since SPLIT
+          and PEEP2 can FAIL within the output pattern.  Exclude 
+          address_operand, since its mode is related to the mode of
+          the memory not the operand.  Exclude the SET_DEST of a call
+          instruction, as that is a common idiom.  */
+
+       if (GET_MODE (pattern) == VOIDmode
+           && code == MATCH_OPERAND
+           && GET_CODE (insn) == DEFINE_INSN
+           && allows_non_const
+           && ! special_mode_pred
+           && pred_name[0] != '\0'
+           && strcmp (pred_name, "address_operand") != 0
+           && strstr (c_test, "operands") == NULL
+           && ! (set
+                 && GET_CODE (set) == SET
+                 && GET_CODE (SET_SRC (set)) == CALL))
+         {
+           message_with_line (pattern_lineno,
+                              "warning: operand %d missing mode?",
+                              XINT (pattern, 0));
+         }
+       return;
+      }
+
+    case SET:
+      {
+       enum machine_mode dmode, smode;
+       rtx dest, src;
+
+       dest = SET_DEST (pattern);
+       src = SET_SRC (pattern);
+
+       /* Find the referant for a DUP.  */
+
+       if (GET_CODE (dest) == MATCH_DUP
+           || GET_CODE (dest) == MATCH_OP_DUP
+           || GET_CODE (dest) == MATCH_PAR_DUP)
+         dest = find_operand (insn, XINT (dest, 0));
+
+       if (GET_CODE (src) == MATCH_DUP
+           || GET_CODE (src) == MATCH_OP_DUP
+           || GET_CODE (src) == MATCH_PAR_DUP)
+         src = find_operand (insn, XINT (src, 0));
+
+       /* STRICT_LOW_PART is a wrapper.  Its argument is the real
+          destination, and it's mode should match the source.  */
+       if (GET_CODE (dest) == STRICT_LOW_PART)
+         dest = XEXP (dest, 0);
+
+       dmode = GET_MODE (dest);
+       smode = GET_MODE (src);
+
+       /* The mode of an ADDRESS_OPERAND is the mode of the memory
+          reference, not the mode of the address.  */
+       if (GET_CODE (src) == MATCH_OPERAND
+           && ! strcmp (XSTR (src, 1), "address_operand"))
+         ;
+
+        /* The operands of a SET must have the same mode unless one
+          is VOIDmode.  */
+        else if (dmode != VOIDmode && smode != VOIDmode && dmode != smode)
+         {
+           message_with_line (pattern_lineno,
+                              "mode mismatch in set: %smode vs %smode",
+                              GET_MODE_NAME (dmode), GET_MODE_NAME (smode));
+           error_count++;
+         }
+
+       /* If only one of the operands is VOIDmode, and PC or CC0 is 
+          not involved, it's probably a mistake.  */
+       else if (dmode != smode
+                && GET_CODE (dest) != PC
+                && GET_CODE (dest) != CC0
+                && GET_CODE (src) != PC
+                && GET_CODE (src) != CC0
+                && GET_CODE (src) != CONST_INT)
+         {
+           const char *which;
+           which = (dmode == VOIDmode ? "destination" : "source");
+           message_with_line (pattern_lineno,
+                              "warning: %s missing a mode?", which);
+         }
+
+       if (dest != SET_DEST (pattern))
+         validate_pattern (dest, insn, pattern);
+       validate_pattern (SET_DEST (pattern), insn, pattern);
+        validate_pattern (SET_SRC (pattern), insn, NULL_RTX);
+        return;
+      }
+
+    case CLOBBER:
+      validate_pattern (SET_DEST (pattern), insn, pattern);
+      return;
+
+    case LABEL_REF:
+      if (GET_MODE (XEXP (pattern, 0)) != VOIDmode)
+       {
+         message_with_line (pattern_lineno,
+                            "operand to label_ref %smode not VOIDmode",
+                            GET_MODE_NAME (GET_MODE (XEXP (pattern, 0))));
+         error_count++;
+       }
+      break;
+
+    default:
+      break;
+    }
+
+  fmt = GET_RTX_FORMAT (code);
+  len = GET_RTX_LENGTH (code);
+  for (i = 0; i < len; i++)
+    {
+      switch (fmt[i])
+       {
+       case 'e': case 'u':
+         validate_pattern (XEXP (pattern, i), insn, NULL_RTX);
+         break;
+
+       case 'E':
+         for (j = 0; j < XVECLEN (pattern, i); j++)
+           validate_pattern (XVECEXP (pattern, i, j), insn, NULL_RTX);
+         break;
+
+       case 'i': case 'w': case '0': case 's':
+         break;
+
+       default:
+         abort ();
+       }
+    }
+}
+
 /* Create a chain of nodes to verify that an rtl expression matches
    PATTERN.
 
@@ -449,7 +767,7 @@ add_to_sequence (pattern, last, position, insn_type, top)
 
            if (i < NUM_KNOWN_PREDS)
              {
-               int allows_const_int, j;
+               int j;
 
                test->u.pred.index = i;
 
@@ -465,15 +783,7 @@ add_to_sequence (pattern, last, position, insn_type, top)
                    }
              }
            else
-             {
-               test->u.pred.index = -1;
-#ifdef PREDICATE_CODES
-               /* If the port has a list of the predicates it uses but
-                  omits one, warn.  */
-               fprintf (stderr, "Warning: `%s' not in PREDICATE_CODES\n",
-                        pred_name);
-#endif
-             }
+             test->u.pred.index = -1;
          }
 
        /* Can't enforce a mode if we allow const_int.  */
@@ -526,32 +836,6 @@ add_to_sequence (pattern, last, position, insn_type, top)
       pattern = XEXP (pattern, 0);
       goto restart;
 
-    case SET:
-      /* The operands of a SET must have the same mode unless one
-        is VOIDmode.  */
-      if (GET_MODE (SET_SRC (pattern)) != VOIDmode
-         && GET_MODE (SET_DEST (pattern)) != VOIDmode
-         && GET_MODE (SET_SRC (pattern)) != GET_MODE (SET_DEST (pattern))
-         /* The mode of an ADDRESS_OPERAND is the mode of the memory
-            reference, not the mode of the address.  */
-         && ! (GET_CODE (SET_SRC (pattern)) == MATCH_OPERAND
-               && ! strcmp (XSTR (SET_SRC (pattern), 1), "address_operand")))
-       {
-         print_rtl (stderr, pattern);
-         fputc ('\n', stderr);
-         fatal ("mode mismatch in SET");
-       }
-      break;
-      
-    case LABEL_REF:
-      if (GET_MODE (XEXP (pattern, 0)) != VOIDmode)
-       {
-         print_rtl (stderr, pattern);
-         fputc ('\n', stderr);
-         fatal ("operand to LABEL_REF not VOIDmode");
-       }
-      break;
-
     default:
       break;
     }
@@ -700,15 +984,18 @@ maybe_both_true_2 (d1, d2)
        {
          if (d2->type == DT_mode)
            {
-             if (d1->u.pred.mode != d2->u.mode)
-               return 0;
-           }
-         else if (d2->type == DT_pred)
-           {
-             if (d2->u.pred.mode != VOIDmode
-                 && d1->u.pred.mode != d2->u.pred.mode)
+             if (d1->u.pred.mode != d2->u.mode
+                 /* The mode of an address_operand predicate is the
+                    mode of the memory, not the operand.  It can only
+                    be used for testing the predicate, so we must
+                    ignore it here.  */
+                 && strcmp (d1->u.pred.name, "address_operand") != 0)
                return 0;
            }
+         /* Don't check two predicate modes here, because if both predicates
+            accept CONST_INT, then both can still be true even if the modes
+            are different.  If they don't accept CONST_INT, there will be a
+            separate DT_mode that will make maybe_both_true_1 return 0.  */
        }
 
       if (d1->u.pred.index >= 0)
@@ -922,7 +1209,17 @@ nodes_identical (d1, d2)
     }
 
   /* For success, they should now both be null.  */
-  return t1 == t2;
+  if (t1 != t2)
+    return 0;
+
+  /* Check that their subnodes are at the same position, as any one set
+     of sibling decisions must be at the same position.  */
+  if (d1->success.first
+      && d2->success.first
+      && strcmp (d1->success.first->position, d2->success.first->position))
+    return 0;
+
+  return 1;
 }
 
 /* A subroutine of merge_trees; given two nodes that have been declared
@@ -966,11 +1263,12 @@ merge_accept_insn (oldd, addd)
     }
   else
     {
-      fatal ("Two actions at one point in tree for insns \"%s\" (%d) and \"%s\" (%d)",
-            get_insn_name (old->u.insn.code_number),
-            old->u.insn.code_number,
-            get_insn_name (add->u.insn.code_number),
-            add->u.insn.code_number);
+      message_with_line (add->u.insn.lineno, "`%s' matches `%s'",
+                        get_insn_name (add->u.insn.code_number),
+                        get_insn_name (old->u.insn.code_number));
+      message_with_line (old->u.insn.lineno, "previous definition of `%s'",
+                        get_insn_name (old->u.insn.code_number));
+      error_count++;
     }
 }
 
@@ -1276,14 +1574,10 @@ change_state (oldpos, newpos, afterward, indent)
   for (old_has_insn = odepth - 1; old_has_insn >= 0; --old_has_insn)
     if (oldpos[old_has_insn] >= 'A' && oldpos[old_has_insn] <= 'Z')
       break;
-  for (new_has_insn = odepth - 1; new_has_insn >= 0; --new_has_insn)
+  for (new_has_insn = ndepth - 1; new_has_insn >= 0; --new_has_insn)
     if (newpos[new_has_insn] >= 'A' && newpos[new_has_insn] <= 'Z')
       break;
 
-  /* Make sure to reset the _last_insn pointer when popping back up.  */
-  if (old_has_insn >= 0 && new_has_insn < 0)
-    printf ("%s_last_insn = insn;\n", indent);
-
   /* Go down to desired level.  */
   while (depth < ndepth)
     {
@@ -1293,21 +1587,20 @@ change_state (oldpos, newpos, afterward, indent)
          /* We can only fail if we're moving down the tree.  */
          if (old_has_insn >= 0 && oldpos[old_has_insn] >= newpos[depth])
            {
-             printf ("%s_last_insn = recog_next_insn (insn, %d);\n", 
+             printf ("%stem = peep2_next_insn (%d);\n", 
                      indent, newpos[depth] - 'A');
            }
          else
            {
-             printf ("%stem = recog_next_insn (insn, %d);\n", 
+             printf ("%stem = peep2_next_insn (%d);\n", 
                      indent, newpos[depth] - 'A');
              printf ("%sif (tem == NULL_RTX)\n", indent);
              if (afterward)
                printf ("%s  goto L%d;\n", indent, afterward->number);
              else
                printf ("%s  goto ret0;\n", indent);
-             printf ("%s_last_insn = tem;\n", indent);
            }
-         printf ("%sx%d = PATTERN (_last_insn);\n", indent, depth + 1);
+         printf ("%sx%d = PATTERN (tem);\n", indent, depth + 1);
        }
       else if (newpos[depth] >= 'a' && newpos[depth] <= 'z')
        printf ("%sx%d = XVECEXP (x%d, 0, %d);\n",
@@ -1374,13 +1667,14 @@ write_switch (start, depth)
     {
       char codemap[NUM_RTX_CODE];
       struct decision *ret;
+      RTX_CODE code;
 
       memset (codemap, 0, sizeof(codemap));
 
       printf ("  switch (GET_CODE (x%d))\n    {\n", depth);
+      code = p->tests->u.code;
       do 
        {
-         RTX_CODE code = p->tests->u.code;
          printf ("    case ");
          print_code (code);
          printf (":\n      goto L%d;\n", p->success.first->number);
@@ -1389,7 +1683,10 @@ write_switch (start, depth)
          codemap[code] = 1;
          p = p->next;
        }
-      while (p && p->tests->type == DT_code && !p->tests->next);
+      while (p
+            && ! p->tests->next
+            && p->tests->type == DT_code
+            && ! codemap[code = p->tests->u.code]);
 
       /* If P is testing a predicate that we know about and we haven't
         seen any of the codes that are valid for the predicate, we can
@@ -1451,30 +1748,29 @@ write_switch (start, depth)
           || type == DT_elt_one_int
           || type == DT_elt_zero_wide)
     {
-      const char *str;
-
       printf ("  switch (");
       switch (type)
        {
        case DT_mode:
-         str = "GET_MODE (x%d)";
+         printf ("GET_MODE (x%d)", depth);
          break;
        case DT_veclen:
-         str = "XVECLEN (x%d, 0)";
+         printf ("XVECLEN (x%d, 0)", depth);
          break;
        case DT_elt_zero_int:
-         str = "XINT (x%d, 0)";
+         printf ("XINT (x%d, 0)", depth);
          break;
        case DT_elt_one_int:
-         str = "XINT (x%d, 1)";
+         printf ("XINT (x%d, 1)", depth);
          break;
        case DT_elt_zero_wide:
-         str = "XWINT (x%d, 0)";
+         /* Convert result of XWINT to int for portability since some C
+            compilers won't do it and some will.  */
+         printf ("(int) XWINT (x%d, 0)", depth);
          break;
        default:
          abort ();
        }
-      printf (str, depth);
       printf (")\n    {\n");
 
       do
@@ -1587,7 +1883,8 @@ write_cond (p, depth, subroutine_type)
    perform a state change.  For the `accept' tests we must do more work.  */
 
 static void
-write_action (test, depth, uncond, success, subroutine_type)
+write_action (p, test, depth, uncond, success, subroutine_type)
+     struct decision *p;
      struct decision_test *test;
      int depth, uncond;
      struct decision *success;
@@ -1641,9 +1938,20 @@ write_action (test, depth, uncond, success, subroutine_type)
          break;
 
        case PEEPHOLE2:
-         printf ("%stem = gen_peephole2_%d (insn, operands);\n",
-                 indent, test->u.insn.code_number);
-         printf ("%sif (tem != 0)\n%s  goto ret1;\n", indent, indent);
+         {
+           int match_len = 0, i;
+
+           for (i = strlen (p->position) - 1; i >= 0; --i)
+             if (p->position[i] >= 'A' && p->position[i] <= 'Z')
+               {
+                 match_len = p->position[i] - 'A';
+                 break;
+               }
+           printf ("%s*_pmatch_len = %d;\n", indent, match_len);
+           printf ("%stem = gen_peephole2_%d (insn, operands);\n",
+                   indent, test->u.insn.code_number);
+           printf ("%sif (tem != 0)\n%s  return tem;\n", indent, indent);
+         }
          break;
 
        default:
@@ -1726,7 +2034,7 @@ write_node (p, depth, subroutine_type)
       printf (")\n");
     }
 
-  write_action (last_test, depth, uncond, p->success.first, subroutine_type);
+  write_action (p, last_test, depth, uncond, p->success.first, subroutine_type);
 
   return uncond > 0;
 }
@@ -1789,7 +2097,7 @@ write_tree (head, prevpos, type, initial)
       };
 
       static const char * const call_suffix[] = {
-         ", pnum_clobbers", "", ", _plast_insn"
+         ", pnum_clobbers", "", ", _pmatch_len"
       };
 
       /* This node has been broken out into a separate subroutine.
@@ -1834,32 +2142,7 @@ write_subroutine (head, type)
      struct decision_head *head;
      enum routine_type type;
 {
-  static const char * const proto_pattern[] = {
-    "%sint recog%s PROTO ((rtx, rtx, int *));\n",
-    "%srtx split%s PROTO ((rtx, rtx));\n",
-    "%srtx peephole2%s PROTO ((rtx, rtx, rtx *));\n"
-  };
-
-  static const char * const decl_pattern[] = {
-"%sint\n\
-recog%s (x0, insn, pnum_clobbers)\n\
-     register rtx x0;\n\
-     rtx insn ATTRIBUTE_UNUSED;\n\
-     int *pnum_clobbers ATTRIBUTE_UNUSED;\n",
-
-"%srtx\n\
-split%s (x0, insn)\n\
-     register rtx x0;\n\
-     rtx insn ATTRIBUTE_UNUSED;\n",
-
-"%srtx\n\
-peephole2%s (x0, insn, _plast_insn)\n\
-     register rtx x0;\n\
-     rtx insn ATTRIBUTE_UNUSED;\n\
-     rtx *_plast_insn ATTRIBUTE_UNUSED;\n"
-  };
-     
-  int subfunction = head->first->subroutine_number;
+  int subfunction = head->first ? head->first->subroutine_number : 0;
   const char *s_or_e;
   char extension[32];
   int i;
@@ -1873,21 +2156,45 @@ peephole2%s (x0, insn, _plast_insn)\n\
   else
     strcpy (extension, "_insns");
 
-  printf (proto_pattern[type], s_or_e, extension);
-  printf (decl_pattern[type], s_or_e, extension);
+  switch (type)
+    {
+    case RECOG:
+      printf ("%sint recog%s PARAMS ((rtx, rtx, int *));\n", s_or_e, extension);
+      printf ("%sint\n\
+recog%s (x0, insn, pnum_clobbers)\n\
+     register rtx x0;\n\
+     rtx insn ATTRIBUTE_UNUSED;\n\
+     int *pnum_clobbers ATTRIBUTE_UNUSED;\n", s_or_e, extension);
+      break;
+    case SPLIT:
+      printf ("%srtx split%s PARAMS ((rtx, rtx));\n", s_or_e, extension);
+      printf ("%srtx\n\
+split%s (x0, insn)\n\
+     register rtx x0;\n\
+     rtx insn ATTRIBUTE_UNUSED;\n", s_or_e, extension);
+      break;
+    case PEEPHOLE2:
+      printf ("%srtx peephole2%s PARAMS ((rtx, rtx, int *));\n",
+             s_or_e, extension);
+      printf ("%srtx\n\
+peephole2%s (x0, insn, _pmatch_len)\n\
+     register rtx x0;\n\
+     rtx insn ATTRIBUTE_UNUSED;\n\
+     int *_pmatch_len ATTRIBUTE_UNUSED;\n", s_or_e, extension);
+      break;
+    }
 
-  printf ("{\n  register rtx * const operands = &recog_data.operand[0];\n");
+  printf ("{\n  register rtx * const operands ATTRIBUTE_UNUSED = &recog_data.operand[0];\n");
   for (i = 1; i <= max_depth; i++)
     printf ("  register rtx x%d ATTRIBUTE_UNUSED;\n", i);
 
-  if (type == PEEPHOLE2)
-    printf ("  register rtx _last_insn = insn;\n");
   printf ("  %s tem ATTRIBUTE_UNUSED;\n", IS_SPLIT (type) ? "rtx" : "int");
 
-  write_tree (head, "", type, 1);
+  if (head->first)
+    write_tree (head, "", type, 1);
+  else
+    printf ("  goto ret0;\n");
 
-  if (type == PEEPHOLE2)
-    printf (" ret1:\n  *_plast_insn = _last_insn;\n  return tem;\n");
   printf (" ret0:\n  return %d;\n}\n\n", IS_SPLIT (type) ? 0 : -1);
 }
 
@@ -1978,9 +2285,11 @@ make_insn_sequence (insn, type)
   struct decision *last;
   struct decision_test *test, **place;
   struct decision_head head;
+  char c_test_pos[2];
 
   record_insn_name (next_insn_code, (type == RECOG ? XSTR (insn, 0) : NULL));
 
+  c_test_pos[0] = '\0';
   if (type == PEEPHOLE2)
     {
       int i, j;
@@ -2002,6 +2311,9 @@ make_insn_sequence (insn, type)
            }
        }
       XVECLEN (x, 0) = j;
+
+      c_test_pos[0] = 'A' + j - 1;
+      c_test_pos[1] = '\0';
     }
   else if (XVECLEN (insn, type == RECOG) == 1)
     x = XVECEXP (insn, type == RECOG, 0);
@@ -2012,6 +2324,8 @@ make_insn_sequence (insn, type)
       PUT_MODE (x, VOIDmode);
     }
 
+  validate_pattern (x, insn, NULL_RTX);
+
   memset(&head, 0, sizeof(head));
   last = add_to_sequence (x, &head, "", type, 1);
 
@@ -2025,7 +2339,7 @@ make_insn_sequence (insn, type)
       /* Need a new node if we have another test to add.  */
       if (test->type == DT_accept_op)
        {
-         last = new_decision ("", &last->success);
+         last = new_decision (c_test_pos, &last->success);
          place = &last->tests;
        }
       test = new_decision_test (DT_c_test, &place);
@@ -2034,6 +2348,7 @@ make_insn_sequence (insn, type)
 
   test = new_decision_test (DT_accept_insn, &place);
   test->u.insn.code_number = next_insn_code;
+  test->u.insn.lineno = pattern_lineno;
   test->u.insn.num_clobbers_to_add = 0;
 
   switch (type)
@@ -2100,6 +2415,7 @@ make_insn_sequence (insn, type)
 
              test = new_decision_test (DT_accept_insn, &place);
              test->u.insn.code_number = next_insn_code;
+             test->u.insn.lineno = pattern_lineno;
              test->u.insn.num_clobbers_to_add = XVECLEN (x, 0) - i;
 
              merge_trees (&head, &clobber_head);
@@ -2109,16 +2425,15 @@ make_insn_sequence (insn, type)
 
     case SPLIT:
       /* Define the subroutine we will call below and emit in genemit.  */
-      printf ("extern rtx gen_split_%d PROTO ((rtx *));\n", next_insn_code);
+      printf ("extern rtx gen_split_%d PARAMS ((rtx *));\n", next_insn_code);
       break;
 
     case PEEPHOLE2:
       /* Define the subroutine we will call below and emit in genemit.  */
-      printf ("extern rtx gen_peephole2_%d PROTO ((rtx, rtx *));\n",
+      printf ("extern rtx gen_peephole2_%d PARAMS ((rtx, rtx *));\n",
              next_insn_code);
       break;
     }
-  next_insn_code++;
 
   return head;
 }
@@ -2129,19 +2444,32 @@ process_tree (head, subroutine_type)
      enum routine_type subroutine_type;
 {
   if (head->first == NULL)
-    return;
+    {
+      /* We can elide peephole2_insns, but not recog or split_insns.  */
+      if (subroutine_type == PEEPHOLE2)
+       return;
+    }
+  else
+    {
+      factor_tests (head);
 
-  factor_tests (head);
-  simplify_tests (head);
+      next_subroutine_number = 0;
+      break_out_subroutines (head, 1);
+      find_afterward (head, NULL);
 
-  next_subroutine_number = 0;
-  break_out_subroutines (head, 1);
-  find_afterward (head, NULL);
+      /* We run this after find_afterward, because find_afterward needs
+        the redundant DT_mode tests on predicates to determine whether
+        two tests can both be true or not.  */
+      simplify_tests(head);
+
+      write_subroutines (head, subroutine_type);
+    }
 
-  write_subroutines (head, subroutine_type);
   write_subroutine (head, subroutine_type);
 }
 \f
+extern int main PARAMS ((int, char **));
+
 int
 main (argc, argv)
      int argc;
@@ -2149,11 +2477,8 @@ main (argc, argv)
 {
   rtx desc;
   struct decision_head recog_tree, split_tree, peephole2_tree, h;
-  FILE *infile;
-  register int c;
 
   progname = "genrecog";
-  obstack_init (rtl_obstack);
 
   memset (&recog_tree, 0, sizeof recog_tree);
   memset (&split_tree, 0, sizeof split_tree);
@@ -2162,12 +2487,8 @@ main (argc, argv)
   if (argc <= 1)
     fatal ("No input file name.");
 
-  infile = fopen (argv[1], "r");
-  if (infile == 0)
-    {
-      perror (argv[1]);
-      return FATAL_EXIT_CODE;
-    }
+  if (init_md_reader (argv[1]) != SUCCESS_EXIT_CODE)
+    return (FATAL_EXIT_CODE);
 
   next_insn_code = 0;
   next_index = 0;
@@ -2178,12 +2499,10 @@ main (argc, argv)
 
   while (1)
     {
-      c = read_skip_spaces (infile);
-      if (c == EOF)
+      desc = read_md_rtx (&pattern_lineno, &next_insn_code);
+      if (desc == NULL)
        break;
-      ungetc (c, infile);
 
-      desc = read_rtx (infile);
       if (GET_CODE (desc) == DEFINE_INSN)
        {
          h = make_insn_sequence (desc, RECOG);
@@ -2200,12 +2519,12 @@ main (argc, argv)
          merge_trees (&peephole2_tree, &h);
        }
        
-      if (GET_CODE (desc) == DEFINE_PEEPHOLE
-         || GET_CODE (desc) == DEFINE_EXPAND)
-       next_insn_code++;
       next_index++;
     }
 
+  if (error_count)
+    return FATAL_EXIT_CODE;
+
   puts ("\n\n");
 
   process_tree (&recog_tree, RECOG);
@@ -2261,42 +2580,6 @@ record_insn_name (code, name)
   insn_name_ptr[code] = new;
 }  
 \f
-char *
-xstrdup (input)
-  const char *input;
-{
-  register size_t len = strlen (input) + 1;
-  register char *output = xmalloc (len);
-  memcpy (output, input, len);
-  return output;
-}
-
-PTR
-xrealloc (old, size)
-  PTR old;
-  size_t size;
-{
-  register PTR ptr;
-  if (old)
-    ptr = (PTR) realloc (old, size);
-  else
-    ptr = (PTR) malloc (size);
-  if (!ptr)
-    fatal ("virtual memory exhausted");
-  return ptr;
-}
-
-PTR
-xmalloc (size)
-  size_t size;
-{
-  register PTR val = (PTR) malloc (size);
-
-  if (val == 0)
-    fatal ("virtual memory exhausted");
-  return val;
-}
-\f
 static void
 debug_decision_2 (test)
      struct decision_test *test;
@@ -2380,7 +2663,9 @@ debug_decision_1 (d, indent)
          debug_decision_2 (test);
        }
     }
-  fprintf (stderr, "} %d\n", d->number);
+  fprintf (stderr, "} %d n %d a %d\n", d->number,
+          (d->next ? d->next->number : -1),
+          (d->afterward ? d->afterward->number : -1));
 }
 
 static void