This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] Your new friend and mine, gensimplify
- To: gcc-patches at gcc dot gnu dot org
- Subject: [PATCH] Your new friend and mine, gensimplify
- From: Daniel Berlin <dan at cgsoftware dot com>
- Date: 11 May 2001 23:26:26 -0400
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