]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/gensupport.c
coroutines: Make call argument handling more robust [PR95440]
[thirdparty/gcc.git] / gcc / gensupport.c
index d2c2f7157297a99cb7c75143bab40271decdc7bd..f2ad54f0c5555b531782d79ed42903d00bc28c6f 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-2020 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.  */
@@ -38,69 +43,98 @@ int insn_elision = 1;
 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 const char *subst_true = "yes";
+static const char *subst_false = "no";
+
 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
+class queue_elem
 {
+public:
   rtx data;
-  const char *filename;
-  int lineno;
-  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;
+  file_location loc;
+  class queue_elem *next;
+  /* In a DEFINE_INSN that came from a DEFINE_INSN_AND_SPLIT or
+     DEFINE_INSN_AND_REWRITE, SPLIT points to the generated DEFINE_SPLIT.  */
+  class 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;
-static struct queue_elem **define_pred_tail = &define_pred_queue;
-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 *other_queue;
-static struct queue_elem **other_tail = &other_queue;
-
-static struct queue_elem *queue_pattern (rtx, struct queue_elem ***,
-                                        const char *, int);
+static class queue_elem *define_attr_queue;
+static class queue_elem **define_attr_tail = &define_attr_queue;
+static class queue_elem *define_pred_queue;
+static class queue_elem **define_pred_tail = &define_pred_queue;
+static class queue_elem *define_insn_queue;
+static class queue_elem **define_insn_tail = &define_insn_queue;
+static class queue_elem *define_cond_exec_queue;
+static class queue_elem **define_cond_exec_tail = &define_cond_exec_queue;
+static class queue_elem *define_subst_queue;
+static class queue_elem **define_subst_tail = &define_subst_queue;
+static class queue_elem *other_queue;
+static class queue_elem **other_tail = &other_queue;
+static class queue_elem *define_subst_attr_queue;
+static class queue_elem **define_subst_attr_tail = &define_subst_attr_queue;
+
+/* 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 int is_predicable (class 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 const char *alter_test_for_insn (class queue_elem *,
+                                       class queue_elem *);
 static char *shift_output_template (char *, const char *, int);
-static const char *alter_output_for_insn (struct queue_elem *,
-                                         struct queue_elem *,
+static const char *alter_output_for_insn (class queue_elem *,
+                                         class queue_elem *,
                                          int, int);
-static void process_one_cond_exec (struct queue_elem *);
+static void process_one_cond_exec (class queue_elem *);
 static void process_define_cond_exec (void);
 static void init_predicate_table (void);
 static void record_insn_name (int, const char *);
+
+static bool has_subst_attribute (class queue_elem *, class queue_elem *);
+static const char * alter_output_for_subst_insn (rtx, int);
+static void alter_attrs_for_subst_insn (class queue_elem *, int);
+static void process_substs_on_one_elem (class queue_elem *,
+                                       class 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);
@@ -108,6 +142,22 @@ 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.
 
@@ -166,11 +216,11 @@ gen_rtx_CONST_INT (enum machine_mode ARG_UNUSED (mode),
 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.  LINENO is
-   the line number on which the directive containing EXP appeared.  */
+   predicate expression EXP, writing the result to CODES.  LOC is
+   the .md file location of the directive containing EXP.  */
 
-static void
-compute_predicate_codes (rtx exp, int lineno, char codes[NUM_RTX_CODE])
+void
+compute_test_codes (rtx exp, file_location loc, char *codes)
 {
   char op0_codes[NUM_RTX_CODE];
   char op1_codes[NUM_RTX_CODE];
@@ -180,29 +230,29 @@ compute_predicate_codes (rtx exp, int lineno, char codes[NUM_RTX_CODE])
   switch (GET_CODE (exp))
     {
     case AND:
-      compute_predicate_codes (XEXP (exp, 0), lineno, op0_codes);
-      compute_predicate_codes (XEXP (exp, 1), lineno, op1_codes);
+      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_predicate_codes (XEXP (exp, 0), lineno, op0_codes);
-      compute_predicate_codes (XEXP (exp, 1), lineno, op1_codes);
+      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_predicate_codes (XEXP (exp, 0), lineno, op0_codes);
+      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_predicate_codes (XEXP (exp, 0), lineno, op0_codes);
-      compute_predicate_codes (XEXP (exp, 1), lineno, op1_codes);
-      compute_predicate_codes (XEXP (exp, 2), lineno, op2_codes);
+      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]),
@@ -226,7 +276,7 @@ compute_predicate_codes (rtx exp, int lineno, char codes[NUM_RTX_CODE])
 
        if (*next_code == '\0')
          {
-           error_with_line (lineno, "empty match_code expression");
+           error_at (loc, "empty match_code expression");
            break;
          }
 
@@ -245,17 +295,16 @@ compute_predicate_codes (rtx exp, int lineno, char codes[NUM_RTX_CODE])
                }
            if (!found_it)
              {
-               error_with_line (lineno,
-                                "match_code \"%.*s\" matches nothing",
-                                (int) n, code);
+               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_with_line (lineno, "(did you mean \"%s\"?)",
-                                        GET_RTX_NAME (i));
+                     message_at (loc, "(did you mean \"%s\"?)",
+                                 GET_RTX_NAME (i));
                    }
              }
          }
@@ -269,8 +318,8 @@ compute_predicate_codes (rtx exp, int lineno, char codes[NUM_RTX_CODE])
        struct pred_data *p = lookup_predicate (XSTR (exp, 1));
        if (!p)
          {
-           error_with_line (lineno, "reference to unknown predicate '%s'",
-                            XSTR (exp, 1));
+           error_at (loc, "reference to unknown predicate '%s'",
+                     XSTR (exp, 1));
            break;
          }
        for (i = 0; i < NUM_RTX_CODE; i++)
@@ -285,9 +334,8 @@ compute_predicate_codes (rtx exp, int lineno, char codes[NUM_RTX_CODE])
       break;
 
     default:
-      error_with_line (lineno,
-                      "'%s' cannot be used in a define_predicate expression",
-                      GET_RTX_NAME (GET_CODE (exp)));
+      error_at (loc, "'%s' cannot be used in predicates or constraints",
+               GET_RTX_NAME (GET_CODE (exp)));
       memset (codes, I, NUM_RTX_CODE);
       break;
     }
@@ -312,12 +360,12 @@ valid_predicate_name_p (const char *name)
   return true;
 }
 
-/* Process define_predicate directive DESC, which appears on line number
-   LINENO.  Compute the set of codes that can be matched, and record this
-   as a known predicate.  */
+/* 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, int lineno)
+process_define_predicate (rtx desc, file_location loc)
 {
   struct pred_data *pred;
   char codes[NUM_RTX_CODE];
@@ -325,9 +373,8 @@ process_define_predicate (rtx desc, int lineno)
 
   if (!valid_predicate_name_p (XSTR (desc, 0)))
     {
-      error_with_line (lineno,
-                      "%s: predicate name must be a valid C function name",
-                      XSTR (desc, 0));
+      error_at (loc, "%s: predicate name must be a valid C function name",
+               XSTR (desc, 0));
       return;
     }
 
@@ -338,7 +385,7 @@ process_define_predicate (rtx desc, int lineno)
   if (GET_CODE (desc) == DEFINE_SPECIAL_PREDICATE)
     pred->special = true;
 
-  compute_predicate_codes (XEXP (desc, 1), lineno, codes);
+  compute_test_codes (XEXP (desc, 1), loc, codes);
 
   for (i = 0; i < NUM_RTX_CODE; i++)
     if (codes[i] != N)
@@ -353,14 +400,13 @@ process_define_predicate (rtx desc, int lineno)
 /* 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)
+static class queue_elem *
+queue_pattern (rtx pattern, class queue_elem ***list_tail,
+              file_location loc)
 {
-  struct queue_elem *e = XNEW(struct queue_elem);
+  class queue_elem *e = XNEW (class queue_elem);
   e->data = pattern;
-  e->filename = filename;
-  e->lineno = lineno;
+  e->loc = loc;
   e->next = NULL;
   e->split = NULL;
   **list_tail = e;
@@ -368,6 +414,45 @@ queue_pattern (rtx pattern, struct queue_elem ***list_tail,
   return e;
 }
 
+/* Remove element ELEM from QUEUE.  */
+static void
+remove_from_queue (class queue_elem *elem, class queue_elem **queue)
+{
+  class 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)
+{
+  class queue_elem *e = XNEW (class 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
@@ -401,46 +486,116 @@ remove_constraints (rtx part)
       }
 }
 
+/* Recursively replace MATCH_OPERANDs with MATCH_DUPs and MATCH_OPERATORs
+   with MATCH_OP_DUPs in X.  */
+
+static rtx
+replace_operands_with_dups (rtx x)
+{
+  if (x == 0)
+    return x;
+
+  rtx newx;
+  if (GET_CODE (x) == MATCH_OPERAND)
+    {
+      newx = rtx_alloc (MATCH_DUP);
+      XINT (newx, 0) = XINT (x, 0);
+      x = newx;
+    }
+  else if (GET_CODE (x) == MATCH_OPERATOR)
+    {
+      newx = rtx_alloc (MATCH_OP_DUP);
+      XINT (newx, 0) = XINT (x, 0);
+      XVEC (newx, 1) = XVEC (x, 2);
+      x = newx;
+    }
+  else
+    newx = shallow_copy_rtx (x);
+
+  const char *format_ptr = GET_RTX_FORMAT (GET_CODE (x));
+  for (int i = 0; i < GET_RTX_LENGTH (GET_CODE (x)); i++)
+    switch (*format_ptr++)
+      {
+      case 'e':
+      case 'u':
+       XEXP (newx, i) = replace_operands_with_dups (XEXP (x, i));
+       break;
+      case 'E':
+       if (XVEC (x, i) != NULL)
+         {
+           XVEC (newx, i) = rtvec_alloc (XVECLEN (x, i));
+           for (int j = 0; j < XVECLEN (x, i); j++)
+             XVECEXP (newx, i, j)
+               = replace_operands_with_dups (XVECEXP (x, i, j));
+         }
+       break;
+      }
+  return newx;
+}
+
+/* Convert matching pattern VEC from a DEFINE_INSN_AND_REWRITE into
+   a sequence that should be generated by the splitter.  */
+
+static rtvec
+gen_rewrite_sequence (rtvec vec)
+{
+  rtvec new_vec = rtvec_alloc (1);
+  rtx x = add_implicit_parallel (vec);
+  RTVEC_ELT (new_vec, 0) = replace_operands_with_dups (x);
+  return new_vec;
+}
+
 /* 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:
     case DEFINE_ENUM_ATTR:
-      queue_pattern (desc, &define_attr_tail, read_md_filename, lineno);
+      queue_pattern (desc, &define_attr_tail, loc);
       break;
 
     case DEFINE_PREDICATE:
     case DEFINE_SPECIAL_PREDICATE:
-      process_define_predicate (desc, lineno);
+      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);
+      queue_pattern (desc, &define_pred_tail, loc);
       break;
 
     case DEFINE_INSN_AND_SPLIT:
+    case DEFINE_INSN_AND_REWRITE:
       {
        const char *split_cond;
        rtx split;
        rtvec attr;
        int i;
-       struct queue_elem *insn_elem;
-       struct queue_elem *split_elem;
+       class queue_elem *insn_elem;
+       class queue_elem *split_elem;
+       int split_code = (GET_CODE (desc) == DEFINE_INSN_AND_REWRITE ? 5 : 6);
 
        /* Create a split with values from the insn_and_split.  */
        split = rtx_alloc (DEFINE_SPLIT);
@@ -458,30 +613,33 @@ 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);
          }
+       else if (GET_CODE (desc) == DEFINE_INSN_AND_REWRITE)
+         error_at (loc, "the rewrite condition must start with `&&'");
        XSTR (split, 1) = split_cond;
-       XVEC (split, 2) = XVEC (desc, 5);
-       XSTR (split, 3) = XSTR (desc, 6);
+       if (GET_CODE (desc) == DEFINE_INSN_AND_REWRITE)
+         XVEC (split, 2) = gen_rewrite_sequence (XVEC (desc, 1));
+       else
+         XVEC (split, 2) = XVEC (desc, 5);
+       XSTR (split, 3) = XSTR (desc, split_code);
 
        /* Fix up the DEFINE_INSN.  */
-       attr = XVEC (desc, 7);
+       attr = XVEC (desc, split_code + 1);
        PUT_CODE (desc, DEFINE_INSN);
        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;
     }
 }
@@ -490,7 +648,7 @@ process_rtx (rtx desc, int lineno)
    a DEFINE_INSN.  */
 
 static int
-is_predicable (struct queue_elem *elem)
+is_predicable (class queue_elem *elem)
 {
   rtvec vec = XVEC (elem->data, 4);
   const char *value;
@@ -515,8 +673,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;
@@ -535,8 +692,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:
@@ -547,34 +703,286 @@ 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 (class queue_elem *elem,
+                       class 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 (class queue_elem *elem, class 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:
+         if (strcmp (XSTR (cur_attr, 0), subst_name) == 0)
+           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.  */
 
 static void
 identify_predicable_attribute (void)
 {
-  struct queue_elem *elem;
+  class queue_elem *elem;
   char *p_true, *p_false;
   const char *value;
 
@@ -583,8 +991,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:
@@ -593,9 +1001,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';
@@ -610,16 +1017,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;
     }
 
@@ -629,10 +1034,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);
     }
 }
 
@@ -650,6 +1054,78 @@ n_alternatives (const char *s)
   return n;
 }
 
+/* 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));
+      /* 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;
+       }
+
+    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.  */
 
@@ -664,12 +1140,12 @@ collect_insn_data (rtx pattern, int *palt, int *pmax)
   switch (code)
     {
     case MATCH_OPERAND:
-      i = n_alternatives (XSTR (pattern, 2));
+    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_SCRATCH:
     case MATCH_PARALLEL:
       i = XINT (pattern, 0);
       if (i > *pmax)
@@ -699,7 +1175,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:
@@ -709,7 +1186,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;
@@ -724,8 +1202,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;
          }
 
@@ -734,7 +1212,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)
@@ -767,8 +1245,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;
@@ -777,13 +1254,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:
@@ -794,27 +1271,265 @@ alter_predicate_for_insn (rtx pattern, int alt, int max_op, int lineno)
   return pattern;
 }
 
-static const char *
-alter_test_for_insn (struct queue_elem *ce_elem,
-                    struct queue_elem *insn_elem)
+/* 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)
 {
-  return join_c_conditions (XSTR (ce_elem->data, 1),
-                           XSTR (insn_elem->data, 2));
-}
+  const char *fmt;
+  enum rtx_code code;
+  int i, j, len;
 
-/* Adjust all of the operand numbers in SRC to match the shift they'll
-   get from an operand displacement of DISP.  Return a pointer after the
-   adjusted string.  */
+  code = GET_CODE (pattern);
+  switch (code)
+    {
+    case MATCH_OPERAND:
+      XSTR (pattern, 2) = alter (XSTR (pattern, 2), n_dup);
+      break;
 
-static char *
-shift_output_template (char *dest, const char *src, int disp)
-{
-  while (*src)
+    default:
+      break;
+    }
+
+  fmt = GET_RTX_FORMAT (code);
+  len = GET_RTX_LENGTH (code);
+  for (i = 0; i < len; i++)
     {
-      char c = *src++;
-      *dest++ = c;
-      if (c == '%')
-       {
+      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 (class queue_elem *ce_elem,
+                    class queue_elem *insn_elem)
+{
+  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)
+    {
+      class 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 (class 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
+   get from an operand displacement of DISP.  Return a pointer after the
+   adjusted string.  */
+
+static char *
+shift_output_template (char *dest, const char *src, int disp)
+{
+  while (*src)
+    {
+      char c = *src++;
+      *dest++ = c;
+      if (c == '%')
+       {
          c = *src++;
          if (ISDIGIT ((unsigned char) c))
            c += disp;
@@ -831,8 +1546,8 @@ shift_output_template (char *dest, const char *src, int disp)
 }
 
 static const char *
-alter_output_for_insn (struct queue_elem *ce_elem,
-                      struct queue_elem *insn_elem,
+alter_output_for_insn (class queue_elem *ce_elem,
+                      class queue_elem *insn_elem,
                       int alt, int max_op)
 {
   const char *ce_out, *insn_out;
@@ -856,7 +1571,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
        {
@@ -890,12 +1605,139 @@ 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
-process_one_cond_exec (struct queue_elem *ce_elem)
+process_one_cond_exec (class queue_elem *ce_elem)
 {
-  struct queue_elem *insn_elem;
+  class queue_elem *insn_elem;
   for (insn_elem = define_insn_queue; insn_elem ; insn_elem = insn_elem->next)
     {
       int alternatives, max_operand;
@@ -913,92 +1755,494 @@ 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;
 
-      /* Construct a new pattern for the new insn.  */
-      insn = copy_rtx (insn_elem->data);
-      new_name = XNEWVAR (char, strlen XSTR (insn_elem->data, 0) + 4);
-      sprintf (new_name, "*p %s", XSTR (insn_elem->data, 0));
-      XSTR (insn, 0) = new_name;
-      pattern = rtx_alloc (COND_EXEC);
-      XEXP (pattern, 0) = pred;
-      if (XVECLEN (insn, 1) == 1)
+      /* Construct a new pattern for the new insn.  */
+      insn = copy_rtx (insn_elem->data);
+      new_name = XNEWVAR (char, strlen XSTR (insn_elem->data, 0) + 4);
+      sprintf (new_name, "*p %s", XSTR (insn_elem->data, 0));
+      XSTR (insn, 0) = new_name;
+      pattern = rtx_alloc (COND_EXEC);
+      XEXP (pattern, 0) = pred;
+      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)
+       {
+         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);
+      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
+        (b) appears after all normal define_insn patterns.
+
+        ??? B is debatable.  If one has normal insns that match
+        cond_exec patterns, they will be preferred over these
+        generated patterns.  Whether this matters in practice, or if
+        it's a good thing, or whether we should thread these new
+        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->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 (class queue_elem *elem,
+                           class queue_elem *queue)
+{
+  class 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)
        {
-         XEXP (pattern, 1) = XVECEXP (insn, 1, 0);
-         XVECEXP (insn, 1, 0) = pattern;
-         PUT_NUM_ELEM (XVEC (insn, 1), 1);
+         match_operand_entries_in_pattern[opno] = pattern;
+         return NULL;
        }
       else
        {
-         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;
+         /* 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;
+}
 
-      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);
+/* 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;
 
-      /* ??? Set `predicable' to false.  Not crucial since it's really
-         only used here, and we won't reprocess this new pattern.  */
+  code = GET_CODE (pattern);
+  switch (code)
+    {
+    case MATCH_DUP:
+    case MATCH_OP_DUP:
+      opno = XINT (pattern, 0);
 
-      /* Put the new pattern on the `other' list so that it
-        (a) is not reprocessed by other define_cond_exec patterns
-        (b) appears after all normal define_insn patterns.
+      gcc_assert (opno >= 0 && opno < MAX_OPERANDS);
 
-        ??? B is debatable.  If one has normal insns that match
-        cond_exec patterns, they will be preferred over these
-        generated patterns.  Whether this matters in practice, or if
-        it's a good thing, or whether we should thread these new
-        patterns into the define_insn chain just after their generator
-        is something we'll have to experiment with.  */
+      if (operand_data[opno])
+       {
+         pattern = copy_rtx (operand_data[opno]);
 
-      queue_pattern (insn, &other_tail, insn_elem->filename,
-                    insn_elem->lineno);
+         /* Duplicate constraints.  */
+         pattern = alter_constraints (pattern, n_subst_alt,
+                                      duplicate_alternatives);
+       }
+      break;
 
-      if (!insn_elem->split)
-       continue;
+    default:
+      break;
+    }
 
-      /* 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;
-       }
-      /* Predicate all of the insns generated by the split.  */
-      for (i = 0; i < XVECLEN (split, 2); i++)
+  fmt = GET_RTX_FORMAT (GET_CODE (pattern));
+  len = GET_RTX_LENGTH (GET_CODE (pattern));
+  for (i = 0; i < len; i++)
+    {
+      switch (fmt[i])
        {
-         pattern = rtx_alloc (COND_EXEC);
-         XEXP (pattern, 0) = pred;
-         XEXP (pattern, 1) = XVECEXP (split, 2, i);
-         XVECEXP (split, 2, i) = pattern;
+       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 ();
        }
-      /* Add the new split to the queue.  */
-      queue_pattern (split, &other_tail, read_md_filename,
-                    insn_elem->split->lineno);
     }
+  return pattern;
 }
 
 /* If we have any DEFINE_COND_EXEC patterns, expand the DEFINE_INSN
@@ -1007,7 +2251,7 @@ process_one_cond_exec (struct queue_elem *ce_elem)
 static void
 process_define_cond_exec (void)
 {
-  struct queue_elem *elem;
+  class queue_elem *elem;
 
   identify_predicable_attribute ();
   if (have_error)
@@ -1016,17 +2260,65 @@ process_define_cond_exec (void)
   for (elem = define_cond_exec_queue; elem ; elem = elem->next)
     process_one_cond_exec (elem);
 }
-\f
-/* A read_md_files callback for reading an rtx.  */
+
+/* If we have any DEFINE_SUBST patterns, expand DEFINE_INSN and
+   DEFINE_EXPAND patterns appropriately.  */
 
 static void
-rtx_handle_directive (int lineno, const char *rtx_name)
+process_define_subst (void)
+{
+  class 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
 {
-  rtx queue, x;
+ 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;
 
-  if (read_rtx (rtx_name, &queue))
-    for (x = queue; x; x = XEXP (x, 1))
-      process_rtx (XEXP (x, 0), lineno);
+  rtx x;
+  unsigned int i;
+  FOR_EACH_VEC_ELT (subrtxs, i, x)
+    process_rtx (x, loc);
 }
 
 /* Comparison function for the mnemonic hash table.  */
@@ -1038,11 +2330,11 @@ htab_eq_string (const void *s1, const void *s2)
 }
 
 /* Add mnemonic STR with length LEN to the mnemonic hash table
-   MNEMONIC_HTAB.  A trailing zero end character is appendend to STR
+   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, int len)
+add_mnemonic_string (htab_t mnemonic_htab, const char *str, size_t len)
 {
   char *new_str;
   void **slot;
@@ -1080,6 +2372,7 @@ gen_mnemonic_setattr (htab_t mnemonic_htab, rtx insn)
   rtx set_attr;
   char *attr_name;
   rtvec new_vec;
+  struct obstack *string_obstack = rtx_reader_ptr->get_string_obstack ();
 
   template_code = XTMPL (insn, 3);
 
@@ -1095,7 +2388,7 @@ gen_mnemonic_setattr (htab_t mnemonic_htab, rtx insn)
   for (i = 0; *cp; )
     {
       const char *ep, *sp;
-      int size = 0;
+      size_t size = 0;
 
       while (ISSPACE (*cp))
        cp++;
@@ -1105,13 +2398,13 @@ gen_mnemonic_setattr (htab_t mnemonic_htab, rtx insn)
          sp = ep + 1;
 
       if (i > 0)
-       obstack_1grow (&string_obstack, ',');
+       obstack_1grow (string_obstack, ',');
 
       while (cp < sp && ((*cp >= '0' && *cp <= '9')
                         || (*cp >= 'a' && *cp <= 'z')))
 
        {
-         obstack_1grow (&string_obstack, *cp);
+         obstack_1grow (string_obstack, *cp);
          cp++;
          size++;
        }
@@ -1122,8 +2415,7 @@ gen_mnemonic_setattr (htab_t mnemonic_htab, rtx insn)
            {
              /* Don't set a value if there are more than one
                 instruction in the string.  */
-             obstack_next_free (&string_obstack) =
-               obstack_next_free (&string_obstack) - size;
+             obstack_blank_fast (string_obstack, -size);
              size = 0;
 
              cp = sp;
@@ -1132,22 +2424,22 @@ gen_mnemonic_setattr (htab_t mnemonic_htab, rtx insn)
          cp++;
        }
       if (size == 0)
-       obstack_1grow (&string_obstack, '*');
+       obstack_1grow (string_obstack, '*');
       else
        add_mnemonic_string (mnemonic_htab,
-                            obstack_next_free (&string_obstack) - size,
+                            (char *) obstack_next_free (string_obstack) - size,
                             size);
       i++;
     }
 
   /* An insn definition might emit an empty string.  */
-  if (obstack_object_size (&string_obstack) == 0)
+  if (obstack_object_size (string_obstack) == 0)
     return;
 
-  obstack_1grow (&string_obstack, '\0');
+  obstack_1grow (string_obstack, '\0');
 
   set_attr = rtx_alloc (SET_ATTR);
-  XSTR (set_attr, 1) = XOBFINISH (&string_obstack, char *);
+  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;
@@ -1170,8 +2462,10 @@ gen_mnemonic_setattr (htab_t mnemonic_htab, rtx insn)
 static int
 mnemonic_htab_callback (void **slot, void *info ATTRIBUTE_UNUSED)
 {
-  obstack_grow (&string_obstack, (char*)*slot, strlen ((char*)*slot));
-  obstack_1grow (&string_obstack, ',');
+  struct obstack *string_obstack = rtx_reader_ptr->get_string_obstack ();
+
+  obstack_grow (string_obstack, (char*) *slot, strlen ((char*) *slot));
+  obstack_1grow (string_obstack, ',');
   return 1;
 }
 
@@ -1184,11 +2478,12 @@ mnemonic_htab_callback (void **slot, void *info ATTRIBUTE_UNUSED)
 static void
 gen_mnemonic_attr (void)
 {
-  struct queue_elem *elem;
+  class 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;
@@ -1216,14 +2511,29 @@ gen_mnemonic_attr (void)
       bool found = false;
 
       /* Check if the insn definition already has
-        (set_attr "mnemonic" ...).  */
+        (set_attr "mnemonic" ...) or (set (attr "mnemonic") ...).  */
       if (XVEC (insn, 4))
        for (i = 0; i < XVECLEN (insn, 4); i++)
-         if (strcmp (XSTR (XVECEXP (insn, 4, i), 0), MNEMONIC_ATTR_NAME) == 0)
-           {
-             found = true;
-             break;
-           }
+         {
+           rtx set_attr = XVECEXP (insn, 4, i);
+
+           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;
+             }
+         }
 
       if (!found)
        gen_mnemonic_setattr (mnemonic_htab, insn);
@@ -1237,107 +2547,219 @@ gen_mnemonic_attr (void)
   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 *);
+  *((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 ()
+{
+  class 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);
+
+      /* Duplicate.  */
+      if (*slot)
+       {
+         error_at (elem->loc, "redefinition of attribute '%s'", attr_name);
+         htab_delete (attr_htab);
+         return;
+       }
+
+      *slot = attr_name;
+    }
+
+  htab_delete (attr_htab);
 }
 
 /* The entry point for initializing the reader.  */
 
-bool
-init_rtx_reader_args_cb (int argc, char **argv,
+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;
 
-  read_md_files (argc, argv, parse_opt, rtx_handle_directive);
+  /* Start at 1, to make 0 available for CODE_FOR_nothing.  */
+  insn_sequence_num = 1;
+
+  /* These sequences are not used as indices, so can start at 1 also.  */
+  split_sequence_num = 1;
+  peephole2_sequence_num = 1;
+
+  gen_reader *reader = new gen_reader ();
+  reader->read_md_files (argc, argv, parse_opt);
+
+  if (define_attr_queue != NULL)
+    check_define_attr_duplicates ();
 
   /* Process define_cond_exec patterns.  */
   if (define_cond_exec_queue != NULL)
     process_define_cond_exec ();
 
+  /* Process define_subst patterns.  */
+  if (define_subst_queue != NULL)
+    process_define_subst ();
+
   if (define_attr_queue != NULL)
     gen_mnemonic_attr ();
 
-  return !have_error;
+  if (have_error)
+    {
+      delete reader;
+      return NULL;
+    }
+
+  return reader;
 }
 
 /* Programs that don't have their own options can use this entry point
    instead.  */
-bool
-init_rtx_reader_args (int argc, char **argv)
+rtx_reader *
+init_rtx_reader_args (int argc, const char **argv)
 {
   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
+    {
+      class 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.  */
@@ -1472,7 +2894,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)
@@ -1509,13 +2933,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}},
@@ -1594,7 +3024,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;
     }
 
@@ -1642,6 +3072,10 @@ get_pattern_stats_1 (struct pattern_stats *stats, rtx x)
       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;
 
@@ -1674,6 +3108,7 @@ get_pattern_stats (struct pattern_stats *stats, rtvec pattern)
 
   stats->max_opno = -1;
   stats->max_dup_opno = -1;
+  stats->min_scratch_opno = -1;
   stats->max_scratch_opno = -1;
   stats->num_dups = 0;
 
@@ -1688,3 +3123,197 @@ get_pattern_stats (struct pattern_stats *stats, rtvec pattern)
                                  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;
+}