diff gcc/cse.c @ 0:a06113de4d67

first commit
author kent <kent@cr.ie.u-ryukyu.ac.jp>
date Fri, 17 Jul 2009 14:47:48 +0900
parents
children 58ad6c70ea60
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gcc/cse.c	Fri Jul 17 14:47:48 2009 +0900
@@ -0,0 +1,7000 @@
+/* Common subexpression elimination for GNU compiler.
+   Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+   Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+/* stdio.h must precede rtl.h for FFS.  */
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "tm_p.h"
+#include "hard-reg-set.h"
+#include "regs.h"
+#include "basic-block.h"
+#include "flags.h"
+#include "real.h"
+#include "insn-config.h"
+#include "recog.h"
+#include "function.h"
+#include "expr.h"
+#include "toplev.h"
+#include "output.h"
+#include "ggc.h"
+#include "timevar.h"
+#include "except.h"
+#include "target.h"
+#include "params.h"
+#include "rtlhooks-def.h"
+#include "tree-pass.h"
+#include "df.h"
+#include "dbgcnt.h"
+
+/* The basic idea of common subexpression elimination is to go
+   through the code, keeping a record of expressions that would
+   have the same value at the current scan point, and replacing
+   expressions encountered with the cheapest equivalent expression.
+
+   It is too complicated to keep track of the different possibilities
+   when control paths merge in this code; so, at each label, we forget all
+   that is known and start fresh.  This can be described as processing each
+   extended basic block separately.  We have a separate pass to perform
+   global CSE.
+
+   Note CSE can turn a conditional or computed jump into a nop or
+   an unconditional jump.  When this occurs we arrange to run the jump
+   optimizer after CSE to delete the unreachable code.
+
+   We use two data structures to record the equivalent expressions:
+   a hash table for most expressions, and a vector of "quantity
+   numbers" to record equivalent (pseudo) registers.
+
+   The use of the special data structure for registers is desirable
+   because it is faster.  It is possible because registers references
+   contain a fairly small number, the register number, taken from
+   a contiguously allocated series, and two register references are
+   identical if they have the same number.  General expressions
+   do not have any such thing, so the only way to retrieve the
+   information recorded on an expression other than a register
+   is to keep it in a hash table.
+
+Registers and "quantity numbers":
+
+   At the start of each basic block, all of the (hardware and pseudo)
+   registers used in the function are given distinct quantity
+   numbers to indicate their contents.  During scan, when the code
+   copies one register into another, we copy the quantity number.
+   When a register is loaded in any other way, we allocate a new
+   quantity number to describe the value generated by this operation.
+   `REG_QTY (N)' records what quantity register N is currently thought
+   of as containing.
+
+   All real quantity numbers are greater than or equal to zero.
+   If register N has not been assigned a quantity, `REG_QTY (N)' will
+   equal -N - 1, which is always negative.
+
+   Quantity numbers below zero do not exist and none of the `qty_table'
+   entries should be referenced with a negative index.
+
+   We also maintain a bidirectional chain of registers for each
+   quantity number.  The `qty_table` members `first_reg' and `last_reg',
+   and `reg_eqv_table' members `next' and `prev' hold these chains.
+
+   The first register in a chain is the one whose lifespan is least local.
+   Among equals, it is the one that was seen first.
+   We replace any equivalent register with that one.
+
+   If two registers have the same quantity number, it must be true that
+   REG expressions with qty_table `mode' must be in the hash table for both
+   registers and must be in the same class.
+
+   The converse is not true.  Since hard registers may be referenced in
+   any mode, two REG expressions might be equivalent in the hash table
+   but not have the same quantity number if the quantity number of one
+   of the registers is not the same mode as those expressions.
+
+Constants and quantity numbers
+
+   When a quantity has a known constant value, that value is stored
+   in the appropriate qty_table `const_rtx'.  This is in addition to
+   putting the constant in the hash table as is usual for non-regs.
+
+   Whether a reg or a constant is preferred is determined by the configuration
+   macro CONST_COSTS and will often depend on the constant value.  In any
+   event, expressions containing constants can be simplified, by fold_rtx.
+
+   When a quantity has a known nearly constant value (such as an address
+   of a stack slot), that value is stored in the appropriate qty_table
+   `const_rtx'.
+
+   Integer constants don't have a machine mode.  However, cse
+   determines the intended machine mode from the destination
+   of the instruction that moves the constant.  The machine mode
+   is recorded in the hash table along with the actual RTL
+   constant expression so that different modes are kept separate.
+
+Other expressions:
+
+   To record known equivalences among expressions in general
+   we use a hash table called `table'.  It has a fixed number of buckets
+   that contain chains of `struct table_elt' elements for expressions.
+   These chains connect the elements whose expressions have the same
+   hash codes.
+
+   Other chains through the same elements connect the elements which
+   currently have equivalent values.
+
+   Register references in an expression are canonicalized before hashing
+   the expression.  This is done using `reg_qty' and qty_table `first_reg'.
+   The hash code of a register reference is computed using the quantity
+   number, not the register number.
+
+   When the value of an expression changes, it is necessary to remove from the
+   hash table not just that expression but all expressions whose values
+   could be different as a result.
+
+     1. If the value changing is in memory, except in special cases
+     ANYTHING referring to memory could be changed.  That is because
+     nobody knows where a pointer does not point.
+     The function `invalidate_memory' removes what is necessary.
+
+     The special cases are when the address is constant or is
+     a constant plus a fixed register such as the frame pointer
+     or a static chain pointer.  When such addresses are stored in,
+     we can tell exactly which other such addresses must be invalidated
+     due to overlap.  `invalidate' does this.
+     All expressions that refer to non-constant
+     memory addresses are also invalidated.  `invalidate_memory' does this.
+
+     2. If the value changing is a register, all expressions
+     containing references to that register, and only those,
+     must be removed.
+
+   Because searching the entire hash table for expressions that contain
+   a register is very slow, we try to figure out when it isn't necessary.
+   Precisely, this is necessary only when expressions have been
+   entered in the hash table using this register, and then the value has
+   changed, and then another expression wants to be added to refer to
+   the register's new value.  This sequence of circumstances is rare
+   within any one basic block.
+
+   `REG_TICK' and `REG_IN_TABLE', accessors for members of
+   cse_reg_info, are used to detect this case.  REG_TICK (i) is
+   incremented whenever a value is stored in register i.
+   REG_IN_TABLE (i) holds -1 if no references to register i have been
+   entered in the table; otherwise, it contains the value REG_TICK (i)
+   had when the references were entered.  If we want to enter a
+   reference and REG_IN_TABLE (i) != REG_TICK (i), we must scan and
+   remove old references.  Until we want to enter a new entry, the
+   mere fact that the two vectors don't match makes the entries be
+   ignored if anyone tries to match them.
+
+   Registers themselves are entered in the hash table as well as in
+   the equivalent-register chains.  However, `REG_TICK' and
+   `REG_IN_TABLE' do not apply to expressions which are simple
+   register references.  These expressions are removed from the table
+   immediately when they become invalid, and this can be done even if
+   we do not immediately search for all the expressions that refer to
+   the register.
+
+   A CLOBBER rtx in an instruction invalidates its operand for further
+   reuse.  A CLOBBER or SET rtx whose operand is a MEM:BLK
+   invalidates everything that resides in memory.
+
+Related expressions:
+
+   Constant expressions that differ only by an additive integer
+   are called related.  When a constant expression is put in
+   the table, the related expression with no constant term
+   is also entered.  These are made to point at each other
+   so that it is possible to find out if there exists any
+   register equivalent to an expression related to a given expression.  */
+
+/* Length of qty_table vector.  We know in advance we will not need
+   a quantity number this big.  */
+
+static int max_qty;
+
+/* Next quantity number to be allocated.
+   This is 1 + the largest number needed so far.  */
+
+static int next_qty;
+
+/* Per-qty information tracking.
+
+   `first_reg' and `last_reg' track the head and tail of the
+   chain of registers which currently contain this quantity.
+
+   `mode' contains the machine mode of this quantity.
+
+   `const_rtx' holds the rtx of the constant value of this
+   quantity, if known.  A summations of the frame/arg pointer
+   and a constant can also be entered here.  When this holds
+   a known value, `const_insn' is the insn which stored the
+   constant value.
+
+   `comparison_{code,const,qty}' are used to track when a
+   comparison between a quantity and some constant or register has
+   been passed.  In such a case, we know the results of the comparison
+   in case we see it again.  These members record a comparison that
+   is known to be true.  `comparison_code' holds the rtx code of such
+   a comparison, else it is set to UNKNOWN and the other two
+   comparison members are undefined.  `comparison_const' holds
+   the constant being compared against, or zero if the comparison
+   is not against a constant.  `comparison_qty' holds the quantity
+   being compared against when the result is known.  If the comparison
+   is not with a register, `comparison_qty' is -1.  */
+
+struct qty_table_elem
+{
+  rtx const_rtx;
+  rtx const_insn;
+  rtx comparison_const;
+  int comparison_qty;
+  unsigned int first_reg, last_reg;
+  /* The sizes of these fields should match the sizes of the
+     code and mode fields of struct rtx_def (see rtl.h).  */
+  ENUM_BITFIELD(rtx_code) comparison_code : 16;
+  ENUM_BITFIELD(machine_mode) mode : 8;
+};
+
+/* The table of all qtys, indexed by qty number.  */
+static struct qty_table_elem *qty_table;
+
+/* Structure used to pass arguments via for_each_rtx to function
+   cse_change_cc_mode.  */
+struct change_cc_mode_args
+{
+  rtx insn;
+  rtx newreg;
+};
+
+#ifdef HAVE_cc0
+/* For machines that have a CC0, we do not record its value in the hash
+   table since its use is guaranteed to be the insn immediately following
+   its definition and any other insn is presumed to invalidate it.
+
+   Instead, we store below the current and last value assigned to CC0.
+   If it should happen to be a constant, it is stored in preference
+   to the actual assigned value.  In case it is a constant, we store
+   the mode in which the constant should be interpreted.  */
+
+static rtx this_insn_cc0, prev_insn_cc0;
+static enum machine_mode this_insn_cc0_mode, prev_insn_cc0_mode;
+#endif
+
+/* Insn being scanned.  */
+
+static rtx this_insn;
+static bool optimize_this_for_speed_p;
+
+/* Index by register number, gives the number of the next (or
+   previous) register in the chain of registers sharing the same
+   value.
+
+   Or -1 if this register is at the end of the chain.
+
+   If REG_QTY (N) == -N - 1, reg_eqv_table[N].next is undefined.  */
+
+/* Per-register equivalence chain.  */
+struct reg_eqv_elem
+{
+  int next, prev;
+};
+
+/* The table of all register equivalence chains.  */
+static struct reg_eqv_elem *reg_eqv_table;
+
+struct cse_reg_info
+{
+  /* The timestamp at which this register is initialized.  */
+  unsigned int timestamp;
+
+  /* The quantity number of the register's current contents.  */
+  int reg_qty;
+
+  /* The number of times the register has been altered in the current
+     basic block.  */
+  int reg_tick;
+
+  /* The REG_TICK value at which rtx's containing this register are
+     valid in the hash table.  If this does not equal the current
+     reg_tick value, such expressions existing in the hash table are
+     invalid.  */
+  int reg_in_table;
+
+  /* The SUBREG that was set when REG_TICK was last incremented.  Set
+     to -1 if the last store was to the whole register, not a subreg.  */
+  unsigned int subreg_ticked;
+};
+
+/* A table of cse_reg_info indexed by register numbers.  */
+static struct cse_reg_info *cse_reg_info_table;
+
+/* The size of the above table.  */
+static unsigned int cse_reg_info_table_size;
+
+/* The index of the first entry that has not been initialized.  */
+static unsigned int cse_reg_info_table_first_uninitialized;
+
+/* The timestamp at the beginning of the current run of
+   cse_extended_basic_block.  We increment this variable at the beginning of
+   the current run of cse_extended_basic_block.  The timestamp field of a
+   cse_reg_info entry matches the value of this variable if and only
+   if the entry has been initialized during the current run of
+   cse_extended_basic_block.  */
+static unsigned int cse_reg_info_timestamp;
+
+/* A HARD_REG_SET containing all the hard registers for which there is
+   currently a REG expression in the hash table.  Note the difference
+   from the above variables, which indicate if the REG is mentioned in some
+   expression in the table.  */
+
+static HARD_REG_SET hard_regs_in_table;
+
+/* True if CSE has altered the CFG.  */
+static bool cse_cfg_altered;
+
+/* True if CSE has altered conditional jump insns in such a way
+   that jump optimization should be redone.  */
+static bool cse_jumps_altered;
+
+/* True if we put a LABEL_REF into the hash table for an INSN
+   without a REG_LABEL_OPERAND, we have to rerun jump after CSE
+   to put in the note.  */
+static bool recorded_label_ref;
+
+/* canon_hash stores 1 in do_not_record
+   if it notices a reference to CC0, PC, or some other volatile
+   subexpression.  */
+
+static int do_not_record;
+
+/* canon_hash stores 1 in hash_arg_in_memory
+   if it notices a reference to memory within the expression being hashed.  */
+
+static int hash_arg_in_memory;
+
+/* The hash table contains buckets which are chains of `struct table_elt's,
+   each recording one expression's information.
+   That expression is in the `exp' field.
+
+   The canon_exp field contains a canonical (from the point of view of
+   alias analysis) version of the `exp' field.
+
+   Those elements with the same hash code are chained in both directions
+   through the `next_same_hash' and `prev_same_hash' fields.
+
+   Each set of expressions with equivalent values
+   are on a two-way chain through the `next_same_value'
+   and `prev_same_value' fields, and all point with
+   the `first_same_value' field at the first element in
+   that chain.  The chain is in order of increasing cost.
+   Each element's cost value is in its `cost' field.
+
+   The `in_memory' field is nonzero for elements that
+   involve any reference to memory.  These elements are removed
+   whenever a write is done to an unidentified location in memory.
+   To be safe, we assume that a memory address is unidentified unless
+   the address is either a symbol constant or a constant plus
+   the frame pointer or argument pointer.
+
+   The `related_value' field is used to connect related expressions
+   (that differ by adding an integer).
+   The related expressions are chained in a circular fashion.
+   `related_value' is zero for expressions for which this
+   chain is not useful.
+
+   The `cost' field stores the cost of this element's expression.
+   The `regcost' field stores the value returned by approx_reg_cost for
+   this element's expression.
+
+   The `is_const' flag is set if the element is a constant (including
+   a fixed address).
+
+   The `flag' field is used as a temporary during some search routines.
+
+   The `mode' field is usually the same as GET_MODE (`exp'), but
+   if `exp' is a CONST_INT and has no machine mode then the `mode'
+   field is the mode it was being used as.  Each constant is
+   recorded separately for each mode it is used with.  */
+
+struct table_elt
+{
+  rtx exp;
+  rtx canon_exp;
+  struct table_elt *next_same_hash;
+  struct table_elt *prev_same_hash;
+  struct table_elt *next_same_value;
+  struct table_elt *prev_same_value;
+  struct table_elt *first_same_value;
+  struct table_elt *related_value;
+  int cost;
+  int regcost;
+  /* The size of this field should match the size
+     of the mode field of struct rtx_def (see rtl.h).  */
+  ENUM_BITFIELD(machine_mode) mode : 8;
+  char in_memory;
+  char is_const;
+  char flag;
+};
+
+/* We don't want a lot of buckets, because we rarely have very many
+   things stored in the hash table, and a lot of buckets slows
+   down a lot of loops that happen frequently.  */
+#define HASH_SHIFT	5
+#define HASH_SIZE	(1 << HASH_SHIFT)
+#define HASH_MASK	(HASH_SIZE - 1)
+
+/* Compute hash code of X in mode M.  Special-case case where X is a pseudo
+   register (hard registers may require `do_not_record' to be set).  */
+
+#define HASH(X, M)	\
+ ((REG_P (X) && REGNO (X) >= FIRST_PSEUDO_REGISTER	\
+  ? (((unsigned) REG << 7) + (unsigned) REG_QTY (REGNO (X)))	\
+  : canon_hash (X, M)) & HASH_MASK)
+
+/* Like HASH, but without side-effects.  */
+#define SAFE_HASH(X, M)	\
+ ((REG_P (X) && REGNO (X) >= FIRST_PSEUDO_REGISTER	\
+  ? (((unsigned) REG << 7) + (unsigned) REG_QTY (REGNO (X)))	\
+  : safe_hash (X, M)) & HASH_MASK)
+
+/* Determine whether register number N is considered a fixed register for the
+   purpose of approximating register costs.
+   It is desirable to replace other regs with fixed regs, to reduce need for
+   non-fixed hard regs.
+   A reg wins if it is either the frame pointer or designated as fixed.  */
+#define FIXED_REGNO_P(N)  \
+  ((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM \
+   || fixed_regs[N] || global_regs[N])
+
+/* Compute cost of X, as stored in the `cost' field of a table_elt.  Fixed
+   hard registers and pointers into the frame are the cheapest with a cost
+   of 0.  Next come pseudos with a cost of one and other hard registers with
+   a cost of 2.  Aside from these special cases, call `rtx_cost'.  */
+
+#define CHEAP_REGNO(N)							\
+  (REGNO_PTR_FRAME_P(N)							\
+   || (HARD_REGISTER_NUM_P (N)						\
+       && FIXED_REGNO_P (N) && REGNO_REG_CLASS (N) != NO_REGS))
+
+#define COST(X) (REG_P (X) ? 0 : notreg_cost (X, SET))
+#define COST_IN(X,OUTER) (REG_P (X) ? 0 : notreg_cost (X, OUTER))
+
+/* Get the number of times this register has been updated in this
+   basic block.  */
+
+#define REG_TICK(N) (get_cse_reg_info (N)->reg_tick)
+
+/* Get the point at which REG was recorded in the table.  */
+
+#define REG_IN_TABLE(N) (get_cse_reg_info (N)->reg_in_table)
+
+/* Get the SUBREG set at the last increment to REG_TICK (-1 if not a
+   SUBREG).  */
+
+#define SUBREG_TICKED(N) (get_cse_reg_info (N)->subreg_ticked)
+
+/* Get the quantity number for REG.  */
+
+#define REG_QTY(N) (get_cse_reg_info (N)->reg_qty)
+
+/* Determine if the quantity number for register X represents a valid index
+   into the qty_table.  */
+
+#define REGNO_QTY_VALID_P(N) (REG_QTY (N) >= 0)
+
+static struct table_elt *table[HASH_SIZE];
+
+/* Chain of `struct table_elt's made so far for this function
+   but currently removed from the table.  */
+
+static struct table_elt *free_element_chain;
+
+/* Set to the cost of a constant pool reference if one was found for a
+   symbolic constant.  If this was found, it means we should try to
+   convert constants into constant pool entries if they don't fit in
+   the insn.  */
+
+static int constant_pool_entries_cost;
+static int constant_pool_entries_regcost;
+
+/* This data describes a block that will be processed by
+   cse_extended_basic_block.  */
+
+struct cse_basic_block_data
+{
+  /* Total number of SETs in block.  */
+  int nsets;
+  /* Size of current branch path, if any.  */
+  int path_size;
+  /* Current path, indicating which basic_blocks will be processed.  */
+  struct branch_path
+    {
+      /* The basic block for this path entry.  */
+      basic_block bb;
+    } *path;
+};
+
+
+/* Pointers to the live in/live out bitmaps for the boundaries of the
+   current EBB.  */
+static bitmap cse_ebb_live_in, cse_ebb_live_out;
+
+/* A simple bitmap to track which basic blocks have been visited
+   already as part of an already processed extended basic block.  */
+static sbitmap cse_visited_basic_blocks;
+
+static bool fixed_base_plus_p (rtx x);
+static int notreg_cost (rtx, enum rtx_code);
+static int approx_reg_cost_1 (rtx *, void *);
+static int approx_reg_cost (rtx);
+static int preferable (int, int, int, int);
+static void new_basic_block (void);
+static void make_new_qty (unsigned int, enum machine_mode);
+static void make_regs_eqv (unsigned int, unsigned int);
+static void delete_reg_equiv (unsigned int);
+static int mention_regs (rtx);
+static int insert_regs (rtx, struct table_elt *, int);
+static void remove_from_table (struct table_elt *, unsigned);
+static void remove_pseudo_from_table (rtx, unsigned);
+static struct table_elt *lookup (rtx, unsigned, enum machine_mode);
+static struct table_elt *lookup_for_remove (rtx, unsigned, enum machine_mode);
+static rtx lookup_as_function (rtx, enum rtx_code);
+static struct table_elt *insert (rtx, struct table_elt *, unsigned,
+				 enum machine_mode);
+static void merge_equiv_classes (struct table_elt *, struct table_elt *);
+static void invalidate (rtx, enum machine_mode);
+static bool cse_rtx_varies_p (const_rtx, bool);
+static void remove_invalid_refs (unsigned int);
+static void remove_invalid_subreg_refs (unsigned int, unsigned int,
+					enum machine_mode);
+static void rehash_using_reg (rtx);
+static void invalidate_memory (void);
+static void invalidate_for_call (void);
+static rtx use_related_value (rtx, struct table_elt *);
+
+static inline unsigned canon_hash (rtx, enum machine_mode);
+static inline unsigned safe_hash (rtx, enum machine_mode);
+static inline unsigned hash_rtx_string (const char *);
+
+static rtx canon_reg (rtx, rtx);
+static enum rtx_code find_comparison_args (enum rtx_code, rtx *, rtx *,
+					   enum machine_mode *,
+					   enum machine_mode *);
+static rtx fold_rtx (rtx, rtx);
+static rtx equiv_constant (rtx);
+static void record_jump_equiv (rtx, bool);
+static void record_jump_cond (enum rtx_code, enum machine_mode, rtx, rtx,
+			      int);
+static void cse_insn (rtx);
+static void cse_prescan_path (struct cse_basic_block_data *);
+static void invalidate_from_clobbers (rtx);
+static rtx cse_process_notes (rtx, rtx, bool *);
+static void cse_extended_basic_block (struct cse_basic_block_data *);
+static void count_reg_usage (rtx, int *, rtx, int);
+static int check_for_label_ref (rtx *, void *);
+extern void dump_class (struct table_elt*);
+static void get_cse_reg_info_1 (unsigned int regno);
+static struct cse_reg_info * get_cse_reg_info (unsigned int regno);
+static int check_dependence (rtx *, void *);
+
+static void flush_hash_table (void);
+static bool insn_live_p (rtx, int *);
+static bool set_live_p (rtx, rtx, int *);
+static int cse_change_cc_mode (rtx *, void *);
+static void cse_change_cc_mode_insn (rtx, rtx);
+static void cse_change_cc_mode_insns (rtx, rtx, rtx);
+static enum machine_mode cse_cc_succs (basic_block, basic_block, rtx, rtx,
+				       bool);
+
+
+#undef RTL_HOOKS_GEN_LOWPART
+#define RTL_HOOKS_GEN_LOWPART		gen_lowpart_if_possible
+
+static const struct rtl_hooks cse_rtl_hooks = RTL_HOOKS_INITIALIZER;
+
+/* Nonzero if X has the form (PLUS frame-pointer integer).  We check for
+   virtual regs here because the simplify_*_operation routines are called
+   by integrate.c, which is called before virtual register instantiation.  */
+
+static bool
+fixed_base_plus_p (rtx x)
+{
+  switch (GET_CODE (x))
+    {
+    case REG:
+      if (x == frame_pointer_rtx || x == hard_frame_pointer_rtx)
+	return true;
+      if (x == arg_pointer_rtx && fixed_regs[ARG_POINTER_REGNUM])
+	return true;
+      if (REGNO (x) >= FIRST_VIRTUAL_REGISTER
+	  && REGNO (x) <= LAST_VIRTUAL_REGISTER)
+	return true;
+      return false;
+
+    case PLUS:
+      if (GET_CODE (XEXP (x, 1)) != CONST_INT)
+	return false;
+      return fixed_base_plus_p (XEXP (x, 0));
+
+    default:
+      return false;
+    }
+}
+
+/* Dump the expressions in the equivalence class indicated by CLASSP.
+   This function is used only for debugging.  */
+void
+dump_class (struct table_elt *classp)
+{
+  struct table_elt *elt;
+
+  fprintf (stderr, "Equivalence chain for ");
+  print_rtl (stderr, classp->exp);
+  fprintf (stderr, ": \n");
+
+  for (elt = classp->first_same_value; elt; elt = elt->next_same_value)
+    {
+      print_rtl (stderr, elt->exp);
+      fprintf (stderr, "\n");
+    }
+}
+
+/* Subroutine of approx_reg_cost; called through for_each_rtx.  */
+
+static int
+approx_reg_cost_1 (rtx *xp, void *data)
+{
+  rtx x = *xp;
+  int *cost_p = (int *) data;
+
+  if (x && REG_P (x))
+    {
+      unsigned int regno = REGNO (x);
+
+      if (! CHEAP_REGNO (regno))
+	{
+	  if (regno < FIRST_PSEUDO_REGISTER)
+	    {
+	      if (SMALL_REGISTER_CLASSES)
+		return 1;
+	      *cost_p += 2;
+	    }
+	  else
+	    *cost_p += 1;
+	}
+    }
+
+  return 0;
+}
+
+/* Return an estimate of the cost of the registers used in an rtx.
+   This is mostly the number of different REG expressions in the rtx;
+   however for some exceptions like fixed registers we use a cost of
+   0.  If any other hard register reference occurs, return MAX_COST.  */
+
+static int
+approx_reg_cost (rtx x)
+{
+  int cost = 0;
+
+  if (for_each_rtx (&x, approx_reg_cost_1, (void *) &cost))
+    return MAX_COST;
+
+  return cost;
+}
+
+/* Return a negative value if an rtx A, whose costs are given by COST_A
+   and REGCOST_A, is more desirable than an rtx B.
+   Return a positive value if A is less desirable, or 0 if the two are
+   equally good.  */
+static int
+preferable (int cost_a, int regcost_a, int cost_b, int regcost_b)
+{
+  /* First, get rid of cases involving expressions that are entirely
+     unwanted.  */
+  if (cost_a != cost_b)
+    {
+      if (cost_a == MAX_COST)
+	return 1;
+      if (cost_b == MAX_COST)
+	return -1;
+    }
+
+  /* Avoid extending lifetimes of hardregs.  */
+  if (regcost_a != regcost_b)
+    {
+      if (regcost_a == MAX_COST)
+	return 1;
+      if (regcost_b == MAX_COST)
+	return -1;
+    }
+
+  /* Normal operation costs take precedence.  */
+  if (cost_a != cost_b)
+    return cost_a - cost_b;
+  /* Only if these are identical consider effects on register pressure.  */
+  if (regcost_a != regcost_b)
+    return regcost_a - regcost_b;
+  return 0;
+}
+
+/* Internal function, to compute cost when X is not a register; called
+   from COST macro to keep it simple.  */
+
+static int
+notreg_cost (rtx x, enum rtx_code outer)
+{
+  return ((GET_CODE (x) == SUBREG
+	   && REG_P (SUBREG_REG (x))
+	   && GET_MODE_CLASS (GET_MODE (x)) == MODE_INT
+	   && GET_MODE_CLASS (GET_MODE (SUBREG_REG (x))) == MODE_INT
+	   && (GET_MODE_SIZE (GET_MODE (x))
+	       < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+	   && subreg_lowpart_p (x)
+	   && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (GET_MODE (x)),
+				     GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x)))))
+	  ? 0
+	  : rtx_cost (x, outer, optimize_this_for_speed_p) * 2);
+}
+
+
+/* Initialize CSE_REG_INFO_TABLE.  */
+
+static void
+init_cse_reg_info (unsigned int nregs)
+{
+  /* Do we need to grow the table?  */
+  if (nregs > cse_reg_info_table_size)
+    {
+      unsigned int new_size;
+
+      if (cse_reg_info_table_size < 2048)
+	{
+	  /* Compute a new size that is a power of 2 and no smaller
+	     than the large of NREGS and 64.  */
+	  new_size = (cse_reg_info_table_size
+		      ? cse_reg_info_table_size : 64);
+
+	  while (new_size < nregs)
+	    new_size *= 2;
+	}
+      else
+	{
+	  /* If we need a big table, allocate just enough to hold
+	     NREGS registers.  */
+	  new_size = nregs;
+	}
+
+      /* Reallocate the table with NEW_SIZE entries.  */
+      if (cse_reg_info_table)
+	free (cse_reg_info_table);
+      cse_reg_info_table = XNEWVEC (struct cse_reg_info, new_size);
+      cse_reg_info_table_size = new_size;
+      cse_reg_info_table_first_uninitialized = 0;
+    }
+
+  /* Do we have all of the first NREGS entries initialized?  */
+  if (cse_reg_info_table_first_uninitialized < nregs)
+    {
+      unsigned int old_timestamp = cse_reg_info_timestamp - 1;
+      unsigned int i;
+
+      /* Put the old timestamp on newly allocated entries so that they
+	 will all be considered out of date.  We do not touch those
+	 entries beyond the first NREGS entries to be nice to the
+	 virtual memory.  */
+      for (i = cse_reg_info_table_first_uninitialized; i < nregs; i++)
+	cse_reg_info_table[i].timestamp = old_timestamp;
+
+      cse_reg_info_table_first_uninitialized = nregs;
+    }
+}
+
+/* Given REGNO, initialize the cse_reg_info entry for REGNO.  */
+
+static void
+get_cse_reg_info_1 (unsigned int regno)
+{
+  /* Set TIMESTAMP field to CSE_REG_INFO_TIMESTAMP so that this
+     entry will be considered to have been initialized.  */
+  cse_reg_info_table[regno].timestamp = cse_reg_info_timestamp;
+
+  /* Initialize the rest of the entry.  */
+  cse_reg_info_table[regno].reg_tick = 1;
+  cse_reg_info_table[regno].reg_in_table = -1;
+  cse_reg_info_table[regno].subreg_ticked = -1;
+  cse_reg_info_table[regno].reg_qty = -regno - 1;
+}
+
+/* Find a cse_reg_info entry for REGNO.  */
+
+static inline struct cse_reg_info *
+get_cse_reg_info (unsigned int regno)
+{
+  struct cse_reg_info *p = &cse_reg_info_table[regno];
+
+  /* If this entry has not been initialized, go ahead and initialize
+     it.  */
+  if (p->timestamp != cse_reg_info_timestamp)
+    get_cse_reg_info_1 (regno);
+
+  return p;
+}
+
+/* Clear the hash table and initialize each register with its own quantity,
+   for a new basic block.  */
+
+static void
+new_basic_block (void)
+{
+  int i;
+
+  next_qty = 0;
+
+  /* Invalidate cse_reg_info_table.  */
+  cse_reg_info_timestamp++;
+
+  /* Clear out hash table state for this pass.  */
+  CLEAR_HARD_REG_SET (hard_regs_in_table);
+
+  /* The per-quantity values used to be initialized here, but it is
+     much faster to initialize each as it is made in `make_new_qty'.  */
+
+  for (i = 0; i < HASH_SIZE; i++)
+    {
+      struct table_elt *first;
+
+      first = table[i];
+      if (first != NULL)
+	{
+	  struct table_elt *last = first;
+
+	  table[i] = NULL;
+
+	  while (last->next_same_hash != NULL)
+	    last = last->next_same_hash;
+
+	  /* Now relink this hash entire chain into
+	     the free element list.  */
+
+	  last->next_same_hash = free_element_chain;
+	  free_element_chain = first;
+	}
+    }
+
+#ifdef HAVE_cc0
+  prev_insn_cc0 = 0;
+#endif
+}
+
+/* Say that register REG contains a quantity in mode MODE not in any
+   register before and initialize that quantity.  */
+
+static void
+make_new_qty (unsigned int reg, enum machine_mode mode)
+{
+  int q;
+  struct qty_table_elem *ent;
+  struct reg_eqv_elem *eqv;
+
+  gcc_assert (next_qty < max_qty);
+
+  q = REG_QTY (reg) = next_qty++;
+  ent = &qty_table[q];
+  ent->first_reg = reg;
+  ent->last_reg = reg;
+  ent->mode = mode;
+  ent->const_rtx = ent->const_insn = NULL_RTX;
+  ent->comparison_code = UNKNOWN;
+
+  eqv = &reg_eqv_table[reg];
+  eqv->next = eqv->prev = -1;
+}
+
+/* Make reg NEW equivalent to reg OLD.
+   OLD is not changing; NEW is.  */
+
+static void
+make_regs_eqv (unsigned int new_reg, unsigned int old_reg)
+{
+  unsigned int lastr, firstr;
+  int q = REG_QTY (old_reg);
+  struct qty_table_elem *ent;
+
+  ent = &qty_table[q];
+
+  /* Nothing should become eqv until it has a "non-invalid" qty number.  */
+  gcc_assert (REGNO_QTY_VALID_P (old_reg));
+
+  REG_QTY (new_reg) = q;
+  firstr = ent->first_reg;
+  lastr = ent->last_reg;
+
+  /* Prefer fixed hard registers to anything.  Prefer pseudo regs to other
+     hard regs.  Among pseudos, if NEW will live longer than any other reg
+     of the same qty, and that is beyond the current basic block,
+     make it the new canonical replacement for this qty.  */
+  if (! (firstr < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (firstr))
+      /* Certain fixed registers might be of the class NO_REGS.  This means
+	 that not only can they not be allocated by the compiler, but
+	 they cannot be used in substitutions or canonicalizations
+	 either.  */
+      && (new_reg >= FIRST_PSEUDO_REGISTER || REGNO_REG_CLASS (new_reg) != NO_REGS)
+      && ((new_reg < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (new_reg))
+	  || (new_reg >= FIRST_PSEUDO_REGISTER
+	      && (firstr < FIRST_PSEUDO_REGISTER
+		  || (bitmap_bit_p (cse_ebb_live_out, new_reg)
+		      && !bitmap_bit_p (cse_ebb_live_out, firstr))
+		  || (bitmap_bit_p (cse_ebb_live_in, new_reg)
+		      && !bitmap_bit_p (cse_ebb_live_in, firstr))))))
+    {
+      reg_eqv_table[firstr].prev = new_reg;
+      reg_eqv_table[new_reg].next = firstr;
+      reg_eqv_table[new_reg].prev = -1;
+      ent->first_reg = new_reg;
+    }
+  else
+    {
+      /* If NEW is a hard reg (known to be non-fixed), insert at end.
+	 Otherwise, insert before any non-fixed hard regs that are at the
+	 end.  Registers of class NO_REGS cannot be used as an
+	 equivalent for anything.  */
+      while (lastr < FIRST_PSEUDO_REGISTER && reg_eqv_table[lastr].prev >= 0
+	     && (REGNO_REG_CLASS (lastr) == NO_REGS || ! FIXED_REGNO_P (lastr))
+	     && new_reg >= FIRST_PSEUDO_REGISTER)
+	lastr = reg_eqv_table[lastr].prev;
+      reg_eqv_table[new_reg].next = reg_eqv_table[lastr].next;
+      if (reg_eqv_table[lastr].next >= 0)
+	reg_eqv_table[reg_eqv_table[lastr].next].prev = new_reg;
+      else
+	qty_table[q].last_reg = new_reg;
+      reg_eqv_table[lastr].next = new_reg;
+      reg_eqv_table[new_reg].prev = lastr;
+    }
+}
+
+/* Remove REG from its equivalence class.  */
+
+static void
+delete_reg_equiv (unsigned int reg)
+{
+  struct qty_table_elem *ent;
+  int q = REG_QTY (reg);
+  int p, n;
+
+  /* If invalid, do nothing.  */
+  if (! REGNO_QTY_VALID_P (reg))
+    return;
+
+  ent = &qty_table[q];
+
+  p = reg_eqv_table[reg].prev;
+  n = reg_eqv_table[reg].next;
+
+  if (n != -1)
+    reg_eqv_table[n].prev = p;
+  else
+    ent->last_reg = p;
+  if (p != -1)
+    reg_eqv_table[p].next = n;
+  else
+    ent->first_reg = n;
+
+  REG_QTY (reg) = -reg - 1;
+}
+
+/* Remove any invalid expressions from the hash table
+   that refer to any of the registers contained in expression X.
+
+   Make sure that newly inserted references to those registers
+   as subexpressions will be considered valid.
+
+   mention_regs is not called when a register itself
+   is being stored in the table.
+
+   Return 1 if we have done something that may have changed the hash code
+   of X.  */
+
+static int
+mention_regs (rtx x)
+{
+  enum rtx_code code;
+  int i, j;
+  const char *fmt;
+  int changed = 0;
+
+  if (x == 0)
+    return 0;
+
+  code = GET_CODE (x);
+  if (code == REG)
+    {
+      unsigned int regno = REGNO (x);
+      unsigned int endregno = END_REGNO (x);
+      unsigned int i;
+
+      for (i = regno; i < endregno; i++)
+	{
+	  if (REG_IN_TABLE (i) >= 0 && REG_IN_TABLE (i) != REG_TICK (i))
+	    remove_invalid_refs (i);
+
+	  REG_IN_TABLE (i) = REG_TICK (i);
+	  SUBREG_TICKED (i) = -1;
+	}
+
+      return 0;
+    }
+
+  /* If this is a SUBREG, we don't want to discard other SUBREGs of the same
+     pseudo if they don't use overlapping words.  We handle only pseudos
+     here for simplicity.  */
+  if (code == SUBREG && REG_P (SUBREG_REG (x))
+      && REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER)
+    {
+      unsigned int i = REGNO (SUBREG_REG (x));
+
+      if (REG_IN_TABLE (i) >= 0 && REG_IN_TABLE (i) != REG_TICK (i))
+	{
+	  /* If REG_IN_TABLE (i) differs from REG_TICK (i) by one, and
+	     the last store to this register really stored into this
+	     subreg, then remove the memory of this subreg.
+	     Otherwise, remove any memory of the entire register and
+	     all its subregs from the table.  */
+	  if (REG_TICK (i) - REG_IN_TABLE (i) > 1
+	      || SUBREG_TICKED (i) != REGNO (SUBREG_REG (x)))
+	    remove_invalid_refs (i);
+	  else
+	    remove_invalid_subreg_refs (i, SUBREG_BYTE (x), GET_MODE (x));
+	}
+
+      REG_IN_TABLE (i) = REG_TICK (i);
+      SUBREG_TICKED (i) = REGNO (SUBREG_REG (x));
+      return 0;
+    }
+
+  /* If X is a comparison or a COMPARE and either operand is a register
+     that does not have a quantity, give it one.  This is so that a later
+     call to record_jump_equiv won't cause X to be assigned a different
+     hash code and not found in the table after that call.
+
+     It is not necessary to do this here, since rehash_using_reg can
+     fix up the table later, but doing this here eliminates the need to
+     call that expensive function in the most common case where the only
+     use of the register is in the comparison.  */
+
+  if (code == COMPARE || COMPARISON_P (x))
+    {
+      if (REG_P (XEXP (x, 0))
+	  && ! REGNO_QTY_VALID_P (REGNO (XEXP (x, 0))))
+	if (insert_regs (XEXP (x, 0), NULL, 0))
+	  {
+	    rehash_using_reg (XEXP (x, 0));
+	    changed = 1;
+	  }
+
+      if (REG_P (XEXP (x, 1))
+	  && ! REGNO_QTY_VALID_P (REGNO (XEXP (x, 1))))
+	if (insert_regs (XEXP (x, 1), NULL, 0))
+	  {
+	    rehash_using_reg (XEXP (x, 1));
+	    changed = 1;
+	  }
+    }
+
+  fmt = GET_RTX_FORMAT (code);
+  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+    if (fmt[i] == 'e')
+      changed |= mention_regs (XEXP (x, i));
+    else if (fmt[i] == 'E')
+      for (j = 0; j < XVECLEN (x, i); j++)
+	changed |= mention_regs (XVECEXP (x, i, j));
+
+  return changed;
+}
+
+/* Update the register quantities for inserting X into the hash table
+   with a value equivalent to CLASSP.
+   (If the class does not contain a REG, it is irrelevant.)
+   If MODIFIED is nonzero, X is a destination; it is being modified.
+   Note that delete_reg_equiv should be called on a register
+   before insert_regs is done on that register with MODIFIED != 0.
+
+   Nonzero value means that elements of reg_qty have changed
+   so X's hash code may be different.  */
+
+static int
+insert_regs (rtx x, struct table_elt *classp, int modified)
+{
+  if (REG_P (x))
+    {
+      unsigned int regno = REGNO (x);
+      int qty_valid;
+
+      /* If REGNO is in the equivalence table already but is of the
+	 wrong mode for that equivalence, don't do anything here.  */
+
+      qty_valid = REGNO_QTY_VALID_P (regno);
+      if (qty_valid)
+	{
+	  struct qty_table_elem *ent = &qty_table[REG_QTY (regno)];
+
+	  if (ent->mode != GET_MODE (x))
+	    return 0;
+	}
+
+      if (modified || ! qty_valid)
+	{
+	  if (classp)
+	    for (classp = classp->first_same_value;
+		 classp != 0;
+		 classp = classp->next_same_value)
+	      if (REG_P (classp->exp)
+		  && GET_MODE (classp->exp) == GET_MODE (x))
+		{
+		  unsigned c_regno = REGNO (classp->exp);
+
+		  gcc_assert (REGNO_QTY_VALID_P (c_regno));
+
+		  /* Suppose that 5 is hard reg and 100 and 101 are
+		     pseudos.  Consider
+
+		     (set (reg:si 100) (reg:si 5))
+		     (set (reg:si 5) (reg:si 100))
+		     (set (reg:di 101) (reg:di 5))
+
+		     We would now set REG_QTY (101) = REG_QTY (5), but the
+		     entry for 5 is in SImode.  When we use this later in
+		     copy propagation, we get the register in wrong mode.  */
+		  if (qty_table[REG_QTY (c_regno)].mode != GET_MODE (x))
+		    continue;
+
+		  make_regs_eqv (regno, c_regno);
+		  return 1;
+		}
+
+	  /* Mention_regs for a SUBREG checks if REG_TICK is exactly one larger
+	     than REG_IN_TABLE to find out if there was only a single preceding
+	     invalidation - for the SUBREG - or another one, which would be
+	     for the full register.  However, if we find here that REG_TICK
+	     indicates that the register is invalid, it means that it has
+	     been invalidated in a separate operation.  The SUBREG might be used
+	     now (then this is a recursive call), or we might use the full REG
+	     now and a SUBREG of it later.  So bump up REG_TICK so that
+	     mention_regs will do the right thing.  */
+	  if (! modified
+	      && REG_IN_TABLE (regno) >= 0
+	      && REG_TICK (regno) == REG_IN_TABLE (regno) + 1)
+	    REG_TICK (regno)++;
+	  make_new_qty (regno, GET_MODE (x));
+	  return 1;
+	}
+
+      return 0;
+    }
+
+  /* If X is a SUBREG, we will likely be inserting the inner register in the
+     table.  If that register doesn't have an assigned quantity number at
+     this point but does later, the insertion that we will be doing now will
+     not be accessible because its hash code will have changed.  So assign
+     a quantity number now.  */
+
+  else if (GET_CODE (x) == SUBREG && REG_P (SUBREG_REG (x))
+	   && ! REGNO_QTY_VALID_P (REGNO (SUBREG_REG (x))))
+    {
+      insert_regs (SUBREG_REG (x), NULL, 0);
+      mention_regs (x);
+      return 1;
+    }
+  else
+    return mention_regs (x);
+}
+
+/* Look in or update the hash table.  */
+
+/* Remove table element ELT from use in the table.
+   HASH is its hash code, made using the HASH macro.
+   It's an argument because often that is known in advance
+   and we save much time not recomputing it.  */
+
+static void
+remove_from_table (struct table_elt *elt, unsigned int hash)
+{
+  if (elt == 0)
+    return;
+
+  /* Mark this element as removed.  See cse_insn.  */
+  elt->first_same_value = 0;
+
+  /* Remove the table element from its equivalence class.  */
+
+  {
+    struct table_elt *prev = elt->prev_same_value;
+    struct table_elt *next = elt->next_same_value;
+
+    if (next)
+      next->prev_same_value = prev;
+
+    if (prev)
+      prev->next_same_value = next;
+    else
+      {
+	struct table_elt *newfirst = next;
+	while (next)
+	  {
+	    next->first_same_value = newfirst;
+	    next = next->next_same_value;
+	  }
+      }
+  }
+
+  /* Remove the table element from its hash bucket.  */
+
+  {
+    struct table_elt *prev = elt->prev_same_hash;
+    struct table_elt *next = elt->next_same_hash;
+
+    if (next)
+      next->prev_same_hash = prev;
+
+    if (prev)
+      prev->next_same_hash = next;
+    else if (table[hash] == elt)
+      table[hash] = next;
+    else
+      {
+	/* This entry is not in the proper hash bucket.  This can happen
+	   when two classes were merged by `merge_equiv_classes'.  Search
+	   for the hash bucket that it heads.  This happens only very
+	   rarely, so the cost is acceptable.  */
+	for (hash = 0; hash < HASH_SIZE; hash++)
+	  if (table[hash] == elt)
+	    table[hash] = next;
+      }
+  }
+
+  /* Remove the table element from its related-value circular chain.  */
+
+  if (elt->related_value != 0 && elt->related_value != elt)
+    {
+      struct table_elt *p = elt->related_value;
+
+      while (p->related_value != elt)
+	p = p->related_value;
+      p->related_value = elt->related_value;
+      if (p->related_value == p)
+	p->related_value = 0;
+    }
+
+  /* Now add it to the free element chain.  */
+  elt->next_same_hash = free_element_chain;
+  free_element_chain = elt;
+}
+
+/* Same as above, but X is a pseudo-register.  */
+
+static void
+remove_pseudo_from_table (rtx x, unsigned int hash)
+{
+  struct table_elt *elt;
+
+  /* Because a pseudo-register can be referenced in more than one
+     mode, we might have to remove more than one table entry.  */
+  while ((elt = lookup_for_remove (x, hash, VOIDmode)))
+    remove_from_table (elt, hash);
+}
+
+/* Look up X in the hash table and return its table element,
+   or 0 if X is not in the table.
+
+   MODE is the machine-mode of X, or if X is an integer constant
+   with VOIDmode then MODE is the mode with which X will be used.
+
+   Here we are satisfied to find an expression whose tree structure
+   looks like X.  */
+
+static struct table_elt *
+lookup (rtx x, unsigned int hash, enum machine_mode mode)
+{
+  struct table_elt *p;
+
+  for (p = table[hash]; p; p = p->next_same_hash)
+    if (mode == p->mode && ((x == p->exp && REG_P (x))
+			    || exp_equiv_p (x, p->exp, !REG_P (x), false)))
+      return p;
+
+  return 0;
+}
+
+/* Like `lookup' but don't care whether the table element uses invalid regs.
+   Also ignore discrepancies in the machine mode of a register.  */
+
+static struct table_elt *
+lookup_for_remove (rtx x, unsigned int hash, enum machine_mode mode)
+{
+  struct table_elt *p;
+
+  if (REG_P (x))
+    {
+      unsigned int regno = REGNO (x);
+
+      /* Don't check the machine mode when comparing registers;
+	 invalidating (REG:SI 0) also invalidates (REG:DF 0).  */
+      for (p = table[hash]; p; p = p->next_same_hash)
+	if (REG_P (p->exp)
+	    && REGNO (p->exp) == regno)
+	  return p;
+    }
+  else
+    {
+      for (p = table[hash]; p; p = p->next_same_hash)
+	if (mode == p->mode
+	    && (x == p->exp || exp_equiv_p (x, p->exp, 0, false)))
+	  return p;
+    }
+
+  return 0;
+}
+
+/* Look for an expression equivalent to X and with code CODE.
+   If one is found, return that expression.  */
+
+static rtx
+lookup_as_function (rtx x, enum rtx_code code)
+{
+  struct table_elt *p
+    = lookup (x, SAFE_HASH (x, VOIDmode), GET_MODE (x));
+
+  if (p == 0)
+    return 0;
+
+  for (p = p->first_same_value; p; p = p->next_same_value)
+    if (GET_CODE (p->exp) == code
+	/* Make sure this is a valid entry in the table.  */
+	&& exp_equiv_p (p->exp, p->exp, 1, false))
+      return p->exp;
+
+  return 0;
+}
+
+/* Insert X in the hash table, assuming HASH is its hash code
+   and CLASSP is an element of the class it should go in
+   (or 0 if a new class should be made).
+   It is inserted at the proper position to keep the class in
+   the order cheapest first.
+
+   MODE is the machine-mode of X, or if X is an integer constant
+   with VOIDmode then MODE is the mode with which X will be used.
+
+   For elements of equal cheapness, the most recent one
+   goes in front, except that the first element in the list
+   remains first unless a cheaper element is added.  The order of
+   pseudo-registers does not matter, as canon_reg will be called to
+   find the cheapest when a register is retrieved from the table.
+
+   The in_memory field in the hash table element is set to 0.
+   The caller must set it nonzero if appropriate.
+
+   You should call insert_regs (X, CLASSP, MODIFY) before calling here,
+   and if insert_regs returns a nonzero value
+   you must then recompute its hash code before calling here.
+
+   If necessary, update table showing constant values of quantities.  */
+
+#define CHEAPER(X, Y) \
+ (preferable ((X)->cost, (X)->regcost, (Y)->cost, (Y)->regcost) < 0)
+
+static struct table_elt *
+insert (rtx x, struct table_elt *classp, unsigned int hash, enum machine_mode mode)
+{
+  struct table_elt *elt;
+
+  /* If X is a register and we haven't made a quantity for it,
+     something is wrong.  */
+  gcc_assert (!REG_P (x) || REGNO_QTY_VALID_P (REGNO (x)));
+
+  /* If X is a hard register, show it is being put in the table.  */
+  if (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER)
+    add_to_hard_reg_set (&hard_regs_in_table, GET_MODE (x), REGNO (x));
+
+  /* Put an element for X into the right hash bucket.  */
+
+  elt = free_element_chain;
+  if (elt)
+    free_element_chain = elt->next_same_hash;
+  else
+    elt = XNEW (struct table_elt);
+
+  elt->exp = x;
+  elt->canon_exp = NULL_RTX;
+  elt->cost = COST (x);
+  elt->regcost = approx_reg_cost (x);
+  elt->next_same_value = 0;
+  elt->prev_same_value = 0;
+  elt->next_same_hash = table[hash];
+  elt->prev_same_hash = 0;
+  elt->related_value = 0;
+  elt->in_memory = 0;
+  elt->mode = mode;
+  elt->is_const = (CONSTANT_P (x) || fixed_base_plus_p (x));
+
+  if (table[hash])
+    table[hash]->prev_same_hash = elt;
+  table[hash] = elt;
+
+  /* Put it into the proper value-class.  */
+  if (classp)
+    {
+      classp = classp->first_same_value;
+      if (CHEAPER (elt, classp))
+	/* Insert at the head of the class.  */
+	{
+	  struct table_elt *p;
+	  elt->next_same_value = classp;
+	  classp->prev_same_value = elt;
+	  elt->first_same_value = elt;
+
+	  for (p = classp; p; p = p->next_same_value)
+	    p->first_same_value = elt;
+	}
+      else
+	{
+	  /* Insert not at head of the class.  */
+	  /* Put it after the last element cheaper than X.  */
+	  struct table_elt *p, *next;
+
+	  for (p = classp; (next = p->next_same_value) && CHEAPER (next, elt);
+	       p = next);
+
+	  /* Put it after P and before NEXT.  */
+	  elt->next_same_value = next;
+	  if (next)
+	    next->prev_same_value = elt;
+
+	  elt->prev_same_value = p;
+	  p->next_same_value = elt;
+	  elt->first_same_value = classp;
+	}
+    }
+  else
+    elt->first_same_value = elt;
+
+  /* If this is a constant being set equivalent to a register or a register
+     being set equivalent to a constant, note the constant equivalence.
+
+     If this is a constant, it cannot be equivalent to a different constant,
+     and a constant is the only thing that can be cheaper than a register.  So
+     we know the register is the head of the class (before the constant was
+     inserted).
+
+     If this is a register that is not already known equivalent to a
+     constant, we must check the entire class.
+
+     If this is a register that is already known equivalent to an insn,
+     update the qtys `const_insn' to show that `this_insn' is the latest
+     insn making that quantity equivalent to the constant.  */
+
+  if (elt->is_const && classp && REG_P (classp->exp)
+      && !REG_P (x))
+    {
+      int exp_q = REG_QTY (REGNO (classp->exp));
+      struct qty_table_elem *exp_ent = &qty_table[exp_q];
+
+      exp_ent->const_rtx = gen_lowpart (exp_ent->mode, x);
+      exp_ent->const_insn = this_insn;
+    }
+
+  else if (REG_P (x)
+	   && classp
+	   && ! qty_table[REG_QTY (REGNO (x))].const_rtx
+	   && ! elt->is_const)
+    {
+      struct table_elt *p;
+
+      for (p = classp; p != 0; p = p->next_same_value)
+	{
+	  if (p->is_const && !REG_P (p->exp))
+	    {
+	      int x_q = REG_QTY (REGNO (x));
+	      struct qty_table_elem *x_ent = &qty_table[x_q];
+
+	      x_ent->const_rtx
+		= gen_lowpart (GET_MODE (x), p->exp);
+	      x_ent->const_insn = this_insn;
+	      break;
+	    }
+	}
+    }
+
+  else if (REG_P (x)
+	   && qty_table[REG_QTY (REGNO (x))].const_rtx
+	   && GET_MODE (x) == qty_table[REG_QTY (REGNO (x))].mode)
+    qty_table[REG_QTY (REGNO (x))].const_insn = this_insn;
+
+  /* If this is a constant with symbolic value,
+     and it has a term with an explicit integer value,
+     link it up with related expressions.  */
+  if (GET_CODE (x) == CONST)
+    {
+      rtx subexp = get_related_value (x);
+      unsigned subhash;
+      struct table_elt *subelt, *subelt_prev;
+
+      if (subexp != 0)
+	{
+	  /* Get the integer-free subexpression in the hash table.  */
+	  subhash = SAFE_HASH (subexp, mode);
+	  subelt = lookup (subexp, subhash, mode);
+	  if (subelt == 0)
+	    subelt = insert (subexp, NULL, subhash, mode);
+	  /* Initialize SUBELT's circular chain if it has none.  */
+	  if (subelt->related_value == 0)
+	    subelt->related_value = subelt;
+	  /* Find the element in the circular chain that precedes SUBELT.  */
+	  subelt_prev = subelt;
+	  while (subelt_prev->related_value != subelt)
+	    subelt_prev = subelt_prev->related_value;
+	  /* Put new ELT into SUBELT's circular chain just before SUBELT.
+	     This way the element that follows SUBELT is the oldest one.  */
+	  elt->related_value = subelt_prev->related_value;
+	  subelt_prev->related_value = elt;
+	}
+    }
+
+  return elt;
+}
+
+/* Given two equivalence classes, CLASS1 and CLASS2, put all the entries from
+   CLASS2 into CLASS1.  This is done when we have reached an insn which makes
+   the two classes equivalent.
+
+   CLASS1 will be the surviving class; CLASS2 should not be used after this
+   call.
+
+   Any invalid entries in CLASS2 will not be copied.  */
+
+static void
+merge_equiv_classes (struct table_elt *class1, struct table_elt *class2)
+{
+  struct table_elt *elt, *next, *new_elt;
+
+  /* Ensure we start with the head of the classes.  */
+  class1 = class1->first_same_value;
+  class2 = class2->first_same_value;
+
+  /* If they were already equal, forget it.  */
+  if (class1 == class2)
+    return;
+
+  for (elt = class2; elt; elt = next)
+    {
+      unsigned int hash;
+      rtx exp = elt->exp;
+      enum machine_mode mode = elt->mode;
+
+      next = elt->next_same_value;
+
+      /* Remove old entry, make a new one in CLASS1's class.
+	 Don't do this for invalid entries as we cannot find their
+	 hash code (it also isn't necessary).  */
+      if (REG_P (exp) || exp_equiv_p (exp, exp, 1, false))
+	{
+	  bool need_rehash = false;
+
+	  hash_arg_in_memory = 0;
+	  hash = HASH (exp, mode);
+
+	  if (REG_P (exp))
+	    {
+	      need_rehash = REGNO_QTY_VALID_P (REGNO (exp));
+	      delete_reg_equiv (REGNO (exp));
+	    }
+
+	  if (REG_P (exp) && REGNO (exp) >= FIRST_PSEUDO_REGISTER)
+	    remove_pseudo_from_table (exp, hash);
+	  else
+	    remove_from_table (elt, hash);
+
+	  if (insert_regs (exp, class1, 0) || need_rehash)
+	    {
+	      rehash_using_reg (exp);
+	      hash = HASH (exp, mode);
+	    }
+	  new_elt = insert (exp, class1, hash, mode);
+	  new_elt->in_memory = hash_arg_in_memory;
+	}
+    }
+}
+
+/* Flush the entire hash table.  */
+
+static void
+flush_hash_table (void)
+{
+  int i;
+  struct table_elt *p;
+
+  for (i = 0; i < HASH_SIZE; i++)
+    for (p = table[i]; p; p = table[i])
+      {
+	/* Note that invalidate can remove elements
+	   after P in the current hash chain.  */
+	if (REG_P (p->exp))
+	  invalidate (p->exp, VOIDmode);
+	else
+	  remove_from_table (p, i);
+      }
+}
+
+/* Function called for each rtx to check whether true dependence exist.  */
+struct check_dependence_data
+{
+  enum machine_mode mode;
+  rtx exp;
+  rtx addr;
+};
+
+static int
+check_dependence (rtx *x, void *data)
+{
+  struct check_dependence_data *d = (struct check_dependence_data *) data;
+  if (*x && MEM_P (*x))
+    return canon_true_dependence (d->exp, d->mode, d->addr, *x,
+		    		  cse_rtx_varies_p);
+  else
+    return 0;
+}
+
+/* Remove from the hash table, or mark as invalid, all expressions whose
+   values could be altered by storing in X.  X is a register, a subreg, or
+   a memory reference with nonvarying address (because, when a memory
+   reference with a varying address is stored in, all memory references are
+   removed by invalidate_memory so specific invalidation is superfluous).
+   FULL_MODE, if not VOIDmode, indicates that this much should be
+   invalidated instead of just the amount indicated by the mode of X.  This
+   is only used for bitfield stores into memory.
+
+   A nonvarying address may be just a register or just a symbol reference,
+   or it may be either of those plus a numeric offset.  */
+
+static void
+invalidate (rtx x, enum machine_mode full_mode)
+{
+  int i;
+  struct table_elt *p;
+  rtx addr;
+
+  switch (GET_CODE (x))
+    {
+    case REG:
+      {
+	/* If X is a register, dependencies on its contents are recorded
+	   through the qty number mechanism.  Just change the qty number of
+	   the register, mark it as invalid for expressions that refer to it,
+	   and remove it itself.  */
+	unsigned int regno = REGNO (x);
+	unsigned int hash = HASH (x, GET_MODE (x));
+
+	/* Remove REGNO from any quantity list it might be on and indicate
+	   that its value might have changed.  If it is a pseudo, remove its
+	   entry from the hash table.
+
+	   For a hard register, we do the first two actions above for any
+	   additional hard registers corresponding to X.  Then, if any of these
+	   registers are in the table, we must remove any REG entries that
+	   overlap these registers.  */
+
+	delete_reg_equiv (regno);
+	REG_TICK (regno)++;
+	SUBREG_TICKED (regno) = -1;
+
+	if (regno >= FIRST_PSEUDO_REGISTER)
+	  remove_pseudo_from_table (x, hash);
+	else
+	  {
+	    HOST_WIDE_INT in_table
+	      = TEST_HARD_REG_BIT (hard_regs_in_table, regno);
+	    unsigned int endregno = END_HARD_REGNO (x);
+	    unsigned int tregno, tendregno, rn;
+	    struct table_elt *p, *next;
+
+	    CLEAR_HARD_REG_BIT (hard_regs_in_table, regno);
+
+	    for (rn = regno + 1; rn < endregno; rn++)
+	      {
+		in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, rn);
+		CLEAR_HARD_REG_BIT (hard_regs_in_table, rn);
+		delete_reg_equiv (rn);
+		REG_TICK (rn)++;
+		SUBREG_TICKED (rn) = -1;
+	      }
+
+	    if (in_table)
+	      for (hash = 0; hash < HASH_SIZE; hash++)
+		for (p = table[hash]; p; p = next)
+		  {
+		    next = p->next_same_hash;
+
+		    if (!REG_P (p->exp)
+			|| REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
+		      continue;
+
+		    tregno = REGNO (p->exp);
+		    tendregno = END_HARD_REGNO (p->exp);
+		    if (tendregno > regno && tregno < endregno)
+		      remove_from_table (p, hash);
+		  }
+	  }
+      }
+      return;
+
+    case SUBREG:
+      invalidate (SUBREG_REG (x), VOIDmode);
+      return;
+
+    case PARALLEL:
+      for (i = XVECLEN (x, 0) - 1; i >= 0; --i)
+	invalidate (XVECEXP (x, 0, i), VOIDmode);
+      return;
+
+    case EXPR_LIST:
+      /* This is part of a disjoint return value; extract the location in
+	 question ignoring the offset.  */
+      invalidate (XEXP (x, 0), VOIDmode);
+      return;
+
+    case MEM:
+      addr = canon_rtx (get_addr (XEXP (x, 0)));
+      /* Calculate the canonical version of X here so that
+	 true_dependence doesn't generate new RTL for X on each call.  */
+      x = canon_rtx (x);
+
+      /* Remove all hash table elements that refer to overlapping pieces of
+	 memory.  */
+      if (full_mode == VOIDmode)
+	full_mode = GET_MODE (x);
+
+      for (i = 0; i < HASH_SIZE; i++)
+	{
+	  struct table_elt *next;
+
+	  for (p = table[i]; p; p = next)
+	    {
+	      next = p->next_same_hash;
+	      if (p->in_memory)
+		{
+		  struct check_dependence_data d;
+
+		  /* Just canonicalize the expression once;
+		     otherwise each time we call invalidate
+		     true_dependence will canonicalize the
+		     expression again.  */
+		  if (!p->canon_exp)
+		    p->canon_exp = canon_rtx (p->exp);
+		  d.exp = x;
+		  d.addr = addr;
+		  d.mode = full_mode;
+		  if (for_each_rtx (&p->canon_exp, check_dependence, &d))
+		    remove_from_table (p, i);
+		}
+	    }
+	}
+      return;
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Remove all expressions that refer to register REGNO,
+   since they are already invalid, and we are about to
+   mark that register valid again and don't want the old
+   expressions to reappear as valid.  */
+
+static void
+remove_invalid_refs (unsigned int regno)
+{
+  unsigned int i;
+  struct table_elt *p, *next;
+
+  for (i = 0; i < HASH_SIZE; i++)
+    for (p = table[i]; p; p = next)
+      {
+	next = p->next_same_hash;
+	if (!REG_P (p->exp)
+	    && refers_to_regno_p (regno, regno + 1, p->exp, (rtx *) 0))
+	  remove_from_table (p, i);
+      }
+}
+
+/* Likewise for a subreg with subreg_reg REGNO, subreg_byte OFFSET,
+   and mode MODE.  */
+static void
+remove_invalid_subreg_refs (unsigned int regno, unsigned int offset,
+			    enum machine_mode mode)
+{
+  unsigned int i;
+  struct table_elt *p, *next;
+  unsigned int end = offset + (GET_MODE_SIZE (mode) - 1);
+
+  for (i = 0; i < HASH_SIZE; i++)
+    for (p = table[i]; p; p = next)
+      {
+	rtx exp = p->exp;
+	next = p->next_same_hash;
+
+	if (!REG_P (exp)
+	    && (GET_CODE (exp) != SUBREG
+		|| !REG_P (SUBREG_REG (exp))
+		|| REGNO (SUBREG_REG (exp)) != regno
+		|| (((SUBREG_BYTE (exp)
+		      + (GET_MODE_SIZE (GET_MODE (exp)) - 1)) >= offset)
+		    && SUBREG_BYTE (exp) <= end))
+	    && refers_to_regno_p (regno, regno + 1, p->exp, (rtx *) 0))
+	  remove_from_table (p, i);
+      }
+}
+
+/* Recompute the hash codes of any valid entries in the hash table that
+   reference X, if X is a register, or SUBREG_REG (X) if X is a SUBREG.
+
+   This is called when we make a jump equivalence.  */
+
+static void
+rehash_using_reg (rtx x)
+{
+  unsigned int i;
+  struct table_elt *p, *next;
+  unsigned hash;
+
+  if (GET_CODE (x) == SUBREG)
+    x = SUBREG_REG (x);
+
+  /* If X is not a register or if the register is known not to be in any
+     valid entries in the table, we have no work to do.  */
+
+  if (!REG_P (x)
+      || REG_IN_TABLE (REGNO (x)) < 0
+      || REG_IN_TABLE (REGNO (x)) != REG_TICK (REGNO (x)))
+    return;
+
+  /* Scan all hash chains looking for valid entries that mention X.
+     If we find one and it is in the wrong hash chain, move it.  */
+
+  for (i = 0; i < HASH_SIZE; i++)
+    for (p = table[i]; p; p = next)
+      {
+	next = p->next_same_hash;
+	if (reg_mentioned_p (x, p->exp)
+	    && exp_equiv_p (p->exp, p->exp, 1, false)
+	    && i != (hash = SAFE_HASH (p->exp, p->mode)))
+	  {
+	    if (p->next_same_hash)
+	      p->next_same_hash->prev_same_hash = p->prev_same_hash;
+
+	    if (p->prev_same_hash)
+	      p->prev_same_hash->next_same_hash = p->next_same_hash;
+	    else
+	      table[i] = p->next_same_hash;
+
+	    p->next_same_hash = table[hash];
+	    p->prev_same_hash = 0;
+	    if (table[hash])
+	      table[hash]->prev_same_hash = p;
+	    table[hash] = p;
+	  }
+      }
+}
+
+/* Remove from the hash table any expression that is a call-clobbered
+   register.  Also update their TICK values.  */
+
+static void
+invalidate_for_call (void)
+{
+  unsigned int regno, endregno;
+  unsigned int i;
+  unsigned hash;
+  struct table_elt *p, *next;
+  int in_table = 0;
+
+  /* Go through all the hard registers.  For each that is clobbered in
+     a CALL_INSN, remove the register from quantity chains and update
+     reg_tick if defined.  Also see if any of these registers is currently
+     in the table.  */
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
+      {
+	delete_reg_equiv (regno);
+	if (REG_TICK (regno) >= 0)
+	  {
+	    REG_TICK (regno)++;
+	    SUBREG_TICKED (regno) = -1;
+	  }
+
+	in_table |= (TEST_HARD_REG_BIT (hard_regs_in_table, regno) != 0);
+      }
+
+  /* In the case where we have no call-clobbered hard registers in the
+     table, we are done.  Otherwise, scan the table and remove any
+     entry that overlaps a call-clobbered register.  */
+
+  if (in_table)
+    for (hash = 0; hash < HASH_SIZE; hash++)
+      for (p = table[hash]; p; p = next)
+	{
+	  next = p->next_same_hash;
+
+	  if (!REG_P (p->exp)
+	      || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
+	    continue;
+
+	  regno = REGNO (p->exp);
+	  endregno = END_HARD_REGNO (p->exp);
+
+	  for (i = regno; i < endregno; i++)
+	    if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i))
+	      {
+		remove_from_table (p, hash);
+		break;
+	      }
+	}
+}
+
+/* Given an expression X of type CONST,
+   and ELT which is its table entry (or 0 if it
+   is not in the hash table),
+   return an alternate expression for X as a register plus integer.
+   If none can be found, return 0.  */
+
+static rtx
+use_related_value (rtx x, struct table_elt *elt)
+{
+  struct table_elt *relt = 0;
+  struct table_elt *p, *q;
+  HOST_WIDE_INT offset;
+
+  /* First, is there anything related known?
+     If we have a table element, we can tell from that.
+     Otherwise, must look it up.  */
+
+  if (elt != 0 && elt->related_value != 0)
+    relt = elt;
+  else if (elt == 0 && GET_CODE (x) == CONST)
+    {
+      rtx subexp = get_related_value (x);
+      if (subexp != 0)
+	relt = lookup (subexp,
+		       SAFE_HASH (subexp, GET_MODE (subexp)),
+		       GET_MODE (subexp));
+    }
+
+  if (relt == 0)
+    return 0;
+
+  /* Search all related table entries for one that has an
+     equivalent register.  */
+
+  p = relt;
+  while (1)
+    {
+      /* This loop is strange in that it is executed in two different cases.
+	 The first is when X is already in the table.  Then it is searching
+	 the RELATED_VALUE list of X's class (RELT).  The second case is when
+	 X is not in the table.  Then RELT points to a class for the related
+	 value.
+
+	 Ensure that, whatever case we are in, that we ignore classes that have
+	 the same value as X.  */
+
+      if (rtx_equal_p (x, p->exp))
+	q = 0;
+      else
+	for (q = p->first_same_value; q; q = q->next_same_value)
+	  if (REG_P (q->exp))
+	    break;
+
+      if (q)
+	break;
+
+      p = p->related_value;
+
+      /* We went all the way around, so there is nothing to be found.
+	 Alternatively, perhaps RELT was in the table for some other reason
+	 and it has no related values recorded.  */
+      if (p == relt || p == 0)
+	break;
+    }
+
+  if (q == 0)
+    return 0;
+
+  offset = (get_integer_term (x) - get_integer_term (p->exp));
+  /* Note: OFFSET may be 0 if P->xexp and X are related by commutativity.  */
+  return plus_constant (q->exp, offset);
+}
+
+
+/* Hash a string.  Just add its bytes up.  */
+static inline unsigned
+hash_rtx_string (const char *ps)
+{
+  unsigned hash = 0;
+  const unsigned char *p = (const unsigned char *) ps;
+
+  if (p)
+    while (*p)
+      hash += *p++;
+
+  return hash;
+}
+
+/* Same as hash_rtx, but call CB on each rtx if it is not NULL.  
+   When the callback returns true, we continue with the new rtx.  */
+
+unsigned
+hash_rtx_cb (const_rtx x, enum machine_mode mode,
+             int *do_not_record_p, int *hash_arg_in_memory_p,
+             bool have_reg_qty, hash_rtx_callback_function cb)
+{
+  int i, j;
+  unsigned hash = 0;
+  enum rtx_code code;
+  const char *fmt;
+  enum machine_mode newmode;
+  rtx newx;
+
+  /* Used to turn recursion into iteration.  We can't rely on GCC's
+     tail-recursion elimination since we need to keep accumulating values
+     in HASH.  */
+ repeat:
+  if (x == 0)
+    return hash;
+
+  /* Invoke the callback first.  */
+  if (cb != NULL 
+      && ((*cb) (x, mode, &newx, &newmode)))
+    {
+      hash += hash_rtx_cb (newx, newmode, do_not_record_p,
+                           hash_arg_in_memory_p, have_reg_qty, cb);
+      return hash;
+    }
+
+  code = GET_CODE (x);
+  switch (code)
+    {
+    case REG:
+      {
+	unsigned int regno = REGNO (x);
+
+	if (do_not_record_p && !reload_completed)
+	  {
+	    /* On some machines, we can't record any non-fixed hard register,
+	       because extending its life will cause reload problems.  We
+	       consider ap, fp, sp, gp to be fixed for this purpose.
+
+	       We also consider CCmode registers to be fixed for this purpose;
+	       failure to do so leads to failure to simplify 0<100 type of
+	       conditionals.
+
+	       On all machines, we can't record any global registers.
+	       Nor should we record any register that is in a small
+	       class, as defined by CLASS_LIKELY_SPILLED_P.  */
+	    bool record;
+
+	    if (regno >= FIRST_PSEUDO_REGISTER)
+	      record = true;
+	    else if (x == frame_pointer_rtx
+		     || x == hard_frame_pointer_rtx
+		     || x == arg_pointer_rtx
+		     || x == stack_pointer_rtx
+		     || x == pic_offset_table_rtx)
+	      record = true;
+	    else if (global_regs[regno])
+	      record = false;
+	    else if (fixed_regs[regno])
+	      record = true;
+	    else if (GET_MODE_CLASS (GET_MODE (x)) == MODE_CC)
+	      record = true;
+	    else if (SMALL_REGISTER_CLASSES)
+	      record = false;
+	    else if (CLASS_LIKELY_SPILLED_P (REGNO_REG_CLASS (regno)))
+	      record = false;
+	    else
+	      record = true;
+
+	    if (!record)
+	      {
+		*do_not_record_p = 1;
+		return 0;
+	      }
+	  }
+
+	hash += ((unsigned int) REG << 7);
+        hash += (have_reg_qty ? (unsigned) REG_QTY (regno) : regno);
+	return hash;
+      }
+
+    /* We handle SUBREG of a REG specially because the underlying
+       reg changes its hash value with every value change; we don't
+       want to have to forget unrelated subregs when one subreg changes.  */
+    case SUBREG:
+      {
+	if (REG_P (SUBREG_REG (x)))
+	  {
+	    hash += (((unsigned int) SUBREG << 7)
+		     + REGNO (SUBREG_REG (x))
+		     + (SUBREG_BYTE (x) / UNITS_PER_WORD));
+	    return hash;
+	  }
+	break;
+      }
+
+    case CONST_INT:
+      hash += (((unsigned int) CONST_INT << 7) + (unsigned int) mode
+               + (unsigned int) INTVAL (x));
+      return hash;
+
+    case CONST_DOUBLE:
+      /* This is like the general case, except that it only counts
+	 the integers representing the constant.  */
+      hash += (unsigned int) code + (unsigned int) GET_MODE (x);
+      if (GET_MODE (x) != VOIDmode)
+	hash += real_hash (CONST_DOUBLE_REAL_VALUE (x));
+      else
+	hash += ((unsigned int) CONST_DOUBLE_LOW (x)
+		 + (unsigned int) CONST_DOUBLE_HIGH (x));
+      return hash;
+
+    case CONST_FIXED:
+      hash += (unsigned int) code + (unsigned int) GET_MODE (x);
+      hash += fixed_hash (CONST_FIXED_VALUE (x));
+      return hash;
+
+    case CONST_VECTOR:
+      {
+	int units;
+	rtx elt;
+
+	units = CONST_VECTOR_NUNITS (x);
+
+	for (i = 0; i < units; ++i)
+	  {
+	    elt = CONST_VECTOR_ELT (x, i);
+	    hash += hash_rtx_cb (elt, GET_MODE (elt),
+                                 do_not_record_p, hash_arg_in_memory_p, 
+                                 have_reg_qty, cb);
+	  }
+
+	return hash;
+      }
+
+      /* Assume there is only one rtx object for any given label.  */
+    case LABEL_REF:
+      /* We don't hash on the address of the CODE_LABEL to avoid bootstrap
+	 differences and differences between each stage's debugging dumps.  */
+	 hash += (((unsigned int) LABEL_REF << 7)
+		  + CODE_LABEL_NUMBER (XEXP (x, 0)));
+      return hash;
+
+    case SYMBOL_REF:
+      {
+	/* Don't hash on the symbol's address to avoid bootstrap differences.
+	   Different hash values may cause expressions to be recorded in
+	   different orders and thus different registers to be used in the
+	   final assembler.  This also avoids differences in the dump files
+	   between various stages.  */
+	unsigned int h = 0;
+	const unsigned char *p = (const unsigned char *) XSTR (x, 0);
+
+	while (*p)
+	  h += (h << 7) + *p++; /* ??? revisit */
+
+	hash += ((unsigned int) SYMBOL_REF << 7) + h;
+	return hash;
+      }
+
+    case MEM:
+      /* We don't record if marked volatile or if BLKmode since we don't
+	 know the size of the move.  */
+      if (do_not_record_p && (MEM_VOLATILE_P (x) || GET_MODE (x) == BLKmode))
+	{
+	  *do_not_record_p = 1;
+	  return 0;
+	}
+      if (hash_arg_in_memory_p && !MEM_READONLY_P (x))
+	*hash_arg_in_memory_p = 1;
+
+      /* Now that we have already found this special case,
+	 might as well speed it up as much as possible.  */
+      hash += (unsigned) MEM;
+      x = XEXP (x, 0);
+      goto repeat;
+
+    case USE:
+      /* A USE that mentions non-volatile memory needs special
+	 handling since the MEM may be BLKmode which normally
+	 prevents an entry from being made.  Pure calls are
+	 marked by a USE which mentions BLKmode memory.
+	 See calls.c:emit_call_1.  */
+      if (MEM_P (XEXP (x, 0))
+	  && ! MEM_VOLATILE_P (XEXP (x, 0)))
+	{
+	  hash += (unsigned) USE;
+	  x = XEXP (x, 0);
+
+	  if (hash_arg_in_memory_p && !MEM_READONLY_P (x))
+	    *hash_arg_in_memory_p = 1;
+
+	  /* Now that we have already found this special case,
+	     might as well speed it up as much as possible.  */
+	  hash += (unsigned) MEM;
+	  x = XEXP (x, 0);
+	  goto repeat;
+	}
+      break;
+
+    case PRE_DEC:
+    case PRE_INC:
+    case POST_DEC:
+    case POST_INC:
+    case PRE_MODIFY:
+    case POST_MODIFY:
+    case PC:
+    case CC0:
+    case CALL:
+    case UNSPEC_VOLATILE:
+      if (do_not_record_p) {
+        *do_not_record_p = 1;
+        return 0;
+      }
+      else
+        return hash;
+      break;
+
+    case ASM_OPERANDS:
+      if (do_not_record_p && MEM_VOLATILE_P (x))
+	{
+	  *do_not_record_p = 1;
+	  return 0;
+	}
+      else
+	{
+	  /* We don't want to take the filename and line into account.  */
+	  hash += (unsigned) code + (unsigned) GET_MODE (x)
+	    + hash_rtx_string (ASM_OPERANDS_TEMPLATE (x))
+	    + hash_rtx_string (ASM_OPERANDS_OUTPUT_CONSTRAINT (x))
+	    + (unsigned) ASM_OPERANDS_OUTPUT_IDX (x);
+
+	  if (ASM_OPERANDS_INPUT_LENGTH (x))
+	    {
+	      for (i = 1; i < ASM_OPERANDS_INPUT_LENGTH (x); i++)
+		{
+		  hash += (hash_rtx_cb (ASM_OPERANDS_INPUT (x, i),
+                                        GET_MODE (ASM_OPERANDS_INPUT (x, i)),
+                                        do_not_record_p, hash_arg_in_memory_p,
+                                        have_reg_qty, cb)
+			   + hash_rtx_string
+                           (ASM_OPERANDS_INPUT_CONSTRAINT (x, i)));
+		}
+
+	      hash += hash_rtx_string (ASM_OPERANDS_INPUT_CONSTRAINT (x, 0));
+	      x = ASM_OPERANDS_INPUT (x, 0);
+	      mode = GET_MODE (x);
+	      goto repeat;
+	    }
+
+	  return hash;
+	}
+      break;
+
+    default:
+      break;
+    }
+
+  i = GET_RTX_LENGTH (code) - 1;
+  hash += (unsigned) code + (unsigned) GET_MODE (x);
+  fmt = GET_RTX_FORMAT (code);
+  for (; i >= 0; i--)
+    {
+      switch (fmt[i])
+	{
+	case 'e':
+	  /* If we are about to do the last recursive call
+	     needed at this level, change it into iteration.
+	     This function  is called enough to be worth it.  */
+	  if (i == 0)
+	    {
+	      x = XEXP (x, i);
+	      goto repeat;
+	    }
+          
+	  hash += hash_rtx_cb (XEXP (x, i), 0, do_not_record_p,
+                               hash_arg_in_memory_p,
+                               have_reg_qty, cb);
+	  break;
+
+	case 'E':
+	  for (j = 0; j < XVECLEN (x, i); j++)
+	    hash += hash_rtx_cb (XVECEXP (x, i, j), 0, do_not_record_p,
+                                 hash_arg_in_memory_p,
+                                 have_reg_qty, cb);
+	  break;
+
+	case 's':
+	  hash += hash_rtx_string (XSTR (x, i));
+	  break;
+
+	case 'i':
+	  hash += (unsigned int) XINT (x, i);
+	  break;
+
+	case '0': case 't':
+	  /* Unused.  */
+	  break;
+
+	default:
+	  gcc_unreachable ();
+	}
+    }
+
+  return hash;
+}
+
+/* Hash an rtx.  We are careful to make sure the value is never negative.
+   Equivalent registers hash identically.
+   MODE is used in hashing for CONST_INTs only;
+   otherwise the mode of X is used.
+
+   Store 1 in DO_NOT_RECORD_P if any subexpression is volatile.
+
+   If HASH_ARG_IN_MEMORY_P is not NULL, store 1 in it if X contains
+   a MEM rtx which does not have the RTX_UNCHANGING_P bit set.
+
+   Note that cse_insn knows that the hash code of a MEM expression
+   is just (int) MEM plus the hash code of the address.  */
+
+unsigned
+hash_rtx (const_rtx x, enum machine_mode mode, int *do_not_record_p,
+	  int *hash_arg_in_memory_p, bool have_reg_qty)
+{
+  return hash_rtx_cb (x, mode, do_not_record_p,
+                      hash_arg_in_memory_p, have_reg_qty, NULL);
+}
+
+/* Hash an rtx X for cse via hash_rtx.
+   Stores 1 in do_not_record if any subexpression is volatile.
+   Stores 1 in hash_arg_in_memory if X contains a mem rtx which
+   does not have the RTX_UNCHANGING_P bit set.  */
+
+static inline unsigned
+canon_hash (rtx x, enum machine_mode mode)
+{
+  return hash_rtx (x, mode, &do_not_record, &hash_arg_in_memory, true);
+}
+
+/* Like canon_hash but with no side effects, i.e. do_not_record
+   and hash_arg_in_memory are not changed.  */
+
+static inline unsigned
+safe_hash (rtx x, enum machine_mode mode)
+{
+  int dummy_do_not_record;
+  return hash_rtx (x, mode, &dummy_do_not_record, NULL, true);
+}
+
+/* Return 1 iff X and Y would canonicalize into the same thing,
+   without actually constructing the canonicalization of either one.
+   If VALIDATE is nonzero,
+   we assume X is an expression being processed from the rtl
+   and Y was found in the hash table.  We check register refs
+   in Y for being marked as valid.
+
+   If FOR_GCSE is true, we compare X and Y for equivalence for GCSE.  */
+
+int
+exp_equiv_p (const_rtx x, const_rtx y, int validate, bool for_gcse)
+{
+  int i, j;
+  enum rtx_code code;
+  const char *fmt;
+
+  /* Note: it is incorrect to assume an expression is equivalent to itself
+     if VALIDATE is nonzero.  */
+  if (x == y && !validate)
+    return 1;
+
+  if (x == 0 || y == 0)
+    return x == y;
+
+  code = GET_CODE (x);
+  if (code != GET_CODE (y))
+    return 0;
+
+  /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent.  */
+  if (GET_MODE (x) != GET_MODE (y))
+    return 0;
+
+  switch (code)
+    {
+    case PC:
+    case CC0:
+    case CONST_INT:
+    case CONST_DOUBLE:
+    case CONST_FIXED:
+      return x == y;
+
+    case LABEL_REF:
+      return XEXP (x, 0) == XEXP (y, 0);
+
+    case SYMBOL_REF:
+      return XSTR (x, 0) == XSTR (y, 0);
+
+    case REG:
+      if (for_gcse)
+	return REGNO (x) == REGNO (y);
+      else
+	{
+	  unsigned int regno = REGNO (y);
+	  unsigned int i;
+	  unsigned int endregno = END_REGNO (y);
+
+	  /* If the quantities are not the same, the expressions are not
+	     equivalent.  If there are and we are not to validate, they
+	     are equivalent.  Otherwise, ensure all regs are up-to-date.  */
+
+	  if (REG_QTY (REGNO (x)) != REG_QTY (regno))
+	    return 0;
+
+	  if (! validate)
+	    return 1;
+
+	  for (i = regno; i < endregno; i++)
+	    if (REG_IN_TABLE (i) != REG_TICK (i))
+	      return 0;
+
+	  return 1;
+	}
+
+    case MEM:
+      if (for_gcse)
+	{
+	  /* A volatile mem should not be considered equivalent to any
+	     other.  */
+	  if (MEM_VOLATILE_P (x) || MEM_VOLATILE_P (y))
+	    return 0;
+
+	  /* Can't merge two expressions in different alias sets, since we
+	     can decide that the expression is transparent in a block when
+	     it isn't, due to it being set with the different alias set.
+
+	     Also, can't merge two expressions with different MEM_ATTRS.
+	     They could e.g. be two different entities allocated into the
+	     same space on the stack (see e.g. PR25130).  In that case, the
+	     MEM addresses can be the same, even though the two MEMs are
+	     absolutely not equivalent.  
+   
+	     But because really all MEM attributes should be the same for
+	     equivalent MEMs, we just use the invariant that MEMs that have
+	     the same attributes share the same mem_attrs data structure.  */
+	  if (MEM_ATTRS (x) != MEM_ATTRS (y))
+	    return 0;
+	}
+      break;
+
+    /*  For commutative operations, check both orders.  */
+    case PLUS:
+    case MULT:
+    case AND:
+    case IOR:
+    case XOR:
+    case NE:
+    case EQ:
+      return ((exp_equiv_p (XEXP (x, 0), XEXP (y, 0),
+			     validate, for_gcse)
+	       && exp_equiv_p (XEXP (x, 1), XEXP (y, 1),
+				validate, for_gcse))
+	      || (exp_equiv_p (XEXP (x, 0), XEXP (y, 1),
+				validate, for_gcse)
+		  && exp_equiv_p (XEXP (x, 1), XEXP (y, 0),
+				   validate, for_gcse)));
+
+    case ASM_OPERANDS:
+      /* We don't use the generic code below because we want to
+	 disregard filename and line numbers.  */
+
+      /* A volatile asm isn't equivalent to any other.  */
+      if (MEM_VOLATILE_P (x) || MEM_VOLATILE_P (y))
+	return 0;
+
+      if (GET_MODE (x) != GET_MODE (y)
+	  || strcmp (ASM_OPERANDS_TEMPLATE (x), ASM_OPERANDS_TEMPLATE (y))
+	  || strcmp (ASM_OPERANDS_OUTPUT_CONSTRAINT (x),
+		     ASM_OPERANDS_OUTPUT_CONSTRAINT (y))
+	  || ASM_OPERANDS_OUTPUT_IDX (x) != ASM_OPERANDS_OUTPUT_IDX (y)
+	  || ASM_OPERANDS_INPUT_LENGTH (x) != ASM_OPERANDS_INPUT_LENGTH (y))
+	return 0;
+
+      if (ASM_OPERANDS_INPUT_LENGTH (x))
+	{
+	  for (i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--)
+	    if (! exp_equiv_p (ASM_OPERANDS_INPUT (x, i),
+			       ASM_OPERANDS_INPUT (y, i),
+			       validate, for_gcse)
+		|| strcmp (ASM_OPERANDS_INPUT_CONSTRAINT (x, i),
+			   ASM_OPERANDS_INPUT_CONSTRAINT (y, i)))
+	      return 0;
+	}
+
+      return 1;
+
+    default:
+      break;
+    }
+
+  /* Compare the elements.  If any pair of corresponding elements
+     fail to match, return 0 for the whole thing.  */
+
+  fmt = GET_RTX_FORMAT (code);
+  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+    {
+      switch (fmt[i])
+	{
+	case 'e':
+	  if (! exp_equiv_p (XEXP (x, i), XEXP (y, i),
+			      validate, for_gcse))
+	    return 0;
+	  break;
+
+	case 'E':
+	  if (XVECLEN (x, i) != XVECLEN (y, i))
+	    return 0;
+	  for (j = 0; j < XVECLEN (x, i); j++)
+	    if (! exp_equiv_p (XVECEXP (x, i, j), XVECEXP (y, i, j),
+				validate, for_gcse))
+	      return 0;
+	  break;
+
+	case 's':
+	  if (strcmp (XSTR (x, i), XSTR (y, i)))
+	    return 0;
+	  break;
+
+	case 'i':
+	  if (XINT (x, i) != XINT (y, i))
+	    return 0;
+	  break;
+
+	case 'w':
+	  if (XWINT (x, i) != XWINT (y, i))
+	    return 0;
+	  break;
+
+	case '0':
+	case 't':
+	  break;
+
+	default:
+	  gcc_unreachable ();
+	}
+    }
+
+  return 1;
+}
+
+/* Return 1 if X has a value that can vary even between two
+   executions of the program.  0 means X can be compared reliably
+   against certain constants or near-constants.  */
+
+static bool
+cse_rtx_varies_p (const_rtx x, bool from_alias)
+{
+  /* We need not check for X and the equivalence class being of the same
+     mode because if X is equivalent to a constant in some mode, it
+     doesn't vary in any mode.  */
+
+  if (REG_P (x)
+      && REGNO_QTY_VALID_P (REGNO (x)))
+    {
+      int x_q = REG_QTY (REGNO (x));
+      struct qty_table_elem *x_ent = &qty_table[x_q];
+
+      if (GET_MODE (x) == x_ent->mode
+	  && x_ent->const_rtx != NULL_RTX)
+	return 0;
+    }
+
+  if (GET_CODE (x) == PLUS
+      && GET_CODE (XEXP (x, 1)) == CONST_INT
+      && REG_P (XEXP (x, 0))
+      && REGNO_QTY_VALID_P (REGNO (XEXP (x, 0))))
+    {
+      int x0_q = REG_QTY (REGNO (XEXP (x, 0)));
+      struct qty_table_elem *x0_ent = &qty_table[x0_q];
+
+      if ((GET_MODE (XEXP (x, 0)) == x0_ent->mode)
+	  && x0_ent->const_rtx != NULL_RTX)
+	return 0;
+    }
+
+  /* This can happen as the result of virtual register instantiation, if
+     the initial constant is too large to be a valid address.  This gives
+     us a three instruction sequence, load large offset into a register,
+     load fp minus a constant into a register, then a MEM which is the
+     sum of the two `constant' registers.  */
+  if (GET_CODE (x) == PLUS
+      && REG_P (XEXP (x, 0))
+      && REG_P (XEXP (x, 1))
+      && REGNO_QTY_VALID_P (REGNO (XEXP (x, 0)))
+      && REGNO_QTY_VALID_P (REGNO (XEXP (x, 1))))
+    {
+      int x0_q = REG_QTY (REGNO (XEXP (x, 0)));
+      int x1_q = REG_QTY (REGNO (XEXP (x, 1)));
+      struct qty_table_elem *x0_ent = &qty_table[x0_q];
+      struct qty_table_elem *x1_ent = &qty_table[x1_q];
+
+      if ((GET_MODE (XEXP (x, 0)) == x0_ent->mode)
+	  && x0_ent->const_rtx != NULL_RTX
+	  && (GET_MODE (XEXP (x, 1)) == x1_ent->mode)
+	  && x1_ent->const_rtx != NULL_RTX)
+	return 0;
+    }
+
+  return rtx_varies_p (x, from_alias);
+}
+
+/* Subroutine of canon_reg.  Pass *XLOC through canon_reg, and validate
+   the result if necessary.  INSN is as for canon_reg.  */
+
+static void
+validate_canon_reg (rtx *xloc, rtx insn)
+{
+  if (*xloc)
+    {
+      rtx new_rtx = canon_reg (*xloc, insn);
+
+      /* If replacing pseudo with hard reg or vice versa, ensure the
+         insn remains valid.  Likewise if the insn has MATCH_DUPs.  */
+      gcc_assert (insn && new_rtx);
+      validate_change (insn, xloc, new_rtx, 1);
+    }
+}
+
+/* Canonicalize an expression:
+   replace each register reference inside it
+   with the "oldest" equivalent register.
+
+   If INSN is nonzero validate_change is used to ensure that INSN remains valid
+   after we make our substitution.  The calls are made with IN_GROUP nonzero
+   so apply_change_group must be called upon the outermost return from this
+   function (unless INSN is zero).  The result of apply_change_group can
+   generally be discarded since the changes we are making are optional.  */
+
+static rtx
+canon_reg (rtx x, rtx insn)
+{
+  int i;
+  enum rtx_code code;
+  const char *fmt;
+
+  if (x == 0)
+    return x;
+
+  code = GET_CODE (x);
+  switch (code)
+    {
+    case PC:
+    case CC0:
+    case CONST:
+    case CONST_INT:
+    case CONST_DOUBLE:
+    case CONST_FIXED:
+    case CONST_VECTOR:
+    case SYMBOL_REF:
+    case LABEL_REF:
+    case ADDR_VEC:
+    case ADDR_DIFF_VEC:
+      return x;
+
+    case REG:
+      {
+	int first;
+	int q;
+	struct qty_table_elem *ent;
+
+	/* Never replace a hard reg, because hard regs can appear
+	   in more than one machine mode, and we must preserve the mode
+	   of each occurrence.  Also, some hard regs appear in
+	   MEMs that are shared and mustn't be altered.  Don't try to
+	   replace any reg that maps to a reg of class NO_REGS.  */
+	if (REGNO (x) < FIRST_PSEUDO_REGISTER
+	    || ! REGNO_QTY_VALID_P (REGNO (x)))
+	  return x;
+
+	q = REG_QTY (REGNO (x));
+	ent = &qty_table[q];
+	first = ent->first_reg;
+	return (first >= FIRST_PSEUDO_REGISTER ? regno_reg_rtx[first]
+		: REGNO_REG_CLASS (first) == NO_REGS ? x
+		: gen_rtx_REG (ent->mode, first));
+      }
+
+    default:
+      break;
+    }
+
+  fmt = GET_RTX_FORMAT (code);
+  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+    {
+      int j;
+
+      if (fmt[i] == 'e')
+	validate_canon_reg (&XEXP (x, i), insn);
+      else if (fmt[i] == 'E')
+	for (j = 0; j < XVECLEN (x, i); j++)
+	  validate_canon_reg (&XVECEXP (x, i, j), insn);
+    }
+
+  return x;
+}
+
+/* Given an operation (CODE, *PARG1, *PARG2), where code is a comparison
+   operation (EQ, NE, GT, etc.), follow it back through the hash table and
+   what values are being compared.
+
+   *PARG1 and *PARG2 are updated to contain the rtx representing the values
+   actually being compared.  For example, if *PARG1 was (cc0) and *PARG2
+   was (const_int 0), *PARG1 and *PARG2 will be set to the objects that were
+   compared to produce cc0.
+
+   The return value is the comparison operator and is either the code of
+   A or the code corresponding to the inverse of the comparison.  */
+
+static enum rtx_code
+find_comparison_args (enum rtx_code code, rtx *parg1, rtx *parg2,
+		      enum machine_mode *pmode1, enum machine_mode *pmode2)
+{
+  rtx arg1, arg2;
+
+  arg1 = *parg1, arg2 = *parg2;
+
+  /* If ARG2 is const0_rtx, see what ARG1 is equivalent to.  */
+
+  while (arg2 == CONST0_RTX (GET_MODE (arg1)))
+    {
+      /* Set nonzero when we find something of interest.  */
+      rtx x = 0;
+      int reverse_code = 0;
+      struct table_elt *p = 0;
+
+      /* If arg1 is a COMPARE, extract the comparison arguments from it.
+	 On machines with CC0, this is the only case that can occur, since
+	 fold_rtx will return the COMPARE or item being compared with zero
+	 when given CC0.  */
+
+      if (GET_CODE (arg1) == COMPARE && arg2 == const0_rtx)
+	x = arg1;
+
+      /* If ARG1 is a comparison operator and CODE is testing for
+	 STORE_FLAG_VALUE, get the inner arguments.  */
+
+      else if (COMPARISON_P (arg1))
+	{
+#ifdef FLOAT_STORE_FLAG_VALUE
+	  REAL_VALUE_TYPE fsfv;
+#endif
+
+	  if (code == NE
+	      || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_INT
+		  && code == LT && STORE_FLAG_VALUE == -1)
+#ifdef FLOAT_STORE_FLAG_VALUE
+	      || (SCALAR_FLOAT_MODE_P (GET_MODE (arg1))
+		  && (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)),
+		      REAL_VALUE_NEGATIVE (fsfv)))
+#endif
+	      )
+	    x = arg1;
+	  else if (code == EQ
+		   || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_INT
+		       && code == GE && STORE_FLAG_VALUE == -1)
+#ifdef FLOAT_STORE_FLAG_VALUE
+		   || (SCALAR_FLOAT_MODE_P (GET_MODE (arg1))
+		       && (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)),
+			   REAL_VALUE_NEGATIVE (fsfv)))
+#endif
+		   )
+	    x = arg1, reverse_code = 1;
+	}
+
+      /* ??? We could also check for
+
+	 (ne (and (eq (...) (const_int 1))) (const_int 0))
+
+	 and related forms, but let's wait until we see them occurring.  */
+
+      if (x == 0)
+	/* Look up ARG1 in the hash table and see if it has an equivalence
+	   that lets us see what is being compared.  */
+	p = lookup (arg1, SAFE_HASH (arg1, GET_MODE (arg1)), GET_MODE (arg1));
+      if (p)
+	{
+	  p = p->first_same_value;
+
+	  /* If what we compare is already known to be constant, that is as
+	     good as it gets.
+	     We need to break the loop in this case, because otherwise we
+	     can have an infinite loop when looking at a reg that is known
+	     to be a constant which is the same as a comparison of a reg
+	     against zero which appears later in the insn stream, which in
+	     turn is constant and the same as the comparison of the first reg
+	     against zero...  */
+	  if (p->is_const)
+	    break;
+	}
+
+      for (; p; p = p->next_same_value)
+	{
+	  enum machine_mode inner_mode = GET_MODE (p->exp);
+#ifdef FLOAT_STORE_FLAG_VALUE
+	  REAL_VALUE_TYPE fsfv;
+#endif
+
+	  /* If the entry isn't valid, skip it.  */
+	  if (! exp_equiv_p (p->exp, p->exp, 1, false))
+	    continue;
+
+	  if (GET_CODE (p->exp) == COMPARE
+	      /* Another possibility is that this machine has a compare insn
+		 that includes the comparison code.  In that case, ARG1 would
+		 be equivalent to a comparison operation that would set ARG1 to
+		 either STORE_FLAG_VALUE or zero.  If this is an NE operation,
+		 ORIG_CODE is the actual comparison being done; if it is an EQ,
+		 we must reverse ORIG_CODE.  On machine with a negative value
+		 for STORE_FLAG_VALUE, also look at LT and GE operations.  */
+	      || ((code == NE
+		   || (code == LT
+		       && GET_MODE_CLASS (inner_mode) == MODE_INT
+		       && (GET_MODE_BITSIZE (inner_mode)
+			   <= HOST_BITS_PER_WIDE_INT)
+		       && (STORE_FLAG_VALUE
+			   & ((HOST_WIDE_INT) 1
+			      << (GET_MODE_BITSIZE (inner_mode) - 1))))
+#ifdef FLOAT_STORE_FLAG_VALUE
+		   || (code == LT
+		       && SCALAR_FLOAT_MODE_P (inner_mode)
+		       && (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)),
+			   REAL_VALUE_NEGATIVE (fsfv)))
+#endif
+		   )
+		  && COMPARISON_P (p->exp)))
+	    {
+	      x = p->exp;
+	      break;
+	    }
+	  else if ((code == EQ
+		    || (code == GE
+			&& GET_MODE_CLASS (inner_mode) == MODE_INT
+			&& (GET_MODE_BITSIZE (inner_mode)
+			    <= HOST_BITS_PER_WIDE_INT)
+			&& (STORE_FLAG_VALUE
+			    & ((HOST_WIDE_INT) 1
+			       << (GET_MODE_BITSIZE (inner_mode) - 1))))
+#ifdef FLOAT_STORE_FLAG_VALUE
+		    || (code == GE
+			&& SCALAR_FLOAT_MODE_P (inner_mode)
+			&& (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)),
+			    REAL_VALUE_NEGATIVE (fsfv)))
+#endif
+		    )
+		   && COMPARISON_P (p->exp))
+	    {
+	      reverse_code = 1;
+	      x = p->exp;
+	      break;
+	    }
+
+	  /* If this non-trapping address, e.g. fp + constant, the
+	     equivalent is a better operand since it may let us predict
+	     the value of the comparison.  */
+	  else if (!rtx_addr_can_trap_p (p->exp))
+	    {
+	      arg1 = p->exp;
+	      continue;
+	    }
+	}
+
+      /* If we didn't find a useful equivalence for ARG1, we are done.
+	 Otherwise, set up for the next iteration.  */
+      if (x == 0)
+	break;
+
+      /* If we need to reverse the comparison, make sure that that is
+	 possible -- we can't necessarily infer the value of GE from LT
+	 with floating-point operands.  */
+      if (reverse_code)
+	{
+	  enum rtx_code reversed = reversed_comparison_code (x, NULL_RTX);
+	  if (reversed == UNKNOWN)
+	    break;
+	  else
+	    code = reversed;
+	}
+      else if (COMPARISON_P (x))
+	code = GET_CODE (x);
+      arg1 = XEXP (x, 0), arg2 = XEXP (x, 1);
+    }
+
+  /* Return our results.  Return the modes from before fold_rtx
+     because fold_rtx might produce const_int, and then it's too late.  */
+  *pmode1 = GET_MODE (arg1), *pmode2 = GET_MODE (arg2);
+  *parg1 = fold_rtx (arg1, 0), *parg2 = fold_rtx (arg2, 0);
+
+  return code;
+}
+
+/* If X is a nontrivial arithmetic operation on an argument for which
+   a constant value can be determined, return the result of operating
+   on that value, as a constant.  Otherwise, return X, possibly with
+   one or more operands changed to a forward-propagated constant.
+
+   If X is a register whose contents are known, we do NOT return
+   those contents here; equiv_constant is called to perform that task.
+   For SUBREGs and MEMs, we do that both here and in equiv_constant.
+
+   INSN is the insn that we may be modifying.  If it is 0, make a copy
+   of X before modifying it.  */
+
+static rtx
+fold_rtx (rtx x, rtx insn)
+{
+  enum rtx_code code;
+  enum machine_mode mode;
+  const char *fmt;
+  int i;
+  rtx new_rtx = 0;
+  int changed = 0;
+
+  /* Operands of X.  */
+  rtx folded_arg0;
+  rtx folded_arg1;
+
+  /* Constant equivalents of first three operands of X;
+     0 when no such equivalent is known.  */
+  rtx const_arg0;
+  rtx const_arg1;
+  rtx const_arg2;
+
+  /* The mode of the first operand of X.  We need this for sign and zero
+     extends.  */
+  enum machine_mode mode_arg0;
+
+  if (x == 0)
+    return x;
+
+  /* Try to perform some initial simplifications on X.  */
+  code = GET_CODE (x);
+  switch (code)
+    {
+    case MEM:
+    case SUBREG:
+      if ((new_rtx = equiv_constant (x)) != NULL_RTX)
+        return new_rtx;
+      return x;
+
+    case CONST:
+    case CONST_INT:
+    case CONST_DOUBLE:
+    case CONST_FIXED:
+    case CONST_VECTOR:
+    case SYMBOL_REF:
+    case LABEL_REF:
+    case REG:
+    case PC:
+      /* No use simplifying an EXPR_LIST
+	 since they are used only for lists of args
+	 in a function call's REG_EQUAL note.  */
+    case EXPR_LIST:
+      return x;
+
+#ifdef HAVE_cc0
+    case CC0:
+      return prev_insn_cc0;
+#endif
+
+    case ASM_OPERANDS:
+      if (insn)
+	{
+	  for (i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--)
+	    validate_change (insn, &ASM_OPERANDS_INPUT (x, i),
+			     fold_rtx (ASM_OPERANDS_INPUT (x, i), insn), 0);
+	}
+      return x;
+
+#ifdef NO_FUNCTION_CSE
+    case CALL:
+      if (CONSTANT_P (XEXP (XEXP (x, 0), 0)))
+	return x;
+      break;
+#endif
+
+    /* Anything else goes through the loop below.  */
+    default:
+      break;
+    }
+
+  mode = GET_MODE (x);
+  const_arg0 = 0;
+  const_arg1 = 0;
+  const_arg2 = 0;
+  mode_arg0 = VOIDmode;
+
+  /* Try folding our operands.
+     Then see which ones have constant values known.  */
+
+  fmt = GET_RTX_FORMAT (code);
+  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+    if (fmt[i] == 'e')
+      {
+	rtx folded_arg = XEXP (x, i), const_arg;
+	enum machine_mode mode_arg = GET_MODE (folded_arg);
+
+	switch (GET_CODE (folded_arg))
+	  {
+	  case MEM:
+	  case REG:
+	  case SUBREG:
+	    const_arg = equiv_constant (folded_arg);
+	    break;
+
+	  case CONST:
+	  case CONST_INT:
+	  case SYMBOL_REF:
+	  case LABEL_REF:
+	  case CONST_DOUBLE:
+	  case CONST_FIXED:
+	  case CONST_VECTOR:
+	    const_arg = folded_arg;
+	    break;
+
+#ifdef HAVE_cc0
+	  case CC0:
+	    folded_arg = prev_insn_cc0;
+	    mode_arg = prev_insn_cc0_mode;
+	    const_arg = equiv_constant (folded_arg);
+	    break;
+#endif
+
+	  default:
+	    folded_arg = fold_rtx (folded_arg, insn);
+	    const_arg = equiv_constant (folded_arg);
+	    break;
+	  }
+
+	/* For the first three operands, see if the operand
+	   is constant or equivalent to a constant.  */
+	switch (i)
+	  {
+	  case 0:
+	    folded_arg0 = folded_arg;
+	    const_arg0 = const_arg;
+	    mode_arg0 = mode_arg;
+	    break;
+	  case 1:
+	    folded_arg1 = folded_arg;
+	    const_arg1 = const_arg;
+	    break;
+	  case 2:
+	    const_arg2 = const_arg;
+	    break;
+	  }
+
+	/* Pick the least expensive of the argument and an equivalent constant
+	   argument.  */
+	if (const_arg != 0
+	    && const_arg != folded_arg
+	    && COST_IN (const_arg, code) <= COST_IN (folded_arg, code)
+
+	    /* It's not safe to substitute the operand of a conversion
+	       operator with a constant, as the conversion's identity
+	       depends upon the mode of its operand.  This optimization
+	       is handled by the call to simplify_unary_operation.  */
+	    && (GET_RTX_CLASS (code) != RTX_UNARY
+		|| GET_MODE (const_arg) == mode_arg0
+		|| (code != ZERO_EXTEND
+		    && code != SIGN_EXTEND
+		    && code != TRUNCATE
+		    && code != FLOAT_TRUNCATE
+		    && code != FLOAT_EXTEND
+		    && code != FLOAT
+		    && code != FIX
+		    && code != UNSIGNED_FLOAT
+		    && code != UNSIGNED_FIX)))
+	  folded_arg = const_arg;
+
+	if (folded_arg == XEXP (x, i))
+	  continue;
+
+	if (insn == NULL_RTX && !changed)
+	  x = copy_rtx (x);
+	changed = 1;
+	validate_unshare_change (insn, &XEXP (x, i), folded_arg, 1);
+      }
+
+  if (changed)
+    {
+      /* Canonicalize X if necessary, and keep const_argN and folded_argN
+	 consistent with the order in X.  */
+      if (canonicalize_change_group (insn, x))
+	{
+	  rtx tem;
+	  tem = const_arg0, const_arg0 = const_arg1, const_arg1 = tem;
+	  tem = folded_arg0, folded_arg0 = folded_arg1, folded_arg1 = tem;
+	}
+
+      apply_change_group ();
+    }
+
+  /* If X is an arithmetic operation, see if we can simplify it.  */
+
+  switch (GET_RTX_CLASS (code))
+    {
+    case RTX_UNARY:
+      {
+	/* We can't simplify extension ops unless we know the
+	   original mode.  */
+	if ((code == ZERO_EXTEND || code == SIGN_EXTEND)
+	    && mode_arg0 == VOIDmode)
+	  break;
+
+	new_rtx = simplify_unary_operation (code, mode,
+					const_arg0 ? const_arg0 : folded_arg0,
+					mode_arg0);
+      }
+      break;
+
+    case RTX_COMPARE:
+    case RTX_COMM_COMPARE:
+      /* See what items are actually being compared and set FOLDED_ARG[01]
+	 to those values and CODE to the actual comparison code.  If any are
+	 constant, set CONST_ARG0 and CONST_ARG1 appropriately.  We needn't
+	 do anything if both operands are already known to be constant.  */
+
+      /* ??? Vector mode comparisons are not supported yet.  */
+      if (VECTOR_MODE_P (mode))
+	break;
+
+      if (const_arg0 == 0 || const_arg1 == 0)
+	{
+	  struct table_elt *p0, *p1;
+	  rtx true_rtx, false_rtx;
+	  enum machine_mode mode_arg1;
+
+	  if (SCALAR_FLOAT_MODE_P (mode))
+	    {
+#ifdef FLOAT_STORE_FLAG_VALUE
+	      true_rtx = (CONST_DOUBLE_FROM_REAL_VALUE
+			  (FLOAT_STORE_FLAG_VALUE (mode), mode));
+#else
+	      true_rtx = NULL_RTX;
+#endif
+	      false_rtx = CONST0_RTX (mode);
+	    }
+	  else
+	    {
+	      true_rtx = const_true_rtx;
+	      false_rtx = const0_rtx;
+	    }
+
+	  code = find_comparison_args (code, &folded_arg0, &folded_arg1,
+				       &mode_arg0, &mode_arg1);
+
+	  /* If the mode is VOIDmode or a MODE_CC mode, we don't know
+	     what kinds of things are being compared, so we can't do
+	     anything with this comparison.  */
+
+	  if (mode_arg0 == VOIDmode || GET_MODE_CLASS (mode_arg0) == MODE_CC)
+	    break;
+
+	  const_arg0 = equiv_constant (folded_arg0);
+	  const_arg1 = equiv_constant (folded_arg1);
+
+	  /* If we do not now have two constants being compared, see
+	     if we can nevertheless deduce some things about the
+	     comparison.  */
+	  if (const_arg0 == 0 || const_arg1 == 0)
+	    {
+	      if (const_arg1 != NULL)
+		{
+		  rtx cheapest_simplification;
+		  int cheapest_cost;
+		  rtx simp_result;
+		  struct table_elt *p;
+
+		  /* See if we can find an equivalent of folded_arg0
+		     that gets us a cheaper expression, possibly a
+		     constant through simplifications.  */
+		  p = lookup (folded_arg0, SAFE_HASH (folded_arg0, mode_arg0),
+			      mode_arg0);
+		  
+		  if (p != NULL)
+		    {
+		      cheapest_simplification = x;
+		      cheapest_cost = COST (x);
+
+		      for (p = p->first_same_value; p != NULL; p = p->next_same_value)
+			{
+			  int cost;
+
+			  /* If the entry isn't valid, skip it.  */
+			  if (! exp_equiv_p (p->exp, p->exp, 1, false))
+			    continue;
+
+			  /* Try to simplify using this equivalence.  */
+			  simp_result
+			    = simplify_relational_operation (code, mode,
+							     mode_arg0,
+							     p->exp,
+							     const_arg1);
+
+			  if (simp_result == NULL)
+			    continue;
+
+			  cost = COST (simp_result);
+			  if (cost < cheapest_cost)
+			    {
+			      cheapest_cost = cost;
+			      cheapest_simplification = simp_result;
+			    }
+			}
+
+		      /* If we have a cheaper expression now, use that
+			 and try folding it further, from the top.  */
+		      if (cheapest_simplification != x)
+			return fold_rtx (copy_rtx (cheapest_simplification),
+					 insn);
+		    }
+		}
+
+	      /* See if the two operands are the same.  */
+
+	      if ((REG_P (folded_arg0)
+		   && REG_P (folded_arg1)
+		   && (REG_QTY (REGNO (folded_arg0))
+		       == REG_QTY (REGNO (folded_arg1))))
+		  || ((p0 = lookup (folded_arg0,
+				    SAFE_HASH (folded_arg0, mode_arg0),
+				    mode_arg0))
+		      && (p1 = lookup (folded_arg1,
+				       SAFE_HASH (folded_arg1, mode_arg0),
+				       mode_arg0))
+		      && p0->first_same_value == p1->first_same_value))
+		folded_arg1 = folded_arg0;
+
+	      /* If FOLDED_ARG0 is a register, see if the comparison we are
+		 doing now is either the same as we did before or the reverse
+		 (we only check the reverse if not floating-point).  */
+	      else if (REG_P (folded_arg0))
+		{
+		  int qty = REG_QTY (REGNO (folded_arg0));
+
+		  if (REGNO_QTY_VALID_P (REGNO (folded_arg0)))
+		    {
+		      struct qty_table_elem *ent = &qty_table[qty];
+
+		      if ((comparison_dominates_p (ent->comparison_code, code)
+			   || (! FLOAT_MODE_P (mode_arg0)
+			       && comparison_dominates_p (ent->comparison_code,
+						          reverse_condition (code))))
+			  && (rtx_equal_p (ent->comparison_const, folded_arg1)
+			      || (const_arg1
+				  && rtx_equal_p (ent->comparison_const,
+						  const_arg1))
+			      || (REG_P (folded_arg1)
+				  && (REG_QTY (REGNO (folded_arg1)) == ent->comparison_qty))))
+			{
+			  if (comparison_dominates_p (ent->comparison_code, code))
+			    {
+			      if (true_rtx)
+				return true_rtx;
+			      else
+				break;
+			    }
+			  else
+			    return false_rtx;
+			}
+		    }
+		}
+	    }
+	}
+
+      /* If we are comparing against zero, see if the first operand is
+	 equivalent to an IOR with a constant.  If so, we may be able to
+	 determine the result of this comparison.  */
+      if (const_arg1 == const0_rtx && !const_arg0)
+	{
+	  rtx y = lookup_as_function (folded_arg0, IOR);
+	  rtx inner_const;
+
+	  if (y != 0
+	      && (inner_const = equiv_constant (XEXP (y, 1))) != 0
+	      && GET_CODE (inner_const) == CONST_INT
+	      && INTVAL (inner_const) != 0)
+	    folded_arg0 = gen_rtx_IOR (mode_arg0, XEXP (y, 0), inner_const);
+	}
+
+      {
+	rtx op0 = const_arg0 ? const_arg0 : folded_arg0;
+	rtx op1 = const_arg1 ? const_arg1 : folded_arg1;
+        new_rtx = simplify_relational_operation (code, mode, mode_arg0, op0, op1);
+      }
+      break;
+
+    case RTX_BIN_ARITH:
+    case RTX_COMM_ARITH:
+      switch (code)
+	{
+	case PLUS:
+	  /* If the second operand is a LABEL_REF, see if the first is a MINUS
+	     with that LABEL_REF as its second operand.  If so, the result is
+	     the first operand of that MINUS.  This handles switches with an
+	     ADDR_DIFF_VEC table.  */
+	  if (const_arg1 && GET_CODE (const_arg1) == LABEL_REF)
+	    {
+	      rtx y
+		= GET_CODE (folded_arg0) == MINUS ? folded_arg0
+		: lookup_as_function (folded_arg0, MINUS);
+
+	      if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF
+		  && XEXP (XEXP (y, 1), 0) == XEXP (const_arg1, 0))
+		return XEXP (y, 0);
+
+	      /* Now try for a CONST of a MINUS like the above.  */
+	      if ((y = (GET_CODE (folded_arg0) == CONST ? folded_arg0
+			: lookup_as_function (folded_arg0, CONST))) != 0
+		  && GET_CODE (XEXP (y, 0)) == MINUS
+		  && GET_CODE (XEXP (XEXP (y, 0), 1)) == LABEL_REF
+		  && XEXP (XEXP (XEXP (y, 0), 1), 0) == XEXP (const_arg1, 0))
+		return XEXP (XEXP (y, 0), 0);
+	    }
+
+	  /* Likewise if the operands are in the other order.  */
+	  if (const_arg0 && GET_CODE (const_arg0) == LABEL_REF)
+	    {
+	      rtx y
+		= GET_CODE (folded_arg1) == MINUS ? folded_arg1
+		: lookup_as_function (folded_arg1, MINUS);
+
+	      if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF
+		  && XEXP (XEXP (y, 1), 0) == XEXP (const_arg0, 0))
+		return XEXP (y, 0);
+
+	      /* Now try for a CONST of a MINUS like the above.  */
+	      if ((y = (GET_CODE (folded_arg1) == CONST ? folded_arg1
+			: lookup_as_function (folded_arg1, CONST))) != 0
+		  && GET_CODE (XEXP (y, 0)) == MINUS
+		  && GET_CODE (XEXP (XEXP (y, 0), 1)) == LABEL_REF
+		  && XEXP (XEXP (XEXP (y, 0), 1), 0) == XEXP (const_arg0, 0))
+		return XEXP (XEXP (y, 0), 0);
+	    }
+
+	  /* If second operand is a register equivalent to a negative
+	     CONST_INT, see if we can find a register equivalent to the
+	     positive constant.  Make a MINUS if so.  Don't do this for
+	     a non-negative constant since we might then alternate between
+	     choosing positive and negative constants.  Having the positive
+	     constant previously-used is the more common case.  Be sure
+	     the resulting constant is non-negative; if const_arg1 were
+	     the smallest negative number this would overflow: depending
+	     on the mode, this would either just be the same value (and
+	     hence not save anything) or be incorrect.  */
+	  if (const_arg1 != 0 && GET_CODE (const_arg1) == CONST_INT
+	      && INTVAL (const_arg1) < 0
+	      /* This used to test
+
+	         -INTVAL (const_arg1) >= 0
+
+		 But The Sun V5.0 compilers mis-compiled that test.  So
+		 instead we test for the problematic value in a more direct
+		 manner and hope the Sun compilers get it correct.  */
+	      && INTVAL (const_arg1) !=
+	        ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1))
+	      && REG_P (folded_arg1))
+	    {
+	      rtx new_const = GEN_INT (-INTVAL (const_arg1));
+	      struct table_elt *p
+		= lookup (new_const, SAFE_HASH (new_const, mode), mode);
+
+	      if (p)
+		for (p = p->first_same_value; p; p = p->next_same_value)
+		  if (REG_P (p->exp))
+		    return simplify_gen_binary (MINUS, mode, folded_arg0,
+						canon_reg (p->exp, NULL_RTX));
+	    }
+	  goto from_plus;
+
+	case MINUS:
+	  /* If we have (MINUS Y C), see if Y is known to be (PLUS Z C2).
+	     If so, produce (PLUS Z C2-C).  */
+	  if (const_arg1 != 0 && GET_CODE (const_arg1) == CONST_INT)
+	    {
+	      rtx y = lookup_as_function (XEXP (x, 0), PLUS);
+	      if (y && GET_CODE (XEXP (y, 1)) == CONST_INT)
+		return fold_rtx (plus_constant (copy_rtx (y),
+						-INTVAL (const_arg1)),
+				 NULL_RTX);
+	    }
+
+	  /* Fall through.  */
+
+	from_plus:
+	case SMIN:    case SMAX:      case UMIN:    case UMAX:
+	case IOR:     case AND:       case XOR:
+	case MULT:
+	case ASHIFT:  case LSHIFTRT:  case ASHIFTRT:
+	  /* If we have (<op> <reg> <const_int>) for an associative OP and REG
+	     is known to be of similar form, we may be able to replace the
+	     operation with a combined operation.  This may eliminate the
+	     intermediate operation if every use is simplified in this way.
+	     Note that the similar optimization done by combine.c only works
+	     if the intermediate operation's result has only one reference.  */
+
+	  if (REG_P (folded_arg0)
+	      && const_arg1 && GET_CODE (const_arg1) == CONST_INT)
+	    {
+	      int is_shift
+		= (code == ASHIFT || code == ASHIFTRT || code == LSHIFTRT);
+	      rtx y, inner_const, new_const;
+	      rtx canon_const_arg1 = const_arg1;
+	      enum rtx_code associate_code;
+
+	      if (is_shift
+		  && (INTVAL (const_arg1) >= GET_MODE_BITSIZE (mode)
+		      || INTVAL (const_arg1) < 0))
+		{
+		  if (SHIFT_COUNT_TRUNCATED)
+		    canon_const_arg1 = GEN_INT (INTVAL (const_arg1)
+						& (GET_MODE_BITSIZE (mode)
+						   - 1));
+		  else
+		    break;
+		}
+
+	      y = lookup_as_function (folded_arg0, code);
+	      if (y == 0)
+		break;
+
+	      /* If we have compiled a statement like
+		 "if (x == (x & mask1))", and now are looking at
+		 "x & mask2", we will have a case where the first operand
+		 of Y is the same as our first operand.  Unless we detect
+		 this case, an infinite loop will result.  */
+	      if (XEXP (y, 0) == folded_arg0)
+		break;
+
+	      inner_const = equiv_constant (fold_rtx (XEXP (y, 1), 0));
+	      if (!inner_const || GET_CODE (inner_const) != CONST_INT)
+		break;
+
+	      /* Don't associate these operations if they are a PLUS with the
+		 same constant and it is a power of two.  These might be doable
+		 with a pre- or post-increment.  Similarly for two subtracts of
+		 identical powers of two with post decrement.  */
+
+	      if (code == PLUS && const_arg1 == inner_const
+		  && ((HAVE_PRE_INCREMENT
+			  && exact_log2 (INTVAL (const_arg1)) >= 0)
+		      || (HAVE_POST_INCREMENT
+			  && exact_log2 (INTVAL (const_arg1)) >= 0)
+		      || (HAVE_PRE_DECREMENT
+			  && exact_log2 (- INTVAL (const_arg1)) >= 0)
+		      || (HAVE_POST_DECREMENT
+			  && exact_log2 (- INTVAL (const_arg1)) >= 0)))
+		break;
+
+	      /* ??? Vector mode shifts by scalar
+		 shift operand are not supported yet.  */
+	      if (is_shift && VECTOR_MODE_P (mode))
+                break;
+
+	      if (is_shift
+		  && (INTVAL (inner_const) >= GET_MODE_BITSIZE (mode)
+		      || INTVAL (inner_const) < 0))
+		{
+		  if (SHIFT_COUNT_TRUNCATED)
+		    inner_const = GEN_INT (INTVAL (inner_const)
+					   & (GET_MODE_BITSIZE (mode) - 1));
+		  else
+		    break;
+		}
+
+	      /* Compute the code used to compose the constants.  For example,
+		 A-C1-C2 is A-(C1 + C2), so if CODE == MINUS, we want PLUS.  */
+
+	      associate_code = (is_shift || code == MINUS ? PLUS : code);
+
+	      new_const = simplify_binary_operation (associate_code, mode,
+						     canon_const_arg1,
+						     inner_const);
+
+	      if (new_const == 0)
+		break;
+
+	      /* If we are associating shift operations, don't let this
+		 produce a shift of the size of the object or larger.
+		 This could occur when we follow a sign-extend by a right
+		 shift on a machine that does a sign-extend as a pair
+		 of shifts.  */
+
+	      if (is_shift
+		  && GET_CODE (new_const) == CONST_INT
+		  && INTVAL (new_const) >= GET_MODE_BITSIZE (mode))
+		{
+		  /* As an exception, we can turn an ASHIFTRT of this
+		     form into a shift of the number of bits - 1.  */
+		  if (code == ASHIFTRT)
+		    new_const = GEN_INT (GET_MODE_BITSIZE (mode) - 1);
+		  else if (!side_effects_p (XEXP (y, 0)))
+		    return CONST0_RTX (mode);
+		  else
+		    break;
+		}
+
+	      y = copy_rtx (XEXP (y, 0));
+
+	      /* If Y contains our first operand (the most common way this
+		 can happen is if Y is a MEM), we would do into an infinite
+		 loop if we tried to fold it.  So don't in that case.  */
+
+	      if (! reg_mentioned_p (folded_arg0, y))
+		y = fold_rtx (y, insn);
+
+	      return simplify_gen_binary (code, mode, y, new_const);
+	    }
+	  break;
+
+	case DIV:       case UDIV:
+	  /* ??? The associative optimization performed immediately above is
+	     also possible for DIV and UDIV using associate_code of MULT.
+	     However, we would need extra code to verify that the
+	     multiplication does not overflow, that is, there is no overflow
+	     in the calculation of new_const.  */
+	  break;
+
+	default:
+	  break;
+	}
+
+      new_rtx = simplify_binary_operation (code, mode,
+				       const_arg0 ? const_arg0 : folded_arg0,
+				       const_arg1 ? const_arg1 : folded_arg1);
+      break;
+
+    case RTX_OBJ:
+      /* (lo_sum (high X) X) is simply X.  */
+      if (code == LO_SUM && const_arg0 != 0
+	  && GET_CODE (const_arg0) == HIGH
+	  && rtx_equal_p (XEXP (const_arg0, 0), const_arg1))
+	return const_arg1;
+      break;
+
+    case RTX_TERNARY:
+    case RTX_BITFIELD_OPS:
+      new_rtx = simplify_ternary_operation (code, mode, mode_arg0,
+					const_arg0 ? const_arg0 : folded_arg0,
+					const_arg1 ? const_arg1 : folded_arg1,
+					const_arg2 ? const_arg2 : XEXP (x, 2));
+      break;
+
+    default:
+      break;
+    }
+
+  return new_rtx ? new_rtx : x;
+}
+
+/* Return a constant value currently equivalent to X.
+   Return 0 if we don't know one.  */
+
+static rtx
+equiv_constant (rtx x)
+{
+  if (REG_P (x)
+      && REGNO_QTY_VALID_P (REGNO (x)))
+    {
+      int x_q = REG_QTY (REGNO (x));
+      struct qty_table_elem *x_ent = &qty_table[x_q];
+
+      if (x_ent->const_rtx)
+	x = gen_lowpart (GET_MODE (x), x_ent->const_rtx);
+    }
+
+  if (x == 0 || CONSTANT_P (x))
+    return x;
+
+  if (GET_CODE (x) == SUBREG)
+    {
+      enum machine_mode mode = GET_MODE (x);
+      enum machine_mode imode = GET_MODE (SUBREG_REG (x));
+      rtx new_rtx;
+
+      /* See if we previously assigned a constant value to this SUBREG.  */
+      if ((new_rtx = lookup_as_function (x, CONST_INT)) != 0
+          || (new_rtx = lookup_as_function (x, CONST_DOUBLE)) != 0
+          || (new_rtx = lookup_as_function (x, CONST_FIXED)) != 0)
+        return new_rtx;
+
+      /* If we didn't and if doing so makes sense, see if we previously
+	 assigned a constant value to the enclosing word mode SUBREG.  */
+      if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (word_mode)
+	  && GET_MODE_SIZE (word_mode) < GET_MODE_SIZE (imode))
+	{
+	  int byte = SUBREG_BYTE (x) - subreg_lowpart_offset (mode, word_mode);
+	  if (byte >= 0 && (byte % UNITS_PER_WORD) == 0)
+	    {
+	      rtx y = gen_rtx_SUBREG (word_mode, SUBREG_REG (x), byte);
+	      new_rtx = lookup_as_function (y, CONST_INT);
+	      if (new_rtx)
+		return gen_lowpart (mode, new_rtx);
+	    }
+	}
+
+      /* Otherwise see if we already have a constant for the inner REG.  */
+      if (REG_P (SUBREG_REG (x))
+	  && (new_rtx = equiv_constant (SUBREG_REG (x))) != 0)
+        return simplify_subreg (mode, new_rtx, imode, SUBREG_BYTE (x));
+
+      return 0;
+    }
+
+  /* If X is a MEM, see if it is a constant-pool reference, or look it up in
+     the hash table in case its value was seen before.  */
+
+  if (MEM_P (x))
+    {
+      struct table_elt *elt;
+
+      x = avoid_constant_pool_reference (x);
+      if (CONSTANT_P (x))
+	return x;
+
+      elt = lookup (x, SAFE_HASH (x, GET_MODE (x)), GET_MODE (x));
+      if (elt == 0)
+	return 0;
+
+      for (elt = elt->first_same_value; elt; elt = elt->next_same_value)
+	if (elt->is_const && CONSTANT_P (elt->exp))
+	  return elt->exp;
+    }
+
+  return 0;
+}
+
+/* Given INSN, a jump insn, TAKEN indicates if we are following the
+   "taken" branch.
+
+   In certain cases, this can cause us to add an equivalence.  For example,
+   if we are following the taken case of
+	if (i == 2)
+   we can add the fact that `i' and '2' are now equivalent.
+
+   In any case, we can record that this comparison was passed.  If the same
+   comparison is seen later, we will know its value.  */
+
+static void
+record_jump_equiv (rtx insn, bool taken)
+{
+  int cond_known_true;
+  rtx op0, op1;
+  rtx set;
+  enum machine_mode mode, mode0, mode1;
+  int reversed_nonequality = 0;
+  enum rtx_code code;
+
+  /* Ensure this is the right kind of insn.  */
+  gcc_assert (any_condjump_p (insn));
+
+  set = pc_set (insn);
+
+  /* See if this jump condition is known true or false.  */
+  if (taken)
+    cond_known_true = (XEXP (SET_SRC (set), 2) == pc_rtx);
+  else
+    cond_known_true = (XEXP (SET_SRC (set), 1) == pc_rtx);
+
+  /* Get the type of comparison being done and the operands being compared.
+     If we had to reverse a non-equality condition, record that fact so we
+     know that it isn't valid for floating-point.  */
+  code = GET_CODE (XEXP (SET_SRC (set), 0));
+  op0 = fold_rtx (XEXP (XEXP (SET_SRC (set), 0), 0), insn);
+  op1 = fold_rtx (XEXP (XEXP (SET_SRC (set), 0), 1), insn);
+
+  code = find_comparison_args (code, &op0, &op1, &mode0, &mode1);
+  if (! cond_known_true)
+    {
+      code = reversed_comparison_code_parts (code, op0, op1, insn);
+
+      /* Don't remember if we can't find the inverse.  */
+      if (code == UNKNOWN)
+	return;
+    }
+
+  /* The mode is the mode of the non-constant.  */
+  mode = mode0;
+  if (mode1 != VOIDmode)
+    mode = mode1;
+
+  record_jump_cond (code, mode, op0, op1, reversed_nonequality);
+}
+
+/* Yet another form of subreg creation.  In this case, we want something in
+   MODE, and we should assume OP has MODE iff it is naturally modeless.  */
+
+static rtx
+record_jump_cond_subreg (enum machine_mode mode, rtx op)
+{
+  enum machine_mode op_mode = GET_MODE (op);
+  if (op_mode == mode || op_mode == VOIDmode)
+    return op;
+  return lowpart_subreg (mode, op, op_mode);
+}
+
+/* We know that comparison CODE applied to OP0 and OP1 in MODE is true.
+   REVERSED_NONEQUALITY is nonzero if CODE had to be swapped.
+   Make any useful entries we can with that information.  Called from
+   above function and called recursively.  */
+
+static void
+record_jump_cond (enum rtx_code code, enum machine_mode mode, rtx op0,
+		  rtx op1, int reversed_nonequality)
+{
+  unsigned op0_hash, op1_hash;
+  int op0_in_memory, op1_in_memory;
+  struct table_elt *op0_elt, *op1_elt;
+
+  /* If OP0 and OP1 are known equal, and either is a paradoxical SUBREG,
+     we know that they are also equal in the smaller mode (this is also
+     true for all smaller modes whether or not there is a SUBREG, but
+     is not worth testing for with no SUBREG).  */
+
+  /* Note that GET_MODE (op0) may not equal MODE.  */
+  if (code == EQ && GET_CODE (op0) == SUBREG
+      && (GET_MODE_SIZE (GET_MODE (op0))
+	  > GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0)))))
+    {
+      enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op0));
+      rtx tem = record_jump_cond_subreg (inner_mode, op1);
+      if (tem)
+	record_jump_cond (code, mode, SUBREG_REG (op0), tem,
+			  reversed_nonequality);
+    }
+
+  if (code == EQ && GET_CODE (op1) == SUBREG
+      && (GET_MODE_SIZE (GET_MODE (op1))
+	  > GET_MODE_SIZE (GET_MODE (SUBREG_REG (op1)))))
+    {
+      enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op1));
+      rtx tem = record_jump_cond_subreg (inner_mode, op0);
+      if (tem)
+	record_jump_cond (code, mode, SUBREG_REG (op1), tem,
+			  reversed_nonequality);
+    }
+
+  /* Similarly, if this is an NE comparison, and either is a SUBREG
+     making a smaller mode, we know the whole thing is also NE.  */
+
+  /* Note that GET_MODE (op0) may not equal MODE;
+     if we test MODE instead, we can get an infinite recursion
+     alternating between two modes each wider than MODE.  */
+
+  if (code == NE && GET_CODE (op0) == SUBREG
+      && subreg_lowpart_p (op0)
+      && (GET_MODE_SIZE (GET_MODE (op0))
+	  < GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0)))))
+    {
+      enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op0));
+      rtx tem = record_jump_cond_subreg (inner_mode, op1);
+      if (tem)
+	record_jump_cond (code, mode, SUBREG_REG (op0), tem,
+			  reversed_nonequality);
+    }
+
+  if (code == NE && GET_CODE (op1) == SUBREG
+      && subreg_lowpart_p (op1)
+      && (GET_MODE_SIZE (GET_MODE (op1))
+	  < GET_MODE_SIZE (GET_MODE (SUBREG_REG (op1)))))
+    {
+      enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op1));
+      rtx tem = record_jump_cond_subreg (inner_mode, op0);
+      if (tem)
+	record_jump_cond (code, mode, SUBREG_REG (op1), tem,
+			  reversed_nonequality);
+    }
+
+  /* Hash both operands.  */
+
+  do_not_record = 0;
+  hash_arg_in_memory = 0;
+  op0_hash = HASH (op0, mode);
+  op0_in_memory = hash_arg_in_memory;
+
+  if (do_not_record)
+    return;
+
+  do_not_record = 0;
+  hash_arg_in_memory = 0;
+  op1_hash = HASH (op1, mode);
+  op1_in_memory = hash_arg_in_memory;
+
+  if (do_not_record)
+    return;
+
+  /* Look up both operands.  */
+  op0_elt = lookup (op0, op0_hash, mode);
+  op1_elt = lookup (op1, op1_hash, mode);
+
+  /* If both operands are already equivalent or if they are not in the
+     table but are identical, do nothing.  */
+  if ((op0_elt != 0 && op1_elt != 0
+       && op0_elt->first_same_value == op1_elt->first_same_value)
+      || op0 == op1 || rtx_equal_p (op0, op1))
+    return;
+
+  /* If we aren't setting two things equal all we can do is save this
+     comparison.   Similarly if this is floating-point.  In the latter
+     case, OP1 might be zero and both -0.0 and 0.0 are equal to it.
+     If we record the equality, we might inadvertently delete code
+     whose intent was to change -0 to +0.  */
+
+  if (code != EQ || FLOAT_MODE_P (GET_MODE (op0)))
+    {
+      struct qty_table_elem *ent;
+      int qty;
+
+      /* If we reversed a floating-point comparison, if OP0 is not a
+	 register, or if OP1 is neither a register or constant, we can't
+	 do anything.  */
+
+      if (!REG_P (op1))
+	op1 = equiv_constant (op1);
+
+      if ((reversed_nonequality && FLOAT_MODE_P (mode))
+	  || !REG_P (op0) || op1 == 0)
+	return;
+
+      /* Put OP0 in the hash table if it isn't already.  This gives it a
+	 new quantity number.  */
+      if (op0_elt == 0)
+	{
+	  if (insert_regs (op0, NULL, 0))
+	    {
+	      rehash_using_reg (op0);
+	      op0_hash = HASH (op0, mode);
+
+	      /* If OP0 is contained in OP1, this changes its hash code
+		 as well.  Faster to rehash than to check, except
+		 for the simple case of a constant.  */
+	      if (! CONSTANT_P (op1))
+		op1_hash = HASH (op1,mode);
+	    }
+
+	  op0_elt = insert (op0, NULL, op0_hash, mode);
+	  op0_elt->in_memory = op0_in_memory;
+	}
+
+      qty = REG_QTY (REGNO (op0));
+      ent = &qty_table[qty];
+
+      ent->comparison_code = code;
+      if (REG_P (op1))
+	{
+	  /* Look it up again--in case op0 and op1 are the same.  */
+	  op1_elt = lookup (op1, op1_hash, mode);
+
+	  /* Put OP1 in the hash table so it gets a new quantity number.  */
+	  if (op1_elt == 0)
+	    {
+	      if (insert_regs (op1, NULL, 0))
+		{
+		  rehash_using_reg (op1);
+		  op1_hash = HASH (op1, mode);
+		}
+
+	      op1_elt = insert (op1, NULL, op1_hash, mode);
+	      op1_elt->in_memory = op1_in_memory;
+	    }
+
+	  ent->comparison_const = NULL_RTX;
+	  ent->comparison_qty = REG_QTY (REGNO (op1));
+	}
+      else
+	{
+	  ent->comparison_const = op1;
+	  ent->comparison_qty = -1;
+	}
+
+      return;
+    }
+
+  /* If either side is still missing an equivalence, make it now,
+     then merge the equivalences.  */
+
+  if (op0_elt == 0)
+    {
+      if (insert_regs (op0, NULL, 0))
+	{
+	  rehash_using_reg (op0);
+	  op0_hash = HASH (op0, mode);
+	}
+
+      op0_elt = insert (op0, NULL, op0_hash, mode);
+      op0_elt->in_memory = op0_in_memory;
+    }
+
+  if (op1_elt == 0)
+    {
+      if (insert_regs (op1, NULL, 0))
+	{
+	  rehash_using_reg (op1);
+	  op1_hash = HASH (op1, mode);
+	}
+
+      op1_elt = insert (op1, NULL, op1_hash, mode);
+      op1_elt->in_memory = op1_in_memory;
+    }
+
+  merge_equiv_classes (op0_elt, op1_elt);
+}
+
+/* CSE processing for one instruction.
+   First simplify sources and addresses of all assignments
+   in the instruction, using previously-computed equivalents values.
+   Then install the new sources and destinations in the table
+   of available values.  */
+
+/* Data on one SET contained in the instruction.  */
+
+struct set
+{
+  /* The SET rtx itself.  */
+  rtx rtl;
+  /* The SET_SRC of the rtx (the original value, if it is changing).  */
+  rtx src;
+  /* The hash-table element for the SET_SRC of the SET.  */
+  struct table_elt *src_elt;
+  /* Hash value for the SET_SRC.  */
+  unsigned src_hash;
+  /* Hash value for the SET_DEST.  */
+  unsigned dest_hash;
+  /* The SET_DEST, with SUBREG, etc., stripped.  */
+  rtx inner_dest;
+  /* Nonzero if the SET_SRC is in memory.  */
+  char src_in_memory;
+  /* Nonzero if the SET_SRC contains something
+     whose value cannot be predicted and understood.  */
+  char src_volatile;
+  /* Original machine mode, in case it becomes a CONST_INT.
+     The size of this field should match the size of the mode
+     field of struct rtx_def (see rtl.h).  */
+  ENUM_BITFIELD(machine_mode) mode : 8;
+  /* A constant equivalent for SET_SRC, if any.  */
+  rtx src_const;
+  /* Hash value of constant equivalent for SET_SRC.  */
+  unsigned src_const_hash;
+  /* Table entry for constant equivalent for SET_SRC, if any.  */
+  struct table_elt *src_const_elt;
+  /* Table entry for the destination address.  */
+  struct table_elt *dest_addr_elt;
+};
+
+static void
+cse_insn (rtx insn)
+{
+  rtx x = PATTERN (insn);
+  int i;
+  rtx tem;
+  int n_sets = 0;
+
+  rtx src_eqv = 0;
+  struct table_elt *src_eqv_elt = 0;
+  int src_eqv_volatile = 0;
+  int src_eqv_in_memory = 0;
+  unsigned src_eqv_hash = 0;
+
+  struct set *sets = (struct set *) 0;
+
+  this_insn = insn;
+#ifdef HAVE_cc0
+  /* Records what this insn does to set CC0.  */
+  this_insn_cc0 = 0;
+  this_insn_cc0_mode = VOIDmode;
+#endif
+
+  /* Find all the SETs and CLOBBERs in this instruction.
+     Record all the SETs in the array `set' and count them.
+     Also determine whether there is a CLOBBER that invalidates
+     all memory references, or all references at varying addresses.  */
+
+  if (CALL_P (insn))
+    {
+      for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1))
+	{
+	  if (GET_CODE (XEXP (tem, 0)) == CLOBBER)
+	    invalidate (SET_DEST (XEXP (tem, 0)), VOIDmode);
+	  XEXP (tem, 0) = canon_reg (XEXP (tem, 0), insn);
+	}
+    }
+
+  if (GET_CODE (x) == SET)
+    {
+      sets = XALLOCA (struct set);
+      sets[0].rtl = x;
+
+      /* Ignore SETs that are unconditional jumps.
+	 They never need cse processing, so this does not hurt.
+	 The reason is not efficiency but rather
+	 so that we can test at the end for instructions
+	 that have been simplified to unconditional jumps
+	 and not be misled by unchanged instructions
+	 that were unconditional jumps to begin with.  */
+      if (SET_DEST (x) == pc_rtx
+	  && GET_CODE (SET_SRC (x)) == LABEL_REF)
+	;
+
+      /* Don't count call-insns, (set (reg 0) (call ...)), as a set.
+	 The hard function value register is used only once, to copy to
+	 someplace else, so it isn't worth cse'ing (and on 80386 is unsafe)!
+	 Ensure we invalidate the destination register.  On the 80386 no
+	 other code would invalidate it since it is a fixed_reg.
+	 We need not check the return of apply_change_group; see canon_reg.  */
+
+      else if (GET_CODE (SET_SRC (x)) == CALL)
+	{
+	  canon_reg (SET_SRC (x), insn);
+	  apply_change_group ();
+	  fold_rtx (SET_SRC (x), insn);
+	  invalidate (SET_DEST (x), VOIDmode);
+	}
+      else
+	n_sets = 1;
+    }
+  else if (GET_CODE (x) == PARALLEL)
+    {
+      int lim = XVECLEN (x, 0);
+
+      sets = XALLOCAVEC (struct set, lim);
+
+      /* Find all regs explicitly clobbered in this insn,
+	 and ensure they are not replaced with any other regs
+	 elsewhere in this insn.
+	 When a reg that is clobbered is also used for input,
+	 we should presume that that is for a reason,
+	 and we should not substitute some other register
+	 which is not supposed to be clobbered.
+	 Therefore, this loop cannot be merged into the one below
+	 because a CALL may precede a CLOBBER and refer to the
+	 value clobbered.  We must not let a canonicalization do
+	 anything in that case.  */
+      for (i = 0; i < lim; i++)
+	{
+	  rtx y = XVECEXP (x, 0, i);
+	  if (GET_CODE (y) == CLOBBER)
+	    {
+	      rtx clobbered = XEXP (y, 0);
+
+	      if (REG_P (clobbered)
+		  || GET_CODE (clobbered) == SUBREG)
+		invalidate (clobbered, VOIDmode);
+	      else if (GET_CODE (clobbered) == STRICT_LOW_PART
+		       || GET_CODE (clobbered) == ZERO_EXTRACT)
+		invalidate (XEXP (clobbered, 0), GET_MODE (clobbered));
+	    }
+	}
+
+      for (i = 0; i < lim; i++)
+	{
+	  rtx y = XVECEXP (x, 0, i);
+	  if (GET_CODE (y) == SET)
+	    {
+	      /* As above, we ignore unconditional jumps and call-insns and
+		 ignore the result of apply_change_group.  */
+	      if (GET_CODE (SET_SRC (y)) == CALL)
+		{
+		  canon_reg (SET_SRC (y), insn);
+		  apply_change_group ();
+		  fold_rtx (SET_SRC (y), insn);
+		  invalidate (SET_DEST (y), VOIDmode);
+		}
+	      else if (SET_DEST (y) == pc_rtx
+		       && GET_CODE (SET_SRC (y)) == LABEL_REF)
+		;
+	      else
+		sets[n_sets++].rtl = y;
+	    }
+	  else if (GET_CODE (y) == CLOBBER)
+	    {
+	      /* If we clobber memory, canon the address.
+		 This does nothing when a register is clobbered
+		 because we have already invalidated the reg.  */
+	      if (MEM_P (XEXP (y, 0)))
+		canon_reg (XEXP (y, 0), insn);
+	    }
+	  else if (GET_CODE (y) == USE
+		   && ! (REG_P (XEXP (y, 0))
+			 && REGNO (XEXP (y, 0)) < FIRST_PSEUDO_REGISTER))
+	    canon_reg (y, insn);
+	  else if (GET_CODE (y) == CALL)
+	    {
+	      /* The result of apply_change_group can be ignored; see
+		 canon_reg.  */
+	      canon_reg (y, insn);
+	      apply_change_group ();
+	      fold_rtx (y, insn);
+	    }
+	}
+    }
+  else if (GET_CODE (x) == CLOBBER)
+    {
+      if (MEM_P (XEXP (x, 0)))
+	canon_reg (XEXP (x, 0), insn);
+    }
+
+  /* Canonicalize a USE of a pseudo register or memory location.  */
+  else if (GET_CODE (x) == USE
+	   && ! (REG_P (XEXP (x, 0))
+		 && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER))
+    canon_reg (XEXP (x, 0), insn);
+  else if (GET_CODE (x) == CALL)
+    {
+      /* The result of apply_change_group can be ignored; see canon_reg.  */
+      canon_reg (x, insn);
+      apply_change_group ();
+      fold_rtx (x, insn);
+    }
+
+  /* Store the equivalent value in SRC_EQV, if different, or if the DEST
+     is a STRICT_LOW_PART.  The latter condition is necessary because SRC_EQV
+     is handled specially for this case, and if it isn't set, then there will
+     be no equivalence for the destination.  */
+  if (n_sets == 1 && REG_NOTES (insn) != 0
+      && (tem = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0
+      && (! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl))
+	  || GET_CODE (SET_DEST (sets[0].rtl)) == STRICT_LOW_PART))
+    {
+      /* The result of apply_change_group can be ignored; see canon_reg.  */
+      canon_reg (XEXP (tem, 0), insn);
+      apply_change_group ();
+      src_eqv = fold_rtx (XEXP (tem, 0), insn);
+      XEXP (tem, 0) = copy_rtx (src_eqv);
+      df_notes_rescan (insn);
+    }
+
+  /* Canonicalize sources and addresses of destinations.
+     We do this in a separate pass to avoid problems when a MATCH_DUP is
+     present in the insn pattern.  In that case, we want to ensure that
+     we don't break the duplicate nature of the pattern.  So we will replace
+     both operands at the same time.  Otherwise, we would fail to find an
+     equivalent substitution in the loop calling validate_change below.
+
+     We used to suppress canonicalization of DEST if it appears in SRC,
+     but we don't do this any more.  */
+
+  for (i = 0; i < n_sets; i++)
+    {
+      rtx dest = SET_DEST (sets[i].rtl);
+      rtx src = SET_SRC (sets[i].rtl);
+      rtx new_rtx = canon_reg (src, insn);
+
+      validate_change (insn, &SET_SRC (sets[i].rtl), new_rtx, 1);
+
+      if (GET_CODE (dest) == ZERO_EXTRACT)
+	{
+	  validate_change (insn, &XEXP (dest, 1),
+			   canon_reg (XEXP (dest, 1), insn), 1);
+	  validate_change (insn, &XEXP (dest, 2),
+			   canon_reg (XEXP (dest, 2), insn), 1);
+	}
+
+      while (GET_CODE (dest) == SUBREG
+	     || GET_CODE (dest) == ZERO_EXTRACT
+	     || GET_CODE (dest) == STRICT_LOW_PART)
+	dest = XEXP (dest, 0);
+
+      if (MEM_P (dest))
+	canon_reg (dest, insn);
+    }
+
+  /* Now that we have done all the replacements, we can apply the change
+     group and see if they all work.  Note that this will cause some
+     canonicalizations that would have worked individually not to be applied
+     because some other canonicalization didn't work, but this should not
+     occur often.
+
+     The result of apply_change_group can be ignored; see canon_reg.  */
+
+  apply_change_group ();
+
+  /* Set sets[i].src_elt to the class each source belongs to.
+     Detect assignments from or to volatile things
+     and set set[i] to zero so they will be ignored
+     in the rest of this function.
+
+     Nothing in this loop changes the hash table or the register chains.  */
+
+  for (i = 0; i < n_sets; i++)
+    {
+      rtx src, dest;
+      rtx src_folded;
+      struct table_elt *elt = 0, *p;
+      enum machine_mode mode;
+      rtx src_eqv_here;
+      rtx src_const = 0;
+      rtx src_related = 0;
+      struct table_elt *src_const_elt = 0;
+      int src_cost = MAX_COST;
+      int src_eqv_cost = MAX_COST;
+      int src_folded_cost = MAX_COST;
+      int src_related_cost = MAX_COST;
+      int src_elt_cost = MAX_COST;
+      int src_regcost = MAX_COST;
+      int src_eqv_regcost = MAX_COST;
+      int src_folded_regcost = MAX_COST;
+      int src_related_regcost = MAX_COST;
+      int src_elt_regcost = MAX_COST;
+      /* Set nonzero if we need to call force_const_mem on with the
+	 contents of src_folded before using it.  */
+      int src_folded_force_flag = 0;
+
+      dest = SET_DEST (sets[i].rtl);
+      src = SET_SRC (sets[i].rtl);
+
+      /* If SRC is a constant that has no machine mode,
+	 hash it with the destination's machine mode.
+	 This way we can keep different modes separate.  */
+
+      mode = GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src);
+      sets[i].mode = mode;
+
+      if (src_eqv)
+	{
+	  enum machine_mode eqvmode = mode;
+	  if (GET_CODE (dest) == STRICT_LOW_PART)
+	    eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0)));
+	  do_not_record = 0;
+	  hash_arg_in_memory = 0;
+	  src_eqv_hash = HASH (src_eqv, eqvmode);
+
+	  /* Find the equivalence class for the equivalent expression.  */
+
+	  if (!do_not_record)
+	    src_eqv_elt = lookup (src_eqv, src_eqv_hash, eqvmode);
+
+	  src_eqv_volatile = do_not_record;
+	  src_eqv_in_memory = hash_arg_in_memory;
+	}
+
+      /* If this is a STRICT_LOW_PART assignment, src_eqv corresponds to the
+	 value of the INNER register, not the destination.  So it is not
+	 a valid substitution for the source.  But save it for later.  */
+      if (GET_CODE (dest) == STRICT_LOW_PART)
+	src_eqv_here = 0;
+      else
+	src_eqv_here = src_eqv;
+
+      /* Simplify and foldable subexpressions in SRC.  Then get the fully-
+	 simplified result, which may not necessarily be valid.  */
+      src_folded = fold_rtx (src, insn);
+
+#if 0
+      /* ??? This caused bad code to be generated for the m68k port with -O2.
+	 Suppose src is (CONST_INT -1), and that after truncation src_folded
+	 is (CONST_INT 3).  Suppose src_folded is then used for src_const.
+	 At the end we will add src and src_const to the same equivalence
+	 class.  We now have 3 and -1 on the same equivalence class.  This
+	 causes later instructions to be mis-optimized.  */
+      /* If storing a constant in a bitfield, pre-truncate the constant
+	 so we will be able to record it later.  */
+      if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT)
+	{
+	  rtx width = XEXP (SET_DEST (sets[i].rtl), 1);
+
+	  if (GET_CODE (src) == CONST_INT
+	      && GET_CODE (width) == CONST_INT
+	      && INTVAL (width) < HOST_BITS_PER_WIDE_INT
+	      && (INTVAL (src) & ((HOST_WIDE_INT) (-1) << INTVAL (width))))
+	    src_folded
+	      = GEN_INT (INTVAL (src) & (((HOST_WIDE_INT) 1
+					  << INTVAL (width)) - 1));
+	}
+#endif
+
+      /* Compute SRC's hash code, and also notice if it
+	 should not be recorded at all.  In that case,
+	 prevent any further processing of this assignment.  */
+      do_not_record = 0;
+      hash_arg_in_memory = 0;
+
+      sets[i].src = src;
+      sets[i].src_hash = HASH (src, mode);
+      sets[i].src_volatile = do_not_record;
+      sets[i].src_in_memory = hash_arg_in_memory;
+
+      /* If SRC is a MEM, there is a REG_EQUIV note for SRC, and DEST is
+	 a pseudo, do not record SRC.  Using SRC as a replacement for
+	 anything else will be incorrect in that situation.  Note that
+	 this usually occurs only for stack slots, in which case all the
+	 RTL would be referring to SRC, so we don't lose any optimization
+	 opportunities by not having SRC in the hash table.  */
+
+      if (MEM_P (src)
+	  && find_reg_note (insn, REG_EQUIV, NULL_RTX) != 0
+	  && REG_P (dest)
+	  && REGNO (dest) >= FIRST_PSEUDO_REGISTER)
+	sets[i].src_volatile = 1;
+
+#if 0
+      /* It is no longer clear why we used to do this, but it doesn't
+	 appear to still be needed.  So let's try without it since this
+	 code hurts cse'ing widened ops.  */
+      /* If source is a paradoxical subreg (such as QI treated as an SI),
+	 treat it as volatile.  It may do the work of an SI in one context
+	 where the extra bits are not being used, but cannot replace an SI
+	 in general.  */
+      if (GET_CODE (src) == SUBREG
+	  && (GET_MODE_SIZE (GET_MODE (src))
+	      > GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))))
+	sets[i].src_volatile = 1;
+#endif
+
+      /* Locate all possible equivalent forms for SRC.  Try to replace
+         SRC in the insn with each cheaper equivalent.
+
+         We have the following types of equivalents: SRC itself, a folded
+         version, a value given in a REG_EQUAL note, or a value related
+	 to a constant.
+
+         Each of these equivalents may be part of an additional class
+         of equivalents (if more than one is in the table, they must be in
+         the same class; we check for this).
+
+	 If the source is volatile, we don't do any table lookups.
+
+         We note any constant equivalent for possible later use in a
+         REG_NOTE.  */
+
+      if (!sets[i].src_volatile)
+	elt = lookup (src, sets[i].src_hash, mode);
+
+      sets[i].src_elt = elt;
+
+      if (elt && src_eqv_here && src_eqv_elt)
+	{
+	  if (elt->first_same_value != src_eqv_elt->first_same_value)
+	    {
+	      /* The REG_EQUAL is indicating that two formerly distinct
+		 classes are now equivalent.  So merge them.  */
+	      merge_equiv_classes (elt, src_eqv_elt);
+	      src_eqv_hash = HASH (src_eqv, elt->mode);
+	      src_eqv_elt = lookup (src_eqv, src_eqv_hash, elt->mode);
+	    }
+
+	  src_eqv_here = 0;
+	}
+
+      else if (src_eqv_elt)
+	elt = src_eqv_elt;
+
+      /* Try to find a constant somewhere and record it in `src_const'.
+	 Record its table element, if any, in `src_const_elt'.  Look in
+	 any known equivalences first.  (If the constant is not in the
+	 table, also set `sets[i].src_const_hash').  */
+      if (elt)
+	for (p = elt->first_same_value; p; p = p->next_same_value)
+	  if (p->is_const)
+	    {
+	      src_const = p->exp;
+	      src_const_elt = elt;
+	      break;
+	    }
+
+      if (src_const == 0
+	  && (CONSTANT_P (src_folded)
+	      /* Consider (minus (label_ref L1) (label_ref L2)) as
+		 "constant" here so we will record it. This allows us
+		 to fold switch statements when an ADDR_DIFF_VEC is used.  */
+	      || (GET_CODE (src_folded) == MINUS
+		  && GET_CODE (XEXP (src_folded, 0)) == LABEL_REF
+		  && GET_CODE (XEXP (src_folded, 1)) == LABEL_REF)))
+	src_const = src_folded, src_const_elt = elt;
+      else if (src_const == 0 && src_eqv_here && CONSTANT_P (src_eqv_here))
+	src_const = src_eqv_here, src_const_elt = src_eqv_elt;
+
+      /* If we don't know if the constant is in the table, get its
+	 hash code and look it up.  */
+      if (src_const && src_const_elt == 0)
+	{
+	  sets[i].src_const_hash = HASH (src_const, mode);
+	  src_const_elt = lookup (src_const, sets[i].src_const_hash, mode);
+	}
+
+      sets[i].src_const = src_const;
+      sets[i].src_const_elt = src_const_elt;
+
+      /* If the constant and our source are both in the table, mark them as
+	 equivalent.  Otherwise, if a constant is in the table but the source
+	 isn't, set ELT to it.  */
+      if (src_const_elt && elt
+	  && src_const_elt->first_same_value != elt->first_same_value)
+	merge_equiv_classes (elt, src_const_elt);
+      else if (src_const_elt && elt == 0)
+	elt = src_const_elt;
+
+      /* See if there is a register linearly related to a constant
+         equivalent of SRC.  */
+      if (src_const
+	  && (GET_CODE (src_const) == CONST
+	      || (src_const_elt && src_const_elt->related_value != 0)))
+	{
+	  src_related = use_related_value (src_const, src_const_elt);
+	  if (src_related)
+	    {
+	      struct table_elt *src_related_elt
+		= lookup (src_related, HASH (src_related, mode), mode);
+	      if (src_related_elt && elt)
+		{
+		  if (elt->first_same_value
+		      != src_related_elt->first_same_value)
+		    /* This can occur when we previously saw a CONST
+		       involving a SYMBOL_REF and then see the SYMBOL_REF
+		       twice.  Merge the involved classes.  */
+		    merge_equiv_classes (elt, src_related_elt);
+
+		  src_related = 0;
+		  src_related_elt = 0;
+		}
+	      else if (src_related_elt && elt == 0)
+		elt = src_related_elt;
+	    }
+	}
+
+      /* See if we have a CONST_INT that is already in a register in a
+	 wider mode.  */
+
+      if (src_const && src_related == 0 && GET_CODE (src_const) == CONST_INT
+	  && GET_MODE_CLASS (mode) == MODE_INT
+	  && GET_MODE_BITSIZE (mode) < BITS_PER_WORD)
+	{
+	  enum machine_mode wider_mode;
+
+	  for (wider_mode = GET_MODE_WIDER_MODE (mode);
+	       wider_mode != VOIDmode
+	       && GET_MODE_BITSIZE (wider_mode) <= BITS_PER_WORD
+	       && src_related == 0;
+	       wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+	    {
+	      struct table_elt *const_elt
+		= lookup (src_const, HASH (src_const, wider_mode), wider_mode);
+
+	      if (const_elt == 0)
+		continue;
+
+	      for (const_elt = const_elt->first_same_value;
+		   const_elt; const_elt = const_elt->next_same_value)
+		if (REG_P (const_elt->exp))
+		  {
+		    src_related = gen_lowpart (mode, const_elt->exp);
+		    break;
+		  }
+	    }
+	}
+
+      /* Another possibility is that we have an AND with a constant in
+	 a mode narrower than a word.  If so, it might have been generated
+	 as part of an "if" which would narrow the AND.  If we already
+	 have done the AND in a wider mode, we can use a SUBREG of that
+	 value.  */
+
+      if (flag_expensive_optimizations && ! src_related
+	  && GET_CODE (src) == AND && GET_CODE (XEXP (src, 1)) == CONST_INT
+	  && GET_MODE_SIZE (mode) < UNITS_PER_WORD)
+	{
+	  enum machine_mode tmode;
+	  rtx new_and = gen_rtx_AND (VOIDmode, NULL_RTX, XEXP (src, 1));
+
+	  for (tmode = GET_MODE_WIDER_MODE (mode);
+	       GET_MODE_SIZE (tmode) <= UNITS_PER_WORD;
+	       tmode = GET_MODE_WIDER_MODE (tmode))
+	    {
+	      rtx inner = gen_lowpart (tmode, XEXP (src, 0));
+	      struct table_elt *larger_elt;
+
+	      if (inner)
+		{
+		  PUT_MODE (new_and, tmode);
+		  XEXP (new_and, 0) = inner;
+		  larger_elt = lookup (new_and, HASH (new_and, tmode), tmode);
+		  if (larger_elt == 0)
+		    continue;
+
+		  for (larger_elt = larger_elt->first_same_value;
+		       larger_elt; larger_elt = larger_elt->next_same_value)
+		    if (REG_P (larger_elt->exp))
+		      {
+			src_related
+			  = gen_lowpart (mode, larger_elt->exp);
+			break;
+		      }
+
+		  if (src_related)
+		    break;
+		}
+	    }
+	}
+
+#ifdef LOAD_EXTEND_OP
+      /* See if a MEM has already been loaded with a widening operation;
+	 if it has, we can use a subreg of that.  Many CISC machines
+	 also have such operations, but this is only likely to be
+	 beneficial on these machines.  */
+
+      if (flag_expensive_optimizations && src_related == 0
+	  && (GET_MODE_SIZE (mode) < UNITS_PER_WORD)
+	  && GET_MODE_CLASS (mode) == MODE_INT
+	  && MEM_P (src) && ! do_not_record
+	  && LOAD_EXTEND_OP (mode) != UNKNOWN)
+	{
+	  struct rtx_def memory_extend_buf;
+	  rtx memory_extend_rtx = &memory_extend_buf;
+	  enum machine_mode tmode;
+
+	  /* Set what we are trying to extend and the operation it might
+	     have been extended with.  */
+	  memset (memory_extend_rtx, 0, sizeof(*memory_extend_rtx));
+	  PUT_CODE (memory_extend_rtx, LOAD_EXTEND_OP (mode));
+	  XEXP (memory_extend_rtx, 0) = src;
+
+	  for (tmode = GET_MODE_WIDER_MODE (mode);
+	       GET_MODE_SIZE (tmode) <= UNITS_PER_WORD;
+	       tmode = GET_MODE_WIDER_MODE (tmode))
+	    {
+	      struct table_elt *larger_elt;
+
+	      PUT_MODE (memory_extend_rtx, tmode);
+	      larger_elt = lookup (memory_extend_rtx,
+				   HASH (memory_extend_rtx, tmode), tmode);
+	      if (larger_elt == 0)
+		continue;
+
+	      for (larger_elt = larger_elt->first_same_value;
+		   larger_elt; larger_elt = larger_elt->next_same_value)
+		if (REG_P (larger_elt->exp))
+		  {
+		    src_related = gen_lowpart (mode, larger_elt->exp);
+		    break;
+		  }
+
+	      if (src_related)
+		break;
+	    }
+	}
+#endif /* LOAD_EXTEND_OP */
+
+      if (src == src_folded)
+	src_folded = 0;
+
+      /* At this point, ELT, if nonzero, points to a class of expressions
+         equivalent to the source of this SET and SRC, SRC_EQV, SRC_FOLDED,
+	 and SRC_RELATED, if nonzero, each contain additional equivalent
+	 expressions.  Prune these latter expressions by deleting expressions
+	 already in the equivalence class.
+
+	 Check for an equivalent identical to the destination.  If found,
+	 this is the preferred equivalent since it will likely lead to
+	 elimination of the insn.  Indicate this by placing it in
+	 `src_related'.  */
+
+      if (elt)
+	elt = elt->first_same_value;
+      for (p = elt; p; p = p->next_same_value)
+	{
+	  enum rtx_code code = GET_CODE (p->exp);
+
+	  /* If the expression is not valid, ignore it.  Then we do not
+	     have to check for validity below.  In most cases, we can use
+	     `rtx_equal_p', since canonicalization has already been done.  */
+	  if (code != REG && ! exp_equiv_p (p->exp, p->exp, 1, false))
+	    continue;
+
+	  /* Also skip paradoxical subregs, unless that's what we're
+	     looking for.  */
+	  if (code == SUBREG
+	      && (GET_MODE_SIZE (GET_MODE (p->exp))
+		  > GET_MODE_SIZE (GET_MODE (SUBREG_REG (p->exp))))
+	      && ! (src != 0
+		    && GET_CODE (src) == SUBREG
+		    && GET_MODE (src) == GET_MODE (p->exp)
+		    && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))
+			< GET_MODE_SIZE (GET_MODE (SUBREG_REG (p->exp))))))
+	    continue;
+
+	  if (src && GET_CODE (src) == code && rtx_equal_p (src, p->exp))
+	    src = 0;
+	  else if (src_folded && GET_CODE (src_folded) == code
+		   && rtx_equal_p (src_folded, p->exp))
+	    src_folded = 0;
+	  else if (src_eqv_here && GET_CODE (src_eqv_here) == code
+		   && rtx_equal_p (src_eqv_here, p->exp))
+	    src_eqv_here = 0;
+	  else if (src_related && GET_CODE (src_related) == code
+		   && rtx_equal_p (src_related, p->exp))
+	    src_related = 0;
+
+	  /* This is the same as the destination of the insns, we want
+	     to prefer it.  Copy it to src_related.  The code below will
+	     then give it a negative cost.  */
+	  if (GET_CODE (dest) == code && rtx_equal_p (p->exp, dest))
+	    src_related = dest;
+	}
+
+      /* Find the cheapest valid equivalent, trying all the available
+         possibilities.  Prefer items not in the hash table to ones
+         that are when they are equal cost.  Note that we can never
+         worsen an insn as the current contents will also succeed.
+	 If we find an equivalent identical to the destination, use it as best,
+	 since this insn will probably be eliminated in that case.  */
+      if (src)
+	{
+	  if (rtx_equal_p (src, dest))
+	    src_cost = src_regcost = -1;
+	  else
+	    {
+	      src_cost = COST (src);
+	      src_regcost = approx_reg_cost (src);
+	    }
+	}
+
+      if (src_eqv_here)
+	{
+	  if (rtx_equal_p (src_eqv_here, dest))
+	    src_eqv_cost = src_eqv_regcost = -1;
+	  else
+	    {
+	      src_eqv_cost = COST (src_eqv_here);
+	      src_eqv_regcost = approx_reg_cost (src_eqv_here);
+	    }
+	}
+
+      if (src_folded)
+	{
+	  if (rtx_equal_p (src_folded, dest))
+	    src_folded_cost = src_folded_regcost = -1;
+	  else
+	    {
+	      src_folded_cost = COST (src_folded);
+	      src_folded_regcost = approx_reg_cost (src_folded);
+	    }
+	}
+
+      if (src_related)
+	{
+	  if (rtx_equal_p (src_related, dest))
+	    src_related_cost = src_related_regcost = -1;
+	  else
+	    {
+	      src_related_cost = COST (src_related);
+	      src_related_regcost = approx_reg_cost (src_related);
+	    }
+	}
+
+      /* If this was an indirect jump insn, a known label will really be
+	 cheaper even though it looks more expensive.  */
+      if (dest == pc_rtx && src_const && GET_CODE (src_const) == LABEL_REF)
+	src_folded = src_const, src_folded_cost = src_folded_regcost = -1;
+
+      /* Terminate loop when replacement made.  This must terminate since
+         the current contents will be tested and will always be valid.  */
+      while (1)
+	{
+	  rtx trial;
+
+	  /* Skip invalid entries.  */
+	  while (elt && !REG_P (elt->exp)
+		 && ! exp_equiv_p (elt->exp, elt->exp, 1, false))
+	    elt = elt->next_same_value;
+
+	  /* A paradoxical subreg would be bad here: it'll be the right
+	     size, but later may be adjusted so that the upper bits aren't
+	     what we want.  So reject it.  */
+	  if (elt != 0
+	      && GET_CODE (elt->exp) == SUBREG
+	      && (GET_MODE_SIZE (GET_MODE (elt->exp))
+		  > GET_MODE_SIZE (GET_MODE (SUBREG_REG (elt->exp))))
+	      /* It is okay, though, if the rtx we're trying to match
+		 will ignore any of the bits we can't predict.  */
+	      && ! (src != 0
+		    && GET_CODE (src) == SUBREG
+		    && GET_MODE (src) == GET_MODE (elt->exp)
+		    && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))
+			< GET_MODE_SIZE (GET_MODE (SUBREG_REG (elt->exp))))))
+	    {
+	      elt = elt->next_same_value;
+	      continue;
+	    }
+
+	  if (elt)
+	    {
+	      src_elt_cost = elt->cost;
+	      src_elt_regcost = elt->regcost;
+	    }
+
+	  /* Find cheapest and skip it for the next time.   For items
+	     of equal cost, use this order:
+	     src_folded, src, src_eqv, src_related and hash table entry.  */
+	  if (src_folded
+	      && preferable (src_folded_cost, src_folded_regcost,
+			     src_cost, src_regcost) <= 0
+	      && preferable (src_folded_cost, src_folded_regcost,
+			     src_eqv_cost, src_eqv_regcost) <= 0
+	      && preferable (src_folded_cost, src_folded_regcost,
+			     src_related_cost, src_related_regcost) <= 0
+	      && preferable (src_folded_cost, src_folded_regcost,
+			     src_elt_cost, src_elt_regcost) <= 0)
+	    {
+	      trial = src_folded, src_folded_cost = MAX_COST;
+	      if (src_folded_force_flag)
+		{
+		  rtx forced = force_const_mem (mode, trial);
+		  if (forced)
+		    trial = forced;
+		}
+	    }
+	  else if (src
+		   && preferable (src_cost, src_regcost,
+				  src_eqv_cost, src_eqv_regcost) <= 0
+		   && preferable (src_cost, src_regcost,
+				  src_related_cost, src_related_regcost) <= 0
+		   && preferable (src_cost, src_regcost,
+				  src_elt_cost, src_elt_regcost) <= 0)
+	    trial = src, src_cost = MAX_COST;
+	  else if (src_eqv_here
+		   && preferable (src_eqv_cost, src_eqv_regcost,
+				  src_related_cost, src_related_regcost) <= 0
+		   && preferable (src_eqv_cost, src_eqv_regcost,
+				  src_elt_cost, src_elt_regcost) <= 0)
+	    trial = src_eqv_here, src_eqv_cost = MAX_COST;
+	  else if (src_related
+		   && preferable (src_related_cost, src_related_regcost,
+				  src_elt_cost, src_elt_regcost) <= 0)
+	    trial = src_related, src_related_cost = MAX_COST;
+	  else
+	    {
+	      trial = elt->exp;
+	      elt = elt->next_same_value;
+	      src_elt_cost = MAX_COST;
+	    }
+
+	  /* Avoid creation of overlapping memory moves.  */
+	  if (MEM_P (trial) && MEM_P (SET_DEST (sets[i].rtl)))
+	    {
+	      rtx src, dest;
+
+	      /* BLKmode moves are not handled by cse anyway.  */
+	      if (GET_MODE (trial) == BLKmode)
+		break;
+
+	      src = canon_rtx (trial);
+	      dest = canon_rtx (SET_DEST (sets[i].rtl));
+
+	      if (!MEM_P (src) || !MEM_P (dest)
+		  || !nonoverlapping_memrefs_p (src, dest))
+		break;
+	    }
+
+	  /* We don't normally have an insn matching (set (pc) (pc)), so
+	     check for this separately here.  We will delete such an
+	     insn below.
+
+	     For other cases such as a table jump or conditional jump
+	     where we know the ultimate target, go ahead and replace the
+	     operand.  While that may not make a valid insn, we will
+	     reemit the jump below (and also insert any necessary
+	     barriers).  */
+	  if (n_sets == 1 && dest == pc_rtx
+	      && (trial == pc_rtx
+		  || (GET_CODE (trial) == LABEL_REF
+		      && ! condjump_p (insn))))
+	    {
+	      /* Don't substitute non-local labels, this confuses CFG.  */
+	      if (GET_CODE (trial) == LABEL_REF
+		  && LABEL_REF_NONLOCAL_P (trial))
+		continue;
+
+	      SET_SRC (sets[i].rtl) = trial;
+	      cse_jumps_altered = true;
+	      break;
+	    }
+
+	  /* Reject certain invalid forms of CONST that we create.  */
+	  else if (CONSTANT_P (trial)
+		   && GET_CODE (trial) == CONST
+		   /* Reject cases that will cause decode_rtx_const to
+		      die.  On the alpha when simplifying a switch, we
+		      get (const (truncate (minus (label_ref)
+		      (label_ref)))).  */
+		   && (GET_CODE (XEXP (trial, 0)) == TRUNCATE
+		       /* Likewise on IA-64, except without the
+			  truncate.  */
+		       || (GET_CODE (XEXP (trial, 0)) == MINUS
+			   && GET_CODE (XEXP (XEXP (trial, 0), 0)) == LABEL_REF
+			   && GET_CODE (XEXP (XEXP (trial, 0), 1)) == LABEL_REF)))
+	    /* Do nothing for this case.  */
+	    ;
+
+	  /* Look for a substitution that makes a valid insn.  */
+	  else if (validate_unshare_change
+		     (insn, &SET_SRC (sets[i].rtl), trial, 0))
+	    {
+	      rtx new_rtx = canon_reg (SET_SRC (sets[i].rtl), insn);
+
+	      /* The result of apply_change_group can be ignored; see
+		 canon_reg.  */
+
+	      validate_change (insn, &SET_SRC (sets[i].rtl), new_rtx, 1);
+	      apply_change_group ();
+
+	      break;
+	    }
+
+	  /* If we previously found constant pool entries for
+	     constants and this is a constant, try making a
+	     pool entry.  Put it in src_folded unless we already have done
+	     this since that is where it likely came from.  */
+
+	  else if (constant_pool_entries_cost
+		   && CONSTANT_P (trial)
+		   && (src_folded == 0
+		       || (!MEM_P (src_folded)
+			   && ! src_folded_force_flag))
+		   && GET_MODE_CLASS (mode) != MODE_CC
+		   && mode != VOIDmode)
+	    {
+	      src_folded_force_flag = 1;
+	      src_folded = trial;
+	      src_folded_cost = constant_pool_entries_cost;
+	      src_folded_regcost = constant_pool_entries_regcost;
+	    }
+	}
+
+      src = SET_SRC (sets[i].rtl);
+
+      /* In general, it is good to have a SET with SET_SRC == SET_DEST.
+	 However, there is an important exception:  If both are registers
+	 that are not the head of their equivalence class, replace SET_SRC
+	 with the head of the class.  If we do not do this, we will have
+	 both registers live over a portion of the basic block.  This way,
+	 their lifetimes will likely abut instead of overlapping.  */
+      if (REG_P (dest)
+	  && REGNO_QTY_VALID_P (REGNO (dest)))
+	{
+	  int dest_q = REG_QTY (REGNO (dest));
+	  struct qty_table_elem *dest_ent = &qty_table[dest_q];
+
+	  if (dest_ent->mode == GET_MODE (dest)
+	      && dest_ent->first_reg != REGNO (dest)
+	      && REG_P (src) && REGNO (src) == REGNO (dest)
+	      /* Don't do this if the original insn had a hard reg as
+		 SET_SRC or SET_DEST.  */
+	      && (!REG_P (sets[i].src)
+		  || REGNO (sets[i].src) >= FIRST_PSEUDO_REGISTER)
+	      && (!REG_P (dest) || REGNO (dest) >= FIRST_PSEUDO_REGISTER))
+	    /* We can't call canon_reg here because it won't do anything if
+	       SRC is a hard register.  */
+	    {
+	      int src_q = REG_QTY (REGNO (src));
+	      struct qty_table_elem *src_ent = &qty_table[src_q];
+	      int first = src_ent->first_reg;
+	      rtx new_src
+		= (first >= FIRST_PSEUDO_REGISTER
+		   ? regno_reg_rtx[first] : gen_rtx_REG (GET_MODE (src), first));
+
+	      /* We must use validate-change even for this, because this
+		 might be a special no-op instruction, suitable only to
+		 tag notes onto.  */
+	      if (validate_change (insn, &SET_SRC (sets[i].rtl), new_src, 0))
+		{
+		  src = new_src;
+		  /* If we had a constant that is cheaper than what we are now
+		     setting SRC to, use that constant.  We ignored it when we
+		     thought we could make this into a no-op.  */
+		  if (src_const && COST (src_const) < COST (src)
+		      && validate_change (insn, &SET_SRC (sets[i].rtl),
+					  src_const, 0))
+		    src = src_const;
+		}
+	    }
+	}
+
+      /* If we made a change, recompute SRC values.  */
+      if (src != sets[i].src)
+	{
+	  do_not_record = 0;
+	  hash_arg_in_memory = 0;
+	  sets[i].src = src;
+	  sets[i].src_hash = HASH (src, mode);
+	  sets[i].src_volatile = do_not_record;
+	  sets[i].src_in_memory = hash_arg_in_memory;
+	  sets[i].src_elt = lookup (src, sets[i].src_hash, mode);
+	}
+
+      /* If this is a single SET, we are setting a register, and we have an
+	 equivalent constant, we want to add a REG_NOTE.   We don't want
+	 to write a REG_EQUAL note for a constant pseudo since verifying that
+	 that pseudo hasn't been eliminated is a pain.  Such a note also
+	 won't help anything.
+
+	 Avoid a REG_EQUAL note for (CONST (MINUS (LABEL_REF) (LABEL_REF)))
+	 which can be created for a reference to a compile time computable
+	 entry in a jump table.  */
+
+      if (n_sets == 1 && src_const && REG_P (dest)
+	  && !REG_P (src_const)
+	  && ! (GET_CODE (src_const) == CONST
+		&& GET_CODE (XEXP (src_const, 0)) == MINUS
+		&& GET_CODE (XEXP (XEXP (src_const, 0), 0)) == LABEL_REF
+		&& GET_CODE (XEXP (XEXP (src_const, 0), 1)) == LABEL_REF))
+	{
+	  /* We only want a REG_EQUAL note if src_const != src.  */
+	  if (! rtx_equal_p (src, src_const))
+	    {
+	      /* Make sure that the rtx is not shared.  */
+	      src_const = copy_rtx (src_const);
+
+	      /* Record the actual constant value in a REG_EQUAL note,
+		 making a new one if one does not already exist.  */
+	      set_unique_reg_note (insn, REG_EQUAL, src_const);
+	      df_notes_rescan (insn);
+	    }
+	}
+
+      /* Now deal with the destination.  */
+      do_not_record = 0;
+
+      /* Look within any ZERO_EXTRACT to the MEM or REG within it.  */
+      while (GET_CODE (dest) == SUBREG
+	     || GET_CODE (dest) == ZERO_EXTRACT
+	     || GET_CODE (dest) == STRICT_LOW_PART)
+	dest = XEXP (dest, 0);
+
+      sets[i].inner_dest = dest;
+
+      if (MEM_P (dest))
+	{
+#ifdef PUSH_ROUNDING
+	  /* Stack pushes invalidate the stack pointer.  */
+	  rtx addr = XEXP (dest, 0);
+	  if (GET_RTX_CLASS (GET_CODE (addr)) == RTX_AUTOINC
+	      && XEXP (addr, 0) == stack_pointer_rtx)
+	    invalidate (stack_pointer_rtx, VOIDmode);
+#endif
+	  dest = fold_rtx (dest, insn);
+	}
+
+      /* Compute the hash code of the destination now,
+	 before the effects of this instruction are recorded,
+	 since the register values used in the address computation
+	 are those before this instruction.  */
+      sets[i].dest_hash = HASH (dest, mode);
+
+      /* Don't enter a bit-field in the hash table
+	 because the value in it after the store
+	 may not equal what was stored, due to truncation.  */
+
+      if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT)
+	{
+	  rtx width = XEXP (SET_DEST (sets[i].rtl), 1);
+
+	  if (src_const != 0 && GET_CODE (src_const) == CONST_INT
+	      && GET_CODE (width) == CONST_INT
+	      && INTVAL (width) < HOST_BITS_PER_WIDE_INT
+	      && ! (INTVAL (src_const)
+		    & ((HOST_WIDE_INT) (-1) << INTVAL (width))))
+	    /* Exception: if the value is constant,
+	       and it won't be truncated, record it.  */
+	    ;
+	  else
+	    {
+	      /* This is chosen so that the destination will be invalidated
+		 but no new value will be recorded.
+		 We must invalidate because sometimes constant
+		 values can be recorded for bitfields.  */
+	      sets[i].src_elt = 0;
+	      sets[i].src_volatile = 1;
+	      src_eqv = 0;
+	      src_eqv_elt = 0;
+	    }
+	}
+
+      /* If only one set in a JUMP_INSN and it is now a no-op, we can delete
+	 the insn.  */
+      else if (n_sets == 1 && dest == pc_rtx && src == pc_rtx)
+	{
+	  /* One less use of the label this insn used to jump to.  */
+	  delete_insn_and_edges (insn);
+	  cse_jumps_altered = true;
+	  /* No more processing for this set.  */
+	  sets[i].rtl = 0;
+	}
+
+      /* If this SET is now setting PC to a label, we know it used to
+	 be a conditional or computed branch.  */
+      else if (dest == pc_rtx && GET_CODE (src) == LABEL_REF
+	       && !LABEL_REF_NONLOCAL_P (src))
+	{
+	  /* We reemit the jump in as many cases as possible just in
+	     case the form of an unconditional jump is significantly
+	     different than a computed jump or conditional jump.
+
+	     If this insn has multiple sets, then reemitting the
+	     jump is nontrivial.  So instead we just force rerecognition
+	     and hope for the best.  */
+	  if (n_sets == 1)
+	    {
+	      rtx new_rtx, note;
+
+	      new_rtx = emit_jump_insn_before (gen_jump (XEXP (src, 0)), insn);
+	      JUMP_LABEL (new_rtx) = XEXP (src, 0);
+	      LABEL_NUSES (XEXP (src, 0))++;
+
+	      /* Make sure to copy over REG_NON_LOCAL_GOTO.  */
+	      note = find_reg_note (insn, REG_NON_LOCAL_GOTO, 0);
+	      if (note)
+		{
+		  XEXP (note, 1) = NULL_RTX;
+		  REG_NOTES (new_rtx) = note;
+		}
+
+	      delete_insn_and_edges (insn);
+	      insn = new_rtx;
+	    }
+	  else
+	    INSN_CODE (insn) = -1;
+
+	  /* Do not bother deleting any unreachable code, let jump do it.  */
+	  cse_jumps_altered = true;
+	  sets[i].rtl = 0;
+	}
+
+      /* If destination is volatile, invalidate it and then do no further
+	 processing for this assignment.  */
+
+      else if (do_not_record)
+	{
+	  if (REG_P (dest) || GET_CODE (dest) == SUBREG)
+	    invalidate (dest, VOIDmode);
+	  else if (MEM_P (dest))
+	    invalidate (dest, VOIDmode);
+	  else if (GET_CODE (dest) == STRICT_LOW_PART
+		   || GET_CODE (dest) == ZERO_EXTRACT)
+	    invalidate (XEXP (dest, 0), GET_MODE (dest));
+	  sets[i].rtl = 0;
+	}
+
+      if (sets[i].rtl != 0 && dest != SET_DEST (sets[i].rtl))
+	sets[i].dest_hash = HASH (SET_DEST (sets[i].rtl), mode);
+
+#ifdef HAVE_cc0
+      /* If setting CC0, record what it was set to, or a constant, if it
+	 is equivalent to a constant.  If it is being set to a floating-point
+	 value, make a COMPARE with the appropriate constant of 0.  If we
+	 don't do this, later code can interpret this as a test against
+	 const0_rtx, which can cause problems if we try to put it into an
+	 insn as a floating-point operand.  */
+      if (dest == cc0_rtx)
+	{
+	  this_insn_cc0 = src_const && mode != VOIDmode ? src_const : src;
+	  this_insn_cc0_mode = mode;
+	  if (FLOAT_MODE_P (mode))
+	    this_insn_cc0 = gen_rtx_COMPARE (VOIDmode, this_insn_cc0,
+					     CONST0_RTX (mode));
+	}
+#endif
+    }
+
+  /* Now enter all non-volatile source expressions in the hash table
+     if they are not already present.
+     Record their equivalence classes in src_elt.
+     This way we can insert the corresponding destinations into
+     the same classes even if the actual sources are no longer in them
+     (having been invalidated).  */
+
+  if (src_eqv && src_eqv_elt == 0 && sets[0].rtl != 0 && ! src_eqv_volatile
+      && ! rtx_equal_p (src_eqv, SET_DEST (sets[0].rtl)))
+    {
+      struct table_elt *elt;
+      struct table_elt *classp = sets[0].src_elt;
+      rtx dest = SET_DEST (sets[0].rtl);
+      enum machine_mode eqvmode = GET_MODE (dest);
+
+      if (GET_CODE (dest) == STRICT_LOW_PART)
+	{
+	  eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0)));
+	  classp = 0;
+	}
+      if (insert_regs (src_eqv, classp, 0))
+	{
+	  rehash_using_reg (src_eqv);
+	  src_eqv_hash = HASH (src_eqv, eqvmode);
+	}
+      elt = insert (src_eqv, classp, src_eqv_hash, eqvmode);
+      elt->in_memory = src_eqv_in_memory;
+      src_eqv_elt = elt;
+
+      /* Check to see if src_eqv_elt is the same as a set source which
+	 does not yet have an elt, and if so set the elt of the set source
+	 to src_eqv_elt.  */
+      for (i = 0; i < n_sets; i++)
+	if (sets[i].rtl && sets[i].src_elt == 0
+	    && rtx_equal_p (SET_SRC (sets[i].rtl), src_eqv))
+	  sets[i].src_elt = src_eqv_elt;
+    }
+
+  for (i = 0; i < n_sets; i++)
+    if (sets[i].rtl && ! sets[i].src_volatile
+	&& ! rtx_equal_p (SET_SRC (sets[i].rtl), SET_DEST (sets[i].rtl)))
+      {
+	if (GET_CODE (SET_DEST (sets[i].rtl)) == STRICT_LOW_PART)
+	  {
+	    /* REG_EQUAL in setting a STRICT_LOW_PART
+	       gives an equivalent for the entire destination register,
+	       not just for the subreg being stored in now.
+	       This is a more interesting equivalence, so we arrange later
+	       to treat the entire reg as the destination.  */
+	    sets[i].src_elt = src_eqv_elt;
+	    sets[i].src_hash = src_eqv_hash;
+	  }
+	else
+	  {
+	    /* Insert source and constant equivalent into hash table, if not
+	       already present.  */
+	    struct table_elt *classp = src_eqv_elt;
+	    rtx src = sets[i].src;
+	    rtx dest = SET_DEST (sets[i].rtl);
+	    enum machine_mode mode
+	      = GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src);
+
+	    /* It's possible that we have a source value known to be
+	       constant but don't have a REG_EQUAL note on the insn.
+	       Lack of a note will mean src_eqv_elt will be NULL.  This
+	       can happen where we've generated a SUBREG to access a
+	       CONST_INT that is already in a register in a wider mode.
+	       Ensure that the source expression is put in the proper
+	       constant class.  */
+	    if (!classp)
+	      classp = sets[i].src_const_elt;
+
+	    if (sets[i].src_elt == 0)
+	      {
+		struct table_elt *elt;
+
+		/* Note that these insert_regs calls cannot remove
+		   any of the src_elt's, because they would have failed to
+		   match if not still valid.  */
+		if (insert_regs (src, classp, 0))
+		  {
+		    rehash_using_reg (src);
+		    sets[i].src_hash = HASH (src, mode);
+		  }
+		elt = insert (src, classp, sets[i].src_hash, mode);
+		elt->in_memory = sets[i].src_in_memory;
+		sets[i].src_elt = classp = elt;
+	      }
+	    if (sets[i].src_const && sets[i].src_const_elt == 0
+		&& src != sets[i].src_const
+		&& ! rtx_equal_p (sets[i].src_const, src))
+	      sets[i].src_elt = insert (sets[i].src_const, classp,
+					sets[i].src_const_hash, mode);
+	  }
+      }
+    else if (sets[i].src_elt == 0)
+      /* If we did not insert the source into the hash table (e.g., it was
+	 volatile), note the equivalence class for the REG_EQUAL value, if any,
+	 so that the destination goes into that class.  */
+      sets[i].src_elt = src_eqv_elt;
+
+  /* Record destination addresses in the hash table.  This allows us to
+     check if they are invalidated by other sets.  */
+  for (i = 0; i < n_sets; i++)
+    {
+      if (sets[i].rtl)
+	{
+	  rtx x = sets[i].inner_dest;
+	  struct table_elt *elt;
+	  enum machine_mode mode;
+	  unsigned hash;
+
+	  if (MEM_P (x))
+	    {
+	      x = XEXP (x, 0);
+	      mode = GET_MODE (x);
+	      hash = HASH (x, mode);
+	      elt = lookup (x, hash, mode);
+	      if (!elt)
+		{
+		  if (insert_regs (x, NULL, 0))
+		    {
+		      rtx dest = SET_DEST (sets[i].rtl);
+
+		      rehash_using_reg (x);
+		      hash = HASH (x, mode);
+		      sets[i].dest_hash = HASH (dest, GET_MODE (dest));
+		    }
+		  elt = insert (x, NULL, hash, mode);
+		}
+
+	      sets[i].dest_addr_elt = elt;
+	    }
+	  else
+	    sets[i].dest_addr_elt = NULL;
+	}
+    }
+
+  invalidate_from_clobbers (x);
+
+  /* Some registers are invalidated by subroutine calls.  Memory is
+     invalidated by non-constant calls.  */
+
+  if (CALL_P (insn))
+    {
+      if (!(RTL_CONST_OR_PURE_CALL_P (insn)))
+	invalidate_memory ();
+      invalidate_for_call ();
+    }
+
+  /* Now invalidate everything set by this instruction.
+     If a SUBREG or other funny destination is being set,
+     sets[i].rtl is still nonzero, so here we invalidate the reg
+     a part of which is being set.  */
+
+  for (i = 0; i < n_sets; i++)
+    if (sets[i].rtl)
+      {
+	/* We can't use the inner dest, because the mode associated with
+	   a ZERO_EXTRACT is significant.  */
+	rtx dest = SET_DEST (sets[i].rtl);
+
+	/* Needed for registers to remove the register from its
+	   previous quantity's chain.
+	   Needed for memory if this is a nonvarying address, unless
+	   we have just done an invalidate_memory that covers even those.  */
+	if (REG_P (dest) || GET_CODE (dest) == SUBREG)
+	  invalidate (dest, VOIDmode);
+	else if (MEM_P (dest))
+	  invalidate (dest, VOIDmode);
+	else if (GET_CODE (dest) == STRICT_LOW_PART
+		 || GET_CODE (dest) == ZERO_EXTRACT)
+	  invalidate (XEXP (dest, 0), GET_MODE (dest));
+      }
+
+  /* A volatile ASM invalidates everything.  */
+  if (NONJUMP_INSN_P (insn)
+      && GET_CODE (PATTERN (insn)) == ASM_OPERANDS
+      && MEM_VOLATILE_P (PATTERN (insn)))
+    flush_hash_table ();
+
+  /* Don't cse over a call to setjmp; on some machines (eg VAX)
+     the regs restored by the longjmp come from a later time
+     than the setjmp.  */
+  if (CALL_P (insn) && find_reg_note (insn, REG_SETJMP, NULL))
+    {
+      flush_hash_table ();
+      goto done;
+    }
+
+  /* Make sure registers mentioned in destinations
+     are safe for use in an expression to be inserted.
+     This removes from the hash table
+     any invalid entry that refers to one of these registers.
+
+     We don't care about the return value from mention_regs because
+     we are going to hash the SET_DEST values unconditionally.  */
+
+  for (i = 0; i < n_sets; i++)
+    {
+      if (sets[i].rtl)
+	{
+	  rtx x = SET_DEST (sets[i].rtl);
+
+	  if (!REG_P (x))
+	    mention_regs (x);
+	  else
+	    {
+	      /* We used to rely on all references to a register becoming
+		 inaccessible when a register changes to a new quantity,
+		 since that changes the hash code.  However, that is not
+		 safe, since after HASH_SIZE new quantities we get a
+		 hash 'collision' of a register with its own invalid
+		 entries.  And since SUBREGs have been changed not to
+		 change their hash code with the hash code of the register,
+		 it wouldn't work any longer at all.  So we have to check
+		 for any invalid references lying around now.
+		 This code is similar to the REG case in mention_regs,
+		 but it knows that reg_tick has been incremented, and
+		 it leaves reg_in_table as -1 .  */
+	      unsigned int regno = REGNO (x);
+	      unsigned int endregno = END_REGNO (x);
+	      unsigned int i;
+
+	      for (i = regno; i < endregno; i++)
+		{
+		  if (REG_IN_TABLE (i) >= 0)
+		    {
+		      remove_invalid_refs (i);
+		      REG_IN_TABLE (i) = -1;
+		    }
+		}
+	    }
+	}
+    }
+
+  /* We may have just removed some of the src_elt's from the hash table.
+     So replace each one with the current head of the same class.
+     Also check if destination addresses have been removed.  */
+
+  for (i = 0; i < n_sets; i++)
+    if (sets[i].rtl)
+      {
+	if (sets[i].dest_addr_elt
+	    && sets[i].dest_addr_elt->first_same_value == 0)
+	  {
+	    /* The elt was removed, which means this destination is not
+	       valid after this instruction.  */
+	    sets[i].rtl = NULL_RTX;
+	  }
+	else if (sets[i].src_elt && sets[i].src_elt->first_same_value == 0)
+	  /* If elt was removed, find current head of same class,
+	     or 0 if nothing remains of that class.  */
+	  {
+	    struct table_elt *elt = sets[i].src_elt;
+
+	    while (elt && elt->prev_same_value)
+	      elt = elt->prev_same_value;
+
+	    while (elt && elt->first_same_value == 0)
+	      elt = elt->next_same_value;
+	    sets[i].src_elt = elt ? elt->first_same_value : 0;
+	  }
+      }
+
+  /* Now insert the destinations into their equivalence classes.  */
+
+  for (i = 0; i < n_sets; i++)
+    if (sets[i].rtl)
+      {
+	rtx dest = SET_DEST (sets[i].rtl);
+	struct table_elt *elt;
+
+	/* Don't record value if we are not supposed to risk allocating
+	   floating-point values in registers that might be wider than
+	   memory.  */
+	if ((flag_float_store
+	     && MEM_P (dest)
+	     && FLOAT_MODE_P (GET_MODE (dest)))
+	    /* Don't record BLKmode values, because we don't know the
+	       size of it, and can't be sure that other BLKmode values
+	       have the same or smaller size.  */
+	    || GET_MODE (dest) == BLKmode
+	    /* If we didn't put a REG_EQUAL value or a source into the hash
+	       table, there is no point is recording DEST.  */
+	    || sets[i].src_elt == 0
+	    /* If DEST is a paradoxical SUBREG and SRC is a ZERO_EXTEND
+	       or SIGN_EXTEND, don't record DEST since it can cause
+	       some tracking to be wrong.
+
+	       ??? Think about this more later.  */
+	    || (GET_CODE (dest) == SUBREG
+		&& (GET_MODE_SIZE (GET_MODE (dest))
+		    > GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))))
+		&& (GET_CODE (sets[i].src) == SIGN_EXTEND
+		    || GET_CODE (sets[i].src) == ZERO_EXTEND)))
+	  continue;
+
+	/* STRICT_LOW_PART isn't part of the value BEING set,
+	   and neither is the SUBREG inside it.
+	   Note that in this case SETS[I].SRC_ELT is really SRC_EQV_ELT.  */
+	if (GET_CODE (dest) == STRICT_LOW_PART)
+	  dest = SUBREG_REG (XEXP (dest, 0));
+
+	if (REG_P (dest) || GET_CODE (dest) == SUBREG)
+	  /* Registers must also be inserted into chains for quantities.  */
+	  if (insert_regs (dest, sets[i].src_elt, 1))
+	    {
+	      /* If `insert_regs' changes something, the hash code must be
+		 recalculated.  */
+	      rehash_using_reg (dest);
+	      sets[i].dest_hash = HASH (dest, GET_MODE (dest));
+	    }
+
+	elt = insert (dest, sets[i].src_elt,
+		      sets[i].dest_hash, GET_MODE (dest));
+
+	elt->in_memory = (MEM_P (sets[i].inner_dest)
+			  && !MEM_READONLY_P (sets[i].inner_dest));
+
+	/* If we have (set (subreg:m1 (reg:m2 foo) 0) (bar:m1)), M1 is no
+	   narrower than M2, and both M1 and M2 are the same number of words,
+	   we are also doing (set (reg:m2 foo) (subreg:m2 (bar:m1) 0)) so
+	   make that equivalence as well.
+
+	   However, BAR may have equivalences for which gen_lowpart
+	   will produce a simpler value than gen_lowpart applied to
+	   BAR (e.g., if BAR was ZERO_EXTENDed from M2), so we will scan all
+	   BAR's equivalences.  If we don't get a simplified form, make
+	   the SUBREG.  It will not be used in an equivalence, but will
+	   cause two similar assignments to be detected.
+
+	   Note the loop below will find SUBREG_REG (DEST) since we have
+	   already entered SRC and DEST of the SET in the table.  */
+
+	if (GET_CODE (dest) == SUBREG
+	    && (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) - 1)
+		 / UNITS_PER_WORD)
+		== (GET_MODE_SIZE (GET_MODE (dest)) - 1) / UNITS_PER_WORD)
+	    && (GET_MODE_SIZE (GET_MODE (dest))
+		>= GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))))
+	    && sets[i].src_elt != 0)
+	  {
+	    enum machine_mode new_mode = GET_MODE (SUBREG_REG (dest));
+	    struct table_elt *elt, *classp = 0;
+
+	    for (elt = sets[i].src_elt->first_same_value; elt;
+		 elt = elt->next_same_value)
+	      {
+		rtx new_src = 0;
+		unsigned src_hash;
+		struct table_elt *src_elt;
+		int byte = 0;
+
+		/* Ignore invalid entries.  */
+		if (!REG_P (elt->exp)
+		    && ! exp_equiv_p (elt->exp, elt->exp, 1, false))
+		  continue;
+
+		/* We may have already been playing subreg games.  If the
+		   mode is already correct for the destination, use it.  */
+		if (GET_MODE (elt->exp) == new_mode)
+		  new_src = elt->exp;
+		else
+		  {
+		    /* Calculate big endian correction for the SUBREG_BYTE.
+		       We have already checked that M1 (GET_MODE (dest))
+		       is not narrower than M2 (new_mode).  */
+		    if (BYTES_BIG_ENDIAN)
+		      byte = (GET_MODE_SIZE (GET_MODE (dest))
+			      - GET_MODE_SIZE (new_mode));
+
+		    new_src = simplify_gen_subreg (new_mode, elt->exp,
+					           GET_MODE (dest), byte);
+		  }
+
+		/* The call to simplify_gen_subreg fails if the value
+		   is VOIDmode, yet we can't do any simplification, e.g.
+		   for EXPR_LISTs denoting function call results.
+		   It is invalid to construct a SUBREG with a VOIDmode
+		   SUBREG_REG, hence a zero new_src means we can't do
+		   this substitution.  */
+		if (! new_src)
+		  continue;
+
+		src_hash = HASH (new_src, new_mode);
+		src_elt = lookup (new_src, src_hash, new_mode);
+
+		/* Put the new source in the hash table is if isn't
+		   already.  */
+		if (src_elt == 0)
+		  {
+		    if (insert_regs (new_src, classp, 0))
+		      {
+			rehash_using_reg (new_src);
+			src_hash = HASH (new_src, new_mode);
+		      }
+		    src_elt = insert (new_src, classp, src_hash, new_mode);
+		    src_elt->in_memory = elt->in_memory;
+		  }
+		else if (classp && classp != src_elt->first_same_value)
+		  /* Show that two things that we've seen before are
+		     actually the same.  */
+		  merge_equiv_classes (src_elt, classp);
+
+		classp = src_elt->first_same_value;
+		/* Ignore invalid entries.  */
+		while (classp
+		       && !REG_P (classp->exp)
+		       && ! exp_equiv_p (classp->exp, classp->exp, 1, false))
+		  classp = classp->next_same_value;
+	      }
+	  }
+      }
+
+  /* Special handling for (set REG0 REG1) where REG0 is the
+     "cheapest", cheaper than REG1.  After cse, REG1 will probably not
+     be used in the sequel, so (if easily done) change this insn to
+     (set REG1 REG0) and replace REG1 with REG0 in the previous insn
+     that computed their value.  Then REG1 will become a dead store
+     and won't cloud the situation for later optimizations.
+
+     Do not make this change if REG1 is a hard register, because it will
+     then be used in the sequel and we may be changing a two-operand insn
+     into a three-operand insn.
+
+     Also do not do this if we are operating on a copy of INSN.  */
+
+  if (n_sets == 1 && sets[0].rtl && REG_P (SET_DEST (sets[0].rtl))
+      && NEXT_INSN (PREV_INSN (insn)) == insn
+      && REG_P (SET_SRC (sets[0].rtl))
+      && REGNO (SET_SRC (sets[0].rtl)) >= FIRST_PSEUDO_REGISTER
+      && REGNO_QTY_VALID_P (REGNO (SET_SRC (sets[0].rtl))))
+    {
+      int src_q = REG_QTY (REGNO (SET_SRC (sets[0].rtl)));
+      struct qty_table_elem *src_ent = &qty_table[src_q];
+
+      if (src_ent->first_reg == REGNO (SET_DEST (sets[0].rtl)))
+	{
+	  /* Scan for the previous nonnote insn, but stop at a basic
+	     block boundary.  */
+	  rtx prev = insn;
+	  rtx bb_head = BB_HEAD (BLOCK_FOR_INSN (insn));
+	  do
+	    {
+	      prev = PREV_INSN (prev);
+	    }
+	  while (prev != bb_head && NOTE_P (prev));
+
+	  /* Do not swap the registers around if the previous instruction
+	     attaches a REG_EQUIV note to REG1.
+
+	     ??? It's not entirely clear whether we can transfer a REG_EQUIV
+	     from the pseudo that originally shadowed an incoming argument
+	     to another register.  Some uses of REG_EQUIV might rely on it
+	     being attached to REG1 rather than REG2.
+
+	     This section previously turned the REG_EQUIV into a REG_EQUAL
+	     note.  We cannot do that because REG_EQUIV may provide an
+	     uninitialized stack slot when REG_PARM_STACK_SPACE is used.  */
+	  if (NONJUMP_INSN_P (prev)
+	      && GET_CODE (PATTERN (prev)) == SET
+	      && SET_DEST (PATTERN (prev)) == SET_SRC (sets[0].rtl)
+	      && ! find_reg_note (prev, REG_EQUIV, NULL_RTX))
+	    {
+	      rtx dest = SET_DEST (sets[0].rtl);
+	      rtx src = SET_SRC (sets[0].rtl);
+	      rtx note;
+
+	      validate_change (prev, &SET_DEST (PATTERN (prev)), dest, 1);
+	      validate_change (insn, &SET_DEST (sets[0].rtl), src, 1);
+	      validate_change (insn, &SET_SRC (sets[0].rtl), dest, 1);
+	      apply_change_group ();
+
+	      /* If INSN has a REG_EQUAL note, and this note mentions
+		 REG0, then we must delete it, because the value in
+		 REG0 has changed.  If the note's value is REG1, we must
+		 also delete it because that is now this insn's dest.  */
+	      note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
+	      if (note != 0
+		  && (reg_mentioned_p (dest, XEXP (note, 0))
+		      || rtx_equal_p (src, XEXP (note, 0))))
+		remove_note (insn, note);
+	    }
+	}
+    }
+
+done:;
+}
+
+/* Remove from the hash table all expressions that reference memory.  */
+
+static void
+invalidate_memory (void)
+{
+  int i;
+  struct table_elt *p, *next;
+
+  for (i = 0; i < HASH_SIZE; i++)
+    for (p = table[i]; p; p = next)
+      {
+	next = p->next_same_hash;
+	if (p->in_memory)
+	  remove_from_table (p, i);
+      }
+}
+
+/* Perform invalidation on the basis of everything about an insn
+   except for invalidating the actual places that are SET in it.
+   This includes the places CLOBBERed, and anything that might
+   alias with something that is SET or CLOBBERed.
+
+   X is the pattern of the insn.  */
+
+static void
+invalidate_from_clobbers (rtx x)
+{
+  if (GET_CODE (x) == CLOBBER)
+    {
+      rtx ref = XEXP (x, 0);
+      if (ref)
+	{
+	  if (REG_P (ref) || GET_CODE (ref) == SUBREG
+	      || MEM_P (ref))
+	    invalidate (ref, VOIDmode);
+	  else if (GET_CODE (ref) == STRICT_LOW_PART
+		   || GET_CODE (ref) == ZERO_EXTRACT)
+	    invalidate (XEXP (ref, 0), GET_MODE (ref));
+	}
+    }
+  else if (GET_CODE (x) == PARALLEL)
+    {
+      int i;
+      for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
+	{
+	  rtx y = XVECEXP (x, 0, i);
+	  if (GET_CODE (y) == CLOBBER)
+	    {
+	      rtx ref = XEXP (y, 0);
+	      if (REG_P (ref) || GET_CODE (ref) == SUBREG
+		  || MEM_P (ref))
+		invalidate (ref, VOIDmode);
+	      else if (GET_CODE (ref) == STRICT_LOW_PART
+		       || GET_CODE (ref) == ZERO_EXTRACT)
+		invalidate (XEXP (ref, 0), GET_MODE (ref));
+	    }
+	}
+    }
+}
+
+/* Process X, part of the REG_NOTES of an insn.  Look at any REG_EQUAL notes
+   and replace any registers in them with either an equivalent constant
+   or the canonical form of the register.  If we are inside an address,
+   only do this if the address remains valid.
+
+   OBJECT is 0 except when within a MEM in which case it is the MEM.
+
+   Return the replacement for X.  */
+
+static rtx
+cse_process_notes_1 (rtx x, rtx object, bool *changed)
+{
+  enum rtx_code code = GET_CODE (x);
+  const char *fmt = GET_RTX_FORMAT (code);
+  int i;
+
+  switch (code)
+    {
+    case CONST_INT:
+    case CONST:
+    case SYMBOL_REF:
+    case LABEL_REF:
+    case CONST_DOUBLE:
+    case CONST_FIXED:
+    case CONST_VECTOR:
+    case PC:
+    case CC0:
+    case LO_SUM:
+      return x;
+
+    case MEM:
+      validate_change (x, &XEXP (x, 0),
+		       cse_process_notes (XEXP (x, 0), x, changed), 0);
+      return x;
+
+    case EXPR_LIST:
+    case INSN_LIST:
+      if (REG_NOTE_KIND (x) == REG_EQUAL)
+	XEXP (x, 0) = cse_process_notes (XEXP (x, 0), NULL_RTX, changed);
+      if (XEXP (x, 1))
+	XEXP (x, 1) = cse_process_notes (XEXP (x, 1), NULL_RTX, changed);
+      return x;
+
+    case SIGN_EXTEND:
+    case ZERO_EXTEND:
+    case SUBREG:
+      {
+	rtx new_rtx = cse_process_notes (XEXP (x, 0), object, changed);
+	/* We don't substitute VOIDmode constants into these rtx,
+	   since they would impede folding.  */
+	if (GET_MODE (new_rtx) != VOIDmode)
+	  validate_change (object, &XEXP (x, 0), new_rtx, 0);
+	return x;
+      }
+
+    case REG:
+      i = REG_QTY (REGNO (x));
+
+      /* Return a constant or a constant register.  */
+      if (REGNO_QTY_VALID_P (REGNO (x)))
+	{
+	  struct qty_table_elem *ent = &qty_table[i];
+
+	  if (ent->const_rtx != NULL_RTX
+	      && (CONSTANT_P (ent->const_rtx)
+		  || REG_P (ent->const_rtx)))
+	    {
+	      rtx new_rtx = gen_lowpart (GET_MODE (x), ent->const_rtx);
+	      if (new_rtx)
+		return copy_rtx (new_rtx);
+	    }
+	}
+
+      /* Otherwise, canonicalize this register.  */
+      return canon_reg (x, NULL_RTX);
+
+    default:
+      break;
+    }
+
+  for (i = 0; i < GET_RTX_LENGTH (code); i++)
+    if (fmt[i] == 'e')
+      validate_change (object, &XEXP (x, i),
+		       cse_process_notes (XEXP (x, i), object, changed), 0);
+
+  return x;
+}
+
+static rtx
+cse_process_notes (rtx x, rtx object, bool *changed)
+{
+  rtx new_rtx = cse_process_notes_1 (x, object, changed);
+  if (new_rtx != x)
+    *changed = true;
+  return new_rtx;
+}
+
+
+/* Find a path in the CFG, starting with FIRST_BB to perform CSE on.
+
+   DATA is a pointer to a struct cse_basic_block_data, that is used to
+   describe the path.
+   It is filled with a queue of basic blocks, starting with FIRST_BB
+   and following a trace through the CFG.
+  
+   If all paths starting at FIRST_BB have been followed, or no new path
+   starting at FIRST_BB can be constructed, this function returns FALSE.
+   Otherwise, DATA->path is filled and the function returns TRUE indicating
+   that a path to follow was found.
+
+   If FOLLOW_JUMPS is false, the maximum path length is 1 and the only
+   block in the path will be FIRST_BB.  */
+
+static bool
+cse_find_path (basic_block first_bb, struct cse_basic_block_data *data,
+	       int follow_jumps)
+{
+  basic_block bb;
+  edge e;
+  int path_size;
+ 
+  SET_BIT (cse_visited_basic_blocks, first_bb->index);
+
+  /* See if there is a previous path.  */
+  path_size = data->path_size;
+
+  /* There is a previous path.  Make sure it started with FIRST_BB.  */
+  if (path_size)
+    gcc_assert (data->path[0].bb == first_bb);
+
+  /* There was only one basic block in the last path.  Clear the path and
+     return, so that paths starting at another basic block can be tried.  */
+  if (path_size == 1)
+    {
+      path_size = 0;
+      goto done;
+    }
+
+  /* If the path was empty from the beginning, construct a new path.  */
+  if (path_size == 0)
+    data->path[path_size++].bb = first_bb;
+  else
+    {
+      /* Otherwise, path_size must be equal to or greater than 2, because
+	 a previous path exists that is at least two basic blocks long.
+
+	 Update the previous branch path, if any.  If the last branch was
+	 previously along the branch edge, take the fallthrough edge now.  */
+      while (path_size >= 2)
+	{
+	  basic_block last_bb_in_path, previous_bb_in_path;
+	  edge e;
+
+	  --path_size;
+	  last_bb_in_path = data->path[path_size].bb;
+	  previous_bb_in_path = data->path[path_size - 1].bb;
+
+	  /* If we previously followed a path along the branch edge, try
+	     the fallthru edge now.  */
+	  if (EDGE_COUNT (previous_bb_in_path->succs) == 2
+	      && any_condjump_p (BB_END (previous_bb_in_path))
+	      && (e = find_edge (previous_bb_in_path, last_bb_in_path))
+	      && e == BRANCH_EDGE (previous_bb_in_path))
+	    {
+	      bb = FALLTHRU_EDGE (previous_bb_in_path)->dest;
+	      if (bb != EXIT_BLOCK_PTR
+		  && single_pred_p (bb)
+		  /* We used to assert here that we would only see blocks
+		     that we have not visited yet.  But we may end up
+		     visiting basic blocks twice if the CFG has changed
+		     in this run of cse_main, because when the CFG changes
+		     the topological sort of the CFG also changes.  A basic
+		     blocks that previously had more than two predecessors
+		     may now have a single predecessor, and become part of
+		     a path that starts at another basic block.
+
+		     We still want to visit each basic block only once, so
+		     halt the path here if we have already visited BB.  */
+		  && !TEST_BIT (cse_visited_basic_blocks, bb->index))
+		{
+		  SET_BIT (cse_visited_basic_blocks, bb->index);
+		  data->path[path_size++].bb = bb;
+		  break;
+		}
+	    }
+
+	  data->path[path_size].bb = NULL;
+	}
+
+      /* If only one block remains in the path, bail.  */
+      if (path_size == 1)
+	{
+	  path_size = 0;
+	  goto done;
+	}
+    }
+
+  /* Extend the path if possible.  */
+  if (follow_jumps)
+    {
+      bb = data->path[path_size - 1].bb;
+      while (bb && path_size < PARAM_VALUE (PARAM_MAX_CSE_PATH_LENGTH))
+	{
+	  if (single_succ_p (bb))
+	    e = single_succ_edge (bb);
+	  else if (EDGE_COUNT (bb->succs) == 2
+		   && any_condjump_p (BB_END (bb)))
+	    {
+	      /* First try to follow the branch.  If that doesn't lead
+		 to a useful path, follow the fallthru edge.  */
+	      e = BRANCH_EDGE (bb);
+	      if (!single_pred_p (e->dest))
+		e = FALLTHRU_EDGE (bb);
+	    }
+	  else
+	    e = NULL;
+
+	  if (e && e->dest != EXIT_BLOCK_PTR
+	      && single_pred_p (e->dest)
+	      /* Avoid visiting basic blocks twice.  The large comment
+		 above explains why this can happen.  */
+	      && !TEST_BIT (cse_visited_basic_blocks, e->dest->index))
+	    {
+	      basic_block bb2 = e->dest;
+	      SET_BIT (cse_visited_basic_blocks, bb2->index);
+	      data->path[path_size++].bb = bb2;
+	      bb = bb2;
+	    }
+	  else
+	    bb = NULL;
+	}
+    }
+
+done:
+  data->path_size = path_size;
+  return path_size != 0;
+}
+
+/* Dump the path in DATA to file F.  NSETS is the number of sets
+   in the path.  */
+
+static void
+cse_dump_path (struct cse_basic_block_data *data, int nsets, FILE *f)
+{
+  int path_entry;
+
+  fprintf (f, ";; Following path with %d sets: ", nsets);
+  for (path_entry = 0; path_entry < data->path_size; path_entry++)
+    fprintf (f, "%d ", (data->path[path_entry].bb)->index);
+  fputc ('\n', dump_file);
+  fflush (f);
+}
+
+
+/* Return true if BB has exception handling successor edges.  */
+
+static bool
+have_eh_succ_edges (basic_block bb)
+{
+  edge e;
+  edge_iterator ei;
+
+  FOR_EACH_EDGE (e, ei, bb->succs)
+    if (e->flags & EDGE_EH)
+      return true;
+
+  return false;
+}
+
+
+/* Scan to the end of the path described by DATA.  Return an estimate of
+   the total number of SETs of all insns in the path.  */
+
+static void
+cse_prescan_path (struct cse_basic_block_data *data)
+{
+  int nsets = 0;
+  int path_size = data->path_size;
+  int path_entry;
+
+  /* Scan to end of each basic block in the path.  */
+  for (path_entry = 0; path_entry < path_size; path_entry++) 
+    {
+      basic_block bb;
+      rtx insn;
+
+      bb = data->path[path_entry].bb;
+
+      FOR_BB_INSNS (bb, insn)
+	{
+	  if (!INSN_P (insn))
+	    continue;
+
+	  /* A PARALLEL can have lots of SETs in it,
+	     especially if it is really an ASM_OPERANDS.  */
+	  if (GET_CODE (PATTERN (insn)) == PARALLEL)
+	    nsets += XVECLEN (PATTERN (insn), 0);
+	  else
+	    nsets += 1;
+	}
+    }
+
+  data->nsets = nsets;
+}
+
+/* Process a single extended basic block described by EBB_DATA.  */
+
+static void
+cse_extended_basic_block (struct cse_basic_block_data *ebb_data)
+{
+  int path_size = ebb_data->path_size;
+  int path_entry;
+  int num_insns = 0;
+
+  /* Allocate the space needed by qty_table.  */
+  qty_table = XNEWVEC (struct qty_table_elem, max_qty);
+
+  new_basic_block ();
+  cse_ebb_live_in = df_get_live_in (ebb_data->path[0].bb);
+  cse_ebb_live_out = df_get_live_out (ebb_data->path[path_size - 1].bb);
+  for (path_entry = 0; path_entry < path_size; path_entry++)
+    {
+      basic_block bb;
+      rtx insn;
+
+      bb = ebb_data->path[path_entry].bb;
+
+      /* Invalidate recorded information for eh regs if there is an EH
+	 edge pointing to that bb.  */
+      if (bb_has_eh_pred (bb))
+	{
+	  df_ref *def_rec;
+
+	  for (def_rec = df_get_artificial_defs (bb->index); *def_rec; def_rec++)
+	    {
+	      df_ref def = *def_rec;
+	      if (DF_REF_FLAGS (def) & DF_REF_AT_TOP)
+		invalidate (DF_REF_REG (def), GET_MODE (DF_REF_REG (def)));
+	    }
+	}
+
+      FOR_BB_INSNS (bb, insn)
+	{
+	  optimize_this_for_speed_p = optimize_bb_for_speed_p (bb);
+	  /* If we have processed 1,000 insns, flush the hash table to
+	     avoid extreme quadratic behavior.  We must not include NOTEs
+	     in the count since there may be more of them when generating
+	     debugging information.  If we clear the table at different
+	     times, code generated with -g -O might be different than code
+	     generated with -O but not -g.
+
+	     FIXME: This is a real kludge and needs to be done some other
+		    way.  */
+	  if (INSN_P (insn)
+	      && num_insns++ > PARAM_VALUE (PARAM_MAX_CSE_INSNS))
+	    {
+	      flush_hash_table ();
+	      num_insns = 0;
+	    }
+
+	  if (INSN_P (insn))
+	    {
+	      /* Process notes first so we have all notes in canonical forms
+		 when looking for duplicate operations.  */
+	      if (REG_NOTES (insn))
+		{
+		  bool changed = false;
+		  REG_NOTES (insn) = cse_process_notes (REG_NOTES (insn),
+						        NULL_RTX, &changed);
+		  if (changed)
+		    df_notes_rescan (insn);
+		}
+
+	      cse_insn (insn);
+
+	      /* If we haven't already found an insn where we added a LABEL_REF,
+		 check this one.  */
+	      if (INSN_P (insn) && !recorded_label_ref
+		  && for_each_rtx (&PATTERN (insn), check_for_label_ref,
+				   (void *) insn))
+		recorded_label_ref = true;
+
+#ifdef HAVE_cc0
+	      /* If the previous insn set CC0 and this insn no longer
+		 references CC0, delete the previous insn.  Here we use
+		 fact that nothing expects CC0 to be valid over an insn,
+		 which is true until the final pass.  */
+	      {
+		rtx prev_insn, tem;
+
+		prev_insn = PREV_INSN (insn);
+		if (prev_insn && NONJUMP_INSN_P (prev_insn)
+		    && (tem = single_set (prev_insn)) != 0
+		    && SET_DEST (tem) == cc0_rtx
+		    && ! reg_mentioned_p (cc0_rtx, PATTERN (insn)))
+		  delete_insn (prev_insn);
+	      }
+
+	      /* If this insn is not the last insn in the basic block,
+		 it will be PREV_INSN(insn) in the next iteration.  If
+		 we recorded any CC0-related information for this insn,
+		 remember it.  */
+	      if (insn != BB_END (bb))
+		{
+		  prev_insn_cc0 = this_insn_cc0;
+		  prev_insn_cc0_mode = this_insn_cc0_mode;
+		}
+#endif
+	    }
+	}
+
+      /* With non-call exceptions, we are not always able to update
+	 the CFG properly inside cse_insn.  So clean up possibly
+	 redundant EH edges here.  */
+      if (flag_non_call_exceptions && have_eh_succ_edges (bb))
+	cse_cfg_altered |= purge_dead_edges (bb);
+
+      /* If we changed a conditional jump, we may have terminated
+	 the path we are following.  Check that by verifying that
+	 the edge we would take still exists.  If the edge does
+	 not exist anymore, purge the remainder of the path.
+	 Note that this will cause us to return to the caller.  */
+      if (path_entry < path_size - 1)
+	{
+	  basic_block next_bb = ebb_data->path[path_entry + 1].bb;
+	  if (!find_edge (bb, next_bb))
+	    {
+	      do
+		{
+		  path_size--;
+
+		  /* If we truncate the path, we must also reset the
+		     visited bit on the remaining blocks in the path,
+		     or we will never visit them at all.  */
+		  RESET_BIT (cse_visited_basic_blocks,
+			     ebb_data->path[path_size].bb->index);
+		  ebb_data->path[path_size].bb = NULL;
+		}
+	      while (path_size - 1 != path_entry);
+	      ebb_data->path_size = path_size;
+	    }
+	}
+
+      /* If this is a conditional jump insn, record any known
+	 equivalences due to the condition being tested.  */
+      insn = BB_END (bb);
+      if (path_entry < path_size - 1
+	  && JUMP_P (insn)
+	  && single_set (insn)
+	  && any_condjump_p (insn))
+	{
+	  basic_block next_bb = ebb_data->path[path_entry + 1].bb;
+	  bool taken = (next_bb == BRANCH_EDGE (bb)->dest);
+	  record_jump_equiv (insn, taken);
+	}
+
+#ifdef HAVE_cc0
+      /* Clear the CC0-tracking related insns, they can't provide
+	 useful information across basic block boundaries.  */
+      prev_insn_cc0 = 0;
+#endif
+    }
+
+  gcc_assert (next_qty <= max_qty);
+
+  free (qty_table);
+}
+
+
+/* Perform cse on the instructions of a function.
+   F is the first instruction.
+   NREGS is one plus the highest pseudo-reg number used in the instruction.
+
+   Return 2 if jump optimizations should be redone due to simplifications
+   in conditional jump instructions.
+   Return 1 if the CFG should be cleaned up because it has been modified.
+   Return 0 otherwise.  */
+
+int
+cse_main (rtx f ATTRIBUTE_UNUSED, int nregs)
+{
+  struct cse_basic_block_data ebb_data;
+  basic_block bb;
+  int *rc_order = XNEWVEC (int, last_basic_block);
+  int i, n_blocks;
+
+  df_set_flags (DF_LR_RUN_DCE);
+  df_analyze ();
+  df_set_flags (DF_DEFER_INSN_RESCAN);
+
+  reg_scan (get_insns (), max_reg_num ());
+  init_cse_reg_info (nregs);
+
+  ebb_data.path = XNEWVEC (struct branch_path,
+			   PARAM_VALUE (PARAM_MAX_CSE_PATH_LENGTH));
+
+  cse_cfg_altered = false;
+  cse_jumps_altered = false;
+  recorded_label_ref = false;
+  constant_pool_entries_cost = 0;
+  constant_pool_entries_regcost = 0;
+  ebb_data.path_size = 0;
+  ebb_data.nsets = 0;
+  rtl_hooks = cse_rtl_hooks;
+
+  init_recog ();
+  init_alias_analysis ();
+
+  reg_eqv_table = XNEWVEC (struct reg_eqv_elem, nregs);
+
+  /* Set up the table of already visited basic blocks.  */
+  cse_visited_basic_blocks = sbitmap_alloc (last_basic_block);
+  sbitmap_zero (cse_visited_basic_blocks);
+
+  /* Loop over basic blocks in reverse completion order (RPO),
+     excluding the ENTRY and EXIT blocks.  */
+  n_blocks = pre_and_rev_post_order_compute (NULL, rc_order, false);
+  i = 0;
+  while (i < n_blocks)
+    {
+      /* Find the first block in the RPO queue that we have not yet
+	 processed before.  */
+      do
+	{
+	  bb = BASIC_BLOCK (rc_order[i++]);
+	}
+      while (TEST_BIT (cse_visited_basic_blocks, bb->index)
+	     && i < n_blocks);
+
+      /* Find all paths starting with BB, and process them.  */
+      while (cse_find_path (bb, &ebb_data, flag_cse_follow_jumps))
+	{
+	  /* Pre-scan the path.  */
+	  cse_prescan_path (&ebb_data);
+
+	  /* If this basic block has no sets, skip it.  */
+	  if (ebb_data.nsets == 0)
+	    continue;
+
+	  /* Get a reasonable estimate for the maximum number of qty's
+	     needed for this path.  For this, we take the number of sets
+	     and multiply that by MAX_RECOG_OPERANDS.  */
+	  max_qty = ebb_data.nsets * MAX_RECOG_OPERANDS;
+
+	  /* Dump the path we're about to process.  */
+	  if (dump_file)
+	    cse_dump_path (&ebb_data, ebb_data.nsets, dump_file);
+
+	  cse_extended_basic_block (&ebb_data);
+	}
+    }
+
+  /* Clean up.  */
+  end_alias_analysis ();
+  free (reg_eqv_table);
+  free (ebb_data.path);
+  sbitmap_free (cse_visited_basic_blocks);
+  free (rc_order);
+  rtl_hooks = general_rtl_hooks;
+
+  if (cse_jumps_altered || recorded_label_ref)
+    return 2;
+  else if (cse_cfg_altered)
+    return 1;
+  else
+    return 0;
+}
+
+/* Called via for_each_rtx to see if an insn is using a LABEL_REF for
+   which there isn't a REG_LABEL_OPERAND note.
+   Return one if so.  DATA is the insn.  */
+
+static int
+check_for_label_ref (rtx *rtl, void *data)
+{
+  rtx insn = (rtx) data;
+
+  /* If this insn uses a LABEL_REF and there isn't a REG_LABEL_OPERAND
+     note for it, we must rerun jump since it needs to place the note.  If
+     this is a LABEL_REF for a CODE_LABEL that isn't in the insn chain,
+     don't do this since no REG_LABEL_OPERAND will be added.  */
+  return (GET_CODE (*rtl) == LABEL_REF
+	  && ! LABEL_REF_NONLOCAL_P (*rtl)
+	  && (!JUMP_P (insn)
+	      || !label_is_jump_target_p (XEXP (*rtl, 0), insn))
+	  && LABEL_P (XEXP (*rtl, 0))
+	  && INSN_UID (XEXP (*rtl, 0)) != 0
+	  && ! find_reg_note (insn, REG_LABEL_OPERAND, XEXP (*rtl, 0)));
+}
+
+/* Count the number of times registers are used (not set) in X.
+   COUNTS is an array in which we accumulate the count, INCR is how much
+   we count each register usage.
+
+   Don't count a usage of DEST, which is the SET_DEST of a SET which
+   contains X in its SET_SRC.  This is because such a SET does not
+   modify the liveness of DEST.
+   DEST is set to pc_rtx for a trapping insn, which means that we must count
+   uses of a SET_DEST regardless because the insn can't be deleted here.  */
+
+static void
+count_reg_usage (rtx x, int *counts, rtx dest, int incr)
+{
+  enum rtx_code code;
+  rtx note;
+  const char *fmt;
+  int i, j;
+
+  if (x == 0)
+    return;
+
+  switch (code = GET_CODE (x))
+    {
+    case REG:
+      if (x != dest)
+	counts[REGNO (x)] += incr;
+      return;
+
+    case PC:
+    case CC0:
+    case CONST:
+    case CONST_INT:
+    case CONST_DOUBLE:
+    case CONST_FIXED:
+    case CONST_VECTOR:
+    case SYMBOL_REF:
+    case LABEL_REF:
+      return;
+
+    case CLOBBER:
+      /* If we are clobbering a MEM, mark any registers inside the address
+         as being used.  */
+      if (MEM_P (XEXP (x, 0)))
+	count_reg_usage (XEXP (XEXP (x, 0), 0), counts, NULL_RTX, incr);
+      return;
+
+    case SET:
+      /* Unless we are setting a REG, count everything in SET_DEST.  */
+      if (!REG_P (SET_DEST (x)))
+	count_reg_usage (SET_DEST (x), counts, NULL_RTX, incr);
+      count_reg_usage (SET_SRC (x), counts,
+		       dest ? dest : SET_DEST (x),
+		       incr);
+      return;
+
+    case CALL_INSN:
+    case INSN:
+    case JUMP_INSN:
+    /* We expect dest to be NULL_RTX here.  If the insn may trap, mark
+       this fact by setting DEST to pc_rtx.  */
+      if (flag_non_call_exceptions && may_trap_p (PATTERN (x)))
+	dest = pc_rtx;
+      if (code == CALL_INSN)
+	count_reg_usage (CALL_INSN_FUNCTION_USAGE (x), counts, dest, incr);
+      count_reg_usage (PATTERN (x), counts, dest, incr);
+
+      /* Things used in a REG_EQUAL note aren't dead since loop may try to
+	 use them.  */
+
+      note = find_reg_equal_equiv_note (x);
+      if (note)
+	{
+	  rtx eqv = XEXP (note, 0);
+
+	  if (GET_CODE (eqv) == EXPR_LIST)
+	  /* This REG_EQUAL note describes the result of a function call.
+	     Process all the arguments.  */
+	    do
+	      {
+		count_reg_usage (XEXP (eqv, 0), counts, dest, incr);
+		eqv = XEXP (eqv, 1);
+	      }
+	    while (eqv && GET_CODE (eqv) == EXPR_LIST);
+	  else
+	    count_reg_usage (eqv, counts, dest, incr);
+	}
+      return;
+
+    case EXPR_LIST:
+      if (REG_NOTE_KIND (x) == REG_EQUAL
+	  || (REG_NOTE_KIND (x) != REG_NONNEG && GET_CODE (XEXP (x,0)) == USE)
+	  /* FUNCTION_USAGE expression lists may include (CLOBBER (mem /u)),
+	     involving registers in the address.  */
+	  || GET_CODE (XEXP (x, 0)) == CLOBBER)
+	count_reg_usage (XEXP (x, 0), counts, NULL_RTX, incr);
+
+      count_reg_usage (XEXP (x, 1), counts, NULL_RTX, incr);
+      return;
+
+    case ASM_OPERANDS:
+      /* If the asm is volatile, then this insn cannot be deleted,
+	 and so the inputs *must* be live.  */
+      if (MEM_VOLATILE_P (x))
+	dest = NULL_RTX;
+      /* Iterate over just the inputs, not the constraints as well.  */
+      for (i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--)
+	count_reg_usage (ASM_OPERANDS_INPUT (x, i), counts, dest, incr);
+      return;
+
+    case INSN_LIST:
+      gcc_unreachable ();
+
+    default:
+      break;
+    }
+
+  fmt = GET_RTX_FORMAT (code);
+  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'e')
+	count_reg_usage (XEXP (x, i), counts, dest, incr);
+      else if (fmt[i] == 'E')
+	for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+	  count_reg_usage (XVECEXP (x, i, j), counts, dest, incr);
+    }
+}
+
+/* Return true if set is live.  */
+static bool
+set_live_p (rtx set, rtx insn ATTRIBUTE_UNUSED, /* Only used with HAVE_cc0.  */
+	    int *counts)
+{
+#ifdef HAVE_cc0
+  rtx tem;
+#endif
+
+  if (set_noop_p (set))
+    ;
+
+#ifdef HAVE_cc0
+  else if (GET_CODE (SET_DEST (set)) == CC0
+	   && !side_effects_p (SET_SRC (set))
+	   && ((tem = next_nonnote_insn (insn)) == 0
+	       || !INSN_P (tem)
+	       || !reg_referenced_p (cc0_rtx, PATTERN (tem))))
+    return false;
+#endif
+  else if (!REG_P (SET_DEST (set))
+	   || REGNO (SET_DEST (set)) < FIRST_PSEUDO_REGISTER
+	   || counts[REGNO (SET_DEST (set))] != 0
+	   || side_effects_p (SET_SRC (set)))
+    return true;
+  return false;
+}
+
+/* Return true if insn is live.  */
+
+static bool
+insn_live_p (rtx insn, int *counts)
+{
+  int i;
+  if (flag_non_call_exceptions && may_trap_p (PATTERN (insn)))
+    return true;
+  else if (GET_CODE (PATTERN (insn)) == SET)
+    return set_live_p (PATTERN (insn), insn, counts);
+  else if (GET_CODE (PATTERN (insn)) == PARALLEL)
+    {
+      for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
+	{
+	  rtx elt = XVECEXP (PATTERN (insn), 0, i);
+
+	  if (GET_CODE (elt) == SET)
+	    {
+	      if (set_live_p (elt, insn, counts))
+		return true;
+	    }
+	  else if (GET_CODE (elt) != CLOBBER && GET_CODE (elt) != USE)
+	    return true;
+	}
+      return false;
+    }
+  else
+    return true;
+}
+
+/* Scan all the insns and delete any that are dead; i.e., they store a register
+   that is never used or they copy a register to itself.
+
+   This is used to remove insns made obviously dead by cse, loop or other
+   optimizations.  It improves the heuristics in loop since it won't try to
+   move dead invariants out of loops or make givs for dead quantities.  The
+   remaining passes of the compilation are also sped up.  */
+
+int
+delete_trivially_dead_insns (rtx insns, int nreg)
+{
+  int *counts;
+  rtx insn, prev;
+  int ndead = 0;
+
+  timevar_push (TV_DELETE_TRIVIALLY_DEAD);
+  /* First count the number of times each register is used.  */
+  counts = XCNEWVEC (int, nreg);
+  for (insn = insns; insn; insn = NEXT_INSN (insn))
+    if (INSN_P (insn))
+      count_reg_usage (insn, counts, NULL_RTX, 1);
+
+  /* Go from the last insn to the first and delete insns that only set unused
+     registers or copy a register to itself.  As we delete an insn, remove
+     usage counts for registers it uses.
+
+     The first jump optimization pass may leave a real insn as the last
+     insn in the function.   We must not skip that insn or we may end
+     up deleting code that is not really dead.  */
+  for (insn = get_last_insn (); insn; insn = prev)
+    {
+      int live_insn = 0;
+
+      prev = PREV_INSN (insn);
+      if (!INSN_P (insn))
+	continue;
+
+      live_insn = insn_live_p (insn, counts);
+
+      /* If this is a dead insn, delete it and show registers in it aren't
+	 being used.  */
+
+      if (! live_insn && dbg_cnt (delete_trivial_dead))
+	{
+	  count_reg_usage (insn, counts, NULL_RTX, -1);
+	  delete_insn_and_edges (insn);
+	  ndead++;
+	}
+    }
+
+  if (dump_file && ndead)
+    fprintf (dump_file, "Deleted %i trivially dead insns\n",
+	     ndead);
+  /* Clean up.  */
+  free (counts);
+  timevar_pop (TV_DELETE_TRIVIALLY_DEAD);
+  return ndead;
+}
+
+/* This function is called via for_each_rtx.  The argument, NEWREG, is
+   a condition code register with the desired mode.  If we are looking
+   at the same register in a different mode, replace it with
+   NEWREG.  */
+
+static int
+cse_change_cc_mode (rtx *loc, void *data)
+{
+  struct change_cc_mode_args* args = (struct change_cc_mode_args*)data;
+
+  if (*loc
+      && REG_P (*loc)
+      && REGNO (*loc) == REGNO (args->newreg)
+      && GET_MODE (*loc) != GET_MODE (args->newreg))
+    {
+      validate_change (args->insn, loc, args->newreg, 1);
+      
+      return -1;
+    }
+  return 0;
+}
+
+/* Change the mode of any reference to the register REGNO (NEWREG) to
+   GET_MODE (NEWREG) in INSN.  */
+
+static void
+cse_change_cc_mode_insn (rtx insn, rtx newreg)
+{
+  struct change_cc_mode_args args;
+  int success;
+
+  if (!INSN_P (insn))
+    return;
+
+  args.insn = insn;
+  args.newreg = newreg;
+  
+  for_each_rtx (&PATTERN (insn), cse_change_cc_mode, &args);
+  for_each_rtx (&REG_NOTES (insn), cse_change_cc_mode, &args);
+  
+  /* If the following assertion was triggered, there is most probably
+     something wrong with the cc_modes_compatible back end function.
+     CC modes only can be considered compatible if the insn - with the mode
+     replaced by any of the compatible modes - can still be recognized.  */
+  success = apply_change_group ();
+  gcc_assert (success);
+}
+
+/* Change the mode of any reference to the register REGNO (NEWREG) to
+   GET_MODE (NEWREG), starting at START.  Stop before END.  Stop at
+   any instruction which modifies NEWREG.  */
+
+static void
+cse_change_cc_mode_insns (rtx start, rtx end, rtx newreg)
+{
+  rtx insn;
+
+  for (insn = start; insn != end; insn = NEXT_INSN (insn))
+    {
+      if (! INSN_P (insn))
+	continue;
+
+      if (reg_set_p (newreg, insn))
+	return;
+
+      cse_change_cc_mode_insn (insn, newreg);
+    }
+}
+
+/* BB is a basic block which finishes with CC_REG as a condition code
+   register which is set to CC_SRC.  Look through the successors of BB
+   to find blocks which have a single predecessor (i.e., this one),
+   and look through those blocks for an assignment to CC_REG which is
+   equivalent to CC_SRC.  CAN_CHANGE_MODE indicates whether we are
+   permitted to change the mode of CC_SRC to a compatible mode.  This
+   returns VOIDmode if no equivalent assignments were found.
+   Otherwise it returns the mode which CC_SRC should wind up with.
+   ORIG_BB should be the same as BB in the outermost cse_cc_succs call,
+   but is passed unmodified down to recursive calls in order to prevent
+   endless recursion.
+
+   The main complexity in this function is handling the mode issues.
+   We may have more than one duplicate which we can eliminate, and we
+   try to find a mode which will work for multiple duplicates.  */
+
+static enum machine_mode
+cse_cc_succs (basic_block bb, basic_block orig_bb, rtx cc_reg, rtx cc_src,
+	      bool can_change_mode)
+{
+  bool found_equiv;
+  enum machine_mode mode;
+  unsigned int insn_count;
+  edge e;
+  rtx insns[2];
+  enum machine_mode modes[2];
+  rtx last_insns[2];
+  unsigned int i;
+  rtx newreg;
+  edge_iterator ei;
+
+  /* We expect to have two successors.  Look at both before picking
+     the final mode for the comparison.  If we have more successors
+     (i.e., some sort of table jump, although that seems unlikely),
+     then we require all beyond the first two to use the same
+     mode.  */
+
+  found_equiv = false;
+  mode = GET_MODE (cc_src);
+  insn_count = 0;
+  FOR_EACH_EDGE (e, ei, bb->succs)
+    {
+      rtx insn;
+      rtx end;
+
+      if (e->flags & EDGE_COMPLEX)
+	continue;
+
+      if (EDGE_COUNT (e->dest->preds) != 1
+	  || e->dest == EXIT_BLOCK_PTR
+	  /* Avoid endless recursion on unreachable blocks.  */
+	  || e->dest == orig_bb)
+	continue;
+
+      end = NEXT_INSN (BB_END (e->dest));
+      for (insn = BB_HEAD (e->dest); insn != end; insn = NEXT_INSN (insn))
+	{
+	  rtx set;
+
+	  if (! INSN_P (insn))
+	    continue;
+
+	  /* If CC_SRC is modified, we have to stop looking for
+	     something which uses it.  */
+	  if (modified_in_p (cc_src, insn))
+	    break;
+
+	  /* Check whether INSN sets CC_REG to CC_SRC.  */
+	  set = single_set (insn);
+	  if (set
+	      && REG_P (SET_DEST (set))
+	      && REGNO (SET_DEST (set)) == REGNO (cc_reg))
+	    {
+	      bool found;
+	      enum machine_mode set_mode;
+	      enum machine_mode comp_mode;
+
+	      found = false;
+	      set_mode = GET_MODE (SET_SRC (set));
+	      comp_mode = set_mode;
+	      if (rtx_equal_p (cc_src, SET_SRC (set)))
+		found = true;
+	      else if (GET_CODE (cc_src) == COMPARE
+		       && GET_CODE (SET_SRC (set)) == COMPARE
+		       && mode != set_mode
+		       && rtx_equal_p (XEXP (cc_src, 0),
+				       XEXP (SET_SRC (set), 0))
+		       && rtx_equal_p (XEXP (cc_src, 1),
+				       XEXP (SET_SRC (set), 1)))
+			   
+		{
+		  comp_mode = targetm.cc_modes_compatible (mode, set_mode);
+		  if (comp_mode != VOIDmode
+		      && (can_change_mode || comp_mode == mode))
+		    found = true;
+		}
+
+	      if (found)
+		{
+		  found_equiv = true;
+		  if (insn_count < ARRAY_SIZE (insns))
+		    {
+		      insns[insn_count] = insn;
+		      modes[insn_count] = set_mode;
+		      last_insns[insn_count] = end;
+		      ++insn_count;
+
+		      if (mode != comp_mode)
+			{
+			  gcc_assert (can_change_mode);
+			  mode = comp_mode;
+
+			  /* The modified insn will be re-recognized later.  */
+			  PUT_MODE (cc_src, mode);
+			}
+		    }
+		  else
+		    {
+		      if (set_mode != mode)
+			{
+			  /* We found a matching expression in the
+			     wrong mode, but we don't have room to
+			     store it in the array.  Punt.  This case
+			     should be rare.  */
+			  break;
+			}
+		      /* INSN sets CC_REG to a value equal to CC_SRC
+			 with the right mode.  We can simply delete
+			 it.  */
+		      delete_insn (insn);
+		    }
+
+		  /* We found an instruction to delete.  Keep looking,
+		     in the hopes of finding a three-way jump.  */
+		  continue;
+		}
+
+	      /* We found an instruction which sets the condition
+		 code, so don't look any farther.  */
+	      break;
+	    }
+
+	  /* If INSN sets CC_REG in some other way, don't look any
+	     farther.  */
+	  if (reg_set_p (cc_reg, insn))
+	    break;
+	}
+
+      /* If we fell off the bottom of the block, we can keep looking
+	 through successors.  We pass CAN_CHANGE_MODE as false because
+	 we aren't prepared to handle compatibility between the
+	 further blocks and this block.  */
+      if (insn == end)
+	{
+	  enum machine_mode submode;
+
+	  submode = cse_cc_succs (e->dest, orig_bb, cc_reg, cc_src, false);
+	  if (submode != VOIDmode)
+	    {
+	      gcc_assert (submode == mode);
+	      found_equiv = true;
+	      can_change_mode = false;
+	    }
+	}
+    }
+
+  if (! found_equiv)
+    return VOIDmode;
+
+  /* Now INSN_COUNT is the number of instructions we found which set
+     CC_REG to a value equivalent to CC_SRC.  The instructions are in
+     INSNS.  The modes used by those instructions are in MODES.  */
+
+  newreg = NULL_RTX;
+  for (i = 0; i < insn_count; ++i)
+    {
+      if (modes[i] != mode)
+	{
+	  /* We need to change the mode of CC_REG in INSNS[i] and
+	     subsequent instructions.  */
+	  if (! newreg)
+	    {
+	      if (GET_MODE (cc_reg) == mode)
+		newreg = cc_reg;
+	      else
+		newreg = gen_rtx_REG (mode, REGNO (cc_reg));
+	    }
+	  cse_change_cc_mode_insns (NEXT_INSN (insns[i]), last_insns[i],
+				    newreg);
+	}
+
+      delete_insn_and_edges (insns[i]);
+    }
+
+  return mode;
+}
+
+/* If we have a fixed condition code register (or two), walk through
+   the instructions and try to eliminate duplicate assignments.  */
+
+static void
+cse_condition_code_reg (void)
+{
+  unsigned int cc_regno_1;
+  unsigned int cc_regno_2;
+  rtx cc_reg_1;
+  rtx cc_reg_2;
+  basic_block bb;
+
+  if (! targetm.fixed_condition_code_regs (&cc_regno_1, &cc_regno_2))
+    return;
+
+  cc_reg_1 = gen_rtx_REG (CCmode, cc_regno_1);
+  if (cc_regno_2 != INVALID_REGNUM)
+    cc_reg_2 = gen_rtx_REG (CCmode, cc_regno_2);
+  else
+    cc_reg_2 = NULL_RTX;
+
+  FOR_EACH_BB (bb)
+    {
+      rtx last_insn;
+      rtx cc_reg;
+      rtx insn;
+      rtx cc_src_insn;
+      rtx cc_src;
+      enum machine_mode mode;
+      enum machine_mode orig_mode;
+
+      /* Look for blocks which end with a conditional jump based on a
+	 condition code register.  Then look for the instruction which
+	 sets the condition code register.  Then look through the
+	 successor blocks for instructions which set the condition
+	 code register to the same value.  There are other possible
+	 uses of the condition code register, but these are by far the
+	 most common and the ones which we are most likely to be able
+	 to optimize.  */
+
+      last_insn = BB_END (bb);
+      if (!JUMP_P (last_insn))
+	continue;
+
+      if (reg_referenced_p (cc_reg_1, PATTERN (last_insn)))
+	cc_reg = cc_reg_1;
+      else if (cc_reg_2 && reg_referenced_p (cc_reg_2, PATTERN (last_insn)))
+	cc_reg = cc_reg_2;
+      else
+	continue;
+
+      cc_src_insn = NULL_RTX;
+      cc_src = NULL_RTX;
+      for (insn = PREV_INSN (last_insn);
+	   insn && insn != PREV_INSN (BB_HEAD (bb));
+	   insn = PREV_INSN (insn))
+	{
+	  rtx set;
+
+	  if (! INSN_P (insn))
+	    continue;
+	  set = single_set (insn);
+	  if (set
+	      && REG_P (SET_DEST (set))
+	      && REGNO (SET_DEST (set)) == REGNO (cc_reg))
+	    {
+	      cc_src_insn = insn;
+	      cc_src = SET_SRC (set);
+	      break;
+	    }
+	  else if (reg_set_p (cc_reg, insn))
+	    break;
+	}
+
+      if (! cc_src_insn)
+	continue;
+
+      if (modified_between_p (cc_src, cc_src_insn, NEXT_INSN (last_insn)))
+	continue;
+
+      /* Now CC_REG is a condition code register used for a
+	 conditional jump at the end of the block, and CC_SRC, in
+	 CC_SRC_INSN, is the value to which that condition code
+	 register is set, and CC_SRC is still meaningful at the end of
+	 the basic block.  */
+
+      orig_mode = GET_MODE (cc_src);
+      mode = cse_cc_succs (bb, bb, cc_reg, cc_src, true);
+      if (mode != VOIDmode)
+	{
+	  gcc_assert (mode == GET_MODE (cc_src));
+	  if (mode != orig_mode)
+	    {
+	      rtx newreg = gen_rtx_REG (mode, REGNO (cc_reg));
+
+	      cse_change_cc_mode_insn (cc_src_insn, newreg);
+
+	      /* Do the same in the following insns that use the
+		 current value of CC_REG within BB.  */
+	      cse_change_cc_mode_insns (NEXT_INSN (cc_src_insn),
+					NEXT_INSN (last_insn),
+					newreg);
+	    }
+	}
+    }
+}
+
+
+/* Perform common subexpression elimination.  Nonzero value from
+   `cse_main' means that jumps were simplified and some code may now
+   be unreachable, so do jump optimization again.  */
+static bool
+gate_handle_cse (void)
+{
+  return optimize > 0;
+}
+
+static unsigned int
+rest_of_handle_cse (void)
+{
+  int tem;
+
+  if (dump_file)
+    dump_flow_info (dump_file, dump_flags);
+
+  tem = cse_main (get_insns (), max_reg_num ());
+
+  /* If we are not running more CSE passes, then we are no longer
+     expecting CSE to be run.  But always rerun it in a cheap mode.  */
+  cse_not_expected = !flag_rerun_cse_after_loop && !flag_gcse;
+
+  if (tem == 2)
+    {
+      timevar_push (TV_JUMP);
+      rebuild_jump_labels (get_insns ());
+      cleanup_cfg (0);
+      timevar_pop (TV_JUMP);
+    }
+  else if (tem == 1 || optimize > 1)
+    cleanup_cfg (0);
+
+  return 0;
+}
+
+struct rtl_opt_pass pass_cse =
+{
+ {
+  RTL_PASS,
+  "cse1",                               /* name */
+  gate_handle_cse,                      /* gate */   
+  rest_of_handle_cse,			/* execute */       
+  NULL,                                 /* sub */
+  NULL,                                 /* next */
+  0,                                    /* static_pass_number */
+  TV_CSE,                               /* tv_id */
+  0,                                    /* properties_required */
+  0,                                    /* properties_provided */
+  0,                                    /* properties_destroyed */
+  0,                                    /* todo_flags_start */
+  TODO_df_finish | TODO_verify_rtl_sharing |
+  TODO_dump_func |
+  TODO_ggc_collect |
+  TODO_verify_flow,                     /* todo_flags_finish */
+ }
+};
+
+
+static bool
+gate_handle_cse2 (void)
+{
+  return optimize > 0 && flag_rerun_cse_after_loop;
+}
+
+/* Run second CSE pass after loop optimizations.  */
+static unsigned int
+rest_of_handle_cse2 (void)
+{
+  int tem;
+
+  if (dump_file)
+    dump_flow_info (dump_file, dump_flags);
+
+  tem = cse_main (get_insns (), max_reg_num ());
+
+  /* Run a pass to eliminate duplicated assignments to condition code
+     registers.  We have to run this after bypass_jumps, because it
+     makes it harder for that pass to determine whether a jump can be
+     bypassed safely.  */
+  cse_condition_code_reg ();
+
+  delete_trivially_dead_insns (get_insns (), max_reg_num ());
+
+  if (tem == 2)
+    {
+      timevar_push (TV_JUMP);
+      rebuild_jump_labels (get_insns ());
+      cleanup_cfg (0);
+      timevar_pop (TV_JUMP);
+    }
+  else if (tem == 1)
+    cleanup_cfg (0);
+
+  cse_not_expected = 1;
+  return 0;
+}
+
+
+struct rtl_opt_pass pass_cse2 =
+{
+ {
+  RTL_PASS,
+  "cse2",                               /* name */
+  gate_handle_cse2,                     /* gate */   
+  rest_of_handle_cse2,			/* execute */       
+  NULL,                                 /* sub */
+  NULL,                                 /* next */
+  0,                                    /* static_pass_number */
+  TV_CSE2,                              /* tv_id */
+  0,                                    /* properties_required */
+  0,                                    /* properties_provided */
+  0,                                    /* properties_destroyed */
+  0,                                    /* todo_flags_start */
+  TODO_df_finish | TODO_verify_rtl_sharing |
+  TODO_dump_func |
+  TODO_ggc_collect |
+  TODO_verify_flow                      /* todo_flags_finish */
+ }
+};
+