i386-common.c (processor_names): Add cascadelake.
[gcc.git] / gcc / gensupport.c
index df0ad5f352ba5b3471f885df84086d9bad1f5090..41ad9bc383ecafa7b962e508b2441ada1b60bea7 100644 (file)
@@ -1,6 +1,5 @@
 /* Support routines for the various generation passes.
-   Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
-   2010, Free Software Foundation, Inc.
+   Copyright (C) 2000-2018 Free Software Foundation, Inc.
 
    This file is part of GCC.
 
 #include "rtl.h"
 #include "obstack.h"
 #include "errors.h"
-#include "hashtab.h"
 #include "read-md.h"
 #include "gensupport.h"
+#include "vec.h"
+
+#define MAX_OPERANDS 40
+
+static rtx operand_data[MAX_OPERANDS];
+static rtx match_operand_entries_in_pattern[MAX_OPERANDS];
+static char used_operands_numbers[MAX_OPERANDS];
 
 
 /* In case some macros used by files we include need it, define this here.  */
@@ -35,39 +40,44 @@ int target_flags;
 
 int insn_elision = 1;
 
-const char *in_fname;
-
-/* This callback will be invoked whenever an rtl include directive is
-   processed.  To be used for creation of the dependency file.  */
-void (*include_callback) (const char *);
-
 static struct obstack obstack;
 struct obstack *rtl_obstack = &obstack;
 
-static int sequence_num;
+/* Counter for named patterns and INSN_CODEs.  */
+static int insn_sequence_num;
+
+/* Counter for define_splits.  */
+static int split_sequence_num;
+
+/* Counter for define_peephole2s.  */
+static int peephole2_sequence_num;
 
 static int predicable_default;
 static const char *predicable_true;
 static const char *predicable_false;
 
-static htab_t condition_table;
+static const char *subst_true = "yes";
+static const char *subst_false = "no";
 
-static char *base_dir = NULL;
+static htab_t condition_table;
 
-/* We initially queue all patterns, process the define_insn and
-   define_cond_exec patterns, then return them one at a time.  */
+/* We initially queue all patterns, process the define_insn,
+   define_cond_exec and define_subst patterns, then return
+   them one at a time.  */
 
 struct queue_elem
 {
   rtx data;
-  const char *filename;
-  int lineno;
+  file_location loc;
   struct queue_elem *next;
   /* In a DEFINE_INSN that came from a DEFINE_INSN_AND_SPLIT, SPLIT
      points to the generated DEFINE_SPLIT.  */
   struct queue_elem *split;
 };
 
+#define MNEMONIC_ATTR_NAME "mnemonic"
+#define MNEMONIC_HTAB_SIZE 1024
+
 static struct queue_elem *define_attr_queue;
 static struct queue_elem **define_attr_tail = &define_attr_queue;
 static struct queue_elem *define_pred_queue;
@@ -76,36 +86,22 @@ static struct queue_elem *define_insn_queue;
 static struct queue_elem **define_insn_tail = &define_insn_queue;
 static struct queue_elem *define_cond_exec_queue;
 static struct queue_elem **define_cond_exec_tail = &define_cond_exec_queue;
+static struct queue_elem *define_subst_queue;
+static struct queue_elem **define_subst_tail = &define_subst_queue;
 static struct queue_elem *other_queue;
 static struct queue_elem **other_tail = &other_queue;
+static struct queue_elem *define_subst_attr_queue;
+static struct queue_elem **define_subst_attr_tail = &define_subst_attr_queue;
 
-static struct queue_elem *queue_pattern (rtx, struct queue_elem ***,
-                                        const char *, int);
-
-/* Current maximum length of directory names in the search path
-   for include files.  (Altered as we get more of them.)  */
-
-size_t max_include_len;
-
-struct file_name_list
-  {
-    struct file_name_list *next;
-    const char *fname;
-  };
-
-struct file_name_list *first_dir_md_include = 0;  /* First dir to search */
-        /* First dir to search for <file> */
-struct file_name_list *first_bracket_include = 0;
-struct file_name_list *last_dir_md_include = 0;        /* Last in chain */
+/* Mapping from DEFINE_* rtxes to their location in the source file.  */
+static hash_map <rtx, file_location> *rtx_locs;
 
 static void remove_constraints (rtx);
-static void process_rtx (rtx, int);
 
 static int is_predicable (struct queue_elem *);
 static void identify_predicable_attribute (void);
 static int n_alternatives (const char *);
 static void collect_insn_data (rtx, int *, int *);
-static rtx alter_predicate_for_insn (rtx, int, int, int);
 static const char *alter_test_for_insn (struct queue_elem *,
                                        struct queue_elem *);
 static char *shift_output_template (char *, const char *, int);
@@ -114,16 +110,30 @@ static const char *alter_output_for_insn (struct queue_elem *,
                                          int, int);
 static void process_one_cond_exec (struct queue_elem *);
 static void process_define_cond_exec (void);
-static void process_include (rtx, int);
-static char *save_string (const char *, int);
 static void init_predicate_table (void);
 static void record_insn_name (int, const char *);
+
+static bool has_subst_attribute (struct queue_elem *, struct queue_elem *);
+static const char * alter_output_for_subst_insn (rtx, int);
+static void alter_attrs_for_subst_insn (struct queue_elem *, int);
+static void process_substs_on_one_elem (struct queue_elem *,
+                                       struct queue_elem *);
+static rtx subst_dup (rtx, int, int);
+static void process_define_subst (void);
+
+static const char * duplicate_alternatives (const char *, int);
+static const char * duplicate_each_alternative (const char * str, int n_dup);
+
+typedef const char * (*constraints_handler_t) (const char *, int);
+static rtx alter_constraints (rtx, int, constraints_handler_t);
+static rtx adjust_operands_numbers (rtx);
+static rtx replace_duplicating_operands_in_pattern (rtx);
 \f
 /* Make a version of gen_rtx_CONST_INT so that GEN_INT can be used in
    the gensupport programs.  */
 
 rtx
-gen_rtx_CONST_INT (enum machine_mode ARG_UNUSED (mode),
+gen_rtx_CONST_INT (machine_mode ARG_UNUSED (mode),
                   HOST_WIDE_INT arg)
 {
   rtx rt = rtx_alloc (CONST_INT);
@@ -131,18 +141,271 @@ gen_rtx_CONST_INT (enum machine_mode ARG_UNUSED (mode),
   XWINT (rt, 0) = arg;
   return rt;
 }
+
+/* Return the rtx pattern specified by the list of rtxes in a
+   define_insn or define_split.  */
+
+rtx
+add_implicit_parallel (rtvec vec)
+{
+  if (GET_NUM_ELEM (vec) == 1)
+    return RTVEC_ELT (vec, 0);
+  else
+    {
+      rtx pattern = rtx_alloc (PARALLEL);
+      XVEC (pattern, 0) = vec;
+      return pattern;
+    }
+}
+\f
+/* Predicate handling.
+
+   We construct from the machine description a table mapping each
+   predicate to a list of the rtl codes it can possibly match.  The
+   function 'maybe_both_true' uses it to deduce that there are no
+   expressions that can be matches by certain pairs of tree nodes.
+   Also, if a predicate can match only one code, we can hardwire that
+   code into the node testing the predicate.
+
+   Some predicates are flagged as special.  validate_pattern will not
+   warn about modeless match_operand expressions if they have a
+   special predicate.  Predicates that allow only constants are also
+   treated as special, for this purpose.
+
+   validate_pattern will warn about predicates that allow non-lvalues
+   when they appear in destination operands.
+
+   Calculating the set of rtx codes that can possibly be accepted by a
+   predicate expression EXP requires a three-state logic: any given
+   subexpression may definitively accept a code C (Y), definitively
+   reject a code C (N), or may have an indeterminate effect (I).  N
+   and I is N; Y or I is Y; Y and I, N or I are both I.  Here are full
+   truth tables.
+
+     a b  a&b  a|b
+     Y Y   Y    Y
+     N Y   N    Y
+     N N   N    N
+     I Y   I    Y
+     I N   N    I
+     I I   I    I
+
+   We represent Y with 1, N with 0, I with 2.  If any code is left in
+   an I state by the complete expression, we must assume that that
+   code can be accepted.  */
+
+#define N 0
+#define Y 1
+#define I 2
+
+#define TRISTATE_AND(a,b)                      \
+  ((a) == I ? ((b) == N ? N : I) :             \
+   (b) == I ? ((a) == N ? N : I) :             \
+   (a) && (b))
+
+#define TRISTATE_OR(a,b)                       \
+  ((a) == I ? ((b) == Y ? Y : I) :             \
+   (b) == I ? ((a) == Y ? Y : I) :             \
+   (a) || (b))
+
+#define TRISTATE_NOT(a)                                \
+  ((a) == I ? I : !(a))
+
+/* 0 means no warning about that code yet, 1 means warned.  */
+static char did_you_mean_codes[NUM_RTX_CODE];
+
+/* Recursively calculate the set of rtx codes accepted by the
+   predicate expression EXP, writing the result to CODES.  LOC is
+   the .md file location of the directive containing EXP.  */
+
+void
+compute_test_codes (rtx exp, file_location loc, char *codes)
+{
+  char op0_codes[NUM_RTX_CODE];
+  char op1_codes[NUM_RTX_CODE];
+  char op2_codes[NUM_RTX_CODE];
+  int i;
+
+  switch (GET_CODE (exp))
+    {
+    case AND:
+      compute_test_codes (XEXP (exp, 0), loc, op0_codes);
+      compute_test_codes (XEXP (exp, 1), loc, op1_codes);
+      for (i = 0; i < NUM_RTX_CODE; i++)
+       codes[i] = TRISTATE_AND (op0_codes[i], op1_codes[i]);
+      break;
+
+    case IOR:
+      compute_test_codes (XEXP (exp, 0), loc, op0_codes);
+      compute_test_codes (XEXP (exp, 1), loc, op1_codes);
+      for (i = 0; i < NUM_RTX_CODE; i++)
+       codes[i] = TRISTATE_OR (op0_codes[i], op1_codes[i]);
+      break;
+    case NOT:
+      compute_test_codes (XEXP (exp, 0), loc, op0_codes);
+      for (i = 0; i < NUM_RTX_CODE; i++)
+       codes[i] = TRISTATE_NOT (op0_codes[i]);
+      break;
+
+    case IF_THEN_ELSE:
+      /* a ? b : c  accepts the same codes as (a & b) | (!a & c).  */
+      compute_test_codes (XEXP (exp, 0), loc, op0_codes);
+      compute_test_codes (XEXP (exp, 1), loc, op1_codes);
+      compute_test_codes (XEXP (exp, 2), loc, op2_codes);
+      for (i = 0; i < NUM_RTX_CODE; i++)
+       codes[i] = TRISTATE_OR (TRISTATE_AND (op0_codes[i], op1_codes[i]),
+                               TRISTATE_AND (TRISTATE_NOT (op0_codes[i]),
+                                             op2_codes[i]));
+      break;
+
+    case MATCH_CODE:
+      /* MATCH_CODE allows a specified list of codes.  However, if it
+        does not apply to the top level of the expression, it does not
+        constrain the set of codes for the top level.  */
+      if (XSTR (exp, 1)[0] != '\0')
+       {
+         memset (codes, Y, NUM_RTX_CODE);
+         break;
+       }
+
+      memset (codes, N, NUM_RTX_CODE);
+      {
+       const char *next_code = XSTR (exp, 0);
+       const char *code;
+
+       if (*next_code == '\0')
+         {
+           error_at (loc, "empty match_code expression");
+           break;
+         }
+
+       while ((code = scan_comma_elt (&next_code)) != 0)
+         {
+           size_t n = next_code - code;
+           int found_it = 0;
+
+           for (i = 0; i < NUM_RTX_CODE; i++)
+             if (!strncmp (code, GET_RTX_NAME (i), n)
+                 && GET_RTX_NAME (i)[n] == '\0')
+               {
+                 codes[i] = Y;
+                 found_it = 1;
+                 break;
+               }
+           if (!found_it)
+             {
+               error_at (loc, "match_code \"%.*s\" matches nothing",
+                         (int) n, code);
+               for (i = 0; i < NUM_RTX_CODE; i++)
+                 if (!strncasecmp (code, GET_RTX_NAME (i), n)
+                     && GET_RTX_NAME (i)[n] == '\0'
+                     && !did_you_mean_codes[i])
+                   {
+                     did_you_mean_codes[i] = 1;
+                     message_at (loc, "(did you mean \"%s\"?)",
+                                 GET_RTX_NAME (i));
+                   }
+             }
+         }
+      }
+      break;
+
+    case MATCH_OPERAND:
+      /* MATCH_OPERAND disallows the set of codes that the named predicate
+        disallows, and is indeterminate for the codes that it does allow.  */
+      {
+       struct pred_data *p = lookup_predicate (XSTR (exp, 1));
+       if (!p)
+         {
+           error_at (loc, "reference to unknown predicate '%s'",
+                     XSTR (exp, 1));
+           break;
+         }
+       for (i = 0; i < NUM_RTX_CODE; i++)
+         codes[i] = p->codes[i] ? I : N;
+      }
+      break;
+
+
+    case MATCH_TEST:
+      /* (match_test WHATEVER) is completely indeterminate.  */
+      memset (codes, I, NUM_RTX_CODE);
+      break;
+
+    default:
+      error_at (loc, "'%s' cannot be used in predicates or constraints",
+               GET_RTX_NAME (GET_CODE (exp)));
+      memset (codes, I, NUM_RTX_CODE);
+      break;
+    }
+}
+
+#undef TRISTATE_OR
+#undef TRISTATE_AND
+#undef TRISTATE_NOT
+
+/* Return true if NAME is a valid predicate name.  */
+
+static bool
+valid_predicate_name_p (const char *name)
+{
+  const char *p;
+
+  if (!ISALPHA (name[0]) && name[0] != '_')
+    return false;
+  for (p = name + 1; *p; p++)
+    if (!ISALNUM (*p) && *p != '_')
+      return false;
+  return true;
+}
+
+/* Process define_predicate directive DESC, which appears at location LOC.
+   Compute the set of codes that can be matched, and record this as a known
+   predicate.  */
+
+static void
+process_define_predicate (rtx desc, file_location loc)
+{
+  struct pred_data *pred;
+  char codes[NUM_RTX_CODE];
+  int i;
+
+  if (!valid_predicate_name_p (XSTR (desc, 0)))
+    {
+      error_at (loc, "%s: predicate name must be a valid C function name",
+               XSTR (desc, 0));
+      return;
+    }
+
+  pred = XCNEW (struct pred_data);
+  pred->name = XSTR (desc, 0);
+  pred->exp = XEXP (desc, 1);
+  pred->c_block = XSTR (desc, 2);
+  if (GET_CODE (desc) == DEFINE_SPECIAL_PREDICATE)
+    pred->special = true;
+
+  compute_test_codes (XEXP (desc, 1), loc, codes);
+
+  for (i = 0; i < NUM_RTX_CODE; i++)
+    if (codes[i] != N)
+      add_predicate_code (pred, (enum rtx_code) i);
+
+  add_predicate (pred);
+}
+#undef I
+#undef N
+#undef Y
 \f
 /* Queue PATTERN on LIST_TAIL.  Return the address of the new queue
    element.  */
 
 static struct queue_elem *
 queue_pattern (rtx pattern, struct queue_elem ***list_tail,
-              const char *filename, int lineno)
+              file_location loc)
 {
-  struct queue_elem *e = XNEW(struct queue_elem);
+  struct queue_elem *e = XNEW (struct queue_elem);
   e->data = pattern;
-  e->filename = filename;
-  e->lineno = lineno;
+  e->loc = loc;
   e->next = NULL;
   e->split = NULL;
   **list_tail = e;
@@ -150,6 +413,45 @@ queue_pattern (rtx pattern, struct queue_elem ***list_tail,
   return e;
 }
 
+/* Remove element ELEM from QUEUE.  */
+static void
+remove_from_queue (struct queue_elem *elem, struct queue_elem **queue)
+{
+  struct queue_elem *prev, *e;
+  prev = NULL;
+  for (e = *queue; e ; e = e->next)
+    {
+      if (e == elem)
+       break;
+      prev = e;
+    }
+  if (e == NULL)
+    return;
+
+  if (prev)
+    prev->next = elem->next;
+  else
+    *queue = elem->next;
+}
+
+/* Build a define_attr for an binary attribute with name NAME and
+   possible values "yes" and "no", and queue it.  */
+static void
+add_define_attr (const char *name)
+{
+  struct queue_elem *e = XNEW (struct queue_elem);
+  rtx t1 = rtx_alloc (DEFINE_ATTR);
+  XSTR (t1, 0) = name;
+  XSTR (t1, 1) = "no,yes";
+  XEXP (t1, 2) = rtx_alloc (CONST_STRING);
+  XSTR (XEXP (t1, 2), 0) = "yes";
+  e->data = t1;
+  e->loc = file_location ("built-in", -1, -1);
+  e->next = define_attr_queue;
+  define_attr_queue = e;
+
+}
+
 /* Recursively remove constraints from an rtx.  */
 
 static void
@@ -183,106 +485,45 @@ remove_constraints (rtx part)
       }
 }
 
-/* Process an include file assuming that it lives in gcc/config/{target}/
-   if the include looks like (include "file").  */
-
-static void
-process_include (rtx desc, int lineno)
-{
-  const char *filename = XSTR (desc, 0);
-  const char *old_filename;
-  int old_lineno;
-  char *pathname;
-  FILE *input_file, *old_file;
-
-  /* If specified file name is absolute, skip the include stack.  */
-  if (! IS_ABSOLUTE_PATH (filename))
-    {
-      struct file_name_list *stackp;
-
-      /* Search directory path, trying to open the file.  */
-      for (stackp = first_dir_md_include; stackp; stackp = stackp->next)
-       {
-         static const char sep[2] = { DIR_SEPARATOR, '\0' };
-
-         pathname = concat (stackp->fname, sep, filename, NULL);
-         input_file = fopen (pathname, "r");
-         if (input_file != NULL)
-           goto success;
-         free (pathname);
-       }
-    }
-
-  if (base_dir)
-    pathname = concat (base_dir, filename, NULL);
-  else
-    pathname = xstrdup (filename);
-  input_file = fopen (pathname, "r");
-  if (input_file == NULL)
-    {
-      free (pathname);
-      error_with_line (lineno, "include file `%s' not found", filename);
-      return;
-    }
- success:
-
-  /* Save old cursor; setup new for the new file.  Note that "lineno" the
-     argument to this function is the beginning of the include statement,
-     while read_md_lineno has already been advanced.  */
-  old_file = read_md_file;
-  old_filename = read_md_filename;
-  old_lineno = read_md_lineno;
-  read_md_file = input_file;
-  read_md_filename = pathname;
-  read_md_lineno = 1;
-
-  if (include_callback)
-    include_callback (pathname);
-
-  /* Read the entire file.  */
-  while (read_rtx (&desc, &lineno))
-    process_rtx (desc, lineno);
-
-  /* Do not free pathname.  It is attached to the various rtx queue
-     elements.  */
-
-  read_md_file = old_file;
-  read_md_filename = old_filename;
-  read_md_lineno = old_lineno;
-
-  fclose (input_file);
-}
-
 /* Process a top level rtx in some way, queuing as appropriate.  */
 
 static void
-process_rtx (rtx desc, int lineno)
+process_rtx (rtx desc, file_location loc)
 {
   switch (GET_CODE (desc))
     {
     case DEFINE_INSN:
-      queue_pattern (desc, &define_insn_tail, read_md_filename, lineno);
+      queue_pattern (desc, &define_insn_tail, loc);
       break;
 
     case DEFINE_COND_EXEC:
-      queue_pattern (desc, &define_cond_exec_tail, read_md_filename, lineno);
+      queue_pattern (desc, &define_cond_exec_tail, loc);
+      break;
+
+    case DEFINE_SUBST:
+      queue_pattern (desc, &define_subst_tail, loc);
+      break;
+
+    case DEFINE_SUBST_ATTR:
+      queue_pattern (desc, &define_subst_attr_tail, loc);
       break;
 
     case DEFINE_ATTR:
-      queue_pattern (desc, &define_attr_tail, read_md_filename, lineno);
+    case DEFINE_ENUM_ATTR:
+      queue_pattern (desc, &define_attr_tail, loc);
       break;
 
     case DEFINE_PREDICATE:
     case DEFINE_SPECIAL_PREDICATE:
+      process_define_predicate (desc, loc);
+      /* Fall through.  */
+
     case DEFINE_CONSTRAINT:
     case DEFINE_REGISTER_CONSTRAINT:
     case DEFINE_MEMORY_CONSTRAINT:
+    case DEFINE_SPECIAL_MEMORY_CONSTRAINT:
     case DEFINE_ADDRESS_CONSTRAINT:
-      queue_pattern (desc, &define_pred_tail, read_md_filename, lineno);
-      break;
-
-    case INCLUDE:
-      process_include (desc, lineno);
+      queue_pattern (desc, &define_pred_tail, loc);
       break;
 
     case DEFINE_INSN_AND_SPLIT:
@@ -310,8 +551,9 @@ process_rtx (rtx desc, int lineno)
        split_cond = XSTR (desc, 4);
        if (split_cond[0] == '&' && split_cond[1] == '&')
          {
-           copy_md_ptr_loc (split_cond + 2, split_cond);
-           split_cond = join_c_conditions (XSTR (desc, 2), split_cond + 2);
+           rtx_reader_ptr->copy_md_ptr_loc (split_cond + 2, split_cond);
+           split_cond = rtx_reader_ptr->join_c_conditions (XSTR (desc, 2),
+                                                           split_cond + 2);
          }
        XSTR (split, 1) = split_cond;
        XVEC (split, 2) = XVEC (desc, 5);
@@ -323,17 +565,14 @@ process_rtx (rtx desc, int lineno)
        XVEC (desc, 4) = attr;
 
        /* Queue them.  */
-       insn_elem
-         = queue_pattern (desc, &define_insn_tail, read_md_filename,
-                          lineno);
-       split_elem
-         = queue_pattern (split, &other_tail, read_md_filename, lineno);
+       insn_elem = queue_pattern (desc, &define_insn_tail, loc);
+       split_elem = queue_pattern (split, &other_tail, loc);
        insn_elem->split = split_elem;
        break;
       }
 
     default:
-      queue_pattern (desc, &other_tail, read_md_filename, lineno);
+      queue_pattern (desc, &other_tail, loc);
       break;
     }
 }
@@ -367,8 +606,7 @@ is_predicable (struct queue_elem *elem)
        case SET_ATTR_ALTERNATIVE:
          if (strcmp (XSTR (sub, 0), "predicable") == 0)
            {
-             error_with_line (elem->lineno,
-                              "multiple alternatives for `predicable'");
+             error_at (elem->loc, "multiple alternatives for `predicable'");
              return 0;
            }
          break;
@@ -387,8 +625,7 @@ is_predicable (struct queue_elem *elem)
          /* ??? It would be possible to handle this if we really tried.
             It's not easy though, and I'm not going to bother until it
             really proves necessary.  */
-         error_with_line (elem->lineno,
-                          "non-constant value for `predicable'");
+         error_at (elem->loc, "non-constant value for `predicable'");
          return 0;
 
        default:
@@ -399,27 +636,278 @@ is_predicable (struct queue_elem *elem)
   return predicable_default;
 
  found:
-  /* Verify that predicability does not vary on the alternative.  */
-  /* ??? It should be possible to handle this by simply eliminating
-     the non-predicable alternatives from the insn.  FRV would like
-     to do this.  Delay this until we've got the basics solid.  */
+  /* Find out which value we're looking at.  Multiple alternatives means at
+     least one is predicable.  */
   if (strchr (value, ',') != NULL)
-    {
-      error_with_line (elem->lineno, "multiple alternatives for `predicable'");
-      return 0;
-    }
-
-  /* Find out which value we're looking at.  */
+    return 1;
   if (strcmp (value, predicable_true) == 0)
     return 1;
   if (strcmp (value, predicable_false) == 0)
     return 0;
 
-  error_with_line (elem->lineno,
-                  "unknown value `%s' for `predicable' attribute", value);
+  error_at (elem->loc, "unknown value `%s' for `predicable' attribute", value);
   return 0;
 }
 
+/* Find attribute SUBST in ELEM and assign NEW_VALUE to it.  */
+static void
+change_subst_attribute (struct queue_elem *elem,
+                       struct queue_elem *subst_elem,
+                       const char *new_value)
+{
+  rtvec attrs_vec = XVEC (elem->data, 4);
+  const char *subst_name = XSTR (subst_elem->data, 0);
+  int i;
+
+  if (! attrs_vec)
+    return;
+
+  for (i = GET_NUM_ELEM (attrs_vec) - 1; i >= 0; --i)
+    {
+      rtx cur_attr = RTVEC_ELT (attrs_vec, i);
+      if (GET_CODE (cur_attr) != SET_ATTR)
+       continue;
+      if (strcmp (XSTR (cur_attr, 0), subst_name) == 0)
+       {
+         XSTR (cur_attr, 1) = new_value;
+         return;
+       }
+    }
+}
+
+/* Return true if ELEM has the attribute with the name of DEFINE_SUBST
+   represented by SUBST_ELEM and this attribute has value SUBST_TRUE.
+   DEFINE_SUBST isn't applied to patterns without such attribute.  In other
+   words, we suppose the default value of the attribute to be 'no' since it is
+   always generated automatically in read-rtl.c.  */
+static bool
+has_subst_attribute (struct queue_elem *elem, struct queue_elem *subst_elem)
+{
+  rtvec attrs_vec = XVEC (elem->data, 4);
+  const char *value, *subst_name = XSTR (subst_elem->data, 0);
+  int i;
+
+  if (! attrs_vec)
+    return false;
+
+  for (i = GET_NUM_ELEM (attrs_vec) - 1; i >= 0; --i)
+    {
+      rtx cur_attr = RTVEC_ELT (attrs_vec, i);
+      switch (GET_CODE (cur_attr))
+       {
+       case SET_ATTR:
+         if (strcmp (XSTR (cur_attr, 0), subst_name) == 0)
+           {
+             value = XSTR (cur_attr, 1);
+             goto found;
+           }
+         break;
+
+       case SET:
+         if (GET_CODE (SET_DEST (cur_attr)) != ATTR
+             || strcmp (XSTR (SET_DEST (cur_attr), 0), subst_name) != 0)
+           break;
+         cur_attr = SET_SRC (cur_attr);
+         if (GET_CODE (cur_attr) == CONST_STRING)
+           {
+             value = XSTR (cur_attr, 0);
+             goto found;
+           }
+
+         /* Only (set_attr "subst" "yes/no") and
+                 (set (attr "subst" (const_string "yes/no")))
+            are currently allowed.  */
+         error_at (elem->loc, "unsupported value for `%s'", subst_name);
+         return false;
+
+       case SET_ATTR_ALTERNATIVE:
+         error_at (elem->loc,
+                   "%s: `set_attr_alternative' is unsupported by "
+                   "`define_subst'", XSTR (elem->data, 0));
+         return false;
+
+
+       default:
+         gcc_unreachable ();
+       }
+    }
+
+  return false;
+
+ found:
+  if (strcmp (value, subst_true) == 0)
+    return true;
+  if (strcmp (value, subst_false) == 0)
+    return false;
+
+  error_at (elem->loc, "unknown value `%s' for `%s' attribute",
+           value, subst_name);
+  return false;
+}
+
+/* Compare RTL-template of original define_insn X to input RTL-template of
+   define_subst PT.  Return 1 if the templates match, 0 otherwise.
+   During the comparison, the routine also fills global_array OPERAND_DATA.  */
+static bool
+subst_pattern_match (rtx x, rtx pt, file_location loc)
+{
+  RTX_CODE code, code_pt;
+  int i, j, len;
+  const char *fmt, *pred_name;
+
+  code = GET_CODE (x);
+  code_pt = GET_CODE (pt);
+
+  if (code_pt == MATCH_OPERAND)
+    {
+      /* MATCH_DUP, and MATCH_OP_DUP don't have a specified mode, so we
+        always accept them.  */
+      if (GET_MODE (pt) != VOIDmode && GET_MODE (x) != GET_MODE (pt)
+         && (code != MATCH_DUP && code != MATCH_OP_DUP))
+       return false; /* Modes don't match.  */
+
+      if (code == MATCH_OPERAND)
+       {
+         pred_name = XSTR (pt, 1);
+         if (pred_name[0] != 0)
+           {
+             const struct pred_data *pred_pt = lookup_predicate (pred_name);
+             if (!pred_pt || pred_pt != lookup_predicate (XSTR (x, 1)))
+               return false; /* Predicates don't match.  */
+           }
+       }
+
+      gcc_assert (XINT (pt, 0) >= 0 && XINT (pt, 0) < MAX_OPERANDS);
+      operand_data[XINT (pt, 0)] = x;
+      return true;
+    }
+
+  if (code_pt == MATCH_OPERATOR)
+    {
+      int x_vecexp_pos = -1;
+
+      /* Compare modes.  */
+      if (GET_MODE (pt) != VOIDmode && GET_MODE (x) != GET_MODE (pt))
+       return false;
+
+      /* In case X is also match_operator, compare predicates.  */
+      if (code == MATCH_OPERATOR)
+       {
+         pred_name = XSTR (pt, 1);
+         if (pred_name[0] != 0)
+           {
+             const struct pred_data *pred_pt = lookup_predicate (pred_name);
+             if (!pred_pt || pred_pt != lookup_predicate (XSTR (x, 1)))
+               return false;
+           }
+       }
+
+      /* Compare operands.
+        MATCH_OPERATOR in input template could match in original template
+        either 1) MATCH_OPERAND, 2) UNSPEC, 3) ordinary operation (like PLUS).
+        In the first case operands are at (XVECEXP (x, 2, j)), in the second
+        - at (XVECEXP (x, 0, j)), in the last one - (XEXP (x, j)).
+        X_VECEXP_POS variable shows, where to look for these operands.  */
+      if (code == UNSPEC
+         || code == UNSPEC_VOLATILE)
+       x_vecexp_pos = 0;
+      else if (code == MATCH_OPERATOR)
+       x_vecexp_pos = 2;
+      else
+       x_vecexp_pos = -1;
+
+      /* MATCH_OPERATOR or UNSPEC case.  */
+      if (x_vecexp_pos >= 0)
+       {
+         /* Compare operands number in X and PT.  */
+         if (XVECLEN (x, x_vecexp_pos) != XVECLEN (pt, 2))
+           return false;
+         for (j = 0; j < XVECLEN (pt, 2); j++)
+           if (!subst_pattern_match (XVECEXP (x, x_vecexp_pos, j),
+                                     XVECEXP (pt, 2, j), loc))
+             return false;
+       }
+
+      /* Ordinary operator.  */
+      else
+       {
+         /* Compare operands number in X and PT.
+            We count operands differently for X and PT since we compare
+            an operator (with operands directly in RTX) and MATCH_OPERATOR
+            (that has a vector with operands).  */
+         if (GET_RTX_LENGTH (code) != XVECLEN (pt, 2))
+           return false;
+         for (j = 0; j < XVECLEN (pt, 2); j++)
+           if (!subst_pattern_match (XEXP (x, j), XVECEXP (pt, 2, j), loc))
+             return false;
+       }
+
+      /* Store the operand to OPERAND_DATA array.  */
+      gcc_assert (XINT (pt, 0) >= 0 && XINT (pt, 0) < MAX_OPERANDS);
+      operand_data[XINT (pt, 0)] = x;
+      return true;
+    }
+
+  if (code_pt == MATCH_PAR_DUP
+      || code_pt == MATCH_DUP
+      || code_pt == MATCH_OP_DUP
+      || code_pt == MATCH_SCRATCH
+      || code_pt == MATCH_PARALLEL)
+    {
+      /* Currently interface for these constructions isn't defined -
+        probably they aren't needed in input template of define_subst at all.
+        So, for now their usage in define_subst is forbidden.  */
+      error_at (loc, "%s cannot be used in define_subst",
+               GET_RTX_NAME (code_pt));
+    }
+
+  gcc_assert (code != MATCH_PAR_DUP
+      && code_pt != MATCH_DUP
+      && code_pt != MATCH_OP_DUP
+      && code_pt != MATCH_SCRATCH
+      && code_pt != MATCH_PARALLEL
+      && code_pt != MATCH_OPERAND
+      && code_pt != MATCH_OPERATOR);
+  /* If PT is none of the handled above, then we match only expressions with
+     the same code in X.  */
+  if (code != code_pt)
+    return false;
+
+  fmt = GET_RTX_FORMAT (code_pt);
+  len = GET_RTX_LENGTH (code_pt);
+
+  for (i = 0; i < len; i++)
+    {
+      if (fmt[i] == '0')
+       break;
+
+      switch (fmt[i])
+       {
+       case 'r': case 'p': case 'i': case 'w': case 's':
+         continue;
+
+       case 'e': case 'u':
+         if (!subst_pattern_match (XEXP (x, i), XEXP (pt, i), loc))
+           return false;
+         break;
+       case 'E':
+         {
+           if (XVECLEN (x, i) != XVECLEN (pt, i))
+             return false;
+           for (j = 0; j < XVECLEN (pt, i); j++)
+             if (!subst_pattern_match (XVECEXP (x, i, j),
+                                       XVECEXP (pt, i, j), loc))
+               return false;
+           break;
+         }
+       default:
+         gcc_unreachable ();
+       }
+    }
+
+  return true;
+}
+
 /* Examine the attribute "predicable"; discover its boolean values
    and its default.  */
 
@@ -435,8 +923,8 @@ identify_predicable_attribute (void)
     if (strcmp (XSTR (elem->data, 0), "predicable") == 0)
       goto found;
 
-  error_with_line (define_cond_exec_queue->lineno,
-                  "attribute `predicable' not defined");
+  error_at (define_cond_exec_queue->loc,
+           "attribute `predicable' not defined");
   return;
 
  found:
@@ -445,9 +933,8 @@ identify_predicable_attribute (void)
   p_true = strchr (p_false, ',');
   if (p_true == NULL || strchr (++p_true, ',') != NULL)
     {
-      error_with_line (elem->lineno, "attribute `predicable' is not a boolean");
-      if (p_false)
-        free (p_false);
+      error_at (elem->loc, "attribute `predicable' is not a boolean");
+      free (p_false);
       return;
     }
   p_true[-1] = '\0';
@@ -462,16 +949,14 @@ identify_predicable_attribute (void)
       break;
 
     case CONST:
-      error_with_line (elem->lineno, "attribute `predicable' cannot be const");
-      if (p_false)
-       free (p_false);
+      error_at (elem->loc, "attribute `predicable' cannot be const");
+      free (p_false);
       return;
 
     default:
-      error_with_line (elem->lineno,
-                      "attribute `predicable' must have a constant default");
-      if (p_false)
-       free (p_false);
+      error_at (elem->loc,
+               "attribute `predicable' must have a constant default");
+      free (p_false);
       return;
     }
 
@@ -481,10 +966,9 @@ identify_predicable_attribute (void)
     predicable_default = 0;
   else
     {
-      error_with_line (elem->lineno,
-                      "unknown value `%s' for `predicable' attribute", value);
-      if (p_false)
-       free (p_false);
+      error_at (elem->loc, "unknown value `%s' for `predicable' attribute",
+               value);
+      free (p_false);
     }
 }
 
@@ -502,26 +986,98 @@ n_alternatives (const char *s)
   return n;
 }
 
-/* Determine how many alternatives there are in INSN, and how many
-   operands.  */
-
-static void
-collect_insn_data (rtx pattern, int *palt, int *pmax)
+/* The routine scans rtl PATTERN, find match_operand in it and counts
+   number of alternatives.  If PATTERN contains several match_operands
+   with different number of alternatives, error is emitted, and the
+   routine returns 0.  If all match_operands in PATTERN have the same
+   number of alternatives, it's stored in N_ALT, and the routine returns 1.
+   LOC is the location of PATTERN, for error reporting.  */
+static int
+get_alternatives_number (rtx pattern, int *n_alt, file_location loc)
 {
   const char *fmt;
   enum rtx_code code;
   int i, j, len;
 
+  if (!n_alt)
+    return 0;
+
   code = GET_CODE (pattern);
   switch (code)
     {
     case MATCH_OPERAND:
       i = n_alternatives (XSTR (pattern, 2));
-      *palt = (i > *palt ? i : *palt);
-      /* Fall through.  */
+      /* n_alternatives returns 1 if constraint string is empty -
+        here we fix it up.  */
+      if (!*(XSTR (pattern, 2)))
+       i = 0;
+      if (*n_alt <= 0)
+       *n_alt = i;
+
+      else if (i && i != *n_alt)
+       {
+         error_at (loc, "wrong number of alternatives in operand %d",
+                   XINT (pattern, 0));
+         return 0;
+       }
 
-    case MATCH_OPERATOR:
+    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':
+         if (!get_alternatives_number (XEXP (pattern, i), n_alt, loc))
+           return 0;
+         break;
+
+       case 'V':
+         if (XVEC (pattern, i) == NULL)
+           break;
+         /* FALLTHRU */
+
+       case 'E':
+         for (j = XVECLEN (pattern, i) - 1; j >= 0; --j)
+           if (!get_alternatives_number (XVECEXP (pattern, i, j), n_alt, loc))
+             return 0;
+         break;
+
+       case 'r': case 'p': case 'i': case 'w':
+       case '0': case 's': case 'S': case 'T':
+         break;
+
+       default:
+         gcc_unreachable ();
+       }
+    }
+    return 1;
+}
+
+/* Determine how many alternatives there are in INSN, and how many
+   operands.  */
+
+static void
+collect_insn_data (rtx pattern, int *palt, int *pmax)
+{
+  const char *fmt;
+  enum rtx_code code;
+  int i, j, len;
+
+  code = GET_CODE (pattern);
+  switch (code)
+    {
+    case MATCH_OPERAND:
     case MATCH_SCRATCH:
+      i = n_alternatives (XSTR (pattern, code == MATCH_SCRATCH ? 1 : 2));
+      *palt = (i > *palt ? i : *palt);
+      /* Fall through.  */
+
+    case MATCH_OPERATOR:
     case MATCH_PARALLEL:
       i = XINT (pattern, 0);
       if (i > *pmax)
@@ -551,7 +1107,8 @@ collect_insn_data (rtx pattern, int *palt, int *pmax)
            collect_insn_data (XVECEXP (pattern, i, j), palt, pmax);
          break;
 
-       case 'i': case 'w': case '0': case 's': case 'S': case 'T':
+       case 'r': case 'p': case 'i': case 'w':
+       case '0': case 's': case 'S': case 'T':
          break;
 
        default:
@@ -561,7 +1118,8 @@ collect_insn_data (rtx pattern, int *palt, int *pmax)
 }
 
 static rtx
-alter_predicate_for_insn (rtx pattern, int alt, int max_op, int lineno)
+alter_predicate_for_insn (rtx pattern, int alt, int max_op,
+                         file_location loc)
 {
   const char *fmt;
   enum rtx_code code;
@@ -576,8 +1134,8 @@ alter_predicate_for_insn (rtx pattern, int alt, int max_op, int lineno)
 
        if (n_alternatives (c) != 1)
          {
-           error_with_line (lineno, "too many alternatives for operand %d",
-                            XINT (pattern, 0));
+           error_at (loc, "too many alternatives for operand %d",
+                     XINT (pattern, 0));
            return NULL;
          }
 
@@ -586,7 +1144,7 @@ alter_predicate_for_insn (rtx pattern, int alt, int max_op, int lineno)
          {
            size_t c_len = strlen (c);
            size_t len = alt * (c_len + 1);
-           char *new_c = XNEWVEC(char, len);
+           char *new_c = XNEWVEC (char, len);
 
            memcpy (new_c, c, c_len);
            for (i = 1; i < alt; ++i)
@@ -619,8 +1177,7 @@ alter_predicate_for_insn (rtx pattern, int alt, int max_op, int lineno)
       switch (fmt[i])
        {
        case 'e': case 'u':
-         r = alter_predicate_for_insn (XEXP (pattern, i), alt,
-                                       max_op, lineno);
+         r = alter_predicate_for_insn (XEXP (pattern, i), alt, max_op, loc);
          if (r == NULL)
            return r;
          break;
@@ -629,13 +1186,13 @@ alter_predicate_for_insn (rtx pattern, int alt, int max_op, int lineno)
          for (j = XVECLEN (pattern, i) - 1; j >= 0; --j)
            {
              r = alter_predicate_for_insn (XVECEXP (pattern, i, j),
-                                           alt, max_op, lineno);
+                                           alt, max_op, loc);
              if (r == NULL)
                return r;
            }
          break;
 
-       case 'i': case 'w': case '0': case 's':
+       case 'r': case 'p': case 'i': case 'w': case '0': case 's':
          break;
 
        default:
@@ -646,12 +1203,250 @@ alter_predicate_for_insn (rtx pattern, int alt, int max_op, int lineno)
   return pattern;
 }
 
+/* Duplicate constraints in PATTERN.  If pattern is from original
+   rtl-template, we need to duplicate each alternative - for that we
+   need to use duplicate_each_alternative () as a functor ALTER.
+   If pattern is from output-pattern of define_subst, we need to
+   duplicate constraints in another way - with duplicate_alternatives ().
+   N_DUP is multiplication factor.  */
+static rtx
+alter_constraints (rtx pattern, int n_dup, constraints_handler_t alter)
+{
+  const char *fmt;
+  enum rtx_code code;
+  int i, j, len;
+
+  code = GET_CODE (pattern);
+  switch (code)
+    {
+    case MATCH_OPERAND:
+      XSTR (pattern, 2) = alter (XSTR (pattern, 2), n_dup);
+      break;
+
+    default:
+      break;
+    }
+
+  fmt = GET_RTX_FORMAT (code);
+  len = GET_RTX_LENGTH (code);
+  for (i = 0; i < len; i++)
+    {
+      rtx r;
+
+      switch (fmt[i])
+       {
+       case 'e': case 'u':
+         r = alter_constraints (XEXP (pattern, i), n_dup, alter);
+         if (r == NULL)
+           return r;
+         break;
+
+       case 'E':
+         for (j = XVECLEN (pattern, i) - 1; j >= 0; --j)
+           {
+             r = alter_constraints (XVECEXP (pattern, i, j), n_dup, alter);
+             if (r == NULL)
+               return r;
+           }
+         break;
+
+       case 'r': case 'p': case 'i': case 'w': case '0': case 's':
+         break;
+
+       default:
+         break;
+       }
+    }
+
+  return pattern;
+}
+
 static const char *
 alter_test_for_insn (struct queue_elem *ce_elem,
                     struct queue_elem *insn_elem)
 {
-  return join_c_conditions (XSTR (ce_elem->data, 1),
-                           XSTR (insn_elem->data, 2));
+  return rtx_reader_ptr->join_c_conditions (XSTR (ce_elem->data, 1),
+                                           XSTR (insn_elem->data, 2));
+}
+
+/* Modify VAL, which is an attribute expression for the "enabled" attribute,
+   to take "ce_enabled" into account.  Return the new expression.  */
+static rtx
+modify_attr_enabled_ce (rtx val)
+{
+  rtx eq_attr, str;
+  rtx ite;
+  eq_attr = rtx_alloc (EQ_ATTR);
+  ite = rtx_alloc (IF_THEN_ELSE);
+  str = rtx_alloc (CONST_STRING);
+
+  XSTR (eq_attr, 0) = "ce_enabled";
+  XSTR (eq_attr, 1) = "yes";
+  XSTR (str, 0) = "no";
+  XEXP (ite, 0) = eq_attr;
+  XEXP (ite, 1) = val;
+  XEXP (ite, 2) = str;
+
+  return ite;
+}
+
+/* Alter the attribute vector of INSN, which is a COND_EXEC variant created
+   from a define_insn pattern.  We must modify the "predicable" attribute
+   to be named "ce_enabled", and also change any "enabled" attribute that's
+   present so that it takes ce_enabled into account.
+   We rely on the fact that INSN was created with copy_rtx, and modify data
+   in-place.  */
+
+static void
+alter_attrs_for_insn (rtx insn)
+{
+  static bool global_changes_made = false;
+  rtvec vec = XVEC (insn, 4);
+  rtvec new_vec;
+  rtx val, set;
+  int num_elem;
+  int predicable_idx = -1;
+  int enabled_idx = -1;
+  int i;
+
+  if (! vec)
+    return;
+
+  num_elem = GET_NUM_ELEM (vec);
+  for (i = num_elem - 1; i >= 0; --i)
+    {
+      rtx sub = RTVEC_ELT (vec, i);
+      switch (GET_CODE (sub))
+       {
+       case SET_ATTR:
+         if (strcmp (XSTR (sub, 0), "predicable") == 0)
+           {
+             predicable_idx = i;
+             XSTR (sub, 0) = "ce_enabled";
+           }
+         else if (strcmp (XSTR (sub, 0), "enabled") == 0)
+           {
+             enabled_idx = i;
+             XSTR (sub, 0) = "nonce_enabled";
+           }
+         break;
+
+       case SET_ATTR_ALTERNATIVE:
+         if (strcmp (XSTR (sub, 0), "predicable") == 0)
+           /* We already give an error elsewhere.  */
+           return;
+         else if (strcmp (XSTR (sub, 0), "enabled") == 0)
+           {
+             enabled_idx = i;
+             XSTR (sub, 0) = "nonce_enabled";
+           }
+         break;
+
+       case SET:
+         if (GET_CODE (SET_DEST (sub)) != ATTR)
+           break;
+         if (strcmp (XSTR (SET_DEST (sub), 0), "predicable") == 0)
+           {
+             sub = SET_SRC (sub);
+             if (GET_CODE (sub) == CONST_STRING)
+               {
+                 predicable_idx = i;
+                 XSTR (sub, 0) = "ce_enabled";
+               }
+             else
+               /* We already give an error elsewhere.  */
+               return;
+             break;
+           }
+         if (strcmp (XSTR (SET_DEST (sub), 0), "enabled") == 0)
+           {
+             enabled_idx = i;
+             XSTR (SET_DEST (sub), 0) = "nonce_enabled";
+           }
+         break;
+
+       default:
+         gcc_unreachable ();
+       }
+    }
+  if (predicable_idx == -1)
+    return;
+
+  if (!global_changes_made)
+    {
+      struct queue_elem *elem;
+
+      global_changes_made = true;
+      add_define_attr ("ce_enabled");
+      add_define_attr ("nonce_enabled");
+
+      for (elem = define_attr_queue; elem ; elem = elem->next)
+       if (strcmp (XSTR (elem->data, 0), "enabled") == 0)
+         {
+           XEXP (elem->data, 2)
+             = modify_attr_enabled_ce (XEXP (elem->data, 2));
+         }
+    }
+  if (enabled_idx == -1)
+    return;
+
+  new_vec = rtvec_alloc (num_elem + 1);
+  for (i = 0; i < num_elem; i++)
+    RTVEC_ELT (new_vec, i) = RTVEC_ELT (vec, i);
+  val = rtx_alloc (IF_THEN_ELSE);
+  XEXP (val, 0) = rtx_alloc (EQ_ATTR);
+  XEXP (val, 1) = rtx_alloc (CONST_STRING);
+  XEXP (val, 2) = rtx_alloc (CONST_STRING);
+  XSTR (XEXP (val, 0), 0) = "nonce_enabled";
+  XSTR (XEXP (val, 0), 1) = "yes";
+  XSTR (XEXP (val, 1), 0) = "yes";
+  XSTR (XEXP (val, 2), 0) = "no";
+  set = rtx_alloc (SET);
+  SET_DEST (set) = rtx_alloc (ATTR);
+  XSTR (SET_DEST (set), 0) = "enabled";
+  SET_SRC (set) = modify_attr_enabled_ce (val);
+  RTVEC_ELT (new_vec, i) = set;
+  XVEC (insn, 4) = new_vec;
+}
+
+/* As number of constraints is changed after define_subst, we need to
+   process attributes as well - we need to duplicate them the same way
+   that we duplicated constraints in original pattern
+   ELEM is a queue element, containing our rtl-template,
+   N_DUP - multiplication factor.  */
+static void
+alter_attrs_for_subst_insn (struct queue_elem * elem, int n_dup)
+{
+  rtvec vec = XVEC (elem->data, 4);
+  int num_elem;
+  int i;
+
+  if (n_dup < 2 || ! vec)
+    return;
+
+  num_elem = GET_NUM_ELEM (vec);
+  for (i = num_elem - 1; i >= 0; --i)
+    {
+      rtx sub = RTVEC_ELT (vec, i);
+      switch (GET_CODE (sub))
+       {
+       case SET_ATTR:
+         if (strchr (XSTR (sub, 1), ',') != NULL)
+           XSTR (sub, 1) = duplicate_alternatives (XSTR (sub, 1), n_dup);
+           break;
+
+       case SET_ATTR_ALTERNATIVE:
+       case SET:
+         error_at (elem->loc,
+                   "%s: `define_subst' does not support attributes "
+                   "assigned by `set' and `set_attr_alternative'",
+                   XSTR (elem->data, 0));
+         return;
+
+       default:
+         gcc_unreachable ();
+       }
+    }
 }
 
 /* Adjust all of the operand numbers in SRC to match the shift they'll
@@ -708,7 +1503,7 @@ alter_output_for_insn (struct queue_elem *ce_elem,
   if (*insn_out == '@')
     {
       len = (ce_len + 1) * alt + insn_len + 1;
-      p = result = XNEWVEC(char, len);
+      p = result = XNEWVEC (char, len);
 
       do
        {
@@ -742,6 +1537,133 @@ alter_output_for_insn (struct queue_elem *ce_elem,
   return result;
 }
 
+/* From string STR "a,b,c" produce "a,b,c,a,b,c,a,b,c", i.e. original
+   string, duplicated N_DUP times.  */
+
+static const char *
+duplicate_alternatives (const char * str, int n_dup)
+{
+  int i, len, new_len;
+  char *result, *sp;
+  const char *cp;
+
+  if (n_dup < 2)
+    return str;
+
+  while (ISSPACE (*str))
+    str++;
+
+  if (*str == '\0')
+    return str;
+
+  cp = str;
+  len = strlen (str);
+  new_len = (len + 1) * n_dup;
+
+  sp = result = XNEWVEC (char, new_len);
+
+  /* Global modifier characters mustn't be duplicated: skip if found.  */
+  if (*cp == '=' || *cp == '+' || *cp == '%')
+    {
+      *sp++ = *cp++;
+      len--;
+    }
+
+  /* Copy original constraints N_DUP times.  */
+  for (i = 0; i < n_dup; i++, sp += len+1)
+    {
+      memcpy (sp, cp, len);
+      *(sp+len) = (i == n_dup - 1) ? '\0' : ',';
+    }
+
+  return result;
+}
+
+/* From string STR "a,b,c" produce "a,a,a,b,b,b,c,c,c", i.e. string where
+   each alternative from the original string is duplicated N_DUP times.  */
+static const char *
+duplicate_each_alternative (const char * str, int n_dup)
+{
+  int i, len, new_len;
+  char *result, *sp, *ep, *cp;
+
+  if (n_dup < 2)
+    return str;
+
+  while (ISSPACE (*str))
+    str++;
+
+  if (*str == '\0')
+    return str;
+
+  cp = xstrdup (str);
+
+  new_len = (strlen (cp) + 1) * n_dup;
+
+  sp = result = XNEWVEC (char, new_len);
+
+  /* Global modifier characters mustn't be duplicated: skip if found.  */
+  if (*cp == '=' || *cp == '+' || *cp == '%')
+      *sp++ = *cp++;
+
+  do
+    {
+      if ((ep = strchr (cp, ',')) != NULL)
+       *ep++ = '\0';
+      len = strlen (cp);
+
+      /* Copy a constraint N_DUP times.  */
+      for (i = 0; i < n_dup; i++, sp += len + 1)
+       {
+         memcpy (sp, cp, len);
+         *(sp+len) = (ep == NULL && i == n_dup - 1) ? '\0' : ',';
+       }
+
+      cp = ep;
+    }
+  while (cp != NULL);
+
+  return result;
+}
+
+/* Alter the output of INSN whose pattern was modified by
+   DEFINE_SUBST.  We must replicate output strings according
+   to the new number of alternatives ALT in substituted pattern.
+   If ALT equals 1, output has one alternative or defined by C
+   code, then output is returned without any changes.  */
+
+static const char *
+alter_output_for_subst_insn (rtx insn, int alt)
+{
+  const char *insn_out, *old_out;
+  char *new_out, *cp;
+  size_t old_len, new_len;
+  int j;
+
+  insn_out = XTMPL (insn, 3);
+
+  if (alt < 2 || *insn_out != '@')
+    return insn_out;
+
+  old_out = insn_out + 1;
+  while (ISSPACE (*old_out))
+    old_out++;
+  old_len = strlen (old_out);
+
+  new_len = alt * (old_len + 1) + 1;
+
+  new_out = XNEWVEC (char, new_len);
+  new_out[0] = '@';
+
+  for (j = 0, cp = new_out + 1; j < alt; j++, cp += old_len + 1)
+    {
+      memcpy (cp, old_out, old_len);
+      cp[old_len] = (j == alt - 1) ? '\0' : '\n';
+    }
+
+  return new_out;
+}
+
 /* Replicate insns as appropriate for the given DEFINE_COND_EXEC.  */
 
 static void
@@ -765,13 +1687,13 @@ process_one_cond_exec (struct queue_elem *ce_elem)
 
       if (XVECLEN (ce_elem->data, 0) != 1)
        {
-         error_with_line (ce_elem->lineno, "too many patterns in predicate");
+         error_at (ce_elem->loc, "too many patterns in predicate");
          return;
        }
 
       pred = copy_rtx (XVECEXP (ce_elem->data, 0, 0));
       pred = alter_predicate_for_insn (pred, alternatives, max_operand,
-                                      ce_elem->lineno);
+                                      ce_elem->loc);
       if (pred == NULL)
        return;
 
@@ -782,26 +1704,29 @@ process_one_cond_exec (struct queue_elem *ce_elem)
       XSTR (insn, 0) = new_name;
       pattern = rtx_alloc (COND_EXEC);
       XEXP (pattern, 0) = pred;
-      if (XVECLEN (insn, 1) == 1)
-       {
-         XEXP (pattern, 1) = XVECEXP (insn, 1, 0);
-         XVECEXP (insn, 1, 0) = pattern;
-         PUT_NUM_ELEM (XVEC (insn, 1), 1);
-       }
-      else
+      XEXP (pattern, 1) = add_implicit_parallel (XVEC (insn, 1));
+      XVEC (insn, 1) = rtvec_alloc (1);
+      XVECEXP (insn, 1, 0) = pattern;
+
+       if (XVEC (ce_elem->data, 3) != NULL)
        {
-         XEXP (pattern, 1) = rtx_alloc (PARALLEL);
-         XVEC (XEXP (pattern, 1), 0) = XVEC (insn, 1);
-         XVEC (insn, 1) = rtvec_alloc (1);
-         XVECEXP (insn, 1, 0) = pattern;
+         rtvec attributes = rtvec_alloc (XVECLEN (insn, 4)
+                                         + XVECLEN (ce_elem->data, 3));
+         int i = 0;
+         int j = 0;
+         for (i = 0; i < XVECLEN (insn, 4); i++)
+           RTVEC_ELT (attributes, i) = XVECEXP (insn, 4, i);
+
+         for (j = 0; j < XVECLEN (ce_elem->data, 3); j++, i++)
+           RTVEC_ELT (attributes, i) = XVECEXP (ce_elem->data, 3, j);
+
+         XVEC (insn, 4) = attributes;
        }
 
       XSTR (insn, 2) = alter_test_for_insn (ce_elem, insn_elem);
       XTMPL (insn, 3) = alter_output_for_insn (ce_elem, insn_elem,
                                              alternatives, max_operand);
-
-      /* ??? Set `predicable' to false.  Not crucial since it's really
-         only used here, and we won't reprocess this new pattern.  */
+      alter_attrs_for_insn (insn);
 
       /* Put the new pattern on the `other' list so that it
         (a) is not reprocessed by other define_cond_exec patterns
@@ -814,299 +1739,959 @@ process_one_cond_exec (struct queue_elem *ce_elem)
         patterns into the define_insn chain just after their generator
         is something we'll have to experiment with.  */
 
-      queue_pattern (insn, &other_tail, insn_elem->filename,
-                    insn_elem->lineno);
+      queue_pattern (insn, &other_tail, insn_elem->loc);
+
+      if (!insn_elem->split)
+       continue;
+
+      /* If the original insn came from a define_insn_and_split,
+        generate a new split to handle the predicated insn.  */
+      split = copy_rtx (insn_elem->split->data);
+      /* Predicate the pattern matched by the split.  */
+      pattern = rtx_alloc (COND_EXEC);
+      XEXP (pattern, 0) = pred;
+      XEXP (pattern, 1) = add_implicit_parallel (XVEC (split, 0));
+      XVEC (split, 0) = rtvec_alloc (1);
+      XVECEXP (split, 0, 0) = pattern;
+
+      /* Predicate all of the insns generated by the split.  */
+      for (i = 0; i < XVECLEN (split, 2); i++)
+       {
+         pattern = rtx_alloc (COND_EXEC);
+         XEXP (pattern, 0) = pred;
+         XEXP (pattern, 1) = XVECEXP (split, 2, i);
+         XVECEXP (split, 2, i) = pattern;
+       }
+      /* Add the new split to the queue.  */
+      queue_pattern (split, &other_tail, insn_elem->split->loc);
+    }
+}
+
+/* Try to apply define_substs to the given ELEM.
+   Only define_substs, specified via attributes would be applied.
+   If attribute, requiring define_subst, is set, but no define_subst
+   was applied, ELEM would be deleted.  */
+
+static void
+process_substs_on_one_elem (struct queue_elem *elem,
+                           struct queue_elem *queue)
+{
+  struct queue_elem *subst_elem;
+  int i, j, patterns_match;
+
+  for (subst_elem = define_subst_queue;
+       subst_elem; subst_elem = subst_elem->next)
+    {
+      int alternatives, alternatives_subst;
+      rtx subst_pattern;
+      rtvec subst_pattern_vec;
+
+      if (!has_subst_attribute (elem, subst_elem))
+       continue;
+
+      /* Compare original rtl-pattern from define_insn with input
+        pattern from define_subst.
+        Also, check if numbers of alternatives are the same in all
+        match_operands.  */
+      if (XVECLEN (elem->data, 1) != XVECLEN (subst_elem->data, 1))
+       continue;
+      patterns_match = 1;
+      alternatives = -1;
+      alternatives_subst = -1;
+      for (j = 0; j < XVECLEN (elem->data, 1); j++)
+       {
+         if (!subst_pattern_match (XVECEXP (elem->data, 1, j),
+                                   XVECEXP (subst_elem->data, 1, j),
+                                   subst_elem->loc))
+           {
+             patterns_match = 0;
+             break;
+           }
+
+         if (!get_alternatives_number (XVECEXP (elem->data, 1, j),
+                                       &alternatives, subst_elem->loc))
+           {
+             patterns_match = 0;
+             break;
+           }
+       }
+
+      /* Check if numbers of alternatives are the same in all
+        match_operands in output template of define_subst.  */
+      for (j = 0; j < XVECLEN (subst_elem->data, 3); j++)
+       {
+         if (!get_alternatives_number (XVECEXP (subst_elem->data, 3, j),
+                                       &alternatives_subst,
+                                       subst_elem->loc))
+           {
+             patterns_match = 0;
+             break;
+           }
+       }
+
+      if (!patterns_match)
+       continue;
+
+      /* Clear array in which we save occupied indexes of operands.  */
+      memset (used_operands_numbers, 0, sizeof (used_operands_numbers));
+
+      /* Create a pattern, based on the output one from define_subst.  */
+      subst_pattern_vec = rtvec_alloc (XVECLEN (subst_elem->data, 3));
+      for (j = 0; j < XVECLEN (subst_elem->data, 3); j++)
+       {
+         subst_pattern = copy_rtx (XVECEXP (subst_elem->data, 3, j));
+
+         /* Duplicate constraints in substitute-pattern.  */
+         subst_pattern = alter_constraints (subst_pattern, alternatives,
+                                            duplicate_each_alternative);
+
+         subst_pattern = adjust_operands_numbers (subst_pattern);
+
+         /* Substitute match_dup and match_op_dup in the new pattern and
+            duplicate constraints.  */
+         subst_pattern = subst_dup (subst_pattern, alternatives,
+                                    alternatives_subst);
+
+         replace_duplicating_operands_in_pattern (subst_pattern);
+
+         /* We don't need any constraints in DEFINE_EXPAND.  */
+         if (GET_CODE (elem->data) == DEFINE_EXPAND)
+           remove_constraints (subst_pattern);
+
+         RTVEC_ELT (subst_pattern_vec, j) = subst_pattern;
+       }
+      XVEC (elem->data, 1) = subst_pattern_vec;
+
+      for (i = 0; i < MAX_OPERANDS; i++)
+         match_operand_entries_in_pattern[i] = NULL;
+
+      if (GET_CODE (elem->data) == DEFINE_INSN)
+       {
+         XTMPL (elem->data, 3) =
+           alter_output_for_subst_insn (elem->data, alternatives_subst);
+         alter_attrs_for_subst_insn (elem, alternatives_subst);
+       }
+
+      /* Recalculate condition, joining conditions from original and
+        DEFINE_SUBST input patterns.  */
+      XSTR (elem->data, 2)
+       = rtx_reader_ptr->join_c_conditions (XSTR (subst_elem->data, 2),
+                                            XSTR (elem->data, 2));
+      /* Mark that subst was applied by changing attribute from "yes"
+        to "no".  */
+      change_subst_attribute (elem, subst_elem, subst_false);
+    }
+
+  /* If ELEM contains a subst attribute with value "yes", then we
+     expected that a subst would be applied, but it wasn't - so,
+     we need to remove that elementto avoid duplicating.  */
+  for (subst_elem = define_subst_queue;
+       subst_elem; subst_elem = subst_elem->next)
+    {
+      if (has_subst_attribute (elem, subst_elem))
+       {
+         remove_from_queue (elem, &queue);
+         return;
+       }
+    }
+}
+
+/* This is a subroutine of mark_operands_used_in_match_dup.
+   This routine is marks all MATCH_OPERANDs inside PATTERN as occupied.  */
+static void
+mark_operands_from_match_dup (rtx pattern)
+{
+  const char *fmt;
+  int i, j, len, opno;
+
+  if (GET_CODE (pattern) == MATCH_OPERAND
+      || GET_CODE (pattern) == MATCH_OPERATOR
+      || GET_CODE (pattern) == MATCH_PARALLEL)
+    {
+      opno = XINT (pattern, 0);
+      gcc_assert (opno >= 0 && opno < MAX_OPERANDS);
+      used_operands_numbers [opno] = 1;
+    }
+  fmt = GET_RTX_FORMAT (GET_CODE (pattern));
+  len = GET_RTX_LENGTH (GET_CODE (pattern));
+  for (i = 0; i < len; i++)
+    {
+      switch (fmt[i])
+       {
+       case 'e': case 'u':
+         mark_operands_from_match_dup (XEXP (pattern, i));
+         break;
+       case 'E':
+         for (j = XVECLEN (pattern, i) - 1; j >= 0; --j)
+           mark_operands_from_match_dup (XVECEXP (pattern, i, j));
+         break;
+       }
+    }
+}
+
+/* This is a subroutine of adjust_operands_numbers.
+   It goes through all expressions in PATTERN and when MATCH_DUP is
+   met, all MATCH_OPERANDs inside it is marked as occupied.  The
+   process of marking is done by routin mark_operands_from_match_dup.  */
+static void
+mark_operands_used_in_match_dup (rtx pattern)
+{
+  const char *fmt;
+  int i, j, len, opno;
+
+  if (GET_CODE (pattern) == MATCH_DUP)
+    {
+      opno = XINT (pattern, 0);
+      gcc_assert (opno >= 0 && opno < MAX_OPERANDS);
+      mark_operands_from_match_dup (operand_data[opno]);
+      return;
+    }
+  fmt = GET_RTX_FORMAT (GET_CODE (pattern));
+  len = GET_RTX_LENGTH (GET_CODE (pattern));
+  for (i = 0; i < len; i++)
+    {
+      switch (fmt[i])
+       {
+       case 'e': case 'u':
+         mark_operands_used_in_match_dup (XEXP (pattern, i));
+         break;
+       case 'E':
+         for (j = XVECLEN (pattern, i) - 1; j >= 0; --j)
+           mark_operands_used_in_match_dup (XVECEXP (pattern, i, j));
+         break;
+       }
+    }
+}
+
+/* This is subroutine of renumerate_operands_in_pattern.
+   It finds first not-occupied operand-index.  */
+static int
+find_first_unused_number_of_operand ()
+{
+  int i;
+  for (i = 0; i < MAX_OPERANDS; i++)
+    if (!used_operands_numbers[i])
+      return i;
+  return MAX_OPERANDS;
+}
+
+/* This is subroutine of adjust_operands_numbers.
+   It visits all expressions in PATTERN and assigns not-occupied
+   operand indexes to MATCH_OPERANDs and MATCH_OPERATORs of this
+   PATTERN.  */
+static void
+renumerate_operands_in_pattern (rtx pattern)
+{
+  const char *fmt;
+  enum rtx_code code;
+  int i, j, len, new_opno;
+  code = GET_CODE (pattern);
+
+  if (code == MATCH_OPERAND
+      || code == MATCH_OPERATOR)
+    {
+      new_opno = find_first_unused_number_of_operand ();
+      gcc_assert (new_opno >= 0 && new_opno < MAX_OPERANDS);
+      XINT (pattern, 0) = new_opno;
+      used_operands_numbers [new_opno] = 1;
+    }
+
+  fmt = GET_RTX_FORMAT (GET_CODE (pattern));
+  len = GET_RTX_LENGTH (GET_CODE (pattern));
+  for (i = 0; i < len; i++)
+    {
+      switch (fmt[i])
+       {
+       case 'e': case 'u':
+         renumerate_operands_in_pattern (XEXP (pattern, i));
+         break;
+       case 'E':
+         for (j = XVECLEN (pattern, i) - 1; j >= 0; --j)
+           renumerate_operands_in_pattern (XVECEXP (pattern, i, j));
+         break;
+       }
+    }
+}
+
+/* If output pattern of define_subst contains MATCH_DUP, then this
+   expression would be replaced with the pattern, matched with
+   MATCH_OPERAND from input pattern.  This pattern could contain any
+   number of MATCH_OPERANDs, MATCH_OPERATORs etc., so it's possible
+   that a MATCH_OPERAND from output_pattern (if any) would have the
+   same number, as MATCH_OPERAND from copied pattern.  To avoid such
+   indexes overlapping, we assign new indexes to MATCH_OPERANDs,
+   laying in the output pattern outside of MATCH_DUPs.  */
+static rtx
+adjust_operands_numbers (rtx pattern)
+{
+  mark_operands_used_in_match_dup (pattern);
+
+  renumerate_operands_in_pattern (pattern);
+
+  return pattern;
+}
+
+/* Generate RTL expression
+   (match_dup OPNO)
+   */
+static rtx
+generate_match_dup (int opno)
+{
+  rtx return_rtx = rtx_alloc (MATCH_DUP);
+  PUT_CODE (return_rtx, MATCH_DUP);
+  XINT (return_rtx, 0) = opno;
+  return return_rtx;
+}
+
+/* This routine checks all match_operands in PATTERN and if some of
+   have the same index, it replaces all of them except the first one  to
+   match_dup.
+   Usually, match_operands with the same indexes are forbidden, but
+   after define_subst copy an RTL-expression from original template,
+   indexes of existed and just-copied match_operands could coincide.
+   To fix it, we replace one of them with match_dup.  */
+static rtx
+replace_duplicating_operands_in_pattern (rtx pattern)
+{
+  const char *fmt;
+  int i, j, len, opno;
+  rtx mdup;
+
+  if (GET_CODE (pattern) == MATCH_OPERAND)
+    {
+      opno = XINT (pattern, 0);
+      gcc_assert (opno >= 0 && opno < MAX_OPERANDS);
+      if (match_operand_entries_in_pattern[opno] == NULL)
+       {
+         match_operand_entries_in_pattern[opno] = pattern;
+         return NULL;
+       }
+      else
+       {
+         /* Compare predicates before replacing with match_dup.  */
+         if (strcmp (XSTR (pattern, 1),
+                     XSTR (match_operand_entries_in_pattern[opno], 1)))
+           {
+             error ("duplicated match_operands with different predicates were"
+                    " found.");
+             return NULL;
+           }
+         return generate_match_dup (opno);
+       }
+    }
+  fmt = GET_RTX_FORMAT (GET_CODE (pattern));
+  len = GET_RTX_LENGTH (GET_CODE (pattern));
+  for (i = 0; i < len; i++)
+    {
+      switch (fmt[i])
+       {
+       case 'e': case 'u':
+         mdup = replace_duplicating_operands_in_pattern (XEXP (pattern, i));
+         if (mdup)
+           XEXP (pattern, i) = mdup;
+         break;
+       case 'E':
+         for (j = XVECLEN (pattern, i) - 1; j >= 0; --j)
+           {
+             mdup =
+               replace_duplicating_operands_in_pattern (XVECEXP
+                                                        (pattern, i, j));
+             if (mdup)
+               XVECEXP (pattern, i, j) = mdup;
+           }
+         break;
+       }
+    }
+  return NULL;
+}
+
+/* The routine modifies given input PATTERN of define_subst, replacing
+   MATCH_DUP and MATCH_OP_DUP with operands from define_insn original
+   pattern, whose operands are stored in OPERAND_DATA array.
+   It also duplicates constraints in operands - constraints from
+   define_insn operands are duplicated N_SUBST_ALT times, constraints
+   from define_subst operands are duplicated N_ALT times.
+   After the duplication, returned output rtl-pattern contains every
+   combination of input constraints Vs constraints from define_subst
+   output.  */
+static rtx
+subst_dup (rtx pattern, int n_alt, int n_subst_alt)
+{
+  const char *fmt;
+  enum rtx_code code;
+  int i, j, len, opno;
+
+  code = GET_CODE (pattern);
+  switch (code)
+    {
+    case MATCH_DUP:
+    case MATCH_OP_DUP:
+      opno = XINT (pattern, 0);
+
+      gcc_assert (opno >= 0 && opno < MAX_OPERANDS);
+
+      if (operand_data[opno])
+       {
+         pattern = copy_rtx (operand_data[opno]);
+
+         /* Duplicate constraints.  */
+         pattern = alter_constraints (pattern, n_subst_alt,
+                                      duplicate_alternatives);
+       }
+      break;
+
+    default:
+      break;
+    }
+
+  fmt = GET_RTX_FORMAT (GET_CODE (pattern));
+  len = GET_RTX_LENGTH (GET_CODE (pattern));
+  for (i = 0; i < len; i++)
+    {
+      switch (fmt[i])
+       {
+       case 'e': case 'u':
+         if (code != MATCH_DUP && code != MATCH_OP_DUP)
+           XEXP (pattern, i) = subst_dup (XEXP (pattern, i),
+                                          n_alt, n_subst_alt);
+         break;
+       case 'V':
+         if (XVEC (pattern, i) == NULL)
+           break;
+         /* FALLTHRU */
+       case 'E':
+         if (code != MATCH_DUP && code != MATCH_OP_DUP)
+           for (j = XVECLEN (pattern, i) - 1; j >= 0; --j)
+             XVECEXP (pattern, i, j) = subst_dup (XVECEXP (pattern, i, j),
+                                                  n_alt, n_subst_alt);
+         break;
+
+       case 'r': case 'p': case 'i': case 'w':
+       case '0': case 's': case 'S': case 'T':
+         break;
+
+       default:
+         gcc_unreachable ();
+       }
+    }
+  return pattern;
+}
+
+/* If we have any DEFINE_COND_EXEC patterns, expand the DEFINE_INSN
+   patterns appropriately.  */
+
+static void
+process_define_cond_exec (void)
+{
+  struct queue_elem *elem;
+
+  identify_predicable_attribute ();
+  if (have_error)
+    return;
+
+  for (elem = define_cond_exec_queue; elem ; elem = elem->next)
+    process_one_cond_exec (elem);
+}
+
+/* If we have any DEFINE_SUBST patterns, expand DEFINE_INSN and
+   DEFINE_EXPAND patterns appropriately.  */
+
+static void
+process_define_subst (void)
+{
+  struct queue_elem *elem, *elem_attr;
+
+  /* Check if each define_subst has corresponding define_subst_attr.  */
+  for (elem = define_subst_queue; elem ; elem = elem->next)
+    {
+      for (elem_attr = define_subst_attr_queue;
+          elem_attr;
+          elem_attr = elem_attr->next)
+       if (strcmp (XSTR (elem->data, 0), XSTR (elem_attr->data, 1)) == 0)
+           goto found;
+
+      error_at (elem->loc,
+               "%s: `define_subst' must have at least one "
+               "corresponding `define_subst_attr'",
+               XSTR (elem->data, 0));
+      return;
+
+      found:
+       continue;
+    }
+
+  for (elem = define_insn_queue; elem ; elem = elem->next)
+    process_substs_on_one_elem (elem, define_insn_queue);
+  for (elem = other_queue; elem ; elem = elem->next)
+    {
+      if (GET_CODE (elem->data) != DEFINE_EXPAND)
+       continue;
+      process_substs_on_one_elem (elem, other_queue);
+    }
+}
+\f
+/* A subclass of rtx_reader which reads .md files and calls process_rtx on
+   the top-level elements.  */
+
+class gen_reader : public rtx_reader
+{
+ public:
+  gen_reader () : rtx_reader (false) {}
+  void handle_unknown_directive (file_location, const char *);
+};
+
+void
+gen_reader::handle_unknown_directive (file_location loc, const char *rtx_name)
+{
+  auto_vec<rtx, 32> subrtxs;
+  if (!read_rtx (rtx_name, &subrtxs))
+    return;
+
+  rtx x;
+  unsigned int i;
+  FOR_EACH_VEC_ELT (subrtxs, i, x)
+    process_rtx (x, loc);
+}
+
+/* Comparison function for the mnemonic hash table.  */
+
+static int
+htab_eq_string (const void *s1, const void *s2)
+{
+  return strcmp ((const char*)s1, (const char*)s2) == 0;
+}
+
+/* Add mnemonic STR with length LEN to the mnemonic hash table
+   MNEMONIC_HTAB.  A trailing zero end character is appended to STR
+   and a permanent heap copy of STR is created.  */
+
+static void
+add_mnemonic_string (htab_t mnemonic_htab, const char *str, size_t len)
+{
+  char *new_str;
+  void **slot;
+  char *str_zero = (char*)alloca (len + 1);
+
+  memcpy (str_zero, str, len);
+  str_zero[len] = '\0';
+
+  slot = htab_find_slot (mnemonic_htab, str_zero, INSERT);
+
+  if (*slot)
+    return;
+
+  /* Not found; create a permanent copy and add it to the hash table.  */
+  new_str = XNEWVAR (char, len + 1);
+  memcpy (new_str, str_zero, len + 1);
+  *slot = new_str;
+}
+
+/* Scan INSN for mnemonic strings and add them to the mnemonic hash
+   table in MNEMONIC_HTAB.
+
+   The mnemonics cannot be found if they are emitted using C code.
+
+   If a mnemonic string contains ';' or a newline the string assumed
+   to consist of more than a single instruction.  The attribute value
+   will then be set to the user defined default value.  */
+
+static void
+gen_mnemonic_setattr (htab_t mnemonic_htab, rtx insn)
+{
+  const char *template_code, *cp;
+  int i;
+  int vec_len;
+  rtx set_attr;
+  char *attr_name;
+  rtvec new_vec;
+  struct obstack *string_obstack = rtx_reader_ptr->get_string_obstack ();
+
+  template_code = XTMPL (insn, 3);
+
+  /* Skip patterns which use C code to emit the template.  */
+  if (template_code[0] == '*')
+    return;
+
+  if (template_code[0] == '@')
+    cp = &template_code[1];
+  else
+    cp = &template_code[0];
+
+  for (i = 0; *cp; )
+    {
+      const char *ep, *sp;
+      size_t size = 0;
 
-      if (!insn_elem->split)
-       continue;
+      while (ISSPACE (*cp))
+       cp++;
+
+      for (ep = sp = cp; !IS_VSPACE (*ep) && *ep != '\0'; ++ep)
+       if (!ISSPACE (*ep))
+         sp = ep + 1;
+
+      if (i > 0)
+       obstack_1grow (string_obstack, ',');
+
+      while (cp < sp && ((*cp >= '0' && *cp <= '9')
+                        || (*cp >= 'a' && *cp <= 'z')))
 
-      /* If the original insn came from a define_insn_and_split,
-        generate a new split to handle the predicated insn.  */
-      split = copy_rtx (insn_elem->split->data);
-      /* Predicate the pattern matched by the split.  */
-      pattern = rtx_alloc (COND_EXEC);
-      XEXP (pattern, 0) = pred;
-      if (XVECLEN (split, 0) == 1)
-       {
-         XEXP (pattern, 1) = XVECEXP (split, 0, 0);
-         XVECEXP (split, 0, 0) = pattern;
-         PUT_NUM_ELEM (XVEC (split, 0), 1);
-       }
-      else
        {
-         XEXP (pattern, 1) = rtx_alloc (PARALLEL);
-         XVEC (XEXP (pattern, 1), 0) = XVEC (split, 0);
-         XVEC (split, 0) = rtvec_alloc (1);
-         XVECEXP (split, 0, 0) = pattern;
+         obstack_1grow (string_obstack, *cp);
+         cp++;
+         size++;
        }
-      /* Predicate all of the insns generated by the split.  */
-      for (i = 0; i < XVECLEN (split, 2); i++)
+
+      while (cp < sp)
        {
-         pattern = rtx_alloc (COND_EXEC);
-         XEXP (pattern, 0) = pred;
-         XEXP (pattern, 1) = XVECEXP (split, 2, i);
-         XVECEXP (split, 2, i) = pattern;
+         if (*cp == ';' || (*cp == '\\' && cp[1] == 'n'))
+           {
+             /* Don't set a value if there are more than one
+                instruction in the string.  */
+             obstack_blank_fast (string_obstack, -size);
+             size = 0;
+
+             cp = sp;
+             break;
+           }
+         cp++;
        }
-      /* Add the new split to the queue.  */
-      queue_pattern (split, &other_tail, read_md_filename,
-                    insn_elem->split->lineno);
+      if (size == 0)
+       obstack_1grow (string_obstack, '*');
+      else
+       add_mnemonic_string (mnemonic_htab,
+                            (char *) obstack_next_free (string_obstack) - size,
+                            size);
+      i++;
     }
-}
 
-/* If we have any DEFINE_COND_EXEC patterns, expand the DEFINE_INSN
-   patterns appropriately.  */
+  /* An insn definition might emit an empty string.  */
+  if (obstack_object_size (string_obstack) == 0)
+    return;
 
-static void
-process_define_cond_exec (void)
-{
-  struct queue_elem *elem;
+  obstack_1grow (string_obstack, '\0');
 
-  identify_predicable_attribute ();
-  if (have_error)
-    return;
+  set_attr = rtx_alloc (SET_ATTR);
+  XSTR (set_attr, 1) = XOBFINISH (string_obstack, char *);
+  attr_name = XNEWVAR (char, strlen (MNEMONIC_ATTR_NAME) + 1);
+  strcpy (attr_name, MNEMONIC_ATTR_NAME);
+  XSTR (set_attr, 0) = attr_name;
 
-  for (elem = define_cond_exec_queue; elem ; elem = elem->next)
-    process_one_cond_exec (elem);
+  if (!XVEC (insn, 4))
+    vec_len = 0;
+  else
+    vec_len = XVECLEN (insn, 4);
+
+  new_vec = rtvec_alloc (vec_len + 1);
+  for (i = 0; i < vec_len; i++)
+    RTVEC_ELT (new_vec, i) = XVECEXP (insn, 4, i);
+  RTVEC_ELT (new_vec, vec_len) = set_attr;
+  XVEC (insn, 4) = new_vec;
 }
 
-static char *
-save_string (const char *s, int len)
+/* This function is called for the elements in the mnemonic hashtable
+   and generates a comma separated list of the mnemonics.  */
+
+static int
+mnemonic_htab_callback (void **slot, void *info ATTRIBUTE_UNUSED)
 {
-  char *result = XNEWVEC (char, len + 1);
+  struct obstack *string_obstack = rtx_reader_ptr->get_string_obstack ();
 
-  memcpy (result, s, len);
-  result[len] = 0;
-  return result;
+  obstack_grow (string_obstack, (char*) *slot, strlen ((char*) *slot));
+  obstack_1grow (string_obstack, ',');
+  return 1;
 }
 
-\f
-/* The entry point for initializing the reader.  */
+/* Generate (set_attr "mnemonic" "..") RTXs and append them to every
+   insn definition in case the back end requests it by defining the
+   mnemonic attribute.  The values for the attribute will be extracted
+   from the output patterns of the insn definitions as far as
+   possible.  */
 
-int
-init_md_reader_args_cb (int argc, char **argv, bool (*parse_opt)(const char *))
+static void
+gen_mnemonic_attr (void)
 {
-  int c, i, lineno;
-  char *lastsl;
-  rtx desc;
-  bool no_more_options;
-  bool already_read_stdin;
+  struct queue_elem *elem;
+  rtx mnemonic_attr = NULL;
+  htab_t mnemonic_htab;
+  const char *str, *p;
+  int i;
+  struct obstack *string_obstack = rtx_reader_ptr->get_string_obstack ();
+
+  if (have_error)
+    return;
+
+  /* Look for the DEFINE_ATTR for `mnemonic'.  */
+  for (elem = define_attr_queue; elem != *define_attr_tail; elem = elem->next)
+    if (GET_CODE (elem->data) == DEFINE_ATTR
+       && strcmp (XSTR (elem->data, 0), MNEMONIC_ATTR_NAME) == 0)
+      {
+       mnemonic_attr = elem->data;
+       break;
+      }
+
+  /* A (define_attr "mnemonic" "...") indicates that the back-end
+     wants a mnemonic attribute to be generated.  */
+  if (!mnemonic_attr)
+    return;
 
-  /* Unlock the stdio streams.  */
-  unlock_std_streams ();
+  mnemonic_htab = htab_create_alloc (MNEMONIC_HTAB_SIZE, htab_hash_string,
+                                    htab_eq_string, 0, xcalloc, free);
 
-  /* First we loop over all the options.  */
-  for (i = 1; i < argc; i++)
+  for (elem = define_insn_queue; elem; elem = elem->next)
     {
-      if (argv[i][0] != '-')
-       continue;
+      rtx insn = elem->data;
+      bool found = false;
 
-      c = argv[i][1];
-      switch (c)
-       {
-       case 'I':               /* Add directory to path for includes.  */
+      /* Check if the insn definition already has
+        (set_attr "mnemonic" ...) or (set (attr "mnemonic") ...).  */
+      if (XVEC (insn, 4))
+       for (i = 0; i < XVECLEN (insn, 4); i++)
          {
-           struct file_name_list *dirtmp;
+           rtx set_attr = XVECEXP (insn, 4, i);
 
-           dirtmp = XNEW (struct file_name_list);
-           dirtmp->next = 0;   /* New one goes on the end */
-           if (first_dir_md_include == 0)
-             first_dir_md_include = dirtmp;
-           else
-             last_dir_md_include->next = dirtmp;
-           last_dir_md_include = dirtmp;       /* Tail follows the last one */
-           if (argv[i][1] == 'I' && argv[i][2] != 0)
-             dirtmp->fname = argv[i] + 2;
-           else if (i + 1 == argc)
-             fatal ("directory name missing after -I option");
-           else
-             dirtmp->fname = argv[++i];
-           if (strlen (dirtmp->fname) > max_include_len)
-             max_include_len = strlen (dirtmp->fname);
+           switch (GET_CODE (set_attr))
+             {
+             case SET_ATTR:
+             case SET_ATTR_ALTERNATIVE:
+               if (strcmp (XSTR (set_attr, 0), MNEMONIC_ATTR_NAME) == 0)
+                 found = true;
+               break;
+             case SET:
+               if (GET_CODE (SET_DEST (set_attr)) == ATTR
+                   && strcmp (XSTR (SET_DEST (set_attr), 0),
+                              MNEMONIC_ATTR_NAME) == 0)
+                 found = true;
+               break;
+             default:
+               break;
+             }
          }
-         break;
 
-       case '\0':
-         /* An argument consisting of exactly one dash is a request to
-            read stdin.  This will be handled in the second loop.  */
-         continue;
+      if (!found)
+       gen_mnemonic_setattr (mnemonic_htab, insn);
+    }
 
-       case '-':
-         /* An argument consisting of just two dashes causes option
-            parsing to cease.  */
-         if (argv[i][2] == '\0')
-           goto stop_parsing_options;
+  /* Add the user defined values to the hash table.  */
+  str = XSTR (mnemonic_attr, 1);
+  while ((p = scan_comma_elt (&str)) != NULL)
+    add_mnemonic_string (mnemonic_htab, p, str - p);
 
-       default:
-         /* The program may have provided a callback so it can
-            accept its own options.  */
-         if (parse_opt && parse_opt (argv[i]))
-           break;
+  htab_traverse (mnemonic_htab, mnemonic_htab_callback, NULL);
+
+  /* Replace the last ',' with the zero end character.  */
+  *((char *) obstack_next_free (string_obstack) - 1) = '\0';
+  XSTR (mnemonic_attr, 1) = XOBFINISH (string_obstack, char *);
+}
+
+/* Check if there are DEFINE_ATTRs with the same name.  */
+static void
+check_define_attr_duplicates ()
+{
+  struct queue_elem *elem;
+  htab_t attr_htab;
+  char * attr_name;
+  void **slot;
+
+  attr_htab = htab_create (500, htab_hash_string, htab_eq_string, NULL);
+
+  for (elem = define_attr_queue; elem; elem = elem->next)
+    {
+      attr_name = xstrdup (XSTR (elem->data, 0));
+
+      slot = htab_find_slot (attr_htab, attr_name, INSERT);
 
-         fatal ("invalid option `%s'", argv[i]);
+      /* Duplicate.  */
+      if (*slot)
+       {
+         error_at (elem->loc, "redefinition of attribute '%s'", attr_name);
+         htab_delete (attr_htab);
+         return;
        }
+
+      *slot = attr_name;
     }
 
- stop_parsing_options:
+  htab_delete (attr_htab);
+}
+
+/* The entry point for initializing the reader.  */
 
+rtx_reader *
+init_rtx_reader_args_cb (int argc, const char **argv,
+                        bool (*parse_opt) (const char *))
+{
   /* Prepare to read input.  */
   condition_table = htab_create (500, hash_c_test, cmp_c_test, NULL);
   init_predicate_table ();
   obstack_init (rtl_obstack);
-  sequence_num = 0;
-  no_more_options = false;
-  already_read_stdin = false;
 
+  /* Start at 1, to make 0 available for CODE_FOR_nothing.  */
+  insn_sequence_num = 1;
 
-  /* Now loop over all input files.  */
-  for (i = 1; i < argc; i++)
-    {
-      if (argv[i][0] == '-')
-       {
-         if (argv[i][1] == '\0')
-           {
-             /* Read stdin.  */
-             if (already_read_stdin)
-               fatal ("cannot read standard input twice");
-
-             base_dir = NULL;
-             read_md_file = stdin;
-             read_md_filename = in_fname = "<stdin>";
-             read_md_lineno = 1;
-             already_read_stdin = true;
-
-             while (read_rtx (&desc, &lineno))
-               process_rtx (desc, lineno);
-             fclose (read_md_file);
-             continue;
-           }
-         else if (argv[i][1] == '-' && argv[i][2] == '\0')
-           {
-             /* No further arguments are to be treated as options.  */
-             no_more_options = true;
-             continue;
-           }
-         else if (!no_more_options)
-           continue;
-       }
+  /* These sequences are not used as indices, so can start at 1 also.  */
+  split_sequence_num = 1;
+  peephole2_sequence_num = 1;
 
-      /* If we get here we are looking at a non-option argument, i.e.
-        a file to be processed.  */
+  gen_reader *reader = new gen_reader ();
+  reader->read_md_files (argc, argv, parse_opt);
 
-      in_fname = argv[i];
-      lastsl = strrchr (in_fname, '/');
-      if (lastsl != NULL)
-       base_dir = save_string (in_fname, lastsl - in_fname + 1 );
-      else
-       base_dir = NULL;
+  if (define_attr_queue != NULL)
+    check_define_attr_duplicates ();
 
-      read_md_file = fopen (in_fname, "r");
-      if (read_md_file == 0)
-       {
-         perror (in_fname);
-         return FATAL_EXIT_CODE;
-       }
-      read_md_filename = in_fname;
-      read_md_lineno = 1;
+  /* Process define_cond_exec patterns.  */
+  if (define_cond_exec_queue != NULL)
+    process_define_cond_exec ();
 
-      while (read_rtx (&desc, &lineno))
-       process_rtx (desc, lineno);
-      fclose (read_md_file);
-    }
+  /* Process define_subst patterns.  */
+  if (define_subst_queue != NULL)
+    process_define_subst ();
 
-  /* If we get to this point without having seen any files to process,
-     read standard input now.  */
-  if (!in_fname)
-    {
-      base_dir = NULL;
-      read_md_file = stdin;
-      read_md_filename = in_fname = "<stdin>";
-      read_md_lineno = 1;
+  if (define_attr_queue != NULL)
+    gen_mnemonic_attr ();
 
-      while (read_rtx (&desc, &lineno))
-       process_rtx (desc, lineno);
-      fclose (read_md_file);
+  if (have_error)
+    {
+      delete reader;
+      return NULL;
     }
 
-  /* Process define_cond_exec patterns.  */
-  if (define_cond_exec_queue != NULL)
-    process_define_cond_exec ();
-
-  return have_error ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
+  return reader;
 }
 
 /* Programs that don't have their own options can use this entry point
    instead.  */
-int
-init_md_reader_args (int argc, char **argv)
+rtx_reader *
+init_rtx_reader_args (int argc, const char **argv)
 {
-  return init_md_reader_args_cb (argc, argv, 0);
+  return init_rtx_reader_args_cb (argc, argv, 0);
 }
 \f
-/* The entry point for reading a single rtx from an md file.  */
+/* Try to read a single rtx from the file.  Return true on success,
+   describing it in *INFO.  */
 
-rtx
-read_md_rtx (int *lineno, int *seqnr)
+bool
+read_md_rtx (md_rtx_info *info)
 {
-  struct queue_elem **queue, *elem;
-  rtx desc;
-
- discard:
-
-  /* Read all patterns from a given queue before moving on to the next.  */
-  if (define_attr_queue != NULL)
-    queue = &define_attr_queue;
-  else if (define_pred_queue != NULL)
-    queue = &define_pred_queue;
-  else if (define_insn_queue != NULL)
-    queue = &define_insn_queue;
-  else if (other_queue != NULL)
-    queue = &other_queue;
-  else
-    return NULL_RTX;
-
-  elem = *queue;
-  *queue = elem->next;
-  desc = elem->data;
-  read_md_filename = elem->filename;
-  *lineno = elem->lineno;
-  *seqnr = sequence_num;
-
-  free (elem);
+  int truth, *counter;
+  rtx def;
 
   /* Discard insn patterns which we know can never match (because
      their C test is provably always false).  If insn_elision is
      false, our caller needs to see all the patterns.  Note that the
      elided patterns are never counted by the sequence numbering; it
-     it is the caller's responsibility, when insn_elision is false, not
+     is the caller's responsibility, when insn_elision is false, not
      to use elided pattern numbers for anything.  */
-  switch (GET_CODE (desc))
+  do
+    {
+      struct queue_elem **queue, *elem;
+
+      /* Read all patterns from a given queue before moving on to the next.  */
+      if (define_attr_queue != NULL)
+       queue = &define_attr_queue;
+      else if (define_pred_queue != NULL)
+       queue = &define_pred_queue;
+      else if (define_insn_queue != NULL)
+       queue = &define_insn_queue;
+      else if (other_queue != NULL)
+       queue = &other_queue;
+      else
+       return false;
+
+      elem = *queue;
+      *queue = elem->next;
+      def = elem->data;
+      info->def = def;
+      info->loc = elem->loc;
+      free (elem);
+
+      truth = maybe_eval_c_test (get_c_test (def));
+    }
+  while (truth == 0 && insn_elision);
+
+  /* Perform code-specific processing and pick the appropriate sequence
+     number counter.  */
+  switch (GET_CODE (def))
     {
     case DEFINE_INSN:
     case DEFINE_EXPAND:
-      if (maybe_eval_c_test (XSTR (desc, 2)) != 0)
-       sequence_num++;
-      else if (insn_elision)
-       goto discard;
-
-      /* *seqnr is used here so the name table will match caller's
+      /* insn_sequence_num is used here so the name table will match caller's
         idea of insn numbering, whether or not elision is active.  */
-      record_insn_name (*seqnr, XSTR (desc, 0));
+      record_insn_name (insn_sequence_num, XSTR (def, 0));
+
+      /* Fall through.  */
+    case DEFINE_PEEPHOLE:
+      counter = &insn_sequence_num;
       break;
 
     case DEFINE_SPLIT:
-    case DEFINE_PEEPHOLE:
+      counter = &split_sequence_num;
+      break;
+
     case DEFINE_PEEPHOLE2:
-      if (maybe_eval_c_test (XSTR (desc, 1)) != 0)
-       sequence_num++;
-      else if (insn_elision)
-           goto discard;
+      counter = &peephole2_sequence_num;
       break;
 
     default:
+      counter = NULL;
       break;
     }
 
-  return desc;
+  if (counter)
+    {
+      info->index = *counter;
+      if (truth != 0)
+       *counter += 1;
+    }
+  else
+    info->index = -1;
+
+  if (!rtx_locs)
+    rtx_locs = new hash_map <rtx, file_location>;
+  rtx_locs->put (info->def, info->loc);
+
+  return true;
+}
+
+/* Return the file location of DEFINE_* rtx X, which was previously
+   returned by read_md_rtx.  */
+file_location
+get_file_location (rtx x)
+{
+  gcc_assert (rtx_locs);
+  file_location *entry = rtx_locs->get (x);
+  gcc_assert (entry);
+  return *entry;
+}
+
+/* Return the number of possible INSN_CODEs.  Only meaningful once the
+   whole file has been processed.  */
+unsigned int
+get_num_insn_codes ()
+{
+  return insn_sequence_num;
+}
+
+/* Return the C test that says whether definition rtx DEF can be used,
+   or "" if it can be used unconditionally.  */
+
+const char *
+get_c_test (rtx x)
+{
+  switch (GET_CODE (x))
+    {
+    case DEFINE_INSN:
+    case DEFINE_EXPAND:
+    case DEFINE_SUBST:
+      return XSTR (x, 2);
+
+    case DEFINE_SPLIT:
+    case DEFINE_PEEPHOLE:
+    case DEFINE_PEEPHOLE2:
+      return XSTR (x, 1);
+
+    default:
+      return "";
+    }
 }
 
 /* Helper functions for insn elision.  */
@@ -1241,7 +2826,9 @@ add_predicate_code (struct pred_data *pred, enum rtx_code code)
          && code != MEM
          && code != CONCAT
          && code != PARALLEL
-         && code != STRICT_LOW_PART)
+         && code != STRICT_LOW_PART
+         && code != ZERO_EXTRACT
+         && code != SCRATCH)
        pred->allows_non_lvalue = true;
 
       if (pred->num_codes == 1)
@@ -1278,13 +2865,19 @@ struct std_pred_table
 
 static const struct std_pred_table std_preds[] = {
   {"general_operand", false, true, {SUBREG, REG, MEM}},
-  {"address_operand", true, true, {SUBREG, REG, MEM, PLUS, MINUS, MULT}},
+  {"address_operand", true, true, {SUBREG, REG, MEM, PLUS, MINUS, MULT,
+                                  ZERO_EXTEND, SIGN_EXTEND, AND}},
   {"register_operand", false, false, {SUBREG, REG}},
   {"pmode_register_operand", true, false, {SUBREG, REG}},
   {"scratch_operand", false, false, {SCRATCH, REG}},
   {"immediate_operand", false, true, {UNKNOWN}},
   {"const_int_operand", false, false, {CONST_INT}},
+#if TARGET_SUPPORTS_WIDE_INT
+  {"const_scalar_int_operand", false, false, {CONST_INT, CONST_WIDE_INT}},
+  {"const_double_operand", false, false, {CONST_DOUBLE}},
+#else
   {"const_double_operand", false, false, {CONST_INT, CONST_DOUBLE}},
+#endif
   {"nonimmediate_operand", false, false, {SUBREG, REG, MEM}},
   {"nonmemory_operand", false, true, {SUBREG, REG}},
   {"push_operand", false, false, {MEM}},
@@ -1363,7 +2956,7 @@ record_insn_name (int code, const char *name)
       new_size = (insn_name_ptr_size ? insn_name_ptr_size * 2 : 512);
       insn_name_ptr = XRESIZEVEC (char *, insn_name_ptr, new_size);
       memset (insn_name_ptr + insn_name_ptr_size, 0,
-             sizeof(char *) * (new_size - insn_name_ptr_size));
+             sizeof (char *) * (new_size - insn_name_ptr_size));
       insn_name_ptr_size = new_size;
     }
 
@@ -1380,3 +2973,279 @@ record_insn_name (int code, const char *name)
 
   insn_name_ptr[code] = new_name;
 }
+\f
+/* Make STATS describe the operands that appear in rtx X.  */
+
+static void
+get_pattern_stats_1 (struct pattern_stats *stats, rtx x)
+{
+  RTX_CODE code;
+  int i;
+  int len;
+  const char *fmt;
+
+  if (x == NULL_RTX)
+    return;
+
+  code = GET_CODE (x);
+  switch (code)
+    {
+    case MATCH_OPERAND:
+    case MATCH_OPERATOR:
+    case MATCH_PARALLEL:
+      stats->max_opno = MAX (stats->max_opno, XINT (x, 0));
+      break;
+
+    case MATCH_DUP:
+    case MATCH_OP_DUP:
+    case MATCH_PAR_DUP:
+      stats->num_dups++;
+      stats->max_dup_opno = MAX (stats->max_dup_opno, XINT (x, 0));
+      break;
+
+    case MATCH_SCRATCH:
+      if (stats->min_scratch_opno == -1)
+       stats->min_scratch_opno = XINT (x, 0);
+      else
+       stats->min_scratch_opno = MIN (stats->min_scratch_opno, XINT (x, 0));
+      stats->max_scratch_opno = MAX (stats->max_scratch_opno, XINT (x, 0));
+      break;
+
+    default:
+      break;
+    }
+
+  fmt = GET_RTX_FORMAT (code);
+  len = GET_RTX_LENGTH (code);
+  for (i = 0; i < len; i++)
+    {
+      if (fmt[i] == 'e' || fmt[i] == 'u')
+       get_pattern_stats_1 (stats, XEXP (x, i));
+      else if (fmt[i] == 'E')
+       {
+         int j;
+         for (j = 0; j < XVECLEN (x, i); j++)
+           get_pattern_stats_1 (stats, XVECEXP (x, i, j));
+       }
+    }
+}
+
+/* Make STATS describe the operands that appear in instruction pattern
+   PATTERN.  */
+
+void
+get_pattern_stats (struct pattern_stats *stats, rtvec pattern)
+{
+  int i, len;
+
+  stats->max_opno = -1;
+  stats->max_dup_opno = -1;
+  stats->min_scratch_opno = -1;
+  stats->max_scratch_opno = -1;
+  stats->num_dups = 0;
+
+  len = GET_NUM_ELEM (pattern);
+  for (i = 0; i < len; i++)
+    get_pattern_stats_1 (stats, RTVEC_ELT (pattern, i));
+
+  stats->num_generator_args = stats->max_opno + 1;
+  stats->num_insn_operands = MAX (stats->max_opno,
+                                 stats->max_scratch_opno) + 1;
+  stats->num_operand_vars = MAX (stats->max_opno,
+                                 MAX (stats->max_dup_opno,
+                                      stats->max_scratch_opno)) + 1;
+}
+
+/* Return the emit_* function that should be used for pattern X, or NULL
+   if we can't pick a particular type at compile time and should instead
+   fall back to "emit".  */
+
+const char *
+get_emit_function (rtx x)
+{
+  switch (classify_insn (x))
+    {
+    case INSN:
+      return "emit_insn";
+
+    case CALL_INSN:
+      return "emit_call_insn";
+
+    case JUMP_INSN:
+      return "emit_jump_insn";
+
+    case UNKNOWN:
+      return NULL;
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Return true if we must emit a barrier after pattern X.  */
+
+bool
+needs_barrier_p (rtx x)
+{
+  return (GET_CODE (x) == SET
+         && GET_CODE (SET_DEST (x)) == PC
+         && GET_CODE (SET_SRC (x)) == LABEL_REF);
+}
+
+#define NS "NULL"
+#define ZS "'\\0'"
+#define OPTAB_CL(o, p, c, b, l)    { #o, p, #b, ZS, #l, o, c, UNKNOWN, 1 },
+#define OPTAB_CX(o, p) { #o, p, NULL, NULL, NULL, o, UNKNOWN, UNKNOWN, 1 },
+#define OPTAB_CD(o, p) { #o, p, NS, ZS, NS, o, UNKNOWN, UNKNOWN, 2 },
+#define OPTAB_NL(o, p, c, b, s, l) { #o, p, #b, #s, #l, o, c, c, 3 },
+#define OPTAB_NC(o, p, c)          { #o, p, NS, ZS, NS, o, c, c, 3 },
+#define OPTAB_NX(o, p) { #o, p, NULL, NULL, NULL, o, UNKNOWN, UNKNOWN, 3 },
+#define OPTAB_VL(o, p, c, b, s, l) { #o, p, #b, #s, #l, o, c, UNKNOWN, 3 },
+#define OPTAB_VC(o, p, c)          { #o, p, NS, ZS, NS, o, c, UNKNOWN, 3 },
+#define OPTAB_VX(o, p) { #o, p, NULL, NULL, NULL, o, UNKNOWN, UNKNOWN, 3 },
+#define OPTAB_DC(o, p, c)          { #o, p, NS, ZS, NS, o, c, c, 4 },
+#define OPTAB_D(o, p)  { #o, p, NS, ZS, NS, o, UNKNOWN, UNKNOWN, 4 },
+
+/* An array of all optabs.  Note that the same optab can appear more
+   than once, with a different pattern.  */
+optab_def optabs[] = {
+  { "unknown_optab", NULL, NS, ZS, NS, unknown_optab, UNKNOWN, UNKNOWN, 0 },
+#include "optabs.def"
+};
+
+/* The number of entries in optabs[].  */
+unsigned int num_optabs = ARRAY_SIZE (optabs);
+
+#undef OPTAB_CL
+#undef OPTAB_CX
+#undef OPTAB_CD
+#undef OPTAB_NL
+#undef OPTAB_NC
+#undef OPTAB_NX
+#undef OPTAB_VL
+#undef OPTAB_VC
+#undef OPTAB_VX
+#undef OPTAB_DC
+#undef OPTAB_D
+
+/* Return true if instruction NAME matches pattern PAT, storing information
+   about the match in P if so.  */
+
+static bool
+match_pattern (optab_pattern *p, const char *name, const char *pat)
+{
+  bool force_float = false;
+  bool force_int = false;
+  bool force_partial_int = false;
+  bool force_fixed = false;
+
+  if (pat == NULL)
+    return false;
+  for (; ; ++pat)
+    {
+      if (*pat != '$')
+       {
+         if (*pat != *name++)
+           return false;
+         if (*pat == '\0')
+           return true;
+         continue;
+       }
+      switch (*++pat)
+       {
+       case 'I':
+         force_int = 1;
+         break;
+       case 'P':
+         force_partial_int = 1;
+         break;
+       case 'F':
+         force_float = 1;
+         break;
+       case 'Q':
+         force_fixed = 1;
+         break;
+
+       case 'a':
+       case 'b':
+         {
+           int i;
+
+           /* This loop will stop at the first prefix match, so
+              look through the modes in reverse order, in case
+              there are extra CC modes and CC is a prefix of the
+              CC modes (as it should be).  */
+           for (i = (MAX_MACHINE_MODE) - 1; i >= 0; i--)
+             {
+               const char *p, *q;
+               for (p = GET_MODE_NAME (i), q = name; *p; p++, q++)
+                 if (TOLOWER (*p) != *q)
+                   break;
+               if (*p == 0
+                   && (! force_int || mode_class[i] == MODE_INT
+                       || mode_class[i] == MODE_VECTOR_INT)
+                   && (! force_partial_int
+                       || mode_class[i] == MODE_INT
+                       || mode_class[i] == MODE_PARTIAL_INT
+                       || mode_class[i] == MODE_VECTOR_INT)
+                   && (! force_float
+                       || mode_class[i] == MODE_FLOAT
+                       || mode_class[i] == MODE_DECIMAL_FLOAT
+                       || mode_class[i] == MODE_COMPLEX_FLOAT
+                       || mode_class[i] == MODE_VECTOR_FLOAT)
+                   && (! force_fixed
+                       || mode_class[i] == MODE_FRACT
+                       || mode_class[i] == MODE_UFRACT
+                       || mode_class[i] == MODE_ACCUM
+                       || mode_class[i] == MODE_UACCUM
+                       || mode_class[i] == MODE_VECTOR_FRACT
+                       || mode_class[i] == MODE_VECTOR_UFRACT
+                       || mode_class[i] == MODE_VECTOR_ACCUM
+                       || mode_class[i] == MODE_VECTOR_UACCUM))
+                 break;
+             }
+
+           if (i < 0)
+             return false;
+           name += strlen (GET_MODE_NAME (i));
+           if (*pat == 'a')
+             p->m1 = i;
+           else
+             p->m2 = i;
+
+           force_int = false;
+           force_partial_int = false;
+           force_float = false;
+           force_fixed = false;
+         }
+         break;
+
+       default:
+         gcc_unreachable ();
+       }
+    }
+}
+
+/* Return true if NAME is the name of an optab, describing it in P if so.  */
+
+bool
+find_optab (optab_pattern *p, const char *name)
+{
+  if (*name == 0 || *name == '*')
+    return false;
+
+  /* See if NAME matches one of the patterns we have for the optabs
+     we know about.  */
+  for (unsigned int pindex = 0; pindex < ARRAY_SIZE (optabs); pindex++)
+    {
+      p->m1 = p->m2 = 0;
+      if (match_pattern (p, name, optabs[pindex].pattern))
+       {
+         p->name = name;
+         p->op = optabs[pindex].op;
+         p->sort_num = (p->op << 16) | (p->m2 << 8) | p->m1;
+         return true;
+       }
+    }
+  return false;
+}