This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

[PATCH] Your new friend and mine, gensimplify


Ignore all the Makefile.in lines it claims changed.
It's due to my tree having ssa-dce.o, ssa-ccp.o, ssa-valnum.o listed.
I removed the differences, but didn't want to confuse diff in any way.
So only the last line of object files has changed, to add
new-simplify.o

This is just a first pass, and it works okay. I named the output new-simplify
because we aren't ready to replace simplify_rtx just yet.
This is also why there is no md.texi documentation. It's not ready for
the tree yet, but i want comments and whatnot.

simplify.md is just starting, i didn't want to write many more in case
it completely changes, since this is, after all, a first pass.

I'm also not positive that the default case of if GET_RTX_LENGTH
(operand) > 1 causing us to recurse is right.

It works, of course, but someone might be able to think of a better
way of handling the problem of recursively simplifying, without having
to manually inform it of where to recurse.

 2001-05-11  Daniel Berlin  <dan@cgsoftware.com>
 
 	* Makefile.in (GEN): Add gensimplify.
 	(STAGESTUFF): Add gensimplify.
 	(new-simplify.c): Add rule for new generated file.
 	(s-simplfy): Add rule for stamp file for new generated file.

 	* gensimplify.c: New file. generate RTL simplifiers from a
 	description mode.
 	* simplify.md: New file. This is what we generate the simplifiers
 	from.  It's just a sample right now, mainly.

Index: Makefile.in
===================================================================
RCS file: /cvs/gcc/egcs/gcc/Makefile.in,v
retrieving revision 1.658
diff -c -3 -p -w -B -b -r1.658 Makefile.in
*** Makefile.in	2001/05/09 14:16:45	1.658
--- Makefile.in	2001/05/12 03:16:18
*************** C_OBJS = c-parse.o c-lang.o $(C_AND_OBJC
*** 734,753 ****
  
  OBJS =									\
   alias.o bb-reorder.o bitmap.o builtins.o caller-save.o calls.o		\
!  combine.o conflict.o convert.o cse.o cselib.o dbxout.o dce.o		\
!  dependence.o diagnostic.o doloop.o dominance.o dwarf2asm.o dwarf2out.o	\
   dwarfout.o emit-rtl.o except.o explow.o expmed.o expr.o final.o flow.o	\
!  fold-const.o function.o gcse.o genrtl.o ggc-common.o global.o graph.o	\
   haifa-sched.o hash.o ifcvt.o insn-attrtab.o insn-emit.o		\
!  insn-extract.o insn-opinit.o insn-output.o insn-peep.o insn-recog.o	\
   integrate.o intl.o jump.o lcm.o lists.o local-alloc.o loop.o mbchar.o	\
   optabs.o params.o predict.o print-rtl.o print-tree.o profile.o real.o	\
   recog.o reg-stack.o regclass.o regmove.o regrename.o reload.o		\
   reload1.o reorg.o resource.o rtl.o rtlanal.o sbitmap.o sched-deps.o	\
   sched-ebb.o sched-rgn.o sched-vis.o sdbout.o sibcall.o simplify-rtx.o	\
   splay-tree.o ssa.o stmt.o stor-layout.o stringpool.o timevar.o		\
!  toplev.o tree.o unroll.o varasm.o varray.o version.o xcoffout.o	\
!  $(GGC) $(out_object_file) $(EXTRA_OBJS)
  
  BACKEND = main.o libbackend.a
  
--- 734,753 ----
  
  OBJS =									\
   alias.o bb-reorder.o bitmap.o builtins.o caller-save.o calls.o		\
!  combine.o conflict.o convert.o cse.o cselib.o dbxout.o dce.o		\
!  dependence.o diagnostic.o doloop.o dominance.o dwarf2asm.o dwarf2out.o	\
   dwarfout.o emit-rtl.o except.o explow.o expmed.o expr.o final.o flow.o	\
!  fold-const.o function.o gcse.o genrtl.o ggc-common.o global.o graph.o 	\
   haifa-sched.o hash.o ifcvt.o insn-attrtab.o insn-emit.o		\
!  insn-extract.o insn-opinit.o insn-output.o insn-peep.o test.o insn-recog.o	\
   integrate.o intl.o jump.o lcm.o lists.o local-alloc.o loop.o mbchar.o	\
   optabs.o params.o predict.o print-rtl.o print-tree.o profile.o real.o	\
   recog.o reg-stack.o regclass.o regmove.o regrename.o reload.o		\
   reload1.o reorg.o resource.o rtl.o rtlanal.o sbitmap.o sched-deps.o	\
   sched-ebb.o sched-rgn.o sched-vis.o sdbout.o sibcall.o simplify-rtx.o	\
   splay-tree.o ssa.o stmt.o stor-layout.o stringpool.o timevar.o		\
!  toplev.o tree.o unroll.o varasm.o varray.o version.o xcoffout.o	\
!  ra.o ssa-ccp.o new-simplify.o $(GGC) $(out_object_file) $(EXTRA_OBJS)
  
  BACKEND = main.o libbackend.a
  
*************** BACKEND = main.o libbackend.a
*** 757,763 ****
  GEN= genemit$(build_exeext) genoutput$(build_exeext) genrecog$(build_exeext) \
   genextract$(build_exeext) genflags$(build_exeext) gencodes$(build_exeext)   \
   genconfig$(build_exeext) genpeep$(build_exeext) gengenrtl$(build_exeext)    \
!  gencheck$(build_exeext)
  
  # Files to be copied away after each stage in building.
  STAGESTUFF = *$(objext) insn-flags.h insn-config.h insn-codes.h \
--- 757,763 ----
  GEN= genemit$(build_exeext) genoutput$(build_exeext) genrecog$(build_exeext) \
   genextract$(build_exeext) genflags$(build_exeext) gencodes$(build_exeext)   \
   genconfig$(build_exeext) genpeep$(build_exeext) gengenrtl$(build_exeext)    \
!  gencheck$(build_exeext) gensimplify$(build_exeext)
  
  # Files to be copied away after each stage in building.
  STAGESTUFF = *$(objext) insn-flags.h insn-config.h insn-codes.h \
*************** STAGESTUFF = *$(objext) insn-flags.h ins
*** 770,776 ****
   genextract$(build_exeext) genflags$(build_exeext) gencodes$(build_exeext) \
   genconfig$(build_exeext) genpeep$(build_exeext) genattrtab$(build_exeext) \
   genattr$(build_exeext) genopinit$(build_exeext) gengenrtl$(build_exeext) \
!  gencheck$(build_exeext) genrtl.c genrtl.h \
   xgcc$(exeext) cpp$(exeext) cc1$(exeext) cpp0$(exeext) $(EXTRA_PASSES) \
   $(EXTRA_PARTS) $(EXTRA_PROGRAMS) gcc-cross$(exeext) cc1obj$(exeext) \
   enquire$(exeext) protoize$(exeext) unprotoize$(exeext) \
--- 770,776 ----
   genextract$(build_exeext) genflags$(build_exeext) gencodes$(build_exeext) \
   genconfig$(build_exeext) genpeep$(build_exeext) genattrtab$(build_exeext) \
   genattr$(build_exeext) genopinit$(build_exeext) gengenrtl$(build_exeext) \
!  gencheck$(build_exeext) gensimplify$(build_exeext) genrtl.c genrtl.h \
   xgcc$(exeext) cpp$(exeext) cc1$(exeext) cpp0$(exeext) $(EXTRA_PASSES) \
   $(EXTRA_PARTS) $(EXTRA_PROGRAMS) gcc-cross$(exeext) cc1obj$(exeext) \
   enquire$(exeext) protoize$(exeext) unprotoize$(exeext) \
*************** s-recog : $(md_file) genrecog$(build_exe
*** 1664,1669 ****
--- 1673,1684 ----
  	$(SHELL) $(srcdir)/move-if-change tmp-recog.c insn-recog.c
  	touch s-recog
  
+ new-simplify.c: s-simplify ; @true
+ s-simplify : simplify.md gensimplify$(build_exeext) $(srcdir)/move-if-change
+ 	./gensimplify$(build_exeext) $(srcdir)/simplify.md > tmp-simplify.c
+ 	$(SHELL) $(srcdir)/move-if-change tmp-simplify.c new-simplify.c
+ 	touch s-simplify
+ 
  insn-opinit.o : insn-opinit.c $(CONFIG_H) $(RTL_H) \
    insn-config.h flags.h $(RECOG_H) $(EXPR_H) reload.h $(SYSTEM_H)
  	$(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -c insn-opinit.c
*************** genopinit.o : genopinit.c $(RTL_H) $(HCO
*** 1796,1801 ****
--- 1811,1825 ----
    $(SYSTEM_H) errors.h gensupport.h
  	$(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/genopinit.c
  
+ gensimplify$(build_exeext) : gensimplify.o $(HOST_RTL) $(HOST_PRINT) $(HOST_ERRORS) $(HOST_LIBDEPS)
+ 	$(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ \
+ 	 gensimplify.o $(HOST_RTL) $(HOST_PRINT) $(HOST_ERRORS) $(HOST_LIBS)
+ 
+ gensimplify.o : gensimplify.c $(RTL_H) $(HCONFIG_H) \
+   $(SYSTEM_H) errors.h gensupport.h
+ 	$(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/gensimplify.c
+ 
+ 
  genrecog$(build_exeext) : genrecog.o $(HOST_RTL) $(HOST_PRINT) $(HOST_ERRORS) $(HOST_LIBDEPS)
  	$(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ \
  	 genrecog.o $(HOST_RTL) $(HOST_PRINT) $(HOST_ERRORS) $(HOST_LIBS)
Index: gensimplify.c
===================================================================
RCS file: gensimplify.c
diff -N gensimplify.c
*** /dev/null	Tue May  5 13:32:27 1998
--- gensimplify.c	Fri May 11 20:16:18 2001
***************
*** 0 ****
--- 1,2881 ----
+ /* Generate code from simplifier description
+    Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1997, 1998,
+    1999, 2000, 2001 Free Software Foundation, Inc.
+ 
+    This file is part of GNU CC.
+ 
+    GNU CC is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+ 
+    GNU CC is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+ 
+    You should have received a copy of the GNU General Public License
+    along with GNU CC; see the file COPYING.  If not, write to
+    the Free Software Foundation, 59 Temple Place - Suite 330,
+    Boston, MA 02111-1307, USA.  */
+ 
+ 
+ /* This program is used to produce new-simplify.c 
+    It contains a function called "simplify_insns", which attempts to 
+    simplify a given rtx.
+ 
+    The 'simplify_insns' function returns 0 if the rtl could not be
+    matched.  If there was a match, the new rtl is returned in a
+    SEQUENCE, and LAST_INSN will point to the last recognized insn in
+    the old sequence.  */ 
+ 
+ #include "hconfig.h"
+ #include "system.h"
+ #include "rtl.h"
+ #include "errors.h"
+ #include "gensupport.h"
+ 
+ 
+ #define OUTPUT_LABEL(INDENT_STRING, LABEL_NUMBER) \
+   printf("%sL%d: ATTRIBUTE_UNUSED_LABEL\n", (INDENT_STRING), (LABEL_NUMBER))
+ 
+ /* Holds an array of names indexed by insn_code_number.  */
+ static char **insn_name_ptr = 0;
+ static int insn_name_ptr_size = 0;
+ 
+ /* A listhead of decision trees.  The alternatives to a node are kept
+    in a doublely-linked list so we can easily add nodes to the proper
+    place when merging.  */
+ 
+ struct decision_head
+ {
+   struct decision *first;
+   struct decision *last;
+ };
+     
+ /* A single test.  The two accept types aren't tests per-se, but
+    their equality (or lack thereof) does affect tree merging so
+    it is convenient to keep them here.  */
+ 
+ struct decision_test
+ {
+   /* A linked list through the tests attached to a node.  */
+   struct decision_test *next;
+   
+   /* These types are roughly in the order in which we'd like to test them.  */
+   enum decision_type {
+     DT_mode, DT_code, DT_veclen,
+     DT_elt_zero_int, DT_elt_one_int, DT_elt_zero_wide,
+     DT_veclen_ge, DT_dup, DT_pred, DT_c_test, 
+     DT_accept_op, DT_accept_insn
+   } type;
+ 
+   union
+   {
+     enum machine_mode mode;	/* Machine mode of node.  */
+     RTX_CODE code;		/* Code to test.  */
+ 
+     struct
+     {
+       const char *name;		/* Predicate to call.  */
+       int index;		/* Index into `preds' or -1.  */
+       enum machine_mode mode;	/* Machine mode for node.  */
+     } pred;
+ 
+     const char *c_test;		/* Additional test to perform.  */
+     int veclen;			/* Length of vector.  */
+     int dup;			/* Number of operand to compare against.  */
+     HOST_WIDE_INT intval;	/* Value for XINT for XWINT.  */
+     int opno;			/* Operand number matched.  */
+ 
+     struct {
+       int code_number;		/* Insn number matched.  */
+       int lineno;		/* Line number of the insn.  */
+       int num_clobbers_to_add;	/* Number of CLOBBERs to be added.  */
+     } insn;
+   } u;
+ };
+ 
+ /* Data structure for decision tree for recognizing legitimate insns.  */
+ 
+ struct decision
+ {
+   struct decision_head success;	/* Nodes to test on success.  */
+   struct decision *next;	/* Node to test on failure.  */
+   struct decision *prev;	/* Node whose failure tests us.  */
+   struct decision *afterward;	/* Node to test on success,
+ 				   but failure of successor nodes.  */
+ 
+   const char *position;		/* String denoting position in pattern.  */
+ 
+   struct decision_test *tests;	/* The tests for this node.  */
+ 
+   int number;			/* Node number, used for labels */
+   int subroutine_number;	/* Number of subroutine this node starts */
+   int need_label;		/* Label needs to be output.  */
+ };
+ 
+ #define SUBROUTINE_THRESHOLD	100
+ 
+ static int next_subroutine_number;
+ 
+ /* Next available node number for tree nodes.  */
+ 
+ static int next_number;
+ 
+ /* Next number to use as an insn_code.  */
+ 
+ static int next_insn_code;
+ 
+ /* Similar, but counts all expressions in the MD file; used for
+    error messages.  */
+ 
+ static int next_index;
+ 
+ /* Record the highest depth we ever have so we know how many variables to
+    allocate in each subroutine we make.  */
+ 
+ static int max_depth;
+ 
+ /* The line number of the start of the pattern currently being processed.  */
+ static int pattern_lineno;
+ 
+ /* Count of errors.  */
+ static int error_count;
+ 
+ /* This table contains a list of the rtl codes that can possibly match a
+    predicate defined in recog.c.  The function `maybe_both_true' uses it to
+    deduce that there are no expressions that can be matches by certain pairs
+    of tree nodes.  Also, if a predicate can match only one code, we can
+    hardwire that code into the node testing the predicate.  */
+ 
+ static struct pred_table
+ {
+   const char *name;
+   RTX_CODE codes[NUM_RTX_CODE];
+ } preds[] = {
+   {"general_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
+ 		       LABEL_REF, SUBREG, REG, MEM}},
+ #ifdef PREDICATE_CODES
+   PREDICATE_CODES
+ #endif
+   {"address_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
+ 		       LABEL_REF, SUBREG, REG, MEM, PLUS, MINUS, MULT}},
+   {"register_operand", {SUBREG, REG}},
+   {"pmode_register_operand", {SUBREG, REG}},
+   {"scratch_operand", {SCRATCH, REG}},
+   {"immediate_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
+ 			 LABEL_REF}},
+   {"const_int_operand", {CONST_INT}},
+   {"const_double_operand", {CONST_INT, CONST_DOUBLE}},
+   {"nonimmediate_operand", {SUBREG, REG, MEM}},
+   {"nonmemory_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
+ 			 LABEL_REF, SUBREG, REG}},
+   {"push_operand", {MEM}},
+   {"pop_operand", {MEM}},
+   {"memory_operand", {SUBREG, MEM}},
+   {"indirect_operand", {SUBREG, MEM}},
+   {"comparison_operator", {EQ, NE, LE, LT, GE, GT, LEU, LTU, GEU, GTU,
+ 			   UNORDERED, ORDERED, UNEQ, UNGE, UNGT, UNLE,
+ 			   UNLT, LTGT}},
+   {"mode_independent_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
+ 				LABEL_REF, SUBREG, REG, MEM}}
+ };
+ 
+ #define NUM_KNOWN_PREDS ARRAY_SIZE (preds)
+ 
+ static const char * special_mode_pred_table[] = {
+ #ifdef SPECIAL_MODE_PREDICATES
+   SPECIAL_MODE_PREDICATES
+ #endif
+   "pmode_register_operand"
+ };
+ 
+ #define NUM_SPECIAL_MODE_PREDS ARRAY_SIZE (special_mode_pred_table)
+ 
+ static struct decision *new_decision PARAMS ((const char *, 
+ 					      struct decision_head *));
+ static struct decision_test *new_decision_test PARAMS ((enum decision_type, 
+ 							struct decision_test ***));
+ static rtx find_operand PARAMS ((rtx, int));
+ static rtx find_matching_operand PARAMS ((rtx, int));
+ static void validate_pattern PARAMS ((rtx, rtx, rtx, int));
+ static struct decision *add_to_sequence PARAMS ((rtx, struct decision_head *, 
+ 						 const char *, int));
+ static int maybe_both_true_2 PARAMS ((struct decision_test *, struct decision_test *));
+ static int maybe_both_true_1 PARAMS ((struct decision_test *, struct decision_test *));
+ static int maybe_both_true PARAMS ((struct decision *, struct decision *, int));
+ static int nodes_identical_1 PARAMS ((struct decision_test *, struct decision_test *));
+ static int nodes_identical PARAMS ((struct decision *, struct decision *));
+ static void merge_accept_insn PARAMS ((struct decision *, struct decision *));
+ static void merge_trees PARAMS ((struct decision_head *, struct decision_head *));
+ 
+ static void factor_tests PARAMS ((struct decision_head *));
+ static void simplify_tests PARAMS ((struct decision_head *));
+ static int break_out_subroutines PARAMS ((struct decision_head *, int));
+ static void find_afterward PARAMS ((struct decision_head *, struct decision *));
+ 
+ static void change_state PARAMS ((const char *, const char *, struct decision *, const char *));
+ static void print_code PARAMS ((enum rtx_code));
+ static void write_afterward PARAMS ((struct decision *, struct decision *, const char *));
+ static struct decision *write_switch PARAMS ((struct decision *, int));
+ static void write_cond PARAMS ((struct decision_test *, int));
+ static void write_action PARAMS ((struct decision *, struct decision_test *, 
+ 				  int, int, struct decision *));
+ static int is_unconditional PARAMS ((struct decision_test *));
+ 
+ static int write_node PARAMS ((struct decision *, int));
+ static void write_tree_1 PARAMS ((struct decision_head *, int));
+ static void write_tree PARAMS ((struct decision_head *, const char *,  int));
+ static void write_subroutine PARAMS ((struct decision_head *));
+ static void write_subroutines PARAMS ((struct decision_head *));
+ static void write_header PARAMS ((void));
+ 
+ static struct decision_head make_insn_sequence PARAMS ((rtx));
+ static void process_tree PARAMS ((struct decision_head *));
+   
+ static void record_insn_name PARAMS ((int, const char *));
+ 
+ static void debug_decision_0 PARAMS ((struct decision *, int, int));
+ static void debug_decision_1 PARAMS ((struct decision *, int));
+ static void debug_decision_2 PARAMS ((struct decision_test *));
+ extern void debug_decision PARAMS ((struct decision *));
+ extern void debug_decision_list PARAMS ((struct decision *));
+ 
+ static int max_opno;
+ static int max_dup_opno;
+ static int max_scratch_opno;
+ static int register_constraints;
+ 
+ /* Data structure for recording the patterns of insns that have CLOBBERs.
+    We use this to output a function that adds these CLOBBERs to a 
+    previously-allocated PARALLEL expression.  */
+ 
+ struct clobber_pat
+ {
+   struct clobber_ent *insns;
+   rtx pattern;
+   int first_clobber;
+   struct clobber_pat *next;
+   int has_hard_reg;
+ } *clobber_list;
+ 
+ /* Records one insn that uses the clobber list.  */
+ 
+ struct clobber_ent
+ {
+   int code_number;		/* Counts only insns.  */
+   struct clobber_ent *next;
+ };
+ 
+ static void max_operand_1		PARAMS ((rtx));
+ static int max_operand_vec		PARAMS ((rtx, int));
+ static void print_code			PARAMS ((RTX_CODE));
+ static void gen_exp			PARAMS ((rtx));
+ static void gen_split			PARAMS ((rtx));
+ static void gen_rtx_scratch		PARAMS ((rtx));
+ 
+ 
+ /* Create a new node in sequence after LAST.  */
+ 
+ static struct decision *
+ new_decision (position, last)
+      const char *position;
+      struct decision_head *last;
+ {
+   register struct decision *new
+     = (struct decision *) xmalloc (sizeof (struct decision));
+ 
+   memset (new, 0, sizeof (*new));
+   new->success = *last;
+   new->position = xstrdup (position);
+   new->number = next_number++;
+ 
+   last->first = last->last = new;
+   return new;
+ }
+ 
+ /* Create a new test and link it in at PLACE.  */
+ 
+ static struct decision_test *
+ new_decision_test (type, pplace)
+      enum decision_type type;
+      struct decision_test ***pplace;
+ {
+   struct decision_test **place = *pplace;
+   struct decision_test *test;
+ 
+   test = (struct decision_test *) xmalloc (sizeof (*test));
+   test->next = *place;
+   test->type = type;
+   *place = test;
+ 
+   place = &test->next;
+   *pplace = place;
+ 
+   return test;
+ }
+ 
+ /* Search for and return operand N.  */
+ 
+ static rtx
+ find_operand (pattern, n)
+      rtx pattern;
+      int n;
+ {
+   const char *fmt;
+   RTX_CODE code;
+   int i, j, len;
+   rtx r;
+ 
+   code = GET_CODE (pattern);
+   if ((code == MATCH_SCRATCH
+        || code == MATCH_INSN
+        || code == MATCH_OPERAND
+        || code == MATCH_OPERATOR
+        || code == MATCH_PARALLEL)
+       && XINT (pattern, 0) == n)
+     return pattern;
+ 
+   fmt = GET_RTX_FORMAT (code);
+   len = GET_RTX_LENGTH (code);
+   for (i = 0; i < len; i++)
+     {
+       switch (fmt[i])
+ 	{
+ 	case 'e': case 'u':
+ 	  if ((r = find_operand (XEXP (pattern, i), n)) != NULL_RTX)
+ 	    return r;
+ 	  break;
+ 
+ 	case 'V':
+ 	  if (! XVEC (pattern, i))
+ 	    break;
+ 	  /* FALLTHRU */
+ 
+ 	case 'E':
+ 	  for (j = 0; j < XVECLEN (pattern, i); j++)
+ 	    if ((r = find_operand (XVECEXP (pattern, i, j), n)) != NULL_RTX)
+ 	      return r;
+ 	  break;
+ 
+ 	case 'i': case 'w': case '0': case 's':
+ 	  break;
+ 
+ 	default:
+ 	  abort ();
+ 	}
+     }
+ 
+   return NULL;
+ }
+ 
+ /* Search for and return operand M, such that it has a matching
+    constraint for operand N.  */
+ 
+ static rtx
+ find_matching_operand (pattern, n)
+      rtx pattern;
+      int n;
+ {
+   const char *fmt;
+   RTX_CODE code;
+   int i, j, len;
+   rtx r;
+ 
+   code = GET_CODE (pattern);
+   if (code == MATCH_OPERAND
+       && (XSTR (pattern, 2)[0] == '0' + n
+ 	  || (XSTR (pattern, 2)[0] == '%'
+ 	      && XSTR (pattern, 2)[1] == '0' + n)))
+     return pattern;
+ 
+   fmt = GET_RTX_FORMAT (code);
+   len = GET_RTX_LENGTH (code);
+   for (i = 0; i < len; i++)
+     {
+       switch (fmt[i])
+ 	{
+ 	case 'e': case 'u':
+ 	  if ((r = find_matching_operand (XEXP (pattern, i), n)))
+ 	    return r;
+ 	  break;
+ 
+ 	case 'V':
+ 	  if (! XVEC (pattern, i))
+ 	    break;
+ 	  /* FALLTHRU */
+ 
+ 	case 'E':
+ 	  for (j = 0; j < XVECLEN (pattern, i); j++)
+ 	    if ((r = find_matching_operand (XVECEXP (pattern, i, j), n)))
+ 	      return r;
+ 	  break;
+ 
+ 	case 'i': case 'w': case '0': case 's':
+ 	  break;
+ 
+ 	default:
+ 	  abort ();
+ 	}
+     }
+ 
+   return NULL;
+ }
+ 
+ 
+ /* Check for various errors in patterns.  SET is nonnull for a destination,
+    and is the complete set pattern.  SET_CODE is '=' for normal sets, and
+    '+' within a context that requires in-out constraints.  */
+ 
+ static void
+ validate_pattern (pattern, insn, set, set_code)
+      rtx pattern;
+      rtx insn;
+      rtx set;
+      int set_code;
+ {
+   const char *fmt;
+   RTX_CODE code;
+   size_t i, len;
+   int j;
+ 
+   code = GET_CODE (pattern);
+   switch (code)
+     {
+     case MATCH_SCRATCH:
+       return;
+ 
+     case MATCH_INSN:
+     case MATCH_OPERAND:
+     case MATCH_OPERATOR:
+       {
+ 	const char *pred_name = XSTR (pattern, 1);
+ 	int allows_non_lvalue = 1, allows_non_const = 1;
+ 	int special_mode_pred = 0;
+ 	const char *c_test;
+ 
+ 	if (GET_CODE (insn) == DEFINE_INSN)
+ 	  c_test = XSTR (insn, 2);
+ 	else
+ 	  c_test = XSTR (insn, 1);
+ 
+ 	if (pred_name[0] != 0)
+ 	  {
+ 	    for (i = 0; i < NUM_KNOWN_PREDS; i++)
+ 	      if (! strcmp (preds[i].name, pred_name))
+ 		break;
+ 
+ 	    if (i < NUM_KNOWN_PREDS)
+ 	      {
+ 		int j;
+ 
+ 		allows_non_lvalue = allows_non_const = 0;
+ 		for (j = 0; preds[i].codes[j] != 0; j++)
+ 		  {
+ 		    RTX_CODE c = preds[i].codes[j];
+ 		    if (c != LABEL_REF
+ 			&& c != SYMBOL_REF
+ 			&& c != CONST_INT
+ 			&& c != CONST_DOUBLE
+ 			&& c != CONST
+ 			&& c != HIGH
+ 			&& c != CONSTANT_P_RTX)
+ 		      allows_non_const = 1;
+ 
+ 		    if (c != REG
+ 			&& c != SUBREG
+ 			&& c != MEM
+ 			&& c != CONCAT
+ 			&& c != PARALLEL
+ 			&& c != STRICT_LOW_PART)
+ 		      allows_non_lvalue = 1;
+ 		  }
+ 	      }
+ 	    else
+ 	      {
+ #ifdef PREDICATE_CODES
+ 		/* If the port has a list of the predicates it uses but
+ 		   omits one, warn.  */
+ 		message_with_line (pattern_lineno,
+ 				   "warning: `%s' not in PREDICATE_CODES",
+ 				   pred_name);
+ #endif
+ 	      }
+ 
+ 	    for (i = 0; i < NUM_SPECIAL_MODE_PREDS; ++i)
+ 	      if (strcmp (pred_name, special_mode_pred_table[i]) == 0)
+ 		{
+ 		  special_mode_pred = 1;
+ 		  break;
+ 		}
+ 	  }
+ 
+ 	/* A MATCH_OPERAND that is a SET should have an output reload.  */
+ 	if (set && code == MATCH_OPERAND
+ 	    && XSTR (pattern, 2)[0] != '\0')
+ 	  {
+ 	    if (set_code == '+')
+ 	      {
+ 		if (XSTR (pattern, 2)[0] == '+')
+ 		  ;
+ 		/* If we've only got an output reload for this operand,
+ 		   we'd better have a matching input operand.  */
+ 		else if (XSTR (pattern, 2)[0] == '='
+ 			 && find_matching_operand (insn, XINT (pattern, 0)))
+ 		  ;
+ 		else
+ 		  {
+ 		    message_with_line (pattern_lineno,
+ 				       "operand %d missing in-out reload",
+ 				       XINT (pattern, 0));
+ 		    error_count++;
+ 		  }
+ 	      }
+ 	    else if (XSTR (pattern, 2)[0] != '='
+ 		     && XSTR (pattern, 2)[0] != '+')
+ 	      {
+ 		message_with_line (pattern_lineno,
+ 				   "operand %d missing output reload", 
+ 				   XINT (pattern, 0));
+ 		error_count++;
+ 	      }
+ 	  }
+ 
+ 	/* Allowing non-lvalues in destinations -- particularly CONST_INT --
+ 	   while not likely to occur at runtime, results in less efficient
+ 	   code from insn-recog.c.  */
+ 	if (set
+ 	    && pred_name[0] != '\0'
+ 	    && allows_non_lvalue)
+ 	  {
+ 	    message_with_line (pattern_lineno,
+ 			"warning: destination operand %d allows non-lvalue",
+ 			XINT (pattern, 0));
+ 	  }
+ 
+ 	/* A modeless MATCH_OPERAND can be handy when we can
+ 	   check for multiple modes in the c_test.  In most other cases,
+ 	   it is a mistake.  Only DEFINE_INSN is eligible, since SPLIT
+ 	   and PEEP2 can FAIL within the output pattern.  Exclude 
+ 	   address_operand, since its mode is related to the mode of
+ 	   the memory not the operand.  Exclude the SET_DEST of a call
+ 	   instruction, as that is a common idiom.  */
+ 
+ 	if (GET_MODE (pattern) == VOIDmode
+ 	    && code == MATCH_OPERAND
+ 	    && GET_CODE (insn) == DEFINE_INSN
+ 	    && allows_non_const
+ 	    && ! special_mode_pred
+ 	    && pred_name[0] != '\0'
+ 	    && strcmp (pred_name, "address_operand") != 0
+ 	    && strstr (c_test, "operands") == NULL
+ 	    && ! (set
+ 		  && GET_CODE (set) == SET
+ 		  && GET_CODE (SET_SRC (set)) == CALL))
+ 	  {
+ 	    message_with_line (pattern_lineno,
+ 			       "warning: operand %d missing mode?",
+ 			       XINT (pattern, 0));
+ 	  }
+ 	return;
+       }
+ 
+     case SET:
+       {
+ 	enum machine_mode dmode, smode;
+ 	rtx dest, src;
+ 
+ 	dest = SET_DEST (pattern);
+ 	src = SET_SRC (pattern);
+ 
+ 	/* Find the referant for a DUP.  */
+ 
+ 	if (GET_CODE (dest) == MATCH_DUP
+ 	    || GET_CODE (dest) == MATCH_OP_DUP
+ 	    || GET_CODE (dest) == MATCH_PAR_DUP)
+ 	  dest = find_operand (insn, XINT (dest, 0));
+ 
+ 	if (GET_CODE (src) == MATCH_DUP
+ 	    || GET_CODE (src) == MATCH_OP_DUP
+ 	    || GET_CODE (src) == MATCH_PAR_DUP)
+ 	  src = find_operand (insn, XINT (src, 0));
+ 
+ 	/* STRICT_LOW_PART is a wrapper.  Its argument is the real
+ 	   destination, and it's mode should match the source.  */
+ 	if (GET_CODE (dest) == STRICT_LOW_PART)
+ 	  dest = XEXP (dest, 0);
+ 
+ 	dmode = GET_MODE (dest);
+ 	smode = GET_MODE (src);
+ 
+ 	/* The mode of an ADDRESS_OPERAND is the mode of the memory
+ 	   reference, not the mode of the address.  */
+ 	if (GET_CODE (src) == MATCH_OPERAND
+ 	    && ! strcmp (XSTR (src, 1), "address_operand"))
+ 	  ;
+ 
+         /* The operands of a SET must have the same mode unless one
+ 	   is VOIDmode.  */
+         else if (dmode != VOIDmode && smode != VOIDmode && dmode != smode)
+ 	  {
+ 	    message_with_line (pattern_lineno,
+ 			       "mode mismatch in set: %smode vs %smode",
+ 			       GET_MODE_NAME (dmode), GET_MODE_NAME (smode));
+ 	    error_count++;
+ 	  }
+ 
+ 	/* If only one of the operands is VOIDmode, and PC or CC0 is 
+ 	   not involved, it's probably a mistake.  */
+ 	else if (dmode != smode
+ 		 && GET_CODE (dest) != PC
+ 		 && GET_CODE (dest) != CC0
+ 		 && GET_CODE (src) != PC
+ 		 && GET_CODE (src) != CC0
+ 		 && GET_CODE (src) != CONST_INT)
+ 	  {
+ 	    const char *which;
+ 	    which = (dmode == VOIDmode ? "destination" : "source");
+ 	    message_with_line (pattern_lineno,
+ 			       "warning: %s missing a mode?", which);
+ 	  }
+ 
+ 	if (dest != SET_DEST (pattern))
+ 	  validate_pattern (dest, insn, pattern, '=');
+ 	validate_pattern (SET_DEST (pattern), insn, pattern, '=');
+         validate_pattern (SET_SRC (pattern), insn, NULL_RTX, 0);
+         return;
+       }
+ 
+     case CLOBBER:
+       validate_pattern (SET_DEST (pattern), insn, pattern, '=');
+       return;
+ 
+     case ZERO_EXTRACT:
+       validate_pattern (XEXP (pattern, 0), insn, set, set ? '+' : 0);
+       validate_pattern (XEXP (pattern, 1), insn, NULL_RTX, 0);
+       validate_pattern (XEXP (pattern, 2), insn, NULL_RTX, 0);
+       return;
+ 
+     case STRICT_LOW_PART:
+       validate_pattern (XEXP (pattern, 0), insn, set, set ? '+' : 0);
+       return;
+ 
+     case LABEL_REF:
+       if (GET_MODE (XEXP (pattern, 0)) != VOIDmode)
+ 	{
+ 	  message_with_line (pattern_lineno,
+ 			     "operand to label_ref %smode not VOIDmode",
+ 			     GET_MODE_NAME (GET_MODE (XEXP (pattern, 0))));
+ 	  error_count++;
+ 	}
+       break;
+ 
+     default:
+       break;
+     }
+ 
+   fmt = GET_RTX_FORMAT (code);
+   len = GET_RTX_LENGTH (code);
+   for (i = 0; i < len; i++)
+     {
+       switch (fmt[i])
+ 	{
+ 	case 'e': case 'u':
+ 	  validate_pattern (XEXP (pattern, i), insn, NULL_RTX, 0);
+ 	  break;
+ 
+ 	case 'E':
+ 	  for (j = 0; j < XVECLEN (pattern, i); j++)
+ 	    validate_pattern (XVECEXP (pattern, i, j), insn, NULL_RTX, 0);
+ 	  break;
+ 
+ 	case 'i': case 'w': case '0': case 's':
+ 	  break;
+ 
+ 	default:
+ 	  abort ();
+ 	}
+     }
+ }
+ 
+ /* Create a chain of nodes to verify that an rtl expression matches
+    PATTERN.
+ 
+    LAST is a pointer to the listhead in the previous node in the chain (or
+    in the calling function, for the first node).
+ 
+    POSITION is the string representing the current position in the insn.
+ 
+    A pointer to the final node in the chain is returned.  */
+ 
+ static struct decision *
+ add_to_sequence (pattern, last, position, top)
+      rtx pattern;
+      struct decision_head *last;
+      const char *position;
+      int top;
+ {
+   RTX_CODE code;
+   struct decision *this, *sub;
+   struct decision_test *test;
+   struct decision_test **place;
+   char *subpos;
+   register size_t i;
+   register const char *fmt;
+   int depth = strlen (position);
+   int len;
+   enum machine_mode mode;
+ 
+   if (depth > max_depth)
+     max_depth = depth;
+ 
+   subpos = (char *) xmalloc (depth + 2);
+   strcpy (subpos, position);
+   subpos[depth + 1] = 0;
+ 
+   sub = this = new_decision (position, last);
+   place = &this->tests;
+ 
+  restart:
+   mode = GET_MODE (pattern);
+   code = GET_CODE (pattern);
+ 
+   switch (code)
+     {
+     case PARALLEL:
+       /* Toplevel peephole or simplify pattern. */
+       if (top)
+ 	{
+ 	  /* We don't need the node we just created -- unlink it.  */
+ 	  last->first = last->last = NULL;
+ 
+ 	  for (i = 0; i < (size_t) XVECLEN (pattern, 0); i++)
+ 	    {
+ 	      /* Which insn we're looking at is represented by A-Z. We don't
+ 	         ever use 'A', however; it is always implied. */
+ 
+ 	      subpos[depth] = (i > 0 ? 'A' + i : 0);
+ 	      sub = add_to_sequence (XVECEXP (pattern, 0, i),
+ 				     last, subpos, 0);
+ 	      last = &sub->success;
+ 	    }
+ 	  goto ret;
+ 	}
+ 
+       /* Else nothing special.  */
+       break;
+ 
+     case MATCH_PARALLEL:
+       /* The explicit patterns within a match_parallel enforce a minimum
+ 	 length on the vector.  The match_parallel predicate may allow
+ 	 for more elements.  We do need to check for this minimum here
+ 	 or the code generated to match the internals may reference data
+ 	 beyond the end of the vector.  */
+       test = new_decision_test (DT_veclen_ge, &place);
+       test->u.veclen = XVECLEN (pattern, 2);
+       /* FALLTHRU */
+ 
+     case MATCH_OPERAND:
+     case MATCH_SCRATCH:
+     case MATCH_OPERATOR:
+     case MATCH_INSN:
+       {
+ 	const char *pred_name;
+ 	RTX_CODE was_code = code;
+ 	int allows_const_int = 1;
+ 
+ 	if (code == MATCH_SCRATCH)
+ 	  {
+ 	    pred_name = "scratch_operand";
+ 	    code = UNKNOWN;
+ 	  }
+ 	else
+ 	  {
+ 	    pred_name = XSTR (pattern, 1);
+ 	    if (code == MATCH_PARALLEL)
+ 	      code = PARALLEL;
+ 	    else
+ 	      code = UNKNOWN;
+ 	  }
+ 
+ 	if (pred_name[0] != 0)
+ 	  {
+ 	    test = new_decision_test (DT_pred, &place);
+ 	    test->u.pred.name = pred_name;
+ 	    test->u.pred.mode = mode;
+ 
+ 	    /* See if we know about this predicate and save its number.  If
+ 	       we do, and it only accepts one code, note that fact.  The
+ 	       predicate `const_int_operand' only tests for a CONST_INT, so
+ 	       if we do so we can avoid calling it at all.
+ 
+ 	       Finally, if we know that the predicate does not allow
+ 	       CONST_INT, we know that the only way the predicate can match
+ 	       is if the modes match (here we use the kludge of relying on
+ 	       the fact that "address_operand" accepts CONST_INT; otherwise,
+ 	       it would have to be a special case), so we can test the mode
+ 	       (but we need not).  This fact should considerably simplify the
+ 	       generated code.  */
+ 
+ 	    for (i = 0; i < NUM_KNOWN_PREDS; i++)
+ 	      if (! strcmp (preds[i].name, pred_name))
+ 		break;
+ 
+ 	    if (i < NUM_KNOWN_PREDS)
+ 	      {
+ 		int j;
+ 
+ 		test->u.pred.index = i;
+ 
+ 		if (preds[i].codes[1] == 0 && code == UNKNOWN)
+ 		  code = preds[i].codes[0];
+ 
+ 		allows_const_int = 0;
+ 		for (j = 0; preds[i].codes[j] != 0; j++)
+ 		  if (preds[i].codes[j] == CONST_INT)
+ 		    {
+ 		      allows_const_int = 1;
+ 		      break;
+ 		    }
+ 	      }
+ 	    else
+ 	      test->u.pred.index = -1;
+ 	  }
+ 
+ 	/* Can't enforce a mode if we allow const_int.  */
+ 	if (allows_const_int)
+ 	  mode = VOIDmode;
+ 
+ 	/* Accept the operand, ie. record it in `operands'.  */
+ 	test = new_decision_test (DT_accept_op, &place);
+ 	test->u.opno = XINT (pattern, 0);
+ 
+ 	if (was_code == MATCH_OPERATOR || was_code == MATCH_PARALLEL)
+ 	  {
+ 	    char base = (was_code == MATCH_OPERATOR ? '0' : 'a');
+ 	    for (i = 0; i < (size_t) XVECLEN (pattern, 2); i++)
+ 	      {
+ 		subpos[depth] = i + base;
+ 		sub = add_to_sequence (XVECEXP (pattern, 2, i),
+ 				       &sub->success, subpos, 0);
+ 	      }
+ 	  }
+ 	goto fini;
+       }
+ 
+     case MATCH_OP_DUP:
+       code = UNKNOWN;
+ 
+       test = new_decision_test (DT_dup, &place);
+       test->u.dup = XINT (pattern, 0);
+ 
+       test = new_decision_test (DT_accept_op, &place);
+       test->u.opno = XINT (pattern, 0);
+ 
+       for (i = 0; i < (size_t) XVECLEN (pattern, 1); i++)
+ 	{
+ 	  subpos[depth] = i + '0';
+ 	  sub = add_to_sequence (XVECEXP (pattern, 1, i),
+ 				 &sub->success, subpos, 0);
+ 	}
+       goto fini;
+ 
+     case MATCH_DUP:
+     case MATCH_PAR_DUP:
+       code = UNKNOWN;
+ 
+       test = new_decision_test (DT_dup, &place);
+       test->u.dup = XINT (pattern, 0);
+       goto fini;
+ 
+     case ADDRESS:
+       pattern = XEXP (pattern, 0);
+       goto restart;
+ 
+     default:
+       break;
+     }
+ 
+   fmt = GET_RTX_FORMAT (code);
+   len = GET_RTX_LENGTH (code);
+ 
+   /* Do tests against the current node first.  */
+   for (i = 0; i < (size_t) len; i++)
+     {
+       if (fmt[i] == 'i')
+ 	{
+ 	  if (i == 0)
+ 	    {
+ 	      test = new_decision_test (DT_elt_zero_int, &place);
+ 	      test->u.intval = XINT (pattern, i);
+ 	    }
+ 	  else if (i == 1)
+ 	    {
+ 	      test = new_decision_test (DT_elt_one_int, &place);
+ 	      test->u.intval = XINT (pattern, i);
+ 	    }
+ 	  else
+ 	    abort ();
+ 	}
+       else if (fmt[i] == 'w')
+ 	{
+ 	  if (i != 0)
+ 	    abort ();
+ 
+ 	  test = new_decision_test (DT_elt_zero_wide, &place);
+ 	  test->u.intval = XWINT (pattern, i);
+ 	}
+       else if (fmt[i] == 'E')
+ 	{
+ 	  if (i != 0)
+ 	    abort ();
+ 
+ 	  test = new_decision_test (DT_veclen, &place);
+ 	  test->u.veclen = XVECLEN (pattern, i);
+ 	}
+     }
+ 
+   /* Now test our sub-patterns.  */
+   for (i = 0; i < (size_t) len; i++)
+     {
+       switch (fmt[i])
+ 	{
+ 	case 'e': case 'u':
+ 	  subpos[depth] = '0' + i;
+ 	  sub = add_to_sequence (XEXP (pattern, i), &sub->success,
+ 				 subpos, 0);
+ 	  break;
+ 
+ 	case 'E':
+ 	  {
+ 	    register int j;
+ 	    for (j = 0; j < XVECLEN (pattern, i); j++)
+ 	      {
+ 		subpos[depth] = 'a' + j;
+ 		sub = add_to_sequence (XVECEXP (pattern, i, j),
+ 				       &sub->success, subpos, 0);
+ 	      }
+ 	    break;
+ 	  }
+ 
+ 	case 'i': case 'w':
+ 	  /* Handled above.  */
+ 	  break;
+ 	case '0':
+ 	  break;
+ 
+ 	default:
+ 	  abort ();
+ 	}
+     }
+ 
+  fini:
+   /* Insert nodes testing mode and code, if they're still relevant,
+      before any of the nodes we may have added above.  */
+   if (code != UNKNOWN)
+     {
+       place = &this->tests;
+       test = new_decision_test (DT_code, &place);
+       test->u.code = code;
+     }
+ 
+   if (mode != VOIDmode)
+     {
+       place = &this->tests;
+       test = new_decision_test (DT_mode, &place);
+       test->u.mode = mode;
+     }
+ 
+   /* If we didn't insert any tests or accept nodes, hork.  */
+   if (this->tests == NULL)
+     abort ();
+ 
+  ret:
+   free (subpos);
+   return sub;
+ }
+ 
+ /* A subroutine of maybe_both_true; examines only one test.
+    Returns > 0 for "definitely both true" and < 0 for "maybe both true".  */
+ 
+ static int
+ maybe_both_true_2 (d1, d2)
+      struct decision_test *d1, *d2;
+ {
+   if (d1->type == d2->type)
+     {
+       switch (d1->type)
+ 	{
+ 	case DT_mode:
+ 	  return d1->u.mode == d2->u.mode;
+ 
+ 	case DT_code:
+ 	  return d1->u.code == d2->u.code;
+ 
+ 	case DT_veclen:
+ 	  return d1->u.veclen == d2->u.veclen;
+ 
+ 	case DT_elt_zero_int:
+ 	case DT_elt_one_int:
+ 	case DT_elt_zero_wide:
+ 	  return d1->u.intval == d2->u.intval;
+ 
+ 	default:
+ 	  break;
+ 	}
+     }
+ 
+   /* If either has a predicate that we know something about, set
+      things up so that D1 is the one that always has a known
+      predicate.  Then see if they have any codes in common.  */
+ 
+   if (d1->type == DT_pred || d2->type == DT_pred)
+     {
+       if (d2->type == DT_pred)
+ 	{
+ 	  struct decision_test *tmp;
+ 	  tmp = d1, d1 = d2, d2 = tmp;
+ 	}
+ 
+       /* If D2 tests a mode, see if it matches D1.  */
+       if (d1->u.pred.mode != VOIDmode)
+ 	{
+ 	  if (d2->type == DT_mode)
+ 	    {
+ 	      if (d1->u.pred.mode != d2->u.mode
+ 		  /* The mode of an address_operand predicate is the
+ 		     mode of the memory, not the operand.  It can only
+ 		     be used for testing the predicate, so we must
+ 		     ignore it here.  */
+ 		  && strcmp (d1->u.pred.name, "address_operand") != 0)
+ 		return 0;
+ 	    }
+ 	  /* Don't check two predicate modes here, because if both predicates
+ 	     accept CONST_INT, then both can still be true even if the modes
+ 	     are different.  If they don't accept CONST_INT, there will be a
+ 	     separate DT_mode that will make maybe_both_true_1 return 0.  */
+ 	}
+ 
+       if (d1->u.pred.index >= 0)
+ 	{
+ 	  /* If D2 tests a code, see if it is in the list of valid
+ 	     codes for D1's predicate.  */
+ 	  if (d2->type == DT_code)
+ 	    {
+ 	      const RTX_CODE *c = &preds[d1->u.pred.index].codes[0];
+ 	      while (*c != 0)
+ 		{
+ 		  if (*c == d2->u.code)
+ 		    break;
+ 		  ++c;
+ 		}
+ 	      if (*c == 0)
+ 		return 0;
+ 	    }
+ 
+ 	  /* Otherwise see if the predicates have any codes in common.  */
+ 	  else if (d2->type == DT_pred && d2->u.pred.index >= 0)
+ 	    {
+ 	      const RTX_CODE *c1 = &preds[d1->u.pred.index].codes[0];
+ 	      int common = 0;
+ 
+ 	      while (*c1 != 0 && !common)
+ 		{
+ 		  const RTX_CODE *c2 = &preds[d2->u.pred.index].codes[0];
+ 		  while (*c2 != 0 && !common)
+ 		    {
+ 		      common = (*c1 == *c2);
+ 		      ++c2;
+ 		    }
+ 		  ++c1;
+ 		}
+ 
+ 	      if (!common)
+ 		return 0;
+ 	    }
+ 	}
+     }
+ 
+   /* Tests vs veclen may be known when strict equality is involved.  */
+   if (d1->type == DT_veclen && d2->type == DT_veclen_ge)
+     return d1->u.veclen >= d2->u.veclen;
+   if (d1->type == DT_veclen_ge && d2->type == DT_veclen)
+     return d2->u.veclen >= d1->u.veclen;
+ 
+   return -1;
+ }
+ 
+ /* A subroutine of maybe_both_true; examines all the tests for a given node.
+    Returns > 0 for "definitely both true" and < 0 for "maybe both true".  */
+ 
+ static int
+ maybe_both_true_1 (d1, d2)
+      struct decision_test *d1, *d2;
+ {
+   struct decision_test *t1, *t2;
+ 
+   /* A match_operand with no predicate can match anything.  Recognize
+      this by the existance of a lone DT_accept_op test.  */
+   if (d1->type == DT_accept_op || d2->type == DT_accept_op)
+     return 1;
+ 
+   /* Eliminate pairs of tests while they can exactly match.  */
+   while (d1 && d2 && d1->type == d2->type)
+     {
+       if (maybe_both_true_2 (d1, d2) == 0)
+ 	return 0;
+       d1 = d1->next, d2 = d2->next;
+     }
+ 
+   /* After that, consider all pairs.  */
+   for (t1 = d1; t1 ; t1 = t1->next)
+     for (t2 = d2; t2 ; t2 = t2->next)
+       if (maybe_both_true_2 (t1, t2) == 0)
+ 	return 0;
+ 
+   return -1;
+ }
+ 
+ /* Return 0 if we can prove that there is no RTL that can match both
+    D1 and D2.  Otherwise, return 1 (it may be that there is an RTL that
+    can match both or just that we couldn't prove there wasn't such an RTL).
+ 
+    TOPLEVEL is non-zero if we are to only look at the top level and not
+    recursively descend.  */
+ 
+ static int
+ maybe_both_true (d1, d2, toplevel)
+      struct decision *d1, *d2;
+      int toplevel;
+ {
+   struct decision *p1, *p2;
+   int cmp;
+ 
+   /* Don't compare strings on the different positions in insn.  Doing so
+      is incorrect and results in false matches from constructs like
+ 
+ 	[(set (subreg:HI (match_operand:SI "register_operand" "r") 0)
+ 	      (subreg:HI (match_operand:SI "register_operand" "r") 0))]
+      vs
+ 	[(set (match_operand:HI "register_operand" "r")
+ 	      (match_operand:HI "register_operand" "r"))]
+ 
+      If we are presented with such, we are recursing through the remainder
+      of a node's success nodes (from the loop at the end of this function).
+      Skip forward until we come to a position that matches.
+ 
+      Due to the way position strings are constructed, we know that iterating
+      forward from the lexically lower position (e.g. "00") will run into
+      the lexically higher position (e.g. "1") and not the other way around.
+      This saves a bit of effort.  */
+ 
+   cmp = strcmp (d1->position, d2->position);
+   if (cmp != 0)
+     {
+       if (toplevel)
+ 	abort();
+ 
+       /* If the d2->position was lexically lower, swap.  */
+       if (cmp > 0)
+ 	p1 = d1, d1 = d2, d2 = p1;
+ 
+       if (d1->success.first == 0)
+ 	return 1;
+       for (p1 = d1->success.first; p1; p1 = p1->next)
+ 	if (maybe_both_true (p1, d2, 0))
+ 	  return 1;
+ 
+       return 0;
+     }
+ 
+   /* Test the current level.  */
+   cmp = maybe_both_true_1 (d1->tests, d2->tests);
+   if (cmp >= 0)
+     return cmp;
+ 
+   /* We can't prove that D1 and D2 cannot both be true.  If we are only
+      to check the top level, return 1.  Otherwise, see if we can prove
+      that all choices in both successors are mutually exclusive.  If
+      either does not have any successors, we can't prove they can't both
+      be true.  */
+ 
+   if (toplevel || d1->success.first == 0 || d2->success.first == 0)
+     return 1;
+ 
+   for (p1 = d1->success.first; p1; p1 = p1->next)
+     for (p2 = d2->success.first; p2; p2 = p2->next)
+       if (maybe_both_true (p1, p2, 0))
+ 	return 1;
+ 
+   return 0;
+ }
+ 
+ /* A subroutine of nodes_identical.  Examine two tests for equivalence.  */
+ 
+ static int
+ nodes_identical_1 (d1, d2)
+      struct decision_test *d1, *d2;
+ {
+   switch (d1->type)
+     {
+     case DT_mode:
+       return d1->u.mode == d2->u.mode;
+ 
+     case DT_code:
+       return d1->u.code == d2->u.code;
+ 
+     case DT_pred:
+       return (d1->u.pred.mode == d2->u.pred.mode
+ 	      && strcmp (d1->u.pred.name, d2->u.pred.name) == 0);
+ 
+     case DT_c_test:
+       return strcmp (d1->u.c_test, d2->u.c_test) == 0;
+ 
+     case DT_veclen:
+     case DT_veclen_ge:
+       return d1->u.veclen == d2->u.veclen;
+ 
+     case DT_dup:
+       return d1->u.dup == d2->u.dup;
+ 
+     case DT_elt_zero_int:
+     case DT_elt_one_int:
+     case DT_elt_zero_wide:
+       return d1->u.intval == d2->u.intval;
+ 
+     case DT_accept_op:
+       return d1->u.opno == d2->u.opno;
+ 
+     case DT_accept_insn:
+       /* Differences will be handled in merge_accept_insn.  */
+       return 1;
+ 
+     default:
+       abort ();
+     }
+ }
+ 
+ /* True iff the two nodes are identical (on one level only).  Due
+    to the way these lists are constructed, we shouldn't have to 
+    consider different orderings on the tests.  */
+ 
+ static int
+ nodes_identical (d1, d2)
+      struct decision *d1, *d2;
+ {
+   struct decision_test *t1, *t2;
+ 
+   for (t1 = d1->tests, t2 = d2->tests; t1 && t2; t1 = t1->next, t2 = t2->next)
+     {
+       if (t1->type != t2->type)
+ 	return 0;
+       if (! nodes_identical_1 (t1, t2))
+ 	return 0;
+     }
+ 
+   /* For success, they should now both be null.  */
+   if (t1 != t2)
+     return 0;
+ 
+   /* Check that their subnodes are at the same position, as any one set
+      of sibling decisions must be at the same position.  Allowing this
+      requires complications to find_afterward and when change_state is
+      invoked.  */
+   if (d1->success.first
+       && d2->success.first
+       && strcmp (d1->success.first->position, d2->success.first->position))
+     return 0;
+ 
+   return 1;
+ }
+ 
+ /* A subroutine of merge_trees; given two nodes that have been declared
+    identical, cope with two insn accept states.  If they differ in the
+    number of clobbers, then the conflict was created by make_insn_sequence
+    and we can drop the with-clobbers version on the floor.  If both 
+    nodes have no additional clobbers, we have found an ambiguity in the
+    source machine description.  */
+ 
+ static void
+ merge_accept_insn (oldd, addd)
+      struct decision *oldd, *addd;
+ {
+   struct decision_test *old, *add;
+ 
+   for (old = oldd->tests; old; old = old->next)
+     if (old->type == DT_accept_insn)
+       break;
+   if (old == NULL)
+     return;
+ 
+   for (add = addd->tests; add; add = add->next)
+     if (add->type == DT_accept_insn)
+       break;
+   if (add == NULL)
+     return;
+ 
+   /* If one node is for a normal insn and the second is for the base
+      insn with clobbers stripped off, the second node should be ignored.  */
+ 
+   if (old->u.insn.num_clobbers_to_add == 0
+       && add->u.insn.num_clobbers_to_add > 0)
+     {
+       /* Nothing to do here.  */
+     }
+   else if (old->u.insn.num_clobbers_to_add > 0
+ 	   && add->u.insn.num_clobbers_to_add == 0)
+     {
+       /* In this case, replace OLD with ADD.  */
+       old->u.insn = add->u.insn;
+     }
+   else
+     {
+       message_with_line (add->u.insn.lineno, "`%s' matches `%s'",
+ 			 get_insn_name (add->u.insn.code_number),
+ 			 get_insn_name (old->u.insn.code_number));
+       message_with_line (old->u.insn.lineno, "previous definition of `%s'",
+ 			 get_insn_name (old->u.insn.code_number));
+       error_count++;
+     }
+ }
+ 
+ /* Merge two decision trees OLDH and ADDH, modifying OLDH destructively.  */
+ 
+ static void
+ merge_trees (oldh, addh)
+      struct decision_head *oldh, *addh;
+ {
+   struct decision *next, *add;
+ 
+   if (addh->first == 0)
+     return;
+   if (oldh->first == 0)
+     {
+       *oldh = *addh;
+       return;
+     }
+ 
+   /* Trying to merge bits at different positions isn't possible.  */
+   if (strcmp (oldh->first->position, addh->first->position))
+     abort ();
+ 
+   for (add = addh->first; add ; add = next)
+     {
+       struct decision *old, *insert_before = NULL;
+ 
+       next = add->next;
+ 
+       /* The semantics of pattern matching state that the tests are
+ 	 done in the order given in the MD file so that if an insn
+ 	 matches two patterns, the first one will be used.  However,
+ 	 in practice, most, if not all, patterns are unambiguous so
+ 	 that their order is independent.  In that case, we can merge
+ 	 identical tests and group all similar modes and codes together.
+ 
+ 	 Scan starting from the end of OLDH until we reach a point
+ 	 where we reach the head of the list or where we pass a
+ 	 pattern that could also be true if NEW is true.  If we find
+ 	 an identical pattern, we can merge them.  Also, record the
+ 	 last node that tests the same code and mode and the last one
+ 	 that tests just the same mode.
+ 
+ 	 If we have no match, place NEW after the closest match we found.  */
+ 	 
+       for (old = oldh->last; old; old = old->prev)
+ 	{
+ 	  if (nodes_identical (old, add))
+ 	    {
+ 	      merge_accept_insn (old, add);
+ 	      merge_trees (&old->success, &add->success);
+ 	      goto merged_nodes;
+ 	    }
+ 
+ 	  if (maybe_both_true (old, add, 0))
+ 	    break;
+ 
+ 	  /* Insert the nodes in DT test type order, which is roughly
+ 	     how expensive/important the test is.  Given that the tests
+ 	     are also ordered within the list, examining the first is
+ 	     sufficient.  */
+ 	  if ((int) add->tests->type < (int) old->tests->type)
+ 	    insert_before = old;
+ 	}
+ 
+       if (insert_before == NULL)
+ 	{
+ 	  add->next = NULL;
+ 	  add->prev = oldh->last;
+ 	  oldh->last->next = add;
+ 	  oldh->last = add;
+ 	}
+       else
+ 	{
+ 	  if ((add->prev = insert_before->prev) != NULL)
+ 	    add->prev->next = add;
+ 	  else
+ 	    oldh->first = add;
+ 	  add->next = insert_before;
+ 	  insert_before->prev = add;
+ 	}
+ 
+     merged_nodes:;
+     }
+ }
+ 
+ /* Walk the tree looking for sub-nodes that perform common tests.  
+    Factor out the common test into a new node.  This enables us
+    (depending on the test type) to emit switch statements later.  */
+ 
+ static void
+ factor_tests (head)
+      struct decision_head *head;
+ {
+   struct decision *first, *next;
+ 
+   for (first = head->first; first && first->next; first = next)
+     {
+       enum decision_type type;
+       struct decision *new, *old_last;
+ 
+       type = first->tests->type;
+       next = first->next;
+ 
+       /* Want at least two compatible sequential nodes.  */
+       if (next->tests->type != type)
+ 	continue;
+ 
+       /* Don't want all node types, just those we can turn into 
+ 	 switch statements.  */
+       if (type != DT_mode
+ 	  && type != DT_code
+ 	  && type != DT_veclen
+ 	  && type != DT_elt_zero_int
+ 	  && type != DT_elt_one_int
+ 	  && type != DT_elt_zero_wide)
+ 	continue;
+ 
+       /* If we'd been performing more than one test, create a new node
+          below our first test.  */
+       if (first->tests->next != NULL)
+ 	{
+ 	  new = new_decision (first->position, &first->success);
+ 	  new->tests = first->tests->next;
+ 	  first->tests->next = NULL;
+ 	}
+ 	
+       /* Crop the node tree off after our first test.  */
+       first->next = NULL;
+       old_last = head->last;
+       head->last = first;
+ 
+       /* For each compatible test, adjust to perform only one test in
+ 	 the top level node, then merge the node back into the tree.  */
+       do
+ 	{
+ 	  struct decision_head h;
+ 
+ 	  if (next->tests->next != NULL)
+ 	    {
+ 	      new = new_decision (next->position, &next->success);
+ 	      new->tests = next->tests->next;
+ 	      next->tests->next = NULL;
+ 	    }
+ 	  new = next;
+ 	  next = next->next;
+ 	  new->next = NULL;
+ 	  h.first = h.last = new;
+ 
+ 	  merge_trees (head, &h);
+ 	}
+       while (next && next->tests->type == type);
+ 
+       /* After we run out of compatible tests, graft the remaining nodes
+ 	 back onto the tree.  */
+       if (next)
+ 	{
+ 	  next->prev = head->last;
+ 	  head->last->next = next;
+ 	  head->last = old_last;
+ 	}
+     }
+ 
+   /* Recurse.  */
+   for (first = head->first; first; first = first->next)
+     factor_tests (&first->success);
+ }
+ 
+ /* After factoring, try to simplify the tests on any one node.
+    Tests that are useful for switch statements are recognizable
+    by having only a single test on a node -- we'll be manipulating
+    nodes with multiple tests:
+ 
+    If we have mode tests or code tests that are redundant with
+    predicates, remove them.  */
+ 
+ static void
+ simplify_tests (head)
+      struct decision_head *head;
+ {
+   struct decision *tree;
+ 
+   for (tree = head->first; tree; tree = tree->next)
+     {
+       struct decision_test *a, *b;
+ 
+       a = tree->tests;
+       b = a->next;
+       if (b == NULL)
+ 	continue;
+ 
+       /* Find a predicate node.  */
+       while (b && b->type != DT_pred)
+ 	b = b->next;
+       if (b)
+ 	{
+ 	  /* Due to how these tests are constructed, we don't even need
+ 	     to check that the mode and code are compatible -- they were
+ 	     generated from the predicate in the first place.  */
+ 	  while (a->type == DT_mode || a->type == DT_code)
+ 	    a = a->next;
+ 	  tree->tests = a;
+ 	}
+     }
+ 
+   /* Recurse.  */
+   for (tree = head->first; tree; tree = tree->next)
+     simplify_tests (&tree->success);
+ }
+ 
+ /* Count the number of subnodes of HEAD.  If the number is high enough,
+    make the first node in HEAD start a separate subroutine in the C code
+    that is generated.  */
+ 
+ static int
+ break_out_subroutines (head, initial)
+      struct decision_head *head;
+      int initial;
+ {
+   int size = 0;
+   struct decision *sub;
+ 
+   for (sub = head->first; sub; sub = sub->next)
+     size += 1 + break_out_subroutines (&sub->success, 0);
+ 
+   if (size > SUBROUTINE_THRESHOLD && ! initial)
+     {
+       head->first->subroutine_number = ++next_subroutine_number;
+       size = 1;
+     }
+   return size;
+ }
+ 
+ /* For each node p, find the next alternative that might be true
+    when p is true.  */
+ 
+ static void
+ find_afterward (head, real_afterward)
+      struct decision_head *head;
+      struct decision *real_afterward;
+ {
+   struct decision *p, *q, *afterward;
+ 
+   /* We can't propogate alternatives across subroutine boundaries. 
+      This is not incorrect, merely a minor optimization loss.  */
+ 
+   p = head->first;
+   afterward = (p->subroutine_number > 0 ? NULL : real_afterward);
+ 
+   for ( ; p ; p = p->next)
+     {
+       /* Find the next node that might be true if this one fails.  */
+       for (q = p->next; q ; q = q->next)
+ 	if (maybe_both_true (p, q, 1))
+ 	  break;
+ 
+       /* If we reached the end of the list without finding one, 
+ 	 use the incoming afterward position.  */
+       if (!q)
+ 	q = afterward;
+       p->afterward = q;
+       if (q)
+ 	q->need_label = 1;
+     }
+ 
+   /* Recurse.  */
+   for (p = head->first; p ; p = p->next)
+     if (p->success.first)
+       find_afterward (&p->success, p->afterward);
+ 
+   /* When we are generating a subroutine, record the real afterward
+      position in the first node where write_tree can find it, and we
+      can do the right thing at the subroutine call site.  */
+   p = head->first;
+   if (p->subroutine_number > 0)
+     p->afterward = real_afterward;
+ }
+ 
+ /* Assuming that the state of argument is denoted by OLDPOS, take whatever
+    actions are necessary to move to NEWPOS.  If we fail to move to the
+    new state, branch to node AFTERWARD if non-zero, otherwise return.
+ 
+    Failure to move to the new state can only occur if we are trying to
+    match multiple insns and we try to step past the end of the stream. */
+ 
+ static void
+ change_state (oldpos, newpos, afterward, indent)
+      const char *oldpos;
+      const char *newpos;
+      struct decision *afterward;
+      const char *indent;
+ {
+   int odepth = strlen (oldpos);
+   int ndepth = strlen (newpos);
+   int depth;
+   int old_has_insn, new_has_insn;
+ 
+   /* Pop up as many levels as necessary.  */
+   for (depth = odepth; strncmp (oldpos, newpos, depth) != 0; --depth)
+     continue;
+ 
+   /* Hunt for the last [A-Z] in both strings.  */
+   for (old_has_insn = odepth - 1; old_has_insn >= 0; --old_has_insn)
+     if (oldpos[old_has_insn] >= 'A' && oldpos[old_has_insn] <= 'Z')
+       break;
+   for (new_has_insn = ndepth - 1; new_has_insn >= 0; --new_has_insn)
+     if (newpos[new_has_insn] >= 'A' && newpos[new_has_insn] <= 'Z')
+       break;
+ 
+   /* Go down to desired level.  */
+   while (depth < ndepth)
+     {
+       /* It's a different insn from the first one. */
+       if (newpos[depth] >= 'A' && newpos[depth] <= 'Z')
+ 	{
+ 	  /* We can only fail if we're moving down the tree.  */
+ 	  if (old_has_insn >= 0 && oldpos[old_has_insn] >= newpos[depth])
+ 	    {
+ 	      printf ("%stem = peep2_next_insn (%d);\n", 
+ 		      indent, newpos[depth] - 'A');
+ 	    }
+ 	  else
+ 	    {
+ 	      printf ("%stem = peep2_next_insn (%d);\n", 
+ 		      indent, newpos[depth] - 'A');
+ 	      printf ("%sif (tem == NULL_RTX)\n", indent);
+ 	      if (afterward)
+ 		printf ("%s  goto L%d;\n", indent, afterward->number);
+ 	      else
+ 		printf ("%s  goto ret0;\n", indent);
+ 	    }
+ 	  printf ("%sx%d = PATTERN (tem);\n", indent, depth + 1);
+ 	}
+       else if (newpos[depth] >= 'a' && newpos[depth] <= 'z')
+ 	printf ("%sx%d = XVECEXP (x%d, 0, %d);\n",
+ 		indent, depth + 1, depth, newpos[depth] - 'a');
+       else
+ 	printf ("%sx%d = XEXP (x%d, %c);\n",
+ 		indent, depth + 1, depth, newpos[depth]);
+       ++depth;
+     }
+ }
+ 
+ /* Print the enumerator constant for CODE -- the upcase version of
+    the name.  */
+ 
+ static void
+ print_code (code)
+      enum rtx_code code;
+ {
+   register const char *p;
+   for (p = GET_RTX_NAME (code); *p; p++)
+     putchar (TOUPPER (*p));
+ }
+ 
+ /* Emit code to cross an afterward link -- change state and branch.  */
+ 
+ static void
+ write_afterward (start, afterward, indent)
+      struct decision *start;
+      struct decision *afterward;
+      const char *indent;
+ {
+   if (!afterward || start->subroutine_number > 0)
+     printf("%sgoto ret0;\n", indent);
+   else
+     {
+       change_state (start->position, afterward->position, NULL, indent);
+       printf ("%sgoto L%d;\n", indent, afterward->number);
+     }
+ }
+ 
+ /* Emit a switch statement, if possible, for an initial sequence of 
+    nodes at START.  Return the first node yet untested.  */
+ 
+ static struct decision *
+ write_switch (start, depth)
+      struct decision *start;
+      int depth;
+ {
+   struct decision *p = start;
+   enum decision_type type = p->tests->type;
+   struct decision *needs_label = NULL;
+ 
+   /* If we have two or more nodes in sequence that test the same one
+      thing, we may be able to use a switch statement.  */
+ 
+   if (!p->next
+       || p->tests->next
+       || p->next->tests->type != type
+       || p->next->tests->next
+       || nodes_identical_1 (p->tests, p->next->tests))
+     return p;
+ 
+   /* DT_code is special in that we can do interesting things with
+      known predicates at the same time.  */
+   if (type == DT_code)
+     {
+       char codemap[NUM_RTX_CODE];
+       struct decision *ret;
+       RTX_CODE code;
+ 
+       memset (codemap, 0, sizeof(codemap));
+ 
+       printf ("  switch (GET_CODE (x%d))\n    {\n", depth);
+       code = p->tests->u.code;
+       do 
+ 	{
+ 	  if (p != start && p->need_label && needs_label == NULL)
+ 	    needs_label = p;
+ 
+ 	  printf ("    case ");
+ 	  print_code (code);
+ 	  printf (":\n      goto L%d;\n", p->success.first->number);
+ 	  p->success.first->need_label = 1;
+ 
+ 	  codemap[code] = 1;
+ 	  p = p->next;
+ 	}
+       while (p
+ 	     && ! p->tests->next
+ 	     && p->tests->type == DT_code
+ 	     && ! codemap[code = p->tests->u.code]);
+ 
+       /* If P is testing a predicate that we know about and we haven't
+ 	 seen any of the codes that are valid for the predicate, we can
+ 	 write a series of "case" statement, one for each possible code.
+ 	 Since we are already in a switch, these redundant tests are very
+ 	 cheap and will reduce the number of predicates called.  */
+ 
+       /* Note that while we write out cases for these predicates here,
+ 	 we don't actually write the test here, as it gets kinda messy.
+ 	 It is trivial to leave this to later by telling our caller that
+ 	 we only processed the CODE tests.  */
+       if (needs_label != NULL)
+ 	ret = needs_label;
+       else
+ 	ret = p;
+ 
+       while (p && p->tests->type == DT_pred
+ 	     && p->tests->u.pred.index >= 0)
+ 	{
+ 	  const RTX_CODE *c;
+ 
+ 	  for (c = &preds[p->tests->u.pred.index].codes[0]; *c ; ++c)
+ 	    if (codemap[(int) *c] != 0)
+ 	      goto pred_done;
+ 
+ 	  for (c = &preds[p->tests->u.pred.index].codes[0]; *c ; ++c)
+ 	    {
+ 	      printf ("    case ");
+ 	      print_code (*c);
+ 	      printf (":\n");
+ 	      codemap[(int) *c] = 1;
+ 	    }
+ 
+ 	  printf ("      {goto L%d;\n}", p->number);
+ 	  p->need_label = 1;
+ 	  p = p->next;
+ 	}
+ 
+     pred_done:
+       /* Make the default case skip the predicates we managed to match.  */
+ 
+       printf ("    default:\n");
+       if (p != ret)
+ 	{
+ 	  if (p)
+ 	    {
+ 	      printf ("      goto L%d;\n", p->number);
+ 	      p->need_label = 1;
+ 	    }
+ 	  else
+ 	    write_afterward (start, start->afterward, "      ");
+ 	}
+       else
+       {
+ 	      if (depth > 0)
+ 		      printf ("\t{ \n\t  if (GET_RTX_LENGTH (GET_CODE (x%d)) > 1)\n\t\treturn simplify_insns (x%d, insn);\n\t}\n    break;\n", depth, depth);
+ 	      else
+ 		      printf ("\tbreak;\n");
+       }
+       printf ("   }\n");
+ 
+       return ret;
+     }
+   else if (type == DT_mode
+ 	   || type == DT_veclen
+ 	   || type == DT_elt_zero_int
+ 	   || type == DT_elt_one_int
+ 	   || type == DT_elt_zero_wide)
+     {
+       /* The argument is casted to int.  In case HOST_WIDE_INT is more exact,
+          we can't safely construct switch statement over it.  */
+       if (type == DT_elt_zero_wide && HOST_BITS_PER_WIDE_INT > sizeof (int) * CHAR_BIT)
+ 	  return p;
+       printf ("  switch (");
+       switch (type)
+ 	{
+ 	case DT_mode:
+ 	  printf ("GET_MODE (x%d)", depth);
+ 	  break;
+ 	case DT_veclen:
+ 	  printf ("XVECLEN (x%d, 0)", depth);
+ 	  break;
+ 	case DT_elt_zero_int:
+ 	  printf ("XINT (x%d, 0)", depth);
+ 	  break;
+ 	case DT_elt_one_int:
+ 	  printf ("XINT (x%d, 1)", depth);
+ 	  break;
+ 	case DT_elt_zero_wide:
+ 	  /* Convert result of XWINT to int for portability since some C
+ 	     compilers won't do it and some will.  */
+ 	  printf ("(int) XWINT (x%d, 0)", depth);
+ 	  break;
+ 	default:
+ 	  abort ();
+ 	}
+       printf (")\n    {\n");
+ 
+       do
+ 	{
+ 	  /* Merge trees will not unify identical nodes if their
+ 	     sub-nodes are at different levels.  Thus we must check
+ 	     for duplicate cases.  */
+ 	  struct decision *q;
+ 	  for (q = start; q != p; q = q->next)
+ 	    if (nodes_identical_1 (p->tests, q->tests))
+ 	      goto case_done;
+ 
+ 	  if (p != start && p->need_label && needs_label == NULL)
+ 	    needs_label = p;
+ 
+ 	  printf ("    case ");
+ 	  switch (type)
+ 	    {
+ 	    case DT_mode:
+ 	      printf ("%smode", GET_MODE_NAME (p->tests->u.mode));
+ 	      break;
+ 	    case DT_veclen:
+ 	      printf ("%d", p->tests->u.veclen);
+ 	      break;
+ 	    case DT_elt_zero_int:
+ 	    case DT_elt_one_int:
+ 	    case DT_elt_zero_wide:
+ 	      printf (HOST_WIDE_INT_PRINT_DEC, p->tests->u.intval);
+ 	      break;
+ 	    default:
+ 	      abort ();
+ 	    }
+ 	  printf (":\n      goto L%d;\n", p->success.first->number);
+ 	  p->success.first->need_label = 1;
+ 
+ 	  p = p->next;
+ 	}
+       while (p && p->tests->type == type && !p->tests->next);
+ 
+     case_done:
+       printf ("    default:\n      if (GET_RTX_LENGTH(x%d) > 1)\n return simplify_insns (x%d);\n break;\n    }\n", depth, depth);
+ 
+       return needs_label != NULL ? needs_label : p;
+     }
+   else
+     {
+       /* None of the other tests are ameanable.  */
+       return p;
+     }
+ }
+ 
+ /* Emit code for one test.  */
+ 
+ static void
+ write_cond (p, depth)
+      struct decision_test *p;
+      int depth;
+ {
+   switch (p->type)
+     {
+     case DT_mode:
+       printf ("GET_MODE (x%d) == %smode", depth, GET_MODE_NAME (p->u.mode));
+       break;
+ 
+     case DT_code:
+       printf ("GET_CODE (x%d) == ", depth);
+       print_code (p->u.code);
+       break;
+ 
+     case DT_veclen:
+       printf ("XVECLEN (x%d, 0) == %d", depth, p->u.veclen);
+       break;
+ 
+     case DT_elt_zero_int:
+       printf ("XINT (x%d, 0) == %d", depth, (int) p->u.intval);
+       break;
+ 
+     case DT_elt_one_int:
+       printf ("XINT (x%d, 1) == %d", depth, (int) p->u.intval);
+       break;
+ 
+     case DT_elt_zero_wide:
+       printf ("XWINT (x%d, 0) == ", depth);
+       printf (HOST_WIDE_INT_PRINT_DEC, p->u.intval);
+       break;
+ 
+     case DT_veclen_ge:
+       printf ("XVECLEN (x%d, 0) >= %d", depth, p->u.veclen);
+       break;
+ 
+     case DT_dup:
+       printf ("rtx_equal_p (x%d, operands[%d])", depth, p->u.dup);
+       break;
+ 
+     case DT_pred:
+       printf ("%s (x%d, %smode)", p->u.pred.name, depth,
+ 	      GET_MODE_NAME (p->u.pred.mode));
+       break;
+ 
+     case DT_c_test:
+       printf ("(%s)", p->u.c_test);
+       break;
+ 
+     case DT_accept_insn:
+       abort ();
+       break;
+ 
+     default:
+       abort ();
+     }
+ }
+ 
+ /* Emit code for one action.  The previous tests have succeeded;
+    TEST is the last of the chain.  In the normal case we simply
+    perform a state change.  For the `accept' tests we must do more work.  */
+ 
+ static void
+ write_action (p, test, depth, uncond, success)
+      struct decision *p;
+      struct decision_test *test;
+      int depth, uncond;
+      struct decision *success;
+ {
+   const char *indent;
+   int want_close = 0;
+ 
+   if (uncond)
+     indent = "  ";
+   else if (test->type == DT_accept_op || test->type == DT_accept_insn)
+     {
+       fputs ("    {\n", stdout);
+       indent = "      ";
+       want_close = 1;
+     }
+   else
+     indent = "    ";
+ 
+   if (test->type == DT_accept_op)
+     {
+       printf("%soperands[%d] = x%d;\n", indent, test->u.opno, depth);
+ 
+       /* Only allow DT_accept_insn to follow.  */
+       if (test->next)
+ 	{
+ 	  test = test->next;
+ 	  if (test->type != DT_accept_insn)
+ 	    abort ();
+ 	}
+     }
+ 
+   /* Sanity check that we're now at the end of the list of tests.  */
+   if (test->next)
+     abort ();
+ 
+   if (test->type == DT_accept_insn)
+     {
+       printf ("%sDEBUG_SIMPLIFY (stderr, \"%s\", insn)\n",indent, get_insn_name (test->u.insn.code_number));
+       printf ("%stem = gen_simplify_%d (insn, operands);\n",
+ 	      indent, test->u.insn.code_number);
+       printf ("%sif (tem != 0)\n%s  return tem;\n", indent, indent);
+     }
+   else
+     {
+       printf("%sgoto L%d;\n", indent, success->number);
+       success->need_label = 1;
+     }
+ 
+   if (want_close)
+     fputs ("    }\n", stdout);
+ }
+ 
+ /* Return 1 if the test is always true and has no fallthru path.  Return -1
+    if the test does have a fallthru path, but requires that the condition be
+    terminated.  Otherwise return 0 for a normal test.  */
+ /* ??? is_unconditional is a stupid name for a tri-state function.  */
+ 
+ static int
+ is_unconditional (t)
+      struct decision_test *t;     
+ {
+   if (t->type == DT_accept_op)
+     return 1;
+ 
+   if (t->type == DT_accept_insn)
+     {
+       return -1;
+     }
+ 
+   return 0;
+ }
+ 
+ /* Emit code for one node -- the conditional and the accompanying action.
+    Return true if there is no fallthru path.  */
+ 
+ static int
+ write_node (p, depth)
+      struct decision *p;
+      int depth;
+ {
+   struct decision_test *test, *last_test;
+   int uncond;
+ 
+   last_test = test = p->tests;
+   uncond = is_unconditional (test);
+   if (uncond == 0)
+     {
+       printf ("  if (");
+       write_cond (test, depth);
+ 
+       while ((test = test->next) != NULL)
+ 	{
+ 	  int uncond2;
+ 
+ 	  last_test = test;
+ 	  uncond2 = is_unconditional (test);
+ 	  if (uncond2 != 0)
+ 	    break;
+ 
+ 	  printf ("\n      && ");
+ 	  write_cond (test, depth);
+ 	}
+ 
+       printf (")\n");
+     }
+ 
+   write_action (p, last_test, depth, uncond, p->success.first);
+ 
+   return uncond > 0;
+ }
+ 
+ /* Emit code for all of the sibling nodes of HEAD.  */
+ 
+ static void
+ write_tree_1 (head, depth)
+      struct decision_head *head;
+      int depth;
+ {
+   struct decision *p, *next;
+   int uncond = 0;
+ 
+   for (p = head->first; p ; p = next)
+     {
+       /* The label for the first element was printed in write_tree.  */
+       if (p != head->first && p->need_label)
+ 	OUTPUT_LABEL (" ", p->number);
+ 
+       /* Attempt to write a switch statement for a whole sequence.  */
+       next = write_switch (p, depth);
+       if (p != next)
+ 	uncond = 0;
+       else
+ 	{
+ 	  /* Failed -- fall back and write one node.  */
+ 	  uncond = write_node (p, depth);
+ 	  next = p->next;
+ 	}
+     }
+ 
+   /* Finished with this chain.  Close a fallthru path by branching
+      to the afterward node.  */
+   if (! uncond)
+     write_afterward (head->last, head->last->afterward, "  ");
+ }
+ 
+ /* Write out the decision tree starting at HEAD.  PREVPOS is the
+    position at the node that branched to this node.  */
+ 
+ static void
+ write_tree (head, prevpos, initial)
+      struct decision_head *head;
+      const char *prevpos;
+      int initial;
+ {
+   register struct decision *p = head->first;
+ 
+   putchar ('\n');
+   if (p->need_label)
+     OUTPUT_LABEL (" ", p->number);
+ 
+   if (! initial && p->subroutine_number > 0)
+     {
+       static const char * const name_prefix = "simplify";
+       static const char * const call_suffix = "";
+       
+ 
+       /* This node has been broken out into a separate subroutine.
+ 	 Call it, test the result, and branch accordingly.  */
+ 
+       if (p->afterward)
+ 	{
+ 	  printf ("  tem = %s_%d (x0, insn%s);\n",
+ 		  name_prefix, p->subroutine_number, call_suffix);
+ 	  printf ("  if (tem != 0)\n    return tem;\n");
+ 	  change_state (p->position, p->afterward->position, NULL, "  ");
+ 	  printf ("  goto L%d;\n", p->afterward->number);
+ 	}
+       else
+ 	{
+ 	  printf ("  return %s_%d (x0, insn%s);\n",
+ 		  name_prefix, p->subroutine_number, call_suffix);
+ 	}
+     }
+   else
+     {
+       int depth = strlen (p->position);
+ 
+       change_state (prevpos, p->position, head->last->afterward, "  ");
+       write_tree_1 (head, depth);
+ 
+       for (p = head->first; p; p = p->next)
+         if (p->success.first)
+           write_tree (&p->success, p->position, 0);
+     }
+ }
+ 
+ /* Write out a subroutine of type TYPE to do comparisons starting at
+    node TREE.  */
+ 
+ static void
+ write_subroutine (head)
+      struct decision_head *head;
+ {
+   int subfunction = head->first ? head->first->subroutine_number : 0;
+   const char *s_or_e;
+   char extension[32];
+   int i;
+   
+   s_or_e = subfunction ? "static " : "";
+ 
+   if (subfunction)
+     sprintf (extension, "_%d", subfunction);
+   else
+     strcpy (extension, "_insns");
+   
+   printf ("%srtx simplify%s PARAMS ((rtx, rtx));\n",
+ 	  s_or_e, extension);
+   printf ("%srtx\n\
+ simplify%s (x0, insn)\n\
+      register rtx x0;\n\
+      rtx insn ATTRIBUTE_UNUSED;\n\ ", s_or_e, extension);
+   
+   printf ("{\n  register rtx * const operands ATTRIBUTE_UNUSED = &recog_data.operand[0];\n");
+   for (i = 1; i <= max_depth; i++)
+     printf ("  register rtx x%d ATTRIBUTE_UNUSED;\n", i);
+   
+   printf ("  rtx tem ATTRIBUTE_UNUSED;\n");
+   
+   if (!subfunction)
+     printf ("  recog_data.insn = NULL_RTX;\n");
+   
+   if (head->first)
+     write_tree (head, "", 1);
+   else
+     printf ("  goto ret0;\n");
+   
+   printf (" ret0:\n  return 0;\n}\n\n");
+ }
+ 
+ /* In break_out_subroutines, we discovered the boundaries for the
+    subroutines, but did not write them out.  Do so now.  */
+ 
+ static void
+ write_subroutines (head)
+      struct decision_head *head;
+ {
+   struct decision *p;
+ 
+   for (p = head->first; p ; p = p->next)
+     if (p->success.first)
+       write_subroutines (&p->success);
+ 
+   if (head->first->subroutine_number > 0)
+     write_subroutine (head);
+ }
+ 
+ /* Begin the output file.  */
+ 
+ static void
+ write_header ()
+ {
+   puts ("\
+ /* Generated automatically by the program `gensimplify` from simplify.md\n\
+    machine description file.  */\n\
+ \n\
+ #include \"config.h\"\n\
+ #include \"system.h\"\n\
+ #include \"rtl.h\"\n\
+ #include \"tm_p.h\"\n\
+ #include \"function.h\"\n\
+ #include \"insn-config.h\"\n\
+ #include \"recog.h\"\n\
+ #include \"real.h\"\n\
+ #include \"output.h\"\n\
+ #include \"flags.h\"\n\
+ #include \"hard-reg-set.h\"\n\
+ #include \"resource.h\"\n\
+ #include \"toplev.h\"\n\
+ \n");
+ 
+   puts ("\n\
+ /*\n\
+    The function simplify_insns returns 0 if the rtl could not\n\
+    be matched. If there was a match, the new rtl is returned in a sequence.\n\
+ */\n\n");
+   puts ("\n\static int simplify_debug_level=1;\n\
+   #define DEBUG_SIMPLIFY(x, y, z) if (simplify_debug_level > 0) { fprintf (x, \"Executing simplifier rule %s on insn \", y); print_rtl_single (x, z); }\n");
+ }
+ 
+ 
+ /* Construct and return a sequence of decisions
+    that will recognize INSN.
+ */
+ 
+ static struct decision_head
+ make_insn_sequence (insn)
+      rtx insn;
+ {
+   rtx x;
+   const char *c_test = XSTR (insn, 2);
+   struct decision *last;
+   struct decision_test *test, **place;
+   struct decision_head head;
+   char c_test_pos[2];
+ 
+   record_insn_name (next_insn_code, (XSTR (insn, 0)));
+ 
+   c_test_pos[0] = '\0';
+ 
+   if (XVECLEN (insn, 1) == 1)
+     x = XVECEXP (insn, 1, 0);
+   else
+     {
+       x = rtx_alloc (PARALLEL);
+       XVEC (x, 0) = XVEC (insn, 1);
+       PUT_MODE (x, VOIDmode);
+     }
+ 
+   validate_pattern (x, insn, NULL_RTX, 0);
+ 
+   memset(&head, 0, sizeof(head));
+   last = add_to_sequence (x, &head, "", 1);
+ 
+   /* Find the end of the test chain on the last node.  */
+   for (test = last->tests; test->next; test = test->next)
+     continue;
+   place = &test->next;
+ 
+   if (c_test[0])
+     {
+       /* Need a new node if we have another test to add.  */
+       if (test->type == DT_accept_op)
+ 	{
+ 	  last = new_decision (c_test_pos, &last->success);
+ 	  place = &last->tests;
+ 	}
+       test = new_decision_test (DT_c_test, &place);
+       test->u.c_test = c_test;
+     }
+ 
+   test = new_decision_test (DT_accept_insn, &place);
+   test->u.insn.code_number = next_insn_code;
+   test->u.insn.lineno = pattern_lineno;
+   test->u.insn.num_clobbers_to_add = 0;
+ 
+   /* Define the subroutine we will call brlow and emit in genemit.  */
+   printf ("extern rtx gen_simplify_%d PARAMS ((rtx, rtx *));\n",
+ 	  next_insn_code);
+ 
+   return head;
+ }
+ 
+ static void
+ process_tree (head)
+      struct decision_head *head;
+ {
+   if (head->first != NULL)
+     {
+       factor_tests (head);
+ 
+       next_subroutine_number = 0;
+       break_out_subroutines (head, 1);
+       find_afterward (head, NULL);
+ 
+       /* We run this after find_afterward, because find_afterward needs
+ 	 the redundant DT_mode tests on predicates to determine whether
+ 	 two tests can both be true or not.  */
+       simplify_tests(head);
+ 
+       write_subroutines (head);
+     }
+ 
+   write_subroutine (head);
+ }
+ 
+ extern int main PARAMS ((int, char **));
+ 
+ int
+ main (argc, argv)
+      int argc;
+      char **argv;
+ {
+   rtx desc;
+   struct decision_head simplify_tree, h;
+ 
+   progname = "gensimplify";
+ 
+   memset (&simplify_tree, 0, sizeof simplify_tree);
+ 
+   if (argc <= 1)
+     fatal ("No input file name.");
+ 
+   if (init_md_reader (argv[1]) != SUCCESS_EXIT_CODE)
+     return (FATAL_EXIT_CODE);
+ 
+   next_insn_code = 0;
+   next_index = 0;
+ 
+   write_header ();
+ 
+   /* Read the machine description.  */
+ 
+   while (1)
+     {
+       desc = read_md_rtx (&pattern_lineno, &next_insn_code);
+       if (desc == NULL)
+ 	break;
+ 
+       else if (GET_CODE (desc) == DEFINE_SIMPLIFY)
+         {
+  	  h = make_insn_sequence (desc);
+ 	  merge_trees (&simplify_tree, &h);
+ 	  gen_split (desc);
+ 	}
+ 	
+       next_index++;
+     }
+ 
+   if (error_count)
+     return FATAL_EXIT_CODE;
+ 
+   puts ("\n\n");
+ 
+   process_tree (&simplify_tree);
+   
+   fflush (stdout);
+   return (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
+ }
+ 
+ /* Define this so we can link with print-rtl.o to get debug_rtx function.  */
+ const char *
+ get_insn_name (code)
+      int code;
+ {
+   if (code < insn_name_ptr_size)
+     return insn_name_ptr[code];
+   else
+     return NULL;
+ }
+ 
+ static void
+ record_insn_name (code, name)
+      int code;
+      const char *name;
+ {
+   static const char *last_real_name = "insn";
+   static int last_real_code = 0;
+   char *new;
+ 
+   if (insn_name_ptr_size <= code)
+     {
+       int new_size;
+       new_size = (insn_name_ptr_size ? insn_name_ptr_size * 2 : 512);
+       insn_name_ptr =
+ 	(char **) xrealloc (insn_name_ptr, sizeof(char *) * new_size);
+       memset (insn_name_ptr + insn_name_ptr_size, 0, 
+ 	      sizeof(char *) * (new_size - insn_name_ptr_size));
+       insn_name_ptr_size = new_size;
+     }
+ 
+   if (!name || name[0] == '\0')
+     {
+       new = xmalloc (strlen (last_real_name) + 10);
+       sprintf (new, "%s+%d", last_real_name, code - last_real_code);
+     }
+   else
+     {
+       last_real_name = new = xstrdup (name);
+       last_real_code = code;
+     }
+   
+   insn_name_ptr[code] = new;
+ }  
+ 
+ static void
+ debug_decision_2 (test)
+      struct decision_test *test;
+ {
+   switch (test->type)
+     {
+     case DT_mode:
+       fprintf (stderr, "mode=%s", GET_MODE_NAME (test->u.mode));
+       break;
+     case DT_code:
+       fprintf (stderr, "code=%s", GET_RTX_NAME (test->u.code));
+       break;
+     case DT_veclen:
+       fprintf (stderr, "veclen=%d", test->u.veclen);
+       break;
+     case DT_elt_zero_int:
+       fprintf (stderr, "elt0_i=%d", (int) test->u.intval);
+       break;
+     case DT_elt_one_int:
+       fprintf (stderr, "elt1_i=%d", (int) test->u.intval);
+       break;
+     case DT_elt_zero_wide:
+       fprintf (stderr, "elt0_w=");
+       fprintf (stderr, HOST_WIDE_INT_PRINT_DEC, test->u.intval);
+       break;
+     case DT_veclen_ge:
+       fprintf (stderr, "veclen>=%d", test->u.veclen);
+       break;
+     case DT_dup:
+       fprintf (stderr, "dup=%d", test->u.dup);
+       break;
+     case DT_pred:
+       fprintf (stderr, "pred=(%s,%s)",
+ 	       test->u.pred.name, GET_MODE_NAME(test->u.pred.mode));
+       break;
+     case DT_c_test:
+       {
+ 	char sub[16+4];
+ 	strncpy (sub, test->u.c_test, sizeof(sub));
+ 	memcpy (sub+16, "...", 4);
+ 	fprintf (stderr, "c_test=\"%s\"", sub);
+       }
+       break;
+     case DT_accept_op:
+       fprintf (stderr, "A_op=%d", test->u.opno);
+       break;
+     case DT_accept_insn:
+       fprintf (stderr, "A_insn=(%d,%d)", 
+ 	       test->u.insn.code_number, test->u.insn.num_clobbers_to_add);
+       break;
+ 
+     default:
+       abort ();
+     }
+ }
+ 
+ static void
+ debug_decision_1 (d, indent)
+      struct decision *d;
+      int indent;
+ {
+   int i;
+   struct decision_test *test;
+ 
+   if (d == NULL)
+     {
+       for (i = 0; i < indent; ++i)
+ 	putc (' ', stderr);
+       fputs ("(nil)\n", stderr);
+       return;
+     }
+ 
+   for (i = 0; i < indent; ++i)
+     putc (' ', stderr);
+ 
+   putc ('{', stderr);
+   test = d->tests;
+   if (test)
+     {
+       debug_decision_2 (test);
+       while ((test = test->next) != NULL)
+ 	{
+ 	  fputs (" + ", stderr);
+ 	  debug_decision_2 (test);
+ 	}
+     }
+   fprintf (stderr, "} %d n %d a %d\n", d->number,
+ 	   (d->next ? d->next->number : -1),
+ 	   (d->afterward ? d->afterward->number : -1));
+ }
+ 
+ static void
+ debug_decision_0 (d, indent, maxdepth)
+      struct decision *d;
+      int indent, maxdepth;
+ {
+   struct decision *n;
+   int i;
+ 
+   if (maxdepth < 0)
+     return;
+   if (d == NULL)
+     {
+       for (i = 0; i < indent; ++i)
+ 	putc (' ', stderr);
+       fputs ("(nil)\n", stderr);
+       return;
+     }
+ 
+   debug_decision_1 (d, indent);
+   for (n = d->success.first; n ; n = n->next)
+     debug_decision_0 (n, indent + 2, maxdepth - 1);
+ }
+ 
+ void
+ debug_decision (d)
+      struct decision *d;
+ {
+   debug_decision_0 (d, 0, 1000000);
+ }
+ 
+ void
+ debug_decision_list (d)
+      struct decision *d;
+ {
+   while (d)
+     {
+       debug_decision_0 (d, 0, 0);
+       d = d->next;
+     }
+ }
+ /* Below here was ripped from genemit.c */
+ 
+ 
+ static void
+ max_operand_1 (x)
+      rtx x;
+ {
+   register RTX_CODE code;
+   register int i;
+   register int len;
+   register const char *fmt;
+ 
+   if (x == 0)
+     return;
+ 
+   code = GET_CODE (x);
+ 
+   if (code == MATCH_OPERAND && XSTR (x, 2) != 0 && *XSTR (x, 2) != '\0')
+     register_constraints = 1;
+   if (code == MATCH_SCRATCH && XSTR (x, 1) != 0 && *XSTR (x, 1) != '\0')
+     register_constraints = 1;
+   if (code == MATCH_OPERAND || code == MATCH_OPERATOR
+       || code == MATCH_PARALLEL)
+     max_opno = MAX (max_opno, XINT (x, 0));
+   if (code == MATCH_DUP || code == MATCH_OP_DUP || code == MATCH_PAR_DUP)
+     max_dup_opno = MAX (max_dup_opno, XINT (x, 0));
+   if (code == MATCH_SCRATCH)
+     max_scratch_opno = MAX (max_scratch_opno, XINT (x, 0));
+ 
+   fmt = GET_RTX_FORMAT (code);
+   len = GET_RTX_LENGTH (code);
+   for (i = 0; i < len; i++)
+     {
+       if (fmt[i] == 'e' || fmt[i] == 'u')
+ 	max_operand_1 (XEXP (x, i));
+       else if (fmt[i] == 'E')
+ 	{
+ 	  int j;
+ 	  for (j = 0; j < XVECLEN (x, i); j++)
+ 	    max_operand_1 (XVECEXP (x, i, j));
+ 	}
+     }
+ }
+ 
+ static int
+ max_operand_vec (insn, arg)
+      rtx insn;
+      int arg;
+ {
+   register int len = XVECLEN (insn, arg);
+   register int i;
+ 
+   max_opno = -1;
+   max_dup_opno = -1;
+   max_scratch_opno = -1;
+ 
+   for (i = 0; i < len; i++)
+     max_operand_1 (XVECEXP (insn, arg, i));
+ 
+   return max_opno + 1;
+ }
+ 
+ static void
+ gen_rtx_scratch (x)
+      rtx x;
+ {
+   printf ("operand%d", XINT (x, 0));
+ }
+ 
+ /* Print a C expression to construct an RTX just like X,
+    substituting any operand references appearing within.  */
+ 
+ static void
+ gen_exp (x)
+      rtx x;
+ {
+   register RTX_CODE code;
+   register int i;
+   register int len;
+   register const char *fmt;
+ 
+   if (x == 0)
+     {
+       printf ("NULL_RTX");
+       return;
+     }
+ 
+   code = GET_CODE (x);
+ 
+   switch (code)
+     {
+     case MATCH_OPERAND:
+     case MATCH_DUP:
+       printf ("operand%d", XINT (x, 0));
+       return;
+ 
+     case MATCH_OP_DUP:
+       printf ("gen_rtx (GET_CODE (operand%d), ", XINT (x, 0));
+       if (GET_MODE (x) == VOIDmode)
+ 	printf ("GET_MODE (operand%d)", XINT (x, 0));
+       else
+ 	printf ("%smode", GET_MODE_NAME (GET_MODE (x)));
+       for (i = 0; i < XVECLEN (x, 1); i++)
+ 	{
+ 	  printf (",\n\t\t");
+ 	  gen_exp (XVECEXP (x, 1, i));
+ 	}
+       printf (")");
+       return;
+ 
+     case MATCH_OPERATOR:
+       printf ("gen_rtx (GET_CODE (operand%d)", XINT (x, 0));
+       printf (", %smode", GET_MODE_NAME (GET_MODE (x)));
+       for (i = 0; i < XVECLEN (x, 2); i++)
+ 	{
+ 	  printf (",\n\t\t");
+ 	  gen_exp (XVECEXP (x, 2, i));
+ 	}
+       printf (")");
+       return;
+ 
+     case MATCH_PARALLEL:
+     case MATCH_PAR_DUP:
+       printf ("operand%d", XINT (x, 0));
+       return;
+ 
+     case MATCH_SCRATCH:
+       gen_rtx_scratch (x);
+       return;
+ 
+     case ADDRESS:
+       fatal ("ADDRESS expression code used in named instruction pattern");
+ 
+     case PC:
+       printf ("pc_rtx");
+       return;
+ 
+     case CC0:
+       printf ("cc0_rtx");
+       return;
+ 
+     case CONST_INT:
+       if (INTVAL (x) == 0)
+ 	printf ("const0_rtx");
+       else if (INTVAL (x) == 1)
+ 	printf ("const1_rtx");
+       else if (INTVAL (x) == -1)
+ 	printf ("constm1_rtx");
+       else if (INTVAL (x) == STORE_FLAG_VALUE)
+ 	printf ("const_true_rtx");
+       else
+ 	{
+ 	  printf ("GEN_INT (");
+ 	  printf (HOST_WIDE_INT_PRINT_DEC, INTVAL (x));
+ 	  printf (")");
+ 	}
+       return;
+ 
+     case CONST_DOUBLE:
+       /* These shouldn't be written in MD files.  Instead, the appropriate
+ 	 routines in varasm.c should be called.  */
+       abort ();
+ 
+     default:
+       break;
+     }
+ 
+   printf ("gen_rtx_");
+   print_code (code);
+   printf (" (%smode", GET_MODE_NAME (GET_MODE (x)));
+ 
+   fmt = GET_RTX_FORMAT (code);
+   len = GET_RTX_LENGTH (code);
+   for (i = 0; i < len; i++)
+     {
+       if (fmt[i] == '0')
+ 	break;
+       printf (",\n\t");
+       if (fmt[i] == 'e' || fmt[i] == 'u')
+ 	gen_exp (XEXP (x, i));
+       else if (fmt[i] == 'i')
+ 	printf ("%u", XINT (x, i));
+       else if (fmt[i] == 's')
+ 	printf ("\"%s\"", XSTR (x, i));
+       else if (fmt[i] == 'E')
+ 	{
+ 	  int j;
+ 	  printf ("gen_rtvec (%d", XVECLEN (x, i));
+ 	  for (j = 0; j < XVECLEN (x, i); j++)
+ 	    {
+ 	      printf (",\n\t\t");
+ 	      gen_exp (XVECEXP (x, i, j));
+ 	    }
+ 	  printf (")");
+ 	}
+       else
+ 	abort ();
+     }
+   printf (")");
+ }  
+ 
+ /* Like gen_expand, but generates a SEQUENCE.  */
+ 
+ static void
+ gen_split (split)
+      rtx split;
+ {
+   register int i;
+   int operands;
+   const char *name = "split";
+   const char *unused;
+ 
+   if (GET_CODE (split) == DEFINE_SIMPLIFY)
+     name = "simplify";
+   if (XVEC (split, 0) == 0)
+     fatal ("define_%s (definition %d) lacks a pattern", name,
+ 	   next_index);
+   else if (XVEC (split, 2) == 0)
+     fatal ("define_%s (definition %d) lacks a replacement pattern", name,
+ 	   next_index);
+ 
+   /* Find out how many operands this function has.  */
+ 
+   max_operand_vec (split, 3);
+   operands = MAX (max_opno, MAX (max_dup_opno, max_scratch_opno)) + 1;
+   unused = (operands == 0 ? " ATTRIBUTE_UNUSED" : "");
+ 
+   /* Output the prototype, function name and argument declarations.  */
+       printf ("rtx\ngen_%s_%d (curr_insn, operands)\n",
+ 	      name, next_insn_code);
+       printf ("     rtx curr_insn ATTRIBUTE_UNUSED;\n");
+       printf ("     rtx *operands%s;\n", unused);
+   printf ("{\n");
+ 
+   /* Declare all local variables.  */
+   for (i = 0; i < operands; i++)
+     printf ("  rtx operand%d;\n", i);
+   printf ("  rtx _val = 0;\n");
+ 
+   printf ("  start_sequence ();\n");
+ 
+   /* The fourth operand of DEFINE_SPLIT is some code to be executed
+      before the actual construction.  */
+ 
+   if (XSTR (split, 4))
+     printf ("%s\n", XSTR (split, 4));
+ 
+   /* Output code to copy the arguments back out of `operands'  */
+   for (i = 0; i < operands; i++)
+     printf ("  operand%d = operands[%d];\n", i, i);
+ 
+   /* Output code to construct the rtl for the instruction bodies.
+      Use emit_insn to add them to the sequence being accumulated.
+      But don't do this if the user's code has set `no_more' nonzero.  */
+ 
+   for (i = 0; i < XVECLEN (split, 3); i++)
+     {
+       rtx next = XVECEXP (split, 3, i);
+       if ((GET_CODE (next) == SET && GET_CODE (SET_DEST (next)) == PC)
+ 	  || (GET_CODE (next) == PARALLEL
+ 	      && GET_CODE (XVECEXP (next, 0, 0)) == SET
+ 	      && GET_CODE (SET_DEST (XVECEXP (next, 0, 0))) == PC)
+ 	  || GET_CODE (next) == RETURN)
+ 	printf ("  emit_jump_insn (");
+       else if ((GET_CODE (next) == SET && GET_CODE (SET_SRC (next)) == CALL)
+ 	       || GET_CODE (next) == CALL
+ 	       || (GET_CODE (next) == PARALLEL
+ 		   && GET_CODE (XVECEXP (next, 0, 0)) == SET
+ 		   && GET_CODE (SET_SRC (XVECEXP (next, 0, 0))) == CALL)
+ 	       || (GET_CODE (next) == PARALLEL
+ 		   && GET_CODE (XVECEXP (next, 0, 0)) == CALL))
+ 	printf ("  emit_call_insn (");
+       else if (GET_CODE (next) == CODE_LABEL)
+ 	printf ("  emit_label (");
+       else if (GET_CODE (next) == MATCH_OPERAND
+ 	       || GET_CODE (next) == MATCH_OPERATOR
+ 	       || GET_CODE (next) == MATCH_PARALLEL
+ 	       || GET_CODE (next) == MATCH_OP_DUP
+ 	       || GET_CODE (next) == MATCH_DUP
+ 	       || GET_CODE (next) == PARALLEL)
+ 	printf ("  emit (");
+       else
+ 	printf ("  emit_insn (");
+       gen_exp (next);
+       printf (");\n");
+       if (GET_CODE (next) == SET && GET_CODE (SET_DEST (next)) == PC
+ 	  && GET_CODE (SET_SRC (next)) == LABEL_REF)
+ 	printf ("  emit_barrier ();");
+     }
+ 
+   /* Call `gen_sequence' to make a SEQUENCE out of all the
+      insns emitted within this gen_... function.  */
+ 
+   printf ("  _val = gen_sequence ();\n");
+   printf ("  end_sequence ();\n");
+   printf ("  return _val;\n}\n\n");
+ }
+ 
+ 
Index: simplify.md
===================================================================
RCS file: simplify.md
diff -N simplify.md
*** /dev/null	Tue May  5 13:32:27 1998
--- simplify.md	Fri May 11 20:16:18 2001
***************
*** 0 ****
--- 1,111 ----
+ ;;; This is almost exactly the define_peephole2 syntax,
+ ;;; the differences are the string for the rule name
+ ;;; and i dont think scratch registers work (I removed the code, so id
+ ;;; be pretty surprised if they work)
+ 
+ (define_simplify  "neg-neg"
+ [(neg (neg (match_operand 0 "" "")))]
+ ""
+ [(match_dup 0)]
+ ""
+ )
+ 
+ 
+ ;; Simplifications for ~
+ 
+ (define_simplify "not-not"
+ [(not (not (match_operand 0 "" "")))]
+ ""
+ [(match_dup 0)]
+ ""
+ )
+ 
+ (define_simplify "~(a nor b)"
+ [(not (ior (match_operand 0 "" "") (match_operand 1 "" "")))]
+ ""
+ [(ior (match_dup 0) (match_dup 1))]
+ ""
+ )
+ 
+ 
+ ;; Simplifications for +, -
+ 
+ 
+ (define_simplify "x + (-y)"
+ [(plus (match_operand 0 "" "") (neg (match_operand 1 "" "")))]
+ ""
+ [(minus (match_dup 0) (match_dup 1))]
+ ""
+ )
+ 
+ (define_simplify "x - (-y)"
+ [(minus (match_operand 0 "" "") (neg (match_operand 1 "" "")))]
+ ""
+ [(plus (match_dup 0) (match_dup 1))]
+ ""
+ )
+ 
+ (define_simplify "x + 0"
+ [(plus (match_operand 0 "" "") (const_int 0))]
+ ""
+ [(match_dup 0)]
+ ""
+ )
+ 
+ (define_simplify "x - 0"
+ [(minus (match_operand 0 "" "") (const_int 0))]
+ ""
+ [(match_dup 0)]
+ ""
+ )
+ 
+ (define_simplify "0 - x"
+ [(minus (const_int 0) (match_operand 0 "" ""))]
+ ""
+ [(neg (match_dup 0))]
+ ""
+ )
+ 
+ (define_simplify "-x + y"
+ [(plus (neg (match_operand 0 "" "")) (match_operand 1 "" ""))]
+ ""
+ [(minus (match_dup 1) (match_dup 0))]
+ ""
+ )
+ 
+ (define_simplify "x - c"
+ [(minus (match_operand 0 "" "") (match_operand 1 "immediate_operand" ""))]
+ ""
+ [(plus (match_dup 0) (neg (match_dup 1)))]
+ )
+ 
+ ;; Simplifications for *
+ 
+ 
+ (define_simplify "x * 1"
+ [(mult (match_operand 0 "" "") (const_int 1))]
+ ""
+ [(match_dup 0)]
+ ""
+ )
+ 
+ (define_simplify "x * -1"
+ [(mult (match_operand 0 "" "") (const_int -1))]
+ ""
+ [(neg  (match_dup 0))]
+ ""
+ )
+ 
+ (define_simplify "j ^ 0"
+ [(xor (match_operand 0 "" "") (const_int 0))]
+ ""
+ [(match_dup 0)]
+ ""
+ )
+ 
+ (define_simplify "j ^ -1"
+ [(xor (match_operand 0 "" "") (const_int -1))]
+ ""
+ [(not (match_dup 0))]
+ ""
+ )

-- 
"Right now I'm having amnesia and deja vu at the same time.  I
think I've forgotten this before.
"-Steven Wright


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]